2 Convert avi movie files into swf.
3 As soon as the size of the generated swfs is reasonable, this file
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 "../lib/rfxswf.h"
16 #include "../lib/args.h"
23 37 bytes per shape (rectangle)
24 8-12 bytes per placeobject
25 4 bytes per removeobject2
26 696+ bytes per definejpeg2 (576 jpegtables)
27 576 bytes per jpegtables
28 122+ bytes per definejpeg
30 blocks*140 = minimal bytes per frames
31 5000/140 = maximal blocks with framerate 5000
36 int cache_size=38; //in frames
39 char * outputfilename = "output.swf";
40 unsigned int firstframe = 0;
41 unsigned int lastframe = 0x7fffffff;
43 int jpeg_quality = 20;
45 #ifndef ST_DEFINEBITSJPEG
46 #define ST_DEFINEBITSJPEG 6
49 struct options_t options[] =
59 int args_callback_option(char*name,char*val)
61 if(!strcmp(name, "V")) {
62 printf("avi2swf - part of %s %s\n", PACKAGE, VERSION);
65 else if(!strcmp(name, "o")) {
69 else if(!strcmp(name, "n")) {
70 lastframe = atoi(val);
73 else if(!strcmp(name, "s")) {
74 firstframe = atoi(val);
78 int args_callback_longoption(char*name,char*val)
80 return args_long2shortoption(options, name, val);
82 void args_callback_usage(char*name)
84 printf("\nUsage: %s file.swf\n", name);
85 printf("\t-h , --help\t\t Print help and exit\n");
86 printf("\t-o , --output=filename\t Specify output filename\n");
87 printf("\t-n , --num=frames\t\t Number of frames to encode\n");
88 printf("\t-s , --start=frame\t\t First frame to encode\n");
89 printf("\t-V , --version\t\t Print program version and exit\n");
92 int args_callback_command(char*name,char*val)
95 fprintf(stderr, "Only one file allowed. You supplied at least two. (%s and %s)\n",
102 /* id allocation/deallocation */
104 unsigned int idtab_pos=1;
107 while(idtab[idtab_pos] || !idtab_pos)
117 void makeshape(int file, int id, int gfxid, int width, int height)
126 tag = swf_InsertTag(NULL, ST_DEFINESHAPE);
128 rgb.b = rgb.g = rgb.r = 0xff;
130 ls = swf_ShapeAddLineStyle(s,20,&rgb);
131 swf_GetMatrix(NULL,&m);
135 fs = swf_ShapeAddBitmapFillStyle(s,&m,gfxid,0);
136 swf_SetU16(tag,id); // ID
143 swf_SetShapeStyles(tag,s);
144 swf_ShapeCountBits(s,NULL,NULL);
145 swf_SetShapeBits(tag,s);
147 swf_ShapeSetAll(tag,s,0,0,lines?ls:0,fs,0);
149 swf_ShapeSetLine(tag,s,width*20,0);
150 swf_ShapeSetLine(tag,s,0,height*20);
151 swf_ShapeSetLine(tag,s,-width*20,0);
152 swf_ShapeSetLine(tag,s,0,-height*20);
153 swf_ShapeSetEnd(tag);
154 swf_WriteTag(file, tag);
159 void setshape(int file,int id,int depth,int x,int y,CXFORM*cx)
163 m.sx = 0x10000; m.sy = 0x10000;
167 if(cx && !((cx->a0!=256)||(cx->r0!=256)||(cx->g0!=256)||(cx->b0!=256)
168 ||(cx->a1|cx->r1|cx->g1|cx->b1))) cx = 0;
169 tag = swf_InsertTag(NULL,ST_PLACEOBJECT2);
170 swf_ObjectPlace(tag,id,depth,&m,cx,0);
171 swf_WriteTag(file, tag);
179 // static int xblocksize;
180 // static int yblocksize;
193 class GfxBlockCache {
196 char*expire; //0=block's free
205 GfxBlockCache(int file)
208 size = xblocks*yblocks*cache_size;
209 printf("initializing cache (%d entries)\n", size);
210 list = new GfxBlock[size];
211 expire = new char[size];
213 memset(expire,0,size);
214 memset(list,0,sizeof(GfxBlock)*size);
215 memset(ids,0,sizeof(int)*size);
220 void insert(GfxBlock*block, int gfxid)
230 // cache full- don't insert item
234 free(list[pos].data);
236 //TODO: free this in the SWF, also
238 list[pos].data=(U8*)malloc(block->len);
239 memcpy(list[pos].data,block->data,block->len);
240 list[pos].len = block->len;
241 expire[pos] = cache_size;
244 int find(GfxBlock*block, CXFORM*cxform)
246 //TODO: do least square regression here to derive cxform
254 int t = (block->len);
255 U8*ptr1 = block->data;
256 U8*ptr2 = list[s].data;
258 // notice: we treat r,g,b as equal here.
260 int a = (*ptr1++)-(*ptr2++);
263 if(bestsum < 0 || bestsum > sum2) {
272 best = bestsum/block->len;
278 expire[bestid]= cache_size;
301 printf("destroying cache...\n");
302 printf("hits:%d (%02d%%)\n", hits, hits*100/(hits+misses));
303 printf("misses:%d (%02d%%)\n", misses, misses*100/(hits+misses));
305 if(expire[t] && list[t].data)
313 class GfxBlockEncoder {
321 void init(int depth, int posx,int posy, int sizex, int sizey)
323 this->basedepth = depth;
328 this->depth[0] = this->depth[1] = this->depth[2] = -1;
332 /* clear everything in the block */
338 tag = swf_InsertTag(NULL, ST_REMOVEOBJECT2);
339 swf_SetU16(tag, basedepth+t); //depth
340 swf_WriteTag(file, tag);
345 void writeiframe(int file, GfxBlock*block)
349 int gfxid = get_free_id();
350 int shapeid = get_free_id();
352 //memset(data,0,sizex*sizey*3);
353 TAG*tag = swf_InsertTag(NULL, ST_DEFINEBITS);
354 JPEGBITS * jb = swf_SetJPEGBitsStart(tag,sizex,sizey,jpeg_quality);
355 tag->len = 0; //bad hack
356 swf_SetU16(tag, gfxid);
359 swf_SetJPEGBitsLine(jb,&block->data[y*sizex*3]);
360 swf_SetJPEGBitsFinish(jb);
361 swf_WriteTag(file, tag);
364 cache->insert(block, shapeid);
366 makeshape(file, shapeid, gfxid, sizex, sizey);
367 setshape(file, shapeid, basedepth+1, posx, posy, 0);
370 void writereference(int file, int shapeid, CXFORM*form)
372 if(depth[1]!=shapeid)
375 setshape(file, shapeid, basedepth+1, posx, posy, form);
379 void compress(int file, GfxBlock*block)
382 int id = cache->find(block, &form);
384 writeiframe(file, block);
386 writereference(file, id, &form);
391 void initdisplay(int file)
397 for(t=0;t<xblocks;t++)
398 blocks[t].clear(file);
403 xblocksize = (width/3)&~7;
404 yblocksize = (height/2)&~7;
405 xblocks = width/xblocksize;
406 yblocks = height/yblocksize;
407 printf("%dx%d blocks of size %dx%d\n", xblocks,yblocks, xblocksize, yblocksize);
408 printf("cutting lower %d lines, right %d columns\n",
409 height-yblocks*yblocksize, width-xblocks*xblocksize);
410 blocks = new GfxBlockEncoder[xblocks*yblocks];
411 blockbuffer = new U8[xblocksize*yblocksize*4]; //should be 3
412 cache = new GfxBlockCache(file);
414 for(t=0;t<xblocks*yblocks;t++) {
416 (t%xblocks)*xblocksize,
417 (t/xblocks)*yblocksize,
418 xblocksize, yblocksize);
421 TAG*tag = swf_InsertTag(NULL, ST_JPEGTABLES);
422 JPEGBITS * jpeg = swf_SetJPEGBitsStart(tag, xblocksize, yblocksize, jpeg_quality);
423 swf_WriteTag(file, tag);
428 void destroydisplay(int file)
438 int main (int argc,char ** argv)
441 IAviReadFile* player;
442 IAviReadStream* astream;
443 IAviReadStream* vstream;
446 double samplesperframe;
450 processargs(argc, argv);
451 lastframe += firstframe;
455 memset(idtab, 0, sizeof(idtab));
457 player = CreateIAviReadFile(filename);
458 player->GetFileHeader(&head);
459 printf("fps: %d\n", 1000000/head.dwMicroSecPerFrame);
460 printf("frames: %d\n", head.dwTotalFrames);
461 printf("streams: %d\n", head.dwStreams);
462 printf("streams: %d\n", player->StreamCount());
463 printf("width: %d\n", head.dwWidth);
464 printf("height: %d\n", head.dwHeight);
466 astream = player->GetStream(0, AviStream::Audio);
467 vstream = player->GetStream(0, AviStream::Video);
469 vstream -> StartStreaming();
470 astream -> StartStreaming();
472 width = head.dwWidth;
473 height = head.dwHeight;
475 printf("sound: %u samples (%f seconds)\n", astream->GetEndPos(),
476 astream->GetEndTime());
477 samplesperframe = astream->GetEndPos()/astream->GetEndTime()*head.dwMicroSecPerFrame/1000000;
478 printf("%f samples/frame\n", samplesperframe);
479 samplerate = (int)(astream->GetEndPos()/astream->GetEndTime());
480 printf("%d samplerate\n", samplerate);
481 samplefix = 44100/samplerate;
484 printf("samplerate too high!\n");
487 printf("%d mp3 samples per movie sample\n", samplefix);
489 file = open(outputfilename,O_WRONLY|O_CREAT|O_TRUNC, 0644);
491 memset(&swf, 0, sizeof(swf));
492 swf.frameRate = (int)(1000000.0/head.dwMicroSecPerFrame*256);
494 swf.fileSize = 476549;//0x0fffffff;
495 swf.frameCount = lastframe - firstframe;
502 swf_WriteHeader(file, &swf);
504 tag = swf_InsertTag(NULL, ST_SETBACKGROUNDCOLOR);
505 swf_SetU8(tag,0); //black
508 swf_WriteTag(file, tag);
511 tag = swf_InsertTag(NULL, ST_SOUNDSTREAMHEAD2);
512 swf_SetSoundStreamHead(tag, 1152);
513 swf_WriteTag(file, tag);
519 int mp3_block_size = 1152;
521 int bufsize = mp3_block_size;
522 if(mp3_block_size < (int)(samplesperframe+1))
523 bufsize = (int)(samplesperframe + 1);
524 unsigned char*buffer = (unsigned char*)malloc(bufsize);
525 short*block = (short*)malloc(bufsize*2*samplefix);
527 unsigned samples_read, bytes_read;
529 double movie_sound_pos = 0;
530 int mp3_sound_pos = 0;
533 astream->GetAudioFormatInfo(&wave,0);
535 printf("nChannels:%d\n", wave.nChannels);
536 printf("nSamplesPerSec:%d\n", wave.nChannels);
537 printf("nAvgBytesPerSec:%d\n", wave.nAvgBytesPerSec);
538 printf("nBlockAlign:%d\n", wave.nBlockAlign);
539 printf("wBitsPerSample:%d\n", wave.wBitsPerSample);
540 printf("cbSize:%d\n", wave.cbSize);
543 if(vstream->ReadFrame()<0) {
548 if(frame < firstframe)
550 if(astream->ReadFrames(buffer, bufsize,
551 (int)samplesperframe,
552 samples_read, bytes_read)<0) {
556 printf("\rskipping frame %d",frame);
559 if(frame == firstframe)
564 printf("\rconvert frame %d",frame);
568 movie_sound_pos += samplesperframe;
571 while(mp3_sound_pos<movie_sound_pos) {
572 if(astream->ReadFrames(buffer, bufsize,
573 mp3_block_size/samplefix,
574 samples_read, bytes_read)<0) {
575 printf("couldn't read %d samples\n", mp3_block_size);
581 for(s=0;s<mp3_block_size;s++) {
582 block[s] = ((int)buffer[t]-128)*256;
589 if(first) { //first run
590 tag = swf_InsertTag(NULL, ST_SOUNDSTREAMBLOCK);
591 swf_SetSoundStreamBlock(tag, block, mp3_block_size,1);
593 swf_SetSoundStreamBlock(tag, block, mp3_block_size,0);
596 mp3_sound_pos += mp3_block_size/samplefix;
598 if(mp3_sound_pos>=movie_sound_pos) { // last run
599 swf_WriteTag(file, tag);
607 CImage*img = vstream->GetFrame();
609 U8*data = img->data();
610 int bpp = img->bpp();
619 /* some movies have changing dimensions */
620 if(img->width() != width ||
621 img->height() != height) {
623 width = img->width();
624 height = img->height();
628 for(yy=0;yy<yblocks;yy++)
629 for(xx=0;xx<xblocks;xx++)
632 for(y=0;y<yblocksize;y++) {
633 U8*mydata = img->at(yy*yblocksize+y);
634 for(x=0;x<xblocksize;x++) {
635 blockbuffer[(y*xblocksize+x)*3+2] = mydata[(xx*xblocksize+x)*3+0];
636 blockbuffer[(y*xblocksize+x)*3+1] = mydata[(xx*xblocksize+x)*3+1];
637 blockbuffer[(y*xblocksize+x)*3+0] = mydata[(xx*xblocksize+x)*3+2];
641 b.data = blockbuffer;
642 b.len = xblocksize*yblocksize*3;
643 blocks[yy*xblocks+xx].compress(file, &b);
646 tag = swf_InsertTag(NULL, ST_SHOWFRAME);
647 swf_WriteTag(file, tag);
653 if(frame == lastframe)
657 destroydisplay(file);
659 tag = swf_InsertTag(NULL, ST_END);
660 swf_WriteTag(file, tag);