From ac566de8ba01fc0572c7aadf34af65f017e311fe Mon Sep 17 00:00:00 2001 From: Darren Salt Date: Sat, 16 Jan 2010 15:40:34 +0000 Subject: Added basic support for .qtl (Quicktime media link). --- src/demuxers/demux_qt.c | 138 ++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 133 insertions(+), 5 deletions(-) (limited to 'src') diff --git a/src/demuxers/demux_qt.c b/src/demuxers/demux_qt.c index 8e00d73a4..4343cde0d 100644 --- a/src/demuxers/demux_qt.c +++ b/src/demuxers/demux_qt.c @@ -670,9 +670,100 @@ static void free_qt_info(qt_info *info) { } } -/* returns 1 if the file is determined to be a QT file, 0 otherwise */ -static int is_qt_file(input_plugin_t *qt_file) { +static char *qtl_file_url (input_plugin_t *input, const unsigned char *preview, int len) +{ + char *url = NULL; + if (len < 64) + return NULL; + + /* skip BOM, if present */ + if (preview[0] == 0xEF && preview[1] == 0xBB && preview[2] == 0xBF) + { + preview += 3; + len -= 3; + } + + xml_node_t *tree = NULL; + xml_parser_t *xml = xml_parser_init_r (preview, len, XML_PARSER_CASE_INSENSITIVE); + if (xml_parser_build_tree_r (xml, &tree) < 0) + return NULL; + + xml_node_t *node = tree; + while (node && strcasecmp (node->name, "embed")) + node = node->next; + + if (!node) + goto not_qtl; + + url = (char *) xml_parser_get_property (node, "src"); + if (url) { + char *slash = strchr (url, '/'); + char *proto = strstr (url, "://"); + if (proto + 1 == slash) + /* absolute */ + url = strdup (url); + else + { /* relative */ + const char *dir = input->get_mrl (input); + slash = strrchr (dir, '/'); + asprintf (&url, "%.*s/%s", + slash ? (int)(slash - dir) : 1, + slash ? dir : ".", url); + } + } + +not_qtl: + xml_parser_free_tree (tree); + return url; +} + +/* Simple approach for parsing qtl files. */ +static int demux_qt_parse_references (demux_qt_t *this, int send) +{ + char *buf = NULL; + int buf_size = 0; + int buf_used = 0; + int len = 0; + + off_t pos = this->input->get_current_pos (this->input); + this->input->seek (this->input, 0, SEEK_SET); + + /* Read in a chunk from the file. + * Hopefully fine since the reference file is small... + */ + do { + buf_size += 1024; + buf = realloc(buf, buf_size+1); + + len = this->input->read (this->input, &buf[buf_used], buf_size - buf_used); + + if (len > 0) + buf_used += len; + + /* 50K of reference file? Something must be wrong */ + if (buf_used > 50*1024) + break; + } while (len > 0); + + this->input->seek (this->input, pos, SEEK_SET); + + char *url = qtl_file_url (this->input, buf, buf_used); + if (url && send) + _x_demux_send_mrl_reference (this->stream, 0, url, NULL, 0, 0); + free (url); + free (buf); + + return !!url; +} + +/* returns 1 if the file is determined to be a QT file, + * 2 if it is a QTL file, + * 0 otherwise + */ +static int id_qt_file(demux_qt_t *this) { + + input_plugin_t *const qt_file = this->input; off_t moov_atom_offset = -1; int64_t moov_atom_size = -1; int i; @@ -685,6 +776,13 @@ static int is_qt_file(input_plugin_t *qt_file) { if ((qt_file->get_capabilities(qt_file) & INPUT_CAP_SEEKABLE) == 0) { memset (&preview, 0, MAX_PREVIEW_SIZE); len = qt_file->get_optional_data(qt_file, preview, INPUT_OPTIONAL_DATA_PREVIEW); + + char *url = qtl_file_url (qt_file, preview, len); + if (url) { + free (url); + return 2; + } + if (_X_BE_32(&preview[4]) == MOOV_ATOM) return 1; else { @@ -707,6 +805,9 @@ static int is_qt_file(input_plugin_t *qt_file) { } } + if (demux_qt_parse_references (this, 0)) + return 2; + find_moov_atom(qt_file, &moov_atom_offset, &moov_atom_size); if (moov_atom_offset == -1) { return 0; @@ -2305,6 +2406,13 @@ static int demux_qt_send_chunk(demux_plugin_t *this_gen) { int dispatch_audio; /* boolean for deciding which trak to dispatch */ int64_t pts_diff; + /* handle QTL here */ + if (!this->qt) { + demux_qt_parse_references (this, 1); + this->status = DEMUX_FINISHED; + return this->status; + } + /* if this is DRM-protected content, finish playback before it even * tries to start */ if (this->qt->last_error == QT_DRM_NOT_SUPPORTED) { @@ -2597,6 +2705,12 @@ static void demux_qt_send_headers(demux_plugin_t *this_gen) { this->status = DEMUX_OK; + if (!this->qt) { + _x_stream_info_set(this->stream, XINE_STREAM_INFO_HAS_VIDEO, 0); + _x_stream_info_set(this->stream, XINE_STREAM_INFO_HAS_AUDIO, 0); + return; + } + /* figure out where the data begins and ends */ if (this->qt->video_trak != -1) { video_trak = &this->qt->traks[this->qt->video_trak]; @@ -2914,6 +3028,10 @@ static int demux_qt_seek (demux_plugin_t *this_gen, start_pos = (off_t) ( (double) start_pos / 65535 * this->data_size ); + /* we could be handling QTL */ + if (!this->qt) + return this->status = DEMUX_OK; + /* short-circuit any attempts to seek in a non-seekable stream, including * seeking in the forward direction; this may change later */ if ((this->input->get_capabilities(this->input) & INPUT_CAP_SEEKABLE) == 0) { @@ -3013,6 +3131,7 @@ static demux_plugin_t *open_plugin (demux_class_t *class_gen, xine_stream_t *str demux_qt_t *this; xine_cfg_entry_t entry; qt_error last_error; + int type; if ((input->get_capabilities(input) & INPUT_CAP_BLOCK)) { return NULL; @@ -3046,10 +3165,14 @@ static demux_plugin_t *open_plugin (demux_class_t *class_gen, xine_stream_t *str case METHOD_BY_CONTENT: - if (!is_qt_file(this->input)) { + type = id_qt_file(this); + if (type < 1) { free (this); return NULL; } + if (type != 1) + break; + if ((this->qt = create_qt_info()) == NULL) { free (this); return NULL; @@ -3082,6 +3205,7 @@ static demux_plugin_t *open_plugin (demux_class_t *class_gen, xine_stream_t *str if (strncasecmp (ending, ".mov", 4) && strncasecmp (ending, ".qt", 3) && + strncasecmp (ending, ".qtl", 4) && strncasecmp (ending, ".mp4", 4)) { free (this); return NULL; @@ -3091,10 +3215,14 @@ static demux_plugin_t *open_plugin (demux_class_t *class_gen, xine_stream_t *str /* we want to fall through here */ case METHOD_EXPLICIT: { - if (!is_qt_file(this->input)) { + type = id_qt_file(this); + if (type < 1) { free (this); return NULL; } + if (type != 1) + break; + if ((this->qt = create_qt_info()) == NULL) { free (this); return NULL; @@ -3126,7 +3254,7 @@ static const char *get_identifier (demux_class_t *this_gen) { } static const char *get_extensions (demux_class_t *this_gen) { - return "mov qt mp4 m4a m4b"; + return "mov qt qtl mp4 m4a m4b"; } static const char *get_mimetypes (demux_class_t *this_gen) { -- cgit v1.2.3 From f6456aeccb0e7a420aa25997d8fe6ee87da489a5 Mon Sep 17 00:00:00 2001 From: Darren Salt Date: Sat, 16 Jan 2010 15:54:36 +0000 Subject: Clean up properly after QTL parsing. --- src/demuxers/demux_qt.c | 1 + 1 file changed, 1 insertion(+) (limited to 'src') diff --git a/src/demuxers/demux_qt.c b/src/demuxers/demux_qt.c index 4343cde0d..bea9c3118 100644 --- a/src/demuxers/demux_qt.c +++ b/src/demuxers/demux_qt.c @@ -715,6 +715,7 @@ static char *qtl_file_url (input_plugin_t *input, const unsigned char *preview, not_qtl: xml_parser_free_tree (tree); + xml_parser_finalize_r (xml); return url; } -- cgit v1.2.3 From e890141a04b1e79feddf17b4db460050a25f613e Mon Sep 17 00:00:00 2001 From: Darren Salt Date: Sun, 17 Jan 2010 03:10:22 +0000 Subject: "Fix" playback of 24-bit FLAC. We pretend that it's 16-bit to avoid "audio device unavailable" (ALSA). Also, the clock is a bit fast. --- src/combined/decoder_flac.c | 25 +++++++++++++++++-------- src/demuxers/demux_flac.c | 7 +++++-- 2 files changed, 22 insertions(+), 10 deletions(-) (limited to 'src') diff --git a/src/combined/decoder_flac.c b/src/combined/decoder_flac.c index b774e0b90..fe1822797 100644 --- a/src/combined/decoder_flac.c +++ b/src/combined/decoder_flac.c @@ -122,7 +122,7 @@ flac_write_callback (const FLAC__StreamDecoder *decoder, flac_decoder_t *this = (flac_decoder_t *)client_data; audio_buffer_t *audio_buffer = NULL; int samples_left = frame->header.blocksize; - int bytes_per_sample = (frame->header.bits_per_sample == 8)?1:2; + int bytes_per_sample = (frame->header.bits_per_sample <= 8) ? 1 : 2; int buf_samples; int8_t *data8; int16_t *data16; @@ -139,21 +139,31 @@ flac_write_callback (const FLAC__StreamDecoder *decoder, else buf_samples = samples_left; - - if( frame->header.bits_per_sample == 8 ) { + switch (frame->header.bits_per_sample) { + case 8: data8 = (int8_t *)audio_buffer->mem; for( j=0; j < buf_samples; j++ ) for( i=0; i < frame->header.channels; i++ ) *data8++ = buffer[i][j]; + break; - } else { - + case 16: data16 = (int16_t *)audio_buffer->mem; for( j=0; j < buf_samples; j++ ) for( i=0; i < frame->header.channels; i++ ) *data16++ = buffer[i][j]; + break; + + case 24: + data16 = (int16_t *)audio_buffer->mem; + + for( j=0; j < buf_samples; j++ ) + for( i=0; i < frame->header.channels; i++ ) + *data16++ = buffer[i][j] >> 8; + break; + } audio_buffer->num_frames = buf_samples; @@ -261,14 +271,13 @@ flac_decode_data (audio_decoder_t *this_gen, buf_element_t *buf) if (!this->output_open) { + const int bits = this->bits_per_sample; this->output_open = (this->stream->audio_out->open) ( this->stream->audio_out, this->stream, - this->bits_per_sample, + bits > 16 ? 16 : bits, this->sample_rate, mode); - - } this->buf_pos = 0; } else if (this->output_open) diff --git a/src/demuxers/demux_flac.c b/src/demuxers/demux_flac.c index 976656016..11bbda061 100644 --- a/src/demuxers/demux_flac.c +++ b/src/demuxers/demux_flac.c @@ -364,12 +364,15 @@ static void demux_flac_send_headers(demux_plugin_t *this_gen) { return; } + /* lie about 24bps */ + int bits = this->bits_per_sample > 16 ? 16 : this->bits_per_sample; + buf = this->audio_fifo->buffer_pool_alloc (this->audio_fifo); buf->type = BUF_AUDIO_FLAC; buf->decoder_flags = BUF_FLAG_HEADER|BUF_FLAG_STDHEADER|BUF_FLAG_FRAME_END; buf->decoder_info[0] = 0; buf->decoder_info[1] = this->sample_rate; - buf->decoder_info[2] = this->bits_per_sample; + buf->decoder_info[2] = bits; buf->decoder_info[3] = this->channels; /* copy the faux WAV header */ buf->size = sizeof(xine_waveformatex) + FLAC_STREAMINFO_SIZE; @@ -386,7 +389,7 @@ static void demux_flac_send_headers(demux_plugin_t *this_gen) { _x_stream_info_set(this->stream, XINE_STREAM_INFO_AUDIO_SAMPLERATE, this->sample_rate); _x_stream_info_set(this->stream, XINE_STREAM_INFO_AUDIO_BITS, - this->bits_per_sample); + bits); this->status = DEMUX_OK; } -- cgit v1.2.3