diff options
Diffstat (limited to 'lib/rbcodec/codecs/libm4a/m4a.c')
-rw-r--r-- | lib/rbcodec/codecs/libm4a/m4a.c | 203 |
1 files changed, 130 insertions, 73 deletions
diff --git a/lib/rbcodec/codecs/libm4a/m4a.c b/lib/rbcodec/codecs/libm4a/m4a.c index 5fe778ac03..ecae2ad633 100644 --- a/lib/rbcodec/codecs/libm4a/m4a.c +++ b/lib/rbcodec/codecs/libm4a/m4a.c @@ -23,6 +23,13 @@ #include <inttypes.h> #include "m4a.h" +#undef DEBUGF +#if defined(DEBUG) +#define DEBUGF stream->ci->debugf +#else +#define DEBUGF(...) +#endif + /* Implementation of the stream.h functions used by libalac */ #define _Swap32(v) do { \ @@ -107,96 +114,145 @@ void stream_create(stream_t *stream,struct codec_api* ci) /* Check if there is a dedicated byte position contained for the given frame. * Return this byte position in case of success or return -1. This allows to - * skip empty samples. - * During standard playback the search result (index i) will always increase. + * skip empty samples. + * During standard playback the search result (index i) will always increase. * Therefor we save this index and let the caller set this value again as start - * index when calling m4a_check_sample_offset() for the next frame. This + * index when calling m4a_check_sample_offset() for the next frame. This * reduces the overall loop count significantly. */ int m4a_check_sample_offset(demux_res_t *demux_res, uint32_t frame, uint32_t *start) { uint32_t i = *start; - for (i=0; i<demux_res->num_lookup_table; ++i) + for (;i < demux_res->num_lookup_table; ++i) { - if (demux_res->lookup_table[i].sample > frame || - demux_res->lookup_table[i].offset == 0) - return -1; - if (demux_res->lookup_table[i].sample == frame) + if (demux_res->lookup_table[i].sample > frame) break; + + if (demux_res->lookup_table[i].sample == frame) + { + *start = i; + return demux_res->lookup_table[i].offset; + } } *start = i; - return demux_res->lookup_table[i].offset; + return -1; } -/* Find the exact or preceding frame in lookup_table[]. Return both frame - * and byte position of this match. */ -static void gather_offset(demux_res_t *demux_res, uint32_t *frame, uint32_t *offset) +/* Seek to desired sound sample location. Return 1 on success (and modify + * sound_samples_done and current_sample), 0 if failed. */ +unsigned int m4a_seek(demux_res_t* demux_res, stream_t* stream, + uint64_t sound_sample_loc, uint64_t* sound_samples_done, + uint32_t* current_sample, uint32_t* lookup_table_idx) { - uint32_t i = 0; - for (i=0; i<demux_res->num_lookup_table; ++i) + uint32_t i, sample_i; + uint32_t time, time_cnt, time_dur; + uint32_t chunk, chunk_first_sample; + uint32_t offset; + uint64_t sound_sample_i; + time_to_sample_t *tts_tab = demux_res->time_to_sample; + sample_offset_t *tco_tab = demux_res->lookup_table; + uint32_t *tsz_tab = demux_res->sample_byte_sizes; + + /* First check we have the required metadata - we should always have it. */ + if (!demux_res->num_time_to_samples || !demux_res->num_sample_byte_sizes) { - if (demux_res->lookup_table[i].offset == 0) - break; - if (demux_res->lookup_table[i].sample > *frame) + return 0; + } + + /* The 'sound_sample_loc' we have is PCM-based and not directly usable. + * We need to convert it to an MP4 sample number 'sample_i' first. */ + sample_i = sound_sample_i = 0; + for (time = 0; time < demux_res->num_time_to_samples; ++time) + { + time_cnt = tts_tab[time].sample_count; + time_dur = tts_tab[time].sample_duration; + uint32_t time_var = time_cnt * time_dur; + + if (sound_sample_loc < sound_sample_i + time_var) + { + time_var = sound_sample_loc - sound_sample_i; + sample_i += time_var / time_dur; break; + } + + sample_i += time_cnt; + sound_sample_i += time_var; } - i = (i>0) ? i-1 : 0; /* We want the last chunk _before_ *frame. */ - *frame = demux_res->lookup_table[i].sample; - *offset = demux_res->lookup_table[i].offset; -} -/* Seek to desired sound sample location. Return 1 on success (and modify - * sound_samples_done and current_sample), 0 if failed. - * - * Find the sample (=frame) that contains the given sound sample, find a best - * fit for this sample in the lookup_table[], seek to the byte position. */ -unsigned int m4a_seek(demux_res_t* demux_res, stream_t* stream, - uint32_t sound_sample_loc, uint32_t* sound_samples_done, - int* current_sample) -{ - uint32_t i = 0; - uint32_t tmp_var, tmp_cnt, tmp_dur; - uint32_t new_sample = 0; /* Holds the amount of chunks/frames. */ - uint32_t new_sound_sample = 0; /* Sums up total amount of samples. */ - uint32_t new_pos; /* Holds the desired chunk/frame index. */ - - /* First check we have the appropriate metadata - we should always - * have it. - */ - if (!demux_res->num_time_to_samples || !demux_res->num_sample_byte_sizes) - { - return 0; + /* Find the chunk after 'sample_i'. */ + for (chunk = 1; chunk < demux_res->num_lookup_table; ++chunk) + { + if (tco_tab[chunk].sample > sample_i) + break; } - /* Find the destination block from time_to_sample array */ - time_to_sample_t *tab = demux_res->time_to_sample; - while (i < demux_res->num_time_to_samples) + /* The preceding chunk is the one that contains 'sample_i'. */ + chunk--; + *lookup_table_idx = chunk; + chunk_first_sample = tco_tab[chunk].sample; + offset = tco_tab[chunk].offset; + + /* Compute the PCM sample number of the chunk's first sample + * to get an accurate base for sound_sample_i. */ + i = sound_sample_i = 0; + for (time = 0; time < demux_res->num_time_to_samples; ++time) { - tmp_cnt = tab[i].sample_count; - tmp_dur = tab[i].sample_duration; - tmp_var = tmp_cnt * tmp_dur; - if (sound_sample_loc <= new_sound_sample + tmp_var) + time_cnt = tts_tab[time].sample_count; + time_dur = tts_tab[time].sample_duration; + + if (chunk_first_sample < i + time_cnt) { - tmp_var = (sound_sample_loc - new_sound_sample); - new_sample += tmp_var / tmp_dur; - new_sound_sample += tmp_var; + sound_sample_i += (chunk_first_sample - i) * time_dur; break; } - new_sample += tmp_cnt; - new_sound_sample += tmp_var; - ++i; + + i += time_cnt; + sound_sample_i += time_cnt * time_dur; } - /* We know the new sample (=frame), now calculate the file position. */ - gather_offset(demux_res, &new_sample, &new_pos); + if (demux_res->sample_byte_sizes_offset) + { + stream->ci->seek_buffer(demux_res->sample_byte_sizes_offset + chunk_first_sample * 4); + } - /* We know the new file position, so let's try to seek to it */ - if (stream->ci->seek_buffer(new_pos)) + if (tsz_tab || demux_res->sample_byte_sizes_offset) { - *sound_samples_done = new_sound_sample; - *current_sample = new_sample; + /* We have a sample-to-bytes table available so we can do accurate + * seeking. Move one sample at a time and update the file offset and + * PCM sample offset as we go. */ + for (i = chunk_first_sample; + i < sample_i && i < demux_res->num_sample_byte_sizes; ++i) + { + /* this could be unnecessary */ + if (time_cnt == 0 && ++time < demux_res->num_time_to_samples) + { + time_cnt = tts_tab[time].sample_count; + time_dur = tts_tab[time].sample_duration; + } + + offset += tsz_tab ? tsz_tab[i] : stream_read_uint32(stream); + sound_sample_i += time_dur; + time_cnt--; + } + } else { + /* No sample-to-bytes table available so we can only seek to the + * start of a chunk, which is often much lower resolution. */ + sample_i = chunk_first_sample; + } + + DEBUGF("seek chunk=%lu, chunk_first_sample=%lu, sample_i=%u, soundsample=%lu, offset=%lu\n", + (unsigned long)chunk, + (unsigned long)chunk_first_sample, + sample_i, + (unsigned long)sound_sample_i, + (unsigned long)offset); + + if (stream->ci->seek_buffer(offset)) + { + *sound_samples_done = sound_sample_i; + *current_sample = sample_i; return 1; } - + return 0; } @@ -208,7 +264,7 @@ unsigned int m4a_seek(demux_res_t* demux_res, stream_t* stream, * 1) the lookup_table array contains the file offset for the first sample * of each chunk. * - * 2) the time_to_sample array contains the duration (in sound samples) + * 2) the time_to_sample array contains the duration (in sound samples) * of each sample of data. * * Locate the chunk containing location (using lookup_table), find the first @@ -216,28 +272,29 @@ unsigned int m4a_seek(demux_res_t* demux_res, stream_t* stream, * calculate the sound_samples_done value. */ unsigned int m4a_seek_raw(demux_res_t* demux_res, stream_t* stream, - uint32_t file_loc, uint32_t* sound_samples_done, - int* current_sample) + uint32_t file_loc, uint64_t* sound_samples_done, + uint32_t* current_sample, uint32_t* lookup_table_idx) { uint32_t i; uint32_t chunk_sample = 0; uint32_t total_samples = 0; - uint32_t new_sound_sample = 0; + uint64_t new_sound_sample = 0; uint32_t tmp_dur; uint32_t tmp_cnt; uint32_t new_pos; - /* We know the desired byte offset, search for the chunk right before. + /* We know the desired byte offset, search for the chunk right before. * Return the associated sample to this chunk as chunk_sample. */ - for (i=0; i < demux_res->num_lookup_table; ++i) + for (i = 1; i < demux_res->num_lookup_table; ++i) { if (demux_res->lookup_table[i].offset > file_loc) break; } - i = (i>0) ? i-1 : 0; /* We want the last chunk _before_ file_loc. */ + --i; /* We want the last chunk _before_ file_loc. */ + *lookup_table_idx = i; chunk_sample = demux_res->lookup_table[i].sample; new_pos = demux_res->lookup_table[i].offset; - + /* Get sound sample offset. */ i = 0; time_to_sample_t *tab2 = demux_res->time_to_sample; @@ -249,19 +306,19 @@ unsigned int m4a_seek_raw(demux_res_t* demux_res, stream_t* stream, new_sound_sample += tmp_cnt * tmp_dur; if (chunk_sample <= total_samples) { - new_sound_sample += (chunk_sample - total_samples) * tmp_dur; + new_sound_sample -= (total_samples - chunk_sample) * tmp_dur; break; } ++i; } /* Go to the new file position. */ - if (stream->ci->seek_buffer(new_pos)) + if (stream->ci->seek_buffer(new_pos)) { *sound_samples_done = new_sound_sample; *current_sample = chunk_sample; return 1; - } + } return 0; } |