/[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 31 by dpavlin, Mon Oct 1 16:34:08 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    
166    =for obsolete
167    
168  sub mp3_frame {  sub mp3_frame {
169          my $frame = join('',          my $frame = join('',
170                  # Frame sync (all bits set)                  # Frame sync (all bits set)
# Line 219  sub mp3_frame { Line 228  sub mp3_frame {
228          return $bits;          return $bits;
229  }  }
230    
231    =cut
232    
233  my @subframes;  my @subframes;
234  my $frame_nr = 1;  my $frame_nr = 1;
235    
# Line 386  sub mkjpg { Line 397  sub mkjpg {
397          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;
398  }  }
399    
400  my ( $riff, $amv ) = x(12, 'Z4x4Z4');  #
401  die "$path not RIFF but $riff" if $riff ne 'RIFF';  # IMA ADPCM decoder
402  die "$path not AMV but $amv" if $amv ne 'AMV ';  #
403    
404    my @index_adjust = ( -1, -1, -1, -1, 2, 4, 6, 8 );
405    
406    my @step_size = (
407            7, 8, 9, 10, 11, 12, 13, 14, 16, 17,
408            19, 21, 23, 25, 28, 31, 34, 37, 41, 45,
409            50, 55, 60, 66, 73, 80, 88, 97, 107, 118,
410            130, 143, 157, 173, 190, 209, 230, 253, 279, 307,
411            337, 371, 408, 449, 494, 544, 598, 658, 724, 796,
412            876, 963, 1060, 1166, 1282, 1411, 1552, 1707, 1878, 2066,
413            2272, 2499, 2749, 3024, 3327, 3660, 4026, 4428, 4871, 5358,
414            5894, 6484, 7132, 7845, 8630, 9493, 10442, 11487, 12635, 13899,
415            15289, 16818, 18500, 20350, 22385, 24623, 27086, 29794, 32767
416    );
417    
418  my $apath = "$dump_dir/audio.wav";  my $pred_val = 0;
419  open(my $audio_fh, '>', $apath) || die "can't open audio file $apath: $!";  my $step_idx = 0;
420    
421  print $audio_fh pack 'a4Va4a4VvvVVv4', (  # This code is "borrowed" from the ALSA library
422          # header 'RIFF', size  # http://www.alsa-project.org
423          'RIFF',-1,  
424          # type: 'WAVE'  sub adpcm_decode_sample {
425          'WAVE',          my $code = shift;
426          'fmt ',0x14,  
427          # format: DVI (IMA) ADPCM Wave Type          my $pred_diff;  # Predicted difference to next sample
428          0x11,          my $step;               # holds previous step_size value
429          # channels  
430          1,          # Separate sign and magnitude
431          # samples/sec          my $sign = $code & 0x8;
432            $code &= 0x7;
433    
434            # Computes pred_diff = (code + 0.5) * step / 4,
435            # but see comment in adpcm_coder.
436    
437            $step = $step_size[$step_idx] || die "no step_size[$step_idx]";
438    
439            # Compute difference and new predicted value
440            $pred_diff = $step >> 3;
441            my $i = 0x4;
442            while( $i ) {
443                    if ($code & $i) {
444                            $pred_diff += $step;
445                    }
446                    $i >>= 1;
447                    $step >>= 1;
448            }
449            $pred_val += $sign ? -$pred_diff : $pred_diff;
450    
451            # Clamp output value
452            if ($pred_val > 32767) {
453                    $pred_val = 32767;
454            } elsif ($pred_val < -32768) {
455                    $pred_val = -32768;
456            }
457    
458            # Find new step_size index value
459            $step_idx += $index_adjust[$code];
460    
461            if ($step_idx < 0) {
462                    $step_idx = 0;
463            } elsif ($step_idx > 88) {
464                    $step_idx = 88;
465            }
466            return $pred_val;
467    }
468    
469    my $au_path = "$dump_dir/sound.au";
470    open(my $au_fh, '>', $au_path) || die "can't open $au_path: $!";
471    print $au_fh pack 'a4N5', (
472            # magic
473            '.snd',
474            # data offset
475            24,
476            # data size
477            -1,
478            # encoding - 16-bit linear PCM
479            3,
480            # sample rate
481          22050,          22050,
482          # avg. bytes/sec (for esimation)          #channels
483          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,  
484  );  );
485    
486  print $audio_fh pack 'a4VVa4V', (  sub audio_frame {
487          # time length of the data in samples          my $data = shift || die "no data?";
488          'fact',4,  
489          220500,          my ( $origin, $index, $bytes ) = unpack 'ssL', substr($data,0,8);
490          #  
491          'data',-1,          $pred_val = $origin;
492  );          $step_idx = $index;
493    
494            my $size = 0;
495    
496            foreach my $b ( map { ord($_) } split(//, substr($data,8)) ) {
497                    print $au_fh pack 'n', adpcm_decode_sample( $b >> 4 );          
498                    print $au_fh pack 'n', adpcm_decode_sample( $b & 15 );          
499                    $size += 2;
500            }
501    
502            warn "length isn't corrent $bytes != $size" if $bytes != $size;
503    }
504    
505    
506    sub x_audio_frame {
507            my $data = shift || die "no data?";
508    
509  my $riff_header_len = tell($audio_fh);          my $apath = sprintf("$dump_dir/%04d.wav", $frame_nr );
510            open(my $audio_fh, '>', $apath) || die "can't open audio file $apath: $!";
511    
512            print $audio_fh pack 'a4Va4a4VvvVVv4', (
513                    # header 'RIFF', size
514                    'RIFF',-1,
515                    # type: 'WAVE'
516                    'WAVE',
517                    'fmt ',0x14,
518                    # format: DVI (IMA) ADPCM Wave Type
519                    0x11,
520                    # channels
521                    1,
522                    # samples/sec
523                    22050,
524                    # avg. bytes/sec (for esimation)
525                    11567,
526                    # block align (size of block)
527                    0x800,
528                    # bits per sample (mono data)
529                    4,
530                    # cbSize (ADPCM with 7 soefficient pairs)
531                    2,
532                    # nSamplesPerBlock
533                    # (((nBlockAlign - (7 * nChannels)) * 8) / (wBitsPerSample * nChannels)) + 2
534                    0x03f9,
535            );
536    
537            print $audio_fh pack 'a4VVa4V', (
538                    # time length of the data in samples
539                    'fact',4,
540                    220500,
541                    #
542                    'data',-1,
543            );
544    
545            my $riff_header_len = tell($audio_fh);
546    
547            print $audio_fh $data;
548    
549            my $size = tell($audio_fh);
550            warn "## wav file $apath size: $size\n";
551    
552            seek( $audio_fh, 4, 0 );
553            print $audio_fh pack("V", $size - 8);
554            seek( $audio_fh, $riff_header_len - 4, 0 );
555            print $audio_fh pack("V", $size - $riff_header_len);
556    
557            close($audio_fh) || die "can't close audio file $apath: $!";
558    }
559    
560    #
561    # read AMV file
562    #
563    
564    my ( $riff, $amv ) = x(12, 'Z4x4Z4');
565    die "$path not RIFF but $riff" if $riff ne 'RIFF';
566    die "$path not AMV but $amv" if $amv ne 'AMV ';
567    
568    my $fps = 16;
569    my $duration;
570    
571  while ( ! defined($d->{eof}) ) {  while ( ! defined($d->{eof}) ) {
572          my ( $list, $name ) = x(12,'A4x4A4');          my ( $list, $name ) = x(12,'A4x4A4');
# Line 445  while ( ! defined($d->{eof}) ) { Line 585  while ( ! defined($d->{eof}) ) {
585                          $h->{$n} = $v;                          $h->{$n} = $v;
586                  } x($len, 'Vx28VVVx8CCv');                  } x($len, 'Vx28VVVx8CCv');
587    
588                  printf "## %s %d*%d %s fps (%d ms/frame) %02d:%02d:%02d\n",                  $duration = sprintf('%02d:%02d:%02d', $h->{hh}, $h->{mm}, $h->{ss} );
589    
590                    printf "## %s %d*%d %s fps (%d ms/frame) %s\n",
591                          $path,                          $path,
592                          $h->{width}, $h->{height}, $h->{fps}, $h->{ms_per_frame},                          $h->{width}, $h->{height}, $h->{fps}, $h->{ms_per_frame},
593                          $h->{hh}, $h->{mm}, $h->{ss};                          $duration;
594    
595                  $d->{amvh} = $h;                  $d->{amvh} = $h;
596                    $fps = $h->{fps};
597    
598          } elsif ( $name eq 'strl' ) {          } elsif ( $name eq 'strl' ) {
599    
# Line 459  while ( ! defined($d->{eof}) ) { Line 602  while ( ! defined($d->{eof}) ) {
602    
603          } elsif ( $name eq 'movi' ) {          } elsif ( $name eq 'movi' ) {
604    
605                  while (1) {                  my $have_parts = 1;
606    
607                    while ( $have_parts ) {
608                          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 );  
                         }  
609    
610                          # remove 8 bytes of something                          my $parts = 0;
                         $audio_frame = substr( $audio_frame, 8 );  
611    
612                          if ( length($audio_frame) % 2 == 0 ) {                          while ( $parts < 2 ) {
613                                  print "#### even sized frame!";  
614  #                               $audio_frame = substr( $audio_frame, 0, -1 );                                  my ( $len, $part ) = next_part();
615                          }  
616                                    if ( ! $len ) {
617                                            $have_parts = 0;
618                                            last;
619                                    }
620    
621                                    if ( $part eq '00dc' ) {
622    
623                                            printf "<< %s 00dc - part %d jpeg %d 0x%x bytes\n", $name, $frame, $len, $len if $verbose;
624                                            mkjpg( x($len) );
625                                            $parts++;
626    
627                                    } elsif ( $part eq '01wb' ) {
628                                            printf "<< %s 01wb - part %d audio %d 0x%x bytes\n", $name, $frame, $len, $len if $verbose;
629    
630                                            my $audio_frame = x( $len );
631    
632                                            if ( $dump_audio ) {
633                                                    printf "#### dumping audio frame %d 0x%x bytes\n", length($audio_frame), length($audio_frame);
634                                                    hex_dump( $audio_frame );
635                                            }
636    
637  #                       print $audio_fh mp3_frame;                  #                       print $audio_fh mp3_frame;
638                          print $audio_fh $audio_frame || die "can't write audio frame in $apath: $!";                                          audio_frame( $audio_frame );
639    
640                                            $parts++;
641                                    } else {
642                                            warn "unknown next part $part with $len bytes, skipping!";
643                                    }
644    
645                                    warn "## #$frame_nr $name $part has $parts parts\n";
646                            }
647    
648                          $frame_nr++;                          $frame_nr++;
649    
650                            if ( $frame_nr % $fps == 0 ) {
651                                    print "\n" if ( ( $frame_nr / $fps ) % 60 == 0 );
652                                    print ".";
653                            }
654                  };                  };
655    
656          } else {          } else {
# Line 496  while ( ! defined($d->{eof}) ) { Line 658  while ( ! defined($d->{eof}) ) {
658          }          }
659  }  }
660    
661  my $cmd = "ffmpeg -i $dump_dir/%04d.jpg -r 16 -y $dump_avi";  my $cmd = "ffmpeg -r $fps -i $dump_dir/%04d.jpg -i $au_path -y $dump_avi";
662  system($cmd) == 0 || die "can't convert frames to avi using $cmd: $!";  system($cmd) == 0 || die "can't convert frames to avi using $cmd: $!";
663    
664  my $size = tell($audio_fh);  print ">>>> created $frame_nr frames $dump_avi ", -s $dump_avi, "\n";
 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);  
   
 close($audio_fh) || die "can't close audio file $apath: $!";  
   
 print ">>>> created $frame_nr frames $dump_avi ", -s $dump_avi, " and $apath ", -s $apath, "\n";  

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

  ViewVC Help
Powered by ViewVC 1.1.26