Tunelización de contenido multimedia

La tunelización multimedia permite que los datos de video comprimidos se hagan un túnel a través de un decodificador de video de hardware directamente a una pantalla, sin que se procesen por el código de la app ni del framework 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 on demand en Android 5 o versiones posteriores, se usa un reloj AudioTrack sincronizado con las marcas de tiempo de presentación de audio que la app pasa

  • Para la reproducción de transmisiones en vivo en Android 11 o versiones posteriores, se usa un reloj de referencia de programa (PCR) o un reloj de hora del sistema (STC) controlado por un sintonizador.

Segundo plano

La reproducción de video tradicional en Android notifica a la app cuando se decodifica un fotograma de video comprimido. Luego, la app libera el fotograma de video decodificado en la pantalla para que se renderice a la misma hora de reloj del sistema que el fotograma de audio correspondiente, lo que recuperará instancias históricas de AudioTimestamps para calcular el tiempo correcto.

Debido a que la reproducción de video en túneles pasa por alto el código de la app y reduce la cantidad de procesos que actúan en el video, puede proporcionar una renderización de video más eficiente según la implementación del OEM. También puede proporcionar una cadencia y sincronización de video más precisas con el reloj elegido (PRC, STC o audio) evitando problemas de sincronización que se producen por un posible sesgo entre los tiempos de las solicitudes de Android para renderizar videos y los tiempos de las vsync de hardware reales. Sin embargo, la tunelización también puede reducir la compatibilidad con efectos de GPU, como el desenfoque o las esquinas redondeadas en las ventanas de pantalla en pantalla (PIP), ya que los búferes omiten la pila de gráficos de Android.

En el siguiente diagrama, se muestra cómo el tunelización simplifica el proceso de reproducción de video.

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

Figura 1: Comparación de los procesos de reproducción de video tradicionales y en túnel

Para desarrolladores de apps

Debido a que la mayoría de los desarrolladores de apps integran una biblioteca para la implementación de la reproducción, en la mayoría de los casos, la implementación solo requiere volver a configurar esa biblioteca para la reproducción en túnel. Para la implementación de bajo nivel de un reproductor de video en túnel, usa las siguientes instrucciones.

Para la reproducción de video a pedido en Android 5 o versiones posteriores:

  1. Crea una instancia de SurfaceView.

  2. Crea una instancia de audioSessionId.

  3. Crea instancias de AudioTrack y MediaCodec con la instancia de audioSessionId que creaste en el paso 2.

  4. Agrega datos de audio a una fila en AudioTrack con la marca de tiempo de presentación para el primer fotograma de audio en los datos de audio.

Para la reproducción de transmisiones en vivo en Android 11 o versiones posteriores, haz lo siguiente:

  1. Crea una instancia de SurfaceView.

  2. Obtén una instancia de avSyncHwId de Tuner.

  3. Crea instancias 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 on demand

Debido a que la reproducción de video on demand tunelizada está vinculada implícitamente a la reproducción AudioTrack, el comportamiento de la reproducción de video en túnel puede depender del comportamiento de la reproducción de audio.

  • En la mayoría de los dispositivos, de forma predeterminada, no se renderiza un fotograma de video hasta que comienza la reproducción de audio. Sin embargo, es posible que la app deba renderizar un fotograma de video antes de iniciar la reproducción de audio, por ejemplo, para mostrarle al usuario la posición actual del video mientras se busca.

    • Para indicar que el primer fotograma de video en fila debe renderizarse en cuanto se decodifica, establece el parámetro PARAMETER_KEY_TUNNEL_PEEK en 1. Cuando los fotogramas de video comprimidos se vuelven a ordenar en la fila (como cuando hay fotogramas B), esto significa que el primer fotograma de video que se muestra siempre debe ser un fotograma I.

    • Si no quieres que se procese el primer fotograma de video en cola hasta que comience la reproducción de audio, establece este parámetro en 0.

    • Si no se establece este parámetro, el OEM determina el comportamiento del dispositivo.

  • Cuando no se proporcionan datos de audio a AudioTrack y los búferes están vacíos (subdesbordamiento 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, es posible que aparezcan discontinuidades que la app 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 fotograma de video actual y las brechas positivas descartando fotogramas de video o insertando fotogramas 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 OEMs deben crear un decodificador de video independiente para admitir la reproducción de video en túnel. Este decodificador debe anunciar que es capaz de reproducir contenido a través de un túnel en el archivo media_codecs.xml:

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

Cuando una instancia MediaCodec tunelizada se configura con un ID de sesión de audio, consulta AudioFlinger para este 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 HW_AV_SYNC del dispositivo de audio principal y lo asocia de forma interna con el ID de 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 creó 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 creó, el ID de HW_AV_SYNC se pasa al flujo de salida durante la creación de AudioTrack. Esto se realiza a través del subproceso 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 una transmisión 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 la transmisión de salida de audio correspondiente o la transmisión del sintonizador.

Durante la configuración de componentes, el componente OMX o Codec2 debe mostrar un controlador de banda lateral que se pueda usar para asociar el códec con una capa de Hardware Composer (HWC). Cuando la app asocia una superficie con MediaCodec, este controlador 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 del sintonizador, combinando los búferes con el contenido actual de otras capas y mostrando la imagen resultante. Esto ocurre independientemente del ciclo normal de preparación y configuración. Las llamadas de preparación y configuración solo se realizan cuando cambian otras capas o cuando cambian las propiedades de la capa lateral (como la posición o el tamaño).

OMX

Un componente de decodificador con túnel debe admitir lo siguiente:

  • Establecer el parámetro extendido de 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 indica al codificador que renderice o no el primer fotograma de video decodificado, independientemente de si comenzó la reproducción de audio

  • Envío del evento OMX_EventOnFirstTunnelFrameReady cuando se decodifica el primer fotograma de video en túnel 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 pasarlo 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.

Codec2

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

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

  • Consulta C2_PARAMKEY_OUTPUT_TUNNEL_HANDLE para asignar y recuperar el control lateral para HWC.

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

  • Controla C2_PARAMKEY_TUNNEL_START_RENDER, que le indica al códec que renderice de inmediato la trama que se marcó con C2_PARAMKEY_TUNNEL_HOLD_RENDER, incluso si no se inició la reproducción de audio.

  • Deja debug.stagefright.ccodec_delayed_params sin configurar (recomendado). Si lo configuras, configúralo como false.

La implementación del 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 controlador de banda lateral a este códec y pasarlo de vuelta a través de C2PortTunnelHandlingTuning para que el HWC pueda identificar el códec asociado.

HAL de audio

Para la reproducción de video on demand, el HAL de audio recibe las marcas de tiempo de presentación de audio intercaladas 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 app:

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 renderice fotogramas de video sincronizados con los fotogramas de audio correspondientes, el HAL de audio debe analizar el encabezado de sincronización y usar la marca de tiempo de presentación para volver a sincronizar el reloj de reproducción con la renderización de audio. Para volver a sincronizarse cuando se reproduce audio comprimido, es posible que la HAL de audio deba analizar los metadatos dentro de los datos de audio comprimidos para determinar la duración de su reproducción.

Pausar asistencia

Android 5 y las versiones anteriores no admiten la pausa. Puedes pausar la reproducción en túnel solo por falta de A/V, pero si el búfer interno para video es grande (por ejemplo, hay un segundo de datos en el componente OMX), la pausa parece no responder.

En Android 5.1 o versiones posteriores, AudioFlinger admite las pausas y la reanudación para salidas de audio directas (túneles). Si el HAL implementa la pausa y la reanudación, la pausa y la reanudación del segmento se reenvían al HAL.

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

Sugerencias de implementación

HAL de audio

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

En el caso de Android 10 o versiones anteriores, los dispositivos compatibles con la reproducción de video en túnel deben tener al menos un perfil de transmisión de salida de audio con las marcas FLAG_HW_AV_SYNC y AUDIO_OUTPUT_FLAG_DIRECT en su archivo audio_policy.conf. Estas marcas se usan 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 video en túnel (los fabricantes pueden tener componentes OMX adicionales para otros tipos de reproducción de audio y video, como la reproducción segura). El componente en túnel debe cumplir con lo siguiente:

  • Especifica 0 búferes (nBufferCountMin, nBufferCountActual) en su puerto de salida.

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

  • Especifica sus capacidades en el archivo media_codecs.xml y declara la función de reproducción en túnel. También debe aclarar cualquier limitación en el tamaño, la alineación o la tasa de bits de los fotogramas. 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 y sin túnel, la función de reproducción con túnel debe dejarse como no obligatoria. Los decodificadores tanto túneles como 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>

Hardware Composer (HWC)

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

El HWC sincroniza los fotogramas de video decodificados (del componente OMX en túnel) con la pista de audio asociada (con el ID de audio-hw-sync). Cuando se vuelve actual un fotograma de video nuevo, el HWC lo combina con el contenido actual de todas las capas recibidas durante la última llamada de preparación o establecimiento, y muestra la imagen resultante. Las llamadas de preparación o configuración se producen solo cuando cambian otras capas, o bien cuando cambian las propiedades de la capa de banda lateral (como la posición o el tamaño).

En la siguiente imagen, se representa el HWC que funciona con el sincronizador de hardware (o kernel o controlador) para combinar fotogramas de video (7b) con la composición más reciente (7a) para mostrarlos en el momento correcto, según el audio (7c).

HWC que combina fotogramas de video basados en audio

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