File Coverage

blib/lib/Video/Dumper/QuickTime.pm
Criterion Covered Total %
statement 463 962 48.1
branch 58 142 40.8
condition 9 23 39.1
subroutine 91 244 37.3
pod 40 234 17.0
total 661 1605 41.1


line stmt bran cond sub pod time code
1             package Video::Dumper::QuickTime;
2            
3             require 5.007003; # for Encode
4            
5 1     1   61138 use strict;
  1         3  
  1         65  
6 1     1   5 use warnings;
  1         2  
  1         35  
7 1     1   5 use Carp;
  1         6  
  1         102  
8 1     1   3125 use Encode;
  1         29443  
  1         111  
9            
10             BEGIN {
11 1     1   10 use Exporter ();
  1         2  
  1         23  
12 1     1   5 use vars qw($VERSION @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS);
  1         2  
  1         181  
13 1     1   2 $VERSION = '1.0005';
14 1         20 @ISA = qw(Exporter);
15 1         3 @EXPORT = qw();
16 1         2 @EXPORT_OK = qw();
17 1         13506 %EXPORT_TAGS = ();
18             }
19            
20             =head1 NAME
21            
22             Video::Dumper::QuickTime - Dump QuickTime movie file structure
23            
24             =head1 VERSION
25            
26             Version 1.0004
27            
28             =head1 SYNOPSIS
29            
30             use Video::Dumper::QuickTime;
31             my $file = QuickTime->new( -filename => $filename, -progress => \&showProgress );
32            
33             eval {$file->Dump ()};
34             print "Error during processing: $@\n" if $@;
35            
36             my $dumpStr = $file->Result ();
37            
38             =head1 DESCRIPTION
39            
40             Video::Dumper::QuickTime parses a QuickTime movie file and generates a
41             multi-line string describing the structure of the file.
42            
43             The module is intended primarily as a diagnostic tool, although it would be
44             possible to subclass Video::Dumper::QuickTime to extract various sections of a
45             QuickTime file.
46            
47             =head1 Methods
48            
49             The methods fall into two groups - those required to use the method, and those
50             useful for subclassing it.
51            
52             The general use methods are presented first, followed by the accessor and
53             utility methods of more use in subclassing.
54            
55             =head2 General use methods
56            
57             =cut
58            
59             =head3 new
60            
61             Create a new C instance.
62            
63             my $msi = QuickTime->new (-filename => $filename);
64            
65             =over 4
66            
67             =item I<-filename>: required
68            
69             the QuickTime movie file to open
70            
71             =item I<-noise>: optional
72            
73             Set error reporting level.
74            
75             Currently recognised levels are:
76            
77             =over 4
78            
79             =item 0: no reporting
80            
81             =item 1: report unknown atoms
82            
83             =item 2: report non-decoded parameters and nested atoms
84            
85             =back
86            
87             =item I<-progress>: optional
88            
89             reference to a callback sub to display parsing progress.
90            
91             The progress sub is passed two parameters, the current position and the total
92             work to be done. A typical callback sub would look like:
93            
94             sub showProgress {
95             my ( $pos, $total ) = @_;
96             ...
97             }
98            
99             =back
100            
101             =cut
102            
103             sub new {
104 1     1 1 50 my $proto = shift;
105 1   33     9 my $class = ref ($proto) || $proto;
106 1         4 my $self = bless {}, $class;
107            
108 1         5 $self = $self->_init (@_);
109            
110 1         4 return $self;
111             }
112            
113             sub _init {
114 1     1   3 my $self = shift;
115 1         4 my %param = @_;
116 1         6 $self->_init_attributes (@_);
117 1   50     7 $self->{indentStr} ||= '. ';
118 1   50     8 $self->{indent} ||= '';
119 1         3 $self->{result} = '';
120 1         3 $self->{unknownAtoms} = {};
121 1 50       7 $self->{noise} = 2 unless exists $self->{noise};
122 1         3 return $self;
123             }
124            
125             sub _init_attributes {
126 1     1   3 my $self = shift;
127 1         3 my %raw = @_;
128 1         2 my %param;
129            
130 1         5 for (keys %raw) {
131 1         8 /^-?(.+)/;
132 1         7 $param{$1} = $raw{$_};
133             }
134            
135 1         11 $self->{noise} = $param{noise};
136 1         3 $self->{parsedSize} = 0;
137 1 50       5 $self->{progress} = $param{progress} if exists $param{progress};
138            
139 1         4 my $filename = $param{filename};
140 1 50       4 croak "filename parameter required" unless defined $filename;
141 1         2 $self->{filename} = $filename;
142 1 50       16 croak "File not found: $filename" unless -f $filename;
143            
144 1         13 $self->{filesize} = -s $filename;
145 1         10 $self->{nextUpdate} = $self->{filesize} / 100;
146            
147 1 50       54 open $self->{handle}, '<', $self->{filename}
148             or die "Can't open $self->{filename}: $!\n";
149 1         7 binmode $self->{handle};
150             }
151            
152             sub _closeFile {
153 1     1   2 my $self = shift;
154            
155 1 50       37 close $self->{handle} if $self->{handle};
156             }
157            
158             =head3 Dump
159            
160             Parse the movie file and return the result string.
161            
162             =cut
163            
164            
165             sub Dump { # Find top level atoms
166 1     1 1 694 my $self = shift;
167 1         2 my $pos = 0;
168            
169 1         2 eval {
170 1         2 push @{$self->{atomStack}}, (['global', {}]);
  1         5  
171 1         35 $pos = $self->describeAtom ($pos) while !eof ($self->{handle});
172             };
173            
174 1         7 $self->_closeFile ();
175            
176 1 50 33     10 die $@ if $@ and $@ ne "end\n";
177 1         6 return $self->{result};
178             }
179            
180             =head3 IndentStr
181            
182             Returns the string used for indenting in the result string.
183            
184             =cut
185            
186             sub IndentStr {
187 0     0 1 0 my $self = shift;
188            
189 0         0 return $self->{indentStr};
190             }
191            
192             =head3 Result
193            
194             C returns the result string generated by Dump. This can be usefull if
195             you need to wrap the call the C in an C, but still want any output
196             that was generated in the case when an exception was thrown.
197            
198             =cut
199            
200             sub Result {
201 1     1 1 7 my $self = shift;
202            
203 1         54 return $self->{result};
204             }
205            
206            
207             =head2 Subclassing utility methods
208            
209             The following methods are used internally to manipulate and decode the data in
210             the movie file. The present documentation if rather scanty, but will be improved
211             over time (sooner if you ask for it!).
212            
213             Generally the method name and parameter list are given. Parameters in C<[]> are
214             optional. Parameters followed by C<...> may be repeated as required.
215            
216             =head3 read ($length[, $offset]);
217            
218             C takes two parameters - a length (required) and a starting offset
219             (optional).
220            
221             C returns a string containing the number of bytes asked for starting from
222             the current position or the given offset.
223            
224             C will C if the end of the file is reached without reading
225             any further bytes.
226            
227             C will C if fewer than the requested bytes
228             are available.
229            
230             =cut
231            
232             sub read {
233 538     538 1 929 my $self = shift;
234 538         2006 my ($len, $offset) = @_;
235 538         1047 my $buf;
236            
237 538 100       5508 seek $self->{handle}, $offset, 0 if defined $offset;
238            
239 538         5101 my $n = read $self->{handle}, $buf, $len;
240 538 50       3252 croak 'read failed' unless defined $n;
241 538 100       2939 die "end\n" if !$n;
242 537 50       2204 croak "short read ($len/$n)" unless $n == $len;
243            
244 537 50       2116 if (defined $self->{progress}) {
245 0         0 $self->{parsedSize} += $n;
246            
247 0 0       0 if ($self->{nextUpdate} >= $self->{parsedSize}) {
248 0         0 $self->{nextUpdate} += $self->{filesize} / 100;
249 0         0 $self->{progress}->($self->{parsedSize}, $self->{filesize});
250             }
251             }
252            
253 537         5044 return $buf;
254             }
255            
256            
257             =head3 append ($str, ...)
258            
259             Append a list of strings to the result string.
260            
261             If the last character in the result string before the append was a new line
262             character prepend the current indent string first.
263            
264             =cut
265            
266             sub append {
267 817     817 1 2154 my $self = shift;
268 817         3667 my $lastChar = substr $self->{result}, -1;
269            
270 817 100       5224 $self->{result} .= $self->{indent} if $lastChar eq "\n";
271 817         5928 $self->{result} .= join '', @_;
272             }
273            
274            
275             =head3 findAtom ($attribute[, $regexp])
276            
277             Search down the atom stack for an atom with a matching attribute and return the
278             atom if found,or undef if no matching atom is found.
279            
280             If the regular expression is provided the value of the attribute mustmatch
281             against the regular expression. The regex should be generated using C.
282            
283             See also L and L
284            
285             =cut
286            
287             sub findAtom {
288 9     9 1 14 my ($self, $attrib, $regexp) = @_;
289 9         12 my $limit = @{$self->{atomStack}};
  9         23  
290 9         13 my $dataRef;
291 9         12 my $index = -1;
292            
293 9         30 while (-$index < $limit) {
294 34         38 $dataRef = \%{$self->{atomStack}[$index--][1]};
  34         95  
295 34 100       144 next if !exists $dataRef->{$attrib};
296 11 100       25 last if !defined $regexp;
297 4 100       42 last if $dataRef->{$attrib} =~ $regexp;
298             }
299            
300 9         120 return $dataRef;
301             }
302            
303             =head3 findAtomValue ($attribute[, $regexp])
304            
305             Search down the atom stack for an atom with a matching attribute and return the
306             attribute value if found, or '' if no matching atom is found.
307            
308             If the regular expression is provided the value of the attribute mustmatch
309             against the regular expression. The regex should be generated using C.
310            
311             See also L and L
312            
313             =cut
314            
315             sub findAtomValue {
316 7     7 1 15 my ($self, $attrib, $regexp) = @_;
317 7         29 my $dataRef = $self->findAtom ($attrib, $regexp);
318            
319 7 50       60 return $dataRef ? $dataRef->{$attrib} : '';
320             }
321            
322             =head3 setParentAttrib ($attrib, $value)
323            
324             Set an attribute on the parent of the current atom.
325            
326             =cut
327            
328             sub setParentAttrib {
329 11     11 1 64 my ($self, $attrib, $value) = @_;
330 11         121 $self->{atomStack}[-2][1]{$attrib} = $value;
331             }
332            
333             =head3 getParentAttribs
334            
335             Return a hash ref containing all the attribute => value pairs for the parent
336             atom.
337            
338             =cut
339            
340             sub getParentAttribs {
341 0     0 1 0 my ($self) = @_;
342            
343 0         0 return $self->{atomStack}[-2][1];
344             }
345            
346             =head3 describeAtom ($pos)
347            
348             Add a descriptive header to the result string for the atom at the given
349             position.
350            
351             If L and L are available for the atom they are called to
352             dump the atom's contents. If specific decoding is not available the atom is
353             flagged as unknown and raw data for it is shown in the Dump result.
354            
355             =head3 dump_xxxx ($pos, $length)
356            
357             C is the four char code for an atom. C is called with the start
358             and length of an atom of type C and is expected to decode the atom's
359             contents.
360            
361             =head3 name_xxxx
362            
363             C is the four char code for an atom. C returns a string to be
364             shown as the name for the atom C.
365            
366             =cut
367            
368             sub describeAtom {
369 60     60 1 120 my $self = shift;
370 60         72 my $pos = shift;
371 60         155 my ($len, $key) = unpack ("Na4", $self->read (8, $pos));
372            
373 59 100 66     756 if (!defined $len or $len == 0) {
374 3         9 $self->append ("End entry\n");
375 3         14 return $pos + 4;
376             }
377            
378 56 50       806 $key = 'x' . unpack ('H8', $key) if $key =~ /[\x00-\x1f]/;
379 56         349 $key =~ tr/ /_/;
380 56         170 $key =~ s/([^\w \d_])/sprintf "%02X", ord ($1)/ge;
  2         8  
381            
382 56 50       753 if (!length $key) {
383 0         0 return $pos;
384             }
385            
386 56         496 my $member = "dump_$key";
387 56         163 my $name = "name_$key";
388            
389 56 100       13668 $name = $self->can ($name) ? $self->$name () . ' ' : '';
390            
391 56         416 my $header = sprintf "'%s' %s@ %s (0x%08x) for %s (0x%08x):", $key, $name,
392             groupDigits ($pos), $pos, groupDigits ($len), $len;
393            
394 56         364 $self->append ("$header\n");
395 56         180 $self->{indent} .= $self->{indentStr};
396 56 100       477 if ($self->can ($member)) {
397 50         54 push @{$self->{atomStack}}, [$key, {}];
  50         1330  
398 50         464 $self->$member ($pos, $len);
399 50         147 pop @{$self->{atomStack}};
  50         112  
400             } else {
401 6         17 $self->append (" Unhandled: length = " . groupDigits ($len) . "\n");
402 6 100       47 $self->dumpBlock ($pos + 8, $len > 24 ? 16 : $len - 8) if $len > 8;
    50          
403 6 50 33     119 if (!$self->{unknownAtoms}{$key}++ && $self->{noise}) {
404 0         0 printf "Unknown atom '%s' %s (0x%08x) long at %s (0x%08x))\n", $key,
405             groupDigits ($pos), $pos, groupDigits ($len), $len;
406             }
407             }
408 56         400 $self->{indent} = substr $self->{indent}, length $self->{indentStr};
409 56         502 return $pos + $len;
410             }
411            
412             =head3 describeAtoms ($pos, $count)
413            
414             Calls L for each of $count atoms starting at $pos.
415            
416             =cut
417            
418             sub describeAtoms {
419 0     0 1 0 my $self = shift;
420 0         0 my ($pos, $count) = @_;
421            
422 0         0 $pos = $self->describeAtom ($pos) while $count--;
423 0         0 return $pos;
424             }
425            
426             =head3 describeAtomsIn ($pos, $end)
427            
428             Calls L for each atom starting at $pos and before $end.
429            
430             =cut
431            
432             sub describeAtomsIn {
433 18     18 1 30 my $self = shift;
434 18         24 my ($pos, $end) = @_;
435            
436 18         123 $pos = $self->describeAtom ($pos) while $pos < $end;
437             }
438            
439             =head3 unwrapAtoms ($pos, $len)
440            
441             Calls L for each atom in the given range. Used to decode an
442             atom that is purely a container for other atoms.
443            
444             =cut
445            
446             sub unwrapAtoms {
447 16     16 1 27 my $self = shift;
448 16         32 my ($pos, $len) = @_;
449            
450 16         129 $self->describeAtomsIn ($pos + 8, $pos + $len);
451             }
452            
453             =head3 atomList ($pos, $len)
454            
455             Dump a version and flags header followed by a list of atoms.
456            
457             =cut
458            
459             sub atomList {
460 2     2 1 3 my $self = shift;
461 2         4 my ($pos, $len) = @_;
462            
463 2         7 $self->append ('Version: ', unpack ('C', $self->read (1)), "\n");
464 2         197 $self->append ('Flags: ', unpack ('B24', $self->read (3)), "\n");
465 2         9 NToSigned ($self->read (4));
466 2         8 $self->describeAtomsIn ($pos + 16, $pos + $len);
467             }
468            
469             #sub construct_hash {
470             # my ($input) = @_;
471             # my %hash;
472             #
473             # while ( length($input) > 0 ) {
474             # my ($len) = NToSigned( substr( $input, 0, 4, '' ) );
475             # my ($cntnt) = substr( $input, 0, $len - 4, '' );
476             # my ($type) = substr( $cntnt, 0, 4, '' );
477             #
478             # if ( exists $hash{$type} ) {
479             # my @a = grep( $type, keys %hash );
480             # $hash{ $type . length(@a) } = $cntnt;
481             # }
482             # else {
483             # $hash{$type} = $cntnt;
484             # }
485             # }
486             # %hash;
487             #}
488            
489             sub dump_A9cmt {
490 0     0 0 0 my $self = shift;
491 0         0 $self->showStr (@_);
492             }
493            
494             sub dump_A9cpy {
495 1     1 0 1 my $self = shift;
496 1         6 $self->showStr (@_);
497             }
498            
499             sub dump_A9des {
500 0     0 0 0 my $self = shift;
501 0         0 $self->showStr (@_);
502             }
503            
504             sub dump_A9inf {
505 0     0 0 0 my $self = shift;
506 0         0 $self->showStr (@_);
507             }
508            
509             sub dump_A9nam {
510 1     1 0 2 my $self = shift;
511 1         3 $self->showStr (@_);
512             }
513            
514             sub dump_actn {
515 0     0 0 0 my $self = shift;
516 0         0 my ($pos, $len) = @_;
517 0         0 my %actionTypes = (
518             1 => 'mcActionIdle',
519             2 => 'mcActionDraw',
520             3 => 'mcActionActivate',
521             4 => 'mcActionDeactivate',
522             5 => 'mcActionMouseDown',
523             6 => 'mcActionKey',
524             8 => 'mcActionPlay',
525             12 => 'mcActionGoToTime',
526             14 => 'mcActionSetVolume',
527             15 => 'mcActionGetVolume',
528             18 => 'mcActionStep',
529             21 => 'mcActionSetLooping',
530             22 => 'mcActionGetLooping',
531             23 => 'mcActionSetLoopIsPalindrome',
532             24 => 'mcActionGetLoopIsPalindrome',
533             25 => 'mcActionSetGrowBoxBounds',
534             26 => 'mcActionControllerSizeChanged',
535             29 => 'mcActionSetSelectionBegin',
536             30 => 'mcActionSetSelectionDuration',
537             32 => 'mcActionSetKeysEnabled',
538             33 => 'mcActionGetKeysEnabled',
539             34 => 'mcActionSetPlaySelection',
540             35 => 'mcActionGetPlaySelection',
541             36 => 'mcActionSetUseBadge',
542             37 => 'mcActionGetUseBadge',
543             38 => 'mcActionSetFlags',
544             39 => 'mcActionGetFlags',
545             40 => 'mcActionSetPlayEveryFrame',
546             41 => 'mcActionGetPlayEveryFrame',
547             42 => 'mcActionGetPlayRate',
548             43 => 'mcActionShowBalloon',
549             44 => 'mcActionBadgeClick',
550             45 => 'mcActionMovieClick',
551             46 => 'mcActionSuspend',
552             47 => 'mcActionResume',
553             48 => 'mcActionSetControllerKeysEnabled',
554             49 => 'mcActionGetTimeSliderRect',
555             50 => 'mcActionMovieEdited',
556             51 => 'mcActionGetDragEnabled',
557             52 => 'mcActionSetDragEnabled',
558             53 => 'mcActionGetSelectionBegin',
559             54 => 'mcActionGetSelectionDuration',
560             55 => 'mcActionPrerollAndPlay',
561             56 => 'mcActionGetCursorSettingEnabled',
562             57 => 'mcActionSetCursorSettingEnabled',
563             58 => 'mcActionSetColorTable',
564             59 => 'mcActionLinkToURL',
565             60 => 'mcActionCustomButtonClick',
566             61 => 'mcActionForceTimeTableUpdate',
567             62 => 'mcActionSetControllerTimeLimits',
568             63 => 'mcActionExecuteAllActionsForQTEvent',
569             64 => 'mcActionExecuteOneActionForQTEvent',
570             65 => 'mcActionAdjustCursor',
571             66 => 'mcActionUseTrackForTimeTable',
572             67 => 'mcActionClickAndHoldPoint',
573             68 => 'mcActionShowMessageString',
574             69 => 'mcActionShowStatusString',
575             70 => 'mcActionGetExternalMovie',
576             71 => 'mcActionGetChapterTime',
577             72 => 'mcActionPerformActionList',
578             73 => 'mcActionEvaluateExpression',
579             74 => 'mcActionFetchParameterAs',
580             75 => 'mcActionGetCursorByID',
581             76 => 'mcActionGetNextURL',
582             77 => 'mcActionMovieChanged',
583             78 => 'mcActionDoScript',
584             79 => 'mcActionRestartAtTime',
585             80 => 'mcActionGetIndChapter',
586             81 => 'mcActionLinkToURLExtended',
587             );
588            
589 0   0     0 my $type = $actionTypes{NToSigned ($self->read (4))} || 'unknown';
590 0         0 $self->append ("Action type: $type\n");
591 0         0 $self->append ("Reserved\n");
592 0         0 $self->unwrapAtoms ($pos + 12, $len - 12);
593             }
594            
595             sub name_actn {
596 0     0 0 0 my $self = shift;
597 0         0 return 'Action';
598             }
599            
600             sub dump_alis {
601 2     2 0 4 my $self = shift;
602 2         3 my ($pos, $len) = @_;
603            
604 2         8 $self->append ('File #', groupDigits (NToSigned ($self->read (4))), "\n");
605             }
606            
607             sub name_alis {
608 2     2 0 5 my $self = shift;
609 2         6 return 'File alias';
610             }
611            
612             sub dump_clip {
613 0     0 0 0 my $self = shift;
614 0         0 $self->unwrapAtoms (@_);
615             }
616            
617             sub name_clip {
618 0     0 0 0 my $self = shift;
619 0         0 return 'Clipping region';
620             }
621            
622             sub dump_cmov {
623 0     0 0 0 my $self = shift;
624 0         0 $self->unwrapAtoms (@_);
625             }
626            
627             sub name_cmov {
628 0     0 0 0 my $self = shift;
629 0         0 return 'Compressed movie';
630             }
631            
632             sub dump_code {
633 0     0 0 0 my $self = shift;
634 0         0 my ($pos, $len) = @_;
635            
636 0         0 $self->showUnknown ();
637 0         0 $self->unwrapAtoms ($pos + 12, $len - 12);
638             }
639            
640             sub name_code {
641 0     0 0 0 my $self = shift;
642 0         0 return 'Code resource';
643             }
644            
645             sub dump_data {
646 0     0 0 0 my $self = shift;
647 0         0 $self->unwrapAtoms (@_);
648             }
649            
650            
651             sub name_data {
652 0     0 0 0 my $self = shift;
653 0         0 return 'Data resource';
654             }
655            
656             sub dump_dcom {
657 0     0 0 0 my $self = shift;
658            
659 0         0 $self->append ('Compression type: ', $self->get4Char (), "\n");
660             }
661            
662             sub name_dcom {
663 0     0 0 0 my $self = shift;
664 0         0 return 'Compression type';
665             }
666            
667             sub dump_dflt {
668 0     0 0 0 my $self = shift;
669            
670 0         0 $self->atomList (@_);
671             }
672            
673             sub name_dflt {
674 0     0 0 0 my $self = shift;
675 0         0 return 'Shared frame';
676             }
677            
678             sub dump_dinf {
679 2     2 0 5 my $self = shift;
680            
681 2         12 $self->unwrapAtoms (@_);
682             }
683            
684             sub name_dint {
685 0     0 0 0 my $self = shift;
686 0         0 return 'Media location';
687             }
688            
689             sub dump_dref {
690 2     2 0 3 my $self = shift;
691            
692 2         7 $self->append ("\n");
693 2         9 $self->atomList (@_);
694             }
695            
696             sub name_dref {
697 2     2 0 6 my $self = shift;
698 2         5 return 'Data references';
699             }
700            
701             sub dump_edts {
702 2     2 0 7 my $self = shift;
703 2         15 $self->unwrapAtoms (@_);
704             }
705            
706             sub name_edts {
707 2     2 0 127 my $self = shift;
708 2         13 return "Edit list";
709             }
710            
711             sub dump_elst {
712 2     2 0 5 my $self = shift;
713            
714 2         8 $self->append ('Version: ', unpack ('C', $self->read (1)), "\n");
715 2         80 $self->append ('Flags: ', unpack ('B24', $self->read (3)), "\n");
716            
717 2         7 my $items = NToSigned ($self->read (4));
718 2         115 for (1 .. $items) {
719 2         11 $self->append (" $_\n");
720 2         10 my $scale = $self->findAtomValue ('timescale');
721 2         7 my $duration = NToSigned ($self->read (4));
722 2 50       12 my $durSecs = $scale ? $duration / $scale : '---';
723 2         239 $self->append (" Duration: $duration ticks (${durSecs} seconds)\n");
724 2         7 $self->append (' Start: ', NToSigned ($self->read (4)), "\n");
725 2         9 $self->append (' Rate: ', NToFixed ($self->read (4)), "\n");
726             }
727             }
728            
729             sub name_elst {
730 2     2 0 6 my $self = shift;
731 2         81 return 'Media edit segment defs';
732             }
733            
734             sub dump_enfs {
735 0     0 0 0 my $self = shift;
736 0         0 my ($pos, $len) = @_;
737            
738 0         0 $self->append ('Enabled: ', cToBool ($self->read (1)), "\n");
739             }
740            
741             sub name_enfs {
742 0     0 0 0 my $self = shift;
743 0         0 return 'Enable Frame Stepping';
744             }
745            
746             sub dump_evnt {
747 0     0 0 0 my $self = shift;
748 0         0 my ($pos, $len) = @_;
749            
750 0         0 $self->append ('Event type: ', $self->get4Char (), "\n");
751            
752 0         0 NToSigned ($self->read (4));
753 0         0 $self->append ("Reserved\n");
754 0         0 $self->read (4);
755 0         0 $self->unwrapAtoms ($pos + 12, $len - 12);
756             }
757            
758             sub name_evnt {
759 0     0 0 0 my $self = shift;
760 0         0 return 'Sprite event';
761             }
762            
763             sub dump_expr {
764 0     0 0 0 my $self = shift;
765 0         0 my ($pos, $len) = @_;
766            
767 0         0 $self->showUnknown ();
768 0         0 $self->unwrapAtoms ($pos + 12, $len - 12);
769             }
770            
771             sub name_expr {
772 0     0 0 0 my $self = shift;
773 0         0 return 'Expression';
774             }
775            
776             sub dump_free {
777 1     1 0 2 my $self = shift;
778 1         3 my ($pos, $len) = @_;
779            
780 1         20 $self->append ("Padding = $len bytes\n");
781 1         3 $self->{parsedSize} += $len - 8;
782             }
783            
784             sub name_free {
785 1     1 0 3 my $self = shift;
786 1         3 return 'Unused space';
787             }
788            
789             sub dump_ftyp {
790 1     1 0 2 my $self = shift;
791 1         3 $self->append (unpack ("a4", $self->read (4)), "\n");
792             }
793            
794             sub name_ftyp {
795 1     1 0 3 my $self = shift;
796 1         4 return 'File type';
797             }
798            
799             sub dump_gmhd {
800 0     0 0 0 my $self = shift;
801 0         0 $self->unwrapAtoms (@_);
802             }
803            
804             sub name_gmhd {
805 0     0 0 0 my $self = shift;
806 0         0 return 'Generic media header';
807             }
808            
809             sub dump_gmin {
810 0     0 0 0 my $self = shift;
811            
812 0         0 $self->append ('Version: ', unpack ('C', $self->read (1)), "\n");
813 0         0 $self->append ('Flags: ', unpack ('B24', $self->read (3)), "\n");
814 0         0 $self->showGMode ();
815 0         0 $self->showRGB ();
816 0         0 $self->append ('Balance: ', nToSigned ($self->read (2)), "\n");
817 0         0 $self->append ("Reserved\n");
818 0         0 $self->read (2);
819             }
820            
821             sub name_gmin {
822 0     0 0 0 my $self = shift;
823 0         0 return 'Generic media information';
824             }
825            
826             sub dump_hdlr {
827 4     4 0 10 my $self = shift;
828            
829 4         14 $self->append ('Version: ', unpack ('C', $self->read (1)), "\n");
830 4         17 $self->append ('Flags: ', unpack ('B24', $self->read (3)), "\n");
831            
832 4         20 my $cmpt = $self->get4Char ();
833 4         17 $self->append ('Component type: ', $cmpt, "\n");
834            
835 4         9 my $subCmpt = $self->get4Char ();
836 4         13 $self->append ('Component sub type: ', $subCmpt, "\n");
837            
838 4         58 $self->setParentAttrib (HdlrCmpt => $cmpt);
839 4         80 $self->setParentAttrib (HdlrSubCmpt => $subCmpt);
840            
841 4         11 $self->append ('Manufacturer: ', $self->get4Char (), "\n");
842 4         61 $self->append ('Flags: ', unpack ('B32', $self->read (4)),
843             "\n");
844 4         14 $self->append ('Mask: ', unpack ('B32', $self->read (4)),
845             "\n");
846            
847 4         14 my $strLen = ord ($self->read (1));
848 4         30 $self->append ('Name: ',
849             unpack ("a$strLen", $self->read ($strLen)), "\n");
850             }
851            
852             sub name_hdlr {
853 4     4 0 7 my $self = shift;
854 4         14 return 'Media data handler';
855             }
856            
857             sub dump_imag {
858 0     0 0 0 my $self = shift;
859 0         0 my ($pos, $len) = @_;
860            
861 0         0 $self->showUnknown ();
862 0         0 $self->unwrapAtoms ($pos + 12, $len - 12);
863             }
864            
865             sub name_imag {
866 0     0 0 0 my $self = shift;
867 0         0 return 'Image';
868             }
869            
870             sub dump_imct {
871 0     0 0 0 my $self = shift;
872 0         0 my ($pos, $len) = @_;
873            
874 0         0 $self->showUnknown ();
875 0         0 $self->unwrapAtoms ($pos + 12, $len - 12);
876             }
877            
878             sub name_imct {
879 0     0 0 0 my $self = shift;
880 0         0 return 'Image container';
881             }
882            
883             sub dump_imda {
884 0     0 0 0 my $self = shift;
885 0         0 my ($pos, $len) = @_;
886            
887 0         0 $len -= 8;
888 0         0 $self->append ("Image data " . groupDigits ($len) . " bytes long\n");
889 0         0 $self->{parsedSize} += $len;
890             }
891            
892             sub name_imda {
893 0     0 0 0 my $self = shift;
894 0         0 return 'Image data';
895             }
896            
897             sub dump_imgp {
898 0     0 0 0 my $self = shift;
899            
900 0         0 $self->unwrapAtoms (@_);
901             }
902            
903             sub name_imgp {
904 0     0 0 0 my $self = shift;
905 0         0 return 'Panorama image container';
906             }
907            
908             sub dump_imrg {
909 0     0 0 0 my $self = shift;
910 0         0 my ($pos, $len) = @_;
911            
912 0         0 $self->showUnknown ();
913 0         0 $self->append ('X: ', NToFixed ($self->read (4)), "\n");
914 0         0 $self->append ('Y: ', NToFixed ($self->read (4)), "\n");
915             }
916            
917             sub name_imrg {
918 0     0 0 0 my $self = shift;
919 0         0 return 'Image group container';
920             }
921            
922             sub dump_list {
923 0     0 0 0 my $self = shift;
924 0         0 my ($pos, $len) = @_;
925            
926 0         0 $self->append ('Id: ', NToSigned ($self->read (4)), "\n");
927 0         0 $self->append ('Items: ', NToSigned ($self->read (4)), "\n");
928 0         0 $self->unwrapAtoms ($pos + 8, $len - 8);
929             }
930            
931             sub name_list {
932 0     0 0 0 my $self = shift;
933 0         0 return 'List';
934             }
935            
936             sub dump_mdat {
937 1     1 0 88 my $self = shift;
938 1         3 my ($pos, $len) = @_;
939            
940 1         2 $len -= 8;
941 1         140 $self->append ("Media data " . groupDigits ($len) . " bytes long.\n");
942             }
943            
944             sub name_mdat {
945 1     1 0 3 my $self = shift;
946 1         3 return 'Media data';
947             }
948            
949             sub dump_MCPS {
950 0     0 0 0 my $self = shift;
951 0         0 my ($pos, $len) = @_;
952 0         0 $self->dumpText ($pos + 8, $len - 8);
953             }
954            
955             sub dump_mdhd {
956 2     2 0 4 my $self = shift;
957            
958 2         8 $self->append ('Version: ', unpack ('C', $self->read (1)), "\n");
959 2         9 $self->append ('Flags: ', unpack ('B24', $self->read (3)), "\n");
960 2         21 $self->append ('Creation time: ', $self->showDate (), "\n");
961 2         10 $self->append ('Modification time: ', $self->showDate (), "\n");
962 2         6 my $timescale = NToSigned ($self->read (4));
963 2         10 $self->setParentAttrib (timescale => $timescale);
964 2         10 $self->append ("Time scale: $timescale ticks per second\n");
965 2         51 my $duration = NToSigned ($self->read (4));
966 2         8 my $durSecs = $duration / $timescale;
967 2         22 $self->append ("Duration: $duration ticks (${durSecs} seconds)\n");
968 2         6 $self->append ('Locale: ', nToSigned ($self->read (2)), "\n");
969 2         207 $self->append ('Quality: ', unpack ('B16', $self->read (2)),
970             "\n");
971             }
972            
973             sub name_mdhd {
974 2     2 0 5 my $self = shift;
975 2         5 return 'Media header';
976             }
977            
978             sub dump_mdia {
979 2     2 0 5 my $self = shift;
980 2         7 $self->unwrapAtoms (@_);
981             }
982            
983             sub name_mdia {
984 2     2 0 5 my $self = shift;
985 2         7 return 'Media container';
986             }
987            
988             sub dump_minf {
989 2     2 0 5 my $self = shift;
990            
991 2         10 $self->unwrapAtoms (@_);
992             }
993            
994             sub name_minf {
995 2     2 0 6 my $self = shift;
996 2         6 return 'Media data';
997             }
998            
999             sub dump_mmdr {
1000 0     0 0 0 my $self = shift;
1001 0         0 my ($pos, $len) = @_;
1002            
1003 0         0 $self->showBogus ();
1004 0         0 $self->append ('Unknown: ', $self->get4Char (), "\n");
1005 0         0 $self->append ('Unknown: ', NToSigned ($self->read (4)), "\n");
1006 0         0 $self->unwrapAtoms ($pos + 21, $len - 21);
1007             }
1008            
1009             sub name_mmdr {
1010 0     0 0 0 my $self = shift;
1011 0         0 return 'Media data reference';
1012             }
1013            
1014             sub dump_moov {
1015 1     1 0 2 my $self = shift;
1016            
1017 1         5 $self->unwrapAtoms (@_);
1018             }
1019            
1020             sub name_moov {
1021 1     1 0 9 my $self = shift;
1022 1         4 return 'Movie container';
1023             }
1024            
1025             sub dump_motx {
1026 0     0 0 0 my $self = shift;
1027 0         0 my ($pos, $len) = @_;
1028            
1029 0         0 $self->showUnknown ();
1030 0         0 $self->append ('Track index: ', NToSigned ($self->read (4)), "\n");
1031             }
1032            
1033             sub name_motx {
1034 0     0 0 0 my $self = shift;
1035 0         0 return 'Media track index';
1036             }
1037            
1038             sub dump_mvhd {
1039 1     1 0 3 my $self = shift;
1040 1         3 my ($pos, $len) = @_;
1041 1         4 my $buffer = $self->read ($len - 8);
1042            
1043 1         9 $self->append ('Version: ',
1044             unpack ('C', substr ($buffer, 0, 1, '')) . "\n");
1045 1         8 $self->append ('Flags: ',
1046             unpack ('B24', substr ($buffer, 0, 3, '')) . "\n");
1047 1         7 $self->append ('Created: ',
1048             $self->showDate (substr ($buffer, 0, 4, '')) . "\n");
1049 1         5 $self->append ('Modified: ',
1050             $self->showDate (substr ($buffer, 0, 4, '')) . "\n");
1051            
1052 1         7 my $timescale = NToSigned (substr ($buffer, 0, 4, ''));
1053 1         6 $self->setParentAttrib (timescale => $timescale);
1054 1         5 $self->append ("Time scale: $timescale ticks per second\n");
1055            
1056 1         3 my $duration = unpack ("N", substr ($buffer, 0, 4, ''));
1057 1         4 my $durSecs = $duration / $timescale;
1058 1         17 $self->append ("Duration: $duration ticks (${durSecs} seconds)\n");
1059 1         7 $self->append ('Pref rate: ',
1060             NToFixed (substr ($buffer, 0, 4, '')) . "\n");
1061 1         7 $self->append ('Pref vol: ',
1062             unpack ("n", substr ($buffer, 0, 2, '')) . "\n");
1063 1         2 $self->append ("Reserved\n");
1064 1         2 substr $buffer, 0, 10, '';
1065 1         6 $self->append ('Matrix: ',
1066             $self->showMatrix (substr ($buffer, 0, 36, '')) . "\n");
1067 1         6 $self->append ('Preview start: ',
1068             unpack ("N", substr ($buffer, 0, 4, '')) . "\n");
1069 1         6 $self->append ('Preview time: ',
1070             unpack ("N", substr ($buffer, 0, 4, '')) . "\n");
1071 1         19 $self->append ('Poster loc: ',
1072             unpack ("N", substr ($buffer, 0, 4, '')) . "\n");
1073 1         6 $self->append ('Sel start: ',
1074             unpack ("N", substr ($buffer, 0, 4, '')) . "\n");
1075 1         5 $self->append ('Sel time: ',
1076             unpack ("N", substr ($buffer, 0, 4, '')) . "\n");
1077 1         6 $self->append ('Time now: ',
1078             unpack ("N", substr ($buffer, 0, 4, '')) . "\n");
1079 1         3 my $nextTrackId = unpack ("N", substr ($buffer, 0, 4, ''));
1080 1         5 $self->append ("Next track: $nextTrackId\n");
1081 1         3 $self->{tracks} = $nextTrackId - 1;
1082             }
1083            
1084             sub name_mvhd {
1085 1     1 0 3 my $self = shift;
1086 1         3 return 'Movie header';
1087             }
1088            
1089             sub dump_name {
1090 0     0 0 0 my $self = shift;
1091 0         0 my ($pos, $len) = @_;
1092 0         0 my $parentType = $self->{atomStack}[-2][0];
1093            
1094 0 0       0 if ($parentType eq 'imag') {
1095 0         0 $self->showUnknown ();
1096 0         0 $self->dumpUnicodeText ($pos + 12, $len - 12);
1097             } else {
1098 0         0 $self->dumpText ($pos + 8, $len - 8);
1099             }
1100             }
1101            
1102             sub name_name {
1103 0     0 0 0 my $self = shift;
1104 0         0 return 'Name';
1105             }
1106            
1107             sub dump_oper {
1108 0     0 0 0 my $self = shift;
1109 0         0 my ($pos, $len) = @_;
1110            
1111 0         0 $self->append ('Operation: ', $self->get4Char (), "\n");
1112 0         0 $self->append ('Operands: ', NToSigned ($self->read (4)), "\n");
1113 0         0 $self->append ("Reserved\n");
1114 0         0 $self->read (4);
1115 0         0 $self->unwrapAtoms ($pos + 12, $len - 12);
1116             }
1117            
1118             sub name_oper {
1119 0     0 0 0 my $self = shift;
1120 0         0 return 'Operation';
1121             }
1122            
1123             sub dump_oprn {
1124 0     0 0 0 my $self = shift;
1125 0         0 my ($pos, $len) = @_;
1126            
1127 0         0 $self->showUnknown ();
1128 0         0 $self->unwrapAtoms ($pos + 12, $len - 12);
1129             }
1130            
1131             sub name_oprn {
1132 0     0 0 0 my $self = shift;
1133 0         0 return 'Operand';
1134             }
1135            
1136             sub dump_parm {
1137 0     0 0 0 my $self = shift;
1138 0         0 my ($pos, $len) = @_;
1139 0         0 my $paramID = NToSigned ($self->read (4));
1140            
1141 0         0 $self->append ('ID: ', $paramID, "\n");
1142 0         0 $self->append ('Unknown 2: ', NToSigned ($self->read (4)), "\n");
1143 0         0 $self->append ('Unknown 3: ', NToSigned ($self->read (4)), "\n");
1144            
1145 0         0 my $actionStr = $self->findAtomValue ('ActionType');
1146 0         0 my $atoms = qq/
1147             kActionCase | kActionWhile
1148             /;
1149 0         0 my $flags = qq/
1150             kActionMovieSetLoopingFlags
1151             /;
1152 0         0 my $fixed = qq/
1153             kActionMovieSetRate | kActionSpriteRotate
1154             /;
1155 0         0 my $fixedFixedBool = qq/
1156             kActionSpriteTranslate
1157             /;
1158 0         0 my $long = qq/
1159             kActionMovieSetLanguage |
1160             kActionMovieSetSelection | kActionMovieRestartAtTime |
1161             kActionQTVRGoToNodeID | kActionMusicPlayNote |
1162             kActionMusicSetController | kOperandSpriteTrackVariable
1163             /;
1164 0         0 my $name = qq/
1165             kActionMovieGoToTimeByName | kActionMovieSetSelectionByName
1166             /;
1167 0         0 my $quadFloat = qq/
1168             kActionSpriteTrackSetVariable
1169             /;
1170 0         0 my $rgnHandle = qq/
1171             kActionTrackSetClip
1172             /;
1173 0         0 my $short = qq/
1174             kActionMovieSetVolume | kActionTrackSetVolume |
1175             kActionTrackSetBalance | kActionTrackSetLayer |
1176             kActionSpriteSetImageIndex | kActionSpriteSetVisible |
1177             kActionSpriteSetLayer
1178             /;
1179 0         0 my $time = qq/
1180             kActionMovieGoToTime
1181             /;
1182            
1183 0 0       0 if ($actionStr =~ m/$atoms/x) {
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
1184 0         0 $self->unwrapAtoms ($pos + 12, $len - 12);
1185             } elsif ($actionStr =~ m/$time/x) {
1186 0         0 $self->unwrapAtoms ($pos + 12, $len - 12);
1187             } elsif ($actionStr =~ m/$flags/x) {
1188 0         0 $self->append ('Flags: ', NToBin ($self->read (4)), "\n");
1189             } elsif ($actionStr =~ m/$fixed/x) {
1190 0         0 $self->append ('Value: ', NToFixed ($self->read (4)), "\n");
1191             } elsif ($actionStr =~ m/$fixedFixedBool/x) {
1192 0         0 $self->append ('Value 1: ', NToFixed ($self->read (4)), "\n");
1193 0         0 $self->append ('Value 2: ', NToFixed ($self->read (4)), "\n");
1194 0         0 $self->append ('Bool value: ', cToBool ($self->read (1)), "\n");
1195             } elsif ($actionStr =~ m/$long/x) {
1196 0         0 $self->append ('Value: ', groupDigits (NToSigned ($self->read (4))),
1197             "\n");
1198             } elsif ($actionStr =~ m/$name/x) {
1199 0         0 $self->dumpText ($pos + 12, $len - 12);
1200             } elsif ($actionStr =~ m/$quadFloat/x) {
1201 0 0       0 if ($paramID == 1) {
1202 0         0 $self->append ('ID: ', NToSigned ($self->read (4)), "\n");
1203             } else {
1204 0         0 $self->append ('value: ', $self->fToFloat ($self->read (4)), "\n");
1205             }
1206             } elsif ($actionStr =~ m/$rgnHandle/x) {
1207 0         0 $self->append ('Size: ', nToSigned ($self->read (2)), "\n");
1208 0         0 $self->append ('Top: ', nToSigned ($self->read (2)), "\n");
1209 0         0 $self->append ('Left: ', nToSigned ($self->read (2)), "\n");
1210 0         0 $self->append ('Bottom: ', nToSigned ($self->read (2)), "\n");
1211 0         0 $self->append ('Right: ', nToSigned ($self->read (2)), "\n");
1212             } elsif ($actionStr =~ m/$short/x) {
1213 0         0 $self->append ('Value: ', nToSigned ($self->read (2)), "\n");
1214             } else {
1215 0         0 $self->append ("Unhandled parameter for action: $actionStr\n");
1216 0 0       0 print "Unhandled parameter for action: $actionStr\n"
1217             if $self->{noise} > 1;
1218             }
1219             }
1220            
1221             sub name_parm {
1222 0     0 0 0 my $self = shift;
1223 0         0 return 'Parameter';
1224             }
1225            
1226             sub dump_play {
1227 0     0 0 0 my $self = shift;
1228 0         0 my ($pos, $len) = @_;
1229            
1230 0         0 $self->append ('Enabled: ', cToBool ($self->read (1)), "\n");
1231             }
1232            
1233             sub name_play {
1234 0     0 0 0 my $self = shift;
1235 0         0 return 'Auto play';
1236             }
1237            
1238             sub dump_rdrf {
1239 0     0 0 0 my $self = shift;
1240 0         0 my ($pos, $len) = @_;
1241            
1242 0         0 $self->append ('Flags: ', NToBin ($self->read (4)), "\n");
1243 0         0 $self->append ('Data reference type: ', $self->get4Char (4), "\n");
1244 0         0 my $size = NToSigned ($self->read (4));
1245 0         0 $self->append ('Data reference size: ', $size, "\n");
1246 0         0 $self->append ('Data reference: ', $self->read ($size), "\n");
1247             }
1248            
1249             sub name_rdrf {
1250 0     0 0 0 my $self = shift;
1251 0         0 return 'Data reference';
1252             }
1253            
1254             sub dump_rmcs {
1255 0     0 0 0 my $self = shift;
1256 0         0 my ($pos, $len) = @_;
1257            
1258 0         0 $self->append ('Flags: ', NToBin ($self->read (4)), "\n");
1259 0         0 $self->append ('CPU speed: ', NToSigned ($self->read (4)), "\n");
1260             }
1261            
1262             sub name_rmcs {
1263 0     0 0 0 my $self = shift;
1264 0         0 return 'CPU speed';
1265             }
1266            
1267             sub dump_rmda {
1268 0     0 0 0 my $self = shift;
1269 0         0 $self->unwrapAtoms (@_);
1270             }
1271            
1272             sub name_rmda {
1273 0     0 0 0 my $self = shift;
1274 0         0 return 'Reference movie descriptor';
1275             }
1276            
1277             sub dump_rmdr {
1278 0     0 0 0 my $self = shift;
1279 0         0 my ($pos, $len) = @_;
1280            
1281 0         0 $self->append ('Flags: ', NToBin ($self->read (4)), "\n");
1282 0         0 $self->append ('Data rate: ', NToSigned ($self->read (4)), "\n");
1283             }
1284            
1285             sub name_rmdr {
1286 0     0 0 0 my $self = shift;
1287 0         0 return 'Data rate';
1288             }
1289            
1290             sub dump_rmqu {
1291 0     0 0 0 my $self = shift;
1292 0         0 my ($pos, $len) = @_;
1293            
1294 0         0 $self->append ('Quality: ', NToSigned ($self->read (4)), "\n");
1295             }
1296            
1297             sub name_rmqu {
1298 0     0 0 0 my $self = shift;
1299 0         0 return 'Quality';
1300             }
1301            
1302             sub dump_rmra {
1303 0     0 0 0 my $self = shift;
1304 0         0 $self->unwrapAtoms (@_);
1305             }
1306            
1307             sub name_rmra {
1308 0     0 0 0 my $self = shift;
1309 0         0 return 'Reference movie';
1310             }
1311            
1312             sub dump_rmvc {
1313 0     0 0 0 my $self = shift;
1314 0         0 my ($pos, $len) = @_;
1315            
1316 0         0 $self->append ('Flags: ', NToBin ($self->read (4)), "\n");
1317 0         0 $self->append ('Software package: ', $self->get4Char (), "\n");
1318 0         0 $self->append ('Version: ', NToHex ($self->read (4)), "\n");
1319 0         0 $self->append ('Mask: ', NToHex ($self->read (4)), "\n");
1320 0         0 $self->append ('Check type: ', nToSigned ($self->read (2)), "\n");
1321             }
1322            
1323             sub name_rmvc {
1324 0     0 0 0 my $self = shift;
1325 0         0 return 'Version check';
1326             }
1327            
1328             sub dump_sean {
1329 0     0 0 0 my $self = shift;
1330 0         0 my ($pos, $len) = @_;
1331 0         0 my $end = $pos + $len;
1332            
1333 0         0 $pos += 20;
1334 0         0 $self->describeAtomsIn ($pos, $end);
1335             }
1336            
1337             sub name_sean {
1338 0     0 0 0 my $self = shift;
1339 0         0 return 'Sprite scene container';
1340             }
1341            
1342             sub dump_slau {
1343 0     0 0 0 my $self = shift;
1344 0         0 my ($pos, $len) = @_;
1345            
1346 0         0 $self->append ('Enabled: ', cToBool ($self->read (1)), "\n");
1347             }
1348            
1349             sub name_slau {
1350 0     0 0 0 my $self = shift;
1351 0         0 return 'Slave audio';
1352             }
1353            
1354             sub dump_slgr {
1355 0     0 0 0 my $self = shift;
1356 0         0 my ($pos, $len) = @_;
1357            
1358 0         0 $self->append ('Enabled: ', cToBool ($self->read (1)), "\n");
1359             }
1360            
1361             sub name_slgr {
1362 0     0 0 0 my $self = shift;
1363 0         0 return 'Slave graphics mode';
1364             }
1365            
1366             sub dump_slti {
1367 0     0 0 0 my $self = shift;
1368 0         0 my ($pos, $len) = @_;
1369            
1370 0         0 $self->append ('Enabled: ', cToBool ($self->read (1)), "\n");
1371             }
1372            
1373             sub name_slti {
1374 0     0 0 0 my $self = shift;
1375 0         0 return 'Slave time';
1376             }
1377            
1378             sub dump_sltr {
1379 0     0 0 0 my $self = shift;
1380 0         0 my ($pos, $len) = @_;
1381            
1382 0         0 $self->append ('Enabled: ', cToBool ($self->read (1)), "\n");
1383             }
1384            
1385             sub name_sltr {
1386 0     0 0 0 my $self = shift;
1387 0         0 return 'Slave track duration';
1388             }
1389            
1390             sub dump_spid {
1391 0     0 0 0 my $self = shift;
1392            
1393 0         0 $self->showUnknown ();
1394 0         0 $self->append ('Sprite id: ', NToSigned ($self->read (4)), "\n");
1395             }
1396            
1397             sub name_spid {
1398 0     0 0 0 my $self = shift;
1399 0         0 return 'Sprite ID';
1400             }
1401            
1402             sub dump_stbl {
1403 2     2 0 5 my $self = shift;
1404            
1405 2         5 $self->unwrapAtoms (@_);
1406             }
1407            
1408             sub name_stbl {
1409 2     2 0 5 my $self = shift;
1410 2         7 return 'Media time to sample data';
1411             }
1412            
1413             sub dump_stco {
1414 2     2 0 4 my $self = shift;
1415 2         5 my ($pos, $len) = @_;
1416 2         91 my $dataRef = $self->findAtom ('HdlrSubCmpt', qr'^(?!alis)');
1417            
1418 2         14 $self->append ('Version: ', unpack ('C', $self->read (1)), "\n");
1419 2         7 $self->append ('Flags: ', unpack ('B24', $self->read (3)), "\n");
1420            
1421 2         5 my $entries = NToSigned ($self->read (4));
1422 2         5 my $digits = length $entries;
1423 2   50     22 my $type = (defined $dataRef && $dataRef->{'HdlrSubCmpt'}) || '';
1424 2         6 $pos += 16;
1425            
1426 2         251 for (1 .. $entries) {
1427 40         193 my $off = NToSigned ($self->read (4, $pos));
1428 40         68 $pos += 4;
1429 40         225 $self->append (sprintf (" %*d ", $digits, $_));
1430 40         544 $self->append ("$type @ ", sprintf "%d (0x%04x)\n", $off, $off);
1431 40 50       252 if ($type =~ /sprt|moov/) {
    100          
1432 0         0 $self->describeAtom ($off + 12);
1433             } elsif ($type eq 'vide') {
1434 20         38 $self->append (" Not expanded\n");
1435             } else {
1436 20 50 33     53 print "stco doesn't handle $type chunks\n"
1437             if $self->{noise} && $self->{noise} > 1;
1438 20         110 next;
1439             }
1440             }
1441             }
1442            
1443             sub name_stco {
1444 2     2 0 8 my $self = shift;
1445 2         10 return 'Media data chunk locations';
1446             }
1447            
1448             sub dump_sprt {
1449 0     0 0 0 my $self = shift;
1450            
1451 0         0 $self->atomList (@_);
1452             }
1453            
1454             sub name_sprt {
1455 0     0 0 0 my $self = shift;
1456 0         0 return 'Sprite key frame';
1457             }
1458            
1459             sub dump_stsc {
1460 2     2 0 4 my $self = shift;
1461 2         47 my ($pos, $len) = @_;
1462            
1463 2         9 $self->append ('Version: ', unpack ('C', $self->read (1)), "\n");
1464 2         9 $self->append ('Flags: ', unpack ('B24', $self->read (3)), "\n");
1465 2         18 my $entries = NToSigned ($self->read (4));
1466 2         5 my $digits = length $entries;
1467            
1468 2         18 for (1 .. $entries) {
1469 27         573 $self->append (sprintf (" %*d\n", $digits, $_));
1470 27         140 $self->append (' first chunk: ', NToSigned ($self->read (4)), "\n");
1471 27         114 $self->append (' samp per chunk: ', NToSigned ($self->read (4)),
1472             "\n");
1473 27         72 $self->append (' samp desc id: ', NToSigned ($self->read (4)),
1474             "\n");
1475             }
1476             }
1477            
1478             sub name_stsc {
1479 2     2 0 49 my $self = shift;
1480 2         6 return 'Sample number to chunk number mapping';
1481             }
1482            
1483             sub dump_stsd {
1484 2     2 0 5 my $self = shift;
1485 2         5 my ($pos, $len) = @_;
1486            
1487 2         7 $self->append ('Version: ', unpack ('C', $self->read (1)), "\n");
1488 2         7 $self->append ('Flags: ', unpack ('B24', $self->read (3)), "\n");
1489 2         6 my $entries = NToSigned ($self->read (4));
1490 2         119 my $digits = length $entries;
1491            
1492 2         9 for (1 .. $entries) {
1493 2         14 $self->append (sprintf (" %*d\n", $digits, $_));
1494 2         7 NToSigned ($self->read (4));
1495 2         11 $self->append (' format: ', $self->get4Char (), "\n");
1496 2         6 $self->append (" Reserved\n");
1497 2         7 NToSigned ($self->read (6));
1498 2         47 $self->append (' index: ', nToSigned ($self->read (2)), "\n");
1499             }
1500             }
1501            
1502             sub name_stsd {
1503 2     2 0 4 my $self = shift;
1504 2         7 return 'Sample description container';
1505             }
1506            
1507             sub dump_stsh {
1508 0     0 0 0 my $self = shift;
1509            
1510 0         0 $self->append ('Version: ', unpack ('C', $self->read (1)), "\n");
1511 0         0 $self->append ('Flags: ', unpack ('B24', $self->read (3)), "\n");
1512 0         0 my $entries = NToSigned ($self->read (4));
1513 0         0 my $digits = length $entries;
1514            
1515 0         0 for (1 .. $entries) {
1516 0         0 $self->append (sprintf ("%*d ", $digits, $_));
1517 0         0 $self->append ('frame diff samp # ', NToSigned ($self->read (4)));
1518 0         0 $self->append (' => sync samp # ', NToSigned ($self->read (4)), "\n");
1519             }
1520             }
1521            
1522             sub name_stsh {
1523 0     0 0 0 my $self = shift;
1524 0         0 return 'Shadow sync table';
1525             }
1526            
1527             sub dump_stsz {
1528 2     2 0 5 my $self = shift;
1529            
1530 2         9 $self->append ('Version: ', unpack ('C', $self->read (1)), "\n");
1531 2         8 $self->append ('Flags: ', unpack ('B24', $self->read (3)), "\n");
1532            
1533 2         9 my $sampleSize = NToSigned ($self->read (4));
1534 2         7 my $entries = NToSigned ($self->read (4));
1535            
1536 2 50       8 if ($sampleSize) {
1537 0         0 $self->append ("Sample size: $sampleSize\n");
1538 0         0 $self->append ("Samples: $entries\n");
1539             } else {
1540 2         209 my $digits = length $entries;
1541            
1542 2         10 for (1 .. $entries) {
1543 183         1505 $self->append (sprintf (" %*d: ", $digits, $_));
1544 183         752 $sampleSize = NToSigned ($self->read (4));
1545 183         1806 $self->append ("sample size $sampleSize\n");
1546 183         679 $self->{parsedSize} += $sampleSize;
1547             }
1548             }
1549             }
1550            
1551             sub name_stsz {
1552 2     2 0 5 my $self = shift;
1553 2         8 return 'Sample size table';
1554             }
1555            
1556             sub dump_stss {
1557 1     1 0 2 my $self = shift;
1558            
1559 1         5 $self->dump_stts (@_);
1560             }
1561            
1562             sub name_stss {
1563 1     1 0 2 my $self = shift;
1564 1         37 return 'Key frame sample numbers table';
1565             }
1566            
1567             sub dump_stts {
1568 3     3 0 8 my $self = shift;
1569 3         6 my ($pos, $len) = @_;
1570            
1571 3         18 $self->append ('Version: ', unpack ('C', $self->read (1)), "\n");
1572 3         11 $self->append ('Flags: ', unpack ('B24', $self->read (3)), "\n");
1573 3         12 my $entries = NToSigned ($self->read (4));
1574 3         10 my $digits = length $entries;
1575 3         13 my $scale = $self->findAtomValue ('timescale');
1576            
1577 3         12 for (1 .. $entries) {
1578 5         28 $self->append (sprintf (" %*d\n", $digits, $_));
1579 5         17 $self->append (' Sample count: ', NToSigned ($self->read (4)), "\n");
1580            
1581 5         15 my $duration = NToSigned ($self->read (4));
1582 5 50       19 my $durSecs = $scale ? $duration / $scale : '---';
1583 5         266 $self->append (
1584             " Duration: $duration ticks (${durSecs} seconds)\n");
1585             }
1586             }
1587            
1588             sub name_stts {
1589 2     2 0 4 my $self = shift;
1590 2         7 return 'Sample number to duration maps';
1591             }
1592            
1593             sub dump_targ {
1594 0     0 0 0 my $self = shift;
1595 0         0 my ($pos, $len) = @_;
1596            
1597 0         0 $self->showUnknown ();
1598 0         0 $self->unwrapAtoms ($pos + 12, $len - 12);
1599             }
1600            
1601             sub dump_test {
1602 0     0 0 0 my $self = shift;
1603 0         0 my ($pos, $len) = @_;
1604            
1605 0         0 $self->showUnknown ();
1606 0         0 $self->unwrapAtoms ($pos + 12, $len - 12);
1607             }
1608            
1609             sub dump_tkhd {
1610 2     2 0 5 my $self = shift;
1611            
1612 2         9 $self->append ('Version: ', unpack ('C', $self->read (1)), "\n");
1613 2         12 $self->append ('Flags: ', unpack ('B24', $self->read (3)), "\n");
1614 2         8 $self->append ('Creation time: ', $self->showDate (), "\n");
1615 2         8 $self->append ('Modification time: ', $self->showDate (), "\n");
1616 2         7 $self->append ('Track ID: ', unpack ("N", $self->read (4)), "\n");
1617 2         7 $self->append ("Reserved\n");
1618 2         6 $self->read (4);
1619 2         9 my $scale = $self->findAtomValue ('timescale');
1620 2         8 my $duration = NToSigned ($self->read (4));
1621 2 50       76 my $durSecs = $scale ? $duration / $scale : '---';
1622 2         67 $self->append ("Duration: $duration ticks (${durSecs} seconds)\n");
1623 2         6 $self->append ("Reserved\n");
1624 2         7 $self->read (8);
1625 2         5 $self->append ('Layer: ', nToSigned ($self->read (2)), "\n");
1626 2         8 $self->append ('Alternate group: ', nToSigned ($self->read (2)), "\n");
1627 2         8 $self->append ('Volume: ', nToUnsigned ($self->read (2)), "\n");
1628 2         9 $self->append ("Reserved\n");
1629 2         6 $self->read (2);
1630 2         9 $self->append ('Matrix structure: ', $self->showMatrix (), "\n");
1631 2         6 $self->append ('Track width: ', NToFixed ($self->read (4)), "\n");
1632 2         9 $self->append ('Track height: ', NToFixed ($self->read (4)), "\n");
1633             }
1634            
1635             sub name_tkhd {
1636 2     2 0 5 my $self = shift;
1637 2         5 return 'Media track header';
1638             }
1639            
1640             sub dump_trak {
1641 2     2 0 5 my $self = shift;
1642            
1643 2         8 $self->unwrapAtoms (@_);
1644             }
1645            
1646             sub name_trak {
1647 2     2 0 5 my $self = shift;
1648 2         6 return 'Media track container';
1649             }
1650            
1651             sub dump_trin {
1652 0     0 0 0 my $self = shift;
1653 0         0 my ($pos, $len) = @_;
1654            
1655 0         0 $self->showUnknown ();
1656 0         0 $self->append ('Track index: ', NToSigned ($self->read (4)), "\n");
1657             }
1658            
1659             sub name_trin {
1660 0     0 0 0 my $self = shift;
1661 0         0 return 'Track index';
1662             }
1663            
1664             sub dump_udta {
1665 3     3 0 5 my $self = shift;
1666            
1667 3         10 $self->unwrapAtoms (@_);
1668             }
1669            
1670             sub name_udta {
1671 3     3 0 6 my $self = shift;
1672 3         8 return 'User data';
1673             }
1674            
1675             sub dump_vmhd {
1676 1     1 0 2 my $self = shift;
1677 1         4 my $parent = $self->{atomStack}[-2][0];
1678            
1679 1 50       6 if ($parent eq 'minf') {
1680 1         4 $self->append ('Version: ', unpack ('C', $self->read (1)), "\n");
1681 1         445 $self->append ('Flags: ', unpack ('B24', $self->read (3)), "\n");
1682            
1683 1         7 $self->showGraphicsXferMode ();
1684 1         6 $self->showRGB ();
1685             } else {
1686 0         0 $self->append ("Unhandled context ($parent) for VideoMediaInfo atom\n");
1687             }
1688             }
1689            
1690             sub name_vmhd {
1691 1     1 0 6 my $self = shift;
1692 1         5 return 'Video media header';
1693             }
1694            
1695             sub dump_whic {
1696 0     0 0 0 my $self = shift;
1697 0         0 my ($pos, $len) = @_;
1698 0         0 my $dataRef = \%{$self->getParentAttribs ()};
  0         0  
1699 0         0 my %actions = (
1700             1024 => 'kActionMovieSetVolume',
1701             1025 => 'kActionMovieSetRate',
1702             1026 => 'kActionMovieSetLoopingFlags',
1703             1027 => 'kActionMovieGoToTime',
1704             1028 => 'kActionMovieGoToTimeByName',
1705             1029 => 'kActionMovieGoToBeginning',
1706             1030 => 'kActionMovieGoToEnd',
1707             1031 => 'kActionMovieStepForward',
1708             1032 => 'kActionMovieStepBackward',
1709             1033 => 'kActionMovieSetSelection',
1710             1034 => 'kActionMovieSetSelectionByName',
1711             1035 => 'kActionMoviePlaySelection',
1712             1036 => 'kActionMovieSetLanguage',
1713             1037 => 'kActionMovieChanged',
1714             1038 => 'kActionMovieRestartAtTime',
1715             2048 => 'kActionTrackSetVolume',
1716             2049 => 'kActionTrackSetBalance',
1717             2050 => 'kActionTrackSetEnabled',
1718             2051 => 'kActionTrackSetMatrix',
1719             2052 => 'kActionTrackSetLayer',
1720             2053 => 'kActionTrackSetClip',
1721             2054 => 'kActionTrackSetCursor',
1722             2055 => 'kActionTrackSetGraphicsMode',
1723             3072 => 'kActionSpriteSetMatrix',
1724             3073 => 'kActionSpriteSetImageIndex',
1725             3074 => 'kActionSpriteSetVisible',
1726             3075 => 'kActionSpriteSetLayer',
1727             3076 => 'kActionSpriteSetGraphicsMode',
1728             3078 => 'kActionSpritePassMouseToCodec',
1729             3079 => 'kActionSpriteClickOnCodec',
1730             3080 => 'kActionSpriteTranslate',
1731             3081 => 'kActionSpriteScale',
1732             3082 => 'kActionSpriteRotate',
1733             3083 => 'kActionSpriteStretch',
1734             4096 => 'kActionQTVRSetPanAngle',
1735             4097 => 'kActionQTVRSetTiltAngle',
1736             4098 => 'kActionQTVRSetFieldOfView',
1737             4099 => 'kActionQTVRShowDefaultView',
1738             4100 => 'kActionQTVRGoToNodeID',
1739             5120 => 'kActionMusicPlayNote',
1740             5121 => 'kActionMusicSetController',
1741             6144 => 'kActionCase',
1742             6145 => 'kActionWhile',
1743             6146 => 'kActionGoToURL',
1744             6147 => 'kActionSendQTEventToSprite',
1745             6148 => 'kActionDebugStr',
1746             6149 => 'kActionPushCurrentTime',
1747             6150 => 'kActionPushCurrentTimeWithLabel',
1748             6151 => 'kActionPopAndGotoTopTime',
1749             6152 => 'kActionPopAndGotoLabeledTime',
1750             6153 => 'kActionStatusString',
1751             6154 => 'kActionSendQTEventToTrackObject',
1752             6155 => 'kActionAddChannelSubscription',
1753             6156 => 'kActionRemoveChannelSubscription',
1754             6157 => 'kActionOpenCustomActionHandler',
1755             6158 => 'kActionDoScript',
1756             7168 => 'kActionSpriteTrackSetVariable',
1757             7169 => 'kActionSpriteTrackNewSprite',
1758             7170 => 'kActionSpriteTrackDisposeSprite',
1759             7171 => 'kActionSpriteTrackSetVariableToString',
1760             7172 => 'kActionSpriteTrackConcatVariables',
1761             7173 => 'kActionSpriteTrackSetVariableToMovieURL',
1762             7174 => 'kActionSpriteTrackSetVariableToMovieBaseURL',
1763             8192 => 'kActionApplicationNumberAndString',
1764             9216 => 'kActionQD3DNamedObjectTranslateTo',
1765             9217 => 'kActionQD3DNamedObjectScaleTo',
1766             9218 => 'kActionQD3DNamedObjectRotateTo',
1767             10240 => 'kActionFlashTrackSetPan',
1768             10241 => 'kActionFlashTrackSetZoom',
1769             10242 => 'kActionFlashTrackSetZoomRect',
1770             10243 => 'kActionFlashTrackGotoFrameNumber',
1771             10244 => 'kActionFlashTrackGotoFrameLabel',
1772             11264 => 'kActionMovieTrackAddChildMovie',
1773             11265 => 'kActionMovieTrackLoadChildMovie',
1774             );
1775            
1776 0         0 $self->showUnknown ();
1777            
1778 0         0 my $action = NToSigned ($self->read (4));
1779 0         0 my $actionStr = $actions{$action};
1780 0 0       0 $actionStr = "Unknown - $action" if !defined $actionStr;
1781 0         0 $self->append ("Type: $actionStr\n");
1782 0         0 $dataRef->{'ActionType'} = $actionStr;
1783             }
1784            
1785             sub name_whic {
1786 0     0 0 0 my $self = shift;
1787 0         0 return 'Which action type';
1788             }
1789            
1790 1     1 0 2 sub dump_wide {
1791             }
1792            
1793             sub name_wide {
1794 1     1 0 2 my $self = shift;
1795 1         2 return '64 bit expansion place holder';
1796             }
1797            
1798             sub dump_WLOC {
1799 1     1 0 2 my $self = shift;
1800 1         64 my ($pos, $len) = @_;
1801            
1802 1         9 $len = 2 * $len - 16;
1803 1         6 $self->append (unpack ("H$len\n", $self->read ($len)), "\n");
1804             }
1805            
1806             sub name_WLOC {
1807 1     1 0 3 my $self = shift;
1808 1         3 return 'Default window location';
1809             }
1810            
1811             sub dump_x00000001 {
1812 0     0 0 0 my $self = shift;
1813 0         0 my $parentType = $self->{atomStack}[-2][0];
1814            
1815 0 0       0 if ($parentType eq 'oprn') {
1816 0         0 my ($pos, $len) = @_;
1817            
1818 0         0 $self->showUnknown ();
1819 0         0 $self->unwrapAtoms ($pos + 12, $len - 12);
1820             } else {
1821            
1822 0         0 $self->showBogus ();
1823 0         0 $self->append ('Matrix structure: ', $self->showMatrix (), "\n");
1824             }
1825             }
1826            
1827             sub name_x00000001 {
1828 0     0 0 0 my $self = shift;
1829 0         0 my $parentType = $self->{atomStack}[-2][0];
1830            
1831 0 0       0 if ($parentType eq 'oprn') {
1832 0         0 return '';
1833             } else {
1834 0         0 return 'kSpritePropertyMatrix';
1835             }
1836             }
1837            
1838             sub dump_x00000002 {
1839 0     0 0 0 my $self = shift;
1840            
1841 0         0 $self->showUnknown ();
1842 0         0 $self->append ('Value: ', groupDigits (nToSigned ($self->read (2))),
1843             "\n");
1844             }
1845            
1846             sub name_x00000002 {
1847 0     0 0 0 my $self = shift;
1848 0         0 return 'Constant';
1849             }
1850            
1851             sub dump_x00000004 {
1852 0     0 0 0 my $self = shift;
1853            
1854 0         0 $self->showBogus ();
1855 0         0 $self->append ('Visible: ', nToSigned ($self->read (2)), "\n");
1856             }
1857            
1858             sub name_x00000004 {
1859 0     0 0 0 my $self = shift;
1860 0         0 return 'kSpritePropertyVisible';
1861             }
1862            
1863             sub dump_x00000005 {
1864 0     0 0 0 my $self = shift;
1865            
1866 0         0 $self->showBogus ();
1867 0         0 $self->append ('Layer: ', nToSigned ($self->read (2)), "\n");
1868             }
1869            
1870             sub name_x00000005 {
1871 0     0 0 0 my $self = shift;
1872 0         0 return 'kSpritePropertyLayer';
1873             }
1874            
1875             sub dump_x00000006 {
1876 0     0 0 0 my $self = shift;
1877            
1878 0         0 $self->showPlayMode ();
1879 0         0 $self->showBogus ();
1880 0         0 $self->showRGB ();
1881             }
1882            
1883             sub name_x00000006 {
1884 0     0 0 0 my $self = shift;
1885 0         0 return 'kSpritePropertyGraphicsMode';
1886             }
1887            
1888 0     0 0 0 sub dump_x00000015 {
1889             }
1890            
1891             sub name_x00000015 {
1892 0     0 0 0 my $self = shift;
1893 0         0 return 'Quicktime version';
1894             }
1895            
1896             sub dump_x00000064 {
1897 0     0 0 0 my $self = shift;
1898 0         0 $self->showBogus ();
1899 0         0 $self->append ('Image index: ', nToSigned ($self->read (2)), "\n");
1900             }
1901            
1902             sub name_x00000064 {
1903 0     0 0 0 my $self = shift;
1904 0         0 return 'kSpritePropertyImageIndex';
1905             }
1906            
1907             sub dump_x00000065 {
1908 0     0 0 0 my $self = shift;
1909            
1910 0         0 $self->append ("Background colour:\n");
1911 0         0 $self->showBogus ();
1912 0         0 $self->showRGB ();
1913             }
1914            
1915             sub name_x00000065 {
1916 0     0 0 0 my $self = shift;
1917 0         0 return 'kSpriteTrackPropertyBackgroundColor';
1918             }
1919            
1920             sub dump_x00000066 {
1921 0     0 0 0 my $self = shift;
1922 0         0 $self->showBogus ();
1923 0         0 $self->append ('Offscreen bit depth: ', nToSigned ($self->read (2)), "\n");
1924             }
1925            
1926             sub name_x00000066 {
1927 0     0 0 0 my $self = shift;
1928 0         0 return 'kSpriteTrackPropertyOffscreenBitDepth';
1929             }
1930            
1931             sub dump_x00000067 {
1932 0     0 0 0 my $self = shift;
1933 0         0 $self->showBogus ();
1934 0         0 $self->append ('Sample format: ', nToSigned ($self->read (2)), "\n");
1935             }
1936            
1937             sub name_x00000067 {
1938 0     0 0 0 my $self = shift;
1939 0         0 return 'kSpriteTrackPropertySampleFormat';
1940             }
1941            
1942             sub dump_x00000069 {
1943 0     0 0 0 my $self = shift;
1944 0         0 $self->showBogus ();
1945 0         0 $self->append ('Has Actions: ', cToBool ($self->read (1)), "\n");
1946             }
1947            
1948             sub name_x00000069 {
1949 0     0 0 0 my $self = shift;
1950 0         0 return 'kSpriteTrackPropertySampleFormat';
1951             }
1952            
1953             sub dump_x0000006a {
1954 0     0 0 0 my $self = shift;
1955 0         0 $self->showBogus ();
1956 0         0 $self->append ('Visible: ', cToBool ($self->read (1)), "\n");
1957             }
1958            
1959             sub name_x0000006a {
1960 0     0 0 0 my $self = shift;
1961 0         0 return 'kSpriteTrackPropertyScaleSpritesToScaleWorld';
1962             }
1963            
1964             sub dump_x0000006b {
1965 0     0 0 0 my $self = shift;
1966 0         0 $self->showBogus ();
1967            
1968 0         0 my $interval = NToUnsigned ($self->read (4));
1969 0 0       0 my $freq = $interval ? (60.0 / $interval) . ' Hz' : 'fastest';
1970            
1971 0 0       0 $freq = 'off' if $interval == 0xffffffff;
1972 0         0 $self->append ("Idle Events: $freq\n");
1973             }
1974            
1975             sub name_x0000006b {
1976 0     0 0 0 my $self = shift;
1977 0         0 return 'kSpriteTrackPropertyHasActions';
1978             }
1979            
1980 0     0 0 0 sub dump_x00000c00 {
1981             }
1982            
1983             sub name_x00000c00 {
1984 0     0 0 0 my $self = shift;
1985 0         0 return 'kOperandSpriteBoundsLeft';
1986             }
1987            
1988 0     0 0 0 sub dump_x00000c01 {
1989             }
1990            
1991             sub name_x00000c01 {
1992 0     0 0 0 my $self = shift;
1993 0         0 return 'kOperandSpriteBoundsTop';
1994             }
1995            
1996 0     0 0 0 sub dump_x00000c02 {
1997             }
1998            
1999             sub name_x00000c02 {
2000 0     0 0 0 my $self = shift;
2001 0         0 return 'kOperandSpriteBoundsRight';
2002             }
2003            
2004 0     0 0 0 sub dump_x00000c03 {
2005             }
2006            
2007             sub name_x00000c03 {
2008 0     0 0 0 my $self = shift;
2009 0         0 return 'kOperandSpriteBoundsBottom';
2010             }
2011            
2012 0     0 0 0 sub dump_x00000c04 {
2013             }
2014            
2015             sub name_x00000c04 {
2016 0     0 0 0 my $self = shift;
2017 0         0 return 'kOperandSpriteImageIndex';
2018             }
2019            
2020 0     0 0 0 sub dump_x00000c05 {
2021             }
2022            
2023             sub name_x00000c05 {
2024 0     0 0 0 my $self = shift;
2025 0         0 return 'kOperandSpriteVisible';
2026             }
2027            
2028 0     0 0 0 sub dump_x00000c06 {
2029             }
2030            
2031             sub name_x00000c06 {
2032 0     0 0 0 my $self = shift;
2033 0         0 return 'kOperandSpriteLayer';
2034             }
2035            
2036             sub dump_x00000c07 {
2037 0     0 0 0 my $self = shift;
2038 0         0 my ($pos, $len) = @_;
2039            
2040 0         0 $self->setParentAttribs (ActionType => 'kOperandSpriteTrackVariable');
2041 0         0 $self->showUnknown ();
2042 0         0 $self->unwrapAtoms ($pos + 12, $len - 12);
2043             }
2044            
2045             sub name_x00000c07 {
2046 0     0 0 0 my $self = shift;
2047 0         0 return 'kOperandSpriteTrackVariable';
2048             }
2049            
2050 0     0 0 0 sub dump_x00001400 {
2051             }
2052            
2053             sub name_x00001400 {
2054 0     0 0 0 my $self = shift;
2055 0         0 return 'kOperandMouseLocalHLoc';
2056             }
2057            
2058 0     0 0 0 sub dump_x00001401 {
2059             }
2060            
2061             sub name_x00001401 {
2062 0     0 0 0 my $self = shift;
2063 0         0 return 'kOperandMouseLocalVLoc';
2064             }
2065            
2066 0     0 0 0 sub dump_x00001402 {
2067             }
2068            
2069             sub name_x00001402 {
2070 0     0 0 0 my $self = shift;
2071 0         0 return 'kOperandKeyIsDown';
2072             }
2073            
2074             =head3 dumpBlock
2075            
2076             $self->dumpBlock ($pos + 8, $len - 8);
2077            
2078             Dump a raw block of data. Generally used where the block can not be further
2079             decoded.
2080            
2081             =cut
2082            
2083             sub dumpBlock {
2084 6     6 1 9 my $self = shift;
2085 6         19 my ($pos, $len) = @_;
2086            
2087 6         16 while ($len) {
2088 6 50       17 my $chunk = $len > 16 ? 16 : $len;
2089 6         18 my $str = $self->read ($chunk);
2090            
2091 6         31 $str =~ s/([\x00-\x1f\x80-\xff])/sprintf "\\x%02x", ord ($1)/ge;
  82         822  
2092 6         22 $self->append ("$str\n");
2093 6         24 $len -= $chunk;
2094             }
2095             }
2096            
2097             =head3 dumpText ($pos, $len)
2098            
2099             Append an ASCII string starting at $pos for $len characters to the Dump result.
2100            
2101             =cut
2102            
2103             sub dumpText {
2104 0     0 1 0 my $self = shift;
2105 0         0 my ($pos, $len) = @_;
2106            
2107 0         0 $self->append (unpack ("a$len", $self->read ($len, $pos)), "\n");
2108             }
2109            
2110             =head3 dumpUnicodeText
2111            
2112             Append a utf16 string starting at $pos for $len bytes to the Dump result.
2113            
2114             =cut
2115            
2116             sub dumpUnicodeText {
2117 0     0 1 0 my $self = shift;
2118 0         0 my ($pos, $len) = @_;
2119            
2120 0         0 my $rawStr = "\xff\xfe" . unpack ("a$len", $self->read ($len, $pos));
2121 0         0 my $str = decode ("utf16", $rawStr);
2122 0         0 $self->append ($str, "\n");
2123             }
2124            
2125             =head3 showBogus
2126            
2127             L the next four bytes as a packed version and flags field then skips eight
2128             bytes.
2129            
2130             =cut
2131            
2132             sub showBogus {
2133 0     0 1 0 my $self = shift;
2134            
2135 0         0 $self->append ('Version: ', unpack ('C', $self->read (1)), "\n");
2136 0         0 $self->append ('Flags: ', unpack ('B24', $self->read (3)), "\n");
2137 0         0 $self->append ("Reserved\n");
2138 0         0 $self->read (8);
2139             }
2140            
2141             =head3 showPlayMode
2142            
2143             L a play mode string decoded from the next four byte flags field.
2144            
2145             =cut
2146            
2147             sub showPlayMode {
2148 0     0 1 0 my $self = shift;
2149 0         0 my $flagBits = shift;
2150 0         0 my $flags = '';
2151            
2152 0 0       0 $flagBits = $self->read (4) if !defined $flagBits;
2153 0         0 $flagBits = NToSigned ($flagBits);
2154            
2155 0 0       0 $flags .= 'fullScreenHideCursor ' if $flags & 1;
2156 0 0       0 $flags .= 'fullScreenAllowEvents ' if $flags & 2;
2157 0 0       0 $flags .= 'fullScreenDontChangeMenuBar ' if $flags & 4;
2158 0 0       0 $flags .= 'fullScreenPreflightSize ' if $flags & 8;
2159 0         0 $self->append ("Play mode flags: $flags\n");
2160             }
2161            
2162             =head3 showGMode
2163            
2164             L a graphics mode line decoded from the next two byte field.
2165            
2166             =cut
2167            
2168             sub showGMode {
2169 0     0 1 0 my $self = shift;
2170 0         0 my $gMode = shift;
2171 0 0       0 $gMode = $self->read (2) if !defined $gMode;
2172 0         0 $gMode = NToSigned ($gMode);
2173            
2174 0         0 my %modes = (
2175             0x0000 => 'Copy',
2176             0x0040 => 'Dither copy',
2177             0x0020 => 'Blend',
2178             0x0024 => 'Transparent',
2179             0x0100 => 'Straight alpha',
2180             0x0101 => 'Premul white alpha',
2181             0x0102 => 'Premul black alpha',
2182             0x0104 => 'Straight alpha blend',
2183             0x0103 => 'Composition (dither copy)',
2184             );
2185            
2186 0         0 $self->append ("Graphics mode: $modes{$gMode}\n");
2187             }
2188            
2189             =head3 showRGB
2190            
2191             L three RGB color lines decoded from the next six bytes.
2192            
2193             =cut
2194            
2195             sub showRGB {
2196 1     1 1 3 my $self = shift;
2197 1         3 my ($red, $green, $blue) = @_;
2198            
2199 1 50       6 $red = $self->read (2) if !defined $red;
2200 1 50       6 $green = $self->read (2) if !defined $green;
2201 1 50       6 $blue = $self->read (2) if !defined $blue;
2202 1         4 $red = nToUnsigned ($red);
2203 1         3 $green = nToUnsigned ($green);
2204 1         12 $blue = nToUnsigned ($blue);
2205            
2206 1         6 $self->append ("Red: $red\n");
2207 1         4 $self->append ("Green: $green\n");
2208 1         4 $self->append ("Blue: $blue\n");
2209             }
2210            
2211             =head3 showGraphicsXferMode
2212            
2213             L a graphics transfer mode string decoded fromthe next two byte field.
2214            
2215             =cut
2216            
2217             sub showGraphicsXferMode {
2218 1     1 1 3 my $self = shift;
2219 1         3 my $gMode = shift;
2220            
2221 1 50       6 $gMode = $self->read (2) if !defined $gMode;
2222 1         6 $gMode = nToSigned ($gMode);
2223            
2224 1         178 my %modes = (
2225             0 => 'srcCopy',
2226             1 => 'srcOr',
2227             2 => 'srcXor',
2228             3 => 'srcBic',
2229             4 => 'notSrcCopy',
2230             5 => 'notSrcOr',
2231             6 => 'notSrcXor',
2232             7 => 'notSrcBic',
2233             8 => 'patCopy',
2234             9 => 'patOr',
2235             10 => 'patXor',
2236             11 => 'patBic',
2237             12 => 'notPatCopy',
2238             13 => 'notPatOr',
2239             14 => 'notPatXor',
2240             15 => 'notPatBic',
2241             49 => 'grayishTextOr',
2242             50 => 'hilite',
2243             50 => 'hilitetransfermode',
2244             32 => 'blend',
2245             33 => 'addPin',
2246             34 => 'addOver',
2247             35 => 'subPin',
2248             37 => 'addMax',
2249             37 => 'adMax',
2250             38 => 'subOver',
2251             39 => 'adMin',
2252             64 => 'ditherCopy',
2253             36 => 'transparent',
2254             );
2255            
2256 1 50       8 if (exists $modes{$gMode}) {
2257 1         43 $self->append ('Mode: ', $modes{$gMode}, "\n");
2258             } else {
2259 0         0 $self->append ('Mode: unknown - ', $gMode, "\n");
2260             }
2261             }
2262            
2263             =head3 showDate
2264            
2265             L a date string decoded from the next four bytes.
2266            
2267             =cut
2268            
2269             sub showDate {
2270 10     10 1 101 my $self = shift;
2271 10         15 my $stamp = shift;
2272            
2273 10 100       35 $stamp = $self->read (4) if !defined $stamp;
2274 10         100 $stamp = NToUnsigned ($stamp);
2275            
2276             # seconds difference between Mac epoch and Unix/Windows.
2277 10 50       43 my $mod =
2278             ($^O =~ /MSWin32/)
2279             ? (2063824538 - 12530100 + 31536000)
2280             : (2063824538 - 12530100);
2281 10 50       787 my $date = ($^O =~ /Mac/) ? localtime ($stamp) : localtime ($stamp - $mod);
2282 10         138 return $date;
2283             }
2284            
2285             =head3 showMatrix ([$matrix])
2286            
2287             Returns a matrix string formedby decoding the 36 byte contents of $matrix or the
2288             next 36 bytes (if $matrix is not provided).
2289            
2290             =cut
2291            
2292             sub showMatrix {
2293 3     3 1 6 my $self = shift;
2294 3         5 my $matrix = shift;
2295            
2296 3 100       12 $matrix = $self->read (36) if !defined $matrix;
2297            
2298 3         6 my $str = '';
2299 3         9 for (1 .. 3) {
2300 9         15 my $sub = substr $matrix, 0, 12, '';
2301 9         24 $str .= NToFixed (substr $sub, 0, 4, '') . ' ';
2302 9         25 $str .= NToFixed (substr $sub, 0, 4, '') . ' ';
2303 9         29 $str .= NToFrac (substr $sub, 0, 4, '') . ' ';
2304 9 100       39 $str .= ' / ' if $_ != 3;
2305             }
2306            
2307 3         14 return $str;
2308             }
2309            
2310             =head3 showStr ($pos)
2311            
2312             L the length prefixed string starting at $pos.
2313            
2314             =cut
2315            
2316             sub showStr {
2317 2     2 1 4 my $self = shift;
2318 2         3 my $pos = shift;
2319 2         6 my ($len, $key) = unpack ("Na4", $self->read (8, $pos));
2320            
2321 2         3 $len -= 12;
2322 2         9 $self->append (unpack ("a$len", $self->read ($len, $pos + 12)), "\n");
2323             }
2324            
2325             =head3 showUnknown
2326            
2327             L out the next 12 bytes as three unknown signed numbers.
2328            
2329             =cut
2330            
2331             sub showUnknown {
2332 0     0 1 0 my $self = shift;
2333            
2334 0         0 $self->append ('Unknown 1: ', groupDigits (NToSigned ($self->read (4))),
2335             "\n");
2336 0         0 $self->append ('Unknown 2: ', groupDigits (NToSigned ($self->read (4))),
2337             "\n");
2338 0         0 $self->append ('Unknown 3: ', groupDigits (NToSigned ($self->read (4))),
2339             "\n");
2340             }
2341            
2342             =head3 get4Char
2343            
2344             Return the next four bytes as a four char code.
2345            
2346             =cut
2347            
2348             sub get4Char {
2349 14     14 1 179 my $self = shift;
2350 14         30 return unpack ("a4", $self->read (4));
2351             }
2352            
2353             =head2 Helper functions
2354            
2355             The following functions are not object members and should be called as:
2356            
2357             my $result = Video::Dumper::QuickTime::functionName (...);
2358            
2359             =head3 groupDigits ($number)
2360            
2361             Inserts commas into a number to group the digits into groups of 3.
2362            
2363             =cut
2364            
2365             sub groupDigits {
2366 121     121 1 725 my $num = reverse shift;
2367            
2368 121         1306 $num =~ s/(\d{3}(?=\d))/$1,/g;
2369 121         1431 return scalar reverse $num;
2370             }
2371            
2372             =head3 show ($string)
2373            
2374             Attempt to make sense of the series of bytes in $string. Maybe useful for
2375             attempting to make sense of unknown atom data.
2376            
2377             =cut
2378            
2379             sub show {
2380 0     0 1 0 local $_;
2381 0         0 my $thing = shift;
2382 0 0       0 if ($thing =~ /^([^\x00]*)\x00\Z/) {
    0          
2383 0         0 return $1;
2384             } elsif ($thing =~ /[\x00-\x1f]/) {
2385 0         0 my $sum = 0;
2386 0         0 my @chars = split '', $thing;
2387 0         0 $sum = $sum * 256 + ord ($_) for @chars;
2388 0         0 return sprintf "0x%0x", $sum;
2389             }
2390            
2391 0         0 return $thing;
2392             }
2393            
2394             =head2 Conversion helper functions
2395            
2396             The following non-member function unpack strings of bytes into another
2397             representation.
2398            
2399             =head3 NToFixed ($str)
2400            
2401             =cut
2402            
2403             sub NToFixed {
2404 25     25 1 34 my $str = shift;
2405 25         116 return unpack ('l', pack ('l', unpack ("N", $str))) / 0x10000;
2406             }
2407            
2408             =head3 fToFloat ($str)
2409            
2410             =cut
2411            
2412             sub fToFloat {
2413 0     0 1 0 my $str = shift;
2414 0         0 return unpack ('l', pack ('l', unpack ("f", $str)));
2415             }
2416            
2417             =head3 NToFrac
2418            
2419             =cut
2420            
2421             sub NToFrac {
2422 9     9 1 9 my $str = shift;
2423 9         20 my $fract = unpack ('l', pack ('l', unpack ("N", $str)));
2424 9         26 return $fract / 0x40000000;
2425             }
2426            
2427             =head3 NToSigned
2428            
2429             =cut
2430            
2431             sub NToSigned {
2432 348     348 1 1233 my $str = shift;
2433 348         3344 return unpack ('l', pack ('l', unpack ("N", $str)));
2434             }
2435            
2436             =head3 NToUnsigned
2437            
2438             =cut
2439            
2440             sub NToUnsigned {
2441 10     10 1 14 my $str = shift;
2442 10         149 return unpack ('L', pack ('L', unpack ("N", $str)));
2443             }
2444            
2445             =head3 NToHex
2446            
2447             =cut
2448            
2449             sub NToHex {
2450 0     0 1 0 my $str = shift;
2451 0         0 return '0x' . unpack ('H8', $str);
2452             }
2453            
2454             =head3 NToBin
2455            
2456             =cut
2457            
2458             sub NToBin {
2459 0     0 1 0 my $str = shift;
2460 0         0 return unpack ('B32', $str);
2461             }
2462            
2463             =head3 nToSigned
2464            
2465             =cut
2466            
2467             sub nToSigned {
2468 9     9 1 17 my $str = shift;
2469 9         60 return unpack ('s', pack ('s', unpack ("n", $str)));
2470             }
2471            
2472             =head3 nToUnsigned
2473            
2474             =cut
2475            
2476             sub nToUnsigned {
2477 5     5 1 11 my $str = shift;
2478 5         21 return unpack ('S', pack ('S', unpack ("n", $str)));
2479             }
2480            
2481             =head3 cToBool
2482            
2483             =cut
2484            
2485             sub cToBool {
2486 0     0 1   my $str = shift;
2487 0           return ord ($str);
2488             }
2489            
2490             1;
2491            
2492             =head2 Subclassing QuickTime
2493            
2494             Because there are a huge number of atom types used by QuickTime (many of them
2495             undocumented) and the number of atom types used is increasing over time,
2496             Video::Dumper::QuickTime makes no attempt to decode all atom types. Instead it is
2497             easy to subclass the QuickTime class to add decoders for atoms of interest, or
2498             to change the way atoms that are currently handled by the QuickTime class are
2499             decoded for some particular application.
2500            
2501             Two methods need to be provided for decoding of an atom. They are of the form:
2502            
2503             sub name_xxxx {
2504             my $self = shift;
2505             return 'The xxxx atom';
2506             }
2507            
2508             sub dump_xxxx {
2509             my $self = shift;
2510             my ( $pos, $len ) = @_;
2511            
2512             ...
2513             }
2514            
2515             where the C is a placeholder for the atom four char code.
2516            
2517             A complete subclass package that handles one atom might look like:
2518            
2519             package Subclass;
2520            
2521             use QuickTime;
2522             use base qw(QuickTime);
2523            
2524             sub name_smhd {
2525             my $self = shift;
2526             return 'The smhd atom';
2527             }
2528            
2529             sub dump_smhd {
2530             my $self = shift;
2531             my ( $pos, $len ) = @_;
2532             }
2533            
2534             There is of course no limit practical to the number of handlers added by a
2535             subclass.
2536            
2537             =head1 REMARKS
2538            
2539             This module recognises a subset of the atoms actually used by QuickTime files.
2540             Generally, well formed files should not present a problem because unrecognised
2541             atoms will be reported and skipped.
2542            
2543             Subclassing Video::Dumper::QuickTime as shown above allows handlers to be added
2544             for unrecognised atoms. The author would appreciate any such handler code being
2545             forwarded for inclusion in future versions of the module.
2546            
2547             =head1 BUGS
2548            
2549             Please report any bugs or feature requests to
2550             C, or through the web interface at
2551             L.
2552             I will be notified, and then you'll automatically be notified of progress on
2553             your bug as I make changes.
2554            
2555             =head1 SUPPORT
2556            
2557             This module is supported by the author through CPAN. The following links may be
2558             of assistance:
2559            
2560             =over 4
2561            
2562             =item * AnnoCPAN: Annotated CPAN documentation
2563            
2564             L
2565            
2566             =item * CPAN Ratings
2567            
2568             L
2569            
2570             =item * RT: CPAN's request tracker
2571            
2572             L
2573            
2574             =item * Search CPAN
2575            
2576             L
2577            
2578             =back
2579            
2580             =head1 ACKNOWLEDGEMENTS
2581            
2582             The author appreciates the receipt of a patch containing bug fixes and
2583             additional atom decoders from Nick Wellnhofer.
2584            
2585             =head1 AUTHOR
2586            
2587             Peter Jaquiery
2588             CPAN ID: GRANDPA
2589             grandpa@cpan.org
2590            
2591             =head1 COPYRIGHT & LICENSE
2592            
2593             This program is free software; you can redistribute
2594             it and/or modify it under the same terms as Perl itself.
2595            
2596             The full text of the license can be found in the
2597             LICENSE file included with this module.
2598            
2599             =cut