88a482a5486bcdcaca373cf64ca5b7ce1434ea64
[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
17 void swf_SetVideoStreamDefine(TAG*tag, U16 frames, U16 width, U16 height)
18 {
19     width=width&~15; height=height&~15;
20     swf_SetU16(tag, frames);
21     swf_SetU16(tag, width);
22     swf_SetU16(tag, height);
23     swf_SetU8(tag, 1); /* smoothing on */
24     swf_SetU8(tag, 2); /* codec = h.263 sorenson spark */
25 }
26
27 struct block_t
28 {
29     int y1[64];
30     int y2[64];
31     int y3[64];
32     int y4[64];
33     int u[64];
34     int v[64];
35 };
36
37 struct fblock_t
38 {
39     double y1[64];
40     double y2[64];
41     double y3[64];
42     double y4[64];
43     double u[64];
44     double v[64];
45 };
46
47 void zigzag(int*src) 
48 {
49     int table[64] = {
50         0, 1, 5, 6, 14, 15, 27, 28, 
51         2, 4, 7, 13, 16, 26, 29, 42, 
52         3, 8, 12, 17, 25, 30, 41, 43, 
53         9, 11, 18, 24, 31, 40, 44, 53, 
54         10, 19, 23, 32, 39, 45, 52, 54, 
55         20, 22, 33, 38, 46, 51, 55, 60, 
56         21, 34, 37, 47, 50, 56, 59, 61, 
57         35, 36, 48, 49, 57, 58, 62, 63};
58     int tmp[64];
59     int t;
60     for(t=0;t<64;t++) {
61         tmp[table[t]] = src[t];
62     }
63     memcpy(src, tmp, sizeof(int)*64);
64 }
65
66 #define PI 3.14159265358979
67 #define SQRT2 1.414214
68 #define RSQRT2 (1.0/1.414214)
69
70 void dct(double*src)
71 {
72     double tmp[64];
73     int x,y,u,v;
74     for(y=0;y<8;y++)
75     for(x=0;x<8;x++)
76     {
77         double c = 0;
78         for(v=0;v<8;v++)
79         for(u=0;u<8;u++)
80         {
81             double f = 0.25*cos(PI*(2.0*x+1.0)*u/16.0)*cos(PI*(2.0*y+1.0)*v/16.0);
82             if(!u) f *= RSQRT2;
83             if(!v) f *= RSQRT2;
84             c+=f*src[v*8+u];
85         }
86         tmp[y*8+x] = c;
87     }
88     memcpy(src, tmp, sizeof(double)*64);
89 }
90
91 void idct(double*src)
92 {
93     double tmp[64];
94     int x,y,u,v;
95     for(v=0;v<8;v++)
96     for(u=0;u<8;u++)
97     {
98         double c = 0;
99         for(y=0;y<8;y++)
100         for(x=0;x<8;x++)
101         {
102             double f = 0.25*cos(PI*(2.0*x+1.0)*u/16.0)*cos(PI*(2.0*y+1.0)*v/16.0);
103             if(!u) f *= RSQRT2;
104             if(!v) f *= RSQRT2;
105             c+=f*src[y*8+x];
106         }
107         tmp[v*8+u] = c;
108     }
109     memcpy(src, tmp, sizeof(double)*64);
110 }
111
112 void getregion(struct fblock_t* bb, RGBA*pic, int bx, int by, int width, int height)
113 {
114     RGBA*p1 = &pic[by*width*16+bx*16];
115     RGBA*p2 = p1;
116     int linex = width;
117     int y1=0, y2=0, y3=0, y4=0;
118     int u=0,v=0;
119     int x,y;
120     for(y=0;y<8;y++) {
121         for(x=0;x<8;x++) {
122             double r,g,b;
123             r = (p2[x*2].r + p2[x*2+1].r + p2[linex+x*2].r + p2[linex+x*2+1].r)/4.0;
124             g = (p2[x*2].g + p2[x*2+1].g + p2[linex+x*2].g + p2[linex+x*2+1].g)/4.0;
125             b = (p2[x*2].b + p2[x*2+1].b + p2[linex+x*2].b + p2[linex+x*2+1].b)/4.0;
126             bb->u[u++] = (r*-0.169 + g*-0.332 + b*0.500 + 128.0);
127             bb->v[v++] = (r*0.500 + g*-0.419 + b*-0.0813 + 128.0);
128
129             r = p1[x].r; g = p1[x].g; b = p1[x].b;
130             bb->y1[y1++] = (r*0.299 + g*0.587 + b*0.114);
131             r = p1[x+8].r; g = p1[x+8].g; b = p1[x+8].b;
132             bb->y2[y2++] = (r*0.299 + g*0.587 + b*0.114);
133             r = p1[linex*8+x].r; g = p1[linex*8+x].g; b = p1[linex*8+x].b;
134             bb->y3[y3++] = (r*0.299 + g*0.587 + b*0.114);
135             r = p1[linex*8+x+8].r; g = p1[linex*8+x+8].g; b = p1[linex*8+x+8].b;
136             bb->y4[y4++] = (r*0.299 + g*0.587 + b*0.114);
137         }
138         p1+=linex;
139         p2+=linex*2;
140     }
141 }
142
143 int valtodc(int val)
144 {
145     assert(val>=0);
146
147     /* table 12/h.263 */
148     val/=8;
149     /* TODO: what to do for zero values? skip the block? */
150     if(val==0)
151         return 1;
152     if(val==128)
153         return 255;
154     if(val>254)
155         return 254;
156     return val;
157 }
158
159 void codehuffman(TAG*tag, struct huffcode*table, int index)
160 {
161     /* TODO: !optimize! */
162     int i=0;
163     while(table[index].code[i]) {
164         if(table[index].code[i]=='0')
165             swf_SetBits(tag, 0, 1);
166         else
167             swf_SetBits(tag, 1, 1);
168         i++;
169     }
170 }
171
172 int see=0;
173
174 void quantize(double*src, int*dest, int quant, int has_dc)
175 {
176     int t,pos=0;
177     if(has_dc) {
178         dest[0] = (int)src[0]; /*DC*/
179         dest[0] = valtodc(dest[0]);
180         pos++;
181     }
182     for(t=pos;t<64;t++)
183     {
184         dest[t] = (int)src[t];
185         //val = (quant*(2*level+1)-1)+quant&1
186         if(quant&1) {
187             dest[t] = (dest[t]/quant - 1)/2;
188         } else {
189             dest[t] = ((dest[t]+1)/quant - 1)/2;
190         }
191     }
192 }
193
194 int hascoef(int*b, int has_dc)
195 {
196     int t;
197     int pos=0;
198     int range=2;
199     if(has_dc)
200         pos++;
201     for(t=pos;t<64;t++) {
202         if(b[t]<=-range || b[t]>=range)
203             return 1;
204     }
205     return 0;
206 }
207
208 void encode8x8(TAG*tag, int*bb, int has_dc, int has_tcoef)
209 {
210     int t;
211     int pos=0;
212
213     if(has_dc) {
214         swf_SetBits(tag, bb[0], 8);
215         pos++;
216     }
217
218     if(has_tcoef) {
219         int last;
220         /* determine last non-null coefficient */
221         for(last=63;last>=pos;last--) {
222             if(bb[last])
223                 break;
224         }
225         assert(bb[last]);
226         /* blocks without coefficients should not be included
227            in the cbpy/cbpc patterns */
228         while(1) {
229             int run=0;
230             int level=0;
231             int islast=0;
232             int sign=0;
233             int t;
234             while(!bb[pos] && pos<last) {
235                 pos++;
236                 run++;
237             }
238             if(pos==last)
239                 islast=1;
240             level=bb[pos];
241             if(level<0) {
242                 level = -level;
243                 sign = 1;
244             }
245             for(t=0;t<RLE_ESCAPE;t++) {
246                 if(rle_params[t].run == run &&
247                    rle_params[t].level == level &&
248                    rle_params[t].last == islast) {
249                     codehuffman(tag, rle, t);
250                     swf_SetBits(tag, sign, 1);
251                     break;
252                 }
253             }
254             if(t==RLE_ESCAPE) {
255                 codehuffman(tag, rle, RLE_ESCAPE);
256                 /* table 14/h.263 */
257                 assert(level);
258                 if(level<-127) level = -127;
259                 if(level>127) level = 127;
260
261                 swf_SetBits(tag, islast, 1);
262                 swf_SetBits(tag, run, 6);
263                 swf_SetBits(tag, level, 8); //fixme
264             }
265
266             if(islast)
267                 break;
268             pos++;
269         }
270
271         //codehuffman(tag, rle, 58);
272         //swf_SetBits(tag, 1, 1); //sign
273     }
274     see++;
275 }
276
277 void encode_blockI(TAG*tag, RGBA*pic, int bx, int by, int width, int height, int*quant)
278 {
279     struct fblock_t b;
280     int dquant=0; /* TODO: should we take advantage of the dquant feature?*/
281     //int cbpcbits = 3;
282     //int cbpybits = 15;
283     int cbpcbits = 0;
284     int cbpybits = 0;
285     int y1[64],y2[64],y3[64],y4[64],u[64],v[64];
286
287     getregion(&b, pic, bx, by, width, height);
288     dct(b.y1); quantize(b.y1,y1,1,*quant); zigzag(y1); cbpybits|=hascoef(y1, 1)*8;
289     dct(b.y2); quantize(b.y2,y2,1,*quant); zigzag(y2); cbpybits|=hascoef(y2, 1)*4;
290     dct(b.y3); quantize(b.y3,y3,1,*quant); zigzag(y3); cbpybits|=hascoef(y3, 1)*2;
291     dct(b.y4); quantize(b.y4,y4,1,*quant); zigzag(y4); cbpybits|=hascoef(y4, 1)*1;
292     dct(b.u); quantize(b.u,u,1,*quant); zigzag(u); cbpcbits|=hascoef(u, 1)*2;
293     dct(b.v); quantize(b.v,v,1,*quant); zigzag(v); cbpcbits|=hascoef(v, 1)*1;
294
295     if(dquant) {
296         codehuffman(tag, mcbpc_intra, 4+cbpcbits);
297     } else {
298         codehuffman(tag, mcbpc_intra, 0+cbpcbits);
299     }
300     //swf_SetBits(tag, 1, 1); /*cbpc-00 mb_type=3 (no bquant), cbc=0*/
301
302     /* if this was an intra frame, we'd need to code (cbpybits^15) */
303     codehuffman(tag, cbpy, cbpybits);
304
305     if(dquant) {
306         /* 00 01 10 11
307            -1 -2 +1 +2
308         */
309         swf_SetBits(tag, 0x3, 2);
310     }
311
312     /* luminance */
313     encode8x8(tag, y1, 1, cbpybits&8);
314     encode8x8(tag, y2, 1, cbpybits&4);
315     encode8x8(tag, y3, 1, cbpybits&2);
316     encode8x8(tag, y4, 1, cbpybits&1);
317     /*swf_SetBits(tag, 0x1, 8);
318     swf_SetBits(tag, 0xfe, 8);
319     swf_SetBits(tag, 0x1, 8);
320     swf_SetBits(tag, 0x1, 8);*/
321
322     /* chrominance */
323     encode8x8(tag, u, 1, cbpcbits&2);
324     encode8x8(tag, v, 1, cbpcbits&1);
325     /*swf_SetBits(tag, 0xfe, 8);
326     swf_SetBits(tag, 0xfe, 8);*/
327 }
328
329 void swf_SetVideoStreamIFrame(TAG*tag, RGBA*pic, U16 width, U16 height, int frame)
330 {
331     U32 i32;
332     int bx, by, bbx, bby;
333     int quant = 9;
334
335     width=width&~15; height=height&~15;
336
337     swf_SetU16(tag, frame);
338     swf_SetBits(tag, 1, 17); /* picture start code*/
339     swf_SetBits(tag, 0, 5); /* version=0, version 1 would optimize rle behaviour*/
340     swf_SetBits(tag, frame, 8); /* time reference */
341
342     /* write dimensions, taking advantage of some predefined sizes
343        if the opportunity presents itself */
344     i32 = width<<16|height;
345     switch(i32)
346     {
347         case 352<<16|288: swf_SetBits(tag, 2, 3);break;
348         case 176<<16|144: swf_SetBits(tag, 3, 3);break;
349         case 128<<16|96: swf_SetBits(tag, 4, 3);break;
350         case 320<<16|240: swf_SetBits(tag, 5, 3);break;
351         case 160<<16|120: swf_SetBits(tag, 6, 3);break;
352         default:
353             if(width>255 || height>255) {
354                 swf_SetBits(tag, 1, 3);
355                 swf_SetBits(tag, width, 16);
356                 swf_SetBits(tag, height, 16);
357             } else {
358                 swf_SetBits(tag, 0, 3);
359                 swf_SetBits(tag, width, 8);
360                 swf_SetBits(tag, height, 8);
361             }
362     }
363
364     swf_SetBits(tag, 0, 2); /* I-Frame */
365     swf_SetBits(tag, 0, 1); /* No deblock filter */
366     swf_SetBits(tag, quant, 5); /* quantizer (1-31), may be updated later on*/
367     swf_SetBits(tag, 0, 1); /* No extra info */
368
369     bbx = (width+15)/16;
370     bby = (height+15)/16;
371
372     for(by=0;by<bby;by++)
373     {
374         for(bx=0;bx<bbx;bx++)
375         {
376             encode_blockI(tag, pic, bx, by, width, height, &quant);
377         }
378     }
379 }
380
381 int main()
382 {
383     int fi;
384     int t;
385     SWF swf;
386     TAG * tag;
387     RGBA* pic, rgb;
388     SWFPLACEOBJECT obj;
389     int width = 0;
390     int height = 0;
391     int frames = 1;
392     unsigned char*data;
393     char* fname = "/home/kramm/pics/lena.png";
394
395     getimage(fname, &width, &height, &data);
396     pic = (RGBA*)malloc(width*height*sizeof(RGBA));
397     memcpy(pic, data, width*height*sizeof(RGBA));
398     free(data);
399     printf("Compressing %s, size %dx%d\n", fname, width, height);
400
401     memset(&swf,0,sizeof(SWF));
402     memset(&obj,0,sizeof(obj));
403
404     swf.fileVersion    = 6;
405     swf.frameRate      = 29*256;
406     swf.movieSize.xmax = 20*width;
407     swf.movieSize.ymax = 20*height;
408
409     swf.firstTag = swf_InsertTag(NULL,ST_SETBACKGROUNDCOLOR);
410     tag = swf.firstTag;
411     rgb.r = 0x00;rgb.g = 0x00;rgb.b = 0x00;
412     swf_SetRGB(tag,&rgb);
413
414     tag = swf_InsertTag(tag, ST_DEFINEVIDEOSTREAM);
415     swf_SetU16(tag, 33);
416     swf_SetVideoStreamDefine(tag, frames, width, height);
417     
418     for(t=0;t<frames;t++)
419     {
420         tag = swf_InsertTag(tag, ST_VIDEOFRAME);
421         swf_SetU16(tag, 33);
422         swf_SetVideoStreamIFrame(tag, pic, width, height, t);
423
424         tag = swf_InsertTag(tag, ST_PLACEOBJECT2);
425         swf_GetPlaceObject(0, &obj);
426         if(t==0) {
427             obj.depth = 1;
428             obj.id = 33;
429         } else {
430             obj.move = 1;
431             obj.depth = 1;
432             obj.ratio = t;
433         }
434         swf_SetPlaceObject(tag,&obj);
435
436         tag = swf_InsertTag(tag, ST_SHOWFRAME);
437     }
438    
439     tag = swf_InsertTag(tag, ST_END);
440
441     fi = open("video3.swf", O_WRONLY|O_CREAT|O_TRUNC, 0644);
442     if(swf_WriteSWF(fi,&swf)<0) {
443         fprintf(stderr,"WriteSWF() failed.\n");
444     }
445     close(fi);
446     swf_FreeTags(&swf);
447 }