Tunnel multimediale

Il tunneling multimediale consente il tunneling di dati video compressi attraverso un hardware decodificatore video direttamente a un display, senza essere elaborato dal codice dell'app o Codice framework Android. Il codice specifico per dispositivo riportato nello stack Android determina quali fotogrammi video inviare al display e quando inviarli tramite confrontando i timestamp della presentazione del frame video con uno dei seguenti tipi di orologio interno:

  • Per riprodurre video on demand su Android 5 o versioni successive, è disponibile AudioTrack orologio sincronizzato con i timestamp della presentazione audio superato tramite l'app

  • Per la riproduzione di trasmissioni dal vivo in Android 11 o versioni successive, un orologio di riferimento del programma (PCR) o orologio del sistema (STC) controllato da un tuner

Premessa

La riproduzione di video tradizionale su Android invia notifiche all'app quando un frame video compresso è stato decodificato. L'app pubblicazioni il frame video decodificato al display in modo che venga visualizzato con lo stesso orologio di sistema tempo come frame audio corrispondente, recupero dei dati storici AudioTimestamps per calcolare la tempistica corretta.

Poiché la riproduzione video con tunnel ignora il codice dell'app e riduce il numero di processi che agiscono sul video, può fornire un rendering più efficiente del video a seconda dell'implementazione dell'OEM. Inoltre, può fornire video più accurati frequenza e sincronizzazione con l'orologio scelto (PRC, STC o audio) evitando problemi di tempistica introdotti da un potenziale disallineamento tra le tempistiche di Android delle richieste di rendering dei video e la tempistica delle vere vsync hardware. Tuttavia, il tunneling può inoltre ridurre il supporto di effetti GPU, come sfocatura o con angoli arrotondati nelle finestre Picture in picture (PIP), perché i buffer bypassare lo stack grafico Android.

Il seguente diagramma mostra in che modo il tunneling semplifica il processo di riproduzione dei video.

confronto tra modalità tradizionale e tunnel

Figura 1. Confronto tra processi di riproduzione video tradizionali e con tunnel

Per gli sviluppatori di app

Perché la maggior parte degli sviluppatori di app integra una libreria per la riproduzione dell'implementazione, nella maggior parte dei casi l'implementazione richiede solo la riconfigurazione per la riproduzione in tunnel. Per l'implementazione di basso livello di un video con tunnel segui queste istruzioni.

Per la riproduzione di video on demand su Android 5 o versioni successive:

  1. Crea un'istanza SurfaceView.

  2. Crea un'istanza audioSessionId.

  3. Crea istanze AudioTrack e MediaCodec con audioSessionId creata nel passaggio 2.

  4. Metti in coda i dati audio su AudioTrack con il timestamp della presentazione per il il primo frame audio nei dati audio.

Per la riproduzione di trasmissioni dal vivo in Android 11 o versioni successive:

  1. Crea un'istanza SurfaceView.

  2. Ottieni un'istanza avSyncHwId da Tuner.

  3. Crea istanze AudioTrack e MediaCodec con l'istanza avSyncHwId creato nel passaggio 2.

Il flusso di chiamata API viene mostrato nei seguenti snippet di codice:

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);

Comportamento della riproduzione di video on demand

Perché la riproduzione di video on demand con tunnel è legata implicitamente a AudioTrack durante la riproduzione, il comportamento della riproduzione dei video con tunnel potrebbe dipendere di riproduzione audio.

  • Sulla maggior parte dei dispositivi, per impostazione predefinita, il rendering di un frame video viene eseguito solo dopo l'audio inizia la riproduzione. Tuttavia, l'app potrebbe dover eseguire il rendering di un frame video prima avviare la riproduzione audio, ad esempio per mostrare all'utente il video corrente posizione durante la ricerca.

    • Per segnalare che il primo fotogramma video in coda deve essere visualizzato non appena che viene decodificato, imposta PARAMETER_KEY_TUNNEL_PEEK su 1. Quando i fotogrammi video compressi vengono riordinati nella coda (ad esempio quando B-frame ), significa che il primo fotogramma video visualizzato deve sempre essere un iframe.

    • Se non vuoi che venga eseguito il rendering del primo fotogramma video in coda prima dell'audio inizia la riproduzione, imposta questo parametro su 0.

    • Se questo parametro non è impostato, il comportamento del dispositivo è determinato dal produttore.

  • Quando non vengono forniti dati audio a AudioTrack e i buffer sono vuoti (audio insufficiente), la riproduzione video si blocca finché non vengono scritti altri dati audio perché l'orologio audio non avanza più.

  • Durante la riproduzione, potrebbero comparire discontinuità in cui l'app non può correggere timestamp della presentazione audio. In questo caso, l'OEM corregge il valore negativo intervalli vuoti bloccando il fotogramma video corrente e intervalli positivi interrompendo fotogrammi video o l'inserimento di fotogrammi audio silenziosi (a seconda dell'OEM) implementazione). La posizione del frame AudioTimestamp non aumenta per sono stati inseriti fotogrammi audio silenziosi.

Per i produttori di dispositivi

Configurazione

Gli OEM devono creare un decodificatore video separato per supportare la riproduzione video con tunnel. Questo decoder deve indicare che è in grado di riprodurre i contenuti con tunneling File media_codecs.xml:

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

Quando un'istanza MediaCodec con tunnel è configurata con un ID sessione audio, query AudioFlinger per questo ID HW_AV_SYNC:

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);
}

Durante questa query, AudioFlinger recupera l'ID HW_AV_SYNC dal dispositivo audio principale e lo associa internamente all'audio ID sessione:

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);

Se è già stata creata un'istanza AudioTrack, l'ID HW_AV_SYNC è passate allo stream di output con lo stesso ID sessione audio. Se non è stato creato, l'ID HW_AV_SYNC viene passato allo stream di output durante Creazione di AudioTrack. Ciò viene fatto dalla riproduzione thread:

mOutput->stream->common.set_parameters(&mOutput->stream->common, AUDIO_PARAMETER_STREAM_HW_AV_SYNC, hwAVSyncId);

L'ID HW_AV_SYNC, se corrisponde a uno stream di output audio o a un Tuner viene passata al componente OMX o Codec2 in modo che Il codice OEM può associare il codec allo stream di output audio corrispondente oppure lo stream del sintonizzatore.

Durante la configurazione dei componenti, il componente OMX o Codec2 deve restituire un handle di banda laterale che può essere usato per associare il codec a un Hardware Composer (HWC). Quando l'app associa una piattaforma a MediaCodec, questa banda laterale viene trasmesso a HWC tramite SurfaceFlinger, che configura il livello come banda laterale.

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 è responsabile della ricezione di nuovi buffer di immagine dall'output del codec al al momento giusto, sincronizzati con lo stream di output audio associato oppure l'orologio di riferimento del programma di sintonizzazione, che comprende i buffer con contenuti di altri livelli e la visualizzazione dell'immagine risultante. Ciò accade indipendentemente dal normale ciclo di preparazione e impostazione. La preparazione e la configurazione delle chiamate avvengono solo quando cambiano altri strati o quando le proprietà dello strato della banda laterale (ad es. posizione o dimensioni).

OMX

Un componente decoder con tunnel deve supportare quanto segue:

  • Impostazione di OMX.google.android.index.configureVideoTunnelMode estesa che utilizza la struttura ConfigureVideoTunnelModeParams per passare nell'ID HW_AV_SYNC associato al dispositivo di output audio.

  • La configurazione del parametro OMX_IndexConfigAndroidTunnelPeek, che indica per eseguire il rendering o non eseguire il rendering del primo frame video decodificato, indipendentemente se la riproduzione audio è iniziata.

  • Invio dell'evento OMX_EventOnFirstTunnelFrameReady durante il primo tunneling in corso... il frame video è stato decodificato ed è pronto per il rendering.

L'implementazione AOSP configura la modalità tunnel ACodec alla OMXNodeInstance come mostrato nello snippet di codice riportato di seguito:

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;

Se il componente supporta questa configurazione, deve allocare una banda laterale a questo codec e lo ripassi al membro pSidebandWindow in modo che che HWC possa identificare il codec associato. Se il componente non questa configurazione, il valore bTunneled deve essere impostato su OMX_FALSE.

Codec2

Su Android 11 o versioni successive, Codec2 supporta la riproduzione con tunnel. Il decoder deve supportare quanto segue:

  • È in corso la configurazione di C2PortTunneledModeTuning, che configura la modalità tunnel passa in HW_AV_SYNC recuperati dal dispositivo di output audio oppure la configurazione del sintonizzatore.

  • Esecuzione di query su C2_PARAMKEY_OUTPUT_TUNNEL_HANDLE per allocare e recuperare handle della banda laterale per HWC.

  • Gestione di C2_PARAMKEY_TUNNEL_HOLD_RENDER quando è collegato a un C2Work, che indica al codec di decodificare e segnalare il completamento del lavoro, ma non di eseguire il rendering nel buffer di output finché 1) il codec riceve le istruzioni per eseguirne il rendering oppure 2) venga avviata la riproduzione audio.

  • Gestione di C2_PARAMKEY_TUNNEL_START_RENDER, che indica al codec di immediatamente il frame contrassegnato con C2_PARAMKEY_TUNNEL_HOLD_RENDER, anche se la riproduzione audio non è iniziata.

  • Lascia debug.stagefright.ccodec_delayed_params non configurato (opzione consigliata). Se devi configurarlo, impostalo su false.

L'implementazione AOSP configura la modalità tunnel CCodec a C2PortTunnelModeTuning, come mostrato nello snippet di codice riportato di seguito:

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;
}

Se il componente supporta questa configurazione, deve allocare una banda laterale a questo codec e lo ritrasmetti tramite C2PortTunnelHandlingTuning in modo che che HWC possa identificare il codec associato.

HAL audio

Per la riproduzione di video on demand, l'Audio HAL riceve la presentazione audio timestamp in linea con i dati audio in formato big-endian all'interno di un'intestazione trovata all'inizio di ogni blocco di dati audio scritti dall'app:

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;
}

Affinché HWC possa eseguire il rendering dei frame video in modo che siano sincronizzati con i frame audio corrispondenti, L'HAL audio deve analizzare l'intestazione di sincronizzazione e utilizzare il timestamp della presentazione per risincronizza l'orologio di riproduzione con il rendering audio. Per risincronizzare quando l'audio compresso è in riproduzione, l'HAL audio potrebbe dover analizzare i metadati all'interno dei dati audio compressi per determinarne la durata di riproduzione.

Metti in pausa il supporto

Android 5 o versioni precedenti non include il supporto per la messa in pausa. Puoi mettere in pausa con tunnel viene riprodotta solo per invasione A/V, ma se il buffer interno per i video è elevato (ad esempio, c'è un secondo di dati nel componente OMX), mette in pausa non sembrano reattivi.

Su Android 5.1 o versioni successive, AudioFlinger supporta la messa in pausa e la ripresa per le chiamate dirette output audio (con tunneling). Se l'HAL implementa pausa e ripresa, monitora la pausa e il curriculum viene inoltrato all'HAL.

La sequenza di chiamate di pausa, svuotamento e ripresa viene rispettata eseguendo le chiamate HAL nel thread di riproduzione (uguale all'offload).

Suggerimenti per l'implementazione

HAL audio

Per Android 11, è possibile usare l'ID di sincronizzazione HW di PCR o STC per la sincronizzazione A/V, è supportato lo stream solo video.

Per Android 10 o versioni precedenti, i dispositivi che supportano la riproduzione di video in tunnel devono avere almeno un profilo di stream di output audio con FLAG_HW_AV_SYNC e Flag AUDIO_OUTPUT_FLAG_DIRECT nel relativo file audio_policy.conf. Queste segnalazioni vengono utilizzati per impostare l'orologio di sistema dall'orologio audio.

OMX

I produttori di dispositivi devono avere un componente OMX separato per i video sottoposti a tunnel (i produttori possono avere componenti OMX aggiuntivi per altri tipi di la riproduzione audio e video, come la riproduzione sicura). Il componente sottoposto a tunnel devono:

  • Specifica 0 buffer (nBufferCountMin, nBufferCountActual) nel relativo output una porta.

  • Implementa l'estensione OMX.google.android.index.prepareForAdaptivePlayback setParameter.

  • Specifica le sue capacità nel file media_codecs.xml e dichiara la funzionalità di riproduzione con tunnel. Dovrebbe anche chiarire eventuali limiti dimensioni, allineamento o velocità in bit. Di seguito è riportato un esempio:

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

Se viene utilizzato lo stesso componente OMX per supportare la decodifica con e senza tunnel, la funzionalità di riproduzione con tunneling non è obbligatoria. Sia con tunnel che i decoder non tunnelizzati hanno le stesse limitazioni di capacità. Un esempio sono come mostrato di seguito:

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

HWC (Hardware Composer)

Quando è presente un livello con tunnel (uno con HWC_SIDEBAND compositionType) attivo in una visualizzazione, il sidebandStream del livello è l'handle della banda laterale assegnato Componente video OMX.

HWC sincronizza i fotogrammi video decodificati (dal componente OMX in tunnel) a la traccia audio associata (con l'ID audio-hw-sync). Quando viene creato un nuovo fotogramma diventa corrente, l'HWC la combina con i contenuti correnti di tutti gli strati ricevuta durante l'ultima chiamata di preparazione o impostazione e visualizza l'immagine risultante. Le chiamate di preparazione o impostazione si verificano solo quando cambiano gli altri strati o quando delle proprietà del livello della banda laterale (come posizione o dimensione).

La figura seguente rappresenta l'HWC che lavora con l'hardware (o kernel driver), per combinare i fotogrammi video (7b) con le composizioni più recenti (7a) per la visualizzazione all'ora corretta, in base all'audio (7c).

HWC che combina fotogrammi video in base all&#39;audio

Figura 2. Sincronizzatore hardware (o kernel o driver) HWC