Package com.aelitis.azureus.core.download

Source Code of com.aelitis.azureus.core.download.EnhancedDownloadManager$progressiveStatsCommon

/*
* 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.net.InetAddress;
import java.util.*;

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.*;
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;
import com.aelitis.azureus.util.PlayUtils;


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;
  private EnhancedDownloadManagerFile   primary_file;




 
  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 = PlayUtils.getPrimaryFileIndex( download_manager );
   
    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 ){

            progressive_active    = false;
           
            if ( current_piece_pickler != null ){
         
              buffer_provider.deactivatecurrent_piece_pickler );
             
              current_piece_pickler  = null
            }
          }
        }
       
        public void
        peerAdded(
          PEPeer   peer )
        {
        }
         
        public void
        peerRemoved(
          PEPeer  peer )
        {
        }
      });
  }

  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;
                     
      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();
   
    if ( torrent == null || primary_file == null ){

      return( false );
    }
   
    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, primary_file );
        }
      }else{
       
        progressive_stats = createProgressiveStats( download_manager, primary_file );
      }
     
      if ( !switching_progressive_downloads ){
       
        if ( active ){
         
          RealTimeInfo.setProgressiveActiveprogressive_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 DiskManagerFileInfo
  getPrimaryFile()
  {
    return( primary_file.getFile());
  }

 
  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();
   
    DiskManagerFileInfo[]   files = dm.getFiles();
   
    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.getDisplayName() + ": " + 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;

        EnhancedDownloadManagerFile file = stats.getFile();

        if ( disk_manager == null || stats == null || file.isComplete()){
            
          deactivate( picker );
         
          return( null );
        }
       
      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 ));
    }
  }
}
TOP

Related Classes of com.aelitis.azureus.core.download.EnhancedDownloadManager$progressiveStatsCommon

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.