Dropbox

HDZeroのDVRはTSフォーマットが推奨されています。そのあたりの事情は以前書いた記事をご覧ください。

その時に手作業でTSファイルからMP4への変換方法を書きましたが、やはり面倒です。ということで少しでも楽をするためのプログラムを書いてみました。

[ 特徴 ]
– 再エンコードなしで動作が速い
– ワンクリックでSDカードのTSファイルをまとめて変換
– TSファイルのタイムスタンプをMP4にも反映(手作業の時は諦めていた) -> DVRファイルの時刻を何時も正しくするためにはHDZeroゴーグルにバッテリーを取り付けます

https://github.com/nkozawa/ts2mp4

[ 前提 ]
プログラムを書いたと言っても実際に動画を変換するのはffmpegというツールです。もしffmpegが導入されていない場合は、まずこれを導入しコマンド窓でffmpegと打ち込んで動くようにしておく必要があります。Windowsの場合、おそらく自分でPATHを通すという作業を行わないといけません。

[ 実行ファイル ]
Python3の環境がある場合、公開されているts2mp4.pyを実行してください。MacOSとWindows11のPython 3.11でテストしています。試してはいませんがLinuxでも動くと思います。MacOSとWindows用にはPythonの環境がなくても稼働する実行ファイルも用意しています。

[ 使い方 ]

– Input path(TS files)ボタンを押してTSファイルのある場所を指定します。HDZeroゴーグルのSDカードを指定する事が多いと思います。その場合、SDカード下のmoviesフォルダーを指定します。
– Output path(MP4 files)ボタンを押してMP4ファイルを格納する場所を指定します。
– TSファイルのタイムスタンプをMP4ファイルに反映させる場合はUse Original File Timestampにチェックを入れます。チェックを入れない場合はMP4ファイルのタイムスタンプは現在の時刻になります。
– Start, no overwriteもしくはStart, overwrite allを押して変換を開始します。
“Start, no overwite”は出力先にすでに該当のMP4ファイルがあれば何もしません。新しく録画したものだけを変換するので普通はこのボタンが便利です。ただし判定はファイル名のみで行っています。SDカードを初期化した場合などは考慮が必要です。”Start, overwrite all”は出力先の状態に関係なく全てのTSファイルを変換します。

[ INIファイル ]
Input path(TS files)、Output path(MP4 files)、Use Original File Timestampの設定値はINIファイルに記録されます。次にプログラムを起動するとINIファイルを自動的に読み込みます。

INIファイルはユーザーのホームディレクトリーにts2mp4.iniという名前で書き込みます。

INIファイルの書き込みは、今のところStart, no overwriteもしくはStart, overwrite allを押した時に行なっています。

自分用の覚書です。zipファイルの中に複数のzipファイルが有り、zip中zipの中から目的のテキストファイルを探し出して読み込むサンプル・コードです。特別なことは何もないですし、注釈は要らないでしょう。

#!/usr/bin/perl

my $fn_to_be_found = "target.txt";

if ($ARGV[0] =~ /zip$/i) {
	use File::Temp qw/tempfile/;
	use Archive::Zip;
	my $zip = Archive::Zip->new;
	$zip->read($ARGV[0]);
	my @members = $zip->membersMatching(/zip$/i);
	my $finish = 0;
	foreach my $m (@members) {
		my $fn = $m->fileName;
		print "zip in zip file: $fn\n";
		my ($temp_fh, $temp_filename) = tempfile(UNLINK => 1);
		print "Temporary file name: $temp_filename\n";
		$m->extractToFileHandle($temp_fh);
		$temp_fh->flush;
		my $zip2 = Archive::Zip->new;
		$zip2->read($temp_filename);
		my $m2 = $zip2->memberNamed($fn_to_be_found);
		if ($m2) {
			my $fn2 = $m2->fileName;
			print ">>$fn2\n";
			$file_contents = $m2->contents;
			$finish = 1;
		}
		close $temp_fh;
		if ($finish) {last;}
	}
}

# print $file_contents;

++++++++++
この記事の内容は古すぎます。(2024/03/28)
現在のおすすめは「【Pyinstaller】MacOS用、Windows用のexeをそれぞれ作成する失敗しない方法」です。
++++++++++

MacとWindows環境の両方で動く小さなツールを作ろうと思いました。作りたいのはCUIベースのもので簡単なファイル操作を行い結果を標準出陸出来れば良いだけです。最悪、メインのロジックだけでも共通化出来れば良いくらいに考えていました。WindowsにActivePerlとかStrawberryPerlを入れてしまえば解決と言えないこともないのですが、もともとPerl実行環境が入っていないWindowsにもツールを提供する方法を模索していました。

色々と巡り巡って(というほどでは無いですが)、結局Perlに戻ってきました。Windows環境でPerlスクリプトをexe化すればPerl実行環境をインストールすることなく作成したツールを実行出来ます。

参考ページ: Strawberry Perl(Portable)でPerlスクリプトのexeファイル化

まったく参考ページ、そのままなので追加する情報もありません。わたしが実際にしようしたバージョン等についてだけ書いておきます。

Windows : 日本語版Windows 7 Ultimate 64bit
Perl : Strawberry Perl Portable version 5.24.0.1 64bit PortableZIP edition + extra PDL related libs

Windows上の適当な場所にダウンロードしたzipファイルを展開します。Portable版なのでインストール作業を行うことなくPerlが使用できます。展開した中にあるportableshell.batを実行するとPerlが使用できるコマンドプロンプトが開きます。以下、そのコマンドプロンプトでの実行になります。

以下のコマンドを実行してcpanからPackerモジュールを導入します。参考ページでは–forceオプションを指定してましたが、わたしの実行環境では普通に導入出来ました。

cpanm PAR::Packer

後は以下のようにして目的のPerlスクリプトをexe化します。

pp -o test.exe test.pl

‘Use Archive::Tar’などモジュールが含まれたものも問題なくexe化が可能でした。

参考ページにオプションの説明があります。そこにも書かれていますが、実行の際にライブラリやスクリプトそのものはC:\Users\xxx\AppData\Local\Temp\par-*に展開されます。削除はされません。

Appleに提出したアプリケーションが審査の段階でクラッシュした場合 iTunes ConnectのResolution Centerにトラブルの発生状況とともにCrsh Reportが添付されます。

Crash Reportをダウンロードしてダブルクリックするとコンソールが立ち上がり內容を読むことが出来ますが、肝心の自分のアプリケーションのシンボルが関連付けられていません。困ったときのGoogle頼みで調べ見るとxcrun atosをCrash reportとアーカイブから取り出した.dSYMファイルを関連付ける方法が見つかりました。ところが試してみてもうまく行きません。

基本にかえってAppleのガイドに従うことにしました。答えは簡単に見つかりました。先のResolution CenterにリンクがあるTechnical Note TN2151によると単純にXcodeのOrganizer/Devices/LIBRARY/Device LogsにダウンロードしたクラッシュレポートをImportすればOKです。もちろん、これはアプリケーションをBuild/ArchiveしたXcodeで行わないとシンボルとの突き合わせは行われません。

Mac OS XはGUIアプリケーションが洗練されていることのみならず、shell script、PythonそれにPerlがそのまま使える事が一部のユーザーにとっては意義のあることになっています。

ターミナル上で簡単に実行出来るスクリプトも便利なものです。常用するスクリプトはパスを通したりaliasで簡単に起動させることで実用になります。しかしながら、場合によってはGUIは無くても良いけどアイコンをクリックして起動させたいと思うこともあります。

スクリプトの.app化については既に多くの情報が出回っていますが、アイコンの作成まで含めると複数の情報を組みあせなければなりませんでした。また、私の目的であるperlスクリプトの実行後に画面を残して置くためには一工夫が必要でした。というわけで、自分への覚え書きにするとともに新たな.app化サンブルの提供を行うことにしました。

細かい説明は省いて、ガシガシとサンプルの作成を行なって行きます。

[ アプリケーション・バンドルの作成 ]
参考webページ: 技術/MacOSX/シェルスクリプトを”.app”(“Bundle”)化する

ターミナルを開いて適当な作業場所に移動します。

mkdir WhatDate.app
cd WhatDate.app
mkdir Contents
cd Contents
mkdir MacOS
mkdir Resources

WhatDate.app/Contents/MacOS/WhatDate.pl を以下の内容で作成。またchmod +xで実行属性も付けておく。

#!/usr/bin/perl
system("date");
print "EPOCH: ", time, "¥n";
@localtime = localtime;
print "JULIAN: $localtime[7]¥n";

コマンドラインから実行してみて動作を確認。

WhatDate.app/Contents/MacOS/WhatDate.bash を以下の内容で作成。これも同様にchmod +xで実行属性を付ける。

#!/bin/bash
BASEDIR=$(dirname $0)
open -a Terminal "${BASEDIR}/WhatDate.pl"

コマンドラインから実行してみる。今度は別のウインドウが開いて先のPerlスクリプトが実行されます。

WhatDate.app/Contents/Info.plist を以下の内容で作成します。これがアプリケーションの詳細を記述した重要なファイルです。

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleExecutable</key>
<string>WhatDate.bash</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleSignature</key>
<string>????</string>
</dict>
</plist>

もうこれでアプリケーション・バンドルとしては成立しています。アイコンをクリックして起動できるかどうか確認してみましょう。

[ アイコンの作成 ]

インターネットで調べるとIcon Composerを使うべしという情報が沢山見つかりますが、今時は iconutil というコマンドライン・ツールを使用します。

参考リンク: MacアプリのRetina対応アイコン(*.icns)を作成する方法

まずはアイコン画像をでっち上げますが、解像度に応じて沢山作らないといけません。ガイドによると全て作らなくても良いと書いてありますが、どれを作っておけば良いかは書いてないので今回は全部用意することにします。

まずは適当な場所にフォルダー作ります。
mkdir WhatDate.iconset

その下に以下の画像ファイルを作成します。
icon_16x16.png
icon_16x16@2x.png
icon_32x32.png
icon_32x32@2x.png
icon_128x128.png
icon_128x128@2x.png
icon_256x256.png
icon_256x256@2x.png
icon_512x512.png
icon_512x512@2x.png

WhatDateIcons

ファイル名の512×512は想像通り画素数です。後ろに@2xが付いている場合は倍の画素数になります。すなわちicon_512x512@2x.pngは1024×1024の画像ファイルを作成します。icon_16x16@2x.pngとicon_32x32.pngは同じ画素数ですが、あくまでもicon_16x16.pngの高解像度版です。場合によっては解像度によって違うデザインを使うこともあります。その場合、icon_16x16@2x.pngはicon_16x16.pngと同じデザインを使用するということです。

以下のコマンドを実行するとWhatDate.icnsが出来上がります。
iconutil -c icns WhatDate.iconset

出来上がった WhatDate.iconset を WhatDate.app/Contents/Resources/ 下にコピーする。

WhatDate.app/Contents/Info.plistに以下の2行を適当な場所に追加する。
<key>CFBundleIconFile</key>
<string>WhatDate.icns</string>

改めてWhatDate.app/Contents/Info.plistを全部書き出すとこんな具合です。

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleExecutable</key>
<string>WhatDate.bash</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleIconFile</key>
<string>WhatDate.icns</string>
<key>CFBundleSignature</key>
<string>????</string>
</dict>
</plist>

これで完成です。実際のアイコンは直ぐには表示されないかも知れません。

[ 配布 ]

これを配布する場合はディスクイメージにするのが簡単です。

まず適当なところにフォルダを作ります。フォルダ名は分かりやすくアプリケーション名と同じにしたりします。その中に、作成したアプリケーション・バンドルをコピー。必要に応じてReadme.txtみたいな説明も入れておきます。

ディスクユーティリティを起動。
「ファイル/新規/フォルダからのディスクイメージ」を選択します。
ファイル・ダイアログが表示されるので先に作ったフォルダを指定すれば配布用のディスクイメージが出来上がります。
配布先ではWhatDate.dmgをダブルクリックして開いた中にWhatDataアプリケーションが見えますので、適当なところにコピーして使用します。

参考までに、 作成したWhatData.dmgをサンブルとして置いておきます。

Adobeからソースコード用のフォントSource Code Proが公開されています。Iやl, Oと0などが視認しやすくなっています。

Mac OS Xでは/Library/Fontsにファイルファイルを入れておけば良いようです。

CotEditorで早速使用してみました。すっきりとして読みやすく、かなり良い感じです。

前のエントリーと関連した話題です。Perlにもいつの間にかデバッガーが搭載されていました。ほぼpythonのものと同等の機能を持っていますが微妙にコマンドが違います。ということで、覚書エントリーです。

起動は、

perl -d hoge.pl

などとします。

コマンドは、

ブレークポイント設定
 b 100
行番号100にブレークポイントが設定される
ブレークポイント
 L
ブレークポイントが設定されていれば、その一覧が表示される
ブレークポイントの解除
 B 100
行番号100のブレークポイントが解除される。番号の代わりに * を指定すると全てのブレークポイントを解除できる
実行
 c
ブレークポイントに遭遇するまで実行する
ステップ動作
 s
一行だけ実行してデバッガーに戻ります。サブルーチンの内部に入り込んでステップ動作します
ステップ動作
 n
一行だけ実行してデバッガーに戻ります。サブルーチンに遭遇しても次の行に進みます
変数の表示
 p $hoge
hoge変数の内容が表示される

今時のpythonにはデバッガーが内蔵されているためブレークポイントを設定したりステップ動作を行いつつ変数の中身を調べるなどということを簡単に行うことが出来ます。たまにしか使わないためすぐコマンドを忘れてしまう自分のために覚書としてエントリーしておきます。

起動方法は簡単で

python -m pdb hoge.py

などとしてコマンドラインで起動します。同時にhoge.pyを行番号表示の出来るエディターで開いておくと分かりやすいです。

コマンドを自分で使う最低限のものだけ紹介します。

ブレークポイント設定
 b 100 
行番号100にブレークポイントが設定される
ブレークポイント
 b 
ブレークポイントが設定されていれば、その一覧が表示される
ブレークポイントの解除
 cl 1 
1番目のブレークポイントが解除される。番号を省略すると全てのブレークポイントを解除できる
実行
 c 
ブレークポイントに遭遇するまで実行する
ステップ動作
 s 
一行だけ実行してデバッガーに戻ります>/td>
変数の表示
 p hoge 
hoge変数の内容が表示される


この本にはデバッガーについての解説は無いと思いますが、私がpythonを始めるにあたって購入した唯一の書籍です。

SQLite3というサーバー要らずのDBエンジンはパーソナルにSQLを使用するのに手軽です。また、最近の多くのモバイル・デバイスの内部でも使用されています。

ということでMac OS X上でもiOSアプリ内(最近はCoreDataに移行中ですが)でも多用しています。単純な単語の場合は問題ありませんが特殊文字も含めてqueryする場合には文字のエスケープを行わなればなりません。iOSアプリの内部で多様な文字列のqueryを行う必要が出てきたために文字列のエスケープについてまとめてみました。

[ LIKE operatorの場合 ]
標準的なSQLで使用されるLIKEと同じです。ワイルドカードとして%(0個以上の任意の文字に一致)と_(任意の一文字に一致)が使用できます。そのため%_自身をマッチさせるためにはエスケープ文字の指定が必要です。 エスケープ文字の指定にはESCAPE節を使用します。

SELECT * FROM hoge WHERE key LIKE '% test^_case %' ESCAPE '^';

という具合に使用します。ESCAPEで指定した文字自体もエスケープ対象になります。

引用符の扱いはperlと同様です。
– シングルクォート ‘ で囲まれた文字列の中ではダブルクォート ” は自由に使用できる。
– ダブルクォート ” で囲まれた文字列の中ではシングルクォート ‘ は自由に使用できる。
– シングルクォートとダブルクォートが混在する場合には囲み文字自体でエスケープ出来る。

SELECT * FROM hoge WHERE key LIKE 'I''m reading the "SNOW WHITE"';

[ GLOB operatorの場合 ]
GLOBはSQLiteに特有なオペレーターです。正規表現的な表現を使用しLIKEより柔軟なパターンマッチングが行えます。またLIKEは大文字小文字の区別がありませんがGLOBでは大文字小文字を区別します。

ここではエスケープについてだけ書きます。エスケープしなければならない文字は [ ] * ? です。エスケープの方法は、それぞれを[]で囲みます。

SELECT * FROM hoge WHERE key GLOB 'How are you[?] *';

といった具合になります。'[Y/n]’のように[]自体が含まれる文字列のマッチングも同様です。

SELECT * FROM hoge WHERE key GLOB '[[]Y/n[]]';

引用符の扱いはLIKEと同じです。

KKJConvでエラーが発生するということで調べてみたところ、どうもMIDP実行環境側の問題のようです。

エラーが発生するのは次のようなコードです。

TextBox tb;
tb.delete(0, tb.size());

このコードはTextBoxの文字列を削除するためのものですが、TextBoxが空の時に左の写真のようにString Index Out Of Boundsというエラーが発生します。

deleteメソッドの第一パラメータはオフセット、第二パラメータは削除する文字列の長さです。

tb.size() が 0 の時の問題なので tb.delete(0, 0); のようにしてテストすると文字列が存在する時にはエラーは発生しません。言い換えるとTextBoxに文字列が存在しない時にdeleteを呼ぶとエラーするということです。一応、ドキュメントを確認しましたが呼び方には問題が無いように思えます。ましてやKKJConvの初期から存在するコードなので実績もおおいにあります。

回避策は簡単なので以下のようなコードに変更してKKJConvのエラーは回避出来ました。

if (0 < tb.size()) {
  tb.delete(0, tb.size());
}

Nokiaにバグではないの?と投げかけようと思いましたが、以前Forum Nokiaにあったバグレポートのシステムが見つかりません。もう一般開発者からのバグレポートは受け付けてないのかな?