Penerowongan multimedia

Penerowongan multimedia memungkinkan data video terkompresi untuk disalurkan melalui dekoder video perangkat keras langsung ke layar, tanpa diproses oleh kode aplikasi atau kode kerangka kerja Android. Kode khusus perangkat di bawah tumpukan Android menentukan bingkai video mana yang akan dikirim ke layar dan waktu pengirimannya dengan membandingkan stempel waktu presentasi bingkai video dengan salah satu jenis jam internal berikut:

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

  • Untuk pemutaran siaran langsung di Android 11 atau lebih tinggi, jam referensi program (PCR) atau jam waktu sistem (STC) yang digerakkan oleh tuner

Latar belakang

Pemutaran video tradisional di Android memberi tahu aplikasi ketika bingkai video terkompresi telah didekodekan. Aplikasi kemudian melepaskan bingkai video yang didekodekan ke tampilan untuk dirender pada waktu jam sistem yang sama dengan bingkai audio yang sesuai, mengambil contoh AudioTimestamps historis untuk menghitung waktu yang tepat.

Karena pemutaran video terowongan melewati kode aplikasi dan mengurangi jumlah proses yang bekerja pada video, pemutaran video terowongan dapat memberikan rendering video yang lebih efisien bergantung pada penerapan OEM. Ini juga dapat memberikan irama dan sinkronisasi video yang lebih akurat ke jam yang dipilih (PRC, STC, atau audio) dengan menghindari masalah pengaturan waktu yang disebabkan oleh potensi ketidaksesuaian antara waktu permintaan Android untuk merender video, dan waktu vsync perangkat keras yang sebenarnya. Namun, penerowongan juga dapat mengurangi dukungan untuk efek GPU seperti sudut buram atau membulat di jendela gambar-dalam-gambar (PiP), karena buffer melewati tumpukan grafis Android.

Diagram berikut menunjukkan bagaimana tunneling menyederhanakan proses pemutaran video.

perbandingan mode tradisi dan terowongan

Gambar 1. Perbandingan proses pemutaran video tradisional dan terowongan

Untuk pengembang aplikasi

Karena sebagian besar pengembang aplikasi berintegrasi dengan perpustakaan untuk implementasi pemutaran, dalam banyak kasus implementasi hanya memerlukan konfigurasi ulang perpustakaan tersebut untuk pemutaran terowongan. Untuk penerapan pemutar video terowongan tingkat rendah, gunakan petunjuk berikut.

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

  1. Buat contoh SurfaceView .

  2. Buat instans audioSessionId .

  3. Buat instans AudioTrack dan MediaCodec dengan instans audioSessionId yang dibuat pada langkah 2.

  4. Antrean data audio ke AudioTrack dengan stempel waktu presentasi untuk bingkai audio pertama dalam data audio.

Untuk pemutaran siaran langsung di Android 11 atau lebih tinggi:

  1. Buat contoh SurfaceView .

  2. Dapatkan instance avSyncHwId dari Tuner .

  3. Buat instans AudioTrack dan MediaCodec dengan instans avSyncHwId yang dibuat pada 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 berdasarkan permintaan

Karena pemutaran video sesuai permintaan yang disalurkan terkait secara implisit dengan pemutaran AudioTrack , perilaku pemutaran video yang disalurkan mungkin bergantung pada perilaku pemutaran audio.

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

    • Untuk memberi sinyal bahwa bingkai video antrean pertama harus dirender segera setelah didekodekan, setel parameter PARAMETER_KEY_TUNNEL_PEEK ke 1 . Ketika bingkai video terkompresi diurutkan ulang dalam antrean (seperti ketika ada bingkai B ), ini berarti bingkai video pertama yang ditampilkan harus selalu berupa bingkai-I.

    • Jika Anda tidak ingin bingkai video antrean pertama dirender hingga pemutaran audio dimulai, setel parameter ini ke 0 .

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

  • Ketika data audio tidak disediakan ke AudioTrack dan buffernya kosong (audio underrun), pemutaran video terhenti hingga lebih banyak data audio ditulis karena jam audio tidak lagi berjalan.

  • Selama pemutaran, diskontinuitas yang tidak dapat diperbaiki oleh aplikasi mungkin muncul di stempel waktu presentasi audio. Jika hal ini terjadi, OEM akan memperbaiki kesenjangan negatif dengan menghentikan frame video saat ini, dan kesenjangan positif dengan menghapus frame video atau memasukkan frame audio senyap (tergantung pada penerapan OEM). Posisi bingkai AudioTimestamp tidak bertambah untuk bingkai audio senyap yang disisipkan.

Untuk produsen perangkat

Konfigurasi

OEM harus membuat dekoder video terpisah untuk mendukung pemutaran video terowongan. Dekoder ini harus menyatakan bahwa ia mampu melakukan pemutaran terowongan di file media_codecs.xml :

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

Ketika instans MediaCodec yang disalurkan dikonfigurasi dengan ID sesi audio, ia menanyakan 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 ID sesi audio:

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 instans AudioTrack telah dibuat, ID HW_AV_SYNC diteruskan ke aliran output dengan ID sesi audio yang sama. Jika belum dibuat, ID HW_AV_SYNC diteruskan ke aliran keluaran selama pembuatan AudioTrack . Hal ini dilakukan oleh thread pemutaran :

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

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

Selama konfigurasi komponen, komponen OMX atau Codec2 harus mengembalikan pegangan sideband yang dapat digunakan untuk mengaitkan codec dengan lapisan Hardware Composer (HWC). Saat aplikasi mengaitkan suatu permukaan dengan MediaCodec , pengendali sideband ini diteruskan ke HWC melalui SurfaceFlinger , yang mengonfigurasi lapisan tersebut 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 buffer gambar baru dari output codec pada waktu yang tepat, baik disinkronkan ke aliran output audio terkait atau jam referensi program tuner, menggabungkan buffer dengan konten lapisan lain saat ini, dan menampilkan gambar yang dihasilkan. Hal ini terjadi secara independen dari siklus persiapan dan pengaturan normal. Panggilan persiapan dan pengaturan hanya terjadi ketika lapisan lain berubah, atau ketika properti lapisan pita samping (seperti posisi atau ukuran) berubah.

Ya ampun

Komponen dekoder yang disalurkan harus mendukung hal berikut:

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

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

  • Mengirim peristiwa OMX_EventOnFirstTunnelFrameReady ketika frame video terowongan pertama telah didekodekan dan siap untuk dirender.

Implementasi AOSP mengonfigurasi mode terowongan di ACodec melalui 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 tersebut harus mengalokasikan pegangan sideband ke codec ini dan meneruskannya kembali melalui anggota pSidebandWindow sehingga HWC dapat mengidentifikasi codec terkait. Jika komponen tidak mendukung konfigurasi ini, komponen harus disetel bTunneled ke OMX_FALSE .

Kodek2

Di Android 11 atau lebih tinggi, Codec2 mendukung pemutaran terowongan. Komponen decoder harus mendukung hal berikut:

  • Mengonfigurasi C2PortTunneledModeTuning , yang mengonfigurasi mode terowongan dan meneruskan HW_AV_SYNC yang diambil dari perangkat output audio atau konfigurasi tuner.

  • Meminta C2_PARAMKEY_OUTPUT_TUNNEL_HANDLE , untuk mengalokasikan dan mengambil pegangan sideband untuk HWC.

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

  • Menangani C2_PARAMKEY_TUNNEL_START_RENDER , yang memerintahkan codec untuk segera merender frame yang ditandai dengan C2_PARAMKEY_TUNNEL_HOLD_RENDER , meskipun pemutaran audio belum dimulai.

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

Implementasi AOSP mengonfigurasi mode terowongan di CCodec melalui 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 tersebut harus mengalokasikan pegangan sideband ke codec ini dan meneruskannya kembali melalui C2PortTunnelHandlingTuning sehingga HWC dapat mengidentifikasi codec terkait.

Audio HAL

Untuk pemutaran video berdasarkan permintaan, Audio HAL menerima stempel waktu presentasi audio sejalan dengan data audio dalam format big-endian di dalam header yang terdapat 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 merender bingkai video secara sinkron dengan bingkai 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 diputar, Audio HAL mungkin perlu menguraikan metadata di dalam data audio terkompresi untuk menentukan durasi pemutarannya.

Jeda dukungan

Android 5 atau lebih rendah tidak menyertakan dukungan jeda. Anda dapat menjeda pemutaran terowongan hanya dengan kelaparan A/V, namun 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 lebih tinggi, AudioFlinger mendukung jeda dan melanjutkan untuk output audio langsung (terowongan). Jika HAL menerapkan jeda dan lanjutkan, jalur jeda dan lanjutkan diteruskan ke HAL.

Urutan panggilan jeda, siram, lanjutkan dipatuhi dengan mengeksekusi panggilan HAL di thread pemutaran (sama seperti offload).

Saran implementasi

Audio HAL

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

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

Ya ampun

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 terowongan harus:

  • Tentukan 0 buffer ( nBufferCountMin , nBufferCountActual ) pada port outputnya.

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

  • Tentukan kemampuannya dalam file media_codecs.xml dan nyatakan fitur pemutaran terowongan. Ini juga harus memperjelas segala batasan pada ukuran frame, penyelarasan, atau bitrate. Contohnya ditunjukkan 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 yang disalurkan dan tidak disalurkan, maka fitur pemutaran yang disalurkan harus dibiarkan sebagai tidak diperlukan. Baik decoder tunneled maupun nontunneled memiliki batasan kemampuan yang sama. Contohnya ditunjukkan 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>

Komposer Perangkat Keras (HWC)

Ketika ada lapisan terowongan (lapisan dengan HWC_SIDEBAND compositionType ) pada tampilan, sidebandStream lapisan tersebut adalah pengendali sideband yang dialokasikan oleh komponen video OMX.

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

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

HWC menggabungkan frame video berdasarkan audio

Gambar 2. Sinkronisasi perangkat keras (atau kernel atau driver) HWC