diff options
author | Mike Melanson <mike@multimedia.cx> | 2003-02-16 05:10:23 +0000 |
---|---|---|
committer | Mike Melanson <mike@multimedia.cx> | 2003-02-16 05:10:23 +0000 |
commit | cc2ebdeb2ca1b13d5cf792799bc4cc3b863a21d5 (patch) | |
tree | 46995687cf6f5e6dddcffe7b92c9bfb4511327a4 | |
parent | e4c1353c5ca297664925742f42947b05e2c64e1b (diff) | |
download | xine-lib-cc2ebdeb2ca1b13d5cf792799bc4cc3b863a21d5.tar.gz xine-lib-cc2ebdeb2ca1b13d5cf792799bc4cc3b863a21d5.tar.bz2 |
QT mega-fix: This commit reworks the operation of the Quicktime demuxer
so that individual audio and video traks are maintained. If multiple
audio or video traks are found, the best one is selected.
Non-interleaved and poorly-interleaved QT files are also handled.
CVS patchset: 4169
CVS date: 2003/02/16 05:10:23
-rw-r--r-- | src/demuxers/demux_qt.c | 1518 |
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; } |