本文转载自: http://blog.youkuaiyun.com/jwybobo2007/article/details/7662653
linux下avi文件解析(附源码)
前段时间由于项目需要,要解avi文件,当时我第一时间想到用ffmpeg来处理,但想想觉得太大了,又是放到arm上跑的,感觉没必要。然后,搜索引擎上稍微搜了一下,没找到有用的示例,大部分都是利用windows的api进行读写,很明显linux下用不了。结果花了2-3天时间研究并写了一个avi文件的解封装代码,但是后来因某些原因没有去使用AVI了,所以代码也没进行后续的完善优化。这里贴出来供记录以及需要的人作参考。
头文件:
- /*!
- @brief avi文件分析提取器
- @author jwybobo2007
- @file avifileparser.h
- @note 代码在使用时,请标明作者,保留出处:http://blog.youkuaiyun.com/jwybobo2007
- */
- #include <stdio.h>
- #include <stdlib.h>
- #include <string>
- #include <vector>
- #include <map>
- //
- #define AVIIF_KEYFRAME 0x00000010L // 索引里面的关键帧标志
- #define AVIIF_LIST 0x00000001L
- // Flags for dwFlags|AVI头中的标志位
- #define AVIFILEINFO_HASINDEX 0x00000010 // 是否有索引
- #define AVIFILEINFO_MUSTUSEINDEX 0x00000020
- #define AVIFILEINFO_ISINTERLEAVED 0x00000100
- #define AVIFILEINFO_WASCAPTUREFILE 0x00010000
- #define AVIFILEINFO_COPYRIGHTED 0x00020000
- // 最大允许的AVI头大小
- #define MAX_ALLOWED_AVI_HEADER_SIZE 131072
- // 打不开文件
- #define ERROR_OPEN_AVI 0
- // 不是有效AVI
- #define ERROR_INVALID_AVI 1
- // 由于编译器c,c++标准过低,没有包含stdint.h头文件,并且现在也没有使用boost库,因此定义一些类型,来兼容以前的代码
- #ifdef NO_STDINT
- typedef char int8_t;
- typedef unsigned char uint8_t;
- typedef short int16_t;
- typedef unsigned short uint16_t;
- typedef int int32_t;
- typedef unsigned int uint32_t;
- typedef long long int64_t;
- typedef unsigned long long uint64_t;
- #endif
- // 双字
- typedef uint32_t DWORD;
- // 单字
- typedef uint16_t WORD;
- // 定义长整型
- typedef DWORD LONG;
- // 字节
- typedef uint8_t BYTE;
- // 定义four cc;
- typedef DWORD FourCC;
- // 定义fourcc对应的整数值,avi文件中保存的是小端
- const DWORD FOURCC_RIFF = 0x46464952;
- const DWORD FOURCC_AVI = 0x20495641;
- const DWORD FOURCC_LIST = 0x5453494C;
- const DWORD FOURCC_hdrl = 0x6C726468;
- const DWORD FOURCC_avih = 0x68697661;
- const DWORD FOURCC_strl = 0x6C727473;
- const DWORD FOURCC_strh = 0x68727473;
- const DWORD FOURCC_strf = 0x66727473;
- const DWORD FOURCC_STRD = 0x64727473;
- const DWORD FOURCC_vids = 0x73646976;
- const DWORD FOURCC_auds = 0x73647561;
- const DWORD FOURCC_INFO = 0x4F464E49;
- const DWORD FOURCC_ISFT = 0x54465349;
- const DWORD FOURCC_idx1 = 0x31786469;
- const DWORD FOURCC_movi = 0x69766F6D;
- const DWORD FOURCC_JUNK = 0x4B4E554A;
- const DWORD FOURCC_vprp = 0x70727076;
- const DWORD FOURCC_PAD = 0x20444150;
- const DWORD FOURCC_DIV3 = 861292868;
- const DWORD FOURCC_DIVX = 1482049860;
- const DWORD FOURCC_XVID = 1145656920;
- const DWORD FOURCC_DX50 = 808802372;
- const DWORD FOURCC_fmt = 0x20746D66; // for WAVE files
- const DWORD FOURCC_data = 0x61746164; // for WAVE files
- const DWORD FOURCC_WAVE = 0x45564157; // for WAVE files
- // 调色板
- typedef struct
- {
- BYTE rgbBlue; // 蓝
- BYTE rgbGreen; // 绿
- BYTE rgbRed; // 红
- BYTE rgbReserved; // 保留
- } RGBQUAD;
- // AVI主头部
- typedef struct
- {
- FourCC fcc; // 必须为 avih
- DWORD cb; // 本数据结构的大小,不包括最初的8个字节(fcc和cb两个域)
- DWORD dwMicroSecPerFrame; // 视频帧间隔时间(以毫秒为单位)
- DWORD dwMaxBytesPerSec; // 这个AVI文件的最大数据率
- DWORD dwPaddingGranularity; // 数据填充的粒度
- DWORD dwFlags; // AVI文件的全局标记,比如是否含有索引块等
- DWORD dwTotalFrames; // 总帧数
- DWORD dwInitialFrames; // 为交互格式指定初始帧数(非交互格式应该指定为0)
- DWORD dwStreams; // 本文件包含的流的个数
- DWORD dwSuggestedBufferSize; // 建议读取本文件的缓存大小(应能容纳最大的块)
- DWORD dwWidth; // 视频图像的宽(以像素为单位)
- DWORD dwHeight; // 视频图像的高(以像素为单位)
- DWORD dwReserved[4]; // 保留
- } AVIMainHeader;
- // 定义矩形区域
- typedef struct
- {
- short int left; // 总边距
- short int top; // 顶边距
- short int right; // 右边距
- short int bottom; // 底边距
- }RECT;
- // AVI流头部
- typedef struct
- {
- FourCC fcc; // 必须为 strh
- DWORD cb; // 本数据结构的大小,不包括最初的8个字节(fcc和cb两个域)
- FourCC fccType; // 流的类型: auds(音频流) vids(视频流) mids(MIDI流) txts(文字流)
- FourCC fccHandler; // 指定流的处理者,对于音视频来说就是解码器
- DWORD dwFlags; // 标记:是否允许这个流输出?调色板是否变化?
- WORD wPriority; // 流的优先级(当有多个相同类型的流时优先级最高的为默认流)
- WORD wLanguage; // 语言
- DWORD dwInitialFrames; // 为交互格式指定初始帧数
- DWORD dwScale; // 每帧视频大小或者音频采样大小
- DWORD dwRate; // dwScale/dwRate,每秒采样率
- DWORD dwStart; // 流的开始时间
- DWORD dwLength; // 流的长度(单位与dwScale和dwRate的定义有关)
- DWORD dwSuggestedBufferSize; // 读取这个流数据建议使用的缓存大小
- DWORD dwQuality; // 流数据的质量指标(0 ~ 10,000)
- DWORD dwSampleSize; // Sample的大小
- RECT rcFrame; // 指定这个流(视频流或文字流)在视频主窗口中的显示位置,视频主窗口由AVIMAINHEADER结构中的dwWidth和dwHeight决定
- } AVIStreamHeader;
- // 位图头
- typedef struct
- {
- DWORD biSize;
- LONG biWidth;
- LONG biHeight;
- WORD biPlanes;
- WORD biBitCount;
- DWORD biCompression;
- DWORD biSizeImage;
- LONG biXPelsPerMeter;
- LONG biYPelsPerMeter;
- DWORD biClrUsed;
- DWORD biClrImportant;
- } BitmapInfoHeader;
- // 位图信息
- typedef struct
- {
- BitmapInfoHeader bmiHeader; // 位图头
- RGBQUAD bmiColors[1]; // 调色板
- } BitmapInfo;
- // 音频波形信息
- typedef struct
- {
- WORD wFormatTag;
- WORD nChannels; // 声道数
- DWORD nSamplesPerSec; // 采样率
- DWORD nAvgBytesPerSec; // 每秒的数据量
- WORD nBlockAlign; // 数据块对齐标志
- WORD wBitsPerSample; // 每次采样的数据量
- WORD cbSize; // 大小
- } WaveFormatEx;
- // 索引节点信息
- typedef struct
- {
- DWORD dwChunkId; // 本数据块的四字符码(00dc 01wb)
- DWORD dwFlags; // 说明本数据块是不是关键帧、是不是‘rec ’列表等信息
- DWORD dwOffset; // 本数据块在文件中的偏移量
- DWORD dwSize; // 本数据块的大小
- } AVIIndexEntry;
- // 索引信息
- typedef struct
- {
- FourCC fcc; // 必须为‘idx1’
- DWORD cb; // 本数据结构的大小,不包括最初的8个字节(fcc和cb两个域)
- uint32_t position; // 数据起始位置偏移
- std::map<uint32_t, std::vector<AVIIndexEntry> > videoIndexMap; // 视频索引表,00dc等转换成整形表示
- std::map<uint32_t, std::vector<AVIIndexEntry> > audioIndexMap; // 音频索引表,00wb等转换成整形表示
- } AVIIndex;
- /*!
- @brief avi文件分析提取器
- */
- class AVIFileParser
- {
- public:
- AVIFileParser();
- AVIFileParser(const char* file);
- ~AVIFileParser(void);
- public:
- /*!
- @brief 打开AVI文件
- */
- void openAVI(const char* file);
- /*!
- @brief 是否是有效avi
- */
- bool isValid(){return _isValid;}
- /*!
- @brief 是否有音频
- */
- bool hasAudio(){return _hasAudio;}
- /*!
- @brief 是否有视频
- */
- bool hasVideo(){return _hasVideo;}
- /*!
- @brief 返回avi头
- */
- const AVIMainHeader* aviMainHeader(){return &_aviMainHeader;}
- /*!
- @brief 返回avi视频流头
- */
- const AVIStreamHeader* aviVideoStreamHeader(){return &_aviVideoStreamHeader;}
- /*!
- @brief 返回avi音频流头
- */
- const AVIStreamHeader* aviAudioStreamHeader(){return &_aviAudioStreamHeader;}
- /*!
- @brief 返回位图信息
- */
- const BitmapInfo* bitmapInfo(){return &_bitmapInfo;}
- /*!
- @brief 返回音频信息
- */
- const WaveFormatEx* waveFormatEx(){return &_waveInfo;}
- /*!
- @brief 最大视频帧大小
- */
- uint32_t maxFrameSize(){return _maxFrameSize;}
- /*!
- @brief 获取视频帧
- @param buf 视频帧存储缓冲
- @param index 帧索引,默认-1,表示从当前的索引继续往下读取
- @return 视频帧大小
- */
- int32_t getVideoFrame(char* buf, int32_t index = -1);
- /*!
- @brief 偏移视频帧从指定位置开始
- @param index 指定索引
- */
- bool seekVideoFrame(int32_t index);
- /*!
- @brief 获取音频帧
- */
- private:
- /// 解析AVI
- void _parseFile();
- /// 从文件读指定长度的数据,出错自动抛出异常
- bool readFile(char* buf, uint32_t len);
- /// 从文件读12个字节并分析
- bool readFileTagSize(DWORD& fourcc, uint32_t& size, DWORD& fourcc2);
- /// 从文件读4字节
- uint32_t readFileDW();
- /// 从文件读2字节
- uint16_t readFileW();
- //
- // 重载
- /// 从文件读4字节
- bool readFileDW(uint32_t& dw);
- /// 从文件读2字节
- bool readFileW(uint16_t& w);
- private:
- // 文件句柄
- FILE* _aviFd;
- // 是否有音频
- bool _hasAudio;
- // 是否有视频
- bool _hasVideo;
- // 是否有效
- bool _isValid;
- // 文件长度
- uint32_t _fLen;
- // avi主头
- AVIMainHeader _aviMainHeader;
- // avi视频流头部
- AVIStreamHeader _aviVideoStreamHeader;
- // avi音频流头部
- AVIStreamHeader _aviAudioStreamHeader;
- // 位图信息
- BitmapInfo _bitmapInfo;
- // 音频信息
- WaveFormatEx _waveInfo;
- // 编码程序
- std::string _soft;
- // 索引信息
- AVIIndex _aviIndex;
- // movi的开始位置偏移
- uint32_t _moviOff;
- // 最大帧大小
- uint32_t _maxFrameSize;
- // 当前视频位置索引(数组中的位置,下标)
- int32_t _currVideoIndex;
- };
源文件:
- #include <assert.h>
- #include <errno.h>
- #include "avifileparser.h"
- #include "byte_write.h"
- AVIFileParser::AVIFileParser(void)
- :_aviFd(NULL),
- _hasAudio(false),
- _hasVideo(false),
- _isValid(false),
- _fLen(0),
- _moviOff(0),
- _maxFrameSize(0),
- _currVideoIndex(0)
- {
- }
- AVIFileParser::AVIFileParser( const char* file )
- :_hasAudio(false),
- _hasVideo(false),
- _isValid(false),
- _fLen(0),
- _moviOff(0),
- _maxFrameSize(0),
- _currVideoIndex(0)
- {
- openAVI(file);
- }
- AVIFileParser::~AVIFileParser(void)
- {
- if (_aviFd != NULL)
- fclose(_aviFd);
- }
- void AVIFileParser::openAVI( const char* file )
- {
- _aviFd = fopen(file, "rb");
- if (_aviFd == NULL)
- {
- #ifndef NPRINT
- printf("open avi error: %d [%s]\n", errno, strerror(errno));
- #endif
- // 抛出错误号
- throw ERROR_OPEN_AVI;
- }
- // 开始解析
- _parseFile();
- _isValid = true;
- }
- void AVIFileParser::_parseFile()
- {
- // 第一次读12个字节
- DWORD fourcc = 0;
- DWORD fourcc2 = 0;
- bool flag = true;
- bool hasIndex = false;
- readFileTagSize(fourcc, _fLen, fourcc2);
- if (fourcc != FOURCC_RIFF || fourcc2 != FOURCC_AVI)
- throw ERROR_INVALID_AVI;
- while (flag)
- {
- uint32_t size = 0;
- bool isEof = readFileDW(fourcc);
- if (isEof)
- return;
- isEof = readFileDW(size);
- if (isEof)
- return;
- if (fourcc == FOURCC_LIST)
- {
- fourcc2 = readFileDW();
- switch (fourcc2)
- {
- case FOURCC_hdrl:
- {
- if (size > MAX_ALLOWED_AVI_HEADER_SIZE)
- throw ERROR_INVALID_AVI;
- // 跳过hdrl
- uint32_t off = 4;
- while (off < size)
- {
- fourcc = readFileDW();
- switch (fourcc)
- {
- case FOURCC_avih:
- {
- _aviMainHeader.fcc = FOURCC_avih;
- _aviMainHeader.cb = readFileDW();
- _aviMainHeader.dwMicroSecPerFrame = readFileDW();
- _aviMainHeader.dwMaxBytesPerSec = readFileDW();
- _aviMainHeader.dwPaddingGranularity = readFileDW();
- _aviMainHeader.dwFlags = readFileDW();
- _aviMainHeader.dwTotalFrames = readFileDW();
- _aviMainHeader.dwInitialFrames = readFileDW();
- _aviMainHeader.dwStreams = readFileDW();
- _aviMainHeader.dwSuggestedBufferSize = readFileDW();
- _aviMainHeader.dwWidth = readFileDW();
- _aviMainHeader.dwHeight = readFileDW();
- if ((AVIFILEINFO_HASINDEX & _aviMainHeader.dwFlags) == AVIFILEINFO_HASINDEX)
- hasIndex = true;
- // 跳过保留字段
- fseek(_aviFd, 16, SEEK_CUR);
- // 跳过avih以及长度各四个字节
- off += _aviMainHeader.cb + 8;
- }
- break;
- case FOURCC_LIST:
- {
- int avListLen = readFileDW();
- if (readFileDW() != FOURCC_strl)
- throw ERROR_INVALID_AVI;
- // 跳过strl
- int tmpOff = 4;
- AVIStreamHeader aviStreamHeader = {0};
- while (tmpOff < avListLen)
- {
- fourcc = readFileDW();
- tmpOff += 4;
- if (fourcc == FOURCC_strh)
- {
- aviStreamHeader.fcc = FOURCC_strh;
- aviStreamHeader.cb = readFileDW();
- aviStreamHeader.fccType = readFileDW();
- aviStreamHeader.fccHandler = readFileDW();
- aviStreamHeader.dwFlags = readFileDW();
- aviStreamHeader.wPriority = readFileW();
- aviStreamHeader.wLanguage = readFileW();
- aviStreamHeader.dwInitialFrames = readFileDW();
- aviStreamHeader.dwScale = readFileDW();
- aviStreamHeader.dwRate = readFileDW();
- aviStreamHeader.dwStart = readFileDW();
- aviStreamHeader.dwLength = readFileDW();
- aviStreamHeader.dwSuggestedBufferSize = readFileDW();
- aviStreamHeader.dwQuality = readFileDW();
- aviStreamHeader.dwSampleSize = readFileDW();
- aviStreamHeader.rcFrame.left = readFileW();
- aviStreamHeader.rcFrame.top = readFileW();
- aviStreamHeader.rcFrame.right = readFileW();
- aviStreamHeader.rcFrame.bottom = readFileW();
- // 跳过长度
- tmpOff += 4;
- tmpOff += aviStreamHeader.cb;
- }
- else if (fourcc == FOURCC_strf)
- {
- int tmpLen = readFileDW();
- if (aviStreamHeader.fccType == FOURCC_vids)
- {
- _hasVideo = true;
- _aviVideoStreamHeader = aviStreamHeader;
- _bitmapInfo.bmiHeader.biSize = readFileDW();
- _bitmapInfo.bmiHeader.biWidth = readFileDW();
- _bitmapInfo.bmiHeader.biHeight = readFileDW();
- _bitmapInfo.bmiHeader.biPlanes = readFileW();
- _bitmapInfo.bmiHeader.biBitCount = readFileW();
- _bitmapInfo.bmiHeader.biCompression = readFileDW();
- _bitmapInfo.bmiHeader.biSizeImage = readFileDW();
- _bitmapInfo.bmiHeader.biXPelsPerMeter = readFileDW();
- _bitmapInfo.bmiHeader.biYPelsPerMeter = readFileDW();
- _bitmapInfo.bmiHeader.biClrUsed = readFileDW();
- _bitmapInfo.bmiHeader.biClrImportant = readFileDW();
- if (tmpLen > _bitmapInfo.bmiHeader.biSize)
- fseek(_aviFd, tmpLen - _bitmapInfo.bmiHeader.biSize, SEEK_CUR);
- }
- else if (aviStreamHeader.fccType == FOURCC_auds)
- {
- _hasAudio = true;
- _aviAudioStreamHeader = aviStreamHeader;
- _waveInfo.wFormatTag = readFileW();
- _waveInfo.nChannels = readFileW();
- _waveInfo.nSamplesPerSec = readFileDW();
- _waveInfo.nAvgBytesPerSec = readFileDW();
- _waveInfo.nBlockAlign = readFileW();
- _waveInfo.wBitsPerSample = readFileW();
- _waveInfo.cbSize = readFileW();
- fseek(_aviFd, _waveInfo.cbSize, SEEK_CUR);
- /*
- #include <mmreg.h>
- WAVE_FORMAT_PCM
- WAVE_FORMAT_ALAW
- */
- }
- // 跳过长度
- tmpOff += 4;
- tmpOff += tmpLen;
- }
- else if (fourcc == FOURCC_JUNK)
- {
- int tmpLen = readFileDW();
- fseek(_aviFd, tmpLen, SEEK_CUR);
- // 跳过长度
- tmpOff += 4;
- tmpOff += tmpLen;
- }
- else if (fourcc == FOURCC_vprp)
- {
- int tmpLen = readFileDW();
- fseek(_aviFd, tmpLen, SEEK_CUR);
- // 跳过长度
- tmpOff += 4;
- tmpOff += tmpLen;
- }
- }
- off += avListLen + 8;
- }
- break;
- case FOURCC_JUNK:
- {
- // 跳过JUNK
- off += 4;
- int tmpLen = readFileDW();
- fseek(_aviFd, tmpLen, SEEK_CUR);
- // 跳过长度
- off += 4;
- off += tmpLen;
- }
- break;
- }
- }
- }
- break;
- case FOURCC_INFO:
- {
- fourcc = readFileDW();
- if (fourcc == FOURCC_ISFT)
- {
- int tmpLen = readFileDW();
- _soft.resize(tmpLen);
- readFile(&_soft[0], tmpLen);
- }
- }
- break;
- case FOURCC_movi:
- {
- _moviOff = ftell(_aviFd);
- if (hasIndex)
- {
- // 跳过movi,直接到idx处
- fseek(_aviFd, size - 4, SEEK_CUR);
- }
- }
- break;
- }
- }
- else if (fourcc == FOURCC_idx1)
- {
- _aviIndex.fcc = FOURCC_idx1;
- _aviIndex.cb = size;
- int tmpOff = 0;
- while (tmpOff < _aviIndex.cb)
- {
- char tmpBuf[4] = {0};
- readFile(tmpBuf, 4);
- int index = read_le_dw(tmpBuf);
- AVIIndexEntry tmpEntry;
- tmpEntry.dwFlags = readFileDW();
- tmpEntry.dwOffset = readFileDW();
- tmpEntry.dwSize = readFileDW();
- if (tmpEntry.dwSize > _maxFrameSize)
- _maxFrameSize = tmpEntry.dwSize;
- // 视频数据
- if (tmpBuf[2] == 'd')
- {
- _aviIndex.videoIndexMap[index].push_back(tmpEntry);
- }
- else if (tmpBuf[2] == 'w')
- {
- _aviIndex.audioIndexMap[index].push_back(tmpEntry);
- }
- // 一个索引信息的长度
- tmpOff += 16;
- }
- }
- else if (fourcc == FOURCC_JUNK)
- {
- // 跳过
- fseek(_aviFd, size, SEEK_CUR);
- }
- }
- }
- bool AVIFileParser::readFile( char* buf, uint32_t len )
- {
- uint32_t ret = fread(buf, 1, len, _aviFd);
- if (ret != len)
- {
- if (feof(_aviFd) != 0)
- return true;
- #ifndef NPRINT
- printf("fread avi error: %d [%s]\n", errno, strerror(errno));
- #endif
- throw ERROR_INVALID_AVI;
- }
- return false;
- }
- bool AVIFileParser::readFileTagSize( DWORD& fourcc, uint32_t& size, DWORD& fourcc2 )
- {
- char tmpBuf[12] = {0};
- bool ret = readFile(tmpBuf, 12);
- fourcc = read_le_dw(tmpBuf);
- size = read_le_dw(tmpBuf + 4);
- fourcc2 = read_le_dw(tmpBuf + 8);
- return ret;
- }
- uint32_t AVIFileParser::readFileDW()
- {
- char tmpBuf[4] = {0};
- readFile(tmpBuf, 4);
- return read_le_dw(tmpBuf);
- }
- bool AVIFileParser::readFileDW( uint32_t& dw )
- {
- char tmpBuf[4] = {0};
- bool ret = readFile(tmpBuf, 4);
- dw = read_le_dw(tmpBuf);
- return ret;
- }
- uint16_t AVIFileParser::readFileW()
- {
- char tmpBuf[2] = {0};
- readFile(tmpBuf, 2);
- return read_le_w(tmpBuf);
- }
- bool AVIFileParser::readFileW( uint16_t& w )
- {
- char tmpBuf[2] = {0};
- bool ret = readFile(tmpBuf, 2);
- w = read_le_w(tmpBuf);
- return ret;
- }
- int32_t AVIFileParser::getVideoFrame( char* buf, int32_t index /*= -1*/ )
- {
- if (!_hasVideo)
- return -1;
- // 只取第一个
- std::vector<AVIIndexEntry>& videoVec = _aviIndex.videoIndexMap.begin()->second;
- int32_t tmpIndex = 0;
- if (index == -1)
- {
- if (_currVideoIndex < videoVec.size())
- tmpIndex = _currVideoIndex++;
- else
- return 0;
- }
- else
- tmpIndex = index;
- AVIIndexEntry& videoEntry = videoVec[tmpIndex];
- // 从movi结尾加上偏移后是dc db的数据,然后再跳过4字节的一个长度值
- fseek(_aviFd, _moviOff + videoEntry.dwOffset + 4, SEEK_SET);
- int32_t ret = fread(buf, 1, videoEntry.dwSize, _aviFd);
- assert(ret == videoEntry.dwSize);
- return ret;
- }
- bool AVIFileParser::seekVideoFrame( int32_t index )
- {
- std::vector<AVIIndexEntry>& videoVec = _aviIndex.videoIndexMap.begin()->second;
- if (index < videoVec.size())
- {
- _currVideoIndex = index;
- return true;
- }
- return false;
- }
转载请标明出处: http://blog.youkuaiyun.com/jwybobo2007/article/details/7662653