Mercurial > mplayer.hg
view libmpdemux/muxer_avi.c @ 11619:179138947307
This patch contains bugfixes for the esd audio output driver that I
uncovered while trying to send sound to a remote esd server over a
wireless (11 mbs, just enough to handle to sound) link.
First, the sound was full "ticking" sounds. I found a bug that
prevented the "send the remainder of this block" code from ever being
called - so large chunks of audio were simply being ignored. Fixing
this bug removed the "ticking" from audio streams.
Fixing this bug, however, uncovered another problem - when the socket
buffer was full, doing a blocking write to finish the buffer would take
far too long and would turn video into a chunky mess. I'd imagine this
blocking write would be fine for an audio-only stream, but it turns out
to hold up the video far too much.
The solution in this patch is to write as much data as possible to the
socket, and then return as soon as possible, reporting the number of
bytes actually written accurately back to mplayer. I've tested it on
both local and remote esd servers, and it works well.
Patch by Benjamin Osheroff <ben@gimbo.net>
author | attila |
---|---|
date | Wed, 10 Dec 2003 12:19:13 +0000 |
parents | e00d3351b912 |
children | 9d5fdc9847ec |
line wrap: on
line source
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <inttypes.h> #include <unistd.h> #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); } #define WFSIZE(wf) (sizeof(WAVEFORMATEX)+(wf)->cbSize) 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;i<muxer->avih.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;i<muxer->avih.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;i<muxer->idx_pos;i++) muxer->idx[i].dwChunkOffset-=muxer->movi_start-4; // write index chunk: for (i=0; i<muxer->idx_pos; i++) le2me_AVIINDEXENTRY((&muxer->idx[i])); write_avi_chunk(muxer->file,ckidAVINEWINDEX,16*muxer->idx_pos,muxer->idx); /* AVIINDEXENTRY */ for (i=0; i<muxer->idx_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; }