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