diff --git a/AUTHORS b/AUTHORS index 47d6f5439a..a502b161bc 100644 --- a/AUTHORS +++ b/AUTHORS @@ -260,6 +260,7 @@ Tobias Diedrich * DXR2 driver * softpulldown video filter * ported Donald Graft's kerndeint video filter + * AVI OpenDML write support Kilian A. Foth * -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 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 (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; @@ -178,6 +220,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; inEntriesInUse; 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){ @@ -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; inbFieldPerFrame; 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; isuidx_size; cx++, i++) { + stream_reset(demuxer->stream); + for (j=0; jnEntriesInUse; 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;kstdidx[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; iidx_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; iidx_size; i++, idx++){ + if (!(idx->dwFlags & AVIIF_KEYFRAME) && idx->ckid == db) break; + } + if (iidx_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; iidx_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;jnEntriesInUse;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; inbFieldPerFrame; 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;i255) 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((posmovi_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((posmovi_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].dwChunkOffsetmovi_start || - (unsigned long)((AVIINDEXENTRY *)priv->idx)[1].dwChunkOffsetmovi_start) + if((AVI_IDX_OFFSET(&((AVIINDEXENTRY *)priv->idx)[0])movi_start || + AVI_IDX_OFFSET(&((AVIINDEXENTRY *)priv->idx)[1])movi_start )&& !priv->isodml) priv->idx_offset=demuxer->movi_start-4; else priv->idx_offset=0; #else - if((unsigned long)((AVIINDEXENTRY *)priv->idx)[0].dwChunkOffsetmovi_start) + if(AVI_IDX_OFFSET(&((AVIINDEXENTRY *)priv->idx)[0])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;iidx_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 #include #include #include #include +#include #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 avifile_write_chunk(muxer_stream_t *s,size_t len,unsigned int flags){ - muxer_t *muxer=s->muxer; +static void write_avi_list(FILE *f,unsigned int id,int len); +static void avifile_write_index(muxer_t *muxer); - // add to the index: - if(muxer->idx_pos>=muxer->idx_size){ - muxer->idx_size+=256; // 4kB - muxer->idx=realloc(muxer->idx,16*muxer->idx_size); +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_KEYFRAME)?0:ODML_NOTKEYFRAME; + si->idx[si->idxpos].ofs=ftello(muxer->file); + si->idx[si->idxpos].len=len; + ++si->idxpos; + + pos = muxer->file_end; + if (pos < ODML_CHUNKLEN && + pos + 16*muxer->idx_pos + len + 8 > ODML_CHUNKLEN) { + + avifile_write_index(muxer); + avifile_odml_new_riff(muxer); + + pos = muxer->file_end = ftello(muxer->file); + } + if (pos % ODML_CHUNKLEN + len + 8 > ODML_CHUNKLEN) { + avifile_odml_new_riff(muxer); + muxer->file_end = ftello(muxer->file); } - 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].dwChunkLength=len; - ++muxer->idx_pos; // write out the chunk: write_avi_chunk(muxer->file,s->ckid,len,s->buffer); /* unsigned char */ // alter counters: + if (len > s->h.dwSuggestedBufferSize){ + s->h.dwSuggestedBufferSize = len; + } 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++; @@ -120,6 +205,7 @@ static void avifile_write_chunk(muxer_stream_t *s,size_t len,unsigned int flags) s->size+=len; if((unsigned int)len>s->h.dwSuggestedBufferSize) s->h.dwSuggestedBufferSize=len; + muxer->file_end += len + 8; } static void write_avi_list(FILE *f,unsigned int id,int len){ @@ -137,28 +223,79 @@ static void write_avi_list(FILE *f,unsigned int id,int len){ #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){ uint32_t riff[3]; + unsigned int dmlh[1]; unsigned int i; unsigned int hdrsize; muxer_info_t info[16]; - FILE *f=muxer->file; + 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->file_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){ + int i; muxer->avih.dwMicroSecPerFrame=1000000.0*muxer->def_v->h.dwScale/muxer->def_v->h.dwRate; // muxer->avih.dwMaxBytesPerSec=1000000; // dummy!!!!! FIXME // muxer->avih.dwPaddingGranularity=2; // ??? muxer->avih.dwFlags|=AVIF_ISINTERLEAVED|AVIF_TRUSTCKTYPE; - muxer->avih.dwTotalFrames=muxer->def_v->h.dwLength; + muxer->avih.dwTotalFrames=0; + for (i=0; iidx_pos; i++) { + if (muxer->idx[i].ckid == muxer->def_v->ckid) + muxer->avih.dwTotalFrames++; + } // muxer->avih.dwSuggestedBufferSize=muxer->def_v->h.dwSuggestedBufferSize; muxer->avih.dwWidth=muxer->def_v->bih->biWidth; muxer->avih.dwHeight=muxer->def_v->bih->biHeight; @@ -166,18 +303,26 @@ static void avifile_write_header(muxer_t *muxer){ // AVI header: hdrsize=sizeof(muxer->avih)+8; + if (isodml) hdrsize+=sizeof(dmlh)+20; // dmlh // calc total header size: for(i=0;iavih.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,40 +332,103 @@ static void avifile_write_header(muxer_t *muxer){ // stream headers: for(i=0;iavih.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; + s->h.rcFrame.right = s->bih->biWidth; + s->h.rcFrame.bottom = s->bih->biHeight; + // fill out vprp info + memset(&vprp, 0, sizeof(vprp)); + vprp.dwVerticalRefreshRate = (s->h.dwRate+s->h.dwScale-1)/s->h.dwScale; + vprp.dwHTotalInT = muxer->avih.dwWidth; + vprp.dwVTotalInLines = muxer->avih.dwHeight; + vprp.dwFrameAspectRatio = avi_aspect(s->source); + vprp.dwFrameWidthInPixels = muxer->avih.dwWidth; + vprp.dwFrameHeightInLines = muxer->avih.dwHeight; + vprp.nbFieldPerFrame = 1; + vprp.FieldInfo[0].CompressedBMHeight = muxer->avih.dwHeight; + vprp.FieldInfo[0].CompressedBMWidth = muxer->avih.dwWidth; + 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); - switch(muxer->streams[i]->type){ + write_avi_list(f,listtypeSTREAMHEADER,hdrsize); + le2me_AVIStreamHeader(&s->h); + write_avi_chunk(f,ckidSTREAMHEADER,sizeof(s->h),&s->h); /* AVISTreamHeader */ // strh + le2me_AVIStreamHeader(&s->h); + + 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; jsuperidx[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 + if (isodml) { + 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 =============== @@ -274,14 +482,115 @@ info[i].id=0; } // 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); + } 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); + mp_msg(MSGT_MUXER, MSGL_ERR, "Try increasing MOVIALIGN in libmpdemux/muxer_avi.c\n", MOVIALIGN, ftell(muxer->file), ftell(muxer->file)-MOVIALIGN); + } + write_avi_list(f,listtypeAVIMOVIE,muxer->movi_end-ftello(f)-12); + } + muxer->movi_start=ftello(muxer->file); +} + +static void avifile_odml_write_index(muxer_t *muxer){ + muxer_stream_t* s; + struct avi_stream_info *si; + int i; + + for (i=0; iavih.dwStreams; i++) { + int j,k,n,idxpos,len,last,entries_per_subidx; + unsigned int idxhdr[8]; + s = muxer->streams[i]; + si = s->priv; + + /* + * 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). + */ + + len = 0; + n = 0; + entries_per_subidx = INT_MAX; + do { + off_t start = si->idx[0].ofs; + last = entries_per_subidx; + for (j=0; jidxpos; 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); + + si->superidxpos = (si->idxpos+entries_per_subidx-1) / entries_per_subidx; + + mp_msg(MSGT_MUXER, MSGL_V, "ODML: Stream %d: Using %d entries per subidx, %d entries in superidx\n", + i, entries_per_subidx, si->superidxpos); + + si->superidxsize = si->superidxpos; + si->superidx = malloc(sizeof(*si->superidx) * si->superidxsize); + memset(si->superidx, 0, sizeof(*si->superidx) * si->superidxsize); + + idxpos = 0; + for (j=0; jsuperidxpos; j++) { + off_t start = si->idx[idxpos].ofs; + int duration; + + duration = 0; + for (k=0; kidxpos; 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 = 32 + 8*k; + si->superidx[j].ofs = ftello(muxer->file); + si->superidx[j].duration = duration; + + fwrite(idxhdr,sizeof(idxhdr),1,muxer->file); + for (k=0; kidxpos; 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=ftello(muxer->file); } static void avifile_write_index(muxer_t *muxer){ - muxer->movi_end=ftell(muxer->file); + + if(muxer->file_end > ODML_CHUNKLEN && + muxer->idx && muxer->idx_pos>0) { + avifile_odml_write_index(muxer); + return; + } + + muxer->movi_end=ftello(muxer->file); if(muxer->idx && muxer->idx_pos>0){ int i; // fixup index entries: @@ -292,7 +601,7 @@ static void avifile_write_index(muxer_t *muxer){ for (i=0; iidx_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){ diff --git a/mencoder.c b/mencoder.c index 4adbd77d58..57ae80bd14 100644 --- a/mencoder.c +++ b/mencoder.c @@ -663,6 +663,12 @@ case VCODEC_COPY: 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);