1 /*
2 * avilib.c
3 *
4 * Copyright (C) Thomas �streich - June 2001
5 * multiple audio track support Copyright (C) 2002 Thomas �streich
6 *
7 * Original code:
8 * Copyright (C) 1999 Rainer Johanni <Rainer@Johanni.de>
9 *
10 * This file is part of transcode, a linux video stream processing tool
11 *
12 * transcode is free software; you can redistribute it and/or modify
13 * it under the terms of the GNU General Public License as published by
14 * the Free Software Foundation; either version 2, or (at your option)
15 * any later version.
16 *
17 * transcode is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with GNU Make; see the file COPYING. If not, write to
24 * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
25 *
26 */
27
28 #include "avilib.h"
29 //#include <time.h>
30
31 #define INFO_LIST
32
33 /* The following variable indicates the kind of error */
34
35 long AVI_errno;
36
37 #define MAX_INFO_STRLEN 64
38 static char id_str[MAX_INFO_STRLEN];
39
40 #define FRAME_RATE_SCALE 1000000
41
42 #ifndef PACKAGE
43 #define PACKAGE "my"
44 #define VERSION "0.00"
45 #endif
46
47 #ifndef O_BINARY
48 /* win32 wants a binary flag to open(); this sets it to null
49 on platforms that don't have it. */
50 #define O_BINARY 0
51 #endif
52
53 /*******************************************************************
54 * *
55 * Utilities for writing an AVI File *
56 * *
57 *******************************************************************/
58
avi_read(int fd,char * buf,size_t len)59 static size_t avi_read(int fd, char *buf, size_t len)
60 {
61 size_t n = 0;
62 size_t r = 0;
63
64 while (r < len) {
65 n = read (fd, buf + r, len - r);
66
67 if (n <= 0)
68 return r;
69 r += n;
70 }
71
72 return r;
73 }
74
avi_write(int fd,char * buf,size_t len)75 static size_t avi_write (int fd, char *buf, size_t len)
76 {
77 size_t n = 0;
78 size_t r = 0;
79
80 while (r < len) {
81 n = write (fd, buf + r, len - r);
82 if (n < 0)
83 return n;
84
85 r += n;
86 }
87 return r;
88 }
89
90 /* HEADERBYTES: The number of bytes to reserve for the header */
91
92 #define HEADERBYTES 2048
93
94 /* AVI_MAX_LEN: The maximum length of an AVI file, we stay a bit below
95 the 2GB limit (Remember: 2*10^9 is smaller than 2 GB) */
96
97 #define AVI_MAX_LEN (UINT_MAX-(1<<20)*16-HEADERBYTES)
98
99 #define PAD_EVEN(x) ( ((x)+1) & ~1 )
100
101
102 /* Copy n into dst as a 4 byte, little endian number.
103 Should also work on big endian machines */
104
long2str(unsigned char * dst,int n)105 static void long2str(unsigned char *dst, int n)
106 {
107 dst[0] = (n )&0xff;
108 dst[1] = (n>> 8)&0xff;
109 dst[2] = (n>>16)&0xff;
110 dst[3] = (n>>24)&0xff;
111 }
112
113 /* Convert a string of 4 or 2 bytes to a number,
114 also working on big endian machines */
115
str2ulong(unsigned char * str)116 static unsigned long str2ulong(unsigned char *str)
117 {
118 return ( str[0] | (str[1]<<8) | (str[2]<<16) | (str[3]<<24) );
119 }
str2ushort(unsigned char * str)120 static unsigned long str2ushort(unsigned char *str)
121 {
122 return ( str[0] | (str[1]<<8) );
123 }
124
125 /* Calculate audio sample size from number of bits and number of channels.
126 This may have to be adjusted for eg. 12 bits and stereo */
127
avi_sampsize(avi_t * AVI,int j)128 static int avi_sampsize(avi_t *AVI, int j)
129 {
130 int s;
131 s = ((AVI->track[j].a_bits+7)/8)*AVI->track[j].a_chans;
132 // if(s==0) s=1; /* avoid possible zero divisions */
133 if(s<4) s=4; /* avoid possible zero divisions */
134 return s;
135 }
136
137 /* Add a chunk (=tag and data) to the AVI file,
138 returns -1 on write error, 0 on success */
139
avi_add_chunk(avi_t * AVI,unsigned char * tag,unsigned char * data,int length)140 static int avi_add_chunk(avi_t *AVI, unsigned char *tag, unsigned char *data, int length)
141 {
142 unsigned char c[8];
143
144 /* Copy tag and length int c, so that we need only 1 write system call
145 for these two values */
146
147 memcpy(c,tag,4);
148 long2str(c+4,length);
149
150 /* Output tag, length and data, restore previous position
151 if the write fails */
152
153 length = PAD_EVEN(length);
154
155 if( avi_write(AVI->fdes,(char *)c,8) != 8 ||
156 avi_write(AVI->fdes,(char *)data,length) != length )
157 {
158 lseek(AVI->fdes,AVI->pos,SEEK_SET);
159 AVI_errno = AVI_ERR_WRITE;
160 return -1;
161 }
162
163 /* Update file position */
164
165 AVI->pos += 8 + length;
166
167 //fprintf(stderr, "pos=%lu %s\n", AVI->pos, tag);
168
169 return 0;
170 }
171
avi_add_index_entry(avi_t * AVI,unsigned char * tag,long flags,unsigned long pos,unsigned long len)172 static int avi_add_index_entry(avi_t *AVI, unsigned char *tag, long flags, unsigned long pos, unsigned long len)
173 {
174 void *ptr;
175
176 if(AVI->n_idx>=AVI->max_idx) {
177 ptr = realloc((void *)AVI->idx,(AVI->max_idx+4096)*16);
178
179 if(ptr == 0) {
180 AVI_errno = AVI_ERR_NO_MEM;
181 return -1;
182 }
183 AVI->max_idx += 4096;
184 AVI->idx = (unsigned char((*)[16]) ) ptr;
185 }
186
187 /* Add index entry */
188
189 // fprintf(stderr, "INDEX %s %ld %lu %lu\n", tag, flags, pos, len);
190
191 memcpy(AVI->idx[AVI->n_idx],tag,4);
192 long2str(AVI->idx[AVI->n_idx]+ 4,flags);
193 long2str(AVI->idx[AVI->n_idx]+ 8, pos);
194 long2str(AVI->idx[AVI->n_idx]+12, len);
195
196 /* Update counter */
197
198 AVI->n_idx++;
199
200 if(len>AVI->max_len) AVI->max_len=len;
201
202 return 0;
203 }
204
205 /*
206 AVI_open_output_file: Open an AVI File and write a bunch
207 of zero bytes as space for the header.
208
209 returns a pointer to avi_t on success, a zero pointer on error
210 */
211
AVI_open_output_file(char * filename)212 avi_t* AVI_open_output_file(char * filename)
213 {
214 avi_t *AVI;
215 int i;
216
217 int mask = 0;
218
219 unsigned char AVI_header[HEADERBYTES];
220
221 /* Allocate the avi_t struct and zero it */
222
223 AVI = (avi_t *) malloc(sizeof(avi_t));
224 if(AVI==0)
225 {
226 AVI_errno = AVI_ERR_NO_MEM;
227 return 0;
228 }
229 memset((void *)AVI,0,sizeof(avi_t));
230
231 /* Since Linux needs a long time when deleting big files,
232 we do not truncate the file when we open it.
233 Instead it is truncated when the AVI file is closed */
234
235 /* mask = umask (0);
236 umask (mask);*/
237
238 AVI->fdes = open(filename, O_RDWR|O_CREAT|O_BINARY, 0644 &~ mask);
239 if (AVI->fdes < 0)
240 {
241 AVI_errno = AVI_ERR_OPEN;
242 free(AVI);
243 return 0;
244 }
245
246 /* Write out HEADERBYTES bytes, the header will go here
247 when we are finished with writing */
248
249 for (i=0;i<HEADERBYTES;i++) AVI_header[i] = 0;
250 i = avi_write(AVI->fdes,(char *)AVI_header,HEADERBYTES);
251 if (i != HEADERBYTES)
252 {
253 close(AVI->fdes);
254 AVI_errno = AVI_ERR_WRITE;
255 free(AVI);
256 return 0;
257 }
258
259 AVI->pos = HEADERBYTES;
260 AVI->mode = AVI_MODE_WRITE; /* open for writing */
261
262 //init
263 AVI->anum = 0;
264 AVI->aptr = 0;
265
266 return AVI;
267 }
268
AVI_set_video(avi_t * AVI,int width,int height,double fps,char * compressor)269 void AVI_set_video(avi_t *AVI, int width, int height, double fps, char *compressor)
270 {
271 /* may only be called if file is open for writing */
272
273 if(AVI->mode==AVI_MODE_READ) return;
274
275 AVI->width = width;
276 AVI->height = height;
277 AVI->fps = fps;
278
279 if(strncmp(compressor, "RGB", 3)==0) {
280 memset(AVI->compressor, 0, 4);
281 } else {
282 memcpy(AVI->compressor,compressor,4);
283 }
284
285 AVI->compressor[4] = 0;
286
287 avi_update_header(AVI);
288 }
289
AVI_set_audio(avi_t * AVI,int channels,long rate,int bits,int format,long mp3rate)290 void AVI_set_audio(avi_t *AVI, int channels, long rate, int bits, int format, long mp3rate)
291 {
292 /* may only be called if file is open for writing */
293
294 if(AVI->mode==AVI_MODE_READ) return;
295
296 //inc audio tracks
297 AVI->aptr=AVI->anum;
298 ++AVI->anum;
299
300 if(AVI->anum > AVI_MAX_TRACKS) {
301 fprintf(stderr, "error - only %d audio tracks supported\n", AVI_MAX_TRACKS);
302 exit(1);
303 }
304
305 AVI->track[AVI->aptr].a_chans = channels;
306 AVI->track[AVI->aptr].a_rate = rate;
307 AVI->track[AVI->aptr].a_bits = bits;
308 AVI->track[AVI->aptr].a_fmt = format;
309 AVI->track[AVI->aptr].mp3rate = mp3rate;
310
311 avi_update_header(AVI);
312 }
313
314 #define OUT4CC(s) \
315 if(nhb<=HEADERBYTES-4) memcpy(AVI_header+nhb,s,4); nhb += 4
316
317 #define OUTLONG(n) \
318 if(nhb<=HEADERBYTES-4) long2str(AVI_header+nhb,n); nhb += 4
319
320 #define OUTSHRT(n) \
321 if(nhb<=HEADERBYTES-2) { \
322 AVI_header[nhb ] = (n )&0xff; \
323 AVI_header[nhb+1] = (n>>8)&0xff; \
324 } \
325 nhb += 2
326
327
328 //ThOe write preliminary AVI file header: 0 frames, max vid/aud size
avi_update_header(avi_t * AVI)329 int avi_update_header(avi_t *AVI)
330 {
331 int njunk, sampsize, hasIndex, ms_per_frame, frate, flag;
332 int movi_len, hdrl_start, strl_start, j;
333 unsigned char AVI_header[HEADERBYTES];
334 long nhb;
335
336 //assume max size
337 movi_len = AVI_MAX_LEN - HEADERBYTES + 4;
338
339 //assume index will be written
340 hasIndex=1;
341
342 if(AVI->fps < 0.001) {
343 frate=0;
344 ms_per_frame=0;
345 } else {
346 frate = (int) (FRAME_RATE_SCALE*AVI->fps + 0.5);
347 ms_per_frame=(int) (1000000/AVI->fps + 0.5);
348 }
349
350 /* Prepare the file header */
351
352 nhb = 0;
353
354 /* The RIFF header */
355
356 OUT4CC ("RIFF");
357 OUTLONG(movi_len); // assume max size
358 OUT4CC ("AVI ");
359
360 /* Start the header list */
361
362 OUT4CC ("LIST");
363 OUTLONG(0); /* Length of list in bytes, don't know yet */
364 hdrl_start = nhb; /* Store start position */
365 OUT4CC ("hdrl");
366
367 /* The main AVI header */
368
369 /* The Flags in AVI File header */
370
371 #define AVIF_HASINDEX 0x00000010 /* Index at end of file */
372 #define AVIF_MUSTUSEINDEX 0x00000020
373 #define AVIF_ISINTERLEAVED 0x00000100
374 #define AVIF_TRUSTCKTYPE 0x00000800 /* Use CKType to find key frames */
375 #define AVIF_WASCAPTUREFILE 0x00010000
376 #define AVIF_COPYRIGHTED 0x00020000
377
378 OUT4CC ("avih");
379 OUTLONG(56); /* # of bytes to follow */
380 OUTLONG(ms_per_frame); /* Microseconds per frame */
381 //ThOe ->0
382 // OUTLONG(10000000); /* MaxBytesPerSec, I hope this will never be used */
383 OUTLONG(0);
384 OUTLONG(0); /* PaddingGranularity (whatever that might be) */
385 /* Other sources call it 'reserved' */
386 flag = AVIF_ISINTERLEAVED;
387 if(hasIndex) flag |= AVIF_HASINDEX;
388 if(hasIndex && AVI->must_use_index) flag |= AVIF_MUSTUSEINDEX;
389 OUTLONG(flag); /* Flags */
390 OUTLONG(0); // no frames yet
391 OUTLONG(0); /* InitialFrames */
392
393 OUTLONG(AVI->anum+1);
394
395 OUTLONG(0); /* SuggestedBufferSize */
396 OUTLONG(AVI->width); /* Width */
397 OUTLONG(AVI->height); /* Height */
398 /* MS calls the following 'reserved': */
399 OUTLONG(0); /* TimeScale: Unit used to measure time */
400 OUTLONG(0); /* DataRate: Data rate of playback */
401 OUTLONG(0); /* StartTime: Starting time of AVI data */
402 OUTLONG(0); /* DataLength: Size of AVI data chunk */
403
404
405 /* Start the video stream list ---------------------------------- */
406
407 OUT4CC ("LIST");
408 OUTLONG(0); /* Length of list in bytes, don't know yet */
409 strl_start = nhb; /* Store start position */
410 OUT4CC ("strl");
411
412 /* The video stream header */
413
414 OUT4CC ("strh");
415 OUTLONG(56); /* # of bytes to follow */
416 OUT4CC ("vids"); /* Type */
417 OUT4CC (AVI->compressor); /* Handler */
418 OUTLONG(0); /* Flags */
419 OUTLONG(0); /* Reserved, MS says: wPriority, wLanguage */
420 OUTLONG(0); /* InitialFrames */
421 OUTLONG(FRAME_RATE_SCALE); /* Scale */
422 OUTLONG(frate); /* Rate: Rate/Scale == samples/second */
423 OUTLONG(0); /* Start */
424 OUTLONG(0); // no frames yet
425 OUTLONG(0); /* SuggestedBufferSize */
426 OUTLONG(-1); /* Quality */
427 OUTLONG(0); /* SampleSize */
428 OUTLONG(0); /* Frame */
429 OUTLONG(0); /* Frame */
430 // OUTLONG(0); /* Frame */
431 //OUTLONG(0); /* Frame */
432
433 /* The video stream format */
434
435 OUT4CC ("strf");
436 OUTLONG(40); /* # of bytes to follow */
437 OUTLONG(40); /* Size */
438 OUTLONG(AVI->width); /* Width */
439 OUTLONG(AVI->height); /* Height */
440 OUTSHRT(1); OUTSHRT(24); /* Planes, Count */
441 OUT4CC (AVI->compressor); /* Compression */
442 // ThOe (*3)
443 OUTLONG(AVI->width*AVI->height*3); /* SizeImage (in bytes?) */
444 OUTLONG(0); /* XPelsPerMeter */
445 OUTLONG(0); /* YPelsPerMeter */
446 OUTLONG(0); /* ClrUsed: Number of colors used */
447 OUTLONG(0); /* ClrImportant: Number of colors important */
448
449 /* Finish stream list, i.e. put number of bytes in the list to proper pos */
450
451 long2str(AVI_header+strl_start-4,nhb-strl_start);
452
453
454 /* Start the audio stream list ---------------------------------- */
455
456 for(j=0; j<AVI->anum; ++j) {
457
458 sampsize = avi_sampsize(AVI, j);
459
460 OUT4CC ("LIST");
461 OUTLONG(0); /* Length of list in bytes, don't know yet */
462 strl_start = nhb; /* Store start position */
463 OUT4CC ("strl");
464
465 /* The audio stream header */
466
467 OUT4CC ("strh");
468 OUTLONG(56); /* # of bytes to follow */
469 OUT4CC ("auds");
470
471 // -----------
472 // ThOe
473 OUTLONG(0); /* Format (Optionally) */
474 // -----------
475
476 OUTLONG(0); /* Flags */
477 OUTLONG(0); /* Reserved, MS says: wPriority, wLanguage */
478 OUTLONG(0); /* InitialFrames */
479
480 // ThOe /4
481 OUTLONG(sampsize/4); /* Scale */
482 OUTLONG(1000*AVI->track[j].mp3rate/8);
483 OUTLONG(0); /* Start */
484 OUTLONG(4*AVI->track[j].audio_bytes/sampsize); /* Length */
485 OUTLONG(0); /* SuggestedBufferSize */
486 OUTLONG(-1); /* Quality */
487
488 // ThOe /4
489 OUTLONG(sampsize/4); /* SampleSize */
490
491 OUTLONG(0); /* Frame */
492 OUTLONG(0); /* Frame */
493 // OUTLONG(0); /* Frame */
494 //OUTLONG(0); /* Frame */
495
496 /* The audio stream format */
497
498 OUT4CC ("strf");
499 OUTLONG(16); /* # of bytes to follow */
500 OUTSHRT(AVI->track[j].a_fmt); /* Format */
501 OUTSHRT(AVI->track[j].a_chans); /* Number of channels */
502 OUTLONG(AVI->track[j].a_rate); /* SamplesPerSec */
503 // ThOe
504 OUTLONG(1000*AVI->track[j].mp3rate/8);
505 //ThOe (/4)
506
507 OUTSHRT(sampsize/4); /* BlockAlign */
508
509
510 OUTSHRT(AVI->track[j].a_bits); /* BitsPerSample */
511
512 /* Finish stream list, i.e. put number of bytes in the list to proper pos */
513
514 long2str(AVI_header+strl_start-4,nhb-strl_start);
515 }
516
517 /* Finish header list */
518
519 long2str(AVI_header+hdrl_start-4,nhb-hdrl_start);
520
521
522 /* Calculate the needed amount of junk bytes, output junk */
523
524 njunk = HEADERBYTES - nhb - 8 - 12;
525
526 /* Safety first: if njunk <= 0, somebody has played with
527 HEADERBYTES without knowing what (s)he did.
528 This is a fatal error */
529
530 if(njunk<=0)
531 {
532 fprintf(stderr,"AVI_close_output_file: # of header bytes too small\n");
533 exit(1);
534 }
535
536 OUT4CC ("JUNK");
537 OUTLONG(njunk);
538 memset(AVI_header+nhb,0,njunk);
539
540 //11/14/01 added id string
541
542 if(njunk > strlen(id_str)+8) {
543 sprintf(id_str, "%s-%s", PACKAGE, VERSION);
544 memcpy(AVI_header+nhb, id_str, strlen(id_str));
545 }
546
547 nhb += njunk;
548
549 /* Start the movi list */
550
551 OUT4CC ("LIST");
552 OUTLONG(movi_len); /* Length of list in bytes */
553 OUT4CC ("movi");
554
555 /* Output the header, truncate the file to the number of bytes
556 actually written, report an error if someting goes wrong */
557
558 if ( lseek(AVI->fdes,0,SEEK_SET)<0 ||
559 avi_write(AVI->fdes,(char *)AVI_header,HEADERBYTES)!=HEADERBYTES ||
560 lseek(AVI->fdes,AVI->pos,SEEK_SET)<0)
561 {
562 AVI_errno = AVI_ERR_CLOSE;
563 return -1;
564 }
565
566 return 0;
567 }
568
569 /*
570 Write the header of an AVI file and close it.
571 returns 0 on success, -1 on write error.
572 */
573
avi_close_output_file(avi_t * AVI)574 static int avi_close_output_file(avi_t *AVI)
575 {
576
577 int ret, njunk, sampsize, hasIndex, ms_per_frame, frate, idxerror, flag;
578 unsigned long movi_len;
579 int hdrl_start, strl_start, j;
580 unsigned char AVI_header[HEADERBYTES];
581 long nhb;
582
583 #ifdef INFO_LIST
584 long info_len;
585 // time_t calptr;
586 #endif
587
588 /* Calculate length of movi list */
589
590 movi_len = AVI->pos - HEADERBYTES + 4;
591
592 /* Try to ouput the index entries. This may fail e.g. if no space
593 is left on device. We will report this as an error, but we still
594 try to write the header correctly (so that the file still may be
595 readable in the most cases */
596
597 idxerror = 0;
598 // fprintf(stderr, "pos=%lu, index_len=%ld \n", AVI->pos, AVI->n_idx*16);
599 ret = avi_add_chunk(AVI, (unsigned char *)"idx1", (void*)AVI->idx, AVI->n_idx*16);
600 hasIndex = (ret==0);
601 //fprintf(stderr, "pos=%lu, index_len=%d\n", AVI->pos, hasIndex);
602
603 if(ret) {
604 idxerror = 1;
605 AVI_errno = AVI_ERR_WRITE_INDEX;
606 }
607
608 /* Calculate Microseconds per frame */
609
610 if(AVI->fps < 0.001) {
611 frate=0;
612 ms_per_frame=0;
613 } else {
614 frate = (int) (FRAME_RATE_SCALE*AVI->fps + 0.5);
615 ms_per_frame=(int) (1000000/AVI->fps + 0.5);
616 }
617
618 /* Prepare the file header */
619
620 nhb = 0;
621
622 /* The RIFF header */
623
624 OUT4CC ("RIFF");
625 OUTLONG(AVI->pos - 8); /* # of bytes to follow */
626 OUT4CC ("AVI ");
627
628 /* Start the header list */
629
630 OUT4CC ("LIST");
631 OUTLONG(0); /* Length of list in bytes, don't know yet */
632 hdrl_start = nhb; /* Store start position */
633 OUT4CC ("hdrl");
634
635 /* The main AVI header */
636
637 /* The Flags in AVI File header */
638
639 #define AVIF_HASINDEX 0x00000010 /* Index at end of file */
640 #define AVIF_MUSTUSEINDEX 0x00000020
641 #define AVIF_ISINTERLEAVED 0x00000100
642 #define AVIF_TRUSTCKTYPE 0x00000800 /* Use CKType to find key frames */
643 #define AVIF_WASCAPTUREFILE 0x00010000
644 #define AVIF_COPYRIGHTED 0x00020000
645
646 OUT4CC ("avih");
647 OUTLONG(56); /* # of bytes to follow */
648 OUTLONG(ms_per_frame); /* Microseconds per frame */
649 //ThOe ->0
650 // OUTLONG(10000000); /* MaxBytesPerSec, I hope this will never be used */
651 OUTLONG(0);
652 OUTLONG(0); /* PaddingGranularity (whatever that might be) */
653 /* Other sources call it 'reserved' */
654 flag = AVIF_ISINTERLEAVED;
655 if(hasIndex) flag |= AVIF_HASINDEX;
656 if(hasIndex && AVI->must_use_index) flag |= AVIF_MUSTUSEINDEX;
657 OUTLONG(flag); /* Flags */
658 OUTLONG(AVI->video_frames); /* TotalFrames */
659 OUTLONG(0); /* InitialFrames */
660
661 OUTLONG(AVI->anum+1);
662 // if (AVI->track[0].audio_bytes)
663 // { OUTLONG(2); } /* Streams */
664 // else
665 // { OUTLONG(1); } /* Streams */
666
667 OUTLONG(0); /* SuggestedBufferSize */
668 OUTLONG(AVI->width); /* Width */
669 OUTLONG(AVI->height); /* Height */
670 /* MS calls the following 'reserved': */
671 OUTLONG(0); /* TimeScale: Unit used to measure time */
672 OUTLONG(0); /* DataRate: Data rate of playback */
673 OUTLONG(0); /* StartTime: Starting time of AVI data */
674 OUTLONG(0); /* DataLength: Size of AVI data chunk */
675
676
677 /* Start the video stream list ---------------------------------- */
678
679 OUT4CC ("LIST");
680 OUTLONG(0); /* Length of list in bytes, don't know yet */
681 strl_start = nhb; /* Store start position */
682 OUT4CC ("strl");
683
684 /* The video stream header */
685
686 OUT4CC ("strh");
687 OUTLONG(56); /* # of bytes to follow */
688 OUT4CC ("vids"); /* Type */
689 OUT4CC (AVI->compressor); /* Handler */
690 OUTLONG(0); /* Flags */
691 OUTLONG(0); /* Reserved, MS says: wPriority, wLanguage */
692 OUTLONG(0); /* InitialFrames */
693 OUTLONG(FRAME_RATE_SCALE); /* Scale */
694 OUTLONG(frate); /* Rate: Rate/Scale == samples/second */
695 OUTLONG(0); /* Start */
696 OUTLONG(AVI->video_frames); /* Length */
697 OUTLONG(0); /* SuggestedBufferSize */
698 OUTLONG(-1); /* Quality */
699 OUTLONG(0); /* SampleSize */
700 OUTLONG(0); /* Frame */
701 OUTLONG(0); /* Frame */
702 // OUTLONG(0); /* Frame */
703 //OUTLONG(0); /* Frame */
704
705 /* The video stream format */
706
707 OUT4CC ("strf");
708 OUTLONG(40); /* # of bytes to follow */
709 OUTLONG(40); /* Size */
710 OUTLONG(AVI->width); /* Width */
711 OUTLONG(AVI->height); /* Height */
712 OUTSHRT(1); OUTSHRT(24); /* Planes, Count */
713 OUT4CC (AVI->compressor); /* Compression */
714 // ThOe (*3)
715 OUTLONG(AVI->width*AVI->height*3); /* SizeImage (in bytes?) */
716 OUTLONG(0); /* XPelsPerMeter */
717 OUTLONG(0); /* YPelsPerMeter */
718 OUTLONG(0); /* ClrUsed: Number of colors used */
719 OUTLONG(0); /* ClrImportant: Number of colors important */
720
721 /* Finish stream list, i.e. put number of bytes in the list to proper pos */
722
723 long2str(AVI_header+strl_start-4,nhb-strl_start);
724
725 /* Start the audio stream list ---------------------------------- */
726
727 for(j=0; j<AVI->anum; ++j) {
728
729 //if (AVI->track[j].a_chans && AVI->track[j].audio_bytes)
730 {
731
732 sampsize = avi_sampsize(AVI, j);
733
734 OUT4CC ("LIST");
735 OUTLONG(0); /* Length of list in bytes, don't know yet */
736 strl_start = nhb; /* Store start position */
737 OUT4CC ("strl");
738
739 /* The audio stream header */
740
741 OUT4CC ("strh");
742 OUTLONG(56); /* # of bytes to follow */
743 OUT4CC ("auds");
744
745 // -----------
746 // ThOe
747 OUTLONG(0); /* Format (Optionally) */
748 // -----------
749
750 OUTLONG(0); /* Flags */
751 OUTLONG(0); /* Reserved, MS says: wPriority, wLanguage */
752 OUTLONG(0); /* InitialFrames */
753
754 // ThOe /4
755 OUTLONG(sampsize/4); /* Scale */
756 OUTLONG(1000*AVI->track[j].mp3rate/8);
757 OUTLONG(0); /* Start */
758 OUTLONG(4*AVI->track[j].audio_bytes/sampsize); /* Length */
759 OUTLONG(0); /* SuggestedBufferSize */
760 OUTLONG(-1); /* Quality */
761
762 // ThOe /4
763 OUTLONG(sampsize/4); /* SampleSize */
764
765 OUTLONG(0); /* Frame */
766 OUTLONG(0); /* Frame */
767 // OUTLONG(0); /* Frame */
768 //OUTLONG(0); /* Frame */
769
770 /* The audio stream format */
771
772 OUT4CC ("strf");
773 OUTLONG(16); /* # of bytes to follow */
774 OUTSHRT(AVI->track[j].a_fmt); /* Format */
775 OUTSHRT(AVI->track[j].a_chans); /* Number of channels */
776 OUTLONG(AVI->track[j].a_rate); /* SamplesPerSec */
777 // ThOe
778 OUTLONG(1000*AVI->track[j].mp3rate/8);
779 //ThOe (/4)
780
781 OUTSHRT(sampsize/4); /* BlockAlign */
782
783
784 OUTSHRT(AVI->track[j].a_bits); /* BitsPerSample */
785
786 /* Finish stream list, i.e. put number of bytes in the list to proper pos */
787 }
788 long2str(AVI_header+strl_start-4,nhb-strl_start);
789 }
790
791 /* Finish header list */
792
793 long2str(AVI_header+hdrl_start-4,nhb-hdrl_start);
794
795
796 // add INFO list --- (0.6.0pre4)
797
798 #ifdef INFO_LIST
799 OUT4CC ("LIST");
800
801 //FIXME
802 info_len = MAX_INFO_STRLEN + 12;
803 OUTLONG(info_len);
804 OUT4CC ("INFO");
805
806 // OUT4CC ("INAM");
807 // OUTLONG(MAX_INFO_STRLEN);
808
809 // sprintf(id_str, "\t");
810 // memset(AVI_header+nhb, 0, MAX_INFO_STRLEN);
811 // memcpy(AVI_header+nhb, id_str, strlen(id_str));
812 // nhb += MAX_INFO_STRLEN;
813
814 OUT4CC ("ISFT");
815 OUTLONG(MAX_INFO_STRLEN);
816
817 sprintf(id_str, "%s-%s", PACKAGE, VERSION);
818 memset(AVI_header+nhb, 0, MAX_INFO_STRLEN);
819 memcpy(AVI_header+nhb, id_str, strlen(id_str));
820 nhb += MAX_INFO_STRLEN;
821
822 // OUT4CC ("ICMT");
823 // OUTLONG(MAX_INFO_STRLEN);
824
825 // calptr=time(NULL);
826 // sprintf(id_str, "\t%s %s", ctime(&calptr), "");
827 // memset(AVI_header+nhb, 0, MAX_INFO_STRLEN);
828 // memcpy(AVI_header+nhb, id_str, 25);
829 // nhb += MAX_INFO_STRLEN;
830 #endif
831
832 // ----------------------------
833
834 /* Calculate the needed amount of junk bytes, output junk */
835
836 njunk = HEADERBYTES - nhb - 8 - 12;
837
838 /* Safety first: if njunk <= 0, somebody has played with
839 HEADERBYTES without knowing what (s)he did.
840 This is a fatal error */
841
842 if(njunk<=0)
843 {
844 fprintf(stderr,"AVI_close_output_file: # of header bytes too small\n");
845 exit(1);
846 }
847
848 OUT4CC ("JUNK");
849 OUTLONG(njunk);
850 memset(AVI_header+nhb,0,njunk);
851
852 nhb += njunk;
853
854 /* Start the movi list */
855
856 OUT4CC ("LIST");
857 OUTLONG(movi_len); /* Length of list in bytes */
858 OUT4CC ("movi");
859
860 /* Output the header, truncate the file to the number of bytes
861 actually written, report an error if someting goes wrong */
862
863 if ( lseek(AVI->fdes,0,SEEK_SET)<0 ||
864 avi_write(AVI->fdes,(char *)AVI_header,HEADERBYTES)!=HEADERBYTES
865 //|| ftruncate(AVI->fdes,AVI->pos)<0
866 )
867 {
868 AVI_errno = AVI_ERR_CLOSE;
869 return -1;
870 }
871
872 if(idxerror) return -1;
873
874 return 0;
875 }
876
877 /*
878 AVI_write_data:
879 Add video or audio data to the file;
880
881 Return values:
882 0 No error;
883 -1 Error, AVI_errno is set appropriatly;
884
885 */
886
avi_write_data(avi_t * AVI,char * data,unsigned long length,int audio,int keyframe)887 static int avi_write_data(avi_t *AVI, char *data, unsigned long length, int audio, int keyframe)
888 {
889 int n;
890
891 unsigned char astr[5];
892
893 /* Check for maximum file length */
894
895 if ( (AVI->pos + 8 + length + 8 + (AVI->n_idx+1)*16) > AVI_MAX_LEN ) {
896 AVI_errno = AVI_ERR_SIZELIM;
897 return -1;
898 }
899
900 /* Add index entry */
901
902 //set tag for current audio track
903 sprintf((char *)astr, "0%1dwb", AVI->aptr+1);
904
905 if(audio)
906 n = avi_add_index_entry(AVI,astr,0x00,AVI->pos,length);
907 else
908 n = avi_add_index_entry(AVI,(unsigned char *) "00db",((keyframe)?0x10:0x0),AVI->pos,length);
909
910 if(n) return -1;
911
912 /* Output tag and data */
913
914 if(audio)
915 n = avi_add_chunk(AVI,(unsigned char *) astr, (unsigned char *)data,length);
916 else
917 n = avi_add_chunk(AVI,(unsigned char *)"00db",(unsigned char *)data,length);
918
919 if (n) return -1;
920
921 return 0;
922 }
923
AVI_write_frame(avi_t * AVI,char * data,long bytes,int keyframe)924 int AVI_write_frame(avi_t *AVI, char *data, long bytes, int keyframe)
925 {
926 unsigned long pos;
927
928 if(AVI->mode==AVI_MODE_READ) { AVI_errno = AVI_ERR_NOT_PERM; return -1; }
929
930 pos = AVI->pos;
931
932 if(avi_write_data(AVI,data,bytes,0,keyframe)) return -1;
933
934 AVI->last_pos = pos;
935 AVI->last_len = bytes;
936 AVI->video_frames++;
937 return 0;
938 }
939
AVI_dup_frame(avi_t * AVI)940 int AVI_dup_frame(avi_t *AVI)
941 {
942 if(AVI->mode==AVI_MODE_READ) { AVI_errno = AVI_ERR_NOT_PERM; return -1; }
943
944 if(AVI->last_pos==0) return 0; /* No previous real frame */
945 if(avi_add_index_entry(AVI,(unsigned char *)"00db",0x10,AVI->last_pos,AVI->last_len)) return -1;
946 AVI->video_frames++;
947 AVI->must_use_index = 1;
948 return 0;
949 }
950
AVI_write_audio(avi_t * AVI,char * data,long bytes)951 int AVI_write_audio(avi_t *AVI, char *data, long bytes)
952 {
953 if(AVI->mode==AVI_MODE_READ) { AVI_errno = AVI_ERR_NOT_PERM; return -1; }
954
955 if( avi_write_data(AVI,data,bytes,1,0) ) return -1;
956 AVI->track[AVI->aptr].audio_bytes += bytes;
957 return 0;
958 }
959
960
AVI_append_audio(avi_t * AVI,char * data,long bytes)961 int AVI_append_audio(avi_t *AVI, char *data, long bytes)
962 {
963
964 long i, length, pos;
965 unsigned char c[4];
966
967 if(AVI->mode==AVI_MODE_READ) { AVI_errno = AVI_ERR_NOT_PERM; return -1; }
968
969 // update last index entry:
970
971 --AVI->n_idx;
972 length = str2ulong(AVI->idx[AVI->n_idx]+12);
973 pos = str2ulong(AVI->idx[AVI->n_idx]+8);
974
975 //update;
976 long2str(AVI->idx[AVI->n_idx]+12,length+bytes);
977
978 ++AVI->n_idx;
979
980 AVI->track[AVI->aptr].audio_bytes += bytes;
981
982 //update chunk header
983 lseek(AVI->fdes, pos+4, SEEK_SET);
984 long2str(c, length+bytes);
985 avi_write(AVI->fdes,(char *) c, 4);
986
987 lseek(AVI->fdes, pos+8+length, SEEK_SET);
988
989 i=PAD_EVEN(length + bytes);
990
991 bytes = i - length;
992 avi_write(AVI->fdes, data, bytes);
993 AVI->pos = pos + 8 + i;
994
995 return 0;
996 }
997
998
AVI_bytes_remain(avi_t * AVI)999 long AVI_bytes_remain(avi_t *AVI)
1000 {
1001 if(AVI->mode==AVI_MODE_READ) return 0;
1002
1003 return ( AVI_MAX_LEN - (AVI->pos + 8 + 16*AVI->n_idx));
1004 }
1005
AVI_bytes_written(avi_t * AVI)1006 long AVI_bytes_written(avi_t *AVI)
1007 {
1008 if(AVI->mode==AVI_MODE_READ) return 0;
1009
1010 return (AVI->pos + 8 + 16*AVI->n_idx);
1011 }
1012
AVI_set_audio_track(avi_t * AVI,int track)1013 int AVI_set_audio_track(avi_t *AVI, int track)
1014 {
1015
1016 if(track < 0 || track + 1 > AVI->anum) return(-1);
1017
1018 //this info is not written to file anyway
1019 AVI->aptr=track;
1020 return 0;
1021 }
1022
AVI_get_audio_track(avi_t * AVI)1023 int AVI_get_audio_track(avi_t *AVI)
1024 {
1025 return(AVI->aptr);
1026 }
1027
1028
1029 /*******************************************************************
1030 * *
1031 * Utilities for reading video and audio from an AVI File *
1032 * *
1033 *******************************************************************/
1034
AVI_close(avi_t * AVI)1035 int AVI_close(avi_t *AVI)
1036 {
1037 int ret;
1038
1039 /* If the file was open for writing, the header and index still have
1040 to be written */
1041
1042 if(AVI->mode == AVI_MODE_WRITE)
1043 ret = avi_close_output_file(AVI);
1044 else
1045 ret = 0;
1046
1047 /* Even if there happened an error, we first clean up */
1048
1049 close(AVI->fdes);
1050 if(AVI->idx) free(AVI->idx);
1051 if(AVI->video_index) free(AVI->video_index);
1052 //FIXME
1053 //if(AVI->audio_index) free(AVI->audio_index);
1054 free(AVI);
1055
1056 return ret;
1057 }
1058
1059
1060 #define ERR_EXIT(x) \
1061 { \
1062 AVI_close(AVI); \
1063 AVI_errno = x; \
1064 return 0; \
1065 }
1066
AVI_open_input_file(char * filename,int getIndex)1067 avi_t *AVI_open_input_file(char *filename, int getIndex)
1068 {
1069 avi_t *AVI=NULL;
1070
1071 /* Create avi_t structure */
1072
1073 AVI = (avi_t *) malloc(sizeof(avi_t));
1074 if(AVI==NULL)
1075 {
1076 AVI_errno = AVI_ERR_NO_MEM;
1077 return 0;
1078 }
1079 memset((void *)AVI,0,sizeof(avi_t));
1080
1081 AVI->mode = AVI_MODE_READ; /* open for reading */
1082
1083 /* Open the file */
1084
1085 AVI->fdes = open(filename,O_RDONLY|O_BINARY);
1086 if(AVI->fdes < 0)
1087 {
1088 AVI_errno = AVI_ERR_OPEN;
1089 free(AVI);
1090 return 0;
1091 }
1092
1093 avi_parse_input_file(AVI, getIndex);
1094
1095 AVI->aptr=0; //reset
1096
1097 return AVI;
1098 }
1099
AVI_open_fd(int fd,int getIndex)1100 avi_t *AVI_open_fd(int fd, int getIndex)
1101 {
1102 avi_t *AVI=NULL;
1103
1104 /* Create avi_t structure */
1105
1106 AVI = (avi_t *) malloc(sizeof(avi_t));
1107 if(AVI==NULL)
1108 {
1109 AVI_errno = AVI_ERR_NO_MEM;
1110 return 0;
1111 }
1112 memset((void *)AVI,0,sizeof(avi_t));
1113
1114 AVI->mode = AVI_MODE_READ; /* open for reading */
1115
1116 // file alread open
1117 AVI->fdes = fd;
1118
1119 avi_parse_input_file(AVI, getIndex);
1120
1121 AVI->aptr=0; //reset
1122
1123 return AVI;
1124 }
1125
avi_parse_input_file(avi_t * AVI,int getIndex)1126 int avi_parse_input_file(avi_t *AVI, int getIndex)
1127 {
1128 long i, n, rate, scale, idx_type;
1129 unsigned char *hdrl_data;
1130 long header_offset=0, hdrl_len=0;
1131 long nvi, nai[AVI_MAX_TRACKS], ioff;
1132 long tot[AVI_MAX_TRACKS];
1133 int j;
1134 int lasttag = 0;
1135 int vids_strh_seen = 0;
1136 int vids_strf_seen = 0;
1137 int auds_strh_seen = 0;
1138 // int auds_strf_seen = 0;
1139 int num_stream = 0;
1140 char data[256];
1141
1142 /* Read first 12 bytes and check that this is an AVI file */
1143
1144 if( avi_read(AVI->fdes,data,12) != 12 ) ERR_EXIT(AVI_ERR_READ)
1145
1146 if( strncasecmp(data ,"RIFF",4) !=0 ||
1147 strncasecmp(data+8,"AVI ",4) !=0 ) ERR_EXIT(AVI_ERR_NO_AVI)
1148
1149 /* Go through the AVI file and extract the header list,
1150 the start position of the 'movi' list and an optionally
1151 present idx1 tag */
1152
1153 hdrl_data = 0;
1154
1155 while(1)
1156 {
1157 if( avi_read(AVI->fdes,data,8) != 8 ) break; /* We assume it's EOF */
1158
1159 n = str2ulong((unsigned char *) data+4);
1160 n = PAD_EVEN(n);
1161
1162 if(strncasecmp(data,"LIST",4) == 0)
1163 {
1164 if( avi_read(AVI->fdes,data,4) != 4 ) ERR_EXIT(AVI_ERR_READ)
1165 n -= 4;
1166 if(strncasecmp(data,"hdrl",4) == 0)
1167 {
1168 hdrl_len = n;
1169 hdrl_data = (unsigned char *) malloc(n);
1170 if(hdrl_data==0) ERR_EXIT(AVI_ERR_NO_MEM);
1171
1172 // offset of header
1173
1174 header_offset = lseek(AVI->fdes,0,SEEK_CUR);
1175
1176 if( avi_read(AVI->fdes,(char *)hdrl_data,n) != n ) ERR_EXIT(AVI_ERR_READ)
1177 }
1178 else if(strncasecmp(data,"movi",4) == 0)
1179 {
1180 AVI->movi_start = lseek(AVI->fdes,0,SEEK_CUR);
1181 lseek(AVI->fdes,n,SEEK_CUR);
1182 }
1183 else
1184 lseek(AVI->fdes,n,SEEK_CUR);
1185 }
1186 else if(strncasecmp(data,"idx1",4) == 0)
1187 {
1188 /* n must be a multiple of 16, but the reading does not
1189 break if this is not the case */
1190
1191 AVI->n_idx = AVI->max_idx = n/16;
1192 AVI->idx = (unsigned char((*)[16]) ) malloc(n);
1193 if(AVI->idx==0) ERR_EXIT(AVI_ERR_NO_MEM)
1194 if(avi_read(AVI->fdes, (char *) AVI->idx, n) != n ) ERR_EXIT(AVI_ERR_READ)
1195 }
1196 else
1197 lseek(AVI->fdes,n,SEEK_CUR);
1198 }
1199
1200 if(!hdrl_data ) ERR_EXIT(AVI_ERR_NO_HDRL)
1201 if(!AVI->movi_start) ERR_EXIT(AVI_ERR_NO_MOVI)
1202
1203 /* Interpret the header list */
1204
1205 for(i=0;i<hdrl_len;)
1206 {
1207 /* List tags are completly ignored */
1208
1209 if(strncasecmp((char *) hdrl_data+i, "LIST",4)==0) { i+= 12; continue; }
1210
1211 n = str2ulong(hdrl_data+i+4);
1212 n = PAD_EVEN(n);
1213
1214 /* Interpret the tag and its args */
1215
1216 if(strncasecmp((char *)hdrl_data+i,"strh",4)==0)
1217 {
1218 i += 8;
1219 if(strncasecmp((char *)hdrl_data+i,"vids",4) == 0 && !vids_strh_seen)
1220 {
1221 memcpy(AVI->compressor,hdrl_data+i+4,4);
1222 AVI->compressor[4] = 0;
1223
1224 // ThOe
1225 AVI->v_codech_off = header_offset + i+4;
1226
1227 scale = str2ulong((unsigned char *)hdrl_data+i+20);
1228 rate = str2ulong(hdrl_data+i+24);
1229 if(scale!=0) AVI->fps = (double)rate/(double)scale;
1230 AVI->video_frames = str2ulong(hdrl_data+i+32);
1231 AVI->video_strn = num_stream;
1232 AVI->max_len = 0;
1233 vids_strh_seen = 1;
1234 lasttag = 1; /* vids */
1235 }
1236 else if (strncasecmp ((char *) hdrl_data+i,"auds",4) ==0 && ! auds_strh_seen)
1237 {
1238
1239 //inc audio tracks
1240 AVI->aptr=AVI->anum;
1241 ++AVI->anum;
1242
1243 if(AVI->anum > AVI_MAX_TRACKS) {
1244 fprintf(stderr, "error - only %d audio tracks supported\n", AVI_MAX_TRACKS);
1245 return(-1);
1246 }
1247
1248 AVI->track[AVI->aptr].audio_bytes = str2ulong(hdrl_data+i+32)*avi_sampsize(AVI, 0);
1249 AVI->track[AVI->aptr].audio_strn = num_stream;
1250 // auds_strh_seen = 1;
1251 lasttag = 2; /* auds */
1252
1253 // ThOe
1254 AVI->track[AVI->aptr].a_codech_off = header_offset + i;
1255
1256 }
1257 else
1258 lasttag = 0;
1259 num_stream++;
1260 }
1261 else if(strncasecmp((char *) hdrl_data+i,"strf",4)==0)
1262 {
1263 i += 8;
1264 if(lasttag == 1)
1265 {
1266 AVI->width = str2ulong(hdrl_data+i+4);
1267 AVI->height = str2ulong(hdrl_data+i+8);
1268 vids_strf_seen = 1;
1269 //ThOe
1270 AVI->v_codecf_off = header_offset + i+16;
1271
1272 memcpy(AVI->compressor2, hdrl_data+i+16, 4);
1273 AVI->compressor2[4] = 0;
1274
1275 }
1276 else if(lasttag == 2)
1277 {
1278 AVI->track[AVI->aptr].a_fmt = str2ushort(hdrl_data+i );
1279
1280 //ThOe
1281 AVI->track[AVI->aptr].a_codecf_off = header_offset + i;
1282
1283 AVI->track[AVI->aptr].a_chans = str2ushort(hdrl_data+i+2);
1284 AVI->track[AVI->aptr].a_rate = str2ulong (hdrl_data+i+4);
1285 //ThOe: read mp3bitrate
1286 AVI->track[AVI->aptr].mp3rate = 8*str2ulong(hdrl_data+i+8)/1000;
1287 //:ThOe
1288 AVI->track[AVI->aptr].a_bits = str2ushort(hdrl_data+i+14);
1289 // auds_strf_seen = 1;
1290 }
1291 lasttag = 0;
1292 }
1293 else
1294 {
1295 i += 8;
1296 lasttag = 0;
1297 }
1298
1299 i += n;
1300 }
1301
1302 free(hdrl_data);
1303
1304 if(!vids_strh_seen || !vids_strf_seen) ERR_EXIT(AVI_ERR_NO_VIDS)
1305
1306 AVI->video_tag[0] = AVI->video_strn/10 + '0';
1307 AVI->video_tag[1] = AVI->video_strn%10 + '0';
1308 AVI->video_tag[2] = 'd';
1309 AVI->video_tag[3] = 'b';
1310
1311 /* Audio tag is set to "99wb" if no audio present */
1312 if(!AVI->track[0].a_chans) AVI->track[0].audio_strn = 99;
1313
1314 for(j=0; j<AVI->anum; ++j) {
1315 AVI->track[j].audio_tag[0] = (j+1)/10 + '0';
1316 AVI->track[j].audio_tag[1] = (j+1)%10 + '0';
1317 AVI->track[j].audio_tag[2] = 'w';
1318 AVI->track[j].audio_tag[3] = 'b';
1319 }
1320
1321 lseek(AVI->fdes,AVI->movi_start,SEEK_SET);
1322
1323 /* get index if wanted */
1324
1325 if(!getIndex) return(0);
1326
1327 /* if the file has an idx1, check if this is relative
1328 to the start of the file or to the start of the movi list */
1329
1330 idx_type = 0;
1331
1332 if(AVI->idx)
1333 {
1334 long pos, len;
1335
1336 /* Search the first videoframe in the idx1 and look where
1337 it is in the file */
1338
1339 for(i=0;i<AVI->n_idx;i++)
1340 if( strncasecmp((char *) AVI->idx[i],(char *) AVI->video_tag,3)==0 ) break;
1341 if(i>=AVI->n_idx) ERR_EXIT(AVI_ERR_NO_VIDS)
1342
1343 pos = str2ulong(AVI->idx[i]+ 8);
1344 len = str2ulong(AVI->idx[i]+12);
1345
1346 lseek(AVI->fdes,pos,SEEK_SET);
1347 if(avi_read(AVI->fdes,data,8)!=8) ERR_EXIT(AVI_ERR_READ)
1348 if( strncasecmp((char *)data,(char *)AVI->idx[i],4)==0 &&
1349 str2ulong((unsigned char *)data+4)==len )
1350 {
1351 idx_type = 1; /* Index from start of file */
1352 }
1353 else
1354 {
1355 lseek(AVI->fdes,pos+AVI->movi_start-4,SEEK_SET);
1356 if(avi_read(AVI->fdes,data,8)!=8) ERR_EXIT(AVI_ERR_READ)
1357 if( strncasecmp((char *)data,(char *)AVI->idx[i],4)==0 && str2ulong((unsigned char *)data+4)==len )
1358 {
1359 idx_type = 2; /* Index from start of movi list */
1360 }
1361 }
1362 /* idx_type remains 0 if neither of the two tests above succeeds */
1363 }
1364
1365 if(idx_type == 0)
1366 {
1367 /* we must search through the file to get the index */
1368
1369 lseek(AVI->fdes, AVI->movi_start, SEEK_SET);
1370
1371 AVI->n_idx = 0;
1372
1373 while(1)
1374 {
1375 if( avi_read(AVI->fdes,data,8) != 8 ) break;
1376 n = str2ulong((unsigned char *)data+4);
1377
1378 /* The movi list may contain sub-lists, ignore them */
1379
1380 if(strncasecmp(data,"LIST",4)==0)
1381 {
1382 lseek(AVI->fdes,4,SEEK_CUR);
1383 continue;
1384 }
1385
1386 /* Check if we got a tag ##db, ##dc or ##wb */
1387
1388 if( ( (data[2]=='d' || data[2]=='D') &&
1389 (data[3]=='b' || data[3]=='B' || data[3]=='c' || data[3]=='C') )
1390 || ( (data[2]=='w' || data[2]=='W') &&
1391 (data[3]=='b' || data[3]=='B') ) )
1392 {
1393 avi_add_index_entry(AVI,(unsigned char *) data,0,lseek(AVI->fdes,0,SEEK_CUR)-8,n);
1394 }
1395
1396 lseek(AVI->fdes,PAD_EVEN(n),SEEK_CUR);
1397 }
1398 idx_type = 1;
1399 }
1400
1401 /* Now generate the video index and audio index arrays */
1402
1403 nvi = 0;
1404 for(j=0; j<AVI->anum; ++j) nai[j] = 0;
1405
1406 for(i=0;i<AVI->n_idx;i++) {
1407
1408 if(strncasecmp((char *)AVI->idx[i],(char *) AVI->video_tag,3) == 0) nvi++;
1409
1410 for(j=0; j<AVI->anum; ++j) if(strncasecmp((char *)AVI->idx[i], AVI->track[j].audio_tag,4) == 0) nai[j]++;
1411 }
1412
1413 AVI->video_frames = nvi;
1414 for(j=0; j<AVI->anum; ++j) AVI->track[j].audio_chunks = nai[j];
1415
1416 // fprintf(stderr, "chunks = %ld %d %s\n", AVI->track[0].audio_chunks, AVI->anum, AVI->track[0].audio_tag);
1417
1418 if(AVI->video_frames==0) ERR_EXIT(AVI_ERR_NO_VIDS);
1419 AVI->video_index = (video_index_entry *) malloc(nvi*sizeof(video_index_entry));
1420 if(AVI->video_index==0) ERR_EXIT(AVI_ERR_NO_MEM);
1421
1422 for(j=0; j<AVI->anum; ++j) {
1423 if(AVI->track[j].audio_chunks) {
1424 AVI->track[j].audio_index = (audio_index_entry *) malloc(nai[j]*sizeof(audio_index_entry));
1425 if(AVI->track[j].audio_index==0) ERR_EXIT(AVI_ERR_NO_MEM);
1426 }
1427 }
1428
1429 nvi = 0;
1430 for(j=0; j<AVI->anum; ++j) nai[j] = tot[j] = 0;
1431
1432 ioff = idx_type == 1 ? 8 : AVI->movi_start+4;
1433
1434 for(i=0;i<AVI->n_idx;i++) {
1435
1436 //video
1437 if(strncasecmp((char *)AVI->idx[i],(char *)AVI->video_tag,3) == 0) {
1438 AVI->video_index[nvi].key = str2ulong(AVI->idx[i]+ 4);
1439 AVI->video_index[nvi].pos = str2ulong(AVI->idx[i]+ 8)+ioff;
1440 AVI->video_index[nvi].len = str2ulong(AVI->idx[i]+12);
1441 nvi++;
1442 }
1443
1444 //audio
1445 for(j=0; j<AVI->anum; ++j) {
1446
1447 if(strncasecmp((char *)AVI->idx[i],AVI->track[j].audio_tag,4) == 0) {
1448 AVI->track[j].audio_index[nai[j]].pos = str2ulong(AVI->idx[i]+ 8)+ioff;
1449 AVI->track[j].audio_index[nai[j]].len = str2ulong(AVI->idx[i]+12);
1450 AVI->track[j].audio_index[nai[j]].tot = tot[j];
1451 tot[j] += AVI->track[j].audio_index[nai[j]].len;
1452 nai[j]++;
1453 }
1454 }
1455 }
1456
1457
1458 for(j=0; j<AVI->anum; ++j) AVI->track[j].audio_bytes = tot[j];
1459
1460 /* Reposition the file */
1461
1462 lseek(AVI->fdes,AVI->movi_start,SEEK_SET);
1463 AVI->video_pos = 0;
1464
1465 return(0);
1466 }
1467
AVI_video_frames(avi_t * AVI)1468 long AVI_video_frames(avi_t *AVI)
1469 {
1470 return AVI->video_frames;
1471 }
AVI_video_width(avi_t * AVI)1472 int AVI_video_width(avi_t *AVI)
1473 {
1474 return AVI->width;
1475 }
AVI_video_height(avi_t * AVI)1476 int AVI_video_height(avi_t *AVI)
1477 {
1478 return AVI->height;
1479 }
AVI_frame_rate(avi_t * AVI)1480 double AVI_frame_rate(avi_t *AVI)
1481 {
1482 return AVI->fps;
1483 }
AVI_video_compressor(avi_t * AVI)1484 char* AVI_video_compressor(avi_t *AVI)
1485 {
1486 return AVI->compressor2;
1487 }
1488
AVI_max_video_chunk(avi_t * AVI)1489 long AVI_max_video_chunk(avi_t *AVI)
1490 {
1491 return AVI->max_len;
1492 }
1493
AVI_audio_tracks(avi_t * AVI)1494 int AVI_audio_tracks(avi_t *AVI)
1495 {
1496 return(AVI->anum);
1497 }
1498
AVI_audio_channels(avi_t * AVI)1499 int AVI_audio_channels(avi_t *AVI)
1500 {
1501 return AVI->track[AVI->aptr].a_chans;
1502 }
1503
AVI_audio_mp3rate(avi_t * AVI)1504 long AVI_audio_mp3rate(avi_t *AVI)
1505 {
1506 return AVI->track[AVI->aptr].mp3rate;
1507 }
1508
AVI_audio_bits(avi_t * AVI)1509 int AVI_audio_bits(avi_t *AVI)
1510 {
1511 return AVI->track[AVI->aptr].a_bits;
1512 }
1513
AVI_audio_format(avi_t * AVI)1514 int AVI_audio_format(avi_t *AVI)
1515 {
1516 return AVI->track[AVI->aptr].a_fmt;
1517 }
1518
AVI_audio_rate(avi_t * AVI)1519 long AVI_audio_rate(avi_t *AVI)
1520 {
1521 return AVI->track[AVI->aptr].a_rate;
1522 }
1523
AVI_audio_bytes(avi_t * AVI)1524 long AVI_audio_bytes(avi_t *AVI)
1525 {
1526 return AVI->track[AVI->aptr].audio_bytes;
1527 }
1528
AVI_audio_chunks(avi_t * AVI)1529 long AVI_audio_chunks(avi_t *AVI)
1530 {
1531 return AVI->track[AVI->aptr].audio_chunks;
1532 }
1533
AVI_audio_codech_offset(avi_t * AVI)1534 long AVI_audio_codech_offset(avi_t *AVI)
1535 {
1536 return AVI->track[AVI->aptr].a_codech_off;
1537 }
1538
AVI_audio_codecf_offset(avi_t * AVI)1539 long AVI_audio_codecf_offset(avi_t *AVI)
1540 {
1541 return AVI->track[AVI->aptr].a_codecf_off;
1542 }
1543
AVI_video_codech_offset(avi_t * AVI)1544 long AVI_video_codech_offset(avi_t *AVI)
1545 {
1546 return AVI->v_codech_off;
1547 }
1548
AVI_video_codecf_offset(avi_t * AVI)1549 long AVI_video_codecf_offset(avi_t *AVI)
1550 {
1551 return AVI->v_codecf_off;
1552 }
1553
AVI_frame_size(avi_t * AVI,long frame)1554 long AVI_frame_size(avi_t *AVI, long frame)
1555 {
1556 if(AVI->mode==AVI_MODE_WRITE) { AVI_errno = AVI_ERR_NOT_PERM; return -1; }
1557 if(!AVI->video_index) { AVI_errno = AVI_ERR_NO_IDX; return -1; }
1558
1559 if(frame < 0 || frame >= AVI->video_frames) return 0;
1560 return(AVI->video_index[frame].len);
1561 }
1562
AVI_audio_size(avi_t * AVI,long frame)1563 long AVI_audio_size(avi_t *AVI, long frame)
1564 {
1565 if(AVI->mode==AVI_MODE_WRITE) { AVI_errno = AVI_ERR_NOT_PERM; return -1; }
1566 if(!AVI->track[AVI->aptr].audio_index) { AVI_errno = AVI_ERR_NO_IDX; return -1; }
1567
1568 if(frame < 0 || frame >= AVI->track[AVI->aptr].audio_chunks) return 0;
1569 return(AVI->track[AVI->aptr].audio_index[frame].len);
1570 }
1571
AVI_get_video_position(avi_t * AVI,long frame)1572 long AVI_get_video_position(avi_t *AVI, long frame)
1573 {
1574 if(AVI->mode==AVI_MODE_WRITE) { AVI_errno = AVI_ERR_NOT_PERM; return -1; }
1575 if(!AVI->video_index) { AVI_errno = AVI_ERR_NO_IDX; return -1; }
1576
1577 if(frame < 0 || frame >= AVI->video_frames) return 0;
1578 return(AVI->video_index[frame].pos);
1579 }
1580
1581
AVI_seek_start(avi_t * AVI)1582 int AVI_seek_start(avi_t *AVI)
1583 {
1584 if(AVI->mode==AVI_MODE_WRITE) { AVI_errno = AVI_ERR_NOT_PERM; return -1; }
1585
1586 lseek(AVI->fdes,AVI->movi_start,SEEK_SET);
1587 AVI->video_pos = 0;
1588 return 0;
1589 }
1590
AVI_set_video_position(avi_t * AVI,long frame)1591 int AVI_set_video_position(avi_t *AVI, long frame)
1592 {
1593 if(AVI->mode==AVI_MODE_WRITE) { AVI_errno = AVI_ERR_NOT_PERM; return -1; }
1594 if(!AVI->video_index) { AVI_errno = AVI_ERR_NO_IDX; return -1; }
1595
1596 if (frame < 0 ) frame = 0;
1597 AVI->video_pos = frame;
1598 return 0;
1599 }
1600
AVI_set_audio_bitrate(avi_t * AVI,long bitrate)1601 int AVI_set_audio_bitrate(avi_t *AVI, long bitrate)
1602 {
1603 if(AVI->mode==AVI_MODE_READ) { AVI_errno = AVI_ERR_NOT_PERM; return -1; }
1604
1605 AVI->track[AVI->aptr].mp3rate = bitrate;
1606 return 0;
1607 }
1608
1609
AVI_read_frame(avi_t * AVI,char * vidbuf,int * keyframe)1610 long AVI_read_frame(avi_t *AVI, char *vidbuf, int *keyframe)
1611 {
1612 long n;
1613
1614 if(AVI->mode==AVI_MODE_WRITE) { AVI_errno = AVI_ERR_NOT_PERM; return -1; }
1615 if(!AVI->video_index) { AVI_errno = AVI_ERR_NO_IDX; return -1; }
1616
1617 if(AVI->video_pos < 0 || AVI->video_pos >= AVI->video_frames) return -1;
1618 n = AVI->video_index[AVI->video_pos].len;
1619
1620 *keyframe = (AVI->video_index[AVI->video_pos].key==0x10) ? 1:0;
1621
1622 lseek(AVI->fdes, AVI->video_index[AVI->video_pos].pos, SEEK_SET);
1623
1624 if (avi_read(AVI->fdes,vidbuf,n) != n)
1625 {
1626 AVI_errno = AVI_ERR_READ;
1627 return -1;
1628 }
1629
1630 AVI->video_pos++;
1631
1632 return n;
1633 }
1634
AVI_set_audio_position(avi_t * AVI,long byte)1635 int AVI_set_audio_position(avi_t *AVI, long byte)
1636 {
1637 long n0, n1, n;
1638
1639 if(AVI->mode==AVI_MODE_WRITE) { AVI_errno = AVI_ERR_NOT_PERM; return -1; }
1640 if(!AVI->track[AVI->aptr].audio_index) { AVI_errno = AVI_ERR_NO_IDX; return -1; }
1641
1642 if(byte < 0) byte = 0;
1643
1644 /* Binary search in the audio chunks */
1645
1646 n0 = 0;
1647 n1 = AVI->track[AVI->aptr].audio_chunks;
1648
1649 while(n0<n1-1)
1650 {
1651 n = (n0+n1)/2;
1652 if(AVI->track[AVI->aptr].audio_index[n].tot>byte)
1653 n1 = n;
1654 else
1655 n0 = n;
1656 }
1657
1658 AVI->track[AVI->aptr].audio_posc = n0;
1659 AVI->track[AVI->aptr].audio_posb = byte - AVI->track[AVI->aptr].audio_index[n0].tot;
1660
1661 return 0;
1662 }
1663
AVI_read_audio(avi_t * AVI,char * audbuf,long bytes)1664 long AVI_read_audio(avi_t *AVI, char *audbuf, long bytes)
1665 {
1666 long nr, pos, left, todo;
1667
1668 if(AVI->mode==AVI_MODE_WRITE) { AVI_errno = AVI_ERR_NOT_PERM; return -1; }
1669 if(!AVI->track[AVI->aptr].audio_index) { AVI_errno = AVI_ERR_NO_IDX; return -1; }
1670
1671 nr = 0; /* total number of bytes read */
1672
1673 while(bytes>0)
1674 {
1675 left = AVI->track[AVI->aptr].audio_index[AVI->track[AVI->aptr].audio_posc].len - AVI->track[AVI->aptr].audio_posb;
1676 if(left==0)
1677 {
1678 if(AVI->track[AVI->aptr].audio_posc>=AVI->track[AVI->aptr].audio_chunks-1) return nr;
1679 AVI->track[AVI->aptr].audio_posc++;
1680 AVI->track[AVI->aptr].audio_posb = 0;
1681 continue;
1682 }
1683 if(bytes<left)
1684 todo = bytes;
1685 else
1686 todo = left;
1687 pos = AVI->track[AVI->aptr].audio_index[AVI->track[AVI->aptr].audio_posc].pos + AVI->track[AVI->aptr].audio_posb;
1688 lseek(AVI->fdes, pos, SEEK_SET);
1689 if (avi_read(AVI->fdes,audbuf+nr,todo) != todo)
1690 {
1691 AVI_errno = AVI_ERR_READ;
1692 return -1;
1693 }
1694 bytes -= todo;
1695 nr += todo;
1696 AVI->track[AVI->aptr].audio_posb += todo;
1697 }
1698
1699 return nr;
1700 }
1701
1702 /* AVI_read_data: Special routine for reading the next audio or video chunk
1703 without having an index of the file. */
1704
AVI_read_data(avi_t * AVI,char * vidbuf,long max_vidbuf,char * audbuf,long max_audbuf,long * len)1705 int AVI_read_data(avi_t *AVI, char *vidbuf, long max_vidbuf,
1706 char *audbuf, long max_audbuf,
1707 long *len)
1708 {
1709
1710 /*
1711 * Return codes:
1712 *
1713 * 1 = video data read
1714 * 2 = audio data read
1715 * 0 = reached EOF
1716 * -1 = video buffer too small
1717 * -2 = audio buffer too small
1718 */
1719
1720 int n;
1721 char data[8];
1722
1723 if(AVI->mode==AVI_MODE_WRITE) return 0;
1724
1725 while(1)
1726 {
1727 /* Read tag and length */
1728
1729 if( avi_read(AVI->fdes,data,8) != 8 ) return 0;
1730
1731 /* if we got a list tag, ignore it */
1732
1733 if(strncasecmp(data,"LIST",4) == 0)
1734 {
1735 lseek(AVI->fdes,4,SEEK_CUR);
1736 continue;
1737 }
1738
1739 n = PAD_EVEN(str2ulong((unsigned char *)data+4));
1740
1741 if(strncasecmp(data,AVI->video_tag,3) == 0)
1742 {
1743 *len = n;
1744 AVI->video_pos++;
1745 if(n>max_vidbuf)
1746 {
1747 lseek(AVI->fdes,n,SEEK_CUR);
1748 return -1;
1749 }
1750 if(avi_read(AVI->fdes,vidbuf,n) != n ) return 0;
1751 return 1;
1752 }
1753 else if(strncasecmp(data,AVI->track[AVI->aptr].audio_tag,4) == 0)
1754 {
1755 *len = n;
1756 if(n>max_audbuf)
1757 {
1758 lseek(AVI->fdes,n,SEEK_CUR);
1759 return -2;
1760 }
1761 if(avi_read(AVI->fdes,audbuf,n) != n ) return 0;
1762 return 2;
1763 break;
1764 }
1765 else
1766 if(lseek(AVI->fdes,n,SEEK_CUR)<0) return 0;
1767 }
1768 }
1769
1770 /* AVI_print_error: Print most recent error (similar to perror) */
1771
1772 char *(avi_errors[]) =
1773 {
1774 /* 0 */ "avilib - No Error",
1775 /* 1 */ "avilib - AVI file size limit reached",
1776 /* 2 */ "avilib - Error opening AVI file",
1777 /* 3 */ "avilib - Error reading from AVI file",
1778 /* 4 */ "avilib - Error writing to AVI file",
1779 /* 5 */ "avilib - Error writing index (file may still be useable)",
1780 /* 6 */ "avilib - Error closing AVI file",
1781 /* 7 */ "avilib - Operation (read/write) not permitted",
1782 /* 8 */ "avilib - Out of memory (malloc failed)",
1783 /* 9 */ "avilib - Not an AVI file",
1784 /* 10 */ "avilib - AVI file has no header list (corrupted?)",
1785 /* 11 */ "avilib - AVI file has no MOVI list (corrupted?)",
1786 /* 12 */ "avilib - AVI file has no video data",
1787 /* 13 */ "avilib - operation needs an index",
1788 /* 14 */ "avilib - Unkown Error"
1789 };
1790 static int num_avi_errors = sizeof(avi_errors)/sizeof(char*);
1791
1792 static char error_string[4096];
1793
AVI_print_error(char * str)1794 void AVI_print_error(char *str)
1795 {
1796 int aerrno;
1797
1798 aerrno = (AVI_errno>=0 && AVI_errno<num_avi_errors) ? AVI_errno : num_avi_errors-1;
1799
1800 fprintf(stderr,"%s: %s\n",str,avi_errors[aerrno]);
1801
1802 /* for the following errors, perror should report a more detailed reason: */
1803
1804 if(AVI_errno == AVI_ERR_OPEN ||
1805 AVI_errno == AVI_ERR_READ ||
1806 AVI_errno == AVI_ERR_WRITE ||
1807 AVI_errno == AVI_ERR_WRITE_INDEX ||
1808 AVI_errno == AVI_ERR_CLOSE )
1809 {
1810 perror("REASON");
1811 }
1812 }
1813
AVI_strerror()1814 char *AVI_strerror()
1815 {
1816 int aerrno;
1817
1818 aerrno = (AVI_errno>=0 && AVI_errno<num_avi_errors) ? AVI_errno : num_avi_errors-1;
1819
1820 if(AVI_errno == AVI_ERR_OPEN ||
1821 AVI_errno == AVI_ERR_READ ||
1822 AVI_errno == AVI_ERR_WRITE ||
1823 AVI_errno == AVI_ERR_WRITE_INDEX ||
1824 AVI_errno == AVI_ERR_CLOSE )
1825 {
1826 sprintf(error_string,"%s - %s",avi_errors[aerrno],strerror(errno));
1827 return error_string;
1828 }
1829 else
1830 {
1831 return avi_errors[aerrno];
1832 }
1833 }
1834
AVI_max_size()1835 uint64_t AVI_max_size()
1836 {
1837 return((uint64_t) AVI_MAX_LEN);
1838 }
1839
1840