tunelización multimedia

La tunelización multimedia permite que los datos de video comprimidos pasen a través de un decodificador de video 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 video enviar a la pantalla y cuándo enviarlos comparando las marcas de tiempo de presentación de fotogramas de video con uno de los siguientes tipos de reloj interno:

  • Para la reproducción de video a pedido en Android 5 o superior, un reloj AudioTrack sincronizado con las marcas de tiempo de la presentación de audio pasadas 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 video tradicional en Android notifica a la aplicación cuando se ha decodificado un cuadro de video comprimido. Luego, la aplicación libera el cuadro de video decodificado en la pantalla para que se reproduzca a la misma hora del reloj del sistema que el cuadro de audio correspondiente, recuperando instancias históricas de AudioTimestamps de tiempo de audio para calcular el tiempo correcto.

Debido a que la reproducción de video tunelizado omite el código de la aplicación y reduce la cantidad de procesos que actúan en 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 una sincronización más precisas con el reloj elegido (PRC, STC o audio) al evitar los problemas de temporización introducidos por un posible sesgo entre la temporización de las solicitudes de Android para renderizar video y la temporización de las verdaderas sincronizaciones de hardware. Sin embargo, la tunelización también puede reducir la compatibilidad con los efectos de la GPU, como el desenfoque o las esquinas redondeadas en las ventanas de imagen en imagen (PiP), porque los búferes omiten la pila de gráficos de Android.

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

comparación de los modos tradición y túnel

Figura 1. Comparación de los procesos de reproducción de video tradicionales y tunelizados

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, use las siguientes instrucciones.

Para reproducción de video a pedido en Android 5 o superior:

  1. Cree una instancia de SurfaceView .

  2. Cree una instancia de audioSessionId .

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

  4. Ponga en cola 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 la reproducción de transmisiones en vivo en Android 11 o superior:

  1. Cree una instancia de SurfaceView .

  2. Obtenga una instancia de avSyncHwId de Tuner .

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

El flujo de llamadas a la 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 video bajo demanda

Debido a que la reproducción de video a pedido tunelizado está ligada implícitamente a la reproducción de AudioTrack , el comportamiento de la reproducción de video tunelizado puede depender del comportamiento de la reproducción de audio.

  • En la mayoría de los dispositivos, de manera predeterminada, un cuadro de video no se procesa hasta que comienza la reproducción de audio. Sin embargo, es posible que la aplicación deba procesar 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 señalar que el primer cuadro de video en cola debe procesarse tan pronto como se decodifique, establezca el parámetro PARAMETER_KEY_TUNNEL_PEEK en 1 . Cuando los cuadros de video comprimidos se reordenan en la cola (como cuando hay cuadros B presentes), esto significa que el primer cuadro de video que se muestra siempre debe ser un cuadro I.

    • Si no desea que se procese el primer cuadro de video 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 búferes están vacíos (insuficiencia de audio), 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, las discontinuidades que la aplicación no puede corregir pueden aparecer 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 marco AudioTimestamp no aumenta para los marcos de audio silenciosos insertados.

Para fabricantes de dispositivos

Configuración

Los OEM deben crear un decodificador de video separado para admitir la reproducción de video tunelizado. Este decodificador debería anunciar que es capaz de reproducir en túnel en el archivo media_codecs.xml :

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

Cuando una instancia de MediaCodec tunelizada se configura con una ID de sesión de audio, consulta a AudioFlinger por esta ID de 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 de 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 de AudioTrack , el ID de 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 de HW_AV_SYNC se pasa al flujo de salida durante la creación de 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 de HW_AV_SYNC , ya sea que corresponda a un flujo de salida de audio o una configuración de 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 correspondiente o el flujo de sintonizador.

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 transmite 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 búferes de imagen de la salida del códec en el momento adecuado, ya sea sincronizados con el flujo de salida de audio asociado o con el reloj de referencia del programa sintonizador, componiendo los búferes con el contenido actual de otras capas y mostrando la imagen resultante. Esto sucede independientemente del ciclo normal de preparación y fraguado. Las llamadas de preparación y configuración ocurren solo cuando cambian otras capas, 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:

  • Configuración del parámetro extendido OMX.google.android.index.configureVideoTunnelMode , que usa la estructura ConfigureVideoTunnelModeParams para pasar el ID de HW_AV_SYNC asociado con el dispositivo de salida de audio.

  • Configurar el parámetro OMX_IndexConfigAndroidTunnelPeek que le dice al códec que procese o no el primer cuadro de video decodificado, independientemente de si la reproducción de audio ha comenzado.

  • Enviar el evento OMX_EventOnFirstTunnelFrameReady cuando el primer cuadro de video tunelizado se haya decodificado y esté listo para renderizarse.

La implementación de AOSP configura el modo de 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 devolverlo 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 establecer 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:

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

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

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

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

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

La implementación de AOSP configura el modo de 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 devolverlo a través de C2PortTunnelHandlingTuning para que el HWC pueda identificar el códec asociado.

HAL de audio

Para la reproducción de video a pedido, 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 la presentación para volver a sincronizar el reloj de reproducción con la reproducción de audio. Para volver a sincronizar cuando se reproduce audio comprimido, es posible que Audio HAL necesite analizar los metadatos dentro de los datos de audio comprimido para determinar la duración de la reproducción.

Pausar soporte

Android 5 o inferior no incluye soporte de pausa. Puede pausar la reproducción tunelada solo por agotamiento 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 pausa y reanudación para salidas de audio directas (tunelizadas). Si la HAL implementa la pausa y la reanudación, la pausa y la reanudación de la pista se reenvían a la HAL.

La secuencia de llamadas de pausa, vaciado y reanudación se respeta mediante la ejecución de las llamadas HAL en el subproceso de reproducción (igual que la descarga).

Sugerencias de implementación

HAL de audio

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

Para Android 10 o versiones anteriores, los dispositivos que admitan la reproducción de video tunelizado deben tener al menos un perfil de flujo de salida de audio con los 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 separado para la reproducción de video tunelizado (los fabricantes pueden tener componentes OMX adicionales para otros tipos de reproducción de audio y video, como la reproducción segura). El componente tunelizado debe:

  • Especifique 0 búferes ( 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 en 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 usa el mismo componente OMX para admitir la decodificación con túnel y sin túnel, debería dejar la función de reproducción con túnel 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 tunelada (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 cuadros de video 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 combina 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 cambian otras capas, 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 mostrar en el momento correcto, según el audio (7c).

HWC que combina cuadros de video basados ​​​​en audio

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