From bf46a109ede5d25f506cc75ae80fe1acc5b34edd Mon Sep 17 00:00:00 2001 From: arpi Date: Fri, 27 Dec 2002 22:43:20 +0000 Subject: the long-waited MUXER layer, and new MPEG-PS muxer patch by Andriy N. Gritsenko git-svn-id: svn://svn.mplayerhq.hu/mplayer/trunk@8586 b3059339-0415-0410-9bf9-f77b7e298cf2 --- TOOLS/vivodump.c | 14 +- cfg-mencoder.h | 13 ++ libmpcodecs/ve_divx4.c | 8 +- libmpcodecs/ve_lavc.c | 8 +- libmpcodecs/ve_libdv.c | 8 +- libmpcodecs/ve_qtvideo.c | 8 +- libmpcodecs/ve_rawrgb.c | 8 +- libmpcodecs/ve_vfw.c | 11 +- libmpcodecs/ve_xvid.c | 8 +- libmpdemux/Makefile | 2 +- libmpdemux/aviwrite.c | 297 -------------------------------- libmpdemux/aviwrite.h | 56 ------ libmpdemux/muxer.c | 28 +++ libmpdemux/muxer.h | 74 ++++++++ libmpdemux/muxer_avi.c | 297 ++++++++++++++++++++++++++++++++ libmpdemux/muxer_mpeg.c | 434 +++++++++++++++++++++++++++++++++++++++++++++++ mencoder.c | 48 ++++-- 17 files changed, 915 insertions(+), 407 deletions(-) delete mode 100644 libmpdemux/aviwrite.c delete mode 100644 libmpdemux/aviwrite.h create mode 100644 libmpdemux/muxer.c create mode 100644 libmpdemux/muxer.h create mode 100644 libmpdemux/muxer_avi.c create mode 100644 libmpdemux/muxer_mpeg.c diff --git a/TOOLS/vivodump.c b/TOOLS/vivodump.c index c0c3417392..57205fd787 100644 --- a/TOOLS/vivodump.c +++ b/TOOLS/vivodump.c @@ -6,7 +6,7 @@ #include "wine/avifmt.h" #include "wine/vfw.h" -#include "aviwrite.h" +#include "muxer.h" static const short h263_format[8][2] = { { 0, 0 }, @@ -149,8 +149,8 @@ int pos=0; int frames=0; FILE *f=fopen("paulvandykforanangel.viv","rb"); FILE *f2=fopen("GB1.avi","wb"); -aviwrite_t* avi=aviwrite_new_muxer(); -aviwrite_stream_t* mux=aviwrite_new_stream(avi,AVIWRITE_TYPE_VIDEO); +muxer_t* avi=muxer_new_muxer(MUXER_TYPE_AVI); +muxer_stream_t* mux=muxer_new_stream(avi,MUXER_TYPE_VIDEO); //unsigned char* buffer=malloc(0x200000); int i,len; int v_id=0; @@ -169,7 +169,7 @@ mux->bih->biSize=sizeof(BITMAPINFOHEADER); mux->bih->biPlanes=1; mux->bih->biBitCount=24; mux->bih->biCompression=0x6f766976;// 7669766f; -aviwrite_write_header(avi,f2); +muxer_write_header(avi,f2); /* c=fgetc(f); if(c) printf("error! not vivo file?\n"); @@ -222,7 +222,7 @@ while((c=fgetc(f))>=0){ // end of frame: printf("Frame size: %d\n",mux->buffer_len); h263_decode_picture_header(mux->buffer); - aviwrite_write_chunk(avi,mux,f2,mux->buffer_len,0x10); + muxer_write_chunk(avi,mux,f2,mux->buffer_len,0x10); mux->buffer_len=0; if((v_id&0xF0)==0x10) fprintf(stderr,"hmm. last video packet %02X\n",v_id); @@ -258,8 +258,8 @@ mux->bih->biWidth=width; mux->bih->biHeight=height; mux->bih->biSizeImage=3*width*height; -aviwrite_write_index(avi,f2); +muxer_write_index(avi,f2); fseek(f2,0,SEEK_SET); -aviwrite_write_header(avi,f2); +muxer_write_header(avi,f2); } diff --git a/cfg-mencoder.h b/cfg-mencoder.h index 37660d38a1..51c694999c 100644 --- a/cfg-mencoder.h +++ b/cfg-mencoder.h @@ -129,6 +129,16 @@ struct config info_conf[]={ {NULL, NULL, 0, 0, 0, 0, NULL} }; +struct config of_conf[]={ + {"avi", &out_file_format, CONF_TYPE_FLAG, 0, 0, MUXER_TYPE_AVI, NULL}, + {"mpeg", &out_file_format, CONF_TYPE_FLAG, 0, 0, MUXER_TYPE_MPEG, NULL}, + {"help", "\nAvailable output formats:\n" + " avi - Microsoft Audio/Video Interleaved\n" + " mpeg - MPEG-1 system stream format\n" + "\n", CONF_TYPE_PRINT, CONF_NOCFG, 0, 0, NULL}, + {NULL, NULL, 0, 0, 0, 0, NULL} +}; + static config_t mencoder_opts[]={ /* name, pointer, type, flags, min, max */ {"include", cfg_include, CONF_TYPE_FUNC_PARAM, CONF_NOSAVE, 0, 0, NULL}, /* this must be the first!!! */ @@ -156,6 +166,9 @@ static config_t mencoder_opts[]={ {"oac", oac_conf, CONF_TYPE_SUBCONFIG, 0, 0, 0, NULL}, {"ovc", ovc_conf, CONF_TYPE_SUBCONFIG, 0, 0, 0, NULL}, + // output file format + {"of", of_conf, CONF_TYPE_SUBCONFIG, 0, 0, 0, NULL}, + // override FOURCC in output file {"ffourcc", &force_fourcc, CONF_TYPE_STRING, 0, 4, 4, NULL}, diff --git a/libmpcodecs/ve_divx4.c b/libmpcodecs/ve_divx4.c index d18713d461..5ef26068cb 100644 --- a/libmpcodecs/ve_divx4.c +++ b/libmpcodecs/ve_divx4.c @@ -13,7 +13,7 @@ #include "demuxer.h" #include "stheader.h" -#include "aviwrite.h" +#include "muxer.h" #include "img_format.h" #include "mp_image.h" @@ -44,7 +44,7 @@ static int pass; extern char* passtmpfile; -extern void mencoder_write_chunk(aviwrite_stream_t *s,int len,unsigned int flags); +extern void mencoder_write_chunk(muxer_stream_t *s,int len,unsigned int flags); #include @@ -96,7 +96,7 @@ struct config divx4opts_conf[]={ }; struct vf_priv_s { - aviwrite_stream_t* mux; + muxer_stream_t* mux; ENC_RESULT enc_result; ENC_FRAME enc_frame; void* enc_handle; @@ -289,7 +289,7 @@ static int vf_open(vf_instance_t *vf, char* args){ #endif vf->priv=malloc(sizeof(struct vf_priv_s)); memset(vf->priv,0,sizeof(struct vf_priv_s)); - vf->priv->mux=(aviwrite_stream_t*)args; + vf->priv->mux=(muxer_stream_t*)args; mux_v->bih=malloc(sizeof(BITMAPINFOHEADER)); mux_v->bih->biSize=sizeof(BITMAPINFOHEADER); diff --git a/libmpcodecs/ve_lavc.c b/libmpcodecs/ve_lavc.c index 30c22b5ef3..b064a777e5 100644 --- a/libmpcodecs/ve_lavc.c +++ b/libmpcodecs/ve_lavc.c @@ -20,14 +20,14 @@ #include "demuxer.h" #include "stheader.h" -#include "aviwrite.h" +#include "muxer.h" #include "img_format.h" #include "mp_image.h" #include "vf.h" extern char* passtmpfile; -extern void mencoder_write_chunk(aviwrite_stream_t *s,int len,unsigned int flags); +extern void mencoder_write_chunk(muxer_stream_t *s,int len,unsigned int flags); //===========================================================================// @@ -175,7 +175,7 @@ struct config lavcopts_conf[]={ #endif struct vf_priv_s { - aviwrite_stream_t* mux; + muxer_stream_t* mux; AVCodecContext *context; AVFrame *pic; AVCodec *codec; @@ -531,7 +531,7 @@ static int vf_open(vf_instance_t *vf, char* args){ vf->put_image=put_image; vf->priv=malloc(sizeof(struct vf_priv_s)); memset(vf->priv,0,sizeof(struct vf_priv_s)); - vf->priv->mux=(aviwrite_stream_t*)args; + vf->priv->mux=(muxer_stream_t*)args; /* XXX: hack: some of the MJPEG decoder DLL's needs exported huffman table, so we define a zero-table, also lavc mjpeg encoder is putting diff --git a/libmpcodecs/ve_libdv.c b/libmpcodecs/ve_libdv.c index 221a0354c7..511c059556 100644 --- a/libmpcodecs/ve_libdv.c +++ b/libmpcodecs/ve_libdv.c @@ -15,7 +15,7 @@ #include "demuxer.h" #include "stheader.h" -#include "aviwrite.h" +#include "muxer.h" #include "img_format.h" #include "mp_image.h" @@ -23,7 +23,7 @@ #include -extern void mencoder_write_chunk(aviwrite_stream_t *s,int len,unsigned int flags); +extern void mencoder_write_chunk(muxer_stream_t *s,int len,unsigned int flags); #ifndef DV_WIDTH #define DV_WIDTH 720 @@ -32,7 +32,7 @@ extern void mencoder_write_chunk(aviwrite_stream_t *s,int len,unsigned int flags #endif struct vf_priv_s { - aviwrite_stream_t* mux; + muxer_stream_t* mux; dv_encoder_t* enc; }; @@ -91,7 +91,7 @@ static int vf_open(vf_instance_t *vf, char* args){ vf->put_image=put_image; vf->priv=malloc(sizeof(struct vf_priv_s)); memset(vf->priv,0,sizeof(struct vf_priv_s)); - vf->priv->mux=(aviwrite_stream_t*)args; + vf->priv->mux=(muxer_stream_t*)args; vf->priv->enc=dv_encoder_new(1,1,1); // FIXME, parse some options! if(!vf->priv->enc) return 0; diff --git a/libmpcodecs/ve_qtvideo.c b/libmpcodecs/ve_qtvideo.c index 4c9c2f0573..e8d292fa79 100644 --- a/libmpcodecs/ve_qtvideo.c +++ b/libmpcodecs/ve_qtvideo.c @@ -21,7 +21,7 @@ #include "demuxer.h" #include "stheader.h" -#include "aviwrite.h" +#include "muxer.h" #include "img_format.h" #include "mp_image.h" @@ -86,7 +86,7 @@ static long (*CountComponents)(ComponentDescription* desc); static OSErr (*GetComponentInfo)(Component prev,ComponentDescription* desc,Handle h1,Handle h2,Handle h3); -extern void mencoder_write_chunk(aviwrite_stream_t *s,int len,unsigned int flags); +extern void mencoder_write_chunk(muxer_stream_t *s,int len,unsigned int flags); //static int format=mmioFOURCC('S','V','Q','1'); @@ -111,7 +111,7 @@ static ImageSequence seq; struct vf_priv_s { - aviwrite_stream_t* mux; + muxer_stream_t* mux; //dv_encoder_t* enc; }; @@ -279,7 +279,7 @@ static int vf_open(vf_instance_t *vf, char* args){ vf->put_image=put_image; vf->priv=malloc(sizeof(struct vf_priv_s)); memset(vf->priv,0,sizeof(struct vf_priv_s)); - vf->priv->mux=(aviwrite_stream_t*)args; + vf->priv->mux=(muxer_stream_t*)args; mux_v->bih=malloc(sizeof(BITMAPINFOHEADER)+MAX_IDSIZE); mux_v->bih->biSize=sizeof(BITMAPINFOHEADER)+MAX_IDSIZE; diff --git a/libmpcodecs/ve_rawrgb.c b/libmpcodecs/ve_rawrgb.c index 55e119fdf7..2388db5f28 100644 --- a/libmpcodecs/ve_rawrgb.c +++ b/libmpcodecs/ve_rawrgb.c @@ -10,18 +10,18 @@ #include "demuxer.h" #include "stheader.h" -#include "aviwrite.h" +#include "muxer.h" #include "img_format.h" #include "mp_image.h" #include "vf.h" -extern void mencoder_write_chunk(aviwrite_stream_t *s,int len,unsigned int flags); +extern void mencoder_write_chunk(muxer_stream_t *s,int len,unsigned int flags); //===========================================================================// struct vf_priv_s { - aviwrite_stream_t* mux; + muxer_stream_t* mux; }; #define mux_v (vf->priv->mux) @@ -61,7 +61,7 @@ static int vf_open(vf_instance_t *vf, char* args){ vf->put_image=put_image; vf->priv=malloc(sizeof(struct vf_priv_s)); memset(vf->priv,0,sizeof(struct vf_priv_s)); - vf->priv->mux=(aviwrite_stream_t*)args; + vf->priv->mux=(muxer_stream_t*)args; mux_v->bih=malloc(sizeof(BITMAPINFOHEADER)); mux_v->bih->biSize=sizeof(BITMAPINFOHEADER); diff --git a/libmpcodecs/ve_vfw.c b/libmpcodecs/ve_vfw.c index 868fb6a385..c1a86da16e 100644 --- a/libmpcodecs/ve_vfw.c +++ b/libmpcodecs/ve_vfw.c @@ -2,6 +2,7 @@ #include #include #include +#include #include "../config.h" #ifdef USE_WIN32DLL @@ -18,13 +19,13 @@ #include "wine/vfw.h" #include "wine/avifmt.h" -#include "aviwrite.h" - #include "img_format.h" #include "mp_image.h" #include "vf.h" -extern void mencoder_write_chunk(aviwrite_stream_t *s,int len,unsigned int flags); +#include "muxer.h" + +extern void mencoder_write_chunk(muxer_stream_t *s,int len,unsigned int flags); //===========================================================================// @@ -38,7 +39,7 @@ struct config vfwopts_conf[]={ }; struct vf_priv_s { - aviwrite_stream_t* mux; + muxer_stream_t* mux; BITMAPINFOHEADER* bih; }; @@ -256,7 +257,7 @@ static int vf_open(vf_instance_t *vf, char* args){ vf->put_image=put_image; vf->priv=malloc(sizeof(struct vf_priv_s)); memset(vf->priv,0,sizeof(struct vf_priv_s)); - vf->priv->mux=(aviwrite_stream_t*)args; + vf->priv->mux=(muxer_stream_t*)args; vfw_bih=malloc(sizeof(BITMAPINFOHEADER)); vfw_bih->biSize=sizeof(BITMAPINFOHEADER); diff --git a/libmpcodecs/ve_xvid.c b/libmpcodecs/ve_xvid.c index 460be88c1c..12f1b64bbc 100644 --- a/libmpcodecs/ve_xvid.c +++ b/libmpcodecs/ve_xvid.c @@ -13,7 +13,7 @@ #include "demuxer.h" #include "stheader.h" -#include "aviwrite.h" +#include "muxer.h" #include "img_format.h" #include "mp_image.h" @@ -65,7 +65,7 @@ static int const motion_presets[7] = { }; extern char* passtmpfile; -extern void mencoder_write_chunk(aviwrite_stream_t *s,int len,unsigned int flags); +extern void mencoder_write_chunk(muxer_stream_t *s,int len,unsigned int flags); static int xvidenc_pass = 0; static int xvidenc_quality = 4; @@ -132,7 +132,7 @@ struct config xvidencopts_conf[] = { }; struct vf_priv_s { - aviwrite_stream_t* mux; + muxer_stream_t* mux; XVID_ENC_FRAME enc_frame; void* enc_handle; vbr_control_t vbr_state; @@ -487,7 +487,7 @@ vf_open(vf_instance_t *vf, char* args) vf->put_image = put_image; vf->priv = malloc(sizeof(struct vf_priv_s)); memset(vf->priv, 0, sizeof(struct vf_priv_s)); - vf->priv->mux = (aviwrite_stream_t*)args; + vf->priv->mux = (muxer_stream_t*)args; vf->priv->mux->bih = malloc(sizeof(BITMAPINFOHEADER)); vf->priv->mux->bih->biSize = sizeof(BITMAPINFOHEADER); diff --git a/libmpdemux/Makefile b/libmpdemux/Makefile index f17407e4fa..a9bfdc617b 100644 --- a/libmpdemux/Makefile +++ b/libmpdemux/Makefile @@ -3,7 +3,7 @@ LIBNAME = libmpdemux.a include ../config.mak -SRCS = mp3_hdr.c video.c mpeg_hdr.c cache2.c asfheader.c aviheader.c aviprint.c aviwrite.c demux_asf.c demux_avi.c demux_mov.c parse_mp4.c demux_mpg.c demux_pva.c demux_viv.c demuxer.c dvdauth.c dvdnav_stream.c open.c parse_es.c stream.c tv.c tvi_dummy.c tvi_v4l.c tvi_bsdbt848.c frequencies.c demux_fli.c demux_real.c demux_y4m.c yuv4mpeg.c yuv4mpeg_ratio.c demux_nuv.c demux_film.c demux_roq.c mf.c demux_mf.c demux_audio.c demux_demuxers.c demux_ogg.c demux_bmp.c cdda.c demux_rawaudio.c cddb.c cdinfo.c demux_rawdv.c ai_alsa.c ai_oss.c audio_in.c demux_smjpeg.c +SRCS = mp3_hdr.c video.c mpeg_hdr.c cache2.c asfheader.c aviheader.c aviprint.c muxer.c muxer_avi.c muxer_mpeg.c demux_asf.c demux_avi.c demux_mov.c parse_mp4.c demux_mpg.c demux_pva.c demux_viv.c demuxer.c dvdauth.c dvdnav_stream.c open.c parse_es.c stream.c tv.c tvi_dummy.c tvi_v4l.c tvi_bsdbt848.c frequencies.c demux_fli.c demux_real.c demux_y4m.c yuv4mpeg.c yuv4mpeg_ratio.c demux_nuv.c demux_film.c demux_roq.c mf.c demux_mf.c demux_audio.c demux_demuxers.c demux_ogg.c demux_bmp.c cdda.c demux_rawaudio.c cddb.c cdinfo.c demux_rawdv.c ai_alsa.c ai_oss.c audio_in.c demux_smjpeg.c ifeq ($(XMMS_PLUGINS),yes) SRCS += demux_xmms.c endif diff --git a/libmpdemux/aviwrite.c b/libmpdemux/aviwrite.c deleted file mode 100644 index e70225c7bd..0000000000 --- a/libmpdemux/aviwrite.c +++ /dev/null @@ -1,297 +0,0 @@ - -#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 "aviwrite.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; - -aviwrite_stream_t* aviwrite_new_stream(aviwrite_t *muxer,int type){ - aviwrite_stream_t* s; - if(muxer->avih.dwStreams>=AVIWRITE_MAX_STREAMS){ - printf("Too many streams! increase AVIWRITE_MAX_STREAMS !\n"); - return NULL; - } - s=malloc(sizeof(aviwrite_stream_t)); - memset(s,0,sizeof(aviwrite_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; - switch(type){ - case AVIWRITE_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 AVIWRITE_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; -} - -aviwrite_t* aviwrite_new_muxer(){ - aviwrite_t* muxer=malloc(sizeof(aviwrite_t)); - memset(muxer,0,sizeof(aviwrite_t)); - return muxer; -} - -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; - } - } -} -} - -void aviwrite_write_chunk(aviwrite_t *muxer,aviwrite_stream_t *s, FILE *f,int len,unsigned int flags){ - - // 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(f)-(muxer->movi_start-4); - muxer->idx[muxer->idx_pos].dwChunkLength=len; - ++muxer->idx_pos; - - // write out the chunk: - write_avi_chunk(f,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(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)) - -void aviwrite_write_header(aviwrite_t *muxer,FILE *f){ - unsigned int riff[3]; - int i; - unsigned int hdrsize; - aviwrite_info_t info[16]; - - // 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 AVIWRITE_TYPE_VIDEO: - hdrsize+=muxer->streams[i]->bih->biSize+8; // strf - break; - case AVIWRITE_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 AVIWRITE_TYPE_VIDEO: - hdrsize+=muxer->streams[i]->bih->biSize+8; // strf - break; - case AVIWRITE_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 AVIWRITE_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 AVIWRITE_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); -} - -void aviwrite_write_index(aviwrite_t *muxer,FILE *f){ - muxer->movi_end=ftell(f); - 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(f,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(f); -} - diff --git a/libmpdemux/aviwrite.h b/libmpdemux/aviwrite.h deleted file mode 100644 index feab55cac0..0000000000 --- a/libmpdemux/aviwrite.h +++ /dev/null @@ -1,56 +0,0 @@ - -#define AVIWRITE_MAX_STREAMS 16 - -#define AVIWRITE_TYPE_VIDEO 0 -#define AVIWRITE_TYPE_AUDIO 1 - -typedef struct { - // muxer data: - int type; // audio or video - int id; // stream no - unsigned int ckid; // chunk id (00dc 01wb etc) - double timer; - off_t size; - // buffering: - unsigned char *buffer; - unsigned int buffer_size; - unsigned int buffer_len; - // source stream: - void* source; // sh_audio or sh_video - int codec; // codec used for encoding. 0 means copy - // avi stream header: - AVIStreamHeader h; // Rate/Scale and SampleSize must be filled by caller! - // stream specific: - WAVEFORMATEX *wf; - BITMAPINFOHEADER *bih; // in format -} aviwrite_stream_t; - -typedef struct { - unsigned int id; - char *text; -} aviwrite_info_t; - -typedef struct { - // encoding: - MainAVIHeader avih; - unsigned int movi_start; - unsigned int movi_end; - unsigned int file_end; - // index: - AVIINDEXENTRY *idx; - int idx_pos; - int idx_size; - // streams: - //int num_streams; - aviwrite_stream_t* def_v; // default video stream (for general headers) - aviwrite_stream_t* streams[AVIWRITE_MAX_STREAMS]; -} aviwrite_t; - -aviwrite_stream_t* aviwrite_new_stream(aviwrite_t *muxer,int type); -aviwrite_t* aviwrite_new_muxer(); -void aviwrite_write_chunk(aviwrite_t *muxer,aviwrite_stream_t *s, FILE *f,int len,unsigned int flags); -void aviwrite_write_header(aviwrite_t *muxer,FILE *f); -void aviwrite_write_index(aviwrite_t *muxer,FILE *f); - - - diff --git a/libmpdemux/muxer.c b/libmpdemux/muxer.c new file mode 100644 index 0000000000..877be8ae53 --- /dev/null +++ b/libmpdemux/muxer.c @@ -0,0 +1,28 @@ + +#include +#include +#include +#include + +#include "config.h" +#include "../version.h" + +#include "wine/mmreg.h" +#include "wine/avifmt.h" +#include "wine/vfw.h" + +#include "muxer.h" + +muxer_t* muxer_new_muxer(int type){ + muxer_t* muxer=malloc(sizeof(muxer_t)); + memset(muxer,0,sizeof(muxer_t)); + switch (type) { + case MUXER_TYPE_MPEG: + muxer_init_muxer_mpeg(muxer); + break; + case MUXER_TYPE_AVI: + default: + muxer_init_muxer_avi(muxer); + } + return muxer; +} diff --git a/libmpdemux/muxer.h b/libmpdemux/muxer.h new file mode 100644 index 0000000000..4410209559 --- /dev/null +++ b/libmpdemux/muxer.h @@ -0,0 +1,74 @@ + +#define MUXER_MAX_STREAMS 16 + +#define MUXER_TYPE_VIDEO 0 +#define MUXER_TYPE_AUDIO 1 + +#define MUXER_TYPE_AVI 0 +#define MUXER_TYPE_MPEG 1 + +#define MUXER_MPEG_BLOCKSIZE 2048 // 2048 or 2324 - ? + +typedef struct { + // muxer data: + int type; // audio or video + int id; // stream no + uint32_t ckid; // chunk id (00dc 01wb etc) + double timer; + off_t size; + // buffering: + unsigned char *buffer; + unsigned int buffer_size; + unsigned int buffer_len; + // mpeg block buffer: + unsigned char *b_buffer; + unsigned int b_buffer_ptr; + // source stream: + void* source; // sh_audio or sh_video + int codec; // codec used for encoding. 0 means copy + // avi stream header: + AVIStreamHeader h; // Rate/Scale and SampleSize must be filled by caller! + // stream specific: + WAVEFORMATEX *wf; + BITMAPINFOHEADER *bih; // in format + // mpeg specific: + unsigned int gop_start; // frame number of this GOP start + size_t ipb[3]; // sizes of I/P/B frames +} muxer_stream_t; + +typedef struct { + uint32_t id; + char *text; +} muxer_info_t; + +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 + // index: + AVIINDEXENTRY *idx; + int idx_pos; + int idx_size; + // streams: + int num_videos; // for MPEG recalculations + unsigned int sysrate; // max rate in bytes/s + //int num_streams; + muxer_stream_t* def_v; // default video stream (for general headers) + muxer_stream_t* streams[MUXER_MAX_STREAMS]; + void (*cont_write_chunk)(struct muxer_t *,muxer_stream_t *,FILE *,size_t,unsigned int); + void (*cont_write_header)(struct muxer_t *,FILE *); + void (*cont_write_index)(struct muxer_t *,FILE *); + muxer_stream_t* (*cont_new_stream)(struct muxer_t *,int); +} muxer_t; + +muxer_t* muxer_new_muxer(int type); +#define muxer_new_stream(muxer,a) muxer->cont_new_stream(muxer,a) +#define muxer_write_chunk(muxer,a,b,c,d) muxer->cont_write_chunk(muxer,a,b,c,d) +#define muxer_write_header(muxer,f) muxer->cont_write_header(muxer,f) +#define muxer_write_index(muxer,f) muxer->cont_write_index(muxer,f) + +void muxer_init_muxer_avi(muxer_t *); +void muxer_init_muxer_mpeg(muxer_t *); + diff --git a/libmpdemux/muxer_avi.c b/libmpdemux/muxer_avi.c new file mode 100644 index 0000000000..6aaeb044ff --- /dev/null +++ b/libmpdemux/muxer_avi.c @@ -0,0 +1,297 @@ + +#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->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; + 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_t *muxer,muxer_stream_t *s, FILE *f,size_t len,unsigned int flags){ + + // 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(f)-(muxer->movi_start-4); + muxer->idx[muxer->idx_pos].dwChunkLength=len; + ++muxer->idx_pos; + + // write out the chunk: + write_avi_chunk(f,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,FILE *f){ + uint32_t riff[3]; + unsigned int i; + unsigned int hdrsize; + muxer_info_t info[16]; + + // 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,FILE *f){ + muxer->movi_end=ftell(f); + 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(f,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(f); +} + +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; +} diff --git a/libmpdemux/muxer_mpeg.c b/libmpdemux/muxer_mpeg.c new file mode 100644 index 0000000000..b086d245ce --- /dev/null +++ b/libmpdemux/muxer_mpeg.c @@ -0,0 +1,434 @@ + +#include +#include +#include +#include + +#include "config.h" +#include "../version.h" + +#include "wine/mmreg.h" +#include "wine/avifmt.h" +#include "wine/vfw.h" +#include "bswap.h" + +#include "muxer.h" + +// 18 bytes reserved for block headers and STD +#define MUXER_MPEG_DATASIZE (MUXER_MPEG_BLOCKSIZE-18) + +// ISO-11172 requirements +#define MPEG_MAX_PTS_DELAY 90000 /* 1s */ +#define MPEG_MAX_SCR_INTERVAL 63000 /* 0.7s */ + +// suggestions +#define MPEG_STARTPTS 45000 /* 0.5s */ +#define MPEG_MIN_PTS_DELAY 9000 /* 0.1s */ +#define MPEG_STARTSCR 9 /* 0.1ms */ + +//static unsigned int mpeg_min_delay; +//static unsigned int mpeg_max_delay; + +static muxer_stream_t* mpegfile_new_stream(muxer_t *muxer,int type){ + muxer_stream_t *s; + + if(muxer->avih.dwStreams>=MUXER_MAX_STREAMS){ + printf("Too many streams! increase MUXER_MAX_STREAMS !\n"); + return NULL; + } + switch (type) { + case MUXER_TYPE_VIDEO: + if (muxer->num_videos >= 15) { + printf ("MPEG stream can't contain above of 15 video streams!\n"); + return NULL; + } + break; + case MUXER_TYPE_AUDIO: + if (muxer->avih.dwStreams - muxer->num_videos >= 31) { + printf ("MPEG stream can't contain above of 31 audio streams!\n"); + return NULL; + } + break; + default: + printf ("Unknown stream type!\n"); + return NULL; + } + s=malloc(sizeof(muxer_stream_t)); + memset(s,0,sizeof(muxer_stream_t)); + if(!s) return NULL; // no mem!? + if (!(s->b_buffer = malloc (MUXER_MPEG_BLOCKSIZE))) { + free (s); + return NULL; // no mem?! + } else if (type == MUXER_TYPE_VIDEO) { + s->ckid = be2me_32 (0x1e0 + muxer->num_videos); + muxer->num_videos++; + s->h.fccType=streamtypeVIDEO; + if(!muxer->def_v) muxer->def_v=s; +// printf ("Added video stream %d\n", muxer->num_videos); + } else { // MUXER_TYPE_AUDIO + s->ckid = be2me_32 (0x1c0 + s->id - muxer->num_videos); + s->h.fccType=streamtypeAUDIO; +// printf ("Added audio stream %d\n", s->id - muxer->num_videos + 1); + } + muxer->streams[muxer->avih.dwStreams]=s; + s->type=type; + s->id=muxer->avih.dwStreams; + s->timer=0.0; + s->size=0; + muxer->avih.dwStreams++; + return s; +} + +static void write_mpeg_ts(unsigned char *b, unsigned int ts, char mod) { + b[0] = ((ts >> 29) & 0xf) | 1 | mod; + b[1] = (ts >> 22) & 0xff; + b[2] = ((ts >> 14) & 0xff) | 1; + b[3] = (ts >> 7) & 0xff; + b[4] = ((ts << 1) & 0xff) | 1; +} + +static void write_mpeg_rate(unsigned char *b, unsigned int rate) { + if (rate) + rate--; // for round upward + rate /= 50; + rate++; // round upward + b[0] = ((rate >> 15) & 0x7f) | 0x80; + b[1] = (rate >> 7) & 0xff; + b[2] = ((rate << 1) & 0xff) | 1; +} + +static void write_mpeg_std(unsigned char *b, unsigned int size, char mod) { + if (size) + size--; // for round upward + if (size < (128 << 8)) + size >>= 7; // by 128 bytes + else { + size >>= 10; + size |= 0x2000; // by 1kbyte + } + size++; // round upward + b[0] = ((size >> 8) & 0x3f) | 0x40 | mod; + b[1] = size & 0xff; +} + +static int write_mpeg_block(muxer_t *muxer, muxer_stream_t *s, FILE *f, char *bl, size_t len, int isoend){ + size_t sz; // rest in block buffer + unsigned char buff[12]; // 0x1ba header + unsigned int mints=0; + uint16_t l1; + + if (s->b_buffer_ptr == 0) { // 00001111 if no PTS + s->b_buffer[0] = 0xf; + s->b_buffer_ptr = 1; + sz = MUXER_MPEG_DATASIZE-1; + } else if (s->b_buffer_ptr > MUXER_MPEG_DATASIZE) { + printf ("Unknown error in write_mpeg_block()!\n"); + return 0; + } else { + sz = MUXER_MPEG_DATASIZE - s->b_buffer_ptr; + if (s->b_buffer[7] == 0xff) // PTS not set yet + s->b_buffer[11] = 0xf; // terminate stuFFing bytes + } + if (len > sz) + len = sz; + *(uint32_t *)buff = be2me_32 (0x1ba); + write_mpeg_ts (buff+4, muxer->file_end, 0x20); // 0010 and SCR + write_mpeg_rate (buff+9, muxer->sysrate); + fwrite (buff, 12, 1, f); + fwrite (&s->ckid, 4, 1, f); // stream_id + memset (buff, 0xff, 12); // stuFFing bytes + sz -= len; + // calculate padding bytes in buffer + while (mints < s->b_buffer_ptr && s->b_buffer[mints] == 0xff) mints++; + if (mints+sz < 12) { // cannot write padding block so write up to 12 stuFFing bytes + l1 = be2me_16 (MUXER_MPEG_DATASIZE); + fwrite (&l1, 2, 1, f); + mints = 0; // so stuFFed bytes will be written all + if (sz) + fwrite (buff, sz, 1, f); + sz = 0; // no padding block anyway + } else { // use padding block + if (sz > 6) // sufficient for PAD header so don't shorter data + mints = 0; + else + sz += mints; // skip stuFFing bytes (sz>8 here) + l1 = be2me_16 (s->b_buffer_ptr+len-mints); + fwrite (&l1, 2, 1, f); + } + if (s->b_buffer_ptr) + fwrite (s->b_buffer+mints, s->b_buffer_ptr-mints, 1, f); + if (len) + fwrite (bl, len, 1, f); + if (sz > 6) { // padding block (0x1be) + uint32_t l0; + + if (isoend) + l0 = be2me_32 (0x1b9); + else + l0 = be2me_32 (0x1be); + sz -= 6; + l1 = be2me_16 (sz); + fwrite (&l0, 4, 1, f); + fwrite (&l1, 2, 1, f); + memset (s->b_buffer, 0xff, sz); // stuFFing bytes + fwrite (s->b_buffer, sz, 1, f); + } + s->b_buffer_ptr = 0; + muxer->movi_end += MUXER_MPEG_BLOCKSIZE; + // prepare timestamps for next pack + mints = (MUXER_MPEG_BLOCKSIZE*90000/muxer->sysrate)+1; // min ts delta + sz = (int)(s->timer*90000) + MPEG_STARTPTS; // new PTS + if (sz > muxer->file_end) + sz -= muxer->file_end; // suggested ts delta + else + { + sz = 0; + printf ("Error in stream: PTS earlier than SCR!\n"); + } + if (sz > MPEG_MAX_PTS_DELAY) { +// printf ("Warning: attempt to set PTS to SCR delay to %u \n", sz); + mints = sz-MPEG_MAX_PTS_DELAY; // try to compensate + if (mints > MPEG_MAX_SCR_INTERVAL) { + printf ("Error in stream: SCR interval %u is too big!\n", mints); + } + } else if (sz > 54000) // assume 0.3...0.7s is optimal + mints += (sz-45000)>>2; // reach 0.5s in 4 blocks ? + else if (sz < 27000) { + unsigned int newsysrate = 0; + + if (s->timer > 0.5) // too early to calculate??? + newsysrate = muxer->movi_end/(s->timer*0.4); // pike-factor 2.5 (8dB) + if (sz < MPEG_MIN_PTS_DELAY) + printf ("Error in stream: PTS to SCR delay %u is too little!\n", sz); + if (muxer->sysrate < newsysrate) + muxer->sysrate = newsysrate; // increase next rate to current rate + else if (!newsysrate) + muxer->sysrate += muxer->sysrate>>3; // increase next rate by 25% + } + muxer->file_end += mints; // update the system timestamp + return len; +} + +static void set_mpeg_pts(muxer_t *muxer, muxer_stream_t *s, unsigned int pts) { + unsigned int dts, nts; + + if (s->b_buffer_ptr != 0 && s->b_buffer[7] != 0xff) + return; // already set + if (s->b_buffer_ptr == 0) { + memset (s->b_buffer, 0xff, 7); // reserved for PTS or STD, stuFFing for now + s->b_buffer_ptr = 12; + } + dts = (int)(s->timer*90000) + MPEG_STARTPTS; // PTS + if (pts) { + write_mpeg_ts (s->b_buffer+2, pts, 0x30); // 0011 and both PTS/DTS + } else { + write_mpeg_ts (s->b_buffer+7, dts, 0x20); // 0010 and PTS only + return; + } + nts = dts - muxer->file_end; +// if (nts < mpeg_min_delay) mpeg_min_delay = nts; +// if (nts > mpeg_max_delay) mpeg_max_delay = nts; + nts = 180000*s->h.dwScale/s->h.dwRate; // two frames + if (dts-nts < muxer->file_end) { + dts += muxer->file_end; + dts /= 2; // calculate average time + printf ("Warning: DTS to SCR delay is too small\n"); + } + else + dts -= nts/2; // one frame :) + write_mpeg_ts (s->b_buffer+7, dts, 0x10); +} + +static void mpegfile_write_chunk(muxer_t *muxer,muxer_stream_t *s,FILE *f,size_t len,unsigned int flags){ + size_t ptr=0, sz; + unsigned int pts=0; + + if (s->type == MUXER_TYPE_VIDEO) { // try to recognize frame type... + if (s->buffer[0] != 0 || s->buffer[1] != 0 || s->buffer[2] != 1 || len<6) { + printf ("Unknown block type, possibly non-MPEG stream!\n"); + sz = len; +// return; + } else if (s->buffer[3] == 0 || s->buffer[3] == 0xb3 || + s->buffer[3] == 0xb8) { // Picture or GOP + int temp_ref; + int pt; + + if (s->buffer[3]) { // GOP -- scan for Picture + s->gop_start = s->h.dwLength; + while (ptr < len-5 && (s->buffer[ptr] != 0 || s->buffer[ptr+1] != 0 || + s->buffer[ptr+2] != 1 || s->buffer[ptr+3] != 0)) ptr++; + if (s->b_buffer_ptr > MUXER_MPEG_DATASIZE-39-12) { // 39 bytes for Gop+Pic+Slice headers + write_mpeg_block (muxer, s, f, NULL, 0, 0); + } + } + if (ptr >= len-5) { + pt = 0; // Picture not found?! + temp_ref = 0; + printf ("Warning: picture not found in GOP!\n"); + } else { + pt = (s->buffer[ptr+5]>>3) & 7; + temp_ref = (s->buffer[ptr+4]<<2)+(s->buffer[ptr+5]>>6); + } + ptr = 0; + temp_ref += s->gop_start; + switch (pt) { + case 2: // predictive + if (s->ipb[0]) { + sz = len + s->ipb[0]; + if (s->ipb[0] < s->ipb[2]) + s->ipb[0] = s->ipb[2]; + s->ipb[2] = 0; + } else if (s->ipb[2]) { + sz = len + s->ipb[2]; + s->ipb[0] = s->ipb[2]; + s->ipb[2] = 0; + } else + sz = 4 * len; // no bidirectional frames yet? + s->ipb[1] = len; + // pictires may be not in frame sequence so recalculate timer + pts = (int)(90000*((double)temp_ref*s->h.dwScale/s->h.dwRate)) + MPEG_STARTPTS; + break; + case 3: // bidirectional + s->ipb[2] += len; + sz = s->ipb[1] + s->ipb[2]; + // pictires may be not in frame sequence so recalculate timer + s->timer = (double)temp_ref*s->h.dwScale/s->h.dwRate; + break; + default: // intra-coded + // pictires may be not in frame sequence so recalculate timer + pts = (int)(90000*((double)temp_ref*s->h.dwScale/s->h.dwRate)) + MPEG_STARTPTS; + sz = len; // no extra buffer for it... + } + } else { + printf ("Unknown block type, possibly non-MPEG stream!\n"); + sz = len; +// return; + } + sz <<= 1; + } else { // MUXER_TYPE_AUDIO + if (len < 2*MUXER_MPEG_DATASIZE) + sz = 2*MUXER_MPEG_DATASIZE; // min requirement + else + sz = len; + } + set_mpeg_pts (muxer, s, pts); + // 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++; + } + if (!muxer->sysrate) { + muxer->sysrate = 2108000/8; // constrained stream parameter + muxer->file_end = MUXER_MPEG_BLOCKSIZE*90000/muxer->sysrate + MPEG_STARTSCR+1; + } + if (sz > s->h.dwSuggestedBufferSize) { // increase and set STD + s->h.dwSuggestedBufferSize = sz; + if (s->b_buffer[2] != 0xff) // has both PTS and DTS + write_mpeg_std (s->b_buffer, s->h.dwSuggestedBufferSize, 0x40); // 01 + else // has only PTS + write_mpeg_std (s->b_buffer+5, s->h.dwSuggestedBufferSize, 0x40); // 01 + } + s->size += len; + // write out block(s) if it's ready + while (s->b_buffer_ptr+len >= MUXER_MPEG_DATASIZE-12) { // reserved for std and pts + // write out the block + sz = write_mpeg_block (muxer, s, f, &s->buffer[ptr], len, 0); + // recalculate the rest of chunk + ptr += sz; + len -= sz; + } + s->timer = (double)s->h.dwLength*s->h.dwScale/s->h.dwRate; + if (len) { // save rest in buffer + if (s->b_buffer_ptr == 0) { + memset (s->b_buffer, 0xff, 12); // stuFFing bytes for now + if (s->type == MUXER_TYPE_AUDIO && s->h.dwSampleSize) { // CBR audio + sz = s->h.dwLength - len/s->h.dwSampleSize; // first sample number + write_mpeg_ts (s->b_buffer+7, + (int)(90000*((double)sz*s->h.dwScale/s->h.dwRate)) + MPEG_STARTPTS, + 0x20); // 0010 and PTS only + } + s->b_buffer_ptr = 12; + } + memcpy (s->b_buffer+s->b_buffer_ptr, s->buffer+ptr, len); + s->b_buffer_ptr += len; + } +} + +static void mpegfile_write_header(muxer_t *muxer,FILE *f){ + unsigned int i; + size_t sz = MUXER_MPEG_BLOCKSIZE-24; + unsigned char buff[12]; + muxer_stream_t *s = muxer->streams[0]; + uint32_t l1; + uint16_t l2; + + if (s == NULL) + return; // no streams!? + // packet header (0x1ba) -- rewrite first stream buffer + *(uint32_t *)buff = be2me_32 (0x1ba); + write_mpeg_ts (buff+4, MPEG_STARTSCR, 0x20); // 0010 -- pack + write_mpeg_rate (buff+9, muxer->sysrate); + fwrite (buff, 12, 1, f); + // start system stream (in own block): Sys (0x1bb) + l1 = be2me_32 (0x1bb); + l2 = be2me_16 (6 + 3*muxer->avih.dwStreams); // header_length + fwrite (&l1, 4, 1, f); + fwrite (&l2, 2, 1, f); + write_mpeg_rate (buff, muxer->sysrate); // rate_bound + // set number of audio/video, fixed_flag=CSPS_flag=system_*_lock_flag=0 + buff[3] = (muxer->avih.dwStreams - muxer->num_videos) << 2; // audio_bound + buff[4] = muxer->num_videos | 0x20; + buff[5] = 0xff; // reserved_byte + fwrite (buff, 6, 1, f); + for (i = 0; i < muxer->avih.dwStreams; i++) { + buff[0] = ((char *)&muxer->streams[i]->ckid)[3]; // last char in big endian +//fprintf (stderr, "... stream 0x1%02x; bufsize %u", (int)buff[0], muxer->streams[i]->h.dwSuggestedBufferSize); + write_mpeg_std (buff+1, muxer->streams[i]->h.dwSuggestedBufferSize, 0xc0); // 11 + fwrite (buff, 3, 1, f); + sz -= 3; + } + if (sz >= 6) { // padding block + l1 = be2me_32 (0x1be); + sz -= 6; + l2 = be2me_16 (sz); + fwrite (&l1, 4, 1, f); + fwrite (&l2, 2, 1, f); + } + s->b_buffer[0] = 0x0f; // end of list - next bit has to be 0 + // stuFFing bytes -- rewrite first stream buffer + if (sz > 1) + memset (s->b_buffer+1, 0xff, sz-1); + fwrite (s->b_buffer, sz, 1, f); + muxer->movi_start = 0; + muxer->movi_end = MUXER_MPEG_BLOCKSIZE; +} + +static void mpegfile_write_index(muxer_t *muxer,FILE *f){ + unsigned int i; + unsigned int rsr; + + if (!muxer->avih.dwStreams) return; // no streams?! + // finish all but one video and audio streams + rsr = muxer->sysrate; // reserve it since it's silly change it at that point + for (i = 0; i < muxer->avih.dwStreams-1; i++) + write_mpeg_block (muxer, muxer->streams[i], f, NULL, 0, 0); + // end sequence: ISO-11172-End (0x1b9) and finish very last block + write_mpeg_block (muxer, muxer->streams[i], f, NULL, 0, 1); +//fprintf (stderr, "PTS to SCR delay: min %u.%03u, max %u.%03u\n", +// mpeg_min_delay/90000, (mpeg_min_delay/90)%1000, +// mpeg_max_delay/90000, (mpeg_max_delay/90)%1000); + muxer->sysrate = rsr; +} + +void muxer_init_muxer_mpeg(muxer_t *muxer){ + muxer->cont_new_stream = &mpegfile_new_stream; + muxer->cont_write_chunk = &mpegfile_write_chunk; + muxer->cont_write_header = &mpegfile_write_header; + muxer->cont_write_index = &mpegfile_write_index; +// mpeg_min_delay = mpeg_max_delay = MPEG_STARTPTS-MPEG_STARTSCR; +} + diff --git a/mencoder.c b/mencoder.c index 1c3d82758c..8f4c498cfd 100644 --- a/mencoder.c +++ b/mencoder.c @@ -47,7 +47,7 @@ static char* banner_text= #include "libmpdemux/demuxer.h" #include "libmpdemux/stheader.h" #include "libmpdemux/mp3_hdr.h" -#include "libmpdemux/aviwrite.h" +#include "libmpdemux/muxer.h" #include "libvo/video_out.h" @@ -108,6 +108,8 @@ static char** video_fm_list=NULL; // override video codec family static int out_audio_codec=-1; static int out_video_codec=-1; +int out_file_format=MUXER_TYPE_AVI; // default to AVI + // audio stream skip/resync functions requires only for seeking. // (they should be implemented in the audio codec layer) //void skip_audio_frame(sh_audio_t *sh_audio){} @@ -301,12 +303,12 @@ static void exit_sighandler(int x){ interrupted=1; } -static aviwrite_t* muxer=NULL; +static muxer_t* muxer=NULL; static FILE* muxer_f=NULL; // callback for ve_*.c: -void mencoder_write_chunk(aviwrite_stream_t *s,int len,unsigned int flags){ - aviwrite_write_chunk(muxer,s,muxer_f,len,flags); +void mencoder_write_chunk(muxer_stream_t *s,int len,unsigned int flags){ + muxer_write_chunk(muxer,s,muxer_f,len,flags); } extern void print_wave_header(WAVEFORMATEX *h); @@ -335,8 +337,8 @@ uint32_t skippedframes=0; uint32_t duplicatedframes=0; uint32_t badframes=0; -aviwrite_stream_t* mux_a=NULL; -aviwrite_stream_t* mux_v=NULL; +muxer_stream_t* mux_a=NULL; +muxer_stream_t* mux_v=NULL; off_t muxer_f_size=0; #ifdef HAVE_MP3LAME @@ -600,11 +602,11 @@ if(!muxer_f) { mencoder_exit(1,NULL); } -muxer=aviwrite_new_muxer(); +muxer=muxer_new_muxer(out_file_format); // ============= VIDEO =============== -mux_v=aviwrite_new_stream(muxer,AVIWRITE_TYPE_VIDEO); +mux_v=muxer_new_stream(muxer,MUXER_TYPE_VIDEO); mux_v->buffer_size=0x200000; // 2MB mux_v->buffer=malloc(mux_v->buffer_size); @@ -698,7 +700,7 @@ if ((force_fourcc != NULL) && (strlen(force_fourcc) >= 4)) // ============= AUDIO =============== if(sh_audio){ -mux_a=aviwrite_new_stream(muxer,AVIWRITE_TYPE_AUDIO); +mux_a=muxer_new_stream(muxer,MUXER_TYPE_AUDIO); mux_a->buffer_size=0x100000; //16384; mux_a->buffer=malloc(mux_a->buffer_size); @@ -809,7 +811,7 @@ if(audio_delay!=0.0){ } // if(sh_audio) printf(MSGTR_WritingAVIHeader); -aviwrite_write_header(muxer,muxer_f); +muxer_write_header(muxer,muxer_f); decoded_frameno=0; @@ -877,6 +879,18 @@ if (seek_to_sec) { // if(demuxer2) demux_seek(demuxer2, d, 1); } +if (out_file_format == MUXER_TYPE_MPEG) + { + if (audio_preload > 0.4) { + fprintf(stderr,"Limiting audio preload to 0.4s\n"); + audio_preload = 0.4; + } + if (audio_density < 4) { + fprintf(stderr,"Increasing audio density to 4\n"); + audio_preload = 4; + } + } + if(tv_param_on == 1) { fprintf(stderr,"Forcing audio preload to 0, max pts correction to 0\n"); @@ -983,7 +997,7 @@ if(sh_audio){ } } if(len<=0) break; // EOF? - aviwrite_write_chunk(muxer,mux_a,muxer_f,len,0x10); + muxer_write_chunk(muxer,mux_a,muxer_f,len,0x10); if(!mux_a->h.dwSampleSize && mux_a->timer>0) mux_a->wf->nAvgBytesPerSec=0.5f+(double)mux_a->size/mux_a->timer; // avg bps (VBR) if(mux_a->buffer_len>=len){ @@ -1063,11 +1077,11 @@ ptimer_start = GetTimerMS(); switch(mux_v->codec){ case VCODEC_COPY: mux_v->buffer=start; - if(skip_flag<=0) aviwrite_write_chunk(muxer,mux_v,muxer_f,in_size,(sh_video->ds->flags&1)?0x10:0); + if(skip_flag<=0) muxer_write_chunk(muxer,mux_v,muxer_f,in_size,(sh_video->ds->flags&1)?0x10:0); break; case VCODEC_FRAMENO: mux_v->buffer=(unsigned char *)&decoded_frameno; // tricky - if(skip_flag<=0) aviwrite_write_chunk(muxer,mux_v,muxer_f,sizeof(int),0x10); + if(skip_flag<=0) muxer_write_chunk(muxer,mux_v,muxer_f,sizeof(int),0x10); break; default: // decode_video will callback down to ve_*.c encoders, through the video filters @@ -1078,7 +1092,7 @@ default: // unwanted skipping of a frame, what to do? if(skip_limit==0){ // skipping not allowed -> write empty frame: - aviwrite_write_chunk(muxer,mux_v,muxer_f,0,0); + muxer_write_chunk(muxer,mux_v,muxer_f,0,0); } else { // skipping allowed -> skip it and distriubute timer error: v_timer_corr-=(float)mux_v->h.dwScale/mux_v->h.dwRate; @@ -1095,7 +1109,7 @@ if(skip_flag<0){ if(!tv_param_on && !verbose) printf(MSGTR_DuplicateFrames,-skip_flag); while(skip_flag<0){ duplicatedframes++; - aviwrite_write_chunk(muxer,mux_v,muxer_f,0,0); + muxer_write_chunk(muxer,mux_v,muxer_f,0,0); ++skip_flag; } } else @@ -1254,11 +1268,11 @@ if(sh_audio && mux_a->codec==ACODEC_VBRMP3 && !lame_param_vbr){ #endif printf(MSGTR_WritingAVIIndex); -aviwrite_write_index(muxer,muxer_f); +muxer_write_index(muxer,muxer_f); muxer_f_size=ftello(muxer_f); printf(MSGTR_FixupAVIHeader); fseek(muxer_f,0,SEEK_SET); -aviwrite_write_header(muxer,muxer_f); // update header +muxer_write_header(muxer,muxer_f); // update header if(ferror(muxer_f) || fclose(muxer_f) != 0) { mp_msg(MSGT_MENCODER,MSGL_FATAL,MSGTR_ErrorWritingFile, out_filename); mencoder_exit(1, NULL); -- cgit v1.2.3