webrtc 支持H265(二) ZLMediaKit通过datachannel通道转发视频流

news/2024/11/17 6:34:33/

webrtc datachannel的协商

ZLMediaKit支持webrtc接入,按正常流程,第一步需要协商出音视频通道。
但方案是只协商出datachannel,sdp如下:

web 发起offer:

v=0^M
o=- 7161281774595815373 2 IN IP4 127.0.0.1^M
s=-^M
t=0 0^M
a=group:BUNDLE 0^M
a=extmap-allow-mixed^M
a=msid-semantic: WMS^M
m=application 9 UDP/DTLS/SCTP webrtc-datachannel^M
c=IN IP4 0.0.0.0^M
a=ice-ufrag:2rgo^M
a=ice-pwd:UpLdvvTbYzdPeYmgLBiCD15D^M
a=ice-options:trickle^M
a=fingerprint:sha-256 25:31:BD:29:3C:FB:50:4E:F5:32:A8:61:7A:D2:75:E3:D0:BA:8E:F7:B8:D9:63:8B:AA:31:26:81:94:A9:D1:B3^M
a=setup:actpass^M
a=mid:0^M
a=sctp-port:5000^M
a=max-message-size:262144^M

ZLMediaKit响应的answer:

v=0
o=- 7161281774595815373 2 IN IP4 172.16.63.52
s=-
t=0 0
a=group:BUNDLE 0
a=msid-semantic: WMS
a=ice-lite
m=application 8000 UDP/DTLS/SCTP webrtc-datachannel
c=IN IP4 172.16.63.52
a=rtcp:8000 IN IP4 172.16.63.52
a=ice-ufrag:zlm_1
a=ice-pwd:QXOSSrdX7wKA6v6OZWWnEcFy
a=ice-options:trickle
a=fingerprint:sha-256 47:99:1F:A6:6F:B0:0C:E8:1F:A3:C0:E2:CD:D7:9B:C9:A3:33:67:50:E4:74:2A:73:68:98:DC:09:F3:78:71:AB
a=setup:passive
a=mid:0
a=ice-lite
a=sctp-port:5000
a=candidate:udpcandidate 1 udp 120 172.16.63.52 8000 typ host
a=candidate:tcpcandidate 1 tcp 115 172.16.63.52 8000 typ host tcptype passive

可以看到sdp中只带一个m行,类型为webrtc-datachannel,在offer的sdp中有一个表示可以发送最大发送消息长度的属性行 a=max-message-size:262144。rtp包的长度不会大于mute,一般是在1500个字节一下,远远小于这个大小。

转发视频流

只有webrtc datachannel通道,ZLMediaKit不会转发视频流。需要修改逻辑,使其也能转发视频流。

编译安装usrcsctp库

ZLMediaKit中的datachannel的实现也是基于usrsctp库,所以首先需要编译安装usrsctp,步骤比较简单这里就不列举。
安装完usrsctp后,再执行ZLMediaKit的cmake时,会打印 -- WebRTC datachannel 功能已打开

逻辑修改

在ZLMediaKit中,WebRtcTransport类负责处理webrtc接入的所有流程及媒体流的转发。

  1. sdp协商成功后,接下的流程就是进行DTLS的协商,void WebRtcTransport::OnDtlsTransportConnected就是DTLS协商成功后的回调。
  2. DTLS协商成功后,接下来就是收发媒体流(如果协商了音视频通道)和自定义消息(如果协商了datachannel通道)。
  3. 在前面一篇文章提到过,data channel通道基于DTLS,处理data channel消息的堆栈如下:

static int onRecvSctpData
SctpAssociation::ProcessSctpData
WebRtcTransport::OnDtlsTransportApplicationDataReceived
WebRtcTransportImp::OnDtlsTransportApplicationDataReceive
DtlsTransport::ProcessDtlsData

sctp消息都会到OnRecvSctpData处理。

  1. usrsctp也有一个sctp连接成功的回调,OnSctpAssociationConnected,在这个回调中就表示可以发送sctp消息了,那么在该回调中添加如下转流逻辑
void WebRtcPlayer:OnSctpAssociationConnected(RTC::SctpAssociation *sctpAssociation) {auto playSrc = _play_src.lock();if(!playSrc){onShutdown(SockException(Err_shutdown, "rtsp media source was shutdown"));return ;}if (isOnlyDatachannel()) {playSrc->pause(false);_reader = playSrc->getRing()->attach(getPoller(), true);weak_ptr<WebRtcPlayer> weak_self = static_pointer_cast<WebRtcPlayer>(shared_from_this());weak_ptr<Session> weak_session = static_pointer_cast<Session>(getSession());_reader->setGetInfoCB([weak_session]() { return weak_session.lock(); });_reader->setReadCB([weak_self](const RtspMediaSource::RingDataType &pkt) {auto strong_self = weak_self.lock();if (!strong_self) {return;}size_t i = 0;pkt->for_each([&](const RtpPacket::Ptr &rtp){if (TrackVideo == rtp->type) {strong_self->SendSctpMessage((uint8_t*)rtp->data(),rtp->size());}});});_reader->setDetachCB([weak_self]() {auto strong_self = weak_self.lock();if (!strong_self) {return;}strong_self->onShutdown(SockException(Err_shutdown, "rtsp ring buffer detached"));});}
}

在只有datachannel通道时,才从src的RingBuffer中获取一个RingReader,开始转流。

优化发送缓存区大小

usrsctp的IO模式被设置成非阻塞IO,默认的发送缓存的大小为262144。如果缓存区满了,那么就会报错Resource temporarily unavailable

在码率和分辨率比较高时,RingBuffer中缓存的GOP大小可能远远大于262144,在一开始转流时就填满了发送缓冲区,造成图像卡顿。

可以将缓存区的大小改大,在SctpAssociation::SctpAssociation构造函数中加入如下代码:

//设置发送缓存区
//默认的buffer大小为262144,将buffer size设置为512000*5
uint32_t bufferSize = 512000*5;
if (usrsctp_setsockopt(this->socket, SOL_SOCKET, SO_SNDBUF, &bufferSize, sizeof(uint32_t))<0) {MS_THROW_ERROR("usrsctp_setsockopt(SO_SNDBUF) failed: %s", std::strerror(errno));
}

不过这样也还是避免不了缓存区被填满的情况,这也是使用webrtc data channel传输视频流的一个弊端。

具体代码看这里


http://www.ppmy.cn/news/668377.html

相关文章

SpringBoot2+Vue2实战(六)登录,注册实现及自定义异常处理,个人信息页面与昵称头像功能实现

一、登录&#xff1a; UserDto Data public class UserDto {private String username;private String password;Alias("nickname")private String nickname;private String avatarUrl; }UserController //登录PostMapping("/login")public Result login(R…

mybatis模拟05

SqlSession 添加 selectOne(String sqlId, Object data) method /*** description 查询一条数据* param sqlId&#xff1a;sql 语句的 id* param data:所需要的数据 * return result 查询结果*/ public Object selectOne(String sqlId, Object data){return null; } 实现 …

【Express】express注意点

express版本 5.x post的body 5.x版本无需安装 body-parser 中间件&#xff0c;只需简单配置即可使用body↓ 码 import express from express const app express() // 配置post的body app.use(express.json()) app.use(express.urlencoded({ extended: true }))app.post(/tes…

苹果6发布时间_2020苹果发布会直播入口+直播时间

阅读本文前&#xff0c;请您先点击上面的“蓝色字体”&#xff0c;再点击“关注”&#xff0c;这样您就可以继续免费收到文章了。每天都有分享&#xff0c;完全是免费订阅&#xff0c;请放心关注。 注&#xff1a;本文转载自网络&#xff0c;不代表本平台立场&#xff0c;仅供读…

苹果iOS 17正式发布!

手机中国新闻】北京时间6月6日凌晨1点&#xff0c;苹果正式召开WWDC开发者大会&#xff0c;全新的iOS 17系统正式发布。 据介绍&#xff0c;iOS 17系统的iMessage新增了check in功能&#xff0c;这是一种便捷、能保护隐私的位置分享功能。它可以告诉你的亲人朋友们你所在的位置…

Vue.js的响应式原理

一、Vue 2 响应式原理 Vue.js是一个渐进式的JavaScript框架&#xff0c;它使用了响应式系统来维护应用程序的状态。响应式系统是Vue.js的核心部分&#xff0c;它使得应用程序能够自动地更新视图&#xff0c;当数据发生变化时。 在Vue.js中&#xff0c;响应式系统使用了一种叫…

消息中间件进阶学习

文章目录 1、RabbitMQ1.1、如何保证消息不丢失&#xff1f;小总结面试快速答法 1.2、消息的重复消费问题面试快速答法 1.3、死信交换机小总结面试快速答法 1.4、消息堆积怎么解决小总结面试快速答法 1.5、集群小总结面试快速答法 2、Kafka2.1、Kafka是如何保证消息不丢失小总结…

JAVA控制台下:商城购物系统(一)数据库设计

前言&#xff1a; 一、数据库设计&#xff1a; /* Navicat MySQL Data TransferSource Server : 127.0.0.1_3306 Source Server Version : 50549 Source Host : 127.0.0.1:3306 Source Database : javashopTarget Server Type : MYSQL Target Se…