El túnel multimedia, también conocido como modo de túnel, permite que los datos de video comprimidos pasen por un decodificador de video de hardware directamente a una pantalla, sin que los procese el código de la app ni el código del framework de Android. El código específico del dispositivo que se encuentra debajo de la pila de Android determina qué fotogramas de video se enviarán a la pantalla y cuándo se enviarán comparando las marcas de tiempo de presentación de los 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, un
AudioTrackreloj sincronizado con las marcas de tiempo de presentación de audio que pasa la appPara la reproducción de transmisión en vivo en Android 11 o versiones posteriores, un reloj de referencia de programa (PCR) o un reloj de hora del sistema (STC) controlado por un sintonizador
Fondo
La reproducción de video en modo no túnel 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 del reloj del sistema que el fotograma de audio correspondiente, y recupera las instancias AudioTimestamphistóricas para calcular la sincronización correcta.
Debido a que la reproducción de video en túnel omite el código de la app y reduce la cantidad de procesos que actúan sobre 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 al reloj elegido (PRC, STC o audio) evitando 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 las vsyncs de hardware verdaderas. Sin embargo, el túnel también puede reducir la compatibilidad con los efectos de GPU, como el desenfoque o las esquinas redondeadas en las ventanas de imagen en imagen (PiP), ya que los búferes omiten la pila de gráficos de Android.
En el siguiente diagrama, se muestra cómo el túnel simplifica el proceso de reproducción de video.

Figura 1: Comparación de los procesos de reproducción de video en túnel y no túnel
Para desarrolladores de apps
Debido a que la mayoría de los desarrolladores de apps 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 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 on demand en Android 5 o versiones posteriores:
Crea una instancia de
SurfaceView.Crea una instancia de
audioSessionId.Crea instancias de
AudioTrackyMediaCodeccon la instancia deaudioSessionIdcreada en el paso 2.Pone en cola los datos de audio en
AudioTrackcon la marca de tiempo de presentación del primer fotograma de audio en los datos de audio.
Para la reproducción de transmisión en vivo en Android 11 o versiones posteriores:
Crea una instancia de
SurfaceView.Obtén una instancia de
avSyncHwIddeTuner.Crea instancias de
AudioTrackyMediaCodeccon la instancia deavSyncHwIdcreada 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 en túnel está vinculada implícitamente a la reproducción de 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 busca.
Para indicar que el primer fotograma de video en cola se debe renderizar en cuanto se decodifique, establece el parámetro en
1.PARAMETER_KEY_TUNNEL_PEEKCuando los fotogramas de video comprimidos se reordenan en la cola (por ejemplo, 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 renderice 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
AudioTracky los búferes están vacíos (subejecución de audio), la reproducción de video se detiene hasta que se escriban más datos de audio porque el reloj de audio ya no avanza.Durante la reproducción, pueden aparecer discontinuidades que la app no puede corregir en las marcas de tiempo de 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
AudioTimestampno aumenta para los fotogramas de audio silenciosos insertados.
Flujo de secuencia de la búsqueda precisa
La búsqueda precisa te permite encontrar un lugar específico en un video. A diferencia de la búsqueda de fotogramas clave, que solo salta al fotograma I más cercano y puede desviarse de la posición objetivo en varios segundos, la búsqueda precisa renderiza el video en la marca de tiempo exacta solicitada. El cumplimiento de esta secuencia de API específica permite que la app realice la sincronización de tiempo y la carga previa en segundo plano sin problemas. Esto garantiza que el fotograma objetivo se muestre de inmediato cuando se reanude la reproducción.
Para realizar una búsqueda precisa, sigue el orden de ejecución que se ilustra en la Figura 2:
Figura 2: Flujo de secuencia para lograr una búsqueda precisa
Los detalles clave incluyen lo siguiente:
Ejecución paralela: Puedes ejecutar pasos dentro de un solo cuadro
parde forma simultánea. Por ejemplo, las llamadas a videoMediaCodecson independientes deAudioTrack.Dependencias secuenciales: Llama a todas las operaciones dentro del primer cuadro
parantes de avanzar al segundo cuadropar. En particular, la app debe asegurarse de queAudioTrack.writey los búferes enMediaCodecde video estén en cola antes de llamar aAudioTrack.play.
Flujo de secuencia de la reproducción de velocidad variable
La reproducción de velocidad variable te permite reproducir video a una velocidad más rápida o más lenta que la normal. Las apps suelen usar esta función para permitir que los usuarios consuman contenido más rápido (por ejemplo, reproducir conferencias educativas o podcasts a 1.5x o 2.0x para ahorrar tiempo) o más lento (por ejemplo, analizar jugadas deportivas o videos instructivos a 0.5x).
Para establecer la velocidad, sigue el orden de ejecución que se ilustra en la Figura 3:
Figura 3: Flujo de secuencia para establecer la velocidad
Los siguientes comportamientos y requisitos técnicos no se capturan en el diagrama de secuencia de la Figura 3:
AudioTrack.getTimestampmuestraframePositionsegún la frecuencia de entrada de audio original. Por ejemplo, con una entrada de 44,100 Hz y una velocidad de reproducción 2.0x, después de reproducir 2 segundos,AudioTrack.getTimestampmuestraframePositionde 176,400.Si la app llama a
setSpeed(1.5)y tiene éxito, y luego la app llama asetSpeed(30)y falla, la reproducción permanece en 1.5x.Si el audio está silenciado (con
setVolume), la app aún debe enviar búferes de audio porque los fotogramas de video se renderizan según la posición del audio.El tono de audio se conserva cuando se cambia la velocidad.
La velocidad de reproducción no se ve afectada por otras acciones de reproducción.
Ejemplo 1: Si la velocidad de reproducción es de 1.5x y
AudioTrackestá en pausa, la velocidad permanece en 1.5x después de reanudarAudioTrack.Ejemplo 2: Si la velocidad de reproducción es de 1.5x y el usuario busca un PTS diferente, siguiendo la Figura 2, la reproducción permanece en 1.5x.
Para garantizar que todos los fotogramas se decodifiquen a tiempo para renderizarse a la velocidad elegida, establece
KEY_OPERATING_RATEpara que coincida con el producto de la velocidad de fotogramas de video y la velocidad de reproducción. SiKEY_OPERATING_RATEno está configurado lo suficientemente alto, es posible que el códec no decodifique los fotogramas lo suficientemente rápido, lo que provocará descartes de fotogramas no deseados durante la reproducción.- Ejemplo: Si la velocidad de fotogramas original del contenido es de 60 fps y la
velocidad de reproducción es de 2x, establece
KEY_OPERATING_RATEen120.
- Ejemplo: Si la velocidad de fotogramas original del contenido es de 60 fps y la
velocidad de reproducción es de 2x, establece
Establecer la velocidad de forma repetida con diferentes velocidades admitidas no debería activar ningún error, y el comportamiento de reproducción después de la última llamada debería ser el mismo que si la velocidad se establece solo una vez, en la configuración de velocidad más reciente.
Instrucciones para los 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 realizar la reproducción en túnel en el archivo media_codecs.xml:
<Feature name="tunneled-playback" required="true"/>
Cuando se configura una instancia de MediaCodec en túnel con un ID de sesión de audio, consulta AudioFlinger para obtener 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 HW_AV_SYNC ID
del dispositivo de audio principal y lo asocia internamente 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 a la transmisión 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 a la transmisión de salida durante la creación de AudioTrack. Esto lo hace el 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 a una configuración de Tuner, se pasa al componente OMX o Codec2 para que el código del 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 del componente, 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 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 búferes de imágenes de la salida del códec en el momento adecuado, ya sea sincronizados con la transmisión de salida de audio asociada o el reloj de referencia del programa del sintonizador, componer los búferes con el contenido actual de otras capas y mostrar la imagen resultante. Esto sucede 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 de banda lateral (como la posición o el tamaño).
OMX
Un componente de decodificador en túnel debe admitir lo siguiente:
Establecer el parámetro extendido
OMX.google.android.index.configureVideoTunnelMode, que usa la estructuraConfigureVideoTunnelModeParamspara pasar el ID deHW_AV_SYNCasociado con el dispositivo de salida de audioConfigurar el parámetro
OMX_IndexConfigAndroidTunnelPeekque le indica al códec que renderice o no el primer fotograma de video decodificado, independientemente de si se inició la reproducción de audioEnviar el evento
OMX_EventOnFirstTunnelFrameReadycuando 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 controlador 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 de decodificador debe admitir lo siguiente:
Configurar
C2PortTunneledModeTuning, que configura el modo de túnel y pasa elHW_AV_SYNCrecuperado del dispositivo de salida de audio o de la configuración del sintonizadorConsultar
C2_PARAMKEY_OUTPUT_TUNNEL_HANDLEpara asignar y recuperar el controlador de banda lateral para HWCControlar
C2_PARAMKEY_TUNNEL_HOLD_RENDERcuando se adjunta a unC2Work, que le indica al códec que decodifique y señale la finalización del trabajo, pero que no renderice el búfer de salida hasta que 1) se le indique al códec que lo renderice más tarde o 2) comience la reproducción de audioControlar
C2_PARAMKEY_TUNNEL_START_RENDER, que le indica al códec que renderice de inmediato el fotograma marcado conC2_PARAMKEY_TUNNEL_HOLD_RENDER, incluso si no se inició la reproducción de audioDejar
debug.stagefright.ccodec_delayed_paramssin configurar (recomendado) Si lo configuras, establecefalse.
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, ¶ms);
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 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 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 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 en sincronización 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 sincronizar cuando se reproduce audio comprimido, es posible que el HAL de audio deba analizar los metadatos dentro de los datos de audio comprimidos para determinar su duración de reproducción.
Compatibilidad con la pausa
Android 5 o versiones anteriores no incluyen compatibilidad con la pausa. Solo puedes pausar la reproducción en túnel por falta de A/V, pero si el búfer interno para el 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 la pausa y la reanudación para las salidas de audio directas (en túnel). 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 llamadas de pausa, vaciado y reanudación se respeta ejecutando las llamadas HAL en el subproceso de reproducción (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 solo de video.
En Android 10 o versiones anteriores, los dispositivos que admiten 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 establecer 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 hacer lo siguiente:
Especificar 0 búferes (
nBufferCountMin,nBufferCountActual) en su puerto de salidaImplementar la extensión
OMX.google.android.index.prepareForAdaptivePlayback setParameterEspecificar sus capacidades en el archivo
media_codecs.xmly declarar 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 del fotograma. 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 en túnel y no túnel, debe dejar la función de reproducción en túnel como no obligatoria. Luego, los decodificadores en túnel y no túnel 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 en túnel (una capa con HWC_SIDEBAND compositionType) en una pantalla, la 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 un fotograma de video nuevo 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 solo se realizan cuando cambian otras capas o cuando cambian las propiedades de la capa de banda lateral (como la posición o el tamaño).
En la siguiente figura, se representa el HWC que trabaja con el sincronizador de hardware (o kernel o controlador) para combinar fotogramas de video (7b) con la composición más reciente (7a) para mostrarla en el momento correcto, según el audio (7c).

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