tunelización multimedia

El túnel multimedia permite que los datos de vídeo comprimidos pasen a través de un decodificador de vídeo de hardware directamente a una pantalla, sin ser procesados ​​por el código de la aplicación o el código del marco de Android. El código específico del dispositivo debajo de la pila de Android determina qué fotogramas de vídeo enviar a la pantalla y cuándo enviarlos comparando las marcas de tiempo de presentación de los fotogramas de vídeo con uno de los siguientes tipos de reloj interno:

  • Para la reproducción de video bajo demanda en Android 5 o superior, un reloj AudioTrack sincronizado con las marcas de tiempo de presentación de audio transmitidas por la aplicación

  • Para la reproducción de transmisiones en vivo en Android 11 o superior, un reloj de referencia de programa (PCR) o un reloj de tiempo del sistema (STC) controlado por un sintonizador

Fondo

La reproducción de vídeo tradicional en Android notifica a la aplicación cuando se ha decodificado un fotograma de vídeo comprimido. Luego, la aplicación libera el cuadro de video decodificado en la pantalla para que se procese a la misma hora del reloj del sistema que el cuadro de audio correspondiente, recuperando instancias históricas AudioTimestamps para calcular el tiempo correcto.

Debido a que la reproducción de video en túnel omite el código de la aplicación y reduce la cantidad de procesos que actúan sobre el video, puede proporcionar una representación de video más eficiente según la implementación del OEM. También puede proporcionar una cadencia de video y sincronización más precisas con el reloj elegido (PRC, STC o audio) al evitar problemas de sincronización introducidos por una posible desviación entre la sincronización de las solicitudes de Android para renderizar video y la sincronización de vsyncs de hardware reales. Sin embargo, el túnel también puede reducir la compatibilidad con efectos de GPU, como desenfoque o esquinas redondeadas en ventanas de imagen en imagen (PiP), porque los buffers omiten la pila de gráficos de Android.

El siguiente diagrama muestra cómo la tunelización simplifica el proceso de reproducción de vídeo.

Comparación de los modos tradicional y túnel.

Figura 1. Comparación de los procesos de reproducción de vídeo tradicional y tunelizado

Para desarrolladores de aplicaciones

Debido a que la mayoría de los desarrolladores de aplicaciones se integran con una biblioteca para la implementación de la reproducción, en la mayoría de los casos la implementación solo requiere reconfigurar esa biblioteca para la reproducción en túnel. Para la implementación de bajo nivel de un reproductor de video tunelizado, utilice las siguientes instrucciones.

Para reproducción de video bajo demanda en Android 5 o superior:

  1. Cree una instancia SurfaceView .

  2. Cree una instancia audioSessionId .

  3. Cree instancias AudioTrack y MediaCodec con la instancia audioSessionId creada en el paso 2.

  4. Ponga en cola los datos de audio en AudioTrack con la marca de tiempo de presentación para el primer cuadro de audio en los datos de audio.

Para reproducción de transmisiones en vivo en Android 11 o superior:

  1. Cree una instancia SurfaceView .

  2. Obtenga una instancia avSyncHwId de Tuner .

  3. Cree instancias AudioTrack y MediaCodec con la instancia avSyncHwId creada en el paso 2.

El flujo de llamadas API se muestra en los siguientes fragmentos de código:

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

Comportamiento de la reproducción de vídeo bajo demanda

Debido a que la reproducción de vídeo bajo demanda mediante túnel está vinculada implícitamente a la reproducción AudioTrack , el comportamiento de la reproducción de vídeo mediante túnel puede depender del comportamiento de la reproducción de audio.

  • En la mayoría de los dispositivos, de forma predeterminada, un fotograma de vídeo no se procesa hasta que comienza la reproducción de audio. Sin embargo, es posible que la aplicación necesite renderizar un cuadro de video antes de iniciar la reproducción de audio, por ejemplo, para mostrar al usuario la posición actual del video mientras busca.

    • Para indicar que el primer fotograma de vídeo en cola debe procesarse tan pronto como se decodifique, establezca el parámetro PARAMETER_KEY_TUNNEL_PEEK en 1 . Cuando los fotogramas de vídeo comprimidos se reordenan en la cola (como cuando hay fotogramas B ), esto significa que el primer fotograma de vídeo mostrado siempre debe ser un fotograma I.

    • Si no desea que se procese el primer fotograma de vídeo en cola hasta que comience la reproducción de audio, establezca este parámetro en 0 .

    • Si este parámetro no está configurado, el OEM determina el comportamiento del dispositivo.

  • Cuando no se proporcionan datos de audio a AudioTrack y los buffers están vacíos (audio insuficiente), la reproducción de video se detiene hasta que se escriben más datos de audio porque el reloj de audio ya no avanza.

  • Durante la reproducción, es posible que aparezcan discontinuidades que la aplicación no puede corregir en las marcas de tiempo de la presentación de audio. Cuando esto sucede, el OEM corrige las brechas negativas deteniendo el cuadro de video actual y las brechas positivas eliminando cuadros de video o insertando cuadros de audio silenciosos (según la implementación del OEM). La posición del fotograma AudioTimestamp no aumenta para los fotogramas de audio silenciosos insertados.

Para fabricantes de dispositivos

Configuración

Los OEM deben crear un descodificador de vídeo independiente para admitir la reproducción de vídeo en túnel. Este decodificador debe anunciar que es capaz de realizar reproducción en túnel en el archivo media_codecs.xml :

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

Cuando una instancia MediaCodec tunelizada se configura con una ID de sesión de audio, consulta AudioFlinger para obtener esta 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 esta consulta, AudioFlinger recupera el ID HW_AV_SYNC del dispositivo de audio principal y lo asocia internamente con el ID de la sesión de audio:

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 ya se ha creado una instancia AudioTrack , el ID HW_AV_SYNC se pasa al flujo de salida con el mismo ID de sesión de audio. Si aún no se ha creado, el ID HW_AV_SYNC se pasa al flujo de salida durante la creación AudioTrack . Esto lo hace el hilo de reproducción :

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

El ID HW_AV_SYNC , ya sea que corresponda a un flujo de salida de audio o una configuración Tuner , se pasa al componente OMX o Codec2 para que el código OEM pueda asociar el códec con el flujo de salida de audio o el flujo de sintonizador correspondiente.

Durante la configuración del componente, el componente OMX o Codec2 debe devolver un identificador de banda lateral que se puede usar para asociar el códec con una capa de Hardware Composer (HWC). Cuando la aplicación asocia una superficie con MediaCodec , este identificador de banda lateral se pasa a HWC a través de SurfaceFlinger , que configura la capa como una capa de banda lateral .

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 es responsable de recibir nuevos buffers de imágenes de la salida del códec en el momento apropiado, ya sea sincronizados con el flujo de salida de audio asociado o el reloj de referencia del programa sintonizador, componiendo los buffers con el contenido actual de otras capas y mostrando la imagen resultante. Esto sucede independientemente del ciclo normal de preparación y configuración. Las llamadas de preparación y configuración ocurren solo cuando otras capas cambian o cuando cambian las propiedades de la capa de banda lateral (como la posición o el tamaño).

OMX

Un componente decodificador tunelizado debe admitir lo siguiente:

  • Configurar el parámetro extendido OMX.google.android.index.configureVideoTunnelMode , que utiliza la estructura ConfigureVideoTunnelModeParams para pasar el ID HW_AV_SYNC asociado con el dispositivo de salida de audio.

  • Configurar el parámetro OMX_IndexConfigAndroidTunnelPeek que le indica al códec que renderice o no el primer fotograma de vídeo decodificado, independientemente de si se ha iniciado la reproducción de audio.

  • Envío del evento OMX_EventOnFirstTunnelFrameReady cuando el primer fotograma de vídeo tunelizado se ha decodificado y está listo para renderizarse.

La implementación de AOSP configura el modo túnel en ACodec a través de OMXNodeInstance como se muestra en el siguiente fragmento de código:

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 el componente admite esta configuración, debe asignar un identificador de banda lateral a este códec y pasarlo nuevamente a través del miembro pSidebandWindow para que el HWC pueda identificar el códec asociado. Si el componente no admite esta configuración, debe configurar bTunneled en OMX_FALSE .

Códec2

En Android 11 o superior, Codec2 admite la reproducción en túnel. El componente decodificador debe admitir lo siguiente:

  • Configurar C2PortTunneledModeTuning , que configura el modo túnel y pasa el HW_AV_SYNC recuperado del dispositivo de salida de audio o de la configuración del sintonizador.

  • Consultando C2_PARAMKEY_OUTPUT_TUNNEL_HANDLE , para asignar y recuperar el identificador de banda lateral para HWC.

  • Manejo de C2_PARAMKEY_TUNNEL_HOLD_RENDER cuando se adjunta a un C2Work , que indica al códec que decodifique y señale la finalización del trabajo, pero no que represente el búfer de salida hasta que 1) se le indique al códec que lo represente o 2) comience la reproducción de audio.

  • Manejo de C2_PARAMKEY_TUNNEL_START_RENDER , que indica al códec que renderice inmediatamente el fotograma marcado con C2_PARAMKEY_TUNNEL_HOLD_RENDER , incluso si la reproducción de audio no ha comenzado.

  • Deje debug.stagefright.ccodec_delayed_params sin configurar (recomendado). Si lo configura, configúrelo en false .

La implementación de AOSP configura el modo túnel en CCodec a través de C2PortTunnelModeTuning , como se muestra en el siguiente fragmento de código:

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 el componente admite esta configuración, debe asignar un identificador de banda lateral a este códec y pasarlo nuevamente a través de C2PortTunnelHandlingTuning para que el HWC pueda identificar el códec asociado.

AudioHAL

Para la reproducción de video bajo demanda, Audio HAL recibe las marcas de tiempo de la presentación de audio en línea con los datos de audio en formato big-endian dentro de un encabezado que se encuentra al comienzo de cada bloque de datos de audio que escribe la aplicación:

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

Para que HWC reproduzca cuadros de video sincronizados con los cuadros de audio correspondientes, Audio HAL debe analizar el encabezado de sincronización y usar la marca de tiempo de presentación para resincronizar el reloj de reproducción con la representación de audio. Para resincronizar cuando se reproduce audio comprimido, es posible que Audio HAL deba analizar los metadatos dentro de los datos de audio comprimido para determinar la duración de la reproducción.

Pausar soporte

Android 5 o versiones anteriores no incluyen soporte para pausas. Puede pausar la reproducción tunelizada solo por inanición de A/V, pero si el búfer interno para video es grande (por ejemplo, hay un segundo de datos en el componente OMX), hace que la pausa parezca no responder.

En Android 5.1 o superior, AudioFlinger admite pausar y reanudar salidas de audio directas (tunelizadas). Si el HAL implementa la pausa y la reanudación, la pausa y la reanudación de la pista se reenvían al HAL.

La secuencia de llamada de pausa, vaciado y reanudación se respeta ejecutando las llamadas HAL en el hilo de reproducción (igual que descargar).

Sugerencias de implementación

AudioHAL

Para Android 11, el ID de sincronización de hardware de PCR o STC se puede usar para la sincronización A/V, por lo que se admite la transmisión de solo video.

Para Android 10 o versiones anteriores, los dispositivos que admiten reproducción de video en túnel deben tener al menos un perfil de flujo de salida de audio con los indicadores FLAG_HW_AV_SYNC y AUDIO_OUTPUT_FLAG_DIRECT en su archivo audio_policy.conf . Estos indicadores se utilizan para configurar el reloj del sistema desde el reloj de audio.

OMX

Los fabricantes de dispositivos deben tener un componente OMX independiente para la reproducción de vídeo en túnel (los fabricantes pueden tener componentes OMX adicionales para otros tipos de reproducción de audio y vídeo, como la reproducción segura). El componente tunelizado debería:

  • Especifique 0 buffers ( nBufferCountMin , nBufferCountActual ) en su puerto de salida.

  • Implemente la extensión OMX.google.android.index.prepareForAdaptivePlayback setParameter .

  • Especifique sus capacidades en el archivo media_codecs.xml y declare la función de reproducción en túnel. También debe aclarar cualquier limitación sobre el tamaño del cuadro, la alineación o la tasa de bits. A continuación se muestra un ejemplo:

    <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 se utiliza el mismo componente OMX para admitir la decodificación tunelizada y no tunelizada, debe dejar la función de reproducción tunelizada como no requerida. Tanto los decodificadores tunelizados como los no tunelizados tienen las mismas limitaciones de capacidad. A continuación se muestra un ejemplo:

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

Compositor de hardware (HWC)

Cuando hay una capa tunelizada (una capa con HWC_SIDEBAND compositionType ) en una pantalla, el sidebandStream de la capa es el identificador de banda lateral asignado por el componente de video OMX.

El HWC sincroniza fotogramas de vídeo decodificados (desde el componente OMX tunelizado) con la pista de audio asociada (con el ID audio-hw-sync ). Cuando un nuevo cuadro de video se vuelve actual, el HWC lo compone con el contenido actual de todas las capas recibidas durante la última llamada de preparación o configuración y muestra la imagen resultante. Las llamadas de preparación o configuración ocurren solo cuando otras capas cambian o cuando cambian las propiedades de la capa de banda lateral (como la posición o el tamaño).

La siguiente figura representa el HWC trabajando con el sincronizador de hardware (o kernel o controlador), para combinar cuadros de video (7b) con la última composición (7a) para mostrarlos en el momento correcto, en función del audio (7c).

HWC que combina fotogramas de vídeo basados ​​en audio

Figura 2. Sincronizador de hardware (o kernel o controlador) HWC