罗斯福年轻时照片:AVI破解及Android2.3实现

来源:百度文库 编辑:偶看新闻 时间:2024/04/28 01:57:10
1、AVI介绍
AVI(Audio Video Interleaved的缩写)是一种RIFF(Resource Interchange FileFormat的缩写)文件格式,多用于音视频捕捉、编辑、回放等应用程序中。通常情况下,一个AVI文件可以包含多个不同类型的媒体流,是Windows操作系统上最基本的、也是最常用的一种媒体文件格式。
AVI的文件结构分为头部、主体和索引三部分。主体中图像数据和声音数据是交互存放的。从尾部的索引可以索引跳到自己想放的位置。AVI本身只是提供了这么一个框架,索引放在了文件尾部,内部的图像数据和声音顺据格式可以是任意的编码形式。
但AVI对高码率VBR(VariableBitRate,就是动态比特率,可以根据当前的需要定义不同的比特率,避免了浪费,并且提高了利用率)音频文件支持不好。因为容器里的图像和声音是分开的,所以播放时需要一个图像和声音的同步过程;VBR音轨是不断的在变换,而AVI没有时间戳去让VBR音轨和图像同步,这样就会产生图像声音不同步的问题。
兼容的视频编码:MPEG-2、MPEG-4、MPEG-4 H.264、VC-1(支持不太好)、VC-1;兼容的音频编码:LinearPCM、DTS-HD DTS、Dolby Digital、AC3、Dolby Digital Plus、Dolby TrueHD、DTSDigital 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 &source);

    virtual size_t countTracks();  /*Track  <->  Stream*/
    virtual sp getTrack(size_t index);
    virtual sp getTrackMetaData(size_t index, uint32_t flags);
    virtual sp getMetaData();

protected:
    virtual ~AVIExtractor();

private:
    struct Stream{
        Stream         *next;
        sp   meta;
        uint32_t       StreamNo;       //stream序号
        uint32_t       NumOfSamples;   //sample数
        uint32_t       NumOfIdxEntry;  //stream中的idx单元数,= sample数
        StreamHeader   mStreamHeader;
        StreamFormat   mStreamFormat;
        sp chunkTable;
    };

    sp mDataSource;
    bool mHaveMetadata;
    bool mHasVideo;
   
    Stream *mFirstStream, *mLastStream;
    sp mFileMetaData;
    Vector 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 &format,
                const sp &dataSource,
                   const sp &chunkTable,
                   uint32_t flags);

    virtual status_t start(MetaData *params = NULL);
    virtual status_t stop();
    virtual sp getFormat();
    virtual status_t read(
            MediaBuffer **buffer, const ReadOptions *options = NULL);
    //read是重要接口,返回给解码器解析的每一帧数据,以00dc或rec组织的数据块

protected:
    virtual ~AVISource();

private:
    Mutex mLock;
    sp   mFormat;
    sp mDataSource;
    //int32_t mTimescale;
    sp 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表。