#!/usr/bin/perl

# GiantDisc mp3 stream sender
#
# Autor: Rolf Brugger
# Date:  Aug 2003
#

use IO::Socket;
use Getopt::Std;
use lib '/home/music/bin';
use gdgentools;
use DBI;


if (!getopts ('h:p:f:b:s:l:d:vi:c:') || !(defined $opt_h)){ # ":" means mandatory parameter
  print <<EOF;
GiantDisc mp3 stream sender. Sends the audiofile as mp3 stream to the specified host. 
- if the audiofile is not a mp3 file, it is re-encoded to a 192kB mp3 stream
- if the audiofile is a URL starting with 'http://' then a mp3 stream is pulled from that address and re-sent to the specified host

Usage: 
  gdstream.pl [options] audiofile

Mandatory options:
  -h host        host, where stream is sent to
  -p port        port, where stream is sent to
  -f fileformat  mp3, ogg or flac
                 ogg and flac files are automatically decoded and re-
                 encoded into a 192kBit/sec mp3 stream.        
optional:
  -b bitrate     average bitrate of audiofile (unit bits/sec)
  -s sec         skip n seconds of audiofile
  -l sec         total length of track in seconds
  
  -d dbhost      database host where current playtime should be written to
                 nothing is written if database host is not specified
  -i playerid    needed to update the current playtime
  -c channel     needed to update the current playtime
  
  -v             verbose
  
Example:
  gdstream.pl -h 192.168.1.30 -p 2020 -f mp3 -v song.mp3
EOF
  exit 0;
}


### open DB connection
my ($dbh, $playerid, $audiochannel);
if (defined $opt_d){
  if (defined $opt_i){$playerid=$opt_i;}    else{print "Error: you have to define a player id (option -i)\n";exit;}
  if (defined $opt_c){$audiochannel=$opt_c;}else{print "Error: you have to define an audiochannel (option -c)\n";exit;}
  if (!defined $opt_b){print "Error: you have to define the bitrate (option -b)\n";exit;}
  $dbh = DBI->connect("DBI:mysql:GiantDisc:".$opt_d, "music", undef)
    or die "unable to connect to GiantDisc db";
}

### set termination handler
$SIG{TERM} = 
sub { 
  # Kill event handler
  print "gdstream.pl killed\n"; 
  gdgentools::exstreamer_command($opt_h, "c=2");

#  if ($use_barixcmd){ # stop playing
#    system "gdbarixcmd.pl -h $opt_h c=2";  
#  }
#  else{
#    my $stopsock = new IO::Socket::INET( PeerAddr => $opt_h,
#                                         PeerPort => $tcp_command_port,
#                                         Proto    => 'tcp');
#    print $stopsock "c=2\n";  # send command: immediately stop playing
#    close $stopsock;
#  }

  if (defined $opt_d){
    $dbh->disconnect; 
  }
  exit;
}; 



#my $songpos_delta = -2;  # correction for songposition due to exstreamer streambuffer
my $songpos_delta = 0;

my $sock = new IO::Socket::INET( PeerAddr => $opt_h,
                                 PeerPort => $opt_p,
                                 Proto    => 'tcp');
  
die "gdstream.pl: could not open socket: $!\n" unless $sock;
  
my ($buf);

### init stuff
my $bytes_sent = 0;
my $packsize = 1024;       # one kByte
my $audiofile = $ARGV[0];
  
my $songpos_sec = 0;
my $songpos_lastupdate = -1;
my $skipsec = 0;

my $recode_bitrate = 192; # kBits/sec
my $mp3stream_bitrate = 128000;


if ($opt_v) {print "send $opt_f file $audiofile...\n\n";}
if (defined $opt_s){
  $skipsec = $opt_s;  # parameter must be defined for ogg and flac files
}

use integer;

if (defined $opt_d && defined $opt_l){ ### write total playtime to database
  gdgentools::pll_write_playtime($dbh, $playerid, $audiochannel, 
    $opt_s * gdgentools::frames_per_second(),     # start position
    $opt_l * gdgentools::frames_per_second());    # total length
}


### get source
if ($opt_f eq "mp3"){     ### mp3 ###
  if (defined $opt_b){
    $mp3stream_bitrate = $opt_b;
  }
  
  if (gdgentools::is_mp3stream($audiofile)){
    open MP3F, "wget --output-document=- --quiet $audiofile |";
  }
  else{
    open MP3F, $audiofile;
  }
  
  if (defined $opt_s and $opt_s > 0){
    ### skip first opt_s seconds
    my $skip_bytes = $opt_s * $opt_b/8;
    if (seek MP3F, $skip_bytes, 0){
      if ($opt_v) {print "skipping $opt_s seconds, $skip_bytes bytes\n";}
    }
    else{ print "Error: can't skip (seek failed)\n"; }
    $bytes_sent = $skip_bytes;
  }

}

elsif ($opt_f eq "ogg"){  ### mp3 ###
  $mp3stream_bitrate = $recode_bitrate*1000;
  if (gdgentools::is_mp3stream($audiofile)){
    print "gdstream.pl - ERROR: can't play ogg stream sources\n";
  }
  else{
    open MP3F, "ogg123 --quiet --skip $skipsec -d raw -o byteorder:big -f - $audiofile  |"
            ." lame -r -b $recode_bitrate --silent - - |";
  }
}

elsif ($opt_f eq "flac"){ ### flac ###
  $mp3stream_bitrate = $recode_bitrate*1000;
  my $skipsamples = $skipsec * gdgentools::samples_per_second();
  if (gdgentools::is_mp3stream($audiofile)){
    print "gdstream.pl - ERROR: can't play flac stream sources\n";
  }
  else{
    open MP3F, "flac -c -d -s --skip $skipsamples --endian=big --sign=signed --force-raw-format $audiofile |"
            ." lame -r -b $recode_bitrate --silent - - |";
  }
}



### Stream the source

while (read MP3F, $buf, $packsize){
  $songpos_sec = ($bytes_sent*8/$mp3stream_bitrate) + $songpos_delta;
  $songpos_sec = ($songpos_sec >=0) ? $songpos_sec : 0;  # filter out negative songpositions
  if($songpos_sec != $songpos_lastupdate){
    $songpos_lastupdate = $songpos_sec;
    if ($opt_v) {        ### print playtime to stdout
      print "$bytes_sent bytes sent [".($songpos_lastupdate/60).":".($songpos_lastupdate%60)."]\n";
    }
    if (defined $opt_d){ ### write playtime to database
      gdgentools::pll_write_playtime_only($dbh, $playerid, $audiochannel, $songpos_lastupdate*gdgentools::frames_per_second());
    }
  }
  print $sock $buf;
  $bytes_sent += $packsize;
}
close MP3FILE;
close($sock);

if ($opt_v) {print "End of stream\n";}


####################################################################################
# ogg filter pipe:
# ogg123 -d raw -o byteorder:big -f - filename.ogg  | lame -r - - | netcat -n 192.168.1.30 2020
#
# flac filter pipe:
# flac -c -d -s --endian=big --sign=signed --force-raw-format filename.flac | lame -r - - | netcat -n 192.168.1.30 2020
#