一. avformat_alloc_context
1.1 在./libavformat/options.c中
-
AVFormatContext *avformat_alloc_context(void)
-
{
-
AVFormatContext *ic;
-
ic = av_malloc(sizeof(AVFormatContext)); //为AVFormatContex分配内存
-
avformat_get_context_defaults(ic); //下面就是一些初始化的操作
-
-
ic->internal = av_mallocz(sizeof(*ic->internal));
-
if (!ic->internal) {
-
avformat_free_context(ic);
-
return NULL;
-
}
-
ic->internal->offset = AV_NOPTS_VALUE;
-
ic->internal->raw_packet_buffer_remaining_size = RAW_PACKET_BUFFER_SIZE;
-
-
return ic;
-
}
为AVFormatContext分配内存并初始化
二. avformat_open_init
2.1 avformat_open_init总体分析
在libavformat/utils.c中
avformat_open_input(&ic, is->filename, is->iformat, &format_opts);
{
1. avformat_alloc_context
::分配一个AVFormatContext结构体
2.init_input(s, filename, &tmp);
--> av_probe_input_format2
::没有打开媒体文件之前的probe
-->s->io_open
::打开媒体文件
-->io_open_default
-->ffio_open_whitelist
-->ffurl_alloc
-->url_find_protocol (file protocol)
-->url_alloc_for_protocol
-->ffurl_connect ::uc->prot->url_open
-->file_open
-->avpriv_open ::即调用系统的open
-->av_probe_input_buffer2
::打开媒体文件之后的probe
--> avio_read ::s->read_packet
--> ffurl_read
--> file_read ::即调用系统的read
-->av_probe_input_format2
--> av_probe_input_format3
--> matroska_probe
--> mov_probe
3.s->iformat->read_header(s)
::读取媒体文件中的信息并分析
--> mov_read_header
}
2.2代码分析
2.2.1 avformat_open_input
--> init_input
-
static int init_input(AVFormatContext *s, const char *filename, AVDictionary **options)
-
{
-
int ret;
-
AVProbeData pd = { filename, NULL, 0 };
-
int score = AVPROBE_SCORE_RETRY;
-
-
if (s->pb) { //如果用户指定了必须使用哪个iformat则进入下面的流程
-
... //这儿略过
-
return 0;
-
}
-
-
if ((s->iformat && s->iformat->flags & AVFMT_NOFILE) ||
-
(!s->iformat && (s->iformat = av_probe_input_format2(&pd, 0, &score))))
-
return score;
-
-
if ((ret = s->io_open(s, &s->pb, filename, AVIO_FLAG_READ | s->avio_flags, options)) < 0)
-
return ret;
-
-
if (s->iformat)
-
return 0;
-
return av_probe_input_buffer2(s->pb, &s->iformat, filename, s, 0, s->format_probesize);
-
}
说明:
a.av_probe_input_format2 时score=25,probe出来的probe_score=0,
probe_scoreiformat=NULL
b. av_probe_input_buffer2中也调用了av_probe_input_format2只不过中间的参数is_opened是1
说明文件己被打开
2.2.2
avformat_open_input
--> init_input
--> av_probe_input_buffer2
-
int av_probe_input_buffer2(AVIOContext *pb, AVInputFormat **fmt,
-
const char *filename, void *logctx,
-
unsigned int offset, unsigned int max_probe_size)
-
{
-
AVProbeData pd = { filename ? filename : "" };
-
uint8_t *buf = NULL;
-
int ret = 0, probe_size, buf_offset = 0;
-
int score = 0;
-
int ret2;
-
-
if (!max_probe_size)
-
max_probe_size = PROBE_BUF_MAX;
-
else if (max_probe_size < PROBE_BUF_MIN) {
-
av_log(logctx, AV_LOG_ERROR,
-
"Specified probe size value %u cannot be < %u\n", max_probe_size, PROBE_BUF_MIN);
-
return AVERROR(EINVAL);
-
}
-
-
if (offset >= max_probe_size)
-
return AVERROR(EINVAL);
-
-
if (pb->av_class) {
-
uint8_t *mime_type_opt = NULL;
-
char *semi;
-
av_opt_get(pb, "mime_type", AV_OPT_SEARCH_CHILDREN, &mime_type_opt);
-
pd.mime_type = (const char *)mime_type_opt;
-
semi = pd.mime_type ? strchr(pd.mime_type, ';') : NULL;
-
if (semi) {
-
*semi = '\0';
-
}
-
}
-
-
-
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))) {
-
score = probe_size < max_probe_size ? AVPROBE_SCORE_RETRY : 0;
-
-
//分配内存为读取媒体文件的内容作准备
-
av_reallocp(&buf, probe_size + AVPROBE_PADDING_SIZE);
-
//读取媒体文件的内容作,就是一个调用read的过程
-
avio_read(pb, buf + buf_offset, probe_size - buf_offset);
-
buf_offset += ret;
-
if (buf_offset < offset)
-
continue;
-
pd.buf_size = buf_offset - offset;
-
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); //对媒体文件的内容进行probe看是属于哪个类型
-
if (*fmt) {
-
/* This can only be true in the last iteration. */
-
if (score <= AVPROBE_SCORE_RETRY) {
-
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)
-
ret = AVERROR_INVALIDDATA;
-
-
fail:
-
/* Rewind. Reuse probe buffer to avoid seeking. */
-
ret2 = ffio_rewind_with_probe_data(pb, &buf, buf_offset);
-
if (ret >= 0)
-
ret = ret2;
-
-
av_freep(&pd.mime_type);
-
return ret < 0 ? ret : score;
-
}
2.2.3
avformat_open_input
--> init_input
--> av_probe_input_buffer2
-->
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, score_max = 0;
-
const static uint8_t zerobuffer[AVPROBE_PADDING_SIZE];
-
enum nodat {
-
NO_ID3,
-
ID3_ALMOST_GREATER_PROBE,
-
ID3_GREATER_PROBE,
-
ID3_GREATER_MAX_PROBE,
-
} nodat = NO_ID3;
-
-
if (!lpd.buf)
-
lpd.buf = (unsigned char *) zerobuffer;
-
-
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) {
-
if (lpd.buf_size < 2LL*id3len + 16)
-
nodat = ID3_ALMOST_GREATER_PROBE;
-
lpd.buf += id3len;
-
lpd.buf_size -= id3len;
-
} else if (id3len >= PROBE_BUF_MAX) {
-
nodat = ID3_GREATER_MAX_PROBE;
-
} else
-
nodat = ID3_GREATER_PROBE;
-
}
-
-
fmt = NULL;
-
while ((fmt1 = av_iformat_next(fmt1))) {
-
//当is_opened=0时,只对alsa fbdev rtp rtsp这样flag=AVFMT_NOFILE的AVInputFormat调用probe
-
//当is_opened=1时,只对ac3 dts h263 h264 这样flag不含AVFMT_NOFILE的调用probe
-
if (!is_opened == !(fmt1->flags & AVFMT_NOFILE) && strcmp(fmt1->name, "image2"))
-
continue;
-
score = 0;
-
if (fmt1->read_probe) {
-
score = fmt1->read_probe(&lpd); //lpd中的buf是媒体文件中的头2048个字节
-
if (score)
-
av_log(NULL, AV_LOG_TRACE, "Probing %s score:%d size:%d\n", fmt1->name, score, lpd.buf_size);
-
if (fmt1->extensions && av_match_ext(lpd.filename, fmt1->extensions)) {
-
switch (nodat) {
-
case NO_ID3:
-
score = FFMAX(score, 1);
-
break;
-
case ID3_GREATER_PROBE:
-
case ID3_ALMOST_GREATER_PROBE:
-
score = FFMAX(score, AVPROBE_SCORE_EXTENSION / 2 - 1);
-
break;
-
case ID3_GREATER_MAX_PROBE:
-
score = FFMAX(score, AVPROBE_SCORE_EXTENSION);
-
break;
-
}
-
}
-
} else if (fmt1->extensions) {
-
if (av_match_ext(lpd.filename, fmt1->extensions))
-
score = AVPROBE_SCORE_EXTENSION;
-
}
-
if (av_match_name(lpd.mime_type, fmt1->mime_type))
-
score = FFMAX(score, AVPROBE_SCORE_MIME);
-
if (score > score_max) { //循环结束之后就是要取最大的score所代表的fmt
-
score_max = score;
-
fmt = fmt1;
-
} else if (score == score_max)
-
fmt = NULL;
-
}
-
if (nodat == ID3_GREATER_PROBE)
-
score_max = FFMIN(AVPROBE_SCORE_EXTENSION / 2 - 1, score_max);
-
*score_ret = score_max; //将probe中产生的最大的score与fmt返回
-
-
return fmt;
-
}
s->iformat->name=mov,mp4,m4a,3gp,3g2,mj2
s->iformat->name=matroska,webm
三. 媒体文件的读写
3.1 媒体文件的打开过程
avformat_open_input
init_input
--> s->io_open
-->io_open_default ::
libavformat/options.c
--> ffio_open_whitelist ::libavformat/aviobuf.c
--> ffurl_open_whitelist ::libavformat/avio.c
-->ffurl_connect
::libavformat/avio.c
--> file_open ::libavformat/file.c
AVFormatContext s;
s->io_open(s, &s->pb, filename, AVIO_FLAG_READ | s->avio_flags, options)
3.2代码分析
3.2.1 avio层
-
int ffio_open_whitelist(AVIOContext **s, const char *filename, int flags,
-
const AVIOInterruptCB *int_cb, AVDictionary **options,
-
const char *whitelist
-
)
-
{
-
ffurl_open_whitelist(&h, filename, flags, int_cb, options, whitelist);
-
--> furl_alloc(puc, filename, flags, int_cb);
-
--> p = url_find_protocol(filename); //根据文件名判断protocol类型,并返回protocol的指针
-
--> url_alloc_for_protocol(puc, p, filename, flags, int_cb); //分配内存并初始化p
-
--> ffurl_connect(*puc, options);
-
ffio_fdopen(s, h);
-
return 0;
-
}
3.2.2 avio层
-
static struct URLProtocol *url_find_protocol(const char *filename)
-
{
-
URLProtocol *up = NULL;
-
char proto_str[128], proto_nested[128], *ptr;
-
size_t proto_len = strspn(filename, URL_SCHEME_CHARS);
-
//根据文件名来判断protocol类型,若文件名中没有:则为file类型的protocol
-
if (filename[proto_len] != ':' &&
-
(strncmp(filename, "subfile,", 8) || !strchr(filename + proto_len + 1, ':')) ||
-
is_dos_path(filename))
-
strcpy(proto_str, "file");
-
else
-
av_strlcpy(proto_str, filename,
-
FFMIN(proto_len + 1, sizeof(proto_str)));
-
-
if ((ptr = strchr(proto_str, ',')))
-
*ptr = '\0';
-
av_strlcpy(proto_nested, proto_str, sizeof(proto_nested));
-
if ((ptr = strchr(proto_nested, '+')))
-
*ptr = '\0';
-
while (up = ffurl_protocol_next(up)) { //查找到file类型的URLProtocol并返回
-
if (!strcmp(proto_str, up->name))
-
break;
-
if (up->flags & URL_PROTOCOL_FLAG_NESTED_SCHEME &&
-
!strcmp(proto_nested, up->name))
-
break;
-
}
-
-
return up;
-
}
注:ffmpeg所支持的所有的protocol都在libavformat/Makefile中以CONFIG_XXX_PROTOCOL标志
3.2.3 avio层
-
int ffurl_connect(URLContext *uc, AVDictionary **options)
-
{
-
//uc->prot->url_open是真正的打开文件的函数
-
uc->prot->url_open2 ? uc->prot->url_open2(uc, uc->filename, uc->flags, options) :
-
uc->prot->url_open(uc, uc->filename, uc->flags);
-
return 0;
-
}
3.2.4 这就到了protocol层
libavformat/file.c
-
static int file_open(URLContext *h, const char *filename, int flags)
-
{
-
FileContext *c = h->priv_data;
-
int fd;
-
-
fd = avpriv_open(filename, access, 0666);
-
--> 这个就是open函数的最后一层封装
-
c->fd = fd;
-
return 0;
-
}
3.3 媒体文件的读过程
3.3.1 读写函数的初始化过程
ffio_open_whitelist ::libavformat/aviobuf.c
ffio_fdopen 初始代读写函数 ::libavformat/aviobuf.c
avio_alloc_context ::libavformat/aviobuf.c
ffio_init_context ::libavformat/aviobuf.c
ffio_init_context ::libavformat/aviobuf.c
s->write_packet = write_packet; :: ffurl_write 终于找到了
s->read_packet = read_packet; :: ffurl_read
3.3.2 读写函数的调用过程
av_probe_input_buffer2 ::libavformat/format.c
avio_read ::libavformat/aviobuf.c ::ffmpeg提供的read接口
s->read_packet
ffurl_read ::libavformat/avio.c
retry_transfer_wrapper ::libavformat/avio.c
阅读(2163) | 评论(0) | 转发(0) |