view libmpdemux/muxer_avi.c @ 9278:caea8ed36b48

The reason why mplayer crashes (in some cases) when using x11 output and -wid (>0) parameter is this: Mplayer by default creates a colormap using DirectColor visual. If the window given to mplayer uses TrueColor visual there will be an error when mplayer sets the colormap for the window. This patch modifies mplayer to use TrueColor visual if the window given to mplayer uses TrueColor. Another solution is to make sure that the window given to mplayer is created using DirectColor visual if it is supported by the display. Jouni Tulkki <jitulkki@cc.hut.fi>
author arpi
date Tue, 04 Feb 2003 18:31:44 +0000
parents 12fc55eb3373
children e00d3351b912
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);
}

// 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;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;
}