summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/demuxers/demux_qt.c1518
1 files changed, 762 insertions, 756 deletions
diff --git a/src/demuxers/demux_qt.c b/src/demuxers/demux_qt.c
index 53ab84190..afed8f15c 100644
--- a/src/demuxers/demux_qt.c
+++ b/src/demuxers/demux_qt.c
@@ -30,7 +30,7 @@
* build_frame_table
* free_qt_info
*
- * $Id: demux_qt.c,v 1.143 2003/02/03 02:10:53 miguelfreitas Exp $
+ * $Id: demux_qt.c,v 1.144 2003/02/16 05:10:23 tmmm Exp $
*
*/
@@ -113,6 +113,8 @@ typedef unsigned int qt_atom;
#define ATOM_PREAMBLE_SIZE 8
#define PALETTE_COUNT 256
+#define MAX_PTS_DIFF 100000
+
/* these are things that can go wrong */
typedef enum {
QT_OK,
@@ -135,12 +137,10 @@ typedef enum {
} media_type;
typedef struct {
- media_type type;
int64_t offset;
unsigned int size;
int64_t pts;
int keyframe;
- unsigned int official_byte_count;
} qt_frame;
typedef struct {
@@ -165,25 +165,57 @@ typedef struct {
union {
struct {
- unsigned int codec_format;
+ unsigned int codec_fourcc;
+ unsigned int codec_buftype;
unsigned int width;
unsigned int height;
int palette_count;
palette_entry_t palette[PALETTE_COUNT];
int depth;
+ int edit_list_compensation; /* special trick for edit lists */
} video;
struct {
- unsigned int codec_format;
+ unsigned int codec_fourcc;
+ unsigned int codec_buftype;
unsigned int sample_rate;
unsigned int channels;
unsigned int bits;
unsigned int vbr;
unsigned int wave_present;
xine_waveformatex wave;
+
+ /* special audio parameters */
+ unsigned int samples_per_packet;
+ unsigned int bytes_per_packet;
+ unsigned int bytes_per_frame;
+ unsigned int bytes_per_sample;
+ unsigned int samples_per_frame;
} audio;
- } media_description;
+ } properties;
+
+ /* internal frame table corresponding to this trak */
+ qt_frame *frames;
+ unsigned int frame_count;
+ unsigned int current_frame;
+
+ /* trak timescale */
+ unsigned int timescale;
+
+ /* flags that indicate how a trak is supposed to be used */
+ unsigned int flags;
+
+ /* decoder data pass information to the AAC decoder */
+ void *decoder_config;
+ int decoder_config_len;
+
+ /* verbatim copy of the stsd atom */
+ int stsd_size;
+ void *stsd;
+
+ /****************************************/
+ /* temporary tables for loading a chunk */
/* edit list table */
unsigned int edit_list_count;
@@ -210,32 +242,7 @@ typedef struct {
unsigned int time_to_sample_count;
time_to_sample_table_t *time_to_sample_table;
- /* temporary frame table corresponding to this sample table */
- qt_frame *frames;
- unsigned int frame_count;
-
- /* trak timescale */
- unsigned int timescale;
-
- /* flags that indicate how a trak is supposed to be used */
- unsigned int flags;
-
- /* decoder data pass information to the AAC decoder */
- void *decoder_config;
- int decoder_config_len;
-
- /* special audio parameters */
- unsigned int samples_per_packet;
- unsigned int bytes_per_packet;
- unsigned int bytes_per_frame;
- unsigned int bytes_per_sample;
- unsigned int samples_per_frame;
-
- /* verbatim copy of the stsd atom */
- int stsd_size;
- void *stsd;
-
-} qt_sample_table;
+} qt_trak;
typedef struct {
FILE *qt_file;
@@ -247,41 +254,18 @@ typedef struct {
unsigned int duration;
int64_t moov_first_offset;
- int64_t moov_last_offset;
-
- qt_atom audio_codec;
- unsigned int audio_type;
- unsigned int audio_sample_rate;
- unsigned int audio_channels;
- unsigned int audio_bits;
- unsigned int audio_vbr; /* flag to indicate if audio is VBR */
- void *audio_decoder_config;
- int audio_decoder_config_len;
- int wave_present;
- xine_waveformatex wave;
- int audio_stsd_size;
- void *audio_stsd;
-
- qt_atom video_codec;
- unsigned int video_type;
- unsigned int video_width;
- unsigned int video_height;
- unsigned int video_depth;
- void *video_decoder_config;
- int video_decoder_config_len;
- int video_stsd_size;
- void *video_stsd;
+ int trak_count;
+ qt_trak *traks;
- qt_frame *frames;
- unsigned int frame_count;
-
- int palette_count;
- palette_entry_t palette[PALETTE_COUNT];
+ /* the trak numbers that won their respective frame count competitions */
+ int video_trak;
+ int audio_trak;
+ int seek_flag; /* this is set to indicate that a seek has just occurred */
- char *copyright;
- char *description;
- char *comment;
+ char *copyright;
+ char *description;
+ char *comment;
qt_error last_error;
} qt_info;
@@ -301,10 +285,6 @@ typedef struct {
int status;
- /* when this flag is set, demuxer only dispatches audio samples until it
- * encounters a video keyframe, then it starts sending every frame again */
- int waiting_for_keyframe;
-
qt_info *qt;
xine_bmiheader bih;
unsigned int current_frame;
@@ -395,7 +375,7 @@ static void hexdump (char *buf, int length) {
for (i = 0; i < length; i++) {
unsigned char c = buf[i];
- if ((c >= 32) && (c <= 128))
+ if ((c >= 32) && (c < 128))
printf ("%c", c);
else
printf (".");
@@ -418,7 +398,7 @@ static void hexdump (char *buf, int length) {
printf ("\n");
}
-void dump_moov_atom(unsigned char *moov_atom, int moov_atom_size) {
+static inline void dump_moov_atom(unsigned char *moov_atom, int moov_atom_size) {
#if DEBUG_DUMP_MOOV
FILE *f;
@@ -540,7 +520,6 @@ qt_info *create_qt_info(void) {
if (!info)
return NULL;
- info->frames = NULL;
info->qt_file = NULL;
info->compressed_header = 0;
@@ -549,29 +528,11 @@ qt_info *create_qt_info(void) {
info->timescale = 0;
info->duration = 0;
- info->audio_codec = 0;
- info->audio_type = 0;
- info->audio_sample_rate = 0;
- info->audio_channels = 0;
- info->audio_bits = 0;
- info->audio_vbr = 0;
- info->audio_decoder_config = NULL;
- info->audio_decoder_config_len = 0;
- info->audio_stsd = NULL;
-
- info->video_codec = 0;
- info->video_type = 0;
- info->video_width = 0;
- info->video_height = 0;
- info->video_depth = 0;
- info->video_decoder_config = NULL;
- info->video_decoder_config_len = 0;
- info->video_stsd = NULL;
-
- info->frames = NULL;
- info->frame_count = 0;
-
- info->palette_count = 0;
+ info->trak_count = 0;
+ info->traks = NULL;
+
+ info->video_trak = -1;
+ info->audio_trak = -1;
info->copyright = NULL;
info->description = NULL;
@@ -586,14 +547,8 @@ qt_info *create_qt_info(void) {
void free_qt_info(qt_info *info) {
if(info) {
- if(info->frames)
- free(info->frames);
- if(info->audio_decoder_config)
- free(info->audio_decoder_config);
- if(info->video_decoder_config)
- free(info->video_decoder_config);
- free(info->audio_stsd);
- free(info->video_stsd);
+ if(info->traks)
+ free(info->traks);
free(info->copyright);
free(info->description);
free(info->comment);
@@ -658,9 +613,9 @@ static int mp4_read_descr_len(unsigned char *s, uint32_t *length) {
/*
* This function traverses through a trak atom searching for the sample
- * table atoms, which it loads into an internal sample table structure.
+ * table atoms, which it loads into an internal trak structure.
*/
-static qt_error parse_trak_atom (qt_sample_table *sample_table,
+static qt_error parse_trak_atom (qt_trak *trak,
unsigned char *trak_atom) {
int i, j;
@@ -681,52 +636,44 @@ static qt_error parse_trak_atom (qt_sample_table *sample_table,
unsigned char *color_table;
/* initialize sample table structure */
- sample_table->edit_list_count = 0;
- sample_table->edit_list_table = NULL;
- sample_table->chunk_offset_count = 0;
- sample_table->chunk_offset_table = NULL;
- sample_table->sample_size = 0;
- sample_table->sample_size_count = 0;
- sample_table->sample_size_table = NULL;
- sample_table->sync_sample_table = 0;
- sample_table->sync_sample_table = NULL;
- sample_table->sample_to_chunk_count = 0;
- sample_table->sample_to_chunk_table = NULL;
- sample_table->time_to_sample_count = 0;
- sample_table->time_to_sample_table = NULL;
- sample_table->frames = NULL;
- sample_table->decoder_config = NULL;
- sample_table->decoder_config_len = 0;
- sample_table->stsd = NULL;
- memset(&sample_table->media_description, 0,
- sizeof(sample_table->media_description));
-
- /* special audio parameters */
- sample_table->samples_per_packet = 0;
- sample_table->bytes_per_packet = 0;
- sample_table->bytes_per_frame = 0;
- sample_table->bytes_per_sample = 0;
- sample_table->samples_per_frame = 0;
+ trak->edit_list_count = 0;
+ trak->edit_list_table = NULL;
+ trak->chunk_offset_count = 0;
+ trak->chunk_offset_table = NULL;
+ trak->sample_size = 0;
+ trak->sample_size_count = 0;
+ trak->sample_size_table = NULL;
+ trak->sync_sample_table = 0;
+ trak->sync_sample_table = NULL;
+ trak->sample_to_chunk_count = 0;
+ trak->sample_to_chunk_table = NULL;
+ trak->time_to_sample_count = 0;
+ trak->time_to_sample_table = NULL;
+ trak->frames = NULL;
+ trak->decoder_config = NULL;
+ trak->decoder_config_len = 0;
+ trak->stsd = NULL;
+ memset(&trak->properties, 0, sizeof(trak->properties));
/* default type */
- sample_table->type = MEDIA_OTHER;
+ trak->type = MEDIA_OTHER;
/* search for media type atoms */
for (i = ATOM_PREAMBLE_SIZE; i < trak_atom_size - 4; i++) {
current_atom = BE_32(&trak_atom[i]);
if (current_atom == VMHD_ATOM) {
- sample_table->type = MEDIA_VIDEO;
+ trak->type = MEDIA_VIDEO;
break;
} else if (current_atom == SMHD_ATOM) {
- sample_table->type = MEDIA_AUDIO;
+ trak->type = MEDIA_AUDIO;
break;
}
}
debug_atom_load(" qt: parsing %s trak atom\n",
- (sample_table->type == MEDIA_VIDEO) ? "video" :
- (sample_table->type == MEDIA_AUDIO) ? "audio" : "other");
+ (trak->type == MEDIA_VIDEO) ? "video" :
+ (trak->type == MEDIA_AUDIO) ? "audio" : "other");
/* search for the useful atoms */
for (i = ATOM_PREAMBLE_SIZE; i < trak_atom_size - 4; i++) {
@@ -734,55 +681,57 @@ static qt_error parse_trak_atom (qt_sample_table *sample_table,
current_atom = BE_32(&trak_atom[i]);
if (current_atom == TKHD_ATOM) {
- sample_table->flags = BE_16(&trak_atom[i + 6]);
+ trak->flags = BE_16(&trak_atom[i + 6]);
- if (sample_table->type == MEDIA_VIDEO) {
+ if (trak->type == MEDIA_VIDEO) {
/* fetch display parameters */
- if( !sample_table->media_description.video.width ||
- !sample_table->media_description.video.height ) {
+ if( !trak->properties.video.width ||
+ !trak->properties.video.height ) {
- sample_table->media_description.video.width =
+ trak->properties.video.width =
BE_16(&trak_atom[i + 0x50]);
- sample_table->media_description.video.height =
+ trak->properties.video.height =
BE_16(&trak_atom[i + 0x54]);
}
}
} else if (current_atom == ELST_ATOM) {
/* there should only be one edit list table */
- if (sample_table->edit_list_table) {
+ if (trak->edit_list_table) {
last_error = QT_HEADER_TROUBLE;
- goto free_sample_table;
+ goto free_trak;
}
- sample_table->edit_list_count = BE_32(&trak_atom[i + 8]);
+ trak->edit_list_count = BE_32(&trak_atom[i + 8]);
debug_atom_load(" qt elst atom (edit list atom): %d entries\n",
- sample_table->edit_list_count);
+ trak->edit_list_count);
- sample_table->edit_list_table = (edit_list_table_t *)malloc(
- sample_table->edit_list_count * sizeof(edit_list_table_t));
- if (!sample_table->edit_list_table) {
+ trak->edit_list_table = (edit_list_table_t *)malloc(
+ trak->edit_list_count * sizeof(edit_list_table_t));
+ if (!trak->edit_list_table) {
last_error = QT_NO_MEMORY;
- goto free_sample_table;
+ goto free_trak;
}
/* load the edit list table */
- for (j = 0; j < sample_table->edit_list_count; j++) {
- sample_table->edit_list_table[j].track_duration =
+ for (j = 0; j < trak->edit_list_count; j++) {
+ trak->edit_list_table[j].track_duration =
BE_32(&trak_atom[i + 12 + j * 12 + 0]);
- sample_table->edit_list_table[j].media_time =
+ trak->edit_list_table[j].media_time =
BE_32(&trak_atom[i + 12 + j * 12 + 4]);
debug_atom_load(" %d: track duration = %d, media time = %d\n",
j,
- sample_table->edit_list_table[j].track_duration,
- sample_table->edit_list_table[j].media_time);
+ trak->edit_list_table[j].track_duration,
+ trak->edit_list_table[j].media_time);
}
} else if (current_atom == MDHD_ATOM)
- sample_table->timescale = BE_32(&trak_atom[i + 0x10]);
+ trak->timescale = BE_32(&trak_atom[i + 0x10]);
else if (current_atom == STSD_ATOM) {
+ int hack_adjust;
+
debug_atom_load ("demux_qt: stsd atom\n");
#if DEBUG_ATOM_LOAD
hexdump (&trak_atom[i], current_atom_size);
@@ -790,33 +739,57 @@ static qt_error parse_trak_atom (qt_sample_table *sample_table,
/* copy whole stsd atom so it can later be sent to the decoder */
- sample_table->stsd_size = current_atom_size;
- sample_table->stsd = xine_xmalloc (current_atom_size);
- memcpy (sample_table->stsd, &trak_atom[i], current_atom_size);
+ trak->stsd_size = current_atom_size;
+ trak->stsd = xine_xmalloc (current_atom_size);
+
+ /* awful, awful hack to support a certain type of stsd atom that
+ * contains more than 1 video description atom */
+ if (BE_32(&trak_atom[i + 8]) == 1) {
+ /* normal case */
+ memcpy (trak->stsd, &trak_atom[i], current_atom_size);
+ hack_adjust = 0;
+ } else {
+ /* pathological case; take this route until a more definite
+ * solution is found: jump over the first atom video
+ * description atom */
+
+ /* copy the first 12 bytes since those remain the same */
+ memcpy (trak->stsd, &trak_atom[i], 12);
+
+ /* skip to the second atom and copy it */
+ hack_adjust = BE_32(&trak_atom[i + 0x0C]);
+ memcpy(trak->stsd + 12, &trak_atom[i + 0x0C + hack_adjust],
+ BE_32(&trak_atom[i + 0x0C + hack_adjust]));
+
+ /* use this variable to reference into the second atom, and
+ * fix at the end of the stsd parser */
+ i += hack_adjust;
+ }
- if (sample_table->type == MEDIA_VIDEO) {
+ if (trak->type == MEDIA_VIDEO) {
/* initialize to sane values */
- sample_table->media_description.video.width = 0;
- sample_table->media_description.video.height = 0;
- sample_table->media_description.video.depth = 0;
+ trak->properties.video.width = 0;
+ trak->properties.video.height = 0;
+ trak->properties.video.depth = 0;
/* assume no palette at first */
- sample_table->media_description.video.palette_count = 0;
+ trak->properties.video.palette_count = 0;
/* fetch video parameters */
- if( BE_16(&trak_atom[i + 0x2C]) && BE_16(&trak_atom[i + 0x2E]) ) {
- sample_table->media_description.video.width =
+ if( BE_16(&trak_atom[i + 0x2C]) &&
+ BE_16(&trak_atom[i + 0x2E]) ) {
+ trak->properties.video.width =
BE_16(&trak_atom[i + 0x2C]);
- sample_table->media_description.video.height =
+ trak->properties.video.height =
BE_16(&trak_atom[i + 0x2E]);
}
- sample_table->media_description.video.codec_format =
+ trak->properties.video.codec_fourcc =
ME_32(&trak_atom[i + 0x10]);
/* figure out the palette situation */
color_depth = trak_atom[i + 0x5F];
- sample_table->media_description.video.depth = color_depth;
+ trak->properties.video.depth = color_depth;
color_greyscale = color_depth & 0x20;
color_depth &= 0x1F;
@@ -827,20 +800,20 @@ static qt_error parse_trak_atom (qt_sample_table *sample_table,
if (color_greyscale) {
- sample_table->media_description.video.palette_count =
+ trak->properties.video.palette_count =
1 << color_depth;
/* compute the greyscale palette */
color_index = 255;
color_dec = 256 /
- (sample_table->media_description.video.palette_count - 1);
+ (trak->properties.video.palette_count - 1);
for (j = 0;
- j < sample_table->media_description.video.palette_count;
+ j < trak->properties.video.palette_count;
j++) {
- sample_table->media_description.video.palette[j].r = color_index;
- sample_table->media_description.video.palette[j].g = color_index;
- sample_table->media_description.video.palette[j].b = color_index;
+ trak->properties.video.palette[j].r = color_index;
+ trak->properties.video.palette[j].g = color_index;
+ trak->properties.video.palette[j].b = color_index;
color_index -= color_dec;
if (color_index < 0)
color_index = 0;
@@ -849,7 +822,7 @@ static qt_error parse_trak_atom (qt_sample_table *sample_table,
} else if (color_flag & 0x08) {
/* if flag bit 3 is set, load the default palette */
- sample_table->media_description.video.palette_count =
+ trak->properties.video.palette_count =
1 << color_depth;
if (color_depth == 2)
@@ -860,14 +833,14 @@ static qt_error parse_trak_atom (qt_sample_table *sample_table,
color_table = qt_default_palette_256;
for (j = 0;
- j < sample_table->media_description.video.palette_count;
+ j < trak->properties.video.palette_count;
j++) {
- sample_table->media_description.video.palette[j].r =
+ trak->properties.video.palette[j].r =
color_table[j * 4 + 0];
- sample_table->media_description.video.palette[j].g =
+ trak->properties.video.palette[j].g =
color_table[j * 4 + 1];
- sample_table->media_description.video.palette[j].b =
+ trak->properties.video.palette[j].b =
color_table[j * 4 + 2];
}
@@ -878,7 +851,7 @@ static qt_error parse_trak_atom (qt_sample_table *sample_table,
color_start = BE_32(&trak_atom[i + 0x62]);
color_count = BE_16(&trak_atom[i + 0x66]);
color_end = BE_16(&trak_atom[i + 0x68]);
- sample_table->media_description.video.palette_count =
+ trak->properties.video.palette_count =
color_end + 1;
for (j = color_start; j <= color_end; j++) {
@@ -887,23 +860,23 @@ static qt_error parse_trak_atom (qt_sample_table *sample_table,
if (color_count & 0x8000)
color_index = j;
if (color_index <
- sample_table->media_description.video.palette_count) {
- sample_table->media_description.video.palette[color_index].r =
+ trak->properties.video.palette_count) {
+ trak->properties.video.palette[color_index].r =
trak_atom[i + 0x6A + j * 8 + 2];
- sample_table->media_description.video.palette[color_index].g =
+ trak->properties.video.palette[color_index].g =
trak_atom[i + 0x6A + j * 8 + 4];
- sample_table->media_description.video.palette[color_index].b =
+ trak->properties.video.palette[color_index].b =
trak_atom[i + 0x6A + j * 8 + 6];
}
}
}
} else
- sample_table->media_description.video.palette_count = 0;
+ trak->properties.video.palette_count = 0;
debug_atom_load(" video description\n");
debug_atom_load(" %dx%d, video fourcc = '%c%c%c%c' (%02X%02X%02X%02X)\n",
- sample_table->media_description.video.width,
- sample_table->media_description.video.height,
+ trak->properties.video.width,
+ trak->properties.video.height,
trak_atom[i + 0x10],
trak_atom[i + 0x11],
trak_atom[i + 0x12],
@@ -913,45 +886,48 @@ static qt_error parse_trak_atom (qt_sample_table *sample_table,
trak_atom[i + 0x12],
trak_atom[i + 0x13]);
debug_atom_load(" %d RGB colors\n",
- sample_table->media_description.video.palette_count);
- for (j = 0; j < sample_table->media_description.video.palette_count;
+ trak->properties.video.palette_count);
+ for (j = 0; j < trak->properties.video.palette_count;
j++)
debug_atom_load(" %d: %3d %3d %3d\n",
j,
- sample_table->media_description.video.palette[j].r,
- sample_table->media_description.video.palette[j].g,
- sample_table->media_description.video.palette[j].b);
+ trak->properties.video.palette[j].r,
+ trak->properties.video.palette[j].g,
+ trak->properties.video.palette[j].b);
- } else if (sample_table->type == MEDIA_AUDIO) {
+ } else if (trak->type == MEDIA_AUDIO) {
/* fetch audio parameters */
- sample_table->media_description.audio.codec_format =
+ trak->properties.audio.codec_fourcc =
ME_32(&trak_atom[i + 0x10]);
- sample_table->media_description.audio.sample_rate =
+ trak->properties.audio.sample_rate =
BE_16(&trak_atom[i + 0x2C]);
- sample_table->media_description.audio.channels = trak_atom[i + 0x25];
- sample_table->media_description.audio.bits = trak_atom[i + 0x27];
+ trak->properties.audio.channels = trak_atom[i + 0x25];
+ trak->properties.audio.bits = trak_atom[i + 0x27];
/* assume uncompressed audio parameters */
- sample_table->bytes_per_sample =
- sample_table->media_description.audio.bits / 8;
- sample_table->samples_per_frame =
- sample_table->media_description.audio.channels;
- sample_table->bytes_per_frame =
- sample_table->bytes_per_sample * sample_table->samples_per_frame;
- sample_table->samples_per_packet = sample_table->samples_per_frame;
- sample_table->bytes_per_packet = sample_table->bytes_per_sample;
+ trak->properties.audio.bytes_per_sample =
+ trak->properties.audio.bits / 8;
+ trak->properties.audio.samples_per_frame =
+ trak->properties.audio.channels;
+ trak->properties.audio.bytes_per_frame =
+ trak->properties.audio.bytes_per_sample *
+ trak->properties.audio.samples_per_frame;
+ trak->properties.audio.samples_per_packet =
+ trak->properties.audio.samples_per_frame;
+ trak->properties.audio.bytes_per_packet =
+ trak->properties.audio.bytes_per_sample;
/* special case time: some ima4-encoded files don't have the
* extra header; compensate */
if (BE_32(&trak_atom[i + 0x10]) == IMA4_FOURCC) {
- sample_table->samples_per_packet = 64;
- sample_table->bytes_per_packet = 34;
- sample_table->bytes_per_frame = 34 *
- sample_table->media_description.audio.channels;
- sample_table->bytes_per_sample = 2;
- sample_table->samples_per_frame = 64 *
- sample_table->media_description.audio.channels;
+ trak->properties.audio.samples_per_packet = 64;
+ trak->properties.audio.bytes_per_packet = 34;
+ trak->properties.audio.bytes_per_frame = 34 *
+ trak->properties.audio.channels;
+ trak->properties.audio.bytes_per_sample = 2;
+ trak->properties.audio.samples_per_frame = 64 *
+ trak->properties.audio.channels;
}
/* it's time to dig a little deeper to determine the real audio
@@ -961,47 +937,52 @@ static qt_error parse_trak_atom (qt_sample_table *sample_table,
if (BE_32(&trak_atom[i + 0x0C]) > 0x24) {
if (BE_32(&trak_atom[i + 0x30]))
- sample_table->samples_per_packet = BE_32(&trak_atom[i + 0x30]);
+ trak->properties.audio.samples_per_packet =
+ BE_32(&trak_atom[i + 0x30]);
if (BE_32(&trak_atom[i + 0x34]))
- sample_table->bytes_per_packet = BE_32(&trak_atom[i + 0x34]);
+ trak->properties.audio.bytes_per_packet =
+ BE_32(&trak_atom[i + 0x34]);
if (BE_32(&trak_atom[i + 0x38]))
- sample_table->bytes_per_frame = BE_32(&trak_atom[i + 0x38]);
+ trak->properties.audio.bytes_per_frame =
+ BE_32(&trak_atom[i + 0x38]);
if (BE_32(&trak_atom[i + 0x3C]))
- sample_table->bytes_per_sample = BE_32(&trak_atom[i + 0x3C]);
- sample_table->samples_per_frame =
- (sample_table->bytes_per_frame / sample_table->bytes_per_packet) *
- sample_table->samples_per_packet;
+ trak->properties.audio.bytes_per_sample =
+ BE_32(&trak_atom[i + 0x3C]);
+ trak->properties.audio.samples_per_frame =
+ (trak->properties.audio.bytes_per_frame /
+ trak->properties.audio.bytes_per_packet) *
+ trak->properties.audio.samples_per_packet;
}
/* see if the trak deserves a promotion to VBR */
if (BE_16(&trak_atom[i + 0x28]) == 0xFFFE)
- sample_table->media_description.audio.vbr = 1;
+ trak->properties.audio.vbr = 1;
else
- sample_table->media_description.audio.vbr = 0;
+ trak->properties.audio.vbr = 0;
- /* if this is MP4 audio, mark it as VBR */
+ /* if this is MP4 audio, mark the trak as VBR */
if (BE_32(&trak_atom[i + 0x10]) == MP4A_FOURCC)
- sample_table->media_description.audio.vbr = 1;
+ trak->properties.audio.vbr = 1;
/* check for a MS-style WAVE format header */
if ((current_atom_size >= 0x48) &&
(BE_32(&trak_atom[i + 0x44]) == WAVE_ATOM)) {
- sample_table->media_description.audio.wave_present = 1;
- memcpy(&sample_table->media_description.audio.wave,
+ trak->properties.audio.wave_present = 1;
+ memcpy(&trak->properties.audio.wave,
&trak_atom[i + 0x5C],
- sizeof(sample_table->media_description.audio.wave));
- xine_waveformatex_le2me(&sample_table->media_description.audio.wave);
+ sizeof(trak->properties.audio.wave));
+ xine_waveformatex_le2me(&trak->properties.audio.wave);
} else {
- sample_table->media_description.audio.wave_present = 0;
+ trak->properties.audio.wave_present = 0;
}
debug_atom_load(" audio description\n");
debug_atom_load(" %d Hz, %d bits, %d channels, %saudio fourcc = '%c%c%c%c' (%02X%02X%02X%02X)\n",
- sample_table->media_description.audio.sample_rate,
- sample_table->media_description.audio.bits,
- sample_table->media_description.audio.channels,
- (sample_table->media_description.audio.vbr) ? "vbr, " : "",
+ trak->properties.audio.sample_rate,
+ trak->properties.audio.bits,
+ trak->properties.audio.channels,
+ (trak->properties.audio.vbr) ? "vbr, " : "",
trak_atom[i + 0x10],
trak_atom[i + 0x11],
trak_atom[i + 0x12],
@@ -1012,23 +993,25 @@ static qt_error parse_trak_atom (qt_sample_table *sample_table,
trak_atom[i + 0x13]);
if (BE_32(&trak_atom[i + 0x0C]) > 0x24) {
debug_atom_load(" %d samples/packet, %d bytes/packet, %d bytes/frame\n",
- sample_table->samples_per_packet,
- sample_table->bytes_per_packet,
- sample_table->bytes_per_frame);
+ trak->properties.audio.samples_per_packet,
+ trak->properties.audio.bytes_per_packet,
+ trak->properties.audio.bytes_per_frame);
debug_atom_load(" %d bytes/sample (%d samples/frame)\n",
- sample_table->bytes_per_sample,
- sample_table->samples_per_frame);
+ trak->properties.audio.bytes_per_sample,
+ trak->properties.audio.samples_per_frame);
}
}
+ i -= hack_adjust;
+
} else if (current_atom == ESDS_ATOM) {
uint32_t len;
debug_atom_load(" qt/mpeg-4 esds atom\n");
- if ((sample_table->type == MEDIA_VIDEO) ||
- (sample_table->type == MEDIA_AUDIO)) {
+ if ((trak->type == MEDIA_VIDEO) ||
+ (trak->type == MEDIA_AUDIO)) {
j = i + 8;
if( trak_atom[j++] == 0x03 ) {
@@ -1043,9 +1026,9 @@ static qt_error parse_trak_atom (qt_sample_table *sample_table,
j += mp4_read_descr_len( &trak_atom[j], &len );
debug_atom_load(" decoder config is %d (0x%X) bytes long\n",
len, len);
- sample_table->decoder_config = malloc(len);
- sample_table->decoder_config_len = len;
- memcpy(sample_table->decoder_config,&trak_atom[j],len);
+ trak->decoder_config = malloc(len);
+ trak->decoder_config_len = len;
+ memcpy(trak->decoder_config,&trak_atom[j],len);
}
}
}
@@ -1053,205 +1036,205 @@ static qt_error parse_trak_atom (qt_sample_table *sample_table,
} else if (current_atom == STSZ_ATOM) {
/* there should only be one of these atoms */
- if (sample_table->sample_size_table) {
+ if (trak->sample_size_table) {
last_error = QT_HEADER_TROUBLE;
- goto free_sample_table;
+ goto free_trak;
}
- sample_table->sample_size = BE_32(&trak_atom[i + 8]);
- sample_table->sample_size_count = BE_32(&trak_atom[i + 12]);
+ trak->sample_size = BE_32(&trak_atom[i + 8]);
+ trak->sample_size_count = BE_32(&trak_atom[i + 12]);
debug_atom_load(" qt stsz atom (sample size atom): sample size = %d, %d entries\n",
- sample_table->sample_size, sample_table->sample_size_count);
+ trak->sample_size, trak->sample_size_count);
/* allocate space and load table only if sample size is 0 */
- if (sample_table->sample_size == 0) {
- sample_table->sample_size_table = (unsigned int *)malloc(
- sample_table->sample_size_count * sizeof(unsigned int));
- if (!sample_table->sample_size_table) {
+ if (trak->sample_size == 0) {
+ trak->sample_size_table = (unsigned int *)malloc(
+ trak->sample_size_count * sizeof(unsigned int));
+ if (!trak->sample_size_table) {
last_error = QT_NO_MEMORY;
- goto free_sample_table;
+ goto free_trak;
}
/* load the sample size table */
- for (j = 0; j < sample_table->sample_size_count; j++) {
- sample_table->sample_size_table[j] =
+ for (j = 0; j < trak->sample_size_count; j++) {
+ trak->sample_size_table[j] =
BE_32(&trak_atom[i + 16 + j * 4]);
debug_atom_load(" sample size %d: %d\n",
- j, sample_table->sample_size_table[j]);
+ j, trak->sample_size_table[j]);
}
} else
/* set the pointer to non-NULL to indicate that the atom type has
* already been seen for this trak atom */
- sample_table->sample_size_table = (void *)-1;
+ trak->sample_size_table = (void *)-1;
} else if (current_atom == STSS_ATOM) {
/* there should only be one of these atoms */
- if (sample_table->sync_sample_table) {
+ if (trak->sync_sample_table) {
last_error = QT_HEADER_TROUBLE;
- goto free_sample_table;
+ goto free_trak;
}
- sample_table->sync_sample_count = BE_32(&trak_atom[i + 8]);
+ trak->sync_sample_count = BE_32(&trak_atom[i + 8]);
debug_atom_load(" qt stss atom (sample sync atom): %d sync samples\n",
- sample_table->sync_sample_count);
+ trak->sync_sample_count);
- sample_table->sync_sample_table = (unsigned int *)malloc(
- sample_table->sync_sample_count * sizeof(unsigned int));
- if (!sample_table->sync_sample_table) {
+ trak->sync_sample_table = (unsigned int *)malloc(
+ trak->sync_sample_count * sizeof(unsigned int));
+ if (!trak->sync_sample_table) {
last_error = QT_NO_MEMORY;
- goto free_sample_table;
+ goto free_trak;
}
/* load the sync sample table */
- for (j = 0; j < sample_table->sync_sample_count; j++) {
- sample_table->sync_sample_table[j] =
+ for (j = 0; j < trak->sync_sample_count; j++) {
+ trak->sync_sample_table[j] =
BE_32(&trak_atom[i + 12 + j * 4]);
debug_atom_load(" sync sample %d: sample %d (%d) is a keyframe\n",
- j, sample_table->sync_sample_table[j],
- sample_table->sync_sample_table[j] - 1);
+ j, trak->sync_sample_table[j],
+ trak->sync_sample_table[j] - 1);
}
} else if (current_atom == STCO_ATOM) {
/* there should only be one of either stco or co64 */
- if (sample_table->chunk_offset_table) {
+ if (trak->chunk_offset_table) {
last_error = QT_HEADER_TROUBLE;
- goto free_sample_table;
+ goto free_trak;
}
- sample_table->chunk_offset_count = BE_32(&trak_atom[i + 8]);
+ trak->chunk_offset_count = BE_32(&trak_atom[i + 8]);
debug_atom_load(" qt stco atom (32-bit chunk offset atom): %d chunk offsets\n",
- sample_table->chunk_offset_count);
+ trak->chunk_offset_count);
- sample_table->chunk_offset_table = (int64_t *)malloc(
- sample_table->chunk_offset_count * sizeof(int64_t));
- if (!sample_table->chunk_offset_table) {
+ trak->chunk_offset_table = (int64_t *)malloc(
+ trak->chunk_offset_count * sizeof(int64_t));
+ if (!trak->chunk_offset_table) {
last_error = QT_NO_MEMORY;
- goto free_sample_table;
+ goto free_trak;
}
/* load the chunk offset table */
- for (j = 0; j < sample_table->chunk_offset_count; j++) {
- sample_table->chunk_offset_table[j] =
+ for (j = 0; j < trak->chunk_offset_count; j++) {
+ trak->chunk_offset_table[j] =
BE_32(&trak_atom[i + 12 + j * 4]);
debug_atom_load(" chunk %d @ 0x%llX\n",
- j, sample_table->chunk_offset_table[j]);
+ j, trak->chunk_offset_table[j]);
}
} else if (current_atom == CO64_ATOM) {
/* there should only be one of either stco or co64 */
- if (sample_table->chunk_offset_table) {
+ if (trak->chunk_offset_table) {
last_error = QT_HEADER_TROUBLE;
- goto free_sample_table;
+ goto free_trak;
}
- sample_table->chunk_offset_count = BE_32(&trak_atom[i + 8]);
+ trak->chunk_offset_count = BE_32(&trak_atom[i + 8]);
debug_atom_load(" qt co64 atom (64-bit chunk offset atom): %d chunk offsets\n",
- sample_table->chunk_offset_count);
+ trak->chunk_offset_count);
- sample_table->chunk_offset_table = (int64_t *)malloc(
- sample_table->chunk_offset_count * sizeof(int64_t));
- if (!sample_table->chunk_offset_table) {
+ trak->chunk_offset_table = (int64_t *)malloc(
+ trak->chunk_offset_count * sizeof(int64_t));
+ if (!trak->chunk_offset_table) {
last_error = QT_NO_MEMORY;
- goto free_sample_table;
+ goto free_trak;
}
/* load the 64-bit chunk offset table */
- for (j = 0; j < sample_table->chunk_offset_count; j++) {
- sample_table->chunk_offset_table[j] =
+ for (j = 0; j < trak->chunk_offset_count; j++) {
+ trak->chunk_offset_table[j] =
BE_32(&trak_atom[i + 12 + j * 8 + 0]);
- sample_table->chunk_offset_table[j] <<= 32;
- sample_table->chunk_offset_table[j] |=
+ trak->chunk_offset_table[j] <<= 32;
+ trak->chunk_offset_table[j] |=
BE_32(&trak_atom[i + 12 + j * 8 + 4]);
debug_atom_load(" chunk %d @ 0x%llX\n",
- j, sample_table->chunk_offset_table[j]);
+ j, trak->chunk_offset_table[j]);
}
} else if (current_atom == STSC_ATOM) {
/* there should only be one of these atoms */
- if (sample_table->sample_to_chunk_table) {
+ if (trak->sample_to_chunk_table) {
last_error = QT_HEADER_TROUBLE;
- goto free_sample_table;
+ goto free_trak;
}
- sample_table->sample_to_chunk_count = BE_32(&trak_atom[i + 8]);
+ trak->sample_to_chunk_count = BE_32(&trak_atom[i + 8]);
debug_atom_load(" qt stsc atom (sample-to-chunk atom): %d entries\n",
- sample_table->sample_to_chunk_count);
+ trak->sample_to_chunk_count);
- sample_table->sample_to_chunk_table = (sample_to_chunk_table_t *)malloc(
- sample_table->sample_to_chunk_count * sizeof(sample_to_chunk_table_t));
- if (!sample_table->sample_to_chunk_table) {
+ trak->sample_to_chunk_table = (sample_to_chunk_table_t *)malloc(
+ trak->sample_to_chunk_count * sizeof(sample_to_chunk_table_t));
+ if (!trak->sample_to_chunk_table) {
last_error = QT_NO_MEMORY;
- goto free_sample_table;
+ goto free_trak;
}
/* load the sample to chunk table */
- for (j = 0; j < sample_table->sample_to_chunk_count; j++) {
- sample_table->sample_to_chunk_table[j].first_chunk =
+ for (j = 0; j < trak->sample_to_chunk_count; j++) {
+ trak->sample_to_chunk_table[j].first_chunk =
BE_32(&trak_atom[i + 12 + j * 12 + 0]);
- sample_table->sample_to_chunk_table[j].samples_per_chunk =
+ trak->sample_to_chunk_table[j].samples_per_chunk =
BE_32(&trak_atom[i + 12 + j * 12 + 4]);
debug_atom_load(" %d: %d samples/chunk starting at chunk %d (%d)\n",
- j, sample_table->sample_to_chunk_table[j].samples_per_chunk,
- sample_table->sample_to_chunk_table[j].first_chunk,
- sample_table->sample_to_chunk_table[j].first_chunk - 1);
+ j, trak->sample_to_chunk_table[j].samples_per_chunk,
+ trak->sample_to_chunk_table[j].first_chunk,
+ trak->sample_to_chunk_table[j].first_chunk - 1);
}
} else if (current_atom == STTS_ATOM) {
/* there should only be one of these atoms */
- if (sample_table->time_to_sample_table) {
+ if (trak->time_to_sample_table) {
last_error = QT_HEADER_TROUBLE;
- goto free_sample_table;
+ goto free_trak;
}
- sample_table->time_to_sample_count = BE_32(&trak_atom[i + 8]);
+ trak->time_to_sample_count = BE_32(&trak_atom[i + 8]);
debug_atom_load(" qt stts atom (time-to-sample atom): %d entries\n",
- sample_table->time_to_sample_count);
+ trak->time_to_sample_count);
- sample_table->time_to_sample_table = (time_to_sample_table_t *)malloc(
- (sample_table->time_to_sample_count+1) * sizeof(time_to_sample_table_t));
- if (!sample_table->time_to_sample_table) {
+ trak->time_to_sample_table = (time_to_sample_table_t *)malloc(
+ (trak->time_to_sample_count+1) * sizeof(time_to_sample_table_t));
+ if (!trak->time_to_sample_table) {
last_error = QT_NO_MEMORY;
- goto free_sample_table;
+ goto free_trak;
}
/* load the time to sample table */
- for (j = 0; j < sample_table->time_to_sample_count; j++) {
- sample_table->time_to_sample_table[j].count =
+ for (j = 0; j < trak->time_to_sample_count; j++) {
+ trak->time_to_sample_table[j].count =
BE_32(&trak_atom[i + 12 + j * 8 + 0]);
- sample_table->time_to_sample_table[j].duration =
+ trak->time_to_sample_table[j].duration =
BE_32(&trak_atom[i + 12 + j * 8 + 4]);
debug_atom_load(" %d: count = %d, duration = %d\n",
- j, sample_table->time_to_sample_table[j].count,
- sample_table->time_to_sample_table[j].duration);
+ j, trak->time_to_sample_table[j].count,
+ trak->time_to_sample_table[j].duration);
}
- sample_table->time_to_sample_table[j].count = 0; /* terminate with zero */
+ trak->time_to_sample_table[j].count = 0; /* terminate with zero */
}
}
return QT_OK;
/* jump here to make sure everything is free'd and avoid leaking memory */
-free_sample_table:
- free(sample_table->edit_list_table);
- free(sample_table->chunk_offset_table);
+free_trak:
+ free(trak->edit_list_table);
+ free(trak->chunk_offset_table);
/* this pointer might have been set to -1 as a special case */
- if (sample_table->sample_size_table != (void *)-1)
- free(sample_table->sample_size_table);
- free(sample_table->sync_sample_table);
- free(sample_table->sample_to_chunk_table);
- free(sample_table->time_to_sample_table);
- free(sample_table->decoder_config);
- free(sample_table->stsd);
+ if (trak->sample_size_table != (void *)-1)
+ free(trak->sample_size_table);
+ free(trak->sync_sample_table);
+ free(trak->sample_to_chunk_table);
+ free(trak->time_to_sample_table);
+ free(trak->decoder_config);
+ free(trak->stsd);
return last_error;
}
@@ -1259,32 +1242,32 @@ free_sample_table:
/* This is a little support function used to process the edit list when
* building a frame table. */
#define MAX_DURATION 0x7FFFFFFFFFFFFFFF
-static void get_next_edit_list_entry(qt_sample_table *sample_table,
+static void get_next_edit_list_entry(qt_trak *trak,
int *edit_list_index,
unsigned int *edit_list_media_time,
int64_t *edit_list_duration,
unsigned int global_timescale) {
/* if there is no edit list, set to max duration and get out */
- if (!sample_table->edit_list_table) {
+ if (!trak->edit_list_table) {
*edit_list_media_time = 0;
*edit_list_duration = MAX_DURATION;
debug_edit_list(" qt: no edit list table, initial = %d, %lld\n", *edit_list_media_time, *edit_list_duration);
return;
- } else while (*edit_list_index < sample_table->edit_list_count) {
+ } else while (*edit_list_index < trak->edit_list_count) {
/* otherwise, find an edit list entries whose media time != -1 */
- if (sample_table->edit_list_table[*edit_list_index].media_time != -1) {
+ if (trak->edit_list_table[*edit_list_index].media_time != -1) {
*edit_list_media_time =
- sample_table->edit_list_table[*edit_list_index].media_time;
+ trak->edit_list_table[*edit_list_index].media_time;
*edit_list_duration =
- sample_table->edit_list_table[*edit_list_index].track_duration;
+ trak->edit_list_table[*edit_list_index].track_duration;
/* duration is in global timescale units; convert to trak timescale */
- *edit_list_duration *= sample_table->timescale;
+ *edit_list_duration *= trak->timescale;
*edit_list_duration /= global_timescale;
*edit_list_index = *edit_list_index + 1;
@@ -1297,12 +1280,12 @@ static void get_next_edit_list_entry(qt_sample_table *sample_table,
/* on the way out, check if this is the last edit list entry; if so,
* don't let the duration expire (so set it to an absurdly large value)
*/
- if (*edit_list_index == sample_table->edit_list_count)
+ if (*edit_list_index == trak->edit_list_count)
*edit_list_duration = MAX_DURATION;
debug_edit_list(" qt: edit list table exists, initial = %d, %lld\n", *edit_list_media_time, *edit_list_duration);
}
-static qt_error build_frame_table(qt_sample_table *sample_table,
+static qt_error build_frame_table(qt_trak *trak,
unsigned int global_timescale) {
int i, j;
@@ -1322,15 +1305,15 @@ static qt_error build_frame_table(qt_sample_table *sample_table,
/* AUDIO and OTHER frame types follow the same rules; VIDEO and vbr audio
* frame types follow a different set */
- if ((sample_table->type == MEDIA_VIDEO) ||
- (sample_table->media_description.audio.vbr)) {
+ if ((trak->type == MEDIA_VIDEO) ||
+ (trak->properties.audio.vbr)) {
/* in this case, the total number of frames is equal to the number of
* entries in the sample size table */
- sample_table->frame_count = sample_table->sample_size_count;
- sample_table->frames = (qt_frame *)malloc(
- sample_table->frame_count * sizeof(qt_frame));
- if (!sample_table->frames)
+ trak->frame_count = trak->sample_size_count;
+ trak->frames = (qt_frame *)malloc(
+ trak->frame_count * sizeof(qt_frame));
+ if (!trak->frames)
return QT_NO_MEMORY;
/* initialize more accounting variables */
@@ -1338,60 +1321,59 @@ static qt_error build_frame_table(qt_sample_table *sample_table,
current_pts = 0;
pts_index = 0;
pts_index_countdown =
- sample_table->time_to_sample_table[pts_index].count;
+ trak->time_to_sample_table[pts_index].count;
/* iterate through each start chunk in the stsc table */
- for (i = 0; i < sample_table->sample_to_chunk_count; i++) {
+ for (i = 0; i < trak->sample_to_chunk_count; i++) {
/* iterate from the first chunk of the current table entry to
* the first chunk of the next table entry */
- chunk_start = sample_table->sample_to_chunk_table[i].first_chunk;
- if (i < sample_table->sample_to_chunk_count - 1)
+ chunk_start = trak->sample_to_chunk_table[i].first_chunk;
+ if (i < trak->sample_to_chunk_count - 1)
chunk_end =
- sample_table->sample_to_chunk_table[i + 1].first_chunk;
+ trak->sample_to_chunk_table[i + 1].first_chunk;
else
/* if the first chunk is in the last table entry, iterate to the
final chunk number (the number of offsets in stco table) */
- chunk_end = sample_table->chunk_offset_count + 1;
+ chunk_end = trak->chunk_offset_count + 1;
/* iterate through each sample in a chunk */
for (j = chunk_start - 1; j < chunk_end - 1; j++) {
samples_per_chunk =
- sample_table->sample_to_chunk_table[i].samples_per_chunk;
- current_offset = sample_table->chunk_offset_table[j];
+ trak->sample_to_chunk_table[i].samples_per_chunk;
+ current_offset = trak->chunk_offset_table[j];
while (samples_per_chunk > 0) {
- sample_table->frames[frame_counter].type = sample_table->type;
/* figure out the offset and size */
- sample_table->frames[frame_counter].offset = current_offset;
- if (sample_table->sample_size) {
- sample_table->frames[frame_counter].size =
- sample_table->sample_size;
- current_offset += sample_table->sample_size;
+ trak->frames[frame_counter].offset = current_offset;
+ if (trak->sample_size) {
+ trak->frames[frame_counter].size =
+ trak->sample_size;
+ current_offset += trak->sample_size;
} else {
- sample_table->frames[frame_counter].size =
- sample_table->sample_size_table[frame_counter];
+ trak->frames[frame_counter].size =
+ trak->sample_size_table[frame_counter];
current_offset +=
- sample_table->sample_size_table[frame_counter];
+ trak->sample_size_table[frame_counter];
}
/* if there is no stss (sample sync) table, make all of the frames
* keyframes; otherwise, clear the keyframe bits for now */
- if (sample_table->sync_sample_table)
- sample_table->frames[frame_counter].keyframe = 0;
+ if (trak->sync_sample_table)
+ trak->frames[frame_counter].keyframe = 0;
else
- sample_table->frames[frame_counter].keyframe = 1;
+ trak->frames[frame_counter].keyframe = 1;
/* figure out the pts situation */
- sample_table->frames[frame_counter].pts = current_pts;
+ trak->frames[frame_counter].pts = current_pts;
current_pts +=
- sample_table->time_to_sample_table[pts_index].duration;
+ trak->time_to_sample_table[pts_index].duration;
pts_index_countdown--;
/* time to refresh countdown? */
if (!pts_index_countdown) {
pts_index++;
pts_index_countdown =
- sample_table->time_to_sample_table[pts_index].count;
+ trak->time_to_sample_table[pts_index].count;
}
samples_per_chunk--;
@@ -1401,117 +1383,115 @@ static qt_error build_frame_table(qt_sample_table *sample_table,
}
/* fill in the keyframe information */
- if (sample_table->sync_sample_table) {
- for (i = 0; i < sample_table->sync_sample_count; i++)
- sample_table->frames[sample_table->sync_sample_table[i] - 1].keyframe = 1;
+ if (trak->sync_sample_table) {
+ for (i = 0; i < trak->sync_sample_count; i++)
+ trak->frames[trak->sync_sample_table[i] - 1].keyframe = 1;
}
/* initialize edit list considerations */
edit_list_index = 0;
- get_next_edit_list_entry(sample_table, &edit_list_index,
+ get_next_edit_list_entry(trak, &edit_list_index,
&edit_list_media_time, &edit_list_duration, global_timescale);
/* fix up pts information w.r.t. the edit list table */
edit_list_pts_counter = 0;
- for (i = 0; i < sample_table->frame_count; i++) {
+ for (i = 0; i < trak->frame_count; i++) {
- debug_edit_list(" %d: (before) pts = %lld...", i, sample_table->frames[i].pts);
+ debug_edit_list(" %d: (before) pts = %lld...", i, trak->frames[i].pts);
- if (sample_table->frames[i].pts < edit_list_media_time)
- sample_table->frames[i].pts = edit_list_pts_counter;
+ if (trak->frames[i].pts < edit_list_media_time)
+ trak->frames[i].pts = edit_list_pts_counter;
else {
- if (i < sample_table->frame_count - 1)
+ if (i < trak->frame_count - 1)
frame_duration =
- (sample_table->frames[i + 1].pts - sample_table->frames[i].pts);
+ (trak->frames[i + 1].pts - trak->frames[i].pts);
debug_edit_list("duration = %lld...", frame_duration);
- sample_table->frames[i].pts = edit_list_pts_counter;
+ trak->frames[i].pts = edit_list_pts_counter;
edit_list_pts_counter += frame_duration;
edit_list_duration -= frame_duration;
}
- debug_edit_list("(fixup) pts = %lld...", sample_table->frames[i].pts);
+ debug_edit_list("(fixup) pts = %lld...", trak->frames[i].pts);
/* reload media time and duration */
if (edit_list_duration <= 0) {
- get_next_edit_list_entry(sample_table, &edit_list_index,
+ get_next_edit_list_entry(trak, &edit_list_index,
&edit_list_media_time, &edit_list_duration, global_timescale);
}
- debug_edit_list("(after) pts = %lld...\n", sample_table->frames[i].pts);
+ debug_edit_list("(after) pts = %lld...\n", trak->frames[i].pts);
}
/* compute final pts values */
- for (i = 0; i < sample_table->frame_count; i++) {
- sample_table->frames[i].pts *= 90000;
- sample_table->frames[i].pts /= sample_table->timescale;
- debug_edit_list(" final pts for sample %d = %lld\n", i, sample_table->frames[i].pts);
+ for (i = 0; i < trak->frame_count; i++) {
+ trak->frames[i].pts *= 90000;
+ trak->frames[i].pts /= trak->timescale;
+ debug_edit_list(" final pts for sample %d = %lld\n", i, trak->frames[i].pts);
}
} else {
/* in this case, the total number of frames is equal to the number of
* chunks */
- sample_table->frame_count = sample_table->chunk_offset_count;
- sample_table->frames = (qt_frame *)malloc(
- sample_table->frame_count * sizeof(qt_frame));
- if (!sample_table->frames)
+ trak->frame_count = trak->chunk_offset_count;
+ trak->frames = (qt_frame *)malloc(
+ trak->frame_count * sizeof(qt_frame));
+ if (!trak->frames)
return QT_NO_MEMORY;
- if (sample_table->type == MEDIA_AUDIO) {
+ if (trak->type == MEDIA_AUDIO) {
/* iterate through each start chunk in the stsc table */
- for (i = 0; i < sample_table->sample_to_chunk_count; i++) {
+ for (i = 0; i < trak->sample_to_chunk_count; i++) {
/* iterate from the first chunk of the current table entry to
* the first chunk of the next table entry */
- chunk_start = sample_table->sample_to_chunk_table[i].first_chunk;
- if (i < sample_table->sample_to_chunk_count - 1)
+ chunk_start = trak->sample_to_chunk_table[i].first_chunk;
+ if (i < trak->sample_to_chunk_count - 1)
chunk_end =
- sample_table->sample_to_chunk_table[i + 1].first_chunk;
+ trak->sample_to_chunk_table[i + 1].first_chunk;
else
/* if the first chunk is in the last table entry, iterate to the
final chunk number (the number of offsets in stco table) */
- chunk_end = sample_table->chunk_offset_count + 1;
+ chunk_end = trak->chunk_offset_count + 1;
/* iterate through each sample in a chunk and fill in size and
* pts information */
for (j = chunk_start - 1; j < chunk_end - 1; j++) {
/* figure out the pts for this chunk */
- sample_table->frames[j].pts = audio_frame_counter;
- sample_table->frames[j].pts *= 90000;
- sample_table->frames[j].pts /= sample_table->timescale;
+ trak->frames[j].pts = audio_frame_counter;
+ trak->frames[j].pts *= 90000;
+ trak->frames[j].pts /= trak->timescale;
/* fetch the alleged chunk size according to the QT header */
- sample_table->frames[j].size =
- sample_table->sample_to_chunk_table[i].samples_per_chunk;
+ trak->frames[j].size =
+ trak->sample_to_chunk_table[i].samples_per_chunk;
/* the chunk size is actually the audio frame count */
- audio_frame_counter += sample_table->frames[j].size;
+ audio_frame_counter += trak->frames[j].size;
/* compute the actual chunk size */
- sample_table->frames[j].size =
- (sample_table->frames[j].size *
- sample_table->media_description.audio.channels) /
- sample_table->samples_per_frame *
- sample_table->bytes_per_frame;
+ trak->frames[j].size =
+ (trak->frames[j].size *
+ trak->properties.audio.channels) /
+ trak->properties.audio.samples_per_frame *
+ trak->properties.audio.bytes_per_frame;
}
}
}
/* fill in the rest of the information for the audio samples */
- for (i = 0; i < sample_table->frame_count; i++) {
- sample_table->frames[i].type = sample_table->type;
- sample_table->frames[i].offset = sample_table->chunk_offset_table[i];
- sample_table->frames[i].keyframe = 0;
- if (sample_table->type != MEDIA_AUDIO)
- sample_table->frames[i].pts = 0;
+ for (i = 0; i < trak->frame_count; i++) {
+ trak->frames[i].offset = trak->chunk_offset_table[i];
+ trak->frames[i].keyframe = 0;
+ if (trak->type != MEDIA_AUDIO)
+ trak->frames[i].pts = 0;
}
}
return QT_OK;
}
-
/*
* This function takes a pointer to a qt_info structure and a pointer to
* a buffer containing an uncompressed moov atom. When the function
@@ -1519,15 +1499,12 @@ static qt_error build_frame_table(qt_sample_table *sample_table,
* ordered by offset.
*/
static void parse_moov_atom(qt_info *info, unsigned char *moov_atom) {
- int i, j;
+ int i;
unsigned int moov_atom_size = BE_32(&moov_atom[0]);
- unsigned int sample_table_count = 0;
- qt_sample_table *sample_tables = NULL;
qt_atom current_atom;
- unsigned int *sample_table_indices;
- unsigned int min_offset_table;
- int64_t min_offset;
int string_size;
+ unsigned int max_video_frames = 0;
+ unsigned int max_audio_frames = 0;
/* make sure this is actually a moov atom */
if (BE_32(&moov_atom[4]) != MOOV_ATOM) {
@@ -1546,13 +1523,12 @@ static void parse_moov_atom(qt_info *info, unsigned char *moov_atom) {
i += BE_32(&moov_atom[i - 4]) - 4;
} else if (current_atom == TRAK_ATOM) {
- /* make a new sample temporary sample table */
- sample_table_count++;
- sample_tables = (qt_sample_table *)realloc(sample_tables,
- sample_table_count * sizeof(qt_sample_table));
+ /* create a new trak structure */
+ info->trak_count++;
+ info->traks = (qt_trak *)realloc(info->traks,
+ info->trak_count * sizeof(qt_trak));
- parse_trak_atom (&sample_tables[sample_table_count - 1],
- &moov_atom[i - 4]);
+ parse_trak_atom (&info->traks[info->trak_count - 1], &moov_atom[i - 4]);
if (info->last_error != QT_OK)
return;
i += BE_32(&moov_atom[i - 4]) - 4;
@@ -1582,147 +1558,27 @@ static void parse_moov_atom(qt_info *info, unsigned char *moov_atom) {
debug_atom_load(" qt: finished parsing moov atom\n");
/* build frame tables corresponding to each sample table */
- info->frame_count = 0;
debug_frame_table(" qt: preparing to build %d frame tables\n",
- sample_table_count);
- for (i = 0; i < sample_table_count; i++) {
+ info->trak_count);
+ for (i = 0; i < info->trak_count; i++) {
debug_frame_table(" qt: building frame table #%d\n", i);
- build_frame_table(&sample_tables[i], info->timescale);
- info->frame_count += sample_tables[i].frame_count;
-
- /* while traversing tables, look for A/V information */
- if (sample_tables[i].type == MEDIA_VIDEO) {
+ build_frame_table(&info->traks[i], info->timescale);
- info->video_width = sample_tables[i].media_description.video.width;
- info->video_height = sample_tables[i].media_description.video.height;
- info->video_depth = sample_tables[i].media_description.video.depth;
- info->video_codec =
- sample_tables[i].media_description.video.codec_format;
-
- /* fixme: there may be multiple trak with decoder_config.
- i don't know if we should choose one or concatenate everything? */
- if( sample_tables[i].frame_count > 1 && sample_tables[i].decoder_config ) {
- info->video_decoder_config = sample_tables[i].decoder_config;
- info->video_decoder_config_len = sample_tables[i].decoder_config_len;
- }
+ /* decide which audio trak and which video trak has the most frames */
+ if ((info->traks[i].type == MEDIA_VIDEO) &&
+ (info->traks[i].frame_count > max_video_frames)) {
- /* pass the palette info to the master qt_info structure */
- if (sample_tables[i].media_description.video.palette_count) {
- info->palette_count =
- sample_tables[i].media_description.video.palette_count;
- memcpy(info->palette,
- sample_tables[i].media_description.video.palette,
- PALETTE_COUNT * sizeof(palette_entry_t));
- }
+ info->video_trak = i;
+ max_video_frames = info->traks[i].frame_count;
- /* stsd atom */
- info->video_stsd = realloc (info->video_stsd, sample_tables[i].stsd_size);
- info->video_stsd_size = sample_tables[i].stsd_size;
- memcpy(info->video_stsd, sample_tables[i].stsd, info->video_stsd_size);
-
- } else if (sample_tables[i].type == MEDIA_AUDIO) {
-
- info->audio_sample_rate =
- sample_tables[i].media_description.audio.sample_rate;
- info->audio_channels =
- sample_tables[i].media_description.audio.channels;
- info->audio_bits =
- sample_tables[i].media_description.audio.bits;
- info->audio_codec =
- sample_tables[i].media_description.audio.codec_format;
- info->audio_vbr =
- sample_tables[i].media_description.audio.vbr;
-
- info->audio_decoder_config = sample_tables[i].decoder_config;
- info->audio_decoder_config_len = sample_tables[i].decoder_config_len;
-
- info->wave_present =
- sample_tables[i].media_description.audio.wave_present;
- memcpy(&info->wave, &sample_tables[i].media_description.audio.wave,
- sizeof(xine_waveformatex));
-
- /* stsd atom */
- info->audio_stsd = realloc (info->audio_stsd, sample_tables[i].stsd_size);
- info->audio_stsd_size = sample_tables[i].stsd_size;
- memcpy(info->audio_stsd, sample_tables[i].stsd, info->audio_stsd_size);
- }
- }
- debug_frame_table(" qt: finished building frame tables, merging into one...\n");
-
- /* allocate the master frame index */
- info->frames = (qt_frame *)malloc(info->frame_count * sizeof(qt_frame));
- if (!info->frames) {
- info->last_error = QT_NO_MEMORY;
- return;
- }
-
- /* allocate and zero out space for table indices */
- sample_table_indices = (unsigned int *)malloc(
- sample_table_count * sizeof(unsigned int));
-
- if (!sample_table_indices) {
- info->last_error = QT_NO_MEMORY;
- return;
- }
- for (i = 0; i < sample_table_count; i++) {
- if (sample_tables[i].frame_count == 0)
- sample_table_indices[i] = 0x7FFFFFF;
- else
- sample_table_indices[i] = 0;
- }
+ } else if ((info->traks[i].type == MEDIA_AUDIO) &&
+ (info->traks[i].frame_count > max_audio_frames)) {
- /* merge the tables, order by frame offset */
- for (i = 0; i < info->frame_count; i++) {
-
- /* get the table number of the smallest frame offset */
- min_offset_table = -1;
- min_offset = 0; /* value not used (avoid compiler warnings) */
- for (j = 0; j < sample_table_count; j++) {
- if ((sample_table_indices[j] < info->frame_count) &&
- (min_offset_table == -1 ||
- (sample_tables[j].frames[sample_table_indices[j]].offset <
- min_offset) ) ) {
- min_offset_table = j;
- min_offset = sample_tables[j].frames[sample_table_indices[j]].offset;
- }
+ info->audio_trak = i;
+ max_audio_frames = info->traks[i].frame_count;
}
-
- info->frames[i] =
- sample_tables[min_offset_table].frames[sample_table_indices[min_offset_table]];
- sample_table_indices[min_offset_table]++;
- if (sample_table_indices[min_offset_table] >=
- sample_tables[min_offset_table].frame_count)
- sample_table_indices[min_offset_table] = info->frame_count;
- }
-
- debug_frame_table(" qt: final frame table contains %d frames\n",
- info->frame_count);
- for (i = 0; i < info->frame_count; i++)
- debug_frame_table(" %d: %s frame @ offset 0x%llX, 0x%X bytes, pts = %lld%s\n",
- i,
- (info->frames[i].type == MEDIA_VIDEO) ? "video" :
- (info->frames[i].type == MEDIA_AUDIO) ? "audio" : "other",
- info->frames[i].offset,
- info->frames[i].size,
- info->frames[i].pts,
- (info->frames[i].keyframe) ? ", keyframe" : "");
-
- /* free the temporary tables on the way out */
- for (i = 0; i < sample_table_count; i++) {
- free(sample_tables[i].edit_list_table);
- free(sample_tables[i].chunk_offset_table);
- /* this pointer might have been set to -1 as a special case */
- if (sample_tables[i].sample_size_table != (void *)-1)
- free(sample_tables[i].sample_size_table);
- free(sample_tables[i].time_to_sample_table);
- free(sample_tables[i].sample_to_chunk_table);
- free(sample_tables[i].sync_sample_table);
- free(sample_tables[i].frames);
- free(sample_tables[i].stsd);
}
- free(sample_tables);
- free(sample_table_indices);
}
static qt_error open_qt_file(qt_info *info, input_plugin_t *input) {
@@ -1742,7 +1598,6 @@ static qt_error open_qt_file(qt_info *info, input_plugin_t *input) {
return info->last_error;
}
info->moov_first_offset = moov_atom_offset;
- info->moov_last_offset = info->moov_first_offset + moov_atom_size;
moov_atom = (unsigned char *)malloc(moov_atom_size);
if (moov_atom == NULL) {
@@ -1839,55 +1694,121 @@ static int demux_qt_send_chunk(demux_plugin_t *this_gen) {
buf_element_t *buf = NULL;
unsigned int i, j;
unsigned int remaining_sample_bytes;
- int edit_list_compensation = 0;
int frame_duration;
int first_buf;
+ qt_trak *video_trak = NULL;
+ qt_trak *audio_trak = NULL;
+ int dispatch_audio; /* boolean for deciding which trak to dispatch */
+ int64_t pts_diff;
+
+ if (this->qt->video_trak != -1) {
+ video_trak = &this->qt->traks[this->qt->video_trak];
+ }
+ if (this->qt->audio_trak != -1) {
+ audio_trak = &this->qt->traks[this->qt->audio_trak];
+ }
- i = this->current_frame;
+ /* check if it is time to seek */
+ if (this->qt->seek_flag) {
+ this->qt->seek_flag = 0;
- /* if there is an incongruency between last and current sample, it
- * must be time to send a new pts */
- if (this->last_frame + 1 != this->current_frame) {
- /* send new pts */
- xine_demux_control_newpts(this->stream, this->qt->frames[i].pts,
- this->qt->frames[i].pts ? BUF_FLAG_SEEK : 0);
+ /* if audio is present, send pts of current audio frame, otherwise
+ * send current video frame pts */
+ if (audio_trak)
+ xine_demux_control_newpts(this->stream,
+ audio_trak->frames[audio_trak->current_frame].pts,
+ audio_trak->frames[audio_trak->current_frame].pts ? BUF_FLAG_SEEK : 0);
+ else
+ xine_demux_control_newpts(this->stream,
+ video_trak->frames[video_trak->current_frame].pts,
+ video_trak->frames[video_trak->current_frame].pts ? BUF_FLAG_SEEK : 0);
}
- this->last_frame = this->current_frame;
- this->current_frame++;
+ /* Decide the trak from which to dispatch a frame. Policy: Dispatch
+ * the frames in offset order as much as possible. If the pts difference
+ * between the current frames from the audio and video tables is too
+ * wide, make an exception. This exception deals with non-interleaved
+ * Quicktime files. */
+ if (!audio_trak && !video_trak) {
- /* check if all the samples have been sent */
- if (i >= this->qt->frame_count) {
+ /* something is really wrong if this case is reached */
this->status = DEMUX_FINISHED;
return this->status;
- }
- /* check if we're only sending audio samples until the next keyframe */
- if ((this->waiting_for_keyframe) &&
- (this->qt->frames[i].type == MEDIA_VIDEO)) {
- if (this->qt->frames[i].keyframe) {
- this->waiting_for_keyframe = 0;
- } else {
- /* move on to the next sample */
+ } else if (!audio_trak) {
+
+ /* only video is present */
+ dispatch_audio = 0;
+ if (video_trak->current_frame >= video_trak->frame_count) {
+ this->status = DEMUX_FINISHED;
return this->status;
}
+
+ } else if (!video_trak) {
+
+ /* only audio is present */
+ dispatch_audio = 1;
+ if (audio_trak->current_frame >= audio_trak->frame_count) {
+ this->status = DEMUX_FINISHED;
+ return this->status;
+ }
+
+ } else {
+
+ /* both audio and video are present; start making some tough choices */
+
+ /* check the frame count limits */
+ if ((audio_trak->current_frame >= audio_trak->frame_count) &&
+ (video_trak->current_frame >= video_trak->frame_count)) {
+
+ this->status = DEMUX_FINISHED;
+ return this->status;
+
+ } else if (video_trak->current_frame >= video_trak->frame_count) {
+
+ dispatch_audio = 1;
+
+ } else if (audio_trak->current_frame >= audio_trak->frame_count) {
+
+ dispatch_audio = 0;
+
+ } else {
+
+ /* at this point, it is certain that both traks still have frames
+ * yet to be dispatched */
+ pts_diff = audio_trak->frames[audio_trak->current_frame].pts;
+ pts_diff -= video_trak->frames[video_trak->current_frame].pts;
+
+ if (pts_diff > MAX_PTS_DIFF) {
+ /* if diff is +max_diff, audio is too far ahead of video */
+ dispatch_audio = 0;
+ } else if (pts_diff < -MAX_PTS_DIFF) {
+ /* if diff is -max_diff, video is too far ahead of audio */
+ dispatch_audio = 1;
+ } else if (audio_trak->frames[audio_trak->current_frame].offset <
+ video_trak->frames[video_trak->current_frame].offset) {
+ /* pts diff is not too wide, decide based on earlier offset */
+ dispatch_audio = 1;
+ } else {
+ dispatch_audio = 0;
+ }
+ }
}
- if (this->qt->frames[i].type == MEDIA_VIDEO) {
- remaining_sample_bytes = this->qt->frames[i].size;
- this->input->seek(this->input, this->qt->frames[i].offset,
+ if (!dispatch_audio) {
+ i = video_trak->current_frame++;
+ remaining_sample_bytes = video_trak->frames[i].size;
+ this->input->seek(this->input, video_trak->frames[i].offset,
SEEK_SET);
- /* frame duration is the pts diff between this video frame and
- * the next video frame (so search for the next video frame) */
- frame_duration = 0;
- j = i;
- while (++j < this->qt->frame_count) {
- if (this->qt->frames[j].type == MEDIA_VIDEO) {
- frame_duration =
- this->qt->frames[j].pts - this->qt->frames[i].pts;
- break;
- }
+ if (i + 1 < video_trak->frame_count) {
+ /* frame duration is the pts diff between this video frame and
+ * the next video frame */
+ frame_duration = video_trak->frames[i + 1].pts;
+ frame_duration -= video_trak->frames[i].pts;
+ } else {
+ /* give the last frame some fixed duration */
+ frame_duration = 12000;
}
/* Due to the edit lists, some successive frames have the same pts
@@ -1896,28 +1817,28 @@ static int demux_qt_send_chunk(demux_plugin_t *this_gen) {
* to compensate. */
if (!frame_duration) {
frame_duration = 1;
- edit_list_compensation++;
+ video_trak->properties.video.edit_list_compensation++;
} else {
- frame_duration -= edit_list_compensation;
- edit_list_compensation = 0;
+ frame_duration -= video_trak->properties.video.edit_list_compensation;
+ video_trak->properties.video.edit_list_compensation = 0;
}
this->stream->stream_info[XINE_STREAM_INFO_FRAME_DURATION] =
frame_duration;
- debug_video_demux(" qt: sending off frame %d (video) %d bytes, %lld pts, duration %d\n",
+ debug_video_demux(" qt: sending off video frame %d from offset 0x%llX, %d bytes, %lld pts\n",
i,
- this->qt->frames[i].size,
- this->qt->frames[i].pts,
- frame_duration);
+ video_trak->frames[i].offset,
+ video_trak->frames[i].size,
+ video_trak->frames[i].pts);
while (remaining_sample_bytes) {
buf = this->video_fifo->buffer_pool_alloc (this->video_fifo);
- buf->type = this->qt->video_type;
- buf->extra_info->input_pos = this->qt->frames[i].offset - this->data_start;
+ buf->type = video_trak->properties.video.codec_buftype;
+ buf->extra_info->input_pos = video_trak->frames[i].offset - this->data_start;
buf->extra_info->input_length = this->data_size;
- buf->extra_info->input_time = this->qt->frames[i].pts / 90;
- buf->pts = this->qt->frames[i].pts;
+ buf->extra_info->input_time = video_trak->frames[i].pts / 90;
+ buf->pts = video_trak->frames[i].pts;
buf->decoder_flags |= BUF_FLAG_FRAMERATE;
buf->decoder_info[0] = frame_duration;
@@ -1935,7 +1856,7 @@ static int demux_qt_send_chunk(demux_plugin_t *this_gen) {
break;
}
- if (this->qt->frames[i].keyframe)
+ if (video_trak->frames[i].keyframe)
buf->decoder_flags |= BUF_FLAG_KEYFRAME;
if (!remaining_sample_bytes)
buf->decoder_flags |= BUF_FLAG_FRAME_END;
@@ -1943,23 +1864,24 @@ static int demux_qt_send_chunk(demux_plugin_t *this_gen) {
this->video_fifo->put(this->video_fifo, buf);
}
- } else if ((this->qt->frames[i].type == MEDIA_AUDIO) &&
- this->audio_fifo && this->qt->audio_type) {
+ } else {
/* load an audio sample and packetize it */
- remaining_sample_bytes = this->qt->frames[i].size;
- this->input->seek(this->input, this->qt->frames[i].offset,
+ i = audio_trak->current_frame++;
+ remaining_sample_bytes = audio_trak->frames[i].size;
+ this->input->seek(this->input, audio_trak->frames[i].offset,
SEEK_SET);
- debug_audio_demux(" qt: sending off frame %d (audio) %d bytes, %lld pts\n",
+ debug_audio_demux(" qt: sending off audio frame %d from offset 0x%llX, %d bytes, %lld pts\n",
i,
- this->qt->frames[i].size,
- this->qt->frames[i].pts);
+ audio_trak->frames[i].offset,
+ audio_trak->frames[i].size,
+ audio_trak->frames[i].pts);
first_buf = 1;
while (remaining_sample_bytes) {
buf = this->audio_fifo->buffer_pool_alloc (this->audio_fifo);
- buf->type = this->qt->audio_type;
- buf->extra_info->input_pos = this->qt->frames[i].offset - this->data_start;
+ buf->type = audio_trak->properties.audio.codec_buftype;
+ buf->extra_info->input_pos = audio_trak->frames[i].offset - this->data_start;
buf->extra_info->input_length = this->data_size;
/* The audio chunk is often broken up into multiple 8K buffers when
* it is sent to the audio decoder. Only attach the proper timestamp
@@ -1970,16 +1892,16 @@ static int demux_qt_send_chunk(demux_plugin_t *this_gen) {
if ((buf->type == BUF_AUDIO_LPCM_BE) ||
(buf->type == BUF_AUDIO_LPCM_LE)) {
if (first_buf) {
- buf->extra_info->input_time = this->qt->frames[i].pts / 90;
- buf->pts = this->qt->frames[i].pts;
+ buf->extra_info->input_time = audio_trak->frames[i].pts / 90;
+ buf->pts = audio_trak->frames[i].pts;
first_buf = 0;
} else {
buf->extra_info->input_time = 0;
buf->pts = 0;
}
} else {
- buf->extra_info->input_time = this->qt->frames[i].pts / 90;
- buf->pts = this->qt->frames[i].pts;
+ buf->extra_info->input_time = audio_trak->frames[i].pts / 90;
+ buf->pts = audio_trak->frames[i].pts;
}
if (remaining_sample_bytes > buf->max_size)
@@ -1997,9 +1919,9 @@ static int demux_qt_send_chunk(demux_plugin_t *this_gen) {
/* Special case alert: If this is signed, 8-bit data, transform
* the data to unsigned. */
- if ((this->qt->audio_bits == 8) &&
- ((this->qt->audio_codec == TWOS_FOURCC) ||
- (this->qt->audio_codec == SOWT_FOURCC)))
+ if ((audio_trak->properties.audio.bits == 8) &&
+ ((audio_trak->properties.audio.codec_fourcc == TWOS_FOURCC) ||
+ (audio_trak->properties.audio.codec_fourcc == SOWT_FOURCC)))
for (j = 0; j < buf->size; j++)
buf->content[j] += 0x80;
@@ -2010,6 +1932,7 @@ static int demux_qt_send_chunk(demux_plugin_t *this_gen) {
this->audio_fifo->put(this->audio_fifo, buf);
}
}
+
return this->status;
}
@@ -2017,54 +1940,115 @@ static void demux_qt_send_headers(demux_plugin_t *this_gen) {
demux_qt_t *this = (demux_qt_t *) this_gen;
buf_element_t *buf;
+ qt_trak *video_trak = NULL;
+ qt_trak *audio_trak = NULL;
+
+ /* for deciding data start and data size */
+ int64_t first_video_offset = -1;
+ int64_t last_video_offset = -1;
+ int64_t first_audio_offset = -1;
+ int64_t last_audio_offset = -1;
this->video_fifo = this->stream->video_fifo;
this->audio_fifo = this->stream->audio_fifo;
this->status = DEMUX_OK;
- this->data_start = this->qt->frames[0].offset;
- this->data_size =
- this->qt->frames[this->qt->frame_count - 1].offset +
- this->qt->frames[this->qt->frame_count - 1].size -
- this->data_start;
+ /* figure out where the data begins and ends */
+ if (this->qt->video_trak != -1) {
+ video_trak = &this->qt->traks[this->qt->video_trak];
+ first_video_offset = video_trak->frames[0].offset;
+ last_video_offset = video_trak->frames[video_trak->frame_count - 1].size +
+ video_trak->frames[video_trak->frame_count - 1].offset;
+ }
+ if (this->qt->audio_trak != -1) {
+ audio_trak = &this->qt->traks[this->qt->audio_trak];
+ first_audio_offset = audio_trak->frames[0].offset;
+ last_audio_offset = audio_trak->frames[audio_trak->frame_count - 1].size +
+ audio_trak->frames[audio_trak->frame_count - 1].offset;
+ }
+
+ if (first_video_offset < first_audio_offset)
+ this->data_start = first_video_offset;
+ else
+ this->data_start = first_audio_offset;
+
+ if (last_video_offset > last_audio_offset)
+ this->data_size = last_video_offset - this->data_size;
+ else
+ this->data_size = last_audio_offset - this->data_size;
+
+ /* sort out the A/V information */
+ if (this->qt->video_trak != -1) {
+
+ this->bih.biSize = sizeof(this->bih);
+ this->bih.biWidth = video_trak->properties.video.width;
+ this->bih.biHeight = video_trak->properties.video.height;
+ this->bih.biBitCount = video_trak->properties.video.depth;
+
+ this->bih.biCompression = video_trak->properties.video.codec_fourcc;
+ video_trak->properties.video.codec_buftype =
+ fourcc_to_buf_video(this->bih.biCompression);
+
+ /* hack: workaround a fourcc clash! 'mpg4' is used by MS and Sorenson
+ * mpeg4 codecs (they are not compatible).
+ */
+ if( video_trak->properties.video.codec_buftype == BUF_VIDEO_MSMPEG4_V1 )
+ video_trak->properties.video.codec_buftype = BUF_VIDEO_MPEG4;
+
+ if( !video_trak->properties.video.codec_buftype &&
+ video_trak->properties.video.codec_fourcc )
+ video_trak->properties.video.codec_buftype = BUF_VIDEO_UNKNOWN;
+
+ this->stream->stream_info[XINE_STREAM_INFO_HAS_VIDEO] = 1;
+ this->stream->stream_info[XINE_STREAM_INFO_VIDEO_WIDTH] =
+ this->bih.biWidth;
+ this->stream->stream_info[XINE_STREAM_INFO_VIDEO_HEIGHT] =
+ this->bih.biHeight;
+ this->stream->stream_info[XINE_STREAM_INFO_VIDEO_FOURCC] =
+ video_trak->properties.video.codec_fourcc;
- this->bih.biSize = sizeof(this->bih);
- this->bih.biWidth = this->qt->video_width;
- this->bih.biHeight = this->qt->video_height;
- this->bih.biBitCount = this->qt->video_depth;
+ } else {
- this->bih.biCompression = this->qt->video_codec;
- this->qt->video_type = fourcc_to_buf_video(this->bih.biCompression);
+ memset(&this->bih, 0, sizeof(this->bih));
+ this->bih.biSize = sizeof(this->bih);
+ this->stream->stream_info[XINE_STREAM_INFO_HAS_VIDEO] = 0;
+ this->stream->stream_info[XINE_STREAM_INFO_VIDEO_WIDTH] = 0;
+ this->stream->stream_info[XINE_STREAM_INFO_VIDEO_HEIGHT] = 0;
+ this->stream->stream_info[XINE_STREAM_INFO_VIDEO_FOURCC] = 0;
- /* hack: workaround a fourcc clash! 'mpg4' is used by MS and Sorenson
- * mpeg4 codecs (they are not compatible).
- */
- if( this->qt->video_type == BUF_VIDEO_MSMPEG4_V1 )
- this->qt->video_type = BUF_VIDEO_MPEG4;
-
- if( !this->qt->video_type && this->qt->video_codec )
- this->qt->video_type = BUF_VIDEO_UNKNOWN;
+ }
- this->qt->audio_type = formattag_to_buf_audio(this->qt->audio_codec);
+ if (this->qt->audio_trak != -1) {
- if( !this->qt->audio_type && this->qt->audio_codec )
- this->qt->audio_type = BUF_AUDIO_UNKNOWN;
-
- /* load stream information */
- this->stream->stream_info[XINE_STREAM_INFO_HAS_VIDEO] =
- (this->qt->video_type) ? 1 : 0;
- this->stream->stream_info[XINE_STREAM_INFO_HAS_AUDIO] =
- (this->qt->audio_type) ? 1 : 0;
- this->stream->stream_info[XINE_STREAM_INFO_VIDEO_WIDTH] = this->bih.biWidth;
- this->stream->stream_info[XINE_STREAM_INFO_VIDEO_HEIGHT] = this->bih.biHeight;
- this->stream->stream_info[XINE_STREAM_INFO_AUDIO_CHANNELS] =
- this->qt->audio_channels;
- this->stream->stream_info[XINE_STREAM_INFO_AUDIO_SAMPLERATE] =
- this->qt->audio_sample_rate;
- this->stream->stream_info[XINE_STREAM_INFO_AUDIO_BITS] =
- this->qt->audio_bits;
+ audio_trak->properties.audio.codec_buftype =
+ formattag_to_buf_audio(audio_trak->properties.audio.codec_fourcc);
+ if( !audio_trak->properties.audio.codec_buftype &&
+ audio_trak->properties.audio.codec_fourcc )
+ audio_trak->properties.audio.codec_buftype = BUF_AUDIO_UNKNOWN;
+
+ this->stream->stream_info[XINE_STREAM_INFO_HAS_AUDIO] = 1;
+ this->stream->stream_info[XINE_STREAM_INFO_AUDIO_CHANNELS] =
+ audio_trak->properties.audio.channels;
+ this->stream->stream_info[XINE_STREAM_INFO_AUDIO_SAMPLERATE] =
+ audio_trak->properties.audio.sample_rate;
+ this->stream->stream_info[XINE_STREAM_INFO_AUDIO_BITS] =
+ audio_trak->properties.audio.bits;
+ this->stream->stream_info[XINE_STREAM_INFO_AUDIO_FOURCC] =
+ audio_trak->properties.audio.codec_fourcc;
+
+ } else {
+
+ this->stream->stream_info[XINE_STREAM_INFO_HAS_AUDIO] = 0;
+ this->stream->stream_info[XINE_STREAM_INFO_AUDIO_CHANNELS] = 0;
+ this->stream->stream_info[XINE_STREAM_INFO_AUDIO_SAMPLERATE] = 0;
+ this->stream->stream_info[XINE_STREAM_INFO_AUDIO_BITS] = 0;
+ this->stream->stream_info[XINE_STREAM_INFO_AUDIO_FOURCC] = 0;
+
+ }
+
+ /* copy over the meta information like artist and title */
if (this->qt->copyright)
this->stream->meta_info[XINE_META_INFO_ARTIST] =
strdup(this->qt->copyright);
@@ -2075,76 +2059,78 @@ static void demux_qt_send_headers(demux_plugin_t *this_gen) {
this->stream->meta_info[XINE_META_INFO_COMMENT] =
strdup(this->qt->comment);
- this->stream->stream_info[XINE_STREAM_INFO_VIDEO_FOURCC] =
- this->qt->video_codec;
- this->stream->stream_info[XINE_STREAM_INFO_AUDIO_FOURCC] =
- this->qt->audio_codec;
-
/* send start buffers */
xine_demux_control_start(this->stream);
/* send init info to decoders */
- buf = this->video_fifo->buffer_pool_alloc (this->video_fifo);
- buf->decoder_flags = BUF_FLAG_HEADER;
- buf->decoder_info[0] = 0;
- buf->decoder_info[1] = 3000; /* initial video_step */
- memcpy(buf->content, &this->bih, sizeof(this->bih));
- buf->size = sizeof(this->bih);
- buf->type = this->qt->video_type;
- this->video_fifo->put (this->video_fifo, buf);
-
- /* send header info to decoder. some mpeg4 streams need this */
- if( this->qt->video_decoder_config ) {
+ if (video_trak &&
+ (video_trak->properties.video.codec_buftype)) {
buf = this->video_fifo->buffer_pool_alloc (this->video_fifo);
- buf->type = this->qt->video_type;
- buf->size = this->qt->video_decoder_config_len;
- buf->content = this->qt->video_decoder_config;
+ buf->decoder_flags = BUF_FLAG_HEADER;
+ buf->decoder_info[0] = 0;
+ /* initial video step; not necessary since each QT frame has its own
+ * duration, but set it non-zero as a matter of custom */
+ buf->decoder_info[1] = 3000;
+ memcpy(buf->content, &this->bih, sizeof(this->bih));
+ buf->size = sizeof(this->bih);
+ buf->type = video_trak->properties.video.codec_buftype;
this->video_fifo->put (this->video_fifo, buf);
- }
+
+ /* send header info to decoder. some mpeg4 streams need this */
+ if( video_trak->decoder_config ) {
+ buf = this->video_fifo->buffer_pool_alloc (this->video_fifo);
+ buf->type = video_trak->properties.video.codec_buftype;
+ buf->size = video_trak->decoder_config_len;
+ buf->content = video_trak->decoder_config;
+ this->video_fifo->put (this->video_fifo, buf);
+ }
+
+ /* send off the palette, if there is one */
+ if (video_trak->properties.video.palette_count) {
+ buf = this->video_fifo->buffer_pool_alloc (this->video_fifo);
+ buf->decoder_flags = BUF_FLAG_SPECIAL;
+ buf->decoder_info[1] = BUF_SPECIAL_PALETTE;
+ buf->decoder_info[2] = video_trak->properties.video.palette_count;
+ buf->decoder_info_ptr[2] = &video_trak->properties.video.palette;
+ buf->size = 0;
+ buf->type = video_trak->properties.video.codec_buftype;
+ this->video_fifo->put (this->video_fifo, buf);
+ }
- /* send off the palette, if there is one */
- if (this->qt->palette_count) {
+ /* send stsd to the decoder */
buf = this->video_fifo->buffer_pool_alloc (this->video_fifo);
buf->decoder_flags = BUF_FLAG_SPECIAL;
- buf->decoder_info[1] = BUF_SPECIAL_PALETTE;
- buf->decoder_info[2] = this->qt->palette_count;
- buf->decoder_info_ptr[2] = &this->qt->palette;
+ buf->decoder_info[1] = BUF_SPECIAL_STSD_ATOM;
+ buf->decoder_info[2] = video_trak->stsd_size;
+ buf->decoder_info_ptr[2] = video_trak->stsd;
buf->size = 0;
- buf->type = this->qt->video_type;
+ buf->type = video_trak->properties.video.codec_buftype;
this->video_fifo->put (this->video_fifo, buf);
}
- /* send stsd to the decoder */
- buf = this->video_fifo->buffer_pool_alloc (this->video_fifo);
- buf->decoder_flags = BUF_FLAG_SPECIAL;
- buf->decoder_info[1] = BUF_SPECIAL_STSD_ATOM;
- buf->decoder_info[2] = this->qt->video_stsd_size;
- buf->decoder_info_ptr[2] = this->qt->video_stsd;
- buf->size = 0;
- buf->type = this->qt->video_type;
- this->video_fifo->put (this->video_fifo, buf);
-
+ if ((this->qt->audio_trak != -1) &&
+ (audio_trak->properties.audio.codec_buftype) &&
+ this->audio_fifo) {
- if (this->audio_fifo && this->qt->audio_type) {
buf = this->audio_fifo->buffer_pool_alloc (this->audio_fifo);
- buf->type = this->qt->audio_type;
+ buf->type = audio_trak->properties.audio.codec_buftype;
buf->decoder_flags = BUF_FLAG_HEADER;
buf->decoder_info[0] = 0;
- buf->decoder_info[1] = this->qt->audio_sample_rate;
- buf->decoder_info[2] = this->qt->audio_bits;
- buf->decoder_info[3] = this->qt->audio_channels;
- buf->content = (void *)&this->qt->wave;
- buf->size = sizeof(this->qt->wave);
+ buf->decoder_info[1] = audio_trak->properties.audio.sample_rate;
+ buf->decoder_info[2] = audio_trak->properties.audio.bits;
+ buf->decoder_info[3] = audio_trak->properties.audio.channels;
+ buf->content = (void *)&audio_trak->properties.audio.wave;
+ buf->size = sizeof(audio_trak->properties.audio.wave);
this->audio_fifo->put (this->audio_fifo, buf);
- if( this->qt->audio_decoder_config ) {
+ if( audio_trak->decoder_config ) {
buf = this->audio_fifo->buffer_pool_alloc (this->audio_fifo);
- buf->type = this->qt->audio_type;
+ buf->type = audio_trak->properties.audio.codec_buftype;
buf->size = 0;
buf->decoder_flags = BUF_FLAG_SPECIAL;
buf->decoder_info[1] = BUF_SPECIAL_DECODER_CONFIG;
- buf->decoder_info[2] = this->qt->audio_decoder_config_len;
- buf->decoder_info_ptr[2] = this->qt->audio_decoder_config;
+ buf->decoder_info[2] = audio_trak->decoder_config_len;
+ buf->decoder_info_ptr[2] = audio_trak->decoder_config;
this->audio_fifo->put (this->audio_fifo, buf);
}
@@ -2152,49 +2138,44 @@ static void demux_qt_send_headers(demux_plugin_t *this_gen) {
buf = this->audio_fifo->buffer_pool_alloc (this->audio_fifo);
buf->decoder_flags = BUF_FLAG_SPECIAL;
buf->decoder_info[1] = BUF_SPECIAL_STSD_ATOM;
- buf->decoder_info[2] = this->qt->audio_stsd_size;
- buf->decoder_info_ptr[2] = this->qt->audio_stsd;
+ buf->decoder_info[2] = audio_trak->stsd_size;
+ buf->decoder_info_ptr[2] = audio_trak->stsd;
buf->size = 0;
- buf->type = this->qt->audio_type;
+ buf->type = audio_trak->properties.audio.codec_buftype;
this->audio_fifo->put (this->audio_fifo, buf);
}
}
-static int demux_qt_seek (demux_plugin_t *this_gen,
- off_t start_pos, int start_time) {
- demux_qt_t *this = (demux_qt_t *) this_gen;
+/* support function that performs a binary seek on a trak; returns the
+ * demux status */
+static int binary_seek(qt_trak *trak, off_t start_pos, int start_time) {
int best_index;
int left, middle, right;
int found;
- int64_t keyframe_pts;
-
- this->waiting_for_keyframe = 0;
/* perform a binary search on the sample table, testing the offset
- * boundaries first */
+ * boundaries first; offset request has precedent over time request */
if (start_pos) {
- if (start_pos <= this->qt->frames[0].offset)
+ if (start_pos <= trak->frames[0].offset)
best_index = 0;
- else if (start_pos >= this->qt->frames[this->qt->frame_count - 1].offset) {
- this->status = DEMUX_FINISHED;
- return this->status;
- } else {
+ else if (start_pos >= trak->frames[trak->frame_count - 1].offset)
+ return DEMUX_FINISHED;
+ else {
left = 0;
- right = this->qt->frame_count - 1;
+ right = trak->frame_count - 1;
found = 0;
while (!found) {
- middle = (left + right) / 2;
- if ((start_pos >= this->qt->frames[middle].offset) &&
- (start_pos <= this->qt->frames[middle].offset +
- this->qt->frames[middle].size)) {
- found = 1;
- } else if (start_pos < this->qt->frames[middle].offset) {
- right = middle;
- } else {
- left = middle;
+ middle = (left + right + 1) / 2;
+ if ((start_pos >= trak->frames[middle].offset) &&
+ (start_pos < trak->frames[middle + 1].offset)) {
+ found = 1;
+ } else if (start_pos < trak->frames[middle].offset) {
+ right = middle - 1;
+ } else {
+ left = middle;
}
}
@@ -2203,17 +2184,16 @@ static int demux_qt_seek (demux_plugin_t *this_gen,
} else {
int64_t pts = 90000 * start_time;
- if (pts <= this->qt->frames[0].pts)
+ if (pts <= trak->frames[0].pts)
best_index = 0;
- else if (pts >= this->qt->frames[this->qt->frame_count - 1].pts) {
- this->status = DEMUX_FINISHED;
- return this->status;
- } else {
+ else if (pts >= trak->frames[trak->frame_count - 1].pts)
+ return DEMUX_FINISHED;
+ else {
left = 0;
- right = this->qt->frame_count - 1;
+ right = trak->frame_count - 1;
do {
middle = (left + right + 1) / 2;
- if (pts < this->qt->frames[middle].pts) {
+ if (pts < trak->frames[middle].pts) {
right = (middle - 1);
} else {
left = middle;
@@ -2224,36 +2204,62 @@ static int demux_qt_seek (demux_plugin_t *this_gen,
}
}
- /* search back in the table for the nearest keyframe */
- while (best_index) {
- if (this->qt->frames[best_index].keyframe) {
- break;
- }
- best_index--;
+ trak->current_frame = best_index;
+ return DEMUX_OK;
+}
+
+static int demux_qt_seek (demux_plugin_t *this_gen,
+ off_t start_pos, int start_time) {
+
+ demux_qt_t *this = (demux_qt_t *) this_gen;
+ qt_trak *video_trak = NULL;
+ qt_trak *audio_trak = NULL;
+
+ int64_t keyframe_pts;
+
+ /* if there is a video trak, position it as close as possible to the
+ * requested position */
+ if (this->qt->video_trak != -1) {
+ video_trak = &this->qt->traks[this->qt->video_trak];
+ this->status = binary_seek(video_trak, start_pos, start_time);
+ if (this->status != DEMUX_OK)
+ return this->status;
}
+ if (this->qt->audio_trak != -1) {
+ audio_trak = &this->qt->traks[this->qt->audio_trak];
+ this->status = binary_seek(audio_trak, start_pos, start_time);
+ if (this->status != DEMUX_OK)
+ return this->status;
+ }
+
+ /* search back in the video trak for the nearest keyframe */
+ if (video_trak)
+ while (video_trak->current_frame) {
+ if (video_trak->frames[video_trak->current_frame].keyframe) {
+ break;
+ }
+ video_trak->current_frame--;
+ }
+
/* not done yet; now that the nearest keyframe has been found, seek
* back to the first audio frame that has a pts less than or equal to
- * that of the keyframe */
- this->waiting_for_keyframe = 1;
- keyframe_pts = this->qt->frames[best_index].pts;
- while (best_index) {
- if ((this->qt->frames[best_index].type == MEDIA_AUDIO) &&
- (this->qt->frames[best_index].pts < keyframe_pts)) {
- break;
+ * that of the keyframe; do not go through with this process there is
+ * no video trak */
+ if (audio_trak && video_trak) {
+ keyframe_pts = video_trak->frames[video_trak->current_frame].pts;
+ while (audio_trak->current_frame) {
+ if (audio_trak->frames[audio_trak->current_frame].pts < keyframe_pts) {
+ break;
+ }
+ audio_trak->current_frame--;
}
- best_index--;
}
- this->current_frame = best_index;
+ this->qt->seek_flag = 1;
this->status = DEMUX_OK;
+ xine_demux_flush_engine(this->stream);
- if( !this->stream->demux_thread_running ) {
- this->last_frame = 0;
- } else {
- xine_demux_flush_engine(this->stream);
- }
-
return this->status;
}