diff options
-rw-r--r-- | src/demuxers/demux_qt.c | 5190 |
1 files changed, 1118 insertions, 4072 deletions
diff --git a/src/demuxers/demux_qt.c b/src/demuxers/demux_qt.c index 6c5b44cca..c545e31e4 100644 --- a/src/demuxers/demux_qt.c +++ b/src/demuxers/demux_qt.c @@ -1,34 +1,41 @@ /* * Copyright (C) 2001-2002 the xine project - * + * * This file is part of xine, a free video player. - * + * * xine is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. - * + * * xine is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA * - * $Id: demux_qt.c,v 1.33 2002/05/25 19:19:17 siggi Exp $ + * Quicktime File Demuxer by Mike Melanson (melanson@pcisys.net) + * based on a Quicktime parsing experiment entitled 'lazyqt' * - * demultiplexer for mpeg-4 system (aka quicktime) streams, based on: + * Ideally, more documentation is forthcoming, but in the meantime: + * functional flow: + * create_qt_info + * open_qt_file + * parse_moov_atom + * parse_mvhd_atom + * parse_minf_atom + * build_frame_table + * free_qt_info * - * openquicktime.c - * - * This file is part of OpenQuicktime, a free QuickTime library. - * - * Based on QT4Linux by Adam Williams. + * $Id: demux_qt.c,v 1.34 2002/06/02 17:01:27 tmmm Exp $ * */ +#define HAVE_LIBZ + #ifdef HAVE_CONFIG_H #include "config.h" @@ -41,3829 +48,1118 @@ #include <string.h> #include <stdlib.h> #include <ctype.h> -#include <zlib.h> #include "xine_internal.h" #include "xineutils.h" #include "demux.h" #include "buffer.h" +#include "bswap.h" -#define WINE_TYPEDEFS_ONLY -#include "libw32dll/wine/avifmt.h" -#include "libw32dll/wine/windef.h" -#include "libw32dll/wine/vfw.h" -#include "libw32dll/wine/mmreg.h" - -/* -#define LOG -*/ - -/* -#define DBG_QT -*/ - -#define VALID_ENDS "mov,mp4" - -/* OpenQuicktime Codec Parameter Types */ -#define QUICKTIME_UNKNOWN_PARAMETER -1 -#define QUICKTIME_STRING_PARAMETER 0 -#define QUICKTIME_BOOLEAN_PARAMETER 1 -#define QUICKTIME_INTEGER_PARAMETER 4 -#define QUICKTIME_UNSIGNED_INTEGER_PARAMETER 5 -#define QUICKTIME_DOUBLE_PARAMETER 8 - -#define HEADER_LENGTH 8 -#define MAXTRACKS 1024 - -typedef int64_t longest; -typedef uint64_t ulongest; - -#ifdef DBG_QT -int debug_fh; +#ifdef HAVE_LIBZ +#include <zlib.h> +#else +#warning: No zlib support compiled into QT demuxer (no support for compressed headers) #endif -/* -typedef __s64 longest; -typedef __u64 ulongest; -*/ - -typedef struct { - longest start; /* byte start in file */ - longest end; /* byte endpoint in file */ - longest size; /* byte size for writing */ - int use_64; /* Use 64 bit header */ - unsigned char type[4]; -} quicktime_atom_t; - -typedef struct { - float values[9]; -} quicktime_matrix_t; - - -typedef struct { - int version; - long flags; - unsigned long creation_time; - unsigned long modification_time; - int track_id; - long reserved1; - long duration; - char reserved2[8]; - int layer; - int alternate_group; - float volume; - long reserved3; - quicktime_matrix_t matrix; - float track_width; - float track_height; -} quicktime_tkhd_t; - - -typedef struct { - long seed; - long flags; - long size; - short int *alpha; - short int *red; - short int *green; - short int *blue; -} quicktime_ctab_t; +typedef unsigned int qt_atom; +#define BE_16(x) (be2me_16(*(unsigned short *)(x))) +#define BE_32(x) (be2me_32(*(unsigned int *)(x))) +#define QT_ATOM( ch0, ch1, ch2, ch3 ) \ + ( (long)(unsigned char)(ch3) | ( (long)(unsigned char)(ch2) << 8 ) | \ + ( (long)(unsigned char)(ch1) << 16 ) | ( (long)(unsigned char)(ch0) << 24 ) ) -/* ===================== sample table ======================== */ +/* top level atoms */ +#define FREE_ATOM QT_ATOM('f', 'r', 'e', 'e') +#define JUNK_ATOM QT_ATOM('j', 'u', 'n', 'k') +#define MDAT_ATOM QT_ATOM('m', 'd', 'a', 't') +#define MOOV_ATOM QT_ATOM('m', 'o', 'o', 'v') +#define PNOT_ATOM QT_ATOM('p', 'n', 'o', 't') +#define SKIP_ATOM QT_ATOM('s', 'k', 'i', 'p') +#define WIDE_ATOM QT_ATOM('w', 'i', 'd', 'e') -/* sample description */ +#define CMOV_ATOM QT_ATOM('c', 'm', 'o', 'v') -typedef struct { - int motion_jpeg_quantization_table; -} quicktime_mjqt_t; +#define MVHD_ATOM QT_ATOM('m', 'v', 'h', 'd') +#define MINF_ATOM QT_ATOM('m', 'i', 'n', 'f') +#define VMHD_ATOM QT_ATOM('v', 'm', 'h', 'd') +#define SMHD_ATOM QT_ATOM('s', 'm', 'h', 'd') -typedef struct { - int motion_jpeg_huffman_table; -} quicktime_mjht_t; +/* atoms in a sample table */ +#define STSD_ATOM QT_ATOM('s', 't', 's', 'd') +#define STSZ_ATOM QT_ATOM('s', 't', 's', 'z') +#define STSC_ATOM QT_ATOM('s', 't', 's', 'c') +#define STCO_ATOM QT_ATOM('s', 't', 'c', 'o') +#define STTS_ATOM QT_ATOM('s', 't', 't', 's') +#define STSS_ATOM QT_ATOM('s', 't', 's', 's') +#define CO64_ATOM QT_ATOM('c', 'o', '6', '4') +/* placeholder for cutting and pasting */ +#define _ATOM QT_ATOM('', '', '', '') -typedef struct { - char format[4]; - char reserved[6]; - int data_reference; - - /* common to audio and video */ - int version; - int revision; - char vendor[4]; - - /* video description */ - long temporal_quality; - long spatial_quality; - int width; - int height; - float dpi_horizontal; - float dpi_vertical; - longest data_size; - int frames_per_sample; - char compressor_name[32]; - int depth; - int ctab_id; - quicktime_ctab_t ctab; - float gamma; - int fields; /* 0, 1, or 2 */ - int field_dominance; /* 0 - unknown 1 - top first 2 - bottom first */ - quicktime_mjqt_t mjqt; - quicktime_mjht_t mjht; - - /* audio description */ - int channels; - int sample_size; - int compression_id; - int packet_size; - float sample_rate; - - /* audio description V1 */ - unsigned int samplesPerPacket; - unsigned int bytesPerPacket; - unsigned int bytesPerFrames; - unsigned int bytesPerSample; - char* private_data; - unsigned int private_data_size; -} quicktime_stsd_table_t; +#define ATOM_PREAMBLE_SIZE 8 +#define VALID_ENDS "mov,mp4,qt" +/* still needed, though it shouldn't be... */ typedef struct { - int version; - long flags; - long total_entries; - quicktime_stsd_table_t *table; -} quicktime_stsd_t; - + long biSize; + long biWidth; + long biHeight; + short biPlanes; + short biBitCount; + long biCompression; + long biSizeImage; + long biXPelsPerMeter; + long biYPelsPerMeter; + long biClrUsed; + long biClrImportant; +} BITMAPINFOHEADER; + +/* these are things that can go wrong */ +typedef enum { + QT_OK, + QT_FILE_READ_ERROR, + QT_NO_MEMORY, + QT_NOT_A_VALID_FILE, + QT_NO_MOOV_ATOM, + QT_NO_ZLIB, + QT_ZLIB_ERROR, + QT_HEADER_TROUBLE +} qt_error; + +/* there are other types but these are the ones we usually care about */ +typedef enum { + + MEDIA_AUDIO, + MEDIA_VIDEO, + MEDIA_OTHER + +} media_type; -/* time to sample */ typedef struct { - long sample_count; - long sample_duration; + media_type type; + int64_t offset; + unsigned int size; int64_t pts; -} quicktime_stts_table_t; - -typedef struct { - int version; - long flags; - long total_entries; - quicktime_stts_table_t *table; -} quicktime_stts_t; - - -/* sync sample */ -typedef struct { - long sample; -} quicktime_stss_table_t; - -typedef struct { - int version; - long flags; - long total_entries; - long entries_allocated; - quicktime_stss_table_t *table; -} quicktime_stss_t; - - -/* sample to chunk */ -typedef struct { - long chunk; - long samples; - long id; -} quicktime_stsc_table_t; - -typedef struct { - int version; - long flags; - long total_entries; - - long entries_allocated; - quicktime_stsc_table_t *table; -} quicktime_stsc_t; + int keyframe; +} qt_frame; - -/* sample size */ -typedef struct { - longest size; -} quicktime_stsz_table_t; - -typedef struct { - int version; - long flags; - longest sample_size; - long total_entries; - - long entries_allocated; /* used by the library for allocating a table */ - quicktime_stsz_table_t *table; -} quicktime_stsz_t; - - -/* chunk offset */ typedef struct { - longest offset; -} quicktime_stco_table_t; + unsigned int first_chunk; + unsigned int samples_per_chunk; +} sample_to_chunk_table_t; typedef struct { - int version; - long flags; - long total_entries; - - long entries_allocated; /* used by the library for allocating a table */ - quicktime_stco_table_t *table; -} quicktime_stco_t; + unsigned int count; + unsigned int duration; +} time_to_sample_table_t; - -/* sample table */ typedef struct { - int version; - long flags; - quicktime_stsd_t stsd; - quicktime_stts_t stts; - quicktime_stss_t stss; - quicktime_stsc_t stsc; - quicktime_stsz_t stsz; - quicktime_stco_t stco; -} quicktime_stbl_t; - -/* data reference */ -typedef struct { - longest size; - char type[4]; - int version; - long flags; - char *data_reference; -} quicktime_dref_table_t; + /* trak description */ + media_type type; + union { -typedef struct { - int version; - long flags; - long total_entries; - quicktime_dref_table_t *table; -} quicktime_dref_t; + struct { + unsigned int codec_format; + unsigned int width; + unsigned int height; + } video; -/* data information */ + struct { + unsigned int codec_format; + unsigned int sample_rate; + unsigned int channels; + unsigned int bits; + } audio; -typedef struct { - quicktime_dref_t dref; -} quicktime_dinf_t; + } media_description; -/* video media header */ + /* chunk offsets */ + unsigned int chunk_offset_count; + int64_t *chunk_offset_table; -typedef struct { - int version; - long flags; - int graphics_mode; - int opcolor[3]; -} quicktime_vmhd_t; + /* sample sizes */ + unsigned int sample_size; + unsigned int sample_size_count; + unsigned int *sample_size_table; + /* sync samples, a.k.a., keyframes */ + unsigned int sync_sample_count; + unsigned int *sync_sample_table; -/* sound media header */ + /* sample to chunk table */ + unsigned int sample_to_chunk_count; + sample_to_chunk_table_t *sample_to_chunk_table; -typedef struct { - int version; - long flags; - int balance; - int reserved; -} quicktime_smhd_t; + /* time to sample table */ + unsigned int time_to_sample_count; + time_to_sample_table_t *time_to_sample_table; -/* handler reference */ + /* temporary frame table corresponding to this sample table */ + qt_frame *frames; + unsigned int frame_count; -typedef struct { - int version; - long flags; - char component_type[4]; - char component_subtype[4]; - long component_manufacturer; - long component_flags; - long component_flag_mask; - char component_name[256]; -} quicktime_hdlr_t; - -/* media information */ +} qt_sample_table; typedef struct { - int is_video; - int is_audio; - quicktime_vmhd_t vmhd; - quicktime_smhd_t smhd; - quicktime_stbl_t stbl; - quicktime_hdlr_t hdlr; - quicktime_dinf_t dinf; -} quicktime_minf_t; + FILE *qt_file; + int compressed_header; /* 1 if there was a compressed moov; just FYI */ + unsigned int creation_time; /* in ms since Jan-01-1904 */ + unsigned int modification_time; + unsigned int time_scale; /* base clock frequency is Hz */ + unsigned int duration; -/* media header */ + int64_t moov_first_offset; + int64_t moov_last_offset; + int64_t mdat_first_offset; + int64_t mdat_last_offset; -typedef struct { - int version; - long flags; - unsigned long creation_time; - unsigned long modification_time; - long time_scale; - long duration; - int language; - int quality; -} quicktime_mdhd_t; + qt_atom audio_codec; + unsigned int audio_type; + unsigned int audio_sample_rate; + unsigned int audio_channels; + unsigned int audio_bits; + qt_atom video_codec; + unsigned int video_type; + unsigned int video_width; + unsigned int video_height; -/* media */ + qt_frame *frames; + unsigned int frame_count; -typedef struct { - quicktime_mdhd_t mdhd; - quicktime_minf_t minf; - quicktime_hdlr_t hdlr; -} quicktime_mdia_t; - -/* edit list */ -typedef struct -{ - long duration; - long time; - float rate; -} quicktime_elst_table_t; - -typedef struct -{ - int version; - long flags; - long total_entries; - - quicktime_elst_table_t *table; -} quicktime_elst_t; - -typedef struct -{ - quicktime_elst_t elst; -} quicktime_edts_t; - - -typedef struct -{ - quicktime_tkhd_t tkhd; - quicktime_mdia_t mdia; - quicktime_edts_t edts; -} quicktime_trak_t; - - -typedef struct -{ - int version; - long flags; - unsigned long creation_time; - unsigned long modification_time; - long time_scale; - long duration; - float preferred_rate; - float preferred_volume; - char reserved[10]; - quicktime_matrix_t matrix; - long preview_time; - long preview_duration; - long poster_time; - long selection_time; - long selection_duration; - long current_time; - long next_track_id; -} quicktime_mvhd_t; - -typedef struct -{ - char *copyright; - int copyright_len; - char *name; - int name_len; - char *info; - int info_len; -} quicktime_udta_t; + qt_error last_error; +} qt_info; -typedef struct { - int total_tracks; - - quicktime_mvhd_t mvhd; - quicktime_trak_t *trak[MAXTRACKS]; - quicktime_udta_t udta; - quicktime_ctab_t ctab; -} quicktime_moov_t; typedef struct { - quicktime_atom_t atom; -} quicktime_mdat_t; - - -/* table of pointers to every track */ -typedef struct { - quicktime_trak_t *track; /* real quicktime track corresponding to this table */ - int channels; /* number of audio channels in the track */ - long current_position; /* current sample in output file */ - long current_chunk; /* current chunk in output file */ - - void *codec; -} quicktime_audio_map_t; - -typedef struct { - quicktime_trak_t *track; - long current_position; /* current frame in output file */ - long current_chunk; /* current chunk in output file */ - - /* Array of pointers to frames of raw data when caching frames. */ - /* unsigned char **frame_cache; */ - /* long frames_cached; */ - - void *codec; -} quicktime_video_map_t; - -/* file descriptor passed to all routines */ - -typedef struct quicktime_struc { - - int(*quicktime_read_data)(struct quicktime_struc *file, char *data, longest size); - /* int(*quicktime_write_data)(struct quicktime_struc *file, char *data, int size); */ - int(*quicktime_fseek)(struct quicktime_struc *file, longest offset); - - - longest total_length; - quicktime_mdat_t mdat; - quicktime_moov_t moov; - - /* for begining and ending frame writes where the user wants to write the */ - /* file descriptor directly */ - longest offset; - /* I/O */ - /* Current position of virtual file descriptor */ - longest file_position; - /* Work around a bug in glibc where ftello returns only 32 bits by maintaining - our own position */ - longest ftell_position; - - - /* Read ahead buffer */ - longest preload_size; /* Enables preload when nonzero. */ - char *preload_buffer; - longest preload_start; /* Start of preload_buffer in file */ - longest preload_end; /* End of preload buffer in file */ - longest preload_ptr; /* Offset of preload_start in preload_buffer */ - - /* mapping of audio channels to movie tracks */ - /* one audio map entry exists for each channel */ - int total_atracks; - quicktime_audio_map_t *atracks; - - /* mapping of video tracks to movie tracks */ - int total_vtracks; - quicktime_video_map_t *vtracks; - - /* Parameters to handle compressed atoms */ - longest decompressed_buffer_size; - char *decompressed_buffer; - longest decompressed_position; - - input_plugin_t *input; - -} quicktime_t; - -typedef struct { - int64_t pts; - off_t offset; - int first_sample; - int last_sample; - int32_t type; - quicktime_trak_t *track; -} qt_idx_t; - -#define MAX_QT_INDEX 200000 - -typedef struct demux_qt_s { - demux_plugin_t demux_plugin; - - xine_t *xine; - - config_values_t *config; - - fifo_buffer_t *audio_fifo; - fifo_buffer_t *video_fifo; - - input_plugin_t *input; - - pthread_t thread; - int thread_running; - pthread_mutex_t mutex; - - int status; - int send_end_buffers; - - quicktime_t *qt; - qt_idx_t index[MAX_QT_INDEX]; - int num_index_entries; - - int has_audio; /* 1 if this qt stream has audio */ - - int video_step; /* in PTS */ - double audio_factor; - - uint32_t video_type; /* BUF_VIDEO_xxx type */ - uint32_t audio_type; /* BUF_AUDIO_xxx type */ - - WAVEFORMATEX wavex; - BITMAPINFOHEADER bih; - - uint8_t scratch[64*1024]; - - int send_newpts; -} demux_qt_t ; - - -static void check_newpts( demux_qt_t *this, int64_t pts ) -{ - if( this->send_newpts && pts ) { - - buf_element_t *buf; - - buf = this->video_fifo->buffer_pool_alloc (this->video_fifo); - buf->type = BUF_CONTROL_NEWPTS; - buf->disc_off = pts; - this->video_fifo->put (this->video_fifo, buf); - - if (this->audio_fifo) { - buf = this->audio_fifo->buffer_pool_alloc (this->audio_fifo); - buf->type = BUF_CONTROL_NEWPTS; - buf->disc_off = pts; - this->audio_fifo->put (this->audio_fifo, buf); - } - this->send_newpts = 0; - } -} - - -/* - * openquicktime stuff - */ - -/* util.c */ - -/* Disk I/O */ - -static longest quicktime_ftell(quicktime_t *file) { - return file->ftell_position; -} - -static int quicktime_fseek(quicktime_t *file, longest offset) { - file->ftell_position = offset; - if(offset > file->total_length || offset < 0) - return 1; - if (file->input->seek(file->input, file->ftell_position, SEEK_SET)) { - /* perror("quicktime_read_data FSEEK"); */ - return 1; - } - return 0; -} - -/* Read entire buffer from the preload buffer */ -static int quicktime_read_preload(quicktime_t *file, char *data, longest size) { - longest selection_start = file->file_position; - longest selection_end = file->file_position + size; - longest fragment_start, fragment_len; - - fragment_start = file->preload_ptr + (selection_start - file->preload_start); - while(fragment_start < 0) - fragment_start += file->preload_size; - while(fragment_start >= file->preload_size) - fragment_start -= file->preload_size; - - // gcc 2.96 fails here - while (selection_start < selection_end) { - fragment_len = selection_end - selection_start; - if (fragment_start + fragment_len > file->preload_size) - fragment_len = file->preload_size - fragment_start; - - memcpy (data, file->preload_buffer + fragment_start, fragment_len); - fragment_start += fragment_len; - data = data + fragment_len; - - if (fragment_start >= file->preload_size) - fragment_start = (longest)0; - selection_start += fragment_len; - } - return 0; -} - -static int quicktime_read_data(quicktime_t *file, char *data, longest size) { - - int result = 1; - - if (file->decompressed_buffer) { - - if (file->decompressed_position < file->decompressed_buffer_size) { - memcpy(data, - file->decompressed_buffer+file->decompressed_position, - size); - file->decompressed_position+=size; - return result; - - } else { - - //printf("Deleting Decompressed buffer\n"); - file->decompressed_position = 0; - file->decompressed_buffer_size=0; - free(file->decompressed_buffer); - file->decompressed_buffer = NULL; - } - } - - - if (!file->preload_size) { - //printf("quicktime_read_data 0x%llx\n", file->file_position); - file->quicktime_fseek(file, file->file_position); - /* result = fread(data, size, 1, (FILE*)file->stream); */ - result = file->input->read(file->input, data, size); - file->ftell_position += size; - - } else { - - longest selection_start = file->file_position; - longest selection_end = file->file_position + size; - longest fragment_start, fragment_len; - - if(selection_end - selection_start > file->preload_size) { - /* Size is larger than preload size. Should never happen. */ - //printf("read data 1\n"); - file->quicktime_fseek(file, file->file_position); - /* result = fread(data, size, 1, (FILE*)file->stream); */ - result = file->input->read(file->input, data, size); - file->ftell_position += size; - - } else if (selection_start >= file->preload_start - && selection_start < file->preload_end - && selection_end <= file->preload_end - && selection_end > file->preload_start) { - /* Entire range is in buffer */ - //printf("read data 2\n"); - quicktime_read_preload(file, data, size); - - } else if (selection_end > file->preload_end - && selection_end - file->preload_size < file->preload_end) { - /* Range is after buffer */ - /* Move the preload start to within one preload length of the selection_end */ - //printf("read data 3\n"); - while (selection_end - file->preload_start > file->preload_size) { - fragment_len = selection_end - file->preload_start - file->preload_size; - if (file->preload_ptr + fragment_len > file->preload_size) - fragment_len = file->preload_size - file->preload_ptr; - file->preload_start += fragment_len; - file->preload_ptr += fragment_len; - if (file->preload_ptr >= file->preload_size) - file->preload_ptr = 0; - } - - /* Append sequential data after the preload end to the new end */ - fragment_start = file->preload_ptr + file->preload_end - file->preload_start; - while (fragment_start >= file->preload_size) - fragment_start -= file->preload_size; - - while (file->preload_end < selection_end) { - fragment_len = selection_end - file->preload_end; - if (fragment_start + fragment_len > file->preload_size) - fragment_len = file->preload_size - fragment_start; - file->quicktime_fseek(file, file->preload_end); - /* result = fread(&(file->preload_buffer[fragment_start]), fragment_len, 1, (FILE*)file->stream); */ - result = file->input->read (file->input, - &(file->preload_buffer[fragment_start]), - fragment_len); - file->ftell_position += fragment_len; - file->preload_end += fragment_len; - fragment_start += fragment_len; - if (fragment_start >= file->preload_size) - fragment_start = 0; - } - - quicktime_read_preload(file, data, size); - } else { - //printf("quicktime_read_data 4 selection_start %lld selection_end %lld preload_start %lld\n", selection_start, selection_end, file->preload_start); - /* Range is before buffer or over a preload_size away from the end of the buffer. */ - /* Replace entire preload buffer with range. */ - file->quicktime_fseek(file, file->file_position); - /* result = fread(file->preload_buffer, size, 1, (FILE*)file->stream); */ - result = file->input->read(file->input, file->preload_buffer, size); - file->ftell_position += size; - file->preload_start = file->file_position; - file->preload_end = file->file_position + size; - file->preload_ptr = 0; - //printf("quicktime_read_data 5\n"); - quicktime_read_preload(file, data, size); - //printf("quicktime_read_data 6\n"); - } - } - - //printf("quicktime_read_data 1 %lld %lld\n", file->file_position, size); - file->file_position += size; - return result; -} - -static longest quicktime_position(quicktime_t *file) { - - if (file->decompressed_buffer) { - return file->decompressed_position; - } - - return file->file_position; -} - -static longest quicktime_byte_position(quicktime_t *file) -{ - return quicktime_position(file); -} - - -static float quicktime_read_fixed32(quicktime_t *file) -{ - unsigned long a, b, c, d; - unsigned char data[4]; - - file->quicktime_read_data(file, (char*)data, 4); - a = data[0]; - b = data[1]; - c = data[2]; - d = data[3]; - - a = (a << 8) + b; - b = (c << 8) + d; - - if(b) - return (float)a + (float)b / 65536; - else - return a; -} - -static float quicktime_read_fixed16(quicktime_t *file) -{ - unsigned char data[2]; - - file->quicktime_read_data(file, (char*)data, 2); - //printf("quicktime_read_fixed16 %02x%02x\n", data[0], data[1]); - if(data[1]) - return (float)data[0] + (float)data[1] / 256; - else - return (float)data[0]; -} - -static unsigned long quicktime_read_uint32(quicktime_t *file) -{ - unsigned long result; - unsigned long a, b, c, d; - char data[4]; - - file->quicktime_read_data(file, (char*)data, 4); - a = (unsigned char)data[0]; - b = (unsigned char)data[1]; - c = (unsigned char)data[2]; - d = (unsigned char)data[3]; - - result = (a << 24) | (b << 16) | (c << 8) | d; - return result; -} - -static long quicktime_read_int32(quicktime_t *file) -{ - unsigned long result; - unsigned long a, b, c, d; - char data[4]; - file->quicktime_read_data(file, (char*)data, 4); - a = (unsigned char)data[0]; - b = (unsigned char)data[1]; - c = (unsigned char)data[2]; - d = (unsigned char)data[3]; + demux_plugin_t demux_plugin; - result = (a << 24) | (b << 16) | (c << 8) | d; - return (long)result; -} - -static longest quicktime_read_int64(quicktime_t *file) -{ - ulongest result, a, b, c, d, e, f, g, h; - char data[8]; - - file->quicktime_read_data(file, (char*)data, 8); - a = (unsigned char)data[0]; - b = (unsigned char)data[1]; - c = (unsigned char)data[2]; - d = (unsigned char)data[3]; - e = (unsigned char)data[4]; - f = (unsigned char)data[5]; - g = (unsigned char)data[6]; - h = (unsigned char)data[7]; - - result = (a << 56) | - (b << 48) | - (c << 40) | - (d << 32) | - (e << 24) | - (f << 16) | - (g << 8) | - h; - return (longest)result; -} - - -static long quicktime_read_int24(quicktime_t *file) -{ - unsigned long result; - unsigned long a, b, c; - char data[4]; - - file->quicktime_read_data(file, (char*)data, 3); - a = (unsigned char)data[0]; - b = (unsigned char)data[1]; - c = (unsigned char)data[2]; - - result = (a << 16) | (b << 8) | c; - return (long)result; -} - -static int quicktime_read_int16(quicktime_t *file) -{ - unsigned long result; - unsigned long a, b; - char data[2]; - - file->quicktime_read_data(file, (char*)data, 2); - a = (unsigned char)data[0]; - b = (unsigned char)data[1]; - - result = (a << 8) | b; - return (int)result; -} - -static int quicktime_read_char(quicktime_t *file) -{ - char output; - file->quicktime_read_data(file, &output, 1); - return output; -} - -static void quicktime_read_char32(quicktime_t *file, char *string) -{ - file->quicktime_read_data(file, string, 4); -} - -static int quicktime_set_position(quicktime_t *file, longest position) { - //if(file->wr) printf("quicktime_set_position 0x%llx\n", position); - if (file->decompressed_buffer) - file->decompressed_position = position; - else - file->file_position = position; + xine_t *xine; - return 0; -} + config_values_t *config; -static void quicktime_copy_char32(char *output, char *input) { + fifo_buffer_t *video_fifo; + fifo_buffer_t *audio_fifo; - *output++ = *input++; - *output++ = *input++; - *output++ = *input++; - *output = *input; -} + input_plugin_t *input; + pthread_t thread; + int thread_running; + pthread_mutex_t mutex; + int send_end_buffers; -static unsigned long quicktime_current_time(void) { + off_t start; + int status; - time_t t; - time (&t); - return (t+(66*31536000)+1468800); -} + qt_info *qt; + BITMAPINFOHEADER bih; + unsigned int current_frame; + unsigned int last_frame; -static int quicktime_match_32(char *input, char *output) { +} demux_qt_t; - if(input[0] == output[0] && - input[1] == output[1] && - input[2] == output[2] && - input[3] == output[3]) - return 1; - else - return 0; -} +/********************************************************************** + * lazyqt functions + **********************************************************************/ -static void quicktime_read_pascal(quicktime_t *file, char *data) { +/* create a qt_info structure or return NULL if no memory */ +qt_info *create_qt_info(void) { + qt_info *info; - char len = quicktime_read_char(file); - file->quicktime_read_data(file, data, len); - data[(int) len] = 0; -} + info = (qt_info *)malloc(sizeof(qt_info)); -/* matrix.c */ + if (!info) + return NULL; -static void quicktime_matrix_init(quicktime_matrix_t *matrix) { + info->qt_file = NULL; + info->compressed_header = 0; - int i; - for (i = 0; i < 9; i++) - matrix->values[i] = 0; + info->creation_time = 0; + info->modification_time = 0; + info->time_scale = 0; + info->duration = 0; - matrix->values[0] = matrix->values[4] = 1; - matrix->values[8] = 16384; -} + info->audio_codec = 0; + info->audio_sample_rate = 0; + info->audio_channels = 0; + info->audio_bits = 0; -static void quicktime_matrix_delete(quicktime_matrix_t *matrix) { -} + info->video_codec = 0; -static void quicktime_read_matrix(quicktime_t *file, - quicktime_matrix_t *matrix) { - int i = 0; - for(i = 0; i < 9; i++) { - matrix->values[i] = quicktime_read_fixed32(file); - } -} + info->last_error = QT_OK; -static int quicktime_get_timescale(float frame_rate) { - int timescale = 600; - /* Encode the 29.97, 23.976, 59.94 framerates */ - if(frame_rate - (int)frame_rate != 0) - timescale = (int)(frame_rate * 1001 + 0.5); - else - if((600 / frame_rate) - (int)(600 / frame_rate) != 0) - timescale = (int)(frame_rate * 100 + 0.5); - /* printf("quicktime_get_timescale %f %d\n", 600.0 / (double)frame_rate, (int)(60 - 0.0 / frame_rate)); */ - return timescale; + return info; } -/* mvhd.c */ - -static int quicktime_mvhd_init(quicktime_mvhd_t *mvhd) { +/* release a qt_info structure and associated data */ +void free_qt_info(qt_info *info) { - int i; + free(info->frames); + free(info); + info = NULL; - mvhd->version = 0; - mvhd->flags = 0; - mvhd->creation_time = quicktime_current_time(); - mvhd->modification_time = quicktime_current_time(); - mvhd->time_scale = 600; - mvhd->duration = 0; - mvhd->preferred_rate = 1.0; - mvhd->preferred_volume = 0.996094; - for(i = 0; i < 10; i++) - mvhd->reserved[i] = 0; - quicktime_matrix_init(&(mvhd->matrix)); - mvhd->preview_time = 0; - mvhd->preview_duration = 0; - mvhd->poster_time = 0; - mvhd->selection_time = 0; - mvhd->selection_duration = 0; - mvhd->current_time = 0; - mvhd->next_track_id = 1; - return 0; } -static int quicktime_mvhd_delete(quicktime_mvhd_t *mvhd) { - return 0; -} +/* returns 1 if the file is determined to be a QT file, 0 otherwise */ +static int is_qt_file(input_plugin_t *qt_file) { + qt_atom atom = 0; -static void quicktime_read_mvhd(quicktime_t *file, quicktime_mvhd_t *mvhd) { - mvhd->version = quicktime_read_char(file); - mvhd->flags = quicktime_read_int24(file); - mvhd->creation_time = quicktime_read_int32(file); - mvhd->modification_time = quicktime_read_int32(file); - mvhd->time_scale = quicktime_read_int32(file); - mvhd->duration = quicktime_read_int32(file); - mvhd->preferred_rate = quicktime_read_fixed32(file); - mvhd->preferred_volume = quicktime_read_fixed16(file); - file->quicktime_read_data(file, mvhd->reserved, 10); - quicktime_read_matrix(file, &(mvhd->matrix)); - mvhd->preview_time = quicktime_read_int32(file); - mvhd->preview_duration = quicktime_read_int32(file); - mvhd->poster_time = quicktime_read_int32(file); - mvhd->selection_time = quicktime_read_int32(file); - mvhd->selection_duration = quicktime_read_int32(file); - mvhd->current_time = quicktime_read_int32(file); - mvhd->next_track_id = quicktime_read_int32(file); -} + if (qt_file->seek(qt_file, 4, SEEK_SET) != 4) + return DEMUX_CANNOT_HANDLE; -static void quicktime_mhvd_init_video(quicktime_t *file, quicktime_mvhd_t *mvhd, float frame_rate) -{ - mvhd->time_scale = quicktime_get_timescale(frame_rate); -} + if (qt_file->read(qt_file, (unsigned char *)&atom, 4) != 4) + return DEMUX_CANNOT_HANDLE; -static int quicktime_atom_reset(quicktime_atom_t *atom) -{ - atom->end = 0; - atom->type[0] = atom->type[1] = atom->type[2] = atom->type[3] = 0; - return 0; -} -static int quicktime_atom_is(quicktime_atom_t *atom, char *_type) -{ - unsigned char *type = _type; - - if(atom->type[0] == type[0] && - atom->type[1] == type[1] && - atom->type[2] == type[2] && - atom->type[3] == type[3]) + atom = be2me_32(atom); + + /* check the first atom against all known top-level atoms */ + if ( + (atom == FREE_ATOM) || + (atom == JUNK_ATOM) || + (atom == MDAT_ATOM) || + (atom == MOOV_ATOM) || + (atom == PNOT_ATOM) || + (atom == SKIP_ATOM) || + (atom == WIDE_ATOM) + ) return 1; else return 0; } -static unsigned long quicktime_atom_read_size(char *data) -{ - unsigned long result; - unsigned long a, b, c, d; - - a = (unsigned char)data[0]; - b = (unsigned char)data[1]; - c = (unsigned char)data[2]; - d = (unsigned char)data[3]; - - result = (a << 24) | (b << 16) | (c << 8) | d; - - /* - extended header is size 1 - if(result < HEADER_LENGTH) result = HEADER_LENGTH; - */ - return result; -} - -static longest quicktime_atom_read_size64(char *data) -{ - ulongest result, a, b, c, d, e, f, g, h; - - a = (unsigned char)data[0]; - b = (unsigned char)data[1]; - c = (unsigned char)data[2]; - d = (unsigned char)data[3]; - e = (unsigned char)data[4]; - f = (unsigned char)data[5]; - g = (unsigned char)data[6]; - h = (unsigned char)data[7]; - - result = (a << 56) | - (b << 48) | - (c << 40) | - (d << 32) | - (e << 24) | - (f << 16) | - (g << 8) | - h; - - if(result < HEADER_LENGTH) result = HEADER_LENGTH; - return (longest)result; -} - -static int quicktime_atom_read_type(char *data, char *type) -{ - type[0] = data[4]; - type[1] = data[5]; - type[2] = data[6]; - type[3] = data[7]; - - //printf("%c%c%c%c ", type[0], type[1], type[2], type[3]); - /* need this for quicktime_check_sig */ - if(isalpha(type[0] & 0xff) && isalpha(type[1] & 0xff) && isalpha(type[2] & 0xff) && isalpha(type[3] & 0xff)) - return 0; - else - return 1; -} - -static int quicktime_atom_skip(quicktime_t *file, quicktime_atom_t *atom) { - - if (atom->start == atom->end) - atom->end++; - - return quicktime_set_position(file, atom->end); -} - - -static int quicktime_atom_read_header(quicktime_t *file, quicktime_atom_t *atom) -{ - char header[10]; - int result; - - quicktime_atom_reset(atom); - - atom->start = quicktime_position(file); - - if (!file->quicktime_read_data(file, header, HEADER_LENGTH)) - return 1; - - result = quicktime_atom_read_type(header, atom->type); - atom->size = quicktime_atom_read_size(header); - atom->end = atom->start + atom->size; - - // printf("quicktime_atom_read_header 1 %c%c%c%c start 0x%llx size %lld end 0x%llx ftell 0x%llx 0x%llx\n", - // atom->type[0], atom->type[1], atom->type[2], atom->type[3], - // atom->start, atom->size, atom->end, - // file->file_position, - // (longest)FTELL(file->stream)); - - - /* Skip placeholder atom */ - if(quicktime_match_32(atom->type, "wide")) { - atom->start = quicktime_position(file); - quicktime_atom_reset(atom); - if(!file->quicktime_read_data(file, header, HEADER_LENGTH)) - return 1; - result = quicktime_atom_read_type(header, atom->type); - atom->size -= 8; - if(atom->size <= 0) { - /* Wrapper ended. Get new atom size */ - atom->size = quicktime_atom_read_size(header); - } - atom->end = atom->start + atom->size; - } else { - /* Get extended size */ - if(atom->size == 1) { - if(!file->quicktime_read_data(file, header, HEADER_LENGTH)) - return 1; - - atom->size = quicktime_atom_read_size64(header); - atom->end = atom->start + atom->size; - /* - * printf("quicktime_atom_read_header 2 %c%c%c%c start 0x%llx size %lld end 0x%llx ftell 0x%llx\n", - * atom->type[0], atom->type[1], atom->type[2], atom->type[3], - * atom->start, atom->size, atom->end, - * file->file_position); - */ - } - } - - return result; -} - -/* udta.c */ - -#define DEFAULT_INFO "Made with Quicktime for Linux" - -static int quicktime_udta_init(quicktime_udta_t *udta) -{ - udta->copyright = 0; - udta->copyright_len = 0; - udta->name = 0; - udta->name_len = 0; - - udta->info = malloc(strlen(DEFAULT_INFO) + 1); - udta->info_len = strlen(DEFAULT_INFO); - sprintf(udta->info, DEFAULT_INFO); - return 0; -} - -static int quicktime_udta_delete(quicktime_udta_t *udta) -{ - if(udta->copyright_len && udta->copyright) - { - free(udta->copyright); - } - if(udta->name_len && udta->name) - { - free(udta->name); - } - if(udta->info_len && udta->info) - { - free(udta->info); - } - - udta->copyright = 0; - udta->copyright_len = 0; - udta->name = 0; - udta->name_len = 0; - udta->info = 0; - udta->info_len = 0; - - return 0; -} - -static int quicktime_read_udta_string(quicktime_t *file, char **string, int *size) -{ - int result; - - if(*size) free(*string); - *size = quicktime_read_int16(file); /* Size of string */ - quicktime_read_int16(file); /* Discard language code */ - *string = malloc(*size + 1); - result = file->quicktime_read_data(file, *string, *size); - (*string)[*size] = 0; - return !result; -} - -static int quicktime_read_udta(quicktime_t *file, quicktime_udta_t *udta, quicktime_atom_t *udta_atom) -{ - quicktime_atom_t leaf_atom; - int result = 0; - - do - { - quicktime_atom_read_header(file, &leaf_atom); - - if(quicktime_atom_is(&leaf_atom, "©cpy")) - { - result += quicktime_read_udta_string(file, &(udta->copyright), &(udta->copyright_len)); - } - else - if(quicktime_atom_is(&leaf_atom, "©nam")) - { - result += quicktime_read_udta_string(file, &(udta->name), &(udta->name_len)); - } - else - if(quicktime_atom_is(&leaf_atom, "©inf")) - { - result += quicktime_read_udta_string(file, &(udta->info), &(udta->info_len)); - } - else - quicktime_atom_skip(file, &leaf_atom); - }while(quicktime_position(file) < udta_atom->end); - - return result; -} - -#if 0 -static int quicktime_set_udta_string(char **string, int *size, char *new_string) -{ - if(*size) free(*string); - *size = strlen(new_string + 1); - *string = malloc(*size + 1); - strcpy(*string, new_string); - return 0; -} -#endif - -static int quicktime_ctab_init(quicktime_ctab_t *ctab) -{ - ctab->seed = 0; - ctab->flags = 0; - ctab->size = 0; - ctab->alpha = 0; - ctab->red = 0; - ctab->green = 0; - ctab->blue = 0; - return 0; -} - -static int quicktime_ctab_delete(quicktime_ctab_t *ctab) -{ - if(ctab->alpha) free(ctab->alpha); - if(ctab->red) free(ctab->red); - if(ctab->green) free(ctab->green); - if(ctab->blue) free(ctab->blue); - return 0; -} - -static int quicktime_read_ctab(quicktime_t *file, quicktime_ctab_t *ctab) -{ - long i; - - ctab->seed = quicktime_read_int32(file); - ctab->flags = quicktime_read_int16(file); - ctab->size = quicktime_read_int16(file) + 1; - ctab->alpha = malloc(sizeof(int16_t) * ctab->size); - ctab->red = malloc(sizeof(int16_t) * ctab->size); - ctab->green = malloc(sizeof(int16_t) * ctab->size); - ctab->blue = malloc(sizeof(int16_t) * ctab->size); - - for(i = 0; i < ctab->size; i++) - { - ctab->alpha[i] = quicktime_read_int16(file); - ctab->red[i] = quicktime_read_int16(file); - ctab->green[i] = quicktime_read_int16(file); - ctab->blue[i] = quicktime_read_int16(file); - } - - return 0; -} - -/* tkhd.c */ - -static int quicktime_tkhd_init(quicktime_tkhd_t *tkhd) -{ - int i; - tkhd->version = 0; - tkhd->flags = 15; - tkhd->creation_time = quicktime_current_time(); - tkhd->modification_time = quicktime_current_time(); - tkhd->track_id=0; - tkhd->reserved1 = 0; - tkhd->duration = 0; /* need to set this when closing */ - for(i = 0; i < 8; i++) tkhd->reserved2[i] = 0; - tkhd->layer = 0; - tkhd->alternate_group = 0; - tkhd->volume = 0.996094; - tkhd->reserved3 = 0; - quicktime_matrix_init(&(tkhd->matrix)); - tkhd->track_width = 0; - tkhd->track_height = 0; - return 0; -} - -static int quicktime_tkhd_delete(quicktime_tkhd_t *tkhd) -{ - return 0; -} - -static void quicktime_read_tkhd(quicktime_t *file, quicktime_tkhd_t *tkhd) -{ - //printf("quicktime_read_tkhd 1 0x%llx\n", quicktime_position(file)); - tkhd->version = quicktime_read_char(file); - tkhd->flags = quicktime_read_int24(file); - tkhd->creation_time = quicktime_read_int32(file); - tkhd->modification_time = quicktime_read_int32(file); - tkhd->track_id = quicktime_read_int32(file); - tkhd->reserved1 = quicktime_read_int32(file); - tkhd->duration = quicktime_read_int32(file); - - file->quicktime_read_data(file, tkhd->reserved2, 8); - - tkhd->layer = quicktime_read_int16(file); - tkhd->alternate_group = quicktime_read_int16(file); - //printf("quicktime_read_tkhd 1 0x%llx\n", quicktime_position(file)); - tkhd->volume = quicktime_read_fixed16(file); - //printf("quicktime_read_tkhd 2\n"); - tkhd->reserved3 = quicktime_read_int16(file); - quicktime_read_matrix(file, &(tkhd->matrix)); - tkhd->track_width = quicktime_read_fixed32(file); - tkhd->track_height = quicktime_read_fixed32(file); -} - - -static void quicktime_tkhd_init_video(quicktime_t *file, - quicktime_tkhd_t *tkhd, - int frame_w, - int frame_h) -{ - tkhd->track_width = frame_w; - tkhd->track_height = frame_h; - tkhd->volume = 0; -} - -/* elst.c */ +/* fetch interesting information from the movie header atom */ +static void parse_mvhd_atom(qt_info *info, unsigned char *mvhd_atom) { + info->creation_time = BE_32(&mvhd_atom[0x0C]); + info->modification_time = BE_32(&mvhd_atom[0x10]); + info->time_scale = BE_32(&mvhd_atom[0x14]); + info->duration = BE_32(&mvhd_atom[0x18]); -static void quicktime_elst_table_init(quicktime_elst_table_t *table) -{ - table->duration = 0; - table->time = 0; - table->rate = 1; } -static void quicktime_elst_table_delete(quicktime_elst_table_t *table) -{ -} - -static void quicktime_read_elst_table(quicktime_t *file, quicktime_elst_table_t *table) -{ - table->duration = quicktime_read_int32(file); - table->time = quicktime_read_int32(file); - table->rate = quicktime_read_fixed32(file); -} - - -static void quicktime_elst_init(quicktime_elst_t *elst) -{ - elst->version = 0; - elst->flags = 0; - elst->total_entries = 0; - elst->table = 0; -} +/* + * This function traverses through a minf atom searching for the sample + * table atoms, which it loads into an internal sample table structure. + */ +static qt_error parse_minf_atom(qt_sample_table *sample_table, + unsigned char *minf_atom) { + + int i, j; + unsigned int minf_atom_size = BE_32(&minf_atom[0]); + qt_atom header_atom; + qt_atom current_atom; + qt_error last_error = QT_OK; + + /* initialize sample table structure */ + sample_table->chunk_offset_table = NULL; + sample_table->sample_size_table = NULL; + sample_table->sync_sample_table = NULL; + sample_table->sample_to_chunk_table = NULL; + sample_table->time_to_sample_table = NULL; + + /* fetch the media type contained in this minf atom */ + header_atom = BE_32(&minf_atom[0x0C]); + if (header_atom == VMHD_ATOM) + sample_table->type = MEDIA_VIDEO; + else if (header_atom == SMHD_ATOM) + sample_table->type = MEDIA_AUDIO; + else + sample_table->type = MEDIA_OTHER; -static void quicktime_elst_init_all(quicktime_elst_t *elst) -{ - if(!elst->total_entries) - { - elst->total_entries = 1; - elst->table = (quicktime_elst_table_t*)malloc(sizeof(quicktime_elst_table_t) * elst->total_entries); - quicktime_elst_table_init(&(elst->table[0])); - } -} + /* search for the useful atoms */ + for (i = ATOM_PREAMBLE_SIZE; i < minf_atom_size - 4; i++) { + current_atom = BE_32(&minf_atom[i]); -static void quicktime_elst_delete(quicktime_elst_t *elst) -{ - int i; - if(elst->total_entries) - { - for(i = 0; i < elst->total_entries; i++) - quicktime_elst_table_delete(&(elst->table[i])); - free(elst->table); - } - elst->total_entries = 0; -} + if (current_atom == STSD_ATOM) { + if (sample_table->type == MEDIA_VIDEO) { -static void quicktime_read_elst(quicktime_t *file, quicktime_elst_t *elst) -{ - long i; - /* quicktime_atom_t leaf_atom; */ - - elst->version = quicktime_read_char(file); - elst->flags = quicktime_read_int24(file); - elst->total_entries = quicktime_read_int32(file); - elst->table = (quicktime_elst_table_t*)calloc(1, sizeof(quicktime_elst_table_t) * elst->total_entries); - for(i = 0; i < elst->total_entries; i++) - { - quicktime_elst_table_init(&(elst->table[i])); - quicktime_read_elst_table(file, &(elst->table[i])); - } -} + /* fetch video parameters */ + sample_table->media_description.video.width = + BE_16(&minf_atom[i + 0x2C]); + sample_table->media_description.video.height = + BE_16(&minf_atom[i + 0x2E]); + sample_table->media_description.video.codec_format = + BE_32(&minf_atom[i + 0x10]); -/* edts.c */ + } else if (sample_table->type == MEDIA_AUDIO) { -static void quicktime_edts_init(quicktime_edts_t *edts) -{ - quicktime_elst_init(&(edts->elst)); -} + /* fetch audio parameters */ + sample_table->media_description.audio.codec_format = + BE_32(&minf_atom[i + 0x10]); + sample_table->media_description.audio.sample_rate = + BE_16(&minf_atom[i + 0x2C]); + sample_table->media_description.audio.channels = minf_atom[i + 0x25]; + sample_table->media_description.audio.bits = minf_atom[i + 0x27]; -static void quicktime_edts_delete(quicktime_edts_t *edts) -{ - quicktime_elst_delete(&(edts->elst)); -} + } -static void quicktime_edts_init_table(quicktime_edts_t *edts) -{ - quicktime_elst_init_all(&(edts->elst)); -} + } else if (current_atom == STSZ_ATOM) { -static void quicktime_read_edts(quicktime_t *file, quicktime_edts_t *edts, quicktime_atom_t *edts_atom) -{ - quicktime_atom_t leaf_atom; - - do - { - quicktime_atom_read_header(file, &leaf_atom); - //printf("quicktime_read_edts %llx %llx\n", quicktime_position(file), leaf_atom.end); - if(quicktime_atom_is(&leaf_atom, "elst")) - { quicktime_read_elst(file, &(edts->elst)); } - else - quicktime_atom_skip(file, &leaf_atom); - }while(quicktime_position(file) < edts_atom->end); -} + /* there should only be one of these atoms */ + if (sample_table->sample_size_table) { + last_error = QT_HEADER_TROUBLE; + goto free_sample_table; + } -/* mdhd.c */ - -void quicktime_mdhd_init(quicktime_mdhd_t *mdhd) -{ - mdhd->version = 0; - mdhd->flags = 0; - mdhd->creation_time = quicktime_current_time(); - mdhd->modification_time = quicktime_current_time(); - mdhd->time_scale = 0; - mdhd->duration = 0; - mdhd->language = 0; - mdhd->quality = 100; -} + sample_table->sample_size = BE_32(&minf_atom[i + 8]); + sample_table->sample_size_count = BE_32(&minf_atom[i + 12]); + + /* 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) { + last_error = QT_NO_MEMORY; + goto free_sample_table; + } + + /* load the sample size table */ + for (j = 0; j < sample_table->sample_size_count; j++) + sample_table->sample_size_table[j] = + BE_32(&minf_atom[i + 16 + j * 4]); + } else + /* set the pointer to non-NULL to indicate that the atom type has + * already been seen for this minf atom */ + sample_table->sample_size_table = (void *)-1; -static void quicktime_mdhd_init_video(quicktime_t *file, - quicktime_mdhd_t *mdhd, - int frame_w, - int frame_h, - float frame_rate) -{ - mdhd->time_scale = quicktime_get_timescale(frame_rate); - //printf("quicktime_mdhd_init_video %ld %f\n", mdhd->time_scale, (double)frame_rate); - mdhd->duration = 0; /* set this when closing */ -} + } else if (current_atom == STSS_ATOM) { -static void quicktime_mdhd_init_audio(quicktime_t *file, - quicktime_mdhd_t *mdhd, - int channels, - int sample_rate, - int bits, - char *compressor) -{ - mdhd->time_scale = sample_rate; - mdhd->duration = 0; /* set this when closing */ -} + /* there should only be one of these atoms */ + if (sample_table->sync_sample_table) { + last_error = QT_HEADER_TROUBLE; + goto free_sample_table; + } -static void quicktime_mdhd_delete(quicktime_mdhd_t *mdhd) -{ -} + sample_table->sync_sample_count = BE_32(&minf_atom[i + 8]); -static void quicktime_read_mdhd(quicktime_t *file, quicktime_mdhd_t *mdhd) -{ - mdhd->version = quicktime_read_char(file); - mdhd->flags = quicktime_read_int24(file); - mdhd->creation_time = quicktime_read_int32(file); - mdhd->modification_time = quicktime_read_int32(file); - mdhd->time_scale = quicktime_read_int32(file); - mdhd->duration = quicktime_read_int32(file); - mdhd->language = quicktime_read_int16(file); - mdhd->quality = quicktime_read_int16(file); -} + sample_table->sync_sample_table = (unsigned int *)malloc( + sample_table->sync_sample_count * sizeof(unsigned int)); + if (!sample_table->sync_sample_table) { + last_error = QT_NO_MEMORY; + goto free_sample_table; + } -/* hdlr.c */ - -static void quicktime_hdlr_init(quicktime_hdlr_t *hdlr) -{ - hdlr->version = 0; - hdlr->flags = 0; - hdlr->component_type[0] = 'm'; - hdlr->component_type[1] = 'h'; - hdlr->component_type[2] = 'l'; - hdlr->component_type[3] = 'r'; - hdlr->component_subtype[0] = 'v'; - hdlr->component_subtype[1] = 'i'; - hdlr->component_subtype[2] = 'd'; - hdlr->component_subtype[3] = 'e'; - hdlr->component_manufacturer = 0; - hdlr->component_flags = 0; - hdlr->component_flag_mask = 0; - strcpy(hdlr->component_name, "Linux Media Handler"); -} + /* load the sync sample table */ + for (j = 0; j < sample_table->sync_sample_count; j++) + sample_table->sync_sample_table[j] = + BE_32(&minf_atom[i + 12 + j * 4]); -static void quicktime_hdlr_init_video(quicktime_hdlr_t *hdlr) -{ - hdlr->component_subtype[0] = 'v'; - hdlr->component_subtype[1] = 'i'; - hdlr->component_subtype[2] = 'd'; - hdlr->component_subtype[3] = 'e'; - strcpy(hdlr->component_name, "Linux Video Media Handler"); -} + } else if (current_atom == STCO_ATOM) { -static void quicktime_hdlr_init_audio(quicktime_hdlr_t *hdlr) -{ - hdlr->component_subtype[0] = 's'; - hdlr->component_subtype[1] = 'o'; - hdlr->component_subtype[2] = 'u'; - hdlr->component_subtype[3] = 'n'; - strcpy(hdlr->component_name, "Linux Sound Media Handler"); -} + /* there should only be one of either stco or co64 */ + if (sample_table->chunk_offset_table) { + last_error = QT_HEADER_TROUBLE; + goto free_sample_table; + } -static void quicktime_hdlr_init_data(quicktime_hdlr_t *hdlr) -{ - hdlr->component_type[0] = 'd'; - hdlr->component_type[1] = 'h'; - hdlr->component_type[2] = 'l'; - hdlr->component_type[3] = 'r'; - hdlr->component_subtype[0] = 'a'; - hdlr->component_subtype[1] = 'l'; - hdlr->component_subtype[2] = 'i'; - hdlr->component_subtype[3] = 's'; - strcpy(hdlr->component_name, "Linux Alias Data Handler"); -} + sample_table->chunk_offset_count = BE_32(&minf_atom[i + 8]); -static void quicktime_hdlr_delete(quicktime_hdlr_t *hdlr) -{ -} + sample_table->chunk_offset_table = (int64_t *)malloc( + sample_table->chunk_offset_count * sizeof(int64_t)); + if (!sample_table->chunk_offset_table) { + last_error = QT_NO_MEMORY; + goto free_sample_table; + } -static void quicktime_read_hdlr(quicktime_t *file, quicktime_hdlr_t *hdlr) -{ - hdlr->version = quicktime_read_char(file); - hdlr->flags = quicktime_read_int24(file); - quicktime_read_char32(file, hdlr->component_type); - quicktime_read_char32(file, hdlr->component_subtype); - hdlr->component_manufacturer = quicktime_read_int32(file); - hdlr->component_flags = quicktime_read_int32(file); - hdlr->component_flag_mask = quicktime_read_int32(file); - quicktime_read_pascal(file, hdlr->component_name); -} + /* load the chunk offset table */ + for (j = 0; j < sample_table->chunk_offset_count; j++) + sample_table->chunk_offset_table[j] = + BE_32(&minf_atom[i + 12 + j * 4]); -/* vmhd.c */ + } else if (current_atom == CO64_ATOM) { -static void quicktime_vmhd_init(quicktime_vmhd_t *vmhd) -{ - vmhd->version = 0; - vmhd->flags = 1; - vmhd->graphics_mode = 64; - vmhd->opcolor[0] = 32768; - vmhd->opcolor[1] = 32768; - vmhd->opcolor[2] = 32768; -} + /* there should only be one of either stco or co64 */ + if (sample_table->chunk_offset_table) { + last_error = QT_HEADER_TROUBLE; + goto free_sample_table; + } -static void quicktime_vmhd_init_video(quicktime_t *file, - quicktime_vmhd_t *vmhd, - int frame_w, - int frame_h, - float frame_rate) -{ -} + sample_table->chunk_offset_count = BE_32(&minf_atom[i + 8]); -static void quicktime_vmhd_delete(quicktime_vmhd_t *vmhd) -{ -} + sample_table->chunk_offset_table = (int64_t *)malloc( + sample_table->chunk_offset_count * sizeof(int64_t)); + if (!sample_table->chunk_offset_table) { + last_error = QT_NO_MEMORY; + goto free_sample_table; + } -static void quicktime_read_vmhd(quicktime_t *file, quicktime_vmhd_t *vmhd) -{ - int i; - vmhd->version = quicktime_read_char(file); - vmhd->flags = quicktime_read_int24(file); - vmhd->graphics_mode = quicktime_read_int16(file); - for(i = 0; i < 3; i++) - vmhd->opcolor[i] = quicktime_read_int16(file); -} + /* load the 64-bit chunk offset table */ + for (j = 0; j < sample_table->chunk_offset_count; j++) { + sample_table->chunk_offset_table[j] = + BE_32(&minf_atom[i + 12 + j * 8 + 0]); + sample_table->chunk_offset_table[j] <<= 32; + sample_table->chunk_offset_table[j] |= + BE_32(&minf_atom[i + 12 + j * 8 + 4]); + } -/* smhd.c */ + } else if (current_atom == STSC_ATOM) { -static void quicktime_smhd_init(quicktime_smhd_t *smhd) -{ - smhd->version = 0; - smhd->flags = 0; - smhd->balance = 0; - smhd->reserved = 0; -} + /* there should only be one of these atoms */ + if (sample_table->sample_to_chunk_table) { + last_error = QT_HEADER_TROUBLE; + goto free_sample_table; + } -static void quicktime_smhd_delete(quicktime_smhd_t *smhd) -{ -} + sample_table->sample_to_chunk_count = BE_32(&minf_atom[i + 8]); -static void quicktime_read_smhd(quicktime_t *file, quicktime_smhd_t *smhd) -{ - smhd->version = quicktime_read_char(file); - smhd->flags = quicktime_read_int24(file); - smhd->balance = quicktime_read_int16(file); - smhd->reserved = quicktime_read_int16(file); -} + 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) { + last_error = QT_NO_MEMORY; + goto free_sample_table; + } -/* dref.c */ - -void quicktime_dref_table_init(quicktime_dref_table_t *table) -{ - table->size = 0; - table->type[0] = 'a'; - table->type[1] = 'l'; - table->type[2] = 'i'; - table->type[3] = 's'; - table->version = 0; - table->flags = 0x0001; - table->data_reference = malloc(256); - table->data_reference[0] = 0; -} + /* 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 = + BE_32(&minf_atom[i + 12 + j * 12 + 0]); + sample_table->sample_to_chunk_table[j].samples_per_chunk = + BE_32(&minf_atom[i + 12 + j * 12 + 4]); + } -void quicktime_dref_table_delete(quicktime_dref_table_t *table) -{ - if(table->data_reference) free(table->data_reference); - table->data_reference = 0; -} + } else if (current_atom == STTS_ATOM) { -void quicktime_read_dref_table(quicktime_t *file, quicktime_dref_table_t *table) -{ - table->size = quicktime_read_int32(file); - quicktime_read_char32(file, table->type); - table->version = quicktime_read_char(file); - table->flags = quicktime_read_int24(file); - if(table->data_reference) free(table->data_reference); - - table->data_reference = malloc(table->size); - if(table->size > 12) - file->quicktime_read_data(file, table->data_reference, table->size - 12); - table->data_reference[table->size - 12] = 0; -} + /* there should only be one of these atoms */ + if (sample_table->time_to_sample_table) { + last_error = QT_HEADER_TROUBLE; + goto free_sample_table; + } + sample_table->time_to_sample_count = BE_32(&minf_atom[i + 8]); -void quicktime_dref_init(quicktime_dref_t *dref) -{ - dref->version = 0; - dref->flags = 0; - dref->total_entries = 0; - dref->table = 0; -} + sample_table->time_to_sample_table = (time_to_sample_table_t *)malloc( + sample_table->time_to_sample_count * sizeof(time_to_sample_table_t)); + if (!sample_table->time_to_sample_table) { + last_error = QT_NO_MEMORY; + goto free_sample_table; + } -void quicktime_dref_init_all(quicktime_dref_t *dref) -{ - if(!dref->total_entries) - { - dref->total_entries = 1; - dref->table = (quicktime_dref_table_t *)malloc(sizeof(quicktime_dref_table_t) * dref->total_entries); - quicktime_dref_table_init(&(dref->table[0])); + /* 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 = + BE_32(&minf_atom[i + 12 + j * 8 + 0]); + sample_table->time_to_sample_table[j].duration = + BE_32(&minf_atom[i + 12 + j * 8 + 4]); + } } -} - -void quicktime_dref_delete(quicktime_dref_t *dref) -{ - if(dref->table) - { - int i; - for(i = 0; i < dref->total_entries; i++) - quicktime_dref_table_delete(&(dref->table[i])); - free(dref->table); - } - dref->total_entries = 0; -} + } -void quicktime_read_dref(quicktime_t *file, quicktime_dref_t *dref) -{ - long i; - - dref->version = quicktime_read_char(file); - dref->flags = quicktime_read_int24(file); - dref->total_entries = quicktime_read_int32(file); - dref->table = (quicktime_dref_table_t*)malloc(sizeof(quicktime_dref_table_t) * dref->total_entries); - for(i = 0; i < dref->total_entries; i++) - { - quicktime_dref_table_init(&(dref->table[i])); - quicktime_read_dref_table(file, &(dref->table[i])); + return QT_OK; + + /* jump here to make sure everything is free'd and avoid leaking memory */ +free_sample_table: + free(sample_table->chunk_offset_table); + 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); + + return last_error; +} + +static qt_error build_frame_table(qt_sample_table *sample_table) { + + int i, j; + unsigned int frame_counter; + unsigned int next_keyframe; + unsigned int keyframe_index; + unsigned int chunk_start, chunk_end; + unsigned int samples_per_chunk; + unsigned int current_offset; + int64_t current_pts; + unsigned int pts_index; + unsigned int pts_index_countdown; + + /* AUDIO and OTHER frame types follow the same rules; VIDEO follows a + * different set */ + if (sample_table->type == MEDIA_VIDEO) { + + /* 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) + return QT_NO_MEMORY; + + /* initialize keyframe management */ + keyframe_index = 0; + if (sample_table->sync_sample_table) { + next_keyframe = sample_table->sync_sample_table[keyframe_index++] - 1; + } else { + next_keyframe = 0xFFFFFFFF; /* this means all frames are key */ } -} - -/* dinf.c */ - -static void quicktime_dinf_init(quicktime_dinf_t *dinf) -{ - quicktime_dref_init(&(dinf->dref)); -} - -static void quicktime_dinf_delete(quicktime_dinf_t *dinf) -{ - quicktime_dref_delete(&(dinf->dref)); -} - -static void quicktime_dinf_init_all(quicktime_dinf_t *dinf) -{ - quicktime_dref_init_all(&(dinf->dref)); -} -static void quicktime_read_dinf(quicktime_t *file, quicktime_dinf_t *dinf, quicktime_atom_t *dinf_atom) -{ - quicktime_atom_t leaf_atom; - - do - { - quicktime_atom_read_header(file, &leaf_atom); - if(quicktime_atom_is(&leaf_atom, "dref")) - { quicktime_read_dref(file, &(dinf->dref)); } + /* initialize more accounting variables */ + frame_counter = 0; + current_pts = 0; + pts_index = 0; + pts_index_countdown = + sample_table->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++) { + /* 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_end = + sample_table->sample_to_chunk_table[i + 1].first_chunk; else - quicktime_atom_skip(file, &leaf_atom); - }while(quicktime_position(file) < dinf_atom->end); -} - -/* stsdtable.c */ - -static void quicktime_mjqt_init(quicktime_mjqt_t *mjqt) -{ -} - -static void quicktime_mjqt_delete(quicktime_mjqt_t *mjqt) -{ -} - -static void quicktime_mjht_init(quicktime_mjht_t *mjht) -{ -} - -static void quicktime_mjht_delete(quicktime_mjht_t *mjht) -{ -} - -static void quicktime_read_stsd_audio(quicktime_t *file, quicktime_stsd_table_t *table, quicktime_atom_t *parent_atom) -{ - quicktime_atom_t atom; - - table->version = quicktime_read_int16(file); - table->revision = quicktime_read_int16(file); - file->quicktime_read_data(file, table->vendor, 4); - table->channels = quicktime_read_int16(file); - table->sample_size = quicktime_read_int16(file); - table->compression_id = quicktime_read_int16(file); - table->packet_size = quicktime_read_int16(file); - table->sample_rate = quicktime_read_fixed32(file); - if(table->compression_id == 65534) - { /* Version 1 */ - table->samplesPerPacket = quicktime_read_fixed32(file); - table->bytesPerPacket = quicktime_read_fixed32(file); - table->bytesPerFrames = quicktime_read_fixed32(file); - table->bytesPerSample = quicktime_read_fixed32(file); - /* Reading the next atom ... could be a wav header */ - quicktime_atom_read_header(file, &atom); - table->private_data = malloc(atom.size); - /* printf("%d%d%d%d", atom.type[0], atom.type[1], atom.type[2], atom.type[3]); */ - file->quicktime_read_data(file, table->private_data, atom.size); - table->private_data_size = atom.size; + /* 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; + + /* 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]; + while (samples_per_chunk > 0) { + sample_table->frames[frame_counter].type = MEDIA_VIDEO; + + /* 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; + } else { + sample_table->frames[frame_counter].size = + sample_table->sample_size_table[frame_counter]; + current_offset += + sample_table->sample_size_table[frame_counter]; + } + + /* figure out the keyframe situation for this frame */ + /* if the next_keyframe is all F's, every frame is a keyframe */ + if (next_keyframe == 0xFFFFFFFF) + sample_table->frames[frame_counter].keyframe = 1; + else if (next_keyframe == frame_counter) { + sample_table->frames[frame_counter].keyframe = 1; + if (keyframe_index < sample_table->sync_sample_count) + next_keyframe = + sample_table->sync_sample_table[keyframe_index++] - 1; + else + /* this frame number will hopefully never be reached */ + next_keyframe = 0xFFFFFFFE; + } + else + sample_table->frames[frame_counter].keyframe = 0; + + /* figure out the pts situation */ + sample_table->frames[frame_counter].pts = current_pts; + current_pts += + sample_table->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; + } + + samples_per_chunk--; + frame_counter++; + } + } } - /* - quicktime_stsd_audio_dump(table); - printf("%lld %lld %lld", file->offset, file->file_position, file->ftell_position); - */ -} + } else { -static void quicktime_read_stsd_video(quicktime_t *file, quicktime_stsd_table_t *table, quicktime_atom_t *parent_atom) -{ - quicktime_atom_t leaf_atom; - int len; - - table->version = quicktime_read_int16(file); - table->revision = quicktime_read_int16(file); - file->quicktime_read_data(file, table->vendor, 4); - table->temporal_quality = quicktime_read_int32(file); - table->spatial_quality = quicktime_read_int32(file); - table->width = quicktime_read_int16(file); - table->height = quicktime_read_int16(file); - table->dpi_horizontal = quicktime_read_fixed32(file); - table->dpi_vertical = quicktime_read_fixed32(file); - table->data_size = quicktime_read_int32(file); - table->frames_per_sample = quicktime_read_int16(file); - len = quicktime_read_char(file); - file->quicktime_read_data(file, table->compressor_name, 31); - table->depth = quicktime_read_int16(file); - table->ctab_id = quicktime_read_int16(file); - - while(quicktime_position(file) < parent_atom->end) - { - quicktime_atom_read_header(file, &leaf_atom); - /* - printf("quicktime_read_stsd_video 1 0x%llx 0x%llx 0x%llx\n", leaf_atom.start, leaf_atom.end, quicktime_position(file)); - */ - - if(quicktime_atom_is(&leaf_atom, "ctab")) - { - quicktime_read_ctab(file, &(table->ctab)); - } + /* 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) + return QT_NO_MEMORY; + + 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].size = 0; /* temporary, of course */ + sample_table->frames[i].keyframe = 0; + if (sample_table->type == MEDIA_AUDIO) + sample_table->frames[i].pts = + sample_table->sample_size_count; /* stash away for audio pts calc */ else - if(quicktime_atom_is(&leaf_atom, "gama")) - { - table->gamma = quicktime_read_fixed32(file); - } - else - if(quicktime_atom_is(&leaf_atom, "fiel")) - { - table->fields = quicktime_read_char(file); - table->field_dominance = quicktime_read_char(file); - } - else - /* if(quicktime_atom_is(&leaf_atom, "mjqt")) */ - /* { */ - /* quicktime_read_mjqt(file, &(table->mjqt)); */ - /* } */ - /* else */ - /* if(quicktime_atom_is(&leaf_atom, "mjht")) */ - /* { */ - /* quicktime_read_mjht(file, &(table->mjht)); */ - /* } */ - /* else */ - quicktime_atom_skip(file, &leaf_atom); + sample_table->frames[i].pts = 0; } - //printf("quicktime_read_stsd_video 2\n"); -} - -static void quicktime_read_stsd_table(quicktime_t *file, quicktime_minf_t *minf, quicktime_stsd_table_t *table) -{ - quicktime_atom_t leaf_atom; - - quicktime_atom_read_header(file, &leaf_atom); - - table->format[0] = leaf_atom.type[0]; - table->format[1] = leaf_atom.type[1]; - table->format[2] = leaf_atom.type[2]; - table->format[3] = leaf_atom.type[3]; - file->quicktime_read_data(file, table->reserved, 6); - table->data_reference = quicktime_read_int16(file); - - if(minf->is_audio) quicktime_read_stsd_audio(file, table, &leaf_atom); - if(minf->is_video) quicktime_read_stsd_video(file, table, &leaf_atom); -} - -static void quicktime_stsd_table_init(quicktime_stsd_table_t *table) -{ - int i; - table->format[0] = 'y'; - table->format[1] = 'u'; - table->format[2] = 'v'; - table->format[3] = '2'; - for(i = 0; i < 6; i++) table->reserved[i] = 0; - table->data_reference = 1; - - table->version = 0; - table->revision = 0; - table->vendor[0] = 'l'; - table->vendor[1] = 'n'; - table->vendor[2] = 'u'; - table->vendor[3] = 'x'; - - table->temporal_quality = 100; - table->spatial_quality = 258; - table->width = 0; - table->height = 0; - table->dpi_horizontal = 72; - table->dpi_vertical = 72; - table->data_size = 0; - table->frames_per_sample = 1; - for(i = 0; i < 32; i++) table->compressor_name[i] = 0; - sprintf(table->compressor_name, "Quicktime for Linux"); - table->depth = 24; - table->ctab_id = 65535; - quicktime_ctab_init(&(table->ctab)); - table->gamma = 0; - table->fields = 0; - table->field_dominance = 1; - quicktime_mjqt_init(&(table->mjqt)); - quicktime_mjht_init(&(table->mjht)); - - table->channels = 0; - table->sample_size = 0; - table->compression_id = 0; - table->packet_size = 0; - table->sample_rate = 0; - - /* For the Version 1 */ - table->private_data = NULL; - table->private_data_size = 0; -} - -static void quicktime_stsd_table_delete(quicktime_stsd_table_t *table) -{ - quicktime_ctab_delete(&(table->ctab)); - quicktime_mjqt_delete(&(table->mjqt)); - quicktime_mjht_delete(&(table->mjht)); -} - -/* stsd.c */ - - -static void quicktime_stsd_init(quicktime_stsd_t *stsd) -{ - stsd->version = 0; - stsd->flags = 0; - stsd->total_entries = 0; -} - -static void quicktime_stsd_init_table(quicktime_stsd_t *stsd) -{ - if(!stsd->total_entries) - { - stsd->total_entries = 1; - stsd->table = (quicktime_stsd_table_t*)calloc(1, sizeof(quicktime_stsd_table_t) * stsd->total_entries); - quicktime_stsd_table_init(&(stsd->table[0])); - } -} - -static void quicktime_stsd_init_video(quicktime_t *file, - quicktime_stsd_t *stsd, - int frame_w, - int frame_h, - float frame_rate, - char *compression) -{ - quicktime_stsd_table_t *table; - quicktime_stsd_init_table(stsd); - //printf("quicktime_stsd_init_video 1\n"); - table = &(stsd->table[0]); - //printf("quicktime_stsd_init_video 1\n"); - - quicktime_copy_char32(table->format, compression); - //printf("quicktime_stsd_init_video 1\n"); - table->width = frame_w; - //printf("quicktime_stsd_init_video 1\n"); - table->height = frame_h; - //printf("quicktime_stsd_init_video 1\n"); - table->frames_per_sample = 1; - //printf("quicktime_stsd_init_video 1\n"); - table->depth = 24; - //printf("quicktime_stsd_init_video 1\n"); - table->ctab_id = 65535; - //printf("quicktime_stsd_init_video 2\n"); -} - -static void quicktime_stsd_init_audio(quicktime_t *file, - quicktime_stsd_t *stsd, - int channels, - int sample_rate, - int bits, - char *compressor) -{ - quicktime_stsd_table_t *table; - quicktime_stsd_init_table(stsd); - table = &(stsd->table[0]); - - quicktime_copy_char32(table->format, compressor); - table->channels = channels; - table->sample_size = bits; - table->sample_rate = sample_rate; -} - -static void quicktime_stsd_delete(quicktime_stsd_t *stsd) -{ - int i; - if(stsd->total_entries) - { - for(i = 0; i < stsd->total_entries; i++) - quicktime_stsd_table_delete(&(stsd->table[i])); - free(stsd->table); - } - - stsd->total_entries = 0; -} - -static void quicktime_read_stsd(quicktime_t *file, quicktime_minf_t *minf, quicktime_stsd_t *stsd) -{ - long i; - /* quicktime_atom_t leaf_atom; */ - - stsd->version = quicktime_read_char(file); - stsd->flags = quicktime_read_int24(file); - stsd->total_entries = quicktime_read_int32(file); - stsd->table = (quicktime_stsd_table_t*)malloc(sizeof(quicktime_stsd_table_t) * stsd->total_entries); - for(i = 0; i < stsd->total_entries; i++) - { - quicktime_stsd_table_init(&(stsd->table[i])); - quicktime_read_stsd_table(file, minf, &(stsd->table[i])); - } -} - -/* stts.c */ - -static void quicktime_stts_init(quicktime_stts_t *stts) { - stts->version = 0; - stts->flags = 0; - stts->total_entries = 0; -} - -static void quicktime_stts_init_table(quicktime_stts_t *stts) { - if(!stts->total_entries) { - stts->total_entries = 1; - stts->table = (quicktime_stts_table_t*)malloc(sizeof(quicktime_stts_table_t) * stts->total_entries); } -} - -static void quicktime_stts_init_video(quicktime_t *file, quicktime_stts_t *stts, int time_scale, float frame_rate) { - quicktime_stts_table_t *table; - quicktime_stts_init_table(stts); - table = &(stts->table[0]); - table->sample_count = 0; /* need to set this when closing */ - table->sample_duration = time_scale / frame_rate; - //printf("quicktime_stts_init_video %ld %f\n", time_scale, (double)frame_rate); + return QT_OK; } -static void quicktime_stts_init_audio(quicktime_t *file, quicktime_stts_t *stts, int sample_rate) { - quicktime_stts_table_t *table; - quicktime_stts_init_table(stts); - table = &(stts->table[0]); - table->sample_count = 0; /* need to set this when closing */ - table->sample_duration = 1; -} - -static void quicktime_stts_delete(quicktime_stts_t *stts) { - if(stts->total_entries) free(stts->table); - stts->total_entries = 0; -} - -static void quicktime_read_stts(quicktime_t *file, - quicktime_stts_t *stts, - long time_scale) { - int i; - - stts->version = quicktime_read_char(file); - stts->flags = quicktime_read_int24(file); - stts->total_entries = quicktime_read_int32(file); - - stts->table = (quicktime_stts_table_t*)malloc(sizeof(quicktime_stts_table_t) * stts->total_entries); - printf ("demux_qt: reading stts... (time scale is %ld units/sec)\n", - time_scale); - for(i = 0; i < stts->total_entries; i++) { - stts->table[i].sample_count = quicktime_read_int32(file); - stts->table[i].sample_duration = quicktime_read_int32(file); +/* + * This function takes a pointer to a qt_info structure and a pointer to + * a buffer containing an uncompressed moov atom. When the function + * finishes successfully, qt_info will have a list of qt_frame objects, + * ordered by offset. + */ +static void parse_moov_atom(qt_info *info, unsigned char *moov_atom) { + int i, j; + 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; + unsigned int audio_byte_counter; + unsigned int total_audio_bytes; + int64_t audio_pts_multiplier; + + /* make sure this is actually a moov atom */ + if (BE_32(&moov_atom[4]) != MOOV_ATOM) { + info->last_error = QT_NO_MOOV_ATOM; + return; } -} - -/* stsc.c */ - -static void quicktime_stsc_init(quicktime_stsc_t *stsc) { - stsc->version = 0; - stsc->flags = 0; - stsc->total_entries = 0; - stsc->entries_allocated = 0; -} -static void quicktime_stsc_init_table(quicktime_t *file, quicktime_stsc_t *stsc) { - if(!stsc->entries_allocated) { - stsc->total_entries = 0; - stsc->entries_allocated = 2000; - stsc->table = (quicktime_stsc_table_t*)calloc(1, sizeof(quicktime_stsc_table_t) * stsc->entries_allocated); + /* prowl through the moov atom looking for very specific targets */ + for (i = ATOM_PREAMBLE_SIZE; i < moov_atom_size - 4; i++) { + current_atom = BE_32(&moov_atom[i]); + + if (current_atom == MVHD_ATOM) { + parse_mvhd_atom(info, &moov_atom[i - 4]); + if (info->last_error != QT_OK) + return; + i += BE_32(&moov_atom[i - 4]) - 4; + } else if (current_atom == MINF_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)); + + parse_minf_atom(&sample_tables[sample_table_count - 1], + &moov_atom[i - 4]); + if (info->last_error != QT_OK) + return; + i += BE_32(&moov_atom[i - 4]) - 4; + } } -} -static void quicktime_stsc_init_video(quicktime_t *file, quicktime_stsc_t *stsc) { - quicktime_stsc_table_t *table; - quicktime_stsc_init_table(file, stsc); - table = &(stsc->table[0]); - table->chunk = 1; - table->samples = 1; - table->id = 1; -} + /* build frame tables corresponding to each sample table */ + info->frame_count = 0; + for (i = 0; i < sample_table_count; i++) { -static void quicktime_stsc_init_audio(quicktime_t *file, quicktime_stsc_t *stsc, int sample_rate) { - quicktime_stsc_table_t *table; - quicktime_stsc_init_table(file, stsc); - table = &(stsc->table[0]); - table->chunk = 1; - table->samples = 0; /* set this after completion or after every audio chunk is written */ - table->id = 1; -} + build_frame_table(&sample_tables[i]); + info->frame_count += sample_tables[i].frame_count; -static void quicktime_stsc_delete(quicktime_stsc_t *stsc) { - if(stsc->total_entries) - free(stsc->table); - stsc->total_entries = 0; -} + /* while traversing tables, look for A/V information */ + if (sample_tables[i].type == MEDIA_VIDEO) { -static void quicktime_read_stsc(quicktime_t *file, quicktime_stsc_t *stsc) -{ - long i; - stsc->version = quicktime_read_char(file); - stsc->flags = quicktime_read_int24(file); - stsc->total_entries = quicktime_read_int32(file); - - stsc->entries_allocated = stsc->total_entries; - stsc->table = (quicktime_stsc_table_t*)malloc(sizeof(quicktime_stsc_table_t) * stsc->total_entries); - for(i = 0; i < stsc->total_entries; i++) { - stsc->table[i].chunk = quicktime_read_int32(file); - stsc->table[i].samples = quicktime_read_int32(file); - stsc->table[i].id = quicktime_read_int32(file); - } -} + info->video_width = sample_tables[i].media_description.video.width; + info->video_height = sample_tables[i].media_description.video.height; + info->video_codec = + sample_tables[i].media_description.video.codec_format; + } else if (sample_tables[i].type == MEDIA_AUDIO) { -static int quicktime_update_stsc(quicktime_stsc_t *stsc, long chunk, long samples) { - /* long i; */ + 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; - if (chunk > stsc->entries_allocated) { - stsc->entries_allocated = chunk * 2; - stsc->table =(quicktime_stsc_table_t*)realloc(stsc->table, sizeof(quicktime_stsc_table_t) * stsc->entries_allocated); + } } - stsc->table[chunk - 1].samples = samples; - stsc->table[chunk - 1].chunk = chunk; - stsc->table[chunk - 1].id = 1; - if (chunk > stsc->total_entries) - stsc->total_entries = chunk; - return 0; -} - -/* Optimizing while writing doesn't allow seeks during recording so */ -/* entries are created for every chunk and only optimized during */ -/* writeout. Unfortunately there's no way to keep audio synchronized */ -/* after overwriting a recording as the fractional audio chunk in the */ -/* middle always overwrites the previous location of a larger chunk. On */ -/* writing, the table must be optimized. RealProducer requires an */ -/* optimized table. */ - -/* stsz.c */ - -static void quicktime_stsz_init(quicktime_stsz_t *stsz) { - stsz->version = 0; - stsz->flags = 0; - stsz->sample_size = 0; - stsz->total_entries = 0; - stsz->entries_allocated = 0; -} - -static void quicktime_stsz_init_video(quicktime_t *file, quicktime_stsz_t *stsz) { - stsz->sample_size = 0; - if(!stsz->entries_allocated) { - stsz->entries_allocated = 2000; - stsz->total_entries = 0; - stsz->table = (quicktime_stsz_table_t*)malloc(sizeof(quicktime_stsz_table_t) * stsz->entries_allocated); + /* 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; } -} - -static void quicktime_stsz_init_audio(quicktime_t *file, quicktime_stsz_t *stsz, int channels, int bits) { - /*stsz->sample_size = channels * bits / 8; */ - stsz->sample_size = 1; /* ? */ - stsz->total_entries = 0; /* set this when closing */ - stsz->entries_allocated = 0; -} - -static void quicktime_stsz_delete(quicktime_stsz_t *stsz) { - if(!stsz->sample_size && stsz->total_entries) - free(stsz->table); - stsz->total_entries = 0; - stsz->entries_allocated = 0; -} -static void quicktime_read_stsz(quicktime_t *file, quicktime_stsz_t *stsz) { - long i; - stsz->version = quicktime_read_char(file); - stsz->flags = quicktime_read_int24(file); - stsz->sample_size = quicktime_read_int32(file); - stsz->total_entries = quicktime_read_int32(file); - stsz->entries_allocated = stsz->total_entries; - if(!stsz->sample_size) { - stsz->table = (quicktime_stsz_table_t*)malloc(sizeof(quicktime_stsz_table_t) * stsz->entries_allocated); - for(i = 0; i < stsz->total_entries; i++) { - stsz->table[i].size = quicktime_read_int32(file); - } + /* 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; } -} - -static void quicktime_update_stsz(quicktime_stsz_t *stsz, long sample, long sample_size) -{ - /* long i; */ - - if(!stsz->sample_size) - { - if(sample >= stsz->entries_allocated) - { - stsz->entries_allocated = sample * 2; - stsz->table = (quicktime_stsz_table_t*)realloc(stsz->table, sizeof(quicktime_stsz_table_t) * stsz->entries_allocated); - } - - // printf("sample %ld sample_size %ld\n", sample, sample_size); - stsz->table[sample].size = sample_size; - if(sample >= stsz->total_entries) stsz->total_entries = sample + 1; - } -} - -/* stco.c */ - -static void quicktime_stco_init(quicktime_stco_t *stco) -{ - stco->version = 0; - stco->flags = 0; - stco->total_entries = 0; - stco->entries_allocated = 0; -} - -static void quicktime_stco_delete(quicktime_stco_t *stco) -{ - if(stco->total_entries) free(stco->table); - stco->total_entries = 0; - stco->entries_allocated = 0; -} - -static void quicktime_stco_init_common(quicktime_t *file, quicktime_stco_t *stco) -{ - if(!stco->entries_allocated) - { - stco->entries_allocated = 2000; - stco->total_entries = 0; - stco->table = (quicktime_stco_table_t*)malloc(sizeof(quicktime_stco_table_t) * stco->entries_allocated); - /*printf("quicktime_stco_init_common %x\n", stco->table); */ - } -} - -static void quicktime_read_stco(quicktime_t *file, quicktime_stco_t *stco) -{ - long i; - stco->version = quicktime_read_char(file); - stco->flags = quicktime_read_int24(file); - stco->total_entries = quicktime_read_int32(file); - stco->entries_allocated = stco->total_entries; - stco->table = (quicktime_stco_table_t*)calloc(1, sizeof(quicktime_stco_table_t) * stco->entries_allocated); - for(i = 0; i < stco->total_entries; i++) - { - stco->table[i].offset = quicktime_read_uint32(file); - } -} - -static void quicktime_read_stco64(quicktime_t *file, quicktime_stco_t *stco) -{ - long i; - stco->version = quicktime_read_char(file); - stco->flags = quicktime_read_int24(file); - stco->total_entries = quicktime_read_int32(file); - stco->entries_allocated = stco->total_entries; - stco->table = (quicktime_stco_table_t*)calloc(1, sizeof(quicktime_stco_table_t) * stco->entries_allocated); - for(i = 0; i < stco->total_entries; i++) - { - stco->table[i].offset = quicktime_read_int64(file); - } -} - -static void quicktime_update_stco(quicktime_stco_t *stco, long chunk, longest offset) -{ - /* long i; */ - - if(chunk > stco->entries_allocated) - { - stco->entries_allocated = chunk * 2; - stco->table = (quicktime_stco_table_t*)realloc(stco->table, sizeof(quicktime_stco_table_t) * stco->entries_allocated); - } - - stco->table[chunk - 1].offset = offset; - if(chunk > stco->total_entries) stco->total_entries = chunk; -} - -/* stss.c */ - -static void quicktime_stss_init(quicktime_stss_t *stss) -{ - stss->version = 0; - stss->flags = 0; - stss->total_entries = 0; -} - -static void quicktime_stss_delete(quicktime_stss_t *stss) -{ - if(stss->total_entries) free(stss->table); - stss->total_entries = 0; -} - -static void quicktime_read_stss(quicktime_t *file, quicktime_stss_t *stss) -{ - long i; - stss->version = quicktime_read_char(file); - stss->flags = quicktime_read_int24(file); - stss->total_entries = quicktime_read_int32(file); - - stss->table = (quicktime_stss_table_t*)malloc(sizeof(quicktime_stss_table_t) * stss->total_entries); - for(i = 0; i < stss->total_entries; i++) - { - stss->table[i].sample = quicktime_read_int32(file); - } -} - - - -/* stbl.c */ - -static void quicktime_stbl_init(quicktime_stbl_t *stbl) -{ - stbl->version = 0; - stbl->flags = 0; - quicktime_stsd_init(&(stbl->stsd)); - quicktime_stts_init(&(stbl->stts)); - quicktime_stss_init(&(stbl->stss)); - quicktime_stsc_init(&(stbl->stsc)); - quicktime_stsz_init(&(stbl->stsz)); - quicktime_stco_init(&(stbl->stco)); -} - -static void quicktime_stbl_init_video(quicktime_t *file, - quicktime_stbl_t *stbl, - int frame_w, - int frame_h, - int time_scale, - float frame_rate, - char *compressor) -{ - //printf("quicktime_stbl_init_video 1\n"); - quicktime_stsd_init_video(file, &(stbl->stsd), frame_w, frame_h, frame_rate, compressor); - //printf("quicktime_stbl_init_video 1 %d %f\n", time_scale, (double)frame_rate); - quicktime_stts_init_video(file, &(stbl->stts), time_scale, frame_rate); - //printf("quicktime_stbl_init_video 1\n"); - quicktime_stsc_init_video(file, &(stbl->stsc)); - //printf("quicktime_stbl_init_video 1\n"); - quicktime_stsz_init_video(file, &(stbl->stsz)); - //printf("quicktime_stbl_init_video 1\n"); - quicktime_stco_init_common(file, &(stbl->stco)); - //printf("quicktime_stbl_init_video 2\n"); -} - - -static void quicktime_stbl_init_audio(quicktime_t *file, - quicktime_stbl_t *stbl, - int channels, - int sample_rate, - int bits, - char *compressor) -{ - quicktime_stsd_init_audio(file, &(stbl->stsd), channels, sample_rate, bits, compressor); - quicktime_stts_init_audio(file, &(stbl->stts), sample_rate); - quicktime_stsc_init_audio(file, &(stbl->stsc), sample_rate); - quicktime_stsz_init_audio(file, &(stbl->stsz), channels, bits); - quicktime_stco_init_common(file, &(stbl->stco)); -} - -static void quicktime_stbl_delete(quicktime_stbl_t *stbl) -{ - quicktime_stsd_delete(&(stbl->stsd)); - quicktime_stts_delete(&(stbl->stts)); - quicktime_stss_delete(&(stbl->stss)); - quicktime_stsc_delete(&(stbl->stsc)); - quicktime_stsz_delete(&(stbl->stsz)); - quicktime_stco_delete(&(stbl->stco)); -} - -static int quicktime_read_stbl(quicktime_t *file, quicktime_minf_t *minf, long time_scale, - quicktime_stbl_t *stbl, quicktime_atom_t *parent_atom) { - quicktime_atom_t leaf_atom; - - do { - quicktime_atom_read_header(file, &leaf_atom); - - //printf("quicktime_read_stbl 1\n"); - /* mandatory */ - if(quicktime_atom_is(&leaf_atom, "stsd")) { - //printf("STSD start %lld end %lld", leaf_atom.start, leaf_atom.end); - quicktime_read_stsd(file, minf, &(stbl->stsd)); - /* Some codecs store extra information at the end of this */ - quicktime_atom_skip(file, &leaf_atom); - } else if(quicktime_atom_is(&leaf_atom, "stts")) { - quicktime_read_stts(file, &(stbl->stts), time_scale); - } else if(quicktime_atom_is(&leaf_atom, "stss")) { - quicktime_read_stss(file, &(stbl->stss)); - } else if(quicktime_atom_is(&leaf_atom, "stsc")) { - quicktime_read_stsc(file, &(stbl->stsc)); - } else if(quicktime_atom_is(&leaf_atom, "stsz")) { - quicktime_read_stsz(file, &(stbl->stsz)); - } else if(quicktime_atom_is(&leaf_atom, "co64")) { - quicktime_read_stco64(file, &(stbl->stco)); - } else if(quicktime_atom_is(&leaf_atom, "stco")) { - quicktime_read_stco(file, &(stbl->stco)); - } else - quicktime_atom_skip(file, &leaf_atom); - } while (quicktime_position(file) < parent_atom->end); - - return 0; -} - - -/* minf.c */ - -static void quicktime_minf_init(quicktime_minf_t *minf) -{ - minf->is_video = minf->is_audio = 0; - quicktime_vmhd_init(&(minf->vmhd)); - quicktime_smhd_init(&(minf->smhd)); - quicktime_hdlr_init(&(minf->hdlr)); - quicktime_dinf_init(&(minf->dinf)); - quicktime_stbl_init(&(minf->stbl)); -} - -static void quicktime_minf_init_video(quicktime_t *file, - quicktime_minf_t *minf, - int frame_w, - int frame_h, - int time_scale, - float frame_rate, - char *compressor) -{ - minf->is_video = 1; - //printf("quicktime_minf_init_video 1\n"); - quicktime_vmhd_init_video(file, &(minf->vmhd), frame_w, frame_h, frame_rate); - //printf("quicktime_minf_init_video 1 %d %f\n", time_scale, (double)frame_rate); - quicktime_stbl_init_video(file, &(minf->stbl), frame_w, frame_h, time_scale, frame_rate, compressor); - //printf("quicktime_minf_init_video 2\n"); - quicktime_hdlr_init_data(&(minf->hdlr)); - //printf("quicktime_minf_init_video 1\n"); - quicktime_dinf_init_all(&(minf->dinf)); - //printf("quicktime_minf_init_video 2\n"); -} - -static void quicktime_minf_init_audio(quicktime_t *file, - quicktime_minf_t *minf, - int channels, - int sample_rate, - int bits, - char *compressor) -{ - minf->is_audio = 1; - /* smhd doesn't store anything worth initializing */ - quicktime_stbl_init_audio(file, &(minf->stbl), channels, sample_rate, bits, compressor); - quicktime_hdlr_init_data(&(minf->hdlr)); - quicktime_dinf_init_all(&(minf->dinf)); -} - -static void quicktime_minf_delete(quicktime_minf_t *minf) -{ - quicktime_vmhd_delete(&(minf->vmhd)); - quicktime_smhd_delete(&(minf->smhd)); - quicktime_dinf_delete(&(minf->dinf)); - quicktime_stbl_delete(&(minf->stbl)); - quicktime_hdlr_delete(&(minf->hdlr)); -} - -static int quicktime_read_minf(quicktime_t *file, quicktime_minf_t *minf, - long time_scale, - quicktime_atom_t *parent_atom) { - quicktime_atom_t leaf_atom; - - do { - quicktime_atom_read_header(file, &leaf_atom); - //printf("quicktime_read_minf 1\n"); - - /* mandatory */ - if (quicktime_atom_is(&leaf_atom, "vmhd")) { - minf->is_video = 1; quicktime_read_vmhd(file, &(minf->vmhd)); - } else if (quicktime_atom_is(&leaf_atom, "smhd")) { - minf->is_audio = 1; quicktime_read_smhd(file, &(minf->smhd)); - } else if (quicktime_atom_is(&leaf_atom, "hdlr")) { - quicktime_read_hdlr(file, &(minf->hdlr)); - /* Main Actor doesn't write component name */ - quicktime_atom_skip(file, &leaf_atom); - } else if (quicktime_atom_is(&leaf_atom, "dinf")) { quicktime_read_dinf(file, &(minf->dinf), &leaf_atom); - } else if (quicktime_atom_is(&leaf_atom, "stbl")){ - quicktime_read_stbl(file, minf, time_scale, &(minf->stbl), &leaf_atom); - } else - quicktime_atom_skip(file, &leaf_atom); - } while (quicktime_position(file) < parent_atom->end); - - return 0; -} - -/* mdia.c */ - - -static void quicktime_mdia_init(quicktime_mdia_t *mdia) -{ - quicktime_mdhd_init(&(mdia->mdhd)); - quicktime_hdlr_init(&(mdia->hdlr)); - quicktime_minf_init(&(mdia->minf)); -} - -static void quicktime_mdia_init_video(quicktime_t *file, - quicktime_mdia_t *mdia, - int frame_w, - int frame_h, - float frame_rate, - char *compressor) -{ - //printf("quicktime_mdia_init_video 1\n"); - quicktime_mdhd_init_video(file, &(mdia->mdhd), frame_w, frame_h, frame_rate); - //printf("quicktime_mdia_init_video 1 %d %f\n", mdia->mdhd.time_scale, (double)frame_rate); - quicktime_minf_init_video(file, &(mdia->minf), frame_w, frame_h, mdia->mdhd.time_scale, frame_rate, compressor); - //printf("quicktime_mdia_init_video 1\n"); - quicktime_hdlr_init_video(&(mdia->hdlr)); - //printf("quicktime_mdia_init_video 2\n"); -} - -static void quicktime_mdia_init_audio(quicktime_t *file, - quicktime_mdia_t *mdia, - int channels, - int sample_rate, - int bits, - char *compressor) -{ - quicktime_mdhd_init_audio(file, &(mdia->mdhd), channels, sample_rate, bits, compressor); - quicktime_minf_init_audio(file, &(mdia->minf), channels, sample_rate, bits, compressor); - quicktime_hdlr_init_audio(&(mdia->hdlr)); -} - -static void quicktime_mdia_delete(quicktime_mdia_t *mdia) -{ - quicktime_mdhd_delete(&(mdia->mdhd)); - quicktime_hdlr_delete(&(mdia->hdlr)); - quicktime_minf_delete(&(mdia->minf)); -} - -static int quicktime_read_mdia(quicktime_t *file, quicktime_mdia_t *mdia, - quicktime_atom_t *trak_atom) { - - quicktime_atom_t leaf_atom; - - do { - quicktime_atom_read_header(file, &leaf_atom); - //printf("quicktime_read_mdia 0x%llx\n", quicktime_position(file)); - - /* mandatory */ - if(quicktime_atom_is(&leaf_atom, "mdhd")) { - quicktime_read_mdhd(file, &(mdia->mdhd)); - } else if(quicktime_atom_is(&leaf_atom, "hdlr")) { - quicktime_read_hdlr(file, &(mdia->hdlr)); - /* Main Actor doesn't write component name */ - quicktime_atom_skip(file, &leaf_atom); - } else if(quicktime_atom_is(&leaf_atom, "minf")) { - quicktime_read_minf(file, &(mdia->minf), mdia->mdhd.time_scale, &leaf_atom); - } else - quicktime_atom_skip(file, &leaf_atom); - } while (quicktime_position(file) < trak_atom->end); - - - return 0; -} - - -/* trak.c */ - - -static int quicktime_trak_init(quicktime_trak_t *trak) -{ - quicktime_tkhd_init(&(trak->tkhd)); - quicktime_edts_init(&(trak->edts)); - quicktime_mdia_init(&(trak->mdia)); - return 0; -} - -static int quicktime_trak_init_video(quicktime_t *file, - quicktime_trak_t *trak, - int frame_w, - int frame_h, - float frame_rate, - char *compressor) -{ - //printf("quicktime_trak_init_video 1\n"); - quicktime_tkhd_init_video(file, - &(trak->tkhd), - frame_w, - frame_h); - //printf("quicktime_trak_init_video 1\n"); - quicktime_mdia_init_video(file, - &(trak->mdia), - frame_w, - frame_h, - frame_rate, - compressor); - //printf("quicktime_trak_init_video 2\n"); - quicktime_edts_init_table(&(trak->edts)); - //printf("quicktime_trak_init_video 2\n"); - - return 0; -} - -static int quicktime_trak_init_audio(quicktime_t *file, - quicktime_trak_t *trak, - int channels, - int sample_rate, - int bits, - char *compressor) -{ - quicktime_mdia_init_audio(file, &(trak->mdia), channels, sample_rate, bits, compressor); - quicktime_edts_init_table(&(trak->edts)); - - return 0; -} - -static int quicktime_trak_delete(quicktime_trak_t *trak) -{ - quicktime_tkhd_delete(&(trak->tkhd)); - quicktime_edts_delete(&(trak->edts)); - quicktime_mdia_delete(&(trak->mdia)); - return 0; -} - - -static quicktime_trak_t* quicktime_add_trak(quicktime_moov_t *moov) -{ - if(moov->total_tracks < MAXTRACKS) - { - moov->trak[moov->total_tracks] = malloc(sizeof(quicktime_trak_t)); - quicktime_trak_init(moov->trak[moov->total_tracks]); - moov->total_tracks++; - } - return moov->trak[moov->total_tracks - 1]; -} - -static int quicktime_delete_trak(quicktime_moov_t *moov) -{ - if(moov->total_tracks) - { - moov->total_tracks--; - quicktime_trak_delete(moov->trak[moov->total_tracks]); - free(moov->trak[moov->total_tracks]); - } - return 0; -} - -static int quicktime_read_trak(quicktime_t *file, quicktime_trak_t *trak, quicktime_atom_t *trak_atom) -{ - quicktime_atom_t leaf_atom; - - do { - quicktime_atom_read_header(file, &leaf_atom); - - /* - printf ("demux_qt: found trak atom, type >%c %c %c %c<\n", - leaf_atom.type[0],leaf_atom.type[1],leaf_atom.type[2],leaf_atom.type[3]); - */ - - /* mandatory */ - - if(quicktime_atom_is(&leaf_atom, "tkhd")) { - quicktime_read_tkhd(file, &(trak->tkhd)); - } else if (quicktime_atom_is(&leaf_atom, "mdia")) { - quicktime_read_mdia(file, &(trak->mdia), &leaf_atom); - } else { /* optional */ - if(quicktime_atom_is(&leaf_atom, "clip")) { - quicktime_atom_skip(file, &leaf_atom); - } else if(quicktime_atom_is(&leaf_atom, "matt")) { - quicktime_atom_skip(file, &leaf_atom); - } else if(quicktime_atom_is(&leaf_atom, "edts")) { - quicktime_read_edts(file, &(trak->edts), &leaf_atom); - } else if (quicktime_atom_is(&leaf_atom, "load")) { - quicktime_atom_skip(file, &leaf_atom); - } else if(quicktime_atom_is(&leaf_atom, "tref")) { - quicktime_atom_skip(file, &leaf_atom); - } else if(quicktime_atom_is(&leaf_atom, "imap")) { - quicktime_atom_skip(file, &leaf_atom); - } else if(quicktime_atom_is(&leaf_atom, "udta")){ - quicktime_atom_skip(file, &leaf_atom); - } else { - /* printf ("skipping this atom\n"); */ - quicktime_atom_skip(file, &leaf_atom); - /* printf("quicktime_read_trak 0x%llx 0x%llx\n", quicktime_position(file), leaf_atom.end); */ + /* 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 = 0; + min_offset = sample_tables[0].frames[sample_table_indices[0]].offset; + for (j = 1; j < sample_table_count; j++) { + if ((sample_table_indices[j] != info->frame_count) && + (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; } } - } while(quicktime_position(file) < trak_atom->end); - - return 0; -} - -#if 0 -static longest quicktime_track_end(quicktime_trak_t *trak) -{ -/* get the byte endpoint of the track in the file */ - longest size = 0; - longest chunk, chunk_offset, chunk_samples, sample; - quicktime_stsz_t *stsz = &(trak->mdia.minf.stbl.stsz); - /* quicktime_stsz_table_t *table = stsz->table; */ - quicktime_stsc_t *stsc = &(trak->mdia.minf.stbl.stsc); - quicktime_stco_t *stco; - - /* get the last chunk offset */ - /* the chunk offsets contain the HEADER_LENGTH themselves */ - stco = &(trak->mdia.minf.stbl.stco); - chunk = stco->total_entries; - size = chunk_offset = stco->table[chunk - 1].offset; - - /* get the number of samples in the last chunk */ - chunk_samples = stsc->table[stsc->total_entries - 1].samples; - - /* get the size of last samples */ - if(stsz->sample_size) - { - /* assume audio so calculate the sample size */ - size += chunk_samples * stsz->sample_size - * trak->mdia.minf.stbl.stsd.table[0].channels - * trak->mdia.minf.stbl.stsd.table[0].sample_size / 8; - } - else - { - /* assume video */ - for(sample = stsz->total_entries - chunk_samples; - sample < stsz->total_entries; sample++) - { - size += stsz->table[sample].size; - } - } - - return size; -} -#endif - -static long quicktime_sample_of_chunk(quicktime_trak_t *trak, long chunk) -{ - quicktime_stsc_table_t *table = trak->mdia.minf.stbl.stsc.table; - long total_entries = trak->mdia.minf.stbl.stsc.total_entries; - long chunk1entry, chunk2entry; - long chunk1, chunk2, chunks, total = 0; - - for(chunk1entry = total_entries - 1, chunk2entry = total_entries; - chunk1entry >= 0; - chunk1entry--, chunk2entry--) - { - chunk1 = table[chunk1entry].chunk; - - if(chunk > chunk1) - { - if(chunk2entry < total_entries) - { - chunk2 = table[chunk2entry].chunk; - - if(chunk < chunk2) chunk2 = chunk; - } - else - chunk2 = chunk; - - chunks = chunk2 - chunk1; - - total += chunks * table[chunk1entry].samples; - } - } - - return total; -} - -static long quicktime_track_samples(quicktime_t *file, quicktime_trak_t *trak) -{ - quicktime_stsc_table_t *table = trak->mdia.minf.stbl.stsc.table; - long total_entries = trak->mdia.minf.stbl.stsc.total_entries; - long chunk = trak->mdia.minf.stbl.stco.total_entries; - long sample; - - if(chunk) - { - sample = quicktime_sample_of_chunk(trak, chunk); - sample += table[total_entries - 1].samples; - } - else - sample = 0; - - return sample; -} -static int quicktime_chunk_of_sample(longest *chunk_sample, - longest *chunk, - quicktime_trak_t *trak, - long sample) -{ - quicktime_stsc_table_t *table = trak->mdia.minf.stbl.stsc.table; - long total_entries = trak->mdia.minf.stbl.stsc.total_entries; - long chunk2entry, i, current_chunk, sample_duration; - long chunk1, chunk2, chunk1samples, range_samples, total = 0; - quicktime_stts_t *stts = &(trak->mdia.minf.stbl.stts); - - chunk1 = 1; - chunk1samples = 0; - chunk2entry = 0; - - if(!total_entries) { - *chunk_sample = 0; - *chunk = 0; - return 0; + 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; } - do - { - chunk2 = table[chunk2entry].chunk; - *chunk = chunk2 - chunk1; - range_samples = *chunk * chunk1samples; - - if(sample < total + range_samples) break; - - /* Yann: I've modified this to handle samples with duration - different from 1 ... needed by ".mp3" fourcc */ - - if(trak->mdia.minf.is_audio) - { - i = stts->total_entries - 1; - - do - { - current_chunk = stts->table[i].sample_count; - i--; - }while(i >= 0 && current_chunk > chunk2entry); - - sample_duration = stts->table[i+1].sample_duration; - } - else - sample_duration = 1; // this way nothing is broken ... I hope - - chunk1samples = table[chunk2entry].samples * sample_duration; - chunk1 = chunk2; - - if(chunk2entry < total_entries) - { - chunk2entry++; - total += range_samples; - } - }while(chunk2entry < total_entries); - - if(chunk1samples) - *chunk = (sample - total) / chunk1samples + chunk1; - else - *chunk = 1; - - *chunk_sample = total + (*chunk - chunk1) * chunk1samples; - return 0; -} - -static longest quicktime_chunk_to_offset(quicktime_trak_t *trak, long chunk) -{ - quicktime_stco_table_t *table = trak->mdia.minf.stbl.stco.table; - - if(trak->mdia.minf.stbl.stco.total_entries && chunk > trak->mdia.minf.stbl.stco.total_entries) - return table[trak->mdia.minf.stbl.stco.total_entries - 1].offset; - else - if(trak->mdia.minf.stbl.stco.total_entries) - return table[chunk - 1].offset; - return HEADER_LENGTH * 2; -} - -static long quicktime_offset_to_chunk(longest *chunk_offset, - quicktime_trak_t *trak, - longest offset) -{ - quicktime_stco_table_t *table = trak->mdia.minf.stbl.stco.table; - int i; - - for(i = trak->mdia.minf.stbl.stco.total_entries - 1; i >= 0; i--) - { - if(table[i].offset <= offset) - { - *chunk_offset = table[i].offset; - return i + 1; - } - } - - /* Yann: I really wonder why we should return this */ - /* *chunk_offset = HEADER_LENGTH * 2; */ - /* I return the first chunk offset instead */ - if(trak->mdia.minf.stbl.stco.total_entries) - *chunk_offset = table[0].offset; - else - /* In this case there is no chunk in the file ... retuning -1 */ - *chunk_offset = -1; - - return 1; -} - -static longest quicktime_samples_to_bytes(quicktime_trak_t *track, long samples) -{ - /* char *compressor = track->mdia.minf.stbl.stsd.table[0].format; */ - int channels = track->mdia.minf.stbl.stsd.table[0].channels; - - /* Default use the sample size specification for TWOS and RAW */ - return samples * channels * track->mdia.minf.stbl.stsd.table[0].sample_size / 8; -} + /* fill in the missing and incomplete information (pts and frame sizes) */ + audio_byte_counter = 0; + audio_pts_multiplier = 90000; + audio_pts_multiplier *= info->duration; + audio_pts_multiplier /= info->time_scale; + for (i = 0; i < info->frame_count; i++) { + if (info->frames[i].type == MEDIA_VIDEO) { + /* finish the pts calculation for this video frame */ + info->frames[i].pts *= 90000; + info->frames[i].pts /= info->time_scale; -static longest quicktime_sample_range_size(quicktime_trak_t *trak, - long chunk_sample, - long sample) -{ - /* quicktime_stsz_table_t *table = trak->mdia.minf.stbl.stsz.table; */ - quicktime_stts_t *stts = &(trak->mdia.minf.stbl.stts); - longest i, total; - - if(trak->mdia.minf.stbl.stsz.sample_size) - { - /* assume audio */ - return quicktime_samples_to_bytes(trak, sample - chunk_sample); - /* return (sample - chunk_sample) * trak->mdia.minf.stbl.stsz.sample_size */ - /* * trak->mdia.minf.stbl.stsd.table[0].channels */ - /* * trak->mdia.minf.stbl.stsd.table[0].sample_size / 8; */ - } - else - { - /* probably video */ - if(trak->mdia.minf.is_video) - { - for(i = chunk_sample, total = 0; i < sample; i++) - { - total += trak->mdia.minf.stbl.stsz.table[i].size; - } - } - else // Yann: again, for my .mp3 VBR ... - { - long duration_index = 0; - long duration = stts->table[duration_index].sample_duration; - long sample_passed = 0; - //printf("\t\t VBR audio duration %d\n", duration); - - for(i = chunk_sample, total = 0; i < sample; i+=duration) - { - long chunk_index = i/duration; - //printf("\t\t i/duration %li\n", i/duration); - total += trak->mdia.minf.stbl.stsz.table[chunk_index].size; - - if(chunk_index > sample_passed + stts->table[duration_index].sample_count) { - sample_passed += stts->table[duration_index].sample_count; - duration_index++; - duration = stts->table[duration_index].sample_duration; - - } - } - //printf("\t\t VBR audio total %d\n", total); - } - } - return total; -} + } else if (info->frames[i].type == MEDIA_AUDIO) { -static longest quicktime_sample_to_offset(quicktime_trak_t *trak, long sample) { - longest chunk, chunk_sample, chunk_offset1, chunk_offset2; + /* finish the pts calculation for this audio frame */ + total_audio_bytes = info->frames[i].pts; + info->frames[i].pts = audio_pts_multiplier; + info->frames[i].pts *= audio_byte_counter; + info->frames[i].pts /= total_audio_bytes; - quicktime_chunk_of_sample(&chunk_sample, &chunk, trak, sample); - printf("demux_qt: quicktime_sample_to_offset chunk %lld, chunk_sample %lld, sample %ld\n", - chunk, chunk_sample, sample); + /* figure out the audio frame size */ + if (i < info->frame_count - 1) + info->frames[i].size = + info->frames[i + 1].offset - info->frames[i].offset; + else + info->frames[i].size = + info->moov_last_offset - info->frames[i].offset; - chunk_offset1 = quicktime_chunk_to_offset(trak, chunk); - chunk_offset2 = chunk_offset1 + quicktime_sample_range_size(trak, chunk_sample, sample); - return chunk_offset2; -} + audio_byte_counter += info->frames[i].size; -static long quicktime_offset_to_sample(quicktime_trak_t *trak, longest offset) -{ - longest chunk_offset; - longest chunk = quicktime_offset_to_chunk(&chunk_offset, trak, offset); - longest chunk_sample = quicktime_sample_of_chunk(trak, chunk); - longest sample, sample_offset; - quicktime_stsz_table_t *table = trak->mdia.minf.stbl.stsz.table; - longest total_samples = trak->mdia.minf.stbl.stsz.total_entries; - - if(trak->mdia.minf.stbl.stsz.sample_size) - { - sample = chunk_sample + (offset - chunk_offset) / - trak->mdia.minf.stbl.stsz.sample_size; } - else - for(sample = chunk_sample, sample_offset = chunk_offset; - sample_offset < offset && sample < total_samples; ) - { - sample_offset += table[sample].size; - if(sample_offset < offset) sample++; - } - - return sample; -} - -#if 0 -static int quicktime_update_tables(quicktime_t *file, - quicktime_trak_t *trak, - longest offset, - longest chunk, - longest sample, - longest samples, - longest sample_size) -{ - if(offset + sample_size > file->mdat.atom.size) file->mdat.atom.size = offset + sample_size; - quicktime_update_stco(&(trak->mdia.minf.stbl.stco), chunk, offset); - if(sample_size) quicktime_update_stsz(&(trak->mdia.minf.stbl.stsz), sample, sample_size); - quicktime_update_stsc(&(trak->mdia.minf.stbl.stsc), chunk, samples); - return 0; -} -#endif - -static int quicktime_trak_duration(quicktime_trak_t *trak, - long *duration, - long *timescale) { - quicktime_stts_t *stts = &(trak->mdia.minf.stbl.stts); - int i; - *duration = 0; - - for(i = 0; i < stts->total_entries; i++) { - *duration += stts->table[i].sample_duration * stts->table[i].sample_count; } - *timescale = trak->mdia.mdhd.time_scale; - return 0; -} - -static int quicktime_trak_fix_counts(quicktime_t *file, quicktime_trak_t *trak) -{ - long samples = quicktime_track_samples(file, trak); - - trak->mdia.minf.stbl.stts.table[0].sample_count = samples; - - if(trak->mdia.minf.stbl.stsz.sample_size) - trak->mdia.minf.stbl.stsz.total_entries = samples; - - return 0; -} - -static long quicktime_chunk_samples(quicktime_trak_t *trak, long chunk) -{ - long result, current_chunk; - quicktime_stsc_t *stsc = &(trak->mdia.minf.stbl.stsc); - long i = stsc->total_entries - 1; - quicktime_stts_t *stts = &(trak->mdia.minf.stbl.stts); - - do - { - current_chunk = stsc->table[i].chunk; - result = stsc->table[i].samples; - i--; - }while(i >= 0 && current_chunk > chunk); - - i = stts->total_entries - 1; - - /* Yann: I've modified this to handle samples with a duration - different from 1 ... needed for ".mp3" fourcc */ - - do - { - current_chunk = stts->table[i].sample_count; - i--; - }while(i >= 0 && current_chunk > chunk); - - return result*stts->table[i+1].sample_duration; -} - -static int quicktime_trak_shift_offsets(quicktime_trak_t *trak, longest offset) -{ - quicktime_stco_t *stco = &(trak->mdia.minf.stbl.stco); - int i; - - for(i = 0; i < stco->total_entries; i++) - { - stco->table[i].offset += offset; - } - return 0; -} - - -/* moov.c */ - -static int quicktime_moov_init(quicktime_moov_t *moov) { - - int i; - - moov->total_tracks = 0; - for (i = 0 ; i < MAXTRACKS; i++) - moov->trak[i] = 0; - quicktime_mvhd_init(&(moov->mvhd)); - quicktime_udta_init(&(moov->udta)); - quicktime_ctab_init(&(moov->ctab)); - return 0; -} - -static int quicktime_moov_delete(quicktime_moov_t *moov) { - - /* int i; */ - while(moov->total_tracks) - quicktime_delete_trak(moov); - - quicktime_mvhd_delete(&(moov->mvhd)); - quicktime_udta_delete(&(moov->udta)); - quicktime_ctab_delete(&(moov->ctab)); - return 0; -} - -#define QT_zlib 0x7A6C6962 - -static int quicktime_read_moov(quicktime_t *file, quicktime_moov_t *moov, - quicktime_atom_t *parent_atom, xine_t *xine) { - - /* mandatory mvhd */ - quicktime_atom_t leaf_atom; - - do { - - quicktime_atom_read_header(file, &leaf_atom); - - /* - printf ("demux_qt: found moov atom, type >%c %c %c %c<\n", - leaf_atom.type[0],leaf_atom.type[1],leaf_atom.type[2],leaf_atom.type[3]); - */ - - if(quicktime_atom_is(&leaf_atom, "cmov")) { - quicktime_atom_t compressed_atom; - - unsigned char *cmov_buf = 0; - unsigned char *moov_buf = 0; - longest cmov_sz, tlen; - int moov_sz; - /* int cmov_ret = 0; */ - /* long cmov_comp = 0; */ - - quicktime_atom_read_header(file, &compressed_atom); - - if(quicktime_atom_is(&compressed_atom, "dcom")) { - /* quicktime_atom_t compressed_type_atom; */ - int zlibfourcc; - longest offset; - - - quicktime_read_char32(file, (char *)&zlibfourcc); - zlibfourcc = quicktime_atom_read_size((char *)&zlibfourcc); - - if (zlibfourcc != QT_zlib) - printf ("demux_qt: header not compressed with zlib\n"); - - if(compressed_atom.size - 4 > 0) { - offset = file->ftell_position + compressed_atom.size - 4; - file->quicktime_fseek(file, offset); - } - } - quicktime_atom_read_header(file, &compressed_atom); - - if(quicktime_atom_is(&compressed_atom, "cmvd")) { - z_stream zstrm; - int zret; - - /* read how large uncompressed moov will be */ - quicktime_read_char32(file, (char *)&moov_sz); - moov_sz = quicktime_atom_read_size((char *)&moov_sz); - cmov_sz = compressed_atom.size - 4; - - /* Allocate buffer for compressed header */ - cmov_buf = (unsigned char *)malloc( cmov_sz ); - if (cmov_buf == 0) { - printf ("demux_qt: QT cmov: malloc err 0"); - abort(); - } - /* Read in compressed header */ - - tlen = file->quicktime_read_data(file, (char*)cmov_buf, cmov_sz); - - if (tlen != 1) { - printf ("demux_qt: QT cmov: read err tlen %llu\n", tlen); - if(cmov_buf) - free(cmov_buf); - return 0; - } - - /* Allocate buffer for decompressed header */ - moov_sz += 16; /* slop?? */ - moov_buf = (unsigned char *)malloc( moov_sz ); - if (moov_buf == 0) { - printf ("demux_qt: QT cmov: malloc err moov_sz %u\n", moov_sz); - abort(); - } - - zstrm.zalloc = (alloc_func)0; - zstrm.zfree = (free_func)0; - zstrm.opaque = (voidpf)0; - zstrm.next_in = cmov_buf; - zstrm.avail_in = cmov_sz; - zstrm.next_out = moov_buf; - zstrm.avail_out = moov_sz; - - zret = inflateInit(&zstrm); - if (zret != Z_OK) { - printf ("demux_qt: QT cmov: inflateInit err %d\n", zret); - break; - } - zret = inflate(&zstrm, Z_NO_FLUSH); - if ((zret != Z_OK) && (zret != Z_STREAM_END)) { - printf ("demux_qt: QT cmov inflate: ERR %d\n", zret); - break; - } else { - FILE *DecOut; - - DecOut = fopen("Out.bin", "w"); - fwrite(moov_buf, 1, moov_sz, DecOut); - fclose(DecOut); - } - moov_sz = zstrm.total_out; - zret = inflateEnd(&zstrm); - - file->decompressed_buffer_size = moov_sz; - file->decompressed_buffer = (char*)moov_buf; - file->decompressed_position = 8; /* Passing the first moov */ - - - - } /* end of "cmvd" */ - /* - if (cmov_buf) free(cmov_buf); - if (moov_buf) free(moov_buf); - if (cmov_ret == 0) return(cmov_ret); //failed or unsupported */ - } /* end of cmov */ - else if(quicktime_atom_is(&leaf_atom, "mvhd")) { - quicktime_read_mvhd(file, &(moov->mvhd)); - } else if(quicktime_atom_is(&leaf_atom, "clip")) { - quicktime_atom_skip(file, &leaf_atom); - } else if(quicktime_atom_is(&leaf_atom, "trak")) { - quicktime_trak_t *trak = quicktime_add_trak(moov); - quicktime_read_trak(file, trak, &leaf_atom); - } else if(quicktime_atom_is(&leaf_atom, "udta")) { - quicktime_read_udta(file, &(moov->udta), &leaf_atom); - quicktime_atom_skip(file, &leaf_atom); - } else if (quicktime_atom_is(&leaf_atom, "ctab")) { - quicktime_read_ctab(file, &(moov->ctab)); - } else - quicktime_atom_skip(file, &leaf_atom); - - /* printf("quicktime_read_moov 0x%llx 0x%llx\n", quicktime_position(file), parent_atom->end); */ - } while ((quicktime_position(file) < parent_atom->end && file->decompressed_buffer==NULL) - || (quicktime_position(file) < file->decompressed_buffer_size - && file->decompressed_buffer!=NULL)); - - return 1; -} - - -static int quicktime_shift_offsets(quicktime_moov_t *moov, longest offset) -{ - int i; - for(i = 0; i < moov->total_tracks; i++) - { - quicktime_trak_shift_offsets(moov->trak[i], offset); - } - return 0; -} - - -static int quicktime_update_positions(quicktime_t *file) -{ - /* Get the sample position from the file offset */ - /* for routines that change the positions of all tracks, like */ - /* seek_end and seek_start but not for routines that reposition one track, like */ - /* set_audio_position. */ - - longest mdat_offset = quicktime_position(file) - file->mdat.atom.start; - longest sample, chunk, chunk_offset; - int i; - - if(file->total_atracks) - { - sample = quicktime_offset_to_sample(file->atracks[0].track, mdat_offset); - chunk = quicktime_offset_to_chunk(&chunk_offset, file->atracks[0].track, mdat_offset); - for(i = 0; i < file->total_atracks; i++) - { - file->atracks[i].current_position = sample; - file->atracks[i].current_chunk = chunk; - } - } - - if(file->total_vtracks) - { - sample = quicktime_offset_to_sample(file->vtracks[0].track, mdat_offset); - chunk = quicktime_offset_to_chunk(&chunk_offset, file->vtracks[0].track, mdat_offset); - for(i = 0; i < file->total_vtracks; i++) - { - file->vtracks[i].current_position = sample; - file->vtracks[i].current_chunk = chunk; - } - } - return 0; -} - -static int quicktime_seek_start(quicktime_t *file) -{ - quicktime_set_position(file, file->mdat.atom.start + HEADER_LENGTH * 2); - quicktime_update_positions(file); - return 0; -} - -static long quicktime_audio_length(quicktime_t *file, int track) -{ - quicktime_stts_t *stts = &(file->atracks[track].track->mdia.minf.stbl.stts); - long i; - - if(file->total_atracks > 0) { - i = stts->total_entries - 1; - - /* Yann: I've modified this to handle samples with a duration - different from 1 ... needed for ".mp3" fourcc */ - - return stts->table[0].sample_duration*quicktime_track_samples(file, file->atracks[track].track); + /* free the temporary tables on the way out */ +/* + for (j = 0; j < sample_table_count; j++) { + free(sample_tables[i].chunk_offset_table); + 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); } - - return 0; -} - -static long quicktime_video_length(quicktime_t *file, int track) -{ - /*printf("quicktime_video_length %d %ld\n", quicktime_track_samples(file, file->vtracks[track].track), track); */ - if(file->total_vtracks > 0) - return quicktime_track_samples(file, file->vtracks[track].track); - return 0; -} - -static long quicktime_audio_position(quicktime_t *file, int track) -{ - return file->atracks[track].current_position; -} - -static long quicktime_video_position(quicktime_t *file, int track) -{ - return file->vtracks[track].current_position; +*/ + free(sample_tables); } -static int quicktime_set_audio_position(quicktime_t *file, longest sample, int track) -{ - longest offset, chunk_sample, chunk; - quicktime_trak_t *trak; - - if(file->total_atracks) { - trak = file->atracks[track].track; - file->atracks[track].current_position = sample; - // printf("BEFORE quicktime_chunk_of_sample track %d sample %li\n", track, sample); - quicktime_chunk_of_sample(&chunk_sample, &chunk, trak, sample); - file->atracks[track].current_chunk = chunk; - // printf("AFTER quicktime_chunk_of_sample chunk %d chunk_sample %d\n", chunk, chunk_sample); - offset = quicktime_sample_to_offset(trak, sample); - // printf("AFTER quicktime_sample_to_offset offset %li\n", offset); - quicktime_set_position(file, offset); - } +static qt_error open_qt_file(qt_info *info, input_plugin_t *input) { - return 0; -} + unsigned char atom_preamble[ATOM_PREAMBLE_SIZE]; + int64_t top_level_atom_size; + qt_atom top_level_atom; + unsigned char *moov_atom = NULL; + int64_t preseek_pos; -static int quicktime_set_video_position(quicktime_t *file, longest frame, - int track) { - longest offset, chunk_sample, chunk; - quicktime_trak_t *trak; - - if(file->total_vtracks) { - trak = file->vtracks[track].track; - file->vtracks[track].current_position = frame; - quicktime_chunk_of_sample(&chunk_sample, &chunk, trak, frame); - file->vtracks[track].current_chunk = chunk; - offset = quicktime_sample_to_offset(trak, frame); - quicktime_set_position(file, offset); - } - return 0; -} + /* zlib stuff */ +#ifdef HAVE_LIBZ + z_stream z_state; + int z_ret_code; + unsigned char *unzip_buffer; +#endif -static int quicktime_audio_tracks(quicktime_t *file) { - int i, result = 0; - quicktime_minf_t *minf; - for(i = 0; i < file->moov.total_tracks; i++) { - minf = &(file->moov.trak[i]->mdia.minf); - if(minf->is_audio) - result++; + /* reset the file */ + if (input->seek(input, 0, SEEK_SET) != 0) { + info->last_error = QT_FILE_READ_ERROR; + return info->last_error; } - return result; -} - -static int quicktime_has_audio(quicktime_t *file) { - if(quicktime_audio_tracks(file)) - return 1; - return 0; -} - -static long quicktime_sample_rate(quicktime_t *file, int track) { - if(file->total_atracks) - return file->atracks[track].track->mdia.minf.stbl.stsd.table[0].sample_rate; - return 0; -} -static int quicktime_audio_bits(quicktime_t *file, int track) { - if(file->total_atracks) - return file->atracks[track].track->mdia.minf.stbl.stsd.table[0].sample_size; - - return 0; -} - -static char* quicktime_audio_compressor(quicktime_t *file, int track) { - if (track < file->total_atracks) - return file->atracks[track].track->mdia.minf.stbl.stsd.table[0].format; - /* - * XXX: quick hack to avoid crashes when there's no audio. - * olympus 2040 digital camera records quicktime video without audio - */ - return "NONE"; -} + /* traverse through the file looking for the moov and mdat atoms */ + info->moov_first_offset = info->mdat_first_offset = -1; -static int quicktime_track_channels(quicktime_t *file, int track) { - if(track < file->total_atracks) - return file->atracks[track].channels; + while ((info->moov_first_offset == -1) || + (info->mdat_first_offset == -1)) { - return 0; -} - -static int quicktime_channel_location(quicktime_t *file, int *quicktime_track, - int *quicktime_channel, int channel) { - int current_channel = 0, current_track = 0; - *quicktime_channel = 0; - *quicktime_track = 0; - for(current_channel = 0, current_track = 0; current_track < file->total_atracks; ) { - if(channel >= current_channel) { - *quicktime_channel = channel - current_channel; - *quicktime_track = current_track; + if (input->read(input, atom_preamble, ATOM_PREAMBLE_SIZE) != + ATOM_PREAMBLE_SIZE) { + info->last_error = QT_FILE_READ_ERROR; + return info->last_error; } - current_channel += file->atracks[current_track].channels; - current_track++; - } - return 0; -} - -static int quicktime_video_tracks(quicktime_t *file) { - int i, result = 0; - for(i = 0; i < file->moov.total_tracks; i++) { - if(file->moov.trak[i]->mdia.minf.is_video) - result++; - } - return result; -} - - -static int quicktime_has_video(quicktime_t *file) { - if (quicktime_video_tracks(file)) - return 1; - return 0; -} - -static int quicktime_video_width(quicktime_t *file, int track) { - if(file->total_vtracks) - return file->vtracks[track].track->tkhd.track_width; - return 0; -} - -static int quicktime_video_height(quicktime_t *file, int track) { - if(file->total_vtracks) - return file->vtracks[track].track->tkhd.track_height; - return 0; -} - -static int quicktime_video_depth(quicktime_t *file, int track) { - if(file->total_vtracks) - return file->vtracks[track].track->mdia.minf.stbl.stsd.table[0].depth; - return 0; -} - - -static float quicktime_frame_rate(quicktime_t *file, int track) { - if(file->total_vtracks > track) - return (float)file->vtracks[track].track->mdia.mdhd.time_scale / - file->vtracks[track].track->mdia.minf.stbl.stts.table[0].sample_duration; - - return 0; -} - -static char* quicktime_video_compressor(quicktime_t *file, int track) { - return file->vtracks[track].track->mdia.minf.stbl.stsd.table[0].format; -} - - - -static int quicktime_init_video_map(quicktime_t *file, quicktime_video_map_t *vtrack, - quicktime_trak_t *trak) { - vtrack->track = trak; - vtrack->current_position = 0; - vtrack->current_chunk = 1; - return 0; -} - -static int quicktime_delete_video_map(quicktime_t *file, quicktime_video_map_t *vtrack) { - return 0; -} - -static int quicktime_init_audio_map(quicktime_t *file, quicktime_audio_map_t *atrack, quicktime_trak_t *trak) -{ - atrack->track = trak; - atrack->channels = trak->mdia.minf.stbl.stsd.table[0].channels; - atrack->current_position = 0; - atrack->current_chunk = 1; - return 0; -} - -static int quicktime_delete_audio_map(quicktime_t *file, quicktime_audio_map_t *atrack) { - return 0; -} - -static void quicktime_mdat_delete(quicktime_mdat_t *mdat) { -} - -static void quicktime_read_mdat(quicktime_t *file, quicktime_mdat_t *mdat, - quicktime_atom_t *parent_atom, xine_t *xine) { - mdat->atom.size = parent_atom->size; - mdat->atom.start = parent_atom->start; - quicktime_atom_skip(file, parent_atom); -} - -static int quicktime_read_info(quicktime_t *file, xine_t *xine) { - - int result = 0, found_moov = 0; - int i, track; - longest start_position = quicktime_position(file); - quicktime_atom_t leaf_atom; - int found_mdat=0; - - quicktime_set_position(file, 0/*LL*/); - - do { - result = quicktime_atom_read_header(file, &leaf_atom); - - /* - printf ("demux_qt: found atom, type >%c %c %c %c<\n", - leaf_atom.type[0],leaf_atom.type[1],leaf_atom.type[2],leaf_atom.type[3]); - */ - - if(!result) { - if(quicktime_atom_is(&leaf_atom, "mdat")) { - quicktime_read_mdat(file, &(file->mdat), &leaf_atom, xine); - found_mdat = 1; - } else if(quicktime_atom_is(&leaf_atom, "moov")) { - found_moov = quicktime_read_moov(file, &(file->moov), &leaf_atom, xine); - } else { - quicktime_atom_skip(file, &leaf_atom); + top_level_atom_size = BE_32(&atom_preamble[0]); + top_level_atom = BE_32(&atom_preamble[4]); + /* 64-bit length special case */ + if (top_level_atom_size == 1) { + preseek_pos = input->get_current_pos(input); + if (input->read(input, atom_preamble, 8) != 8) { + info->last_error = QT_FILE_READ_ERROR; + return info->last_error; + } + top_level_atom_size = BE_32(&atom_preamble[0]); + top_level_atom_size <<= 32; + top_level_atom_size = BE_32(&atom_preamble[4]); + + /* rewind 8 bytes */ + if (input->seek(input, preseek_pos, SEEK_SET) != preseek_pos) { + info->last_error = QT_FILE_READ_ERROR; + return info->last_error; } } - } while (!result && (found_mdat + found_moov != 2)); - - /* go back to the original position */ - quicktime_set_position(file, start_position); - - if (found_moov) { - /* get tables for all the different tracks */ - file->total_atracks = quicktime_audio_tracks(file); - file->atracks = (quicktime_audio_map_t*)calloc(1, sizeof(quicktime_audio_map_t) * file->total_atracks); - - for(i = 0, track = 0; i < file->total_atracks; i++) { - while(!file->moov.trak[track]->mdia.minf.is_audio) - track++; - quicktime_init_audio_map(file, &(file->atracks[i]), file->moov.trak[track]); - } - - file->total_vtracks = quicktime_video_tracks(file); - file->vtracks = (quicktime_video_map_t*)calloc(1, sizeof(quicktime_video_map_t) * file->total_vtracks); - - for(track = 0, i = 0; i < file->total_vtracks; i++) { - while(!file->moov.trak[track]->mdia.minf.is_video) - track++; - - quicktime_init_video_map(file, &(file->vtracks[i]), file->moov.trak[track]); - } - } - - return !found_moov; -} - -/* ============================= Initialization functions */ - -static int quicktime_init(quicktime_t *file) { - - memset(file, sizeof(quicktime_t), 0); - - file->quicktime_read_data = quicktime_read_data; - file->quicktime_fseek = quicktime_fseek; - - quicktime_moov_init(&(file->moov)); - return 0; -} - -static int quicktime_delete(quicktime_t *file) { - int i; - - if(file->total_atracks) { - for(i = 0; i < file->total_atracks; i++) - quicktime_delete_audio_map(file, &(file->atracks[i])); - free(file->atracks); - } - - if(file->total_vtracks) { - for(i = 0; i < file->total_vtracks; i++) - quicktime_delete_video_map(file, &(file->vtracks[i])); - free(file->vtracks); - } - - file->total_atracks = 0; - file->total_vtracks = 0; - - if(file->preload_size) { - free(file->preload_buffer); - file->preload_size = 0; - } - - quicktime_moov_delete(&(file->moov)); - - quicktime_mdat_delete(&(file->mdat)); - - return 0; -} - -/* ================================== Entry points ============================= */ - -static longest get_file_length( quicktime_t *file) { - - return file->input->get_length (file->input); - -} - - -static int quicktime_check_sig(input_plugin_t *input) { - - quicktime_t *file; - quicktime_atom_t leaf_atom; - int result1 = 0, result2 = 0; - - file = xine_xmalloc (sizeof (quicktime_t)); - - quicktime_init(file); - - file->input = input; - - input->seek (input, 0, SEEK_SET); - - file->total_length = get_file_length(file); + if (top_level_atom == MDAT_ATOM) { + info->mdat_first_offset = input->get_current_pos(input) - + ATOM_PREAMBLE_SIZE; + info->mdat_last_offset = + info->mdat_first_offset + top_level_atom_size; + + /* skip to the next atom */ + input->seek(input, top_level_atom_size - ATOM_PREAMBLE_SIZE, SEEK_CUR); + } else if (top_level_atom == MOOV_ATOM) { + info->moov_first_offset = input->get_current_pos(input) - + ATOM_PREAMBLE_SIZE; + info->moov_last_offset = + info->moov_first_offset + top_level_atom_size; + + moov_atom = (unsigned char *)malloc(top_level_atom_size); + if (moov_atom == NULL) { + info->last_error = QT_NO_MEMORY; + return info->last_error; + } + /* rewind to start of moov atom */ + if (input->seek(input, info->moov_first_offset, SEEK_SET) != + info->moov_first_offset) { + info->last_error = QT_FILE_READ_ERROR; + return info->last_error; + } + if (input->read(input, moov_atom, top_level_atom_size) != + top_level_atom_size) { + free(moov_atom); + info->last_error = QT_FILE_READ_ERROR; + return info->last_error; + } - do { - result1 = quicktime_atom_read_header(file, &leaf_atom); - - /* - printf ("demux_qt: found atom, type >%c %c %c %c<\n", - leaf_atom.type[0],leaf_atom.type[1],leaf_atom.type[2],leaf_atom.type[3]); - */ - - if(!result1) { - /* just want the "moov" atom */ - if(quicktime_atom_is(&leaf_atom, "moov")) { - result2 = 1; - } else - quicktime_atom_skip(file, &leaf_atom); + /* check if moov is compressed */ + if (BE_32(&moov_atom[12]) == CMOV_ATOM) { + + info->compressed_header = 1; + +#ifndef HAVE_LIBZ + info->last_error = QT_NO_ZLIB; + return info->last_error; +#else + if (BE_32(&moov_atom[12]) == CMOV_ATOM) { + z_state.next_in = &moov_atom[0x28]; + z_state.avail_in = top_level_atom_size - 0x28; + z_state.avail_out = BE_32(&moov_atom[0x24]); + unzip_buffer = (unsigned char *)malloc(BE_32(&moov_atom[0x24])); + if (!unzip_buffer) { + info->last_error = QT_NO_MEMORY; + return info->last_error; + } + } else { + z_state.next_in = &moov_atom[ATOM_PREAMBLE_SIZE]; + z_state.avail_in = top_level_atom_size - ATOM_PREAMBLE_SIZE; + z_state.avail_out = 65536; + unzip_buffer = (unsigned char *)malloc(65536); + if (!unzip_buffer) { + info->last_error = QT_NO_MEMORY; + return info->last_error; + } + } + + z_state.next_out = unzip_buffer; + z_state.zalloc = (alloc_func)0; + z_state.zfree = (free_func)0; + z_state.opaque = (voidpf)0; + + z_ret_code = inflateInit (&z_state); + if (Z_OK != z_ret_code) { + info->last_error = QT_ZLIB_ERROR; + return info->last_error; + } + + z_ret_code = inflate(&z_state, Z_NO_FLUSH); + if ((z_ret_code != Z_OK) && (z_ret_code != Z_STREAM_END)) { + info->last_error = QT_ZLIB_ERROR; + return info->last_error; + } + + z_ret_code = inflateEnd(&z_state); + if (Z_OK != z_ret_code) { + info->last_error = QT_ZLIB_ERROR; + return info->last_error; + } + + /* replace the compressed moov atom with the decompressed atom */ + free (moov_atom); + moov_atom = unzip_buffer; + top_level_atom_size = BE_32 (&moov_atom[0]); +#endif + } + } else { + input->seek(input, top_level_atom_size - ATOM_PREAMBLE_SIZE, SEEK_CUR); } - }while(!result1 && !result2 && quicktime_position(file) < file->total_length); - - quicktime_delete(file); - - free(file); - - return result2; -} - -static void quicktime_close(quicktime_t *file) -{ - quicktime_delete(file); - free(file); -} - -static quicktime_t* quicktime_open(input_plugin_t *input, xine_t *xine) { - quicktime_t *new_file = calloc(1, sizeof(quicktime_t)); - - quicktime_init(new_file); - new_file->mdat.atom.start = 0; - - new_file->decompressed_buffer_size = 0; - new_file->decompressed_buffer = NULL; - new_file->decompressed_position = 0; - - new_file->input = input; - - new_file->quicktime_read_data = quicktime_read_data; - new_file->quicktime_fseek = quicktime_fseek; + } - input->seek (input, 0, SEEK_SET); + /* take apart the moov atom */ + if ((info->moov_first_offset != -1) && + (info->mdat_first_offset != -1)) + parse_moov_atom(info, moov_atom); - /* Get length. */ - new_file->total_length = get_file_length(new_file); - - if(quicktime_read_info(new_file, xine)) { - quicktime_close(new_file); - printf ("demux_qt: quicktime_open: error in header\n"); - new_file = 0; + if (!moov_atom) { + info->last_error = QT_NO_MOOV_ATOM; + return info->last_error; } - return new_file; + return QT_OK; } -/* - * now for the xine-specific demuxer stuff - */ - -static off_t demux_qt_get_sample_size (quicktime_trak_t *trak, int sample_num) { - - quicktime_stsz_t *stsz; - - stsz = &trak->mdia.minf.stbl.stsz; - - if (stsz->sample_size) - return stsz->sample_size; - - return stsz->table[sample_num].size; -} +/********************************************************************** + * xine demuxer functions + **********************************************************************/ static void *demux_qt_loop (void *this_gen) { + demux_qt_t *this = (demux_qt_t *) this_gen; buf_element_t *buf = NULL; - demux_qt_t *this = (demux_qt_t *) this_gen; - int idx; - off_t offset, size, todo; - fifo_buffer_t *fifo; - int64_t pts; - uint32_t flags; - int is_audio; - int sample_num; - -#ifdef LOG - printf ("demux_qt: demux loop starting...\n"); -#endif - - idx = 0; + int64_t last_frame_pts = 0; + unsigned int i; + unsigned int remaining_sample_bytes; pthread_mutex_lock( &this->mutex ); + /* do-while needed to seek after demux finished */ do { - /* main demuxer loop */ - while(this->status == DEMUX_OK) { - - if (idx >= this->num_index_entries) { - this->status = DEMUX_FINISHED; - break; - } - - is_audio = (this->index[idx].type & BUF_MAJOR_MASK) == BUF_AUDIO_BASE; - - if (is_audio) - fifo = this->audio_fifo; - else - fifo = this->video_fifo; - - offset = this->index[idx].offset; - pts = this->index[idx].pts; - - check_newpts( this, pts ); - - for (sample_num = this->index[idx].first_sample; sample_num <= this->index[idx].last_sample; sample_num++) { - - todo = demux_qt_get_sample_size (this->index[idx].track, sample_num); - -#ifdef LOG - printf ("demux_qt: [idx:%04d type:%08x len:%08lld ] ---------------------------\n", - idx, this->index[idx].type, todo); -#endif - - flags = BUF_FLAG_FRAME_START; - while (todo) { - - buf = fifo->buffer_pool_alloc (fifo); - - if (todo>buf->max_size) - size = buf->max_size; - else - size = todo; - todo -= size; - - quicktime_set_position (this->qt, offset); - - buf->size = this->qt->quicktime_read_data (this->qt, buf->mem, size); -#ifdef LOG - printf ("demux_qt: generated buffer of %d bytes \n", buf->size); -#endif - - buf->content = buf->mem; - -#ifdef DBG_QT - if (is_audio) - write (debug_fh, buf->mem, buf->size); -#endif - - buf->type = this->index[idx].type; - buf->pts = pts; - buf->input_pos = this->qt->file_position; - if (todo) - buf->decoder_flags = flags; - else - buf->decoder_flags = flags | BUF_FLAG_FRAME_END; - + while (this->status == DEMUX_OK) { + + i = this->current_frame; + + /* 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) { + xine_flush_engine(this->xine); + + /* send new pts */ + buf = this->video_fifo->buffer_pool_alloc (this->video_fifo); + buf->type = BUF_CONTROL_NEWPTS; + buf->disc_off = this->qt->frames[i].pts; + this->video_fifo->put (this->video_fifo, buf); + + if (this->audio_fifo) { + buf = this->audio_fifo->buffer_pool_alloc (this->audio_fifo); + buf->type = BUF_CONTROL_NEWPTS; + buf->disc_off = this->qt->frames[i].pts; + this->audio_fifo->put (this->audio_fifo, buf); + } + + /* set last_frame_pts to some sane value on seek */ + last_frame_pts = this->qt->frames[i].pts - 3000; + } - fifo->put (fifo, buf); + /* check if all the samples have been sent */ + if (i >= this->qt->frame_count) { + this->status = DEMUX_FINISHED; + break; + } - pts = 0; - flags = 0; - offset += size; + 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, + SEEK_SET); + + while (remaining_sample_bytes) { + buf = this->video_fifo->buffer_pool_alloc (this->video_fifo); + buf->content = buf->mem; + buf->type = this->qt->video_type; + buf->input_pos = this->qt->frames[i].offset; + buf->pts = this->qt->frames[i].pts; + buf->decoder_flags = 0; + + if (last_frame_pts) { + buf->decoder_flags |= BUF_FLAG_FRAMERATE; + buf->decoder_info[0] = buf->pts - last_frame_pts; + } + + if (remaining_sample_bytes > buf->max_size) + buf->size = buf->max_size; + else + buf->size = remaining_sample_bytes; + remaining_sample_bytes -= buf->size; + + if (this->input->read(this->input, buf->content, buf->size) != + buf->size) { + this->status = DEMUX_FINISHED; + break; + } + + if (this->qt->frames[i].keyframe) + buf->decoder_flags |= BUF_FLAG_KEYFRAME; + if (!remaining_sample_bytes) + buf->decoder_flags |= BUF_FLAG_FRAME_END; + + this->video_fifo->put(this->video_fifo, buf); + } + last_frame_pts = buf->pts; + } else if ((this->qt->frames[i].type == MEDIA_AUDIO) && + this->audio_fifo) { + /* 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, + SEEK_SET); + + while (remaining_sample_bytes) { + buf = this->audio_fifo->buffer_pool_alloc (this->audio_fifo); + buf->content = buf->mem; + buf->type = this->qt->audio_type; + buf->input_pos = this->qt->frames[i].offset; + buf->pts = this->qt->frames[i].pts; + buf->decoder_flags = 0; + + if (remaining_sample_bytes > buf->max_size) + buf->size = buf->max_size; + else + buf->size = remaining_sample_bytes; + remaining_sample_bytes -= buf->size; + + if (this->input->read(this->input, buf->content, buf->size) != + buf->size) { + this->status = DEMUX_FINISHED; + break; + } + + if (!remaining_sample_bytes) + buf->decoder_flags |= BUF_FLAG_FRAME_END; + this->audio_fifo->put(this->audio_fifo, buf); + } } - } - idx++; + this->last_frame = this->current_frame; + this->current_frame++; /* someone may want to interrupt us */ pthread_mutex_unlock( &this->mutex ); pthread_mutex_lock( &this->mutex ); + } /* wait before sending end buffers: user might want to do a new seek */ @@ -3874,10 +1170,10 @@ static void *demux_qt_loop (void *this_gen) { pthread_mutex_lock( &this->mutex ); } - } while( this->status == DEMUX_OK ); + } while (this->status == DEMUX_OK); printf ("demux_qt: demux loop finished (status: %d)\n", - this->status); + this->status); this->status = DEMUX_FINISHED; @@ -3893,528 +1189,276 @@ static void *demux_qt_loop (void *this_gen) { buf->decoder_flags = BUF_FLAG_END_STREAM; /* stream finished */ this->audio_fifo->put (this->audio_fifo, buf); } - } this->thread_running = 0; - pthread_mutex_unlock( &this->mutex ); - - pthread_exit(NULL); - + pthread_mutex_unlock(&this->mutex); return NULL; } -static void demux_qt_close (demux_plugin_t *this_gen) { - - demux_qt_t *this = (demux_qt_t *) this_gen; - - if (this->qt) - quicktime_close (this->qt); - - pthread_mutex_destroy (&this->mutex); - free (this); - -#ifdef DBG_QT - close (debug_fh); -#endif - -} - -static void demux_qt_stop (demux_plugin_t *this_gen) { - - demux_qt_t *this = (demux_qt_t *) this_gen; - buf_element_t *buf; - void *p; - - pthread_mutex_lock( &this->mutex ); - - if (!this->thread_running) { - printf ("demux_qt: stop...ignored\n"); - pthread_mutex_unlock( &this->mutex ); - return; - } - - this->send_end_buffers = 0; - this->status = DEMUX_FINISHED; - - pthread_mutex_unlock( &this->mutex ); - pthread_join (this->thread, &p); - - xine_flush_engine(this->xine); - - buf = this->video_fifo->buffer_pool_alloc (this->video_fifo); - buf->type = BUF_CONTROL_END; - buf->decoder_flags = BUF_FLAG_END_USER; /* user finished */ - this->video_fifo->put (this->video_fifo, buf); - - if(this->audio_fifo) { - buf = this->audio_fifo->buffer_pool_alloc (this->audio_fifo); - buf->type = BUF_CONTROL_END; - buf->decoder_flags = BUF_FLAG_END_USER; /* user finished */ - this->audio_fifo->put (this->audio_fifo, buf); - } -} +static int demux_qt_open(demux_plugin_t *this_gen, + input_plugin_t *input, int stage) { -static int demux_qt_get_status (demux_plugin_t *this_gen) { demux_qt_t *this = (demux_qt_t *) this_gen; - return (this->thread_running?DEMUX_OK:DEMUX_FINISHED); -} - -static int demux_qt_detect_compressors (demux_qt_t *this) { - - char *video, *audio; - - video = quicktime_video_compressor (this->qt, 0); - - this->bih.biSize=sizeof(this->bih); - this->bih.biWidth = quicktime_video_width (this->qt, 0); - this->bih.biHeight= quicktime_video_height (this->qt, 0); - this->bih.biPlanes= 0; - memcpy( &this->bih.biCompression, video, 4); - this->bih.biBitCount= quicktime_video_depth(this->qt, 0); - this->bih.biSizeImage=this->bih.biWidth*this->bih.biHeight; - this->bih.biXPelsPerMeter=1; - this->bih.biYPelsPerMeter=1; - this->bih.biClrUsed=0; - this->bih.biClrImportant=0; - - this->video_step = 90000.0 / quicktime_frame_rate (this->qt, 0); - - this->video_type = fourcc_to_buf_video( video ); - - /* FIXME: do we really need to set biCompression again here? */ - if (this->video_type == BUF_VIDEO_CINEPAK) { - this->bih.biCompression=mmioFOURCC('c', 'v', 'i', 'd'); - } - - if (!this->video_type) { - xine_log (this->xine, XINE_LOG_FORMAT, - _("demux_qt: unknown video codec '%s'\n"), video); - return 0; - } - - printf ("demux_qt: video codec is '%s'\n", video); - - this->wavex.nChannels = quicktime_track_channels (this->qt, 0); - this->wavex.nSamplesPerSec = quicktime_sample_rate (this->qt, 0); - this->wavex.nAvgBytesPerSec = 0; /* FIXME */ - this->wavex.nBlockAlign = 16; - this->wavex.wBitsPerSample = quicktime_audio_bits (this->qt, 0); - this->wavex.cbSize = sizeof (this->wavex); /* FIXME */ - - this->audio_factor = 90000.0 / ((double) quicktime_sample_rate (this->qt, 0)) ; - - this->has_audio = quicktime_has_audio (this->qt); - - audio = quicktime_audio_compressor (this->qt, 0); + this->input = input; - printf ("demux_qt: audio codec is '%s'\n", audio); - - if (!strncasecmp (audio, "raw ", 4)) { - this->audio_type = BUF_AUDIO_LPCM_LE; - this->wavex.wFormatTag = WAVE_FORMAT_ADPCM; - } else if (!strncasecmp (audio, ".mp3", 4)) { - this->audio_type = BUF_AUDIO_MPEG; - } else if (!strncasecmp (audio, "mp4a", 4)) { - this->audio_type = BUF_AUDIO_AAC; - } else { - printf ("demux_qt: unknown audio codec >%s<\n", - audio); - this->audio_type = BUF_CONTROL_NOP; - } - - return 1; -} - -static void demux_qt_add_index_entry (demux_qt_t *this, off_t offset, - int first_sample, int last_sample, - int64_t pts, int32_t type, - quicktime_trak_t *track) { - - int i,j; - - /* - * insertion sort - */ + switch(stage) { + case STAGE_BY_CONTENT: { + if ((input->get_capabilities(input) & INPUT_CAP_SEEKABLE) == 0) + return DEMUX_CANNOT_HANDLE; - for (i=0; i<this->num_index_entries; i++) { - if (this->index[i].pts >= pts) - break; + return is_qt_file(input); } + break; - for (j=this->num_index_entries; j>i; j--) - this->index[j] = this->index[j-1]; - - this->index[i].pts = pts; - this->index[i].offset = offset; - this->index[i].first_sample = first_sample; - this->index[i].last_sample = last_sample; - this->index[i].type = type; - this->index[i].track = track; - - this->num_index_entries++; -} - -static void demux_qt_index_trak (demux_qt_t *this, quicktime_trak_t *trak, uint32_t type) { - - quicktime_stsz_t *stsz; - quicktime_stsc_t *stsc; - quicktime_stco_t *stco; - quicktime_stts_t *stts; - long time_scale; - int stsc_entry, stsc_first, stsc_cur, stsc_next; - int stsc_samples, stsc_last_sample, stsc_first_sample; - int stts_entry, stts_first_sample, stts_last_sample; - int64_t stts_duration, stts_pts, pts; - off_t chunk_offset; - - stts = &trak->mdia.minf.stbl.stts; - stsz = &trak->mdia.minf.stbl.stsz; - stsc = &trak->mdia.minf.stbl.stsc; - stco = &trak->mdia.minf.stbl.stco; - time_scale = trak->mdia.mdhd.time_scale; - pts = 0; - - /* - * generate one entry per chunk - */ - - /* chunk-tracking */ - - stsc_entry = 0; - stsc_first = stsc->table[stsc_entry].chunk; - stsc_cur = stsc_first; - - if (stsc->total_entries>(stsc_entry+1)) - stsc_next = stsc->table[stsc_entry+1].chunk; - else - stsc_next = 1000000; - - stsc_samples = stsc->table[stsc_entry].samples; - stsc_last_sample = stsc_samples-1; - stsc_first_sample = 0; - - /* time-to-sample tracking */ - - stts_entry = 0; - stts_first_sample = 0; - stts_last_sample = stts->table[stts_entry].sample_count-1; - stts_duration = stts->table[stts_entry].sample_duration * 90000 / time_scale; - stts_pts = 0; - - while (stsc_cur < stco->total_entries) { -#ifdef LOG - printf ("demux_qt: chunk # is %d...\n", stsc_cur); -#endif - - chunk_offset = stco->table[stsc_cur-1].offset; - - /* - * add index entry - */ - -#ifdef LOG - printf ("demux_qt: index entry, sample_num=%d, offset = %lld, sample %d-%d, pts = %lld\n", - stsc_first_sample, chunk_offset, stsc_first_sample, stsc_last_sample, pts); -#endif - - demux_qt_add_index_entry (this, chunk_offset, stsc_first_sample, stsc_last_sample, pts, type, - trak); - - /* - * next chunk - */ - - stsc_cur ++; - - /* - * offset of chunk / sample - */ - - while (stsc_cur >= stsc_next) { - - stsc_entry++; - - stsc_first = stsc->table[stsc_entry].chunk; - - if (stsc->total_entries>(stsc_entry+1)) - stsc_next = stsc->table[stsc_entry+1].chunk; - else - stsc_next = 1000000; + case STAGE_BY_EXTENSION: { + char *suffix; + char *MRL; + char *m, *valid_ends; - stsc_samples = stsc->table[stsc_entry].samples; - } + MRL = input->get_mrl (input); - stsc_first_sample = stsc_last_sample + 1; - stsc_last_sample = stsc_first_sample + stsc_samples - 1; + suffix = strrchr(MRL, '.'); -#ifdef LOG - printf ("demux_qt: chunk offset is %lld...\n", chunk_offset); -#endif + if(!suffix) + return DEMUX_CANNOT_HANDLE; - /* - * find out about pts of sample - */ + xine_strdupa(valid_ends, (this->config->register_string(this->config, + "mrl.ends_qt", VALID_ENDS, + "valid mrls ending for qt demuxer", + NULL, NULL, NULL))); + while((m = xine_strsep(&valid_ends, ",")) != NULL) { - while (stsc_first_sample > stts_last_sample) { + while(*m == ' ' || *m == '\t') m++; - stts_pts += stts_duration * stts->table[stts_entry].sample_count; - stts_entry++; - stts_first_sample = stts_last_sample+1; - stts_last_sample += stts->table[stts_entry].sample_count; - stts_duration = stts->table[stts_entry].sample_duration * 90000 / time_scale; + if(!strcasecmp((suffix + 1), m)) { + this->input = input; + return DEMUX_CAN_HANDLE; + } } - - pts = stts_pts + (stsc_first_sample - stts_first_sample) * stts_duration; - + return DEMUX_CANNOT_HANDLE; } + break; -} - -static void demux_qt_create_index (demux_qt_t *this) { - - int track_num; - quicktime_trak_t *trak; - - this->num_index_entries = 0; - - /* video */ - - printf ("demux_qt: generating index, video entries...\n"); - - for (track_num = 0; track_num<this->qt->total_vtracks; track_num++) { - - trak = this->qt->vtracks[track_num].track; + default: + return DEMUX_CANNOT_HANDLE; + break; - demux_qt_index_trak (this, trak, this->video_type | track_num); } - /* audio */ - - printf ("demux_qt: generating index, audio entries...\n"); - - for (track_num = 0; track_num<this->qt->total_atracks; track_num++) { - - trak = this->qt->atracks[track_num].track; - - demux_qt_index_trak (this, trak, this->audio_type | track_num); - } - printf ("demux_qt: index generation done.\n"); + return DEMUX_CANNOT_HANDLE; } static int demux_qt_start (demux_plugin_t *this_gen, - fifo_buffer_t *video_fifo, - fifo_buffer_t *audio_fifo, - off_t start_pos, int start_time) { - + fifo_buffer_t *video_fifo, + fifo_buffer_t *audio_fifo, + off_t start_pos, int start_time) { demux_qt_t *this = (demux_qt_t *) this_gen; buf_element_t *buf; int err; - int status; - - pthread_mutex_lock( &this->mutex ); - - if( !this->thread_running ) { - this->video_fifo = video_fifo; - this->audio_fifo = audio_fifo; - -#ifdef DBG_QT - debug_fh = open ("/tmp/t.mp3", O_CREAT | O_WRONLY | O_TRUNC, 0644); -#endif + unsigned int le_fourcc; - /* - * init quicktime parser - */ + pthread_mutex_lock(&this->mutex); - this->qt = quicktime_open (this->input, this->xine); + /* if thread is not running, initialize demuxer */ + if (!this->thread_running) { + this->video_fifo = video_fifo; + this->audio_fifo = audio_fifo; - if (!this->qt) { - this->status = DEMUX_FINISHED; - status = this->status; - pthread_mutex_unlock( &this->mutex ); - return status; + /* create the QT structure */ + if ((this->qt = create_qt_info()) == NULL) { + pthread_mutex_unlock(&this->mutex); + return DEMUX_FINISHED; } - if (!demux_qt_detect_compressors (this)) { - this->status = DEMUX_FINISHED; - status = this->status; - pthread_mutex_unlock( &this->mutex ); - return status; + /* open the QT file */ + if (open_qt_file(this->qt, this->input) != QT_OK) { + pthread_mutex_unlock(&this->mutex); + return DEMUX_FINISHED; } + this->bih.biWidth = this->qt->video_width; + this->bih.biHeight = this->qt->video_height; + + /* fourcc was stored in opposite byte order that mapping routine wants */ + le_fourcc = + ((this->qt->video_codec & 0xFF000000) >> 24) | + ((this->qt->video_codec & 0x00FF0000) >> 8) | + ((this->qt->video_codec & 0x0000FF00) << 8) | + ((this->qt->video_codec & 0x000000FF) << 24); + this->qt->video_type = fourcc_to_buf_video(&le_fourcc); + + /* fourcc was stored in opposite byte order that mapping routine wants */ + le_fourcc = + ((this->qt->audio_codec & 0xFF000000) >> 24) | + ((this->qt->audio_codec & 0x00FF0000) >> 8) | + ((this->qt->audio_codec & 0x0000FF00) << 8) | + ((this->qt->audio_codec & 0x000000FF) << 24); + this->qt->audio_type = formattag_to_buf_audio(le_fourcc); + + /* print vital stats */ xine_log (this->xine, XINE_LOG_FORMAT, - _("demux_qt: video codec %s (%f fps), audio codec %s (%ld Hz, %d bits)\n"), - quicktime_video_compressor (this->qt,0), - quicktime_frame_rate (this->qt,0), - quicktime_audio_compressor (this->qt,0), - quicktime_sample_rate (this->qt,0), - quicktime_audio_bits (this->qt,0)); - - /* - * generate index - */ - - demux_qt_create_index (this); - - /* - * send start buffer - */ - - buf = this->video_fifo->buffer_pool_alloc (this->video_fifo); - buf->type = BUF_CONTROL_START; - this->video_fifo->put (this->video_fifo, buf); + _("demux_qt: Apple Quicktime file, running time: %d min, %d sec\n"), + this->qt->duration / this->qt->time_scale / 60, + this->qt->duration / this->qt->time_scale % 60); + if (this->qt->video_codec) + xine_log (this->xine, XINE_LOG_FORMAT, + _("demux_qt: '%c%c%c%c' video @ %dx%d, %d Hz playback clock\n"), + (this->qt->video_codec >> 24) & 0xFF, + (this->qt->video_codec >> 16) & 0xFF, + (this->qt->video_codec >> 8) & 0xFF, + (this->qt->video_codec >> 0) & 0xFF, + this->bih.biWidth, + this->bih.biHeight, + this->qt->time_scale); + if (this->qt->audio_codec) + xine_log (this->xine, XINE_LOG_FORMAT, + _("demux_qt: '%c%c%c%c' audio @ %d Hz, %d bits, %d channel%c\n"), + (this->qt->audio_codec >> 24) & 0xFF, + (this->qt->audio_codec >> 16) & 0xFF, + (this->qt->audio_codec >> 8) & 0xFF, + (this->qt->audio_codec >> 0) & 0xFF, + this->qt->audio_sample_rate, + this->qt->audio_bits, + this->qt->audio_channels, + (this->qt->audio_channels == 1) ? 0 : 's'); + + /* send start buffers */ + buf = this->video_fifo->buffer_pool_alloc(this->video_fifo); + buf->type = BUF_CONTROL_START; + this->video_fifo->put(this->video_fifo, buf); - if(this->audio_fifo) { - buf = this->audio_fifo->buffer_pool_alloc (this->audio_fifo); - buf->type = BUF_CONTROL_START; - this->audio_fifo->put (this->audio_fifo, buf); + if (this->audio_fifo) { + buf = this->audio_fifo->buffer_pool_alloc(this->audio_fifo); + buf->type = BUF_CONTROL_START; + this->audio_fifo->put(this->audio_fifo, buf); } - } - - /* - * seek to start pos/time - */ - if (start_pos) { - - double f = (double) start_pos / (double) this->input->get_length (this->input) ; - - - quicktime_set_audio_position (this->qt, - quicktime_audio_length (this->qt, 0) * f, 0); - - quicktime_set_video_position (this->qt, - quicktime_video_length (this->qt, 0) * f, 0); - - } - this->send_newpts = 1; - - if( !this->thread_running ) { - /* - * send init info to decoders - */ + /* send init info to decoders */ buf = this->video_fifo->buffer_pool_alloc (this->video_fifo); buf->content = buf->mem; - buf->decoder_flags = BUF_FLAG_HEADER; - buf->decoder_info[0] = 0; /* first package, containing bih */ - buf->decoder_info[1] = this->video_step; - memcpy (buf->content, &this->bih, sizeof (this->bih)); - buf->size = sizeof (this->bih); - - buf->type = this->video_type; - + 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); - if(this->audio_fifo) { + if (this->audio_fifo) { buf = this->audio_fifo->buffer_pool_alloc (this->audio_fifo); buf->content = buf->mem; - memcpy (buf->content, &this->wavex, - sizeof (this->wavex)); - buf->size = sizeof (this->wavex); - buf->type = this->audio_type; - buf->decoder_flags = BUF_FLAG_HEADER; - buf->decoder_info[0] = 0; /* first package, containing wavex */ - buf->decoder_info[1] = quicktime_sample_rate (this->qt, 0); - buf->decoder_info[2] = quicktime_audio_bits (this->qt, 0); - buf->decoder_info[3] = quicktime_track_channels (this->qt, 0); + buf->type = this->qt->audio_type; + 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; this->audio_fifo->put (this->audio_fifo, buf); } - /* - * now start demuxing - */ - - this->status = DEMUX_OK ; + this->status = DEMUX_OK; this->send_end_buffers = 1; this->thread_running = 1; + this->current_frame = 0; + this->last_frame = 0; + if ((err = pthread_create (&this->thread, NULL, demux_qt_loop, this)) != 0) { printf ("demux_qt: can't create new thread (%s)\n", strerror(err)); abort(); } } - else { - xine_flush_engine(this->xine); - } - /* this->status is saved because we can be interrupted between - * pthread_mutex_unlock and return - */ - status = this->status; - pthread_mutex_unlock( &this->mutex ); - return status; + pthread_mutex_unlock(&this->mutex); +return 0; } + static int demux_qt_seek (demux_plugin_t *this_gen, - off_t start_pos, int start_time) { + off_t start_pos, int start_time) { demux_qt_t *this = (demux_qt_t *) this_gen; - return demux_qt_start (this_gen, this->video_fifo, this->audio_fifo, - start_pos, start_time); -} + int i; + int best_index; + pthread_mutex_lock(&this->mutex); -static int demux_qt_open(demux_plugin_t *this_gen, - input_plugin_t *input, int stage) { + if (!this->thread_running) { + pthread_mutex_unlock( &this->mutex ); + return this->status; + } - demux_qt_t *this = (demux_qt_t *) this_gen; + /* perform a linear search through the table to get the closest offset */ + best_index = this->qt->frame_count - 1; + for (i = 0; i < this->qt->frame_count; i++) { + if (this->qt->frames[i].offset > start_pos) { + best_index = i; + break; + } + } - switch(stage) { + /* search back in the table for the nearest keyframe */ + while (best_index--) { + if (this->qt->frames[best_index].keyframe) { + this->current_frame = best_index; + break; + } + } - case STAGE_BY_CONTENT: { + this->status = DEMUX_OK; + pthread_mutex_unlock( &this->mutex ); - if ((input->get_capabilities(input) & INPUT_CAP_SEEKABLE) == 0) - return DEMUX_CANNOT_HANDLE; - if ((input->get_capabilities(input) & INPUT_CAP_BLOCK) != 0) - return DEMUX_CANNOT_HANDLE; + return this->status; +} - if (quicktime_check_sig (input)) { - this->input = input; - return DEMUX_CAN_HANDLE; - } +static void demux_qt_stop (demux_plugin_t *this_gen) { - return DEMUX_CANNOT_HANDLE; + demux_qt_t *this = (demux_qt_t *) this_gen; + buf_element_t *buf; + void *p; - } - break; + pthread_mutex_lock( &this->mutex ); - case STAGE_BY_EXTENSION: { - char *suffix; - char *MRL; - char *m, *valid_ends; + if (!this->thread_running) { + pthread_mutex_unlock( &this->mutex ); + return; + } - MRL = input->get_mrl (input); + this->send_end_buffers = 0; + this->status = DEMUX_FINISHED; - suffix = strrchr(MRL, '.'); + pthread_mutex_unlock( &this->mutex ); + pthread_join (this->thread, &p); - if(!suffix) - return DEMUX_CANNOT_HANDLE; + xine_flush_engine(this->xine); - xine_strdupa(valid_ends, (this->config->register_string(this->config, - "mrl.ends_qt", VALID_ENDS, - "valid mrls ending for qt demuxer", - NULL, NULL, NULL))); - while((m = xine_strsep(&valid_ends, ",")) != NULL) { + buf = this->video_fifo->buffer_pool_alloc (this->video_fifo); + buf->type = BUF_CONTROL_END; + buf->decoder_flags = BUF_FLAG_END_USER; /* user finished */ + this->video_fifo->put (this->video_fifo, buf); - while(*m == ' ' || *m == '\t') m++; + if(this->audio_fifo) { + buf = this->audio_fifo->buffer_pool_alloc (this->audio_fifo); + buf->type = BUF_CONTROL_END; + buf->decoder_flags = BUF_FLAG_END_USER; /* user finished */ + this->audio_fifo->put (this->audio_fifo, buf); + } +} - if(!strcasecmp((suffix + 1), m)) { - this->input = input; - return DEMUX_CAN_HANDLE; - } - } - return DEMUX_CANNOT_HANDLE; +static void demux_qt_close (demux_plugin_t *this_gen) { + demux_qt_t *this = (demux_qt_t *) this_gen; - } - break; + free_qt_info(this->qt); + free(this); + pthread_mutex_destroy (&this->mutex); +} - default: - return DEMUX_CANNOT_HANDLE; - break; - } +static int demux_qt_get_status (demux_plugin_t *this_gen) { + demux_qt_t *this = (demux_qt_t *) this_gen; - return DEMUX_CANNOT_HANDLE; + return this->status; } static char *demux_qt_get_id(void) { @@ -4426,11 +1470,12 @@ static char *demux_qt_get_mimetypes(void) { "video/x-quicktime: mov,qt: Quicktime animation;"; } +/* return the approximate length in seconds */ static int demux_qt_get_stream_length (demux_plugin_t *this_gen) { demux_qt_t *this = (demux_qt_t *) this_gen; - return this->video_step * quicktime_video_length (this->qt, 0); + return this->qt->duration; } demux_plugin_t *init_demuxer_plugin(int iface, xine_t *xine) { @@ -4439,9 +1484,10 @@ demux_plugin_t *init_demuxer_plugin(int iface, xine_t *xine) { if (iface != 8) { printf ("demux_qt: plugin doesn't support plugin API version %d.\n" - " this means there's a version mismatch between xine and this " - " demuxer plugin.\nInstalling current demux plugins should help.\n", - iface); + " this means there's a version mismatch between xine and this " + " demuxer plugin.\nInstalling current demux plugins should +help.\n", + iface); return NULL; } @@ -4450,9 +1496,9 @@ demux_plugin_t *init_demuxer_plugin(int iface, xine_t *xine) { this->xine = xine; (void*) this->config->register_string(this->config, - "mrl.ends_qt", VALID_ENDS, - "valid mrls ending for qt demuxer", - NULL, NULL, NULL); + "mrl.ends_qt", VALID_ENDS, + "valid mrls ending for qt demuxer", + NULL, NULL, NULL); this->demux_plugin.interface_version = DEMUXER_PLUGIN_IFACE_VERSION; this->demux_plugin.open = demux_qt_open; @@ -4464,9 +1510,9 @@ demux_plugin_t *init_demuxer_plugin(int iface, xine_t *xine) { this->demux_plugin.get_identifier = demux_qt_get_id; this->demux_plugin.get_stream_length = demux_qt_get_stream_length; this->demux_plugin.get_mimetypes = demux_qt_get_mimetypes; - + this->status = DEMUX_FINISHED; pthread_mutex_init( &this->mutex, NULL ); - + return (demux_plugin_t *) this; } |