mirror of
https://github.com/mpv-player/mpv
synced 2025-01-24 19:37:30 +01:00
AVI muxer rewritten
git-svn-id: svn://svn.mplayerhq.hu/mplayer/trunk@2530 b3059339-0415-0410-9bf9-f77b7e298cf2
This commit is contained in:
parent
3e59e2495c
commit
6320e46eb7
@ -1,17 +1,56 @@
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
|
||||
//#include "stream.h"
|
||||
//#include "demuxer.h"
|
||||
//#include "stheader.h"
|
||||
|
||||
#include "wine/mmreg.h"
|
||||
#include "wine/avifmt.h"
|
||||
#include "wine/vfw.h"
|
||||
|
||||
extern char* encode_name;
|
||||
extern char* encode_index_name;
|
||||
#include "aviwrite.h"
|
||||
|
||||
void write_avi_chunk(FILE *f,unsigned int id,int len,void* data){
|
||||
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;
|
||||
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){
|
||||
fwrite(&id,4,1,f);
|
||||
fwrite(&len,4,1,f);
|
||||
if(len>0){
|
||||
@ -34,11 +73,39 @@ if(len>0){
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
// 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;
|
||||
if(len>s->h.dwSuggestedBufferSize) s->h.dwSuggestedBufferSize=len;
|
||||
|
||||
}
|
||||
|
||||
|
||||
void write_avi_list(FILE *f,unsigned int id,int len){
|
||||
static void write_avi_list(FILE *f,unsigned int id,int len){
|
||||
unsigned int list_id=FOURCC_LIST;
|
||||
len+=4; // list fix
|
||||
fwrite(&list_id,4,1,f);
|
||||
@ -46,127 +113,86 @@ void write_avi_list(FILE *f,unsigned int id,int len){
|
||||
fwrite(&id,4,1,f);
|
||||
}
|
||||
|
||||
struct {
|
||||
MainAVIHeader avih;
|
||||
AVIStreamHeader video;
|
||||
BITMAPINFOHEADER bih;
|
||||
unsigned int movi_start;
|
||||
unsigned int movi_end;
|
||||
unsigned int file_end;
|
||||
} wah;
|
||||
|
||||
void write_avi_header(FILE *f){
|
||||
void aviwrite_write_header(aviwrite_t *muxer,FILE *f){
|
||||
unsigned int riff[3];
|
||||
int i;
|
||||
unsigned int hdrsize;
|
||||
// RIFF header:
|
||||
riff[0]=mmioFOURCC('R','I','F','F');
|
||||
riff[1]=wah.file_end; // filesize
|
||||
riff[1]=muxer->file_end; // filesize
|
||||
riff[2]=formtypeAVI; // 'AVI '
|
||||
fwrite(&riff,12,1,f);
|
||||
// AVI header:
|
||||
write_avi_list(f,listtypeAVIHEADER,sizeof(wah.avih)+8+12+sizeof(wah.video)+8+sizeof(wah.bih)+8);
|
||||
write_avi_chunk(f,ckidAVIMAINHDR,sizeof(wah.avih),&wah.avih);
|
||||
// stream header:
|
||||
write_avi_list(f,listtypeSTREAMHEADER,sizeof(wah.video)+8+sizeof(wah.bih)+8);
|
||||
write_avi_chunk(f,ckidSTREAMHEADER,sizeof(wah.video),&wah.video);
|
||||
write_avi_chunk(f,ckidSTREAMFORMAT,sizeof(wah.bih),&wah.bih);
|
||||
// JUNK:
|
||||
write_avi_chunk(f,ckidAVIPADDING,2048-(ftell(f)&2047)-8,NULL);
|
||||
// 'movi' header:
|
||||
write_avi_list(f,listtypeAVIMOVIE,wah.movi_end-ftell(f)-12);
|
||||
wah.movi_start=ftell(f);
|
||||
}
|
||||
|
||||
// called _before_ encoding: (write placeholders and video info)
|
||||
void write_avi_header_1(FILE *f,int fcc,float fps,int width,int height){
|
||||
int frames=8*3600*fps; // 8 hours
|
||||
|
||||
wah.file_end=
|
||||
wah.movi_end=0x7f000000;
|
||||
|
||||
wah.avih.dwMicroSecPerFrame=1000000.0f/fps;
|
||||
wah.avih.dwMaxBytesPerSec=fps*500000; // ?????
|
||||
wah.avih.dwPaddingGranularity=1; // padding
|
||||
wah.avih.dwFlags=AVIF_ISINTERLEAVED;
|
||||
wah.avih.dwTotalFrames=frames;
|
||||
wah.avih.dwInitialFrames=0;
|
||||
wah.avih.dwStreams=1;
|
||||
wah.avih.dwSuggestedBufferSize=0x10000; // 1MB
|
||||
wah.avih.dwWidth=width;
|
||||
wah.avih.dwHeight=height;
|
||||
wah.avih.dwReserved[0]=
|
||||
wah.avih.dwReserved[1]=
|
||||
wah.avih.dwReserved[2]=
|
||||
wah.avih.dwReserved[3]=0;
|
||||
|
||||
wah.video.fccType=streamtypeVIDEO;
|
||||
wah.video.fccHandler=fcc;
|
||||
wah.video.dwFlags=0;
|
||||
wah.video.wPriority=0;
|
||||
wah.video.wLanguage=0;
|
||||
wah.video.dwInitialFrames=0;
|
||||
wah.video.dwScale=10000;
|
||||
wah.video.dwRate=fps*10000;
|
||||
wah.video.dwStart=0;
|
||||
wah.video.dwLength=frames;
|
||||
wah.video.dwSuggestedBufferSize=0x100000; // 1MB ????
|
||||
wah.video.dwQuality=10000;
|
||||
wah.video.dwSampleSize=width*height*3;
|
||||
|
||||
wah.bih.biSize=sizeof(wah.bih); // 40 ?
|
||||
wah.bih.biWidth=width;
|
||||
wah.bih.biHeight=height;
|
||||
wah.bih.biPlanes=1;
|
||||
wah.bih.biBitCount=24;
|
||||
wah.bih.biCompression=fcc;
|
||||
wah.bih.biSizeImage=3*width*height;
|
||||
wah.bih.biXPelsPerMeter=
|
||||
wah.bih.biYPelsPerMeter=
|
||||
wah.bih.biClrUsed=
|
||||
wah.bih.biClrImportant=0;
|
||||
|
||||
write_avi_header(f);
|
||||
}
|
||||
|
||||
void avi_fixate(){
|
||||
// append index and fix avi headers:
|
||||
FILE *f1=fopen(encode_name,"r+");
|
||||
FILE *f2;
|
||||
|
||||
if(!f1) return; // error
|
||||
|
||||
fseek(f1,0,SEEK_END);
|
||||
wah.file_end=wah.movi_end=ftell(f1);
|
||||
|
||||
// index:
|
||||
if(encode_index_name && (f2=fopen(encode_index_name,"rb"))){
|
||||
AVIINDEXENTRY idx;
|
||||
unsigned int pos=0;
|
||||
int frames=0;
|
||||
write_avi_chunk(f1,ckidAVINEWINDEX,0,NULL);
|
||||
while(fread(&idx,sizeof(idx),1,f2)>0){
|
||||
idx.dwChunkOffset-=wah.movi_start-4;
|
||||
fwrite(&idx,sizeof(idx),1,f1);
|
||||
++frames;
|
||||
}
|
||||
fclose(f2);
|
||||
unlink(encode_index_name);
|
||||
wah.file_end=ftell(f1);
|
||||
// re-write idx1 length:
|
||||
pos=wah.file_end-wah.movi_end-8;
|
||||
fseek(f1,wah.movi_end+4,SEEK_SET);
|
||||
fwrite(&pos,4,1,f1);
|
||||
// fixup frames:
|
||||
wah.avih.dwTotalFrames=frames;
|
||||
wah.video.dwLength=frames;
|
||||
// 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;
|
||||
}
|
||||
|
||||
// re-write avi header:
|
||||
fseek(f1,0,SEEK_SET);
|
||||
write_avi_header(f1);
|
||||
// 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 AVIWRITE_TYPE_VIDEO:
|
||||
hdrsize+=muxer->streams[i]->bih->biSize+8; // strf
|
||||
break;
|
||||
case AVIWRITE_TYPE_AUDIO:
|
||||
hdrsize+=sizeof(WAVEFORMATEX)+muxer->streams[i]->wf->cbSize+8; // strf
|
||||
break;
|
||||
}
|
||||
}
|
||||
write_avi_list(f,listtypeAVIHEADER,hdrsize);
|
||||
write_avi_chunk(f,ckidAVIMAINHDR,sizeof(muxer->avih),&muxer->avih);
|
||||
|
||||
fclose(f1);
|
||||
|
||||
// stream headers:
|
||||
for(i=0;i<muxer->avih.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+=sizeof(WAVEFORMATEX)+muxer->streams[i]->wf->cbSize+8; // strf
|
||||
break;
|
||||
}
|
||||
write_avi_list(f,listtypeSTREAMHEADER,hdrsize);
|
||||
write_avi_chunk(f,ckidSTREAMHEADER,sizeof(muxer->streams[i]->h),&muxer->streams[i]->h); // strh
|
||||
switch(muxer->streams[i]->type){
|
||||
case AVIWRITE_TYPE_VIDEO:
|
||||
write_avi_chunk(f,ckidSTREAMFORMAT,muxer->streams[i]->bih->biSize,muxer->streams[i]->bih);
|
||||
break;
|
||||
case AVIWRITE_TYPE_AUDIO:
|
||||
write_avi_chunk(f,ckidSTREAMFORMAT,sizeof(WAVEFORMATEX)+muxer->streams[i]->wf->cbSize,muxer->streams[i]->wf);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// JUNK:
|
||||
write_avi_chunk(f,ckidAVIPADDING,2048-(ftell(f)&2047)-8,NULL);
|
||||
// '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){
|
||||
// fixup index entries:
|
||||
// int i;
|
||||
// for(i=0;i<muxer->idx_pos;i++) muxer->idx[i].dwChunkOffset-=muxer->movi_start-4;
|
||||
// write index chunk:
|
||||
write_avi_chunk(f,ckidAVINEWINDEX,16*muxer->idx_pos,muxer->idx);
|
||||
muxer->avih.dwFlags|=AVIF_HASINDEX;
|
||||
}
|
||||
muxer->file_end=ftell(f);
|
||||
}
|
||||
|
||||
|
49
libmpdemux/aviwrite.h
Normal file
49
libmpdemux/aviwrite.h
Normal file
@ -0,0 +1,49 @@
|
||||
|
||||
#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;
|
||||
// buffering:
|
||||
unsigned char *buffer;
|
||||
unsigned int buffer_size;
|
||||
// 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 {
|
||||
// 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);
|
||||
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user