123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839 |
- /*
- * avilib.c
- *
- * Copyright (C) Thomas Östreich - June 2001
- * multiple audio track support Copyright (C) 2002 Thomas Östreich
- *
- * Original code:
- * Copyright (C) 1999 Rainer Johanni <Rainer@Johanni.de>
- *
- * This file is part of transcode, a linux video stream processing tool
- *
- * transcode is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2, or (at your option)
- * any later version.
- *
- * transcode is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with GNU Make; see the file COPYING. If not, write to
- * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
- *
- */
- #include "avilib.h"
- //#include <time.h>
- #define INFO_LIST
- /* The following variable indicates the kind of error */
- long AVI_errno;
- #define MAX_INFO_STRLEN 64
- static char id_str[MAX_INFO_STRLEN];
- #define FRAME_RATE_SCALE 1000000
- #ifndef PACKAGE
- #define PACKAGE "my"
- #define VERSION "0.00"
- #endif
- #ifndef O_BINARY
- /* win32 wants a binary flag to open(); this sets it to null
- on platforms that don't have it. */
- #define O_BINARY 0
- #endif
- /*******************************************************************
- * *
- * Utilities for writing an AVI File *
- * *
- *******************************************************************/
- static size_t avi_read(int fd, char *buf, size_t len)
- {
- size_t n = 0;
- size_t r = 0;
- while (r < len) {
- n = read (fd, buf + r, len - r);
- if (n <= 0)
- return r;
- r += n;
- }
- return r;
- }
- static size_t avi_write (int fd, char *buf, size_t len)
- {
- size_t n = 0;
- size_t r = 0;
- while (r < len) {
- n = write (fd, buf + r, len - r);
- if (n < 0)
- return n;
-
- r += n;
- }
- return r;
- }
- /* HEADERBYTES: The number of bytes to reserve for the header */
- #define HEADERBYTES 2048
- /* AVI_MAX_LEN: The maximum length of an AVI file, we stay a bit below
- the 2GB limit (Remember: 2*10^9 is smaller than 2 GB) */
- #define AVI_MAX_LEN (UINT_MAX-(1<<20)*16-HEADERBYTES)
- #define PAD_EVEN(x) ( ((x)+1) & ~1 )
- /* Copy n into dst as a 4 byte, little endian number.
- Should also work on big endian machines */
- static void long2str(unsigned char *dst, int n)
- {
- dst[0] = (n )&0xff;
- dst[1] = (n>> 8)&0xff;
- dst[2] = (n>>16)&0xff;
- dst[3] = (n>>24)&0xff;
- }
- /* Convert a string of 4 or 2 bytes to a number,
- also working on big endian machines */
- static unsigned long str2ulong(unsigned char *str)
- {
- return ( str[0] | (str[1]<<8) | (str[2]<<16) | (str[3]<<24) );
- }
- static unsigned long str2ushort(unsigned char *str)
- {
- return ( str[0] | (str[1]<<8) );
- }
- /* Calculate audio sample size from number of bits and number of channels.
- This may have to be adjusted for eg. 12 bits and stereo */
- static int avi_sampsize(avi_t *AVI, int j)
- {
- int s;
- s = ((AVI->track[j].a_bits+7)/8)*AVI->track[j].a_chans;
- // if(s==0) s=1; /* avoid possible zero divisions */
- if(s<4) s=4; /* avoid possible zero divisions */
- return s;
- }
- /* Add a chunk (=tag and data) to the AVI file,
- returns -1 on write error, 0 on success */
- static int avi_add_chunk(avi_t *AVI, unsigned char *tag, unsigned char *data, int length)
- {
- unsigned char c[8];
- /* Copy tag and length int c, so that we need only 1 write system call
- for these two values */
- memcpy(c,tag,4);
- long2str(c+4,length);
- /* Output tag, length and data, restore previous position
- if the write fails */
- length = PAD_EVEN(length);
- if( avi_write(AVI->fdes,(char *)c,8) != 8 ||
- avi_write(AVI->fdes,(char *)data,length) != length )
- {
- lseek(AVI->fdes,AVI->pos,SEEK_SET);
- AVI_errno = AVI_ERR_WRITE;
- return -1;
- }
- /* Update file position */
- AVI->pos += 8 + length;
- //fprintf(stderr, "pos=%lu %s\n", AVI->pos, tag);
- return 0;
- }
- static int avi_add_index_entry(avi_t *AVI, unsigned char *tag, long flags, unsigned long pos, unsigned long len)
- {
- void *ptr;
- if(AVI->n_idx>=AVI->max_idx) {
- ptr = realloc((void *)AVI->idx,(AVI->max_idx+4096)*16);
-
- if(ptr == 0) {
- AVI_errno = AVI_ERR_NO_MEM;
- return -1;
- }
- AVI->max_idx += 4096;
- AVI->idx = (unsigned char((*)[16]) ) ptr;
- }
-
- /* Add index entry */
- // fprintf(stderr, "INDEX %s %ld %lu %lu\n", tag, flags, pos, len);
- memcpy(AVI->idx[AVI->n_idx],tag,4);
- long2str(AVI->idx[AVI->n_idx]+ 4,flags);
- long2str(AVI->idx[AVI->n_idx]+ 8, pos);
- long2str(AVI->idx[AVI->n_idx]+12, len);
-
- /* Update counter */
- AVI->n_idx++;
- if(len>AVI->max_len) AVI->max_len=len;
- return 0;
- }
- /*
- AVI_open_output_file: Open an AVI File and write a bunch
- of zero bytes as space for the header.
- returns a pointer to avi_t on success, a zero pointer on error
- */
- avi_t* AVI_open_output_file(char * filename)
- {
- avi_t *AVI;
- int i;
- int mask = 0;
-
- unsigned char AVI_header[HEADERBYTES];
- /* Allocate the avi_t struct and zero it */
- AVI = (avi_t *) malloc(sizeof(avi_t));
- if(AVI==0)
- {
- AVI_errno = AVI_ERR_NO_MEM;
- return 0;
- }
- memset((void *)AVI,0,sizeof(avi_t));
- /* Since Linux needs a long time when deleting big files,
- we do not truncate the file when we open it.
- Instead it is truncated when the AVI file is closed */
- /* mask = umask (0);
- umask (mask);*/
- AVI->fdes = open(filename, O_RDWR|O_CREAT|O_BINARY, 0644 &~ mask);
- if (AVI->fdes < 0)
- {
- AVI_errno = AVI_ERR_OPEN;
- free(AVI);
- return 0;
- }
- /* Write out HEADERBYTES bytes, the header will go here
- when we are finished with writing */
- for (i=0;i<HEADERBYTES;i++) AVI_header[i] = 0;
- i = avi_write(AVI->fdes,(char *)AVI_header,HEADERBYTES);
- if (i != HEADERBYTES)
- {
- close(AVI->fdes);
- AVI_errno = AVI_ERR_WRITE;
- free(AVI);
- return 0;
- }
- AVI->pos = HEADERBYTES;
- AVI->mode = AVI_MODE_WRITE; /* open for writing */
- //init
- AVI->anum = 0;
- AVI->aptr = 0;
- return AVI;
- }
- void AVI_set_video(avi_t *AVI, int width, int height, double fps, char *compressor)
- {
- /* may only be called if file is open for writing */
- if(AVI->mode==AVI_MODE_READ) return;
- AVI->width = width;
- AVI->height = height;
- AVI->fps = fps;
-
- if(strncmp(compressor, "RGB", 3)==0) {
- memset(AVI->compressor, 0, 4);
- } else {
- memcpy(AVI->compressor,compressor,4);
- }
-
- AVI->compressor[4] = 0;
- avi_update_header(AVI);
- }
- void AVI_set_audio(avi_t *AVI, int channels, long rate, int bits, int format, long mp3rate)
- {
- /* may only be called if file is open for writing */
- if(AVI->mode==AVI_MODE_READ) return;
- //inc audio tracks
- AVI->aptr=AVI->anum;
- ++AVI->anum;
- if(AVI->anum > AVI_MAX_TRACKS) {
- fprintf(stderr, "error - only %d audio tracks supported\n", AVI_MAX_TRACKS);
- exit(1);
- }
- AVI->track[AVI->aptr].a_chans = channels;
- AVI->track[AVI->aptr].a_rate = rate;
- AVI->track[AVI->aptr].a_bits = bits;
- AVI->track[AVI->aptr].a_fmt = format;
- AVI->track[AVI->aptr].mp3rate = mp3rate;
- avi_update_header(AVI);
- }
- #define OUT4CC(s) \
- if(nhb<=HEADERBYTES-4) memcpy(AVI_header+nhb,s,4); nhb += 4
- #define OUTLONG(n) \
- if(nhb<=HEADERBYTES-4) long2str(AVI_header+nhb,n); nhb += 4
- #define OUTSHRT(n) \
- if(nhb<=HEADERBYTES-2) { \
- AVI_header[nhb ] = (n )&0xff; \
- AVI_header[nhb+1] = (n>>8)&0xff; \
- } \
- nhb += 2
- //ThOe write preliminary AVI file header: 0 frames, max vid/aud size
- int avi_update_header(avi_t *AVI)
- {
- int njunk, sampsize, hasIndex, ms_per_frame, frate, flag;
- int movi_len, hdrl_start, strl_start, j;
- unsigned char AVI_header[HEADERBYTES];
- long nhb;
- //assume max size
- movi_len = AVI_MAX_LEN - HEADERBYTES + 4;
- //assume index will be written
- hasIndex=1;
- if(AVI->fps < 0.001) {
- frate=0;
- ms_per_frame=0;
- } else {
- frate = (int) (FRAME_RATE_SCALE*AVI->fps + 0.5);
- ms_per_frame=(int) (1000000/AVI->fps + 0.5);
- }
- /* Prepare the file header */
- nhb = 0;
- /* The RIFF header */
- OUT4CC ("RIFF");
- OUTLONG(movi_len); // assume max size
- OUT4CC ("AVI ");
- /* Start the header list */
- OUT4CC ("LIST");
- OUTLONG(0); /* Length of list in bytes, don't know yet */
- hdrl_start = nhb; /* Store start position */
- OUT4CC ("hdrl");
- /* The main AVI header */
- /* The Flags in AVI File header */
- #define AVIF_HASINDEX 0x00000010 /* Index at end of file */
- #define AVIF_MUSTUSEINDEX 0x00000020
- #define AVIF_ISINTERLEAVED 0x00000100
- #define AVIF_TRUSTCKTYPE 0x00000800 /* Use CKType to find key frames */
- #define AVIF_WASCAPTUREFILE 0x00010000
- #define AVIF_COPYRIGHTED 0x00020000
- OUT4CC ("avih");
- OUTLONG(56); /* # of bytes to follow */
- OUTLONG(ms_per_frame); /* Microseconds per frame */
- //ThOe ->0
- // OUTLONG(10000000); /* MaxBytesPerSec, I hope this will never be used */
- OUTLONG(0);
- OUTLONG(0); /* PaddingGranularity (whatever that might be) */
- /* Other sources call it 'reserved' */
- flag = AVIF_ISINTERLEAVED;
- if(hasIndex) flag |= AVIF_HASINDEX;
- if(hasIndex && AVI->must_use_index) flag |= AVIF_MUSTUSEINDEX;
- OUTLONG(flag); /* Flags */
- OUTLONG(0); // no frames yet
- OUTLONG(0); /* InitialFrames */
- OUTLONG(AVI->anum+1);
- OUTLONG(0); /* SuggestedBufferSize */
- OUTLONG(AVI->width); /* Width */
- OUTLONG(AVI->height); /* Height */
- /* MS calls the following 'reserved': */
- OUTLONG(0); /* TimeScale: Unit used to measure time */
- OUTLONG(0); /* DataRate: Data rate of playback */
- OUTLONG(0); /* StartTime: Starting time of AVI data */
- OUTLONG(0); /* DataLength: Size of AVI data chunk */
- /* Start the video stream list ---------------------------------- */
- OUT4CC ("LIST");
- OUTLONG(0); /* Length of list in bytes, don't know yet */
- strl_start = nhb; /* Store start position */
- OUT4CC ("strl");
- /* The video stream header */
- OUT4CC ("strh");
- OUTLONG(56); /* # of bytes to follow */
- OUT4CC ("vids"); /* Type */
- OUT4CC (AVI->compressor); /* Handler */
- OUTLONG(0); /* Flags */
- OUTLONG(0); /* Reserved, MS says: wPriority, wLanguage */
- OUTLONG(0); /* InitialFrames */
- OUTLONG(FRAME_RATE_SCALE); /* Scale */
- OUTLONG(frate); /* Rate: Rate/Scale == samples/second */
- OUTLONG(0); /* Start */
- OUTLONG(0); // no frames yet
- OUTLONG(0); /* SuggestedBufferSize */
- OUTLONG(-1); /* Quality */
- OUTLONG(0); /* SampleSize */
- OUTLONG(0); /* Frame */
- OUTLONG(0); /* Frame */
- // OUTLONG(0); /* Frame */
- //OUTLONG(0); /* Frame */
- /* The video stream format */
- OUT4CC ("strf");
- OUTLONG(40); /* # of bytes to follow */
- OUTLONG(40); /* Size */
- OUTLONG(AVI->width); /* Width */
- OUTLONG(AVI->height); /* Height */
- OUTSHRT(1); OUTSHRT(24); /* Planes, Count */
- OUT4CC (AVI->compressor); /* Compression */
- // ThOe (*3)
- OUTLONG(AVI->width*AVI->height*3); /* SizeImage (in bytes?) */
- OUTLONG(0); /* XPelsPerMeter */
- OUTLONG(0); /* YPelsPerMeter */
- OUTLONG(0); /* ClrUsed: Number of colors used */
- OUTLONG(0); /* ClrImportant: Number of colors important */
- /* Finish stream list, i.e. put number of bytes in the list to proper pos */
- long2str(AVI_header+strl_start-4,nhb-strl_start);
-
- /* Start the audio stream list ---------------------------------- */
-
- for(j=0; j<AVI->anum; ++j) {
-
- sampsize = avi_sampsize(AVI, j);
-
- OUT4CC ("LIST");
- OUTLONG(0); /* Length of list in bytes, don't know yet */
- strl_start = nhb; /* Store start position */
- OUT4CC ("strl");
-
- /* The audio stream header */
-
- OUT4CC ("strh");
- OUTLONG(56); /* # of bytes to follow */
- OUT4CC ("auds");
-
- // -----------
- // ThOe
- OUTLONG(0); /* Format (Optionally) */
- // -----------
-
- OUTLONG(0); /* Flags */
- OUTLONG(0); /* Reserved, MS says: wPriority, wLanguage */
- OUTLONG(0); /* InitialFrames */
-
- // ThOe /4
- OUTLONG(sampsize/4); /* Scale */
- OUTLONG(1000*AVI->track[j].mp3rate/8);
- OUTLONG(0); /* Start */
- OUTLONG(4*AVI->track[j].audio_bytes/sampsize); /* Length */
- OUTLONG(0); /* SuggestedBufferSize */
- OUTLONG(-1); /* Quality */
-
- // ThOe /4
- OUTLONG(sampsize/4); /* SampleSize */
-
- OUTLONG(0); /* Frame */
- OUTLONG(0); /* Frame */
- // OUTLONG(0); /* Frame */
- //OUTLONG(0); /* Frame */
-
- /* The audio stream format */
-
- OUT4CC ("strf");
- OUTLONG(16); /* # of bytes to follow */
- OUTSHRT(AVI->track[j].a_fmt); /* Format */
- OUTSHRT(AVI->track[j].a_chans); /* Number of channels */
- OUTLONG(AVI->track[j].a_rate); /* SamplesPerSec */
- // ThOe
- OUTLONG(1000*AVI->track[j].mp3rate/8);
- //ThOe (/4)
-
- OUTSHRT(sampsize/4); /* BlockAlign */
-
-
- OUTSHRT(AVI->track[j].a_bits); /* BitsPerSample */
-
- /* Finish stream list, i.e. put number of bytes in the list to proper pos */
-
- long2str(AVI_header+strl_start-4,nhb-strl_start);
- }
-
- /* Finish header list */
-
- long2str(AVI_header+hdrl_start-4,nhb-hdrl_start);
-
-
- /* Calculate the needed amount of junk bytes, output junk */
-
- njunk = HEADERBYTES - nhb - 8 - 12;
-
- /* Safety first: if njunk <= 0, somebody has played with
- HEADERBYTES without knowing what (s)he did.
- This is a fatal error */
-
- if(njunk<=0)
- {
- fprintf(stderr,"AVI_close_output_file: # of header bytes too small\n");
- exit(1);
- }
-
- OUT4CC ("JUNK");
- OUTLONG(njunk);
- memset(AVI_header+nhb,0,njunk);
-
- //11/14/01 added id string
- if(njunk > strlen(id_str)+8) {
- sprintf(id_str, "%s-%s", PACKAGE, VERSION);
- memcpy(AVI_header+nhb, id_str, strlen(id_str));
- }
-
- nhb += njunk;
- /* Start the movi list */
- OUT4CC ("LIST");
- OUTLONG(movi_len); /* Length of list in bytes */
- OUT4CC ("movi");
- /* Output the header, truncate the file to the number of bytes
- actually written, report an error if someting goes wrong */
- if ( lseek(AVI->fdes,0,SEEK_SET)<0 ||
- avi_write(AVI->fdes,(char *)AVI_header,HEADERBYTES)!=HEADERBYTES ||
- lseek(AVI->fdes,AVI->pos,SEEK_SET)<0)
- {
- AVI_errno = AVI_ERR_CLOSE;
- return -1;
- }
- return 0;
- }
- /*
- Write the header of an AVI file and close it.
- returns 0 on success, -1 on write error.
- */
- static int avi_close_output_file(avi_t *AVI)
- {
- int ret, njunk, sampsize, hasIndex, ms_per_frame, frate, idxerror, flag;
- unsigned long movi_len;
- int hdrl_start, strl_start, j;
- unsigned char AVI_header[HEADERBYTES];
- long nhb;
- #ifdef INFO_LIST
- long info_len;
- // time_t calptr;
- #endif
- /* Calculate length of movi list */
- movi_len = AVI->pos - HEADERBYTES + 4;
- /* Try to ouput the index entries. This may fail e.g. if no space
- is left on device. We will report this as an error, but we still
- try to write the header correctly (so that the file still may be
- readable in the most cases */
- idxerror = 0;
- // fprintf(stderr, "pos=%lu, index_len=%ld \n", AVI->pos, AVI->n_idx*16);
- ret = avi_add_chunk(AVI, (unsigned char *)"idx1", (void*)AVI->idx, AVI->n_idx*16);
- hasIndex = (ret==0);
- //fprintf(stderr, "pos=%lu, index_len=%d\n", AVI->pos, hasIndex);
- if(ret) {
- idxerror = 1;
- AVI_errno = AVI_ERR_WRITE_INDEX;
- }
-
- /* Calculate Microseconds per frame */
- if(AVI->fps < 0.001) {
- frate=0;
- ms_per_frame=0;
- } else {
- frate = (int) (FRAME_RATE_SCALE*AVI->fps + 0.5);
- ms_per_frame=(int) (1000000/AVI->fps + 0.5);
- }
- /* Prepare the file header */
- nhb = 0;
- /* The RIFF header */
- OUT4CC ("RIFF");
- OUTLONG(AVI->pos - 8); /* # of bytes to follow */
- OUT4CC ("AVI ");
- /* Start the header list */
- OUT4CC ("LIST");
- OUTLONG(0); /* Length of list in bytes, don't know yet */
- hdrl_start = nhb; /* Store start position */
- OUT4CC ("hdrl");
- /* The main AVI header */
- /* The Flags in AVI File header */
- #define AVIF_HASINDEX 0x00000010 /* Index at end of file */
- #define AVIF_MUSTUSEINDEX 0x00000020
- #define AVIF_ISINTERLEAVED 0x00000100
- #define AVIF_TRUSTCKTYPE 0x00000800 /* Use CKType to find key frames */
- #define AVIF_WASCAPTUREFILE 0x00010000
- #define AVIF_COPYRIGHTED 0x00020000
- OUT4CC ("avih");
- OUTLONG(56); /* # of bytes to follow */
- OUTLONG(ms_per_frame); /* Microseconds per frame */
- //ThOe ->0
- // OUTLONG(10000000); /* MaxBytesPerSec, I hope this will never be used */
- OUTLONG(0);
- OUTLONG(0); /* PaddingGranularity (whatever that might be) */
- /* Other sources call it 'reserved' */
- flag = AVIF_ISINTERLEAVED;
- if(hasIndex) flag |= AVIF_HASINDEX;
- if(hasIndex && AVI->must_use_index) flag |= AVIF_MUSTUSEINDEX;
- OUTLONG(flag); /* Flags */
- OUTLONG(AVI->video_frames); /* TotalFrames */
- OUTLONG(0); /* InitialFrames */
- OUTLONG(AVI->anum+1);
- // if (AVI->track[0].audio_bytes)
- // { OUTLONG(2); } /* Streams */
- // else
- // { OUTLONG(1); } /* Streams */
- OUTLONG(0); /* SuggestedBufferSize */
- OUTLONG(AVI->width); /* Width */
- OUTLONG(AVI->height); /* Height */
- /* MS calls the following 'reserved': */
- OUTLONG(0); /* TimeScale: Unit used to measure time */
- OUTLONG(0); /* DataRate: Data rate of playback */
- OUTLONG(0); /* StartTime: Starting time of AVI data */
- OUTLONG(0); /* DataLength: Size of AVI data chunk */
- /* Start the video stream list ---------------------------------- */
- OUT4CC ("LIST");
- OUTLONG(0); /* Length of list in bytes, don't know yet */
- strl_start = nhb; /* Store start position */
- OUT4CC ("strl");
- /* The video stream header */
- OUT4CC ("strh");
- OUTLONG(56); /* # of bytes to follow */
- OUT4CC ("vids"); /* Type */
- OUT4CC (AVI->compressor); /* Handler */
- OUTLONG(0); /* Flags */
- OUTLONG(0); /* Reserved, MS says: wPriority, wLanguage */
- OUTLONG(0); /* InitialFrames */
- OUTLONG(FRAME_RATE_SCALE); /* Scale */
- OUTLONG(frate); /* Rate: Rate/Scale == samples/second */
- OUTLONG(0); /* Start */
- OUTLONG(AVI->video_frames); /* Length */
- OUTLONG(0); /* SuggestedBufferSize */
- OUTLONG(-1); /* Quality */
- OUTLONG(0); /* SampleSize */
- OUTLONG(0); /* Frame */
- OUTLONG(0); /* Frame */
- // OUTLONG(0); /* Frame */
- //OUTLONG(0); /* Frame */
- /* The video stream format */
- OUT4CC ("strf");
- OUTLONG(40); /* # of bytes to follow */
- OUTLONG(40); /* Size */
- OUTLONG(AVI->width); /* Width */
- OUTLONG(AVI->height); /* Height */
- OUTSHRT(1); OUTSHRT(24); /* Planes, Count */
- OUT4CC (AVI->compressor); /* Compression */
- // ThOe (*3)
- OUTLONG(AVI->width*AVI->height*3); /* SizeImage (in bytes?) */
- OUTLONG(0); /* XPelsPerMeter */
- OUTLONG(0); /* YPelsPerMeter */
- OUTLONG(0); /* ClrUsed: Number of colors used */
- OUTLONG(0); /* ClrImportant: Number of colors important */
- /* Finish stream list, i.e. put number of bytes in the list to proper pos */
- long2str(AVI_header+strl_start-4,nhb-strl_start);
- /* Start the audio stream list ---------------------------------- */
- for(j=0; j<AVI->anum; ++j) {
-
- //if (AVI->track[j].a_chans && AVI->track[j].audio_bytes)
- {
-
- sampsize = avi_sampsize(AVI, j);
-
- OUT4CC ("LIST");
- OUTLONG(0); /* Length of list in bytes, don't know yet */
- strl_start = nhb; /* Store start position */
- OUT4CC ("strl");
-
- /* The audio stream header */
-
- OUT4CC ("strh");
- OUTLONG(56); /* # of bytes to follow */
- OUT4CC ("auds");
-
- // -----------
- // ThOe
- OUTLONG(0); /* Format (Optionally) */
- // -----------
-
- OUTLONG(0); /* Flags */
- OUTLONG(0); /* Reserved, MS says: wPriority, wLanguage */
- OUTLONG(0); /* InitialFrames */
-
- // ThOe /4
- OUTLONG(sampsize/4); /* Scale */
- OUTLONG(1000*AVI->track[j].mp3rate/8);
- OUTLONG(0); /* Start */
- OUTLONG(4*AVI->track[j].audio_bytes/sampsize); /* Length */
- OUTLONG(0); /* SuggestedBufferSize */
- OUTLONG(-1); /* Quality */
-
- // ThOe /4
- OUTLONG(sampsize/4); /* SampleSize */
-
- OUTLONG(0); /* Frame */
- OUTLONG(0); /* Frame */
- // OUTLONG(0); /* Frame */
- //OUTLONG(0); /* Frame */
-
- /* The audio stream format */
-
- OUT4CC ("strf");
- OUTLONG(16); /* # of bytes to follow */
- OUTSHRT(AVI->track[j].a_fmt); /* Format */
- OUTSHRT(AVI->track[j].a_chans); /* Number of channels */
- OUTLONG(AVI->track[j].a_rate); /* SamplesPerSec */
- // ThOe
- OUTLONG(1000*AVI->track[j].mp3rate/8);
- //ThOe (/4)
-
- OUTSHRT(sampsize/4); /* BlockAlign */
-
-
- OUTSHRT(AVI->track[j].a_bits); /* BitsPerSample */
-
- /* Finish stream list, i.e. put number of bytes in the list to proper pos */
- }
- long2str(AVI_header+strl_start-4,nhb-strl_start);
- }
-
- /* Finish header list */
-
- long2str(AVI_header+hdrl_start-4,nhb-hdrl_start);
- // add INFO list --- (0.6.0pre4)
- #ifdef INFO_LIST
- OUT4CC ("LIST");
-
- //FIXME
- info_len = MAX_INFO_STRLEN + 12;
- OUTLONG(info_len);
- OUT4CC ("INFO");
- // OUT4CC ("INAM");
- // OUTLONG(MAX_INFO_STRLEN);
- // sprintf(id_str, "\t");
- // memset(AVI_header+nhb, 0, MAX_INFO_STRLEN);
- // memcpy(AVI_header+nhb, id_str, strlen(id_str));
- // nhb += MAX_INFO_STRLEN;
- OUT4CC ("ISFT");
- OUTLONG(MAX_INFO_STRLEN);
- sprintf(id_str, "%s-%s", PACKAGE, VERSION);
- memset(AVI_header+nhb, 0, MAX_INFO_STRLEN);
- memcpy(AVI_header+nhb, id_str, strlen(id_str));
- nhb += MAX_INFO_STRLEN;
- // OUT4CC ("ICMT");
- // OUTLONG(MAX_INFO_STRLEN);
- // calptr=time(NULL);
- // sprintf(id_str, "\t%s %s", ctime(&calptr), "");
- // memset(AVI_header+nhb, 0, MAX_INFO_STRLEN);
- // memcpy(AVI_header+nhb, id_str, 25);
- // nhb += MAX_INFO_STRLEN;
- #endif
- // ----------------------------
-
- /* Calculate the needed amount of junk bytes, output junk */
-
- njunk = HEADERBYTES - nhb - 8 - 12;
-
- /* Safety first: if njunk <= 0, somebody has played with
- HEADERBYTES without knowing what (s)he did.
- This is a fatal error */
-
- if(njunk<=0)
- {
- fprintf(stderr,"AVI_close_output_file: # of header bytes too small\n");
- exit(1);
- }
- OUT4CC ("JUNK");
- OUTLONG(njunk);
- memset(AVI_header+nhb,0,njunk);
-
- nhb += njunk;
- /* Start the movi list */
- OUT4CC ("LIST");
- OUTLONG(movi_len); /* Length of list in bytes */
- OUT4CC ("movi");
- /* Output the header, truncate the file to the number of bytes
- actually written, report an error if someting goes wrong */
- if ( lseek(AVI->fdes,0,SEEK_SET)<0 ||
- avi_write(AVI->fdes,(char *)AVI_header,HEADERBYTES)!=HEADERBYTES
- //|| ftruncate(AVI->fdes,AVI->pos)<0
- )
- {
- AVI_errno = AVI_ERR_CLOSE;
- return -1;
- }
- if(idxerror) return -1;
- return 0;
- }
- /*
- AVI_write_data:
- Add video or audio data to the file;
- Return values:
- 0 No error;
- -1 Error, AVI_errno is set appropriatly;
- */
- static int avi_write_data(avi_t *AVI, char *data, unsigned long length, int audio, int keyframe)
- {
- int n;
- unsigned char astr[5];
- /* Check for maximum file length */
-
- if ( (AVI->pos + 8 + length + 8 + (AVI->n_idx+1)*16) > AVI_MAX_LEN ) {
- AVI_errno = AVI_ERR_SIZELIM;
- return -1;
- }
-
- /* Add index entry */
- //set tag for current audio track
- sprintf((char *)astr, "0%1dwb", AVI->aptr+1);
- if(audio)
- n = avi_add_index_entry(AVI,astr,0x00,AVI->pos,length);
- else
- n = avi_add_index_entry(AVI,(unsigned char *) "00db",((keyframe)?0x10:0x0),AVI->pos,length);
-
- if(n) return -1;
-
- /* Output tag and data */
-
- if(audio)
- n = avi_add_chunk(AVI,(unsigned char *) astr, (unsigned char *)data,length);
- else
- n = avi_add_chunk(AVI,(unsigned char *)"00db",(unsigned char *)data,length);
-
- if (n) return -1;
-
- return 0;
- }
- int AVI_write_frame(avi_t *AVI, char *data, long bytes, int keyframe)
- {
- unsigned long pos;
-
- if(AVI->mode==AVI_MODE_READ) { AVI_errno = AVI_ERR_NOT_PERM; return -1; }
-
- pos = AVI->pos;
- if(avi_write_data(AVI,data,bytes,0,keyframe)) return -1;
-
- AVI->last_pos = pos;
- AVI->last_len = bytes;
- AVI->video_frames++;
- return 0;
- }
- int AVI_dup_frame(avi_t *AVI)
- {
- if(AVI->mode==AVI_MODE_READ) { AVI_errno = AVI_ERR_NOT_PERM; return -1; }
- if(AVI->last_pos==0) return 0; /* No previous real frame */
- if(avi_add_index_entry(AVI,(unsigned char *)"00db",0x10,AVI->last_pos,AVI->last_len)) return -1;
- AVI->video_frames++;
- AVI->must_use_index = 1;
- return 0;
- }
- int AVI_write_audio(avi_t *AVI, char *data, long bytes)
- {
- if(AVI->mode==AVI_MODE_READ) { AVI_errno = AVI_ERR_NOT_PERM; return -1; }
- if( avi_write_data(AVI,data,bytes,1,0) ) return -1;
- AVI->track[AVI->aptr].audio_bytes += bytes;
- return 0;
- }
- int AVI_append_audio(avi_t *AVI, char *data, long bytes)
- {
- long i, length, pos;
- unsigned char c[4];
- if(AVI->mode==AVI_MODE_READ) { AVI_errno = AVI_ERR_NOT_PERM; return -1; }
-
- // update last index entry:
-
- --AVI->n_idx;
- length = str2ulong(AVI->idx[AVI->n_idx]+12);
- pos = str2ulong(AVI->idx[AVI->n_idx]+8);
- //update;
- long2str(AVI->idx[AVI->n_idx]+12,length+bytes);
- ++AVI->n_idx;
- AVI->track[AVI->aptr].audio_bytes += bytes;
- //update chunk header
- lseek(AVI->fdes, pos+4, SEEK_SET);
- long2str(c, length+bytes);
- avi_write(AVI->fdes,(char *) c, 4);
- lseek(AVI->fdes, pos+8+length, SEEK_SET);
- i=PAD_EVEN(length + bytes);
- bytes = i - length;
- avi_write(AVI->fdes, data, bytes);
- AVI->pos = pos + 8 + i;
- return 0;
- }
- long AVI_bytes_remain(avi_t *AVI)
- {
- if(AVI->mode==AVI_MODE_READ) return 0;
- return ( AVI_MAX_LEN - (AVI->pos + 8 + 16*AVI->n_idx));
- }
- long AVI_bytes_written(avi_t *AVI)
- {
- if(AVI->mode==AVI_MODE_READ) return 0;
- return (AVI->pos + 8 + 16*AVI->n_idx);
- }
- int AVI_set_audio_track(avi_t *AVI, int track)
- {
-
- if(track < 0 || track + 1 > AVI->anum) return(-1);
- //this info is not written to file anyway
- AVI->aptr=track;
- return 0;
- }
- int AVI_get_audio_track(avi_t *AVI)
- {
- return(AVI->aptr);
- }
- /*******************************************************************
- * *
- * Utilities for reading video and audio from an AVI File *
- * *
- *******************************************************************/
- int AVI_close(avi_t *AVI)
- {
- int ret;
- /* If the file was open for writing, the header and index still have
- to be written */
- if(AVI->mode == AVI_MODE_WRITE)
- ret = avi_close_output_file(AVI);
- else
- ret = 0;
- /* Even if there happened an error, we first clean up */
- close(AVI->fdes);
- if(AVI->idx) free(AVI->idx);
- if(AVI->video_index) free(AVI->video_index);
- //FIXME
- //if(AVI->audio_index) free(AVI->audio_index);
- free(AVI);
- return ret;
- }
- #define ERR_EXIT(x) \
- { \
- AVI_close(AVI); \
- AVI_errno = x; \
- return 0; \
- }
- avi_t *AVI_open_input_file(char *filename, int getIndex)
- {
- avi_t *AVI=NULL;
-
- /* Create avi_t structure */
-
- AVI = (avi_t *) malloc(sizeof(avi_t));
- if(AVI==NULL)
- {
- AVI_errno = AVI_ERR_NO_MEM;
- return 0;
- }
- memset((void *)AVI,0,sizeof(avi_t));
-
- AVI->mode = AVI_MODE_READ; /* open for reading */
-
- /* Open the file */
-
- AVI->fdes = open(filename,O_RDONLY|O_BINARY);
- if(AVI->fdes < 0)
- {
- AVI_errno = AVI_ERR_OPEN;
- free(AVI);
- return 0;
- }
-
- avi_parse_input_file(AVI, getIndex);
- AVI->aptr=0; //reset
- return AVI;
- }
- avi_t *AVI_open_fd(int fd, int getIndex)
- {
- avi_t *AVI=NULL;
-
- /* Create avi_t structure */
-
- AVI = (avi_t *) malloc(sizeof(avi_t));
- if(AVI==NULL)
- {
- AVI_errno = AVI_ERR_NO_MEM;
- return 0;
- }
- memset((void *)AVI,0,sizeof(avi_t));
-
- AVI->mode = AVI_MODE_READ; /* open for reading */
-
- // file alread open
- AVI->fdes = fd;
-
- avi_parse_input_file(AVI, getIndex);
- AVI->aptr=0; //reset
-
- return AVI;
- }
- int avi_parse_input_file(avi_t *AVI, int getIndex)
- {
- long i, n, rate, scale, idx_type;
- unsigned char *hdrl_data;
- long header_offset=0, hdrl_len=0;
- long nvi, nai[AVI_MAX_TRACKS], ioff;
- long tot[AVI_MAX_TRACKS];
- int j;
- int lasttag = 0;
- int vids_strh_seen = 0;
- int vids_strf_seen = 0;
- int auds_strh_seen = 0;
- // int auds_strf_seen = 0;
- int num_stream = 0;
- char data[256];
-
- /* Read first 12 bytes and check that this is an AVI file */
- if( avi_read(AVI->fdes,data,12) != 12 ) ERR_EXIT(AVI_ERR_READ)
- if( strncasecmp(data ,"RIFF",4) !=0 ||
- strncasecmp(data+8,"AVI ",4) !=0 ) ERR_EXIT(AVI_ERR_NO_AVI)
- /* Go through the AVI file and extract the header list,
- the start position of the 'movi' list and an optionally
- present idx1 tag */
- hdrl_data = 0;
- while(1)
- {
- if( avi_read(AVI->fdes,data,8) != 8 ) break; /* We assume it's EOF */
- n = str2ulong((unsigned char *) data+4);
- n = PAD_EVEN(n);
- if(strncasecmp(data,"LIST",4) == 0)
- {
- if( avi_read(AVI->fdes,data,4) != 4 ) ERR_EXIT(AVI_ERR_READ)
- n -= 4;
- if(strncasecmp(data,"hdrl",4) == 0)
- {
- hdrl_len = n;
- hdrl_data = (unsigned char *) malloc(n);
- if(hdrl_data==0) ERR_EXIT(AVI_ERR_NO_MEM);
-
- // offset of header
-
- header_offset = lseek(AVI->fdes,0,SEEK_CUR);
-
- if( avi_read(AVI->fdes,(char *)hdrl_data,n) != n ) ERR_EXIT(AVI_ERR_READ)
- }
- else if(strncasecmp(data,"movi",4) == 0)
- {
- AVI->movi_start = lseek(AVI->fdes,0,SEEK_CUR);
- lseek(AVI->fdes,n,SEEK_CUR);
- }
- else
- lseek(AVI->fdes,n,SEEK_CUR);
- }
- else if(strncasecmp(data,"idx1",4) == 0)
- {
- /* n must be a multiple of 16, but the reading does not
- break if this is not the case */
- AVI->n_idx = AVI->max_idx = n/16;
- AVI->idx = (unsigned char((*)[16]) ) malloc(n);
- if(AVI->idx==0) ERR_EXIT(AVI_ERR_NO_MEM)
- if(avi_read(AVI->fdes, (char *) AVI->idx, n) != n ) ERR_EXIT(AVI_ERR_READ)
- }
- else
- lseek(AVI->fdes,n,SEEK_CUR);
- }
- if(!hdrl_data ) ERR_EXIT(AVI_ERR_NO_HDRL)
- if(!AVI->movi_start) ERR_EXIT(AVI_ERR_NO_MOVI)
- /* Interpret the header list */
- for(i=0;i<hdrl_len;)
- {
- /* List tags are completly ignored */
- if(strncasecmp((char *) hdrl_data+i, "LIST",4)==0) { i+= 12; continue; }
- n = str2ulong(hdrl_data+i+4);
- n = PAD_EVEN(n);
- /* Interpret the tag and its args */
- if(strncasecmp((char *)hdrl_data+i,"strh",4)==0)
- {
- i += 8;
- if(strncasecmp((char *)hdrl_data+i,"vids",4) == 0 && !vids_strh_seen)
- {
- memcpy(AVI->compressor,hdrl_data+i+4,4);
- AVI->compressor[4] = 0;
- // ThOe
- AVI->v_codech_off = header_offset + i+4;
- scale = str2ulong((unsigned char *)hdrl_data+i+20);
- rate = str2ulong(hdrl_data+i+24);
- if(scale!=0) AVI->fps = (double)rate/(double)scale;
- AVI->video_frames = str2ulong(hdrl_data+i+32);
- AVI->video_strn = num_stream;
- AVI->max_len = 0;
- vids_strh_seen = 1;
- lasttag = 1; /* vids */
- }
- else if (strncasecmp ((char *) hdrl_data+i,"auds",4) ==0 && ! auds_strh_seen)
- {
- //inc audio tracks
- AVI->aptr=AVI->anum;
- ++AVI->anum;
-
- if(AVI->anum > AVI_MAX_TRACKS) {
- fprintf(stderr, "error - only %d audio tracks supported\n", AVI_MAX_TRACKS);
- return(-1);
- }
-
- AVI->track[AVI->aptr].audio_bytes = str2ulong(hdrl_data+i+32)*avi_sampsize(AVI, 0);
- AVI->track[AVI->aptr].audio_strn = num_stream;
- // auds_strh_seen = 1;
- lasttag = 2; /* auds */
-
- // ThOe
- AVI->track[AVI->aptr].a_codech_off = header_offset + i;
-
- }
- else
- lasttag = 0;
- num_stream++;
- }
- else if(strncasecmp((char *) hdrl_data+i,"strf",4)==0)
- {
- i += 8;
- if(lasttag == 1)
- {
- AVI->width = str2ulong(hdrl_data+i+4);
- AVI->height = str2ulong(hdrl_data+i+8);
- vids_strf_seen = 1;
- //ThOe
- AVI->v_codecf_off = header_offset + i+16;
- memcpy(AVI->compressor2, hdrl_data+i+16, 4);
- AVI->compressor2[4] = 0;
- }
- else if(lasttag == 2)
- {
- AVI->track[AVI->aptr].a_fmt = str2ushort(hdrl_data+i );
- //ThOe
- AVI->track[AVI->aptr].a_codecf_off = header_offset + i;
-
- AVI->track[AVI->aptr].a_chans = str2ushort(hdrl_data+i+2);
- AVI->track[AVI->aptr].a_rate = str2ulong (hdrl_data+i+4);
- //ThOe: read mp3bitrate
- AVI->track[AVI->aptr].mp3rate = 8*str2ulong(hdrl_data+i+8)/1000;
- //:ThOe
- AVI->track[AVI->aptr].a_bits = str2ushort(hdrl_data+i+14);
- // auds_strf_seen = 1;
- }
- lasttag = 0;
- }
- else
- {
- i += 8;
- lasttag = 0;
- }
- i += n;
- }
- free(hdrl_data);
- if(!vids_strh_seen || !vids_strf_seen) ERR_EXIT(AVI_ERR_NO_VIDS)
- AVI->video_tag[0] = AVI->video_strn/10 + '0';
- AVI->video_tag[1] = AVI->video_strn%10 + '0';
- AVI->video_tag[2] = 'd';
- AVI->video_tag[3] = 'b';
- /* Audio tag is set to "99wb" if no audio present */
- if(!AVI->track[0].a_chans) AVI->track[0].audio_strn = 99;
- for(j=0; j<AVI->anum; ++j) {
- AVI->track[j].audio_tag[0] = (j+1)/10 + '0';
- AVI->track[j].audio_tag[1] = (j+1)%10 + '0';
- AVI->track[j].audio_tag[2] = 'w';
- AVI->track[j].audio_tag[3] = 'b';
- }
- lseek(AVI->fdes,AVI->movi_start,SEEK_SET);
- /* get index if wanted */
- if(!getIndex) return(0);
- /* if the file has an idx1, check if this is relative
- to the start of the file or to the start of the movi list */
- idx_type = 0;
- if(AVI->idx)
- {
- long pos, len;
- /* Search the first videoframe in the idx1 and look where
- it is in the file */
- for(i=0;i<AVI->n_idx;i++)
- if( strncasecmp((char *) AVI->idx[i],(char *) AVI->video_tag,3)==0 ) break;
- if(i>=AVI->n_idx) ERR_EXIT(AVI_ERR_NO_VIDS)
- pos = str2ulong(AVI->idx[i]+ 8);
- len = str2ulong(AVI->idx[i]+12);
- lseek(AVI->fdes,pos,SEEK_SET);
- if(avi_read(AVI->fdes,data,8)!=8) ERR_EXIT(AVI_ERR_READ)
- if( strncasecmp((char *)data,(char *)AVI->idx[i],4)==0 &&
- str2ulong((unsigned char *)data+4)==len )
- {
- idx_type = 1; /* Index from start of file */
- }
- else
- {
- lseek(AVI->fdes,pos+AVI->movi_start-4,SEEK_SET);
- if(avi_read(AVI->fdes,data,8)!=8) ERR_EXIT(AVI_ERR_READ)
- if( strncasecmp((char *)data,(char *)AVI->idx[i],4)==0 && str2ulong((unsigned char *)data+4)==len )
- {
- idx_type = 2; /* Index from start of movi list */
- }
- }
- /* idx_type remains 0 if neither of the two tests above succeeds */
- }
- if(idx_type == 0)
- {
- /* we must search through the file to get the index */
- lseek(AVI->fdes, AVI->movi_start, SEEK_SET);
- AVI->n_idx = 0;
- while(1)
- {
- if( avi_read(AVI->fdes,data,8) != 8 ) break;
- n = str2ulong((unsigned char *)data+4);
- /* The movi list may contain sub-lists, ignore them */
- if(strncasecmp(data,"LIST",4)==0)
- {
- lseek(AVI->fdes,4,SEEK_CUR);
- continue;
- }
- /* Check if we got a tag ##db, ##dc or ##wb */
-
- if( ( (data[2]=='d' || data[2]=='D') &&
- (data[3]=='b' || data[3]=='B' || data[3]=='c' || data[3]=='C') )
- || ( (data[2]=='w' || data[2]=='W') &&
- (data[3]=='b' || data[3]=='B') ) )
- {
- avi_add_index_entry(AVI,(unsigned char *) data,0,lseek(AVI->fdes,0,SEEK_CUR)-8,n);
- }
-
- lseek(AVI->fdes,PAD_EVEN(n),SEEK_CUR);
- }
- idx_type = 1;
- }
- /* Now generate the video index and audio index arrays */
- nvi = 0;
- for(j=0; j<AVI->anum; ++j) nai[j] = 0;
- for(i=0;i<AVI->n_idx;i++) {
-
- if(strncasecmp((char *)AVI->idx[i],(char *) AVI->video_tag,3) == 0) nvi++;
-
- for(j=0; j<AVI->anum; ++j) if(strncasecmp((char *)AVI->idx[i], AVI->track[j].audio_tag,4) == 0) nai[j]++;
- }
-
- AVI->video_frames = nvi;
- for(j=0; j<AVI->anum; ++j) AVI->track[j].audio_chunks = nai[j];
- // fprintf(stderr, "chunks = %ld %d %s\n", AVI->track[0].audio_chunks, AVI->anum, AVI->track[0].audio_tag);
- if(AVI->video_frames==0) ERR_EXIT(AVI_ERR_NO_VIDS);
- AVI->video_index = (video_index_entry *) malloc(nvi*sizeof(video_index_entry));
- if(AVI->video_index==0) ERR_EXIT(AVI_ERR_NO_MEM);
-
- for(j=0; j<AVI->anum; ++j) {
- if(AVI->track[j].audio_chunks) {
- AVI->track[j].audio_index = (audio_index_entry *) malloc(nai[j]*sizeof(audio_index_entry));
- if(AVI->track[j].audio_index==0) ERR_EXIT(AVI_ERR_NO_MEM);
- }
- }
-
- nvi = 0;
- for(j=0; j<AVI->anum; ++j) nai[j] = tot[j] = 0;
-
- ioff = idx_type == 1 ? 8 : AVI->movi_start+4;
-
- for(i=0;i<AVI->n_idx;i++) {
- //video
- if(strncasecmp((char *)AVI->idx[i],(char *)AVI->video_tag,3) == 0) {
- AVI->video_index[nvi].key = str2ulong(AVI->idx[i]+ 4);
- AVI->video_index[nvi].pos = str2ulong(AVI->idx[i]+ 8)+ioff;
- AVI->video_index[nvi].len = str2ulong(AVI->idx[i]+12);
- nvi++;
- }
-
- //audio
- for(j=0; j<AVI->anum; ++j) {
-
- if(strncasecmp((char *)AVI->idx[i],AVI->track[j].audio_tag,4) == 0) {
- AVI->track[j].audio_index[nai[j]].pos = str2ulong(AVI->idx[i]+ 8)+ioff;
- AVI->track[j].audio_index[nai[j]].len = str2ulong(AVI->idx[i]+12);
- AVI->track[j].audio_index[nai[j]].tot = tot[j];
- tot[j] += AVI->track[j].audio_index[nai[j]].len;
- nai[j]++;
- }
- }
- }
-
-
- for(j=0; j<AVI->anum; ++j) AVI->track[j].audio_bytes = tot[j];
-
- /* Reposition the file */
-
- lseek(AVI->fdes,AVI->movi_start,SEEK_SET);
- AVI->video_pos = 0;
- return(0);
- }
- long AVI_video_frames(avi_t *AVI)
- {
- return AVI->video_frames;
- }
- int AVI_video_width(avi_t *AVI)
- {
- return AVI->width;
- }
- int AVI_video_height(avi_t *AVI)
- {
- return AVI->height;
- }
- double AVI_frame_rate(avi_t *AVI)
- {
- return AVI->fps;
- }
- char* AVI_video_compressor(avi_t *AVI)
- {
- return AVI->compressor2;
- }
- long AVI_max_video_chunk(avi_t *AVI)
- {
- return AVI->max_len;
- }
- int AVI_audio_tracks(avi_t *AVI)
- {
- return(AVI->anum);
- }
- int AVI_audio_channels(avi_t *AVI)
- {
- return AVI->track[AVI->aptr].a_chans;
- }
- long AVI_audio_mp3rate(avi_t *AVI)
- {
- return AVI->track[AVI->aptr].mp3rate;
- }
- int AVI_audio_bits(avi_t *AVI)
- {
- return AVI->track[AVI->aptr].a_bits;
- }
- int AVI_audio_format(avi_t *AVI)
- {
- return AVI->track[AVI->aptr].a_fmt;
- }
- long AVI_audio_rate(avi_t *AVI)
- {
- return AVI->track[AVI->aptr].a_rate;
- }
- long AVI_audio_bytes(avi_t *AVI)
- {
- return AVI->track[AVI->aptr].audio_bytes;
- }
- long AVI_audio_chunks(avi_t *AVI)
- {
- return AVI->track[AVI->aptr].audio_chunks;
- }
- long AVI_audio_codech_offset(avi_t *AVI)
- {
- return AVI->track[AVI->aptr].a_codech_off;
- }
- long AVI_audio_codecf_offset(avi_t *AVI)
- {
- return AVI->track[AVI->aptr].a_codecf_off;
- }
- long AVI_video_codech_offset(avi_t *AVI)
- {
- return AVI->v_codech_off;
- }
- long AVI_video_codecf_offset(avi_t *AVI)
- {
- return AVI->v_codecf_off;
- }
- long AVI_frame_size(avi_t *AVI, long frame)
- {
- if(AVI->mode==AVI_MODE_WRITE) { AVI_errno = AVI_ERR_NOT_PERM; return -1; }
- if(!AVI->video_index) { AVI_errno = AVI_ERR_NO_IDX; return -1; }
- if(frame < 0 || frame >= AVI->video_frames) return 0;
- return(AVI->video_index[frame].len);
- }
- long AVI_audio_size(avi_t *AVI, long frame)
- {
- if(AVI->mode==AVI_MODE_WRITE) { AVI_errno = AVI_ERR_NOT_PERM; return -1; }
- if(!AVI->track[AVI->aptr].audio_index) { AVI_errno = AVI_ERR_NO_IDX; return -1; }
-
- if(frame < 0 || frame >= AVI->track[AVI->aptr].audio_chunks) return 0;
- return(AVI->track[AVI->aptr].audio_index[frame].len);
- }
- long AVI_get_video_position(avi_t *AVI, long frame)
- {
- if(AVI->mode==AVI_MODE_WRITE) { AVI_errno = AVI_ERR_NOT_PERM; return -1; }
- if(!AVI->video_index) { AVI_errno = AVI_ERR_NO_IDX; return -1; }
- if(frame < 0 || frame >= AVI->video_frames) return 0;
- return(AVI->video_index[frame].pos);
- }
- int AVI_seek_start(avi_t *AVI)
- {
- if(AVI->mode==AVI_MODE_WRITE) { AVI_errno = AVI_ERR_NOT_PERM; return -1; }
- lseek(AVI->fdes,AVI->movi_start,SEEK_SET);
- AVI->video_pos = 0;
- return 0;
- }
- int AVI_set_video_position(avi_t *AVI, long frame)
- {
- if(AVI->mode==AVI_MODE_WRITE) { AVI_errno = AVI_ERR_NOT_PERM; return -1; }
- if(!AVI->video_index) { AVI_errno = AVI_ERR_NO_IDX; return -1; }
- if (frame < 0 ) frame = 0;
- AVI->video_pos = frame;
- return 0;
- }
- int AVI_set_audio_bitrate(avi_t *AVI, long bitrate)
- {
- if(AVI->mode==AVI_MODE_READ) { AVI_errno = AVI_ERR_NOT_PERM; return -1; }
- AVI->track[AVI->aptr].mp3rate = bitrate;
- return 0;
- }
-
- long AVI_read_frame(avi_t *AVI, char *vidbuf, int *keyframe)
- {
- long n;
- if(AVI->mode==AVI_MODE_WRITE) { AVI_errno = AVI_ERR_NOT_PERM; return -1; }
- if(!AVI->video_index) { AVI_errno = AVI_ERR_NO_IDX; return -1; }
- if(AVI->video_pos < 0 || AVI->video_pos >= AVI->video_frames) return -1;
- n = AVI->video_index[AVI->video_pos].len;
- *keyframe = (AVI->video_index[AVI->video_pos].key==0x10) ? 1:0;
- lseek(AVI->fdes, AVI->video_index[AVI->video_pos].pos, SEEK_SET);
- if (avi_read(AVI->fdes,vidbuf,n) != n)
- {
- AVI_errno = AVI_ERR_READ;
- return -1;
- }
- AVI->video_pos++;
- return n;
- }
- int AVI_set_audio_position(avi_t *AVI, long byte)
- {
- long n0, n1, n;
- if(AVI->mode==AVI_MODE_WRITE) { AVI_errno = AVI_ERR_NOT_PERM; return -1; }
- if(!AVI->track[AVI->aptr].audio_index) { AVI_errno = AVI_ERR_NO_IDX; return -1; }
- if(byte < 0) byte = 0;
- /* Binary search in the audio chunks */
- n0 = 0;
- n1 = AVI->track[AVI->aptr].audio_chunks;
- while(n0<n1-1)
- {
- n = (n0+n1)/2;
- if(AVI->track[AVI->aptr].audio_index[n].tot>byte)
- n1 = n;
- else
- n0 = n;
- }
- AVI->track[AVI->aptr].audio_posc = n0;
- AVI->track[AVI->aptr].audio_posb = byte - AVI->track[AVI->aptr].audio_index[n0].tot;
- return 0;
- }
- long AVI_read_audio(avi_t *AVI, char *audbuf, long bytes)
- {
- long nr, pos, left, todo;
- if(AVI->mode==AVI_MODE_WRITE) { AVI_errno = AVI_ERR_NOT_PERM; return -1; }
- if(!AVI->track[AVI->aptr].audio_index) { AVI_errno = AVI_ERR_NO_IDX; return -1; }
- nr = 0; /* total number of bytes read */
- while(bytes>0)
- {
- left = AVI->track[AVI->aptr].audio_index[AVI->track[AVI->aptr].audio_posc].len - AVI->track[AVI->aptr].audio_posb;
- if(left==0)
- {
- if(AVI->track[AVI->aptr].audio_posc>=AVI->track[AVI->aptr].audio_chunks-1) return nr;
- AVI->track[AVI->aptr].audio_posc++;
- AVI->track[AVI->aptr].audio_posb = 0;
- continue;
- }
- if(bytes<left)
- todo = bytes;
- else
- todo = left;
- pos = AVI->track[AVI->aptr].audio_index[AVI->track[AVI->aptr].audio_posc].pos + AVI->track[AVI->aptr].audio_posb;
- lseek(AVI->fdes, pos, SEEK_SET);
- if (avi_read(AVI->fdes,audbuf+nr,todo) != todo)
- {
- AVI_errno = AVI_ERR_READ;
- return -1;
- }
- bytes -= todo;
- nr += todo;
- AVI->track[AVI->aptr].audio_posb += todo;
- }
- return nr;
- }
- /* AVI_read_data: Special routine for reading the next audio or video chunk
- without having an index of the file. */
- int AVI_read_data(avi_t *AVI, char *vidbuf, long max_vidbuf,
- char *audbuf, long max_audbuf,
- long *len)
- {
- /*
- * Return codes:
- *
- * 1 = video data read
- * 2 = audio data read
- * 0 = reached EOF
- * -1 = video buffer too small
- * -2 = audio buffer too small
- */
- int n;
- char data[8];
-
- if(AVI->mode==AVI_MODE_WRITE) return 0;
- while(1)
- {
- /* Read tag and length */
- if( avi_read(AVI->fdes,data,8) != 8 ) return 0;
- /* if we got a list tag, ignore it */
- if(strncasecmp(data,"LIST",4) == 0)
- {
- lseek(AVI->fdes,4,SEEK_CUR);
- continue;
- }
- n = PAD_EVEN(str2ulong((unsigned char *)data+4));
- if(strncasecmp(data,AVI->video_tag,3) == 0)
- {
- *len = n;
- AVI->video_pos++;
- if(n>max_vidbuf)
- {
- lseek(AVI->fdes,n,SEEK_CUR);
- return -1;
- }
- if(avi_read(AVI->fdes,vidbuf,n) != n ) return 0;
- return 1;
- }
- else if(strncasecmp(data,AVI->track[AVI->aptr].audio_tag,4) == 0)
- {
- *len = n;
- if(n>max_audbuf)
- {
- lseek(AVI->fdes,n,SEEK_CUR);
- return -2;
- }
- if(avi_read(AVI->fdes,audbuf,n) != n ) return 0;
- return 2;
- break;
- }
- else
- if(lseek(AVI->fdes,n,SEEK_CUR)<0) return 0;
- }
- }
- /* AVI_print_error: Print most recent error (similar to perror) */
- char *(avi_errors[]) =
- {
- /* 0 */ "avilib - No Error",
- /* 1 */ "avilib - AVI file size limit reached",
- /* 2 */ "avilib - Error opening AVI file",
- /* 3 */ "avilib - Error reading from AVI file",
- /* 4 */ "avilib - Error writing to AVI file",
- /* 5 */ "avilib - Error writing index (file may still be useable)",
- /* 6 */ "avilib - Error closing AVI file",
- /* 7 */ "avilib - Operation (read/write) not permitted",
- /* 8 */ "avilib - Out of memory (malloc failed)",
- /* 9 */ "avilib - Not an AVI file",
- /* 10 */ "avilib - AVI file has no header list (corrupted?)",
- /* 11 */ "avilib - AVI file has no MOVI list (corrupted?)",
- /* 12 */ "avilib - AVI file has no video data",
- /* 13 */ "avilib - operation needs an index",
- /* 14 */ "avilib - Unkown Error"
- };
- static int num_avi_errors = sizeof(avi_errors)/sizeof(char*);
- static char error_string[4096];
- void AVI_print_error(char *str)
- {
- int aerrno;
- aerrno = (AVI_errno>=0 && AVI_errno<num_avi_errors) ? AVI_errno : num_avi_errors-1;
- fprintf(stderr,"%s: %s\n",str,avi_errors[aerrno]);
- /* for the following errors, perror should report a more detailed reason: */
- if(AVI_errno == AVI_ERR_OPEN ||
- AVI_errno == AVI_ERR_READ ||
- AVI_errno == AVI_ERR_WRITE ||
- AVI_errno == AVI_ERR_WRITE_INDEX ||
- AVI_errno == AVI_ERR_CLOSE )
- {
- perror("REASON");
- }
- }
- char *AVI_strerror()
- {
- int aerrno;
- aerrno = (AVI_errno>=0 && AVI_errno<num_avi_errors) ? AVI_errno : num_avi_errors-1;
- if(AVI_errno == AVI_ERR_OPEN ||
- AVI_errno == AVI_ERR_READ ||
- AVI_errno == AVI_ERR_WRITE ||
- AVI_errno == AVI_ERR_WRITE_INDEX ||
- AVI_errno == AVI_ERR_CLOSE )
- {
- sprintf(error_string,"%s - %s",avi_errors[aerrno],strerror(errno));
- return error_string;
- }
- else
- {
- return avi_errors[aerrno];
- }
- }
- uint64_t AVI_max_size()
- {
- return((uint64_t) AVI_MAX_LEN);
- }
|