summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorcvs2svn <admin@example.com>2010-02-07 01:39:30 +0000
committercvs2svn <admin@example.com>2010-02-07 01:39:30 +0000
commit3c2b5df6fbd812c3404cd18d062219bbec96d7e4 (patch)
treed7cc92e45d3b21d3567538e0e4d16ebb6bb1bb8e
parentf7c1f7c41b2f59ff5f9bf5748949a7023b50662a (diff)
parent7a47234e4ed7c1761e4d3478dee2db9779ad79e3 (diff)
downloadxineliboutput-1_0_5.tar.gz
xineliboutput-1_0_5.tar.bz2
This commit was manufactured by cvs2svn to create tag 'xineliboutput-1_0_5'.xineliboutput-1_0_5
-rw-r--r--xine/BluRay/Makefile167
-rw-r--r--xine/BluRay/decode_spuhdmv.c1023
-rw-r--r--xine/BluRay/demux_ts.c2604
-rw-r--r--xine/BluRay/input_bluray.c667
-rw-r--r--xine/BluRay/patches/xine-lib-1.1.16.3-ffmpeg-vc1-extradata.diff62
-rw-r--r--xine/BluRay/patches/xine-lib-1.1.16.3-ffmpeg-vc1-reopen.diff25
6 files changed, 4548 insertions, 0 deletions
diff --git a/xine/BluRay/Makefile b/xine/BluRay/Makefile
new file mode 100644
index 00000000..861be796
--- /dev/null
+++ b/xine/BluRay/Makefile
@@ -0,0 +1,167 @@
+#
+# deps
+#
+
+BLURAY_INC_DIR ?= /usr/src/libbluray/src/
+
+#
+# select plugins
+#
+
+ENABLE_DEMUX_PLUGIN ?= yes
+
+ifeq ($(shell grep BLURAY $(BLURAY_INC_DIR)/bluray.h > /dev/null && echo yes), yes)
+ ENABLE_INPUT_PLUGIN ?= yes
+else
+ ENABLE_INPUT_PLUGIN ?= no
+ $(warning libbluray not found)
+endif
+
+ifeq ($(shell pkg-config libxine --atleast-version=1.1.17 || echo "yes"), yes)
+ ENABLE_SPU_PLUGIN ?= yes
+else
+ ENABLE_SPU_PLUGIN ?= no
+ $(warning SPU plugin is included in xine-lib 1.1.17+)
+endif
+
+ifeq ($(shell pkg-config libxine --atleast-version=1.1.17.1 || echo "yes"), yes)
+ ENABLE_PCM_PLUGIN ?= yes
+else
+ ENABLE_PCM_PLUGIN ?= no
+ $(warning PCM plugin is included in xine-lib 1.1.17.1+)
+endif
+
+#
+# compiler options
+#
+
+CFLAGS += -O2 -fPIC
+CFLAGS += -Wall
+CFLAGS += -D_REENTRANT -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64
+CFLAGS += $(shell pkg-config libxine --cflags)
+CFLAGS += $(CFLAGS_BD)
+INSTALL = install
+
+ifeq ($(ENABLE_INPUT_PLUGIN), yes)
+CFLAGS_BD ?= -I$(BLURAY_INC_DIR)
+endif
+
+#
+# linker options
+#
+
+LIBS_XINE = $(shell pkg-config libxine --libs)
+LDFLAGS += -shared -fvisibility=hidden
+
+ifeq ($(ENABLE_INPUT_PLUGIN), yes)
+LIBS_BD ?= -L/usr/local/lib -lbluray
+endif
+
+#
+# targets
+#
+
+DESTDIR ?= /
+XINEPLUGINDIR = $(shell pkg-config libxine --variable=plugindir)
+
+ifeq ($(ENABLE_DEMUX_PLUGIN), yes)
+ XINEDMXPLUGIN = xineplug_dmx_mpeg_ts_hdmv.so
+else
+ $(warning Not building HDMV MPEG-TS demuxer plugin)
+endif
+
+ifeq ($(ENABLE_SPU_PLUGIN), yes)
+ XINESPUPLUGIN = xineplug_decode_spuhdmv.so
+else
+ $(warning Not building HDMV SPU plugin)
+endif
+
+ifeq ($(ENABLE_INPUT_PLUGIN), yes)
+ XINEINPUTPLUGIN= xineplug_inp_bluray.so
+else
+ $(warning Not building BluRay input plugin)
+endif
+
+ifeq ($(ENABLE_PCM_PLUGIN), yes)
+ XINELPCMPLUGIN = xineplug_decode_lpcm_hdmv.so
+else
+ $(warning Not building BluRay PCM plugin)
+endif
+
+#
+# object files
+#
+
+OBJS_XINEDMXPLUGIN = demux_ts.o
+OBJS_XINESPUPLUGIN = decode_spuhdmv.o
+OBJS_XINEINPUTPLUGIN = input_bluray.o
+OBJS_XINELPCMPLUGIN = xine_lpcm_decoder.o
+
+#
+# rules
+#
+
+all: $(XINEDMXPLUGIN) $(XINESPUPLUGIN) $(XINEINPUTPLUGIN) $(XINELPCMPLUGIN)
+
+ifeq ($(ENABLE_DEMUX_PLUGIN), yes)
+$(XINEDMXPLUGIN): $(OBJS_XINEDMXPLUGIN)
+ $(CC) $(LDFLAGS) $(LIBS_XINE) -o $@ $(OBJS_XINEDMXPLUGIN)
+endif
+
+ifeq ($(ENABLE_SPU_PLUGIN), yes)
+$(XINESPUPLUGIN): $(OBJS_XINESPUPLUGIN)
+ $(CC) $(LDFLAGS) $(LIBS_XINE) -o $@ $(OBJS_XINESPUPLUGIN)
+endif
+
+ifeq ($(ENABLE_INPUT_PLUGIN), yes)
+$(XINEINPUTPLUGIN): $(OBJS_XINEINPUTPLUGIN)
+ $(CC) $(LDFLAGS) $(LIBS_XINE) $(LIBS_BD) -o $@ $(OBJS_XINEINPUTPLUGIN)
+endif
+
+ifeq ($(ENABLE_PCM_PLUGIN), yes)
+$(XINELPCMPLUGIN): $(OBJS_XINELPCMPLUGIN)
+ $(CC) $(LDFLAGS) $(LIBS_XINE) -o $@ $(OBJS_XINELPCMPLUGIN)
+endif
+
+#
+# targets
+#
+
+clean:
+ @rm -rf *.o *.so *~
+
+install: all uninstall
+ifeq ($(ENABLE_DEMUX_PLUGIN), yes)
+ @echo Installing $(DESTDIR)/$(XINEPLUGINDIR)/$(XINEDMXPLUGIN)
+ @$(INSTALL) -m 0644 $(XINEDMXPLUGIN) $(DESTDIR)/$(XINEPLUGINDIR)/$(XINEDMXPLUGIN)
+endif
+ifeq ($(ENABLE_SPU_PLUGIN), yes)
+ @echo Installing $(DESTDIR)/$(XINEPLUGINDIR)/$(XINESPUPLUGIN)
+ @$(INSTALL) -m 0644 $(XINESPUPLUGIN) $(DESTDIR)/$(XINEPLUGINDIR)/$(XINESPUPLUGIN)
+endif
+ifeq ($(ENABLE_INPUT_PLUGIN), yes)
+ @echo Installing $(DESTDIR)/$(XINEPLUGINDIR)/$(XINEINPUTPLUGIN)
+ @$(INSTALL) -m 0644 $(XINEINPUTPLUGIN) $(DESTDIR)/$(XINEPLUGINDIR)/$(XINEINPUTPLUGIN)
+endif
+ifeq ($(ENABLE_PCM_PLUGIN), yes)
+ @echo Installing $(DESTDIR)/$(XINEPLUGINDIR)/$(XINELPCMPLUGIN)
+ @$(INSTALL) -m 0644 $(XINELPCMPLUGIN) $(DESTDIR)/$(XINEPLUGINDIR)/$(XINELPCMPLUGIN)
+endif
+
+uninstall:
+ifeq ($(ENABLE_DEMUX_PLUGIN), yes)
+ @echo Removing $(DESTDIR)/$(XINEPLUGINDIR)/$(XINEDMXPLUGIN)
+ @-rm -rf $(DESTDIR)/$(XINEPLUGINDIR)/$(XINEDMXPLUGIN)
+endif
+ifeq ($(ENABLE_SPU_PLUGIN), yes)
+ @echo Removing $(DESTDIR)/$(XINEPLUGINDIR)/$(XINESPUPLUGIN)
+ @-rm -rf $(DESTDIR)/$(XINEPLUGINDIR)/$(XINESPUPLUGIN)
+endif
+ifeq ($(ENABLE_INPUT_PLUGIN), yes)
+ @echo Removing $(DESTDIR)/$(XINEPLUGINDIR)/$(XINEINPUTPLUGIN)
+ @-rm -rf $(DESTDIR)/$(XINEPLUGINDIR)/$(XINEINPUTPLUGIN)
+endif
+ifeq ($(ENABLE_PCM_PLUGIN), yes)
+ @echo Removing $(DESTDIR)/$(XINEPLUGINDIR)/$(XINELPCMPLUGIN)
+ @-rm -rf $(DESTDIR)/$(XINEPLUGINDIR)/$(XINELPCMPLUGIN)
+endif
diff --git a/xine/BluRay/decode_spuhdmv.c b/xine/BluRay/decode_spuhdmv.c
new file mode 100644
index 00000000..d5815f56
--- /dev/null
+++ b/xine/BluRay/decode_spuhdmv.c
@@ -0,0 +1,1023 @@
+/*
+ * 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>
+
+#ifdef HAVE_CONFIG_H
+# include "xine_internal.h"
+# include "buffer.h"
+# include "xineutils.h"
+# include "video_out.h"
+# include "video_overlay.h"
+#else
+# include <xine/xine_internal.h>
+# include <xine/buffer.h>
+# include <xine/xineutils.h>
+# include <xine/video_out.h>
+# include <xine/video_overlay.h>
+#endif
+
+#define XINE_HDMV_TRACE(x...) printf(x)
+/*#define XINE_HDMV_TRACE(x...) */
+#define XINE_HDMV_ERROR(x...) fprintf(stderr, "spuhdmv: " x)
+/*#define XINE_HDMV_ERROR(x...) lprintf(x) */
+
+#ifndef EXPORTED
+# define EXPORTED __attribute__((visibility("default")))
+#endif
+
+#ifndef BUF_SPU_HDMV
+# define BUF_SPU_HDMV 0x04080000
+#endif
+
+enum {
+ SEGTYPE_PALETTE = 0x14,
+ SEGTYPE_OBJECT = 0x15,
+ SEGTYPE_PRESENTATION_SEGMENT = 0x16,
+ SEGTYPE_WINDOW_DEFINITION = 0x17,
+ SEGTYPE_INTERACTIVE = 0x18,
+ SEGTYPE_END_OF_DISPLAY = 0x80,
+} eSegmentType;
+
+/*
+ * 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;
+
+ int shown;
+};
+
+/*
+ * 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;
+ unsigned int num_rle;
+ size_t data_size;
+
+#if 0
+ uint8_t *raw_data; /* partial RLE data in HDMV format */
+ size_t raw_data_len;
+ size_t raw_data_size;
+#endif
+
+ subtitle_object_t *next;
+
+ int shown;
+};
+
+/*
+ * 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;
+
+ int shown;
+};
+
+
+/*
+ * 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;
+
+ int shown;
+};
+
+typedef struct composition_descriptor_s composition_descriptor_t;
+struct composition_descriptor_s {
+ uint16_t number;
+ uint8_t state;
+};
+
+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;
+ int shown;
+};
+
+/*
+ * list handling
+ */
+
+#define LIST_REPLACE(list, obj, FREE_FUNC) \
+ do { \
+ unsigned int 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_FUNC(tmp); \
+ } \
+ } while (0);
+
+#define LIST_DESTROY(list, FREE_FUNC) \
+ while (list) { \
+ void *tmp = (void*)list; \
+ list = list->next; \
+ FREE_FUNC(tmp); \
+ }
+
+static void free_subtitle_object(void *ptr)
+{
+ if (ptr) {
+ free(((subtitle_object_t*)ptr)->rle);
+ free(ptr);
+ }
+}
+static void free_presentation_segment(void *ptr)
+{
+ if (ptr) {
+ presentation_segment_t *seg = (presentation_segment_t*)ptr;
+ LIST_DESTROY(seg->comp_objs, free);
+ free(ptr);
+ }
+}
+
+
+/*
+ * 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)) {
+ XINE_HDMV_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);
+
+ XINE_HDMV_TRACE(" skip_segment: %zd bytes left\n", buf->len);
+ } else {
+ XINE_HDMV_ERROR(" skip_segment: ERROR - %zd bytes queued, %d required\n",
+ 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];
+ XINE_HDMV_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);
+}
+
+/*
+ * 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) {
+ XINE_HDMV_ERROR(" decode_palette: segment size error (%zd ; expected %zd for %zd entries)\n",
+ len, (5 * entries), entries);
+ return NULL;
+ }
+ XINE_HDMV_TRACE("decode_palette: %zd items (id %d, version %d)\n",
+ 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);
+
+ XINE_HDMV_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);
+
+ XINE_HDMV_TRACE(" object length %d bytes, size %dx%d\n", data_len, obj->width, obj->height);
+
+ segbuf_decode_rle (buf, obj);
+
+ if (buf->error) {
+ free_subtitle_object(obj);
+ return NULL;
+ }
+
+ } else {
+ XINE_HDMV_ERROR(" TODO: APPEND RLE, length %d bytes\n", buf->segment_len - 4);
+ /* TODO */
+ free_subtitle_object(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);
+
+ XINE_HDMV_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);
+
+ XINE_HDMV_TRACE(" video_descriptor: %dx%d fps %d\n", width, height, frame_rate);
+ return buf->error;
+}
+
+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);
+
+ XINE_HDMV_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;
+ }
+
+ XINE_HDMV_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 presentation_segment_t *segbuf_decode_presentation_segment(segment_buffer_t *buf)
+{
+ presentation_segment_t *seg = calloc(1, sizeof(presentation_segment_t));
+ int index;
+
+ segbuf_decode_video_descriptor (buf);
+ segbuf_decode_composition_descriptor (buf, &seg->comp_descr);
+
+ seg->palette_update_flag = !!((segbuf_get_u8(buf)) & 0x80);
+ seg->palette_id_ref = segbuf_get_u8 (buf);
+ seg->object_number = segbuf_get_u8 (buf);
+
+ XINE_HDMV_TRACE(" presentation_segment: object_number %d, palette %d\n",
+ seg->object_number, seg->palette_id_ref);
+
+ for (index = 0; index < seg->object_number; index++) {
+ composition_object_t *cobj = segbuf_decode_composition_object (buf);
+ cobj->next = seg->comp_objs;
+ seg->comp_objs = cobj;
+ }
+
+ if (buf->error) {
+ free_presentation_segment(seg);
+ return NULL;
+ }
+
+ return seg;
+}
+
+static rle_elem_t *copy_crop_rle(subtitle_object_t *obj, composition_object_t *cobj)
+{
+ /* TODO: cropping (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;
+ presentation_segment_t *segments;
+
+ int overlay_handles[MAX_OBJECTS];
+
+ int64_t pts;
+
+} spuhdmv_decoder_t;
+
+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, free);
+
+ 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, free_subtitle_object);
+
+ 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, free);
+
+ return 0;
+}
+
+static int decode_presentation_segment(spuhdmv_decoder_t *this)
+{
+ /* decode */
+ presentation_segment_t *seg = segbuf_decode_presentation_segment(this->buf);
+ if (!seg)
+ return 1;
+
+ seg->pts = this->pts;
+
+ /* replace */
+ if (this->segments)
+ LIST_DESTROY(this->segments, free_presentation_segment);
+ this->segments = seg;
+
+ return 0;
+}
+
+static int show_overlay(spuhdmv_decoder_t *this, composition_object_t *cobj, unsigned int palette_id_ref,
+ int overlay_index, int64_t pts, int force_update)
+{
+ 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) {
+ XINE_HDMV_TRACE(" show_overlay: clut %d not found !\n", palette_id_ref);
+ return -1;
+ }
+
+ /* find RLE image */
+ subtitle_object_t *obj = this->objects;
+ while (obj && obj->id != cobj->object_id_ref)
+ obj = obj->next;
+ if (!obj) {
+ XINE_HDMV_TRACE(" show_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) {
+ XINE_HDMV_TRACE(" show_overlay: window %d not found !\n", cobj->window_id_ref);
+ return -1;
+ }
+
+ /* do not show again if all elements are unchanged */
+ if (!force_update && clut->shown && obj->shown && wnd->shown && cobj->shown)
+ return 0;
+ clut->shown = obj->shown = wnd->shown = cobj->shown = 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);
+
+ /* 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;
+
+ XINE_HDMV_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);
+
+ return 0;
+}
+
+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) {
+ XINE_HDMV_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 void update_overlays(spuhdmv_decoder_t *this)
+{
+ presentation_segment_t *pseg = this->segments;
+
+ while (pseg) {
+
+ if (!pseg->comp_descr.state) {
+
+ /* HIDE */
+ if (!pseg->shown)
+ hide_overlays (this, pseg->pts);
+
+ } else {
+
+ /* SHOW */
+ composition_object_t *cobj = pseg->comp_objs;
+ int i;
+
+ for (i = 0; i < pseg->object_number; i++) {
+ if (!cobj) {
+ XINE_HDMV_ERROR("show_overlays: composition object %d missing !\n", i);
+ } else {
+ show_overlay(this, cobj, pseg->palette_id_ref, i, pseg->pts, !pseg->shown);
+ cobj = cobj->next;
+ }
+ }
+ }
+
+ pseg->shown = 1;
+
+ pseg = pseg->next;
+ }
+}
+
+static void free_objs(spuhdmv_decoder_t *this)
+{
+ LIST_DESTROY (this->cluts, free);
+ LIST_DESTROY (this->objects, free_subtitle_object);
+ LIST_DESTROY (this->windows, free);
+ LIST_DESTROY (this->segments, free_presentation_segment);
+}
+
+static void decode_segment(spuhdmv_decoder_t *this)
+{
+ XINE_HDMV_TRACE("*** new segment, pts %010"PRId64": 0x%02x (%8d bytes)",
+ this->pts, this->buf->segment_type, this->buf->segment_len);
+
+ switch (segbuf_segment_type(this->buf)) {
+ case SEGTYPE_PALETTE:
+ XINE_HDMV_TRACE(" segment: PALETTE\n");
+ decode_palette(this);
+ break;
+ case SEGTYPE_OBJECT:
+ XINE_HDMV_TRACE(" segment: OBJECT\n");
+ decode_object(this);
+ break;
+ case SEGTYPE_PRESENTATION_SEGMENT:
+ XINE_HDMV_TRACE(" segment: PRESENTATION SEGMENT\n");
+ decode_presentation_segment(this);
+ break;
+ case SEGTYPE_WINDOW_DEFINITION:
+ XINE_HDMV_TRACE(" segment: WINDOW DEFINITION\n");
+ decode_window_definition(this);
+ break;
+ case SEGTYPE_INTERACTIVE:
+ XINE_HDMV_TRACE(" segment: INTERACTIVE\n");
+ break;
+ case SEGTYPE_END_OF_DISPLAY:
+ XINE_HDMV_TRACE(" segment: END OF DISPLAY\n");
+ /* drop all cached objects */
+ free_objs(this);
+ break;
+ default:
+ XINE_HDMV_ERROR(" segment type 0x%x unknown, skipping\n", segbuf_segment_type(this->buf));
+ break;
+ }
+ if (this->buf->error) {
+ XINE_HDMV_ERROR("*** DECODE ERROR ***\n");
+ }
+
+ update_overlays (this);
+}
+
+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);
+
+ free_objs(this);
+
+ 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);
+
+ free_objs(this);
+
+ 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 char *get_identifier (spu_decoder_class_t *this)
+{
+ return "spuhdmv";
+}
+
+static char *get_description (spu_decoder_class_t *this)
+{
+ return "HDMV/BluRay bitmap SPU decoder plugin";
+}
+
+static void dispose_class (spu_decoder_class_t *this)
+{
+ free (this);
+}
+
+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.get_identifier = get_identifier;
+ this->decoder_class.get_description = get_description;
+ this->decoder_class.dispose = dispose_class;
+
+ return this;
+}
+
+/* plugin catalog information */
+static 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, 16, "spuhdmv", XINE_VERSION_CODE, &dec_info_data, &init_plugin },
+ { PLUGIN_NONE, 0, "", 0, NULL, NULL }
+};
diff --git a/xine/BluRay/demux_ts.c b/xine/BluRay/demux_ts.c
new file mode 100644
index 00000000..13bcb859
--- /dev/null
+++ b/xine/BluRay/demux_ts.c
@@ -0,0 +1,2604 @@
+
+/*
+ * Copyright (C) 2000-2003 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 MPEG2 Transport Streams.
+ *
+ * For the purposes of playing video, we make some assumptions about the
+ * kinds of TS we have to process. The most important simplification is to
+ * assume that the TS contains a single program (SPTS) because this then
+ * allows significant simplifications to be made in processing PATs.
+ *
+ * The next simplification is to assume that the program has a reasonable
+ * number of video, audio and other streams. This allows PMT processing to
+ * be simplified.
+ *
+ * MODIFICATION HISTORY
+ *
+ * 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
+ *
+ * 28-Aug-2004 James Courtier-Dutton <jcdutton>
+ * - Improve PAT and PMT handling. Added some FIXME comments.
+ *
+ * 9-Aug-2003 James Courtier-Dutton <jcdutton>
+ * - Improve readability of code. Added some FIXME comments.
+ *
+ * 25-Nov-2002 Peter Liljenberg
+ * - Added DVBSUB support
+ *
+ * 07-Nov-2992 Howdy Pierce
+ * - various bugfixes
+ *
+ * 30-May-2002 Mauro Borghi
+ * - dynamic allocation leaks fixes
+ *
+ * 27-May-2002 Giovanni Baronetti and Mauro Borghi <mauro.borghi@tilab.com>
+ * - fill buffers before putting them in fifos
+ * - force PMT reparsing when PMT PID changes
+ * - accept non seekable input plugins -- FIX?
+ * - accept dvb as input plugin
+ * - optimised read operations
+ * - modified resync code
+ *
+ * 16-May-2002 Thibaut Mattern <tmattern@noos.fr>
+ * - fix demux loop
+ *
+ * 07-Jan-2002 Andr Draszik <andid@gmx.net>
+ * - added support for single-section PMTs
+ * spanning multiple TS packets
+ *
+ * 10-Sep-2001 James Courtier-Dutton <jcdutton>
+ * - re-wrote sync code so that it now does not loose any data
+ *
+ * 27-Aug-2001 Hubert Matthews Reviewed by: n/a
+ * - added in synchronisation code
+ *
+ * 1-Aug-2001 James Courtier-Dutton <jcdutton> Reviewed by: n/a
+ * - TS Streams with zero PES length should now work
+ *
+ * 30-Jul-2001 shaheedhaque Reviewed by: n/a
+ * - PATs and PMTs seem to work
+ *
+ * 29-Jul-2001 shaheedhaque Reviewed by: n/a
+ * - Compiles!
+ *
+ *
+ * TODO: do without memcpys, preview buffers
+ */
+
+
+/** HOW TO IMPLEMENT A DVBSUB DECODER.
+ *
+ * The DVBSUB protocol is specified in ETSI EN 300 743. It can be
+ * downloaded for free (registration required, though) from
+ * www.etsi.org.
+ *
+ * The spu_decoder should handle the packet type BUF_SPU_DVB.
+ *
+ * BUF_SPU_DVBSUB packets without the flag BUF_FLAG_SPECIAL contain
+ * the payload of the PES packets carrying DVBSUB data. Since the
+ * payload can be broken up over several buf_element_t and the DVBSUB
+ * is PES oriented, the decoder_info[2] field (low 16 bits) is used to convey the
+ * packet boundaries to the decoder:
+ *
+ * + For the first buffer of a packet, buf->content points to the
+ * first byte of the PES payload. decoder_info[2] is set to the length of the
+ * payload. The decoder can use this value to determine when a
+ * complete PES packet has been collected.
+ *
+ * + For the following buffers of the PES packet, decoder_info[2] is 0.
+ *
+ * The decoder can either use this information to reconstruct the PES
+ * payload, or ignore it and implement a parser that handles the
+ * irregularites at the start and end of PES packets.
+ *
+ * In any case buf->pts is always set to the PTS of the PES packet.
+ *
+ *
+ * BUF_SPU_DVB with BUF_FLAG_SPECIAL set contains no payload, and is
+ * used to pass control information to the decoder.
+ *
+ * If decoder_info[1] == BUF_SPECIAL_SPU_DVB_DESCRIPTOR then
+ * decoder_info_ptr[2] either points to a spu_dvb_descriptor_t or is NULL.
+ *
+ * If it is 0, the user has disabled the subtitling, or has selected a
+ * channel that is not present in the stream. The decoder should
+ * remove any visible subtitling.
+ *
+ * If it is a pointer, the decoder should reset itself and start
+ * extracting the subtitle service identified by comp_page_id and
+ * aux_page_id in the spu_dvb_descriptor_t, (the composition and
+ * auxilliary page ids, respectively).
+ **/
+
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <string.h>
+
+#define LOG_MODULE "demux_ts"
+#define LOG_VERBOSE
+/*
+#define LOG
+*/
+
+#include <xine/xine_internal.h>
+#include <xine/xineutils.h>
+#include <xine/demux.h>
+
+//#define TS_PMT_LOG
+//#define TS_PAT_LOG
+
+#ifndef EXPORTED
+# define EXPORTED __attribute__((visibility("default")))
+#endif
+
+#ifndef BUF_SPU_HDMV
+# define BUF_SPU_HDMV 0x04080000
+#endif
+
+/*
+ #define TS_LOG
+ #define TS_PMT_LOG
+ #define TS_PAT_LOG
+
+ #define TS_READ_STATS // activates read statistics generation
+ #define TS_HEADER_LOG // prints out the Transport packet header.
+*/
+
+/*
+ * The maximum number of PIDs we are prepared to handle in a single program
+ * is the number that fits in a single-packet PMT.
+ */
+#define PKT_SIZE 188
+#define BODY_SIZE (188 - 4)
+/* more PIDS are needed due "auto-detection". 40 spare media entries */
+#define MAX_PIDS ((BODY_SIZE - 1 - 13) / 4) + 40
+#define MAX_PMTS ((BODY_SIZE - 1 - 13) / 4) + 10
+#define SYNC_BYTE 0x47
+
+#define MIN_SYNCS 3
+#define NPKT_PER_READ 96 // 96*188 = 94*192
+
+#define BUF_SIZE (NPKT_PER_READ * (PKT_SIZE + 4))
+
+#define MAX_PES_BUF_SIZE 2048
+
+#define CORRUPT_PES_THRESHOLD 10
+
+#define NULL_PID 0x1fff
+#define INVALID_PID ((unsigned int)(-1))
+#define INVALID_PROGRAM ((unsigned int)(-1))
+#define INVALID_CC ((unsigned int)(-1))
+
+#define PROG_STREAM_MAP 0xBC
+#define PRIVATE_STREAM1 0xBD
+#define PADDING_STREAM 0xBE
+#define PRIVATE_STREAM2 0xBF
+#define AUDIO_STREAM_S 0xC0
+#define AUDIO_STREAM_E 0xDF
+#define VIDEO_STREAM_S 0xE0
+#define VIDEO_STREAM_E 0xEF
+#define ECM_STREAM 0xF0
+#define EMM_STREAM 0xF1
+#define DSM_CC_STREAM 0xF2
+#define ISO13522_STREAM 0xF3
+#define PROG_STREAM_DIR 0xFF
+
+ typedef enum
+ {
+ ISO_11172_VIDEO = 0x01, /* ISO/IEC 11172 Video */
+ ISO_13818_VIDEO = 0x02, /* ISO/IEC 13818-2 Video */
+ ISO_11172_AUDIO = 0x03, /* ISO/IEC 11172 Audio */
+ ISO_13818_AUDIO = 0x04, /* ISO/IEC 13818-3 Audi */
+ ISO_13818_PRIVATE = 0x05, /* ISO/IEC 13818-1 private sections */
+ ISO_13818_PES_PRIVATE = 0x06, /* ISO/IEC 13818-1 PES packets containing private data */
+ ISO_13522_MHEG = 0x07, /* ISO/IEC 13512 MHEG */
+ ISO_13818_DSMCC = 0x08, /* ISO/IEC 13818-1 Annex A DSM CC */
+ ISO_13818_TYPE_A = 0x0a, /* ISO/IEC 13818-6 Multiprotocol encapsulation */
+ ISO_13818_TYPE_B = 0x0b, /* ISO/IEC 13818-6 DSM-CC U-N Messages */
+ ISO_13818_TYPE_C = 0x0c, /* ISO/IEC 13818-6 Stream Descriptors */
+ ISO_13818_TYPE_D = 0x0d, /* ISO/IEC 13818-6 Sections (any type, including private data) */
+ ISO_13818_AUX = 0x0e, /* ISO/IEC 13818-1 auxiliary */
+ ISO_13818_PART7_AUDIO = 0x0f, /* ISO/IEC 13818-7 Audio with ADTS transport sytax */
+ 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_VC1 = 0xea, /* VC-1 Video */
+ STREAM_VIDEO_SMTPE_VC1 = 0xeb, /* SMTPE VC-1 */
+
+ HDMV_AUDIO_80_PCM = 0x80, /* BluRay PCM */
+ HDMV_AUDIO_82_DTS = 0x82, /* DTS */
+ HDMV_AUDIO_83_TRUEHD = 0x83, /* Dolby TrueHD, primary audio */
+ HDMV_AUDIO_84_EAC3 = 0x84, /* Dolby Digital plus, primary audio */
+ HDMV_AUDIO_85_DTS_HRA = 0x85, /* DTS-HRA */
+ HDMV_AUDIO_86_DTS_HD_MA = 0x86, /* DTS-HD Master audio */
+
+ HDMV_SPU_BITMAP = 0x90,
+ HDMV_SPU_INTERACTIVE = 0x91,
+ HDMV_SPU_TEXT = 0x92,
+
+ } streamType;
+
+#define WRAP_THRESHOLD 270000
+
+#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
+**
+*/
+
+/*
+ * Describe a single elementary stream.
+ */
+typedef struct {
+ unsigned int pid;
+ fifo_buffer_t *fifo;
+ uint8_t *content;
+ uint32_t size;
+ uint32_t type;
+ int64_t pts;
+ buf_element_t *buf;
+ unsigned int counter;
+ uint16_t descriptor_tag; /* +0x100 for PES stream IDs (no available TS descriptor tag?) */
+ int64_t packet_count;
+ int corrupted_pes;
+ uint32_t buffered_bytes;
+ int autodetected;
+
+} demux_ts_media;
+
+/* DVBSUB */
+#define MAX_SPU_LANGS 32
+
+typedef struct {
+ spu_dvb_descriptor_t desc;
+ int pid;
+ int media_index;
+} demux_ts_spu_lang;
+
+/* Audio Channels */
+#define MAX_AUDIO_TRACKS 32
+
+typedef struct {
+ int pid;
+ int media_index;
+ char lang[4];
+} demux_ts_audio_track;
+
+typedef struct {
+ /*
+ * The first field must be the "base class" for the plugin!
+ */
+ demux_plugin_t demux_plugin;
+
+ xine_stream_t *stream;
+
+ config_values_t *config;
+
+ fifo_buffer_t *audio_fifo;
+ fifo_buffer_t *video_fifo;
+
+ input_plugin_t *input;
+
+ 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;
+ demux_ts_media media[MAX_PIDS];
+ uint32_t program_number[MAX_PMTS];
+ uint32_t pmt_pid[MAX_PMTS];
+ uint8_t *pmt[MAX_PMTS];
+ uint8_t *pmt_write_ptr[MAX_PMTS];
+ uint32_t crc32_table[256];
+ uint32_t last_pmt_crc;
+ /*
+ * Stuff to do with the transport header. As well as the video
+ * and audio PIDs, we keep the index of the corresponding entry
+ * inthe media[] array.
+ */
+ unsigned int programNumber;
+ unsigned int pcrPid;
+ unsigned int pid;
+ unsigned int pid_count;
+ unsigned int videoPid;
+ unsigned int videoMedia;
+
+ demux_ts_audio_track audio_tracks[MAX_AUDIO_TRACKS];
+ int audio_tracks_count;
+
+ int send_end_buffers;
+ int64_t last_pts[2];
+ int send_newpts;
+ int buf_flag_seek;
+
+ unsigned int scrambled_pids[MAX_PIDS];
+ unsigned int scrambled_npids;
+
+#ifdef TS_READ_STATS
+ uint32_t rstat[NPKT_PER_READ + 1];
+#endif
+
+ /* DVBSUB */
+ unsigned int spu_pid;
+ unsigned int spu_media;
+ demux_ts_spu_lang spu_langs[MAX_SPU_LANGS];
+ int spu_langs_count;
+ int current_spu_channel;
+
+ /* dvb */
+ xine_event_queue_t *event_queue;
+ /* For syncronisation */
+ int32_t packet_number;
+ /* NEW: var to keep track of number of last read packets */
+ int32_t npkt_read;
+
+ uint8_t buf[BUF_SIZE]; /* == PKT_SIZE * NPKT_PER_READ */
+
+ int numPreview;
+
+} demux_ts_t;
+
+typedef struct {
+
+ demux_class_t demux_class;
+
+ /* class-wide, global variables here */
+
+ xine_t *xine;
+ config_values_t *config;
+} demux_ts_class_t;
+
+
+static void demux_ts_build_crc32_table(demux_ts_t*this) {
+ uint32_t i, j, k;
+
+ for( i = 0 ; i < 256 ; i++ ) {
+ k = 0;
+ for (j = (i << 24) | 0x800000 ; j != 0x80000000 ; j <<= 1) {
+ k = (k << 1) ^ (((k ^ j) & 0x80000000) ? 0x04c11db7 : 0);
+ }
+ this->crc32_table[i] = k;
+ }
+}
+
+static uint32_t demux_ts_compute_crc32(demux_ts_t*this, uint8_t *data,
+ int32_t length, uint32_t crc32) {
+ int32_t i;
+
+ for(i = 0; i < length; i++) {
+ crc32 = (crc32 << 8) ^ this->crc32_table[(crc32 >> 24) ^ data[i]];
+ }
+ return crc32;
+}
+
+/* redefine abs as macro to handle 64-bit diffs.
+ i guess llabs may not be available everywhere */
+#define abs(x) ( ((x)<0) ? -(x) : (x) )
+
+static void check_newpts( demux_ts_t *this, int64_t pts, int video )
+{
+ int64_t diff;
+
+#ifdef TS_LOG
+ printf ("demux_ts: check_newpts %lld, send_newpts %d, buf_flag_seek %d\n",
+ pts, this->send_newpts, this->buf_flag_seek);
+#endif
+
+ diff = pts - this->last_pts[video];
+
+ if( pts &&
+ (this->send_newpts || (this->last_pts[video] && abs(diff)>WRAP_THRESHOLD) ) ) {
+
+ if (this->buf_flag_seek) {
+ _x_demux_control_newpts(this->stream, pts, BUF_FLAG_SEEK);
+ this->buf_flag_seek = 0;
+ } else {
+ _x_demux_control_newpts(this->stream, pts, 0);
+ }
+ this->send_newpts = 0;
+ this->last_pts[1-video] = 0;
+ }
+
+ if( pts )
+ {
+ /* don't detect a discontinuity only for video respectively audio. It's also a discontinuity
+ indication when audio and video pts differ to much e. g. when a pts wrap happens.
+ The original code worked well when the wrap happend like this:
+
+ V7 A7 V8 V9 A9 Dv V0 V1 da A1 V2 V3 A3 V4
+
+ Legend:
+ Vn = video packet with timestamp n
+ An = audio packet with timestamp n
+ Dv = discontinuity detected on following video packet
+ Da = discontinuity detected on following audio packet
+ dv = discontinuity detected on following video packet but ignored
+ da = discontinuity detected on following audio packet but ignored
+
+ But with a certain delay between audio and video packets (e. g. the way DVB-S broadcasts
+ the packets) the code didn't work:
+
+ V7 V8 A7 V9 Dv V0 _A9_ V1 V2 Da _A1_ V3 V4 A3
+
+ Packet A9 caused audio to jump forward and A1 caused it to jump backward with inserting
+ a delay of almoust 26.5 hours!
+
+ The new code gives the following sequences for the above examples:
+
+ V7 A7 V8 V9 A9 Dv V0 V1 A1 V2 V3 A3 V4
+
+ V7 V8 A7 V9 Dv V0 Da A9 Dv V1 V2 A1 V3 V4 A3
+
+ After proving this code it should be cleaned up to use just a single variable "last_pts". */
+
+/*
+ this->last_pts[video] = pts;
+*/
+ this->last_pts[video] = this->last_pts[1-video] = pts;
+ }
+}
+
+/* Send a BUF_SPU_DVB to let xine know of that 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 = spu_type|spu_channel;
+ buf->content = buf->mem;
+ buf->size = 0;
+ this->video_fifo->put( this->video_fifo, buf );
+}
+
+static void demux_send_special_audio_buf( demux_ts_t *this, uint16_t stream_type, int audio_channel )
+{
+ uint32_t type = 0;
+
+ switch(stream_type) {
+ case ISO_11172_AUDIO:
+ case ISO_13818_AUDIO: type = BUF_AUDIO_MPEG; break;
+ case ISO_13818_PART7_AUDIO:
+ case ISO_14496_PART3_AUDIO: type = BUF_AUDIO_AAC; break;
+ default:
+ case STREAM_AUDIO_AC3: type = BUF_AUDIO_A52; break;
+ }
+
+ if (this->hdmv > 0) {
+ switch(stream_type) {
+ case HDMV_AUDIO_80_PCM: type = BUF_AUDIO_LPCM_BE; break;
+ case HDMV_AUDIO_83_TRUEHD:
+ case STREAM_AUDIO_AC3: type = BUF_AUDIO_A52; break;
+ case HDMV_AUDIO_82_DTS:
+ case HDMV_AUDIO_85_DTS_HRA:
+ case HDMV_AUDIO_86_DTS_HD_MA: type = BUF_AUDIO_DTS; break;
+ }
+ }
+
+ if (type && this->audio_fifo) {
+ buf_element_t *buf = this->audio_fifo->buffer_pool_alloc( this->audio_fifo );
+ buf->type = type | audio_channel;
+ buf->decoder_flags = BUF_FLAG_PREVIEW;
+ buf->content = buf->mem;
+ buf->size = 0;
+ this->audio_fifo->put( this->audio_fifo, buf );
+ }
+}
+
+/*
+ * demux_ts_update_spu_channel
+ *
+ * Send a BUF_SPU_DVB with BUF_SPECIAL_SPU_DVB_DESCRIPTOR to tell
+ * the decoder to reset itself on the new channel.
+ */
+static void demux_ts_update_spu_channel(demux_ts_t *this)
+{
+ buf_element_t *buf;
+
+ this->current_spu_channel = this->stream->spu_channel;
+
+ buf = this->video_fifo->buffer_pool_alloc(this->video_fifo);
+ buf->type = BUF_SPU_DVB;
+ buf->content = buf->mem;
+ buf->decoder_flags = BUF_FLAG_SPECIAL;
+ buf->decoder_info[1] = BUF_SPECIAL_SPU_DVB_DESCRIPTOR;
+ buf->size = 0;
+
+ if (this->current_spu_channel >= 0
+ && this->current_spu_channel < this->spu_langs_count)
+ {
+ demux_ts_spu_lang *lang = &this->spu_langs[this->current_spu_channel];
+
+ buf->decoder_info[2] = sizeof(lang->desc);
+ buf->decoder_info_ptr[2] = &(lang->desc);
+ buf->type |= this->current_spu_channel;
+
+ this->spu_pid = lang->pid;
+ this->spu_media = lang->media_index;
+
+#ifdef TS_LOG
+ printf("demux_ts: DVBSUB: selecting lang: %s page %ld %ld\n",
+ lang->desc.lang, lang->desc.comp_page_id, lang->desc.aux_page_id);
+#endif
+ }
+ else
+ {
+ buf->decoder_info_ptr[2] = NULL;
+
+ this->spu_pid = INVALID_PID;
+
+#ifdef TS_LOG
+ printf("demux_ts: DVBSUB: deselecting lang\n");
+#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);
+}
+
+/*
+ * demux_ts_parse_pat
+ *
+ * Parse a program association table (PAT).
+ * The PAT is expected to be exactly one section long,
+ * and that section is expected to be contained in a single TS packet.
+ *
+ * The PAT is assumed to contain a single program definition, though
+ * we can cope with the stupidity of SPTSs which contain NITs.
+ */
+static void demux_ts_parse_pat (demux_ts_t*this, unsigned char *original_pkt,
+ unsigned char *pkt, unsigned int pusi) {
+ uint32_t table_id;
+ uint32_t section_syntax_indicator;
+ int32_t section_length;
+ uint32_t transport_stream_id;
+ uint32_t version_number;
+ uint32_t current_next_indicator;
+ uint32_t section_number;
+ uint32_t last_section_number;
+ uint32_t crc32;
+ uint32_t calc_crc32;
+
+ unsigned char *program;
+ unsigned int program_number;
+ unsigned int pmt_pid;
+ unsigned int program_count;
+
+ /*
+ * A PAT in a single section should start with a payload unit start
+ * indicator set.
+ */
+ if (!pusi) {
+ xprintf (this->stream->xine, XINE_VERBOSITY_DEBUG,
+ "demux_ts: demux error! PAT without payload unit start indicator\n");
+ return;
+ }
+
+ /*
+ * sections start with a pointer. Skip it!
+ */
+ pkt += pkt[4];
+ if (pkt - original_pkt > PKT_SIZE) {
+ xprintf (this->stream->xine, XINE_VERBOSITY_DEBUG,
+ "demux_ts: demux error! PAT with invalid pointer\n");
+ return;
+ }
+ table_id = (unsigned int)pkt[5] ;
+ section_syntax_indicator = (((unsigned int)pkt[6] >> 7) & 1) ;
+ section_length = (((unsigned int)pkt[6] & 0x03) << 8) | pkt[7];
+ transport_stream_id = ((uint32_t)pkt[8] << 8) | pkt[9];
+ version_number = ((uint32_t)pkt[10] >> 1) & 0x1f;
+ current_next_indicator = ((uint32_t)pkt[10] & 0x01);
+ section_number = (uint32_t)pkt[11];
+ last_section_number = (uint32_t)pkt[12];
+ crc32 = (uint32_t)pkt[4+section_length] << 24;
+ crc32 |= (uint32_t)pkt[5+section_length] << 16;
+ crc32 |= (uint32_t)pkt[6+section_length] << 8;
+ crc32 |= (uint32_t)pkt[7+section_length] ;
+
+#ifdef TS_PAT_LOG
+ printf ("demux_ts: PAT table_id: %.2x\n", table_id);
+ printf (" section_syntax: %d\n", section_syntax_indicator);
+ printf (" section_length: %d (%#.3x)\n",
+ section_length, section_length);
+ printf (" transport_stream_id: %#.4x\n", transport_stream_id);
+ printf (" version_number: %d\n", version_number);
+ printf (" c/n indicator: %d\n", current_next_indicator);
+ printf (" section_number: %d\n", section_number);
+ printf (" last_section_number: %d\n", last_section_number);
+#endif
+
+ if ((section_syntax_indicator != 1) || !(current_next_indicator)) {
+ return;
+ }
+
+ if (pkt - original_pkt > BODY_SIZE - 1 - 3 - section_length) {
+ xprintf (this->stream->xine, XINE_VERBOSITY_DEBUG,
+ "demux_ts: FIXME: (unsupported )PAT spans multiple TS packets\n");
+ return;
+ }
+
+ if ((section_number != 0) || (last_section_number != 0)) {
+ xprintf (this->stream->xine, XINE_VERBOSITY_DEBUG,
+ "demux_ts: FIXME: (unsupported) PAT consists of multiple (%d) sections\n", last_section_number);
+ return;
+ }
+
+ /* Check CRC. */
+ calc_crc32 = demux_ts_compute_crc32 (this, pkt+5, section_length+3-4,
+ 0xffffffff);
+ if (crc32 != calc_crc32) {
+ xprintf (this->stream->xine, XINE_VERBOSITY_DEBUG,
+ "demux_ts: demux error! PAT with invalid CRC32: packet_crc32: %.8x calc_crc32: %.8x\n",
+ crc32,calc_crc32);
+ return;
+ }
+#ifdef TS_PAT_LOG
+ else {
+ printf ("demux_ts: PAT CRC32 ok.\n");
+ }
+#endif
+
+ /*
+ * Process all programs in the program loop.
+ */
+ program_count = 0;
+ for (program = pkt + 13;
+ program < pkt + 13 + section_length - 9;
+ program += 4) {
+ program_number = ((unsigned int)program[0] << 8) | program[1];
+ pmt_pid = (((unsigned int)program[2] & 0x1f) << 8) | program[3];
+
+ /*
+ * completely skip NIT pids.
+ */
+ if (program_number == 0x0000)
+ continue;
+
+ /*
+ * If we have yet to learn our program number, then learn it,
+ * use this loop to eventually add support for dynamically changing
+ * PATs.
+ */
+ program_count = 0;
+
+ while ((this->program_number[program_count] != INVALID_PROGRAM) &&
+ (this->program_number[program_count] != program_number) &&
+ (program_count+1 < MAX_PMTS ) ) {
+ program_count++;
+ }
+ this->program_number[program_count] = program_number;
+
+ /* force PMT reparsing when pmt_pid changes */
+ if (this->pmt_pid[program_count] != pmt_pid) {
+ this->pmt_pid[program_count] = pmt_pid;
+ this->audio_tracks_count = 0;
+ this->last_pmt_crc = 0;
+ this->videoPid = INVALID_PID;
+ this->spu_pid = INVALID_PID;
+
+ this->pmt_pid[program_count] = pmt_pid;
+
+ if (this->pmt[program_count] != NULL) {
+ free(this->pmt[program_count]);
+ this->pmt[program_count] = NULL;
+ this->pmt_write_ptr[program_count] = NULL;
+ }
+ }
+
+#ifdef TS_PAT_LOG
+ if (this->program_number[program_count] != INVALID_PROGRAM)
+ printf ("demux_ts: PAT acquired count=%d programNumber=0x%04x "
+ "pmtPid=0x%04x\n",
+ program_count,
+ this->program_number[program_count],
+ this->pmt_pid[program_count]);
+#endif
+ }
+}
+
+static int demux_ts_parse_pes_header (xine_t *xine, demux_ts_media *m,
+ uint8_t *buf, int packet_len,
+ xine_stream_t *stream) {
+
+ unsigned char *p;
+ uint32_t header_len;
+ int64_t pts;
+ uint32_t stream_id;
+ int pkt_len;
+
+ p = buf;
+ pkt_len = packet_len;
+
+ /* we should have a PES packet here */
+
+ if (p[0] || p[1] || (p[2] != 1)) {
+ xprintf (xine, XINE_VERBOSITY_DEBUG,
+ "demux_ts: error %02x %02x %02x (should be 0x000001) \n", p[0], p[1], p[2]);
+ return 0 ;
+ }
+
+ packet_len -= 6;
+ /* packet_len = p[4] << 8 | p[5]; */
+ stream_id = p[3];
+
+ if (packet_len==0)
+ {
+ xprintf (xine, XINE_VERBOSITY_DEBUG,
+ "demux_ts: error pes length 0\n");
+ return 0;
+ }
+
+#ifdef TS_LOG
+ printf ("demux_ts: packet stream id: %.2x len: %d (%x)\n",
+ stream_id, packet_len, packet_len);
+#endif
+
+ if (p[7] & 0x80) { /* pts avail */
+
+ pts = (int64_t)(p[ 9] & 0x0E) << 29 ;
+ pts |= p[10] << 22 ;
+ pts |= (p[11] & 0xFE) << 14 ;
+ pts |= p[12] << 7 ;
+ pts |= (p[13] & 0xFE) >> 1 ;
+
+ } else
+ pts = 0;
+
+ /* code works but not used in xine
+ if (p[7] & 0x40) {
+
+ DTS = (p[14] & 0x0E) << 29 ;
+ DTS |= p[15] << 22 ;
+ DTS |= (p[16] & 0xFE) << 14 ;
+ DTS |= p[17] << 7 ;
+ DTS |= (p[18] & 0xFE) >> 1 ;
+
+ } else
+ DTS = 0;
+ */
+
+ m->pts = pts;
+
+ header_len = p[8];
+
+ /* sometimes corruption on header_len causes segfault in memcpy below */
+ if (header_len + 9 > pkt_len) {
+ xprintf (xine, XINE_VERBOSITY_DEBUG,
+ "demux_ts: illegal value for PES_header_data_length (0x%x)\n", header_len);
+ return 0;
+ }
+
+ p += header_len + 9;
+ packet_len -= header_len + 3;
+
+ if (m->descriptor_tag == STREAM_VIDEO_VC1) {
+ m->content = p;
+ m->size = packet_len;
+ m->type = BUF_VIDEO_VC1;
+ return 1;
+ }
+
+ if (m->descriptor_tag == HDMV_SPU_BITMAP) {
+ 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;
+
+ lprintf ("audio buf = %02X %02X %02X %02X %02X %02X %02X %02X\n",
+ p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7]);
+
+ /*
+ * we check the descriptor tag first because some stations
+ * do not include any of the ac3 header info in their audio tracks
+ * these "raw" streams may begin with a byte that looks like a stream type.
+ * For audio streams, m->type already contains the stream no.
+ */
+ if((m->descriptor_tag == STREAM_AUDIO_AC3) || /* ac3 - raw */
+ (p[0] == 0x0B && p[1] == 0x77)) { /* ac3 - syncword */
+ m->content = p;
+ m->size = packet_len;
+ m->type |= BUF_AUDIO_A52;
+ return 1;
+
+ } else if (m->descriptor_tag == HDMV_AUDIO_83_TRUEHD) {
+ /* TODO: separate AC3 and TrueHD streams ... */
+ m->content = p;
+ m->size = packet_len;
+ m->type |= BUF_AUDIO_A52;
+ return 1;
+
+ } else if (m->descriptor_tag == HDMV_AUDIO_82_DTS ||
+ m->descriptor_tag == HDMV_AUDIO_85_DTS_HRA ||
+ m->descriptor_tag == HDMV_AUDIO_86_DTS_HD_MA ) {
+ m->content = p;
+ m->size = packet_len;
+ m->type |= BUF_AUDIO_DTS;
+ return 1;
+
+ } else if (m->descriptor_tag == HDMV_AUDIO_80_PCM) {
+
+ m->content = p + 4;
+ m->size = packet_len - 4;
+ m->type |= BUF_AUDIO_LPCM_BE;
+
+ m->buf->decoder_flags |= BUF_FLAG_SPECIAL;
+ m->buf->decoder_info[1] = BUF_SPECIAL_LPCM_CONFIG;
+ m->buf->decoder_info[2] = (p[3]<<24) | (p[2]<<16) | (p[1]<<8) | p[0];
+
+ return 1;
+
+ } else if (m->descriptor_tag == ISO_13818_PES_PRIVATE
+ && p[0] == 0x20 && p[1] == 0x00) {
+ /* DVBSUB */
+ long payload_len = ((buf[4] << 8) | buf[5]) - header_len - 3;
+
+ m->content = p;
+ m->size = packet_len;
+ m->type |= BUF_SPU_DVB;
+ m->buf->decoder_info[2] = payload_len;
+ return 1;
+ } else if ((p[0] & 0xE0) == 0x20) {
+ spu_id = (p[0] & 0x1f);
+
+ m->content = p+1;
+ m->size = packet_len-1;
+ m->type = BUF_SPU_DVD + spu_id;
+ return 1;
+ } else if ((p[0] & 0xF0) == 0x80) {
+
+ m->content = p+4;
+ m->size = packet_len - 4;
+ m->type |= BUF_AUDIO_A52;
+ return 1;
+
+ } else if ((p[0]&0xf0) == 0xa0) {
+
+ int pcm_offset;
+
+ for (pcm_offset=0; ++pcm_offset < packet_len-1 ; ){
+ if (p[pcm_offset] == 0x01 && p[pcm_offset+1] == 0x80 ) { /* START */
+ pcm_offset += 2;
+ break;
+ }
+ }
+
+ m->content = p+pcm_offset;
+ m->size = packet_len-pcm_offset;
+ m->type |= BUF_AUDIO_LPCM_BE;
+ return 1;
+ }
+
+ } else if ((stream_id >= 0xbc) && ((stream_id & 0xf0) == 0xe0)) {
+
+ m->content = p;
+ m->size = packet_len;
+ switch (m->descriptor_tag) {
+ case ISO_11172_VIDEO:
+ case ISO_13818_VIDEO:
+ case STREAM_VIDEO_MPEG:
+ lprintf ("demux_ts: found MPEG video type.\n");
+ m->type = BUF_VIDEO_MPEG;
+ break;
+ case ISO_14496_PART2_VIDEO:
+ lprintf ("demux_ts: found MPEG4 video type.\n");
+ m->type = BUF_VIDEO_MPEG4;
+ break;
+ case ISO_14496_PART10_VIDEO:
+ lprintf ("demux_ts: found H264 video type.\n");
+ m->type = BUF_VIDEO_H264;
+ break;
+ default:
+ lprintf ("demux_ts: unknown video type: %d, defaulting to MPEG.\n", m->descriptor_tag);
+ m->type = BUF_VIDEO_MPEG;
+ break;
+ }
+ return 1;
+
+ } else if ((stream_id & 0xe0) == 0xc0) {
+
+ m->content = p;
+ m->size = packet_len;
+ switch (m->descriptor_tag) {
+ case ISO_11172_AUDIO:
+ case ISO_13818_AUDIO:
+ lprintf ("demux_ts: found MPEG audio track.\n");
+ m->type |= BUF_AUDIO_MPEG;
+ break;
+ case ISO_13818_PART7_AUDIO:
+ case ISO_14496_PART3_AUDIO:
+ lprintf ("demux_ts: found AAC audio track.\n");
+ m->type |= BUF_AUDIO_AAC;
+ break;
+ default:
+ lprintf ("demux_ts: unknown audio type: %d, defaulting to MPEG.\n", m->descriptor_tag);
+ m->type |= BUF_AUDIO_MPEG;
+ break;
+ }
+ return 1;
+
+ } else {
+#ifdef TS_LOG
+ printf ("demux_ts: unknown packet, id: %x\n", stream_id);
+#endif
+ }
+
+ return 0 ;
+}
+
+
+/*
+ * buffer arriving pes data
+ */
+static void demux_ts_buffer_pes(demux_ts_t*this, unsigned char *ts,
+ unsigned int mediaIndex,
+ unsigned int pus,
+ unsigned int cc,
+ unsigned int len) {
+
+ demux_ts_media *m = &this->media[mediaIndex];
+
+ if (!m->fifo) {
+#ifdef TS_LOG
+ printf ("fifo unavailable (%d)\n", mediaIndex);
+#endif
+ return; /* To avoid segfault if video out or audio out plugin not loaded */
+ }
+
+ /* By checking the CC here, we avoid the need to check for the no-payload
+ case (i.e. adaptation field only) when it does not get bumped. */
+ if (m->counter != INVALID_CC) {
+ if ((m->counter & 0x0f) != cc) {
+ xprintf(this->stream->xine, XINE_VERBOSITY_DEBUG,
+ "demux_ts: PID 0x%.4x: unexpected cc %d (expected %d)\n", m->pid, cc, m->counter);
+ }
+ }
+ m->counter = cc;
+ m->counter++;
+
+ if (pus) { /* new PES packet */
+
+ if (m->buffered_bytes) {
+ m->buf->content = m->buf->mem;
+ m->buf->size = m->buffered_bytes;
+ m->buf->type = m->type;
+ if( (m->buf->type & 0xffff0000) == BUF_SPU_DVD ) {
+ m->buf->decoder_flags |= BUF_FLAG_SPECIAL;
+ m->buf->decoder_info[1] = BUF_SPECIAL_SPU_DVD_SUBTYPE;
+ m->buf->decoder_info[2] = SPU_DVD_SUBTYPE_PACKAGE;
+ }
+ else {
+ m->buf->decoder_flags |= BUF_FLAG_FRAME_END;
+ }
+ m->buf->pts = m->pts;
+ m->buf->decoder_info[0] = 1;
+
+ if( this->input->get_length (this->input) )
+ m->buf->extra_info->input_normpos = (int)( (double) this->input->get_current_pos (this->input) *
+ 65535 / this->input->get_length (this->input) );
+ if (this->rate)
+ m->buf->extra_info->input_time = (int)((int64_t)this->input->get_current_pos (this->input)
+ * 1000 / (this->rate * 50));
+ m->fifo->put(m->fifo, m->buf);
+ m->buffered_bytes = 0;
+ m->buf = NULL; /* forget about buf -- not our responsibility anymore */
+#ifdef TS_LOG
+ printf ("demux_ts: produced buffer, pts=%lld\n", m->pts);
+#endif
+ }
+ /* allocate the buffer here, as pes_header needs a valid buf for dvbsubs */
+ m->buf = m->fifo->buffer_pool_alloc(m->fifo);
+
+ if (!demux_ts_parse_pes_header(this->stream->xine, m, ts, len, this->stream)) {
+ m->buf->free_buffer(m->buf);
+ m->buf = NULL;
+
+ if (m->corrupted_pes > CORRUPT_PES_THRESHOLD && m->autodetected) {
+ if (this->videoPid == m->pid) {
+ this->videoPid = INVALID_PID;
+ this->last_pmt_crc = 0;
+ }
+ } else {
+ m->corrupted_pes++;
+ xprintf(this->stream->xine, XINE_VERBOSITY_DEBUG,
+ "demux_ts: PID 0x%.4x: corrupted pes encountered\n", m->pid);
+ }
+ } else {
+
+ m->corrupted_pes = 0;
+ memcpy(m->buf->mem, ts+len-m->size, m->size);
+ m->buffered_bytes = m->size;
+ }
+
+ } else if (!m->corrupted_pes) { /* no pus -- PES packet continuation */
+
+ if ((m->buffered_bytes + len) > MAX_PES_BUF_SIZE) {
+ m->buf->content = m->buf->mem;
+ m->buf->size = m->buffered_bytes;
+ m->buf->type = m->type;
+ m->buf->pts = m->pts;
+ m->buf->decoder_info[0] = 1;
+ if( this->input->get_length (this->input) )
+ m->buf->extra_info->input_normpos = (int)( (double) this->input->get_current_pos (this->input) *
+ 65535 / this->input->get_length (this->input) );
+ if (this->rate)
+ m->buf->extra_info->input_time = (int)((int64_t)this->input->get_current_pos (this->input)
+ * 1000 / (this->rate * 50));
+
+ m->fifo->put(m->fifo, m->buf);
+ m->buffered_bytes = 0;
+ m->buf = m->fifo->buffer_pool_alloc(m->fifo);
+
+#ifdef TS_LOG
+ printf ("demux_ts: produced buffer, pts=%lld\n", m->pts);
+#endif
+
+ }
+ memcpy(m->buf->mem + m->buffered_bytes, ts, len);
+ m->buffered_bytes += len;
+ }
+}
+
+/*
+ * Create a buffer for a PES stream.
+ */
+static void demux_ts_pes_new(demux_ts_t*this,
+ unsigned int mediaIndex,
+ unsigned int pid,
+ fifo_buffer_t *fifo,
+ uint16_t descriptor) {
+
+ demux_ts_media *m = &this->media[mediaIndex];
+
+ /* new PID seen - initialise stuff */
+ m->pid = pid;
+ m->fifo = fifo;
+
+ if (m->buf != NULL) m->buf->free_buffer(m->buf);
+ m->buf = NULL;
+ m->counter = INVALID_CC;
+ m->descriptor_tag = descriptor;
+ m->corrupted_pes = 1;
+ m->buffered_bytes = 0;
+}
+
+
+/* Find the first ISO 639 language descriptor (tag 10) and
+ * store the 3-char code in dest, nullterminated. If no
+ * code is found, zero out dest.
+ **/
+static void demux_ts_get_lang_desc(demux_ts_t *this, char *dest,
+ const unsigned char *data, int length)
+{
+ const unsigned char *d = data;
+
+ while (d < (data + length))
+
+ {
+ if (d[0] == 10 && d[1] >= 4)
+
+ {
+ memcpy(dest, d + 2, 3);
+ dest[3] = 0;
+ xprintf(this->stream->xine, XINE_VERBOSITY_DEBUG, "demux_ts: found ISO 639 lang: %s\n", dest);
+ return;
+ }
+ d += 2 + d[1];
+ }
+ xprintf(this->stream->xine, XINE_VERBOSITY_DEBUG, "demux_ts: found no ISO 639 lang\n");
+ memset(dest, 0, 4);
+}
+
+/* Find the registration code (tag=5) and return it as a uint32_t
+ * This should return "AC-3" or 0x41432d33 for AC3/A52 audio tracks.
+ */
+static void demux_ts_get_reg_desc(demux_ts_t *this, uint32_t *dest,
+ const unsigned char *data, int length)
+{
+ const unsigned char *d = data;
+
+ while (d < (data + length))
+
+ {
+ if (d[0] == 5 && d[1] >= 4)
+
+ {
+ *dest = (d[2] << 24) | (d[3] << 16) | (d[4] << 8) | d[5];
+ xprintf(this->stream->xine, XINE_VERBOSITY_DEBUG,
+ "demux_ts: found registration format identifier: 0x%.4x\n", *dest);
+ return;
+ }
+ d += 2 + d[1];
+ }
+ xprintf(this->stream->xine, XINE_VERBOSITY_DEBUG, "demux_ts: found no format id\n");
+ *dest = 0;
+}
+
+static inline int ts_payloadsize(unsigned char * tsp)
+{
+ if (!(tsp[3] & 0x10))
+ return 0;
+ if (tsp[3] & 0x20) {
+ if (tsp[4] > 183)
+ return 0;
+ else
+ return 183 - tsp[4];
+ }
+ return 184;
+}
+
+
+/*
+ * NAME demux_ts_parse_pmt
+ *
+ * Parse a PMT. The PMT is expected to be exactly one section long,
+ * and that section is expected to be contained in a single TS packet.
+ *
+ * In other words, the PMT is assumed to describe a reasonable number of
+ * video, audio and other streams (with descriptors).
+ * FIXME: Implement support for multi section PMT.
+ */
+static void demux_ts_parse_pmt (demux_ts_t *this,
+ unsigned char *originalPkt,
+ unsigned char *pkt,
+ unsigned int pusi,
+ uint32_t program_count) {
+
+ uint32_t table_id;
+ uint32_t section_syntax_indicator;
+ uint32_t section_length = 0; /* to calm down gcc */
+ uint32_t program_number;
+ uint32_t version_number;
+ uint32_t current_next_indicator;
+ uint32_t section_number;
+ uint32_t last_section_number;
+ uint32_t program_info_length;
+ uint32_t crc32;
+ uint32_t calc_crc32;
+ uint32_t coded_length;
+ unsigned int pid;
+ unsigned char *stream;
+ unsigned int i;
+ int count;
+ char *ptr = NULL;
+ unsigned char len;
+ unsigned int offset=0;
+
+ /*
+ * A new section should start with the payload unit start
+ * indicator set. We allocate some mem (max. allowed for a PM section)
+ * to copy the complete section into one chunk.
+ */
+ if (pusi) {
+ pkt+=pkt[4]; /* pointer to start of section */
+ offset=1;
+
+ if (this->pmt[program_count] != NULL) free(this->pmt[program_count]);
+ this->pmt[program_count] = (uint8_t *) calloc(4096, sizeof(unsigned char));
+ this->pmt_write_ptr[program_count] = this->pmt[program_count];
+
+ table_id = pkt[5] ;
+ section_syntax_indicator = (pkt[6] >> 7) & 0x01;
+ section_length = (((uint32_t) pkt[6] << 8) | pkt[7]) & 0x03ff;
+ program_number = ((uint32_t) pkt[8] << 8) | pkt[9];
+ version_number = (pkt[10] >> 1) & 0x1f;
+ current_next_indicator = pkt[10] & 0x01;
+ section_number = pkt[11];
+ last_section_number = pkt[12];
+
+#ifdef TS_PMT_LOG
+ printf ("demux_ts: PMT table_id: %2x\n", table_id);
+ printf (" section_syntax: %d\n", section_syntax_indicator);
+ printf (" section_length: %d (%#.3x)\n",
+ section_length, section_length);
+ printf (" program_number: %#.4x\n", program_number);
+ printf (" version_number: %d\n", version_number);
+ printf (" c/n indicator: %d\n", current_next_indicator);
+ printf (" section_number: %d\n", section_number);
+ printf (" last_section_number: %d\n", last_section_number);
+#endif
+
+ if ((section_syntax_indicator != 1) || !current_next_indicator) {
+#ifdef TS_PMT_LOG
+ printf ("ts_demux: section_syntax_indicator != 1 "
+ "|| !current_next_indicator\n");
+#endif
+ return;
+ }
+
+ if (program_number != this->program_number[program_count]) {
+ /* several programs can share the same PMT pid */
+#ifdef TS_PMT_LOG
+printf("Program Number is %i, looking for %i\n",program_number,this->program_number[program_count]);
+ printf ("ts_demux: waiting for next PMT on this PID...\n");
+#endif
+ return;
+ }
+
+ if ((section_number != 0) || (last_section_number != 0)) {
+ xprintf (this->stream->xine, XINE_VERBOSITY_DEBUG,
+ "demux_ts: FIXME (unsupported) PMT consists of multiple (%d) sections\n", last_section_number);
+ return;
+ }
+
+ }
+
+ if (!this->pmt[program_count]) {
+ /* not the first TS packet of a PMT, or the calloc didn't work */
+#ifdef TS_PMT_LOG
+ printf ("ts_demux: not the first TS packet of a PMT...\n");
+#endif
+ return;
+ }
+
+ if (!pusi){
+ section_length = (this->pmt[program_count][1] << 8
+ | this->pmt[program_count][2]) & 0x03ff;
+ }
+
+ count=ts_payloadsize(originalPkt);
+
+ ptr = originalPkt+offset+(PKT_SIZE-count);
+ len = count-offset;
+ memcpy (this->pmt_write_ptr[program_count], ptr, len);
+ this->pmt_write_ptr[program_count] +=len;
+
+#ifdef TS_PMT_LOG
+ printf ("ts_demux: wr_ptr: %p, will be %p when finished\n",
+ this->pmt_write_ptr[program_count],
+ this->pmt[program_count] + section_length);
+#endif
+ if (this->pmt_write_ptr[program_count] < this->pmt[program_count]
+ + section_length) {
+ /* didn't get all TS packets for this section yet */
+#ifdef TS_PMT_LOG
+ printf ("ts_demux: didn't get all PMT TS packets yet...\n");
+#endif
+ return;
+ }
+
+ if (!section_length) {
+ free (this->pmt[program_count]);
+ this->pmt[program_count] = NULL;
+#ifdef TS_PMT_LOG
+ printf ("ts_demux: eek, zero-length section?\n");
+#endif
+ return;
+ }
+
+#ifdef TS_PMT_LOG
+ printf ("ts_demux: have all TS packets for the PMT section\n");
+#endif
+
+ crc32 = (uint32_t) this->pmt[program_count][section_length+3-4] << 24;
+ crc32 |= (uint32_t) this->pmt[program_count][section_length+3-3] << 16;
+ crc32 |= (uint32_t) this->pmt[program_count][section_length+3-2] << 8;
+ crc32 |= (uint32_t) this->pmt[program_count][section_length+3-1] ;
+
+ /* Check CRC. */
+ calc_crc32 = demux_ts_compute_crc32 (this,
+ this->pmt[program_count],
+ section_length+3-4, 0xffffffff);
+ if (crc32 != calc_crc32) {
+ xprintf (this->stream->xine, XINE_VERBOSITY_DEBUG,
+ "demux_ts: demux error! PMT with invalid CRC32: packet_crc32: %#.8x calc_crc32: %#.8x\n",
+ crc32,calc_crc32);
+ return;
+ }
+ else {
+#ifdef TS_PMT_LOG
+ printf ("demux_ts: PMT CRC32 ok.\n");
+#endif
+ if ( crc32==this->last_pmt_crc ) {
+#ifdef TS_PMT_LOG
+ printf("demux_ts: PMT with CRC32=%d already parsed. Skipping.\n", crc32);
+#endif
+ return;
+ }
+ else {
+#ifdef TS_PMT_LOG
+ printf("demux_ts: new PMT, parsing...\n");
+#endif
+ this->last_pmt_crc = crc32;
+ }
+ }
+
+ /*
+ * Forget the current video, audio and subtitle PIDs; if the PMT has not
+ * changed, we'll pick them up again when we parse this PMT, while if the
+ * PMT has changed (e.g. an IPTV streamer that's just changed its source),
+ * we'll get new PIDs that we should follow.
+ */
+ this->audio_tracks_count = 0;
+ this->videoPid = INVALID_PID;
+ this->spu_pid = INVALID_PID;
+
+ /*
+ * ES definitions start here...we are going to learn upto one video
+ * PID and one audio PID.
+ */
+ program_info_length = ((this->pmt[program_count][10] << 8)
+ | this->pmt[program_count][11]) & 0x0fff;
+
+/* Program info descriptor is currently just ignored.
+ * printf ("demux_ts: program_info_desc: ");
+ * for (i = 0; i < program_info_length; i++)
+ * printf ("%.2x ", this->pmt[program_count][12+i]);
+ * printf ("\n");
+ */
+ stream = &this->pmt[program_count][12] + program_info_length;
+ coded_length = 13 + program_info_length;
+ if (coded_length > section_length) {
+ xprintf (this->stream->xine, XINE_VERBOSITY_DEBUG,
+ "demux error! PMT with inconsistent progInfo length\n");
+ return;
+ }
+ section_length -= coded_length;
+
+ /*
+ * Extract the elementary streams.
+ */
+ this->spu_langs_count = 0;
+ while (section_length > 0) {
+ unsigned int stream_info_length;
+
+ pid = ((stream[1] << 8) | stream[2]) & 0x1fff;
+ stream_info_length = ((stream[3] << 8) | stream[4]) & 0x0fff;
+ coded_length = 5 + stream_info_length;
+ if (coded_length > section_length) {
+ xprintf (this->stream->xine, XINE_VERBOSITY_DEBUG,
+ "demux error! PMT with inconsistent streamInfo length\n");
+ return;
+ }
+
+ /*
+ * Squirrel away the first audio and the first video stream. TBD: there
+ * should really be a way to select the stream of interest.
+ */
+ switch (stream[0]) {
+ case ISO_11172_VIDEO:
+ case ISO_13818_VIDEO:
+ case ISO_14496_PART2_VIDEO:
+ case ISO_14496_PART10_VIDEO:
+ case STREAM_VIDEO_VC1:
+ if (this->videoPid == INVALID_PID) {
+#ifdef TS_PMT_LOG
+ printf ("demux_ts: PMT video pid 0x%.4x type %2.2x\n", pid, stream[0]);
+#endif
+ demux_ts_pes_new(this, this->media_num, pid, this->video_fifo,stream[0]);
+ this->videoMedia = this->media_num;
+ this->videoPid = pid;
+ }
+
+ break;
+ case ISO_11172_AUDIO:
+ case ISO_13818_AUDIO:
+ case ISO_13818_PART7_AUDIO:
+ case ISO_14496_PART3_AUDIO:
+ if (this->audio_tracks_count < MAX_AUDIO_TRACKS) {
+ int i, found = 0;
+ for(i = 0; i < this->audio_tracks_count; i++) {
+ if(this->audio_tracks[i].pid == pid) {
+ found = 1;
+ break;
+ }
+ }
+ if(!found) {
+#ifdef TS_PMT_LOG
+ printf ("demux_ts: PMT audio pid 0x%.4x type %2.2x\n", pid, stream[0]);
+#endif
+ demux_ts_pes_new(this, this->media_num, pid, this->audio_fifo,stream[0]);
+ this->audio_tracks[this->audio_tracks_count].pid = pid;
+ this->audio_tracks[this->audio_tracks_count].media_index = this->media_num;
+ this->media[this->media_num].type = this->audio_tracks_count;
+ demux_ts_get_lang_desc(this, this->audio_tracks[this->audio_tracks_count].lang,
+ stream + 5, stream_info_length);
+ demux_send_special_audio_buf(this, stream[0], this->audio_tracks_count);
+ this->audio_tracks_count++;
+ }
+ }
+ break;
+ case ISO_13818_PRIVATE:
+#ifdef TS_PMT_LOG
+ printf ("demux_ts: PMT streamtype 13818_PRIVATE, pid: 0x%.4x type %2.2x\n", pid, stream[0]);
+
+ for (i = 5; i < coded_length; i++)
+ printf ("%.2x ", stream[i]);
+ printf ("\n");
+#endif
+ break;
+ case ISO_13818_TYPE_C: /* data carousel */
+#ifdef TS_PMT_LOG
+ printf ("demux_ts: PMT streamtype 13818_TYPE_C, pid: 0x%.4x type %2.2x\n", pid, stream[0]);
+#endif
+ break;
+ case ISO_13818_PES_PRIVATE:
+ for (i = 5; i < coded_length; i += stream[i+1] + 2) {
+ if ((stream[i] == 0x6a) && (this->audio_tracks_count < MAX_AUDIO_TRACKS)) {
+ int i, found = 0;
+ for(i = 0; i < this->audio_tracks_count; i++) {
+ if(this->audio_tracks[i].pid == pid) {
+ found = 1;
+ break;
+ }
+ }
+ if(!found) {
+#ifdef TS_PMT_LOG
+ printf ("demux_ts: PMT AC3 audio pid 0x%.4x type %2.2x\n", pid, stream[0]);
+#endif
+ demux_ts_pes_new(this, this->media_num, pid,
+ this->audio_fifo, STREAM_AUDIO_AC3);
+
+ this->audio_tracks[this->audio_tracks_count].pid = pid;
+ this->audio_tracks[this->audio_tracks_count].media_index = this->media_num;
+ this->media[this->media_num].type = this->audio_tracks_count;
+ demux_ts_get_lang_desc(this, this->audio_tracks[this->audio_tracks_count].lang,
+ stream + 5, stream_info_length);
+ demux_send_special_audio_buf(this, STREAM_AUDIO_AC3, this->audio_tracks_count);
+ this->audio_tracks_count++;
+ break;
+ }
+ }
+ /* Teletext */
+ else if (stream[i] == 0x56)
+ {
+#ifdef TS_PMT_LOG
+ printf ("demux_ts: PMT Teletext, pid: 0x%.4x type %2.2x\n", pid, stream[0]);
+
+ for (i = 5; i < coded_length; i++)
+ printf ("%.2x ", stream[i]);
+ printf ("\n");
+#endif
+ break;
+ }
+
+ /* DVBSUB */
+ else if (stream[i] == 0x59)
+ {
+ int pos;
+ for (pos = i + 2;
+ pos + 8 <= i + 2 + stream[i + 1]
+ && this->spu_langs_count < MAX_SPU_LANGS;
+ pos += 8)
+ {
+ int no = this->spu_langs_count;
+ demux_ts_spu_lang *lang = &this->spu_langs[no];
+
+ this->spu_langs_count++;
+
+ memcpy(lang->desc.lang, &stream[pos], 3);
+ lang->desc.lang[3] = 0;
+ lang->desc.comp_page_id =
+ (stream[pos + 4] << 8) | stream[pos + 5];
+ lang->desc.aux_page_id =
+ (stream[pos + 6] << 8) | stream[pos + 7];
+ lang->pid = pid;
+ 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, 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,
+ lang->desc.comp_page_id,
+ lang->desc.aux_page_id,
+ stream[0]);
+#endif
+ }
+ }
+ }
+ break;
+
+ case HDMV_SPU_INTERACTIVE:
+ case HDMV_SPU_TEXT:
+ if (this->hdmv > 0) {
+ printf("demux_ts: Skipping unsupported HDMV subtitle stream_type: 0x%.2x pid: 0x%.4x\n",
+ stream[0], pid);
+ break;
+ }
+ /* fall thru */
+
+ case HDMV_SPU_BITMAP:
+ if (this->hdmv > 0) {
+ if (pid >= 0x1200 && pid < 0x1300) {
+ /* HDMV Presentation Graphics / SPU */
+
+ if (this->spu_langs_count >= MAX_SPU_LANGS) {
+ xprintf (this->stream->xine, XINE_VERBOSITY_DEBUG,
+ "demux_ts: too many SPU tracks! ignoring pid 0x%.4x\n",
+ pid);
+ break;
+ }
+
+ 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
+ * We first check that the stream id >= 0x80, because all values below that are invalid if not handled above,
+ * then we check the registration format identifier to see if it holds "AC-3" (0x41432d33) and
+ * if is does, we tag this as an audio stream.
+ * FIXME: This will need expanding if we ever see a DTS or other media format here.
+ */
+ if ((this->audio_tracks_count < MAX_AUDIO_TRACKS) && (stream[0] >= 0x80) ) {
+ int i, found = 0;
+ for(i = 0; i < this->audio_tracks_count; i++) {
+ if(this->audio_tracks[i].pid == pid) {
+ found = 1;
+ break;
+ }
+ }
+ if(!found) {
+ uint32_t format_identifier=0;
+ demux_ts_get_reg_desc(this, &format_identifier,
+ stream + 5, stream_info_length);
+ /* If no format identifier, assume A52 */
+ if (( format_identifier == 0x41432d33) ||
+ ( format_identifier == 0) ||
+ ((format_identifier == 0x48444d56 || this->hdmv>0) && stream[0] == HDMV_AUDIO_80_PCM) /* BluRay PCM */) {
+
+ demux_ts_pes_new(this, this->media_num, pid, this->audio_fifo, stream[0]);
+ this->audio_tracks[this->audio_tracks_count].pid = pid;
+ this->audio_tracks[this->audio_tracks_count].media_index = this->media_num;
+ this->media[this->media_num].type = this->audio_tracks_count;
+ demux_ts_get_lang_desc(this, this->audio_tracks[this->audio_tracks_count].lang,
+ stream + 5, stream_info_length);
+ demux_send_special_audio_buf(this, stream[0], this->audio_tracks_count);
+ this->audio_tracks_count++;
+ break;
+ }
+ }
+ } else {
+#ifdef TS_PMT_LOG
+ printf ("demux_ts: PMT unknown stream_type: 0x%.2x pid: 0x%.4x\n",
+ stream[0], pid);
+
+ for (i = 5; i < coded_length; i++)
+ printf ("%.2x ", stream[i]);
+ printf ("\n");
+#endif
+ }
+ break;
+ }
+ this->media_num++;
+ stream += coded_length;
+ section_length -= coded_length;
+ }
+
+ /*
+ * Get the current PCR PID.
+ */
+ pid = ((this->pmt[program_count][8] << 8)
+ | this->pmt[program_count][9]) & 0x1fff;
+ if (this->pcrPid != pid) {
+#ifdef TS_PMT_LOG
+ if (this->pcrPid == INVALID_PID) {
+ printf ("demux_ts: PMT pcr pid 0x%.4x\n", pid);
+ } else {
+ printf ("demux_ts: PMT pcr pid changed 0x%.4x\n", pid);
+ }
+#endif
+ this->pcrPid = pid;
+ }
+
+ if ( this->stream->spu_channel>=0 && this->spu_langs_count>0 )
+ demux_ts_update_spu_channel( this );
+
+ /* Inform UI of channels changes */
+ xine_event_t ui_event;
+ ui_event.type = XINE_EVENT_UI_CHANNELS_CHANGED;
+ ui_event.data_length = 0;
+ xine_event_send( this->stream, &ui_event );
+}
+
+static int sync_correct(demux_ts_t*this, uint8_t *buf, int32_t npkt_read) {
+
+ int p = 0;
+ int n = 0;
+ int i = 0;
+ int sync_ok = 0;
+ int read_length;
+
+ xprintf (this->stream->xine, XINE_VERBOSITY_DEBUG, "demux_ts: about to resync!\n");
+
+ for (p=0; p < npkt_read; p++) {
+ for(n=0; n < this->pkt_size; n++) {
+ sync_ok = 1;
+ for (i=0; i < MIN(MIN_SYNCS, npkt_read - p); i++) {
+ if (buf[this->pkt_offset + n + ((i+p) * this->pkt_size)] != SYNC_BYTE) {
+ sync_ok = 0;
+ break;
+ }
+ }
+ if (sync_ok) break;
+ }
+ if (sync_ok) break;
+ }
+
+ if (sync_ok) {
+ /* Found sync, fill in */
+ memmove(&buf[0], &buf[n + p * this->pkt_size],
+ ((this->pkt_size * (npkt_read - p)) - n));
+ read_length = this->input->read(this->input,
+ &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 * this->pkt_size)) {
+ xprintf (this->stream->xine, XINE_VERBOSITY_DEBUG,
+ "demux_ts_tsync_correct: sync found, but read failed\n");
+ return 0;
+ }
+ } else {
+ xprintf (this->stream->xine, XINE_VERBOSITY_DEBUG, "demux_ts_tsync_correct: sync not found! Stop demuxing\n");
+ return 0;
+ }
+ xprintf (this->stream->xine, XINE_VERBOSITY_DEBUG, "demux_ts: resync successful!\n");
+ return 1;
+}
+
+static int sync_detect(demux_ts_t*this, uint8_t *buf, int32_t npkt_read) {
+
+ int i, sync_ok;
+
+ 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;
+ break;
+ }
+ }
+ if (!sync_ok) return sync_correct(this, buf, npkt_read);
+ return sync_ok;
+}
+
+
+/*
+ * Main synchronisation routine.
+ */
+static unsigned char * demux_synchronise(demux_ts_t* this) {
+
+ uint8_t *return_pointer = NULL;
+ int32_t read_length;
+ if ( (this->packet_number) >= this->npkt_read) {
+
+ /* NEW: handle read returning less packets than NPKT_PER_READ... */
+ do {
+ read_length = this->input->read(this->input, this->buf,
+ 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, this->pkt_size);
+ this->status = DEMUX_FINISHED;
+ return NULL;
+ }
+ this->npkt_read = read_length / this->pkt_size;
+
+#ifdef TS_READ_STATS
+ this->rstat[this->npkt_read]++;
+#endif
+ /*
+ * what if this->npkt_read < 5 ? --> ok in sync_detect
+ *
+ * NEW: stop demuxing if read returns 0 a few times... (200)
+ */
+
+ if (this->npkt_read == 0) {
+ xprintf (this->stream->xine, XINE_VERBOSITY_DEBUG, "demux_ts: read 0 packets\n");
+ this->status = DEMUX_FINISHED;
+ return NULL;
+ }
+
+ } while (! read_length);
+
+ this->packet_number = 0;
+
+ if (!sync_detect(this, &(this->buf)[0], this->npkt_read)) {
+ xprintf (this->stream->xine, XINE_VERBOSITY_DEBUG, "demux_ts: sync error.\n");
+ this->status = DEMUX_FINISHED;
+ return NULL;
+ }
+ }
+ return_pointer = &(this->buf)[this->pkt_offset + this->pkt_size * this->packet_number];
+ this->packet_number++;
+ return return_pointer;
+}
+
+
+static int64_t demux_ts_adaptation_field_parse(uint8_t *data,
+ uint32_t adaptation_field_length) {
+
+ uint32_t discontinuity_indicator=0;
+ uint32_t random_access_indicator=0;
+ uint32_t elementary_stream_priority_indicator=0;
+ uint32_t PCR_flag=0;
+ int64_t PCR=0;
+ uint32_t EPCR=0;
+ uint32_t OPCR_flag=0;
+ uint32_t OPCR=0;
+ uint32_t EOPCR=0;
+ uint32_t slicing_point_flag=0;
+ uint32_t transport_private_data_flag=0;
+ uint32_t adaptation_field_extension_flag=0;
+ uint32_t offset = 1;
+
+ discontinuity_indicator = ((data[0] >> 7) & 0x01);
+ random_access_indicator = ((data[0] >> 6) & 0x01);
+ elementary_stream_priority_indicator = ((data[0] >> 5) & 0x01);
+ PCR_flag = ((data[0] >> 4) & 0x01);
+ OPCR_flag = ((data[0] >> 3) & 0x01);
+ slicing_point_flag = ((data[0] >> 2) & 0x01);
+ transport_private_data_flag = ((data[0] >> 1) & 0x01);
+ adaptation_field_extension_flag = (data[0] & 0x01);
+
+#ifdef TS_LOG
+ printf ("demux_ts: ADAPTATION FIELD length: %d (%x)\n",
+ adaptation_field_length, adaptation_field_length);
+ if(discontinuity_indicator) {
+ printf (" Discontinuity indicator: %d\n",
+ discontinuity_indicator);
+ }
+ if(random_access_indicator) {
+ printf (" Random_access indicator: %d\n",
+ random_access_indicator);
+ }
+ if(elementary_stream_priority_indicator) {
+ printf (" Elementary_stream_priority_indicator: %d\n",
+ elementary_stream_priority_indicator);
+ }
+#endif
+ if(PCR_flag) {
+ PCR = (((int64_t) data[offset]) & 0xFF) << 25;
+ PCR += (int64_t) ((data[offset+1] & 0xFF) << 17);
+ PCR += (int64_t) ((data[offset+2] & 0xFF) << 9);
+ PCR += (int64_t) ((data[offset+3] & 0xFF) << 1);
+ PCR += (int64_t) ((data[offset+4] & 0x80) >> 7);
+
+ EPCR = ((data[offset+4] & 0x1) << 8) | data[offset+5];
+#ifdef TS_LOG
+ printf ("demux_ts: PCR: %lld, EPCR: %u\n",
+ PCR, EPCR);
+#endif
+ offset+=6;
+ }
+ if(OPCR_flag) {
+ OPCR = data[offset] << 25;
+ OPCR |= data[offset+1] << 17;
+ OPCR |= data[offset+2] << 9;
+ OPCR |= data[offset+3] << 1;
+ OPCR |= (data[offset+4] >> 7) & 0x01;
+ EOPCR = ((data[offset+4] & 0x1) << 8) | data[offset+5];
+#ifdef TS_LOG
+ printf ("demux_ts: OPCR: %u, EOPCR: %u\n",
+ OPCR,EOPCR);
+#endif
+ offset+=6;
+ }
+#ifdef TS_LOG
+ if(slicing_point_flag) {
+ printf ("demux_ts: slicing_point_flag: %d\n",
+ slicing_point_flag);
+ }
+ if(transport_private_data_flag) {
+ printf ("demux_ts: transport_private_data_flag: %d\n",
+ transport_private_data_flag);
+ }
+ if(adaptation_field_extension_flag) {
+ printf ("demux_ts: adaptation_field_extension_flag: %d\n",
+ adaptation_field_extension_flag);
+ }
+#endif
+ return PCR;
+}
+
+/* check if an apid is in the list of known apids */
+static int apid_check(demux_ts_t*this, unsigned int pid) {
+ int i;
+ for(i=0; i<this->audio_tracks_count; i++) {
+ if(this->audio_tracks[i].pid == pid)
+ return i;
+ }
+ return -1;
+}
+
+/* transport stream packet layer */
+static void demux_ts_parse_packet (demux_ts_t*this) {
+
+ unsigned char *originalPkt;
+ unsigned int sync_byte;
+ unsigned int transport_error_indicator;
+ unsigned int payload_unit_start_indicator;
+ unsigned int transport_priority;
+ unsigned int pid;
+ unsigned int transport_scrambling_control;
+ unsigned int adaptation_field_control;
+ unsigned int continuity_counter;
+ unsigned int data_offset;
+ unsigned int data_len;
+ uint32_t program_count;
+ int i;
+
+ /* get next synchronised packet, or NULL */
+ originalPkt = demux_synchronise(this);
+ if (originalPkt == NULL)
+ return;
+
+ sync_byte = originalPkt[0];
+ transport_error_indicator = (originalPkt[1] >> 7) & 0x01;
+ payload_unit_start_indicator = (originalPkt[1] >> 6) & 0x01;
+ transport_priority = (originalPkt[1] >> 5) & 0x01;
+ pid = ((originalPkt[1] << 8) |
+ originalPkt[2]) & 0x1fff;
+ transport_scrambling_control = (originalPkt[3] >> 6) & 0x03;
+ adaptation_field_control = (originalPkt[3] >> 4) & 0x03;
+ continuity_counter = originalPkt[3] & 0x0f;
+
+
+#ifdef TS_HEADER_LOG
+ printf("demux_ts:ts_header:sync_byte=0x%.2x\n",sync_byte);
+ printf("demux_ts:ts_header:transport_error_indicator=%d\n", transport_error_indicator);
+ printf("demux_ts:ts_header:payload_unit_start_indicator=%d\n", payload_unit_start_indicator);
+ printf("demux_ts:ts_header:transport_priority=%d\n", transport_priority);
+ printf("demux_ts:ts_header:pid=0x%.4x\n", pid);
+ printf("demux_ts:ts_header:transport_scrambling_control=0x%.1x\n", transport_scrambling_control);
+ printf("demux_ts:ts_header:adaptation_field_control=0x%.1x\n", adaptation_field_control);
+ printf("demux_ts:ts_header:continuity_counter=0x%.1x\n", continuity_counter);
+#endif
+ /*
+ * Discard packets that are obviously bad.
+ */
+ if (sync_byte != SYNC_BYTE) {
+ xprintf (this->stream->xine, XINE_VERBOSITY_DEBUG,
+ "demux error! invalid ts sync byte %.2x\n", sync_byte);
+ return;
+ }
+ if (transport_error_indicator) {
+ xprintf (this->stream->xine, XINE_VERBOSITY_DEBUG, "demux error! transport error\n");
+ return;
+ }
+ if (pid == 0x1ffb) {
+ /* printf ("demux_ts: PSIP table. Program Guide etc....not supported yet. PID = 0x1ffb\n"); */
+ return;
+ }
+
+ if (transport_scrambling_control) {
+ if (this->videoPid == pid) {
+ xprintf (this->stream->xine, XINE_VERBOSITY_DEBUG,
+ "demux_ts: selected videoPid is scrambled; skipping...\n");
+ }
+ for (i=0; i < this->scrambled_npids; i++) {
+ if (this->scrambled_pids[i] == pid) return;
+ }
+ this->scrambled_pids[this->scrambled_npids] = pid;
+ this->scrambled_npids++;
+
+ xprintf (this->stream->xine, XINE_VERBOSITY_DEBUG, "demux_ts: PID 0x%.4x is scrambled!\n", pid);
+ return;
+ }
+
+ data_offset = 4;
+
+ if( adaptation_field_control & 0x2 ){
+ uint32_t adaptation_field_length = originalPkt[4];
+ if (adaptation_field_length > 0) {
+ demux_ts_adaptation_field_parse (originalPkt+5, adaptation_field_length);
+ }
+ /*
+ * Skip adaptation header.
+ */
+ data_offset += adaptation_field_length + 1;
+ }
+
+ if (! (adaptation_field_control & 0x1)) {
+ return;
+ }
+
+ data_len = PKT_SIZE - data_offset;
+
+ /*
+ * audio/video pid auto-detection, if necessary
+ */
+ program_count=0;
+ if(this->media_num<MAX_PMTS)
+ while ((this->program_number[program_count] != INVALID_PROGRAM) &&
+ (program_count < MAX_PMTS)) {
+ if (pid == this->pmt_pid[program_count]) {
+
+#ifdef TS_LOG
+ printf ("demux_ts: PMT prog: 0x%.4x pid: 0x%.4x\n",
+ this->program_number[program_count],
+ this->pmt_pid[program_count]);
+#endif
+ demux_ts_parse_pmt (this, originalPkt, originalPkt+data_offset-4,
+ payload_unit_start_indicator,
+ program_count);
+ return;
+ }
+ program_count++;
+ }
+
+ if (payload_unit_start_indicator && this->media_num < MAX_PIDS){
+ int pes_stream_id;
+ if (pid == 0) {
+ demux_ts_parse_pat (this, originalPkt, originalPkt+data_offset-4,
+ payload_unit_start_indicator);
+ return;
+ }
+ program_count = 0;
+ pes_stream_id = originalPkt[data_offset+3];
+
+#ifdef TS_HEADER_LOG
+ printf("demux_ts:ts_pes_header:stream_id=0x%.2x\n",pes_stream_id);
+#endif
+
+ if ( (pes_stream_id >= VIDEO_STREAM_S) && (pes_stream_id <= VIDEO_STREAM_E) ) {
+ if ( this->videoPid == INVALID_PID) {
+ int i, found = 0;
+ for(i = 0; i < this->media_num; i++) {
+ if (this->media[i].pid == pid) {
+ found = 1;
+ break;
+ }
+ }
+
+ if (found && (this->media[i].corrupted_pes == 0)) {
+ this->videoPid = pid;
+ this->videoMedia = i;
+ } else if (!found) {
+ this->videoPid = pid;
+ this->videoMedia = this->media_num;
+ this->media[this->videoMedia].autodetected = 1;
+ demux_ts_pes_new(this, this->media_num++, pid, this->video_fifo, 0x100 + pes_stream_id);
+ }
+
+ if (this->videoPid != INVALID_PID) {
+ xprintf (this->stream->xine, XINE_VERBOSITY_DEBUG,
+ "demux_ts: auto-detected video pid 0x%.4x\n", pid);
+ }
+ }
+ } else if ( (pes_stream_id >= AUDIO_STREAM_S) && (pes_stream_id <= AUDIO_STREAM_E) ) {
+ if (this->audio_tracks_count < MAX_AUDIO_TRACKS) {
+ int i, found = 0;
+ for(i = 0; i < this->audio_tracks_count; i++) {
+ if(this->audio_tracks[i].pid == pid) {
+ found = 1;
+ break;
+ }
+ }
+ if(!found) {
+#ifdef TS_PMT_LOG
+ xprintf (this->stream->xine, XINE_VERBOSITY_DEBUG,
+ "demux_ts: auto-detected audio pid 0x%.4x\n", pid);
+#endif
+ /* store PID, index and stream no. */
+ this->audio_tracks[this->audio_tracks_count].pid = pid;
+ this->audio_tracks[this->audio_tracks_count].media_index = this->media_num;
+ this->media[this->media_num].type = this->audio_tracks_count;
+ demux_ts_pes_new(this, this->media_num++, pid,
+ this->audio_fifo, 0x100 + pes_stream_id);
+ this->audio_tracks_count++;
+ }
+ }
+ }
+ }
+
+ if (data_len > PKT_SIZE) {
+
+ xprintf (this->stream->xine, XINE_VERBOSITY_DEBUG,
+ "demux_ts: demux error! invalid payload size %d\n", data_len);
+
+ } else {
+
+ /*
+ * Do the demuxing in descending order of packet frequency!
+ */
+ int index;
+ if (pid == this->videoPid) {
+#ifdef TS_LOG
+ printf ("demux_ts: Video pid: 0x%.4x\n", pid);
+#endif
+ check_newpts(this, this->media[this->videoMedia].pts, PTS_VIDEO);
+ demux_ts_buffer_pes (this, originalPkt+data_offset, this->videoMedia,
+ payload_unit_start_indicator, continuity_counter,
+ data_len);
+ return;
+ }
+ else if ((index = apid_check(this, pid)) > -1) {
+#ifdef TS_LOG
+ printf ("demux_ts: Audio pid: 0x%.4x\n", pid);
+#endif
+ check_newpts(this, this->media[this->audio_tracks[index].media_index].pts, PTS_AUDIO);
+ demux_ts_buffer_pes (this, originalPkt+data_offset,
+ this->audio_tracks[index].media_index,
+ payload_unit_start_indicator, continuity_counter,
+ data_len);
+ return;
+ }
+ else if (pid == NULL_PID) {
+#ifdef TS_LOG
+ printf ("demux_ts: Null Packet\n");
+#endif
+ return;
+ }
+ /* DVBSUB */
+ else if (pid == this->spu_pid) {
+#ifdef TS_LOG
+ printf ("demux_ts: SPU pid: 0x%.4x\n", pid);
+#endif
+ demux_ts_buffer_pes (this, originalPkt+data_offset, this->spu_media,
+ payload_unit_start_indicator, continuity_counter,
+ data_len);
+ return;
+ }
+ }
+}
+
+/*
+ * check for pids change events
+ */
+
+static void demux_ts_event_handler (demux_ts_t *this) {
+
+ xine_event_t *event;
+
+ while ((event = xine_event_get (this->event_queue))) {
+
+
+ switch (event->type) {
+
+ case XINE_EVENT_PIDS_CHANGE:
+
+ this->videoPid = INVALID_PID;
+ this->audio_tracks_count = 0;
+ this->media_num = 0;
+ this->send_newpts = 1;
+ this->spu_pid = INVALID_PID;
+ this->spu_media = 0;
+ this->spu_langs_count= 0;
+ this->last_pmt_crc = 0;
+ _x_demux_control_start (this->stream);
+ break;
+
+ }
+
+ xine_event_free (event);
+ }
+}
+
+/*
+ * send a piece of data down the fifos
+ */
+
+static int demux_ts_send_chunk (demux_plugin_t *this_gen) {
+
+ demux_ts_t*this = (demux_ts_t*)this_gen;
+
+ demux_ts_event_handler (this);
+
+ demux_ts_parse_packet(this);
+
+ /* DVBSUB: check if channel has changed. Dunno if I should, or
+ * even could, lock the xine object. */
+ if (this->stream->spu_channel != this->current_spu_channel) {
+ demux_ts_update_spu_channel(this);
+ }
+
+ return this->status;
+}
+
+static void demux_ts_dispose (demux_plugin_t *this_gen) {
+ int i;
+ demux_ts_t*this = (demux_ts_t*)this_gen;
+
+ for (i=0; i < MAX_PMTS; i++) {
+ if (this->pmt[i] != NULL) {
+ free(this->pmt[i]);
+ this->pmt[i] = NULL;
+ }
+ }
+ for (i=0; i < MAX_PIDS; i++) {
+ if (this->media[i].buf != NULL) {
+ this->media[i].buf->free_buffer(this->media[i].buf);
+ this->media[i].buf = NULL;
+ }
+ }
+
+ xine_event_dispose_queue (this->event_queue);
+
+ free(this_gen);
+}
+
+static int demux_ts_get_status(demux_plugin_t *this_gen) {
+
+ demux_ts_t*this = (demux_ts_t*)this_gen;
+
+ return this->status;
+}
+
+static void demux_ts_send_headers (demux_plugin_t *this_gen) {
+
+ demux_ts_t *this = (demux_ts_t *) this_gen;
+
+ this->video_fifo = this->stream->video_fifo;
+ this->audio_fifo = this->stream->audio_fifo;
+
+ this->status = DEMUX_OK;
+
+ /*
+ * send start buffers
+ */
+
+ this->videoPid = INVALID_PID;
+ this->audio_tracks_count = 0;
+ this->media_num= 0;
+ this->last_pmt_crc = 0;
+
+ _x_demux_control_start (this->stream);
+
+ this->input->seek (this->input, 0, SEEK_SET);
+
+ this->send_newpts = 1;
+
+ demux_ts_build_crc32_table (this);
+
+ this->status = DEMUX_OK ;
+
+ this->send_end_buffers = 1;
+ this->scrambled_npids = 0;
+
+ /* DVBSUB */
+ this->spu_pid = INVALID_PID;
+ this->spu_langs_count = 0;
+ this->current_spu_channel = -1;
+
+ /* FIXME ? */
+ _x_stream_info_set(this->stream, XINE_STREAM_INFO_HAS_VIDEO, 1);
+ _x_stream_info_set(this->stream, XINE_STREAM_INFO_HAS_AUDIO, 1);
+}
+
+static int demux_ts_seek (demux_plugin_t *this_gen,
+ off_t start_pos, int start_time, int playing) {
+
+ demux_ts_t *this = (demux_ts_t *) this_gen;
+ int i;
+ start_time /= 1000;
+ start_pos = (off_t) ( (double) start_pos / 65535 *
+ this->input->get_length (this->input) );
+
+ if (this->input->get_capabilities(this->input) & INPUT_CAP_SEEKABLE) {
+
+ if ((!start_pos) && (start_time)) {
+ start_pos = start_time;
+ start_pos *= this->rate;
+ start_pos *= 50;
+ }
+ this->input->seek (this->input, start_pos, SEEK_SET);
+
+ }
+
+ this->send_newpts = 1;
+
+ for (i=0; i<MAX_PIDS; i++) {
+ demux_ts_media *m = &this->media[i];
+
+ if (m->buf != NULL)
+ m->buf->free_buffer(m->buf);
+ m->buf = NULL;
+ m->counter = INVALID_CC;
+ m->corrupted_pes = 1;
+ m->buffered_bytes = 0;
+ }
+
+ if( !playing ) {
+
+ this->status = DEMUX_OK;
+ this->buf_flag_seek = 0;
+
+ } else {
+
+ this->buf_flag_seek = 1;
+ _x_demux_flush_engine(this->stream);
+
+ }
+
+ return this->status;
+}
+
+static int demux_ts_get_stream_length (demux_plugin_t *this_gen) {
+
+ demux_ts_t*this = (demux_ts_t*)this_gen;
+
+ if (this->rate)
+ return (int)((int64_t) this->input->get_length (this->input)
+ * 1000 / (this->rate * 50));
+ else
+ return 0;
+}
+
+
+static uint32_t demux_ts_get_capabilities(demux_plugin_t *this_gen)
+{
+ return DEMUX_CAP_AUDIOLANG | DEMUX_CAP_SPULANG;
+}
+
+static int demux_ts_get_optional_data(demux_plugin_t *this_gen,
+ void *data, int data_type)
+{
+ demux_ts_t *this = (demux_ts_t *) this_gen;
+ char *str = data;
+ int channel = *((int *)data);
+
+ /* be a bit paranoid */
+ if (this == NULL || this->stream == NULL)
+ return DEMUX_OPTIONAL_UNSUPPORTED;
+
+ switch (data_type)
+ {
+ case DEMUX_OPTIONAL_DATA_AUDIOLANG:
+ if ((channel >= 0) && (channel < this->audio_tracks_count)) {
+ if(this->audio_tracks[channel].lang[0])
+ strcpy(str, this->audio_tracks[channel].lang);
+ else {
+ input_plugin_t *input_plugin = this->stream->input_plugin;
+ /* Language not known. Use track number. */
+ sprintf(str, "%3i", channel);
+ /* Ask input plugin */
+ if (input_plugin && input_plugin->get_capabilities (input_plugin) & INPUT_CAP_SPULANG)
+ return input_plugin->get_optional_data (input_plugin, data, data_type);
+ }
+ }
+ else {
+ strcpy(str, "none");
+ return DEMUX_OPTIONAL_UNSUPPORTED;
+ }
+ return DEMUX_OPTIONAL_SUCCESS;
+
+ case DEMUX_OPTIONAL_DATA_SPULANG:
+ if (channel>=0 && channel<this->spu_langs_count) {
+ if (this->spu_langs[channel].desc.lang[0]) {
+ memcpy(str, this->spu_langs[channel].desc.lang, 3);
+ str[3] = 0;
+ } else {
+ input_plugin_t *input_plugin = this->stream->input_plugin;
+ /* Language not known. Use track number. */
+ sprintf(str, "%3i", channel);
+ /* Ask input plugin */
+ if (input_plugin && input_plugin->get_capabilities (input_plugin) & INPUT_CAP_SPULANG)
+ return input_plugin->get_optional_data (input_plugin, data, data_type);
+ }
+ }
+ else {
+ strcpy(str, "none");
+ return DEMUX_OPTIONAL_UNSUPPORTED;
+ }
+ return DEMUX_OPTIONAL_SUCCESS;
+
+ default:
+ return DEMUX_OPTIONAL_UNSUPPORTED;
+ }
+}
+
+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,
+ input_plugin_t *input) {
+
+ demux_ts_t *this;
+ int i;
+ int hdmv = -1;
+
+ switch (stream->content_detection_method) {
+
+ case METHOD_BY_CONTENT: {
+ uint8_t buf[2069];
+
+ if (!_x_demux_read_header(input, buf, sizeof(buf)))
+ return NULL;
+
+ 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;
+
+ case METHOD_BY_EXTENSION: {
+ const char *const mrl = input->get_mrl (input);
+
+ if (_x_demux_check_extension (mrl, "m2ts mts"))
+ hdmv = 1;
+ else
+ hdmv = 0;
+
+ /* check extension */
+ const char *const extensions = class_gen->get_extensions (class_gen);
+
+ if (_x_demux_check_extension (mrl, extensions))
+ break;
+
+ /* accept dvb streams */
+ /*
+ * Also handle the special dvbs,dvbt and dvbc mrl formats:
+ * the content is exactly the same but the input plugin
+ * uses a different tuning algorithm [Pragma]
+ */
+
+ if (!strncasecmp (mrl, "dvb://", 6))
+ break;
+ if (!strncasecmp (mrl, "dvbs://", 7))
+ break;
+ if (!strncasecmp (mrl, "dvbc://", 7))
+ break;
+ if (!strncasecmp (mrl, "dvbt://", 7))
+ break;
+
+ /* accept BluRay discs */
+ if (!strncasecmp (mrl, "bluray:/", 8)) {
+ hdmv = 1;
+ break;
+ }
+
+ return NULL;
+ }
+
+ case METHOD_EXPLICIT:
+ break;
+
+ default:
+ return NULL;
+ }
+
+ /*
+ * if we reach this point, the input has been accepted.
+ */
+
+ this = calloc(1, sizeof(*this));
+ this->stream = stream;
+ this->input = input;
+ this->blockSize = PKT_SIZE;
+
+ this->demux_plugin.send_headers = demux_ts_send_headers;
+ this->demux_plugin.send_chunk = demux_ts_send_chunk;
+ this->demux_plugin.seek = demux_ts_seek;
+ this->demux_plugin.dispose = demux_ts_dispose;
+ this->demux_plugin.get_status = demux_ts_get_status;
+ this->demux_plugin.get_stream_length = demux_ts_get_stream_length;
+ this->demux_plugin.get_capabilities = demux_ts_get_capabilities;
+ this->demux_plugin.get_optional_data = demux_ts_get_optional_data;
+ this->demux_plugin.demux_class = class_gen;
+
+ /*
+ * Initialise our specialised data.
+ */
+ for (i = 0; i < MAX_PIDS; i++) {
+ this->media[i].pid = INVALID_PID;
+ this->media[i].buf = NULL;
+ this->media[i].autodetected = 0;
+ }
+
+ for (i = 0; i < MAX_PMTS; i++) {
+ this->program_number[i] = INVALID_PROGRAM;
+ this->pmt_pid[i] = INVALID_PID;
+ this->pmt[i] = NULL;
+ this->pmt_write_ptr[i] = NULL;
+ }
+
+ this->programNumber = INVALID_PROGRAM;
+ this->pcrPid = INVALID_PID;
+ this->scrambled_npids = 0;
+ this->videoPid = INVALID_PID;
+ this->audio_tracks_count = 0;
+ this->last_pmt_crc = 0;
+
+ this->rate = 16000; /* FIXME */
+
+ this->status = DEMUX_FINISHED;
+
+ /* DVBSUB */
+ this->spu_pid = INVALID_PID;
+ this->spu_langs_count = 0;
+ this->current_spu_channel = -1;
+
+ /* 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;
+}
+
+/*
+ * ts demuxer class
+ */
+
+static const char *get_description (demux_class_t *this_gen) {
+ return "MPEG Transport Stream demuxer";
+}
+
+static const char *get_identifier (demux_class_t *this_gen) {
+ return "MPEG_TS_HDMV";
+}
+
+static const char *get_extensions (demux_class_t *this_gen) {
+ return "m2ts mts";
+}
+
+static const char *get_mimetypes (demux_class_t *this_gen) {
+ return NULL;
+}
+
+static void class_dispose (demux_class_t *this_gen) {
+
+ demux_ts_class_t *this = (demux_ts_class_t *) this_gen;
+
+ free (this);
+}
+
+static void *init_class (xine_t *xine, void *data) {
+
+ demux_ts_class_t *this;
+
+ this = calloc(1, sizeof(demux_ts_class_t));
+ this->config = xine->config;
+ this->xine = xine;
+
+ this->demux_class.open_plugin = open_plugin;
+ this->demux_class.get_description = get_description;
+ this->demux_class.get_identifier = get_identifier;
+ this->demux_class.get_mimetypes = get_mimetypes;
+ this->demux_class.get_extensions = get_extensions;
+ this->demux_class.dispose = class_dispose;
+
+ return this;
+}
+
+
+/*
+ * exported plugin catalog entry
+ */
+static const demuxer_info_t demux_info_ts = {
+ 5 /* priority */
+};
+
+const plugin_info_t xine_plugin_info[] EXPORTED = {
+ /* type, API, "name", version, special_info, init_function */
+ { PLUGIN_DEMUX, 26, "mpeg-ts-hdmv", XINE_VERSION_CODE, &demux_info_ts, init_class },
+ { PLUGIN_NONE, 0, "", 0, NULL, NULL }
+};
+
diff --git a/xine/BluRay/input_bluray.c b/xine/BluRay/input_bluray.c
new file mode 100644
index 00000000..aa04122c
--- /dev/null
+++ b/xine/BluRay/input_bluray.c
@@ -0,0 +1,667 @@
+/*
+ * Copyright (C) 2000-2005 the xine project
+ *
+ * Copyright (C) 2009 Petri Hintukainen <phintuka@users.sourceforge.net>
+ *
+ * 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
+ *
+ * Input plugin for BluRay discs / images
+ *
+ * Requires libbluray from http://www.assembla.com/spaces/libbluray/
+ * Tested with SVN revision 103
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+/* asprintf: */
+#define _GNU_SOURCE
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <dlfcn.h>
+
+#include <bluray.h>
+#include <libbdnav/navigation.h>
+
+#define LOG_MODULE "input_bluray"
+#define LOG_VERBOSE
+
+#define LOG
+
+#define LOGMSG(x...) xine_log (this->stream->xine, XINE_LOG_MSG, "input_bluray: " x);
+
+
+#ifdef HAVE_CONFIG_H
+# include "xine_internal.h"
+# include "input_plugin.h"
+#else
+# include <xine/xine_internal.h>
+# include <xine/input_plugin.h>
+#endif
+
+#ifndef EXPORTED
+# define EXPORTED __attribute__((visibility("default")))
+#endif
+
+#ifndef MIN
+# define MIN(a,b) ((a)<(b)?(a):(b))
+#endif
+#ifndef MAX
+# define MAX(a,b) ((a)>(b)?(a):(b))
+#endif
+
+#define ALIGNED_UNIT_SIZE 6144
+#define PKT_SIZE 192
+#define TICKS_IN_MS 45
+
+typedef struct {
+
+ input_class_t input_class;
+
+ xine_t *xine;
+
+ /* config */
+ char *mountpoint;
+ char *keyfile;
+ char *device;
+
+} bluray_input_class_t;
+
+typedef struct {
+ input_plugin_t input_plugin;
+
+ xine_stream_t *stream;
+ bluray_input_class_t *class;
+ char *mrl;
+ char *disc_root;
+
+ BLURAY *bdh;
+ NAV_TITLE *nav_title;
+
+} bluray_input_plugin_t;
+
+static int open_clip (bluray_input_plugin_t *this, NAV_CLIP *clip)
+{
+ /* NOTE: bluray.h bd_select_title() actually opens CLIP, not TITLE ! */
+
+ int clip_id;
+ if (sscanf(clip->name, "%d.m2ts", &clip_id) != 1)
+ clip_id = 0;
+
+ lprintf("Selecting clip %d: bd_select_title(%d - %s) for title %s\n",
+ clip_id, clip_id, clip->name, this->nav_title->name);
+
+ if (!bd_select_title(this->bdh, clip_id)) {
+ LOGMSG("bd_select_title(%d) failed: %s\n", clip_id, strerror(errno));
+ _x_message(this->stream, XINE_MSG_FILE_NOT_FOUND, this->mrl, NULL);
+
+ return -1;
+ }
+
+ lprintf("Clip length: %"PRIu64" bytes\n", (uint64_t)this->bdh->s_size);
+
+ return 1;
+}
+
+static int next_clip (bluray_input_plugin_t *this)
+{
+ /* select clip */
+
+ NAV_CLIP *clip = nav_next_clip(this->nav_title, NULL);
+ if (!clip) {
+ LOGMSG("nav_next_clip() FAILED\n");
+ return -1;
+ }
+ lprintf("clip change: title %s clip %s (%d clips, %d chapters)\n",
+ this->nav_title->name, clip->name,
+ this->nav_title->clip_list.count, this->nav_title->chap_list.count);
+
+ /* open clip */
+
+ return open_clip(this, clip);
+}
+
+static int open_title (bluray_input_plugin_t *this, int title)
+{
+ if (this->nav_title)
+ nav_title_close(this->nav_title);
+
+ /* open title */
+
+ char mpls[11] = {0};
+ snprintf(mpls, sizeof(mpls), "%05d.mpls", MIN(99999, MAX(0, title)));
+
+ this->nav_title = nav_title_open(this->disc_root, mpls);
+
+ if (!this->nav_title) {
+ LOGMSG("nav_title_open(%s, %s) FAILED\n", this->disc_root, mpls);
+ return -1;
+ }
+
+#ifdef LOG
+ int ms = this->nav_title->duration / TICKS_IN_MS;
+ lprintf("Opened title %s. Length %"PRId64" bytes / %02d:%02d:%02d.%03d\n",
+ this->nav_title->name, (int64_t)this->nav_title->packets * PKT_SIZE,
+ ms / 3600000, (ms % 3600000 / 60000), (ms % 60000) / 1000, ms % 1000);
+#endif
+
+ /* set stream metainfo */
+
+ /* title */
+ if (strcmp(this->disc_root, this->class->mountpoint)) {
+ char *t = strrchr(this->disc_root, '/');
+ if (!t[1])
+ while (t > this->disc_root && t[-1] != '/') t--;
+ t = strdup(t);
+ if (t[strlen(t)-1] == '/')
+ t[strlen(t)-1] = 0;
+ _x_meta_info_set(this->stream, XINE_META_INFO_TITLE, t);
+ free(t);
+ }
+
+ _x_stream_info_set(this->stream, XINE_STREAM_INFO_DVD_TITLE_NUMBER, title);
+ _x_stream_info_set(this->stream, XINE_STREAM_INFO_DVD_ANGLE_NUMBER, this->nav_title->angle);
+ _x_stream_info_set(this->stream, XINE_STREAM_INFO_DVD_CHAPTER_NUMBER, 1);
+ _x_stream_info_set(this->stream, XINE_STREAM_INFO_DVD_CHAPTER_COUNT, this->nav_title->chap_list.count);
+ _x_stream_info_set(this->stream, XINE_STREAM_INFO_HAS_CHAPTERS, this->nav_title->chap_list.count>0);
+
+ return next_clip(this);
+}
+
+/*
+ * xine plugin interface
+ */
+
+static uint32_t bluray_plugin_get_capabilities (input_plugin_t *this_gen)
+{
+ return INPUT_CAP_SEEKABLE |
+ INPUT_CAP_BLOCK |
+ INPUT_CAP_AUDIOLANG |
+ INPUT_CAP_SPULANG;
+}
+
+static off_t bluray_plugin_read (input_plugin_t *this_gen, char *buf, off_t len)
+{
+ bluray_input_plugin_t *this = (bluray_input_plugin_t *) this_gen;
+
+ if (!this || !this->bdh || len < 0)
+ return -1;
+
+ if (this->bdh->aacs) {
+
+ /*
+ * Split large reads to aligned units
+ */
+
+ off_t todo = len;
+ off_t block = MIN(todo, ALIGNED_UNIT_SIZE - (this->bdh->s_pos % ALIGNED_UNIT_SIZE));
+
+ while (block > 0) {
+ off_t result = bd_read(this->bdh, (unsigned char *)buf, block);
+ if (result != block) {
+ if (result < 0) {
+ LOGMSG("ERROR: bd_read(aacs, %"PRId64") : got %"PRId64" !\n", block, result);
+ return result;
+ }
+ return len - todo + MAX(0, result);
+ }
+ todo -= result;
+ buf += result;
+
+ block = MIN(todo, ALIGNED_UNIT_SIZE);
+ }
+
+ return len;
+ }
+
+ off_t result = bd_read (this->bdh, (unsigned char *)buf, len);
+
+ if (result < 0)
+ LOGMSG("bd_read() failed: %s (%d of %d)\n", strerror(errno), (int)result, (int)len);
+
+#if 0
+ if (buf[4] != 0x47) {
+ LOGMSG("bd_read(): invalid data ? [%02x %02x %02x %02x %02x ...]\n",
+ buf[0], buf[1], buf[2], buf[3], buf[4]);
+ return 0;
+ }
+#endif
+
+ return result;
+}
+
+static buf_element_t *bluray_plugin_read_block (input_plugin_t *this_gen, fifo_buffer_t *fifo, off_t todo)
+{
+ buf_element_t *buf = fifo->buffer_pool_alloc (fifo);
+
+ if (todo > (off_t)buf->max_size)
+ todo = buf->max_size;
+
+ if (todo > ALIGNED_UNIT_SIZE)
+ todo = ALIGNED_UNIT_SIZE;
+
+ if (todo > 0) {
+
+ buf->size = bluray_plugin_read(this_gen, (char*)buf->mem, todo);
+ buf->type = BUF_DEMUX_BLOCK;
+
+ if (buf->size > 0)
+ return buf;
+ }
+
+ buf->free_buffer (buf);
+ return NULL;
+}
+
+static off_t bluray_plugin_seek (input_plugin_t *this_gen, off_t offset, int origin)
+{
+ bluray_input_plugin_t *this = (bluray_input_plugin_t *) this_gen;
+
+ if (!this || !this->bdh || !this->nav_title)
+ return -1;
+
+ /* convert relative seeks to absolute */
+
+ if (origin == SEEK_CUR) {
+ offset = this->bdh->s_pos + offset;
+ }
+ else if (origin == SEEK_END) {
+ if (offset < this->bdh->s_size)
+ offset = this->bdh->s_size - offset;
+ else
+ offset = 0;
+ }
+
+ /* clip seek point to nearest random access point */
+
+ uint32_t in_pkt = offset / PKT_SIZE;
+ uint32_t out_pkt = in_pkt;
+ uint32_t out_time = 0;
+ nav_packet_search(this->nav_title, in_pkt, &out_pkt, &out_time);
+ lprintf("bluray_plugin_seek() seeking to %"PRId64" (packet %d)\n", offset, in_pkt);
+ offset = (off_t)PKT_SIZE * (off_t)out_pkt;
+ lprintf("Nearest random access point at %"PRId64" (packet %d)\n", offset, out_pkt);
+
+ /* clip to aligned unit start */
+
+ offset -= (offset % ALIGNED_UNIT_SIZE);
+
+ /* seek */
+
+ lprintf("bluray_plugin_seek() seeking to %lld (aligned unit)\n", (long long)offset);
+
+ return bd_seek (this->bdh, offset);
+}
+
+static off_t bluray_plugin_get_current_pos (input_plugin_t *this_gen)
+{
+ bluray_input_plugin_t *this = (bluray_input_plugin_t *) this_gen;
+
+ return this->bdh ? this->bdh->s_pos : 0;
+}
+
+static off_t bluray_plugin_get_length (input_plugin_t *this_gen)
+{
+ bluray_input_plugin_t *this = (bluray_input_plugin_t *) this_gen;
+
+ return this->bdh ? this->bdh->s_size : -1;
+}
+
+static uint32_t bluray_plugin_get_blocksize (input_plugin_t *this_gen)
+{
+ return ALIGNED_UNIT_SIZE;
+}
+
+static const char* bluray_plugin_get_mrl (input_plugin_t *this_gen)
+{
+ bluray_input_plugin_t *this = (bluray_input_plugin_t *) this_gen;
+
+ return this->mrl;
+}
+
+static int bluray_plugin_get_optional_data (input_plugin_t *this_gen, void *data, int data_type)
+{
+ bluray_input_plugin_t *this = (bluray_input_plugin_t *) this_gen;
+
+ if (!this || !this->stream || !data)
+ return INPUT_OPTIONAL_UNSUPPORTED;
+
+ switch (data_type) {
+
+ case INPUT_OPTIONAL_DATA_DEMUXER:
+#ifdef HAVE_CONFIG_H
+ *(const char **)data = "mpeg-ts";
+#else
+ *(const char **)data = "mpeg-ts-hdmv";
+#endif
+ return INPUT_OPTIONAL_SUCCESS;
+
+ /*
+ * audio track language:
+ * - channel number can be mpeg-ts PID (0x1100 ... 0x11ff)
+ */
+ case INPUT_OPTIONAL_DATA_AUDIOLANG:
+ if (this->nav_title) {
+ int channel = *((int *)data);
+ CLPI_PROG *prog = &this->nav_title->clip_list.clip->cl->program.progs[0];
+ int i, n = 0;
+ for (i=0 ; i < prog->num_streams; i++)
+ if (prog->streams[i].pid >= 0x1100 && prog->streams[i].pid < 0x1200) {
+ /* audio stream #n */
+ if (channel == n || channel == prog->streams[i].pid) {
+ memcpy(data, prog->streams[i].lang, 4);
+
+ lprintf("INPUT_OPTIONAL_DATA_AUDIOLANG: ch %d pid %x: %s\n",
+ channel, prog->streams[i].pid, prog->streams[i].lang);
+
+ return INPUT_OPTIONAL_SUCCESS;
+ }
+ n++;
+ }
+ }
+ return INPUT_OPTIONAL_UNSUPPORTED;
+
+ /*
+ * SPU track language:
+ * - channel number can be mpeg-ts PID (0x1200 ... 0x12ff)
+ */
+ case INPUT_OPTIONAL_DATA_SPULANG:
+ if (this->nav_title) {
+ int channel = *((int *)data);
+ CLPI_PROG *prog = &this->nav_title->clip_list.clip->cl->program.progs[0];
+ int i, n = 0;
+ for (i=0 ; i < prog->num_streams; i++)
+ if (prog->streams[i].pid >= 0x1200 && prog->streams[i].pid < 0x1300 &&
+ prog->streams[i].coding_type >= 0x90 && prog->streams[i].coding_type <= 0x92) {
+ /* subtitle stream #n */
+ if (channel == n || channel == prog->streams[i].pid) {
+
+ memcpy(data, prog->streams[i].lang, 4);
+
+ lprintf("INPUT_OPTIONAL_DATA_SPULANG: ch %d pid %x: %s\n",
+ channel, prog->streams[i].pid, prog->streams[i].lang);
+
+ return INPUT_OPTIONAL_SUCCESS;
+ }
+ n++;
+ }
+ }
+ return INPUT_OPTIONAL_UNSUPPORTED;
+
+ default:
+ return DEMUX_OPTIONAL_UNSUPPORTED;
+ }
+
+ return INPUT_OPTIONAL_UNSUPPORTED;
+}
+
+static void bluray_plugin_dispose (input_plugin_t *this_gen)
+{
+ bluray_input_plugin_t *this = (bluray_input_plugin_t *) this_gen;
+
+ if (this->bdh)
+ bd_close(this->bdh);
+
+ if (this->nav_title)
+ nav_title_close(this->nav_title);
+
+ free (this->mrl);
+ free (this->disc_root);
+
+ free (this);
+}
+
+static int bluray_plugin_open (input_plugin_t *this_gen)
+{
+ bluray_input_plugin_t *this = (bluray_input_plugin_t *) this_gen;
+ int title = -1, chapter = 0;
+
+ lprintf("bluray_plugin_open\n");
+
+ /* validate mrl */
+
+ if (strncasecmp (this->mrl, "bluray:", 7))
+ return -1;
+
+ if (!strcasecmp (this->mrl, "bluray:") ||
+ !strcasecmp (this->mrl, "bluray:/") ||
+ !strcasecmp (this->mrl, "bluray://") ||
+ !strcasecmp (this->mrl, "bluray:///")) {
+
+ this->disc_root = strdup(this->class->mountpoint);
+
+ } else if (!strncasecmp (this->mrl, "bluray:/", 8)) {
+
+ if (!strncasecmp (this->mrl, "bluray:///", 10))
+ this->disc_root = strdup(this->mrl + 9);
+ else if (!strncasecmp (this->mrl, "bluray://", 9))
+ this->disc_root = strdup(this->mrl + 8);
+ else
+ this->disc_root = strdup(this->mrl + 7);
+
+ _x_mrl_unescape(this->disc_root);
+
+ if (this->disc_root[strlen(this->disc_root)-1] != '/') {
+ char *end = strrchr(this->disc_root, '/');
+ if (end && end[1])
+ if (sscanf(end, "/%d.%d", &title, &chapter) < 1)
+ title = 0;
+ *end = 0;
+ }
+
+ } else {
+ return -1;
+ }
+
+ /* if title was not in mrl, find the main title */
+
+ if (title < 0) {
+ char *main_title = nav_find_main_title(this->disc_root);
+ title = 0;
+ if (main_title) {
+ if (sscanf(main_title, "%d.mpls", &title) != 1)
+ title = 0;
+ lprintf("main title: %s (%d) \n", main_title, title);
+ } else {
+ LOGMSG("nav_find_main_title(%s) failed\n", this->disc_root);
+ }
+ }
+
+ /* open libbluray */
+
+ /* replace ~/ in keyfile path */
+ char *keyfile = NULL;
+ if (this->class->keyfile && !strncmp(this->class->keyfile, "~/", 2))
+ if (asprintf(&keyfile, "%s/%s", xine_get_homedir(), this->class->keyfile + 2) < 0)
+ keyfile = NULL;
+ /* open */
+ if (! (this->bdh = bd_open (this->disc_root, keyfile ?: this->class->keyfile))) {
+ LOGMSG("bd_open(\'%s\') failed: %s\n", this->disc_root, strerror(errno));
+ free(keyfile);
+ return -1;
+ }
+ free(keyfile);
+ lprintf("bd_open(\'%s\') OK\n", this->disc_root);
+
+ /* select title */
+
+ if (open_title(this, title) < 0)
+ return -1;
+
+ /* jump to chapter */
+
+ if (chapter > 0) {
+ uint32_t out_pkt = 0;
+ NAV_CLIP *clip = nav_chapter_search(this->nav_title, chapter, &out_pkt);
+ bluray_plugin_seek(&this->input_plugin, (off_t)(clip->title_pkt + out_pkt) * PKT_SIZE, SEEK_SET);
+
+ _x_stream_info_set(this->stream, XINE_STREAM_INFO_DVD_CHAPTER_NUMBER, chapter);
+ }
+
+ return 1;
+}
+
+static input_plugin_t *bluray_class_get_instance (input_class_t *cls_gen, xine_stream_t *stream,
+ const char *mrl)
+{
+ bluray_input_plugin_t *this;
+
+ lprintf("bluray_class_get_instance\n");
+
+ if (strncasecmp (mrl, "bluray:", 7))
+ return NULL;
+
+ this = (bluray_input_plugin_t *) calloc(1, sizeof (bluray_input_plugin_t));
+
+ this->stream = stream;
+ this->class = (bluray_input_class_t*)cls_gen;
+ this->mrl = strdup(mrl);
+
+ this->input_plugin.open = bluray_plugin_open;
+ this->input_plugin.get_capabilities = bluray_plugin_get_capabilities;
+ this->input_plugin.read = bluray_plugin_read;
+ this->input_plugin.read_block = bluray_plugin_read_block;
+ this->input_plugin.seek = bluray_plugin_seek;
+ this->input_plugin.get_current_pos = bluray_plugin_get_current_pos;
+ this->input_plugin.get_length = bluray_plugin_get_length;
+ this->input_plugin.get_blocksize = bluray_plugin_get_blocksize;
+ this->input_plugin.get_mrl = bluray_plugin_get_mrl;
+ this->input_plugin.get_optional_data = bluray_plugin_get_optional_data;
+ this->input_plugin.dispose = bluray_plugin_dispose;
+ this->input_plugin.input_class = cls_gen;
+
+ return &this->input_plugin;
+}
+
+/*
+ * plugin class
+ */
+
+static void mountpoint_change_cb(void *data, xine_cfg_entry_t *cfg)
+{
+ bluray_input_class_t *this = (bluray_input_class_t *) data;
+
+ this->mountpoint = cfg->str_value;
+}
+
+static void device_change_cb(void *data, xine_cfg_entry_t *cfg)
+{
+ bluray_input_class_t *this = (bluray_input_class_t *) data;
+
+ this->device = cfg->str_value;
+}
+
+static void keyfile_change_cb(void *data, xine_cfg_entry_t *cfg)
+{
+ bluray_input_class_t *this = (bluray_input_class_t *) data;
+
+ this->keyfile = cfg->str_value;
+}
+
+static const char *bluray_class_get_description (input_class_t *this_gen)
+{
+ return _("BluRay input plugin");
+}
+
+static const char *bluray_class_get_identifier (input_class_t *this_gen)
+{
+ return "bluray";
+}
+
+static char **bluray_class_get_autoplay_list (input_class_t *this_gen, int *num_files)
+{
+ static char *autoplay_list[] = { "bluray:/", NULL };
+
+ *num_files = 1;
+
+ return autoplay_list;
+}
+
+static int bluray_class_eject_media (input_class_t *this_gen)
+{
+#if 0
+ bluray_input_class_t *this = (bluray_input_class_t*) this_gen;
+
+ return media_eject_media (this->xine, this->device);
+#endif
+ return 1;
+}
+
+static void bluray_class_dispose (input_class_t *this_gen)
+{
+ bluray_input_class_t *this = (bluray_input_class_t *) this_gen;
+ config_values_t *config = this->xine->config;
+
+ config->unregister_callback(config, "media.bluray.mountpoint");
+ config->unregister_callback(config, "media.bluray.device");
+ config->unregister_callback(config, "media.bluray.keyfile");
+
+ free (this);
+}
+
+static void *bluray_init_plugin (xine_t *xine, void *data)
+{
+ config_values_t *config = xine->config;
+ bluray_input_class_t *this = (bluray_input_class_t *) calloc(1, sizeof (bluray_input_class_t));
+
+ this->xine = xine;
+
+ this->input_class.get_instance = bluray_class_get_instance;
+ this->input_class.get_identifier = bluray_class_get_identifier;
+ this->input_class.get_description = bluray_class_get_description;
+ this->input_class.get_dir = NULL;
+ this->input_class.get_autoplay_list = bluray_class_get_autoplay_list;
+ this->input_class.dispose = bluray_class_dispose;
+ this->input_class.eject_media = bluray_class_eject_media;
+
+ this->mountpoint = config->register_filename(config, "media.bluray.mountpoint",
+ "/mnt/bluray", XINE_CONFIG_STRING_IS_DIRECTORY_NAME,
+ _("BluRay mount point"),
+ _("Default mount location for BluRay discs."),
+ 0, mountpoint_change_cb, (void *) this);
+ this->device = config->register_filename(config, "media.bluray.device",
+ "/dev/dvd", XINE_CONFIG_STRING_IS_DIRECTORY_NAME,
+ _("device used for BluRay playback"),
+ _("The path to the device "
+ "which you intend to use for playing BluRy discs."),
+ 0, device_change_cb, (void *) this);
+ this->keyfile = config->register_filename(config, "media.bluray.keyfile",
+ "~/.xine/aacskeys.bin", XINE_CONFIG_STRING_IS_DIRECTORY_NAME,
+ _("AACS key file"),
+ _("Location of libaacs key file."),
+ 0, keyfile_change_cb, (void *) this);
+
+ return this;
+}
+
+/*
+ * exported plugin catalog entry
+ */
+
+const plugin_info_t xine_plugin_info[] EXPORTED = {
+ /* type, API, "name", version, special_info, init_function */
+ { PLUGIN_INPUT | PLUGIN_MUST_PRELOAD, 17, "BLURAY", XINE_VERSION_CODE, NULL, bluray_init_plugin },
+ { PLUGIN_NONE, 0, "", 0, NULL, NULL }
+};
diff --git a/xine/BluRay/patches/xine-lib-1.1.16.3-ffmpeg-vc1-extradata.diff b/xine/BluRay/patches/xine-lib-1.1.16.3-ffmpeg-vc1-extradata.diff
new file mode 100644
index 00000000..c37c7e28
--- /dev/null
+++ b/xine/BluRay/patches/xine-lib-1.1.16.3-ffmpeg-vc1-extradata.diff
@@ -0,0 +1,62 @@
+--- src/combined/ffmpeg/ff_video_decoder.c 2009-04-16 14:24:20.000000000 +0300
++++ src/combined/ffmpeg/ff_video_decoder.c 2009-09-24 15:21:25.000000000 +0300
+@@ -1165,6 +1181,49 @@
+ }
+ }
+
++static int ff_vc1_find_header(ff_video_decoder_t *this, buf_element_t *buf)
++{
++ uint8_t *p = buf->content;
++
++ if (!p[0] && !p[1] && p[2] == 1 && p[3] == 0x0f) {
++ int i;
++
++ this->context->extradata = calloc(1, buf->size);
++ this->context->extradata_size = 0;
++
++ for (i = 0; i < buf->size && i < 128; i++) {
++ if (!p[i] && !p[i+1] && p[i+2]) {
++ lprintf("00 00 01 %02x at %d\n", p[i+3], i);
++ if (p[i+3] != 0x0e && p[i+3] != 0x0f)
++ break;
++ }
++ this->context->extradata[i] = p[i];
++ this->context->extradata_size++;
++ }
++
++ lprintf("ff_video_decoder: found VC1 sequence header\n");
++ return 1;
++ }
++
++ xprintf(this->stream->xine, XINE_VERBOSITY_DEBUG,
++ "ffmpeg_video_dec: VC1 extradata missing !\n");
++ return 0;
++}
++
++static int ff_check_extradata(ff_video_decoder_t *this, unsigned int codec_type, buf_element_t *buf)
++{
++ if (this->context && this->context->extradata)
++ return 1;
++
++ switch (codec_type) {
++ case BUF_VIDEO_VC1:
++ return ff_vc1_find_header(this, buf);
++ default:;
++ }
++
++ return 1;
++}
++
+ #endif /* AVCODEC_HAS_REORDERED_OPAQUE */
+ static void ff_handle_buffer (ff_video_decoder_t *this, buf_element_t *buf) {
+ uint8_t *chunk_buf = this->buf;
+@@ -1176,6 +1235,9 @@
+ if (this->decoder_init_mode) {
+ int codec_type = buf->type & 0xFFFF0000;
+
++ if (!ff_check_extradata(this, codec_type, buf))
++ return;
++
+ /* init ffmpeg decoder */
+ init_video_codec(this, codec_type);
+ init_postprocess(this);
diff --git a/xine/BluRay/patches/xine-lib-1.1.16.3-ffmpeg-vc1-reopen.diff b/xine/BluRay/patches/xine-lib-1.1.16.3-ffmpeg-vc1-reopen.diff
new file mode 100644
index 00000000..745931b5
--- /dev/null
+++ b/xine/BluRay/patches/xine-lib-1.1.16.3-ffmpeg-vc1-reopen.diff
@@ -0,0 +1,25 @@
+--- src/combined/ffmpeg/ff_video_decoder.c 2009-04-16 14:24:20.000000000 +0300
++++ src/combined/ffmpeg/ff_video_decoder.c 2009-09-24 15:21:25.000000000 +0300
+@@ -340,6 +340,22 @@
+ return;
+ }
+
++ if (this->codec->id == CODEC_ID_VC1 &&
++ (!this->bih.biWidth || !this->bih.biHeight)) {
++ /* VC1 codec must be re-opened with correct width and height. */
++ avcodec_close(this->context);
++
++ if (avcodec_open (this->context, this->codec) < 0) {
++ pthread_mutex_unlock(&ffmpeg_lock);
++ xprintf (this->stream->xine, XINE_VERBOSITY_LOG,
++ _("ffmpeg_video_dec: couldn't open decoder (pass 2)\n"));
++ free(this->context);
++ this->context = NULL;
++ _x_stream_info_set(this->stream, XINE_STREAM_INFO_VIDEO_HANDLED, 0);
++ return;
++ }
++ }
++
+ if (this->class->thread_count > 1) {
+ avcodec_thread_init(this->context, this->class->thread_count);
+ this->context->thread_count = this->class->thread_count;