From 2f278af7606039857bb92d659c811403f27ad463 Mon Sep 17 00:00:00 2001 From: Aidan MacDonald Date: Sun, 13 Nov 2022 10:59:26 +0000 Subject: codecs: alac: Improve resume accuracy and clean up rounding errors Resume by offset was obviously inaccurate for ALAC -- it tried to convert the offset to an elapsed time using the approximate bitrate, which is going to be wrong for VBR files. This became a problem since commit 26ffcd8f9f restored the ability to resume by offset. It turns out that m4a_seek_raw() has terrible resolution since it can only seek to chunk boundaries, and lies about the real sample position; basically the same issue that affected seeking described in commit 4dd3c2b33e. Resuming by offset is still not very accurate because of this. Prefer to resume by time first, which is normally highly accurate (and never worse than offset) but use the file offset if it's the only thing we have. There were a couple time calculations still using 32-bit math, so clean those up too to reduce issues due to rounding errors. Change-Id: Idd3bccd67505f4e59e784d92e45ea80a273975bb --- lib/rbcodec/codecs/alac.c | 64 ++++++++++++++++++++++++----------------------- 1 file changed, 33 insertions(+), 31 deletions(-) diff --git a/lib/rbcodec/codecs/alac.c b/lib/rbcodec/codecs/alac.c index 903ea2850e..052a44cd70 100644 --- a/lib/rbcodec/codecs/alac.c +++ b/lib/rbcodec/codecs/alac.c @@ -31,6 +31,12 @@ CODEC_HEADER static int32_t outputbuffer[ALAC_MAX_CHANNELS][ALAC_BLOCKSIZE] IBSS_ATTR; +static void set_elapsed_samples(uint32_t samplesdone) +{ + uint32_t elapsedtime = (uint64_t)samplesdone * 1000ULL / ci->id3->frequency; + ci->set_elapsed(elapsedtime); +} + /* this is the codec entry point */ enum codec_status codec_main(enum codec_entry_call_reason reason) { @@ -50,12 +56,14 @@ enum codec_status codec_run(void) demux_res_t demux_res; stream_t input_stream; uint32_t samplesdone; - uint32_t elapsedtime; int samplesdecoded; unsigned int i; unsigned char* buffer; alac_file alac; intptr_t param; + unsigned long resume_time; + uint32_t resume_offset; + unsigned int did_resume; /* Clean and initialize decoder structures */ memset(&demux_res , 0, sizeof(demux_res)); @@ -71,10 +79,10 @@ enum codec_status codec_run(void) stream_create(&input_stream,ci); - /* Read resume info before calling qtmovie_read. */ - elapsedtime = ci->id3->elapsed; - samplesdone = ci->id3->offset; - + /* Save resume info because qtmovie_read() can modify it. */ + resume_time = ci->id3->elapsed; + resume_offset = ci->id3->offset; + /* if qtmovie_read returns successfully, the stream is up to * the movie data, which can be used directly by the decoder */ if (!qtmovie_read(&input_stream, &demux_res)) { @@ -84,28 +92,24 @@ enum codec_status codec_run(void) /* initialise the sound converter */ alac_set_info(&alac, demux_res.codecdata); - - /* Set i for first frame, seek to desired sample position for resuming. */ - i=0; - - if (elapsedtime || samplesdone) { - if (samplesdone) { - samplesdone = - (uint32_t)((uint64_t)samplesdone*ci->id3->frequency / - (ci->id3->bitrate*128)); - } - else { - samplesdone = (elapsedtime/10) * (ci->id3->frequency/100); - } - if (!m4a_seek(&demux_res, &input_stream, samplesdone, - &samplesdone, (int*) &i)) { - samplesdone = 0; - } + if (resume_time) + did_resume = m4a_seek(&demux_res, &input_stream, + (uint64_t)resume_time * ci->id3->frequency / 1000ULL, + &samplesdone, (int *) &i); + else if (resume_offset) + did_resume = m4a_seek_raw(&demux_res, &input_stream, resume_offset, + &samplesdone, (int *) &i); + else + did_resume = 0; + + /* Start from the beginning if we did not resume. */ + if (!did_resume) { + i = 0; + samplesdone = 0; } - elapsedtime = samplesdone * 1000LL / ci->id3->frequency; - ci->set_elapsed(elapsedtime); + set_elapsed_samples(samplesdone); /* The main decoding loop */ while (i < demux_res.num_sample_byte_sizes) { @@ -117,11 +121,10 @@ enum codec_status codec_run(void) /* Deal with any pending seek requests */ if (action == CODEC_ACTION_SEEK_TIME) { if (m4a_seek(&demux_res, &input_stream, - (param/10) * (ci->id3->frequency/100), - &samplesdone, (int *)&i)) { - elapsedtime=samplesdone*1000LL/ci->id3->frequency; - } - ci->set_elapsed(elapsedtime); + (uint64_t)param * ci->id3->frequency / 1000ULL, + &samplesdone, (int *) &i)) + set_elapsed_samples(samplesdone); + ci->seek_complete(); } @@ -140,8 +143,7 @@ enum codec_status codec_run(void) /* Update the elapsed-time indicator */ samplesdone+=samplesdecoded; - elapsedtime=samplesdone*1000LL/ci->id3->frequency; - ci->set_elapsed(elapsedtime); + set_elapsed_samples(samplesdone); i++; } -- cgit