[MPlayer-dev-eng] [PATCH] OpenDML AVI2.0 read support

Tilmann Bitterberg transcode at tibit.org
Thu Jan 29 18:00:05 CET 2004


This patch adds support for reading OpenDML (AVI 2.0) files.
In case you don't know what odml is, have a look at
http://www.the-labs.com/Video/odmlff2-avidef.pdf

The patch tries to be as unintrusive as possible. You'll notice
that it barely changes any lines but adds new ones instead.

The patch is quite well tested, only on x86, though. on mphq
there are already some odml sample files:

../samples/avi/
  ./processor_burning.avi
  ./!AVI-demuxer-bugs/forceidx_fixes/iceage.avi

These files have a standard idx1 chunk and opendml ix00 chunks.
I tested also with odml files written by kino and written by
transcode(<=0.6.12). Its easy to create such files with avimerge
from the transcode package, For your convenience, I have put up
a precompiled dynamic version of this tool at (hope it works)
http://zebra.fh-weingarten.de/~maxi/avimerge
Just take any regular (700MB) AVI and merge them:
avimerge -c -i file.avi file.avi file.avi file.avi -o large.avi
so that large.avi will be larger than 2GB. avimerge creates an
odml file in such a case.

Details:
Since mplayer does not have an AVI index abstraction I had to
do some trickery. In AVI2.0, the indices are not interleaved
which means I have to do that. I do this in such a way, that
mplayers seeking algorithm will find the matching packet as soon
as possible.
To find the base file offset of a particular chunk, I code an
index into the offset table in the upper 16bits of the dwFlags
field. This adds a slight overhead at playtime, since there will
be table lookup for every chunk. All the rest of this patch is
happening at init time only.
Multiple audio tracks are supported as well.

Limitations:
The AVI 2.0 standard talks about a superindex of superindices. I
have never seen such a file and I don't support it.
If there are odml indices, I always use them and forget about
the standard idx1 chunk. That can be good or bad.
I trust the values which are written in the super index header.

Tilmann
-- 
Sometimes transcode changes or   |    http://www.transcoding.org/
adds new features while you      |      Searchable ML-archives
are encoding.                    |  http://itdp.de/transcode-users/
            -- ThOe              |         IRCnet #transcode
-------------- next part --------------
diff -Nur -X ../dontdiff orig/libmpdemux/aviheader.c main/libmpdemux/aviheader.c
--- orig/libmpdemux/aviheader.c	2003-10-22 19:01:37.000000000 +0000
+++ main/libmpdemux/aviheader.c	2004-01-29 16:45:07.595823808 +0000
@@ -26,6 +26,24 @@
 extern void print_video_header(BITMAPINFOHEADER *h);
 extern void print_index(AVIINDEXENTRY *idx,int idx_size);
 
+void print_avistdindex_chunk(avistdindex_chunk *h){
+    printf("====== AVI Standard Index Header ========\n");
+    printf("  FCC (%.4s) dwSize (%d) wLongsPerEntry(%d)\n", h->fcc, h->dwSize, h->wLongsPerEntry);
+    printf("  bIndexSubType (%d) bIndexType (%d)\n", h->bIndexSubType, h->bIndexType);
+    printf("  nEntriesInUse (%d) dwChunkId (%.4s)\n", h->nEntriesInUse, h->dwChunkId);
+    printf("  qwBaseOffset (0x%llX) dwReserved3 (%d)\n", h->qwBaseOffset, h->dwReserved3);
+    printf("===========================\n");
+}
+void print_avisuperindex_chunk(avisuperindex_chunk *h){
+    printf("====== AVI Super Index Header ========\n");
+    printf("  FCC (%.4s) dwSize (%d) wLongsPerEntry(%d)\n", h->fcc, h->dwSize, h->wLongsPerEntry);
+    printf("  bIndexSubType (%d) bIndexType (%d)\n", h->bIndexSubType, h->bIndexType);
+    printf("  nEntriesInUse (%d) dwChunkId (%.4s)\n", h->nEntriesInUse, h->dwChunkId);
+    printf("  dwReserved[0] (%d) dwReserved[1] (%d) dwReserved[2] (%d)\n", 
+	    h->dwReserved[0], h->dwReserved[1], h->dwReserved[2]);
+    printf("===========================\n");
+}
+
 void read_avi_header(demuxer_t *demuxer,int index_mode){
 sh_audio_t *sh_audio=NULL;
 sh_video_t *sh_video=NULL;
@@ -179,6 +197,45 @@
       last_fccType=h.fccType;
       if(verbose>=1) print_strh(&h);
       break; }
+    case mmioFOURCC('i', 'n', 'd', 'x'): {
+      DWORD i;
+      unsigned msize = 0;
+      avisuperindex_chunk *s;
+      priv->suidx_size++;
+      priv->suidx = realloc(priv->suidx, priv->suidx_size * sizeof (avisuperindex_chunk));
+      s = &priv->suidx[priv->suidx_size-1];
+
+      memcpy(s->fcc, "indx", 4);
+      s->dwSize = size2;
+      s->wLongsPerEntry = stream_read_word_le(demuxer->stream);
+      s->bIndexSubType = stream_read_char(demuxer->stream);
+      s->bIndexType = stream_read_char(demuxer->stream);
+      s->nEntriesInUse = stream_read_dword_le(demuxer->stream);
+      *(uint32_t *)s->dwChunkId = stream_read_dword_le(demuxer->stream);
+      stream_read(demuxer->stream, (char *)s->dwReserved, 3*4);
+      memset(s->dwReserved, 0, 3*4);
+	  
+      if (verbose>0) print_avisuperindex_chunk(s);
+
+      msize = sizeof (uint32_t) * s->wLongsPerEntry * s->nEntriesInUse;
+      s->aIndex = malloc(msize);
+      memset (s->aIndex, 0, msize);
+      s->stdidx = malloc (s->nEntriesInUse * sizeof (avistdindex_chunk));
+      memset (s->stdidx, 0, s->nEntriesInUse * sizeof (avistdindex_chunk));
+
+      // now the real index of indices
+      for (i=0; i<s->nEntriesInUse; i++) {
+	  s->aIndex[i].qwOffset = stream_read_dword_le(demuxer->stream) & 0xffffffff;
+	  s->aIndex[i].qwOffset |= ((uint64_t)stream_read_dword_le(demuxer->stream) & 0xffffffff)<<32;
+	  s->aIndex[i].dwSize = stream_read_dword_le(demuxer->stream);
+	  s->aIndex[i].dwDuration = stream_read_dword_le(demuxer->stream);
+	  if (verbose>0)printf("ODML (%.4s): [%d] 0x%016llx 0x%04lx %ld\n", 
+		  (s->dwChunkId), i,
+		  (uint64_t)s->aIndex[i].qwOffset, s->aIndex[i].dwSize, s->aIndex[i].dwDuration);
+      }
+      priv->isodml++;
+
+      break; }
     case ckidSTREAMFORMAT: {      // read 'strf'
       if(last_fccType==streamtypeVIDEO){
         sh_video->bih=calloc((chunksize<sizeof(BITMAPINFOHEADER))?sizeof(BITMAPINFOHEADER):chunksize,1);
@@ -246,10 +303,17 @@
       }
       break;
     }
+    case mmioFOURCC('d', 'm', 'l', 'h'): {
+	// dmlh 00 00 00 04 frms
+	unsigned total_frames = stream_read_dword_le(demuxer->stream);
+	mp_msg(MSGT_HEADER,MSGL_V,"AVI: dmlh found (size=%d) (total_frames=%d)\n", chunksize, total_frames);
+	stream_skip(demuxer->stream, chunksize-4);
+    }
+    break;
     case ckidAVINEWINDEX:
     if(demuxer->movi_end>stream_tell(demuxer->stream))
 	demuxer->movi_end=stream_tell(demuxer->stream); // fixup movi-end
-    if(index_mode){
+    if(index_mode && !priv->isodml){
       int i;
       priv->idx_size=size2>>4;
       mp_msg(MSGT_HEADER,MSGL_V,"Reading INDEX block, %d chunks for %ld frames (fpos=%p)\n",
@@ -303,6 +367,199 @@
   
 }
 
+
+if (priv->isodml) {
+    int i, j, k;
+    int safety=1000;
+
+    avisuperindex_chunk *cx;
+    AVIINDEXENTRY *idx;
+
+
+    if (priv->idx_size) free(priv->idx);
+    priv->offsets_size=0;
+    priv->idx_size = 0;
+    priv->idx_offset = 0;
+    priv->idx = NULL;
+
+    if (demuxer->stream->type != STREAMTYPE_FILE) {
+	priv->isodml=0;
+	goto freeout;
+    }
+    mp_msg(MSGT_HEADER, MSGL_INFO, 
+	    "AVI: ODML: Building odml index (%d superindexchunks)\n", priv->suidx_size);
+
+    //  count number of stdindex entries in all superindices
+    j=0; cx = &priv->suidx[0];
+    do j+=cx->nEntriesInUse;
+    while (cx++ != &priv->suidx[priv->suidx_size-1]);
+
+    priv->offsets = malloc(sizeof(uint64_t) * (j+1));
+
+    // read the standard indices
+    for (cx = &priv->suidx[0], i=0; i<priv->suidx_size; cx++, i++) {
+	stream_reset(demuxer->stream);
+	for (j=0; j<cx->nEntriesInUse; j++) {
+	    int ret1, ret2;
+	    memset(&cx->stdidx[j], 0, 32);
+	    ret1 = stream_seek(demuxer->stream, (off_t)cx->aIndex[j].qwOffset);
+	    ret2 = stream_read(demuxer->stream, (char *)&cx->stdidx[j], 32);
+	    if (ret1 != 1 || ret2 != 32 || cx->stdidx[j].nEntriesInUse==0) {
+		// this is a broken file (probably incomplete) let the standard
+		// gen_index routine handle this
+		priv->isodml = 0;
+		priv->idx_size = 0;
+		memset(priv->offsets, 0, 64);
+		priv->offsets_size = 0;
+		mp_msg(MSGT_HEADER, MSGL_WARN,
+			"AVI: ODML: Broken (incomplete?) file detected. Will use traditional index\n");
+		goto freeout;
+	    }
+
+	    if (verbose>0) print_avistdindex_chunk(&cx->stdidx[j]);
+	    le2me_AVISTDIDXCHUNK(&cx->stdidx[j]);
+	    priv->idx_size += cx->stdidx[j].nEntriesInUse;
+	    priv->offsets[priv->offsets_size++] = cx->stdidx[j].qwBaseOffset - 8;
+	    cx->stdidx[j].aIndex = malloc(cx->stdidx[j].nEntriesInUse*sizeof(avistdindex_entry));
+	    stream_read(demuxer->stream, (char *)cx->stdidx[j].aIndex, 
+		    cx->stdidx[j].nEntriesInUse*sizeof(avistdindex_entry));
+	    //printf("[%d:%d] Entries (%ld)\n", i, j, cx->stdidx[j].nEntriesInUse);
+	    for (k=0;k<cx->stdidx[j].nEntriesInUse; k++)
+		le2me_AVISTDIDXENTRY(cx->stdidx[j].aIndex[k]);
+
+	    cx->stdidx[j].dwReserved3 = 0;
+
+	}
+    }
+
+    // put the number of frames per superindex into dwReserved[0]
+    cx = &priv->suidx[0];
+    do for (j=0;j<cx->nEntriesInUse;j++)
+	    cx->dwReserved[0] += cx->stdidx[j].nEntriesInUse;
+    while (cx++ != &priv->suidx[priv->suidx_size-1]);
+
+    /* 
+       priv->
+       suidx[0] ----- stdidx[0] ----- aIndex[0]
+               \                `-----aIndex[1]
+                `---- stdidx[1] --...
+       suidx[1] ---...
+
+       This code is not nice but has to done. It copies the stdindex entries
+       into the standard index structure mplayer wants. It has to interleave
+       them while doing this. The algorithm does so by looking at how many
+       packets from one stream already went into the index and choosing the
+       stream which has the least percent of packets in the idx1.
+                                     --tibit
+
+       We "recycle" the dwReserved variables as counters
+       cx->dwReserved[0] = number of total frames per superindex
+       cx->dwReserved[2] = number of used frames per superindex
+       cx->stdidx[n].dwReserved3 = number of used frames in this stdindex
+     */
+
+
+    priv->idx = malloc(priv->idx_size * sizeof (AVIINDEXENTRY));
+    idx = &((AVIINDEXENTRY *)priv->idx)[0];
+
+    cx = &priv->suidx[0];
+
+    safety = 1000;
+
+    // Interleave. Could be done smarter I guess
+    for (i=0; i<priv->idx_size; i++) {
+	avistdindex_entry *e;
+
+	//if (i>10000)printf("[%08d-%08d] %.4s\n", i, priv->idx_size, cx->dwChunkId);
+
+	// NR of used / NR of total == stdindex index
+	j = cx->dwReserved[2] / cx->dwReserved[0];
+	if (j==cx->nEntriesInUse) { i--; safety--; goto tryagain; }
+
+	e = &cx->stdidx[j].aIndex[cx->stdidx[j].dwReserved3];
+	memcpy(&idx->ckid, cx->stdidx[j].dwChunkId, 4);
+	idx->dwChunkOffset = e->dwOffset;
+	idx->dwFlags = idx->dwChunkLength = e->dwSize;
+	idx->dwChunkLength &= 0x7fffffff;
+	idx->dwFlags = (idx->dwFlags&0x80000000)?0x0:AVIIF_KEYFRAME; // first bit denotes !keyframe
+	k = 0;
+	while (priv->offsets[k]+8 != cx->stdidx[j].qwBaseOffset) {
+	    k++;
+	    if (k==priv->offsets_size)
+		    mp_msg(MSGT_HEADER,MSGL_ERR, 
+			    "AVI: ODML: Internal error. Can't find offset in array\n");
+	}
+	// We now put the index chunk where this is in into the upper 16 bits
+	idx->dwFlags |= (k<<16)&0xffff0000;
+
+	cx->dwReserved[2]++;
+	cx->stdidx[j].dwReserved3++;
+
+	idx++;
+	safety = 1000;
+tryagain:
+	{
+	    avisuperindex_chunk *mincx;
+	    float percent, old_percent=1000.0;
+
+	    // find the cx with the least percentage written
+	    cx = &priv->suidx[0];
+	    do {
+		percent = cx->dwReserved[2]*1000.0/cx->dwReserved[0];
+		if (percent < old_percent) {
+		    old_percent = percent;
+		    mincx = cx;
+		}
+
+	    } while (cx++ != &priv->suidx[priv->suidx_size-1]);
+
+	    cx = mincx;
+	    if (safety <= 0) mp_msg(MSGT_HEADER, MSGL_ERR,
+		    "AVI: ODML: Internal error. Endless loop. Please bugreport\n");
+	}
+    }
+
+    /* 
+       Hack to work around a wrong index in div3 odml files
+       (processor_burning.avi as an example)
+       They have 00dc on non keyframes but the ix00 tells us they are 00db.
+       Read the fcc of a non-keyframe vid frame and check it.
+     */
+    if (idxfix_divx==1) {
+	uint32_t id;
+	uint32_t db = mmioFOURCC('0','0','d','b');
+	stream_reset (demuxer->stream);
+	// check first
+	for (idx = &((AVIINDEXENTRY *)priv->idx)[0], i=0; i<priv->idx_size; i++, idx++){
+	    // find first non keyframe
+	    if (!((idx->dwFlags&0xffff) & AVIIF_KEYFRAME) && idx->ckid == db) break;
+	}
+	if (i<priv->idx_size) { // found one, fix it
+	    stream_seek(demuxer->stream, idx->dwChunkOffset+priv->offsets[(idx->dwFlags>>16)&0xffff]);
+	    id = stream_read_dword_le(demuxer->stream);
+	    if (id != db)
+		for (idx = &((AVIINDEXENTRY *)priv->idx)[0], i=0; i<priv->idx_size; i++, idx++){
+		    if (!((idx->dwFlags&0xffff) & AVIIF_KEYFRAME) && idx->ckid == db)
+			idx->ckid = id;
+	    }
+	}
+    }
+freeout:
+
+    // free unneeded stuff
+    cx = &priv->suidx[0];
+    do {
+	for (j=0;j<cx->nEntriesInUse;j++)
+	    if (cx->stdidx[j].nEntriesInUse) free(cx->stdidx[j].aIndex);
+	free(cx->stdidx);
+
+    } while (cx++ != &priv->suidx[priv->suidx_size-1]);
+    free(priv->suidx);
+
+    //printf("TIBIT: index entries: %ld, size=%d\n", priv->idx_size, priv->suidx_size);
+    
+}
+
 /* Read a saved index file */
 if (index_file_load) {
   FILE *fp;
@@ -437,6 +694,7 @@
     fwrite(&priv->idx_size, sizeof(priv->idx_size), 1, fp);
     for (i=0; i<priv->idx_size; i++) {
       AVIINDEXENTRY *idx = &((AVIINDEXENTRY *)priv->idx)[i];
+      idx->dwFlags &= 0xffff;
       fwrite(idx, sizeof(AVIINDEXENTRY), 1, fp);
     }
     fclose(fp);
diff -Nur -X ../dontdiff orig/libmpdemux/aviheader.h main/libmpdemux/aviheader.h
--- orig/libmpdemux/aviheader.h	2002-11-16 03:42:14.000000000 +0000
+++ main/libmpdemux/aviheader.h	2004-01-29 09:48:07.000000000 +0000
@@ -4,6 +4,48 @@
 //#include "config.h"	/* get correct definition WORDS_BIGENDIAN */
 #include "bswap.h"
 
+typedef struct _avisuperindex_entry {
+    uint64_t qwOffset;           // absolute file offset
+    uint32_t dwSize;             // size of index chunk at this offset
+    uint32_t dwDuration;         // time span in stream ticks
+} avisuperindex_entry;
+
+typedef struct _avistdindex_entry {
+    uint32_t dwOffset;           // qwBaseOffset + this is absolute file offset
+    uint32_t dwSize;             // bit 31 is set if this is NOT a keyframe
+} avistdindex_entry;
+
+// Standard index 
+typedef struct _avistdindex_chunk {
+    char           fcc[4];       // ix##
+    uint32_t  dwSize;            // size of this chunk
+    uint16_t wLongsPerEntry;     // must be sizeof(aIndex[0])/sizeof(DWORD)
+    uint8_t  bIndexSubType;      // must be 0
+    uint8_t  bIndexType;         // must be AVI_INDEX_OF_CHUNKS
+    uint32_t  nEntriesInUse;     // first unused entry
+    char           dwChunkId[4]; // '##dc' or '##db' or '##wb' etc..
+    uint64_t qwBaseOffset;       // all dwOffsets in aIndex array are relative to this
+    uint32_t  dwReserved3;       // must be 0
+    avistdindex_entry *aIndex;   // the actual frames
+} avistdindex_chunk;
+    
+
+// Base Index Form 'indx'
+typedef struct _avisuperindex_chunk {
+    char           fcc[4];
+    uint32_t  dwSize;                // size of this chunk
+    uint16_t wLongsPerEntry;         // size of each entry in aIndex array (must be 4*4 for us)
+    uint8_t  bIndexSubType;          // future use. must be 0
+    uint8_t  bIndexType;             // one of AVI_INDEX_* codes
+    uint32_t  nEntriesInUse;         // index of first unused member in aIndex array
+    char       dwChunkId[4];         // fcc of what is indexed
+    uint32_t  dwReserved[3];         // meaning differs for each index type/subtype.
+                                     // 0 if unused
+    avisuperindex_entry *aIndex;     // position of ix## chunks
+    avistdindex_chunk *stdidx;       // the actual std indices
+} avisuperindex_chunk;
+
+
 /*
  * Some macros to swap little endian structures read from an AVI file
  * into machine endian format
@@ -72,6 +114,20 @@
     (h)->dwChunkOffset = le2me_32((h)->dwChunkOffset);			\
     (h)->dwChunkLength = le2me_32((h)->dwChunkLength);			\
 }
+#define le2me_AVISTDIDXCHUNK(h) {\
+    (h)->fcc = le2me_32((h)->fcc);  \
+    (h)->dwSize = le2me_32((h)->dwSize);  \
+    (h)->wLongsPerEntry = le2me_16((h)->wLongsPerEntry);  \
+    (h)->nEntriesInUse = le2me_32((h)->nEntriesInUse);  \
+    (h)->dwChunkId = le2me_32((h)->dwChunkId);  \
+    (h)->qwBaseOffset = le2me_64((h)->qwBaseOffset);  \
+    (h)->dwReserved3 = le2me_32((h)->dwReserved3);  \
+}
+#define le2me_AVISTDIDXENTRY(h)  {\
+    (h)->dwOffset = le2me_32((h)->dwOffset);  \
+    (h)->dwSize = le2me_32((h)->dwSize);  \
+}
+
 #else
 #define	le2me_MainAVIHeader(h)	    /**/
 #define le2me_AVIStreamHeader(h)    /**/
@@ -79,6 +135,8 @@
 #define le2me_BITMAPINFOHEADER(h)   /**/
 #define le2me_WAVEFORMATEX(h)	    /**/
 #define le2me_AVIINDEXENTRY(h)	    /**/
+#define le2me_AVISTDIDXCHUNK(h)     /**/
+#define le2me_AVISTDIDXENTRY(h)     /**/
 #endif
 
 
@@ -107,6 +165,11 @@
   unsigned char pts_corrected;
   unsigned char pts_has_video;
   unsigned int numberofframes;
+  avisuperindex_chunk *suidx;
+  int suidx_size;
+  uint64_t *offsets;
+  int offsets_size;
+  int isodml;
 } avi_priv_t;
 
 #define AVI_PRIV ((avi_priv_t*)(demuxer->priv))
diff -Nur -X ../dontdiff orig/libmpdemux/demux_avi.c main/libmpdemux/demux_avi.c
--- orig/libmpdemux/demux_avi.c	2003-10-22 19:01:37.000000000 +0000
+++ main/libmpdemux/demux_avi.c	2004-01-29 16:58:12.842448176 +0000
@@ -170,6 +170,13 @@
   return ds?1:0;
 }
 
+
+inline static off_t demux_avi_dml_offset(demuxer_t *demux, AVIINDEXENTRY *idx)
+{
+    avi_priv_t *priv = demux->priv;
+    return (off_t)(priv->offsets?priv->offsets[(idx->dwFlags>>16)&0xffff]:0);
+}
+
 // return value:
 //     0 = EOF or no stream found
 //     1 = successfully read a packet
@@ -213,7 +220,7 @@
       continue; // skip this chunk
     }
 
-    pos = priv->idx_offset + (unsigned long)idx->dwChunkOffset;
+    pos = priv->idx_offset + (unsigned long)idx->dwChunkOffset + demux_avi_dml_offset(demux, idx);
     if((pos<demux->movi_start || pos>=demux->movi_end) && (demux->movi_end>demux->movi_start) && (demux->stream->type!=STREAMTYPE_STREAM)){
       mp_msg(MSGT_DEMUX,MSGL_V,"ChunkOffset out of range!   idx=0x%X  \n",pos);
       continue;
@@ -243,7 +250,7 @@
       if(len>0x200000 && idx->dwChunkLength>0x200000) continue; // both values bad :(
       len=choose_chunk_len(idx->dwChunkLength,len);
     }
-    if(!(idx->dwFlags&AVIIF_KEYFRAME)) flags=0;
+    if(!((idx->dwFlags&0xffff)&AVIIF_KEYFRAME)) flags=0;
   } else {
     demux->filepos=stream_tell(demux->stream);
     if(demux->filepos>=demux->movi_end && demux->movi_end>demux->movi_start && (demux->stream->type!=STREAMTYPE_STREAM)){
@@ -354,7 +361,7 @@
       if(len>0x200000 && idx->dwChunkLength>0x200000) continue; // both values bad :(
       len=choose_chunk_len(idx->dwChunkLength,len);
     }
-    if(!(idx->dwFlags&AVIIF_KEYFRAME)) flags=0;
+    if(!((idx->dwFlags&0xffff)&AVIIF_KEYFRAME)) flags=0;
   } else return 0;
   ret=demux_avi_read_packet(demux,demux_avi_select_stream(demux,id),id,len,idx_pos,flags);
 //      if(!ret && priv->skip_video_frames<=0)
@@ -446,6 +453,12 @@
   priv->video_pack_no=0;
   priv->audio_block_no=0;
   priv->audio_block_size=0;
+  priv->isodml = 0;
+  priv->offsets_size = 0;
+  priv->offsets = NULL;
+  priv->suidx_size = 0;
+  priv->suidx = NULL;
+
   demuxer->priv=(void*)priv;
 
   //---- AVI header:
@@ -468,8 +481,8 @@
   if(priv->idx_size>1){
     // decide index format:
 #if 1
-    if((unsigned long)((AVIINDEXENTRY *)priv->idx)[0].dwChunkOffset<demuxer->movi_start ||
-       (unsigned long)((AVIINDEXENTRY *)priv->idx)[1].dwChunkOffset<demuxer->movi_start)
+    if(((unsigned long)((AVIINDEXENTRY *)priv->idx)[0].dwChunkOffset<demuxer->movi_start ||
+        (unsigned long)((AVIINDEXENTRY *)priv->idx)[1].dwChunkOffset<demuxer->movi_start )&& !priv->isodml)
       priv->idx_offset=demuxer->movi_start-4;
     else
       priv->idx_offset=0;


More information about the MPlayer-dev-eng mailing list