Google berkomitmen untuk mendorong terwujudnya keadilan ras bagi komunitas Kulit Hitam. Lihat caranya.

Terowongan multimedia

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 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 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.

perbandingan mode tradisi dan terowongan

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:

  1. Buat instance SurfaceView .

  2. Buat instance audioSessionId .

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

  4. 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:

  1. Buat instance SurfaceView .

  2. Dapatkan instance avSyncHwId dari Tuner .

  3. Buat AudioTrack dan MediaCodec dengan avSyncHwId 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 ke 1 . 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 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 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 meneruskan HW_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 ke C2Work , 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 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 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 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).

HWC menggabungkan bingkai video berdasarkan audio

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