diff options
-rw-r--r-- | config/ampache.cfg.php.dist | 31 | ||||
-rwxr-xr-x | docs/CHANGELOG | 3 | ||||
-rw-r--r-- | lib/general.lib.php | 79 | ||||
-rw-r--r-- | lib/rss.php | 3 | ||||
-rw-r--r-- | lib/stream.lib.php | 249 | ||||
-rw-r--r-- | modules/init.php | 3 | ||||
-rw-r--r-- | play/index.php | 48 | ||||
-rw-r--r-- | server.php | 12 |
8 files changed, 298 insertions, 130 deletions
diff --git a/config/ampache.cfg.php.dist b/config/ampache.cfg.php.dist index bf727079..38940ad4 100644 --- a/config/ampache.cfg.php.dist +++ b/config/ampache.cfg.php.dist @@ -18,13 +18,6 @@ # DEFAULT: "" #web_path = "" - -# Lang (define the locale you want to use -# this is a cheeseball fix for now it will be smarter -# later. -# DEFAULT: en_US -#lang = "en_US" - #################### # The libglue Vars # #################### @@ -266,6 +259,28 @@ allow_stream_playback = true #allow_slim_playback = false ####################################################### +# These options control the dynamic downsampling based +* on current useage +# *Note* Downsampling must be enabled and working +####################################################### + +# Attempt to optimize bandwidth by dynamically downsampling +# all connections from users to fit within a maximum bandwidth. +# The benefit is that it won't downsample more than it needs to. As it only +# adjusts the sample rate at the beginning of a song, it may take a few +# minutes to reset all connections to a lower rate. This won't never go higher +# than a user's sample rate and only applies to users who are set to +# the Downsample playback method +# DEFAULT: 576 +#max_bit_rate = 576 + +# If min_bit_rate is set then new streams will be denied if it would +# cause all streams to be downsampled below this rate. +# DEFAULT: 48 +#min_bit_rate = 48 + + +####################################################### # These options control how searching works ####################################################### @@ -460,7 +475,7 @@ rss_song_description = <![CDATA[$song->f_title @ $album played by $user->fullna # If set to true MPD is not displayed on the main page, but on it's # own distinct page. This is called localplay, because eventually -# it will control all "LOCAL" style play methods including localplay +# it will control all "LOCAL" style play methods including localplay, xmms2 # and the slimserver # DEFAULT: false #localplay_menu = true diff --git a/docs/CHANGELOG b/docs/CHANGELOG index be4b9bf2..1cae7418 100755 --- a/docs/CHANGELOG +++ b/docs/CHANGELOG @@ -17,6 +17,9 @@ it handles it correctly, and doesn't remove the row after the script execution has finished, and depends upon the GC to catch it + - Added Optional Automatic Bandwidth management for downsampled + users based on defined bandwidth limits (Thx Jens) + - Prevented Load of XML-RPC library if xml_rpc isn't enabled -------------------------------------------------------------------------- diff --git a/lib/general.lib.php b/lib/general.lib.php index 297ac667..16d0ac11 100644 --- a/lib/general.lib.php +++ b/lib/general.lib.php @@ -743,83 +743,4 @@ function tbl_name($table) { } // tbl_name -/** - * insert_now_playing - * This function takes care of inserting the now playing data - * we use this function because we need to do thing differently - * depending upon which play is actually streaming - * @package General - * @catagory Now Playing - */ -function insert_now_playing($song_id,$uid,$song_length) { - - $user_agent = $_SERVER['HTTP_USER_AGENT']; - $time = time(); - - /* Windows Media Player is evil and it makes multiple requests per song */ - if (stristr($user_agent,"NSPlayer")) { return false; } - - /* Set the Expire Time */ - - // If they are using Windows media player - if (stristr($user_agent,"Windows-Media-Player")) { - // WMP does keep the session open so we need to cheat a little here - $expire = $time + $song_length; - } - else { - $expire = $time; - } - - $sql = "INSERT INTO now_playing (`song_id`, `user`, `start_time`)" . - " VALUES ('$song_id', '$uid', '$expire')"; - - $db_result = mysql_query($sql, dbh()); - - $insert_id = mysql_insert_id(dbh()); - - return $insert_id; - -} // insert_now_playing - -/** - * delete_now_playing - * This function checks to see if we should delete the last now playing entry now that it's - * finished streaming the song. Basicly this is an exception for WMP10 - * @package General - * @catagory Now Playing - */ -function delete_now_playing($insert_id) { - - $user_agent = $_SERVER['HTTP_USER_AGENT']; - - if (stristr($user_agent,"Windows-Media-Player")) { - // Commented out until I can figure out the - // trick to making this work - //return true; - } - - - // Remove the song from the now_playing table - $sql = "DELETE FROM now_playing WHERE id = '$insert_id'"; - $db_result = mysql_query($sql, dbh()); - -} // delete_now_playing - -/** - * gc_now_playing - * this is a garbage collection function for now playing this is called every time something - * is streamed - * @package General - * @catagory Now Playing - */ -function gc_now_playing() { - - $time = time(); - $expire = $time - 1800; // 86400 seconds = 1 day - - $sql = "DELETE FROM now_playing WHERE start_time < $expire"; - $db_result = mysql_query($sql, dbh()); - -} // gc_now_playing - ?> diff --git a/lib/rss.php b/lib/rss.php index efb6cba9..4fc849d2 100644 --- a/lib/rss.php +++ b/lib/rss.php @@ -1,6 +1,9 @@ <?php /* + Copyright 2001 - 2005 Ampache.org + All Rights Reserved + 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; either version 2 diff --git a/lib/stream.lib.php b/lib/stream.lib.php new file mode 100644 index 00000000..a022b6da --- /dev/null +++ b/lib/stream.lib.php @@ -0,0 +1,249 @@ +<?php +/* + + Copyright 2001 - 2005 Ampache.org + All Rights Reserved + + 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; either version 2 + of the License, or (at your option) any later version. + + 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. + +*/ + +/** + * delete_now_playing + * This function checks to see if we should delete the last now playing entry now that it's + * finished streaming the song. Basicly this is an exception for WMP10 + * @package General + * @catagory Now Playing + */ +function delete_now_playing($insert_id) { + + $user_agent = $_SERVER['HTTP_USER_AGENT']; + + if (stristr($user_agent,"Windows-Media-Player")) { + // Commented out until I can figure out the + // trick to making this work + //return true; + } + + + // Remove the song from the now_playing table + $sql = "DELETE FROM now_playing WHERE id = '$insert_id'"; + $db_result = mysql_query($sql, dbh()); + +} // delete_now_playing + +/** + * gc_now_playing + * this is a garbage collection function for now playing this is called every time something + * is streamed + * @package General + * @catagory Now Playing + */ +function gc_now_playing() { + + $time = time(); + $expire = $time - 1800; // 86400 seconds = 1 day + + $sql = "DELETE FROM now_playing WHERE start_time < $expire"; + $db_result = mysql_query($sql, dbh()); + +} // gc_now_playing + +/** + * insert_now_playing + * This function takes care of inserting the now playing data + * we use this function because we need to do thing differently + * depending upon which play is actually streaming + * @package General + * @catagory Now Playing + */ +function insert_now_playing($song_id,$uid,$song_length) { + + $user_agent = $_SERVER['HTTP_USER_AGENT']; + $time = time(); + + /* Windows Media Player is evil and it makes multiple requests per song */ + if (stristr($user_agent,"NSPlayer")) { return false; } + + /* Set the Expire Time */ + + // If they are using Windows media player + if (stristr($user_agent,"Windows-Media-Player")) { + // WMP does keep the session open so we need to cheat a little here + $expire = $time + $song_length; + } + else { + $expire = $time; + } + + $sql = "INSERT INTO now_playing (`song_id`, `user`, `start_time`)" . + " VALUES ('$song_id', '$uid', '$expire')"; + + $db_result = mysql_query($sql, dbh()); + + $insert_id = mysql_insert_id(dbh()); + + return $insert_id; + +} // insert_now_playing + +/** + * check_lock_songs + * This checks to see if the song is already playing, if it is then it prevents the user + * from streaming it + * @package General + * @catagory Security + */ +function check_lock_songs($song_id) { + + $sql = "SELECT COUNT(*) FROM now_playing " . + "WHERE song_id = '$song_id'"; + $db_results = mysql_query($sql, dbh()); + + if (mysql_num_rows($db_results)) { + return false; + } + + return true; + +} // check_lock_songs + +/** + * start_downsample + * This is a rather complext function that starts the downsampling of a song and returns the + * opened file handled + * @package General + * @catagory Downsample + * @param $song The Song Object + */ +function start_downsample($song,$now_playing_id=0,$song_name=0) { + + /* Check to see if bitrates are set if so let's go ahead and optomize! */ + $max_bitrate = conf('max_bitrate'); + $min_bitrate = conf('min_bitrate'); + $time = time(); + $dbh = dbh(); + $user_sample_rate = $GLOBALS['user']->prefs['sample_rate']; + $browser = new Browser(); + + if (!$song_name) { + $song_name = $song->f_artist_full . " - " . $song->title . "." . $song->type; + } + + if ($max_bitrate > 1 AND $min_bitrate < $max_bitrate) { + $last_seen_time = $time - 1200; //20 min. + + /********************************** + * Commenting out the following, I'd rather have it slightly less accurate and avoid a 4 table join + // Count the users connected in the last 20 min using downsampling + $sql = "SELECT COUNT(DISTINCT session.username) FROM session,user,user_preference,preferences " . + "WHERE user.username = session.username AND session.expire > '$time' AND user.last_seen > $last_seen_time " . + "AND preferences.name = 'play_type' AND user_preference.preference = preferences.id AND " . + "user_preference.user = user.username AND user_preference.value='downsample'"; + $db_result = mysql_query($sql, $dbh); + $results = mysql_fetch_row($db_result); + + // The current number of connected users + $current_users_count = $results[0]; + ************************************/ + + + $sql = "SELECT COUNT(*) FROM now_playing, user_preference, preferences" . + "WHERE preferences.name = 'play_type' AND user_preference.preference = preferences.id " . + "AND now_playing.user = user_preference.user AND user_preference.value='downsample'"; + $db_results = mysql_query($sql,$dbh); + $results = mysql_fetch_row($db_result); + + // Current number of active streams + 1 (the one we are starting) + $active_streams = $results[0] + 1; + + + /* If only one user, they'll get all available. Otherwise split up equally. */ + $sample_rate = floor($max_bitrate/$active_streams); + + /* If min_bitrate is set, then we'll exit if the bandwidth would need to be split up smaller than the min. */ + if ($min_bitrate > 1 AND ($max_bitrate/$active_streams) < $min_bitrate) { + + /* Log the failure */ + if (conf('debug')) { + log_event($user->username,' downsample ',"Error: Max bandwidith already allocated. $active_streams Active Streams"); + } + + /* Toast the now playing entry, then tell em to try again later */ + delete_now_playing($now_playing_id); + echo "Maximum bandwidth already allocated. Try again later."; + exit(); + + } + else { + $sample_rate = floor($max_bitrate/$active_streams); + } // end else + + /* Round to standard bitrates */ + $sample_rate = 8*(floor($sample_rate/8)); + + // Never go over the users sample rate + if ($sample_rate > $user_sample_rate) { $sample_rate = $user_sample_rate; } + + if (conf('debug')) { + log_event($GLOBALS['user']->username, ' downsample ',"Downsampled: $active_streams current active streams, downsampling to $sample_rate"); + } + + } // end if we've got bitrates + + else { + $sample_rate = $user_sample_rate; + } + + /* Never Upsample a song */ + if (($sample_rate*1000) > $song->bitrate) { + unset($sample_rate); + } + + $sample_ratio = $sample_rate/($song->bitrate/1000); + + $browser->downloadHeaders($song_name, $song->mime, false,$sample_ratio*$song->size); + + /* Get Offset */ + $offset = ( $start*$song->time )/( $sample_ratio*$song->size ); + $offsetmm = floor($offset/60); + $offsetss = floor($offset-$offsetmm*60); + $offset = sprintf("%02d.%02d",$offsetmm,$offsetss); + + /* Get EOF */ + $eofmm = floor($song->time/60); + $eofss = floor($song->time-$eofmm*60); + $eof = sprintf("%02d.%02d",$eofmm,$eofss); + + /* Replace Variables */ + $downsample_command = conf('downsample_cmd'); + $downsample_command = str_replace("%FILE%",$song->file,$downsample_command); + $downsample_command = str_replace("%OFFSET%",$offset,$downsample_command); + $downsample_command = str_replace("%EOF%",$eof,$downsample_command); + $downsample_command = str_replace("%SAMPLE%",$sample_rate,$downsample_command); + + // If we are debugging log this event + if (conf('debug')) { + $message = "Start Downsample: $downsample_command"; + log_event($GLOBALS['user']->username,' downsample ',$message); + } // if debug + + $fp = @popen($downsample_command, 'r'); + + return ($fp); + +} // start_downsample + +?> diff --git a/modules/init.php b/modules/init.php index 5974b0e9..d38a5289 100644 --- a/modules/init.php +++ b/modules/init.php @@ -83,7 +83,7 @@ if (!$results['conf']['allow_stream_playback']) { } $results['conf']['web_path'] = $http_type . $_SERVER['HTTP_HOST'] . $results['conf']['web_path']; -$results['conf']['version'] = '3.3.2-Alpha2 Build (001)'; +$results['conf']['version'] = '3.3.2-Alpha2 Build (002)'; $results['conf']['catalog_file_pattern']= 'mp3|mpc|m4p|m4a|mp4|aac|ogg|rm|wma|asf|flac|spx'; $results['libglue']['local_table'] = 'session'; $results['libglue']['local_sid'] = 'id'; @@ -163,6 +163,7 @@ require_once(conf('prefix') . "/lib/ui.lib.php"); require_once(conf('prefix') . "/lib/gettext.php"); require_once(conf('prefix') . "/lib/batch.lib.php"); require_once(conf('prefix') . "/lib/themes.php"); +require_once(conf('prefix') . "/lib/stream.lib.php"); require_once(conf('prefix') . "/modules/lib.php"); require_once(conf('prefix') . "/modules/admin.php"); require_once(conf('prefix') . "/modules/catalog.php"); diff --git a/play/index.php b/play/index.php index 874de187..660ae9d4 100644 --- a/play/index.php +++ b/play/index.php @@ -138,6 +138,8 @@ if ( $catalog->catalog_type == 'remote' ) { header("Location: " . $song->file . $extra_info); if (conf('debug')) { log_event($user->username,' xmlrpc-stream ',"Start XML-RPC Stream - " . $song->file . $extra_info); } } + + else { if ($user->prefs['play_type'] == 'downsample') { $ds = $user->prefs['sample_rate']; @@ -150,16 +152,8 @@ else { gc_now_playing(); // If we are running in Legalize mode, don't play songs already playing - if (conf('lock_songs') == 'true') { - $sql = "SELECT COUNT(*) FROM now_playing" . - " WHERE song_id = '$song_id'"; - $db_result = mysql_query($sql, $dbh); - while ($r = mysql_fetch_row($db_result)) { - if ($r[0] == 1) { - // Song is already playing, so exit without returning song - exit; - } - } + if (conf('lock_songs')) { + if (!check_lock_songs($song->id)) { exit(); } } // Put this song in the now_playing table @@ -214,37 +208,11 @@ else { // Prevent the script from timing out set_time_limit(0); - if ($ds) { - $ds = $user->prefs['sample_rate']; - $dsratio = $ds/$song->bitrate*1024; - $browser->downloadHeaders($song_name, $song->mime, false,$dsratio*$song->size); - - /* Get Offset */ - $offset = ( $start*$song->time )/( $dsratio*$song->size ); - $offsetmm = floor($offset/60); - $offsetss = floor($offset-$offsetmm*60); - $offset = sprintf("%02d.%02d",$offsetmm,$offsetss); + if ($user->prefs['play_type'] == 'downsample') { - /* Get EOF */ - $eofmm = floor($song->time/60); - $eofss = floor($song->time-$eofmm*60); - $eof = sprintf("%02d.%02d",$eofmm,$eofss); - - /* Replace Variables */ - $downsample_command = conf('downsample_cmd'); - $downsample_command = str_replace("%FILE%",$song->file,$downsample_command); - $downsample_command = str_replace("%OFFSET%",$offset,$downsample_command); - $downsample_command = str_replace("%EOF%",$eof,$downsample_command); - $downsample_command = str_replace("%SAMPLE%",$ds,$downsample_command); - - // If we are debugging log this event - if (conf('debug')) { - $message = "Exec: $downsample_command"; - log_event($user->username,'downsample',$message); - } // if debug + $fp = start_downsample($song,$lastid,$song_name); - $fp = @popen($downsample_command, 'r'); - } // if downsampling + } // end if downsampling elseif ($start) { $browser->downloadHeaders($song_name, $song->mime, false, $song->size); @@ -282,7 +250,7 @@ else { delete_now_playing($lastid); /* Clean up any open ends */ - if ($ds) { + if ($user->prefs['play_type'] == 'downsample') { @pclose($fp); } else { @@ -1,6 +1,9 @@ <?php /* + Copyright 2001 - 2005 Ampache.org + All Rights Reserved + 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; either version 2 @@ -19,8 +22,13 @@ $no_session = true; require_once('modules/init.php'); -require_once(conf('prefix') . "/modules/xmlrpc/xmlrpcs.inc"); -require_once(conf('prefix') . "/modules/xmlrpc/xmlrpc.inc"); + + +if (conf('xml_rpc')) { + require_once(conf('prefix') . "/modules/xmlrpc/xmlrpcs.inc"); + require_once(conf('prefix') . "/modules/xmlrpc/xmlrpc.inc"); +} +else { exit(); } /* Setup the vars we are going to need */ $access = new Access(); |