#!/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);
use File::Spec::Unix qw(re2abs);
use File::Path;

#require "/home/peter/.vdrsync/VDRRECORDING.pm";
my $version           = "0.1.3PRE1-050322"; # dev 4
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           => "",
    mpg123            => "",
    mp2enc            => "",
    "dvd-menu.pl"     => "",
    "vdrsync-buffer.pl"=>"",
    "vdrsync.pl"      =>"",
);




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

my @ignore_list;
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
my $vdrconvert_mode   = 0;
my $fix_audio         = 0;                  # If the Audio format changes, vdrsync tries to open a new file
                                            # If this parameter is set, it tries to install a filter chain (mpg123 and mp2enc)
############################################################################
# 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
my $tmp_dir           = "/tmp";             # default path for a temp dir (must exist!)
my $tmp_path          = "";                 # the actual tmp dir that will be created
############################################################################
# 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 $use_new_pipe      = 0;                  # same as above, but using a c-implementation of the buffering program
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 $dont_delete       = 0;

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;
my $save_chapter_shots= 0;                  # Only do chapter screen shots an then exit
my $dvd_chapter_menus = 0;                  # should chapter menus be created?
my $chapter_menu_length = 3;                # length of the animation in chapter menus (in secs)
my @dvd_chapters;
my $chapter_string    = "";                 # String as passed to dvdauthor to define dvd chapters
############################################################################


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

$| = 1;

my $debug_file = ">./DEBUG.LOG";
#open DEBUGFH, ">./DEBUG.LOG" or die "Can not open Debug Log\n";

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

SearchPRGs();
parse_parameters();
validate_params();if ($info) {
    compute_info();
    exit;
}



$tmp_path = $tmp_dir . "/" . $main_pid . time() . "/"; 
mkpath($tmp_path) or  die "Could not create tmp dir: $!\n";



if ($save_chapter_shots) {
    foreach(@dir_list) {
        my $ret_dir = save_chapter_shots($_);
        print  "Saved shots in $ret_dir\n";
    }
    rmdir $tmp_path;
    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 $cmd = $progs{"vdrsync.pl"};
        foreach (@dir_list) {
            $cmd .= " \"$_\" ";
        }
        $cmd .= "  -i -script-output ";
        #my $recs = join("  ", @dir_list);
        my $ign_parm = join "+", @ignore_list;
        if ($ign_parm) {
            $cmd .= " -ignore $ign_parm";
        }
        if ($cut_recording) {
            $cmd .= " -cut";
        }
        print "Runing $cmd\n";
        my @results = `$cmd`;
        my $all = join(" ", @results);
       
        print "Runing $cmd\n resulted in $all\n";
        
        (my $requant_factor) = $all =~ /Requant_Factor=(\S+)/;
        if ((!$all) || (!$requant_factor)) {
            if (! $all) {
                die "Failed to do an Info run with:\n$cmd \n\nMaybe vdrsync.pl is not in your PATH?\n";
            } else {
                die "Failed to extract the requant factor from:\n$all\n\nMaybe you have an old vdrsync.pl version in your Path.";
            }
        }
        print ("Determined a requant Faktor of $requant_factor\n");
        
        if ($requant_factor > 1) {
            $requant_factor = (int($requant_factor * 100 + 1))/100;
            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) {
        
        prepare_filters();
    }
    
    my @fifo_list;    
    
    foreach (@dir_list) {
        
        %ignore_hash = ();
        my $ignore_parm = shift(@ignore_list);
        
        if ($ignore_parm) {
            foreach (split/,/,$ignore_parm) {
                print "ignoring Stream $_\n" if $debug;
                $ignore_hash{$_} = 1;
            }
        }
     
        #if ($use_pipe) {
            my $timer = 0;
            dprint("Checking lock 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, then starting $_\n";
                }
                if ($timer > 300) {                 
                    die "Something went very wrong\n";
                }
            }
            dprint ("lock 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 ("lock file /tmp/vdrsync_lock_$main_pid created\n");
        #}
        if ($use_pipe) {
            @fifo_list = @{ prepare_fifos($_) };
            prepare_forks(@fifo_list);
        }
        my $PES_Obj = create_PS_Stream_Object($_);
        if (!$PES_Obj) {
            eprint("could not create the PES Stream Obj");
        }
        dprint("\nCreated PES Object for for $_\n");
        $PES_Obj->process_PES_packets();
        dprint ("\nAfter Processing, about to jump to postprocess\n");
        post_process($PES_Obj, \@fifo_list);
        $PES_Obj = "";
        print("\nFinished processing $_ \n");
        $current_title_set++; 
        if (! $use_pipe) {
            unlink("/tmp/vdrsync_lock_$main_pid");
        }
    }
   
    my $timer = 0;
    dprint ("\nObserving lock 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 ("lock file /tmp/vdrsync_lock_$main_pid gone\n");
    if (($use_pipe) && ($master_dvd)) {
        print "Need to finalize the DVD image....\n";
        finalize_dvd_image();
    } else {
        print "No need to finish DVD Image, since there is none\n";
    }    
    
    #sleep 1;
    print "Last fork ended for vdrsync.pl $main_pid\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));
    
    my $command = sprintf("rm -r \"%s%s\"", $path_param, $basename);
    dprint("Deleting DVD Files with: $command\n");
    execute $command;
}



if ($postexec) {
    print "Executing: $postexec\n";
    exec " $postexec ";
}

if (! $dont_delete) {
    rmdir $tmp_path;
}
exit;

 
##
# Helpers
# #

sub prepare_filters {
    #print "$all";
    #$audio_filter = " ./thread.pl.old audio%d_fifo ";
    if ($use_new_pipe) {
        if ($audio_filter) {
            $audio_filter = " vdrsync_buffer alog_new 2>/dev/null | " . $audio_filter  . " > /tmp/audio%d_fifo$main_pid "; #2>/dev/null
        } else {
            $audio_filter = " vdrsync_buffer alog_new > /tmp/audio%d_fifo$main_pid  "; #2>/dev/null
        }
    
        if ($video_filter) {
            $video_filter = " vdrsync_buffer vlog_new  2>/dev/null | " . $video_filter  . " > /tmp/video_fifo$main_pid "; #2>/dev/null
        } else {
            $video_filter = " vdrsync_buffer vlog_new  > /tmp/video_fifo$main_pid "; #2>/dev/null
        }
    
        if ($ac3_filter) {
            $ac3_filter = " vdrsync_buffer ac3log_new 2>/dev/null | " . $ac3_filter . " > /tmp/ac3_fifo$main_pid "; # 2> /dev/null 
        } else {
            $ac3_filter = " vdrsync_buffer ac3log_new > /tmp/ac3_fifo$main_pid 2>/dev/null "; #2>/dev/null
        }
    } else {
            if ($audio_filter) {
            $audio_filter = " vdrsync_buffer.pl STDOUT 2>/dev/null | " . $audio_filter  . " > /tmp/audio%d_fifo$main_pid "; #2>/dev/null
        } else {
            $audio_filter = " vdrsync_buffer.pl /tmp/audio%d_fifo$main_pid 2>/dev/null "; #2>/dev/null
        }
    
        if ($video_filter) {
            $video_filter = " vdrsync_buffer.pl STDOUT  2>/dev/null | " . $video_filter  . " > /tmp/video_fifo$main_pid "; #2>/dev/null
        } else {
            $video_filter = " vdrsync_buffer.pl /tmp/video_fifo$main_pid 2>/dev/null"; #2>/dev/null
        }
    
        if ($ac3_filter) {
            $ac3_filter = " vdrsync_buffer.pl STDOUT 2>/dev/null | " . $ac3_filter . " > /tmp/ac3_fifo$main_pid "; # 2> /dev/null 
        } else {
            $ac3_filter = " vdrsync_buffer.pl /tmp/ac3_fifo$main_pid 2>/dev/null "; #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"} ." \"$input_dir\" -i -script-output";
    my @results = `$cmd`;
 
    #$PES_Info_Obj = "";
    
    my $all = join "", @results;
    #print "All is now:\n$all";
        
    if (-e "/tmp/video_fifo$main_pid") {
        system "rm /tmp/video_fifo$main_pid";
        dprint ("deleting video_fifo$main_pid\n");
    }
    my $result = execute ("mkfifo /tmp/video_fifo$main_pid");
    dprint ("Tried to create video fifo with result $result\n");
    push @fifo_list, "/tmp/video_fifo$main_pid";
    
    
    my $stream_id = "bd_Audio_stream=yes";
    if (($all =~ /$stream_id/) && (! $ignore_hash{bd})) {
        dprint("Need to create a fifo for stream $stream_id\n");
        if (-e "/tmp/ac3_fifo$main_pid") {
            system "rm /tmp/ac3_fifo$main_pid";
            dprint ("deleting ac3_fifo$main_pid\n");
        }
        $result = execute ("mkfifo /tmp/ac3_fifo$main_pid");
        dprint ("Tried to create fifo with result $result\n");
        if ($result) {die "FIFO Creation failed\n";}
        push @fifo_list, "/tmp/ac3_fifo$main_pid";
    }
    
    for (my $i = 0; $i < 7; $i++) {
        $stream_id = "c$i" . "_Audio_stream=yes";
        my $id = "c$i";
        if ($ignore_hash{$id}) {
            next;
        }
        #print "Testing for stream $stream_id...\n";
        if ($all =~ /$stream_id/ ) {
            if (-e ("/tmp/audio$i" . "_fifo$main_pid")) {
            #if (-e ("./audio" . "_fifo")) {
                system ("rm /tmp/audio$i" . "_fifo$main_pid");
      #          print "deleting ./audio$i" . "_fifo\n";
            }
      #      print "Need to create a fifo for stream $stream_id\n";
            $result = execute ("mkfifo /tmp/audio$i", "_fifo$main_pid");
      #      print "Tried to create fifo with result $result\n";
            push @fifo_list,  "/tmp/audio$i". "_fifo$main_pid";
            
        }
    }
    foreach (@fifo_list) {
        dprint ("$_ is on the fifo list\n");
    }
    ($asp_ratio) = $all =~/Aspect_ratio=(.+)/;
    
    ($movie_length) = $all =~/e.{1}_Total_time=(.+)/;
    if ((! $asp_ratio) || (! $movie_length)) {
        die "Could not extract movie length or aspect ratio from:\n$all\n";
    }
    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$main_pid";
        if (-p "/tmp/remuxfifo$main_pid") {
            dprint ("remuxfifo does exist\n");
        } else {
            my $result = execute ("mkfifo  /tmp/remuxfifo$main_pid");
            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);
                dprint ("deleting lockfile after dvdauthor finished\n");
                unlink("/tmp/vdrsync_lock_$main_pid");
                exit;
            }
        }
        return;
    }
    else {
        print ("Setting up mutliplex process for $main_pid in $$\n");
        multiplex($target, \@fifo_list);
        if (! $master_dvd) {
            dprint ("deleting lockfile after mplex finished\n");
            unlink("/tmp/vdrsync_lock_$main_pid");
        }
        exit;
    }
}

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


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

    dprint ("arrived at multiplex with pid $$\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);
    } 
    dprint ("Leaving multiplex with pid $$\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 $out_path = $tmp_path;
    if ((!$mplex) || (! $use_pipe)) {
        $out_path = $path_param; 
    } 
    my $PES_Object = MPEGSTREAM->new(
      streamcode     =>"PES_stream",
      debug          => $debug,
      outputpath     => $out_path,
      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,
      vdrconvert_mode=> $vdrconvert_mode,
      fix_audio      => $fix_audio,
      progs          => \%progs,
    );
    return $PES_Object;
}


sub compute_info {
    my $counter = 0;
    my $temp_dir = "/tmp/";
    my $total_video = 0;
    my $total_audio = 0;
    (my @ig_list) = (@ignore_list);
    my %ig_hash;
    
    print "list " . (join " ", @ig_list) . "\n";
    
    
    foreach my $dir (@dir_list) {
        
        
        %ig_hash = ();
        
        print "Processing $dir for info\n";
         
        my $ignore_parm = shift(@ig_list);
        
        if ($ignore_parm) {
            foreach (split/,/,$ignore_parm) {
                print "ignoring Stream $_\n" if $debug;
                $ig_hash{$_} = 1;
            }
        }
        
        
        
        
        @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() };
        my $current_stream;
        foreach (@results) {
            #print "FROM RESULTS: $_";
            if ($_ =~/_stream_(.{2})=yes/) {
                $current_stream = $1;
                print "setting stream to $current_stream\n";
            }
        
            if ($_  =~ /Video_Total_bytes=(\d+)/) {
                $total_video += $1;
            }
            if ($_  =~ /Audio_Total_bytes=(\d+)/) {

                if ($ig_hash{$current_stream}) {
                    print "Skipping stream $current_stream in calculation\n";
                }
                else {
                    print "adding bytes for stream $current_stream to total audio\n";
                    $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;
    if ($chapter_string) {
        return $chapter_string
    }
    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...using $remuxfifo";
    
    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 $chapter_shot_dir = "";
        if ($dvd_chapter_menus) {
            $chapter_shot_dir = save_chapter_shots($dir_list[$current_title_set]);
        }
        if (! $chapter_string) {
            $chapter_string = dvdauthor_length();
        } else {
            dprint( "already have a chapter string: $chapter_string\n");
        }
        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,
          $chapter_string,
          );
          
        if ($dvd_bg_images[$current_title_set]) {
            my $bg_image = $dvd_bg_images[$current_title_set];
            $cmd .= ",background=\"$bg_image\"";
        }
        if ($chapter_shot_dir) {
            
            $cmd .= ",chapter_dir=\"$chapter_shot_dir\"";
        }
        
        $cmd .= sprintf(" -run-dvdauthor -o \"%s/%s\" -font-color white -enhance-text-area 20 --tmp-dir \"%s\" ",
            $path_param, 
            $basename,
            $tmp_path,
            );
            dprint("Executing: $cmd\n\n");
            #die "Now command is $cmd\n";
        if (execute($cmd)) {
            die "Error while running $cmd\n";
        }
        
    } 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,
           )
        );
    }
    print "Finished Add_to_DVD_Image\n";
    #finalize_dvd_image();
}


sub finalize_dvd_image {
    if ($dvd_menu) {
        my $button_no = scalar(@dir_list);
        my @main_menu_items = split /\|/, $dvd_root_text;
        
        if (scalar(@main_menu_items) > $button_no) {
            $button_no = scalar(@main_menu_items);
        }
        my $cmd = sprintf("%s -root title=\"%s\",text=\"%s\",buttons=%s",
              $progs{"dvd-menu.pl"},
              $dvd_root_title,
              $dvd_root_text,
              $button_no,
            );
          
        if ($dvd_root_image) {
            print "extracted bg image name: $dvd_root_image\n";
            $cmd .= ",background=\"$dvd_root_image\"";
        }         
        $cmd .= sprintf( " -run-dvdauthor -o \"%s/%s\" -font-color white -enhance-text-area 20 --tmp-dir \"%s\" ",
          $path_param, 
          $basename,
          $tmp_path,
          );
        print("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$main_pid") {
        print "remuxfifo$main_pid does exist\n";
    } else {
        my $result = execute "mkfifo  /tmp/remuxfifo$main_pid";
        dprint "the attempt to create FIFO returnd $result\n";
    }
    
    
    if ($pid = fork) {
        #nprint "Creating DVD structure using dvdauthor...";
        add_to_dvd_image("/tmp/remuxfifo$main_pid");
        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$main_pid", \@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;
        print "finished mplexing\n";
        die "The multiplex fork has finished\n";
        print "I got never as far as this\n";
    }
    
    
}

sub clean_up {
    
    my @filestomux = @_;
    if ($dont_delete) {
        print "No clean up because the --dont-delete Option has been given.\n";
        return;
    }
    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");
    }
}

sub save_chapter_shots {
    
    my $directory = shift;
    if (! $directory) {die "Can not create shots from nothing, directory parameter missing!\n"}
    print "tmp_path is now $tmp_path\n";
    my $temp_dir = $tmp_path . "/chapter_shots" . time() . $$. "/" ;
    print "want to create $temp_dir\n";
    mkpath("$temp_dir") or die "Can not create $temp_dir: $!\n";
    my $rec = VDRRECORDING->new(
            chapter_marks => \@dvd_chapters,
        );
    $rec->init_recording($directory);
    
    if (scalar(@{ $rec->{chapter_marks} }) == 0) {
        $rec->generate_chapters($chapter, $cut_recording);
    } else {
        @{ $rec->{chapter_marks} } = $rec->verify_chapter_marks();
    }
    
    foreach (@{ $rec->{chapter_marks} } ) {
        dprint("Generated chapter mark: $_\n");
    }
    $rec->get_chapter_shots($temp_dir, $chapter_menu_length, "jpeg2yuv");
    my @correct_chapter_marks;
    if ($cut_recording) {
        @correct_chapter_marks = $rec->get_cut_chapter_marks();
    } else {
        foreach (@{ $rec->{chapter_marks} }) {
        push @correct_chapter_marks, ($rec->HMS2sec($_) * 25);
        }
    }
    $chapter_string = "";
    foreach (@correct_chapter_marks) {
        dprint("Corrected is $_ " . ($_* 0.04) ." in secs\n");
        #print "In hms: " . $rec->frames2HMS($_) ."\n";
        $chapter_string .= $rec->frames2HMS($_) . ",";
    }
    chop $chapter_string;
    #    die "Final string: $chapter_string\n";
    return $temp_dir;
}

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(":", $ENV{"PATH"}));
    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 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");
            $param = File::Spec::Unix->rel2abs( $param );
          
            if ($param !~ /\/$/) {
                $param .="/";
            }
            push @dir_list, $param;
            next;
        }
        if (-f $param) {
            push @files, $param;
        } else {
            die "Could not find file $param\n";
        }
    }
    
    $path_param = File::Spec::Unix->rel2abs($path_param);
    if (!($path_param =~ /\/$/)) {
        $path_param .= "/";
    }
    
    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 =~ /^-{1,2}buffer-only$/) {

        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;
        
    }
    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 =~ /^-{1,2}mpeg2$/) {
        $mpeg2 = 1;
        $master_dvd = 0;
        $mplex = 1;
        $mkiso = 0;
    }
    elsif ($parameter eq "-i")    {
        $info    = 1;
    }
    elsif ($parameter =~ /^-{1,2}use-pipe$/)    {
        $use_pipe  = 1;
    }
    elsif ($parameter =~ /^-{1,2}use-new-pipe$/)    {
        $use_new_pipe  = 1;
        $use_pipe = 1;
    }
    elsif ($parameter =~ /^-{1,2}stdin$/)    {
        $stdin   = 1;
    }
    elsif ($parameter =~ /^-{1,2}ignore$/)    {
        if (@parameter_list) {
            @ignore_list = split /\+/,(shift @parameter_list);
        }
    }
    elsif ($parameter =~ /^-{1,2}dump-packets$/){
        $dump_packets = (shift @parameter_list);
    }
    elsif ($parameter =~ /^-{1,2}show-time-drift$/){
        $show_drift = 1;
    }
    elsif (($parameter =~ /^-{1,2}cut-recording$/) || ($parameter =~ /^-{1,2}cut$/)){
        $cut_recording = 1;
    }
    elsif ($parameter =~ /^-{1,2}dump-debug-buffer$/) {
        $dump_buffer = 1;
    }
    elsif ($parameter =~ /^-{1,2}dump-payload$/) {
        $dump_payload = (shift @parameter_list);
    }
    elsif ($parameter =~ /^-{1,2}audio-only$/) {
        $audio_only = 1;
    }
    elsif ($parameter =~ /^-{1,2}script-output/) {
        $script_output = 1;
        if ($parameter =~ /^-{1,2}script-output=/) {
            
            $script_output_file = (split /=/,$parameter)[1];
        }
    }
    elsif ($parameter eq "-v" || $parameter =~ /^-{1,2}verbose$/ )    {
        $verbose = 1;
        $quiet   = 0;
        
    }
    elsif ($parameter =~ /^-{1,2}divx$/) {
        $divx = 1;
        if ($parameter_list[0] eq "ac3"){
            $divxac3 = (shift @parameter_list);
        }
    }
    elsif ($parameter =~ /^-{1,2}master-dvd$/){
        $master_dvd = 1;
        $mplex = 1;
        if ( ! $parameter_list[0] =~/^-/ ){
            $master_dvd_param = (shift @parameter_list);
        }
    }
    elsif ($parameter =~ /^-{1,2}mkiso$/){
        $mkiso = 1;
        $mplex = 1;
        $master_dvd = 1;
    }
    elsif ($parameter =~ /^-{1,2}chapter$/) {
        $chapter = shift(@parameter_list);
        print "got chapter parameter\n";
        if ((-f $chapter) && (-s $chapter)) {
            print "chapter parameter is a file\n";
            open CFH, $chapter || die "Could not open chapter file $chapter: $!\n";
            my @raw_chapter_marks = <CFH>;
            close CFH;
            foreach my $raw_chap (@raw_chapter_marks) {
                print "testing $raw_chap\n";
                chomp $raw_chap;
                if ($raw_chap =~ /^\s+$/) {
                    next;
                }
                if ($raw_chap =~ /\d{1,2}\:\d{1,2}\:\d{1,2}\.\d{1,2}/) {
                    push @dvd_chapters, $raw_chap;
            
                } else {
                    die "found not recognized mark in your chapter file: $raw_chap\n";
                }
            }
            $chapter_string = join ",",@dvd_chapters;
            print "$chapter_string is final\n";
            $chapter = 300;
        } elsif (($chapter !~ /\d+/) ||
          ($chapter % 60)) {
            die "Chapter Intervalls have to be specified in seconds, and must be multiple of 60\n";
        }
    }
    elsif ($parameter =~ /^-{1,2}vdrconvert$/){
        $vdrconvert_mode = 1;
        $debug           = 1;
        $quiet           = 0;
        $verbose         = 1;
    }
    elsif ($parameter =~ /^-{1,2}postexec$/){
        $postexec  = (shift @parameter_list);
    }
   # elsif ($parameter eq "-isoname"){
   #     $isoname = shift(@parameter_list);
   # } 
    elsif ($parameter =~ /^-{1,2}help$/ || $parameter eq "-h" || $parameter eq "-?") {
        usage();
    }
    elsif ($parameter =~ /^-{1,2}basename$/) {
        $basename = shift(@parameter_list);
    }
    elsif ($parameter =~ /^-{1,2}a-filter$/) {
        $audio_filter = shift(@parameter_list);
    }
    elsif ($parameter =~ /^-{1,2}v-filter$/) {
        $video_filter = shift(@parameter_list);
    }
    elsif ($parameter =~ /^-{1,2}ac3-filter$/) {
        $ac3_filter = shift(@parameter_list);
    }
    elsif ($parameter =~ /^-{1,2}dvd-menu$/) {
        $dvd_menu = 1;
        $mplex = 1;
        $master_dvd = 1;
    }
    elsif ($parameter =~ /^-{1,2}dvd-menu-images$/) {
        @dvd_bg_images = split /(?=[^\\]),/, shift @parameter_list;
    }
    elsif ($parameter =~ /^-{1,2}dvd-menu-titles$/) {
        @dvd_titles = @{ split_dvd_menu_lists(shift @parameter_list)  };
    }
    elsif ($parameter =~ /^-{1,2}dvd-root-title$/) {
        $dvd_root_title = shift @parameter_list;
    }
    elsif ($parameter =~ /^-{1,2}dvd-root-labels$/) {
        $dvd_root_text = shift @parameter_list;
    }
    elsif ($parameter =~ /^-{1,2}dvd-root-image$/) {
        $dvd_root_image = shift @parameter_list;
        if (! -f $dvd_root_image)  {
            die "$dvd_root_image does not exist!\n";
        }
    }
    elsif ($parameter =~ /^-{1,2}dvd-menu-texts$/) {
        @dvd_texts = @{ split_dvd_menu_lists(shift @parameter_list)  };
    } 
    elsif ($parameter =~ /^-{1,2}fit-to-dvd$/) {
        $fit_to_dvd = 1;
    }
    elsif ($parameter =~ /^-{1,2}fix-audio$/) {
        $fix_audio = 1;
    }
    elsif ($parameter =~ /^-{1,2}dvd-chapter-menus$/) {
        $dvd_chapter_menus = 1;
        $master_dvd = 1;
        $mplex = 1;
        $dvd_menu = 1;
    }
    elsif ($parameter =~ /^-{1,2}dvd-chapter-menu-length$/) {
        if ($parameter_list[0] && ($parameter_list[0] =~/^\d+$/)) {
            $chapter_menu_length = shift @parameter_list;
        } else {
            die "You have to specify a duration in secondes when using\n--dvd-chapter-menu-length\n";
        }
    }
    elsif ($parameter =~ /^-{1,2}save-chapter-shots$/) {
        $save_chapter_shots = 1;
    }
    elsif ($parameter =~ /^-{1,2}tmp-dir$/) {
        if ( -d $parameter_list[0]) {
            $tmp_dir =  shift @parameter_list;
        } else {
            die "The temp dir that was specified on the command line is not existing\n";
        }
    }
    elsif ($parameter =~ /^-{1,2}dont-delete$/) {
        $dont_delete = 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_list)) ." 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";
    }
    if ($vdrconvert_mode && ($mplex || $master_dvd || $mkiso || $dvd_menu || $use_pipe)) {
        die "When running in vdrconvert mode all postprocessing options must not be used.\n";   
    }
    if ($fit_to_dvd && (!$progs{"vdrsync.pl"})) {
        die "If -fit-to-dvd is specified, a current Version fo VDRsync must be in your path\n";
    }
    if ($use_pipe && (!$progs{"vdrsync.pl"})) {
        die "If -use-pipe is specified, a current Version fo VDRsync must be in your path\n";
    }
    #~ if ($cut_recording && @dvd_chapters) {
        #~ print "ARghhh, I have to correct the DVD chapter to something sensible AFTER cutting...\n";
        #~ comp_dvd_chapters_after_cut();
    #~ }
    
}

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!";
            } else {
                $dvd_titles[$i] =~ s/_/ /gm;  
            }
        }
        if (! $dvd_texts[$i]) {
            if ($dir_list[$i]) {  # else we have Input from STDIN
                if ( -f "$dir_list[$i]/summary.vdr") {
                    $dvd_texts[$i] = File::Spec::Unix->rel2abs("$dir_list[$i]/summary.vdr");
                }
            }
            if(!$dvd_texts[$i])  {
                $dvd_texts[$i] = "No Text available!";
            }
        } 
        if (! $dvd_bg_images[$i])  {
            if (($dir_list[$i]) && (-f "$dir_list[$i]/bg.jpg")) {
                $dvd_bg_images[$i] = File::Spec::Unix->rel2abs("$dir_list[$i]/bg.jpg");
                print "bg image setting is now $dvd_bg_images[$i]\n";
            }
        } else {
            $dvd_bg_images[$i] = File::Spec::Unix->rel2abs($dvd_bg_images[$i]);
        }
        $dummy_root_text .= "$dvd_titles[$i]|";
        
        
        if (! $dvd_root_image)  {
            if ( -f "$dir_list[$i]/main.jpg") {
                $dvd_root_image = File::Spec::Unix->rel2abs("$dir_list[$i]/main.jpg");
            }
        } else {
            if (! -f $dvd_root_image) {
                die "You specified an image to be used for the DVD main menu ($dvd_root_image),\n but it could not be found.\n";
            }
            $dvd_root_image = File::Spec::Unix->rel2abs($dvd_root_image);
        }
    }
    
    $dvd_root_title = $dvd_root_title || "Menu created by VDRsync";
    $dvd_root_text  = $dvd_root_text  || $dummy_root_text;
    
    
}

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-chapter-menus
            Tells VDRsync to crate animated scene selection menus
            when producing a DVD
        
        -dvd-chapter-menu-length  NN  
            Specifys the length of the animated chapter menu in seconds 
            (default=3)
            
        -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
        
        -ignore stream1[,stream2]...  
            tells the script to ignore  stream1..n. Useful for 
            deliberately omitting streams (like AC3) or if you have 
            trouble with one stream. NOTE: If you supply multiple
            recording directories on the command line, you have to specify
            the streams FOR EACH RECORDING! You can do that by supplying 
            a "+" separated list, for example:
            -ignore c0+c1,bd+bd
            would ignore the audio stream c0 in the first recording, c1 and 
            bd in the second recording and bd in the third recording
        
        -m 
            tries to multiplex the demuxed streams into a dvd 
            compatible stream (uses mplex from the mjpegtools)
        
        -m tcmplex
            uses tcmplex instead of mplex
        
        -m panteltje 
            uses tcmplex-panteltje instead of mplex
        
        -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)
        
        -save-chapter-shots        
            This is mainly for extraction of stills from the movie, 
            according to a chapters_maerks.vdr file, or to auto-
            generated chapters. Internally this function is used
            to created animated scene selection menus
                
        -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-chapter-menus                                           \\
        -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 $current_last_PTS = 0;
our $current_total    = 0;
our $need_fix_run     = 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; #dev3 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
    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 strange 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
    total_length           => 0,        # total of all frames written to disk in ticks (1/90000 sec)
    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
    kill_score             => 0,        # This var is increased with every serious flush problem, and decreased after every successfull flush
    cuts_in_buffer         => {},       # This is the list of cuts in the current video Buffer (6 GOPs)
    cuts_processed         => {},       # A hash that keeps track of the cuts that have been processed
    last_GOP_checked       => 0,        # This is the last GOP that was checked (for a cut)
    ############################### # Input 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,
    ############################### # ???
    packet_buffer          => {},       # Holds payloads of every stream for later analysis
    
    ###############################
    
    output_buffer          => {},       # Holds payloads of every stream before it is processed and flushed
    last_flush_GOP         => 0,
    last_PTS               => 0,        # last timestamp that was written to the disk, or better the last PTS attached to data written to the disk
    GOP_pts                => {},       # Holds the starting PTS  for every GOP, key is GOPno
    GOP_pics               => {},       # Holds the number of pics  for every GOP, key is GOPno
    cutflag                => 0,
    testbuffer             => "",
    ###############################
    vdrconvert_mode        => 0,        # If this is set to 1, filenames will be choosen like e0.mpv and c1.mpa and -v and -d will be active
    audio_change           => 0,        # This is set to 1 if there is a format change i Audio that needs a conversion and a filter was installed
    
    buffer_rest            => "",       # Since I want to write always 4096 Byte chunks out, I have to store the rest inbetween
    
    @_,                                 # Override previous attributes
    };
    
    @cutlist          = ();
    @kill_me          = ();
    #$total_size       = 0;
    $bytes_read       = 0;
    #$module_verbose   = 0;
    $current_last_PTS = 0;
    $current_total    = 0;
    $need_fix_run     = 0;
    
        
    
    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->{vdrconvert_mode}) {
            $self->{outfilename} = $self->{streamcode} . ".ac3";
            vprint("\nWant to open $self->{outfilename}\n");
            open $self->{outfile}, ">$self->{outfilename}"  or die "Can not open file for AC3: $!\n";
        }
        elsif ($self->{ac3_filter}) {
            #$self->{outfilename} = "$self->{outputpath}/$self->{basename}.ac3_audio";
            my $filterpipe = "  $self->{ac3_filter} ";
            $self->{outfilename} = "$self->{outputpath}/$self->{basename}.ac3";
            vprint("\nsetting outfilename to $self->{outfilename}\n");
            vprint("Want to open $filterpipe\n");
            
            if (! $self->{use_pipe}) {
                open $self->{outfile}, "|  $filterpipe > $self->{outfilename}"  or die "Can not open file for AC3 Audio: $!\n";
            } else {
                open $self->{outfile}, "|  $filterpipe " or die "Can not open file for AC3 Audio: $!\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 scan_for_format_change {
    my $self = shift;
    my $payload = shift;
    my %frame_info = %{ shift @_};
    
    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
            return 0;
        }
        

        elsif (!$self->{frames_written}) {    
            print "Flushing old buffer and starting new, since nothing has been written yet...\n";
            sleep 2;
            print " " . scalar(@{$self->{frame_times}}) ."\n";
            @{$self->{frame_times}}  = ();
            $self->{frame_times}[0] = 0;
            @{$self->{frame_buffer}} = ();
            @{$self->{frame_length}} = ();
            $self->{total_length} = 0;
            $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";
            print "Flushing old buffer and starting new, since nothing has been written yet...\n";
            return 0;
        }
        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 0;
        }
        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";
            
                     
            print "\nNew parameters:\n";
            print "\naudio stream $self->{streamcode} info ($self->{streamtype}):\n";
            print "Sample frequency:    $frame_info{freq}\n";            # only for Audio 
            print "Bitrate:             $frame_info{bitrate}\n";             # only for Audio
            print "Mode:                $frame_info{mode}\n";             # only for Audio
            print "Copyright:           $frame_info{copyright}\n" if  $self->{copyright};        # only for Audio
            print "Frame length (bytes) $frame_info{frame_bytes}\n";        # only for Audio
            print "Frame length (ticks) $frame_info{frame_ticks} (90000 / sec)\n\n\n";
            print "Change can not be ignored, returning -1\n";
            
            #push @kill_me, $self->{streamcode};
            return -1;
        }
}


sub sync_audio_end {
    my $self = shift;
    my $chunk_end = shift;
    my $video_frameno = shift;
    
    
    my $FH = $self->{outfile};
    my $counter = 0;
    
    my $flush_buffer = $self->{buffer_rest};
    
    print "\nsyncing end of chunk in stream $self->{streamcode}...\n" if $self->{debug};
    print "we have in buffer: ". scalar(@{$self->{frame_times}}) . " frames $self->{frame_ticks}\n" if $self->{debug};   
        
    my $video_time = $video_frameno * 3600;
        
    
    my $ticks_needed = $video_time - $self->{total_length};
    my $frames_needed = int(($ticks_needed / $self->{frame_ticks}) + 0.5); # I want to calculate the number of frames I need for catching up with video. There should be no more frames written than those.
    
    

    while ((${$self->{frame_times}}[0] < $chunk_end - ($self->{frame_ticks} * 0.5))
            && (@{$self->{frame_buffer}})
            && ($frames_needed)) { #+ ($self->{frame_ticks} * 0.5) - $self->{final_desync}
        
        if (--$frames_needed == 0) {
            print "Ok, we will stop flushing stream $self->{streamcode} since we already are equal with video\n" if $self->{debug};
            print "last PTS: $self->{last_PTS}\n";
        }
        $self->{last_PTS} = shift @{$self->{frame_times}};
        $self->{total_length} += shift @{$self->{frame_lengths}};;
        $flush_buffer .= shift @{$self->{frame_buffer}};
        $self->{frames_written}++;
        $counter++;
        if (scalar(@{$self->{frame_times}}) == 0) {
            if (! ($cutlist[$self->{cutcounter}+1])) {
                print "Assuming final sync for stream $self->{streamcode}, cause there is no new chunk_start\n";
                last;
            } else {
                print "End Sync failed for stream $self->{streamcode} for chunk $self->{cutcounter} while trying to write Frames until $chunk_end\n";
                print "time of last frame written was $self->{last_PTS}\n";
                $self->{kill_score} += 10;
                if ($self->{kill_score} > 100) {
                    push @kill_me, $self->{streamcode};
                }
                last;
            }
        }
    }
    print "last PTS written: $self->{last_PTS}\n" if $self->{debug};

    if ($flush_buffer) {
        my $rest = length($flush_buffer) % 4096;
        my $limit = length($flush_buffer) - $rest;
        print $FH substr($flush_buffer, 0, $limit);
        $self->{buffer_rest} = substr($flush_buffer, $limit);
    }
    
    print "AUDIO $self->{streamcode}: At sync end $self->{frames_written} frames, time $self->{last_PTS}\n" if $self->{debug};
    
    my $aud_time = $self->{frames_written} * $self->{frame_ticks};
    my $frame_diff = $video_time - $self->{total_length};
    my $alt_diff = ($video_time - $self->{total_length});
    if ($alt_diff != $frame_diff) {
        die "$alt_diff != $frame_diff in sync end $video_time, $aud_time, $current_total, $self->{total_length}\n"
    }
    print "For the end sync $self->{cutcounter} of $self->{streamcode} frames were printed up to $self->{last_PTS} to match $chunk_end of chunk $self->{cutcounter} leaving: $frame_diff)\n" if $self->{debug};
    my $ins_frames = insert_frames($self, $frame_diff);
    $self->{time_drift} = 0;
    $self->{written_chunks}[$self->{cutcounter}] .= ($self->{last_PTS} + $self->{frame_ticks} * ($ins_frames+1));
    
}

sub sync_audio_start {
    my $self = shift;
    my $chunk_start = shift;
    my $prev_chunk_end;
    
    
    if ($self->{audio_only}) {
        return;
    }
    if (! $chunk_start) {
        print "Hmmm, should sync audio start, but did not get a chunk start, delaying\n";
        $self->{kill_score} += 10;
        if ($self->{kill_score} > 100) {
            push @kill_me, $self->{streamcode};
        }
        return -1;
    }
    
    if (! ${$self->{frame_times}}[0]) {
        print "Some serious problem at Start Sync for stream $self->{streamcode} at cut $self->{cutcounter}\n \n";
        $self->{kill_score} += 10;
        if ($self->{kill_score} > 100) {
            push @kill_me, $self->{streamcode};
        }
        return  -1;
    }
    
    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;
    
    print "cutcounter is at $self->{cutcounter}\n" if $self->{debug};
    
    
    
    while ((${$self->{frame_times}}[0] < ($chunk_start  - $self->{frame_ticks} * 0.5))
          || ((${$self->{frame_times}}[0] - $chunk_start) > 4000000000) ){
        #||  ((${$self->{frame_times}}[0] - $chunk_start) > 9000000 )) { # For PTS overflows and "catted" movies
        
        print "\ndropping frame with time ${$self->{frame_times}}[0] cause ${$self->{frame_times}}[0] < $chunk_start\n" if $self->{debug};
        shift @{$self->{frame_times}};
        shift @{$self->{frame_buffer}};
        shift @{$self->{frame_lengths}};
        $counter++;
        if (! @{$self->{frame_times}}) {
            print "\nStart Sync failed for stream $self->{streamcode} while trying to drop Frames until $chunk_start\n";
            $self->{kill_score} += 10;
            if ($self->{kill_score} > 100) {
                push @kill_me, $self->{streamcode};
            }
            return -1;
        }
    }
    #print "next in buffer is ${$self->{frame_times}}[0]\n";
    my $diff =  ${$self->{frame_times}}[0] - $chunk_start;
    if ($diff > 90000) {
        $diff = $current_total - $self->{frames_written} * $self->{frame_times};
        print "WARNING!!! Something seems pretty wrong with stream $self->{streamcode}!\n\tTrying my best by setting diff to $diff (was: " . (${$self->{frame_times}}[0] - $chunk_start) .") \n";
    }
    print "Hopping to ins frames with diff $diff for stream $self->{streamcode}, last time was ${$self->{frame_times}}[0]\n" if $self->{debug};
    my $ins_frames = insert_frames($self, $diff);
    $diff += $ins_frames * $self->{frame_ticks} ;
    print "IN SYNC AUDIO START ($self->{streamcode}): diff is now: $diff and $ins_frames frames were inserted\n" if $self->{debug};
    #$self->{frames_written} += $ins_frames;
    #$diff += $ins_frames * $self->{frame_ticks};
    $self->{last_PTS} = ${$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 = $self->{buffer_rest};
    
    if ($diff < 0) {
        return 0;
    }
    
    #$diff += $self->{final_desync};
    my $FH = $self->{outfile};
    my $abs_diff = abs($diff);
    my $start_diff = $diff;
    
    my $counter = 0;
    if ($abs_diff > 900000) {
        
        print "more than 10 seconds auf Audio missing, killing stream $self->{streamcode}\n";
        push @kill_me, $self->{streamcode}; 
        return 0;
    }
    
    while ($diff > ($self->{frame_ticks} * 0.5 )) {
        $flush_buffer .= $self->{single_frame};
        
        $self->{frames_written}++;
        #print $FH $self->{single_frame};
        $diff -= $self->{frame_ticks};
        $counter++;
        $self->{total_length} += $self->{frame_ticks}; 
        if ($flush_buffer) {
            my $rest = length($flush_buffer) % 4096;
            my $limit = length($flush_buffer) - $rest;
            print $FH substr($flush_buffer, 0, $limit);
            $self->{buffer_rest} = substr($flush_buffer, $limit);
            $flush_buffer = $self->{buffer_rest};
        }
    }
    #~ if ($self->{use_pipe}) {
        #~ my $length = pack("V", (length($flush_buffer))) if ($flush_buffer);
        #~ print $FH $length       if ($flush_buffer);
    #~ }    
    #~ print $FH $flush_buffer if ($flush_buffer);
    
    print "INSERT FRAMES in stream $self->{streamcode}: wrote additional $counter frames to close $start_diff ticks, leaving $diff \n" if $self->{debug};
    $self->{final_desync} = $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"; 
        push @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->{vdrconvert_mode}) {
            $self->{outfilename} = $self->{streamcode} . ".mpv";
            vprint("\nWant to open $self->{outfilename}\n");
            open $self->{outfile}, ">$self->{outfilename}"  or die "Can not open file for mpv: $!\n";
        }
        elsif ($self->{video_filter}) {
            $self->{outfilename} = "$self->{outputpath}/$self->{basename}.mpv";
            my $filterpipe = "  $self->{video_filter} ";
                    
            vprint ("\nWant to open $filterpipe\n");
            if (! $self->{use_pipe}) {
                #print " opening |  $filterpipe > $self->{outfilename} \n" ;
                open $self->{outfile}, "|  $filterpipe > $self->{outfilename}"  or die "Can not open file for Video: $!\n";                     
            } else {
                open $self->{outfile}, "|  $filterpipe " or die "Can not open file for Video: $!\n";                     
            }
 
        }
        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"; 
            push @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 search_audio_sync {
        
    my $self = shift;
    my $payload = shift;
    my $new_offset = -1;
    my $full_audio_buffer = join "", @{ $self->{frame_buffer} };
    
    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($full_audio_buffer)) {
            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($full_audio_buffer,($potential_start + $frame_info{frame_bytes}),2)) ."\n" if $self->{debug};
        
            if ($frame_info{packet_start} eq substr($full_audio_buffer, ($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;
    print "Printing Stats\n" if $self->{debug};
    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} || $self->{vdrconvert_mode}) {    
            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};
    
    
    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);
    
    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};#/
    
    $self->{single_frame} = get_silent_frame($self, \%frame_info);
    if ($self->{single_frame}  eq "-1") {
        print "Failed to get single frame for $self->{streamtype} in stream $self->{streamcode}\n";
    } else {
        print "Got single frame for $self->{streamtype} in stream $self->{streamcode}\n" if $self->{debug};
    }
    
    
    if (! $self->{info}) {    
        if ($self->{vdrconvert_mode}) {
            $self->{outfilename} = "$self->{outputpath}/$self->{streamcode}" . ".mpa";
            vprint("\nWant to open $self->{outfilename}\n");
            open $self->{outfile}, ">$self->{outfilename}"  or die "Can not open file for mpv: $!\n";
        }
        elsif ($self->{audio_filter}) {
    
            if ($self->{audio_filter} =~ /%d/) {
                $self->{audio_filter} =~ s/%d/$streamnumber/;
            }
            $self->{outfilename} = "$self->{outputpath}/$self->{basename}$streamnumber.mpa";
            my $filterpipe = "  $self->{audio_filter} ";
            vprint("\nsetting outfilename to $self->{outfilename}\n");
            if (! $self->{use_pipe}) {
                #print " opening |  $filterpipe > $self->{outfilename} \n" ;
                open $self->{outfile}, "|  $filterpipe > $self->{outfilename}"  or die "Can not open file for MP2 Audio: $!\n";
            } else {
                open $self->{outfile}, "|  $filterpipe " or die "Can not open file for MP2 Audio: $!\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 %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}
    
    
    
    if ($bitrates{$streamtype}{substr($BITS,16,4)}) {
        $frame_info{bitrate} = $bitrates{$streamtype}{substr($BITS,16,4)} . "000"
    }
    else {
        print " $streamtype\n";
        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;
    } elsif ( $frame_info{freq} ne "48000") {
        print "ATTENTION: Found a potential AudioFrame start with a frequency not equal to 48 kHz.\n\tThis is ignored since I never observed another frequency in real recordings.\n\tIf you feel that this is an error, please mail to peter\@vdr-portal.de\n";
        return -1;
    }
    
    $frame_info{padding_bit} = substr($BITS,22,1);


    $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}; 
   
    $frame_info{streamtype} = $streamtype;
    
    $frame_info{copyright} = substr($BITS,15,1);
    
    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};

    #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 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 kill_streams {
    
    my $self = shift;
    my $kill_all = 0;
    if ($self->{dump_buffer}) {
        dump_debug_buffer($self);
        @kill_me = keys(% {$self->{streams} });
        $kill_all = 1;
    }
    
    print "got the kill list @kill_me\n";
    foreach (@kill_me) {
        if ($_ eq "all") {
            $kill_all = 1;
            next;
        }
        $self->{ignore_streams}{$_} = 1;
        my $FH = $self->{streams}{$_}->{outfile};
        close $FH;
        unlink $self->{streams}{$_}->{outfilename};
        print "Stream $_ was killed due to an error\n";
        delete $self->{streams}{$_};
    }
    if ($kill_all eq "all") {die "Script stopped due to an error\n";}
    @kill_me = ();
}


sub fix_run {
    my $self = shift;
   
    $need_fix_run = 0;
    foreach(keys(%{$self->{streams}})) {
        print "Gotta check $_\n";
        if ($_ eq $self->{masterstream}) {
            print "DON'T CHALLENGE THE MASTER ;-)\n";
            print "some sanity checks here...\n";
            my @gop_numbers = sort(keys(%{$self->{GOP_pts}}));
            my $last = 0; # = $gop_numbers[0];
            my $cuts_found = 0;
            foreach(sort(keys(%{$self->{GOP_pts}}))) {
                print"MASTERTIMER $self->{GOP_pts}{$_}\n";
                if ($last) {
                    my $diff = $self->{GOP_pts}{$_} - $last;
                    if (abs($diff - $self->{GOP_pics}{$_} * 3600) > 30000) {
                        print "found cut in videostream while checking the buffer\n";
                        $cuts_found++;
                    }
                }
                $last = $self->{GOP_pts}{$_};
            }
            if ($cuts_found > 1) {
                print "There are $cuts_found cuts in the video buffer!!!\n";
            }
            next;
        }
        for (my $i = 0; $i < scalar(@{ $self->{streams}{$_}{frame_times} }); $i++) {
            print "checking $_ and ${ $self->{streams}{$_}{frame_times} }[$i]\n";
            
            analyse_audio_format($self->{streams}{$_});
        }
    }
    #die;
}


sub analyse_audio_format {
    my $self = shift;
    my $payload_buffer = shift;
    
    my $format_changes = 0;
    
    
    
    my $total_audio = join "", @{ $payload_buffer };
    print "got total audio with length " . length($total_audio) . "\n";
    ($total_audio, my  %frame_info) = &{$self->{analyse_frames}}($self, $total_audio);
    my $frame_format = join "", sort(values(%frame_info));
    my %new_frame_info = %frame_info;
    my @formats;
    push @formats, "0:$frame_format";
    my $frame_counter = 0;
    
    while( length($total_audio) > $new_frame_info{frame_bytes}) {
        ($total_audio, %new_frame_info)  = &{$self->{analyse_frames}}($self, $total_audio);
        $total_audio = substr($total_audio, $new_frame_info{frame_bytes});
        my $curr_frame_format = join "", sort(values(%new_frame_info));
         push @formats, "$frame_counter:$frame_format";
        if ($curr_frame_format ne  $frame_format) {
            print "audio Format change found in buffer, arrg\n";
            $format_changes++;
            $frame_format = $curr_frame_format;
           
        }
        $frame_counter++;
    }
    
    if ($format_changes > 0) {
        if ($format_changes > 1) {
            my $start_frame_format = join "", sort(values(%frame_info));
            if ($start_frame_format eq $frame_format) {
                print "Probably a few broken Audio frames, this can be repaired\n";
            }
        } else {
            print "Bad luck, there is a real Audio Format change in this buffer...\n";
            print "And I do not think that a mplexer can andle this\n";
        }
    }
   
}


sub process_PES_packets    {
    
    #
    # Some subs are very long, and could easily be splitted in several subs. But for 
    # performance reasons I decided to bundle several thing
    #
    
    
    my $self = shift;
    my $limit = shift;
    
    my $lengthcounter = 0;
    my $packetcounter = 0;
    
    my $seqstart = pack("H8", "000001b3");
    my $picstart = pack ("H8", "00000100");
    
    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 ($need_fix_run) {
            fix_run($self); # Nothing useful yet
        }
        
        if (scalar(@kill_me)) {
            kill_streams($self); # Killing streams that are not fixable
        }
        
        ($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
            # note that only 32 Bits of the Timestamp are actually decoded, it is not necessary to decode 
            # all 33 Bits, and there are some (rare?) recordings, that have the highest Bit switched on in some streams
            $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); # Not used anymore
        
        # Check whether there is enough data in the buffer, else read some more
        if ((($self->{offset} + $pes_header{PES_packet_length} + 150) > (length($self->{buffer}))) && ($self->{EOF} == 0)) {
            my $helpbuffer = substr($self->{buffer}, $self->{offset});
            $self->{buffer} = $helpbuffer . ${ readNextChunk($self) };
            $self->{offset} = 0;
        }

        my $packet  = substr($self->{buffer}, $self->{offset}, $pes_header{PES_packet_length} + 6);
        my $payload = substr($packet, $pes_header{payload_start});
        $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 "here we might want to change something in the future....\n";
            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;
        }
        
        ####################
        # Here we start a new way of handling the data, we just store all the audio and all the video
        # for a max of 6 GOPs in buffers, and the flush the buffers. Analysis and Syncing is done before flushing
        # That should drastically reduce sub-routine calls, and maybe buffer memory. It also gets the script
        # closer to "pipe-through" functionality, since it should reduce the delay of audio-data output
        ####################
        
        
        # The video Stream is handled here
        if ($pes_header{stream_id} eq $self->{masterstream}) {
    
            if ($payload =~ /$seqstart/gm) {
                # First Run?
                if (! $self->{testbuffer}) {
                    if (pos($payload)-4 != 0) { # The first Buffer needs to start with a gop, everything before is skipped
                        $payload = substr($payload, (pos($payload)-4));
                    }
                    $self->{testbuffer} .= $payload;
                    if ((! $self->{GOP_pts}{$self->{GOPno}}) && $pes_header{PTS_decimal}) {
                        #print "setting PTS for GOP $self->{GOPno} to $pes_header{PTS_decimal}\n";
                        $self->{GOP_pts}{$self->{GOPno}} = $pes_header{PTS_decimal};
                    }
                    next;
                }
                my @hits = $self->{testbuffer} =~ m/$picstart/gm;
                
                $self->{GOP_pics}{$self->{GOPno}} = scalar(@hits);
                $self->{GOPs}{$self->{GOPno}}     = $self->{testbuffer};
                $self->{testbuffer}               ="";
                $self->{GOPno}++;
            }
            $self->{testbuffer} .= $payload;
            if ((! $self->{GOP_pts}{$self->{GOPno}}) && $pes_header{PTS_decimal}) {
                #print "setting PTS for GOP $self->{GOPno} to $pes_header{PTS_decimal}\n";
                $self->{GOP_pts}{$self->{GOPno}} = $pes_header{PTS_decimal};
            }
        } 
        # Audio streams are handled now, only Timestamps and payloads are buffered. The analysis is done directly before a buffer-flush
        else {
            # For audio streams, just buffer all packets and the PTSs
            push @{ $self->{output_buffer}{$pes_header{stream_id}}{payload} }, $payload;
            push @{ $self->{output_buffer}{$pes_header{stream_id}}{pts} }, $pes_header{PTS_decimal};
        }
        
        # If there are more than 6 GOPs in the buffer, a flush is started
        
        if (! $self->{audio_only}) {
            if (scalar(keys(%{$self->{GOP_pics}}))  > 6) {
                flush_GOP_buffer($self);
                foreach(keys(%{$self->{streams}})) {
                    if ($_ eq $self->{masterstream}) {
                        next;
                    }
                    flush_audio_buffer($self->{streams}{$_}, 
                        $self->{output_buffer}{$_}{payload},
                        $self->{output_buffer}{$_}{pts},
                    );
                }
            }
        } else {
            if (scalar(@{ $self->{output_buffer}{$pes_header{stream_id}}{payload} }) > 100) {
                flush_audio_buffer($self->{streams}{$pes_header{stream_id}}, 
                        $self->{output_buffer}{$pes_header{stream_id}}{payload},
                        $self->{output_buffer}{$pes_header{stream_id}}{pts},
                );
            }
        }
    }
    
    print "\n $packetcounter PES packets processed\n";
    
    if (($self->{first_flush}) && (! $self->{audio_only})) {
        flush_GOP_buffer($self);
    }
    
    if (! $self->{audio_only}) {
        print "Final Video Flush\n" if $self->{debug};
        # Final video flush
        # First, analyse the last GOP in the buffer
        
        my @hits = $self->{testbuffer} =~ m/$picstart/gm;
        $self->{GOP_pics}{$self->{GOPno}} = scalar(@hits);
        $self->{GOPs}{$self->{GOPno}}     = $self->{testbuffer};
        
        my @gop_numbers = sort { $a <=> $b } keys(%{ $self->{GOP_pts} });
        
        my $FH = $self->{streams}{$self->{masterstream}}{outfile};
        my $oldfh = select $FH; $| = 1; select $oldfh;
        my $flush_buffer;
        while ( $gop_numbers[0]) {
            
            $flush_buffer = $self->{buffer_rest} . $self->{GOPs}{$gop_numbers[0]};
            if ($flush_buffer) {
                my $rest = length($flush_buffer) % 4096;
                my $limit = length($flush_buffer) - $rest;
                print $FH substr($flush_buffer, 0, $limit);
                $self->{buffer_rest} = substr($flush_buffer, $limit);
            }
            
            $self->{frames_written} += $self->{GOP_pics}{$gop_numbers[0]};
            $self->{last_PTS} = $self->{GOP_pts}{$gop_numbers[0]};
            delete $self->{GOP_pts}{$gop_numbers[0]};
            delete $self->{GOP_pics}{$gop_numbers[0]};
            delete $self->{GOPs}{$gop_numbers[0]};
            shift @gop_numbers;
        }
        print "Printing a rest for video: " . length($self->{buffer_rest}) . " bytes\n" if $self->{debug};
        print $FH  $self->{buffer_rest};
        $self->{buffer_rest} = "";
        $current_last_PTS = $self->{last_PTS} + 3600 * scalar(@hits);
        print "cutcounter at $self->{cutcounter}, last PTS should be $current_last_PTS , last GOP shoud be $self->{GOPno}\n" if $self->{debug};
        $cutlist[$self->{cutcounter}] .= "::$current_last_PTS" . "::" . $self->{frames_written};
        foreach (@cutlist) {
            print "cutlist entry: $_\n" if $self->{debug};
        }
        
        #close $FH;
    }
    
    # Final Audio flushes...
    foreach (keys (%{$self->{streams}})) {
        if ($self->{ignore_streams}{$_}) {next}
        if (! ($self->{masterstream} eq $_)) {
            print "Final flush for stream $_\n" if $self->{debug};
            flush_audio_buffer($self->{streams}{$_}, 
                    $self->{output_buffer}{$_}{payload},
                    $self->{output_buffer}{$_}{pts},
                    );
            my $FH = $self->{streams}{$_}{outfile};
            my $seconds = $self->{streams}{$_}{frames_written} * $self->{streams}{$_}{frame_ticks} / 90000; #/
            print "$self->{streams}{$_}{frames_written} frames written for stream $_ ($seconds sec) \n" if $self->{debug};
            print "Printing a rest for $_: " . length($self->{streams}{$_}{buffer_rest}) . " bytes\n" if $self->{debug};
            print $FH  $self->{streams}{$_}{buffer_rest};
            $self->{buffer_rest} = "";
            #$self->{streams}{$_}->final_flush($self->{streams}{$_});
        } else {
            if (!$self->{audio_only}) {
                my $seconds = $self->{streams}{$_}{frames_written} * $self->{streams}{$_}{frame_ticks} / 90000; #/
                print "$self->{streams}{$_}{frames_written} frames written for stream $_ ($seconds sec) \n" if $self->{debug};
            }
        }
    }
    foreach (keys (%{$self->{streams}})) {
        my $FH = $self->{streams}{$_}{outfile};
        
        print "Closing FH for stream $_\n" if $self->{debug};
        #print $FH "0";
        close $FH;
        print "FH closed\n" if $self->{debug};
    }
    print_script_output($self);
    if (! $self->{script_output}){print_stats($self)};
}


sub use_audio_conversion_filter {
    my $self = shift;
    my %frame_info = %{ shift @_};
    if ($fix_audio && (! ($progs{mpg123} && $progs{mp2enc}))) {
        print "FATAL: Audio format changed and a conversion filter could not be applied (either mpg123 or mp2enc not found)\n";
        return -1;
    }
    if ($self->{audio_filter})  {
            print "WARNING: Audio format changed and a conversion filter could not be applied (There is already an audio filter in place)\n";
            print "I will just continue and hope that your filter does the job.\n";
            return;
    }
    my $FH = $self->{outfile};
    
    # First we flush all Audio in the previous format, if the change occurs at a cut, we flush only to the chunk end
    # 
    my ($chunk_start, $chunk_end, $video_frameno) = split /::/,$cutlist[$self->{cutcounter}];
    if ($self->{fix_audio}) {
        print "Trying to install an Audio Conversion Filter \n";
    } else {
        print "Switching Audio File\n";
    }
    sleep 1;
    if ($chunk_end) {
        print "The Audio change seems to correspondend to a cut, doing an end sync...\n";
        sync_audio_end($self, $chunk_end, $video_frameno);

        while (@{$self->{frame_times}}) {
            print "Dropping frame with time " . (shift @{$self->{frame_times}}) ."\n";
            shift @{$self->{frame_lengths}};
            shift @{$self->{frame_buffer}};
        }
    } else {   
        print "The Audio change seems not to correspondend to a cut, just flushing the buffer\n";
        while ((@{$self->{frame_buffer}}) && (length(${$self->{frame_buffer}}[0]) == $self->{frame_bytes})) {
            $self->{last_PTS} = shift @{$self->{frame_times}};
            $self->{total_length} += shift @{$self->{frame_lengths}};
            print $FH shift @{$self->{frame_buffer}};
            $self->{frames_written}++;
            #print "Printed frame with time $self->{last_PTS} \n";
        }
        @{$self->{frame_times}} = ();
        @{$self->{frame_lengths}} = ();
        @{$self->{frame_buffer}} = ();
        print "$self->{frames_written} frames with length $self->{total_length} written,\n\tcomparing to $current_total and $current_last_PTS\n\tlast_PTS is $self->{last_PTS}\n";
    }
    # Now we try to close the output-filehandle, and re-open it with a filter 
    print "closing old FH\n";
    close $FH;
    
    if ($fix_audio) {
        my $mp2_br = $self->{bitrate} * 0.001;
        my $mp2_mode = "-s";
        my $mpg123_mode = "--stereo";
        if ($self->{mode} eq "mono") {
            $mp2_mode    = "-m";
            $mpg123_mode = "--mono";
        }
    
        my $streamnumber = substr($self->{streamcode},1,1);

        $self->{audio_filter} = " mpg123  $mpg123_mode -r 48000 -w - - | mp2enc -b $mp2_br -e -r 48000 $mp2_mode -o - >> $self->{outfilename}";
        
        print "Trying to install filter: $self->{audio_filter}\n";
        if ($self->{vdrconvert_mode}) {
            #$self->{outfilename} = "$self->{outputpath}/$self->{streamcode}.mpa";
            vprint("\nWant to open $self->{outfilename}\n");
            open $self->{outfile}, " | $self->{audio_filter}  >>$self->{outfilename}"  or die "Can not open conversion filter for audio: $!\n";
        } else {
            #$self->{outfilename} = "$self->{outputpath}/$self->{basename}$streamnumber.mpa";
            vprint("Want to open $self->{outfilename}\n");
            open $self->{outfile}, " | $self->{audio_filter} >>$self->{outfilename}"  or die "Can not open conversion filter for mpa: $!\n";
            #print "Opend | $self->{audio_filter} >$self->{outfilename}\n";
        }
    } else {
        $self->{outfilename} .= "_$frame_info{mode}-$frame_info{freq}-$frame_info{bitrate}";
        open $self->{outfile}, ">$self->{outfilename}"  or die "Can not open conversion filter for mpa: $self->{outfilename} $!\n";
        print "Opend $self->{outfilename} after Audio Format change and writing new Audio to this file\n";  
    }
    
    
    $self->{audio_change} = $self->{last_PTS};
    
    binmode $self->{outfile};   
   
}

sub flush_audio_buffer {
    
    #
    # Some subs are very long, and could easily be splitted in several subs. But for 
    # performance reasons I decided to bundle several thing
    #
    
    my $self           = shift;
    my $payload_buffer = shift;
    my $pts_buffer     = shift;
    
    my $FH = $self->{outfile};
    my $oldfh = select $FH; $| = 1; select $oldfh;
    
    my $packet_offset = 0;
    my $flush_buffer;               # Just used for the use-pipe option to make sure we send the size of the data-paket first
    #my $substream_code = pack ("H6", "800000");  # Obviously not constant, not working with 1.3.23 :-(
  

    #
    # First get all the audio in single frames with correct PTS
    #
     
    my $counter = 0;
    my ($payload, $PTS, %frame_info);
    
    while (@$payload_buffer) {
        
        if ($self->{packet_rest}) {
            $payload = $self->{packet_rest} . shift @$payload_buffer ;
            $self->{packet_rest} = "";
        } else {
            $payload = shift @$payload_buffer ;
        }
            
        $PTS = shift @$pts_buffer;
        #print "shifted payload has a length of" . length($payload) . "\n";
        
        ($payload, %frame_info) = &{$self->{analyse_frames}}($self, $payload) ;
        
        if($payload eq "-1") {
            print "Failed to find frame, skipping\n";
            next;
        }
        
        if ((length($payload) < 100)) {
            print "Payload too short here: $payload\n";
            $self->{kill_score} += 10;
            if ($self->{kill_score} > 100) {
                push @kill_me, $self->{streamcode};
            }
            next;
        }
        if ((!($self->{frame_bytes} eq $frame_info{frame_bytes}) || 
            (!($self->{bitrate} eq $frame_info{bitrate}))) 
            && ($self->{streamcode} ne "bd") 
            && (!$self->{audio_filter})) {
            
            my $result = scan_for_format_change($self, $payload, \%frame_info);
            if ($result eq "-1") {
                $result = use_audio_conversion_filter($self, \%frame_info);
                if ($result eq "-1") {
                    die "Audio Format changed and a conversion Filter could not be set up\n";
                } else {
                    print "Audio Format changed in stream $self->{streamcode}, installed a conversion filter or switched files...\n";
                    $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 "Audio Mode Now:  $self->{mode}  $self->{freq} Hz  $self->{bitrate} Bit/s\n";
                    $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};
                    }
                }
            } else {
                print "Audio Format Changed, replaced some Audio by silence frames\n";
            }
        }
        if ((! $self->{frame_times}[0]) && ($PTS) && ($self->{frameno} > 0) && (! $self->{audio_only}) && (! $self->{frames_written})){
            #print "\nFirst audio timestamp at $self->{frameno} for stream $self->{streamcode}\n";# if $self->{debug};
            print "First Audio Timestamp is $PTS\n" if $self->{debug};
            for (my $i = 1;  $i <= $self->{frameno}; $i++) {
                $self->{frame_times}[$self->{frameno} - $i] = $PTS - ($i * $self->{frame_ticks});
            }
            print "\nAudio now starting at $self->{frame_times}[0]\n" if $self->{debug};
        }
         
        my $first_timestamp = 0;
    
        if ($PTS) {
            $first_timestamp = $PTS;
        }
        else {
            $first_timestamp = ${$self->{frame_times}}[-1] + $frame_info{frame_ticks} if (${$self->{frame_times}}[-1]);
        }
        
        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_lengths}}, $frame_info{frame_ticks};

                $packet_offset += $frame_info{frame_bytes};
                if ($self->{streamcode} eq "bd") {
                    if ((substr($payload, $packet_offset + 4, 2)) =~ /^$self->{frame_regex}$/) {
                        $packet_offset += 4;
                        if (! $self->{new_VDR_Warning}) {
                            print "\n\nWarning! Probably a VDR Version > 1.3.19 was used to record this broadcast.\n";
                            print "Warning! AC3 processing highly experimental!\n\n";
                            $self->{new_VDR_Warning} = 1;
                        }
                    } 
                }
                $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} = "";
        }
        $counter = 0;
        $packet_offset = 0;
    }
    
    
    #   
    #   if this is audio-only, just flush the buffer and out...
    #
    
    if ($self->{audio_only}) {
        while (@{$self->{frame_times}}) {
            $self->{last_PTS} = shift @{$self->{frame_times}};
            $self->{total_length} += shift @{$self->{frame_lengths}};
            print $FH shift @{$self->{frame_buffer}};
            $self->{frames_written}++;
        }
        return;
    }
    
    #
    # For the real flush, get the beginning and (possibly) the end of the current chunk
    #
    
    my ($chunk_start, $chunk_end, $video_frameno) = split /::/,$cutlist[$self->{cutcounter}];
    
    #
    # If this is the first flush, we need to sync the start
    #
       
    if ($self->{first_flush}){
        
        if (! $self->{single_frame}) {
            $self->{single_frame} = get_silent_frame($self, \%frame_info); # Always try to get the latest Frame ##FIXME
            if ($self->{single_frame} eq "-1") {
                $self->{single_frame} = ${$self->{frame_buffer}}[0];
                print "\nDEFINED THE FIRST FRAME IN BUFFER FOR $self->{streamcode} AS SINGLE FRAME WITH length " . length($self->{single_frame}) . " and frame bytes are $self->{frame_bytes}\n";
            }
        }
        print "GOT A $self->{streamcode} SINGLE FRAME WITH length " . length($self->{single_frame}) . " and frame bytes are $self->{frame_bytes}\n" if $self->{debug};
        my $result = sync_audio_start($self, $chunk_start);
        if ($result eq "-1") {
            print "syncing failed for the first time, delaying\n";
            $self->{kill_score} += 10;
            if ($self->{kill_score} > 100) {
                push @kill_me, $self->{streamcode};
            }
            return;
        }
        $self->{first_flush} = 0;
    }
    
    #
    # If the cunk end is defined, then we actually are at the end of the current chunk and need to sync it (as well as the nexts chunk start)
    #
    
    if ($chunk_end) {

        my $next_chunk_start; 
        
        # Check for PTS Overflow
        # FIXME THIS HAS TO BE CHECKED MORE CAREFULLY
        my $overflow = 0;
        if ($cutlist[$self->{cutcounter} + 1]) {
            ($next_chunk_start, undef, undef) = split /::/,$cutlist[$self->{cutcounter} + 1];
            if (($chunk_end > $next_chunk_start) && (($chunk_end - $chunk_start) > 4292267296)) {
                $overflow = 1;
                print "A PTS Overflow has been detected in stream $self->{streamcode}\n";
                #foreach (@{$self->{frame_times}}) {
                #    print "Overflow Buffer PTS: $_\n";
                #}
            }        
        }
        
        if ($overflow) {
            print "doing a overflow flush...${$self->{frame_times}}[0]\n";
            $flush_buffer  = $self->{buffer_rest};
            while (${$self->{frame_times}}[0] > $next_chunk_start) {
                shift @{$self->{frame_times}};
                $self->{total_length}  += shift @{$self->{frame_lengths}};
                $flush_buffer .= shift @{$self->{frame_buffer}};
                $self->{frames_written}++;
        
            }
            #~ if ($self->{use_pipe}) {
                #~ my $length = pack("V",length($flush_buffer))  if ($flush_buffer);
                #~ print $FH $length                             if ($flush_buffer);
            #~ }    
            #~ print $FH $flush_buffer if ($flush_buffer);
            
            
            
            if ($flush_buffer) {
                my $rest = length($flush_buffer) % 4096;
                my $limit = length($flush_buffer) - $rest;
                print $FH substr($flush_buffer, 0, $limit);
                $self->{buffer_rest} = substr($flush_buffer, $limit);
            }
            #print "After overflow flush...${$self->{frame_times}}[0]\n";
        }
        else {
            my $result = sync_audio_end($self, $chunk_end, $video_frameno);
            if ($result eq -1) {
                die "syncing end of chunk $self->{cutcounter} in  $self->{streamcode} failed\n";
            }
            
        }
        
        $self->{cutcounter}++;

        if (!$cutlist[$self->{cutcounter}]) {
            print "no next chunk start, assuming last flush...\n" if $self->{debug};
            #if ($use_pipe) {
            #    print $FH pack("V", 0);
            #}
            return;
        }
        ($chunk_start, $chunk_end, $video_frameno) = split /::/,$cutlist[$self->{cutcounter}];
        if (! $overflow) {
            my $result = sync_audio_start($self, $chunk_start);
            if ($result eq -1) {
                foreach(@cutlist) {
                    print "cutlist: $_\n";
                }
                die "syncing start of chunk $self->{cutcounter} in $self->{streamcode} failed\n";
            }
        } else {
            print "Skipping start sync cause it is an overflow $current_last_PTS\n";
            return;
        }
    }
    
    #
    # Now flush audio til it matches the current video pos....
    #
    
    
    my $help_PTS   = $self->{last_PTS};
    $flush_buffer  = $self->{buffer_rest};
    
    while ((${$self->{frame_times}}[0] < $current_last_PTS) 
        && ($self->{total_length} < $current_total)
        && @{$self->{frame_buffer}}) {
        
        $self->{last_PTS} = shift @{$self->{frame_times}};
        $self->{total_length}  += shift @{$self->{frame_lengths}};
        $flush_buffer .= shift @{$self->{frame_buffer}};
        $self->{frames_written}++;
    }
    #~ if ($self->{use_pipe}) {
        #~ my $length = pack("V",length($flush_buffer))  if ($flush_buffer);
        #~ print $FH $length                             if ($flush_buffer);
    #~ } 
    #~ print $FH $flush_buffer if ($flush_buffer);
    if ($flush_buffer) {
        my $rest = length($flush_buffer) % 4096;
        my $limit = length($flush_buffer) - $rest;
        print $FH substr($flush_buffer, 0, $limit);
        $self->{buffer_rest} = substr($flush_buffer, $limit);
    }
    
    if (${$self->{frame_times}}[0] >= $current_last_PTS) {

        if (($current_total - $self->{total_length}) > 3600) {
            print "WARNING: Stopped Audio flush for stream $self->{streamcode} because the last Audio PTS is bigger than the last Video PTS, but the Audio length is shorter than the Video length:\n\tVideo length $current_total Audio length  $self->{total_length} Diff: " . ($current_total - $self->{total_length}) ."\n";
            my $ins_frames = insert_frames($self, ($current_total - $self->{total_length}));
            print "\tCORRECTION: Inserted $ins_frames additional frames cause at $current_last_PTS there was not enough audio\n";
            $self->{frames_written} += $ins_frames;
            $self->{kill_score} += 10;
            if ($self->{kill_score} > 100) {
                push @kill_me, $self->{streamcode};
            }
        }
    } elsif ($self->{total_length} >  $current_total) {
      #  print "Stopped flushin cause length limit was reached $self->{total_length} >= $current_total\n";
        #print "Stopped cont:length:  current_total $current_total total_length = $self->{total_length} diff: " . ($current_total - $self->{total_length}) ."\n";
        if ($self->{audio_change} >= $current_last_PTS) {
            print "Ignoring the need for sync since Audioformat changed\n";
        }
        elsif ((($current_last_PTS - ${$self->{frame_times}}[0]) > 3600) && (@{$self->{frame_times}})) {
            if (($current_last_PTS - ${$self->{frame_times}}[0]) > 4000000000){
                print "Just an overflow in stream $self->{streamcode}...\n"; 
            } else {
                print "Got in buffer " . scalar(@{$self->{frame_buffer}}) . " packets $current_last_PTS ${$self->{frame_times}}[0]\n";
                print "WARNING: Too much Audio of stream $self->{streamcode}, after adjusting Audio Stream length to Video Stream length a PTS diff of : " . ($current_last_PTS - ${$self->{frame_times}}[0]) . " is present. Starting to drop Audio Frames\n";
                while ((($current_last_PTS - ${$self->{frame_times}}[0]) > 3600) && (@{$self->{frame_buffer}})) {
                    print "\tCORRECTION: dropping Frame with PTS " . (shift @{$self->{frame_times}}) . "\n";
                    shift @{$self->{frame_buffer}};
                    shift @{$self->{frame_lengths}};
                }
                $self->{kill_score} += 10;
                if ($self->{kill_score} > 100) {
                    push @kill_me, $self->{streamcode};
                }
            }
        }
     } elsif (($current_total - $self->{total_length}) > 3600) {
        print "WARNING: Stopped flushing because the Audio framebuffer of stream $self->{streamcode} is empty. This is not supposed to happen!\n";
        $self->{kill_score} += 10;
        if ($self->{kill_score} > 100) {
            push @kill_me, $self->{streamcode};
        }
        #my $length = pack("V", $self->{frame_bytes});
        if ( $current_total - $self->{total_length} > 900000) {
            push @kill_me, $self->{streamcode};
            print "WARNING: Killing stream $self->{streamcode} because too much Audio is missing (> 10 sec)\n";
        }
        while ($self->{total_length} < $current_total) {
            #~ if ($self->{use_pipe}) {
                #~ print $FH $length;
            #~ }
            #~ print $FH $self->{single_frame};
            $flush_buffer =  $self->{buffer_rest} .  $self->{single_frame};
            if ($flush_buffer) {
                my $rest = length($flush_buffer) % 4096;
                my $limit = length($flush_buffer) - $rest;
                print $FH substr($flush_buffer, 0, $limit);
                $self->{buffer_rest} = substr($flush_buffer, $limit);
            }
            $self->{total_length}+= $self->{frame_ticks};
            print "\nCORRECTION: Added a frame to reach $current_total, we are now at $self->{total_length}\n";
            
        }
    } else {
        #die "should not have stopped at all\n";
    }
    
    
    if ($self->{kill_score}) {
        $self->{kill_score}--;
        print "kill_score is now: $self->{kill_score}\n";
    }
    my $length_diff = ($current_total - $self->{total_length});
    #~ if ($self->{streamcode} eq "bd") {
        #~ print "NOW TOTAL FRAMES: $self->{frames_written}\n";
        #~ if ($self->{frames_written} > 37000) {
            #~ die "Stopping\n";
        #~ }
    #~ }
    #print "leaving audio flush with a rest of length " . length($self->{buffer_rest}) . "\n";
   # print "AUDIO $self->{streamcode}: written $self->{frames_written} frames, total length $self->{total_length} up to $self->{last_PTS} " . scalar(@{$self->{frame_buffer}}) . " frames remain in buffer, diff wrt PTS is " . ($current_last_PTS - $self->{last_PTS}) . " lengthdiff is $length_diff\n" if (abs($length_diff) > 2000);
}

sub flush_GOP_buffer {
    
    my $self = shift;
    my $first_PTS = 0;
    my $picstart = pack ("H8", "00000100");
    
    my $FH = $self->{streams}{$self->{masterstream}}{outfile};
    my $oldfh = select $FH; $| = 1; select $oldfh;

    my $master = $self->{masterstream};
    my $flush_buffer = "";
    
    # Define the first Video PTS of the recording 
    if (! $self->{frames_written}) {
        print "setting first timestamp to $self->{GOP_pts}{0}\n" if $self->{debug};
        $cutlist[$self->{cutcounter}] = $self->{GOP_pts}{0};
        if (! $self->{GOP_pts}{0}) {
            die "no PTS in the first GOP, giving up\n";
        }
    }
    
    my $prev_PTS = $self->{last_PTS};
    my $prev_pics = $self->{last_pics} || 0;
    
    my @gop_numbers = sort { $a <=> $b } keys(%{ $self->{GOP_pts} });        
    my $flush_until = $gop_numbers[3];
    
    # First we check the GOPs in buffer for one or more cuts
    
    #print "DEBUG checking GOP range $self->{last_GOP_checked}  $gop_numbers[-1]\n";
    
    for (my $i = $self->{last_GOP_checked}; $i <= $gop_numbers[-1]; $i++)  {
        
        
        if (! $self->{GOP_pts}{$i}) {
            $self->{GOP_pts}{$i} = $self->{GOP_pts}{$i-1} + 3600 * $prev_pics;
            print "WARNING: No PTS found in GOP $i, calculating one\n";
            sleep 2;
        }
            
        if ($prev_PTS) {
            my $diff = $self->{GOP_pts}{$i} - $self->{GOP_pts}{$i-1};#$prev_PTS;
            my $calc = 3600 *  $prev_pics; #$self->{GOP_pics}{$i-1};#
            my $drift = $diff - $calc;
            if ($drift) {
                $self->{time_drift} += $drift;
                print "Drift in Video is $drift, total is $self->{time_drift}\n" if (($drift > 100) && ($self->{debug}));
            }
            if (abs($drift) > 36000) {
                print "DEBUG Cut detected at $self->{GOP_pts}{$i} before GOP $i\n";
                if (! $self->{cuts_processed}{$i})  {
                    print "\nCUT detected in Video at $self->{GOP_pts}{$i} before GOP $i\n";
                    my $chunk_end = $prev_PTS + 3600 * $prev_pics;
                    my $end_frames = $self->{frames_written};
                    for (my $j = $gop_numbers[0]; $j < $i; $j++) {
                        $end_frames += $self->{GOP_pics}{$j}; 
                    }   
                    $self->{cuts_in_buffer}{$i} = "::$chunk_end" . "::$end_frames" . "__NEW__$self->{GOP_pts}{$i}";
                    print "DEBUG $self->{cuts_in_buffer}{$i}\n";
                } else {
                        print "DEBUG cut at $i already processed\n";
                }
                if ($drift < 0) {
                    print "\nPTS Overflow before GOP $i: $prev_PTS $self->{GOP_pts}{$i}\n";
                }
            } else {
                #print "DEBUG OK, $self->{GOP_pts}{$i} - $prev_PTS = $diff\n";
            }
        }
        $prev_PTS = $self->{GOP_pts}{$i};
        $prev_pics = $self->{GOP_pics}{$i};
    }            
    $self->{last_GOP_checked} = $gop_numbers[-1];
    $self->{last_PTS} = $self->{GOP_pts}{ $gop_numbers[-2] };
    $self->{last_pics} = $self->{GOP_pics}{ $gop_numbers[-2] };
    
    #print "DEBUG Setting last_GOP_checked $self->{last_GOP_checked} and last_PTS to $self->{last_PTS}\n";
    
    if (%{$self->{cuts_in_buffer}}) {
        print "DEBUG Found non processed cuts in the buffer\n";
       
        my $cut_to_process = (sort { $a <=> $b } (keys(%{$self->{cuts_in_buffer}})))[0];
        if ((! $cut_to_process) || (! $self->{GOP_pts}{$cut_to_process})) {
            die "Did not find gop $cut_to_process in buffer, but is should be there\n";
        }
        print "DEBUG processing Cut at GOP $cut_to_process\n";
        if ($gop_numbers[0]  < $cut_to_process - 3) {
            $flush_until = $cut_to_process - 3;
            print "DEBUG Delaying processing cause cut is at the buffer end: $cut_to_process, and start is $gop_numbers[0]\n";
        } else {
            my ($old_chunk_end,  $new_start) = split /__NEW__/, $self->{cuts_in_buffer}{$cut_to_process};
            print "DEBUG Processing cut: $old_chunk_end, $new_start\n";
            $cutlist[$self->{cutcounter}] .= $old_chunk_end;
            $self->{cutcounter}++;
            print "DEBUG Setting cutcounter to $self->{cutcounter}\n";
            $cutlist[$self->{cutcounter}] = $new_start;
            $flush_until = $gop_numbers[1];
            $self->{cuts_processed}{$cut_to_process} = 1;
            delete ($self->{cuts_in_buffer}{$cut_to_process});
            $self->{time_drift} = 0;
        }
    }
    #print "DEBUG ($flush_until > $gop_numbers[0])\n";
    if ($flush_until > $gop_numbers[0]) {
        $current_last_PTS = $self->{GOP_pts}{$flush_until - 1} + 3600 * $self->{GOP_pics}{$flush_until - 1};
    } else {
        print "Not changing current_last_pts cause $flush_until is equal to $gop_numbers[0]\n";
        die "This is not supposed to happen!\n";
    }

   
    while ( $gop_numbers[0] < $flush_until) {

        #~ if ($use_pipe) {
            #~ my $length = pack("V", length($self->{GOPs}{$gop_numbers[0]}));
            #~ print $FH $length;
        #~ }
        $flush_buffer = $self->{buffer_rest} . $self->{GOPs}{$gop_numbers[0]};
        
        if ($flush_buffer) {
            my $rest = length($flush_buffer) % 4096;
            my $limit = length($flush_buffer) - $rest;
            print $FH substr($flush_buffer, 0, $limit);
            $self->{buffer_rest} = substr($flush_buffer, $limit);
        }
        
        $self->{frames_written} += $self->{GOP_pics}{$gop_numbers[0]};
        #$self->{last_PTS} = $self->{GOP_pts}{$gop_numbers[0]};
        delete $self->{GOP_pts}{$gop_numbers[0]};
        #$self->{last_pics} = $self->{GOP_pics}{$gop_numbers[0]};
        delete $self->{GOP_pics}{$gop_numbers[0]};
        delete $self->{GOPs}{$gop_numbers[0]};
        shift @gop_numbers;
    }
    $current_total = $self->{frames_written} * 3600;
   
}


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->{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->{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) {push @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} = oct("0b" . substr($pes_header{PTS_value_bits},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->{ignore_streams}{$pes_header{stream_id}} = 1;
                    
                    next;
                }
                
                if ($self->{masterstream}){
                    vprint ("Video stream already defined, but found stream $pes_header{stream_id} in addition. Ignoring stream $pes_header{stream_id}\n");
                    $self->{ignore_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},
                            vdrconvert_mode => $self->{vdrconvert_mode},
                            progs           => $self->{progs},
                            );
                    $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},
                            vdrconvert_mode => $self->{vdrconvert_mode},
                            progs           => $self->{progs},
                            fix_audio       => $self->{fix_audio},
                            );    
                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_cut", "movie_size", "movie_size_cut") {
                print "$_ : $Recording->{$_} \n";
            }
            if ($self->{cut_recording}) {
                print "CUTFLAG HAS BEEN SET\n";
                print "Changing $movie_length to $Recording->{movie_length_cut}\n";
                $movie_length = $Recording->{movie_length_cut};
                print "Changing $total_size to $Recording->{movie_size_cut}\n";
                $total_size = $Recording->{movie_size_cut};
            }
        } 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}{$_}});
        if ($packet_number < 10) {
            print "Not enough pakets for stream $_, ignoring\n";
            next;
        }
        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_cut};
            
            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      VDRRECORDING;
require      Exporter;

use strict;
use warnings;
use File::Path;

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

our $VERSION   = "0.0.6";		        # Version number


sub new {
	my $invocant = shift;
    my $class   = ref($invocant) || $invocant;
    my $self = {
		files 			  => [],
		file_handles 	  => [],
		file_sizes 		  => [],
		marks_file 		  => "",
        chapter_marks_file=> "",
		summary_file 	  => "",
        movie_size        => 0,
        movie_size_cut => 0,
        movie_length      => 0,
        movie_length_cut=> 0,
		index_file 		  => "",
		IFH 			  => "",
		marks 			  => [],
        chapter_marks     => [],
		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  => "",
                
                        }, 
        debug               => 0,
        create_index        => "/usr/bin/create_indexvdr.pl",
        debug_log          =>"/tmp/vdrrecording.debug",
		@_,
	};
	return bless $self, $class;
}
sub verify_chapter_marks {
    my $self = shift;
    my @marks = @{ $self->{marks} };
    my @chapter_marks = @{  $self->{chapter_marks} };
    my @new_marks;
    
    $self->VR_dprint("verifying Chapter marks\n");
    
    for (my $i = 0; $i < scalar(@chapter_marks); $i++)  {
        my $cf = $self->mark2frames($chapter_marks[$i]);
        print "checking mark $chapter_marks[$i] at frame $cf \n" if $self->{debug};
        for (my $j = 0; $j < scalar(@marks); $j +=2) {
            my $ok = 0;
            my $f = $self->mark2frames($marks[$j]);
            print "comparing with mark $marks[$j] at frame $f \n" if $self->{debug};
            if ($f <= $cf) {
                print "$f < $cf \n";
                my $ef = $self->mark2frames($marks[$j+1]);
                print "Ok now $ef should be bigger than $cf\n" if $self->{debug};
                if ($ef >= $cf) {
                    print "$ef > $cf \n" if $self->{debug};
                    push @new_marks, $chapter_marks[$i];
                    print "Mark $cf verified...\n" if $self->{debug};
                } 
            } else {
                print "Mark $cf will be removed\n" if $self->{debug};
                last;
            }
        }
    }
    return @new_marks;
}
sub generate_chapters {
   
    my $self = shift;
    my $chapter_interval = shift;
    my $cut = shift || 0;
    
    my @marks = @{ $self->{marks} };
    my @auto_chapters;
    my @frame_marks;
    my $offset = 0;
    my $end = 0;
    my $current_start;
    my $current_end;
    
    
    if (! $cut) {
        for (my $i = 0; $i < $self->{total_frames}; $i += $chapter_interval * 25) {
            push @auto_chapters, $i;             # Also a chapter start at 0...
        }
    } 
    
    else {
    
        for (my $i = 0; $i < scalar(@marks); $i+=2)  {
            my $startf = $self->mark2frames($marks[$i]);
            my $endf;
            if ($marks[$i+1]) {
                $endf = $self->mark2frames($marks[$i+1]);
            } else {
                $endf = $self->{total_frames};
            }
            push @frame_marks, "$startf TO $endf";
            #print "$startf TO $endf\n";
        }
        
        my $counter = 0;
        ($current_start, $current_end) = split / TO /, $frame_marks[$counter];
        $offset = $current_start;
        push @auto_chapters, $offset;           # Always include a chapter start at 0
        #print "starting at $offset to $current_end\n" i;
        while (! $end) {
            if (($offset + $chapter_interval * 25) <= $current_end) {
                $offset += $chapter_interval * 25;
                push @auto_chapters, $offset;
                next;
            } else {
                if (!$frame_marks[$counter+1]) {
                    $end = 1;
                    next;
                }
                my $rest = $current_end - $offset;
                $counter++;
                ($current_start, $current_end) = split / TO /, $frame_marks[$counter];
                $offset = $current_start - $rest;
            }   
        }
    }
    
    foreach(@auto_chapters) {
        my ($real_frame, undef) = $self->find_GOP_offset($_);
        push @{$self->{chapter_marks}}, $self->frames2mark($real_frame);
        $self->VR_dprint("pushed $real_frame as ${$self->{chapter_marks}}[-1]\n") if $self->{debug};
    }
}

sub get_cut_chapter_marks {
    
    my $self = shift;
    
    my @marks = @{ $self->{marks} };
    my @chapter_marks = @{  $self->{chapter_marks} };
    my @new_marks;
    
    
    for (my $i = 0; $i < scalar(@chapter_marks); $i++)  {
        my $cf = $self->mark2frames($chapter_marks[$i]);
        $self->VR_dprint("checking mark $chapter_marks[$i] at frame $cf \n") if $self->{debug};
        my $offset = 0;
        for (my $j = 0; $j < scalar(@marks); $j +=2) {
            my $ok = 0;
            my $f = $self->mark2frames($marks[$j]);
            $self->VR_dprint("Comparing with mark $marks[$j] at frame $f \n") if $self->{debug};
            if ($f <= $cf) {
                $self->VR_dprint("$f < $cf \n") if $self->{debug};
                my $ef = $self->mark2frames($marks[$j+1]);
                $self->VR_dprint("Ok now $ef should be bigger than $cf\n") if $self->{debug};
                if ($ef >= $cf) {
                    $self->VR_dprint("$ef > $cf \n") if $self->{debug};
                    push @new_marks, $offset + $cf - $f;
                    $self->VR_dprint("Mark $cf verified...\n") if $self->{debug};
                    last;
                } 
                $offset += ($ef -$f);
                $self->VR_dprint("offset is now $offset frames\n") if $self->{debug};
                
            } else {
                $self->VR_dprint( "Mark $cf will be removed\n") if $self->{debug};
                last;
            }
        }
    }
    return @new_marks;
}

sub get_cut_marks {
    
    my $self = shift;
    
    my @marks = @{ $self->{marks} };
    
    my @new_marks;
    my $offset = 0;
    
    for (my $j = 0; $j < scalar(@marks); $j +=2) {
        my $f = ($self->mark2frames($marks[$j]));
        $self->VR_dprint("Comparing with mark $marks[$j] at frame $f \n") if $self->{debug};
        my $ef = $self->mark2frames($marks[$j+1]);
        my $chunk_length = $ef - $f;
        push @new_marks, $offset;
        push @new_marks, $offset + $chunk_length - 10;
        $offset += $chunk_length; 
    }
    return @new_marks;
}

sub get_chapter_shots{
 
    my $self   = shift;
    my $subdir = shift;
    my $chapter_length = shift;
    my $help_prog = shift;
    
    my $transcode;
    my $jpeg2yuv;
    
    if ($help_prog =~ /transcode/) {
        $transcode = 1;
    } elsif ($help_prog =~ /jpeg2yuv/) {
        $jpeg2yuv = 1;
    } else {
        die "Need to know which helper program to use...\n";
    }
    
    my @chapter_list;
        
    # First, extract the Video Bits and transform them into JPEGs
    #
    if (! -d "$subdir") {
        mkpath("$subdir") or die "Could not create $subdir: $!\n"; 
    }
    chdir "$subdir/" or die "Could not change to $subdir: $!\n";
    for (my $i = 0; $i < scalar(@{ $self->{chapter_marks} }); $i++) {
        #print "Chunk start mark: ${ $self->{marks} }[$i]\n";
        my $sec = $self->HMS2sec(${ $self->{chapter_marks} }[$i]);
        #print "in sec: $sec\n";
        my $dirname = sprintf("chapter_%04u", $i+1);
        #print "Trying to create $dirname\n";
        if (! -d  "./$dirname") {
            mkpath("./$dirname") or die "Could not create $dirname: $!\n";
        }
        my ($gop, $gopno) = $self->get_gop_at($sec, int($chapter_length * 2.2) +2);
        #print "Got a Video chunk of length " . length($gop) . "\n";
        open FH, ">./$gopno.mpv" or die "can not open file: $!\n";
        binmode FH;
        print FH $gop;
        close FH;
        my ($cmd, $error);
        if ($transcode) {
            $cmd = "transcode -i $gopno.mpv   -z -k -Z 180x144 -F jpg  -x mpeg2,null -o " . $gopno . "_  -y im,null ";
        } elsif ($jpeg2yuv) {
            $cmd = "ffmpeg -i $gopno.mpv -s 150x120 -t $chapter_length ./$dirname/$gopno" . "_%4d.jpg ";
        } else {
            die "You need either transcode or jpeg2yuv for this script to work!\n";
        }
        
        
        $self->VR_dprint("running: $cmd\n");
        $error = system ($cmd . " &> /dev/null");
        
        #unlink "./$gopno.mpv";
        
        if ($error) {
            #cmd_error($cmd);
            $self->VR_dprint("The follwing command returned the error $error\n: $cmd\n");
        }
    }
    chdir ".." or die "Could not change the dir \n";;
    
}

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)) {
		die "could not seek, dieing..\n";
	}
	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->binidx2frame($frame)) {
        return ($frame, $self->binidx2frame($frame));
	} 
	my $return_frame = 0;
	my $i = 1;
	while ((! $return_frame) && ($i < 10)) {
        if ($self->binidx2frame($frame - $i))  {
        	#$self->VR_dprint("Frame $i frames before $frame is an I-Frame. Returning " . ($frame - $i) . " " . $self->binidx2frame($frame - $i) . "\n") if $self->{debug};;
		    return ($frame - $i , $self->binidx2frame($frame - $i));
		}
        elsif ($self->binidx2frame($frame + $i)) {
			#$self->VR_dprint(print "Frame $i frames behind $frame is an I-Frame. returning " . ($frame + $i) . " " . $self->binidx2frame($frame + $i) . "\n") if $self->{debug};;
            return ($frame + $i , $self->binidx2frame($frame + $i));
		}
		else {
			#$self->VR_dprint(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 $gops_to_get = shift || 1;
    (my $current_gop, my $return_frameno) = get_gop($self, $sec_to_get, $gops_to_get);
	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;
    closedir $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");
	$self->{chapter_marks_file} = $indir . "chapter_marks.vdr" if (-e "$indir/chapter_marks.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;
	}
}

sub read_marks_file {
	my $self = shift;
	if (! $self->{marks_file}) {return;}
	open MFH, "$self->{marks_file}" || die "Could not open marks.vdr";
	@{$self->{marks}} = ();
	my @raw_marks = <MFH>;
	foreach (@raw_marks) {
		chomp $_;
        $_ = (split /\s/, $_)[0];
        if ($_ !~ /^\d{1,2}:\d{1,2}:\d{1,2}\.\d{1,2}$/) {
            $self->VR_dprint("Could not read mark $_, skipping\n");
            next;
        }
		my ($h,$m,$s,$f) = split /[:.]/,$_;
		my $frame = ($h * 3600 + $m * 60 + $s)* 25 + $f;
		my $real_frame = (find_GOP_offset($self, $frame))[0];
		my $real_mark = $self->frames2mark($real_frame);
        if ($real_mark ne "-1") {
            push @{$self->{marks}}, $real_mark;
        }
	}
}


sub read_chapter_marks_file {
	my $self = shift;
	if (! $self->{chapter_marks_file}) {return;}
	#print "Trying to get chapter_marks_file: $self->{chapter_marks_f}\n";
    
	open MFH, "$self->{chapter_marks_file}" or die "Could not open chapter_marks.vdr";
	@{$self->{chapter_marks}} = <MFH>;
	foreach (@{$self->{chapter_marks}}) {
		#print "got chapter_mark $_";
		chomp $_;
        my $mark = (split(/\s/,$_))[0];
		my ($h,$m,$s,$f) = split /[:.]/,$mark;
		#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];
		
		$_ = $self->frames2mark($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";
	binmode FH;
	my $buffer;
	my $bytesread = sysread (FH, $buffer, 10000000);
    $self->{binindex} = $buffer;
	close FH;
    my $offset = 0;
	my $I_counter = 0;
	
  
    my $li_frame = 0;
    $offset = 8;
    while ($li_frame == 0) {
        my ($c, $Ptype, $number, $reserved) = unpack ("I C C S", substr($buffer, (length($buffer) - $offset), 8));
        if ($Ptype == 1) {
            $li_frame = int ((length($buffer) - $offset) / 8);
            #print "last Iframe found as frame $li_frame with offset $offset and " . (length($buffer)) ."\n";
        } else {
            $offset += 8;
        }
        if ($offset >= (length($buffer)) ) {
            die "Something went horribly wrong\n";
        }
    }
	
	$self->{total_frames} = int(length($self->{binindex}) / 8);
    $self->{last_Iframe} = $li_frame;
   
    return ;
}


sub get_gop {
	my $self = shift;
	my $sec = shift;
	my $gops_to_get = shift || 1;
	if ($sec > $self->{total_frames} * 0.04) {
		$sec = ($self->{total_frames} * 0.04) -1;
	}
	if ($sec < 0) {
		$sec = 0;
	}
	
	my $frame_offset = $sec * 25;
	
	my ($frameno, $gop_offset) = find_GOP_offset($self, $frame_offset);
	$self->VR_dprint("Wanted frame $frame_offset, got $frameno at $gop_offset\n") if $self->{debug};
	
	my $counter = $frameno;
	
    while ($gops_to_get) {
		$counter++;
		if ($counter > $self->{total_frames}) {
            $counter = $self->{last_Iframe};
		}
        if (! $self->binidx2frame($counter)) {
       		next;
		}
		else {
			$gops_to_get--;
		}
	}
	my $gop_end = $self->binidx2frame($counter);
    if ($gop_offset eq $gop_end) {
        my $fileno = (split " : ",$gop_offset)[1];
        $gop_end = ${$self->{file_sizes}}[$fileno - 1] . " : $fileno";
        #print "changed gop end to $gop_end\n";
    }
	$self->VR_dprint("Now we should read the range $gop_offset to $gop_end\n") if $self->{debug};
	
	my $raw_chunk = ${ $self->read_chunk ($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;#
		$packet_counter++;
	}

	$self->VR_dprint ("returning " . length($video_stream) . " bytes of video data\n");
	$raw_chunk = "";
    
    my %GOP_info;
    my $seqstart = pack("H8", "000001b3");
    
    
    
    if ($video_stream !~ /$seqstart(.{8})/s) {
        print "No luck here...\n";
        print "length is " . (length($video_stream)) ."\n";
    }
    
    my $bitview = unpack("B64", $1);
    my $hor_size = "0b" . substr($bitview,0,12);
    my $horizontal_size = oct $hor_size; 
    
    my $ver_size = "0b" . substr($bitview,12,12);
    my $vertical_size = oct $ver_size;
    
    my $asp_ratio = substr($bitview,24,4);
    my $aspect_ratio;
    if ($asp_ratio eq "0001"){
        $aspect_ratio = "1:1";
    }
    elsif ($asp_ratio eq "0010") {
        $aspect_ratio = "4:3";
    }
    elsif ($asp_ratio eq "0011") {
        $aspect_ratio = "16:9";
    }
    elsif ($asp_ratio eq "0111") {
        $aspect_ratio = "2.21:1";
    }
    else {
        $aspect_ratio = "$asp_ratio";
    }
    
    
    my $fps = substr($bitview,28,4);
    if ($fps eq "0001")    {
        $fps = 23.967;
    }
    elsif ($fps eq "0010")    {
        $fps = 24;
    }
    elsif ($fps eq "0011")    {
        $fps = 25;
    }
    elsif ($fps eq "0100")    {
        $fps = 29.97;
    }
    elsif ($fps eq "0101")    {
        $fps = 30.97;
    }
    my $bitrate_value = 400 * ( oct ("0b" . substr($bitview,32, 18)));
    $self->VR_dprint("Got the following infos:\n\taspect ratio: $aspect_ratio\n\tsize: $horizontal_size" . "x$vertical_size\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);
	$self->VR_dprint("IN get_movie_chunk: wanted frame $frame_offset, got $frameno at $gop_offset\n") if $self->{debug};
	
	my $counter = $frameno;
	while ($gops_to_get) {
		$counter++;
		if ($counter > $self->{total_frames}) {
			$counter = $self->{last_Iframe};
		}
		if (! $self->binidx2frame($counter)) {
			next;
		}
		else {
			$gops_to_get--;
		}
	}
	my $gop_end = $self->binidx2frame($counter);
	$self->VR_dprint("Now we should read the range $gop_offset to $gop_end\n") if $self->{debug};
	
	my $raw_chunk = ${ $self->read_chunk($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 .= $payload;
		$packet_counter++;
	}

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

sub cut_movie {
    my $self = shift;
    my $out_dir = shift || "./";
    my $split = shift || 0;
    my $mainw = shift || 0;
    my $prog_ind = shift || 0;   
    my $file_size = shift || 2000000000;
   
    my $stepsize = 0;
    
    
    $self->compute_movie_cuts();
    if ($mainw && $prog_ind && $self->{movie_size_cut}) {
        $stepsize = 100 / ($self->{movie_size_cut} / 10000000);
        #print "$mainw and $prog_ind and $stepsize and $self->{movie_size_cut}\n";
    }
  
	
    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;
    my $offset = 0;
    my $EOF = 0;
    my $current_chunk = 0;
    my $current_file = 0;
    my $bytes_written = 0;
    
    
    
    ($start_offset, $start_file, $end_offset, $end_file) = split/ : | TO /, $chunks[$current_chunk];


    $current_file   = $start_file;
    $offset = $start_offset;
    
    my $IFH = ${$self->{file_handles}}[$start_file - 1];
    
    my $success = (sysseek $IFH, $start_offset, 0);
    print "seek to $success in $start_file\n";
	
	if (! $success) {
		print "could not seek, dieing..\n"; die;
	}
    print "opening | $self->{create_index}  -s $file_size -o \"$out_dir\"\n";
    open OFH, "| $self->{create_index}  -s $file_size -o \"$out_dir\"" or die "Could not open output file\n";
    binmode OFH;
    select((select(OFH), $| = 1)[0]);
    while (! $EOF) {
        my $bytes_to_read = 10000000;
        while ($bytes_to_read ) {
            print "we are now at: " . (sysseek($IFH, 0, 1)) ." in file $IFH\n";
            print "Current Chunk : $chunks[$current_chunk] \n";
            if ($current_file == $end_file) {
                if (($end_offset - $offset ) > 10000000) {
                    print "Enough bytes to read left in this file for $bytes_to_read \n";
                    my $rbytes = sysread $IFH, $rbuffer, $bytes_to_read, 0;
                    print "Read $rbytes\n";
                    if ($rbytes == 0) {
                        die "Holy shit, start_offset is $start_offset, offset is $offset, end_offset is $end_offset, diff is " . ($end_offset - $offset) ." and we are at the end of the file...?\n";
                    }
                    $bytes_to_read  -= $rbytes;
                    $buffer         .= $rbuffer;
                    $offset += $rbytes;
                    print "EE offset is now $offset, end_offset is $end_offset, diff is " . ($end_offset - $offset) ."\n";
                    #print "bytes_to_read is now $bytes_to_read\n";
                }
                else {
                    print "not enough bytes to read left in current chunk or this was the last chunk\n";
                    print "Trying to read " . ($end_offset - $offset) ."\n";
                    my $rbytes = sysread $IFH, $rbuffer, ($end_offset - $offset), 0;
                    $bytes_to_read  -= $rbytes;
                    $offset += $rbytes;
                    $buffer         .= $rbuffer;
                   
                    if ($chunks[$current_chunk + 1]) {
                        # Here we go for additional Audio at the end of a chunk
                        # So we try to red the Audio from the next GOP, and append it to the current buffer.
                        # Thereby we should always have enough Audio in the buffer, and can prevent the
                        # insertion of Silent frames
                        #
                    
                        #my $add_audio = get_audio_from_gop($self);
                    
                        #print "length before " . length($buffer) . "\n" if $self->{debug};
                        #$buffer      .= $add_audio if $add_audio;
                        #$buffer      .= $add_audio if $add_audio;
                        #print "length after " . length($buffer) . "\n" if $self->{debug};
                        #
                        #
                        $current_chunk++;
                        ($start_offset, $start_file, $end_offset, $end_file) = split/ : | TO /, $chunks[$current_chunk];
                        $current_file   = $start_file;
                        $offset = $start_offset;
                        $IFH = ${$self->{file_handles}}[$start_file - 1];
                        #print "$IFH\n";
                        $success = (sysseek $IFH, $start_offset, 0);
                        print "After Switch: $start_file}, $offset}, $end_file, $end_offset\n";
                    } else {
                        print "This was the last chunk!\n";
                        $bytes_to_read = 0;
                        $EOF = 1;
                    }
                }
            }
            else { 
                my $rbytes = sysread $IFH, $rbuffer, $bytes_to_read, 0;
                $bytes_to_read  -= $rbytes;
                $offset += $rbytes;
                $buffer .= $rbuffer;
                
                print "NE offset is now $offset, end_offset is $end_offset, diff is " . ($end_offset - $offset) ." in file $current_file\n";
                if ($bytes_to_read) {
                    print "That was not enough in this file, switching to next file $current_file\n";
                    $IFH = ${$self->{file_handles}}[$current_file];
                    $current_file++;
                    #print "$IFH\n";
                    $success = (sysseek $IFH, 0, 0);
                    $offset = 0;
                    #print "After Switch: $start_file, $start_offset, $end_file, $end_offset\n";
                } 
            }
            
            $bytes_written += length($buffer);
            
            print "in VDRrecording read $bytes_written until now\n";
            if ($mainw && $prog_ind) {
                $$prog_ind += $stepsize;
                print "$prog_ind is now prog $stepsize\n";
                $mainw->update; 
            }
           #print TOF $buffer;
            print OFH $buffer;
            
            $buffer = "";
        }
    }
    close OFH;
    my @new_chap_marks =  $self->get_cut_chapter_marks();
    open OFH, ">$out_dir/chapter_marks.vdr" or die "Could not open chapter_marks.vdr: $!\n";
    foreach (@new_chap_marks) {
        print OFH ($self->frames2mark($_) . "\n");
    }
    close OFH;
    my @new_marks =  $self->get_cut_marks();
    open OFH, ">$out_dir/marks.vdr" or die "Could not open marks.vdr: $!\n";
    foreach (@new_marks) {
        print OFH ($self->frames2mark($_) . "\n");
    }
    close OFH;
}
    
    



sub get_movie_piecewise {
	my $self = shift;
    my $bytes_to_read = shift || 10000000;
	
    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;
	}
    
    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}) > $bytes_to_read) {
                #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]) {
                    # Here we go for additional Audio at the end of a chunk
                    # So we try to red the Audio from the next GOP, and append it to the current buffer.
                    # Thereby we should always have enough Audio in the buffer, and can prevent the
                    # insertion of Silent frames
                    #
                    
                    my $add_audio = get_audio_from_gop($self);
                    print "length before " . length($buffer) . "\n" if $self->{debug};
                    $buffer      .= $add_audio if $add_audio;
                    $buffer      .= $add_audio if $add_audio;
                    print "length after " . length($buffer) . "\n" if $self->{debug};
                    #
                    #
                    $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";
            } 
        }
            
    }
    
    if ($self->{EOF}) {
        # Here we go for additional Audio at the end of a chunk
        # So we try to red the Audio from the next GOP, and append it to the current buffer.
        # Thereby we should always have enough Audio in the buffer, and can prevent the
        # insertion of Silent frames
        #
        my $add_audio = get_audio_from_gop($self);
        $buffer .= $add_audio if $add_audio;
    }


    #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 get_audio_from_gop {
    my $self = shift;
    print "current chunk is $self->{current_chunk}\n" if $self->{debug};
    print "In marks that means ${$self->{marks}}[$self->{current_chunk}*2] and ${$self->{marks}}[$self->{current_chunk}*2+1]\n" if $self->{debug};;
    print "$self->{bytes_written} bytes written for ${$self->{cut_chunks}}[$self->{current_chunk}]\n"if $self->{debug};;
    
    my $search_frame = mark2frames($self, ${$self->{marks}}[$self->{current_chunk}*2+1]);
    
    #Check whether the next chunk is just behind, then we skip additional Audio
    if (${$self->{marks}}[$self->{current_chunk} * 2 + 2]) {
        my $next_frame = mark2frames($self, ${$self->{marks}}[$self->{current_chunk}*2 + 2]);
        if (($next_frame - $search_frame) < 36) {
            return;
        }
    }
 
    print "searching for additional GOP starting at $search_frame\n" if $self->{debug};
    my $counter = 0;
 
    if (! $self->binidx2frame($search_frame)) {
        print "did not find additional GOP at end of this chunk\n";
        return;
    }
    
    my ($frameno, $gop_offset) = find_GOP_offset($self, $search_frame);
	print "wanted frame $search_frame, got $frameno at $gop_offset\n" if $self->{debug};
	
	    
    $self->VR_dprint("Next GOP should start at frame $search_frame\n") if $self->{debug};
       
    my $gops_to_get = 2;
    
    $counter = $search_frame;
	
    while ($gops_to_get) {
		$counter++;
		if ($counter > $self->{total_frames}) {
			$counter = $self->{last_Iframe};
		}
		if (! $self->binidx2frame($counter)) {
			next;
		}
		else {
			$gops_to_get--;
		}
	}
	my $gop_end = $self->binidx2frame($counter);
	
    print "Now we should read the range $gop_offset to $gop_end\n" if $self->{debug};
	
	my $raw_chunk = ${ read_chunk ($self, $gop_offset, $gop_end) };
	my $offset = 0;
	my $audio_buffer = "";
	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";
			$offset +=  $plength + 6;
			next;
		}
		if (($offset + $plength) > (length($raw_chunk))) {
			die "Did not get a rawchunk back that ends with a packet...\n";
			return \$audio_buffer;
		}
		
		my $packet = substr($raw_chunk, $offset, ($plength + 6));
		my $payload = (substr($packet, 9 + $header_length));
		$offset +=  $plength + 6;
		#print "adding a packet for stream $streamcode\n";
        $audio_buffer .= $packet;
        $packet_counter++;
	}

	print "returning " . length($audio_buffer) . " bytes of audio packet data\n" if $self->{debug};
	return $audio_buffer;
}

sub set_movie_position {
    my $self = shift;
    my $frameno = shift || 1;
    my $return_no = 1;
    my $chunk_no  = 1;
    my @marks = @{$self->{marks}};
    if (! @marks) {
        push @marks,"0:00:00.1";
    }
    
    if ($frameno < $self->mark2frames($marks[0])) {
        $return_no =  $self->mark2frames($marks[0]);
    }
    if ($frameno > $self->mark2frames($marks[-1])) {
        $return_no = $self->mark2frames($marks[0]);
    }
        
    for (my $j = 0; $j < scalar(@marks); $j +=2) {
        my $ok = 0;
        my $sf = $self->mark2frames($marks[$j]);
        print "comparing with mark $marks[$j] at frame $sf \n";
        if ($sf <= $frameno) {
            my $ef = $self->mark2frames($marks[$j+1]);
            if ($ef >= $frameno) {
                $return_no = $frameno;
                $chunk_no = ($j + 2) / 2;
            }
        } 
    }
    my ($offset, $current_file) = split( / : /,$self->binidx2frame($return_no));
    print "Setting offset, cureent_file and chunk_no to $offset, $current_file and $chunk_no for the curent position...\n";
    $self->{current_chunk}  = $chunk_no - 1;
    $self->{current_file}   = $current_file;
    $self->{current_offset} = $offset;
}


sub compute_movie_cuts {
	my $self = shift;
	
	my @chunks;
	my @marks                  = @{$self->{marks}};
	my @file_sizes             = @{$self->{file_sizes}};
    my $total_frames_cut    = 0;
    $self->{movie_size_cut} = 0;
    
    
    
    if (scalar(@marks) < 2) {
        print "no cutting without marks...\n";
        push @marks, "0:00:00.01";
        push @marks, $self->frames2mark($self->{last_Iframe});
        print "Since there were no marks, we created fake ones: " . (join " ", @marks) . "\n";
    }
    
    # Here we check whether the marks are just remains from an already cut movie
    # Criteria: First and last I-Frame are marked, and the diff between the marks inside are smaller than 20
    
    my $is_cut = 1;
    if (($marks[0] eq "0:00:00.01") && (mark2frames($self, $marks[-1]) == $self->{last_Iframe})) {
        for (my $i = 1; $i < scalar(@marks) - 2; $i += 2) {
            if ((mark2frames($self,$marks[$i+1]) - mark2frames($self,$marks[$i])) < 20) {
                next;
            } else {
                $is_cut = 0; 
                last;
            }
        }
    } else {
        $is_cut = 0;
    }    
    
    if ($is_cut) {
        print "CAUTION: You specified the -cut Option, but the marks look like remains of an already cut movie!\nI will process the full movie!\n";
        my $startframe = mark2frames($self, $marks[0]);
        my $startpos   = $self->binidx2frame($startframe);
        my $endframe   = mark2frames($self, $marks[-1]);
        my $endpos     = $self->binidx2frame($endframe);
        if ((! $startpos) || (! $endpos)) {
            print "Mark does not mark an Iframe, cutting stopped!\n";
            return -1;
        }
        $total_frames_cut += $endframe - $startframe;
        push @chunks, "$startpos TO $endpos";
    } else {
        for (my $i = 0; $i < (scalar(@marks)); $i += 2) {
            if ($marks[$i+1]) {
                my $startframe = mark2frames($self, $marks[$i]);
                #print "startframe is $startframe\n";
                my $startpos   = $self->binidx2frame($startframe);
                my $endframe   = mark2frames($self, $marks[$i + 1]);
                #print "Endframe is $endframe $self->{last_Iframe}\n";
                my $endpos     = $self->binidx2frame($endframe);
                if ((! $startpos) || (! $endpos)) {
                    print "Mark does not mark an Iframe, cutting stopped!\n";
                    return -1;
                }
                $total_frames_cut += $endframe - $startframe;
                push @chunks, "$startpos TO $endpos";
            }
        }
    }
		
	@{$self->{cut_chunks}} = @chunks;
	foreach (@chunks) {
		my ($start_offset, $start_file, $end_offset, $end_file) = split/ : | TO /, $_;
        if ($start_file == $end_file) {
            $self->{movie_size_cut} += $end_offset - $start_offset;
        } else {
            while ($start_file < $end_file) {
                $self->{movie_size_cut} += $self->{file_sizes}[$start_file -1] - $start_offset;
                $start_offset = 0;
                $start_file++;
            }
            
            $self->{movie_size_cut} += $end_offset;
            
        }
	}
    $self->{movie_length_cut} = HMS2sec($self, frames2mark($self, $total_frames_cut));
    
}


sub frames2mark {
	my $self = shift;
	my $now = shift;
	if (! $now) {
		$now = $self->{current_frame};
	}
    $now++;
	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 - 1 ;
	return $frame;
}


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->{bytes_read_for_chunk} = 0;
	$self->{binindex} = 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 is read
	read_marks_file($self);
    # If an exisiting chapter_marks file is found, it is read
	read_chapter_marks_file($self);

	
    $self->{frameno} = 1;
	$self->{movie_length} = frames2mark($self, $self->{total_frames});

	$self->{current_pos} = frames2mark($self, $self->{frame_pos});
	$self->{mark_no} = scalar(@{$self->{marks}});
    $self->compute_movie_cuts;
}
sub HMS2sec {
    my $self = shift;
    my $HMS = shift;
      
    my ($h, $m, $s, $f) = split /:|\./, $HMS;
    return ($h * 3600 + $m * 60 + $s + $f * 0.04);
}

sub VR_dprint {
    my $self = shift;
   # print "IN VR_dprint \n";
    
    if ((! $self->{debug}) || (! $self->{debug_log})) {
        return
    }
    my $msg = shift;
    print "$msg";
    open DFH, ">>$self->{debug_log}" or die "Can not open debug.log: $!\n";
    binmode DFH;
    print "VDRRECORDING: $msg";
    print DFH "VDRRECORDING: $msg";
    close DFH;
}

sub frames2HMS {
    my $self = shift;
    my $frames = shift;
    
    my $total_sec = int($frames * 0.04 + 0.5);
    
    my ($h, $m, $s);

    $h = int ($total_sec / 3600);
    $m = int(($total_sec - $h *3600) / 60);
    $s = int($total_sec - $h *3600 - $m * 60);
    
    return sprintf("%02u:%02u:%02u", $h, $m, $s);
}

sub binidx2frame {
    my $self = shift;
    my $frame = int (shift );
    
   
    #print "bindex has a length of " . length($self->{binindex}) .",searching for frame $frame\n";
    if ($frame > $self->{last_Iframe}) {
        print "Got only $self->{total_frames}, you wanted $frame and last IFrame is $self->{last_Iframe}...\n";
        $frame = $self->{last_Iframe};
    }
    my $offset = 8 * $frame;
    #print "going to show frame $frame of $self->{total_frames} at offset $offset\n";
    my ($c, $Ptype, $number, $reserved) = unpack ("I C C S", substr($self->{binindex}, $offset, 8));
		#print "Got $c, $Ptype, $number, $reserved\n";
    if ($Ptype == 1) {
        $self->VR_dprint("returning $c : $number\n") if $self->{debug};
        return "$c : $number" ;
    } else {
        return undef;
    }
} 

}