1. struct define
typedef struct AVOutputFormat {
const char *name;
/**
* Descriptive name for the format, meant to be more human-readable
* than \p name. You \e should use the NULL_IF_CONFIG_SMALL() macro
* to define it.
*/
const char *long_name;
const char *mime_type;
const char *extensions; /**< comma-separated filename extensions */
/** size of private data so that it can be allocated in the wrapper */
int priv_data_size;
/* output support */
enum CodecID audio_codec; /**< default audio codec */
enum CodecID video_codec; /**< default video codec */
int (*write_header)(struct AVFormatContext *);
int (*write_packet)(struct AVFormatContext *, AVPacket *pkt);
int (*write_trailer)(struct AVFormatContext *);
/** can use flags: AVFMT_NOFILE, AVFMT_NEEDNUMBER, AVFMT_GLOBALHEADER */
int flags;
/** Currently only used to set pixel format if not YUV420P. */
int (*set_parameters)(struct AVFormatContext *, AVFormatParameters *);
int (*interleave_packet)(struct AVFormatContext *, AVPacket *out,
AVPacket *in, int flush);
/**
* List of supported codec_id-codec_tag pairs, ordered by "better
* choice first". The arrays are all terminated by CODEC_ID_NONE.
*/
const struct AVCodecTag * const *codec_tag;
enum CodecID subtitle_codec; /**< default subtitle codec */
const AVMetadataConv *metadata_conv;
/* private fields */
struct AVOutputFormat *next;
} AVOutputFormat;
2. callback set etc
AVOutputFormat rtp_muxer = {
"rtp",
3. relative functions
3.1 rtp_write_header
static int rtp_write_header(AVFormatContext *s1)
{
RTPMuxContext *s = s1->priv_data;
int payload_type, max_packet_size, n;
AVStream *st;
int i = 0;
if (s1->nb_streams > RTP_MAX_STREAMS) return -1;
start:
st = s1->streams[i];
s->stream_index = i;
s->ntp_time_pts0 = 0;
payload_type = ff_rtp_get_payload_type(st->codec);
if (payload_type < 0)
payload_type = RTP_PT_PRIVATE+st->index; /* private payload type */
s->payload_type = payload_type;
av_log(s1, AV_LOG_DEBUG, "latm: %d \n", s1->audio_latm );
// following 2 FIXMEs could be set based on the current time, there is normally no info leak, as RTP will likely be transmitted immediately
s->base_timestamp = 0; /* FIXME: was random(), what should this be? */
s->timestamp = s->base_timestamp;
s->cur_timestamp = 0;
s->ssrc = 0; /* FIXME: was random(), what should this be? */
s->first_packet = 1;
s->first_rtcp_ntp_time = AV_NOPTS_VALUE;
max_packet_size = url_fget_max_packet_size(s1->pb);
if (max_packet_size <= 12)
return AVERROR(EIO);
s->buf = av_malloc(max_packet_size);
if (s->buf == NULL) {
return AVERROR(ENOMEM);
}
s->max_payload_size = max_packet_size - 12;
s->max_frames_per_packet = 0;
if (s1->max_delay) {
if (st->codec->codec_type == CODEC_TYPE_AUDIO) {
if (st->codec->frame_size == 0) {
av_log(s1, AV_LOG_ERROR, "Cannot respect max delay: frame size = 0\n");
} else {
s->max_frames_per_packet = av_rescale_rnd(s1->max_delay, st->codec->sample_rate, AV_TIME_BASE * st->codec->frame_size, AV_ROUND_DOWN);
}
}
if (st->codec->codec_type == CODEC_TYPE_VIDEO) {
/* FIXME: We should round down here... */
s->max_frames_per_packet = av_rescale_q(s1->max_delay, (AVRational){1, 1000000}, st->codec->time_base);
}
}
av_set_pts_info(st, 32, 1, 90000);
switch(st->codec->codec_id) {
case CODEC_ID_MP2:
case CODEC_ID_MP3:
s->buf_ptr = s->buf + 4;
break;
case CODEC_ID_MPEG1VIDEO:
case CODEC_ID_MPEG2VIDEO:
break;
case CODEC_ID_MPEG2TS:
n = s->max_payload_size / TS_PACKET_SIZE;
if (n < 1)
n = 1;
s->max_payload_size = n * TS_PACKET_SIZE;
s->buf_ptr = s->buf;
break;
case CODEC_ID_AAC:
case CODEC_ID_AMR_NB:
case CODEC_ID_AMR_WB:
s->num_frames = 0;
default:
if (st->codec->codec_type == CODEC_TYPE_AUDIO) {
av_set_pts_info(st, 32, 1, st->codec->sample_rate);
}
s->buf_ptr = s->buf;
break;
}
/* to avoid changing a lot of indents, use 'goto' instead of 'for' */
i ++;
if (i < s1->nb_streams) {
s ++;
goto start;
}
return 0;
}
3.2 rtp_write_packet
/* write an RTP packet. 'buf1' must contain a single specific frame. */
static int rtp_write_packet(AVFormatContext *s1, AVPacket *pkt)
{
RTPMuxContext *s = (RTPMuxContext *)s1->priv_data + pkt->stream_index;
AVStream *st = s1->streams[pkt->stream_index];
int rtcp_bytes;
int size= pkt->size;
uint8_t *buf1= pkt->data;
dprintf(s1, "%d: write len=%d\n", pkt->stream_index, size);
rtcp_bytes = ((s->octet_count - s->last_octet_count) * RTCP_TX_RATIO_NUM) /
RTCP_TX_RATIO_DEN;
s->cur_timestamp = s->base_timestamp + pkt->pts;
if (s->first_packet || ((rtcp_bytes >= RTCP_SR_SIZE) &&
/* CMMB requests that rtcp SR interval < 2s */
(ntp_time() - s->last_rtcp_ntp_time > 1500000))) {
rtcp_send_sr(s1, s, ntp_time());
s->last_octet_count = s->octet_count;
s->first_packet = 0;
}
switch(st->codec->codec_id) {
case CODEC_ID_PCM_MULAW:
case CODEC_ID_PCM_ALAW:
case CODEC_ID_PCM_U8:
case CODEC_ID_PCM_S8:
rtp_send_samples(s1, s, buf1, size, 1 * st->codec->channels);
break;
case CODEC_ID_PCM_U16BE:
case CODEC_ID_PCM_U16LE:
case CODEC_ID_PCM_S16BE:
case CODEC_ID_PCM_S16LE:
rtp_send_samples(s1, s, buf1, size, 2 * st->codec->channels);
break;
case CODEC_ID_MP2:
case CODEC_ID_MP3:
rtp_send_mpegaudio(s1, s, buf1, size);
break;
case CODEC_ID_MPEG1VIDEO:
case CODEC_ID_MPEG2VIDEO:
ff_rtp_send_mpegvideo(s1, s, buf1, size);
break;
case CODEC_ID_AAC:
ff_rtp_send_aac(s1, s, buf1, size);
break;
case CODEC_ID_MPEG2TS:
rtp_send_mpegts_raw(s1, s, buf1, size);
break;
case CODEC_ID_H264:
ff_rtp_send_h264(s1, s, buf1, size); // What focused on
break;
case CODEC_ID_H263:
case CODEC_ID_H263P:
ff_rtp_send_h263(s1, s, buf1, size);
break;
case CODEC_ID_AMR_NB:
case CODEC_ID_AMR_WB:
ff_rtp_send_amr(s1, s, buf1, size);
break;
default:
/* better than nothing : send the codec raw data */
rtp_send_raw(s1, s, buf1, size);
break;
}
return 0;
}
3.3 rtp_write_trailer
static int rtp_write_trailer(AVFormatContext *s1)
{
RTPMuxContext *s = s1->priv_data;
int i;
for (i = 0; i < s1->nb_streams; i ++) {
av_freep(&s->buf);
s ++;
}
return 0;
}
4. rtp_h264
static void nal_send(AVFormatContext *s1, RTPMuxContext *s, const uint8_t *buf, int size, int last)
{
uint8_t type = buf[0] & 0x1F;
av_log(s1, AV_LOG_DEBUG, "Sending NAL %x of len %d M=%d\n", buf[0] & 0x1F, size, last);
/* don't send NAL with type = access unit delimiter, some players don't like it */
if (type == 9) return;
if (size <= s->max_payload_size) {
ff_rtp_send_data(s1, s, buf, size, last);
} else {
uint8_t nri = buf[0] & 0x60;
av_log(s1, AV_LOG_DEBUG, "NAL size %d > %d\n", size, s->max_payload_size);
s->buf[0] = 28; /* FU Indicator; Type = 28 ---> FU-A */
s->buf[0] |= nri;
s->buf[1] = type;
s->buf[1] |= 1 << 7;
buf += 1;
size -= 1;
while (size + 2 > s->max_payload_size) {
memcpy(&s->buf[2], buf, s->max_payload_size - 2);
ff_rtp_send_data(s1, s, s->buf, s->max_payload_size, 0);
buf += s->max_payload_size - 2;
size -= s->max_payload_size - 2;
s->buf[1] &= ~(1 << 7);
}
s->buf[1] |= 1 << 6;
memcpy(&s->buf[2], buf, size);
ff_rtp_send_data(s1, s, s->buf, size + 2, last);
}
}
void ff_rtp_send_h264(AVFormatContext *s1, RTPMuxContext *s, const uint8_t *buf1, int size)
{
const uint8_t *r;
s->timestamp = s->cur_timestamp;
r = ff_avc_find_startcode(buf1, buf1 + size);
while (r < buf1 + size) {
const uint8_t *r1;
while(!*(r++));
r1 = ff_avc_find_startcode(r, buf1 + size);
nal_send(s1, s, r, r1 - r, (r1 == buf1 + size));
r = r1;
}
}
/* send an rtp packet. sequence number is incremented, but the caller
must update the timestamp itself */
void ff_rtp_send_data(AVFormatContext *s1, RTPMuxContext *s, const uint8_t *buf1, int len, int m)
{
dprintf(s1, "rtp_send_data size=%d\n", len);
set_rtp_output_stream_index(s1, s->stream_index);
/* build the RTP header */
put_byte(s1->pb, (RTP_VERSION << 6));
put_byte(s1->pb, (s->payload_type & 0x7f) | ((m & 0x01) << 7));
put_be16(s1->pb, s->seq);
put_be32(s1->pb, s->timestamp);
put_be32(s1->pb, s->ssrc);
put_buffer(s1->pb, buf1, len);
put_flush_packet(s1->pb);
s->seq++;
s->octet_count += len;
s->packet_count++;
}