Android平台WebRTC开启H264软编解码

1,140 阅读3分钟

由于版权问题WebRTC一直不支持H264,直到Cisco宣布旗下的H264 Codec开源为OpenH264,并且替所有OpenH264的使用者支付了H264的专利费,以此为契机,在IETF的WebRTC会议中,把H264和VP8都列入了WebRTC所必须要支持的视频编码器,接下来Google终于在WebRTC中增加了对H264的支持,对于Android平台, 编码器是用OpenH264, 解码器是用FFMPEG, 也可以用 MediaCodec. 这个对于广大需要H264的公司来说是一大福音. 在下载的WebRTC代码中做稍许配置, 就可以使用H264了,本文介绍针对M86分支增加H264软编的步骤。

开启H264编解码

打开开关

  • 修改third_party/ffmpeg/ffmpeg_generated.gni
    use_linux_config = is_linux || is_fuchsia
->  use_linux_config = is_linux || is_fuchsia || is_android
  • 修改third_party/ffmpeg/chromium/config/Chrome/android/arm64/config.h:
    #define CONFIG_H264_DECODER 0
->  #define CONFIG_H264_DECODER 1
  • 修改third_party/ffmpeg/chromium/config/Chrome/android/arm64/libavcodec/parser_list.c
    static const AVCodecParser * const parser_list[] = {
    ...
    &ff_vp9_parser,
+   &ff_h264_parser,
    NULL };
  • 修改third_party/ffmpeg/chromium/config/Chrome/android/arm64/libavcodec/codec_list.c
    static const AVCodec * const codec_list[] = {
    ...
    &ff_libopus_decoder,
+   &ff_h264_decoder,
    NULL };

增加H264编解码支持

  • sdk/android/api/org/webrtc中增加LibH264Decoder.javaLibH264Encoder.java,格式与原有Decoder、Encoder一致:
package org.webrtc;

public class LibH264Decoder extends WrappedNativeVideoDecoder {
  @Override
  public long createNativeVideoDecoder() {
    return nativeCreateDecoder();
  }
  static native long nativeCreateDecoder();
}
package org.webrtc;

public class LibH264Encoder extends WrappedNativeVideoEncoder {
  @Override
  public long createNativeVideoEncoder() {
    return nativeCreateEncoder();
  }

  static native long nativeCreateEncoder();

  @Override
  public boolean isHardwareEncoder() {
    return false;
  }
}
  • sdk/android/src/jni中添加h264_codec.cc,格式与原有vp8_codec.cc/vp9_codec.cc一致:
#include <jni.h>
#include "modules/video_coding/codecs/h264/include/h264.h"
#include "sdk/android/generated_libH264_jni/LibH264Decoder_jni.h"
#include "sdk/android/generated_libH264_jni/LibH264Encoder_jni.h"
#include "sdk/android/src/jni/jni_helpers.h"

namespace webrtc {
namespace jni {

static jlong JNI_LibH264Encoder_CreateEncoder(JNIEnv* jni) {
  return jlongFromPointer(H264Encoder::Create().release());
}

static jlong JNI_LibH264Decoder_CreateDecoder(JNIEnv* jni) {
  return jlongFromPointer(H264Decoder::Create().release());
}

}  // namespace jni
}  // namespace webrtc
  • modules/video_coding/codecs/h264/h264.cc中增加上H264Encoder的构造函数:
std::unique_ptr<H264Encoder> H264Encoder::Create() {
#if defined(WEBRTC_USE_H264)
  RTC_LOG(LS_INFO) << "Creating H264EncoderImpl.";
  return std::make_unique<H264EncoderImpl>(cricket::VideoCodec("H264"));
#else
  RTC_NOTREACHED();
  return nullptr;
#endif
}
  • modules/video_coding/codecs/h264/include/h264.h头文件中声明函数:
class RTC_EXPORT H264Encoder : public VideoEncoder {
 public:
+ static std::unique_ptr<H264Encoder> Create();
  static std::unique_ptr<H264Encoder> Create(const cricket::VideoCodec& codec);
  // If H.264 is supported (any implementation).
  static bool IsSupported();

  ~H264Encoder() override {}
};
  • sdk/android/api/org/webrtc/中修改SoftwareVideoDecoderFactory.javaSoftwareVideoEncoderFactory.java增加对H264的支持:
  public VideoDecoder createDecoder(VideoCodecInfo codecType) {
+   if (codecType.getName().equalsIgnoreCase("H264")) {
+     return new LibH264Decoder();
+   }
    ...
    return null;
  }

  static VideoCodecInfo[] supportedCodecs() {
    List<VideoCodecInfo> codecs = new ArrayList<VideoCodecInfo>();
    
+   codecs.add(new VideoCodecInfo("H264", new HashMap<>()));
    codecs.add(new VideoCodecInfo("VP8", new HashMap<>()));
    ...
    return codecs.toArray(new VideoCodecInfo[codecs.size()]);
  }
  public VideoEncoder createEncoder(VideoCodecInfo info) {
+   if(info.name.equalsIgnoreCase("H264")) {
+     return new LibH264Encoder();
    }
    ...
    return null;
  }

  static VideoCodecInfo[] supportedCodecs() {
    List<VideoCodecInfo> codecs = new ArrayList<VideoCodecInfo>();

+   codecs.add(new VideoCodecInfo("H264", new HashMap<>()));
    codecs.add(new VideoCodecInfo("VP8", new HashMap<>()));
    ...
    return codecs.toArray(new VideoCodecInfo[codecs.size()]);
  }

增加编译支持

sdk/android/BUILD.gn中关联上述新加逻辑:

+ rtc_android_library("libH264_java") {
+   visibility = [ "*" ]
+   sources = [
+     "api/org/webrtc/LibH264Decoder.java",
+     "api/org/webrtc/LibH264Encoder.java",
+   ]
+   deps = [
+     ":base_java",
+     ":video_api_java",
+     ":video_java",
+     "//rtc_base:base_java",
+   ]
+ }

  rtc_android_library("libvpx_vp9_java") {
    visibility = [ "*" ]
    sources = [
      "api/org/webrtc/LibvpxVp9Decoder.java",
      "api/org/webrtc/LibvpxVp9Encoder.java",
    ]
    deps = [
      ":base_java",
      ":video_api_java",
      ":video_java",
      "//rtc_base:base_java",
    ]
  }

...

+  rtc_library("libH264_jni") {
+   visibility = [ "*" ]
+   allow_poison = [ "software_video_codecs" ]
+   sources = [ "src/jni/h264_codec.cc" ]
+   deps = [
+     ":base_jni",
+     ":generated_libH264_jni",
+     ":video_jni",
+     "../../modules/video_coding:webrtc_h264",
+   ]
+ }

  rtc_library("libvpx_vp9_jni") {
    visibility = [ "*" ]
    allow_poison = [ "software_video_codecs" ]
    sources = [ "src/jni/vp9_codec.cc" ]
    deps = [
      ":base_jni",
      ":generated_libvpx_vp9_jni",
      ":video_jni",
      "../../modules/video_coding:webrtc_vp9",
    ]
  }

    rtc_library("swcodecs_jni") {
    visibility = [ "*" ]
    allow_poison = [ "software_video_codecs" ]
    deps = [
+     ":libH264_jni",
      ":libvpx_vp8_jni",
      ":libvpx_vp9_jni",
    ]
  }

...

+   generate_jni("generated_libH264_jni") {
+   sources = [
+     "api/org/webrtc/LibH264Decoder.java",
+     "api/org/webrtc/LibH264Encoder.java",
+   ]

+   namespace = "webrtc::jni"
+   jni_generator_include = "//sdk/android/src/jni/jni_generator_helper.h"
+ }

  generate_jni("generated_libvpx_vp9_jni") {
    sources = [
      "api/org/webrtc/LibvpxVp9Decoder.java",
      "api/org/webrtc/LibvpxVp9Encoder.java",
    ]

    namespace = "webrtc::jni"
    jni_generator_include = "//sdk/android/src/jni/jni_generator_helper.h"
  }

编译命令

./tools_webrtc/android/build_aar.py --build-dir Build --arch arm64-v8a --extra-gn-args 'rtc_use_h264=true ffmpeg_branding="Chrome"'

end

WebRTC的音视频数据传输使用的是RTP协议,RTP报文分为报头和载荷两部分,不同类型的载荷有不同的格式,所以就需要单独实现把编码数据打包为RTP报文的逻辑以及从RTP报文解析已编码数据的逻辑。庆幸的是WebRTC以及帮我们处理好了H264的封包解包逻辑,我们只需要添加支持,并把逻辑打包进aar即可,而若要支持H265,则需要自己处理封包解包逻辑,好消息是OWT中包含了H265的支持,我们不用从0️⃣开始。

参考: