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 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,31 +98,27 @@ 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;
}
@ -120,19 +127,20 @@ next_track:
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;
}
@ -141,9 +149,9 @@ next_track:
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);
&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;

View file

@ -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));

View file

@ -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;
return 1;
} else {
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.
*/
unsigned int alac_seek_raw (demux_res_t* demux_res,
stream_t* stream,
unsigned int file_loc,
uint32_t* samplesdone, int* currentblock)
if (stream->ci->seek_buffer(new_pos))
{
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;(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;
*sound_samples_done = new_sound_sample;
*current_sample = new_sample;
return 1;
} else {
}
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;
}

View file

@ -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 */