WebRTC系列-移动端硬件编码支持Simulcast

写在开头: 本篇文章扩展自Simulcast这篇文章,这篇文章只写了个大概,这里细说实现细节;以下的功能实现都是在M76版本源码。

联播在H264EncoderImpl 实现
在移动端的WebRTC的native源码中H264有两种实现:基于系统的硬件支持的h264和在h264_encoder_impl.cc(video_coding/codecs/h264/)中实现的openH264软件编码;
硬件的Simulcast功能除了本文的描述有一种(这种方案已经在实际羡慕中测试验证),软件H264的实现是基于FFmpeg和openH264的实现:代码可以参考 ;

类和编码器的初始化
 

int32_t H264EncoderImpl::InitEncode(const VideoCodec* inst,
                                    int32_t number_of_cores,
                                    size_t max_payload_size) {
 // ReportInit();
 // 省略一些参数检查代码

// 下面两行代码 获取联播的个数
  int number_of_streams = SimulcastUtility::NumberOfSimulcastStreams(*inst);
  bool doing_simulcast = (number_of_streams > 1);
// 验证联播参数是否合法
  if (doing_simulcast &&
      !SimulcastUtility::ValidSimulcastParameters(*inst, number_of_streams)) {
    return WEBRTC_VIDEO_CODEC_ERR_SIMULCAST_PARAMETERS_NOT_SUPPORTED;
  }
 // 保存一些数据到变量 保存在configurations_
  num_temporal_layers_ = codec_.H264()->numberOfTemporalLayers;

  for (int i = 0, idx = number_of_streams - 1; i < number_of_streams;
       ++i, --idx) {
    ISVCEncoder* openh264_encoder;
    // 创建编码器
    if (WelsCreateSVCEncoder(&openh264_encoder) != 0) {
      // Failed to create encoder.
     // 省略代码
      return WEBRTC_VIDEO_CODEC_ERROR;
    }
    // 将编码器存储在数组中
    encoders_[i] = openh264_encoder;
    // 当simucast层数大于1的时候创建缩小的图像需要缓冲区.
    // 设置开始码率(startBitrate)和最大码率(maxBitrate) 保存在configurations_
   
    //根据每个联播层的配置创建编码器参数。从configurations_中取
    SEncParamExt encoder_params = CreateEncoderParams(i);
    // 初始化编码器
    if (openh264_encoder->InitializeExt(&encoder_params) != 0) {
     // 省略代码
      return WEBRTC_VIDEO_CODEC_ERROR;
    }
    // 省略代码
    ...
  return WEBRTC_VIDEO_CODEC_OK;
}

从上述源码中的 H264EncoderImpl::InitEncode函数可以看到, 真正负责编码工作的对象是 ISVCEncoder* openh264_encoder,在InitEncode函数中如果有多个联播层就去循环创建编码器及初始化编码器,准备联播层缩放缓冲区等参数的初始化;
编码是在Encode函数中:

Simucast编码
编码实现比较复杂代码量较大这里只看核心处理代码:
 

int32_t H264EncoderImpl::Encode(
    const VideoFrame& input_frame,
    const std::vector<VideoFrameType>* frame_types) {
    //代码省略
    ...
  // 遍历存储的编码器,编码对应每一个视频层
  for (size_t i = 0; i < encoders_.size(); ++i) {
    //准备编码输入帧
    pictures_[i] = {0};
   //代码省略
    ...
    // Downscale images on second and ongoing layers.
    if (i == 0) { //代码省略
      ...
    }else{ //缩小第二层和正在进行的层上的图像。
      //代码省略
      ...
    }
    //如果配置次层不需要发送则忽略
    if (!configurations_[i].sending) {continue; }
    //编码关键帧
    if (send_key_frame) {
      //代码省略
      ...=
	    int enc_ret = encoders_[i]->EncodeFrame(&pictures_[i], &info);
	    if (enc_ret != 0) { return WEBRTC_VIDEO_CODEC_ERROR;
    }
    // 将编码图像分割成片段,同时也更新
    // |encoded_image_|.
    RTPFragmentationHeader frag_header;
    RtpFragmentize(&encoded_images_[i], *frame_buffer, &info, &frag_header);

    // 在这种情况下,编码器可以跳过帧以节省带宽
    // |encoded_images_[i]._length| == 0.
    if (encoded_images_[i].size() > 0) {
      //代码省略
      ...=
      encoded_image_callback_->OnEncodedImage(encoded_images_[i],
                                              &codec_specific, &frag_header);
    }
  }
  return WEBRTC_VIDEO_CODEC_OK;
}

上述就是编码多个编码器编码多路视频的简述代码,具体代码可以参看源码实现;

在 Android/iOS 等移动端, H264EncoderImpl 实现的 Simulcast 因 OpenH264 软件编码的缘故, 往往有CPU算力和电池续航方面的不足.

另外, WebRTC iOS编译 FFmpeg 和 OpenH264 依然是一个巨坑. 那么有没有利用硬件编码实现的Simulcast呢.

上述是原文中的话,iOS端在尝试把 rtc_use_h264设置成true,但是会出现其他问题,在谷歌谈论组 WebRTC IOS h264 support!,中有描述和实际编译遇到的问题一致;大致如图:


在webrtc中关于联播还有一个适配器的实现SimulcastEncoderAdapter

 

SimulcastEncoderAdapter 实现

 

基于适配器类实现硬件联播

在chromium blink的代码中有一个类似的实现,具体可以参见Chromium 源码

创建相应工厂

参照blink实现创建工厂类,.h文件:

#include <memory>


#include "rtc_base/system/rtc_export.h"
#include "api/video_codecs/video_encoder_factory.h"


namespace webrtc {

// Creates a new factory that can create the built-in types of video encoders.
// The factory has simulcast support for VP8.
RTC_EXPORT std::unique_ptr<VideoEncoderFactory>
CreateVideoEncoderAdapterFactory(std::unique_ptr<VideoEncoderFactory> hardware_encoder_factory);

}  // namespace webrtc


.m文件实现:

#include "video/video_codec_factory_adapter.h"
//#include "base/memory/scoped_refptr.h"
//#include "base/memory/ptr_util.h"

#include "build/build_config.h"
//#include "media/base/media_switches.h"
//#include "media/engine/internal_decoder_factory.h"
#include "api/video_codecs/video_encoder_software_fallback_wrapper.h"
#include "media/engine/simulcast_encoder_adapter.h"
#include <vector>

#include "absl/memory/memory.h"
#include "absl/strings/match.h"
#include "api/video_codecs/sdp_video_format.h"
#include "api/video_codecs/video_encoder.h"
#include "media/base/codec.h"
#include "media/base/media_constants.h"
#include "media/engine/encoder_simulcast_proxy.h"
#include "media/engine/internal_encoder_factory.h"
#include "rtc_base/checks.h"


namespace webrtc {
//template<typename T, typename... Ts>
//std::unique_ptr<T> make_unique(Ts&&... params)
//{
//    return std::unique_ptr<T>(new T(std::forward<Ts>(params)...));
//}
namespace {

template <typename Factory>
bool IsFormatSupported(const Factory* factory,
                       const webrtc::SdpVideoFormat& format) {
  return factory && format.IsCodecInList(factory->GetSupportedFormats());
}

// Merge |formats1| and |formats2|, but avoid adding duplicate formats.
std::vector<webrtc::SdpVideoFormat> MergeFormats(
    std::vector<webrtc::SdpVideoFormat> formats1,
    const std::vector<webrtc::SdpVideoFormat>& formats2) {
  for (const webrtc::SdpVideoFormat& format : formats2) {
    // Don't add same format twice.
    if (!format.IsCodecInList(formats1))
      formats1.push_back(format);
  }
  return formats1;
}
//bool IsFormatSupported(
//                       const Factory* factory,
//                       const webrtc::SdpVideoFormat& format) {
//  return factory && IsFormatSupported(factory->GetSupportedFormats(), format);
//}
//
 Merge |formats1| and |formats2|, but avoid adding duplicate formats.
//std::vector<webrtc::SdpVideoFormat> MergeFormats(
//    std::vector<webrtc::SdpVideoFormat> formats1,
//    const std::vector<webrtc::SdpVideoFormat>& formats2) {
//  for (const webrtc::SdpVideoFormat& format : formats2) {
//    // Don't add same format twice.
//    if (!IsFormatSupported(formats1, format))
//      formats1.push_back(format);
//  }
//  return formats1;
//}

// This class combines a hardware factory with the internal factory and adds
// internal SW codecs, simulcast, and SW fallback wrappers.
class EncoderAdapter : public webrtc::VideoEncoderFactory {
 public:
   EncoderAdapter(
      std::unique_ptr<webrtc::VideoEncoderFactory> hardware_encoder_factory)
      : hardware_encoder_factory_(std::move(hardware_encoder_factory)) {}

  webrtc::VideoEncoderFactory::CodecInfo QueryVideoEncoder(
      const webrtc::SdpVideoFormat& format) const override {
    const webrtc::VideoEncoderFactory* factory =
          IsFormatSupported(hardware_encoder_factory_.get(), format)
            ? hardware_encoder_factory_.get()
            : &software_encoder_factory_;
    return factory->QueryVideoEncoder(format);
  }

    std::unique_ptr<webrtc::VideoEncoder> CreateVideoEncoder(
      const webrtc::SdpVideoFormat& format) override {
    const bool supported_in_software =
        IsFormatSupported(&software_encoder_factory_, format);
    const bool supported_in_hardware =
        IsFormatSupported(hardware_encoder_factory_.get(), format);

    if (!supported_in_software && !supported_in_hardware)
      return nullptr;

    if (absl::EqualsIgnoreCase(format.name.c_str(),
                                         cricket::kVp9CodecName)) {
      // For VP9 and AV1 we don't use simulcast.
      // return software_encoder_factory_.CreateVideoEncoder(format);
      return hardware_encoder_factory_->CreateVideoEncoder(format);
    }

    if (!supported_in_hardware || !hardware_encoder_factory_.get()) {
      return absl::make_unique<webrtc::SimulcastEncoderAdapter>(
          &software_encoder_factory_, format);
    } else if (!supported_in_software) {
    //这里原来使用std::make_unique,这个api在c++14 中,所以这里替换成absl中的 实现
      return absl::make_unique<webrtc::SimulcastEncoderAdapter>(
          hardware_encoder_factory_.get(), format);
    }

    return absl::make_unique<webrtc::SimulcastEncoderAdapter>(&software_encoder_factory_, format);
  }

  std::vector<webrtc::SdpVideoFormat> GetSupportedFormats() const override {
    std::vector<webrtc::SdpVideoFormat> software_formats =
        software_encoder_factory_.GetSupportedFormats();
    return hardware_encoder_factory_
               ? MergeFormats(software_formats,
                              hardware_encoder_factory_->GetSupportedFormats())
               : software_formats;
  }

 private:
  webrtc::InternalEncoderFactory software_encoder_factory_;
  const std::unique_ptr<webrtc::VideoEncoderFactory> hardware_encoder_factory_;
};
} // namespace
std::unique_ptr<VideoEncoderFactory> CreateVideoEncoderAdapterFactory(
  std::unique_ptr<webrtc::VideoEncoderFactory> hardware_encoder_factory) {
  return absl::make_unique<EncoderAdapter>(std::move(hardware_encoder_factory));
}

}  // namespace webrtc

注意:SimulcastEncoderAdapter在最新代码中是有三个参数的构造方法;

使用方法

//  ios 使用
        auto video_encoder_factory = std::move(native_encoder_factory);
        return [self initWithNativeAudioEncoderFactory:webrtc::CreateBuiltinAudioEncoderFactory()
                             nativeAudioDecoderFactory:webrtc::CreateBuiltinAudioDecoderFactory()
                             nativeVideoEncoderFactory:webrtc::CreateVideoEncoderAdapterFactory(std::move(video_encoder_factory))
                             nativeVideoDecoderFactory:webrtc::CreateBuiltinVideoDecoderFactory()
                                     audioDeviceModule:[self audioDeviceModule]
                                 audioProcessingModule:nullptr];
// android 使用
  cricket::MediaEngineDependencies media_dependencies;
  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);
  auto video_encoder_factory =
      absl::WrapUnique(CreateVideoEncoderFactory(jni, jencoder_factory));
  media_dependencies.video_encoder_factory = 
        CreateVideoEncoderAdapterFactory(std::move(video_encoder_factory));
  media_dependencies.video_decoder_factory =
      absl::WrapUnique(CreateVideoDecoderFactory(jni, jdecoder_factory));
  dependencies.media_engine =
      cricket::CreateMediaEngine(std::move(media_dependencies));

  rtc::scoped_refptr<PeerConnectionFactoryInterface> factory =
      CreateModularPeerConnectionFactory(std::move(dependencies));

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: webrtc-qt-example是一个基于Qt框架开发的WebRTC示例项目。 WebRTC是一种开源的实时通信技术,能够支持音频、视频和数据的实时传输。它通过浏览器之间的连接,实现了高质量的实时通信。 webrtc-qt-example的目的是展示如何使用Qt进行WebRTC开发。Qt是一套跨平台的C++应用程序开发框架,它提供了丰富的工具和库,使开发者能够快速构建可靠的应用程序。 这个示例项目提供了一些基本的功能和界面,使开发者能够了解和学习如何将WebRTC集成到Qt应用程序中。它包含了常见的WebRTC功能,如媒体流捕获、媒体流传输、信令交换等。 通过webrtc-qt-example,开发者可以学习到如何使用Qt的多媒体模块来捕获音频、视频和媒体设备。同时,也可以学习到如何使用Qt的网络模块来进行实时信令交换和流传输。 这个示例项目还提供了一些简单的界面,方便开发者进行测试和调试。开发者可以通过该界面实现与其他WebRTC应用的通信,例如建立视频通话、音频通话等。 总之,webrtc-qt-example是一个非常实用的示例项目,可以帮助开发者快速上手并掌握WebRTC在Qt中的开发。 ### 回答2: webrtc-qt-example是一个基于Qt框架的WebRTC示例应用程序。WebRTC是一种开源项目,它提供了在浏览器之间进行实时通信的能力,包括视频和音频的传输。而webrtc-qt-example则是将这种技术集成到Qt应用程序中的一个示例。 在webrtc-qt-example中,它使用了Qt的多媒体框架和WebRTC提供的API来实现音视频的传输和显示。通过使用WebRTC的API,webrtc-qt-example可以建立点对点的连接,进行音频和视频的实时传输。 webrtc-qt-example中的代码结构清晰,易于理解和扩展。它提供了一些基本的功能,如建立连接、发送和接收音视频流、呼叫取消等。开发者可以根据自己的需求来对这些功能进行定制和扩展。 此外,webrtc-qt-example还支持一些高级特性,如媒体设备的选择、音视频的编码和解码等。开发者可以通过修改代码来选择不同的媒体设备,并且可以使用不同的编码和解码算法来满足自己的需求。 总之,webrtc-qt-example是一个很棒的WebRTC示例应用程序,它可以帮助开发者快速了解和使用WebRTC技术。无论是为了实现实时视频通话、视频会议还是其他需要音视频传输的应用场景,webrtc-qt-example都提供了一个良好的起点,帮助开发者快速上手并实现自己的需求。 ### 回答3: webrtc-qt-example是一个基于Qt框架和WebRTC技术的示例应用。WebRTC是一种用于在Web浏览器上实现实时通信的开源项目,它提供了一套丰富的API和协议,可以实现音视频通话、数据传输以及屏幕共享等功能。 webrtc-qt-example利用Qt框架提供的跨平台能力,结合WebRTC技术,展示了在Qt应用中如何实现实时通信功能。这个示例应用具有以下特点和功能: 1. 界面友好:webrtc-qt-example使用Qt的GUI绘制工具,具有美观、直观的用户界面,便于用户操作和使用。 2. 实时通信:webrtc-qt-example内置了WebRTC的音视频通信功能,可以实现实时的语音和视频通话,支持两个或多个用户之间的通信。 3. 数据传输:除了音视频通话,webrtc-qt-example还支持在通话中传输数据。可以通过编写代码,实现实时文本传输或共享文件等功能。 4. 屏幕共享:webrtc-qt-example还支持屏幕共享功能,可以将自己的屏幕内容分享给其他用户,实现远程协助或在线教育等应用场景。 通过webrtc-qt-example的学习和实践,开发者可以了解并深入理解WebRTC技术的使用方法,以及在Qt框架中的应用。同时,借助webrtc-qt-example提供的示例代码和API文档,开发者可以进一步开发出更加复杂和功能丰富的实时通信应用,满足不同领域的需求。 总之,webrtc-qt-example是一个基于Qt框架和WebRTC技术的示例应用,具备实时音视频通话、数据传输和屏幕共享等功能,适用于开发者学习、实践和开发基于WebRTC的实时通信应用程序。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值