rtp
RTP是一种用于实时传输音视频数据的协议,常用于视频会议、流媒体等场景。
在FFmpeg中,rtpenc
是一个用于将音视频数据封装成RTP(Real-time Transport Protocol)数据包并发送到网络上的编码器。
rtpenc
可以将音视频数据封装成RTP数据包,并通过UDP协议发送到指定的IP地址和端口号。它支持多种音视频编码格式,如H.264、AAC等,并可以设置RTP头部信息、负载类型、时间戳等参数。
ff_rtp_muxer
了解rtpenc,就来看rtp_muxer,rtp_muxer也是由AVOutputFormat定义,位于libavformat/rtpenc.c
,在rtp muxer的定义中,默认的codec格式是AV_CODEC_ID_MPEG4
和AV_CODEC_ID_PCM_MULAW
,rtp_write_packet
可以看到这个rtp packet的封包发送过程。
const AVOutputFormat ff_rtp_muxer = {
.name = "rtp",
.long_name = NULL_IF_CONFIG_SMALL("RTP output"),
.priv_data_size = sizeof(RTPMuxContext),
.audio_codec = AV_CODEC_ID_PCM_MULAW,
.video_codec = AV_CODEC_ID_MPEG4,
.write_header = rtp_write_header,
.write_packet = rtp_write_packet,
.write_trailer = rtp_write_trailer,
.priv_class = &rtp_muxer_class,
.flags = AVFMT_TS_NONSTRICT,
};
这里再说一下AVOutputFormat
:
虽然AVOutputFormat
在libavdevice和libavformat中都存在,但是它们的作用不同。在libavdevice中,AVOutputFormat
用于描述音视频设备的输出格式,而在libavformat中,AVOutputFormat
用于描述音视频文件的输出格式。
因此,AVOutputFormat
在libavdevice和libavformat中的区别在于它们所描述的对象不同。
rtp_write_packet
rtp_write_packet中根据codec_id,调用对应的ff_rtp_send_xxx函数,比如H264对应的是ff_rtp_send_h264_hevc:
static int rtp_write_packet(AVFormatContext *s1, AVPacket *pkt) {
RTPMuxContext *s = s1->priv_data;
AVStream *st = s1->streams[0];
int rtcp_bytes;
int size= pkt->size;
switch(st->codecpar->codec_id) {
case AV_CODEC_ID_AAC:
if (s->flags & FF_RTP_FLAG_MP4A_LATM)
ff_rtp_send_latm(s1, pkt->data, size);
else
ff_rtp_send_aac(s1, pkt->data, size);
break;
case AV_CODEC_ID_H264:
ff_rtp_send_h264_hevc(s1, pkt->data, size);
break;
}
ff_rtp_send_h264_hevc
该函数的主要作用是将H.264/HEVC视频流分割成NAL单元,并将其打包成RTP数据包进行传输。
该函数首先将传入的视频流数据buf1拷贝到RTPMuxContext
结构体中,并设置当前时间戳为s->cur_timestamp。然后,该函数通过调用ff_avc_find_startcode
或ff_avc_mp4_find_startcode
函数找到NAL单元的起始位置,并将NAL单元的数据通过nal_send
函数打包成RTP数据包进行传输。最后,该函数通过调用flush_buffered函数将缓存中的数据进行传输。
需要注意的是,该函数中的s->nal_length_size
表示NAL单元长度的字节数,不为0,buffer就是avcc类型,如果为0,就是annex
格式,根据起始码0x000001
或0x00000001
来找到buffer的结束位置。
void ff_rtp_send_h264_hevc(AVFormatContext *s1, const uint8_t *buf1, int size)
{
const uint8_t *r, *end = buf1 + size;
RTPMuxContext *s = s1->priv_data;
s->timestamp = s->cur_timestamp;
s->buf_ptr = s->buf;
if (s->nal_length_size)
r = ff_avc_mp4_find_startcode(buf1, end, s->nal_length_size) ? buf1 : end;
else
r = ff_avc_find_startcode(buf1, end);
while (r < end) {
const uint8_t *r1;
if (s->nal_length_size) {
r1 = ff_avc_mp4_find_startcode(r, end, s->nal_length_size);
if (!r1)
r1 = end;
r += s->nal_length_size;
} else {
while (!*(r++));
r1 = ff_avc_find_startcode(r, end);
}
nal_send(s1, r, r1 - r, r1 == end);
r = r1;
}
flush_buffered(s1, 1);
}
然后nal_send
将NAL单元发送到RTP流中。NAL单元是视频编码中的基本单元,它包含了视频编码中的一个帧或片段。
nal_send
首先检查NAL单元的大小是否小于等于最大负载大小,如果是,则将NAL单元添加到缓冲区中。如果缓冲区中已经有了一些NAL单元,则将它们作为STAP-A/AP包发送。如果NAL单元的大小大于最大负载大小,则将其分割成多个分片,并将它们作为FU-A包发送。
nal_send
还根据编解码器类型设置了一些标志位,以便在发送NAL单元时进行适当的处理。例如,对于H.264编解码器,如果使用了FF_RTP_FLAG_H264_MODE0标志,则将NAL单元分割成多个分片。
ff_rtp_send_data
ff_rtp_send_data
是rtp用于将数据通过RTP协议发送出去的函数。具体来说,它会构建RTP头部,将数据写入输出流,并更新一些统计信息。
void ff_rtp_send_data(AVFormatContext *s1, const uint8_t *buf1, int len, int m)
{
RTPMuxContext *s = s1->priv_data;
av_log(s1, AV_LOG_TRACE, "rtp_send_data size=%d\n", len);
/* build the RTP header */
avio_w8(s1->pb, RTP_VERSION << 6);
avio_w8(s1->pb, (s->payload_type & 0x7f) | ((m & 0x01) << 7));
avio_wb16(s1->pb, s->seq);
avio_wb32(s1->pb, s->timestamp);
avio_wb32(s1->pb, s->ssrc);
avio_write(s1->pb, buf1, len);
avio_flush(s1->pb);
s->seq = (s->seq + 1) & 0xffff;
s->octet_count += len;
s->packet_count++;
}
函数的参数包括一个AVFormatContext
结构体指针s1,一个指向数据缓冲区的指针buf1,数据长度len,以及一个标志位m。其中,s1包含了输出流的相关信息,buf1是要发送的数据,len是数据长度,m表示是否是最后一个数据包。
函数首先从s1中获取RTPMuxContext
结构体指针s,该结构体包含了一些RTP相关的信息。然后,函数会使用avio_w8、avio_wb16、avio_wb32等函数将RTP头写入输出流中。其中,RTP头部的格式如下:
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|V=2|P|X| CC |M| PT | sequence number |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| timestamp |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| synchronization source (SSRC) identifier |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| contributing source (CSRC) identifiers |
| .... |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
其中,V表示协议版本号,P表示是否有填充,X表示是否有扩展头部,CC表示CSRC计数器,M表示是否是最后一个数据包,PT表示负载类型,sequence number表示序列号,timestamp表示时间戳,SSRC表示同步源标识符,CSRC表示贡献源标识符。
接下来,函数会使用avio_write函数将数据写入输出流中,并使用avio_flush函数将输出流中的数据刷新。最后,函数会更新s中的一些统计信息,包括序列号、字节计数和数据包计数。
rtp_mpegts
rtp_mpegts是ffmpeg中支持的唯一一个rtp muxer,通过rtp_mpegts发送音视频数据,可以解决rtp只支持一路流的问题,默认支持的audio codec是PCM_MULAW,video codec是MPEG4,通过acodec和vcodec参数可以指定codec。
比如可以这样发送:
ffmpeg -re -stream_loop -1 -i h264.mp4 -i aac.mp4 -f rtp_mpegts rtp://10.0.1.1:5008
指定codec:
ffmpeg -re -stream_loop -1 -i h264.mp4 -i aac.mp4 -acodec aac -vcodec h264 -f rtp_mpegts rtp://10.0.1.1:5008
h264.mp4 - 只包含h264编码的视频文件
aac.mp4 - 只包含aac编码的音频文件
如果使用rtp发送,则会输出ERROR:“Only one stream supported in the RTP muxer”。
ffmpeg -re -stream_loop -1 -i h264.mp4 -i aac.mp4 -f rtp rtp://10.0.1.1:5008
ff_rtp_mpegts_muxer
const AVOutputFormat ff_rtp_mpegts_muxer = {
.name = "rtp_mpegts",
.long_name = NULL_IF_CONFIG_SMALL("RTP/mpegts output format"),
.priv_data_size = sizeof(MuxChain),
.audio_codec = AV_CODEC_ID_AAC,
.video_codec = AV_CODEC_ID_MPEG4,
.write_header = rtp_mpegts_write_header,
.write_packet = rtp_mpegts_write_packet,
.write_trailer = rtp_mpegts_write_close,
.priv_class = &rtp_mpegts_class,
};
在write_header
函数中,指定codec id为AV_CODEC_ID_MPEG2TS
,这个会在rtp enc中处理,调用rtp_send_mpegts_raw
,发送mpegts数据。文章来源:https://www.toymoban.com/news/detail-688639.html
st->time_base.num = 1;
st->time_base.den = 90000;
st->codecpar->codec_id = AV_CODEC_ID_MPEG2TS;
rtp_ctx->pb = s->pb;
所以,rtp muxer直接对音视频数据封包成rtp payload进行发送,而rtp_mpegts则对音视频数据先进行mpegts封装,然后再封装为rtp payload发送。文章来源地址https://www.toymoban.com/news/detail-688639.html
到了这里,关于FFmpeg rtp & rtp_mpegts的区别的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!