4 Part of the swftools package.
6 Copyright (c) 2003 Matthias Kramm <kramm@quiss.org> */
12 #include "../lib/rfxswf.h"
14 #include "h263tables.c"
16 typedef struct _VIDEOSTREAM
25 void swf_SetVideoStreamDefine(TAG*tag, VIDEOSTREAM*stream, U16 frames, U16 width, U16 height)
27 width=width&~15; height=height&~15;
28 swf_SetU16(tag, frames);
29 swf_SetU16(tag, width);
30 swf_SetU16(tag, height);
31 swf_SetU8(tag, 1); /* smoothing on */
32 swf_SetU8(tag, 2); /* codec = h.263 sorenson spark */
34 memset(stream, 0, sizeof(VIDEOSTREAM));
35 stream->width = width&~15;
36 stream->height = height&~15;
37 stream->oldpic = (RGBA*)malloc(width*height*sizeof(RGBA));
39 memset(stream->oldpic, 0, width*height*sizeof(RGBA));
42 typedef struct _block_t
52 typedef struct _fblock_t
62 static void fzigzag(double*src)
65 0, 1, 5, 6, 14, 15, 27, 28,
66 2, 4, 7, 13, 16, 26, 29, 42,
67 3, 8, 12, 17, 25, 30, 41, 43,
68 9, 11, 18, 24, 31, 40, 44, 53,
69 10, 19, 23, 32, 39, 45, 52, 54,
70 20, 22, 33, 38, 46, 51, 55, 60,
71 21, 34, 37, 47, 50, 56, 59, 61,
72 35, 36, 48, 49, 57, 58, 62, 63};
76 ((int*)&tmp[table[t]])[0] = ((int*)&src[t])[0];
77 ((int*)&tmp[table[t]])[1] = ((int*)&src[t])[1];
79 memcpy(src, tmp, sizeof(double)*64);
82 #define PI 3.14159265358979
83 #define SQRT2 1.414214
84 #define RSQRT2 (1.0/1.414214)
86 static double table[8][8] =
88 {0.707106781186548,0.707106781186548,0.707106781186548,0.707106781186548,0.707106781186548,0.707106781186548,0.707106781186548,0.707106781186548},
89 {0.980785280403230,0.831469612302545,0.555570233019602,0.195090322016128,-0.195090322016128,-0.555570233019602,-0.831469612302545,-0.980785280403230},
90 {0.923879532511287,0.382683432365090,-0.382683432365090,-0.923879532511287,-0.923879532511287,-0.382683432365090,0.382683432365090,0.923879532511287},
91 {0.831469612302545,-0.195090322016128,-0.980785280403230,-0.555570233019602,0.555570233019602,0.980785280403230,0.195090322016129,-0.831469612302545},
92 {0.707106781186548,-0.707106781186547,-0.707106781186548,0.707106781186547,0.707106781186548,-0.707106781186547,-0.707106781186547,0.707106781186547},
93 {0.555570233019602,-0.980785280403230,0.195090322016128,0.831469612302545,-0.831469612302545,-0.195090322016128,0.980785280403231,-0.555570233019602},
94 {0.382683432365090,-0.923879532511287,0.923879532511287,-0.382683432365090,-0.382683432365091,0.923879532511287,-0.923879532511286,0.382683432365090},
95 {0.195090322016128,-0.555570233019602,0.831469612302545,-0.980785280403231,0.980785280403230,-0.831469612302545,0.555570233019602,-0.195090322016129}
98 static void dct(double*src)
109 c+=table[u][x]*src[v*8+x];
119 c+=table[v][y]*tmp[y*8+u];
125 static void idct(double*src)
135 c+=table[u][x]*src[y*8+u];
145 c+=table[v][y]*tmp[v*8+x];
151 static void getregion(fblock_t* bb, RGBA*pic, int bx, int by, int width, int height)
153 RGBA*p1 = &pic[by*width*16+bx*16];
156 int y1=0, y2=0, y3=0, y4=0;
162 r = (p2[x*2].r + p2[x*2+1].r + p2[linex+x*2].r + p2[linex+x*2+1].r)/4.0;
163 g = (p2[x*2].g + p2[x*2+1].g + p2[linex+x*2].g + p2[linex+x*2+1].g)/4.0;
164 b = (p2[x*2].b + p2[x*2+1].b + p2[linex+x*2].b + p2[linex+x*2+1].b)/4.0;
165 bb->u[u++] = (r*-0.169 + g*-0.332 + b*0.500 + 128.0);
166 bb->v[v++] = (r*0.500 + g*-0.419 + b*-0.0813 + 128.0);
168 r = p1[x].r; g = p1[x].g; b = p1[x].b;
169 bb->y1[y1++] = (r*0.299 + g*0.587 + b*0.114);
170 r = p1[x+8].r; g = p1[x+8].g; b = p1[x+8].b;
171 bb->y2[y2++] = (r*0.299 + g*0.587 + b*0.114);
172 r = p1[linex*8+x].r; g = p1[linex*8+x].g; b = p1[linex*8+x].b;
173 bb->y3[y3++] = (r*0.299 + g*0.587 + b*0.114);
174 r = p1[linex*8+x+8].r; g = p1[linex*8+x+8].g; b = p1[linex*8+x+8].b;
175 bb->y4[y4++] = (r*0.299 + g*0.587 + b*0.114);
182 int compareregions(VIDEOSTREAM*s, int bx, int by)
184 int linex = s->width;
185 RGBA*p1 = &s->current[by*linex*16+bx*16];
186 RGBA*p2 = &s->oldpic[by*linex*16+bx*16];
204 static int valtodc(int val)
212 /* TODO: what to do for zero values? skip the block? */
222 static void codehuffman(TAG*tag, struct huffcode*table, int index)
224 /* TODO: !optimize! */
226 while(table[index].code[i]) {
227 if(table[index].code[i]=='0')
228 swf_SetBits(tag, 0, 1);
230 swf_SetBits(tag, 1, 1);
235 static void quantize8x8(double*src, int*dest, int has_dc, int quant)
239 dest[0] = valtodc((int)src[0]); /*DC*/
244 dest[t] = (int)src[t];
245 //val = (quant*(2*level+1)-1)+quant&1
247 dest[t] = (dest[t]/quant - 1)/2;
249 dest[t] = ((dest[t]+1)/quant - 1)/2;
251 //dest[t] = (dest[t]/quant-1)/2;
252 dest[t] = dest[t]/(quant*2);
256 static int hascoef(int*b, int has_dc)
260 int range=2; /*TODO: should be a global parameter */
263 for(t=pos;t<64;t++) {
264 if(b[t]<=-range || b[t]>=range)
270 static void encode8x8(TAG*tag, int*bb, int has_dc, int has_tcoef)
276 swf_SetBits(tag, bb[0], 8);
282 /* determine last non-null coefficient */
283 for(last=63;last>=pos;last--) {
284 /* TODO: we could leave out small coefficients
285 after a certain point (32?) */
289 /* blocks without coefficients should not be included
290 in the cbpy/cbpc patterns: */
299 while(!bb[pos] && pos<last) {
311 for(t=0;t<RLE_ESCAPE;t++) {
312 if(rle_params[t].run == run &&
313 rle_params[t].level == level &&
314 rle_params[t].last == islast) {
315 codehuffman(tag, rle, t);
316 swf_SetBits(tag, sign, 1);
321 codehuffman(tag, rle, RLE_ESCAPE);
325 if(level<-127) level = -127;
326 if(level>127) level = 127;
328 swf_SetBits(tag, islast, 1);
329 swf_SetBits(tag, run, 6);
330 swf_SetBits(tag, level, 8); //fixme
338 //codehuffman(tag, rle, 58);
339 //swf_SetBits(tag, 1, 1); //sign
343 static void dodct(fblock_t*fb)
345 dct(fb->y1); dct(fb->y2); dct(fb->y3); dct(fb->y4);
346 dct(fb->u); dct(fb->v);
355 static void quantize(fblock_t*fb, block_t*b, int has_dc, int quant)
357 quantize8x8(fb->y1,b->y1,has_dc,quant);
358 quantize8x8(fb->y2,b->y2,has_dc,quant);
359 quantize8x8(fb->y3,b->y3,has_dc,quant);
360 quantize8x8(fb->y4,b->y4,has_dc,quant);
361 quantize8x8(fb->u,b->u,has_dc,quant);
362 quantize8x8(fb->v,b->v,has_dc,quant);
365 static void getblockpatterns(block_t*b, int*cbpybits,int*cbpcbits, int has_dc)
370 *cbpybits|=hascoef(b->y1, has_dc)*8;
371 *cbpybits|=hascoef(b->y2, has_dc)*4;
372 *cbpybits|=hascoef(b->y3, has_dc)*2;
373 *cbpybits|=hascoef(b->y4, has_dc)*1;
375 *cbpcbits|=hascoef(b->u, has_dc)*2;
376 *cbpcbits|=hascoef(b->v, has_dc)*1;
379 static void setQuant(TAG*tag, int dquant)
386 swf_SetBits(tag, 0x0, 2);
387 } else if(dquant == -2) {
388 swf_SetBits(tag, 0x1, 2);
389 } else if(dquant == +1) {
390 swf_SetBits(tag, 0x2, 2);
391 } else if(dquant == +2) {
392 swf_SetBits(tag, 0x3, 2);
394 assert(0*strlen("invalid dquant"));
398 static void change_quant(int quant, int*dquant)
404 void decode_blockI(VIDEOSTREAM*s, block_t*b, int bx, int by)
410 s->oldpic[(y+by*16)*s->width+(x+bx*16)] = s->current[(y+by*16)*s->width+(x+bx*16)];
414 void encode_blockI(TAG*tag, VIDEOSTREAM*s, int bx, int by, int*quant)
419 int cbpcbits = 0, cbpybits=0;
421 getregion(&fb, s->current, bx, by, s->width, s->height);
424 change_quant(*quant, &dquant);
426 quantize(&fb, &b, 1, *quant);
428 decode_blockI(s, &b, bx, by);
430 getblockpatterns(&b, &cbpybits, &cbpcbits, 1);
433 codehuffman(tag, mcbpc_intra, 4+cbpcbits);
435 codehuffman(tag, mcbpc_intra, 0+cbpcbits);
438 codehuffman(tag, cbpy, cbpybits);
441 setQuant(tag, dquant);
445 encode8x8(tag, b.y1, 1, cbpybits&8);
446 encode8x8(tag, b.y2, 1, cbpybits&4);
447 encode8x8(tag, b.y3, 1, cbpybits&2);
448 encode8x8(tag, b.y4, 1, cbpybits&1);
451 encode8x8(tag, b.u, 1, cbpcbits&2);
452 encode8x8(tag, b.v, 1, cbpcbits&1);
455 void encode_blockP(TAG*tag, VIDEOSTREAM*s, int bx, int by, int*quant)
464 int cbpcbits = 0, cbpybits=0;
468 diff = compareregions(s, bx, by);
469 if(diff < 64/*TODO: should be a parameter*/) {
470 swf_SetBits(tag, 1,1); /* cod=1, block skipped */
475 getregion(&fb, s->current, bx, by, s->width, s->height);
478 change_quant(*quant, &dquant);
481 quantize(&fb, &b, has_dc, *quant);
483 getblockpatterns(&b, &cbpybits, &cbpcbits, has_dc);
485 if(!dquant && has_mvd && !has_mvd24 && !has_dc) mode = 0;
486 else if(dquant && has_mvd && !has_mvd24 && !has_dc) mode = 1;
487 else if(!dquant && has_mvd && has_mvd24 && !has_dc) mode = 2;
488 else if(!dquant && !has_mvd && !has_mvd24 && has_dc) mode = 3;
489 else if(dquant && !has_mvd && !has_mvd24 && has_dc) mode = 4;
492 swf_SetBits(tag,0,1); /* cod - 1 if we're not going to code this block*/
494 codehuffman(tag, mcbpc_inter, mode*4+cbpcbits);
495 codehuffman(tag, cbpy, (mode==3 || mode==4)?cbpybits:cbpybits^15);
498 printf("cbpcbits: %d\n", cbpcbits);
499 printf("cbpybits: %d\n", cbpybits);
503 setQuant(tag, dquant);
512 encode8x8(tag, b.y1, has_dc, cbpybits&8);
513 encode8x8(tag, b.y2, has_dc, cbpybits&4);
514 encode8x8(tag, b.y3, has_dc, cbpybits&2);
515 encode8x8(tag, b.y4, has_dc, cbpybits&1);
518 encode8x8(tag, b.u, has_dc, cbpcbits&2);
519 encode8x8(tag, b.v, has_dc, cbpcbits&1);
522 #define TYPE_IFRAME 0
523 #define TYPE_PFRAME 1
525 static void writeHeader(TAG*tag, int width, int height, int frame, int quant, int type)
528 swf_SetU16(tag, frame);
529 swf_SetBits(tag, 1, 17); /* picture start code*/
530 swf_SetBits(tag, 0, 5); /* version=0, version 1 would optimize rle behaviour*/
531 swf_SetBits(tag, frame, 8); /* time reference */
533 /* write dimensions, taking advantage of some predefined sizes
534 if the opportunity presents itself */
535 i32 = width<<16|height;
538 case 352<<16|288: swf_SetBits(tag, 2, 3);break;
539 case 176<<16|144: swf_SetBits(tag, 3, 3);break;
540 case 128<<16|96: swf_SetBits(tag, 4, 3);break;
541 case 320<<16|240: swf_SetBits(tag, 5, 3);break;
542 case 160<<16|120: swf_SetBits(tag, 6, 3);break;
544 if(width>255 || height>255) {
545 swf_SetBits(tag, 1, 3);
546 swf_SetBits(tag, width, 16);
547 swf_SetBits(tag, height, 16);
549 swf_SetBits(tag, 0, 3);
550 swf_SetBits(tag, width, 8);
551 swf_SetBits(tag, height, 8);
555 swf_SetBits(tag, type, 2); /* I-Frame or P-Frame */
556 swf_SetBits(tag, 0, 1); /* No deblock filter */
557 swf_SetBits(tag, quant, 5); /* quantizer (1-31), may be updated later on*/
558 swf_SetBits(tag, 0, 1); /* No extra info */
561 void swf_SetVideoStreamIFrame(TAG*tag, VIDEOSTREAM*s, RGBA*pic)
563 int bx, by, bbx, bby;
566 writeHeader(tag, s->width, s->height, s->frame, quant, TYPE_IFRAME);
568 bbx = (s->width+15)/16;
569 bby = (s->height+15)/16;
573 for(by=0;by<bby;by++)
575 for(bx=0;bx<bbx;bx++)
577 encode_blockI(tag, s, bx, by, &quant);
583 void swf_SetVideoStreamPFrame(TAG*tag, VIDEOSTREAM*s, RGBA*pic)
585 int bx, by, bbx, bby;
588 writeHeader(tag, s->width, s->height, s->frame, quant, TYPE_PFRAME);
590 bbx = (s->width+15)/16;
591 bby = (s->height+15)/16;
595 for(by=0;by<bby;by++)
597 for(bx=0;bx<bbx;bx++)
599 encode_blockP(tag, s, bx, by, &quant);
605 int main(int argn, char*argv[])
611 RGBA* pic, *pic2, rgb;
617 char* fname = "/home/kramm/pics/peppers.png";
621 memset(&stream, 0, sizeof(stream));
623 getPNG(fname, &width, &height, &data);
624 pic = (RGBA*)malloc(width*height*sizeof(RGBA));
625 pic2 = (RGBA*)malloc(width*height*sizeof(RGBA));
626 memcpy(pic, data, width*height*sizeof(RGBA));
629 printf("Compressing %s, size %dx%d\n", fname, width, height);
631 memset(&swf,0,sizeof(SWF));
632 memset(&obj,0,sizeof(obj));
635 swf.frameRate = 29*256;
636 swf.movieSize.xmax = 20*width;
637 swf.movieSize.ymax = 20*height;
639 swf.firstTag = swf_InsertTag(NULL,ST_SETBACKGROUNDCOLOR);
641 rgb.r = 0x00;rgb.g = 0x00;rgb.b = 0x00;
642 swf_SetRGB(tag,&rgb);
644 tag = swf_InsertTag(tag, ST_DEFINEVIDEOSTREAM);
646 swf_SetVideoStreamDefine(tag, &stream, frames, width, height);
648 for(t=0;t<frames;t++)
652 for(y=0,yy=0;y<height;y++,yy+=d) {
653 RGBA*line = &pic[((int)yy)*width];
654 for(x=0,xx=0;x<width;x++,xx+=d) {
655 pic2[y*width+x] = line[((int)xx)];
658 printf("frame:%d\n", t);fflush(stdout);
660 tag = swf_InsertTag(tag, ST_VIDEOFRAME);
663 swf_SetVideoStreamIFrame(tag, &stream, pic2);
665 swf_SetVideoStreamPFrame(tag, &stream, pic2);
667 tag = swf_InsertTag(tag, ST_PLACEOBJECT2);
668 swf_GetPlaceObject(0, &obj);
677 swf_SetPlaceObject(tag,&obj);
679 tag = swf_InsertTag(tag, ST_SHOWFRAME);
683 tag = swf_InsertTag(tag, ST_END);
685 fi = open("video3.swf", O_WRONLY|O_CREAT|O_TRUNC, 0644);
686 if(swf_WriteSWF(fi,&swf)<0) {
687 fprintf(stderr,"WriteSWF() failed.\n");