美文网首页程序员
WebRTC android h264 编解码适配

WebRTC android h264 编解码适配

作者: 还我漂漂拳哒哒哒 | 来源:发表于2019-03-25 18:51 被阅读42次

自从Cisco 宣布旗下的H264 Codec开源为OpenH264,并且替所有OpenH264的使用者支付了H264的专利费,WebRTC也随即对h264进行了支持, 在Android平台, 软编用 OpenH264, 软解用FFMPGE, 硬编硬解用 MediaCodec. 在android和iOs中软编软解默认是禁止的,要想启用需要把OpenH264和FFMPGE编译进来,这样就会大大增加库的大小,况且软编软解比较费cpu,耗电量和发热都会增加,使用软编软解的好处是编解码不依赖于硬件,不存在设备适配问题。硬件编解码能够解决软编软解的痛处,但是存在设备适配问题,因为不同的设备使用的硬解码器不同,可能会导致编码失败及解码失败。以android为例,由于android厂商众多,使用的cpu芯片更是繁多,同一芯片厂商不同型号实现的硬解码器也有不同,这样就造成适配很困难,况且h264不是google推荐使用的编解码器(毕竟不是亲生的),所以在适配方面存在很多问题。

android中硬编码实现在MediaCodecVideoEncoder.java中,硬解码实现在MediaCodecVideoDecoder.java中,下面逐个分析

来看一下如何创建编码器:

boolean initEncode(VideoCodecType type, int profile, int width, int height, int kbps, int fps,

      boolean useSurface) {

    ...

    EncoderProperties properties = null;

    String mime = null;

    int keyFrameIntervalSec = 0;

    boolean configureH264HighProfile = false;

    if (type == VideoCodecType.VIDEO_CODEC_VP8) {

      mime = VP8_MIME_TYPE;

      properties = findHwEncoder(

          VP8_MIME_TYPE, vp8HwList(), useSurface ? supportedSurfaceColorList : supportedColorList);

      keyFrameIntervalSec = 100;

    } else if (type == VideoCodecType.VIDEO_CODEC_VP9) {

      mime = VP9_MIME_TYPE;

      properties = findHwEncoder(

          VP9_MIME_TYPE, vp9HwList, useSurface ? supportedSurfaceColorList : supportedColorList);

      keyFrameIntervalSec = 100;

    } else if (type == VideoCodecType.VIDEO_CODEC_H264) {

      mime = H264_MIME_TYPE;

      properties = findHwEncoder(H264_MIME_TYPE, h264HwList(),

          useSurface ? supportedSurfaceColorList : supportedColorList);

      if (profile == H264Profile.CONSTRAINED_HIGH.getValue()) {

        EncoderProperties h264HighProfileProperties = findHwEncoder(H264_MIME_TYPE,

            h264HighProfileHwList, useSurface ? supportedSurfaceColorList : supportedColorList);

        if (h264HighProfileProperties != null) {

          Logging.d(TAG, "High profile H.264 encoder supported.");

          configureH264HighProfile = true;

        } else {

          Logging.d(TAG, "High profile H.264 encoder requested, but not supported. Use baseline.");

        }

      }

      keyFrameIntervalSec = 20;

    } else {

      throw new RuntimeException("initEncode: Non-supported codec " + type);

    }

    ...

}

由于我们使用的是h264编码,传入initEncode函数的type参数是VideoCodecType.VIDEO_CODEC_H264,接下来调用:

findHwEncoder(H264_MIME_TYPE, h264HwList(),useSurface ? supportedSurfaceColorList : supportedColorList)去寻找可用的编码器,该函数第一个和最后一个参数很好理解,中间h264HwList()是什么鬼?往下看:

// List of supported HW H.264 encoders.

  private static final MediaCodecProperties qcomH264HwProperties = new MediaCodecProperties(

      "OMX.qcom.", Build.VERSION_CODES.KITKAT, BitrateAdjustmentType.NO_ADJUSTMENT);

  private static final MediaCodecProperties exynosH264HwProperties = new MediaCodecProperties(

      "OMX.Exynos.", Build.VERSION_CODES.LOLLIPOP, BitrateAdjustmentType.FRAMERATE_ADJUSTMENT);

  private static final MediaCodecProperties mediatekH264HwProperties = new MediaCodecProperties(

      "OMX.MTK.", Build.VERSION_CODES.O_MR1, BitrateAdjustmentType.FRAMERATE_ADJUSTMENT);

  private static final MediaCodecProperties[] h264HwList() {

    final ArrayList<MediaCodecProperties> supported_codecs = new ArrayList<MediaCodecProperties>();

    supported_codecs.add(qcomH264HwProperties);

    supported_codecs.add(exynosH264HwProperties);

    if (PeerConnectionFactory.fieldTrialsFindFullName("WebRTC-MediaTekH264").equals("Enabled")) {

      supported_codecs.add(mediatekH264HwProperties);

    }

    return supported_codecs.toArray(new MediaCodecProperties[supported_codecs.size()]);

  }

注释写都很清楚:支持的硬件h264编码器列表,这里竟然写成了白名单的形式,my God!!!,再往下看findHwEncoder的具体实现:

private static @Nullable EncoderProperties findHwEncoder(

      String mime, MediaCodecProperties[] supportedHwCodecProperties, int[] colorList) {

    //仅支持4.4及其以上系统

    if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) {

      return null;

    }

    ...

    // Check if this is supported HW encoder.

      boolean supportedCodec = false;

      BitrateAdjustmentType bitrateAdjustmentType = BitrateAdjustmentType.NO_ADJUSTMENT;

      for (MediaCodecProperties codecProperties : supportedHwCodecProperties) {

        //判断寻找到的编码器是否在名单中

        if (name.startsWith(codecProperties.codecPrefix)) {

          if (Build.VERSION.SDK_INT < codecProperties.minSdk) {

            Logging.w(

                TAG, "Codec " + name + " is disabled due to SDK version " + Build.VERSION.SDK_INT);

            continue;

          }

          if (codecProperties.bitrateAdjustmentType != BitrateAdjustmentType.NO_ADJUSTMENT) {

            bitrateAdjustmentType = codecProperties.bitrateAdjustmentType;

            Logging.w(

                TAG, "Codec " + name + " requires bitrate adjustment: " + bitrateAdjustmentType);

          }

          supportedCodec = true;

          break;

        }

      }

      if (!supportedCodec) {

        continue;

      }

      ...

}

这里的确对设备支持的h264编码器进行了白名单比对,而白名单中仅列出了"OMX.qcom.","OMX.Exynos.","OMX.MTK." 三种,可实际上呢,还有很多,比如"OMX.Intel.", "OMX.hisi.", "OMX.google.","OMX.rk", "OMX.Exynos.",  即使最新的WebRTC代码仍然仅支持上面的三种,假如你什么都不改动,你会发现至少有一半都设备无法使用h264编解码,我这里仅分析了编码的情况,事实上解码同样存在这个问题,那解决方法呢?

有三种解决方案:

第一种:把所有的h264编码器名字都加入白名单,可实际上很难做到,市场上android机千奇百怪,很难全部罗列完。

第二种:去除白名单比对。代码如下:

private static @Nullable EncoderProperties findHwEncoder(

      String mime, MediaCodecProperties[] supportedHwCodecProperties, int[] colorList) {

    ...

    /*

    // Check if this is supported HW encoder.

      boolean supportedCodec = false;

      BitrateAdjustmentType bitrateAdjustmentType = BitrateAdjustmentType.NO_ADJUSTMENT;

      for (MediaCodecProperties codecProperties : supportedHwCodecProperties) {

        //判断寻找到到编码器是否在白名单中

        if (name.startsWith(codecProperties.codecPrefix)) {

          if (Build.VERSION.SDK_INT < codecProperties.minSdk) {

            Logging.w(

                TAG, "Codec " + name + " is disabled due to SDK version " + Build.VERSION.SDK_INT);

            continue;

          }

          if (codecProperties.bitrateAdjustmentType != BitrateAdjustmentType.NO_ADJUSTMENT) {

            bitrateAdjustmentType = codecProperties.bitrateAdjustmentType;

            Logging.w(

                TAG, "Codec " + name + " requires bitrate adjustment: " + bitrateAdjustmentType);

          }

          supportedCodec = true;

          break;

        }

      }

      if (!supportedCodec) {

        continue;

      }

      */

      ...

}

仅仅对白名单比对的代码进行了注释,这里有个隐患,只使用发现到第一个编码器,设备可能支持的h264不只一个,该使用哪个暂时无法判断,还好,目前使用该方法没有发现有任何问题。

第三种:使用createEncoderByType函数创建编码器,只要传入正确的type参数即可。

综合以上三种方法,较好的是第二种和第三种。

总结:WebRTC在h264支持方面还是不够好,存在很多问题,本文仅仅列出一处比较严重的地方,事实上还有很多。毕竟不是亲生的啊,支持力度就是不一样。

欢迎关注公众号:

或者微信搜索公众号:webrtc home

版权声明:本文为博主原创文章,转载请附上博文链接!

相关文章

网友评论

    本文标题:WebRTC android h264 编解码适配

    本文链接:https://www.haomeiwen.com/subject/cygbvqtx.html