[MPlayer-dev-eng] [PATCH] OpenDML AVI2.0 read support
Tilmann Bitterberg
transcode at tibit.org
Fri Feb 6 16:09:51 CET 2004
On 06.02.04, Tilmann Bitterberg wrote:
>On 29.01.04, Tilmann Bitterberg wrote:
>
>>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
>
>Not a single comment? Oh well ...
Here is an updated version of the patch (fixes a few bugs)
Tested with a 5.4 GB AVI file:
ID_LENGTH=44109
A:44109.9 V:44109.8 A-V: 0.054 ct: 0.009 1102747/1102747
Thanks,
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-06 13:37:45.904448752 +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,222 @@
}
+
+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] = (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));
+ //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;
+ int widx;
+
+ //if (i>10000)printf("[%08d-%08d] %.4s\n", i, priv->idx_size, cx->dwChunkId);
+
+ 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;
+
+ /*
+ for (i=0; i<priv->idx_size; i++) {
+ idx = &((AVIINDEXENTRY *)priv->idx)[i];
+ fprintf (stderr, "[%08d] pos=%d offidx=%d\n", i, idx->dwChunkOffset, idx->dwFlags>>16&0xffff);
+ }
+ */
+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 +717,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.840749672 +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