#!/usr/bin/perl

#
# marks2pts  (c) 2003-2004 by  Dr. Peter Sebbel, a perl script to convert VDR mark files to files containing PTS
# timestamps and back
# 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 LWP::Simple;
use Time::Local; 



my %config_hash;
my $version      = "0.1.3";



##############################
my %epg_hash;
my %rec_info;
my $pts_file;
my $recording;
my $indir;
my $mode = $0;
my $upload = 0;
my $configure = 0;
my $modify_settings = 0;
my $when = "";

parse_options(@ARGV);
read_config();

my $start_time = localtime();
dprint ("\n###\n###\n###\nStarted run at $start_time with the following parameters: " . (join " ", @ARGV) ."\n");


    
if ($configure) {
    create_config();
    exit;
}

if ($modify_settings) {
    modify_server_settings();
    exit;
}


if ($when) {
    if ($when =~ /before/) {
        create_rec_info();
        exit;
    } elsif ($when =~ /edited/) {
        if ($config_hash{after_cutting} =~ /^(Y|y|Yes|yes)$/) {
            $mode = "marks2pts";
            $indir =~ s/\/%/\//;
            if (! -e "$indir/sharemarks.conf") {
                dprint ("Tried to find sharemarks.conf in the original recording dir, but failed. No upload after editing\n");
                exit 1;
            }
            $upload = 1;
        } else {
            exit;
        }
    }
} 


if  ((!$indir) || (!-d $indir)) {
    print "Input Directory not specified or non-existing\n";
    usage();
}

%rec_info = get_rec_info();
$recording = VDRRECORDING->new(
    debug     => $config_hash{debug},
    debug_log =>"$config_hash{debug_log}",
    
    );
$recording->init_recording($indir);


if ($mode =~ /marks2pts/) {
    dprint( "Invoked by marks2pts, looking for $indir/marks.vdr\n");
    my @ptsmarks = @{create_pts_marks()};
    if (! $upload) {
       print "Why don't you upload your file....?\n";
       exit 0;
    }
    upload_ptsmarks(@ptsmarks);
} 
elsif ($mode =~ /pts2marks/) {
    dprint( "Invoked by pts2marks, \n");
    $pts_file = "$indir/ptsmarks.vdr";
   
    if (! -e $pts_file) {
        dprint( "$pts_file not found, trying to download\n");
   
        my $result = download_ptsmarks($indir);
        dprint( "Got $result from download\n");
        if ($result ne "Success") {
            print "Keine Schnittmarken gefunden!\n";
            exit 1;
        }
    } else {
        dprint( "$pts_file found\n");
    }
    create_normal_marks()
} else { 
    die "The name of the script must be marks2pts or pts2marks and not $mode\nThe mode of operation is determined by its name!\n";
}


sub parse_options {
    my @params = @_;
    
    while ($params[0]) {
        
        my $parm = shift @params;
        if ($parm =~ /-upload/) {
            $upload = 1;
    
        } 
        elsif ($parm =~ /-configure/) {
            $configure = 1;
        }
        elsif ($parm =~ /-server-settings/) {
            $modify_settings = 1;
        }         
        elsif ($parm =~ /^\//) {
            $indir = $parm;
            if (! ($indir =~ /\/$/)) {
                $indir .= "/";
            }
        }
        elsif ($parm =~ /(before|after|edited)/) {
            $when = $parm;
        } 
        else {
            print "Unknown Option: $parm\n";
            usage();
        }
    }
    
}

sub modify_server_settings {
    #print "not yet implemented...\n";
    
    if (!$config_hash{user_name}) {
        print "*" x20 . "\n";
        print "To register or change server settings a user name and a password are needed\nStarting confguration\n";
        print "*" x20 . "\n\n";
        create_config();
    }
    
    my $url = "http://xpix.dieserver.de/cgi/pts/pts2.cgi?action=";
    my $add_cmd = "add_user&user=$config_hash{user_name}&offset=$config_hash{gop_drift}&password=$config_hash{passwd}";
    my $chg_cmd = "change_user&user=$config_hash{user_name}&offset=$config_hash{gop_drift}&password=$config_hash{passwd}";
    
    print "*" x 40 ."\nTrying to create / modify your server settings...\n" . "*" x 40 . "\n";
    print "For registration with your current settings press:  1)\n";
    print "To change your current offset press:                2)\n";
    print "To cancel press:                                    3)\n";
    print "Settings: user name: $config_hash{user_name}\npassword: $config_hash{passwd}\nOffset: $config_hash{gop_drift}\n";
    my $choice = 0;
    while (! $choice) {
    
        $choice = <STDIN>;
        chomp $choice;
        #print "read $choice\n";
        if ($choice !~ /[1-3]/) {
            print "Please enter 1, 2, or 3\n";
            $choice = 0;
        }
    }
    if ($choice == 3) {
        print "No changes were made, exiting\n";
        exit;
    }
    if ($choice == 1) {
        print "*" x 40 ."\nTrying to register\n" . "*" x 40 . "\n";
        $url .= $add_cmd;
        my $content = get("$url");
        print "received: $content\n";
        exit;
    }
    if ($choice == 2) {
        print "*" x 40 . "\nTrying to change offset\n". "*" x 40 . "\n";
        $url .= $chg_cmd;
        my $content = get("$url");
        print "received: $content\n";
        exit;
    }
    
    
}
sub read_config {
    my $config_file;
    my $dummy;
    my $home_dir = (getpwuid($>))[7];
    if (-e "$home_dir/.marks2pts.conf") {
        $config_file = "$home_dir/.marks2pts.conf";
    } elsif (-e "/etc/marks2pts.conf") {
        $config_file = "/etc/marks2pts.conf";
    } else {
        if (! $when) {
            $config_file = create_config();
        } else {
            dprint("Found no config file while run non-interactively, exiting\n");
            die "Found no config file while run non-interactively, exiting\n";
        }
    }

    {
    
    local( $/, *FH ) ;
    open FH, "$config_file" or die "Found config file $config_file but can't open it...\n";
    binmode FH;
    $dummy = <FH>;
    }
    %config_hash = $dummy =~ /^(\w+)=(\S+)$/mg ;
    
    
}


sub create_config {
    
    if ($when) {
        die "Called non interactively to create config, exiting\n";
    }
    my $home_dir = (getpwuid($>))[7];
    
    my %conf_question = (
    video => {
        "q" => "What is the main directory for recordings",
        "a" => "/video",
        "no" => 10,
    },
    epg => { 
        "q" => "Where is epg.data located",
        "a" => "/video/epg.data",
        "no" => 20,
    },
    channel => { 
        "q" => "Where is channels.conf located",
        "a" => "/video/channels.conf",
        "no" => 30,
    },
    timer => { 
        "q" => "Where is timers.conf located",
        "a" => "/video/timers.conf",
        "no" => 40,
    },
    use_svdrp => { 
        "q" => "Do you want status messages on the OSD (sent via svdrp)?",
        "a" => "No",
        "no" => 41,
    },
    svdrp => { 
        "q" => "Where is svdrpsend.pl located (only needed if OSD messages are enabled)?",
        "a" => "/usr/bin/svdrpsend.pl",
        "no" => 42,
    },
    
    fake_source => { 
        "q" => "What is your source",
        "a" => "S19.2E",
        "no" => 50,
    },
    gop_drift => { 
        "q" => "How big is the offset I need to correct (in GOPs)",
        "a" => "0",
        "no" => 60,
    },
    user_name => { 
        "q" => "What is your Username on the server (leave empty if you are not registered)",
        "a" => "",
        "no" => 70,
    },
    passwd => { 
        "q" => "What is your Password on the server (leave empty if you are not registered)",
        "a" => "",
        "no" => 80,
    },
    after_cutting => { 
        "q" => "Do you want to upload your marks automatically after cutting?",
        "a" => "Yes",
        "no" => 81,
    },
    debug => { 
        "q" => "Do you want to enable debug messages?",
        "a" => "Yes",
        "no" => 90,
    },
    debug_log=> { 
        "q" => "Where should I place the debug file?",
        "a" => "$home_dir/sharemarks_debug.log",
        "no" => 91,
    },
    );
    
    print "*" x 40 ."\nTrying to create a configuration file...\n" . "*" x 40 . "\n";
    
    for (sort {  $conf_question{$a}{no} <=>  $conf_question{$b}{no} } keys %conf_question)
    #foreach(keys(%conf_question)) {
    {
        my $default_a; 
        if ($config_hash{$_}) {
            $default_a = $config_hash{$_};
            #print "taking setting from existing config $config_hash{$_}\n";
        } else {
            $default_a = $conf_question{$_}{a};
        }
        print "$conf_question{$_}{q} [$default_a]? ";
        my $a = <STDIN>;
        chomp $a;
        if ( $a eq "") {
            $a = $default_a;
        }
        $config_hash{$_} = $a;
    }
    print "*" x 40 ."\nI am going to write this settings\n" . "*" x 40 . "\n";
    foreach (keys(%config_hash)) {
        print " $_\t : $config_hash{$_}\n";
    }
    
    my $config_file = "$home_dir/.marks2pts.conf";
    if ( $> == 0) {
        print "\nSince you are root you should write to /etc/marks2pts.conf\n";
        print "\nWhere do you want to create the config file [/etc/marks2pts.conf]?";
        $config_file = <STDIN>;
        chomp $config_file;
        if (! $config_file) {
            $config_file = "/etc/marks2pts.conf";
        }
    } 
    print "\nCreating config file as $config_file\n";
    open OFH, ">$config_file\n" or die "Can not open $config_file for writing\n";
    binmode OFH;
    foreach (sort(keys(%config_hash))) {
        print OFH "$_=$config_hash{$_}\n";
    }
    close OFH;
    return $config_file;
    
}


sub upload_ptsmarks {
    
    my @ptsmarks = @_;
    my $put_url = "http://xpix.dieserver.de/cgi/pts/pts2.cgi?channel=$rec_info{nchid}&marks=";

    foreach(@ptsmarks) {
        #print "$_\n"; 
        $put_url .= "$_-";
    }
    
    chop $put_url;
    if (($config_hash{user_name}) && ($config_hash{passwd})) {
        $put_url .= "&user=$config_hash{user_name}&password=$config_hash{passwd}";
    }
    dprint( "*"x40 ."\n$put_url\n");
    my $content = get("$put_url");
    dprint( "received: $content\n");
    if (! $content) {
        print "Could not connect to server!\n";
    }
    if ($content !~ /Danke/) {
        if ($content =~ /Sorry, this pts\(\d+\) in Channel/) {
            dprint ("At least one of the ptsmarks is already on the server!\n");
            #print "At least one of the ptsmarks is already on the server!\n";
        } else {
            print "Upload did not work\n";
            dprint ("upload did not work\n");
        }
    } else {
        my @thanx = ($content =~ /Danke/g);
        print "Es wurden " . scalar(@thanx) ." Schnittmarken gespeichert.\n"
    }
}

sub create_sharemarks_conf {
    
    if ($when) {
        return -1;
    }
    my ($dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$size,
    $atime,$mtime,$ctime,$blksize,$blocks)
    = stat "$indir/index.vdr";
    

    my $date_part = (split/\//, $indir)[-1];
    my ($year, $mon, $day, $hour, $min) = $date_part =~ /(\d{4})-(\d{2})-(\d{2}).(\d{2})[.:](\d{2})/;
    my $start = tm2unix("$day.$mon.$year $hour:$min");

    open FH, $config_hash{channel} or die "can not open: $!\n";
    my @channels = (<FH>);
    close FH;

    my $i;
    my $j;
    my $channel;
    for ($i = 0; $i <= scalar(@channels) ; $i += 20 ) {
        for ($j = 0; $j < 20; $j++) {
            print $i+$j ." " .   $channels[$i + $j]  if $channels[$i + $j];
 
        }
        my $dummy = <STDIN>;
        chomp $dummy;
        if ($dummy =~ /^\d+$/) {
            $channel = $channels[$dummy];
            last;
        }
    }

    if (! $channel) {
        print "You have to choose a valid channel!\n";
        return -1;
    }



    my ($name, $freq, $parm, $source, $srate, $vpid, $apid, $tpid, $ca, $sid)= split /:/,$channel;
    if ( $config_hash{fake_source}) {
        $source = $config_hash{fake_source};
    }
    
    if ($apid !~ /^\d+$/) {
        dprint( "Changing audio PID $apid to ");
        my @apids = split /[,;]/, $apid;
        for (my $i = 0; $i < scalar(@apids); $i++) {
            $apids[$i] =~ s/[^\d]//g;
        }
        $apid = (sort(@apids))[0];
        dprint(" $apid\n");
    }
       
    my $cid  = "$source-0-$freq-$sid";
    my $nchid = "$source-$sid-$vpid-$apid";


    dprint( "\n\nAssuming recording from \nchannel $name \nfrom " . localtime($start) . " \nto " . localtime($mtime) . "\n");

    dprint( "Writing the following to $indir/sharemarks.conf:\n");
    dprint( "channel $cid\n");
    dprint( "nchid   $nchid\n");
    dprint( "start   $start\n");
    dprint( "end     $mtime\n");

    open  FH, ">$indir/sharemarks.conf" or die "Can not open file for writing: $!\n";
    print FH "channel $cid\n";
    print FH "nchid   $nchid\n";
    print FH "start   $start\n";
    print FH "end     $mtime\n";
    close FH;
    return 0;
}

sub get_rec_info {
    
    if (! -e "$indir/sharemarks.conf") {
        if ($when) {
            dprint("Now sharemarks.conf found in $indir, and running from script. Just exiting\n");
            exit;
        }
        dprint( "Could not find sharemarks.conf in $indir, trying to create one...\n");
        if (create_sharemarks_conf() != 0) {
            die "Failed to create sharemarks.conf, exiting\n";
        }
    }
    
    open FH, "$indir/sharemarks.conf";
    binmode FH;
    my @conf = (<FH>);
    my $jconf = join "", @conf;
    (my $channel) =  $jconf =~ /channel\s+(.*)\n/;
    (my $nchid)   =  $jconf =~ /nchid\s+(.*)\n/;
    (my $start)   =  $jconf =~ /start\s+(.*)\n/;
    (my $end)     =  $jconf =~ /end\s+(.*)\n/;
    
    if ( (! $channel) ||
         (! $start  ) ||
         (! $end    ) ) {
        die "Invalid sharemarks.conf!\n";
    }
    
    if (! $nchid) {
        $nchid = channel2nchid($channel);
        if (! $nchid) {die "Could not construct new channel id\n";}
    }
    
    return (
           channel => "$channel",
           nchid   => "$nchid",
           start   => "$start",
           end     => "$end",
           );
}

sub channel2nchid {
    my $channel = shift;
    if (! -e $config_hash{channel}) {
        die "Can not find channel.conf at $config_hash{channel}\n";
    }
    
    my ($tsource, $tnid, $tfreq, $tsid, undef) = split /-/, $channel;
    my $ttid = 0;
    #print "got $tsource, $tfreq, $tnid, $tsid from timer channel\n";
    
    open FH, "$config_hash{channel}" or die "Could not open $config_hash{channel}: $!\n";
    binmode FH;
    my @channels = (<FH>);
    close FH;
    foreach (@channels) {
        if ($_ =~ /^:/) {next}
        my (undef, $freq, undef, $source, undef, $vpid, $apid, $tpid, undef, $sid, $nid, $tid, undef) =
               split /:/, $_;
        #print "Analysing channel entry $_";
        #print "extracted $freq, $source, $vpid, $apid, $tpid, $sid, $nid, $tid, \n";
        if ($nid && $tid && $tnid && (! $ttid))  {
            #print "Found nid and tid, assuming AutoPID and looking for TID instead of freq\n";
            $ttid = $tfreq;
            $tfreq = 0;
        }
        
        #print "Testing $freq, , $source, $vpid, $apid, $sid\n";
        if ($apid !~ /^\d+$/) {
            dprint( "Changing audio PID $apid to ");
            my @apids = split /[,;]/, $apid;
            for (my $i = 0; $i < scalar(@apids); $i++) {
                $apids[$i] =~ s/[^\d]//g;
            }
            $apid = (sort(@apids))[0];
            dprint(" $apid\n");
        }
        if (($source eq "C") && (! $ttid)) {
            $freq  = substr ($freq, 0, 3);
            $tfreq = substr ($tfreq, 0, 3);
        }
        #print "Testing from channel: $freq, $source, $vpid, $apid, $sid, $tid against from Timer: $tfreq, $tsource, $tsid,  $ttid\n";
        if (($sid     == $tsid) &&
            ($source  eq $tsource)) {
            #print "matched source and sid\n";
            if (($tid == $ttid) || ($freq == $tfreq)) {
                #print "Found channel: $_\n";
                if ($config_hash{fake_source}) {
                    $source = $config_hash{fake_source};
                }
            return "$source-$sid-$vpid-$apid";
            }
        }
    }
    return 0;
}

sub create_rec_info {
    
    if ($when !~ "before") {
        dprint( "Not at start of a recording, but at $when, exiting\n");
        exit;
    }
    
    dprint("looking for timers\n");
    my @timer_hits = @{ get_channel_from_timers($indir) };
    
    if (!@timer_hits) {
        dprint("could not extract the channelID from timers.conf\n");
        die "could not extract the channelID from timers.conf";
    }
    my $conf_info = $timer_hits[0];
    
    if (scalar(@timer_hits) != 1) {
            dprint("Failed to get timer\n");
            die "Failed to determine the channel-id of this recording\n";
    }
    dprint("Writing conf $indir/sharemarks.conf $conf_info\n");
    
    my $pid;
    if ($pid = fork) {
        dprint("parent exiting\n");
        exit;
        # parent here
        
    }
    else {
        dprint("child sleeping\n");
        sleep 10;
        open  FH, ">$indir/sharemarks.conf" or die "Can not open $indir/sharemarks.conf: $!\n";
        binmode FH;
        print FH $conf_info;
        close FH;
        dprint("Wrote conf\n");
        exit;    
    }
}
    


sub get_channel_from_timers {
    
    my @time_hits;
    my @hits;
    
    my @dir_parts = split /\//, $indir;
    
    my $date = $dir_parts[-1];
    
    my ($year, $mon, $mday, $hour, $min) = $date =~ /(\d{4})-(\d{2})-(\d{2})[.:](\d{2})[.:](\d{2})/;
    if ((! $year) || (! $mon) || (! $mday) || (! $hour) || (! $min)) {
        die "Could not extract a date from $date\n";
    }
    my $start = tm2unix("$mday.$mon.$year $hour:$min");
       
    open FH, $config_hash{timer} or die "could not open file: $!\n";
    binmode FH;
    my @timers = (<FH>);
    close FH;   
    
    foreach (@timers) {
        dprint( "Checking $_\n");
        my ($status, $cid, $tday, $tstime, $tetime, $prio, $lifetime, $title) = (split/:/,$_)[0..7];
        my $tshour = substr($tstime, 0, 2);
        my $tsmin  = substr($tstime, 2, 2);
        my $tehour = substr($tetime, 0, 2);
        my $temin  = substr($tetime, 2, 2);
        
        if (! $status) {
            next;
        }
        
        if ($tday !~ /^\d{1,2}$/) {
            my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime();
            
            if (substr($tday, ($wday - 1), 1) !~ /-/) {
                dprint( "ok, this timer should be active today...\n");
                $tday = $mday;
            } else {
                dprint( "not active on $wday: " . substr($tday, $wday, 1) ."\n");
                next;
            }
        }

        my $s_uxtime = tm2unix("$tday.$mon.$year  $tshour:$tsmin");
        my $e_uxtime = tm2unix("$tday.$mon.$year  $tehour:$temin");
        
        if ($e_uxtime < $s_uxtime) {
                $e_uxtime += 86400;
        }
        $title =~ s/[^-a-zA-Z1-9]/.+?/gm;
        
        my $title_regex = qr/$title/;

        if (abs($start - $s_uxtime) < 180) {
            my $nchid = channel2nchid($cid);
            if (! $nchid) {die "conversion to nchid did not work\n";}
            push @time_hits, "channel $cid\nchid    $nchid\nstart   $s_uxtime\nend     $e_uxtime\n";
            if ($indir =~ $title_regex) {
                dprint ("$indir  matched $title_regex\n");
                push @hits, "channel $cid\nchid    $nchid\nstart   $s_uxtime\nend     $e_uxtime\n";
            }
        } 
    }
    if ((! @hits) && (scalar(@time_hits) == 1)) {
        @hits = @time_hits;
        dprint( "Timer confirmed only by time, not by name...\n");
    }
    return \@hits;
}


sub download_ptsmarks {
    my $indir = shift;
    my $from  = $rec_info{start} - 300;
    my $to    = $rec_info{end}   + 300;
    my $url   = "http://xpix.dieserver.de/cgi/pts/pts2.cgi?channel=$rec_info{nchid}&from=$from&to=$to&txt=1"; # 
    if (($config_hash{user_name}) && ($config_hash{passwd})) {
        #$url .= "&user=$config_hash{user_name}&password=$config_hash{passwd}";
    }
    
    dprint( "retrieving $url\n");
    (my $content) = get($url);
    dprint( "got $content\n");
    my @ptsmarks;
    my @uxtime;
    if (! $content) { 
        print "No records in the database, sorry\n";
        return "Failure";
    }
    
    while ($content =~ /zeit\s+(\d{4})-(\d{2})-(\d{2}) (\d{2}:\d{2}:\d{2})\n[^\n]+\npts\s+(\d+)\n/g) {
        my $year  = $1;
        my $month = $2;
        my $mday  = $3;
        my $time  = $4;
        my $mark  = $5;
        dprint( "Matched $time and $mark\n");
        push @ptsmarks, $mark;
        push @uxtime, tm2unix("$mday.$month.$year $time");
    }
    
    if (! @ptsmarks) {
        dprint ("Could not download ptsmarks....\n");
        return "Failure";
    }
  
    open PTSFH, ">$indir/ptsmarks.vdr" or die "$!\n";
    binmode PTSFH;
    for (my $i = 0; $i < scalar(@ptsmarks); $i++) {
        print PTSFH "$ptsmarks[$i]_$uxtime[$i]\n";
    }
    close PTSFH;
    return "Success";
}


sub create_pts_marks {
    if ( scalar@{$recording->{marks}} < 2) {
        dprint("Need at least 2 marks in marks.vdr for conversion to ptsmarks\n");
        die "Need at least 2 marks in marks.vdr\n";
    }
    my @ptsmarks;
    dprint ("Starting conversion from marks to ptsmarks\n");
    open OFH, ">$indir/ptsmarks.vdr" or die "$!\n";
    binmode OFH;
    foreach (@{$recording->{marks}}) {
        my $pts = mark2pts($_);
        dprint ("Got $pts\n");
        print OFH $pts ."\n";
        push @ptsmarks, $pts
    }
    close OFH;
    dprint( "Output is $indir/ptsmarks.vdr\n");
    return  \@ptsmarks;
    
}

sub create_normal_marks {
    
    my @raw_pts;
    
    dprint( "Converting $pts_file\n");
    open PFH, $pts_file or die "$!\n";
    binmode PFH;
    while(<PFH>) {
        chomp $_;
        push @raw_pts, $_;
    }
    close PFH;
    my $first_pts = (split/_/, mark2pts("00:00:00.1"))[0];
    #print "The first PTS of the recording is: $first_pts\n";
    
    my $last_IFrame = ${$recording->{Iframe_list}}[-2];
    my $last_gop_start = $recording->{Iframes}{$last_IFrame};
    my ($gop, $dummy) = $recording->get_gop_at(($last_IFrame/25),1);
    my $last_pts = extract_first_pts($gop);
    #print "The first PTS of the recording is: $last_pts\n";
        
    if (-e "$indir/marks.vdr") {
        my $err = system ("mv $indir/marks.vdr $indir/marks.vdr.orig");
        if ($err) {die "Something happend while backing up original mark file: $!...\n";}
    }
    my @marks;
    
    foreach (@raw_pts) {
        my $hit = pts2mark($_, $first_pts, $last_pts);
        
        if ($hit eq "-1") {
            print "Found no matching PTS in the recording ...\n";
            print "Skipping this mark\n";
            next;
        }
        elsif ($hit) {
                push @marks, $hit;
                #print  "$mark\n";
        }
    }
    @marks = sort(@marks);
    open OFH, ">$indir/marks.vdr" or die "$!\n";
    binmode OFH;
    print OFH (join"\n",@marks) ."\n";
    close OFH;
    dprint ("Output is: $indir/marks.vdr\n");
    print scalar(@marks) . " Schnittmarken gefunden!\n";
}

sub pts2mark {
    
    my ($pts, $first, $last)  = @_;
    
    my $hit   = 0;
    my $overflow    = 0;
    my $cutted      = 0;
    my $constant    = 8589934592;
    my $last_IFrame = ${$recording->{Iframe_list}}[-2];
    my $frame = 1;
    
    if (($last - $first) < 0) {
        dprint( "PTS Overflow detected\nCorrecting last from $last to");
        $overflow =  1;
        $last     += $constant;
        dprint( " $last\n");
    }
    my $pts_frameno = ($last - $first) / 3600;
    #print "$pts_frameno against $recording->{total_frames}\n";
    if (abs((($last - $first) / 3600) - $recording->{total_frames}) > 30) {
        #print "The amount of frames computed from pts and from analysing index.vdr differ, assuming cutted movie\n";
        dprint( "Cut or other problem detected, doing a slow search for the correct GOP\n");
        $cutted = 1;
    }
    
    $pts = (split /_/,$pts)[0];
   
        
    if ($overflow) {
        if ($pts < $first) {
            dprint( "Correcting ptsmark $pts to ");
            $pts += $constant;    
            dprint( "$pts\n");
        }
    }
        
    if ($pts < $first || $pts >= $last) {
        dprint( "ptsmarks out of range, skipping....\n");
        return -1;
    }
    my $diff = $pts - $first;
    #my $search_start = int ($diff / 3600) - 24;
    #print "diff is $diff\n";
    if ($diff == 0) {
        dprint( "Found correct pts\n");
        $hit = 1;
        #return "0:00:00.01";
    }
    
    if (! $cutted) {
        ($frame, undef) = $recording->find_GOP_offset(int (($diff / 3600) - 36));
    }
    my $test_pts = frames2pts($frame);
    
    if ($overflow) {
        if ($test_pts < $first) {
        dprint( "Correcting testptsmark $test_pts to ");
            $test_pts += $constant;    
            dprint( "$test_pts\n");
        }
    }
    
    
    my $last_frame = $frame;
    #print "starting search for GOP with ptsmark $pts at frame $frame with ptsmark $test_pts\n";
    
   while (! $hit) {
        
        # First we check whether the calculated frame has a timestamp that is close enough to the pts we are looking for
        my $pts_diff = $pts - $test_pts;
     #   print "pts_diff is $pts_diff\n";
        if (! $cutted) {
            if ($pts_diff > 0) {                    # This means that we are before the correct GOP (that's ok)
                if ($pts_diff >  900000) {          # If we are more than 10 Secs (in ptsmarks)  before the correct GOP, step forward (this should never happen)
                    ($frame, undef) = $recording->find_GOP_offset (int($frame - (($pts_diff * 0.4 ) / 3600)));
                    $test_pts = frames2pts($frame);
                    if ($overflow && ($test_pts < $first)) {$test_pts += $constant}
                    dprint( "Continuing search for GOP with ptsmark at frame $frame, allthough this should never happen\n");
                    next;
                }
            }
        }
        else {                 
            $frame = 1;
            $pts_diff = $pts - $first;
            #print "This recording has probably been cutted, so a slow search routine must be used to identify the correct mark...\n";
            while ($pts_diff >= 0) {
                $last_frame = $frame;
                $frame += 720;
                if ($frame > $last_IFrame) {
                    $frame = $last_IFrame;
                    dprint( "reached end of file, hoping for the best\n");
                    last;
                }
                ($frame, undef) = $recording->find_GOP_offset($frame);
                
                $test_pts = frames2pts($frame);
                if ($overflow && ($test_pts < $first) ) {
                    $test_pts += $constant;
                }
                $pts_diff = $pts - $test_pts;
                #print "Testing frame $frame with pts $test_pts\n";
            }
            $frame = $last_frame;
        }
        
        #print "Starting GOP for GOP search at frame $last_frame\n";
        
        while (! $hit) {        
           # print "Searching at frame $frame\n";
            ($frame, undef) = $recording->find_GOP_offset ($frame);
            $test_pts = frames2pts($frame);
            
            if ($overflow && ($test_pts < $first)) {$test_pts += $constant}
            
            if ($test_pts > $pts) {
                dprint( "This pts $pts can not be mapped accurately\n");
                return -1;
            }
            if ($test_pts == $pts) {
                dprint( "Found correct pts\n");
                $hit = 1;
                last;
                #return ($recording->frames2mark($frame));
            }
            $frame++;
            while (! $recording->{Iframes}{$frame}) {
                $frame++;
                #print "testing frame $frame\n";
                if ($frame > $last_IFrame) {
                    dprint( "Something weird happend, $pts could not by mapped\n");
                    return -1;
                }
            }
         #   print "frame $frame should be an Iframe\n";
        }
    }
    if (($config_hash{gop_drift}) && ($frame > 1) && ($frame+2 < $recording->{total_frames}))  {
        dprint( "We need to correct the gop_drift\n");
        $frame = correct_frame_for_drift($frame, 1);
        if ($frame == -1) {
            dprint( "Correction failed somehow\n");
            return -1;
        }
    } else {
        dprint( "not correcting for $config_hash{gop_drift}\n")  if $config_hash{gop_drift}
    }
    return ($recording->frames2mark($frame));
}


sub mark2pts {
    
    my $mark = shift;
    #dprint ("converting $mark\n");
    my $frames = $recording->mark2frames($mark);
    #dprint ("mark $mark is in frames: $frames\n");
    my ($gop, $returned_frame) = $recording->get_gop_at(($frames/25),1);
    if (($config_hash{gop_drift}) && ($returned_frame > 1) && ($returned_frame+2 < $recording->{total_frames})) {
        dprint( "trying to correct $returned_frame for gop drift of $config_hash{gop_drift}\n");
        $returned_frame = correct_frame_for_drift($returned_frame, -1);
        if ($returned_frame == -1) {
            return -1;
        }
        #print "Got $returned_frame\n";
        ($gop, $returned_frame) = $recording->get_gop_at(($returned_frame/25),1);
        print "Got $returned_frame\n";
    } 
    #else { print "not correcting in marks to pts\n";
    #}
    #print ("Extracted gop with a length of " . length($gop) . " starting with frame $returned_frame\n");
    my $pts = extract_first_pts($gop);
    
    #dprint ("mark $mark has pts $pts\n");
    my $pts_time  = ($rec_info{start} + $frames * 0.04);
    
   dprint ("this ptsmark is roughly at $pts_time " . localtime($pts_time) . "\n");
   dprint ("returning $pts\n");
    return $pts . "_" . $pts_time;
}

sub correct_frame_for_drift {
    my $frame_basis = shift;
    my $drift_to_correct = $config_hash{gop_drift};
    
    my $inc = shift;                  # This is the direction of the correction, down for marks2pts and up for pts2marks
    if ($inc ==  1) {
        $drift_to_correct *= -1;
    }
    
    if ($config_hash{gop_drift} < 0) {
        $inc *= -1;
    }
    my $frame_index = $frame_basis;
    my $counter = 0;
    
    while ($drift_to_correct) {
        $counter++;
        $frame_index += $inc;
        if (($frame_index < 1 ) || ($frame_index > $recording->{total_frames})) {
            dprint( "Sorry, reached $frame_index and give up to correct frame for GOP drift...\n");
            return -1;
        }
        
        if ($recording->{Iframes}{$frame_index}) {
            
            $drift_to_correct += $inc;
        }
    }
    #dprint( "returning frame $frame_index, that is $config_hash{gop_drift} GOPs or  $counter frames  next to $frame_basis\n";
    return $frame_index;
}



# ------------------------------------------
sub tm2unix {
# ------------------------------------------
    my $value = shift || time;
    my $calc;

    if($value =~ /^\d+$/) {
        my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) =
        localtime($value);
        $calc = sprintf('%02d.%02d.%04d %02d:%02d',
        $mday, $mon+1, $year + 1900,
        $hour, $min);
    } elsif ($value =~ /(\d{1,2})\.(\d{2})\.(\d{4})\s+(\d{2})\:(\d{2})\:(\d{2})/) {
        $calc = timelocal($6, $5, $4, $1, $2-1, $3);
    } elsif ($value =~ /(\d{4})-(\d{2})-(\d{2})\s+(\d{2})\:(\d{2})\:(\d{2})/) {
        $calc = timelocal($6, $5, $4, $3, $2-1, $1);
    } elsif($value =~ /(\d{1,2})\.(\d{2})\.(\d{4})\s+(\d{2})\:(\d{2})/) {
        $calc = timelocal(0, $5, $4, $1, $2-1, $3);
    } else {
        die "Wrong Format for date: $value!";
    }
 return $calc;
}



sub read_epg_data {

 if (! -e "$config_hash{video}/epg.data") {
        die "Need the file epg.data $config_hash{video} but could not find it!\n";
    }
    open FH, "$config_hash{video}/epg.data";
    binmode FH;
    my @helper = (<FH>);
    close FH;
    my $epgs = join "", @helper;
    my @channels = split /^C /m, $epgs;
    shift @channels;
    foreach (@channels) {
        (my $cid) = $_ =~ /(\S+)/;
        #print "Found channel $cid\n";
        my @events = $_ =~ /\nE(.+?)\ne/gs;
        foreach (@events) {
           # print "Got event $_\n\n";
            (my $eventid)  = $_ =~ /\s(\S+)/;
            ($epg_hash{$cid}{$eventid}{starttime}, 
             $epg_hash{$cid}{$eventid}{duration})  = $_ =~ /\s\S+\s+(\S+)\s+(\S+)/;
            ($epg_hash{$cid}{$eventid}{title})     = $_ =~ /\nT\s(.*)/;
            if ($_ =~ /\nS\s(.*)/) {
                $epg_hash{$cid}{$eventid}{subtitle}= $1;
            }
            foreach(values(%{ $epg_hash{$cid}{$eventid} })) {
                dprint( "EXTRACTED $_\n");
            
            }
        }
    }
}

sub frames2pts {
    my $frames = shift;
    #my $frames = $recording->mark2frames($mark);
    #print "frames: $frames\n";
    my ($gop, $dummy) = $recording->get_gop_at(($frames/25),1);
    my $pts = extract_first_pts($gop);
    return $pts;
}


sub extract_first_pts {

    my $buffer = shift;        
    #print "buffer has length of " . length($buffer) . "\n";
    my $offset = 0;
    my %pes_header;
    my $rbytes = 0;
    my $head_tmpl = 'H6 H2 n1 B8 B8 C1 B40';
    my $first_ts = 0; 
    my $packet = "";
    my $md5sum = "";
    while (! $first_ts) {

        ($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($buffer, $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") {
            print "Found $pes_header{startcode} instead\n";
            open DFH, ">./debug_GOP.pts" or die "$!\n";
            binmode DFH;
            print DFH $buffer;
            close DFH;
            die "No 0x000001 found at current packet, dying\n";
        }
        # We check whether a TimeStamp is present:
        $pes_header{PTS_DTS_flags} = substr($pes_header{eflags2}, 0, 2);
        
        if (($pes_header{PTS_DTS_flags} eq "10") || ($pes_header{PTS_DTS_flags} eq "11")) {
            $pes_header{PTS_value_bits} = substr($pes_header{PTS_raw_bits},4,3) . substr($pes_header{PTS_raw_bits},8,15) . substr($pes_header{PTS_raw_bits},24,15);
            # decode the timestamp
            $pes_header{PTS_decimal} = unpack("N", (pack ("B32", substr($pes_header{PTS_value_bits},1))));
            $pes_header{PTS_decimal} += 4294967296 if (substr($pes_header{PTS_value_bits},0,1) == 1);
  
        }
        else {
            $pes_header{PTS_decimal} = 0;
            
        }
        if ($pes_header{PTS_decimal}) {
           $first_ts = $pes_header{PTS_decimal};
        }
        $packet = substr($buffer, $offset, $pes_header{PES_packet_length} + 6);
        
       # open PFH, ">./packet" || die "could not open File: $!\n";
       # binmode PFH;
       # print PFH $packet;
       # close PFH;
       # $md5sum = `md5sum packet`;
       # $md5sum = (split / /,$md5sum)[0];
        #print "Got md5sum $md5sum\n";
        #system "echo \"$packet\" | md5sum";
        $offset +=  $pes_header{PES_packet_length} + 6;
    }
    #print "$first_ts comes from a Packet with md5sum: $md5sum\n";
    return $first_ts;
}



sub usage {
    print "marks2pts Version $version\n";
    print "usage: marks2pts /Path/to/recording  OR\n";
    print "       marks2pts --configure         OR\n";
    print "       marks2pts --sever-settings    OR\n";
    print "       pts2marks /Path/to/recording  OR\n";
    die   "       marks2pts [before|after|edited] /Path/to/recording (called from vdr)\n";
}
sub dprint {
    if ($config_hash{debug} ne "Yes") {
        return; # 
    }
    my $msg = shift;
    open DFH, ">>$config_hash{debug_log}";
    binmode DFH;
    #print $msg;
    print DFH $msg;
    close DFH;
}

#
# Very broken and early Version of a Perl Module that should allow standard manipulations of a VDR recording like 
# setting marks, cutting, retrieving certain pieces of the recording, or pieces of certain streams
#


package      VDRRECORDING;
require      Exporter;

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

our $VERSION   = "0.0.2";		        # Version number


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

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

sub find_GOP_offset{
	my $self = shift;
	my $frame = int (shift);
	#print "got frame $frame\n";
	if ($self->{Iframes}{$frame}) {
		return ($frame, $self->{Iframes}{$frame});
	}
	my $return_frame = 0;
	my $i = 1;
	while ((! $return_frame) && ($i < 10)) {
		if ($self->{Iframes}{$frame - $i}) {
			VR_dprint ($self, "Frame $i frames before $frame is an I-Frame. Returning " . ($frame - $i) . " " .$self->{Iframes}{$frame - $i} . "\n") if $self->{debug};;
			return ($frame - $i , $self->{Iframes}{$frame - $i});
		}
		elsif ($self->{Iframes}{$frame + $i}) {
			VR_dprint ($self, "Frame $i frames behind $frame is an I-Frame. returning " . ($frame + $i) . " " .$self->{Iframes}{$frame + $i} . "\n") if $self->{debug};;
			return ($frame + $i , $self->{Iframes}{$frame + $i});
		}
		else {
			VR_dprint ($self, "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;
    (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;
	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_f} = $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;
	}
	#foreach (@{$self->{file_handles}}) {
	#	print "handle: $_\n";
	#}
	#foreach (@{$self->{file_sizes}}) {
	#	print "size: $_\n";
	#}
};

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


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


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

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


sub get_gop {
	my $self = shift;
	my $sec = shift;
	my $gops_to_get = shift;
	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);
	VR_dprint ($self, "In get_gop: 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->{Iframe_list}}[-1];
		}
		if (! $self->{Iframes}{$counter}) {
			next;
		}
		else {
			$gops_to_get--;
		}
	}
	my $gop_end = $self->{Iframes}{$counter};
	VR_dprint ($self,  "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 $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 .=  $packet; #$payload;
		$packet_counter++;
	}

	VR_dprint ($self, "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) {
        die "No luck here...\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)));
    
    #print "Got the following infos:\n\taspect ratio: $aspect_ratio\n\tsize: $horizontal_size" . "x$vertical_size\n";
    #sleep 2;
    
	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);
	VR_Dprint ($self, "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->{Iframe_list}}[-1];
		}
		if (! $self->{Iframes}{$counter}) {
			next;
		}
		else {
			$gops_to_get--;
		}
	}
	my $gop_end = $self->{Iframes}{$counter};
	print "Now we should read the range $gop_offset to $gop_end\n" if $self->{debug};
	
	my $raw_chunk = ${ read_chunk($self, $gop_offset, $gop_end) };
	my $offset = 0;
	my $video_stream = "";
	my $plength = 0;
	my $packet_counter = 0;
	
	while ($offset + $plength < length($raw_chunk))	{
		# analyse the needed info from PES Header
		my ($startcode,  $streamcode, $plength, $notused, $header_length) = unpack ( "H6 H2 n1 n1 C1", substr($raw_chunk, $offset, 9));
		
		if ($startcode ne "000001") {
			print "No 0x000001 found at current packet, found $startcode and $streamcode instead in packet\n"; exit
		}
		if (($streamcode =~/^b/)) {
			#print "video stream $streamcode found\n";
			#print "not a video stream\n";
			$offset +=  $plength + 6;
			next;
		}
		if (($offset + $plength) > (length($raw_chunk))) {
			die "Did not get a rawchunk back that ends with a packet...\n";
			return \$video_stream;
		}
		
		my $packet = substr($raw_chunk, $offset, ($plength + 6));
		my $payload = (substr($packet, 9 + $header_length));
		$offset +=  $plength + 6;
		$video_stream .= $payload;
		$packet_counter++;
	}

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

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

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

    if ((! $self->{current_chunk}) && (! $self->{bytes_written})) {
        #print "seems to be the very first read...\n";
        $self->{current_file}   = $start_file;
        $self->{current_offset} = $start_offset;
    }
        #print " Now we move to $self->{current_file}, $self->{current_offset}, $end_file, $end_offset\n";
        
        
	my $IFH = ${$self->{file_handles}}[$self->{current_file} - 1];
    #print "$IFH\n";
    my $success = (sysseek $IFH, $self->{current_offset}, 0);
	#print "seeked to pos $success, $start\n";
	if (!($self->{current_offset} == $success)) {
		print "could not seek, dieing..\n"; die;
	}
	my $bytes_to_read = 10000000;
    while ($bytes_to_read && (! $self->{EOF})) {
        if ($self->{current_file} == $end_file) {
            #print "start file is end file: $self->{current_file}, $self->{current_offset}, $end_file, $end_offset\n";
            if (($end_offset - $self->{current_offset}) > 10000000) {
                #print "Enough bytes to read left in this file\n";
                my $rbytes = sysread $IFH, $rbuffer, $bytes_to_read, 0;
                #print "Read $rbytes\n";
                $bytes_to_read  -= $rbytes;
                $buffer         .= $rbuffer;
                $self->{current_offset} += $rbytes;
                #print "current offset is now $self->{current_offset}\n";
                #print "bytes_to_read is now $bytes_to_read\n";
            }
            else {
                #print "not enough bytes to read left in current file or end of recording\n";
                #print "Trying to read " . ($end_offset - $self->{current_offset}) ."\n";
                my $rbytes = sysread $IFH, $rbuffer, ($end_offset - $self->{current_offset}), 0;
                $bytes_to_read  -= $rbytes;
                $self->{current_offset} += $rbytes;
                $buffer         .= $rbuffer;
                if ($chunks[$self->{current_chunk} + 1]) {
                    # 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;
    #print "Got " . scalar(keys(%{$self->{Iframes}})) . " to search\n";
    #while ((! $self->{Iframes}{$search_frame}) && ($counter < 20)) {
    #    print "testing $search_frame\n";
    #    $search_frame++;
    #    if (! $self->{Iframes}{$search_frame}) {
    #        print "No Iframe here $search_frame\n";
    #    } else {
    ##        print "Iframe here $search_frame $self->{Iframes}{$search_frame}\n";
    #    }
    #    $counter++;
    #}
    
    if (! $self->{Iframes}{$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);
	VR_dprint ($self, "In get audio: wanted frame $search_frame, got $frameno at $gop_offset\n") if $self->{debug};
	
	    
    print "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->{Iframe_list}}[-1];
		}
		if (! $self->{Iframes}{$counter}) {
			next;
		}
		else {
			$gops_to_get--;
		}
	}
	my $gop_end = $self->{Iframes}{$counter};
	
    print "Now we should read the range $gop_offset to $gop_end\n" 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 compute_movie_cuts {
	my $self = shift;
	
	my @chunks;
	my @marks                  = @{$self->{marks}};
	my @file_sizes             = @{$self->{file_sizes}};
    my $total_frames_cutted    = 0;
    $self->{movie_size_cutted} = 0;
    
    
    
    if (scalar(@marks) < 2) {
        print "no cutting without marks...\n";
        return -1;
    }
    
    # Here we check whether the marks are just remains from an already cutted movie
    # Criteria: First and last I-Frame are marked, and the diff between the marks inside are smaller than 20
    
    my $is_cutted = 1;
    if (($marks[0] eq "0:00:00.01") && (mark2frames($self, $marks[-1]) == ${$self->{Iframe_list}}[-1])) {
        for (my $i = 1; $i < scalar(@marks) - 2; $i += 2) {
            if ((mark2frames($self,$marks[$i+1]) - mark2frames($self,$marks[$i])) < 20) {
                next;
            } else {
                $is_cutted = 0; 
                last;
            }
        }
    } else {
        $is_cutted = 0;
    }    
    
    if ($is_cutted) {
        print "CAUTION: You specified the -cut Option, but the marks look like remains of an already cutted movie!\nI will process the full movie!\n";
        my $startframe = mark2frames($self, $marks[0]);
        my $startpos   = $self->{Iframes}{$startframe};
        my $endframe   = mark2frames($self, $marks[-1]);
        my $endpos     = $self->{Iframes}{$endframe};
        if ((! $startpos) || (! $endpos)) {
            print "Mark does not mark an Iframe, cutting stopped!\n";
            return -1;
        }
        $total_frames_cutted += $endframe - $startframe;
        push @chunks, "$startpos TO $endpos";
    } else {
        for (my $i = 0; $i < (scalar(@marks)); $i += 2) {
            if ($marks[$i+1]) {
                my $startframe = mark2frames($self, $marks[$i]);
                my $startpos   = $self->{Iframes}{$startframe};
                my $endframe   = mark2frames($self, $marks[$i + 1]);
                my $endpos     = $self->{Iframes}{$endframe};
                if ((! $startpos) || (! $endpos)) {
                    print "Mark does not mark an Iframe, cutting stopped!\n";
                    return -1;
                }
                $total_frames_cutted += $endframe - $startframe;
                push @chunks, "$startpos TO $endpos";
            }
        }
    }
		
	@{$self->{cut_chunks}} = @chunks;
	foreach (@chunks) {
		my ($start_offset, $start_file, $end_offset, $end_file) = split/ : | TO /, $_;
        #print "A chunk: $_\n";
        if ($start_file == $end_file) {
            $self->{movie_size_cutted} += $end_offset - $start_offset;
        } else {
            while ($start_file < $end_file) {
                $self->{movie_size_cutted} += $self->{file_sizes}[$start_file -1] - $start_offset;
                $start_offset = 0;
                $start_file++;
            }
            
            $self->{movie_size_cutted} += $end_offset;
            
        }
	}
    $self->{movie_length_cutted} = HMS2sec($self, frames2mark($self, $total_frames_cutted));
    
}


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

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


sub init_recording {
	my $self = shift;
	my $indir = shift;
	if (! $indir) {
		print "Did not recieve 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;
	
	
	# 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}});
}
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;
}

























