[MPlayer-dev-eng] Re: OpenDML Read/Write support

Tobias Diedrich ranma at gmx.at
Tue Mar 9 20:49:42 CET 2004


This updated patch now includes Video Property Header support (Among
other things stores aspect ratio).
However neither Window Media Player nor Media Player Classic seem to
support it...

-- 
Tobias						PGP: http://9ac7e0bc.2ya.com
Be vigilant!
-------------- next part --------------
? audiodump.wav
? test.wav
Index: mencoder.c
===================================================================
RCS file: /cvsroot/mplayer/main/mencoder.c,v
retrieving revision 1.235
diff -u -r1.235 mencoder.c
--- mencoder.c	18 Feb 2004 13:33:16 -0000	1.235
+++ mencoder.c	9 Mar 2004 19:31:00 -0000
@@ -659,6 +659,12 @@
 	mux_v->bih->biBitCount=24; // FIXME!!!
 	mux_v->bih->biSizeImage=mux_v->bih->biWidth*mux_v->bih->biHeight*(mux_v->bih->biBitCount/8);
     }
+    /*
+     * FIXME: with -ovc copy we don't get aspect ratio information
+     *        from the source stream.
+     */
+    if(movie_aspect>-1.0) sh_video->aspect = movie_aspect;
+
     printf("videocodec: framecopy (%dx%d %dbpp fourcc=%x)\n",
 	mux_v->bih->biWidth, mux_v->bih->biHeight,
 	mux_v->bih->biBitCount, mux_v->bih->biCompression);
Index: DOCS/man/en/mplayer.1
===================================================================
RCS file: /cvsroot/mplayer/main/DOCS/man/en/mplayer.1,v
retrieving revision 1.553
diff -u -r1.553 mplayer.1
--- DOCS/man/en/mplayer.1	5 Mar 2004 04:49:36 -0000	1.553
+++ DOCS/man/en/mplayer.1	9 Mar 2004 19:31:45 -0000
@@ -817,7 +817,7 @@
 from a different AVI, but this is sure to cause unfavorable results.
 .br
 .I NOTE:
-This option will be obsoleted once AVI gets ODML support!
+This option is obsolete.
 .TP
 .B \-mc <seconds/frame>
 Maximum A-V sync correction per frame (in seconds).
@@ -926,20 +926,9 @@
 Force rebuilding of INDEX and output to a separate file specified by the
 argument filename.
 Currently this only works with AVI files.
-Although you can use MEncoder to fix files without indexes, the AVI
-container format is limited to indexing files up to 2GB in size.
-It is however possible to store the index in a separate file and use it later
-with \-loadidx, which is faster than rebuilding the index (with \-idx or
-\-forceidx) each time the movie is opened.
-(This is a limitation of the AVI format, and although there exists an
-extension to index beyond 2GB, MPlayer doesn't yet support this extension.)
-After the index file is created, MPlayer will begin to play the video.
-If you want to automate index file generation (after encoding a large file
-off a TV capture card, for example), you can specify \-frames 0 to
-prevent MPlayer from playing the video after generating the index.
 .br
 .I NOTE:
-This option will be obsoleted once AVI gets ODML support!
+This option is obsolete, because mplayer has OpenDML support.
 .TP
 .B \-sb <byte\ position> (see \-ss option too)
 Seek to byte position.
Index: libmpdemux/aviheader.c
===================================================================
RCS file: /cvsroot/mplayer/main/libmpdemux/aviheader.c,v
retrieving revision 1.51
diff -u -r1.51 aviheader.c
--- libmpdemux/aviheader.c	22 Oct 2003 19:01:37 -0000	1.51
+++ libmpdemux/aviheader.c	9 Mar 2004 19:32:02 -0000
@@ -25,6 +25,32 @@
 extern void print_wave_header(WAVEFORMATEX *h);
 extern void print_video_header(BITMAPINFOHEADER *h);
 extern void print_index(AVIINDEXENTRY *idx,int idx_size);
+extern void print_avistdindex_chunk(avistdindex_chunk *h);
+extern void print_avisuperindex_chunk(avisuperindex_chunk *h);
+
+int odml_get_vstream_id(int id, unsigned char res[])
+{
+    unsigned char *p = (unsigned char *)&id;
+
+#if WORDS_BIGENDIAN
+    if (p[1] == 'd') {
+	if (res) {
+	    res[0] = p[3];
+	    res[1] = p[2];
+	}
+	return 1;
+    }
+#else
+    if (p[2] == 'd') {
+	if (res) {
+	    res[0] = p[0];
+	    res[1] = p[1];
+	}
+	return 1;
+    }
+#endif
+    return 0;
+}
 
 void read_avi_header(demuxer_t *demuxer,int index_mode){
 sh_audio_t *sh_audio=NULL;
@@ -179,6 +205,47 @@
       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];
+
+      chunksize-=24;
+      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);
+	  
+      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++) {
+	  chunksize-=16;
+	  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);
+	  mp_msg (MSGT_HEADER, MSGL_V, "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 +313,38 @@
       }
       break;
     }
+    case mmioFOURCC('v', 'p', 'r', 'p'): {
+	VideoPropHeader *vprp = malloc(chunksize);
+	int i;
+	stream_read(demuxer->stream, (void*)vprp, chunksize);
+	le2me_VideoPropHeader(vprp);
+	chunksize -= sizeof(*vprp)-sizeof(vprp->FieldInfo);
+	chunksize /= sizeof(VIDEO_FIELD_DESC);
+	if (vprp->nbFieldPerFrame > chunksize) {
+	    vprp->nbFieldPerFrame = chunksize;
+	}
+	chunksize = 0;
+	for (i=0; i<vprp->nbFieldPerFrame; i++) {
+		le2me_VIDEO_FIELD_DESC(&vprp->FieldInfo[i]);
+	}
+	if (sh_video) {
+		sh_video->aspect = GET_AVI_ASPECT(vprp->dwFrameAspectRatio);
+	}
+	if(verbose>=1) print_vprp(vprp);
+	break;
+    }
+    case mmioFOURCC('d', 'm', 'l', 'h'): {
+	// dmlh 00 00 00 04 frms
+	unsigned int 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);
+	chunksize = 0;
+    }
+    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",
@@ -261,8 +356,8 @@
 	le2me_AVIINDEXENTRY((AVIINDEXENTRY*)priv->idx + i);
       chunksize-=priv->idx_size<<4;
       if(verbose>=2) print_index(priv->idx,priv->idx_size);
-      break;
     }
+    break;
     /* added May 2002 */
     case mmioFOURCC('R','I','F','F'): {
 	char riff_type[4];
@@ -275,6 +370,29 @@
 	chunksize = 0;
 	list_end = 0; /* a new list will follow */
 	break; }
+    case mmioFOURCC('A', 'T', 'T', 'R'): {
+	unsigned int nrinfo;
+	char *data=malloc(chunksize), *c;
+	nrinfo = stream_read_dword_le(demuxer->stream);
+	mp_msg(MSGT_HEADER, MSGL_V, "[BTV] Beyond TV metainfo found with %d entries\n", nrinfo);
+	memset (data, '\0', chunksize);
+	stream_read (demuxer->stream, data, chunksize-4);
+	c = data;
+	while (nrinfo--) {
+	    mp_msg(MSGT_HEADER, MSGL_V, "[BTV] %s: ", c);
+	    c += strlen(c)+1;
+	    if (strchr(c, '\n')) *strchr(c, '\n') = ' ';
+	    if (!*c) mp_msg(MSGT_HEADER, MSGL_V, "not set\n");
+	    else mp_msg(MSGT_HEADER, MSGL_V, "%s\n", c);
+	    c += strlen(c)+1;
+	}
+	chunksize = 0;
+	}
+       break;
+    case ckidAVIPADDING:
+	stream_skip(demuxer->stream, chunksize);
+	chunksize = 0;
+	break;
   }
   if(hdr){
     mp_msg(MSGT_HEADER,MSGL_V,"hdr=%s  size=%u\n",hdr,size2);
@@ -293,6 +411,8 @@
   mp_msg(MSGT_HEADER,MSGL_DBG2,"list_end=0x%X  pos=0x%X  chunksize=0x%X  next=0x%X\n",
       (int)list_end, (int)stream_tell(demuxer->stream),
       chunksize, (int)chunksize+stream_tell(demuxer->stream));
+  if(list_end>0 &&
+     chunksize+stream_tell(demuxer->stream) == list_end) list_end=0;
   if(list_end>0 && chunksize+stream_tell(demuxer->stream)>list_end){
       mp_msg(MSGT_HEADER,MSGL_V,"Broken chunk?  chunksize=%d  (id=%.4s)\n",chunksize,(char *) &id);
       stream_seek(demuxer->stream,list_end);
@@ -303,6 +423,242 @@
   
 }
 
+if (priv->isodml && (index_mode==-1 || index_mode==0)) {
+    int i, j, k;
+    int safety=1000;
+    int nroffsets;
+
+    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;
+
+    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
+    nroffsets=0; cx = &priv->suidx[0];
+    do nroffsets+=cx->nEntriesInUse;
+    while (cx++ != &priv->suidx[priv->suidx_size-1]);
+
+    priv->offsets = malloc(sizeof(uint64_t) * (nroffsets+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, (nroffsets+1)*sizeof(uint64_t));
+		priv->offsets_size = 0;
+		mp_msg(MSGT_HEADER, MSGL_WARN,
+			"AVI: ODML: Broken (incomplete?) file detected. Will use traditional index\n");
+		goto freeout;
+	    }
+
+	    le2me_AVISTDIDXCHUNK(&cx->stdidx[j]);
+	    print_avistdindex_chunk(&cx->stdidx[j]);
+	    priv->idx_size += cx->stdidx[j].nEntriesInUse;
+	    priv->offsets[priv->offsets_size] = (off_t)(cx->stdidx[j].qwBaseOffset - 8);
+	    priv->offsets_size++;
+	    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));
+	    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;
+	int widx;
+	int id=0;
+
+	j = 1;
+	widx=cx->stdidx[0].nEntriesInUse;
+	while (cx->dwReserved[2] >= widx) {
+	    if (j==cx->nEntriesInUse) { i--; safety--; goto tryagain; }
+	    widx += cx->stdidx[j].nEntriesInUse;
+	    j++;
+	}
+	j--;
+
+	e = &cx->stdidx[j].aIndex[cx->stdidx[j].dwReserved3];
+	memcpy(&idx->ckid, cx->stdidx[j].dwChunkId, 4);
+	id = idx->ckid;
+	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++;
+
+	// bogus entry, whats the purpose of that? Just delete it.
+	if (idx->dwChunkOffset==0 && idx->dwChunkLength==0) {
+	    mp_msg(MSGT_HEADER, MSGL_V,
+	           "AVI: ODML: Skipping bogus entry nr %d fcc %.4s\n", i, (char *)&idx->ckid);
+	    idx--;
+	    priv->idx_size--;
+	    i--;
+	}
+	idx++;
+	safety = 1000;
+tryagain:
+	{
+	    avisuperindex_chunk *mincx;
+	    float percent, old_percent=1000.0;
+	    float audio_bonus = 0.0;
+
+	    // the audio_bonus ensures that audio chunks gets added
+	    // into idx prior to video chunks. mplayer likes this better.
+	    if (id && odml_get_vstream_id(id, NULL)==0)
+		audio_bonus = 5.0;
+
+	    // find the cx with the least percentage written
+	    cx = &priv->suidx[0];
+	    do {
+		percent = (cx->dwReserved[2]+audio_bonus)*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");
+		mp_msg(MSGT_HEADER, MSGL_ERR, 
+			"cx->id = %.4s perc=%f %d / %d\n", cx->dwChunkId, percent,
+			cx->dwReserved[2], cx->dwReserved[0]);
+		sleep(10);
+		return;
+	    }
+	}
+    }
+
+    /* 
+       Hack to work around a "wrong" index in some divx odml files
+       (processor_burning.avi as an example)
+       They have ##dc on non keyframes but the ix00 tells us they are ##db.
+       Read the fcc of a non-keyframe vid frame and check it.
+     */
+
+    {
+	uint32_t id;
+	uint32_t db = 0;
+	stream_reset (demuxer->stream);
+
+	// find out the video stream id. I have seen files with 01db.
+	for (idx = &((AVIINDEXENTRY *)priv->idx)[0], i=0; i<priv->idx_size; i++, idx++){
+	    unsigned char res[2];
+	    if (odml_get_vstream_id(idx->ckid, res)) {
+		db = mmioFOURCC(res[0], res[1], 'd', 'b');
+		break;
+	    }
+	}
+
+	// find first non keyframe
+	for (idx = &((AVIINDEXENTRY *)priv->idx)[0], i=0; i<priv->idx_size; i++, idx++){
+	    if (!(idx->dwFlags & AVIIF_KEYFRAME) && idx->ckid == db) break;
+	}
+	if (i<priv->idx_size && db) {
+	    stream_seek(demuxer->stream, idx->dwChunkOffset+priv->offsets[idx->dwFlags>>16&0xffff]);
+	    id = stream_read_dword_le(demuxer->stream);
+	    if (id && id != db) // index fcc and real fcc differ? fix it.
+		for (idx = &((AVIINDEXENTRY *)priv->idx)[0], i=0; i<priv->idx_size; i++, idx++){
+		    if (!(idx->dwFlags & AVIIF_KEYFRAME) && idx->ckid == db)
+			idx->ckid = id;
+	    }
+	}
+    }
+
+    if (verbose>=2) // dump the constructed index
+	for (idx = &((AVIINDEXENTRY *)priv->idx)[0], i=0; i<priv->idx_size; i++, idx++)
+	    // chunknr fcc offset length flags base_offset
+	    mp_msg(MSGT_HEADER, MSGL_DBG2, 
+		"ODML %6d: %.4s %08x %08x %02x %016llx\n", i, (char *)&idx->ckid, 
+		idx->dwChunkOffset, idx->dwChunkLength, idx->dwFlags&0xffff, 
+		priv->offsets[(idx->dwFlags>>16)&0xffff]);
+
+
+    demuxer->movi_end=demuxer->stream->end_pos;
+
+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);
+
+}
+
 /* Read a saved index file */
 if (index_file_load) {
   FILE *fp;
Index: libmpdemux/aviheader.h
===================================================================
RCS file: /cvsroot/mplayer/main/libmpdemux/aviheader.h,v
retrieving revision 1.6
diff -u -r1.6 aviheader.h
--- libmpdemux/aviheader.h	16 Nov 2002 03:42:14 -0000	1.6
+++ libmpdemux/aviheader.h	9 Mar 2004 19:32:05 -0000
@@ -4,6 +4,89 @@
 //#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;
+
+typedef struct {
+	uint32_t CompressedBMHeight;
+	uint32_t CompressedBMWidth;
+	uint32_t ValidBMHeight;
+	uint32_t ValidBMWidth;
+	uint32_t ValidBMXOffset;
+	uint32_t ValidBMYOffset;
+	uint32_t VideoXOffsetInT;
+	uint32_t VideoYValidStartLine;
+} VIDEO_FIELD_DESC;
+
+typedef struct {
+	uint32_t VideoFormatToken;
+	uint32_t VideoStandard;
+	uint32_t dwVerticalRefreshRate;
+	uint32_t dwHTotalInT;
+	uint32_t dwVTotalInLines;
+	uint32_t dwFrameAspectRatio;
+	uint32_t dwFrameWidthInPixels;
+	uint32_t dwFrameHeightInLines;
+	uint32_t nbFieldPerFrame;
+	VIDEO_FIELD_DESC FieldInfo[2];
+} VideoPropHeader;
+
+enum {
+	FORMAT_UNKNOWN,
+	FORMAT_PAL_SQUARE,
+	FORMAT_PAL_CCIR_601,
+	FORMAT_NTSC_SQUARE,
+	FORMAT_NTSC_CCIR_601,
+} VIDEO_FORMAT;
+
+enum {
+	STANDARD_UNKNOWN,
+	STANDARD_PAL,
+	STANDARD_NTSC,
+	STANDARD_SECAM
+} VIDEO_STANDARD;
+
+#define MAKE_AVI_ASPECT(a, b) (((a)<<16)|(b))
+#define GET_AVI_ASPECT(a) ((float)((a)>>16)/(float)((a)&0xffff))
+
 /*
  * Some macros to swap little endian structures read from an AVI file
  * into machine endian format
@@ -72,6 +155,44 @@
     (h)->dwChunkOffset = le2me_32((h)->dwChunkOffset);			\
     (h)->dwChunkLength = le2me_32((h)->dwChunkLength);			\
 }
+#define le2me_AVISTDIDXCHUNK(h) {\
+    char c; \
+    c = (h)->fcc[0]; (h)->fcc[0] = (h)->fcc[3]; (h)->fcc[3] = c;  \
+    c = (h)->fcc[1]; (h)->fcc[1] = (h)->fcc[2]; (h)->fcc[2] = c;  \
+    (h)->dwSize = le2me_32((h)->dwSize);  \
+    (h)->wLongsPerEntry = le2me_16((h)->wLongsPerEntry);  \
+    (h)->nEntriesInUse = le2me_32((h)->nEntriesInUse);  \
+    c = (h)->dwChunkId[0]; (h)->dwChunkId[0] = (h)->dwChunkId[3]; (h)->dwChunkId[3] = c;  \
+    c = (h)->dwChunkId[1]; (h)->dwChunkId[1] = (h)->dwChunkId[2]; (h)->dwChunkId[2] = c;  \
+    (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);  \
+}
+#define le2me_VideoPropHeader(h) {					\
+    (h)->VideoFormatToken = le2me_32((h)->VideoFormatToke)		\
+    (h)->VideoStandrad = le2me_32((h)->VideoStandard)			\
+    (h)->dwVerticalRefreshRate = le2me_32((h)->dwVerticalRefreshRate)	\
+    (h)->dwHTotalInT = le2me_32((h)->dwHTotalInT)			\
+    (h)->dwVTotalInLines = le2me_32((h)->dwVTotalInLines)		\
+    (h)->dwFrameAspectRatio = le2me_32((h)->dwFrameAspectRatio)		\
+    (h)->dwFrameWidthInPixels = le2me_32((h)->dwFrameWidthInPixels)	\
+    (h)->dwFrameHeightInLines = le2me_32((h)->dwFrameHeightInLines)	\
+    (h)->nbFieldPerFrame = le2me_32((h)->nbFieldPerFrame)		\
+}
+#define le2me_VIDEO_FIELD_DESC(h) {					\
+    (h)->CompressedBMHeight = le2me_32((h)->CompressedBMHeight)		\
+    (h)->CompressedBMWidth = le2me_32((h)->CompressedBMWidth)		\
+    (h)->ValidBMHeight = le2me_32((h)->ValidBMHeight)			\
+    (h)->ValidBMWidth = le2me_32((h)->ValidBMWidth)			\
+    (h)->ValidBMXOffset = le2me_32((h)->ValidXOffset)			\
+    (h)->ValidBMYOffset = le2me_32((h)->ValidYOffset)			\
+    (h)->VideoXOffsetInT = le2me_32((h)->VideoXOffsetInT)		\
+    (h)->VideoYValidStartLine = le2me_32((h)->VideoYValidStartLine)	\
+}
+
 #else
 #define	le2me_MainAVIHeader(h)	    /**/
 #define le2me_AVIStreamHeader(h)    /**/
@@ -79,12 +200,12 @@
 #define le2me_BITMAPINFOHEADER(h)   /**/
 #define le2me_WAVEFORMATEX(h)	    /**/
 #define le2me_AVIINDEXENTRY(h)	    /**/
+#define le2me_AVISTDIDXCHUNK(h)     /**/
+#define le2me_AVISTDIDXENTRY(h)     /**/
+#define le2me_VideoPropHeader(h)    /**/
+#define le2me_VIDEO_FIELD_DESC(h)   /**/
 #endif
 
-
-#endif
-
-
 typedef struct {
   // index stuff:
   void* idx;
@@ -107,6 +228,13 @@
   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))
+
+#endif /* _aviheader_h */
Index: libmpdemux/aviprint.c
===================================================================
RCS file: /cvsroot/mplayer/main/libmpdemux/aviprint.c,v
retrieving revision 1.17
diff -u -r1.17 aviprint.c
--- libmpdemux/aviprint.c	27 Jul 2003 22:15:08 -0000	1.17
+++ libmpdemux/aviprint.c	9 Mar 2004 19:32:05 -0000
@@ -13,6 +13,8 @@
 #include "wine/avifmt.h"
 #include "wine/vfw.h"
 
+#include "aviheader.h"
+
 //#include "codec-cfg.h"
 //#include "stheader.h"
 
@@ -51,6 +53,7 @@
   printf("Suggested BufferSize: %ld\n",h->dwSuggestedBufferSize);
   printf("Quality %ld\n",h->dwQuality);
   printf("Sample size: %ld\n",h->dwSampleSize);
+  printf("Frame: l=%d r=%d t=%d b=%d\n",h->rcFrame.left, h->rcFrame.right, h->rcFrame.top, h->rcFrame.bottom);
   printf("==========================\n");
 }
 
@@ -105,6 +108,31 @@
   printf("===========================\n");
 }
 
+void print_vprp(VideoPropHeader *vprp){
+  int i;
+  printf("======= Video Properties Header =======\n");
+  printf("Format: %d  VideoStandard: %d\n",
+         vprp->VideoFormatToken,vprp->VideoStandard);
+  printf("VRefresh: %d  HTotal: %d  VTotal: %d\n",
+         vprp->dwVerticalRefreshRate, vprp->dwHTotalInT, vprp->dwVTotalInLines);
+  printf("FrameAspect: %d:%d  Framewidth: %d  Frameheight: %d\n",
+         vprp->dwFrameAspectRatio&0xffff, vprp->dwFrameAspectRatio >> 16,
+         vprp->dwFrameWidthInPixels, vprp->dwFrameHeightInLines);
+  printf("Fields: %d\n", vprp->nbFieldPerFrame);
+  for (i=0; i<vprp->nbFieldPerFrame; i++) {
+    VIDEO_FIELD_DESC *vfd = &vprp->FieldInfo[i];
+    printf("  == Field %d description ==\n", i);
+    printf("  CompressedBMHeight: %d  CompressedBMWidth: %d\n",
+           vfd->CompressedBMHeight, vfd->CompressedBMWidth);
+    printf("  ValidBMHeight: %d  ValidBMWidth: %d\n",
+           vfd->ValidBMHeight, vfd->ValidBMWidth);
+    printf("  ValidBMXOffset: %d  ValidBMYOffset: %d\n",
+           vfd->ValidBMXOffset, vfd->ValidBMYOffset);
+    printf("  VideoXOffsetInT: %d  VideoYValidStartLine: %d\n",
+           vfd->VideoXOffsetInT, vfd->VideoYValidStartLine);
+  }
+  printf("=======================================\n");
+}
 
 void print_index(AVIINDEXENTRY *idx,int idx_size){
   int i;
@@ -128,4 +156,21 @@
   }
 }
 
+void print_avistdindex_chunk(avistdindex_chunk *h){
+    mp_msg (MSGT_HEADER, MSGL_V, "====== AVI Standard Index Header ========\n");
+    mp_msg (MSGT_HEADER, MSGL_V, "  FCC (%.4s) dwSize (%d) wLongsPerEntry(%d)\n", h->fcc, h->dwSize, h->wLongsPerEntry);
+    mp_msg (MSGT_HEADER, MSGL_V, "  bIndexSubType (%d) bIndexType (%d)\n", h->bIndexSubType, h->bIndexType);
+    mp_msg (MSGT_HEADER, MSGL_V, "  nEntriesInUse (%d) dwChunkId (%.4s)\n", h->nEntriesInUse, h->dwChunkId);
+    mp_msg (MSGT_HEADER, MSGL_V, "  qwBaseOffset (0x%llX) dwReserved3 (%d)\n", h->qwBaseOffset, h->dwReserved3);
+    mp_msg (MSGT_HEADER, MSGL_V, "===========================\n");
+}
+void print_avisuperindex_chunk(avisuperindex_chunk *h){
+    mp_msg (MSGT_HEADER, MSGL_V, "====== AVI Super Index Header ========\n");
+    mp_msg (MSGT_HEADER, MSGL_V, "  FCC (%.4s) dwSize (%d) wLongsPerEntry(%d)\n", h->fcc, h->dwSize, h->wLongsPerEntry);
+    mp_msg (MSGT_HEADER, MSGL_V, "  bIndexSubType (%d) bIndexType (%d)\n", h->bIndexSubType, h->bIndexType);
+    mp_msg (MSGT_HEADER, MSGL_V, "  nEntriesInUse (%d) dwChunkId (%.4s)\n", h->nEntriesInUse, h->dwChunkId);
+    mp_msg (MSGT_HEADER, MSGL_V, "  dwReserved[0] (%d) dwReserved[1] (%d) dwReserved[2] (%d)\n", 
+	    h->dwReserved[0], h->dwReserved[1], h->dwReserved[2]);
+    mp_msg (MSGT_HEADER, MSGL_V, "===========================\n");
+}
 
Index: libmpdemux/demux_avi.c
===================================================================
RCS file: /cvsroot/mplayer/main/libmpdemux/demux_avi.c,v
retrieving revision 1.59
diff -u -r1.59 demux_avi.c
--- libmpdemux/demux_avi.c	17 Feb 2004 12:30:44 -0000	1.59
+++ libmpdemux/demux_avi.c	9 Mar 2004 19:32:14 -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 = (off_t)priv->idx_offset + (off_t)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->flags & STREAM_SEEK)){
       mp_msg(MSGT_DEMUX,MSGL_V,"ChunkOffset out of range!   idx=0x%X  \n",pos);
       continue;
@@ -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;
Index: libmpdemux/muxer.h
===================================================================
RCS file: /cvsroot/mplayer/main/libmpdemux/muxer.h,v
retrieving revision 1.8
diff -u -r1.8 muxer.h
--- libmpdemux/muxer.h	22 Oct 2003 17:04:39 -0000	1.8
+++ libmpdemux/muxer.h	9 Mar 2004 19:32:16 -0000
@@ -47,9 +47,9 @@
 typedef struct muxer_t{
   // encoding:
   MainAVIHeader avih;
-  unsigned int movi_start;
-  unsigned int movi_end;
-  unsigned int file_end; // for MPEG it's system timestamp in 1/90000 s
+  off_t movi_start;
+  off_t movi_end;
+  off_t file_end; // for MPEG it's system timestamp in 1/90000 s
   // index:
   AVIINDEXENTRY *idx;
   int idx_pos;
Index: libmpdemux/muxer_avi.c
===================================================================
RCS file: /cvsroot/mplayer/main/libmpdemux/muxer_avi.c,v
retrieving revision 1.17
diff -u -r1.17 muxer_avi.c
--- libmpdemux/muxer_avi.c	28 Jan 2004 07:47:48 -0000	1.17
+++ libmpdemux/muxer_avi.c	9 Mar 2004 19:32:18 -0000
@@ -1,24 +1,24 @@
-
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 #include <inttypes.h>
 #include <unistd.h>
+#include <limits.h>
 
 #include "config.h"
 #include "../version.h"
 
-//#include "stream.h"
-//#include "demuxer.h"
-//#include "stheader.h"
-
 #include "wine/mmreg.h"
 #include "wine/avifmt.h"
 #include "wine/vfw.h"
 #include "bswap.h"
 
+#include "stream.h"
+#include "demuxer.h"
+#include "stheader.h"
 #include "muxer.h"
 #include "aviheader.h"
+#include "mp_msg.h"
 
 extern char *info_name;
 extern char *info_artist;
@@ -28,11 +28,38 @@
 extern char *info_sourceform;
 extern char *info_comment;
 
+/* #define ODML_CHUNKLEN    0x02000000 */ /* for testing purposes */
+#define ODML_CHUNKLEN    0x40000000
+#define ODML_NOTKEYFRAME 0x80000000U
+#define MOVIALIGN        0x00001000
+
+struct avi_odmlidx_entry {
+	uint64_t ofs;
+	uint32_t len;
+	uint32_t flags;
+};
+
+struct avi_odmlsuperidx_entry {
+	uint64_t ofs;
+	uint32_t len;
+	uint32_t duration;
+};
+
+struct avi_stream_info {
+	int idxsize;
+	int idxpos;
+	int superidxpos;
+	int superidxsize;
+	struct avi_odmlidx_entry *idx;
+	struct avi_odmlsuperidx_entry *superidx;
+};
+
 static muxer_stream_t* avifile_new_stream(muxer_t *muxer,int type){
+    struct avi_stream_info *si;
     muxer_stream_t* s;
     if (!muxer) return NULL;
     if(muxer->avih.dwStreams>=MUXER_MAX_STREAMS){
-	printf("Too many streams! increase MUXER_MAX_STREAMS !\n");
+	mp_msg(MSGT_MUXER, MSGL_ERR, "Too many streams! increase MUXER_MAX_STREAMS !\n");
 	return NULL;
     }
     s=malloc(sizeof(muxer_stream_t));
@@ -44,6 +71,11 @@
     s->timer=0.0;
     s->size=0;
     s->muxer=muxer;
+    s->priv=si=malloc(sizeof(struct avi_stream_info));
+    memset(si,0,sizeof(struct avi_stream_info));
+    si->idxsize=256;
+    si->idx=malloc(sizeof(struct avi_odmlidx_entry)*si->idxsize);
+
     switch(type){
     case MUXER_TYPE_VIDEO:
       s->ckid=mmioFOURCC(('0'+s->id/10),('0'+(s->id%10)),'d','c');
@@ -55,7 +87,7 @@
       s->h.fccType=streamtypeAUDIO;
       break;
     default:
-      printf("WarninG! unknown stream type: %d\n",type);
+      mp_msg(MSGT_MUXER, MSGL_WARN, "Warning! unknown stream type: %d\n",type);
       return NULL;
     }
     muxer->avih.dwStreams++;
@@ -90,20 +122,65 @@
 }
 }
 
+static void write_avi_list(FILE *f,unsigned int id,int len);
+static void avifile_write_index(muxer_t *muxer);
+
+static void avifile_odml_new_riff(muxer_t *muxer)
+{
+    FILE *f = muxer->file;
+    uint32_t riff[3];
+
+    /* Pad to ODML_CHUNKLEN */
+    write_avi_chunk(f,ckidAVIPADDING,ODML_CHUNKLEN - (ftello(f)%ODML_CHUNKLEN) - 8,NULL);
+
+    /* RIFF/AVIX chunk */
+    riff[0]=le2me_32(mmioFOURCC('R','I','F','F'));
+    riff[1]=0;
+    riff[2]=le2me_32(mmioFOURCC('A','V','I','X'));
+    fwrite(riff,12,1,f);
+
+    write_avi_list(f,listtypeAVIMOVIE,0);
+}
+
 static void avifile_write_chunk(muxer_stream_t *s,size_t len,unsigned int flags){
+    off_t pos;
+    struct avi_stream_info *si = s->priv;
     muxer_t *muxer=s->muxer;
 
-    // add to the index:
+    // add to the traditional index:
     if(muxer->idx_pos>=muxer->idx_size){
 	muxer->idx_size+=256; // 4kB
 	muxer->idx=realloc(muxer->idx,16*muxer->idx_size);
     }
     muxer->idx[muxer->idx_pos].ckid=s->ckid;
     muxer->idx[muxer->idx_pos].dwFlags=flags; // keyframe?
-    muxer->idx[muxer->idx_pos].dwChunkOffset=ftell(muxer->file)-(muxer->movi_start-4);
+    muxer->idx[muxer->idx_pos].dwChunkOffset=ftello(muxer->file)-(muxer->movi_start-4);
     muxer->idx[muxer->idx_pos].dwChunkLength=len;
     ++muxer->idx_pos;
 
+    // add to odml index
+    if(si->idxpos>=si->idxsize){
+	si->idxsize+=256;
+	si->idx=realloc(si->idx,sizeof(*si->idx)*si->idxsize);
+    }
+    si->idx[si->idxpos].flags=(flags&AVIIF_KEYFRAME)?0:ODML_NOTKEYFRAME;
+    si->idx[si->idxpos].ofs=ftello(muxer->file);
+    si->idx[si->idxpos].len=len;
+    ++si->idxpos;
+
+    pos = ftello(muxer->file);
+    if (pos < ODML_CHUNKLEN &&
+	pos + 16*muxer->idx_pos + len + 8 > ODML_CHUNKLEN) {
+
+	avifile_write_index(muxer);
+	avifile_odml_new_riff(muxer);
+    }
+    pos = ftello(muxer->file);
+    if (pos % ODML_CHUNKLEN + len + 8 > ODML_CHUNKLEN) {
+	muxer->movi_end = ftello(muxer->file);
+	avifile_odml_new_riff(muxer);
+    }
+
     // write out the chunk:
     write_avi_chunk(muxer->file,s->ckid,len,s->buffer); /* unsigned char */
 
@@ -111,7 +188,7 @@
     if(s->h.dwSampleSize){
 	// CBR
 	s->h.dwLength+=len/s->h.dwSampleSize;
-	if(len%s->h.dwSampleSize) printf("Warning! len isn't divisable by samplesize!\n");
+	if(len%s->h.dwSampleSize) mp_msg(MSGT_MUXER, MSGL_WARN, "Warning! len isn't divisable by samplesize!\n");
     } else {
 	// VBR
 	s->h.dwLength++;
@@ -137,21 +214,67 @@
 
 #define WFSIZE(wf) (sizeof(WAVEFORMATEX)+(wf)->cbSize)
 
+static unsigned int avi_aspect(sh_video_t *sh_video)
+{
+    float aspect = sh_video->aspect;
+    if (aspect <= 0.0) {
+        aspect = (float)sh_video->disp_w/(float)sh_video->disp_h;
+    }
+    if (aspect >= 3.99/3.0 &&
+        aspect <= 4.01/3.0) return MAKE_AVI_ASPECT(4,3);
+    if (aspect >= 15.99/9.0 &&
+        aspect <= 16.01/9.0) return MAKE_AVI_ASPECT(16,9);
+    if (aspect >= 0.99 &&
+        aspect <= 1.01) return MAKE_AVI_ASPECT(1,1);
+    if (aspect<1.0) return MAKE_AVI_ASPECT((int)(aspect*8192),8192);
+    return MAKE_AVI_ASPECT(8192,(int)(8192/aspect));
+}
+
 static void avifile_write_header(muxer_t *muxer){
+  FILE *f = muxer->file;
   uint32_t riff[3];
+  unsigned int dmlh[1];
   unsigned int i;
   unsigned int hdrsize;
   muxer_info_t info[16];
-  FILE *f=muxer->file;
+  VideoPropHeader vprp;
+  off_t pos;
+  int isodml = muxer->file_end > ODML_CHUNKLEN ? 1 : 0;
+
+  if (isodml) {
+    for (pos = 0; pos < muxer->file_end; pos += ODML_CHUNKLEN) {
+      unsigned int rifflen, movilen;
+
+      /* fixup RIFF length */
+      if (muxer->file_end - pos > ODML_CHUNKLEN) {
+	  rifflen = le2me_32(ODML_CHUNKLEN - 8);
+	  movilen = le2me_32(ODML_CHUNKLEN - 20);
+      } else {
+	  rifflen = le2me_32(muxer->file_end - pos - 8);
+	  movilen = le2me_32(muxer->movi_end - pos - 20);
+      }
+      fseeko(f, pos + 4, SEEK_SET);
+      fwrite(&rifflen,4,1,f);
+
+      /* fixup movi length */
+      if (pos > 0) {
+	  fseeko(f, pos + 16, SEEK_SET);
+	  fwrite(&movilen,4,1,f);
+      }
+    }
+
+    fseeko(f, 12, SEEK_SET);
+  } else {
+    // RIFF header:
+    riff[0]=mmioFOURCC('R','I','F','F');
+    riff[1]=muxer->file_end-2*sizeof(unsigned int);  // filesize
+    riff[2]=formtypeAVI; // 'AVI '
+    riff[0]=le2me_32(riff[0]);
+    riff[1]=le2me_32(riff[1]);
+    riff[2]=le2me_32(riff[2]);
+    fwrite(&riff,12,1,f);
+  }
 
-  // RIFF header:
-  riff[0]=mmioFOURCC('R','I','F','F');
-  riff[1]=muxer->file_end-2*sizeof(unsigned int);  // filesize
-  riff[2]=formtypeAVI; // 'AVI '
-  riff[0]=le2me_32(riff[0]);
-  riff[1]=le2me_32(riff[1]);
-  riff[2]=le2me_32(riff[2]);
-  fwrite(&riff,12,1,f);
   // update AVI header:
   if(muxer->def_v){
       muxer->avih.dwMicroSecPerFrame=1000000.0*muxer->def_v->h.dwScale/muxer->def_v->h.dwRate;
@@ -166,18 +289,26 @@
 
   // AVI header:
   hdrsize=sizeof(muxer->avih)+8;
+  if (isodml) hdrsize+=sizeof(dmlh)+20; // dmlh
   // calc total header size:
   for(i=0;i<muxer->avih.dwStreams;i++){
+      muxer_stream_t *s = muxer->streams[i];
+      struct avi_stream_info *si = s->priv;
+
       hdrsize+=12; // LIST
       hdrsize+=sizeof(muxer->streams[i]->h)+8; // strh
       switch(muxer->streams[i]->type){
       case MUXER_TYPE_VIDEO:
           hdrsize+=muxer->streams[i]->bih->biSize+8; // strf
+	  hdrsize+=8+4*(9+8*1); // vprp
 	  break;
       case MUXER_TYPE_AUDIO:
           hdrsize+=WFSIZE(muxer->streams[i]->wf)+8; // strf
 	  break;
       }
+      if (isodml && si && si->superidx && si->superidxsize) {
+	  hdrsize += 32 + 16*si->superidxsize; //indx
+      }
   }
   write_avi_list(f,listtypeAVIHEADER,hdrsize);
   
@@ -187,42 +318,97 @@
 
   // stream headers:
   for(i=0;i<muxer->avih.dwStreams;i++){
-      hdrsize=sizeof(muxer->streams[i]->h)+8; // strh
-      switch(muxer->streams[i]->type){
+      muxer_stream_t *s = muxer->streams[i];
+      struct avi_stream_info *si = s->priv;
+      unsigned int idxhdr[8];
+      int j,n;
+
+      hdrsize=sizeof(s->h)+8; // strh
+      if (si && si->superidx && si->superidxsize) {
+	  hdrsize += 32 + 16*si->superidxsize; //indx
+      }
+      switch(s->type){
       case MUXER_TYPE_VIDEO:
-          hdrsize+=muxer->streams[i]->bih->biSize+8; // strf
-          muxer->streams[i]->h.fccHandler = muxer->streams[i]->bih->biCompression;
+          hdrsize+=s->bih->biSize+8; // strf
+          s->h.fccHandler = s->bih->biCompression;
+	  // fill out vprp info
+	  memset(&vprp, 0, sizeof(vprp));
+	  vprp.dwVerticalRefreshRate = (s->h.dwRate+s->h.dwScale-1)/s->h.dwScale;
+	  vprp.dwFrameAspectRatio = avi_aspect(s->source);
+	  vprp.dwFrameWidthInPixels = muxer->avih.dwWidth;
+	  vprp.dwFrameHeightInLines = muxer->avih.dwHeight;
+	  vprp.nbFieldPerFrame = 1;
+	  vprp.FieldInfo[0].ValidBMHeight = muxer->avih.dwHeight;
+	  vprp.FieldInfo[0].ValidBMWidth = muxer->avih.dwWidth;
+          hdrsize+=8+4*(9+8*1); // vprp
+
 	  break;
       case MUXER_TYPE_AUDIO:
-          hdrsize+=WFSIZE(muxer->streams[i]->wf)+8; // strf
-          muxer->streams[i]->h.fccHandler = muxer->streams[i]->wf->wFormatTag;
+          hdrsize+=WFSIZE(s->wf)+8; // strf
+          s->h.fccHandler = s->wf->wFormatTag;
 	  break;
       }
       write_avi_list(f,listtypeSTREAMHEADER,hdrsize);
-      le2me_AVIStreamHeader(&muxer->streams[i]->h);
-      write_avi_chunk(f,ckidSTREAMHEADER,sizeof(muxer->streams[i]->h),&muxer->streams[i]->h); /* AVISTreamHeader */ // strh
-      le2me_AVIStreamHeader(&muxer->streams[i]->h);
+      le2me_AVIStreamHeader(&s->h);
+      write_avi_chunk(f,ckidSTREAMHEADER,sizeof(s->h),&s->h); /* AVISTreamHeader */ // strh
+      le2me_AVIStreamHeader(&s->h);
 
-      switch(muxer->streams[i]->type){
+      switch(s->type){
       case MUXER_TYPE_VIDEO:
 {
-          int biSize=muxer->streams[i]->bih->biSize;
-          le2me_BITMAPINFOHEADER(muxer->streams[i]->bih);
-          write_avi_chunk(f,ckidSTREAMFORMAT,biSize,muxer->streams[i]->bih); /* BITMAPINFOHEADER */
-          le2me_BITMAPINFOHEADER(muxer->streams[i]->bih);
+          int biSize=s->bih->biSize;
+          le2me_BITMAPINFOHEADER(s->bih);
+          write_avi_chunk(f,ckidSTREAMFORMAT,biSize,s->bih); /* BITMAPINFOHEADER */
+          le2me_BITMAPINFOHEADER(s->bih);
+	  le2me_VideoPropHeader(&vprp);
+	  le2me_VIDEO_FIELD_DESC(&vprp.FieldInfo[0]);
+	  le2me_VIDEO_FIELD_DESC(&vprp.FieldInfo[1]);
+	  write_avi_chunk(f,mmioFOURCC('v','p','r','p'),
+	                  sizeof(VideoPropHeader) -
+	                  sizeof(VIDEO_FIELD_DESC)*(2-vprp.nbFieldPerFrame),
+	                  &vprp); /* Video Properties Header */
 }
 	  break;
       case MUXER_TYPE_AUDIO:
 {
-          int wfsize = WFSIZE(muxer->streams[i]->wf);
-          le2me_WAVEFORMATEX(muxer->streams[i]->wf);
-          write_avi_chunk(f,ckidSTREAMFORMAT,wfsize,muxer->streams[i]->wf); /* WAVEFORMATEX */
-          le2me_WAVEFORMATEX(muxer->streams[i]->wf);
+          int wfsize = WFSIZE(s->wf);
+          le2me_WAVEFORMATEX(s->wf);
+          write_avi_chunk(f,ckidSTREAMFORMAT,wfsize,s->wf); /* WAVEFORMATEX */
+          le2me_WAVEFORMATEX(s->wf);
 }	  
 	  break;
       }
+      if (isodml && si && si->superidx && si->superidxsize) {
+	  n = si->superidxsize;
+
+	  idxhdr[0] = le2me_32(mmioFOURCC('i', 'n', 'd', 'x'));
+	  idxhdr[1] = le2me_32(24 + 16*n);
+	  idxhdr[2] = le2me_32(0x00000004);
+	  idxhdr[3] = le2me_32(si->superidxpos);
+	  idxhdr[4] = le2me_32(s->ckid);
+	  idxhdr[5] = 0;
+	  idxhdr[6] = 0;
+	  idxhdr[7] = 0;
+
+	  fwrite(idxhdr,sizeof(idxhdr),1,f);
+	  for (j=0; j<n; j++) {
+	      struct avi_odmlsuperidx_entry *entry = &si->superidx[j];
+	      unsigned int data[4];
+	      data[0] = le2me_32(entry->ofs);
+	      data[1] = le2me_32(entry->ofs >> 32);
+	      data[2] = le2me_32(entry->len);
+	      data[3] = le2me_32(entry->duration);
+	      fwrite(data,sizeof(data),1,f);
+	  }
+      }
   }
 
+  // ODML
+  memset(dmlh, 0, sizeof(dmlh));
+  dmlh[0] = le2me_32(muxer->avih.dwTotalFrames);
+  write_avi_list(f,mmioFOURCC('o','d','m','l'),sizeof(dmlh)+8);
+  write_avi_chunk(f,mmioFOURCC('d','m','l','h'),sizeof(dmlh),dmlh);
+
 // ============= INFO ===============
 // always include software info
 info[0].id=mmioFOURCC('I','S','F','T'); // Software:
@@ -274,14 +460,140 @@
   }
 
   // JUNK:
-  write_avi_chunk(f,ckidAVIPADDING,2048-(ftell(f)&2047)-8,NULL); /* junk */  
-  // 'movi' header:
-  write_avi_list(f,listtypeAVIMOVIE,muxer->movi_end-ftell(f)-12);
-  muxer->movi_start=ftell(f);
+  write_avi_chunk(f,ckidAVIPADDING,MOVIALIGN-(ftello(f)%MOVIALIGN)-8,NULL); /* junk */
+  if (!isodml) {
+    // 'movi' header:
+    write_avi_list(f,listtypeAVIMOVIE,muxer->movi_end-ftello(f)-12);
+    muxer->movi_start=ftello(muxer->file);
+  } else {
+    if (ftello(f) != MOVIALIGN) {
+	mp_msg(MSGT_MUXER, MSGL_ERR, "Opendml superindex is too big for reserved space!\n");
+	mp_msg(MSGT_MUXER, MSGL_ERR, "Expected filepos %d, real filepos %d, missing space %d\n", MOVIALIGN, ftell(muxer->file), ftell(muxer->file)-MOVIALIGN);
+    }
+  }
+}
+
+static void avifile_odml_write_index(muxer_t *muxer){
+  muxer_stream_t* s;
+  struct avi_stream_info *si;
+  int entries_per_subidx = INT_MAX;
+  int entries_per_superidx = 0;
+  int i;
+
+  /*
+   * FIXME:
+   * The following code is supposed to keep MS Mediaplayer happy,
+   * but the resulting AVIs still don't work with it.
+   *
+   * According to Avery Lee MSMP wants the subidx chunks to have the same size.
+   *
+   * So this code figures out how many entries we can put into
+   * an ix?? chunk, so that each ix?? chunk has the same size and the offsets
+   * don't overflow (Using ODML_CHUNKLEN for that is a bit more restrictive
+   * than it has to be though).
+   *
+   * It also makes sure the indx chunks in the header are all the same size,
+   * which may not be necessary.
+   */
+  for (i=0; i<muxer->avih.dwStreams; i++) {
+    int j,n,len,last;
+    s = muxer->streams[i];
+    si = s->priv;
+
+    len = 0;
+    n = 0;
+    do {
+	off_t start = si->idx[0].ofs;
+	last = entries_per_subidx;
+	for (j=0; j<si->idxpos; j++) {
+	    len = si->idx[j].ofs - start;
+	    if(len >= ODML_CHUNKLEN || n >= entries_per_subidx) {
+		if (entries_per_subidx > n) {
+		    entries_per_subidx = n;
+		}
+		start = si->idx[j].ofs;
+		len = 0;
+		n = 0;
+	    }
+	    n++;
+	}
+    } while (last != entries_per_subidx);
+  }
+
+  for (i=0; i<muxer->avih.dwStreams; i++) {
+    s = muxer->streams[i];
+    si = s->priv;
+    si->superidxpos = (si->idxpos+entries_per_subidx-1) / entries_per_subidx;
+    if (si->superidxpos > entries_per_superidx) {
+	entries_per_superidx = si->superidxpos;
+    }
+  }
+
+  /*
+   * end of MS compat calculations
+   */
+
+  for (i=0; i<muxer->avih.dwStreams; i++) {
+    int j,k,n,idxpos;
+    unsigned int idxhdr[8];
+    s = muxer->streams[i];
+    si = s->priv;
+
+    if (si->idxpos > entries_per_subidx) n = entries_per_subidx;
+    else n = si->idxpos;
+
+    mp_msg(MSGT_MUXER, MSGL_V, "ODML: Stream %d: Using %d entries per subidx, %d entries in superidx\n",
+		i, n, si->superidxpos);
+
+    si->superidxsize = entries_per_superidx;
+    si->superidx = malloc(sizeof(*si->superidx) * si->superidxsize);
+    memset(si->superidx, 0, sizeof(*si->superidx) * si->superidxsize);
+
+    idxpos = 0;
+    for (j=0; j<si->superidxpos; j++) {
+	off_t start = si->idx[idxpos].ofs;
+	int duration;
+
+	duration = 0;
+	for (k=0; k<n && idxpos+k<si->idxpos; k++) {
+		duration += s->h.dwSampleSize ? si->idx[idxpos+k].len/s->h.dwSampleSize : 1;
+	}
+
+	idxhdr[0] = le2me_32((s->ckid << 16) | mmioFOURCC('i', 'x', 0, 0));
+	idxhdr[1] = le2me_32(24 + 8*k);
+	idxhdr[2] = le2me_32(0x01000002);
+	idxhdr[3] = le2me_32(k);
+	idxhdr[4] = le2me_32(s->ckid);
+	idxhdr[5] = le2me_32(start + 8);
+	idxhdr[6] = le2me_32((start + 8)>> 32);
+	idxhdr[7] = 0; /* unused */
+
+	si->superidx[j].len = 24 + 8*k;
+	si->superidx[j].ofs = ftello(muxer->file);
+	si->superidx[j].duration = duration;
+
+	fwrite(idxhdr,sizeof(idxhdr),1,muxer->file);
+	for (k=0; k<n && idxpos<si->idxpos; k++) {
+	    unsigned int entry[2];
+	    entry[0] = le2me_32(si->idx[idxpos].ofs - start);
+	    entry[1] = le2me_32(si->idx[idxpos].len | si->idx[idxpos].flags);
+	    idxpos++;
+	    fwrite(entry,sizeof(entry),1,muxer->file);
+	}
+     }
+  }
+  muxer->file_end=muxer->movi_end=ftello(muxer->file);
 }
 
 static void avifile_write_index(muxer_t *muxer){
-  muxer->movi_end=ftell(muxer->file);
+  muxer->movi_end=ftello(muxer->file);
+
+  if(muxer->movi_end > ODML_CHUNKLEN &&
+     muxer->idx && muxer->idx_pos>0) {
+    avifile_odml_write_index(muxer);
+    return;
+  }
+
   if(muxer->idx && muxer->idx_pos>0){
       int i;
       // fixup index entries:
@@ -292,7 +604,7 @@
       for (i=0; i<muxer->idx_pos; i++) le2me_AVIINDEXENTRY((&muxer->idx[i]));
       muxer->avih.dwFlags|=AVIF_HASINDEX;
   }
-  muxer->file_end=ftell(muxer->file);
+  muxer->file_end=ftello(muxer->file);
 }
 
 void muxer_init_muxer_avi(muxer_t *muxer){


More information about the MPlayer-dev-eng mailing list