調諧器架構

如果是 Android 11 以上版本,可以使用 Android Tuner 架構傳送影音內容。這個架構會使用供應商的硬體管道,因此適用於低階和高階 SoC。這個架構提供安全的方式,透過受信任的執行環境 (TEE) 和安全媒體路徑 (SMP) 傳送受保護的 A/V 內容,因此可用於高度受限的內容保護環境。

Tuner 和 Android CAS 之間的標準化介面,可加快 Tuner 供應商和 CAS 供應商的整合速度。Tuner 介面可與 MediaCodecAudioTrack 搭配使用,為 Android TV 建構單一全球解決方案。Tuner 介面支援數位電視和類比電視,主要依據廣播標準。

元件

Android 11 專為 TV 平台設計了三種元件。

  • Tuner HAL:架構與供應商之間的介面
  • Tuner SDK API:架構和應用程式之間的介面
  • Tuner Resource Manager (TRM):協調 Tuner 硬體資源

Android 11 強化了下列元件。

  • CAS V2
  • TvInputService 或電視輸入服務 (TIS)
  • TvInputManagerService 或 TV Input Manager Service (TIMS)
  • MediaCodec 或媒體轉碼器
  • AudioTrack或音軌
  • MediaResourceManager 或媒體資源管理員 (MRM)

Tuner 架構元件的流程圖。

圖 1. Android TV 元件之間的互動

功能

前端支援下列 DTV 標準。

  • ATSC
  • ATSC3
  • DVB C/S/T
  • ISDB S/S3/T
  • 類比

搭載 Android 12 的前端搭配 Tuner HAL 1.1 以上版本,支援下列 DTV 標準。

  • DTMB

Demux 支援下列串流通訊協定。

  • 傳輸串流 (TS)
  • MPEG 媒體傳輸通訊協定 (MMTP)
  • 網際網路通訊協定 (IP)
  • 類型長度值 (TLV)
  • ATSC 連結層通訊協定 (ALP)

解擾器支援下列內容保護機制。

  • 安全媒體路徑
  • 清除媒體路徑
  • 確保本機記錄安全
  • 安全播放本機內容

Tuner API 支援下列用途。

  • 掃描
  • 直播
  • 播放
  • 錄影

Tuner、MediaCodecAudioTrack 支援下列資料流模式。

  • ES 酬載,並清除記憶體緩衝區
  • 具有安全記憶體控制代碼的 ES 酬載
  • 透視

整體設計

Tuner HAL 是在 Android 架構和供應商硬體之間定義。

  • 說明架構對供應商的期望,以及供應商可能採取的做法。
  • 透過 IFrontendIDemuxIDescramblerIFilterIDvrILnb 介面,將前端、解多工和解擾器功能匯出至架構。
  • 包含將 Tuner HAL 與其他架構元件 (例如 MediaCodecAudioTrack) 整合的函式。

系統會建立 Tuner Java 類別和原生類別。

  • 應用程式可透過 Tuner Java API,透過公開 API 存取 Tuner HAL。
  • 原生類別可透過 Tuner HAL 控制權限,並處理大量錄製或播放資料。
  • 原生 Tuner 模組是 Tuner Java 類別和 Tuner HAL 之間的橋樑。

建立 TRM 類別。

  • 管理有限的 Tuner 資源,例如前端、LNB、 CAS 工作階段,以及 TV 輸入 HAL 中的 TV 輸入裝置。
  • 對應用程式套用規則,回收不足的資源。預設規則是前景勝出。

媒體 CAS 和 CAS HAL 經過強化,可提供下列功能。

  • 開啟不同用途和演算法的 CAS 工作階段。
  • 支援動態 CAS 系統,例如移除和插入 CICAM。
  • 提供金鑰權杖,與 Tuner HAL 整合。

MediaCodecAudioTrack 強化了下列功能。

  • 以安全音訊/視訊記憶體做為內容輸入。
  • 設定為在通道式播放中執行硬體 A/V 同步。
  • 已設定支援 ES_payload 和直通模式。

Tuner HAL 的整體設計。

圖 2. Tuner HAL 內的元件圖

整體工作流程

下圖說明直播播放的呼叫序列。

設定

設定直播播放順序圖表。

圖 3. 設定直播播放順序

處理音訊/影片

處理直播播放的音訊/視訊流程圖。

圖 4. 處理直播播放的音訊/視訊

處理內容遭竄改的情況

處理直播播放的加密內容流程圖。

圖 5. 處理直播播放時的內容亂碼

處理音訊/視訊資料

處理音訊/視訊資料,以便播放直播節目。

圖 6. 處理直播影片的音訊/視訊

Tuner SDK API

Tuner SDK API 會處理與 Tuner JNI、Tuner HAL 和 TunerResourceManager 的互動。TIS 應用程式會使用 Tuner SDK API 存取 Tuner 資源和子元件,例如篩選器和解擾器。前端和 demux 是內部元件。

Tuner SDK API 的流程圖。

圖 7. 與 Tuner SDK API 互動

版本

從 Android 12 開始,Tuner SDK API 支援 Tuner HAL 1.1 的新功能,這是 Tuner 1.0 的回溯相容版本升級。

使用下列 API 檢查正在執行的 HAL 版本。

  • android.media.tv.tuner.TunerVersionChecker.getTunerVersion()

如要瞭解最低 HAL 版本需求,請參閱 Android 12 新版 API 的說明文件。

套件

Tuner SDK API 提供下列四個套件。

  • android.media.tv.tuner
  • android.media.tv.tuner.frontend
  • android.media.tv.tuner.filter
  • android.media.tv.tuner.dvr

Tuner SDK API 套件的流程圖。

圖 8. Tuner SDK API 套件

Android.media.tv.tuner

Tuner 套件是使用 Tuner 架構的進入點。TIS 應用程式會使用這個套件,透過指定初始設定和回呼來初始化及取得資源例項。

  • tuner():指定 useCasesessionId 參數,藉此初始化 Tuner 例項。
  • tune():取得前端資源,並透過指定 FrontendSetting 參數進行調整。
  • openFilter():指定篩選器類型,取得篩選器例項。
  • openDvrRecorder():指定緩衝區大小,取得錄製例項。
  • openDvrPlayback():指定緩衝區大小,取得播放執行個體。
  • openDescrambler():取得解擾器執行個體。
  • 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

從 Android 12 開始,只要使用 Tuner HAL 1.1 以上版本,即可支援下列 DTV 標準。

  • DtmbFrontendSettings

FrontendCapabilities 是由下列類別針對不同 DTV 標準衍生而來。

  • AnalogFrontendCapabilities
  • Atsc3FrontendCapabilities
  • AtscFrontendCapabilities
  • DvbcFrontendCapabilities
  • DvbsFrontendCapabilities
  • DvbtFrontendCapabilities
  • Isdbs3FrontendCapabilities
  • IsdbsFrontendCapabilities
  • IsdbtFrontendCapabilities

從 Android 12 開始,只要使用 Tuner HAL 1.1 以上版本,即可支援下列 DTV 標準。

  • DtmbFrontendCapabilities

FrontendInfo 會擷取前端的資訊。 FrontendStatus 會擷取前端的目前狀態。 OnTuneEventListener 會監聽前端的事件。TIS 應用程式會使用 ScanCallback 處理前端的掃描訊息。

掃描頻道

設定電視時,應用程式會掃描可能的頻率,並建立頻道陣容供使用者存取。TIS 可能會使用 Tuner.tuneTuner.scan(BLIND_SCAN)Tuner.scan(AUTO_SCAN) 完成頻道掃描。

如果 TIS 擁有訊號的準確傳送資訊,例如頻率、標準 (例如 T/T2、S/S2) 和其他必要資訊 (例如 PLD ID),建議使用 Tuner.tune,因為速度較快。

使用者呼叫 Tuner.tune 時,會發生下列動作:

  • TIS 會使用 Tuner.tune,將必要資訊填入 FrontendSettings
  • 如果訊號已鎖定,HAL 會回報調整 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 會使用 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() 來暫停或結束掃描。

TIS 掃描程序的流程圖。

圖 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

從 Android 12 開始,只要使用 Tuner HAL 1.1 以上版本,即可支援下列事件。

  • IpCidChangeEvent
  • RestartEvent
  • ScramblingStatusEvent
篩選器中的事件和資料格式
篩選器類型 旗幟 事件 資料作業 資料格式
TS.SECTION
MMTP.SECTION
IP.SECTION
TLV.SECTION
ALP.SECTION
isRaw:
true
必填:
DemuxFilterStatus::DATA_READY
DemuxFilterStatus::DATA_OVERFLOW

建議:
DemuxFilterStatus::LOW_WATER
DemuxFilterStatus::HIGH_WATER
根據事件和內部時間表,執行
Filter.read(buffer, offset, adjustedSize) 一或多次。

資料會從 HAL 的 MQ 複製到用戶端緩衝區。
另一個工作階段套件會填入 FMQ 中的一個組裝工作階段套件。
isRaw:
false
必要:
DemuxFilterEvent::DemuxFilterSectionEvent[n]
DemuxFilterStatus::DATA_READY
DemuxFilterStatus::DATA_OVERFLOW

選用:
DemuxFilterStatus::LOW_WATER
DemuxFilterStatus::HIGH_WATER
for i=0; i<n; i++
Filter.read(buffer, offset, DemuxFilterSectionEven[i].size)


資料會從 HAL 的 MQ 複製到用戶端緩衝區。
TS.PES isRaw:
true
必要:
DemuxFilterStatus::DATA_READY
DemuxFilterStatus::DATA_OVERFLOW

建議:
DemuxFilterStatus::LOW_WATER
DemuxFilterStatus::HIGH_WATER
根據事件和內部時間表,執行
Filter.read(buffer, offset, adjustedSize) 一或多次。

資料會從 HAL 的 MQ 複製到用戶端緩衝區。
一個組裝好的 PES 封包會由另一個 PES 封包填入 FMQ。
isRaw:
false
必要:
DemuxFilterEvent::DemuxFilterPesEvent[n]
DemuxFilterStatus::DATA_READY
DemuxFilterStatus::DATA_OVERFLOW

選用:
DemuxFilterStatus::LOW_WATER
DemuxFilterStatus::HIGH_WATER
for i=0; i<n; i++
Filter.read(buffer, offset, DemuxFilterPesEven[i].size)


資料會從 HAL 的 MQ 複製到用戶端緩衝區。
MMTP.PES isRaw:
true
必要:
DemuxFilterStatus::DATA_READY
DemuxFilterStatus::DATA_OVERFLOW

建議:
DemuxFilterStatus::LOW_WATER
DemuxFilterStatus::HIGH_WATER
根據事件和內部時間表,執行
Filter.read(buffer, offset, adjustedSize) 一或多次。

資料會從 HAL 的 MQ 複製到用戶端緩衝區。
一個組裝好的 MFU 封裝會由另一個 MFU 封裝填入 FMQ。
isRaw:
false
必要:
DemuxFilterEvent::DemuxFilterPesEvent[n]
DemuxFilterStatus::DATA_READY
DemuxFilterStatus::DATA_OVERFLOW

選用:
DemuxFilterStatus::LOW_WATER
DemuxFilterStatus::HIGH_WATER
for i=0; i<n; i++
Filter.read(buffer, offset, DemuxFilterPesEven[i].size)


資料會從 HAL 的 MQ 複製到用戶端緩衝區。
TS.TS
必要:
DemuxFilterStatus::DATA_READY
DemuxFilterStatus::DATA_OVERFLOW

建議:
DemuxFilterStatus::LOW_WATER
DemuxFilterStatus::HIGH_WATER
根據事件和內部時間表,執行
Filter.read(buffer, offset, adjustedSize) 一或多次。

資料會從 HAL 的 MQ 複製到用戶端緩衝區。
已篩除 ts,且 ts 標頭
已填入 FMQ。
TS.Audio
TS.Video
MMTP.Audio
MMTP.Video
isPassthrough:
true
選用:
DemuxFilterStatus::DATA_READY
DemuxFilterStatus::DATA_OVERFLOW
用戶端收到 DemuxFilterStatus::DATA_READY 後,即可開始 MediaCodec
用戶端收到 DemuxFilterStatus::DATA_OVERFLOW 後,即可呼叫 Filter.flush
isPassthrough:
false
必要:
DemuxFilterEvent::DemuxFilterMediaEvent[n]
DemuxFilterStatus::DATA_READY
DemuxFilterStatus::DATA_OVERFLOW

選用:
DemuxFilterStatus::LOW_WATER
DemuxFilterStatus::HIGH_WATER
如何使用「MediaCodec」:
for i=0; i<n; i++
linearblock = MediaEvent[i].getLinearBlock();
codec.startQueueLinearBlock(linearblock)
linearblock.recycle()


如何使用「AudioTrack」的直接音訊:
for i=0; i<n; i++
audioHandle = MediaEvent[i].getAudioHandle();
audiotrack.write(encapsulated(audiohandle))
ION 記憶體中的 ES 或部分 ES 資料。
TS.PCR
IP.NTP
ALP.PTP
必要:不適用
選填:不適用
不適用
TS.RECORD 必要:
DemuxFilterEvent::DemuxFilterTsRecordEvent[n]
RecordStatus::DATA_READY
RecordStatus::DATA_OVERFLOW
RecordStatus::LOW_WATER
RecordStatus::HIGH_WATER

選用:
DemuxFilterStatus::DATA_READY
DemuxFilterStatus::DATA_OVERFLOW
DemuxFilterStatus::LOW_WATER
DemuxFilterStatus::HIGH_WATER
索引資料:
for i=0; i<n; i++
DemuxFilterTsRecordEvent[i];


錄製內容: 根據 RecordStatus::* 和內部時間表,執行下列其中一項操作:
  • 執行 DvrRecord.write(adustedSize) 一或多次,即可儲存。
    資料會從 HAL 的 MQ 轉移至儲存空間。
  • 執行 DvrRecord.write(buffer, adustedSize) 一或多次,即可緩衝。
    資料會從 HAL 的 MQ 複製到用戶端緩衝區。
索引資料:在事件酬載中傳送。

錄製內容:在 FMQ 中填入多工 TS 串流。
TS.TEMI 必要:
DemuxFilterEvent::DemuxFilterTemiEvent[n]

選用:
DemuxFilterStatus::DATA_READY
DemuxFilterStatus::DATA_OVERFLOW
DemuxFilterStatus::LOW_WATER
DemuxFilterStatus::HIGH_WATER
for i=0; i<n; i++
DemuxFilterTemiEvent[i];
MMTP.MMTP 必要:
DemuxFilterStatus::DATA_READY
DemuxFilterStatus::DATA_OVERFLOW

建議:
DemuxFilterStatus::LOW_WATER
DemuxFilterStatus::HIGH_WATER
根據事件和內部時間表,執行
Filter.read(buffer, offset, adjustedSize) 一或多次。

資料會從 HAL 的 MQ 複製到用戶端緩衝區。
已篩除 mmtp,且 mmtp 標頭
已填入 FMQ。
MMTP.RECORD 必要:
DemuxFilterEvent::DemuxFilterMmtpRecordEvent[n]
RecordStatus::DATA_READY
RecordStatus::DATA_OVERFLOW
RecordStatus::LOW_WATER
RecordStatus::HIGH_WATER

選用:
DemuxFilterStatus::DATA_READY
DemuxFilterStatus::DATA_OVERFLOW
DemuxFilterStatus::LOW_WATER
DemuxFilterStatus::HIGH_WATER
索引資料: for i=0; i<n; i++
DemuxFilterMmtpRecordEvent[i];


錄製內容:根據 RecordStatus::*和內部時間表,採取下列其中一種做法:
  • 執行 DvrRecord.write(adjustedSize) 一或多次,即可儲存。
    資料會從 HAL 的 MQ 轉移至儲存空間。
  • 執行 DvrRecord.write(buffer, adjustedSize)一或多次,即可緩衝。
    資料會從 HAL 的 MQ 複製到用戶端緩衝區。
索引資料:在事件酬載中傳送。

錄製內容:填入 FMQ 的混合錄製串流。

如果記錄的篩選器來源是 TLV.TLVIP.IP (含直通),記錄的串流會包含 TLV 和 IP 標頭。
MMTP.DOWNLOAD 必要:
DemuxFilterEvent::DemuxFilterDownloadEvent[n]
DemuxFilterStatus::DATA_READY
DemuxFilterStatus::DATA_OVERFLOW

選用:
DemuxFilterStatus::LOW_WATER
DemuxFilterStatus::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_READY
DemuxFilterStatus::DATA_OVERFLOW

選用:
DemuxFilterStatus::LOW_WATER
DemuxFilterStatus::HIGH_WATER
for i=0; i<n; i++ Filter.read(buffer, offset, DemuxFilterIpPayloadEvent[i].size)

資料會從 HAL 的 MQ 複製到用戶端緩衝區。
另一個 IP 酬載封包會填入 FMQ 的 IP 酬載封包。
IP.IP
TLV.TLV
ALP.ALP
isPassthrough:
true
選用:
DemuxFilterStatus::DATA_READY
DemuxFilterStatus::DATA_OVERFLOW
篩除的通訊協定子串流會饋送至篩選器鏈結中的下一個篩選器。
isPassthrough:
false
必要:
DemuxFilterStatus::DATA_READY
DemuxFilterStatus::DATA_OVERFLOW

建議:
DemuxFilterStatus::LOW_WATER
DemuxFilterStatus::HIGH_WATER
根據事件和內部時間表,執行
Filter.read(buffer, offset, adjustedSize) 一或多次。

資料會從 HAL 的 MQ 複製到用戶端緩衝區。
已填入通訊協定標頭的通訊協定子串流已遭濾除。 FMQ。
IP.PAYLOAD_THROUGH
TLV.PAYLOAD_THROUGH
ALP.PAYLOAD_THROUGH
選用:
DemuxFilterStatus::DATA_READY
DemuxFilterStatus::DATA_OVERFLOW
篩除的通訊協定酬載會提供給篩選器鏈結中的下一個篩選器。
使用篩選器建構 PSI/SI 的範例流程

使用篩選器建構 PSI/SI 的範例流程。

圖 10. 建構 PSI/SI 的流程

  1. 開啟篩選器。

    Filter filter = tuner.openFilter(
      Filter.TYPE_TS,
      Filter.SUBTYPE_SECTION,
      /* bufferSize */1000,
      executor,
      filterCallback
    );
    
  2. 設定並啟動篩選器。

    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();
    
  3. 處理 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 的範例流程

從篩選器使用 MediaEvent 的流程範例。

圖 11. 從篩選器使用 MediaEvent 的流程

  1. 開啟、設定及啟動 A/V 篩選器。
  2. 處理 MediaEvent
  3. 接收 MediaEvent
  4. 將線性區塊排入 codec 的佇列。
  5. 資料耗用完畢後,請釋放音訊/視訊控制代碼。

Android.media.tv.tuner.dvr

DvrRecorder 提供下列錄製方法。

  • configure
  • attachFilter
  • detachFilter
  • start
  • flush
  • stop
  • setFileDescriptor
  • write

DvrPlayback 提供這些播放方法。

  • configure
  • start
  • flush
  • stop
  • setFileDescriptor
  • read

DvrSettings 用於設定 DvrRecorderDvrPlaybackOnPlaybackStatusChangedListenerOnRecordStatusChangedListener 用於回報 DVR 執行個體的狀態。

開始錄製的範例流程

開始錄製的範例流程。

圖 12. 開始錄製的流程

  1. 開啟、設定及啟動 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();
    
  2. 接收 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. }
          }
        }
    };
    
  3. 初始化 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);
          }
        }
      };
    

調諧器 HAL

Tuner HAL 遵循 HIDL,並定義架構與供應商硬體之間的介面。供應商會使用這個介面實作 Tuner HAL,架構則會使用這個介面與 Tuner HAL 實作項目通訊。

模組

調諧器 HAL 1.0

模組 基本控制選項 模組專屬控制項 HAL 檔案
ITuner frontend(open, getIds, getInfo)openDemuxopenDescrambleropenLnbgetDemuxCaps ITuner.hal
IFrontend setCallbackgetStatusclose tunestopTunescanstopScansetLnb IFrontend.hal
IFrontendCallback.hal
IDemux close setFrontendDataSourceopenFilteropenDvrgetAvSyncHwIdgetAvSyncTimeconnect / disconnectCiCam IDemux.hal
IDvr closestartstopconfigure attach/detachFiltersflushgetQueueDesc IDvr.hal
IDvrCallback.hal
IFilter closestartstopconfiguregetId flushgetQueueDescreleaseAvHandlesetDataSource IFilter.hal
IFilterCallback.hal
ILnb closesetCallback setVoltagesetTonesetSatellitePositionsendDiseqcMessage ILnb.hal
ILnbCallback.hal
IDescrambler close setDemuxSourcesetKeyTokenaddPidremovePid IDescrambler.hal

Tuner HAL 1.1 (衍生自 Tuner HAL 1.0)

模組 基本控制選項 模組專屬控制項 HAL 檔案
ITuner getFrontendDtmbCapabilities @1.1::ITuner.hal
IFrontend tune_1_1scan_1_1getStatusExt1_1 link/unlinkCiCam @1.1::IFrontend.hal
@1.1::IFrontendCallback.hal
IFilter getStatusExt1_1 configureIpCidconfigureAvStreamTypegetAvSharedHandleconfigureMonitorEvent @1.1::IFilter.hal
@1.1::IFilterCallback.hal

Tuner 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. 多個圖層的篩選器連結流程圖

調諧器資源管理員

在 Tuner Resource Manager (TRM) 推出前,切換兩個應用程式需要使用相同的 Tuner 硬體。電視輸入架構 (TIF) 採用「先搶先贏」機制,也就是先取得資源的應用程式會保留該資源。不過,這個機制可能不適合某些複雜的用途。

TRM 會以系統服務的形式執行,負責管理應用程式的 Tuner、TVInput 和 CAS 硬體資源。TRM 使用「前景勝出」機制,根據應用程式的前景或背景狀態和用途類型計算應用程式的優先順序。TRM 會根據優先順序授予或撤銷資源。TRM 可集中管理 ATV 資源,適用於傳統電視、OTT 和 DVR。

TRM 介面

TRM 會在 ITunerResourceManager.aidlMediaCasTvInputHardwareManager 中公開 Tuner 架構的 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,判斷應用程式是前景還是背景應用程式。如要建立 mTvInputSessionIdTvInputService.onCreateSessionTvInputService.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 會提供 updateClientPriority,供用戶端更新任意優先順序值和 nice 值。任意優先順序值會覆寫從用途類型和工作階段 ID 計算出的優先順序值。

當用戶端與其他用戶端發生衝突時,nice 值會指出用戶端的行為有多寬鬆。在與競爭用戶端比較優先順序值之前,nice 值會先降低用戶端的優先順序值。

回收機制

下圖顯示發生資源衝突時,資源的回收和指派方式。

回收機制程序圖。

圖 15. Tuner 資源發生衝突時的回收機制圖