上篇文章分析过发送自定义视频的可能实现方式, 看上去可行的是自定义编码器的方法. 现在具体讲讲如何实现.
webrtc默认的视频发送流程:
视频捕获--> 视频编码-->视频发送.
我们要做的是停止捕获, 伪造编码,准备自定义视频数据, 发送视频.
流程还是webrtc的流程, 只是替换了流程中的一部分.
如何替换编码器呢? 我们需要一个假的编码器, 它有编码器类的所有接口, 但是不做实际编码动作. 这样的类webrtc自带一个FakeVP8Encoder(也可以用修改vp8的类, 然后注释掉其中的encode函数).
webrtc留给用户的接口类有PeerConnection/ PeerConnectionFactory等, 这两个类是核心, 所以其它操作的源头. 自定义实现自然要从这入口开始.
准备替换:
基于webrtc的应用基本都要创建PeerConnection/ PeerConnectionFactory两个类的实例. 创建factory的时候可以传入VideoEncoderFactory, 这是自定义encoder的开始.
this->peerConnectionFactory = webrtc::CreatePeerConnectionFactory(
this->networkThread.get(),
this->workerThread.get(),
this->signalingThread.get(),
nullptr /*default_adm*/,
webrtc::CreateBuiltinAudioEncoderFactory(),
webrtc::CreateBuiltinAudioDecoderFactory(),
//webrtc::CreateBuiltinVideoEncoderFactory(),
webrtc::test::CreateFakeVideoEncoderFactory(), // 自定义encoder的开始.
webrtc::CreateBuiltinVideoDecoderFactory(),
nullptr /*audio_mixer*/,
nullptr /*audio_processing*/);
然后来看 webrtc::test::CreateFakeVideoEncoderFactory()的实现:
std::unique_ptr<FakeVideoEncoderFactory> CreateFakeVideoEncoderFactory() {
return std::make_unique<FakeVideoEncoderFactory>();
}
然后看FakeVideoEncoderFactory类的实现:
class RTC_EXPORT FakeVideoEncoderFactory : public VideoEncoderFactory {
public:
FakeVideoEncoderFactory();
// VideoEncoderFactory implementation
std::vector<SdpVideoFormat> GetSupportedFormats() const override;
VideoEncoderFactory::CodecInfo QueryVideoEncoder(
const SdpVideoFormat& format) const override;
std::unique_ptr<VideoEncoder> CreateVideoEncoder(
const SdpVideoFormat& format) override;
// add by yyq
const std::unique_ptr<TaskQueueFactory> task_queue_factory_; // 是因为要创建一个task用于连续发送自定义视频数据.
};
看看CreateVideoEncoder的实现:
std::unique_ptr<VideoEncoder> FakeVideoEncoderFactory::CreateVideoEncoder(
const SdpVideoFormat& format) {
return std::make_unique<test::FakeVP8Encoder>(Clock::GetRealTimeClock(), *task_queue_factory_);
}
这里我们就实现了用FakeVP8Encoder替换系统默认的Encoder. 这样我们可以在我们自定义的Encoder里边实现想要的操作.
下边简单看下具体实现:
主要是创建了一个FakeVP8Encoder. 简单看下该类的声明:
class FakeVP8Encoder : public FakeEncoder {
public:
// explicit FakeVP8Encoder(Clock* clock);
explicit FakeVP8Encoder(Clock* clock, TaskQueueFactory& task_queue_factory);
....
}
该类继承自FakeEncoder:
class FakeEncoder : public VideoEncoder {
public:
// explicit FakeEncoder(Clock* clock);
explicit FakeEncoder(Clock* clock, TaskQueueFactory& task_queue_factory);
int32_t Encode(const VideoFrame& input_image,
const std::vector<VideoFrameType>* frame_types) override;
int32_t RegisterEncodeCompleteCallback(
EncodedImageCallback* callback) override;
// add by yyq
int32_t ForwardFrame();
...
// add by yyq
private:
RepeatingTaskHandle frame_encoder_task_;
rtc::TaskQueue task_queue_;
// const std::unique_ptr<webrtc::TaskQueueFactory> tq_factory_;
}
来看看FakeEncoder里边的关键函数 Encode 和 RegisterEncodeCompleteCallback:
Encode函数负责编码, 边把编码后的数据通过RegisterEncodeCompleteCallback函数投递给后续处理者, 就是要调用后续模块进行发送.
我们要修改的点就在这里了.
1. 我们的Encode函数由于是fake encode, 实现自然是没有去实际编码, 我们完全可以随意修改, 自定义视频数据, 然后发送出去.
2. 创建一个定时任务, 用于解析来自文件的视频码流并通过RegisterEncodeCompleteCallback函数注册的处理对象, 投递给后续处理逻辑即可. 这样就完美替换掉了编码数据以及使用了webrtc原始的发送流程.