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

Tilmann Bitterberg transcode at tibit.org
Sun Feb 8 16:08:51 CET 2004


On 06.02.04, Attila Kinali wrote:

>On Fri, 6 Feb 2004 16:09:51 +0100 (CET)
>Tilmann Bitterberg <transcode at tibit.org> wrote:
>
>> Here is an updated version of the patch (fixes a few bugs)
>> Tested with a 5.4 GB AVI file:
>
>I just had a very quick look at it.
>Please change the printfs to mp_msg calls.

Done.

>Otherwise it looks ok for me (as somone
>not knowing anything about avi)

Cheers,
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-02-08 15:10:03.433023472 +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){
+    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");
+}
+
 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);
+	  
+      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);
+	  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 +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,211 @@
   
 }
 
+
+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;
+	    }
+
+	    print_avistdindex_chunk(&cx->stdidx[j]);
+	    le2me_AVISTDIDXCHUNK(&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;
+
+	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);
+	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");
+		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 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;
+	    }
+	}
+    }
+
+    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;
@@ -437,6 +706,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-02-06 11:45:47.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-02-06 14:51:28.115170536 +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->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