安卓mediasoup webrtc h264 软编解码相关源码分析

Posted 清霜之辰

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了安卓mediasoup webrtc h264 软编解码相关源码分析相关的知识,希望对你有一定的参考价值。

安卓mediasoup webrtc h264 软编解码相关源码分析

本文首发地址 https://blog.csdn.net/CSqingchen/article/details/120218923
最新更新地址 https://gitee.com/chenjim/chenjimblog
本文基于libmediasoupclient 3.2.0 和 webrtc branch-heads/4147(m84)
本文得熟悉相关基础,参考 文1文2

mediasoup H264 支持

  • 打开 rtc_use_h264
    webrtc.gni 中可以看到如下,也就是安卓默认不支持 h264
    rtc_use_h264 = proprietary_codecs && !is_android && !is_ios && !(is_win && !is_clang)
    可以将此处改为 true ,也可以带上编译参数 'rtc_use_h264=true,如下:
    ./tools_webrtc/android/build_aar.py --extra-gn-args 'rtc_use_h264=true'

  • mediasoup-demo-android 的 forceH264
    通过代码我们看到,这个参数只在 UrlFactory.java 中拼接地址使用
    正常情况 如果 url 中 有forceH264=true, 就应该采用H264编码
    如浏览器输入 https://v3demo.mediasoup.org/?forceH264=true&roomId=123456 输出的视频就是H264
    (安卓默认显示黑屏,缺少H264解码器,后文解决)
    但是 mediasoup-demo-android 菜单勾选forceH264了还是 VP8 编码,算是个BUG …


安卓 webrtc 视频采集流程源码分析

webrtc针对视频采集对外主要提供的是VideoCapturer接口,实现类有ScreenCapturerAndroid、FileVideoCapturer和CameraCapturer,分别表示屏幕、文件、摄像头三种不同的视频来源,因为android系统先后提供了camera1.0和camera2.0接口,因此CameraCapturer又用Camera1Capturer和Camera2Capturer两个子类分别表示。
主要类图

视频采集和分发流程如下图

更多细节可以参考原文 webrtc源码分析之视频采集之一, 感谢 Jimmy2012


安卓 mediasoup 为啥没有使用H264硬编解码

  1. 安卓 mediasoup Java层 CreateMediaEngine 如下
    mediasoup-demo-android\\app\\src\\main\\java\\org\\mediasoup\\droid\\lib\\PeerConnectionUtils.java
    VideoEncoderFactory encoderFactory = new DefaultVideoEncoderFactory( mEglBase.getEglBaseContext(), true /* enableIntelVp8Encoder */, true);
    VideoDecoderFactory decoderFactory = new DefaultVideoDecoderFactory(mEglBase.getEglBaseContext());
    mPeerConnectionFactory = builder.setAudioDeviceModule(adm)
            .setVideoEncoderFactory(encoderFactory)
            .setVideoDecoderFactory(decoderFactory)
            .createPeerConnectionFactory();

大致跟原生的 webrtc (AppRTCDemo\\app\\src\\main\\java\\org\\appspot\\apprtc\\PeerConnectionClient.java)相同,原生的支持硬编解码,
其中 .createPeerConnectionFactory(); 相关流程如下
–> createPeerConnectionFactory()
–> JNI_PeerConnectionFactory_CreatePeerConnectionFactory(…) (webrtc/src/sdk/android/src/jni/pc/peer_connection_factory.cc)
–> CreatePeerConnectionFactoryForJava(…) (webrtc/src/sdk/android/src/jni/pc/peer_connection_factory.cc) 部分代码如下

  media_dependencies.task_queue_factory = dependencies.task_queue_factory.get();
  media_dependencies.adm = std::move(audio_device_module);
  media_dependencies.audio_encoder_factory = std::move(audio_encoder_factory);
  media_dependencies.audio_decoder_factory = std::move(audio_decoder_factory);
  media_dependencies.audio_processing = std::move(audio_processor);
  media_dependencies.video_encoder_factory = absl::WrapUnique(CreateVideoEncoderFactory(jni, jencoder_factory));
  media_dependencies.video_decoder_factory = absl::WrapUnique(CreateVideoDecoderFactory(jni, jdecoder_factory));
  dependencies.media_engine = cricket::CreateMediaEngine(std::move(media_dependencies));
  1. 安装 mediasoup Native 层 CreateMediaEngine 如下
    Handler::GetNativeRtpCapabilities (libmediasoupclient/src/Device.cpp)
    –> std::unique_ptr pc(new PeerConnection(privateListener.get(), peerConnectionOptions))
    (libmediasoupclient/src/Handler.cpp)
    –> webrtc::CreateBuiltinVideoEncoderFactory (deps/libmediasoupclient/src/PeerConnection.cpp)
    –> webrtc::CreatePeerConnectionFactory
    –> cricket::CreateMediaEngine(std::move(media_dependencies)) (webrtc/src/api/create_peerconnection_factory.cc)
    –> CreateMediaEngine (webrtc/src/media/engine/webrtc_media_engine.cc)
    –> WebRtcVideoEngine::WebRtcVideoEngine (webrtc/src/media/engine/webrtc_media_engine.cc)

libmediasoupclient/src/PeerConnection.cppPeerConnection 构造函数部分代码如下,我们可以看到音视频编解码器的创建工厂

  this->peerConnectionFactory = webrtc::CreatePeerConnectionFactory(
    this->networkThread.get(),
    this->workerThread.get(),
    this->signalingThread.get(),
    nullptr /*default_adm*/,
    webrtc::CreateBuiltinAudioEncoderFactory(),
    webrtc::CreateBuiltinAudioDecoderFactory(),
    webrtc::CreateBuiltinVideoEncoderFactory(),
    webrtc::CreateBuiltinVideoDecoderFactory(),
    nullptr /*audio_mixer*/,
    nullptr /*audio_processing*/);

从上面1 2 可以看到 CreateMediaEngine 传入了不同的Factory,实际编码使用的是后者,
因此 mediasoup 默认无法支持安卓h264的硬编解码…


webrtc openh264 软编码源码分析

  • VideoStreamEncoder 创建流程如下
    SetRemoteContent_w (webrtc/src/pc/channel.cc)
    –> media_channel()->SetSendParameters(send_params)
    –> WebRtcVideoChannel::SetSendParameters (webrtc/src/media/engine/webrtc_video_engine.cc)
    –> ApplyChangedParams(changed_params)
    –> kv.second->SetSendParameters(changed_params)
    –> SetCodec(params.send_codec)
    –> RecreateWebRtcStream(…)
    –> stream_ = call_->CreateVideoSendStream
    –> CreateVideoSendStream (webrtc/src/call/call.cc)
    –> VideoSendStream
    send_stream = new VideoSendStream(…) (webrtc/src/call/call.cc)
    –> video_stream_encoder_ = CreateVideoStreamEncoder(…) (webrtc/src/video/video_send_stream.cc)
    –> VideoStreamEncoder::VideoStreamEncoder(…) (webrtc/src/video/video_stream_encoder.cc)

  • H264Encoder 创建
    视频帧分发到 VideoStreamEncoder::OnFrame (webrtc/src/video/video_stream_encoder.cc)
    –> ReconfigureEncoder()
    –> encoder_ = settings_.encoder_factory->CreateVideoEncoder(encoder_config_.video_format)
    (webrtc/src/video/video_stream_encoder.cc)
    –> internal_encoder = std::make_unique(internal_encoder_factory_.get(), format) (webrtc\\src\\api\\video_codecs\\builtin_video_encoder_factory.cc)
    –> EncoderSimulcastProxy::EncoderSimulcastProxy (webrtc\\src\\media\\engine\\encoder_simulcast_proxy.cc)
    –> InternalEncoderFactory::CreateVideoEncoder (webrtc/src/media/engine/internal_encoder_factory.cc)
    –> H264Encoder::Create() (webrtc/src/modules/video_coding/codecs/h264/h264.cc)
    –> H264EncoderImpl::H264EncoderImpl (webrtc/src/modules/video_coding/codecs/h264/h264_encoder_impl.cc)

internal_encoder_factory.cc 中我们可以看到有 V8 V9 H264 AV1 Encoder 的创建,如下:

std::unique_ptr<VideoEncoder> InternalEncoderFactory::CreateVideoEncoder(const SdpVideoFormat& format) {
  if (absl::EqualsIgnoreCase(format.name, cricket::kVp8CodecName))
    return VP8Encoder::Create();
  if (absl::EqualsIgnoreCase(format.name, cricket::kVp9CodecName))
    return VP9Encoder::Create(cricket::VideoCodec(format));
  if (absl::EqualsIgnoreCase(format.name, cricket::kH264CodecName))
    return H264Encoder::Create(cricket::VideoCodec(format));
  if (kIsLibaomAv1EncoderSupported &&absl::EqualsIgnoreCase(format.name, cricket::kAv1CodecName))
    return CreateLibaomAv1Encoder();
  return nullptr;
}
  • H264Encoder 初始化
    视频帧分发到 VideoStreamEncoder::OnFrame (webrtc/src/video/video_stream_encoder.cc)
    ReconfigureEncoder()
    –> encoder_->InitEncode(…)
    –> EncoderSimulcastProxy::InitEncode(…) (webrtc/src/media/engine/encoder_simulcast_proxy.cc)
    –> H264EncoderImpl::InitEncode(…) (webrtc/src/modules/video_coding/codecs/h264/h264_encoder_impl.cc)
    其中还会调用 H264EncoderImpl::SetRatesH264EncoderImpl::CreateEncoderParams

  • H264Encoder 编码
    视频帧分发到 VideoStreamEncoder::OnFrame (webrtc/src/video/video_stream_encoder.cc)
    –> MaybeEncodeVideoFrame
    –> VideoStreamEncoder::EncodeVideoFrame
    –> encoder_->Encode(…)
    –> H264EncoderImpl::Encode (webrtc/src/modules/video_coding/codecs/h264/h264_encoder_impl.cc)
    –> encoded_image_callback_->OnEncodedImage 编码完成回调


H264Decoder初始化流程

VideoReceiver2::Decode (modules/video_coding/video_receiver2.cc)
–>
VCMDecoderDataBase::GetDecoder (modules/video_coding/decoder_database.cc)
–>
H264DecoderImpl::InitDecode (modules/video_coding/codecs/h264/h264_decoder_impl.cc)
–>
avcodec_find_decoder (third_party/ffmpeg/libavcodec/allcodecs.c)
其中 av_codec_iterate 会用到 codec_list(在 libavcodec/codec_list.c)
也就是为啥 开启h264软编解码 需要修改此处


openh264 提供编解码相关函数

WelsCreateDecoder;
WelsCreateSVCEncoder;
WelsDestroyDecoder;
WelsDestroySVCEncoder;
WelsGetCodecVersion;
WelsGetCodecVersionEx;

使用 openh264 编解码示例
https://blog.csdn.net/NB_vol_1/article/details/103376649


webrtc 视频 H264 硬编码

安卓设备由于碎片化,早期的版本并不支持硬编码,又存在不同的芯片厂商如高通、MTK、海思、三星等
最终并不是所有安卓设备都支持硬编解码

  • 修改 MediaCodecUtils.javaSOFTWARE_IMPLEMENTATION_PREFIXES
    建议把 "OMX.SEC." 去掉
    因为在 HardwareVideoDecoderFactory.javaPlatformSoftwareVideoDecoderFactory 中,
    MediaCodecUtils.SOFTWARE_IMPLEMENTATION_PREFIXES 包含芯片厂家被加不支持硬编码黑名单了。。。

  • 支持的 H264 的芯片代码前缀如下,参考自 一朵桃花压海棠 博文

private static final String[] supportedH264HwCodecPrefixes = {
    "OMX.qcom.", "OMX.Intel.", "OMX.Exynos."
    ,"OMX.Nvidia.H264."     /*Nexus 7(2012), Nexus 9, Tegra 3, Tegra K1*/
    ,"OMX.ittiam.video."    /*Xiaomi Mi 1s*/
    ,"OMX.SEC.avc."         /*Exynos 3110, Nexus S*/
    ,"OMX.IMG.MSVDX."       /*Huawei Honor 6, Kirin 920*/
    ,"OMX.k3.video."        /*Huawei Honor 3C, Kirin 910*/
    ,"OMX.hisi."            /*Huawei Premium Phones, Kirin 950*/
    ,"OMX.TI.DUCATI1."      /*Galaxy Nexus, Ti OMAP4460*/
    ,"OMX.MTK.VIDEO."       /*no sense*/
    ,"OMX.LG.decoder."      /*no sense*/
    ,"OMX.rk.video_decoder."/*Youku TVBox. our service doesn't need this */
    ,"OMX.amlogic.avc"      /*MiBox1, 1s, 2. our service doesn't need this */
};

其它相关文档

以上是关于安卓mediasoup webrtc h264 软编解码相关源码分析的主要内容,如果未能解决你的问题,请参考以下文章

安卓mediasoup webrtc h264 编解码相关源码分析

安卓mediasoup webrtc h264 编解码相关源码分析

安卓mediasoup输出H264流(支持H264编码)

安卓mediasoup输出H264流(支持H264编码)

安卓mediasoup输出H264流(支持H264编码)

安卓增加 mediasoup webrtc 日志输出