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