linux下avi檔案解析(附源碼)

來源:互聯網
上載者:User

前段時間由於項目需要,要解avi檔案,當時我第一時間想到用ffmpeg來處理,但想想覺得太大了,又是放到arm上跑的,感覺沒必要。然後,搜尋引擎上稍微搜了一下,沒找到有用的樣本,大部分都是利用windows的api進行讀寫,很明顯linux下用不了。結果花了2-3天時間研究並寫了一個avi檔案的解鎖裝代碼,但是後來因某些原因沒有去使用AVI了,所以代碼也沒進行後續的完善最佳化。這裡貼出來供記錄以及需要的人作參考。

標頭檔:

/*!@brief avi檔案分析提取器@author jwybobo2007@file avifileparser.h@note 代碼在使用時,請標明作者,保留出處:http://blog.csdn.net/jwybobo2007*/#include <stdio.h>#include <stdlib.h>#include <string>#include <vector>#include <map>//////////////////////////////////////////////////////////////////////////#define AVIIF_KEYFRAME0x00000010L// 索引裡面的主要畫面格標誌#define AVIIF_LIST0x00000001L// Flags for dwFlags|AVI頭中的標誌位#define AVIFILEINFO_HASINDEX0x00000010// 是否有索引#define AVIFILEINFO_MUSTUSEINDEX0x00000020#define AVIFILEINFO_ISINTERLEAVED0x00000100#define AVIFILEINFO_WASCAPTUREFILE0x00010000#define AVIFILEINFO_COPYRIGHTED0x00020000// 最大允許的AVI頭大小#define MAX_ALLOWED_AVI_HEADER_SIZE 131072// 打不開檔案#define ERROR_OPEN_AVI0// 不是有效AVI#define ERROR_INVALID_AVI1// 由於編譯器c,c++標準過低,沒有包含stdint.h標頭檔,並且現在也沒有使用boost庫,因此定義一些類型,來相容以前的代碼#ifdef NO_STDINTtypedef 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 filesconst DWORD FOURCC_data = 0x61746164;// for WAVE filesconst DWORD FOURCC_WAVE = 0x45564157;// for WAVE files// 調色盤typedef struct{BYTE rgbBlue;// 藍BYTE rgbGreen;// 綠BYTE rgbRed;// 紅BYTE rgbReserved;// 保留} RGBQUAD;// AVI主頭部typedef struct{FourCC fcc;// 必須為 avihDWORD 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;// 必須為 strhDWORD 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:/// 解析AVIvoid _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 NPRINTprintf("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;// 跳過hdrluint32_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;// 跳過strlint 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_PCMWAVE_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:{// 跳過JUNKoff += 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 NPRINTprintf("fread avi error: %d [%s]\n", errno, strerror(errno));#endifthrow 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++;elsereturn 0;}elsetmpIndex = 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.csdn.net/jwybobo2007/article/details/7662653

相關文章

Beyond APAC's No.1 Cloud

19.6% IaaS Market Share in Asia Pacific - Gartner IT Service report, 2018

Learn more >

Apsara Conference 2019

The Rise of Data Intelligence, September 25th - 27th, Hangzhou, China

Learn more >

Alibaba Cloud Free Trial

Learn and experience the power of Alibaba Cloud with a free trial worth $300-1200 USD

Learn more >

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。