Tunneling multimediale

Puoi implementare il tunneling multimediale nel framework Android 5.0 e versioni successive. Sebbene il tunneling multimediale non sia richiesto per Android TV, offre la migliore esperienza per i contenuti ad altissima definizione (4K).

Per Android 11 o versioni successive, puoi implementare il tunneling multimediale con contenuti audio e video alimentati direttamente da Tuner. Codec2 e AudioTrack possibile utilizzare l'ID di sincronizzazione HW dal sintonizzatore, che potrebbe corrispondere al clock di riferimento del programma (PCR) o il canale orologio di sistema (STC).

Sfondo

Il framework multimediale Android gestisce i contenuti audio/video in quattro modi:

  • Software puro (decodifica locale): Il processore applicativo (AP) decodifica audio localmente modulazione a impulsi codice (PCM) senza accelerazione speciale. Utilizzato sempre per Ogg Vorbis e utilizzato per MP3 e AAC quando non è disponibile il supporto per l'offload compresso.
  • Offload audio compresso invia i dati audio compressi direttamente al processore di segnale digitale (DSP) e mantiene il AP fuori il più possibile. Utilizzare per riprodurre file musicali con lo schermo spento.
  • Passthrough audio compresso invia audio compresso (specificamente AC3 e E-AC3) direttamente sopra HDMI a un televisore esterno o ricevitore audio, senza decodificarli sul dispositivo Android TV. La parte video viene gestita separatamente.
  • Multimedia tunneling invia compressi dati audio e video insieme. Quando il flusso codificato viene ricevuto dai decodificatori video e audio, non torna al framework. Idealmente, il flusso non interrompe l'AP.
  • Multimedia passthrough invia compressi dati audio e video insieme dal Tuner al video e decoder audio senza coinvolgere il quadro.
Diagramma di flusso del tunneling multimediale
Flusso tunneling Figura 1. Multimedia

Approccio al confronto

Software puro Scaricamento audio compresso Passthrough audio compresso Tunneling multimediale Passthrough multimediale
Decodifica posizione AP DSP TV o ricevitore audio/video (AVR) TV o AVR TV o AVR
Gestisce l'audio no
Maniglie video no no no

Per gli sviluppatori di app

Creare un SurfaceView esempio, ottenere un ID sessione audio, quindi creare le AudioTrack e MediaCodec istanze di fornire i tempi e le configurazioni necessarie per la decodifica e la riproduzione fotogramma video.

Per Android 11 o superiore, come un supplente per l'ID sessione audio, l'applicazione può ottenere la sincronizzazione HW ID da Tuner e fornirlo al AudioTrack e MediaCodec istanze per A / V sync.

Sincronizzazione A/V

In modalità tunneling multimediale, audio e video sono sincronizzati su un master clock.

  • Per Android 11 o versioni successive, PCR o STC di Tuner potrebbe essere il master clock per la sincronizzazione A/V.
  • Per Android 10 o versioni precedenti, l'orologio audio è l'orologio principale utilizzato per la riproduzione A/V.

Se il video tunnel MediaCodec dell'istanza e AudioTrack casi sono legati alla HW_AV_SYNC esempio nel AudioTrack , l'orologio implicita derivante da HW_AV_SYNC restringe quando viene presentato ogni fotogramma video e audio del campione, sulla base del reale o audio video frame tempo di presentazione di bollo (PTS).

Flusso di chiamate API

Per Android 11 o versioni successive, il client può utilizzare l'ID di sincronizzazione HW di Tuner.

  1. Creare un SurfaceView istanza.
    SurfaceView sv = new SurfaceView(mContext);
  2. Ottieni un ID sessione audio. Questo ID univoco viene utilizzato nella creazione della traccia audio ( AudioTrack ). E 'passato al codec multimediale ( MediaCodec ) e utilizzato dal framework multimediale per collegare i percorsi di audio e video.
    AudioManager am = mContext.getSystemService(AUDIO_SERVICE);
    int audioSessionId = am.generateAudioSessionId()
    // or, for Android 11 or higher
    int avSyncId = tuner.getAvSyncHwId();
  3. Creare AudioTrack con HW A / V Sync AudioAttributes .

    Il gestore delle politiche audio chiede al livello di astrazione hardware (HAL) per un dispositivo di output che supporta FLAG_HW_AV_SYNC , e crea una traccia audio direttamente collegato a questa uscita senza mixer intermedio.

    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);
    
  4. Creare un video MediaCodec istanza e configurarlo per la riproduzione video con tunnel.
    // 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);
    
  5. Decodifica i fotogrammi 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;

Nota: È possibile cambiare l'ordine delle fasi 3 e 4 in questo processo, come si vede nelle due figure seguenti.

Schema della traccia audio creata prima della configurazione del codec
Figura 2. La traccia audio creato prima configurare codec
Schema della traccia audio creata dopo la configurazione del codec
Figura 3. La traccia audio creato dopo configurare codec

Per i produttori di dispositivi

OEM dovrebbe creare un altro video decoder OpenMAX IL (OMX) componente di sostegno tunnel riproduzione video. Questo componente OMX deve pubblicizzare che è in grado di riprodurre con tunnel (in media_codecs.xml ).

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

Il componente deve supportare un OMX esteso parametro OMX.google.android.index.configureVideoTunnelMode che utilizza una ConfigureVideoTunnelModeParams struttura.

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

Quando un tunnel MediaCodec richiesta di creazione è fatta, il quadro configura il componente OMX in modalità tunnel (impostando bTunneled a OMX_TRUE ) e passa il dispositivo di uscita audio associato creato con AUDIO_HW_AV_SYNC bandiera al componente OMX (in nAudioHwSync ).

Se i supporti compongono questa configurazione, si dovrebbe assegnare una maniglia banda laterale di questo codec e passare indietro attraverso il pSidebandWindow utente. Una maniglia banda laterale è un tag ID per lo strato di tunnel che consente Hardware Compositore (HW Compositore) identificarlo. Se il componente non supporta questa configurazione, si dovrebbe impostare bTunneled a OMX_FALSE .

Il framework recupera il livello tunnel (l'handle della banda laterale) allocato dal componente OMX e lo passa all'HW Composer. Questo strato di compositionType viene impostato su HWC_SIDEBAND . (Vedere hardware/libhardware/include/hardware/hwcomposer.h .)

HW Composer è responsabile della ricezione di nuovi buffer di immagine dal flusso al momento opportuno (ad esempio, sincronizzato con il dispositivo di uscita audio associato), della loro composizione con il contenuto corrente di altri livelli e della visualizzazione dell'immagine risultante. Ciò avviene indipendentemente dal normale ciclo di preparazione/impostazione. Le chiamate di preparazione/impostazione si verificano solo quando cambiano gli altri livelli o quando cambiano le proprietà del livello della banda laterale (come la posizione o la dimensione).

Configurazione

frameworks/av/services/audioflinger/AudioFlinger.cpp

Il HAL restituisce l' HW_AV_SYNC ID come rappresentazione decimale stringa di caratteri di un intero a 64 bit. (Vedere 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

Il quadro audio deve trovare il flusso di output HAL a cui corrisponde questa sessione d'identità, e interrogare l'HAL per i hwAVSyncId utilizzando set_parameters .

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

Configurazione del decodificatore OMX

MediaCodec.java

Il framework audio trova il flusso di output HAL corrispondente per questo ID di sessione e recupera l' audio-hw-sync ID interrogando il HAL per AUDIO_PARAMETER_STREAM_HW_AV_SYNC bandiera utilizzando 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);
}

// ...

Questo HW sincronizzazione ID viene passato alla OMX tunneling decoder video utilizzando il parametro personalizzato OMX.google.android.index.configureVideoTunnelMode .

ACodec.cpp

Dopo aver ottenuto l'ID di sincronizzazione dell'hardware audio, ACodec lo utilizza per configurare il decoder video con tunnel in modo che il decoder video con tunnel sappia quale traccia audio sincronizzare.

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

Il componente OMX è configurata dal configureVideoTunnelMode metodo di cui sopra.

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

Dopo che il componente OMX è stato configurato in modalità tunnel, l'handle della banda laterale è associato alla superficie di 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; }

Quindi il suggerimento di risoluzione massima, se presente, viene inviato al componente.

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

Metti in pausa il supporto

Android 5.0 e versioni precedenti non includono il supporto per la pausa. È possibile mettere in pausa la riproduzione tramite tunnel solo per mancanza di A/V, ma se il buffer interno per il video è grande (ad esempio, c'è 1 secondo di dati nel componente OMX), la pausa sembra non rispondere.

In Android 5.1 e versioni successive, AudioFlinger supporta la pausa e la ripresa per le uscite audio dirette (tunnel). Se l'HAL implementa la pausa/ripresa, la pausa/ripresa della traccia viene inoltrata all'HAL.

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

Supporto Codec2

Per Android 11 o versioni successive, Codec2 supporta la riproduzione tramite tunnel.

CCodec.cpp

Per supportare la riproduzione con tunnel, Codec2 funziona in modo simile a OMX. Per supportare l'ID di sincronizzazione HW da Tuner, Codec2 cerca l'ID di sincronizzazione dalle fonti sottostanti.

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

Suggerimenti per l'implementazione

Audio HAL

Per Android 11, l'ID di sincronizzazione HW da PCR o STC può essere utilizzato per la sincronizzazione A/V, quindi è supportato lo streaming solo video.

Per Android 10 o inferiore, dispositivi che supportano la riproduzione video con tunnel dovrebbero avere almeno un audio profilo di flusso di output con le bandiere FLAG_HW_AV_SYNC e AUDIO_OUTPUT_FLAG_DIRECT nella sua audio_policy.conf file. Questi flag vengono utilizzati per impostare l'orologio di sistema dall'orologio audio.

OMX

I produttori di dispositivi dovrebbero disporre di un componente OMX separato per la riproduzione di video tramite tunnel. I produttori possono disporre di componenti OMX aggiuntivi per altri tipi di riproduzione audio e video, come la riproduzione sicura.

Questo componente deve specificare 0 buffer ( nBufferCountMin , nBufferCountActual ) sulla sua porta di uscita.

Il componente tunnel deve implementare anche OMX.google.android.index.prepareForAdaptivePlayback setParameter estensione.

Il componente tunnel deve specificare le sue capacità nel media_codecs.xml di file e dichiarare la funzione di riproduzione tunnel. Dovrebbe anche chiarire eventuali limitazioni alla dimensione del fotogramma, all'allineamento o al 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>

Se lo stesso componente OMX viene utilizzato per supportare la decodifica con e senza tunnel, allora dovrebbe lasciare la funzione di riproduzione con tunnel come non necessaria. Entrambi i decoder tunnel e non tunnel hanno quindi le stesse limitazioni di capacità.

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

Compositore HW

Quando c'è uno strato tunnel (uno strato con HWC_SIDEBAND compositionType ) su un display, del livello sidebandStream è la maniglia banda laterale assegnato dal componente video OMX.

Il compositore HW sincronizza decodificato fotogrammi video (dal componente OMX tunnel) per la traccia audio associato (con l' audio-hw-sync ID). Quando un nuovo fotogramma video diventa corrente, HW Composer lo compone con il contenuto corrente di tutti i livelli ricevuti durante l'ultima chiamata di preparazione/impostazione e visualizza l'immagine risultante. Le chiamate di preparazione/impostazione si verificano solo quando cambiano gli altri livelli o quando cambiano le proprietà del livello della banda laterale (come la posizione o la dimensione).

La Figura 4 rappresenta l'HW Composer che lavora con il sincronizzatore HW (o kernel/driver), per combinare i fotogrammi video (7b) con l'ultima composizione (7a) per la visualizzazione all'ora corretta, basata sull'audio (7c).

Schema del compositore hardware che combina i fotogrammi video in base all'audio
Figura 4. HW compositore lavorare con HW (o kernel / driver) sincronizzatore