#!/usr/bin/perl

#
#  vdrsync (c) 2003-2004 by  Dr. Peter Sebbel, a perl script to demux VDR recordings and 
#  correcting for missing/additional  Audio frames to ensure sync of audio and 
#  video streams. Contact: peter@vdr-portal.de
#
#   This program is free software; you can redistribute it and/or modify
#   it under the terms of the GNU General Public License as published by
#   the Free Software Foundation; version 2 of the License
#
#  This program is distributed in the hope that it will be useful,
#  but WITHOUT ANY WARRANTY; without even the implied warranty of
#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#  GNU General Public License for more details.

#  You should have received a copy of the GNU General Public License
#  along with this program; if not, write to the Free Software
#  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA



use strict;
use warnings;
use POSIX qw(strftime);

my $version           = "0.1.2.2"; # dev 2
my @parameter_list    = @ARGV;              
my $remuxfilename     = "";

my $main_pid = $$;

#########################################################################
# Hash that lists potential helper Programs
#########################################################################
my %progs = (
    tcmplex           => "",
    mplex             => "",
    tcmplex_panteltje => "",
    dvdauthor         => "",
    mkisofs           => "",
    transcode         => "",
    tcrequant         => "",
    requant           => "",
    "dvd-menu.pl"     => "",
    "vdrsync-buffer.pl"=>"",
    "vdrsync.pl"      =>"",
);




############################################################################
# Options passed to the PES-Stream Module
############################################################################

my @ignore_streams;
my $dump_packets      = 0;
my $dump_payload      = 0;
my $audio_only        = 0;
my $script_output     = 0;
my $show_drift        = 0;
my $audio_filter      = "";
my $video_filter      = "";
my $ac3_filter        = "";
my $script_output_file= "";
my $cut_recording     = 0;
my $stdin             = 0;
my $dump_buffer       = 0;                  # In case of a serious error, dump the current buffer

############################################################################
# Options used in the PES-Module and also in the "User Interface Part"
############################################################################

my $verbose           = 0;
my $basename          = "vdrsync";
my $info              = 0;
my $debug             = 0;
my $path_param        = "./";               # default path for all output files

############################################################################
# Options relevant for postprocessing / User Interface Part
############################################################################

my $divx              = 0;
my $divxac3           = 0;
my $divx_param        = " -V -y divx4 -w 1000 "; #enter additional parameters for divx here
my $divx_ac3_param    = " -V -y divx4 -b 256 -w 1000 "; #enter additional parameters for divx with ac3 sound here
my $master_dvd        = 0;                  # Create DVD Structure at the end (-master-dvd)
my $master_dvd_param  = "";                 #
my $fit_to_dvd        = 0;                  # Fit to 4.3 GB using requant
my $postexec          = 0;                  # run the following command at the end

my $mkiso             = 0;                  # Create a DVD-Iso-Image at the end
my $quiet             = 1;                  # Be quite an do not output anything
my $chapter           = 300;                # Standard DVD Chapter Intervall
my @dir_list;                               # List of recording directories passed on the CL
my $movie_length      = 0;                  # Length of the movie in sec. Deduced from index.vdr or at the end of the run
my $asp_ratio         = "4:3";              # Aspect Ratio used by dvd-author, returned after run
my $use_pipe          = 0;                  # Defines whether the Streams a directly piped to a multiplexer or not
my %ignore_hash;                            # Hash of streams that are ignored
my $mplex             = 0;                  # Do we need to mplex?
my $mpeg2             = 0;                  # Should we mplex to generic mpeg2 (not DVD compat)
my (@files, $index_file);


##### Which multiplexer should be used?
my $panteltje         = 0;                  #uses tcmplex-panteltje 
my $mjpeg             = 0;                  # use mplex (default!)
my $transc_mpx        = 0;                  # use tcmplex
############################################################################

# Options for DVD Menu Creation...
my $dvd_menu             = 0;
my @dvd_titles;
my @dvd_texts;
my $dvd_title_set_no;
my @dvd_bg_images;
my $dvd_root_text;
my $dvd_root_title;
my $dvd_root_image;
my $current_title_set = 0;

############################################################################


sub true  () { 1 };
sub false () { 0 };

$| = 1;

###
# main
usage() if (scalar(@ARGV) == 0);

SearchPRGs();
parse_parameters();
validate_params();




if ($info) {
    compute_info();
    exit;
}



if ((! @dir_list) && (@files || $stdin)) {
    my $PES_Obj = create_PS_Stream_Object();
    if (!$PES_Obj) {
        print "could not create the PES Stream Obj\n";
        exit 1;
    }
    # some fallback?
    $PES_Obj->process_PES_packets($_);
    post_process($PES_Obj);
}
else {
    
    if ($fit_to_dvd) {
        my $recs = join(" ", @dir_list);
        my $cmd = $progs{"vdrsync.pl"} . " $recs -i -script-output";
        if ($cut_recording) {
            $cmd .= " -cut";
        }
        my @results = `$cmd`;
        my $all = join(" ", @results);
       
        #print $all;
        
        (my $requant_factor) = $all =~ /Requant_Factor=(\S+)/;
        
        dprint ("Determined a requant Faktor of $requant_factor\n");
        
        if ($requant_factor > 1) {
            if ($progs{tcrequant}) {
                $video_filter = " $progs{tcrequant} -f $requant_factor ";
            } elsif ($progs{requant}){
                $video_filter = " $progs{requant} $requant_factor ";
            } else {
                die "I need a program to shrink the video stream, but could not neither find tcrequant nor requant\n";
            }
        }
    }
    
    if ($use_pipe) {
        #print "PREPARING FILTERS\n" x 10;
        prepare_filters();
    }
    
    
    
    foreach (@dir_list) {
        
        if ($use_pipe) {
            my $timer = 0;
            dprint("Checking log file /tmp/vdrsync_lock_$main_pid\n");
            #print "LOOK ABOVE\n" x 10;
            sleep 2;
            while (-f "/tmp/vdrsync_lock_$main_pid") {
                $timer++;
                sleep 1;
                if ($timer % 5 == 0) { 
                    print "Waiting for multipex / authoring to finish for $_\n";
                }
                if ($timer > 300) {                 
                    die "Something went very wrong\n";
                }
            }
            dprint ("log file /tmp/vdrsync_lock_$main_pid not there\n");
            open  OFH, ">/tmp/vdrsync_lock_$main_pid" or die "Could not open lockfile: $!\n";
            print OFH localtime();
            close OFH;
            dprint ("log file /tmp/vdrsync_lock_$main_pid created\n");
        }
        if ($use_pipe) {
            my @fifo_list = @{ prepare_fifos($_) };
            prepare_forks(@fifo_list);
        }
        dprint("Preparing everything for $_\n");
        my $PES_Obj = create_PS_Stream_Object($_);
        if (!$PES_Obj) {
            eprint("could not create the PES Stream Obj");
        }
        
        $PES_Obj->process_PES_packets();
        #print "about to jump to postprocess\n";
        post_process($PES_Obj);
        $PES_Obj = "";
        print "\nfinished processing $_ \n";
        $current_title_set++; 
        
    }
    if ($use_pipe) {
        my $timer = 0;
        dprint ("Observing log file /tmp/vdrsync_lock_$main_pid\n");
        while (-f "/tmp/vdrsync_lock_$main_pid") {
            $timer++;
            sleep 1;
            if ($timer % 5 == 0) { 
                print "Waiting for multipex / authoring to finish\n";
            }
            if ($timer > 300) {
                die "Something went very wrong\n";
            }
        }
        dprint ("log file /tmp/vdrsync_lock_$main_pid gone\n");
    }
    
    #sleep 1;
    #print "exiting main prog\n";
    #exit;
}


if ($mkiso) {
    nprint("Creating ISO Image...");
    execute (sprintf("mkisofs -dvd-video -o \"%s/%s\" \"%s/%s\"",
      $path_param,
      "$basename.iso",
      $path_param,
      $basename));
}



if ($postexec) {
    print "Executing: $postexec\n";
    #if ($postexec =~ /VSlength/) {
     #   my $length = $PES_Obj->get_movie_length();
     #   $postexec =~ s/VSlength/$length/g;
    #}
    exec " $postexec ";
}



exit;

##
# Helpers
# #

sub prepare_filters {
    #print "$all";
    #$audio_filter = " ./thread.pl.old audio%d_fifo ";
    if ($audio_filter) {
        $audio_filter = " vdrsync_buffer.pl STDOUT 2>/dev/null | " . $audio_filter  . " > /tmp/audio%d_fifo ";
    } else {
        $audio_filter = " vdrsync_buffer.pl /tmp/audio%d_fifo 2>/dev/null ";
    }
    
    if ($video_filter) {
        $video_filter = " vdrsync_buffer.pl STDOUT  2>/dev/null | " . $video_filter  . " > /tmp/video_fifo 2> /dev/null ";
    } else {
        $video_filter = " vdrsync_buffer.pl /tmp/video_fifo 2>/dev/null";
    }
    
    if ($ac3_filter) {
        $ac3_filter = " vdrsync_buffer.pl STDOUT 2>/dev/null| " . $ac3_filter . " > /tmp/ac3_fifo ";
    } else {
        $ac3_filter = " vdrsync_buffer.pl /tmp/ac3_fifo 2>/dev/null";
    }
    dprint ("$audio_filter\n");
    dprint ("$video_filter\n");
    dprint ("$ac3_filter\n");
}

sub prepare_fifos {

    my $input_dir = shift;
    my @fifo_list;
    
    #my $PES_Info_Obj = create_PS_Stream_Object($input_dir);
    #my @results = @{$PES_Info_Obj->compute_record_info()};
    my $cmd = $progs{"vdrsync.pl"} ." $_ -i -script-output";
    my @results = `$cmd`;
    
    #$PES_Info_Obj = "";
    
    my $all = join "", @results;
    #print "$all";
        
    if (-e "/tmp/video_fifo") {
        system "rm /tmp/video_fifo";
        dprint ("deleting video_fifo\n");
    }
    my $result = execute ("mkfifo /tmp/video_fifo");
    dprint ("Tried to create video fifo with result $result\n");
    push @fifo_list, "/tmp/video_fifo";
    
    
    my $stream_id = "Audio_stream_bd=yes";
    if ($all =~ /$stream_id/ ) {
        print "Need to create a fifo for stream $stream_id\n";
        if (-e "/tmp/ac3_fifo") {
            system "rm /tmp/ac3_fifo";
            dprint ("deleting ac3_fifo\n");
        }
        $result = execute ("mkfifo /tmp/ac3_fifo");
        dprint ("Tried to create fifo with result $result\n");
        push @fifo_list, "/tmp/ac3_fifo",
    }
    
    for (my $i = 0; $i < 7; $i++) {
        $stream_id = "c$i" . "_Audio_stream=yes";
        #print "Testing for stream $stream_id...\n";
        if ($all =~ /$stream_id/ ) {
            if (-e ("/tmp/audio$i" . "_fifo")) {
            #if (-e ("./audio" . "_fifo")) {
                system ("rm /tmp/audio$i" . "_fifo");
      #          print "deleting ./audio$i" . "_fifo\n";
            }
      #      print "Need to create a fifo for stream $stream_id\n";
            $result = execute ("mkfifo /tmp/audio$i", "_fifo");
      #      print "Tried to create fifo with result $result\n";
            push @fifo_list,  "/tmp/audio$i". "_fifo";
            
        }
    }
    foreach (@fifo_list) {
        dprint ("$_ is on the fifo list\n");
    }
    ($asp_ratio) = $all =~/Aspect_ratio=(.+)/;
    
    ($movie_length) = $all =~/e.{1}_Total_time=(.+)/;
    
    dprint ("Setting Aspect Ratio to $asp_ratio and Movie Length to $movie_length\n");
    #print "aspect ratio is $asp_ratio\n";
    return \@fifo_list;
}    
sub prepare_forks {    
    my @fifo_list = @_;
    my $pid;
    my @pidlist;
    my $target;
    
    if ($master_dvd) {
        $target = "/tmp/remuxfifo";
        if (-p "/tmp/remuxfifo") {
            dprint ("remuxfifo does exist\n");
        } else {
            my $result = execute ("mkfifo  /tmp/remuxfifo");
            dprint ("the attempt to create FIFO returned $result\n");
        }
    } elsif ($mplex) {
        $target = "$path_param/$basename.mpg";
    } else {
        eprint ("Nothing to fork...\n");
    }
    

    if ($pid = fork) {
        if ($master_dvd) {
            my $pid2;
            if ($pid2 = fork) {
                return;
            } else {
                add_to_dvd_image($target);
                unlink("/tmp/vdrsync_lock_$main_pid");
                exit;
            }
        } 
        return;
    }
    else {
        dprint ("Setting up mutliplex process\n");
        multiplex($target, \@fifo_list);
        if (! $master_dvd) {
             unlink("/tmp/vdrsync_lock_$main_pid");
        }
        exit;
    }
}
    # else {
    #     nprint ("Creating DVD structure using dvdauthor...");
    #     execute(
    #     sprintf("%s -v %s %s -c %s -o \"%s/%s\" %s/remuxfifo",
    #     $progs{dvdauthor},
    #      $asp_ratio,
    #      $master_dvd_param,
    #      #dvdauthor_length(),
    #      "0,10:00,20:00,30:00,40:00,50:00,01:00:00,01:10:00,01:20:00,01:30:00,01:40:00,01:50:00,02:00:00,02:20:00,02:30:00,02:40:00,02:50:00,03:00:00",
    #      $path_param,
    #      $basename,
    #      $path_param
    #     )
    #    );
    #    print "DVDauthor finished\n";
    ##    nprint ("Creating table of content...");
    #    execute(sprintf("%s -T -o \"%s/%s\"", $progs{dvdauthor}, $path_param, $basename));
    #    sleep 1;
    #    print "DVDauthor finished\n";
    #    exit;
    #}



sub post_process {
    dprint ("arrived at postprocess\n");
    my $PES_Obj = shift;
    $movie_length = $PES_Obj->get_movie_length();
    $asp_ratio = $PES_Obj->get_aspect_ratio();
    #return;  
    if (! $use_pipe) {
        if ($master_dvd) {
            my @filestomux = @{$PES_Obj->output_files()};
            #dprint("Got " . scalar(@filestomux) ." files back\n");
            #my $mplex_command = multiplex(@filestomux);
            create_dvd_image( \@filestomux);
            clean_up(@filestomux);
        } elsif ($mplex) {
            my @filestomux = @{$PES_Obj->output_files()};
            dprint("Got " . scalar(@filestomux) ." files back\n");
            multiplex("$basename.mpg", \@filestomux);
            clean_up(@filestomux);
        }
    } 
    dprint ("Leaving postprocess\n");
}


sub multiplex {
    my $target = shift;
    my @filestomux = @{ shift @_ };
    
   # my $mplex_command;

    dprint ("arrived at multiplex\n");

    if (scalar(@filestomux) < 2) {
        eprint("Need at least two streams for multiplexing");
    }
    if ($panteltje) {
        dprint ("Trying to use tcmplex-panteltje \n");
        mpx_panteltje($target, \@filestomux);
    } elsif ($transc_mpx) {    
        mpx_tcmplex($target, \@filestomux);
        #print "Using tcmplex...\n";
    }
    else {
        dprint ("Trying to use mplex from mjpegtools\n");
        mpx_mjpeg_mplex($target, \@filestomux);
    } 
    print "Finished multiplexing\n";
}

sub mpx_panteltje {
    
    my $target = shift;
    my @filestomux = @{ shift @_ };
   
    my $mplex_command;

    $mplex_command = "nice -n 19 $progs{tcmplex_panteltje} -i $filestomux[0] -0 $filestomux[1]";
    dprint("Mplexing using tcmplex-panteltje\n");
    my $counter = 2;
    while (scalar(@filestomux) > $counter) {
        dprint("adding to $counter $filestomux[$counter]\n");
        $mplex_command .= " -" . ($counter-1) . " $filestomux[$counter]";
        $counter++;
    }
    $remuxfilename .= "$basename.mpg";
    if ($mpeg2) {
        $mplex_command .= " -m 2 ";
    } 
    else {
        $mplex_command .= " -m d ";
    }
    
    dprint("Saving File as $remuxfilename\n");
    #if (! $master_dvd)    {
        $mplex_command .= "-o \"$target\"";
        nprint("\nStarting multiplexer");
        dprint("\nexecuting $mplex_command\n");
        execute($mplex_command);
    #} else {
    #    return $mplex_command;
    #}
    
}

sub mpx_tcmplex {
    
    my $target = shift;
    my @filestomux = @{ shift @_ };
        
    my $mplex_command;

    $mplex_command = "nice -n 19 $progs{tcmplex} -i $filestomux[0] -p $filestomux[1]";
    if (scalar(@filestomux) > 2) {
        $mplex_command .= " -s $filestomux[2]";
    }
    $remuxfilename .= "$basename.mpg";

    if ($mpeg2) {
        $mplex_command .= " -m 2 ";
    } 
    else {
        $mplex_command .= " -m d ";
    }
    
    dprint("Saving File as $remuxfilename\n");
    
    #if (! $master_dvd)    {
        $mplex_command .= "-o \"$target\"";
        nprint("\nStarting multiplexer");
        dprint("\nexecuting $mplex_command\n");
        execute($mplex_command);
    #} else {
    #    return $mplex_command;
    #}
}

sub mpx_mjpeg_mplex {
    
    my $target = shift;
    my @filestomux = @{ shift @_ };
    
    my $mplex_command;

    $mplex_command = "nice -n 19 $progs{mplex} ";
    
    if ($mpeg2) {
        $mplex_command .= " -V -f 3 -r 9800 ";
    } else {
        $mplex_command .= " -f 8 ";
    }
    
    
    if (! $master_dvd) {
        $target =~ s/\.mpg/%d\.mpg/;
        print "Modfied Target to $target\n";
        
    }
    $mplex_command .= " -o $target ";
    $mplex_command .= " $filestomux[0]  $filestomux[1]";
    my $counter = 2;
    while (scalar(@filestomux) > $counter) {
        dprint("adding to $counter $filestomux[$counter]\n");
        $mplex_command .= " $filestomux[$counter]";
        $counter++;
    }

    dprint("Saving File as $remuxfilename\n");
    
   # if (! $master_dvd)    {
        #$mplex_command .= "-o \"$remuxfilename\"";
        nprint("\nStarting multiplexer");
        dprint("\nexecuting $mplex_command\n");
        execute($mplex_command);
    #} else {
    #    return $mplex_command;
    #}
}


sub create_PS_Stream_Object {
    my $indir = shift;
    if ($indir) {
        @files = @{ get_file_list($indir) };
        dprint ("Preparing stream Object for input dir $indir") ;
        foreach (@files) {
            $_ = "$indir/$_";
            dprint ("Using file $_\n");
        }
    }
    
    my $PES_Object = MPEGSTREAM->new(
      streamcode     =>"PES_stream",
      debug          => $debug,
      outputpath     => $path_param,
      files          => \@files,
      ignore_streams => \%ignore_hash,
      dump_packets   => $dump_packets,
      dump_payload   => $dump_payload,
      audio_only     => $audio_only,
      show_drift     => $show_drift,
      script_output  => $script_output,
      script_output_file  => $script_output_file,
      dump_buffer    => $dump_buffer,
      verbose        => $verbose,
      basename       => $basename,
      audio_filter   => $audio_filter,
      video_filter   => $video_filter,
      ac3_filter     => $ac3_filter,
      indir          => $indir,
      info           => $info,
      cut_recording  => $cut_recording,
      stdin          => $stdin,
      use_pipe       => $use_pipe,
      
    );
    return $PES_Object;
}


sub compute_info {
    my $counter = 0;
    my $temp_dir = "/tmp/";
    my $total_video = 0;
    my $total_audio = 0;
    foreach my $dir (@dir_list) {
        print "Processing $dir for info\n";
        @files = ();
        my @dir_files = @{ get_file_list($dir) };
        foreach (@dir_files) {
            push @files, "$dir/$_";
        }
           
        my $PES_Obj = create_PS_Stream_Object($dir);
        
        my @results = @{ $PES_Obj->compute_record_info() };
        foreach (@results) {
            #print "FROM RESULTS: $_";
            if ($_  =~ /Video_Total_bytes=(\d+)/) {
                $total_video += $1;
            }
            if ($_  =~ /Audio_Total_bytes=(\d+)/) {
                $total_audio += $1;
            }
        }
        $counter++;
        
        $PES_Obj = "";
    }
    if (!@dir_list) {
        my $PES_Obj = create_PS_Stream_Object();
        my @results = @{ $PES_Obj->compute_record_info() };
        foreach (@results) {
            #print "FROM RESULTS: $_";
            if ($_  =~ /Video_Total_bytes=(\d+)/) {
                $total_video += $1;
            }
            if ($_  =~ /Audio_Total_bytes=(\d+)/) {
                $total_audio += $1;
            }
        }
    }
    $total_video /= 1000000;
    $total_audio /= 1000000;
    my $sum = $total_video + $total_audio;
    my $too_much = 0;
    my $requant_factor = 1;
    if ($sum >= 4440) {
        $too_much = $sum - 4440;
        $requant_factor = $total_video / ($total_video - $too_much) ;
    }
    
    print "\n\ntotal_video=$total_video\ntotal_audio=$total_audio\nSum=$sum\n";
    print "oversize=$too_much\n" if ($requant_factor != 1);
    print "Requant_Factor=$requant_factor\n\n";
    return $requant_factor;
}


sub dvdauthor_length {
    #my $length = 10800;
    my $length = $movie_length;
    dprint ("Movie has length of $length\n");
    #my $chapter = 300; # set chapter all $chapter seconds
    my $minute = 0;
    my @string;
    push(@string, $minute . ":00");
    while($length > 59) {
        $minute = $minute + ($chapter / 60);
        push(@string, $minute . ":00");
        $length = $length - $chapter;
   }
   return join(",", @string);
   #return "0,10:00,20:00,30:00,40:00,50:00,01:00:00,01:10:00,01:20:00,01:30:00,01:40:00,01:50:00,02:00:00,02:10:00,02:20:00,02:30:00,02:40:00,02:50:00";
}

sub execute {
    my $cmd = join("", @_);
    print "$cmd IS THE FULL COMMAND\n"  if($debug);
    $cmd = "$cmd > /dev/null 2> /dev/null" if $quiet;
    system($cmd);
    if($? != 0) {
        printf("Error while running '%s'\n", $cmd);
        return 1;
    }
    return 0;
}

sub dprint {
    my $msg = join("", @_);
    chomp $msg;
    print "$msg\n" if $debug;
}

sub nprint {
    print @_, "\n";
}

sub eprint{
    my $msg = shift;
    die "$msg\n";
}


sub add_to_dvd_image {
  
    my $remuxfifo = shift || "/tmp/remuxfifo";
    my $timer = 0;
    
    nprint "\nCreating DVD structure using dvdauthor...";
    
    dprint("Current title set is $current_title_set\n");
    
    
    if ($dvd_menu) {
        #print "Title List = " . (join "\n",@dvd_titles);
        #print "Text List = " . (join "\n",@dvd_titles);
        #die;
        my $cmd = sprintf("%s -title title=\"%s\",text=\"%s\",vob=\"%s\",chapters=\"%s\"",
          $progs{"dvd-menu.pl"},
          ($dvd_titles[$current_title_set]),
          ($dvd_texts[$current_title_set]),
          $remuxfifo,
          dvdauthor_length(),
          );
          
        if ($dvd_bg_images[0]) {
            my $bg_image = $dvd_bg_images[$current_title_set];
            $cmd .= ",background=\"$bg_image\"";
        }
        
        $cmd .= sprintf(" -run-dvdauthor -o \"%s/%s\" -font-color white -enhance-text-area 20 ",    
            $path_param, 
            $basename,
            );
            dprint("Executing: $cmd\n\n");
        execute($cmd);
        
    } else {
                
        execute(
           sprintf("%s -v %s %s -c %s -o \"%s/%s\" \"%s\"",
           $progs{dvdauthor},
           $asp_ratio,
           $master_dvd_param,
           dvdauthor_length(),
           $path_param,
           $basename,
           $remuxfifo,
           )
        );
    }
    
    finalize_dvd_image();
}


sub finalize_dvd_image {
    if ($dvd_menu) {
        my $cmd = sprintf("%s -root title=\"%s\",text=\"%s\",buttons=%s",
              $progs{"dvd-menu.pl"},
              $dvd_root_title,
              $dvd_root_text,
              scalar(@dir_list)
            );
          
        if ($dvd_root_image) {
            $cmd .= ",background=\"$dvd_root_image\"";
        }         
        $cmd .= sprintf( " -run-dvdauthor -o \"%s/%s\" -font-color white -enhance-text-area 20",
          $path_param, 
          $basename,
          );
        dprint("Executing: $cmd\n\n");
        execute($cmd);
           
    } else {
        nprint "\nCreating table of content...";
        execute(sprintf("%s -T -o \"%s/%s\"", $progs{dvdauthor}, $path_param, $basename));
        unlink("/tmp/remuxfifo");
        sleep 1;
    }
}

sub create_dvd_image {
    
    print "Start dvdimage creation\n";
    #my $mplex_command = shift;
    
    my @files_to_mux = @{ shift @_ };
    my $pid;
    if (-p "/tmp/remuxfifo") {
        print "remuxfifo does exist\n";
    } else {
        my $result = execute "mkfifo  /tmp/remuxfifo";
        dprint "the attempt to create FIFO returnd $result\n";
    }
    
    
    if ($pid = fork) {
        #nprint "Creating DVD structure using dvdauthor...";
        add_to_dvd_image("/tmp/remuxfifo");
        #finalize_dvd_image();
        #execute(
        #  sprintf("%s -v %s %s -c %s -o \"%s/%s\" /tmp/remuxfifo",
        #  $progs{dvdauthor},
        ##  $asp_ratio,
        #  $master_dvd_param,
        #  dvdauthor_length(),
        #  $path_param,
        #  $basename,
        #  
        #  )
        #);
       
        #nprint "Creating table of content...";
        #execute(sprintf("%s -T -o \"%s/%s\"", $progs{dvdauthor}, $path_param, $basename));
        #unlink("/tmp/remuxfifo");
        #sleep 1;
        
    }
    else {
        
        
        dprint "mplexing file to fifo\n";
        multiplex("/tmp/remuxfifo", \@files_to_mux);
        
        #dprint "should execute $mplex_command and put it to the fifo\n";
        #$mplex_command .= " -o /tmp/remuxfifo";
        #dprint "now the command is $mplex_command\n";
        #nprint "Multiplexing streams for dvdauthor...";
        #execute $mplex_command;
        dprint "finished mplexing\n";
        exit;
    }
    
    
}

sub clean_up {
    return;
    my @filestomux = @_;
    nprint "Housekeeping...";
    dprint "Deleting temp files\n";
    for my $delfile (@filestomux) {
        if (-e $delfile){
            dprint("$delfile\n");
            unlink($delfile);
        }
    }
    if (-e  "$path_param"."remuxfifo") {
        unlink ("$path_param"."remuxfifo");
    }
    if ($mkiso) {
        my $command = sprintf("rm -r \"%s%s\"", $path_param, $basename);
        dprint "Deleting DVD Files with: $command\n";
        execute $command;
        
    }
}

sub get_file_list {
    my $indir = shift;
    my $DIR;
    opendir $DIR, $indir  or die "Can not open $indir $!\n";
    dprint "trying to open $indir\n";
    
    #my @allfiles =   grep { ! /^\./  } readdir $DIR;
    #my @vdrfiles =   sort (grep { /\d{3}.vdr$/  } @allfiles);
    my @vdrfiles =   sort (grep { /\d{3}.vdr$/  } readdir $DIR);
    return \@vdrfiles;
    #$index_file = $indir . "index.vdr" if (-e "$indir/index.vdr");
    #$summary_file = $indir . "summary.vdr" if (-e "$indir/summary.vdr");
    #$marks_file = $indir . "marks.vdr" if (-e "$indir/marks.vdr");
}


sub SearchPRGs {
    #my $program = shift;
    my @path = split(":", "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/bin/X11:/vdr/bin");
    my $found = false;
    foreach my $program(keys(%progs)) {
        for my $path (@path) {
            my $file = $path . "/$program";
            if (-e $file) { 
                $found = $file;
                
                $progs{$program} = $file;
                last;
            }    
        } 
    }
    # return($found);
    #foreach(keys(%progs)) {
    #    print "final: $_ at $progs{$_}\n" if $progs{$_};
    #}
  
}

#sub SearchPRG {
#  my $program = shift;
#  my @path = split(":", "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/bin/X11:/vdr/bin");
#  my $found = false;
#  for my $path (@path) {
#    my $file = $path . "/$program";
#    if(-e $file) { 
#      $found = $file;
#    } 
#  } 
#  return($found);
#}

sub parse_parameters {
    while (my $param = shift(@parameter_list))  { # check whether all files hat we got as parameters exist 
        dprint("Got parameter $param\n");
        if ($param =~ /^-/) {
            parse_parameter($param);
            next;
        }
        if (-d $param) {
            dprint("got a directory on the command line\n");
            #$indir = $param;
            push @dir_list, $param;
            #my @dir_files = @{ get_file_list($param) };
            #foreach (@dir_files) {
            #    push @files, "$param/$_";
            #}
            next;
        }
        if (-f $param) {
            push @files, $param;
        } else {
            die "Could not find file $param\n";
        }
    }
    if ($dvd_menu) {
        fix_dvd_menus();
    }
}
    
    
sub parse_parameter {    
    
    
    my $parameter = shift;
    if ($parameter eq "-d") {
        $debug = 1;
        print "Setting debug to 1\n";
    }
    elsif ($parameter eq "-buffer-only") {
        #print "Got $parameter_list[0]\n";
        if ((! $parameter_list[0]))  {# || (! (-e $parameter_list[0]))) {
            eprint ("need a fifo as parameter for buffer-only");
        }
        buffer_only($parameter_list[0]);
    }
    elsif ($parameter eq "-o")    {
        $path_param = shift @parameter_list;
        if (!($path_param =~ /\/$/)) {
            $path_param .= "/";
        }
    }
    elsif ($parameter eq "-m")    {
        $mplex = 1;
        if ($parameter_list[0] && ($parameter_list[0] eq "panteltje")){
            $panteltje = (shift @parameter_list);
        } elsif ($parameter_list[0] && ($parameter_list[0] eq "tcmplex")) {
            $transc_mpx = (shift @parameter_list);
        } 
    }
    elsif ($parameter eq "-mpeg2") {
        $mpeg2 = 1;
        $master_dvd = 0;
        $mplex = 1;
        $mkiso = 0;
    }
    elsif ($parameter eq "-i")    {
        $info    = 1;
    }
      elsif ($parameter eq "-use-pipe")    {
        $use_pipe  = 1;
    }
    elsif ($parameter eq "-stdin")    {
        $stdin   = 1;
    }
    elsif ($parameter eq "-ignore")    {
         @ignore_streams = split /,/,(shift @parameter_list);
        foreach (@ignore_streams) {
            $ignore_hash{$_} = 1;
        }
    }
    elsif ($parameter eq "-dump-packets"){
        $dump_packets = (shift @parameter_list);
    }
    elsif ($parameter eq "-show-time-drift"){
        $show_drift = 1;
    }
     elsif (($parameter eq "-cut-recording") || ($parameter eq "-cut")){
        $cut_recording = 1;
    }
    elsif ($parameter eq "-dump-debug-buffer"){
        $dump_buffer = 1;
    }
    elsif ($parameter eq "-dump-payload"){
        $dump_payload = (shift @parameter_list);
    }
    elsif ($parameter eq "-audio-only")    {
        $audio_only = 1;
    }
    elsif ($parameter =~ /-script-output/)    {
        $script_output = 1;
        if ($parameter =~ /-script-output=(\w+)/) {
            $script_output_file = $1;
        }
    }
    elsif ($parameter eq "-v" || $parameter eq "-verbose" )    {
        $verbose = 1;
        $quiet   = 0;
    }
    elsif ($parameter eq "-divx") {
        $divx = 1;
        if ($parameter_list[0] eq "ac3"){
            $divxac3 = (shift @parameter_list);
        }
    }
    elsif ($parameter eq "-master-dvd"){
        $master_dvd = 1;
        $mplex = 1;
        if ( ! $parameter_list[0] =~/^-/ ){
            $master_dvd_param = (shift @parameter_list);
        }
    }
    elsif ($parameter eq "-mkiso"){
        $mkiso = 1;
        $mplex = 1;
        $master_dvd = 1;
    }
    elsif ($parameter eq "-chapter") {
        $chapter = shift(@parameter_list);
        if (($chapter !~ /\d+/) ||
          ($chapter % 60)) {
            die "Chapter Intervalls have to be specified in seconds, and must be multiple of 60\n";
        }
    }
    elsif ($parameter eq "-postexec"){
        $postexec  = (shift @parameter_list);
    }
   # elsif ($parameter eq "-isoname"){
   #     $isoname = shift(@parameter_list);
   # } 
    elsif ($parameter eq "-help" || $parameter eq "-h" || $parameter eq "--help"|| $parameter eq "-?") {
        usage();
    }
    elsif ($parameter eq "-basename") {
        $basename = shift(@parameter_list);
    }
    elsif ($parameter eq "-a-filter") {
        $audio_filter = shift(@parameter_list);
    }
    elsif ($parameter eq "-v-filter") {
        $video_filter = shift(@parameter_list);
    }
    elsif ($parameter eq "-ac3-filter") {
        $ac3_filter = shift(@parameter_list);
    }
    elsif ($parameter eq "-dvd-menu") {
        $dvd_menu = 1;
        $mplex = 1;
        $master_dvd = 1;
    }
    elsif ($parameter eq "-dvd-menu-images") {
        @dvd_bg_images = split /(?=[^\\]),/, shift @parameter_list;
    }
    elsif ($parameter eq "-dvd-menu-titles") {
        @dvd_titles = @{ split_dvd_menu_lists(shift @parameter_list)  };
    }
    elsif ($parameter eq "-dvd-root-title") {
        $dvd_root_title = shift @parameter_list;
    }
    elsif ($parameter eq "-dvd-root-labels") {
        $dvd_root_text = shift @parameter_list;
    }
    elsif ($parameter eq "-dvd-root-image") {
        $dvd_root_image = shift @parameter_list;
        if (! -f $dvd_root_image)  {
            die "$dvd_root_image does not exist!\n";
        }
    }
    elsif ($parameter eq "-dvd-menu-texts") {
        @dvd_texts = @{ split_dvd_menu_lists(shift @parameter_list)  };
    } 
    elsif ($parameter eq "-fit-to-dvd") {
        $fit_to_dvd = 1;
        
    }
    else {
        print "Unknown parameter $parameter\n";
        usage();
        exit;
    }
}

sub validate_params {
    print "Parameter validation not complete yet\n";
    if (!(-e $path_param)) {
        eprint ("Output Directory $path_param does not exist"); 
    }
    dprint("Output files will be stored in $path_param\n");
    dprint("Found " . (scalar(@ignore_streams)) ." to ignore\n");
    
    if ($transc_mpx && (! $progs{tcmplex})) {
        die "Usage of tcmplex was specified, but the program could not be found within your path\n";
    }
    if ($panteltje && (! $progs{tcmplex_panteltje})) {
        die "Usage of tcmplex-panteltje was specified, but the program could not be found within your path\n";
    }
    if ($mplex && (! $progs{tcmplex_panteltje}) && (! $progs{tcmplex}) && (! $progs{mplex})) {
        die "Need to multiplex the files, but did not find a single multiplexer\n";
    }
    if ($mplex && (! $panteltje) && (! $transc_mpx) && (! $progs{mplex})) {
        if ($progs{tcmplex_panteltje}) {
            $panteltje = 1;
        } elsif ($progs{tcmplex}) {
            $transc_mpx = 1;
        } else {
            die "Did not find a supported multiplexer in your path, but need one\n";
        }
    }
    
    if (($master_dvd || $mkiso) && (! $progs{dvdauthor})) {
        die "Did not find dvdauthor in your path, needed for -master-dvd and for -mkiso options\n";
    }
    if ($mkiso && (! $progs{mkisofs})) {
        die "Did not find mkisofs in your path, needed for -mkiso option\n";
    }
    
    if (@files && @dir_list) {
        print "files :";
        print @files;
        print "\ndirs: ";
        print @dir_list;
        print "\n";
        die "Mixing of directories and single files as Input not supported\n";
        
    }
    if ($stdin && $use_pipe) {
        die "Sorry, -use-pipe not yet availabe with -stdin\n";
    }
    if (@files && $use_pipe) {
        die "Sorry, -use-pipe only available if a directory was specified as input\n";
    }
    
}


sub fix_dvd_menus {
    my $title_set_no = scalar(@dir_list) || 1; # If Input comes from STDIN, there is no @dir_list
    my $dummy_root_text = "";
    for (my $i = 0; $i < $title_set_no; $i++) {
        if (! $dvd_titles[$i]) {
            if ($dir_list[$i]) {
                $dvd_titles[$i] = (split /\//, $dir_list[$i])[-2] ;
            } 
            if (!$dvd_titles[$i]) {
                $dvd_titles[$i] = "No information available!";
            }
        }
        #print "setting name for title set $i to $dvd_titles[$i]\n";
        if (! $dvd_texts[$i]) {
            if ($dir_list[$i]) {  # else we have Input from STDIN
                if ( -f "$dir_list[$i]/summary.vdr") {
                    $dvd_texts[$i] = "$dir_list[$i]/summary.vdr";
                    #print "setting text for title set $i to $dvd_texts[$i]\n";
                }
            }
            if(!$dvd_texts[$i])  {
                $dvd_texts[$i] = "No Text available!";
            }
        } 
        
        #print "setting text for title set $i to $dvd_texts[$i]\n";
        if (! $dvd_bg_images[$i]) {
            if (($dir_list[$i]) && (-f "$dir_list[$i]/bg.jpg")) {
                $dvd_bg_images[$i] = "$dir_list[$i]/bg.jpg";
                #print "setting background for title set $i to $dvd_bg_images[$i]\n";
            }
        }
        $dummy_root_text .= "$dvd_titles[$i]|";
        if ((! $dvd_root_image) && ( -f "$dir_list[$i]/main.jpg")) {
            $dvd_root_image = "$dir_list[$i]/main.jpg";
        }
    }
    
    $dvd_root_title = $dvd_root_title || "Menu created by VDRsync";
    $dvd_root_text  = $dvd_root_text  || $dummy_root_text;
    
    #print "Settings for root: $dvd_root_bg_image, $dvd_root_text; $dvd_root_title\n";

}

sub split_dvd_menu_lists {
    my $param = shift;
    my @list = split /(?=[^\\]),/, $param;
    return \@list;
}


sub usage {
  
  
    print qq{
        VDRsync    Version $version
          
        Usage:     vdrsync.pl /path/to/vdr/recording/
        or   :     vdrsync.pl vdr-file1 .... vdr-fileN
        
        VDRsync is a tool  to demux and sync VDR recordings. It can 
        also pass the  resulting  Audio  and  Videostreams to other 
        applications to allow  a  single-command  conversion  of a 
        recording to a DVD-Structure
        
        Options:
        
        -a-filter " PRG " 
            pipes MP2 Audio through the specified Filter. Do not
            forget to redirect the output to a file! A good choice 
            would be "> basename&d.mpa", since then the postprocessing
            commands find the output file.If you do not specify the 
            basename option, redirect to vdrsync0.mpa (that is the default
            basename)
        
        -ac3-filter " PRG " 
            pipes AC3 Audio through the specified Filter. Do not
            forget to redirect the output to a file! A good choice 
            would be "> basename.ac3", since then the postprocessing
            commands find the output file. If you do not specify the 
            basename option, redirect to vdrsync.ac3 (that is the default
            basename)
        
        -audio-only 
            discards all video streams and just writes audio
                
        -basename <name>
            set a name prefix for all output files and DVD Directories
        
        
        -chapter <x> 
            sets chapter marks all <x> seconds (must be a multiple
            of 60)

        -cut
            cuts the recording according to the marks.vdr
        
        -d 
            print strange debug messages

        -divx 
            tries to transcode the demuxed streams into a divx movie
            For DIVX  with  AC3  (if  present in the recording) type 
            -divx ac3 (uses transcode, not yet usable)
        
        -dump-debug-buffer 
            in case of a problem with one of the streams 3 MB of
            the recording are dumped to the file debug.buffer
        
        -dump-packets NNNN 
            tells the script to dump  the first NNNN full PES-packets
            (to file STREAM_ID.pes_dump) Useful for debugging (if you
            want  to extract  a stream that does not work and mail it 
            to me)
        
        -dump-payload NNNN 
            tells the script to dump  the  raw payload of the first 
            NNNN PES-packets  (in file STREAM_ID.dump)  Useful  for 
            debugging (if you want to extract a stream-payload that
            does not work and test it with other tools)
    
        -dvd-menu 
            Tells vdrsync to generate a DVD Menu for the recording
            (implies -master-dvd). See the following options for more
            information
        
        -dvd-menu-images
            a comma-separated list of image files that will be used as
            background images for the DVD Menus. If you do not specify images
            the script will look for a file called bg.jpg in the recording 
            directory. If this is also not found, a white background will be 
            used. It is also possible to not specify an image for one menu, 
            but for the others. Just make sure you have the appropriate number of 
            commas in the list. Example:
            -dvd-menu-images ,bg.jpg,bg2.jpg,
            would leave first Title Set menu nachground empty, define a background 
            for the second and third title set and leave all other title
            sets blank
            
        -dvd-menu-titles
            a comma-separated list of titles (or text files) that 
            will be used as caprions for the title set DVD Menus. 
        
        -dvd-menu-texts
            a comma-separated list of texts or text files that will be used 
            as text for DVD Menus. See also -dvd-menu-images
        
        -dvd-root-image
            the image for the DVD main menu background (jpeg)
        
        -dvd-root-labels
            a | separated List of the Button Labels in the main DVD menu. Have a
            look at the examples at the end of this text.
            See also -dvd-menu-images
            
        -dvd-root-title
            a "|" separated list of titles (or text files) that 
            will be used as titles for DVD Menus. First entry in the list 
            ist the title for the main menu, then the titles for the title 
            set menus follow. See also -dvd-menu-images
        
        -fit-to-DVD
            Tries to estimate the amount of data and adds a tcrequant filter
            to shrink the video "on-the-fly" to allow everything to fit on 
            a single DVD. If tcrequant is not found, requant is used if 
            available.
        
        -i 
            just tries to analyse the .vdr file and exits. All other
            Options are ignored
        
        -ingore stream1[,stream2]...  
            tells the script to ignore  stream1..n. Useful for 
            deliberately omitting streams (like AC3) or if you have 
            trouble with one stream
        
        -m 
            tries to multiplex the demuxed streams into a dvd 
            compatible stream (uses mplex from the mjpegtools)
        
        -m panteltje 
            uses tcmplex-panteltje instead of tcmplex
        
        -master-dvd  
            creates a DVD structure in a subdirectory of outputpath
        
        -mkiso 
            creates an ISO Image suitable for burning a DVD
        
        -mpeg2 
            creates a generic MPEG2 File (FIXME)
                
        -o output_dir  
            sets the directory where the result files are written to
                
        -postexec COMMAND
            executes the command specified after finishing. Some 
            Dummy variables like VSlength are replaced by actual 
            values (not yet usable)
               
        -script-output[=filename]
            writes a special output format  about the movie at the
            end of the vdrsync run. Useful for scripting, contains
            corrected info about the streams.  If you specify file
            name, the  information  is  written  to filename. Very 
            handy four "source-ing" that file.
        
        -show-time-drift 
            prints information about the difference between time-
            stamps and actual video / audio data in the recording
        
        -stdin 
            expect input from stdin, all files and directories are 
            ignored

        -use-pipe 
            uses FIFOs all the way from recording to DVD structure
            Experimental! Needs quite a bit of memory, but does not
            consume HD Space and speeds up conversion by  20% on my
            system. Combine with -cut for the most space  efficient 
            conversion I know of...
            
        -v-filter " PRG " 
            pipes Video through the specified Filter.Do not
            forget to redirect the output to a file! A good choice 
            would be "> basename.mpv", since then the postprocessing
            commands find the output file. If you do not specify the 
            basename option, redirect to vdrsync.mpv (that is the default
            basename)
    
    Examples:
    
    A simple demux and sync run:
    vdrsync.pl 001.vdr
        or
    vdrsync.pl /video/MyRecording/DATE/
        
    Specifiying the output location and filename:
    vdrsync.pl /video/MyRecording/DATE/ -o /film/ -basename NewFilm
        
    Postprocessing:
    vdrsync.pl /video/MyRecording/DATE/ -m
    vdrsync.pl /video/MyRecording/DATE/ -master-dvd
    vdrsync.pl /video/MyRecording/DATE/ -mpeg2
    vdrsync.pl /video/MyRecording/DATE/ -mkiso
   
    Save time and space by using FIFOs:
    vdrsync.pl /video/MyRecording/DATE/ -master-dvd -use-pipe
    
    Cutting:
    vdrsync.pl /video/MyRecording/DATE/  -cut
    
    DVD Menus:
    vdrsync.pl /video/MyRecording/DATE/ -dvd-menu 
    
    Full featured with all the "goodies" and multiple recordings:
    vdrsync.pl 
        /video/MyRecording1/DATE/       \\
        /video/MyRecording2/DATE/       \\
        -dvd-menu                       \\
        -cut                            \\
        -use-pipe                       \\
        -dvd-menu-images bg1.jpg,bg2.jpg                             \\
        -dvd-menu-titles "Item 1","Item 2"                           \\
        -dvd-menu-texts  summary1.vdr,summary2.vdr                   \\
        -dvd-root-title  "My Collection "                            \\
        -dvd-root-labels "Jump to Item 1|Jump to item2"              \\
        -dvd-root-image  main.jpg                                    \\
        -fit-to-DVD
        \n};
  
    exit;
} 

#############################################################################
# MPEGSTREAM part begins here
# 
BEGIN
{
package      MPEGSTREAM;
require      Exporter;
# A place available to all instances for storing the cuts in the file...
our @cutlist;
our $kill_me = "";    
our $total_size =     0;
our $bytes_read =     0;
our $module_verbose = 0;
our %bitrates =    (
    MPEG1_Layer_1 => {
        "0001"    => "32",
        "0010"    => "64",
        "0011"    => "96",
        "0100"    => "128",
        "0101"    => "160",
        "0110"    => "192",
        "0111"    => "224",
        "1000"    => "256",
        "1001"    => "288",
        "1010"    => "320",
        "1011"    => "352",
        "1100"    => "384",
        "1101"    => "416",
        "1110"    => "448",
    },
    MPEG1_Layer_2 => {
        "0001"    => "32",
        "0010"    => "48",
        "0011"    => "56",
        "0100"    => "64",
        "0101"    => "80",
        "0110"    => "96",
        "0111"    => "112",
        "1000"    => "128",
        "1001"    => "160",
        "1010"    => "192",
        "1011"    => "224",
        "1100"    => "256",
        "1101"    => "320",
        "1110"    => "384",
    },
    MPEG1_Layer_3 => {
        "0001"    => "32",
        "0010"    => "40",
        "0011"    => "48",
        "0100"    => "56",
        "0101"    => "64",
        "0110"    => "80",
        "0111"    => "96",
        "1000"    => "112",
        "1001"    => "128",
        "1010"    => "160",
        "1011"    => "192",
        "1100"    => "224",
        "1101"    => "256",
        "1110"    => "320",
    },
    MPEG2_Layer_1 => {
        "0001"    => "32",
        "0010"    => "64",
        "0011"    => "96",
        "0100"    => "128",
        "0101"    => "160",
        "0110"    => "192",
        "0111"    => "224",
        "1000"    => "256",
        "1001"    => "288",
        "1010"    => "320",
        "1011"    => "352",
        "1100"    => "384",
        "1101"    => "416",
        "1110"    => "448",
    },
    MPEG2_Layer_2 => {
        "0001"    => "32",
        "0010"    => "48",
        "0011"    => "56",
        "0100"    => "64",
        "0101"    => "80",
        "0110"    => "96",
        "0111"    => "112",
        "1000"    => "128",
        "1001"    => "160",
        "1010"    => "192",
        "1011"    => "224",
        "1100"    => "256",
        "1101"    => "320",
        "1110"    => "384",
    },
    MPEG2_Layer_3 => {
        "0001"    => "8",
        "0010"    => "16",
        "0011"    => "24",
        "0100"    => "32",
        "0101"    => "64",
        "0110"    => "80",
        "0111"    => "56",
        "1000"    => "64",
        "1001"    => "128",
        "1010"    => "160",
        "1011"    => "112",
        "1100"    => "128",
        "1101"    => "256",
        "1110"    => "320",
    },
);
our %freqs =(
        MPEG1 => {
            "00" =>  "44100",
            "01" =>  "48000",
            "10" =>  "32000",
        },
        MPEG2 => {
            "00" => "22050",
            "01" => "24000",
            "10" => "16000",
        },
);

our %AC3_frame_info =    (
    "32000" => {
        "000000"    => "192",
        "000001"    => "192",
        "000010"    => "240",
        "000011"    => "240",
        "000100"    => "288",
        "000101"    => "288",
        "000110"    => "336",
        "000111"    => "336",
        "001000"    => "384",
        "001001"    => "384",
        "001010"    => "480",
        "001011"    => "480",
        "001100"    => "576",
        "001101"    => "576",
        "001110"    => "672",
        "001111"    => "672",
        "010000"    => "768",
        "010001"    => "768",
        "010010"    => "960",
        "010011"    => "960",
        "010100"    => "1152",
        "010101"    => "1152",
        "010110"    => "1344",
        "010111"    => "1344",
        "011000"    => "1536",
        "011001"    => "1536",
        "011010"    => "1920",
        "011011"    => "1920",
        "011100"    => "2304",
        "011101"    => "2304",
        "011110"    => "2688",
        "011111"    => "2688",
        "100000"    => "3072",
        "100001"    => "3072",
        "100010"    => "3456",
        "100011"    => "3456",
        "100100"    => "3840",
        "100101"    => "3840",
    },
    "44100" => {
        "000000"    => "138",
        "000001"    => "140",
        "000010"    => "194",
        "000011"    => "196",
        "000100"    => "208",
        "000101"    => "210",
        "000110"    => "242",
        "000111"    => "244",
        "001000"    => "278",
        "001001"    => "280",
        "001010"    => "348",
        "001011"    => "350",
        "001100"    => "416",
        "001101"    => "418",
        "001110"    => "486",
        "001111"    => "488",
        "010000"    => "556",
        "010001"    => "558",
        "010010"    => "696",
        "010011"    => "698",
        "010100"    => "834",
        "010101"    => "836",
        "010110"    => "974",
        "010111"    => "976",
        "011000"    => "1114",
        "011001"    => "1116",
        "011010"    => "1392",
        "011011"    => "1394",
        "011100"    => "1670",
        "011101"    => "1672",
        "011110"    => "1950",
        "011111"    => "1952",
        "100000"    => "2228",
        "100001"    => "2230",
        "100010"    => "2506",
        "100011"    => "2508",
        "100100"    => "2786",
        "100101"    => "2788",
    },
    "48000" => {
        "000000"    => "128",
        "000001"    => "120",
        "000010"    => "160",
        "000011"    => "160",
        "000100"    => "192",
        "000101"    => "192",
        "000110"    => "224",
        "000111"    => "224",
        "001000"    => "256",
        "001001"    => "256",
        "001010"    => "320",
        "001011"    => "320",
        "001100"    => "384",
        "001101"    => "384",
        "001110"    => "448",
        "001111"    => "448",
        "010000"    => "512",
        "010001"    => "512",
        "010010"    => "640",
        "010011"    => "640",
        "010100"    => "768",
        "010101"    => "768",
        "010110"    => "896",
        "010111"    => "896",
        "011000"    => "1024",
        "011001"    => "1024",
        "011010"    => "1280",
        "011011"    => "1280",
        "011100"    => "1336",
        "011101"    => "1336",
        "011110"    => "1792",
        "011111"    => "1792",
        "100000"    => "2048",
        "100001"    => "2048",
        "100010"    => "2304",
        "100011"    => "2304",
        "100100"    => "2560",
        "100101"    => "2560",
    },
    "bitrate" => {
        "000000"    => "32000",
        "000001"    => "32000",
        "000010"    => "40000",
        "000011"    => "40000",
        "000100"    => "48000",
        "000101"    => "48000",
        "000110"    => "56000",
        "000111"    => "56000",
        "001000"    => "64000",
        "001001"    => "64000",
        "001010"    => "80000",
        "001011"    => "80000",
        "001100"    => "96000",
        "001101"    => "96000",
        "001110"    => "112000",
        "001111"    => "112000",
        "010000"    => "128000",
        "010001"    => "128000",
        "010010"    => "160000",
        "010011"    => "160000",
        "010100"    => "192000",
        "010101"    => "192000",
        "010110"    => "224000",
        "010111"    => "224000",
        "011000"    => "256000",
        "011001"    => "256000",
        "011010"    => "320000",
        "011011"    => "320000",
        "011100"    => "384000",
        "011101"    => "384000",
        "011110"    => "448000",
        "011111"    => "448000",
        "100000"    => "512000",
        "100001"    => "512000",
        "100010"    => "576000",
        "100011"    => "576000",
        "100100"    => "640000",
        "100101"    => "640000",
    },
);
    
our $mp2_regex = 
        #(pack ("B16", "1111111111110000")) . "|" .
        #(pack ("B16", "1111111111110001")) . "|" .
        #(pack ("B16", "1111111111110010")) . "|" .
        #(pack ("B16", "1111111111110011")) . "|" .                      These are Mpeg 2 Audio  Headers, 
        #(pack ("B16", "1111111111110100")) . "|" .                They should not occur
        #(pack ("B16", "1111111111110101")) . "|" .
        #(pack ("B16", "1111111111110110")) . "|" .
        #(pack ("B16", "1111111111110111")) . "|" .
        
        #(pack ("B16", "1111111111111000")) . "|" .
        #(pack ("B16", "1111111111111001")) . "|" .
        #(pack ("B16", "1111111111111010")) . "|" .                  These are Layer I and III headers
        #(pack ("B16", "1111111111111011")) . "|" .
        #(pack ("B16", "1111111111111110")) . "|" .
        #(pack ("B16", "1111111111111111")) . "|" .
        
        (pack ("B16", "1111111111111100")) . "|" .
        (pack ("B16", "1111111111111101"))        
        ;    
    
$mp2_regex = qr/$mp2_regex/;

our $AC3_regex =
        (pack("B16", "0000101101110111"));  # AC3 Frames always have this 16 Bit Syncword at the beginning
        
$AC3_regex = qr/$AC3_regex/;        


sub get_cutlist { return @cutlist; }

our @ISA       = qw(Exporter);
our @EXPORT    = qw(    add_PES_packet
            bitrate
            streamtype
            streamcode
            print_stats
            output_files
            process_PES_packets
            compute_record_info
           );

our $VERSION   = 0.1.2.2; #dev2 DEVELOPERS VERSION; # Version number




sub new {
    my $invocant = shift;
    my $class   = ref($invocant) || $invocant;
    
    my $self = {
    streamcode             => "PES_stream", #will be overridden
    streamtype             => undef,    # 
    bitrate                => undef,    # only for Audio
    freq                   => undef,    # only for Audio 
    copyright              => undef,    # only for Audio
    frame_bytes            => undef,    # only for Audio
    frame_ticks            => undef,    # one tick is 1 / 90000 of a second
    frameno                => 0,        # READ frames counter, not written frames
    GOPno                  => 0,        # just for video stats
    packet_start           => undef,    # a signature that is found a the beginning of a frame, should read frame_start
    packet_rest            => "",       # only audio, if a frame does not finish in a PES-Packet, the first part of the frame is stored here
    check_frames           => undef,    # a placeholder for the sub that actually manages the frames of a given type (mp2 / ac3 / mpv)
    analyse_frames         => undef,    # a placeholder for the sub that actually analyses the frames of a given type (mp2 / ac3 )
    masterstream           => "",       # here the stream_id of the videostream is stored (it is not always e0 as I thought first)
    outputpath             => "./",     # where to store the result files
    outfilename            => "",       # The name of the output file
    cutcounter             => 0,        # used as an index to the cuts list (see below), for sync we need the current and the previous cut
    last_ts                => 0,        # last timestamp that was read
    final_desync           => [],
    GOP_buffer             => "",       # A GOP is buffered here before analysed and written
    GOP_ts                 => 0,        # Here we store the first GOP ts, for the strrange recordings that have no ts at GOP start
    frame_buffer           => [],       # an array that holds the buffered audio frames
    frame_times            => [],       # an array that holds the timestamp for each audio frame
    frame_lengths          => [],       # an array that holds the length for each audio frame
    frames_written         => 0,        # used only for stats
    timestamps             => [],       # all timesstamps are stored here, probably a waste of memory, but maybe used for stricter checks
    written_chunks         => [],       # an array of scalars for debugging, from where to where were Audio STreams written?
    final_desync           => 0,        # how many ticks desync after correction? Used in the next sync attempt
    show_drift             => 0,        # if set to 1, info about timedrift will be printed
    time_drift             => 0,        # The current mismatch between TS and actual Pakets found
    audio_only             => 0,        # if this is set, all video Streams are discarded and audio ist just written out
    script_output          => 0,
    basename               => "vdrsync",# basename for all output files
    audio_filter           => "",       # filter chain for MP2 Audio
    video_filter           => "",       # filter chain for Video
    ac3_filter             => "",       # filter chain  for ac3 Audio
    first_GOP_ts           => 0,
    verbose                => 0,        # print a some information
    indir                  => "",
    Unit_analysis_counter  => 0,
    Unit_analysis_hash     => {},       # Here the different settings for apsepect_ratio etc are collected for later print
    first_flush            => 1,        # This flag indicates that it is the first time that the audio buffer is flushrd, an a start sync is needed
    info                   => 0,        # just print some info about the recording
    
    ############################### # Buffer Handling
    buffer                 => "",       # Holds the chunk that is currently processed
    offset                 => 0,        # Offset of the next Paket that is to be processed
    total_input            => 0,
    cut_recording          => 0,
    ############################### # Info vars
    packet_buffer          => {},       # Holds payloads of every stream for later analysis
    
    ###############################
    
    
    @_,                                 # Override previous attributes
    };
    if ($self->{streamcode} eq "PES_stream")    {
        
        if ((scalar(@{$self->{files}}) == 0) && (! $self->{stdin}))   {
            print "No input specified, exiting\n"; exit 1 ;
        }
        if ($self->{verbose}) {
            $module_verbose = 1;
        }
        foreach (@{$self->{files}})  {# check whether all files that we got as parameters exist
            if (!(-e $_)) {print "Input file $_ not found\n"; exit 1 }
        }
        $self->{packets_before_sync} = 0;
        print "dump only the first $self->{dump_packets} packets\n"  if $self->{dump_packets};
        foreach (keys (%{$self->{ignore_streams}})) {print "Ignoring stream $_\n";}
        print "Lots of debug stuff will be printed\n"  if $self->{debug};
        print "Printing Information about the time drift\n" if $self->{show_drift};
        $total_size = 0;
        foreach(@{$self->{files}}) {
            $total_size += -s $_;
        }
        init_PES_stream($self);
        vprint("Filtering Output through $audio_filter\n");
        vprint("Total Input Size is $total_size\n");
    }
    return bless $self, $class;
}

sub vprint {
    #my $self = shift;
    my $msg = join("", @_);
    #my $msg = shift;
    chomp($msg);
    print "$msg\n" if $module_verbose;
}



sub init_stream    {
    my $self = shift;
    my $payload = shift;
    my %pes_header = %{shift @_};
    my $decimal_code = hex($pes_header{stream_id});
    print "we got the decimal code $decimal_code for $pes_header{stream_id}\n"if $self->{debug};
    if (($decimal_code > 191) && ($decimal_code < 224))        {
        print "we got the stream $decimal_code ($pes_header{stream_id}), checking Audio\n"if $self->{debug};
        analyse_audio_stream($self, $payload, \%pes_header);
        #$self->{streamtype} = "audio";
    }
    elsif (($decimal_code > 223) && ($decimal_code < 240))    {
        print "we got the stream $decimal_code ($pes_header{stream_id}), checking Video\n"if $self->{debug};
        analyse_video_stream($self, $payload, \%pes_header);
        $self->{streamtype} = "video";
    }
    elsif ($decimal_code == 189)        {
        analyse_ac3_stream($self, $payload, \%pes_header);
        $self->{streamtype} = "AC3_audio";
    }
    else    {
        print "unknown stream type found, skipping contents of stream $pes_header{stream_id}\n";
    }
}


sub output_files {
    my $self = shift;
    
    if ($self->{streamcode} ne "PES_stream") {return}
    
    my @audiofiles;
    my $moviefile;
    my @filelist;
    my $ac3file;
    
    foreach(keys(%{$self->{streams}})) {
        if ($self->{ignore_streams}{$_}){
            next;
        }
        if ( $_ =~ /^c/ ) {
            push @audiofiles, $self->{streams}{$_}{outfilename};
        }
        elsif ( $_ =~ /^b/ ) {
            $ac3file = $self->{streams}{$_}{outfilename};
        }
        else {
            $moviefile = $self->{streams}{$_}{outfilename};
        }
    }
    @audiofiles = sort(@audiofiles);
    push @filelist, $moviefile  if $moviefile;
    push @filelist, $ac3file    if $ac3file;
    push @filelist, sort(@audiofiles) if @audiofiles;
    return \@filelist;
}

sub analyse_ac3_stream {
    my $self = shift;
    my $payload = shift;
    my %pes_header = %{shift @_};
    $self->{frame_regex} = $AC3_regex;    
    ($payload, my %frame_info) = analyse_ac3_frame($self, $payload);
    if ($payload eq "-1") {
        print "AC3 Packet could not be analysed, skipping\n";
        return;
    }
    
    $self->{packet_start} = substr($payload,0,2);
    
    
    $self->{freq} = $frame_info{freq};
    
    $self->{frame_bytes} = $frame_info{frame_bytes};
    $self->{bitrate}     = $frame_info{bitrate};
    $self->{mode}         = $frame_info{mode};
    
    ##################################################################
    ############ ONE FRAME is 32 ms
    ##################################################################
    
    $self->{frame_ticks} =  $frame_info{frame_ticks};
          
    if (! $self->{info}) {
        if ($self->{ac3_filter}) {
            #$self->{outfilename} = "$self->{outputpath}/$self->{basename}.ac3_audio";
            my $filterpipe = "  $self->{ac3_filter} ";
            vprint("setting outfilename to $self->{outfilename}\n");
            #print"setting pipe to $filter_pipe\n";
            #$filterpipe .= $self->{outfilename};
            vprint("Want to open $filterpipe\n");

            #sleep 1;
            open $self->{outfile}, "|-", " $filterpipe"  or die "Can not open file for ac3: $!\n";
        }
        else {
            $self->{outfilename} = "$self->{outputpath}/$self->{basename}.ac3";
            vprint("\nWant to open $self->{outfilename}\n");
            #sleep 1;
            open $self->{outfile}, ">$self->{outfilename}"  or die "Can not open file for ac3: $!\n";
        }
       
        binmode $self->{outfile};
    }
        $self->{check_frames} = \&check_audio_frames;
        $self->{analyse_frames} = \&analyse_ac3_frame;
        $self->{frame_regex} = $AC3_regex;
    
}

sub analyse_ac3_frame {
    my $self = shift;
    my $payload = shift;
    my $bits = unpack("B64", substr($payload, 0, 8));
    my %frame_info;
    if (!(substr($payload, 0, 2) =~ /^$AC3_regex$/)) {
    #if (substr($bits,0,16) ne "0000101101110111") {
        print "No Audio syncword found for stream $self->{streamcode}, searching for audio sync\n" if $self->{debug};
        print substr($bits,0,16) . " found, 0000101101110111 expected\n" if $self->{debug};
        if ($payload =~ /$AC3_regex/g) {
            print "\n AC3 regex matched at " . pos($payload) . "\n" if $self->{debug};
            $payload = substr($payload, (pos($payload)-2));
        }
        else {
            print "No audio frame found in this paket\n";
            return -1;
        }
        $bits = unpack("B64", substr($payload, 0, 8));
    }
    
    my $fscod = substr($bits,32,2);
    
    if ($fscod == "00")    {$frame_info{freq} = 48000}
    elsif ($fscod == "01") {$frame_info{freq} = 44100}
    elsif ($fscod == "11") {$frame_info{freq} = 32000}
    elsif ($fscod == "10") {print "Illeagal AC3 freq\n"; return -1}
        
    my $frmsizecod = substr($bits,34,6);
    $frame_info{frame_bytes} = $AC3_frame_info{$frame_info{freq}}{$frmsizecod};
    $frame_info{bitrate} = $AC3_frame_info{bitrate}{$frmsizecod};
    if (! $frame_info{bitrate}) {
        print "Broken Audio packet in stream $self->{streamtype} (paket $self->{frameno})\n";
        return -1;
    }
    
    my $acmod = substr($bits, 48, 3);
    if    ($acmod == "000") {$frame_info{mode} = "1+1"}
    elsif ($acmod == "001") {$frame_info{mode} = "1/0"}
    elsif ($acmod == "010") {$frame_info{mode} = "2/0"}
    elsif ($acmod == "011") {$frame_info{mode} = "3/0"}
    elsif ($acmod == "100") {$frame_info{mode} = "2/1"}
    elsif ($acmod == "101") {$frame_info{mode} = "3/1"}
    elsif ($acmod == "110") {$frame_info{mode} = "2/2"}
    elsif ($acmod == "111") {$frame_info{mode} = "3/2"}
    
    ##################################################################
    ############ ONE FRAME is 32 ms
    ##################################################################
    if ($fscod ne "00") {print "not an 48 KHz stream, exiting\n"; return -1}
    $frame_info{frame_ticks} =  8 * $frame_info{frame_bytes} * 90000 / $frame_info{bitrate};
    $frame_info{streamtype} = "AC3_Audio";
    if ($self->{Unit_analysis_counter}++ > 20) {
        $self->{Unit_analysis_hash}{mode}{$frame_info{mode}}++;
    }

    
    return $payload, %frame_info;
}

sub check_audio_frames    {
    my $self = shift;
    my $payload = shift;
    my %pes_header = %{shift @_};
    my $counter = 0;
    my $FH = $self->{outfile};
    
    
    if ($self->{change_message}) {return}
    
    if ($self->{dump_payload}) {
        print "Dumping payload of stream $self->{streamcode}, ($self->{dump_payload} to dump left)\n" if $self->{debug};
        my $DUMPFH;
        if (! $self->{"$self->{streamcode}.dump"}) {
            print "Trying to open dumpfile\n";
            open $DUMPFH, ">$self->{streamcode}.dump"  or die "Can not open dumpfile: $!\n";
            binmode $DUMPFH;
            $self->{"$self->{streamcode}.dump"} = $DUMPFH;
            print "opened dumpfile\n";
        }
        $DUMPFH = $self->{"$self->{streamcode}.dump"};
        print $DUMPFH $payload;
        if ($self->{dump_payload}-- == 1) {
            exit;
        }
        return;
    }
    
    $payload = $self->{packet_rest} . $payload;
    
    ($payload, my %frame_info) = &{$self->{analyse_frames}}($self, $payload) ;
    if  (!$frame_info{frame_ticks}) {
        print "Analysis failed in audio stream $self->{streamcode}, returning from check_audio_frames\n" if $self->{debug};
        return;
    }
    if ((!($self->{frame_bytes} eq $frame_info{frame_bytes}) || (!($self->{bitrate} eq $frame_info{bitrate}))) && ($self->{streamcode} ne "bd")) {
        if ($self->{audio_only}){
            $self->{copyright} = $frame_info{copyright};
            $self->{bitrate} = $frame_info{bitrate};
            $self->{freq} = $frame_info{freq};
            $self->{padding_bit} = $frame_info{padding_bit};
            $self->{streamtype} = $frame_info{streamtype};
            $self->{frame_bytes} = $frame_info{frame_bytes}; 
            $self->{frame_ticks} = $frame_info{frame_ticks}; 
            $self->{mode}        = $frame_info{mode};
            print "\nAudio Format Changed:\n";
            print "\naudio stream $self->{streamcode} info ($self->{streamtype}):\n";
            print "Sample frequency:    $self->{freq}\n";            # only for Audio 
            print "Bitrate:             $self->{bitrate}\n";             # only for Audio
            print "Mode:                $self->{mode}\n";             # only for Audio
            print "Copyright:           $self->{copyright}\n" if  $self->{copyright};        # only for Audio
            print "Frame length (bytes) $self->{frame_bytes}\n";        # only for Audio
            print "Frame length (ticks) $self->{frame_ticks} (90000 / sec)\n\n\n";        # one tick is 1 / 90000 of a second
        }
        
        
        elsif (!$self->{frames_written}) {    
            @{$self->{frame_times}}  = ();
            $self->{frame_times}[0] = 0;
            @{$self->{frame_buffer}} = ();
            @{$self->{frame_length}} = ();
            $self->{frameno} = 0;
            $self->{copyright} = $frame_info{copyright};
            $self->{bitrate} = $frame_info{bitrate};
            $self->{freq} = $frame_info{freq};
            $self->{padding_bit} = $frame_info{padding_bit};
            $self->{streamtype} = $frame_info{streamtype};
            $self->{frame_bytes} = $frame_info{frame_bytes}; 
            $self->{frame_ticks} = $frame_info{frame_ticks}; 
            $self->{mode}        = $frame_info{mode};
            
            $self->{single_frame} = get_silent_frame($self, \%frame_info);
            if ($self->{single_frame}  eq "-1") {
                $self->{single_frame} = substr($payload, 0, $frame_info{frame_bytes});
                print "Defined first audio frame as single frame for later sync in stream $self->{streamcode}\n" if $self->{debug};
                print "Frame has a length of " . length($self->{single_frame}) . "\n" if $self->{debug};
            }
                    
            print "\nAUDIO FORMAT CHANGED, new parameters:\n";
            print "\naudio stream $self->{streamcode} info ($self->{streamtype}):\n";
            print "Sample frequency:    $self->{freq}\n";            # only for Audio 
            print "Bitrate:             $self->{bitrate}\n";             # only for Audio
            print "Mode:                $self->{mode}\n";             # only for Audio
            print "Copyright:           $self->{copyright}\n" if  $self->{copyright};        # only for Audio
            print "Frame length (bytes) $self->{frame_bytes}\n";        # only for Audio
            print "Frame length (ticks) $self->{frame_ticks} (90000 / sec)\n\n\n";
        }
        elsif ($total_size - $bytes_read < 10000000) {
                if (! $self->{change_message}) {
                    print "\nAudio Format changed " . int(($total_size - $bytes_read)/1000000) ." MBytes before end, continuing...\n";
                    $self->{change_message} = 1;
                }
                return;
        }
        else {
            print "Change in Audio Mode from $self->{mode} to $frame_info{mode}, this will not work during remux, stopping!\n";
            print "Change in Audio bitrate from $self->{bitrate} to $frame_info{bitrate}, this will not work during remux, stopping!\n";
            print "You should cut the movie at this position and process the pieces independently\n";
            my $frame_number = $self->{frames_written} + scalar(@{$self->{frame_buffer}});
            my $seconds = $frame_number * $self->{frame_ticks} / 90000; #/
            print "Current position: $seconds sec.\n";
            $kill_me = $self->{streamcode};
            return;
        }
    }
    
    if ((! $self->{frame_times}[0]) && ( $pes_header{PTS_decimal}) && ($self->{frameno} > 0)){
        print "\nFirst audio timestamp at $self->{frameno} for stream $self->{streamcode}\n" if $self->{debug};
        print "First Audio Timestamp is $pes_header{PTS_decimal}\n" if $self->{debug};
        for (my $i = 1;  $i <= $self->{frameno}; $i++) {
            $self->{frame_times}[$self->{frameno} - $i] = $pes_header{PTS_decimal} - ($i * $self->{frame_ticks});
        }
    print "\nAudio now starting at $self->{frame_times}[0]\n" if $self->{debug};
    }
        
    my $first_timestamp = 0;
    
    if ($pes_header{PTS_decimal}) {
        $first_timestamp = $pes_header{PTS_decimal};
    }
    else {
        $first_timestamp = ${$self->{frame_times}}[-1] + $frame_info{frame_ticks} if (${$self->{frame_times}}[-1]);
    }
    
    if (!($self->{single_frame})) {
        
        $self->{single_frame} = get_silent_frame($self, \%frame_info);
        if ($self->{single_frame}  eq "-1") {
            $self->{single_frame} = substr($payload, 0, $frame_info{frame_bytes});
            print "\nDefined first audio frame as single frame for later sync in stream $self->{streamcode}\n" if $self->{debug};
            print "Frame has a length of " . length($self->{single_frame}) . "\n" if $self->{debug};
        }
    }
    
    my $packet_offset = 0;
    while ($packet_offset + $frame_info{frame_bytes} +2 <= (length($payload))) {
        
        if ((substr($payload, $packet_offset, 2) =~ /^$self->{frame_regex}$/)) {
            push @{$self->{frame_buffer}}, substr($payload, $packet_offset, $frame_info{frame_bytes});
            
            push @{$self->{frame_times}}, $first_timestamp + $counter * $frame_info{frame_ticks};
            push @{$self->{frame_length}}, $frame_info{frame_ticks};
            $packet_offset += $frame_info{frame_bytes};
            $counter++;
            $self->{frameno}++;
        }
        else {
            print "\n$self->{frame_regex} did not match against " . (substr($payload,$packet_offset,2)) . "\n" if $self->{debug};
            if ($counter == 0) {
                $payload = search_audio_sync($self, substr($payload, $packet_offset));
                $self->{packet_rest} = "";
                $packet_offset = 0;
            }
            else {
                print "\nAudio Format change within packet, skipping \n" if $self->{debug};
                $self->{packet_rest} ="";
                return;
            }
        }
    }
    
    if ($packet_offset < (length($payload)))    {
        $self->{packet_rest} = substr($payload, $packet_offset);
    }
    else { 
        $self->{packet_rest} = "";
    }
    if (scalar(@{$self->{frame_times}}) < 200)    {
        return;
    }
    new_flush($self);
    return;
}

sub new_flush {
    my $self = shift;
    my $FH = $self->{outfile};
    my $flush_buffer;
    my $oldfh = select $FH; $| = 1; select $oldfh;
    
    if ($self->{audio_only}) {
        while (scalar(@{$self->{frame_times}}) > 50) {
            $self->{last_frame_time} =  shift @{$self->{frame_times}};
            shift @{$self->{frame_length}};
            print $FH shift @{$self->{frame_buffer}};
            $self->{frames_written} ++;
        }
        return;
    }

    my ($chunk_start, $chunk_end, $video_frameno) = split /::/,$cutlist[$self->{cutcounter}];
    if ($self->{first_flush}){
        sync_audio_start($self, $chunk_start);
        $self->{first_flush} = 0;
    }
    
    #
    # Here the new time drift correction is performed. Note that this is performed at the the beginning of an Audio flush, NOT when it occurs
    # This should be ok, since a) The shift builds up very slowly and b) a buffer flush is only 120 Frames  long
    #
    if (abs($self->{time_drift}) > ($self->{frame_ticks} * 1.5)) {
        if (abs($self->{time_drift}) > 900000) {
            print "Timedrift bigger than 10 Seconds, killing stream $self->{streamcode}\n";
            $kill_me = $self->{streamcode};
            return;
        }
        print "\nTimedrift is too big, trying to correct $self->{time_drift} ticks\n";
        #sleep 1;
        if ($self->{time_drift} < 0) {
            while ($self->{time_drift} < ($self->{frame_ticks} * -1.5)) {
                shift @{$self->{frame_buffer}};
                shift @{$self->{frame_times}};
                $self->{time_drift} += shift @{$self->{frame_length}};
                print "Dropped frame to correct time drift EXPERIMENTAL!!!\n";
                if (@{$self->{frame_buffer}} == 0) {
                    print "could not drop enough Audio frames in stream $self->{streamcode}to compensate for timedrift, still $self->{time_drift} left\nContinuing anyway...\n";
        #            sleep 1;
                    if ($self->{dump_buffer}) {$kill_me = "all"}
                    return;
                }
            }
        }
        else {        
            my $ins_frames = insert_frames($self, $self->{time_drift});
            my $ins_time = $ins_frames * $self->{frame_ticks};
            print "Inserted $ins_frames Audio Frames ($ins_time ticks) to correct a drift of $self->{time_drift}. EXPERIMENTAL\n";
            $self->{time_drift} -= $ins_time;
            $self->{frames_written} += $ins_frames;
        }
    }
    
    #
    #If the currenct chunk's end has not been determined yet, then we just flush 60 frames and calculate the timedrift
    #
    
    if (! $chunk_end) {
        #my $frame;
        #my $time;
        #print "No Chunk end, just flushing\n";
        my $counter = 0;
        my $bufferstart = ${$self->{frame_times}}[0];
        my $drift;
        my $calc_end = $bufferstart;
        while (scalar(@{$self->{frame_times}}) > 100) {
            $self->{last_frame_time} =  shift @{$self->{frame_times}};
            $calc_end += shift @{$self->{frame_length}};
            $flush_buffer .= shift @{$self->{frame_buffer}};
            #print $FH shift @{$self->{frame_buffer}};
            $counter++;
            $self->{frames_written}++;
        }
        #print $FH $flush_buffer;
        if ($self->{use_pipe}) {
            #print "Should write " . length($flush_buffer) . " bytes\n";
            my $length = pack("V", (length($flush_buffer)));
            print $FH $length       if $flush_buffer;
            print $FH $flush_buffer if $flush_buffer;
        } else {
            print $FH $flush_buffer if $flush_buffer;
        }
        $drift = $self->{last_frame_time} - $calc_end  + $self->{frame_ticks};
        
        #This should not be necessary, since a PTS Overflow will be treated as a cut...
        #
        #if ($drift < -8000000000) {
        #    print "Time drift indicates a PTS overflow, correcting\n";
        #    $drift += 8589934592;
        #    print "Now time drift will be " . ($self->{time_drift} + $drift) .  "\n";
        #}
        $self->{time_drift} += $drift;
        if ($self->{show_drift} && (abs($drift) > 0)) {
            print "\nAUDIO TIMEDRIFT for stream $self->{streamcode}: $self->{time_drift}\n" if (abs($self->{time_drift}) > 100);
        }
        return;
    }
    
    #
    #If the currenct chunk's end has been determined, but is at the end of the buffer, only a few Frames are flushed to "move" the cut to the middle of the buffer
    #Thereby we ensure that enough Audioframes for syncing are in the buffer before AND after the cut
    #
    my $cut_diff = $chunk_end - $self->{last_frame_time};
    if (130 * ${$self->{frame_length}}[0] + $self->{last_frame_time}  < $chunk_end) {
        
        print "delaying sync to next run, chunk_end is $chunk_end\n" if $self->{debug};
        #my $calc = 120 * ${$self->{frame_length}}[0] + $self->{last_frame_time};
        while (scalar(@{$self->{frame_times}}) > 100) {
            $self->{last_frame_time} =  shift @{$self->{frame_times}};
            shift @{$self->{frame_length}};
            $flush_buffer .= shift @{$self->{frame_buffer}};
            #print $FH shift @{$self->{frame_buffer}};
            $self->{frames_written}++;
        }
        #print $FH $flush_buffer;
        if ($self->{use_pipe}) {
            my $length = pack("V", (length($flush_buffer)));
            print $FH $length       if $flush_buffer;
            print $FH $flush_buffer if $flush_buffer;
        } else {
            print $FH $flush_buffer if $flush_buffer;
        }
        return;
    }
    
    # Then we test wether the cut is no cut but a PTS overflow .... If yes, we just dump the buffer, increase the cutcount and continue. 
    # No Timedrift is computed for this buffer
    if ($chunk_end > 8589889595) {
        print "syncing at PTS Overflow, special care is taken!\n";
        while (scalar(@{$self->{frame_times}}) > 60) {
            $self->{last_frame_time} =  shift @{$self->{frame_times}};
            shift @{$self->{frame_length}};
            $flush_buffer .= shift @{$self->{frame_buffer}};
            #print $FH shift @{$self->{frame_buffer}};
            $self->{frames_written}++;
        }
        $self->{cutcounter}++;
        #print $FH $flush_buffer;
        if ($self->{use_pipe}) {
            my $length = pack("V", (length($flush_buffer)));
            print $FH $length       if $flush_buffer;
            print $FH $flush_buffer if $flush_buffer;
        } else {
            print $FH $flush_buffer if $flush_buffer;
        }
        return;
    }
    
    
    #
    # Finally we sync the end of the Audio chunk
    #
    
    
    sync_audio_end($self, $chunk_end, $video_frameno);
    $self->{cutcounter}++;
    # Extract the beginning of the next chunk
    ($chunk_start, $chunk_end) = split /::/,$cutlist[$self->{cutcounter}];
    if (! $chunk_start) {
        print "Could not extract chunk start from $cutlist[$self->{cutcounter}]\n";
        $kill_me = $self->{streamcode};
        return -1;
    }
    # And sync the start of the next chunk
    sync_audio_start($self, $chunk_start);
}

sub sync_audio_end {
    my $self = shift;
    my $chunk_end = shift;
    my $video_frameno = shift;
    
    my $FH = $self->{outfile};
    my $counter = 0;
    my $cut_frame = 0;
    my $flush_buffer = "";
    
    for (my $i = 0; $i < (scalar(@{$self->{frame_times}}) - 1); $i++)  {
        if (abs(${$self->{frame_times}}[$i + 1] - ${$self->{frame_times}}[$i]) > 90000) {
            $cut_frame = $i;
            last;
        } 
    }
    if (!$cut_frame) {
        $kill_me = $self->{streamcode}; 
        print "No cut found within this buffer, but there should be one, killing \n";
        print "first ts: ${$self->{frame_times}}[0]\n";
        print "last ts: ${$self->{frame_times}}[-1]\n";
        print "number of frames in buffer: " . (scalar(@{$self->{frame_times}})) . "\n";
        return -1;
    }
    
    print "\nsyncing end of chunk in stream $self->{streamcode}...\n" if $self->{debug};
    my $bufferstart = ${$self->{frame_times}}[0];
    my $calc_end = $bufferstart;
    while ((${$self->{frame_times}}[0] < $chunk_end - $self->{final_desync} + ($self->{frame_ticks} * 0.5))
          && ($counter < $cut_frame)){
        
        $self->{last_frame_time} = shift @{$self->{frame_times}};
        $calc_end += shift @{$self->{frame_length}};
        $flush_buffer .= shift @{$self->{frame_buffer}};
        #print $FH shift @{$self->{frame_buffer}};
        $self->{frames_written}++;
        $counter++;
        if (scalar(@{$self->{frame_times}}) == 0) {
            print "syncing failed for stream $self->{streamcode} while trying to write Frames until $chunk_end\n";
            print "time of last frame written was $self->{last_frame_time}\n";
            $kill_me = $self->{streamcode};
            return -1;
        }
    }
    #print $FH $flush_buffer;
    if ($self->{use_pipe}) {
        my $length = pack("V", (length($flush_buffer)));
        print $FH $length       if $flush_buffer;
        print $FH $flush_buffer if $flush_buffer;
    } else {
        print $FH $flush_buffer if $flush_buffer;
    }
    print "written $self->{frames_written} frames, time $self->{last_frame_time}\n"if $self->{debug};
    #
    # We dont need a timedrift calc at the end of a cunk, it is not corrected anyway...At the end the real length correction is performed
    #
    #my $drift = $self->{last_frame_time} - $calc_end  + ${$self->{frame_length}}[0];
    #if ($drift < -8000000000) {
    #        print "Time drift indicates a PTS overflow, correcting\n";
    #        $drift += 8589934592;
    #        print "Now time drift will be " . ($self->{time_drift} + $drift) .  "\n";
    #}
    #$self->{time_drift} += $drift;
    #if ($self->{show_drift} && (abs($drift) > 0)) {
    #    print "\nAUDIO TIMEDRIFT for stream $self->{streamcode}: $self->{time_drift}\n" if (abs($self->{time_drift}) > 100);
    #}
    #my $diff = $chunk_end - ($self->{last_frame_time} + $self->{frame_ticks});
    my $vid_time = $video_frameno * 3600;
    my $aud_time = $self->{frames_written} * $self->{frame_ticks};
    my $frame_diff = $vid_time - $aud_time;
    print "For the end sync $self->{cutcounter} of $self->{streamcode} frames were printed up to $self->{last_frame_time} to match $chunk_end of chunk $self->{cutcounter} leaving: $frame_diff)\n"if $self->{debug};
    my $ins_frames = insert_frames($self, $frame_diff);
    $self->{frames_written} += $ins_frames;
    $aud_time = $self->{frames_written} * $self->{frame_ticks};
    $self->{final_desync} = $vid_time - $aud_time;
    $self->{time_drift} = 0;
    $self->{written_chunks}[$self->{cutcounter}] .= ($self->{last_frame_time} + $self->{frame_ticks} * ($ins_frames+1));
}

sub sync_audio_start {
    my $self = shift;
    my $chunk_start = shift;
    
    if ($self->{audio_only}) {
        return;
    }
    print "\nFirst packet ts for stream $self->{streamcode} in buffer $self->{frame_times}[0] last:$self->{frame_times}[-1]\n" if $self->{debug};
    print "\nIn stream $self->{streamcode}: new start $chunk_start\n" if $self->{debug};
    
    my $counter = 0;
    while ((${$self->{frame_times}}[0] < $chunk_start - $self->{frame_ticks} * 0.5)
       || ( (${$self->{frame_times}}[0] - $chunk_start) > 90000 )) { # For PTS overflows and "catted" movies
        #print "\ndropping frame with time ${$self->{frame_times}}[$counter]\n" if $self->{debug};
        shift @{$self->{frame_times}};
        shift @{$self->{frame_buffer}};
        shift @{$self->{frame_length}};
        $counter++;
        if (scalar(@{$self->{frame_times}}) == 0) {
            print "\nsyncing failed for stream $self->{streamcode} while trying to drop Frames until $chunk_start\n";
            #print "time of last frame dropped was $time_drop\n";
            $kill_me = $self->{streamcode};
            return -1;
        }
    }
    my $diff = $chunk_start - ${$self->{frame_times}}[0];
    my $ins_frames = insert_frames($self, $diff);
    print "IN SYNC AUDIO START ($self->{streamcode}): diff is now: $diff\n" if $self->{debug};
    $self->{frames_written} += $ins_frames;
    $self->{last_frame_time} = ${$self->{frame_times}}[0];
    $self->{written_chunks}[$self->{cutcounter}] = (${$self->{frame_times}}[0] - $self->{frame_ticks} * $ins_frames) . "::";
    print "\nCHUNKSTART: dropped $counter frames at the beginning of $self->{streamcode}, start is now ${$self->{frame_times}}[0] matching $chunk_start leaving $diff\n" if $self->{debug};
}



sub insert_frames {
    my $self = shift;
    my $diff = shift;
    my $flush_buffer;
    
    $diff += $self->{final_desync};
    my $FH = $self->{outfile};
    my $abs_diff = abs($diff);
    
    my $counter = 0;
    if ($abs_diff > 900000) {
        print "more than 10 seconds auf Audio missing, killing stream $self->{streamcode}\n";
        $kill_me = $self->{streamcode}; 
        return -1;
    }
    while ($abs_diff > ($self->{frame_ticks} * 0.5 )) {
        $flush_buffer .= $self->{single_frame};
        #print $FH $self->{single_frame};
        $abs_diff -= $self->{frame_ticks};
        $counter++;
    }
    if ($self->{use_pipe} && $flush_buffer) {
        my $length = pack("V", (length($flush_buffer)));
        print $FH $length;
        print $FH $flush_buffer;
    } else {
        print $FH $flush_buffer if $flush_buffer;
    }
    if ($diff < 0) {$abs_diff *= -1}
    print "INSERT FRAMES:wrote additional $counter frames to close $diff ticks, leaving $abs_diff\n" if $self->{debug};
    $self->{final_desync} = $abs_diff;
    #print "Final desync is now $self->{final_desync}\n";
    return $counter;
}

sub analyse_video_stream {
    my $self = shift;
    my $payload = shift;
    my %pes_header = %{shift @_};
    
    $self->{packet_start} = pack("H8", "00000100");
    my $seqstart = pack("H8", "000001b3");
    if (!($payload =~ /$seqstart(.{8})/s)) {
        print "No SeqHeader in the first Frame, exiting\n"; 
        $kill_me = "all";
        return -1;
        }
    
    # Here we used to kill the stream if no PTS is found, that changed... We kill the stream if we find a GOP withou ANY ts
    $self->{check_frames} = \&check_GOPs;
    
    my $bitview = unpack("B64", $1);
    print "Matched: $& at $-[0] bitview of the header\n$bitview \n" if $self->{debug};
    my $hor_size = "0b" . substr($bitview,0,12);
    $self->{horizontal_size} = oct $hor_size; 
    my $ver_size = "0b" . substr($bitview,12,12);
    $self->{vertical_size} = oct $ver_size;
    my $asp_ratio = substr($bitview,24,4);

    if ($asp_ratio eq "0001"){
        $self->{aspect_ratio} = "1:1";
    }
    elsif ($asp_ratio eq "0010") {
        $self->{aspect_ratio} = "4:3";
    }
    elsif ($asp_ratio eq "0011") {
        $self->{aspect_ratio} = "16:9";
    }
    elsif ($asp_ratio eq "0111") {
        $self->{aspect_ratio} = "2.21:1";
    }
    else {
        $self->{aspect_ratio} = "$asp_ratio";
    }
    
    my $fps = substr($bitview,28,4);
    if ($fps eq "0001")    {
        $self->{fps} = 23.967;
    }
    elsif ($fps eq "0010")    {
        $self->{fps} = 24;
    }
    elsif ($fps eq "0011")    {
        $self->{fps} = 25;
    }
    elsif ($fps eq "0100")    {
        $self->{fps} = 29.97;
    }
    elsif ($fps eq "0101")    {
        $self->{fps} = 30.97;
    }
     
    $self->{bitrate_value} = 400 * ( oct ("0b" . substr($bitview,32, 18)));
    my $marker_bit = substr($bitview,50, 1);
    print "we got for fps: $fps\n" if $self->{debug};
    $self->{frame_ticks} =  90000 / $self->{fps};
    
    
        
    if (! $self->{info}) {
        if ($self->{video_filter}) {
            #$self->{outfilename} .= "$self->{outputpath}/$self->{basename}.video";
            my $filterpipe = "  $self->{video_filter} ";
            #vprint("\nsetting outfilename to $self->{outfilename}\n");
            $filterpipe .= $self->{outfilename};
            vprint("\nWant to open $filterpipe\n");

            #sleep 1;
            #open $self->{outfile}, "|-", " $filterpipe "  or die "Can not open file for Video: $!\n";
            open $self->{outfile}, "|  $filterpipe "  or die "Can not open file for Video: $!\n";
            
            sleep 3;
        }
        else {
           
            $self->{outfilename} .= "$self->{outputpath}/$self->{basename}.mpv";
            vprint("\nWant to open $self->{outfilename}\n");
            #sleep 1;
            open $self->{outfile}, ">$self->{outfilename}"  or die "Can not open file for mpv: $!\n";
        }
        binmode $self->{outfile};
        #$self->{outfilename} = "$self->{outputpath}/$self->{streamcode}.mpv";
        #open $self->{outfile}, ">$self->{outfilename}"  or die "Can not open file for m2v: $!\n";
    }
    
}


sub analyse_GOP {
    my $self = shift;

    
    my %GOP_info;
    my $seqstart = pack("H8", "000001b3");
    if (!($self->{GOP_buffer} =~ /$seqstart(.{8})/s)) {
    
        if (! $self->{GOPno}) {
            print "\nNo Seq Header in the GOP, exiting\n"; 
            $kill_me = "all";
            return -1;
        }
        else {
            print "\nA GOP without Sequence Header. Weird recording\n";
            open DFH, ">./strange_gop.mpg";
            print DFH $self->{GOP_buffer};
            close DFH;
            return;
        }
    }
    
    my $bitview = unpack("B64", $1);
    my $hor_size = "0b" . substr($bitview,0,12);
    $GOP_info{horizontal_size} = oct $hor_size; 
    $self->{Unit_analysis_hash}{horizontal_size}{$GOP_info{horizontal_size}}++;
    my $ver_size = "0b" . substr($bitview,12,12);
    $GOP_info{vertical_size} = oct $ver_size;
    $self->{Unit_analysis_hash}{vertical_size}{$GOP_info{vertical_size}}++;
    my $asp_ratio = substr($bitview,24,4);

    if ($asp_ratio eq "0001"){
        $GOP_info{aspect_ratio} = "1:1";
    }
    elsif ($asp_ratio eq "0010") {
        $GOP_info{aspect_ratio} = "4:3";
    }
    elsif ($asp_ratio eq "0011") {
        $GOP_info{aspect_ratio} = "16:9";
    }
    elsif ($asp_ratio eq "0111") {
        $GOP_info{aspect_ratio} = "2.21:1";
    }
    else {
        $GOP_info{aspect_ratio} = "$asp_ratio";
    }
    
    
    my $fps = substr($bitview,28,4);
    if ($fps eq "0001")    {
        $GOP_info{fps} = 23.967;
    }
    elsif ($fps eq "0010")    {
        $GOP_info{fps} = 24;
    }
    elsif ($fps eq "0011")    {
        $GOP_info{fps} = 25;
    }
    elsif ($fps eq "0100")    {
        $GOP_info{fps} = 29.97;
    }
    elsif ($fps eq "0101")    {
        $GOP_info{fps} = 30.97;
    }
    
    
    $GOP_info{bitrate_value} = 400 * ( oct ("0b" . substr($bitview,32, 18)));
    $self->{Unit_analysis_hash}{bitrate_value}{$GOP_info{bitrate_value}}++;
    $self->{Unit_analysis_hash}{aspect_ratio}{$GOP_info{aspect_ratio}}++;
    $self->{Unit_analysis_hash}{fps}{$GOP_info{fps}}++;

}


sub check_GOPs {
    my $self = shift;
    my $payload = shift;
    my %pes_header = %{shift @_};
    my $FH = $self->{outfile};
    my $gopstart = pack("H8", "000001b8");
    
    if ($self->{dump_payload}) {
        print "Dumping payload of stream $self->{streamcode}, ($self->{dump_payload} to dump left)\n" if $self->{debug};
        my $DUMPFH;
        if (! $self->{"$self->{streamcode}.dump"}) {
            print "Trying to open dumpfile\n";
            open $DUMPFH, ">$self->{streamcode}.dump"  or die "Can not open dumpfile: $!\n";
            binmode $DUMPFH;
            $self->{"$self->{streamcode}.dump"} = $DUMPFH;
            print "opened dumpfile\n";
        }
        $DUMPFH = $self->{"$self->{streamcode}.dump"};
        print $DUMPFH $payload;
        if ($self->{dump_payload}-- == 1) {
            exit;
        }
        return;
    }
    
      
    # First we just grep for the GOP start sequence
    #if ($pes_header{PTS_decimal}) { print "Got timestamp: $pes_header{PTS_decimal}\n"; }
    if ((! $self->{GOPno})  && (! $self->{GOP_buffer} )) {
        if ($pes_header{PTS_decimal}) {
            $self->{first_GOP_ts} = $pes_header{PTS_decimal};
        }
        #$cutlist[$self->{cutcounter}] = "$pes_header{PTS_decimal}";
        #push @{$self->{timestamps}}, $pes_header{PTS_decimal};
        my $seqstart = pack("H8", "000001b3");
        $payload =~ /$seqstart/g;
        if (pos($payload)-4 != 0) {
            print "The very first GOP did not start at 0, but at " . (pos($payload)-4) ."\n" if $self->{debug};
            $payload = substr($payload, (pos($payload)-4));
        }
        else {
            print "Yes, it did start at 0\n" if $self->{debug};
        }
        $self->{GOP_buffer} .= $payload;
        return;
    }
    
    if (! ($payload =~ /$gopstart/gm) ){
        $self->{GOP_buffer} .= $payload;
        #print "no GOPstart found, returning\n";
        if ((!$self->{first_GOP_ts}) && $pes_header{PTS_decimal}) {
            $self->{first_GOP_ts} = $pes_header{PTS_decimal};
        }
        return;
    }
        
    
    ##################
    # We have a full GOP in the buffer...
    ##################
    
    if (!$self->{first_GOP_ts}) {die "There was not a single timestamp in this GOP, giving up\n";}
    
    if (! $self->{GOPno}) {
        $cutlist[$self->{cutcounter}] = "$self->{first_GOP_ts}";
    }
    
    push @{$self->{timestamps}}, $self->{first_GOP_ts};
    
    if ($pes_header{PTS_decimal}) {
        $self->{first_GOP_ts} = $pes_header{PTS_decimal};
    } else {
        $self->{first_GOP_ts} = 0;
    }
        
    $self->{GOPno}++;
    
    my @pics = $self->{GOP_buffer} =~ /$self->{packet_start}/gm;
    my $frames_in_GOP = scalar(@pics);
    

    $self->{frameno} += $frames_in_GOP;
    
    if ($self->{Unit_analysis_counter}++ > 9) {
        $self->{Unit_analysis_counter} = 1;
        analyse_GOP($self);
    }
    
    if (scalar(@{$self->{timestamps}}) > 1) {
        my $pts_diff = ${$self->{timestamps}}[-1] - ${$self->{timestamps}}[-2];
        my $calc = $self->{frame_ticks} * $frames_in_GOP;
        #print "Timestamp diff is $pts_diff and frame time is $calc\n";
    
        if ($calc != $pts_diff)    {
            my $ts_shift = $pts_diff - $calc;
            #print "TS not as expected, diff is $ts_shift\n";
            if (abs($ts_shift) > 90000) {
                my $chunk_end = ${$self->{timestamps}}[-2] + $frames_in_GOP * $self->{frame_ticks}; 
                if ($ts_shift > 0) {
                    print "\nCut detected in Video at $chunk_end\n";
                }
                else { 
                    print "\nCut detected at $chunk_end, possibly a PTS Overflow\n";
                }
                $cutlist[$self->{cutcounter}] .= "::$chunk_end" . "::$self->{frameno}";
                print "Chunk entry is $cutlist[$self->{cutcounter}]\n"if $self->{debug};
                $self->{cutcounter}++;
                $cutlist[$self->{cutcounter}] = "$pes_header{PTS_decimal}";
                $self->{time_drift} = 0;
            }
            else {
                #print "shift is too small for a cut, storing the shift for later use\n";
                $self->{time_drift} += $ts_shift; 
                if (abs($self->{time_drift}) > 1000) {
                    if (($self->{dump_buffer}) && (abs($self->{time_drift}) > 30000)) {
                        $kill_me = "all";
                        print "\nTimeshift in Video too big ($self->{time_drift}), PLEASE mail the file debug.buffer to peter\@vdr-portal.de\n";
                    }
                    print "\nTime Drift in VIDEO is now $self->{time_drift}, ts: ${$self->{timestamps}}[-2] and ${$self->{timestamps}}[-1] \n" if $self->{show_drift};
                }
            }
        }
    }
    
    if ($self->{use_pipe}) {
        my $oldfh = select $FH; $| = 1; select $oldfh;
        #print "Should write " . length($self->{GOP_buffer}) . " bytes for video\n";
        my $length = pack "V", (length($self->{GOP_buffer}));
        my $hexstr = (unpack ('H8', $length));
        #print "The length is encoded as $length with a length of " . length($length) . " bytes, in reconverted it is $hexstr\n";
        print $FH $length;
        #sleep 2;
      #  die;
        print $FH $self->{GOP_buffer};
    
    } else {
        print $FH $self->{GOP_buffer};
    }
    $self->{GOP_buffer} = $payload;
}


sub search_audio_sync {
        
    my $self = shift;
    my $payload = shift;
    my $new_offset = -1;
    print "Searching for audio sync\n"if $self->{debug};
    while ($payload =~ /$self->{frame_regex}/g)    {
        my $potential_start = (pos($payload)-2);
        print "\tFound a  potential audio syncword at $potential_start: " . unpack("H*" ,substr($payload,$potential_start,3)) . "\n" if $self->{debug};
        (my $dummy_payload, my %frame_info) = analyse_audio_frame($self, substr($payload,$potential_start));
        if (($potential_start + $frame_info{frame_bytes}) < length($payload)) {
            print "\twe should have enough payload left to verify...\n" if $self->{debug};
            #(my $dummy_payload, my %frame_info) = analyse_audio_frame($self, substr($payload,$potential_start));
            
            print "\tchecking " .  unpack("H4",substr($payload,($potential_start + $frame_info{frame_bytes}),2)) ."\n" if $self->{debug};
        
            if ($frame_info{packet_start} eq substr($payload,($potential_start + $frame_info{frame_bytes}),2)) {
                print "\tfound an additional audio sync in the right distance\n\tAll should be fine\n" if $self->{debug};
                $new_offset = $potential_start;
                print "\tNew attempt will be started at $new_offset\n" if $self->{debug};
                $self->{packet_rest} ="";
                last;
            }
            else {
                print "\tNext audio start not found, continuing to check...\n" if $self->{debug};
            }
        }
        else {
            print "\tnot enough payload left to verify\n\tGood Luck\n\tSkipping to next packet\n" if $self->{debug};
            last;
        }
    }
    
    if ($new_offset != -1)    {
        return (substr($payload,$new_offset));
        #        &{$self->{check_frames}} ($self, substr($payload,$new_offset), \%pes_header);
    }
    else {
        print "\tCould not find anything usefull in this packet, contents dropped, maybe next packet?\n" if $self->{debug};
        return (-1);
    }
}

sub print_stats    {
    my $self = shift;
    
    foreach (keys(%{$self->{streams}}))    {
        print "$self->{streams}{$_}{streamtype} for stream $_\n" if $self->{debug};
        if ($self->{ignore_streams}{$_}) {
            print "Ignoring stream $_\n" if $self->{debug};
            next;
        }
        if ($self->{info}) {    
            if (($self->{streams}{$_}{streamtype} =~ /audio/) || ($self->{streams}{$_}{streamtype} =~ /Layer/))    {
                print "\naudio stream $_ info ($self->{streams}{$_}{streamtype}):\n";
                print "Sample frequency:    $self->{streams}{$_}->{freq}\n";            # only for Audio 
                print "Bitrate:             $self->{streams}{$_}->{bitrate}\n";             # only for Audio
                print "Mode:                $self->{streams}{$_}->{mode}\n";             # only for Audio
                print "Copyright:           $self->{streams}{$_}->{copyright}\n" if  $self->{streams}{$_}->{copyright};        # only for Audio
                print "Frame length (bytes) $self->{streams}{$_}->{frame_bytes}\n";        # only for Audio
                print "Frame length (ticks) $self->{streams}{$_}->{frame_ticks} (90000 / sec)\n";        # one tick is 1 / 90000 of a second
                print "App. Size (bytes)    $self->{streams}{$_}->{total_bytes}\n\n\n" if $self->{streams}{$_}->{total_bytes} ;
            }
            if ($self->{streams}{$_}{streamtype} eq "video") {
                print "\n\nvideo stream $_ info:\n";
                print "Frame length (ticks) $self->{streams}{$_}->{frame_ticks} (90000 / sec)\n";        # one tick is 1 / 90000 of a second
                print "Aspect ratio         $self->{streams}{$_}->{aspect_ratio}\n";
                print "Horizontal size      $self->{streams}{$_}->{horizontal_size} \n";
                print "Vertical size        $self->{streams}{$_}->{vertical_size} \n";
                print "Frames per Second    $self->{streams}{$_}->{fps} \n";
                print "Bitrate              $self->{streams}{$_}->{bitrate_value}\n";
                print "Bitrate (estm)       $self->{streams}{$_}->{bitrate_estm}\n"    if $self->{streams}{$_}->{bitrate_estm};
                print "App. Size (bytes)    $self->{streams}{$_}->{total_bytes}\n\n\n" if $self->{streams}{$_}->{total_bytes} ;
                
            }
        }
    }
}
    
sub analyse_audio_stream {
    my $self = shift;
    my $payload = shift;
    my %pes_header = %{shift @_};
    
    #$self->{cuts}[0] = "new start: $self->{first_ts} at 0\t"; #{frameno}  : $pes_header{PTS_decimal} : -1 : -1 : $self->{streamcode} : ";
    
    $self->{frame_regex} = $mp2_regex;
    
    ($payload, my %frame_info) = analyse_audio_frame($self, $payload);
    
    if ($payload eq "-1") {
        print "Audio Packet could not be analysed, skipping\n" if $self->{debug};
        return;
    }
    $self->{copyright} = $frame_info{copyright};
    $self->{bitrate} = $frame_info{bitrate};
    $self->{freq} = $frame_info{freq};
    $self->{padding_bit} = $frame_info{padding_bit};
    $self->{streamtype} = $frame_info{streamtype};
    $self->{frame_bytes} = $frame_info{frame_bytes}; 
    $self->{frame_ticks} = $frame_info{frame_ticks}; 
    $self->{mode}        = $frame_info{mode};
    print "streamtype is: $self->{streamtype}\nbitrate is $self->{bitrate}\nfreq is $self->{freq}\ncopyright is $self->{copyright}\nframelength in byte is $self->{frame_bytes}\nin sec: " . ($self->{frame_ticks}/90000) . "\n" if $self->{debug};#/
    
    if ($self->{streamtype} eq "MPEG1_Layer_2")    {
        $self->{check_frames} = \&check_audio_frames;
        $self->{analyse_frames} = \&analyse_audio_frame;
        $self->{frame_regex} = $mp2_regex;
    }
    
    my $streamnumber = substr($self->{streamcode},1,1);
    
    if (! $self->{info}) {    
        if ($self->{audio_filter}) {
            #$self->{outfilename} = "$self->{outputpath}/$self->{basename}$streamnumber.audio";
            if ($self->{audio_filter} =~ /%d/) {
                $self->{audio_filter} =~ s/%d/$streamnumber/;
            } else {
               # $self->{audio_filter} .= $streamnumber;
            }
            my $filterpipe = "  $self->{audio_filter} ";
            vprint("\nsetting outfilename to $self->{outfilename}\n");
            #print "\nsetting outfilename to $self->{outfilename}, pipe is $filterpipe\n";
            #$filterpipe .= $self->{outfilename};
            #vprint("\nWant to open $filterpipe\n");
            #sleep 1;
            #open $self->{outfile}, "|-", " $filterpipe"  or die "Can not open file for mpa: $!\n";
            #print"Want to open $filterpipe\n";
            open $self->{outfile}, "|  $filterpipe"  or die "Can not open file for mpa: $!\n";
        }
        else {
            $self->{outfilename} = "$self->{outputpath}/$self->{basename}$streamnumber.mpa";
            vprint("Want to open $self->{outfilename}\n");
            #sleep 1;
            open $self->{outfile}, ">$self->{outfilename}"  or die "Can not open file for mpa: $!\n";
        }
    
        binmode $self->{outfile};
        #open $self->{outfile}, ">$self->{outfilename}"  or die "Can not open file for mpa: $!\n";
    }
}

sub analyse_audio_frame    {
    my $self = shift;
    my $frame = shift;
    #my %pes_header = %{shift @_};
    
    my %frame_info;    
    my $streamtype = "";
    if (!(substr($frame, 0, 2) =~ /^$mp2_regex$/)) {
        print "No Audio syncword at the beginning of the frame, searching\n" if $self->{debug};
        if ($frame =~ /$mp2_regex/g) {
            $frame = substr($frame, (pos($frame) - 2))
        }
        else {
            print "No Audio Sync found, returning -1\n" if $self->{debug}; 
            return -1;
        }
        print "found sync for mp2 stream $self->{streamcode}\n" if $self->{debug};
    }
    if (length($frame) < 8) {print "Frame too small to analyse, returning for stream $self->{streamcode}\n"; return;}
    my $BITS = unpack("B32", $frame);
    if (substr($BITS,12,1) == 1) {$streamtype = "MPEG1"} else {$streamtype = "MPEG2"}

    if (substr($BITS,13,2) eq "01") {$streamtype .= "_Layer_3"}
    elsif (substr($BITS,13,2) eq "10") {$streamtype .= "_Layer_2"}
    elsif (substr($BITS,13,2) eq "11") {$streamtype .= "_Layer_1"}
    else {print "Streamtype invalid, returning -1\n"; return -1}
    
    $frame_info{copyright} = substr($BITS,15,1);
    if ($bitrates{$streamtype}{substr($BITS,16,4)}) {
        $frame_info{bitrate} = $bitrates{$streamtype}{substr($BITS,16,4)} . "000"
    }
    else {
        print "\nBitrate of the audio stream $self->{streamcode} could not be determined, skipping packet and trying again later\n";
        print "Bitrate bits are:" .  substr($BITS,14,4) . "\n" if $self->{debug};
        return -1;
    }
    
    $frame_info{freq} = $freqs{substr($streamtype,0,5)}{substr($BITS,20,2)};
    if (! $frame_info{freq}) {
        print "\nFrequence of the audio stream $self->{streamcode} could not be determined, skipping packet and trying again later\n";
        print "Freq bits are:" .  substr($BITS,20,2) . "\n" if $self->{debug};
        return -1;
    }
    $frame_info{padding_bit} = substr($BITS,22,1);

    $frame_info{streamtype} = $streamtype;
    
    
    if    (substr($BITS,24,2) eq "00") {$frame_info{mode} = "stereo"}
    elsif (substr($BITS,24,2) eq "01") {$frame_info{mode} = "joint_stereo"}
    elsif (substr($BITS,24,2) eq "10") {$frame_info{mode} = "dual_channel"}
    elsif (substr($BITS,24,2) eq "11") {$frame_info{mode} = "mono"}
    #print "Mode: $frame_info{mode}\nbitrate: $frame_info{bitrate}, freq: $frame_info{freq}" if $self->{debug};
    
    
    $frame_info{frame_bytes} = ($frame_info{bitrate} * 144 / $frame_info{freq}) + $frame_info{padding_bit}; 
    $frame_info{frame_ticks} = 8 * $frame_info{frame_bytes} * 90000 / $frame_info{bitrate}; 
    
    #print "Mode: $frame_info{mode}\nbytes: $frame_info{frame_bytes}\nticks: $frame_info{frame_ticks}\nbitrate: $frame_info{bitrate}\n";
    $frame_info{packet_start} = substr($frame,0,2);
    #print "The start of the packet is: $frame_info{packet_start}\n";
    return ($frame, %frame_info);
    #print "frame analysed\n";
}

sub find_next_pes_packet {
    my $self = shift;
    my $buffer = shift;
    my $old_offset = shift;
    my $search_buffer;
    if ($old_offset > 0) {
        $search_buffer = substr($buffer, $old_offset);
    }
    else {
        $search_buffer = $buffer;
    }
    my $packet_start = pack("H6", "000001");
    print "TRYING TO FIND A NEW PES PACKET START AFTER $old_offset\n";
     print "Got a buffer of length " . length($buffer) . " to match against\n";  
    open DOF, ">./debug_buffer.vdr"  or die "Can not open File to dump buffer\n";
    binmode DOF;
    print DOF $buffer;
    close DOF;
    my $counter = 0;
    while ($search_buffer =~ /$packet_start/g)    {
        
        print "Now we found the new start at " . (pos($search_buffer)-3) . " \n";
        return (pos($search_buffer) - 3 + (length($buffer) - length($search_buffer)));
    }
    
    print "did not find  a new start\n";
    return -1;
}

sub final_flush {
    my $self = shift;
    my $flush_buffer; 
    
    my $FH = $self->{outfile};
    my $oldfh = select $FH; $| = 1; select $oldfh;
    print "should do the final flush for stream $self->{streamcode}\n"if $self->{debug};
    if ($self->{streamcode} eq $self->{masterstream}) {
        print "Flushing Video Buffer, " . (length($self->{GOP_buffer})) . " bytes left\n"if $self->{debug};
        my @pics = $self->{GOP_buffer} =~ /$self->{packet_start}/gm;
        print "The last GOP contains " . (scalar(@pics)) . " pics\n"if $self->{debug};
        #print $FH $self->{GOP_buffer};
        $flush_buffer .= $self->{GOP_buffer};
        $self->{GOP_buffer} = "";
        $self->{frameno} += scalar(@pics);
        my $chunk_end = ${$self->{timestamps}}[-1] + (scalar(@pics)) * $self->{frame_ticks};
        #my $chunk_end = ${$self->{timestamps}}[-1];
        $cutlist[$self->{cutcounter}] .= "::$chunk_end" . "::$self->{frameno}";
        if ($self->{debug}) {
            foreach(@cutlist) {print "FINAL: $_\n";}
        }
        $self->{frames_written} = $self->{frameno};
        print "\nclosing FH for stream $self->{streamcode}\n" if $self->{debug};
        #sleep 2;
        #print $FH $flush_buffer;
        if ($self->{use_pipe}) {
            my $length = pack("V", (length($flush_buffer)));
            print $FH $length       if $flush_buffer;
            print $FH $flush_buffer if $flush_buffer;
            print $FH pack("V", 0);
        } else {
            print $FH $flush_buffer if $flush_buffer;
        }
        close FH;
        return;
    }
    if (($self->{first_flush}) && (!$self->{audio_only})){
        my ($chunk_start, $chunk_end, $video_frameno) = split /::/,$cutlist[$self->{cutcounter}];
        sync_audio_start($self, $chunk_start);
    }
    
    #print $FH @{$self->{frame_buffer}};
    $flush_buffer = join "",@{$self->{frame_buffer}};
    $self->{frames_written} += scalar(@{$self->{frame_buffer}});
    @{$self->{frame_buffer}} = "";
    if (! $self->{audio_only}) {
        my ($chunk_start, $chunk_end, $video_frameno) = split /::/,$cutlist[-1];
        my $vid_time = $video_frameno * 3600;
        my $aud_time = $self->{frames_written} * $self->{frame_ticks};
        my $diff = $vid_time - $aud_time;
        #$self->{frames_written} += insert_frames($self, ($diff + ($self->{frame_ticks} * 20)));
    }
    #print $FH $flush_buffer;
    if ($self->{use_pipe}) {
        my $length = pack("V", (length($flush_buffer)));
        print $FH $length       if $flush_buffer;
        print $FH $flush_buffer if $flush_buffer;
        print $FH pack("V", 0);
    } else {
        print $FH $flush_buffer if $flush_buffer;
    }
    print"\nclosing FH for stream $self->{streamcode}\n" if $self->{debug};
    
    close FH;
}


sub dump_debug_buffer {
    my $self = shift;
    print "Something nasty happend, dumping debug.buffer and exit\n";
    my $start = 0;
    if ($self->{offset} < 10000) {
        print "Unfortunately the nasty thing happend at the start of a buffer \n";
        print "offset is only $self->{offset}; Dumping anyway...\n";
    }
    if ((length($self->{buffer}) - $self->{offset} ) < 1000000) {
        print "Unfortunately the nasty thing happend at the end of a buffer \n";
        print "offset is already $self->{offset} Dumping anyway...\n";
    }
    if ($self->{offset} > 1000000) {
        $start = $self->{offset} - 1000000
    }
    open DFH,  ">$self->{outputpath}/debug.buffer";
    binmode DFH;
    print DFH substr($self->{buffer}, $start, $start + 2000000);
    close DFH;
    print "please mail the file $self->{outputpath}/debug.buffer and a log of the\n";
    print "vdrsync run to peter\@vdr-portal.de, then I will try to fix the bug\n";
}


sub process_PES_packets    {
    my $self = shift;
    my $limit = shift;
    
    my $lengthcounter = 0;
    my $packetcounter = 0;
    
    my %pes_header;
    my $head_tmpl = 'H6 H2 n1 B8 B8 C1 B40';
    my $dts_tmpl = 'B40';
    
      # First we read the first chunk of data
    
    ( $pes_header{startcode}, $pes_header{stream_id}, 
        $pes_header{PES_packet_length}, $pes_header{eflags1}, 
        $pes_header{eflags2}, $pes_header{header_length},
        $pes_header{PTS_raw_bits}) 
        = unpack ( $head_tmpl, substr($self->{buffer}, 0, 12) );
        
    while (($self->{EOF} eq 0 || $self->{offset} +  $pes_header{PES_packet_length}  + 1) < (length($self->{buffer}))) {
        if ($kill_me) {
            if ($self->{dump_buffer}) {
                dump_debug_buffer($self);
                $kill_me = "all";
            }
            my @kill_list;
            if ($kill_me eq "all") { 
                @kill_list = keys(% {$self->{streams} })
            }
            else { 
                push @kill_list, $kill_me 
            }
            print "got the kill list @kill_list\n";
            foreach (@kill_list) {
                $self->{ignore_streams}{$_} = 1;
                my $FH = $self->{streams}{$_}->{outfile};
                close $FH;
                unlink $self->{streams}{$_}->{outfilename};
                print "Stream $_ was killed due to an error\n";
            }
            if ($kill_me eq "all") {die "Script stopped due to an error\n";}
            $kill_me="";
            
        }
        
        
        ( $pes_header{startcode}, $pes_header{stream_id}, 
        $pes_header{PES_packet_length}, $pes_header{eflags1}, 
        $pes_header{eflags2}, $pes_header{header_length},
        $pes_header{PTS_raw_bits}) 
        = unpack ( $head_tmpl, substr($self->{buffer}, $self->{offset}, 14) );
    
        if ($pes_header{startcode} ne "000001")    {
            print "No 0x000001 found at current packet, found $pes_header{startcode} instead\noffset is $self->{offset}\n"; 
            $self->{offset} = find_next_pes_packet($self, $self->{buffer}, $self->{offset});
            
            next;
        }
        my $decimal_code = hex($pes_header{stream_id});
        if ((!(191 < $decimal_code ) &&  (240 > $decimal_code)) &&  (! $decimal_code == 189)) {
            print "unknown Streamtype $pes_header{stream_id}, decimal $decimal_code ignoring\n" if $self->{debug};
            $self->{offset} +=  $pes_header{PES_packet_length} + 6;
            next;
        }
        # MPEG2 Audio and Video must have an extended Header as well as AC3
        $pes_header{payload_start} = (9 + $pes_header{header_length});
        # We check whether a TimeStamp is present:
        $pes_header{PTS_DTS_flags} = substr($pes_header{eflags2}, 0, 2);

        # overflow of PTS not checked, but it is now checked within every stream -> makes much more sense
        if (($pes_header{PTS_DTS_flags} eq "10") || ($pes_header{PTS_DTS_flags} eq "11")) {
            $pes_header{PTS_value_bits} = substr($pes_header{PTS_raw_bits},4,3) . substr($pes_header{PTS_raw_bits},8,15) . substr($pes_header{PTS_raw_bits},24,15);
            # decode the timestamp
            $pes_header{PTS_decimal} = oct("0b" . substr($pes_header{PTS_value_bits},1));
            $pes_header{PTS_decimal} += 4294967296 if (substr($pes_header{PTS_value_bits},0,1) == 1);
        }
        else {
            $pes_header{PTS_decimal} = 0;
        }
        $pes_header{data_align} = substr($pes_header{eflags1},6,1);
        
        if ((($self->{offset} + $pes_header{PES_packet_length} + 150) > (length($self->{buffer}))) && ($self->{EOF} == 0)) {
            #print "Reading some more data\n";
            #print "$self->{offset} is the offset\n";
            
            my $helpbuffer = substr($self->{buffer}, $self->{offset});
            $self->{buffer} = $helpbuffer . ${ readNextChunk($self) };
            #print "after reading buffer has a length of " . length($self->{buffer})  . "\n";
            $self->{offset} = 0;
        }

        my $packet = substr($self->{buffer}, $self->{offset}, $pes_header{PES_packet_length} + 6);
        $self->{offset} +=  $pes_header{PES_packet_length} + 6;
        $packetcounter++;
        $lengthcounter += ($pes_header{PES_packet_length} + 6);
        
        
        if ($self->{ignore_streams}{$pes_header{stream_id}}) {
            print "ignoring packet for stream $pes_header{stream_id}\n" if $self->{debug};
            next;
        }
        if (!($self->{streams}{$pes_header{stream_id}})) {
            print "\nNew Stream with id $pes_header{stream_id}. Ignoring the stream\n" if (! $self->{audio_only});
            $self->{ignore_streams}{$pes_header{stream_id}} = 1;
            next;
        }
        if ($self->{dump_packets}) {
            print "Dumping packet of stream $pes_header{stream_id} ($self->{dump_packets} to dump left)\n";
            my $DUMPFH;
            if (! $self->{"$pes_header{stream_id}.pes_dump"}) {
                open $DUMPFH, ">$pes_header{stream_id}.pes_dump"  or die "Can not open dumpfile: $!\n";
                binmode $DUMPFH;
                $self->{"$pes_header{stream_id}.pes_dump"} = $DUMPFH;
            }
            $DUMPFH = $self->{"$pes_header{stream_id}.pes_dump"};
            print $DUMPFH $packet;
            if ($self->{dump_packets}-- == 1) {
                exit;
            }
            next;
        }
        &{$self->{streams}{$pes_header{stream_id}}->{check_frames}} (
                    $self->{streams}{$pes_header{stream_id}},
                    substr($packet,$pes_header{payload_start}), 
                    \%pes_header
                    );
        }
    
    vprint "\n $packetcounter PES packets processed\n";
    
    
    if (! $self->{audio_only}) {
        $self->{streams}{$self->{masterstream}}->final_flush($self->{streams}{$self->{masterstream}});
    }
    foreach (keys (%{$self->{streams}})) {
        if ($self->{ignore_streams}{$_}) {next}
        if (! ($self->{masterstream} eq $_)) {
            print "Final flush for stream $_\n"if $self->{debug};
            $self->{streams}{$_}->final_flush($self->{streams}{$_});
        }
        my $seconds = $self->{streams}{$_}{frames_written} * $self->{streams}{$_}{frame_ticks} / 90000; #/
        vprint "$self->{streams}{$_}{frames_written} frames written for stream $_ ($seconds sec) \n";
        next;
    }
    print_script_output($self);
    if (! $self->{script_output}){print_stats($self)};
    
    
}

sub print_script_output {
    my $self = shift;
    
    my %final_properties;
    my $master = $self->{streams}{$self->{masterstream}};
    my $max = 0;
    
    foreach my $movie_property(keys(%{$master->{Unit_analysis_hash}})) {
        print "found key $movie_property\n" if $self->{debug};
        foreach my $subkey (keys(%{$master->{Unit_analysis_hash}{$movie_property}})) {
            print "found subkey $subkey with value $master->{Unit_analysis_hash}{$movie_property}{$subkey}\n" if $self->{debug};
            if ((!$final_properties{$movie_property}) || $max < $master->{Unit_analysis_hash}{$movie_property}{$subkey}) {
                $final_properties{$movie_property} = $subkey;
                $max = $master->{Unit_analysis_hash}{$movie_property}{$subkey};
            }
        
        }
    }
    $max = 0;
    if ($self->{streams}{bd}) {
        my $ac3_audio= $self->{streams}{bd};
        foreach my $ac3_property(keys(%{$ac3_audio->{Unit_analysis_hash}})) {
            print "found key $ac3_property\n" if $self->{debug};
            foreach my $subkey (keys(%{$ac3_audio->{Unit_analysis_hash}{$ac3_property}})) {
                print "found subkey $subkey with value $ac3_audio->{Unit_analysis_hash}{$ac3_property}{$subkey}\n" if $self->{debug};
                if ((!$final_properties{$ac3_property}) || $max < $ac3_audio->{Unit_analysis_hash}{$ac3_property}{$subkey}) {
                    $final_properties{$ac3_property} = $subkey;
                    $max = $ac3_audio->{Unit_analysis_hash}{$ac3_property}{$subkey};
                }
            }
        }
    }
    
    
    foreach (keys(%final_properties)) {
        print "Property $_ is $final_properties{$_}\n" if $self->{debug};
        $self->{streams}{$self->{masterstream}}->{$_} = $final_properties{$_}; 
    }
    if (! $self->{script_output}) {
        return;
    }
    
    
    print "\n" . "*" x 45  ."\n";
    my $SOF;
    if ($self->{script_output_file}) {
        open $SOF, ">$self->{outputpath}/$self->{script_output_file}"  or die "Can not open $self->{outputpath}/$self->{script_output_file}: $!\n";
    } else {
        $SOF = *STDOUT;
    }
    
    foreach (keys(%{$self->{streams}}))    {
        print "$self->{streams}{$_}{streamtype} for stream $_\n" if $self->{debug};
        if ($self->{ignore_streams}{$_}) {
            print "Ignoring stream $_\n" if $self->{debug};
            next;
        }
            
        if (($self->{streams}{$_}{streamtype} =~ /audio/) || ($self->{streams}{$_}{streamtype} =~ /Layer/))    {
            
            my $seconds = $self->{streams}{$_}->{frames_written} * $self->{streams}{$_}->{frame_ticks} / 90000; #/
            print $SOF "$_" . "_Audio_stream=yes\n";
            print $SOF "$_" . "_Audio_type=$self->{streams}{$_}{streamtype}\n";
            print $SOF "$_" . "_Sample_frequency=$self->{streams}{$_}->{freq}\n";            # only for Audio 
            print $SOF "$_" . "_Bitrate=$self->{streams}{$_}->{bitrate}\n";             # only for Audio
            print $SOF "$_" . "_Mode=$self->{streams}{$_}->{mode}\n";             # only for Audio
            print $SOF "$_" . "_Copyright=$self->{streams}{$_}->{copyright}\n" if  $self->{streams}{$_}->{copyright};        # only for Audio
            print $SOF "$_" . "_Bytes_per_frame=$self->{streams}{$_}->{frame_bytes}\n";        # only for Audio
            print $SOF "$_" . "_Ticks_per_frame=$self->{streams}{$_}->{frame_ticks}\n";        # one tick is 1 / 90000 of a second
            print $SOF "$_" . "_Total_frames=$self->{streams}{$_}->{frames_written}\n";
            print $SOF "$_" . "_Total_time=$seconds\n";
            print $SOF "$_" . "_Filename=$self->{streams}{$_}->{outfilename}\n\n";
            
        }
        if ($self->{streams}{$_}{streamtype} eq "video") {
                        
            my $seconds = $self->{streams}{$_}->{frames_written} * $self->{streams}{$_}->{frame_ticks} / 90000; #/
            print $SOF "$_" . "_Video_stream=yes\n";
            print $SOF "$_" . "_Aspect_ratio=$self->{streams}{$_}->{aspect_ratio}\n";
            print $SOF "$_" . "_Horizontal_size=$self->{streams}{$_}->{horizontal_size}\n";
            print $SOF "$_" . "_Vertical_size=$self->{streams}{$_}->{vertical_size}\n";
            print $SOF "$_" . "_Frames_per_Second=$self->{streams}{$_}->{fps}\n";
            print $SOF "$_" . "_Bitrate=$self->{streams}{$_}{bitrate_value}\n";
            print $SOF "$_" . "_Ticks_per_frame=$self->{streams}{$_}->{frame_ticks}\n";        
            print $SOF "$_" . "_Total_frames=$self->{streams}{$_}->{frames_written}\n";
            print $SOF "$_" . "_Total_time=$seconds\n";
            print $SOF "$_" . "_Filename=$self->{streams}{$_}->{outfilename}\n\n";
            
        }
    }
    close $SOF if ($self->{script_output_file});
}

sub init_PES_stream {
    
    my $self = shift;
    my $limit = shift;
    my $pakets_to_analyse = 200;
    if ($self->{info}) {
        $pakets_to_analyse = 10000;
    }
    my $plength = 0;
    $self->{IFH} = undef;         # Stores the filehandle for the current file
    $self->{EOF} = 0;         # All files processed ?
    $self->{total_input} = 0;
    my %pes_header;
    $pes_header{packet_length} = 0;
    my $head_tmpl = 'H6 H2 n1 B8 B8 C1 B40';
    #my %packet_buffer;
        
    
    my $packetcounter = 0;
    
    print "Initialising and analysing the streams....\n";
    
    
    $self->{buffer} = ${ readNextChunk($self) };     # First we read the first chunk of data
    
    while ((($self->{offset} + $pes_header{packet_length} + 1) < length($self->{buffer})) && ($packetcounter < $pakets_to_analyse) && (! $self->{EOF})) {
        ( $pes_header{startcode}, $pes_header{stream_id}, 
        $pes_header{PES_packet_length}, $pes_header{eflags1}, 
        $pes_header{eflags2}, $pes_header{header_length},
        $pes_header{PTS_raw_bits}) 
        = unpack ( $head_tmpl, substr($self->{buffer}, $self->{offset}, 12) );
        
        $pes_header{payload_start} = (9 + $pes_header{header_length});
        # there are at leat six bytes of header at the beginning of a PES packet
        if ( $pes_header{startcode} ne "000001") {
            vprint "No 0x000001 found at current packet, searching for next Packet start\n";
            $self->{offset} = find_next_pes_packet($self, $self->{buffer}, $self->{offset});
            if ($self->{offset} == -1) {$kill_me = "all"}
            next;            
        }
        # We check whether a TimeStamp is present:
        $pes_header{PTS_DTS_flags} = substr($pes_header{eflags2}, 0, 2);
        if (($pes_header{PTS_DTS_flags} eq "10") || ($pes_header{PTS_DTS_flags} eq "11")) {
            $pes_header{PTS_value_bits} = substr($pes_header{PTS_raw_bits},4,3) . substr($pes_header{PTS_raw_bits},8,15) . substr($pes_header{PTS_raw_bits},24,15);
            # decode the timestamp
            $pes_header{PTS_decimal} = unpack("N", (pack ("B32", substr($pes_header{PTS_value_bits},1))));
            $pes_header{PTS_decimal} += 4294967296 if (substr($pes_header{PTS_value_bits},0,1) == 1);
        }
        else {
            $pes_header{PTS_decimal} = 0;
        }
        
        my $packet = substr($self->{buffer}, $self->{offset}, ($pes_header{PES_packet_length} + 6));
        $self->{offset} +=  $pes_header{PES_packet_length} + 6;
        $packetcounter++;
        
        #print "analysed the first $packetcounter packets...\n" if $self->{debug};
       
        if (!$self->{streams}{$pes_header{stream_id}}) {
            if ($self->{ignore_streams}{$pes_header{stream_id}}){
                next;
            }
            
            my $decimal_code = hex($pes_header{stream_id});
            if ((223 < $decimal_code ) &&  (240 > $decimal_code)){
                if ($self->{audio_only}) {
                    $self->{ingnore_streams}{$pes_header{stream_id}} = 1;
                    next;
                }
                
                if ($self->{masterstream}){
                    vprint "Video stream already defined, but found stream $pes_header{stream_id} in addition. Ingnoring stream $pes_header{stream_id}\n";
                    $self->{ingnore_streams}{$pes_header{stream_id}} = 1;
                    next;
                }
                else {
                    $self->{streams}{$pes_header{stream_id}} = MPEGSTREAM->new
                            (
                            streamcode      => $pes_header{stream_id},
                            outputpath      => $self->{outputpath}, 
                            masterstream    => $pes_header{stream_id}, 
                            debug           => $self->{debug},
                            dump_payload    => $self->{dump_payload},
                            show_drift      => $self->{show_drift},
                            dump_buffer     => $self->{dump_buffer},
                            video_filter    => $self->{video_filter},
                            basename        => $self->{basename},
                            info            => $self->{info},
                            use_pipe        => $self->{use_pipe},
                            );
                    $self->{masterstream} = $pes_header{stream_id};
                    vprint "\nCreated new MPEG stream object for stream $pes_header{stream_id}, master video stream\n";
                    
                }
            }
            else {
                $self->{streams}{$pes_header{stream_id}} = MPEGSTREAM->new
                            (
                            streamcode      => $pes_header{stream_id}, 
                            masterstream    => $self->{masterstream}, 
                            outputpath      => $self->{outputpath}, 
                            debug           => $self->{debug},
                            dump_payload    => $self->{dump_payload},
                            audio_only      => $self->{audio_only},
                            show_drift      => $self->{show_drift},
                            dump_buffer     => $self->{dump_buffer},
                            audio_filter    => $self->{audio_filter},
                            ac3_filter      => $self->{ac3_filter},
                            basename        => $self->{basename},
                            info            => $self->{info},
                            use_pipe        => $self->{use_pipe},
                            );    
                vprint "\nCreated new MPEG stream object for stream $pes_header{stream_id} \n";
            }
                        
        }
      
        if (!($self->{streams}{$pes_header{stream_id}}{check_frames})) {
            print "sending frame number $packetcounter for analysis of stream $pes_header{stream_id}\n" if $self->{debug};
            init_stream($self->{streams}{$pes_header{stream_id}}, substr($packet, $pes_header{payload_start}), \%pes_header);
            $self->{packet_buffer}{$pes_header{stream_id}} = [ ];
        }
        if ($self->{info}) {
            push @{$self->{packet_buffer}{$pes_header{stream_id}}}, substr($packet, $pes_header{payload_start});
        }
    }
    
    vprint "analysed the first $packetcounter packets...\n";
    
    $self->{offset} = 0;
    
    if (!($self->{masterstream}))   {
        die "No video stream could be found within the first 2000 packets, exiting\n" if (! $self->{audio_only});
    }
    
    foreach (keys(%{$self->{streams}})) {
        $self->{streams}{$_}{masterstream} = $self->{masterstream};
        if ((!($self->{streams}{$_}{check_frames})) && (!($self->{ignore_streams}{$_}))) {
            vprint "The contents of stream $_ could not be identified, the stream will be skipped!\n";
            $self->{ignore_streams}{$_} = 1;
        }
    }
    print_stats($self) if $self->{debug};
    
    return;
}

sub compute_record_info {
    my $self = shift;
    
    my $index_size = 0;
    my $movie_length;
    my @results;


    foreach (keys(%{$self->{packet_buffer}})) {
        print "Got " . scalar(@{$self->{packet_buffer}{$_}}) ." packets for stream $_ to analyse\n" if ($self->{debug});
    }
    if ($self->{indir})  {
        #print "Got a dir at the command line $self->{indir}. trying to get the size of index.vdr\n";
        if (-e "$self->{indir}/index.vdr") {
            $index_size = -s "$self->{indir}/index.vdr";
            $movie_length = $index_size / 200;
            print "movie has a length of $movie_length\n";
            my $Recording = VDRRECORDING->new();
            $Recording->init_recording($self->{indir});
            
            foreach ( "movie_length", "movie_length_cutted", "movie_size", "movie_size_cutted") {
                print "$_ : $Recording->{$_} \n";
            }
            if ($self->{cut_recording}) {
                print "CUTFLAG HAS BEEN SET\n";
                print "Changing $movie_length to $Recording->{movie_length_cutted}\n";
                $movie_length = $Recording->{movie_length_cutted};
                print "Changing $total_size to $Recording->{movie_size_cutted}\n";
                $total_size = $Recording->{movie_size_cutted};
            }
        } else {
            die "no index.vdr in $self->{indir} found\n";
        }
    } else {
        #die "no indir specified\n";
    }
    print "\nindex.vdr has a size of $index_size, so movie has a length of $movie_length\n" if ($self->{dedug});
    foreach (keys(%{ $self->{packet_buffer} })) {
        if ($_ =~/^e/) {next}
       # print "here is stream $_\n";
        my %frame_info;
        my $packet_number = scalar(@{$self->{packet_buffer}{$_}});
        my $dummy = 0;
        
        for (my $i = $packet_number - 10; $i <= $packet_number; $i++) {
            ($dummy, %frame_info) = &{$self->{streams}{$_}->{analyse_frames}} ( $self->{streams}{$_},  ${$self->{packet_buffer}{$_}}[$i]);
            if ($dummy ne "-1") {
                last
            }
        }
        if ($self->{ignore_streams}{$_}) {
            print "Ignoring stream $_\n" if $self->{debug};
            next;
        }
        $self->{streams}{$_}->{freq}        = $frame_info{freq};
        $self->{streams}{$_}->{bitrate}     = $frame_info{bitrate};
        $self->{streams}{$_}->{mode}        = $frame_info{mode};             
        $self->{streams}{$_}->{copyright}   = $frame_info{copyright} if  $frame_info{copyright};        
        $self->{streams}{$_}->{frame_bytes} = $frame_info{frame_bytes};        
        $self->{streams}{$_}->{frame_ticks} = $frame_info{frame_ticks};
    }
     
    my $total_audio = 0;
    my $SOF;
    if ($self->{script_output_file}) {
        open $SOF, ">$self->{outputpath}/$self->{script_output_file}"  or die "Can not open $self->{outputpath}/$self->{script_output_file}: $!\n";
    } else {
        $SOF = *STDOUT;
    }
    
    foreach (keys(%{$self->{streams}}))    {
        print "$self->{streams}{$_}{streamtype} for stream $_\n" if $self->{debug};
        if ($self->{ignore_streams}{$_}) {
            print "Ignoring stream $_\n" if $self->{debug};
            next;
        }
            
        if (($self->{streams}{$_}{streamtype} =~ /audio/) || ($self->{streams}{$_}{streamtype} =~ /Layer/))    {
            
            my $stream_size = $self->{streams}{$_}->{bitrate} * $movie_length / 8;
            $self->{streams}{$_}->{total_bytes} = $stream_size;
            $total_audio += $stream_size ;
            
            
            push @results, "Audio_stream_" . "$_=yes\n";
            push @results, "Audio_type=$self->{streams}{$_}{streamtype}\n";
            push @results, "Audio_Bitrate=$self->{streams}{$_}->{bitrate}\n";
            push @results, "Audio_Total_bytes=$stream_size\n";
            
            if ($self->{script_output}) {        
                print $SOF "\n$_" . "_Audio_stream=yes\n";
                print $SOF "$_" . "_Audio_type=$self->{streams}{$_}{streamtype}\n";
                print $SOF "$_" . "_Sample_frequency=$self->{streams}{$_}->{freq}\n";            # only for Audio 
                print $SOF "$_" . "_Bitrate=$self->{streams}{$_}->{bitrate}\n";             # only for Audio
                print $SOF "$_" . "_Mode=$self->{streams}{$_}->{mode}\n";             # only for Audio
                print $SOF "$_" . "_Copyright=$self->{streams}{$_}->{copyright}\n" if  $self->{streams}{$_}->{copyright};        # only for Audio
                print $SOF "$_" . "_Bytes_per_frame=$self->{streams}{$_}->{frame_bytes}\n";        # only for Audio
                print $SOF "$_" . "_Ticks_per_frame=$self->{streams}{$_}->{frame_ticks}\n";        # one tick is 1 / 90000 of a second
                print $SOF "$_" . "_Total_frames=$self->{streams}{$_}->{frames_written}\n";
                print $SOF "$_" . "_Total_time=$movie_length\n";
                print $SOF "$_" . "_Total_bytes=$stream_size\n";
            }
        }
    }
    my $ms = $self->{masterstream};
    my $video_size = int(($total_size * 0.995 ) - $total_audio);
    $self->{streams}{$ms}->{total_bytes} = $video_size;
    $self->{streams}{$ms}{bitrate_estm}  = int($video_size * 8 / $movie_length) if $movie_length;
    
    
    push @results, "Video_stream_" . "$ms" . "=yes\n";
    push @results, "Video_Bitrate=$self->{streams}{$ms}{bitrate_estm}\n";
    push @results, "Video_Total_bytes=$video_size\n";
    push @results, "Aspect_ratio=$self->{streams}{$ms}{aspect_ratio}\n";
            
    
    
    if ($self->{script_output}) {
        print $SOF "\n$ms" . "_Video_stream=yes\n";
        print $SOF "$ms" . "_Aspect_ratio=$self->{streams}{$ms}->{aspect_ratio}\n";
        print $SOF "$ms" . "_Horizontal_size=$self->{streams}{$ms}->{horizontal_size}\n";
        print $SOF "$ms" . "_Vertical_size=$self->{streams}{$ms}->{vertical_size}\n";
        print $SOF "$ms" . "_Frames_per_Second=$self->{streams}{$ms}->{fps}\n";
        print $SOF "$ms" . "_Ticks_per_frame=$self->{streams}{$ms}->{frame_ticks}\n"; 
        print $SOF "$ms" . "_Bitrate=$self->{streams}{$ms}{bitrate_value}\n";
        print $SOF "$ms" . "_Real_Bitrate=$self->{streams}{$ms}{bitrate_estm}\n";
        print $SOF "$ms" . "_Total_time=$movie_length\n";
        print $SOF "$ms" . "_Total_bytes=$video_size\n";            
        close $SOF if ($self->{script_output_file});
    } else {
        print_stats($self);
    }
    return \@results;
}
    
    


sub get_aspect_ratio {
    my $self = shift;
    return $self->{streams}{$self->{masterstream}}->{aspect_ratio};
}

sub get_movie_length {
    my $self = shift;
    if ($self->{audio_only}) {
        if ($self->{streams}{c0}->{frames_written}) {
            return $self->{streams}{c0}->{frames_written}  * 0.024;
        } else {
            return -1;
        }
    }
    return $self->{streams}{$self->{masterstream}}->{frames_written} / 25;#/
}
    


sub readNextChunk {
    my $self = shift;
    my $IFH = $self->{IFH};
    
    #print "\nReadNextChunkStats:\t" if ($self->{debug});
    #print "Total Input: $self->{total_input}\n" if ($self->{debug});
    if ($self->{cut_recording}) {
        if (($self->{indir}) && (!$self->{total_input})) {
            $self->{VDRrecording} = VDRRECORDING->new;
            $self->{VDRrecording}->init_recording($self->{indir});
            
            
            $total_size = $self->{VDRrecording}->{movie_size_cutted};
            
            my $buffer = $self->{VDRrecording}->get_movie_piecewise();
            if ($buffer) {
                $self->{total_input} += length($buffer);
                #print "After reading for the first time: Total Input: $self->{total_input}\n";
                if (length($buffer) < 10000000) {
                    $self->{EOF} = 1;
                }
                my $status = sprintf ("%4d",  ($self->{total_input} / 1000000));#/
                print "\r$status Mbytes of " . int($total_size/1000000) . " read"; #if $self->{debug};  f
                return \$buffer;
            } else {
                $self->{EOF} = 1;
                return;
            }
        }
        if ($self->{indir}) {
            my $buffer =  $self->{VDRrecording}->get_movie_piecewise();
            if ($buffer) {
                $self->{total_input} += length($buffer);
                #print "After reading again: Total Input: $self->{total_input}\n";
                if (length($buffer) < 10000000) {
                    $self->{EOF} = 1;
                }
                my $status = sprintf ("%4d",  ($self->{total_input} / 1000000));#/
                #$bytes_read = $self->{total_input};
                print "\r$status Mbytes of " . int($total_size/1000000) . " read"; #if $self->{debug};  f
                return \$buffer
            }
            else {
                $self->{EOF} = 1;
                return;
            }
        }
    }
    
    if ($self->{stdin}) {
        my $buffer = "";
        my $helpbuffer;
        
        
        while ((length($buffer) < 10000000) && (sysread STDIN, $helpbuffer, 1000000, 0)) {
            $buffer .= $helpbuffer;
            $helpbuffer = 0;
        }
        if (length($buffer) < 10000000) {
            $self->{EOF} = 1;
            print "\n All Input from STDIN read\n";
        }
        $self->{total_input} += length($buffer);
        #print "After reading again: Total Input: $self->{total_input}\n";
        my $status = sprintf ("%4d",  ($self->{total_input} / 1000000));#/
                #$bytes_read = $self->{total_input};
        print "\r$status Mbytes read from STDIN"; #if $self->{debug};  f
        return \$buffer;
    }
    
    if (!($self->{IFH})) {
        my $firstfile = shift @{$self->{files}};
        print "trying to open $firstfile\n" if $self->{debug};
        open $IFH, $firstfile   or die "$! happend while opening $firstfile\n";
        binmode $IFH;
        $self->{IFH} = $IFH;
    }
    
    my $rbuffer;
    my $rbytes = sysread $IFH, $rbuffer, 10000000, 0;
    
    if ($rbytes != 10000000 ) {
        if ((scalar(@{$self->{files}}))== 0) {
            vprint "\nall Input files processed\n";
            vprint "EOF reached\n";
            $self->{EOF} = 1;
        }
        else {
            my $nextf = shift @{$self->{files}};
            vprint "$nextf is the next file\n";
            close $IFH;
            open $IFH, $nextf   or die "$! happend while opening $nextf\n";
            binmode $IFH;
            $self->{IFH} = $IFH;
            my $helpbuffer;
            my $helprbytes = sysread $self->{IFH}, $helpbuffer, (10000000 - length($rbuffer)), 0;
            $rbuffer .= $helpbuffer;
            $rbytes += $helprbytes;
        }
    }
     $self->{total_input} += $rbytes;
    my $status = sprintf ("%4d",  ($self->{total_input} / 1000000));#/
    $bytes_read = $self->{total_input};
    print "\r$status Mbytes of " . int($total_size/1000000) . " read"; #if $self->{debug};  f
    return \$rbuffer;
}



sub get_silent_frame {
    my $self = shift;
    my %info = % { shift @_ };
    my $frame;
    my $function;
    
    if ($info{streamtype} eq "MPEG1_Layer_2") {
        $info{mode} =~ s/joint_stereo/stereo/;
        $function = "get_$info{mode}_" . "$info{bitrate}";
    }
    elsif ($info{streamtype} eq "AC3_Audio") {
        $info{mode} =~ s/\//_/;
        $function = "get_ac3_$info{mode}_" . "$info{bitrate}";
    }
    else {
        print "Can not understand Format $info{streamtype}, no silent frame available\n" if $self->{debug}; return -1;
    }
    
    my $uu_frame =  eval $function ;
    if (! $uu_frame) {print "No silent frame available for $function\n" if $self->{debug};  return -1}
    foreach (split "\n", $uu_frame) {
        last if /^end/;
        next if /[a-z]/;
        next unless int((((ord() - 32) & 077) + 2) / 3) == int(length() / 4);
        $frame .= unpack "u", $_;
    }
    
    return $frame;
}



sub get_mono_32000 {
my $frame = <<'End_FRAME';
M__T4P!%)I&JJOOOOOOOOFM?/EL?/FM:^?+8^?-:U\^6Q\^:UKY\MCY\UK7SY
M;'SYK6OGRV/GS6M?/EL?/FM:^?+8^?-:U\^6Q\^:UKY\MCY\UK7SY;'SYK6O
&GRV/GS0`
end
End_FRAME
return $frame;
}

sub get_mono_48000 {
my $frame = <<'End_FRAME';
M__TDP#-R-NJJOOOOOOOOEL6Q;'=W=UL6Q;%L6Q;'=W=UL6Q;%L6Q;'=W=UL6
MQ;%L6Q;'=W=UL6Q;%L6Q;'=W=UL6Q;%L6Q;'=W=UL6Q;%L6Q;'=W=UL6Q;%L
M6Q;'=W=UL6Q;%L6Q;'=W=UL6Q;%L6Q;'=W=UL6Q;%L6Q;'=W=UL6Q;%L6Q;'
)=W=UL6Q;````
end
End_FRAME
return $frame;
}

sub get_mono_56000 {

my $frame = <<'End_FRAME';
M__TTP!(C,R(B$D``````JJJJOOOOOOOOOOOOOFMMMMMMMM\^?/FM:UMMMMMM
MMOGSY\UK6MMMMMMMM\^?/FM:UMMMMMMMOGSY\UK6MMMMMMMM\^?/FM:UMMMM
MMMMOGSY\UK6MMMMMMMM\^?/FM:UMMMMMMMOGSY\UK6MMMMMMMM\^?/FM:UMM
AMMMMMOGSY\UK6MMMMMMMM\^?/FM:UMMMMMMMOGSY\UK0
end
End_FRAME

return $frame;
}

sub get_mono_64000 {
my $frame = <<'End_FRAME';
M__U$P"(D1$,B)$``````JJJJOOOOOOOOOOOOOFVVVVMBV+8MC;?/GSYK;;;;
M6Q;%L6QMOGSY\UMMMMK8MBV+8VWSY\^:VVVVUL6Q;%L;;Y\^?-;;;;:V+8MB
MV-M\^?/FMMMMM;%L6Q;&V^?/GS6VVVVMBV+8MC;?/GSYK;;;;6Q;%L6QMOGS
MY\UMMMMK8MBV+8VWSY\^:VVVVUL6Q;%L;;Y\^?-;;;;:V+8MBV-M\^?/FMMM
,MM;%L6Q;&V^?/GS0
end
End_FRAME
return $frame;
}


sub get_mono_80000 {
my $frame = <<'End_FRAME';
M__U4P"(U541#-HD`````JJJJJ^^^^^^^^^^^^^^^;;;=W=W=W=W6Q;%L;;;;
M;YK6VVW=W=W=W=UL6Q;&VVVV^:UMMMW=W=W=W=;%L6QMMMMOFM;;;=W=W=W=
MW6Q;%L;;;;;YK6VVW=W=W=W=UL6Q;&VVVV^:UMMMW=W=W=W=;%L6QMMMMOFM
M;;;=W=W=W=W6Q;%L;;;;;YK6VVW=W=W=W=UL6Q;&VVVV^:UMMMW=W=W=W=;%
ML6QMMMMOFM;;;=W=W=W=W6Q;%L;;;;;YK6VVW=W=W=W=UL6Q;&VVVV^:UMMM
/W=W=W=W=;%L6QMMMMOFM
end
End_FRAME
return $frame;
}

sub get_mono_96000 {
my $frame = <<'End_FRAME';
M__UDP#,V9E5$2-$@````JJJJJOOOOOOOOOOOOOOOOG=W=W=WO>][WO>[N[NM
MBV+8MC;?-:[N[N[N][WO>][W=W=UL6Q;%L;;YK7=W=W=WO>][WO>[N[NMBV+
M8MC;?-:[N[N[N][WO>][W=W=UL6Q;%L;;YK7=W=W=WO>][WO>[N[NMBV+8MC
M;?-:[N[N[N][WO>][W=W=UL6Q;%L;;YK7=W=W=WO>][WO>[N[NMBV+8MC;?-
M:[N[N[N][WO>][W=W=UL6Q;%L;;YK7=W=W=WO>][WO>[N[NMBV+8MC;?-:[N
M[N[N][WO>][W=W=UL6Q;%L;;YK7=W=W=WO>][WO>[N[NMBV+8MC;?-:[N[N[
2N][WO>][W=W=UL6Q;%L;;YK0
end
End_FRAME
return $frame;
}

sub get_mono_112000 {
my $frame = <<'End_FRAME';
M__UTP#-&=F956R(D````JJJJJK[[[[[[[[[[[[[[[[YW=W=[WO>]]]][WO>]
M[WN[N[N[N[K8MCYK7=W=WO>][WWWWO>][WO>[N[N[N[NMBV/FM=W=W>][WO?
M??>][WO>][N[N[N[NZV+8^:UW=W=[WO>]]]][WO>][WN[N[N[N[K8MCYK7=W
M=WO>][WWWWO>][WO>[N[N[N[NMBV/FM=W=W>][WO???>][WO>][N[N[N[NZV
M+8^:UW=W=[WO>]]]][WO>][WN[N[N[N[K8MCYK7=W=WO>][WWWWO>][WO>[N
M[N[N[NMBV/FM=W=W>][WO???>][WO>][N[N[N[NZV+8^:UW=W=[WO>]]]][W
MO>][WN[N[N[N[K8MCYK7=W=WO>][WWWWO>][WO>[N[N[N[NMBV/FM=W=W>][
5WO???>][WO>][N[N[N[NZV+8^:T`
end
End_FRAME
return $frame;
}

sub get_mono_128000 {
my $frame = <<'End_FRAME';
M__V$P$17=V9F:V-D````JJJJJK[[[[[[[[[[[[[[[[Y[WO>]]]]]]]]]]]]]
M[WO>][WO>][WN[N[K8VVVU[WO>]]]]]]]]]]]]][WO>][WO>][WN[N[K8VVV
MU[WO>]]]]]]]]]]]]][WO>][WO>][WN[N[K8VVVU[WO>]]]]]]]]]]]]][WO
M>][WO>][WN[N[K8VVVU[WO>]]]]]]]]]]]]][WO>][WO>][WN[N[K8VVVU[W
MO>]]]]]]]]]]]]][WO>][WO>][WN[N[K8VVVU[WO>]]]]]]]]]]]]][WO>][
MWO>][WN[N[K8VVVU[WO>]]]]]]]]]]]]][WO>][WO>][WN[N[K8VVVU[WO>]
M]]]]]]]]]]]][WO>][WO>][WN[N[K8VVVU[WO>]]]]]]]]]]]]][WO>][WO>
M][WN[N[K8VVVU[WO>]]]]]]]]]]]]][WO>][WO>][WN[N[K8VVVU[WO>]]]]
8]]]]]]]]][WO>][WO>][WN[N[K8VVVT`
end
End_FRAME
return $frame;
}

sub get_mono_160000 {
my $frame = <<'End_FRAME';
M__V4P%5HF(=W?;60@```JJJJJJ^^^^^^^^^^^^^^^^^^???????OW[]^_?W]
M_?OW[]^_????????????>][WO>][NZV+8U]]]]]]^_?OW[]_?W]^_?OW[]]]
M]]]]]]]]]][WO>][WN[K8MC7WWWWWW[]^_?OW]_?W[]^_?OWWWWWWWWWWWWW
MO>][WO>[NMBV-???????OW[]^_?W]_?OW[]^_????????????>][WO>][NZV
M+8U]]]]]]^_?OW[]_?W]^_?OW[]]]]]]]]]]]]][WO>][WN[K8MC7WWWWWW[
M]^_?OW]_?W[]^_?OWWWWWWWWWWWWWO>][WO>[NMBV-???????OW[]^_?W]_?
MOW[]^_????????????>][WO>][NZV+8U]]]]]]^_?OW[]_?W]^_?OW[]]]]]
M]]]]]]]][WO>][WN[K8MC7WWWWWW[]^_?OW]_?W[]^_?OWWWWWWWWWWWWWO>
M][WO>[NMBV-???????OW[]^_?W]_?OW[]^_????????????>][WO>][NZV+8
MU]]]]]]^_?OW[]_?W]^_?OW[]]]]]]]]]]]]][WO>][WN[K8MC7WWWWWW[]^
>_?OW]_?W[]^_?OWWWWWWWWWWWWWO>][WO>[NMBV-
end
End_FRAME
return $frame;
}

sub get_mono_192000 {
my $frame = <<'End_FRAME';
M__VDP%9IF8B(?[:Q$```JJJJJJOOOOOOOOOOOOOOOOOOOGWWW[]^_?OW]_?W
M]_?W]_?W[]^_?OW[]^_?OWWWW__?_]__WO>][WO>[NMCYK[[[]^_?OW[^_O[
M^_O[^_O[]^_?OW[]^_?OW[[[[__O_^__[WO>][WO=W6Q\U]]]^_?OW[]_?W]
M_?W]_?W]^_?OW[]^_?OW[]]]]__W__?_][WO>][WN[K8^:^^^_?OW[]^_O[^
M_O[^_O[^_?OW[]^_?OW[]^^^^__[__O_^][WO>][W=UL?-????OW[]^_?W]_
M?W]_?W]_?OW[]^_?OW[]^_????_]__W__>][WO>][NZV/FOOOOW[]^_?O[^_
MO[^_O[^_OW[]^_?OW[]^_?OOOO_^__[__O>][WO>]W=;'S7WWW[]^_?OW]_?
MW]_?W]_?W[]^_?OW[]^_?OWWWW__?_]__WO>][WO>[NMCYK[[[]^_?OW[^_O
M[^_O[^_O[]^_?OW[]^_?OW[[[[__O_^__[WO>][WO=W6Q\U]]]^_?OW[]_?W
M]_?W]_?W]^_?OW[]^_?OW[]]]]__W__?_][WO>][WN[K8^:^^^_?OW[]^_O[
M^_O[^_O[^_?OW[]^_?OW[]^^^^__[__O_^][WO>][W=UL?-????OW[]^_?W]
M_?W]_?W]_?OW[]^_?OW[]^_????_]__W__>][WO>][NZV/FOOOOW[]^_?O[^
D_O[^_O[^_OW[]^_?OW[]^_?OOOO_^__[__O>][WO>]W=;'S0
end
End_FRAME
return $frame;
}


sub get_stereo_48000 {
my $frame = <<'End_FRAME';
M__TD```1)22)))JJJJK[[[[[[[[[[[[[YK6M?/GSYK6M:UK6M?/GSYK6M:UK
M6M?/GSYK6M:UK6M?/GSYK6M:UK6M?/GSYK6M:UK6M?/GSYK6M:UK6M?/GSYK
M6M:UK6M?/GSYK6M:UK6M?/GSYK6M:UK6M?/GSYK6M:UK6M?/GSYK6M:UK6M?
)/GSYK6M:T```
end
End_FRAME
return $frame;
}

sub get_stereo_56000{
my $frame = <<'End_FRAME';
M__TT`!$11222))JJJJJOOOOOOOOOOOOOOOOFM:U\U\^?/GSYK6M:UK7S7SY\
M^?/FM:UK6M?-?/GSY\^:UK6M:U\U\^?/GSYK6M:UK7S7SY\^?/FM:UK6M?-?
M/GSY\^:UK6M:U\U\^?/GSYK6M:UK7S7SY\^?/FM:UK6M?-?/GSY\^:UK6M:U
A\U\^?/GSYK6M:UK7S7SY\^?/FM:UK6M?-?/GSY\^:UK0
end
End_FRAME
return $frame;
}

sub get_stereo_64000 {
my $frame = <<'End_FRAME';
M__U$`!$12;;21)JJJJJOOOOOOOOOOOOOOOOFM:U\^6Q;%L6Q\^?-:UK6M?/E
ML6Q;%L?/GS6M:UK7SY;%L6Q;'SY\UK6M:U\^6Q;%L6Q\^?-:UK6M?/EL6Q;%
ML?/GS6M:UK7SY;%L6Q;'SY\UK6M:U\^6Q;%L6Q\^?-:UK6M?/EL6Q;%L?/GS
M6M:UK7SY;%L6Q;'SY\UK6M:U\^6Q;%L6Q\^?-:UK6M?/EL6Q;%L?/GS6M:UK
,7SY;%L6Q;'SY\UK0
end
End_FRAME
return $frame;
}

sub get_stereo_80000{
my $frame = <<'End_FRAME';
M__U4`"(B;;;;;2JJJJJOOOOOOOOOOOOOOOOGSY\^6Q;%L6Q;%L6Q;%L6Q\^?
M/GSY;%L6Q;%L6Q;%L6Q;'SY\^?/EL6Q;%L6Q;%L6Q;%L?/GSY\^6Q;%L6Q;%
ML6Q;%L6Q\^?/GSY;%L6Q;%L6Q;%L6Q;'SY\^?/EL6Q;%L6Q;%L6Q;%L?/GSY
M\^6Q;%L6Q;%L6Q;%L6Q\^?/GSY;%L6Q;%L6Q;%L6Q;'SY\^?/EL6Q;%L6Q;%
ML6Q;%L?/GSY\^6Q;%L6Q;%L6Q;%L6Q\^?/GSY;%L6Q;%L6Q;%L6Q;'SY\^?/
/EL6Q;%L6Q;%L6Q;%L?/@
end
End_FRAME
return $frame;
}

sub get_stereo_96000{
my $frame = <<'End_FRAME';
M__UD`#,SCDDD;;JJJJJOOOOOOOOOOOOOOOOEL6Q;%L=W6QW=W=W=W=W=W=;%
ML6Q;%L6Q;%L=W6QW=W=W=W=W=W=;%L6Q;%L6Q;%L=W6QW=W=W=W=W=W=;%L6
MQ;%L6Q;%L=W6QW=W=W=W=W=W=;%L6Q;%L6Q;%L=W6QW=W=W=W=W=W=;%L6Q;
M%L6Q;%L=W6QW=W=W=W=W=W=;%L6Q;%L6Q;%L=W6QW=W=W=W=W=W=;%L6Q;%L
M6Q;%L=W6QW=W=W=W=W=W=;%L6Q;%L6Q;%L=W6QW=W=W=W=W=W=;%L6Q;%L6Q
M;%L=W6QW=W=W=W=W=W=;%L6Q;%L6Q;%L=W6QW=W=W=W=W=W=;%L6Q;%L6Q;%
2L=W6QW=W=W=W=W=W=;%L6Q;`
end
End_FRAME
return $frame;
}

sub get_stereo_112000{
my $frame = <<'End_FRAME';
M__UT`!$B(C-#,S(B(B(1))````````````"JJJJJJJJOOOOOOOOOOOOOOOOO
MOOOOOOOOOFM;;;;;;;;;6QMMMMMM\^?/GSY\UK6M:UK;;;;;;;;:V-MMMMMO
MGSY\^?/FM:UK6M;;;;;;;;;6QMMMMMM\^?/GSY\UK6M:UK;;;;;;;;:V-MMM
MMMOGSY\^?/FM:UK6M;;;;;;;;;6QMMMMMM\^?/GSY\UK6M:UK;;;;;;;;:V-
MMMMMMOGSY\^?/FM:UK6M;;;;;;;;;6QMMMMMM\^?/GSY\UK6M:UK;;;;;;;;
M:V-MMMMMOGSY\^?/FM:UK6M;;;;;;;;;6QMMMMMM\^?/GSY\UK6M:UK;;;;;
M;;;:V-MMMMMOGSY\^?/FM:UK6M;;;;;;;;;6QMMMMMM\^?/GSY\UK6M:UK;;
5;;;;;;:V-MMMMMOGSY\^?/FM:UK0
end
End_FRAME
return $frame;
}

sub get_stereo_128000{
my $frame = <<'End_FRAME';
M__V$`"(B(D1$1#,R(B(B)))```````````"JJJJJJJJJ^^^^^^^^^^^^^^^^
M^^^^^^^^^^^^;;;;;;;;;6Q;%L6Q;%L;;;;;Y\^?/GSYK6M:UMMMMMMMMM;%
ML6Q;%L6QMMMMOGSY\^?/FM:UK6VVVVVVVVUL6Q;%L6Q;&VVVV^?/GSY\^:UK
M6M;;;;;;;;;6Q;%L6Q;%L;;;;;Y\^?/GSYK6M:UMMMMMMMMM;%L6Q;%L6QMM
MMMOGSY\^?/FM:UK6VVVVVVVVUL6Q;%L6Q;&VVVV^?/GSY\^:UK6M;;;;;;;;
M;6Q;%L6Q;%L;;;;;Y\^?/GSYK6M:UMMMMMMMMM;%L6Q;%L6QMMMMOGSY\^?/
MFM:UK6VVVVVVVVUL6Q;%L6Q;&VVVV^?/GSY\^:UK6M;;;;;;;;;6Q;%L6Q;%
ML;;;;;Y\^?/GSYK6M:UMMMMMMMMM;%L6Q;%L6QMMMMOGSY\^?/FM:UK6VVVV
8VVVVUL6Q;%L6Q;&VVVV^?/GSY\^:UK6M
end
End_FRAME
return $frame;
}


sub get_stereo_160000{
my $frame = <<'End_FRAME';
M__V4`"(R,U55541$0S,S;2))``````````"JJJJJJJJJK[[[[[[[[[[[[[[[
M[[[[[[[[[[[[[[YMMMW=MN[N[N[N[N[N[N[N[K8MBV+8MC;;;;;;;;;;?/FM
M:UMMMW=MN[N[N[N[N[N[N[N[K8MBV+8MC;;;;;;;;;;?/FM:UMMMW=MN[N[N
M[N[N[N[N[N[K8MBV+8MC;;;;;;;;;;?/FM:UMMMW=MN[N[N[N[N[N[N[N[K8
MMBV+8MC;;;;;;;;;;?/FM:UMMMW=MN[N[N[N[N[N[N[N[K8MBV+8MC;;;;;;
M;;;;?/FM:UMMMW=MN[N[N[N[N[N[N[N[K8MBV+8MC;;;;;;;;;;?/FM:UMMM
MW=MN[N[N[N[N[N[N[N[K8MBV+8MC;;;;;;;;;;?/FM:UMMMW=MN[N[N[N[N[
MN[N[N[K8MBV+8MC;;;;;;;;;;?/FM:UMMMW=MN[N[N[N[N[N[N[N[K8MBV+8
MMC;;;;;;;;;;?/FM:UMMMW=MN[N[N[N[N[N[N[N[K8MBV+8MC;;;;;;;;;;?
M/FM:UMMMW=MN[N[N[N[N[N[N[N[K8MBV+8MC;;;;;;;;;;?/FM:UMMMW=MN[
>N[N[N[N[N[N[N[K8MBV+8MC;;;;;;;;;;?/FM:T`
end
End_FRAME
return $frame;
}

sub get_stereo_192000{
my $frame = <<'End_FRAME';
M__VD`#,S,V9F9E551$1$DC21)`````````"JJJJJJJJJJOOOOOOOOOOOOOOO
MOOOOOOOOOOOOOOOOOG=W=W=W=W=W=WO>][WO>][WO>][W=W=W=W=UL6Q;%L6
MQ;%L6Q;&V^?/FM:[N[N[N[N[N[N][WO>][WO>][WO>[N[N[N[NMBV+8MBV+8
MMBV+8VWSY\UK7=W=W=W=W=W=WO>][WO>][WO>][W=W=W=W=UL6Q;%L6Q;%L6
MQ;&V^?/FM:[N[N[N[N[N[N][WO>][WO>][WO>[N[N[N[NMBV+8MBV+8MBV+8
MVWSY\UK7=W=W=W=W=W=WO>][WO>][WO>][W=W=W=W=UL6Q;%L6Q;%L6Q;&V^
M?/FM:[N[N[N[N[N[N][WO>][WO>][WO>[N[N[N[NMBV+8MBV+8MBV+8VWSY\
MUK7=W=W=W=W=W=WO>][WO>][WO>][W=W=W=W=UL6Q;%L6Q;%L6Q;&V^?/FM:
M[N[N[N[N[N[N][WO>][WO>][WO>[N[N[N[NMBV+8MBV+8MBV+8VWSY\UK7=W
M=W=W=W=W=WO>][WO>][WO>][W=W=W=W=UL6Q;%L6Q;%L6Q;&V^?/FM:[N[N[
MN[N[N[N][WO>][WO>][WO>[N[N[N[NMBV+8MBV+8MBV+8VWSY\UK7=W=W=W=
MW=W=WO>][WO>][WO>][W=W=W=W=UL6Q;%L6Q;%L6Q;&V^?/FM:[N[N[N[N[N
D[N][WO>][WO>][WO>[N[N[N[NMBV+8MBV+8MBV+8VWSY\UK0
end
End_FRAME
return $frame;
}


sub get_stereo_224000{
my $frame = <<'End_FRAME';
M__VT`#,S1'=W9F9F5555MD;2))````````"JJJJJJJJJJJ^^^^^^^^^^^^^^
M^^^^^^^^^^^^^^^^^^^^=W=W=W=W>][WO????????????>][WO>][WO>][WO
M=W=W=W=W=W=W=W=W6Q;&VVWSYK6M=W=W=W=W>][WO????????????>][WO>]
M[WO>][WO=W=W=W=W=W=W=W=W6Q;&VVWSYK6M=W=W=W=W>][WO???????????
M?>][WO>][WO>][WO=W=W=W=W=W=W=W=W6Q;&VVWSYK6M=W=W=W=W>][WO???
M?????????>][WO>][WO>][WO=W=W=W=W=W=W=W=W6Q;&VVWSYK6M=W=W=W=W
M>][WO????????????>][WO>][WO>][WO=W=W=W=W=W=W=W=W6Q;&VVWSYK6M
M=W=W=W=W>][WO????????????>][WO>][WO>][WO=W=W=W=W=W=W=W=W6Q;&
MVVWSYK6M=W=W=W=W>][WO????????????>][WO>][WO>][WO=W=W=W=W=W=W
M=W=W6Q;&VVWSYK6M=W=W=W=W>][WO????????????>][WO>][WO>][WO=W=W
M=W=W=W=W=W=W6Q;&VVWSYK6M=W=W=W=W>][WO????????????>][WO>][WO>
M][WO=W=W=W=W=W=W=W=W6Q;&VVWSYK6M=W=W=W=W>][WO????????????>][
MWO>][WO>][WO=W=W=W=W=W=W=W=W6Q;&VVWSYK6M=W=W=W=W>][WO???????
M?????>][WO>][WO>][WO=W=W=W=W=W=W=W=W6Q;&VVWSYK6M=W=W=W=W>][W
JO????????????>][WO>][WO>][WO=W=W=W=W=W=W=W=W6Q;&VVWSYK6M
end
End_FRAME
return $frame;
}

sub get_stereo_256000{
my $frame = <<'End_FRAME';
M__W$`$1$57=W=V9F9F9FVMD;:)````````"JJJJJJJJJJJ^^^^^^^^^^^^^^
M^^^^^^^^^^^^^^^^^^^^>][WO>][WO????????????????????????>][WO>
M][WO>][WO>][WO>][WO>][WO=W=W6Q;&VVVV^:U[WO>][WO>]]]]]]]]]]]]
M]]]]]]]]]]]]][WO>][WO>][WO>][WO>][WO>][WO>]W=W=;%L;;;;;YK7O>
M][WO>][WWWWWWWWWWWWWWWWWWWWWWWWWO>][WO>][WO>][WO>][WO>][WO>]
M[W=W=UL6QMMMMOFM>][WO>][WO????????????????????????>][WO>][WO
M>][WO>][WO>][WO>][WO=W=W6Q;&VVVV^:U[WO>][WO>]]]]]]]]]]]]]]]]
M]]]]]]]]][WO>][WO>][WO>][WO>][WO>][WO>]W=W=;%L;;;;;YK7O>][WO
M>][WWWWWWWWWWWWWWWWWWWWWWWWWO>][WO>][WO>][WO>][WO>][WO>][W=W
M=UL6QMMMMOFM>][WO>][WO????????????????????????>][WO>][WO>][W
MO>][WO>][WO>][WO=W=W6Q;&VVVV^:U[WO>][WO>]]]]]]]]]]]]]]]]]]]]
M]]]]][WO>][WO>][WO>][WO>][WO>][WO>]W=W=;%L;;;;;YK7O>][WO>][W
MWWWWWWWWWWWWWWWWWWWWWWWWO>][WO>][WO>][WO>][WO>][WO>][W=W=UL6
MQMMMMOFM>][WO>][WO????????????????????????>][WO>][WO>][WO>][
MWO>][WO>][WO=W=W6Q;&VVVV^:U[WO>][WO>]]]]]]]]]]]]]]]]]]]]]]]]
M][WO>][WO>][WO>][WO>][WO>][WO>]W=W=;%L;;;;;YK7O>][WO>][WWWWW
MWWWWWWWWWWWWWWWWWWWWO>][WO>][WO>][WO>][WO>][WO>][W=W=UL6QMMM
#MOFM
end
End_FRAME
return $frame;
}

sub get_stereo_320000{
my $frame = <<'End_FRAME';
M__W4`%559IB9B(AW=W=WVVVMDC)```````"JJJJJJJJJJJK[[[[[[[[[[[[[
M[[[[[[[[[[[[[[[[[[[[[[Y]]]]]]]]]]]]^_?OW[]_?W]^_?O[^_O[^_OW[
M]^_?OW[]^_?OOOOOOOOOOOOOOOOOOOOOOOOO>][WO>][WO>][WN[N[K8MBV-
MMK7WWWWWWWWWWWW[]^_?OW]_?W[]^_O[^_O[^_?OW[]^_?OW[]^^^^^^^^^^
M^^^^^^^^^^^^^^^][WO>][WO>][WO>[N[NMBV+8VVM?????????????OW[]^
M_?W]_?OW[^_O[^_O[]^_?OW[]^_?OW[[[[[[[[[[[[[[[[[[[[[[[[[WO>][
MWO>][WO>][N[NZV+8MC;:U]]]]]]]]]]]]^_?OW[]_?W]^_?O[^_O[^_OW[]
M^_?OW[]^_?OOOOOOOOOOOOOOOOOOOOOOOOO>][WO>][WO>][WN[N[K8MBV-M
MK7WWWWWWWWWWWW[]^_?OW]_?W[]^_O[^_O[^_?OW[]^_?OW[]^^^^^^^^^^^
M^^^^^^^^^^^^^^][WO>][WO>][WO>[N[NMBV+8VVM?????????????OW[]^_
M?W]_?OW[^_O[^_O[]^_?OW[]^_?OW[[[[[[[[[[[[[[[[[[[[[[[[[WO>][W
MO>][WO>][N[NZV+8MC;:U]]]]]]]]]]]]^_?OW[]_?W]^_?O[^_O[^_OW[]^
M_?OW[]^_?OOOOOOOOOOOOOOOOOOOOOOOOO>][WO>][WO>][WN[N[K8MBV-MK
M7WWWWWWWWWWWW[]^_?OW]_?W[]^_O[^_O[^_?OW[]^_?OW[]^^^^^^^^^^^^
M^^^^^^^^^^^^^][WO>][WO>][WO>[N[NMBV+8VVM?????????????OW[]^_?
MW]_?OW[^_O[^_O[]^_?OW[]^_?OW[[[[[[[[[[[[[[[[[[[[[[[[[WO>][WO
M>][WO>][N[NZV+8MC;:U]]]]]]]]]]]]^_?OW[]_?W]^_?O[^_O[^_OW[]^_
M?OW[]^_?OOOOOOOOOOOOOOOOOOOOOOOOO>][WO>][WO>][WN[N[K8MBV-MK7
MWWWWWWWWWWWW[]^_?OW]_?W[]^_O[^_O[^_?OW[]^_?OW[]^^^^^^^^^^^^^
M^^^^^^^^^^^^][WO>][WO>][WO>[N[NMBV+8VVM?????????????OW[]^_?W
M]_?OW[^_O[^_O[]^_?OW[]^_?OW[[[[[[[[[[[[[[[[[[[[[[[[[WO>][WO>
/][WO>][N[NZV+8MC;:T`
end
End_FRAME
return $frame;
}


sub get_stereo_384000 {
my $frame = <<'End_FRAME';
M__WD`%5F9IF9F8B(AW=W_^VML;2```````"JJJJJJJJJJJK[[[[[[[[[[[[[
M[[[[[[[[[[[[[[[[[[[[[[Y]]]]]]^_?OW[]^_?OW[]_?W]_?W]_?W]_?W]_
M?W]_?W]^_?OW[]^_?OW[]^_?OOOOOOOOOOOOOOOO_^__[__O_^__[__O_^__
M[__O>][WO>]W=W=W=;&VVWSY]]]]]]^_?OW[]^_?OW[]_?W]_?W]_?W]_?W]
M_?W]_?W]^_?OW[]^_?OW[]^_?OOOOOOOOOOOOOOOO_^__[__O_^__[__O_^_
M_[__O>][WO>]W=W=W=;&VVWSY]]]]]]^_?OW[]^_?OW[]_?W]_?W]_?W]_?W
M]_?W]_?W]^_?OW[]^_?OW[]^_?OOOOOOOOOOOOOOOO_^__[__O_^__[__O_^
M__[__O>][WO>]W=W=W=;&VVWSY]]]]]]^_?OW[]^_?OW[]_?W]_?W]_?W]_?
MW]_?W]_?W]^_?OW[]^_?OW[]^_?OOOOOOOOOOOOOOOO_^__[__O_^__[__O_
M^__[__O>][WO>]W=W=W=;&VVWSY]]]]]]^_?OW[]^_?OW[]_?W]_?W]_?W]_
M?W]_?W]_?W]^_?OW[]^_?OW[]^_?OOOOOOOOOOOOOOOO_^__[__O_^__[__O
M_^__[__O>][WO>]W=W=W=;&VVWSY]]]]]]^_?OW[]^_?OW[]_?W]_?W]_?W]
M_?W]_?W]_?W]^_?OW[]^_?OW[]^_?OOOOOOOOOOOOOOOO_^__[__O_^__[__
MO_^__[__O>][WO>]W=W=W=;&VVWSY]]]]]]^_?OW[]^_?OW[]_?W]_?W]_?W
M]_?W]_?W]_?W]^_?OW[]^_?OW[]^_?OOOOOOOOOOOOOOOO_^__[__O_^__[_
M_O_^__[__O>][WO>]W=W=W=;&VVWSY]]]]]]^_?OW[]^_?OW[]_?W]_?W]_?
MW]_?W]_?W]_?W]^_?OW[]^_?OW[]^_?OOOOOOOOOOOOOOOO_^__[__O_^__[
M__O_^__[__O>][WO>]W=W=W=;&VVWSY]]]]]]^_?OW[]^_?OW[]_?W]_?W]_
M?W]_?W]_?W]_?W]^_?OW[]^_?OW[]^_?OOOOOOOOOOOOOOOO_^__[__O_^__
M[__O_^__[__O>][WO>]W=W=W=;&VVWSY]]]]]]^_?OW[]^_?OW[]_?W]_?W]
M_?W]_?W]_?W]_?W]^_?OW[]^_?OW[]^_?OOOOOOOOOOOOOOOO_^__[__O_^_
M_[__O_^__[__O>][WO>]W=W=W=;&VVWSY]]]]]]^_?OW[]^_?OW[]_?W]_?W
M]_?W]_?W]_?W]_?W]^_?OW[]^_?OW[]^_?OOOOOOOOOOOOOOOO_^__[__O_^
M__[__O_^__[__O>][WO>]W=W=W=;&VVWSY]]]]]]^_?OW[]^_?OW[]_?W]_?
MW]_?W]_?W]_?W]_?W]^_?OW[]^_?OW[]^_?OOOOOOOOOOOOOOOO_^__[__O_
;^__[__O_^__[__O>][WO>]W=W=W=;&VVWSX`
end
End_FRAME
return $frame;
}


sub get_ac3_2_0_448000 {
my $frame = <<'End_FRAME';
M"W>KMQY`0W_X2P:@N&'_.KY\^?/GSY\^?/GSY\^?/GSY\^?/GSY\^?/GSY\^
M?/GSY\^?/GSY\^?/GSY\^?/GSY\^?/GSY\^?/GSY\^??_.KY\^?/GSY\^?/G
MSY\^?/GSY\^?/GSY\^?/GSY\^?/GSY\^?/GSY\^?/GSY\^?/GSY\^?/GSY\^
M?/GSY\^?>4^D($``````````````````````````````````````````````
M````````````````````````````````````````````````````````````
M`````````````````````````````````````````````````````````\>/
M'CQX\>/'CQX`````````````````````````````````````````````````
M````````````````````````````````````````````````````````````
M`````````````````````````````````````````````````````'CQX\>/
M'CQX\>/#````````````````````````````````````````````````````
M````````````````````````````````````````````````````````````
M````````````````````````````````````````````````````'CQX\>/'
MCQX\>/``````````````````````````````````````````````````````
M````````````````````````````````````````````````````````````
M`````````````````````````````````````````````````\>/'CQX\>/'
MCQX8````````````````````````````````````````````````````````
M````````````````````````````````````````````````````````````
M````````````````````````````````````````````````\>/'CQX\>/'C
MQX``````````````````````````````````````````````````````````
M````````````````````````````````````````````````````````````
M````````````````````````````````````````````'CQX\>/'CQX\>/#`
M````````````````````````````````````````````````````````````
M````````````````````````````````````````````````````````````
M```````````````````````````````````````````'CQX\>/'CQX\>/```
M````````````````````````````````````````````````````````````
M````````````````````````````````````````````````````````````
M````````````````````````````````````````\>/'CQX\>/'CQX8`0>`7
M9',I_X0C2.]?[C,4O>``````````````````````````````````````````
M````````````````````````````````````````````````````````````
M````````````````````````````````````````````````````````````
M'CQX\>/'CQX\>/``````````````````````````````````````````````
M````````````````````````````````````````````````````````````
M`````````````````````````````````````````````````````````\>/
M'CQX\>/'CQX8`0X`1GEVD"Z"X6#I04H";@OIC#8Z]Q8X)BNY;28^@```````
M````````````````````````````````````````````````````````````
M````````````````````````````````````````````````````````````
M``````````````````````````````````!X\>/'CQX\>/'CP```````````
M````````````````````````````````````````````````````````````
M````````````````````````````````````````````````````````````
E```````````````````````````````/'CQX\>/'CQX\>``C>@``
end
End_FRAME
return $frame;
}

sub get_ac3_3_2_448000 {
my $frame = <<'End_FRAME';
M"W>9&!Y`X=_^$L`^_UE_P\/X>'\/#^'A_#P55X^?/GSY\^?/GSY\^?/GSY\^
M??\ZOGSY\^?/GSY\^?/GSY\^?/GSY\^?/GSY\^?/GSY\^?/GSY\^?/GSY\^?
M/GW_SJ^?/GSY\^?/GSY\^?/GSY\^?/GSY\^?/GSY\^?/GSY\^?/GSY\^?/GS
MY\^??_.KY\^?/GSY\^?/GSY\^?/GSY\^?/GSY\^?/GSY\^?/GSY\^?/GSY\^
M?/GSY]_\ZOGSY\^?/GSY\^?/GSY\^?/GSY\^?/GSY\^?/GSY\^?/GSY\^?/G
MSY\^?/GW_SJ^?/GSY\^?/GSY\^?/GSY\^?/GSY\^?/GSY\^?/GSY\^?/GSY\
M^?/GSY\^??_.N4^&)$B1(D2)``````&^;;;;;;;;>/'CN[N[N[N[N[N[N[N[
MN[QX\;;;?/FM:UK6M:UK6M:UK6M:UK0````&VVVVVVVV\>/'=W=W=W=W=W=W
M=W=W=WCQXVVV^?-:UK6M:UK6M:UK6M:UK6@````-MMMMMMMMX\>.[N[N[N[N
M[N[N[N[N[O'CQMMM\^:UK6M:UK6M:UK6M:UK6M`````;YMMMMMMMMX\>.[N[
MN[N[N[N[N[N[N[O'CQMMM\^:UK6M:UK6M:UK6M:UK6M`````;;;;;;;;;QX\
M=W=W=W=W=W=W=W=W=W>/'C;;;Y\UK6M:UK6M:UK6M:UK6M:`````V#X`````
M```!OFVVVVVVVWCQX[N[N[N[N[N[N[N[N[N\>/&VVWSYK6M:UK6M:UK6M:UK
M6M:T````!MMMMMMMMO'CQW=W=W=W=W=W=W=W=W=X\>-MMOGS6M:UK6M:UK6M
M:UK6M:UH````#;;;;;;;;>/'CN[N[N[N[N[N[N[N[N[QX\;;;?/FM:UK6M:U
MK6M:UK6M:UK0````&^;;;;;;;;>/'CN[N[N[N[N[N[N[N[N[QX\;;;?/FM:U
MK6M:UK6M:UK6M:UK0````&VVVVVVVV\>/'=W=W=W=W=W=W=W=W=WCQXVVV^?
M-:UK6M:UK6M:UK6M:UK6@````-@^`````````;YMMMMMMMMX\>.[N[N[N[N[
MN[N[N[N[O'CQMMM\^:UK6M:UK6M:UK6M:UK6M`````;;;;;;;;;QX\=W=W=W
M=W=W=W=W=W=W>/'C;;;Y\UK6M:UK6M:UK6M:UK6M:`````VVVVVVVVWCQX[N
M[N[N[N[N[N[N[N[N\>/&VVWSYK6M:UK6M:UK6M:UK6M:T````!OFVVVVVVVW
MCQX[N[N[N[N[N[N[N[N[N\>/&VVWSYK6M:UK6M:UK6M:UK6M:T````!MMMMM
MMMMO'CQW=W=W=W=W=W=W=W=W=X\>-MMOGS6M:UK6M:UK6M:UK6M:UH````#8
M/@````````&^;;;;;;;;>/'CN[N[N[N[N[N[N[N[N[QX\;;;?/FM:UK6M:UK
M6M:UK6M:UK0````&VVVVVVVV\>/'=W=W=W=W=W=W=W=W=WCQXVVV^?-:UK6M
M:UK6M:UK6M:UK6@````-MMMMMMMMX\>.[N[N[N[N[N[N[N[N[O'CQMMM\^:U
MK6M:UK6M:UK6M:UK6M`````;YMMMMMMMMX\>.[N[N[N[N[N[N[N[N[O'CQMM
MM\^:UK6M:UK6M:UK6M:UK6M`````;;;;;;;;;QX\=W=W=W=W=W=W=W=W=W>/
M'C;;;Y\UK6M:UK6M:UK6M:UK6M:`````V#X````````!OFVVVVVVVWCQX[N[
MN[N[N[N[N[N[N[N\>/&VVWSYK6M:UK6M:UK6M:UK6M:T````!MMMMMMMMO'C
MQW=W=W=W=W=W=W=W=W=X\>-MMOGS6M:UK6M:UK6M:UK6M:UH````#;;;;;;;
M;>/'CN[N[N[N[N[N[N[N[N[QX\;;;?/FM:UK6M:UK6M:UK6M:UK0````&^;;
M;;;;;;>/'CN[N[N[N[N[N[N[N[N[QX\;;;?/FM:UK6M:UK6M:UK6M:UK0```
M`&VVVVVVVV\>/'=W=W=W=W=W=W=W=W=WCQXVVV^?-:UK6M:UK6M:UK6M:UK6
M@````-@^```!`@!=D<R`````WS;;;;;;;;QX\=W=W=W=W=W=W=W=W=W>/'C;
M;;Y\UK6M:UK6M:UK6M:UK6M:`````VVVVVVVVWCQX[N[N[N[N[N[N[N[N[N\
M>/&VVWSYK6M:UK6M:UK6M:UK6M:T````!MMMMMMMMO'CQW=W=W=W=W=W=W=W
M=W=X\>-MMOGS6M:UK6M:UK6M:UK6M:UH````#?-MMMMMMMO'CQW=W=W=W=W=
MW=W=W=W=X\>-MMOGS6M:UK6M:UK6M:UK6M:UH````#;;;;;;;;>/'CN[N[N[
EN[N[N[N[N[N[QX\;;;?/FM:UK6M:UK6M:UK6M:UK0````&PK$```
end
End_FRAME
return $frame;
}


###################################################################################
# Package VDR recording for handling marks.vdr, index.vdr and requesting specific parts of a recording
###################################################################################
package      VDRRECORDING;
require      Exporter;

our @ISA       = qw(Exporter);
our @EXPORT    = qw(
		);

our $VERSION   = "0.0.1";		        # Version number
use strict;
use warnings;

sub new {
	my $invocant = shift;
    my $class   = ref($invocant) || $invocant;
    my $self = {
		files 			    => [],
		file_handles 	    => [],
		file_sizes 		    => [],
		marks_file 		    => "",
		summary_file 	    => "",
        movie_size          => 0,
        movie_size_cutted   => 0,
        movie_length        => 0,
        movie_length_cutted => 0,
		index_file 		    => "",
		IFH 			    => "",
		marks 			    => [],
		Iframes 		    => {}, 		  # Hash that contains the Framenumber as the key, and the fole and offset as the value
		Iframe_list 	    => [],		  # A list that just contains the Frame Number of of the I-Frames. useful for finding the nth Iframe
		EOF 			    => 0,
		filepos 		    => 0,
		total_input 	    => 0,
		DIR 			    => "",		  # A dirhandle to the recording's DIR
		total_frames 	    => 0,
		frame_pos 		    => 0,          # Inidicates at which frame the last operation started, usefull for relative Operations
		current_pos 	    => 0,  
		current_frame	    => 0,
		split_output	    => 0,
        cut_info            => {
                start           => 0,
                start_file      => 1,
                start_offset    => 0,
                chunk_length    => 0,
                bytes_written   => 0,
                total_bytes     => 0,
                current_chunk   => "",
                current_offset  => "",
                
                        },
		@_,
	};
	return bless $self, $class;
}
sub read_chunk{
	my $self = shift;
	my ($start, $end) = @_;
	($start, my $fileno) = ( split/ : /, $start );
	$fileno--;
	$end = ( split/ : /, $end )[0];
	my $bytes_to_read = $end - $start;
	my $IFH = ${$self->{file_handles}}[$fileno];
	
	
	my $success = (sysseek $IFH, $start, 0);
	#print "seeked to pos $success, $start\n";
	if (!($start == $success)) {
		print "could not seek, dieing..\n"; die;
	}
	my $rbuffer;
	my $rbytes = sysread $IFH, $rbuffer, $bytes_to_read, 0;

		if ($rbytes != $bytes_to_read) {
				if (! ${$self->{file_handles}}[$fileno+1]) {
					print "Something weird happend, GOP not in files\n";
					exit;
				}
			else {
				$IFH = ${$self->{file_handles}}[$fileno+1];
				
				my $helpbuffer;
				my $helprbytes = sysread $IFH, $helpbuffer, ($bytes_to_read - length($rbuffer)), 0;
				$rbuffer .= $helpbuffer;
				$rbytes += $helprbytes;
			}
		}
	return \$rbuffer;
};

sub find_GOP_offset{
	my $self = shift;
	my $frame = int (shift);
	#print "got frame $frame\n";
	if ($self->{Iframes}{$frame}) {
		return ($frame, $self->{Iframes}{$frame});
	}
	my $return_frame = 0;
	my $i = 1;
	while ((! $return_frame) && ($i < 10)) {
		if ($self->{Iframes}{$frame - $i}) {
			print "Frame $i frames before $frame is an I-Frame. Returning " . ($frame - $i) . " " .$self->{Iframes}{$frame - $i} . "\n";
			return ($frame - $i , $self->{Iframes}{$frame - $i});
		}
		elsif ($self->{Iframes}{$frame + $i}) {
			print "Frame $i frames behind $frame is an I-Frame. returning " . ($frame + $i) . " " .$self->{Iframes}{$frame + $i} . "\n";
			return ($frame + $i , $self->{Iframes}{$frame + $i});
		}
		else {
			print "no iframe found in $i frame distance\n",
			$i++;
		}
	}
	die "could not find a reasonably positioned iframe, exiting\n";
};

sub get_gop_at{
	my $self = shift;
	my $sec_to_get = shift;
	(my $current_gop, my $return_frameno) = get_gop($self, $sec_to_get, 1);
	return ($current_gop, $return_frameno);
};

sub get_next_gop{
	my $self = shift;
	my $diff = shift;
	if (! $self->{current_frame}) {
		$self->{current_frame} = 1;
	}
	my $now = $self->{current_frame} * 0.04;
	(my $current_gop, $self->{current_frame}) = get_gop($self, $now + $diff, 1);
	return ($current_gop, $self->{current_frame});
};

sub get_file_list {
	my $self = shift;
	my $DIR;
	my $indir = $self->{indir};
	opendir $DIR, $indir  or die "Can not open $indir $!\n";
	#print "trying to open $indir\n";
	if (! $DIR) {
		die "did not get a handle back\n";
	}
	my @allfiles =   grep { ! /^\./  } readdir $DIR;
	my @files =   sort (grep { /\d{3}.vdr$/  } @allfiles);

	$self->{summary_file} = $indir . "summary.vdr" if (-e "$indir/summary.vdr");
	$self->{marks_file} = $indir . "marks.vdr" if (-e "$indir/marks.vdr");
	$self->{index_file} = $indir . "index.vdr" if (-e "$indir/index.vdr");
	
	foreach (@files) {
		$_ = $indir . $_;
		#print "trying to open $_\n";
		my $DUMMYFH;
		open $DUMMYFH, "$_"  or die "Can not open $_: $!\n";
		push @{$self->{file_handles}}, $DUMMYFH;
		my ($dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$size,$atime,$mtime,$ctime,$blksize,$blocks)
            = stat $DUMMYFH;
		push @{$self->{file_sizes}},$size;
        $self->{movie_size} += $size;
	}
	foreach (@{$self->{file_handles}}) {
		#print "handle: $_\n";
	}
	foreach (@{$self->{file_sizes}}) {
		#print "size: $_\n";
	}
};

sub read_marks_file {
	my $self = shift;
	if (! $self->{marks_file}) {return;}
	#print "Trying to get marks_file: $self->{marks_file}\n";
	open MFH, "$self->{marks_file}"  or die "Could not open marks.vdr";
	@{$self->{marks}} = <MFH>;
	foreach (@{$self->{marks}}) {
		#print "got mark $_";
		chomp $_;
		my ($h,$m,$s,$f) = split /[:.]/,$_;
		#print "$h,$m,$s,$f\n";
		my $frame = ($h * 3600 + $m * 60 + $s)* 25 + $f;
		#print "$frame\n";
		my $real_frame = (find_GOP_offset($self, $frame))[0];
		
		$_ = frames2mark($self, $real_frame);
		#print "replaced $frame with $real_frame, result is: $_\n";
	}
};

sub read_index_file{
	my $self = shift;
	
	#print "*" x40 ."\n";
	#print "READING INDEX FILE, please be patient\n";
	#print "*" x40 . "\n";
	my $index_file = $self->{index_file};
	open FH,"$index_file"  or die "Can not open File: $!\n";
	
	my $buffer;
	my $bytesread = sysread (FH, $buffer, 10000000);

	my $offset = 0;
	my $I_counter = 0;
	
	while ($offset < $bytesread) {
		my ($c, $Ptype, $number, $reserved) = unpack ("I C C S", substr($buffer, $offset, 8));
		#print "Got $c, $Ptype, $number, $reserved\n";
		$offset += 8;
		#print "offset is $offset\n";
		$I_counter++;
		$self->{Iframes}{$I_counter} = "$c : $number" if ($Ptype == 1);
		push @{$self->{Iframe_list}},$I_counter if ($Ptype == 1);
		
	}
	#print "got " . (scalar(keys(%{$self->{Iframes}}))) . " I-Frames in this index file...\n";
	$self->{total_frames} = $I_counter;
	return ;
};


sub get_gop {
	my $self = shift;
	my $sec = shift;
	
	if ($sec > $self->{total_frames} * 0.04) {
		$sec = ($self->{total_frames} * 0.04) -1;
	}
	if ($sec < 0) {
		$sec = 0;
	}
	my $gops_to_get = shift;
	my $frame_offset = $sec * 25;
	
	my ($frameno, $gop_offset) = find_GOP_offset($self, $frame_offset);
	print "wanted frame $frame_offset, got $frameno at $gop_offset\n";
	
	my $counter = $frameno;
	while ($gops_to_get) {
		$counter++;
		if ($counter > $self->{total_frames}) {
			$counter = ${$self->{Iframe_list}}[-1];
		}
		if (! $self->{Iframes}{$counter}) {
			next;
		}
		else {
			$gops_to_get--;
		}
	}
	my $gop_end = $self->{Iframes}{$counter};
	print "Now we should read the range $gop_offset to $gop_end\n";
	
	my $raw_chunk = ${ read_chunk ($self, $gop_offset, $gop_end) };
	my $offset = 0;
	my $video_stream = "";
	my $plength = 0;
	my $packet_counter = 0;
	
	while ($offset + $plength < length($raw_chunk))	{
		# analyse the needed info from PES Header
		my ($startcode,  $streamcode, $plength, $notused, $header_length) = unpack ( "H6 H2 n1 n1 C1", substr($raw_chunk, $offset, 9));
		
		if ($startcode ne "000001") {
			print "No 0x000001 found at current packet, found $startcode and $streamcode instead in packet\n"; exit
		}
		if (! ($streamcode =~/^e/)) {
			#print "video stream $streamcode found\n";
			#print "not a video stream\n";
			$offset +=  $plength + 6;
			next;
		}
		if (($offset + $plength) > (length($raw_chunk))) {
			die "Did not get a rawchunk back that ends with a packet...\n";
			return \$video_stream;
		}
		
		my $packet = substr($raw_chunk, $offset, ($plength + 6));
		my $payload = (substr($packet, 9 + $header_length));
		$offset +=  $plength + 6;
		$video_stream .= $payload;
		$packet_counter++;
	}

	print "returning " . length($video_stream) . " bytes of video data\n";
	return $video_stream, $frameno;
};

sub get_movie_chunk	{
	my $self =shift;
	my $sec = shift;
	
	if ($sec > $self->{total_frames} * 0.04) {
		$sec = ($self->{total_frames} * 0.04) -1;
	}
	if ($sec < 0) {
		$sec = 0;
	}
	my $gops_to_get = shift;
	my $frame_offset = $sec * 25;
	
	my ($frameno, $gop_offset) = find_GOP_offset($self, $frame_offset);
	print "IN get_movie_chunk: wanted frame $frame_offset, got $frameno at $gop_offset\n";
	
	my $counter = $frameno;
	while ($gops_to_get) {
		$counter++;
		if ($counter > $self->{total_frames}) {
			$counter = ${$self->{Iframe_list}}[-1];
		}
		if (! $self->{Iframes}{$counter}) {
			next;
		}
		else {
			$gops_to_get--;
		}
	}
	my $gop_end = $self->{Iframes}{$counter};
	print "Now we should read the range $gop_offset to $gop_end\n";
	
	my $raw_chunk = ${ read_chunk($self, $gop_offset, $gop_end) };
	my $offset = 0;
	my $video_stream = "";
	my $plength = 0;
	my $packet_counter = 0;
	
	while ($offset + $plength < length($raw_chunk))	{
		# analyse the needed info from PES Header
		my ($startcode,  $streamcode, $plength, $notused, $header_length) = unpack ( "H6 H2 n1 n1 C1", substr($raw_chunk, $offset, 9));
		
		if ($startcode ne "000001") {
			print "No 0x000001 found at current packet, found $startcode and $streamcode instead in packet\n"; exit
		}
		if (($streamcode =~/^b/)) {
			#print "video stream $streamcode found\n";
			#print "not a video stream\n";
			$offset +=  $plength + 6;
			next;
		}
		if (($offset + $plength) > (length($raw_chunk))) {
			die "Did not get a rawchunk back that ends with a packet...\n";
			return \$video_stream;
		}
		
		my $packet = substr($raw_chunk, $offset, ($plength + 6));
		#my $payload = (substr($packet, 9 + $header_length));
		$offset +=  $plength + 6;
		$video_stream .= $packet;
		$packet_counter++;
	}

	print "returning " . length($video_stream) . " bytes of vdr data\n";
	return $video_stream, $frameno;
};



sub get_next_movie_chunk {
	
    my $self = shift;
	my @chunks = @{$self->{cut_chunks}};
	print "in do_cut we got @chunks and $self->{split_output} for split\n";
	my $start;
	my $total_bytes    = 0;
	#my $finished       = 0;
	       
    my $read_for_chunk = 0;
    my $cunk_no = scalar(@chunks);
        
    foreach (@chunks) {
		print "Processing $_\n";
        my ($start, $end) = split/ TO /, $_;
		print "do_cut: $start, $end\n";
    }
	    
	my $counter = 1;	
	my $filename = sprintf "chunk_%03u.vdr", $counter;
	
    my $start_file;
    my $end_file;
    my $start_offset;
    my $end_offset;
	my $buffer;
    my $rbuffer;
    
    open TOF, ">./testout.vdr";
    
    while (! $self->{EOF}) {
        print "next round of reading\n";
        if ((! $self->{current_chunk}) && (! $self->{bytes_written})) {
            #print "seems to be the very first read...\n";
            ($start_offset, $start_file, $end_offset, $end_file) = split/ : | TO /, $chunks[$self->{current_chunk}];
            $self->{current_file}   = $start_file;
            $self->{current_offset} = $start_offset;
        }
        #print " Now we move to $self->{current_file}, $self->{current_offset}, $end_file, $end_offset\n";
        
        my $fileno = $self->{current_file} -1;
		my $IFH = ${$self->{file_handles}}[$fileno];
        #print "$IFH\n";
        my $success = (sysseek $IFH, $self->{current_offset}, 0);
		#print "seeked to pos $success, $start\n";
		if (!($self->{current_offset} == $success)) {
			print "could not seek, dieing..\n"; die;
		}
		my $bytes_to_read = 10000000;
        while ($bytes_to_read && (! $self->{EOF})) {
            if ($self->{current_file} == $end_file) {
                print "start file is end file: $self->{current_file}, $self->{current_offset}, $end_file, $end_offset\n";
                if (($end_offset - $self->{current_offset}) > 10000000) {
                    print "print Enough bytes to read left in this file\n";
                    my $rbytes = sysread $IFH, $rbuffer, $bytes_to_read, 0;
                    print "Read $rbytes\n";
                    $bytes_to_read  -= $rbytes;
                    $buffer         .= $rbuffer;
                    $self->{current_offset} += $rbytes;
                    print "current offset is now $self->{current_offset}\n";
                    print "bytes_to_read is now $bytes_to_read\n";
                }
                else {
                    #print "not enough bytes to read left in current file or end of recording\n";
                    #print "Trying to read " . ($end_offset - $self->{current_offset}) ."\n";
                    my $rbytes = sysread $IFH, $rbuffer, ($end_offset - $self->{current_offset}), 0;
                    $bytes_to_read  -= $rbytes;
                    $self->{current_offset} += $rbytes;
                    $buffer         .= $rbuffer;
                    if ($chunks[$self->{current_chunk} + 1]) {
                        #print "switching to next chunk\n";
                        $self->{current_chunk}++;
                        ($start_offset, $start_file, $end_offset, $end_file) = split/ : | TO /, $chunks[$self->{current_chunk}];
                        $self->{current_file}   = $start_file;
                        $self->{current_offset} = $start_offset;
                        $IFH = ${$self->{file_handles}}[$start_file - 1];
                        #print "$IFH\n";
                        $success = (sysseek $IFH, $self->{current_offset}, 0);
                        #print "After Switch: $self->{current_file}, $self->{current_offset}, $end_file, $end_offset\n";
                    } else {
                        $self->{EOF} = 1;
                    }
                }
            }
            else { 
                my $rbytes = sysread $IFH, $rbuffer, $bytes_to_read, 0;
                $bytes_to_read  -= $rbytes;
                $self->{current_offset} += $rbytes;
                $buffer         .= $rbuffer;
                print "in the current file could read " . length($buffer) . " bytes\n";
                if ($bytes_to_read) {
                    #print "That was not enough, awitching to next file\n";
                    $IFH = ${$self->{file_handles}}[$self->{current_file}];
                    $self->{current_file}++;
                    #print "$IFH\n";
                    $success = (sysseek $IFH, 0, 0);
                    $self->{current_offset} = 0;
                    #print "After Switch: $start_file, $start_offset, $end_file, $end_offset\n";
                } 
            }
            
        }
		
        print "read " . length($buffer) . " until now\n";
        $self->{bytes_written} += length($buffer);
        print TOF $buffer;
        $buffer = "";
    }
    close TOF;
    die "finished all?\n";

};


sub get_movie_piecewise {
	
    my $self = shift;
	
    my @chunks = @{$self->{cut_chunks}};
	#print "in do_cut we got @chunks and $self->{split_output} for split\n";
	
    my $start;
	my $total_bytes    = 0;
	    
    my $cunk_no = scalar(@chunks);
    
        	
    my $start_file;
    my $end_file;
    my $start_offset;
    my $end_offset;
	my $buffer;
    my $rbuffer;
    
    if (! $self->{bytes_written}) {
       # foreach (@chunks) {
       #     print "Processing $_\n";
       #     my ($start, $end) = split/ TO /, $_;
       #     print "do_cut: $start, $end\n";
        #}
    }
    
    
    if ($self->{EOF}) {
        return "";
    }

    #print "next round of reading\n";
    ($start_offset, $start_file, $end_offset, $end_file) = split/ : | TO /, $chunks[$self->{current_chunk}];

    if ((! $self->{current_chunk}) && (! $self->{bytes_written})) {
        #print "seems to be the very first read...\n";
        $self->{current_file}   = $start_file;
        $self->{current_offset} = $start_offset;
    }
        #print " Now we move to $self->{current_file}, $self->{current_offset}, $end_file, $end_offset\n";
        
        
	my $IFH = ${$self->{file_handles}}[$self->{current_file} - 1];
    #print "$IFH\n";
    my $success = (sysseek $IFH, $self->{current_offset}, 0);
	#print "seeked to pos $success, $start\n";
	if (!($self->{current_offset} == $success)) {
		print "could not seek, dieing..\n"; die;
	}
	my $bytes_to_read = 10000000;
    while ($bytes_to_read && (! $self->{EOF})) {
        if ($self->{current_file} == $end_file) {
            #print "start file is end file: $self->{current_file}, $self->{current_offset}, $end_file, $end_offset\n";
            if (($end_offset - $self->{current_offset}) > 10000000) {
                #print "Enough bytes to read left in this file\n";
                my $rbytes = sysread $IFH, $rbuffer, $bytes_to_read, 0;
                #print "Read $rbytes\n";
                $bytes_to_read  -= $rbytes;
                $buffer         .= $rbuffer;
                $self->{current_offset} += $rbytes;
                #print "current offset is now $self->{current_offset}\n";
                #print "bytes_to_read is now $bytes_to_read\n";
            }
            else {
                #print "not enough bytes to read left in current file or end of recording\n";
                #print "Trying to read " . ($end_offset - $self->{current_offset}) ."\n";
                my $rbytes = sysread $IFH, $rbuffer, ($end_offset - $self->{current_offset}), 0;
                $bytes_to_read  -= $rbytes;
                $self->{current_offset} += $rbytes;
                $buffer         .= $rbuffer;
                if ($chunks[$self->{current_chunk} + 1]) {
                    #print "switching to next chunk\n";
                    $self->{current_chunk}++;
                    ($start_offset, $start_file, $end_offset, $end_file) = split/ : | TO /, $chunks[$self->{current_chunk}];
                    $self->{current_file}   = $start_file;
                    $self->{current_offset} = $start_offset;
                    $IFH = ${$self->{file_handles}}[$start_file - 1];
                    #print "$IFH\n";
                    $success = (sysseek $IFH, $self->{current_offset}, 0);
                    #print "After Switch: $self->{current_file}, $self->{current_offset}, $end_file, $end_offset\n";
                } else {
                    $self->{EOF} = 1;
                }
            }
        }
        else { 
            my $rbytes = sysread $IFH, $rbuffer, $bytes_to_read, 0;
            $bytes_to_read  -= $rbytes;
            $self->{current_offset} += $rbytes;
            $buffer         .= $rbuffer;
            #print "in the current file could read " . length($buffer) . " bytes\n";
            if ($bytes_to_read) {
                #print "That was not enough, switching to next file\n";
                $IFH = ${$self->{file_handles}}[$self->{current_file}];
                $self->{current_file}++;
                #print "$IFH\n";
                $success = (sysseek $IFH, 0, 0);
                $self->{current_offset} = 0;
                #print "After Switch: $start_file, $start_offset, $end_file, $end_offset\n";
            } 
        }
            
    }
		
    #print "in VDRrecording read " . length($buffer) . " until now\n";
    $self->{bytes_written} += length($buffer);
    #print TOF $buffer;
    return $buffer;
    #close TOF;
    #die "finished all?\n";
}



sub frames2mark {
	my $self = shift;
	my $now = shift;
	if (! $now) {
		$now = $self->{current_frame};
	}
	#print "now is $now\n";
	my $rest = $now  % 25;
	my $full_secs = int ($now  * 0.04);
	
	my $h = int($full_secs/3600);
	my $min = int(($full_secs - (3600 * $h)) / 60);
	my $sec = $full_secs - (3600 * $h + 60 * $min);
	return sprintf "%1u:%02u:%02u.%02u", $h, $min, $sec, $rest;
};

sub mark2frames{
	my $self = shift;
	my $mark = shift;
	my($h, $m, $s, $f) = split /[:.]/, $mark;
	my $frame = (3600 * $h + 60 * $m + $s) * 25 + $f ;
	return $frame;
};

sub compute_movie_cuts {
	my $self = shift;
	
	my @chunks;
	my @marks                  = @{$self->{marks}};
	my @file_sizes             = @{$self->{file_sizes}};
    my $total_frames_cutted    = 0;
    $self->{movie_size_cutted} = 0;
    
    
    if (scalar(@marks) < 2) {
        print "no cutting without marks...\n";
        return -1;
    }
    for (my $i = 0; $i < (scalar(@marks)); $i += 2) {
        
        if ($marks[$i+1]) {
            my $startframe = mark2frames($self, $marks[$i]);
            my $startpos   = $self->{Iframes}{$startframe};
            my $endframe   = mark2frames($self, $marks[$i + 1]);
            my $endpos     = $self->{Iframes}{$endframe};
            if ((! $startpos) || (! $endpos)) {
                print "Mark does not mark an Iframe, cutting stopped!\n";
                return -1;
            }
            $total_frames_cutted += $endframe - $startframe;
            push @chunks, "$startpos TO $endpos";
            
        }
	}
		
	@{$self->{cut_chunks}} = @chunks;
	foreach (@chunks) {
		my ($start_offset, $start_file, $end_offset, $end_file) = split/ : | TO /, $_;
        #print "A chunk: $_\n";
        if ($start_file == $end_file) {
            $self->{movie_size_cutted} += $end_offset - $start_offset;
        } else {
            while ($start_file < $end_file) {
                $self->{movie_size_cutted} += $self->{file_sizes}[$start_file -1] - $start_offset;
                $start_offset = 0;
                $start_file++;
            }
            
            $self->{movie_size_cutted} += $end_offset;
            
        }
        
	}
    $self->{movie_length_cutted} = HMS2sec($self, frames2mark($self, $total_frames_cutted));
    
}

sub init_recording {
	my $self = shift;
	my $indir = shift;
	if (! $indir) {
		print "Did not receive a dir!\n";
		return;
	}
	$self->{indir} = $indir;
    
	foreach (@{$self->{file_handles}}) {close $_};
	@{$self->{file_handles}} = ();
	@{$self->{files}}        = ();
	@{$self->{marks}}        = ();
	@{$self->{file_sizes}}   = ();
	@{$self->{cut_chunks}}   = ();
	$self->{total_input}     = 0;
	$self->{EOF}             = 0;
	$self->{filepos}         = 0;
	$self->{total_frames}    = 0;
	$self->{current_chunk}   = 0;
	$self->{current_offset}  = 0;
    $self->{current_file}    = 0;
    $self->{bytes_written}   = 0;
    
	# check the files in the Dir 
	get_file_list($self);
	# THIS SCRIPT NEEDS A VALID index.vdr
	read_index_file($self);
	# If an exisiting marks file is found, it read
	read_marks_file($self);
    
    compute_movie_cuts($self);
	
	$self->{frameno} = 1;
	$self->{movie_length} = HMS2sec($self, frames2mark($self, $self->{total_frames}));
	$self->{current_pos} = frames2mark($self, $self->{frame_pos});
	$self->{mark_no} = scalar(@{$self->{marks}});
};

sub HMS2sec {
    my $self = shift;
    my $HMS = shift;
      
    my ($h, $m, $s, $f) = split /:|\./, $HMS;
    return ($h * 3600 + $m * 60 + $s + $f * 0.04);
}

}

