summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar waker <wakeroid@gmail.com>2012-08-22 22:27:36 +0200
committerGravatar waker <wakeroid@gmail.com>2012-08-24 22:12:20 +0200
commitf3f1983b3156a97b8e2f492dd9e1db31cd2e4817 (patch)
tree4bda37fa9865d0e223cd8e8d33d5052c82ec3127
parent84c9ebfa0f8eef41948929fb85739727bab06b05 (diff)
aac: added chapters support
-rw-r--r--plugins/aac/aac.c286
-rw-r--r--plugins/libmp4ff/mp4atom.c162
-rw-r--r--plugins/libmp4ff/mp4ff.c10
-rw-r--r--plugins/libmp4ff/mp4ff.h35
-rw-r--r--plugins/libmp4ff/mp4ffint.h29
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
}