00001 /* 00002 * avilib.c 00003 * 00004 * Copyright (C) Thomas Östreich - June 2001 00005 * multiple audio track support Copyright (C) 2002 Thomas Östreich 00006 * 00007 * Original code: 00008 * Copyright (C) 1999 Rainer Johanni <Rainer@Johanni.de> 00009 * 00010 * This file is part of transcode, a linux video stream processing tool 00011 * 00012 * transcode is free software; you can redistribute it and/or modify 00013 * it under the terms of the GNU General Public License as published by 00014 * the Free Software Foundation; either version 2, or (at your option) 00015 * any later version. 00016 * 00017 * transcode is distributed in the hope that it will be useful, 00018 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00019 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00020 * GNU General Public License for more details. 00021 * 00022 * You should have received a copy of the GNU General Public License 00023 * along with GNU Make; see the file COPYING. If not, write to 00024 * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. 00025 * 00026 */ 00027 00028 //SLM 00029 #ifdef WIN32 00030 #include <io.h> 00031 #define ftruncate _chsize 00032 #define strncasecmp _strnicmp 00033 typedef int ssize_t; 00034 #endif 00035 00036 #ifdef __CYGWIN__ 00037 #include <unistd.h> 00038 #endif 00039 00040 #include "avilib.h" 00041 //#include <time.h> 00042 00043 #define INFO_LIST 00044 00045 /* The following variable indicates the kind of error */ 00046 00047 long AVI_errno = 0; 00048 00049 #define MAX_INFO_STRLEN 64 00050 static char id_str[MAX_INFO_STRLEN]; 00051 00052 #define FRAME_RATE_SCALE 1000000 00053 00054 /******************************************************************* 00055 * * 00056 * Utilities for writing an AVI File * 00057 * * 00058 *******************************************************************/ 00059 00060 static size_t avi_read(int fd, char *buf, size_t len) 00061 { 00062 size_t n = 0; 00063 size_t r = 0; 00064 00065 while (r < len) { 00066 n = read (fd, buf + r, len - r); 00067 00068 if ((ssize_t)n <= 0) 00069 return r; 00070 r += n; 00071 } 00072 00073 return r; 00074 } 00075 00076 static size_t avi_write (int fd, char *buf, size_t len) 00077 { 00078 size_t n = 0; 00079 size_t r = 0; 00080 00081 while (r < len) { 00082 n = write (fd, buf + r, len - r); 00083 if ((ssize_t)n < 0) 00084 return n; 00085 00086 r += n; 00087 } 00088 return r; 00089 } 00090 00091 /* HEADERBYTES: The number of bytes to reserve for the header */ 00092 00093 #define HEADERBYTES 2048 00094 00095 /* AVI_MAX_LEN: The maximum length of an AVI file, we stay a bit below 00096 the 2GB limit (Remember: 2*10^9 is smaller than 2 GB) */ 00097 00098 #define AVI_MAX_LEN (UINT_MAX-(1<<20)*16-HEADERBYTES) 00099 00100 #define PAD_EVEN(x) ( ((x)+1) & ~1 ) 00101 00102 00103 /* Copy n into dst as a 4 byte, little endian number. 00104 Should also work on big endian machines */ 00105 00106 static void long2str(unsigned char *dst, int n) 00107 { 00108 dst[0] = (n )&0xff; 00109 dst[1] = (n>> 8)&0xff; 00110 dst[2] = (n>>16)&0xff; 00111 dst[3] = (n>>24)&0xff; 00112 } 00113 00114 /* Convert a string of 4 or 2 bytes to a number, 00115 also working on big endian machines */ 00116 00117 static unsigned long str2ulong(unsigned char *str) 00118 { 00119 return ( str[0] | (str[1]<<8) | (str[2]<<16) | (str[3]<<24) ); 00120 } 00121 static unsigned long str2ushort(unsigned char *str) 00122 { 00123 return ( str[0] | (str[1]<<8) ); 00124 } 00125 00126 /* Calculate audio sample size from number of bits and number of channels. 00127 This may have to be adjusted for eg. 12 bits and stereo */ 00128 00129 static int avi_sampsize(avi_t *AVI, int j) 00130 { 00131 int s; 00132 s = ((AVI->track[j].a_bits+7)/8)*AVI->track[j].a_chans; 00133 // if(s==0) s=1; /* avoid possible zero divisions */ 00134 if(s<4) s=4; /* avoid possible zero divisions */ 00135 return s; 00136 } 00137 00138 /* Add a chunk (=tag and data) to the AVI file, 00139 returns -1 on write error, 0 on success */ 00140 00141 static int avi_add_chunk(avi_t *AVI, unsigned char *tag, unsigned char *data, int length) 00142 { 00143 unsigned char c[8]; 00144 00145 /* Copy tag and length int c, so that we need only 1 write system call 00146 for these two values */ 00147 00148 memcpy(c,tag,4); 00149 long2str(c+4,length); 00150 00151 /* Output tag, length and data, restore previous position 00152 if the write fails */ 00153 00154 length = PAD_EVEN(length); 00155 00156 if( avi_write(AVI->fdes,(char *)c,8) != 8 || 00157 avi_write(AVI->fdes,(char *)data,length) != length ) 00158 { 00159 lseek(AVI->fdes,AVI->pos,SEEK_SET); 00160 AVI_errno = AVI_ERR_WRITE; 00161 return -1; 00162 } 00163 00164 /* Update file position */ 00165 00166 AVI->pos += 8 + length; 00167 00168 //fprintf(stderr, "pos=%lu %s\n", AVI->pos, tag); 00169 00170 return 0; 00171 } 00172 00173 static int avi_add_index_entry(avi_t *AVI, unsigned char *tag, long flags, unsigned long pos, unsigned long len) 00174 { 00175 void *ptr; 00176 00177 if(AVI->n_idx>=AVI->max_idx) { 00178 ptr = realloc((void *)AVI->idx,(AVI->max_idx+4096)*16); 00179 00180 if(ptr == 0) { 00181 AVI_errno = AVI_ERR_NO_MEM; 00182 return -1; 00183 } 00184 AVI->max_idx += 4096; 00185 AVI->idx = (unsigned char((*)[16]) ) ptr; 00186 } 00187 00188 /* Add index entry */ 00189 00190 // fprintf(stderr, "INDEX %s %ld %lu %lu\n", tag, flags, pos, len); 00191 00192 memcpy(AVI->idx[AVI->n_idx],tag,4); 00193 long2str(AVI->idx[AVI->n_idx]+ 4,flags); 00194 long2str(AVI->idx[AVI->n_idx]+ 8, pos); 00195 long2str(AVI->idx[AVI->n_idx]+12, len); 00196 00197 /* Update counter */ 00198 00199 AVI->n_idx++; 00200 00201 if(len>AVI->max_len) AVI->max_len=len; 00202 00203 return 0; 00204 } 00205 00206 //SLM 00207 #ifndef S_IRUSR 00208 #define S_IRWXU 00700 /* read, write, execute: owner */ 00209 #define S_IRUSR 00400 /* read permission: owner */ 00210 #define S_IWUSR 00200 /* write permission: owner */ 00211 #define S_IXUSR 00100 /* execute permission: owner */ 00212 #define S_IRWXG 00070 /* read, write, execute: group */ 00213 #define S_IRGRP 00040 /* read permission: group */ 00214 #define S_IWGRP 00020 /* write permission: group */ 00215 #define S_IXGRP 00010 /* execute permission: group */ 00216 #define S_IRWXO 00007 /* read, write, execute: other */ 00217 #define S_IROTH 00004 /* read permission: other */ 00218 #define S_IWOTH 00002 /* write permission: other */ 00219 #define S_IXOTH 00001 /* execute permission: other */ 00220 #endif 00221 00222 /* 00223 AVI_open_output_file: Open an AVI File and write a bunch 00224 of zero bytes as space for the header. 00225 00226 returns a pointer to avi_t on success, a zero pointer on error 00227 */ 00228 00229 avi_t* AVI_open_output_file(char * filename) 00230 { 00231 avi_t *AVI; 00232 int i; 00233 00234 int mask; 00235 00236 unsigned char AVI_header[HEADERBYTES]; 00237 00238 /* Allocate the avi_t struct and zero it */ 00239 00240 AVI = (avi_t *) malloc(sizeof(avi_t)); 00241 if(AVI==0) 00242 { 00243 AVI_errno = AVI_ERR_NO_MEM; 00244 return 0; 00245 } 00246 memset((void *)AVI,0,sizeof(avi_t)); 00247 00248 /* Since Linux needs a long time when deleting big files, 00249 we do not truncate the file when we open it. 00250 Instead it is truncated when the AVI file is closed */ 00251 00252 mask = umask (0); 00253 umask (mask); 00254 00255 #ifdef WIN32 00256 AVI->fdes = open(filename, O_RDWR|O_CREAT|O_BINARY, (S_IRUSR | S_IWUSR) &~ mask); 00257 #else 00258 AVI->fdes = open(filename, O_RDWR|O_CREAT, (S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH) &~ mask); 00259 #endif 00260 if (AVI->fdes < 0) 00261 { 00262 AVI_errno = AVI_ERR_OPEN; 00263 free(AVI); 00264 return 0; 00265 } 00266 00267 /* Write out HEADERBYTES bytes, the header will go here 00268 when we are finished with writing */ 00269 00270 for (i=0;i<HEADERBYTES;i++) AVI_header[i] = 0; 00271 i = avi_write(AVI->fdes,(char *)AVI_header,HEADERBYTES); 00272 if (i != HEADERBYTES) 00273 { 00274 close(AVI->fdes); 00275 AVI_errno = AVI_ERR_WRITE; 00276 free(AVI); 00277 return 0; 00278 } 00279 00280 AVI->pos = HEADERBYTES; 00281 AVI->mode = AVI_MODE_WRITE; /* open for writing */ 00282 00283 //init 00284 AVI->anum = 0; 00285 AVI->aptr = 0; 00286 00287 return AVI; 00288 } 00289 00290 void AVI_set_video(avi_t *AVI, int width, int height, double fps, char *compressor) 00291 { 00292 /* may only be called if file is open for writing */ 00293 00294 if(AVI->mode==AVI_MODE_READ) return; 00295 00296 AVI->width = width; 00297 AVI->height = height; 00298 AVI->fps = fps; 00299 00300 if(strncmp(compressor, "RGB", 3)==0) { 00301 memset(AVI->compressor, 0, 4); 00302 } else { 00303 memcpy(AVI->compressor,compressor,4); 00304 } 00305 00306 AVI->compressor[4] = 0; 00307 00308 avi_update_header(AVI); 00309 } 00310 00311 void AVI_set_audio(avi_t *AVI, int channels, long rate, int bits, int format, long mp3rate) 00312 { 00313 /* may only be called if file is open for writing */ 00314 00315 if(AVI->mode==AVI_MODE_READ) return; 00316 00317 //inc audio tracks 00318 AVI->aptr=AVI->anum; 00319 ++AVI->anum; 00320 00321 if(AVI->anum > AVI_MAX_TRACKS) { 00322 fprintf(stderr, "error - only %d audio tracks supported\n", AVI_MAX_TRACKS); 00323 exit(1); 00324 } 00325 00326 AVI->track[AVI->aptr].a_chans = channels; 00327 AVI->track[AVI->aptr].a_rate = rate; 00328 AVI->track[AVI->aptr].a_bits = bits; 00329 AVI->track[AVI->aptr].a_fmt = format; 00330 AVI->track[AVI->aptr].mp3rate = mp3rate; 00331 00332 avi_update_header(AVI); 00333 } 00334 00335 #define OUT4CC(s) \ 00336 if(nhb<=HEADERBYTES-4) memcpy(AVI_header+nhb,s,4); nhb += 4 00337 00338 #define OUTLONG(n) \ 00339 if(nhb<=HEADERBYTES-4) long2str(AVI_header+nhb,n); nhb += 4 00340 00341 #define OUTSHRT(n) \ 00342 if(nhb<=HEADERBYTES-2) { \ 00343 AVI_header[nhb ] = (n )&0xff; \ 00344 AVI_header[nhb+1] = (n>>8)&0xff; \ 00345 } \ 00346 nhb += 2 00347 00348 00349 //ThOe write preliminary AVI file header: 0 frames, max vid/aud size 00350 int avi_update_header(avi_t *AVI) 00351 { 00352 int njunk, sampsize, hasIndex, ms_per_frame, frate, flag; 00353 int movi_len, hdrl_start, strl_start, j; 00354 unsigned char AVI_header[HEADERBYTES]; 00355 long nhb; 00356 00357 //assume max size 00358 movi_len = AVI_MAX_LEN - HEADERBYTES + 4; 00359 00360 //assume index will be written 00361 hasIndex=1; 00362 00363 if(AVI->fps < 0.001) { 00364 frate=0; 00365 ms_per_frame=0; 00366 } else { 00367 frate = (int) (FRAME_RATE_SCALE*AVI->fps + 0.5); 00368 ms_per_frame=(int) (1000000/AVI->fps + 0.5); 00369 } 00370 00371 /* Prepare the file header */ 00372 00373 nhb = 0; 00374 00375 /* The RIFF header */ 00376 00377 OUT4CC ("RIFF"); 00378 OUTLONG(movi_len); // assume max size 00379 OUT4CC ("AVI "); 00380 00381 /* Start the header list */ 00382 00383 OUT4CC ("LIST"); 00384 OUTLONG(0); /* Length of list in bytes, don't know yet */ 00385 hdrl_start = nhb; /* Store start position */ 00386 OUT4CC ("hdrl"); 00387 00388 /* The main AVI header */ 00389 00390 /* The Flags in AVI File header */ 00391 00392 #define AVIF_HASINDEX 0x00000010 /* Index at end of file */ 00393 #define AVIF_MUSTUSEINDEX 0x00000020 00394 #define AVIF_ISINTERLEAVED 0x00000100 00395 #define AVIF_TRUSTCKTYPE 0x00000800 /* Use CKType to find key frames */ 00396 #define AVIF_WASCAPTUREFILE 0x00010000 00397 #define AVIF_COPYRIGHTED 0x00020000 00398 00399 OUT4CC ("avih"); 00400 OUTLONG(56); /* # of bytes to follow */ 00401 OUTLONG(ms_per_frame); /* Microseconds per frame */ 00402 //ThOe ->0 00403 // OUTLONG(10000000); /* MaxBytesPerSec, I hope this will never be used */ 00404 OUTLONG(0); 00405 OUTLONG(0); /* PaddingGranularity (whatever that might be) */ 00406 /* Other sources call it 'reserved' */ 00407 flag = AVIF_ISINTERLEAVED; 00408 if(hasIndex) flag |= AVIF_HASINDEX; 00409 if(hasIndex && AVI->must_use_index) flag |= AVIF_MUSTUSEINDEX; 00410 OUTLONG(flag); /* Flags */ 00411 OUTLONG(0); // no frames yet 00412 OUTLONG(0); /* InitialFrames */ 00413 00414 OUTLONG(AVI->anum+1); 00415 00416 OUTLONG(0); /* SuggestedBufferSize */ 00417 OUTLONG(AVI->width); /* Width */ 00418 OUTLONG(AVI->height); /* Height */ 00419 /* MS calls the following 'reserved': */ 00420 OUTLONG(0); /* TimeScale: Unit used to measure time */ 00421 OUTLONG(0); /* DataRate: Data rate of playback */ 00422 OUTLONG(0); /* StartTime: Starting time of AVI data */ 00423 OUTLONG(0); /* DataLength: Size of AVI data chunk */ 00424 00425 00426 /* Start the video stream list ---------------------------------- */ 00427 00428 OUT4CC ("LIST"); 00429 OUTLONG(0); /* Length of list in bytes, don't know yet */ 00430 strl_start = nhb; /* Store start position */ 00431 OUT4CC ("strl"); 00432 00433 /* The video stream header */ 00434 00435 OUT4CC ("strh"); 00436 OUTLONG(56); /* # of bytes to follow */ 00437 OUT4CC ("vids"); /* Type */ 00438 OUT4CC (AVI->compressor); /* Handler */ 00439 OUTLONG(0); /* Flags */ 00440 OUTLONG(0); /* Reserved, MS says: wPriority, wLanguage */ 00441 OUTLONG(0); /* InitialFrames */ 00442 OUTLONG(FRAME_RATE_SCALE); /* Scale */ 00443 OUTLONG(frate); /* Rate: Rate/Scale == samples/second */ 00444 OUTLONG(0); /* Start */ 00445 OUTLONG(0); // no frames yet 00446 OUTLONG(0); /* SuggestedBufferSize */ 00447 OUTLONG(-1); /* Quality */ 00448 OUTLONG(0); /* SampleSize */ 00449 OUTLONG(0); /* Frame */ 00450 OUTLONG(0); /* Frame */ 00451 // OUTLONG(0); /* Frame */ 00452 //OUTLONG(0); /* Frame */ 00453 00454 /* The video stream format */ 00455 00456 OUT4CC ("strf"); 00457 OUTLONG(40); /* # of bytes to follow */ 00458 OUTLONG(40); /* Size */ 00459 OUTLONG(AVI->width); /* Width */ 00460 OUTLONG(AVI->height); /* Height */ 00461 OUTSHRT(1); OUTSHRT(24); /* Planes, Count */ 00462 OUT4CC (AVI->compressor); /* Compression */ 00463 // ThOe (*3) 00464 OUTLONG(AVI->width*AVI->height*3); /* SizeImage (in bytes?) */ 00465 OUTLONG(0); /* XPelsPerMeter */ 00466 OUTLONG(0); /* YPelsPerMeter */ 00467 OUTLONG(0); /* ClrUsed: Number of colors used */ 00468 OUTLONG(0); /* ClrImportant: Number of colors important */ 00469 00470 /* Finish stream list, i.e. put number of bytes in the list to proper pos */ 00471 00472 long2str(AVI_header+strl_start-4,nhb-strl_start); 00473 00474 00475 /* Start the audio stream list ---------------------------------- */ 00476 00477 for(j=0; j<AVI->anum; ++j) { 00478 00479 sampsize = avi_sampsize(AVI, j); 00480 00481 OUT4CC ("LIST"); 00482 OUTLONG(0); /* Length of list in bytes, don't know yet */ 00483 strl_start = nhb; /* Store start position */ 00484 OUT4CC ("strl"); 00485 00486 /* The audio stream header */ 00487 00488 OUT4CC ("strh"); 00489 OUTLONG(56); /* # of bytes to follow */ 00490 OUT4CC ("auds"); 00491 00492 // ----------- 00493 // ThOe 00494 OUTLONG(0); /* Format (Optionally) */ 00495 // ----------- 00496 00497 OUTLONG(0); /* Flags */ 00498 OUTLONG(0); /* Reserved, MS says: wPriority, wLanguage */ 00499 OUTLONG(0); /* InitialFrames */ 00500 00501 // ThOe /4 00502 OUTLONG(sampsize/4); /* Scale */ 00503 OUTLONG(1000*AVI->track[j].mp3rate/8); 00504 OUTLONG(0); /* Start */ 00505 OUTLONG(4*AVI->track[j].audio_bytes/sampsize); /* Length */ 00506 OUTLONG(0); /* SuggestedBufferSize */ 00507 OUTLONG(-1); /* Quality */ 00508 00509 // ThOe /4 00510 OUTLONG(sampsize/4); /* SampleSize */ 00511 00512 OUTLONG(0); /* Frame */ 00513 OUTLONG(0); /* Frame */ 00514 // OUTLONG(0); /* Frame */ 00515 //OUTLONG(0); /* Frame */ 00516 00517 /* The audio stream format */ 00518 00519 OUT4CC ("strf"); 00520 OUTLONG(16); /* # of bytes to follow */ 00521 OUTSHRT(AVI->track[j].a_fmt); /* Format */ 00522 OUTSHRT(AVI->track[j].a_chans); /* Number of channels */ 00523 OUTLONG(AVI->track[j].a_rate); /* SamplesPerSec */ 00524 // ThOe 00525 OUTLONG(1000*AVI->track[j].mp3rate/8); 00526 //ThOe (/4) 00527 00528 OUTSHRT(sampsize/4); /* BlockAlign */ 00529 00530 00531 OUTSHRT(AVI->track[j].a_bits); /* BitsPerSample */ 00532 00533 /* Finish stream list, i.e. put number of bytes in the list to proper pos */ 00534 00535 long2str(AVI_header+strl_start-4,nhb-strl_start); 00536 } 00537 00538 /* Finish header list */ 00539 00540 long2str(AVI_header+hdrl_start-4,nhb-hdrl_start); 00541 00542 00543 /* Calculate the needed amount of junk bytes, output junk */ 00544 00545 njunk = HEADERBYTES - nhb - 8 - 12; 00546 00547 /* Safety first: if njunk <= 0, somebody has played with 00548 HEADERBYTES without knowing what (s)he did. 00549 This is a fatal error */ 00550 00551 if(njunk<=0) 00552 { 00553 fprintf(stderr,"AVI_close_output_file: # of header bytes too small\n"); 00554 exit(1); 00555 } 00556 00557 OUT4CC ("JUNK"); 00558 OUTLONG(njunk); 00559 memset(AVI_header+nhb,0,njunk); 00560 00561 //2001-11-14 added id string 00562 00563 if(njunk > strlen(id_str)+8) { 00564 //sprintf(id_str, "%s-%s", PACKAGE, VERSION); 00565 //memcpy(AVI_header+nhb, id_str, strlen(id_str)); 00566 } 00567 00568 nhb += njunk; 00569 00570 /* Start the movi list */ 00571 00572 OUT4CC ("LIST"); 00573 OUTLONG(movi_len); /* Length of list in bytes */ 00574 OUT4CC ("movi"); 00575 00576 /* Output the header, truncate the file to the number of bytes 00577 actually written, report an error if someting goes wrong */ 00578 00579 if ( lseek(AVI->fdes,0,SEEK_SET)<0 || 00580 avi_write(AVI->fdes,(char *)AVI_header,HEADERBYTES)!=HEADERBYTES || 00581 lseek(AVI->fdes,AVI->pos,SEEK_SET)<0) 00582 { 00583 AVI_errno = AVI_ERR_CLOSE; 00584 return -1; 00585 } 00586 00587 return 0; 00588 } 00589 00590 /* 00591 Write the header of an AVI file and close it. 00592 returns 0 on success, -1 on write error. 00593 */ 00594 00595 static int avi_close_output_file(avi_t *AVI) 00596 { 00597 00598 int ret, njunk, sampsize, hasIndex, ms_per_frame, frate, idxerror, flag; 00599 unsigned long movi_len; 00600 int hdrl_start, strl_start, j; 00601 unsigned char AVI_header[HEADERBYTES]; 00602 long nhb; 00603 00604 #ifdef INFO_LIST 00605 long info_len; 00606 // time_t calptr; 00607 #endif 00608 00609 /* Calculate length of movi list */ 00610 00611 movi_len = AVI->pos - HEADERBYTES + 4; 00612 00613 /* Try to ouput the index entries. This may fail e.g. if no space 00614 is left on device. We will report this as an error, but we still 00615 try to write the header correctly (so that the file still may be 00616 readable in the most cases */ 00617 00618 idxerror = 0; 00619 // fprintf(stderr, "pos=%lu, index_len=%ld\n", AVI->pos, AVI->n_idx*16); 00620 ret = avi_add_chunk(AVI, (unsigned char *)"idx1", (unsigned char *)AVI->idx, AVI->n_idx*16); 00621 hasIndex = (ret==0); 00622 //fprintf(stderr, "pos=%lu, index_len=%d\n", AVI->pos, hasIndex); 00623 00624 if(ret) { 00625 idxerror = 1; 00626 AVI_errno = AVI_ERR_WRITE_INDEX; 00627 } 00628 00629 /* Calculate Microseconds per frame */ 00630 00631 if(AVI->fps < 0.001) { 00632 frate=0; 00633 ms_per_frame=0; 00634 } else { 00635 frate = (int) (FRAME_RATE_SCALE*AVI->fps + 0.5); 00636 ms_per_frame=(int) (1000000/AVI->fps + 0.5); 00637 } 00638 00639 /* Prepare the file header */ 00640 00641 nhb = 0; 00642 00643 /* The RIFF header */ 00644 00645 OUT4CC ("RIFF"); 00646 OUTLONG(AVI->pos - 8); /* # of bytes to follow */ 00647 OUT4CC ("AVI "); 00648 00649 /* Start the header list */ 00650 00651 OUT4CC ("LIST"); 00652 OUTLONG(0); /* Length of list in bytes, don't know yet */ 00653 hdrl_start = nhb; /* Store start position */ 00654 OUT4CC ("hdrl"); 00655 00656 /* The main AVI header */ 00657 00658 /* The Flags in AVI File header */ 00659 00660 #define AVIF_HASINDEX 0x00000010 /* Index at end of file */ 00661 #define AVIF_MUSTUSEINDEX 0x00000020 00662 #define AVIF_ISINTERLEAVED 0x00000100 00663 #define AVIF_TRUSTCKTYPE 0x00000800 /* Use CKType to find key frames */ 00664 #define AVIF_WASCAPTUREFILE 0x00010000 00665 #define AVIF_COPYRIGHTED 0x00020000 00666 00667 OUT4CC ("avih"); 00668 OUTLONG(56); /* # of bytes to follow */ 00669 OUTLONG(ms_per_frame); /* Microseconds per frame */ 00670 //ThOe ->0 00671 // OUTLONG(10000000); /* MaxBytesPerSec, I hope this will never be used */ 00672 OUTLONG(0); 00673 OUTLONG(0); /* PaddingGranularity (whatever that might be) */ 00674 /* Other sources call it 'reserved' */ 00675 flag = AVIF_ISINTERLEAVED; 00676 if(hasIndex) flag |= AVIF_HASINDEX; 00677 if(hasIndex && AVI->must_use_index) flag |= AVIF_MUSTUSEINDEX; 00678 OUTLONG(flag); /* Flags */ 00679 OUTLONG(AVI->video_frames); /* TotalFrames */ 00680 OUTLONG(0); /* InitialFrames */ 00681 00682 OUTLONG(AVI->anum+1); 00683 // if (AVI->track[0].audio_bytes) 00684 // { OUTLONG(2); } /* Streams */ 00685 // else 00686 // { OUTLONG(1); } /* Streams */ 00687 00688 OUTLONG(0); /* SuggestedBufferSize */ 00689 OUTLONG(AVI->width); /* Width */ 00690 OUTLONG(AVI->height); /* Height */ 00691 /* MS calls the following 'reserved': */ 00692 OUTLONG(0); /* TimeScale: Unit used to measure time */ 00693 OUTLONG(0); /* DataRate: Data rate of playback */ 00694 OUTLONG(0); /* StartTime: Starting time of AVI data */ 00695 OUTLONG(0); /* DataLength: Size of AVI data chunk */ 00696 00697 00698 /* Start the video stream list ---------------------------------- */ 00699 00700 OUT4CC ("LIST"); 00701 OUTLONG(0); /* Length of list in bytes, don't know yet */ 00702 strl_start = nhb; /* Store start position */ 00703 OUT4CC ("strl"); 00704 00705 /* The video stream header */ 00706 00707 OUT4CC ("strh"); 00708 OUTLONG(56); /* # of bytes to follow */ 00709 OUT4CC ("vids"); /* Type */ 00710 OUT4CC (AVI->compressor); /* Handler */ 00711 OUTLONG(0); /* Flags */ 00712 OUTLONG(0); /* Reserved, MS says: wPriority, wLanguage */ 00713 OUTLONG(0); /* InitialFrames */ 00714 OUTLONG(FRAME_RATE_SCALE); /* Scale */ 00715 OUTLONG(frate); /* Rate: Rate/Scale == samples/second */ 00716 OUTLONG(0); /* Start */ 00717 OUTLONG(AVI->video_frames); /* Length */ 00718 OUTLONG(0); /* SuggestedBufferSize */ 00719 OUTLONG(-1); /* Quality */ 00720 OUTLONG(0); /* SampleSize */ 00721 OUTLONG(0); /* Frame */ 00722 OUTLONG(0); /* Frame */ 00723 // OUTLONG(0); /* Frame */ 00724 //OUTLONG(0); /* Frame */ 00725 00726 /* The video stream format */ 00727 00728 OUT4CC ("strf"); 00729 OUTLONG(40); /* # of bytes to follow */ 00730 OUTLONG(40); /* Size */ 00731 OUTLONG(AVI->width); /* Width */ 00732 OUTLONG(AVI->height); /* Height */ 00733 OUTSHRT(1); OUTSHRT(24); /* Planes, Count */ 00734 OUT4CC (AVI->compressor); /* Compression */ 00735 // ThOe (*3) 00736 OUTLONG(AVI->width*AVI->height*3); /* SizeImage (in bytes?) */ 00737 OUTLONG(0); /* XPelsPerMeter */ 00738 OUTLONG(0); /* YPelsPerMeter */ 00739 OUTLONG(0); /* ClrUsed: Number of colors used */ 00740 OUTLONG(0); /* ClrImportant: Number of colors important */ 00741 00742 /* Finish stream list, i.e. put number of bytes in the list to proper pos */ 00743 00744 long2str(AVI_header+strl_start-4,nhb-strl_start); 00745 00746 /* Start the audio stream list ---------------------------------- */ 00747 00748 for(j=0; j<AVI->anum; ++j) { 00749 00750 //if (AVI->track[j].a_chans && AVI->track[j].audio_bytes) 00751 { 00752 00753 sampsize = avi_sampsize(AVI, j); 00754 00755 OUT4CC ("LIST"); 00756 OUTLONG(0); /* Length of list in bytes, don't know yet */ 00757 strl_start = nhb; /* Store start position */ 00758 OUT4CC ("strl"); 00759 00760 /* The audio stream header */ 00761 00762 OUT4CC ("strh"); 00763 OUTLONG(56); /* # of bytes to follow */ 00764 OUT4CC ("auds"); 00765 00766 // ----------- 00767 // ThOe 00768 OUTLONG(0); /* Format (Optionally) */ 00769 // ----------- 00770 00771 OUTLONG(0); /* Flags */ 00772 OUTLONG(0); /* Reserved, MS says: wPriority, wLanguage */ 00773 OUTLONG(0); /* InitialFrames */ 00774 00775 // ThOe /4 00776 OUTLONG(sampsize/4); /* Scale */ 00777 OUTLONG(1000*AVI->track[j].mp3rate/8); 00778 OUTLONG(0); /* Start */ 00779 OUTLONG(4*AVI->track[j].audio_bytes/sampsize); /* Length */ 00780 OUTLONG(0); /* SuggestedBufferSize */ 00781 OUTLONG(-1); /* Quality */ 00782 00783 // ThOe /4 00784 OUTLONG(sampsize/4); /* SampleSize */ 00785 00786 OUTLONG(0); /* Frame */ 00787 OUTLONG(0); /* Frame */ 00788 // OUTLONG(0); /* Frame */ 00789 //OUTLONG(0); /* Frame */ 00790 00791 /* The audio stream format */ 00792 00793 OUT4CC ("strf"); 00794 OUTLONG(16); /* # of bytes to follow */ 00795 OUTSHRT(AVI->track[j].a_fmt); /* Format */ 00796 OUTSHRT(AVI->track[j].a_chans); /* Number of channels */ 00797 OUTLONG(AVI->track[j].a_rate); /* SamplesPerSec */ 00798 // ThOe 00799 OUTLONG(1000*AVI->track[j].mp3rate/8); 00800 //ThOe (/4) 00801 00802 OUTSHRT(sampsize/4); /* BlockAlign */ 00803 00804 00805 OUTSHRT(AVI->track[j].a_bits); /* BitsPerSample */ 00806 00807 /* Finish stream list, i.e. put number of bytes in the list to proper pos */ 00808 } 00809 long2str(AVI_header+strl_start-4,nhb-strl_start); 00810 } 00811 00812 /* Finish header list */ 00813 00814 long2str(AVI_header+hdrl_start-4,nhb-hdrl_start); 00815 00816 00817 // add INFO list --- (0.6.0pre4) 00818 00819 #ifdef INFO_LIST 00820 OUT4CC ("LIST"); 00821 00822 //FIXME 00823 info_len = MAX_INFO_STRLEN + 12; 00824 OUTLONG(info_len); 00825 OUT4CC ("INFO"); 00826 00827 // OUT4CC ("INAM"); 00828 // OUTLONG(MAX_INFO_STRLEN); 00829 00830 // sprintf(id_str, "\t"); 00831 // memset(AVI_header+nhb, 0, MAX_INFO_STRLEN); 00832 // memcpy(AVI_header+nhb, id_str, strlen(id_str)); 00833 // nhb += MAX_INFO_STRLEN; 00834 00835 OUT4CC ("ISFT"); 00836 OUTLONG(MAX_INFO_STRLEN); 00837 00838 //sprintf(id_str, "%s-%s", PACKAGE, VERSION); 00839 memset(AVI_header+nhb, 0, MAX_INFO_STRLEN); 00840 //memcpy(AVI_header+nhb, id_str, strlen(id_str)); 00841 nhb += MAX_INFO_STRLEN; 00842 00843 // OUT4CC ("ICMT"); 00844 // OUTLONG(MAX_INFO_STRLEN); 00845 00846 // calptr=time(NULL); 00847 // sprintf(id_str, "\t%s %s", ctime(&calptr), ""); 00848 // memset(AVI_header+nhb, 0, MAX_INFO_STRLEN); 00849 // memcpy(AVI_header+nhb, id_str, 25); 00850 // nhb += MAX_INFO_STRLEN; 00851 #endif 00852 00853 // ---------------------------- 00854 00855 /* Calculate the needed amount of junk bytes, output junk */ 00856 00857 njunk = HEADERBYTES - nhb - 8 - 12; 00858 00859 /* Safety first: if njunk <= 0, somebody has played with 00860 HEADERBYTES without knowing what (s)he did. 00861 This is a fatal error */ 00862 00863 if(njunk<=0) 00864 { 00865 fprintf(stderr,"AVI_close_output_file: # of header bytes too small\n"); 00866 exit(1); 00867 } 00868 00869 OUT4CC ("JUNK"); 00870 OUTLONG(njunk); 00871 memset(AVI_header+nhb,0,njunk); 00872 00873 nhb += njunk; 00874 00875 /* Start the movi list */ 00876 00877 OUT4CC ("LIST"); 00878 OUTLONG(movi_len); /* Length of list in bytes */ 00879 OUT4CC ("movi"); 00880 00881 /* Output the header, truncate the file to the number of bytes 00882 actually written, report an error if someting goes wrong */ 00883 00884 if ( lseek(AVI->fdes,0,SEEK_SET)<0 || 00885 avi_write(AVI->fdes,(char *)AVI_header,HEADERBYTES)!=HEADERBYTES || 00886 ftruncate(AVI->fdes,AVI->pos)<0 ) 00887 { 00888 AVI_errno = AVI_ERR_CLOSE; 00889 return -1; 00890 } 00891 00892 if(idxerror) return -1; 00893 00894 return 0; 00895 } 00896 00897 /* 00898 AVI_write_data: 00899 Add video or audio data to the file; 00900 00901 Return values: 00902 0 No error; 00903 -1 Error, AVI_errno is set appropriatly; 00904 00905 */ 00906 00907 static int avi_write_data(avi_t *AVI, char *data, unsigned long length, int audio, int keyframe) 00908 { 00909 int n; 00910 00911 unsigned char astr[5]; 00912 00913 /* Check for maximum file length */ 00914 00915 if ( (AVI->pos + 8 + length + 8 + (AVI->n_idx+1)*16) > AVI_MAX_LEN ) { 00916 AVI_errno = AVI_ERR_SIZELIM; 00917 return -1; 00918 } 00919 00920 /* Add index entry */ 00921 00922 //set tag for current audio track 00923 sprintf((char *)astr, "0%1dwb", (int)(AVI->aptr+1)); 00924 00925 if(audio) 00926 n = avi_add_index_entry(AVI,astr,0x00,AVI->pos,length); 00927 else 00928 n = avi_add_index_entry(AVI,(unsigned char *)"00db",((keyframe)?0x10:0x0),AVI->pos,length); 00929 00930 if(n) return -1; 00931 00932 /* Output tag and data */ 00933 00934 if(audio) 00935 n = avi_add_chunk(AVI,astr,(unsigned char *)data,length); 00936 else 00937 n = avi_add_chunk(AVI,(unsigned char *)"00db",(unsigned char *)data,length); 00938 00939 if (n) return -1; 00940 00941 return 0; 00942 } 00943 00944 int AVI_write_frame(avi_t *AVI, char *data, long bytes, int keyframe) 00945 { 00946 unsigned long pos; 00947 00948 if(AVI->mode==AVI_MODE_READ) { AVI_errno = AVI_ERR_NOT_PERM; return -1; } 00949 00950 pos = AVI->pos; 00951 00952 if(avi_write_data(AVI,data,bytes,0,keyframe)) return -1; 00953 00954 AVI->last_pos = pos; 00955 AVI->last_len = bytes; 00956 AVI->video_frames++; 00957 return 0; 00958 } 00959 00960 int AVI_dup_frame(avi_t *AVI) 00961 { 00962 if(AVI->mode==AVI_MODE_READ) { AVI_errno = AVI_ERR_NOT_PERM; return -1; } 00963 00964 if(AVI->last_pos==0) return 0; /* No previous real frame */ 00965 if(avi_add_index_entry(AVI,(unsigned char *)"00db",0x10,AVI->last_pos,AVI->last_len)) return -1; 00966 AVI->video_frames++; 00967 AVI->must_use_index = 1; 00968 return 0; 00969 } 00970 00971 int AVI_write_audio(avi_t *AVI, char *data, long bytes) 00972 { 00973 if(AVI->mode==AVI_MODE_READ) { AVI_errno = AVI_ERR_NOT_PERM; return -1; } 00974 00975 if( avi_write_data(AVI,data,bytes,1,0) ) return -1; 00976 AVI->track[AVI->aptr].audio_bytes += bytes; 00977 return 0; 00978 } 00979 00980 00981 int AVI_append_audio(avi_t *AVI, char *data, long bytes) 00982 { 00983 00984 long i, length, pos; 00985 unsigned char c[4]; 00986 00987 if(AVI->mode==AVI_MODE_READ) { AVI_errno = AVI_ERR_NOT_PERM; return -1; } 00988 00989 // update last index entry: 00990 00991 --AVI->n_idx; 00992 length = str2ulong(AVI->idx[AVI->n_idx]+12); 00993 pos = str2ulong(AVI->idx[AVI->n_idx]+8); 00994 00995 //update; 00996 long2str(AVI->idx[AVI->n_idx]+12,length+bytes); 00997 00998 ++AVI->n_idx; 00999 01000 AVI->track[AVI->aptr].audio_bytes += bytes; 01001 01002 //update chunk header 01003 lseek(AVI->fdes, pos+4, SEEK_SET); 01004 long2str(c, length+bytes); 01005 avi_write(AVI->fdes, (char *)c, 4); 01006 01007 lseek(AVI->fdes, pos+8+length, SEEK_SET); 01008 01009 i=PAD_EVEN(length + bytes); 01010 01011 bytes = i - length; 01012 avi_write(AVI->fdes, data, bytes); 01013 AVI->pos = pos + 8 + i; 01014 01015 return 0; 01016 } 01017 01018 01019 long AVI_bytes_remain(avi_t *AVI) 01020 { 01021 if(AVI->mode==AVI_MODE_READ) return 0; 01022 01023 return ( AVI_MAX_LEN - (AVI->pos + 8 + 16*AVI->n_idx)); 01024 } 01025 01026 long AVI_bytes_written(avi_t *AVI) 01027 { 01028 if(AVI->mode==AVI_MODE_READ) return 0; 01029 01030 return (AVI->pos + 8 + 16*AVI->n_idx); 01031 } 01032 01033 int AVI_set_audio_track(avi_t *AVI, int track) 01034 { 01035 01036 if(track < 0 || track + 1 > AVI->anum) return(-1); 01037 01038 //this info is not written to file anyway 01039 AVI->aptr=track; 01040 return 0; 01041 } 01042 01043 int AVI_get_audio_track(avi_t *AVI) 01044 { 01045 return(AVI->aptr); 01046 } 01047 01048 01049 /******************************************************************* 01050 * * 01051 * Utilities for reading video and audio from an AVI File * 01052 * * 01053 *******************************************************************/ 01054 01055 int AVI_close(avi_t *AVI) 01056 { 01057 int ret, i; 01058 01059 /* If the file was open for writing, the header and index still have 01060 to be written */ 01061 01062 if(AVI->mode == AVI_MODE_WRITE) 01063 ret = avi_close_output_file(AVI); 01064 else 01065 ret = 0; 01066 01067 /* Even if there happened an error, we first clean up */ 01068 01069 close(AVI->fdes); 01070 if(AVI->idx) free(AVI->idx); 01071 if(AVI->video_index) free(AVI->video_index); 01072 //FIXME 01073 //if(AVI->audio_index) free(AVI->audio_index); 01074 if (AVI->bitmap_info_header) 01075 free(AVI->bitmap_info_header); 01076 for (i = 0; i < AVI->anum; i++) { 01077 if (AVI->wave_format_ex[i]) 01078 free(AVI->wave_format_ex[i]); 01079 if (AVI->track[i].audio_chunks) 01080 free(AVI->track[i].audio_index); 01081 } 01082 free(AVI); 01083 01084 return ret; 01085 } 01086 01087 01088 #define ERR_EXIT(x) \ 01089 { \ 01090 AVI_close(AVI); \ 01091 AVI_errno = x; \ 01092 return 0; \ 01093 } 01094 01095 avi_t *AVI_open_input_file(const char *filename, int getIndex) 01096 { 01097 avi_t *AVI=NULL; 01098 01099 /* Create avi_t structure */ 01100 01101 AVI = (avi_t *) malloc(sizeof(avi_t)); 01102 if(AVI==NULL) 01103 { 01104 AVI_errno = AVI_ERR_NO_MEM; 01105 return 0; 01106 } 01107 memset((void *)AVI,0,sizeof(avi_t)); 01108 01109 AVI->mode = AVI_MODE_READ; /* open for reading */ 01110 01111 /* Open the file */ 01112 01113 #ifdef WIN32 01114 AVI->fdes = open(filename,O_RDONLY|O_BINARY); 01115 #else 01116 AVI->fdes = open(filename,O_RDONLY); 01117 #endif 01118 if(AVI->fdes < 0) 01119 { 01120 AVI_errno = AVI_ERR_OPEN; 01121 free(AVI); 01122 return 0; 01123 } 01124 01125 avi_parse_input_file(AVI, getIndex); 01126 01127 AVI->aptr=0; //reset 01128 01129 return AVI; 01130 } 01131 01132 avi_t *AVI_open_fd(int fd, int getIndex) 01133 { 01134 avi_t *AVI=NULL; 01135 01136 /* Create avi_t structure */ 01137 01138 AVI = (avi_t *) malloc(sizeof(avi_t)); 01139 if(AVI==NULL) 01140 { 01141 AVI_errno = AVI_ERR_NO_MEM; 01142 return 0; 01143 } 01144 memset((void *)AVI,0,sizeof(avi_t)); 01145 01146 AVI->mode = AVI_MODE_READ; /* open for reading */ 01147 01148 // file alread open 01149 AVI->fdes = fd; 01150 01151 avi_parse_input_file(AVI, getIndex); 01152 01153 AVI->aptr=0; //reset 01154 01155 return AVI; 01156 } 01157 01158 int avi_parse_input_file(avi_t *AVI, int getIndex) 01159 { 01160 long i, rate, scale, idx_type; 01161 off_t n; 01162 unsigned char *hdrl_data; 01163 long header_offset=0, hdrl_len=0; 01164 long nvi, nai[AVI_MAX_TRACKS], ioff; 01165 long tot[AVI_MAX_TRACKS]; 01166 int j; 01167 int lasttag = 0; 01168 int vids_strh_seen = 0; 01169 int vids_strf_seen = 0; 01170 int auds_strh_seen = 0; 01171 // int auds_strf_seen = 0; 01172 int num_stream = 0; 01173 char data[256]; 01174 01175 /* Read first 12 bytes and check that this is an AVI file */ 01176 01177 if( avi_read(AVI->fdes,data,12) != 12 ) ERR_EXIT(AVI_ERR_READ) 01178 01179 if( strncasecmp(data ,"RIFF",4) !=0 || 01180 strncasecmp(data+8,"AVI ",4) !=0 ) ERR_EXIT(AVI_ERR_NO_AVI) 01181 01182 /* Go through the AVI file and extract the header list, 01183 the start position of the 'movi' list and an optionally 01184 present idx1 tag */ 01185 01186 hdrl_data = 0; 01187 01188 while(1) 01189 { 01190 if( avi_read(AVI->fdes,data,8) != 8 ) break; /* We assume it's EOF */ 01191 01192 n = str2ulong((unsigned char *)data+4); 01193 n = PAD_EVEN(n); 01194 01195 if(strncasecmp(data,"LIST",4) == 0) 01196 { 01197 if( avi_read(AVI->fdes,data,4) != 4 ) ERR_EXIT(AVI_ERR_READ) 01198 n -= 4; 01199 if(strncasecmp(data,"hdrl",4) == 0) 01200 { 01201 hdrl_len = n; 01202 hdrl_data = (unsigned char *) malloc(n); 01203 if(hdrl_data==0) ERR_EXIT(AVI_ERR_NO_MEM); 01204 01205 // offset of header 01206 01207 header_offset = lseek(AVI->fdes,0,SEEK_CUR); 01208 01209 if( avi_read(AVI->fdes,(char *)hdrl_data,n) != n ) ERR_EXIT(AVI_ERR_READ) 01210 } 01211 else if(strncasecmp(data,"movi",4) == 0) 01212 { 01213 AVI->movi_start = lseek(AVI->fdes,0,SEEK_CUR); 01214 lseek(AVI->fdes,n,SEEK_CUR); 01215 } 01216 else 01217 lseek(AVI->fdes,n,SEEK_CUR); 01218 } 01219 else if(strncasecmp(data,"idx1",4) == 0) 01220 { 01221 /* n must be a multiple of 16, but the reading does not 01222 break if this is not the case */ 01223 01224 AVI->n_idx = AVI->max_idx = n/16; 01225 AVI->idx = (unsigned char((*)[16]) ) malloc(n); 01226 if(AVI->idx==0) ERR_EXIT(AVI_ERR_NO_MEM) 01227 if(avi_read(AVI->fdes, (char *) AVI->idx, n) != n ) ERR_EXIT(AVI_ERR_READ) 01228 } 01229 else 01230 lseek(AVI->fdes,n,SEEK_CUR); 01231 } 01232 01233 if(!hdrl_data ) ERR_EXIT(AVI_ERR_NO_HDRL) 01234 if(!AVI->movi_start) ERR_EXIT(AVI_ERR_NO_MOVI) 01235 01236 /* Interpret the header list */ 01237 01238 for(i=0;i<hdrl_len;) 01239 { 01240 /* List tags are completly ignored */ 01241 01242 if(strncasecmp((char *)hdrl_data+i,"LIST",4)==0) { i+= 12; continue; } 01243 01244 n = str2ulong(hdrl_data+i+4); 01245 n = PAD_EVEN(n); 01246 01247 /* Interpret the tag and its args */ 01248 01249 if(strncasecmp((char *)hdrl_data+i,"strh",4)==0) 01250 { 01251 i += 8; 01252 if(strncasecmp((char *)hdrl_data+i,"vids",4) == 0 && !vids_strh_seen) 01253 { 01254 memcpy(AVI->compressor,hdrl_data+i+4,4); 01255 AVI->compressor[4] = 0; 01256 01257 // ThOe 01258 AVI->v_codech_off = header_offset + i+4; 01259 01260 scale = str2ulong(hdrl_data+i+20); 01261 rate = str2ulong(hdrl_data+i+24); 01262 if(scale!=0) AVI->fps = (double)rate/(double)scale; 01263 AVI->video_frames = str2ulong(hdrl_data+i+32); 01264 AVI->video_strn = num_stream; 01265 AVI->max_len = 0; 01266 vids_strh_seen = 1; 01267 lasttag = 1; /* vids */ 01268 } 01269 else if (strncasecmp ((char *)hdrl_data+i,"auds",4) ==0 && ! auds_strh_seen) 01270 { 01271 01272 //inc audio tracks 01273 AVI->aptr=AVI->anum; 01274 ++AVI->anum; 01275 01276 if(AVI->anum > AVI_MAX_TRACKS) { 01277 fprintf(stderr, "error - only %d audio tracks supported\n", AVI_MAX_TRACKS); 01278 return(-1); 01279 } 01280 01281 AVI->track[AVI->aptr].audio_bytes = str2ulong(hdrl_data+i+32)*avi_sampsize(AVI, 0); 01282 AVI->track[AVI->aptr].audio_strn = num_stream; 01283 // auds_strh_seen = 1; 01284 lasttag = 2; /* auds */ 01285 01286 // ThOe 01287 AVI->track[AVI->aptr].a_codech_off = header_offset + i; 01288 01289 } 01290 else 01291 lasttag = 0; 01292 num_stream++; 01293 } 01294 else if(strncasecmp((char *)hdrl_data+i,"strf",4)==0) 01295 { 01296 i += 8; 01297 if(lasttag == 1) 01298 { 01299 BITMAPINFOHEADER_avilib bih; 01300 01301 memcpy(&bih, hdrl_data + i, sizeof(BITMAPINFOHEADER_avilib)); 01302 AVI->bitmap_info_header = (BITMAPINFOHEADER_avilib *)malloc(bih.bi_size); 01303 if (AVI->bitmap_info_header != NULL) 01304 memcpy(AVI->bitmap_info_header, hdrl_data + i, bih.bi_size); 01305 01306 AVI->width = str2ulong(hdrl_data+i+4); 01307 AVI->height = str2ulong(hdrl_data+i+8); 01308 vids_strf_seen = 1; 01309 //ThOe 01310 AVI->v_codecf_off = header_offset + i+16; 01311 01312 memcpy(AVI->compressor2, hdrl_data+i+16, 4); 01313 AVI->compressor2[4] = 0; 01314 01315 } 01316 else if(lasttag == 2) 01317 { 01318 WAVEFORMATEX_avilib *wfe; 01319 char *nwfe; 01320 int wfes; 01321 01322 if ((hdrl_len - i) < sizeof(WAVEFORMATEX_avilib)) 01323 wfes = hdrl_len - i; 01324 else 01325 wfes = sizeof(WAVEFORMATEX_avilib); 01326 wfe = (WAVEFORMATEX_avilib *)malloc(sizeof(WAVEFORMATEX_avilib)); 01327 if (wfe != NULL) { 01328 memset(wfe, 0, sizeof(WAVEFORMATEX_avilib)); 01329 memcpy(wfe, hdrl_data + i, wfes); 01330 if (wfe->cb_size != 0) { 01331 nwfe = (char *)realloc(wfe, sizeof(WAVEFORMATEX_avilib) + 01332 wfe->cb_size); 01333 if (nwfe != 0) { 01334 off_t lpos = lseek(AVI->fdes, 0, SEEK_CUR); 01335 lseek(AVI->fdes, header_offset + i + sizeof(WAVEFORMATEX_avilib), 01336 SEEK_SET); 01337 wfe = (WAVEFORMATEX_avilib *)nwfe; 01338 nwfe = &nwfe[sizeof(WAVEFORMATEX_avilib)]; 01339 avi_read(AVI->fdes, nwfe, wfe->cb_size); 01340 lseek(AVI->fdes, lpos, SEEK_SET); 01341 } 01342 } 01343 AVI->wave_format_ex[AVI->aptr] = wfe; 01344 } 01345 01346 AVI->track[AVI->aptr].a_fmt = str2ushort(hdrl_data+i ); 01347 01348 //ThOe 01349 AVI->track[AVI->aptr].a_codecf_off = header_offset + i; 01350 01351 AVI->track[AVI->aptr].a_chans = str2ushort(hdrl_data+i+2); 01352 AVI->track[AVI->aptr].a_rate = str2ulong (hdrl_data+i+4); 01353 //ThOe: read mp3bitrate 01354 AVI->track[AVI->aptr].mp3rate = 8*str2ulong(hdrl_data+i+8)/1000; 01355 //:ThOe 01356 AVI->track[AVI->aptr].a_bits = str2ushort(hdrl_data+i+14); 01357 // auds_strf_seen = 1; 01358 } 01359 lasttag = 0; 01360 } 01361 else 01362 { 01363 i += 8; 01364 lasttag = 0; 01365 } 01366 01367 i += n; 01368 } 01369 01370 free(hdrl_data); 01371 01372 if(!vids_strh_seen || !vids_strf_seen) ERR_EXIT(AVI_ERR_NO_VIDS) 01373 01374 AVI->video_tag[0] = AVI->video_strn/10 + '0'; 01375 AVI->video_tag[1] = AVI->video_strn%10 + '0'; 01376 AVI->video_tag[2] = 'd'; 01377 AVI->video_tag[3] = 'b'; 01378 01379 /* Audio tag is set to "99wb" if no audio present */ 01380 if(!AVI->track[0].a_chans) AVI->track[0].audio_strn = 99; 01381 01382 for(j=0; j<AVI->anum; ++j) { 01383 AVI->track[j].audio_tag[0] = (j+1)/10 + '0'; 01384 AVI->track[j].audio_tag[1] = (j+1)%10 + '0'; 01385 AVI->track[j].audio_tag[2] = 'w'; 01386 AVI->track[j].audio_tag[3] = 'b'; 01387 } 01388 01389 lseek(AVI->fdes,AVI->movi_start,SEEK_SET); 01390 01391 /* get index if wanted */ 01392 01393 if(!getIndex) return(0); 01394 01395 /* if the file has an idx1, check if this is relative 01396 to the start of the file or to the start of the movi list */ 01397 01398 idx_type = 0; 01399 01400 if(AVI->idx) 01401 { 01402 off_t pos, len; 01403 01404 /* Search the first videoframe in the idx1 and look where 01405 it is in the file */ 01406 01407 for(i=0;i<AVI->n_idx;i++) 01408 if( strncasecmp((char *)AVI->idx[i],(char *)AVI->video_tag,3)==0 ) break; 01409 if(i>=AVI->n_idx) ERR_EXIT(AVI_ERR_NO_VIDS) 01410 01411 pos = str2ulong(AVI->idx[i]+ 8); 01412 len = str2ulong(AVI->idx[i]+12); 01413 01414 lseek(AVI->fdes,pos,SEEK_SET); 01415 if(avi_read(AVI->fdes,data,8)!=8) ERR_EXIT(AVI_ERR_READ) 01416 if( strncasecmp(data,(char *)AVI->idx[i],4)==0 && str2ulong((unsigned char *)data+4)==len ) 01417 { 01418 idx_type = 1; /* Index from start of file */ 01419 } 01420 else 01421 { 01422 lseek(AVI->fdes,pos+AVI->movi_start-4,SEEK_SET); 01423 if(avi_read(AVI->fdes,data,8)!=8) ERR_EXIT(AVI_ERR_READ) 01424 if( strncasecmp(data,(char *)AVI->idx[i],4)==0 && str2ulong((unsigned char *)data+4)==len ) 01425 { 01426 idx_type = 2; /* Index from start of movi list */ 01427 } 01428 } 01429 /* idx_type remains 0 if neither of the two tests above succeeds */ 01430 } 01431 01432 if(idx_type == 0) 01433 { 01434 /* we must search through the file to get the index */ 01435 01436 lseek(AVI->fdes, AVI->movi_start, SEEK_SET); 01437 01438 AVI->n_idx = 0; 01439 01440 while(1) 01441 { 01442 if( avi_read(AVI->fdes,data,8) != 8 ) break; 01443 n = str2ulong((unsigned char *)data+4); 01444 01445 /* The movi list may contain sub-lists, ignore them */ 01446 01447 if(strncasecmp(data,"LIST",4)==0) 01448 { 01449 lseek(AVI->fdes,4,SEEK_CUR); 01450 continue; 01451 } 01452 01453 /* Check if we got a tag ##db, ##dc or ##wb */ 01454 01455 if( ( (data[2]=='d' || data[2]=='D') && 01456 (data[3]=='b' || data[3]=='B' || data[3]=='c' || data[3]=='C') ) 01457 || ( (data[2]=='w' || data[2]=='W') && 01458 (data[3]=='b' || data[3]=='B') ) ) 01459 { 01460 avi_add_index_entry(AVI,(unsigned char *)data,0,lseek(AVI->fdes,0,SEEK_CUR)-8,n); 01461 } 01462 01463 lseek(AVI->fdes,PAD_EVEN(n),SEEK_CUR); 01464 } 01465 idx_type = 1; 01466 } 01467 01468 /* Now generate the video index and audio index arrays */ 01469 01470 nvi = 0; 01471 for(j=0; j<AVI->anum; ++j) nai[j] = 0; 01472 01473 for(i=0;i<AVI->n_idx;i++) { 01474 01475 if(strncasecmp((char *)AVI->idx[i],AVI->video_tag,3) == 0) nvi++; 01476 01477 for(j=0; j<AVI->anum; ++j) if(strncasecmp((char *)AVI->idx[i], AVI->track[j].audio_tag,4) == 0) nai[j]++; 01478 } 01479 01480 AVI->video_frames = nvi; 01481 for(j=0; j<AVI->anum; ++j) AVI->track[j].audio_chunks = nai[j]; 01482 01483 // fprintf(stderr, "chunks = %ld %d %s\n", AVI->track[0].audio_chunks, AVI->anum, AVI->track[0].audio_tag); 01484 01485 if(AVI->video_frames==0) ERR_EXIT(AVI_ERR_NO_VIDS); 01486 AVI->video_index = (video_index_entry *) malloc(nvi*sizeof(video_index_entry)); 01487 if(AVI->video_index==0) ERR_EXIT(AVI_ERR_NO_MEM); 01488 01489 for(j=0; j<AVI->anum; ++j) { 01490 if(AVI->track[j].audio_chunks) { 01491 AVI->track[j].audio_index = (audio_index_entry *) malloc((nai[j]+1)*sizeof(audio_index_entry)); 01492 memset(AVI->track[j].audio_index, 0, (nai[j]+1)*(sizeof(audio_index_entry))); 01493 if(AVI->track[j].audio_index==0) ERR_EXIT(AVI_ERR_NO_MEM); 01494 } 01495 } 01496 01497 nvi = 0; 01498 for(j=0; j<AVI->anum; ++j) nai[j] = tot[j] = 0; 01499 01500 ioff = idx_type == 1 ? 8 : AVI->movi_start+4; 01501 01502 for(i=0;i<AVI->n_idx;i++) { 01503 01504 //video 01505 if(strncasecmp((char *)AVI->idx[i],AVI->video_tag,3) == 0) { 01506 AVI->video_index[nvi].key = str2ulong(AVI->idx[i]+ 4); 01507 AVI->video_index[nvi].pos = str2ulong(AVI->idx[i]+ 8)+ioff; 01508 AVI->video_index[nvi].len = str2ulong(AVI->idx[i]+12); 01509 nvi++; 01510 } 01511 01512 //audio 01513 for(j=0; j<AVI->anum; ++j) { 01514 01515 if(strncasecmp((char *)AVI->idx[i],AVI->track[j].audio_tag,4) == 0) { 01516 AVI->track[j].audio_index[nai[j]].pos = str2ulong(AVI->idx[i]+ 8)+ioff; 01517 AVI->track[j].audio_index[nai[j]].len = str2ulong(AVI->idx[i]+12); 01518 AVI->track[j].audio_index[nai[j]].tot = tot[j]; 01519 tot[j] += AVI->track[j].audio_index[nai[j]].len; 01520 nai[j]++; 01521 } 01522 } 01523 } 01524 01525 01526 for(j=0; j<AVI->anum; ++j) AVI->track[j].audio_bytes = tot[j]; 01527 01528 /* Reposition the file */ 01529 01530 lseek(AVI->fdes,AVI->movi_start,SEEK_SET); 01531 AVI->video_pos = 0; 01532 01533 return(0); 01534 } 01535 01536 long AVI_video_frames(avi_t *AVI) 01537 { 01538 return AVI->video_frames; 01539 } 01540 int AVI_video_width(avi_t *AVI) 01541 { 01542 return AVI->width; 01543 } 01544 int AVI_video_height(avi_t *AVI) 01545 { 01546 return AVI->height; 01547 } 01548 double AVI_frame_rate(avi_t *AVI) 01549 { 01550 return AVI->fps; 01551 } 01552 char* AVI_video_compressor(avi_t *AVI) 01553 { 01554 return AVI->compressor2; 01555 } 01556 01557 long AVI_max_video_chunk(avi_t *AVI) 01558 { 01559 return AVI->max_len; 01560 } 01561 01562 int AVI_audio_tracks(avi_t *AVI) 01563 { 01564 return(AVI->anum); 01565 } 01566 01567 int AVI_audio_channels(avi_t *AVI) 01568 { 01569 return AVI->track[AVI->aptr].a_chans; 01570 } 01571 01572 long AVI_audio_mp3rate(avi_t *AVI) 01573 { 01574 return AVI->track[AVI->aptr].mp3rate; 01575 } 01576 01577 int AVI_audio_bits(avi_t *AVI) 01578 { 01579 return AVI->track[AVI->aptr].a_bits; 01580 } 01581 01582 int AVI_audio_format(avi_t *AVI) 01583 { 01584 return AVI->track[AVI->aptr].a_fmt; 01585 } 01586 01587 long AVI_audio_rate(avi_t *AVI) 01588 { 01589 return AVI->track[AVI->aptr].a_rate; 01590 } 01591 01592 long AVI_audio_bytes(avi_t *AVI) 01593 { 01594 return AVI->track[AVI->aptr].audio_bytes; 01595 } 01596 01597 long AVI_audio_chunks(avi_t *AVI) 01598 { 01599 return AVI->track[AVI->aptr].audio_chunks; 01600 } 01601 01602 long AVI_audio_codech_offset(avi_t *AVI) 01603 { 01604 return AVI->track[AVI->aptr].a_codech_off; 01605 } 01606 01607 long AVI_audio_codecf_offset(avi_t *AVI) 01608 { 01609 return AVI->track[AVI->aptr].a_codecf_off; 01610 } 01611 01612 long AVI_video_codech_offset(avi_t *AVI) 01613 { 01614 return AVI->v_codech_off; 01615 } 01616 01617 long AVI_video_codecf_offset(avi_t *AVI) 01618 { 01619 return AVI->v_codecf_off; 01620 } 01621 01622 long AVI_frame_size(avi_t *AVI, long frame) 01623 { 01624 if(AVI->mode==AVI_MODE_WRITE) { AVI_errno = AVI_ERR_NOT_PERM; return -1; } 01625 if(!AVI->video_index) { AVI_errno = AVI_ERR_NO_IDX; return -1; } 01626 01627 if(frame < 0 || frame >= AVI->video_frames) return 0; 01628 return(AVI->video_index[frame].len); 01629 } 01630 01631 long AVI_audio_size(avi_t *AVI, long frame) 01632 { 01633 if(AVI->mode==AVI_MODE_WRITE) { AVI_errno = AVI_ERR_NOT_PERM; return -1; } 01634 if(!AVI->track[AVI->aptr].audio_index) { AVI_errno = AVI_ERR_NO_IDX; return -1; } 01635 01636 if(frame < 0 || frame >= AVI->track[AVI->aptr].audio_chunks) return 0; 01637 return(AVI->track[AVI->aptr].audio_index[frame].len); 01638 } 01639 01640 long AVI_get_video_position(avi_t *AVI, long frame) 01641 { 01642 if(AVI->mode==AVI_MODE_WRITE) { AVI_errno = AVI_ERR_NOT_PERM; return -1; } 01643 if(!AVI->video_index) { AVI_errno = AVI_ERR_NO_IDX; return -1; } 01644 01645 if(frame < 0 || frame >= AVI->video_frames) return 0; 01646 return(AVI->video_index[frame].pos); 01647 } 01648 01649 01650 int AVI_seek_start(avi_t *AVI) 01651 { 01652 if(AVI->mode==AVI_MODE_WRITE) { AVI_errno = AVI_ERR_NOT_PERM; return -1; } 01653 01654 lseek(AVI->fdes,AVI->movi_start,SEEK_SET); 01655 AVI->video_pos = 0; 01656 return 0; 01657 } 01658 01659 int AVI_set_video_position(avi_t *AVI, long frame) 01660 { 01661 if(AVI->mode==AVI_MODE_WRITE) { AVI_errno = AVI_ERR_NOT_PERM; return -1; } 01662 if(!AVI->video_index) { AVI_errno = AVI_ERR_NO_IDX; return -1; } 01663 01664 if (frame < 0 ) frame = 0; 01665 AVI->video_pos = frame; 01666 return 0; 01667 } 01668 01669 int AVI_set_audio_bitrate(avi_t *AVI, long bitrate) 01670 { 01671 if(AVI->mode==AVI_MODE_READ) { AVI_errno = AVI_ERR_NOT_PERM; return -1; } 01672 01673 AVI->track[AVI->aptr].mp3rate = bitrate; 01674 return 0; 01675 } 01676 01677 01678 long AVI_read_frame(avi_t *AVI, char *vidbuf, int *keyframe) 01679 { 01680 long n; 01681 01682 if(AVI->mode==AVI_MODE_WRITE) { AVI_errno = AVI_ERR_NOT_PERM; return -1; } 01683 if(!AVI->video_index) { AVI_errno = AVI_ERR_NO_IDX; return -1; } 01684 01685 if(AVI->video_pos < 0 || AVI->video_pos >= AVI->video_frames) return -1; 01686 n = AVI->video_index[AVI->video_pos].len; 01687 01688 *keyframe = (AVI->video_index[AVI->video_pos].key==0x10) ? 1:0; 01689 01690 lseek(AVI->fdes, AVI->video_index[AVI->video_pos].pos, SEEK_SET); 01691 01692 if (avi_read(AVI->fdes,vidbuf,n) != n) 01693 { 01694 AVI_errno = AVI_ERR_READ; 01695 return -1; 01696 } 01697 01698 AVI->video_pos++; 01699 01700 return n; 01701 } 01702 01703 int AVI_set_audio_position(avi_t *AVI, long byte) 01704 { 01705 long n0, n1, n; 01706 01707 if(AVI->mode==AVI_MODE_WRITE) { AVI_errno = AVI_ERR_NOT_PERM; return -1; } 01708 if(!AVI->track[AVI->aptr].audio_index) { AVI_errno = AVI_ERR_NO_IDX; return -1; } 01709 01710 if(byte < 0) byte = 0; 01711 01712 /* Binary search in the audio chunks */ 01713 01714 n0 = 0; 01715 n1 = AVI->track[AVI->aptr].audio_chunks; 01716 01717 while(n0<n1-1) 01718 { 01719 n = (n0+n1)/2; 01720 if(AVI->track[AVI->aptr].audio_index[n].tot>byte) 01721 n1 = n; 01722 else 01723 n0 = n; 01724 } 01725 01726 AVI->track[AVI->aptr].audio_posc = n0; 01727 AVI->track[AVI->aptr].audio_posb = byte - AVI->track[AVI->aptr].audio_index[n0].tot; 01728 01729 return 0; 01730 } 01731 01732 long AVI_read_audio(avi_t *AVI, char *audbuf, long bytes) 01733 { 01734 long nr, pos, left, todo; 01735 01736 if(AVI->mode==AVI_MODE_WRITE) { AVI_errno = AVI_ERR_NOT_PERM; return -1; } 01737 if(!AVI->track[AVI->aptr].audio_index) { AVI_errno = AVI_ERR_NO_IDX; return -1; } 01738 01739 nr = 0; /* total number of bytes read */ 01740 01741 while(bytes>0) 01742 { 01743 left = AVI->track[AVI->aptr].audio_index[AVI->track[AVI->aptr].audio_posc].len - AVI->track[AVI->aptr].audio_posb; 01744 if(left==0) 01745 { 01746 if(AVI->track[AVI->aptr].audio_posc>=AVI->track[AVI->aptr].audio_chunks-1) return nr; 01747 AVI->track[AVI->aptr].audio_posc++; 01748 AVI->track[AVI->aptr].audio_posb = 0; 01749 continue; 01750 } 01751 if(bytes<left) 01752 todo = bytes; 01753 else 01754 todo = left; 01755 pos = AVI->track[AVI->aptr].audio_index[AVI->track[AVI->aptr].audio_posc].pos + AVI->track[AVI->aptr].audio_posb; 01756 lseek(AVI->fdes, pos, SEEK_SET); 01757 if (avi_read(AVI->fdes,audbuf+nr,todo) != todo) 01758 { 01759 AVI_errno = AVI_ERR_READ; 01760 return -1; 01761 } 01762 bytes -= todo; 01763 nr += todo; 01764 AVI->track[AVI->aptr].audio_posb += todo; 01765 } 01766 01767 return nr; 01768 } 01769 01770 long AVI_read_audio_chunk(avi_t *AVI, char *audbuf) 01771 { 01772 long pos, left; 01773 01774 if(AVI->mode==AVI_MODE_WRITE) { AVI_errno = AVI_ERR_NOT_PERM; return -1; } 01775 if(!AVI->track[AVI->aptr].audio_index) { AVI_errno = AVI_ERR_NO_IDX; return -1; } 01776 01777 if (AVI->track[AVI->aptr].audio_index[AVI->track[AVI->aptr].audio_posc].len == 0) return 0; 01778 left = AVI->track[AVI->aptr].audio_index[AVI->track[AVI->aptr].audio_posc].len - AVI->track[AVI->aptr].audio_posb; 01779 01780 if (audbuf == NULL) return left; 01781 01782 if(left==0) return 0; 01783 01784 pos = AVI->track[AVI->aptr].audio_index[AVI->track[AVI->aptr].audio_posc].pos + AVI->track[AVI->aptr].audio_posb; 01785 lseek(AVI->fdes, pos, SEEK_SET); 01786 if (avi_read(AVI->fdes,audbuf,left) != left) 01787 { 01788 AVI_errno = AVI_ERR_READ; 01789 return -1; 01790 } 01791 AVI->track[AVI->aptr].audio_posc++; 01792 AVI->track[AVI->aptr].audio_posb = 0; 01793 01794 return left; 01795 } 01796 01797 /* AVI_read_data: Special routine for reading the next audio or video chunk 01798 without having an index of the file. */ 01799 01800 int AVI_read_data(avi_t *AVI, char *vidbuf, long max_vidbuf, 01801 char *audbuf, long max_audbuf, 01802 long *len) 01803 { 01804 01805 /* 01806 * Return codes: 01807 * 01808 * 1 = video data read 01809 * 2 = audio data read 01810 * 0 = reached EOF 01811 * -1 = video buffer too small 01812 * -2 = audio buffer too small 01813 */ 01814 01815 off_t n; 01816 char data[8]; 01817 01818 if(AVI->mode==AVI_MODE_WRITE) return 0; 01819 01820 while(1) 01821 { 01822 /* Read tag and length */ 01823 01824 if( avi_read(AVI->fdes,data,8) != 8 ) return 0; 01825 01826 /* if we got a list tag, ignore it */ 01827 01828 if(strncasecmp(data,"LIST",4) == 0) 01829 { 01830 lseek(AVI->fdes,4,SEEK_CUR); 01831 continue; 01832 } 01833 01834 n = PAD_EVEN(str2ulong((unsigned char *)data+4)); 01835 01836 if(strncasecmp(data,AVI->video_tag,3) == 0) 01837 { 01838 *len = n; 01839 AVI->video_pos++; 01840 if(n>max_vidbuf) 01841 { 01842 lseek(AVI->fdes,n,SEEK_CUR); 01843 return -1; 01844 } 01845 if(avi_read(AVI->fdes,vidbuf,n) != n ) return 0; 01846 return 1; 01847 } 01848 else if(strncasecmp(data,AVI->track[AVI->aptr].audio_tag,4) == 0) 01849 { 01850 *len = n; 01851 if(n>max_audbuf) 01852 { 01853 lseek(AVI->fdes,n,SEEK_CUR); 01854 return -2; 01855 } 01856 if(avi_read(AVI->fdes,audbuf,n) != n ) return 0; 01857 return 2; 01858 break; 01859 } 01860 else 01861 if(lseek(AVI->fdes,n,SEEK_CUR)<0) return 0; 01862 } 01863 } 01864 01865 /* AVI_print_error: Print most recent error (similar to perror) */ 01866 01867 char *(avi_errors[]) = 01868 { 01869 /* 0 */ "avilib - No Error", 01870 /* 1 */ "avilib - AVI file size limit reached", 01871 /* 2 */ "avilib - Error opening AVI file", 01872 /* 3 */ "avilib - Error reading from AVI file", 01873 /* 4 */ "avilib - Error writing to AVI file", 01874 /* 5 */ "avilib - Error writing index (file may still be useable)", 01875 /* 6 */ "avilib - Error closing AVI file", 01876 /* 7 */ "avilib - Operation (read/write) not permitted", 01877 /* 8 */ "avilib - Out of memory (malloc failed)", 01878 /* 9 */ "avilib - Not an AVI file", 01879 /* 10 */ "avilib - AVI file has no header list (corrupted?)", 01880 /* 11 */ "avilib - AVI file has no MOVI list (corrupted?)", 01881 /* 12 */ "avilib - AVI file has no video data", 01882 /* 13 */ "avilib - operation needs an index", 01883 /* 14 */ "avilib - Unkown Error" 01884 }; 01885 static int num_avi_errors = sizeof(avi_errors)/sizeof(char*); 01886 01887 static char error_string[4096]; 01888 01889 void AVI_print_error(char *str) 01890 { 01891 int aerrno; 01892 01893 aerrno = (AVI_errno>=0 && AVI_errno<num_avi_errors) ? AVI_errno : num_avi_errors-1; 01894 01895 fprintf(stderr,"%s: %s\n",str,avi_errors[aerrno]); 01896 01897 /* for the following errors, perror should report a more detailed reason: */ 01898 01899 if(AVI_errno == AVI_ERR_OPEN || 01900 AVI_errno == AVI_ERR_READ || 01901 AVI_errno == AVI_ERR_WRITE || 01902 AVI_errno == AVI_ERR_WRITE_INDEX || 01903 AVI_errno == AVI_ERR_CLOSE ) 01904 { 01905 perror("REASON"); 01906 } 01907 } 01908 01909 char *AVI_strerror() 01910 { 01911 int aerrno; 01912 01913 aerrno = (AVI_errno>=0 && AVI_errno<num_avi_errors) ? AVI_errno : num_avi_errors-1; 01914 01915 if(AVI_errno == AVI_ERR_OPEN || 01916 AVI_errno == AVI_ERR_READ || 01917 AVI_errno == AVI_ERR_WRITE || 01918 AVI_errno == AVI_ERR_WRITE_INDEX || 01919 AVI_errno == AVI_ERR_CLOSE ) 01920 { 01921 sprintf(error_string,"%s - %s",avi_errors[aerrno],strerror(errno)); 01922 return error_string; 01923 } 01924 else 01925 { 01926 return avi_errors[aerrno]; 01927 } 01928 } 01929 01930 uint64_t AVI_max_size() 01931 { 01932 return((uint64_t) AVI_MAX_LEN); 01933 } 01934