From 4e60fb77e07fa417efebb58d673fb0b2156f55db Mon Sep 17 00:00:00 2001 From: Aidan MacDonald Date: Mon, 31 Oct 2022 11:47:37 +0000 Subject: 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 --- lib/rbcodec/codecs/mpa.c | 87 ++++++++++++++++++------------------------------ 1 file changed, 32 insertions(+), 55 deletions(-) diff --git a/lib/rbcodec/codecs/mpa.c b/lib/rbcodec/codecs/mpa.c index d374a5229a..bafed3970c 100644 --- a/lib/rbcodec/codecs/mpa.c +++ b/lib/rbcodec/codecs/mpa.c @@ -176,38 +176,27 @@ static int get_file_pos(int newtime) struct mp3entry *id3 = ci->id3; 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) { /* Use the TOC to find the new position */ - unsigned int percent, remainder; - int curtoc, nexttoc, plen; - - percent = (newtime_s*100) / length_s; + unsigned int percent = ((uint64_t)newtime * 100) / id3->length; if (percent > 99) percent = 99; - curtoc = id3->toc[percent]; - - if (percent < 99) { - nexttoc = id3->toc[percent+1]; - } else { - nexttoc = 256; - } + 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; - pos = (id3->filesize/256)*curtoc; + /* Seek to TOC mark */ + pos = cur_toc * toc_sizestep; - /* Use the remainder to get a more accurate position */ - remainder = (newtime_s*100) % length_s; - remainder = (remainder*100) / length_s; - plen = (nexttoc - curtoc)*(id3->filesize/256); - pos += (plen/100)*remainder; + /* Interpolate between this TOC mark and the next TOC mark */ + newtime -= percent * pct_timestep; + pos += (uint64_t)plength * newtime / pct_timestep; } else { /* 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) { pos = newtime * (id3->bitrate / 8); @@ -234,43 +223,31 @@ static void set_elapsed(struct mp3entry* id3) if ( id3->vbr ) { if ( id3->has_toc ) { - /* calculate elapsed time using TOC */ - int i; - unsigned int remainder, plen, relpos, nextpos; + unsigned int pct_timestep = id3->length / 100; + unsigned int toc_sizestep = id3->filesize / 256; - /* find wich percent we're at */ - for (i=0; i<100; i++ ) - if ( offset < id3->toc[i] * (id3->filesize / 256) ) + int percent; + for (percent = 0; percent < 100; ++percent) + if (offset < id3->toc[percent] * toc_sizestep) break; + if (percent > 0) + --percent; - i--; - if (i < 0) - i = 0; - - relpos = id3->toc[i]; + 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 (i < 99) - nextpos = id3->toc[i+1]; - else - nextpos = 256; - - remainder = offset - (relpos * (id3->filesize / 256)); - - /* set time for this percent (divide before multiply to prevent - overflow on long files. loss of precision is negligible on - short files) */ - elapsed = i * (id3->length / 100); + /* Set elapsed time to the TOC mark */ + elapsed = percent * pct_timestep; - /* 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; + /* Interpolate between this TOC mark and the next TOC mark */ + offset -= cur_toc * toc_sizestep; + elapsed += (uint64_t)pct_timestep * offset / plength; + } else { + /* No TOC, use an approximation (this'll be wildly inaccurate) */ + uint64_t data_size = id3->filesize - + id3->first_frame_offset - id3->id3v1len; + elapsed = (uint64_t)id3->length * offset / data_size; } } else -- cgit v1.2.3