#!/usr/bin/perl

#
#  vdrsync_buffer (c) 2003-2004  by  Dr. Peter Sebbel. This is a helper script to vdrsync. 
# vdrsync is 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;
############################################################################
# The next vars are only used for buffer-only
############################################################################
my $bo_fifo_name;
my $bo_quit;
my $bo_index;
my $bo_read_count;
my $bo_write_count;
my @bo_buffer;
my $bo_buffer_size;
#################
my $fifo_name = shift;

buffer_only($fifo_name);


sub buffer_only {
    my $fifo_name = shift;
    
    if (! $fifo_name) {
        print "Usage: vdrsync-buffer.pl FIFO\n";
        exit 0;
    }
    
    use threads;
    use threads::shared;
    use Time::HiRes qw(usleep);
    
    share($bo_quit);
    share($bo_index);
    share($bo_read_count);
    share($bo_write_count);
    share($bo_buffer_size);
    my $buffer_elements = 2000;

    $| = 1; 

    for (my $i = 0; $i < $buffer_elements; $i++) {
        $bo_buffer[$i] = "";
        share($bo_buffer[$i]);
    }
    my $read_index = 0;
    $read_index    = 1;
    $bo_quit       = 0;
    $bo_index      = 0;
    $bo_buffer_size= 0;
    $bo_read_count = 0;
    $bo_write_count= 0;
    my $total_size = 0;
    
    my $bytes_read = 0;
    my $writeout_thread = threads->new(\&write_out_thread, $fifo_name, $buffer_elements);    
    my $dummy = "";
    my $wait_counter = 0;

    while (! $bo_quit) {
    
        if (($read_index != $bo_index))  {
            $wait_counter = 0;
            my $length_bytes;
            $bo_buffer[$read_index] = "";
            if ($bo_buffer_size > 10000000) {
                print STDERR "buffer full with $bo_buffer_size\n";
                usleep(100000);
                next;
            }
            if ($read_index == $bo_index) {
                usleep 10000;
                #print"Waiting in reader thread for $fifo_name\n";
                if ($wait_counter++ > 1000) {
                    $wait_counter = 0;
                    print STDERR "For $fifo_name: read_index $read_index, index $bo_index read_count $bo_read_count write_count $bo_write_count $fifo_name with $bo_buffer_size in buffer\n";
                }
                next;
            }
            $bytes_read = sysread STDIN, $bo_buffer[$read_index], 4096;
            {
            lock ($bo_buffer_size);
            $bo_buffer_size += $bytes_read;
            }
            die "System read error: $!\n" unless defined $bytes_read;
            if ($bytes_read != 4096) {
                print STDERR "Assuming end of Imput since block had only $bytes_read bytes $fifo_name\n";
                {
                lock($bo_quit);
                $bo_quit = 1;
                }
                next;
            }
            $bo_read_count++;
            $read_index++;
            if ($read_index >= $buffer_elements){
                $read_index = 0;
            }

        } else {
            usleep(1000);
            if ($wait_counter++ > 100) {
                $wait_counter = 0;
                print STDERR "read_index $read_index, index $bo_index read_count $bo_read_count write_count $bo_write_count $fifo_name with $bo_buffer_size in buffer\n";
            }
            next;
        }
    }
    print STDERR "Trying to join output thread at $fifo_name\n";
    my $result = $writeout_thread->join;
    print STDERR "Thread returned $result at $fifo_name\n";
    #$writeout_thread->detach;
    #print "Thread detached\n";#returned $result\n";
    exit;
}


sub write_out_thread {
    #use Fcntl;
    my $fifo_name = shift;
    my $wait_counter = 0;
    my $started = 0;
    my $buffer_elements = shift;
    
    print STDERR "Welcome to writer thread for $fifo_name, got a buffer of " . scalar(@bo_buffer) . " entries\n";
    #sysopen(OFH, $fifo_name, O_RDWR | O_NONBLOCK | O_SYNC ) or die "Could not open FIFO $fifo_name\n"; #| O_NONBLOCK 
    my $OFH;
    if ($fifo_name eq "STDOUT") {
        $OFH = *STDOUT;
    } else {
        open $OFH, ">$fifo_name" or die "Can not open FIFO\n";
    }
    my $oldfh = select $OFH; $| = 1; select $oldfh;
    print STDERR "opened FIFO for stream $fifo_name\n";
 
    while (! $bo_quit) {
        if (($bo_read_count > $bo_write_count)) {
            my $dummy;
            my $counter = 0;
            $wait_counter = 0;
            while ($bo_read_count > $bo_write_count) {
                {
                lock ($bo_buffer_size);
                $bo_buffer_size -= length($bo_buffer[$bo_index]);
                }
                print $OFH $bo_buffer[$bo_index];
                $bo_buffer[$bo_index] = "";
                $bo_index++;
             
                if ($bo_index >= $buffer_elements) {
                    $bo_index = 0;
                }    
                $bo_write_count++;
           #     $counter++;
            }
        }
        else {
            usleep(100000);
            if ($wait_counter++ > 100) {
                $wait_counter = 0;
                
                my $buffer_size = 0;
                #for (my $i = 0; $i < $buffer_elements; $i++) {
                #    $buffer_size += length($bo_buffer[$i]);
                #}
                print STDERR "Waiting... with $bo_buffer_size in buffer $bo_read_count and $bo_write_count for $fifo_name and $bo_buffer_size bytes in buffer\n"; 
            }
        }
    }
#    print "Quit must be 1 for $fifo_name\n";
    my $rest;
    for (my $i = 0; $i <= (scalar(@bo_buffer)) ; $i ++) {
        #print "testing buffer $bo_index\t";
        if ($bo_buffer[$bo_index]) {
            #print "found sth in buffer\t";
            $rest .= $bo_buffer[$bo_index];
            $bo_buffer[$bo_index] = "";
        }
 
            $bo_index++;
            if ($bo_index > $buffer_elements) {
                $bo_index = 0;
            }
 
    }
    if ($rest) {
 #       print "Got a rest of " . length($rest) ." to print at $fifo_name\n";
        print $OFH $rest;
    }
  #  print "unlocked after final flush at $fifo_name\n"; 
    close $OFH;
#    print "Closed File for $fifo_name\n";
    return 0;
}

