/[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

Contents of /amv.pl

Parent Directory Parent Directory | Revision Log Revision Log


Revision 23 - (show annotations)
Sat Jul 21 17:03:56 2007 UTC (16 years, 9 months ago) by dpavlin
File MIME type: text/plain
File size: 9960 byte(s)
use jpegtran -flip vertical to flip frames so that movies are not upside-down.
you can change this using --jpegtran '' to gain some speed (we fork jpegtran
for each frame which is wastefull)
1 #!/usr/bin/perl -w
2
3 # amv.pl
4 #
5 # 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;
14
15 use Data::Dump qw/dump/;
16 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";
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: $!";
43
44 # offset in file
45 my $o = 0;
46
47 # shared data hash
48 my $d;
49
50 sub hex_dump {
51 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;
63 $ascii =~ s/\W/./gs;
64 my $hex = uc( unpack('h*', $bytes) );
65 $hex =~ s/(..)/$1 /g;
66 # calculate number of characters for offset
67 #my $d = length( sprintf("%x",length($bytes)) );
68 my $d = 4;
69 my $prefix = '#.';
70 while ( $hex =~ s/^((?:\w\w\s){1,16})// ) {
71 printf "$prefix %0${d}x | %-48s| %s\n", $o, $1, substr( $ascii, 0, 16 );
72 $prefix = '##';
73 if ( length($ascii) >= 16 ) {
74 $ascii = substr( $ascii, 16 );
75 $o += 16;
76 } else {
77 $o += length($ascii);
78 last;
79 }
80 }
81
82 $o = $old_o if $old_o;
83 }
84
85 sub x {
86 my ($len,$format) = @_;
87
88 my $bytes;
89 read($fh, $bytes, $len);
90
91 my $r_len = length($bytes);
92 confess "read $r_len bytes, expected $len" if $len != $r_len;
93
94 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 ) {
103 my @data = unpack($format, $bytes);
104 print "## unpacked = ",dump(@data),"\n" if $debug;
105 return @data;
106 } else {
107 return $bytes;
108 }
109 }
110
111 sub next_part {
112 my ( $expected_part, $expected_len, $skip ) = @_;
113 my ( $part, $len ) = x(8,'A4V');
114 return unless $len;
115 confess "not $expected_part but $part" if $expected_part ne $part;
116 if ( $expected_len ) {
117 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;
120 x($len) if $skip;
121 return $len;
122 }
123
124 sub quality {
125 my @table = @_;
126 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}) ) {
319 my ( $list, $name ) = x(12,'A4x4A4');
320 die "not LIST but $list" if $list ne 'LIST';
321 print "< $list * $name\n";
322
323 if ( $name eq 'hdrl' ) {
324
325 my $len = next_part( 'amvh', hex(38) );
326
327 my @names = ( qw/ms_per_frame width height fps ss mm hh/ );
328 my $h;
329 map {
330 my $v = $_;
331 my $n = shift @names || die "no more names?";
332 $h->{$n} = $v;
333 } x($len, 'Vx28VVVx8CCv');
334
335 printf "## %s %d*%d %s fps (%d ms/frame) %02d:%02d:%02d\n",
336 $path,
337 $h->{width}, $h->{height}, $h->{fps}, $h->{ms_per_frame},
338 $h->{hh}, $h->{mm}, $h->{ss};
339
340 $d->{amvh} = $h;
341
342 } elsif ( $name eq 'strl' ) {
343
344 next_part( 'strh', 0, 1 );
345 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 {
362 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

Properties

Name Value
svn:executable *

  ViewVC Help
Powered by ViewVC 1.1.26