webrtc服务端如何录像

1 前言

当前基于sfu服务的webrtc通信模式下,服务端录像和服务端旁路引流,是后端服务的一个重要需求。

本文介绍webrtc服务端如何进行录像,文章通过几部分进行讨论:

  • mediasoup sfu的旁路录像

  • srs webrtc的旁路录像

  • webrtc录像的格式讨论

2 mediasoup webrtc的旁路录像

基本的架构图如下:

如上图,这里主要讨论,如何建立一个mediasoup mcu服务,从sfu中拉取用户A和用户B的音视频流,并且录像后,上传到对应的文件存储系统(如阿里云,腾讯云等的存储系统)。

这里介绍开源cpp_streamer通过mediasoup webrtc拉流组件,拉取音频(opus)+视频流(H264 baseline),并存储成mpegts格式。

cpp_streamer开源自己构造一个简单的PeerConnection类,实现:

  • sdp 信息解析

  • stun 协商

  • dtls 协商

  • rtp/rtcp报文传输

  • 自定义快速jitterbuffer的实现

  • H264 in rtp的组装/解封装

通过以上的功能,构造mspull组件,也就是mediasoup pull拉流组件,实现webrtc的拉流,过内部jitterbuffer组装后,输出H264和opus。

这里录像采用的格式是mpegts,选取的原因是mpegts支持opus音频编码的封装。

2.1 代码实现

基于cpp_streamer的代码实现,其实比较简单,只需要3个组件,并且把他们联结在一起:

  • mspull: mediasoup拉流组件,把对应的音视频流拉取过来;

  • timesync: 音视频时间同步组件,因为webrtc的音视频流,rtp中的时间戳是完全不同步的。timesync组件帮助实现对音视频时间戳进行更新,让其同步;

  • mpegtsmux: mpegts封装组件,将拉取的音视频流(H264+Opus)封装成mpegts格式;

demo代码在: https://github.com/runner365/cpp_streamer/blob/v1.1/src/tools/mediasoup_pull_demo.cpp

cpp 复制代码
class MsPull2MpegtsStreamerMgr: public StreamerReport, public CppStreamerInterface
{
public:
    //src_url: 为mediasoup sfu对应音视频的信息地址
    // "https://xxxxx.com?roomId=100&userId=1000&vpid=xxxxx&apid=xxxx" 
    // 其中roomId: 房间id; vpid是video的producerId; apid是audio的producerId
    //output_ts: 要存的输出文件
    MsPull2MpegtsStreamerMgr(const std::string& src_url, 
            const std::string& output_ts):ts_file_(output_ts)
                                           , src_url_(src_url)
    {
    }
    virtual ~MsPull2MpegtsStreamerMgr()
    {
        mspull_ready_ = false;
    }

    int MakeStreamers(uv_loop_t* loop_handle) {
        loop_ = loop_handle;

        //构造一个mspull组件,拉去mediasoup sfu的音视频流
        mspull_streamer_ = CppStreamerFactory::MakeStreamer("mspull");
        mspull_streamer_->SetLogger(logger_);
        mspull_streamer_->SetReporter(this);

        //构造一个mpegts封装组件,将音视频流封装成mpegts格式
        ts_streamer_ = CppStreamerFactory::MakeStreamer("mpegtsmux");
        ts_streamer_->SetLogger(logger_);
        ts_streamer_->SetReporter(this);
        
        //构造一个timesync组件,实现音频与视频时间戳的同步
        timesync_streamer_ = CppStreamerFactory::MakeStreamer("timesync");
        timesync_streamer_->SetLogger(logger_);
        timesync_streamer_->SetReporter(this);

        //将mspull组件,timersync组件,mpegtsmux组件联结在一起
        // mspull--->timesync--->mpegtsmux--->this(存储mpegts成文件)
        mspull_streamer_->AddSinker(timesync_streamer_);
        timesync_streamer_->AddSinker(ts_streamer_);
        ts_streamer_->AddSinker(this);

        return 0;

    }
    //接收mpegtsmux输出的mpegt数据,存成mpegts的文件。
    virtual int SourceData(Media_Packet_Ptr pkt_ptr) override {
        FILE* file_p = fopen(ts_file_.c_str(), "ab+");
        if (file_p) {
            fwrite(pkt_ptr->buffer_ptr_->Data(), 1, pkt_ptr->buffer_ptr_->DataLen(), file_p);
            fclose(file_p);
        }

        return 0;
    }
}

3 Srs的旁路录像

Srs是强大的开源多媒体流服务器,支持webrtc sfu功能,并且还支持rtc2rtmp的功能(opus转码成aac),opus转码成aac对cpu的消耗不小,一个转码大概消耗2%左右的cpu。

如果用户接入路数不多,不考虑性能问题,也直接考虑用Srs配置rtc2rtmp,配置hls的录像,直接用srs实现录像功能。

本文主要考虑性能问题,介绍旁路录像的方案,也就是启动一个服务,从Srs拉取webrtc的音视频流,并保存成mpegts文件。

Srs支持Whep标准的webrtc拉流接口,这里cpp_streamer实现对应的whep拉流组件,对其拉流获取音视频,并通过mpegtsmux组件实现mpegts对音视频流(H264+Opus)的封装。

3.1 代码实现

基于cpp_streamer的代码实现,其实比较简单,只需要3个组件,并且把他们联结在一起:

  • whep: whep拉流组件,把对应srs webrtc的音视频流拉取过来;

  • mpegtsmux: mpegts封装组件,将拉取的音视频流(H264+Opus)封装成mpegts格式;

demo代码地址:

https://github.com/runner365/cpp_streamer/blob/v1.1/src/tools/whep_srs_demo.cpp

cpp 复制代码
class Whep2MpegtsStreamerMgr: public StreamerReport, public CppStreamerInterface
{
public:
    //src_url: srs的whep地址
    // eg: "http://10.0.8.5:1985/rtc/v1/whip-play/?app=live&stream=1000"
    //output_ts: 录像文件,mpegts格式
    Whep2MpegtsStreamerMgr(const std::string& src_url, 
            const std::string& output_ts):ts_file_(output_ts)
                                           , src_url_(src_url)
    {
    }
    virtual ~Whep2MpegtsStreamerMgr()
    {
        whep_ready_ = false;
    }

    int MakeStreamers(uv_loop_t* loop_handle) {
        loop_ = loop_handle;
        //whep组件:从srs拉流webrtc音视频流
        whep_streamer_ = CppStreamerFactory::MakeStreamer("whep");
        whep_streamer_->SetLogger(logger_);
        whep_streamer_->SetReporter(this);
        //mpgetsmux组件: 把H264+Opus保存为mpegts格式
        ts_streamer_ = CppStreamerFactory::MakeStreamer("mpegtsmux");
        ts_streamer_->SetLogger(logger_);
        ts_streamer_->SetReporter(this);

        //联结whep组件和mpegtsmux组件,
        // whep-->mpegtsmux-->this(保存mpegts文件)
        whep_streamer_->AddSinker(ts_streamer_);
        ts_streamer_->AddSinker(this);

        return 0;
    }
    
    //保存mpegts文件
    virtual int SourceData(Media_Packet_Ptr pkt_ptr) override {
        FILE* file_p = fopen(ts_file_.c_str(), "ab+");
        if (file_p) {
            fwrite(pkt_ptr->buffer_ptr_->Data(), 1, pkt_ptr->buffer_ptr_->DataLen(), file_p);
            fclose(file_p);
        }

        return 0;
    }
}

4 录像格式讨论

本文的录像demo都采用mpegts,而且保存的编码格式都是H264+Opus.

并没有支持视频编码: Vp8/Vp9/Av1。

这里讨论如果做兼容各种编码的录像,在不转码的情况下(视频转码非常消耗服务器性能),采用什么封装格式比较推荐。

当前支持codec类型较多的封装格式有: Mp4, Mkv。

都能支持H264/H265/Vp8/Vp9/Av1。

4.1 Mp4(fMp4)

对于流媒体格式,Mp4不能很好的支持动态连续的录像,不过fMp4格式是能支持动态的持续的添加音视频流。

今后的文章会专门讨论fMp4用了哪些mp4 box来支持动态持续添加音视频流,专门做一期详解。

4.2 Mkv

MKV(Matroska Video File)是一种Matroska媒体格式的多媒体封装格式(Multimedia Container Format,简称MCF)。Matroska来自于俄语,影射俄罗斯娃娃,就是下面这个啦,表示一层包着另外一层。

可以支持动态持续添加音视频流,并且支持H264/H265/Vp8/Vp9/Av1。

今后的文章会专门介绍Mkv的格式封装,MKV采用可扩展二进制元语言EBML(Extensible Binary Meta Language)来描述其文件结构,EBML用元素(Elements)来描述EBML文档。

今后cpp_streamer会加入,mp4(fmp4), mkv等流行封装的组件。

后面会专门有后续博文介绍:

  • mp4/fmp4格式详解

  • mkv格式详解

相关推荐
大佐不会说日语~2 天前
WebRTC技术实现简易直播平台
webrtc
YRYDZFtyVKg4 天前
光伏MPPT仿真之扰动观察法探索
webrtc
Knight_AL9 天前
WebRTC / HLS / HTTP-FLV 的本质区别与选型指南
网络协议·http·webrtc
runner365.git9 天前
webrtc推流能成为直播的主要方案吗?
webrtc
XHW___00110 天前
RTP/RTCP 基本知识
webrtc·rtp/rtcp
runner365.git10 天前
语言接入大模型,websocket还是webrtc?
websocket·网络协议·webrtc
好多渔鱼好多13 天前
【流媒体协议】WebRTC 技术详解
webrtc
txp玩Linux13 天前
webrtc降噪模块NS源码解析(1)
webrtc
鲲鹏混子鱼13 天前
WebRTC P2P信令服务架构设计文档
网络协议·webrtc·p2p