summaryrefslogtreecommitdiff
path: root/src/demuxers
diff options
context:
space:
mode:
Diffstat (limited to 'src/demuxers')
-rw-r--r--src/demuxers/Makefile.am4
-rw-r--r--src/demuxers/demux_matroska-chapters.c433
-rw-r--r--src/demuxers/demux_matroska.c485
-rw-r--r--src/demuxers/demux_matroska.h150
-rw-r--r--src/demuxers/demux_ts.c183
-rw-r--r--src/demuxers/matroska.h53
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;