codecs: mpa: Improve seek & resume accuracy for VBR files

The codec used 32-bit math for elapsed time <-> file position
calculations. The rounding errors seem to be the cause of poor
seek/resume accuracy on long VBR files; switching to 64-bit math
makes things much better.

Change-Id: Iba638d9e031a891022510c31c141cc4541e3f149
This commit is contained in:
Aidan MacDonald 2022-10-31 11:47:37 +00:00
parent 26ffcd8f9f
commit 4e60fb77e0

View file

@ -176,38 +176,27 @@ static int get_file_pos(int newtime)
struct mp3entry *id3 = ci->id3; struct mp3entry *id3 = ci->id3;
if (id3->vbr) { if (id3->vbr) {
/* Convert newtime and id3->length to seconds to
* avoid overflow */
unsigned int newtime_s = newtime/1000;
unsigned int length_s = id3->length/1000;
if (id3->has_toc) { if (id3->has_toc) {
/* Use the TOC to find the new position */ /* Use the TOC to find the new position */
unsigned int percent, remainder; unsigned int percent = ((uint64_t)newtime * 100) / id3->length;
int curtoc, nexttoc, plen;
percent = (newtime_s*100) / length_s;
if (percent > 99) if (percent > 99)
percent = 99; percent = 99;
curtoc = id3->toc[percent]; unsigned int pct_timestep = id3->length / 100;
unsigned int toc_sizestep = id3->filesize / 256;
unsigned int cur_toc = id3->toc[percent];
unsigned int next_toc = percent < 99 ? id3->toc[percent+1] : 256;
unsigned int plength = (next_toc - cur_toc) * toc_sizestep;
if (percent < 99) { /* Seek to TOC mark */
nexttoc = id3->toc[percent+1]; pos = cur_toc * toc_sizestep;
} else {
nexttoc = 256;
}
pos = (id3->filesize/256)*curtoc; /* Interpolate between this TOC mark and the next TOC mark */
newtime -= percent * pct_timestep;
/* Use the remainder to get a more accurate position */ pos += (uint64_t)plength * newtime / pct_timestep;
remainder = (newtime_s*100) % length_s;
remainder = (remainder*100) / length_s;
plen = (nexttoc - curtoc)*(id3->filesize/256);
pos += (plen/100)*remainder;
} else { } else {
/* No TOC exists, estimate the new position */ /* No TOC exists, estimate the new position */
pos = (id3->filesize / length_s) * newtime_s; pos = (uint64_t)newtime * id3->filesize / id3->length;
} }
} else if (id3->bitrate) { } else if (id3->bitrate) {
pos = newtime * (id3->bitrate / 8); pos = newtime * (id3->bitrate / 8);
@ -234,43 +223,31 @@ static void set_elapsed(struct mp3entry* id3)
if ( id3->vbr ) { if ( id3->vbr ) {
if ( id3->has_toc ) { if ( id3->has_toc ) {
/* calculate elapsed time using TOC */ unsigned int pct_timestep = id3->length / 100;
int i; unsigned int toc_sizestep = id3->filesize / 256;
unsigned int remainder, plen, relpos, nextpos;
/* find wich percent we're at */ int percent;
for (i=0; i<100; i++ ) for (percent = 0; percent < 100; ++percent)
if ( offset < id3->toc[i] * (id3->filesize / 256) ) if (offset < id3->toc[percent] * toc_sizestep)
break; break;
if (percent > 0)
--percent;
i--; unsigned int cur_toc = id3->toc[percent];
if (i < 0) unsigned int next_toc = percent < 99 ? id3->toc[percent+1] : 256;
i = 0; unsigned int plength = (next_toc - cur_toc) * toc_sizestep;
relpos = id3->toc[i]; /* Set elapsed time to the TOC mark */
elapsed = percent * pct_timestep;
if (i < 99) /* Interpolate between this TOC mark and the next TOC mark */
nextpos = id3->toc[i+1]; offset -= cur_toc * toc_sizestep;
else elapsed += (uint64_t)pct_timestep * offset / plength;
nextpos = 256; } else {
/* No TOC, use an approximation (this'll be wildly inaccurate) */
remainder = offset - (relpos * (id3->filesize / 256)); uint64_t data_size = id3->filesize -
id3->first_frame_offset - id3->id3v1len;
/* set time for this percent (divide before multiply to prevent elapsed = (uint64_t)id3->length * offset / data_size;
overflow on long files. loss of precision is negligible on
short files) */
elapsed = i * (id3->length / 100);
/* calculate remainder time */
plen = (nextpos - relpos) * (id3->filesize / 256);
elapsed += (((remainder * 100) / plen) * (id3->length / 10000));
}
else {
/* no TOC exists. set a rough estimate using average bitrate */
int tpk = id3->length /
((id3->filesize - id3->first_frame_offset - id3->id3v1len) /
1024);
elapsed = offset / 1024 * tpk;
} }
} }
else else