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