您的位置:首页 > 运维架构

ffmpeg源码分析之四-----avformat_open_input()下

2013-06-28 16:18 459 查看
上篇分析到在Init_input中打开一个媒体文件 本篇将分析剩下的流程。看代码

static int init_input(AVFormatContext *s, const char *filename, AVDictionary **options)
{
int ret;
AVProbeData pd = {filename, NULL, 0};

if (s->pb) {
s->flags |= AVFMT_FLAG_CUSTOM_IO;
if (!s->iformat)
return av_probe_input_buffer(s->pb, &s->iformat, filename, s, 0, 0);
else if (s->iformat->flags & AVFMT_NOFILE)
av_log(s, AV_LOG_WARNING, "Custom AVIOContext makes no sense and "
"will be ignored with AVFMT_NOFILE format.\n");
return 0;
}

if ( (s->iformat && s->iformat->flags & AVFMT_NOFILE) ||
(!s->iformat && (s->iformat = av_probe_input_format(&pd, 0))))
return 0;
//avio_open2定义在libavformat/aviobuf.c中 但是在avio.h中声明
//Create and initialize a AVIOContext for accessing the resource indicated by url
if ((ret = avio_open2(&s->pb, filename, AVIO_FLAG_READ,
&s->interrupt_callback, options)) < 0)
return ret;
if (s->iformat)
return 0;
av_log(0,AV_LOG_INFO,"注意:程序将要执行av_probe_input_buffer\n");
return av_probe_input_buffer(s->pb, &s->iformat, filename, s, 0, 0);
}


来看av_probe_input_buffer():

int av_probe_input_buffer(AVIOContext *pb, AVInputFormat **fmt,
const char *filename, void *logctx,
unsigned int offset, unsigned int max_probe_size)
{
AVProbeData pd = { filename ? filename : "", NULL, -offset };
unsigned char *buf = NULL;
int ret = 0, probe_size;

if (!max_probe_size) {
max_probe_size = PROBE_BUF_MAX;
} else if (max_probe_size > PROBE_BUF_MAX) {
max_probe_size = PROBE_BUF_MAX;
} else if (max_probe_size < PROBE_BUF_MIN) {
return AVERROR(EINVAL);
}

if (offset >= max_probe_size) {
return AVERROR(EINVAL);
}

for(probe_size= PROBE_BUF_MIN; probe_size<=max_probe_size && !*fmt;
probe_size = FFMIN(probe_size<<1, FFMAX(max_probe_size, probe_size+1))) {
int score = probe_size < max_probe_size ? AVPROBE_SCORE_MAX/4 : 0;
int buf_offset = (probe_size == PROBE_BUF_MIN) ? 0 : probe_size>>1;
void *buftmp;

if (probe_size < offset) {
continue;
}

/* read probe data */
buftmp = av_realloc(buf, probe_size + AVPROBE_PADDING_SIZE);
if(!buftmp){
av_free(buf);
return AVERROR(ENOMEM);
}
buf=buftmp;
if ((ret = avio_read(pb, buf + buf_offset, probe_size - buf_offset)) < 0) {
/* fail if error was not end of file, otherwise, lower score */
if (ret != AVERROR_EOF) {
av_free(buf);
return ret;
}
score = 0;
ret = 0;            /* error was end of file, nothing read */
}
pd.buf_size += ret;
pd.buf = &buf[offset];

memset(pd.buf + pd.buf_size, 0, AVPROBE_PADDING_SIZE);

/* guess file format */
*fmt = av_probe_input_format2(&pd, 1, &score);
if(*fmt){
if(score <= AVPROBE_SCORE_MAX/4){ //this can only be true in the last iteration
av_log(logctx, AV_LOG_WARNING, "Format %s detected only with low score of %d, misdetection possible!\n", (*fmt)->name, score);
}else
av_log(logctx, AV_LOG_DEBUG, "Format %s probed with size=%d and score=%d\n", (*fmt)->name, probe_size, score);
}
}

if (!*fmt) {
av_free(buf);
return AVERROR_INVALIDDATA;
}

/* rewind. reuse probe buffer to avoid seeking */
if ((ret = ffio_rewind_with_probe_data(pb, buf, pd.buf_size)) < 0)
av_free(buf);

return ret;
}

本函数中用到了av_probe_input_format2 在其中又调用了av_probe_input_format3 请看代码

AVInputFormat *av_probe_input_format3(AVProbeData *pd, int is_opened, int *score_ret)
{
AVProbeData lpd = *pd;
AVInputFormat *fmt1 = NULL, *fmt;
int score, nodat = 0, score_max=0;

if (lpd.buf_size > 10 && ff_id3v2_match(lpd.buf, ID3v2_DEFAULT_MAGIC)) {
int id3len = ff_id3v2_tag_len(lpd.buf);
if (lpd.buf_size > id3len + 16) {
lpd.buf += id3len;
lpd.buf_size -= id3len;
}else
nodat = 1;
}
//从注册的链表中寻找对应的AVInputFormat对象 若是VC1源 最终匹配到AVInputFormat ff_mpegts_demuxer: 定义于libavformat/mpegts.c中
fmt = NULL;
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;
}
*score_ret= score_max;

return fmt;
}

看吧 该函数又从注册的全局链表结构中找到一个AVInputFormat结构,根据输入文件的具体格式 由程序自动完成的。经过这些步骤 来看avformat_open_input的剩下流程

int avformat_open_input(AVFormatContext **ps, const char *filename, AVInputFormat *fmt, AVDictionary **options)
{
AVFormatContext *s = *ps;
int ret = 0;
AVFormatParameters ap = { { 0 } };
AVDictionary *tmp = NULL;
//avformat_alloc_context()位于libavformat/options.c中 主要是为AVFormatContext分配空间 并且设置默认值
if (!s && !(s = avformat_alloc_context()))
return AVERROR(ENOMEM);
if (fmt)
s->iformat = fmt;

if (options)
av_dict_copy(&tmp, *options, 0);

if ((ret = av_opt_set_dict(s, &tmp)) < 0)
goto fail;
//init_input初始化AVInputFormat 然后在AVInputformat的read_header()中初始化AVFormatContext的结构 同时也会填充AVStream信息
if ((ret = init_input(s, filename, &tmp)) < 0)
goto fail;

/* check filename in case an image number is expected */
if (s->iformat->flags & AVFMT_NEEDNUMBER) {
if (!av_filename_number_test(filename)) {
ret = AVERROR(EINVAL);
goto fail;
}
}

s->duration = s->start_time = AV_NOPTS_VALUE;
av_strlcpy(s->filename, filename, sizeof(s->filename));

/* allocate private data */
if (s->iformat->priv_data_size > 0) {
if (!(s->priv_data = av_mallocz(s->iformat->priv_data_size))) {
ret = AVERROR(ENOMEM);
goto fail;
}
if (s->iformat->priv_class) {
*(const AVClass**)s->priv_data = s->iformat->priv_class;
av_opt_set_defaults(s->priv_data);
if ((ret = av_opt_set_dict(s->priv_data, &tmp)) < 0)
goto fail;
}
}

/* e.g. AVFMT_NOFILE formats will not have a AVIOContext */
if (s->pb)
ff_id3v2_read(s, ID3v2_DEFAULT_MAGIC);
//----若是VC1流 则read_header指针指向ff_raw_video_read_header 该函数位于libavformat/rawdec.c中 这个注释有错误
if (!(s->flags&AVFMT_FLAG_PRIV_OPT) && s->iformat->read_header){
if ((ret = s->iformat->read_header(s, &ap)) < 0)
goto fail;
}

if (!(s->flags&AVFMT_FLAG_PRIV_OPT) && s->pb && !s->data_offset)
s->data_offset = avio_tell(s->pb);

s->raw_packet_buffer_remaining_size = RAW_PACKET_BUFFER_SIZE;

if (options) {
av_dict_free(options);
*options = tmp;
}
*ps = s;
return 0;

fail:
av_dict_free(&tmp);
if (s->pb && !(s->flags & AVFMT_FLAG_CUSTOM_IO))
avio_close(s->pb);
avformat_free_context(s);
*ps = NULL;
return ret;
}

经过av_probe_input_format3寻找到合适的AVInputFormat结构后 赋予了s->iformat成员 而后会调用AVInputFormat中的read_header()函数 以vc1源为例 会匹配到libavformat/mpegts.c中的 AVInputFormat ff_mpegts_demuxer结构 而该对象中的read_header指针指向了mpegts_read_header 如下

AVInputFormat ff_mpegts_demuxer = {
.name           = "mpegts",
.long_name      = NULL_IF_CONFIG_SMALL("MPEG-2 transport stream format"),
.priv_data_size = sizeof(MpegTSContext),
.read_probe     = mpegts_probe,
.read_header    = mpegts_read_header,
.read_packet    = mpegts_read_packet,
.read_close     = mpegts_read_close,
.read_timestamp = mpegts_get_pcr,
.flags = AVFMT_SHOW_IDS|AVFMT_TS_DISCONT,
#ifdef USE_SYNCPOINT_SEARCH
.read_seek2 = read_seek2,
#endif
};

来看mpegts_read_header函数

static int mpegts_read_header(AVFormatContext *s,
AVFormatParameters *ap)
{
MpegTSContext *ts = s->priv_data;
AVIOContext *pb = s->pb;
uint8_t buf[8*1024];
int len;
int64_t pos;

av_log(s,AV_LOG_INFO,"进入mpegts_read_header中\n");
//av_log(s,AV_LOG_INFO,"stream地址:%d\n",s->streams[0]);

/* read the first 8192 bytes to get packet size */
pos = avio_tell(pb);
len = avio_read(pb, buf, sizeof(buf));
if (len != sizeof(buf))
goto fail;
ts->raw_packet_size = get_packet_size(buf, sizeof(buf));
if (ts->raw_packet_size <= 0) {
av_log(s, AV_LOG_WARNING, "Could not detect TS packet size, defaulting to non-FEC/DVHS\n");
ts->raw_packet_size = TS_PACKET_SIZE;
}
ts->stream = s;
ts->auto_guess = 0;
if (s->iformat == &ff_mpegts_demuxer) {
/* normal demux */

/* first do a scan to get all the services */
/* NOTE: We attempt to seek on non-seekable files as well, as the
* probe buffer usually is big enough. Only warn if the seek failed
* on files where the seek should work. */
if (avio_seek(pb, pos, SEEK_SET) < 0)
av_log(s, pb->seekable ? AV_LOG_ERROR : AV_LOG_INFO, "Unable to seek back to the start\n");

mpegts_open_section_filter(ts, SDT_PID, sdt_cb, ts, 1);

mpegts_open_section_filter(ts, PAT_PID, pat_cb, ts, 1);
//经过跟踪发现 填充s->streams的操作是在下面这个函数中完成的
handle_packets(ts, s->probesize / ts->raw_packet_size);
/* if could not find service, enable auto_guess */

ts->auto_guess = 1;

av_dlog(ts->stream, "tuning done\n");

/* only flag NOHEADER if we are in file mode,
in streaming mode scanning may take too long for users */
if (!url_is_streamed(pb))
s->ctx_flags |= AVFMTCTX_NOHEADER;
} else {
AVStream *st;
int pcr_pid, pid, nb_packets, nb_pcrs, ret, pcr_l;
int64_t pcrs[2], pcr_h;
int packet_count[2];
uint8_t packet[TS_PACKET_SIZE];

/* only read packets */

st = avformat_new_stream(s, NULL);
if (!st)
goto fail;
avpriv_set_pts_info(st, 60, 1, 27000000);
st->codec->codec_type = AVMEDIA_TYPE_DATA;
st->codec->codec_id = CODEC_ID_MPEG2TS;

/* we iterate until we find two PCRs to estimate the bitrate */
pcr_pid = -1;
nb_pcrs = 0;
nb_packets = 0;
for(;;) {
ret = read_packet(s, packet, ts->raw_packet_size);
if (ret < 0)
return -1;
pid = AV_RB16(packet + 1) & 0x1fff;
if ((pcr_pid == -1 || pcr_pid == pid) &&
parse_pcr(&pcr_h, &pcr_l, packet) == 0) {
pcr_pid = pid;
packet_count[nb_pcrs] = nb_packets;
pcrs[nb_pcrs] = pcr_h * 300 + pcr_l;
nb_pcrs++;
if (nb_pcrs >= 2)
break;
}
nb_packets++;
}

/* NOTE1: the bitrate is computed without the FEC */
/* NOTE2: it is only the bitrate of the start of the stream */
ts->pcr_incr = (pcrs[1] - pcrs[0]) / (packet_count[1] - packet_count[0]);
ts->cur_pcr = pcrs[0] - ts->pcr_incr * packet_count[0];
s->bit_rate = (TS_PACKET_SIZE * 8) * 27e6 / ts->pcr_incr;
st->codec->bit_rate = s->bit_rate;
st->start_time = ts->cur_pcr;
av_dlog(ts->stream, "start=%0.3f pcr=%0.3f incr=%d\n",
st->start_time / 1000000.0, pcrs[0] / 27e6, ts->pcr_incr);
}

avio_seek(pb, pos, SEEK_SET);
return 0;
fail:
return -1;
}

跟踪avformat_new_stream如下:

AVStream *avformat_new_stream(AVFormatContext *s, AVCodec *c)
{
AVStream *st;
int i;
AVStream **streams;

if (s->nb_streams >= INT_MAX/sizeof(*streams))
return NULL;
streams = av_realloc(s->streams, (s->nb_streams + 1) * sizeof(*streams));
if (!streams)
return NULL;
s->streams = streams;

st = av_mallocz(sizeof(AVStream));
if (!st)
return NULL;
if (!(st->info = av_mallocz(sizeof(*st->info)))) {
av_free(st);
return NULL;
}

st->codec = avcodec_alloc_context3(c);
if (s->iformat) {
/* no default bitrate if decoding */
st->codec->bit_rate = 0;
}
st->index = s->nb_streams;
st->start_time = AV_NOPTS_VALUE;
st->duration = AV_NOPTS_VALUE;
/* we set the current DTS to 0 so that formats without any timestamps
but durations get some timestamps, formats with some unknown
timestamps have their first few packets buffered and the
timestamps corrected before they are returned to the user */
st->cur_dts = 0;
st->first_dts = AV_NOPTS_VALUE;
st->probe_packets = MAX_PROBE_PACKETS;

/* default pts setting is MPEG-like */
avpriv_set_pts_info(st, 33, 1, 90000);
st->last_IP_pts = AV_NOPTS_VALUE;
for(i=0; i<MAX_REORDER_DELAY+1; i++)
st->pts_buffer[i]= AV_NOPTS_VALUE;
st->reference_dts = AV_NOPTS_VALUE;

st->sample_aspect_ratio = (AVRational){0,1};

s->streams[s->nb_streams++] = st;
return st;
}

这里为AVStream分配空间 同时为AVCodecContext分配空间 呵呵 发现了什么?这里还有一个问题 就是在if判断中 不会走avformat_new_stream流程 而会走handle_packets流程 具体分析留给大家
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: