Tunnelisation multimédia

L'encapsulation multimédia permet d'acheminer des données vidéo compressées via un tunnel un décodeur vidéo directement sur un écran, sans être traité par le code de l'application ni Code du framework Android. Le code spécifique à l'appareil sous la pile Android détermine quelles images vidéo envoyer à l'écran et à quel moment en comparant les codes temporels de la présentation des images vidéo avec l'un des codes suivants : types d'horloge interne:

  • Pour la lecture de vidéos à la demande sous Android 5 ou version ultérieure, AudioTrack horloge synchronisée avec les codes temporels de la présentation audio réussi par l'application

  • Pour la diffusion en direct sous Android 11 ou version ultérieure, une horloge de référence de programme (PCR) ou horloge système (STC) pilotée par tuner

Arrière-plan

La fonctionnalité de lecture vidéo traditionnelle sur Android informe l'application lorsqu'une image vidéo compressée a été décodée. L'application versions l'image vidéo décodée à l'écran pour qu'elle soit restituée avec la même horloge système comme la trame audio correspondante, récupérer l'historique AudioTimestamps Compute Engine pour calculer le délai correct.

La lecture par tunnel contourne le code de l'application et réduit le nombre agissant sur la vidéo, il peut fournir un rendu vidéo plus efficace. selon l'implémentation OEM. Il peut aussi fournir des images la cadence et la synchronisation avec l'horloge choisie (PRC, STC ou audio) en évitant problèmes de synchronisation introduits par un décalage potentiel entre le moment où Android les requêtes de rendu vidéo et la chronologie des véritables vsyncs matérielles. Toutefois, la tunnelisation peut également réduire la prise en charge des effets GPU tels que floutage ou arrondis dans les fenêtres Picture-in-picture (PIP), la pile graphique Android.

Le schéma suivant montre comment la tunnelisation simplifie le processus de lecture vidéo.

comparaison entre le mode traditionnel et le mode tunnel

Figure 1 : Comparaison entre les processus de lecture vidéo traditionnels et les processus de lecture par tunnel

Pour les développeurs d'applications

Comme la plupart des développeurs d'applications intègrent une bibliothèque pour la lecture Dans la plupart des cas, il suffit de reconfigurer pour la lecture par tunnel. Pour l'implémentation de bas niveau d'une vidéo acheminée par tunnel suivez les instructions ci-dessous.

Pour lire des vidéos à la demande sous Android 5 ou version ultérieure:

  1. Créez une instance SurfaceView.

  2. Créez une instance audioSessionId.

  3. Créer des instances AudioTrack et MediaCodec avec audioSessionId que vous avez créée à l'étape 2.

  4. Mettez en file d'attente les données audio dans AudioTrack avec le code temporel de présentation de première trame audio dans les données audio.

Pour une diffusion en direct sous Android 11 ou version ultérieure:

  1. Créez une instance SurfaceView.

  2. Obtenez une instance avSyncHwId à partir de Tuner.

  3. Créer des instances AudioTrack et MediaCodec avec l'instance avSyncHwId créé à l'étape 2.

Le flux d'appel d'API est illustré dans les extraits de code suivants:

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

Comportement lors de la lecture de vidéos à la demande

Parce que la lecture des vidéos à la demande via un tunnel est associée implicitement à AudioTrack la lecture en tunnel peut dépendre du comportement de lecture audio.

  • Sur la plupart des appareils, par défaut, l'affichage d'une image vidéo n'est effectué que lorsque le son la lecture commence. Toutefois, il se peut que l'application doive rendre une image vidéo avant Le démarrage de la lecture audio, par exemple, pour montrer à l'utilisateur lors de la recherche.

    • Pour signaler que la première image vidéo en file d'attente doit être affichée dès que si elle est décodée, définissez PARAMETER_KEY_TUNNEL_PEEK sur 1. Lorsque les images vidéo compressées sont réorganisées dans la file d'attente (par exemple, Cadres en B sont présentes), cela signifie que la première image vidéo affichée doit toujours être iFrame.

    • Si vous ne voulez pas que la première image vidéo mise en file d'attente soit affichée avant que le son la lecture commence. Définissez ce paramètre sur 0.

    • Si ce paramètre n'est pas défini, l'OEM détermine le comportement de l'appareil.

  • Lorsque les données audio ne sont pas fournies à AudioTrack et que les tampons sont vides (sous-utilisation audio), la lecture de la vidéo se bloque jusqu'à ce que d'autres données audio soient écrites car l'horloge audio n'avance plus.

  • Lors de la lecture, des discontinuités pour lesquelles l'application ne peut pas se corriger peuvent apparaître des horodatages de présentation audio. Dans ce cas, l'OEM corrige les erreurs des blancs en bloquant l'image vidéo actuelle, et des intervalles positifs en retirant d'images vidéo ou d'insertion d'images audio silencieuses (selon l'OEM) mise en œuvre). La position du cadre AudioTimestamp n'augmente pas pour inséré des trames audio silencieuses.

Pour les fabricants d'appareils

Configuration

Les OEM doivent créer un décodeur vidéo distinct pour permettre la lecture vidéo par tunnel. Ce décodeur doit annoncer qu'il est compatible avec la lecture par tunnel Fichier media_codecs.xml:

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

Lorsqu'une instance MediaCodec acheminée par tunnel est configurée avec un ID de session audio, elle interroge AudioFlinger pour cet 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);
}

Au cours de cette requête, AudioFlinger récupère l'ID HW_AV_SYNC. depuis l'appareil audio principal et l'associe en interne à l'audio ID de session:

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

Si une instance AudioTrack a déjà été créée, l'ID HW_AV_SYNC est transmis au flux de sortie avec le même ID de session audio. Si elle n'a pas été déjà créé, l'ID HW_AV_SYNC est transmis au flux de sortie lors Création de AudioTrack. Cela se fait par la lecture fil de discussion:

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

L'ID HW_AV_SYNC, qu'il corresponde à un flux de sortie audio ou à un Tuner est transmise au composant OMX ou Codec2, de sorte que Le code OEM peut associer le codec au flux de sortie audio correspondant ou le flux du tuner.

Lors de la configuration des composants, le composant OMX ou Codec2 doit renvoyer une poignée de bande latérale pouvant être utilisée pour associer le codec à un Composer de matériel (HWC). Lorsque l'application associe une surface à MediaCodec, ce bande latérale est transmis au matériel via SurfaceFlinger, qui configure le en tant que bande latérale.

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

Le matériel est chargé de recevoir les nouveaux tampons d'image de la sortie du codec au niveau l'heure appropriée, soit synchronisée avec le flux de sortie audio associé, soit l'horloge de référence du programme tuner, qui compose les tampons avec les valeurs contenu d'autres calques et afficher l'image obtenue. Cela se produit indépendamment du cycle normal de préparation et de redressement. Appels de préparation et de définition se produire uniquement lorsque d'autres couches changent ou lorsque les propriétés de la couche de bande latérale (position ou taille, par exemple).

OMX

Un composant de décodeur acheminé par tunnel doit prendre en charge les éléments suivants:

  • Définir l'extension OMX.google.android.index.configureVideoTunnelMode , qui utilise la structure ConfigureVideoTunnelModeParams pour transmettre dans l'ID HW_AV_SYNC associé au périphérique de sortie audio.

  • Configurer le paramètre OMX_IndexConfigAndroidTunnelPeek qui indique au pour afficher ou non la première image vidéo décodée, quel que soit si la lecture audio a démarré.

  • Envoyer l'événement OMX_EventOnFirstTunnelFrameReady lorsque le premier tunnel a été acheminé l'image vidéo a été décodée et est prête à être affichée.

L'implémentation d'AOSP configure le mode tunnel dans ACodec à OMXNodeInstance comme indiqué dans l'extrait de code suivant:

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;

Si le composant est compatible avec cette configuration, il doit allouer une bande latérale à ce codec et le transmettre via le membre pSidebandWindow pour que que le HWC puisse identifier le codec associé. Si le composant n'est pas acceptent cette configuration, bTunneled doit être défini sur OMX_FALSE.

Codec2

Sous Android 11 ou version ultérieure, Codec2 est compatible avec la lecture par tunnel. Le décodeur doit être compatible avec les éléments suivants:

  • Configurer C2PortTunneledModeTuning, qui configure le mode tunnel et transmet le HW_AV_SYNC récupéré depuis le périphérique de sortie audio ou la configuration du tuner.

  • en interrogeant C2_PARAMKEY_OUTPUT_TUNNEL_HANDLE pour allouer et récupérer les bande latérale pour matériel.

  • Gestion de C2_PARAMKEY_TUNNEL_HOLD_RENDER lorsqu'il est associé à un C2Work, qui demande au codec de décoder et de signaler la fin du travail, mais pas de l'afficher dans le tampon de sortie jusqu'à ce que 1) le codec soit invité à le rendre plus tard ou 2) la lecture audio commence.

  • Gérer C2_PARAMKEY_TUNNEL_START_RENDER, qui indique au codec affiche immédiatement le cadre qui a été marqué avec C2_PARAMKEY_TUNNEL_HOLD_RENDER, même si la lecture audio n'a pas commencé.

  • Laissez debug.stagefright.ccodec_delayed_params non configuré (recommandé). Si configurez-la, définissez-la sur false.

L'implémentation d'AOSP configure le mode tunnel dans CCodec via C2PortTunnelModeTuning, comme indiqué dans l'extrait de code suivant:

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

Si le composant est compatible avec cette configuration, il doit allouer une bande latérale gérer à ce codec et le transmettre via C2PortTunnelHandlingTuning afin de que le HWC puisse identifier le codec associé.

HAL audio

Pour la lecture de la vidéo à la demande, le HAL audio reçoit la présentation audio codes temporels intégrés aux données audio au format big-endian à l'intérieur d'un en-tête trouvé au début de chaque bloc de données audio que l'application écrit:

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

Pour que HWC effectue le rendu des images vidéo synchronisées avec les images audio correspondantes, le L'HAL audio doit analyser l'en-tête de synchronisation et utiliser le code temporel de la présentation pour resynchroniser l'horloge de lecture avec le rendu audio. Pour les resynchroniser lorsque audio compressé en cours de lecture, le HAL audio peut avoir besoin d'analyser les métadonnées dans les données audio compressées pour déterminer sa durée de lecture.

Suspendre l'assistance

Android 5 ou version antérieure n'est pas compatible avec la suspension. Vous pouvez suspendre la tunnelisation uniquement par manque d'audiovisuel, mais si la mémoire tampon interne de la vidéo est volumineuse (par exemple, le composant OMX contient une seconde de données), semble ne pas répondre.

Sur Android 5.1 ou version ultérieure, AudioFlinger permet de suspendre et de reprendre la lecture en direct les sorties audio (tunnelisées). Si le HAL implémente la pause et la reprise, suivre la pause et votre CV sont transmis au HAL.

La séquence d'appels de mise en pause, de vidage et de reprise est respectée par l'exécution des appels HAL. dans le thread de lecture (comme pour "offload").

Suggestions d'implémentation

HAL audio

Pour Android 11, l'ID de synchronisation matérielle PCR ou STC peut être utilisé pour la synchronisation A/V. le flux vidéo uniquement est pris en charge.

Pour Android 10 ou version antérieure, les appareils compatibles avec la lecture vidéo par tunnel doivent au moins un profil de flux de sortie audio avec FLAG_HW_AV_SYNC et options AUDIO_OUTPUT_FLAG_DIRECT dans son fichier audio_policy.conf. Ces indicateurs sont utilisés pour définir l'horloge système à partir de l'horloge audio.

OMX

Les fabricants d'appareils doivent disposer d'un composant OMX distinct pour les vidéos acheminées par tunnel. (les fabricants peuvent proposer des composants OMX supplémentaires pour d'autres types audio et vidéo, comme la lecture sécurisée). Composant acheminé par tunnel doit:

  • Spécifiez 0 tampon (nBufferCountMin, nBufferCountActual) sur sa sortie .

  • Implémentez l'extension OMX.google.android.index.prepareForAdaptivePlayback setParameter.

  • Spécifiez ses capacités dans le fichier media_codecs.xml et déclarez la fonctionnalité de lecture par tunnel. Cela devrait également clarifier les limites de cadre la taille, l'alignement ou le débit. Voici un exemple :

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

Si le même composant OMX est utilisé pour prendre en charge le décodage par tunnel et non tunnel, il doit laisser la fonctionnalité de lecture par tunnel comme non requise. À la fois acheminés par tunnel et les décodeurs non tunnelisés ont alors les mêmes limites de capacité. Par exemple, comme indiqué ci-dessous:

<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 Composer (HWC)

En cas de tunnelisation (couche avec HWC_SIDEBAND compositionType) un affichage, le sidebandStream de la couche est le handle de bande latérale alloué par le Composant vidéo OMX.

Le matériel synchronise les images vidéo décodées (à partir du composant OMX acheminé par tunnel) vers La piste audio associée (avec l'ID audio-hw-sync) Lorsqu'une nouvelle image vidéo devient actuel, le HWC la compose avec le contenu actuel de toutes les couches reçu lors du dernier appel de préparation ou de définition, et affiche l'image résultante. Les appels de préparation ou de définition se produisent uniquement lorsque d'autres couches changent, ou lorsque les propriétés de la couche de bande latérale (comme la position ou la taille) changent.

La figure suivante représente le HWC qui fonctionne avec le matériel (ou le noyau ou pilote), pour combiner les images vidéo (7b) avec la dernière composition (7a) pour l'affichage au bon moment, en fonction de l'audio (7c).

HWC combinant des images vidéo en fonction de l&#39;audio

Figure 2. Synchronisateur de matériel HWC (ou noyau ou pilote)