view libmpdemux/muxer_avi.c @ 10252:d275152390ee

I've found some time to implement the encoding support for the new DivX API. Now it's possible to play and encode movies with the latest DivX release. One thing that doesn't work is the new Video Buffer Verifier (VBV) multipass encoding. The encoder segfaults. Maybe it just isn't supported with the standard profile of the released binary encoder. Andreas Hess <jaska@gmx.net>
author arpi
date Fri, 06 Jun 2003 19:57:37 +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;
}