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个字节(fcccb两个域)

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个字节(fcccb两个域)

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
;                 // 流的长度(单位与dwScaledwRate的定义有关)

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结构中的dwWidthdwHeight决定

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
}


 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值