1
0
Fork 0
forked from len0rd/rockbox

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
This commit is contained in:
Magnus Holmgren 2006-10-11 17:02:23 +00:00
parent ca3d872699
commit 9896fd1ade
4 changed files with 454 additions and 254 deletions

View file

@ -29,6 +29,8 @@ CODEC_HEADER
extern char iramcopy[]; extern char iramcopy[];
extern char iramstart[]; extern char iramstart[];
extern char iramend[]; extern char iramend[];
extern char iedata[];
extern char iend[];
#endif #endif
struct codec_api* rb; struct codec_api* rb;
@ -37,27 +39,35 @@ struct codec_api* ci;
/* this is the codec entry point */ /* this is the codec entry point */
enum codec_status codec_start(struct codec_api* api) 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; size_t n;
static demux_res_t demux_res; static demux_res_t demux_res;
stream_t input_stream; stream_t input_stream;
uint32_t samplesdone; uint32_t sound_samples_done;
uint32_t elapsedtime; uint32_t elapsed_time;
uint32_t sample_duration; uint32_t sample_duration;
uint32_t sample_byte_size; uint32_t sample_byte_size;
int samplesdecoded; int file_offset;
unsigned int i; unsigned int i;
unsigned char* buffer; unsigned char* buffer;
static NeAACDecFrameInfo frameInfo; static NeAACDecFrameInfo frame_info;
NeAACDecHandle hDecoder; NeAACDecHandle decoder;
int err; int err;
int16_t* decodedbuffer; uint32_t s = 0;
unsigned char c = 0;
/* Generic codec initialisation */ /* Generic codec initialisation */
rb = api; rb = api;
ci = api; ci = api;
#ifndef SIMULATOR #ifndef SIMULATOR
rb->memcpy(iramstart, iramcopy, iramend-iramstart); ci->memcpy(iramstart, iramcopy, iramend-iramstart);
ci->memset(iedata, 0, iend - iedata);
#endif #endif
ci->configure(CODEC_SET_FILEBUF_CHUNKSIZE, (int *)(1024*16)); 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)); ci->configure(DSP_SET_SAMPLE_DEPTH, (int *)(29));
next_track: next_track:
err = CODEC_OK;
if (codec_init(api)) { if (codec_init(api)) {
LOGF("FAAD: Error initialising codec\n"); LOGF("FAAD: Codec init error\n");
err = CODEC_ERROR; err = CODEC_ERROR;
goto exit; goto exit;
} }
@ -78,7 +89,7 @@ next_track:
while (!*ci->taginfo_ready && !ci->stop_codec) while (!*ci->taginfo_ready && !ci->stop_codec)
ci->sleep(1); ci->sleep(1);
samplesdone = ci->id3->offset; sound_samples_done = ci->id3->offset;
ci->configure(DSP_SET_FREQUENCY, (long *)(rb->id3->frequency)); 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 /* if qtmovie_read returns successfully, the stream is up to
* the movie data, which can be used directly by the decoder */ * the movie data, which can be used directly by the decoder */
if (!qtmovie_read(&input_stream, &demux_res)) { if (!qtmovie_read(&input_stream, &demux_res)) {
LOGF("FAAD: Error initialising file\n"); LOGF("FAAD: File init error\n");
err = CODEC_ERROR; err = CODEC_ERROR;
goto done; goto done;
} }
/* initialise the sound converter */ /* initialise the sound converter */
hDecoder = NULL; decoder = NeAACDecOpen();
hDecoder = NeAACDecOpen();
if (!hDecoder) { if (!decoder) {
LOGF("FAAD: Error opening decoder\n"); LOGF("FAAD: Decode open error\n");
err = CODEC_ERROR; err = CODEC_ERROR;
goto done; goto done;
} }
NeAACDecConfigurationPtr conf = NeAACDecGetCurrentConfiguration(hDecoder); NeAACDecConfigurationPtr conf = NeAACDecGetCurrentConfiguration(decoder);
conf->outputFormat = FAAD_FMT_24BIT; /* irrelevant, we don't convert */ conf->outputFormat = FAAD_FMT_24BIT; /* irrelevant, we don't convert */
NeAACDecSetConfiguration(hDecoder, conf); NeAACDecSetConfiguration(decoder, conf);
uint32_t s=0; err = NeAACDecInit2(decoder, demux_res.codecdata, demux_res.codecdata_len, &s, &c);
unsigned char c=0;
err = NeAACDecInit2(hDecoder, demux_res.codecdata,demux_res.codecdata_len, &s, &c);
if (err) { 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; err = CODEC_ERROR;
goto done; goto done;
} }
ci->id3->frequency=s; ci->id3->frequency = s;
i=0; i = 0;
if (samplesdone > 0) { if (sound_samples_done > 0) {
if (alac_seek_raw(&demux_res, &input_stream, samplesdone, if (alac_seek_raw(&demux_res, &input_stream, sound_samples_done,
&samplesdone, (int *)&i)) { &sound_samples_done, (int*) &i)) {
elapsedtime=(samplesdone*10)/(ci->id3->frequency/100); elapsed_time = (sound_samples_done * 10) / (ci->id3->frequency / 100);
ci->set_elapsed(elapsedtime); ci->set_elapsed(elapsed_time);
} else { } else {
samplesdone=0; sound_samples_done = 0;
} }
} }
/* The main decoding loop */ /* The main decoding loop */
while (i < demux_res.num_sample_byte_sizes) { while (i < demux_res.num_sample_byte_sizes) {
rb->yield(); rb->yield();
if (ci->stop_codec || ci->new_track) { if (ci->stop_codec || ci->new_track) {
break; break;
} }
@ -140,10 +148,10 @@ next_track:
/* Deal with any pending seek requests */ /* Deal with any pending seek requests */
if (ci->seek_time) { if (ci->seek_time) {
if (alac_seek(&demux_res, &input_stream, if (alac_seek(&demux_res, &input_stream,
((ci->seek_time-1)/10) * (ci->id3->frequency/100), ((ci->seek_time-1)/10)*(ci->id3->frequency/100),
&samplesdone, (int *)&i)) { &sound_samples_done, (int*) &i)) {
elapsedtime=(samplesdone*10)/(ci->id3->frequency/100); elapsed_time = (sound_samples_done * 10) / (ci->id3->frequency / 100);
ci->set_elapsed(elapsedtime); ci->set_elapsed(elapsed_time);
} }
ci->seek_complete(); ci->seek_complete();
} }
@ -151,52 +159,65 @@ next_track:
/* Lookup the length (in samples and bytes) of block i */ /* Lookup the length (in samples and bytes) of block i */
if (!get_sample_info(&demux_res, i, &sample_duration, if (!get_sample_info(&demux_res, i, &sample_duration,
&sample_byte_size)) { &sample_byte_size)) {
LOGF("AAC: Error in get_sample_info\n"); LOGF("AAC: get_sample_info error\n");
err = CODEC_ERROR; err = CODEC_ERROR;
goto done; 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 */ /* Request the required number of bytes from the input buffer */
buffer=ci->request_buffer(&n,sample_byte_size); buffer=ci->request_buffer(&n,sample_byte_size);
/* Decode one block - returned samples will be host-endian */ /* Decode one block - returned samples will be host-endian */
rb->yield(); NeAACDecDecode(decoder, &frame_info, buffer, n);
decodedbuffer = NeAACDecDecode(hDecoder, &frameInfo, buffer, n); /* Ignore return value, we access samples in the decoder struct
/* ignore decodedbuffer return value, we access samples in the * directly.
decoder struct directly */ */
if (frameInfo.error > 0) { if (frame_info.error > 0) {
LOGF("FAAD: decoding error \"%s\"\n", NeAACDecGetErrorMessage(frameInfo.error)); LOGF("FAAD: decode error '%s'\n", NeAACDecGetErrorMessage(frame_info.error));
err = CODEC_ERROR; err = CODEC_ERROR;
goto done; goto done;
} }
/* Get the number of decoded samples */
samplesdecoded=frameInfo.samples;
/* Advance codec buffer */ /* Advance codec buffer */
ci->advance_buffer(n); ci->advance_buffer(n);
/* Output the audio */ /* Output the audio */
rb->yield(); rb->yield();
while (!rb->pcmbuf_insert_split(hDecoder->time_out[0], while (!rb->pcmbuf_insert_split(decoder->time_out[0],
hDecoder->time_out[1], decoder->time_out[1],
frameInfo.samples*2)) frame_info.samples * 2))
rb->yield(); {
rb->sleep(1);
}
/* Update the elapsed-time indicator */ /* Update the elapsed-time indicator */
samplesdone+=sample_duration; sound_samples_done += sample_duration;
elapsedtime=(samplesdone*10)/(ci->id3->frequency/100); elapsed_time = (sound_samples_done * 10) / (ci->id3->frequency / 100);
ci->set_elapsed(elapsedtime); ci->set_elapsed(elapsed_time);
/* Keep track of current position - for resuming */ /* Keep track of current position - for resuming */
ci->set_offset(elapsedtime); ci->set_offset(elapsed_time);
i++; i++;
} }
err = CODEC_OK; err = CODEC_OK;
done: done:
LOGF("AAC: Decoded %d samples\n",samplesdone); LOGF("AAC: Decoded %d samples, %d frames\n", sound_samples_done);
if (ci->request_next_track()) if (ci->request_next_track())
goto next_track; goto next_track;

View file

@ -55,12 +55,14 @@ static void read_chunk_ftyp(qtmovie_t *qtmovie, size_t chunk_len)
{ {
fourcc_t type; fourcc_t type;
uint32_t minor_ver; 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); type = stream_read_uint32(qtmovie->stream);
size_remaining-=4; size_remaining-=4;
if ((type != MAKEFOURCC('M','4','A',' ')) && 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"); DEBUGF("not M4A file\n");
return; 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) static void read_chunk_tkhd(qtmovie_t *qtmovie, size_t chunk_len)
{ {
/* don't need anything from here atm, skip */ /* 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); 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) static void read_chunk_mdhd(qtmovie_t *qtmovie, size_t chunk_len)
{ {
/* don't need anything from here atm, skip */ /* 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); 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) static void read_chunk_hdlr(qtmovie_t *qtmovie, size_t chunk_len)
{ {
fourcc_t comptype, compsubtype; fourcc_t comptype, compsubtype;
size_t size_remaining = chunk_len - 8; /* FIXME WRONG */ size_t size_remaining = chunk_len - 8;
int strlen; int strlen;
char str[256] = {0}; 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 ???? temp=stream_read_int32(qtmovie->stream);//0x15000414 ????
maxBitrate = stream_read_int32(qtmovie->stream); maxBitrate = stream_read_int32(qtmovie->stream);
avgBitrate = stream_read_int32(qtmovie->stream); avgBitrate = stream_read_int32(qtmovie->stream);
DEBUGF("audioType=%d, maxBitrate=%d, avgBitrate=%d\n",audioType,maxBitrate,avgBitrate); DEBUGF("audioType=%d, maxBitrate=%d, avgBitrate=%d\n",audioType,maxBitrate,avgBitrate);
/* get and verify DecSpecificInfoTag */ /* get and verify DecSpecificInfoTag */
@ -224,7 +225,7 @@ static bool read_chunk_stsd(qtmovie_t *qtmovie, size_t chunk_len)
unsigned int i; unsigned int i;
int j; int j;
uint32_t numentries; uint32_t numentries;
size_t size_remaining = chunk_len - 8; /* FIXME WRONG */ size_t size_remaining = chunk_len - 8;
/* version */ /* version */
stream_read_uint8(qtmovie->stream); 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++) for (i = 0; i < numentries; i++)
{ {
uint32_t entry_size; uint32_t entry_size;
uint16_t version;
uint32_t entry_remaining; uint32_t entry_remaining;
@ -259,43 +259,20 @@ static bool read_chunk_stsd(qtmovie_t *qtmovie, size_t chunk_len)
/* sound info: */ /* sound info: */
stream_skip(qtmovie->stream, 6); /* reserved */ /* reserved + data reference index + sound version + reserved */
entry_remaining -= 6; stream_skip(qtmovie->stream, 6 + 2 + 2 + 6);
entry_remaining -= 6 + 2 + 2 + 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;
qtmovie->res->num_channels = stream_read_uint16(qtmovie->stream); qtmovie->res->num_channels = stream_read_uint16(qtmovie->stream);
qtmovie->res->sound_sample_size = stream_read_uint16(qtmovie->stream);
qtmovie->res->sample_size = stream_read_uint16(qtmovie->stream);
entry_remaining -= 4; entry_remaining -= 4;
/* compression id */
stream_read_uint16(qtmovie->stream);
/* packet size */ /* 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); 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 */ /* remaining is codec data */
@ -372,7 +349,7 @@ static void read_chunk_stts(qtmovie_t *qtmovie, size_t chunk_len)
{ {
unsigned int i; unsigned int i;
uint32_t numentries; uint32_t numentries;
size_t size_remaining = chunk_len - 8; /* FIXME WRONG */ size_t size_remaining = chunk_len - 8;
/* version */ /* version */
stream_read_uint8(qtmovie->stream); stream_read_uint8(qtmovie->stream);
@ -407,7 +384,7 @@ static void read_chunk_stsz(qtmovie_t *qtmovie, size_t chunk_len)
{ {
unsigned int i; unsigned int i;
uint32_t numentries; uint32_t numentries;
size_t size_remaining = chunk_len - 8; /* FIXME WRONG */ size_t size_remaining = chunk_len - 8;
/* version */ /* version */
stream_read_uint8(qtmovie->stream); 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) 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) 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); read_chunk_stsz(qtmovie, sub_chunk_len);
break; break;
case MAKEFOURCC('s','t','s','c'): case MAKEFOURCC('s','t','s','c'):
read_chunk_stsc(qtmovie, sub_chunk_len);
break;
case MAKEFOURCC('s','t','c','o'): case MAKEFOURCC('s','t','c','o'):
/* skip these, no indexing for us! */ read_chunk_stco(qtmovie, sub_chunk_len);
stream_skip(qtmovie->stream, sub_chunk_len - 8);
break; break;
default: default:
DEBUGF("(stbl) unknown chunk id: %c%c%c%c\n", DEBUGF("(stbl) unknown chunk id: %c%c%c%c\n",
SPLITFOURCC(sub_chunk_id)); 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; 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) static bool read_chunk_minf(qtmovie_t *qtmovie, size_t chunk_len)
{ {
size_t dinf_size, stbl_size; 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; uint32_t i;
/**** SOUND HEADER CHUNK ****/ /**** 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) 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) 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 */ /* 'trak' - a movie track - contains other atoms */
static bool read_chunk_trak(qtmovie_t *qtmovie, size_t chunk_len) 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) 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) static void read_chunk_mvhd(qtmovie_t *qtmovie, size_t chunk_len)
{ {
/* don't need anything from here atm, skip */ /* 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); 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) static void read_chunk_udta(qtmovie_t *qtmovie, size_t chunk_len)
{ {
/* don't need anything from here atm, skip */ /* 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); 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 */ /* 'moov' movie atom - contains other atoms */
static bool read_chunk_moov(qtmovie_t *qtmovie, size_t chunk_len) 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) while (size_remaining)
{ {
@ -688,7 +730,7 @@ static bool read_chunk_moov(qtmovie_t *qtmovie, size_t chunk_len)
default: default:
DEBUGF("(moov) unknown chunk id: %c%c%c%c\n", DEBUGF("(moov) unknown chunk id: %c%c%c%c\n",
SPLITFOURCC(sub_chunk_id)); 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; 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) 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; 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) 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 !!!! */ /* these following atoms can be skipped !!!! */
case MAKEFOURCC('f','r','e','e'): case MAKEFOURCC('f','r','e','e'):
stream_skip(qtmovie.stream, chunk_len - 8); /* FIXME not 8 */ stream_skip(qtmovie.stream, chunk_len - 8);
break; break;
default: default:
//DEBUGF("(top) unknown chunk id: %c%c%c%c\n",SPLITFOURCC(chunk_id)); //DEBUGF("(top) unknown chunk id: %c%c%c%c\n",SPLITFOURCC(chunk_id));

View file

@ -21,6 +21,13 @@
#include <inttypes.h> #include <inttypes.h>
#include "m4a.h" #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 */ /* Implementation of the stream.h functions used by libalac */
#define _Swap32(v) do { \ #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_skip(stream_t *stream, size_t skip)
{ {
(void)stream; stream->ci->advance_buffer(skip);
#if 1
char buf;
while (skip > 0) {
stream->ci->read_filebuf(&buf,1);
skip--;
}
#endif
//stream->ci->advance_buffer(skip);
} }
int stream_eof(stream_t *stream) int stream_eof(stream_t *stream)
@ -158,136 +157,273 @@ int get_sample_info(demux_res_t *demux_res, uint32_t samplenum,
return 1; return 1;
} }
/* Seek to sample_loc (or close to it). Return 1 on success (and unsigned int get_sample_offset(demux_res_t *demux_res, uint32_t sample)
modify samplesdone and currentblock), 0 if failed
Seeking uses the following two arrays:
1) the sample_byte_size array contains the length in bytes of
each block ("sample" in Applespeak).
2) the time_to_sample array contains the duration (in samples) of
each block of data.
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).
Each ALAC block seems to be independent of all the others.
*/
unsigned int alac_seek (demux_res_t* demux_res,
stream_t* stream,
unsigned int sample_loc,
uint32_t* samplesdone, int* currentblock)
{ {
int flag; uint32_t chunk = 1;
unsigned int i,j; uint32_t range_samples = 0;
unsigned int newblock; uint32_t total_samples = 0;
unsigned int newsample; uint32_t chunk_sample;
unsigned int newpos; uint32_t prev_chunk;
uint32_t prev_chunk_samples;
/* First check we have the appropriate metadata - we should always uint32_t file_offset;
have it. */ uint32_t i;
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 ((i<demux_res->num_time_to_samples) && (flag==0) &&
(newsample < sample_loc)) {
j=(sample_loc-newsample) /
demux_res->time_to_sample[i].sample_duration;
if (j <= demux_res->time_to_sample[i].sample_count) { /* First check we have the appropriate metadata - we should always
newblock+=j; * have it.
newsample+=j*demux_res->time_to_sample[i].sample_duration; */
flag=1;
} else { if (sample >= demux_res->num_sample_byte_sizes ||
newsample+=(demux_res->time_to_sample[i].sample_duration !demux_res->num_sample_to_chunks ||
* demux_res->time_to_sample[i].sample_count); !demux_res->num_chunk_offsets)
newblock+=demux_res->time_to_sample[i].sample_count; {
i++; return 0;
} }
}
/* We know the new block, now calculate the file position */ /* Locate the chunk containing the sample */
newpos=demux_res->mdat_offset;
for (i=0;i<newblock;i++) { prev_chunk = demux_res->sample_to_chunk[0].first_chunk;
newpos+=demux_res->sample_byte_size[i]; prev_chunk_samples = demux_res->sample_to_chunk[0].num_samples;
}
/* We know the new file position, so let's try to seek to it */ for (i = 1; i < demux_res->num_sample_to_chunks; i++)
if (stream->ci->seek_buffer(newpos)) { {
*samplesdone=newsample; chunk = demux_res->sample_to_chunk[i].first_chunk;
*currentblock=newblock; range_samples = (chunk - prev_chunk) * prev_chunk_samples;
return 1;
} else { if (sample < total_samples + range_samples)
return 0; {
} break;
}
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;
}
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;
} }
/* Seek to file_loc (or close to it). Return 1 on success (and /* Seek to the sample containing sound_sample_loc. Return 1 on success
modify samplesdone and currentblock), 0 if failed * (and modify sound_samples_done and current_sample), 0 if failed.
*
Seeking uses the following array: * Seeking uses the following arrays:
*
the sample_byte_size array contains the length in bytes of * 1) the time_to_sample array contains the duration (in sound samples)
each block ("sample" in Applespeak). * of each sample of data.
*
So we just find the last block before (or at) the requested position. * 2) the sample_byte_size array contains the length in bytes of each
* sample.
Each ALAC block seems to be independent of all the others. *
* 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,
unsigned int alac_seek_raw (demux_res_t* demux_res, uint32_t sound_sample_loc, uint32_t* sound_samples_done,
stream_t* stream, int* current_sample)
unsigned int file_loc,
uint32_t* samplesdone, int* currentblock)
{ {
unsigned int i; uint32_t i;
unsigned int j; uint32_t j;
unsigned int newblock; uint32_t new_sample;
unsigned int newsample; uint32_t new_sound_sample;
unsigned int newpos; uint32_t new_pos;
/* First check we have the appropriate metadata - we should always /* First check we have the appropriate metadata - we should always
have it. */ * have it.
if ((demux_res->num_time_to_samples==0) || */
(demux_res->num_sample_byte_sizes==0)) { return 0; }
if ((demux_res->num_time_to_samples==0) ||
/* Find the destination block from the sample_byte_size array. */ (demux_res->num_sample_byte_sizes==0))
newpos=demux_res->mdat_offset; {
for (i=0;(i<demux_res->num_sample_byte_sizes) && return 0;
(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;(i<demux_res->num_time_to_samples) && (j<newblock);
i++,j+=demux_res->time_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);
} }
}
/* We know the new file position, so let's try to seek to it */ /* Find the destination block from time_to_sample array */
if (stream->ci->seek_buffer(newpos)) {
*samplesdone=newsample; i = 0;
*currentblock=newblock; new_sample = 0;
return 1; new_sound_sample = 0;
} else {
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. */
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 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;
}
/* Locate the chunk containing file_loc. */
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];
/* 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;
/* 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;
}
return 0; return 0;
}
} }

View file

@ -33,11 +33,20 @@ typedef uint32_t fourcc_t;
typedef struct typedef struct
{ {
uint16_t num_channels; uint16_t num_channels;
uint16_t sample_size; uint16_t sound_sample_size;
uint32_t sample_rate; uint32_t sound_sample_rate;
fourcc_t format; fourcc_t format;
void *buf; 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 { struct {
uint32_t sample_count; uint32_t sample_count;
uint32_t sample_duration; uint32_t sample_duration;
@ -93,16 +102,13 @@ void stream_skip(stream_t *stream, size_t skip);
int stream_eof(stream_t *stream); int stream_eof(stream_t *stream);
void stream_create(stream_t *stream,struct codec_api* ci); void stream_create(stream_t *stream,struct codec_api* ci);
int get_sample_info(demux_res_t *demux_res, uint32_t samplenum, int get_sample_info(demux_res_t *demux_res, uint32_t sample,
uint32_t *sample_duration, uint32_t *sample_duration, uint32_t *sample_byte_size);
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, unsigned int alac_seek (demux_res_t* demux_res, stream_t* stream,
stream_t* stream, uint32_t sound_sample_loc, uint32_t* sound_samples_done,
unsigned int sample_loc, int* current_sample);
uint32_t* samplesdone, int* currentblock); unsigned int alac_seek_raw (demux_res_t* demux_res, stream_t* stream,
unsigned int alac_seek_raw (demux_res_t* demux_res, uint32_t file_loc, uint32_t* sound_samples_done, int* current_sample);
stream_t* stream,
unsigned int file_loc,
uint32_t* samplesdone, int* currentblock);
#endif /* STREAM_H */ #endif /* STREAM_H */