From 9896fd1adef70e77b2e226d5e44fabd23192266d Mon Sep 17 00:00:00 2001 From: Magnus Holmgren Date: Wed, 11 Oct 2006 17:02:23 +0000 Subject: AAC codec: Improved MP4 file parsing. Should now handle most streamable files. Also some code cleanup and policing. git-svn-id: svn://svn.rockbox.org/rockbox/trunk@11187 a1c6a512-1295-4272-9138-f99709370657 --- apps/codecs/aac.c | 129 ++++++++------- apps/codecs/libm4a/demux.c | 155 +++++++++++------- apps/codecs/libm4a/m4a.c | 380 ++++++++++++++++++++++++++++++--------------- apps/codecs/libm4a/m4a.h | 32 ++-- 4 files changed, 448 insertions(+), 248 deletions(-) (limited to 'apps') diff --git a/apps/codecs/aac.c b/apps/codecs/aac.c index 6c86f38372..0c48422a53 100644 --- a/apps/codecs/aac.c +++ b/apps/codecs/aac.c @@ -29,6 +29,8 @@ CODEC_HEADER extern char iramcopy[]; extern char iramstart[]; extern char iramend[]; +extern char iedata[]; +extern char iend[]; #endif struct codec_api* rb; @@ -37,27 +39,35 @@ struct codec_api* ci; /* this is the codec entry point */ enum codec_status codec_start(struct codec_api* api) { + /* Note that when dealing with QuickTime/MPEG4 files, terminology is + * a bit confusing. Files with sound are split up in chunks, where + * each chunk contains one or more samples. Each sample in turn + * contains a number of "sound samples" (the kind you refer to with + * the sampling frequency). + */ size_t n; static demux_res_t demux_res; stream_t input_stream; - uint32_t samplesdone; - uint32_t elapsedtime; + uint32_t sound_samples_done; + uint32_t elapsed_time; uint32_t sample_duration; uint32_t sample_byte_size; - int samplesdecoded; + int file_offset; unsigned int i; unsigned char* buffer; - static NeAACDecFrameInfo frameInfo; - NeAACDecHandle hDecoder; + static NeAACDecFrameInfo frame_info; + NeAACDecHandle decoder; int err; - int16_t* decodedbuffer; + uint32_t s = 0; + unsigned char c = 0; /* Generic codec initialisation */ rb = api; ci = api; #ifndef SIMULATOR - rb->memcpy(iramstart, iramcopy, iramend-iramstart); + ci->memcpy(iramstart, iramcopy, iramend-iramstart); + ci->memset(iedata, 0, iend - iedata); #endif ci->configure(CODEC_SET_FILEBUF_CHUNKSIZE, (int *)(1024*16)); @@ -68,9 +78,10 @@ enum codec_status codec_start(struct codec_api* api) ci->configure(DSP_SET_SAMPLE_DEPTH, (int *)(29)); next_track: + err = CODEC_OK; if (codec_init(api)) { - LOGF("FAAD: Error initialising codec\n"); + LOGF("FAAD: Codec init error\n"); err = CODEC_ERROR; goto exit; } @@ -78,7 +89,7 @@ next_track: while (!*ci->taginfo_ready && !ci->stop_codec) ci->sleep(1); - samplesdone = ci->id3->offset; + sound_samples_done = ci->id3->offset; ci->configure(DSP_SET_FREQUENCY, (long *)(rb->id3->frequency)); @@ -87,52 +98,49 @@ next_track: /* 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)) { - LOGF("FAAD: Error initialising file\n"); + LOGF("FAAD: File init error\n"); err = CODEC_ERROR; goto done; } /* initialise the sound converter */ - hDecoder = NULL; - hDecoder = NeAACDecOpen(); + decoder = NeAACDecOpen(); - if (!hDecoder) { - LOGF("FAAD: Error opening decoder\n"); + if (!decoder) { + LOGF("FAAD: Decode open error\n"); err = CODEC_ERROR; goto done; } - NeAACDecConfigurationPtr conf = NeAACDecGetCurrentConfiguration(hDecoder); + NeAACDecConfigurationPtr conf = NeAACDecGetCurrentConfiguration(decoder); conf->outputFormat = FAAD_FMT_24BIT; /* irrelevant, we don't convert */ - NeAACDecSetConfiguration(hDecoder, conf); - - uint32_t s=0; - unsigned char c=0; + NeAACDecSetConfiguration(decoder, conf); - err = NeAACDecInit2(hDecoder, demux_res.codecdata,demux_res.codecdata_len, &s, &c); + err = NeAACDecInit2(decoder, demux_res.codecdata, demux_res.codecdata_len, &s, &c); if (err) { - LOGF("FAAD: Error initialising decoder: %d, type=%d\n", err,hDecoder->object_type); + LOGF("FAAD: DecInit: %d, %d\n", err, decoder->object_type); err = CODEC_ERROR; goto done; } - ci->id3->frequency=s; + ci->id3->frequency = s; - i=0; + i = 0; - if (samplesdone > 0) { - if (alac_seek_raw(&demux_res, &input_stream, samplesdone, - &samplesdone, (int *)&i)) { - elapsedtime=(samplesdone*10)/(ci->id3->frequency/100); - ci->set_elapsed(elapsedtime); + if (sound_samples_done > 0) { + if (alac_seek_raw(&demux_res, &input_stream, sound_samples_done, + &sound_samples_done, (int*) &i)) { + elapsed_time = (sound_samples_done * 10) / (ci->id3->frequency / 100); + ci->set_elapsed(elapsed_time); } else { - samplesdone=0; + sound_samples_done = 0; } } /* The main decoding loop */ while (i < demux_res.num_sample_byte_sizes) { rb->yield(); + if (ci->stop_codec || ci->new_track) { break; } @@ -140,10 +148,10 @@ next_track: /* Deal with any pending seek requests */ if (ci->seek_time) { if (alac_seek(&demux_res, &input_stream, - ((ci->seek_time-1)/10) * (ci->id3->frequency/100), - &samplesdone, (int *)&i)) { - elapsedtime=(samplesdone*10)/(ci->id3->frequency/100); - ci->set_elapsed(elapsedtime); + ((ci->seek_time-1)/10)*(ci->id3->frequency/100), + &sound_samples_done, (int*) &i)) { + elapsed_time = (sound_samples_done * 10) / (ci->id3->frequency / 100); + ci->set_elapsed(elapsed_time); } ci->seek_complete(); } @@ -151,52 +159,65 @@ next_track: /* Lookup the length (in samples and bytes) of block i */ if (!get_sample_info(&demux_res, i, &sample_duration, &sample_byte_size)) { - LOGF("AAC: Error in get_sample_info\n"); + 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); + + if (file_offset > ci->curpos) + { + ci->advance_buffer(file_offset - ci->curpos); + } + /* Request the required number of bytes from the input buffer */ buffer=ci->request_buffer(&n,sample_byte_size); /* Decode one block - returned samples will be host-endian */ - rb->yield(); - decodedbuffer = NeAACDecDecode(hDecoder, &frameInfo, buffer, n); - /* ignore decodedbuffer return value, we access samples in the - decoder struct directly */ - if (frameInfo.error > 0) { - LOGF("FAAD: decoding error \"%s\"\n", NeAACDecGetErrorMessage(frameInfo.error)); - err = CODEC_ERROR; - goto done; + NeAACDecDecode(decoder, &frame_info, buffer, n); + /* Ignore return value, we access samples in the decoder struct + * directly. + */ + if (frame_info.error > 0) { + LOGF("FAAD: decode error '%s'\n", NeAACDecGetErrorMessage(frame_info.error)); + err = CODEC_ERROR; + goto done; } - /* Get the number of decoded samples */ - samplesdecoded=frameInfo.samples; - /* Advance codec buffer */ ci->advance_buffer(n); /* Output the audio */ rb->yield(); - while (!rb->pcmbuf_insert_split(hDecoder->time_out[0], - hDecoder->time_out[1], - frameInfo.samples*2)) - rb->yield(); + while (!rb->pcmbuf_insert_split(decoder->time_out[0], + decoder->time_out[1], + frame_info.samples * 2)) + { + rb->sleep(1); + } /* Update the elapsed-time indicator */ - samplesdone+=sample_duration; - elapsedtime=(samplesdone*10)/(ci->id3->frequency/100); - ci->set_elapsed(elapsedtime); + sound_samples_done += sample_duration; + elapsed_time = (sound_samples_done * 10) / (ci->id3->frequency / 100); + ci->set_elapsed(elapsed_time); /* Keep track of current position - for resuming */ - ci->set_offset(elapsedtime); + ci->set_offset(elapsed_time); i++; } + err = CODEC_OK; done: - LOGF("AAC: Decoded %d samples\n",samplesdone); + LOGF("AAC: Decoded %d samples, %d frames\n", sound_samples_done); if (ci->request_next_track()) goto next_track; diff --git a/apps/codecs/libm4a/demux.c b/apps/codecs/libm4a/demux.c index 1beeced8e6..44261fdef6 100644 --- a/apps/codecs/libm4a/demux.c +++ b/apps/codecs/libm4a/demux.c @@ -55,12 +55,14 @@ static void read_chunk_ftyp(qtmovie_t *qtmovie, size_t chunk_len) { fourcc_t type; uint32_t minor_ver; - size_t size_remaining = chunk_len - 8; /* FIXME: can't hardcode 8, size may be 64bit */ + size_t size_remaining = chunk_len - 8; type = stream_read_uint32(qtmovie->stream); size_remaining-=4; if ((type != MAKEFOURCC('M','4','A',' ')) && - (type != MAKEFOURCC('m','p','4','2'))) + (type != MAKEFOURCC('m','p','4','2')) && + (type != MAKEFOURCC('3','g','p','6')) && + (type != MAKEFOURCC('q','t',' ',' '))) { DEBUGF("not M4A file\n"); return; @@ -80,7 +82,7 @@ static void read_chunk_ftyp(qtmovie_t *qtmovie, size_t chunk_len) static void read_chunk_tkhd(qtmovie_t *qtmovie, size_t chunk_len) { /* don't need anything from here atm, skip */ - size_t size_remaining = chunk_len - 8; /* FIXME WRONG */ + size_t size_remaining = chunk_len - 8; stream_skip(qtmovie->stream, size_remaining); } @@ -88,7 +90,7 @@ static void read_chunk_tkhd(qtmovie_t *qtmovie, size_t chunk_len) static void read_chunk_mdhd(qtmovie_t *qtmovie, size_t chunk_len) { /* don't need anything from here atm, skip */ - size_t size_remaining = chunk_len - 8; /* FIXME WRONG */ + size_t size_remaining = chunk_len - 8; stream_skip(qtmovie->stream, size_remaining); } @@ -97,7 +99,7 @@ static void read_chunk_mdhd(qtmovie_t *qtmovie, size_t chunk_len) static void read_chunk_hdlr(qtmovie_t *qtmovie, size_t chunk_len) { fourcc_t comptype, compsubtype; - size_t size_remaining = chunk_len - 8; /* FIXME WRONG */ + size_t size_remaining = chunk_len - 8; int strlen; char str[256] = {0}; @@ -196,7 +198,6 @@ static bool read_chunk_esds(qtmovie_t *qtmovie, size_t chunk_len) temp=stream_read_int32(qtmovie->stream);//0x15000414 ???? maxBitrate = stream_read_int32(qtmovie->stream); avgBitrate = stream_read_int32(qtmovie->stream); - DEBUGF("audioType=%d, maxBitrate=%d, avgBitrate=%d\n",audioType,maxBitrate,avgBitrate); /* get and verify DecSpecificInfoTag */ @@ -224,7 +225,7 @@ static bool read_chunk_stsd(qtmovie_t *qtmovie, size_t chunk_len) unsigned int i; int j; uint32_t numentries; - size_t size_remaining = chunk_len - 8; /* FIXME WRONG */ + size_t size_remaining = chunk_len - 8; /* version */ stream_read_uint8(qtmovie->stream); @@ -247,7 +248,6 @@ static bool read_chunk_stsd(qtmovie_t *qtmovie, size_t chunk_len) for (i = 0; i < numentries; i++) { uint32_t entry_size; - uint16_t version; uint32_t entry_remaining; @@ -259,43 +259,20 @@ static bool read_chunk_stsd(qtmovie_t *qtmovie, size_t chunk_len) /* sound info: */ - stream_skip(qtmovie->stream, 6); /* reserved */ - entry_remaining -= 6; - - version = stream_read_uint16(qtmovie->stream); - // if (version != 1) - //fprintf(stderr, "unknown version??\n"); - entry_remaining -= 2; - - /* revision level */ - stream_read_uint16(qtmovie->stream); - /* vendor */ - stream_read_uint32(qtmovie->stream); - entry_remaining -= 6; - - /* EH?? spec doesn't say theres an extra 16 bits here.. but there is! */ - stream_read_uint16(qtmovie->stream); - entry_remaining -= 2; + /* reserved + data reference index + sound version + reserved */ + stream_skip(qtmovie->stream, 6 + 2 + 2 + 6); + entry_remaining -= 6 + 2 + 2 + 6; qtmovie->res->num_channels = stream_read_uint16(qtmovie->stream); - - qtmovie->res->sample_size = stream_read_uint16(qtmovie->stream); + qtmovie->res->sound_sample_size = stream_read_uint16(qtmovie->stream); entry_remaining -= 4; - /* compression id */ - stream_read_uint16(qtmovie->stream); /* packet size */ - stream_read_uint16(qtmovie->stream); - entry_remaining -= 4; - - /* sample rate - 32bit fixed point = 16bit?? */ - qtmovie->res->sample_rate = stream_read_uint16(qtmovie->stream); - entry_remaining -= 2; - - /* skip 2 */ stream_skip(qtmovie->stream, 2); - entry_remaining -= 2; - + qtmovie->res->sound_sample_rate = stream_read_uint32(qtmovie->stream); + /* reserved size */ + stream_skip(qtmovie->stream, 2); + entry_remaining -= 8; /* remaining is codec data */ @@ -372,7 +349,7 @@ static void read_chunk_stts(qtmovie_t *qtmovie, size_t chunk_len) { unsigned int i; uint32_t numentries; - size_t size_remaining = chunk_len - 8; /* FIXME WRONG */ + size_t size_remaining = chunk_len - 8; /* version */ stream_read_uint8(qtmovie->stream); @@ -407,7 +384,7 @@ static void read_chunk_stsz(qtmovie_t *qtmovie, size_t chunk_len) { unsigned int i; uint32_t numentries; - size_t size_remaining = chunk_len - 8; /* FIXME WRONG */ + size_t size_remaining = chunk_len - 8; /* version */ stream_read_uint8(qtmovie->stream); @@ -447,9 +424,73 @@ static void read_chunk_stsz(qtmovie_t *qtmovie, size_t chunk_len) } } +static void read_chunk_stsc(qtmovie_t *qtmovie, size_t chunk_len) +{ + unsigned int i; + uint32_t numentries; + size_t size_remaining = chunk_len - 8; + + /* version + flags */ + stream_read_uint32(qtmovie->stream); + size_remaining -= 4; + + numentries = stream_read_uint32(qtmovie->stream); + size_remaining -= 4; + + qtmovie->res->num_sample_to_chunks = numentries; + qtmovie->res->sample_to_chunk = malloc(numentries * + sizeof(*qtmovie->res->sample_to_chunk)); + + for (i = 0; i < numentries; i++) + { + qtmovie->res->sample_to_chunk[i].first_chunk = + stream_read_uint32(qtmovie->stream); + qtmovie->res->sample_to_chunk[i].num_samples = + stream_read_uint32(qtmovie->stream); + stream_read_uint32(qtmovie->stream); + size_remaining -= 12; + } + + if (size_remaining) + { + DEBUGF("ehm, size remianing?\n"); + stream_skip(qtmovie->stream, size_remaining); + } +} + +static void read_chunk_stco(qtmovie_t *qtmovie, size_t chunk_len) +{ + unsigned int i; + uint32_t numentries; + size_t size_remaining = chunk_len - 8; + + /* version + flags */ + stream_read_uint32(qtmovie->stream); + size_remaining -= 4; + + numentries = stream_read_uint32(qtmovie->stream); + size_remaining -= 4; + + qtmovie->res->num_chunk_offsets = numentries; + qtmovie->res->chunk_offset = malloc(numentries * + sizeof(*qtmovie->res->chunk_offset)); + + for (i = 0; i < numentries; i++) + { + qtmovie->res->chunk_offset[i] = stream_read_uint32(qtmovie->stream); + size_remaining -= 4; + } + + if (size_remaining) + { + DEBUGF("ehm, size remianing?\n"); + stream_skip(qtmovie->stream, size_remaining); + } +} + static bool read_chunk_stbl(qtmovie_t *qtmovie, size_t chunk_len) { - size_t size_remaining = chunk_len - 8; /* FIXME WRONG */ + size_t size_remaining = chunk_len - 8; while (size_remaining) { @@ -479,14 +520,15 @@ static bool read_chunk_stbl(qtmovie_t *qtmovie, size_t chunk_len) read_chunk_stsz(qtmovie, sub_chunk_len); break; case MAKEFOURCC('s','t','s','c'): + read_chunk_stsc(qtmovie, sub_chunk_len); + break; case MAKEFOURCC('s','t','c','o'): - /* skip these, no indexing for us! */ - stream_skip(qtmovie->stream, sub_chunk_len - 8); + read_chunk_stco(qtmovie, sub_chunk_len); break; default: DEBUGF("(stbl) unknown chunk id: %c%c%c%c\n", SPLITFOURCC(sub_chunk_id)); - stream_skip(qtmovie->stream, sub_chunk_len - 8); /* FIXME not 8 */ + stream_skip(qtmovie->stream, sub_chunk_len - 8); } size_remaining -= sub_chunk_len; @@ -497,7 +539,7 @@ static bool read_chunk_stbl(qtmovie_t *qtmovie, size_t chunk_len) static bool read_chunk_minf(qtmovie_t *qtmovie, size_t chunk_len) { size_t dinf_size, stbl_size; - size_t size_remaining = chunk_len - 8; /* FIXME WRONG */ + size_t size_remaining = chunk_len - 8; uint32_t i; /**** SOUND HEADER CHUNK ****/ @@ -553,7 +595,7 @@ static bool read_chunk_minf(qtmovie_t *qtmovie, size_t chunk_len) static bool read_chunk_mdia(qtmovie_t *qtmovie, size_t chunk_len) { - size_t size_remaining = chunk_len - 8; /* FIXME WRONG */ + size_t size_remaining = chunk_len - 8; while (size_remaining) { @@ -597,7 +639,7 @@ static bool read_chunk_mdia(qtmovie_t *qtmovie, size_t chunk_len) /* 'trak' - a movie track - contains other atoms */ static bool read_chunk_trak(qtmovie_t *qtmovie, size_t chunk_len) { - size_t size_remaining = chunk_len - 8; /* FIXME WRONG */ + size_t size_remaining = chunk_len - 8; while (size_remaining) { @@ -639,7 +681,7 @@ static bool read_chunk_trak(qtmovie_t *qtmovie, size_t chunk_len) static void read_chunk_mvhd(qtmovie_t *qtmovie, size_t chunk_len) { /* don't need anything from here atm, skip */ - size_t size_remaining = chunk_len - 8; /* FIXME WRONG */ + size_t size_remaining = chunk_len - 8; stream_skip(qtmovie->stream, size_remaining); } @@ -648,7 +690,7 @@ static void read_chunk_mvhd(qtmovie_t *qtmovie, size_t chunk_len) static void read_chunk_udta(qtmovie_t *qtmovie, size_t chunk_len) { /* don't need anything from here atm, skip */ - size_t size_remaining = chunk_len - 8; /* FIXME WRONG */ + size_t size_remaining = chunk_len - 8; stream_skip(qtmovie->stream, size_remaining); } @@ -656,7 +698,7 @@ static void read_chunk_udta(qtmovie_t *qtmovie, size_t chunk_len) /* 'moov' movie atom - contains other atoms */ static bool read_chunk_moov(qtmovie_t *qtmovie, size_t chunk_len) { - size_t size_remaining = chunk_len - 8; /* FIXME WRONG */ + size_t size_remaining = chunk_len - 8; while (size_remaining) { @@ -688,7 +730,7 @@ static bool read_chunk_moov(qtmovie_t *qtmovie, size_t chunk_len) default: DEBUGF("(moov) unknown chunk id: %c%c%c%c\n", SPLITFOURCC(sub_chunk_id)); - stream_skip(qtmovie->stream, sub_chunk_len - 8); /* FIXME not 8 */ + stream_skip(qtmovie->stream, sub_chunk_len - 8); } size_remaining -= sub_chunk_len; @@ -698,14 +740,9 @@ static bool read_chunk_moov(qtmovie_t *qtmovie, size_t chunk_len) static void read_chunk_mdat(qtmovie_t *qtmovie, size_t chunk_len) { - size_t size_remaining = chunk_len - 8; /* FIXME WRONG */ + size_t size_remaining = chunk_len - 8; qtmovie->res->mdat_len = size_remaining; -#if 0 - qtmovie->res->mdat = malloc(size_remaining); - - stream_read(qtmovie->stream, size_remaining, qtmovie->res->mdat); -#endif } int qtmovie_read(stream_t *file, demux_res_t *demux_res) @@ -760,7 +797,7 @@ int qtmovie_read(stream_t *file, demux_res_t *demux_res) /* these following atoms can be skipped !!!! */ case MAKEFOURCC('f','r','e','e'): - stream_skip(qtmovie.stream, chunk_len - 8); /* FIXME not 8 */ + stream_skip(qtmovie.stream, chunk_len - 8); break; default: //DEBUGF("(top) unknown chunk id: %c%c%c%c\n",SPLITFOURCC(chunk_id)); diff --git a/apps/codecs/libm4a/m4a.c b/apps/codecs/libm4a/m4a.c index f914f4e4d1..0a87ec35ea 100644 --- a/apps/codecs/libm4a/m4a.c +++ b/apps/codecs/libm4a/m4a.c @@ -21,6 +21,13 @@ #include #include "m4a.h" +#if defined(DEBUG) || defined(SIMULATOR) +extern struct codec_api* rb; +#define DEBUGF rb->debugf +#else +#define DEBUGF(...) +#endif + /* Implementation of the stream.h functions used by libalac */ #define _Swap32(v) do { \ @@ -101,15 +108,7 @@ uint8_t stream_read_uint8(stream_t *stream) void stream_skip(stream_t *stream, size_t skip) { - (void)stream; -#if 1 - char buf; - while (skip > 0) { - stream->ci->read_filebuf(&buf,1); - skip--; - } -#endif - //stream->ci->advance_buffer(skip); + stream->ci->advance_buffer(skip); } int stream_eof(stream_t *stream) @@ -158,136 +157,273 @@ int get_sample_info(demux_res_t *demux_res, uint32_t samplenum, return 1; } -/* Seek to sample_loc (or close to it). Return 1 on success (and - modify samplesdone and currentblock), 0 if failed +unsigned int get_sample_offset(demux_res_t *demux_res, uint32_t sample) +{ + uint32_t chunk = 1; + uint32_t range_samples = 0; + uint32_t total_samples = 0; + uint32_t chunk_sample; + uint32_t prev_chunk; + uint32_t prev_chunk_samples; + uint32_t file_offset; + uint32_t i; + + /* First check we have the appropriate metadata - we should always + * have it. + */ + + if (sample >= demux_res->num_sample_byte_sizes || + !demux_res->num_sample_to_chunks || + !demux_res->num_chunk_offsets) + { + return 0; + } - Seeking uses the following two arrays: + /* Locate the chunk containing the sample */ + + prev_chunk = demux_res->sample_to_chunk[0].first_chunk; + prev_chunk_samples = demux_res->sample_to_chunk[0].num_samples; - 1) the sample_byte_size array contains the length in bytes of - each block ("sample" in Applespeak). + for (i = 1; i < demux_res->num_sample_to_chunks; i++) + { + chunk = demux_res->sample_to_chunk[i].first_chunk; + range_samples = (chunk - prev_chunk) * prev_chunk_samples; - 2) the time_to_sample array contains the duration (in samples) of - each block of data. + if (sample < total_samples + range_samples) + { + break; + } - So we just find the block number we are going to seek to (using - time_to_sample) and then find the offset in the file (using - sample_byte_size). + total_samples += range_samples; + prev_chunk = demux_res->sample_to_chunk[i].first_chunk; + prev_chunk_samples = demux_res->sample_to_chunk[i].num_samples; + } - Each ALAC block seems to be independent of all the others. - */ + if (demux_res->num_sample_to_chunks > 1) + { + chunk = prev_chunk + (sample - total_samples) / prev_chunk_samples; + } + else + { + chunk = 1; + } + + /* Get sample of the first sample in the chunk */ + + chunk_sample = total_samples + (chunk - prev_chunk) * prev_chunk_samples; + + /* Get offset in file */ + + if (chunk > demux_res->num_chunk_offsets) + { + file_offset = demux_res->chunk_offset[demux_res->num_chunk_offsets - 1]; + } + else + { + file_offset = demux_res->chunk_offset[chunk - 1]; + } + + if (chunk_sample > sample) { + return 0; + } + + for (i = chunk_sample; i < sample; i++) + { + file_offset += demux_res->sample_byte_size[i]; + } + + if (file_offset > demux_res->mdat_offset + demux_res->mdat_len) + { + return 0; + } + + return file_offset; +} -unsigned int alac_seek (demux_res_t* demux_res, - stream_t* stream, - unsigned int sample_loc, - uint32_t* samplesdone, int* currentblock) +/* Seek to the sample containing sound_sample_loc. Return 1 on success + * (and modify sound_samples_done and current_sample), 0 if failed. + * + * Seeking uses the following arrays: + * + * 1) the time_to_sample array contains the duration (in sound samples) + * of each sample of data. + * + * 2) the sample_byte_size array contains the length in bytes of each + * sample. + * + * 3) the sample_to_chunk array contains information about which chunk + * of samples each sample belongs to. + * + * 4) the chunk_offset array contains the file offset of each chunk. + * + * So find the sample number we are going to seek to (using time_to_sample) + * and then find the offset in the file (using sample_to_chunk, + * chunk_offset sample_byte_size, in that order.). + * + */ +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) { - int flag; - unsigned int i,j; - unsigned int newblock; - unsigned int newsample; - unsigned int newpos; - - /* First check we have the appropriate metadata - we should always - have it. */ - if ((demux_res->num_time_to_samples==0) || - (demux_res->num_sample_byte_sizes==0)) { return 0; } - - /* Find the destination block from time_to_sample array */ - i=0; - newblock=0; - newsample=0; - flag=0; - - while ((inum_time_to_samples) && (flag==0) && - (newsample < sample_loc)) { - j=(sample_loc-newsample) / - demux_res->time_to_sample[i].sample_duration; + uint32_t i; + uint32_t j; + uint32_t new_sample; + uint32_t new_sound_sample; + uint32_t new_pos; + + /* First check we have the appropriate metadata - we should always + * have it. + */ + + if ((demux_res->num_time_to_samples==0) || + (demux_res->num_sample_byte_sizes==0)) + { + return 0; + } + + /* Find the destination block from time_to_sample array */ - if (j <= demux_res->time_to_sample[i].sample_count) { - newblock+=j; - newsample+=j*demux_res->time_to_sample[i].sample_duration; - flag=1; - } else { - newsample+=(demux_res->time_to_sample[i].sample_duration - * demux_res->time_to_sample[i].sample_count); - newblock+=demux_res->time_to_sample[i].sample_count; - i++; + i = 0; + new_sample = 0; + new_sound_sample = 0; + + while ((i < demux_res->num_time_to_samples) && + (new_sound_sample < sound_sample_loc)) + { + j = (sound_sample_loc - new_sound_sample) / + demux_res->time_to_sample[i].sample_duration; + + if (j <= demux_res->time_to_sample[i].sample_count) + { + new_sample += j; + new_sound_sample += j * + demux_res->time_to_sample[i].sample_duration; + break; + } + else + { + new_sound_sample += (demux_res->time_to_sample[i].sample_duration + * demux_res->time_to_sample[i].sample_count); + new_sample += demux_res->time_to_sample[i].sample_count; + i++; + } } - } - - /* We know the new block, now calculate the file position */ - newpos=demux_res->mdat_offset; - for (i=0;isample_byte_size[i]; - } - - /* We know the new file position, so let's try to seek to it */ - if (stream->ci->seek_buffer(newpos)) { - *samplesdone=newsample; - *currentblock=newblock; - return 1; - } else { + + /* We know the new block, now calculate the file position. */ + + new_pos = get_sample_offset(demux_res, new_sample); + + /* We know the new file position, so let's try to seek to it */ + + if (stream->ci->seek_buffer(new_pos)) + { + *sound_samples_done = new_sound_sample; + *current_sample = new_sample; + return 1; + } + return 0; - } } -/* Seek to file_loc (or close to it). Return 1 on success (and - modify samplesdone and currentblock), 0 if failed - - Seeking uses the following array: +/* 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 chunk_sample = 0; + uint32_t total_samples = 0; + uint32_t new_sound_sample = 0; + uint32_t new_pos; + uint32_t chunk; + uint32_t i; + + if (!demux_res->num_chunk_offsets || + !demux_res->num_sample_to_chunks) + { + return 0; + } - the sample_byte_size array contains the length in bytes of - each block ("sample" in Applespeak). + /* Locate the chunk containing file_loc. */ - So we just find the last block before (or at) the requested position. + for (i = 0; i < demux_res->num_chunk_offsets && + file_loc < demux_res->chunk_offset[i]; i++) + { + } + + chunk = i + 1; + new_pos = demux_res->chunk_offset[chunk - 1]; - Each ALAC block seems to be independent of all the others. - */ + /* Get the first sample of the chunk. */ + + for (i = 1; i < demux_res->num_sample_to_chunks && + chunk < demux_res->sample_to_chunk[i - 1].first_chunk; i++) + { + chunk_sample += demux_res->sample_to_chunk[i - 1].num_samples * + (demux_res->sample_to_chunk[i].first_chunk - + demux_res->sample_to_chunk[i - 1].first_chunk); + } + + chunk_sample += (chunk - demux_res->sample_to_chunk[i - 1].first_chunk) * + demux_res->sample_to_chunk[i - 1].num_samples; -unsigned int alac_seek_raw (demux_res_t* demux_res, - stream_t* stream, - unsigned int file_loc, - uint32_t* samplesdone, int* currentblock) -{ - unsigned int i; - unsigned int j; - unsigned int newblock; - unsigned int newsample; - unsigned int newpos; - - /* First check we have the appropriate metadata - we should always - have it. */ - if ((demux_res->num_time_to_samples==0) || - (demux_res->num_sample_byte_sizes==0)) { return 0; } - - /* Find the destination block from the sample_byte_size array. */ - newpos=demux_res->mdat_offset; - for (i=0;(inum_sample_byte_sizes) && - (newpos+demux_res->sample_byte_size[i]<=file_loc);i++) { - newpos+=demux_res->sample_byte_size[i]; - } - - newblock=i; - newsample=0; - - /* Get the sample offset of the block */ - for (i=0,j=0;(inum_time_to_samples) && (jtime_to_sample[i].sample_count) { - if (newblock-j < demux_res->time_to_sample[i].sample_count) { - newsample+=(newblock-j)*demux_res->time_to_sample[i].sample_duration; - break; - } else { - newsample+=(demux_res->time_to_sample[i].sample_duration - * demux_res->time_to_sample[i].sample_count); + /* Get the position within the chunk. */ + + for (; chunk_sample < demux_res->num_sample_byte_sizes; chunk_sample++) + { + if (file_loc < new_pos + demux_res->sample_byte_size[chunk_sample]) + { + break; + } + + new_pos += demux_res->sample_byte_size[chunk_sample]; + } + + /* Get sound sample offset. */ + + for (i = 0; i < demux_res->num_time_to_samples; i++) + { + if (chunk_sample < + total_samples + demux_res->time_to_sample[i].sample_count) + { + break; + } + + total_samples += demux_res->time_to_sample[i].sample_count; + new_sound_sample += demux_res->time_to_sample[i].sample_count + * demux_res->time_to_sample[i].sample_duration; } - } + + new_sound_sample += (chunk_sample - total_samples) + * demux_res->time_to_sample[i].sample_duration; + + /* 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; + } - /* We know the new file position, so let's try to seek to it */ - if (stream->ci->seek_buffer(newpos)) { - *samplesdone=newsample; - *currentblock=newblock; - return 1; - } else { return 0; - } } diff --git a/apps/codecs/libm4a/m4a.h b/apps/codecs/libm4a/m4a.h index 7fea37513d..17f54c0146 100644 --- a/apps/codecs/libm4a/m4a.h +++ b/apps/codecs/libm4a/m4a.h @@ -33,11 +33,20 @@ typedef uint32_t fourcc_t; typedef struct { uint16_t num_channels; - uint16_t sample_size; - uint32_t sample_rate; + uint16_t sound_sample_size; + uint32_t sound_sample_rate; fourcc_t format; void *buf; + struct { + uint32_t first_chunk; + uint32_t num_samples; + } *sample_to_chunk; + uint32_t num_sample_to_chunks; + + uint32_t *chunk_offset; + uint32_t num_chunk_offsets; + struct { uint32_t sample_count; uint32_t sample_duration; @@ -93,16 +102,13 @@ void stream_skip(stream_t *stream, size_t skip); int stream_eof(stream_t *stream); void stream_create(stream_t *stream,struct codec_api* ci); -int get_sample_info(demux_res_t *demux_res, uint32_t samplenum, - uint32_t *sample_duration, - uint32_t *sample_byte_size); -unsigned int alac_seek (demux_res_t* demux_res, - stream_t* stream, - unsigned int sample_loc, - uint32_t* samplesdone, int* currentblock); -unsigned int alac_seek_raw (demux_res_t* demux_res, - stream_t* stream, - unsigned int file_loc, - uint32_t* samplesdone, int* currentblock); +int get_sample_info(demux_res_t *demux_res, uint32_t sample, + uint32_t *sample_duration, uint32_t *sample_byte_size); +unsigned int get_sample_offset(demux_res_t *demux_res, uint32_t sample); +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); +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); #endif /* STREAM_H */ -- cgit v1.2.3