fb7a1d9c7912c6610986707ec80cf17ff3c660ba
[swftools.git] / lib / h.263 / mkvideo.c
1 /* mkvideo.c
2    Create a video file.
3
4    Part of the swftools package.
5    
6    Copyright (c) 2003 Matthias Kramm <kramm@quiss.org> */
7
8 #include <stdlib.h>
9 #include <stdio.h>
10 #include <assert.h>
11 #include <math.h>
12 #include "../lib/rfxswf.h"
13 #include "png.h"
14 #include "h263tables.c"
15
16 typedef struct _VIDEOSTREAM
17 {
18     int width;
19     int height;
20     RGBA*oldpic;
21 } VIDEOSTREAM;
22
23 void swf_SetVideoStreamDefine(TAG*tag, VIDEOSTREAM*stream, U16 frames, U16 width, U16 height)
24 {
25     width=width&~15; height=height&~15;
26     swf_SetU16(tag, frames);
27     swf_SetU16(tag, width);
28     swf_SetU16(tag, height);
29     swf_SetU8(tag, 1); /* smoothing on */
30     swf_SetU8(tag, 2); /* codec = h.263 sorenson spark */
31
32     stream->width = width;
33     stream->height = height;
34     stream->oldpic = 0;
35 }
36
37 typedef struct _block_t
38 {
39     int y1[64];
40     int y2[64];
41     int y3[64];
42     int y4[64];
43     int u[64];
44     int v[64];
45 } block_t;
46
47 typedef struct _fblock_t
48 {
49     double y1[64];
50     double y2[64];
51     double y3[64];
52     double y4[64];
53     double u[64];
54     double v[64];
55 } fblock_t;
56
57 static void fzigzag(double*src) 
58 {
59     int table[64] = {
60         0, 1, 5, 6, 14, 15, 27, 28, 
61         2, 4, 7, 13, 16, 26, 29, 42, 
62         3, 8, 12, 17, 25, 30, 41, 43, 
63         9, 11, 18, 24, 31, 40, 44, 53, 
64         10, 19, 23, 32, 39, 45, 52, 54, 
65         20, 22, 33, 38, 46, 51, 55, 60, 
66         21, 34, 37, 47, 50, 56, 59, 61, 
67         35, 36, 48, 49, 57, 58, 62, 63};
68     double tmp[64];
69     int t;
70     for(t=0;t<64;t++) {
71         ((int*)&tmp[table[t]])[0] = ((int*)&src[t])[0];
72         ((int*)&tmp[table[t]])[1] = ((int*)&src[t])[1];
73     }
74     memcpy(src, tmp, sizeof(double)*64);
75 }
76
77 #define PI 3.14159265358979
78 #define SQRT2 1.414214
79 #define RSQRT2 (1.0/1.414214)
80
81 static double table[8][8] =
82 {
83 {0.707106781186548,0.707106781186548,0.707106781186548,0.707106781186548,0.707106781186548,0.707106781186548,0.707106781186548,0.707106781186548},
84 {0.980785280403230,0.831469612302545,0.555570233019602,0.195090322016128,-0.195090322016128,-0.555570233019602,-0.831469612302545,-0.980785280403230},
85 {0.923879532511287,0.382683432365090,-0.382683432365090,-0.923879532511287,-0.923879532511287,-0.382683432365090,0.382683432365090,0.923879532511287},
86 {0.831469612302545,-0.195090322016128,-0.980785280403230,-0.555570233019602,0.555570233019602,0.980785280403230,0.195090322016129,-0.831469612302545},
87 {0.707106781186548,-0.707106781186547,-0.707106781186548,0.707106781186547,0.707106781186548,-0.707106781186547,-0.707106781186547,0.707106781186547},
88 {0.555570233019602,-0.980785280403230,0.195090322016128,0.831469612302545,-0.831469612302545,-0.195090322016128,0.980785280403231,-0.555570233019602},
89 {0.382683432365090,-0.923879532511287,0.923879532511287,-0.382683432365090,-0.382683432365091,0.923879532511287,-0.923879532511286,0.382683432365090},
90 {0.195090322016128,-0.555570233019602,0.831469612302545,-0.980785280403231,0.980785280403230,-0.831469612302545,0.555570233019602,-0.195090322016129}
91 };
92
93 static void dct(double*src)
94 {
95     double tmp[64];
96     int x,y,u,v,t;
97
98     for(v=0;v<8;v++)
99     for(u=0;u<8;u++)
100     {
101         double c = 0;
102         for(x=0;x<8;x++)
103         {
104             c+=table[u][x]*src[v*8+x];
105         }
106         tmp[v*8+u] = c;
107     }
108     for(u=0;u<8;u++)
109     for(v=0;v<8;v++)
110     {
111         double c = 0;
112         for(y=0;y<8;y++)
113         {
114             c+=table[v][y]*tmp[y*8+u];
115         }
116         src[v*8+u] = c*0.25;
117     }
118 }
119
120 static void idct(double*src)
121 {
122     double tmp[64];
123     int x,y,u,v;
124     for(y=0;y<8;y++)
125     for(x=0;x<8;x++)
126     {
127         double c = 0;
128         for(u=0;u<8;u++)
129         {
130             c+=table[u][x]*src[y*8+u];
131         }
132         tmp[y*8+x] = c;
133     }
134     for(y=0;y<8;y++)
135     for(x=0;x<8;x++)
136     {
137         double c = 0;
138         for(v=0;v<8;v++)
139         {
140             c+=table[v][y]*tmp[v*8+x];
141         }
142         src[y*8+x] = c*0.25;
143     }
144 }
145
146 static void getregion(fblock_t* bb, RGBA*pic, int bx, int by, int width, int height)
147 {
148     RGBA*p1 = &pic[by*width*16+bx*16];
149     RGBA*p2 = p1;
150     int linex = width;
151     int y1=0, y2=0, y3=0, y4=0;
152     int u=0,v=0;
153     int x,y;
154     for(y=0;y<8;y++) {
155         for(x=0;x<8;x++) {
156             double r,g,b;
157             r = (p2[x*2].r + p2[x*2+1].r + p2[linex+x*2].r + p2[linex+x*2+1].r)/4.0;
158             g = (p2[x*2].g + p2[x*2+1].g + p2[linex+x*2].g + p2[linex+x*2+1].g)/4.0;
159             b = (p2[x*2].b + p2[x*2+1].b + p2[linex+x*2].b + p2[linex+x*2+1].b)/4.0;
160             bb->u[u++] = (r*-0.169 + g*-0.332 + b*0.500 + 128.0);
161             bb->v[v++] = (r*0.500 + g*-0.419 + b*-0.0813 + 128.0);
162
163             r = p1[x].r; g = p1[x].g; b = p1[x].b;
164             bb->y1[y1++] = (r*0.299 + g*0.587 + b*0.114);
165             r = p1[x+8].r; g = p1[x+8].g; b = p1[x+8].b;
166             bb->y2[y2++] = (r*0.299 + g*0.587 + b*0.114);
167             r = p1[linex*8+x].r; g = p1[linex*8+x].g; b = p1[linex*8+x].b;
168             bb->y3[y3++] = (r*0.299 + g*0.587 + b*0.114);
169             r = p1[linex*8+x+8].r; g = p1[linex*8+x+8].g; b = p1[linex*8+x+8].b;
170             bb->y4[y4++] = (r*0.299 + g*0.587 + b*0.114);
171         }
172         p1+=linex;
173         p2+=linex*2;
174     }
175 }
176
177 static int valtodc(int val)
178 {
179     assert(val>=0);
180
181     /* table 12/h.263 */
182
183     val+=4; //round
184     val/=8;
185     /* TODO: what to do for zero values? skip the block? */
186     if(val==0)
187         return 1;
188     if(val==128)
189         return 255;
190     if(val>254)
191         return 254;
192     return val;
193 }
194
195 static void codehuffman(TAG*tag, struct huffcode*table, int index)
196 {
197     /* TODO: !optimize! */
198     int i=0;
199     while(table[index].code[i]) {
200         if(table[index].code[i]=='0')
201             swf_SetBits(tag, 0, 1);
202         else
203             swf_SetBits(tag, 1, 1);
204         i++;
205     }
206 }
207
208 static void quantize8x8(double*src, int*dest, int has_dc, int quant)
209 {
210     int t,pos=0;
211     if(has_dc) {
212         dest[0] = valtodc((int)src[0]); /*DC*/
213         pos++;
214     }
215     for(t=pos;t<64;t++)
216     {
217         dest[t] = (int)src[t];
218         //val = (quant*(2*level+1)-1)+quant&1
219 /*      if(quant&1) {
220             dest[t] = (dest[t]/quant - 1)/2;
221         } else {
222             dest[t] = ((dest[t]+1)/quant - 1)/2;
223         }*/
224         //dest[t] = (dest[t]/quant-1)/2;
225         dest[t] = dest[t]/(quant*2);
226     }
227 }
228
229 static int hascoef(int*b, int has_dc)
230 {
231     int t;
232     int pos=0;
233     int range=2; /*TODO: should be a global parameter */
234     if(has_dc)
235         pos++;
236     for(t=pos;t<64;t++) {
237         if(b[t]<=-range || b[t]>=range)
238             return 1;
239     }
240     return 0;
241 }
242
243 static void encode8x8(TAG*tag, int*bb, int has_dc, int has_tcoef)
244 {
245     int t;
246     int pos=0;
247
248     if(has_dc) {
249         swf_SetBits(tag, bb[0], 8);
250         pos++;
251     }
252
253     if(has_tcoef) {
254         int last;
255         /* determine last non-null coefficient */
256         for(last=63;last>=pos;last--) {
257             /* TODO: we could leave out small coefficients
258                      after a certain point (32?) */
259             if(bb[last])
260                 break;
261         }
262         /* blocks without coefficients should not be included
263            in the cbpy/cbpc patterns: */
264         assert(bb[last]);
265
266         while(1) {
267             int run=0;
268             int level=0;
269             int islast=0;
270             int sign=0;
271             int t;
272             while(!bb[pos] && pos<last) {
273                 pos++;
274                 run++;
275             }
276             if(pos==last)
277                 islast=1;
278             level=bb[pos];
279             assert(level);
280             if(level<0) {
281                 level = -level;
282                 sign = 1;
283             }
284             for(t=0;t<RLE_ESCAPE;t++) {
285                 if(rle_params[t].run == run &&
286                    rle_params[t].level == level &&
287                    rle_params[t].last == islast) {
288                     codehuffman(tag, rle, t);
289                     swf_SetBits(tag, sign, 1);
290                     break;
291                 }
292             }
293             if(t==RLE_ESCAPE) {
294                 codehuffman(tag, rle, RLE_ESCAPE);
295                 level=bb[pos];
296                 /* table 14/h.263 */
297                 assert(level);
298                 if(level<-127) level = -127;
299                 if(level>127) level = 127;
300
301                 swf_SetBits(tag, islast, 1);
302                 swf_SetBits(tag, run, 6);
303                 swf_SetBits(tag, level, 8); //fixme
304             }
305
306             if(islast)
307                 break;
308             pos++;
309         }
310
311         //codehuffman(tag, rle, 58);
312         //swf_SetBits(tag, 1, 1); //sign
313     }
314 }
315
316 static void dodct(fblock_t*fb)
317 {
318     dct(fb->y1); dct(fb->y2); dct(fb->y3); dct(fb->y4); 
319     dct(fb->u);  dct(fb->v);  
320     fzigzag(fb->y1);
321     fzigzag(fb->y2);
322     fzigzag(fb->y3);
323     fzigzag(fb->y4);
324     fzigzag(fb->u);
325     fzigzag(fb->v); 
326 }
327
328 static void quantize(fblock_t*fb, block_t*b, int has_dc, int quant)
329 {
330     quantize8x8(fb->y1,b->y1,has_dc,quant); 
331     quantize8x8(fb->y2,b->y2,has_dc,quant); 
332     quantize8x8(fb->y3,b->y3,has_dc,quant); 
333     quantize8x8(fb->y4,b->y4,has_dc,quant); 
334     quantize8x8(fb->u,b->u,has_dc,quant);   
335     quantize8x8(fb->v,b->v,has_dc,quant);   
336 }
337
338 static void getblockpatterns(block_t*b, int*cbpybits,int*cbpcbits, int has_dc)
339 {
340     *cbpybits = 0;
341     *cbpcbits = 0;
342
343     *cbpybits|=hascoef(b->y1, has_dc)*8;
344     *cbpybits|=hascoef(b->y2, has_dc)*4;
345     *cbpybits|=hascoef(b->y3, has_dc)*2;
346     *cbpybits|=hascoef(b->y4, has_dc)*1;
347
348     *cbpcbits|=hascoef(b->u, has_dc)*2;
349     *cbpcbits|=hascoef(b->v, has_dc)*1;
350 }
351
352 static void setQuant(TAG*tag, int dquant)
353 {
354     int code = 0;
355     /* 00 01 10 11
356        -1 -2 +1 +2
357     */
358     if(dquant == -1) {
359         swf_SetBits(tag, 0x0, 2);
360     } else if(dquant == -2) {
361         swf_SetBits(tag, 0x1, 2);
362     } else if(dquant == +1) {
363         swf_SetBits(tag, 0x2, 2);
364     } else if(dquant == +2) {
365         swf_SetBits(tag, 0x3, 2);
366     } else {
367         assert(0*strlen("invalid dquant"));
368     }
369 }
370
371 static void change_quant(int quant, int*dquant)
372 {
373     /* TODO */
374     *dquant = 0;
375 }
376
377 void encode_blockI(TAG*tag, RGBA*pic, int bx, int by, int width, int height, int*quant)
378 {
379     fblock_t fb;
380     block_t b;
381     int dquant=0;
382     int cbpcbits = 0, cbpybits=0;
383
384     getregion(&fb, pic, bx, by, width, height);
385     dodct(&fb);
386     
387     change_quant(*quant, &dquant);
388     *quant+=dquant;
389
390     quantize(&fb, &b, 1, *quant);
391     getblockpatterns(&b, &cbpybits, &cbpcbits, 1);
392
393     if(dquant) {
394         codehuffman(tag, mcbpc_intra, 4+cbpcbits);
395     } else {
396         codehuffman(tag, mcbpc_intra, 0+cbpcbits);
397     }
398
399     codehuffman(tag, cbpy, cbpybits);
400
401     if(dquant) {
402         setQuant(tag, dquant);
403     }
404
405     /* luminance */
406     encode8x8(tag, b.y1, 1, cbpybits&8);
407     encode8x8(tag, b.y2, 1, cbpybits&4);
408     encode8x8(tag, b.y3, 1, cbpybits&2);
409     encode8x8(tag, b.y4, 1, cbpybits&1);
410
411     /* chrominance */
412     encode8x8(tag, b.u, 1, cbpcbits&2);
413     encode8x8(tag, b.v, 1, cbpcbits&1);
414 }
415
416 void encode_blockP(TAG*tag, RGBA*pic, int bx, int by, int width, int height, int*quant)
417 {
418     fblock_t fb;
419     block_t b;
420     int dquant=0;
421     int has_mvd=0;
422     int has_mvd24=0;
423     int has_dc=1;
424     int mode = 0;
425     int cbpcbits = 0, cbpybits=0;
426
427     getregion(&fb, pic, bx, by, width, height);
428     dodct(&fb);
429
430     change_quant(*quant, &dquant);
431     *quant += dquant;
432
433     quantize(&fb, &b, has_dc, *quant);
434
435     getblockpatterns(&b, &cbpybits, &cbpcbits, has_dc);
436
437     if(!dquant && has_mvd && !has_mvd24 && !has_dc) mode = 0;
438     else if(dquant && has_mvd && !has_mvd24 && !has_dc) mode = 1;
439     else if(!dquant && has_mvd && has_mvd24 && !has_dc) mode = 2;
440     else if(!dquant && !has_mvd && !has_mvd24 && has_dc) mode = 3;
441     else if(dquant && !has_mvd && !has_mvd24 && has_dc) mode = 4;
442     else exit(1);
443
444     swf_SetBits(tag,0,1); /* cod - 1 if we're not going to code this block*/
445         
446     codehuffman(tag, mcbpc_inter, mode*4+cbpcbits);
447     codehuffman(tag, cbpy, (mode==3 || mode==4)?cbpybits:cbpybits^15);
448
449     if(!bx&&!by) {
450         printf("cbpcbits: %d\n", cbpcbits);
451         printf("cbpybits: %d\n", cbpybits);
452     }
453
454     if(dquant) {
455         setQuant(tag, dquant);
456     }
457
458     if(has_mvd) {
459     }
460     if(has_mvd24) {
461     }
462
463     /* luminance */
464     encode8x8(tag, b.y1, has_dc, cbpybits&8);
465     encode8x8(tag, b.y2, has_dc, cbpybits&4);
466     encode8x8(tag, b.y3, has_dc, cbpybits&2);
467     encode8x8(tag, b.y4, has_dc, cbpybits&1);
468
469     /* chrominance */
470     encode8x8(tag, b.u, has_dc, cbpcbits&2);
471     encode8x8(tag, b.v, has_dc, cbpcbits&1);
472 }
473
474 #define TYPE_IFRAME 0
475 #define TYPE_PFRAME 1
476
477 static void writeHeader(TAG*tag, int width, int height, int frame, int quant, int type)
478 {
479     U32 i32;
480     swf_SetU16(tag, frame);
481     swf_SetBits(tag, 1, 17); /* picture start code*/
482     swf_SetBits(tag, 0, 5); /* version=0, version 1 would optimize rle behaviour*/
483     swf_SetBits(tag, frame, 8); /* time reference */
484
485     /* write dimensions, taking advantage of some predefined sizes
486        if the opportunity presents itself */
487     i32 = width<<16|height;
488     switch(i32)
489     {
490         case 352<<16|288: swf_SetBits(tag, 2, 3);break;
491         case 176<<16|144: swf_SetBits(tag, 3, 3);break;
492         case 128<<16|96: swf_SetBits(tag, 4, 3);break;
493         case 320<<16|240: swf_SetBits(tag, 5, 3);break;
494         case 160<<16|120: swf_SetBits(tag, 6, 3);break;
495         default:
496             if(width>255 || height>255) {
497                 swf_SetBits(tag, 1, 3);
498                 swf_SetBits(tag, width, 16);
499                 swf_SetBits(tag, height, 16);
500             } else {
501                 swf_SetBits(tag, 0, 3);
502                 swf_SetBits(tag, width, 8);
503                 swf_SetBits(tag, height, 8);
504             }
505     }
506
507     swf_SetBits(tag, type, 2); /* I-Frame or P-Frame */
508     swf_SetBits(tag, 0, 1); /* No deblock filter */
509     swf_SetBits(tag, quant, 5); /* quantizer (1-31), may be updated later on*/
510     swf_SetBits(tag, 0, 1); /* No extra info */
511 }
512
513 void swf_SetVideoStreamIFrame(TAG*tag, VIDEOSTREAM*s, RGBA*pic, U16 width, U16 height, int frame)
514 {
515     int bx, by, bbx, bby;
516     int quant = 7;
517
518     /* TODO: width not divisible by 16 will get us in trouble */
519     width=width&~15; height=height&~15;
520
521     writeHeader(tag, width, height, frame, quant, TYPE_IFRAME);
522
523     bbx = (width+15)/16;
524     bby = (height+15)/16;
525
526     for(by=0;by<bby;by++)
527     {
528         for(bx=0;bx<bbx;bx++)
529         {
530             encode_blockI(tag, pic, bx, by, width, height, &quant);
531         }
532     }
533 }
534
535 void swf_SetVideoStreamPFrame(TAG*tag, VIDEOSTREAM*s, RGBA*pic, U16 width, U16 height, int frame)
536 {
537     int bx, by, bbx, bby;
538     int quant = 7;
539
540     /* TODO: width not divisible by 16 will get us in trouble */
541     width=width&~15; height=height&~15;
542
543     writeHeader(tag, width, height, frame, quant, TYPE_PFRAME);
544
545     bbx = (width+15)/16;
546     bby = (height+15)/16;
547
548     for(by=0;by<bby;by++)
549     {
550         for(bx=0;bx<bbx;bx++)
551         {
552             encode_blockP(tag, pic, bx, by, width, height, &quant);
553         }
554     }
555 }
556
557 int main(int argn, char*argv[])
558 {
559     int fi;
560     int t;
561     SWF swf;
562     TAG * tag;
563     RGBA* pic, rgb;
564     SWFPLACEOBJECT obj;
565     int width = 0;
566     int height = 0;
567     int frames = 2;
568     unsigned char*data;
569     char* fname = "/home/kramm/pics/peppers.png";
570     VIDEOSTREAM stream;
571
572     memset(&stream, 0, sizeof(stream));
573
574     getPNG(fname, &width, &height, &data);
575     pic = (RGBA*)malloc(width*height*sizeof(RGBA));
576     memcpy(pic, data, width*height*sizeof(RGBA));
577     free(data);
578
579     printf("Compressing %s, size %dx%d\n", fname, width, height);
580
581     memset(&swf,0,sizeof(SWF));
582     memset(&obj,0,sizeof(obj));
583
584     swf.fileVersion    = 6;
585     swf.frameRate      = 29*256;
586     swf.movieSize.xmax = 20*width;
587     swf.movieSize.ymax = 20*height;
588
589     swf.firstTag = swf_InsertTag(NULL,ST_SETBACKGROUNDCOLOR);
590     tag = swf.firstTag;
591     rgb.r = 0x00;rgb.g = 0x00;rgb.b = 0x00;
592     swf_SetRGB(tag,&rgb);
593
594     tag = swf_InsertTag(tag, ST_DEFINEVIDEOSTREAM);
595     swf_SetU16(tag, 33);
596     swf_SetVideoStreamDefine(tag, &stream, frames, width, height);
597     
598     for(t=0;t<frames;t++)
599     {
600         tag = swf_InsertTag(tag, ST_VIDEOFRAME);
601         swf_SetU16(tag, 33);
602         if(t==0)
603             swf_SetVideoStreamIFrame(tag, &stream, pic, width, height, t);
604         else
605             swf_SetVideoStreamPFrame(tag, &stream, pic, width, height, t);
606
607         tag = swf_InsertTag(tag, ST_PLACEOBJECT2);
608         swf_GetPlaceObject(0, &obj);
609         if(t==0) {
610             obj.depth = 1;
611             obj.id = 33;
612         } else {
613             obj.move = 1;
614             obj.depth = 1;
615             obj.ratio = t;
616         }
617         swf_SetPlaceObject(tag,&obj);
618
619         tag = swf_InsertTag(tag, ST_SHOWFRAME);
620     }
621    
622     tag = swf_InsertTag(tag, ST_END);
623
624     fi = open("video3.swf", O_WRONLY|O_CREAT|O_TRUNC, 0644);
625     if(swf_WriteSWF(fi,&swf)<0) {
626         fprintf(stderr,"WriteSWF() failed.\n");
627     }
628     close(fi);
629     swf_FreeTags(&swf);
630 }