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:
parent
ca3d872699
commit
9896fd1ade
4 changed files with 454 additions and 254 deletions
|
|
@ -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);
|
||||
NeAACDecSetConfiguration(decoder, conf);
|
||||
|
||||
uint32_t s=0;
|
||||
unsigned char c=0;
|
||||
|
||||
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));
|
||||
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;
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
|
|
|
|||
|
|
@ -21,6 +21,13 @@
|
|||
#include <inttypes.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 */
|
||||
|
||||
#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
|
||||
|
||||
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)
|
||||
unsigned int get_sample_offset(demux_res_t *demux_res, uint32_t sample)
|
||||
{
|
||||
int flag;
|
||||
unsigned int i,j;
|
||||
unsigned int newblock;
|
||||
unsigned int newsample;
|
||||
unsigned int newpos;
|
||||
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. */
|
||||
* have it.
|
||||
*/
|
||||
|
||||
if (sample >= demux_res->num_sample_byte_sizes ||
|
||||
!demux_res->num_sample_to_chunks ||
|
||||
!demux_res->num_chunk_offsets)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* 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;
|
||||
|
||||
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;
|
||||
|
||||
if (sample < total_samples + range_samples)
|
||||
{
|
||||
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 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)
|
||||
{
|
||||
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; }
|
||||
(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) /
|
||||
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) {
|
||||
newblock+=j;
|
||||
newsample+=j*demux_res->time_to_sample[i].sample_duration;
|
||||
flag=1;
|
||||
} else {
|
||||
newsample+=(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);
|
||||
newblock+=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;i<newblock;i++) {
|
||||
newpos+=demux_res->sample_byte_size[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(newpos)) {
|
||||
*samplesdone=newsample;
|
||||
*currentblock=newblock;
|
||||
|
||||
if (stream->ci->seek_buffer(new_pos))
|
||||
{
|
||||
*sound_samples_done = new_sound_sample;
|
||||
*current_sample = new_sample;
|
||||
return 1;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
|
||||
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:
|
||||
|
||||
the sample_byte_size array contains the length in bytes of
|
||||
each block ("sample" in Applespeak).
|
||||
|
||||
So we just find the last block before (or at) the requested position.
|
||||
|
||||
Each ALAC block seems to be independent of all the others.
|
||||
/* 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,
|
||||
unsigned int file_loc,
|
||||
uint32_t* samplesdone, int* currentblock)
|
||||
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)
|
||||
{
|
||||
unsigned int i;
|
||||
unsigned int j;
|
||||
unsigned int newblock;
|
||||
unsigned int newsample;
|
||||
unsigned int newpos;
|
||||
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;
|
||||
|
||||
/* 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;(i<demux_res->num_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;(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 */
|
||||
if (stream->ci->seek_buffer(newpos)) {
|
||||
*samplesdone=newsample;
|
||||
*currentblock=newblock;
|
||||
return 1;
|
||||
} else {
|
||||
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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue