Tunneling multimedia

Tunneling multimedia, juga dikenal sebagai mode tunnel, memungkinkan data video terkompresi di-tunnel melalui dekoder video hardware langsung ke layar, tanpa diproses oleh kode aplikasi atau kode framework Android. Kode khusus perangkat di bawah stack Android menentukan frame video mana yang akan dikirim ke layar dan kapan harus dikirim dengan membandingkan stempel waktu presentasi frame video dengan salah satu jenis clock internal berikut:

  • Untuk pemutaran video sesuai permintaan di Android 5 atau yang lebih tinggi, AudioTrack clock yang disinkronkan dengan stempel waktu presentasi audio diteruskan oleh aplikasi

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

Latar belakang

Pemutaran video mode non-tunnel di Android memberi tahu aplikasi saat frame video terkompresi telah didekode. Kemudian, aplikasi melepaskan frame video yang didekode ke layar untuk dirender pada waktu clock sistem yang sama dengan frame audio yang sesuai, mengambil instance AudioTimestamp historis untuk menghitung waktu yang tepat.

Karena pemutaran video yang di-tunnel melewati kode aplikasi dan mengurangi jumlah proses yang memengaruhi video, pemutaran ini dapat memberikan rendering video yang lebih efisien bergantung pada penerapan OEM. Hal ini juga dapat memberikan irama dan sinkronisasi video yang lebih akurat ke clock yang dipilih (PRC, STC, atau audio) dengan menghindari masalah pengaturan waktu yang disebabkan oleh potensi kemiringan antara waktu permintaan Android untuk merender video, dan waktu sinkronisasi vertikal hardware yang sebenarnya. Namun, tunneling juga dapat mengurangi dukungan untuk efek GPU seperti blurring atau sudut bulat di jendela picture-in-picture (PiP), karena buffer melewati stack grafis Android.

Diagram berikut menunjukkan cara tunneling menyederhanakan proses pemutaran video.

perbandingan mode tradisional dan tunnel

Gambar 1. Perbandingan proses pemutaran video yang tidak melalui tunnel dan yang melalui tunnel.

Untuk developer aplikasi

Karena sebagian besar developer aplikasi berintegrasi dengan library untuk penerapan pemutaran, dalam sebagian besar kasus, penerapan hanya memerlukan konfigurasi ulang library tersebut untuk pemutaran melalui tunnel. Untuk penerapan tingkat rendah pemutar video yang di-tunnel, gunakan petunjuk berikut.

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

  1. Buat instance SurfaceView.

  2. Buat instance audioSessionId.

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

  4. Mengantrekan data audio ke AudioTrack dengan stempel waktu presentasi untuk frame audio pertama dalam data audio.

Untuk pemutaran siaran langsung di Android 11 atau yang lebih baru:

  1. Buat instance SurfaceView.

  2. Dapatkan instance avSyncHwId dari Tuner.

  3. Buat instance AudioTrack dan MediaCodec dengan instance avSyncHwId yang dibuat pada langkah 2.

Alur panggilan API ditunjukkan 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 di-tunnel secara implisit terkait dengan pemutaran AudioTrack kembali, perilaku pemutaran video yang di-tunnel mungkin bergantung pada perilaku pemutaran audio.

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

    • Untuk menandakan bahwa frame video yang diantrekan pertama harus dirender segera setelah didekode, tetapkan parameter PARAMETER_KEY_TUNNEL_PEEK ke 1. Jika frame video yang dikompresi diurutkan ulang dalam antrean (seperti saat B-frame ada), ini berarti frame video pertama yang ditampilkan harus selalu berupa I-frame.

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

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

  • Jika data audio tidak diberikan ke AudioTrack dan buffer kosong (underrun audio), pemutaran video akan terhenti hingga lebih banyak data audio ditulis karena clock audio tidak lagi berjalan.

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

Alur urutan pencarian akurat

Pencarian akurat memungkinkan Anda menemukan titik tertentu dalam video. Berbeda dengan pencarian keyframe, yang hanya melompat ke frame I terdekat dan dapat menyimpang dari posisi target beberapa detik, pencarian presisi merender video pada stempel waktu yang diminta secara tepat. Dengan mematuhi urutan API tertentu ini, aplikasi dapat melakukan pra-pemutaran latar belakang dan sinkronisasi pengaturan waktu dengan lancar. Hal ini memastikan frame target ditampilkan secara instan saat pemutaran dilanjutkan.

Untuk melakukan penelusuran yang presisi, ikuti urutan eksekusi yang diilustrasikan dalam Gambar 2:

alur urutan penelusuran

Gambar 2. Alur urutan untuk mencapai pencarian yang akurat.

Detail penting meliputi:

  • Eksekusi paralel: Anda dapat menjalankan langkah-langkah dalam satu kotak par secara serentak. Misalnya, panggilan video MediaCodec tidak bergantung pada AudioTrack.

  • Dependensi berurutan: Panggil semua operasi dalam kotak par pertama sebelum melanjutkan ke kotak par kedua. Secara khusus, aplikasi harus memastikan bahwa AudioTrack.write dan buffer dalam video MediaCodec diantrekan sebelum memanggil AudioTrack.play.

Alur urutan pemutaran kecepatan variabel

Pemutaran kecepatan variabel memungkinkan Anda memutar video dengan kecepatan yang lebih cepat atau lebih lambat daripada kecepatan normal. Fitur ini umumnya digunakan oleh aplikasi untuk memungkinkan pengguna mengonsumsi konten lebih cepat (seperti memutar kuliah atau podcast edukasi pada kecepatan 1,5x atau 2,0x untuk menghemat waktu) atau lebih lambat (seperti menganalisis permainan atletik atau video instruksional pada kecepatan 0,5x).

Untuk menyetel kecepatan, ikuti urutan eksekusi yang diilustrasikan dalam Gambar 3:

alur urutan kecepatan

Gambar 3. Urutan alur untuk menyetel kecepatan.

Perilaku dan persyaratan teknis berikut tidak tercakup dalam diagram urutan Gambar 3:

  • AudioTrack.getTimestamp menampilkan framePosition berdasarkan frekuensi input audio asli. Misalnya, dengan input 44100 Hz dan kecepatan pemutaran 2,0x, setelah diputar selama 2 detik, AudioTrack.getTimestamp akan menampilkan framePosition sebesar 176400.

  • Jika aplikasi memanggil setSpeed(1.5) dan berhasil, lalu aplikasi memanggil setSpeed(30) dan gagal, pemutaran akan tetap pada 1,5x.

  • Jika audio dibisukan (menggunakan setVolume), aplikasi tetap harus mengirim buffer audio karena frame video dirender berdasarkan posisi audio.

  • Nada audio dipertahankan saat kecepatan diubah.

  • Kecepatan pemutaran tidak terpengaruh oleh tindakan pemutaran lainnya.

    • Contoh 1: Jika kecepatan pemutaran adalah 1,5x dan AudioTrack dijeda, kecepatan akan tetap 1,5x setelah AudioTrack dilanjutkan.

    • Contoh 2: Jika kecepatan pemutaran adalah 1,5x dan pengguna mencari PTS yang berbeda, dengan mengikuti Gambar 2, pemutaran akan tetap pada 1,5x.

  • Untuk membantu memastikan semua frame didekode tepat waktu agar dapat dirender pada kecepatan yang dipilih, setel KEY_OPERATING_RATE agar sesuai dengan hasil perkalian kecepatan frame video dan kecepatan pemutaran. Jika KEY_OPERATING_RATE tidak disetel cukup tinggi, codec mungkin tidak dapat mendekode frame dengan cukup cepat, sehingga menyebabkan penghapusan frame yang tidak diinginkan selama pemutaran.

    • Contoh: Jika kecepatan frame asli konten adalah 60 fps, dan kecepatan pemutaran adalah 2x, tetapkan KEY_OPERATING_RATE ke 120.
  • Menetapkan kecepatan berulang kali dengan kecepatan yang didukung berbeda tidak akan memicu error apa pun, dan perilaku pemutaran setelah panggilan terakhir akan sama seperti jika kecepatan ditetapkan hanya sekali, ke setelan kecepatan terbaru.

Untuk produsen perangkat

Konfigurasi

OEM harus membuat dekoder video terpisah untuk mendukung pemutaran video yang di-tunnel. Decoder ini harus mengiklankan bahwa decoder tersebut mampu melakukan pemutaran yang di-tunnel di file media_codecs.xml:

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

Saat instance MediaCodec yang di-tunnel dikonfigurasi dengan ID sesi audio, instance tersebut akan 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 secara internal mengaitkannya 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 instance AudioTrack telah dibuat, ID HW_AV_SYNC akan diteruskan ke aliran output dengan ID sesi audio yang sama. Jika belum dibuat, ID HW_AV_SYNC akan diteruskan ke aliran output 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 yang 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 streaming tuner.

Selama konfigurasi komponen, komponen OMX atau Codec2 harus menampilkan handle sideband yang dapat digunakan untuk mengaitkan codec dengan lapisan Hardware Composer (HWC). Saat aplikasi mengaitkan platform dengan MediaCodec, handle sideband ini diteruskan ke HWC melalui SurfaceFlinger, yang mengonfigurasi lapisan 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 dengan aliran output audio terkait atau clock referensi program tuner, menggabungkan buffer dengan konten lapisan lain saat ini, dan menampilkan gambar yang dihasilkan. Hal ini terjadi secara independen dari siklus penyiapan dan setel normal. Panggilan prepare dan set hanya terjadi saat lapisan lain berubah, atau saat properti lapisan sideband (seperti posisi atau ukuran) berubah.

OMX

Komponen decoder yang di-tunnel harus mendukung hal berikut:

  • Menetapkan parameter OMX.google.android.index.configureVideoTunnelMode yang diperluas, 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 frame video pertama yang didekode, terlepas dari apakah pemutaran audio telah dimulai.

  • Mengirim peristiwa OMX_EventOnFirstTunnelFrameReady saat frame video pertama yang di-tunnel telah didekode dan siap dirender.

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

Codec2

Di Android 11 atau yang lebih tinggi, Codec2 mendukung pemutaran melalui tunnel. Komponen dekoder harus mendukung hal berikut:

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

  • Membuat kueri C2_PARAMKEY_OUTPUT_TUNNEL_HANDLE, untuk mengalokasikan dan mengambil handle sideband untuk HWC.

  • Menangani C2_PARAMKEY_TUNNEL_HOLD_RENDER saat terpasang ke C2Work, yang menginstruksikan codec untuk mendekode dan memberi sinyal penyelesaian tugas, tetapi tidak 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 merender frame 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, tetapkan ke false.

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

HAL Audio

Untuk pemutaran video on-demand, HAL Audio menerima stempel waktu presentasi audio sebaris 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 merender frame video secara sinkron dengan frame audio yang sesuai, HAL Audio harus mem-parsing header sinkronisasi dan menggunakan stempel waktu presentasi untuk menyinkronkan ulang clock pemutaran dengan rendering audio. Untuk menyinkronkan ulang saat audio terkompresi sedang diputar, Audio HAL mungkin perlu mem-parsing metadata di dalam data audio terkompresi untuk menentukan durasi pemutarannya.

Menjeda dukungan

Android 5 atau yang lebih rendah tidak menyertakan dukungan jeda. Anda dapat menjeda pemutaran yang di-tunnel hanya dengan kekurangan A/V, tetapi jika buffer internal untuk video berukuran 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 output audio langsung (ber-tunnel). Jika HAL menerapkan jeda dan lanjutkan, jeda dan lanjutkan pelacakan akan diteruskan ke HAL.

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

Saran penerapan

HAL Audio

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

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

OMX

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

  • Tentukan 0 buffer (nBufferCountMin, nBufferCountActual) di port output-nya.

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

  • Tentukan kemampuannya dalam file media_codecs.xml dan deklarasikan fitur pemutaran yang di-tunnel. Dokumen ini juga harus mengklarifikasi batasan pada ukuran frame, perataan, atau bitrate. 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 yang di-tunnel dan tidak di-tunnel, komponen tersebut harus membiarkan fitur pemutaran yang di-tunnel sebagai tidak diperlukan. Dekoder yang di-tunnel dan yang tidak di-tunnel kemudian memiliki batasan kemampuan yang sama. Contohnya 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)

Jika ada lapisan yang di-tunnel (lapisan dengan HWC_SIDEBAND compositionType) di layar, sidebandStream lapisan adalah handle sideband yang dialokasikan oleh komponen video OMX.

HWC menyinkronkan frame video yang didekode (dari komponen OMX yang di-tunnel) ke trek audio terkait (dengan ID audio-hw-sync). Saat frame video baru menjadi saat ini, HWC menggabungkannya dengan konten saat ini dari semua lapisan yang diterima selama panggilan persiapan atau set terakhir, dan menampilkan gambar yang dihasilkan. Panggilan persiapan atau set hanya terjadi saat lapisan lain berubah, atau saat properti lapisan sideband (seperti posisi atau ukuran) berubah.

Gambar berikut menunjukkan HWC yang bekerja dengan sinkronisasi hardware (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 4. Penyinkron hardware HWC (atau kernel atau driver).