summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ChangeLog2
-rw-r--r--include/xine/buffer.h1
-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
-rw-r--r--src/spu_dec/Makefile.am4
-rw-r--r--src/spu_dec/spuhdmv_decoder.c944
10 files changed, 1996 insertions, 263 deletions
diff --git a/ChangeLog b/ChangeLog
index f21aed026..8dac0b84b 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -90,6 +90,8 @@ xine-lib (1.1.17) 2009-??-??
viewing of Apple film trailers.
* Fixed int-to-float conversion in the JACK output plugin.
* Work around MOD files with reported length == 0.
+ * Reworked Matroska demuxer. Now reads files created by mkvmerge 2.7.0.
+ * Support BluRay/HDMV streams & subtitles.
xine-lib (1.1.16.3) 2009-04-03
* Security fixes:
diff --git a/include/xine/buffer.h b/include/xine/buffer.h
index 487e17a93..2a396fa3d 100644
--- a/include/xine/buffer.h
+++ b/include/xine/buffer.h
@@ -280,6 +280,7 @@ extern "C" {
#define BUF_SPU_CVD 0x04050000
#define BUF_SPU_OGM 0x04060000
#define BUF_SPU_CMML 0x04070000
+#define BUF_SPU_HDMV 0x04080000
/*@}*/
/**
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;
diff --git a/src/spu_dec/Makefile.am b/src/spu_dec/Makefile.am
index bd3e0b7fa..ced8946e2 100644
--- a/src/spu_dec/Makefile.am
+++ b/src/spu_dec/Makefile.am
@@ -9,6 +9,7 @@ xineplug_LTLIBRARIES = \
xineplug_decode_spucmml.la \
xineplug_decode_spu.la \
xineplug_decode_spudvb.la \
+ xineplug_decode_spuhdmv.la \
xineplug_sputext.la
xineplug_decode_spucc_la_SOURCES = cc_decoder.c cc_decoder.h xine_cc_decoder.c
@@ -33,5 +34,8 @@ xineplug_decode_spu_la_CFLAGS = $(AM_CFLAGS) $(DVDNAV_CFLAGS) -I$(top_srcdir)/s
xineplug_decode_spudvb_la_SOURCES = spudvb_decoder.c
xineplug_decode_spudvb_la_LIBADD = $(XINE_LIB) $(PTHREAD_LIBS) $(LTLIBINTL)
+xineplug_decode_spuhdmv_la_SOURCES = spuhdmv_decoder.c
+xineplug_decode_spuhdmv_la_LIBADD = $(XINE_LIB) $(PTHREAD_LIBS) $(LTLIBINTL)
+
xineplug_sputext_la_SOURCES = sputext_demuxer.c sputext_decoder.c
xineplug_sputext_la_LIBADD = $(XINE_LIB) $(LTLIBINTL)
diff --git a/src/spu_dec/spuhdmv_decoder.c b/src/spu_dec/spuhdmv_decoder.c
new file mode 100644
index 000000000..f23d34c50
--- /dev/null
+++ b/src/spu_dec/spuhdmv_decoder.c
@@ -0,0 +1,944 @@
+/*
+ * Copyright (C) 2000-2009 the xine project
+ *
+ * Copyright (C) 2009 Petri Hintukainen <phintuka@users.sourceforge.net>
+ *
+ * This file is part of xine, a unix 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
+ *
+ * Decoder for HDMV/BluRay bitmap subtitles
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <inttypes.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+
+#include <xine/xine_internal.h>
+#include <xine/buffer.h>
+#include <xine/xineutils.h>
+#include <xine/video_out.h>
+#include <xine/video_overlay.h>
+
+#define TRACE(x...) printf(x)
+/*#define TRACE(x...) */
+#define ERROR(x...) fprintf(stderr, x)
+/*#define ERROR(x...) lprintf(x) */
+
+/*
+ * cached palette (xine-lib format)
+ */
+typedef struct subtitle_clut_s subtitle_clut_t;
+struct subtitle_clut_s {
+ uint8_t id;
+ uint32_t color[256];
+ uint8_t trans[256];
+ subtitle_clut_t *next;
+};
+
+/*
+ * cached RLE image (xine-lib format)
+ */
+typedef struct subtitle_object_s subtitle_object_t;
+struct subtitle_object_s {
+ uint16_t id;
+ uint16_t xpos, ypos;
+ uint16_t width, height;
+
+ rle_elem_t *rle;
+ uint num_rle;
+ size_t data_size;
+
+ uint8_t *raw_data; /* partial RLE data in HDMV format */
+ size_t raw_data_len;
+ size_t raw_data_size;
+
+ subtitle_object_t *next;
+};
+
+/*
+ * Window definition
+ */
+typedef struct window_def_s window_def_t;
+struct window_def_s {
+ uint8_t id;
+ uint16_t xpos, ypos;
+ uint16_t width, height;
+
+ window_def_t *next;
+};
+
+
+/*
+ * decoded SPU
+ */
+typedef struct composition_object_s composition_object_t;
+struct composition_object_s {
+ uint8_t window_id_ref;
+ uint8_t object_id_ref;
+
+ uint16_t xpos, ypos;
+
+ uint8_t forced_flag;
+ uint8_t cropped_flag;
+ uint16_t crop_horiz_pos, crop_vert_pos;
+ uint16_t crop_width, crop_height;
+
+ composition_object_t *next;
+};
+
+/*
+ * segment_buffer_t
+ *
+ * assemble and decode segments
+ */
+
+typedef struct {
+ /* current segment */
+ int segment_len; /* length of current segment (without 3-byte header) */
+ uint8_t segment_type; /* current segment type */
+ uint8_t *segment_data; /* pointer to current segment payload */
+ uint8_t *segment_end; /* pointer to last byte + 1 of current segment */
+ uint8_t error; /* boolean: buffer overflow etc. */
+
+ /* accumulated data */
+ uint8_t *buf; /* */
+ size_t len; /* count of unprocessed bytes */
+ size_t data_size; /* allocated buffer size */
+} segment_buffer_t;
+
+/*
+ * mgmt
+ */
+
+static segment_buffer_t *segbuf_init(void)
+{
+ segment_buffer_t *buf = calloc(1, sizeof(segment_buffer_t));
+ return buf;
+}
+
+static void segbuf_dispose(segment_buffer_t *buf)
+{
+ if (buf->buf)
+ free (buf->buf);
+ free (buf);
+}
+
+static void segbuf_reset(segment_buffer_t *buf)
+{
+ buf->segment_end = buf->segment_data = buf->buf;
+ buf->len = 0;
+ buf->segment_len = -1;
+ buf->segment_type = 0;
+ buf->error = 0;
+}
+
+/*
+ * assemble, parse
+ */
+
+static void segbuf_parse_segment_header(segment_buffer_t *buf)
+{
+ if (buf->len > 2) {
+ buf->segment_type = buf->buf[0];
+ buf->segment_len = (buf->buf[1] << 8) | buf->buf[2];
+ buf->segment_data = buf->buf + 3;
+ buf->segment_end = buf->segment_data + buf->segment_len;
+ buf->error = 0;
+
+ if ( buf->segment_type < 0x14 ||
+ ( buf->segment_type > 0x18 &&
+ buf->segment_type != 0x80)) {
+ ERROR("unknown segment type, resetting\n");
+ segbuf_reset(buf);
+ }
+ } else {
+ buf->segment_len = -1;
+ buf->error = 1;
+ }
+}
+
+static void segbuf_fill(segment_buffer_t *buf, uint8_t *data, size_t len)
+{
+ if (buf->len + len > buf->data_size) {
+ buf->data_size = buf->len + len;
+ if (buf->buf)
+ buf->buf = realloc(buf->buf, buf->data_size);
+ else
+ buf->buf = malloc(buf->data_size);
+ }
+
+ memcpy(buf->buf + buf->len, data, len);
+ buf->len += len;
+
+ segbuf_parse_segment_header(buf);
+}
+
+static int segbuf_segment_complete(segment_buffer_t *buf)
+{
+ return (buf->segment_len >= 0) && (buf->len >= buf->segment_len + 3);
+}
+
+static void segbuf_skip_segment(segment_buffer_t *buf)
+{
+ if (segbuf_segment_complete (buf)) {
+ buf->len -= buf->segment_len + 3;
+ if (buf->len > 0)
+ memmove(buf->buf, buf->buf + buf->segment_len + 3, buf->len);
+
+ segbuf_parse_segment_header(buf);
+
+ TRACE(" skip_segment: %d bytes left\n", (uint)buf->len);
+ } else {
+ ERROR(" skip_segment: ERROR - %d bytes queued, %d required\n",
+ (uint)buf->len, buf->segment_len);
+ segbuf_reset (buf);
+ }
+}
+
+/*
+ * access segment data
+ */
+
+static uint8_t segbuf_segment_type(segment_buffer_t *buf)
+{
+ return buf->segment_type;
+}
+
+static size_t segbuf_data_length(segment_buffer_t *buf)
+{
+ ssize_t val = buf->segment_end - buf->segment_data;
+ if (val < 0) val = 0;
+ return (size_t)val;
+}
+
+static uint8_t segbuf_get_u8(segment_buffer_t *buf)
+{
+ if (!(buf->error = ++buf->segment_data > buf->segment_end))
+ return buf->segment_data[-1];
+ ERROR("segbuf_get_u8: read failed (end of segment reached) !");
+ return 0;
+}
+
+static uint16_t segbuf_get_u16(segment_buffer_t *buf)
+{
+ return (segbuf_get_u8(buf) << 8) | segbuf_get_u8(buf);
+}
+
+static uint32_t segbuf_get_u24(segment_buffer_t *buf)
+{
+ return (segbuf_get_u8(buf) << 16) | (segbuf_get_u8(buf) << 8) | segbuf_get_u8(buf);
+}
+
+static uint8_t *segbuf_get_string(segment_buffer_t *buf, size_t len)
+{
+ if (len > 0) {
+ uint8_t *val = buf->segment_data;
+ buf->segment_data += len;
+ if (buf->segment_data <= buf->segment_end)
+ return val;
+ }
+ ERROR("segbuf_get_string(%d): read failed (end of segment reached) !", (int)len);
+ buf->error = 1;
+ return NULL;
+}
+
+/*
+ * decode segments
+ */
+
+static subtitle_clut_t *segbuf_decode_palette(segment_buffer_t *buf)
+{
+ uint8_t palette_id = segbuf_get_u8 (buf);
+ uint8_t palette_version_number = segbuf_get_u8 (buf);
+
+ size_t len = segbuf_data_length(buf);
+ size_t entries = len / 5;
+ int i;
+
+ if (buf->error)
+ return NULL;
+
+ if (len % 5) {
+ ERROR(" decode_palette: segment size error (%d ; expected %d for %d entries)\n",
+ (uint)len, (uint)(5 * entries), (uint)entries);
+ return NULL;
+ }
+ TRACE("decode_palette: %d items (id %d, version %d)\n",
+ (uint)entries, palette_id, palette_version_number);
+
+ /* convert to xine-lib clut */
+ subtitle_clut_t *clut = calloc(1, sizeof(subtitle_clut_t));
+ clut->id = palette_id;
+
+ for (i = 0; i < entries; i++) {
+ uint8_t index = segbuf_get_u8 (buf);
+ uint8_t Y = segbuf_get_u8 (buf);
+ uint8_t Cr = segbuf_get_u8 (buf);
+ uint8_t Cb = segbuf_get_u8 (buf);
+ uint8_t alpha = segbuf_get_u8 (buf);
+ clut->color[index] = (Y << 16) | (Cr << 8) | Cb;
+ clut->trans[index] = alpha >> 4;
+ }
+
+ return clut;
+}
+
+static int segbuf_decode_rle(segment_buffer_t *buf, subtitle_object_t *obj)
+{
+ int x = 0, y = 0;
+ int rle_size = sizeof(rle_elem_t) * obj->width / 16 * obj->height + 1;
+ rle_elem_t *rlep = malloc(rle_size);
+
+ free (obj->rle);
+ obj->rle = rlep;
+ obj->data_size = rle_size;
+ obj->num_rle = 0;
+
+ /* convert to xine-lib rle format */
+ while (y < obj->height && !buf->error) {
+
+ /* decode RLE element */
+ uint8_t byte = segbuf_get_u8 (buf);
+ if (byte != 0) {
+ rlep->color = byte;
+ rlep->len = 1;
+ } else {
+ byte = segbuf_get_u8 (buf);
+ if (!(byte & 0x80)) {
+ rlep->color = 0;
+ if (!(byte & 0x40))
+ rlep->len = byte & 0x3f;
+ else
+ rlep->len = ((byte & 0x3f) << 8) | segbuf_get_u8 (buf);
+ } else {
+ if (!(byte & 0x40))
+ rlep->len = byte & 0x3f;
+ else
+ rlep->len = ((byte & 0x3f) << 8) | segbuf_get_u8 (buf);
+ rlep->color = segbuf_get_u8 (buf);
+ }
+ }
+
+ /* move to next element */
+ if (rlep->len > 0) {
+ x += rlep->len;
+ rlep++;
+ obj->num_rle ++;
+ } else {
+ /* end of line marker (00 00) */
+ if (x < obj->width) {
+ rlep->len = obj->width - x;
+ rlep->color = 0xff;
+ rlep++;
+ obj->num_rle ++;
+ }
+ x = 0;
+ y++;
+ }
+
+ /* grow allocated RLE data size ? */
+ if (obj->data_size <= (obj->num_rle + 1) * sizeof(rle_elem_t)) {
+ obj->data_size *= 2;
+ obj->rle = realloc(obj->rle, obj->data_size);
+ rlep = obj->rle + obj->num_rle;
+ }
+ }
+
+ return buf->error;
+}
+
+static subtitle_object_t *segbuf_decode_object(segment_buffer_t *buf)
+{
+ uint8_t object_id = segbuf_get_u16(buf);
+ uint8_t version = segbuf_get_u8 (buf);
+ uint8_t seq_desc = segbuf_get_u8 (buf);
+
+ TRACE(" decode_object: object_id %d, version %d, seq 0x%x\n",
+ object_id, version, seq_desc);
+
+ //LIST_FIND();
+ subtitle_object_t *obj = calloc(1, sizeof(subtitle_object_t));
+ obj->id = object_id;
+
+ if (seq_desc & 0x80) {
+
+ uint32_t data_len = segbuf_get_u24(buf);
+ obj->width = segbuf_get_u16(buf);
+ obj->height = segbuf_get_u16(buf);
+
+ TRACE(" object length %d bytes, size %dx%d\n", data_len, obj->width, obj->height);
+
+ segbuf_decode_rle (buf, obj);
+
+ if (buf->error) {
+ free(obj);
+ return NULL;
+ }
+
+ } else {
+ ERROR(" TODO: APPEND RLE, length %d bytes\n", buf->segment_len - 4);
+ /* TODO */
+ free(obj);
+ return NULL;
+ }
+
+ return obj;
+}
+
+static window_def_t *segbuf_decode_window_definition(segment_buffer_t *buf)
+{
+ window_def_t *wnd = calloc(1, sizeof(window_def_t));
+
+ uint8_t a = segbuf_get_u8 (buf);
+ wnd->id = segbuf_get_u8 (buf);
+ wnd->xpos = segbuf_get_u16 (buf);
+ wnd->ypos = segbuf_get_u16 (buf);
+ wnd->width = segbuf_get_u16 (buf);
+ wnd->height = segbuf_get_u16 (buf);
+
+ TRACE(" window: [%02x %d] %d,%d %dx%d\n", a,
+ wnd->id, wnd->xpos, wnd->ypos, wnd->width, wnd->height);
+
+ if (buf->error) {
+ free(wnd);
+ return NULL;
+ }
+
+ return wnd;
+}
+
+static int segbuf_decode_video_descriptor(segment_buffer_t *buf)
+{
+ uint16_t width = segbuf_get_u16(buf);
+ uint16_t height = segbuf_get_u16(buf);
+ uint8_t frame_rate = segbuf_get_u8 (buf);
+
+ TRACE(" video_descriptor: %dx%d fps %d\n", width, height, frame_rate);
+ return buf->error;
+}
+
+typedef struct composition_descriptor_s composition_descriptor_t;
+struct composition_descriptor_s {
+ uint16_t number;
+ uint8_t state;
+};
+
+static int segbuf_decode_composition_descriptor(segment_buffer_t *buf, composition_descriptor_t *descr)
+{
+ descr->number = segbuf_get_u16(buf);
+ descr->state = segbuf_get_u8 (buf);
+
+ TRACE(" composition_descriptor: number %d, state %d\n", descr->number, descr->state);
+ return buf->error;
+}
+
+static composition_object_t *segbuf_decode_composition_object(segment_buffer_t *buf)
+{
+ composition_object_t *cobj = calloc(1, sizeof(composition_object_t));
+
+ cobj->object_id_ref = segbuf_get_u16 (buf);
+ cobj->window_id_ref = segbuf_get_u8 (buf);
+ uint8_t tmp = segbuf_get_u8 (buf);
+ cobj->cropped_flag = !!(tmp & 0x80);
+ cobj->forced_flag = !!(tmp & 0x40);
+ cobj->xpos = segbuf_get_u16 (buf);
+ cobj->ypos = segbuf_get_u16 (buf);
+ if (cobj->cropped_flag) {
+ /* x,y where to take the image from */
+ cobj->crop_horiz_pos = segbuf_get_u8 (buf);
+ cobj->crop_vert_pos = segbuf_get_u8 (buf);
+ /* size of the cropped image */
+ cobj->crop_width = segbuf_get_u8 (buf);
+ cobj->crop_height = segbuf_get_u8 (buf);
+ }
+
+ if (buf->error) {
+ free(cobj);
+ return NULL;
+ }
+
+ TRACE(" composition_object: id: %d, win: %d, position %d,%d crop %d forced %d\n",
+ cobj->object_id_ref, cobj->window_id_ref, cobj->xpos, cobj->ypos,
+ cobj->cropped_flag, cobj->forced_flag);
+
+ return cobj;
+}
+
+static rle_elem_t *copy_crop_rle(subtitle_object_t *obj, composition_object_t *cobj)
+{
+ /* TODO: exec cropping here (w,h sized image from pos x,y) */
+
+ rle_elem_t *rle = calloc (obj->num_rle, sizeof(rle_elem_t));
+ memcpy (rle, obj->rle, obj->num_rle * sizeof(rle_elem_t));
+ return rle;
+}
+
+
+/*
+ * xine plugin
+ */
+
+typedef struct {
+ spu_decoder_class_t decoder_class;
+} spuhdmv_class_t;
+
+typedef struct spuhdmv_decoder_s {
+ spu_decoder_t spu_decoder;
+
+ spuhdmv_class_t *class;
+ xine_stream_t *stream;
+
+ segment_buffer_t *buf;
+
+ subtitle_clut_t *cluts;
+ subtitle_object_t *objects;
+ window_def_t *windows;
+ int overlay_handles[MAX_OBJECTS];
+
+ int64_t pts;
+
+} spuhdmv_decoder_t;
+
+#define LIST_REPLACE_OLD(type, list, obj) \
+ do { \
+ /* insert to list */ \
+ obj->next = list; \
+ list = obj; \
+\
+ /* remove old */ \
+ type *i = list; \
+ while (i->next && i->next->id != obj->id) \
+ i = i->next; \
+ if (i->next) { \
+ void *tmp = (void*)i->next; \
+ i->next = i->next->next; \
+ free(tmp); \
+ } \
+ } while (0);
+
+#define LIST_REPLACE(list, obj) \
+ do { \
+ uint id = obj->id; \
+ \
+ /* insert to list */ \
+ obj->next = list; \
+ list = obj; \
+ \
+ /* remove old */ \
+ while (obj->next && obj->next->id != id) \
+ obj = obj->next; \
+ if (obj->next) { \
+ void *tmp = (void*)obj->next; \
+ obj->next = obj->next->next; \
+ free(tmp); \
+ } \
+ } while (0);
+
+#define LIST_DESTROY(list) \
+ while (list) { \
+ void *tmp = (void*)list; \
+ list = list->next; \
+ free (tmp); \
+ }
+
+static int decode_palette(spuhdmv_decoder_t *this)
+{
+ /* decode */
+ subtitle_clut_t *clut = segbuf_decode_palette(this->buf);
+ if (!clut)
+ return 1;
+
+ LIST_REPLACE (this->cluts, clut);
+
+ return 0;
+}
+
+static int decode_object(spuhdmv_decoder_t *this)
+{
+ /* decode */
+ subtitle_object_t *obj = segbuf_decode_object(this->buf);
+ if (!obj)
+ return 1;
+
+ LIST_REPLACE (this->objects, obj);
+
+ return 0;
+}
+
+static int decode_window_definition(spuhdmv_decoder_t *this)
+{
+ /* decode */
+ window_def_t *wnd = segbuf_decode_window_definition (this->buf);
+ if (!wnd)
+ return 1;
+
+ LIST_REPLACE (this->windows, wnd);
+
+ return 0;
+}
+
+static int show_overlay(spuhdmv_decoder_t *this, composition_object_t *cobj, uint palette_id_ref,
+ int overlay_index, int64_t pts)
+{
+ video_overlay_manager_t *ovl_manager = this->stream->video_out->get_overlay_manager(this->stream->video_out);
+ metronom_t *metronom = this->stream->metronom;
+ video_overlay_event_t event = {0};
+ vo_overlay_t overlay = {0};
+
+ /* find palette */
+ subtitle_clut_t *clut = this->cluts;
+ while (clut && clut->id != palette_id_ref)
+ clut = clut->next;
+ if (!clut) {
+ ERROR(" fill_overlay: clut %d not found !\n", palette_id_ref);
+ return -1;
+ }
+
+ /* copy palette to xine overlay */
+ overlay.rgb_clut = 0;
+ memcpy(overlay.color, clut->color, sizeof(uint32_t) * 256);
+ memcpy(overlay.trans, clut->trans, sizeof(uint8_t) * 256);
+
+ /* find RLE image */
+ subtitle_object_t *obj = this->objects;
+ while (obj && obj->id != cobj->object_id_ref)
+ obj = obj->next;
+ if (!obj) {
+ ERROR(" fill_overlay: object %d not found !\n", cobj->object_id_ref);
+ return -1;
+ }
+
+ /* find window */
+ window_def_t *wnd = this->windows;
+ while (wnd && wnd->id != cobj->window_id_ref)
+ wnd = wnd->next;
+ if (!wnd) {
+ ERROR(" fill_overlay: window %d not found !\n", cobj->window_id_ref);
+ return -1;
+ }
+
+ /* copy and crop RLE image to xine overlay */
+ overlay.width = obj->width;
+ overlay.height = obj->height;
+
+ overlay.rle = copy_crop_rle (obj, cobj);
+ overlay.num_rle = obj->num_rle;
+ overlay.data_size = obj->num_rle * sizeof(rle_elem_t);
+
+ /* */
+
+ overlay.x = /*wnd->xpos +*/ cobj->xpos;
+ overlay.y = /*wnd->ypos +*/ cobj->ypos;
+
+ overlay.unscaled = 0;
+ overlay.hili_top = -1;
+ overlay.hili_bottom = -1;
+ overlay.hili_left = -1;
+ overlay.hili_right = -1;
+
+ TRACE(" -> overlay: %d,%d %dx%d\n",
+ overlay.x, overlay.y, overlay.width, overlay.height);
+
+
+ /* set timings */
+
+ if (pts > 0)
+ event.vpts = metronom->got_spu_packet (metronom, pts);
+ else
+ event.vpts = 0;
+
+
+ /* generate SHOW event */
+
+ this->stream->video_out->enable_ovl(this->stream->video_out, 1);
+
+ if (this->overlay_handles[overlay_index] < 0)
+ this->overlay_handles[overlay_index] = ovl_manager->get_handle(ovl_manager, 0);
+
+ event.event_type = OVERLAY_EVENT_SHOW;
+ event.object.handle = this->overlay_handles[overlay_index];
+ event.object.overlay = &overlay;
+ event.object.object_type = 0; /* subtitle */
+
+ ovl_manager->add_event (ovl_manager, (void *)&event);
+
+ obj->rle = NULL;
+
+ return 0;
+}
+
+typedef struct presentation_segment_s presentation_segment_t;
+struct presentation_segment_s {
+ composition_descriptor_t comp_descr;
+
+ uint8_t palette_update_flag;
+ uint8_t palette_id_ref;
+ uint8_t object_number;
+
+ composition_object_t *comp_objs;
+
+ presentation_segment_t *next;
+
+ int64_t pts;
+};
+
+static void show_overlays(spuhdmv_decoder_t *this, presentation_segment_t *pseg)
+{
+ composition_object_t *cobj = pseg->comp_objs;
+ int i;
+
+ for (i = 0; i < pseg->object_number; i++) {
+ if (!cobj) {
+ ERROR("show_overlays: composition object %d missing !\n", i);
+ } else {
+ show_overlay(this, cobj, pseg->palette_id_ref, i, pseg->pts);
+ cobj = cobj->next;
+ }
+ }
+}
+
+static void hide_overlays(spuhdmv_decoder_t *this, int64_t pts)
+{
+ video_overlay_event_t event = {0};
+ int i = 0;
+
+ while (this->overlay_handles[i] >= 0) {
+ TRACE(" -> HIDE %d\n", i);
+
+ video_overlay_manager_t *ovl_manager = this->stream->video_out->get_overlay_manager(this->stream->video_out);
+ metronom_t *metronom = this->stream->metronom;
+
+ event.object.handle = this->overlay_handles[i];
+ if (this)
+ event.vpts = metronom->got_spu_packet (metronom, pts);
+ else
+ event.vpts = 0;
+ event.event_type = OVERLAY_EVENT_HIDE;
+ event.object.overlay = NULL;
+ ovl_manager->add_event (ovl_manager, (void *)&event);
+
+ //this->overlay_handles[i] = -1;
+ i++;
+ }
+}
+
+static int decode_presentation_segment(spuhdmv_decoder_t *this)
+{
+ presentation_segment_t p = {};
+ segment_buffer_t *buf = this->buf;
+ int index;
+
+ segbuf_decode_video_descriptor (this->buf);
+ segbuf_decode_composition_descriptor (this->buf, &p.comp_descr);
+
+ p.palette_update_flag = !!((segbuf_get_u8(buf)) & 0x80);
+ p.palette_id_ref = segbuf_get_u8 (buf);
+ p.object_number = segbuf_get_u8 (buf);
+
+ TRACE(" presentation_segment: object_number %d, palette %d\n",
+ p.object_number, p.palette_id_ref);
+
+ p.pts = this->pts; /* !! todo - use it ? */
+
+ for (index = 0; index < p.object_number; index++) {
+ composition_object_t *cobj = segbuf_decode_composition_object (this->buf);
+ cobj->next = p.comp_objs;
+ p.comp_objs = cobj;
+ }
+
+ if (!p.comp_descr.state) {
+ hide_overlays (this, this->pts);
+ } else {
+ show_overlays (this, &p);
+ LIST_DESTROY (p.comp_objs);
+ }
+
+ return buf->error;
+}
+
+static void decode_segment(spuhdmv_decoder_t *this)
+{
+ TRACE("*** new segment, pts %010ld: 0x%02x (%8d bytes)",
+ this->pts, (uint)this->buf->segment_type, (uint)this->buf->segment_len);
+
+ switch (this->buf->segment_type) {
+ case 0x14:
+ TRACE(" segment: PALETTE\n");
+ decode_palette(this);
+ break;
+ case 0x15:
+ TRACE(" segment: OBJECT\n");
+ decode_object(this);
+ break;
+ case 0x16:
+ TRACE(" segment: PRESENTATION SEGMENT\n");
+ decode_presentation_segment(this);
+ break;
+ case 0x17:
+ TRACE(" segment: WINDOW DEFINITION\n");
+ decode_window_definition(this);
+ break;
+ case 0x18:
+ TRACE(" segment: INTERACTIVE\n");
+ break;
+ case 0x80:
+ TRACE(" segment: END OF DISPLAY\n");
+ {
+ int64_t pts = xine_get_current_vpts(this->stream) -
+ this->stream->metronom->get_option(this->stream->metronom,
+ METRONOM_VPTS_OFFSET);
+ TRACE(" * current pts = %ld\n", pts);
+ }
+
+ break;
+ default:
+ ERROR(" segment type 0x%x unknown, skipping\n", this->buf->segment_type);
+ break;
+ }
+ if (this->buf->error) {
+ ERROR("*** DECODE ERROR ***\n");
+ }
+}
+
+static void close_osd(spuhdmv_decoder_t *this)
+{
+ video_overlay_manager_t *ovl_manager = this->stream->video_out->get_overlay_manager (this->stream->video_out);
+
+ int i = 0;
+ while (this->overlay_handles[i] >= 0) {
+ ovl_manager->free_handle(ovl_manager, this->overlay_handles[i]);
+ this->overlay_handles[i] = -1;
+ i++;
+ }
+}
+
+static void spudec_decode_data (spu_decoder_t * this_gen, buf_element_t * buf)
+{
+ spuhdmv_decoder_t *this = (spuhdmv_decoder_t *) this_gen;
+
+ if ((buf->type & 0xffff0000) != BUF_SPU_HDMV)
+ return;
+
+ if (buf->size < 1)
+ return;
+
+ if (buf->pts)
+ this->pts = buf->pts;
+
+#ifdef DUMP_SPU_DATA
+ int i;
+ for(i = 0; i < buf->size; i++)
+ printf(" %02x", buf->content[i]);
+ printf("\n");
+#endif
+
+ segbuf_fill(this->buf, buf->content, buf->size);
+
+ while (segbuf_segment_complete(this->buf)) {
+ decode_segment(this);
+ segbuf_skip_segment(this->buf);
+ }
+}
+
+static void spudec_reset (spu_decoder_t * this_gen)
+{
+ spuhdmv_decoder_t *this = (spuhdmv_decoder_t *) this_gen;
+
+ if (this->buf)
+ segbuf_reset(this->buf);
+
+ LIST_DESTROY (this->cluts);
+ LIST_DESTROY (this->objects);
+ LIST_DESTROY (this->windows);
+
+ close_osd(this);
+}
+
+static void spudec_discontinuity (spu_decoder_t *this_gen)
+{
+ spuhdmv_decoder_t *this = (spuhdmv_decoder_t *) this_gen;
+
+ close_osd(this);
+}
+
+static void spudec_dispose (spu_decoder_t *this_gen)
+{
+ spuhdmv_decoder_t *this = (spuhdmv_decoder_t *) this_gen;
+
+ close_osd (this);
+ segbuf_dispose (this->buf);
+
+ LIST_DESTROY (this->cluts);
+ LIST_DESTROY (this->objects);
+ LIST_DESTROY (this->windows);
+
+ free (this);
+}
+
+static spu_decoder_t *open_plugin (spu_decoder_class_t *class_gen, xine_stream_t *stream)
+{
+ spuhdmv_decoder_t *this;
+
+ this = (spuhdmv_decoder_t *) calloc(1, sizeof (spuhdmv_decoder_t));
+
+ this->spu_decoder.decode_data = spudec_decode_data;
+ this->spu_decoder.reset = spudec_reset;
+ this->spu_decoder.discontinuity = spudec_discontinuity;
+ this->spu_decoder.dispose = spudec_dispose;
+ this->spu_decoder.get_interact_info = NULL;
+ this->spu_decoder.set_button = NULL;
+ this->stream = stream;
+ this->class = (spuhdmv_class_t *) class_gen;
+
+ this->buf = segbuf_init();
+
+ memset(this->overlay_handles, 0xff, sizeof(this->overlay_handles)); /* --> -1 */
+
+ return &this->spu_decoder;
+}
+
+static void *init_plugin (xine_t *xine, void *data)
+{
+ spuhdmv_class_t *this;
+
+ this = calloc(1, sizeof (spuhdmv_class_t));
+
+ this->decoder_class.open_plugin = open_plugin;
+ this->decoder_class.identifier = "spuhdmv";
+ this->decoder_class.description = "HDMV/BluRay bitmap SPU decoder plugin";
+ this->decoder_class.dispose = default_spu_decoder_class_dispose;
+
+ return this;
+}
+
+/* plugin catalog information */
+static const uint32_t supported_types[] = { BUF_SPU_HDMV, 0 };
+
+static const decoder_info_t dec_info_data = {
+ supported_types, /* supported types */
+ 5 /* priority */
+};
+
+const plugin_info_t xine_plugin_info[] EXPORTED = {
+ /* type, API, "name", version, special_info, init_function */
+ { PLUGIN_SPU_DECODER, 17, "spuhdmv", XINE_VERSION_CODE, &dec_info_data, &init_plugin },
+ { PLUGIN_NONE, 0, "", 0, NULL, NULL }
+};