/[amv]/amv.pl
This is repository of my old source code which isn't updated any more. Go to git.rot13.org for current projects!
ViewVC logotype

Diff of /amv.pl

Parent Directory Parent Directory | Revision Log Revision Log | View Patch Patch

revision 3 by dpavlin, Thu Jul 19 20:17:37 2007 UTC revision 23 by dpavlin, Sat Jul 21 17:03:56 2007 UTC
# Line 3  Line 3 
3  # amv.pl  # amv.pl
4  #  #
5  # 07/19/07 19:21:39 CEST Dobrica Pavlinusic <dpavlin@rot13.org>  # 07/19/07 19:21:39 CEST Dobrica Pavlinusic <dpavlin@rot13.org>
6    #
7    # Various useful links used to produce this:
8    # http://www.moviecodec.com/topics/15431p1.html
9    # http://en.wikipedia.org/wiki/RIFF_(File_format)
10    # http://www.obrador.com/essentialjpeg/HeaderInfo.htm
11    # http://lists.helixcommunity.org/pipermail/datatype-dev/2005-January/001886.html
12    
13  use strict;  use strict;
14    
15  use Data::Dump qw/dump/;  use Data::Dump qw/dump/;
16  use Carp qw/confess/;  use Carp qw/confess/;
17    use File::Path;
18    use Getopt::Long;
19    
20    my $dump = 0;
21    my $debug = 0;
22    my $dump_dir = '/tmp/dump/';
23    my $dump_avi = "dump.avi";
24    my $no_jpeg_header = 0;
25    my $jpeg_q = 100;
26    my $jpegtran = '-flip vertical';
27    
28    GetOptions(
29            "dump!"                 => \$dump,
30            "debug!"                => \$debug,
31            "dump-dir=s"    => \$dump_dir,
32            "no-jpeg-headers!" => \$no_jpeg_header,
33            "jpegtran=s"    => \$jpegtran
34    );
35    
36  my $path = shift @ARGV || die "usage: $0 movie.amv\n";  my $path = shift @ARGV || die "usage: $0 movie.amv\n";
37    
38    
39    rmtree $dump_dir if -e $dump_dir;
40    mkpath $dump_dir || die "can't create $dump_dir: $!";
41    
42  open(my $fh, '<', $path) || die "can't open $path: $!";  open(my $fh, '<', $path) || die "can't open $path: $!";
43    
44    # offset in file
45    my $o = 0;
46    
47    # shared data hash
48    my $d;
49    
50  sub hex_dump {  sub hex_dump {
51          my $bytes = shift || return;          return unless $dump;
52    
53            my ( $bytes, $offset ) = @_;
54            return unless $bytes;
55    
56            my $old_o;
57            if (defined($offset)) {
58                    $old_o = $o;
59                    $o = $offset;
60            }
61    
62          my $ascii = $bytes;          my $ascii = $bytes;
63          $ascii =~ s/\W/./gs;          $ascii =~ s/\W/./gs;
64          my $hex = unpack('h*', $bytes);          my $hex = uc( unpack('h*', $bytes) );
65          $hex =~ s/(..)/$1 /g;          $hex =~ s/(..)/$1 /g;
         my $o = 0;  
66          # calculate number of characters for offset          # calculate number of characters for offset
67          my $d = length( sprintf("%x",length($bytes)) );          #my $d = length( sprintf("%x",length($bytes)) );
68            my $d = 4;
69            my $prefix = '#.';
70          while ( $hex =~ s/^((?:\w\w\s){1,16})// ) {          while ( $hex =~ s/^((?:\w\w\s){1,16})// ) {
71                  printf "## %0${d}x | %-48s| %s\n", $o, $1, substr( $ascii, 0, 16 );                  printf "$prefix %0${d}x | %-48s| %s\n", $o, $1, substr( $ascii, 0, 16 );
72                    $prefix = '##';
73                  if ( length($ascii) >= 16 ) {                  if ( length($ascii) >= 16 ) {
74                          $ascii = substr( $ascii, 16 );                          $ascii = substr( $ascii, 16 );
75                            $o += 16;
76                  } else {                  } else {
77                            $o += length($ascii);
78                          last;                          last;
79                  }                  }
                 $o += 16;  
80          }          }
81    
82            $o = $old_o if $old_o;
83  }  }
84    
85  sub x {  sub x {
# Line 45  sub x { Line 93  sub x {
93    
94          hex_dump( $bytes );          hex_dump( $bytes );
95    
96            if ( $bytes eq 'AMV_END_' ) {
97                    print "> end of file marker AMV_END_\n" if $dump;
98                    $d->{eof}++;
99                    return;
100            }
101    
102          if ( $format ) {          if ( $format ) {
103                  my @data = unpack($format, $bytes);                  my @data = unpack($format, $bytes);
104                  dump(@data);                  print "## unpacked = ",dump(@data),"\n" if $debug;
105                  return @data;                  return @data;
106          } else {          } else {
107                  return $bytes;                  return $bytes;
# Line 57  sub x { Line 111  sub x {
111  sub next_part {  sub next_part {
112          my ( $expected_part, $expected_len, $skip ) = @_;          my ( $expected_part, $expected_len, $skip ) = @_;
113          my ( $part, $len ) = x(8,'A4V');          my ( $part, $len ) = x(8,'A4V');
114            return unless $len;
115          confess "not $expected_part but $part" if $expected_part ne $part;          confess "not $expected_part but $part" if $expected_part ne $part;
116          if ( $expected_len ) {          if ( $expected_len ) {
117                  confess "expected $expected_len bytes for $part got $len" if $len != $expected_len;                  confess "expected $expected_len bytes for $part got $len" if $len != $expected_len;
118          }          }
119          printf ">> %s | %d 0x%x bytes\n", $part, $len, $len;          printf "<< %s - %d 0x%x bytes\n", $part, $len, $len;
120          x($len) if $skip;          x($len) if $skip;
121          return $len;          return $len;
122  }  }
123    
124  my ( $riff, $amv ) = x(12, 'Z8Z4');  sub quality {
125  die "not RIFF but $riff" if $riff ne 'RIFF';          my @table = @_;
126  die "not AMV but $amv" if $amv ne 'AMV ';          die "quantization matrice needs to have 64 bytes!" if $#table != 63;
127    
128            my $in = join('', map { chr($_) } @table );
129            my $out;
130    
131            foreach my $t ( @table ) {
132                    $t = int( ( $t * $jpeg_q ) / 100 );
133                    $t = 255 if $t > 255;
134                    $out .= chr($t);
135            }
136    
137  my $d;          if ( $dump ) {
138                    print "## quantization table original\n";
139                    hex_dump( $in );
140                    print "## quantization table for $jpeg_q %\n";
141                    hex_dump( $out );
142            }
143    
144            return $out;
145    }
146    
147    my @subframes;
148    my $frame_nr = 1;
149    
150    # how many subframes to join into single frame?
151    my $join_subframes = 0;
152    
153  while ( 1 ) {  sub mkjpg {
154            my ($data) = @_;
155    
156            confess "no SOI marker in data" if substr($data,0,2) ne "\xFF\xD8";
157            confess "no EOI marker in data" if substr($data,-2,2) ne "\xFF\xD9";
158            $data = substr($data,2,-2);
159    
160            if ( $#subframes < ( $join_subframes - 1 ) ) {
161                    push @subframes, $data;
162                    print "## saved $frame_nr/", $#subframes + 1, " subframe of ", length($data), " bytes\n";
163                    return;
164            }
165    
166            my $w = $d->{amvh}->{width} || die "no width?";
167            my $h = $d->{amvh}->{height} || confess "no height?";
168    
169            my $header =
170            # Start of Image (SOI) marker
171            "\xFF\xD8".
172            # JFIF marker
173            "\xFF\xE0".
174            pack("nZ5CCCnnCC",
175                    16,                     # length
176                    'JFIF',         # identifier (JFIF)
177                    1,1,            # version
178                    0,                      # units (none)
179                    1,1,            # X,Y density
180                    0,0,            # X,Y thumbnail
181            ).
182            "\xFF\xFE".
183            "\x00\x3CCREATOR: amv dumper (compat. IJG JPEG v62), quality = 100\n".
184            # quantization table (quaility=100%)
185            "\xFF\xDB".
186            "\x00\x43".
187            # 8 bit values, table 1
188            "\x00".
189            quality(
190        0x10, 0x0B, 0x0C, 0x0E, 0x0C, 0x0A, 0x10, 0x0E,
191        0x0D, 0x0E, 0x12, 0x11, 0x10, 0x13, 0x18, 0x28,
192        0x1A, 0x18, 0x16, 0x16, 0x18, 0x31, 0x23, 0x25,
193        0x1D, 0x28, 0x3A, 0x33, 0x3D, 0x3C, 0x39, 0x33,
194        0x38, 0x37, 0x40, 0x48, 0x5C, 0x4E, 0x40, 0x44,
195        0x57, 0x45, 0x37, 0x38, 0x50, 0x6D, 0x51, 0x57,
196        0x5F, 0x62, 0x67, 0x68, 0x67, 0x3E, 0x4D, 0x71,
197        0x79, 0x70, 0x64, 0x78, 0x5C, 0x65, 0x67, 0x63,
198            ).
199            "\xFF\xDB".
200            "\x00\x43".
201            # 8 bit values, table 1
202            "\x01".
203            quality(
204        0x11, 0x12, 0x12, 0x18, 0x15, 0x18, 0x2F, 0x1A,
205        0x1A, 0x2F, 0x63, 0x42, 0x38, 0x42, 0x63, 0x63,
206        0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63,
207        0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63,
208        0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63,
209        0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63,
210        0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63,
211        0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63,
212            ).
213            # start of frame
214            "\xFF\xC0".
215            pack("ncnncc9",
216                    17,                     # len
217                    8,                      # sample precision in bits
218                    $h,$w,          # X,Y size
219                    3,                      # number of components
220                    1,0x22,0,       # Component ID, H+V sampling factors, Quantization table number
221                    2,0x11,1,
222                    3,0x11,1,
223            ).
224            # Define huffman table (section B.2.4.1)
225            "\xFF\xC4".     # Marker
226            "\x00\x1F".     # Length (31 bytes)
227            "\x00".         # DC luminance, table 0
228            "\x00\x01\x05\x01\x01\x01\x01\x01\x01\x00\x00\x00\x00\x00\x00\x00".
229            "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B".
230            # Define huffman table (section B.2.4.1)
231            "\xFF\xC4".     # Marker
232            "\x00\xB5".     # Length (181 bytes)
233            "\x10".         # AC luminance, table 0
234            "\x00\x02\x01\x03\x03\x02\x04\x03\x05\x05\x04\x04\x00\x00\x01\x7D".
235            "\x01\x02\x03\x00\x04\x11\x05\x12".
236            "\x21\x31\x41\x06\x13\x51\x61\x07\x22\x71\x14\x32".
237            "\x81\x91\xA1\x08\x23\x42\xB1\xC1\x15\x52\xD1\xF0".
238            "\x24\x33\x62\x72\x82\x09\x0A\x16\x17\x18\x19\x1A".
239            "\x25\x26\x27\x28\x29\x2A\x34\x35\x36\x37\x38\x39".
240            "\x3A\x43\x44\x45\x46\x47\x48\x49\x4A\x53\x54\x55".
241            "\x56\x57\x58\x59\x5A\x63\x64\x65\x66\x67\x68\x69".
242            "\x6A\x73\x74\x75\x76\x77\x78\x79\x7A\x83\x84\x85".
243            "\x86\x87\x88\x89\x8A\x92\x93\x94\x95\x96\x97\x98".
244            "\x99\x9A\xA2\xA3\xA4\xA5\xA6\xA7\xA8\xA9\xAA\xB2".
245            "\xB3\xB4\xB5\xB6\xB7\xB8\xB9\xBA\xC2\xC3\xC4\xC5".
246            "\xC6\xC7\xC8\xC9\xCA\xD2\xD3\xD4\xD5\xD6\xD7\xD8".
247            "\xD9\xDA\xE1\xE2\xE3\xE4\xE5\xE6\xE7\xE8\xE9\xEA".
248            "\xF1\xF2\xF3\xF4\xF5\xF6\xF7\xF8\xF9\xFA".
249            # Define huffman table (section B.2.4.1)
250            "\xFF\xC4".     # Marker
251            "\x00\x1F".     # Length (31 bytes)
252            "\x01".         # DC chrominance, table 1
253            "\x00\x03\x01\x01\x01\x01\x01\x01\x01\x01\x01\x00".
254            "\x00\x00\x00\x00".
255            "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B".
256            #/* Define huffman table (section B.2.4.1) */
257            "\xFF\xC4".     # Marker
258            "\x00\xB5".     # Length (181 bytes)
259            "\x11".         # AC chrominance, table 1
260            "\x00\x02\x01\x02\x04\x04\x03\x04\x07\x05\x04\x04".
261            "\x00\x01\x02\x77".
262            "\x00\x01\x02\x03\x11\x04\x05\x21".
263            "\x31\x06\x12\x41\x51\x07\x61\x71\x13\x22\x32\x81".
264            "\x08\x14\x42\x91\xA1\xB1\xC1\x09\x23\x33\x52\xF0".
265            "\x15\x62\x72\xD1\x0A\x16\x24\x34\xE1\x25\xF1\x17".
266            "\x18\x19\x1A\x26\x27\x28\x29\x2A\x35\x36\x37\x38".
267            "\x39\x3A\x43\x44\x45\x46\x47\x48\x49\x4A\x53\x54".
268            "\x55\x56\x57\x58\x59\x5A\x63\x64\x65\x66\x67\x68".
269            "\x69\x6A\x73\x74\x75\x76\x77\x78\x79\x7A\x82\x83".
270            "\x84\x85\x86\x87\x88\x89\x8A\x92\x93\x94\x95\x96".
271            "\x97\x98\x99\x9A\xA2\xA3\xA4\xA5\xA6\xA7\xA8\xA9".
272            "\xAA\xB2\xB3\xB4\xB5\xB6\xB7\xB8\xB9\xBA\xC2\xC3".
273            "\xC4\xC5\xC6\xC7\xC8\xC9\xCA\xD2\xD3\xD4\xD5\xD6".
274            "\xD7\xD8\xD9\xDA\xE2\xE3\xE4\xE5\xE6\xE7\xE8\xE9".
275            "\xEA\xF2\xF3\xF4\xF5\xF6\xF7\xF8\xF9\xFA".
276            # Start of Scan marker
277            "\xFF\xDA".
278            pack("nC10",
279                    12,                     # length
280                    3,                      # number of components
281                    1,0x00,         # Scan 1: use DC/AC huff tables 0/0
282                    2,0x11,         # Scan 2: use DC/AC huff tables 1/1
283                    3,0x11,         # Scan 3: use DC/AC huff tables 1/1
284                    0,0x3f,         # Ss, Se
285                    0,                      # Ah, Ai (not used)
286            );
287    
288            if ( $dump ) {
289                    print "## created JPEG header...\n";
290                    hex_dump( $header, 0 );
291            }
292    
293            my $frame = join('', @subframes ) . $data;
294            @subframes = ();
295    
296            my $path = sprintf("$dump_dir/%04d.jpg", $frame_nr++ );
297    
298            my $fh;
299            if ( $jpegtran ) {
300                    open($fh, '|-', "jpegtran $jpegtran > $path") || die "can't create $path: $!";
301            } else {
302                    open($fh, '>', $path) || die "can't create $path: $!";
303            }
304    
305            if ( ! $no_jpeg_header ) {
306                    print $fh $header . $frame . "\xFF\xD9" || die "can't write jpeg $path: $!";
307            } else {
308                    print $fh $frame || die "can't write raw jpeg $path: $!";
309            }
310            close $fh || die "can't close $path: $!";
311            print ">> created $frame_nr ", $no_jpeg_header ? 'raw' : '', " jpeg $path ", -s $path, " bytes\n";
312    }
313    
314    my ( $riff, $amv ) = x(12, 'Z4x4Z4');
315    die "$path not RIFF but $riff" if $riff ne 'RIFF';
316    die "$path not AMV but $amv" if $amv ne 'AMV ';
317    
318    while ( ! defined($d->{eof}) ) {
319          my ( $list, $name ) = x(12,'A4x4A4');          my ( $list, $name ) = x(12,'A4x4A4');
320          die "not LIST but $list" if $list ne 'LIST';          die "not LIST but $list" if $list ne 'LIST';
321          print "> $list .. $name\n";          print "< $list * $name\n";
322    
323          if ( $name eq 'hdrl' ) {          if ( $name eq 'hdrl' ) {
324    
325                  my $len = next_part( 'amvh', hex(38) );                  my $len = next_part( 'amvh', hex(38) );
           
                 print ">> $name $len\n";  
326    
                 my (  
                         $ms_per_frame,  
                         $width,  
                         $height,  
                         $fps,  
                         $ss,  
                         $mm,  
                         $hh,  
                 ) =  
327                  my @names = ( qw/ms_per_frame width height fps ss mm hh/ );                  my @names = ( qw/ms_per_frame width height fps ss mm hh/ );
328                  my $h;                  my $h;
329                  map {                  map {
# Line 101  while ( 1 ) { Line 333  while ( 1 ) {
333                  } x($len, 'Vx28VVVx8CCv');                  } x($len, 'Vx28VVVx8CCv');
334    
335                  printf "## %s %d*%d %s fps (%d ms/frame) %02d:%02d:%02d\n",                  printf "## %s %d*%d %s fps (%d ms/frame) %02d:%02d:%02d\n",
336                          $h->{path},                          $path,
337                          $h->{width}, $h->{height}, $h->{fps}, $h->{ms_per_frame},                          $h->{width}, $h->{height}, $h->{fps}, $h->{ms_per_frame},
338                          $h->{hh}, $h->{mm}, $h->{ss};                          $h->{hh}, $h->{mm}, $h->{ss};
339    
# Line 112  while ( 1 ) { Line 344  while ( 1 ) {
344                  next_part( 'strh', 0, 1 );                  next_part( 'strh', 0, 1 );
345                  next_part( 'strf', 0, 1 );                  next_part( 'strf', 0, 1 );
346    
347            } elsif ( $name eq 'movi' ) {
348    
349                    while (1) {
350                            my $frame = $d->{movi}++;
351                    
352                            my $len = next_part( '00dc' );
353                            last unless $len;
354                            printf "<< %s 00dc - frame %d jpeg %d 0x%x bytes\n", $name, $frame, $len, $len;
355                            mkjpg( x($len) );
356    
357                            $len = next_part( '01wb', 0, 1 );
358                            printf "<< %s 01wb - frame %d audio %d 0x%x bytes\n", $name, $frame, $len, $len;
359                    };
360    
361          } else {          } else {
362                  die "unknown $list $name";                  die "unknown $list $name";
363          }          }
364  }  }
365    
366    my $cmd = "ffmpeg -i $dump_dir/%04d.jpg -r 16 -y $dump_avi";
367    system($cmd) == 0 || die "can't convert frames to avi using $cmd: $!";
368    

Legend:
Removed from v.3  
changed lines
  Added in v.23

  ViewVC Help
Powered by ViewVC 1.1.26