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