WebRTC sur Android: comment activer l'encodage matériel sur plusieurs appareils

Pour les appels vidéo dans Badoo, nous utilisons la norme WebRTC et le codec H.264. Si vous croyez à la documentation, ce codec devrait fonctionner sans problème sur n'importe quel appareil Android depuis Android 5.0. Mais dans la pratique, tout s'est avéré ne pas l'être. Dans cet article, je vais parler des fonctionnalités de l'implémentation du codage matériel pour le codec H.264 dans WebRTC et comment le faire fonctionner sur plus d'appareils.



Pourquoi H.264?


Lorsqu'il est connecté via WebRTC, tous les appareils participant à la session transmettent divers paramètres de communication, y compris les codecs vidéo et audio. Si les périphériques prennent en charge plusieurs codecs (par exemple, VP8 et H.264), les codecs prioritaires pour la plate-forme sont répertoriés en premier. Ces données sont utilisées au stade de la réconciliation dans WebRTC, après quoi seuls les codecs pris en charge par tous les appareils restent. Un exemple de telles données avec décryptage peut être vu dans ce document .

Dans le cas d'appels vidéo, si l'un des appareils ne prend pas en charge le codec H.264, les deux appareils peuvent passer, par exemple, au codec VP8, qui ne dépend pas de l'implémentation matérielle de l'appareil. Mais notre application est disponible sur une variété de gadgets, y compris les smartphones des générations précédentes. Par conséquent, pour les communications vidéo, nous voulions utiliser l'encodage matériel autant que possible: il réduit la charge sur le processeur et ne consomme pas beaucoup la batterie, ce qui est essentiel pour les gadgets obsolètes. La prise en charge du codage matériel H.264 est implémentée sur un grand nombre d'appareils, contrairement au même VP8 .

Prise en charge H.264 sur Android


Si vous pensez que la description de la prise en charge des formats multimédias , le décodage du profil de base H.264 devrait fonctionner sur tous les appareils Android et l'encodage - à commencer par Android 3.0. Dans Badoo, nous prenons en charge les appareils commençant par Android 5.0, nous ne devrions donc pas avoir de problèmes. Mais ce n'était pas si simple: même dans les gadgets avec la cinquième version, nous avons trouvé un grand nombre de fonctionnalités.

Avec quoi peut-il être connecté?

Comme vous le savez, lors du développement d'un nouvel appareil sur Android, tout fabricant doit réussir la suite de tests de compatibilité. Il fonctionne sur un PC connecté à l'appareil et ses résultats doivent être envoyés à Google pour confirmer que l'appareil répond aux exigences du système d'exploitation Android de la version spécifiée. Ce n'est qu'après que le gadget peut être mis sur le marché.

Nous nous intéressons aux tests multimédias de cette suite de tests, et plus spécifiquement aux tests d'encodage et de décodage vidéo. J'ai décidé de m'attarder sur les tests EncodeDecodeTest , MediaCodecTest , DecoderTest et EncoderTest , car ils sont présents sur toutes les versions d'Android depuis la 4.3. Le graphique du nombre de lignes de code dans ces tests ressemble à ceci:



Avant la version 4.3, la plupart de ces tests n'existaient tout simplement pas, et leur augmentation significative est tombée sur les versions 5 et 7. Par conséquent, nous pouvons dire qu'avant la version Android 4.3, Google ne vérifiait pas la conformité des appareils avec ses spécifications d'encodage et de décodage vidéo, mais en version 5.0 a grandement amélioré cette vérification.

Il semblerait que cela indique qu'à partir de la version 5.0 avec encodage, tout devrait être en ordre. Mais, compte tenu de mon expérience précédente avec le décodage de vidéos en streaming sur Android, j'étais sûr que ce n'était pas le cas. Il suffisait de regarder le nombre de sujets sur le codage dans le groupe Google discuter-webrtc .

Les fichiers source WebRTC, qui sont dans le domaine public, nous ont aidés à rechercher les pièges. Examinons-les plus en détail.

Prise en charge H.264 dans WebRTC


Commençons par le HardwareVideoEncoderFactory .

Il existe une méthode avec le nom parlant 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);
}

Comme nous pouvons le voir, la prise en charge du codage matériel sur Android n'est implémentée que pour les chipsets Qualcomm et Exynos. Pourquoi les autres chipsets ne sont-ils pas pris en charge dans l'implémentation WebRTC standard? Cela est probablement dû aux particularités de la mise en œuvre des fabricants de codecs matériels. Et il est souvent possible d'identifier ces fonctionnalités uniquement en production, car il n'est pas toujours possible de trouver des appareils particuliers.

Toutes les descriptions de codec sur l'appareil sont stockées dans le fichier media_codecs.xml. Voici, par exemple, ce fichier pour le Pixel XL et pour le HUAWEI P8 lite . Lorsque vous obtenez une liste de codecs à l'aide de la méthode getCodecInfos () de l'objet MediaCodecList, ce fichier est analysé - et les codecs qui y sont stockés sont retournés. Cette opération et le remplissage correct de ce fichier par le constructeur sont couverts par le test CTS.MediaCodecListTest , qui est également passé de 160 lignes de code dans Android 4.3 à 740 lignes dans Android 10.

Dans Badoo, nous avons changé le code de méthode isHardwareSupportedInCurrentSdkH264, rejetant la liste de codecs «blanche» et la remplaçant par une liste «noire» de préfixes de codec de programme répertoriés dans WebRTC:

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

Mais vous ne pouvez pas simplement prendre et implémenter la prise en charge de tous les codecs sans prêter attention aux fonctionnalités des fabricants. D'après les noms des sujets consacrés au codage matériel sur Android dans le groupe discuter-webrtc , nous pouvons comprendre que dans ce cas, nous rencontrerons certainement des erreurs. Fondamentalement, ils apparaissent au stade de la configuration du codec.

Options de configuration du codec


L'initialisation du codec pour l'encodage est la suivante:

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);

Il est facile de faire une erreur dans certains de ces paramètres, ce qui lèvera une exception lors de la configuration du codec et perturbera l'application. De plus, lorsque vous travaillez avec un codec, vous devrez peut-être ajuster son débit binaire en fonction de divers facteurs, car le codec lui-même le fait mal. Pour cela, dans WebRTC, la classe BaseBitrateAdjuster est responsable , qui a deux descendants:


En conséquence, pour chaque codec, vous devez choisir votre propre mécanisme de réglage du débit binaire. Examinons plus en détail les fonctionnalités de définition des paramètres d'initialisation pour les codecs matériels.

Résolution de flux


Après avoir reçu l'objet MediaCodecInfo pour le codec, vous pouvez examiner le codec plus en détail en obtenant ses capacités dans la classe CodecCapabilities . À partir d'eux, vous pouvez savoir si le codec prend en charge la résolution et la fréquence d'images sélectionnées. S'il prend en charge ces paramètres, ils peuvent être définis en toute sécurité.

Cependant, parfois cette règle ne fonctionne pas. Nous sommes confrontés au fait que les codecs avec le préfixe «OMX.MARVELL». encodé incorrectement, montrant des bandes vertes sur les bords de l'écran si la résolution du flux était différente de 4: 3. Dans le même temps, le codec lui-même a affirmé que la résolution et la fréquence d'images sélectionnées étaient prises en charge.

Mode débit binaire


Le mode standard pour tous les codecs vidéo est un débit binaire constant. Cependant, une fois que nous devions utiliser un débit binaire variable:

format.setInteger(MediaFormat.KEY_BITRATE_MODE, VIDEO_ControlRateVariable);

Cela s'est produit sur un appareil Lenovo A1000 avec le chipset Spreadtrum (maintenant Unisoc) commençant par le préfixe «OMX.sprd». Une recherche sur Internet nous a conduit à un enfant de six ans après sur Firefox OS qui décrit ce problème et comment le résoudre.

Format de couleur


Lorsque vous utilisez le codec en mode tampon d'octets, vous devez sélectionner le format correct. Cela se fait généralement en utilisant une fonction de la forme suivante:

@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 gros, le premier des formats de couleurs pris en charge est toujours sélectionné.

Cependant, dans le cas des codecs HUAWEI commençant par les préfixes "OMX.IMG.TOPAZ.", "OMX.hisi". et "OMX.k3.", cela n'a pas fonctionné, et après une longue recherche, nous avons trouvé une solution: quel que soit le format retourné par ces codecs, vous devez utiliser le format COLOR_FormatYUV420SemiPlanar . Le fil nous a aidés à comprendre cela dans un forum chinois.

Réglage du débit binaire


Le code WebRTC standard contient les éléments suivants :

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();
}

Comme vous pouvez le voir dans ce code, pour tous les chipsets sauf Exynos, le réglage du débit binaire est désactivé. Mais cela ne s'applique qu'à Qualcomm, car seuls Exynos et Qualcomm sont pris en charge dans le code standard. L'expérimentation de diverses valeurs de ce paramètre, ainsi que la recherche sur Internet, nous avons découvert que pour les codecs avec préfixes «OMX.MTK». il doit également être activé. Il est également nécessaire de le faire pour les codecs HUAWEI commençant par le préfixe "OMX.IMG.TOPAZ.", "OMX.hisi". ou "OMX.k3". Cela est dû au fait que ces codecs n'utilisent pas d'horodatage des trames pour ajuster le débit binaire, en supposant que toutes les trames sont livrées avec le même ensemble de fréquences lors de la configuration du codec.

En conclusion, je vais donner une liste des codecs que nous avons reçus pour les appareils sur Android 5.0 et 5.1. Ils nous intéressaient principalement parce que sur les nouvelles versions d'Android, la situation s'améliore et les codecs non standard deviennent plus petits.

Cela peut être vu dans le graphique ci-dessous. L'échelle logarithmique pour mieux montrer les cas rares.


Comme nous pouvons le voir, la plupart des appareils avaient des chipsets Spreadtrum, MediaTek, HUAWEI et MARVELL - par conséquent, nos modifications ont permis d'activer l'encodage matériel sur ces gadgets.

Résultat


Bien que nous pensions que certains appareils auraient des problèmes de fonctionnement avec H.264, Android a de nouveau pu nous surprendre. Comme nous pouvons le voir dans les statistiques des utilisateurs de Badoo, il y a encore beaucoup d'appareils entre les mains des utilisateurs en 2014-2016, qu'ils ne veulent pas ou ne peuvent pas mettre à jour. Et bien que la situation avec la sortie des mises à jour Android pour les nouveaux appareils soit déjà bien meilleure qu'il y a quelques années, la part des gadgets de la génération précédente diminue assez lentement et devra être maintenue pendant un certain temps.

Maintenant WebRTC est activement développé par Google en raison de son utilisation dans le projet Stadia (voici la vidéoavec des détails sur ce sujet), il deviendra donc de mieux en mieux et, très probablement, deviendra la norme pour la mise en œuvre des communications vidéo. J'espère que cet article vous aidera à comprendre les fonctionnalités de l'utilisation de H.264 dans WebRTC et à l'utiliser dans vos projets.

All Articles