清霜辰-博客

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

[TOC]

本文首发地址 https://h89.cn/archives/250.html
最新更新地址 https://gitee.com/chenjim/chenjimblog
本文基于libmediasoupclient 3.2.0 和 webrtc branch-heads/4147(m84)
本文得熟悉相关基础,参考 文1文2

mediasoup H264 支持


安卓 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();

    大致跟 AppRTCDemo PeerConnectionClient.java 相同,AppRTCDemo 支持H264软硬编解码
    其中 .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) 
    CreatePeerConnectionFactoryForJava(...){}
      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);
    //分别是PeerConnectionUtils.java 传入的 encoderFactory 和 decoderFactory  
      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));
    } 
  2. 安卓 mediasoup Native 层 CreateMediaEngine 如下

    Handler::GetNativeRtpCapabilities (libmediasoupclient/src/Device.cpp)  
    -->  std::unique_ptr<PeerConnection> pc(new PeerConnection(privateListener.get(), peerConnectionOptions))  
    (libmediasoupclient/src/Handler.cpp)  
    -->  webrtc::CreateBuiltinVideoEncoderFactory  (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*/);

这里的 CreateBuiltin...Factory 也传到 CreateMediaEngine
从上面 1 2 可以看到 CreateMediaEngine 传入了不同的 Factory,而且实际编解码使用的是后者,参见后文


mediasoup-client-android 中 VideoStreamEncoder 初始化

后文前提是已经能够 使用H264软编码
调用堆栈如下

private void onNewConsumer(...)  (org.mediasoup.droid.lib.RoomClient.java)
-->  public Consumer consume(...)  (org.mediasoup.droid.RecvTransport.java)  
-->  Java_org_mediasoup_droid_RecvTransport_nativeConsume   (mediasoup-client-android\mediasoup-client\src\main\jni\transport_jni.cpp)   
-->  Consumer* RecvTransport::Consume(...)  (libmediasoupclient/src/Transport.cpp)    
-->  RecvHandler::RecvResult RecvHandler::Receive(...)  (libmediasoupclient/src/Handler.cpp)   
-->  this->pc->SetRemoteDescription(PeerConnection::SdpType::OFFER, offer)    
-->  PeerConnection::SetRemoteDescription(...)    (webrtc/src/pc/peer_connection.cc)   
-->  PeerConnection::DoSetRemoteDescription(...)
-->  PeerConnection::ApplyRemoteDescription(...) 
-->  PeerConnection::UpdateSessionState(...)  
-->  PeerConnection::PushdownMediaDescription(...)    
-->  BaseChannel::SetRemoteContent(...)    (webrtc/src/pc/channel.cc)   
-->  VoiceChannel::SetRemoteContent_w(...)
-->  WebRtcVideoChannel::SetSendParameters  (webrtc/src/media/engine/webrtc_video_engine.cc)  
-->  WebRtcVideoChannel::ApplyChangedParams(...) 
-->  WebRtcVideoChannel::WebRtcVideoSendStream::SetSendParameters(...)   
-->  WebRtcVideoChannel::WebRtcVideoSendStream::SetCodec(..)  
-->  WebRtcVideoChannel::WebRtcVideoSendStream::RecreateWebRtcStream(...)  
-->  webrtc::VideoSendStream* Call::CreateVideoSendStream(,)  (webrtc/src/call/call.cc)  
-->  webrtc::VideoSendStream* Call::CreateVideoSendStream(,,) 
-->  VideoSendStream::VideoSendStream(..)  (webrtc/src/video/video_send_stream.cc)  
-->  std::unique_ptr<VideoStreamEncoderInterface> CreateVideoStreamEncoder(..) (webrtc/src/api/video/video_stream_encoder_create.cc)
-->  VideoStreamEncoder::VideoStreamEncoder(...)  (webrtc/src/video/video_stream_encoder.cc)  

webrtc 中 VideoStreamEncoder 初始化

这里基于应用 AppRTCDemo,并在应用中开启H264编解码,相关调用堆栈及说明如下

onCreateSuccess()   (src/main/java/org/appspot/apprtc/PeerConnectionClient.java)  
-->  peerConnection.setLocalDescription(sdpObserver, newDesc)   
-->  Java_org_webrtc_PeerConnection_nativeSetLocalDescription(...)   
(./out/release-build/arm64-v8a/gen/sdk/android/generated_peerconnection_jni/PeerConnection_jni.h)   
-->  PeerConnection::SetLocalDescription (pc/peer_connection.cc)
-->  PeerConnection::DoSetLocalDescription 
-->  PeerConnection::ApplyLocalDescription 
-->  PeerConnection::UpdateSessionState 
-->  PeerConnection::PushdownMediaDescription()  (pc/peer_connection.cc)
-->  BaseChannel::SetLocalContent()   (pc/channel.cc)    
-->  VideoChannel::SetLocalContent_w(...)
-->  BaseChannel::UpdateLocalStreams_w(..)    
-->  WebRtcVideoChannel::AddSendStream(..)      (media/engine/webrtc_video_engine.cc)  
-->  WebRtcVideoChannel::WebRtcVideoSendStream::WebRtcVideoSendStream(..)  
-->  WebRtcVideoChannel::WebRtcVideoSendStream::SetCodec(..)  
-->  WebRtcVideoChannel::WebRtcVideoSendStream::RecreateWebRtcStream()  
-->  webrtc::VideoSendStream* Call::CreateVideoSendStream(..)  (call/call.cc)   
-->  VideoSendStream::VideoSendStream(..)   (video/video_send_stream.cc)   
-->  std::unique_ptr<VideoStreamEncoderInterface> CreateVideoStreamEncoder(..)  (api/video/video_stream_encoder_create.cc)  
-->  VideoStreamEncoder::VideoStreamEncoder()   (video/video_stream_encoder.cc)   

从上面两小节可以看到:mediasoup 和 webrtc VideoStreamEncoder 创建有稍微差别


解码器 H264Encoder 创建

视频帧分发

VideoStreamEncoder::OnFrame (webrtc/src/video/video_stream_encoder.cc)    
-->  VideoStreamEncoder::ReconfigureEncoder()   
-->  encoder_ = settings_.encoder_factory->CreateVideoEncoder(encoder_config_.video_format)    
  (webrtc/src/video/video_stream_encoder.cc)  
//这里的 `settings_` 是在上步创建时赋值,会影响后续 `Encode` 的创建        

另:在 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;
}

mediasoup 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 编码完成回调


webrtc H264Encoder 初始化

VideoStreamEncoder::OnFrame (webrtc/src/video/video_stream_encoder.cc)  
VideoStreamEncoder::MaybeEncodeVideoFrame 
VideoStreamEncoder::ReconfigureEncoder()
VideoEncoderSoftwareFallbackWrapper::InitEncode  (api/video_codecs/video_encoder_software_fallback_wrapper.cc)   
VideoEncoderWrapper::InitEncode  (sdk/android/src/jni/video_encoder_wrapper.cc)    
VideoEncoderWrapper::InitEncodeInternal 
Java_VideoEncoderWrapper_createEncoderCallback  (out/release-build/arm64-v8a/gen/sdk/android/generated_video_jni/VideoEncoderWrapper_jni.h)
//最终使用 JAVA 层 HardwareVideoEncoder
public VideoCodecStatus initEncode(Settings settings, Callback callback)  (sdk/android/src/java/org/webrtc/HardwareVideoEncoder.java)

webrtc 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软编解码 需要修改此处


mediasoup 信令过程

参考链接
https://www.cnblogs.com/WillingCPP/p/13646225.html


openh264 提供编解码相关函数

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

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


webrtc 视频 H264 硬编码

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


webrtc 视频流显示

通过 CallActivity.java 可以看到,用 SurfaceViewRenderer.java 显示 VideoFrame
SurfaceViewRenderer 使用了 SurfaceEglRenderer.java
相关类关系如下图


其它相关文档

当前页面是本站的「Google AMP」版。查看和发表评论请点击:完整版 »