summaryrefslogtreecommitdiffstats
path: root/debian/transcode/transcode-1.1.7/avilib/avilib.c
diff options
context:
space:
mode:
Diffstat (limited to 'debian/transcode/transcode-1.1.7/avilib/avilib.c')
-rw-r--r--debian/transcode/transcode-1.1.7/avilib/avilib.c3242
1 files changed, 3242 insertions, 0 deletions
diff --git a/debian/transcode/transcode-1.1.7/avilib/avilib.c b/debian/transcode/transcode-1.1.7/avilib/avilib.c
new file mode 100644
index 00000000..3aa019a8
--- /dev/null
+++ b/debian/transcode/transcode-1.1.7/avilib/avilib.c
@@ -0,0 +1,3242 @@
+/*
+ * avilib.c
+ *
+ * Copyright (C) Thomas Oestreich - June 2001
+ * multiple audio track support Copyright (C) 2002 Thomas Oestreich
+ * Version 1.1.0: Copyright (C) 2007-2008 Francesco Romani
+ *
+ * Original code:
+ * Copyright (C) 1999 Rainer Johanni <Rainer@Johanni.de>
+ *
+ * This file is part of transcode, a 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.
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#else
+#define PACKAGE "transcode"
+#define VERSION "1.1.0"
+#endif
+
+#include <unistd.h>
+
+#include "avilib.h"
+#include "platform.h"
+
+#define INFO_LIST
+
+enum {
+ NEW_RIFF_THRES = (1900*1024*1024), /* new riff chunk after XX MB */
+ NR_IXNN_CHUNKS = 32, /* Maximum indices per stream */
+ MAX_INFO_STRLEN = 64, /* XXX: ??? */
+ FRAME_RATE_SCALE = 1000000, /* XXX: ??? */
+ HEADERBYTES = 2048, /* bytes for the header */
+};
+
+/* 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 )
+
+
+/*************************************************************************/
+/* forward declarations */
+
+static int avi_parse_input_file(avi_t *AVI, int getIndex);
+static int avi_parse_index_from_file(avi_t *AVI, const char *filename);
+static int avi_update_header(avi_t *AVI);
+
+
+
+/*************************************************************************/
+
+/* The following variable indicates the kind of error */
+static long AVI_errno = 0;
+
+
+/*************************************************************************/
+
+/* Copy n into dst as a 4 or 2 byte, little endian number.
+ Should also work on big endian machines */
+
+static void long2str(unsigned char *dst, int32_t n)
+{
+ dst[0] = (n )&0xff;
+ dst[1] = (n>> 8)&0xff;
+ dst[2] = (n>>16)&0xff;
+ dst[3] = (n>>24)&0xff;
+}
+
+#ifdef WORDS_BIGENDIAN
+static void short2str(unsigned char *dst, int32_t n)
+{
+ dst[0] = (n )&0xff;
+ dst[1] = (n>> 8)&0xff;
+}
+#endif
+
+/* Convert a string of 4 or 2 bytes to a number,
+ also working on big endian machines */
+
+static uint64_t str2ullong(unsigned char *str)
+{
+ uint64_t r = (str[0] | (str[1]<<8) | (str[2]<<16) | (str[3]<<24));
+ uint64_t s = (str[4] | (str[5]<<8) | (str[6]<<16) | (str[7]<<24));
+ return ((s<<32)&0xffffffff00000000ULL)|(r&0xffffffffULL);
+}
+
+static uint32_t str2ulong(unsigned char *str)
+{
+ return ( str[0] | (str[1]<<8) | (str[2]<<16) | (str[3]<<24) );
+}
+static uint32_t str2ushort(unsigned char *str)
+{
+ return ( str[0] | (str[1]<<8) );
+}
+
+// bit 31 denotes a keyframe
+static uint32_t str2ulong_len (unsigned char *str)
+{
+ return str2ulong(str) & 0x7fffffff;
+}
+
+
+// if bit 31 is 0, its a keyframe
+static uint32_t str2ulong_key (unsigned char *str)
+{
+ uint32_t c = str2ulong(str);
+ return ((c & 0x80000000) ? 0 : 0x10);
+}
+
+/*************************************************************************/
+
+/* 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, const unsigned char *tag,
+ const unsigned char *data, int length)
+{
+ unsigned char c[8];
+ char p=0;
+
+ /* 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 */
+
+ if( plat_write(AVI->fdes,(char *)c,8) != 8 ||
+ plat_write(AVI->fdes,(char *)data,length) != length ||
+ plat_write(AVI->fdes,&p,length&1) != (length&1)) // if len is uneven, write a pad byte
+ {
+ plat_seek(AVI->fdes,AVI->pos,SEEK_SET);
+ AVI_errno = AVI_ERR_WRITE;
+ return -1;
+ }
+
+ /* Update file position */
+
+ AVI->pos += 8 + PAD_EVEN(length);
+
+ //fprintf(stderr, "pos=%lu %s\n", AVI->pos, tag);
+
+ return 0;
+}
+
+#define OUTD(n) long2str(ix00+bl,n); bl+=4
+#define OUTW(n) ix00[bl] = (n)&0xff; ix00[bl+1] = (n>>8)&0xff; bl+=2
+#define OUTC(n) ix00[bl] = (n)&0xff; bl+=1
+#define OUTS(s) memcpy(ix00+bl,s,4); bl+=4
+
+// this does the physical writeout of the ix## structure
+static int avi_ixnn_entry(avi_t *AVI, avistdindex_chunk *ch,
+ avisuperindex_entry *en)
+{
+ int bl, k;
+ unsigned int max = ch->nEntriesInUse * sizeof(uint32_t) * ch->wLongsPerEntry + 24; // header
+ char *ix00 = plat_malloc(max);
+ char dfcc[5];
+ memcpy(dfcc, ch->fcc, 4);
+ dfcc[4] = 0;
+
+ bl = 0;
+
+ if (en) {
+ en->qwOffset = AVI->pos;
+ en->dwSize = max;
+ //en->dwDuration = ch->nEntriesInUse -1;
+ // NUMBER OF stream ticks == frames for video/samples for audio
+ }
+
+#ifdef DEBUG_ODML
+ //printf ("ODML Write %s: Entries %ld size %d \n", dfcc, ch->nEntriesInUse, max);
+#endif
+
+ //OUTS(ch->fcc);
+ //OUTD(max);
+ OUTW(ch->wLongsPerEntry);
+ OUTC(ch->bIndexSubType);
+ OUTC(ch->bIndexType);
+ OUTD(ch->nEntriesInUse);
+ OUTS(ch->dwChunkId);
+ OUTD(ch->qwBaseOffset&0xffffffff);
+ OUTD((ch->qwBaseOffset>>32)&0xffffffff);
+ OUTD(ch->dwReserved3);
+
+ for (k = 0; k < ch->nEntriesInUse; k++) {
+ OUTD(ch->aIndex[k].dwOffset);
+ OUTD(ch->aIndex[k].dwSize);
+ }
+ avi_add_chunk(AVI, ch->fcc, ix00, max);
+
+ plat_free(ix00);
+
+ return 0;
+}
+#undef OUTS
+#undef OUTW
+#undef OUTD
+#undef OUTC
+
+// inits a super index structure including its enclosed stdindex
+static int avi_init_super_index(avi_t *AVI, const unsigned char *idxtag,
+ avisuperindex_chunk **si)
+{
+ int k;
+
+ avisuperindex_chunk *sil = plat_zalloc(sizeof(avisuperindex_chunk));
+ if (sil == NULL) {
+ AVI_errno = AVI_ERR_NO_MEM;
+ return -1;
+ }
+ memcpy(sil->fcc, "indx", 4);
+ sil->dwSize = 0; // size of this chunk
+ sil->wLongsPerEntry = 4;
+ sil->bIndexSubType = 0;
+ sil->bIndexType = AVI_INDEX_OF_INDEXES;
+ sil->nEntriesInUse = 0; // none are in use
+ memcpy(sil->dwChunkId, idxtag, 4);
+ memset(sil->dwReserved, 0, sizeof(sil->dwReserved));
+
+ // NR_IXNN_CHUNKS == allow 32 indices which means 32 GB files -- arbitrary
+ sil->aIndex = plat_zalloc(sil->wLongsPerEntry * NR_IXNN_CHUNKS * sizeof(uint32_t));
+ if (!sil->aIndex) {
+ AVI_errno = AVI_ERR_NO_MEM;
+ return -1;
+ }
+
+ sil->stdindex = plat_zalloc(NR_IXNN_CHUNKS * sizeof(avistdindex_chunk *));
+ if (!sil->stdindex) {
+ AVI_errno = AVI_ERR_NO_MEM;
+ return -1;
+ }
+ for (k = 0; k < NR_IXNN_CHUNKS; k++) {
+ sil->stdindex[k] = plat_zalloc(sizeof(avistdindex_chunk));
+ // gets rewritten later
+ sil->stdindex[k]->qwBaseOffset = (uint64_t)k * NEW_RIFF_THRES;
+ }
+
+ *si = sil;
+
+ return 0;
+}
+
+// fills an alloc'ed stdindex structure and mallocs some entries for the
+// actual chunks
+static int avi_add_std_index(avi_t *AVI, const unsigned char *idxtag,
+ const unsigned char *strtag,
+ avistdindex_chunk *stdil)
+{
+
+ memcpy(stdil->fcc, idxtag, 4);
+ stdil->dwSize = 4096;
+ stdil->wLongsPerEntry = 2; //sizeof(avistdindex_entry)/sizeof(uint32_t);
+ stdil->bIndexSubType = 0;
+ stdil->bIndexType = AVI_INDEX_OF_CHUNKS;
+ stdil->nEntriesInUse = 0;
+
+ // cp 00db ChunkId
+ memcpy(stdil->dwChunkId, strtag, 4);
+
+ //stdil->qwBaseOffset = AVI->video_superindex->aIndex[ cur_std_idx ]->qwOffset;
+
+ stdil->aIndex = plat_zalloc(stdil->dwSize * sizeof(uint32_t) * stdil->wLongsPerEntry);
+
+ if (!stdil->aIndex) {
+ AVI_errno = AVI_ERR_NO_MEM;
+ return -1;
+ }
+ return 0;
+}
+
+static int avi_add_odml_index_entry_core(avi_t *AVI, long flags, off_t pos,
+ unsigned long len,
+ avistdindex_chunk *si)
+{
+ int cur_chunk_idx;
+ // put new chunk into index
+ si->nEntriesInUse++;
+ cur_chunk_idx = si->nEntriesInUse-1;
+
+ // need to fetch more memory
+ if (cur_chunk_idx >= si->dwSize) {
+ si->dwSize += 4096;
+ si->aIndex = plat_realloc( si->aIndex, si->dwSize * sizeof(uint32_t) * si->wLongsPerEntry);
+ }
+
+ if (len>AVI->max_len)
+ AVI->max_len=len;
+
+ // if bit 31 is set, it is NOT a keyframe
+ if (flags != 0x10) {
+ len |= 0x80000000;
+ }
+
+ si->aIndex[cur_chunk_idx].dwSize = len;
+ si->aIndex[cur_chunk_idx].dwOffset = pos - si->qwBaseOffset + 8;
+
+ //printf("ODML: POS: 0x%lX\n", si->aIndex [ cur_chunk_idx ].dwOffset);
+ return 0;
+}
+
+static int avi_add_odml_index_entry(avi_t *AVI, const unsigned char *tag,
+ long flags, off_t pos,
+ unsigned long len)
+{
+ char fcc[5];
+
+ int audio = (strchr (tag, 'w')?1:0);
+ int video = !audio;
+
+ unsigned int cur_std_idx;
+ int audtr;
+ off_t towrite = 0LL;
+
+ if (video) {
+
+ if (!AVI->video_superindex) {
+ if (avi_init_super_index(AVI, "ix00", &AVI->video_superindex) < 0) return -1;
+ AVI->video_superindex->nEntriesInUse++;
+ cur_std_idx = AVI->video_superindex->nEntriesInUse-1;
+
+ if (avi_add_std_index (AVI, "ix00", "00db", AVI->video_superindex->stdindex[ cur_std_idx ]) < 0)
+ return -1;
+ } // init
+
+ } // video
+
+ if (audio) {
+
+ fcc[0] = 'i'; fcc[1] = 'x'; fcc[2] = tag[0]; fcc[3] = tag[1]; fcc[4] = '\0';
+ if (!AVI->track[AVI->aptr].audio_superindex) {
+
+#ifdef DEBUG_ODML
+ printf("ODML: fcc = %s\n", fcc);
+#endif
+ if (avi_init_super_index(AVI, fcc, &AVI->track[AVI->aptr].audio_superindex) < 0) return -1;
+
+
+ AVI->track[AVI->aptr].audio_superindex->nEntriesInUse++;
+
+ snprintf(fcc, sizeof(fcc), "ix%02d", AVI->aptr+1);
+ if (avi_add_std_index (AVI, fcc, tag, AVI->track[AVI->aptr].audio_superindex->stdindex[
+ AVI->track[AVI->aptr].audio_superindex->nEntriesInUse - 1 ]) < 0
+ ) return -1;
+ } // init
+
+ }
+
+ towrite = 0;
+ if (AVI->video_superindex) {
+
+ cur_std_idx = AVI->video_superindex->nEntriesInUse-1;
+ towrite += AVI->video_superindex->stdindex[cur_std_idx]->nEntriesInUse*8
+ + 4+4+2+1+1+4+4+8+4;
+ if (cur_std_idx == 0) {
+ towrite += AVI->n_idx*16 + 8;
+ towrite += HEADERBYTES;
+ }
+ }
+
+ for (audtr=0; audtr<AVI->anum; audtr++) {
+ if (AVI->track[audtr].audio_superindex) {
+ cur_std_idx = AVI->track[audtr].audio_superindex->nEntriesInUse-1;
+ towrite += AVI->track[audtr].audio_superindex->stdindex[cur_std_idx]->nEntriesInUse*8
+ + 4+4+2+1+1+4+4+8+4;
+ }
+ }
+ towrite += len + (len&1) + 8;
+
+ //printf("ODML: towrite = 0x%llX = %lld\n", towrite, towrite);
+
+ if (AVI->video_superindex &&
+ (off_t)(AVI->pos+towrite) > (off_t)((off_t)NEW_RIFF_THRES*AVI->video_superindex->nEntriesInUse)) {
+
+ plat_log_send(PLAT_LOG_INFO, __FILE__, "Adding a new RIFF chunk: %d",
+ AVI->video_superindex->nEntriesInUse);
+
+ // rotate ALL indices
+ AVI->video_superindex->nEntriesInUse++;
+ cur_std_idx = AVI->video_superindex->nEntriesInUse-1;
+
+ if (AVI->video_superindex->nEntriesInUse > NR_IXNN_CHUNKS) {
+ plat_log_send(PLAT_LOG_ERROR, __FILE__,
+ "Internal error in avilib - redefine NR_IXNN_CHUNKS");
+ plat_log_send(PLAT_LOG_ERROR, __FILE__,
+ "[avilib dump] cur_std_idx=%d NR_IXNN_CHUNKS=%d"
+ "POS=%lld towrite=%lld",
+ cur_std_idx,NR_IXNN_CHUNKS, (long long)AVI->pos, (long long)towrite);
+ return -1;
+ }
+
+ if (avi_add_std_index (AVI, "ix00", "00db", AVI->video_superindex->stdindex[ cur_std_idx ]) < 0)
+ return -1;
+
+ for (audtr = 0; audtr < AVI->anum; audtr++) {
+ char aud[5];
+ if (!AVI->track[audtr].audio_superindex) {
+ // not initialized -> no index
+ continue;
+ }
+ AVI->track[audtr].audio_superindex->nEntriesInUse++;
+
+ snprintf(fcc, sizeof(fcc), "ix%02d", audtr+1);
+ snprintf(aud, sizeof(aud), "0%01dwb", audtr+1);
+ if (avi_add_std_index (AVI, fcc, aud, AVI->track[audtr].audio_superindex->stdindex[
+ AVI->track[audtr].audio_superindex->nEntriesInUse - 1 ]) < 0
+ ) return -1;
+ }
+
+ // write the new riff;
+ if (cur_std_idx > 0) {
+
+ // dump the _previous_ == already finished index
+ avi_ixnn_entry (AVI, AVI->video_superindex->stdindex[cur_std_idx - 1],
+ &AVI->video_superindex->aIndex[cur_std_idx - 1]);
+ AVI->video_superindex->aIndex[cur_std_idx - 1].dwDuration =
+ AVI->video_superindex->stdindex[cur_std_idx - 1]->nEntriesInUse - 1;
+
+ for (audtr = 0; audtr < AVI->anum; audtr++) {
+
+ if (!AVI->track[audtr].audio_superindex) {
+ // not initialized -> no index
+ continue;
+ }
+ avi_ixnn_entry (AVI, AVI->track[audtr].audio_superindex->stdindex[cur_std_idx - 1],
+ &AVI->track[audtr].audio_superindex->aIndex[cur_std_idx - 1]);
+
+ AVI->track[audtr].audio_superindex->aIndex[cur_std_idx - 1].dwDuration =
+ AVI->track[audtr].audio_superindex->stdindex[cur_std_idx - 1]->nEntriesInUse - 1;
+ if (AVI->track[audtr].a_fmt == 0x1) {
+ AVI->track[audtr].audio_superindex->aIndex[cur_std_idx - 1].dwDuration *=
+ AVI->track[audtr].a_bits*AVI->track[audtr].a_rate*AVI->track[audtr].a_chans/800;
+ }
+ }
+
+ // XXX: dump idx1 structure
+ if (cur_std_idx == 1) {
+ avi_add_chunk(AVI, (unsigned char *)"idx1", (unsigned char *)AVI->idx, AVI->n_idx*16);
+ // qwBaseOffset will contain the start of the second riff chunk
+ }
+ // Fix the Offsets later at closing time
+ avi_add_chunk(AVI, (unsigned char *)"RIFF", "AVIXLIST\0\0\0\0movi", 16);
+
+ AVI->video_superindex->stdindex[ cur_std_idx ]->qwBaseOffset = AVI->pos -16 -8;
+#ifdef DEBUG_ODML
+ printf("ODML: RIFF No.%02d at Offset 0x%llX\n", cur_std_idx, AVI->pos -16 -8);
+#endif
+
+ for (audtr = 0; audtr < AVI->anum; audtr++) {
+ if (AVI->track[audtr].audio_superindex)
+ AVI->track[audtr].audio_superindex->stdindex[ cur_std_idx ]->qwBaseOffset =
+ AVI->pos -16 -8;
+
+ }
+
+ // now we can be sure
+ AVI->is_opendml++;
+ }
+
+ }
+
+
+ if (video) {
+ avi_add_odml_index_entry_core(AVI, flags, AVI->pos, len,
+ AVI->video_superindex->stdindex[ AVI->video_superindex->nEntriesInUse-1 ]);
+
+ AVI->total_frames++;
+ } // video
+
+ if (audio) {
+ avi_add_odml_index_entry_core(AVI, flags, AVI->pos, len,
+ AVI->track[AVI->aptr].audio_superindex->stdindex[
+ AVI->track[AVI->aptr].audio_superindex->nEntriesInUse-1 ]);
+ }
+
+
+ return 0;
+}
+
+// #undef NR_IXNN_CHUNKS
+
+static int avi_add_index_entry(avi_t *AVI, const unsigned char *tag, long flags,
+ unsigned long pos, unsigned long len)
+{
+ void *ptr;
+
+ if(AVI->n_idx>=AVI->max_idx) {
+ ptr = plat_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;
+}
+
+/* Returns 1 if more audio is in that video junk */
+int AVI_can_read_audio(avi_t *AVI)
+{
+ if(AVI->mode==AVI_MODE_WRITE) { return -1; }
+ if(!AVI->video_index) { return -1; }
+ if(!AVI->track[AVI->aptr].audio_index) { return -1; }
+
+ // is it -1? the last ones got left out --tibit
+ //if (AVI->track[AVI->aptr].audio_posc>=AVI->track[AVI->aptr].audio_chunks-1) {
+ if (AVI->track[AVI->aptr].audio_posc>=AVI->track[AVI->aptr].audio_chunks) {
+ return 0;
+ }
+
+ if (AVI->video_pos >= AVI->video_frames) return 1;
+
+ if (AVI->track[AVI->aptr].audio_index[AVI->track[AVI->aptr].audio_posc].pos < AVI->video_index[AVI->video_pos].pos) return 1;
+ else 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(const char *filename)
+{
+ avi_t *AVI = NULL;
+ uint8_t AVI_header[HEADERBYTES];
+ int i;
+
+ /* Allocate the avi_t struct and zero it */
+
+ AVI = plat_zalloc(sizeof(avi_t));
+ if (!AVI) {
+ AVI_errno = AVI_ERR_NO_MEM;
+ return NULL;
+ }
+
+ AVI->fdes = plat_open(filename, O_RDWR|O_CREAT|O_TRUNC,
+ S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH);
+
+ if (AVI->fdes < 0) {
+ AVI_errno = AVI_ERR_OPEN;
+ plat_free(AVI);
+ return NULL;
+ }
+
+ /* Write out HEADERBYTES bytes, the header will go here
+ when we are finished with writing */
+
+ memset(AVI_header, 0, sizeof(AVI_header));
+ i = plat_write(AVI->fdes,(char *)AVI_header,HEADERBYTES);
+ if (i != HEADERBYTES) {
+ plat_close(AVI->fdes);
+ AVI_errno = AVI_ERR_WRITE;
+ plat_free(AVI);
+ return NULL;
+ }
+
+ 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,
+ const char *compressor)
+{
+ /* may only be called if file is open for writing */
+ if(AVI->mode != AVI_MODE_READ) {
+ 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);
+ }
+ return;
+}
+
+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) {
+ AVI->aptr = AVI->anum;
+ AVI->anum++;
+
+ if (AVI->anum > AVI_MAX_TRACKS) {
+ plat_log_send(PLAT_LOG_ERROR, __FILE__,
+ "error - only %d audio tracks supported", AVI_MAX_TRACKS);
+ exit(1); // XXX XXX XXX
+ }
+
+ 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);
+ }
+ return;
+}
+
+#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
+
+#define OUTCHR(n) \
+ if(nhb<=HEADERBYTES-1) { \
+ AVI_header[nhb ] = (n )&0xff; \
+ } \
+ nhb += 1
+
+#define OUTMEM(d, s) \
+ { \
+ unsigned int s_ = (s); \
+ if(nhb <= HEADERBYTES-s_) \
+ memcpy(AVI_header+nhb, (d), s_); \
+ nhb += s_; \
+ }
+
+
+//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;
+ unsigned long xd_size, xd_size_align2;
+
+ //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 */
+
+ xd_size = AVI->extradata_size;
+ xd_size_align2 = (AVI->extradata_size+1) & ~1;
+
+ OUT4CC ("strf");
+ OUTLONG(40 + xd_size_align2);/* # of bytes to follow */
+ OUTLONG(40 + xd_size); /* 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 */
+
+ // write extradata
+ if (xd_size > 0 && AVI->extradata) {
+ OUTMEM(AVI->extradata, xd_size);
+ if (xd_size != xd_size_align2) {
+ OUTCHR(0);
+ }
+ }
+
+ /* 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)
+ {
+ plat_log_send(PLAT_LOG_ERROR, __FILE__,
+ "AVI_close_output_file: # of header bytes too small");
+ exit(1); // XX XXX XXX
+ }
+
+ 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 ( plat_seek(AVI->fdes,0,SEEK_SET)<0 ||
+ plat_write(AVI->fdes,(char *)AVI_header,HEADERBYTES)!=HEADERBYTES ||
+ plat_seek(AVI->fdes,AVI->pos,SEEK_SET)<0)
+ {
+ AVI_errno = AVI_ERR_CLOSE;
+ return -1;
+ }
+
+ return 0;
+}
+
+static int valid_info_tag(char *c)
+{
+ if (!strncmp(c, "IARL", 4)) return 1;
+ else if (!strncmp(c, "IART", 4)) return 1;
+ else if (!strncmp(c, "ICMS", 4)) return 1;
+ else if (!strncmp(c, "ICMT", 4)) return 1;
+ else if (!strncmp(c, "ICOP", 4)) return 1;
+ else if (!strncmp(c, "ICRD", 4)) return 1;
+ else if (!strncmp(c, "ICRP", 4)) return 1;
+ else if (!strncmp(c, "IDIM", 4)) return 1;
+ else if (!strncmp(c, "IDPI", 4)) return 1;
+ else if (!strncmp(c, "IENG", 4)) return 1;
+ else if (!strncmp(c, "IGNR", 4)) return 1;
+ else if (!strncmp(c, "IKEY", 4)) return 1;
+ else if (!strncmp(c, "ILGT", 4)) return 1;
+ else if (!strncmp(c, "IMED", 4)) return 1;
+ else if (!strncmp(c, "INAM", 4)) return 1;
+ else if (!strncmp(c, "IPLT", 4)) return 1;
+ else if (!strncmp(c, "IPRD", 4)) return 1;
+ else if (!strncmp(c, "ISBJ", 4)) return 1;
+ else if (!strncmp(c, "ISHP", 4)) return 1;
+ else if (!strncmp(c, "ISRC", 4)) return 1;
+ else if (!strncmp(c, "ISRF", 4)) return 1;
+ else if (!strncmp(c, "ITCH", 4)) return 1;
+ else return 0;
+
+ return 0;
+}
+// see ../docs/avi_comments.txt
+// returns the length of written stream (-1 on error)
+static int avi_parse_comments (int fd, char *buf, int space_left)
+{
+ int len=0, readlen=0, k;
+ char *data, *c, *d;
+ struct stat st;
+
+ // safety checks
+ if (fd<=0 || !buf || space_left<=0)
+ return -1;
+
+ memset (buf, 0, space_left);
+ if (fstat (fd, &st) == -1) {
+ perror ("stat");
+ return -1;
+ }
+
+ data = plat_malloc(st.st_size*sizeof(char)+1);
+ if (!data) {
+ plat_log_send(PLAT_LOG_ERROR, __FILE__, "malloc failed");
+ return -1;
+ }
+
+ readlen = plat_read ( fd, data, st.st_size);
+
+ //printf("Read %d bytes from %d\n", readlen, fd);
+
+ c = data;
+ space_left--;
+
+ while (len < space_left) {
+ if ( !c || *c == '\0')
+ break; // eof;
+ else if (*c == '#') { // comment, ignore
+ c = strchr(c, '\n')+1;
+ }
+ else if (*c == '\n') { // empty, ignore
+ c++;
+ }
+ else if (*c == 'I') {
+
+ // do not write ISFT -- this is written by transcode
+ // and important for debugging.
+ if (!valid_info_tag(c)) {
+ // skip this line
+ while (c && *c && *c != '\n' ) {
+ c++;
+ }
+ continue;
+ }
+
+ // set d after TAG
+ d = c+4;
+
+ // find first non-blank (!space or !TAB)
+ while ( d && *d && (*d == ' ' || *d == ' ')) ++d;
+ if (!d) break;
+
+ // TAG without argument is fine but ignored
+ if (*d == '\n' || *d == '\r') {
+ c = d+1;
+ if (*c == '\n') ++c;
+ continue;
+ }
+
+ k = 0;
+ while (d[k] != '\r' && d[k] != '\n' && d[k] != '\0') ++k;
+ if (k>=space_left) return len;
+
+ // write TAG
+ memcpy(buf+len,c,4);
+ len += 4;
+
+ // write length + '\0'
+ long2str(buf+len, k+1); len += 4;
+
+ // write comment string
+ memcpy (buf+len, d, k);
+ // must be null terminated
+ *(buf+len+k+1) = '\0';
+
+ // PAD
+ if ((k+1)&1) {
+ k++;
+ *(buf+len+k+1) = '\0';
+ }
+ len += k+1;
+
+ // advance c
+ while (*c != '\n' && *c != '\0') ++c;
+ if (*c != '\0') ++c;
+ else break;
+
+ } else {
+
+ // ignore junk lines
+ while (c && *c && *c != '\n' ) {
+ if (*c == ' ' || *c == ' ') { c++; break; }
+ c++;
+ }
+ if (!c) break;
+ }
+
+ }
+ plat_free(data);
+
+ return len;
+}
+
+//SLM
+#ifndef S_IRUSR
+#define S_IRWXU 00700 /* read, write, execute: owner */
+#define S_IRUSR 00400 /* read permission: owner */
+#define S_IWUSR 00200 /* write permission: owner */
+#define S_IXUSR 00100 /* execute permission: owner */
+#define S_IRWXG 00070 /* read, write, execute: group */
+#define S_IRGRP 00040 /* read permission: group */
+#define S_IWGRP 00020 /* write permission: group */
+#define S_IXGRP 00010 /* execute permission: group */
+#define S_IRWXO 00007 /* read, write, execute: other */
+#define S_IROTH 00004 /* read permission: other */
+#define S_IWOTH 00002 /* write permission: other */
+#define S_IXOTH 00001 /* execute permission: other */
+#endif
+
+/*
+ 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)
+{
+ char id_str[MAX_INFO_STRLEN];
+ 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;
+ unsigned long xd_size, xd_size_align2;
+
+#ifdef INFO_LIST
+ long info_len;
+ long id_len, real_id_len;
+ long info_start_pos;
+// time_t calptr;
+#endif
+
+ /* Calculate length of movi list */
+
+ // dump the rest of the index
+ if (AVI->is_opendml) {
+ int cur_std_idx = AVI->video_superindex->nEntriesInUse-1;
+ int audtr;
+
+#ifdef DEBUG_ODML
+ printf("ODML dump the rest indices\n");
+#endif
+ avi_ixnn_entry (AVI, AVI->video_superindex->stdindex[cur_std_idx],
+ &AVI->video_superindex->aIndex[cur_std_idx]);
+
+ AVI->video_superindex->aIndex[cur_std_idx].dwDuration =
+ AVI->video_superindex->stdindex[cur_std_idx]->nEntriesInUse - 1;
+
+ for (audtr = 0; audtr < AVI->anum; audtr++) {
+ if (!AVI->track[audtr].audio_superindex) {
+ // not initialized -> no index
+ continue;
+ }
+ avi_ixnn_entry (AVI, AVI->track[audtr].audio_superindex->stdindex[cur_std_idx],
+ &AVI->track[audtr].audio_superindex->aIndex[cur_std_idx]);
+ AVI->track[audtr].audio_superindex->aIndex[cur_std_idx].dwDuration =
+ AVI->track[audtr].audio_superindex->stdindex[cur_std_idx]->nEntriesInUse - 1;
+ if (AVI->track[audtr].a_fmt == 0x1) {
+ AVI->track[audtr].audio_superindex->aIndex[cur_std_idx].dwDuration *=
+ AVI->track[audtr].a_bits*AVI->track[audtr].a_rate*AVI->track[audtr].a_chans/800;
+ }
+ }
+ // The AVI->video_superindex->nEntriesInUse contains the offset
+ AVI->video_superindex->stdindex[ cur_std_idx+1 ]->qwBaseOffset = AVI->pos;
+ }
+
+ if (AVI->is_opendml) {
+ // Correct!
+ movi_len = AVI->video_superindex->stdindex[ 1 ]->qwBaseOffset - HEADERBYTES+4 - AVI->n_idx*16 - 8;
+ } else {
+ 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;
+ hasIndex = 1;
+ if (!AVI->is_opendml) {
+ // fprintf(stderr, "pos=%lu, index_len=%ld \n", AVI->pos, AVI->n_idx*16);
+ ret = avi_add_chunk(AVI, (unsigned char *)"idx1", (unsigned char *)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");
+ if (AVI->is_opendml) {
+ OUTLONG(AVI->video_superindex->stdindex[ 1 ]->qwBaseOffset - 8); /* # of bytes to follow */
+ } else {
+ 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(AVI->max_len); /* SuggestedBufferSize */
+ OUTLONG(0); /* Quality */
+ OUTLONG(0); /* SampleSize */
+ OUTLONG(0); /* Frame */
+ OUTLONG(0); /* Frame */
+ //OUTLONG(0); /* Frame */
+ //OUTLONG(0); /* Frame */
+
+ /* The video stream format */
+
+ xd_size = AVI->extradata_size;
+ xd_size_align2 = (AVI->extradata_size+1) & ~1;
+
+ OUT4CC ("strf");
+ OUTLONG(40 + xd_size_align2);/* # of bytes to follow */
+ OUTLONG(40 + xd_size); /* 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 */
+
+ // write extradata if present
+ if (xd_size > 0 && AVI->extradata) {
+ OUTMEM(AVI->extradata, xd_size);
+ if (xd_size != xd_size_align2) {
+ OUTCHR(0);
+ }
+ }
+
+ // dump index of indices for audio
+ if (AVI->is_opendml) {
+
+ int k;
+
+ OUT4CC(AVI->video_superindex->fcc);
+ OUTLONG(2+1+1+4+4+3*4 + AVI->video_superindex->nEntriesInUse * (8+4+4));
+ OUTSHRT(AVI->video_superindex->wLongsPerEntry);
+ OUTCHR(AVI->video_superindex->bIndexSubType);
+ OUTCHR(AVI->video_superindex->bIndexType);
+ OUTLONG(AVI->video_superindex->nEntriesInUse);
+ OUT4CC(AVI->video_superindex->dwChunkId);
+ OUTLONG(0);
+ OUTLONG(0);
+ OUTLONG(0);
+
+
+ for (k = 0; k < AVI->video_superindex->nEntriesInUse; k++) {
+ uint32_t r = (AVI->video_superindex->aIndex[k].qwOffset >> 32) & 0xffffffff;
+ uint32_t s = (AVI->video_superindex->aIndex[k].qwOffset) & 0xffffffff;
+
+ plat_log_send(PLAT_LOG_DEBUG, __FILE__, "VID NrEntries %d/%ld (%c%c%c%c) |0x%llX|%ld|%ld|", k,
+ (unsigned long)AVI->video_superindex->nEntriesInUse,
+ AVI->video_superindex->dwChunkId[0],
+ AVI->video_superindex->dwChunkId[1],
+ AVI->video_superindex->dwChunkId[2],
+ AVI->video_superindex->dwChunkId[3],
+ (unsigned long long)AVI->video_superindex->aIndex[k].qwOffset,
+ (unsigned long)AVI->video_superindex->aIndex[k].dwSize,
+ (unsigned long)AVI->video_superindex->aIndex[k].dwDuration
+ );
+ /*
+ */
+
+ OUTLONG(s);
+ OUTLONG(r);
+ OUTLONG(AVI->video_superindex->aIndex[k].dwSize);
+ OUTLONG(AVI->video_superindex->aIndex[k].dwDuration);
+ }
+
+ }
+
+ /* 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)
+ {
+ unsigned long nBlockAlign = 0;
+ unsigned long avgbsec = 0;
+ unsigned long scalerate = 0;
+
+ sampsize = avi_sampsize(AVI, j);
+ sampsize = AVI->track[j].a_fmt==0x1?sampsize*4:sampsize;
+
+ nBlockAlign = (AVI->track[j].a_rate<32000)?576:1152;
+ /*
+ printf("XXX sampsize (%d) block (%ld) rate (%ld) audio_bytes (%ld) mp3rate(%ld,%ld)\n",
+ sampsize, nBlockAlign, AVI->track[j].a_rate,
+ (long int)AVI->track[j].audio_bytes,
+ 1000*AVI->track[j].mp3rate/8, AVI->track[j].mp3rate);
+ */
+
+ if (AVI->track[j].a_fmt==0x1) {
+ sampsize = (AVI->track[j].a_chans<2)?sampsize/2:sampsize;
+ avgbsec = AVI->track[j].a_rate*sampsize/4;
+ scalerate = AVI->track[j].a_rate*sampsize/4;
+ } else {
+ avgbsec = 1000*AVI->track[j].mp3rate/8;
+ scalerate = 1000*AVI->track[j].mp3rate/8;
+ }
+
+ 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 */
+
+ // VBR
+ if (AVI->track[j].a_fmt == 0x55 && AVI->track[j].a_vbr) {
+ OUTLONG(nBlockAlign); /* Scale */
+ OUTLONG(AVI->track[j].a_rate); /* Rate */
+ OUTLONG(0); /* Start */
+ OUTLONG(AVI->track[j].audio_chunks); /* Length */
+ OUTLONG(0); /* SuggestedBufferSize */
+ OUTLONG(0); /* Quality */
+ OUTLONG(0); /* SampleSize */
+ OUTLONG(0); /* Frame */
+ OUTLONG(0); /* Frame */
+ } else {
+ OUTLONG(sampsize/4); /* Scale */
+ OUTLONG(scalerate); /* Rate */
+ OUTLONG(0); /* Start */
+ OUTLONG(4*AVI->track[j].audio_bytes/sampsize); /* Length */
+ OUTLONG(0); /* SuggestedBufferSize */
+ OUTLONG(0xffffffff); /* Quality */
+ OUTLONG(sampsize/4); /* SampleSize */
+ OUTLONG(0); /* Frame */
+ OUTLONG(0); /* Frame */
+ }
+
+ /* The audio stream format */
+
+ OUT4CC ("strf");
+
+ if (AVI->track[j].a_fmt == 0x55 && AVI->track[j].a_vbr) {
+
+ OUTLONG(30); /* # of bytes to follow */ // mplayer writes 28
+ OUTSHRT(AVI->track[j].a_fmt); /* Format */ // 2
+ OUTSHRT(AVI->track[j].a_chans); /* Number of channels */ // 2
+ OUTLONG(AVI->track[j].a_rate); /* SamplesPerSec */ // 4
+ //ThOe/tibit
+ OUTLONG(1000*AVI->track[j].mp3rate/8); /* maybe we should write an avg. */ // 4
+ OUTSHRT(nBlockAlign); /* BlockAlign */ // 2
+ OUTSHRT(AVI->track[j].a_bits); /* BitsPerSample */ // 2
+
+ OUTSHRT(12); /* cbSize */ // 2
+ OUTSHRT(1); /* wID */ // 2
+ OUTLONG(2); /* fdwFlags */ // 4
+ OUTSHRT(nBlockAlign); /* nBlockSize */ // 2
+ OUTSHRT(1); /* nFramesPerBlock */ // 2
+ OUTSHRT(0); /* nCodecDelay */ // 2
+
+ } else if (AVI->track[j].a_fmt == 0x55 && !AVI->track[j].a_vbr) {
+
+ OUTLONG(30); /* # 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/tibit
+ OUTLONG(1000*AVI->track[j].mp3rate/8);
+ OUTSHRT(sampsize/4); /* BlockAlign */
+ OUTSHRT(AVI->track[j].a_bits); /* BitsPerSample */
+
+ OUTSHRT(12); /* cbSize */
+ OUTSHRT(1); /* wID */
+ OUTLONG(2); /* fdwFlags */
+ OUTSHRT(nBlockAlign); /* nBlockSize */
+ OUTSHRT(1); /* nFramesPerBlock */
+ OUTSHRT(0); /* nCodecDelay */
+
+ } else {
+
+ OUTLONG(18); /* # 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/tibit
+ OUTLONG(avgbsec); /* Avg bytes/sec */
+ OUTSHRT(sampsize/4); /* BlockAlign */
+ OUTSHRT(AVI->track[j].a_bits); /* BitsPerSample */
+ OUTSHRT(0); /* cbSize */
+
+ }
+ }
+ if (AVI->is_opendml) {
+
+ int k ;
+
+ if (!AVI->track[j].audio_superindex) {
+ // not initialized -> no index
+ continue;
+ }
+
+ OUT4CC(AVI->track[j].audio_superindex->fcc); /* "indx" */
+ OUTLONG(2+1+1+4+4+3*4 + AVI->track[j].audio_superindex->nEntriesInUse * (8+4+4));
+ OUTSHRT(AVI->track[j].audio_superindex->wLongsPerEntry);
+ OUTCHR(AVI->track[j].audio_superindex->bIndexSubType);
+ OUTCHR(AVI->track[j].audio_superindex->bIndexType);
+ OUTLONG(AVI->track[j].audio_superindex->nEntriesInUse);
+ OUT4CC(AVI->track[j].audio_superindex->dwChunkId);
+ OUTLONG(0); OUTLONG(0); OUTLONG(0);
+
+ for (k = 0; k < AVI->track[j].audio_superindex->nEntriesInUse; k++) {
+ uint32_t r = (AVI->track[j].audio_superindex->aIndex[k].qwOffset >> 32) & 0xffffffff;
+ uint32_t s = (AVI->track[j].audio_superindex->aIndex[k].qwOffset) & 0xffffffff;
+
+ /*
+ printf("AUD[%d] NrEntries %d/%ld (%c%c%c%c) |0x%llX|%ld|%ld| \n", j, k,
+ AVI->track[j].audio_superindex->nEntriesInUse,
+ AVI->track[j].audio_superindex->dwChunkId[0], AVI->track[j].audio_superindex->dwChunkId[1],
+ AVI->track[j].audio_superindex->dwChunkId[2], AVI->track[j].audio_superindex->dwChunkId[3],
+ AVI->track[j].audio_superindex->aIndex[k].qwOffset,
+ AVI->track[j].audio_superindex->aIndex[k].dwSize,
+ AVI->track[j].audio_superindex->aIndex[k].dwDuration
+ );
+ */
+
+ OUTLONG(s);
+ OUTLONG(r);
+ OUTLONG(AVI->track[j].audio_superindex->aIndex[k].dwSize);
+ OUTLONG(AVI->track[j].audio_superindex->aIndex[k].dwDuration);
+ }
+ }
+ /* Finish stream list, i.e. put number of bytes in the list to proper pos */
+ long2str(AVI_header+strl_start-4,nhb-strl_start);
+ }
+
+ if (AVI->is_opendml) {
+ OUT4CC("LIST");
+ OUTLONG(16);
+ OUT4CC("odml");
+ OUT4CC("dmlh");
+ OUTLONG(4);
+ OUTLONG(AVI->total_frames);
+ }
+
+ /* Finish header list */
+
+ long2str(AVI_header+hdrl_start-4,nhb-hdrl_start);
+
+
+ // add INFO list --- (0.6.0pre4)
+
+#ifdef INFO_LIST
+ OUT4CC ("LIST");
+
+ info_start_pos = nhb;
+ info_len = MAX_INFO_STRLEN + 12;
+ OUTLONG(info_len); // rewritten later
+ OUT4CC ("INFO");
+
+ OUT4CC ("ISFT");
+ //OUTLONG(MAX_INFO_STRLEN);
+ memset(id_str, 0, MAX_INFO_STRLEN);
+
+ snprintf(id_str, sizeof(id_str), "%s-%s", PACKAGE, VERSION);
+ real_id_len = id_len = strlen(id_str)+1;
+ if (id_len&1) id_len++;
+
+ OUTLONG(real_id_len);
+
+ memset(AVI_header+nhb, 0, id_len);
+ memcpy(AVI_header+nhb, id_str, id_len);
+ nhb += id_len;
+
+ info_len = avi_parse_comments (AVI->comment_fd, AVI_header+nhb, HEADERBYTES - nhb - 8 - 12);
+ if (info_len <= 0) info_len=0;
+
+ // write correct len
+ long2str(AVI_header+info_start_pos, info_len + id_len + 4+4+4);
+
+ nhb += info_len;
+
+// OUT4CC ("ICMT");
+// OUTLONG(MAX_INFO_STRLEN);
+
+// calptr=time(NULL);
+// snprintf(id_str, sizeof(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)
+ {
+ plat_log_send(PLAT_LOG_ERROR, __FILE__,
+ "AVI_close_output_file: # of header bytes too small");
+ exit(1); // XXX XXX XXX
+ }
+
+ 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 ( plat_seek(AVI->fdes,0,SEEK_SET)<0 ||
+ plat_write(AVI->fdes,(char *)AVI_header,HEADERBYTES)!=HEADERBYTES ||
+ plat_ftruncate(AVI->fdes,AVI->pos)<0 )
+ {
+ AVI_errno = AVI_ERR_CLOSE;
+ return -1;
+ }
+
+
+ // Fix up the empty additional RIFF and LIST chunks
+ if (AVI->is_opendml) {
+ int k = 0;
+ char f[4];
+ unsigned int len;
+
+ for (k=1; k<AVI->video_superindex->nEntriesInUse; k++) {
+ // the len of the RIFF Chunk
+ plat_seek(AVI->fdes, AVI->video_superindex->stdindex[k]->qwBaseOffset+4, SEEK_SET);
+ len = AVI->video_superindex->stdindex[k+1]->qwBaseOffset -
+ AVI->video_superindex->stdindex[k]->qwBaseOffset - 8;
+ long2str(f, len);
+ plat_write(AVI->fdes, f, 4);
+
+ // len of the LIST/movi chunk
+ plat_seek(AVI->fdes, 8, SEEK_CUR);
+ len -= 12;
+ long2str(f, len);
+ plat_write(AVI->fdes, f, 4);
+ }
+ }
+
+
+ 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 plat_write_data(avi_t *AVI, const char *data, unsigned long length,
+ int audio, int keyframe)
+{
+ int n = 0;
+
+ unsigned char astr[5];
+
+ // transcode core itself checks for the size -- unneeded and
+ // does harm to xvid 2pass encodes where the first pass can get
+ // _very_ large -- tibit.
+
+#if 0
+ /* 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;
+ }
+#endif
+
+ /* Add index entry */
+
+ //set tag for current audio track
+ snprintf((char *)astr, sizeof(astr), "0%1dwb", (int)(AVI->aptr+1));
+
+ if(audio) {
+ if (!AVI->is_opendml) n = avi_add_index_entry(AVI,astr,0x10,AVI->pos,length);
+ n += avi_add_odml_index_entry(AVI,astr,0x10,AVI->pos,length);
+ } else {
+ if (!AVI->is_opendml) n = avi_add_index_entry(AVI,(unsigned char *)"00db",((keyframe)?0x10:0x0),AVI->pos,length);
+ n += avi_add_odml_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,data,length);
+ else
+ n = avi_add_chunk(AVI,(unsigned char *)"00db",data,length);
+
+ if (n) return -1;
+
+ return 0;
+}
+
+int AVI_write_frame(avi_t *AVI, const char *data, long bytes, int keyframe)
+{
+ off_t pos;
+
+ if(AVI->mode==AVI_MODE_READ) { AVI_errno = AVI_ERR_NOT_PERM; return -1; }
+
+ pos = AVI->pos;
+
+ if(plat_write_data(AVI,data,bytes,0,keyframe)) return -1;
+
+ AVI->last_pos = pos;
+ AVI->last_len = bytes;
+ AVI->video_frames++;
+ return 0;
+}
+
+int AVI_write_audio(avi_t *AVI, const char *data, long bytes)
+{
+ if(AVI->mode==AVI_MODE_READ) { AVI_errno = AVI_ERR_NOT_PERM; return -1; }
+
+ if( plat_write_data(AVI,data,bytes,1,0) ) return -1;
+ AVI->track[AVI->aptr].audio_bytes += bytes;
+ AVI->track[AVI->aptr].audio_chunks++;
+ 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);
+}
+
+void AVI_set_audio_vbr(avi_t *AVI, long is_vbr)
+{
+ AVI->track[AVI->aptr].a_vbr = is_vbr;
+}
+
+long AVI_get_audio_vbr(avi_t *AVI)
+{
+ return(AVI->track[AVI->aptr].a_vbr);
+}
+
+void AVI_set_comment_fd(avi_t *AVI, int fd)
+{
+ AVI->comment_fd = fd;
+}
+int AVI_get_comment_fd(avi_t *AVI)
+{
+ return AVI->comment_fd;
+}
+
+
+/*******************************************************************
+ * *
+ * Utilities for reading video and audio from an AVI File *
+ * *
+ *******************************************************************/
+
+int AVI_close(avi_t *AVI)
+{
+ int j, k, ret = 0;
+
+ /* 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);
+ }
+
+ /* Even if there happened an error, we first clean up */
+
+ if (AVI->comment_fd>0)
+ plat_close(AVI->comment_fd);
+ AVI->comment_fd = -1;
+
+ plat_close(AVI->fdes);
+
+ if (AVI->idx)
+ plat_free(AVI->idx);
+ if (AVI->video_index)
+ plat_free(AVI->video_index);
+
+ if (AVI->video_superindex && AVI->video_superindex->stdindex) {
+ for (j = 0; j < NR_IXNN_CHUNKS; j++) {
+ if (AVI->video_superindex->stdindex[j]) {
+ if (AVI->video_superindex->stdindex[j]->aIndex) {
+ plat_free(AVI->video_superindex->stdindex[j]->aIndex);
+ }
+ plat_free(AVI->video_superindex->stdindex[j]);
+ }
+ }
+ if (AVI->video_superindex->stdindex)
+ plat_free(AVI->video_superindex->stdindex);
+ if (AVI->video_superindex->aIndex)
+ plat_free(AVI->video_superindex->aIndex);
+ plat_free(AVI->video_superindex);
+ }
+
+ for (j = 0; j < AVI->anum; j++) {
+ if (AVI->track[j].audio_index)
+ plat_free(AVI->track[j].audio_index);
+ if (AVI->track[j].audio_superindex) {
+ // shortcut
+ avisuperindex_chunk *a = AVI->track[j].audio_superindex;
+ for (k = 0; k < NR_IXNN_CHUNKS; k++) {
+ if (a->stdindex && a->stdindex[k]) {
+ if (a->stdindex[k]->aIndex) {
+ plat_free(a->stdindex[k]->aIndex);
+ }
+ plat_free(a->stdindex[k]);
+ }
+ }
+ if (a->stdindex)
+ plat_free(a->stdindex);
+ if (a->aIndex)
+ plat_free(a->aIndex);
+ plat_free(a);
+ }
+ }
+
+ if (AVI->bitmap_info_header)
+ plat_free(AVI->bitmap_info_header);
+ for (j = 0; j < AVI->anum; j++)
+ if (AVI->wave_format_ex[j])
+ plat_free(AVI->wave_format_ex[j]);
+
+ plat_free(AVI);
+ AVI = NULL;
+
+ return ret;
+}
+
+
+#define ERR_EXIT(x) do { \
+ AVI_close(AVI); \
+ AVI_errno = x; \
+ return 0; \
+} while (0)
+
+avi_t *AVI_open_indexfd(int fd, int getIndex, const char *indexfile)
+{
+ avi_t *AVI = plat_zalloc(sizeof(avi_t));
+ if (AVI == NULL) {
+ AVI_errno = AVI_ERR_NO_MEM;
+ return NULL;
+ }
+
+ AVI->mode = AVI_MODE_READ; /* open for reading */
+
+ // file alread open
+ AVI->fdes = fd;
+
+ if (indexfile) {
+ AVI->index_file = strdup(indexfile);
+ }
+ AVI_errno = 0;
+ avi_parse_input_file(AVI, getIndex);
+
+ if (AVI != NULL && !AVI_errno) {
+ AVI->aptr = 0; //reset
+ }
+
+ return (AVI_errno) ?NULL :AVI;
+}
+
+avi_t *AVI_open_input_indexfile(const char *filename, int getIndex,
+ const char *indexfile)
+{
+ int fd = plat_open(filename, O_RDONLY, 0);
+ if (fd < 0) {
+ AVI_errno = AVI_ERR_OPEN;
+ return NULL;
+ }
+ return AVI_open_indexfd(fd, getIndex, indexfile);
+}
+
+avi_t *AVI_open_input_file(const char *filename, int getIndex)
+{
+ return AVI_open_input_indexfile(filename, getIndex, NULL);
+}
+
+avi_t *AVI_open_fd(int fd, int getIndex)
+{
+ return AVI_open_indexfd(fd, getIndex, NULL);
+}
+
+// transcode-0.6.8
+// reads a file generated by aviindex and builds the index out of it.
+
+int avi_parse_index_from_file(avi_t *AVI, const char *filename)
+{
+ char data[100]; // line buffer
+ FILE *fd = NULL; // read from
+ off_t pos, len, f_pos, tot_chunks[AVI_MAX_TRACKS];
+ int key=0, type;
+ int vid_chunks=0, aud_chunks[AVI_MAX_TRACKS];
+ long line_count=0;
+ char *c, d;
+ int i,j;
+
+ for (i=0; i<AVI_MAX_TRACKS; i++) aud_chunks[i] = 0;
+ i=0;
+
+ // already have an index -- may be incomplete
+ if (AVI->video_index) {
+ plat_free(AVI->video_index);
+ AVI->video_index = NULL;
+ }
+
+ for(j=0; j<AVI->anum; ++j) {
+ if(AVI->track[j].audio_index) {
+ plat_free(AVI->track[j].audio_index);
+ }
+ AVI->track[j].audio_index = NULL;
+ AVI->track[j].audio_chunks = 0;
+ }
+
+ if (!(fd = fopen(filename, "r"))) { perror ("avi_parse_index_from_file: fopen"); return -1; }
+
+ // read header
+ fgets(data, 100, fd);
+
+ if ( strncasecmp(data, "AVIIDX1", 7) != 0) {
+ plat_log_send(PLAT_LOG_ERROR, __FILE__, "%s: Not an AVI index file", filename);
+ return -1;
+ }
+
+ // read comment
+ fgets(data, 100, fd);
+ f_pos = ftell(fd);
+ while (fgets(data, 100, fd)) {
+ d = data[5] - '1';
+ if (d == 0) {
+ vid_chunks++;
+ } else if (d == 1 || d == 2 || d == 3 || d == 4 ||
+ d == 5 || d == 6 || d == 7 || d == 8 ) {
+ aud_chunks[d-1]++;
+ } else
+ continue;
+
+ line_count++;
+ }
+
+ AVI->video_frames = vid_chunks;
+ for(j=0; j<AVI->anum; ++j) AVI->track[j].audio_chunks = aud_chunks[j];
+
+ if(AVI->video_frames==0) ERR_EXIT(AVI_ERR_NO_VIDS);
+ AVI->video_index = plat_malloc(vid_chunks*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 = plat_malloc(aud_chunks[j]*sizeof(audio_index_entry));
+ if(AVI->track[j].audio_index==0) ERR_EXIT(AVI_ERR_NO_MEM);
+ }
+ }
+
+ // reset after header
+ fseek(fd, f_pos, SEEK_SET);
+
+ vid_chunks = 0;
+ for(j=0; j<AVI->anum; ++j) aud_chunks[j] = tot_chunks[j] = 0;
+
+ while (fgets(data, 100, fd)) {
+ // this is very slow
+ // sscanf(data, "%*s %d %*d %*d %lld %lld %d %*f", &type, &pos, &len, &key);
+ c = strchr (data, ' ');
+ type = strtol(c+1, &c, 10);
+ //ch = strtol(c+1, &c, 10);
+ c = strchr(c+1, ' ');
+ //chtype= strtol(c+1, &c, 10);
+ c = strchr(c+1, ' ');
+ pos = strtoll(c+1, &c, 10);
+ len = strtol(c+1, &c, 10);
+ key = strtol(c+1, &c, 10);
+ //ms = strtod(c+1, NULL);
+ i = type-1;
+
+ switch (i) {
+ case 0: // video
+ AVI->video_index[vid_chunks].key = (off_t)(key?0x10:0);
+ AVI->video_index[vid_chunks].pos = pos+8;
+ AVI->video_index[vid_chunks].len = len;
+ vid_chunks++;
+ break;
+ case 1: case 2: case 3: case 4:
+ case 5: case 6: case 7: case 8:
+ j=i-1;
+ AVI->track[j].audio_index[aud_chunks[j]].pos = pos+8;
+ AVI->track[j].audio_index[aud_chunks[j]].len = len;
+ AVI->track[j].audio_index[aud_chunks[j]].tot = tot_chunks[j];
+ tot_chunks[j] += AVI->track[j].audio_index[aud_chunks[j]].len;
+ aud_chunks[j]++;
+ break;
+ default:
+ continue;
+ }
+
+ }
+ for(j=0; j<AVI->anum; ++j) AVI->track[j].audio_bytes = tot_chunks[j];
+
+ fclose (fd);
+
+ return 0;
+}
+
+static uint8_t *avi_build_audio_superindex(avisuperindex_chunk *si, uint8_t *a)
+{
+ int j = 0;
+
+ memcpy(si->fcc, a, 4); a += 4;
+ si->dwSize = str2ulong(a); a += 4;
+ si->wLongsPerEntry = str2ushort(a); a += 2;
+ si->bIndexSubType = *a; a += 1;
+ si->bIndexType = *a; a += 1;
+ si->nEntriesInUse = str2ulong(a); a += 4;
+ memcpy(si->dwChunkId, a, 4); a += 4;
+ // 3 * reserved
+ a += (3 * 4);
+
+ if (si->bIndexSubType != 0) {
+ plat_log_send(PLAT_LOG_WARNING, __FILE__, "Invalid Header, bIndexSubType != 0");
+ }
+
+ si->aIndex = plat_zalloc(si->wLongsPerEntry * si->nEntriesInUse * sizeof(uint32_t));
+ // position of ix## chunks
+ for (j=0; j < si->nEntriesInUse; ++j) {
+ si->aIndex[j].qwOffset = str2ullong(a); a += 8;
+ si->aIndex[j].dwSize = str2ulong(a); a += 4;
+ si->aIndex[j].dwDuration = str2ulong(a); a += 4;
+#ifdef DEBUG_ODML
+ printf("[%d] offset=0x%llx size=0x%lx duration=%lu\n", j,
+ (unsigned long long)si->aIndex[j].qwOffset,
+ (unsigned long)si->aIndex[j].dwSize,
+ (unsigned long)si->aIndex[j].dwDuration);
+#endif
+ }
+#ifdef DEBUG_ODML
+ printf(" FOURCC \"%.4s\"\n", si->fcc);
+ printf(" LEN \"%ld\"\n", si->dwSize);
+ printf(" wLongsPerEntry \"%d\"\n", si->wLongsPerEntry);
+ printf(" bIndexSubType \"%d\"\n", si->bIndexSubType);
+ printf(" bIndexType \"%d\"\n", si->bIndexType);
+ printf(" nEntriesInUse \"%ld\"\n", si->nEntriesInUse);
+ printf(" dwChunkId \"%.4s\"\n", si->dwChunkId[0]);
+ printf("--\n");
+#endif
+ return a;
+}
+
+
+
+static int avi_parse_input_file(avi_t *AVI, int getIndex)
+{
+ long i, rate, scale, idx_type;
+ uint8_t *hdrl_data = NULL;
+ long header_offset = 0, hdrl_len = 0;
+ long nvi, nai[AVI_MAX_TRACKS], ioff;
+ long tot[AVI_MAX_TRACKS];
+ int j, num_stream = 0;
+ int lasttag = 0;
+ int vids_strh_seen = 0;
+ int vids_strf_seen = 0;
+ int auds_strh_seen = 0;
+ // int auds_strf_seen = 0;
+ char data[256];
+ off_t oldpos=-1, newpos=-1, n;
+
+ /* Read first 12 bytes and check that this is an AVI file */
+
+ if( plat_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 */
+
+ while (1) {
+ if( plat_read(AVI->fdes,data,8) != 8 ) break; /* We assume it's EOF */
+ newpos=plat_seek(AVI->fdes,0,SEEK_CUR);
+ if(oldpos==newpos) {
+ /* This is a broken AVI stream... */
+ return -1;
+ }
+ oldpos=newpos;
+
+ n = str2ulong((unsigned char *)data+4);
+ n = PAD_EVEN(n);
+
+ if(strncasecmp(data,"LIST",4) == 0)
+ {
+ if( plat_read(AVI->fdes,data,4) != 4 ) ERR_EXIT(AVI_ERR_READ);
+ n -= 4;
+ if(strncasecmp(data,"hdrl",4) == 0)
+ {
+ hdrl_len = n;
+ hdrl_data = plat_malloc(n);
+ if(hdrl_data==0) ERR_EXIT(AVI_ERR_NO_MEM);
+
+ // offset of header
+
+ header_offset = plat_seek(AVI->fdes,0,SEEK_CUR);
+
+ if( plat_read(AVI->fdes,(char *)hdrl_data,n) != n ) ERR_EXIT(AVI_ERR_READ);
+ }
+ else if(strncasecmp(data,"movi",4) == 0)
+ {
+ AVI->movi_start = plat_seek(AVI->fdes,0,SEEK_CUR);
+ if (plat_seek(AVI->fdes,n,SEEK_CUR)==(off_t)-1) break;
+ }
+ else
+ if (plat_seek(AVI->fdes,n,SEEK_CUR)==(off_t)-1) break;
+ }
+ 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]) ) plat_malloc(n);
+ if(AVI->idx==0) ERR_EXIT(AVI_ERR_NO_MEM);
+ if(plat_read(AVI->fdes, (char *) AVI->idx, n) != n ) {
+ free ( AVI->idx); AVI->idx=NULL;
+ AVI->n_idx = 0;
+ }
+ }
+ else
+ plat_seek(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 */
+
+#ifdef DEBUG_ODML
+ printf("TAG %c%c%c%c\n", (hdrl_data+i)[0], (hdrl_data+i)[1], (hdrl_data+i)[2], (hdrl_data+i)[3]);
+#endif
+
+ 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;
+#ifdef DEBUG_ODML
+ printf("TAG %c%c%c%c\n", (hdrl_data+i)[0], (hdrl_data+i)[1], (hdrl_data+i)[2], (hdrl_data+i)[3]);
+#endif
+ 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(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) {
+ plat_log_send(PLAT_LOG_ERROR, __FILE__, "only %d audio tracks supported", 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;
+
+ // if samplesize==0 -> vbr
+ AVI->track[AVI->aptr].a_vbr = !str2ulong(hdrl_data+i+44);
+
+ AVI->track[AVI->aptr].padrate = str2ulong(hdrl_data+i+24);
+
+ // auds_strh_seen = 1;
+ lasttag = 2; /* auds */
+
+ // ThOe
+ AVI->track[AVI->aptr].a_codech_off = header_offset + i;
+
+ }
+ else if (strncasecmp (hdrl_data+i,"iavs",4) ==0 && ! auds_strh_seen) {
+ plat_log_send(PLAT_LOG_ERROR, __FILE__, "DV AVI Type 1 no supported");
+ return (-1);
+ }
+ else
+ lasttag = 0;
+ num_stream++;
+ }
+ else if(strncasecmp(hdrl_data+i,"dmlh",4) == 0) {
+ AVI->total_frames = str2ulong(hdrl_data+i+8);
+#ifdef DEBUG_ODML
+ fprintf(stderr, "real number of frames %d\n", AVI->total_frames);
+#endif
+ i += 8;
+ }
+ else if(strncasecmp((char *)hdrl_data+i,"strf",4)==0)
+ {
+ i += 8;
+ if(lasttag == 1)
+ {
+ alBITMAPINFOHEADER bih;
+
+ memcpy(&bih, hdrl_data + i, sizeof(alBITMAPINFOHEADER));
+ AVI->bitmap_info_header = plat_malloc(str2ulong((unsigned char *)&bih.bi_size));
+ if (AVI->bitmap_info_header != NULL)
+ memcpy(AVI->bitmap_info_header, hdrl_data + i,
+ str2ulong((unsigned char *)&bih.bi_size));
+
+ 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)
+ {
+ alWAVEFORMATEX *wfe;
+ char *nwfe;
+ int wfes;
+
+ if ((hdrl_len - i) < sizeof(alWAVEFORMATEX))
+ wfes = hdrl_len - i;
+ else
+ wfes = sizeof(alWAVEFORMATEX);
+ wfe = plat_zalloc(sizeof(alWAVEFORMATEX));
+ if (wfe != NULL) {
+ memcpy(wfe, hdrl_data + i, wfes);
+ if (str2ushort((unsigned char *)&wfe->cb_size) != 0) {
+ nwfe = plat_realloc(wfe, sizeof(alWAVEFORMATEX) +
+ str2ushort((unsigned char *)&wfe->cb_size));
+ if (nwfe != 0) {
+ off_t lpos = plat_seek(AVI->fdes, 0, SEEK_CUR);
+ plat_seek(AVI->fdes, header_offset + i + sizeof(alWAVEFORMATEX),
+ SEEK_SET);
+ wfe = (alWAVEFORMATEX *)nwfe;
+ nwfe = &nwfe[sizeof(alWAVEFORMATEX)];
+ plat_read(AVI->fdes, nwfe,
+ str2ushort((unsigned char *)&wfe->cb_size));
+ plat_seek(AVI->fdes, lpos, SEEK_SET);
+ }
+ }
+ AVI->wave_format_ex[AVI->aptr] = wfe;
+ }
+
+ 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;
+ }
+ }
+ else if(strncasecmp(hdrl_data+i,"indx",4) == 0) {
+ char *a;
+ int j;
+
+ if(lasttag == 1) // V I D E O
+ {
+
+ a = hdrl_data+i;
+
+ AVI->video_superindex = plat_zalloc(sizeof(avisuperindex_chunk));
+ memcpy (AVI->video_superindex->fcc, a, 4); a += 4;
+ AVI->video_superindex->dwSize = str2ulong(a); a += 4;
+ AVI->video_superindex->wLongsPerEntry = str2ushort(a); a += 2;
+ AVI->video_superindex->bIndexSubType = *a; a += 1;
+ AVI->video_superindex->bIndexType = *a; a += 1;
+ AVI->video_superindex->nEntriesInUse = str2ulong(a); a += 4;
+ memcpy (AVI->video_superindex->dwChunkId, a, 4); a += 4;
+
+ // 3 * reserved
+ a += 4; a += 4; a += 4;
+
+ if (AVI->video_superindex->bIndexSubType != 0) {
+ plat_log_send(PLAT_LOG_WARNING, __FILE__, "Invalid Header, bIndexSubType != 0");
+ }
+
+ AVI->video_superindex->aIndex =
+ plat_malloc(AVI->video_superindex->wLongsPerEntry * AVI->video_superindex->nEntriesInUse * sizeof(uint32_t));
+
+ // position of ix## chunks
+ for (j=0; j<AVI->video_superindex->nEntriesInUse; ++j) {
+ AVI->video_superindex->aIndex[j].qwOffset = str2ullong (a); a += 8;
+ AVI->video_superindex->aIndex[j].dwSize = str2ulong (a); a += 4;
+ AVI->video_superindex->aIndex[j].dwDuration = str2ulong (a); a += 4;
+
+#ifdef DEBUG_ODML
+ printf("[%d] 0x%llx 0x%lx %lu\n", j,
+ (unsigned long long)AVI->video_superindex->aIndex[j].qwOffset,
+ (unsigned long)AVI->video_superindex->aIndex[j].dwSize,
+ (unsigned long)AVI->video_superindex->aIndex[j].dwDuration);
+#endif
+ }
+
+
+#ifdef DEBUG_ODML
+ printf("FOURCC \"%c%c%c%c\"\n", AVI->video_superindex->fcc[0], AVI->video_superindex->fcc[1],
+ AVI->video_superindex->fcc[2], AVI->video_superindex->fcc[3]);
+ printf("LEN \"%ld\"\n", (long)AVI->video_superindex->dwSize);
+ printf("wLongsPerEntry \"%d\"\n", AVI->video_superindex->wLongsPerEntry);
+ printf("bIndexSubType \"%d\"\n", AVI->video_superindex->bIndexSubType);
+ printf("bIndexType \"%d\"\n", AVI->video_superindex->bIndexType);
+ printf("nEntriesInUse \"%ld\"\n", (long)AVI->video_superindex->nEntriesInUse);
+ printf("dwChunkId \"%c%c%c%c\"\n", AVI->video_superindex->dwChunkId[0], AVI->video_superindex->dwChunkId[1],
+ AVI->video_superindex->dwChunkId[2], AVI->video_superindex->dwChunkId[3]);
+ printf("--\n");
+#endif
+
+ AVI->is_opendml = 1;
+
+ }
+ else if(lasttag == 2) // A U D I O
+ {
+ a = hdrl_data+i;
+
+ AVI->track[AVI->aptr].audio_superindex = plat_zalloc(sizeof(avisuperindex_chunk));
+
+ a = avi_build_audio_superindex(AVI->track[AVI->aptr].audio_superindex, a);
+ }
+ i += 8;
+ }
+ else if((strncasecmp(hdrl_data+i,"JUNK",4) == 0) ||
+ (strncasecmp(hdrl_data+i,"strn",4) == 0) ||
+ (strncasecmp(hdrl_data+i,"vprp",4) == 0)){
+ i += 8;
+ // do not reset lasttag
+ } else
+ {
+ i += 8;
+ lasttag = 0;
+ }
+ //printf("adding %ld bytes\n", (long int)n);
+
+ i += n;
+ }
+
+ plat_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;
+
+ {
+ int i=0;
+ for(j=0; j<AVI->anum+1; ++j) {
+ if (j == AVI->video_strn) continue;
+ AVI->track[i].audio_tag[0] = j/10 + '0';
+ AVI->track[i].audio_tag[1] = j%10 + '0';
+ AVI->track[i].audio_tag[2] = 'w';
+ AVI->track[i].audio_tag[3] = 'b';
+ ++i;
+ }
+ }
+
+ plat_seek(AVI->fdes,AVI->movi_start,SEEK_SET);
+
+ /* get index if wanted */
+
+ if(AVI->index_file && !getIndex) {
+ int ret;
+
+ ret = avi_parse_index_from_file(AVI, AVI->index_file);
+
+ /* Reposition the file */
+
+ plat_seek(AVI->fdes,AVI->movi_start,SEEK_SET);
+ AVI->video_pos = 0;
+ return (ret);
+
+ }
+ 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)
+ {
+ off_t 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);
+
+ plat_seek(AVI->fdes,pos,SEEK_SET);
+ if(plat_read(AVI->fdes,data,8)!=8) ERR_EXIT(AVI_ERR_READ);
+ if( strncasecmp(data,(char *)AVI->idx[i],4)==0 && str2ulong((unsigned char *)data+4)==len )
+ {
+ idx_type = 1; /* Index from start of file */
+ }
+ else
+ {
+ plat_seek(AVI->fdes,pos+AVI->movi_start-4,SEEK_SET);
+ if(plat_read(AVI->fdes,data,8)!=8) ERR_EXIT(AVI_ERR_READ);
+ if( strncasecmp(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 && !AVI->is_opendml && !AVI->total_frames)
+ {
+ /* we must search through the file to get the index */
+
+ plat_seek(AVI->fdes, AVI->movi_start, SEEK_SET);
+
+ AVI->n_idx = 0;
+
+ while(1)
+ {
+ if( plat_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)
+ {
+ plat_seek(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,plat_seek(AVI->fdes,0,SEEK_CUR)-8,n);
+ }
+
+ plat_seek(AVI->fdes,PAD_EVEN(n),SEEK_CUR);
+ }
+ idx_type = 1;
+ }
+
+ // ************************
+ // OPENDML
+ // ************************
+
+ // read extended index chunks
+ if (AVI->is_opendml) {
+ uint64_t offset = 0;
+ int hdrl_len = 4+4+2+1+1+4+4+8+4;
+ char *en, *chunk_start;
+ int k = 0, audtr = 0;
+ uint32_t nrEntries = 0;
+
+ AVI->video_index = NULL;
+
+ nvi = 0;
+ for(audtr=0; audtr<AVI->anum; ++audtr) nai[audtr] = tot[audtr] = 0;
+
+ // ************************
+ // VIDEO
+ // ************************
+
+ for (j=0; j<AVI->video_superindex->nEntriesInUse; j++) {
+
+ // read from file
+ chunk_start = en = plat_malloc (AVI->video_superindex->aIndex[j].dwSize+hdrl_len);
+
+ if (plat_seek(AVI->fdes, AVI->video_superindex->aIndex[j].qwOffset, SEEK_SET) == (off_t)-1) {
+ plat_log_send(PLAT_LOG_WARNING, __FILE__, "cannot seek to 0x%llx",
+ (unsigned long long)AVI->video_superindex->aIndex[j].qwOffset);
+ plat_free(chunk_start);
+ continue;
+ }
+
+ if (plat_read(AVI->fdes, en, AVI->video_superindex->aIndex[j].dwSize+hdrl_len) <= 0) {
+ plat_log_send(PLAT_LOG_WARNING, __FILE__,
+ "cannot read from offset 0x%llx %ld bytes; broken (incomplete) file?",
+ (unsigned long long)AVI->video_superindex->aIndex[j].qwOffset,
+ (unsigned long)AVI->video_superindex->aIndex[j].dwSize+hdrl_len);
+ plat_free(chunk_start);
+ continue;
+ }
+
+ nrEntries = str2ulong(en + 12);
+#ifdef DEBUG_ODML
+ //printf("[%d:0] Video nrEntries %ld\n", j, nrEntries);
+#endif
+ offset = str2ullong(en + 20);
+
+ // skip header
+ en += hdrl_len;
+ nvi += nrEntries;
+ AVI->video_index = plat_realloc(AVI->video_index, nvi * sizeof(video_index_entry));
+ if (!AVI->video_index) {
+ plat_log_send(PLAT_LOG_ERROR, __FILE__, "out of mem (size = %ld)",
+ nvi * sizeof(video_index_entry));
+ exit(1); // XXX XXX XXX
+ }
+
+ while (k < nvi) {
+
+ AVI->video_index[k].pos = offset + str2ulong(en); en += 4;
+ AVI->video_index[k].len = str2ulong_len(en);
+ AVI->video_index[k].key = str2ulong_key(en); en += 4;
+
+ // completely empty chunk
+ if (AVI->video_index[k].pos-offset == 0 && AVI->video_index[k].len == 0) {
+ k--;
+ nvi--;
+ }
+
+#ifdef DEBUG_ODML
+ /*
+ printf("[%d] POS 0x%llX len=%d key=%s offset (%llx) (%ld)\n", k,
+ AVI->video_index[k].pos,
+ (int)AVI->video_index[k].len,
+ AVI->video_index[k].key?"yes":"no ", offset,
+ AVI->video_superindex->aIndex[j].dwSize);
+ */
+#endif
+
+ k++;
+ }
+
+ plat_free(chunk_start);
+ }
+
+ AVI->video_frames = nvi;
+ // this should deal with broken 'rec ' odml files.
+ if (AVI->video_frames == 0) {
+ AVI->is_opendml=0;
+ goto multiple_riff;
+ }
+
+ // ************************
+ // AUDIO
+ // ************************
+
+ for(audtr=0; audtr<AVI->anum; ++audtr) {
+
+ k = 0;
+ if (!AVI->track[audtr].audio_superindex) {
+ plat_log_send(PLAT_LOG_WARNING, __FILE__, "cannot read audio index for track %d", audtr);
+ continue;
+ }
+ for (j=0; j<AVI->track[audtr].audio_superindex->nEntriesInUse; j++) {
+
+ // read from file
+ chunk_start = en = plat_malloc(AVI->track[audtr].audio_superindex->aIndex[j].dwSize+hdrl_len);
+
+ if (plat_seek(AVI->fdes, AVI->track[audtr].audio_superindex->aIndex[j].qwOffset, SEEK_SET) == (off_t)-1) {
+ plat_log_send(PLAT_LOG_WARNING, __FILE__,
+ "cannot seek to 0x%llx",
+ (unsigned long long)AVI->track[audtr].audio_superindex->aIndex[j].qwOffset);
+ plat_free(chunk_start);
+ continue;
+ }
+
+ if (plat_read(AVI->fdes, en, AVI->track[audtr].audio_superindex->aIndex[j].dwSize+hdrl_len) <= 0) {
+ plat_log_send(PLAT_LOG_WARNING, __FILE__,
+ "cannot read from offset 0x%llx; broken (incomplete) file?",
+ (unsigned long long) AVI->track[audtr].audio_superindex->aIndex[j].qwOffset);
+ plat_free(chunk_start);
+ continue;
+ }
+
+ nrEntries = str2ulong(en + 12);
+ //if (nrEntries > 50) nrEntries = 2; // XXX
+#ifdef DEBUG_ODML
+ //printf("[%d:%d] Audio nrEntries %ld\n", j, audtr, nrEntries);
+#endif
+ offset = str2ullong(en + 20);
+
+ // skip header
+ en += hdrl_len;
+ nai[audtr] += nrEntries;
+ AVI->track[audtr].audio_index = plat_realloc(AVI->track[audtr].audio_index, nai[audtr] * sizeof(audio_index_entry));
+
+ while (k < nai[audtr]) {
+
+ AVI->track[audtr].audio_index[k].pos = offset + str2ulong(en); en += 4;
+ AVI->track[audtr].audio_index[k].len = str2ulong_len(en); en += 4;
+ AVI->track[audtr].audio_index[k].tot = tot[audtr];
+ tot[audtr] += AVI->track[audtr].audio_index[k].len;
+
+#ifdef DEBUG_ODML
+ /*
+ printf("[%d:%d] POS 0x%llX len=%d offset (%llx) (%ld)\n", k, audtr,
+ AVI->track[audtr].audio_index[k].pos,
+ (int)AVI->track[audtr].audio_index[k].len,
+ offset, AVI->track[audtr].audio_superindex->aIndex[j].dwSize);
+ */
+#endif
+
+ ++k;
+ }
+
+ plat_free(chunk_start);
+ }
+
+ AVI->track[audtr].audio_chunks = nai[audtr];
+ AVI->track[audtr].audio_bytes = tot[audtr];
+ }
+ } // is opendml
+
+ else if (AVI->total_frames && !AVI->is_opendml && idx_type==0) {
+
+ // *********************
+ // MULTIPLE RIFF CHUNKS (and no index)
+ // *********************
+
+ long aud_chunks = 0;
+multiple_riff:
+
+ plat_seek(AVI->fdes, AVI->movi_start, SEEK_SET);
+
+ AVI->n_idx = 0;
+
+ plat_log_send(PLAT_LOG_INFO, __FILE__, "Reconstructing index...");
+
+ // Number of frames; only one audio track supported
+ nvi = AVI->video_frames = AVI->total_frames;
+ nai[0] = AVI->track[0].audio_chunks = AVI->total_frames;
+ for(j=1; j<AVI->anum; ++j) AVI->track[j].audio_chunks = 0;
+
+ AVI->video_index = plat_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 = plat_zalloc((nai[j]+1)*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;
+
+ aud_chunks = AVI->total_frames;
+
+ while(1)
+ {
+ if (nvi >= AVI->total_frames) break;
+
+ if( plat_read(AVI->fdes,data,8) != 8 ) break;
+ n = str2ulong((unsigned char *)data+4);
+
+
+ j=0;
+
+ if (aud_chunks - nai[j] -1 <= 0) {
+ aud_chunks += AVI->total_frames;
+ AVI->track[j].audio_index = plat_realloc( AVI->track[j].audio_index, (aud_chunks+1)*sizeof(audio_index_entry));
+ if (!AVI->track[j].audio_index) {
+ plat_log_send(PLAT_LOG_ERROR, __FILE__, "Internal error -- no mem");
+ AVI_errno = AVI_ERR_NO_MEM;
+ return -1;
+ }
+ }
+
+ /* Check if we got a tag ##db, ##dc or ##wb */
+
+ // VIDEO
+ if(
+ (data[0]=='0' || data[1]=='0') &&
+ (data[2]=='d' || data[2]=='D') &&
+ (data[3]=='b' || data[3]=='B' || data[3]=='c' || data[3]=='C') ) {
+
+ AVI->video_index[nvi].key = 0x0;
+ AVI->video_index[nvi].pos = plat_seek(AVI->fdes,0,SEEK_CUR);
+ AVI->video_index[nvi].len = n;
+
+ /*
+ fprintf(stderr, "Frame %ld pos %lld len %lld key %ld\n",
+ nvi, AVI->video_index[nvi].pos, AVI->video_index[nvi].len, (long)AVI->video_index[nvi].key);
+ */
+ nvi++;
+ plat_seek(AVI->fdes,PAD_EVEN(n),SEEK_CUR);
+ }
+
+ //AUDIO
+ else if(
+ (data[0]=='0' || data[1]=='1') &&
+ (data[2]=='w' || data[2]=='W') &&
+ (data[3]=='b' || data[3]=='B') ) {
+
+
+ AVI->track[j].audio_index[nai[j]].pos = plat_seek(AVI->fdes,0,SEEK_CUR);
+ AVI->track[j].audio_index[nai[j]].len = n;
+ AVI->track[j].audio_index[nai[j]].tot = tot[j];
+ tot[j] += AVI->track[j].audio_index[nai[j]].len;
+ nai[j]++;
+
+ plat_seek(AVI->fdes,PAD_EVEN(n),SEEK_CUR);
+ }
+ else {
+ plat_seek(AVI->fdes,-4,SEEK_CUR);
+ }
+
+ }
+ if (nvi < AVI->total_frames) {
+ plat_log_send(PLAT_LOG_WARNING, __FILE__,
+ "Uh? Some frames seems missing (%ld/%d)",
+ nvi, AVI->total_frames);
+ }
+
+
+ AVI->video_frames = nvi;
+ AVI->track[0].audio_chunks = nai[0];
+
+ for(j=0; j<AVI->anum; ++j) AVI->track[j].audio_bytes = tot[j];
+ idx_type = 1;
+ plat_log_send(PLAT_LOG_INFO, __FILE__,
+ "done. nvi=%ld nai=%ld tot=%ld", nvi, nai[0], tot[0]);
+
+ } // total_frames but no indx chunk (xawtv does this)
+
+ else
+
+ {
+ // ******************
+ // NO OPENDML
+ // ******************
+
+ /* 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],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];
+
+
+ if(AVI->video_frames==0) ERR_EXIT(AVI_ERR_NO_VIDS);
+ AVI->video_index = plat_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 = plat_zalloc((nai[j]+1)*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],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];
+
+ } // is no opendml
+
+ /* Reposition the file */
+
+ plat_seek(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;
+}
+
+long AVI_audio_padrate(avi_t *AVI)
+{
+ return AVI->track[AVI->aptr].padrate;
+}
+
+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 -1;
+ 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; }
+
+ plat_seek(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_video(avi_t *AVI, char *vidbuf, long bytes, 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;
+
+ if (bytes != -1 && bytes < n) {
+ AVI_errno = AVI_ERR_NO_BUFSIZE;
+ return -1;
+ }
+
+ *keyframe = (AVI->video_index[AVI->video_pos].key==0x10) ? 1:0;
+
+ if (vidbuf == NULL) {
+ AVI->video_pos++;
+ return n;
+ }
+
+ plat_seek(AVI->fdes, AVI->video_index[AVI->video_pos].pos, SEEK_SET);
+
+ if (plat_read(AVI->fdes,vidbuf,n) != n)
+ {
+ AVI_errno = AVI_ERR_READ;
+ return -1;
+ }
+
+ AVI->video_pos++;
+
+ return n;
+}
+
+long AVI_read_frame(avi_t *AVI, char *vidbuf, int *keyframe)
+{
+ return AVI_read_video(AVI, vidbuf, -1, keyframe);
+}
+
+
+long AVI_get_audio_position_index(avi_t *AVI)
+{
+ 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; }
+
+ return (AVI->track[AVI->aptr].audio_posc);
+}
+
+int AVI_set_audio_position_index(avi_t *AVI, long indexpos)
+{
+ 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(indexpos > AVI->track[AVI->aptr].audio_chunks) { AVI_errno = AVI_ERR_NO_IDX; return -1; }
+
+ AVI->track[AVI->aptr].audio_posc = indexpos;
+ AVI->track[AVI->aptr].audio_posb = 0;
+
+ return 0;
+}
+
+
+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, left, todo;
+ off_t pos;
+
+ 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 */
+
+ if (bytes==0) {
+ AVI->track[AVI->aptr].audio_posc++;
+ AVI->track[AVI->aptr].audio_posb = 0;
+ plat_seek(AVI->fdes, 0LL, SEEK_CUR);
+ }
+ while(bytes>0)
+ {
+ off_t ret;
+ 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;
+ plat_seek(AVI->fdes, pos, SEEK_SET);
+ if ( (ret = plat_read(AVI->fdes,audbuf+nr,todo)) != todo)
+ {
+ plat_log_send(PLAT_LOG_DEBUG, __FILE__, "XXX pos = %lld, ret = %lld, todo = %ld",
+ (long long)pos, (long long)ret, todo);
+ AVI_errno = AVI_ERR_READ;
+ return -1;
+ }
+ bytes -= todo;
+ nr += todo;
+ AVI->track[AVI->aptr].audio_posb += todo;
+ }
+
+ return nr;
+}
+
+long AVI_read_audio_chunk(avi_t *AVI, char *audbuf)
+{
+ long left;
+ off_t pos;
+
+ 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 (AVI->track[AVI->aptr].audio_posc+1>AVI->track[AVI->aptr].audio_chunks) return -1;
+
+ left = AVI->track[AVI->aptr].audio_index[AVI->track[AVI->aptr].audio_posc].len - AVI->track[AVI->aptr].audio_posb;
+
+ if (audbuf == NULL) return left;
+
+ if(left==0) {
+ AVI->track[AVI->aptr].audio_posc++;
+ AVI->track[AVI->aptr].audio_posb = 0;
+ return 0;
+ }
+
+ pos = AVI->track[AVI->aptr].audio_index[AVI->track[AVI->aptr].audio_posc].pos + AVI->track[AVI->aptr].audio_posb;
+ plat_seek(AVI->fdes, pos, SEEK_SET);
+ if (plat_read(AVI->fdes,audbuf,left) != left)
+ {
+ AVI_errno = AVI_ERR_READ;
+ return -1;
+ }
+ AVI->track[AVI->aptr].audio_posc++;
+ AVI->track[AVI->aptr].audio_posb = 0;
+
+ return left;
+}
+
+/* AVI_print_error: Print most recent error (similar to perror) */
+
+static const 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 - destination buffer is too small",
+ /* 15 */ "avilib - Unkown Error"
+};
+static int num_avi_errors = sizeof(avi_errors)/sizeof(char*);
+
+void AVI_print_error(const char *str)
+{
+ int aerrno = (AVI_errno>=0 && AVI_errno<num_avi_errors) ?AVI_errno :num_avi_errors-1;
+
+ if (aerrno != 0)
+ plat_log_send(PLAT_LOG_ERROR, __FILE__, "%s: %s", 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");
+ }
+}
+
+const char *AVI_strerror(void)
+{
+ static char error_string[4096];
+ int 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 ) {
+ snprintf(error_string, sizeof(error_string), "%s - %s",avi_errors[aerrno],strerror(errno));
+ return error_string;
+ }
+ return avi_errors[aerrno];
+}
+
+uint64_t AVI_max_size(void)
+{
+ return((uint64_t)AVI_MAX_LEN);
+}
+
+// EOF