diff options
Diffstat (limited to 'src/demuxers')
-rw-r--r-- | src/demuxers/Makefile.am | 4 | ||||
-rw-r--r-- | src/demuxers/demux_matroska-chapters.c | 433 | ||||
-rw-r--r-- | src/demuxers/demux_matroska.c | 485 | ||||
-rw-r--r-- | src/demuxers/demux_matroska.h | 150 | ||||
-rw-r--r-- | src/demuxers/demux_ts.c | 183 | ||||
-rw-r--r-- | src/demuxers/matroska.h | 53 |
6 files changed, 1045 insertions, 263 deletions
diff --git a/src/demuxers/Makefile.am b/src/demuxers/Makefile.am index 9fda6adfa..29ca47187 100644 --- a/src/demuxers/Makefile.am +++ b/src/demuxers/Makefile.am @@ -9,7 +9,7 @@ AM_LDFLAGS = $(xineplug_ldflags) # --------- # All of xine demuxer plugins should be named like the scheme "xineplug_dmx_" -noinst_HEADERS = asfheader.h qtpalette.h group_games.h group_audio.h id3.h ebml.h matroska.h iff.h flacutils.h real_common.h +noinst_HEADERS = asfheader.h qtpalette.h group_games.h group_audio.h id3.h ebml.h matroska.h demux_matroska.h iff.h flacutils.h real_common.h if ENABLE_ASF asf_module = xineplug_dmx_asf.la @@ -134,7 +134,7 @@ xineplug_dmx_image_la_LIBADD = $(XINE_LIB) xineplug_dmx_nsv_la_SOURCES = demux_nsv.c xineplug_dmx_nsv_la_LIBADD = $(XINE_LIB) -xineplug_dmx_matroska_la_SOURCES = demux_matroska.c ebml.c +xineplug_dmx_matroska_la_SOURCES = demux_matroska.c demux_matroska-chapters.c ebml.c xineplug_dmx_matroska_la_LIBADD = $(XINE_LIB) $(LTLIBINTL) $(ZLIB_LIBS) xineplug_dmx_matroska_la_CFLAGS = $(AM_CFLAGS) -fno-strict-aliasing xineplug_dmx_matroska_la_CPPFLAGS = $(AM_CPPFLAGS) $(ZLIB_CPPFLAGS) diff --git a/src/demuxers/demux_matroska-chapters.c b/src/demuxers/demux_matroska-chapters.c new file mode 100644 index 000000000..7176467fe --- /dev/null +++ b/src/demuxers/demux_matroska-chapters.c @@ -0,0 +1,433 @@ +/* + * Copyright (C) 2009 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA + * + * demultiplexer for matroska streams: chapter handling + * + * TODO: + * - nested chapters + * + * Authors: + * Nicos Gollan <gtdev@spearhead.de> + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#define LOG_MODULE "demux_matroska_chapters" +#define LOG_VERBOSE +/* +#define LOG +*/ + +#include <xine/xine_internal.h> +#include <xine/xineutils.h> +#include <xine/demux.h> + +#include "ebml.h" +#include "matroska.h" +#include "demux_matroska.h" + +/* TODO: this only handles one single (title, language, country) tuple. + * See the header for information. */ +static int parse_chapter_display(demux_matroska_t *this, matroska_chapter_t *chap, int level) { + ebml_parser_t *ebml = this->ebml; + int next_level = level+1; + char* tmp_name = NULL; + char* tmp_lang = NULL; + char* tmp_country = NULL; + + while (next_level == level+1) { + ebml_elem_t elem; + + if (!ebml_read_elem_head(ebml, &elem)) + return 0; + + switch (elem.id) { + + case MATROSKA_ID_CH_STRING: + tmp_name = ebml_alloc_read_ascii(ebml, &elem); + break; + + case MATROSKA_ID_CH_LANGUAGE: + tmp_lang = ebml_alloc_read_ascii(ebml, &elem); + break; + + case MATROSKA_ID_CH_COUNTRY: + tmp_country = ebml_alloc_read_ascii(ebml, &elem); + break; + + default: + lprintf("Unhandled ID (inside ChapterDisplay): 0x%x\n", elem.id); + if (!ebml_skip(ebml, &elem)) + return 0; + } + + next_level = ebml_get_next_level(ebml, &elem); + } + + if (NULL != chap->title) { + chap->title = tmp_name; + + free(chap->language); + chap->language = tmp_lang; + + free(chap->country); + chap->country = tmp_country; + } else if (tmp_lang != NULL && !strcmp("eng", tmp_lang) && (chap->language == NULL || strcmp("eng", chap->language))) { + free(chap->title); + chap->title = tmp_name; + + free(chap->language); + chap->language = tmp_lang; + + free(chap->country); + chap->country = tmp_country; + } else { + free(tmp_name); + free(tmp_lang); + free(tmp_country); + } + + return 1; +} + +static int parse_chapter_atom(demux_matroska_t *this, matroska_chapter_t *chap, int level) { + ebml_parser_t *ebml = this->ebml; + int next_level = level+1; + uint64_t num; + + chap->time_start = 0; + chap->time_end = 0; + chap->hidden = 0; + chap->enabled = 1; + + while (next_level == level+1) { + ebml_elem_t elem; + + if (!ebml_read_elem_head(ebml, &elem)) { + lprintf("invalid head\n"); + return 0; + } + + switch (elem.id) { + case MATROSKA_ID_CH_UID: + if (!ebml_read_uint(ebml, &elem, &chap->uid)) { + lprintf("invalid UID\n"); + return 0; + } + break; + + case MATROSKA_ID_CH_TIMESTART: + if (!ebml_read_uint(ebml, &elem, &chap->time_start)) { + lprintf("invalid start time\n"); + return 0; + } + /* convert to xine timing: Matroska timestamps are in nanoseconds, + * xine's PTS are in 1/90,000s */ + chap->time_start /= 100000; + chap->time_start *= 9; + break; + + case MATROSKA_ID_CH_TIMEEND: + if (!ebml_read_uint(ebml, &elem, &chap->time_end)) { + lprintf("invalid end time\n"); + return 0; + } + /* convert to xine timing */ + chap->time_end /= 100000; + chap->time_end *= 9; + break; + + case MATROSKA_ID_CH_DISPLAY: + if (!ebml_read_master(ebml, &elem)) + return 0; + + lprintf("ChapterDisplay\n"); + if(!parse_chapter_display(this, chap, level+1)) { + lprintf("invalid display information\n"); + return 0; + } + break; + + case MATROSKA_ID_CH_HIDDEN: + if (!ebml_read_uint(ebml, &elem, &num)) + return 0; + chap->hidden = (int)num; + break; + + case MATROSKA_ID_CH_ENABLED: + if (!ebml_read_uint(ebml, &elem, &num)) + return 0; + chap->enabled = (int)num; + break; + + case MATROSKA_ID_CH_ATOM: /* TODO */ + xprintf(this->stream->xine, XINE_VERBOSITY_NONE, + LOG_MODULE ": Warning: Nested chapters are not supported, playback may suffer!\n"); + if (!ebml_skip(ebml, &elem)) + return 0; + break; + + case MATROSKA_ID_CH_TRACK: /* TODO */ + xprintf(this->stream->xine, XINE_VERBOSITY_NONE, + LOG_MODULE ": Warning: Specific track information in chapters is not supported, playback may suffer!\n"); + if (!ebml_skip(ebml, &elem)) + return 0; + break; + + default: + lprintf("Unhandled ID (inside ChapterAtom): 0x%x\n", elem.id); + if (!ebml_skip(ebml, &elem)) + return 0; + } + + next_level = ebml_get_next_level(ebml, &elem); + } + + /* fallback information */ + /* FIXME: check allocations! */ + if (NULL == chap->title) { + chap->title = malloc(9); + if (chap->title != NULL) + strncpy(chap->title, "No title", 9); + } + + if (NULL == chap->language) { + chap->language = malloc(4); + if (chap->language != NULL) + strncpy(chap->language, "unk", 4); + } + + if (NULL == chap->country) { + chap->country = malloc(3); + if (chap->country != NULL) + strncpy(chap->country, "XX", 3); + } + + lprintf( "Chapter 0x%" PRIx64 ": %" PRIu64 "-%" PRIu64 "(pts), %s (%s). %shidden, %senabled.\n", + chap->uid, chap->time_start, chap->time_end, + chap->title, chap->language, + (chap->hidden ? "" : "not "), + (chap->enabled ? "" : "not ")); + + return 1; +} + +static void free_chapter(demux_matroska_t *this, matroska_chapter_t *chap) { + free(chap->title); + free(chap->language); + free(chap->country); + + free(chap); +} + +static int parse_edition_entry(demux_matroska_t *this, matroska_edition_t *ed) { + ebml_parser_t *ebml = this->ebml; + int next_level = 3; + uint64_t num; + int i; + + ed->hidden = 0; + ed->is_default = 0; + ed->ordered = 0; + + while (next_level == 3) { + ebml_elem_t elem; + + if (!ebml_read_elem_head(ebml, &elem)) + return 0; + + switch (elem.id) { + case MATROSKA_ID_CH_ED_UID: + if (!ebml_read_uint(ebml, &elem, &ed->uid)) + return 0; + break; + + case MATROSKA_ID_CH_ED_HIDDEN: + if (!ebml_read_uint(ebml, &elem, &num)) + return 0; + ed->hidden = (int)num; + break; + + case MATROSKA_ID_CH_ED_DEFAULT: + if (!ebml_read_uint(ebml, &elem, &num)) + return 0; + ed->is_default = (int)num; + break; + + case MATROSKA_ID_CH_ED_ORDERED: + if (!ebml_read_uint(ebml, &elem, &num)) + return 0; + ed->ordered = (int)num; + break; + + case MATROSKA_ID_CH_ATOM: + { + matroska_chapter_t *chapter = calloc(1, sizeof(matroska_chapter_t)); + if (NULL == chapter) + return 0; + + lprintf("ChapterAtom\n"); + if (!ebml_read_master(ebml, &elem)) + return 0; + + if (!parse_chapter_atom(this, chapter, next_level)) + return 0; + + /* resize chapters array if necessary */ + if (ed->num_chapters >= ed->cap_chapters) { + matroska_chapter_t** old_chapters = ed->chapters; + ed->cap_chapters += 10; + ed->chapters = realloc(ed->chapters, ed->cap_chapters * sizeof(matroska_chapter_t*)); + + if (NULL == ed->chapters) { + ed->chapters = old_chapters; + ed->cap_chapters -= 10; + return 0; + } + } + + ed->chapters[ed->num_chapters] = chapter; + ++ed->num_chapters; + + break; + } + + default: + lprintf("Unhandled ID (inside EditionEntry): 0x%x\n", elem.id); + if (!ebml_skip(ebml, &elem)) + return 0; + } + + next_level = ebml_get_next_level(ebml, &elem); + } + + xprintf( this->stream->xine, XINE_VERBOSITY_LOG, + LOG_MODULE ": Edition 0x%" PRIx64 ": %shidden, %sdefault, %sordered. %d chapters:\n", + ed->uid, + (ed->hidden ? "" : "not "), + (ed->is_default ? "" : "not "), + (ed->ordered ? "" : "not "), + ed->num_chapters ); + + for (i=0; i<ed->num_chapters; ++i) { + matroska_chapter_t* chap = ed->chapters[i]; + xprintf( this->stream->xine, XINE_VERBOSITY_LOG, + LOG_MODULE ": Chapter %d: %" PRIu64 "-%" PRIu64 "(pts), %s (%s). %shidden, %senabled.\n", + i+1, chap->time_start, chap->time_end, + chap->title, chap->language, + (chap->hidden ? "" : "not "), + (chap->enabled ? "" : "not ")); + } + + return 1; +} + +static void free_edition(demux_matroska_t *this, matroska_edition_t *ed) { + int i; + + for(i=0; i<ed->num_chapters; ++i) { + free_chapter(this, ed->chapters[i]); + } + free(ed->chapters); + free(ed); +} + +int matroska_parse_chapters(demux_matroska_t *this) { + ebml_parser_t *ebml = this->ebml; + int next_level = 2; + + while (next_level == 2) { + ebml_elem_t elem; + + if (!ebml_read_elem_head(ebml, &elem)) + return 0; + + switch (elem.id) { + case MATROSKA_ID_CH_EDITIONENTRY: + { + matroska_edition_t *edition = calloc(1, sizeof(matroska_edition_t)); + if (NULL == edition) + return 0; + + lprintf("EditionEntry\n"); + if (!ebml_read_master(ebml, &elem)) + return 0; + + if (!parse_edition_entry(this, edition)) + return 0; + + /* resize editions array if necessary */ + if (this->num_editions >= this->cap_editions) { + matroska_edition_t** old_editions = this->editions; + this->cap_editions += 10; + this->editions = realloc(this->editions, this->cap_editions * sizeof(matroska_edition_t*)); + + if (NULL == this->editions) { + this->editions = old_editions; + this->cap_editions -= 10; + return 0; + } + } + + this->editions[this->num_editions] = edition; + ++this->num_editions; + + break; + } + + default: + lprintf("Unhandled ID: 0x%x\n", elem.id); + if (!ebml_skip(ebml, &elem)) + return 0; + } + + next_level = ebml_get_next_level(ebml, &elem); + } + + return 1; +} + +void matroska_free_editions(demux_matroska_t *this) { + int i; + + for(i=0; i<this->num_editions; ++i) { + free_edition(this, this->editions[i]); + } + free(this->editions); + this->num_editions = 0; + this->cap_editions = 0; +} + +int matroska_get_chapter(demux_matroska_t *this, uint64_t tc, matroska_edition_t** ed) { + uint64_t block_pts = (tc * this->timecode_scale) / 100000 * 9; + int chapter_idx = 0; + + if (this->num_editions < 1) + return -1; + + while (chapter_idx < (*ed)->num_chapters && block_pts > (*ed)->chapters[chapter_idx]->time_start) + ++chapter_idx; + + if (chapter_idx > 0) + --chapter_idx; + + return chapter_idx; +} diff --git a/src/demuxers/demux_matroska.c b/src/demuxers/demux_matroska.c index 829581925..6106bf83d 100644 --- a/src/demuxers/demux_matroska.c +++ b/src/demuxers/demux_matroska.c @@ -42,6 +42,7 @@ /* #define LOG */ + #include <xine/xine_internal.h> #include <xine/xineutils.h> #include <xine/demux.h> @@ -50,89 +51,7 @@ #include "ebml.h" #include "matroska.h" - -#define NUM_PREVIEW_BUFFERS 10 - -#define MAX_STREAMS 128 -#define MAX_FRAMES 32 - -#define WRAP_THRESHOLD 90000 - -typedef struct { - int track_num; - off_t *pos; - uint64_t *timecode; - int num_entries; - -} matroska_index_t; - -typedef struct { - - demux_plugin_t demux_plugin; - - xine_stream_t *stream; - - input_plugin_t *input; - - int status; - - ebml_parser_t *ebml; - - /* segment element */ - ebml_elem_t segment; - uint64_t timecode_scale; - int duration; /* in millis */ - int preview_sent; - int preview_mode; - - /* meta seek info */ - int has_seekhead; - int seekhead_handled; - - /* seek info */ - matroska_index_t *indexes; - int num_indexes; - int first_cluster_found; - int skip_to_timecode; - int skip_for_track; - - /* tracks */ - int num_tracks; - int num_video_tracks; - int num_audio_tracks; - int num_sub_tracks; - - matroska_track_t *tracks[MAX_STREAMS]; - - /* block */ - uint8_t *block_data; - size_t block_data_size; - - /* current tracks */ - matroska_track_t *video_track; /* to remove */ - matroska_track_t *audio_track; /* to remove */ - matroska_track_t *sub_track; /* to remove */ - - int send_newpts; - int buf_flag_seek; - - /* seekhead parsing */ - int top_level_list_size; - int top_level_list_max_size; - off_t *top_level_list; - -} demux_matroska_t ; - -typedef struct { - - demux_class_t demux_class; - - /* class-wide, global variables here */ - - xine_t *xine; - -} demux_matroska_class_t; - +#include "demux_matroska.h" static void check_newpts (demux_matroska_t *this, int64_t pts, matroska_track_t *track) { @@ -206,10 +125,10 @@ static int parse_info(demux_matroska_t *this) { ebml_parser_t *ebml = this->ebml; int next_level = 2; double duration = 0.0; /* in matroska unit */ - + while (next_level == 2) { ebml_elem_t elem; - + if (!ebml_read_elem_head(ebml, &elem)) return 0; @@ -219,14 +138,22 @@ static int parse_info(demux_matroska_t *this) { if (!ebml_read_uint(ebml, &elem, &this->timecode_scale)) return 0; break; - case MATROSKA_ID_I_DURATION: { - + + case MATROSKA_ID_I_DURATION: lprintf("duration\n"); if (!ebml_read_float(ebml, &elem, &duration)) return 0; - } - break; - + break; + + case MATROSKA_ID_I_TITLE: + lprintf("title\n"); + if (NULL != this->title) + free(this->title); + + this->title = ebml_alloc_read_ascii(ebml, &elem); + _x_meta_info_set_utf8(this->stream, XINE_META_INFO_TITLE, this->title); + break; + default: lprintf("Unhandled ID: 0x%x\n", elem.id); if (!ebml_skip(ebml, &elem)) @@ -240,6 +167,8 @@ static int parse_info(demux_matroska_t *this) { this->duration = (int)(duration * (double)this->timecode_scale / 1000000.0); lprintf("timecode_scale: %" PRId64 "\n", this->timecode_scale); lprintf("duration: %d\n", this->duration); + lprintf("title: %s\n", (NULL != this->title ? this->title : "(none)")); + return 1; } @@ -607,6 +536,8 @@ static void init_codec_xiph(demux_matroska_t *this, matroska_track_t *track) { int i; uint8_t *data; + if (track->codec_private_len < 3) + return; nb_lace = track->codec_private[0]; if (nb_lace != 2) return; @@ -614,6 +545,8 @@ static void init_codec_xiph(demux_matroska_t *this, matroska_track_t *track) { frame[0] = track->codec_private[1]; frame[1] = track->codec_private[2]; frame[2] = track->codec_private_len - frame[0] - frame[1] - 3; + if (frame[2] < 0) + return; data = track->codec_private + 3; for (i = 0; i < 3; i++) { @@ -1147,67 +1080,72 @@ static void handle_vobsub (demux_plugin_t *this_gen, matroska_track_t *track, static int parse_track_entry(demux_matroska_t *this, matroska_track_t *track) { ebml_parser_t *ebml = this->ebml; int next_level = 3; - + while (next_level == 3) { ebml_elem_t elem; - + if (!ebml_read_elem_head(ebml, &elem)) return 0; switch (elem.id) { - case MATROSKA_ID_TR_NUMBER: { - uint64_t num; - lprintf("TrackNumber\n"); - if (!ebml_read_uint(ebml, &elem, &num)) - return 0; - track->track_num = num; - } - break; - - case MATROSKA_ID_TR_TYPE: { - uint64_t num; - lprintf("TrackType\n"); - if (!ebml_read_uint(ebml, &elem, &num)) - return 0; - track->track_type = num; - } - break; - - case MATROSKA_ID_TR_CODECID: { - char *codec_id = ebml_alloc_read_ascii (ebml, &elem); - lprintf("CodecID\n"); - if (!codec_id) - return 0; - track->codec_id = codec_id; - } - break; - - case MATROSKA_ID_TR_CODECPRIVATE: { - uint8_t *codec_private; - if (elem.len >= 0x80000000) - return 0; - codec_private = malloc (elem.len); - if (! codec_private) - return 0; - lprintf("CodecPrivate\n"); - if (!ebml_read_binary(ebml, &elem, codec_private)) { - free(codec_private); - return 0; - } - track->codec_private = codec_private; - track->codec_private_len = elem.len; - } - break; - - case MATROSKA_ID_TR_LANGUAGE: { - char *language = ebml_alloc_read_ascii (ebml, &elem); - lprintf("Language\n"); - if (!language) - return 0; - track->language = language; - } - break; - + case MATROSKA_ID_TR_NUMBER: + { + uint64_t num; + lprintf("TrackNumber\n"); + if (!ebml_read_uint(ebml, &elem, &num)) + return 0; + track->track_num = num; + } + break; + + case MATROSKA_ID_TR_TYPE: + { + uint64_t num; + lprintf("TrackType\n"); + if (!ebml_read_uint(ebml, &elem, &num)) + return 0; + track->track_type = num; + } + break; + + case MATROSKA_ID_TR_CODECID: + { + char *codec_id = ebml_alloc_read_ascii (ebml, &elem); + lprintf("CodecID\n"); + if (!codec_id) + return 0; + track->codec_id = codec_id; + } + break; + + case MATROSKA_ID_TR_CODECPRIVATE: + { + uint8_t *codec_private; + if (elem.len >= 0x80000000) + return 0; + codec_private = malloc (elem.len); + if (! codec_private) + return 0; + lprintf("CodecPrivate\n"); + if (!ebml_read_binary(ebml, &elem, codec_private)) { + free(codec_private); + return 0; + } + track->codec_private = codec_private; + track->codec_private_len = elem.len; + } + break; + + case MATROSKA_ID_TR_LANGUAGE: + { + char *language = ebml_alloc_read_ascii (ebml, &elem); + lprintf("Language\n"); + if (!language) + return 0; + track->language = language; + } + break; + case MATROSKA_ID_TV: lprintf("Video\n"); if (track->video_track) @@ -1217,8 +1155,8 @@ static int parse_track_entry(demux_matroska_t *this, matroska_track_t *track) { return 0; if ((elem.len > 0) && !parse_video_track(this, track->video_track)) return 0; - break; - + break; + case MATROSKA_ID_TA: lprintf("Audio\n"); if (track->audio_track) @@ -1228,38 +1166,54 @@ static int parse_track_entry(demux_matroska_t *this, matroska_track_t *track) { return 0; if ((elem.len > 0) && !parse_audio_track(this, track->audio_track)) return 0; - break; - - case MATROSKA_ID_TR_FLAGDEFAULT: { - uint64_t val; - - lprintf("Default\n"); - if (!ebml_read_uint(ebml, &elem, &val)) - return 0; - track->default_flag = (int)val; - } - break; + break; - case MATROSKA_ID_TR_DEFAULTDURATION: { - uint64_t val; + case MATROSKA_ID_TR_FLAGDEFAULT: + { + uint64_t val; - if (!ebml_read_uint(ebml, &elem, &val)) - return 0; - track->default_duration = val; - lprintf("Default Duration: %"PRIu64"\n", track->default_duration); - } - break; + lprintf("Default\n"); + if (!ebml_read_uint(ebml, &elem, &val)) + return 0; + track->default_flag = (int)val; + } + break; - case MATROSKA_ID_CONTENTENCODINGS: { - lprintf("ContentEncodings\n"); - if (!ebml_read_master (ebml, &elem)) - return 0; - if ((elem.len > 0) && !parse_content_encodings(this, track)) - return 0; - } - break; + case MATROSKA_ID_TR_DEFAULTDURATION: + { + uint64_t val; + + if (!ebml_read_uint(ebml, &elem, &val)) + return 0; + track->default_duration = val; + lprintf("Default Duration: %"PRIu64"\n", track->default_duration); + } + break; + + case MATROSKA_ID_CONTENTENCODINGS: + { + lprintf("ContentEncodings\n"); + if (!ebml_read_master (ebml, &elem)) + return 0; + if ((elem.len > 0) && !parse_content_encodings(this, track)) + return 0; + } + break; + + case MATROSKA_ID_TR_UID: + { + uint64_t val; + + if (!ebml_read_uint(ebml, &elem, &val)) { + lprintf("Track UID (invalid)\n"); + return 0; + } + + track->uid = val; + lprintf("Track UID: 0x%" PRIx64 "\n", track->uid); + } + break; - case MATROSKA_ID_TR_UID: case MATROSKA_ID_TR_FLAGENABLED: case MATROSKA_ID_TR_FLAGLACING: case MATROSKA_ID_TR_MINCACHE: @@ -1280,7 +1234,7 @@ static int parse_track_entry(demux_matroska_t *this, matroska_track_t *track) { } next_level = ebml_get_next_level(ebml, &elem); } - + xprintf(this->stream->xine, XINE_VERBOSITY_LOG, "demux_matroska: Track %d, %s %s\n", track->track_num, @@ -1308,8 +1262,11 @@ static int parse_track_entry(demux_matroska_t *this, matroska_track_t *track) { (!strcmp(track->codec_id, MATROSKA_CODEC_ID_V_MPEG4_ASP)) || (!strcmp(track->codec_id, MATROSKA_CODEC_ID_V_MPEG4_AP))) { xine_bmiheader *bih; - + lprintf("MATROSKA_CODEC_ID_V_MPEG4_*\n"); + if (track->codec_private_len > 0x7fffffff - sizeof(xine_bmiheader)) + track->codec_private_len = 0x7fffffff - sizeof(xine_bmiheader); + /* create a bitmap info header struct for MPEG 4 */ bih = calloc(1, sizeof(xine_bmiheader) + track->codec_private_len); bih->biSize = sizeof(xine_bmiheader) + track->codec_private_len; @@ -1317,20 +1274,23 @@ static int parse_track_entry(demux_matroska_t *this, matroska_track_t *track) { bih->biWidth = track->video_track->pixel_width; bih->biHeight = track->video_track->pixel_height; _x_bmiheader_le2me(bih); - + /* add bih extra data */ memcpy(bih + 1, track->codec_private, track->codec_private_len); free(track->codec_private); track->codec_private = (uint8_t *)bih; track->codec_private_len = bih->biSize; track->buf_type = BUF_VIDEO_MPEG4; - + /* init as a vfw decoder */ init_codec = init_codec_video; } else if (!strcmp(track->codec_id, MATROSKA_CODEC_ID_V_MPEG4_AVC)) { xine_bmiheader *bih; - + lprintf("MATROSKA_CODEC_ID_V_MPEG4_AVC\n"); + if (track->codec_private_len > 0x7fffffff - sizeof(xine_bmiheader)) + track->codec_private_len = 0x7fffffff - sizeof(xine_bmiheader); + /* create a bitmap info header struct for h264 */ bih = calloc(1, sizeof(xine_bmiheader) + track->codec_private_len); bih->biSize = sizeof(xine_bmiheader) + track->codec_private_len; @@ -1338,14 +1298,14 @@ static int parse_track_entry(demux_matroska_t *this, matroska_track_t *track) { bih->biWidth = track->video_track->pixel_width; bih->biHeight = track->video_track->pixel_height; _x_bmiheader_le2me(bih); - + /* add bih extra data */ memcpy(bih + 1, track->codec_private, track->codec_private_len); free(track->codec_private); track->codec_private = (uint8_t *)bih; track->codec_private_len = bih->biSize; track->buf_type = BUF_VIDEO_H264; - + /* init as a vfw decoder */ init_codec = init_codec_video; } else if (!strcmp(track->codec_id, MATROSKA_CODEC_ID_V_MSMPEG4V3)) { @@ -1366,7 +1326,7 @@ static int parse_track_entry(demux_matroska_t *this, matroska_track_t *track) { track->handle_content = handle_realvideo; init_codec = init_codec_real; } else if (!strcmp(track->codec_id, MATROSKA_CODEC_ID_V_REAL_RV40)) { - + lprintf("MATROSKA_CODEC_ID_V_REAL_RV40\n"); track->buf_type = BUF_VIDEO_RV40; track->handle_content = handle_realvideo; @@ -1391,7 +1351,7 @@ static int parse_track_entry(demux_matroska_t *this, matroska_track_t *track) { lprintf("MATROSKA_CODEC_ID_A_AC3\n"); track->buf_type = BUF_AUDIO_A52; init_codec = init_codec_audio; - + } else if (!strcmp(track->codec_id, MATROSKA_CODEC_ID_A_DTS)) { lprintf("MATROSKA_CODEC_ID_A_DTS\n"); track->buf_type = BUF_AUDIO_DTS; @@ -1437,17 +1397,17 @@ static int parse_track_entry(demux_matroska_t *this, matroska_track_t *track) { track->buf_type = BUF_AUDIO_ATRK; init_codec = init_codec_real; } else if (!strcmp(track->codec_id, MATROSKA_CODEC_ID_S_TEXT_UTF8) || - !strcmp(track->codec_id, MATROSKA_CODEC_ID_S_UTF8)) { + !strcmp(track->codec_id, MATROSKA_CODEC_ID_S_UTF8)) { lprintf("MATROSKA_CODEC_ID_S_TEXT_UTF8\n"); track->buf_type = BUF_SPU_OGM; track->handle_content = handle_sub_utf8; } else if (!strcmp(track->codec_id, MATROSKA_CODEC_ID_S_TEXT_SSA) || - !strcmp(track->codec_id, MATROSKA_CODEC_ID_S_SSA)) { + !strcmp(track->codec_id, MATROSKA_CODEC_ID_S_SSA)) { lprintf("MATROSKA_CODEC_ID_S_TEXT_SSA\n"); track->buf_type = BUF_SPU_OGM; track->handle_content = handle_sub_ssa; } else if (!strcmp(track->codec_id, MATROSKA_CODEC_ID_S_TEXT_ASS) || - !strcmp(track->codec_id, MATROSKA_CODEC_ID_S_ASS)) { + !strcmp(track->codec_id, MATROSKA_CODEC_ID_S_ASS)) { lprintf("MATROSKA_CODEC_ID_S_TEXT_ASS\n"); track->buf_type = BUF_SPU_OGM; track->handle_content = handle_sub_ssa; @@ -1460,7 +1420,7 @@ static int parse_track_entry(demux_matroska_t *this, matroska_track_t *track) { track->buf_type = BUF_SPU_DVD; track->handle_content = handle_vobsub; init_codec = init_codec_vobsub; - + /* Enable autodetection of the zlib compression, unless it was * explicitely set. Most vobsubs are compressed with zlib but * are not declared as such. @@ -1506,7 +1466,7 @@ static int parse_track_entry(demux_matroska_t *this, matroska_track_t *track) { } } } - + return 1; } @@ -1525,6 +1485,12 @@ static int parse_tracks(demux_matroska_t *this) { case MATROSKA_ID_TR_ENTRY: { matroska_track_t *track; + /* bail out early if no more tracks can be handled! */ + if (this->num_tracks >= MAX_STREAMS) { + lprintf("Too many tracks!\n"); + return 0; + } + /* alloc and initialize a track with 0 */ track = calloc(1, sizeof(matroska_track_t)); track->compress_algo = MATROSKA_COMPRESS_NONE; @@ -1538,29 +1504,7 @@ static int parse_tracks(demux_matroska_t *this) { this->num_tracks++; } break; - - default: - lprintf("Unhandled ID: 0x%x\n", elem.id); - if (!ebml_skip(ebml, &elem)) - return 0; - } - next_level = ebml_get_next_level(ebml, &elem); - } - return 1; -} - -static int parse_chapters(demux_matroska_t *this) { - ebml_parser_t *ebml = this->ebml; - int next_level = 2; - - while (next_level == 2) { - ebml_elem_t elem; - - if (!ebml_read_elem_head(ebml, &elem)) - return 0; - - switch (elem.id) { default: lprintf("Unhandled ID: 0x%x\n", elem.id); if (!ebml_skip(ebml, &elem)) @@ -1571,7 +1515,6 @@ static int parse_chapters(demux_matroska_t *this) { return 1; } - static int parse_cue_trackposition(demux_matroska_t *this, int *track_num, int64_t *pos) { ebml_parser_t *ebml = this->ebml; @@ -1826,6 +1769,11 @@ static int read_block_data (demux_matroska_t *this, size_t len) { alloc_block_data(this, len); /* block datas */ + if (! this->block_data) { + xprintf(this->stream->xine, XINE_VERBOSITY_LOG, + "demux_matroska: memory allocation error\n"); + return 0; + } if (this->input->read(this->input, this->block_data, len) != len) { off_t pos = this->input->get_current_pos(this->input); xprintf(this->stream->xine, XINE_VERBOSITY_LOG, @@ -2151,6 +2099,48 @@ static int parse_block_group(demux_matroska_t *this, return 1; } +static int demux_matroska_seek (demux_plugin_t*, off_t, int, int); + +static void handle_events(demux_matroska_t *this) { + xine_event_t* event; + + while ((event = xine_event_get(this->event_queue))) { + if (this->num_editions > 0) { + matroska_edition_t* ed = this->editions[0]; + int chapter_idx = matroska_get_chapter(this, this->last_timecode, &ed); + uint64_t next_time; + + if (chapter_idx < 0) { + xine_event_free(event); + continue; + } + + switch(event->type) { + case XINE_EVENT_INPUT_NEXT: + if (chapter_idx < ed->num_chapters-1) { + next_time = ed->chapters[chapter_idx+1]->time_start / 90; + demux_matroska_seek((demux_plugin_t*)this, 0, next_time, 1); + } + break; + + /* TODO: should this try to implement common "start of chapter" + * functionality? */ + case XINE_EVENT_INPUT_PREVIOUS: + if (chapter_idx > 0) { + next_time = ed->chapters[chapter_idx-1]->time_start / 90; + demux_matroska_seek((demux_plugin_t*)this, 0, next_time, 1); + } + break; + + default: + break; + } + } + + xine_event_free(event); + } +} + static int parse_cluster(demux_matroska_t *this) { ebml_parser_t *ebml = this->ebml; int this_level = ebml->level; @@ -2171,6 +2161,8 @@ static int parse_cluster(demux_matroska_t *this) { this->first_cluster_found = 1; } + handle_events(this); + while (next_level == this_level) { ebml_elem_t elem; @@ -2212,6 +2204,49 @@ static int parse_cluster(demux_matroska_t *this) { } next_level = ebml_get_next_level(ebml, &elem); } + + /* at this point, we MUST have a timecode (according to format spec). + * Use that to find the chapter we are in, and adjust the title. + * + * TODO: this only looks at the chapters in the first edition. + */ + + this->last_timecode = timecode; + + if (this->num_editions <= 0) + return 1; + matroska_edition_t *ed = this->editions[0]; + + if (ed->num_chapters <= 0) + return 1; + + /* fix up a makeshift title if none has been set yet (e.g. filename) */ + if (NULL == this->title && NULL != _x_meta_info_get(this->stream, XINE_META_INFO_TITLE)) + this->title = strdup(_x_meta_info_get(this->stream, XINE_META_INFO_TITLE)); + + if (NULL == this->title) + this->title = strdup("(No title)"); + + if (NULL == this->title) { + lprintf("Failed to determine a valid stream title!\n"); + return 1; + } + + int chapter_idx = matroska_get_chapter(this, timecode, &ed); + if (chapter_idx < 0) { + _x_meta_info_set_utf8(this->stream, XINE_META_INFO_TITLE, this->title); + return 1; + } + + xine_ui_data_t uidata = { + .str = {0, }, + .str_len = 0, + }; + + uidata.str_len = snprintf(uidata.str, sizeof(uidata.str), "%s / (%d) %s", + this->title, chapter_idx+1, ed->chapters[chapter_idx]->title); + _x_meta_info_set_utf8(this->stream, XINE_META_INFO_TITLE, uidata.str); + return 1; } @@ -2385,7 +2420,7 @@ static int parse_top_level_head(demux_matroska_t *this, int *next_level) { lprintf("Chapters\n"); if (!ebml_read_master (ebml, &elem)) return 0; - if ((elem.len > 0) && !parse_chapters(this)) + if ((elem.len > 0) && !matroska_parse_chapters(this)) return 0; break; case MATROSKA_ID_CLUSTER: @@ -2749,6 +2784,8 @@ static void demux_matroska_dispose (demux_plugin_t *this_gen) { demux_matroska_t *this = (demux_matroska_t *) this_gen; int i; + free(this->block_data); + /* free tracks */ for (i = 0; i < this->num_tracks; i++) { matroska_track_t *const track = this->tracks[i]; @@ -2772,7 +2809,12 @@ static void demux_matroska_dispose (demux_plugin_t *this_gen) { /* Free the top_level elem list */ free(this->top_level_list); + free(this->title); + + matroska_free_editions(this); + dispose_ebml_parser(this->ebml); + xine_event_dispose_queue(this->event_queue); free (this); } @@ -2786,7 +2828,13 @@ static int demux_matroska_get_stream_length (demux_plugin_t *this_gen) { static uint32_t demux_matroska_get_capabilities (demux_plugin_t *this_gen) { - return DEMUX_CAP_SPULANG | DEMUX_CAP_AUDIOLANG; + demux_matroska_t* this = (demux_matroska_t*)this_gen; + uint32_t caps = DEMUX_CAP_SPULANG | DEMUX_CAP_AUDIOLANG; + + if(this->num_editions > 0 && this->editions[0]->num_chapters > 0) + caps |= DEMUX_CAP_CHAPTERS; + + return caps; } @@ -2906,11 +2954,18 @@ static demux_plugin_t *open_plugin (demux_class_t *class_gen, xine_stream_t *str if (strcmp(ebml->doctype, "matroska")) goto error; + this->event_queue = xine_event_new_queue(this->stream); + return &this->demux_plugin; error: dispose_ebml_parser(ebml); - free(this); + + if (NULL != this) { + xine_event_dispose_queue(this->event_queue); + free(this); + } + return NULL; } diff --git a/src/demuxers/demux_matroska.h b/src/demuxers/demux_matroska.h new file mode 100644 index 000000000..d8c3840b1 --- /dev/null +++ b/src/demuxers/demux_matroska.h @@ -0,0 +1,150 @@ +/* + * Copyright (C) 2000-2008 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA + * + * demultiplexer for matroska streams: shared header + */ + +#ifndef _DEMUX_MATROSKA_H_ +#define _DEMUX_MATROSKA_H_ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <ctype.h> +#include <stdio.h> +#include <fcntl.h> +#include <unistd.h> +#include <string.h> +#include <stdlib.h> +#include <zlib.h> + +#include <xine/xine_internal.h> +#include <xine/demux.h> +#include <xine/buffer.h> +#include "bswap.h" + +#include "ebml.h" +#include "matroska.h" + +#define NUM_PREVIEW_BUFFERS 10 + +#define MAX_STREAMS 128 +#define MAX_FRAMES 32 + +#define WRAP_THRESHOLD 90000 + +typedef struct { + int track_num; + off_t *pos; + uint64_t *timecode; + int num_entries; + +} matroska_index_t; + +typedef struct { + + demux_plugin_t demux_plugin; + + xine_stream_t *stream; + + input_plugin_t *input; + + int status; + + ebml_parser_t *ebml; + + /* segment element */ + ebml_elem_t segment; + uint64_t timecode_scale; + int duration; /* in millis */ + int preview_sent; + int preview_mode; + char *title; + + /* meta seek info */ + int has_seekhead; + int seekhead_handled; + + /* seek info */ + matroska_index_t *indexes; + int num_indexes; + int first_cluster_found; + int skip_to_timecode; + int skip_for_track; + + /* tracks */ + int num_tracks; + int num_video_tracks; + int num_audio_tracks; + int num_sub_tracks; + + matroska_track_t *tracks[MAX_STREAMS]; + + /* maintain editions, number and capacity */ + int num_editions, cap_editions; + matroska_edition_t **editions; + + /* block */ + uint8_t *block_data; + size_t block_data_size; + + /* current tracks */ + matroska_track_t *video_track; /* to remove */ + matroska_track_t *audio_track; /* to remove */ + matroska_track_t *sub_track; /* to remove */ + uint64_t last_timecode; + + int send_newpts; + int buf_flag_seek; + + /* seekhead parsing */ + int top_level_list_size; + int top_level_list_max_size; + off_t *top_level_list; + + /* event handling (chapter navigation) */ + xine_event_queue_t *event_queue; +} demux_matroska_t ; + +typedef struct { + + demux_class_t demux_class; + + /* class-wide, global variables here */ + + xine_t *xine; + +} demux_matroska_class_t; + +/* "entry points" for chapter handling. + * The parser descends into "Chapters" elements at the _parse_ function, + * and editions care about cleanup internally. */ +int matroska_parse_chapters(demux_matroska_t*); +void matroska_free_editions(demux_matroska_t*); + +/* Search an edition for the chapter matching a given timecode. + * + * Return: chapter index, or -1 if none is found. + * + * TODO: does not handle chapter end times yet. + */ +int matroska_get_chapter(demux_matroska_t*, uint64_t, matroska_edition_t**); + +#endif /* _DEMUX_MATROSKA_H_ */ diff --git a/src/demuxers/demux_ts.c b/src/demuxers/demux_ts.c index 0575bcff3..6f5053a37 100644 --- a/src/demuxers/demux_ts.c +++ b/src/demuxers/demux_ts.c @@ -33,6 +33,11 @@ * Date Author * ---- ------ * + * 8-Apr-2009 Petri Hintukainen <phi@sdf-eu.org> + * - support for 192-byte packets (HDMV/BluRay) + * - support for audio inside PES PID 0xfd (HDMV/BluRay) + * - demux HDMV/BluRay bitmap subtitles + * * 28-Nov-2004 Mike Lampard <mlampard> * - Added support for PMT sections larger than 1 ts packet * @@ -179,9 +184,9 @@ #define SYNC_BYTE 0x47 #define MIN_SYNCS 3 -#define NPKT_PER_READ 100 +#define NPKT_PER_READ 96 // 96*188 = 94*192 -#define BUF_SIZE (NPKT_PER_READ * PKT_SIZE) +#define BUF_SIZE (NPKT_PER_READ * (PKT_SIZE + 4)) #define MAX_PES_BUF_SIZE 2048 @@ -225,8 +230,9 @@ ISO_14496_PART2_VIDEO = 0x10, /* ISO/IEC 14496-2 Visual (MPEG-4) */ ISO_14496_PART3_AUDIO = 0x11, /* ISO/IEC 14496-3 Audio with LATM transport syntax */ ISO_14496_PART10_VIDEO = 0x1b, /* ISO/IEC 14496-10 Video (MPEG-4 part 10/AVC, aka H.264) */ - STREAM_VIDEO_MPEG = 0x80, - STREAM_AUDIO_AC3 = 0x81, + STREAM_VIDEO_MPEG = 0x80, + STREAM_AUDIO_AC3 = 0x81, + STREAM_SPU_BITMAP_HDMV = 0x90, } streamType; #define WRAP_THRESHOLD 270000 @@ -234,6 +240,11 @@ #define PTS_AUDIO 0 #define PTS_VIDEO 1 +#undef MIN +#define MIN(a,b) ((a)<(b)?(a):(b)) +#undef MAX +#define MAX(a,b) ((a)>(b)?(a):(b)) + /* ** ** DATA STRUCTURES @@ -309,6 +320,10 @@ typedef struct { int status; + int hdmv; /* -1 = unknown, 0 = mpeg-ts, 1 = hdmv/m2ts */ + int pkt_size; /* TS packet size */ + int pkt_offset; /* TS packet offset */ + int blockSize; int rate; int media_num; @@ -433,12 +448,12 @@ static void check_newpts( demux_ts_t *this, int64_t pts, int video ) } /* Send a BUF_SPU_DVB to let xine know of that channel. */ -static void demux_send_special_spu_buf( demux_ts_t *this, int spu_channel ) +static void demux_send_special_spu_buf( demux_ts_t *this, uint32_t spu_type, int spu_channel ) { buf_element_t *buf; buf = this->video_fifo->buffer_pool_alloc( this->video_fifo ); - buf->type = BUF_SPU_DVB|spu_channel; + buf->type = spu_type|spu_channel; buf->content = buf->mem; buf->size = 0; this->video_fifo->put( this->video_fifo, buf ); @@ -491,6 +506,10 @@ static void demux_ts_update_spu_channel(demux_ts_t *this) #endif } + if ((this->media[this->spu_media].type & BUF_MAJOR_MASK) == BUF_SPU_HDMV) { + buf->type = BUF_SPU_HDMV; + } + this->video_fifo->put(this->video_fifo, buf); } @@ -727,7 +746,18 @@ static int demux_ts_parse_pes_header (xine_t *xine, demux_ts_media *m, p += header_len + 9; packet_len -= header_len + 3; - if (stream_id == 0xbd) { + if (m->descriptor_tag == STREAM_SPU_BITMAP_HDMV) { + long payload_len = ((buf[4] << 8) | buf[5]) - header_len - 3; + + m->content = p; + m->size = packet_len; + m->type |= BUF_SPU_HDMV; + m->buf->decoder_info[2] = payload_len; + return 1; + + } else + + if (stream_id == 0xbd || stream_id == 0xfd /* HDMV */) { int spu_id; @@ -1399,7 +1429,7 @@ printf("Program Number is %i, looking for %i\n",program_number,this->program_num lang->media_index = this->media_num; this->media[this->media_num].type = no; demux_ts_pes_new(this, this->media_num, pid, this->video_fifo, stream[0]); - demux_send_special_spu_buf( this, no ); + demux_send_special_spu_buf( this, BUF_SPU_DVB, no ); #ifdef TS_LOG printf("demux_ts: DVBSUB: pid 0x%.4x: %s page %ld %ld type %2.2x\n", pid, lang->desc.lang, @@ -1412,6 +1442,29 @@ printf("Program Number is %i, looking for %i\n",program_number,this->program_num } break; + case STREAM_SPU_BITMAP_HDMV: + if (this->hdmv > 0) { + if (pid >= 0x1200 && pid < 0x1300) { + /* HDMV Presentation Graphics / SPU */ + demux_ts_spu_lang *lang = &this->spu_langs[this->spu_langs_count]; + + memset(lang->desc.lang, 0, sizeof(lang->desc.lang)); + /*memcpy(lang->desc.lang, &stream[pos], 3);*/ + /*lang->desc.lang[3] = 0;*/ + lang->pid = pid; + lang->media_index = this->media_num; + this->media[this->media_num].type = this->spu_langs_count; + demux_ts_pes_new(this, this->media_num, pid, this->video_fifo, stream[0]); + demux_send_special_spu_buf( this, BUF_SPU_HDMV, this->spu_langs_count ); + this->spu_langs_count++; +#ifdef TS_PMT_LOG + printf("demux_ts: HDMV subtitle stream_type: 0x%.2x pid: 0x%.4x\n", + stream[0], pid); +#endif + break; + } + } + /* fall thru */ default: /* This following section handles all the cases where the audio track info is stored in PMT user info with stream id >= 0x80 @@ -1498,10 +1551,10 @@ static int sync_correct(demux_ts_t*this, uint8_t *buf, int32_t npkt_read) { xprintf (this->stream->xine, XINE_VERBOSITY_DEBUG, "demux_ts: about to resync!\n"); for (p=0; p < npkt_read; p++) { - for(n=0; n < PKT_SIZE; n++) { + for(n=0; n < this->pkt_size; n++) { sync_ok = 1; for (i=0; i < MIN(MIN_SYNCS, npkt_read - p); i++) { - if (buf[n + ((i+p) * PKT_SIZE)] != SYNC_BYTE) { + if (buf[this->pkt_offset + n + ((i+p) * this->pkt_size)] != SYNC_BYTE) { sync_ok = 0; break; } @@ -1513,13 +1566,13 @@ static int sync_correct(demux_ts_t*this, uint8_t *buf, int32_t npkt_read) { if (sync_ok) { /* Found sync, fill in */ - memmove(&buf[0], &buf[n + p * PKT_SIZE], - ((PKT_SIZE * (npkt_read - p)) - n)); + memmove(&buf[0], &buf[n + p * this->pkt_size], + ((this->pkt_size * (npkt_read - p)) - n)); read_length = this->input->read(this->input, - &buf[(PKT_SIZE * (npkt_read - p)) - n], - n + p * PKT_SIZE); + &buf[(this->pkt_size * (npkt_read - p)) - n], + n + p * this->pkt_size); /* FIXME: when read_length is not as required... we now stop demuxing */ - if (read_length != (n + p * PKT_SIZE)) { + if (read_length != (n + p * this->pkt_size)) { xprintf (this->stream->xine, XINE_VERBOSITY_DEBUG, "demux_ts_tsync_correct: sync found, but read failed\n"); return 0; @@ -1538,6 +1591,32 @@ static int sync_detect(demux_ts_t*this, uint8_t *buf, int32_t npkt_read) { sync_ok = 1; + if (this->hdmv) { + this->pkt_size = PKT_SIZE + 4; + this->pkt_offset = 4; + for (i=0; i < MIN(MIN_SYNCS, npkt_read - 3); i++) { + if (buf[this->pkt_offset + i * this->pkt_size] != SYNC_BYTE) { + sync_ok = 0; + break; + } + } + if (sync_ok) { + if (this->hdmv < 0) { + /* fix npkt_read (packet size is 192, not 188) */ + this->npkt_read = npkt_read * PKT_SIZE / this->pkt_size; + } + this->hdmv = 1; + return sync_ok; + } + if (this->hdmv > 0) + return sync_correct(this, buf, npkt_read); + + /* plain ts */ + this->hdmv = 0; + this->pkt_size = PKT_SIZE; + this->pkt_offset = 0; + } + for (i=0; i < MIN(MIN_SYNCS, npkt_read); i++) { if (buf[i * PKT_SIZE] != SYNC_BYTE) { sync_ok = 0; @@ -1561,15 +1640,15 @@ static unsigned char * demux_synchronise(demux_ts_t* this) { /* NEW: handle read returning less packets than NPKT_PER_READ... */ do { read_length = this->input->read(this->input, this->buf, - PKT_SIZE * NPKT_PER_READ); - if (read_length < 0 || read_length % PKT_SIZE) { + this->pkt_size * NPKT_PER_READ); + if (read_length < 0 || read_length % this->pkt_size) { xprintf (this->stream->xine, XINE_VERBOSITY_DEBUG, "demux_ts: read returned %d bytes (not a multiple of %d!)\n", - read_length, PKT_SIZE); + read_length, this->pkt_size); this->status = DEMUX_FINISHED; return NULL; } - this->npkt_read = read_length / PKT_SIZE; + this->npkt_read = read_length / this->pkt_size; #ifdef TS_READ_STATS this->rstat[this->npkt_read]++; @@ -1596,7 +1675,7 @@ static unsigned char * demux_synchronise(demux_ts_t* this) { return NULL; } } - return_pointer = &(this->buf)[PKT_SIZE * this->packet_number]; + return_pointer = &(this->buf)[this->pkt_offset + this->pkt_size * this->packet_number]; this->packet_number++; return return_pointer; } @@ -1744,7 +1823,7 @@ static void demux_ts_parse_packet (demux_ts_t*this) { /* * Discard packets that are obviously bad. */ - if (sync_byte != 0x47) { + if (sync_byte != SYNC_BYTE) { xprintf (this->stream->xine, XINE_VERBOSITY_DEBUG, "demux error! invalid ts sync byte %.2x\n", sync_byte); return; @@ -2154,6 +2233,32 @@ static int demux_ts_get_optional_data(demux_plugin_t *this_gen, } } +static int detect_ts(uint8_t *buf, size_t len, int ts_size) +{ + int i, j; + int try_again, ts_detected = 0; + size_t packs = len / ts_size - 2; + + for (i = 0; i < ts_size; i++) { + try_again = 0; + if (buf[i] == SYNC_BYTE) { + for (j = 1; j < packs; j++) { + if (buf[i + j*ts_size] != SYNC_BYTE) { + try_again = 1; + break; + } + } + if (try_again == 0) { +#ifdef TS_LOG + printf ("demux_ts: found 0x47 pattern at offset %d\n", i); +#endif + ts_detected = 1; + } + } + } + + return ts_detected; +} static demux_plugin_t *open_plugin (demux_class_t *class_gen, xine_stream_t *stream, @@ -2161,6 +2266,7 @@ static demux_plugin_t *open_plugin (demux_class_t *class_gen, demux_ts_t *this; int i; + int hdmv = -1; switch (stream->content_detection_method) { @@ -2169,30 +2275,14 @@ static demux_plugin_t *open_plugin (demux_class_t *class_gen, int i, j; int try_again, ts_detected; - if (!_x_demux_read_header(input, buf, 2069)) + if (!_x_demux_read_header(input, buf, sizeof(buf))) return NULL; - ts_detected = 0; - - for (i = 0; i < 188; i++) { - try_again = 0; - if (buf[i] == 0x47) { - for (j = 1; j <= 10; j++) { - if (buf[i + j*188] != 0x47) { - try_again = 1; - break; - } - } - if (try_again == 0) { -#ifdef TS_LOG - printf ("demux_ts: found 0x47 pattern at offset %d\n", i); -#endif - ts_detected = 1; - } - } - } - - if (!ts_detected) + if (detect_ts(buf, sizeof(buf), PKT_SIZE)) + hdmv = 0; + else if (detect_ts(buf, sizeof(buf), PKT_SIZE+4)) + hdmv = 1; + else return NULL; } break; @@ -2259,7 +2349,12 @@ static demux_plugin_t *open_plugin (demux_class_t *class_gen, /* dvb */ this->event_queue = xine_event_new_queue (this->stream); - + + /* HDMV */ + this->hdmv = hdmv; + this->pkt_offset = (hdmv > 0) ? 4 : 0; + this->pkt_size = PKT_SIZE + this->pkt_offset; + this->numPreview=0; return &this->demux_plugin; diff --git a/src/demuxers/matroska.h b/src/demuxers/matroska.h index 50583df9c..2c1e15e12 100644 --- a/src/demuxers/matroska.h +++ b/src/demuxers/matroska.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2000-2007 the xine project + * Copyright (C) 2000-2009 the xine project * * This file is part of xine, a free video player. * @@ -16,6 +16,8 @@ * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA + * + * Matroska EBML stream handling */ #ifndef MATROSKA_H #define MATROSKA_H @@ -169,10 +171,16 @@ /* Chapters */ #define MATROSKA_ID_CHAPTERS 0x1043A770 #define MATROSKA_ID_CH_EDITIONENTRY 0x45B9 +#define MATROSKA_ID_CH_ED_UID 0x45BC +#define MATROSKA_ID_CH_ED_HIDDEN 0x45BD +#define MATROSKA_ID_CH_ED_DEFAULT 0x45DB +#define MATROSKA_ID_CH_ED_ORDERED 0x45DD #define MATROSKA_ID_CH_ATOM 0xB6 #define MATROSKA_ID_CH_UID 0x73C4 #define MATROSKA_ID_CH_TIMESTART 0x91 #define MATROSKA_ID_CH_TIMEEND 0x92 +#define MATROSKA_ID_CH_HIDDEN 0x98 +#define MATROSKA_ID_CH_ENABLED 0x4598 #define MATROSKA_ID_CH_TRACK 0x8F #define MATROSKA_ID_CH_TRACKNUMBER 0x89 #define MATROSKA_ID_CH_DISPLAY 0x80 @@ -183,6 +191,46 @@ /* Tags */ #define MATROSKA_ID_TAGS 0x1254C367 +/* Chapter (used in tracks) */ +typedef struct { + uint64_t uid; + uint64_t time_start; + uint64_t time_end; + /* if not 0, the chapter could e.g. be used for skipping, but not + * be shown in the chapter list */ + int hidden; + /* disabled chapters should be skipped during playback (using this + * would require parsing control blocks) */ + int enabled; + /* Tracks this chapter belongs to. + * Remember that elements can occur in any order, so in theory the + * chapters could become available before the tracks do. + * TODO: currently unused + */ + /* uint64_t* tracks; */ + /* Chapter titles and locale information + * TODO: chapters can have multiple sets of those, i.e. several tuples + * (title, language, country). The current implementation picks from + * those by the following rules: + * 1) remember the first element + * 2) overwrite with an element where language=="eng" + */ + char* title; + char* language; + char* country; +} matroska_chapter_t; + +/* Edition */ +typedef struct { + uint64_t uid; + unsigned int hidden; + unsigned int is_default; + unsigned int ordered; + + int num_chapters, cap_chapters; + matroska_chapter_t** chapters; +} matroska_edition_t; + /* Matroska Track */ typedef struct { int flag_interlaced; @@ -214,7 +262,8 @@ typedef struct { typedef struct matroska_track_s matroska_track_t; struct matroska_track_s { int track_num; - + uint64_t uid; + uint32_t track_type; uint64_t default_duration; char *language; |