OpenDML read/write support

git-svn-id: svn://svn.mplayerhq.hu/mplayer/trunk@12037 b3059339-0415-0410-9bf9-f77b7e298cf2
This commit is contained in:
ranma 2004-03-17 14:50:37 +00:00
parent 6080f08560
commit 873b579c1a
9 changed files with 830 additions and 86 deletions

View File

@ -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:

View File

@ -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.

View File

@ -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; 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){
@ -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
}
}

View File

@ -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 */

View File

@ -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");
}

View File

@ -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;

View File

@ -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;

View File

@ -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 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; i<muxer->idx_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;i<muxer->avih.dwStreams;i++){
muxer_stream_t *s = muxer->streams[i];
struct avi_stream_info *si = s->priv;
hdrsize+=12; // LIST
hdrsize+=sizeof(muxer->streams[i]->h)+8; // strh
switch(muxer->streams[i]->type){
case MUXER_TYPE_VIDEO:
hdrsize+=muxer->streams[i]->bih->biSize+8; // strf
hdrsize+=8+4*(9+8*1); // vprp
break;
case MUXER_TYPE_AUDIO:
hdrsize+=WFSIZE(muxer->streams[i]->wf)+8; // strf
break;
}
if (isodml && si && si->superidx && si->superidxsize) {
hdrsize += 32 + 16*si->superidxsize; //indx
}
}
write_avi_list(f,listtypeAVIHEADER,hdrsize);
@ -187,40 +332,103 @@ static void avifile_write_header(muxer_t *muxer){
// stream headers:
for(i=0;i<muxer->avih.dwStreams;i++){
hdrsize=sizeof(muxer->streams[i]->h)+8; // strh
switch(muxer->streams[i]->type){
muxer_stream_t *s = muxer->streams[i];
struct avi_stream_info *si = s->priv;
unsigned int idxhdr[8];
int j,n;
hdrsize=sizeof(s->h)+8; // strh
if (si && si->superidx && si->superidxsize) {
hdrsize += 32 + 16*si->superidxsize; //indx
}
switch(s->type){
case MUXER_TYPE_VIDEO:
hdrsize+=muxer->streams[i]->bih->biSize+8; // strf
muxer->streams[i]->h.fccHandler = muxer->streams[i]->bih->biCompression;
hdrsize+=s->bih->biSize+8; // strf
s->h.fccHandler = s->bih->biCompression;
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; j<n; j++) {
struct avi_odmlsuperidx_entry *entry = &si->superidx[j];
unsigned int data[4];
data[0] = le2me_32(entry->ofs);
data[1] = le2me_32(entry->ofs >> 32);
data[2] = le2me_32(entry->len);
data[3] = le2me_32(entry->duration);
fwrite(data,sizeof(data),1,f);
}
}
}
// ODML
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; i<muxer->avih.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; j<si->idxpos; j++) {
len = si->idx[j].ofs - start;
if(len >= ODML_CHUNKLEN || n >= entries_per_subidx) {
if (entries_per_subidx > n) {
entries_per_subidx = n;
}
start = si->idx[j].ofs;
len = 0;
n = 0;
}
n++;
}
} while (last != entries_per_subidx);
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; j<si->superidxpos; j++) {
off_t start = si->idx[idxpos].ofs;
int duration;
duration = 0;
for (k=0; k<entries_per_subidx && idxpos+k<si->idxpos; k++) {
duration += s->h.dwSampleSize ? si->idx[idxpos+k].len/s->h.dwSampleSize : 1;
}
idxhdr[0] = le2me_32((s->ckid << 16) | mmioFOURCC('i', 'x', 0, 0));
idxhdr[1] = le2me_32(24 + 8*k);
idxhdr[2] = le2me_32(0x01000002);
idxhdr[3] = le2me_32(k);
idxhdr[4] = le2me_32(s->ckid);
idxhdr[5] = le2me_32(start + 8);
idxhdr[6] = le2me_32((start + 8)>> 32);
idxhdr[7] = 0; /* unused */
si->superidx[j].len = 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; k<entries_per_subidx && idxpos<si->idxpos; k++) {
unsigned int entry[2];
entry[0] = le2me_32(si->idx[idxpos].ofs - start);
entry[1] = le2me_32(si->idx[idxpos].len | si->idx[idxpos].flags);
idxpos++;
fwrite(entry,sizeof(entry),1,muxer->file);
}
}
}
muxer->file_end=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; i<muxer->idx_pos; i++) le2me_AVIINDEXENTRY((&muxer->idx[i]));
muxer->avih.dwFlags|=AVIF_HASINDEX;
}
muxer->file_end=ftell(muxer->file);
muxer->file_end=ftello(muxer->file);
}
void muxer_init_muxer_avi(muxer_t *muxer){

View File

@ -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);