1、封包
代码位置:FFmpeg的文件rtpenc.c(4.0.5版本)
主要是把RTP头信息放到AVStream里面,然后再进行处理。过程不是很明显,可以看下面的解包代码
static int rtp_write_header(AVFormatContext *s1)
{
RTPMuxContext *s = s1->priv_data;
int n, ret = AVERROR(EINVAL);
AVStream *st;
if (s1->nb_streams != 1) {
av_log(s1, AV_LOG_ERROR, "Only one stream supported in the RTP muxer\n");
return AVERROR(EINVAL);
}
st = s1->streams[0];
if (!is_supported(st->codecpar->codec_id)) {
av_log(s1, AV_LOG_ERROR, "Unsupported codec %s\n", avcodec_get_name(st->codecpar->codec_id));
return -1;
}
if (s->payload_type < 0) {
/* Re-validate non-dynamic payload types */
if (st->id < RTP_PT_PRIVATE)
st->id = ff_rtp_get_payload_type(s1, st->codecpar, -1);
s->payload_type = st->id;
} else {
/* private option takes priority */
st->id = s->payload_type;
}
s->base_timestamp = av_get_random_seed();
s->timestamp = s->base_timestamp;
s->cur_timestamp = 0;
if (!s->ssrc)
s->ssrc = av_get_random_seed();
s->first_packet = 1;
s->first_rtcp_ntp_time = ff_ntp_time();
if (s1->start_time_realtime != 0 && s1->start_time_realtime != AV_NOPTS_VALUE)
/* Round the NTP time to whole milliseconds. */
s->first_rtcp_ntp_time = (s1->start_time_realtime / 1000) * 1000 +
NTP_OFFSET_US;
// Pick a random sequence start number, but in the lower end of the
// available range, so that any wraparound doesn't happen immediately.
// (Immediate wraparound would be an issue for SRTP.)
if (s->seq < 0) {
if (s1->flags & AVFMT_FLAG_BITEXACT) {
s->seq = 0;
} else
s->seq = av_get_random_seed() & 0x0fff;
} else
s->seq &= 0xffff; // Use the given parameter, wrapped to the right interval
if (s1->packet_size) {
if (s1->pb->max_packet_size)
s1->packet_size = FFMIN(s1->packet_size,
s1->pb->max_packet_size);
} else
s1->packet_size = s1->pb->max_packet_size;
if (s1->packet_size <= 12) {
av_log(s1, AV_LOG_ERROR, "Max packet size %u too low\n", s1->packet_size);
return AVERROR(EIO);
}
s->buf = av_malloc(s1->packet_size);
if (!s->buf) {
return AVERROR(ENOMEM);
}
s->max_payload_size = s1->packet_size - 12;
if (st->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) {
avpriv_set_pts_info(st, 32, 1, st->codecpar->sample_rate);
} else {
avpriv_set_pts_info(st, 32, 1, 90000);
}
s->buf_ptr = s->buf;
switch(st->codecpar->codec_id) {
case AV_CODEC_ID_MP2:
case AV_CODEC_ID_MP3:
s->buf_ptr = s->buf + 4;
avpriv_set_pts_info(st, 32, 1, 90000);
break;
case AV_CODEC_ID_MPEG1VIDEO:
case AV_CODEC_ID_MPEG2VIDEO:
break;
case AV_CODEC_ID_MPEG2TS:
n = s->max_payload_size / TS_PACKET_SIZE;
if (n < 1)
n = 1;
s->max_payload_size = n * TS_PACKET_SIZE;
break;
case AV_CODEC_ID_DIRAC:
if (s1->strict_std_compliance > FF_COMPLIANCE_EXPERIMENTAL) {
av_log(s, AV_LOG_ERROR,
"Packetizing VC-2 is experimental and does not use all values "
"of the specification "
"(even though most receivers may handle it just fine). "
"Please set -strict experimental in order to enable it.\n");
ret = AVERROR_EXPERIMENTAL;
goto fail;
}
break;
case AV_CODEC_ID_H261:
if (s1->strict_std_compliance > FF_COMPLIANCE_EXPERIMENTAL) {
av_log(s, AV_LOG_ERROR,
"Packetizing H.261 is experimental and produces incorrect "
"packetization for cases where GOBs don't fit into packets "
"(even though most receivers may handle it just fine). "
"Please set -f_strict experimental in order to enable it.\n");
ret = AVERROR_EXPERIMENTAL;
goto fail;
}
break;
case AV_CODEC_ID_H264:
/* check for H.264 MP4 syntax */
if (st->codecpar->extradata_size > 4 && st->codecpar->extradata[0] == 1) {
s->nal_length_size = (st->codecpar->extradata[4] & 0x03) + 1;
}
break;
case AV_CODEC_ID_HEVC:
/* Only check for the standardized hvcC version of extradata, keeping
* things simple and similar to the avcC/H.264 case above, instead
* of trying to handle the pre-standardization versions (as in
* libavcodec/hevc.c). */
if (st->codecpar->extradata_size > 21 && st->codecpar->extradata[0] == 1) {
s->nal_length_size = (st->codecpar->extradata[21] & 0x03) + 1;
}
break;
case AV_CODEC_ID_VP9:
if (s1->strict_std_compliance > FF_COMPLIANCE_EXPERIMENTAL) {
av_log(s, AV_LOG_ERROR,
"Packetizing VP9 is experimental and its specification is "
"still in draft state. "
"Please set -strict experimental in order to enable it.\n");
ret = AVERROR_EXPERIMENTAL;
goto fail;
}
break;
case AV_CODEC_ID_VORBIS:
case AV_CODEC_ID_THEORA:
s->max_frames_per_packet = 15;
break;
case AV_CODEC_ID_ADPCM_G722:
/* Due to a historical error, the clock rate for G722 in RTP is
* 8000, even if the sample rate is 16000. See RFC 3551. */
avpriv_set_pts_info(st, 32, 1, 8000);
break;
case AV_CODEC_ID_OPUS:
if (st->codecpar->channels > 2) {
av_log(s1, AV_LOG_ERROR, "Multistream opus not supported in RTP\n");
goto fail;
}
/* The opus RTP RFC says that all opus streams should use 48000 Hz
* as clock rate, since all opus sample rates can be expressed in
* this clock rate, and sample rate changes on the fly are supported. */
avpriv_set_pts_info(st, 32, 1, 48000);
break;
case AV_CODEC_ID_ILBC:
if (st->codecpar->block_align != 38 && st->codecpar->block_align != 50) {
av_log(s1, AV_LOG_ERROR, "Incorrect iLBC block size specified\n");
goto fail;
}
s->max_frames_per_packet = s->max_payload_size / st->codecpar->block_align;
break;
case AV_CODEC_ID_AMR_NB:
case AV_CODEC_ID_AMR_WB:
s->max_frames_per_packet = 50;
if (st->codecpar->codec_id == AV_CODEC_ID_AMR_NB)
n = 31;
else
n = 61;
/* max_header_toc_size + the largest AMR payload must fit */
if (1 + s->max_frames_per_packet + n > s->max_payload_size) {
av_log(s1, AV_LOG_ERROR, "RTP max payload size too small for AMR\n");
goto fail;
}
if (st->codecpar->channels != 1) {
av_log(s1, AV_LOG_ERROR, "Only mono is supported\n");
goto fail;
}
break;
case AV_CODEC_ID_AAC:
s->max_frames_per_packet = 50;
break;
default:
break;
}
return 0;
fail:
av_freep(&s->buf);
return ret;
}
2、解包
代码位置:FFmpeg的文件rtpdec.c(4.0.5版本)
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; //CC:CSRC计数器
ext = buf[0] & 0x10;//X:扩展标志
payload_type = buf[1] & 0x7f;//PT: 有效载荷类型
if (buf[1] & 0x80)//M: 标记
flags |= RTP_FLAG_MARKER;
seq = AV_RB16(buf + 2);//序列号
timestamp = AV_RB32(buf + 4);//时间戳
ssrc = AV_RB32(buf + 8);//SSRC
/* 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;
}
//P:填充标志
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;
}