#include #include #include #include #include #include "config.h" #include "../version.h" //#include "stream.h" //#include "demuxer.h" //#include "stheader.h" #include "wine/mmreg.h" #include "wine/avifmt.h" #include "wine/vfw.h" #include "bswap.h" #include "muxer.h" #include "aviheader.h" extern char *info_name; extern char *info_artist; extern char *info_genre; extern char *info_subject; extern char *info_copyright; extern char *info_sourceform; extern char *info_comment; static muxer_stream_t* avifile_new_stream(muxer_t *muxer,int type){ muxer_stream_t* s; if (!muxer) return NULL; if(muxer->avih.dwStreams>=MUXER_MAX_STREAMS){ printf("Too many streams! increase MUXER_MAX_STREAMS !\n"); return NULL; } s=malloc(sizeof(muxer_stream_t)); memset(s,0,sizeof(muxer_stream_t)); if(!s) return NULL; // no mem!? muxer->streams[muxer->avih.dwStreams]=s; s->type=type; s->id=muxer->avih.dwStreams; s->timer=0.0; s->size=0; s->muxer=muxer; switch(type){ case MUXER_TYPE_VIDEO: s->ckid=mmioFOURCC(('0'+s->id/10),('0'+(s->id%10)),'d','c'); s->h.fccType=streamtypeVIDEO; if(!muxer->def_v) muxer->def_v=s; break; case MUXER_TYPE_AUDIO: s->ckid=mmioFOURCC(('0'+s->id/10),('0'+(s->id%10)),'w','b'); s->h.fccType=streamtypeAUDIO; break; default: printf("WarninG! unknown stream type: %d\n",type); return NULL; } muxer->avih.dwStreams++; return s; } static void write_avi_chunk(FILE *f,unsigned int id,int len,void* data){ int le_len = le2me_32(len); int le_id = le2me_32(id); fwrite(&le_id,4,1,f); fwrite(&le_len,4,1,f); if(len>0){ if(data){ // DATA fwrite(data,len,1,f); if(len&1){ // padding unsigned char zerobyte=0; fwrite(&zerobyte,1,1,f); } } else { // JUNK char *avi_junk_data="[= MPlayer junk data! =]"; if(len&1) ++len; // padding while(len>0){ int l=strlen(avi_junk_data); if(l>len) l=len; fwrite(avi_junk_data,l,1,f); len-=l; } } } } static void avifile_write_chunk(muxer_stream_t *s,size_t len,unsigned int flags){ muxer_t *muxer=s->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); } 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(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"); } else { // VBR s->h.dwLength++; } s->timer=(double)s->h.dwLength*s->h.dwScale/s->h.dwRate; s->size+=len; if((unsigned int)len>s->h.dwSuggestedBufferSize) s->h.dwSuggestedBufferSize=len; } static void write_avi_list(FILE *f,unsigned int id,int len){ unsigned int list_id=FOURCC_LIST; int le_len; int le_id; len+=4; // list fix list_id = le2me_32(list_id); le_len = le2me_32(len); le_id = le2me_32(id); fwrite(&list_id,4,1,f); fwrite(&le_len,4,1,f); fwrite(&le_id,4,1,f); } // muxer->streams[i]->wf->cbSize #define WFSIZE(wf) (sizeof(WAVEFORMATEX)+(((wf)->cbSize)?((wf)->cbSize-2):0)) static void avifile_write_header(muxer_t *muxer){ uint32_t riff[3]; unsigned int i; unsigned int hdrsize; muxer_info_t info[16]; FILE *f=muxer->file; // 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){ 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.dwSuggestedBufferSize=muxer->def_v->h.dwSuggestedBufferSize; muxer->avih.dwWidth=muxer->def_v->bih->biWidth; muxer->avih.dwHeight=muxer->def_v->bih->biHeight; } // AVI header: hdrsize=sizeof(muxer->avih)+8; // calc total header size: for(i=0;iavih.dwStreams;i++){ 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 break; case MUXER_TYPE_AUDIO: hdrsize+=WFSIZE(muxer->streams[i]->wf)+8; // strf break; } } write_avi_list(f,listtypeAVIHEADER,hdrsize); le2me_MainAVIHeader(&muxer->avih); write_avi_chunk(f,ckidAVIMAINHDR,sizeof(muxer->avih),&muxer->avih); /* MainAVIHeader */ le2me_MainAVIHeader(&muxer->avih); // stream headers: for(i=0;iavih.dwStreams;i++){ 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 break; case MUXER_TYPE_AUDIO: hdrsize+=WFSIZE(muxer->streams[i]->wf)+8; // strf 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){ 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); } 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); } break; } } // ============= INFO =============== // always include software info info[0].id=mmioFOURCC('I','S','F','T'); // Software: info[0].text="MEncoder " VERSION; // include any optional strings i=1; if(info_name!=NULL){ info[i].id=mmioFOURCC('I','N','A','M'); // Name: info[i++].text=info_name; } if(info_artist!=NULL){ info[i].id=mmioFOURCC('I','A','R','T'); // Artist: info[i++].text=info_artist; } if(info_genre!=NULL){ info[i].id=mmioFOURCC('I','G','N','R'); // Genre: info[i++].text=info_genre; } if(info_subject!=NULL){ info[i].id=mmioFOURCC('I','S','B','J'); // Subject: info[i++].text=info_subject; } if(info_copyright!=NULL){ info[i].id=mmioFOURCC('I','C','O','P'); // Copyright: info[i++].text=info_copyright; } if(info_sourceform!=NULL){ info[i].id=mmioFOURCC('I','S','R','F'); // Source Form: info[i++].text=info_sourceform; } if(info_comment!=NULL){ info[i].id=mmioFOURCC('I','C','M','T'); // Comment: info[i++].text=info_comment; } info[i].id=0; hdrsize=0; // calc info size: for(i=0;info[i].id!=0;i++) if(info[i].text){ size_t sz=strlen(info[i].text)+1; hdrsize+=sz+8+sz%2; } // write infos: if (hdrsize!=0){ write_avi_list(f,mmioFOURCC('I','N','F','O'),hdrsize); for(i=0;info[i].id!=0;i++) if(info[i].text){ write_avi_chunk(f,info[i].id,strlen(info[i].text)+1,info[i].text); } } // 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); } static void avifile_write_index(muxer_t *muxer){ muxer->movi_end=ftell(muxer->file); if(muxer->idx && muxer->idx_pos>0){ int i; // fixup index entries: // for(i=0;iidx_pos;i++) muxer->idx[i].dwChunkOffset-=muxer->movi_start-4; // write index chunk: for (i=0; iidx_pos; i++) le2me_AVIINDEXENTRY((&muxer->idx[i])); write_avi_chunk(muxer->file,ckidAVINEWINDEX,16*muxer->idx_pos,muxer->idx); /* AVIINDEXENTRY */ for (i=0; iidx_pos; i++) le2me_AVIINDEXENTRY((&muxer->idx[i])); muxer->avih.dwFlags|=AVIF_HASINDEX; } muxer->file_end=ftell(muxer->file); } void muxer_init_muxer_avi(muxer_t *muxer){ muxer->cont_new_stream = &avifile_new_stream; muxer->cont_write_chunk = &avifile_write_chunk; muxer->cont_write_header = &avifile_write_header; muxer->cont_write_index = &avifile_write_index; }