ffmpeg的主函数入口:ffmpeg.c中的main函数
int main(int argc, char **argv)
{
int i, ret;
int64_t ti;
init_dynload();
register_exit(ffmpeg_cleanup);
setvbuf(stderr,NULL,_IONBF,0); /* win32 runtime needs this */
av_log_set_flags(AV_LOG_SKIP_REPEATED);
parse_loglevel(argc, argv, options);
if(argc>1 && !strcmp(argv[1], "-d")){
run_as_daemon=1;
av_log_set_callback(log_callback_null);
argc--;
argv++;
}
avcodec_register_all();//注册所有的编解码器和硬解加速器
#if CONFIG_AVDEVICE
avdevice_register_all();
#endif
avfilter_register_all();//注册滤波器,就是把filter加到AVFilter链表尾部
av_register_all();//把对应的format加到AVInputFormat,AVOutputFormat链表的尾部
avformat_network_init();
show_banner(argc, argv, options);
/* parse options and open all input/output files */
ret = ffmpeg_parse_options(argc, argv);
if (ret < 0)
exit_program(1);
if (nb_output_files <= 0 && nb_input_files == 0) {
show_usage();
av_log(NULL, AV_LOG_WARNING, "Use -h to get full help or, even better, run 'man %s'\n", program_name);
exit_program(1);
}
/* file converter / grab */
if (nb_output_files <= 0) {
av_log(NULL, AV_LOG_FATAL, "At least one output file must be specified\n");
exit_program(1);
}
// if (nb_input_files == 0) {
// av_log(NULL, AV_LOG_FATAL, "At least one input file must be specified\n");
// exit_program(1);
// }
for (i = 0; i < nb_output_files; i++) {
if (strcmp(output_files[i]->ctx->oformat->name, "rtp"))
want_sdp = 0;
}
current_time = ti = getutime();
if (transcode() < 0)
exit_program(1);
ti = getutime() - ti;
if (do_benchmark) {
av_log(NULL, AV_LOG_INFO, "bench: utime=%0.3fs\n", ti / 1000000.0);
}
av_log(NULL, AV_LOG_DEBUG, "%"PRIu64" frames successfully decoded, %"PRIu64" decoding errors\n",
decode_error_stat[0], decode_error_stat[1]);
if ((decode_error_stat[0] + decode_error_stat[1]) * max_error_rate < decode_error_stat[1])
exit_program(69);
exit_program(received_nb_signals ? 255 : main_return_code);
return main_return_code;
}
avcodec_register_all–>register_all–>REGISTER_HWACCEL REGISTER_ENCODER REGISTER_DECODER REGISTER_PARSER –>av_register_hwaccel avcodec_register avcodec_register av_register_codec_parser
(加入到AVHWAccel,AVCodec,AVCodecParser链表的尾部)
一个位置位于doc/examples/的demo,如文件decode_video.c:
int main(int argc, char **argv)
{
const char *filename, *outfilename;
const AVCodec *codec;
AVCodecParserContext *parser;
AVCodecContext *c= NULL;
FILE *f;
AVFrame *frame;
uint8_t inbuf[INBUF_SIZE + AV_INPUT_BUFFER_PADDING_SIZE];
uint8_t *data;
size_t data_size;
int ret;
AVPacket *pkt;
if (argc <= 2) {
fprintf(stderr, "Usage: %s <input file> <output file>\n", argv[0]);
exit(0);
}
filename = argv[1];
outfilename = argv[2];
avcodec_register_all();//注册所有的编解码器和硬解加速器
pkt = av_packet_alloc();//分配pkt地址,并初始化
if (!pkt)
exit(1);
/* set end of buffer to 0 (this ensures that no overreading happens for damaged MPEG streams) */
memset(inbuf + INBUF_SIZE, 0, AV_INPUT_BUFFER_PADDING_SIZE);
/* find the MPEG-1 video decoder */
codec = avcodec_find_decoder(AV_CODEC_ID_MPEG1VIDEO);//在解码器链表中查找对应的解码器
if (!codec) {
fprintf(stderr, "Codec not found\n");
exit(1);
}
parser = av_parser_init(codec->id);//在解析器链表中查找对应的parser,并对AVCodecParserContext进行初始化
if (!parser) {
fprintf(stderr, "parser not found\n");
exit(1);
}
c = avcodec_alloc_context3(codec);//对AVCodecContext进行初始化
if (!c) {
fprintf(stderr, "Could not allocate video codec context\n");
exit(1);
}
/* For some codecs, such as msmpeg4 and mpeg4, width and height
MUST be initialized there because this information is not
available in the bitstream. */
/* open it */
if (avcodec_open2(c, codec, NULL) < 0) {//打开解码器
fprintf(stderr, "Could not open codec\n");
exit(1);
}
f = fopen(filename, "rb");//打开输入码流
if (!f) {
fprintf(stderr, "Could not open %s\n", filename);
exit(1);
}
frame = av_frame_alloc();//对输出帧frame分配地址
if (!frame) {
fprintf(stderr, "Could not allocate video frame\n");
exit(1);
}
while (!feof(f)) {
/* read raw data from the input file */
data_size = fread(inbuf, 1, INBUF_SIZE, f);//从输入文件中读取数据
if (!data_size)
break;
/* use the parser to split the data into frames */
data = inbuf;
while (data_size > 0) {
ret = av_parser_parse2(parser, c, &pkt->data, &pkt->size,
data, data_size, AV_NOPTS_VALUE, AV_NOPTS_VALUE, 0);//用于得到一帧
if (ret < 0) {
fprintf(stderr, "Error while parsing\n");
exit(1);
}
data += ret;
data_size -= ret;
if (pkt->size)
decode(c, frame, pkt, outfilename);//解码。。会调用avctx->codec->decode解码一帧
}
}
/* flush the decoder */
decode(c, frame, NULL, outfilename);
fclose(f);
av_parser_close(parser);
avcodec_free_context(&c);
av_frame_free(&frame);
av_packet_free(&pkt);
return 0;
}
了解FFMPEG API最好的方式就是参考API DEMO了:
源码位置位于tests/api/,比如文件api-h264-test.c:
int main(int argc, char **argv)
{
if (argc < 2)
{
av_log(NULL, AV_LOG_ERROR, "Incorrect input\n");
return 1;
}
av_register_all();//注册encoder、decoder、parser等
if (video_decode_example(argv[1]) != 0)//用于解码
return 1;
return 0;
}
static int video_decode_example(const char *input_filename)
{
AVCodec *codec = NULL;
AVCodecContext *ctx= NULL;
AVCodecParameters *origin_par = NULL;
AVFrame *fr = NULL;
uint8_t *byte_buffer = NULL;
AVPacket pkt;
AVFormatContext *fmt_ctx = NULL;
int number_of_written_bytes;
int video_stream;
int got_frame = 0;
int byte_buffer_size;
int i = 0;
int result;
int end_of_stream = 0;
result = avformat_open_input(&fmt_ctx, input_filename, NULL, NULL);//打开输入的文件
if (result < 0) {
av_log(NULL, AV_LOG_ERROR, "Can't open file\n");
return result;
}
result = avformat_find_stream_info(fmt_ctx, NULL);//从文件中提取流信息
if (result < 0) {
av_log(NULL, AV_LOG_ERROR, "Can't get stream info\n");
return result;
}
video_stream = av_find_best_stream(fmt_ctx, AVMEDIA_TYPE_VIDEO, -1, -1, NULL, 0);
if (video_stream < 0) {
av_log(NULL, AV_LOG_ERROR, "Can't find video stream in input file\n");
return -1;
}
origin_par = fmt_ctx->streams[video_stream]->codecpar;
codec = avcodec_find_decoder(origin_par->codec_id);//找到对应的解码器
if (!codec) {
av_log(NULL, AV_LOG_ERROR, "Can't find decoder\n");
return -1;
}
ctx = avcodec_alloc_context3(codec);//对AVCodecContext进行初始化
if (!ctx) {
av_log(NULL, AV_LOG_ERROR, "Can't allocate decoder context\n");
return AVERROR(ENOMEM);
}
result = avcodec_parameters_to_context(ctx, origin_par);//将参数origin_par赋值给ctx
if (result) {
av_log(NULL, AV_LOG_ERROR, "Can't copy decoder context\n");
return result;
}
result = avcodec_open2(ctx, codec, NULL);//打开解码器
if (result < 0) {
av_log(ctx, AV_LOG_ERROR, "Can't open decoder\n");
return result;
}
fr = av_frame_alloc();//为解码输出的帧fr分配内存
if (!fr) {
av_log(NULL, AV_LOG_ERROR, "Can't allocate frame\n");
return AVERROR(ENOMEM);
}
byte_buffer_size = av_image_get_buffer_size(ctx->pix_fmt, ctx->width, ctx->height, 16);//根据pix_fmt、width、height得到大小
byte_buffer = av_malloc(byte_buffer_size);
if (!byte_buffer) {
av_log(NULL, AV_LOG_ERROR, "Can't allocate buffer\n");
return AVERROR(ENOMEM);
}
printf("#tb %d: %d/%d\n", video_stream, fmt_ctx->streams[video_stream]->time_base.num, fmt_ctx->streams[video_stream]->time_base.den);
i = 0;
av_init_packet(&pkt);//初始化码流包pkt
do {
if (!end_of_stream)
if (av_read_frame(fmt_ctx, &pkt) < 0)//从码流中读取一帧数据,放入pkg
end_of_stream = 1;
if (end_of_stream) {
pkt.data = NULL;
pkt.size = 0;
}
if (pkt.stream_index == video_stream || end_of_stream) {
got_frame = 0;
if (pkt.pts == AV_NOPTS_VALUE)
pkt.pts = pkt.dts = i;
result = avcodec_decode_video2(ctx, fr, &got_frame, &pkt);//会调用avctx->codec->decode解码一帧
if (result < 0) {
av_log(NULL, AV_LOG_ERROR, "Error decoding frame\n");
return result;
}
if (got_frame) {
number_of_written_bytes = av_image_copy_to_buffer(byte_buffer, byte_buffer_size,
(const uint8_t* const *)fr->data, (const int*) fr->linesize,
ctx->pix_fmt, ctx->width, ctx->height, 1);
if (number_of_written_bytes < 0) {
av_log(NULL, AV_LOG_ERROR, "Can't copy image to buffer\n");
return number_of_written_bytes;
}
printf("%d, %10"PRId64", %10"PRId64", %8"PRId64", %8d, 0x%08lx\n", video_stream,
fr->pts, fr->pkt_dts, fr->pkt_duration,
number_of_written_bytes, av_adler32_update(0, (const uint8_t*)byte_buffer, number_of_written_bytes));
}
av_packet_unref(&pkt);
av_init_packet(&pkt);
}
i++;
} while (!end_of_stream || got_frame);
av_packet_unref(&pkt);
av_frame_free(&fr);
avcodec_close(ctx);
avformat_close_input(&fmt_ctx);
avcodec_free_context(&ctx);
av_freep(&byte_buffer);
return 0;
}