hello,各位好,本人是一名嵌入式软件工程师,目前正使用ffmpeg开发一款嵌入式多媒体播放器,《ffmpeg分析》系列博文是本人在阅读ffmpeg源代码时所做的笔记,希望对各位有点帮助。分析过程结合下面的例程:
一. 调用av_register_all函数注册所有的格式和编码解码器.
1.1 先调用avcodec_register_all函数注册所有的编码解码器.
1. 下面列出与H264相关的注册:
// 注册硬件加速器
REGISTER_HWACCEL (H264_DXVA2, h264_dxva2);
REGISTER_HWACCEL (H264_VAAPI, h264_vaapi);
// 注册解码器
REGISTER_DECODER (H264, h264);
REGISTER_DECODER (H264_VDPAU, h264_vdpau);
// 注册编码器
REGISTER_ENCODER (LIBX264, libx264);
// 注册分析器
REGISTER_PARSER (H264, h264);
// 注册位流分离器
REGISTER_BSF (H264_MP4TOANNEXB, h264_mp4toannexb);
|
2. 下面列出注册宏:
#define REGISTER_HWACCEL(X,x){
\ extern AVHWAccel x##_hwaccel; \ if(CONFIG_##X##_HWACCEL)
av_register_hwaccel(&x##_hwaccel);}
#define REGISTER_ENCODER(X,x){ \ extern AVCodec x##_encoder; \ if(CONFIG_##X##_ENCODER)
avcodec_register(&x##_encoder);}
#define REGISTER_DECODER(X,x){ \ extern AVCodec x##_decoder; \ if(CONFIG_##X##_DECODER)
avcodec_register(&x##_decoder);}
#define REGISTER_ENCDEC(X,x) REGISTER_ENCODER(X,x);
REGISTER_DECODER(X,x)
#define REGISTER_PARSER(X,x){ \ extern AVCodecParser x##_parser; \ if(CONFIG_##X##_PARSER)
av_register_codec_parser(&x##_parser);}
#define REGISTER_BSF(X,x){ \ extern AVBitStreamFilter x##_bsf; \ if(CONFIG_##X##_BSF)
av_register_bitstream_filter(&x##_bsf);}
|
3. 分析一下注册函数, 以avcodec_register函数为例:
void avcodec_register(AVCodec*codec) {
AVCodec **p;
avcodec_init();
p = &first_avcodec; while (*p!=
NULL) p
=&(*p)->next; *p = codec;
codec->next=
NULL; }
|
可以看到avcodec_register函数把输入的AVCodec连成一个链表, 其它注册函数与之类似, 就不多言了.
4. 上面调用了avcodec_init函数:
void avcodec_init(void) { static int initialized= 0;
if (initialized!= 0) return;
initialized = 1;
dsputil_static_init (); }
|
这个函数只会真正执行一次.
5. 上面调用了dsputil_static_init函数:
av_coldvoid dsputil_static_init(void) { int i;
for(i=0;i<256;i++)
ff_cropTbl[i + MAX_NEG_CROP]= i; for(i=0;i<MAX_NEG_CROP;i++){
ff_cropTbl[i]= 0;
ff_cropTbl[i
+ MAX_NEG_CROP + 256]= 255; }
for(i=0;i<512;i++){
ff_squareTbl[i]=
(i - 256)
*(i
- 256); }
for(i=0; i<64; i++)
inv_zigzag_direct16[ff_zigzag_direct[i]]= i+1; }
|
可以看到, 它初始化了一些静态数据.
1.2 注册所有的格式和外部库及协议.
1. 下面列出与H264相关的注册:
// 注册分离器和混合器
REGISTER_MUXDEMUX (H264, h264);
// 注册文件协议
REGISTER_PROTOCOL (FILE,file);
|
2. 下面列出注册宏:
#define REGISTER_MUXER(X,x){
\ extern AVOutputFormat x##_muxer; \ if(CONFIG_##X##_MUXER)
av_register_output_format(&x##_muxer);}
#define REGISTER_DEMUXER(X,x){ \ extern AVInputFormat x##_demuxer; \ if(CONFIG_##X##_DEMUXER)
av_register_input_format(&x##_demuxer);}
#define REGISTER_MUXDEMUX(X,x) REGISTER_MUXER(X,x);
REGISTER_DEMUXER(X,x)
#define REGISTER_PROTOCOL(X,x){ \ extern URLProtocol x##_protocol; \ if(CONFIG_##X##_PROTOCOL)
av_register_protocol(&x##_protocol);}
|
这些注册函数与avcodec_register函数类似, 就不多言了.
URL协议结构:
typedefstructURLProtocol{ constchar*name; int(*url_open)(URLContext*h,constchar*url,intflags); int(*url_read)(URLContext*h,unsignedchar*buf,intsize); int(*url_write)(URLContext*h,unsignedchar*buf,intsize); int64_t(*url_seek)(URLContext*h,int64_tpos,intwhence); int(*url_close)(URLContext*h); structURLProtocol*next; int(*url_read_pause)(URLContext*h,intpause); int64_t(*url_read_seek)(URLContext*h,intstream_index, int64_ttimestamp,intflags); int(*url_get_file_handle)(URLContext*h); }URLProtocol;
|
libavformat/file.c文件的file协议:
staticint file_open(URLContext*h,
const char
*filename,
int flags) { int access; int fd;
av_strstart(filename,"file:",
&filename);
if (flags& URL_RDWR){
access = O_CREAT
| O_TRUNC | O_RDWR; } else
if (flags & URL_WRONLY){
access = O_CREAT
| O_TRUNC | O_WRONLY; } else
{
access = O_RDONLY; } #ifdef O_BINARY
access |= O_BINARY; #endif
fd = open(filename, access, 0666); if (fd
== -1) return AVERROR(errno);
h->priv_data=
(void*)
(intptr_t) fd; return 0; }
static int file_read(URLContext*h,
unsigned char
*buf,
int size) { int fd =(intptr_t) h->priv_data; return read(fd, buf, size); }
static int file_write(URLContext*h,
unsigned char
*buf,
int size) { int fd =(intptr_t) h->priv_data; return write(fd, buf, size); }
/* XXX: use llseek */ static int64_t file_seek(URLContext*h,
int64_t pos,
int whence) { int fd =(intptr_t) h->priv_data; if (whence== AVSEEK_SIZE){ struct stat st; int ret = fstat(fd,&st); return ret
< 0 ? AVERROR(errno): st.st_size; } return lseek(fd, pos, whence); }
static int file_close(URLContext*h) { int fd =(intptr_t) h->priv_data; return close(fd); }
static int file_get_handle(URLContext*h) { return (intptr_t) h->priv_data; }
URLProtocol file_protocol =
{ "file",
file_open,
file_read,
file_write,
file_seek,
file_close, .url_get_file_handle
= file_get_handle, };
|
libavformat/allformats.c文件的av_register_all函数注册了file协议:
#define REGISTER_PROTOCOL(X,x){
\ extern URLProtocol x##_protocol; \ if(CONFIG_##X##_PROTOCOL)
av_register_protocol(&x##_protocol);}
|
void av_register_all(void) { /* 省略部分代码 */ /* protocols */
REGISTER_PROTOCOL (FILE,file); /* 省略部分代码 */ }
|
URLProtocol*first_protocol
=NULL;
int av_register_protocol(URLProtocol*protocol)
{
URLProtocol **p;
p = &first_protocol;
while (*p!=
NULL) p
=&(*p)->next;
*p = protocol;
protocol->next=
NULL;
return 0;
}
http://blogold.chinaunix.net/u3/104564/showart_2369209.html
探测数据结构:
/** This structure contains the data a format has to probe a file. */ typedefstructAVProbeData{ constchar*filename; unsignedchar*buf;/**< Buffer must have AVPROBE_PADDING_SIZE of extra allocated bytes
filled with zero. */ intbuf_size;/**< Size of buf except extra allocated bytes */ }AVProbeData;
|
h264的探测函数:
staticint h264_probe(AVProbeData*p) { uint32_t code=-1; int sps=0, pps=0, idr=0,
res=0, sli=0; int i;
for(i=0; i<p->buf_size;
i++){
code = (code<<8)+ p->buf[i]; if ((code& 0xffffff00)==
0x100){ int ref_idc=(code>>5)&3; int type
= code & 0x1F; static
const int8_t ref_zero[32]={
2, 0, 0, 0, 0,-1,
1,-1, -1, 1, 1, 1, 1,-1,
2, 2,
2, 2, 2, 0, 2, 2, 2,
2,
2, 2, 2, 2, 2, 2, 2,
2 };
if(code& 0x80)
//forbidden bit
return 0;
if(ref_zero[type]== 1
&& ref_idc) return 0; if(ref_zero[type]==-1&&
!ref_idc) return 0; if(ref_zero[type]== 2)
res++;
switch(type){ case 1: sli++;break; case 5: idr++;break; case 7: if(p->buf[i+2]&0x0F) return 0;
sps++; break; case 8: pps++;break; } } } if(sps
&& pps &&(idr||sli>3)&&
res<(sps+pps+idr)) return AVPROBE_SCORE_MAX/2+1;// +1 for .mpg
return 0; }
|
视频读首部函数:
staticint video_read_header(AVFormatContext*s,
AVFormatParameters *ap) {
AVStream *st;
st = av_new_stream(s, 0); if (!st) return AVERROR(ENOMEM);
st->codec->codec_type= AVMEDIA_TYPE_VIDEO;
st->codec->codec_id= s->iformat->value;
st->need_parsing= AVSTREAM_PARSE_FULL;
/* for MJPEG, specify frame rate */ /* for MPEG-4 specify it, too (most MPEG-4 streams do not have the fixed_vop_rate set ...)*/ if (ap->time_base.num){
st->codec->time_base= ap->time_base; } else
if ( st->codec->codec_id==
CODEC_ID_MJPEG||
st->codec->codec_id== CODEC_ID_MPEG4||
st->codec->codec_id== CODEC_ID_DIRAC||
st->codec->codec_id== CODEC_ID_DNXHD||
st->codec->codec_id== CODEC_ID_H264){
st->codec->time_base=(AVRational){1,25}; }
av_set_pts_info(st, 64, 1, 1200000);
return 0; }
|
原始地读实际的包函数:
int ff_raw_read_partial_packet(AVFormatContext*s,
AVPacket*pkt) { int ret, size;
size = RAW_PACKET_SIZE;
if (av_new_packet(pkt, size)< 0) return AVERROR(ENOMEM);
pkt->pos= url_ftell(s->pb);
pkt->stream_index= 0;
ret = get_partial_buffer(s->pb, pkt->data,
size); if (ret< 0)
{
av_free_packet(pkt); return ret; }
pkt->size= ret; return ret; }
|
原始地写包函数:
staticintraw_write_packet(structAVFormatContext*s,AVPacket*pkt) {
put_buffer(s->pb,pkt->data,pkt->size);
put_flush_packet(s->pb); return0; }
|
h264混合器:
AVOutputFormat h264_muxer={ "h264",
NULL_IF_CONFIG_SMALL("raw H.264 video format"), NULL, "h264",
0,
CODEC_ID_NONE,
CODEC_ID_H264, NULL,
raw_write_packet, .flags=AVFMT_NOTIMESTAMPS, };
|
h264分离器:
AVInputFormat h264_demuxer=
{ "h264",
NULL_IF_CONFIG_SMALL("raw H.264 video format"),
0,
h264_probe,
video_read_header,
ff_raw_read_partial_packet, .flags= AVFMT_GENERIC_INDEX, .extensions ="h26l,h264,264",//FIXME remove after writing mpeg4_probe .value = CODEC_ID_H264, }
|
libavformat/allformats.c文件的av_register_all函数注册了h264分离器和混合器:
#define REGISTER_MUXER(X,x){
\ extern AVOutputFormat x##_muxer; \ if(CONFIG_##X##_MUXER)
av_register_output_format(&x##_muxer);}
#define REGISTER_DEMUXER(X,x){ \ extern AVInputFormat x##_demuxer; \ if(CONFIG_##X##_DEMUXER)
av_register_input_format(&x##_demuxer);}
#define REGISTER_MUXDEMUX(X,x) REGISTER_MUXER(X,x);
REGISTER_DEMUXER(X,x)
|
void av_register_all(void) { /* 省略部分代码 */ /* protocols */
REGISTER_MUXDEMUX (H264, h264); /* 省略部分代码 */ }
|
把注册格式函数也贴出来吧:
/** head of registered input format linked list */
AVInputFormat *first_iformat
= NULL; /** head of registered output format linked list */
AVOutputFormat *first_oformat
= NULL;
void av_register_input_format(AVInputFormat*format) {
AVInputFormat **p;
p = &first_iformat; while (*p!=
NULL) p
=&(*p)->next; *p = format;
format->next=
NULL; }
void av_register_output_format(AVOutputFormat*format) {
AVOutputFormat **p;
p = &first_oformat; while (*p!=
NULL) p
=&(*p)->next; *p = format;
format->next=
NULL; }
|
http://blogold.chinaunix.net/u3/104564/showart_2369231.html
调用av_open_input_file(&pFormatCtx, is->filename,NULL,
0,NULL)函数打开输入的文件.
1. 分析一下函数原型:
int av_open_input_file(AVFormatContext**ic_ptr,// 输出参数:
格式上下文
const
char *filename,// 文件名
AVInputFormat *fmt,// 输入的格式, 为NULL, 即未知
int buf_size,// 缓冲的大小, 为0
AVFormatParameters *ap);// 格式的参数, 为NULL
2. 初始化探测数据:
AVProbeData probe_data,
*pd = &probe_data;
pd->filename=
"";
if (filename)
pd->filename= filename;
pd->buf=
NULL;
pd->buf_size= 0;
3. 探测输入的格式:
if (!fmt){
// fmt == NULL, 成立
fmt = av_probe_input_format(pd, 0);
}
进入av_probe_input_format函数:
AVInputFormat *av_probe_input_format(AVProbeData*pd,
int is_opened)
{
int score=0;
return av_probe_input_format2(pd, is_opened,&score);
}
进入av_probe_input_format2函数:
AVInputFormat *av_probe_input_format2(AVProbeData*pd,
int is_opened,
int *score_max)
{
AVInputFormat *fmt1,*fmt;
int score;
fmt = NULL;
for(fmt1= first_iformat; fmt1!=
NULL; fmt1
= fmt1->next){
if (!is_opened==
!(fmt1->flags& AVFMT_NOFILE))//
is_opened == 0, fmt1->flags 没有设置 AVFMT_NOFILE 标志时成立
continue;
/* 省略部分代码 */
}
见libavformat/raw.c文件:
AVInputFormat h264_demuxer =
{
"h264",
NULL_IF_CONFIG_SMALL("raw H.264 video format"),
0,
h264_probe,
video_read_header,
ff_raw_read_partial_packet,
.flags= AVFMT_GENERIC_INDEX,
.extensions ="h26l,h264,264",//FIXME remove after writing mpeg4_probe
.value = CODEC_ID_H264,
};
由于 h264_demuxer.flags
== AVFMT_GENERIC_INDEX, 所以上面成立,continue, 返回的 AVInputFormat 指针为NULL,
探测不成功.
1. 打开文件:
if(!fmt||!(fmt->flags&AVFMT_NOFILE)){
|
因 fmt == NULL, 上面成立, 再看下面的代码:
ByteIOContext*pb
= NULL;
// 字节IO上下文
if ((err=url_fopen(&pb,
filename, URL_RDONLY))< 0)
{ // 只读方式打开输入的文件 goto fail; } if (buf_size> 0)
{ // 因 buf_size == 0, 不成立
url_setbufsize(pb, buf_size);
}
|
进入url_fopen函数:
int url_fopen(ByteIOContext**s,//
输出参数: 字节IO上下文 const
char *filename,// 文件名 int flags)// 标志 {
URLContext *h;// URL(统一资源定位)上下文 int err;
err = url_open(&h, filename, flags);//
打开URL if (err< 0) return err;
err = url_fdopen(s, h);// 用URL上下文打开字节IO上下文 if (err< 0)
{
url_close(h); return err; } return 0; }
|
进入url_open函数:
int url_open(URLContext**puc, //输出参数:URL上下文
constchar
*filename, // 文件名
int flags)// 标志
{
URLProtocol *up; const char*p; char proto_str[128],*q;
// 提取协议
p = filename;
q = proto_str; while (*p!=
'\0' &&*p
!=':')
{ // 未结束, 并未遇到分隔符':' if (!isalpha(*p))//
如果不是英文字母 goto file_proto; if ((q- proto_str)<
sizeof(proto_str)- 1) *q++=
*p;// 记录协议字符串
p++; }
if (*p==
'\0' || is_dos_path(filename)){
// 如果上面是因为结束而跳出, 或且 文件名是DOS路径
file_proto: strcpy(proto_str,"file");//
文件协议 } else
{ *q ='\0';
// 追加结束符 }
up = first_protocol; while (up!=
NULL)
{ if (!strcmp(proto_str, up->name))//
协议匹配 return url_open_protocol
(puc, up, filename, flags);// 用这个协议打开URL
up = up->next; } *puc =
NULL; return AVERROR(ENOENT); }
|
进入
url_open_protocol函数:
int url_open_protocol(URLContext
**puc, //输出参数:URL上下文
struct URLProtocol*up, //
URL协议
constchar
*filename, // 文件名
int flags)// 标志 {
URLContext *uc; int err;
// 网络初始化 #if CONFIG_NETWORK if (!ff_network_init()) return AVERROR(EIO); #endif
// 分配URL上下文并加上文件名的存储空间
uc = av_mallocz(sizeof(URLContext)+
strlen(filename)+ 1); if (!uc){
err = AVERROR(ENOMEM); goto fail; }
// 初始化URL上下文 #if LIBAVFORMAT_VERSION_MAJOR>= 53
uc->av_class=
&urlcontext_class; #endif
// 记录文件名
uc->filename=
(char*)
&uc[1]; strcpy(uc->filename, filename);
uc->prot= up; // URL协议
uc->flags= flags;//
标志
uc->is_streamed= 0;// 默认不是流, 可以在up->url_open函数里修改
uc->max_packet_size= 0;
// 包最大多大,默认为0,可以在up->url_open函数里修改 // 打开URL
err = up->url_open(uc, filename, flags); if (err< 0)
{
av_free(uc); goto fail; }
if((flags
& (URL_WRONLY
| URL_RDWR))//如果以可写方式打开
||!strcmp(up->name,"file"))//或且是文件协议
//如果不是流并且不可以url_seek
if(!uc->is_streamed&&
url_seek(uc, 0,SEEK_SET)
< 0)
uc->is_streamed= 1;//强制为流
// 输出 参数:URL上下文
*puc= uc; return 0;
fail: *puc =
NULL; #if CONFIG_NETWORK
ff_network_close(); #endif return err; }
|
先来看看url_get_max_packet_size函数
inturl_get_max_packet_size(URLContext*h) { returnh->max_packet_size;// 包最大多大, 被上面初始化为0 }
|
进入url_fdopen函数:
int url_fdopen(
ByteIOContext**s,//
输出参数: 字节IO上下文
URLContext*h)
// URL上下文 { uint8_t *buffer; int buffer_size, max_packet_size;
max_packet_size = url_get_max_packet_size(h); if (max_packet_size){
buffer_size = max_packet_size; } else
{
buffer_size = IO_BUFFER_SIZE;// 缓冲大小为IO_BUFFER_SIZE }
buffer = av_malloc(buffer_size);// 分配缓冲 if (!buffer) return AVERROR(ENOMEM);
*s = av_mallocz(sizeof(ByteIOContext));//
分配字节IO上下文
if(!*s){
av_free(buffer); return AVERROR(ENOMEM); }
if (init_put_byte(*s, buffer, buffer_size, (h->flags& URL_WRONLY
|| h->flags& URL_RDWR),
h,
url_read, url_write, url_seek)< 0)
{
av_free(buffer);
av_freep(s); return AVERROR(EIO); } (*s)->is_streamed= h->is_streamed;//
是否为流 (*s)->max_packet_size= max_packet_size;//包最大多大 if(h->prot){ (*s)->read_pause=
(int(*)(void*,
int))h->prot->url_read_pause;//
读暂停函数 (*s)->read_seek=
(int64_t(*)(void*,
int,
int64_t,int))h->prot->url_read_seek;//
读seek函数 } return 0; }
|
进入init_put_byte函数:
int init_put_byte(ByteIOContext*s,
// 字节IO上下文
unsigned
char *buffer,// 缓冲
int buffer_size,// 缓冲的大小
int write_flag,// 写标志
void
*opaque, // URL上下文
int
(*read_packet)(void*opaque,
uint8_t *buf,int buf_size),//
读包
int
(*write_packet)(void*opaque,
uint8_t *buf,int buf_size),//
写包
int64_t
(*seek)(void*opaque,
int64_t offset,
int whence))// 调整文件指针
{
s->buffer= buffer;
s->buffer_size= buffer_size;
s->buf_ptr= buffer;
s->opaque= opaque;
url_resetbuf(s, write_flag? URL_WRONLY
: URL_RDONLY);
s->write_packet= write_packet;
s->read_packet= read_packet;
s->seek= seek;
s->pos
= 0;
s->must_flush= 0;
s->eof_reached= 0;
s->error= 0;
s->is_streamed= 0;
s->max_packet_size= 0;
s->update_checksum=NULL;
if(!read_packet&&
!write_flag){
s->pos= buffer_size;
s->buf_end= s->buffer+ buffer_size;
}
s->read_pause=
NULL;
s->read_seek=
NULL;
return 0;
}
void*logctx= ap&&
ap->prealloced_context?
*ic_ptr :
NULL;// 因为 ap == NULL, 所以 logctx 也 == NULL.
if (!fmt&&
(err = ff_probe_input_buffer(&pb,&fmt,
filename, logctx, 0,
logctx ? (*ic_ptr)->probesize:
0))< 0)
{
goto fail;
}
// fmt == NULL 时才执行 ff_probe_input_buffer 函数,因为 fmt 就等于NULL, 成立.
ff_probe_input_buffer函数的原型:
int ff_probe_input_buffer(ByteIOContext**pb,//
字节IO上下文, 执行url_fopen得到的
AVInputFormat **fmt,// 输出参数: 输入的格式 const
char *filename,// 文件名 void
*logctx, // NULL unsigned
int offset,
// 0 unsigned
int max_probe_size) // 0
|
关键的代码片断:
/* 读待探测的数据 */
buf = av_realloc(buf, probe_size+ AVPROBE_PADDING_SIZE); if ((ret= get_buffer(*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);
/* 猜测文件格式 */ *fmt = av_probe_input_format2(&pd, 1,&score);
|
get_buffer函数, 有两处比较关键:
intget_buffer(ByteIOContext*s,
unsigned char
*buf,
int size); {
/* 省略部分代码 */ /* 读包 */ if(s->read_packet)
len = s->read_packet(s->opaque,
buf, size);
/* 省略部分代码 */
/* 填充缓冲 */
fill_buffer(s);
/* 省略部分代码 */
}
|
fill_buffer函数, 有一处比较关键:
staticvoid fill_buffer(ByteIOContext*s) { /* 省略部分代码 */ /* 读包 */ if(s->read_packet)
len = s->read_packet(s->opaque,
dst, len);
/* 省略部分代码 */ }
|
好了, 到第二次探测输入格式的地方了:
*fmt= av_probe_input_format2(&pd,
1,&score);
|
进入av_probe_input_format2函数:
AVInputFormat*av_probe_input_format2(AVProbeData*pd,
int is_opened,
int *score_max) {
AVInputFormat *fmt1,*fmt; int score;
fmt = NULL; for(fmt1= first_iformat; fmt1!=
NULL; fmt1
= fmt1->next){ if (!is_opened==
!(fmt1->flags& AVFMT_NOFILE)) continue;
/* 这次 is_opened == 1, fmt1->flags设置AVFMT_NOFILE标志才时成立*/
/* 由于 h264_demuxer.flags == AVFMT_GENERIC_INDEX, 所以上面不成立, 继续执行*/ score = 0; if (fmt1->read_probe){
score = fmt1->read_probe(pd);/*调用h264_demuxer.h264_probe
*/ } elseif
(fmt1->extensions){ if (av_match_ext(pd->filename, fmt1->extensions)){/*文件名和格式扩展名的匹配
*/
/*h264_demuxer.extensions = "h26l,h264,264" */ score = 50; } } if (score>
*score_max){ *score_max
= score;
fmt = fmt1; }elseif
(score ==
*score_max)
fmt =
NULL; } return fmt; }
|
av_match_ext函数:
int av_match_ext(constchar
*filename,const
char *extensions) { const char*ext,
*p; char ext1[32],*q;
if(!filename) return 0;
ext = strrchr(filename,'.'); if (ext){
ext++;
p = extensions; for(;;){
q = ext1; while
(*p !='\0'
&&*p
!=','
&& q-ext1<sizeof(ext1)-1) *q++=
*p++; *q ='\0'; if (!strcasecmp(ext1, ext)) return 1; if (*p==
'\0') break;
p++; } } return 0; }
|
总算探测到输入格式了.
err
= av_open_input_stream(ic_ptr, pb, filename, fmt, ap);
int av_open_input_stream(
AVFormatContext**ic_ptr,//
输出参数: 格式上下文
ByteIOContext *pb, // 字节IO上下文
constchar
*filename,// 文件名
AVInputFormat *fmt, // 输入的格式
AVFormatParameters*ap)// 格式参数, 调用时为NULL { int err;
AVFormatContext *ic;
AVFormatParameters default_ap;
// 使用缺省的格式参数 if(!ap){
ap=&default_ap; memset(ap, 0,sizeof(default_ap)); }
if(!ap->prealloced_context)
ic = avformat_alloc_context();// 分配格式上下文 else
ic = *ic_ptr; if (!ic){
err = AVERROR(ENOMEM); goto fail; }
// 初始化格式上下文 ic->iformat= fmt;// 格式
ic->pb
= pb;// 字节IO上下文
ic->duration= AV_NOPTS_VALUE;
ic->start_time= AV_NOPTS_VALUE;
av_strlcpy(ic->filename, filename,sizeof(ic->filename));//
文件名
/* 分配私有数据 */ if (fmt->priv_data_size> 0)
{
ic->priv_data= av_mallocz(fmt->priv_data_size); if (!ic->priv_data){
err = AVERROR(ENOMEM); goto fail; } } else
{
ic->priv_data=
NULL; }
// 读首部 if (ic->iformat->read_header){
err = ic->iformat->read_header(ic,
ap); if (err< 0) goto fail; }
// 获得数据偏移 if (pb
&& !ic->data_offset)
ic->data_offset= url_ftell(ic->pb);
#if LIBAVFORMAT_VERSION_MAJOR< 53
ff_metadata_demux_compat(ic); #endif
// 原始的包缓冲剩余的大小
ic->raw_packet_buffer_remaining_size= RAW_PACKET_BUFFER_SIZE;
// 输出参数:格式上下文
*ic_ptr= ic; return 0;
}
|
具体请参看
格式上下文结构:
typedefstructAVFormatContext{ constAVClass*av_class;/**< Set by avformat_alloc_context. */
//省略部分内容 }
|
AV类结构:
typedefstruct{ /**
* The name of the class; usually it is the same name as the
* context structure type to which the AVClass is associated.
*/ constchar*class_name;
/**
* A pointer to a function which returns the name of a context
* instance ctx associated with the class.
*/ constchar*(*item_name)(void*ctx);
/**
* a pointer to the first option specified in the class if any or NULL
*
* @see av_set_default_options()
*/ conststructAVOption*option;
/**
* LIBAVUTIL_VERSION with which this structure was created.
* This is used to allow fields to be added without requiring major
* version bumps everywhere.
*/
intversion; }AVClass;
|
进入avformat_alloc_context函数, 分配格式上下文:
AVFormatContext*avformat_alloc_context(void) {
AVFormatContext *ic;
ic = av_malloc(sizeof(AVFormatContext)); if (!ic)return ic;
avformat_get_context_defaults(ic);
ic->av_class=
&av_format_context_class; return ic; }
|
staticconst AVClass av_format_context_class
= {
"AVFormatContext", format_to_name, options, LIBAVUTIL_VERSION_INT};
|
进入avformat_get_context_defaults函数, 格式获得缺省上下文:
staticvoid avformat_get_context_defaults(AVFormatContext*s) { memset(s, 0,sizeof(AVFormatContext));
s->av_class=
&av_format_context_class;
av_opt_set_defaults(s); }
|
av_opt_set_defaults函数就不分析了.
下面继续分析:
err=ic->iformat->read_header(ic,ap)
|
以输入格式为libavformat/raw.c下的h264_demuxer为例:
AVInputFormat h264_demuxer={ "h264",
NULL_IF_CONFIG_SMALL("raw H.264 video format"),
0,
h264_probe,
video_read_header,
ff_raw_read_partial_packet, .flags=AVFMT_GENERIC_INDEX, .extensions="h26l,h264,264",//FIXME remove after writing mpeg4_probe
.value=CODEC_ID_H264, };
|
会调用video_read_header函数:
staticint video_read_header(AVFormatContext*s,
AVFormatParameters *ap) {
AVStream *st;
st = av_new_stream(s, 0);//
格式上下文增加一个流 if (!st) return AVERROR(ENOMEM);
// 初始化流
st->codec->codec_type= AVMEDIA_TYPE_VIDEO;//编码编码器类型
st->codec->codec_id= s->iformat->value;//
为 CODEC_ID_H264
st->need_parsing= AVSTREAM_PARSE_FULL;//需要全分析
/* for MJPEG, specify frame rate */ /* for MPEG-4 specify it, too (most MPEG-4 streams do not have the fixed_vop_rate set ...)*/ if (ap->time_base.num){
st->codec->time_base=
ap->time_base; } else
if ( st->codec->codec_id==
CODEC_ID_MJPEG||
st->codec->codec_id== CODEC_ID_MPEG4||
st->codec->codec_id== CODEC_ID_DIRAC||
st->codec->codec_id== CODEC_ID_DNXHD||
st->codec->codec_id== CODEC_ID_H264){
st->codec->time_base=(AVRational){1,25};//设置时基 }
av_set_pts_info(st, 64, 1, 1200000);//设置PTS(显示时间截)信息
return 0; }
|
进入av_new_stream函数:
AVStream*av_new_stream(AVFormatContext*s,
int id) {
AVStream *st; int i;
// 格式上下文不能太多流 if (s->nb_streams>= MAX_STREAMS) return NULL;
// 分配一个流
st = av_mallocz(sizeof(AVStream)); if (!st) return NULL;
// 分配解码器上下文
st->codec= avcodec_alloc_context(); if (s->iformat){ /* no default bitrate if decoding */
st->codec->bit_rate= 0; }
st->index= s->nb_streams;//
流索引
st->id
= id;// ID, 为0
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 */
av_set_pts_info(st, 33, 1, 90000);//设置PTS显示时间截信息
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; }
|
分配编码解码器上下文:
staticconst AVClass av_codec_context_class
= {
"AVCodecContext", context_to_name, options, LIBAVUTIL_VERSION_INT};
void avcodec_get_context_defaults2(AVCodecContext*s,
enum AVMediaType codec_type){
int flags=0;
memset(s, 0,sizeof(AVCodecContext));
s->av_class=&av_codec_context_class;
s->codec_type= codec_type;
if(codec_type== AVMEDIA_TYPE_AUDIO)
flags= AV_OPT_FLAG_AUDIO_PARAM;
else if(codec_type== AVMEDIA_TYPE_VIDEO)
flags= AV_OPT_FLAG_VIDEO_PARAM;
else if(codec_type== AVMEDIA_TYPE_SUBTITLE)
flags= AV_OPT_FLAG_SUBTITLE_PARAM;
av_opt_set_defaults2(s, flags, flags);
s->time_base=(AVRational){0,1};
s->get_buffer= avcodec_default_get_buffer;
s->release_buffer= avcodec_default_release_buffer;
s->get_format= avcodec_default_get_format;
s->execute= avcodec_default_execute;
s->execute2= avcodec_default_execute2;
s->sample_aspect_ratio=(AVRational){0,1};
s->pix_fmt= PIX_FMT_NONE;
s->sample_fmt= SAMPLE_FMT_NONE;
s->palctrl=
NULL;
s->reget_buffer= avcodec_default_reget_buffer;
s->reordered_opaque= AV_NOPTS_VALUE;
}
AVCodecContext *avcodec_alloc_context2(enum AVMediaType codec_type){
AVCodecContext *avctx= av_malloc(sizeof(AVCodecContext));
if(avctx==NULL)return
NULL;
avcodec_get_context_defaults2(avctx, codec_type);
return avctx;
}
void avcodec_get_context_defaults(AVCodecContext*s){
avcodec_get_context_defaults2(s, AVMEDIA_TYPE_UNKNOWN);
}
AVCodecContext *avcodec_alloc_context(void){
return avcodec_alloc_context2(AVMEDIA_TYPE_UNKNOWN);
}
分享到:
相关推荐
解协议的作用,就是将流媒体协议的数据,解析为标准的相应的封装格式数据。...有关本文中涉及到的协议数据、封装格式数据、视频编码数据、音频编码数据、视频像素数据、音频采样数据的分析可以参考下面系列文章:
第十二章 flv 文件格式分析 12.1 概述 FLV 视频格式是 Adobe 公司设计开发的一种流媒体的封装格式,总体上看,FLV 包括文件头(Flv Header) 和文件体(Flv Body)两部分,其中文件体由一系列的 Tag 及 Tag Size 对...
三大命令行的选项分析:ffmpeg,ffplay,ffprobe 音视频流信息的分析 音视频的封装与解封装操作 音视频的编码与解码操作 字幕与水印的操作 音视频的各种特效操作 流媒体与直播的操作:rtp/rtcp/rtsp/hls(m3u8)/...
libavfilter提供了一种通过一系列过滤器更改解码的音频和视频的方法。 libavdevice提供访问访问捕获和回放设备的抽象。 libswresample实现音频混合和重采样例程。 libswscale实现颜色转换和缩放例程。 工具类 是...
1、标准文档: 需求说明书、系统分析、概要设计、数据结构、编码、测试、总结、面试技巧等。 2、Java编码: SSH框架,标准流程,可以自由扩展。 3、音视频转码:最新版FFmpeg4.3.1,灵活的转码模板,可以自由扩展...
libavfilter提供了一种通过一系列过滤器更改解码的音频和视频的方法。 libavdevice提供访问访问捕获和回放设备的抽象。 libswresample实现音频混合和重采样例程。 libswscale实现颜色转换和缩放例程。 工具 是一...
libavfilter提供了一种通过一系列过滤器更改解码的音频和视频的方法。 libavdevice提供访问访问捕获和回放设备的抽象。 libswresample实现音频混合和重采样例程。 libswscale实现颜色转换和缩放例程。 工具 是一...
libavfilter提供了一种通过一系列过滤器更改解码的音频和视频的方法。 libavdevice提供访问访问捕获和回放设备的抽象。 libswresample实现音频混合和重采样例程。 libswscale实现颜色转换和缩放例程。 工具 是...
FFmpeg自述文件FFmpeg是用于处理多媒体内容(例如音频,视频,字幕和相关元数据)的库和工具的集合。图书馆libavcodec提供了更多编解码器的实现。 libavformat实现流协议,容器格式和基本I / O访问。 libavutil包括...
FFmpeg自述文件FFmpeg是用于处理多媒体内容(例如音频,视频,字幕和相关元数据)的库和工具的集合。图书馆libavcodec提供了更多编解码器的实现。 libavformat实现流协议,容器格式和基本的I / O访问。 libavutil...
我将带领大家全面分析RTSP+LIVE555,实现摄像头直播功能。 您将亲自动手来操练,搭建环境、学习理论,分析总结: RTSP+RTP/RTCP+X264+LIVE555 具体包括包括如下: RTSP协议讲解 RTP/RTCP协议讲解 使用...
三大多媒体框架(DirectShow、GStreamer、FFmpeg)简介与分析FFmpeg4.3的开发环境搭建(重要讲解windows下的源码编译及vs2015的环境)ffmpeg.c(4.3.1)源码剖析(分析开源大师们的设计理念和编码技巧)亲手封装私有...
libavfilter提供了一种通过一系列过滤器更改解码的音频和视频的方法。 libavdevice提供访问访问捕获和回放设备的抽象。 libswresample实现音频混合和重采样例程。 libswscale实现颜色转换和缩放例程。 工具 是一...
FFmpeg自述文件FFmpeg是用于处理多媒体内容(例如音频,视频,字幕和相关元数据)的库和工具的集合。图书馆libavcodec提供了更多编解码器的实现。 libavformat实现流协议,容器格式和基本的I / O访问。 libavutil...
您将亲自动手来操练,搭建环境、学习理论,分析总结:m3u8+Nginx+OpenSSL+FFmpeg 具体包括包括如下: HLS直播协议详解 FFmpeg+Nginx+VLC打造M3U8点播 FFmpeg+Nginx+VLC打造M3U8直播 FFmpeg:M3U8的多码流自适应 Win...
mjbots quad 系列机器人的源代码和设计文件、它们的控制界面以及用于开发和操作它们的实用程序 目录结构 base/ - 许多应用程序通用的 C++ 源文件。 ffmpeg/ - C++ ffmpeg 包装器。 gl/ - C++ GL 包装器。 mech/ - 专...
我将带领大家一起来学习SDL2.0的编程知识,包括SDL2.0的编程框架、原理流程分析,几个核心对象、事件机制、扩展库的使用。 具体内容包括: 1.搭建开发环境:VS2015或Qt5.9,共两套环境。 2.核心对象编程:窗口、表面...
FFmpeg自述文件FFmpeg是用于处理多媒体内容(例如音频,视频,字幕和相关元数据)的库和工具的集合。图书馆libavcodec提供了更多编解码器的实现。 libavformat实现流协议,容器格式和基本的I / O访问。 libavutil...
libavfilter提供了一种通过一系列过滤器更改解码的音频和视频的方法。 libavdevice提供访问访问捕获和回放设备的抽象。 libswresample实现音频混合和重采样例程。 libswscale实现颜色转换和缩放例程。 工具 是一...
实现了一个视频网站的上传视频、播放视频、个人主页、订阅、评论、通知等基本功能。 MySQL 是一款广受欢迎的开源... sys(自MySQL 5.7版本):提供更易用的视图来访问performance_schema中的信息,简化性能分析工作。