FFMPEG版本为3.2 release。
libavformat/rtmpproto.c
函数rtmp_read()调用关系
avformat_open_input(&ic, filename, file_iformat, &o->g->format_opts) ->
init_input(s, filename, &tmp) ->
av_probe_input_buffer2(s->pb, &s->iformat, filename, s, 0, s->format_probesize) ->
avio_read(pb, buf + buf_offset, probe_size - buf_offset) ->
fill_buffer(s) ->
s->read_packet(s->opaque, dst, len) 实际回调函数io_read_packet() ->
ffurl_read(internal->h, buf, buf_size) ->
retry_transfer_wrapper(h, buf, size, 1, h->prot->url_read) ->
transfer_func(h, buf + len, size - len) 实际回调函数rtmp_read()
第一次执行到rtmp_read()时,栈信息:
(gdb) bt
#0 rtmp_read (s=0x2768fc0, buf=0x276b8a0 "", size=32768) at libavformat/rtmpproto.c:2905
#1 0x00000000005f3744 in retry_transfer_wrapper (h=0x2768fc0, buf=0x276b8a0 "", size=32768) at libavformat/avio.c:378
#2 ffurl_read (h=0x2768fc0, buf=0x276b8a0 "", size=32768) at libavformat/avio.c:411
#3 0x00000000005f4cac in fill_buffer (s=0x276a460) at libavformat/aviobuf.c:540
#4 0x00000000005f4f80 in avio_read (s=0x276a460, buf=0x276a7f0 "\030-Y\002", size=2048) at libavformat/aviobuf.c:634
#5 0x000000000061ba07 in av_probe_input_buffer2 (pb=0x276a460, fmt=0x27686e8, filename=<value optimized out>, logctx=0x27686e0, offset=0, max_probe_size=1048576)
at libavformat/format.c:314
#6 0x00000000007044ef in init_input (ps=0x7fffffffdbd8, filename=0x7fffffffe759 "rtmp://223.203.1.34:1936/live?vhost=cc.com/stream_1", fmt=<value optimized out>, options=0x2768428)
at libavformat/utils.c:420
#7 avformat_open_input (ps=0x7fffffffdbd8, filename=0x7fffffffe759 "rtmp://223.203.1.34:1936/live?vhost=cc.com/stream_1", fmt=<value optimized out>, options=0x2768428)
at libavformat/utils.c:529
#8 0x000000000047e65a in open_input_file (o=0x7fffffffdc80, filename=<value optimized out>) at ffmpeg_opt.c:997
#9 0x000000000047bb76 in open_files (l=0x2767998, inout=0x18b68a6 "input", open_file=0x47e220 <open_input_file>) at ffmpeg_opt.c:3135
#10 0x000000000047bde7 in ffmpeg_parse_options (argc=<value optimized out>, argv=<value optimized out>) at ffmpeg_opt.c:3175
#11 0x00000000004927c4 in main (argc=12, argv=0x7fffffffe488) at ffmpeg.c:4564
rtmp_read()函数体
static int rtmp_read(URLContext *s, uint8_t *buf, int size)
{
RTMPContext *rt = s->priv_data;
int orig_size = size;
int ret;
while (size > 0) {
int data_left = rt->flv_size - rt->flv_off;
if (data_left >= size) {
memcpy(buf, rt->flv_data + rt->flv_off, size);
rt->flv_off += size;
return orig_size;
}
if (data_left > 0) {
memcpy(buf, rt->flv_data + rt->flv_off, data_left);
buf += data_left;
size -= data_left;
rt->flv_off = rt->flv_size;
return data_left;
}
if ((ret = get_packet(s, 0)) < 0)
return ret;
}
return orig_size;
}
get_packet()函数
与服务器端交互,直到接收到音视频数据,或者metadata数据
/**
* Interact with the server by receiving and sending RTMP packets until
* there is some significant data (media data or expected status notification).
*
* @param s reading context
* @param for_header non-zero value tells function to work until it
* gets notification from the server that playing has been started,
* otherwise function will work until some media data is received (or
* an error happens)
* @return 0 for successful operation, negative value in case of error
*/
static int get_packet(URLContext *s, int for_header)
{
RTMPContext *rt = s->priv_data;
int ret;
if (rt->state == STATE_STOPPED)
return AVERROR_EOF;
for (;;) {
RTMPPacket rpkt = { 0 };
if ((ret = ff_rtmp_packet_read(rt->stream, &rpkt,
rt->in_chunk_size, &rt->prev_pkt[0],
&rt->nb_prev_pkt[0])) <= 0) {
if (ret == 0) {
return AVERROR(EAGAIN);
} else {
return AVERROR(EIO);
}
}
// Track timestamp for later use
rt->last_timestamp = rpkt.timestamp;
rt->bytes_read += ret;
if (rt->bytes_read - rt->last_bytes_read > rt->client_report_size) {
av_log(s, AV_LOG_DEBUG, "Sending bytes read report\n");
if ((ret = gen_bytes_read(s, rt, rpkt.timestamp + 1)) < 0)
return ret;
rt->last_bytes_read = rt->bytes_read;
}
ret = rtmp_parse_result(s, rt, &rpkt);
// At this point we must check if we are in the seek state and continue
// with the next packet. handle_invoke will get us out of this state
// when the right message is encountered
if (rt->state == STATE_SEEKING) {
ff_rtmp_packet_destroy(&rpkt);
// We continue, let the natural flow of things happen:
// AVERROR(EAGAIN) or handle_invoke gets us out of here
continue;
}
if (ret < 0) {//serious error in current packet
ff_rtmp_packet_destroy(&rpkt);
return ret;
}
if (rt->do_reconnect && for_header) {
ff_rtmp_packet_destroy(&rpkt);
return 0;
}
if (rt->state == STATE_STOPPED) {
ff_rtmp_packet_destroy(&rpkt);
return AVERROR_EOF;
}
if (for_header && (rt->state == STATE_PLAYING ||
rt->state == STATE_PUBLISHING ||
rt->state == STATE_SENDING ||
rt->state == STATE_RECEIVING)) {
ff_rtmp_packet_destroy(&rpkt);
return 0;
}
if (!rpkt.size || !rt->is_input) {
ff_rtmp_packet_destroy(&rpkt);
continue;
}
if (rpkt.type == RTMP_PT_VIDEO || rpkt.type == RTMP_PT_AUDIO) {
ret = append_flv_data(rt, &rpkt, 0);
ff_rtmp_packet_destroy(&rpkt);
return ret;
} else if (rpkt.type == RTMP_PT_NOTIFY) {
ret = handle_notify(s, &rpkt);
ff_rtmp_packet_destroy(&rpkt);
return ret;
} else if (rpkt.type == RTMP_PT_METADATA) {
ret = handle_metadata(rt, &rpkt);
ff_rtmp_packet_destroy(&rpkt);
return 0;
}
ff_rtmp_packet_destroy(&rpkt);
}
}