FFMPEG版本爲3.2 release。服務器
libavformat/rtmpproto.capp
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
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; }
與服務器端交互,直到接收到音視頻數據,或者metadata數據ui
/** * 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); } }