libavi - parse and demux AVI

 

libavi - parse and demux AVI

AVI represents Audio Video Interleave file format. This is a multimedia file format displine which is regulated by Microsoft. AVI is wide-spreadly used under Windows operating system. For the moment this file format maybe the most popular multimedia file format. In AVI file, audio data and video data are interleaved, as its name indicates. Two preliminary elements in AVI is LIST and CHUNK. LIST can recursively contain LIST and CHUNK, but CHUNK cannot contain anything else other than pure data. As time passed by, multimedia file in Internet becomes larger and larger. Today, numerous high definite films compressed in DVDrip can be easily end up with dozen  GB of data. The prime AVI seems too limited, since it claims that an AVI cannot exceed 1 GB size. Under this situation, an extension to prime AVI is appealed, this is eXtended AVI file format, which is commonly called OpenDML AVI file format extensions. This extension introduces some modern characteristics in AVI, such as field indexing, M-JPEG support, et al.

 

I design a library which can parse AVI file format, traverse its data chunk and split video stream and audio stream. I'll give out the whole source code of this library in follows. The source code is comprised of two file: libavi.h and libavi.c. The main concept in this library is the AVI object, which actually is a structure of C. This structure simulates a C++ class, though indeed it's not. To test and verify this library, I write a test programme, which is called test_libavi.c. I'd expose it at the end of the two main source file.

 

I have tested this library under Linux. If you would like to use it under other platform, you may have to inspect it more to satisfy some details, such as byteordering. please report any bug to ybc2084@163.com or to my weblog: www.dormforce.net/blog/rockins. Enjoy yourself!

 

libavi.h

 

1 /*********************************************************************
  2 * $file    :    libavi.h
  3 * $desc    :    parsing AVI format file
  4 * $author    :    rockins
  5 * $date    :    Mon Nov 26 02:46:53 CST 2007
  6 * $copyright    :    all copyrights(c) reserved by rockins.
  7 **********************************************************************/
  8 #ifndef    _LIB_AVI_H_
  9 #define    _LIB_AVI_H_
 10
 11 #ifdef __cplusplus
 12     extern "C" {
 13 #endif
 14
 15 /********************************** error code *************************/
 16 #define    AVI_ERR_READ        0x00000001    // read error

 17 #define    AVI_ERR_MEM            0x00000002    // memory access error

 18 #define    AVI_ERR_NOT_RIFF    0x00000003    // not RIFF

 19 #define    AVI_ERR_NOT_AVI        0x00000004    // not AVI file

 20 #define AVI_ERR_BROKEN        0x00000005    // broken AVI file

 21
 22 #define AVIIF_KEYFRAME 0x00000010L    // indicate a keyfram

 23
 24 // chunk size must be aligned even

 25 #define    ALIGN_EVEN(x)    x = (((x) & 0x01) ? ((x)+1) : (x))
 26
 27 /***************************** typedef ***********************/
 28 typedef    unsigned char         FOURCC[4];
 29 typedef    unsigned short        WORD;
 30 typedef    unsigned int        DWORD;
 31 typedef unsigned long long     DWORDLONG;
 32
 33 // FOURCC

 34 typedef enum {
 35     FOURCC_UNDEF = 0,
 36     FOURCC_RIFF,
 37     FOURCC_LIST,
 38     FOURCC_JUNK,
 39     FOURCC_AVI,
 40     FOURCC_AVIX,
 41     FOURCC_WAVE,
 42     FOURCC_INFO,
 43     FOURCC_avih,
 44     FOURCC_hdrl,
 45     FOURCC_movi,
 46     FOURCC_idx1,
 47     FOURCC_strl,
 48     FOURCC_strh,
 49     FOURCC_strf,
 50     FOURCC_strd,
 51     FOURCC_strn,
 52     FOURCC_indx,
 53     FOURCC_rec,
 54     FOURCC_auds,
 55     FOURCC_vids,
 56     FOURCC_txts,
 57     FOURCC_mids,
 58     
 59     // following fourcc are used to identify subchunks of INFO chunk,

 60     // they are ignored in this library.

 61     FOURCC_IARL,
 62     FOURCC_IART,
 63     FOURCC_ICMS,
 64     FOURCC_ICMT,
 65     FOURCC_ICOP,
 66     FOURCC_ICRD,
 67     FOURCC_ICRP,
 68     FOURCC_IDIM,
 69     FOURCC_IDPI,
 70     FOURCC_IENG,
 71     FOURCC_IGNR,
 72     FOURCC_IKEY,
 73     FOURCC_ILGT,
 74     FOURCC_IMED,
 75     FOURCC_INAM,
 76     FOURCC_IPLT,
 77     FOURCC_IPRD,
 78     FOURCC_ISBJ,
 79     FOURCC_ISFT,
 80     FOURCC_ISHP,
 81     FOURCC_ISRC,
 82     FOURCC_ISRF,
 83     FOURCC_ITCH,
 84     FOURCC_ISMP,
 85     FOURCC_IDIT,
 86 }AVI_FOURCC_TYPE;
 87
 88 // TWOCC

 89 typedef enum {
 90     TWOCC_UNDEF = 0,
 91     TWOCC_wb,
 92     TWOCC_db,
 93     TWOCC_dc,
 94     TWOCC_pc,
 95 }AVI_TWOCC_TYPE;
 96
 97
 98 // flags for dwFlags field in AVIMAINHEADER

 99 // prefix AMHF denoting Avi Main Header Flag

100 #define    AMHF_HASINDEX        0x00000010
101 #define    AMHF_MUSTUSEINDEX    0x00000020
102 #define    AMHF_ISINTERLEAVED    0x00000100
103 #define    AMHF_TRUSTCKTYPE    0x00000800 /* Use CKType to find key frames? */
104 #define    AMHF_WASCAPTUREFILE    0x00010000
105 #define    AMHF_COPYRIGHTED    0x00020000
106 typedef struct _avimainheader {
107 FOURCC         fcc;                     // 必须为‘avih’

108 DWORD         cb;                     // 本数据结构的大小,不包括最初的8个字节(fcc和cb两个域)

109 DWORD         dwMicroSecPerFrame;     // 视频帧间隔时间(以毫秒为单位)

110 DWORD         dwMaxBytesPerSec;     // 这个AVI文件的最大数据率

111 DWORD        dwPaddingGranularity;     // 数据填充的粒度

112 DWORD         dwFlags;         // AVI文件的全局标记,比如是否含有索引块等

113 DWORD         dwTotalFrames;         // 总帧数

114 DWORD         dwInitialFrames;         // 为交互格式指定初始帧数(非交互格式应该指定为0)

115 DWORD         dwStreams;         // 本文件包含的流的个数

116 DWORD         dwSuggestedBufferSize;     // 建议读取本文件的缓存大小(应能容纳最大的块)

117 DWORD         dwWidth;         // 视频图像的宽(以像素为单位)

118 DWORD         dwHeight;         // 视频图像的高(以像素为单位)

119 DWORD         dwReserved[4];         // 保留

120 }AVIMAINHEADER;
121
122 typedef struct _avistreamheader {
123     FOURCC         fcc;                     // 必须为‘strh’

124     DWORD         cb;                     // 本数据结构的大小,不包括最初的8个字节(fcc和cb两个域)

125     FOURCC         fccType;             // 流的类型:‘auds’(音频流)、‘vids’(视频流)、‘mids’(MIDI流)、‘txts’(文字流)

126     FOURCC         fccHandler;             // 指定流的处理者,对于音视频来说就是解码器

127     DWORD         dwFlags;             // 标记:是否允许这个流输出?调色板是否变化?

128     WORD         wPriority;             // 流的优先级(当有多个相同类型的流时优先级最高的为默认流)

129     WORD         wLanguage;
130     DWORD         dwInitialFrames;         // 为交互格式指定初始帧数

131     DWORD         dwScale;                 // 这个流使用的时间尺度

132     DWORD         dwRate;
133     DWORD         dwStart;                 // 流的开始时间

134     DWORD         dwLength;                 // 流的长度(单位与dwScale和dwRate的定义有关)

135     DWORD         dwSuggestedBufferSize;     // 读取这个流数据建议使用的缓存大小

136     DWORD         dwQuality;             // 流数据的质量指标(0 ~ 10,000)

137     DWORD         dwSampleSize;             // Sample的大小

138     struct {
139         short int left;
140         short int top;
141         short int right;
142         short int bottom;
143     }rcFrame;                             // 指定这个流(视频流或文字流)在视频主窗口中的显示位置

144                                         // 视频主窗口由AVIMAINHEADER结构中的dwWidth和dwHeight决定

145 }AVISTREAMHEADER;
146
147 // flags for dwFlags field of AVIIDX1ENTRY

148 // prefix AIEF denoting Avi Index Entry Flag

149 #define    AIEF_LIST            0x00000001L        // indexed chunk is a list

150 #define    AIEF_KEYFRAME        0x00000010L        // indexed chunk is a key frame

151 #define    AIEF_NOTIME            0x00000100L        // indexed chunk frame do not consume any time

152 #define    AIEF_COMPUSE        0x0FFF0000L        // these bits are used by compressor

153 #define    AIEF_FIXKEYFRAME    0x00001000L        // XXX: borrowed from VLC, avoid using

154 typedef struct _aviidx1_entry {
155 FOURCC     dwChunkId;             // 表征本数据块的四字符码,eg, 01wb, 00dc, et al

156 DWORD     dwFlags;             // 说明本数据块是不是关键帧、是不是‘rec ’列表等信息

157 DWORD     dwOffset;             // 本数据块在文件中的偏移量,可能相对于文件开头,也可能相对于movi块

158 DWORD     dwSize;             // 本数据块的大小

159 }AVIIDX1ENTRY;
160
161 typedef struct _avisuperindex_entry {
162 DWORDLONG     qwOffset;     // absolute file offset

163 DWORD         dwSize;     // size of index chunk at this offset

164 DWORD         dwDuration;     // time span in stream ticks

165 } avisuperindex_entry;
166
167 typedef struct _avistdindex_entry {
168 DWORD         dwOffset;     // qwBaseOffset + this is absolute file offset

169 DWORD         dwSize;     // bit 31 is set if this is NOT a keyframe

170 } avistdindex_entry;
171
172 typedef struct _avistdindex_chunk {
173 FOURCC         fcc;                 // ix##

174 DWORD         dwSize;         // size of this chunk

175 WORD         wLongsPerEntry;     // must be sizeof(aIndex[0])/sizeof(DWORD)

176 char         bIndexSubType;     // must be 0

177 char         bIndexType;     // must be AVI_INDEX_OF_CHUNKS

178 DWORD         nEntriesInUse;         // first unused entry

179 FOURCC         dwChunkId;                 // '##dc' or '##db' or '##wb' etc..

180 DWORDLONG     qwBaseOffset;     // all dwOffsets in aIndex array are relative to this

181 DWORD         dwReserved3;         // must be 0

182 avistdindex_entry *aIndex;         // the actual frames

183 } avistdindex_chunk;
184
185 typedef struct _avisuperindex {
186 FOURCC     fcc;         // 'indx'

187 DWORD         cb;         // size of this structure

188 WORD     wLongsPerEntry;         // ==4

189 char     bIndexSubType;         // ==0 (frame index) or AVI_INDEX_SUB_2FIELD

190 char         bIndexType;         // ==AVI_INDEX_OF_INDEXES

191 DWORD     nEntriesInUse;         // offset of next unused entry in aIndex

192 DWORD     dwChunkId;         // chunk ID of chunks being indexed, (i.e. RGB8)

193 DWORD     dwReserved[3];         // must be 0

194 avisuperindex_entry *aIndex;            // index to std indices

195 avistdindex_chunk *stdidx;     // the actual std indices

196 } AVISUPERINDEX;
197
198 /*********************************customarized defines*************************************/
199 typedef struct _aviidx1_table {
200     AVIIDX1ENTRY    * idx1_head;
201     AVIIDX1ENTRY    * current_vid_idx;    // for traversing

202     AVIIDX1ENTRY    * current_aud_idx;
203     int                idx1_count;
204     int                idx1_length;
205 }AVIIDX1TABLE;
206
207 typedef struct _aviindex_table {
208     AVISUPERINDEX *index_head;
209     int             index_count;
210 }AVIINDXTABLE;
211
212 typedef struct _aviheader {
213
214     // structure for maintaining and accessing

215     AVIMAINHEADER    mainheader;
216     AVISTREAMHEADER    vid_streamheader;
217     AVISTREAMHEADER    aud_streamheader;
218     AVIIDX1TABLE    idx1_table;
219     AVIINDXTABLE     index_table;
220     int                movi_offset;
221     int                movi_length;
222     
223     // some indicator

224     int             have_hdrl;
225     int             have_strl;
226     int             have_movi;
227     int             have_idx1;
228     int             have_rec;
229     int             have_indx;
230     int             isodml;        // is OpenDML extension

231 }AVIHEADER;
232
233 /**************************** AVI object ***************************/
234 typedef struct _avi {
235     char            * file_name;    // avi file's name

236     int                fd;                // avi file's descriptor

237     
238     AVIHEADER        aviheader;
239     
240     int    (*init)(struct _avi * avi);
241     int (*parse)(struct _avi * avi);
242     
243     int (*goto_first_video_block)(struct _avi * avi);
244     int (*goto_first_audio_block)(struct _avi * avi);
245     
246     int (*goto_next_video_block)(struct _avi * avi);
247     int    (*goto_next_audio_block)(struct _avi * avi);
248     
249     int (*get_curr_video_block_size)(struct _avi * avi);
250     int (*get_curr_audio_block_size)(struct _avi * avi);
251     
252     int (*get_curr_video_block)(struct _avi * avi, char * video_block, int len);
253     int (*get_curr_audio_block)(struct _avi * avi, char * audio_block, int len);
254 }AVI;
255
256
257 /***************************** real interface ***************************/
258 static int    avi_init(AVI * avi);
259 static int    avi_parse(AVI * avi);
260 static int    avi_goto_first_video_block(AVI * avi);
261 static int    avi_goto_first_audio_block(AVI * avi);
262 static int    avi_goto_next_video_block(AVI * avi);
263 static int    avi_goto_next_audio_block(AVI * avi);
264 static int    avi_get_curr_video_block_size(AVI * avi);
265 static int    avi_get_curr_audio_block_size(AVI * avi);
266 static int    avi_get_curr_video_block(AVI * avi, char * video_block, int len);
267 static int    avi_get_curr_audio_block(AVI * avi, char * audio_block, int len);
268
269 /************ constructor & deconstructor ******************/
270 AVI * avi_new(char * file);
271 void avi_del(AVI * avi);
272
273 #ifdef __cplusplus
274     };
275 #endif    // __cplusplus

276
277 #endif    //_LIB_AVI_H_

 

libavi.c

1 /*********************************************************************
  2 * $file    :    libavi.c
  3 * $desc    :    parsing AVI format file
  4 * $author    :    rockins
  5 * $date    :    Mon Nov 26 02:50:17 CST 2007
  6 * $copyright    :    all copyrights(c) reserved by rockins.
  7 **********************************************************************/
  8
  9 #include <stdio.h>
 10 #include <stdlib.h>
 11 #include <strings.h>
 12 #include <sys/types.h>
 13 #include <sys/stat.h>
 14 #include <fcntl.h>
 15 #include "libavi.h"
 16
 17 //

 18 // compare two indices' offset

 19 //    returns:

 20 //         1 if elem1's offset large than elem2's

 21 //         0 if equal

 22 //         -1 if less

 23 //

 24 int avi_idx_cmp(const void * elem1, const void * elem2)
 25 {
 26     AVIIDX1ENTRY * e1 = (AVIIDX1ENTRY *)elem1;
 27     AVIIDX1ENTRY * e2 = (AVIIDX1ENTRY *)elem2;
 28     DWORD a = e1->dwOffset;
 29     DWORD b = e2->dwOffset;
 30     return (a > b) - (b > a);
 31 }
 32
 33 //

 34 // initializae: return 0

 35 //

 36 int
 37 avi_init(AVI * avi)
 38 {
 39     // clear aviheader

 40     memset(&avi->aviheader, 0, sizeof(AVIHEADER));
 41
 42     return (0);
 43 }
 44
 45 //

 46 // parse:

 47 // parse the AVI file

 48 // returns:

 49 //    0 if success, otherwise following error code returned.

 50 //    AVI_ERR_READ        if read error

 51 //    AVI_ERR_NOT_RIFF    if not RIFF chunk

 52 //    AVI_ERR_NOT_AVI    if not AVI file

 53 //    AVI_ERR_MEM        if memory error

 54 //    AVI_ERR_BROKEN    if AVI file is damaged

 55 //

 56 int
 57 avi_parse(AVI * avi)
 58 {
 59     AVISTREAMHEADER    avi_streamheader;
 60     AVISUPERINDEX * s;
 61     char data[256];
 62     int len;
 63     int    file_len;
 64     AVI_FOURCC_TYPE fourcc_type = FOURCC_UNDEF;
 65     AVI_TWOCC_TYPE twocc_type = TWOCC_UNDEF;
 66     
 67     // get file's total length

 68     file_len = lseek(avi->fd, 0, SEEK_END);
 69     
 70     // reset input stream to beginning

 71     lseek(avi->fd, 0, SEEK_SET);
 72
 73     // validate that the file is a RIFF AVI format file

 74     if ( read(avi->fd, data, 12) != 12 )
 75         return (AVI_ERR_READ);
 76     if ( !(strncasecmp(data, "RIFF", 4) == 0 ) )
 77         return (AVI_ERR_NOT_RIFF);
 78 if ( !(strncasecmp(data+8, "AVI ", 4) == 0) )
 79         return (AVI_ERR_NOT_AVI);
 80     
 81     do {
 82         // clear data and read

 83         len = 0;
 84         memset(data, 0, 256);
 85         if ( read(avi->fd, data, 8) != 8 )
 86             return (AVI_ERR_READ);
 87         
 88         fourcc_type = FOURCC_UNDEF;
 89         if ( strncasecmp(data, "LIST", 4) == 0 )            // list type

 90             fourcc_type = FOURCC_LIST;
 91         else if ( strncasecmp(data, "avih", 4) == 0 )    // following are all chunks type

 92             fourcc_type = FOURCC_avih;
 93         else if ( strncasecmp(data, "strh", 4) == 0 )
 94             fourcc_type = FOURCC_strh;
 95         else if ( strncasecmp(data, "strf", 4) == 0 )
 96             fourcc_type = FOURCC_strf;
 97         else if ( strncasecmp(data, "strd", 4) == 0 )
 98             fourcc_type = FOURCC_strd;
 99         else if ( strncasecmp(data, "strn", 4) == 0 )
100             fourcc_type = FOURCC_strn;
101         else if ( strncasecmp(data, "idx1", 4) == 0 )
102             fourcc_type = FOURCC_idx1;
103         else if ( strncasecmp(data, "indx", 4) == 0 )
104             fourcc_type = FOURCC_indx;
105         else if ( strncasecmp(data, "JUNK", 4) == 0 )
106             fourcc_type = FOURCC_JUNK;
107         else if ( strncasecmp(data, "RIFF", 4) == 0 )    // for OpenDML extension

108             fourcc_type = FOURCC_RIFF;
109
110         // main parse place

111         switch (fourcc_type) {
112         case FOURCC_LIST:
113             // reserved chunk size, used by movi handler branch

114             len = *(int *)(data + 4);
115             ALIGN_EVEN(len);            // align chunk size to even border

116             
117             if ( read(avi->fd, data, 4) != 4 )
118                 return (AVI_ERR_READ);
119             
120             if ( strncasecmp(data, "hdrl", 4) == 0 )
121                 avi->aviheader.have_hdrl = 1;
122             if ( strncasecmp(data, "strl", 4) == 0 )
123                 avi->aviheader.have_strl = 1;
124             if ( strncasecmp(data, "rec ", 4) == 0 )
125                 avi->aviheader.have_rec = 1;
126             if ( strncasecmp(data, "movi", 4) == 0 ) {
127                 avi->aviheader.have_movi = 1;
128                 
129                 // get movi offset, aligned

130                 if(!avi->aviheader.movi_offset){
131                     avi->aviheader.movi_offset = lseek(avi->fd, 0, SEEK_CUR);
132                     ALIGN_EVEN(avi->aviheader.movi_offset);
133                 }
134                 // get movi length

135                 avi->aviheader.movi_length = len;
136                 
137                 // if there are indicies at file end, just skip movi block

138                 if ( avi->aviheader.have_idx1 )
139                     lseek(avi->fd, len - 4, SEEK_CUR);
140             }
141             if (strncasecmp(data, "INFO", 4) == 0) {
142                 lseek(avi->fd, -12, SEEK_CUR);
143                 if ( read(avi->fd, data, 8) != 8 )
144                     return (AVI_ERR_READ);
145                 len = *(int *)(data + 4);
146                 ALIGN_EVEN(len);            // align chunk size to even border

147                 lseek(avi->fd, len, SEEK_CUR);
148             }
149             break;
150         case FOURCC_avih:
151             if ( avi->aviheader.have_hdrl ) {
152                 // back trace 8 bytes

153                 lseek(avi->fd, -8, SEEK_CUR);
154                 
155                 len = sizeof(AVIMAINHEADER);
156                 ALIGN_EVEN(len);            // align chunk size to even border

157                 if ( read(avi->fd,
158                     (char *)&avi->aviheader.mainheader,
159                     len) != len )
160                     return (AVI_ERR_READ);
161                 
162                 if ( avi->aviheader.mainheader.dwFlags & AMHF_HASINDEX)
163                     avi->aviheader.have_idx1 = 1;
164             }
165             break;
166         case FOURCC_strh:
167             if ( avi->aviheader.have_strl ) {
168                 // back trace 8 bytes

169                 lseek(avi->fd, -8, SEEK_CUR);
170                 
171                 len = sizeof(AVISTREAMHEADER);
172                 ALIGN_EVEN(len);            // align chunk size to even border

173                 if ( read(avi->fd,
174                     (char *)&avi_streamheader, len) != len )
175                     return (AVI_ERR_READ);
176                 
177                 if ( strncasecmp(avi_streamheader.fccType, "vids", 4) == 0 )
178                     avi->aviheader.vid_streamheader = avi_streamheader;
179                 else if ( strncasecmp(avi_streamheader.fccType, "auds", 4) == 0 )
180                     avi->aviheader.aud_streamheader = avi_streamheader;
181             }
182             break;
183         case FOURCC_idx1:
184             // read in index

185             len = *(int *)(data + 4);
186             ALIGN_EVEN(len);            // align chunk size to even border

187             avi->aviheader.idx1_table.idx1_head = (AVIIDX1ENTRY *)malloc(len);
188             if ( avi->aviheader.idx1_table.idx1_head == NULL )
189                 return (AVI_ERR_MEM);
190             if ( read(avi->fd, (char *)avi->aviheader.idx1_table.idx1_head,
191                                     len) != len )
192                 return (AVI_ERR_READ);
193             avi->aviheader.idx1_table.idx1_length = len;
194             avi->aviheader.idx1_table.idx1_count = len / sizeof(AVIIDX1ENTRY);
195             avi->aviheader.have_idx1 = 1;
196             break;
197         case FOURCC_indx:
198             // super index

199             len = *(int *)(data + 4);
200             ALIGN_EVEN(len);            // align chunk size to even border

201             if (len < 24)
202                 break;
203             avi->aviheader.index_table.index_count++;
204             avi->aviheader.index_table.index_head = realloc(
205                     avi->aviheader.index_table.index_head,
206                     avi->aviheader.index_table.index_count * sizeof(AVISUPERINDEX));
207             if (avi->aviheader.index_table.index_head == NULL)
208                 return (AVI_ERR_MEM);
209             s = avi->aviheader.index_table.index_head + avi->aviheader.index_table.index_count -1;
210             memcpy(s->fcc, "indx", 4);
211             s->cb = len ;
212             if (read(avi->fd, ((char *)s)+8, 24)!=24)
213                 return (AVI_ERR_READ);
214             memset(s->dwReserved, 0, 3 * 4);        // stuff dwReserved[3] of AVISUPERINDEX

215             s->aIndex = calloc(s->nEntriesInUse, sizeof(avisuperindex_entry));
216             s->stdidx = calloc(s->nEntriesInUse, sizeof(avistdindex_chunk));
217             if (s->aIndex == NULL)
218                 return (AVI_ERR_MEM);
219             if (read(avi->fd, (char *)s->aIndex,
220                 s->nEntriesInUse * sizeof(avisuperindex_entry))
221                 != s->nEntriesInUse * sizeof(avisuperindex_entry))
222                 return (AVI_ERR_READ);
223             avi->aviheader.have_indx = 1 ;
224             break;
225         case FOURCC_RIFF:
226             // another RIFF List, for OpenDML

227             if (read(avi->fd, data, 4) != 4)
228                 return (AVI_ERR_READ);
229             if (strncmp(data, "AVIX", 4))
230                 return (AVI_ERR_NOT_RIFF);
231             else{
232                 /* We got an extended AVI header, so we need to switch to
233                   * OpenDML to get seeking to work, provided we got indx chunks
234                   * in the header(have_indx == 1)*/
235                 if( avi->aviheader.have_indx)
236                     avi->aviheader.isodml = 1 ;
237             }
238             break;
239         case FOURCC_strf:
240         case FOURCC_strd:
241         case FOURCC_strn:
242         case FOURCC_JUNK:    
243         default:
244             // skipped

245             len = *(int *)(data + 4);
246             ALIGN_EVEN(len);            // align chunk size to even border

247             lseek(avi->fd, len, SEEK_CUR);
248             break;
249         }
250     } while ( lseek(avi->fd, 0, SEEK_CUR) < file_len );
251     
252     // if neither idx1 nor indx, then generate idx1

253     if (!avi->aviheader.have_idx1 && !avi->aviheader.have_indx) {
254         //rewind the stream position to the beginning of the file

255         lseek(avi->fd, 0, SEEK_SET);
256         
257         //seek to the begining of movi chunk

258         lseek(avi->fd, avi->aviheader.movi_offset, SEEK_CUR);
259         
260         AVIIDX1ENTRY * curidx;
261         char id[4];
262         while (read(avi->fd, id, 4) >=0 && lseek(avi->fd, 0, SEEK_CUR) <
263                 avi->aviheader.movi_offset + avi->aviheader.movi_length - 4){
264             if (read(avi->fd, data, 4) != 4)
265                 return (AVI_ERR_READ);
266             len = *(int*)data;
267             avi->aviheader.idx1_table.idx1_head = realloc(
268                     avi->aviheader.idx1_table.idx1_head,
269                     avi->aviheader.idx1_table.idx1_length + sizeof(AVIIDX1ENTRY));
270             if ( avi->aviheader.idx1_table.idx1_head != NULL )
271                 curidx = avi->aviheader.idx1_table.idx1_head + avi->aviheader.idx1_table.idx1_count;
272             else
273                 return (AVI_ERR_MEM);
274             memcpy(&curidx->dwChunkId, id, 4);    // FOURCC

275             curidx->dwFlags = AVIIF_KEYFRAME;
276             curidx->dwFlags |= (lseek(avi->fd, 0, SEEK_CUR)) >> 16 & 0xffff0000U;
277             curidx->dwOffset = (unsigned int)(lseek(avi->fd, 0, SEEK_CUR) -
278                             avi->aviheader.movi_offset - 4);    // offset relative to movi

279             curidx->dwSize = len;
280             avi->aviheader.idx1_table.idx1_count++;
281             avi->aviheader.idx1_table.idx1_length += sizeof(AVIIDX1ENTRY);
282             ALIGN_EVEN(len);
283             lseek(avi->fd, len, SEEK_CUR);
284         }
285         avi->aviheader.have_idx1 = 1 ;
286     }
287     
288     //deal with super index

289     if (avi->aviheader.isodml && avi->aviheader.have_indx) {
290         AVISUPERINDEX * cx;
291     AVIIDX1ENTRY * idx;
292         int i, j;
293         
294         if (avi->aviheader.idx1_table.idx1_head)
295             free(avi->aviheader.idx1_table.idx1_head);
296         avi->aviheader.idx1_table.idx1_count = 0;
297         avi->aviheader.idx1_table.idx1_head = NULL ;
298         
299         // read the standard indices

300         for (cx = &avi->aviheader.index_table.index_head[0], i = 0;
301                 i < avi->aviheader.index_table.index_count; cx++, i++) {
302             for (j = 0; j < cx->nEntriesInUse; j++){
303                 int ret1, ret2;
304                 
305                 memset(&cx->stdidx[j], 0, 32);
306                 // reset input stream to beginning

307                 lseek(avi->fd, 0, SEEK_SET);
308                 ret1 = lseek(avi->fd, (DWORDLONG)cx->aIndex[j].qwOffset, SEEK_CUR);
309                 if ((ret2 = read(avi->fd, (char *)&cx->stdidx[j], 32)) != 32)
310                     return (AVI_ERR_READ);
311                 if (ret1 < 0 || cx->stdidx[j].nEntriesInUse == 0) {
312                     // this is a broken file (probably incomplete)

313                     avi->aviheader.isodml = 0;
314                     avi->aviheader.idx1_table.idx1_count = 0 ;
315                     return (AVI_ERR_BROKEN);
316                 }
317                 avi->aviheader.idx1_table.idx1_count += cx->stdidx[j].nEntriesInUse;
318                 cx->stdidx[j].aIndex = malloc(cx->stdidx[j].nEntriesInUse * sizeof(avistdindex_entry));
319                 if (cx->stdidx[j].aIndex == NULL)
320                      return (AVI_ERR_MEM);
321                 if (read(avi->fd, (char *)cx->stdidx[j].aIndex,
322                     cx->stdidx[j].nEntriesInUse * sizeof(avistdindex_entry)) !=
323                     cx->stdidx[j].nEntriesInUse * sizeof(avistdindex_entry))
324                      return (AVI_ERR_READ);
325                 cx->stdidx[j].dwReserved3 = 0;
326             }
327         }
328         
329         /*
330         * convert the index by translating all entries into AVIIDX1ENTRYs
331         * and sorting them by offset.
332         */
333     avi->aviheader.idx1_table.idx1_head = malloc(
334                 avi->aviheader.idx1_table.idx1_count * sizeof(AVIIDX1ENTRY));
335         idx = avi->aviheader.idx1_table.idx1_head;
336         if (idx == NULL)
337             return (AVI_ERR_MEM);
338         for (cx = avi->aviheader.index_table.index_head;
339             cx != &avi->aviheader.index_table.index_head[avi->aviheader.index_table.index_count]; cx++) {
340             avistdindex_chunk * sic;
341             for (sic = cx->stdidx; sic != &cx->stdidx[cx->nEntriesInUse]; sic++){
342                 avistdindex_entry * sie;
343                 for (sie=sic->aIndex; sie != &sic->aIndex[sic->nEntriesInUse]; sie++){
344                     DWORDLONG offset = sie->dwOffset + sic->qwBaseOffset;
345                     memcpy(idx->dwChunkId, sic->dwChunkId, 4);
346                     idx->dwOffset = offset;
347                     idx->dwFlags = (offset >> 32) << 16;
348                     idx->dwSize = sie->dwSize & 0x7fffffff;
349                     idx->dwFlags |= (sie->dwSize & 0x80000000)?0x0:AVIIF_KEYFRAME;
350                     idx++;
351                 }
352             }
353         }
354         
355         // sorting in offset

356         qsort(avi->aviheader.idx1_table.idx1_head, avi->aviheader.idx1_table.idx1_count,
357             sizeof(AVIIDX1ENTRY), avi_idx_cmp);
358     }
359     
360     return (0);
361 }
362
363 //

364 // go to first video block

365 // return 0 if found, else -1

366 //

367 int
368 avi_goto_first_video_block(AVI * avi)
369 {
370     AVIIDX1ENTRY    * pIdx1Entry;
371     FOURCC            four_cc;
372     
373     if ( avi->aviheader.have_idx1 || avi->aviheader.have_indx) {
374         pIdx1Entry = avi->aviheader.idx1_table.idx1_head;
375         memcpy(four_cc, &pIdx1Entry->dwChunkId, 4);
376         
377         while ( ! ( (four_cc[2] == 'd' && four_cc[3] == 'b') ||
378                     (four_cc[2] == 'd' && four_cc[3] == 'c') ) ) {
379             if ( pIdx1Entry++ < avi->aviheader.idx1_table.idx1_head + avi->aviheader.idx1_table.idx1_count )
380                 memcpy(four_cc, &pIdx1Entry->dwChunkId, 4);
381         }
382         
383         if ( pIdx1Entry >= avi->aviheader.idx1_table.idx1_head + avi->aviheader.idx1_table.idx1_count ) {
384             avi->aviheader.idx1_table.current_vid_idx = NULL;
385             return (-1);
386         } else {
387             avi->aviheader.idx1_table.current_vid_idx = pIdx1Entry;        // that's it

388             return (0);
389         }
390     } else
391         return (AVI_ERR_NOT_AVI);
392     
393     return (-1);
394 }
395
396 //

397 // go to first audio block

398 // return 0 if found, else -1

399 //

400 int
401 avi_goto_first_audio_block(AVI * avi)
402 {
403     AVIIDX1ENTRY    * pIdx1Entry;
404     FOURCC            four_cc;
405     
406     if ( avi->aviheader.have_idx1 || avi->aviheader.have_indx) {
407         pIdx1Entry = avi->aviheader.idx1_table.idx1_head;
408         memcpy(four_cc, &pIdx1Entry->dwChunkId, 4);
409         
410         while ( ! (four_cc[2] == 'w' && four_cc[3] == 'b') ) {
411             if ( pIdx1Entry++ < avi->aviheader.idx1_table.idx1_head + avi->aviheader.idx1_table.idx1_count )
412                 memcpy(four_cc, &pIdx1Entry->dwChunkId, 4);
413         }
414         
415         if ( pIdx1Entry >= avi->aviheader.idx1_table.idx1_head + avi->aviheader.idx1_table.idx1_count ) {
416             avi->aviheader.idx1_table.current_aud_idx = NULL;
417             return (-1);
418         } else {
419             avi->aviheader.idx1_table.current_aud_idx = pIdx1Entry;        // that's it

420             return (0);
421         }
422     } else
423         return (AVI_ERR_NOT_AVI);
424     
425     return (-1);
426 }
427
428 //

429 // step to next video block

430 //     returns: 1 if next video block exist, otherwise 0

431 //

432 int
433 avi_goto_next_video_block(AVI * avi)
434 {
435     AVIIDX1ENTRY    * pIdx1Entry;
436     FOURCC            four_cc;
437     
438     pIdx1Entry = avi->aviheader.idx1_table.current_vid_idx + 1;
439     if (pIdx1Entry < avi->aviheader.idx1_table.idx1_head + avi->aviheader.idx1_table.idx1_count) {
440         memcpy(four_cc, &pIdx1Entry->dwChunkId, 4);
441         
442         while ( ! ( (four_cc[2] == 'd' && four_cc[3] == 'b') ||
443                     (four_cc[2] == 'd' && four_cc[3] == 'c') ) ) {
444             if (pIdx1Entry++ < avi->aviheader.idx1_table.idx1_head + avi->aviheader.idx1_table.idx1_count)
445                 memcpy(four_cc, &pIdx1Entry->dwChunkId, 4);
446             else
447                 break;
448         }
449         
450         if (pIdx1Entry >= avi->aviheader.idx1_table.idx1_head + avi->aviheader.idx1_table.idx1_count) {
451             avi->aviheader.idx1_table.current_vid_idx = NULL;
452         } else {
453             avi->aviheader.idx1_table.current_vid_idx = pIdx1Entry;    // that's it, update it

454         }
455     } else {
456         avi->aviheader.idx1_table.current_vid_idx = NULL;
457     }
458     
459     return (avi->aviheader.idx1_table.current_vid_idx?1:0);
460 }
461
462 //

463 // step to next audio block

464 //     returns: 1 if next audio block exist, otherwise 0

465 //

466 int
467 avi_goto_next_audio_block(AVI * avi)
468 {
469     AVIIDX1ENTRY    * pIdx1Entry;
470     FOURCC            four_cc;
471     
472     pIdx1Entry = avi->aviheader.idx1_table.current_aud_idx + 1;
473     if (pIdx1Entry < avi->aviheader.idx1_table.idx1_head + avi->aviheader.idx1_table.idx1_count) {
474         memcpy(four_cc, &pIdx1Entry->dwChunkId, 4);
475         
476         while ( ! (four_cc[2] == 'w' && four_cc[3] == 'b') ) {
477             if (pIdx1Entry++ < avi->aviheader.idx1_table.idx1_head + avi->aviheader.idx1_table.idx1_count)
478                 memcpy(four_cc, &pIdx1Entry->dwChunkId, 4);
479             else
480                 break;
481         }
482         
483         if (pIdx1Entry >= avi->aviheader.idx1_table.idx1_head + avi->aviheader.idx1_table.idx1_count) {
484             avi->aviheader.idx1_table.current_aud_idx = NULL;
485         } else {
486             avi->aviheader.idx1_table.current_aud_idx = pIdx1Entry;    // that's it, update it

487         }
488     } else {
489         avi->aviheader.idx1_table.current_aud_idx = NULL;
490     }
491     
492     return (avi->aviheader.idx1_table.current_aud_idx?1:0);
493 }
494
495 //

496 // get current video block size:

497 // return block size in byte if success, else -1

498 //

499 int
500 avi_get_curr_video_block_size(AVI * avi)
501 {
502     if ( avi->aviheader.have_idx1 || avi->aviheader.have_indx ) {
503         if ( avi->aviheader.idx1_table.current_vid_idx == NULL ||
504              avi->aviheader.idx1_table.current_vid_idx >=
505              avi->aviheader.idx1_table.idx1_head + avi->aviheader.idx1_table.idx1_count) {
506             avi->aviheader.idx1_table.current_vid_idx == NULL;
507             return (-1);
508         } else
509             return ( avi->aviheader.idx1_table.current_vid_idx->dwSize );
510     }
511     
512     return (-1);
513 }
514
515 //

516 // get current audio block size

517 // return block size in byte if success, else -1

518 //

519 int
520 avi_get_curr_audio_block_size(AVI * avi)
521 {
522     if ( avi->aviheader.have_idx1 || avi->aviheader.have_indx) {
523         if ( avi->aviheader.idx1_table.current_aud_idx == NULL ||
524              avi->aviheader.idx1_table.current_aud_idx >=
525              avi->aviheader.idx1_table.idx1_head + avi->aviheader.idx1_table.idx1_count) {
526             avi->aviheader.idx1_table.current_aud_idx == NULL;
527             return (-1);
528         } else
529             return ( avi->aviheader.idx1_table.current_aud_idx->dwSize );
530     }
531     
532     return (-1);
533 }
534
535 //

536 // get current video block

537 // return actual size if success, else -1

538 //

539 int
540 avi_get_curr_video_block(AVI * avi, char * video_block, int len)
541 {
542     FOURCC            four_cc;
543     int                ret;
544     
545     if (avi->aviheader.have_idx1 || avi->aviheader.have_indx) {
546         // validate

547         if ( avi->aviheader.idx1_table.current_vid_idx == NULL ||
548              avi->aviheader.idx1_table.current_vid_idx->dwSize > len ||
549              avi->aviheader.idx1_table.current_vid_idx >=
550              avi->aviheader.idx1_table.idx1_head + avi->aviheader.idx1_table.idx1_count )
551             return (-1);
552         
553         // seek and read video block

554         lseek(avi->fd, (1 - avi->aviheader.have_indx) * (avi->aviheader.movi_offset + 4) +
555                 avi->aviheader.idx1_table.current_vid_idx->dwOffset, SEEK_SET);
556         ret = read(avi->fd, video_block,
557                     avi->aviheader.idx1_table.current_vid_idx->dwSize);
558                     
559         return (ret);
560     }
561     
562     return (-1);
563 }
564
565 //

566 // get current audio block

567 // return actual size in byte if success, else -1

568 //

569 int
570 avi_get_curr_audio_block(AVI * avi, char * audio_block, int len)
571 {
572     FOURCC            four_cc;
573     int                ret;
574     
575     if (avi->aviheader.have_idx1 || avi->aviheader.have_indx) {
576         // validate

577         if ( avi->aviheader.idx1_table.current_aud_idx == NULL ||
578              avi->aviheader.idx1_table.current_aud_idx->dwSize > len ||
579              avi->aviheader.idx1_table.current_aud_idx >=
580              avi->aviheader.idx1_table.idx1_head + avi->aviheader.idx1_table.idx1_count )
581             return (-1);
582         
583         // seek and read audio block

584         lseek(avi->fd, (1 - avi->aviheader.have_indx) * (avi->aviheader.movi_offset + 4) +
585                 avi->aviheader.idx1_table.current_aud_idx->dwOffset, SEEK_SET);
586         ret = read(avi->fd, audio_block,
587                     avi->aviheader.idx1_table.current_aud_idx->dwSize);
588         
589         return (ret);
590     }
591     
592     return (-1);
593 }
594
595
596 /************ constructor & deconstructor ******************/
597 //

598 // constructor:

599 //    param: file - input file's name

600 //    return: an AVI object if success, otherwise NULL

601 //

602 AVI * avi_new(char * file)
603 {
604     AVI * avi;
605     
606     avi = (AVI *)malloc(sizeof(AVI));
607     if (avi) {
608         avi->file_name = file;
609         avi->fd = open(file, O_RDONLY);
610     }
611     
612     // install member functions

613     avi->init = avi_init;
614     avi->parse = avi_parse;
615     avi->goto_first_video_block = avi_goto_first_video_block;
616     avi->goto_first_audio_block = avi_goto_first_audio_block;
617     avi->goto_next_video_block = avi_goto_next_video_block;
618     avi->goto_next_audio_block = avi_goto_next_audio_block;
619     avi->get_curr_video_block_size = avi_get_curr_video_block_size;
620     avi->get_curr_audio_block_size = avi_get_curr_audio_block_size;
621     avi->get_curr_video_block = avi_get_curr_video_block;
622     avi->get_curr_audio_block = avi_get_curr_audio_block;
623     
624     return (avi?avi:NULL);
625 }
626
627 //

628 // deconstructor

629 //    param: an AVI object

630 //

631 void avi_del(AVI * avi)
632 {
633     if (avi) {
634         close(avi->fd);
635         free(avi);
636     }
637 }

test_libavi.c

1 /*********************************************************************
 2 * $file    :    test_libavi.c
 3 * $desc    :    test libavi library, function as split video/audio
 4 *                 data from interleaved audio-video data stream
 5 * $author    :    rockins
 6 * $date    :    Tue Nov 27 17:14:47 CST 2007
 7 * $copyright    :    all copyrights(c) reserved by rockins.
 8 **********************************************************************/
 9 #include <stdio.h>
10 #include <stdlib.h>
11 #include <unistd.h>
12 #include <assert.h>
13 #include "libavi.h"
14
15 #define    DUMP_VIDEO    1        // control dump video or audio

16
17 #define    BUFF_SZ        100 * 1024
18 int
19 main(int argc, char *argv[])
20 {
21     AVI     * avi;
22     char    buff[BUFF_SZ];
23     int        block_sz;
24     int        block_len;
25     int        ret;
26     
27     avi = avi_new("zsh_yxdcb.avi");
28     
29     avi->init(avi);
30     avi->parse(avi);
31
32 #if DUMP_VIDEO
33     ret = avi->goto_first_video_block(avi);
34     assert(ret == 0);
35     
36     block_sz = avi->get_curr_video_block_size(avi);
37     while (block_sz > 0) {
38         assert(block_sz < BUFF_SZ);
39         block_len = avi->get_curr_video_block(avi, buff, block_sz);
40         assert(block_len == block_sz);
41         
42         write(STDOUT_FILENO, buff, block_len);
43         
44         avi->goto_next_video_block(avi);
45         block_sz = avi->get_curr_video_block_size(avi);
46     }
47 #else    // !DUMP_VIDEO, dump audio data

48     ret = avi->goto_first_audio_block(avi);
49     assert(ret == 0);
50     
51     block_sz = avi->get_curr_audio_block_size(avi);
52     while (block_sz > 0) {
53         assert(block_sz < BUFF_SZ);
54         block_len = avi->get_curr_audio_block(avi, buff, block_sz);
55         assert(block_len == block_sz);
56         
57         write(STDOUT_FILENO, buff, block_len);
58         
59         avi->goto_next_audio_block(avi);
60         block_sz = avi->get_curr_audio_block_size(avi);
61     }
62 #endif    //DUMP_VIDEO

63     
64     avi_del(avi);
65     
66     return (0);
67 }

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值