arib_std_b25-0.2.5.lzhから初期インポート

This commit is contained in:
tslroom 2012-07-23 19:32:01 +09:00
commit 8cc9fe12a4
19 changed files with 6121 additions and 0 deletions

372
README.txt Normal file
View File

@ -0,0 +1,372 @@
【名称】
 ARIB STD-B25 仕様確認テストプログラムソースコード
【バージョン】
 0.2.5
【作者】
 茂木 和洋 (MOGI, Kazuhiro)
 kazhiro@marumo.ne.jp
【一次配布元】
 http://www.marumo.ne.jp/db2012_2.htm#13 又は
 あるいは
 http://www.marumo.ne.jp/junk/arib_std_b25-0.2.5.lzh
【目的】
 ARIB STD-B25 の仕様を理解する為の、参考用の実装として公開
【背景】
 2011 年 7 月の地上アナログ放送停波を控え、廉価な地上デジタル放送
 受信機の販売が待たれている
 しかし、ARIB の標準文書はわざと判りにくく書いて開発費をかさませ
 ようとしているとしか思えないほどに意味不明瞭な記述になっており
 このままでは低価格受信機の開発など不可能に思える
 そこで、自分なりに ARIB 標準文書を読み、理解した範囲をソース
 コードの形にまとめて公開することにした
 このコードが安価な受信機の開発の一助となることを期待する
 なお、あくまでも仕様理解を目的としたものであるため、ビルド済み
 バイナリファイルは配布しない
【実装した範囲】
 CA システム (B-CAS カード関連) を中心に ECM(table_id=0x82) の処理と
 ストリーム暗号の復号処理、EMM(table_id=0x84) の処理までを実装した
 EMM メッセージ (table_id=0x85) 関連は未実装となっている
【プログラムの動作環境】
 ISO 7816 対応の IC カードリーダがインストールされた Windows PC を
 想定動作環境とする
 ISO 7816 対応スマートカードリーダーは一般に
 「住基カード対応 IC カードリーダ」「e-Tax 対応 IC カードリーダ」
 などとして 4000 円程度で販売されているものが利用可能である
 日立マクセル製の HX-520UJJ と NTT コミュニケーションズの SCR3310
 で正常に動作することを確認している
【ソースコードのライセンスについて】
 ・ソースコードを利用したことによって、特許上のトラブルが発生しても
  茂木 和洋は責任を負わない
 ・ソースコードを利用したことによって、プログラムに問題が発生しても
  茂木 和洋は責任を負わない
 上記 2 条件に同意して作成された二次的著作物に対して、茂木 和洋は
 原著作者に与えられる諸権利を行使しない
【プログラムの構成】
 ・arib_std_b25.h/c
  ARIB STD-B25 記載の処理を行うためのモジュール
  MPEG-2 TS の分離、CA システム (B-CAS カード) 機能の呼び出し、
  MULTI2 復号機能の呼び出し等を担当する
 ・ts_section_parser.h/c
  MPEG-2 TS のセクション形式データの分割処理を担当する
 ・b_cas_card.h/c
  CA システム (B-CAS カード) のリソース管理および直接の制御を
  担当する
 ・multi2.h/c
  MULTI2 暗号の符号化と復号を担当する
 ・td.c
  テストドライバ
  PAT/PMT/ECM を含む MPEG-2 TS ファイルを読み込み、復号後の
  MPEG-2 TS ファイルを出力する
  コマンドラインオプションで MULTI2 暗号のラウンド数を指定可能
  ラウンド数を指定しない場合の初期値は 4
  このラウンド数 4 は MULTI2 用語では 32 に相当する
  ARIB STD-B25 では MULTI2 のラウンド数は非公開パラメータだが
  総当たりで実際のラウンド数は推定可能である
【処理の流れ】
 ・起動時
  1 アプリケーションは B_CAS_CARD モジュールのインスタンスを
   作成し、B_CAS_CARD モジュールに、初期化を依頼する
  1.a B_CAS_CARD モジュールは WIN32 API のスマートカード関連
    API を呼び出し、CA システムに接続する
  1.b B_CAS_CARD モジュールは ARIB STD-B25 記載の「初期条件
    設定コマンドを CA システムに発行し、システム鍵 (64 byte)
    初期 CBC 状態 (8 byte) を受け取る
  2 アプリケーションは ARIB_STD_B25 モジュールのインスタンスを
   作成し、B_CAS_CARD モジュールを ARIB_STD_B25 モジュールに
   登録する
 ・データ処理時
  1 アプリケーションは ARIB_STD_B25 モジュールに順次データを
   提供し、ARIB_STD_B25 モジュールから処理完了データを受け
   取ってファイルに出力していく
  ・ARIB_STD_B25 モジュール内
   1 TS パケットのユニットサイズ (188/192/204 などが一般的) が
    特定されていない場合 8K まで入力データをバッファしてから、
    ユニットサイズを特定する
    ユニットサイズが特定できなかった場合は、エラー終了する
   2 PAT が発見されていない場合、PAT が発見できるまで入力
    データをバッファし続ける
    PAT が発見できずにバッファサイズが 16M を超過した場合
    エラー終了する
    PAT が発見できた場合、プログラム配列を作成し PID マップ
    配列に登録する
   3 PAT に登録されていた PMT すべてが発見されるか、どれか
    ひとつの PMT で 2 個目のセクションが到着するまで入力
    データをバッファし続ける
    上記条件を満たさずにバッファサイズが 32M を超過した場合
    エラー終了する
    PMT が到着する毎に ECM の有無を確認し、ECM が存在する
    場合はデクリプタを作成してプログラムに所属するストリーム
    と PID マップ上で関連付ける
   4 PMT に登録されていた ECM すべてが発見されるか、どれか
    ひとつの ECM で 2 個目のセクションが到着するまで入力
    データをバッファし続ける
    上記条件を満たさずにバッファサイズが 32M を超過した場合
    エラー終了する
    各 ECM に対して、最初のセクションデータが到着した時点で
    MULTI2 モジュールのインスタンスをデクリプタ上に作成する
    ECM セクションデータは B_CAS_CARD モジュールに提供して
    スクランブル鍵を受け取り、MULTI2 モジュールにシステム鍵、
    初期 CBC 状態、スクランブル鍵を渡し、MULTI2 復号の準備を
    行う
   5.a 暗号化されている TS パケットであれば、PID から対応
     ECM ストリームを特定し、デクリプタの MULTI2 モジュー
     ルに復号させて出力バッファに積む
    
   5.b 暗号化されていない TS パケットであれば、そのまま出力
     バッファに積む
   5.c CAT を検出した場合、EMM の PID を取得して EMM の処理
     準備を行う
   5.d EMM を受け取った場合、B-CAS カード ID と比較し、自分
     宛ての EMM であれば B-CAS カードに引き渡して処理させる
     # EMM 処理オプションが指定されている場合
   6 ECM が更新された場合、B_CAS_CARD モジュールに処理を
    依頼し、出力されたスクランブル鍵を MULTI2 モジュールに
    登録する
   7 PMT が更新された場合、ECM PID が変化していれば新たに
    デクリプタを作成して 4 に戻る
   8 PAT が更新された場合、プログラム配列を破棄して
    3 に戻る
 ・終了時
  1 各モジュールが確保したリソースを解放する
【更新履歴】
 ・2012, 2/13 - ver. 0.2.5
  WOWOW でノンスクランブル <-> スクランブル切り替え後に復号が
  行われないことがあるバグを修正
  http://www.marumo.ne.jp/db2012_2.htm#13 又は
  http://www.marumo.ne.jp/junk/arib_std_b25-0.2.5.lzh
 ・2009, 4/19 - ver. 0.2.4
  終端パケットが野良パケット (PMT に記載されていない PID の
  パケット) だった場合に、ECM が 1 つだけでも復号が行われない
  バグを修正
  transport_error_indicator が立っている場合はパケット処理を
  行わず、そのまま素通しするように変更
  http://www.marumo.ne.jp/db2009_4.htm#19 又は
  http://www.marumo.ne.jp/junk/arib_std_b25-0.2.4.lzh
 ・2008, 12/30 - ver. 0.2.3
  CA_descriptor の解釈を行う際に CA_system_id が B-CAS カード
  から取得したものと一致するか確認を行うように変更
  http://www.marumo.ne.jp/db2008_c.htm#30 又は
  http://www.marumo.ne.jp/junk/arib_std_b25-0.2.3.lzh
 ・2008, 11/10 - ver. 0.2.2
  修正ユリウス日から年月日への変換処理をより正確なものへ変更
  TS パケットサイズの特定方法を変更
  http://www.marumo.ne.jp/db2008_b.htm#10 又は
  http://www.marumo.ne.jp/junk/arib_std_b25-0.2.2.lzh
 ・2008, 4/9 - ver. 0.2.1
  PAT 更新時に復号漏れが発生していたバグを修正
  (ver. 0.2.0 でのエンバグ)
  野良 PID (PMT に記載されていないストリーム) が存在した場合
  TS 内の ECM がひとつだけならば、その ECM で復号する形に変更
  EMM の B-CAS カードへの送信をオプションで選択可能に変更 (-m)
  進捗状況の表示をオプションで選択可能に変更 (-v)
  通電制御情報 (EMM受信用) を表示するオプションを追加 (-p)
  http://www.marumo.ne.jp/db2008_4.htm#9 又は
  http://www.marumo.ne.jp/junk/arib_std_b25-0.2.1.lzh
 ・2008, 4/6 - ver. 0.2.0
  EMM 対応
  利用中の B-CAS カード ID 向けの EMM を検出した場合、EMM を
  B-CAS カードに渡す処理を追加
  ECM 処理の際に未契約応答が返された場合、処理負荷軽減の為、
  以降、その PID の ECM を B-CAS カードで処理しないように変
  更 (EMM を処理した場合は再び ECM を処理するように戻す)
  進捗を nn.nn% の書式で標準エラー出力に表示するように変更
  
  http://www.marumo.ne.jp/db2008_4.htm#6 又は
  http://www.marumo.ne.jp/junk/arib_std_b25-0.2.0.lzh
 ・2008, 3/31 - ver. 0.1.9
  MULTI2 モジュールのインスタンスが未作製の状況で、MULTI2 の
  機能を呼び出して例外を発生させることがあったバグを修正
  # パッチを提供してくれた方に感謝
  http://www.marumo.ne.jp/db2008_3.htm#31 又は
  http://www.marumo.ne.jp/junk/arib_std_b25-0.1.9.lzh
 ・2008, 3/24 - ver. 0.1.8
  -s オプション (NULL パケットの削除) を追加
  -s 1 で NULL パケットを出力ファイルには保存しなくなる
  デフォルトは -s 0 の NULL パケット保持
  http://www.marumo.ne.jp/db2008_3.htm#24 又は
  http://www.marumo.ne.jp/junk/arib_std_b25-0.1.8.lzh
 ・2008, 3/17 - ver. 0.1.7
  arib_std_b25.h に「extern "C" {」を閉じるコードがなかった問題
  (C++ コードから利用する場合にコンパイルエラーを発生させる) を
  修正
  TS パケットの中途でストリームが切り替わるケースで問題が発生し
  にくくなるように、arib_std_b25.c 内のコードを修正
  http://www.marumo.ne.jp/db2008_3.htm#17 又は
  http://www.marumo.ne.jp/junk/arib_std_b25-0.1.7.lzh
 ・2008, 3/16 - ver. 0.1.6
  PMT 更新の際、ECM 関連の状況が変更 (スクランブル - ノンスク
  ランブルの切り替えや、ECM PID の変更等) が行われても、それが
  反映されていなかった問題を修正
  http://www.marumo.ne.jp/db2008_3.htm#16 又は
  http://www.marumo.ne.jp/junk/arib_std_b25-0.1.6.lzh
 ・2008, 2/14
  readme.txt (このファイル) を修正
  ソースコードのライセンスについての記述を追加
 ・2008, 2/12 - ver. 0.1.5
  PMT の更新に伴い、どのプログラムにも所属しなくなった PID (スト
  リーム) でパケットが送信され続けた場合、そのパケットの復号が
  できなくなっていた問題を修正
  http://www.marumo.ne.jp/db2008_2.htm#12 又は
  http://www.marumo.ne.jp/junk/arib_std_b25-0.1.5.lzh
 ・2008, 2/2 - ver. 0.1.4
  ver. 0.1.3 での PMT 処理方法変更に問題があり、PMT が更新された
  場合、それ以降で正常な処理が行えなくなっていたバグを修正
  B-CAS カードとの通信でエラーが発生した場合のリトライ処理が機能
  していなかったバグを修正
  http://www.marumo.ne.jp/db2008_2.htm#2 又は
  http://www.marumo.ne.jp/junk/arib_std_b25-0.1.4.lzh
 ・2008, 2/1 - ver. 0.1.3
  有料放送等で未契約状態の B-CAS カードを使った際に、鍵が取得で
  きていないにもかかわらず、間違った鍵で復号をしていた問題に対処
  鍵が取得できなかった ECM に関連付けられたストリームでは復号を
  行わず、スクランブルフラグを残したまま入力を素通しする形に変更
  鍵が取得できない ECM が存在する場合、終了時にチャネル番号と
  B-CAS カードから取得できたエラー番号を警告メッセージとして表示
  する形に変更
  暗号化されていないプログラムで例外を発生させていたバグを修正
  http://www.marumo.ne.jp/db2008_2.htm#1 又は
  http://www.marumo.ne.jp/junk/arib_std_b25-0.1.3.lzh
 ・2008, 1/11 - ver. 0.1.2
  デジタル BS 放送等で、PAT に登録されているのに、ストリーム内で
  PMT が一切出現しないことがある場合に対応
  PMT 内の記述子領域 2 に CA_descriptor が存在する場合に対応する
  ため arib_std_b25.c 内部での処理構造を変更
  別プログラムと同時実行するためにスマートカードの排他制御指定を
  変更
  http://www.marumo.ne.jp/db2008_1.htm#11 又は
  http://www.marumo.ne.jp/junk/arib_std_b25-0.1.2.lzh
 ・2008, 1/7 - ver. 0.1.1
  セクション (PAT/PMT/ECM 等) が複数の TS パケットに分割されている
  場合に、正常に処理できなかったり、例外を発生をさせることがある
  バグを修正
  http://www.marumo.ne.jp/db2008_1.htm#7 又は
  http://www.marumo.ne.jp/junk/arib_std_b25-0.1.1.lzh
 ・2007, 11/25 - ver. 0.1.0
  公開
  http://www.marumo.ne.jp/db2007_b.htm#25 又は
  http://www.marumo.ne.jp/junk/arib_std_b25-0.1.0.lzh

20
arib_std_b25.sln Normal file
View File

@ -0,0 +1,20 @@

Microsoft Visual Studio Solution File, Format Version 9.00
# Visual C++ Express 2005
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "arib_std_b25", "arib_std_b25.vcproj", "{6E77C1AC-A31A-49B9-9A52-9FE1E03B8FEC}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Win32 = Debug|Win32
Release|Win32 = Release|Win32
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{6E77C1AC-A31A-49B9-9A52-9FE1E03B8FEC}.Debug|Win32.ActiveCfg = Debug|Win32
{6E77C1AC-A31A-49B9-9A52-9FE1E03B8FEC}.Debug|Win32.Build.0 = Debug|Win32
{6E77C1AC-A31A-49B9-9A52-9FE1E03B8FEC}.Release|Win32.ActiveCfg = Release|Win32
{6E77C1AC-A31A-49B9-9A52-9FE1E03B8FEC}.Release|Win32.Build.0 = Release|Win32
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
EndGlobal

259
arib_std_b25.vcproj Normal file
View File

@ -0,0 +1,259 @@
<?xml version="1.0" encoding="shift_jis"?>
<VisualStudioProject
ProjectType="Visual C++"
Version="8.00"
Name="arib_std_b25"
ProjectGUID="{6E77C1AC-A31A-49B9-9A52-9FE1E03B8FEC}"
RootNamespace="arib_std_b25"
Keyword="Win32Proj"
>
<Platforms>
<Platform
Name="Win32"
/>
</Platforms>
<ToolFiles>
</ToolFiles>
<Configurations>
<Configuration
Name="Debug|Win32"
OutputDirectory="$(SolutionDir)$(ConfigurationName)"
IntermediateDirectory="$(ConfigurationName)"
ConfigurationType="1"
CharacterSet="2"
>
<Tool
Name="VCPreBuildEventTool"
/>
<Tool
Name="VCCustomBuildTool"
/>
<Tool
Name="VCXMLDataGeneratorTool"
/>
<Tool
Name="VCWebServiceProxyGeneratorTool"
/>
<Tool
Name="VCMIDLTool"
/>
<Tool
Name="VCCLCompilerTool"
UseUnicodeResponseFiles="false"
Optimization="0"
PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE"
MinimalRebuild="true"
BasicRuntimeChecks="3"
RuntimeLibrary="1"
UsePrecompiledHeader="0"
WarningLevel="3"
Detect64BitPortabilityProblems="false"
DebugInformationFormat="4"
/>
<Tool
Name="VCManagedResourceCompilerTool"
/>
<Tool
Name="VCResourceCompilerTool"
/>
<Tool
Name="VCPreLinkEventTool"
/>
<Tool
Name="VCLinkerTool"
AdditionalDependencies="winscard.lib"
OutputFile="$(OutDir)\b25.exe"
LinkIncremental="2"
GenerateDebugInformation="true"
SubSystem="1"
TargetMachine="1"
/>
<Tool
Name="VCALinkTool"
/>
<Tool
Name="VCManifestTool"
/>
<Tool
Name="VCXDCMakeTool"
/>
<Tool
Name="VCBscMakeTool"
/>
<Tool
Name="VCFxCopTool"
/>
<Tool
Name="VCAppVerifierTool"
/>
<Tool
Name="VCWebDeploymentTool"
/>
<Tool
Name="VCPostBuildEventTool"
/>
</Configuration>
<Configuration
Name="Release|Win32"
OutputDirectory="$(SolutionDir)$(ConfigurationName)"
IntermediateDirectory="$(ConfigurationName)"
ConfigurationType="1"
CharacterSet="2"
WholeProgramOptimization="1"
>
<Tool
Name="VCPreBuildEventTool"
/>
<Tool
Name="VCCustomBuildTool"
/>
<Tool
Name="VCXMLDataGeneratorTool"
/>
<Tool
Name="VCWebServiceProxyGeneratorTool"
/>
<Tool
Name="VCMIDLTool"
/>
<Tool
Name="VCCLCompilerTool"
UseUnicodeResponseFiles="false"
PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE"
RuntimeLibrary="0"
UsePrecompiledHeader="0"
WarningLevel="3"
Detect64BitPortabilityProblems="false"
DebugInformationFormat="3"
/>
<Tool
Name="VCManagedResourceCompilerTool"
/>
<Tool
Name="VCResourceCompilerTool"
/>
<Tool
Name="VCPreLinkEventTool"
/>
<Tool
Name="VCLinkerTool"
AdditionalDependencies="winscard.lib"
OutputFile="$(OutDir)\b25.exe"
LinkIncremental="1"
GenerateDebugInformation="true"
SubSystem="1"
OptimizeReferences="2"
EnableCOMDATFolding="2"
TargetMachine="1"
/>
<Tool
Name="VCALinkTool"
/>
<Tool
Name="VCManifestTool"
/>
<Tool
Name="VCXDCMakeTool"
/>
<Tool
Name="VCBscMakeTool"
/>
<Tool
Name="VCFxCopTool"
/>
<Tool
Name="VCAppVerifierTool"
/>
<Tool
Name="VCWebDeploymentTool"
/>
<Tool
Name="VCPostBuildEventTool"
/>
</Configuration>
</Configurations>
<References>
</References>
<Files>
<Filter
Name="ソース ファイル"
Filter="cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx"
UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}"
>
<File
RelativePath=".\src\arib_std_b25.c"
>
</File>
<File
RelativePath=".\src\b_cas_card.c"
>
</File>
<File
RelativePath=".\src\multi2.c"
>
</File>
<File
RelativePath=".\src\td.c"
>
</File>
<File
RelativePath=".\src\ts_section_parser.c"
>
</File>
</Filter>
<Filter
Name="ヘッダー ファイル"
Filter="h;hpp;hxx;hm;inl;inc;xsd"
UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}"
>
<File
RelativePath=".\src\arib_std_b25.h"
>
</File>
<File
RelativePath=".\src\arib_std_b25_error_code.h"
>
</File>
<File
RelativePath=".\src\b_cas_card.h"
>
</File>
<File
RelativePath=".\src\b_cas_card_error_code.h"
>
</File>
<File
RelativePath=".\src\multi2.h"
>
</File>
<File
RelativePath=".\src\multi2_error_code.h"
>
</File>
<File
RelativePath=".\src\portable.h"
>
</File>
<File
RelativePath=".\src\ts_common_types.h"
>
</File>
<File
RelativePath=".\src\ts_section_parser.h"
>
</File>
<File
RelativePath=".\src\ts_section_parser_error_code.h"
>
</File>
</Filter>
<Filter
Name="リソース ファイル"
Filter="rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav"
UniqueIdentifier="{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}"
>
</Filter>
</Files>
<Globals>
</Globals>
</VisualStudioProject>

2652
src/arib_std_b25.c Normal file

File diff suppressed because it is too large Load Diff

60
src/arib_std_b25.h Normal file
View File

@ -0,0 +1,60 @@
#ifndef ARIB_STD_B25_H
#define ARIB_STD_B25_H
#include "portable.h"
#include "b_cas_card.h"
typedef struct {
uint8_t *data;
int32_t size;
} ARIB_STD_B25_BUFFER;
typedef struct {
int32_t program_number; /* channel */
int32_t ecm_unpurchased_count;
int32_t last_ecm_error_code;
int32_t padding;
int64_t total_packet_count;
int64_t undecrypted_packet_count;
} ARIB_STD_B25_PROGRAM_INFO;
typedef struct {
void *private_data;
void (* release)(void *std_b25);
int (* set_multi2_round)(void *std_b25, int32_t round);
int (* set_strip)(void *std_b25, int32_t strip);
int (* set_emm_proc)(void *std_b25, int32_t on);
int (* set_b_cas_card)(void *std_b25, B_CAS_CARD *bcas);
int (* reset)(void *std_b25);
int (* flush)(void *std_b25);
int (* put)(void *std_b25, ARIB_STD_B25_BUFFER *buf);
int (* get)(void *std_b25, ARIB_STD_B25_BUFFER *buf);
int (* get_program_count)(void *std_b25);
int (* get_program_info)(void *std_b25, ARIB_STD_B25_PROGRAM_INFO *info, int32_t idx);
} ARIB_STD_B25;
#ifdef __cplusplus
extern "C" {
#endif
extern ARIB_STD_B25 *create_arib_std_b25();
#ifdef __cplusplus
}
#endif
#endif /* ARIB_STD_B25_H */

View File

@ -0,0 +1,25 @@
#ifndef ARIB_STD_B25_ERROR_CODE_H
#define ARIB_STD_B25_ERROR_CODE_H
#define ARIB_STD_B25_ERROR_INVALID_PARAM -1
#define ARIB_STD_B25_ERROR_NO_ENOUGH_MEMORY -2
#define ARIB_STD_B25_ERROR_NON_TS_INPUT_STREAM -3
#define ARIB_STD_B25_ERROR_NO_PAT_IN_HEAD_16M -4
#define ARIB_STD_B25_ERROR_NO_PMT_IN_HEAD_32M -5
#define ARIB_STD_B25_ERROR_NO_ECM_IN_HEAD_32M -6
#define ARIB_STD_B25_ERROR_EMPTY_B_CAS_CARD -7
#define ARIB_STD_B25_ERROR_INVALID_B_CAS_STATUS -8
#define ARIB_STD_B25_ERROR_ECM_PROC_FAILURE -9
#define ARIB_STD_B25_ERROR_DECRYPT_FAILURE -10
#define ARIB_STD_B25_ERROR_PAT_PARSE_FAILURE -11
#define ARIB_STD_B25_ERROR_PMT_PARSE_FAILURE -12
#define ARIB_STD_B25_ERROR_ECM_PARSE_FAILURE -13
#define ARIB_STD_B25_ERROR_CAT_PARSE_FAILURE -14
#define ARIB_STD_B25_ERROR_EMM_PARSE_FAILURE -15
#define ARIB_STD_B25_ERROR_EMM_PROC_FAILURE -16
#define ARIB_STD_B25_WARN_UNPURCHASED_ECM 1
#define ARIB_STD_B25_WARN_TS_SECTION_ID_MISSMATCH 2
#define ARIB_STD_B25_WARN_BROKEN_TS_SECTION 3
#endif /* ARIB_STD_B25_ERROR_CODE_H */

747
src/b_cas_card.c Normal file
View File

@ -0,0 +1,747 @@
#include "b_cas_card.h"
#include "b_cas_card_error_code.h"
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <windows.h>
#include <winscard.h>
/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
inner structures
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
typedef struct {
SCARDCONTEXT mng;
SCARDHANDLE card;
uint8_t *pool;
char *reader;
uint8_t *sbuf;
uint8_t *rbuf;
B_CAS_INIT_STATUS stat;
B_CAS_ID id;
int32_t id_max;
B_CAS_PWR_ON_CTRL_INFO pwc;
int32_t pwc_max;
} B_CAS_CARD_PRIVATE_DATA;
/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
constant values
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
static const uint8_t INITIAL_SETTING_CONDITIONS_CMD[] = {
0x90, 0x30, 0x00, 0x00, 0x00,
};
static const uint8_t CARD_ID_INFORMATION_ACQUIRE_CMD[] = {
0x90, 0x32, 0x00, 0x00, 0x00,
};
static const uint8_t POWER_ON_CONTROL_INFORMATION_REQUEST_CMD[] = {
0x90, 0x80, 0x00, 0x00, 0x01, 0x00, 0x00,
};
static const uint8_t ECM_RECEIVE_CMD_HEADER[] = {
0x90, 0x34, 0x00, 0x00,
};
static const uint8_t EMM_RECEIVE_CMD_HEADER[] = {
0x90, 0x36, 0x00, 0x00,
};
#define B_CAS_BUFFER_MAX (4*1024)
/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
function prottypes (interface method)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
static void release_b_cas_card(void *bcas);
static int init_b_cas_card(void *bcas);
static int get_init_status_b_cas_card(void *bcas, B_CAS_INIT_STATUS *stat);
static int get_id_b_cas_card(void *bcas, B_CAS_ID *dst);
static int get_pwr_on_ctrl_b_cas_card(void *bcas, B_CAS_PWR_ON_CTRL_INFO *dst);
static int proc_ecm_b_cas_card(void *bcas, B_CAS_ECM_RESULT *dst, uint8_t *src, int len);
static int proc_emm_b_cas_card(void *bcas, uint8_t *src, int len);
/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
global function implementation
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
B_CAS_CARD *create_b_cas_card()
{
int n;
B_CAS_CARD *r;
B_CAS_CARD_PRIVATE_DATA *prv;
n = sizeof(B_CAS_CARD) + sizeof(B_CAS_CARD_PRIVATE_DATA);
prv = (B_CAS_CARD_PRIVATE_DATA *)calloc(1, n);
if(prv == NULL){
return NULL;
}
r = (B_CAS_CARD *)(prv+1);
r->private_data = prv;
r->release = release_b_cas_card;
r->init = init_b_cas_card;
r->get_init_status = get_init_status_b_cas_card;
r->get_id = get_id_b_cas_card;
r->get_pwr_on_ctrl = get_pwr_on_ctrl_b_cas_card;
r->proc_ecm = proc_ecm_b_cas_card;
r->proc_emm = proc_emm_b_cas_card;
return r;
}
/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
function prottypes (private method)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
static B_CAS_CARD_PRIVATE_DATA *private_data(void *bcas);
static void teardown(B_CAS_CARD_PRIVATE_DATA *prv);
static int change_id_max(B_CAS_CARD_PRIVATE_DATA *prv, int max);
static int change_pwc_max(B_CAS_CARD_PRIVATE_DATA *prv, int max);
static int connect_card(B_CAS_CARD_PRIVATE_DATA *prv, const char *reader_name);
static void extract_power_on_ctrl_response(B_CAS_PWR_ON_CTRL *dst, uint8_t *src);
static void extract_mjd(int *yy, int *mm, int *dd, int mjd);
static int setup_ecm_receive_command(uint8_t *dst, uint8_t *src, int len);
static int setup_emm_receive_command(uint8_t *dst, uint8_t *src, int len);
static int32_t load_be_uint16(uint8_t *p);
static int64_t load_be_uint48(uint8_t *p);
/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
interface method implementation
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
static void release_b_cas_card(void *bcas)
{
B_CAS_CARD_PRIVATE_DATA *prv;
prv = private_data(bcas);
if(prv == NULL){
/* do nothing */
return;
}
teardown(prv);
free(prv);
}
static int init_b_cas_card(void *bcas)
{
int m;
LONG ret;
DWORD len;
B_CAS_CARD_PRIVATE_DATA *prv;
prv = private_data(bcas);
if(prv == NULL){
return B_CAS_CARD_ERROR_INVALID_PARAMETER;
}
teardown(prv);
ret = SCardEstablishContext(SCARD_SCOPE_USER, NULL, NULL, &(prv->mng));
if(ret != SCARD_S_SUCCESS){
return B_CAS_CARD_ERROR_NO_SMART_CARD_READER;
}
ret = SCardListReaders(prv->mng, NULL, NULL, &len);
if(ret != SCARD_S_SUCCESS){
return B_CAS_CARD_ERROR_NO_SMART_CARD_READER;
}
len += 256;
m = len + (2*B_CAS_BUFFER_MAX) + (sizeof(int64_t)*16) + (sizeof(B_CAS_PWR_ON_CTRL)*16);
prv->pool = (uint8_t *)malloc(m);
if(prv->pool == NULL){
return B_CAS_CARD_ERROR_NO_ENOUGH_MEMORY;
}
prv->reader = (char *)(prv->pool);
prv->sbuf = prv->pool + len;
prv->rbuf = prv->sbuf + B_CAS_BUFFER_MAX;
prv->id.data = (int64_t *)(prv->rbuf + B_CAS_BUFFER_MAX);
prv->id_max = 16;
prv->pwc.data = (B_CAS_PWR_ON_CTRL *)(prv->id.data + prv->id_max);
prv->pwc_max = 16;
ret = SCardListReaders(prv->mng, NULL, prv->reader, &len);
if(ret != SCARD_S_SUCCESS){
return B_CAS_CARD_ERROR_NO_SMART_CARD_READER;
}
while( prv->reader[0] != 0 ){
if(connect_card(prv, prv->reader)){
break;
}
prv->reader += (strlen(prv->reader) + 1);
}
if(prv->card == 0){
return B_CAS_CARD_ERROR_ALL_READERS_CONNECTION_FAILED;
}
return 0;
}
static int get_init_status_b_cas_card(void *bcas, B_CAS_INIT_STATUS *stat)
{
B_CAS_CARD_PRIVATE_DATA *prv;
prv = private_data(bcas);
if( (prv == NULL) || (stat == NULL) ){
return B_CAS_CARD_ERROR_INVALID_PARAMETER;
}
if(prv->card == 0){
return B_CAS_CARD_ERROR_NOT_INITIALIZED;
}
memcpy(stat, &(prv->stat), sizeof(B_CAS_INIT_STATUS));
return 0;
}
static int get_id_b_cas_card(void *bcas, B_CAS_ID *dst)
{
LONG ret;
DWORD slen;
DWORD rlen;
int i,num;
uint8_t *p;
uint8_t *tail;
B_CAS_CARD_PRIVATE_DATA *prv;
SCARD_IO_REQUEST sir;
prv = private_data(bcas);
if( (prv == NULL) || (dst == NULL) ){
return B_CAS_CARD_ERROR_INVALID_PARAMETER;
}
if(prv->card == 0){
return B_CAS_CARD_ERROR_NOT_INITIALIZED;
}
slen = sizeof(CARD_ID_INFORMATION_ACQUIRE_CMD);
memcpy(prv->sbuf, CARD_ID_INFORMATION_ACQUIRE_CMD, slen);
memcpy(&sir, SCARD_PCI_T1, sizeof(sir));
rlen = B_CAS_BUFFER_MAX;
ret = SCardTransmit(prv->card, SCARD_PCI_T1, prv->sbuf, slen, &sir, prv->rbuf, &rlen);
if( (ret != SCARD_S_SUCCESS) || (rlen < 19) ){
return B_CAS_CARD_ERROR_TRANSMIT_FAILED;
}
p = prv->rbuf + 6;
tail = prv->rbuf + rlen;
if( p+1 > tail ){
return B_CAS_CARD_ERROR_TRANSMIT_FAILED;
}
num = p[0];
if(num > prv->id_max){
if(change_id_max(prv, num+4) < 0){
return B_CAS_CARD_ERROR_NO_ENOUGH_MEMORY;
}
}
p += 1;
for(i=0;i<num;i++){
if( p+10 > tail ){
return B_CAS_CARD_ERROR_TRANSMIT_FAILED;
}
{
int maker_id;
int version;
int check_code;
maker_id = p[0];
version = p[1];
prv->id.data[i] = load_be_uint48(p+2);
check_code = load_be_uint16(p+8);
}
p += 10;
}
prv->id.count = num;
memcpy(dst, &(prv->id), sizeof(B_CAS_ID));
return 0;
}
static int get_pwr_on_ctrl_b_cas_card(void *bcas, B_CAS_PWR_ON_CTRL_INFO *dst)
{
LONG ret;
DWORD slen;
DWORD rlen;
int i,num,code;
B_CAS_CARD_PRIVATE_DATA *prv;
SCARD_IO_REQUEST sir;
memset(dst, 0, sizeof(B_CAS_PWR_ON_CTRL_INFO));
prv = private_data(bcas);
if( (prv == NULL) || (dst == NULL) ){
return B_CAS_CARD_ERROR_INVALID_PARAMETER;
}
if(prv->card == 0){
return B_CAS_CARD_ERROR_NOT_INITIALIZED;
}
slen = sizeof(POWER_ON_CONTROL_INFORMATION_REQUEST_CMD);
memcpy(prv->sbuf, POWER_ON_CONTROL_INFORMATION_REQUEST_CMD, slen);
prv->sbuf[5] = 0;
memcpy(&sir, SCARD_PCI_T1, sizeof(sir));
rlen = B_CAS_BUFFER_MAX;
ret = SCardTransmit(prv->card, SCARD_PCI_T1, prv->sbuf, slen, &sir, prv->rbuf, &rlen);
if( (ret != SCARD_S_SUCCESS) || (rlen < 18) || (prv->rbuf[6] != 0) ){
return B_CAS_CARD_ERROR_TRANSMIT_FAILED;
}
code = load_be_uint16(prv->rbuf+4);
if(code == 0xa101){
/* no data */
return 0;
}else if(code != 0x2100){
return B_CAS_CARD_ERROR_TRANSMIT_FAILED;
}
num = (prv->rbuf[7] + 1);
if(prv->pwc_max < num){
if(change_pwc_max(prv, num+4) < 0){
return B_CAS_CARD_ERROR_NO_ENOUGH_MEMORY;
}
}
extract_power_on_ctrl_response(prv->pwc.data+0, prv->rbuf);
for(i=1;i<num;i++){
prv->sbuf[5] = i;
rlen = B_CAS_BUFFER_MAX;
ret = SCardTransmit(prv->card, SCARD_PCI_T1, prv->sbuf, slen, &sir, prv->rbuf, &rlen);
if( (ret != SCARD_S_SUCCESS) || (rlen < 18) || (prv->rbuf[6] != i) ){
return B_CAS_CARD_ERROR_TRANSMIT_FAILED;
}
extract_power_on_ctrl_response(prv->pwc.data+i, prv->rbuf);
}
prv->pwc.count = num;
memcpy(dst, &(prv->pwc), sizeof(B_CAS_PWR_ON_CTRL_INFO));
return 0;
}
static int proc_ecm_b_cas_card(void *bcas, B_CAS_ECM_RESULT *dst, uint8_t *src, int len)
{
int retry_count;
LONG ret;
DWORD slen;
DWORD rlen;
B_CAS_CARD_PRIVATE_DATA *prv;
SCARD_IO_REQUEST sir;
prv = private_data(bcas);
if( (prv == NULL) ||
(dst == NULL) ||
(src == NULL) ||
(len < 1) ){
return B_CAS_CARD_ERROR_INVALID_PARAMETER;
}
if(prv->card == 0){
return B_CAS_CARD_ERROR_NOT_INITIALIZED;
}
slen = setup_ecm_receive_command(prv->sbuf, src, len);
memcpy(&sir, SCARD_PCI_T1, sizeof(sir));
rlen = B_CAS_BUFFER_MAX;
retry_count = 0;
ret = SCardTransmit(prv->card, SCARD_PCI_T1, prv->sbuf, slen, &sir, prv->rbuf, &rlen);
while( ((ret != SCARD_S_SUCCESS) || (rlen < 25)) && (retry_count < 10) ){
retry_count += 1;
if(!connect_card(prv, prv->reader)){
continue;
}
slen = setup_ecm_receive_command(prv->sbuf, src, len);
memcpy(&sir, SCARD_PCI_T1, sizeof(sir));
rlen = B_CAS_BUFFER_MAX;
ret = SCardTransmit(prv->card, SCARD_PCI_T1, prv->sbuf, slen, &sir, prv->rbuf, &rlen);
}
if( (ret != SCARD_S_SUCCESS) || (rlen < 25) ){
return B_CAS_CARD_ERROR_TRANSMIT_FAILED;
}
memcpy(dst->scramble_key, prv->rbuf+6, 16);
dst->return_code = load_be_uint16(prv->rbuf+4);
return 0;
}
static int proc_emm_b_cas_card(void *bcas, uint8_t *src, int len)
{
int retry_count;
LONG ret;
DWORD slen;
DWORD rlen;
B_CAS_CARD_PRIVATE_DATA *prv;
SCARD_IO_REQUEST sir;
prv = private_data(bcas);
if( (prv == NULL) ||
(src == NULL) ||
(len < 1) ){
return B_CAS_CARD_ERROR_INVALID_PARAMETER;
}
if(prv->card == 0){
return B_CAS_CARD_ERROR_NOT_INITIALIZED;
}
slen = setup_emm_receive_command(prv->sbuf, src, len);
memcpy(&sir, SCARD_PCI_T1, sizeof(sir));
rlen = B_CAS_BUFFER_MAX;
retry_count = 0;
ret = SCardTransmit(prv->card, SCARD_PCI_T1, prv->sbuf, slen, &sir, prv->rbuf, &rlen);
while( ((ret != SCARD_S_SUCCESS) || (rlen < 6)) && (retry_count < 2) ){
retry_count += 1;
if(!connect_card(prv, prv->reader)){
continue;
}
slen = setup_emm_receive_command(prv->sbuf, src, len);
memcpy(&sir, SCARD_PCI_T1, sizeof(sir));
rlen = B_CAS_BUFFER_MAX;
ret = SCardTransmit(prv->card, SCARD_PCI_T1, prv->sbuf, slen, &sir, prv->rbuf, &rlen);
}
if( (ret != SCARD_S_SUCCESS) || (rlen < 6) ){
return B_CAS_CARD_ERROR_TRANSMIT_FAILED;
}
return 0;
}
/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
private method implementation
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
static B_CAS_CARD_PRIVATE_DATA *private_data(void *bcas)
{
B_CAS_CARD_PRIVATE_DATA *r;
B_CAS_CARD *p;
p = (B_CAS_CARD *)bcas;
if(p == NULL){
return NULL;
}
r = (B_CAS_CARD_PRIVATE_DATA *)(p->private_data);
if( ((void *)(r+1)) != ((void *)p) ){
return NULL;
}
return r;
}
static void teardown(B_CAS_CARD_PRIVATE_DATA *prv)
{
if(prv->card != 0){
SCardDisconnect(prv->card, SCARD_LEAVE_CARD);
prv->card = 0;
}
if(prv->mng != 0){
SCardReleaseContext(prv->mng);
prv->mng = 0;
}
if(prv->pool != NULL){
free(prv->pool);
prv->pool = NULL;
}
prv->reader = NULL;
prv->sbuf = NULL;
prv->rbuf = NULL;
prv->id.data = NULL;
prv->id_max = 0;
}
static int change_id_max(B_CAS_CARD_PRIVATE_DATA *prv, int max)
{
int m;
int reader_size;
int pwctrl_size;
uint8_t *p;
uint8_t *old_reader;
uint8_t *old_pwctrl;
reader_size = prv->sbuf - prv->pool;
pwctrl_size = prv->pwc.count * sizeof(B_CAS_PWR_ON_CTRL);
m = reader_size;
m += (2*B_CAS_BUFFER_MAX);
m += (max*sizeof(int64_t));
m += (prv->pwc_max*sizeof(B_CAS_PWR_ON_CTRL));
p = (uint8_t *)malloc(m);
if(p == NULL){
return B_CAS_CARD_ERROR_NO_ENOUGH_MEMORY;
}
old_reader = (uint8_t *)(prv->reader);
old_pwctrl = (uint8_t *)(prv->pwc.data);
prv->reader = (char *)p;
prv->sbuf = prv->pool + reader_size;
prv->rbuf = prv->sbuf + B_CAS_BUFFER_MAX;
prv->id.data = (int64_t *)(prv->rbuf + B_CAS_BUFFER_MAX);
prv->id_max = max;
prv->pwc.data = (B_CAS_PWR_ON_CTRL *)(prv->id.data + prv->id_max);
memcpy(prv->reader, old_reader, reader_size);
memcpy(prv->pwc.data, old_pwctrl, pwctrl_size);
free(prv->pool);
prv->pool = p;
return 0;
}
static int change_pwc_max(B_CAS_CARD_PRIVATE_DATA *prv, int max)
{
int m;
int reader_size;
int cardid_size;
uint8_t *p;
uint8_t *old_reader;
uint8_t *old_cardid;
reader_size = prv->sbuf - prv->pool;
cardid_size = prv->id.count * sizeof(int64_t);
m = reader_size;
m += (2*B_CAS_BUFFER_MAX);
m += (prv->id_max*sizeof(int64_t));
m += (max*sizeof(B_CAS_PWR_ON_CTRL));
p = (uint8_t *)malloc(m);
if(p == NULL){
return B_CAS_CARD_ERROR_NO_ENOUGH_MEMORY;
}
old_reader = (uint8_t *)(prv->reader);
old_cardid = (uint8_t *)(prv->id.data);
prv->reader = (char *)p;
prv->sbuf = prv->pool + reader_size;
prv->rbuf = prv->sbuf + B_CAS_BUFFER_MAX;
prv->id.data = (int64_t *)(prv->rbuf + B_CAS_BUFFER_MAX);
prv->pwc.data = (B_CAS_PWR_ON_CTRL *)(prv->id.data + prv->id_max);
prv->pwc_max = max;
memcpy(prv->reader, old_reader, reader_size);
memcpy(prv->id.data, old_cardid, cardid_size);
free(prv->pool);
prv->pool = p;
return 0;
}
static int connect_card(B_CAS_CARD_PRIVATE_DATA *prv, const char *reader_name)
{
int m,n;
LONG ret;
DWORD rlen,protocol;
uint8_t *p;
SCARD_IO_REQUEST sir;
if(prv->card != 0){
SCardDisconnect(prv->card, SCARD_RESET_CARD);
prv->card = 0;
}
ret = SCardConnect(prv->mng, reader_name, SCARD_SHARE_SHARED, SCARD_PROTOCOL_T1, &(prv->card), &protocol);
if(ret != SCARD_S_SUCCESS){
return 0;
}
m = sizeof(INITIAL_SETTING_CONDITIONS_CMD);
memcpy(prv->sbuf, INITIAL_SETTING_CONDITIONS_CMD, m);
memcpy(&sir, SCARD_PCI_T1, sizeof(sir));
rlen = B_CAS_BUFFER_MAX;
ret = SCardTransmit(prv->card, SCARD_PCI_T1, prv->sbuf, m, &sir, prv->rbuf, &rlen);
if(ret != SCARD_S_SUCCESS){
return 0;
}
if(rlen < 57){
return 0;
}
p = prv->rbuf;
n = load_be_uint16(p+4);
if(n != 0x2100){ // return code missmatch
return 0;
}
memcpy(prv->stat.system_key, p+16, 32);
memcpy(prv->stat.init_cbc, p+48, 8);
prv->stat.bcas_card_id = load_be_uint48(p+8);
prv->stat.card_status = load_be_uint16(p+2);
prv->stat.ca_system_id = load_be_uint16(p+6);
return 1;
}
static void extract_power_on_ctrl_response(B_CAS_PWR_ON_CTRL *dst, uint8_t *src)
{
int referrence;
int start;
int limit;
dst->broadcaster_group_id = src[8];
referrence = (src[9]<<8)|src[10];
start = referrence - src[11];
limit = start + (src[12]-1);
extract_mjd(&(dst->s_yy), &(dst->s_mm), &(dst->s_dd), start);
extract_mjd(&(dst->l_yy), &(dst->l_mm), &(dst->l_dd), limit);
dst->hold_time = src[13];
dst->network_id = (src[14]<<8)|src[15];
dst->transport_id = (src[16]<<8)|src[17];
}
static void extract_mjd(int *yy, int *mm, int *dd, int mjd)
{
int a1,m1;
int a2,m2;
int a3,m3;
int a4,m4;
int mw;
int dw;
int yw;
mjd -= 51604; // 2000,3/1
if(mjd < 0){
mjd += 0x10000;
}
a1 = mjd / 146097;
m1 = mjd % 146097;
a2 = m1 / 36524;
m2 = m1 - (a2 * 36524);
a3 = m2 / 1461;
m3 = m2 - (a3 * 1461);
a4 = m3 / 365;
if(a4 > 3){
a4 = 3;
}
m4 = m3 - (a4 * 365);
mw = (1071*m4+450) >> 15;
dw = m4 - ((979*mw+16) >> 5);
yw = a1*400 + a2*100 + a3*4 + a4 + 2000;
mw += 3;
if(mw > 12){
mw -= 12;
yw += 1;
}
dw += 1;
*yy = yw;
*mm = mw;
*dd = dw;
}
static int setup_ecm_receive_command(uint8_t *dst, uint8_t *src, int len)
{
int r;
r = sizeof(ECM_RECEIVE_CMD_HEADER);
memcpy(dst+0, ECM_RECEIVE_CMD_HEADER, r);
dst[r] = (uint8_t)(len & 0xff);
r += 1;
memcpy(dst+r, src, len);
r += len;
dst[r] = 0;
r += 1;
return r;
}
static int setup_emm_receive_command(uint8_t *dst, uint8_t *src, int len)
{
int r;
r = sizeof(EMM_RECEIVE_CMD_HEADER);
memcpy(dst+0, EMM_RECEIVE_CMD_HEADER, r);
dst[r] = (uint8_t)(len & 0xff);
r += 1;
memcpy(dst+r, src, len);
r += len;
dst[r] = 0;
r += 1;
return r;
}
static int32_t load_be_uint16(uint8_t *p)
{
return ((p[0]<<8)|p[1]);
}
static int64_t load_be_uint48(uint8_t *p)
{
int i;
int64_t r;
r = p[0];
for(i=1;i<6;i++){
r <<= 8;
r |= p[i];
}
return r;
}

75
src/b_cas_card.h Normal file
View File

@ -0,0 +1,75 @@
#ifndef B_CAS_CARD_H
#define B_CAS_CARD_H
#include "portable.h"
typedef struct {
uint8_t system_key[32];
uint8_t init_cbc[8];
int64_t bcas_card_id;
int32_t card_status;
int32_t ca_system_id;
} B_CAS_INIT_STATUS;
typedef struct {
int64_t *data;
int32_t count;
} B_CAS_ID;
typedef struct {
int32_t s_yy; /* start date : year */
int32_t s_mm; /* start date : month */
int32_t s_dd; /* start date : day */
int32_t l_yy; /* limit date : year */
int32_t l_mm; /* limit date : month */
int32_t l_dd; /* limit date : day */
int32_t hold_time; /* in hour unit */
int32_t broadcaster_group_id;
int32_t network_id;
int32_t transport_id;
} B_CAS_PWR_ON_CTRL;
typedef struct {
B_CAS_PWR_ON_CTRL *data;
int32_t count;
} B_CAS_PWR_ON_CTRL_INFO;
typedef struct {
uint8_t scramble_key[16];
uint32_t return_code;
} B_CAS_ECM_RESULT;
typedef struct {
void *private_data;
void (* release)(void *bcas);
int (* init)(void *bcas);
int (* get_init_status)(void *bcas, B_CAS_INIT_STATUS *stat);
int (* get_id)(void *bcas, B_CAS_ID *dst);
int (* get_pwr_on_ctrl)(void *bcas, B_CAS_PWR_ON_CTRL_INFO *dst);
int (* proc_ecm)(void *bcas, B_CAS_ECM_RESULT *dst, uint8_t *src, int len);
int (* proc_emm)(void *bcas, uint8_t *src, int len);
} B_CAS_CARD;
#ifdef __cplusplus
extern "C" {
#endif
extern B_CAS_CARD *create_b_cas_card();
#ifdef __cplusplus
}
#endif
#endif /* B_CAS_CARD_H */

View File

@ -0,0 +1,11 @@
#ifndef B_CAS_CARD_ERROR_CODE_H
#define B_CAS_CARD_ERROR_CODE_H
#define B_CAS_CARD_ERROR_INVALID_PARAMETER -1
#define B_CAS_CARD_ERROR_NOT_INITIALIZED -2
#define B_CAS_CARD_ERROR_NO_SMART_CARD_READER -3
#define B_CAS_CARD_ERROR_ALL_READERS_CONNECTION_FAILED -4
#define B_CAS_CARD_ERROR_NO_ENOUGH_MEMORY -5
#define B_CAS_CARD_ERROR_TRANSMIT_FAILED -6
#endif /* B_CAS_CARD_ERROR_CODE_H */

33
src/makefile.win Normal file
View File

@ -0,0 +1,33 @@
CC = cl
CFLAG = /c /MT /W4 /O2
LINK = link
LFLAG = /nologo
LIBS = winscard.lib
ALL: b25.exe
arib_std_b25.obj: arib_std_b25.c arib_std_b25.h portable.h b_cas_card.h arib_std_b25_error_code.h multi2.h ts_section_parser.h ts_common_types.h
$(CC) $(CFLAG) arib_std_b25.c
b_cas_card.obj: b_cas_card.c b_cas_card.h portable.h b_cas_card_error_code.h
$(CC) $(CFLAG) b_cas_card.c
multi2.obj: multi2.c multi2.h portable.h multi2_error_code.h
$(CC) $(CFLAG) multi2.c
td.obj: td.c arib_std_b25.h portable.h b_cas_card.h
$(CC) $(CFLAG) td.c
ts_section_parser.obj: ts_section_parser.c ts_section_parser.h ts_common_types.h portable.h ts_section_parser_error_code.h
$(CC) $(CFLAG) ts_section_parser.c
OBJ = arib_std_b25.obj b_cas_card.obj multi2.obj td.obj ts_section_parser.obj
b25.exe: $(OBJ)
$(LINK) $(LFLAG) $(LIBS) /OUT:b25.exe $(OBJ)
clean:
DEL *.obj
DEL *.exe

527
src/multi2.c Normal file
View File

@ -0,0 +1,527 @@
#include <stdlib.h>
#include <string.h>
#include "multi2.h"
#include "multi2_error_code.h"
/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
inline functions
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
static __inline uint8_t *load_be_uint32(uint32_t *dst, uint8_t *src)
{
*dst = ((src[0]<<24)|(src[1]<<16)|(src[2]<<8)|src[3]);
return src+4;
}
static __inline uint8_t *save_be_uint32(uint8_t *dst, uint32_t src)
{
dst[0] = (uint8_t)((src>>24) & 0xff);
dst[1] = (uint8_t)((src>>16) & 0xff);
dst[2] = (uint8_t)((src>> 8) & 0xff);
dst[3] = (uint8_t)( src & 0xff);
return dst+4;
}
static __inline uint32_t left_rotate_uint32(uint32_t val, uint32_t count)
{
return ((val << count) | (val >> (32-count)));
}
/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
inner structures
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
typedef struct {
uint32_t key[8];
} CORE_PARAM;
typedef struct {
uint32_t l;
uint32_t r;
} CORE_DATA;
typedef struct {
int32_t ref_count;
CORE_DATA cbc_init;
CORE_PARAM sys;
CORE_DATA scr[2]; /* 0: odd, 1: even */
CORE_PARAM wrk[2]; /* 0: odd, 1: even */
uint32_t round;
uint32_t state;
} MULTI2_PRIVATE_DATA;
/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
constant values
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
#define MULTI2_STATE_CBC_INIT_SET (0x0001)
#define MULTI2_STATE_SYSTEM_KEY_SET (0x0002)
#define MULTI2_STATE_SCRAMBLE_KEY_SET (0x0004)
/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
function prottypes (interface method)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
static void release_multi2(void *m2);
static int add_ref_multi2(void *m2);
static int set_round_multi2(void *m2, int32_t val);
static int set_system_key_multi2(void *m2, uint8_t *val);
static int set_init_cbc_multi2(void *m2, uint8_t *val);
static int set_scramble_key_multi2(void *m2, uint8_t *val);
static int clear_scramble_key_multi2(void *m2);
static int encrypt_multi2(void *m2, int32_t type, uint8_t *buf, int32_t size);
static int decrypt_multi2(void *m2, int32_t type, uint8_t *buf, int32_t size);
/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
global function implementation
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
MULTI2 *create_multi2()
{
int n;
MULTI2 *r;
MULTI2_PRIVATE_DATA *prv;
n = sizeof(MULTI2_PRIVATE_DATA);
n += sizeof(MULTI2);
prv = (MULTI2_PRIVATE_DATA *)calloc(1, n);
if(prv == NULL){
return NULL;
}
r = (MULTI2 *)(prv+1);
r->private_data = prv;
prv->ref_count = 1;
prv->round = 4;
r->release = release_multi2;
r->add_ref = add_ref_multi2;
r->set_round = set_round_multi2;
r->set_system_key = set_system_key_multi2;
r->set_init_cbc = set_init_cbc_multi2;
r->set_scramble_key = set_scramble_key_multi2;
r->clear_scramble_key = clear_scramble_key_multi2;
r->encrypt = encrypt_multi2;
r->decrypt = decrypt_multi2;
return r;
}
/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
function prottypes (private method)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
static MULTI2_PRIVATE_DATA *private_data(void *m2);
static void core_schedule(CORE_PARAM *work, CORE_PARAM *skey, CORE_DATA *dkey);
static void core_encrypt(CORE_DATA *dst, CORE_DATA *src, CORE_PARAM *w, int32_t round);
static void core_decrypt(CORE_DATA *dst, CORE_DATA *src, CORE_PARAM *w, int32_t round);
static void core_pi1(CORE_DATA *dst, CORE_DATA *src);
static void core_pi2(CORE_DATA *dst, CORE_DATA *src, uint32_t a);
static void core_pi3(CORE_DATA *dst, CORE_DATA *src, uint32_t a, uint32_t b);
static void core_pi4(CORE_DATA *dst, CORE_DATA *src, uint32_t a);
/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
interface method implementation
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
static void release_multi2(void *m2)
{
MULTI2_PRIVATE_DATA *prv;
prv = private_data(m2);
if(prv == NULL){
/* do nothing */
return;
}
prv->ref_count -= 1;
if(prv->ref_count == 0){
free(prv);
}
}
static int add_ref_multi2(void *m2)
{
MULTI2_PRIVATE_DATA *prv;
prv = private_data(m2);
if(prv == NULL){
return MULTI2_ERROR_INVALID_PARAMETER;
}
prv->ref_count += 1;
return 0;
}
static int set_round_multi2(void *m2, int32_t val)
{
MULTI2_PRIVATE_DATA *prv;
prv = private_data(m2);
if(prv == NULL){
/* do nothing */
return MULTI2_ERROR_INVALID_PARAMETER;
}
prv->round = val;
return 0;
}
static int set_system_key_multi2(void *m2, uint8_t *val)
{
int i;
uint8_t *p;
MULTI2_PRIVATE_DATA *prv;
prv = private_data(m2);
if( (prv == NULL) || (val == NULL) ){
return MULTI2_ERROR_INVALID_PARAMETER;
}
p = val;
for(i=0;i<8;i++){
p = load_be_uint32(prv->sys.key+i, p);
}
prv->state |= MULTI2_STATE_SYSTEM_KEY_SET;
return 0;
}
static int set_init_cbc_multi2(void *m2, uint8_t *val)
{
uint8_t *p;
MULTI2_PRIVATE_DATA *prv;
prv = private_data(m2);
if( (prv == NULL) || (val == NULL) ){
return MULTI2_ERROR_INVALID_PARAMETER;
}
p = val;
p = load_be_uint32(&(prv->cbc_init.l), p);
p = load_be_uint32(&(prv->cbc_init.r), p);
prv->state |= MULTI2_STATE_CBC_INIT_SET;
return 0;
}
static int set_scramble_key_multi2(void *m2, uint8_t *val)
{
uint8_t *p;
MULTI2_PRIVATE_DATA *prv;
prv = private_data(m2);
if( (prv == NULL) || (val == NULL) ){
return MULTI2_ERROR_INVALID_PARAMETER;
}
p = val;
p = load_be_uint32(&(prv->scr[0].l), p);
p = load_be_uint32(&(prv->scr[0].r), p);
p = load_be_uint32(&(prv->scr[1].l), p);
p = load_be_uint32(&(prv->scr[1].r), p);
core_schedule(prv->wrk+0, &(prv->sys), prv->scr+0);
core_schedule(prv->wrk+1, &(prv->sys), prv->scr+1);
prv->state |= MULTI2_STATE_SCRAMBLE_KEY_SET;
return 0;
}
static int clear_scramble_key_multi2(void *m2)
{
MULTI2_PRIVATE_DATA *prv;
prv = private_data(m2);
if(prv == NULL){
return MULTI2_ERROR_INVALID_PARAMETER;
}
memset(prv->scr, 0, sizeof(prv->scr));
memset(prv->wrk, 0, sizeof(prv->wrk));
prv->state &= (~MULTI2_STATE_SCRAMBLE_KEY_SET);
return 0;
}
static int encrypt_multi2(void *m2, int32_t type, uint8_t *buf, int32_t size)
{
CORE_DATA src,dst;
CORE_PARAM *prm;
uint8_t *p;
MULTI2_PRIVATE_DATA *prv;
prv = private_data(m2);
if( (prv == NULL) || (buf == NULL) || (size < 1) ){
return MULTI2_ERROR_INVALID_PARAMETER;
}
if(prv->state != (MULTI2_STATE_CBC_INIT_SET|MULTI2_STATE_SYSTEM_KEY_SET|MULTI2_STATE_SCRAMBLE_KEY_SET)){
if( (prv->state & MULTI2_STATE_CBC_INIT_SET) == 0 ){
return MULTI2_ERROR_UNSET_CBC_INIT;
}
if( (prv->state & MULTI2_STATE_SYSTEM_KEY_SET) == 0 ){
return MULTI2_ERROR_UNSET_SYSTEM_KEY;
}
if( (prv->state & MULTI2_STATE_SCRAMBLE_KEY_SET) == 0 ){
return MULTI2_ERROR_UNSET_SCRAMBLE_KEY;
}
}
if(type == 0x02){
prm = prv->wrk+1;
}else{
prm = prv->wrk+0;
}
dst.l = prv->cbc_init.l;
dst.r = prv->cbc_init.r;
p = buf;
while(size >= 8){
load_be_uint32(&(src.l), p+0);
load_be_uint32(&(src.r), p+4);
src.l = src.l ^ dst.l;
src.r = src.r ^ dst.r;
core_encrypt(&dst, &src, prm, prv->round);
p = save_be_uint32(p, dst.l);
p = save_be_uint32(p, dst.r);
size -= 8;
}
if(size > 0){
int i;
uint8_t tmp[8];
src.l = dst.l;
src.r = dst.r;
core_encrypt(&dst, &src, prm, prv->round);
save_be_uint32(tmp+0, dst.l);
save_be_uint32(tmp+4, dst.r);
for(i=0;i<size;i++){
p[i] = (uint8_t)(p[i] ^ tmp[i]);
}
}
return 0;
}
static int decrypt_multi2(void *m2, int32_t type, uint8_t *buf, int32_t size)
{
CORE_DATA src,dst,cbc;
CORE_PARAM *prm;
uint8_t *p;
MULTI2_PRIVATE_DATA *prv;
prv = private_data(m2);
if( (prv == NULL) || (buf == NULL) || (size < 1) ){
return MULTI2_ERROR_INVALID_PARAMETER;
}
if(prv->state != (MULTI2_STATE_CBC_INIT_SET|MULTI2_STATE_SYSTEM_KEY_SET|MULTI2_STATE_SCRAMBLE_KEY_SET)){
if( (prv->state & MULTI2_STATE_CBC_INIT_SET) == 0 ){
return MULTI2_ERROR_UNSET_CBC_INIT;
}
if( (prv->state & MULTI2_STATE_SYSTEM_KEY_SET) == 0 ){
return MULTI2_ERROR_UNSET_SYSTEM_KEY;
}
if( (prv->state & MULTI2_STATE_SCRAMBLE_KEY_SET) == 0 ){
return MULTI2_ERROR_UNSET_SCRAMBLE_KEY;
}
}
if(type == 0x02){
prm = prv->wrk+1;
}else{
prm = prv->wrk+0;
}
cbc.l = prv->cbc_init.l;
cbc.r = prv->cbc_init.r;
p = buf;
while(size >= 8){
load_be_uint32(&(src.l), p+0);
load_be_uint32(&(src.r), p+4);
core_decrypt(&dst, &src, prm, prv->round);
dst.l = dst.l ^ cbc.l;
dst.r = dst.r ^ cbc.r;
cbc.l = src.l;
cbc.r = src.r;
p = save_be_uint32(p, dst.l);
p = save_be_uint32(p, dst.r);
size -= 8;
}
if(size > 0){
int i;
uint8_t tmp[8];
core_encrypt(&dst, &cbc, prm, prv->round);
save_be_uint32(tmp+0, dst.l);
save_be_uint32(tmp+4, dst.r);
for(i=0;i<size;i++){
p[i] = (uint8_t)(p[i] ^ tmp[i]);
}
}
return 0;
}
/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
private method implementation
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
static MULTI2_PRIVATE_DATA *private_data(void *m2)
{
MULTI2_PRIVATE_DATA *r;
MULTI2 *p;
p = (MULTI2 *)m2;
if(p == NULL){
return NULL;
}
r = (MULTI2_PRIVATE_DATA *)(p->private_data);
if( ((void *)(r+1)) != ((void *)p) ){
return NULL;
}
return r;
}
static void core_schedule(CORE_PARAM *work, CORE_PARAM *skey, CORE_DATA *dkey)
{
CORE_DATA b1,b2,b3,b4,b5,b6,b7,b8,b9;
core_pi1(&b1, dkey);
core_pi2(&b2, &b1, skey->key[0]);
work->key[0] = b2.l;
core_pi3(&b3, &b2, skey->key[1], skey->key[2]);
work->key[1] = b3.r;
core_pi4(&b4, &b3, skey->key[3]);
work->key[2] = b4.l;
core_pi1(&b5, &b4);
work->key[3] = b5.r;
core_pi2(&b6, &b5, skey->key[4]);
work->key[4] = b6.l;
core_pi3(&b7, &b6, skey->key[5], skey->key[6]);
work->key[5] = b7.r;
core_pi4(&b8, &b7, skey->key[7]);
work->key[6] = b8.l;
core_pi1(&b9, &b8);
work->key[7] = b9.r;
}
static void core_encrypt(CORE_DATA *dst, CORE_DATA *src, CORE_PARAM *w, int32_t round)
{
int32_t i;
CORE_DATA tmp;
dst->l = src->l;
dst->r = src->r;
for(i=0;i<round;i++){
core_pi1(&tmp, dst);
core_pi2( dst, &tmp, w->key[0]);
core_pi3(&tmp, dst, w->key[1], w->key[2]);
core_pi4( dst, &tmp, w->key[3]);
core_pi1(&tmp, dst);
core_pi2( dst, &tmp, w->key[4]);
core_pi3(&tmp, dst, w->key[5], w->key[6]);
core_pi4( dst, &tmp, w->key[7]);
}
}
static void core_decrypt(CORE_DATA *dst, CORE_DATA *src, CORE_PARAM *w, int32_t round)
{
int32_t i;
CORE_DATA tmp;
dst->l = src->l;
dst->r = src->r;
for(i=0;i<round;i++){
core_pi4(&tmp, dst, w->key[7]);
core_pi3( dst, &tmp, w->key[5], w->key[6]);
core_pi2(&tmp, dst, w->key[4]);
core_pi1( dst, &tmp);
core_pi4(&tmp, dst, w->key[3]);
core_pi3( dst, &tmp, w->key[1], w->key[2]);
core_pi2(&tmp, dst, w->key[0]);
core_pi1( dst, &tmp);
}
}
static void core_pi1(CORE_DATA *dst, CORE_DATA *src)
{
dst->l = src->l;
dst->r = src->r ^ src->l;
}
static void core_pi2(CORE_DATA *dst, CORE_DATA *src, uint32_t a)
{
uint32_t t0,t1,t2;
t0 = src->r + a;
t1 = left_rotate_uint32(t0, 1) + t0 - 1;
t2 = left_rotate_uint32(t1, 4) ^ t1;
dst->l = src->l ^ t2;
dst->r = src->r;
}
static void core_pi3(CORE_DATA *dst, CORE_DATA *src, uint32_t a, uint32_t b)
{
uint32_t t0,t1,t2,t3,t4,t5;
t0 = src->l + a;
t1 = left_rotate_uint32(t0, 2) + t0 + 1;
t2 = left_rotate_uint32(t1, 8) ^ t1;
t3 = t2 + b;
t4 = left_rotate_uint32(t3, 1) - t3;
t5 = left_rotate_uint32(t4, 16) ^ (t4 | src->l);
dst->l = src->l;
dst->r = src->r ^ t5;
}
static void core_pi4(CORE_DATA *dst, CORE_DATA *src, uint32_t a)
{
uint32_t t0,t1;
t0 = src->r + a;
t1 = left_rotate_uint32(t0, 2) + t0 + 1;
dst->l = src->l ^ t1;
dst->r = src->r;
}

35
src/multi2.h Normal file
View File

@ -0,0 +1,35 @@
#ifndef MULTI2_H
#define MULTI2_H
#include "portable.h"
typedef struct {
void *private_data;
void (* release)(void *m2);
int (* add_ref)(void *m2);
int (* set_round)(void *m2, int32_t val);
int (* set_system_key)(void *m2, uint8_t *val);
int (* set_init_cbc)(void *m2, uint8_t *val);
int (* set_scramble_key)(void *m2, uint8_t *val);
int (* clear_scramble_key)(void *m2);
int (* encrypt)(void *m2, int32_t type, uint8_t *buf, int32_t size);
int (* decrypt)(void *m2, int32_t type, uint8_t *buf, int32_t size);
} MULTI2;
#ifdef __cplusplus
extern "C" {
#endif
extern MULTI2 *create_multi2();
#ifdef __cplusplus
}
#endif
#endif /* MULTI2_H */

9
src/multi2_error_code.h Normal file
View File

@ -0,0 +1,9 @@
#ifndef MULTI2_ERROR_CODE_H
#define MULTI2_ERROR_CODE_H
#define MULTI2_ERROR_INVALID_PARAMETER -1
#define MULTI2_ERROR_UNSET_SYSTEM_KEY -2
#define MULTI2_ERROR_UNSET_CBC_INIT -3
#define MULTI2_ERROR_UNSET_SCRAMBLE_KEY -4
#endif /* MULTI2_ERROR_CODE_H */

21
src/portable.h Normal file
View File

@ -0,0 +1,21 @@
#ifndef PORTABLE_H
#define PORTABLE_H
#if (MSC_VER < 1300)
typedef unsigned char uint8_t;
typedef signed char int8_t;
typedef unsigned short uint16_t;
typedef signed short int16_t;
typedef unsigned int uint32_t;
typedef signed int int32_t;
typedef unsigned __int64 uint64_t;
typedef signed __int64 int64_t;
#else
#include <inttypes.h>
#endif
#endif /* PORTABLE_H */

385
src/td.c Normal file
View File

@ -0,0 +1,385 @@
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <io.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <windows.h>
#include <crtdbg.h>
#include "arib_std_b25.h"
#include "b_cas_card.h"
typedef struct {
int32_t round;
int32_t strip;
int32_t emm;
int32_t verbose;
int32_t power_ctrl;
} OPTION;
static void show_usage();
static int parse_arg(OPTION *dst, int argc, char **argv);
static void test_arib_std_b25(const char *src, const char *dst, OPTION *opt);
static void show_bcas_power_on_control_info(B_CAS_CARD *bcas);
int main(int argc, char **argv)
{
int n;
OPTION opt;
_CrtSetReportMode( _CRT_WARN, _CRTDBG_MODE_FILE );
_CrtSetReportFile( _CRT_WARN, _CRTDBG_FILE_STDOUT );
_CrtSetReportMode( _CRT_ERROR, _CRTDBG_MODE_FILE );
_CrtSetReportFile( _CRT_ERROR, _CRTDBG_FILE_STDOUT );
_CrtSetReportMode( _CRT_ASSERT, _CRTDBG_MODE_FILE );
_CrtSetReportFile( _CRT_ASSERT, _CRTDBG_FILE_STDOUT );
_CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF|_CRTDBG_DELAY_FREE_MEM_DF|_CRTDBG_CHECK_ALWAYS_DF|_CRTDBG_LEAK_CHECK_DF);
n = parse_arg(&opt, argc, argv);
if(n+2 > argc){
show_usage();
exit(EXIT_FAILURE);
}
for(;n<=(argc-2);n+=2){
test_arib_std_b25(argv[n+0], argv[n+1], &opt);
}
_CrtDumpMemoryLeaks();
return EXIT_SUCCESS;
}
static void show_usage()
{
fprintf(stderr, "b25 - ARIB STD-B25 test program ver. 0.2.5 (2012, 2/13)\n");
fprintf(stderr, "usage: b25 [options] src.m2t dst.m2t [more pair ..]\n");
fprintf(stderr, "options:\n");
fprintf(stderr, " -r round (integer, default=4)\n");
fprintf(stderr, " -s strip\n");
fprintf(stderr, " 0: keep null(padding) stream (default)\n");
fprintf(stderr, " 1: strip null stream\n");
fprintf(stderr, " -m EMM\n");
fprintf(stderr, " 0: ignore EMM (default)\n");
fprintf(stderr, " 1: send EMM to B-CAS card\n");
fprintf(stderr, " -p power_on_control_info\n");
fprintf(stderr, " 0: do nothing additionaly\n");
fprintf(stderr, " 1: show B-CAS EMM receiving request (default)\n");
fprintf(stderr, " -v verbose\n");
fprintf(stderr, " 0: silent\n");
fprintf(stderr, " 1: show processing status (default)\n");
fprintf(stderr, "\n");
}
static int parse_arg(OPTION *dst, int argc, char **argv)
{
int i;
dst->round = 4;
dst->strip = 0;
dst->emm = 0;
dst->power_ctrl = 1;
dst->verbose = 1;
for(i=1;i<argc;i++){
if(argv[i][0] != '-'){
break;
}
switch(argv[i][1]){
case 'm':
if(argv[i][2]){
dst->emm = atoi(argv[i]+2);
}else{
dst->emm = atoi(argv[i+1]);
i += 1;
}
break;
case 'p':
if(argv[i][2]){
dst->power_ctrl = atoi(argv[i]+2);
}else{
dst->power_ctrl = atoi(argv[i+1]);
i += 1;
}
break;
case 'r':
if(argv[i][2]){
dst->round = atoi(argv[i]+2);
}else{
dst->round = atoi(argv[i+1]);
i += 1;
}
break;
case 's':
if(argv[i][2]){
dst->strip = atoi(argv[i]+2);
}else{
dst->strip = atoi(argv[i+1]);
i += 1;
}
break;
case 'v':
if(argv[i][2]){
dst->verbose = atoi(argv[i]+2);
}else{
dst->verbose = atoi(argv[i+1]);
i += 1;
}
break;
default:
fprintf(stderr, "error - unknown option '-%c'\n", argv[i][1]);
return argc;
}
}
return i;
}
static void test_arib_std_b25(const char *src, const char *dst, OPTION *opt)
{
int code,i,n,m;
int sfd,dfd;
int64_t total;
int64_t offset;
unsigned long tick,tock;
double mbps;
ARIB_STD_B25 *b25;
B_CAS_CARD *bcas;
ARIB_STD_B25_PROGRAM_INFO pgrm;
uint8_t data[64*1024];
ARIB_STD_B25_BUFFER sbuf;
ARIB_STD_B25_BUFFER dbuf;
sfd = -1;
dfd = -1;
b25 = NULL;
bcas = NULL;
sfd = _open(src, _O_BINARY|_O_RDONLY|_O_SEQUENTIAL);
if(sfd < 0){
fprintf(stderr, "error - failed on _open(%s) [src]\n", src);
goto LAST;
}
_lseeki64(sfd, 0, SEEK_END);
total = _telli64(sfd);
_lseeki64(sfd, 0, SEEK_SET);
b25 = create_arib_std_b25();
if(b25 == NULL){
fprintf(stderr, "error - failed on create_arib_std_b25()\n");
goto LAST;
}
code = b25->set_multi2_round(b25, opt->round);
if(code < 0){
fprintf(stderr, "error - failed on ARIB_STD_B25::set_multi2_round() : code=%d\n", code);
goto LAST;
}
code = b25->set_strip(b25, opt->strip);
if(code < 0){
fprintf(stderr, "error - failed on ARIB_STD_B25::set_strip() : code=%d\n", code);
goto LAST;
}
code = b25->set_emm_proc(b25, opt->emm);
if(code < 0){
fprintf(stderr, "error - failed on ARIB_STD_B25::set_emm_proc() : code=%d\n", code);
goto LAST;
}
bcas = create_b_cas_card();
if(bcas == NULL){
fprintf(stderr, "error - failed on create_b_cas_card()\n");
goto LAST;
}
code = bcas->init(bcas);
if(code < 0){
fprintf(stderr, "error - failed on B_CAS_CARD::init() : code=%d\n", code);
goto LAST;
}
code = b25->set_b_cas_card(b25, bcas);
if(code < 0){
fprintf(stderr, "error - failed on ARIB_STD_B25::set_b_cas_card() : code=%d\n", code);
goto LAST;
}
dfd = _open(dst, _O_BINARY|_O_WRONLY|_O_SEQUENTIAL|_O_CREAT|_O_TRUNC, _S_IREAD|_S_IWRITE);
if(dfd < 0){
fprintf(stderr, "error - failed on _open(%s) [dst]\n", dst);
goto LAST;
}
offset = 0;
tock = GetTickCount();
while( (n = _read(sfd, data, sizeof(data))) > 0 ){
sbuf.data = data;
sbuf.size = n;
code = b25->put(b25, &sbuf);
if(code < 0){
fprintf(stderr, "error - failed on ARIB_STD_B25::put() : code=%d\n", code);
goto LAST;
}
code = b25->get(b25, &dbuf);
if(code < 0){
fprintf(stderr, "error - failed on ARIB_STD_B25::get() : code=%d\n", code);
goto LAST;
}
if(dbuf.size > 0){
n = _write(dfd, dbuf.data, dbuf.size);
if(n != dbuf.size){
fprintf(stderr, "error failed on _write(%d)\n", dbuf.size);
goto LAST;
}
}
offset += sbuf.size;
if(opt->verbose != 0){
m = (int)(10000*offset/total);
tick = GetTickCount();
mbps = 0.0;
if (tick-tock > 100) {
mbps = offset;
mbps /= 1024;
mbps /= (tick-tock);
}
fprintf(stderr, "\rprocessing: %2d.%02d%% [%6.2f MB/sec]", m/100, m%100, mbps);
}
}
code = b25->flush(b25);
if(code < 0){
fprintf(stderr, "error - failed on ARIB_STD_B25::flush() : code=%d\n", code);
goto LAST;
}
code = b25->get(b25, &dbuf);
if(code < 0){
fprintf(stderr, "error - failed on ARIB_STD_B25::get() : code=%d\n", code);
goto LAST;
}
if(dbuf.size > 0){
n = _write(dfd, dbuf.data, dbuf.size);
if(n != dbuf.size){
fprintf(stderr, "error - failed on _write(%d)\n", dbuf.size);
goto LAST;
}
}
if(opt->verbose != 0){
mbps = 0.0;
if (tick-tock > 100) {
mbps = offset;
mbps /= 1024;
mbps /= (tick-tock);
}
fprintf(stderr, "\rprocessing: finish [%6.2f MB/sec]\n", mbps);
fflush(stderr);
fflush(stdout);
}
n = b25->get_program_count(b25);
if(n < 0){
fprintf(stderr, "error - failed on ARIB_STD_B25::get_program_count() : code=%d\n", code);
goto LAST;
}
for(i=0;i<n;i++){
code = b25->get_program_info(b25, &pgrm, i);
if(code < 0){
fprintf(stderr, "error - failed on ARIB_STD_B25::get_program_info(%d) : code=%d\n", i, code);
goto LAST;
}
if(pgrm.ecm_unpurchased_count > 0){
fprintf(stderr, "warning - unpurchased ECM is detected\n");
fprintf(stderr, " channel: %d\n", pgrm.program_number);
fprintf(stderr, " unpurchased ECM count: %d\n", pgrm.ecm_unpurchased_count);
fprintf(stderr, " last ECM error code: %04x\n", pgrm.last_ecm_error_code);
fprintf(stderr, " undecrypted TS packet: %d\n", pgrm.undecrypted_packet_count);
fprintf(stderr, " total TS packet: %d\n", pgrm.total_packet_count);
}
}
if(opt->power_ctrl != 0){
show_bcas_power_on_control_info(bcas);
}
LAST:
if(sfd >= 0){
_close(sfd);
sfd = -1;
}
if(dfd >= 0){
_close(dfd);
dfd = -1;
}
if(b25 != NULL){
b25->release(b25);
b25 = NULL;
}
if(bcas != NULL){
bcas->release(bcas);
bcas = NULL;
}
}
static void show_bcas_power_on_control_info(B_CAS_CARD *bcas)
{
int code;
int i,w;
B_CAS_PWR_ON_CTRL_INFO pwc;
code = bcas->get_pwr_on_ctrl(bcas, &pwc);
if(code < 0){
fprintf(stderr, "error - failed on B_CAS_CARD::get_pwr_on_ctrl() : code=%d\n", code);
return;
}
if(pwc.count == 0){
fprintf(stdout, "no EMM receiving request\n");
return;
}
fprintf(stdout, "total %d EMM receiving request\n", pwc.count);
for(i=0;i<pwc.count;i++){
fprintf(stdout, "+ [%d] : tune ", i);
switch(pwc.data[i].network_id){
case 4:
w = pwc.data[i].transport_id;
fprintf(stdout, "BS-%d/TS-%d ", ((w >> 4) & 0x1f), (w & 7));
break;
case 6:
case 7:
w = pwc.data[i].transport_id;
fprintf(stdout, "ND-%d/TS-%d ", ((w >> 4) & 0x1f), (w & 7));
break;
default:
fprintf(stdout, "unknown(b:0x%02x,n:0x%04x,t:0x%04x) ", pwc.data[i].broadcaster_group_id, pwc.data[i].network_id, pwc.data[i].transport_id);
break;
}
fprintf(stdout, "between %04d %02d/%02d ", pwc.data[i].s_yy, pwc.data[i].s_mm, pwc.data[i].s_dd);
fprintf(stdout, "to %04d %02d/%02d ", pwc.data[i].l_yy, pwc.data[i].l_mm, pwc.data[i].l_dd);
fprintf(stdout, "least %d hours\n", pwc.data[i].hold_time);
}
}

36
src/ts_common_types.h Normal file
View File

@ -0,0 +1,36 @@
#ifndef TS_COMMON_TYPES_H
#define TS_COMMON_TYPES_H
#include "portable.h"
typedef struct {
int32_t sync; /* 0- 7 : 8 bits */
int32_t transport_error_indicator; /* 8- 8 : 1 bit */
int32_t payload_unit_start_indicator; /* 9- 9 : 1 bit */
int32_t transport_priority; /* 10-10 : 1 bits */
int32_t pid; /* 11-23 : 13 bits */
int32_t transport_scrambling_control; /* 24-25 : 2 bits */
int32_t adaptation_field_control; /* 26-27 : 2 bits */
int32_t continuity_counter; /* 28-31 : 4 bits */
} TS_HEADER;
typedef struct {
int32_t table_id; /* 0- 7 : 8 bits */
int32_t section_syntax_indicator; /* 8- 8 : 1 bit */
int32_t private_indicator; /* 9- 9 : 1 bit */
int32_t section_length; /* 12-23 : 12 bits */
int32_t table_id_extension; /* 24-39 : 16 bits */
int32_t version_number; /* 42-46 : 5 bits */
int32_t current_next_indicator; /* 47-57 : 1 bit */
int32_t section_number; /* 48-55 : 8 bits */
int32_t last_section_number; /* 56-63 : 8 bits */
} TS_SECTION_HEADER;
typedef struct {
TS_SECTION_HEADER hdr;
uint8_t *raw;
uint8_t *data;
uint8_t *tail;
} TS_SECTION;
#endif /* TS_COMMON_TYPES_H */

802
src/ts_section_parser.c Normal file
View File

@ -0,0 +1,802 @@
#include <stdlib.h>
#include <string.h>
#include "ts_section_parser.h"
#include "ts_section_parser_error_code.h"
/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
inner structures
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
typedef struct {
void *prev;
void *next;
TS_SECTION sect;
int32_t ref;
} TS_SECTION_ELEM;
typedef struct {
TS_SECTION_ELEM *head;
TS_SECTION_ELEM *tail;
int32_t count;
} TS_SECTION_LIST;
typedef struct {
int32_t pid;
TS_SECTION_ELEM *work;
TS_SECTION_ELEM *last;
TS_SECTION_LIST pool;
TS_SECTION_LIST buff;
TS_SECTION_PARSER_STAT stat;
} TS_SECTION_PARSER_PRIVATE_DATA;
/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
constant values
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
#define MAX_RAW_SECTION_SIZE 4100
/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
function prottypes (interface method)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
static void release_ts_section_parser(void *parser);
static int reset_ts_section_parser(void *parser);
static int put_ts_section_parser(void *parser, TS_HEADER *hdr, uint8_t *data, int size);
static int get_ts_section_parser(void *parser, TS_SECTION *sect);
static int ret_ts_section_parser(void *parser, TS_SECTION *sect);
static int get_count_ts_section_parser(void *parser);
static int get_stat_ts_section_parser(void *parser, TS_SECTION_PARSER_STAT *stat);
/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
global function implementation (factory method)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
TS_SECTION_PARSER *create_ts_section_parser()
{
TS_SECTION_PARSER *r;
TS_SECTION_PARSER_PRIVATE_DATA *prv;
int n;
n = sizeof(TS_SECTION_PARSER_PRIVATE_DATA);
n += sizeof(TS_SECTION_PARSER);
prv = (TS_SECTION_PARSER_PRIVATE_DATA *)calloc(1, n);
if(prv == NULL){
/* failed on malloc() - no enough memory */
return NULL;
}
prv->pid = -1;
r = (TS_SECTION_PARSER *)(prv+1);
r->private_data = prv;
r->release = release_ts_section_parser;
r->reset = reset_ts_section_parser;
r->put = put_ts_section_parser;
r->get = get_ts_section_parser;
r->ret = ret_ts_section_parser;
r->get_count = get_count_ts_section_parser;
r->get_stat = get_stat_ts_section_parser;
return r;
}
/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
function prottypes (private method)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
static TS_SECTION_PARSER_PRIVATE_DATA *private_data(void *parser);
static void teardown(TS_SECTION_PARSER_PRIVATE_DATA *prv);
static int put_exclude_section_start(TS_SECTION_PARSER_PRIVATE_DATA *prv, uint8_t *data, int size);
static int put_include_section_start(TS_SECTION_PARSER_PRIVATE_DATA *prv, uint8_t *data, int size);
static void reset_section(TS_SECTION *sect);
static void append_section_data(TS_SECTION *sect, uint8_t *data, int size);
static int check_section_complete(TS_SECTION *sect);
static int compare_elem_section(TS_SECTION_ELEM *a, TS_SECTION_ELEM *b);
static void cancel_elem_empty(TS_SECTION_PARSER_PRIVATE_DATA *prv, TS_SECTION_ELEM *elem);
static void cancel_elem_error(TS_SECTION_PARSER_PRIVATE_DATA *prv, TS_SECTION_ELEM *elem);
static void cancel_elem_same(TS_SECTION_PARSER_PRIVATE_DATA *prv, TS_SECTION_ELEM *elem);
static void commit_elem_updated(TS_SECTION_PARSER_PRIVATE_DATA *prv, TS_SECTION_ELEM *elem);
static TS_SECTION_ELEM *query_work_elem(TS_SECTION_PARSER_PRIVATE_DATA *prv);
static void extract_ts_section_header(TS_SECTION *sect);
static TS_SECTION_ELEM *create_ts_section_elem();
static TS_SECTION_ELEM *get_ts_section_list_head(TS_SECTION_LIST *list);
static void put_ts_section_list_tail(TS_SECTION_LIST *list, TS_SECTION_ELEM *elem);
static void unlink_ts_section_list(TS_SECTION_LIST *list, TS_SECTION_ELEM *elem);
static void clear_ts_section_list(TS_SECTION_LIST *list);
static uint32_t crc32(uint8_t *head, uint8_t *tail);
/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
function implementation (interface method)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
static void release_ts_section_parser(void *parser)
{
TS_SECTION_PARSER_PRIVATE_DATA *prv;
prv = private_data(parser);
if(prv == NULL){
return;
}
teardown(prv);
memset(parser, 0, sizeof(TS_SECTION_PARSER));
free(prv);
}
static int reset_ts_section_parser(void *parser)
{
TS_SECTION_PARSER_PRIVATE_DATA *prv;
prv = private_data(parser);
if(prv == NULL){
return TS_SECTION_PARSER_ERROR_INVALID_PARAM;
}
teardown(prv);
return 0;
}
static int put_ts_section_parser(void *parser, TS_HEADER *hdr, uint8_t *data, int size)
{
TS_SECTION_PARSER_PRIVATE_DATA *prv;
prv = private_data(parser);
if( (prv == NULL) || (hdr == NULL) || (data == NULL) || (size < 1) ){
return TS_SECTION_PARSER_ERROR_INVALID_PARAM;
}
if( (prv->pid >= 0) && (prv->pid != hdr->pid) ){
return TS_SECTION_PARSER_ERROR_INVALID_TS_PID;
}
prv->pid = hdr->pid;
if(hdr->payload_unit_start_indicator == 0){
/* exclude section start */
return put_exclude_section_start(prv, data, size);
}else{
/* include section start */
return put_include_section_start(prv, data, size);
}
}
static int get_ts_section_parser(void *parser, TS_SECTION *sect)
{
TS_SECTION_PARSER_PRIVATE_DATA *prv;
TS_SECTION_ELEM *w;
prv = private_data(parser);
if( (prv == NULL) || (sect == NULL) ){
return TS_SECTION_PARSER_ERROR_INVALID_PARAM;
}
w = get_ts_section_list_head(&(prv->buff));
if(w == NULL){
memset(sect, 0, sizeof(TS_SECTION));
return TS_SECTION_PARSER_ERROR_NO_SECTION_DATA;
}
memcpy(sect, &(w->sect), sizeof(TS_SECTION));
put_ts_section_list_tail(&(prv->pool), w);
return 0;
}
static int ret_ts_section_parser(void *parser, TS_SECTION *sect)
{
TS_SECTION_PARSER_PRIVATE_DATA *prv;
TS_SECTION_ELEM *w;
prv = private_data(parser);
if( (prv == NULL) || (sect == NULL) ){
return TS_SECTION_PARSER_ERROR_INVALID_PARAM;
}
w = prv->pool.tail;
while(w != NULL){
if(w->sect.data == sect->data){
break;
}
w = (TS_SECTION_ELEM *)(w->prev);
}
if( (w != NULL) && (w->ref > 0) ){
w->ref -= 1;
}
return 0;
}
static int get_count_ts_section_parser(void *parser)
{
TS_SECTION_PARSER_PRIVATE_DATA *prv;
prv = private_data(parser);
if(prv == NULL){
return TS_SECTION_PARSER_ERROR_INVALID_PARAM;
}
return prv->buff.count;
}
static int get_stat_ts_section_parser(void *parser, TS_SECTION_PARSER_STAT *stat)
{
TS_SECTION_PARSER_PRIVATE_DATA *prv;
prv = private_data(parser);
if( (prv == NULL) || (stat == NULL) ){
return TS_SECTION_PARSER_ERROR_INVALID_PARAM;
}
memcpy(stat, &(prv->stat), sizeof(TS_SECTION_PARSER_STAT));
return 0;
}
/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
function implementation (private method)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
static TS_SECTION_PARSER_PRIVATE_DATA *private_data(void *parser)
{
TS_SECTION_PARSER_PRIVATE_DATA *r;
TS_SECTION_PARSER *p;
p = (TS_SECTION_PARSER *)parser;
if(p == NULL){
return NULL;
}
r = (TS_SECTION_PARSER_PRIVATE_DATA *)(p->private_data);
if( ((void *)(r+1)) != parser ){
return NULL;
}
return r;
}
static void teardown(TS_SECTION_PARSER_PRIVATE_DATA *prv)
{
prv->pid = -1;
if(prv->work != NULL){
free(prv->work);
prv->work = NULL;
}
prv->last = NULL;
clear_ts_section_list(&(prv->pool));
clear_ts_section_list(&(prv->buff));
memset(&(prv->stat), 0, sizeof(TS_SECTION_PARSER_STAT));
}
static int put_exclude_section_start(TS_SECTION_PARSER_PRIVATE_DATA *prv, uint8_t *data, int size)
{
TS_SECTION_ELEM *w;
w = prv->work;
if( (w == NULL) || (w->sect.raw == w->sect.tail) ){
/* no previous data */
return 0;
}
append_section_data(&(w->sect), data, size);
if(check_section_complete(&(w->sect)) == 0){
/* need more data */
return 0;
}
prv->work = NULL;
if( (w->sect.hdr.section_syntax_indicator != 0) &&
(crc32(w->sect.raw, w->sect.tail) != 0) ){
cancel_elem_error(prv, w);
return TS_SECTION_PARSER_WARN_CRC_MISSMATCH;
}
if(compare_elem_section(w, prv->last) == 0){
/* same section data */
cancel_elem_same(prv, w);
return 0;
}
commit_elem_updated(prv, w);
return 0;
}
static int put_include_section_start(TS_SECTION_PARSER_PRIVATE_DATA *prv, uint8_t *data, int size)
{
TS_SECTION_ELEM *w;
int pointer_field;
uint8_t *p;
uint8_t *tail;
int r;
int length;
p = data;
tail = p + size;
r = 0;
pointer_field = p[0];
p += 1;
if( (p+pointer_field) >= tail ){
/* input data is probably broken */
w = prv->work;
prv->work = NULL;
if(w != NULL) {
if(w->sect.raw != w->sect.tail){
cancel_elem_error(prv, w);
return TS_SECTION_PARSER_WARN_LENGTH_MISSMATCH;
}
cancel_elem_empty(prv, w);
}
return 0;
}
if(pointer_field > 0){
r = put_exclude_section_start(prv, p, pointer_field);
if(r < 0){
return r;
}
p += pointer_field;
}
w = prv->work;
prv->work = NULL;
if(w != NULL){
if(w->sect.raw != w->sect.tail){
cancel_elem_error(prv, w);
r = TS_SECTION_PARSER_WARN_LENGTH_MISSMATCH;
}else{
cancel_elem_empty(prv, w);
}
w = NULL;
}
do {
w = query_work_elem(prv);
if(w == NULL){
return TS_SECTION_PARSER_ERROR_NO_ENOUGH_MEMORY;
}
append_section_data(&(w->sect), p, tail-p);
if(check_section_complete(&(w->sect)) == 0){
/* need more data */
prv->work = w;
return 0;
}
length = (w->sect.tail - w->sect.raw);
if( (w->sect.hdr.section_syntax_indicator != 0) &&
(crc32(w->sect.raw, w->sect.tail) != 0) ){
cancel_elem_error(prv, w);
r = TS_SECTION_PARSER_WARN_CRC_MISSMATCH;
}else if(compare_elem_section(w, prv->last) == 0){
cancel_elem_same(prv, w);
}else{
commit_elem_updated(prv, w);
}
p += length;
} while ( (p < tail) && (p[0] != 0xff) );
return r;
}
static void reset_section(TS_SECTION *sect)
{
memset(&(sect->hdr), 0, sizeof(TS_SECTION_HEADER));
sect->tail = sect->raw;
sect->data = NULL;
}
static void append_section_data(TS_SECTION *sect, uint8_t *data, int size)
{
int m,n;
m = sect->tail - sect->raw;
n = MAX_RAW_SECTION_SIZE - m;
if(size < n){
n = size;
}
memcpy(sect->tail, data, n);
sect->tail += n;
m += n;
if(sect->data == NULL){
extract_ts_section_header(sect);
}
if(sect->data == NULL){
/* need more data */
return;
}
n = sect->hdr.section_length + 3;
if(m > n){
sect->tail = sect->raw + n;
}
return;
}
static int check_section_complete(TS_SECTION *sect)
{
int m,n;
if(sect->data == NULL){
return 0;
}
m = sect->tail - sect->raw;
n = sect->hdr.section_length + 3;
if(n > m){
return 0;
}
return 1;
}
static int compare_elem_section(TS_SECTION_ELEM *a, TS_SECTION_ELEM *b)
{
int m,n;
if( (a == NULL) || (b == NULL) ){
return 1;
}
m = a->sect.tail - a->sect.raw;
n = b->sect.tail - b->sect.raw;
if( m != n ){
return 1;
}
if(memcmp(a->sect.raw, b->sect.raw, m) != 0){
return 1;
}
return 0;
}
static void cancel_elem_empty(TS_SECTION_PARSER_PRIVATE_DATA *prv, TS_SECTION_ELEM *elem)
{
reset_section(&(elem->sect));
elem->ref = 0;
put_ts_section_list_tail(&(prv->pool), elem);
}
static void cancel_elem_error(TS_SECTION_PARSER_PRIVATE_DATA *prv, TS_SECTION_ELEM *elem)
{
reset_section(&(elem->sect));
elem->ref = 0;
put_ts_section_list_tail(&(prv->pool), elem);
prv->stat.total += 1;
prv->stat.error += 1;
}
static void cancel_elem_same(TS_SECTION_PARSER_PRIVATE_DATA *prv, TS_SECTION_ELEM *elem)
{
reset_section(&(elem->sect));
elem->ref = 0;
put_ts_section_list_tail(&(prv->pool), elem);
prv->stat.total +=1;
}
static void commit_elem_updated(TS_SECTION_PARSER_PRIVATE_DATA *prv, TS_SECTION_ELEM *elem)
{
if( (prv->last != NULL) && (prv->last->ref > 0) ){
prv->last->ref -= 1;
}
elem->ref = 2;
prv->last = elem;
put_ts_section_list_tail(&(prv->buff), elem);
prv->stat.total += 1;
prv->stat.unique += 1;
}
static TS_SECTION_ELEM *query_work_elem(TS_SECTION_PARSER_PRIVATE_DATA *prv)
{
TS_SECTION_ELEM *r;
r = prv->pool.head;
while(r != NULL){
if(r->ref < 1){
break;
}
r = (TS_SECTION_ELEM *)(r->next);
}
if(r != NULL){
unlink_ts_section_list(&(prv->pool), r);
reset_section(&(r->sect));
r->ref = 0;
return r;
}
return create_ts_section_elem();
}
static void extract_ts_section_header(TS_SECTION *sect)
{
int size;
uint8_t *p;
sect->data = NULL;
size = sect->tail - sect->raw;
if(size < 3){
/* need more data */
return;
}
p = sect->raw;
sect->hdr.table_id = p[0];
sect->hdr.section_syntax_indicator = (p[1] >> 7) & 0x01;
sect->hdr.private_indicator = (p[1] >> 6) & 0x01;
sect->hdr.section_length =((p[1] << 8) | p[2]) & 0x0fff;
if(sect->hdr.section_syntax_indicator == 0){
/* short format section header */
sect->data = p+3;
return;
}
/* long format section header */
if(size < 8){
/* need more data */
return;
}
sect->hdr.table_id_extension =((p[3] << 8) | p[4]);
sect->hdr.version_number = (p[5] >> 1) & 0x1f;
sect->hdr.current_next_indicator = p[5] & 0x01;
sect->hdr.section_number = p[6];
sect->hdr.last_section_number = p[7];
sect->data = p+8;
return;
}
static TS_SECTION_ELEM *create_ts_section_elem()
{
TS_SECTION_ELEM *r;
int n;
n = sizeof(TS_SECTION_ELEM) + MAX_RAW_SECTION_SIZE;
r = (TS_SECTION_ELEM *)calloc(1, n);
if(r == NULL){
/* failed on malloc() */
return NULL;
}
r->sect.raw = (uint8_t *)(r+1);
r->sect.tail = r->sect.raw;
return r;
}
static TS_SECTION_ELEM *get_ts_section_list_head(TS_SECTION_LIST *list)
{
TS_SECTION_ELEM *r;
if(list == NULL){/* invalid param */
return NULL;
}
r = list->head;
if(r == NULL){
return NULL;
}
list->head = (TS_SECTION_ELEM *)(r->next);
if(list->head != NULL){
list->head->prev = NULL;
list->count -= 1;
}else{
list->tail = NULL;
list->count = 0;
}
r->next = NULL;
return r;
}
static void put_ts_section_list_tail(TS_SECTION_LIST *list, TS_SECTION_ELEM *elem)
{
if( (list == NULL) || (elem == NULL) ){
/* invalid param */
return;
}
if(list->tail != NULL){
elem->prev = list->tail;
elem->next = NULL;
list->tail->next = elem;
list->tail = elem;
list->count += 1;
}else{
elem->prev = NULL;
elem->next = NULL;
list->head = elem;
list->tail = elem;
list->count = 1;
}
}
static void unlink_ts_section_list(TS_SECTION_LIST *list, TS_SECTION_ELEM *elem)
{
TS_SECTION_ELEM *prev;
TS_SECTION_ELEM *next;
if( (list == NULL) || (elem == NULL) ){
/* invalid param */
return;
}
prev = (TS_SECTION_ELEM *)(elem->prev);
next = (TS_SECTION_ELEM *)(elem->next);
if(prev != NULL){
prev->next = next;
}else{
list->head = next;
}
if(next != NULL){
next->prev = prev;
}else{
list->tail = prev;
}
list->count -= 1;
}
static void clear_ts_section_list(TS_SECTION_LIST *list)
{
TS_SECTION_ELEM *e;
TS_SECTION_ELEM *n;
if(list == NULL){ /* invalid param */
return;
}
e = list->head;
while(e != NULL){
n = (TS_SECTION_ELEM *)(e->next);
free(e);
e = n;
}
list->head = NULL;
list->tail = NULL;
list->count = 0;
}
static uint32_t crc32(uint8_t *head, uint8_t *tail)
{
uint32_t crc;
uint8_t *p;
static const uint32_t table[256] = {
0x00000000, 0x04C11DB7, 0x09823B6E, 0x0D4326D9,
0x130476DC, 0x17C56B6B, 0x1A864DB2, 0x1E475005,
0x2608EDB8, 0x22C9F00F, 0x2F8AD6D6, 0x2B4BCB61,
0x350C9B64, 0x31CD86D3, 0x3C8EA00A, 0x384FBDBD,
0x4C11DB70, 0x48D0C6C7, 0x4593E01E, 0x4152FDA9,
0x5F15ADAC, 0x5BD4B01B, 0x569796C2, 0x52568B75,
0x6A1936C8, 0x6ED82B7F, 0x639B0DA6, 0x675A1011,
0x791D4014, 0x7DDC5DA3, 0x709F7B7A, 0x745E66CD,
0x9823B6E0, 0x9CE2AB57, 0x91A18D8E, 0x95609039,
0x8B27C03C, 0x8FE6DD8B, 0x82A5FB52, 0x8664E6E5,
0xBE2B5B58, 0xBAEA46EF, 0xB7A96036, 0xB3687D81,
0xAD2F2D84, 0xA9EE3033, 0xA4AD16EA, 0xA06C0B5D,
0xD4326D90, 0xD0F37027, 0xDDB056FE, 0xD9714B49,
0xC7361B4C, 0xC3F706FB, 0xCEB42022, 0xCA753D95,
0xF23A8028, 0xF6FB9D9F, 0xFBB8BB46, 0xFF79A6F1,
0xE13EF6F4, 0xE5FFEB43, 0xE8BCCD9A, 0xEC7DD02D,
0x34867077, 0x30476DC0, 0x3D044B19, 0x39C556AE,
0x278206AB, 0x23431B1C, 0x2E003DC5, 0x2AC12072,
0x128E9DCF, 0x164F8078, 0x1B0CA6A1, 0x1FCDBB16,
0x018AEB13, 0x054BF6A4, 0x0808D07D, 0x0CC9CDCA,
0x7897AB07, 0x7C56B6B0, 0x71159069, 0x75D48DDE,
0x6B93DDDB, 0x6F52C06C, 0x6211E6B5, 0x66D0FB02,
0x5E9F46BF, 0x5A5E5B08, 0x571D7DD1, 0x53DC6066,
0x4D9B3063, 0x495A2DD4, 0x44190B0D, 0x40D816BA,
0xACA5C697, 0xA864DB20, 0xA527FDF9, 0xA1E6E04E,
0xBFA1B04B, 0xBB60ADFC, 0xB6238B25, 0xB2E29692,
0x8AAD2B2F, 0x8E6C3698, 0x832F1041, 0x87EE0DF6,
0x99A95DF3, 0x9D684044, 0x902B669D, 0x94EA7B2A,
0xE0B41DE7, 0xE4750050, 0xE9362689, 0xEDF73B3E,
0xF3B06B3B, 0xF771768C, 0xFA325055, 0xFEF34DE2,
0xC6BCF05F, 0xC27DEDE8, 0xCF3ECB31, 0xCBFFD686,
0xD5B88683, 0xD1799B34, 0xDC3ABDED, 0xD8FBA05A,
0x690CE0EE, 0x6DCDFD59, 0x608EDB80, 0x644FC637,
0x7A089632, 0x7EC98B85, 0x738AAD5C, 0x774BB0EB,
0x4F040D56, 0x4BC510E1, 0x46863638, 0x42472B8F,
0x5C007B8A, 0x58C1663D, 0x558240E4, 0x51435D53,
0x251D3B9E, 0x21DC2629, 0x2C9F00F0, 0x285E1D47,
0x36194D42, 0x32D850F5, 0x3F9B762C, 0x3B5A6B9B,
0x0315D626, 0x07D4CB91, 0x0A97ED48, 0x0E56F0FF,
0x1011A0FA, 0x14D0BD4D, 0x19939B94, 0x1D528623,
0xF12F560E, 0xF5EE4BB9, 0xF8AD6D60, 0xFC6C70D7,
0xE22B20D2, 0xE6EA3D65, 0xEBA91BBC, 0xEF68060B,
0xD727BBB6, 0xD3E6A601, 0xDEA580D8, 0xDA649D6F,
0xC423CD6A, 0xC0E2D0DD, 0xCDA1F604, 0xC960EBB3,
0xBD3E8D7E, 0xB9FF90C9, 0xB4BCB610, 0xB07DABA7,
0xAE3AFBA2, 0xAAFBE615, 0xA7B8C0CC, 0xA379DD7B,
0x9B3660C6, 0x9FF77D71, 0x92B45BA8, 0x9675461F,
0x8832161A, 0x8CF30BAD, 0x81B02D74, 0x857130C3,
0x5D8A9099, 0x594B8D2E, 0x5408ABF7, 0x50C9B640,
0x4E8EE645, 0x4A4FFBF2, 0x470CDD2B, 0x43CDC09C,
0x7B827D21, 0x7F436096, 0x7200464F, 0x76C15BF8,
0x68860BFD, 0x6C47164A, 0x61043093, 0x65C52D24,
0x119B4BE9, 0x155A565E, 0x18197087, 0x1CD86D30,
0x029F3D35, 0x065E2082, 0x0B1D065B, 0x0FDC1BEC,
0x3793A651, 0x3352BBE6, 0x3E119D3F, 0x3AD08088,
0x2497D08D, 0x2056CD3A, 0x2D15EBE3, 0x29D4F654,
0xC5A92679, 0xC1683BCE, 0xCC2B1D17, 0xC8EA00A0,
0xD6AD50A5, 0xD26C4D12, 0xDF2F6BCB, 0xDBEE767C,
0xE3A1CBC1, 0xE760D676, 0xEA23F0AF, 0xEEE2ED18,
0xF0A5BD1D, 0xF464A0AA, 0xF9278673, 0xFDE69BC4,
0x89B8FD09, 0x8D79E0BE, 0x803AC667, 0x84FBDBD0,
0x9ABC8BD5, 0x9E7D9662, 0x933EB0BB, 0x97FFAD0C,
0xAFB010B1, 0xAB710D06, 0xA6322BDF, 0xA2F33668,
0xBCB4666D, 0xB8757BDA, 0xB5365D03, 0xB1F740B4,
};
crc = 0xffffffff;
p = head;
while(p < tail){
crc = (crc << 8) ^ table[ ((crc >> 24) ^ p[0]) & 0xff ];
p += 1;
}
return crc;
}

40
src/ts_section_parser.h Normal file
View File

@ -0,0 +1,40 @@
#ifndef TS_SECTION_PARSER_H
#define TS_SECTION_PARSER_H
#include "ts_common_types.h"
typedef struct {
int64_t total; /* total received section count */
int64_t unique; /* unique section count */
int64_t error; /* crc and other error section count */
} TS_SECTION_PARSER_STAT;
typedef struct {
void *private_data;
void (* release)(void *parser);
int (* reset)(void *parser);
int (* put)(void *parser, TS_HEADER *hdr, uint8_t *data, int size);
int (* get)(void *parser, TS_SECTION *sect);
int (* ret)(void *parser, TS_SECTION *sect);
int (* get_count)(void *parser);
int (* get_stat)(void *parser, TS_SECTION_PARSER_STAT *stat);
} TS_SECTION_PARSER;
#ifdef __cplusplus
extern "C" {
#endif
extern TS_SECTION_PARSER *create_ts_section_parser();
#ifdef __cplusplus
}
#endif
#endif /* TS_SECTION_PARSER_H */

View File

@ -0,0 +1,12 @@
#ifndef TS_SECTION_PARSER_ERROR_CODE_H
#define TS_SECTION_PARESR_ERROR_CODE_H
#define TS_SECTION_PARSER_ERROR_INVALID_PARAM -1
#define TS_SECTION_PARSER_ERROR_NO_ENOUGH_MEMORY -2
#define TS_SECTION_PARSER_ERROR_INVALID_TS_PID -3
#define TS_SECTION_PARSER_ERROR_NO_SECTION_DATA -4
#define TS_SECTION_PARSER_WARN_CRC_MISSMATCH 1
#define TS_SECTION_PARSER_WARN_LENGTH_MISSMATCH 2
#endif /* TS_SECTION_PARSER_ERROR_CODE_H */