Multimedia-Tunneling

Sie können Multimedia-Tunneling in Android-Framework 5.0 und höher implementieren. Obwohl für Android TV kein Multimedia-Tunneling erforderlich ist, bietet es die beste Erfahrung für Inhalte in Ultra-High-Definition (4K).

Für Android 11 oder höher können Sie Multimedia-Tunneling mit Audio- und Videoinhalten implementieren, die direkt vom Tuner eingespeist werden. Codec2 und AudioTrack kann die HW - Sync - ID vom Tuner verwenden, die auf die Programmtaktreferenz (PCR) oder Systemzeittakt (STC) -Kanals entsprechen könnten.

Hintergrund

Das Android Media Framework verarbeitet Audio-/Videoinhalte auf vier Arten:

  • Reine Software (lokale Decodierung): Der Anwendungsprozessor (AP) dekodiert lokal Audio Puls-Code - Modulation (PCM) ohne besondere Beschleunigung. Wird immer für Ogg Vorbis verwendet und wird für MP3 und AAC verwendet, wenn keine komprimierte Offload-Unterstützung vorhanden ist.
  • Komprimierte Audio Offload sendet komprimierte Audiodaten direkt an den digitalen Signalprozessor (DSP) und hält die AP ab , so viel wie möglich. Zum Abspielen von Musikdateien bei ausgeschaltetem Bildschirm.
  • Komprimierte Audio-Pass - Through sendet komprimiertes Audio (speziell AC3 und E-AC3) direkt über HDMI an einen externen TV oder Audioempfänger, ohne es zu Decodierung auf dem Android TV - Gerät. Der Videoteil wird separat behandelt.
  • Multimedia - Tunneling sendet zusammen Audio- und Videodaten komprimiert. Wenn der codierte Stream von den Video- und Audiodecodern empfangen wird, kehrt er nicht zum Framework zurück. Idealerweise unterbricht der Stream den AP nicht.
  • Multimedia Pass - Through - Sends komprimierte Audio- und Videodaten zusammen von Tuner zu Video- und Audio - Decoder ohne den Rahmen beteiligt ist .
Multimedia-Tunneling-Flussdiagramm
Abbildung 1. Multimedia - Tunneling Strömungs

Ansatzvergleich

Reine Software Komprimiertes Audio-Offload Komprimiertes Audio-Passthrough Multimedia-Tunneling Multimedia-Passthrough
Standort decodieren AP DSP TV oder Audio-/Video-Receiver (AVR) Fernseher oder AVR Fernseher oder AVR
Verarbeitet Audio Jawohl Jawohl Jawohl Jawohl Nein
Behandelt Videos Jawohl Nein Nein Jawohl Nein

Für App-Entwickler

Erstellen Sie eine SurfaceView Instanz, erhält eine Audio - Session - ID, erstellen Sie dann den AudioTrack und MediaCodec Fälle das notwendige Timing und Konfigurationen für die Wiedergabe und Video - Frame - Decodierung zur Verfügung zu stellen.

Für Android 11 oder höher, als Stellvertreter für die Audio-Session - ID kann die App des HW - Sync - ID von Tuner erhalten und es bieten AudioTrack und MediaCodec Instanzen für A / V - Sync.

A/V-Synchronisierung

Im Multimedia-Tunneling-Modus werden Audio und Video auf einer Hauptuhr synchronisiert.

  • Für Android 11 oder höher könnte PCR oder STC von Tuner die Hauptuhr für die A/V-Synchronisierung sein.
  • Für Android 10 oder niedriger ist die Audiouhr die Hauptuhr für die A/V-Wiedergabe.

Wenn getunnelt Video MediaCodec Instanz und AudioTrack - Instanzen zum verknüpft sind HW_AV_SYNC weise in AudioTrack , der implizite Takt von abgeleiteten HW_AV_SYNC einschränkt , wenn jeder Videorahmen und Audioprobe präsentiert wird, auf der Grundlage der tatsächlichen Audio - oder Video - Rahmen - Darstellungs - Zeitstempel (PTS).

API-Aufrufablauf

Für Android 11 oder höher kann der Client die HW-Sync-ID von Tuner verwenden.

  1. Erstellen Sie eine SurfaceView Instanz.
    SurfaceView sv = new SurfaceView(mContext);
  2. Rufen Sie eine Audiositzungs-ID ab. Diese eindeutige ID wird bei der Erstellung der Audiospur (verwendet AudioTrack ). Es ist an die Medien weitergegeben Codec ( MediaCodec ) und von den Medien Rahmen verwendet , um die Audio- und Video - Pfade zu verknüpfen.
    AudioManager am = mContext.getSystemService(AUDIO_SERVICE);
    int audioSessionId = am.generateAudioSessionId()
    // or, for Android 11 or higher
    int avSyncId = tuner.getAvSyncHwId();
  3. Erstellen Sie AudioTrack mit HW A / V Sync AudioAttributes .

    Die Audio - Policy Manager fragt die Hardware - Abstraktionsschicht (HAL) für ein Gerät , das Ausgangs unterstützt FLAG_HW_AV_SYNC und erzeugt eine Audiospur direkt ohne Zwischenmischer an diesen Ausgang angeschlossen.

    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. Erstellen Sie ein Video MediaCodec Instanz und konfigurieren Sie es für getunnelte Video - Wiedergabe.
    // 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. Decodieren Sie die Videoframes.
    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;

Hinweis: Sie können die Reihenfolge der Schritte 3 und 4 in diesem Prozess wechseln, wie unten in den beiden Figuren zu sehen.

Diagramm der Audiospur, die vor der Codec-Konfiguration erstellt wurde
Abbildung 2. Audiospur vor Codec configure erstellt
Diagramm der nach der Codec-Konfiguration erstellten Audiospur
Abbildung 3. Audiospur nach Codec configure erstellt

Für Gerätehersteller

OEMs sollten einen separaten Videodecoder erstellen OpenMAX IL (OMX) Komponente zu unterstützen getunnelt Video - Wiedergabe. Diese OMX - Komponente muss werben , dass sie von getunnelt Wiedergabe (in der Lage ist media_codecs.xml ).

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

Die Komponente muss auch eine OMX unterstützen erweiterten Parameter OMX.google.android.index.configureVideoTunnelMode , die eine verwendet ConfigureVideoTunnelModeParams Struktur.

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

Wenn eine getunnelt MediaCodec Erzeugungsanforderung gemacht wird, konfiguriert der Rahmen , um die Komponente in OMX getunnelt Modus (durch Einstellen bTunneled zu OMX_TRUE ) und übergibt das zugehörige Audio - Ausgabegerät mit einem erstellt AUDIO_HW_AV_SYNC Flag an die Komponente OMX (in nAudioHwSync ).

Wenn die Komponente unterstützt diese Konfiguration, sollte es einen Seitenband - Handle zu diesem Codec zuzuteilen und geben es durch die Hinter pSidebandWindow Mitglied. Ein Seitenband - Griff ist ein ID - Tag für die getunnelte Schicht , die das ermöglicht Hardware Composer (HW Komponist) identifizieren sie. Wenn die Komponente nicht diese Konfiguration nicht unterstützt, sollte es eingestellt bTunneled zu OMX_FALSE .

Das Framework ruft die von der OMX-Komponente zugewiesene getunnelte Schicht (das Seitenband-Handle) ab und übergibt sie an den HW Composer. Diese Schicht ist compositionType wird eingestellt HWC_SIDEBAND . (Siehe hardware/libhardware/include/hardware/hwcomposer.h .)

Der HW Composer ist dafür verantwortlich, zum richtigen Zeitpunkt neue Bildpuffer aus dem Stream zu empfangen (z. B. synchronisiert mit dem zugehörigen Audioausgabegerät), diese mit den aktuellen Inhalten anderer Ebenen zusammenzusetzen und das resultierende Bild anzuzeigen. Dies geschieht unabhängig vom normalen Vorbereitungs-/Einstellzyklus. Die Vorbereitungs-/Set-Aufrufe erfolgen nur, wenn sich andere Layer ändern oder wenn sich Eigenschaften des Seitenband-Layers (wie Position oder Größe) ändern.

Aufbau

Frameworks/av/services/audioflinger/AudioFlinger.cpp

Die HAL gibt die HW_AV_SYNC ID als Zeichenkette dezimale Darstellung einer 64-Bit - Ganzzahl. (Siehe 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

Der Audio - Rahmen muss den HAL Ausgabestrom zu dem diese Session - ID entspricht, und Abfrage der HAL für den findet hwAVSyncId mit set_parameters .

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

Konfiguration des OMX-Decoders

MediaCodec.java

Der Audiorahmen findet den entsprechenden HAL Ausgangsstrom für diese Session ID und ruft die audio-hw-sync - ID durch die HAL für die Abfrage von AUDIO_PARAMETER_STREAM_HW_AV_SYNC Flag verwendet 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);
}

// ...

Diese HW - Sync - ID wird an die OMX geführt getunnelt Videodecoder des benutzerdefinierten Parameter verwendet OMX.google.android.index.configureVideoTunnelMode .

ACodec.cpp

Nachdem Sie die Audio-Hardware-Synchronisierungs-ID erhalten haben, verwendet ACodec sie, um den getunnelten Videodecoder zu konfigurieren, damit der getunnelte Videodecoder weiß, welche Audiospur synchronisiert werden soll.

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

Die OMX Komponente wird durch die konfigurierte configureVideoTunnelMode oben beschriebene Methode.

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

Nachdem die OMX-Komponente im Tunnelmodus konfiguriert wurde, wird das Seitenband-Handle der Rendering-Oberfläche zugeordnet.

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

Dann wird der Hinweis zur maximalen Auflösung, falls vorhanden, an die Komponente gesendet.

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

Unterstützung pausieren

Android 5.0 und niedriger bieten keine Unterstützung für Pausen. Sie können die getunnelte Wiedergabe nur durch A/V-Aushungerung anhalten, aber wenn der interne Puffer für Video groß ist (z.

In Android 5.1 und höher unterstützt AudioFlinger das Anhalten und Fortsetzen für direkte (getunnelte) Audioausgaben. Wenn die HAL eine Pause/Wiederaufnahme implementiert, wird eine Spurpause/Wiederaufnahme an die HAL weitergeleitet.

Die Aufrufsequenz Pause, Flush, Resume wird respektiert, indem die HAL-Aufrufe im Playback-Thread ausgeführt werden (wie beim Offload).

Codec2-Unterstützung

Für Android 11 oder höher unterstützt Codec2 getunnelte Wiedergabe.

CCodec.cpp

Um die getunnelte Wiedergabe zu unterstützen, funktioniert Codec2 ähnlich wie OMX. Um die HW-Sync-ID von Tuner zu unterstützen, sucht Codec2 nach der Sync-ID aus den folgenden Quellen.

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

Umsetzungsvorschläge

Audio-HAL

Für Android 11 kann die HW-Synchronisierungs-ID von PCR oder STC für die A/V-Synchronisierung verwendet werden, sodass nur Videostreams unterstützt werden.

Für Android 10 oder niedrigen Stützvorrichtungen Video - Wiedergabe getunnelt sollten mit den Flaggen mindestens ein Audio-Ausgabe - Stream Profil haben FLAG_HW_AV_SYNC und AUDIO_OUTPUT_FLAG_DIRECT in seiner audio_policy.conf Datei. Diese Flags werden verwendet, um die Systemuhr anhand der Audiouhr einzustellen.

OMX

Gerätehersteller sollten eine separate OMX-Komponente für die getunnelte Videowiedergabe haben. Hersteller können zusätzliche OMX-Komponenten für andere Arten der Audio- und Videowiedergabe anbieten, z. B. sichere Wiedergabe.

Diese Komponente sollte angeben , 0 - Puffer ( nBufferCountMin , nBufferCountActual ) an seinem Ausgangsport.

Die getunnelt Komponente muss auch die Umsetzung OMX.google.android.index.prepareForAdaptivePlayback setParameter Erweiterung.

Die getunnelt Komponente muss seine Fähigkeiten in der angeben media_codecs.xml - Datei und die getunnelt Wiedergabe - Funktion deklarieren. Es sollte auch alle Einschränkungen der Framegröße, Ausrichtung oder Bitrate klären.


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

Wenn dieselbe OMX-Komponente verwendet wird, um getunnelte und nicht getunnelte Dekodierung zu unterstützen, sollte die getunnelte Wiedergabefunktion als nicht erforderlich belassen werden. Sowohl getunnelte als auch nicht getunnelte Decoder haben dann die gleichen Leistungsbeschränkungen.

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

HW Composer

Wenn es gibt eine getunnelte Schicht (eine Schicht mit HWC_SIDEBAND compositionType ) auf einer Anzeige, die Ebene des sidebandStream ist das Seitenband Griff durch die Videokomponente OMX zugeordnet.

Video - Frames decodiert (aus dem getunnelten OMX - Komponente) zu der zugehörigen Audiospur die HW Composer synchronisiert (mit der audio-hw-sync - ID). Wenn ein neues Videobild aktuell wird, setzt der HW Composer es mit den aktuellen Inhalten aller Ebenen zusammen, die während des letzten Vorbereitungs-/Einstellaufrufs empfangen wurden, und zeigt das resultierende Bild an. Die Prepare/Set-Aufrufe erfolgen nur, wenn sich andere Layer ändern oder wenn sich Eigenschaften des Sideband-Layers (wie Position oder Größe) ändern.

Abbildung 4 stellt den HW Composer dar, der mit dem HW- (oder Kernel/Treiber-)Synchronizer arbeitet, um Videoframes (7b) mit der neuesten Komposition (7a) für die Anzeige zum richtigen Zeitpunkt basierend auf Audio (7c) zu kombinieren.

Diagramm des Hardware-Composers, der Videoframes basierend auf dem Audio kombiniert
Abbildung 4. HW Composer arbeitet mit der HW (oder kernel / Treiber) Synchronisierer