一、引言
由《音视频入门基础:RTP专题(9)——FFmpeg接收RTP流的原理和内部实现》可以知道,FFmpeg接收RTP流时,其源码内部会调用rtp_read函数。而rtp_read函数内部会通过recvfrom函数接收基于UDP的RTP音视频数据。一般情况下,每通过一次recvfrom函数接收到数据算一个RTP packet。然后FFmpeg会通过rtp_parse_packet_internal函数解析该RTP packet。
二、rtp_parse_packet_internal函数的定义
rtp_parse_packet_internal函数定义在FFmpeg源码(本文演示用的FFmpeg源码版本为7.0.1)的源文件libavformat/rtpdec.c中:
static int rtp_parse_packet_internal(RTPDemuxContext *s, AVPacket *pkt,
const uint8_t *buf, int len)
{
unsigned int ssrc;
int payload_type, seq, flags = 0;
int ext, csrc;
AVStream *st;
uint32_t timestamp;
int rv = 0;
csrc = buf[0] & 0x0f;
ext = buf[0] & 0x10;
payload_type = buf[1] & 0x7f;
if (buf[1] & 0x80)
flags |= RTP_FLAG_MARKER;
seq = AV_RB16(buf + 2);
timestamp = AV_RB32(buf + 4);
ssrc = AV_RB32(buf + 8);
/* store the ssrc in the RTPDemuxContext */
s->ssrc = ssrc;
/* NOTE: we can handle only one payload type */
if (s->payload_type != payload_type)
return -1;
st = s->st;
// only do something with this if all the rtp checks pass...
if (!rtp_valid_packet_in_sequence(&s->statistics, seq)) {
av_log(s->ic, AV_LOG_ERROR,
"RTP: PT=%02x: bad cseq %04x expected=%04x\n",
payload_type, seq, ((s->seq + 1) & 0xffff));
return -1;
}
if (buf[0] & 0x20) {
int padding = buf[len - 1];
if (len >= 12 + padding)
len -= padding;
}
s->seq = seq;
len -= 12;
buf += 12;
len -= 4 * csrc;
buf += 4 * csrc;
if (len < 0)
return AVERROR_INVALIDDATA;
/* RFC 3550 Section 5.3.1 RTP Header Extension handling */
if (ext) {
if (len < 4)
return -1;
/* calculate the header extension length (stored as number
* of 32-bit words) */
ext = (AV_RB16(buf + 2) + 1) << 2;
if (len < ext)
return -1;
// skip past RTP header extension
len -= ext;
buf += ext;
}
if (s->handler && s->handler->parse_packet) {
rv = s->handler->parse_packet(s->ic, s->dynamic_protocol_context,
s->st, pkt, ×tamp, buf, len, seq,
flags);
} else if (st) {
if ((rv = av_new_packet(pkt, len)) < 0)
return rv;
memcpy(pkt->data, buf, len);
pkt->stream_index = st->index;
} else {
return AVERROR(EINVAL);
}
// now perform timestamp things....
finalize_packet(s, pkt, timestamp);
return rv;
}
该函数的作用是:解析一个RTP packet。该函数的前半部分实现了解析该RTP packet的RTP header的功能。
形参s:既是输入型参数也是输出型参数,指向一个RTPDemuxContext类型变量。RTPDemuxContext结构体声明如下,存贮RTP解复用的上下文信息:
struct RTPDemuxContext {
AVFormatContext *ic;
AVStream *st;
int payload_type;
uint32_t ssrc;
uint16_t seq;
uint32_t timestamp;
uint32_t base_timestamp;
int64_t unwrapped_timestamp;
int64_t range_start_offset;
int max_payload_size;
/* used to send back RTCP RR */
char hostname[256];
int srtp_enabled;
struct SRTPContext srtp;
/** Statistics for this stream (used by RTCP receiver reports) */
RTPStatistics statistics;
/** Fields for packet reordering @{ */
int prev_ret; ///< The return value of the actual parsing of the previous packet
RTPPacket* queue; ///< A sorted queue of buffered packets not yet returned
int queue_len; ///< The number of packets in queue
int queue_size; ///< The size of queue, or 0 if reordering is disabled
/*@}*/
/* rtcp sender statistics receive */
uint64_t last_rtcp_ntp_time;
int64_t last_rtcp_reception_time;
uint64_t first_rtcp_ntp_time;
uint32_t last_rtcp_timestamp;
int64_t rtcp_ts_offset;
/* rtcp sender statistics */
unsigned int packet_count;
unsigned int octet_count;
unsigned int last_octet_count;
int64_t last_feedback_time;
/* dynamic payload stuff */
const RTPDynamicProtocolHandler *handler;
PayloadContext *dynamic_protocol_context;
};
形参pkt:输出型参数。执行rtp_parse_packet_internal函数后,pkt会得到从该RTP packet解析出来的信息。
形参buf:输入型参数,存放需要被解析的该RTP packet数据的缓冲区。
形参len:输入型参数,该RTP packet的字节数。
返回值:返回非负数表示成功,返回负数表示失败。
三、rtp_parse_packet_internal函数中,解析RTP header的实现
rtp_parse_packet_internal函数中,首先通过下面语句将RTP header中的CSRC count字段读取出来:
csrc = buf[0] & 0x0f;
将RTP header中的extension字段读取出来:
ext = buf[0] & 0x10;
将RTP header中的payload type(有效载荷类型)字段读取出来:
payload_type = buf[1] & 0x7f;
判断RTP header中的marker字段值的值是否为1,如果为1,设置局部变量flags:
if (buf[1] & 0x80)
flags |= RTP_FLAG_MARKER;
将RTP header中的sequence number(序列号)字段读取出来。关于AV_RB16宏定义的用法可以参考:《FFmpeg源码:AV_RB32、AV_RB16、AV_RB8宏定义分析》:
seq = AV_RB16(buf + 2);
//...
s->seq = seq;
将RTP header中的timestamp(时间戳)字段读取出来:
timestamp = AV_RB32(buf + 4);
将RTP header中的synchronization source (SSRC) identifier字段读取出来:
ssrc = AV_RB32(buf + 8);
/* store the ssrc in the RTPDemuxContext */
s->ssrc = ssrc;
判断RTP header中的padding属性的值是否为1,如果为1,表示RTP packet末端会附加填充字节:
if (buf[0] & 0x20) {
int padding = buf[len - 1];
if (len >= 12 + padding)
len -= padding;
}
RTP header中的RTP Fixed Header(RTP header中的固定长度部分)固定占12字节,RTP Fixed Header之后紧接着的是contributing source(CSRC) identifiers,让指针buf移动到contributing source(CSRC) identifiers之后,使得可以读取之后的数据:
len -= 12;
buf += 12;
len -= 4 * csrc;
buf += 4 * csrc;
if (len < 0)
return AVERROR_INVALIDDATA;
如果RTP header中存在RTP Header Extension,跳过RTP Header Extension的读取:
/* RFC 3550 Section 5.3.1 RTP Header Extension handling */
if (ext) {
if (len < 4)
return -1;
/* calculate the header extension length (stored as number
* of 32-bit words) */
ext = (AV_RB16(buf + 2) + 1) << 2;
if (len < ext)
return -1;
// skip past RTP header extension
len -= ext;
buf += ext;
}