FFMPEG avi文件封装实现

FFMPEG avi录像实现

date:2022.09.27
author:lyn
version:ffmpeg4.1.3

1.avi数据结构

2.ffmpeg avi封装实现

3.avi函数调用关系

4.参考资料


1.avi数据结构
1.1 avi简介

网上太多阐述avi文件格式的文章了,对于初学者而言,这些文章看起来很高深,里面太多的专业词汇和简写,如果没有相关的基础很难理解;

avi的简介参考维基百科,AVI的文件结构分为头部主体索引三部分。主体中图像数据和声音数据是交互存放的。从尾部的索引可以索引跳到自己想放的位置。

AVI将视频和音频封装在一个文件里,其顺序是:若干视频帧(Video Frame)之后接着若干音频帧(Audio Frame),再然后是视频帧、音频帧,故名为“音频视频交织”,意即音频和视频按帧交错排列,以此达到音频同步于视频播放的效果。和DVD视频格式一样,AVI文件支持多视频流和音频流,虽然这些功能很少使用。大多数AVI文件还支持由Matrox OpenDML集团于1996年2月开发的格式后缀。这些文件非正式的称为“AVI 2.0”,并得到微软的支持。

AVI本身只是提供了这么一个框架,内部的图像数据和声音顺据格式可以是任意的编码形式。但是由于索引放在了文件尾部,所以在播放internet流媒体时已属力不从心。

理解avi的文件格式,首先需要认识avi文件是怎么组织的,按照一般的理解,应该要有标识封装文件格式的标志,是movavimkv或者其他的封装格式,然后开始记录音视频流的相关参数,包括图像和音频的参数(宽高、码率、时长、采样率),然后就是具体的编码数据。按照这样的理解,这样就可以入门理解各种视频封装的格式;

1.2 avi名词解释和文件结构

avi比较麻烦的是它的各种char id,他们通常采用简写方式,需要看官方文档才明白具体的简写是什么意思;

avi文件的层级结构如下所示,最外的是一个RIFF标志的根,然后在内部包含其他LIST和Chunk;

RIFF AVI
	->LIST hdrl #Header List 信息块,用于描述avi媒体格式
		->avih #Main AVI Header avi的头部,主要的参数有图像的宽高参数和数据流个数等全局的参数,是否包含索引块,交叉存储等信息;
		->LIST strl #视频流信息 Stream Header List数据流头部list,包含了strlh 和strf;
			->strh  #Stream Header 数据流的头部,主要的记录音视频流相关的参数,采样率,流标识等相关参数;
			->strf  #Stream Format 数据流的相关格式,数据流的宽高等参数记录在这里
			->JUNK
		->LIST strl #音频流信息
			->strh
			->strf
			->JUNK
		->LIST strl #字幕等信息
			->strh
			->JUNK
	->LIST INFO #AVSII码标志描述的metadata信息,包含作者,版权名字等相关信息;
		->ISFT  #描述文件的打包程序
	->LIST movi #数据块,音频/视频/字幕数据
		->movi
	->idx1 #索引块,可选有多种类型的索引dc:视频 wb:音频 tx:字幕

分析avi文件二进制代码可以获得如下的文件结构
avi 文件结构


2.ffmpeg avi封装实现
avio_open
avi_write_header
avi_write_packet
avi_write_trailer
avio_close

视频和音频封装为avi格式调用接口顺序如上所示,avio_open avio_close接口取代旧的 url_fopenurl_fclose 然后直接调用avi_write_header写avi文件的头部,数据包通过avi_write_packet一帧一帧的将数据写入,不断调用这个接口,最后调用avi_write_trailer将文件尾部写入;这样就将编码数据封装为avi格式的文件;


3.avi函数调用关系
	avio_open
	{
		avio_open2
        {
            ffurl_open_whitelist //初始化URLContext
            {
                ffurl_alloc
                {
                    url_find_protocol  //查找合适的URLProtocol,并创建,file也是一种协议
                    url_alloc_for_protocol
                }
                ffurl_connect //打开获得的URLProtocol
            }    
            ffio_fdopen //URLContext初始化AVIOContext
            {
                avio_alloc_context
                {
                	ffio_init_context
                }
            }
        }
	}
    
    avi_write_header
    {
        list1 = avi_start_new_riff(s, pb, "AVI ", "hdrl");  //avi header list 写头部和 1:hdrl List
        ffio_wfourcc(pb, "avih");		    //avih
        /* stream list 包含音频和视频两个list,循环写入 */
        list2 = ff_start_tag(pb, "LIST");	// LIST 这里有音频和视频两个List 2:视频 3:音频
        ffio_wfourcc(pb, "strl");			// strl char id
        ff_start_tag(pb, "strh");			// strh char id
        ffio_wfourcc(pb, "vids");			// 视频char id
        ffio_wfourcc(pb, "auds");			// 音频char id
        ff_start_tag(pb, "strf");			// strf char id
        ff_end_tag(pb, strf);				// 结束strf chunk
        ff_riff_write_info_tag(s->pb, "strn", t->value); // strn info tag
        write_odml_master
        {
            ff_start_tag(pb, "JUNK");
        }
        ff_start_tag(pb, "vprp");
        ff_end_tag(pb, vprp);				// 结束vprp chunk
        ff_end_tag(pb, list2);              // 结束一个流LIST
        
        
        ff_end_tag(pb, list1);				// 结束hdrl List
        ff_riff_write_info(s);				// 写INFO List 4:INFO
        ff_start_tag(pb, "LIST");			
        ffio_wfourcc(pb, "movi");			// 写 movi List 5:movi 数据块的List
    }
    avi_write_packet
    {
        ff_check_h264_startcode				// 检查h264数据startcode
        write_skip_frames					// 写入跳过的帧,根据dts判断要不要写入空的packet
        {
            av_init_packet
            avi_write_packet_internal
            {
                avi_write_ix;
                 avi_write_idx1;
                avi_start_new_riff(s, pb, "AVIX", "movi");
                avi_stream2fourcc			// dc:视频流 wb:音频和其他
                avio_write(pb, tag, 4);		// 写一帧的数据,前4个字节是类型char id
                avio_wl32(pb, size);		// 数据长度
                avio_write(pb, pkt->data, size);     // 写编码数据           
            }
        }
        
        avi_write_packet_internal			// 写数据包
        {
            avi_write_ix;
            avi_write_idx1;
            avi_start_new_riff(s, pb, "AVIX", "movi");
            avi_stream2fourcc			// dc:视频流 wb:音频和其他
            avio_write(pb, tag, 4);		// 写一帧的数据,前4个字节是类型char id
            avio_wl32(pb, size);		// 数据长度
            avio_write(pb, pkt->data, size);     // 写编码数据                    
        }        
    }
    avi_write_trailer
    {
        write_skip_frames				// 写入跳过的帧,根据dts判断要不要写入空的packet
        ff_end_tag(pb, avi->movi_list);		// 结束 movi List
        avi_write_idx1(s);					// 写入索引块chunk
        {
            ff_start_tag(pb, "idx1");
            /* 轮询写入每一帧的索引信息 */
            avi_stream2fourcc(tag, stream_id,
                              s->streams[stream_id]->codecpar->codec_type);
            ffio_wfourcc(pb, tag);        	// 写入char id 00dc:视频帧 00wb:音频帧
            avio_wl32(pb, ie->flags);		// ckid chunk的索引id,根据ent_id/AVI_INDEX_CLUSTER_SIZE
            avio_wl32(pb, ie->pos);			// 帧的位置偏移
            avio_wl32(pb, ie->len);         // 帧数据长度
        }
        ff_end_tag(pb, avi->riff_start);	// 结束RIFF            
    }
    avio_close							// 释放资源
    {
        avio_flush
        av_opt_free
        avio_context_free
        ffurl_close
    }

4.参考资料

avi 简介
avi格式解析
avio函数分析


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值