/*
* Created on 1 Nov 2006
* Created by Paul Gardner
* Copyright (C) 2006 Aelitis, 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.
*
* AELITIS, SAS au capital de 63.529,40 euros
* 8 Allee Lenotre, La Grille Royale, 78600 Le Mesnil le Roi, France.
*
*/
package com.aelitis.azureus.core.download;
import java.util.List;
import org.gudy.azureus2.core3.config.COConfigurationManager;
import org.gudy.azureus2.core3.config.ParameterListener;
import org.gudy.azureus2.core3.config.impl.TransferSpeedValidator;
import org.gudy.azureus2.core3.disk.DiskManager;
import org.gudy.azureus2.core3.disk.DiskManagerFileInfo;
import org.gudy.azureus2.core3.disk.DiskManagerPiece;
import org.gudy.azureus2.core3.download.DownloadManager;
import org.gudy.azureus2.core3.download.DownloadManagerListener;
import org.gudy.azureus2.core3.download.DownloadManagerPeerListener;
import org.gudy.azureus2.core3.download.impl.DownloadManagerAdapter;
import org.gudy.azureus2.core3.global.GlobalManager;
import org.gudy.azureus2.core3.peer.PEPeer;
import org.gudy.azureus2.core3.peer.PEPeerManager;
import org.gudy.azureus2.core3.torrent.TOTorrent;
import org.gudy.azureus2.core3.util.*;
import com.aelitis.azureus.core.peermanager.piecepicker.PiecePicker;
import com.aelitis.azureus.core.peermanager.piecepicker.PieceRTAProvider;
import com.aelitis.azureus.core.torrent.PlatformTorrentUtils;
import com.aelitis.azureus.core.util.average.Average;
import com.aelitis.azureus.core.util.average.AverageFactory;
import com.aelitis.azureus.util.ConstantsVuze;
public class
EnhancedDownloadManager
{
public static int DEFAULT_MINIMUM_INITIAL_BUFFER_SECS_FOR_ETA = 30;
// number of seconds of buffer required before we fall back to normal bt mode
public static int MINIMUM_INITIAL_BUFFER_SECS;
static{
COConfigurationManager.addAndFireParameterListeners(
new String[]{
"filechannel.rt.buffer.millis"
},
new ParameterListener()
{
public void
parameterChanged(
String parameterName )
{
int channel_buffer_millis = COConfigurationManager.getIntParameter( "filechannel.rt.buffer.millis" );
MINIMUM_INITIAL_BUFFER_SECS = (2 * channel_buffer_millis )/1000;
}
});
}
public static final int REACTIVATE_PROVIDER_PERIOD = 5*1000;
public static final int REACTIVATE_PROVIDER_PERIOD_TICKS = REACTIVATE_PROVIDER_PERIOD/DownloadManagerEnhancer.TICK_PERIOD;
public static final int LOG_PROG_STATS_PERIOD = 10*1000;
public static final int LOG_PROG_STATS_TICKS = LOG_PROG_STATS_PERIOD/DownloadManagerEnhancer.TICK_PERIOD;
private static final int content_stream_bps_min_increase_ratio = 5;
private static final int content_stream_bps_max_increase_ratio = 8;
private DownloadManagerEnhancer enhancer;
private DownloadManager download_manager;
private boolean explicit_progressive;
private volatile PiecePicker current_piece_pickler;
private volatile boolean progressive_active = false;
private long content_min_delivery_bps;
private int minimum_initial_buffer_secs_for_eta;
private bufferETAProvider buffer_provider = new bufferETAProvider();
private progressiveStats progressive_stats;
private boolean marked_active;
private boolean destroyed;
private DownloadManagerListener dmListener;
private EnhancedDownloadManagerFile[] enhanced_files;
protected
EnhancedDownloadManager(
DownloadManagerEnhancer _enhancer,
DownloadManager _download_manager )
{
enhancer = _enhancer;
download_manager = _download_manager;
DiskManagerFileInfo[] files = download_manager.getDiskManagerFileInfo();
minimum_initial_buffer_secs_for_eta = DEFAULT_MINIMUM_INITIAL_BUFFER_SECS_FOR_ETA;
enhanced_files = new EnhancedDownloadManagerFile[files.length];
long offset = 0;
for (int i=0;i<files.length;i++){
DiskManagerFileInfo f = files[i];
enhanced_files[i] = new EnhancedDownloadManagerFile( f, offset );
offset += f.getLength();
}
int primary_index = getPrimaryFileIndex();
EnhancedDownloadManagerFile primary_file = enhanced_files[primary_index==-1?0:primary_index];
progressive_stats = createProgressiveStats( download_manager, primary_file );
download_manager.addPeerListener(
new DownloadManagerPeerListener()
{
public void
peerManagerWillBeAdded(
PEPeerManager peer_manager )
{
}
public void
peerManagerAdded(
PEPeerManager manager )
{
synchronized( EnhancedDownloadManager.this ){
current_piece_pickler = manager.getPiecePicker();
if ( progressive_active && current_piece_pickler != null ){
buffer_provider.activate( current_piece_pickler );
}
}
}
public void
peerManagerRemoved(
PEPeerManager manager )
{
synchronized( EnhancedDownloadManager.this ){
if ( progressive_active ){
setProgressiveMode( false );
}
}
}
public void
peerAdded(
PEPeer peer )
{
}
public void
peerRemoved(
PEPeer peer )
{
}
});
}
public int getPrimaryFileIndex() {
DiskManagerFileInfo info = download_manager.getDownloadState().getPrimaryFile();
if ( info == null ){
return( -1 );
}
return( info.getIndex());
}
public void
setExplicitProgressive(
int min_initial_buffer_secs,
long min_bps,
int file_index )
{
if ( file_index >= 0 && file_index < enhanced_files.length ){
explicit_progressive = true;
minimum_initial_buffer_secs_for_eta = min_initial_buffer_secs;
content_min_delivery_bps = min_bps;
EnhancedDownloadManagerFile primary_file = enhanced_files[file_index];
progressive_stats = createProgressiveStats( download_manager, primary_file );
}
}
public String
getName()
{
return( download_manager.getDisplayName());
}
public byte[]
getHash()
{
TOTorrent t = download_manager.getTorrent();
if ( t == null ){
return( null );
}
try{
return( t.getHash());
}catch( Throwable e ){
return( null );
}
}
public boolean
isPlatform()
{
TOTorrent torrent = download_manager.getTorrent();
if ( torrent != null ){
return( PlatformTorrentUtils.isContent( torrent, true ));
}
return( false );
}
public EnhancedDownloadManagerFile[]
getFiles()
{
return( enhanced_files );
}
protected long
getTargetSpeed()
{
long target_speed = progressive_active?progressive_stats.getStreamBytesPerSecondMax():content_min_delivery_bps;
if ( target_speed < content_min_delivery_bps ){
target_speed = content_min_delivery_bps;
}
return( target_speed );
}
protected boolean
updateStats(
int tick_count )
{
return( updateProgressiveStats( tick_count ));
}
public boolean
supportsProgressiveMode()
{
TOTorrent torrent = download_manager.getTorrent();
if ( torrent == null ){
return( false );
}
return( enhancer.isProgressiveAvailable() &&
( PlatformTorrentUtils.isContentProgressive( torrent ) || explicit_progressive ));
}
public void
prepareForProgressiveMode(
boolean active )
{
enhancer.prepareForProgressiveMode( download_manager, active );
}
public boolean
setProgressiveMode(
boolean active )
{
return( setProgressiveMode( active, false ));
}
protected boolean
setProgressiveMode(
boolean active,
boolean switching_progressive_downloads )
{
TOTorrent torrent = download_manager.getTorrent();
DiskManagerFileInfo primaryFile = download_manager.getDownloadState().getPrimaryFile();
if ( torrent == null || primaryFile == null ){
return( false );
}
EnhancedDownloadManagerFile enhanced_file = enhanced_files[primaryFile.getIndex()];
synchronized( this ){
if ( progressive_active == active ){
return( true );
}
if (active && !supportsProgressiveMode()) {
Debug.out( "Attempt to set progress mode on non-progressible content - " + getName());
return( false );
}
log( "Progressive mode changed to " + active );
final GlobalManager gm = download_manager.getGlobalManager();
if (active) {
if (dmListener == null) {
dmListener = new DownloadManagerAdapter() {
public void downloadComplete(DownloadManager manager) {
enhancer.resume();
}
};
}
download_manager.addListener(dmListener);
// Check existing downloading torrents and turn off any
// existing progressive/downloading
Object[] dms = gm.getDownloadManagers().toArray();
for (int i = 0; i < dms.length; i++) {
DownloadManager dmCheck = (DownloadManager) dms[i];
if (dmCheck.equals(download_manager)) {
continue;
}
if (!dmCheck.isDownloadComplete(false)) {
int state = dmCheck.getState();
if (state == DownloadManager.STATE_DOWNLOADING
|| state == DownloadManager.STATE_QUEUED) {
enhancer.pause( dmCheck );
}
EnhancedDownloadManager edmCheck = enhancer.getEnhancedDownload(dmCheck);
if (edmCheck != null && edmCheck.getProgressiveMode()) {
edmCheck.setProgressiveMode(false, true);
}
}
}
if (download_manager.isPaused()) {
enhancer.resume( download_manager );
}
// Make sure download can start by moving out of stop state
// and putting at top
if (download_manager.getState() == DownloadManager.STATE_STOPPED) {
download_manager.setStateWaiting();
}
if (download_manager.getPosition() != 1) {
download_manager.getGlobalManager().moveTo(download_manager, 1);
}
} else {
download_manager.removeListener(dmListener);
if ( !switching_progressive_downloads ){
enhancer.resume();
}
}
progressive_active = active;
if ( progressive_active ){
enhancer.progressiveActivated();
}
if ( current_piece_pickler != null ){
if ( progressive_active ){
buffer_provider.activate( current_piece_pickler );
progressive_stats.update( 0 );
}else{
buffer_provider.deactivate( current_piece_pickler );
progressive_stats = createProgressiveStats( download_manager, enhanced_file );
}
}else{
progressive_stats = createProgressiveStats( download_manager, enhanced_file );
}
if ( !switching_progressive_downloads ){
if ( active ){
RealTimeInfo.setProgressiveActive( progressive_stats.getStreamBytesPerSecondMax());
}else{
RealTimeInfo.setProgressiveInactive();
}
}
}
return( true );
}
public boolean
getProgressiveMode()
{
return( progressive_active );
}
public long
getProgressivePlayETA()
{
progressiveStats stats = getProgressiveStats();
if ( stats == null ){
return( Long.MAX_VALUE );
}
long eta = stats.getETA();
return( eta );
}
protected progressiveStats
getProgressiveStats()
{
synchronized( this ){
if ( progressive_stats == null ){
return( null );
}
return( progressive_stats.getCopy());
}
}
protected progressiveStats
createProgressiveStats(
DownloadManager dm,
EnhancedDownloadManagerFile file )
{
return( new progressiveStatsCommon( dm, file ));
}
protected boolean
updateProgressiveStats(
int tick_count )
{
if ( !progressive_active ){
return( false );
}
synchronized( this ){
if ( !progressive_active || progressive_stats == null ){
return( false );
}
if ( tick_count % REACTIVATE_PROVIDER_PERIOD_TICKS == 0 ){
PiecePicker piece_picker = current_piece_pickler;
if ( piece_picker != null ){
buffer_provider.checkActivation( piece_picker );
}
}
progressive_stats.update( tick_count );
long current_max = progressive_stats.getStreamBytesPerSecondMax();
if ( RealTimeInfo.getProgressiveActiveBytesPerSec() != current_max ){
RealTimeInfo.setProgressiveActive( current_max );
}
}
return( true );
}
protected void
setRTA(
boolean active )
{
synchronized( this ){
if ( marked_active && !active ){
marked_active = false;
RealTimeInfo.removeRealTimeTask();
}
if ( destroyed ){
return;
}
if ( !marked_active && active ){
marked_active = true;
RealTimeInfo.addRealTimeTask();
}
}
}
public long
getContiguousAvailableBytes(
int file_index,
long file_start_offset,
long stop_counting_after )
{
if ( file_index < 0 || file_index >= enhanced_files.length ) {
return( -1 );
}
EnhancedDownloadManagerFile efile = enhanced_files[ file_index ];
DiskManagerFileInfo file = efile.getFile();
DiskManager dm = download_manager.getDiskManager();
if ( dm == null ){
if ( file.getDownloaded() == file.getLength()){
return( file.getLength() - file_start_offset );
}
return( -1 );
}
int piece_size = dm.getPieceLength();
long start_index = efile.getByteOffestInTorrent() + file_start_offset;
int first_piece_index = (int)( start_index / piece_size );
int first_piece_offset = (int)( start_index % piece_size );
int last_piece_index = file.getLastPieceNumber();
DiskManagerPiece[] pieces = dm.getPieces();
DiskManagerPiece first_piece = pieces[first_piece_index];
long available = 0;
if ( !first_piece.isDone()){
boolean[] blocks = first_piece.getWritten();
if ( blocks == null ){
if ( first_piece.isDone()){
available = first_piece.getLength() - first_piece_offset;
}
}else{
int piece_offset = 0;
for (int j=0;j<blocks.length;j++){
if ( blocks[j] ){
int block_size = first_piece.getBlockSize( j );
piece_offset = piece_offset + block_size;
if ( available == 0 ){
if ( piece_offset > first_piece_offset ){
available = piece_offset - first_piece_offset;
}
}else{
available += block_size;
}
}else{
break;
}
}
}
}else{
available = first_piece.getLength() - first_piece_offset;
for (int i=first_piece_index+1;i<=last_piece_index;i++){
if ( stop_counting_after > 0 && available >= stop_counting_after ){
break;
}
DiskManagerPiece piece = pieces[i];
if ( piece.isDone()){
available += piece.getLength();
}else{
boolean[] blocks = piece.getWritten();
if ( blocks == null ){
if ( piece.isDone()){
available += piece.getLength();
}else{
break;
}
}else{
for (int j=0;j<blocks.length;j++){
if ( blocks[j] ){
available += piece.getBlockSize( j );
}else{
break;
}
}
}
break;
}
}
}
long max_available = file.getLength() - file_start_offset;
if ( available > max_available ){
available = max_available;
}
return( available );
}
public DownloadManager
getDownloadManager()
{
return download_manager;
}
protected void
destroy()
{
synchronized( this ){
setRTA( false );
destroyed = true;
}
}
protected void
log(
String str )
{
log( str, true );
}
protected void
log(
String str,
boolean to_file )
{
log( download_manager, str, to_file );
}
protected void
log(
DownloadManager dm,
String str,
boolean to_file )
{
str = dm.toString() + ": " + str;
if ( to_file ){
AEDiagnosticsLogger diag_logger = AEDiagnostics.getLogger("v3.Stream");
diag_logger.log(str);
}
if ( ConstantsVuze.DIAG_TO_STDOUT ) {
System.out.println(Thread.currentThread().getName() + "|"
+ System.currentTimeMillis() + "] " + str);
}
}
protected class
bufferETAProvider
implements PieceRTAProvider
{
private boolean is_buffering = true;
private long[] piece_rtas;
private long last_buffer_size;
private long last_buffer_size_time;
private boolean active;
private long last_recalc;
protected void
activate(
PiecePicker picker )
{
synchronized( EnhancedDownloadManager.this ){
if ( !active ){
log( "Activating RTA provider" );
active = true;
picker.addRTAProvider( this );
}
}
}
protected void
deactivate(
PiecePicker picker )
{
synchronized( EnhancedDownloadManager.this ){
if ( active ){
log( "Deactivating RTA provider" );
picker.removeRTAProvider( this );
}
piece_rtas = null;
active = false;
}
}
protected void
checkActivation(
PiecePicker picker )
{
// might need to re-enable the buffer provider if speeds change
if ( getProgressivePlayETA() > 0 ){
synchronized( EnhancedDownloadManager.this ){
if ( piece_rtas == null ){
activate( picker );
}
}
}
}
public long[]
updateRTAs(
PiecePicker picker )
{
long mono_now = SystemTime.getMonotonousTime();
if ( mono_now - last_recalc < 500 ){
return( piece_rtas );
}
last_recalc = mono_now;
DiskManager disk_manager = download_manager.getDiskManager();
progressiveStats stats = progressive_stats;
if ( disk_manager == null || stats == null || stats.getFile().isComplete()){
deactivate( picker );
return( null );
}
EnhancedDownloadManagerFile file = stats.getFile();
long abs_provider_pos = stats.getCurrentProviderPosition( true );
long rel_provider_pos = stats.getCurrentProviderPosition( false );
long buffer_bytes = stats.getBufferBytes();
boolean buffering = getProgressivePlayETA() >= 0;
if ( buffering ){
long buffer_size = getContiguousAvailableBytes( file.getIndex(), rel_provider_pos, buffer_bytes );
if ( buffer_size == buffer_bytes ){
buffering = false;
}
}
if ( buffering != is_buffering ){
if ( buffering ){
log( "Switching to buffer mode" );
}else{
log( "Switching to RTA mode" );
}
is_buffering = buffering;
}
long piece_size = disk_manager.getPieceLength();
int start_piece = (int)( abs_provider_pos / piece_size );
int end_piece = file.getFile().getLastPieceNumber();
piece_rtas = new long[ picker.getNumberOfPieces()];
long now = SystemTime.getCurrentTime();
if ( is_buffering ){
for (int i=start_piece;i<=end_piece;i++){
// not bothered about times here but need to use large increments to ensure
// that pieces are picked in order even for slower peers
piece_rtas[i] = now+(i*60000);
}
long buffer_size = getContiguousAvailableBytes( file.getIndex(), rel_provider_pos, 0 );
if ( last_buffer_size != buffer_size ){
last_buffer_size = buffer_size;
last_buffer_size_time = now;
}else{
if ( now < last_buffer_size_time ){
last_buffer_size_time = now;
}else{
long stalled_for = now - last_buffer_size_time;
long dl_speed = progressive_stats.getDownloadBytesPerSecond();
if ( dl_speed > 0 ){
long block_time = (DiskManager.BLOCK_SIZE * 1000) / dl_speed;
if ( stalled_for > Math.max( 5000, 5*block_time )){
long target_rta = now + block_time;
int blocked_piece_index = (int)((abs_provider_pos + buffer_size ) / disk_manager.getPieceLength());
DiskManagerPiece[] pieces = disk_manager.getPieces();
if ( blocked_piece_index < pieces.length ){
if ( pieces[blocked_piece_index].isDone()){
blocked_piece_index++;
if ( blocked_piece_index < pieces.length ){
if ( pieces[blocked_piece_index].isDone()){
blocked_piece_index = -1;
}
}else{
blocked_piece_index = -1;
}
}
}
if ( blocked_piece_index >= 0 ){
piece_rtas[blocked_piece_index] = target_rta;
log( "Buffer provider: reprioritising lagging piece " + blocked_piece_index + " with rta " + block_time );
}
}
}
}
}
}else{
long bytes_offset = 0;
long max_bps = stats.getStreamBytesPerSecondMax();
for (int i=start_piece;i<=end_piece;i++){
piece_rtas[i] = now + ( 1000 * ( bytes_offset / max_bps ));
bytes_offset += piece_size;
if ( bytes_offset > buffer_bytes ){
break;
}
}
}
return( piece_rtas );
}
public long
getCurrentPosition()
{
return( 0 );
}
public long
getStartTime()
{
return( 0 );
}
public long
getStartPosition()
{
return( 0 );
}
public long
getBlockingPosition()
{
return( 0 );
}
public void
setBufferMillis(
long millis,
long delay_millis )
{
}
public String
getUserAgent()
{
return( null );
}
}
protected abstract class
progressiveStats
implements Cloneable
{
protected abstract EnhancedDownloadManagerFile
getFile();
protected abstract boolean
isProviderActive();
protected abstract long
getCurrentProviderPosition(
boolean absolute );
protected abstract long
getStreamBytesPerSecondMax();
protected abstract long
getStreamBytesPerSecondMin();
protected abstract long
getDownloadBytesPerSecond();
protected abstract long
getETA();
public abstract long
getBufferBytes();
protected abstract long
getSecondsToDownload();
protected abstract long
getSecondsToWatch();
protected abstract void
update(
int tick_count );
protected progressiveStats
getCopy()
{
try{
return((progressiveStats)clone());
}catch( CloneNotSupportedException e ){
Debug.printStackTrace(e);
return( null );
}
}
protected String
formatBytes(
long l )
{
return( DisplayFormatters.formatByteCountToKiBEtc( l ));
}
protected String
formatSpeed(
long l )
{
return( DisplayFormatters.formatByteCountToKiBEtcPerSec( l ));
}
}
protected class
progressiveStatsCommon
extends progressiveStats
{
private EnhancedDownloadManagerFile primary_file;
private PieceRTAProvider current_provider;
private String current_user_agent;
private long content_stream_bps_min;
private long content_stream_bps_max;
private Average capped_download_rate_average = AverageFactory.MovingImmediateAverage( 10 );
private Average discard_rate_average = AverageFactory.MovingImmediateAverage( 10 );
private long last_discard_bytes = download_manager.getStats().getDiscarded();
private long actual_bytes_to_download;
private long weighted_bytes_to_download; // gives less weight to incomplete pieces
private long provider_life_secs;
private long provider_initial_position;
private long provider_byte_position;
private long provider_last_byte_position = -1;
private long provider_blocking_byte_position;
private Average provider_speed_average = AverageFactory.MovingImmediateAverage( 10 );
protected
progressiveStatsCommon(
DownloadManager _dm,
EnhancedDownloadManagerFile _primary_file )
{
primary_file = _primary_file;
TOTorrent torrent = download_manager.getTorrent();
content_stream_bps_min = explicit_progressive?content_min_delivery_bps:PlatformTorrentUtils.getContentStreamSpeedBps( torrent );
if ( content_stream_bps_min == 0 ){
// hack in some test values for torrents that don't have a bps in them yet
long size = torrent.getSize();
if ( size < 200*1024*1024 ){
content_stream_bps_min = 30*1024;
}else if ( size < 1000*1024*1024L ){
content_stream_bps_min = 200*1024;
}else{
content_stream_bps_min = 400*1024;
}
}
// bump it up by a bit to be conservative to deal with fluctuations, discards etc.
content_stream_bps_min += content_stream_bps_min / content_stream_bps_min_increase_ratio;
content_stream_bps_max = content_stream_bps_min + ( content_stream_bps_min / content_stream_bps_max_increase_ratio );
setRTA( false );
log( download_manager,
"content_stream_bps=" + getStreamBytesPerSecondMin() +
",primary=" + primary_file.getFile().getIndex(),
true );
}
protected void
updateCurrentProvider(
PieceRTAProvider provider )
{
long file_start = primary_file.getByteOffestInTorrent();
if ( current_provider != provider || provider == null ){
current_provider = provider;
current_user_agent = null;
provider_speed_average = AverageFactory.MovingImmediateAverage( 10 );
if ( current_provider == null ){
provider_life_secs = 0;
provider_initial_position = file_start;
provider_byte_position = file_start;
provider_blocking_byte_position = -1;
provider_last_byte_position = -1;
}else{
provider_initial_position = Math.max( file_start, current_provider.getStartPosition());
provider_byte_position = provider_initial_position;
provider_last_byte_position = provider_initial_position;
provider_blocking_byte_position = current_provider.getBlockingPosition();
provider_life_secs = ( SystemTime.getCurrentTime() - current_provider.getStartTime()) / 1000;
if ( provider_life_secs < 0 ){
provider_life_secs = 0;
}
}
setRTA( current_provider != null );
}else{
provider_life_secs++;
if ( current_user_agent == null ){
current_user_agent = current_provider.getUserAgent();
if ( current_user_agent != null ){
log( "Provider user agent = " + current_user_agent );
}
}
provider_byte_position = Math.max( file_start, current_provider.getCurrentPosition());
provider_blocking_byte_position = current_provider.getBlockingPosition();
long bytes_read = provider_byte_position - provider_last_byte_position;
provider_speed_average.update( bytes_read );
provider_last_byte_position = provider_byte_position;
}
}
protected boolean
isProviderActive()
{
return( current_provider != null );
}
protected long
getInitialProviderPosition()
{
return( provider_initial_position );
}
protected long
getCurrentProviderPosition(
boolean absolute )
{
long res = provider_byte_position;
if ( absolute ){
if ( res == 0 ){
res = primary_file.getByteOffestInTorrent();
}
}else{
res -= primary_file.getByteOffestInTorrent();
if ( res < 0 ){
res = 0;
}
}
return( res );
}
protected long
getProviderLifeSecs()
{
return( provider_life_secs );
}
protected void
update(
int tick_count )
{
long download_rate = download_manager.getStats().getDataReceiveRate();
capped_download_rate_average.update( download_rate );
long discards = download_manager.getStats().getDiscarded();
discard_rate_average.update( discards - last_discard_bytes );
last_discard_bytes = discards;
DiskManager disk_manager = download_manager.getDiskManager();
PiecePicker picker = current_piece_pickler;
if ( getStreamBytesPerSecondMin() > 0 && disk_manager != null && picker != null ){
List providers = picker.getRTAProviders();
long max_cp = 0;
PieceRTAProvider best_provider = null;
for (int i=0;i<providers.size();i++){
PieceRTAProvider provider = (PieceRTAProvider)providers.get(i);
if ( provider.getStartTime() > 0 ){
long cp = provider.getCurrentPosition();
if ( cp >= max_cp ){
best_provider = provider;
max_cp = cp;
}
}
}
updateCurrentProvider( best_provider );
if ( best_provider != null ){
// the file channel provider will try best-effort-RTA based which will result
// in high discard - back it off based on how much slack we have
long relative_pos = getCurrentProviderPosition( false );
long buffer_bytes = getContiguousAvailableBytes( primary_file.getIndex(), relative_pos, getStreamBytesPerSecondMin() * 60 );
long buffer_secs = buffer_bytes / getStreamBytesPerSecondMin();
// don't be too aggresive with small buffers
buffer_secs = Math.max( 10, buffer_secs );
best_provider.setBufferMillis( 15*1000, buffer_secs * 1000 );
}
DiskManagerPiece[] pieces = disk_manager.getPieces();
actual_bytes_to_download = 0;
weighted_bytes_to_download = 0;
int first_incomplete_piece = -1;
int piece_size = disk_manager.getPieceLength();
int last_piece_number = primary_file.getFile().getLastPieceNumber();
for (int i=(int)(provider_byte_position/piece_size);i<=last_piece_number;i++){
DiskManagerPiece piece = pieces[i];
if ( piece.isDone()){
continue;
}
if ( first_incomplete_piece == -1 ){
first_incomplete_piece = i;
}
boolean[] blocks = piece.getWritten();
int bytes_this_piece = 0;
if ( blocks == null ){
bytes_this_piece = piece.getLength();
}else{
for (int j=0;j<blocks.length;j++){
if ( !blocks[j] ){
bytes_this_piece += piece.getBlockSize( j );
}
}
}
if ( bytes_this_piece > 0 ){
actual_bytes_to_download += bytes_this_piece;
int diff = i - first_incomplete_piece;
if ( diff == 0 ){
weighted_bytes_to_download += bytes_this_piece;
}else{
int weighted_bytes_done = piece.getLength() - bytes_this_piece;
weighted_bytes_done = ( weighted_bytes_done * ( pieces.length - i )) / (pieces.length - first_incomplete_piece);
weighted_bytes_to_download += piece.getLength() - weighted_bytes_done;
}
}
}
}
log( getString(), tick_count % LOG_PROG_STATS_TICKS == 0 );
}
protected long
getETA()
{
DiskManagerFileInfo file = primary_file.getFile();
if ( file.getLength() == file.getDownloaded()){
return( 0 );
}
long download_rate = getDownloadBytesPerSecond();
if ( download_rate <= 0 ){
return( Long.MAX_VALUE );
}
long buffer_bytes = getBufferBytes();
long buffer_done = getContiguousAvailableBytes( file.getIndex(), getCurrentProviderPosition( false ), buffer_bytes );
long rem_buffer = buffer_bytes - buffer_done; // ok as initial dl is forced in order byte buffer-rta
long rem_secs = (rem_buffer<=0)?0:(rem_buffer / download_rate);
long secs_to_download = getSecondsToDownload();
long secs_to_watch = getSecondsToWatch();
long eta = secs_to_download - secs_to_watch;
if ( rem_secs > eta && rem_secs > 0 ){
eta = rem_secs;
}
return( eta );
}
protected long
getStreamBytesPerSecondMax()
{
return( content_stream_bps_max );
}
protected long
getStreamBytesPerSecondMin()
{
return( content_stream_bps_min );
}
public long
getBufferBytes()
{
long min_dl = minimum_initial_buffer_secs_for_eta * getStreamBytesPerSecondMax();
return( min_dl );
}
protected EnhancedDownloadManagerFile
getFile()
{
return( primary_file );
}
protected long
getDownloadBytesPerSecond()
{
long original = (long)capped_download_rate_average.getAverage();
long current = original;
int dl_limit = download_manager.getStats().getDownloadRateLimitBytesPerSecond();
if ( dl_limit > 0 ){
current = Math.min( current, dl_limit );
}
int global_limit = TransferSpeedValidator.getGlobalDownloadRateLimitBytesPerSecond();
if ( global_limit > 0 ){
current = Math.min( current, global_limit );
}
return( current );
}
protected long
getSecondsToDownload()
{
long download_rate = getDownloadBytesPerSecond();
if ( download_rate == 0 ){
return( Long.MAX_VALUE );
}
return( weighted_bytes_to_download / download_rate );
}
public long
getSecondsToWatch()
{
return(( primary_file.getLength() - getCurrentProviderPosition( false )) / getStreamBytesPerSecondMin());
}
protected String
getString()
{
long dl_rate = getDownloadBytesPerSecond();
long buffer_bytes = getBufferBytes();
long buffer_done = getContiguousAvailableBytes( primary_file.getIndex(), getCurrentProviderPosition( false ), buffer_bytes );
return( "play_eta=" + getETA() + "/d=" + getSecondsToDownload() + "/w=" + getSecondsToWatch()+
", dl_rate=" + formatSpeed(dl_rate)+ ", download_rem=" + formatBytes(weighted_bytes_to_download) + "/" + formatBytes(actual_bytes_to_download) +
", discard_rate=" + formatSpeed((long)discard_rate_average.getAverage()) +
", buffer: " + buffer_bytes + "/" + buffer_done +
", prov: byte=" + formatBytes( provider_byte_position ) + " secs=" + ( provider_byte_position/getStreamBytesPerSecondMin()) + " speed=" + formatSpeed((long)provider_speed_average.getAverage()) +
" block= " + formatBytes( provider_blocking_byte_position ));
}
}
}