Index: apps/codecs/aac.c =================================================================== --- apps/codecs/aac.c (revision 29719) +++ apps/codecs/aac.c (working copy) @@ -47,8 +47,7 @@ stream_t input_stream; uint32_t sound_samples_done; uint32_t elapsed_time; - uint32_t sample_duration; - uint32_t sample_byte_size; + int max_frame_size; int file_offset; int framelength; int lead_trim = 0; @@ -157,6 +156,15 @@ i = 0; + /************************** + * TEST BEGIN + *************************/ + max_frame_size = demux_res.max_sample_byte_size; +// build_m4a_sync_tab(&demux_res); + /************************** + * TEST END + *************************/ + if (file_offset > 0) { /* Resume the desired (byte) position. Important: When resuming SBR * upsampling files the resulting sound_samples_done must be expanded @@ -207,25 +215,13 @@ ci->seek_complete(); } - /* Lookup the length (in samples and bytes) of block i */ - if (!get_sample_info(&demux_res, i, &sample_duration, - &sample_byte_size)) { - LOGF("AAC: get_sample_info error\n"); - err = CODEC_ERROR; - goto done; - } - - /* There can be gaps between chunks, so skip ahead if needed. It - * doesn't seem to happen much, but it probably means that a - * "proper" file can have chunks out of order. Why one would want - * that an good question (but files with gaps do exist, so who - * knows?), so we don't support that - for now, at least. - */ - file_offset = get_sample_offset(&demux_res, i); + /* There can be gaps between chunks, so skip ahead if needed. */ + file_offset = check_sample_offset(&demux_res, i); - if (file_offset > ci->curpos) + int bytes_to_skip = file_offset - ci->curpos; + if (bytes_to_skip > 0) { - ci->advance_buffer(file_offset - ci->curpos); + ci->advance_buffer(bytes_to_skip); } else if (file_offset == 0) { @@ -235,7 +231,7 @@ } /* Request the required number of bytes from the input buffer */ - buffer=ci->request_buffer(&n,sample_byte_size); + buffer=ci->request_buffer(&n, max_frame_size); /* Decode one block - returned samples will be host-endian */ ret = NeAACDecDecode(decoder, &frame_info, buffer, n); @@ -248,34 +244,17 @@ } /* Advance codec buffer (no need to call set_offset because of this) */ - ci->advance_buffer(n); + ci->advance_buffer(frame_info.bytesconsumed); /* Output the audio */ ci->yield(); - /* Ensure correct sample_duration is used. For SBR upsampling files - * sample_duration is only half the size of real output frame size. */ - sample_duration *= sbr_fac; - + /* Gather number of samples for the decoded frame. */ framelength = (frame_info.samples >> 1) - lead_trim; if (i == demux_res.num_sample_byte_sizes - 1 && framelength > 0) { - /* Currently limited to at most one frame of tail_trim. - * Seems to be enough. - */ - if (ci->id3->tail_trim == 0 - && sample_duration < (frame_info.samples >> 1)) - { - /* Subtract lead_trim just in case we decode a file with - * only one audio frame with actual data. - */ - framelength = sample_duration - lead_trim; - } - else - { - framelength -= ci->id3->tail_trim; - } + framelength -= ci->id3->tail_trim; } if (framelength > 0) Index: apps/codecs/alac.c =================================================================== --- apps/codecs/alac.c (revision 29719) +++ apps/codecs/alac.c (working copy) @@ -113,12 +113,14 @@ } /* Lookup the length (in samples and bytes) of block i */ + /* AB if (!get_sample_info(&demux_res, i, &sample_duration, &sample_byte_size)) { LOGF("ALAC: Error in get_sample_info\n"); retval = CODEC_ERROR; goto done; } + AB */ /* Request the required number of bytes from the input buffer */ Index: apps/codecs/libm4a/m4a.c =================================================================== --- apps/codecs/libm4a/m4a.c (revision 29719) +++ apps/codecs/libm4a/m4a.c (working copy) @@ -122,8 +122,74 @@ stream->eof=0; } +/* TEST */ +int check_sample_offset(demux_res_t *demux_res, uint32_t frame) +{ + unsigned int i = 0; + for (i=0; inum_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) + break; + } + return demux_res->lookup_table[i].offset; +} + +static void gather_offset(demux_res_t *demux_res, uint32_t *frame, int *offset) +{ + uint32_t i = 0; + for (i=0; inum_lookup_table; ++i) + { + if (demux_res->lookup_table[i].offset == 0) + break; + if (demux_res->lookup_table[i].sample > *frame) + break; + } + 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; +} +#if 0 +void build_m4a_sync_tab(demux_res_t *demux_res) +{ + /* Extract look-up based on sample_to_chunk[] and chunk_offset[]. + * Pro: Does not need sample_byte_size[] at all. + * Faster build-up of table. + * Con: Larger look-up table (still ~1/10 of num_sample_byte_sizes[]) */ + uint32_t i = 0; + uint32_t k = 0; + int32_t frame = 0; + int32_t idx = 0; + printf("-- num_chunk_offsets = %d\n", demux_res->num_chunk_offsets); + memset(demux_res->lookup_table, 0, demux_res->num_lookup_table); + for (k=1; knum_chunk_offsets; ++k) + { + frame = 0; + for (i=1; inum_sample_to_chunks; ++i) + { + int old_first = demux_res->sample_to_chunk[i-1].first_chunk; + int new_first = demux_res->sample_to_chunk[i ].first_chunk; + + if (demux_res->sample_to_chunk[i].first_chunk > k) + break; + + frame += (new_first - old_first) * demux_res->sample_to_chunk[i-1].num_samples; + } + frame += (k - demux_res->sample_to_chunk[i-1].first_chunk)*demux_res->sample_to_chunk[i-1].num_samples; + + demux_res->lookup_table[idx].sample = frame; + demux_res->lookup_table[idx].offset = demux_res->chunk_offset[k-1]; + idx++; + } + printf("-- lookup_table[] has %d entries\n", idx); +} +#endif +/* TEST */ + /* This function was part of the original alac decoder implementation */ - +/* AB int get_sample_info(demux_res_t *demux_res, uint32_t samplenum, uint32_t *sample_duration, uint32_t *sample_byte_size) @@ -156,7 +222,132 @@ return 1; } +AB */ +#if 1 +unsigned int alac_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 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) + { + 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) + { + tmp_var = (sound_sample_loc - new_sound_sample); + new_sample += tmp_var / tmp_dur; + new_sound_sample += tmp_var; + break; + } + new_sample += tmp_cnt; + new_sound_sample += tmp_var; + ++i; + } + + /* We know the new sample (=frame), now calculate the file position. */ + gather_offset(demux_res, &new_sample, &new_pos); + + /* We know the new file position, so let's try to seek to it */ + printf("-- alac_seek to %d -> %d %d\n", sound_sample_loc, new_sample, new_pos); + if (stream->ci->seek_buffer(new_pos)) + { + *sound_samples_done = new_sound_sample; + *current_sample = new_sample; + return 1; + } + + return 0; +} + +/* Seek to the sample containing file_loc. Return 1 on success (and modify + * sound_samples_done and current_sample), 0 if failed. + * + * Seeking uses the following arrays: + * + * 1) the chunk_offset array contains the file offset of each chunk. + * + * 2) the sample_to_chunk array contains information about which chunk + * of samples each sample belongs to. + * + * 3) the sample_byte_size array contains the length in bytes of each + * sample. + * + * 4) the time_to_sample array contains the duration (in sound samples) + * of each sample of data. + * + * Locate the chunk containing location (using chunk_offset), find the + * sample of that chunk (using sample_to_chunk) and finally the location + * of that sample (using sample_byte_size). Then use time_to_sample to + * calculate the sound_samples_done value. + */ +unsigned int alac_seek_raw(demux_res_t* demux_res, stream_t* stream, + uint32_t file_loc, uint32_t* sound_samples_done, + int* current_sample) +{ + uint32_t i; + uint32_t chunk_sample = 0; + uint32_t total_samples = 0; + uint32_t new_sound_sample = 0; + uint32_t tmp_dur; + uint32_t tmp_cnt; + uint32_t new_pos; + + /* Gather sample index. */ + for (i=0; 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. */ + chunk_sample = demux_res->lookup_table[i].sample; + new_pos = demux_res->lookup_table[i].offset; + printf("-- alac_seek_raw to %d -> (%d) %d %d\n", file_loc, i, chunk_sample, new_pos); + + /* Get sound sample offset. */ + i = 0; + time_to_sample_t *tab2 = demux_res->time_to_sample; + while (i < demux_res->num_time_to_samples) + { + tmp_dur = tab2[i].sample_duration; + tmp_cnt = tab2[i].sample_count; + total_samples += tmp_cnt; + new_sound_sample += tmp_cnt * tmp_dur; + if (chunk_sample <= total_samples) + { + new_sound_sample += (chunk_sample - total_samples) * tmp_dur; + break; + } + ++i; + } + + /* Go to the new file position. */ + if (stream->ci->seek_buffer(new_pos)) + { + *sound_samples_done = new_sound_sample; + *current_sample = chunk_sample; + return 1; + } + + return 0; +} +#else unsigned int get_sample_offset(demux_res_t *demux_res, uint32_t sample) { uint32_t chunk = 1; @@ -412,3 +603,4 @@ return 0; } +#endif Index: apps/codecs/libm4a/demux.c =================================================================== --- apps/codecs/libm4a/demux.c (revision 29719) +++ apps/codecs/libm4a/demux.c (working copy) @@ -381,6 +381,7 @@ size_remaining -= 4; qtmovie->res->num_sample_byte_sizes = numentries; +/* AB qtmovie->res->sample_byte_size = malloc(numentries * sizeof(*qtmovie->res->sample_byte_size)); if (!qtmovie->res->sample_byte_size) @@ -388,11 +389,11 @@ DEBUGF("stsz too large\n"); return false; } - +*/ for (i = 0; i < numentries; i++) { uint32_t v = stream_read_uint32(qtmovie->stream); - +/* AB if (v > 0x0000ffff) { DEBUGF("stsz[%d] > 65 kB (%ld)\n", i, (long)v); @@ -400,12 +401,14 @@ } qtmovie->res->sample_byte_size[i] = v; +*/ + if (v > qtmovie->res->max_sample_byte_size) + qtmovie->res->max_sample_byte_size = v; size_remaining -= 4; } if (size_remaining) { - DEBUGF("ehm, size remianing?\n"); stream_skip(qtmovie->stream, size_remaining); } @@ -426,8 +429,7 @@ size_remaining -= 4; qtmovie->res->num_sample_to_chunks = numentries; - qtmovie->res->sample_to_chunk = malloc(numentries * - sizeof(*qtmovie->res->sample_to_chunk)); + qtmovie->res->sample_to_chunk = malloc(numentries * sizeof(*qtmovie->res->sample_to_chunk)); if (!qtmovie->res->sample_to_chunk) { @@ -456,7 +458,7 @@ static bool read_chunk_stco(qtmovie_t *qtmovie, size_t chunk_len) { - unsigned int i; + uint32_t i, k; uint32_t numentries; size_t size_remaining = chunk_len - 8; @@ -468,20 +470,58 @@ size_remaining -= 4; qtmovie->res->num_chunk_offsets = numentries; - qtmovie->res->chunk_offset = malloc(numentries * - sizeof(*qtmovie->res->chunk_offset)); - +// qtmovie->res->chunk_offset = malloc(numentries * sizeof(*qtmovie->res->chunk_offset)); +/* if (!qtmovie->res->chunk_offset) { - DEBUGF("stco too large\n"); + DEBUGF("stco too large to allocate chunk_offset[]\n"); return false; } +*/ + qtmovie->res->num_lookup_table = numentries; + qtmovie->res->lookup_table = malloc(numentries * sizeof(*qtmovie->res->lookup_table)); - for (i = 0; i < numentries; i++) + if (!qtmovie->res->lookup_table) { - qtmovie->res->chunk_offset[i] = stream_read_uint32(qtmovie->stream); + DEBUGF("stco too large to allocate lookup_table[]\n"); + return false; + } +/* + for (k = 0; k < numentries; k++) + { + qtmovie->res->chunk_offset[k] = stream_read_uint32(qtmovie->stream); size_remaining -= 4; } +*/ + /*****************/ + int frame; + int idx = 0; + int offset = stream_read_uint32(qtmovie->stream); + size_remaining -= 4; + + for (k=1; kres->num_sample_to_chunks; ++i) + { + int old_first = qtmovie->res->sample_to_chunk[i-1].first_chunk; + int new_first = qtmovie->res->sample_to_chunk[i ].first_chunk; + + if (qtmovie->res->sample_to_chunk[i].first_chunk > k) + break; + + frame += (new_first - old_first) * qtmovie->res->sample_to_chunk[i-1].num_samples; + } + frame += (k - qtmovie->res->sample_to_chunk[i-1].first_chunk)*qtmovie->res->sample_to_chunk[i-1].num_samples; + + qtmovie->res->lookup_table[idx].sample = frame; + qtmovie->res->lookup_table[idx].offset = offset; + idx++; + + offset = stream_read_uint32(qtmovie->stream); + size_remaining -= 4; + } + /*****************/ if (size_remaining) { Index: apps/codecs/libm4a/m4a.h =================================================================== --- apps/codecs/libm4a/m4a.h (revision 29719) +++ apps/codecs/libm4a/m4a.h (working copy) @@ -59,6 +59,12 @@ typedef struct { + uint32_t sample; + uint32_t offset; +} sample_offset_t; + +typedef struct +{ uint16_t num_channels; uint16_t sound_sample_size; uint32_t sound_sample_rate; @@ -68,14 +74,19 @@ sample_to_chunk_t *sample_to_chunk; uint32_t num_sample_to_chunks; - uint32_t *chunk_offset; + sample_offset_t *lookup_table; + uint32_t num_lookup_table; + +// uint32_t *chunk_offset; uint32_t num_chunk_offsets; time_to_sample_t *time_to_sample; uint32_t num_time_to_samples; - +/* AB uint16_t *sample_byte_size; +*/ uint32_t num_sample_byte_sizes; + uint32_t max_sample_byte_size; uint32_t codecdata_len; uint8_t codecdata[MAX_CODECDATA_SIZE]; @@ -132,5 +143,10 @@ int* current_sample); unsigned int alac_seek_raw (demux_res_t* demux_res, stream_t* stream, uint32_t file_loc, uint32_t* sound_samples_done, int* current_sample); + +/* TEST */ +int check_sample_offset(demux_res_t *demux_res, uint32_t frame); +void build_m4a_sync_tab(demux_res_t *demux_res); +/* TEST */ #endif /* STREAM_H */