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'appPer 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.
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:
Crea un'istanza
SurfaceView
.Crea un'istanza
audioSessionId
.Crea istanze
AudioTrack
eMediaCodec
conaudioSessionId
creata nel passaggio 2.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:
Crea un'istanza
SurfaceView
.Ottieni un'istanza
avSyncHwId
daTuner
.Crea istanze
AudioTrack
eMediaCodec
con l'istanzaavSyncHwId
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
su1
. 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 strutturaConfigureVideoTunnelModeParams
per passare nell'IDHW_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 inHW_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 unC2Work
, 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 conC2_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 sufalse
.
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, ¶ms);
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).
Figura 2. Sincronizzatore hardware (o kernel o driver) HWC