From 38e53859fdf5702bb82d937345c2158c2394a3f1 Mon Sep 17 00:00:00 2001 From: Nicos Gollan Date: Sat, 10 Oct 2009 14:47:57 +0100 Subject: Extended Matroska demuxer This adds the following functionality: * Read segment title and uses that for display in a UI There is an issue when the file does not specify a segment title. It will then fall back to a generic "(No title)", since I could not find a way to retrieve the file name the player shows. * More implementation files Added: - demux_matroska.h - demux_matroska_chapters.h This breaks the OO-ish C visibility a bit, since there need to be public (i.e. non-static) interfaces between the units. * Chapter Handling I did a rough initial implementation of Matroska's "editions" system. The demuxer will parse all editions from the header, and for each edition the top level of chapters. This is not quite the full spec as Matroska intends, but it should work fine as long as there is only a single edition and all editions/chapters only reference only one (the first and only) segment in the stream, and are supposed to apply to all tracks therein. When the stream has chapters, the demuxer will now handle skip events from the player to jump between chapters. --- src/demuxers/Makefile.am | 4 +- src/demuxers/demux_matroska-chapters.c | 433 ++++++++++++++++++++++++++++ src/demuxers/demux_matroska.c | 506 ++++++++++++++++++--------------- src/demuxers/demux_matroska.h | 150 ++++++++++ src/demuxers/matroska.h | 53 +++- 5 files changed, 909 insertions(+), 237 deletions(-) create mode 100644 src/demuxers/demux_matroska-chapters.c create mode 100644 src/demuxers/demux_matroska.h (limited to 'src/demuxers') diff --git a/src/demuxers/Makefile.am b/src/demuxers/Makefile.am index 3bc1103ec..4c2aac019 100644 --- a/src/demuxers/Makefile.am +++ b/src/demuxers/Makefile.am @@ -130,7 +130,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) $(ZLIB_LIBS) $(LTLIBINTL) xineplug_dmx_matroska_la_CPPFLAGS = $(ZLIB_CPPFLAGS) xineplug_dmx_matroska_la_CFLAGS = $(AM_CFLAGS) -fno-strict-aliasing @@ -142,4 +142,4 @@ xineplug_dmx_flv_la_SOURCES = demux_flv.c xineplug_dmx_flv_la_LIBADD = $(XINE_LIB) $(LTLIBINTL) xineinclude_HEADERS = demux.h -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 diff --git a/src/demuxers/demux_matroska-chapters.c b/src/demuxers/demux_matroska-chapters.c new file mode 100644 index 000000000..aad8fe46d --- /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 + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#define LOG_MODULE "demux_matroska_chapters" +#define LOG_VERBOSE +/* +#define LOG +*/ + +#include "xine_internal.h" +#include "xineutils.h" +#include "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; inum_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; inum_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; inum_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 5cf783bf5..9d224c62c 100644 --- a/src/demuxers/demux_matroska.c +++ b/src/demuxers/demux_matroska.c @@ -42,6 +42,7 @@ /* #define LOG */ + #include "xine_internal.h" #include "xineutils.h" #include "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; } @@ -1151,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: { - char *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: + { + char *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) @@ -1221,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) @@ -1232,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: @@ -1284,12 +1234,12 @@ 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, - (track->codec_id ? track->codec_id : ""), - (track->language ? track->language : "")); + "demux_matroska: Track %d, %s %s\n", + track->track_num, + (track->codec_id ? track->codec_id : ""), + (track->language ? track->language : "")); if (track->codec_id) { void (*init_codec)(demux_matroska_t *, matroska_track_t *) = NULL; @@ -1307,14 +1257,14 @@ static int parse_track_entry(demux_matroska_t *this, matroska_track_t *track) { } else if (!strcmp(track->codec_id, MATROSKA_CODEC_ID_V_UNCOMPRESSED)) { } else if ((!strcmp(track->codec_id, MATROSKA_CODEC_ID_V_MPEG4_SP)) || - (!strcmp(track->codec_id, MATROSKA_CODEC_ID_V_MPEG4_ASP)) || - (!strcmp(track->codec_id, MATROSKA_CODEC_ID_V_MPEG4_AP))) { + (!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; @@ -1322,23 +1272,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; @@ -1346,14 +1296,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)) { @@ -1374,7 +1324,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; @@ -1386,8 +1336,8 @@ static int parse_track_entry(demux_matroska_t *this, matroska_track_t *track) { track->buf_type = BUF_VIDEO_THEORA_RAW; init_codec = init_codec_xiph; } else if ((!strcmp(track->codec_id, MATROSKA_CODEC_ID_A_MPEG1_L1)) || - (!strcmp(track->codec_id, MATROSKA_CODEC_ID_A_MPEG1_L2)) || - (!strcmp(track->codec_id, MATROSKA_CODEC_ID_A_MPEG1_L3))) { + (!strcmp(track->codec_id, MATROSKA_CODEC_ID_A_MPEG1_L2)) || + (!strcmp(track->codec_id, MATROSKA_CODEC_ID_A_MPEG1_L3))) { lprintf("MATROSKA_CODEC_ID_A_MPEG1\n"); track->buf_type = BUF_AUDIO_MPEG; init_codec = init_codec_audio; @@ -1399,7 +1349,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; @@ -1423,7 +1373,7 @@ static int parse_track_entry(demux_matroska_t *this, matroska_track_t *track) { init_codec = init_codec_audio; } } else if (!strncmp(track->codec_id, MATROSKA_CODEC_ID_A_AAC, - sizeof(MATROSKA_CODEC_ID_A_AAC) - 1)) { + sizeof(MATROSKA_CODEC_ID_A_AAC) - 1)) { lprintf("MATROSKA_CODEC_ID_A_AAC\n"); track->buf_type = BUF_AUDIO_AAC; init_codec = init_codec_aac; @@ -1443,17 +1393,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; @@ -1466,7 +1416,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. @@ -1503,16 +1453,16 @@ static int parse_track_entry(demux_matroska_t *this, matroska_track_t *track) { } if (init_codec) { - if (! track->fifo) { - xprintf(this->stream->xine, XINE_VERBOSITY_LOG, - "demux_matroska: Error: fifo not set up for track of type type %" PRIu32 "\n", track->track_type); - return 0; + if (! track->fifo) { + xprintf(this->stream->xine, XINE_VERBOSITY_LOG, + "demux_matroska: Error: fifo not set up for track of type type %" PRIu32 "\n", track->track_type); + return 0; } init_codec(this, track); } } } - + return 1; } @@ -1531,6 +1481,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; @@ -1544,29 +1500,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)) @@ -1577,7 +1511,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; @@ -2165,6 +2098,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; @@ -2185,6 +2160,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; @@ -2226,6 +2203,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; } @@ -2399,7 +2419,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: @@ -2763,6 +2783,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 *track; @@ -2780,7 +2802,7 @@ static void demux_matroska_dispose (demux_plugin_t *this_gen) { free (track->audio_track); if (track->sub_track) free (track->sub_track); - + free (track); } /* Free the cues. */ @@ -2792,12 +2814,17 @@ static void demux_matroska_dispose (demux_plugin_t *this_gen) { } if (this->indexes) free(this->indexes); - - /* Free the top_level elem list */ + + /* Free the top_level elem list */ if (this->top_level_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); } @@ -2811,7 +2838,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; } @@ -2942,11 +2975,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..a62aba498 --- /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 +#include +#include +#include +#include +#include +#include + +#include "xine_internal.h" +#include "demux.h" +#include "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/matroska.h b/src/demuxers/matroska.h index df6e8ad1b..b32725b3b 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; -- cgit v1.2.3 From 583ecbfa02687d27b987b55c2cb55529f86b4096 Mon Sep 17 00:00:00 2001 From: Petri Hintukainen Date: Mon, 31 Aug 2009 01:04:08 +0100 Subject: Cosmetics. Splitted detect_ts() from open_plugin(). --- src/demuxers/demux_ts.c | 54 ++++++++++++++++++++++++++----------------------- 1 file changed, 29 insertions(+), 25 deletions(-) (limited to 'src/demuxers') diff --git a/src/demuxers/demux_ts.c b/src/demuxers/demux_ts.c index 98de1f9ea..a83ccd8ca 100644 --- a/src/demuxers/demux_ts.c +++ b/src/demuxers/demux_ts.c @@ -1758,7 +1758,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; @@ -2170,6 +2170,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, @@ -2182,33 +2208,11 @@ static demux_plugin_t *open_plugin (demux_class_t *class_gen, case METHOD_BY_CONTENT: { uint8_t buf[2069]; - 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)) return NULL; } break; -- cgit v1.2.3 From e7bd06ed2951ee173a58528a88859c159bc3f5b0 Mon Sep 17 00:00:00 2001 From: Petri Hintukainen Date: Mon, 31 Aug 2009 01:12:24 +0100 Subject: PKT_SIZE --> this->pkt_size (in selected places) --- src/demuxers/demux_ts.c | 30 +++++++++++++++++------------- 1 file changed, 17 insertions(+), 13 deletions(-) (limited to 'src/demuxers') diff --git a/src/demuxers/demux_ts.c b/src/demuxers/demux_ts.c index a83ccd8ca..c02aae1e8 100644 --- a/src/demuxers/demux_ts.c +++ b/src/demuxers/demux_ts.c @@ -288,6 +288,8 @@ typedef struct { int status; + int pkt_size; /* TS packet size */ + int blockSize; int rate; int media_num; @@ -1512,10 +1514,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[n + ((i+p) * this->pkt_size)] != SYNC_BYTE) { sync_ok = 0; break; } @@ -1527,13 +1529,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; @@ -1575,15 +1577,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]++; @@ -1610,7 +1612,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_size * this->packet_number]; this->packet_number++; return return_pointer; } @@ -2305,7 +2307,9 @@ static demux_plugin_t *open_plugin (demux_class_t *class_gen, /* dvb */ this->event_queue = xine_event_new_queue (this->stream); - + + this->pkt_size = PKT_SIZE; + this->numPreview=0; return &this->demux_plugin; -- cgit v1.2.3 From 40a7fb9d58287dc0cb27591836b892f2d10fd778 Mon Sep 17 00:00:00 2001 From: Petri Hintukainen Date: Mon, 31 Aug 2009 01:16:10 +0100 Subject: Support for BluRay/HDMV 192-byte TS packets --- src/demuxers/demux_ts.c | 63 ++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 57 insertions(+), 6 deletions(-) (limited to 'src/demuxers') diff --git a/src/demuxers/demux_ts.c b/src/demuxers/demux_ts.c index c02aae1e8..aaf19b1d2 100644 --- a/src/demuxers/demux_ts.c +++ b/src/demuxers/demux_ts.c @@ -33,6 +33,11 @@ * Date Author * ---- ------ * + * 8-Apr-2009 Petri Hintukainen + * - 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 * - Added support for PMT sections larger than 1 ts packet * @@ -172,9 +177,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 @@ -227,6 +232,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 @@ -288,7 +298,9 @@ 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; @@ -1517,7 +1529,7 @@ static int sync_correct(demux_ts_t*this, uint8_t *buf, int32_t npkt_read) { 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) * this->pkt_size)] != SYNC_BYTE) { + if (buf[this->pkt_offset + n + ((i+p) * this->pkt_size)] != SYNC_BYTE) { sync_ok = 0; break; } @@ -1554,6 +1566,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; @@ -1612,7 +1650,7 @@ static unsigned char * demux_synchronise(demux_ts_t* this) { return NULL; } } - return_pointer = &(this->buf)[this->pkt_size * this->packet_number]; + return_pointer = &(this->buf)[this->pkt_offset + this->pkt_size * this->packet_number]; this->packet_number++; return return_pointer; } @@ -2205,6 +2243,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) { @@ -2214,7 +2253,11 @@ static demux_plugin_t *open_plugin (demux_class_t *class_gen, if (!_x_demux_read_header(input, buf, sizeof(buf))) return NULL; - if (! detect_ts(buf, sizeof(buf), PKT_SIZE)) + 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; @@ -2222,6 +2265,11 @@ static demux_plugin_t *open_plugin (demux_class_t *class_gen, case METHOD_BY_EXTENSION: { const char *const mrl = input->get_mrl (input); + if (_x_demux_check_extension (mrl, "m2ts mts")) + hdmv = 1; + else + hdmv = 0; + /* check extension */ const char *const extensions = class_gen->get_extensions (class_gen); @@ -2308,7 +2356,10 @@ static demux_plugin_t *open_plugin (demux_class_t *class_gen, /* dvb */ this->event_queue = xine_event_new_queue (this->stream); - this->pkt_size = PKT_SIZE; + /* HDMV */ + this->hdmv = hdmv; + this->pkt_offset = (hdmv > 0) ? 4 : 0; + this->pkt_size = PKT_SIZE + this->pkt_offset; this->numPreview=0; -- cgit v1.2.3 From d7aedf3ba509ef563f1839cc2c3cdde449a46c7f Mon Sep 17 00:00:00 2001 From: Petri Hintukainen Date: Mon, 31 Aug 2009 01:22:19 +0100 Subject: Fixed audio. HDMV uses PES stream 0xfd instead of 0xbd. --- src/demuxers/demux_ts.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/demuxers') diff --git a/src/demuxers/demux_ts.c b/src/demuxers/demux_ts.c index aaf19b1d2..0bb70b74b 100644 --- a/src/demuxers/demux_ts.c +++ b/src/demuxers/demux_ts.c @@ -755,7 +755,7 @@ 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 (stream_id == 0xbd || stream_id == 0xfd /* HDMV */) { int spu_id; -- cgit v1.2.3 From d122ca934a7551a2a98108bb15fe5f2b15524cc2 Mon Sep 17 00:00:00 2001 From: Petri Hintukainen Date: Mon, 31 Aug 2009 01:25:47 +0100 Subject: added spu_type parameter to demux_send_special_spu_buf() --- src/demuxers/demux_ts.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'src/demuxers') diff --git a/src/demuxers/demux_ts.c b/src/demuxers/demux_ts.c index 0bb70b74b..408ae43cf 100644 --- a/src/demuxers/demux_ts.c +++ b/src/demuxers/demux_ts.c @@ -460,12 +460,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 ); @@ -1427,7 +1427,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, -- cgit v1.2.3 From 70396b8a99ef58182c493dab0348286fdb25e369 Mon Sep 17 00:00:00 2001 From: Petri Hintukainen Date: Mon, 31 Aug 2009 01:33:27 +0100 Subject: Demux HDMV/BluRay bitmap subtitles --- src/demuxers/demux_ts.c | 39 +++++++++++++++++++++++++++++++++++++-- 1 file changed, 37 insertions(+), 2 deletions(-) (limited to 'src/demuxers') diff --git a/src/demuxers/demux_ts.c b/src/demuxers/demux_ts.c index 408ae43cf..872d4a85f 100644 --- a/src/demuxers/demux_ts.c +++ b/src/demuxers/demux_ts.c @@ -223,8 +223,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 @@ -755,6 +756,17 @@ static int demux_ts_parse_pes_header (xine_t *xine, demux_ts_media *m, p += header_len + 9; packet_len -= header_len + 3; + 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; @@ -1440,6 +1452,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 -- cgit v1.2.3 From affa7abe6c5509149a8fffff5bcaacaf2be3f33e Mon Sep 17 00:00:00 2001 From: Petri Hintukainen Date: Mon, 31 Aug 2009 01:34:16 +0100 Subject: Fixed selecting HDMV SPU track --- src/demuxers/demux_ts.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'src/demuxers') diff --git a/src/demuxers/demux_ts.c b/src/demuxers/demux_ts.c index 872d4a85f..e52c4c765 100644 --- a/src/demuxers/demux_ts.c +++ b/src/demuxers/demux_ts.c @@ -519,6 +519,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); } -- cgit v1.2.3