2 Convert avi movie files into swf.
3 As soon as there's an algorithm implemented for writing the
4 data directly to disk, this file should maybe go to ../src.
6 Part of the swftools package.
8 Copyright (c) 2001 Matthias Kramm <kramm@quiss.org>
10 This file is distributed under the GPL, see file COPYING for details */
15 #include "../rfxswf.h"
22 37 bytes per shape (rectangle)
23 8-12 bytes per placeobject
24 4 bytes per removeobject2
25 696+ bytes per definejpeg2 (576 jpegtables)
26 576 bytes per jpegtables
27 122+ bytes per definejpeg
29 blocks*140 = minimal bytes per frames
30 5000/140 = maximal blocks with framerate 5000
35 int cache_size=38; //in frames
38 char * outputfilename = "output.swf";
39 unsigned int firstframe = 0;
40 unsigned int lastframe = 0x7fffffff;
42 int jpeg_quality = 20;
44 #ifndef ST_DEFINEBITSJPEG
45 #define ST_DEFINEBITSJPEG 6
48 struct options_t options[] =
58 int args_callback_option(char*name,char*val)
60 if(!strcmp(name, "V")) {
61 printf("avi2swf - part of %s %s\n", PACKAGE, VERSION);
64 else if(!strcmp(name, "o")) {
68 else if(!strcmp(name, "n")) {
69 lastframe = atoi(val);
72 else if(!strcmp(name, "s")) {
73 firstframe = atoi(val);
77 int args_callback_longoption(char*name,char*val)
79 return args_long2shortoption(options, name, val);
81 void args_callback_usage(char*name)
83 printf("\nUsage: %s file.swf\n", name);
84 printf("\t-h , --help\t\t Print help and exit\n");
85 printf("\t-o , --output=filename\t Specify output filename\n");
86 printf("\t-n , --num=frames\t\t Number of frames to encode\n");
87 printf("\t-s , --start=frame\t\t First frame to encode\n");
88 printf("\t-V , --version\t\t Print program version and exit\n");
91 int args_callback_command(char*name,char*val)
94 fprintf(stderr, "Only one file allowed. You supplied at least two. (%s and %s)\n",
101 /* id allocation/deallocation */
103 unsigned int idtab_pos=1;
106 while(idtab[idtab_pos] || !idtab_pos)
116 void makeshape(int file, int id, int gfxid, int width, int height)
125 tag = swf_InsertTag(NULL, ST_DEFINESHAPE);
127 rgb.b = rgb.g = rgb.r = 0xff;
129 ls = swf_ShapeAddLineStyle(s,20,&rgb);
130 swf_GetMatrix(NULL,&m);
134 fs = swf_ShapeAddBitmapFillStyle(s,&m,gfxid,0);
135 swf_SetU16(tag,id); // ID
142 swf_SetShapeStyles(tag,s);
143 swf_ShapeCountBits(s,NULL,NULL);
144 swf_SetShapeBits(tag,s);
146 swf_ShapeSetAll(tag,s,0,0,lines?ls:0,fs,0);
148 swf_ShapeSetLine(tag,s,width*20,0);
149 swf_ShapeSetLine(tag,s,0,height*20);
150 swf_ShapeSetLine(tag,s,-width*20,0);
151 swf_ShapeSetLine(tag,s,0,-height*20);
152 swf_ShapeSetEnd(tag);
153 swf_WriteTag(file, tag);
158 void setshape(int file,int id,int depth,int x,int y,CXFORM*cx)
162 m.sx = 0x10000; m.sy = 0x10000;
166 if(cx && !((cx->a0!=256)||(cx->r0!=256)||(cx->g0!=256)||(cx->b0!=256)
167 ||(cx->a1|cx->r1|cx->g1|cx->b1))) cx = 0;
168 tag = swf_InsertTag(NULL,ST_PLACEOBJECT2);
169 swf_ObjectPlace(tag,id,depth,&m,cx,0);
170 swf_WriteTag(file, tag);
178 // static int xblocksize;
179 // static int yblocksize;
192 class GfxBlockCache {
195 char*expire; //0=block's free
204 GfxBlockCache(int file)
207 size = xblocks*yblocks*cache_size;
208 printf("initializing cache (%d entries)\n", size);
209 list = new GfxBlock[size];
210 expire = new char[size];
212 memset(expire,0,size);
213 memset(list,0,sizeof(GfxBlock)*size);
214 memset(ids,0,sizeof(int)*size);
219 void insert(GfxBlock*block, int gfxid)
229 // cache full- don't insert item
233 free(list[pos].data);
235 //TODO: free this in the SWF, also
237 list[pos].data=(U8*)malloc(block->len);
238 memcpy(list[pos].data,block->data,block->len);
239 list[pos].len = block->len;
240 expire[pos] = cache_size;
243 int find(GfxBlock*block, CXFORM*cxform)
245 //TODO: do least square regression here to derive cxform
253 int t = (block->len);
254 U8*ptr1 = block->data;
255 U8*ptr2 = list[s].data;
257 // notice: we treat r,g,b as equal here.
259 int a = (*ptr1++)-(*ptr2++);
262 if(bestsum < 0 || bestsum > sum2) {
271 best = bestsum/block->len;
277 expire[bestid]= cache_size;
300 printf("destroying cache...\n");
301 printf("hits:%d (%02d%%)\n", hits, hits*100/(hits+misses));
302 printf("misses:%d (%02d%%)\n", misses, misses*100/(hits+misses));
304 if(expire[t] && list[t].data)
312 class GfxBlockEncoder {
320 void init(int depth, int posx,int posy, int sizex, int sizey)
322 this->basedepth = depth;
327 this->depth[0] = this->depth[1] = this->depth[2] = -1;
331 /* clear everything in the block */
337 tag = swf_InsertTag(NULL, ST_REMOVEOBJECT2);
338 swf_SetU16(tag, basedepth+t); //depth
339 swf_WriteTag(file, tag);
344 void writeiframe(int file, GfxBlock*block)
348 int gfxid = get_free_id();
349 int shapeid = get_free_id();
351 //memset(data,0,sizex*sizey*3);
352 TAG*tag = swf_InsertTag(NULL, ST_DEFINEBITS);
353 JPEGBITS * jb = swf_SetJPEGBitsStart(tag,sizex,sizey,jpeg_quality);
354 tag->len = 0; //bad hack
355 swf_SetU16(tag, gfxid);
358 swf_SetJPEGBitsLine(jb,&block->data[y*sizex*3]);
359 swf_SetJPEGBitsFinish(jb);
360 swf_WriteTag(file, tag);
363 cache->insert(block, shapeid);
365 makeshape(file, shapeid, gfxid, sizex, sizey);
366 setshape(file, shapeid, basedepth+1, posx, posy, 0);
369 void writereference(int file, int shapeid, CXFORM*form)
371 if(depth[1]!=shapeid)
374 setshape(file, shapeid, basedepth+1, posx, posy, form);
378 void compress(int file, GfxBlock*block)
381 int id = cache->find(block, &form);
383 writeiframe(file, block);
385 writereference(file, id, &form);
390 void initdisplay(int file)
396 for(t=0;t<xblocks;t++)
397 blocks[t].clear(file);
402 xblocksize = (width/3)&~7;
403 yblocksize = (height/2)&~7;
404 xblocks = width/xblocksize;
405 yblocks = height/yblocksize;
406 printf("%dx%d blocks of size %dx%d\n", xblocks,yblocks, xblocksize, yblocksize);
407 printf("cutting lower %d lines, right %d columns\n",
408 height-yblocks*yblocksize, width-xblocks*xblocksize);
409 blocks = new GfxBlockEncoder[xblocks*yblocks];
410 blockbuffer = new U8[xblocksize*yblocksize*4]; //should be 3
411 cache = new GfxBlockCache(file);
413 for(t=0;t<xblocks*yblocks;t++) {
415 (t%xblocks)*xblocksize,
416 (t/xblocks)*yblocksize,
417 xblocksize, yblocksize);
420 TAG*tag = swf_InsertTag(NULL, ST_JPEGTABLES);
421 JPEGBITS * jpeg = swf_SetJPEGBitsStart(tag, xblocksize, yblocksize, jpeg_quality);
422 swf_WriteTag(file, tag);
427 void destroydisplay(int file)
437 int main (int argc,char ** argv)
440 IAviReadFile* player;
441 IAviReadStream* astream;
442 IAviReadStream* vstream;
446 processargs(argc, argv);
447 lastframe += firstframe;
451 memset(idtab, 0, sizeof(idtab));
453 player = CreateIAviReadFile(filename);
454 player->GetFileHeader(&head);
455 printf("fps: %d\n", 1000000/head.dwMicroSecPerFrame);
456 printf("frames: %d\n", head.dwTotalFrames);
457 printf("streams: %d\n", head.dwStreams);
458 printf("streams: %d\n", player->StreamCount());
459 printf("width: %d\n", head.dwWidth);
460 printf("height: %d\n", head.dwHeight);
462 astream = player->GetStream(0, AviStream::Audio);
463 vstream = player->GetStream(0, AviStream::Video);
465 vstream -> StartStreaming();
467 width = head.dwWidth;
468 height = head.dwHeight;
470 file = open(outputfilename,O_WRONLY|O_CREAT|O_TRUNC, 0644);
472 memset(&swf, 0, sizeof(swf));
473 swf.frameRate = (int)(1000000.0/head.dwMicroSecPerFrame*256);
475 swf.fileSize = 0x0fffffff;
482 swf_WriteHeader(file, &swf);
484 tag = swf_InsertTag(NULL, ST_SETBACKGROUNDCOLOR);
485 swf_SetU8(tag,0); //black
488 swf_WriteTag(file, tag);
494 while(frame<firstframe) {
495 if(vstream->ReadFrame()<0)
497 printf("\rskipping frame %d",frame);
504 if(vstream->ReadFrame()<0) {
508 printf("\rconvert frame %d",frame);
510 CImage*img = vstream->GetFrame();
512 U8*data = img->data();
513 int bpp = img->bpp();
522 /* some movies have changing dimensions */
523 if(img->width() != width ||
524 img->height() != height) {
526 width = img->width();
527 height = img->height();
531 for(yy=0;yy<yblocks;yy++)
532 for(xx=0;xx<xblocks;xx++)
535 for(y=0;y<yblocksize;y++) {
536 U8*mydata = img->at(yy*yblocksize+y);
537 for(x=0;x<xblocksize;x++) {
538 blockbuffer[(y*xblocksize+x)*3+2] = mydata[(xx*xblocksize+x)*3+0];
539 blockbuffer[(y*xblocksize+x)*3+1] = mydata[(xx*xblocksize+x)*3+1];
540 blockbuffer[(y*xblocksize+x)*3+0] = mydata[(xx*xblocksize+x)*3+2];
544 b.data = blockbuffer;
545 b.len = xblocksize*yblocksize*3;
546 blocks[yy*xblocks+xx].compress(file, &b);
549 tag = swf_InsertTag(NULL, ST_SHOWFRAME);
550 swf_WriteTag(file, tag);
556 if(frame == lastframe)
560 destroydisplay(file);
562 tag = swf_InsertTag(NULL, ST_END);
563 swf_WriteTag(file, tag);