先日、作った(2018年6月1日の日記参照)ISDBというかARIBのデスクランブラの続きです。
DVB APIで制御可能なチューナー(私はPT2で確認しています)を使っている方であれば、下記のようにチューニング(コードは GitHubにあります)できます。チューニングに成功して放送が受信されると、/dev/dvb/adapter0/dvr0からスクランブルの掛かったMPEG2-TSが出力されます。
例: BSプレミアム(衛星はアダプタ0か2、地デジはアダプタ1か3を使います) $ ./sample_dvb 0 S BS 3 0x4031 ... ごちゃごちゃ出るのが邪魔くさければ、 $ ./sample_dvb 0 S BS 3 0x4031 > /dev/null
スマートカードリーダーをPCに接続し、B-CASカードをリーダーに挿入した上で、下記のようにデスクランブル(コードは GitHubにあります)できます。デスクランブルしたMPEG2-TSはUDPで送るか、ファイルに保存できます。
例: 自分自身にUDPで送る $ ./arib_descramble /dev/dvb/adapter0/dvr0 localhost 1234 ...
VLCを起動し、udp://@:1234を再生すると、受信中の放送が映るはずです。
自身の規格理解のためもあって、かなり手抜き実装していて、異常に重いため、いくつか改良してみました。まずプロファイラで見てみると、MULTI2復号と、どこかにある無駄なコピーに、時間がかかっているようです。
MULTI2復号の高速化にはSSE2を使ってみました。MULTI2の復号は8バイトずつですが、SSE2を活用するには32バイトの方が都合が良いです。ですので4単位まとめて(4 x 8バイト = 32バイト = 128bit = SSE2のレジスタ幅)処理して、残った32バイトに満たないデータは従来どおり8バイトずつ処理します。
残念ながら、結果から言うとあまり最適化が効きませんでした。SIMDで高速化できないロード/ストアの割合が高いのか、計算が占める割合が低いのか、いまいちわからなかったのですが、あまり高速化できませんでした。CPU利用率でいうと12% が11% になるか、ならないか…程度です。
無駄なコピーは2箇所見つけたのでガッツリ消しました。これは効果があったようで、CPU利用率でいうと11% が10% くらいまで削減できました。
無駄なコピーはもう1つありましたが、単純に消すわけにいかなくてやや難しそうだったので、また今度にします。
PCだと、CPU利用率10% 程度だったので、最近のマルチコアCPUなら割と余裕の負荷です。ではショボいCPUで実行するとどうなるか、試してみました。
手持ちのRaspberry Pi 3(ARM Cortex A53 x 4/1.4GHz)で実行してみたところ、CPU利用率25〜27% 程度でした。動かないかもしれないと思っていたので、正直意外でした。かなり健闘していると思います。
ARMにはNEONというSIMD命令がありますが、NEONを使った復号の高速化にはまだ手を出していません。今度やってみますが、SSE2の結果を見た限りでは、絶大な効果は見込めないでしょう。きっと。
Raspberry Pi 3を持っているのですが、あまり速くない(当たり前ですけど)こともあり、ほとんどコンパイルには使っていませんでした。今日、久しぶりにコードのビルドに使ってみたら、変な症状にハマりました。
$ autoconf --version autoconf (GNU Autoconf) 2.69 ... $ automake --version automake (GNU automake) 1.15 ... $ autoreconf -fi aclocal: warning: couldn't open directory 'm4': No such file or directory configure.ac:23: installing 'conf/compile' configure.ac:16: installing 'conf/install-sh' configure.ac:16: installing 'conf/missing' src/Makefile.am: installing 'conf/depcomp'
更地からのビルドなのでautoreconf -fiを実行しています。この時点では特にエラーも出ずに終わったように見えます。
$ ./configure checking for a BSD-compatible install... /usr/bin/install -c checking whether build environment is sane... yes checking for a thread-safe mkdir -p... /bin/mkdir -p checking for gawk... gawk checking whether make sets $(MAKE)... yes checking whether make supports nested variables... yes checking whether make supports nested variables... (cached) yes ./configure: line 2505: syntax error near unexpected token `ac_ext=c' ./configure: line 2505: `ac_ext=c'
しかしconfigureが謎のエラーで終了してしまいます。しかもconfig.logにエラーの内容が記録されておらず、怪しいです。
しばしconfigure.acをいじってみてわかったことは、以下の条件を満たしていると、このエラーが発生するようです。
解決策はlibtoolをインストールするか、libtoolを使っていないならconfigure.acからLT_INIT() を削除しても良いです。
この辺の仕組みは詳しくありませんが、libtoolが無いなら無いと言ってくれれば、もう少しわかりやすいのにな…と思います。
Android 8のチューナー周りについて、家でも少し調べていたので、わかったことのメモを貼っておきます。環境は下記の通りです。
チューナーの動作は大きく分けると、チャンネルスキャン、視聴の2つです。
チャンネルスキャンは全てのチャンネルに対してチューニングを行って、受信できるかどうか試す動作を指します。Android 8の場合、フレームワーク内部にチャンネルのリストを持っていて、リストの先頭から試す仕組みになっているようです。
コードを見た感じ、処理は下記のように進むようです。
// @ src/com/android/tv/tuner/setup/TunerSetupActivity.java TunerSetupActivity::executeAction(String category, int actionId, Bundle params) switch (category) case ConnectionTypeFragment.ACTION_CATEGORY: mLastScanFragment = new ScanFragment(); Bundle args1 = new Bundle(); // actionIdはGUI画面の選択肢(Antenna, Cable, Not sure)の // 上から何番目か?に相当する、一番上がactionId = 0 // ★★CHANNEL_MAP_SCAN_FILEは後述 args1.putInt(ScanFragment.EXTRA_FOR_CHANNEL_SCAN_FILE, CHANNEL_MAP_SCAN_FILE[actionId]); // ...snip... // ScanFragmentを実行、表示する showFragment(mLastScanFragment); ScanFragment::onCreateView() @ src/com/android/tv/tuner/setup/ScanFragment.java ScanFragment::startScan() new ChannelScanTask() ChannelScanTask::execute() ScanFragment::ChannelScanTask::doInBackground() @ src/com/android/tv/tuner/setup/ScanFragment.java //チャンネルリストを得る ChannelScanFileParser::parseScanFile() @ src/com/android/tv/tuner/setup/ScanFragment.java ScanFragment::ChannelScanTask::scanChannels() TunerTsStreamer::startStream(ChannelScanFileParser.ScanChannel) @ src/com/android/tv/tuner/source/TunerTsStreamer.java TunerHal::tune() @ src/com/android/tv/tuner/TunerHal.java Java_com_android_tv_tuner_TunerHal_nativeTune() @ jni/tunertvinput_jni.cpp DvbManager::tune() @ jni/DvbManager.cpp //DVB API v5でのチューニング方法 ioctl(FE_SET_PROPERTY) // /dev/dvb0.fe0などに対して実施
DvbManager::tune() はATSC決め打ちの場所があって、DVBやISDBなど他の放送規格には対応していないように見えます。
CHANNEL_MAP_SCAN_FILEはスキャンするチャンネルの一覧が書いてあるファイルのリソースIDが並んでいます。
private static final int CHANNEL_MAP_SCAN_FILE[] = { R.raw.ut_us_atsc_center_frequencies_8vsb, R.raw.ut_us_cable_standard_center_frequencies_qam256, R.raw.ut_us_all, R.raw.ut_kr_atsc_center_frequencies_8vsb, R.raw.ut_kr_cable_standard_center_frequencies_qam256, R.raw.ut_kr_all, R.raw.ut_kr_dev_cj_cable_center_frequencies_qam256, R.raw.ut_euro_dvbt_all, R.raw.ut_euro_dvbt_all, R.raw.ut_euro_dvbt_all };
ちなみにファイルはpackages/apps/TV/usbtuner-res/rawの下にあります。もし新しいファイルfile_nameを追加したいときは、ディレクトリの下にファイルを追加し、CHANNEL_MAP_SCAN_FILEにR.raw.file_nameのように、追加すれば良いみたいです。
TV視聴の際は、チューニングとMPEG2-TSデータを受け取って、セクション解析する処理が行われるようです。
チューニングについては、チャネルスキャンで紹介したTunerHal::tune() が使われますので割愛して、TSデータを受け取るところを調べます。どうも下記のようになっているようです。
TunerTsStreamer::StreamingThread::run() @ src/com/android/tv/tuner/source/TunerTsStreamer.java TunerHal::readTsStream() @ src/com/android/tv/tuner/TunerHal.java TunerHal::nativeWriteInBuffer() Java_com_android_tv_tuner_TunerHal_nativeWriteInBuffer() @ jni/tunertvinput_jni.cpp DvbManager::readTsStream() @ jni/DvbManager.cpp read() // /dev/dvb0.dvr0など EventDetector::feedTSStream() @ src/com/android/tv/tuner/tvinput/EventDetector.java TsParser::feedTSData() @ src/com/android/tv/tuner/ts/TsParser.java TsParser::feedTSPacket() Stream::feedData() SectionStream::handleData() SectionParser::parseSections() @ src/com/android/tv/tuner/ts/SectionParser.java
詳細に調べ切れていませんが、動画や音声データはcom.android.tv.tunerではあまり触らないようです。ExoPlayerに丸投げですかね?
Namazuの延命策として、検索文字列にUTF-8文字列を投げられるようにしました。
といっても、オリジナルのCGIをnamazu.cgi → namazu2.cgiにリネームして、下記のラッパーCGIを用意しただけです。
SakuraはPerlが2種類(/usr/bin/perl = perl 5.8, /usr/bin/perl5 = perl 5.14)入っていて、perl 5.14だけText::Iconvがインストールされています。しかしなぜか @INCにパスが通っていません。仕方ないので、かなりダサいですが、強引にパスを通しています。
#!/usr/bin/perl5
use lib '/usr/local/lib/perl5/site_perl/5.14/mach';
use CGI;
use Text::Iconv;
my $cv = Text::Iconv->new("UTF-8", "EUC-JP");
my $cgi = CGI->new;
my $q_cv = $cv->convert(scalar $cgi->param('query'));
$cgi->param('query', $q_cv);
$q_str = $cgi->query_string();
print "Location: namazu2.cgi?" . $q_str . "\n\n";
今回スクリプトを書いていて初めて知ったのですがCGIのparam関数を読んでスカラ値を受けたい場合scalar $cgi->param('query') という書き方をした方が良いそうです。
理由の詳細はこのブログ(New Class of Vulnerability in Perl Web Applications)に載っていますが、ハッシュで受けると意図せず他の値を上書きしてしまう可能性があるので、明示的にスカラとして受け取るべき、ということらしいです。
Perl初心者の私がなぜ気づいたかというと、動作テストをしたときにApacheの動作ログに、下記のWarningが記録されたからです。
AH01215: CGI::param called in list context from /home/katsuhiro/public_html/namazu/aaa.cgi line 8, this can lead to vulnerabilities. See the warning in "Fetching the value or values of a single named parameter" at /usr/share/perl5/CGI.pm line 412.
初心者にはエラーメッセージだけだと意味がわかりませんが、Warningメッセージに言われるがまま /usr/share/perl5/CGI.pm 412行目を見ると、
# list context can be dangerous so warn:
# http://blog.gerv.net/2014.10/new-class-of-vulnerability-in-perl-web-applications
if ( wantarray && $LIST_CONTEXT_WARN == 1 ) {
my ( $package, $filename, $line ) = caller;
if ( $package ne 'CGI' ) {
$LIST_CONTEXT_WARN++; # only warn once
warn "CGI::param called in list context from $filename line $line, this can lead to vulnerabilities. "
. 'See the warning in "Fetching the value or values of a single named parameter"';
}
}
危険な理由として、先ほど紹介したブログのURLを書いてくれていました。親切ですね。ブログを読んだので何となくわかりましたが、説明なしでは若干難解なコードかもしれません…。
目次: Kindle
Kindleの本は「ダブって買うことがない」利点があります。
が、例外もあって、今回ハマってしまいました。
Amazonの電子書籍には必ずASIN(Amazon Standard Identification Number)というユニークなIDが振られています。AmazonストアのURLの最後に付いている、謎の英数字、あれがASINです。
Kindleストアで本を選択したとき、自動的に購入済みのASINかどうか(=その本を持っているかどうか)チェックされ、購入済みか否かで、ストアに表示されるボタンが変わります。例えば500円の本なら、
このように3パターンのうち適切なボタンが表示されます。また、ブラウザの戻る機能で購入ページに戻り、同じ購入ボタンを押しても「あなた、もう買ってるでしょ?」って言われます。Kindleのダブり防止機能はかなり強力です。
この素敵な機能のおかげで、同じ本を2冊買うことはほぼありません。
Kindleのダブり防止は「同じ本なら、ずっと同じASIN」という前提に依存していますので、この前提が崩れてしまうと、チェックが効かず、同じ本を2回買えてしまいます。例えば、下記のような場合です。
私が今回ハマったパターンは後者でした。オンラインThe Comicという本の9巻目で、同じ本のようですが、なぜか ASIN: B07CH794VZ(以降、旧版)と、ASIN: B07CSVJYL2(以降、新版)の2つのASIDが存在しています。そのため、
このような経緯で2冊買ってしまいました。ちなみに現在、旧版は買えず、評価ページだけが悲しく残っています。
Amazonのカスタマーサポートに連絡したところ、新版の購入を取り消して、返金してもらうことができました。またカスタマーサポートの方が言うには「ストア内で本の入れ替えはある」ようです。
今回のようなASINの付け替えは良くあるのか?珍しいのか?ちょっと気になります。あまり気軽にASINの付け替えをやられますと、購入、未購入の管理が結構辛くなります。
しかしまあ、今まで6,000冊ほど買ってきて、今回初めてASINの付け替えに当たったので、体感的にそんなに頻度は高くないように思います。普通に楽しむ程度なら、まず困らない頻度だとは思います。
このサイトにはNamazuを使った検索機能を設置していますが、撤去したいと思っています。NamazuはSakuraのサーバーのPerlのバージョンが入れ替わるたびに動かなくなってメンテナンスが面倒、既に開発が止まっている、など今後が不安です。
一番困っている点はUTF-8に対応していないことで、日記の表示されているページ(UTF-8)から、日本語を入力して検索すると文字化けしてしまいます。
実はNamazuの検索結果ページはEUC-JPで出力されるので、一度、検索結果ページを表示してから、もう一回、日本語で入力すれば文字化けしません。しかし検索機能を使う人はそんなこと知りませんから、ナニコレ…日本語検索できないじゃん……。と思ったはずです。
Namazuの代わりを探して、お試しでGoogleカスタム検索の検索ボックスを設置しました。
検索ボックスを設置した感想としては、手間要らず、動作も良好、これは便利です。色々なサイトで設置されているのも納得できます。少し使ってみた感想としては、Namazuと比べるとGoogleの検索結果はやや少なく出ると思います。
Google先生は、相手が有名サイトであれば、多少単語が間違っていようと、見たかった検索結果を返してくれます。ここのように無名なサイトだと、ドンピシャのキーワードを入れても、検索結果に出ないことがあります。おそらくこのサイトはGoogleにあまりクロールされていないのでしょう。
Googleの検索結果はどうしようもないので、NamazuのUTF-8文字化け問題を直した方が良いのかなあ。うーむ。
< | 2018 | > | ||||
<< | < | 07 | > | >> | ||
日 | 月 | 火 | 水 | 木 | 金 | 土 |
1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 | 23 | 24 | 25 | 26 | 27 | 28 |
29 | 30 | 31 | - | - | - | - |
合計:
本日: