Google is committed to advancing racial equity for Black communities. See how.
本頁面由 Cloud Translation API 翻譯而成。
Switch to English

多媒體隧道

您可以在Android Framework 5.0及更高版本中實現多媒體隧道。儘管An​​droid TV不需要多媒體隧道,但它為超高清(4K)內容提供了最佳體驗。

對於Android 11或更高版本,您可以使用直接從Tuner饋送的音頻和視頻內容來實現多媒體隧道。 Codec2AudioTrack可以使用來自Tuner的硬件同步ID,該ID可能對應於程序時鐘參考(PCR)或系統時間時鐘(STC)通道。

背景

Android媒體框架以四種方式處理音頻/視頻內容:

  • 純軟件(本地解碼):應用處理器(AP)在不進行特殊加速的情況下將音頻本地解碼為脈衝編碼調製(PCM)。在沒有壓縮卸載支持的情況下,始終用於Ogg Vorbis,並用於MP3和AAC。
  • 壓縮音頻卸載將壓縮音頻數據直接發送到數字信號處理器(DSP),並儘可能使AP保持關閉狀態。用於在屏幕關閉的情況下播放音樂文件。
  • 壓縮音頻直通直接通過HDMI將壓縮音頻(特別是AC3和E-AC3)直接發送到外部電視或音頻接收器,而無需在Android TV設備上對其進行解碼。視頻部分是單獨處理的。
  • 多媒體隧道將壓縮的音頻和視頻數據一起發送。當視頻和音頻解碼器接收到已編碼的流時,它不會返回框架。理想情況下,流不會中斷AP。
  • 多媒體直通將壓縮的音頻和視頻數據從Tuner一起發送到視頻和音頻解碼器,而無需使用該框架。
多媒體隧道流程圖
圖1.多媒體隧道流

方法比較

純軟件壓縮音頻卸載壓縮音頻直通多媒體隧道多媒體直通
解碼位置美聯社數字信號處理器電視或音頻/視頻接收器(AVR)電視或AVR電視或AVR
處理音頻沒有
處理視頻沒有沒有沒有

對於應用程序開發人員

創建一個SurfaceView實例,獲取音頻會話ID,然後創建AudioTrackMediaCodec實例,以提供播放和視頻幀解碼所需的時序和配置。

對於Android 11或更高版本,作為音頻會話ID的替代,應用程序可以從Tuner獲取硬件同步ID,並將其提供給AudioTrackMediaCodec實例進行A / V同步。

音視頻同步

在多媒體隧道模式下,音頻和視頻在主時鐘上同步。

  • 對於Android 11或更高版本,來自Tuner的PCR或STC可能是A / V同步的主時鐘。
  • 對於Android 10或更低版本,音頻時鐘是用於A / V播放的主時鐘。

如果隧道視頻MediaCodec實例和AudioTrack實例鏈接到HW_AV_SYNC在實例AudioTrack ,衍生自所述隱式時鐘HW_AV_SYNC限制時每個視頻幀和音頻樣本被呈現,根據實際的音頻或視頻幀顯現時間標記(PTS)。

API調用流程

對於Android 11或更高版本,客戶端可以使用Tuner中的HW同步ID。

  1. 創建一個SurfaceView實例。
    SurfaceView sv = new SurfaceView(mContext);
  2. 獲取音頻會話ID。此唯一ID用於創建音軌( AudioTrack )。它被傳遞給媒體編解碼器( MediaCodec ),並由媒體框架用來鏈接音頻和視頻路徑。
    AudioManager am = mContext.getSystemService(AUDIO_SERVICE);
    int audioSessionId = am.generateAudioSessionId()
    // or, for Android 11 or higher
    int avSyncId = tuner.getAvSyncHwId();
  3. 創建AudioTrack與HW A / V同步AudioAttributes

    音頻策略管理器向硬件抽象層(HAL)詢問支持FLAG_HW_AV_SYNC的設備輸出,並創建一個音頻軌道,該音頻軌道直接連接到此輸出,而沒有中間混音器。

    AudioAttributes.Builder aab = new AudioAttributes.Builder();
    aab.setUsage(AudioAttributes.USAGE_MEDIA);
    aab.setContentType(AudioAttributes.CONTENT_TYPE_MOVIE);
    aab.setFlag(AudioAttributes.FLAG_HW_AV_SYNC);
    
    // or, for Android 11 or higher
    new tunerConfig = TunerConfiguration(0, avSyncId);
    aab.setTunerConfiguration(tunerConfig);
    
    AudioAttributes aa = aab.build();
    AudioTrack at = new AudioTrack(aa);
    
  4. 創建一個視頻MediaCodec實例,並將其配置為通過隧道播放視頻。
    // retrieve codec with tunneled video playback feature
    MediaFormat mf = MediaFormat.createVideoFormat(“video/hevc”, 3840, 2160);
    mf.setFeatureEnabled(CodecCapabilities.FEATURE_TunneledPlayback, true);
    MediaCodecList mcl = new MediaCodecList(MediaCodecList.ALL_CODECS);
    String codecName = mcl.findDecoderForFormat(mf);
    if (codecName == null) {
      return FAILURE;
    }
    // create codec and configure it
    mf.setInteger(MediaFormat.KEY_AUDIO_SESSION_ID, audioSessionId);
    
    // or, for Android 11 or higher
    mf.setInteger(MediaFormat.KEY_HARDWARE_AV_SYNC_ID, avSyncId);
    
    MediaCodec mc = MediaCodec.createCodecByName(codecName);
    mc.configure(mf, sv.getSurfaceHolder().getSurface(), null, 0);
    
  5. 解碼視頻幀。
    mc.start();
     for (;;) {
       int ibi = mc.dequeueInputBuffer(timeoutUs);
       if (ibi >= 0) {
         ByteBuffer ib = mc.getInputBuffer(ibi);
         // fill input buffer (ib) with valid data
         ...
         mc.queueInputBuffer(ibi, ...);
       }
       // no need to dequeue explicitly output buffers. The codec
       // does this directly to the sideband layer.
     }
     mc.stop();
     mc.release();
     mc = null;

注意:您可以在此過程中切換步驟3和4的順序,如下圖所示。

編解碼器配置之前創建的音軌圖
圖2.編解碼器配置之前創建的音軌
編解碼器配置後創建的音軌圖
圖3.編解碼器配置後創建的音軌

對於設備製造商

OEM應該創建一個單獨的視頻解碼器OpenMAX IL(OMX)組件以支持隧道視頻播放。此OMX組件必須公告其具有隧道播放功能(在media_codecs.xml )。

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

該組件還必須支持使用ConfigureVideoTunnelModeParams結構的OMX擴展參數OMX.google.android.index.configureVideoTunnelMode

struct ConfigureVideoTunnelModeParams {
    OMX_U32 nSize;              // IN
    OMX_VERSIONTYPE nVersion;   // IN
    OMX_U32 nPortIndex;         // IN
    OMX_BOOL bTunneled;         // IN/OUT
    OMX_U32 nAudioHwSync;       // IN
    OMX_PTR pSidebandWindow;    // OUT
};

發出隧道式MediaCodec創建請求時,框架將以隧道模式配置OMX組件(通過將bTunneled設置為OMX_TRUE ),並將使用AUDIO_HW_AV_SYNC標誌創建的關聯音頻輸出設備傳遞給OMX組件(在nAudioHwSync )。

如果組件支持此配置,則應為該編解碼器分配邊帶句柄,並將其通過pSidebandWindow成員傳遞回去。邊帶句柄是隧道層的ID標籤,可讓Hardware Composer(HW Composer)對其進行標識。如果組件不支持此配置,則應將bTunneled設置為OMX_FALSE

框架檢索由OMX組件分配的隧道層(邊帶句柄),並將其傳遞給HW Composer。該層的compositionType設置為HWC_SIDEBAND 。 (請參閱hardware/libhardware/include/hardware/hwcomposer.h 。)

HW Composer負責在適當的時間從流中接收新的圖像緩衝區(例如,同步到關聯的音頻輸出設備),將它們與其他層的當前內容進行組合,並顯示結果圖像。這與正常的準備/固化週期無關。僅當其他層更改或邊帶層的屬性(例如位置或大小)更改時,才進行prepare / set調用。

組態

框架/ av /服務/audioflinger/AudioFlinger.cpp

HAL將HW_AV_SYNC ID返回為64位整數的字符串十進製表示形式。 (請參閱frameworks/av/services/audioflinger/AudioFlinger.cpp 。)

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);

框架/ av /服務/audioflinger/Threads.cpp

音頻框架必須找到此會話ID對應的HAL輸出流,並使用set_parameters在HAL中查詢hwAVSyncId

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

OMX解碼器配置

MediaCodec.java

音頻框架為此會話ID找到相應的HAL輸出流,並通過使用get_parameters向HAL查詢AUDIO_PARAMETER_STREAM_HW_AV_SYNC標誌來檢索audio-hw-sync ID。

// Retrieve HW AV sync audio output device from Audio Service
// in MediaCodec.configure()
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);
}

// ...

使用自定義參數OMX.google.android.index.configureVideoTunnelMode將此HW同步ID傳遞到OMX隧道視頻解碼器。

ACodec.cpp

在獲得音頻硬件同步ID之後,ACodec使用它來配置隧道視頻解碼器,以便隧道視頻解碼器知道要同步的音頻軌道。

// Assume you're going to use tunneled video rendering.
// Configure OMX component in tunneled mode and grab sideband handle (sidebandHandle) from OMX
// component.

native_handle_t* sidebandHandle;

// Configure OMX component in tunneled mode
status_t err = mOMX->configureVideoTunnelMode(mNode, kPortIndexOutput,
        OMX_TRUE, audioHwSync, &sidebandHandle);

OMXNodeInstance.cpp

OMX組件是通過上面的configureVideoTunnelMode方法configureVideoTunnelMode

// paraphrased

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;

ACodec.cpp

在以隧道模式配置OMX組件之後,邊帶手柄與渲染表面相關聯。

  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; }

然後,最大分辨率提示(如果存在)將發送到組件。

// Configure max adaptive playback resolution - as for any other video decoder
int32_t maxWidth = 0, maxHeight = 0;
if (msg->findInt32("max-width", &maxWidth) &&
    msg->findInt32("max-height", &maxHeight)) {
    err = mOMX->prepareForAdaptivePlayback(
              mNode, kPortIndexOutput, OMX_TRUE, maxWidth, maxHeight);
}

暫停支援

Android 5.0及更低版本不包含暫停支持。您只能通過A / V飢餓來暫停隧道播放,但是如果視頻的內部緩衝區很大(例如,OMX組件中有1秒的數據),它會使暫停顯得無響應。

在Android 5.1及更高版本中,AudioFlinger支持暫停和恢復直接(隧道式)音頻輸出。如果HAL實現暫停/恢復,則軌道暫停/恢復將轉發到HAL。

通過在回放線程中執行HAL調用(與卸載相同),可以遵守暫停,刷新,恢復調用順序。

編解碼器2支持

對於Android 11或更高版本,Codec2支持隧道播放。

CCodec.cpp

為了支持隧道播放,Codec2的工作方式與OMX類似。為了支持Tuner的HW同步ID,Codec2從下面的源中尋找同步ID。

sp<ANativeWindow> nativeWindow = static_cast<ANativeWindow *>(surface.get());
int32_t audioHwSync = 0;
if (!msg->findInt32("hw-av-sync-id", &audioHwSync)) {
       if (!msg->findInt32("audio-hw-sync", &audioHwSync)) {
       }
}
err = configureTunneledVideoPlayback(comp, audioHwSync, nativeWindow);

實施建議

音訊HAL

對於Android 11,可以將來自PCR或STC的硬件同步ID用於A / V同步,因此支持僅視頻流。

對於Android 10或更低版本,支持隧道視頻播放的設備應在其audio_policy.conf文件中至少具有一個帶有標誌FLAG_HW_AV_SYNCAUDIO_OUTPUT_FLAG_DIRECT音頻輸出流配置文件。這些標誌用於從音頻時鐘設置系統時鐘。

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>

硬件作曲家

當顯示器上有隧道層(具有HWC_SIDEBAND compositionType的層)時,該層的sidebandStream是由OMX視頻組件分配的邊帶句柄。

HW Composer會將解碼的視頻幀(來自隧道OMX組件)同步到關聯的音軌(帶有audio-hw-sync ID)。當新的視頻幀變為最新視頻時,HW Composer會將其與上一次準備/設置調用期間接收到的所有圖層的當前內容進行合成,並顯示結果圖像。僅當其他層發生更改或邊帶層的屬性(例如位置或大小)發生更改時,才會進行prepare / set調用。

圖4表示HW Composer與HW(或內核/驅動程序)同步器一起工作,將視頻幀(7b)與最新的合成(7a)組合在一起,以便基於音頻(7c)在正確的時間顯示。

基於音頻組合視頻幀的硬件組合器圖
圖4.使用HW(或內核/驅動程序)同步器的HW Composer