Tunneling multimedia memungkinkan data video yang dikompresi untuk melakukan tunneling melalui decoder 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 waktu pengirimannya dengan membandingkan stempel waktu presentasi frame video dengan salah satu jenis jam internal berikut:
Untuk pemutaran video on-demand di Android 5 atau yang lebih tinggi, jam
AudioTrack
yang disinkronkan ke stempel waktu presentasi audio diteruskan oleh aplikasiUntuk pemutaran live streaming di Android 11 atau yang lebih tinggi, jam referensi program (PCR) atau jam waktu sistem (STC) yang dikendalikan oleh tuner
Latar belakang
Pemutaran video tradisional di Android memberi tahu
aplikasi saat frame video yang dikompresi telah didekode. Kemudian, aplikasi
merilis
bingkai video yang didekode ke layar untuk dirender pada waktu clock sistem
yang sama dengan bingkai audio yang sesuai,
mengambil instance
AudioTimestamps
historis untuk menghitung waktu yang benar.
Karena pemutaran video yang di-tunnel mengabaikan kode aplikasi dan mengurangi jumlah proses yang bekerja pada video, pemutaran ini dapat memberikan rendering video yang lebih efisien bergantung pada penerapan OEM. Hal ini juga dapat memberikan sinkronisasi dan metrik video yang lebih akurat ke jam yang dipilih (PRC, STC, atau audio) dengan menghindari masalah pengaturan waktu yang disebabkan oleh potensi penyimpangan antara pengaturan waktu permintaan Android untuk merender video, dan pengaturan waktu vsync hardware yang sebenarnya. Namun, tunneling juga dapat mengurangi dukungan untuk efek GPU seperti blurring atau sudut membulat di jendela picture-in-picture (PiP), karena buffer mengabaikan stack grafis Android.
Diagram berikut menunjukkan bagaimana tunneling menyederhanakan proses pemutaran video.
Gambar 1. Perbandingan proses pemutaran video tradisional dan 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 implementasi tingkat rendah dari pemutar video yang disalurkan, gunakan petunjuk berikut.
Untuk pemutaran video on demand di Android 5 atau yang lebih baru:
Buat instance
SurfaceView
.Buat instance
audioSessionId
.Buat instance
AudioTrack
danMediaCodec
dengan instanceaudioSessionId
yang dibuat di langkah 2.Antrekan 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:
Buat instance
SurfaceView
.Dapatkan instance
avSyncHwId
dariTuner
.Buat instance
AudioTrack
danMediaCodec
dengan instanceavSyncHwId
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 di-tunnel secara implisit terikat dengan pemutaran
AudioTrack
, 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 menampilkan posisi video saat ini kepada pengguna saat mencari.
Untuk memberi sinyal bahwa frame video pertama yang diantrekan harus dirender segera setelah didekode, tetapkan parameter
PARAMETER_KEY_TUNNEL_PEEK
ke1
. 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 frame video pertama dalam antrean dirender hingga pemutaran audio dimulai, tetapkan parameter ini ke
0
.Jika parameter ini tidak ditetapkan, OEM akan menentukan perilaku untuk perangkat.
Jika 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, ketidakkontinuan yang tidak dapat diperbaiki aplikasi mungkin muncul dalam stempel waktu presentasi audio. Jika hal ini terjadi, OEM akan memperbaiki celah negatif dengan menjeda frame video saat ini, dan celah positif dengan menghapus frame video atau menyisipkan frame audio tanpa suara (bergantung pada penerapan OEM ). Posisi frame
AudioTimestamp
tidak bertambah untuk frame audio bisu yang disisipkan.
Untuk produsen perangkat
Konfigurasi
OEM harus membuat dekoder video terpisah untuk mendukung pemutaran video yang di-tunnel.
Dekoder ini harus mengiklankan bahwa dekoder ini mampu melakukan pemutaran melalui tunnel dalam
file media_codecs.xml
:
<Feature name="tunneled-playback" required="true"/>
Saat instance MediaCodec
yang di-tunnel dikonfigurasi dengan ID sesi audio, instance tersebut akan membuat kueri 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 instance AudioTrack
sudah dibuat, ID HW_AV_SYNC
diteruskan ke streaming 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
playback:
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
, tuas sideband
ini diturunkan 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 ke aliran output audio terkait atau jam referensi program tuner, mengomposisi buffer dengan konten lapisan lain saat ini, dan menampilkan gambar yang dihasilkan. Hal ini terjadi secara independen dari siklus penyiapan dan penetapan normal. Panggilan persiapan dan penetapan 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 ekstensi
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 frame video pertama yang didekode, terlepas dari apakah pemutaran audio telah dimulai atau tidak.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 decoder
harus mendukung hal berikut:
Mengonfigurasi
C2PortTunneledModeTuning
, yang mengonfigurasi mode tunnel dan meneruskanHW_AV_SYNC
yang diambil dari perangkat output audio atau konfigurasi tuner.Mengkueri
C2_PARAMKEY_OUTPUT_TUNNEL_HANDLE
, guna mengalokasikan dan mengambil handle sideband untuk HWC.Menangani
C2_PARAMKEY_TUNNEL_HOLD_RENDER
saat dilampirkan keC2Work
, 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 merender frame yang ditandai denganC2_PARAMKEY_TUNNEL_HOLD_RENDER
, meskipun pemutaran audio belum dimulai.Biarkan
debug.stagefright.ccodec_delayed_params
tidak dikonfigurasi (direkomendasikan). Jika Anda mengonfigurasinya, tetapkan 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 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, Audio HAL menerima stempel waktu presentasi audio yang inline 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 yang sinkron dengan frame audio yang sesuai, HAL Audio 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 yang dikompresi untuk menentukan durasi pemutarannya.
Jeda dukungan
Android 5 atau yang lebih lama tidak menyertakan dukungan jeda. Anda dapat menjeda pemutaran yang disalurkan hanya dengan kekurangan A/V, tetapi jika buffer internal untuk video berukuran besar (misalnya, ada satu detik data dalam komponen OMX), jeda tersebut akan membuat jeda terlihat tidak responsif.
Di Android 5.1 atau yang lebih tinggi, AudioFlinger
mendukung jeda dan lanjutkan untuk output audio
langsung (tunneled). Jika HAL menerapkan jeda dan lanjutkan, lacak jeda
dan lanjutkan akan diteruskan ke HAL.
Urutan panggilan jeda, flush, lanjutkan diikuti 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 lama, perangkat yang mendukung pemutaran video yang di-tunnel harus memiliki
setidaknya satu profil streaming output audio dengan flag 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 aman). Komponen yang disalurkan harus:
Tentukan 0 buffering (
nBufferCountMin
,nBufferCountActual
) pada port output-nya.Implementasikan ekstensi
OMX.google.android.index.prepareForAdaptivePlayback setParameter
.Tentukan kemampuannya dalam file
media_codecs.xml
dan deklarasikan fitur pemutaran tunnel. Hal ini juga harus memperjelas batasan terkait ukuran frame, penyelarasan, 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 dengan tunnel dan tanpa tunnel, komponen tersebut harus membiarkan fitur pemutaran dengan tunnel sebagai tidak diperlukan. Baik decoder yang disalurkan maupun nontunneled 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 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 disalurkan) ke
trek audio terkait (dengan ID audio-hw-sync
). Saat frame video baru
menjadi saat ini, HWC akan menggabungkannya dengan konten saat ini dari semua lapisan
yang diterima selama panggilan persiapan atau penetapan terakhir, dan menampilkan gambar yang dihasilkan.
Panggilan persiapan atau setel hanya terjadi saat lapisan lain berubah, atau saat
properti lapisan sideband (seperti posisi atau ukuran) berubah.
Gambar berikut menunjukkan HWC yang bekerja dengan sinkroniser hardware (atau kernel atau driver), untuk menggabungkan frame video (7b) dengan komposisi terbaru (7a) untuk ditampilkan pada waktu yang tepat, berdasarkan audio (7c).
Gambar 2. Sinkronisator hardware (atau kernel atau driver) HWC