diff options
author | ranma <ranma@b3059339-0415-0410-9bf9-f77b7e298cf2> | 2004-03-17 14:50:37 +0000 |
---|---|---|
committer | ranma <ranma@b3059339-0415-0410-9bf9-f77b7e298cf2> | 2004-03-17 14:50:37 +0000 |
commit | 873b579c1afbe832b108bf4254adc9bb27130db6 (patch) | |
tree | 052e21d9fefff721cbe8d91329e92c325597a242 | |
parent | 6080f085606ffe918a0260dc94a0e9c59f02cce7 (diff) | |
download | mpv-873b579c1afbe832b108bf4254adc9bb27130db6.tar.bz2 mpv-873b579c1afbe832b108bf4254adc9bb27130db6.tar.xz |
OpenDML read/write support
git-svn-id: svn://svn.mplayerhq.hu/mplayer/trunk@12037 b3059339-0415-0410-9bf9-f77b7e298cf2
-rw-r--r-- | AUTHORS | 4 | ||||
-rw-r--r-- | DOCS/man/en/mplayer.1 | 15 | ||||
-rw-r--r-- | libmpdemux/aviheader.c | 268 | ||||
-rw-r--r-- | libmpdemux/aviheader.h | 136 | ||||
-rw-r--r-- | libmpdemux/aviprint.c | 50 | ||||
-rw-r--r-- | libmpdemux/demux_avi.c | 16 | ||||
-rw-r--r-- | libmpdemux/muxer.h | 6 | ||||
-rw-r--r-- | libmpdemux/muxer_avi.c | 411 | ||||
-rw-r--r-- | mencoder.c | 6 |
9 files changed, 828 insertions, 84 deletions
@@ -260,6 +260,7 @@ Tobias Diedrich <ranma@gmx.at> * DXR2 driver * softpulldown video filter * ported Donald Graft's kerndeint video filter + * AVI OpenDML write support Kilian A. Foth <foth@informatik.uni-hamburg.de> * -slave mode @@ -623,6 +624,9 @@ Jake Janovetz Vivien Chappelier, Damien Vincent * libFAME authors [fast mpeg-1 encoder, used by -vo mpegpes/-vo dxr3] +Tilmann Bitterberg + * AVI OpenDML read support + _____________________________________________________ Their code is not used in the current player version, but I've got some ideas or other technical help from: diff --git a/DOCS/man/en/mplayer.1 b/DOCS/man/en/mplayer.1 index a65d16852a..9809923d49 100644 --- a/DOCS/man/en/mplayer.1 +++ b/DOCS/man/en/mplayer.1 @@ -817,7 +817,7 @@ Further, MPlayer won't prevent you from loading an index file generated 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, because MPlayer has OpenDML support. .TP .B \-mc <seconds/frame> Maximum A-V sync correction per frame (in seconds). @@ -926,20 +926,9 @@ not pass incoming UDP packets (see http://www.live.com/mplayer/). 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. diff --git a/libmpdemux/aviheader.c b/libmpdemux/aviheader.c index 332b9d9b16..7727d5285c 100644 --- a/libmpdemux/aviheader.c +++ b/libmpdemux/aviheader.c @@ -25,6 +25,48 @@ extern void print_strh(AVIStreamHeader *h); 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); + +static int odml_get_vstream_id(int id, unsigned char res[]) +{ + unsigned char *p = (unsigned char *)&id; + id = le2me_32(id); + + if (p[2] == 'd') { + if (res) { + res[0] = p[0]; + res[1] = p[1]; + } + return 1; + } + return 0; +} + +/* + * Simple quicksort for AVIINDEXENTRYs + */ +static void avi_idx_quicksort(AVIINDEXENTRY *idx, int from, int to) +{ + AVIINDEXENTRY temp; + int lo = to; + int hi = from; + off_t pivot_ofs = AVI_IDX_OFFSET(&idx[(from + to) / 2]); + do { + while(pivot_ofs < AVI_IDX_OFFSET(&idx[lo])) lo--; + while(pivot_ofs > AVI_IDX_OFFSET(&idx[hi])) hi++; + if(hi <= lo) { + if (hi != lo) { + memcpy(&temp, &idx[lo], sizeof(temp)); + memcpy(&idx[lo], &idx[hi], sizeof(temp)); + memcpy(&idx[hi], &temp, sizeof(temp)); + } + lo--; hi++; + } + } while (lo >= hi); + if (from < lo) avi_idx_quicksort(idx, from, lo); + if (to > hi) avi_idx_quicksort(idx, hi, to); +} void read_avi_header(demuxer_t *demuxer,int index_mode){ sh_audio_t *sh_audio=NULL; @@ -179,6 +221,47 @@ while(1){ 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,11 +329,41 @@ while(1){ } 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; + off_t base = 0; + uint32_t last_off = 0; priv->idx_size=size2>>4; mp_msg(MSGT_HEADER,MSGL_V,"Reading INDEX block, %d chunks for %ld frames (fpos=%p)\n", priv->idx_size,avih.dwTotalFrames, stream_tell(demuxer->stream)); @@ -261,8 +374,21 @@ while(1){ le2me_AVIINDEXENTRY((AVIINDEXENTRY*)priv->idx + i); chunksize-=priv->idx_size<<4; if(verbose>=2) print_index(priv->idx,priv->idx_size); - break; + /* + * Fixup index for files >4GB + */ + for (i = 0; i < priv->idx_size; i++) { + AVIINDEXENTRY *idx = (AVIINDEXENTRY*)priv->idx + i; + idx->dwFlags &= 0xffff; + if (idx->dwChunkOffset < last_off) { + mp_msg(MSGT_HEADER,MSGL_WARN,"Index offset going backwards (last=%08X, now=%08X), compensating...\n", last_off, idx->dwChunkOffset); + base += 0x100000000LL; + } + idx->dwFlags |= base >> 16; + last_off = idx->dwChunkOffset; + } } + break; /* added May 2002 */ case mmioFOURCC('R','I','F','F'): { char riff_type[4]; @@ -275,6 +401,10 @@ while(1){ chunksize = 0; list_end = 0; /* a new list will follow */ 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 +423,8 @@ while(1){ 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 +435,133 @@ while(1){ } +if (priv->isodml && (index_mode==-1 || index_mode==0)) { + int i, j, k; + int safety=1000; + + avisuperindex_chunk *cx; + AVIINDEXENTRY *idx; + + + if (priv->idx_size) free(priv->idx); + 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); + + // 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; + 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; + 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; + + } + } + + /* + * We convert the index by translating all entries into AVIINDEXENTRYs + * and sorting them by offset. The result should be the same index + * we would get with -forceidx. + */ + + idx = priv->idx = malloc(priv->idx_size * sizeof (AVIINDEXENTRY)); + + for (cx = priv->suidx; cx != &priv->suidx[priv->suidx_size]; cx++) { + avistdindex_chunk *sic; + for (sic = cx->stdidx; sic != &cx->stdidx[cx->nEntriesInUse]; sic++) { + avistdindex_entry *sie; + for (sie = sic->aIndex; sie != &sic->aIndex[sic->nEntriesInUse]; sie++) { + uint64_t off = sic->qwBaseOffset + sie->dwOffset - 8; + memcpy(&idx->ckid, sic->dwChunkId, 4); + idx->dwChunkOffset = off; + idx->dwFlags = (off >> 32) << 16; + idx->dwChunkLength = sie->dwSize & 0x7fffffff; + idx->dwFlags |= (sie->dwSize&0x80000000)?0x0:AVIIF_KEYFRAME; // bit 31 denotes !keyframe + idx++; + } + } + } + avi_idx_quicksort(priv->idx, 0, priv->idx_size-1); + + /* + 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, AVI_IDX_OFFSET(idx)); + 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) print_index(priv->idx, priv->idx_size); + + 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; @@ -376,6 +635,7 @@ if(index_mode>=2 || (priv->idx_size==0 && index_mode==1)){ idx=&((AVIINDEXENTRY *)priv->idx)[priv->idx_pos++]; idx->ckid=id; idx->dwFlags=AVIIF_KEYFRAME; // FIXME + idx->dwFlags|=(demuxer->filepos>>16)&0xffff0000U; idx->dwChunkOffset=(unsigned long)demuxer->filepos; idx->dwChunkLength=len; @@ -386,8 +646,8 @@ if(index_mode>=2 || (priv->idx_size==0 && index_mode==1)){ if(avi_stream_id(id)==idxfix_videostream){ switch(idxfix_divx){ case 3: c=stream_read_dword(demuxer->stream)<<5; //skip 32+5 bits for m$mpeg4v1 - case 1: if(c&0x40000000) idx->dwFlags=0;break; // divx 3 - case 2: if(c==0x1B6) idx->dwFlags=0;break; // divx 4 + case 1: if(c&0x40000000) idx->dwFlags&=~AVIIF_KEYFRAME;break; // divx 3 + case 2: if(c==0x1B6) idx->dwFlags&=~AVIIF_KEYFRAME;break; // divx 4 } } diff --git a/libmpdemux/aviheader.h b/libmpdemux/aviheader.h index 4ca63fc9c0..80449a548b 100644 --- a/libmpdemux/aviheader.h +++ b/libmpdemux/aviheader.h @@ -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 @@ typedef struct { unsigned char pts_corrected; unsigned char pts_has_video; unsigned int numberofframes; + avisuperindex_chunk *suidx; + int suidx_size; + int isodml; } avi_priv_t; #define AVI_PRIV ((avi_priv_t*)(demuxer->priv)) + +#define AVI_IDX_OFFSET(x) ((((uint64_t)(x)->dwFlags&0xffff0000)<<16)+(x)->dwChunkOffset) + +#endif /* _aviheader_h */ diff --git a/libmpdemux/aviprint.c b/libmpdemux/aviprint.c index 0f582da5a9..da271eae15 100644 --- a/libmpdemux/aviprint.c +++ b/libmpdemux/aviprint.c @@ -13,6 +13,8 @@ #include "wine/avifmt.h" #include "wine/vfw.h" +#include "aviheader.h" + //#include "codec-cfg.h" //#include "stheader.h" @@ -105,6 +107,31 @@ void print_video_header(BITMAPINFOHEADER *h){ 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 >> 16, vprp->dwFrameAspectRatio & 0xffff, + 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; @@ -114,10 +141,10 @@ void print_index(AVIINDEXENTRY *idx,int idx_size){ for(i=0;i<idx_size;i++){ int id=avi_stream_id(idx[i].ckid); if(id<0 || id>255) id=255; - printf("%5d: %.4s %4X %08X len:%6ld pos:%7d->%7.3f %7d->%7.3f\n",i, + printf("%5d: %.4s %4X %016llX len:%6ld pos:%7d->%7.3f %7d->%7.3f\n",i, (char *)&idx[i].ckid, - (unsigned int)idx[i].dwFlags, - (unsigned int)idx[i].dwChunkOffset, + (unsigned int)idx[i].dwFlags&0xffff, + (uint64_t)AVI_IDX_OFFSET(&idx[i]), // idx[i].dwChunkOffset+demuxer->movi_start, idx[i].dwChunkLength, pos[id],(float)pos[id]/18747.0f, @@ -128,4 +155,21 @@ 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"); +} diff --git a/libmpdemux/demux_avi.c b/libmpdemux/demux_avi.c index 9d2fa17f1d..bef49b1e45 100644 --- a/libmpdemux/demux_avi.c +++ b/libmpdemux/demux_avi.c @@ -213,7 +213,7 @@ do{ continue; // skip this chunk } - pos = priv->idx_offset + (unsigned long)idx->dwChunkOffset; + pos = (off_t)priv->idx_offset+AVI_IDX_OFFSET(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; @@ -325,7 +325,7 @@ do{ continue; // skip this chunk } - pos = priv->idx_offset+(unsigned long)idx->dwChunkOffset; + pos = priv->idx_offset+AVI_IDX_OFFSET(idx); if((pos<demux->movi_start || pos>=demux->movi_end) && (demux->movi_end>demux->movi_start)){ mp_msg(MSGT_DEMUX,MSGL_V,"ChunkOffset out of range! current=0x%X idx=0x%X \n",demux->filepos,pos); continue; @@ -446,6 +446,10 @@ demuxer_t* demux_open_avi(demuxer_t* demuxer){ priv->video_pack_no=0; priv->audio_block_no=0; priv->audio_block_size=0; + priv->isodml = 0; + priv->suidx_size = 0; + priv->suidx = NULL; + demuxer->priv=(void*)priv; //---- AVI header: @@ -468,13 +472,13 @@ demuxer_t* demux_open_avi(demuxer_t* demuxer){ 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((AVI_IDX_OFFSET(&((AVIINDEXENTRY *)priv->idx)[0])<demuxer->movi_start || + AVI_IDX_OFFSET(&((AVIINDEXENTRY *)priv->idx)[1])<demuxer->movi_start )&& !priv->isodml) priv->idx_offset=demuxer->movi_start-4; else priv->idx_offset=0; #else - if((unsigned long)((AVIINDEXENTRY *)priv->idx)[0].dwChunkOffset<demuxer->movi_start) + if(AVI_IDX_OFFSET(&((AVIINDEXENTRY *)priv->idx)[0])<demuxer->movi_start) priv->idx_offset=demuxer->movi_start-4; else priv->idx_offset=0; @@ -494,7 +498,7 @@ demuxer_t* demux_open_avi(demuxer_t* demuxer){ for(i=0;i<priv->idx_size;i++){ AVIINDEXENTRY* idx=&((AVIINDEXENTRY *)priv->idx)[i]; demux_stream_t* ds=demux_avi_select_stream(demuxer,idx->ckid); - off_t pos = priv->idx_offset + (unsigned long)idx->dwChunkOffset; + off_t pos = priv->idx_offset + AVI_IDX_OFFSET(idx); if(a_pos==-1 && ds==demuxer->audio){ a_pos=pos; if(v_pos!=-1) break; diff --git a/libmpdemux/muxer.h b/libmpdemux/muxer.h index 696d25bc32..bf546fca1f 100644 --- a/libmpdemux/muxer.h +++ b/libmpdemux/muxer.h @@ -48,9 +48,9 @@ typedef struct { 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; diff --git a/libmpdemux/muxer_avi.c b/libmpdemux/muxer_avi.c index f98f4b0002..f6be330f69 100644 --- a/libmpdemux/muxer_avi.c +++ b/libmpdemux/muxer_avi.c @@ -1,16 +1,16 @@ - #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 "stream.h" +#include "demuxer.h" +#include "stheader.h" #include "wine/mmreg.h" #include "wine/avifmt.h" @@ -19,6 +19,7 @@ #include "muxer.h" #include "aviheader.h" +#include "mp_msg.h" extern char *info_name; extern char *info_artist; @@ -28,11 +29,38 @@ extern char *info_copyright; 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 +72,11 @@ static muxer_stream_t* avifile_new_stream(muxer_t *muxer,int type){ 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 +88,7 @@ static muxer_stream_t* avifile_new_stream(muxer_t *muxer,int type){ 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,28 +123,80 @@ if(len>0){ } } +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; + int isodml = muxer->file_end > ODML_CHUNKLEN ? 1 : 0; + + if (!isodml) { + // 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=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_KEYFR |