Multimedia-Tunneling

Multimedia-Tunneling ermöglicht komprimierte Videodaten für das Tunneling durch eine Hardware Videodecoder direkt an ein Display übertragen, ohne durch App-Code oder Android-Framework-Code Der gerätespezifische Code unterhalb des Android-Stacks bestimmt, welche Videoframes wann an den Bildschirm gesendet werden, Vergleichen der Zeitstempel der Präsentation des Videoframes mit einem der folgenden Werte Arten der internen Uhr:

  • Für die On-Demand-Videowiedergabe unter Android 5 oder höher muss AudioTrack Mit Zeitstempeln der Audiopräsentation synchronisierte Uhr Bestanden durch die App

  • Für die Wiedergabe von Liveübertragungen unter Android 11 oder höher eine Programmreferenzuhr oder Systemzeituhr (STC), die von einem Tuner

Hintergrund

Herkömmliche Videowiedergabe auf Android benachrichtigt wenn ein komprimierter Videoframe decodiert wurde. Die App Releases Der decodierte Videoframe wird zur Anzeige übertragen, damit er mit derselben Systemuhr gerendert wird. als entsprechenden Audioframe, Bisherige Daten abrufen AudioTimestamps um das richtige Timing zu berechnen.

Da bei der getunnelten Videowiedergabe der App-Code umgangen und die Anzahl der Prozesse, die auf das Video reagieren, können ein effizienteres je nach OEM-Implementierung. Außerdem können Sie damit genauere Videos liefern, Kadenz und Synchronisierung mit der ausgewählten Uhr (PRC, STC oder Audio), indem die Zeitprobleme, die durch eine potenzielle Verzerrung zwischen dem Timing von Android zum Rendern von Videos sowie das Timing echter vSync-Hardware. Sie können jedoch Tunneling kann auch die Unterstützung von GPU-Effekten wie Unkenntlich machen abgerundete Ecken in Bild-im-Bild-Fenstern (BiB), da der Zwischenspeicher den Android-Grafikstapel umgehen.

Das folgende Diagramm zeigt, wie Tunneling die Videowiedergabe vereinfacht.

Vergleich von traditionellen und Tunnelmodi

Abbildung 1: Vergleich zwischen herkömmlicher und getunnelter Videowiedergabe

Für App-Entwickler

Die meisten App-Entwickler integrieren müssen Sie in den meisten Fällen lediglich für die getunnelte Wiedergabe. Zur Low-Level-Implementierung eines getunnelten Videos Player verwenden, folgen Sie der Anleitung unten.

On-Demand-Videowiedergabe unter Android 5 oder höher:

  1. Erstellen Sie eine SurfaceView-Instanz.

  2. Erstellen Sie eine audioSessionId-Instanz.

  3. Instanzen AudioTrack und MediaCodec mit dem audioSessionId erstellen die in Schritt 2 erstellt wurde.

  4. Audiodaten mit dem Präsentationszeitstempel für AudioTrack in die Warteschlange stellen: ersten Audioframe in den Audiodaten.

Für die Wiedergabe von Liveübertragungen unter Android 11 oder höher:

  1. Erstellen Sie eine SurfaceView-Instanz.

  2. Rufen Sie eine avSyncHwId-Instanz aus Tuner ab.

  3. Instanzen AudioTrack und MediaCodec mit der Instanz avSyncHwId erstellen die in Schritt 2 erstellt wurden.

Der API-Aufruffluss wird in den folgenden Code-Snippets gezeigt:

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

Verhalten der On-Demand-Videowiedergabe

Weil die getunnelte On-Demand-Videowiedergabe implizit an AudioTrack gebunden ist kann das Verhalten der getunnelten Videowiedergabe der Audiowiedergabe.

  • Auf den meisten Geräten wird ein Videoframe standardmäßig erst gerendert, wenn Audio wiedergegeben wird die Wiedergabe beginnt. Die App muss jedoch möglicherweise einen Videoframe rendern, bevor Starten der Audiowiedergabe, z. B. um dem Nutzer das aktuelle Video zu zeigen Position beim Suchen angezeigt.

    • Um zu signalisieren, dass der erste Videoframe in der Warteschlange gerendert werden soll, sobald wenn es decodiert wird, PARAMETER_KEY_TUNNEL_PEEK auf 1 setzen. Wenn komprimierte Videoframes in der Warteschlange neu angeordnet werden (zum Beispiel wenn B-Frames vorhanden sind, bedeutet dies, dass der erste angezeigte Videoframe immer ein iFrame

    • Wenn Sie nicht möchten, dass der erste Frame aus der Warteschlange bis zum Wiedergabe beginnt, setze diesen Parameter auf 0.

    • Wenn dieser Parameter nicht festgelegt ist, bestimmt der OEM das Verhalten für das Gerät.

  • Wenn AudioTrack keine Audiodaten erhält und die Zwischenspeicher leer sind (Audiounterschreitung), die Videowiedergabe wird angehalten, bis weitere Audiodaten geschrieben wurden. weil die Audiouhr nicht mehr weiterläuft.

  • Während der Wiedergabe können Unterbrechungen, die durch die App nicht korrigiert werden können, in die Zeitstempel der Audiopräsentation. In diesem Fall korrigiert der OEM negative Lücken, indem der aktuelle Video-Frame verzögert wird, und positive Lücken, indem oder stille Audioframes einzufügen (je nach OEM Implementierung). Die Frame-Position AudioTimestamp erhöht sich für stille Audioframes eingefügt.

Für Gerätehersteller

Konfiguration

OEMs sollten einen separaten Videodecoder erstellen, um eine getunnelte Videowiedergabe zu unterstützen. Dieser Decoder sollte bekannt geben, dass er im media_codecs.xml-Datei:

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

Wenn eine getunnelte MediaCodec-Instanz mit einer Audiositzungs-ID konfiguriert ist, fragt AudioFlinger für diese HW_AV_SYNC-ID ab:

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

Bei dieser Abfrage AudioFlinger ruft die HW_AV_SYNC-ID ab vom primären Audiogerät erstellt und intern mit dem Sitzungs-ID:

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

Wenn bereits eine AudioTrack-Instanz erstellt wurde, lautet die HW_AV_SYNC-ID die mit derselben Audiositzungs-ID an den Ausgabestream übergeben werden. Falls noch nicht erstellt wurde, wird die HW_AV_SYNC-ID während AudioTrack-Erstellung. Dies geschieht durch die Wiedergabe Thread:

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

Die HW_AV_SYNC-ID, ob sie einem Audioausgabestream oder einem Tuner-Konfiguration an die OMX- oder Codec2-Komponente übergeben wird, Der OEM-Code kann den Codec mit dem entsprechenden Audioausgabestream oder den Tuner-Stream.

Während der Komponentenkonfiguration sollte die OMX- oder Codec2-Komponente eine Seitenband-Handle, mit dem der Codec mit einem Hardware-Composer verknüpft werden kann HWC-Ebene. Wenn die App eine Oberfläche mit MediaCodec verknüpft, wird dieses Seitenband Handle wird über SurfaceFlinger an HWC übergeben, wodurch die Ebene als sideband-Schicht.

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 ist für den Empfang neuer Bildpuffer aus der Codec-Ausgabe am entsprechende Zeit, die entweder mit dem zugehörigen Audioausgabestream synchronisiert ist oder der Programmreferenzuhr des Tuner-Programms, wobei die Puffer mit dem aktuellen anderer Layers und die Anzeige des resultierenden Bildes. Das passiert und zwar unabhängig vom normalen Vorbereitungs- und Einstellzyklus. Anrufe vorbereiten und einrichten treten nur auf, wenn sich andere Schichten ändern oder wenn die Eigenschaften der Seitenbandschicht wie Position oder Größe.

Logo: OMX

Eine Tunneled-Decoder-Komponente sollte Folgendes unterstützen:

  • Erweiterte OMX.google.android.index.configureVideoTunnelMode festlegen Parameter, der die ConfigureVideoTunnelModeParams-Struktur zur Übergabe in der HW_AV_SYNC-ID, die dem Audioausgabegerät zugeordnet ist.

  • Den Parameter OMX_IndexConfigAndroidTunnelPeek konfigurieren, der den Codec verwendet, um den ersten decodierten Videoframe unabhängig von ob die Audiowiedergabe gestartet wurde.

  • Das Ereignis OMX_EventOnFirstTunnelFrameReady wird gesendet, wenn die erste getunnelte Videoframe wurde decodiert und kann gerendert werden.

Die AOSP-Implementierung konfiguriert den Tunnelmodus in ACodec bis OMXNodeInstance Dies wird im folgenden Code-Snippet gezeigt:

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;

Wenn die Komponente diese Konfiguration unterstützt, sollte ein Seitenband zugewiesen werden. Handle an diesen Codec und übergib ihn durch das pSidebandWindow-Mitglied, damit die HWC den zugehörigen Codec identifizieren kann. Wenn die Komponente nicht diese Konfiguration unterstützen, sollte bTunneled auf OMX_FALSE festgelegt werden.

Codec2

Ab Android 11 unterstützt Codec2 die getunnelte Wiedergabe. Der Decoder -Komponente sollte Folgendes unterstützen:

  • Konfigurieren von C2PortTunneledModeTuning, um den Tunnelmodus und Karten/Tickets im HW_AV_SYNC, das entweder vom Audioausgabegerät oder vom die Tuner-Konfiguration.

  • Abfrage von C2_PARAMKEY_OUTPUT_TUNNEL_HANDLE zum Zuweisen und Abrufen des Seitenband-Handle für HWC.

  • Verarbeitung von C2_PARAMKEY_TUNNEL_HOLD_RENDER, wenn sie an eine C2Work angehängt ist, die weist den Codec an, den Abschluss der Arbeit zu decodieren und zu signalisieren, aber nicht zu rendern Ausgabepuffer, bis 1) der Codec später angewiesen wird, ihn zu rendern. oder 2) die Audiowiedergabe beginnt.

  • Verarbeitung von C2_PARAMKEY_TUNNEL_START_RENDER, die den Codec anweist, den mit der Kennzeichnung C2_PARAMKEY_TUNNEL_HOLD_RENDER, auch wenn die Audiowiedergabe nicht gestartet wurde.

  • debug.stagefright.ccodec_delayed_params nicht konfiguriert lassen (empfohlen). Wenn legen Sie für die Konfiguration false fest.

Die AOSP-Implementierung konfiguriert den Tunnelmodus in CCodec über C2PortTunnelModeTuning, wie im folgenden Code-Snippet gezeigt:

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

Wenn die Komponente diese Konfiguration unterstützt, sollte ein Seitenband zugewiesen werden. an diesen Codec übergeben und über C2PortTunnelHandlingTuning zurückgegeben, damit die HWC den zugehörigen Codec identifizieren kann.

Audio-HAL

Bei der On-Demand-Videowiedergabe empfängt der Audio-HAL die Audiopräsentation. Zeitstempel in Übereinstimmung mit den Audiodaten im Big-Endian-Format in einem Header, der gefunden wurde zu Beginn jedes Blocks an Audiodaten, die die App schreibt:

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

Damit HWC Videoframes synchron mit den entsprechenden Audioframes rendern kann, Audio-HAL sollte den Synchronisierungs-Header parsen und den Präsentationszeitstempel verwenden, um die Wiedergabeuhr mit dem Audiorendering neu zu synchronisieren. Um neu zu synchronisieren, wenn wenn komprimierte Audio-Inhalte wiedergegeben werden, muss der Audio-HAL unter Umständen Metadaten parsen. in den komprimierten Audiodaten, um die Wiedergabedauer zu bestimmen.

Support pausieren

Android 5 oder niedriger bietet keine Unterstützung für Pausen. Sie können „Tunnel“ pausieren Wiedergabe nur bei geringer Qualität, aber der interne Puffer für das Video ist groß z. B. wenn die OMX-Komponente eine Sekunde lang Daten enthält, wird eine Pause gemacht, reagiert nicht.

Ab Android 5.1 unterstützt AudioFlinger das Pausieren und Fortsetzen für den direkten Zugriff (getunnelte) Audioausgaben. Wenn der HAL „Pause“ und „Resume“ implementiert, wird „Pause“ verfolgt und Fortsetzen an den HAL weitergeleitet.

Die Aufrufsequenz zum Anhalten, Leeren und Fortsetzen wird durch Ausführen der HAL-Aufrufe berücksichtigt im Wiedergabe-Thread (wie Auslagerung).

Implementierungsvorschläge

Audio-HAL

Bei Android 11 kann die Hardwaresynchronisierungs-ID von PCR oder STC für die A/V-Synchronisierung verwendet werden. der reine Videostream unterstützt wird.

Für Geräte mit Android 10 oder niedriger, die die getunnelte Videowiedergabe unterstützen, sollten mindestens ein Audioausgabestreamprofil mit FLAG_HW_AV_SYNC und AUDIO_OUTPUT_FLAG_DIRECT-Flags in der zugehörigen audio_policy.conf-Datei. Diese Flags werden verwendet, um die Systemuhr von der Audiozeit aus einzustellen.

Logo: OMX

Gerätehersteller sollten eine separate OMX-Komponente für getunnelte Videos haben. Wiedergabe (Hersteller können zusätzliche OMX-Komponenten für andere Audio- und Videowiedergabe, z. B. sichere Wiedergabe). Tunneled-Komponente sollte:

  • Geben Sie 0 Zwischenspeicher (nBufferCountMin, nBufferCountActual) für die Ausgabe an Port.

  • Implementieren Sie die Erweiterung OMX.google.android.index.prepareForAdaptivePlayback setParameter.

  • Geben Sie die Funktionen in der Datei media_codecs.xml an und deklarieren Sie die getunnelte Wiedergabe. Außerdem sollten etwaige Einschränkungen bei Frames verdeutlicht werden. Größe, Ausrichtung oder Bitrate. Hier ein Beispiel:

    <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 für die Decodierung dieselbe OMX-Komponente und die nicht getunnelte Decodierung verwendet werden, sollte die getunnelte Wiedergabefunktion nicht erforderlich sein. Sowohl getunnelt als auch Decoder ohne Tunnel haben dann dieselben Funktionsbeschränkungen. Ein Beispiel: (siehe unten):

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

Hardware-Komponisten (HWC)

Wenn eine Tunnelebene vorhanden ist (eine Ebene mit HWC_SIDEBAND compositionType) ein Display ist, ist der sidebandStream der Ebene der vom OMX-Videokomponente.

Die HWC synchronisiert decodierte Videoframes (von der getunnelten OMX-Komponente) mit Den verknüpften Audiotrack (mit der audio-hw-sync-ID). Wenn ein neuer Videoframe aktualisiert wird, setzt die HWC sie mit den aktuellen Inhalten aller Ebenen zusammen. erhalten haben, und zeigt das resultierende Bild an. Die Aufrufe zum Vorbereiten oder Festlegen erfolgen nur, wenn sich andere Ebenen ändern oder wenn der Seitenband-Layer ändern, wie z. B. Position oder Größe.

Die folgende Abbildung zeigt die HWC, die mit der Hardware (oder dem Kernel oder Treiber)-Synchronisierer, um Videoframes (7b) mit der neuesten Komposition (7a) für die Anzeige zum richtigen Zeitpunkt, basierend auf den Audiodaten (7c).

HWC kombiniert Videoframes basierend auf Audio

Abbildung 2: HWC-Hardware- (oder Kernel- oder Treiber-)Synchronisierer