Tunneling multimedia

Tunneling multimedia memungkinkan data video terkompresi melakukan tunneling melalui hardware decoder video langsung ke tampilan, tanpa diproses oleh kode aplikasi atau Kode framework Android. Kode khusus perangkat di bawah stack Android menentukan {i>frame<i} video mana yang akan dikirim ke layar dan kapan membandingkan stempel waktu presentasi {i>frame<i} video dengan salah satu jenis jam internal:

  • Untuk pemutaran video on demand di Android 5 atau yang lebih tinggi, AudioTrack jam disinkronkan ke stempel waktu presentasi audio lulus oleh aplikasi

  • Untuk pemutaran live streaming di Android 11 atau yang lebih baru, jam referensi program (PCR) atau jam waktu sistem (STC) yang digerakkan oleh tuner

Latar belakang

Pemutaran video konvensional di Android memberi notifikasi aplikasi ketika {i>frame<i} video terkompresi telah didekode. Aplikasi kemudian rilis frame video yang didekode ke layar untuk dirender pada jam sistem yang sama sebagai {i>frame<i} audio yang sesuai, mengambil histori AudioTimestamps untuk menghitung waktu yang tepat.

Karena pemutaran video yang disalurkan melewati kode aplikasi dan mengurangi jumlah proses yang bekerja pada video, maka dapat memberikan proses rendering video yang lebih efisien bergantung pada implementasi OEM. Hal ini juga dapat memberikan video yang lebih akurat ritme dan sinkronisasi ke jam yang dipilih (PRC, STC, atau audio) dengan menghindari masalah pengaturan waktu yang disebabkan oleh potensi penyimpangan antara pengaturan waktu Android permintaan untuk merender video, dan pengaturan waktu vsyncs perangkat keras yang sebenarnya. Namun, tunneling juga dapat mengurangi dukungan untuk efek GPU seperti memburamkan atau sudut membulat di jendela picture-in-picture (PiP), karena buffer melewati tumpukan grafis Android.

Diagram berikut menunjukkan bagaimana tunneling menyederhanakan proses pemutaran video.

perbandingan mode terowongan dan tradisi

Gambar 1. Perbandingan proses pemutaran video tradisional dan yang disalurkan

Untuk developer aplikasi

Karena sebagian besar developer aplikasi mengintegrasikan dengan library untuk pemutaran implementasi. Dalam kebanyakan kasus, implementasi hanya memerlukan konfigurasi ulang library untuk pemutaran yang disalurkan. Untuk penerapan tingkat rendah video yang disalurkan pemutar, gunakan petunjuk berikut.

Untuk pemutaran video on demand di Android 5 atau yang lebih tinggi:

  1. Buat instance SurfaceView.

  2. Buat instance audioSessionId.

  3. Membuat instance AudioTrack dan MediaCodec dengan audioSessionId di instance yang dibuat di langkah 2.

  4. Antrean data audio ke AudioTrack dengan stempel waktu presentasi untuk {i>frame<i} audio pertama dalam data audio.

Untuk pemutaran live streaming di Android 11 atau yang lebih baru:

  1. Buat instance SurfaceView.

  2. Dapatkan instance avSyncHwId dari Tuner.

  3. Membuat instance AudioTrack dan MediaCodec dengan instance avSyncHwId yang dibuat di langkah 2.

Alur panggilan API ditampilkan dalam cuplikan kode berikut:

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

Perilaku pemutaran video on demand

Karena pemutaran video on-demand yang disalurkan terikat secara implisit ke AudioTrack pemutaran video, perilaku pemutaran video yang disalurkan mungkin tergantung pada perilaku pemutaran audio.

  • Pada sebagian besar perangkat, secara default, frame video tidak dirender hingga audio pemutaran dimulai. Namun, aplikasi mungkin perlu merender frame video sebelum memulai pemutaran audio, misalnya, untuk menampilkan video saat ini kepada pengguna posisi saat mencari.

    • Untuk menunjukkan bahwa frame video dalam antrean pertama harus dirender segera setelah kodenya didekode, PARAMETER_KEY_TUNNEL_PEEK parameter ke 1. Saat frame video yang dikompresi diurutkan ulang dalam antrean (seperti saat B-frame ada), ini berarti bahwa {i>frame<i} video pertama yang ditampilkan harus selalu berupa Bingkai I.

    • Jika Anda tidak ingin frame video dalam antrean pertama dirender hingga audio pemutaran dimulai, tetapkan parameter ini ke 0.

    • Jika parameter ini tidak disetel, OEM akan menentukan perilaku perangkat.

  • Saat data audio tidak disediakan ke AudioTrack dan buffer kosong (underrun audio), pemutaran video akan terhenti hingga lebih banyak data audio ditulis karena jam audio tidak lagi maju.

  • Selama pemutaran, diskon yang tidak dapat diperbaiki oleh aplikasi mungkin muncul di stempel waktu presentasi audio. Jika hal ini terjadi, OEM akan mengoreksi hasil negatif kesenjangan dengan menghentikan frame video saat ini, dan kesenjangan positif karena penurunan frame video atau menyisipkan frame audio diam (bergantung pada OEM penerapan). Posisi frame AudioTimestamp tidak meningkat untuk {i>frame<i} audio senyap yang disisipkan.

Untuk produsen perangkat

Konfigurasi

OEM harus membuat dekoder video terpisah untuk mendukung pemutaran video yang disalurkan. Decoder ini harus menyatakan bahwa ia mampu melakukan pemutaran terowongan di File media_codecs.xml:

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

Saat instance MediaCodec yang disalurkan dikonfigurasi dengan ID sesi audio, instance tersebut mengkueri AudioFlinger untuk ID HW_AV_SYNC ini:

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

Selama kueri ini, AudioFlinger mengambil ID HW_AV_SYNC dari perangkat audio utama dan mengaitkannya secara internal dengan audio ID sesi:

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

Jika instance AudioTrack sudah dibuat, ID HW_AV_SYNC adalah diteruskan ke streaming output dengan ID sesi audio yang sama. Jika belum dibuat, maka ID HW_AV_SYNC diteruskan ke aliran output selama Pembuatan AudioTrack. Hal ini dilakukan oleh pemutaran rangkaian pesan:

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

ID HW_AV_SYNC, apakah sesuai dengan streaming output audio atau konfigurasi Tuner, diteruskan ke komponen OMX atau Codec2 sehingga Kode OEM dapat mengaitkan codec dengan streaming output audio yang sesuai, atau aliran tuner.

Selama konfigurasi komponen, komponen OMX atau Codec2 harus mengembalikan tuas sideband yang dapat digunakan untuk mengaitkan codec dengan Hardware Composer (HWC). Saat aplikasi mengaitkan platform dengan MediaCodec, sideband ini diteruskan ke HWC melalui SurfaceFlinger, yang mengonfigurasi sebagai lapisan 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 bertanggung jawab untuk menerima{i> buffer <i}gambar baru dari {i>output <i}coder waktu yang tepat, baik yang disinkronkan ke streaming output audio terkait atau jam referensi program tuner, yang menyusun {i>buffer<i} dengan arus lapisan lain, dan menampilkan gambar yang dihasilkan. Hal ini sering terjadi terlepas dari siklus persiapan dan set normal. Panggilan persiapan dan penetapan hanya terjadi ketika lapisan lain berubah, atau ketika properti lapisan sideband (seperti posisi atau ukuran).

OMX

Komponen decoder yang disalurkan harus mendukung hal berikut:

  • Menyetel OMX.google.android.index.configureVideoTunnelMode diperluas , yang menggunakan struktur ConfigureVideoTunnelModeParams untuk meneruskan di ID HW_AV_SYNC yang terkait dengan perangkat output audio.

  • Mengonfigurasi parameter OMX_IndexConfigAndroidTunnelPeek yang memberi tahu codec untuk merender atau tidak merender frame video pertama yang didekode, terlepas dari apakah pemutaran audio telah dimulai.

  • Mengirim peristiwa OMX_EventOnFirstTunnelFrameReady saat terowongan pertama kali dilakukan frame video telah didekode dan siap dirender.

Implementasi AOSP mengonfigurasi mode tunnel di ACodec sampai OMXNodeInstance seperti yang ditunjukkan dalam cuplikan kode berikut:

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;

Jika komponen mendukung konfigurasi ini, komponen harus mengalokasikan {i>sideband<i} menangani codec ini dan meneruskannya kembali melalui anggota pSidebandWindow sehingga sehingga HWC dapat mengidentifikasi {i> codec<i} yang terkait. Jika komponen tidak mendukung konfigurasi ini, maka harus menyetel bTunneled ke OMX_FALSE.

Codec2

Di Android 11 atau yang lebih tinggi, Codec2 mendukung pemutaran yang disalurkan. Decoder harus mendukung hal berikut:

  • Mengonfigurasi C2PortTunneledModeTuning, yang mengonfigurasi mode tunnel dan yang diteruskan di HW_AV_SYNC yang diambil dari perangkat output audio atau konfigurasi tuner.

  • Membuat kueri C2_PARAMKEY_OUTPUT_TUNNEL_HANDLE, untuk mengalokasikan dan mengambil {i>sideband<i} untuk HWC.

  • Menangani C2_PARAMKEY_TUNNEL_HOLD_RENDER saat dilampirkan ke C2Work, yang menginstruksikan codec untuk mendekode dan memberi sinyal penyelesaian pekerjaan, tetapi tidak untuk merender buffer output hingga 1) codec kemudian diinstruksikan untuk merendernya atau 2) pemutaran audio dimulai.

  • Menangani C2_PARAMKEY_TUNNEL_START_RENDER, yang menginstruksikan codec untuk segera {i>render<i} {i>frame<i} yang ditandai dengan C2_PARAMKEY_TUNNEL_HOLD_RENDER, meskipun pemutaran audio belum dimulai.

  • Biarkan debug.stagefright.ccodec_delayed_params tidak dikonfigurasi (direkomendasikan). Jika Anda mengonfigurasinya, setel ke false.

Implementasi AOSP mengonfigurasi mode tunnel di CCodec hingga C2PortTunnelModeTuning, seperti yang ditunjukkan dalam cuplikan kode berikut:

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

Jika komponen mendukung konfigurasi ini, komponen harus mengalokasikan {i>sideband<i} menangani codec ini dan meneruskannya kembali melalui C2PortTunnelHandlingTuning sehingga sehingga HWC dapat mengidentifikasi {i> codec<i} yang terkait.

HAL Audio

Untuk pemutaran video on demand, Audio HAL menerima presentasi audio stempel waktu yang sesuai dengan data audio dalam format big-endian di dalam header yang ditemukan di awal setiap blok data audio yang ditulis aplikasi:

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

Agar HWC dapat merender {i>frame<i} video yang sinkron dengan {i>frame<i} audio yang sesuai, Audio HAL harus mengurai header sinkronisasi dan menggunakan stempel waktu presentasi untuk menyinkronkan ulang jam pemutaran dengan rendering audio. Untuk menyinkronkan ulang saat audio terkompresi sedang diputar, Audio HAL mungkin perlu mengurai metadata di dalam data audio terkompresi untuk menentukan durasi pemutarannya.

Jeda dukungan

Android 5 atau yang lebih rendah tidak menyertakan dukungan jeda. Anda dapat menjeda tunnel pemutaran hanya jika terjadi kekurangan A/V, tetapi jika buffer internal untuk video besar (misalnya, ada satu detik data dalam komponen OMX), hal ini membuat jeda terlihat tidak responsif.

Di Android 5.1 atau yang lebih tinggi, AudioFlinger mendukung jeda dan lanjutkan untuk langsung Output audio (tunneled). Jika HAL menerapkan jeda dan lanjutkan, lacak jeda dan resume diteruskan ke HAL.

Urutan jeda, flush, lanjutkan panggilan dipatuhi dengan mengeksekusi panggilan HAL di thread pemutaran (sama seperti pengurangan beban).

Saran penerapan

HAL Audio

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

Untuk Android 10 atau yang lebih rendah, perangkat yang mendukung pemutaran video yang disalurkan harus memiliki setidaknya satu profil streaming output audio dengan FLAG_HW_AV_SYNC dan AUDIO_OUTPUT_FLAG_DIRECT dalam file audio_policy.conf-nya. Tanda ini digunakan untuk menyetel jam sistem dari jam audio.

OMX

Produsen perangkat harus memiliki komponen OMX terpisah untuk video yang disalurkan pemutaran (produsen dapat memiliki komponen OMX tambahan untuk jenis pemutaran audio dan video, seperti pemutaran yang aman). Komponen yang disalurkan harus:

  • Menentukan 0 buffer (nBufferCountMin, nBufferCountActual) pada outputnya porta.

  • Implementasikan ekstensi OMX.google.android.index.prepareForAdaptivePlayback setParameter.

  • Tentukan kemampuannya dalam file media_codecs.xml dan deklarasikan atribut fitur pemutaran yang di-salurkan. Ini juga harus memperjelas batasan apa pun pada {i>frame<i} ukuran, perataan, atau kecepatan bit. Contohnya ditampilkan di bawah ini:

    <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 terowongan dan nontunnel, fitur pemutaran yang disalurkan seharusnya tidak diperlukan. Disalurkan dan decoder {i>nontunneled<i} kemudian memiliki batasan kemampuan yang sama. Contohnya adalah yang ditampilkan di bawah ini:

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

Hardware Composer (HWC)

Ketika ada lapisan terowongan (lapisan dengan HWC_SIDEBAND compositionType) di tampilan, sidebandStream lapisan adalah tuas sideband yang dialokasikan oleh Komponen video OMX.

HWC menyinkronkan frame video yang didekode (dari komponen OMX yang disalurkan) ke trek audio yang terkait (dengan ID audio-hw-sync). Saat frame video baru menjadi terkini, HWC mengomposisikannya dengan isi saat ini dari semua lapisan yang diterima selama panggilan persiapan atau setel terakhir, dan menampilkan gambar yang dihasilkan. Panggilan persiapan atau penetapan hanya terjadi jika lapisan lain berubah, atau ketika properti lapisan sideband (seperti posisi atau ukuran) yang berubah.

Gambar berikut menunjukkan HWC yang bekerja dengan perangkat keras (atau {i>kernel<i} atau driver), untuk menggabungkan frame video (7b) dengan komposisi terbaru (7a) untuk ditampilkan pada waktu yang tepat, berdasarkan audio (7c).

HWC yang menggabungkan frame video berdasarkan audio

Gambar 2. Sinkronisasi hardware (atau kernel atau driver) HWC