利用ffmpeg来分析wma文件信息
STEP1: 参考ffprobe编写如下测试代码。
fileParse.c
#include <libavformat/avformat.h>
#include <libavcodec/avcodec.h>
#include <libavutil/avstring.h>
#include <libavutil/opt.h>
#include <libavutil/pixdesc.h>
#include <libavutil/dict.h>
#define print_error(a, b) printf(" Open %s with error (%d) \n", a, b)
int dec_init()
{
av_register_all();
avformat_network_init();
}
int dec_exit()
{
avformat_network_deinit();
}
int dec_parse(const char *filename)
{
int err, i;
AVFormatContext *fmt_ctx = NULL;
AVDictionaryEntry *t;
AVInputFormat *iformat = NULL;
AVDictionary *format_opts = NULL;
if ((err = avformat_open_input(&fmt_ctx, filename, iformat, &format_opts)) < 0) {
print_error(filename, err);
return err;
}
if ((t = av_dict_get(format_opts, "", NULL, AV_DICT_IGNORE_SUFFIX))) {
av_log(NULL, AV_LOG_ERROR, "Option %s not found.\n", t->key);
return AVERROR_OPTION_NOT_FOUND;
}
/* fill the streams in the format context */
if ((err = avformat_find_stream_info(fmt_ctx, NULL)) < 0) {
print_error(filename, err);
return err;
}
av_dump_format(fmt_ctx, 0, filename, 0);
/* bind a decoder to each input stream */
for (i = 0; i < fmt_ctx->nb_streams; i++) {
AVStream *stream = fmt_ctx->streams[i];
AVCodec *codec;
if (!(codec = avcodec_find_decoder(stream->codec->codec_id))) {
fprintf(stderr, "Unsupported codec with id %d for input stream %d\n",
stream->codec->codec_id, stream->index);
} else if (avcodec_open2(stream->codec, codec, NULL) < 0) {
fprintf(stderr, "Error while opening codec for input stream %d\n",
stream->index);
}
}
avformat_close_input(&fmt_ctx);
return 0;
}
int main(int argc, char **argv)
{
dec_init();
dec_parse(argv[1]);
dec_exit();
}
Makefile文件
.PHONY: fileParse
INCLUDE= -I../ffmpeg-0.9.2/libavcodec -I../ffmpeg-0.9.2/libavformat -I../ffmpeg-0.9.2/libavfilter -I../ffmpeg-0.9.2/libavformat -I../ffmpeg-0.9.2/libswscale
fileParse:
-rm fileParse
gcc -g ${INCLUDE} -o fileParse fileParse.c -lavformat -lavcodec -lavutil -lswscale -lbz2 -lz -lm `sdl-config --cflags --libs`
chmod +x fileParse
STEP2: 使用gdb来Debug FFMPEG
1〉修改ffmpeg配置。
make clean
./configure --enable-debug --disable-optimizations --disable-yasm --disable-asm
make
make install
2> 使用gdb 来调试fileParse
为了了解ffmpeg流程,用wma来测试。
注意到wma采用asf封装,猜测格式检测函数是asf_probe
文件:libavformat/Asfdec.c
.read_probe = asf_probe
gdb
file fileParse
b asf_probe
r /projects/media/wma/123.wma (测试文件路径)
bt
#0 asf_probe (pd=0xbffff12c) at libavformat/asfdec.c:139
#1 0x0812a2a3 in av_probe_input_format3 (pd=0xbffff1c0, is_opened=1,
score_ret=0xbffff178) at libavformat/utils.c:351
#2 0x0812a384 in av_probe_input_format2 (pd=0xbffff1c0, is_opened=1,
score_max=0xbffff1bc) at libavformat/utils.c:373
#3 0x0812aae5 in av_probe_input_buffer (pb=0x8c375d0, fmt=0x8c2f024,
filename=0xbffff548 "/projects/media/wma/123.wma", logctx=0x8c2f020,
offset=0, max_probe_size=1048576) at libavformat/utils.c:586
#4 0x0812add6 in init_input (s=0x8c2f020,
filename=0xbffff548 "/projects/media/wma/123.wma", options=0xbffff24c)
at libavformat/utils.c:653
#5 0x0812ae90 in avformat_open_input (ps=0xbffff2a4,
filename=0xbffff548 "/projects/media/wma/123.wma", fmt=0x0,
options=0xbffff2a0) at libavformat/utils.c:674
#6 0x0804a48f in dec_parse (filename=0xbffff548 "/projects/media/wma/123.wma")
at fileParse.c:29
#7 0x0804a66d in main (argc=2, argv=0xbffff394) at fileParse.c:69
可见ffmpeg的调用关系如下:
avformat_open_input
↓
init_input
↓
av_probe_input_buffer
↓
av_probe_input_format2
↓
av_probe_input_format3
↓
while ((fmt1 = av_iformat_next(fmt1))) {
if (!is_opened == !(fmt1->flags & AVFMT_NOFILE))
continue;
score = 0;
if (fmt1->read_probe) {
score = fmt1->read_probe(&lpd);
if(fmt1->extensions && av_match_ext(lpd.filename, fmt1->extensions))
score = FFMAX(score, nodat ? AVPROBE_SCORE_MAX/4-1 : 1);
} else if (fmt1->extensions) {
if (av_match_ext(lpd.filename, fmt1->extensions)) {
score = 50;
}
}
if (score > score_max) {
score_max = score;
fmt = fmt1;
}else if (score == score_max)
fmt = NULL;
}
评分算法:
1〉文件头检查OK, 直接打100分
2〉文件后缀匹配,直接打50分
av_iformat_next遍历的链表信息主要在av_register_all中调用REGISTER_MUXER宏来
注册。
同样
b asf_read_header
bt
可以得到如下调试信息
#0 asf_read_header (s=0x8c2f020, ap=0xbffff250) at libavformat/asfdec.c:581
#1 0x0812aff8 in avformat_open_input (ps=0xbffff2a4,
filename=0xbffff548 "/projects/media/wma/123.wma", fmt=0x0,
options=0xbffff2a0) at libavformat/utils.c:707
#2 0x0804a48f in dec_parse (filename=0xbffff548 "/projects/media/wma/123.wma")
at fileParse.c:29
#3 0x0804a66d in main (argc=2, argv=0xbffff394) at fileParse.c:69
通过以上分析可以知道,对于ASF文件格式解析来说,
我们只需要研究如下函数。
.read_probe = asf_probe,
.read_header = asf_read_header,
.read_packet = asf_read_packet,
.read_close = asf_read_close,
.read_seek = asf_read_seek,
.read_timestamp = asf_read_pts,
STEP3:
参考上面的函数,在工作平台实现相关函数。(略)
STEP1: 参考ffprobe编写如下测试代码。
fileParse.c
#include <libavformat/avformat.h>
#include <libavcodec/avcodec.h>
#include <libavutil/avstring.h>
#include <libavutil/opt.h>
#include <libavutil/pixdesc.h>
#include <libavutil/dict.h>
#define print_error(a, b) printf(" Open %s with error (%d) \n", a, b)
int dec_init()
{
av_register_all();
avformat_network_init();
}
int dec_exit()
{
avformat_network_deinit();
}
int dec_parse(const char *filename)
{
int err, i;
AVFormatContext *fmt_ctx = NULL;
AVDictionaryEntry *t;
AVInputFormat *iformat = NULL;
AVDictionary *format_opts = NULL;
if ((err = avformat_open_input(&fmt_ctx, filename, iformat, &format_opts)) < 0) {
print_error(filename, err);
return err;
}
if ((t = av_dict_get(format_opts, "", NULL, AV_DICT_IGNORE_SUFFIX))) {
av_log(NULL, AV_LOG_ERROR, "Option %s not found.\n", t->key);
return AVERROR_OPTION_NOT_FOUND;
}
/* fill the streams in the format context */
if ((err = avformat_find_stream_info(fmt_ctx, NULL)) < 0) {
print_error(filename, err);
return err;
}
av_dump_format(fmt_ctx, 0, filename, 0);
/* bind a decoder to each input stream */
for (i = 0; i < fmt_ctx->nb_streams; i++) {
AVStream *stream = fmt_ctx->streams[i];
AVCodec *codec;
if (!(codec = avcodec_find_decoder(stream->codec->codec_id))) {
fprintf(stderr, "Unsupported codec with id %d for input stream %d\n",
stream->codec->codec_id, stream->index);
} else if (avcodec_open2(stream->codec, codec, NULL) < 0) {
fprintf(stderr, "Error while opening codec for input stream %d\n",
stream->index);
}
}
avformat_close_input(&fmt_ctx);
return 0;
}
int main(int argc, char **argv)
{
dec_init();
dec_parse(argv[1]);
dec_exit();
}
Makefile文件
.PHONY: fileParse
INCLUDE= -I../ffmpeg-0.9.2/libavcodec -I../ffmpeg-0.9.2/libavformat -I../ffmpeg-0.9.2/libavfilter -I../ffmpeg-0.9.2/libavformat -I../ffmpeg-0.9.2/libswscale
fileParse:
-rm fileParse
gcc -g ${INCLUDE} -o fileParse fileParse.c -lavformat -lavcodec -lavutil -lswscale -lbz2 -lz -lm `sdl-config --cflags --libs`
chmod +x fileParse
STEP2: 使用gdb来Debug FFMPEG
1〉修改ffmpeg配置。
make clean
./configure --enable-debug --disable-optimizations --disable-yasm --disable-asm
make
make install
2> 使用gdb 来调试fileParse
为了了解ffmpeg流程,用wma来测试。
注意到wma采用asf封装,猜测格式检测函数是asf_probe
文件:libavformat/Asfdec.c
.read_probe = asf_probe
gdb
file fileParse
b asf_probe
r /projects/media/wma/123.wma (测试文件路径)
bt
#0 asf_probe (pd=0xbffff12c) at libavformat/asfdec.c:139
#1 0x0812a2a3 in av_probe_input_format3 (pd=0xbffff1c0, is_opened=1,
score_ret=0xbffff178) at libavformat/utils.c:351
#2 0x0812a384 in av_probe_input_format2 (pd=0xbffff1c0, is_opened=1,
score_max=0xbffff1bc) at libavformat/utils.c:373
#3 0x0812aae5 in av_probe_input_buffer (pb=0x8c375d0, fmt=0x8c2f024,
filename=0xbffff548 "/projects/media/wma/123.wma", logctx=0x8c2f020,
offset=0, max_probe_size=1048576) at libavformat/utils.c:586
#4 0x0812add6 in init_input (s=0x8c2f020,
filename=0xbffff548 "/projects/media/wma/123.wma", options=0xbffff24c)
at libavformat/utils.c:653
#5 0x0812ae90 in avformat_open_input (ps=0xbffff2a4,
filename=0xbffff548 "/projects/media/wma/123.wma", fmt=0x0,
options=0xbffff2a0) at libavformat/utils.c:674
#6 0x0804a48f in dec_parse (filename=0xbffff548 "/projects/media/wma/123.wma")
at fileParse.c:29
#7 0x0804a66d in main (argc=2, argv=0xbffff394) at fileParse.c:69
可见ffmpeg的调用关系如下:
avformat_open_input
↓
init_input
↓
av_probe_input_buffer
↓
av_probe_input_format2
↓
av_probe_input_format3
↓
while ((fmt1 = av_iformat_next(fmt1))) {
if (!is_opened == !(fmt1->flags & AVFMT_NOFILE))
continue;
score = 0;
if (fmt1->read_probe) {
score = fmt1->read_probe(&lpd);
if(fmt1->extensions && av_match_ext(lpd.filename, fmt1->extensions))
score = FFMAX(score, nodat ? AVPROBE_SCORE_MAX/4-1 : 1);
} else if (fmt1->extensions) {
if (av_match_ext(lpd.filename, fmt1->extensions)) {
score = 50;
}
}
if (score > score_max) {
score_max = score;
fmt = fmt1;
}else if (score == score_max)
fmt = NULL;
}
评分算法:
1〉文件头检查OK, 直接打100分
2〉文件后缀匹配,直接打50分
av_iformat_next遍历的链表信息主要在av_register_all中调用REGISTER_MUXER宏来
注册。
同样
b asf_read_header
bt
可以得到如下调试信息
#0 asf_read_header (s=0x8c2f020, ap=0xbffff250) at libavformat/asfdec.c:581
#1 0x0812aff8 in avformat_open_input (ps=0xbffff2a4,
filename=0xbffff548 "/projects/media/wma/123.wma", fmt=0x0,
options=0xbffff2a0) at libavformat/utils.c:707
#2 0x0804a48f in dec_parse (filename=0xbffff548 "/projects/media/wma/123.wma")
at fileParse.c:29
#3 0x0804a66d in main (argc=2, argv=0xbffff394) at fileParse.c:69
通过以上分析可以知道,对于ASF文件格式解析来说,
我们只需要研究如下函数。
.read_probe = asf_probe,
.read_header = asf_read_header,
.read_packet = asf_read_packet,
.read_close = asf_read_close,
.read_seek = asf_read_seek,
.read_timestamp = asf_read_pts,
STEP3:
参考上面的函数,在工作平台实现相关函数。(略)