/[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 7 by dpavlin, Thu Jul 19 21:16:30 2007 UTC revision 23 by dpavlin, Sat Jul 21 17:03:56 2007 UTC
# Line 7  Line 7 
7  # Various useful links used to produce this:  # Various useful links used to produce this:
8  # http://www.moviecodec.com/topics/15431p1.html  # http://www.moviecodec.com/topics/15431p1.html
9  # http://en.wikipedia.org/wiki/RIFF_(File_format)  # 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  # offset in file
# Line 24  my $o = 0; Line 48  my $o = 0;
48  my $d;  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;
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)) );
# Line 45  sub hex_dump { Line 78  sub hex_dump {
78                          last;                          last;
79                  }                  }
80          }          }
81    
82            $o = $old_o if $old_o;
83  }  }
84    
85  sub x {  sub x {
# Line 59  sub x { Line 94  sub x {
94          hex_dump( $bytes );          hex_dump( $bytes );
95    
96          if ( $bytes eq 'AMV_END_' ) {          if ( $bytes eq 'AMV_END_' ) {
97                  warn "> end of file marker AMV_END_\n";                  print "> end of file marker AMV_END_\n" if $dump;
98                  $d->{eof}++;                  $d->{eof}++;
99                  return;                  return;
100          }          }
101    
102          if ( $format ) {          if ( $format ) {
103                  my @data = unpack($format, $bytes);                  my @data = unpack($format, $bytes);
104                  warn "## unpacked = ",dump(@data),"\n";                  print "## unpacked = ",dump(@data),"\n" if $debug;
105                  return @data;                  return @data;
106          } else {          } else {
107                  return $bytes;                  return $bytes;
# Line 81  sub next_part { Line 116  sub next_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            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    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}) ) {  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    
# Line 108  while ( ! defined($d->{eof}) ) { Line 333  while ( ! defined($d->{eof}) ) {
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 124  while ( ! defined($d->{eof}) ) { Line 349  while ( ! defined($d->{eof}) ) {
349                  while (1) {                  while (1) {
350                          my $frame = $d->{movi}++;                          my $frame = $d->{movi}++;
351                                    
352                          my $len = next_part( '00dc', 0, 1 );                          my $len = next_part( '00dc' );
353                          last unless $len;                          last unless $len;
354                          printf ">> %s 00dc - frame %d jpeg %d 0x%x bytes\n", $name, $frame, $len, $len;                          printf "<< %s 00dc - frame %d jpeg %d 0x%x bytes\n", $name, $frame, $len, $len;
355                            mkjpg( x($len) );
356    
357                          my $len = next_part( '01wb', 0, 1 );                          $len = next_part( '01wb', 0, 1 );
358                          printf ">> %s 01wb - frame %d audio %d 0x%x bytes\n", $name, $frame, $len, $len;                          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.7  
changed lines
  Added in v.23

  ViewVC Help
Powered by ViewVC 1.1.26