由于H265受版权的影响,浏览器都不愿意支持,所以webrtc不能实现H265的解码播放。但是工作中需要h265去解决带宽问题,所以要解决浏览器不支持H265播放。从度娘上了解,可以通过发送sctp替换rtp包(webrtc的数据通道可以走三种协议:1、SCTP;2、RTP;3、QUIC。这里先研究第一种)。web端接收数据,然后解码、显示。
目前谷歌支持:
WebCodecs(browserInfo.type.toLowerCase()===’chrome’ && browserInfo.version >= 107&&(location.protocol===’https:’||location)
MSE(isTypeSupported(‘video/mp4; codecs=”hev1.1.6.L123.b0″‘))
ffmpeg wasm,本文讲解才用ffmpeg wasm软解。
SCTP
SCTP的全称是Stream Control Transmission Protocol,它是一种传输协议,默认使用5000端口,在TCP/IP协议栈中所处的位置和TCP、UDP类似,同时具备TCP和UDP的特征。
sdp交互
offer:
a=end-of-candidates
m=application 9 UDP/DTLS/SCTP webrtc-datachannel
c=IN IP4 23.101.8.213
a=sendrecv
a=sctp-port:5000
a=mid:data
a=ice-ufrag:ePgh
a=ice-pwd:ZURiB67/xs69E76aOa7JDw
a=ice-options:trickle
a=fingerprint:sha-256 EF:7A:50:9C:05:8C:EF:84:4D:72:B2:74:30:BA:FD:82:76:D1:C3:FE:0C:A0:10:43:B8:6C:B2:ED:B3:F7:77:8B
a=setup:actpass
a=candidate:1 1 udp 2013266431 23.101.8.213 41901 typ host
a=end-of-candidates
answer:
m=application 9 UDP/DTLS/SCTP webrtc-datachannel
c=IN IP4 0.0.0.0
a=sendrecv
a=ice-pwd:518cb4fc626f82bef2ada4e9221dfb50
a=ice-ufrag:d5484fed
a=mid:data
a=setup:active
a=sctp-port:5000
a=max-message-size:1073741823
static void onapplication(void* param, uint32_t track,/* uint32_t object, */int width, int height, const void* extra, size_t bytes)
{
ice_session_t* s = (ice_session_t*)param;
// 6.2. SDP Parameters
static const char* pattern =
"m=application 9 UDP/DTLS/SCTP webrtc-datachanneln"
"a=mid:2n"
"a=sctp-port:5000n"
"a=sendrecvn";
"a=setup:passiven";
strcat((char*)s->app.sender.buffer, pattern);
int n = snprintf(s->app.sdp, sizeof(s->app.sdp), "%s", (char*)s->app.sender.buffer);
n += ice_transport_getsdp(s->avt, s->app.stream, (char*)s->app.sender.buffer + n, sizeof(s->app.sender.buffer) - n);
}
数据传输
DataChannel的数据通过boringssl加密后再通过udp发送给对端,发送datachannel数据和音视频数据的是同一个socket。音视频数据传输使用了RTP协议,通过NACK和FEC的策略来抗丢包,而DataChannel底层使用了SCTP(stream control transmission protocol)协议,SCTP和TCP类似,是一个具备流量控制、拥塞控制的可靠性传输协议,因此无需再采取其它措施即可实现DataChannel数据的可靠传输。webrtc采用了第三方开源库usrsctplib来实现SCTP协议。
注:usrsctp提供了SCTP的UDP封装,但我们需要这些消息封装在DTLS中,实际由我们之前交互的DTLS同个socket发送/接收,而不是由usrscp本身……因此,我们使用AF_CONN方法。
demo
int ice_transport_send_sctp(struct ice_transport_t* avt, int streamid, uint64_t time, int flags ,const void* data, int bytes)
{
int n = RTP_FIXED_HEADER + bytes;
unsigned char* ptr = (uint8_t*)calloc( 1,n+1);
rtp_header_t header;
memset(&header,0,sizeof(rtp_header_t));
header.timestamp = 1667895211;
header.v = RTP_VERSION;
header.pt = 100;
header.ssrc = bytes;
nbo_write_rtp_header(ptr, &header);
memcpy(ptr + RTP_FIXED_HEADER, data, bytes);//data 265的裸数据
janus_dtls_wrap_sctp_data(avt->dtls, "doc-datachannel", "udp",0, ptr, n);
free(ptr);//后面优化释放
return -1;
}
web接收
接收数据,解析rtp包头:
function ReadBig32(array, index) {
return ((array[index] << 24) |
(array[index + 1] << 16) |
(array[index + 2] << 8) |
(array[index + 3]));
}
function initH265DC(pc, player) {
console.log("initH265DC", Date());
bFindFirstKeyFrame = false;
bRecH265 = false;
isKeyFrame = false;
receivet1 = new Date().getTime();
h265DC = pc.createDataChannel("webrtc-datachannel");
// var ctx = canvas.getContext("2d");
console.log("initH265DC0", h265DC);
h265DC.onmessage = function (event) {
// console.log("receive message: ",event.data.slice(12));
//console.log("receive message: ", buf2hex(event.data));
let data = new Uint8Array(event.data);
version = ((data[0] & 0xC0) >>> 6);
padding = ((data[0] & 0x20) >>> 5);
itemCount = (data[0] & 0x1F);
packetType = data[1];
let pts = ReadBig32(data, 4);
let len = ReadBig32(data, 8);
console.log("initH265DC0", version,padding ,itemCount,packetType,pts,len);
//console.log("receive message: ", buf2hex(event.data.split(12)));event.data.byteLength-12
var req = {
t: ksendPlayerVideoFrameReq,
l: len,
d: data.subarray(12).buffer
};
player.postMessage(req, [req.d]);
};
参考
- [1] github
- [2] flv-h265.js、webrtc_H265player.juans
- [3] C/C++面向WebAssembly编程
作者:aliveyun
版权声明:本文内容转自互联网,本文观点仅代表作者本人。本站仅提供信息存储空间服务,所有权归原作者所有。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至1393616908@qq.com 举报,一经查实,本站将立刻删除。