summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTorsten Jager <t.jager@gmx.de>2014-01-22 15:31:50 +0100
committerTorsten Jager <t.jager@gmx.de>2014-01-22 15:31:50 +0100
commit458f02dbd3f76749e061fbb780c3e74d161db88c (patch)
tree4c109f43fbad205dfa7203b21a4c6b4a5565aa28
parent463819ab7ee6bf65fe440b537118bc9ed718ac84 (diff)
downloadxine-lib-458f02dbd3f76749e061fbb780c3e74d161db88c.tar.gz
xine-lib-458f02dbd3f76749e061fbb780c3e74d161db88c.tar.bz2
demux_qt: add fragment support.
Follow ISO 14496-12 (2005).
-rw-r--r--src/demuxers/demux_qt.c316
1 files changed, 314 insertions, 2 deletions
diff --git a/src/demuxers/demux_qt.c b/src/demuxers/demux_qt.c
index f9aace0da..9b5b50dd4 100644
--- a/src/demuxers/demux_qt.c
+++ b/src/demuxers/demux_qt.c
@@ -133,6 +133,16 @@ typedef unsigned int qt_atom;
#define RMVC_ATOM QT_ATOM('r', 'm', 'v', 'c')
#define QTIM_ATOM QT_ATOM('q', 't', 'i', 'm')
+/* fragment stuff */
+#define MVEX_ATOM QT_ATOM('m', 'v', 'e', 'x')
+#define MEHD_ATOM QT_ATOM('m', 'e', 'h', 'd')
+#define TREX_ATOM QT_ATOM('t', 'r', 'e', 'x')
+#define MOOF_ATOM QT_ATOM('m', 'o', 'o', 'f')
+#define MFHD_ATOM QT_ATOM('m', 'v', 'h', 'd')
+#define TRAF_ATOM QT_ATOM('t', 'r', 'a', 'f')
+#define TFHD_ATOM QT_ATOM('t', 'f', 'h', 'd')
+#define TRUN_ATOM QT_ATOM('t', 'r', 'u', 'n')
+
/* placeholder for cutting and pasting
#define _ATOM QT_ATOM('', '', '', '')
*/
@@ -260,6 +270,7 @@ typedef struct {
/* trak description */
media_type type;
+ int id;
/* one or more properties atoms for this trak */
properties_t *stsd_atoms;
@@ -323,6 +334,15 @@ typedef struct {
int lang;
+ /* fragment defaults */
+ int default_sample_description_index;
+ int default_sample_duration;
+ int default_sample_size;
+ int default_sample_flags;
+ /* fragment seamless dts */
+ int64_t fragment_dts;
+ /* fragment frame array size */
+ int fragment_frames;
} qt_trak;
typedef struct {
@@ -347,6 +367,9 @@ typedef struct {
int audio_trak;
int seek_flag; /* this is set to indicate that a seek has just occurred */
+ /* fragment mode */
+ int fragment_count;
+
char *artist;
char *name;
char *album;
@@ -920,6 +943,7 @@ static qt_error parse_trak_atom (qt_trak *trak,
qt_error last_error = QT_OK;
/* initialize trak structure */
+ trak->id = -1;
trak->edit_list_count = 0;
trak->edit_list_table = NULL;
trak->chunk_offset_count = 0;
@@ -974,6 +998,10 @@ static qt_error parse_trak_atom (qt_trak *trak,
switch(current_atom) {
case TKHD_ATOM:
+ if (_X_BE_16 (trak_atom + i + 4) == 1)
+ trak->id = _X_BE_32 (trak_atom + i + 24);
+ else
+ trak->id = _X_BE_32 (trak_atom + i + 16);
trak->flags = _X_BE_16(&trak_atom[i + 6]);
break;
@@ -2133,6 +2161,269 @@ static qt_error build_frame_table(qt_trak *trak,
return QT_OK;
}
+/************************************************************************
+* Fragment stuff *
+************************************************************************/
+
+static qt_trak *find_trak_by_id (qt_info *info, int id) {
+ int i;
+
+ for (i = 0; i < info->trak_count; i++) {
+ if (info->traks[i].id == id)
+ return &(info->traks[i]);
+ }
+ return NULL;
+}
+
+static int parse_mvex_atom (qt_info *info, unsigned char *mvex_atom, int bufsize) {
+ int i, j, mvex_size;
+ uint32_t traknum = 0, subtype, subsize = 0;
+ qt_trak *trak;
+
+ /* limit to atom size */
+ if (bufsize < 8)
+ return 0;
+ mvex_size = _X_BE_32 (mvex_atom);
+ if (bufsize < mvex_size)
+ mvex_size = bufsize;
+ /* scan subatoms */
+ for (i = 8; i + 8 <= mvex_size; i += subsize) {
+ subsize = _X_BE_32 (&mvex_atom[i]);
+ subtype = _X_BE_32 (&mvex_atom[i + 4]);
+ if (i + subsize > mvex_size)
+ break;
+ switch (subtype) {
+ case MEHD_ATOM:
+ break;
+ case TREX_ATOM:
+ if (subsize < 8 + 24)
+ break;
+ traknum = _X_BE_32 (&mvex_atom[i + 8 + 4]);
+ trak = find_trak_by_id (info, traknum);
+ if (!trak)
+ break;
+ trak->default_sample_description_index = _X_BE_32 (&mvex_atom[i + 8 + 8]);
+ trak->default_sample_duration = _X_BE_32 (&mvex_atom[i + 8 + 12]);
+ trak->default_sample_size = _X_BE_32 (&mvex_atom[i + 8 + 16]);
+ trak->default_sample_flags = _X_BE_32 (&mvex_atom[i + 8 + 20]);
+ j = trak->frame_count;
+ trak->fragment_dts = (j >= 2) ? 2 * trak->frames[j - 1].pts - trak->frames[j - 2].pts : 0;
+ trak->fragment_frames = trak->frame_count;
+ info->fragment_count = -1;
+ break;
+ default: ;
+ }
+ }
+
+ return 1;
+}
+
+static int parse_traf_atom (qt_info *info, unsigned char *traf_atom, int trafsize, off_t moofpos) {
+ int i, n, done = 0, samples;
+ uint32_t subtype, subsize = 0, tfhd_flags, trun_flags;
+ uint32_t sample_description_index;
+ uint32_t default_sample_duration, sample_duration;
+ uint32_t default_sample_size, sample_size;
+ uint32_t default_sample_flags, first_sample_flags, sample_flags;
+ off_t base_data_offset, data_pos;
+ int64_t sample_dts;
+ unsigned char *p;
+ qt_trak *trak = NULL;
+ qt_frame *frame;
+
+ for (i = 8; i + 8 <= trafsize; i += subsize) {
+ subsize = _X_BE_32 (&traf_atom[i]);
+ subtype = _X_BE_32 (&traf_atom[i + 4]);
+ if (i + subsize > trafsize)
+ break;
+ switch (subtype) {
+
+ case TFHD_ATOM:
+ p = traf_atom + i + 8;
+ tfhd_flags = _X_BE_32 (p); p += 4;
+ trak = find_trak_by_id (info, _X_BE_32 (p)); p += 4;
+ if (!trak)
+ break;
+ if (tfhd_flags & 1)
+ base_data_offset = _X_BE_64 (p), p += 8;
+ else
+ base_data_offset = moofpos;
+ data_pos = base_data_offset;
+ if (tfhd_flags & 2)
+ sample_description_index = _X_BE_32 (p), p += 4;
+ else
+ sample_description_index = trak->default_sample_description_index;
+ if (tfhd_flags & 8)
+ default_sample_duration = _X_BE_32 (p), p += 4;
+ else
+ default_sample_duration = trak->default_sample_duration;
+ if (tfhd_flags & 0x10)
+ default_sample_size = _X_BE_32 (p), p += 4;
+ else
+ default_sample_size = trak->default_sample_size;
+ if (tfhd_flags & 0x20)
+ default_sample_flags = _X_BE_32 (p), p += 4;
+ else
+ default_sample_flags = trak->default_sample_flags;
+ break;
+
+ case TRUN_ATOM:
+ /* get head */
+ if (!trak)
+ break;
+ p = traf_atom + i + 8;
+ trun_flags = _X_BE_32 (p); p += 4;
+ samples = _X_BE_32 (p); p += 4;
+ if (trun_flags & 1) {
+ uint32_t o = _X_BE_32 (p);
+ p += 4;
+ data_pos = base_data_offset + (off_t)((int32_t)o);
+ }
+ if (trun_flags & 4)
+ first_sample_flags = _X_BE_32 (p), p += 4;
+ else
+ first_sample_flags = default_sample_flags;
+ /* truncation paranoia */
+ n = 0;
+ if (trun_flags & 0x100) n += 4;
+ if (trun_flags & 0x200) n += 4;
+ if (trun_flags & 0x400) n += 4;
+ if (trun_flags & 0x800) n += 4;
+ if (n) {
+ n = (i + subsize - (p - traf_atom)) / n;
+ if (samples > n) samples = n;
+ }
+ if (!samples)
+ break;
+ /* enlarge frame table in steps of 64k frames, to avoid a flood of reallocations */
+ frame = trak->frames;
+ n = trak->frame_count + samples;
+ if (n > trak->fragment_frames) {
+ n = (n + 0xffff) & ~0xffff;
+ frame = realloc (trak->frames, n * sizeof (*frame));
+ if (!frame)
+ break;
+ trak->fragment_frames = n;
+ trak->frames = frame;
+ }
+ /* get defaults */
+ frame += trak->frame_count;
+ sample_dts = trak->fragment_dts;
+ sample_duration = default_sample_duration;
+ sample_size = default_sample_size;
+ sample_flags = first_sample_flags;
+ /* add frames */
+ while (samples--) {
+ frame->media_id = trak->id;
+ frame->pts = sample_dts;
+ if (trun_flags & 0x100)
+ sample_duration = _X_BE_32 (p), p += 4;
+ sample_dts += 90000 * sample_duration / trak->timescale;
+ frame->offset = data_pos;
+ if (trun_flags & 0x200)
+ sample_size = _X_BE_32 (p), p += 4;
+ frame->size = sample_size;
+ data_pos += sample_size;
+ if (trun_flags & 0x400)
+ sample_flags = _X_BE_32 (p), p += 4;
+ frame->keyframe = !(sample_flags & 0x10000);
+ sample_flags = default_sample_flags;
+ if (trun_flags & 0x800) {
+ uint32_t o = _X_BE_32 (p);
+ p += 4;
+ frame->ptsoffs = (int32_t)90000 * (int32_t)o / (int32_t)trak->timescale;
+ } else
+ frame->ptsoffs = 0;
+ frame++;
+ (trak->frame_count)++;
+ }
+ trak->fragment_dts = sample_dts;
+ done++;
+ break;
+
+ default: ;
+ }
+ }
+ return done;
+}
+
+static int parse_moof_atom (qt_info *info, unsigned char *moof_atom, int moofsize, off_t moofpos) {
+ int i, subtype, subsize = 0, done = 0;
+
+ for (i = 8; i + 8 <= moofsize; i += subsize) {
+ subsize = _X_BE_32 (&moof_atom[i]);
+ subtype = _X_BE_32 (&moof_atom[i + 4]);
+ if (i + subsize > moofsize)
+ break;
+ switch (subtype) {
+ case MFHD_ATOM:
+ /* TODO: check sequence # here */
+ break;
+ case TRAF_ATOM:
+ if (parse_traf_atom (info, &moof_atom[i], subsize, moofpos))
+ done++;
+ break;
+ default: ;
+ }
+ }
+ return done;
+}
+
+static int fragment_scan (qt_info *info, input_plugin_t *input) {
+ unsigned char *buf;
+ off_t pos, fsize, atomsize = 0, bufsize = 16;
+ int frags = 0, atomtype;
+
+ /* prerequisites */
+ if (info->fragment_count != -1)
+ return 0;
+ if (!INPUT_IS_SEEKABLE (input))
+ return 0;
+ fsize = input->get_length (input);
+ if (fsize <= 0)
+ return 0;
+ buf = malloc (bufsize);
+ if (!buf)
+ return 0;
+
+ for (pos = 0; pos < fsize; pos += atomsize) {
+ input->seek (input, pos, SEEK_SET);
+ if (input->read (input, buf, 16) != 16)
+ break;
+ atomsize = _X_BE_32 (buf);
+ atomtype = _X_BE_32 (&buf[4]);
+ if (atomsize == 1)
+ atomsize = _X_BE_64 (&buf[8]);
+ if (atomtype == MOOF_ATOM) {
+ if (atomsize > (80 << 20))
+ break;
+ if (atomsize > bufsize) {
+ unsigned char *b2;
+ bufsize = atomsize + (atomsize >> 1);
+ b2 = realloc (buf, bufsize);
+ if (b2)
+ buf = b2;
+ else
+ break;
+ }
+ if (atomsize > 16) {
+ if (input->read (input, buf + 16, atomsize - 16) != atomsize - 16)
+ break;
+ }
+ if (parse_moof_atom (info, buf, atomsize, pos))
+ frags++;
+ }
+ }
+
+ info->fragment_count = frags;
+ free (buf);
+ return frags;
+}
+
+/************************************************************************
+* /Fragment stuff *
+************************************************************************/
+
/*
* This function takes a pointer to a qt_info structure and a pointer to
* a buffer containing an uncompressed moov atom. When the function
@@ -2140,13 +2431,17 @@ static qt_error build_frame_table(qt_trak *trak,
* ordered by offset.
*/
static void parse_moov_atom(qt_info *info, unsigned char *moov_atom,
- int64_t bandwidth) {
+ int64_t bandwidth, input_plugin_t *input) {
int i, j;
unsigned int moov_atom_size = _X_BE_32(&moov_atom[0]);
int string_size, error;
unsigned int max_video_frames = 0;
unsigned int max_audio_frames = 0;
+ /* must parse mvex _after_ building traks */
+ unsigned char *mvex_atom = NULL;
+ int mvex_size = 0;
+
/* make sure this is actually a moov atom (will also accept 'free' as
* a special case) */
if ((_X_BE_32(&moov_atom[4]) != MOOV_ATOM) &&
@@ -2236,6 +2531,11 @@ static void parse_moov_atom(qt_info *info, unsigned char *moov_atom,
}
break;
+ case MVEX_ATOM:
+ mvex_atom = &moov_atom[i - 4];
+ mvex_size = moov_atom_size - i + 4;
+ break;
+
default:
debug_atom_load(" qt: unknown atom into the moov atom (0x%08X)\n", current_atom);
}
@@ -2254,7 +2554,15 @@ static void parse_moov_atom(qt_info *info, unsigned char *moov_atom,
info->last_error = error;
return;
}
+ }
+
+ if (mvex_atom) {
+ parse_mvex_atom (info, mvex_atom, mvex_size);
+ /* reassemble fragments, if any */
+ fragment_scan (info, input);
+ }
+ for (i = 0; i < info->trak_count; i++) {
/* dump the frame table in debug mode */
for (j = 0; j < info->traks[i].frame_count; j++)
debug_frame_table(" %d: %8X bytes @ %"PRIX64", %"PRId64" pts, media id %d%s\n",
@@ -2442,7 +2750,7 @@ static qt_error open_qt_file(qt_info *info, input_plugin_t *input,
dump_moov_atom(moov_atom, moov_atom_size);
/* take apart the moov atom */
- parse_moov_atom(info, moov_atom, bandwidth);
+ parse_moov_atom(info, moov_atom, bandwidth, input);
if (info->last_error != QT_OK) {
free(moov_atom);
return info->last_error;
@@ -3310,6 +3618,10 @@ static demux_plugin_t *open_plugin (demux_class_t *class_gen, xine_stream_t *str
return NULL;
}
+ if (this->qt->fragment_count > 0)
+ xprintf (this->stream->xine, XINE_VERBOSITY_DEBUG,
+ _("demux_qt: added %d fragments\n"), this->qt->fragment_count);
+
strncpy (this->last_mrl, input->get_mrl (input), 1024);
return &this->demux_plugin;