首页 诗词 字典 板报 句子 名言 友答 励志 学校 网站地图
当前位置: 首页 > 教程频道 > 其他教程 > 互联网 >

FFMPEG推送rtmp源实例

2013-10-13 
FFMPEG推送rtmp流实例165行代码说明使用ffmpeg的api如何将文件推送到rtmp服务器。执行:输入flv文件,输出rtm

FFMPEG推送rtmp流实例

165行代码说明使用ffmpeg的api如何将文件推送到rtmp服务器。

执行:


输入flv文件,输出rtmp流:

FFMPEG数据结构:和格式mux/demux相关的context为AVFormatContext。主要是处理封装的信息,譬如格式和流。AVFormatContext{    struct AVInputFormat *iformat; // 控制格式的,譬如从文件读取flv格式。    struct AVOutputFormat *oformat;    AVIOContext *pb; // 处理文件和url的,有点像IOStream,读取文件的。    AVStream **streams; // 媒体流逻辑。}int avformat_open_input(AVFormatContext **ps, const char *filename, AVInputFormat *fmt, AVDictionary **options);      avformat_open_input(&fmt_ctx, src_filename, NULL, NULL)int avformat_alloc_output_context2(AVFormatContext **ctx, AVOutputFormat *oformat, const char *format_name, const char *filename);      avformat_alloc_output_context2(&oc, NULL, NULL, filename)void avformat_close_input(AVFormatContext **s);      avformat_close_input(&fmt_ctx)void avformat_free_context(AVFormatContext *s);      avformat_free_context(oc)int avio_open2(AVIOContext **s, const char *url, int flags, const AVIOInterruptCB *int_cb, AVDictionary **options);      avio_open2(&oc->pb, filename, AVIO_FLAG_WRITE, &oc->interrupt_callback, &output_files[nb_output_files - 1]->opts)AVStream主要是媒体流,结构为AVStream.主要是封装了媒体流的信息,里面有编码结构。AVStream{    AVCodecContext *codec;    int64_t duration;}int avformat_find_stream_info(AVFormatContext *ic, AVDictionary **options); // input时,需要先使用这个来读取解码头。      avformat_find_stream_info(fmt_ctx, NULL)int av_find_best_stream(AVFormatContext *ic, enum AVMediaType type, int wanted_stream_nb, int related_stream, AVCodec **decoder_ret, int flags);      stream_idx = av_find_best_stream(fmt_ctx, type, -1, -1, NULL, 0);AVStream *avformat_new_stream(AVFormatContext *s, const AVCodec *c);      AVStream *st = avformat_new_stream(oc, NULL);void avformat_free_context(AVFormatContext *s);int avformat_write_header(AVFormatContext *s, AVDictionary **options);和codec相关的为AVCodexContext。包含了编解码的一些信息。typedef struct AVCodecContext {    enum AVCodecID     codec_id; /* see AV_CODEC_ID_xxx */}可以根据codec_id查找对应的编解码器。int avcodec_close(AVCodecContext *avctx);     avcodec_close(video_dec_ctx)编解码器的结构是AVCodec。主要是用来做具体的编解码的。AVCodec {    enum AVMediaType type;    enum AVCodecID id;}AVCodec *avcodec_find_decoder(enum AVCodecID id);      dec = avcodec_find_decoder(dec_ctx->codec_id);int avcodec_open2(AVCodecContext *avctx, const AVCodec *codec, AVDictionary **options);      avcodec_open2(dec_ctx, dec, NULL)建立起编解码器之后,就可以读取信息。/*** This structure stores compressed data. It is typically exported by demuxers* and then passed as input to decoders, or received as output from encoders and* then passed to muxers.*/AVPacket{    int64_t pts;    int64_t dts;    uint8_t *data;    int   size;}void av_init_packet(AVPacket *pkt);int av_read_frame(AVFormatContext *s, AVPacket *pkt);void av_free_packet(AVPacket *pkt);int av_interleaved_write_frame(AVFormatContext *s, AVPacket *pkt);/*** This structure describes decoded (raw) audio or video data.*/AVFrame{}int avcodec_decode_video2(AVCodecContext *avctx, AVFrame *picture, int *got_picture_ptr, const AVPacket *avpkt);读取AVPacket的逻辑是,由AVInputFormat的read_packet读取。譬如:在find_stream_info时需要读取几个packet,调用堆栈如下:s->iformat->read_packet(s, pkt);flv_read_packet (s=0x3855a15ca2, pkt=0x7fffffffdb30) at libavformat/flvdec.c:644ff_read_packet (s=0xd21040, pkt=0x7fffffffdf30) at libavformat/utils.c:746read_frame_internal (s=0xd21040, pkt=0x7fffffffe2b0) at libavformat/utils.c:1387avformat_find_stream_info (ic=0xd21040, options=0x0) at libavformat/utils.c:2823读取到Meatadata时,调用flv_read_metabody会设置AVInputFormat的duration等信息:s->duration = num_val * AV_TIME_BASE;          amf_parse_object (s=0xd21040, astream=0xd21ee0, vstream=0xd217a0, key=0x7fffffffd7f0 "duration", max_pos=430, depth=1) at libavformat/flvdec.c:403amf_parse_object (s=0xd21040, astream=0xd21ee0, vstream=0xd217a0, key=0x7fffffffflv_read_metabody (s=0xd21040, next_pos=430) at libavformat/flvdec.c:495flv_read_packet (s=0xd21040, pkt=0x7fffffffdf30) at libavformat/flvdec.c:700ff_read_packet (s=0xd21040, pkt=0x7fffffffdf30) at libavformat/utils.c:746read_frame_internal (s=0xd21040, pkt=0x7fffffffe2b0) at libavformat/utils.c:1387avformat_find_stream_info (ic=0xd21040, options=0x0) at libavformat/utils.c:2823实际上会预读一次文件,缓存到(差不多是63732字节,或者1667毫秒的数据)    struct AVPacketList *packet_buffer;    struct AVPacketList *packet_buffer_end;里面,若缓存读完后,就会调用mux/demux从文件读取:          av_get_packet (s=0xd296e0, pkt=0xd217a0, size=0) at libavformat/utils.c:269          flv_read_packet (s=0xd21040, pkt=0x7fffffffe260) at libavformat/flvdec.c:645          ff_read_packet (s=0xd21040, pkt=0x7fffffffe260) at libavformat/utils.c:746read_frame_internal (s=0xd21040, pkt=0x7fffffffe440) at libavformat/utils.c:1382av_read_frame (s=0xd21040, pkt=0x7fffffffe440) at libavformat/utils.c:1489av_get_packet从文件流读取数据到pkt。mux,首先也是先创建一个AVFormatContext,需要用avformat_alloc_output_context2。调试ffmpeg的命令:ffmpeg -re -i /home/winlin/test_22m.flv -vcodec copy -acodec copy -f flv -y rtmp://dev:1935/live/livestream首先初始化网络:#0  avformat_network_init () at libavformat/utils.c:4144#1  0x000000000041f6cf in main (argc=12, argv=0x7fffffffe528) at ffmpeg.c:3315设置断点avformat_alloc_output_context2,会发现参数是:           #0  avformat_alloc_output_context2 (avctx=0x7fffffffdf10, oformat=0xd4af20, format=0x1074b80 "flv", filename=0x7fffffffe839 "rtmp://dev:1935/live/livestream") at libavformat/mux.c:166然后是打开文件:#0  ffio_fdopen (s=0x106cfa0, h=0x106e720) at libavformat/aviobuf.c:710#1  0x00000000004bbbb6 in avio_open2 (s=0x106cfa0, filename=0x7fffffffe820 "rtmp://dev:1935/live/livestream", flags=2, int_cb=0x106d430, options=0x1079ec8) at libavformat/aviobuf.c:812#2  0x000000000040c57b in open_output_file (o=0x7fffffffe010, filename=0x7fffffffe820 "rtmp://dev:1935/live/livestream") at ffmpeg_opt.c:1703#3  0x000000000040ead8 in open_files (l=0xf1b040, inout=0xa3994b "output", open_file=0x40aff4 <open_output_file>) at ffmpeg_opt.c:2307#4  0x000000000040ecba in ffmpeg_parse_options (argc=12, argv=0x7fffffffe528) at ffmpeg_opt.c:2351#5  0x000000000041f6fd in main (argc=12, argv=0x7fffffffe528) at ffmpeg.c:3322然后是创建stream:#0  avformat_new_stream (s=0xa7a154, c=0xab9ea0) at libavformat/utils.c:3303#1  0x0000000000408116 in new_output_stream (o=0x7fffffffe030, oc=0x106cf80, type=AVMEDIA_TYPE_VIDEO, source_index=0) at ffmpeg_opt.c:906#2  0x0000000000408d5f in new_video_stream (o=0x7fffffffe030, oc=0x106cf80, source_index=0) at ffmpeg_opt.c:1049#3  0x000000000040b688 in open_output_file (o=0x7fffffffe030, filename=0x7fffffffe839 "rtmp://dev:1935/live/livestream") at ffmpeg_opt.c:1531#4  0x000000000040ead8 in open_files (l=0xf1b040, inout=0xa3994b "output", open_file=0x40aff4 <open_output_file>) at ffmpeg_opt.c:2307#5  0x000000000040ecba in ffmpeg_parse_options (argc=12, argv=0x7fffffffe548) at ffmpeg_opt.c:2351创建stream后需要设置stream信息。#0  avformat_write_header (s=0x0, options=0x106cd00) at libavformat/mux.c:384#1  0x000000000041c495 in transcode_init () at ffmpeg.c:2480#2  0x000000000041f1c0 in transcode () at ffmpeg.c:3138#3  0x000000000041f79a in main (argc=12, argv=0x7fffffffe528) at ffmpeg.c:3344H264和AAC需要将extra data从input拷贝到output(其他信息也在这里拷贝):(gdb) f#0  transcode_init () at ffmpeg.c:21692169                 memcpy(codec->extradata, icodec->extradata, icodec->extradata_size);(gdb) bt#0  transcode_init () at ffmpeg.c:2169#1  0x000000000041f1c0 in transcode () at ffmpeg.c:3138#2  0x000000000041f79a in main (argc=12, argv=0x7fffffffe528) at ffmpeg.c:3344写入header时,会将编码包也写入,这个很重要:#0  flv_write_header (s=0x106cf80) at libavformat/flvenc.c:197#1  0x00000000004f7690 in avformat_write_header (s=0x106cf80, options=0x1079ec8) at libavformat/mux.c:391#2  0x000000000041c495 in transcode_init () at ffmpeg.c:2480#3  0x000000000041f1c0 in transcode () at ffmpeg.c:3138#4  0x000000000041f79a in main (argc=12, argv=0x7fffffffe528) at ffmpeg.c:3344读取包的流程是:#0  av_read_frame (s=0x0, pkt=0x0) at libavformat/utils.c:1480#1  0x000000000041d84c in get_input_packet (f=0x106ce80, pkt=0x7fffffffdff0) at ffmpeg.c:2828#2  0x000000000041d976 in process_input (file_index=0) at ffmpeg.c:2865#3  0x000000000041f14c in transcode_step () at ffmpeg.c:3115#4  0x000000000041f263 in transcode () at ffmpeg.c:3167#5  0x000000000041f79a in main (argc=12, argv=0x7fffffffe548) at ffmpeg.c:3344修改包的时间戳:#0  process_input (file_index=0) at ffmpeg.c:2963#1  0x000000000041f14c in transcode_step () at ffmpeg.c:3115#2  0x000000000041f263 in transcode () at ffmpeg.c:3167#3  0x000000000041f79a in main (argc=12, argv=0x7fffffffe528) at ffmpeg.c:3344开始写入数据:#0  rtmp_write (s=0x106e720, buf=0xf4a240 "\b", size=296) at libavformat/librtmp.c:142#1  0x00000000004b90f9 in retry_transfer_wrapper (h=0x106e720, buf=0xf4a240 "\b", size=296, size_min=296, transfer_func=0x4caf12 <rtmp_write>) at libavformat/avio.c:274#2  0x00000000004b933b in ffurl_write (h=0x106e720, buf=0xf4a240 "\b", size=296) at libavformat/avio.c:325#3  0x00000000004b9a25 in writeout (s=0x107bb00, data=0xf4a240 "\b", len=296) at libavformat/aviobuf.c:129#4  0x00000000004b9ab7 in flush_buffer (s=0x107bb00) at libavformat/aviobuf.c:140#5  0x00000000004b9d56 in avio_flush (s=0x107bb00) at libavformat/aviobuf.c:194#6  0x00000000004c2874 in flv_write_packet (s=0x106cf80, pkt=0x7fffffffda10) at libavformat/flvenc.c:575#7  0x00000000004f7e80 in split_write_packet (s=0x106cf80, pkt=0x7fffffffda10) at libavformat/mux.c:496#8  0x00000000004f8e77 in av_interleaved_write_frame (s=0x106cf80, pkt=0x7fffffffdcd0) at libavformat/mux.c:757#9  0x0000000000413b6b in write_frame (s=0x106cf80, pkt=0x7fffffffdcd0, ost=0x106dbc0) at ffmpeg.c:610#10 0x0000000000417926 in do_streamcopy (ist=0x106cb60, ost=0x106dbc0, pkt=0x7fffffffdff0) at ffmpeg.c:1488#11 0x00000000004199fa in output_packet (ist=0x106cb60, pkt=0x7fffffffdff0) at ffmpeg.c:1932#12 0x000000000041eda5 in process_input (file_index=0) at ffmpeg.c:3019#13 0x000000000041f14c in transcode_step () at ffmpeg.c:3115#14 0x000000000041f263 in transcode () at ffmpeg.c:3167#15 0x000000000041f79a in main (argc=12, argv=0x7fffffffe548) at ffmpeg.c:3344可见是由flv format mux写入,然后会调用指定的librtmp的写入。另外,packet不必拷贝,对比:Run till exit from #0  get_input_packet (f=0x106e500, pkt=0x200000001) at ffmpeg.c:2823$9 = {pts = 84, dts = 42, data = 0xf325c0 "", size = 111, stream_index = 0, flags = 0, side_data = 0x0, side_data_elems = 0, duration = 0, destruct = 0x5160be <av_destruct_packet>,  priv = 0x0, pos = 36086, convergence_duration = 0}Breakpoint 10, output_packet (ist=0x2a, pkt=0xf4240) at ffmpeg.c:1804$12 = {pts = 42, dts = 0, data = 0xf325c0 "", size = 111, stream_index = 0, flags = 0, side_data = 0x0, side_data_elems = 0, duration = 0, destruct = 0x5160be <av_destruct_packet>,  priv = 0x0, pos = 36086, convergence_duration = 0}其实包没有改变,直接读取然后写入就可以了。


热点排行