diff options
author | waker <wakeroid@gmail.com> | 2012-08-22 22:27:36 +0200 |
---|---|---|
committer | waker <wakeroid@gmail.com> | 2012-08-24 22:12:20 +0200 |
commit | f3f1983b3156a97b8e2f492dd9e1db31cd2e4817 (patch) | |
tree | 4bda37fa9865d0e223cd8e8d33d5052c82ec3127 | |
parent | 84c9ebfa0f8eef41948929fb85739727bab06b05 (diff) |
aac: added chapters support
-rw-r--r-- | plugins/aac/aac.c | 286 | ||||
-rw-r--r-- | plugins/libmp4ff/mp4atom.c | 162 | ||||
-rw-r--r-- | plugins/libmp4ff/mp4ff.c | 10 | ||||
-rw-r--r-- | plugins/libmp4ff/mp4ff.h | 35 | ||||
-rw-r--r-- | plugins/libmp4ff/mp4ffint.h | 29 |
5 files changed, 404 insertions, 118 deletions
diff --git a/plugins/aac/aac.c b/plugins/aac/aac.c index ef2d835e..c361c86a 100644 --- a/plugins/aac/aac.c +++ b/plugins/aac/aac.c @@ -34,8 +34,8 @@ #define min(x,y) ((x)<(y)?(x):(y)) #define max(x,y) ((x)>(y)?(x):(y)) -//#define trace(...) { fprintf(stderr, __VA_ARGS__); } -#define trace(fmt,...) +#define trace(...) { fprintf(stderr, __VA_ARGS__); } +//#define trace(fmt,...) static DB_decoder_t plugin; static DB_functions_t *deadbeef; @@ -64,10 +64,9 @@ typedef struct { DB_fileinfo_t info; NeAACDecHandle dec; DB_FILE *file; - MP4FILE mp4file; + MP4FILE mp4; MP4FILE_CB mp4reader; NeAACDecFrameInfo frame_info; // last frame info - int32_t timescale; int mp4track; int mp4samples; int mp4sample; @@ -330,39 +329,37 @@ aac_init (DB_fileinfo_t *_info, DB_playItem_t *it) { if (!info->file->vfs->is_streaming ()) { trace ("aac_init: mp4ff_open_read %s\n", deadbeef->pl_find_meta (it, ":URI")); - info->mp4file = mp4ff_open_read (&info->mp4reader); - if (info->mp4file) { - info->mp4track = deadbeef->pl_find_meta_int (it, ":TRACKNUM", -1); - trace ("track: %d\n", info->mp4track); - if (info->mp4track < 0) { - trace ("aac: warning: the file was added by old aac plugin, might be wrong, please re-add\n"); - // find - int ntracks = mp4ff_total_tracks (info->mp4file); - for (int i = 0; i < ntracks; i++) { - int res = mp4_track_get_info (info->mp4file, i, &duration, &samplerate, &channels, &totalsamples); - if (res == 0) { - info->mp4track = i; - break; - } + info->mp4 = mp4ff_open_read (&info->mp4reader); + if (info->mp4) { + int ntracks = mp4ff_total_tracks (info->mp4); + for (int i = 0; i < ntracks; i++) { + if (mp4ff_get_track_type (info->mp4, i) != TRACK_AUDIO) { + continue; + } + int res = mp4_track_get_info (info->mp4, i, &duration, &samplerate, &channels, &totalsamples); + if (res >= 0 && duration > 0) { + info->mp4track = i; + break; } } + trace ("track: %d\n", info->mp4track); if (info->mp4track >= 0) { // prepare decoder - int res = mp4_track_get_info (info->mp4file, info->mp4track, &duration, &samplerate, &channels, &totalsamples); + int res = mp4_track_get_info (info->mp4, info->mp4track, &duration, &samplerate, &channels, &totalsamples); if (res != 0) { trace ("aac: mp4_track_get_info(%d) returned error\n", info->mp4track); return -1; } // init mp4 decoding - info->mp4samples = mp4ff_num_samples(info->mp4file, info->mp4track); + info->mp4samples = mp4ff_num_samples(info->mp4, info->mp4track); info->dec = NeAACDecOpen (); unsigned long srate; unsigned char ch; unsigned char* buff = 0; unsigned int buff_size = 0; mp4AudioSpecificConfig mp4ASC; - mp4ff_get_decoder_config (info->mp4file, info->mp4track, &buff, &buff_size); + mp4ff_get_decoder_config (info->mp4, info->mp4track, &buff, &buff_size); if(buff) { int rc = AudioSpecificConfig(buff, buff_size, &mp4ASC); if(rc < 0) { @@ -447,7 +444,7 @@ aac_init (DB_fileinfo_t *_info, DB_playItem_t *it) { _info->fmt.bps = 16; _info->plugin = &plugin; - if (!info->mp4file) { + if (!info->mp4) { trace ("NeAACDecOpen for raw stream\n"); info->dec = NeAACDecOpen (); @@ -511,8 +508,8 @@ aac_free (DB_fileinfo_t *_info) { if (info->file) { deadbeef->fclose (info->file); } - if (info->mp4file) { - mp4ff_close (info->mp4file); + if (info->mp4) { + mp4ff_close (info->mp4); } if (info->dec) { NeAACDecClose (info->dec); @@ -644,15 +641,15 @@ aac_read (DB_fileinfo_t *_info, char *bytes, int size) { char *samples = NULL; - if (info->mp4file) { + if (info->mp4) { if (info->mp4sample >= info->mp4samples) { - printf ("aac: finished with the last mp4sample\n"); + trace ("aac: finished with the last mp4sample\n"); break; } unsigned char *buffer = NULL; int buffer_size = 0; - int rc = mp4ff_read_sample (info->mp4file, info->mp4track, info->mp4sample, &buffer, &buffer_size); + int rc = mp4ff_read_sample (info->mp4, info->mp4track, info->mp4sample, &buffer, &buffer_size); if (rc == 0) { trace ("mp4ff_read_sample failed\n"); info->eof = 1; @@ -665,7 +662,7 @@ aac_read (DB_fileinfo_t *_info, char *bytes, int size) { free (buffer); } if (!samples) { - printf ("aac: NeAACDecDecode returned NULL\n"); + trace ("aac: NeAACDecDecode returned NULL\n"); break; } } @@ -779,7 +776,7 @@ aac_seek_sample (DB_fileinfo_t *_info, int sample) { aac_info_t *info = (aac_info_t *)_info; sample += info->startsample; - if (info->mp4file) { + if (info->mp4) { int scale = info->mp4framesize; info->mp4sample = sample / scale; info->skipsamples = sample - info->mp4sample * scale; @@ -940,6 +937,117 @@ aac_read_metadata (DB_playItem_t *it) { return 0; } +typedef struct { + char *title; + int32_t startsample; + int32_t endsample; +} aac_chapter_t; + +static aac_chapter_t * +aac_load_itunes_chapters (mp4ff_t *mp4, /* out */ int *num_chapters, int samplerate) { + *num_chapters = 0; + int i_entry_count = mp4ff_chap_get_num_tracks (mp4); + int i_tracks = mp4ff_total_tracks (mp4); + int i, j; + for( i = 0; i < i_entry_count; i++ ) + { + for( j = 0; j < i_tracks; j++ ) + { + int32_t tt = mp4ff_get_track_type (mp4, j); + trace ("aac: i_tracks=%d found track id=%d type=%d (expected %d %d)\n", i_tracks, mp4ff_get_track_id (mp4, j), mp4ff_get_track_type (mp4, j), mp4ff_chap_get_track_id (mp4, i), TRACK_TEXT); + if(mp4ff_chap_get_track_id (mp4, i) == mp4ff_get_track_id (mp4, j) && + mp4ff_get_track_type (mp4, j) == TRACK_TEXT) { + trace ("aac: found subt track\n"); + break; + } + } + if( j < i_tracks ) + { + int i_sample_count = mp4ff_num_samples (mp4, j); + int i_sample; + + aac_chapter_t *chapters = malloc (sizeof (aac_chapter_t) * i_sample_count); + memset (chapters, 0, sizeof (aac_chapter_t) * i_sample_count); + *num_chapters = 0; + + int64_t total_dur = 0; + int64_t curr_sample = 0; + for( i_sample = 0; i_sample < i_sample_count; i_sample++ ) + { + int32_t dur = (int64_t)1000 * mp4ff_get_sample_duration (mp4, j, i_sample) / mp4ff_time_scale (mp4, j); // milliseconds + total_dur += dur; + trace ("dur: %d (%f min)\n", dur, dur / 1000.f / 60.f); + unsigned char *buffer = NULL; + int buffer_size = 0; + + int rc = mp4ff_read_sample (mp4, j, i_sample, &buffer, &buffer_size); + + if (rc == 0 || !buffer) { + continue; + } + int len = (buffer[0] << 8) | buffer[1]; + len = min (len, buffer_size - 2); + chapters[*num_chapters].title = strndup (&buffer[2], len); + chapters[*num_chapters].startsample = curr_sample; + curr_sample += (int64_t)dur * samplerate / 1000.f; + chapters[*num_chapters].endsample = curr_sample - 1; + trace ("aac: chapter %d: %s, s=%d e=%d\n", *num_chapters, chapters[*num_chapters].title, chapters[*num_chapters].startsample, chapters[*num_chapters].endsample); + if (buffer) { + free (buffer); + } + (*num_chapters)++; + } + trace ("aac: total dur: %lld (%f min)\n", total_dur, total_dur / 1000.f / 60.f); + return chapters; + } + } + return NULL; +} + +static DB_playItem_t * +aac_insert_with_chapters (ddb_playlist_t *plt, DB_playItem_t *after, DB_playItem_t *origin, aac_chapter_t *chapters, int num_chapters, int totalsamples, int samplerate) { + deadbeef->pl_lock (); + DB_playItem_t *ins = after; + for (int i = 0; i < num_chapters; i++) { + const char *uri = deadbeef->pl_find_meta_raw (origin, ":URI"); + const char *dec = deadbeef->pl_find_meta_raw (origin, ":DECODER"); + const char *ftype= "MP4 AAC";//pl_find_meta_raw (origin, ":FILETYPE"); + + DB_playItem_t *it = deadbeef->pl_item_alloc_init (uri, dec); + deadbeef->pl_set_meta_int (it, ":TRACKNUM", i); + deadbeef->pl_set_meta_int (it, "TRACK", i); + deadbeef->pl_add_meta (it, "title", chapters[i].title); + it->startsample = chapters[i].startsample; + it->endsample = chapters[i].endsample; + deadbeef->pl_replace_meta (it, ":FILETYPE", ftype); + deadbeef->plt_set_item_duration (plt, it, (float)(it->endsample - it->startsample + 1) / samplerate); + after = deadbeef->plt_insert_item (plt, after, it); + deadbeef->pl_item_unref (it); + } + deadbeef->pl_item_ref (after); + + DB_playItem_t *first = deadbeef->pl_get_next (ins, PL_MAIN); + + if (!first) { + first = deadbeef->plt_get_first (plt, PL_MAIN); + } + + if (!first) { + deadbeef->pl_unlock (); + return NULL; + } + trace ("aac: split by chapters success\n"); + // copy metadata from embedded tags + uint32_t f = deadbeef->pl_get_item_flags (origin); + f |= DDB_IS_SUBTRACK; + deadbeef->pl_set_item_flags (origin, f); + deadbeef->pl_items_copy_junk (origin, first, after); + deadbeef->pl_item_unref (first); + + deadbeef->pl_unlock (); + return after; +} + static DB_playItem_t * aac_insert (ddb_playlist_t *plt, DB_playItem_t *after, const char *fname) { trace ("adding %s\n", fname); @@ -985,79 +1093,97 @@ aac_insert (ddb_playlist_t *plt, DB_playItem_t *after, const char *fname) { mp4ff_t *mp4 = mp4ff_open_read (&cb); if (mp4) { int ntracks = mp4ff_total_tracks (mp4); + trace ("aac: numtracks=%d\n", ntracks); - // calculate number of aac subtracks - int num_aac_tracks = 0; - for (int i = 0; i < ntracks; i++) { - int res = mp4_track_get_info (mp4, i, &duration, &samplerate, &channels, &totalsamples); - if (res >= 0) { - num_aac_tracks++; - } - } +/// TODO: this is needed for multi-track files, but we disable this for now +/// // calculate number of aac subtracks +/// int num_aac_tracks = 0; +/// for (int i = 0; i < ntracks; i++) { +/// if (mp4ff_get_track_type (mp4, i) == TRACK_AUDIO) +/// num_aac_tracks++; // FIXME: will count alac too +/// } +/// } for (int i = 0; i < ntracks; i++) { + if (mp4ff_get_track_type (mp4, i) != TRACK_AUDIO) { + continue; + } int res = mp4_track_get_info (mp4, i, &duration, &samplerate, &channels, &totalsamples); - if (res >= 0) { + if (res >= 0 && duration > 0) { trace ("aac: found audio track %d\n", i); + int num_chapters; + aac_chapter_t *chapters = NULL; + if (mp4ff_chap_get_num_tracks(mp4) > 0) { + chapters = aac_load_itunes_chapters (mp4, &num_chapters, samplerate); + } + DB_playItem_t *it = deadbeef->pl_item_alloc_init (fname, plugin.plugin.id); deadbeef->pl_add_meta (it, ":FILETYPE", ftype); deadbeef->pl_set_meta_int (it, ":TRACKNUM", i); - if (num_aac_tracks > 1) { - deadbeef->pl_set_item_flags (it, DDB_IS_SUBTRACK); - } deadbeef->plt_set_item_duration (plt, it, duration); aac_load_tags (it, mp4); - if (num_aac_tracks == 1) { - int apeerr = deadbeef->junk_apev2_read (it, fp); - int v2err = deadbeef->junk_id3v2_read (it, fp); - int v1err = deadbeef->junk_id3v1_read (it, fp); - } + int apeerr = deadbeef->junk_apev2_read (it, fp); + int v2err = deadbeef->junk_id3v2_read (it, fp); + int v1err = deadbeef->junk_id3v1_read (it, fp); int64_t fsize = deadbeef->fgetlength (fp); - if (duration > 0) { - char s[100]; - snprintf (s, sizeof (s), "%lld", fsize); - deadbeef->pl_add_meta (it, ":FILE_SIZE", s); - deadbeef->pl_add_meta (it, ":BPS", "16"); - snprintf (s, sizeof (s), "%d", channels); - deadbeef->pl_add_meta (it, ":CHANNELS", s); - snprintf (s, sizeof (s), "%d", samplerate); - deadbeef->pl_add_meta (it, ":SAMPLERATE", s); - int br = (int)roundf(fsize / duration * 8 / 1000); - snprintf (s, sizeof (s), "%d", br); - deadbeef->pl_add_meta (it, ":BITRATE", s); - - // only attempt cuesheet for single-track files - if (num_aac_tracks == 1) { - // embedded cue - deadbeef->pl_lock (); - const char *cuesheet = deadbeef->pl_find_meta (it, "cuesheet"); - DB_playItem_t *cue = NULL; - - if (cuesheet) { - cue = deadbeef->plt_insert_cue_from_buffer (plt, after, it, cuesheet, strlen (cuesheet), totalsamples, samplerate); - if (cue) { - deadbeef->pl_item_unref (it); - deadbeef->pl_item_unref (cue); - deadbeef->pl_unlock (); - return cue; - } + char s[100]; + snprintf (s, sizeof (s), "%lld", fsize); + deadbeef->pl_add_meta (it, ":FILE_SIZE", s); + deadbeef->pl_add_meta (it, ":BPS", "16"); + snprintf (s, sizeof (s), "%d", channels); + deadbeef->pl_add_meta (it, ":CHANNELS", s); + snprintf (s, sizeof (s), "%d", samplerate); + deadbeef->pl_add_meta (it, ":SAMPLERATE", s); + int br = (int)roundf(fsize / duration * 8 / 1000); + snprintf (s, sizeof (s), "%d", br); + deadbeef->pl_add_meta (it, ":BITRATE", s); + + // embedded chapters + deadbeef->pl_lock (); // FIXME: is it needed? + if (chapters && num_chapters > 0) { + DB_playItem_t *cue = aac_insert_with_chapters (plt, after, it, chapters, num_chapters, totalsamples, samplerate); + for (int n = 0; n < num_chapters; n++) { + if (chapters[n].title) { + free (chapters[n].title); } + } + free (chapters); + if (cue) { + deadbeef->pl_item_unref (it); + deadbeef->pl_item_unref (cue); deadbeef->pl_unlock (); + return cue; + } + } - cue = deadbeef->plt_insert_cue (plt, after, it, totalsamples, samplerate); - if (cue) { - deadbeef->pl_item_unref (it); - deadbeef->pl_item_unref (cue); - return cue; - } + // embedded cue + const char *cuesheet = deadbeef->pl_find_meta (it, "cuesheet"); + DB_playItem_t *cue = NULL; + + if (cuesheet) { + cue = deadbeef->plt_insert_cue_from_buffer (plt, after, it, cuesheet, strlen (cuesheet), totalsamples, samplerate); + if (cue) { + deadbeef->pl_item_unref (it); + deadbeef->pl_item_unref (cue); + deadbeef->pl_unlock (); + return cue; } } + deadbeef->pl_unlock (); + + cue = deadbeef->plt_insert_cue (plt, after, it, totalsamples, samplerate); + if (cue) { + deadbeef->pl_item_unref (it); + deadbeef->pl_item_unref (cue); + return cue; + } after = deadbeef->plt_insert_item (plt, after, it); deadbeef->pl_item_unref (it); + break; } } ftype = "MP4 AAC"; diff --git a/plugins/libmp4ff/mp4atom.c b/plugins/libmp4ff/mp4atom.c index 823fa056..692b5a9a 100644 --- a/plugins/libmp4ff/mp4atom.c +++ b/plugins/libmp4ff/mp4atom.c @@ -42,6 +42,7 @@ # include <string.h> #endif #include "mp4ffint.h" +#include <stdio.h> #define COPYRIGHT_SYMBOL ((int8_t)0xA9) @@ -226,6 +227,18 @@ static uint8_t mp4ff_atom_name_to_type(const int8_t a, const int8_t b, return ATOM_PODCAST; else if (mp4ff_atom_compare(a,b,c,d, '-','-','-','-')) return ATOM_CUSTOM; + else if (mp4ff_atom_compare(a,b,c,d, 'c','h','p','l')) + return ATOM_CHPL; + else if (mp4ff_atom_compare(a,b,c,d, 'c','h','a','p')) + return ATOM_CHAP; + else if (mp4ff_atom_compare(a,b,c,d, 't','e','x','t')) + return ATOM_TEXT; + else if (mp4ff_atom_compare(a,b,c,d, 's','u','b','p')) + return ATOM_TEXT; + else if (mp4ff_atom_compare(a,b,c,d, 't','x','3','g')) + return ATOM_TEXT; + else if (mp4ff_atom_compare(a,b,c,d, 's','b','t','l')) + return ATOM_TEXT; else return ATOM_UNKNOWN; } @@ -251,7 +264,7 @@ uint64_t mp4ff_atom_read_header(mp4ff_t *f, uint8_t *atom_type, uint8_t *header_ size = mp4ff_read_int64(f); } - //printf("%c%c%c%c\n", atom_header[4], atom_header[5], atom_header[6], atom_header[7]); +// printf("%c%c%c%c\n", atom_header[4], atom_header[5], atom_header[6], atom_header[7]); *atom_type = mp4ff_atom_name_to_type(atom_header[4], atom_header[5], atom_header[6], atom_header[7]); @@ -403,6 +416,8 @@ static int32_t mp4ff_read_stsd(mp4ff_t *f) f->track[f->total_tracks - 1]->type = TRACK_VIDEO; } else if (atom_type == ATOM_MP4S) { f->track[f->total_tracks - 1]->type = TRACK_SYSTEM; + } else if (atom_type == ATOM_TEXT) { + f->track[f->total_tracks - 1]->type = TRACK_TEXT; } else { f->track[f->total_tracks - 1]->type = TRACK_UNKNOWN; } @@ -552,7 +567,6 @@ static int32_t mp4ff_read_mvhd(mp4ff_t *f) return 0; } -#if 0 static int32_t mp4ff_read_tkhd(mp4ff_t *f) { uint8_t version; @@ -563,21 +577,22 @@ static int32_t mp4ff_read_tkhd(mp4ff_t *f) { mp4ff_read_int64(f);//creation-time mp4ff_read_int64(f);//modification-time - mp4ff_read_int32(f);//track-id + f->track[f->total_tracks - 1]->id = mp4ff_read_int32(f);//track-id mp4ff_read_int32(f);//reserved - f->track[f->total_tracks - 1]->duration = mp4ff_read_int64(f);//duration +// f->track[f->total_tracks - 1]->duration = mp4ff_read_int64(f);//duration } else //version == 0 { mp4ff_read_int32(f);//creation-time mp4ff_read_int32(f);//modification-time - mp4ff_read_int32(f);//track-id + f->track[f->total_tracks - 1]->id = mp4ff_read_int32(f);//track-id mp4ff_read_int32(f);//reserved - f->track[f->total_tracks - 1]->duration = mp4ff_read_int32(f);//duration - if (f->track[f->total_tracks - 1]->duration == 0xFFFFFFFF) - f->track[f->total_tracks - 1]->duration = 0xFFFFFFFFFFFFFFFF; +// f->track[f->total_tracks - 1]->duration = mp4ff_read_int32(f);//duration +// if (f->track[f->total_tracks - 1]->duration == 0xFFFFFFFF) +// f->track[f->total_tracks - 1]->duration = 0xFFFFFFFFFFFFFFFF; } +#if 0 mp4ff_read_int32(f);//reserved mp4ff_read_int32(f);//reserved mp4ff_read_int16(f);//layer @@ -591,9 +606,9 @@ static int32_t mp4ff_read_tkhd(mp4ff_t *f) mp4ff_read_int32(f); mp4ff_read_int32(f); mp4ff_read_int32(f); mp4ff_read_int32(f);//width mp4ff_read_int32(f);//height +#endif return 1; } -#endif static int32_t mp4ff_read_mdhd(mp4ff_t *f) { @@ -649,6 +664,129 @@ static int32_t mp4ff_read_meta(mp4ff_t *f, const uint64_t size) } #endif +static int32_t mp4ff_read_chpl(mp4ff_t *f, const uint64_t size) +{ + int i; + int i_read = size; + + mp4ff_read_char(f); /* version */ + mp4ff_read_int24(f); /* flags */ + + mp4ff_chapterdata_t *p_chpl = &f->chapters; + + p_chpl->i_chapter = mp4ff_read_char (f); + i_read -= 5; + + for( i = 0; i < p_chpl->i_chapter; i++ ) + { + uint64_t i_start; + uint8_t i_len; + int i_copy; + i_start = mp4ff_read_int64 (f); + i_read -= 8; + i_len = mp4ff_read_char (f); + i_read -= 1; + + p_chpl->chapter[i].psz_name = malloc( i_len + 1 ); + if( !p_chpl->chapter[i].psz_name ) + goto error; + + i_copy = i_len < i_read ? i_len : i_read; + if( i_copy > 0 ) + mp4ff_read_data (f, p_chpl->chapter[i].psz_name, i_copy); + p_chpl->chapter[i].psz_name[i_copy] = '\0'; + p_chpl->chapter[i].i_start = i_start; + + i_read -= i_copy; + } + /* Bubble sort by increasing start date */ + do + { + for( i = 0; i < p_chpl->i_chapter - 1; i++ ) + { + if( p_chpl->chapter[i].i_start > p_chpl->chapter[i+1].i_start ) + { + char *psz = p_chpl->chapter[i+1].psz_name; + int64_t i64 = p_chpl->chapter[i+1].i_start; + + p_chpl->chapter[i+1].psz_name = p_chpl->chapter[i].psz_name; + p_chpl->chapter[i+1].i_start = p_chpl->chapter[i].i_start; + + p_chpl->chapter[i].psz_name = psz; + p_chpl->chapter[i].i_start = i64; + + i = -1; + break; + } + } + } while( i == -1 ); + + return 0; + +error: + return -1; +} + +int32_t mp4ff_chapters_get_num_items (mp4ff_t *f) +{ + return f->chapters.i_chapter; +} + +const char *mp4ff_chapters_get_item (mp4ff_t *f, int i) +{ + return f->chapters.chapter[i].psz_name; +} + +void mp4ff_chapters_free (mp4ff_t *f) +{ + int i; + for (i = 0; i < f->chapters.i_chapter; i++) + { + free (f->chapters.chapter[i].psz_name); + f->chapters.chapter[i].psz_name = NULL; + } +} + +static int32_t mp4ff_read_tref(mp4ff_t *f, const uint64_t size) +{ + int i; + + mp4ff_trefdata_t *p_tref = &f->tref; + + p_tref->i_track_ID = NULL; + p_tref->i_entry_count = (size-8)/ sizeof(uint32_t); + if( p_tref->i_entry_count > 0 ) { + p_tref->i_track_ID = calloc( p_tref->i_entry_count, sizeof(uint32_t) ); + } + if( p_tref->i_track_ID == NULL ) + return -1; + + for( i = 0; i < p_tref->i_entry_count; i++ ) + { + p_tref->i_track_ID[i] = mp4ff_read_int32 (f); + } + + return 0; +} + +void mp4ff_tref_free (mp4ff_t *f) +{ + if (f->tref.i_track_ID) { + free (f->tref.i_track_ID); + f->tref.i_track_ID = NULL; + } +} + +int32_t mp4ff_chap_get_num_tracks (mp4ff_t *f) +{ + return f->tref.i_entry_count; +} + +int32_t mp4ff_chap_get_track_id (mp4ff_t *f, int t) +{ + return f->tref.i_track_ID[t]; +} + int32_t mp4ff_atom_read(mp4ff_t *f, const int32_t size, const uint8_t atom_type) { uint64_t dest_position = mp4ff_position(f)+size-8; @@ -682,6 +820,12 @@ int32_t mp4ff_atom_read(mp4ff_t *f, const int32_t size, const uint8_t atom_type) /* iTunes Metadata box */ mp4ff_read_meta(f, size); #endif + } else if (atom_type == ATOM_CHPL) { + mp4ff_read_chpl(f, size); + } else if (atom_type == ATOM_CHAP) { + mp4ff_read_tref(f, size); + } else if (atom_type == ATOM_TKHD) { + mp4ff_read_tkhd(f); } mp4ff_set_position(f, dest_position); diff --git a/plugins/libmp4ff/mp4ff.c b/plugins/libmp4ff/mp4ff.c index ce33aad5..311746f2 100644 --- a/plugins/libmp4ff/mp4ff.c +++ b/plugins/libmp4ff/mp4ff.c @@ -99,6 +99,9 @@ void mp4ff_close(mp4ff_t *ff) mp4ff_tag_delete(&(ff->tags)); #endif + mp4ff_chapters_free (ff); + mp4ff_tref_free (ff); + if (ff) free(ff); } @@ -255,6 +258,11 @@ int32_t mp4ff_get_track_type(const mp4ff_t *f, const int track) return f->track[track]->type; } +int32_t mp4ff_get_track_id(const mp4ff_t *f, const int track) +{ + return f->track[track]->id; +} + int32_t mp4ff_total_tracks(const mp4ff_t *f) { return f->total_tracks; @@ -428,7 +436,7 @@ int32_t mp4ff_read_sample(mp4ff_t *f, const int32_t track, const int32_t sample, *audio_buffer = (uint8_t*)malloc(*bytes); if (!(*audio_buffer)) { - fprintf (stderr, "mp4ff_read_sample: malloc failure (tried to alloc %d bytes). possible mp4ff bug or memleak! please report a bug to deadbeef developers (i'm serious).\n", *bytes); + //fprintf (stderr, "mp4ff_read_sample: malloc failure (tried to alloc %d bytes). possible mp4ff bug or memleak! please report a bug to deadbeef developers (i'm serious).\n", *bytes); return 0; } diff --git a/plugins/libmp4ff/mp4ff.h b/plugins/libmp4ff/mp4ff.h index f8828f4f..0b2ac985 100644 --- a/plugins/libmp4ff/mp4ff.h +++ b/plugins/libmp4ff/mp4ff.h @@ -40,20 +40,7 @@ extern "C" { #else #include "mp4ff_int_types.h" #endif - -/* file callback structure */ -typedef struct -{ - uint32_t (*read)(void *user_data, void *buffer, uint32_t length); - uint32_t (*write)(void *udata, void *buffer, uint32_t length); - uint32_t (*seek)(void *user_data, uint64_t position); - uint32_t (*truncate)(void *user_data); - void *user_data; -} mp4ff_callback_t; - -/* mp4 main file structure */ -typedef void* mp4ff_t; - +#include "mp4ffint.h" /* API */ @@ -78,6 +65,9 @@ int32_t mp4ff_read_sample_getsize(mp4ff_t *f, const int track, const int sample) int32_t mp4ff_get_decoder_config(const mp4ff_t *f, const int track, unsigned char** ppBuf, unsigned int* pBufSize); int32_t mp4ff_get_track_type(const mp4ff_t *f, const int track); +int32_t mp4ff_get_track_id(const mp4ff_t *f, const int track); +int32_t mp4ff_get_track_fmt_cat(const mp4ff_t *f, const int track); +int32_t mp4ff_get_track_fmt_codec(const mp4ff_t *f, const int track); int32_t mp4ff_total_tracks(const mp4ff_t *f); int32_t mp4ff_num_samples(const mp4ff_t *f, const int track); int32_t mp4ff_time_scale(const mp4ff_t *f, const int track); @@ -113,19 +103,10 @@ int mp4ff_meta_get_tempo(const mp4ff_t *f, char **value); int32_t mp4ff_meta_get_coverart(const mp4ff_t *f, char **value); #ifdef USE_TAGGING -/* metadata tag structure */ -typedef struct -{ - char *item; - char *value; -} mp4ff_tag_t; - -/* metadata list structure */ -typedef struct -{ - mp4ff_tag_t *tags; - uint32_t count; -} mp4ff_metadata_t; +int32_t mp4ff_chapters_get_num_items (mp4ff_t *f); +const char *mp4ff_chapters_get_item (mp4ff_t *f, int i); +int32_t mp4ff_chap_get_num_tracks (mp4ff_t *f); +int32_t mp4ff_chap_get_track_id (mp4ff_t *f, int t); int32_t mp4ff_meta_update(mp4ff_callback_t *f,const mp4ff_metadata_t * data); diff --git a/plugins/libmp4ff/mp4ffint.h b/plugins/libmp4ff/mp4ffint.h index ec0b51bb..bfe157a6 100644 --- a/plugins/libmp4ff/mp4ffint.h +++ b/plugins/libmp4ff/mp4ffint.h @@ -43,7 +43,9 @@ extern "C" { #define TRACK_AUDIO 1 #define TRACK_VIDEO 2 #define TRACK_SYSTEM 3 +#define TRACK_TEXT 4 +#define ATOM_TREF 100 #define SUBATOMIC 128 @@ -52,7 +54,6 @@ extern "C" { #define ATOM_MDAT 130 #define ATOM_MVHD 131 #define ATOM_TKHD 132 -#define ATOM_TREF 133 #define ATOM_MDHD 134 #define ATOM_VMHD 135 #define ATOM_SMHD 136 @@ -76,6 +77,7 @@ extern "C" { #define ATOM_PRIV 154 #define ATOM_USER 155 #define ATOM_KEY 156 +#define ATOM_TEXT 157 #define ATOM_ALBUM_ARTIST 157 #define ATOM_CONTENTGROUP 158 @@ -94,6 +96,8 @@ extern "C" { #define ATOM_EPISODE 171 #define ATOM_PODCAST 172 #define ATOM_CUSTOM 173 +#define ATOM_CHPL 174 +#define ATOM_CHAP 175 #define ATOM_UNKNOWN 255 #define ATOM_FREE ATOM_UNKNOWN @@ -166,6 +170,7 @@ typedef struct typedef struct { int32_t type; + int32_t id; int32_t channelCount; int32_t sampleSize; uint16_t sampleRate; @@ -211,6 +216,22 @@ typedef struct } mp4ff_track_t; +typedef struct +{ + uint8_t i_chapter; + struct + { + char *psz_name; + int64_t i_start; + } chapter[256]; +} mp4ff_chapterdata_t; + +typedef struct +{ + uint32_t i_entry_count; + uint32_t *i_track_ID; +} mp4ff_trefdata_t; + /* mp4 main file structure */ typedef struct { @@ -236,6 +257,10 @@ typedef struct /* metadata */ mp4ff_metadata_t tags; + + /* chapters */ + mp4ff_chapterdata_t chapters; + mp4ff_trefdata_t tref; } mp4ff_t; @@ -337,6 +362,8 @@ int32_t mp4ff_num_samples(const mp4ff_t *f, const int32_t track); uint32_t mp4ff_meta_genre_to_index(const char * genrestr);//returns 1-based index, 0 if not found const char * mp4ff_meta_index_to_genre(uint32_t idx);//returns pointer to static string +void mp4ff_chapters_free (mp4ff_t *f); +void mp4ff_tref_free (mp4ff_t *f); #ifdef __cplusplus } |