diff options
Diffstat (limited to 'src')
-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; } |