optimized huffman reader.
[swftools.git] / lib / h.263 / video.c
1 /* video.c
2    Shows the structure of a swf file containing video
3
4    Part of the swftools package.
5    
6    Copyright (c) 2003 Matthias Kramm <kramm@quiss.org> */
7
8 #include "../../config.h"
9 #include <unistd.h>
10 #include <stdio.h>
11 #include <fcntl.h>
12 #include <stdarg.h>
13 #include <assert.h>
14 #include "../rfxswf.h"
15 #include "../args.h"
16 #include "h263tables.c"
17
18 static char * filename = 0;
19 static char * indent = "                ";
20 int hex = 0;
21 int debug = 0;
22
23 struct options_t options[] =
24 {
25  {"v","verbose"},
26  {"V","version"},
27  {"d","hex"},
28  {"M","video"},
29  {0,0}
30 };
31
32 /* TODO:
33    * check rle tables
34 */
35 int args_callback_option(char*name,char*val)
36 {
37     if(!strcmp(name, "V")) {
38         printf("swfdump - part of %s %s\n", PACKAGE, VERSION);
39         exit(0);
40     }
41     else if(name[0]=='d') {
42         hex = 1;
43         return 0;
44     }
45     else if(name[0]=='v') {
46         debug = 1;
47         return 0;
48     }
49     else {
50         printf("Unknown option: -%s\n", name);
51         exit(1);
52     }
53
54     return 0;
55 }
56 int args_callback_longoption(char*name,char*val)
57 {
58     return args_long2shortoption(options, name, val);
59 }
60 void args_callback_usage(char*name)
61 {
62     printf("Usage: %s [-at] file.swf\n", name);
63     printf("\t-h , --help\t\t Print help and exit\n");
64     printf("\t-d , --hex\t\t Print hex output of tag data, too\n");
65     printf("\t-v , --verbose\t\t Print debugging information\n");
66     printf("\t-V , --version\t\t Print program version and exit\n");
67 }
68 int args_callback_command(char*name,char*val)
69 {
70     if(filename) {
71         fprintf(stderr, "Only one file allowed. You supplied at least two. (%s and %s)\n",
72                  filename, name);
73     }
74     filename = name;
75     return 0;
76 }
77
78 #define DEBUG if(debug)
79
80 void handleVideoStream(TAG*tag, char*prefix)
81 {
82     U16 id = swf_GetU16(tag);
83     U16 frames = swf_GetU16(tag);
84     U16 width = swf_GetU16(tag);
85     U16 height = swf_GetU16(tag);
86     U8 flags = swf_GetU8(tag); //5-2(videopacket 01=off 10=on)-1(smoothing 1=on)
87     U8 codec = swf_GetU8(tag);
88     printf(" (%d frames, %dx%d", frames, width, height);
89     if(flags&1)
90         printf(" smoothed");
91     if(codec == 2)
92         printf(" sorenson h.263)");
93     else
94         printf(" codec 0x%02x)", codec);
95 }
96
97 int checkhufftable(struct huffcode*code, char*name)
98 {
99     int t=0;
100     while(code[t].code) {
101         int s=0;
102         if(strlen(code[t].code)!=code[t].len) {
103             printf("len mismatch in %s, index %d\n", name, t);
104             exit(1);
105         }
106         if(t != code[t].index) {
107             printf("index mismatch in %s, index %d\n", name, t);
108             exit(1);
109         }
110         while(code[s].code) {
111             char*a = code[s].code;
112             char*b = code[t].code;
113             int ai = s;
114             int bi = t;
115             if(s==t) {s++;continue;}
116             if(code[t].len < code[s].len) {
117                  a = code[t].code;
118                  b = code[s].code;
119                  ai = t; bi = s;
120             }
121             if(!strncmp(a,b,strlen(a))) {
122                 printf("index %d (%s) is prefix of %d (%s)\n", ai, a, bi, b);
123                 exit(1);
124             }
125
126             s++;
127         }
128
129         t++;
130     }
131 }
132
133
134 struct hufftree
135 {
136     struct hufftree*left;//0
137     struct hufftree*right;//1
138     int index;
139 };
140
141 struct hufftree * rle_tree;
142 struct hufftree * mcbpc_intra_tree;
143 struct hufftree * mcbpc_inter_tree;
144 struct hufftree * cbpy_tree;
145 struct hufftree * mvd_tree;
146
147 static void insert(struct hufftree*tree, char*code, int index)
148 {
149     if(!*code) {
150         assert(!tree->left); //shannon conditional
151         assert(!tree->right);
152         tree->left = 0;
153         tree->right = 0;
154         tree->index = index;
155         return;
156     }
157     if(code[0] == '0') {
158         if(!tree->left) {
159             tree->left = (struct hufftree*)malloc(sizeof(struct hufftree));
160             memset(tree->left, 0, sizeof(struct hufftree));
161             tree->left->index = -1;
162         }
163         insert(tree->left, code+1, index);
164         return;
165     } else {
166         assert(code[0] == '1');
167         if(!tree->right) {
168             tree->right = (struct hufftree*)malloc(sizeof(struct hufftree));
169             memset(tree->right, 0, sizeof(struct hufftree));
170             tree->right->index = -1;
171         }
172         insert(tree->right, code+1, index);
173         return;
174     }
175 }
176
177 struct hufftree* huffcode2tree(struct huffcode*code)
178 {
179     struct hufftree* t = malloc(sizeof(struct hufftree));
180     memset(t, 0, sizeof(struct hufftree));
181     t->index = -1;
182     while(code->code) {
183         insert(t, code->code, code->index);
184         code++;
185     }
186     return t;
187 }
188
189 int gethuffvalue(TAG*tag, struct huffcode*code)
190 {
191     int len = 0;
192     char bits[80];
193     while(1) {
194         int t=0,pot=0;
195         bits[len] = swf_GetBits(tag, 1)+0x30;
196         len++;
197         bits[len] = 0;
198         while(code[t].code) {
199             if(!strcmp(bits, code[t].code))
200                 return t;
201             t++;
202             if(code[t].len >= len)
203                 pot++;
204         }
205         if(!pot) {
206             int nr=0;
207             printf("error: %s\n", bits);
208             while(tag->pos < tag->len && nr<80) {
209                 int b = swf_GetBits(tag, 1);
210                 printf("%d", b);
211                 nr++;
212             }
213             if(nr==80)
214                 printf("...");
215             printf("\n");
216             exit(1);
217             return -1;
218         }
219     }
220
221         /*type = 0; // mb-type =3, cbpc = 00
222         if(!bit) {
223             printf("can't handle i-frame mcbpc bits %d yet\n", bit);
224         }
225         bit = swf_GetBits(tag, 1);
226         type = 0; // mb-type =0, cbpc = 00
227         if(!bit) {
228             bit = swf_GetBits(tag, 2);
229             type = 8; // mb-type =2, cbpc = 00
230             if(bit!=3) {
231                 printf("can't handle p-frame mcbpc bits 0-%d yet\n", bit);
232                 exit(1);
233             }
234         }*/
235 }
236                 
237 int gethuffvalue2(TAG*tag, struct huffcode*code, struct hufftree*tree)
238 {
239     while(1) {
240         if(tree->index>=0) {
241             return tree->index;
242         }
243         if(!swf_GetBits(tag, 1)) {
244             assert(tree->left);
245             tree=tree->left;
246         } else {
247             assert(tree->right);
248             tree=tree->right;
249         }
250     }
251 }
252
253 void get_DC_TCOEF(TAG*tag, int t, int has_dc, int has_tcoef)
254 {
255     int dc;
256     int ac;// = swf_GetBits();
257     int index;
258     int pos = 0;
259     //printf("DC:%d\n", dc);
260     if(has_dc) {
261         dc = swf_GetBits(tag, 8);
262         if(dc == 0 || dc == 128) {
263             printf("error: dc=%d\n", dc);
264             exit(1);
265         }
266         DEBUG printf(" %d ", dc);
267         pos++;
268     }
269
270     if(has_tcoef) {
271         DEBUG printf("[");
272         while(1) {
273             int last;
274             int run;
275             int level;
276             index = gethuffvalue2(tag, rle, rle_tree);
277             last = rle_params[index].last;
278             run = rle_params[index].run;
279             level = rle_params[index].level;
280             //DEBUG printf("index:%d\n", index);
281             if(index == RLE_ESCAPE) {
282                 last = swf_GetBits(tag, 1);
283                 run = swf_GetBits(tag, 6);
284                 level = swf_GetBits(tag, 8);
285                 if(run)
286                     DEBUG printf("(%d) E%d", run, level);
287                 else
288                     DEBUG printf("E");
289                 if(level == 0 || level == 128) {
290                     printf("error: level=%d\n", level);
291                     exit(1);
292                 }
293                 level = (int)((signed char)level);
294             } else {
295                 int sign = swf_GetBits(tag, 1);
296                 if(sign)
297                     level = -level;
298                 if(run)
299                     DEBUG printf("(%d) %s%d", run, level>0?"+":"",level);
300                 else
301                     DEBUG printf("%s%d", level>0?"+":"",level);
302             }
303             pos += run+1;
304             //DEBUG printf("run:%d level:%d\n", run, level);
305             if(last) {
306                 DEBUG printf("] pos: %d", pos);
307                 if(pos>64) {
308                     printf("\nerror:bad pos (%d)\n", pos);
309                     exit(1);
310                 }
311                 return;
312             }
313         }
314     }
315 }
316             
317 int readMVD(TAG*tag)
318 {
319     int index = gethuffvalue2(tag, mvd, mvd_tree);
320     DEBUG printf("mvd index:%d\n", index);
321     return index;
322 }
323
324 char has_quant[] = {0,1,0,0,1};
325 char has_mvd[] = {1,1,3,0,0};
326
327 #define TYPE_INTRA 0
328 #define TYPE_INTER 1
329
330 int tagnr = 0;
331
332 void decode_block(TAG*tag, int pictype)
333 {
334     int t;
335     int mb_type = -1, cbpc = -1;
336     int dquant;
337     int cbpy_index, cbpy_value;
338     int intrablock = 0;
339     int type;
340     if(pictype == TYPE_INTER) /* non-intra pictures have a cod flag */
341     {
342         int cod = swf_GetBits(tag, 1);
343         DEBUG printf("cod=%d\n",cod);
344         if(cod) {
345             printf(".");
346             return;
347         }
348     }
349     type = -1;
350     
351     /* read mcbpc */
352
353     if(pictype == TYPE_INTRA) { /* I-frame */
354         type = gethuffvalue2(tag, mcbpc_intra, mcbpc_intra_tree);
355         DEBUG printf("mcbpc=%d\n",type);
356         mb_type = mcbpc_intra_params[type].mb_type;
357         cbpc = mcbpc_intra_params[type].cbpc;
358         if(type == MCBPC_INTRA_STUFFING) {
359             printf("stuffing not supported yet!\n");
360             exit(1); //TODO: goto COD
361         }
362     } 
363     else if(pictype == 1) { /* P-frame */
364         type = gethuffvalue2(tag, mcbpc_inter, mcbpc_inter_tree);
365         DEBUG printf("mcbpc=%d\n",type);
366         mb_type = mcbpc_inter_params[type].mb_type;
367         cbpc = mcbpc_inter_params[type].cbpc;
368         if(type == MCBPC_INTER_STUFFING) {
369             printf("stuffing not supported yet!(2)\n");
370             exit(1); //TODO: goto COD
371         }
372     }
373
374     if(mb_type == 3 || mb_type == 4)
375     {
376         intrablock = 1;
377     }
378
379     printf("%c", "vqVii"[mb_type]);
380
381     DEBUG printf("mcbpc type: %d mb_type:%d cbpc:%d\n", type, mb_type, cbpc);
382
383     /* read cbpy */
384
385     cbpy_index = gethuffvalue2(tag, cbpy, cbpy_tree);
386     cbpy_value = cbpy_index;
387     if(!intrablock)
388         cbpy_value ^= 15;
389     DEBUG printf("cbpy value:%d (%d)\n", cbpy_value, cbpy_index);
390
391
392     /* I 0: 00 mcbpc/cbpy 
393        P 0: 00 cod/mcbpc/cbpy/mvd
394        P 6: 10 cod/mcbpc/cbpy/dquant/mvd
395        P 8: 00 cod/mcbpc/cbpy/mvd/mvd24
396     */
397
398     /* quantizer */
399     if(has_quant[mb_type]) {
400         dquant = swf_GetBits(tag, 2);
401         if(dquant == 0) dquant = -1;
402         else if(dquant == 1) dquant = -2;
403         else if(dquant == 2) dquant = +1;
404         else if(dquant == 3) dquant = +2;
405         DEBUG printf("dquant: %d\n", dquant);
406     }
407
408     if(has_mvd[mb_type]&1) {
409         int x,y;
410         x = readMVD(tag); //horizontal
411         y = readMVD(tag); //vertical
412         if(x==32 && y==32)
413             printf("\b0");
414     }
415     if(has_mvd[mb_type]&2) {
416         /* only in advanced prediction mode */
417         readMVD(tag); //horizontal
418         readMVD(tag); //vertical
419         readMVD(tag); //horizontal
420         readMVD(tag); //vertical
421         readMVD(tag); //horizontal
422         readMVD(tag); //vertical
423     }
424
425     for(t=0;t<4;t++) {
426         int has_intradc = intrablock;
427         int has_tcoef = cbpy_value & (8>>t);
428         DEBUG printf("luminance%d ", t);
429         get_DC_TCOEF(tag, t, has_intradc, has_tcoef); /*luminance - affected by cbpy*/
430         DEBUG printf("\n");
431     }
432     for(t=0;t<2;t++) {
433         int has_intradc = intrablock;
434         int has_tcoef = cbpc & (2>>t);
435         DEBUG printf("chrominance%d ", t); 
436         get_DC_TCOEF(tag, t, has_intradc, has_tcoef); /*chrominance - affected by mcbc*/
437         DEBUG printf("\n");
438     }
439 }
440
441 void handleVideoFrame(TAG*tag, char*prefix)
442 {
443     U32 code, version, reference, sizeflags;
444     U32 width, height;
445     U8 blocktype, pictype;
446     U16 id = swf_GetU16(tag);
447     U16 frame = swf_GetU16(tag);
448     U8 deblock,flags, tmp, bit;
449     U32 quantizer, extrainfo;
450     int skipped = 0;
451     int pos=0;
452     int num;
453     int disposable = 0;
454     int blocknum;
455     int bbx,bby,bx,by;
456     char*types[] = {"intra- (I-)frame", "inter- (P-)frame", "disposable interframe", "<reserved>"};
457     printf("============================= frame %d ===================================", frame);
458
459     /* video packet follows */
460     printf("\n");
461     code = swf_GetBits(tag, 17);
462     if(code!=1) {
463         printf("code: %x (?)\n", code);
464         return;
465     }
466     version = swf_GetBits(tag, 5); /*actually, part of the picture start code */
467     //printf("version: %x\n", version); /*usually 0*/
468     if(version >= 1) {
469         /* version 1 has some different transform coefficient coding which we
470            can't handle yet */
471         printf("spark version %d not supported yet\n", version);
472         exit(1);
473     }
474     reference = swf_GetBits(tag, 8);
475     DEBUG printf("reference: %d\n", reference); /*usually same as frame number (unless frames were skipped while encoding)*/
476
477     sizeflags = swf_GetBits(tag, 3);
478     switch(sizeflags)
479     {
480         case 0: width = swf_GetBits(tag,8); height = swf_GetBits(tag,8); break;
481         case 1: width = swf_GetBits(tag, 16); height = swf_GetBits(tag, 16); break;
482         case 2: width = 352; height = 288; break;
483         case 3: width = 176; height = 144; break;
484         case 4: width = 128; height = 96; break;
485         case 5: width = 320; height = 240; break;
486         case 6: width = 160; height = 120; break;
487         case 7: width = -1; height = -1;/*reserved*/ break;
488     }
489     
490     pictype = swf_GetBits(tag, 2);
491     if(pictype==3) {
492         printf("error: unknown pictype: %d\n", pictype);
493         exit(1);
494     }
495     if(pictype==2)  {
496         pictype = TYPE_INTER;
497         disposable = 1;
498     }
499     if(pictype==TYPE_INTER)
500         printf("INTER P%s", disposable?" (disposeable)":"");
501     else
502         printf("INTRA I");
503
504     deblock = swf_GetBits(tag, 1); /*usually 0*/
505     DEBUG printf("deblock: %d\n", deblock);
506     quantizer = swf_GetBits(tag, 5); /*usually 9*/
507     DEBUG printf("quantizer: %d\n", quantizer);
508
509     extrainfo = swf_GetBits(tag, 1); /*usually none */
510     while(extrainfo) {
511         extrainfo = swf_GetBits(tag, 8);
512         printf("extrainfo: %02x\n", extrainfo);
513         extrainfo = swf_GetBits(tag, 1);
514     }
515
516     /* macro block */
517     bbx = (width+15)/16;
518     bby = (height+15)/16;
519     blocknum = bbx*bby;
520     printf("%dx%d [blocks: %dx%d=%d]\n", width, height, bbx,bby, blocknum);
521
522     /*if(pictype == TYPE_INTER)
523         return;*/
524     /*if(pictype == TYPE_INTRA)
525         return;*/
526
527     /*tagnr++;
528     if(tagnr!=2)
529         return;*/
530
531     DEBUG printf("\n");
532     for(by=0;by<bby;by++)
533     {
534         for(bx=0;bx<bbx;bx++)
535         {
536             decode_block(tag, pictype);
537         }
538         printf("\n");
539     }
540 }
541
542 void hexdumpTag(TAG*tag, char* prefix)
543 {
544     int t;
545     printf("                %s-=> ",prefix);
546     for(t=0;t<tag->len;t++) {
547         printf("%02x ", tag->data[t]);
548         if((t && ((t&15)==15)) || (t==tag->len-1))
549         {
550             if(t==tag->len-1)
551                 printf("\n");
552             else
553                 printf("\n                %s-=> ",prefix);
554         }
555     }
556 }
557
558 int main (int argc,char ** argv)
559 {
560     TAG*tag;
561     SWF swf;
562     int f;
563     char prefix[128];
564     int filesize = 0;
565     prefix[0] = 0;
566
567     checkhufftable(rle, "rle");
568     checkhufftable(mcbpc_intra, "intra");
569     checkhufftable(mcbpc_inter, "inter");
570     checkhufftable(cbpy, "cbpy");
571     checkhufftable(mvd, "mvd");
572
573     rle_tree = huffcode2tree(rle);
574     mcbpc_intra_tree = huffcode2tree(mcbpc_intra);
575     mcbpc_inter_tree = huffcode2tree(mcbpc_inter);
576     cbpy_tree = huffcode2tree(cbpy);
577     mvd_tree = huffcode2tree(mvd);
578
579     processargs(argc, argv);
580     if(!filename) {
581         fprintf(stderr, "You must supply a filename.\n");
582         return 1;
583     }
584
585     f = open(filename,O_RDONLY);
586
587     if (f<0) {
588         perror("Couldn't open file: ");
589         exit(1);
590     }
591     if FAILED(swf_ReadSWF(f,&swf)) {
592         fprintf(stderr, "%s is not a valid SWF file or contains errors.\n",filename);
593         close(f);
594         exit(1);
595     }
596
597     close(f);
598
599     printf("[HEADER]        File version: %d\n", swf.fileVersion);
600     if(swf.compressed) {
601         printf("[HEADER]        File is zlib compressed.");
602         if(filesize && swf.fileSize)
603             printf(" Ratio: %02d%%\n", filesize*100/(swf.fileSize));
604         else
605             printf("\n");
606     }
607     printf("[HEADER]        File size: %ld%s\n", swf.fileSize, swf.compressed?" (Depacked)":"");
608     printf("[HEADER]        Frame rate: %f\n",swf.frameRate/256.0);
609     printf("[HEADER]        Frame count: %d\n",swf.frameCount);
610     printf("[HEADER]        Movie width: %.2f",(swf.movieSize.xmax-swf.movieSize.xmin)/20.0);
611     if(swf.movieSize.xmin)
612         printf(" (left offset: %.2f)\n", swf.movieSize.xmin/20.0);
613     else
614         printf("\n");
615     printf("[HEADER]        Movie height: %.2f",(swf.movieSize.ymax-swf.movieSize.ymin)/20.0);
616     if(swf.movieSize.ymin)
617         printf(" (top offset: %.2f)\n", swf.movieSize.ymin/20.0);
618     else
619         printf("\n");
620
621     tag = swf.firstTag;
622
623     while(tag) {
624         char*name = swf_TagGetName(tag);
625         char myprefix[128];
626         //printf("[%03x] %9ld %s%s", tag->id, tag->len, prefix, swf_TagGetName(tag));
627
628         if(swf_isDefiningTag(tag)) {
629             U16 id = swf_GetDefineID(tag);
630             //printf(" defines id %04d", id);
631         }
632         else if(swf_isPseudoDefiningTag(tag)) {
633             U16 id = swf_GetDefineID(tag);
634             //printf(" adds information to id %04d", id);
635         }
636
637         if(tag->id == ST_VIDEOFRAME) {
638             handleVideoFrame(tag, myprefix);
639             //printf("\n");
640         }
641         else if(tag->id == ST_DEFINEVIDEOSTREAM) {
642             handleVideoStream(tag, myprefix);
643             printf("\n");
644         }
645         else {
646             //printf("\n");
647         }
648
649         sprintf(myprefix, "                %s", prefix);
650
651         if(tag->len && hex) {
652             hexdumpTag(tag, prefix);
653         }
654         tag = tag->next;
655         fflush(stdout);
656     }
657
658     swf_FreeTags(&swf);
659     return 0;
660 }
661
662