/[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 25 by dpavlin, Sat Aug 18 11:20:25 2007 UTC revision 36 by dpavlin, Tue Oct 2 19:12:07 2007 UTC
# Line 10  Line 10 
10  # http://www.obrador.com/essentialjpeg/HeaderInfo.htm  # http://www.obrador.com/essentialjpeg/HeaderInfo.htm
11  # http://lists.helixcommunity.org/pipermail/datatype-dev/2005-January/001886.html  # http://lists.helixcommunity.org/pipermail/datatype-dev/2005-January/001886.html
12  # http://mpgedit.org/mpgedit/mpeg_format/mpeghdr.htm  # http://mpgedit.org/mpgedit/mpeg_format/mpeghdr.htm
13    # http://wiki.multimedia.cx/index.php?title=IMA_ADPCM
14    
15  use strict;  use strict;
16    
# Line 45  GetOptions( Line 46  GetOptions(
46  my $path = shift @ARGV || die "usage: $0 movie.amv\n";  my $path = shift @ARGV || die "usage: $0 movie.amv\n";
47    
48  # by default, flip frames  # by default, flip frames
49  #$jpegtran = '-flip vertical' unless defined($jpegtran);  $jpegtran = '-flip vertical' unless defined($jpegtran);
50    
51  rmtree $dump_dir if -e $dump_dir;  rmtree $dump_dir if -e $dump_dir;
52  mkpath $dump_dir || die "can't create $dump_dir: $!";  mkpath $dump_dir || die "can't create $dump_dir: $!";
53    
54    $| = 1;
55    
56  open(my $fh, '<', $path) || die "can't open $path: $!";  open(my $fh, '<', $path) || die "can't open $path: $!";
57    
58  # offset in file  # offset in file
# Line 120  sub x { Line 123  sub x {
123          }          }
124  }  }
125    
126    # my $len = next_part( 'boob' );
127    # my ( $len, $part ) = next_part();
128    
129  sub next_part {  sub next_part {
130          my ( $expected_part, $expected_len, $skip ) = @_;          my ( $expected_part, $expected_len, $skip ) = @_;
131          my ( $part, $len ) = x(8,'A4V');          my ( $part, $len ) = x(8,'A4V');
132          return unless $len;          return unless $len;
133          confess "not $expected_part but $part" if $expected_part ne $part;          confess "not $expected_part but $part" if $expected_part && $expected_part ne $part;
134          if ( $expected_len ) {          if ( $expected_len ) {
135                  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;
136          }          }
137          printf "## next_part %s - %d 0x%x bytes\n", $part, $len, $len if $debug;          printf "## next_part %s - %d 0x%x bytes\n", $part, $len, $len if $debug;
138          x($len) if $skip;          x($len) if $skip;
139            return ( $len, $part )  if wantarray;
140          return $len;          return $len;
141  }  }
142    
# Line 156  sub quality { Line 163  sub quality {
163          return $out;          return $out;
164  }  }
165    
 sub mp3_frame {  
         my $frame = join('',  
                 # Frame sync (all bits set)  
                 1 x 11 .  
                 # MPEG Audio version ID  
                 # 00 - MPEG Version 2.5 (unofficial)  
                 # 01 - reserved  
                 # 10 - MPEG Version 2 (ISO/IEC 13818-3)  
                 # 11 - MPEG Version 1 (ISO/IEC 11172-3)  
                 1,0,  
                 # Layer description  
                 # 00 - reserved  
                 # 01 - Layer III  
                 # 10 - Layer II  
                 # 11 - Layer I  
                 0,1,  
                 # Protection bit  
                 # 0 - Protected by CRC (16bit crc follows header)  
                 # 1 - Not protected  
                 0,  
                 # Bitrate index  
                 0,0,0,0,  
                 # Sampling rate frequency index (22050)  
                 0,0,  
                 # Padding bit  
                 # 0 - frame is not padded  
                 # 1 - frame is padded with one extra slot  
                 0,  
                 # Private bit  
                 0,  
                 # Channel Mode  
                 # 00 - Stereo  
                 # 01 - Joint stereo (Stereo)  
                 # 10 - Dual channel (2 mono channels)  
                 # 11 - Single channel (Mono)  
                 1,1,  
                 # Mode extension (Only if Joint stereo)  
                 0,0,  
                 # Copyright  
                 0,  
                 # Original  
                 0,  
                 # Emphasis  
                 # 00 - none  
                 # 01 - 50/15 ms  
                 # 10 - reserved  
                 # 11 - CCIT J.17  
                 0,0,  
         );  
   
         die "frame must have 32 bits, not ", length($frame), " for $frame" if length($frame) != 32;  
   
         my $bits = pack("b32", $frame);  
   
         die "packed bits must be 4 bytes, not $bits" if length($bits) != 4;  
   
         my $t = $frame;  
         $t =~ s/(.{8})/$1 /g;  
         warn "## mp3 frame frame = $t\n";  
   
         return $bits;  
 }  
   
166  my @subframes;  my @subframes;
167  my $frame_nr = 1;  my $frame_nr = 1;
168    
# Line 386  sub mkjpg { Line 330  sub mkjpg {
330          print ">> created $frame_nr ", $no_jpeg_header ? 'raw' : '', " jpeg $path ", -s $path, " bytes\n" if $verbose;          print ">> created $frame_nr ", $no_jpeg_header ? 'raw' : '', " jpeg $path ", -s $path, " bytes\n" if $verbose;
331  }  }
332    
333  my ( $riff, $amv ) = x(12, 'Z4x4Z4');  #
334  die "$path not RIFF but $riff" if $riff ne 'RIFF';  # IMA ADPCM decoder
335  die "$path not AMV but $amv" if $amv ne 'AMV ';  #
336    
337    my @index_adjust = ( -1, -1, -1, -1, 2, 4, 6, 8 );
338    
339    my @step_size = (
340            7, 8, 9, 10, 11, 12, 13, 14, 16, 17,
341            19, 21, 23, 25, 28, 31, 34, 37, 41, 45,
342            50, 55, 60, 66, 73, 80, 88, 97, 107, 118,
343            130, 143, 157, 173, 190, 209, 230, 253, 279, 307,
344            337, 371, 408, 449, 494, 544, 598, 658, 724, 796,
345            876, 963, 1060, 1166, 1282, 1411, 1552, 1707, 1878, 2066,
346            2272, 2499, 2749, 3024, 3327, 3660, 4026, 4428, 4871, 5358,
347            5894, 6484, 7132, 7845, 8630, 9493, 10442, 11487, 12635, 13899,
348            15289, 16818, 18500, 20350, 22385, 24623, 27086, 29794, 32767
349    );
350    
351  my $apath = "$dump_dir/audio.wav";  my $pred_val = 0;
352  open(my $audio_fh, '>', $apath) || die "can't open audio file $apath: $!";  my $step_idx = 0;
353    
354  print $audio_fh pack 'a4Va4a4VvvVVv4', (  # This code is "borrowed" from the ALSA library
355          # header 'RIFF', size  # http://www.alsa-project.org
356          'RIFF',-1,  
357          # type: 'WAVE'  sub adpcm_decode_sample {
358          'WAVE',          my $code = shift;
359          'fmt ',0x14,  
360          # format: DVI (IMA) ADPCM Wave Type          my $pred_diff;  # Predicted difference to next sample
361          0x11,          my $step;               # holds previous step_size value
362          # channels  
363          1,          # Separate sign and magnitude
364          # samples/sec          my $sign = $code & 0x8;
365            $code &= 0x7;
366    
367            # Computes pred_diff = (code + 0.5) * step / 4,
368            # but see comment in adpcm_coder.
369    
370            $step = $step_size[$step_idx] || die "no step_size[$step_idx]";
371    
372            # Compute difference and new predicted value
373            $pred_diff = $step >> 3;
374            my $i = 0x4;
375            while( $i ) {
376                    if ($code & $i) {
377                            $pred_diff += $step;
378                    }
379                    $i >>= 1;
380                    $step >>= 1;
381            }
382            $pred_val += $sign ? -$pred_diff : $pred_diff;
383    
384            # Clamp output value
385            if ($pred_val > 32767) {
386                    $pred_val = 32767;
387            } elsif ($pred_val < -32768) {
388                    $pred_val = -32768;
389            }
390    
391            # Find new step_size index value
392            $step_idx += $index_adjust[$code];
393    
394            if ($step_idx < 0) {
395                    $step_idx = 0;
396            } elsif ($step_idx > 88) {
397                    $step_idx = 88;
398            }
399            return $pred_val;
400    }
401    
402    my $au_path = "$dump_dir/sound.au";
403    open(my $au_fh, '>', $au_path) || die "can't open $au_path: $!";
404    print $au_fh pack 'a4N5', (
405            # magic
406            '.snd',
407            # data offset
408            24,
409            # data size
410            -1,
411            # encoding - 16-bit linear PCM
412            3,
413            # sample rate
414          22050,          22050,
415          # avg. bytes/sec (for esimation)          #channels
416          11567,          1,
         # block align (size of block)  
         0x800,  
         # bits per sample (mono data)  
         4,  
         # cbSize (ADPCM with 7 soefficient pairs)  
         2,  
         # nSamplesPerBlock  
         # (((nBlockAlign - (7 * nChannels)) * 8) / (wBitsPerSample * nChannels)) + 2  
         0x0ff9,  
417  );  );
418    
419  print $audio_fh pack 'a4VVa4V', (  sub audio_frame {
420          # time length of the data in samples          my $data = shift || die "no data?";
421          'fact',4,  
422          220500,          my ( $origin, $index, $bytes ) = unpack 'ssL', substr($data,0,8);
         #  
         'data',-1,  
 );  
423    
424  my $riff_header_len = tell($audio_fh);          $pred_val = $origin;
425            $step_idx = $index;
426    
427            my $size = 0;
428    
429            foreach my $b ( map { ord($_) } split(//, substr($data,8)) ) {
430                    print $au_fh pack 'n', adpcm_decode_sample( $b >> 4 );          
431                    print $au_fh pack 'n', adpcm_decode_sample( $b & 15 );          
432                    $size += 2;
433            }
434    
435            warn "length isn't corrent $bytes != $size" if $debug && $bytes != $size;
436    }
437    
438    #
439    # read AMV file
440    #
441    
442    my ( $riff, $amv ) = x(12, 'Z4x4Z4');
443    die "$path not RIFF but $riff" if $riff ne 'RIFF';
444    die "$path not AMV but $amv" if $amv ne 'AMV ';
445    
446    my $fps = 16;
447    my $duration;
448    
449  while ( ! defined($d->{eof}) ) {  while ( ! defined($d->{eof}) ) {
450          my ( $list, $name ) = x(12,'A4x4A4');          my ( $list, $name ) = x(12,'A4x4A4');
# Line 445  while ( ! defined($d->{eof}) ) { Line 463  while ( ! defined($d->{eof}) ) {
463                          $h->{$n} = $v;                          $h->{$n} = $v;
464                  } x($len, 'Vx28VVVx8CCv');                  } x($len, 'Vx28VVVx8CCv');
465    
466                  printf "## %s %d*%d %s fps (%d ms/frame) %02d:%02d:%02d\n",                  $duration = sprintf('%02d:%02d:%02d', $h->{hh}, $h->{mm}, $h->{ss} );
467    
468                    printf "## %s %d*%d %s fps (%d ms/frame) %s\n",
469                          $path,                          $path,
470                          $h->{width}, $h->{height}, $h->{fps}, $h->{ms_per_frame},                          $h->{width}, $h->{height}, $h->{fps}, $h->{ms_per_frame},
471                          $h->{hh}, $h->{mm}, $h->{ss};                          $duration;
472    
473                  $d->{amvh} = $h;                  $d->{amvh} = $h;
474                    $fps = $h->{fps};
475    
476          } elsif ( $name eq 'strl' ) {          } elsif ( $name eq 'strl' ) {
477    
# Line 459  while ( ! defined($d->{eof}) ) { Line 480  while ( ! defined($d->{eof}) ) {
480    
481          } elsif ( $name eq 'movi' ) {          } elsif ( $name eq 'movi' ) {
482    
483                  while (1) {                  my $have_parts = 1;
484    
485                    while ( $have_parts ) {
486                          my $frame = $d->{movi}++;                          my $frame = $d->{movi}++;
                   
                         my $len = next_part( '00dc' );  
                         last unless $len;  
                         printf "<< %s 00dc - part %d jpeg %d 0x%x bytes\n", $name, $frame, $len, $len if $verbose;  
                         mkjpg( x($len) );  
   
                         $len = next_part( '01wb' );  
                         printf "<< %s 01wb - part %d audio %d 0x%x bytes\n", $name, $frame, $len, $len if $verbose;  
   
                         my $audio_frame = x( $len );  
   
                         if ( $dump_audio ) {  
                                 printf "#### dumping audio frame %d 0x%x bytes\n", length($audio_frame), length($audio_frame);  
                                 hex_dump( $audio_frame );  
                         }  
487    
488                          # remove 8 bytes of something                          my $parts = 0;
                         $audio_frame = substr( $audio_frame, 8 );  
489    
490                          if ( length($audio_frame) % 2 == 0 ) {                          while ( $parts < 2 ) {
491                                  print "#### even sized frame!";  
492  #                               $audio_frame = substr( $audio_frame, 0, -1 );                                  my ( $len, $part ) = next_part();
                         }  
493    
494  #                       print $audio_fh mp3_frame;                                  if ( ! $len ) {
495                          print $audio_fh $audio_frame || die "can't write audio frame in $apath: $!";                                          $have_parts = 0;
496                                            last;
497                                    }
498    
499                                    if ( $part eq '00dc' ) {
500    
501                                            printf "<< %s 00dc - part %d jpeg %d 0x%x bytes\n", $name, $frame, $len, $len if $verbose;
502                                            mkjpg( x($len) );
503                                            $parts++;
504    
505                                    } elsif ( $part eq '01wb' ) {
506                                            printf "<< %s 01wb - part %d audio %d 0x%x bytes\n", $name, $frame, $len, $len if $verbose;
507    
508                                            my $audio_frame = x( $len );
509    
510                                            if ( $dump_audio ) {
511                                                    printf "#### dumping audio frame %d 0x%x bytes\n", length($audio_frame), length($audio_frame);
512                                                    hex_dump( $audio_frame );
513                                            }
514    
515                    #                       print $audio_fh mp3_frame;
516                                            audio_frame( $audio_frame );
517    
518                                            $parts++;
519                                    } else {
520                                            warn "unknown next part $part with $len bytes, skipping!";
521                                    }
522    
523                                    warn "## #$frame_nr $name $part has $parts parts\n" if $debug;
524                            }
525    
526                          $frame_nr++;                          $frame_nr++;
527    
528                            if ( $frame_nr % $fps == 0 ) {
529                                    print "\n" if ( ( $frame_nr / $fps ) % 60 == 0 );
530                                    print ".";
531                            }
532                  };                  };
533    
534          } else {          } else {
# Line 496  while ( ! defined($d->{eof}) ) { Line 536  while ( ! defined($d->{eof}) ) {
536          }          }
537  }  }
538    
539  my $cmd = "ffmpeg -i $dump_dir/%04d.jpg -r 16 -y $dump_avi";  if ( $fps == 12 ) {
540  system($cmd) == 0 || die "can't convert frames to avi using $cmd: $!";          warn "fixup $au_path for $fps fps -- 16000 Hz\n";
541            seek($au_fh, 16, 0);    # sample rate offset
542  my $size = tell($audio_fh);          print $au_fh pack 'N', 16000;
543  warn "## wav file size: $size\n";  }
   
 seek( $audio_fh, 4, 0 );  
 print $audio_fh pack("V", $size - 8);  
 seek( $audio_fh, $riff_header_len - 4, 0 );  
 print $audio_fh pack("V", $size - $riff_header_len);  
544    
545  close($audio_fh) || die "can't close audio file $apath: $!";  my $cmd = "ffmpeg -r $fps -i $dump_dir/%04d.jpg -i $au_path -y $dump_avi";
546    system($cmd) == 0 || die "can't convert frames to avi using $cmd: $!";
547    
548  print ">>>> created $frame_nr frames $dump_avi ", -s $dump_avi, " and $apath ", -s $apath, "\n";  print ">>>> created $frame_nr frames $dump_avi ", -s $dump_avi, "\n";

Legend:
Removed from v.25  
changed lines
  Added in v.36

  ViewVC Help
Powered by ViewVC 1.1.26