WebRTC android h264 编解码适配

自从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 supported_codecs = new ArrayList();
    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 android h264 编解码适配_第1张图片

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

你可能感兴趣的:(android,Webrtc)