X-Git-Url: http://git.asbjorn.biz/?a=blobdiff_plain;f=lib%2Fexample%2Favi2swf.cc;h=6825217f6d7470f03c4597627c1645a618fdf5ee;hb=59d9bd4c09c2cce20acc37e22a2dc189fb3c17cc;hp=7f3ad62756008bea2f0cb841913a17505694ed25;hpb=10feac6a669258d489cbfd21a2842359093ca687;p=swftools.git diff --git a/lib/example/avi2swf.cc b/lib/example/avi2swf.cc index 7f3ad62..6825217 100644 --- a/lib/example/avi2swf.cc +++ b/lib/example/avi2swf.cc @@ -18,13 +18,39 @@ extern "C" { #include "avifile.h" #include "aviplay.h" +/* + 37 bytes per shape (rectangle) + 8-12 bytes per placeobject + 4 bytes per removeobject2 + 696+ bytes per definejpeg2 (576 jpegtables) + 576 bytes per jpegtables + 122+ bytes per definejpeg + + blocks*140 = minimal bytes per frames + 5000/140 = maximal blocks with framerate 5000 + + 2 bytes per showframe +*/ + +int cache_size=38; //in frames + char * filename = 0; char * outputfilename = "output.swf"; +unsigned int firstframe = 0; +unsigned int lastframe = 0x7fffffff; + +int jpeg_quality = 20; + +#ifndef ST_DEFINEBITSJPEG +#define ST_DEFINEBITSJPEG 6 +#endif struct options_t options[] = { {"v","verbose"}, {"o","output"}, + {"n","num"}, + {"s","start"}, {"V","version"}, {0,0} }; @@ -34,11 +60,19 @@ int args_callback_option(char*name,char*val) if(!strcmp(name, "V")) { printf("avi2swf - part of %s %s\n", PACKAGE, VERSION); exit(0); - } else - if(!strcmp(name, "o")) { + } + else if(!strcmp(name, "o")) { outputfilename = val; return 1; } + else if(!strcmp(name, "n")) { + lastframe = atoi(val); + return 1; + } + else if(!strcmp(name, "s")) { + firstframe = atoi(val); + return 1; + } } int args_callback_longoption(char*name,char*val) { @@ -49,6 +83,8 @@ void args_callback_usage(char*name) printf("\nUsage: %s file.swf\n", name); printf("\t-h , --help\t\t Print help and exit\n"); printf("\t-o , --output=filename\t Specify output filename\n"); + printf("\t-n , --num=frames\t\t Number of frames to encode\n"); + printf("\t-s , --start=frame\t\t First frame to encode\n"); printf("\t-V , --version\t\t Print program version and exit\n"); exit(0); } @@ -62,22 +98,361 @@ int args_callback_command(char*name,char*val) return 0; } +/* id allocation/deallocation */ +char idtab[65536]; +unsigned int idtab_pos=1; +int get_free_id() +{ + while(idtab[idtab_pos] || !idtab_pos) + idtab_pos++; + idtab[idtab_pos]=1; + return idtab_pos; +} +void free_id(int id) +{ + idtab[id] = 0; +} + +void makeshape(int file, int id, int gfxid, int width, int height) +{ + TAG*tag; + RGBA rgb; + MATRIX m; + SHAPE*s; + SRECT r; + int lines = 0; + int ls,fs; + tag = swf_InsertTag(NULL, ST_DEFINESHAPE); + swf_ShapeNew(&s); + rgb.b = rgb.g = rgb.r = 0xff; + if(lines) + ls = swf_ShapeAddLineStyle(s,20,&rgb); + swf_GetMatrix(NULL,&m); + m.sx = 20*65536; + m.sy = 20*65536; + + fs = swf_ShapeAddBitmapFillStyle(s,&m,gfxid,0); + swf_SetU16(tag,id); // ID + r.xmin = 0; + r.ymin = 0; + r.xmax = width*20; + r.ymax = height*20; + swf_SetRect(tag,&r); + + swf_SetShapeStyles(tag,s); + swf_ShapeCountBits(s,NULL,NULL); + swf_SetShapeBits(tag,s); + + swf_ShapeSetAll(tag,s,0,0,lines?ls:0,fs,0); + + swf_ShapeSetLine(tag,s,width*20,0); + swf_ShapeSetLine(tag,s,0,height*20); + swf_ShapeSetLine(tag,s,-width*20,0); + swf_ShapeSetLine(tag,s,0,-height*20); + swf_ShapeSetEnd(tag); + swf_WriteTag(file, tag); + swf_DeleteTag(tag); + swf_ShapeFree(s); +} + +void setshape(int file,int id,int depth,int x,int y,CXFORM*cx) +{ + TAG*tag; + MATRIX m; + m.sx = 0x10000; m.sy = 0x10000; + m.r0 = 0; m.r1 = 0; + m.tx = x*20; + m.ty = y*20; + if(cx && !((cx->a0!=256)||(cx->r0!=256)||(cx->g0!=256)||(cx->b0!=256) + ||(cx->a1|cx->r1|cx->g1|cx->b1))) cx = 0; + tag = swf_InsertTag(NULL,ST_PLACEOBJECT2); + swf_ObjectPlace(tag,id,depth,&m,cx,0); + swf_WriteTag(file, tag); + swf_DeleteTag(tag); +} + + +int xblocksize; +int yblocksize; +struct GfxBlock { +// static int xblocksize; +// static int yblocksize; + U8*data; + int len; +}; + +int width=0; +int height=0; + +int xblocks; +int yblocks; + +U8* blockbuffer = 0; + +class GfxBlockCache { + + GfxBlock*list; + char*expire; //0=block's free + int*ids; + int size; + int pos; + int hits; + int misses; + + public: + + GfxBlockCache(int file) + { + list=0; + size = xblocks*yblocks*cache_size; + printf("initializing cache (%d entries)\n", size); + list = new GfxBlock[size]; + expire = new char[size]; + ids = new int[size]; + memset(expire,0,size); + memset(list,0,sizeof(GfxBlock)*size); + memset(ids,0,sizeof(int)*size); + pos = 0; + hits =0; + misses =0; + } + void insert(GfxBlock*block, int gfxid) + { + int oldpos = pos; + while(++pos!=oldpos) + { + if(pos==size) pos=0; + if(!expire[pos]) + break; + } + if(pos==oldpos) { + // cache full- don't insert item + return; + } + if(list[pos].data) { + free(list[pos].data); + list[pos].data = 0; + //TODO: free this in the SWF, also + } + list[pos].data=(U8*)malloc(block->len); + memcpy(list[pos].data,block->data,block->len); + list[pos].len = block->len; + expire[pos] = cache_size; + ids[pos] = gfxid; + } + int find(GfxBlock*block, CXFORM*cxform) + { + //TODO: do least square regression here to derive cxform + int s; + int bestsum=-1; + int bestid; + float best; + for(s=0;slen); + U8*ptr1 = block->data; + U8*ptr2 = list[s].data; + int sum2 = 0; + // notice: we treat r,g,b as equal here. + do { + int a = (*ptr1++)-(*ptr2++); + sum2 += a*a; + } while(--t); + if(bestsum < 0 || bestsum > sum2) { + bestid = s; + bestsum = sum2; + } + } + if(bestsum<0) { + misses++; + return -1; + } + best = bestsum/block->len; + + if(best > 96.0) { + misses++; + return -1; + } + expire[bestid]= cache_size; + hits++; + cxform->a0 = 256; + cxform->r0 = 256; + cxform->g0 = 256; + cxform->b0 = 256; + cxform->a1 = 0; + cxform->r1 = 0; + cxform->g1 = 0; + cxform->b1 = 0; + return ids[bestid]; + } + void newframe() + { + int t; + for(t=0;tbasedepth = depth; + this->posx = posx; + this->posy = posy; + this->sizex = sizex; + this->sizey = sizey; + this->depth[0] = this->depth[1] = this->depth[2] = -1; + } + void clear(int file) + { + /* clear everything in the block */ + int t; + for(t=0;t<3;t++) + if(depth[t]>=0) + { + TAG*tag; + tag = swf_InsertTag(NULL, ST_REMOVEOBJECT2); + swf_SetU16(tag, basedepth+t); //depth + swf_WriteTag(file, tag); + swf_DeleteTag(tag); + depth[t] = -1; + } + } + void writeiframe(int file, GfxBlock*block) + { + clear(file); + + int gfxid = get_free_id(); + int shapeid = get_free_id(); + + //memset(data,0,sizex*sizey*3); + TAG*tag = swf_InsertTag(NULL, ST_DEFINEBITS); + JPEGBITS * jb = swf_SetJPEGBitsStart(tag,sizex,sizey,jpeg_quality); + tag->len = 0; //bad hack + swf_SetU16(tag, gfxid); + int y; + for(y=0;ydata[y*sizex*3]); + swf_SetJPEGBitsFinish(jb); + swf_WriteTag(file, tag); + swf_DeleteTag(tag); + + cache->insert(block, shapeid); + + makeshape(file, shapeid, gfxid, sizex, sizey); + setshape(file, shapeid, basedepth+1, posx, posy, 0); + depth[1] = shapeid; + } + void writereference(int file, int shapeid, CXFORM*form) + { + if(depth[1]!=shapeid) + { + clear(file); + setshape(file, shapeid, basedepth+1, posx, posy, form); + depth[1] = shapeid; + } + } + void compress(int file, GfxBlock*block) + { + CXFORM form; + int id = cache->find(block, &form); + if(id<0) + writeiframe(file, block); + else { + writereference(file, id, &form); + } + } +} *blocks = 0; + +void initdisplay(int file) +{ + if(blockbuffer) + free(blockbuffer); + if(blocks) { + int t; + for(t=0;tGetFileHeader(&head); printf("fps: %d\n", 1000000/head.dwMicroSecPerFrame); @@ -91,186 +466,204 @@ int main (int argc,char ** argv) vstream = player->GetStream(0, AviStream::Video); vstream -> StartStreaming(); + astream -> StartStreaming(); + + width = head.dwWidth; + height = head.dwHeight; + + printf("sound: %u samples (%f seconds)\n", astream->GetEndPos(), + astream->GetEndTime()); + samplesperframe = astream->GetEndPos()/astream->GetEndTime()*head.dwMicroSecPerFrame/1000000; + printf("%f samples/frame\n", samplesperframe); + samplerate = (int)(astream->GetEndPos()/astream->GetEndTime()); + printf("%d samplerate\n", samplerate); + samplefix = 44100/samplerate; + + if(!samplefix) { + printf("samplerate too high!\n"); + return 0; + } + printf("%d mp3 samples per movie sample\n", samplefix); + + file = open(outputfilename,O_WRONLY|O_CREAT|O_TRUNC, 0644); + memset(&swf, 0, sizeof(swf)); swf.frameRate = (int)(1000000.0/head.dwMicroSecPerFrame*256); swf.fileVersion = 4; + swf.fileSize = 476549;//0x0fffffff; + swf.frameCount = lastframe - firstframe; r.xmin = 0; r.ymin = 0; - r.xmax = head.dwWidth*20; - r.ymax = head.dwHeight*20; + r.xmax = width*20; + r.ymax = height*20; swf.movieSize = r; + + swf_WriteHeader(file, &swf); + tag = swf_InsertTag(NULL, ST_SETBACKGROUNDCOLOR); - swf.firstTag = tag; swf_SetU8(tag,0); //black swf_SetU8(tag,0); swf_SetU8(tag,0); + swf_WriteTag(file, tag); + swf_DeleteTag(tag); - U8*newdata = (U8*)malloc((head.dwWidth+3) * head.dwHeight * 4); + tag = swf_InsertTag(NULL, ST_SOUNDSTREAMHEAD2); + swf_SetSoundStreamHead(tag, 1152); + swf_WriteTag(file, tag); + swf_DeleteTag(tag); int frame = 0; + initdisplay(file); + + int mp3_block_size = 1152; + + int bufsize = mp3_block_size; + if(mp3_block_size < (int)(samplesperframe+1)) + bufsize = (int)(samplesperframe + 1); + unsigned char*buffer = (unsigned char*)malloc(bufsize); + short*block = (short*)malloc(bufsize*2*samplefix); + + unsigned samples_read, bytes_read; - int lastsize = (head.dwWidth+3) * head.dwHeight * 4; - U8* lastdata = (U8*)malloc(lastsize); - U8* data; - memset(lastdata,0, lastsize); + double movie_sound_pos = 0; + int mp3_sound_pos = 0; + + WAVEFORMATEX wave; + astream->GetAudioFormatInfo(&wave,0); + + printf("nChannels:%d\n", wave.nChannels); + printf("nSamplesPerSec:%d\n", wave.nChannels); + printf("nAvgBytesPerSec:%d\n", wave.nAvgBytesPerSec); + printf("nBlockAlign:%d\n", wave.nBlockAlign); + printf("wBitsPerSample:%d\n", wave.wBitsPerSample); + printf("cbSize:%d\n", wave.cbSize); while(1) { if(vstream->ReadFrame()<0) { printf("\n"); break; } + + if(frame < firstframe) + { + if(astream->ReadFrames(buffer, bufsize, + (int)samplesperframe, + samples_read, bytes_read)<0) { + printf("\n"); + break; + }; + printf("\rskipping frame %d",frame); + fflush(stdout); + frame++; + if(frame == firstframe) + printf("\n"); + continue; + } + printf("\rconvert frame %d",frame); fflush(stdout); + + // audio + movie_sound_pos += samplesperframe; + + int first=1; + while(mp3_sound_posReadFrames(buffer, bufsize, + mp3_block_size/samplefix, + samples_read, bytes_read)<0) { + printf("couldn't read %d samples\n", mp3_block_size); + break; + }; + int t=0; + int s; + int c=0; + for(s=0;s=movie_sound_pos) { // last run + swf_WriteTag(file, tag); + swf_DeleteTag(tag); + } + first = 0; + } + + // video + CImage*img = vstream->GetFrame(); img->ToRGB(); - data = img->data(); - int width = img->width(); + U8*data = img->data(); int bpp = img->bpp(); - int width4 = width*4; - int height = img->height(); int x,y; + int xx,yy; int fs,ls; SHAPE*s; MATRIX m; SRECT r; RGBA rgb; - if(frame!=0) { - tag = swf_InsertTag(tag, ST_REMOVEOBJECT2); - swf_SetU16(tag, 1); //depth + /* some movies have changing dimensions */ + if(img->width() != width || + img->height() != height) { + printf("\n"); + width = img->width(); + height = img->height(); + initdisplay(file); } - /* todo: dynamically decide whether to generate jpeg/lossless - bitmaps, (using transparency to modify the previous - picture), and which jpeg compression depth to use. - (btw: Are there video frame transitions which can - reasonably approximated by shapes?) - */ - - int type = 1; - int rel = 0; - if(type == 0) { - tag = swf_InsertTag(tag, ST_DEFINEBITSLOSSLESS); - swf_SetU16(tag, frame*2); - U8*mylastdata = lastdata; - for(y=0;yat(y); - if(!rel) - for(x=0;x64) - { - nd[3]=mydata[0]; - nd[2]=mydata[1]; - nd[1]=mydata[2]; - nd[0]=255; - } else { - nd[3]=0; - nd[2]=0; - nd[1]=0; - nd[0]=0; - } - mylastdata[2] = mydata[2]; - mylastdata[1] = mydata[1]; - mylastdata[0] = mydata[0]; - nd+=4; - mydata+=3; - mylastdata+=3; + for(yy=0;yyat(yy*yblocksize+y); + for(x=0;xat(y); - for(x=0;xnewframe(); - swf_ShapeSetLine(tag,s,width*20,0); - swf_ShapeSetLine(tag,s,0,height*20); - swf_ShapeSetLine(tag,s,-width*20,0); - swf_ShapeSetLine(tag,s,0,-height*20); - swf_ShapeSetEnd(tag); - - tag = swf_InsertTag(tag,ST_PLACEOBJECT2); - swf_ObjectPlace(tag,frame*2+1,1,0,0,0); - - tag = swf_InsertTag(tag, ST_SHOWFRAME); - -/* frame++; - if(frame == 200) - break;*/ + frame++; + if(frame == lastframe) + break; } - free(newdata); - - tag = swf_InsertTag(tag, ST_END); - - f = open(outputfilename,O_WRONLY|O_CREAT|O_TRUNC, 0644); - if FAILED(swf_WriteSWF(f,&swf)) fprintf(stderr,"WriteSWF() failed.\n"); - close(f); + printf("\n"); + destroydisplay(file); - swf_FreeTags(&swf); // cleanup + printf("mp3 samples read:%d\n", mp3_sound_pos); + printf("mp3 samples read:%d\n", mp3_sound_pos); + + tag = swf_InsertTag(NULL, ST_END); + swf_WriteTag(file, tag); + swf_DeleteTag(tag); + close(file); + return 0; }