WebRTC en Android: cómo habilitar la codificación de hardware en múltiples dispositivos

Para las videollamadas en Badoo, utilizamos el estándar WebRTC y el códec H.264. Si cree en la documentación, este códec debería funcionar sin problemas en cualquier dispositivo Android desde Android 5.0. Pero en la práctica, todo resultó no ser así. En este artículo, hablaré sobre las características de implementar la codificación de hardware para el códec H.264 en WebRTC y cómo hacer que funcione en más dispositivos.



¿Por qué H.264?


Cuando se conecta a través de WebRTC, todos los dispositivos que participan en la sesión transmiten varios parámetros de comunicación, incluidos los códecs de audio y video. Si los dispositivos admiten múltiples códecs (por ejemplo, VP8 y H.264), los códecs de prioridad para la plataforma se enumeran primero. Estos datos se usan en la etapa de reconciliación en WebRTC, después de lo cual solo quedan los códecs compatibles con todos los dispositivos. Un ejemplo de tales datos con descifrado se puede ver en este documento .

En el caso de las videollamadas, si uno de los dispositivos no admite el códec H.264, ambos dispositivos pueden cambiar, por ejemplo, al códec VP8, que es independiente de la implementación de hardware en el dispositivo. Pero nuestra aplicación está disponible en una variedad de dispositivos, incluidos los teléfonos inteligentes de generaciones anteriores. Por lo tanto, para las comunicaciones de video, queríamos utilizar la codificación de hardware siempre que sea posible: reduce la carga en el procesador y no consume tanta batería, lo cual es crítico para los dispositivos obsoletos. El soporte de codificación de hardware H.264 se implementa en una gran cantidad de dispositivos, a diferencia del mismo VP8 .

Soporte H.264 en Android


Si cree que la descripción del soporte para formatos multimedia , la decodificación del perfil de línea base H.264 debería funcionar en todos los dispositivos Android y la codificación, comenzando con Android 3.0. En Badoo, admitimos dispositivos que comienzan con Android 5.0, por lo que no deberíamos tener ningún problema. Pero no fue tan simple: incluso en dispositivos con la quinta versión, encontramos una gran cantidad de características.

¿Con qué se puede conectar?

Como sabe, al desarrollar un nuevo dispositivo en Android, cualquier fabricante debe pasar el Paquete de pruebas de compatibilidad. Se ejecuta en una PC conectada al dispositivo, y sus resultados deben enviarse a Google para confirmar que el dispositivo cumple con los requisitos del sistema operativo Android de la versión especificada. Solo después de eso, el gadget se puede lanzar al mercado.

Estamos interesados ​​en pruebas multimedia en este conjunto de pruebas y, más específicamente, en pruebas de codificación y decodificación de video. Decidí detenerme en las pruebas EncodeDecodeTest , MediaCodecTest , DecoderTest y EncoderTest , ya que están presentes en todas las versiones de Android desde 4.3. El gráfico del número de líneas de código en estas pruebas se ve así:



Antes de la versión 4.3, la mayoría de estas pruebas simplemente no existían, y su aumento significativo cayó en las versiones 5 y 7. Por lo tanto, podemos decir que antes de la versión Android 4.3 Google no verificó el cumplimiento de los dispositivos con sus especificaciones para codificar y decodificar video, sino en la versión 5.0 mejoró enormemente esta verificación.

Parece que esto indica que a partir de la versión 5.0 con la codificación, todo debería estar en orden. Pero, dada mi experiencia previa con la decodificación de transmisión de video en Android, estaba seguro de que no era así. Fue suficiente para ver la cantidad de temas sobre codificación en el grupo de Google debate-webrtc .

Los archivos fuente de WebRTC, que son de dominio público, nos ayudaron a buscar dificultades. Consideremos con más detalle.

Soporte H.264 en WebRTC


Comencemos con HardwareVideoEncoderFactory .

Hay un método con el nombre parlante isHardwareSupportedInCurrentSdkH264:

private boolean isHardwareSupportedInCurrentSdkH264(MediaCodecInfo info) {
  // First, H264 hardware might perform poorly on this model.
  if (H264_HW_EXCEPTION_MODELS.contains(Build.MODEL)) {
    return false;
  }
  String name = info.getName();
  // QCOM H264 encoder is supported in KITKAT or later.
  return (name.startsWith(QCOM_PREFIX) && Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT)
      // Exynos H264 encoder is supported in LOLLIPOP or later.
      || (name.startsWith(EXYNOS_PREFIX)
             && Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP);
}

Como podemos ver, el soporte para la codificación de hardware en Android se implementa solo para los conjuntos de chips Qualcomm y Exynos. ¿Por qué no hay soporte para otros conjuntos de chips en la implementación estándar de WebRTC? Lo más probable es que esto se deba a las peculiaridades de la implementación de los fabricantes de códecs de hardware. Y a menudo es posible identificar estas características solo en producción, ya que no siempre es posible encontrar dispositivos particulares.

Todas las descripciones de códecs en el dispositivo se almacenan en el archivo media_codecs.xml. Aquí, por ejemplo, está este archivo para Pixel XL y para HUAWEI P8 lite . Cuando recibe una lista de códecs utilizando el método getCodecInfos () del objeto MediaCodecList, este archivo se analiza y se devuelven los códecs almacenados en él. Esta operación y el llenado correcto de este archivo por parte del fabricante están cubiertos en la prueba CTS.MediaCodecListTest , que también aumentó de 160 líneas de código en Android 4.3 a 740 líneas en Android 10.

En Badoo, cambiamos el código del método isHardwareSupportedInCurrentSdkH264, rechazando la lista de códec "blanco" y reemplazándolo con una lista "negra" de prefijos de códec de programa que se enumeran en WebRTC:

static final String[] SOFTWARE_IMPLEMENTATION_PREFIXES = {"OMX.google.", "OMX.SEC."};

Pero no puede simplemente tomar e implementar el soporte para todos los códecs sin prestar atención a las características de los fabricantes. Por los nombres de los temas dedicados a la codificación de hardware en Android en el grupo discusion-webrtc , podemos entender que en este caso definitivamente encontraremos errores. Básicamente, aparecen en la etapa de configuración del códec.

Opciones de configuración de códec


La inicialización del códec para codificar es la siguiente:

MediaCodec mediaCodec = createByCodecName(codecName);
MediaFormat format = MediaFormat.createVideoFormat(mime, width, height);
format.setInteger(MediaFormat.KEY_BIT_RATE, targetBitrateBps);
format.setInteger(MediaFormat.KEY_BITRATE_MODE, VIDEO_ControlRateConstant);
format.setInteger(MediaFormat.KEY_COLOR_FORMAT, colorFormat);
format.setInteger(MediaFormat.KEY_FRAME_RATE, targetFps);
format.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, keyFrameIntervalSec);
mediaCodec.configure(format, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);

Es fácil cometer un error en algunos de estos parámetros, lo que generará una excepción durante la configuración del códec e interrumpirá la aplicación. Además, cuando trabaje con un códec, es posible que deba ajustar su velocidad de bits dependiendo de varios factores, ya que el códec lo hace mal. Para esto, en WebRTC, la clase BaseBitrateAdjuster es responsable , que tiene dos descendientes:


En consecuencia, para cada códec, debe elegir su propio mecanismo de ajuste de velocidad de bits. Consideremos con más detalle las características de establecer parámetros de inicialización para códecs de hardware.

Resolución de la corriente


Después de recibir el objeto MediaCodecInfo para el códec, puede examinar el códec con más detalle obteniendo sus capacidades en la clase CodecCapabilities . A partir de ellos, puede averiguar si el códec admite la resolución y la velocidad de fotogramas seleccionadas. Si admite estos parámetros, se pueden configurar de forma segura.

Sin embargo, a veces esta regla no funciona. Nos enfrentamos al hecho de que los códecs con el prefijo "OMX.MARVELL". codificado incorrectamente, mostrando franjas verdes en los bordes de la pantalla si la resolución de la transmisión fue diferente de 4: 3. Al mismo tiempo, el códec mismo afirmó que la resolución seleccionada y la velocidad de cuadros son compatibles.

Modo de velocidad de bits


El modo estándar para todos los códecs de video es una tasa de bits constante. Sin embargo, una vez que tuvimos que usar una tasa de bits variable:

format.setInteger(MediaFormat.KEY_BITRATE_MODE, VIDEO_ControlRateVariable);

Esto sucedió en un dispositivo Lenovo A1000 con el chipset Spreadtrum (ahora Unisoc) comenzando con el prefijo "OMX.sprd". Una búsqueda en Internet nos llevó a una publicación de seis años en Firefox OS que describe este problema y cómo resolverlo.

Formato de color


Al usar el códec en modo byte-buffer, debe seleccionar el formato correcto. Esto generalmente se hace usando una función de la siguiente forma:

@Nullable
static Integer selectColorFormat(int[] supportedColorFormats, CodecCapabilities capabilities) {
  for (int supportedColorFormat : supportedColorFormats) {
    for (int codecColorFormat : capabilities.colorFormats) {
      if (codecColorFormat == supportedColorFormat) {
        return codecColorFormat;
      }
    }
  }
  return null;
}

En términos generales, siempre se selecciona el primero de los formatos de color admitidos.

Sin embargo, en el caso de los códecs HUAWEI que comienzan con los prefijos "OMX.IMG.TOPAZ.", "OMX.hisi". y "OMX.k3.", no funcionó, y después de una larga búsqueda encontramos una solución: no importa qué formato devuelvan estos códecs, debe usar el formato COLOR_FormatYUV420SemiPlanar . El hilo nos ayudó a resolver esto en un foro chino.

Ajuste de tasa de bits


El código estándar de WebRTC contiene lo siguiente :

private BitrateAdjuster createBitrateAdjuster(VideoCodecMimeType type, String codecName) {
  if (codecName.startsWith(EXYNOS_PREFIX)) {
    if (type == VideoCodecMimeType.VP8) {
        // Exynos VP8 encoders need dynamic bitrate adjustment.
        return new DynamicBitrateAdjuster();
      } else {
        // Exynos VP9 and H264 encoders need framerate-based bitrate adjustment.
        return new FramerateBitrateAdjuster();
    }
  }
  // Other codecs don't need bitrate adjustment.
  return new BaseBitrateAdjuster();
}

Como puede ver en este código, para todos los conjuntos de chips, excepto Exynos, el ajuste de velocidad de bits está desactivado. Pero esto solo se aplica a Qualcomm, ya que solo Exynos y Qualcomm son compatibles con el código estándar. Después de experimentar con varios valores de esta configuración, y también buscó en Internet, descubrimos que para los códecs con prefijos "OMX.MTK". También necesita ser encendido. También es necesario hacer esto para los códecs HUAWEI que comienzan con el prefijo "OMX.IMG.TOPAZ.", "OMX.hisi". o "OMX.k3". Esto se debe al hecho de que estos códecs no usan marcas de tiempo de cuadros para ajustar la tasa de bits, suponiendo que todos los cuadros vengan con el mismo conjunto de frecuencias al configurar el códec.

En conclusión, daré una lista de códecs que recibimos para dispositivos con Android 5.0 y 5.1. Nos interesaron principalmente porque en las versiones más recientes de Android la situación está mejorando y los códecs no estándar son cada vez más pequeños.

Esto se puede ver en el siguiente gráfico. La escala logarítmica para mostrar mejor los casos raros.


Como podemos ver, la mayoría de los dispositivos tenían chipsets Spreadtrum, MediaTek, HUAWEI y MARVELL, por lo que nuestros cambios ayudaron a activar la codificación de hardware en estos dispositivos.

Resultado


Aunque asumimos que algunos dispositivos tendrían problemas para trabajar con H.264, Android nuevamente pudo sorprendernos. Como podemos ver en las estadísticas de usuarios de Badoo, todavía hay bastantes dispositivos en manos de los usuarios en 2014-2016, que no quieren o no pueden actualizar. Y aunque la situación con el lanzamiento de actualizaciones de Android para nuevos dispositivos ya es mucho mejor que hace unos años, la proporción de dispositivos de la generación anterior está disminuyendo muy lentamente y tendrá que mantenerse durante bastante tiempo.

Ahora WebRTC está siendo desarrollado activamente por Google debido a su uso en el proyecto Stadia (aquí está el videocon detalles sobre este tema), por lo que será cada vez mejor y, lo más probable, se convertirá en el estándar para implementar comunicaciones de video. Espero que este artículo lo ayude a comprender las características de trabajar con H.264 en WebRTC y usarlo en sus proyectos.

All Articles