1、AVI介绍 AVI(Audio Video Interleaved的缩写)是一种RIFF(Resource Interchange File Format的缩写)文件格式,多用于音视频捕捉、编辑、回放等应用程序中。通常情况下,一个AVI文件可以包含多个不同类型的媒体流,是Windows 操作系统上最基本的、也是最常用的一种媒体文件格式。 AVI的文件结构分为头部、主体和索引三部分。主体中图像数据和声音数据是交互存放的。从尾部的索引可以索引跳到自己想放的位置。AVI本身只是提供了这么一个框架,索引放在了文件尾部,内部的图像数据和声音顺据格式可以是任意的编码形式。 但AVI对高码率VBR(Variable BitRate,就是动态比特率,可以根据当前的需要定义不同的比特率,避免了浪费,并且提高了利用率)音频文件支持不好。因为容器里的图像和声音是分开 的,所以播放时需要一个图像和声音的同步过程;VBR音轨是不断的在变换,而AVI没有时间戳去让VBR音轨和图像同步,这样就会产生图像声音不同步的问 题。 兼容的视频编码:MPEG-2、MPEG-4、MPEG-4 H.264、VC-1(支持不太好)、VC-1;兼容的音频编码:Linear PCM、DTS-HD DTS、Dolby Digital、AC3、Dolby Digital Plus、Dolby TrueHD、DTS Digital Surround。 2、AVI文件组织方式 RIFF格式是一种树状的结构,其基本组成单元为LIST和CHUNK,分别如树的节点和叶子。在RIFF文件中,数据保存的基本单元是CHUNK,可用于保存音视频数据或者一些参数信息,LIST相当于文件系统的目录,可以包含多个CHUNK或者多个LIST。 AVI文件的两种原子类型: (1)LIST typedef struct{ DWORD dwList; DWORD dwSize; DWORD dwFourCC; BYTE data[dwSize -4]; //contain lists and chunks }LIST; (2)CHUNK typedef struct{ DWORD dwFourCC; DWORD dwSize; BYTE data[dwSize ]; //contain headers or video/audio data }CHUNK; 用LIST类型的只有 ‘RIFF’或’LIST’,类似树枝,而CHUNK类似叶子。下图中ID为RIFF、hdrl、strl、odml、INFO、movi均为LIST类 型,hdrl LST块定义AVI文件的数据格式,movi LIST包含音视频序列数据,idx1 CHUNK存放索引数据,该块可选。 HDRL LIST信息块 dwList为’LIST’,dwFourCC为’hdrl’,data部分包含了avih CHUNK和其他strl LIST,需要递归解析。Size部分是指从dwFourCC开始的字段大小。 AVIH CHUNK中data字段结构如下: STRL LIST 这个LIST部分包括以下几个CHUNK:STRH、STRF、STRD、STRN、JUNK。iStreamListSize保存STRL LIST的data部分长度。 STRH CHUNK中data字段结构如下: 注意: wPriority和wLanguage并不是四字节存储,解析时注意偏移位置,dWFlags有两种取值:AVISF_VIDEO_PALCHANGES、AVISF_DISABLED rcFrame包括上left、top、right、bottom四个值,分别用short int(占两个字节)保存 帧率的计算:SamplingRate(samples/second)= dwRate/dwScale STRF CHUNK Strf结构与strh的fccType定义的类型有关。AVIStream::fccType的可能取值有:PV_2_AUDIO、PV_2_VIDEO、MIDI和TEXT。 (1)AVIStream::fccType为视频PV_2_VIDEO时,STRF CHUNK的结构为BITMAPINFO: typedef struct { BitmapInfoHhr BmiHeader; //header uint32 BmiColorsCount; //number of RGBQuads actually present RGBQuad BmiColors[MAX_COLOR_TABLE_SIZE]; } BitMapInfoStruct; RGBQuad结构如下: typedef struct { uint8 Blue; uint8 Green; uint8 Red; uint8 Reserved; } RGBQuad; BitmapInfoHhr结构如下: biClrUsed标识了BmiColorsCount,当这个字段值为0时,需要由biBiBitCount字段(每帧的比特数)决定color table的大小(BmiColorsCount): biBiBitCount =1时,color table size = 2^1 biBiBitCount = 2时,color table size = 2^2 biBiBitCount = 4时,color table size = 2^4 biBiBitCount = 8时,color table size = 2^8 biBiBitCount = 16/32时,color table size由biCompression的值决定: BiCompression= BI_BITFIELDS,color table size = 3 BiCompression= BI_ALPHABITFIELDS,color table size = 4 BiCompression= BI_RGB,color table size = 0 biBiBitCount = 24时,color table size = 0 BmiColors数组用来存放color table entry,详见ParseStreamFormat函数中的解析过程 (2) AVIStream::fccType为音频PV_2_AUDIO时,STRF CHUNK结构为WAVEFORMAT: 计算时间戳 音频根据帧率计算: 该帧大小arSize/每帧所占的字节数=samplecount iTimeStampAudio = iTimeStampAudio+(sampleCount * 1000) / samplingRate 视频根据帧间隔计算: frameDurationInms =(iMainHeader.iMicroSecPerFrame) / 1000 arTimeStamp = (iStreamSampleCount[aStreamNo] * (frameDurationInms)) 查找sample: 确定时间,相对于媒体时间坐标系统 查找对应该时间戳的sample序号(需要在解析的时候保存到iIndexTable表,如果不存在该索引表,需要在解析movi chunk时记录下来) 根据sample号查找该sample在文件中位移和大小(索引表iIndexTable或movi chunk) 用Idx1中的标志位查找关键帧 3、代码清单 3.1》新增文件: mydroid下framework/base/media/libstagefright路径 AVIExtractor.cpp、ChunkTable.cpp mydroid下framework/base/media/libstagefright/include路径 AVIExtractor.h、AVIType.h、ChunkTable.h 3.2》部分修改: mydroid下framework/base/media/libstagefright/ MediaExtractor.cpp、DataSource.cpp、MediaDefs.cpp mydroid下framework/base/include/media/stagefright/ MediaExtractor.h frameworks/base/media/libstagefright/codecs/m4v_h263/dec M4vH263Decoder.cpp -----esds信息 frameworks/base/media/libstagefright/codecs/aacdec AACDecoder.cpp -----esds信息 3.3》播放应用程序: packages/apps/MediaPlayer 文件夹 ,由于Gallery不能识别avi文件,MediaPlayer.Apk(编译后生成)应用程序可选择要播放的文件。 3.4》AVIExtractor AVIExtractor 负责AVI容器的解析,保存stream流到track链中。每个stream流(track)包含多个sample(帧),解码单位为sample,sample可能是以chunk或rec方式组织。 class AVIExtractor : public MediaExtractor { public: // Extractor assumes ownership of "source". AVIExtractor(const sp<DataSource> &source); virtual size_t countTracks(); /*Track <-> Stream*/ virtual sp<MediaSource> getTrack(size_t index); virtual sp<MetaData> getTrackMetaData(size_t index, uint32_t flags); virtual sp<MetaData> getMetaData(); protected: virtual ~AVIExtractor(); private: struct Stream{ Stream *next; sp<MetaData> meta; uint32_t StreamNo; //stream序号 uint32_t NumOfSamples; //sample数 uint32_t NumOfIdxEntry; //stream中的idx单元数,= sample数 StreamHeader mStreamHeader; StreamFormat mStreamFormat; sp<ChunkTable> chunkTable; }; sp<DataSource> mDataSource; bool mHaveMetadata; bool mHasVideo; Stream *mFirstStream, *mLastStream; sp<MetaData> mFileMetaData; Vector<uint32_t> mPath; AVIFileMainHeader *mFileHeader; uint32_t mStreamCount; /*stream numbers, stream == track*/ uint32_t mfileLength; /*file length*/ uint32_t mMoviChunkPos; /* some stuff for saving ... */ uint32_t mIdxChunkPos; uint32_t mNextFramePos; uint32_t mInitialFrames; status_t readMetaData(); status_t parseChunk(off_t *offset, int depth); status_t parseMetaData(off_t offset, size_t size); AVIExtractor(const AVIExtractor &); AVIExtractor &operator=(const AVIExtractor &); }; 函数说明: countTracks():计算轨道数,即stream number,avih中的dwStreams getTrack(size_t index):获取轨道,即取得相应的stream信息 getTrackMetaData:获取轨道元数据,即取得stream中的MetaData getMetaData:获取文件信息,包括文件创建时间等信息 readMetaData():内部函数,读取文件元数据,调用具体的解析函数 parseChunk:内部函数,具体的解析函数 parseMetaData:内部函数,解析文件信息,保存在mFileMetaData,解析INFO LIST部分 Extractor->fileMetaData:封装文件信息(如文件类型、文件创建时间、tracknumber、作曲家、版权等信息,信息从INFO LISt获取) mFileMetaData->setCString(kKeyMIMEType, "video/mp4") mFileMetaData->setCString(kKeyDate, s.string()) mFileMetaData->setCString(kKeyCDTrackNumber, tmp) mFileMetaData->setCString(kKeyDiscNumber, tmp) mFileMetaData->setData() 3.5》AVISource AVISource 保存avi文件中解析出的audio和 video frame(帧信息) class AVISource : public MediaSource{ public: // Caller retains ownership of both "dataSource" and "sampleTable". AVISource(const sp<MetaData> &format, const sp<DataSource> &dataSource, const sp<ChunkTable> &chunkTable, uint32_t flags); virtual status_t start(MetaData *params = NULL); virtual status_t stop(); virtual sp<MetaData> getFormat(); virtual status_t read( MediaBuffer **buffer, const ReadOptions *options = NULL); //read是重要接口,返回给解码器解析的每一帧数据,以00dc或rec组织的数据块 protected: virtual ~AVISource(); private: Mutex mLock; sp<MetaData> mFormat; sp<DataSource> mDataSource; //int32_t mTimescale; sp<ChunkTable> mChunkTable; uint32_t mCurrentSampleIndex; uint32_t mFlags; //avih->flags bool mStarted; MediaBufferGroup *mGroup; MediaBuffer *mBuffer; uint8_t *mSrcBuffer; AVISource(const AVISource &); AVISource &operator=(const AVISource &); }; 3.6》Stream Stream封装每个数据流的信息 Stream->meta封装音视流(轨道)的基本信息 包括音视频编码类型(如mp4a、mp3、avc等类型)、该track的时间长度、声道数、帧率、视频帧的宽和高、最大sample长度等。 mLastTrack->meta->setCString(kKeyMIMEType, FourCC2MIME(chunk_type)); mLastTrack->meta->setInt64(kKeyDuration, (duration * 1000000) / mLastTrack->timescale); mLastTrack->meta->setInt32(kKeyChannelCount, num_channels); mLastTrack->meta->setInt32(kKeySampleRate, sample_rate); mLastTrack->meta->setInt32(kKeyWidth, width); mLastTrack->meta->setInt32(kKeyHeight, height); mLastTrack->meta->setInt32(kKeyMaxInputSize, XXX); mLastTrack->meta->setData(kKeyAVCC,XXX); Stream->chunkTable封装帧信息 Track对应一维链表IdxChunkTbl,保存当前流中每个数据块的信息,通过该信息可以查找具体的每一sample信息(在文件中的位移、大小、 时间戳等)。与MP4不同的是,AVI在文件解析的时候创建一维链表,查找的时候计算相对简单。SampleIterator是通过 IdxChunkTbl链查找当前播放的sample信息并保存,便于扩展,考虑多个sample以rec方式组织,此处的chunk实际为rec,其中 包含多个数据块。 IdxChunkTbl数据结构: struct IdxChunkTbl{ IdxChunkTbl *next; uint32_t chunkId; uint32_t flags; uint32_t sampleIndex; uint32_t offset; uint32_t datasize; uint32_t iTimeStamp; bool isKeyFrame; bool ifRecList; bool ifNoTime; }; IdxChunkTbl的建立过程: 如果idx1 chunk存在,那么每一帧的位移、大小等信息从idx1的子chunk获取,时间戳信息需要通过解析movi list建立; 如果idx1 chunk不存在,直接解析movi list创建IdxChunkTbl表。 |
|
来自: shaobin0604@1... > 《Android》