Penerowongan multimedia memungkinkan data video terkompresi untuk disalurkan melalui dekoder video perangkat keras langsung ke tampilan, 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 kapan harus mengirimnya dengan membandingkan stempel waktu presentasi bingkai video dengan salah satu jenis jam internal berikut:
Untuk pemutaran video sesuai permintaan di Android 5 atau lebih tinggi, jam
AudioTrack
yang disinkronkan dengan stempel waktu presentasi audio yang diteruskan oleh aplikasiUntuk 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 saat 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, ini dapat memberikan rendering video yang lebih efisien tergantung pada implementasi OEM. Ini juga dapat memberikan irama dan sinkronisasi video yang lebih akurat ke jam yang dipilih (PRC, STC, atau audio) dengan menghindari masalah waktu yang disebabkan oleh potensi kemiringan antara waktu permintaan Android untuk merender video, dan waktu vsync perangkat keras yang sebenarnya. Namun, tunneling juga dapat mengurangi dukungan untuk efek GPU seperti buram atau sudut membulat di jendela picture-in-picture (PiP), karena buffer melewati tumpukan grafis Android.
Diagram berikut menunjukkan bagaimana tunneling menyederhanakan proses pemutaran video.
Gambar 1. Perbandingan proses pemutaran video tradisional dan tunneled
Untuk pengembang aplikasi
Karena sebagian besar pengembang aplikasi berintegrasi dengan pustaka untuk implementasi pemutaran, dalam banyak kasus implementasi hanya memerlukan konfigurasi ulang pustaka tersebut untuk pemutaran terowongan. Untuk penerapan pemutar video tunneled tingkat rendah, gunakan petunjuk berikut.
Untuk pemutaran video sesuai permintaan di Android 5 atau lebih tinggi:
Buat instance
SurfaceView
.Buat instance
audioSessionId
.Buat
AudioTrack
danMediaCodec
dengan instanceaudioSessionId
yang dibuat di langkah 2.Antrekan 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:
Buat instance
SurfaceView
.Dapatkan instance
avSyncHwId
dariTuner
.Buat
AudioTrack
danMediaCodec
denganavSyncHwId
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 sesuai permintaan
Karena pemutaran video tunneled sesuai permintaan terikat secara implisit dengan pemutaran AudioTrack
, perilaku pemutaran video tunneling 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 didekode, setel parameter
PARAMETER_KEY_TUNNEL_PEEK
ke1
. Ketika frame video terkompresi diurutkan ulang dalam antrian (seperti ketika B-frame hadir), ini berarti frame video pertama yang ditampilkan harus selalu menjadi I-frame.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.
Saat data audio tidak diberikan ke
AudioTrack
dan buffer kosong (audio underrun), pemutaran video terhenti hingga lebih banyak data audio ditulis karena jam audio tidak lagi maju.Selama pemutaran, diskontinuitas yang tidak dapat diperbaiki aplikasi mungkin muncul di stempel waktu presentasi audio. Ketika ini terjadi, OEM mengoreksi celah negatif dengan mengulur bingkai video saat ini, dan celah positif dengan menjatuhkan bingkai video atau menyisipkan bingkai audio senyap (bergantung pada implementasi OEM). Posisi bingkai
AudioTimestamp
tidak meningkat untuk bingkai audio senyap yang disisipkan.
Untuk produsen perangkat
Konfigurasi
OEM harus membuat dekoder video terpisah untuk mendukung pemutaran video terowongan. Dekoder ini harus mengiklankan bahwa ia mampu melakukan pemutaran terowongan di file media_codecs.xml
:
<Feature name="tunneled-playback" required="true"/>
Saat instance MediaCodec
dikonfigurasi dengan ID sesi audio, ia akan 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 AudioTrack
telah dibuat, ID HW_AV_SYNC
diteruskan ke aliran keluaran dengan ID sesi audio yang sama. Jika belum dibuat, maka ID HW_AV_SYNC
diteruskan ke aliran keluaran selama pembuatan AudioTrack
. Ini dilakukan oleh utas pemutaran :
mOutput->stream->common.set_parameters(&mOutput->stream->common, AUDIO_PARAMETER_STREAM_HW_AV_SYNC, hwAVSyncId);
ID HW_AV_SYNC
, apakah itu sesuai 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 pita samping yang dapat digunakan untuk mengaitkan codec dengan lapisan Komposer Perangkat Keras (HWC). Saat aplikasi mengaitkan permukaan dengan MediaCodec
, pegangan pita samping ini diteruskan ke HWC melalui SurfaceFlinger
, yang mengonfigurasi lapisan sebagai lapisan pita samping.
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 saat ini dari lapisan lain, dan menampilkan gambar yang dihasilkan. 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.
OMX
Komponen decoder tunneled harus mendukung hal berikut:
Menyetel parameter perluasan
OMX.google.android.index.configureVideoTunnelMode
, yang menggunakan strukturConfigureVideoTunnelModeParams
untuk meneruskan IDHW_AV_SYNC
yang terkait dengan perangkat output audio.Mengonfigurasi parameter
OMX_IndexConfigAndroidTunnelPeek
yang memberi tahu codec untuk merender atau tidak merender bingkai video dekode pertama, terlepas dari apakah pemutaran audio telah dimulai.Mengirim acara
OMX_EventOnFirstTunnelFrameReady
saat frame video tunneled pertama telah didekode dan siap untuk 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 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, itu harus disetel bTunneled
ke OMX_FALSE
.
Codec2
Di Android 11 atau lebih tinggi, Codec2
mendukung pemutaran terowongan. Komponen decoder harus mendukung hal berikut:
Mengonfigurasi
C2PortTunneledModeTuning
, yang mengonfigurasi mode terowongan dan meneruskanHW_AV_SYNC
yang diambil dari perangkat output audio atau konfigurasi tuner.C2_PARAMKEY_OUTPUT_TUNNEL_HANDLE
, untuk mengalokasikan dan mengambil pegangan sideband untuk HWC.Menangani
C2_PARAMKEY_TUNNEL_HOLD_RENDER
saat dilampirkan keC2Work
, yang menginstruksikan codec untuk memecahkan kode dan memberi sinyal penyelesaian pekerjaan, tetapi tidak merender buffer output sampai 1) codec kemudian diinstruksikan untuk merendernya atau 2) pemutaran audio dimulai.Menangani
C2_PARAMKEY_TUNNEL_START_RENDER
, yang memerintahkan codec untuk segera merender bingkai yang ditandai denganC2_PARAMKEY_TUNNEL_HOLD_RENDER
, meskipun pemutaran audio belum dimulai.Biarkan
debug.stagefright.ccodec_delayed_params
tidak dikonfigurasi (disarankan). Jika Anda mengonfigurasinya, setel kefalse
.
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, ¶ms);
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 pita samping ke codec ini dan meneruskannya kembali melalui C2PortTunnelHandlingTuning
sehingga HWC dapat mengidentifikasi codec terkait.
Audio HAL
Untuk pemutaran video sesuai permintaan, Audio HAL menerima stempel waktu presentasi audio sejalan 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 bingkai video 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 mengurai 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, tetapi jika buffer internal untuk video besar (misalnya, ada satu detik data dalam komponen OMX), itu membuat jeda terlihat tidak responsif.
Di Android 5.1 atau lebih tinggi, AudioFlinger
mendukung jeda dan lanjutkan untuk output audio langsung (terowongan). Jika HAL mengimplementasikan pause dan resume, track pause dan resume diteruskan ke HAL.
Jeda, flush, lanjutkan urutan panggilan dihormati dengan mengeksekusi panggilan HAL di utas pemutaran (sama seperti offload).
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 tunneled 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.
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 tunneled 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 batasan apa pun pada ukuran bingkai, perataan, atau bitrate. Contoh 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 tunneled dan nontunneled, fitur pemutaran tunneled tidak diperlukan. Baik decoder tunneled dan nontunneled kemudian memiliki keterbatasan kemampuan yang sama. Contoh 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)
Saat ada layer tunneled (layer dengan HWC_SIDEBAND
compositionType
) pada layar, sidebandStream
layer adalah pegangan sideband yang dialokasikan oleh komponen video OMX.
HWC menyinkronkan bingkai video yang didekodekan (dari komponen OMX tunneled) ke trek audio terkait (dengan ID audio-hw-sync
). Ketika bingkai video baru menjadi terkini, HWC menggabungkannya dengan konten saat ini 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 bingkai video (7b) dengan komposisi terbaru (7a) untuk ditampilkan pada waktu yang tepat, berdasarkan audio (7c).
Gambar 2. Sinkronisasi perangkat keras (atau kernel atau driver) HWC