FFMPEG推送rtmp流实例

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

执行:

 

./objs/tool /home/winlin/test_22m.flv rtmp://dev:1935/live/livestream1

等价于:

 

ffmpeg -re -i /home/winlin/test_22m.flv -vcodec copy -acodec copy -f flv -y rtmp://dev:1935/live/livestream

结果:

 

 

输入flv文件,输出rtmp流:

 

/*
The MIT License (MIT)

Copyright (c) 2013 winlin

Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
/**
tool.cpp to implements the following command:
ffmpeg -re -i /home/winlin/test_22m.flv -vcodec copy -acodec copy -f flv -y rtmp://dev:1935/live/livestream
*/

// for int64_t print using PRId64 format.
#ifndef __STDC_FORMAT_MACROS
    #define __STDC_FORMAT_MACROS
#endif
// for cpp to use c-style macro UINT64_C in libavformat
#ifndef __STDC_CONSTANT_MACROS
    #define __STDC_CONSTANT_MACROS
#endif

#include 

extern "C"{
#include 
}

#include   

/**
 * @ingroup lavc_decoding
 * Required number of additionally allocated bytes at the end of the input bitstream for decoding.
 * This is mainly needed because some optimized bitstream readers read
 * 32 or 64 bit at once and could read over the end.

 * Note: If the first 23 bits of the additional bytes are not 0, then damaged
 * MPEG bitstreams could cause overread and segfault.
 */
#define FF_INPUT_BUFFER_PADDING_SIZE 16

void copy_stream_info(AVStream* ostream, AVStream* istream, AVFormatContext* ofmt_ctx){
    AVCodecContext* icodec = istream->codec;
    AVCodecContext* ocodec = ostream->codec;
    
    ostream->id = istream->id;
    ocodec->codec_id = icodec->codec_id;
    ocodec->codec_type = icodec->codec_type;
    ocodec->bit_rate = icodec->bit_rate;
    
    int extra_size = (uint64_t)icodec->extradata_size + FF_INPUT_BUFFER_PADDING_SIZE;
    ocodec->extradata = (uint8_t*)av_mallocz(extra_size);
    memcpy(ocodec->extradata, icodec->extradata, icodec->extradata_size);
    ocodec->extradata_size= icodec->extradata_size;
    
    // Some formats want stream headers to be separate.
    if (ofmt_ctx->oformat->flags & AVFMT_GLOBALHEADER){
        ostream->codec->flags |= CODEC_FLAG_GLOBAL_HEADER;
    }
}

void copy_video_stream_info(AVStream* ostream, AVStream* istream, AVFormatContext* ofmt_ctx){
    copy_stream_info(ostream, istream, ofmt_ctx);
    
    AVCodecContext* icodec = istream->codec;
    AVCodecContext* ocodec = ostream->codec;
    
    ocodec->width = icodec->width;
    ocodec->height = icodec->height;
    ocodec->time_base = icodec->time_base;
    ocodec->gop_size = icodec->gop_size;
    ocodec->pix_fmt = icodec->pix_fmt;
}

void copy_audio_stream_info(AVStream* ostream, AVStream* istream, AVFormatContext* ofmt_ctx){
    copy_stream_info(ostream, istream, ofmt_ctx);
    
    AVCodecContext* icodec = istream->codec;
    AVCodecContext* ocodec = ostream->codec;
    
    ocodec->sample_fmt = icodec->sample_fmt;
    ocodec->sample_rate = icodec->sample_rate;
    ocodec->channels = icodec->channels;
}

int main(int argc, char** argv){
    if (argc <= 2) {
        printf("Usage: %s  \n"
            "%s /home/winlin/test_22m.flv rtmp://dev:1935/live/livestream\n", 
            argv[0], argv[0]); 
        exit(-1);
    }
    
    const char* filename = argv[1];
    const char* url = argv[2];
    
    av_register_all();
    avformat_network_init();
    
    // open format context
    AVFormatContext* ifmt_ctx = NULL;
    if(avformat_open_input(&ifmt_ctx, filename, NULL, NULL) != 0){
        return -1;
    }
    
    if(avformat_find_stream_info(ifmt_ctx, NULL) < 0){
        return -1;
    }
    
    // create an output format context
    AVFormatContext* ofmt_ctx = NULL;
    if(avformat_alloc_output_context2(&ofmt_ctx, NULL, "flv", url) < 0){
        return -1;
    }
    
    // open file.
    if(avio_open2(&ofmt_ctx->pb, url, AVIO_FLAG_WRITE, &ofmt_ctx->interrupt_callback, NULL) < 0){
        return -1;
    }
    
    // create stream
    int stream_index;
    if((stream_index = av_find_best_stream(ifmt_ctx, AVMEDIA_TYPE_VIDEO, -1, -1, NULL, 0)) >= 0){
        AVStream* istream = ifmt_ctx->streams[stream_index];
        AVStream* ostream = avformat_new_stream(ofmt_ctx, NULL);
        if(!ostream){
            return -1;
        }
        
        copy_video_stream_info(ostream, istream, ofmt_ctx);
    }
    if((stream_index = av_find_best_stream(ifmt_ctx, AVMEDIA_TYPE_AUDIO, -1, -1, NULL, 0)) >= 0){
        AVStream* istream = ifmt_ctx->streams[stream_index];
        AVStream* ostream = avformat_new_stream(ofmt_ctx, NULL);
        if(!ostream){
            return -1;
        }
        
        copy_audio_stream_info(ostream, istream, ofmt_ctx);
    }
    
    av_dump_format(ofmt_ctx, 0, url, 1);
    
    // write header
    if(avformat_write_header(ofmt_ctx, NULL) != 0){
        return -1;
    }
    
    // init packet(compressed data)
    AVPacket pkt;
    av_init_packet(&pkt);
    
    // read packet
    int64_t last_pts = 0;
    while(av_read_frame(ifmt_ctx, &pkt) >= 0){
        if(av_interleaved_write_frame(ofmt_ctx, &pkt) < 0){
            return -1;
        }
        
        double time_ms = 0;
        AVStream* stream = ofmt_ctx->streams[pkt.stream_index];
        if(stream->time_base.den > 0){
            time_ms = (pkt.pts - last_pts) * stream->time_base.num * 1000 / stream->time_base.den;
        }
        
        printf("write packet pts=%"PRId64", dts=%"PRId64", stream=%d sleep %.1fms\n", pkt.pts, pkt.dts, pkt.stream_index, time_ms);
        
        if(time_ms > 500){
            av_usleep(time_ms * 1000);
            last_pts = pkt.pts;
        }
        
        av_free_packet(&pkt);
    }
    
    // cleanup
    avformat_free_context(ofmt_ctx);
    avformat_close_input(&ifmt_ctx);
    
    return 0;
}


extern "C"{
#include 
}

#include   

/**
 * @ingroup lavc_decoding
 * Required number of additionally allocated bytes at the end of the input bitstream for decoding.
 * This is mainly needed because some optimized bitstream readers read
 * 32 or 64 bit at once and could read over the end.

 * Note: If the first 23 bits of the additional bytes are not 0, then damaged
 * MPEG bitstreams could cause overread and segfault.
 */
#define FF_INPUT_BUFFER_PADDING_SIZE 16

void copy_stream_info(AVStream* ostream, AVStream* istream, AVFormatContext* ofmt_ctx){
    AVCodecContext* icodec = istream->codec;
    AVCodecContext* ocodec = ostream->codec;
    
    ostream->id = istream->id;
    ocodec->codec_id = icodec->codec_id;
    ocodec->codec_type = icodec->codec_type;
    ocodec->bit_rate = icodec->bit_rate;
    
    int extra_size = (uint64_t)icodec->extradata_size + FF_INPUT_BUFFER_PADDING_SIZE;
    ocodec->extradata = (uint8_t*)av_mallocz(extra_size);
    memcpy(ocodec->extradata, icodec->extradata, icodec->extradata_size);
    ocodec->extradata_size= icodec->extradata_size;
    
    // Some formats want stream headers to be separate.
    if (ofmt_ctx->oformat->flags & AVFMT_GLOBALHEADER){
        ostream->codec->flags |= CODEC_FLAG_GLOBAL_HEADER;
    }
}

void copy_video_stream_info(AVStream* ostream, AVStream* istream, AVFormatContext* ofmt_ctx){
    copy_stream_info(ostream, istream, ofmt_ctx);
    
    AVCodecContext* icodec = istream->codec;
    AVCodecContext* ocodec = ostream->codec;
    
    ocodec->width = icodec->width;
    ocodec->height = icodec->height;
    ocodec->time_base = icodec->time_base;
    ocodec->gop_size = icodec->gop_size;
    ocodec->pix_fmt = icodec->pix_fmt;
}

void copy_audio_stream_info(AVStream* ostream, AVStream* istream, AVFormatContext* ofmt_ctx){
    copy_stream_info(ostream, istream, ofmt_ctx);
    
    AVCodecContext* icodec = istream->codec;
    AVCodecContext* ocodec = ostream->codec;
    
    ocodec->sample_fmt = icodec->sample_fmt;
    ocodec->sample_rate = icodec->sample_rate;
    ocodec->channels = icodec->channels;
}

int main(int argc, char** argv){
    if (argc <= 2) {
        printf("Usage: %s  \n"
            "%s /home/winlin/test_22m.flv rtmp://dev:1935/live/livestream\n", 
            argv[0], argv[0]); 
        exit(-1);
    }
    
    const char* filename = argv[1];
    const char* url = argv[2];
    
    av_register_all();
    avformat_network_init();
    
    // open format context
    AVFormatContext* ifmt_ctx = NULL;
    if(avformat_open_input(&ifmt_ctx, filename, NULL, NULL) != 0){
        return -1;
    }
    
    if(avformat_find_stream_info(ifmt_ctx, NULL) < 0){
        return -1;
    }
    
    // create an output format context
    AVFormatContext* ofmt_ctx = NULL;
    if(avformat_alloc_output_context2(&ofmt_ctx, NULL, "flv", url) < 0){
        return -1;
    }
    
    // open file.
    if(avio_open2(&ofmt_ctx->pb, url, AVIO_FLAG_WRITE, &ofmt_ctx->interrupt_callback, NULL) < 0){
        return -1;
    }
    
    // create stream
    int stream_index;
    if((stream_index = av_find_best_stream(ifmt_ctx, AVMEDIA_TYPE_VIDEO, -1, -1, NULL, 0)) >= 0){
        AVStream* istream = ifmt_ctx->streams[stream_index];
        AVStream* ostream = avformat_new_stream(ofmt_ctx, NULL);
        if(!ostream){
            return -1;
        }
        
        copy_video_stream_info(ostream, istream, ofmt_ctx);
    }
    if((stream_index = av_find_best_stream(ifmt_ctx, AVMEDIA_TYPE_AUDIO, -1, -1, NULL, 0)) >= 0){
        AVStream* istream = ifmt_ctx->streams[stream_index];
        AVStream* ostream = avformat_new_stream(ofmt_ctx, NULL);
        if(!ostream){
            return -1;
        }
        
        copy_audio_stream_info(ostream, istream, ofmt_ctx);
    }
    
    av_dump_format(ofmt_ctx, 0, url, 1);
    
    // write header
    if(avformat_write_header(ofmt_ctx, NULL) != 0){
        return -1;
    }
    
    // init packet(compressed data)
    AVPacket pkt;
    av_init_packet(&pkt);
    
    // read packet
    int64_t last_pts = 0;
    while(av_read_frame(ifmt_ctx, &pkt) >= 0){
        if(av_interleaved_write_frame(ofmt_ctx, &pkt) < 0){
            return -1;
        }
        
        double time_ms = 0;
        AVStream* stream = ofmt_ctx->streams[pkt.stream_index];
        if(stream->time_base.den > 0){
            time_ms = (pkt.pts - last_pts) * stream->time_base.num * 1000 / stream->time_base.den;
        }
        
        printf("write packet pts=%"PRId64", dts=%"PRId64", stream=%d sleep %.1fms\n", pkt.pts, pkt.dts, pkt.stream_index, time_ms);
        
        if(time_ms > 500){
            av_usleep(time_ms * 1000);
            last_pts = pkt.pts;
        }
        
        av_free_packet(&pkt);
    }
    
    // cleanup
    avformat_free_context(ofmt_ctx);
    avformat_close_input(&ifmt_ctx);
    
    return 0;
}

 

 

Makefile:

 

 

 

.PHONY: default _prepare_dir

CXXFLAGS = -std=c++98 -Wall -Wextra -g -O0
GCC = g++
LINK = $(GCC)

FFMPEG_LIB = _release
INC = $(FFMPEG_LIB)/include
LIB = $(FFMPEG_LIB)/lib

OBJS = objs

default: _prepare_dir Makefile hello.o
	$(LINK) -o $(OBJS)/tool $(OBJS)/hello.o $(LIB)/libavformat.a $(LIB)/libavcodec.a $(LIB)/libavutil.a $(LIB)/librtmp.a $(LIB)/libx264.a $(LIB)/libmp3lame.a $(LIB)/libfdk-aac.a -lz -lm -lpthread

_prepare_dir:
	@mkdir -p $(OBJS)

hello.o: hello.cpp
	$(GCC) -c $(CXXFLAGS) -I$(INC) -o $(OBJS)/hello.o hello.cpp


编译FFMPEG的脚本:

 

 

sudo yum install -y autoconf automake gcc gcc-c++ make

cd /home/winlin/bravoserver/trunk/src/3rdparty &&
PREFIX_DIR=`pwd`/_release

cd /home/winlin/bravoserver/trunk/src/3rdparty &&
tar xf fdk-aac-master.tar.gz &&
cd fdk-aac-master &&
libtoolize --copy --force &&
aclocal -I m4 &&
automake --add-missing --copy --foreign && autoconf &&
./configure --prefix=${PREFIX_DIR} --enable-static --disable-shared &&
make && make install
ret=$?; if [[ 0 -ne $ret ]]; then echo "build fdk-aac failed. ret=$ret"; exit $ret; fi

cd /home/winlin/bravoserver/trunk/src/3rdparty &&
tar xf lame-3.99.1.tar.gz &&
cd lame-3.99.1 &&
./configure --prefix=${PREFIX_DIR} --enable-static --disable-shared &&
make && make install
ret=$?; if [[ 0 -ne $ret ]]; then echo "build lame failed. ret=$ret"; exit $ret; fi

cd /home/winlin/bravoserver/trunk/src/3rdparty &&
tar xf x264-snapshot-20130526-2245-stable.tar.bz2 &&
cd x264-snapshot-20130526-2245 &&
./configure --prefix=${PREFIX_DIR} --enable-static --disable-shared --disable-opencl &&
make && make install
ret=$?; if [[ 0 -ne $ret ]]; then echo "build x264 failed. ret=$ret"; exit $ret; fi

cd /home/winlin/bravoserver/trunk/src/3rdparty &&
tar xf rtmpdump-2.3.tgz &&
cd rtmpdump-2.3 &&
make CRYPTO= &&
cp ./librtmp/librtmp.a ${PREFIX_DIR}/lib &&
mkdir -p ${PREFIX_DIR}/include/librtmp &&
cp ./librtmp/amf.h ${PREFIX_DIR}/include/librtmp &&
cp ./librtmp/rtmp.h ${PREFIX_DIR}/include/librtmp &&
cp ./librtmp/log.h ${PREFIX_DIR}/include/librtmp

cd /home/winlin/bravoserver/trunk/src/3rdparty &&
tar xf ffmpeg-1.2.1.tar.bz2 &&
cd ffmpeg-1.2.1 &&
export EXTRA_CFLAGS="-I${PREFIX_DIR}/include" &&
export EXTRA_LDFLAGS="-L${PREFIX_DIR}/lib" &&
./configure \
    --prefix=${PREFIX_DIR} \
    --enable-static --disable-shared \
    --extra-cflags="${EXTRA_CFLAGS}" \
    --extra-ldflags="${EXTRA_LDFLAGS}" \
    --enable-bzlib \
    --enable-debug \
    --enable-zlib \
    --disable-ffserver \
    --enable-gpl \
    --enable-version3 \
    --enable-nonfree \
    --enable-avfilter \
    --enable-postproc \
    --enable-libfdk-aac \
    --enable-libfreetype \
    --enable-libmp3lame \
    --enable-libx264 \
    --enable-librtmp \
    --disable-ffplay --disable-ffserver \
    --disable-encoders \
    --disable-parsers --disable-devices \
    --disable-bsfs --disable-muxers --disable-demuxers \
    --enable-encoder=libx264 --enable-encoder=libmp3lame \
    --enable-encoder=libfdk_aac --enable-encoder=libx264 --enable-encoder=libmp3lame \
    --enable-indev=v4l2 --enable-indev=alsa \
    --enable-muxer=flv --enable-muxer=mpegts \
    --enable-muxer=mp4 \
    --enable-parser=aac --enable-parser=mpegvideo --enable-parser=h264 \
    --enable-bsf=aac_adtstoasc --enable-bsf=h264_mp4toannexb \
    --enable-demuxer=mov --enable-demuxer=flv --enable-demuxer=mpegts --enable-demuxer=hls \
    --disable-decoders \
    --enable-decoder=h264 --enable-decoder=mpeg2video --enable-decoder=mpegvideo \
    --enable-decoder=aac --enable-decoder=mp3 --enable-decoder=ac3 \
    --enable-decoder=mp2 --enable-decoder=rawvideo \
    --enable-decoder=pcm_s16le &&
sed -i "s/-O3/-O0/g" config.mak &&
make && make install
ret=$?; if [[ 0 -ne $ret ]]; then echo "build ffmpeg failed. ret=$ret"; exit $ret; fi

sudo rm -f /bin/ffmpeg &&
sudo ln -sf `pwd`/ffmpeg_g /bin/ffmpeg

echo "you can publish stream:"
echo "ffmpeg -re -i test_22m.flv -vcodec copy -acodec copy -f flv -y rtmp://127.0.0.1:1935/live/livestream"

 

分析过程如下:

 

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:644
ff_read_packet (s=0xd21040, pkt=0x7fffffffdf30) at libavformat/utils.c:746
read_frame_internal (s=0xd21040, pkt=0x7fffffffe2b0) at libavformat/utils.c:1387
avformat_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:403
amf_parse_object (s=0xd21040, astream=0xd21ee0, vstream=0xd217a0, key=0x7fffffff
flv_read_metabody (s=0xd21040, next_pos=430) at libavformat/flvdec.c:495
flv_read_packet (s=0xd21040, pkt=0x7fffffffdf30) at libavformat/flvdec.c:700
ff_read_packet (s=0xd21040, pkt=0x7fffffffdf30) at libavformat/utils.c:746
read_frame_internal (s=0xd21040, pkt=0x7fffffffe2b0) at libavformat/utils.c:1387
avformat_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:746
read_frame_internal (s=0xd21040, pkt=0x7fffffffe440) at libavformat/utils.c:1382
av_read_frame (s=0xd21040, pkt=0x7fffffffe440) at libavformat/utils.c:1489
av_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:3344
H264和AAC需要将extra data从input拷贝到output(其他信息也在这里拷贝):
(gdb) f
#0  transcode_init () at ffmpeg.c:2169
2169                 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}
其实包没有改变,直接读取然后写入就可以了。

 

 

 

 

 

 

  • 2
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

winlinvip

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值