多媒體隧道

多媒體通道讓壓縮的影片資料能夠穿過硬體 不必由應用程式程式碼處理 Android 架構程式碼。Android 堆疊下方的裝置特定程式碼 決定要傳送至螢幕的影片影格和傳送時間 比較影片影格顯示時間戳記與下列任一項 內部時鐘的類型:

  • 如要在 Android 5 以上版本中隨選播放影片, AudioTrack敬上 與音訊簡報時間戳記同步的時鐘 通過 應用程式中的檔案

  • 如要在 Android 11 以上版本中播放即時直播,請參閱節目的參考時鐘 (PCR) 或系統時間 (STC), 調音器

背景

在 Android 裝置上播放傳統影片播放功能通知 應用程式會載入經過壓縮的影片影格。接著 版本 以相同系統時鐘轉譯顯示至螢幕上的已解碼影片影格 對應音訊影格的時間 擷取歷來資料 AudioTimestamps 例項來計算正確時間。

通道式影片播放時,可略過應用程式的程式碼,因此能減少 與影片相關的處理程序,因此能更有效率地顯示影片 會視原始設備製造商 (OEM) 實作而定。還能提供更準確的影片 調整頻率和與所選時鐘 (PRC、STC 或音訊) 的同步 從 Android 更新時間之間可能存在偏差所造成的時間問題 轉譯影片的要求,以及實際硬體 Vsync 的時間。不過 此外,通道設定也有助於減少對 GPU 效果的支援,例如: 模糊處理 子母畫面 (PiP) 視窗中的緩衝區或圓角,因為緩衝區會 繞過 Android 圖形堆疊。

下圖顯示通道簡化了影片播放程序。

傳統與隧道模式的比較

圖 1. 比較傳統與通道影片播放程序

應用程式開發人員專區

大多數應用程式開發人員都會整合用於播放的程式庫 在大多數情況下 只需重新設定 每個通道都需要程式庫用於低階導入通道 請依照下列指示進行。

在 Android 5 以上版本中,隨選播放影片:

  1. 建立 SurfaceView 執行個體。

  2. 建立 audioSessionId 執行個體。

  3. 使用 audioSessionId 建立 AudioTrackMediaCodec 執行個體 執行個體已建立於步驟 2 中。

  4. 使用 AudioTrack 的顯示時間戳記,將音訊資料排入佇列 產生的第一個音訊影格

如要在 Android 11 以上版本中播放即時播送內容:

  1. 建立 SurfaceView 執行個體。

  2. Tuner 取得 avSyncHwId 執行個體。

  3. 使用 avSyncHwId 執行個體建立 AudioTrackMediaCodec 執行個體

API 呼叫流程如以下程式碼片段所示:

aab.setContentType(AudioAttributes.CONTENT_TYPE_MOVIE);

// configure for audio clock sync
aab.setFlag(AudioAttributes.FLAG_HW_AV_SYNC);
// or, for tuner clock sync (Android 11 or higher)
new tunerConfig = TunerConfiguration(0, avSyncId);
aab.setTunerConfiguration(tunerConfig);
if (codecName == null) {
  return FAILURE;
}

// configure for audio clock sync
mf.setInteger(MediaFormat.KEY_AUDIO_SESSION_ID, audioSessionId);
// or, for tuner clock sync (Android 11 or higher)
mf.setInteger(MediaFormat.KEY_HARDWARE_AV_SYNC_ID, avSyncId);

隨選影片播放的行為

因為通道式隨選影片播放是隱含與 AudioTrack 的關聯 通道影片播放時,通道播放行為的運作模式可能取決於行為 以及音訊播放等等

  • 在大多數裝置上,系統預設會在播放音訊後顯示影片影格 即開始播放。不過,應用程式可能需要在 開始播放音訊,例如顯示使用者目前的影片 繼續跳轉時的位置和位置

    • 表示應盡快算繪第一個待播影片影格 解碼器則會產生 PARAMETER_KEY_TUNNEL_PEEK敬上 參數傳送至 1。對壓縮過的影片在佇列中重新排序時 (例如 B 框架 也就是說,第一個顯示的影片影格一律應為 I-Frame。

    • 如果不想在音訊前顯示第一個加入佇列的影片影格 就開始播放,請將這個參數設為 0

    • 如未設定這項參數,原始設備製造商 (OEM) 會判斷裝置的行為。

  • 未將音訊資料提供給 AudioTrack 且緩衝區為空白時 (音訊不足),影片播放中斷,直到寫入更多音訊資料為止 因為音訊時鐘不再繼續前進

  • 在播放過程中,出現應用程式無法正確修正的停滯情形,可能會顯示在 輸出音訊的時間戳記在這種情況下,原始設備製造商 (OEM) 會修正負值 落差、因為停滯目前影片畫面而造成落差 影片影格或插入無聲音訊影格 (視原始設備製造商 (OEM) 而定) )。應用程式的 AudioTimestamp 影格位置並未增加 已插入無聲音訊影格。

裝置製造商專用

設定

原始設備製造商 (OEM) 應建立個別的影片解碼器,用來支援通道影片播放。 這個解碼器應公告 media_codecs.xml 檔案:

<Feature name="tunneled-playback" required="true"/>

如果經過通道的 MediaCodec 執行個體設定了音訊工作階段 ID, 對這個 HW_AV_SYNC ID 的查詢為 AudioFlinger

if (entry.getKey().equals(MediaFormat.KEY_AUDIO_SESSION_ID)) {
    int sessionId = 0;
    try {
        sessionId = (Integer)entry.getValue();
    }
    catch (Exception e) {
        throw new IllegalArgumentException("Wrong Session ID Parameter!");
    }
    keys[i] = "audio-hw-sync";
    values[i] = AudioSystem.getAudioHwSyncForSession(sessionId);
}

執行這項查詢時 AudioFlinger 會擷取 HW_AV_SYNC ID 再於內部將該裝置與音訊內容建立關聯 工作階段 ID:

audio_hw_device_t *dev = mPrimaryHardwareDev->hwDevice();
char *reply = dev->get_parameters(dev, AUDIO_PARAMETER_HW_AV_SYNC);
AudioParameter param = AudioParameter(String8(reply));
int hwAVSyncId;
param.getInt(String8(AUDIO_PARAMETER_HW_AV_SYNC), hwAVSyncId);

如果已建立 AudioTrack 執行個體,則 HW_AV_SYNC ID 為 傳遞到具有相同音訊工作階段 ID 的輸出串流。如果還未 HW_AV_SYNC ID 會傳遞至輸出串流 建立「AudioTrack」。使用者必須透過播放 執行緒

mOutput->stream->common.set_parameters(&mOutput->stream->common, AUDIO_PARAMETER_STREAM_HW_AV_SYNC, hwAVSyncId);

HW_AV_SYNC ID,無論是對應至音訊輸出串流或 Tuner 設定會傳遞至 OMX 或 Codec2 元件,讓 原始設備製造商 (OEM) 代碼可以將轉碼器與對應的音訊輸出串流建立關聯,或是 調整器串流

在元件設定期間,OMX 或 Codec2 元件應傳回 可用來將轉碼器與 Hardware Composer 建立關聯的側邊頻帶控點 (HWC) 層當應用程式將途徑與 MediaCodec 建立關聯時,此側邊頻帶 帳號代碼會透過 SurfaceFlinger 向下傳遞至 HWC,以設定 視為 「sideband」層。

err = native_window_set_sideband_stream(nativeWindow.get(), sidebandHandle);
if (err != OK) {
  ALOGE("native_window_set_sideband_stream(%p) failed! (err %d).", sidebandHandle, err);
  return err;
}

HWC 負責接收來自 與相關的音訊輸出串流同步,或者 調音器程式參考時鐘,將緩衝區與目前的 並顯示產生的圖片。發生這種情況 獨立於正常的準備和設定的週期準備與設定呼叫 只有在其他圖層變更,或側邊頻層的屬性時 (例如位置或大小) 變動。

OMX

通道解碼器元件應支援下列項目:

  • 設定擴充的 OMX.google.android.index.configureVideoTunnelMode 參數,該參數使用 ConfigureVideoTunnelModeParams 結構將 (與音訊輸出裝置相關聯的 HW_AV_SYNC ID)。

  • 設定 OMX_IndexConfigAndroidTunnelPeek 參數,指出 要顯示或不算繪第一個已解碼的影片影格,無論如何 是否開始播放音訊。

  • 在第一個通道時傳送 OMX_EventOnFirstTunnelFrameReady 事件 影片影格已經過解碼,隨時可以顯示。

Android 開放原始碼計畫實作項目會在 ACodec敬上 到 OMXNodeInstance。 如以下程式碼片段所示:

OMX_INDEXTYPE index;
OMX_STRING name = const_cast<OMX_STRING>(
        "OMX.google.android.index.configureVideoTunnelMode");

OMX_ERRORTYPE err = OMX_GetExtensionIndex(mHandle, name, &index);

ConfigureVideoTunnelModeParams tunnelParams;
InitOMXParams(&tunnelParams);
tunnelParams.nPortIndex = portIndex;
tunnelParams.bTunneled = tunneled;
tunnelParams.nAudioHwSync = audioHwSync;
err = OMX_SetParameter(mHandle, index, &tunnelParams);
err = OMX_GetParameter(mHandle, index, &tunnelParams);
sidebandHandle = (native_handle_t*)tunnelParams.pSidebandWindow;

如果元件支援這項設定,就應分配側邊頻 處理到這個轉碼器,並透過 pSidebandWindow 成員傳回, HWC 可識別相關聯的轉碼器。如果元件並未 支援這項設定,應將 bTunneled 設為 OMX_FALSE

轉碼器 2

在 Android 11 以上版本中,Codec2 支援通道播放。解碼器 元件應支援下列項目:

  • 設定 C2PortTunneledModeTuning,可設定通道模式並 傳入從音訊輸出裝置所擷取的 HW_AV_SYNC,或 調整調音器設定

  • 查詢 C2_PARAMKEY_OUTPUT_TUNNEL_HANDLE 來分配及擷取 HWC 的側頻控點

  • 在附加 C2Work 時處理 C2_PARAMKEY_TUNNEL_HOLD_RENDER, 會指示轉碼器解碼並表示工作完成,但不表示轉譯成功 輸出緩衝區直到 1) 稍後會指示轉碼器進行轉譯 或 2) 開始播放音訊。

  • 處理 C2_PARAMKEY_TUNNEL_START_RENDER,指示轉碼器 就會立即顯示 C2_PARAMKEY_TUNNEL_HOLD_RENDER (即使尚未開始播放音訊)。

  • 保留 debug.stagefright.ccodec_delayed_params 的設定 (建議選項)。如果 那就設定在 false 中。

Android 開放原始碼計畫實作項目會在 CCodec敬上 透過 C2PortTunnelModeTuning 執行,如以下程式碼片段所示:

if (msg->findInt32("audio-hw-sync", &tunneledPlayback->m.syncId[0])) {
    tunneledPlayback->m.syncType =
            C2PortTunneledModeTuning::Struct::sync_type_t::AUDIO_HW_SYNC;
} else if (msg->findInt32("hw-av-sync-id", &tunneledPlayback->m.syncId[0])) {
    tunneledPlayback->m.syncType =
            C2PortTunneledModeTuning::Struct::sync_type_t::HW_AV_SYNC;
} else {
    tunneledPlayback->m.syncType =
            C2PortTunneledModeTuning::Struct::sync_type_t::REALTIME;
    tunneledPlayback->setFlexCount(0);
}
c2_status_t c2err = comp->config({ tunneledPlayback.get() }, C2_MAY_BLOCK,
        failures);
std::vector<std::unique_ptr<C2Param>> params;
c2err = comp->query({}, {C2PortTunnelHandleTuning::output::PARAM_TYPE},
        C2_DONT_BLOCK, &params);
if (c2err == C2_OK && params.size() == 1u) {
    C2PortTunnelHandleTuning::output *videoTunnelSideband =
            C2PortTunnelHandleTuning::output::From(params[0].get());
    return OK;
}

如果元件支援這項設定,就應分配側邊頻 處理到這個轉碼器,並透過 C2PortTunnelHandlingTuning 傳回, HWC 可識別相關聯的轉碼器。

音訊 HAL

如果是隨選影片播放,音訊 HAL 會收到音訊簡報 內嵌於標頭中的音訊資料時間戳記 在應用程式寫入的每個音訊資料區塊的開頭處加入下列程式碼:

struct TunnelModeSyncHeader {
  // The 32-bit data to identify the sync header (0x55550002)
  int32 syncWord;
  // The size of the audio data following the sync header before the next sync
  // header might be found.
  int32 sizeInBytes;
  // The presentation timestamp of the first audio sample following the sync
  // header.
  int64 presentationTimestamp;
  // The number of bytes to skip after the beginning of the sync header to find the
  // first audio sample (20 bytes for compressed audio, or larger for PCM, aligned
  // to the channel count and sample size).
  int32 offset;
}

如要讓 HWC 轉譯視訊影格與對應的音訊影格同步, 音訊 HAL 應剖析同步處理標頭,並使用顯示時間戳記來 重新同步處理播放時鐘與音訊算繪。如要重新同步處理 正在播放壓縮的音訊,音訊 HAL 可能需要剖析中繼資料 來判斷播放時間。

暫停支援

Android 5 以下版本不支援暫停功能。您可以暫停通道 只能用 A/V 飢餓來播放,但如果影片內部緩衝區較大 (例如 OMX 元件有 1 秒資料),此動作會暫停 看起來沒有回應

在 Android 5.1 以上版本中,AudioFlinger 支援直接暫停和繼續播放功能 (通道) 音訊輸出。如果 HAL 導入了暫停和繼續作業,則追蹤暫停 履歷轉送到 HAL

執行 HAL 呼叫時,系統會遵循暫停、清除、繼續呼叫序列 播放執行緒的記錄 (與卸載相同)。

導入方式建議

音訊 HAL

在 Android 11 中,PCR 或 STC 的 HW 同步 ID 可用於影音同步, 支援純視訊串流。

如果裝置搭載 Android 10 以下版本,支援通道影片播放的裝置應符合以下條件: 至少有一個具有 FLAG_HW_AV_SYNC 的音訊輸出串流設定檔 audio_policy.conf 檔案中的 AUDIO_OUTPUT_FLAG_DIRECT 旗標。這些旗標 用於從音訊時鐘設定系統時鐘。

OMX

裝置製造商應使用獨立的 OMX 元件製作通道影片 播放 (製造商可針對其他類型的 OMX 元件提供額外的 OMX 元件) 例如安全播放)。通道元件 應:

  • 在其輸出內容中指定 0 個緩衝區 (nBufferCountMinnBufferCountActual) 通訊埠。

  • 實作 OMX.google.android.index.prepareForAdaptivePlayback setParameter 擴充功能。

  • media_codecs.xml 檔案中指定其功能,並宣告 循環播放功能此外,也應清楚說明影格限制 大小、對齊或位元率。範例如下所示:

    <MediaCodec name="OMX.OEM_NAME.VIDEO.DECODER.AVC.tunneled"
    type="video/avc" >
        <Feature name="adaptive-playback" />
        <Feature name="tunneled-playback" required=”true” />
        <Limit name="size" min="32x32" max="3840x2160" />
        <Limit name="alignment" value="2x2" />
        <Limit name="bitrate" range="1-20000000" />
            ...
    </MediaCodec>
    

如果使用相同的 OMX 元件支援通道和非通道解碼 讓通道播放功能維持非必要狀態無論是通道或端點 就會有相同的功能限制例如 如下所示:

<MediaCodec name="OMX._OEM\_NAME_.VIDEO.DECODER.AVC" type="video/avc" >
    <Feature name="adaptive-playback" />
    <Feature name="tunneled-playback" />
    <Limit name="size" min="32x32" max="3840x2160" />
    <Limit name="alignment" value="2x2" />
    <Limit name="bitrate" range="1-20000000" />
        ...
</MediaCodec>

硬體 Composer (HWC)

有隧道圖層 (HWC_SIDEBAND compositionType 的圖層) 時 螢幕,圖層的 sidebandStream 是 OMX 影片元件。

HWC 會將已解碼的視訊影格 (從通道 OMX 元件) 同步到 相關的音軌 (含 audio-hw-sync ID)。開啟新影片影格時 而 HWC 會將該模型與所有圖層的目前內容相結合 並顯示產生的圖片。 準備或設定呼叫只會在其他層變更,或發生下列情況時 變更側邊頻寬圖層的屬性 (例如位置或大小) 的變更。

下圖代表與硬體 (或核心或 驅動程式) 同步器,將影片影格 (7b) 與最新的組合結合 (7a) 根據音訊 (7c) 技術,在正確的時間顯示。

HWC 根據音訊結合影片影格

圖 2. HWC 硬體 (或核心或驅動程式) 同步器