Ejemplo n.º 1
0
CdParanoia::CdParanoia(const QString &device, bool full, bool noSkip, bool playback)
    : drive(0)
    , paranoia(0)
    , paranoiaMode(0)
    , neverSkip(noSkip)
    , maxRetries(20)
{
    paranoia = 0;
    drive = 0;
    QMutexLocker locker(&mutex);
    if (!lockedDevices.contains(device)) {
        dev = device;
        if (init()) {
            lockedDevices.insert(device);
        } else {
            dev=QString();
        }
    }

    if (!dev.isEmpty()) {
        setFullParanoiaMode(full);
        if (playback) {
            maxRetries=1;
            #ifdef CDPARANOIA_HAS_CACHEMODEL_SIZE
            paranoia_cachemodel_size(paranoia, 24);
            #endif
        }
    }
}
Ejemplo n.º 2
0
static int
cda_init (DB_fileinfo_t *_info, DB_playItem_t *it)
{
    deadbeef->pl_lock();
    const char *uri = deadbeef->pl_find_meta(it, ":URI");
    const char *nr = uri ? strchr(uri, '#') : NULL;
    if (!nr || nr == uri) {
        deadbeef->pl_unlock();
        trace ("cdda: bad name: %s\n", uri);
        return -1;
    }

    trace ("cdda: init %s\n", uri);
    const int track_nr = atoi(nr+1);
    const size_t device_length = nr - uri;
    char device[device_length+1];
    strncpy(device, uri, device_length);
    device[device_length] = '\0';
    deadbeef->pl_unlock();

    cdda_info_t *info = (cdda_info_t *)_info;
    info->cdio = cdio_open(device, DRIVER_UNKNOWN);
    if  (!info->cdio) {
        trace ("cdda: Could not open CD device\n");
        return -1;
    }

    const int need_bitrate = info->hints & DDB_DECODER_HINT_NEED_BITRATE;
    const int drive_speed = deadbeef->conf_get_int("cdda.drive_speed", 2);
    cdio_set_speed(info->cdio, need_bitrate && drive_speed < 5 ? 1<<drive_speed : -1);

    cddb_disc_t *disc = create_disc(info->cdio);
    if (!disc) {
        return -1;
    }

    const unsigned long discid = cddb_disc_get_discid(disc);
    cddb_disc_destroy(disc);

    deadbeef->pl_lock();
    const char *discid_hex = deadbeef->pl_find_meta(it, CDDB_DISCID_TAG);
    const unsigned long trk_discid = discid_hex ? strtoul(discid_hex, NULL, 16) : 0;
    deadbeef->pl_unlock();
    if (trk_discid != discid) {
        trace ("cdda: the track belongs to another disc (%x vs %x), skipped\n", trk_discid, discid);
        return -1;
    }

    if (cdio_get_track_format(info->cdio, track_nr) != TRACK_FORMAT_AUDIO) {
        trace ("cdda: Not an audio track (%d)\n", track_nr);
        return -1;
    }

    info->first_sector = cdio_get_track_lsn(info->cdio, track_nr);
    info->current_sector = info->first_sector;
    info->last_sector = info->first_sector + cdio_get_track_sec_count(info->cdio, track_nr) - 1;
    trace("cdio nchannels (should always be 2 for an audio track): %d\n", cdio_get_track_channels (info->cdio, track_nr));
    if (info->first_sector == CDIO_INVALID_LSN || info->last_sector <= info->first_sector) {
        trace ("cdda: invalid track\n");
        return -1;
    }

#if USE_PARANOIA
    if (cdio_get_driver_id(info->cdio) != DRIVER_NRG) {
        info->cdrom = cdda_identify(device, CDDA_MESSAGE_FORGETIT, NULL);
        if (info->cdrom) {
            cdda_open(info->cdrom);
            info->paranoia = paranoia_init(info->cdrom);
        }
        if (!info->paranoia) {
            trace ("cdda: cannot re-open %s for paranoia\n", device);
            return -1;
        }
        const int no_paranoia = need_bitrate || !deadbeef->conf_get_int("cdda.paranoia", 0);
        if (no_paranoia) {
            paranoia_cachemodel_size(info->paranoia, 100);
        }
        paranoia_modeset(info->paranoia, no_paranoia ? PARANOIA_MODE_DISABLE : PARANOIA_MODE_FULL^PARANOIA_MODE_NEVERSKIP);
        paranoia_seek(info->paranoia, info->first_sector, SEEK_SET);
    }
#endif
    return 0;
}
static gboolean
gst_cd_paranoia_src_open (GstAudioCdSrc * audiocdsrc, const gchar * device)
{
  GstCdParanoiaSrc *src = GST_CD_PARANOIA_SRC (audiocdsrc);
  gint i, cache_size;

  GST_DEBUG_OBJECT (src, "trying to open device %s (generic-device=%s) ...",
      device, GST_STR_NULL (src->generic_device));

  /* find the device */
  if (src->generic_device != NULL) {
    src->d = cdda_identify_scsi (src->generic_device, device, FALSE, NULL);
  } else {
    if (device != NULL) {
      src->d = cdda_identify (device, FALSE, NULL);
    } else {
      src->d = cdda_identify ("/dev/cdrom", FALSE, NULL);
    }
  }

  /* fail if the device couldn't be found */
  if (src->d == NULL)
    goto no_device;

  /* set verbosity mode */
  cdda_verbose_set (src->d, CDDA_MESSAGE_FORGETIT, CDDA_MESSAGE_FORGETIT);

  /* open the disc */
  if (cdda_open (src->d))
    goto open_failed;

  GST_INFO_OBJECT (src, "set read speed to %d", src->read_speed);
  cdda_speed_set (src->d, src->read_speed);

  for (i = 1; i < src->d->tracks + 1; i++) {
    GstAudioCdSrcTrack track = { 0, };

    track.num = i;
    track.is_audio = IS_AUDIO (src->d, i - 1);
    track.start = cdda_track_firstsector (src->d, i);
    track.end = cdda_track_lastsector (src->d, i);
    track.tags = NULL;

    gst_audio_cd_src_add_track (GST_AUDIO_CD_SRC (src), &track);
  }

  /* create the paranoia struct and set it up */
  src->p = paranoia_init (src->d);
  if (src->p == NULL)
    goto init_failed;

  paranoia_modeset (src->p, src->paranoia_mode);
  GST_INFO_OBJECT (src, "set paranoia mode to 0x%02x", src->paranoia_mode);

  if (src->search_overlap != -1) {
    paranoia_overlapset (src->p, src->search_overlap);
    GST_INFO_OBJECT (src, "search overlap set to %u", src->search_overlap);
  }

  cache_size = src->cache_size;
  if (cache_size == -1) {
    /* if paranoia mode is low (the default), assume we're doing playback */
    if (src->paranoia_mode <= PARANOIA_MODE_FRAGMENT)
      cache_size = 150;
    else
      cache_size = paranoia_cachemodel_size (src->p, -1);
  }
  paranoia_cachemodel_size (src->p, cache_size);
  GST_INFO_OBJECT (src, "set cachemodel size to %u", cache_size);

  src->next_sector = -1;

  return TRUE;

  /* ERRORS */
no_device:
  {
    GST_ELEMENT_ERROR (src, RESOURCE, OPEN_READ,
        (_("Could not open CD device for reading.")), ("cdda_identify failed"));
    return FALSE;
  }
open_failed:
  {
    GST_ELEMENT_ERROR (src, RESOURCE, OPEN_READ,
        (_("Could not open CD device for reading.")), ("cdda_open failed"));
    cdda_close (src->d);
    src->d = NULL;
    return FALSE;
  }
init_failed:
  {
    GST_ELEMENT_ERROR (src, LIBRARY, INIT,
        ("failed to initialize paranoia"), ("failed to initialize paranoia"));
    return FALSE;
  }
}
Ejemplo n.º 4
0
int analyze_cache(cdrom_drive *d, FILE *progress, FILE *log, int speed){

  /* Some assumptions about timing: 

     We can't perform cache determination timing based on looking at
     average transfer times; on slow setups, the speed of a drive
     reading sectors via PIO will not be reliably distinguishable from
     the same drive returning data from the cache via pio.  We need
     something even more noticable and reliable: the seek time. It is
     unlikely we'd ever see a seek latency of under ~10ms given the
     synchronization requirements of a CD and the maximum possible
     rotational velocity. A cache hit would always be faster, even
     with PIO.

     Further complicating things, we have to watch the data collection
     carefully as we're not always going to be on an unloaded system,
     and we even have to guard against other apps accessing the drive
     (something that should never happen on purpose, but could happen
     by accident).  As we know in our testing when seeks should never
     occur, a sudden seek-sized latency popping up in the middle of a
     collection is an indication that collection is possibly invalid.

     A second cause of 'spurious latency' would be media damage; if
     we're consistently hitting latency on the same sector during
     initial collection, may need to move past it. */

  int i,j,ret=0,x;
  int firstsector=-1;
  int lastsector=-1;
  int firsttest=-1;
  int lasttest=-1;
  int offset;
  int warn=0;
  int current=1000;
  int hi=15000;
  int cachesize=0;
  int readahead=0;
  int rollbehind=0;
  int cachegran=0;
  float mspersector=0;
  if(speed<=0)speed=-1;

  reportC("\n=================== Checking drive cache/timing behavior ===================\n");
  d->error_retry=0;

  /* verify the lib and cache analysis match */
  if(strcmp(VERSIONNUM,paranoia_version())){
    reportC("\nWARNING: cdparanoia application (and thus the cache tests) does not match the"
	    "\ninstalled (or in use) libcdda_paranoia.so library.  The final verdict of this"
	    "\ntesting may or may not be accurate for the actual version of the paranoia"
	    "library.  Continuing anyway...\n\n");
  }

  /* find the longest stretch of available audio data */

  for(i=0;i<d->tracks;i++){
    if(cdda_track_audiop(d,i+1)==1){
      if(firsttest == -1)
	firsttest=cdda_track_firstsector(d,i+1);
      lasttest=cdda_track_lastsector(d,i+1);
      if(lasttest-firsttest > lastsector-firstsector){
	firstsector=firsttest;
	lastsector=lasttest;
      }
    }else{
      firsttest=-1;
      lasttest=-1;
    }
  }

  if(firstsector==-1){
    reportC("\n\tNo audio on disc; Cannot determine timing behavior...");
    return -1;
  }

  /* Dump some initial timing data to give a little context for human
     eyes.  Take readings ten minutes apart (45000 sectors) and at end of disk. */
  {
    int best=0;
    int bestcount=0;
    int iterating=0;

    offset = lastsector-firstsector-current-1;

    reportC("\nSeek/read timing:\n");

    while(offset>=firstsector){
      int m = offset/4500;
      int s = (offset-m*4500)/75;
      int f = offset-m*4500-s*75;
      int sofar;

      if(iterating){
	reportC("\n");
      }else{
	printC("\r");
	logC("\n");
      }
      reportC("\t[%02d:%02d.%02d]: ",m,s,f);

      /* initial seek to put at at a small offset past end of upcoming reads */
      if((ret=cdda_read(d,NULL,offset+current+1,1))<0){
	/* media error! grr!  retry elsewhere */
	if(ret==-404)return -1;
	reportC("\n\tWARNING: media error during read; continuing at next offset...");
	offset = (offset-firstsector+44999)/45000*45000+firstsector;
	offset-=45000;
	continue;
      }
  
      sofar=time_drive(d,progress, log, offset, current, 1);
      if(offset==firstsector)mspersector = sofar/(float)current;
      if(sofar==-404)
	return -1;
      else if(sofar<0){
	reportC("\n\tWARNING: media error during read; continuing at next offset...");
	offset = (offset-firstsector+44999)/45000*45000+firstsector;
	offset-=45000;
	continue;
      }else{
	if(!iterating){
	  if(best==0 || sofar*1.01<best){
	    best= sofar;
	    bestcount=0;
	  }else{
	    bestcount+=sofar;
	    if(bestcount>sofar && bestcount>4000)
	      iterating=1;
	  }
	}
      }

      if(iterating){
	offset = (offset-firstsector+44999)/45000*45000+firstsector;
	offset-=45000;
	printC("                 ");
      }else{
	offset--;
	printC(" spinning up...  ");
      }
    }
  }

  reportC("\n\nAnalyzing cache behavior...\n");
  
  /* search on cache size; cache hits are fast, seeks are not, so a
     linear search through cache hits up to a miss are faster than a
     bisection */
  {
    int under=1;
    int onex=0;
    current=0;
    offset = firstsector+10;
    
    while(current <= hi && under){
      int i,j;
      under=0;
      current++;
      
      if(onex){
	if(speed==-1){
	  logC("\tAttempting to reset read speed to full... ");
	}else{
	  logC("\tAttempting to reset read speed to %dx... ",speed);
	}
	if(cdda_speed_set(d,speed)){
	  logC("failed.\n");
	}else{
	  logC("drive said OK\n");
	}
	onex=0;
      }

      printC("\r");
      reportC("\tFast search for approximate cache size... %d sectors            ",current-1);
      logC("\n");
      
      for(i=0;i<25 && !under;i++){
	for(j=0;;j++){
	  int ret1=0,ret2=0;
	  if(i>=15){
	    int sofar=0;
	    
	    if(i==15){
	      printC("\r");
	      reportC("\tSlow verify for approximate cache size... %d sectors",current-1);
	      logC("\n");
	      
	      logC("\tAttempting to reduce read speed to 1x... ");
	      if(cdda_speed_set(d,1)){
		logC("failed.\n");
	      }else{
		logC("drive said OK\n");
	      }
	      onex=1;
	    }
	    printC(".");
	    logC("\t\t>>> ");
	    
	    while(sofar<current){
	      ret1 = cdda_read_timed(d,NULL,offset+sofar,current-sofar,&x);
	      logC("slow_read=%d:%d:%d ",offset+sofar,ret1,x);
	      if(ret1<=0)break;
	      sofar+=ret1;
	    }
	  }else{
	    ret1 = cdda_read_timed(d,NULL,offset+current-1,1,&x);
	    logC("\t\t>>> fast_read=%d:%d:%d ",offset+current-1,ret1,x);

	    /* Some drives are 'easily distracted' when readahead is
	       running ahead of the read cursor, causing accesses of
	       the earliest sectors in the cache to show bursty
	       latency. If there's no seek here after a borderline
	       long read of the earliest sector in the cache, then the
	       cache must not have been dumped yet. */

	    if(ret==1 && i && x<MIN_SEEK_MS){ 
	      under=1;
	      logC("\n");
	      break;
	    }
	  }
	  ret2 = cdda_read_timed(d,NULL,offset,1,&x);
	  logC("seek_read=%d:%d:%d\n",offset,ret2,x);
	  
	  if(ret1<=0 || ret2<=0){
	    offset+=current+100;
	    if(j==10 || offset+current>lastsector){
	      reportC("\n\tToo many read errors while performing drive cache checks;"
		      "\n\t  aborting test.\n\n");
	      return(-1);
	    }
	    reportC("\n\tRead error while performing drive cache checks;"
		    "\n\t  choosing new offset and trying again.\n");
	  }else{
	    if(x==-1){
	      reportC("\n\tTiming error while performing drive cache checks; aborting test.\n");
	      return(-1);
	    }else{
	      if(x<MIN_SEEK_MS){
		under=1;
	      }
	      break;
	    }
	  }
	}
      }
    } 
  }
  cachesize=current-1;

  printC("\r");
  if(cachesize==hi){
    reportC("\tWARNING: Cannot determine drive cache size or behavior!          \n");
    return 1;
  }else if(cachesize){
    reportC("\tApproximate random access cache size: %d sector(s)               \n",cachesize);
  }else{
    reportC("\tDrive does not cache nonlinear access                            \n");
    return 0;
  }
  
  /* does the readahead cache exceed the maximum Paranoia currently expects? */
  {
    cdrom_paranoia *p=paranoia_init(d);
    if(cachesize > paranoia_cachemodel_size(p,-1)){
      reportC("\nWARNING: This drive appears to be caching more sectors of\n"
	      "           readahead than Paranoia can currently handle!\n");
      warn=1;
      
    }
    paranoia_free(p);
  }
  if(speed==-1){
    logC("\tAttempting to reset read speed to full... ");
  }else{
    logC("\tAttempting to reset read speed to %d... ",speed);
  }
  if(cdda_speed_set(d,speed)){
    logC("failed.\n");
  }else{
    logC("drive said OK\n");
  }

  /* This is similar to the Fast search above, but just in case the
     cache is being tracked as multiple areas that are treated
     differently if non-contiguous.... */
  {
    int seekoff = cachesize*3;
    int under=0;
    reportC("\tVerifying that cache is contiguous...");
  
    for(i=0;i<20 && !under;i++){
      printC(".");
      for(j=0;;j++){
	int ret1,ret2;

	if(offset+seekoff>lastsector){
	  reportC("\n\tOut of readable space on CDROM while performing drive checks;"
		  "\n\t  aborting test.\n\n");
	  return(-1);
	}
	

	ret1 = cdda_read_timed(d,NULL,offset+seekoff,1,&x);
	logC("\t\t>>> %d:%d:%d ",offset+seekoff,ret1,x);
	ret2 = cdda_read_timed(d,NULL,offset,1,&x);
	logC("seek_read:%d:%d:%d\n",offset,ret2,x);
	
	if(ret1<=0 || ret2<=0){
	  offset+=cachesize+100;
	  if(j==10){
	    reportC("\n\tToo many read errors while performing drive cache checks;"
		    "\n\t  aborting test.\n\n");
	    return(-1);
	  }
	  reportC("\n\tRead error while performing drive cache checks;"
		  "\n\t  choosing new offset and trying again.\n");
	}else{
	  if(x==-1){
	    reportC("\n\tTiming error while performing drive cache checks; aborting test.\n");
	    return(-1);
	  }else{
	    if(x<MIN_SEEK_MS)under=1;
	    break;
	  }
	}
      }
    }
    printC("\r");
    if(under){
      reportC("\nWARNING: Drive cache does not appear to be contiguous!\n");
      warn=1;
    }else{
      reportC("\tDrive cache tests as contiguous                           \n");
    }
  }

  /* The readahead cache size ascertained above is likely qualified by
     background 'rollahead'; that is, the drive's readahead process is
     often working ahead of our actual linear reads, and if reads stop
     or are interrupted, readahead continues and overflows the cache.
     It is also the case that the cache size we determined above is
     slightly too low because readahead is probably always working
     ahead of reads. 

     Determine the rollahead size a few ways (which may disagree:
     1) Read number of sectors equal to cache size; pause; read backward until seek
     2) Read sectors equal to cache-rollahead; verify reading back to beginning does not seek 
     3) Read sectors equal to cache; pause; read ahead until seek delay
  */

  {
    int lower=0;
    int gran=64;
    int it=3;
    int tests=0;
    int under=1;
    readahead=0;
    
    while(gran>1 || under){
      tests++;
      if(tests>8 && gran<64){
	gran<<=3;
	tests=0;
	it=3;
      }
      if(gran && !under){
	gran>>=3;
	tests=0;
	if(gran==1)it=10;
      }

      under=0;
      readahead=lower+gran;

      printC("\r");
      logC("\n");
      reportC("\tTesting background readahead past read cursor... %d",readahead);
      printC("           \b\b\b\b\b\b\b\b\b\b\b");
      for(i=0;i<it;i++){
	int sofar=0,ret;
	logC("\n\t\t%d >>> ",i);

	while(sofar<cachesize){
	  ret = cdda_read_timed(d,NULL,offset+sofar,cachesize-sofar,&x);
	  if(ret<=0)goto error;
	  logC("%d:%d:%d ",offset+sofar,ret,x);

	  /* some drives can lose sync and perform an internal resync,
	     which can also cause readahead to restart.  If we see
	     seek-like delays during the initial cahe load, retry the
	     preload */

	  sofar+=ret;
	}
	
	printC(".");

	/* what we'd predict is needed to let the readahead process work. */
	{
	  int usec=mspersector*(readahead)*(6+i)*200;
	  int max= 13000*2*readahead; /* corresponds to .5x */
	  if(usec>max)usec=max;
	  logC("sleep=%dus ",usec);
	  usleep(usec);
	}
	
	/* seek to offset+cachesize+readahead */
	ret = cdda_read_timed(d,NULL,offset+cachesize+readahead-1,1,&x);
	if(ret<=0)break;
	logC("seek=%d:%d:%d",offset+cachesize+readahead-1,ret,x);
	if(x<MIN_SEEK_MS){
	  under=1;
	  break;
	}else if(i%3==1){
	  /* retime the drive just to be conservative */
	  mspersector=retime_drive(d, progress, log, offset, readahead, mspersector);
	}
      }
      
      if(under)
	lower=readahead;

    }
    readahead=lower;
  }