BLOGSpeee開発陣による技術情報発信ブログです。バックエンド技術Tweetこんにちは。二次元エンジニアです。春アニメが軒並み終盤を迎え、新アニメも出そろい、暑い夏を感じる季節となりました。皆様は来期どんなアニメをご覧になりますでしょうか?僕は「ソードアートオンラインII」と「アルドノア・ゼロ」と「Free!」と「グラスリップ」と・・・などなど合計32本ほど視聴予定です。いつものアニメ一覧さて、今回はアニメに少し関連したネタです。週数十本も見るような方はシステマティックに録画と視聴を運用していると思います。僕の周りでよくあるパターンは「torne最強杉ワロタ組」「リアルタイムで見ないとかw組」「レコーダーあるよ組」そして「PT3のようなPC用チューナで録画して保存している組」だと思います。今回は一番最後の「PT3のようなPC用チューナで録画して保存している組」の方々向けの記事となっています。わかさトラップとは?皆さんはわかさトラップをご存知でしょうか?わかさトラップとは巷で行われている地デジ録画をしたtsファイルに稀に起こる現象です。tsファイルをmp4などにエンコードしようとした際にSD画質からHD画質への映像の切り替わりポイントでエンコードが失敗してしまいます。T○ky○MXさんの深夜のアニメを録画した時に起こるようです。数年ほど前に「わかさ◯活」のCMでよく起こっていたため、この呼び方になっています。今回は、このわかさトラップを回避するためのtsパケットの読み方を解説したいと思います。この記事はあくまでtsパケットを勉強し、バイナリを読む事に対する敷居を下げる事が目的です。※地上デジで録画した物を私的利用の範囲を超えて利用する事は犯罪です。不正利用ダメ!絶対ダメ!次の新しいアニメに出会うため、積極的に円盤を買いましょう!なお、今回説明する内容は以下に全部書いてあります。合法的に入手したtsファイルバイナリエディタ2進数、16進数に対する強い心python(バージョンは何でも可)tsパケットってなに?地上デジタル放送ではMPEG2-TSという規格でデータが送信されています。この中には、フルセグのデータ、ワンセグのデータ、番組情報、データ放送の情報等がまとめて入っています。これらのデータは全てパケットの中身を示すtsパケットバイトを含む、188バイトのパケットの連続で構成されています。従って、実際にデータを読む時にはこのパケットを次々に解釈してデータを取り出すことになります。番組情報の切り替わりポイントで頭出しを行うことで回避をしたいと思います。このPMTパケットを探すため、tsヘッダ部分を説明します。tsヘッダを読む一つのパケットの先頭に存在するtsパケットは以下のようになっています。sync_byte(同期ワード)transport_error_indicator(トランスポートエラーインジケータ)payload_unit_start_indicator(ペイロードユニットスタートインジケータ)transport_priority(トランスポート優先度)PID(プログラムID)transport_scrambling_control(トランスポートスクランブルコントロール)adaptation_field_control(アダプテーションフィールドコントロール)continuity_control(巡回カウンター)この中で大事なパラメータは同期ワード、payload_unit_start_indicator、PIDの部分です。tsパケットの同期ワードは必ず「0x47」になります。188バイトのパケットサイズでは入りきらない場合、複数パケットに連続してデータが入ります。後続なのかそうでないのかを確認しなければ上手く動きません。後続パケットの場合「0」となります。最後にPIDはパケットを識別するIDになります。このPIDを手繰っていくことで、データを読みます。各サービスのストリーム情報が入っているPMTパケットのPIDが格納されています。そもそもtsファイルには複数のストリームが格納できる仕様になっているため、このPMTパケットにストリームの情報が格納されます。PATパケットのPIDは「0x00」で予約されています。PID=0000000000000)を探すこととなります。このPATパケットを読みます。http://www.arib.or.jp/english/html/overview/doc/2-STD-B10v4_7.pdfの16ページ目に詳細が載っています。1D」のうち先頭の「0x00」については区切り文字なので無視します。下図のPAT構造に照らし合わせながら見るのですが、ここで大事なのはセクション長の部分です。一つのPATの中に可変個のPMTパケットのPIDが格納されるのためです。このパケットではセクション長は「0xB01D」から先頭4bitを切った「0x001D」となります。したがって、29バイトとなります。直後のバイトから0xFFまでちょうど29バイトになっています。実際のPMT情報はこのセクション長データの直後から9バイト(PAT情報5バイト+番組情報4バイト)先から入っています。上図のオレンジ部分です。このデータの場合は4つ入っています。最後の4バイト(上図緑)は誤り検知用のCRCとなっています。tsパケットは188バイト全てを使わなかった場合はすべて1埋めされるため、CRCより後は0xFFの連続となっています。C9」の4つのPMT情報があるようです。PATデータ構造の繰り返し部分の下の方がこれらのPMT情報の構造になっています。今回はPMTパケットのPIDが必要なので、これらの下部13ビットが必要になります。「0x1FFF」で&を取って、それぞれマスクをかけます。今回は「0x0101」「0x0102」「0x1FC8」「0x1FC9」の4つのPIDが抽出できました。PMTパケットを探します先ほど見つけたPMTのPIDからPMTパケットを探します。今回は「0x0101」を探します。私のプログラムでは決めうちしています。単純にバイナリエディタで検索できないので、ちょっとプログラムを書きます。exit()f.close()今回の場合は「0x47610115」というtsヘッダが見つかりました。これをバイナリエディタで検索すると下図のようなパケットをゲットしました。PMTパケットは可変部分(赤い部分)が2カ所含まれるため多少面倒になっています。スキップしパケットの先頭から16,17バイト目の番組情報長を取得します。この値の分だけ次の記述子領域1の繰り返し回数があります。パケットの赤枠の部分の「0xF00F」が番組情報長です。上部3bitは「1111」になっているので、「0xF」つまり15が番組情長です。記述子領域1は1セクション8bitなので15バイトあるようです。ここから15バイト先は「0x02E121F0」になります。分かりにくいので、形式を変えて説明します。先頭0x02はstream_typeで動画を意味する。先頭0x0Fは音声を意味する。#可変長部分#三つ目のstream以降はstream_typeが0x0Dでちょっと良く分かりません。FFここまで来るとほぼわかさトラップは回避できます。わかさトラップの仕込まれた動画では、このPMT内の動画streamのPIDが番組の切り替わり時に変化してしまいます。この動画では、「0x121」であった動画streamのPIDが、本編が始まると「0x111」に変わります。ffmpegではこの変化に対応できず、「0x121」のstreamを見続けてしまい、エンコードが失敗します。わかさトラップ回避わかさトラップが起こる原因は分かりました。これを修正したいとおもいます。手順は以下の通りです。1ファイル中のPMT情報を見て、動画streamのPIDが変化している点を見つける。2変化点のパケット以前のデータを全て捨てて、頭出しを行う。3保存して、エンコードする。読み出し側は頭出しされたファイルに含まれるPATパケットとPMTパケットをみるため、188バイトの構造が壊れない限り問題ありません。PATパケットとPMTパケットは複数回出現するため、ほぼ問題ないでしょう。サンプルプログラムです。ファイル全体をスキャンすると時間がかかるため、5分の1だけスキャンしています。output_file)終わりに今のところ上のサンプルプログラムで特に問題が出た事はありません。ひどく遅いプログラムなので、書き換えたいことだけが。参考のPDFを見ると分かるのですが、tsが読めると色々分かるようです。CMカットとかも出来ると非常にありがたい事になります。みなさんも触ってみてください。最後に地上デジで録画した物を私的利用の範囲を超えて利用する事は犯罪です。不正利用ダメ!絶対ダメ!面白いアニメを作ってくれる制作会社がつぶれないように円盤を買いましょう!ウィーザードバリスターズのブルーレイ初動610枚の1枚は僕です。この記事でアニメライフが充実した方がいれば幸いです。小ネタ今回はバイナリエディタにHexFiendを使っています。MACだと0xEDってのもありますが、Hexで検索のできるHexFiendの方がいいかんじです。