Android 11 以降では、Android チューナー フレームワークを使用して A/V コンテンツを配信できます。このフレームワークは、ベンダーのハードウェア パイプラインを使用するため、ローエンド SoC とハイエンド SoC の両方に適しています。また、高信頼実行環境(TEE)とセキュアなメディアパス(SMP)によって保護された音声および映像(A/V)コンテンツを配信するセキュアな方法を提供するため、制限が厳しいコンテンツ保護環境でも使用できます。
チューナーと Android CAS 間の標準化されたインターフェースにより、チューナー ベンダーと CAS ベンダー間の統合をすばやく行うことができます。チューナー インターフェースは、MediaCodec および AudioTrack と連携して、Android TV 用の 1 つのグローバル ソリューションを構築します。チューナー インターフェースは、主要なブロードキャスト標準に基づくデジタルテレビとアナログテレビの両方をサポートしています。
コンポーネント
Android 11 では、TV プラットフォーム用に 3 つのコンポーネントが特別に設計されています。
- Tuner HAL: フレームワークとベンダー間のインターフェース
- Tuner SDK API: フレームワークとアプリ間のインターフェース
- Tuner Resource Manager(TRM): Tuner HW リソースを調整
Android 11 では、次のコンポーネントが拡張されました。
- CAS V2
- TvInputService: TV 入力サービス(TIS)
- TvInputManagerService: TV 入力マネージャー サービス(TIMS)
- MediaCodec: メディア コーデック
- AudioTrack: 音声トラック
- MediaResourceManager: メディア リソース マネージャー(MRM)
 
図 1. Android TV コンポーネント間のインタラクション
機能
フロントエンドは、以下の DTV 標準をサポートしています。
- ATSC
- ATSC3
- DVB C/S/T
- ISDB S/S3/T
- アナログ
Tuner HAL 1.1 以降を搭載した Android 12 のフロントエンドは、以下の DTV 標準をサポートしています。
- DTMB
Demux は以下のストリーム プロトコルをサポートしています。
- トランスポート ストリーム(TS)
- MPEG メディア トランスポート プロトコル(MMTP)
- インターネット プロトコル(IP)
- Type length value (TLV)
- ATSC リンクレイヤ プロトコル(ALP)
Decrambler は、以下のコンテンツの保護をサポートしています。
- セキュアなメディアパス
- 明確なメディアパス
- セキュアなローカル レコード
- セキュアなローカル再生
Tuner API は、以下のユースケースをサポートします。
- スキャン
- ライブ
- 再生
- 録画
チューナー、MediaCodec、AudioTrack は、以下のデータフロー モードをサポートします。
- 明確なメモリバッファを含む ES ペイロード
- セキュアなメモリハンドルを含む ES ペイロード
- パススルー
全体的な設計
Tuner HAL は、Android フレームワークとベンダーのハードウェア間で定義されます。Tuner HAL の全体的な設計は以下のとおりです。
- フレームワークがベンダーに期待する内容と、ベンダーがそれを実現する方法を記述します。
- IFrontend、- IDemux、- IDescrambler、- IFilter、- IDvr、- ILnbインターフェースを介して、フロントエンド、Demux、descramber の機能をフレームワークにエクスポートします。
- MediaCodecや- AudioTrackなど、他のフレームワーク コンポーネントと Tuner HAL を統合する関数が含まれています。
Tuner Java クラスとネイティブ クラスが作成されます。
- Tuner Java API により、アプリは公開 API を通じてチューナー HAL にアクセスできます。
- ネイティブ クラスにより、Tuner HAL を使った大量の録画データや再生データに対する権限の制御と取り扱いが可能になります。
- ネイティブ チューナー モジュールは、Tuner Java クラスと Tuner HAL 間のブリッジとなります。
TRM クラスが作成されます。
- フロントエンド、LNB、CAS セッション、テレビ入力デバイスなどの限られたチューナー リソースを TV 入力 HAL から管理します。
- 不足しているリソースをアプリから再利用するためのルールを適用します。デフォルトのルールでは、フォアグラウンドが優先されます。
Media CAS と CAS HAL が拡張され、次の機能が導入されます。
- さまざまな使用目的とアルゴリズムのために CAS セッションを開きます。
- CICAM の削除や挿入など、動的な CAS システムをサポートします。
- キートークンを提供して Tuner HAL と統合します。
MediaCodec と AudioTrack が拡張され、次の機能が導入されます。
- セキュアな A/V メモリをコンテンツ入力として受け取ります。
- トンネルモードの再生でハードウェア A/V 同期を行うように構成されています。
- ES_payloadとパススルー モードをサポートするように構成されています。
 
図 2. Tuner HAL 内のコンポーネントの図
全体的なワークフロー
ライブ配信再生の呼び出しシーケンスを下の図に示します。
セットアップ
 
図 3. ライブ配信再生の設定シーケンス
A/V の取り扱い
 
図 4. ライブ配信再生の A/V の取り扱い
スクランブル化されたコンテンツの取り扱い
 
図 5. ライブ配信再生のスクランブル化されたコンテンツの取り扱い
A/V データの処理
 
図 6. ライブ配信再生の A/V の処理
Tuner SDK API
Tuner SDK API は、Tuner JNI、Tuner HAL、TunerResourceManager との連携を扱います。TIS アプリは、Tuner SDK API を使用して、フィルタや descrambler などのチューナーのリソースおよびサブコンポーネントにアクセスします。フロントエンドと demux は内部コンポーネントです。
 
図 7. Tuner SDK API との連携
バージョン
Android 12 以降、Tuner SDK API は、Tuner 1.0 と下位互換性のあるバージョン アップグレードである Tuner HAL 1.1 の新機能をサポートしています。
次の API を使用して、実行中の HAL のバージョンを確認できます。
- android.media.tv.tuner.TunerVersionChecker.getTunerVersion()
最低限必要な HAL バージョンについては、新しい Android 12 API のドキュメントをご覧ください。
パッケージ
Tuner SDK API は以下の 4 つのパッケージを提供します。
- android.media.tv.tuner
- android.media.tv.tuner.frontend
- android.media.tv.tuner.filter
- android.media.tv.tuner.dvr
 
図 8. Tuner SDK API パッケージ
Android.media.tv.tuner
このチューナー パッケージは、チューナー フレームワークを使用するためのエントリ ポイントです。TIS アプリはこのパッケージを使用することで、初期設定とコールバックを指定して、リソース インスタンスを初期化、取得します。
- tuner():- useCaseパラメータと- sessionIdパラメータを指定して、チューナー インスタンスを初期化します。
- tune():- FrontendSettingパラメータを指定して、フロントエンド リソースとチューナーを取得します。
- openFilter(): フィルタタイプを指定して、フィルタ インスタンスを取得します。
- openDvrRecorder(): バッファサイズを指定して、録画インスタンスを取得します。
- openDvrPlayback(): バッファサイズを指定して、再生インスタンスを取得します。
- openDescrambler(): descrambler インスタンスを取得します。
- openLnb(): 内部 LNB インスタンスを取得します。
- openLnbByName(): 外部 LNB インスタンスを取得します。
- openTimeFilter(): 時間フィルタ インスタンスを取得します。
Tuner パッケージは、フィルタ、DVR、フロントエンド パッケージでは対応していない機能を提供します。該当する機能は以下のとおりです。
- cancelTuning
- scan/- cancelScanning
- getAvSyncHwId
- getAvSyncTime
- connectCiCam1/- disconnectCiCam
- shareFrontendFromTuner
- updateResourcePriority
- setOnTuneEventListener
- setResourceLostListener
Android.media.tv.tuner.frontend
このフロントエンド パッケージには、フロントエンド関連の設定、情報、ステータス、イベント、機能がまとめられています。
クラス
FrontendSettings は、以下のクラスで異なる DTV 標準用に取得されます。
- AnalogFrontendSettings
- Atsc3FrontendSettings
- AtscFrontendSettings
- DvbcFrontendSettings
- DvbsFrontendSettings
- DvbtFrontendSettings
- Isdbs3FrontendSettings
- IsdbsFrontendSettings
- IsdbtFrontendSettings
Tuner HAL 1.1 以降を搭載した Android 12 以降では、次の DTV 標準がサポートされています。
- DtmbFrontendSettings
FrontendCapabilities は、以下のクラスで異なる DTV 標準用に取得されます。
- AnalogFrontendCapabilities
- Atsc3FrontendCapabilities
- AtscFrontendCapabilities
- DvbcFrontendCapabilities
- DvbsFrontendCapabilities
- DvbtFrontendCapabilities
- Isdbs3FrontendCapabilities
- IsdbsFrontendCapabilities
- IsdbtFrontendCapabilities
Tuner HAL 1.1 以降を搭載した Android 12 以降では、次の DTV 標準がサポートされています。
- DtmbFrontendCapabilities
FrontendInfo は、フロントエンドの情報を取得します。FrontendStatus は、フロントエンドの現在のステータスを取得します。OnTuneEventListener は、フロントエンドでイベントをリッスンします。TIS アプリは、ScanCallback を使用してフロントエンドからのスキャン メッセージを処理します。
チャンネル スキャン
テレビをセットアップするため、アプリは考えられる周波数をスキャンし、ユーザーがアクセスするチャンネル ラインナップを作成します。TIS は、Tuner.tune、Tuner.scan(BLIND_SCAN)、または Tuner.scan(AUTO_SCAN) を使用してチャンネル スキャンを完了します。
周波数、標準規格(T/T2、S/S2 など)、その他の必要な情報(PLD ID など)など、TIS にシグナルの正確な配信情報がある場合、より迅速なオプションとして Tuner.tune をおすすめします。
ユーザーが Tuner.tune を呼び出すと、次のアクションが発生します。
- TIS が Tuner.tuneを使用してFrontendSettingsに必須情報を入力する。
- シグナルがロックされている場合、HAL が Tune LOCKEDメッセージをレポートする。
- TIS が Frontend.getStatusを使用して必要な情報を収集する。
- TIS が周波数リストの次の使用可能な周波数に移動する。
TIS は、すべての周波数がなくなるまで Tuner.tune を再度呼び出します。
チューニング中、stopTune() または close() を呼び出して、Tuner.tune 呼び出しを一時停止または終了できます。
Tuner.scan(AUTO_SCAN)
TIS に Tuner.tune を使用するための十分な情報がないものの、周波数リストと標準規格タイプ(DVB T/C/S など)がある場合は、Tuner.scan(AUTO_SCAN) をおすすめします。
ユーザーが Tuner.scan(AUTO_SCAN) を呼び出すと、次のアクションが発生します。
- TIS が - Tuner.scan(AUTO_SCAN)を周波数が入力された- FrontendSettingsとともに使用する。
- シグナルがロックされている場合、HAL がスキャン - LOCKEDメッセージをレポートする。HAL は、他のスキャン メッセージをレポートしてシグナルに関する追加情報を提供する場合もあります。
- TIS が - Frontend.getStatusを使用して必要な情報を収集する。
- TIS が、HAL に対して - Tuner.scanを呼び出し、同じ周波数で次の設定に移行する。- FrontendSettings構造が空の場合、HAL は次の使用可能な設定を使用する。それ以外の場合、HAL が 1 回限りのスキャンに- FrontendSettingsを使用し、スキャン操作が完了したことを示す- ENDを送信する。
- TIS が、周波数のすべての設定がなくるまで上記の操作を繰り返す。 
- HAL が - ENDを送信し、スキャン操作が完了したことを示す。
- TIS が周波数リストの次の使用可能な周波数に移動する。 
TIS は、すべての周波数がなくなるまで Tuner.scan(AUTO_SCAN) を再度呼び出します。
スキャン中、stopScan() または close() を呼び出して、スキャンを一時停止または終了できます。
Tuner.scan(BLIND_SCAN)
TIS に周波数リストがなく、ベンダー HAL でユーザー指定のフロントエンドの周波数を検索してフロントエンド リソースを取得できる場合は、Tuner.scan(BLIND_SCAN) をおすすめします。
- TIS が Tuner.scan(BLIND_SCAN)を使用する。周波数の開始周波数はFrontendSettingsで指定できますが、TIS ではFrontendSettingsの他の設定は無視されます。
- シグナルがロックされている場合、HAL がスキャン LOCKEDメッセージをレポートする。
- TIS が Frontend.getStatusを使用して必要な情報を収集する。
- TIS は Tuner.scanを再度呼び出し、スキャンを続行する(FrontendSettingsは無視されます)。
- TIS が、周波数のすべての設定がなくるまで上記の操作を繰り返す。HAL が、TIS からのアクションを必要としない周波数の数値を増分する。HAL が PROGRESSをレポートする。
TIS は、すべての周波数がなくなるまで Tuner.scan(AUTO_SCAN) を再度呼び出します。HAL が END をレポートし、スキャン操作が完了したことを示します。
スキャン中、stopScan() または close() を呼び出して、スキャンを一時停止または終了できます。
 
図 9. TIS スキャンのフロー図
Android.media.tv.tuner.filter
フィルタ パッケージには、構成、設定、コールバック、イベントを含むフィルタ オペレーションがまとめられています。パッケージには、次のオペレーションが含まれます。オペレーションの完全なリストについては、Android のソースコードを参照してください。
- configure()
- start()
- stop()
- flush()
- read()
完全なリストについては、Android のソースコードを参照してください。
FilterConfiguration は、以下のクラスから取得されます。この構成はメインのフィルタタイプ用で、フィルタがデータを抽出するために使用するプロトコルを指定します。
- AlpFilterConfiguration
- IpFilterConfiguration
- MmtpFilterConfiguration
- TlvFilterConfiguration
- TsFilterConfiguration
これらの設定は次のクラスから取得されます。この設定はフィルタ サブタイプ用で、フィルタが除外できるデータの種類を指定します。
- SectionSettings
- AvSettings
- PesSettings
- RecordSettings
- DownloadSettings
FilterEvent は、以下のクラスから取得され、さまざまな種類のデータのイベントをレポートします。
- SectionEvent
- MediaEvent
- PesEvent
- TsRecordEvent
- MmtpRecordEvent
- TemiEvent
- DownloadEvent
- IpPayloadEvent
Tuner HAL 1.1 以降を搭載した Android 12 以降、以下のイベントがサポートされています。
- IpCidChangeEvent
- RestartEvent
- ScramblingStatusEvent
フィルタのイベントとデータ形式
| フィルタの種類 | フラグ | イベント | データ オペレーション | データ形式 | 
|---|---|---|---|---|
| TS.SECTIONMMTP.SECTIONIP.SECTIONTLV.SECTIONALP.SECTION | isRaw: | 必須: DemuxFilterStatus::DATA_READYDemuxFilterStatus::DATA_OVERFLOW推奨: DemuxFilterStatus::LOW_WATERDemuxFilterStatus::HIGH_WATER | イベントと内部スケジュールに従って、 Filter.read(buffer, offset, adjustedSize)を 1 回以上実行します。データは HAL の MQ からクライアント バッファにコピーされます。 | 構成済みのセッション パッケージは、別のセッション パッケージによって FMQ に入力されます。 | 
| isRaw: | 必須: DemuxFilterEvent::DemuxFilterSectionEvent[n]DemuxFilterStatus::DATA_READYDemuxFilterStatus::DATA_OVERFLOW省略可: DemuxFilterStatus::LOW_WATERDemuxFilterStatus::HIGH_WATER | for i=0; i<n; i++データは HAL の MQ からクライアント バッファにコピーされます。 | ||
| TS.PES | isRaw: | 必須: DemuxFilterStatus::DATA_READYDemuxFilterStatus::DATA_OVERFLOW推奨: DemuxFilterStatus::LOW_WATERDemuxFilterStatus::HIGH_WATER | イベントと内部スケジュールに従って、 Filter.read(buffer, offset, adjustedSize)を 1 回以上実行します。データは HAL の MQ からクライアント バッファにコピーされます。 | 構成済みの PES パッケージは、別の PES パッケージによって FMQ に入力されます。 | 
| isRaw: | 必須: DemuxFilterEvent::DemuxFilterPesEvent[n]DemuxFilterStatus::DATA_READYDemuxFilterStatus::DATA_OVERFLOW省略可: DemuxFilterStatus::LOW_WATERDemuxFilterStatus::HIGH_WATER | for i=0; i<n; i++データは HAL の MQ からクライアント バッファにコピーされます。 | ||
| MMTP.PES | isRaw: | 必須: DemuxFilterStatus::DATA_READYDemuxFilterStatus::DATA_OVERFLOW推奨: DemuxFilterStatus::LOW_WATERDemuxFilterStatus::HIGH_WATER | イベントと内部スケジュールに従って、 Filter.read(buffer, offset, adjustedSize)を 1 回以上実行します。データは HAL の MQ からクライアント バッファにコピーされます。 | 構成済みの MFU パッケージは、別の MFU パッケージによって FMQ に入力されます。 | 
| isRaw: | 必須: DemuxFilterEvent::DemuxFilterPesEvent[n]DemuxFilterStatus::DATA_READYDemuxFilterStatus::DATA_OVERFLOW省略可: DemuxFilterStatus::LOW_WATERDemuxFilterStatus::HIGH_WATER | for i=0; i<n; i++データは HAL の MQ からクライアント バッファにコピーされます。 | ||
| TS.TS | 該当なし | 必須: DemuxFilterStatus::DATA_READYDemuxFilterStatus::DATA_OVERFLOW推奨: DemuxFilterStatus::LOW_WATERDemuxFilterStatus::HIGH_WATER | イベントと内部スケジュールに従って、 Filter.read(buffer, offset, adjustedSize)を 1 回以上実行します。データは HAL の MQ からクライアント バッファにコピーされます。 | tsヘッダーで除外され除去されたtsがFMQ で入力されます。 | 
| TS.AudioTS.VideoMMTP.AudioMMTP.Video | isPassthrough: | 省略可: DemuxFilterStatus::DATA_READYDemuxFilterStatus::DATA_OVERFLOW | クライアントは DemuxFilterStatus::DATA_READYを受け取った後にMediaCodecを開始できます。クライアントは、 DemuxFilterStatus::DATA_OVERFLOWを受け取った後にFilter.flushを呼び出すことができます。 | 該当なし | 
| isPassthrough: | 必須: DemuxFilterEvent::DemuxFilterMediaEvent[n]DemuxFilterStatus::DATA_READYDemuxFilterStatus::DATA_OVERFLOW省略可: DemuxFilterStatus::LOW_WATERDemuxFilterStatus::HIGH_WATER | MediaCodecを使用するには:for i=0; i<n; i++AudioTrackのダイレクト オーディオを使用するには:for i=0; i<n; i++ | ION メモリ内の ES データまたは部分的な ES データ。 | |
| TS.PCRIP.NTPALP.PTP | 該当なし | 必須: 該当なし 省略可: 該当なし | 該当なし | 該当なし | 
| TS.RECORD | 該当なし | 必須:  DemuxFilterEvent::DemuxFilterTsRecordEvent[n]RecordStatus::DATA_READYRecordStatus::DATA_OVERFLOWRecordStatus::LOW_WATERRecordStatus::HIGH_WATER省略可: DemuxFilterStatus::DATA_READYDemuxFilterStatus::DATA_OVERFLOWDemuxFilterStatus::LOW_WATERDemuxFilterStatus::HIGH_WATER | インデックス データの場合: for i=0; i<n; i++録画コンテンツの場合、 RecordStatus::*と内部スケジュールに従って、次のいずれかを行います。
 | インデックス データの場合: イベント ペイロードで実施されます。 録画コンテンツの場合: 多重化 TS ストリームが FMQ 内に入力されます。 | 
| TS.TEMI | 該当なし | 必須: DemuxFilterEvent::DemuxFilterTemiEvent[n]省略可: DemuxFilterStatus::DATA_READYDemuxFilterStatus::DATA_OVERFLOWDemuxFilterStatus::LOW_WATERDemuxFilterStatus::HIGH_WATER | for i=0; i<n; i++ | 該当なし | 
| MMTP.MMTP | 該当なし | 必須: DemuxFilterStatus::DATA_READYDemuxFilterStatus::DATA_OVERFLOW推奨: DemuxFilterStatus::LOW_WATERDemuxFilterStatus::HIGH_WATER | イベントと内部スケジュールに従って、 Filter.read(buffer, offset, adjustedSize)を 1 回以上実行します。データは HAL の MQ からクライアント バッファにコピーされます。 | mmtpヘッダーで除外され除去されたmmtpがFMQ で入力されます。 | 
| MMTP.RECORD | 該当なし | 必須: DemuxFilterEvent::DemuxFilterMmtpRecordEvent[n]RecordStatus::DATA_READYRecordStatus::DATA_OVERFLOWRecordStatus::LOW_WATERRecordStatus::HIGH_WATER省略可: DemuxFilterStatus::DATA_READYDemuxFilterStatus::DATA_OVERFLOWDemuxFilterStatus::LOW_WATERDemuxFilterStatus::HIGH_WATER | インデックス データの場合:  for i=0; i<n; i++録画コンテンツの場合、 RecordStatus::*と内部スケジュールに従って、次のいずれかを行います。
 | インデックス データの場合: イベント ペイロードで実施されます。 録画コンテンツの場合: 多重化 TS ストリームが FMQ 内に入力されます。 録音用のフィルタソースが TLV.TLVからIP.IPへのパススルーの場合、録画されたストリームには TLV と IP ヘッダーがあります。 | 
| MMTP.DOWNLOAD | 該当なし | 必須: DemuxFilterEvent::DemuxFilterDownloadEvent[n]DemuxFilterStatus::DATA_READYDemuxFilterStatus::DATA_OVERFLOW省略可: DemuxFilterStatus::LOW_WATERDemuxFilterStatus::HIGH_WATER | for i=0; i<n; i++
        Filter.read(buffer, offset, DemuxFilterDownloadEvent[i].size)データは HAL の MQ からクライアント バッファにコピーされます。 | ダウンロード パッケージは、別の IP ダウンロード パッケージによって FMQ に入力されます。 | 
| IP.IP_PAYLOAD | 該当なし | 必須: DemuxFilterEvent::DemuxFilterIpPayloadEvent[n]DemuxFilterStatus::DATA_READYDemuxFilterStatus::DATA_OVERFLOW省略可: DemuxFilterStatus::LOW_WATERDemuxFilterStatus::HIGH_WATER | for i=0; i<n; i++
        Filter.read(buffer, offset, DemuxFilterIpPayloadEvent[i].size)データは HAL の MQ からクライアント バッファにコピーされます。 | IP ペイロード パッケージは、別の IP ペイロード パッケージによって FMQ に入力されます。 | 
| IP.IPTLV.TLVALP.ALP | isPassthrough: | 省略可: DemuxFilterStatus::DATA_READYDemuxFilterStatus::DATA_OVERFLOW | 除去されたプロトコル サブストリームは、フィルタ チェーンの次のフィルタをフィードします。 | 該当なし | 
| isPassthrough: | 必須: DemuxFilterStatus::DATA_READYDemuxFilterStatus::DATA_OVERFLOW推奨: DemuxFilterStatus::LOW_WATERDemuxFilterStatus::HIGH_WATER | イベントと内部スケジュールに従って、 Filter.read(buffer, offset, adjustedSize)を 1 回以上実行します。データは HAL の MQ からクライアント バッファにコピーされます。 | プロトコル ヘッダーを含む除去されたプロトコル サブストリームは FMQ で入力されます。 | |
| IP.PAYLOAD_THROUGHTLV.PAYLOAD_THROUGHALP.PAYLOAD_THROUGH | 該当なし | 省略可: DemuxFilterStatus::DATA_READYDemuxFilterStatus::DATA_OVERFLOW | 除外されたプロトコル ペイロードは、フィルタ チェーンの次のフィルタをフィードします。 | 該当なし | 
フィルタを使用して PSI/SI をビルドするフローの例
 
図 10. PSI/SI をビルドするフロー
- フィルタを開きます。 - Filter filter = tuner.openFilter( Filter.TYPE_TS, Filter.SUBTYPE_SECTION, /* bufferSize */1000, executor, filterCallback );
- フィルタを構成して開始します。 - Settings settings = SectionSettingsWithTableInfo .builder(Filter.TYPE_TS) .setTableId(2) .setVersion(1) .setCrcEnabled(true) .setRaw(false) .setRepeat(false) .build(); FilterConfiguration config = TsFilterConfiguration .builder() .setTpid(10) .setSettings(settings) .build(); filter.configure(config); filter.start();
- SectionEventを処理します。- FilterCallback filterCallback = new FilterCallback() { @Override public void onFilterEvent(Filter filter, FilterEvent[] events) { for (FilterEvent event : events) { if (event instanceof SectionEvent) { SectionEvent sectionEvent = (SectionEvent) event; int tableId = sectionEvent.getTableId(); int version = sectionEvent.getVersion(); int dataLength = sectionEvent.getDataLength(); int sectionNumber = sectionEvent.getSectionNumber(); filter.read(buffer, 0, dataLength); } } } };
フィルタから MediaEvent を使用するフローの例
 
図 11. フィルタから MediaEvent を使用するフロー
- A/V フィルタを開いて構成し、起動します。
- MediaEventを処理します。
- MediaEventを受信します。
- リニアブロックを codecにキューします。
- データが消費されたら A/V ハンドルを解放します。
Android.media.tv.tuner.dvr
DvrRecorder は、録画のための次のメソッドを提供します。
- configure
- attachFilter
- detachFilter
- start
- flush
- stop
- setFileDescriptor
- write
DvrPlayback は、再生のための次のメソッドを提供します。
- configure
- start
- flush
- stop
- setFileDescriptor
- read
DvrSettings は、DvrRecorder と DvrPlayback を構成するために使用されます。OnPlaybackStatusChangedListener と OnRecordStatusChangedListener は、DVR インスタンスのステータスをレポートするために使用されます。
録画を開始するフローの例
 
図 12. 録画を開始するフロー
- DvrRecorderを開いて構成し、起動します。- DvrRecorder recorder = openDvrRecorder(/* bufferSize */ 1000, executor, listener); DvrSettings dvrSettings = DvrSettings .builder() .setDataFormat(DvrSettings.DATA_FORMAT_TS) .setLowThreshold(100) .setHighThreshold(900) .setPacketSize(188) .build(); recorder.configure(dvrSettings); recorder.attachFilter(filter); recorder.setFileDescriptor(fd); recorder.start();
- RecordEventを受信し、インデックス情報を取得します。- FilterCallback filterCallback = new FilterCallback() { @Override public void onFilterEvent(Filter filter, FilterEvent[] events) { for (FilterEvent event : events) { if (event instanceof TsRecordEvent) { TsRecordEvent recordEvent = (TsRecordEvent) event; int tsMask = recordEvent.getTsIndexMask(); int scMask = recordEvent.getScIndexMask(); int packetId = recordEvent.getPacketId(); long dataLength = recordEvent.getDataLength(); // handle the masks etc. } } } };
- OnRecordStatusChangedListenerを初期化し、レコードデータを保存します。- OnRecordStatusChangedListener listener = new OnRecordStatusChangedListener() { @Override public void onRecordStatusChanged(int status) { // a customized way to consume data efficiently by using status as a hint. if (status == Filter.STATUS_DATA_READY) { recorder.write(size); } } };
Tuner HAL
Tuner HAL は HIDL をフォローし、フレームワークとベンダー ハードウェア間のインターフェースを定義します。ベンダーはインターフェースを使用して Tuner HAL を実装し、フレームワークはそれを使用して Tuner HAL 実装と通信します。
モジュール
Tuner HAL 1.0
| モジュール | 基本的な制御 | モジュール固有の制御 | HAL ファイル | 
|---|---|---|---|
| ITuner | 該当なし | frontend(open, getIds, getInfo)、openDemux、openDescrambler、openLnb、getDemuxCaps | ITuner.hal | 
| IFrontend | setCallback、getStatus、close | tune、stopTune、scan、stopScan、setLnb | IFrontend.halIFrontendCallback.hal | 
| IDemux | close | setFrontendDataSource、openFilter、openDvr、getAvSyncHwId、getAvSyncTime、connect/disconnectCiCam | IDemux.hal | 
| IDvr | close、start、stop、configure | attach/detachFilters、flush、getQueueDesc | IDvr.halIDvrCallback.hal | 
| IFilter | close、start、stop、configure、getId | flush、getQueueDesc、releaseAvHandle、setDataSource | IFilter.halIFilterCallback.hal | 
| ILnb | close、setCallback | setVoltage、setTone、setSatellitePosition、sendDiseqcMessage | ILnb.halILnbCallback.hal | 
| IDescrambler | close | setDemuxSource、setKeyToken、addPid、removePid | IDescrambler.hal | 
Tuner HAL 1.1(Tuner HAL 1.0 から取得)
| モジュール | 基本的な制御 | モジュール固有の制御 | HAL ファイル | 
|---|---|---|---|
| ITuner | 該当なし | getFrontendDtmbCapabilities | @1.1::ITuner.hal | 
| IFrontend | tune_1_1、scan_1_1、getStatusExt1_1 | link/unlinkCiCam | @1.1::IFrontend.hal@1.1::IFrontendCallback.hal | 
| IFilter | getStatusExt1_1 | configureIpCid、configureAvStreamType、getAvSharedHandle、configureMonitorEvent | @1.1::IFilter.hal@1.1::IFilterCallback.hal | 
 
図 13. Tuner HAL のモジュール間のインタラクションのフロー図
フィルタのリンク
Tuner HAL は、複数のレイヤのフィルタを他のフィルタにリンクできるように、フィルタのリンクをサポートしています。フィルタには、次のルールが適用されます。
- フィルタはツリーとしてリンクされ、閉じられたパスは使用できません。
- ルートノードは demux です。
- フィルタは独立して動作します。
- すべてのフィルタでデータの取得が開始されます。
- フィルタのリンクは最後のフィルタでフラッシュされます。
以下のコードブロックと図 14 は、複数のレイヤのフィルタリングの例を示しています。
demuxCaps = ITuner.getDemuxCap;
If (demuxCaps[IP][MMTP] == true) {
        ipFilter = ITuner.openFilter(<IP, ..>)
        mmtpFilter1 = ITuner.openFilter(<MMTP ..>)
        mmtpFilter2 = ITuner.openFilter(<MMTP ..>)
        mmtpFilter1.setDataSource(<ipFilter>)
        mmtpFilter2.setDataSource(<ipFilter>)
}
 
図 14. 複数のレイヤのフィルタのリンクを示すフロー図
チューナー リソース マネージャー
チューナー リソース マネージャー(TRM)以前は、2 つのアプリを切り替えるには同じチューナー ハードウェアが必要でした。テレビ入力フレームワーク(TIF)は「先行取得優先」メカニズムを使用しました。これは、最初にリソースを取得したアプリがリソースを保持することを意味します。ただし、このメカニズムは一部の複雑なユースケースには適さない場合があります。
TRM は、アプリのチューナー、TVInput、CAS のハードウェア リソースを管理するためのシステム サービスとして実行されます。TRM は「フォアグラウンド優先」メカニズムを使用します。このメカニズムでは、アプリのフォアグラウンドまたはバックグラウンドのステータスとユースケースのタイプに基づいてアプリの優先度を計算します。TRM は、その優先度に基づいてリソースを許可または取り消します。TRM は、ブロードキャスト、OTT、DVR の ATV リソース管理を一元管理します。
TRM インターフェース
TRM は、チューナー フレームワーク、MediaCas、TvInputHardwareManager の ITunerResourceManager.aidl で AIDL インターフェースを公開し、リソースを登録、リクエスト、またはリリースします。
クライアント管理のインターフェースは以下のとおりです。
- registerClientProfile(in ResourceClientProfile profile, IResourcesReclaimListener listener, out int[] clientId)
- unregisterClientProfile(in int clientId)
リソースをリクエストしてリリースするためのインターフェースを以下に示します。
- requestFrontend(TunerFrontendRequest request, int[] frontendHandle)/- releaseFrontend
- requestDemux(TunerDemuxRequest request, int[] demuxHandle)/- releaseDemux
- requestDescrambler(TunerDescramblerRequest request, int[] descramblerHandle)/- releaseDescrambler
- requestCasSession(CasSessionRequest request, int[] casSessionHandle)/- releaseCasSession
- requestLnb(TunerLnbRequest request, int[] lnbHandle)/- releaseLnb
クライアント クラスとリクエスト クラスを以下に示します。
- ResourceClientProfile
- ResourcesReclaimListener
- TunerFrontendRequest
- TunerDemuxRequest
- TunerDescramblerRequest
- CasSessionRequest
- TunerLnbRequest
クライアントの優先度
TRM は、クライアントのプロファイルのパラメータと構成ファイルの優先度値を使用して、クライアントの優先度を計算します。優先度は、クライアントの任意の優先値によって更新される場合もあります。
クライアントのプロファイルのパラメータ
TRM は、mTvInputSessionId からプロセス ID を取得して、アプリがフォアグラウンド アプリであるかバックグラウンド アプリかを判断します。mTvInputSessionId、TvInputService.onCreateSession または TvInputService.onCreateRecordingSession を作成するには、TIS セッションを初期化します。
mUseCase はセッションのユースケースを示します。事前定義されたユースケースを以下に示します。
TvInputService.PriorityHintUseCaseType  {
  PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK
  PRIORITY_HINT_USE_CASE_TYPE_LIVE
  PRIORITY_HINT_USE_CASE_TYPE_RECORD,
  PRIORITY_HINT_USE_CASE_TYPE_SCAN,
  PRIORITY_HINT_USE_CASE_TYPE_BACKGROUND
}
構成ファイル
デフォルトの構成ファイル
以下のデフォルト構成ファイルでは、事前定義されたユースケースの優先度を示しています。ユーザーは、カスタマイズされた構成ファイルを使用して値を変更できます。
| ユースケース | フォアグラウンド | バックグラウンド | 
|---|---|---|
| LIVE | 490 | 400 | 
| PLAYBACK | 480 | 300 | 
| RECORD | 600 | 500 | 
| SCAN | 450 | 200 | 
| BACKGROUND | 180 | 100 | 
カスタマイズされた構成ファイル
ベンダーは構成ファイル /vendor/etc/tunerResourceManagerUseCaseConfig.xml をカスタマイズできます。このファイルは、ユースケースのタイプとユースケースの優先度値を追加、削除、または更新するために使用されます。カスタマイズされたファイルでは、platform/hardware/interfaces/tv/tuner/1.0/config/tunerResourceManagerUseCaseConfigSample.xml をテンプレートとして使用できます。
たとえば、新しいベンダーのユースケースは VENDOR_USE_CASE__[A-Z0-9]+, [0 - 1000] です。形式は、platform/hardware/interfaces/tv/tuner/1.0/config/tunerResourceManagerUseCaseConfig.xsd に従う必要があります。
任意の優先度値と nice 値
TRM は、クライアントが任意の優先度値と nice 値を更新するための updateClientPriority を提供します。任意の優先度値は、ユースケースのタイプとセッション ID から計算された優先度値を上書きします。
nice 値は、別のクライアントとの競合した場合の、クライアントの動作の寛容度を示します。nice 値は、クライアントの優先度値が競合するクライアントと比較される前に、そのクライアントの優先度値を減少させます。
再利用メカニズム
下の図は、リソースの競合が発生したときに、リソースがどのように再利用され、割り当てられるかを示しています。
 
図 15. チューナー リソース間の競合における再利用メカニズムの図