diff options
author | cvs2svn <admin@example.com> | 2010-02-07 01:39:30 +0000 |
---|---|---|
committer | cvs2svn <admin@example.com> | 2010-02-07 01:39:30 +0000 |
commit | 3c2b5df6fbd812c3404cd18d062219bbec96d7e4 (patch) | |
tree | d7cc92e45d3b21d3567538e0e4d16ebb6bb1bb8e | |
parent | f7c1f7c41b2f59ff5f9bf5748949a7023b50662a (diff) | |
parent | 7a47234e4ed7c1761e4d3478dee2db9779ad79e3 (diff) | |
download | xineliboutput-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/Makefile | 167 | ||||
-rw-r--r-- | xine/BluRay/decode_spuhdmv.c | 1023 | ||||
-rw-r--r-- | xine/BluRay/demux_ts.c | 2604 | ||||
-rw-r--r-- | xine/BluRay/input_bluray.c | 667 | ||||
-rw-r--r-- | xine/BluRay/patches/xine-lib-1.1.16.3-ffmpeg-vc1-extradata.diff | 62 | ||||
-rw-r--r-- | xine/BluRay/patches/xine-lib-1.1.16.3-ffmpeg-vc1-reopen.diff | 25 |
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; |