From 8b9a8cfb4bf5d42b3fd4a1ce7caab45f1126a304 Mon Sep 17 00:00:00 2001 From: Thomas Schmitt Date: Wed, 26 Aug 2020 16:04:06 +0200 Subject: [PATCH] New API call burn_nominal_slowdown() --- cdrskin/cdrskin_timestamp.h | 2 +- libburn/libburn.h | 55 ++++++++++++++++++++++-- libburn/libburn.ver | 1 + libburn/write.c | 85 +++++++++++++++++++++++++++---------- 4 files changed, 116 insertions(+), 27 deletions(-) diff --git a/cdrskin/cdrskin_timestamp.h b/cdrskin/cdrskin_timestamp.h index a3f0b63..d9104af 100644 --- a/cdrskin/cdrskin_timestamp.h +++ b/cdrskin/cdrskin_timestamp.h @@ -1 +1 @@ -#define Cdrskin_timestamP "2020.08.24.132739" +#define Cdrskin_timestamP "2020.08.26.140343" diff --git a/libburn/libburn.h b/libburn/libburn.h index e8e3f0d..e5a9de6 100644 --- a/libburn/libburn.h +++ b/libburn/libburn.h @@ -29,6 +29,9 @@ processing tracks of more than 2 GB size. */ #include +/* For struct timeval */ +#include + #ifndef DOXYGEN #if defined(__cplusplus) @@ -3013,6 +3016,9 @@ void burn_drive_set_speed(struct burn_drive *d, int read, int write); MMC specifies that with the Exact bit the desired speed settings shall either be obeyed by the drive exactly, or that the drive shall indicate failure and not accept the settings. + But many drives reply no error and nevertheless adjust their read speed + only coarsly or ignore the setting after a few MB of fast read attempts. + The call parameters have the same meaning as with burn_drive_set_speed(). @param d The drive to set speed for. It must be a role 1 drive. @param read Read speed in k/s (0 is max, -1 is min). @@ -3023,6 +3029,44 @@ void burn_drive_set_speed(struct burn_drive *d, int read, int write); int burn_drive_set_speed_exact(struct burn_drive *d, int read, int write); +/* ts C00822 */ +/** Waits until the time has elapsed since the given previous time to transmit + the given byte count with the given speed in KB/second (KB = 1000 bytes). + This call may be used between random access read operations like + burn_read_data() in order to force a slower speed than the drive is + willing to use if it gets read requests as fast as it delivers data. + + The parameter us_corr carries microseconds of time deviations from one + call to the next one. Such deviations may happen because of small + inexactnesses of the sleeper function and because of temporary delays + in the data supply so that sleeping for a negative time span would have + been necessary. The next call will reduce or enlarge its own sleeping + period by this value. + + @param kb_per_second the desired speed in 1000 bytes per second. + Supplied by the caller. + @max_corr the maximum backlog in microseconds which shall + be compensated by the next call. Supplied by the + caller. Not more than 1 billion = 1000 seconds. + @param prev_time time keeper updated by burn_nominal_slowdown(). + The caller provides the memory and elsewise should + carry it unchanged from call to call. + @param us_corr updated by burn_nominal_slowdown(). See above. + The caller provides the memory and elsewise should + carry it unchanged from call to call. + @param b_since_prev byte count since the previous call. This number + has to be counted and supplied by the caller. + @param flag Bitfield for control purposes: + bit0= initialize *prev_time and *us_corr, + ignore other parameters, do not wait + @return 2=no wait because no usable kb_per_second , 1=success , 0=failure + @since 1.5.4 +*/ +int burn_nominal_slowdown(int kb_per_second, int max_corr, + struct timeval *prev_time, + int *us_corr, off_t b_since_prev, int flag); + + /* ts A70711 */ /** Controls the behavior with writing when the drive buffer is suspected to be full. To check and wait for enough free buffer space before writing @@ -4067,10 +4111,13 @@ int burn_random_access_write(struct burn_drive *d, off_t byte_address, /* ts A81215 */ /** Inquire the maximum amount of readable data. - It is supposed that all LBAs in the range from 0 to capacity - 1 - can be read via burn_read_data() although some of them may never have been - recorded. If tracks are recognizable then it is better to only read - LBAs which are part of some track. + On DVD and BD it is supposed that all LBAs in the range from 0 to + capacity - 1 can be read via burn_read_data() although some of them may + never have been recorded. With multi-session CD there have to be + expected unreadable TAO Run-out blocks. + If tracks are recognizable then it is better to only read LBAs which + are part of some track and on CD to be cautious about the last two blocks + of each track which might be TAO Run-out blocks. If the drive is actually a large file or block device, then the capacity is curbed to a maximum of 0x7ffffff0 blocks = 4 TB - 32 KB. @param d The drive from which to read diff --git a/libburn/libburn.ver b/libburn/libburn.ver index 71a6e51..44c7508 100644 --- a/libburn/libburn.ver +++ b/libburn/libburn.ver @@ -108,6 +108,7 @@ burn_msf_to_sectors; burn_msgs_obtain; burn_msgs_set_severities; burn_msgs_submit; +burn_nominal_slowdown; burn_obtain_profile_name; burn_offst_source_new; burn_os_alloc_buffer; diff --git a/libburn/write.c b/libburn/write.c index d011813..4f5c544 100644 --- a/libburn/write.c +++ b/libburn/write.c @@ -2765,30 +2765,57 @@ void burn_stdio_mmc_sync_cache(struct burn_drive *d) } -/* ts A70912 */ -/* Enforces eventual nominal write speed. - @param flag bit0= initialize *prev_time */ -int burn_stdio_slowdown(struct burn_drive *d, struct timeval *prev_time, - int amount, int flag) +/* ts C00824 : API */ +/* Enforces nominal write speed */ +int burn_nominal_slowdown(int kb_per_second, int max_corr, + struct timeval *prev_time, + int *us_corr, off_t b_since_prev, int flag) { struct timeval tnow; - double to_wait; + double to_wait, goal, corr; + int abs_max_corr; if (flag & 1) { gettimeofday(prev_time, NULL); + *us_corr = 0; return 1; } - if(d->nominal_write_speed <= 0) + if (kb_per_second <= 0) return 2; + + if (max_corr < -1.0e9 || max_corr > 1.0e9) + abs_max_corr = 1000000000; + else + abs_max_corr = abs(max_corr); gettimeofday(&tnow, NULL); - to_wait = ( ((double) amount) / (double) d->nominal_write_speed ) - - (double) ( tnow.tv_sec - prev_time->tv_sec ) - - (double) ( tnow.tv_usec - prev_time->tv_usec ) / 1.0e6 - - 0.001; /* best would be 1 / kernel granularity HZ */ - if (to_wait >= 0.0001) { - usleep((int) (to_wait * 1000000.0)); + goal = ((double) b_since_prev) / 1000.0 / ((double) kb_per_second) + + ((double) prev_time->tv_sec) + + ((double) prev_time->tv_usec) / 1.0e6 + + ((double) *us_corr) / 1.0e6 ; + to_wait = goal - ((double) tnow.tv_sec) - + ((double) tnow.tv_usec) / 1.0e6; + + /* usleep might be restricted to 999999 microseconds */ + while (to_wait > 0.0) { + if (to_wait >= 0.5) { + usleep(500000); + to_wait -= 0.5; + } else if (to_wait >= 0.00001) { + usleep((int) (to_wait * 1000000.0)); + to_wait = 0.0; + } else { + to_wait = 0.0; + } } gettimeofday(prev_time, NULL); + corr = (goal - ((double) prev_time->tv_sec) - + ((double) prev_time->tv_usec) / 1.0e6) * 1.0e6; + if (corr > abs_max_corr) + *us_corr = abs_max_corr; + else if (corr < -abs_max_corr) + *us_corr = -abs_max_corr; + else + *us_corr = corr; return 1; } @@ -2801,7 +2828,7 @@ int burn_stdio_write_track(struct burn_write_opts *o, struct burn_session *s, struct burn_track *t = s->track[tnum]; struct burn_drive *d = o->drive; char *buf = NULL; - int i, prev_sync_sector = 0; + int i, prev_sync_sector = 0, us_corr = 0, max_corr = 250000; struct buffer *out = d->buffer; struct timeval prev_time; @@ -2821,7 +2848,10 @@ int burn_stdio_write_track(struct burn_write_opts *o, struct burn_session *s, d->do_simulate = o->simulate; d->sync_cache = burn_stdio_mmc_sync_cache; - burn_stdio_slowdown(d, &prev_time, 0, 1); /* initialize */ + /* initialize */ + burn_nominal_slowdown(d->nominal_write_speed, max_corr, + &prev_time, &us_corr, (off_t) 0, 1); + for (i = 0; open_ended || i < sectors; i++) { /* transact a (CD sized) sector */ if (!sector_data(o, t, 0)) @@ -2834,14 +2864,25 @@ int burn_stdio_write_track(struct burn_write_opts *o, struct burn_session *s, } d->progress.sector++; /* Flush to disk from time to time */ - if (d->progress.sector - prev_sync_sector >= - o->stdio_fsync_size && o->stdio_fsync_size > 0) { - prev_sync_sector = d->progress.sector; - if (!o->simulate) - burn_stdio_sync_cache(d->stdio_fd, d, 1); + if (o->stdio_fsync_size > 0) { + if (d->progress.sector - prev_sync_sector >= + o->stdio_fsync_size) { + if (!o->simulate) + burn_stdio_sync_cache(d->stdio_fd, d, + 1); + burn_nominal_slowdown( + d->nominal_write_speed, max_corr, + &prev_time, &us_corr, + (off_t) (d->progress.sector - + prev_sync_sector) * + (off_t) 2048, + 0); + prev_sync_sector = d->progress.sector; + } + } else if ((d->progress.sector % 512) == 0) { + burn_nominal_slowdown(d->nominal_write_speed, max_corr, + &prev_time, &us_corr, (off_t) (512 * 2048), 0); } - if ((d->progress.sector % 512) == 0) - burn_stdio_slowdown(d, &prev_time, 512 * 2, 0); } /* Pad up buffer to next full o->obs (usually 32 kB) */