diff options
author | Guenter Bartsch <guenter@users.sourceforge.net> | 2001-09-10 03:04:48 +0000 |
---|---|---|
committer | Guenter Bartsch <guenter@users.sourceforge.net> | 2001-09-10 03:04:48 +0000 |
commit | 6fdc5685796399e9ddea30b7dcffc607bd4c10b3 (patch) | |
tree | 4b180a7aa74abbc6b6d8be29520fa27ef0cb848c | |
parent | 148419cfcd693508fc7a23158b393070a32f97d0 (diff) | |
download | xine-lib-6fdc5685796399e9ddea30b7dcffc607bd4c10b3.tar.gz xine-lib-6fdc5685796399e9ddea30b7dcffc607bd4c10b3.tar.bz2 |
a quicktime demuxer based on openquicktime, cinepack support fixed, minor bugfixes regarding buffer type handling
CVS patchset: 600
CVS date: 2001/09/10 03:04:48
-rw-r--r-- | src/demuxers/Makefile.am | 5 | ||||
-rw-r--r-- | src/demuxers/demux_avi.c | 4 | ||||
-rw-r--r-- | src/demuxers/demux_elem.c | 5 | ||||
-rw-r--r-- | src/demuxers/demux_qt.c | 4354 | ||||
-rw-r--r-- | src/liblpcm/xine_decoder.c | 16 | ||||
-rw-r--r-- | src/libw32dll/w32codec.c | 9 | ||||
-rw-r--r-- | src/xine-engine/audio_decoder.c | 18 | ||||
-rw-r--r-- | src/xine-engine/audio_out.c | 10 | ||||
-rw-r--r-- | src/xine-engine/audio_out.h | 3 | ||||
-rw-r--r-- | src/xine-engine/buffer.h | 9 | ||||
-rw-r--r-- | src/xine-engine/xine.c | 6 |
11 files changed, 4406 insertions, 33 deletions
diff --git a/src/demuxers/Makefile.am b/src/demuxers/Makefile.am index faca83356..78bdb99e6 100644 --- a/src/demuxers/Makefile.am +++ b/src/demuxers/Makefile.am @@ -12,7 +12,7 @@ libdir = $(XINE_PLUGINDIR) lib_LTLIBRARIES = xineplug_dmx_avi.la xineplug_dmx_mpeg_block.la \ xineplug_dmx_mpeg.la xineplug_dmx_mpeg_elem.la \ xineplug_dmx_mpeg_audio.la xineplug_dmx_mpeg_pes.la \ - xineplug_dmx_mpeg_ts.la + xineplug_dmx_mpeg_ts.la xineplug_dmx_qt.la xineplug_dmx_avi_la_SOURCES = demux_avi.c xineplug_dmx_avi_la_LDFLAGS = -avoid-version -module @@ -36,6 +36,9 @@ xineplug_dmx_mpeg_pes_la_LDFLAGS = -avoid-version -module xineplug_dmx_mpeg_ts_la_SOURCES = demux_ts.c xineplug_dmx_mpeg_ts_la_LDFLAGS = -avoid-version -module +xineplug_dmx_qt_la_SOURCES = demux_qt.c +xineplug_dmx_qt_la_LDFLAGS = -avoid-version -module + include_HEADERS = demux.h ## diff --git a/src/demuxers/demux_avi.c b/src/demuxers/demux_avi.c index 9ac122007..3fb87df48 100644 --- a/src/demuxers/demux_avi.c +++ b/src/demuxers/demux_avi.c @@ -17,7 +17,7 @@ * 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_avi.c,v 1.39 2001/09/09 15:56:55 jkeil Exp $ + * $Id: demux_avi.c,v 1.40 2001/09/10 03:04:48 guenter Exp $ * * demultiplexer for avi streams * @@ -1035,7 +1035,7 @@ static void demux_avi_start (demux_plugin_t *this_gen, case mmioFOURCC('c', 'v', 'i', 'd'): /* Video in Cinepak format */ - this->avi->video_type = BUF_VIDEO_CINEPACK; + this->avi->video_type = BUF_VIDEO_CINEPAK; break; case mmioFOURCC('V', 'C', 'R', '1'): /* Video in ATI VCR1 format */ diff --git a/src/demuxers/demux_elem.c b/src/demuxers/demux_elem.c index 8bfad002c..41e02acbd 100644 --- a/src/demuxers/demux_elem.c +++ b/src/demuxers/demux_elem.c @@ -17,7 +17,7 @@ * 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_elem.c,v 1.18 2001/09/04 16:19:27 guenter Exp $ + * $Id: demux_elem.c,v 1.19 2001/09/10 03:04:48 guenter Exp $ * * demultiplexer for elementary mpeg streams * @@ -251,10 +251,11 @@ static int demux_mpeg_elem_open(demux_plugin_t *this_gen, bs = 4; if (input->read(input, this->scratch, bs) == bs) { - + /* printf ("demux_elem: %02x %02x %02x %02x (bs=%d)\n", this->scratch[0], this->scratch[1], this->scratch[2], this->scratch[3], bs); + */ if (this->scratch[0] || this->scratch[1] || (this->scratch[2] != 0x01) || (this->scratch[3] != 0xb3)) diff --git a/src/demuxers/demux_qt.c b/src/demuxers/demux_qt.c new file mode 100644 index 000000000..3241e9eaf --- /dev/null +++ b/src/demuxers/demux_qt.c @@ -0,0 +1,4354 @@ +/* + * Copyright (C) 2001 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.1 2001/09/10 03:04:48 guenter Exp $ + * + * demultiplexer for quicktime streams, based on: + * + * openquicktime.c + * + * This file is part of OpenQuicktime, a free QuickTime library. + * + * Based on QT4Linux by Adam Williams. + * + */ + + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <stdio.h> +#include <fcntl.h> +#include <unistd.h> +#include <pthread.h> +#include <string.h> +#include <stdlib.h> +#include <ctype.h> +#include <zlib.h> + +#include "xine_internal.h" +#include "monitor.h" +#include "demux.h" +#include "buffer.h" +#include "utils.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" + +static uint32_t xine_debug; + +/* 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; + +/* +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; + + + +/* ===================== sample table ======================== */ + +/* sample description */ + +typedef struct { + int motion_jpeg_quantization_table; +} quicktime_mjqt_t; + + +typedef struct { + int motion_jpeg_huffman_table; +} quicktime_mjht_t; + + +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; + + +typedef struct { + int version; + long flags; + long total_entries; + quicktime_stsd_table_t *table; +} quicktime_stsd_t; + + +/* time to sample */ +typedef struct { + long sample_count; + long sample_duration; +} 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; + + +/* 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; + +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; + + +/* 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; + +typedef struct { + int version; + long flags; + long total_entries; + quicktime_dref_table_t *table; +} quicktime_dref_t; + +/* data information */ + +typedef struct { + quicktime_dref_t dref; +} quicktime_dinf_t; + +/* video media header */ + +typedef struct { + int version; + long flags; + int graphics_mode; + int opcolor[3]; +} quicktime_vmhd_t; + + +/* sound media header */ + +typedef struct { + int version; + long flags; + int balance; + int reserved; +} quicktime_smhd_t; + +/* handler reference */ + +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 */ + +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; + + +/* media header */ + +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; + + +/* media */ + +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; + + +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 demux_qt_s { + demux_plugin_t demux_plugin; + + fifo_buffer_t *audio_fifo; + fifo_buffer_t *video_fifo; + + input_plugin_t *input; + + pthread_t thread; + + int status; + int send_end_buffers; + + quicktime_t *qt; + + 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]; + +} demux_qt_t ; + +/* + * 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]; + + 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; + + return 0; +} + +static void quicktime_copy_char32(char *output, char *input) +{ + *output++ = *input++; + *output++ = *input++; + *output++ = *input++; + *output = *input; +} + + +static unsigned long quicktime_current_time(void) +{ + time_t t; + time (&t); + return (t+(66*31536000)+1468800); +} + +static int quicktime_match_32(char *input, char *output) +{ + if(input[0] == output[0] && + input[1] == output[1] && + input[2] == output[2] && + input[3] == output[3]) + return 1; + else + return 0; +} + +static void quicktime_read_pascal(quicktime_t *file, char *data) +{ + char len = quicktime_read_char(file); + file->quicktime_read_data(file, data, len); + data[(int) len] = 0; +} + +/* matrix.c */ + +static void quicktime_matrix_init(quicktime_matrix_t *matrix) +{ + int i; + for(i = 0; i < 9; i++) matrix->values[i] = 0; + matrix->values[0] = matrix->values[4] = 1; + matrix->values[8] = 16384; +} + +static void quicktime_matrix_delete(quicktime_matrix_t *matrix) +{ +} + +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); + } +} + +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; +} + +/* mvhd.c */ + +static int quicktime_mvhd_init(quicktime_mvhd_t *mvhd) +{ + int i; + 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; +} + +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); +} + +static void quicktime_mhvd_init_video(quicktime_t *file, quicktime_mvhd_t *mvhd, float frame_rate) +{ + mvhd->time_scale = quicktime_get_timescale(frame_rate); +} + +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) +{ + if(atom->type[0] == type[0] && + atom->type[1] == type[1] && + atom->type[2] == type[2] && + atom->type[3] == type[3]) + 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]) && isalpha(type[1]) && isalpha(type[2]) && isalpha(type[3])) + 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->info) + { + free(udta->name); + } + if(udta->info_len && udta->info) + { + free(udta->info); + } + + quicktime_udta_init(udta); + + 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; +} + +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; +} + +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 */ + + +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; +} + +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])); + } +} + +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; +} + + +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])); + } +} + +/* edts.c */ + +static void quicktime_edts_init(quicktime_edts_t *edts) +{ + quicktime_elst_init(&(edts->elst)); +} + +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)); +} + +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); +} + +/* 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; +} + +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 */ +} + +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 */ +} + +static void quicktime_mdhd_delete(quicktime_mdhd_t *mdhd) +{ +} + +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); +} + +/* 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"); +} + +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"); +} + +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"); +} + +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"); +} + +static void quicktime_hdlr_delete(quicktime_hdlr_t *hdlr) +{ +} + +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); +} + +/* vmhd.c */ + +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; +} + +static void quicktime_vmhd_init_video(quicktime_t *file, + quicktime_vmhd_t *vmhd, + int frame_w, + int frame_h, + float frame_rate) +{ +} + +static void quicktime_vmhd_delete(quicktime_vmhd_t *vmhd) +{ +} + +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); +} + +/* smhd.c */ + +static void quicktime_smhd_init(quicktime_smhd_t *smhd) +{ + smhd->version = 0; + smhd->flags = 0; + smhd->balance = 0; + smhd->reserved = 0; +} + +static void quicktime_smhd_delete(quicktime_smhd_t *smhd) +{ +} + +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); +} + +/* 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; +} + +void quicktime_dref_table_delete(quicktime_dref_table_t *table) +{ + if(table->data_reference) free(table->data_reference); + table->data_reference = 0; +} + +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; +} + + +void quicktime_dref_init(quicktime_dref_t *dref) +{ + dref->version = 0; + dref->flags = 0; + dref->total_entries = 0; + dref->table = 0; +} + +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])); + } +} + +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])); + } +} + +/* 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)); } + 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; + } + + /* + quicktime_stsd_audio_dump(table); + printf("%lld %lld %lld", file->offset, file->file_position, file->ftell_position); + */ +} + +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)); + } + 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); + } + //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); +} + +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) +{ + 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); + 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); + } +} + +/* 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); + } +} + +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; +} + +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; +} + +static void quicktime_stsc_delete(quicktime_stsc_t *stsc) +{ + if(stsc->total_entries) free(stsc->table); + stsc->total_entries = 0; +} + +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); + } +} + + +static int quicktime_update_stsc(quicktime_stsc_t *stsc, long chunk, long samples) +{ + /* long i; */ + + 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); + } +} + +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); + } + } +} + +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, 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)); } + 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, 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, &(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), &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)); + 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); */ + } + } + } while(quicktime_position(file) < trak_atom->end); + + return 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; +} + +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; + } + + 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; +} + + + +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; +} + +static longest quicktime_sample_to_offset(quicktime_trak_t *trak, long sample) +{ + longest chunk, chunk_sample, chunk_offset1, chunk_offset2; + + quicktime_chunk_of_sample(&chunk_sample, &chunk, trak, sample); + // printf("\tBEFORE quicktime_chunk_to_offset chunk %lld, chunk_sample %lld\n", chunk, chunk_sample); + chunk_offset1 = quicktime_chunk_to_offset(trak, chunk); + // printf("\tAFTER quicktime_chunk_to_offset %lld\n", chunk_offset1); + chunk_offset2 = chunk_offset1 + quicktime_sample_range_size(trak, chunk_sample, sample); + // printf("\tAFTER AFTER %lld\n", chunk_offset2); + //printf("quicktime_sample_to_offset chunk %lld sample %lld chunk_offset %lld chunk_sample %lld chunk_offset + samples %lld\n", + // chunk, sample, chunk_offset1, chunk_sample, chunk_offset2); + return chunk_offset2; +} + +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; +} + +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; +} + +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) { + + /* 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("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) { + fprintf(stderr, "QT cmov: malloc err 0"); + exit(1); + } + /* Read in compressed header */ + + tlen = file->quicktime_read_data(file, (char*)cmov_buf, cmov_sz); + + if (tlen != 1) { + fprintf(stderr,"QT cmov: read err tlen %llu\n", tlen); + 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) { + fprintf(stderr,"QT cmov: malloc err moov_sz %u\n", moov_sz); + exit(1); + } + + 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) { + fprintf(stderr,"QT cmov: inflateInit err %d\n",zret); + break; + } + zret = inflate(&zstrm, Z_NO_FLUSH); + if ((zret != Z_OK) && (zret != Z_STREAM_END)) { + fprintf(stderr,"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 0; +} + + +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); + } + + 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; +} + +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); + } + + return 0; +} + +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; +} + +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++; + } + 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) +{ + return file->atracks[track].track->mdia.minf.stbl.stsd.table[0].format; +} + +static int quicktime_track_channels(quicktime_t *file, int track) +{ + if(track < file->total_atracks) + return file->atracks[track].channels; + + 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; + } + + 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 long quicktime_read_audio(quicktime_t *file, char *audio_buffer, long samples, int track) +{ + longest chunk_sample, chunk; + int result = 1; + quicktime_trak_t *trak = file->atracks[track].track; + longest fragment_len, chunk_end; + longest position = file->atracks[track].current_position; + longest end = position + samples; + longest bytes, total_bytes = 0; + longest buffer_offset; + + quicktime_chunk_of_sample(&chunk_sample, &chunk, trak, position); + buffer_offset = 0; + + while(position < end && result) + { + quicktime_set_audio_position(file, position, track); + fragment_len = quicktime_chunk_samples(trak, chunk); + chunk_end = chunk_sample + fragment_len; + fragment_len -= position - chunk_sample; + if(position + fragment_len > chunk_end) fragment_len = chunk_end - position; + if(position + fragment_len > end) fragment_len = end - position; + + bytes = quicktime_samples_to_bytes(trak, fragment_len); + result = file->quicktime_read_data(file, &audio_buffer[buffer_offset], bytes); + + total_bytes += bytes; + position += fragment_len; + chunk_sample = position; + buffer_offset += bytes; + chunk++; + } + + file->atracks[track].current_position = position; + if(!result) return 0; + return total_bytes; +} + +static int quicktime_read_chunk(quicktime_t *file, char *output, int track, longest chunk, longest byte_start, longest byte_len) +{ + quicktime_set_position(file, quicktime_chunk_to_offset(file->atracks[track].track, chunk) + byte_start); + if(file->quicktime_read_data(file, output, byte_len)) return 0; + else + return 1; +} + +static long quicktime_frame_size(quicktime_t *file, long frame, int track) +{ + long bytes = 0; + quicktime_trak_t *trak = file->vtracks[track].track; + + if(trak->mdia.minf.stbl.stsz.sample_size) + { + bytes = trak->mdia.minf.stbl.stsz.sample_size; + } + else + { + long total_frames = quicktime_track_samples(file, trak); + if(frame < 0) frame = 0; + else + if(frame > total_frames - 1) frame = total_frames - 1; + bytes = trak->mdia.minf.stbl.stsz.table[frame].size; + } + + + return bytes; +} + + +static longest quicktime_read_next_packet(quicktime_t *file, unsigned char *outbuf, int *isVideo, int *thetrak) +{ + longest packet_start; + longest min_video_delta = 100000000000; + longest min_audio_delta = 100000000000; + longest min_video_start=0; + longest min_audio_start=0; + longest current_position = quicktime_position(file); + long packet = 0; + long min_audio_packet=0; + long min_video_packet=0; + int trak = 0; + int min_audio_trak=0; + int min_video_trak=0; + + + for(trak = 0; trak < file->total_vtracks; trak++) { + packet = quicktime_offset_to_chunk(&packet_start, file->vtracks[trak].track, current_position); + /* printf("video_packet %d, video position %li\n", packet, packet_start); */ + if(current_position - packet_start < min_video_delta) { + min_video_delta = current_position - packet_start; + min_video_trak = trak; + min_video_packet = packet; + min_video_start = packet_start; + } + } + + for(trak = 0; trak < file->total_atracks; trak++) { + packet = quicktime_offset_to_chunk(&packet_start, file->atracks[trak].track, current_position); + /* printf("audio packet %d, audio position %li ", packet, packet_start); */ + if(current_position - packet_start < min_audio_delta) { + min_audio_delta = current_position - packet_start; + min_audio_trak = trak; + min_audio_packet = packet; + min_audio_start = packet_start; + } + } + if(min_audio_delta < min_video_delta) { + longest chunksize = file->atracks[min_audio_trak].track->mdia.minf.stbl.stsz.table[min_audio_packet-1].size; + /* printf("audio chunksize %li min_audio_start %li\n", chunksize, min_audio_start); */ + *thetrak = min_audio_trak; + *isVideo = 0; + file->quicktime_fseek(file, min_audio_start); + file->quicktime_read_data(file,(char*)outbuf, chunksize); + return chunksize; + } else { + longest chunksize = file->vtracks[min_video_trak].track->mdia.minf.stbl.stsz.table[min_video_packet-1].size; + /* printf("video chunksize %li\n", chunksize); */ + *thetrak = min_video_trak; + *isVideo = 1; + file->quicktime_fseek(file, min_video_start); + file->quicktime_read_data(file,(char*)outbuf, chunksize); + return chunksize; + + } + + return 0; +} + + + +static long quicktime_read_frame(quicktime_t *file, unsigned char *video_buffer, int track) +{ + longest bytes; + int result = 0; + + /* quicktime_trak_t *trak = file->vtracks[track].track; */ + bytes = quicktime_frame_size(file, file->vtracks[track].current_position, track); + + quicktime_set_video_position(file, file->vtracks[track].current_position, track); + result = file->quicktime_read_data(file, (char*)video_buffer, bytes); + file->vtracks[track].current_position++; + + if(!result) return 0; + return bytes; +} + +/* return -1 if there is NO keyframe after */ +static long quicktime_get_keyframe_after(quicktime_t *file, long frame, int track) +{ + quicktime_trak_t *trak = file->vtracks[track].track; + quicktime_stss_t *stss = &trak->mdia.minf.stbl.stss; + int lo, hi; + + lo = 0; + hi = stss->total_entries-1; + if (stss->table[lo].sample-1>=frame) return stss->table[lo].sample-1; + if (stss->table[hi].sample-1< frame) return -1; + while (hi>lo+1) { + /* here: stss->table[lo].sample-1<frame + stss->table[hi].sample-1>=frame */ + int med = (lo+hi)/2; + if (stss->table[med].sample-1<frame) lo = med; else hi = med; + } + /* here: hi=lo+1 */ + return stss->table[hi].sample-1; +} + + + +static int quicktime_read_frame_init(quicktime_t *file, int track) +{ + /* quicktime_trak_t *trak = file->vtracks[track].track; */ + quicktime_set_video_position(file, file->vtracks[track].current_position, track); + if(quicktime_ftell(file) != file->file_position) + { + file->input->seek (file->input, file->file_position, SEEK_SET); + file->ftell_position = file->file_position; + } + return 0; +} + +static int quicktime_read_frame_end(quicktime_t *file, int track) +{ + file->file_position = quicktime_ftell(file); + file->vtracks[track].current_position++; + return 0; +} + +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) +{ + 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) { + + 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); + found_mdat = 1; + } else if(quicktime_atom_is(&leaf_atom, "moov")) { + quicktime_read_moov(file, &(file->moov), &leaf_atom); + found_moov = 1; + } else { + quicktime_atom_skip(file, &leaf_atom); + } + } + } 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 = xmalloc (sizeof (quicktime_t)); + + quicktime_init(file); + + file->input = input; + + input->seek (input, 0, SEEK_SET); + + file->total_length = get_file_length(file); + + 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); + } + }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) +{ + 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); + + /* Get length. */ + new_file->total_length = get_file_length(new_file); + + if(quicktime_read_info(new_file)) { + quicktime_close(new_file); + printf("demux_qt: quicktime_open: error in header\n"); + new_file = 0; + } + + return new_file; +} + + +/* + * now for the xine-specific demuxer stuff + */ + +static void *demux_qt_loop (void *this_gen) { + + buf_element_t *buf = NULL; + demux_qt_t *this = (demux_qt_t *) this_gen; + uint32_t audio_pts, video_pts, frame_num; + + /* printf ("demux_qt: demux loop starting...\n"); */ + + do { + + audio_pts = quicktime_audio_position (this->qt, 0) * this->audio_factor ; + frame_num = quicktime_video_position (this->qt, 0); + video_pts = frame_num * this->video_step ; + + if ( this->audio_fifo && (audio_pts < video_pts)) { + + buf = this->audio_fifo->buffer_pool_alloc (this->audio_fifo); + + buf->content = buf->mem; + + if ( !(buf->size = quicktime_read_audio (this->qt, buf->content, 256, 0)) ) { + this->status = DEMUX_FINISHED; + buf->free_buffer (buf); + } else { + + buf->PTS = audio_pts; + buf->type = this->audio_type; + buf->decoder_info[0] = 1; + buf->input_time = 0; + buf->input_pos = 0; + + this->audio_fifo->put (this->audio_fifo, buf); + } + + } else { + + int size;/* = quicktime_frame_size (this->qt, frame_num, 0); */ + + if (! (size=quicktime_read_frame (this->qt, this->scratch, 0))) + this->status = DEMUX_FINISHED; + else { + int pos=0; + + while (size>0) { + int copy_bytes; + + buf = this->video_fifo->buffer_pool_alloc (this->video_fifo); + + if (size>buf->max_size) { + copy_bytes = buf->max_size; + buf->decoder_info[0] = 1; + } else { + copy_bytes = size; + buf->decoder_info[0] = 2; + } + + memcpy (buf->mem, &this->scratch[pos], copy_bytes); + + buf->content = buf->mem; + buf->size = copy_bytes; + buf->PTS = video_pts; + buf->type = this->video_type; + buf->input_time = video_pts / 90000; + buf->input_pos = this->input->get_current_pos(this->input); + + this->video_fifo->put (this->video_fifo, buf); + + pos += copy_bytes; + size -= copy_bytes; + } + } + } + + } while (this->status == DEMUX_OK) ; + + + /* + printf ("demux_qt: demux loop finished (status: %d)\n", + this->status); + */ + + this->status = DEMUX_FINISHED; + + if (this->send_end_buffers) { + buf = this->video_fifo->buffer_pool_alloc (this->video_fifo); + buf->type = BUF_CONTROL_END; + buf->decoder_info[0] = 0; /* stream 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_info[0] = 0; /* stream finished */ + this->audio_fifo->put (this->audio_fifo, buf); + } + + } + + pthread_exit(NULL); + + return NULL; +} + +static void demux_qt_close (demux_plugin_t *this_gen) { + + demux_qt_t *this = (demux_qt_t *) this_gen; + free (this); + +} + +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; + + if (this->status != DEMUX_OK) { + printf ("demux_qt: stop...ignored\n"); + return; + } + + this->send_end_buffers = 0; + this->status = DEMUX_FINISHED; + + pthread_cancel (this->thread); + pthread_join (this->thread, &p); + + this->video_fifo->clear(this->video_fifo); + if (this->audio_fifo) + this->audio_fifo->clear(this->audio_fifo); + + buf = this->video_fifo->buffer_pool_alloc (this->video_fifo); + buf->type = BUF_CONTROL_END; + buf->decoder_info[0] = 1; /* forced */ + + 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_info[0] = 1; /* forced */ + this->audio_fifo->put (this->audio_fifo, buf); + } +} + +static int demux_qt_get_status (demux_plugin_t *this_gen) { + demux_qt_t *this = (demux_qt_t *) this_gen; + + return this->status; +} + +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; + this->bih.biCompression=0; /* FIXME */ + this->bih.biBitCount=0; + this->bih.biSizeImage=0; + 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); + + if (!strncasecmp (video, "cvid", 4)) { + this->video_type = BUF_VIDEO_CINEPAK; + + this->bih.biCompression=mmioFOURCC('c', 'v', 'i', 'd'); + + } else if (!strncasecmp (video, "jpeg", 4)) + this->video_type = BUF_VIDEO_JPEG; + else if (!strncasecmp (video, "raw ", 4)) + this->video_type = BUF_VIDEO_RGB; + else if (!strncasecmp (video, "yuv2", 4)) + this->video_type = BUF_VIDEO_YUY2; + else if (!strncasecmp (video, "mpeg", 4)) + this->video_type = BUF_VIDEO_MPEG; + else if (!strncasecmp (video, "mjpa", 4)) + this->video_type = BUF_VIDEO_MJPEG; + else if (!strncasecmp (video, "mjpb", 4)) + this->video_type = BUF_VIDEO_MJPEG; + else if (!strncasecmp (video, "svq1", 4)) + this->video_type = BUF_VIDEO_SORENSON; + else if (!strncasecmp (video, "svqi", 4)) + this->video_type = BUF_VIDEO_SORENSON; + else { + printf ("demux_qt: unknown video codec >%s<\n", + video); + return 0; + } + + + audio = quicktime_audio_compressor (this->qt, 0); + + 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)) ; + + if (!strncasecmp (audio, "raw ", 4)) { + this->audio_type = BUF_AUDIO_LPCM_LE; + this->wavex.wFormatTag = WAVE_FORMAT_ADPCM; + } else { + printf ("demux_qt: unknown audio codec >%s<\n", + audio); + return 0; + } + + return 1; +} + +static void 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, + gui_get_next_mrl_cb_t next_mrl_cb, + gui_branched_cb_t branched_cb) +{ + + demux_qt_t *this = (demux_qt_t *) this_gen; + buf_element_t *buf; + + this->video_fifo = video_fifo; + this->audio_fifo = audio_fifo; + + /* + * init quicktime parser + */ + + this->qt = quicktime_open (this->input); + + if (!this->qt) { + this->status = DEMUX_FINISHED; + return; + } + + printf ("demux_qt: video codec %s (%f fps), audio codec %s (%d 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)); + + if (!demux_qt_detect_compressors (this)) { + this->status = DEMUX_FINISHED; + return; + } + + /* + * 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); + + 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); + + } + + /* + * send init info to decoders + */ + + buf = this->video_fifo->buffer_pool_alloc (this->video_fifo); + buf->content = buf->mem; + 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; + + this->video_fifo->put (this->video_fifo, buf); + + 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_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); + this->audio_fifo->put (this->audio_fifo, buf); + } + + /* + * now start demuxing + */ + + this->status = DEMUX_OK ; + + pthread_create (&this->thread, NULL, demux_qt_loop, this) ; +} + +static int demux_qt_open(demux_plugin_t *this_gen, + input_plugin_t *input, int stage) { + + demux_qt_t *this = (demux_qt_t *) this_gen; + + switch(stage) { + + case STAGE_BY_CONTENT: { + + 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; + + if (quicktime_check_sig (input)) { + this->input = input; + return DEMUX_CAN_HANDLE; + } + + return DEMUX_CANNOT_HANDLE; + + } + break; + + case STAGE_BY_EXTENSION: { + char *suffix; + char *MRL; + + MRL = input->get_mrl (input); + + suffix = strrchr(MRL, '.'); + + if(!suffix) + return DEMUX_CANNOT_HANDLE; + + if (!strcasecmp(suffix, ".mov")) { + this->input = input; + return DEMUX_CAN_HANDLE; + } + return DEMUX_CANNOT_HANDLE; + + } + break; + + default: + return DEMUX_CANNOT_HANDLE; + break; + } + + return DEMUX_CANNOT_HANDLE; +} + +static char *demux_qt_get_id(void) { + return "QUICKTIME"; +} + +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); +} + +demux_plugin_t *init_demuxer_plugin(int iface, config_values_t *config) { + + demux_qt_t *this; + + if (iface != 3) { + printf( "demux_mpeg: plugin doesn't support plugin API version %d.\n" + "demux_mpeg: this means there's a version mismatch between xine and this " + "demux_mpeg: demuxer plugin.\nInstalling current demux plugins should help.\n", + iface); + return NULL; + } + + this = xmalloc (sizeof (demux_qt_t)); + xine_debug = config->lookup_int (config, "xine_debug", 0); + + this->demux_plugin.interface_version = DEMUXER_PLUGIN_IFACE_VERSION; + this->demux_plugin.open = demux_qt_open; + this->demux_plugin.start = demux_qt_start; + this->demux_plugin.stop = demux_qt_stop; + this->demux_plugin.close = demux_qt_close; + this->demux_plugin.get_status = demux_qt_get_status; + this->demux_plugin.get_identifier = demux_qt_get_id; + this->demux_plugin.get_stream_length = demux_qt_get_stream_length; + + return (demux_plugin_t *) this; +} diff --git a/src/liblpcm/xine_decoder.c b/src/liblpcm/xine_decoder.c index 89a34fc35..62698bacc 100644 --- a/src/liblpcm/xine_decoder.c +++ b/src/liblpcm/xine_decoder.c @@ -17,7 +17,7 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA * - * $Id: xine_decoder.c,v 1.7 2001/09/06 13:36:18 jkeil Exp $ + * $Id: xine_decoder.c,v 1.8 2001/09/10 03:04:48 guenter Exp $ * * 31-8-2001 Added LPCM rate sensing. * (c) 2001 James Courtier-Dutton James@superbug.demon.co.uk @@ -83,13 +83,17 @@ void lpcm_decode_data (audio_decoder_t *this_gen, buf_element_t *buf) { int stream_be; this->pts = buf->PTS; - this->rate=buf->decoder_info[1]; - this->bits_per_sample=buf->decoder_info[2] ; - this->number_of_channels=buf->decoder_info[3] ; - this->ao_cap_mode=(this->number_of_channels == 2) ? AO_CAP_MODE_STEREO : AO_CAP_MODE_MONO; - if (buf->decoder_info[0] == 0) + if (buf->decoder_info[0] == 0) { + this->rate=buf->decoder_info[1]; + this->bits_per_sample=buf->decoder_info[2] ; + this->number_of_channels=buf->decoder_info[3] ; + this->ao_cap_mode=(this->number_of_channels == 2) ? AO_CAP_MODE_STEREO : AO_CAP_MODE_MONO; return; + } + if (!this->output_open) { + printf ("liblpcm: opening audio output (%d Hz sampling rate, mode=%d)\n", + this->rate, this->ao_cap_mode); this->output_open = (this->audio_out->open (this->audio_out, this->bits_per_sample, this->rate, this->ao_cap_mode) == 1); diff --git a/src/libw32dll/w32codec.c b/src/libw32dll/w32codec.c index 32e2864c3..76caf4161 100644 --- a/src/libw32dll/w32codec.c +++ b/src/libw32dll/w32codec.c @@ -17,7 +17,7 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA * - * $Id: w32codec.c,v 1.20 2001/09/08 18:11:41 guenter Exp $ + * $Id: w32codec.c,v 1.21 2001/09/10 03:04:48 guenter Exp $ * * routines for using w32 codecs * @@ -102,9 +102,10 @@ static char* get_vids_codec_name(w32v_decoder_t *this, this->flipped=1; return "ir32_32.dll"; - case BUF_VIDEO_CINEPACK: + case BUF_VIDEO_CINEPAK: /* Video in Cinepak format */ - this->yuv_supported=1; + this->flipped=1; + this->yuv_supported=0; return "iccvid.dll"; /*** Only 16bit .DLL available (can't load under linux) *** @@ -144,7 +145,7 @@ static int w32v_can_handle (video_decoder_t *this_gen, int buf_type) { return ( buf_type == BUF_VIDEO_IV50 || buf_type == BUF_VIDEO_IV41 || buf_type == BUF_VIDEO_IV32 || - buf_type == BUF_VIDEO_CINEPACK || + buf_type == BUF_VIDEO_CINEPAK || /* buf_type == BUF_VIDEO_ATIVCR1 || */ buf_type == BUF_VIDEO_ATIVCR2 || buf_type == BUF_VIDEO_I263); diff --git a/src/xine-engine/audio_decoder.c b/src/xine-engine/audio_decoder.c index d1cc90071..76bf4d03c 100644 --- a/src/xine-engine/audio_decoder.c +++ b/src/xine-engine/audio_decoder.c @@ -17,7 +17,7 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA * - * $Id: audio_decoder.c,v 1.34 2001/09/09 15:39:47 jkeil Exp $ + * $Id: audio_decoder.c,v 1.35 2001/09/10 03:04:48 guenter Exp $ * * * functions that implement audio decoding @@ -44,11 +44,11 @@ void *audio_decoder_loop (void *this_gen) { while (running) { - /* printf ("audio_loop: waiting for package...\n"); */ + /* printf ("audio_loop: waiting for package...\n"); */ buf = this->audio_fifo->get (this->audio_fifo); - - /* printf ("audio_loop: got package pts = %d\n", buf->PTS); */ + + /* printf ("audio_loop: got package pts = %d\n", buf->PTS); */ if (buf->input_pos) this->cur_input_pos = buf->input_pos; @@ -134,7 +134,7 @@ void *audio_decoder_loop (void *this_gen) { if ( (buf->type & 0xFF000000) == BUF_AUDIO_BASE ) { - /* printf ("audio_decoder: got an audio buffer, type %08x\n", buf->type); */ + /* printf ("audio_loop: got an audio buffer, type %08x\n", buf->type); */ /* update track map */ @@ -143,7 +143,7 @@ void *audio_decoder_loop (void *this_gen) { i++; /* - printf ("audio_decoder: got an audio buffer, type %08x, %d map entries, i=%d\n", + printf ("audio_loop: got an audio buffer, type %08x, %d map entries, i=%d\n", buf->type, this->audio_track_map_entries, i); */ @@ -184,16 +184,18 @@ void *audio_decoder_loop (void *this_gen) { this->cur_audio_decoder_plugin = decoder; this->cur_audio_decoder_plugin->init (this->cur_audio_decoder_plugin, this->audio_out); - printf ("audio_decoder: using decoder >%s< \n", + printf ("audio_loop: using decoder >%s< \n", decoder->get_identifier()); } + + /* printf ("audio_loop: sending data to decoder\n"); */ decoder->decode_data (decoder, buf); } } } else - printf ("audio_decoder: unknown buffer type: %08x\n", buf->type); + printf ("audio_loop: unknown buffer type: %08x\n", buf->type); profiler_stop_count (1); } diff --git a/src/xine-engine/audio_out.c b/src/xine-engine/audio_out.c index cb9846410..f3cef32fd 100644 --- a/src/xine-engine/audio_out.c +++ b/src/xine-engine/audio_out.c @@ -17,7 +17,7 @@ * along with self program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA * - * $Id: audio_out.c,v 1.12 2001/09/06 15:26:07 joachim_koenig Exp $ + * $Id: audio_out.c,v 1.13 2001/09/10 03:04:48 guenter Exp $ * * 22-8-2001 James imported some useful AC3 sections from the previous alsa driver. * (c) 2001 Andy Lo A Foe <andy@alsaplayer.org> @@ -135,6 +135,7 @@ static int ao_open(ao_instance_t *this, this->mode = mode; this->input_frame_rate = rate; + this->bits = bits; this->audio_started = 0; this->last_audio_vpts = 0; @@ -150,6 +151,11 @@ static int ao_open(ao_instance_t *this, default: /* AUTO */ this->do_resample = this->output_frame_rate != this->input_frame_rate; } + + /* HACK: we do not have resample functions for 8-bit audio */ + if (this->bits==8) + this->do_resample = 0; + if (this->do_resample) printf("audio_out: will resample audio from %d to %d\n", this->input_frame_rate, this->output_frame_rate); @@ -311,7 +317,7 @@ static int ao_write(ao_instance_t *this, } else switch (this->mode) { case AO_CAP_MODE_MONO: audio_out_resample_mono (output_frames, num_frames, - this->frame_buffer, num_output_frames); + this->frame_buffer, num_output_frames); this->driver->write(this->driver, this->frame_buffer, num_output_frames); break; case AO_CAP_MODE_STEREO: diff --git a/src/xine-engine/audio_out.h b/src/xine-engine/audio_out.h index f2a0e2b4b..83a18a1ce 100644 --- a/src/xine-engine/audio_out.h +++ b/src/xine-engine/audio_out.h @@ -17,7 +17,7 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA * - * $Id: audio_out.h,v 1.13 2001/09/06 13:27:47 jkeil Exp $ + * $Id: audio_out.h,v 1.14 2001/09/10 03:04:48 guenter Exp $ */ #ifndef HAVE_AUDIO_OUT_H #define HAVE_AUDIO_OUT_H @@ -167,6 +167,7 @@ struct ao_instance_s { int resample_conf; int do_resample; int mode; + int bits; int gap_tolerance; uint16_t *frame_buffer; int16_t *zero_space; diff --git a/src/xine-engine/buffer.h b/src/xine-engine/buffer.h index 2830cb18c..85f99e585 100644 --- a/src/xine-engine/buffer.h +++ b/src/xine-engine/buffer.h @@ -17,7 +17,7 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA * - * $Id: buffer.h,v 1.11 2001/09/08 18:11:41 guenter Exp $ + * $Id: buffer.h,v 1.12 2001/09/10 03:04:48 guenter Exp $ * * * contents: @@ -70,7 +70,7 @@ extern "C" { #define BUF_VIDEO_BASE 0x02000000 #define BUF_VIDEO_MPEG 0x02000000 #define BUF_VIDEO_MPEG4 0x02010000 -#define BUF_VIDEO_QUICKTIME 0x02020000 +#define BUF_VIDEO_CINEPAK 0x02020000 #define BUF_VIDEO_SORENSON 0x02030000 #define BUF_VIDEO_MSMPEG4 0x02040000 #define BUF_VIDEO_MJPEG 0x02050000 @@ -82,7 +82,10 @@ extern "C" { #define BUF_VIDEO_ATIVCR2 0x020b0000 #define BUF_VIDEO_I263 0x020c0000 #define BUF_VIDEO_RV10 0x020d0000 -#define BUF_VIDEO_CINEPACK 0x020e0000 +#define BUF_VIDEO_FILL 0x020e0000 +#define BUF_VIDEO_RGB 0x020f0000 +#define BUF_VIDEO_YUY2 0x02100000 +#define BUF_VIDEO_JPEG 0x02110000 /* audio buffer types: */ diff --git a/src/xine-engine/xine.c b/src/xine-engine/xine.c index 8af7b3461..b78b3a994 100644 --- a/src/xine-engine/xine.c +++ b/src/xine-engine/xine.c @@ -17,7 +17,7 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA * - * $Id: xine.c,v 1.58 2001/09/06 18:38:12 jkeil Exp $ + * $Id: xine.c,v 1.59 2001/09/10 03:04:48 guenter Exp $ * * top-level xine functions * @@ -127,7 +127,7 @@ static int try_demux_with_stages(xine_t *this, const char *MRL, while(stages[s] != -1) { for(i = 0; i < this->num_demuxer_plugins; i++) { - printf ("trying demuxer %s\n", this->demuxer_plugins[i]->get_identifier()); + /* printf ("trying demuxer %s\n", this->demuxer_plugins[i]->get_identifier()); */ if(this->demuxer_plugins[i]->open(this->demuxer_plugins[i], this->cur_input_plugin, stages[s]) == DEMUX_CAN_HANDLE) { @@ -284,8 +284,6 @@ void xine_play (xine_t *this, char *mrl, this->metronom->set_speed (this->metronom, SPEED_NORMAL); this->audio_mute = 0; this->speed = SPEED_NORMAL; - - printf ("xine_play: demuxer started\n"); } pthread_mutex_unlock (&this->xine_lock); |