Terowongan Multimedia

Anda dapat mengimplementasikan tunneling multimedia di framework Android 5.0 dan yang lebih tinggi. Meskipun penerowongan multimedia tidak diperlukan untuk Android TV, ini memberikan pengalaman terbaik untuk konten definisi sangat tinggi (4K).

Untuk Android 11 atau lebih tinggi, Anda dapat menerapkan tunneling multimedia dengan konten audio dan video yang diumpankan langsung dari Tuner. Codec2 dan AudioTrack dapat menggunakan ID sinkronisasi HW dari Tuner, yang mungkin sesuai dengan saluran referensi jam program (PCR) atau jam waktu sistem (STC).

Latar belakang

Kerangka kerja media Android menangani konten audio/video dalam empat cara:

  • Perangkat lunak murni (decoding lokal): Prosesor aplikasi (AP) secara lokal menerjemahkan audio ke modulasi kode-pulsa (PCM) tanpa akselerasi khusus. Selalu digunakan untuk Ogg Vorbis, dan digunakan untuk MP3 dan AAC ketika tidak ada dukungan offload terkompresi.
  • Offload audio terkompresi mengirimkan data audio terkompresi langsung ke prosesor sinyal digital (DSP) dan membuat AP mati sebanyak mungkin. Gunakan untuk memutar file musik dengan layar mati.
  • Passthrough audio terkompresi mengirimkan audio terkompresi (khususnya AC3 dan E-AC3) langsung melalui HDMI ke TV eksternal atau penerima audio, tanpa mendekodekannya di perangkat Android TV. Bagian video ditangani secara terpisah.
  • Penerowongan multimedia mengirimkan data audio dan video terkompresi secara bersamaan. Ketika aliran yang disandikan diterima oleh dekoder video dan audio, itu tidak kembali ke kerangka kerja. Idealnya, aliran tidak mengganggu AP.
  • Passthrough multimedia mengirimkan data audio dan video terkompresi bersama-sama dari Tuner ke dekoder video dan audio tanpa melibatkan kerangka kerja.
Diagram Alir Tunneling Multimedia
Gambar 1. Aliran tunneling multimedia

Perbandingan pendekatan

Perangkat lunak murni Pembongkaran audio terkompresi Passthrough audio terkompresi Terowongan multimedia Lintasan multimedia
Dekode lokasi AP DSP TV atau penerima audio/video (AVR) TV atau AVR TV atau AVR
Menangani audio Ya Ya Ya Ya Tidak
Menangani video Ya Tidak Tidak Ya Tidak

Untuk pengembang aplikasi

Buat instance SurfaceView , dapatkan ID sesi audio, lalu buat AudioTrack dan MediaCodec untuk menyediakan pengaturan waktu dan konfigurasi yang diperlukan untuk pemutaran dan decoding bingkai video.

Untuk Android 11 atau lebih tinggi, sebagai alternatif untuk ID sesi audio, aplikasi bisa mendapatkan ID sinkronisasi HW dari Tuner dan memberikannya ke AudioTrack dan MediaCodec untuk sinkronisasi A/V.

Sinkronisasi A/V

Dalam mode tunneling multimedia, audio dan video disinkronkan pada jam master.

  • Untuk Android 11 atau lebih tinggi, PCR atau STC dari Tuner mungkin merupakan jam utama untuk sinkronisasi A/V.
  • Untuk Android 10 atau lebih rendah, jam audio adalah jam utama yang digunakan untuk pemutaran A/V.

Jika instans MediaCodec video MediaCodec dan AudioTrack ditautkan ke HW_AV_SYNC di AudioTrack , jam implisit yang diturunkan dari HW_AV_SYNC membatasi kapan setiap bingkai video dan sampel audio disajikan, berdasarkan stempel waktu presentasi bingkai audio atau video (PTS) yang sebenarnya.

Alur panggilan API

Untuk Android 11 atau lebih tinggi, klien dapat menggunakan ID sinkronisasi HW dari Tuner.

  1. Buat instance SurfaceView .
    SurfaceView sv = new SurfaceView(mContext);
  2. Dapatkan ID sesi audio. ID unik ini digunakan dalam membuat trek audio ( AudioTrack ). Ini diteruskan ke codec media ( MediaCodec ) dan digunakan oleh kerangka kerja media untuk menautkan jalur audio dan video.
    AudioManager am = mContext.getSystemService(AUDIO_SERVICE);
    int audioSessionId = am.generateAudioSessionId()
    // or, for Android 11 or higher
    int avSyncId = tuner.getAvSyncHwId();
  3. Buat AudioTrack dengan AudioAttributes sinkronisasi A/V HW.

    Manajer kebijakan audio meminta lapisan abstraksi perangkat keras (HAL) untuk output perangkat yang mendukung FLAG_HW_AV_SYNC , dan membuat trek audio yang terhubung langsung ke output ini tanpa mixer perantara.

    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. Buat instance MediaCodec video dan konfigurasikan untuk pemutaran video tunneled.
    // 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. Decode bingkai video.
    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;

Catatan: Anda dapat mengganti urutan langkah 3 dan 4 dalam proses ini, seperti yang terlihat pada dua gambar di bawah ini.

Diagram trek audio yang dibuat sebelum konfigurasi codec
Gambar 2. Trek audio yang dibuat sebelum konfigurasi codec
Diagram trek audio yang dibuat setelah konfigurasi codec
Gambar 3. Trek audio dibuat setelah konfigurasi codec

Untuk produsen perangkat

OEM harus membuat komponen OpenMAX IL (OMX) dekoder video terpisah untuk mendukung pemutaran video terowongan. Komponen OMX ini harus mengiklankan bahwa ia mampu melakukan pemutaran terowongan (di media_codecs.xml ).

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

Komponen juga harus mendukung parameter perluasan OMX OMX.google.android.index.configureVideoTunnelMode yang menggunakan struktur ConfigureVideoTunnelModeParams .

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

Saat permintaan pembuatan MediaCodec dibuat, framework mengonfigurasi komponen OMX dalam mode tunneled (dengan menyetel bTunneled ke OMX_TRUE ) dan meneruskan perangkat output audio terkait yang dibuat dengan flag AUDIO_HW_AV_SYNC ke komponen OMX (dalam nAudioHwSync ).

Jika komponen mendukung konfigurasi ini, komponen tersebut harus mengalokasikan pegangan sideband ke codec ini dan meneruskannya kembali melalui anggota pSidebandWindow . Pegangan sideband adalah tag ID untuk lapisan terowongan yang memungkinkan Komposer Perangkat Keras (HW Composer) mengidentifikasinya. Jika komponen tidak mendukung konfigurasi ini, komponen tersebut harus disetel bTunneled ke OMX_FALSE .

Framework mengambil layer tunneled (pegangan sideband) yang dialokasikan oleh komponen OMX dan meneruskannya ke HW Composer. Jenis compositionType lapisan ini disetel ke HWC_SIDEBAND . (Lihat hardware/libhardware/include/hardware/hwcomposer.h .)

Komposer HW bertanggung jawab untuk menerima buffer gambar baru dari aliran pada waktu yang tepat (misalnya, disinkronkan ke perangkat output audio terkait), menggabungkannya dengan konten saat ini dari lapisan lain, dan menampilkan gambar yang dihasilkan. Ini terjadi terlepas dari siklus persiapan/pengaturan normal. Panggilan persiapan/pengaturan hanya terjadi ketika lapisan lain berubah, atau ketika properti lapisan pita samping (seperti posisi atau ukuran) berubah.

Konfigurasi

frameworks/av/services/audioFlinger/AudioFlinger.cpp

HAL mengembalikan ID HW_AV_SYNC sebagai representasi desimal string karakter dari integer 64-bit. (Lihat 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);

frameworks/av/services/audioflinger/Threads.cpp

Kerangka kerja audio harus menemukan aliran keluaran HAL yang sesuai dengan ID sesi ini, dan menanyakan HAL untuk hwAVSyncId menggunakan set_parameters .

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

Konfigurasi dekoder OMX

MediaCodec.java

Kerangka audio menemukan aliran keluaran HAL yang sesuai untuk ID sesi ini dan mengambil ID audio-hw-sync dengan menanyakan HAL untuk tanda AUDIO_PARAMETER_STREAM_HW_AV_SYNC menggunakan get_parameters .

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

// ...

ID sinkronisasi HW ini diteruskan ke dekoder video tunneled OMX menggunakan parameter khusus OMX.google.android.index.configureVideoTunnelMode .

ACodec.cpp

Setelah Anda mendapatkan ID sinkronisasi perangkat keras audio, ACodec menggunakannya untuk mengonfigurasi dekoder video tunneled sehingga dekoder video tunneled mengetahui trek audio mana yang akan disinkronkan.

// 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

Komponen OMX dikonfigurasi dengan metode configureVideoTunnelMode di atas.

// 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

Setelah komponen OMX dikonfigurasi dalam mode tunneled, pegangan sideband dikaitkan dengan permukaan rendering.

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

Kemudian petunjuk resolusi maksimal, jika ada, dikirim ke komponen.

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

Jeda dukungan

Android 5.0 dan yang lebih rendah tidak menyertakan dukungan jeda. Anda dapat menjeda pemutaran terowongan hanya dengan kelaparan A/V, tetapi jika buffer internal untuk video besar (misalnya, ada 1 detik data dalam komponen OMX), itu membuat jeda terlihat tidak responsif.

Di Android 5.1 dan yang lebih tinggi, AudioFlinger mendukung jeda dan lanjutkan untuk output audio langsung (tunnel). Jika HAL mengimplementasikan pause/resume, track pause/resume diteruskan ke HAL.

Jeda, flush, lanjutkan urutan panggilan dihormati dengan mengeksekusi panggilan HAL di utas pemutaran (sama seperti offload).

Dukungan Codec2

Untuk Android 11 atau lebih tinggi, Codec2 mendukung pemutaran terowongan.

CCodec.cpp

Untuk mendukung pemutaran terowongan, Codec2 bekerja mirip dengan OMX. Untuk mendukung ID sinkronisasi HW dari Tuner, Codec2 mencari ID sinkronisasi dari sumber di bawah.

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

Saran implementasi

Audio HAL

Untuk Android 11, ID sinkronisasi HW dari PCR atau STC dapat digunakan untuk sinkronisasi A/V, jadi streaming video saja didukung.

Untuk Android 10 atau lebih rendah, perangkat yang mendukung pemutaran video terowongan harus memiliki setidaknya satu profil aliran keluaran audio dengan flag FLAG_HW_AV_SYNC dan AUDIO_OUTPUT_FLAG_DIRECT dalam file audio_policy.conf -nya. Bendera ini digunakan untuk mengatur jam sistem dari jam audio.

OMX

Produsen perangkat harus memiliki komponen OMX terpisah untuk pemutaran video terowongan. Produsen dapat memiliki komponen OMX tambahan untuk jenis pemutaran audio dan video lainnya, seperti pemutaran aman.

Komponen ini harus menentukan 0 buffer ( nBufferCountMin , nBufferCountActual ) pada port outputnya.

Komponen tunneled juga harus mengimplementasikan ekstensi OMX.google.android.index.prepareForAdaptivePlayback setParameter .

Komponen tunneled harus menentukan kemampuannya dalam file media_codecs.xml dan mendeklarasikan fitur pemutaran tunneled. Ini juga harus memperjelas batasan apa pun pada ukuran bingkai, perataan, atau bitrate.


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

Jika komponen OMX yang sama digunakan untuk mendukung decoding tunneled dan non-tunneled, maka fitur pemutaran tunneled tidak diperlukan. Baik decoder tunneled dan non-tunneled kemudian memiliki keterbatasan kemampuan yang sama.

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

Komposer HW

Saat ada layer tunneled (layer dengan HWC_SIDEBAND compositionType ) pada tampilan, sidebandStream layer adalah pegangan sideband yang dialokasikan oleh komponen video OMX.

Komposer HW menyinkronkan bingkai video yang didekodekan (dari komponen OMX tunneled) ke trek audio terkait (dengan ID audio-hw-sync ). Ketika bingkai video baru menjadi terkini, Komposer HW menggabungkannya dengan konten saat ini dari semua lapisan yang diterima selama panggilan persiapan/pengaturan terakhir, dan menampilkan gambar yang dihasilkan. Panggilan persiapan/pengaturan hanya terjadi ketika lapisan lain berubah, atau ketika properti lapisan pita samping (seperti posisi atau ukuran) berubah.

Gambar 4 menunjukkan Komposer HW yang bekerja dengan sinkronisasi HW (atau kernel/driver), untuk menggabungkan bingkai video (7b) dengan komposisi terbaru (7a) untuk ditampilkan pada waktu yang tepat, berdasarkan audio (7c).

Diagram penyusun perangkat keras yang menggabungkan bingkai video berdasarkan audio
Gambar 4. Komposer HW bekerja dengan sinkronisasi HW (atau kernel/driver)