Puede implementar túneles multimedia en el marco de Android 5.0 y superior. Aunque no se requiere túnel multimedia para Android TV, proporciona la mejor experiencia para contenido de ultra alta definición (4K).
Para Android 11 o superior, puede implementar túneles multimedia con contenido de audio y video alimentado directamente desde Tuner. Codec2
y AudioTrack
pueden usar la ID de sincronización HW de Tuner, que podría corresponder al canal de referencia del reloj del programa (PCR) o al canal del reloj de tiempo del sistema (STC).
Fondo
El marco de medios de Android maneja el contenido de audio/video de cuatro maneras:
- Software puro (descodificación local): el procesador de aplicaciones (AP) decodifica localmente audio a modulación de código de pulso (PCM) sin aceleración especial. Siempre se usa para Ogg Vorbis y se usa para MP3 y AAC cuando no hay soporte de descarga comprimida.
- La descarga de audio comprimido envía datos de audio comprimidos directamente al procesador de señal digital (DSP) y mantiene el AP apagado tanto como sea posible. Úselo para reproducir archivos de música con la pantalla apagada.
- La transferencia de audio comprimido envía audio comprimido (específicamente AC3 y E-AC3) directamente a través de HDMI a un televisor o receptor de audio externo, sin decodificarlo en el dispositivo Android TV. La porción de video se maneja por separado.
- La tunelización multimedia envía datos de audio y video comprimidos juntos. Cuando los decodificadores de video y audio reciben el flujo codificado, no vuelve al marco. Idealmente, la transmisión no interrumpe el AP.
- El paso multimedia envía datos de audio y video comprimidos juntos desde Tuner a decodificadores de video y audio sin involucrar el marco.
Comparación de enfoques
software puro | Descarga de audio comprimido | Transferencia de audio comprimido | tunelización multimedia | Transferencia multimedia | |
---|---|---|---|---|---|
Decodificar ubicación | punto de acceso | DSP | TV o receptor de audio/video (AVR) | TV o AVR | TV o AVR |
Maneja audio | sí | sí | sí | sí | no |
vídeo de las manijas | sí | no | no | sí | no |
Para desarrolladores de aplicaciones
Cree una instancia de SurfaceView
, obtenga una ID de sesión de audio, luego cree las instancias de AudioTrack
y MediaCodec
para proporcionar la sincronización y las configuraciones necesarias para la reproducción y la decodificación de cuadros de video.
Para Android 11 o superior, como alternativa para la identificación de la sesión de audio, la aplicación puede obtener la identificación de sincronización HW de Tuner y proporcionarla a las instancias de AudioTrack
y MediaCodec
para la sincronización A/V.
sincronización audiovisual
En el modo de tunelización multimedia, el audio y el video se sincronizan en un reloj maestro.
- Para Android 11 o superior, PCR o STC de Tuner pueden ser el reloj maestro para la sincronización A/V.
- Para Android 10 o inferior, el reloj de audio es el reloj maestro que se usa para la reproducción de A/V.
Si la instancia de MediaCodec
de video tunelizado y las instancias de AudioTrack
están vinculadas a la instancia HW_AV_SYNC
en AudioTrack
, el reloj implícito derivado de HW_AV_SYNC
restringe cuándo se presenta cada cuadro de video y muestra de audio, según la marca de tiempo de presentación (PTS) del cuadro de audio o video real.
Flujo de llamadas a la API
Para Android 11 o superior, el cliente puede usar la ID de sincronización HW de Tuner.
- Cree una instancia de
SurfaceView
.SurfaceView sv = new SurfaceView(mContext);
- Obtenga una ID de sesión de audio. Esta ID única se utiliza para crear la pista de audio (
AudioTrack
). Se pasa al códec de medios (MediaCodec
) y lo utiliza el marco de medios para vincular las rutas de audio y video.AudioManager am = mContext.getSystemService(AUDIO_SERVICE); int audioSessionId = am.generateAudioSessionId() // or, for Android 11 or higher int avSyncId = tuner.getAvSyncHwId();
- Cree
AudioTrack
con HW A/V syncAudioAttributes
.El administrador de políticas de audio solicita a la capa de abstracción de hardware (HAL) una salida de dispositivo que admita
FLAG_HW_AV_SYNC
y crea una pista de audio conectada directamente a esta salida sin un mezclador intermedio.AudioAttributes.Builder aab = new AudioAttributes.Builder(); aab.setUsage(AudioAttributes.USAGE_MEDIA); aab.setContentType(AudioAttributes.CONTENT_TYPE_MOVIE); aab.setFlag(AudioAttributes.FLAG_HW_AV_SYNC); // or, for Android 11 or higher new tunerConfig = TunerConfiguration(0, avSyncId); aab.setTunerConfiguration(tunerConfig); AudioAttributes aa = aab.build(); AudioTrack at = new AudioTrack(aa);
- Cree una instancia de
MediaCodec
de video y configúrela para la reproducción de video tunelizado.// retrieve codec with tunneled video playback feature MediaFormat mf = MediaFormat.createVideoFormat(“video/hevc”, 3840, 2160); mf.setFeatureEnabled(CodecCapabilities.FEATURE_TunneledPlayback, true); MediaCodecList mcl = new MediaCodecList(MediaCodecList.ALL_CODECS); String codecName = mcl.findDecoderForFormat(mf); if (codecName == null) { return FAILURE; } // create codec and configure it mf.setInteger(MediaFormat.KEY_AUDIO_SESSION_ID, audioSessionId); // or, for Android 11 or higher mf.setInteger(MediaFormat.KEY_HARDWARE_AV_SYNC_ID, avSyncId); MediaCodec mc = MediaCodec.createCodecByName(codecName); mc.configure(mf, sv.getSurfaceHolder().getSurface(), null, 0);
- Decodifica los cuadros de video.
mc.start(); for (;;) { int ibi = mc.dequeueInputBuffer(timeoutUs); if (ibi >= 0) { ByteBuffer ib = mc.getInputBuffer(ibi); // fill input buffer (ib) with valid data ... mc.queueInputBuffer(ibi, ...); } // no need to dequeue explicitly output buffers. The codec // does this directly to the sideband layer. } mc.stop(); mc.release(); mc = null;
Nota: Puede cambiar el orden de los pasos 3 y 4 en este proceso, como se ve en las dos figuras a continuación.
Para fabricantes de dispositivos
Los OEM deben crear un componente OpenMAX IL (OMX) decodificador de video separado para admitir la reproducción de video tunelizado. Este componente OMX debe anunciar que es capaz de reproducción en túnel (en media_codecs.xml
).
<Feature name=”tunneled-playback” required=”true” />
El componente también debe admitir un parámetro extendido OMX OMX.google.android.index.configureVideoTunnelMode
que usa una estructura ConfigureVideoTunnelModeParams
.
struct ConfigureVideoTunnelModeParams { OMX_U32 nSize; // IN OMX_VERSIONTYPE nVersion; // IN OMX_U32 nPortIndex; // IN OMX_BOOL bTunneled; // IN/OUT OMX_U32 nAudioHwSync; // IN OMX_PTR pSidebandWindow; // OUT };
Cuando se realiza una solicitud de creación de MediaCodec
tunelizado, el marco configura el componente OMX en modo tunelizado (configurando bTunneled
en OMX_TRUE
) y pasa el dispositivo de salida de audio asociado creado con un indicador AUDIO_HW_AV_SYNC
al componente OMX (en nAudioHwSync
).
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
. Un identificador de banda lateral es una etiqueta de identificación para la capa tunelada que permite que Hardware Composer (HW Composer) la identifique. Si el componente no admite esta configuración, debe establecer bTunneled
en OMX_FALSE
.
El marco recupera la capa tunelada (el identificador de banda lateral) asignada por el componente OMX y la pasa a HW Composer. El tipo de compositionType
de esta capa se establece en HWC_SIDEBAND
. (Consulte hardware/libhardware/include/hardware/hwcomposer.h
).
HW Composer es responsable de recibir nuevos búferes de imágenes del flujo en el momento adecuado (por ejemplo, sincronizados con el dispositivo de salida de audio asociado), componerlos con el contenido actual de otras capas y mostrar la imagen resultante. Esto sucede independientemente del ciclo normal de preparación/programación. Las llamadas de preparación/establecimiento 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).
Configuración
frameworks/av/services/audioflinger/AudioFlinger.cpp
HAL devuelve el ID de HW_AV_SYNC
como una representación decimal de cadena de caracteres de un entero de 64 bits. (Consulte frameworks/av/services/audioflinger/AudioFlinger.cpp
).
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);
frameworks/av/services/audioflinger/Threads.cpp
El marco de trabajo de audio debe encontrar el flujo de salida HAL al que corresponde este ID de sesión y consultar el HAL para el hwAVSyncId
usando set_parameters
.
mOutput->stream->common.set_parameters(&mOutput->stream->common, AUDIO_PARAMETER_STREAM_HW_AV_SYNC=hwAVSyncId);
Configuración del decodificador OMX
MediaCodec.java
El marco de trabajo de audio encuentra el flujo de salida HAL correspondiente para este ID de sesión y recupera el ID audio-hw-sync
consultando HAL para el indicador AUDIO_PARAMETER_STREAM_HW_AV_SYNC
usando get_parameters
.
// Retrieve HW AV sync audio output device from Audio Service // in MediaCodec.configure() 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); } // ...
Este ID de sincronización HW se pasa al decodificador de video tunelizado OMX mediante el parámetro personalizado OMX.google.android.index.configureVideoTunnelMode
.
ACodec.cpp
Después de obtener el ID de sincronización del hardware de audio, ACodec lo usa para configurar el decodificador de video tunelizado para que el decodificador de video tunelizado sepa qué pista de audio sincronizar.
// Assume you're going to use tunneled video rendering. // Configure OMX component in tunneled mode and grab sideband handle (sidebandHandle) from OMX // component. native_handle_t* sidebandHandle; // Configure OMX component in tunneled mode status_t err = mOMX->configureVideoTunnelMode(mNode, kPortIndexOutput, OMX_TRUE, audioHwSync, &sidebandHandle);
OMXNodeInstance.cpp
El componente OMX se configura mediante el método configureVideoTunnelMode
anterior.
// paraphrased 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;
ACodec.cpp
Una vez que el componente OMX se configura en modo de túnel, el identificador de la banda lateral se asocia con la superficie de representación.
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; }
Luego, la sugerencia de resolución máxima, si está presente, se envía al componente.
// Configure max adaptive playback resolution - as for any other video decoder int32_t maxWidth = 0, maxHeight = 0; if (msg->findInt32("max-width", &maxWidth) && msg->findInt32("max-height", &maxHeight)) { err = mOMX->prepareForAdaptivePlayback( mNode, kPortIndexOutput, OMX_TRUE, maxWidth, maxHeight); }
Pausar soporte
Android 5.0 y versiones anteriores no incluyen compatibilidad con pausas. 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 1 segundo de datos en el componente OMX), hace que la pausa parezca no responder.
En Android 5.1 y superior, AudioFlinger admite pausa y reanudación para salidas de audio directas (tunelizadas). Si la HAL implementa pausa/reanudación, la pausa/reanudación de la pista se reenvía 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).
Compatibilidad con códec2
Para Android 11 o superior, Codec2 admite la reproducción en túnel.
CCodec.cpp
Para admitir la reproducción en túnel, Codec2 funciona de manera similar a OMX. Para admitir la identificación de sincronización HW de Tuner, Codec2 busca la identificación de sincronización de las fuentes a continuación.
sp<ANativeWindow> nativeWindow = static_cast<ANativeWindow *>(surface.get()); int32_t audioHwSync = 0; if (!msg->findInt32("hw-av-sync-id", &audioHwSync)) { if (!msg->findInt32("audio-hw-sync", &audioHwSync)) { } } err = configureTunneledVideoPlayback(comp, audioHwSync, nativeWindow);
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 en túnel deben tener al menos un perfil de flujo de salida de audio con las 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.
Este componente debe especificar 0 búferes ( nBufferCountMin
, nBufferCountActual
) en su puerto de salida.
El componente tunelizado también debe implementar la extensión OMX.google.android.index.prepareForAdaptivePlayback setParameter
.
El componente tunelizado debe especificar sus capacidades en el archivo media_codecs.xml
y declarar la característica de reproducción tunelizada. También debe aclarar cualquier limitación en el tamaño del cuadro, la alineación o la tasa de bits.
<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 con túnel y sin túnel, entonces 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.
<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
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.
HW Composer sincroniza fotogramas de vídeo decodificados (del componente OMX tunelizado) con la pista de audio asociada (con el ID audio-hw-sync
). Cuando un nuevo cuadro de video se vuelve actual, HW Composer lo combina con el contenido actual de todas las capas recibidas durante la última llamada de preparación/configuración y muestra la imagen resultante. Las llamadas de preparación/establecimiento 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 figura 4 representa el HW Composer trabajando con el sincronizador HW (o kernel/driver) para combinar fotogramas de video (7b) con la última composición (7a) para mostrarlos en el momento correcto, según el audio (7c).