Anda dapat mengimplementasikan tunneling multimedia di framework Android 5.0 dan yang lebih tinggi. Meskipun penerowongan multimedia tidak diperlukan untuk Android TV, ini memberikan pengalaman terbaik untuk konten definisi sangat tinggi (4K).
Untuk Android 11 atau lebih tinggi, Anda dapat menerapkan tunneling multimedia dengan konten audio dan video yang diumpankan langsung dari Tuner. Codec2
dan AudioTrack
dapat menggunakan ID sinkronisasi HW dari Tuner, yang mungkin sesuai dengan saluran referensi jam program (PCR) atau jam waktu sistem (STC).
Latar belakang
Kerangka kerja media Android menangani konten audio/video dalam empat cara:
- Perangkat lunak murni (decoding lokal): Prosesor aplikasi (AP) secara lokal menerjemahkan audio ke modulasi kode-pulsa (PCM) tanpa akselerasi khusus. Selalu digunakan untuk Ogg Vorbis, dan digunakan untuk MP3 dan AAC ketika tidak ada dukungan offload terkompresi.
- Offload audio terkompresi mengirimkan data audio terkompresi langsung ke prosesor sinyal digital (DSP) dan membuat AP mati sebanyak mungkin. Gunakan untuk memutar file musik dengan layar mati.
- Passthrough audio terkompresi mengirimkan audio terkompresi (khususnya AC3 dan E-AC3) langsung melalui HDMI ke TV eksternal atau penerima audio, tanpa mendekodekannya di perangkat Android TV. Bagian video ditangani secara terpisah.
- Penerowongan multimedia mengirimkan data audio dan video terkompresi secara bersamaan. Ketika aliran yang disandikan diterima oleh dekoder video dan audio, itu tidak kembali ke kerangka kerja. Idealnya, aliran tidak mengganggu AP.
- Passthrough multimedia mengirimkan data audio dan video terkompresi bersama-sama dari Tuner ke dekoder video dan audio tanpa melibatkan kerangka kerja.
Perbandingan pendekatan
Perangkat lunak murni | Pembongkaran audio terkompresi | Passthrough audio terkompresi | Terowongan multimedia | Lintasan multimedia | |
---|---|---|---|---|---|
Dekode lokasi | AP | DSP | TV atau penerima audio/video (AVR) | TV atau AVR | TV atau AVR |
Menangani audio | Ya | Ya | Ya | Ya | Tidak |
Menangani video | Ya | Tidak | Tidak | Ya | Tidak |
Untuk pengembang aplikasi
Buat instance SurfaceView
, dapatkan ID sesi audio, lalu buat AudioTrack
dan MediaCodec
untuk menyediakan pengaturan waktu dan konfigurasi yang diperlukan untuk pemutaran dan decoding bingkai video.
Untuk Android 11 atau lebih tinggi, sebagai alternatif untuk ID sesi audio, aplikasi bisa mendapatkan ID sinkronisasi HW dari Tuner dan memberikannya ke AudioTrack
dan MediaCodec
untuk sinkronisasi A/V.
Sinkronisasi A/V
Dalam mode tunneling multimedia, audio dan video disinkronkan pada jam master.
- Untuk Android 11 atau lebih tinggi, PCR atau STC dari Tuner mungkin merupakan jam utama untuk sinkronisasi A/V.
- Untuk Android 10 atau lebih rendah, jam audio adalah jam utama yang digunakan untuk pemutaran A/V.
Jika instans MediaCodec video MediaCodec
dan AudioTrack
ditautkan ke HW_AV_SYNC
di AudioTrack
, jam implisit yang diturunkan dari HW_AV_SYNC
membatasi kapan setiap bingkai video dan sampel audio disajikan, berdasarkan stempel waktu presentasi bingkai audio atau video (PTS) yang sebenarnya.
Alur panggilan API
Untuk Android 11 atau lebih tinggi, klien dapat menggunakan ID sinkronisasi HW dari Tuner.
- Buat instance
SurfaceView
.SurfaceView sv = new SurfaceView(mContext);
- Dapatkan ID sesi audio. ID unik ini digunakan dalam membuat trek audio (
AudioTrack
). Ini diteruskan ke codec media (MediaCodec
) dan digunakan oleh kerangka kerja media untuk menautkan jalur audio dan video.AudioManager am = mContext.getSystemService(AUDIO_SERVICE); int audioSessionId = am.generateAudioSessionId() // or, for Android 11 or higher int avSyncId = tuner.getAvSyncHwId();
- Buat
AudioTrack
denganAudioAttributes
sinkronisasi A/V HW.Manajer kebijakan audio meminta lapisan abstraksi perangkat keras (HAL) untuk output perangkat yang mendukung
FLAG_HW_AV_SYNC
, dan membuat trek audio yang terhubung langsung ke output ini tanpa mixer perantara.AudioAttributes.Builder aab = new AudioAttributes.Builder(); aab.setUsage(AudioAttributes.USAGE_MEDIA); aab.setContentType(AudioAttributes.CONTENT_TYPE_MOVIE); aab.setFlag(AudioAttributes.FLAG_HW_AV_SYNC); // or, for Android 11 or higher new tunerConfig = TunerConfiguration(0, avSyncId); aab.setTunerConfiguration(tunerConfig); AudioAttributes aa = aab.build(); AudioTrack at = new AudioTrack(aa);
- Buat instance
MediaCodec
video dan konfigurasikan untuk pemutaran video tunneled.// retrieve codec with tunneled video playback feature MediaFormat mf = MediaFormat.createVideoFormat(“video/hevc”, 3840, 2160); mf.setFeatureEnabled(CodecCapabilities.FEATURE_TunneledPlayback, true); MediaCodecList mcl = new MediaCodecList(MediaCodecList.ALL_CODECS); String codecName = mcl.findDecoderForFormat(mf); if (codecName == null) { return FAILURE; } // create codec and configure it mf.setInteger(MediaFormat.KEY_AUDIO_SESSION_ID, audioSessionId); // or, for Android 11 or higher mf.setInteger(MediaFormat.KEY_HARDWARE_AV_SYNC_ID, avSyncId); MediaCodec mc = MediaCodec.createCodecByName(codecName); mc.configure(mf, sv.getSurfaceHolder().getSurface(), null, 0);
- Decode bingkai video.
mc.start(); for (;;) { int ibi = mc.dequeueInputBuffer(timeoutUs); if (ibi >= 0) { ByteBuffer ib = mc.getInputBuffer(ibi); // fill input buffer (ib) with valid data ... mc.queueInputBuffer(ibi, ...); } // no need to dequeue explicitly output buffers. The codec // does this directly to the sideband layer. } mc.stop(); mc.release(); mc = null;
Catatan: Anda dapat mengganti urutan langkah 3 dan 4 dalam proses ini, seperti yang terlihat pada dua gambar di bawah ini.
Untuk produsen perangkat
OEM harus membuat komponen OpenMAX IL (OMX) dekoder video terpisah untuk mendukung pemutaran video terowongan. Komponen OMX ini harus mengiklankan bahwa ia mampu melakukan pemutaran terowongan (di media_codecs.xml
).
<Feature name=”tunneled-playback” required=”true” />
Komponen juga harus mendukung parameter perluasan OMX OMX.google.android.index.configureVideoTunnelMode
yang menggunakan struktur ConfigureVideoTunnelModeParams
.
struct ConfigureVideoTunnelModeParams { OMX_U32 nSize; // IN OMX_VERSIONTYPE nVersion; // IN OMX_U32 nPortIndex; // IN OMX_BOOL bTunneled; // IN/OUT OMX_U32 nAudioHwSync; // IN OMX_PTR pSidebandWindow; // OUT };
Saat permintaan pembuatan MediaCodec
dibuat, framework mengonfigurasi komponen OMX dalam mode tunneled (dengan menyetel bTunneled
ke OMX_TRUE
) dan meneruskan perangkat output audio terkait yang dibuat dengan flag AUDIO_HW_AV_SYNC
ke komponen OMX (dalam nAudioHwSync
).
Jika komponen mendukung konfigurasi ini, komponen tersebut harus mengalokasikan pegangan sideband ke codec ini dan meneruskannya kembali melalui anggota pSidebandWindow
. Pegangan sideband adalah tag ID untuk lapisan terowongan yang memungkinkan Komposer Perangkat Keras (HW Composer) mengidentifikasinya. Jika komponen tidak mendukung konfigurasi ini, komponen tersebut harus disetel bTunneled
ke OMX_FALSE
.
Framework mengambil layer tunneled (pegangan sideband) yang dialokasikan oleh komponen OMX dan meneruskannya ke HW Composer. Jenis compositionType
lapisan ini disetel ke HWC_SIDEBAND
. (Lihat hardware/libhardware/include/hardware/hwcomposer.h
.)
Komposer HW bertanggung jawab untuk menerima buffer gambar baru dari aliran pada waktu yang tepat (misalnya, disinkronkan ke perangkat output audio terkait), menggabungkannya dengan konten saat ini dari lapisan lain, dan menampilkan gambar yang dihasilkan. Ini terjadi terlepas dari siklus persiapan/pengaturan normal. Panggilan persiapan/pengaturan hanya terjadi ketika lapisan lain berubah, atau ketika properti lapisan pita samping (seperti posisi atau ukuran) berubah.
Konfigurasi
frameworks/av/services/audioFlinger/AudioFlinger.cpp
HAL mengembalikan ID HW_AV_SYNC
sebagai representasi desimal string karakter dari integer 64-bit. (Lihat frameworks/av/services/audioflinger/AudioFlinger.cpp
.)
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);
frameworks/av/services/audioflinger/Threads.cpp
Kerangka kerja audio harus menemukan aliran keluaran HAL yang sesuai dengan ID sesi ini, dan menanyakan HAL untuk hwAVSyncId
menggunakan set_parameters
.
mOutput->stream->common.set_parameters(&mOutput->stream->common, AUDIO_PARAMETER_STREAM_HW_AV_SYNC=hwAVSyncId);
Konfigurasi dekoder OMX
MediaCodec.java
Kerangka audio menemukan aliran keluaran HAL yang sesuai untuk ID sesi ini dan mengambil ID audio-hw-sync
dengan menanyakan HAL untuk tanda AUDIO_PARAMETER_STREAM_HW_AV_SYNC
menggunakan get_parameters
.
// Retrieve HW AV sync audio output device from Audio Service // in MediaCodec.configure() 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); } // ...
ID sinkronisasi HW ini diteruskan ke dekoder video tunneled OMX menggunakan parameter khusus OMX.google.android.index.configureVideoTunnelMode
.
ACodec.cpp
Setelah Anda mendapatkan ID sinkronisasi perangkat keras audio, ACodec menggunakannya untuk mengonfigurasi dekoder video tunneled sehingga dekoder video tunneled mengetahui trek audio mana yang akan disinkronkan.
// Assume you're going to use tunneled video rendering. // Configure OMX component in tunneled mode and grab sideband handle (sidebandHandle) from OMX // component. native_handle_t* sidebandHandle; // Configure OMX component in tunneled mode status_t err = mOMX->configureVideoTunnelMode(mNode, kPortIndexOutput, OMX_TRUE, audioHwSync, &sidebandHandle);
OMXNodeInstance.cpp
Komponen OMX dikonfigurasi dengan metode configureVideoTunnelMode
di atas.
// paraphrased 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;
ACodec.cpp
Setelah komponen OMX dikonfigurasi dalam mode tunneled, pegangan sideband dikaitkan dengan permukaan rendering.
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; }
Kemudian petunjuk resolusi maksimal, jika ada, dikirim ke komponen.
// Configure max adaptive playback resolution - as for any other video decoder int32_t maxWidth = 0, maxHeight = 0; if (msg->findInt32("max-width", &maxWidth) && msg->findInt32("max-height", &maxHeight)) { err = mOMX->prepareForAdaptivePlayback( mNode, kPortIndexOutput, OMX_TRUE, maxWidth, maxHeight); }
Jeda dukungan
Android 5.0 dan yang 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 1 detik data dalam komponen OMX), itu membuat jeda terlihat tidak responsif.
Di Android 5.1 dan yang lebih tinggi, AudioFlinger mendukung jeda dan lanjutkan untuk output audio langsung (tunnel). Jika HAL mengimplementasikan pause/resume, track pause/resume diteruskan ke HAL.
Jeda, flush, lanjutkan urutan panggilan dihormati dengan mengeksekusi panggilan HAL di utas pemutaran (sama seperti offload).
Dukungan Codec2
Untuk Android 11 atau lebih tinggi, Codec2 mendukung pemutaran terowongan.
CCodec.cpp
Untuk mendukung pemutaran terowongan, Codec2 bekerja mirip dengan OMX. Untuk mendukung ID sinkronisasi HW dari Tuner, Codec2 mencari ID sinkronisasi dari sumber di bawah.
sp<ANativeWindow> nativeWindow = static_cast<ANativeWindow *>(surface.get()); int32_t audioHwSync = 0; if (!msg->findInt32("hw-av-sync-id", &audioHwSync)) { if (!msg->findInt32("audio-hw-sync", &audioHwSync)) { } } err = configureTunneledVideoPlayback(comp, audioHwSync, nativeWindow);
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 terowongan harus memiliki setidaknya satu profil aliran keluaran audio dengan flag FLAG_HW_AV_SYNC
dan AUDIO_OUTPUT_FLAG_DIRECT
dalam 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 ini harus menentukan 0 buffer ( nBufferCountMin
, nBufferCountActual
) pada port outputnya.
Komponen tunneled juga harus mengimplementasikan ekstensi OMX.google.android.index.prepareForAdaptivePlayback setParameter
.
Komponen tunneled harus menentukan kemampuannya dalam file media_codecs.xml
dan mendeklarasikan fitur pemutaran tunneled. Ini juga harus memperjelas batasan apa pun pada ukuran bingkai, perataan, atau bitrate.
<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 non-tunneled, maka fitur pemutaran tunneled tidak diperlukan. Baik decoder tunneled dan non-tunneled kemudian memiliki keterbatasan kemampuan yang sama.
<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 HW
Saat ada layer tunneled (layer dengan HWC_SIDEBAND
compositionType
) pada tampilan, sidebandStream
layer adalah pegangan sideband yang dialokasikan oleh komponen video OMX.
Komposer HW menyinkronkan bingkai video yang didekodekan (dari komponen OMX tunneled) ke trek audio terkait (dengan ID audio-hw-sync
). Ketika bingkai video baru menjadi terkini, Komposer HW menggabungkannya dengan konten saat ini dari semua lapisan yang diterima selama panggilan persiapan/pengaturan terakhir, dan menampilkan gambar yang dihasilkan. Panggilan persiapan/pengaturan hanya terjadi ketika lapisan lain berubah, atau ketika properti lapisan pita samping (seperti posisi atau ukuran) berubah.
Gambar 4 menunjukkan Komposer HW yang bekerja dengan sinkronisasi HW (atau kernel/driver), untuk menggabungkan bingkai video (7b) dengan komposisi terbaru (7a) untuk ditampilkan pada waktu yang tepat, berdasarkan audio (7c).