diff --git a/apps/codecs/aac.c b/apps/codecs/aac.c index 6046035..7410c82 100644 --- a/apps/codecs/aac.c +++ b/apps/codecs/aac.c @@ -79,7 +79,11 @@ 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)) { + if (qtmovie_read(&input_stream, &demux_res)) { + // OK, wind back to start of first mdat + input_stream.ci->seek_buffer(demux_res.mdat_chunks[0].offset); + } else { + DEBUGF("qtmovie_read failed....\n"); LOGF("FAAD: File init error\n"); err = CODEC_ERROR; goto done; diff --git a/apps/codecs/libm4a/demux.c b/apps/codecs/libm4a/demux.c index f0c6922..db1f6c9 100644 --- a/apps/codecs/libm4a/demux.c +++ b/apps/codecs/libm4a/demux.c @@ -720,20 +720,60 @@ static bool read_chunk_moov(qtmovie_t *qtmovie, size_t chunk_len) return true; } -static void read_chunk_mdat(qtmovie_t *qtmovie, size_t chunk_len) +/* + Add information about an mdat chunk into mdat_chunks array. + If we run out of space (>MAX_MDAT_CHUNKS) let qtmovie_read + know and exit. +*/ +static bool read_chunk_mdat(qtmovie_t *qtmovie, size_t chunk_len) { - size_t size_remaining = chunk_len - 8; + // Ignore the size of the atom header + size_t len = chunk_len - 8; + int chunk = qtmovie->res->num_mdat_chunks; - qtmovie->res->mdat_len = size_remaining; + if (len>0) + { + if (chunkres->mdat_chunks[chunk]; + md->offset = stream_tell(qtmovie->stream); + md->len = len; + + DEBUGF("read_chunk_mdat: off=%d, len=%d\n", + md->offset, md->len); + + // Skip on + stream_skip(qtmovie->stream, len); + qtmovie->res->num_mdat_chunks++; + return false; + } + + // Out of space + return true; + } + + // ignore 0 length mdat's + return false; } +/* + qtmovie_read + + Read through the "QuickTime" file filling in the information we need + to know about it. + + 0 = Something went wrong + 1 = Ready to go +*/ + int qtmovie_read(stream_t *file, demux_res_t *demux_res) { - qtmovie_t qtmovie; + qtmovie_t qtm; + qtmovie_t *qtmovie=&qtm; // hacky for DEBUGF /* construct the stream */ - qtmovie.stream = file; - qtmovie.res = demux_res; + qtm.stream = file; + qtm.res = demux_res; /* read the chunks */ while (1) @@ -741,10 +781,16 @@ int qtmovie_read(stream_t *file, demux_res_t *demux_res) size_t chunk_len; fourcc_t chunk_id; - chunk_len = stream_read_uint32(qtmovie.stream); - if (stream_eof(qtmovie.stream)) + chunk_len = stream_read_uint32(qtm.stream); + if (stream_eof(qtm.stream)) { - return 0; + if (qtmovie->res->num_mdat_chunks>0) + { + // We found some mdat atoms + return 1; + } else { + return 0; + } } if (chunk_len == 1) @@ -752,38 +798,38 @@ int qtmovie_read(stream_t *file, demux_res_t *demux_res) //DEBUGF("need 64bit support\n"); return 0; } - chunk_id = stream_read_uint32(qtmovie.stream); + chunk_id = stream_read_uint32(qtm.stream); - //DEBUGF("Found a chunk %c%c%c%c, length=%d\n",SPLITFOURCC(chunk_id),chunk_len); + DEBUGF("qtmovie_read: Found a chunk %c%c%c%c, length=%ld\n",SPLITFOURCC(chunk_id),chunk_len); + switch (chunk_id) { case MAKEFOURCC('f','t','y','p'): - read_chunk_ftyp(&qtmovie, chunk_len); + read_chunk_ftyp(&qtm, chunk_len); break; case MAKEFOURCC('m','o','o','v'): - if (!read_chunk_moov(&qtmovie, chunk_len)) { + if (!read_chunk_moov(&qtm, chunk_len)) { return 0; } break; - /* once we hit mdat we stop reading and return. - * this is on the assumption that there is no furhter interesting - * stuff in the stream. if there is stuff will fail (:()). - * But we need the read pointer to be at the mdat stuff - * for the decoder. And we don't want to rely on fseek/ftell, - * as they may not always be avilable */ case MAKEFOURCC('m','d','a','t'): - read_chunk_mdat(&qtmovie, chunk_len); - /* Keep track of start of stream in file - used for seeking */ - qtmovie.res->mdat_offset=stream_tell(qtmovie.stream); - /* There can be empty mdats before the real one. If so, skip them */ - if (qtmovie.res->mdat_len > 0) { - return 1; + /* + We build an array of mdat chunks for this file that + describe where each set of mdat appears in the file. + + If we have found more than MAX_MDAT_CHUNKS we leave and + pray enough of the file is playable + */ + { + bool full = read_chunk_mdat(&qtm, chunk_len); + if (full) + return 1; } break; /* these following atoms can be skipped !!!! */ case MAKEFOURCC('f','r','e','e'): - stream_skip(qtmovie.stream, chunk_len - 8); + stream_skip(qtm.stream, chunk_len - 8); break; default: //DEBUGF("(top) unknown chunk id: %c%c%c%c\n",SPLITFOURCC(chunk_id)); @@ -791,6 +837,8 @@ int qtmovie_read(stream_t *file, demux_res_t *demux_res) } } + + // Never get here return 0; } diff --git a/apps/codecs/libm4a/m4a.c b/apps/codecs/libm4a/m4a.c index f066640..bfffeea 100644 --- a/apps/codecs/libm4a/m4a.c +++ b/apps/codecs/libm4a/m4a.c @@ -228,11 +228,12 @@ unsigned int get_sample_offset(demux_res_t *demux_res, uint32_t sample) file_offset += demux_res->sample_byte_size[i]; } - if (file_offset > demux_res->mdat_offset + demux_res->mdat_len) +/*TODO: FIXME - this will break hard + if (file_offset > demux_res->mdat_offset + demux_res->mdat_len) { return 0; } - +*/ return file_offset; } diff --git a/apps/codecs/libm4a/m4a.h b/apps/codecs/libm4a/m4a.h index e2d4376..29eab84 100644 --- a/apps/codecs/libm4a/m4a.h +++ b/apps/codecs/libm4a/m4a.h @@ -45,6 +45,20 @@ typedef struct { typedef uint32_t fourcc_t; +/* + mp4 files can contain a number of mdat chunks which contain the + "media data" throughout the file. We store an array of + offset/lengths that describe the file. I'm hopping we never see + more than MAX_MDAT_CHUNKS! +*/ + +#define MAX_MDAT_CHUNKS 10 + +typedef struct { + int offset; + uint32_t len; +} mdat_chunk_t; + typedef struct { uint16_t num_channels; @@ -74,11 +88,8 @@ typedef struct uint32_t codecdata_len; uint8_t codecdata[MAX_CODECDATA_SIZE]; - int mdat_offset; - uint32_t mdat_len; -#if 0 - void *mdat; -#endif + mdat_chunk_t mdat_chunks[MAX_MDAT_CHUNKS]; + int num_mdat_chunks; } demux_res_t; int qtmovie_read(stream_t *stream, demux_res_t *demux_res); diff --git a/apps/metadata/mp4.c b/apps/metadata/mp4.c index b6aeaf1..e2434a6 100644 --- a/apps/metadata/mp4.c +++ b/apps/metadata/mp4.c @@ -130,41 +130,61 @@ static unsigned int read_mp4_tag_string(int fd, int size_left, char** buffer, return length; } +/* + Read an MP4 atom. This consists of the following: + + size: u32 can be 0 (last atom), 1 (use extended size), size in bytes + type: u32 usually a 4 char code + extended_size: 64 bit size, not likely on Rockbox supportable files +*/ + static unsigned int read_mp4_atom(int fd, unsigned int* size, unsigned int* type, unsigned int size_left) { read_uint32be(fd, size); read_uint32be(fd, type); - if (*size == 1) + switch (*size) { - /* FAT32 doesn't support files this big, so something seems to - * be wrong. (64-bit sizes should only be used when required.) - */ - errno = EFBIG; - *type = 0; - return 0; - } - - if (*size > 0) - { - if (*size > size_left) + /* Last Atom */ + case 0: { + *size = size_left - 8; size_left = 0; } - else + /* Extended size atom + + FAT32 doesn't support files this big, so something seems to + be wrong. (64-bit sizes should only be used when required.) + */ + case 1: { - size_left -= *size; + errno = EFBIG; + *type = 0; + return 0; + } + /* Normal atom */ + default: + { + /* Was this the last atom? */ + if (*size >= size_left) + { + size_left = 0; + } + else + { + size_left -= *size; + } + + /* The atom size is the size of the whole atom (including + it's header. However we want the size of the data + contained in the atom (certainly for the seek + calculations) + */ + *size -= 8; } - - *size -= 8; - } - else - { - *size = size_left; - size_left = 0; } - + return size_left; } @@ -549,14 +569,18 @@ static bool read_mp4_container(int fd, struct mp3entry* id3, unsigned int type; unsigned int handler = 0; bool rc = true; + + DEBUGF("read_mp4_container(%d, %p, %d)\n", fd, id3, size_left); do { size_left = read_mp4_atom(fd, &size, &type, size_left); - - /* DEBUGF("Atom: '%c%c%c%c' (0x%08x, %d bytes left)\n", + + /* + DEBUGF("Atom: '%c%c%c%c' (0x%08x, %d bytes left)\n", (type >> 24) & 0xff, (type >> 16) & 0xff, (type >> 8) & 0xff, - type & 0xff, type, size); */ + type & 0xff, type, size_left); + */ switch (type) { @@ -578,10 +602,10 @@ static bool read_mp4_container(int fd, struct mp3entry* id3, } break; + /* ISO/IEC 14496-12:2008(E) Section 8.11.1 */ case MP4_meta: - lseek(fd, 4, SEEK_CUR); /* Skip version */ - size -= 4; - /* Fall through */ + /* Do nothing but skip */ + break; case MP4_moov: case MP4_udta: @@ -678,20 +702,29 @@ static bool read_mp4_container(int fd, struct mp3entry* id3, } break; + /* + The mdat atom specifies the media sample data (audio or + video bitstream), which is what we decode. + + We currently only handle simple mp4 files where mdat is + the last atom we see. However the MP4 parsing code has + been tweaked to parse beyond that although it currently + doesn't play + */ case MP4_mdat: - id3->filesize = size; + if (id3->filesize) + DEBUGF(" second MP4_mdat (%d), this file may not play\n", size); + id3->filesize += size; break; default: + DEBUGF(" un-handled case: type=0x%x\n", size); break; } lseek(fd, size, SEEK_CUR); } - while (rc && (size_left > 0) && (errno == 0) && (id3->filesize == 0)); - /* Break on non-zero filesize, since Rockbox currently doesn't support - * metadata after the mdat atom (which sets the filesize field). - */ + while (rc && (size_left > 0) && (errno == 0)); return rc; } @@ -701,10 +734,20 @@ bool get_mp4_metadata(int fd, struct mp3entry* id3) id3->codectype = AFMT_UNKNOWN; id3->filesize = 0; errno = 0; + bool ok; + + ok = read_mp4_container(fd, id3, filesize(fd)); - if (read_mp4_container(fd, id3, filesize(fd)) && (errno == 0) - && (id3->samples > 0) && (id3->frequency > 0) - && (id3->filesize > 0)) + if (ok==false || errno != 0) + { + DEBUGF("read_mp4_container failed (errno=%d)\n", errno); + return false; + } + + + if ((id3->samples > 0) && + (id3->frequency > 0) && + (id3->filesize > 0)) { if (id3->codectype == AFMT_UNKNOWN) { @@ -726,6 +769,7 @@ bool get_mp4_metadata(int fd, struct mp3entry* id3) } else { + /* Invalid Metadata */ logf("MP4 metadata error"); DEBUGF("MP4 metadata error. errno %d, frequency %ld, filesize %ld\n", errno, id3->frequency, id3->filesize); diff --git a/apps/playback.c b/apps/playback.c index 0795264..4019c9d 100644 --- a/apps/playback.c +++ b/apps/playback.c @@ -125,18 +125,18 @@ enum { Q_NULL = 0, Q_AUDIO_PLAY = 1, - Q_AUDIO_STOP, - Q_AUDIO_PAUSE, - Q_AUDIO_SKIP, - Q_AUDIO_PRE_FF_REWIND, - Q_AUDIO_FF_REWIND, - Q_AUDIO_CHECK_NEW_TRACK, - Q_AUDIO_FLUSH, - Q_AUDIO_TRACK_CHANGED, - Q_AUDIO_DIR_SKIP, - Q_AUDIO_POSTINIT, - Q_AUDIO_FILL_BUFFER, - Q_AUDIO_FINISH_LOAD, + Q_AUDIO_STOP, //2 + Q_AUDIO_PAUSE, //3 + Q_AUDIO_SKIP, //4 + Q_AUDIO_PRE_FF_REWIND, //5 + Q_AUDIO_FF_REWIND, //6 + Q_AUDIO_CHECK_NEW_TRACK, //7 + Q_AUDIO_FLUSH, //8 + Q_AUDIO_TRACK_CHANGED, //9 + Q_AUDIO_DIR_SKIP, //10 + Q_AUDIO_POSTINIT, //11 + Q_AUDIO_FILL_BUFFER, //12 + Q_AUDIO_FINISH_LOAD, //13 Q_CODEC_REQUEST_COMPLETE, Q_CODEC_REQUEST_FAILED, @@ -1280,6 +1280,11 @@ static bool codec_request_next_track_callback(void) } } +/* + * This is the codec thread. It runs forever servicing codec events as + * it goes. + * + */ static void codec_thread(void) { struct queue_event ev;