From 188d7498b854233ac5f329fa342b16cbe1087d62 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Diego=20=27Flameeyes=27=20Petten=C3=B2?= Date: Sat, 22 Dec 2007 23:39:35 +0100 Subject: Move all the SubPicture decoders together in the spu_dec directory. Remove superfluous xine_ prefixes from source files. --HG-- rename : src/libspucc/cc_decoder.c => src/spu_dec/cc_decoder.c rename : src/libspucc/cc_decoder.h => src/spu_dec/cc_decoder.h rename : src/libspucmml/xine_cmml_decoder.c => src/spu_dec/cmml_decoder.c rename : src/libspudec/nav_read.c => src/spu_dec/nav_read.c rename : src/libspudec/xine_spu_decoder.c => src/spu_dec/spu_decoder.c rename : src/libspudec/spudec.c => src/spu_dec/spudec.c rename : src/libspudec/spudec.h => src/spu_dec/spudec.h rename : src/libspudvb/xine_spudvb_decoder.c => src/spu_dec/spudvb_decoder.c rename : src/libsputext/xine_sputext_decoder.c => src/spu_dec/sputext_decoder.c rename : src/libsputext/demux_sputext.c => src/spu_dec/sputext_demuxer.c rename : src/libspucc/xine_cc_decoder.c => src/spu_dec/xine_cc_decoder.c --- src/Makefile.am | 6 +- src/libspucc/Makefile.am | 12 - src/libspucc/cc_decoder.c | 1484 -------------------------------- src/libspucc/cc_decoder.h | 87 -- src/libspucc/xine_cc_decoder.c | 353 -------- src/libspucmml/Makefile.am | 9 - src/libspucmml/xine_cmml_decoder.c | 537 ------------ src/libspudec/Makefile.am | 21 - src/libspudec/nav_read.c | 1 - src/libspudec/spudec.c | 1026 ----------------------- src/libspudec/spudec.h | 142 ---- src/libspudec/xine_spu_decoder.c | 381 --------- src/libspudvb/Makefile.am | 9 - src/libspudvb/xine_spudvb_decoder.c | 999 ---------------------- src/libsputext/Makefile.am | 12 - src/libsputext/demux_sputext.c | 1489 --------------------------------- src/libsputext/xine_sputext_decoder.c | 992 ---------------------- src/spu_dec/Makefile.am | 39 + src/spu_dec/cc_decoder.c | 1484 ++++++++++++++++++++++++++++++++ src/spu_dec/cc_decoder.h | 87 ++ src/spu_dec/cmml_decoder.c | 537 ++++++++++++ src/spu_dec/nav_read.c | 1 + src/spu_dec/spu_decoder.c | 381 +++++++++ src/spu_dec/spudec.c | 1026 +++++++++++++++++++++++ src/spu_dec/spudec.h | 142 ++++ src/spu_dec/spudvb_decoder.c | 999 ++++++++++++++++++++++ src/spu_dec/sputext_decoder.c | 992 ++++++++++++++++++++++ src/spu_dec/sputext_demuxer.c | 1489 +++++++++++++++++++++++++++++++++ src/spu_dec/xine_cc_decoder.c | 353 ++++++++ 29 files changed, 7531 insertions(+), 7559 deletions(-) delete mode 100644 src/libspucc/Makefile.am delete mode 100644 src/libspucc/cc_decoder.c delete mode 100644 src/libspucc/cc_decoder.h delete mode 100644 src/libspucc/xine_cc_decoder.c delete mode 100644 src/libspucmml/Makefile.am delete mode 100644 src/libspucmml/xine_cmml_decoder.c delete mode 100644 src/libspudec/Makefile.am delete mode 100644 src/libspudec/nav_read.c delete mode 100644 src/libspudec/spudec.c delete mode 100644 src/libspudec/spudec.h delete mode 100644 src/libspudec/xine_spu_decoder.c delete mode 100644 src/libspudvb/Makefile.am delete mode 100644 src/libspudvb/xine_spudvb_decoder.c delete mode 100644 src/libsputext/Makefile.am delete mode 100644 src/libsputext/demux_sputext.c delete mode 100644 src/libsputext/xine_sputext_decoder.c create mode 100644 src/spu_dec/Makefile.am create mode 100644 src/spu_dec/cc_decoder.c create mode 100644 src/spu_dec/cc_decoder.h create mode 100644 src/spu_dec/cmml_decoder.c create mode 100644 src/spu_dec/nav_read.c create mode 100644 src/spu_dec/spu_decoder.c create mode 100644 src/spu_dec/spudec.c create mode 100644 src/spu_dec/spudec.h create mode 100644 src/spu_dec/spudvb_decoder.c create mode 100644 src/spu_dec/sputext_decoder.c create mode 100644 src/spu_dec/sputext_demuxer.c create mode 100644 src/spu_dec/xine_cc_decoder.c (limited to 'src') diff --git a/src/Makefile.am b/src/Makefile.am index a94673fb7..c9d9497ae 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -8,14 +8,10 @@ SUBDIRS = \ audio_dec \ video_out \ video_dec \ + spu_dec \ dxr3 \ input \ demuxers \ - libspudec \ - libspucc \ - libspucmml \ - libspudvb \ - libsputext \ libw32dll \ libreal \ post \ diff --git a/src/libspucc/Makefile.am b/src/libspucc/Makefile.am deleted file mode 100644 index 87266505c..000000000 --- a/src/libspucc/Makefile.am +++ /dev/null @@ -1,12 +0,0 @@ -include $(top_srcdir)/misc/Makefile.common - -AM_CFLAGS = $(DEFAULT_OCFLAGS) $(VISIBILITY_FLAG) -AM_LDFLAGS = $(xineplug_ldflags) - -noinst_HEADERS = cc_decoder.h - -xineplug_LTLIBRARIES = xineplug_decode_spucc.la - -xineplug_decode_spucc_la_SOURCES = cc_decoder.c xine_cc_decoder.c -xineplug_decode_spucc_la_LIBADD = $(XINE_LIB) -xineplug_decode_spucc_la_CFLAGS = $(AM_CFLAGS) -fno-strict-aliasing diff --git a/src/libspucc/cc_decoder.c b/src/libspucc/cc_decoder.c deleted file mode 100644 index e8fc09895..000000000 --- a/src/libspucc/cc_decoder.c +++ /dev/null @@ -1,1484 +0,0 @@ -/* - * Copyright (C) 2000-2003 the xine project - * - * Copyright (C) Christian Vogler - * cvogler@gradient.cis.upenn.edu - December 2001 - * - * 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 - * - * stuff needed to provide closed captioning decoding and display - * - * Some small bits and pieces of the EIA-608 captioning decoder were - * adapted from CCDecoder 0.9.1 by Mike Baker. The latest version is - * available at http://sourceforge.net/projects/ccdecoder/. - */ - -#include -#include -#include -#include -#include - -#include - -#include -#include -#include -#include -#include "cc_decoder.h" -#include - -/* -#define LOG_DEBUG 3 -*/ - -/* at 29.97 fps, each NTSC frame takes 3003 metronom ticks on the average. */ -#define NTSC_FRAME_DURATION 3003 - -#define CC_ROWS 15 -#define CC_COLUMNS 32 -#define CC_CHANNELS 2 - -/* 1 is the caption background color index in the OSD palettes. */ -#define CAP_BG_COL 1 - -/* number of text colors specified by EIA-608 standard */ -#define NUM_FG_COL 7 - -#ifndef WIN32 -/* colors specified by the EIA 608 standard */ -enum { WHITE, GREEN, BLUE, CYAN, RED, YELLOW, MAGENTA, BLACK, TRANSPARENT }; -#else -/* colors specified by the EIA 608 standard */ -enum { WHITE, GREEN, BLUE, CYAN, RED, YELLOW, MAGENTA, BLACK }; -#endif - - - -/* color mapping to OSD text color indices */ -static const int text_colormap[NUM_FG_COL] = { - OSD_TEXT1, OSD_TEXT2, OSD_TEXT3, OSD_TEXT4, OSD_TEXT5, OSD_TEXT6, OSD_TEXT7 -}; - - -/* -------------------- caption text colors -----------------------------*/ -/* FIXME: The colors look fine on an XShm display, but they look *terrible* - with the Xv display on the NVidia driver on a GeForce 3. The colors bleed - into each other more than I'd expect from the downsampling into YUV - colorspace. - At this moment, it looks like a problem in the Xv YUV blending functions. -*/ -typedef struct colorinfo_s { - clut_t bgcol; /* text background color */ - clut_t bordercol; /* text border color */ - clut_t textcol; /* text color */ -} colorinfo_t; - - -static const colorinfo_t cc_text_trans[NUM_FG_COL] = { - /* white, black border, translucid */ - { - CLUT_Y_CR_CB_INIT(0x80, 0x80, 0x80), - CLUT_Y_CR_CB_INIT(0x00, 0x80, 0x80), - CLUT_Y_CR_CB_INIT(0xff, 0x80, 0x80) - }, - - /* green, black border, translucid */ - { - CLUT_Y_CR_CB_INIT(0x80, 0x80, 0x80), - CLUT_Y_CR_CB_INIT(0x00, 0x80, 0x80), - CLUT_Y_CR_CB_INIT(0x90, 0x22, 0x35) - }, - - /* blue, black border, translucid */ - { - CLUT_Y_CR_CB_INIT(0x80, 0x80, 0x80), - CLUT_Y_CR_CB_INIT(0x00, 0x80, 0x80), - CLUT_Y_CR_CB_INIT(0x29, 0x6e, 0xff) - }, - - /* cyan, black border, translucid */ - { - CLUT_Y_CR_CB_INIT(0x80, 0x80, 0x80), - CLUT_Y_CR_CB_INIT(0x00, 0x80, 0x80), - CLUT_Y_CR_CB_INIT(0xaa, 0x10, 0xa6) - }, - - /* red, black border, translucid */ - { - CLUT_Y_CR_CB_INIT(0x80, 0x80, 0x80), - CLUT_Y_CR_CB_INIT(0x00, 0x80, 0x80), - CLUT_Y_CR_CB_INIT(0x52, 0xf0, 0x5a) - }, - - /* yellow, black border, translucid */ - { - CLUT_Y_CR_CB_INIT(0x80, 0x80, 0x80), - CLUT_Y_CR_CB_INIT(0x00, 0x80, 0x80), - CLUT_Y_CR_CB_INIT(0xd4, 0x92, 0x10) - }, - - /* magenta, black border, translucid */ - { - CLUT_Y_CR_CB_INIT(0x80, 0x80, 0x80), - CLUT_Y_CR_CB_INIT(0x00, 0x80, 0x80), - CLUT_Y_CR_CB_INIT(0x6b, 0xde, 0xca) - } -}; - -static const colorinfo_t cc_text_solid[NUM_FG_COL] = { - /* white, black border, solid */ - { - CLUT_Y_CR_CB_INIT(0x00, 0x80, 0x80), - CLUT_Y_CR_CB_INIT(0x00, 0x80, 0x80), - CLUT_Y_CR_CB_INIT(0xff, 0x80, 0x80) - }, - - /* green, black border, solid */ - { - CLUT_Y_CR_CB_INIT(0x00, 0x80, 0x80), - CLUT_Y_CR_CB_INIT(0x00, 0x80, 0x80), - CLUT_Y_CR_CB_INIT(0x90, 0x22, 0x35) - }, - - /* blue, black border, solid */ - { - CLUT_Y_CR_CB_INIT(0x00, 0x80, 0x80), - CLUT_Y_CR_CB_INIT(0x00, 0x80, 0x80), - CLUT_Y_CR_CB_INIT(0x29, 0x6e, 0xff) - }, - - /* cyan, black border, solid */ - { - CLUT_Y_CR_CB_INIT(0x00, 0x80, 0x80), - CLUT_Y_CR_CB_INIT(0x00, 0x80, 0x80), - CLUT_Y_CR_CB_INIT(0xaa, 0x10, 0xa6) - }, - - /* red, black border, solid */ - { - CLUT_Y_CR_CB_INIT(0x00, 0x80, 0x80), - CLUT_Y_CR_CB_INIT(0x00, 0x80, 0x80), - CLUT_Y_CR_CB_INIT(0x52, 0xf0, 0x5a) - }, - - /* yellow, black border, solid */ - { - CLUT_Y_CR_CB_INIT(0x00, 0x80, 0x80), - CLUT_Y_CR_CB_INIT(0x00, 0x80, 0x80), - CLUT_Y_CR_CB_INIT(0xd4, 0x92, 0x10) - }, - - /* magenta, black border, solid */ - { - CLUT_Y_CR_CB_INIT(0x00, 0x80, 0x80), - CLUT_Y_CR_CB_INIT(0x00, 0x80, 0x80), - CLUT_Y_CR_CB_INIT(0x6b, 0xde, 0xca) - } -}; - - -static const uint8_t cc_text_trans_alpha[TEXT_PALETTE_SIZE] = { - 0, 8, 9, 10, 11, 12, 15, 15, 15, 15, 15 -}; - -static const uint8_t cc_text_solid_alpha[TEXT_PALETTE_SIZE] = { - 0, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15 -}; - - -static const colorinfo_t *const cc_text_palettes[NUM_CC_PALETTES] = { - cc_text_trans, - cc_text_solid -}; - -static const uint8_t *const cc_alpha_palettes[NUM_CC_PALETTES] = { - cc_text_trans_alpha, - cc_text_solid_alpha -}; - -/* --------------------- misc. EIA 608 definitions -------------------*/ - -#define TRANSP_SPACE 0x19 /* code for transparent space, essentially - arbitrary */ - -#define MAX(a, b) ((a) > (b)? (a) : (b)) - -/* mapping from PAC row code to actual CC row */ -static const int rowdata[] = {10, -1, 0, 1, 2, 3, 11, 12, 13, 14, 4, 5, 6, - 7, 8, 9}; -/* FIXME: do real ™ (U+2122) */ -/* Code 182 must be mapped as a musical note ('♪', U+266A) in the caption font */ -static const char specialchar[] = { - 174 /* ® */, 176 /* ° */, 189 /* ½ */, 191 /* ¿ */, - 'T' /* ™ */, 162 /* ¢ */, 163 /* £ */, 182 /* ¶ => ♪ */, - 224 /* à */, TRANSP_SPACE,232 /* è */, 226 /* â */, - 234 /* ê */, 238 /* î */, 244 /* ô */, 251 /* û */ -}; - -/* character translation table - EIA 608 codes are not all the same as ASCII */ -static char chartbl[128]; - -/* CC codes use odd parity for error detection, since they originally were */ -/* transmitted via noisy video signals */ -static int parity_table[256]; - - -/*---------------- decoder data structures -----------------------*/ - -/* CC renderer */ -struct cc_renderer_s { - int video_width; /* video dimensions */ - int video_height; - - int x; /* coordinates of the captioning area */ - int y; - int width; - int height; - int max_char_height; /* captioning font properties */ - int max_char_width; - - osd_renderer_t *osd_renderer; /* active OSD renderer */ - osd_object_t *cap_display; /* caption display object */ - int displayed; /* true when caption currently is displayed */ - - /* the next variable is a hack: hiding a caption with vpts 0 doesn't seem - to work if the caption has been registered in the SPU event queue, but - not yet displayed. So we remember the vpts of the show event, and use - that as the vpts of the hide event upon an osd free. - */ -/*FIXME: bug in OSD or SPU?*/ - int64_t display_vpts; /* vpts of currently displayed caption */ - - /* this variable is an even worse hack: in some rare cases, the pts - information on the DVD gets out of sync with the caption information. - If this happens, the vpts of a hide caption event can actually be - slightly higher than the vpts of the following show caption event. - For this reason, we remember the vpts of the hide event and force - the next show event's vpts to be at least equal to the hide event's - vpts. - */ - int64_t last_hide_vpts; - - /* caption palette and alpha channel */ - uint32_t cc_palette[OVL_PALETTE_SIZE]; - uint8_t cc_trans[OVL_PALETTE_SIZE]; - - metronom_t *metronom; /* the active xine metronom */ - - cc_state_t *cc_state; /* captioning configuration */ -}; - - -/* CC attribute */ -typedef struct cc_attribute_s { - uint8_t italic; - uint8_t underline; - uint8_t foreground; - uint8_t background; -} cc_attribute_t; - -/* CC character cell */ -typedef struct cc_char_cell_s { - uint8_t c; /* character code, not the same as ASCII */ - cc_attribute_t attributes; /* attributes of this character, if changed */ - /* here */ - int midrow_attr; /* true if this cell changes an attribute */ -} cc_char_cell_t; - -/* a single row in the closed captioning memory */ -typedef struct cc_row_s { - cc_char_cell_t cells[CC_COLUMNS]; - int pos; /* position of the cursor */ - int num_chars; /* how many characters in the row are data */ - int attr_chg; /* true if midrow attr. change at cursor pos */ - int pac_attr_chg; /* true if attribute has changed via PAC */ - cc_attribute_t pac_attr; /* PAC attr. that hasn't been applied yet */ -} cc_row_t; - -/* closed captioning memory for a single channel */ -typedef struct cc_buffer_s { - cc_row_t rows[CC_ROWS]; - int rowpos; /* row cursor position */ -} cc_buffer_t; - -/* captioning memory for all channels */ -typedef struct cc_memory_s { - cc_buffer_t channel[CC_CHANNELS]; - int channel_no; /* currently active channel */ -} cc_memory_t; - -/* The closed captioning decoder data structure */ -struct cc_decoder_s { - /* CC decoder buffer - one onscreen, one offscreen */ - cc_memory_t buffer[2]; - /* onscreen, offscreen buffer ptrs */ - cc_memory_t *on_buf; - cc_memory_t *off_buf; - /* which buffer is active for receiving data */ - cc_memory_t **active; - - /* for logging and debugging purposes, captions are assigned increasing */ - /* unique ids. */ - uint32_t capid; - - /* the last captioning code seen (control codes are often sent twice - in a row, but should be processed only once) */ - uint32_t lastcode; - - /* The PTS and SCR at which the captioning chunk started */ - int64_t pts; - /* holds the NTSC frame offset to last known pts/scr */ - uint32_t f_offset; - - /* active OSD renderer */ - osd_renderer_t *renderer; - /* true when caption currently is displayed */ - int displayed; - - /* configuration and intrinsics of CC decoder */ - cc_state_t *cc_state; - - metronom_t *metronom; -}; - - -/*---------------- general utility functions ---------------------*/ - -static void get_font_metrics(osd_renderer_t *renderer, - const char *fontname, int font_size, - int *maxw, int *maxh) -{ - int c; - osd_object_t *testc = renderer->new_object(renderer, 640, 480); - - *maxw = 0; - *maxh = 0; - - renderer->set_font(testc, (char *) fontname, font_size); - renderer->set_encoding(testc, "iso-8859-1"); - for (c = 32; c < 256; c++) { - int tw, th; - char buf[2]; - - buf[0] = (char)c; - buf[1] = '\0'; - - renderer->get_text_size(testc, buf, &tw, &th); - *maxw = MAX(*maxw, tw); - *maxh = MAX(*maxh, th); - } - renderer->free_object(testc); -} - - -static int parity(uint8_t byte) -{ - int i; - int ones = 0; - - for (i = 0; i < 7; i++) { - if (byte & (1 << i)) - ones++; - } - - return ones & 1; -} - - -static void build_parity_table(void) -{ - uint8_t byte; - int parity_v; - for (byte = 0; byte <= 127; byte++) { - parity_v = parity(byte); - /* CC uses odd parity (i.e., # of 1's in byte is odd.) */ - parity_table[byte] = parity_v; - parity_table[byte | 0x80] = !parity_v; - } -} - - -static int good_parity(uint16_t data) -{ - int ret = parity_table[data & 0xff] && parity_table[(data & 0xff00) >> 8]; - if (! ret) - printf("Bad parity in EIA-608 data (%x)\n", data); - return ret; -} - - -static void build_char_table(void) -{ - int i; - /* first the normal ASCII codes */ - for (i = 0; i < 128; i++) - chartbl[i] = (char) i; - /* now the special codes */ - chartbl[0x2a] = 225; /* á */ - chartbl[0x5c] = 233; /* é */ - chartbl[0x5e] = 237; /* í */ - chartbl[0x5f] = 243; /* ó */ - chartbl[0x60] = 250; /* ú */ - chartbl[0x7b] = 231; /* ç */ - chartbl[0x7c] = 247; /* ÷ */ - chartbl[0x7d] = 209; /* Ñ */ - chartbl[0x7e] = 241; /* ñ */ - chartbl[0x7f] = 164; /* ¤ FIXME: should be a solid block ('█'; U+2588) */ -} - - -static clut_t interpolate_color(clut_t src, clut_t dest, int steps, - int current_step) -{ - int diff_y = ((int) dest.y) - ((int) src.y); - int diff_cr = ((int) dest.cr) - ((int) src.cr); - int diff_cb = ((int) dest.cb) - ((int) src.cb); - int res_y = ((int) src.y) + (diff_y * current_step / (steps + 1)); - int res_cr = ((int) src.cr) + (diff_cr * current_step / (steps + 1)); - int res_cb = ((int) src.cb) + (diff_cb * current_step / (steps + 1)); -#if __SUNPRO_C - /* - * Sun's Forte compiler refuses to initialize automatic structure - * variable with bitfields, so we use explicit assignments for now. - */ - clut_t res; - res.y = res_y; - res.cr = res_cr; - res.cb = res_cb; - res.foo = 0; -#else - clut_t res = CLUT_Y_CR_CB_INIT((uint8_t) res_y, (uint8_t) res_cr, - (uint8_t) res_cb); -#endif - return res; -} - -/*----------------- cc_row_t methods --------------------------------*/ - -static void ccrow_fill_transp(cc_row_t *rowbuf){ - int i; - -#ifdef LOG_DEBUG - printf("cc_decoder: ccrow_fill_transp: Filling in %d transparent spaces.\n", - rowbuf->pos - rowbuf->num_chars); -#endif - for (i = rowbuf->num_chars; i < rowbuf->pos; i++) { - rowbuf->cells[i].c = TRANSP_SPACE; - rowbuf->cells[i].midrow_attr = 0; - } -} - - -static int ccrow_find_next_text_part(cc_row_t *this, int pos) -{ - while (pos < this->num_chars && this->cells[pos].c == TRANSP_SPACE) - pos++; - return pos; -} - - -static int ccrow_find_end_of_text_part(cc_row_t *this, int pos) -{ - while (pos < this->num_chars && this->cells[pos].c != TRANSP_SPACE) - pos++; - return pos; -} - - -static int ccrow_find_current_attr(cc_row_t *this, int pos) -{ - while (pos > 0 && !this->cells[pos].midrow_attr) - pos--; - return pos; -} - - -static int ccrow_find_next_attr_change(cc_row_t *this, int pos, int lastpos) -{ - pos++; - while (pos < lastpos && !this->cells[pos].midrow_attr) - pos++; - return pos; -} - - -static void ccrow_set_attributes(cc_renderer_t *renderer, cc_row_t *this, - int pos) -{ - const cc_attribute_t *attr = &this->cells[pos].attributes; - const char *fontname; - cc_config_t *cap_info = renderer->cc_state->cc_cfg; - - if (attr->italic) - fontname = cap_info->italic_font; - else - fontname = cap_info->font; - renderer->osd_renderer->set_font(renderer->cap_display, (char *) fontname, - cap_info->font_size); -} - - -static void ccrow_render(cc_renderer_t *renderer, cc_row_t *this, int rownum) -{ - char buf[CC_COLUMNS + 1]; - int base_y; - int pos = ccrow_find_next_text_part(this, 0); - cc_config_t *cap_info = renderer->cc_state->cc_cfg; - osd_renderer_t *osd_renderer = renderer->osd_renderer; - - /* find y coordinate of caption */ - if (cap_info->center) { - /* find y-center of the desired row; the next line computes */ - /* cap_info->height * (rownum + 0.5) / CC_ROWS */ - /* in integer arithmetic for this purpose. */ - base_y = (renderer->height * rownum * 100 + renderer->height * 50) / - (CC_ROWS * 100); - } - else - base_y = renderer->height * rownum / CC_ROWS; - - /* break down captions into parts separated by transparent space, and */ - /* center each part individually along the x axis */ - while (pos < this->num_chars) { - int endpos = ccrow_find_end_of_text_part(this, pos); - int seg_begin = pos; - int seg_end; - int i; - int text_w = 0, text_h = 0; - int x, y; - int seg_w, seg_h; - int seg_pos[CC_COLUMNS + 1]; - int seg_attr[CC_COLUMNS]; - int cumulative_seg_width[CC_COLUMNS + 1]; - int num_seg = 0; - int seg; - - /* break down each part into segments bounded by attribute changes and */ - /* find text metrics of the parts */ - seg_pos[0] = seg_begin; - cumulative_seg_width[0] = 0; - while (seg_begin < endpos) { - int attr_pos = ccrow_find_current_attr(this, seg_begin); - seg_end = ccrow_find_next_attr_change(this, seg_begin, endpos); - - /* compute text size of segment */ - for (i = seg_begin; i < seg_end; i++) - buf[i - seg_begin] = this->cells[i].c; - buf[seg_end - seg_begin] = '\0'; - ccrow_set_attributes(renderer, this, attr_pos); - osd_renderer->get_text_size(renderer->cap_display, buf, - &seg_w, &seg_h); - - /* update cumulative segment statistics */ - text_w += seg_w; - text_h += seg_h; - seg_pos[num_seg + 1] = seg_end; - seg_attr[num_seg] = attr_pos; - cumulative_seg_width[num_seg + 1] = text_w; - num_seg++; - - seg_begin = seg_end; - } - - /* compute x coordinate of part */ - if (cap_info->center) { - int cell_width = renderer->width / CC_COLUMNS; - x = (renderer->width * (pos + endpos) / 2) / CC_COLUMNS; - x -= text_w / 2; - /* clamp x coordinate to nearest character cell */ - x = ((x + cell_width / 2) / CC_COLUMNS) * CC_COLUMNS + cell_width; - y = base_y - (renderer->max_char_height + 1) / 2; - } - else { - x = renderer->width * pos / CC_COLUMNS; - y = base_y; - } - -#ifdef LOG_DEBUG - printf("text_w, text_h = %d, %d\n", text_w, text_h); - printf("cc from %d to %d; text plotting from %d, %d (basey = %d)\n", pos, endpos, x, y, base_y); -#endif - - /* render text part by rendering each attributed text segment */ - for (seg = 0; seg < num_seg; seg++) { - int textcol = text_colormap[this->cells[seg_attr[seg]].attributes.foreground]; - int box_x1 = x + cumulative_seg_width[seg]; - int box_x2 = x + cumulative_seg_width[seg + 1]; - -#ifdef LOG_DEBUG - printf("ccrow_render: rendering segment %d from %d to %d / %d to %d\n", - seg, seg_pos[seg], seg_pos[seg + 1], - x + cumulative_seg_width[seg], x + cumulative_seg_width[seg + 1]); -#endif - /* make caption background a uniform box. Without this line, the */ - /* background is uneven for superscript characters. */ - /* Also pad left & right ends of caption to make it more readable */ -/*FIXME: There may be off-by one errors in the rendering - check with Miguel*/ - if (seg == 0) - box_x1 -= renderer->max_char_width; - if (seg == num_seg - 1) - box_x2 += renderer->max_char_width; - osd_renderer->filled_rect(renderer->cap_display, box_x1, y, box_x2, - y + renderer->max_char_height, - textcol + CAP_BG_COL); - - for (i = seg_pos[seg]; i < seg_pos[seg + 1]; i++) - buf[i - seg_pos[seg]] = this->cells[i].c; - buf[seg_pos[seg + 1] - seg_pos[seg]] = '\0'; - ccrow_set_attributes(renderer, this, seg_attr[seg]); - - /* text is already mapped from EIA-608 into iso-8859-1 */ - osd_renderer->render_text(renderer->cap_display, - x + cumulative_seg_width[seg], y, buf, - textcol); - } - - pos = ccrow_find_next_text_part(this, endpos); - } -} - - -/*----------------- cc_buffer_t methods --------------------------------*/ - -static int ccbuf_has_displayable(cc_buffer_t *this) -{ - int i; - int found = 0; - for (i = 0; !found && i < CC_ROWS; i++) { - if (this->rows[i].num_chars > 0) - found = 1; - } - return found; -} - - -static void ccbuf_add_char(cc_buffer_t *this, uint8_t c) -{ - cc_row_t *rowbuf = &this->rows[this->rowpos]; - int pos = rowbuf->pos; - int left_displayable = (pos > 0) && (pos <= rowbuf->num_chars); - -#if LOG_DEBUG > 2 - printf("cc_decoder: ccbuf_add_char: %c @ %d/%d\n", c, this->rowpos, pos); -#endif - - if (pos >= CC_COLUMNS) { - printf("cc_decoder: ccbuf_add_char: row buffer overflow\n"); - return; - } - - if (pos > rowbuf->num_chars) { - /* fill up to indented position with transparent spaces, if necessary */ - ccrow_fill_transp(rowbuf); - } - - /* midrow PAC attributes are applied only if there is no displayable */ - /* character to the immediate left. This makes the implementation rather */ - /* complicated, but this is what the EIA-608 standard specifies. :-( */ - if (rowbuf->pac_attr_chg && !rowbuf->attr_chg && !left_displayable) { - rowbuf->attr_chg = 1; - rowbuf->cells[pos].attributes = rowbuf->pac_attr; -#ifdef LOG_DEBUG - printf("cc_decoder: ccbuf_add_char: Applying midrow PAC.\n"); -#endif - } - - rowbuf->cells[pos].c = c; - rowbuf->cells[pos].midrow_attr = rowbuf->attr_chg; - rowbuf->pos++; - - if (rowbuf->num_chars < rowbuf->pos) - rowbuf->num_chars = rowbuf->pos; - - rowbuf->attr_chg = 0; - rowbuf->pac_attr_chg = 0; -} - - -static void ccbuf_set_cursor(cc_buffer_t *this, int row, int column, - int underline, int italics, int color) -{ - cc_row_t *rowbuf = &this->rows[row]; - cc_attribute_t attr; - - attr.italic = italics; - attr.underline = underline; - attr.foreground = color; - attr.background = BLACK; - - rowbuf->pac_attr = attr; - rowbuf->pac_attr_chg = 1; - - this->rowpos = row; - rowbuf->pos = column; - rowbuf->attr_chg = 0; -} - - -static void ccbuf_apply_attribute(cc_buffer_t *this, cc_attribute_t *attr) -{ - cc_row_t *rowbuf = &this->rows[this->rowpos]; - int pos = rowbuf->pos; - - rowbuf->attr_chg = 1; - rowbuf->cells[pos].attributes = *attr; - /* A midrow attribute always counts as a space */ - ccbuf_add_char(this, chartbl[(unsigned int) ' ']); -} - - -static void ccbuf_tab(cc_buffer_t *this, int tabsize) -{ - cc_row_t *rowbuf = &this->rows[this->rowpos]; - rowbuf->pos += tabsize; - if (rowbuf->pos > CC_COLUMNS) { -#ifdef LOG_DEBUG - printf("cc_decoder: ccbuf_tab: row buffer overflow\n"); -#endif - rowbuf->pos = CC_COLUMNS; - return; - } - /* tabs have no effect on pending PAC attribute changes */ -} - - -static void ccbuf_render(cc_renderer_t *renderer, cc_buffer_t *this) -{ - int row; - -#ifdef LOG_DEBUG - printf("cc_decoder: ccbuf_render\n"); -#endif - - for (row = 0; row < CC_ROWS; ++row) { - if (this->rows[row].num_chars > 0) - ccrow_render(renderer, &this->rows[row], row); - } -} - - -/*----------------- cc_memory_t methods --------------------------------*/ - -static void ccmem_clear(cc_memory_t *this) -{ -#ifdef LOG_DEBUG - printf("cc_decoder.c: ccmem_clear: Clearing CC memory\n"); -#endif - memset(this, 0, sizeof (cc_memory_t)); -} - - -static void ccmem_init(cc_memory_t *this) -{ - ccmem_clear(this); -} - - -static void ccmem_exit(cc_memory_t *this) -{ -/*FIXME: anything to deallocate?*/ -} - - -/*----------------- cc_renderer_t methods -------------------------------*/ - -static void cc_renderer_build_palette(cc_renderer_t *this) -{ - int i, j; - const colorinfo_t *cc_text = cc_text_palettes[this->cc_state->cc_cfg->cc_scheme]; - const uint8_t *cc_alpha = cc_alpha_palettes[this->cc_state->cc_cfg->cc_scheme]; - - memset(this->cc_palette, 0, sizeof (this->cc_palette)); - memset(this->cc_trans, 0, sizeof (this->cc_trans)); - for (i = 0; i < NUM_FG_COL; i++) { - /* background color */ - this->cc_palette[i * TEXT_PALETTE_SIZE + 1 + OSD_TEXT1] = - *(uint32_t *) &cc_text[i].bgcol; - /* background -> border */ - for (j = 2; j <= 5; j++) { - clut_t col = interpolate_color(cc_text[i].bgcol, - cc_text[i].bordercol, 4, j - 1); - this->cc_palette[i * TEXT_PALETTE_SIZE + j + OSD_TEXT1] = - *(uint32_t *) &col; - } - /* border color */ - this->cc_palette[i * TEXT_PALETTE_SIZE + 6 + OSD_TEXT1] = - *(uint32_t *) &cc_text[i].bordercol; - /* border -> foreground */ - for (j = 7; j <= 9; j++) { - clut_t col = interpolate_color(cc_text[i].bordercol, - cc_text[i].textcol, 3, j - 6); - this->cc_palette[i * TEXT_PALETTE_SIZE + j + OSD_TEXT1] = - *(uint32_t *) &col; - } - /* foreground color */ - this->cc_palette[i * TEXT_PALETTE_SIZE + 10 + OSD_TEXT1] = - *(uint32_t *) &cc_text[i].textcol; - - /* alpha values */ - for (j = 0; j <= 10; j++) - this->cc_trans[i * TEXT_PALETTE_SIZE + j + OSD_TEXT1] = cc_alpha[j]; - } -} - - -static int64_t cc_renderer_calc_vpts(cc_renderer_t *this, int64_t pts, - uint32_t ntsc_frame_offset) -{ - metronom_t *metronom = this->metronom; - int64_t vpts = metronom->got_spu_packet(metronom, pts); - return vpts + ntsc_frame_offset * NTSC_FRAME_DURATION; -} - - -/* returns true if a caption is on display */ -static int cc_renderer_on_display(cc_renderer_t *this) -{ - return this->displayed; -} - - -static void cc_renderer_hide_caption(cc_renderer_t *this, int64_t vpts) -{ - if (this->displayed) { - this->osd_renderer->hide(this->cap_display, vpts); - this->displayed = 0; - this->last_hide_vpts = vpts; - } -} - - -static void cc_renderer_show_caption(cc_renderer_t *this, cc_buffer_t *buf, - int64_t vpts) -{ -#ifdef LOG_DEBUG - printf("spucc: cc_renderer: show\n"); -#endif - - if (this->displayed) { - cc_renderer_hide_caption(this, vpts); - printf("spucc: cc_renderer: show: OOPS - caption was already displayed!\n"); - } - - this->osd_renderer->clear(this->cap_display); - ccbuf_render(this, buf); - this->osd_renderer->set_position(this->cap_display, - this->x, - this->y); - vpts = MAX(vpts, this->last_hide_vpts); - this->osd_renderer->show(this->cap_display, vpts); - - this->displayed = 1; - this->display_vpts = vpts; -} - - -static void cc_renderer_free_osd_object(cc_renderer_t *this) -{ - /* hide and free old displayed caption object if necessary */ - if (this->cap_display) { - cc_renderer_hide_caption(this, this->display_vpts); - this->osd_renderer->free_object(this->cap_display); - this->cap_display = NULL; - } -} - - -static void cc_renderer_adjust_osd_object(cc_renderer_t *this) -{ - cc_renderer_free_osd_object(this); - -#ifdef LOG_DEBUG - printf("spucc: cc_renderer: adjust_osd_object: creating %dx%d OSD object\n", - this->width, this->height); -#endif - - /* create display object */ - this->cap_display = this->osd_renderer->new_object(this->osd_renderer, - this->width, - this->height); - this->osd_renderer->set_palette(this->cap_display, this->cc_palette, - this->cc_trans); - this->osd_renderer->set_encoding(this->cap_display, "iso-8859-1"); -} - - -cc_renderer_t *cc_renderer_open(osd_renderer_t *osd_renderer, - metronom_t *metronom, cc_state_t *cc_state, - int video_width, int video_height) -{ - cc_renderer_t *this = (cc_renderer_t *) xine_xmalloc(sizeof (cc_renderer_t)); - - this->osd_renderer = osd_renderer; - this->metronom = metronom; - this->cc_state = cc_state; - cc_renderer_update_cfg(this, video_width, video_height); -#ifdef LOG_DEBUG - printf("spucc: cc_renderer: open\n"); -#endif - return this; -} - - -void cc_renderer_close(cc_renderer_t *this_obj) -{ - cc_renderer_free_osd_object(this_obj); - free(this_obj); - -#ifdef LOG_DEBUG - printf("spucc: cc_renderer: close\n"); -#endif -} - - -void cc_renderer_update_cfg(cc_renderer_t *this_obj, int video_width, - int video_height) -{ - int fontw, fonth; - int required_w, required_h; - - this_obj->video_width = video_width; - this_obj->video_height = video_height; - - /* fill in text palette */ - cc_renderer_build_palette(this_obj); - - /* calculate preferred captioning area, as per the EIA-608 standard */ - this_obj->x = this_obj->video_width * 10 / 100; - this_obj->y = this_obj->video_height * 10 / 100; - this_obj->width = this_obj->video_width * 80 / 100; - this_obj->height = this_obj->video_height * 80 / 100; - - /* find maximum text width and height for normal & italic captioning */ - /* font */ - get_font_metrics(this_obj->osd_renderer, this_obj->cc_state->cc_cfg->font, - this_obj->cc_state->cc_cfg->font_size, &fontw, &fonth); - this_obj->max_char_width = fontw; - this_obj->max_char_height = fonth; - get_font_metrics(this_obj->osd_renderer, this_obj->cc_state->cc_cfg->italic_font, - this_obj->cc_state->cc_cfg->font_size, &fontw, &fonth); - this_obj->max_char_width = MAX(fontw, this_obj->max_char_width); - this_obj->max_char_height = MAX(fonth, this_obj->max_char_height); -#ifdef LOG_DEBUG - printf("spucc: cc_renderer: update config: max text extents: %d, %d\n", - this_obj->max_char_width, this_obj->max_char_height); -#endif - - /* need to adjust captioning area to accommodate font? */ - required_w = CC_COLUMNS * (this_obj->max_char_width + 1); - required_h = CC_ROWS * (this_obj->max_char_height + 1); - if (required_w > this_obj->width) { -#ifdef LOG_DEBUG - printf("spucc: cc_renderer: update config: adjusting cap area width: %d\n", - required_w); -#endif - this_obj->width = required_w; - this_obj->x = (this_obj->video_width - required_w) / 2; - } - if (required_h > this_obj->height) { -#ifdef LOG_DEBUG - printf("spucc: cc_renderer: update config: adjusting cap area height: %d\n", - required_h); -#endif - this_obj->height = required_h; - this_obj->y = (this_obj->video_height - required_h) / 2; - } - - if (required_w <= this_obj->video_width && - required_h <= this_obj->video_height) { - this_obj->cc_state->can_cc = 1; - cc_renderer_adjust_osd_object(this_obj); - } - else { - this_obj->cc_state->can_cc = 0; - cc_renderer_free_osd_object(this_obj); - printf("spucc: required captioning area %dx%d exceeds screen %dx%d!\n" - " Captions disabled. Perhaps you should choose a smaller" - " font?\n", - required_w, required_h, this_obj->video_width, - this_obj->video_height); - } -} - - -/*----------------- cc_decoder_t methods --------------------------------*/ - -static void cc_set_channel(cc_decoder_t *this, int channel) -{ - (*this->active)->channel_no = channel; -#ifdef LOG_DEBUG - printf("cc_decoder: cc_set_channel: selecting channel %d\n", channel); -#endif -} - - -static cc_buffer_t *active_ccbuffer(cc_decoder_t *this) -{ - cc_memory_t *mem = *this->active; - return &mem->channel[mem->channel_no]; -} - - -static int cc_onscreen_displayable(cc_decoder_t *this) -{ - return ccbuf_has_displayable(&this->on_buf->channel[this->on_buf->channel_no]); -} - - -static void cc_hide_displayed(cc_decoder_t *this) -{ -#ifdef LOG_DEBUG - printf("cc_decoder: cc_hide_displayed\n"); -#endif - - if (cc_renderer_on_display(this->cc_state->renderer)) { - int64_t vpts = cc_renderer_calc_vpts(this->cc_state->renderer, this->pts, - this->f_offset); -#ifdef LOG_DEBUG - printf("cc_decoder: cc_hide_displayed: hiding caption %u at vpts %u\n", this->capid, vpts); -#endif - cc_renderer_hide_caption(this->cc_state->renderer, vpts); - } -} - - -static void cc_show_displayed(cc_decoder_t *this) -{ -#ifdef LOG_DEBUG - printf("cc_decoder: cc_show_displayed\n"); -#endif - - if (cc_onscreen_displayable(this)) { - int64_t vpts = cc_renderer_calc_vpts(this->cc_state->renderer, this->pts, - this->f_offset); -#ifdef LOG_DEBUG - printf("cc_decoder: cc_show_displayed: showing caption %u at vpts %u\n", this->capid, vpts); -#endif - this->capid++; - cc_renderer_show_caption(this->cc_state->renderer, - &this->on_buf->channel[this->on_buf->channel_no], - vpts); - } -} - - -static void cc_swap_buffers(cc_decoder_t *this) -{ - cc_memory_t *temp; - - /* hide caption in displayed memory */ - cc_hide_displayed(this); - -#ifdef LOG_DEBUG - printf("cc_decoder: cc_swap_buffers: swapping caption memory\n"); -#endif - temp = this->on_buf; - this->on_buf = this->off_buf; - this->off_buf = temp; - - /* show new displayed memory */ - cc_show_displayed(this); -} - -static void cc_decode_standard_char(cc_decoder_t *this, uint8_t c1, uint8_t c2) -{ - cc_buffer_t *buf = active_ccbuffer(this); - /* c1 always is a valid character */ - ccbuf_add_char(buf, chartbl[c1]); - /* c2 might not be a printable character, even if c1 was */ - if (c2 & 0x60) - ccbuf_add_char(buf, chartbl[c2]); -} - - -static void cc_decode_PAC(cc_decoder_t *this, int channel, - uint8_t c1, uint8_t c2) -{ - cc_buffer_t *buf; - int row, column = 0; - int underline, italics = 0, color; - - /* There is one invalid PAC code combination. Ignore it. */ - if (c1 == 0x10 && c2 > 0x5f) - return; - - cc_set_channel(this, channel); - buf = active_ccbuffer(this); - - row = rowdata[((c1 & 0x07) << 1) | ((c2 & 0x20) >> 5)]; - if (c2 & 0x10) { - column = ((c2 & 0x0e) >> 1) * 4; /* preamble indentation */ - color = WHITE; /* indented lines have white color */ - } - else if ((c2 & 0x0e) == 0x0e) { - italics = 1; /* italics, they are always white */ - color = WHITE; - } - else - color = (c2 & 0x0e) >> 1; - underline = c2 & 0x01; - -#ifdef LOG_DEBUG - printf("cc_decoder: cc_decode_PAC: row %d, col %d, ul %d, it %d, clr %d\n", - row, column, underline, italics, color); -#endif - - ccbuf_set_cursor(buf, row, column, underline, italics, color); -} - - -static void cc_decode_ext_attribute(cc_decoder_t *this, int channel, - uint8_t c1, uint8_t c2) -{ - cc_set_channel(this, channel); -} - - -static void cc_decode_special_char(cc_decoder_t *this, int channel, - uint8_t c1, uint8_t c2) -{ - cc_buffer_t *buf; - - cc_set_channel(this, channel); - buf = active_ccbuffer(this); -#ifdef LOG_DEBUG - printf("cc_decoder: cc_decode_special_char: Mapping %x to %x\n", c2, specialchar[c2 & 0xf]); -#endif - ccbuf_add_char(buf, specialchar[c2 & 0xf]); -} - - -static void cc_decode_midrow_attr(cc_decoder_t *this, int channel, - uint8_t c1, uint8_t c2) -{ - cc_buffer_t *buf; - cc_attribute_t attr; - - cc_set_channel(this, channel); - buf = active_ccbuffer(this); - if (c2 < 0x2e) { - attr.italic = 0; - attr.foreground = (c2 & 0xe) >> 1; - } - else { - attr.italic = 1; - attr.foreground = WHITE; - } - attr.underline = c2 & 0x1; - attr.background = BLACK; -#ifdef LOG_DEBUG - printf("cc_decoder: cc_decode_midrow_attr: attribute %x\n", c2); - printf("cc_decoder: cc_decode_midrow_attr: ul %d, it %d, clr %d\n", - attr.underline, attr.italic, attr.foreground); -#endif - - ccbuf_apply_attribute(buf, &attr); -} - - -static void cc_decode_misc_control_code(cc_decoder_t *this, int channel, - uint8_t c1, uint8_t c2) -{ -#ifdef LOG_DEBUG - printf("cc_decoder: decode_misc: decoding %x %x\n", c1, c2); -#endif - - cc_set_channel(this, channel); - - switch (c2) { /* 0x20 <= c2 <= 0x2f */ - - case 0x20: /* RCL */ - break; - - case 0x21: /* backspace */ -#ifdef LOG_DEBUG - printf("cc_decoder: backspace\n"); -#endif - break; - - case 0x24: /* DER */ - break; - - case 0x25: /* RU2 */ - break; - - case 0x26: /* RU3 */ - break; - - case 0x27: /* RU4 */ - break; - - case 0x28: /* FON */ - break; - - case 0x29: /* RDC */ - break; - - case 0x2a: /* TR */ - break; - - case 0x2b: /* RTD */ - break; - - case 0x2c: /* EDM - erase displayed memory */ - cc_hide_displayed(this); - ccmem_clear(this->on_buf); - break; - - case 0x2d: /* carriage return */ - break; - - case 0x2e: /* ENM - erase non-displayed memory */ - ccmem_clear(this->off_buf); - break; - - case 0x2f: /* EOC - swap displayed and non displayed memory */ - cc_swap_buffers(this); - break; - } -} - - -static void cc_decode_tab(cc_decoder_t *this, int channel, - uint8_t c1, uint8_t c2) -{ - cc_buffer_t *buf; - - cc_set_channel(this, channel); - buf = active_ccbuffer(this); - ccbuf_tab(buf, c2 & 0x3); -} - - -static void cc_decode_EIA608(cc_decoder_t *this, uint16_t data) -{ - uint8_t c1 = data & 0x7f; - uint8_t c2 = (data >> 8) & 0x7f; - -#if LOG_DEBUG >= 3 - printf("decoding %x %x\n", c1, c2); -#endif - - if (c1 & 0x60) { /* normal character, 0x20 <= c1 <= 0x7f */ - cc_decode_standard_char(this, c1, c2); - } - else if (c1 & 0x10) { /* control code or special character */ - /* 0x10 <= c1 <= 0x1f */ - int channel = (c1 & 0x08) >> 3; - c1 &= ~0x08; - - /* control sequences are often repeated. In this case, we should */ - /* evaluate it only once. */ - if (data != this->lastcode) { - - if (c2 & 0x40) { /* preamble address code: 0x40 <= c2 <= 0x7f */ - cc_decode_PAC(this, channel, c1, c2); - } - else { - switch (c1) { - - case 0x10: /* extended background attribute code */ - cc_decode_ext_attribute(this, channel, c1, c2); - break; - - case 0x11: /* attribute or special character */ - if ((c2 & 0x30) == 0x30) { /* special char: 0x30 <= c2 <= 0x3f */ - cc_decode_special_char(this, channel, c1, c2); - } - else if (c2 & 0x20) { /* midrow attribute: 0x20 <= c2 <= 0x2f */ - cc_decode_midrow_attr(this, channel, c1, c2); - } - break; - - case 0x14: /* possibly miscellaneous control code */ - cc_decode_misc_control_code(this, channel, c1, c2); - break; - - case 0x17: /* possibly misc. control code TAB offset */ - /* 0x21 <= c2 <= 0x23 */ - if (c2 >= 0x21 && c2 <= 0x23) { - cc_decode_tab(this, channel, c1, c2); - } - break; - } - } - } - } - - this->lastcode = data; -} - - -void decode_cc(cc_decoder_t *this, uint8_t *buffer, uint32_t buf_len, - int64_t pts) -{ - /* The first number may denote a channel number. I don't have the - * EIA-708 standard, so it is hard to say. - * From what I could figure out so far, the general format seems to be: - * - * repeat - * - * 0xfe starts 2 byte sequence of unknown purpose. It might denote - * field #2 in line 21 of the VBI. We'll ignore it for the - * time being. - * - * 0xff starts 2 byte EIA-608 sequence, field #1 in line 21 of the VBI. - * Followed by a 3-code triplet that starts either with 0xff or - * 0xfe. In either case, the following triplet needs to be ignored - * for line 21, field 1. - * - * 0x00 is padding, followed by 2 more 0x00. - * - * 0x01 always seems to appear at the beginning, always seems to - * be followed by 0xf8, 8-bit number. - * The lower 7 bits of this 8-bit number seem to denote the - * number of code triplets that follow. - * The most significant bit denotes whether the Line 21 field 1 - * captioning information is at odd or even triplet offsets from this - * beginning triplet. 1 denotes odd offsets, 0 denotes even offsets. - * - * Most captions are encoded with odd offsets, so this is what we - * will assume. - * - * until end of packet - */ - uint8_t *current = buffer; - uint32_t curbytes = 0; - uint8_t data1, data2; - uint8_t cc_code; - int odd_offset = 1; - - this->f_offset = 0; - this->pts = pts; - -#if LOG_DEBUG >= 2 - printf("libspucc: decode_cc: got pts %u\n", pts); - { - uint8_t *cur_d = buffer; - printf("libspucc: decode_cc: codes: "); - while (cur_d < buffer + buf_len) { - printf("0x%0x ", *cur_d++); - } - printf("\n"); - } -#endif - - while (curbytes < buf_len) { - int skip = 2; - - cc_code = *current++; - curbytes++; - - if (buf_len - curbytes < 2) { -#ifdef LOG_DEBUG - fprintf(stderr, "Not enough data for 2-byte CC encoding\n"); -#endif - break; - } - - data1 = *current; - data2 = *(current + 1); - - switch (cc_code) { - case 0xfe: - /* expect 2 byte encoding (perhaps CC3, CC4?) */ - /* ignore for time being */ - skip = 2; - break; - - case 0xff: - /* expect EIA-608 CC1/CC2 encoding */ - if (good_parity(data1 | (data2 << 8))) { - cc_decode_EIA608(this, data1 | (data2 << 8)); - this->f_offset++; - } - skip = 5; - break; - - case 0x00: - /* This seems to be just padding */ - skip = 2; - break; - - case 0x01: - odd_offset = data2 & 0x80; - if (odd_offset) - skip = 2; - else - skip = 5; - break; - - default: -#ifdef LOG_DEBUG - fprintf(stderr, "Unknown CC encoding: %x\n", cc_code); -#endif - skip = 2; - break; - } - current += skip; - curbytes += skip; - } -} - - - -cc_decoder_t *cc_decoder_open(cc_state_t *cc_state) -{ - cc_decoder_t *this = (cc_decoder_t *) xine_xmalloc(sizeof (cc_decoder_t)); - /* configfile stuff */ - this->cc_state = cc_state; - - ccmem_init(&this->buffer[0]); - ccmem_init(&this->buffer[1]); - this->on_buf = &this->buffer[0]; - this->off_buf = &this->buffer[1]; - this->active = &this->off_buf; - - this->lastcode = 0; - this->capid = 0; - - this->pts = this->f_offset = 0; - -#ifdef LOG_DEBUG - printf("spucc: cc_decoder_open\n"); -#endif - return this; -} - - -void cc_decoder_close(cc_decoder_t *this) -{ - ccmem_exit(&this->buffer[0]); - ccmem_exit(&this->buffer[1]); - - free(this); - -#ifdef LOG_DEBUG - printf("spucc: cc_decoder_close\n"); -#endif -} - - -/*--------------- initialization methods --------------------------*/ - -void cc_decoder_init(void) -{ - build_parity_table(); - build_char_table(); -} - diff --git a/src/libspucc/cc_decoder.h b/src/libspucc/cc_decoder.h deleted file mode 100644 index 3924bb8be..000000000 --- a/src/libspucc/cc_decoder.h +++ /dev/null @@ -1,87 +0,0 @@ -/* - * Copyright (C) 2000-2003 the xine project - * - * Copyright (C) Christian Vogler - * cvogler@gradient.cis.upenn.edu - December 2001 - * - * 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 - * - * stuff needed to provide closed captioning decoding and display - * - * Some small bits and pieces of the EIA-608 captioning decoder were - * adapted from CCDecoder 0.9.1 by Mike Baker. The latest version is - * available at http://sourceforge.net/projects/ccdecoder/. - */ - -typedef struct cc_decoder_s cc_decoder_t; -typedef struct cc_renderer_s cc_renderer_t; - -#define NUM_CC_PALETTES 2 -static const char *const cc_schemes[NUM_CC_PALETTES + 1] = { - "White/Gray/Translucent", - "White/Black/Solid", - NULL -}; - -#define CC_FONT_MAX 256 - -typedef struct cc_config_s { - int cc_enabled; /* true if closed captions are enabled */ - char font[CC_FONT_MAX]; /* standard captioning font & size */ - int font_size; - char italic_font[CC_FONT_MAX]; /* italic captioning font & size */ - int center; /* true if captions should be centered */ - /* according to text width */ - int cc_scheme; /* which captioning scheme to use */ - - int config_version; /* the decoder should be updated when this is increased */ -} cc_config_t; - -typedef struct spucc_class_s { - spu_decoder_class_t spu_class; - cc_config_t cc_cfg; -} spucc_class_t; - -typedef struct cc_state_s { - cc_config_t *cc_cfg; - /* the following variables are not controlled by configuration files; they */ - /* are intrinsic to the properties of the configuration options and the */ - /* currently played video */ - int can_cc; /* true if captions can be displayed */ - /* (e.g., font fits on screen) */ - cc_renderer_t *renderer; /* closed captioning renderer */ -} cc_state_t; - -cc_decoder_t *cc_decoder_open(cc_state_t *cc_state); -void cc_decoder_close(cc_decoder_t *this_obj); -void cc_decoder_init(void); - -void decode_cc(cc_decoder_t *this, uint8_t *buffer, uint32_t buf_len, - int64_t pts); - -/* Instantiates a new closed captioning renderer. */ -cc_renderer_t *cc_renderer_open(osd_renderer_t *osd_renderer, - metronom_t *metronom, cc_state_t *cc_state, - int video_width, int video_height); - -/* Destroys a closed captioning renderer. */ -void cc_renderer_close(cc_renderer_t *this_obj); - -/* Updates the renderer configuration variables */ -void cc_renderer_update_cfg(cc_renderer_t *this_obj, int video_width, - int video_height); - diff --git a/src/libspucc/xine_cc_decoder.c b/src/libspucc/xine_cc_decoder.c deleted file mode 100644 index e8a02a996..000000000 --- a/src/libspucc/xine_cc_decoder.c +++ /dev/null @@ -1,353 +0,0 @@ -/* - * 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 - * - * closed caption spu decoder. receive data by events. - */ - -#include -#include -#include - -#include -#include -#include -#include "cc_decoder.h" - -/* -#define LOG_DEBUG 1 -*/ - -typedef struct spucc_decoder_s { - spu_decoder_t spu_decoder; - - xine_stream_t *stream; - - /* closed captioning decoder state */ - cc_decoder_t *ccdec; - /* true if ccdec has been initialized */ - int cc_open; - - /* closed captioning decoder configuration and intrinsics */ - cc_state_t cc_state; - /* this is to detect configuration changes */ - int config_version; - - /* video dimensions captured in frame change events */ - int video_width; - int video_height; - - /* events will be sent here */ - xine_event_queue_t *queue; - -} spucc_decoder_t; - - -/*------------------- general utility functions ----------------------------*/ - -static void copy_str(char *d, const char *s, size_t maxbytes) -{ - strncpy(d, s, maxbytes - 1); - d[maxbytes - 1] = '\0'; -} - - -/*------------------- private methods --------------------------------------*/ - -static void spucc_update_intrinsics(spucc_decoder_t *this) -{ -#ifdef LOG_DEBUG - printf("spucc: update_intrinsics\n"); -#endif - - if (this->cc_open) - cc_renderer_update_cfg(this->cc_state.renderer, this->video_width, - this->video_height); -} - -static void spucc_do_close(spucc_decoder_t *this) -{ - if (this->cc_open) { -#ifdef LOG_DEBUG - printf("spucc: close\n"); -#endif - cc_decoder_close(this->ccdec); - cc_renderer_close(this->cc_state.renderer); - this->cc_open = 0; - } -} - -static void spucc_do_init (spucc_decoder_t *this) -{ - if (! this->cc_open) { -#ifdef LOG_DEBUG - printf("spucc: init\n"); -#endif - /* initialize caption renderer */ - this->cc_state.renderer = cc_renderer_open(this->stream->osd_renderer, - this->stream->metronom, - &this->cc_state, - this->video_width, - this->video_height); - spucc_update_intrinsics(this); - /* initialize CC decoder */ - this->ccdec = cc_decoder_open(&this->cc_state); - this->cc_open = 1; - } -} - - -/*----------------- configuration listeners --------------------------------*/ - -static void spucc_cfg_enable_change(void *this_gen, xine_cfg_entry_t *value) -{ - spucc_class_t *this = (spucc_class_t *) this_gen; - cc_config_t *cc_cfg = &this->cc_cfg; - - cc_cfg->cc_enabled = value->num_value; -#ifdef LOG_DEBUG - printf("spucc: closed captions are now %s.\n", cc_cfg->cc_enabled? - "enabled" : "disabled"); -#endif - cc_cfg->config_version++; -} - - -static void spucc_cfg_scheme_change(void *this_gen, xine_cfg_entry_t *value) -{ - spucc_class_t *this = (spucc_class_t *) this_gen; - cc_config_t *cc_cfg = &this->cc_cfg; - - cc_cfg->cc_scheme = value->num_value; -#ifdef LOG_DEBUG - printf("spucc: closed captioning scheme is now %s.\n", - cc_schemes[cc_cfg->cc_scheme]); -#endif - cc_cfg->config_version++; -} - - -static void spucc_font_change(void *this_gen, xine_cfg_entry_t *value) -{ - spucc_class_t *this = (spucc_class_t *) this_gen; - cc_config_t *cc_cfg = &this->cc_cfg; - char *font; - - if (strcmp(value->key, "subtitles.closedcaption.font") == 0) - font = cc_cfg->font; - else - font = cc_cfg->italic_font; - - copy_str(font, value->str_value, CC_FONT_MAX); -#ifdef LOG_DEBUG - printf("spucc: changing %s to font %s\n", value->key, font); -#endif - cc_cfg->config_version++; -} - - -static void spucc_num_change(void *this_gen, xine_cfg_entry_t *value) -{ - spucc_class_t *this = (spucc_class_t *) this_gen; - cc_config_t *cc_cfg = &this->cc_cfg; - int *num; - - if (strcmp(value->key, "subtitles.closedcaption.font_size") == 0) - num = &cc_cfg->font_size; - else - num = &cc_cfg->center; - - *num = value->num_value; -#ifdef LOG_DEBUG - printf("spucc: changing %s to %d\n", value->key, *num); -#endif - cc_cfg->config_version++; -} - - -static void spucc_register_cfg_vars(spucc_class_t *this, - config_values_t *xine_cfg) { - cc_config_t *cc_vars = &this->cc_cfg; - - cc_vars->cc_enabled = xine_cfg->register_bool(xine_cfg, - "subtitles.closedcaption.enabled", 0, - _("display closed captions in MPEG-2 streams"), - _("Closed Captions are subtitles mostly meant " - "to help the hearing impaired."), - 0, spucc_cfg_enable_change, this); - - cc_vars->cc_scheme = xine_cfg->register_enum(xine_cfg, - "subtitles.closedcaption.scheme", 0, - cc_schemes, - _("closed-captioning foreground/background scheme"), - _("Choose your favourite rendering of the closed " - "captions."), - 10, spucc_cfg_scheme_change, this); - - copy_str(cc_vars->font, - xine_cfg->register_string(xine_cfg, "subtitles.closedcaption.font", "cc", - _("standard closed captioning font"), - _("Choose the font for standard closed captions text."), - 20, spucc_font_change, this), - CC_FONT_MAX); - - copy_str(cc_vars->italic_font, - xine_cfg->register_string(xine_cfg, "subtitles.closedcaption.italic_font", "cci", - _("italic closed captioning font"), - _("Choose the font for italic closed captions text."), - 20, spucc_font_change, this), - CC_FONT_MAX); - - cc_vars->font_size = xine_cfg->register_num(xine_cfg, "subtitles.closedcaption.font_size", - 24, - _("closed captioning font size"), - _("Choose the font size for closed captions text."), - 10, spucc_num_change, this); - - cc_vars->center = xine_cfg->register_bool(xine_cfg, "subtitles.closedcaption.center", 1, - _("center-adjust closed captions"), - _("When enabled, closed captions will be positioned " - "by the center of the individual lines."), - 20, spucc_num_change, this); -} - - -/* called when the video frame size changes */ -static void spucc_notify_frame_change(spucc_decoder_t *this, - int width, int height) { -#ifdef LOG_DEBUG - printf("spucc: new frame size: %dx%d\n", width, height); -#endif - - this->video_width = width; - this->video_height = height; - spucc_update_intrinsics(this); -} - - -/*------------------- implementation of spudec interface -------------------*/ - -static void spudec_decode_data (spu_decoder_t *this_gen, buf_element_t *buf) { - spucc_decoder_t *this = (spucc_decoder_t *) this_gen; - xine_event_t *event; - - while ((event = xine_event_get(this->queue))) { - switch (event->type) { - case XINE_EVENT_FRAME_FORMAT_CHANGE: - { - xine_format_change_data_t *frame_change = - (xine_format_change_data_t *)event->data; - - spucc_notify_frame_change(this, frame_change->width, - frame_change->height); - } - break; - } - xine_event_free(event); - } - - if (buf->decoder_flags & BUF_FLAG_PREVIEW) { - } else { - - if (this->cc_state.cc_cfg->config_version > this->config_version) { - spucc_update_intrinsics(this); - if (!this->cc_state.cc_cfg->cc_enabled) - spucc_do_close(this); - this->config_version = this->cc_state.cc_cfg->config_version; - } - - if (this->cc_state.cc_cfg->cc_enabled) { - if( !this->cc_open ) - spucc_do_init (this); - if(this->cc_state.can_cc) { - decode_cc(this->ccdec, buf->content, buf->size, - buf->pts); - } - } - } -} - -static void spudec_reset (spu_decoder_t *this_gen) { -} - -static void spudec_discontinuity (spu_decoder_t *this_gen) { -} - -static void spudec_dispose (spu_decoder_t *this_gen) { - spucc_decoder_t *this = (spucc_decoder_t *) this_gen; - - spucc_do_close(this); - xine_event_dispose_queue(this->queue); - free (this); -} - - -static spu_decoder_t *spudec_open_plugin (spu_decoder_class_t *class, xine_stream_t *stream) { - - spucc_decoder_t *this ; - - this = (spucc_decoder_t *) xine_xmalloc (sizeof (spucc_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->queue = xine_event_new_queue(stream); - this->cc_state.cc_cfg = &((spucc_class_t *)class)->cc_cfg; - this->config_version = 0; - this->cc_open = 0; - - cc_decoder_init(); - - return &this->spu_decoder; -} - -static void *init_spu_decoder_plugin (xine_t *xine, void *data) { - - spucc_class_t *this ; - - this = (spucc_class_t *) xine_xmalloc (sizeof (spucc_class_t)); - - this->spu_class.open_plugin = spudec_open_plugin; - this->spu_class.identifier = "spucc"; - this->spu_class.description = N_("closed caption decoder plugin"); - this->spu_class.dispose = default_spu_decoder_class_dispose; - - spucc_register_cfg_vars(this, xine->config); - this->cc_cfg.config_version = 0; - - return &this->spu_class; -} - -/* plugin catalog information */ -static uint32_t supported_types[] = { BUF_SPU_CC, 0 }; - -static const decoder_info_t spudec_info = { - supported_types, /* supported types */ - 1 /* priority */ -}; - -const plugin_info_t xine_plugin_info[] EXPORTED = { - /* type, API, "name", version, special_info, init_function */ - { PLUGIN_SPU_DECODER, 17, "spucc", XINE_VERSION_CODE, &spudec_info, &init_spu_decoder_plugin }, - { PLUGIN_NONE, 0, "", 0, NULL, NULL } -}; diff --git a/src/libspucmml/Makefile.am b/src/libspucmml/Makefile.am deleted file mode 100644 index 85f5dbcd9..000000000 --- a/src/libspucmml/Makefile.am +++ /dev/null @@ -1,9 +0,0 @@ -include $(top_srcdir)/misc/Makefile.common - -AM_CFLAGS = $(DEFAULT_OCFLAGS) $(VISIBILITY_FLAG) -AM_LDFLAGS = $(xineplug_ldflags) - -xineplug_LTLIBRARIES = xineplug_decode_spucmml.la - -xineplug_decode_spucmml_la_SOURCES = xine_cmml_decoder.c -xineplug_decode_spucmml_la_LIBADD = $(XINE_LIB) $(LTLIBINTL) diff --git a/src/libspucmml/xine_cmml_decoder.c b/src/libspucmml/xine_cmml_decoder.c deleted file mode 100644 index 6ce6d3f90..000000000 --- a/src/libspucmml/xine_cmml_decoder.c +++ /dev/null @@ -1,537 +0,0 @@ -/* - * 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 - */ - -#define LOG_MODULE "libspucmml" -#define LOG_VERBOSE -/* -#define LOG -*/ -#define LOG_OSD 0 -#define LOG_SCHEDULING 0 -#define LOG_WIDTH 0 - -#define SUB_BUFSIZE 1024 -#define SUB_MAX_TEXT 5 - -#include - -typedef enum { - SUBTITLE_SIZE_SMALL = 0, - SUBTITLE_SIZE_NORMAL, - SUBTITLE_SIZE_LARGE, - - SUBTITLE_SIZE_NUM /* number of values in enum */ -} subtitle_size; - - -typedef struct spucmml_class_s { - spu_decoder_class_t class; - char *src_encoding; /* encoding of subtitle file */ - xine_t *xine; - -} spucmml_class_t; - - -typedef struct cmml_anchor_s { - char *text; - char *href; -} cmml_anchor_t; - - -typedef struct spucmml_decoder_s { - spu_decoder_t spu_decoder; - - spucmml_class_t *class; - xine_stream_t *stream; - - xine_event_queue_t *event_queue; - - int lines; - char text[SUB_MAX_TEXT][SUB_BUFSIZE]; - - int cached_width; /* frame width */ - int cached_height; /* frame height */ - int64_t cached_img_duration; - int font_size; - int line_height; - int master_started; - int slave_started; - - char *font; /* subtitle font */ - subtitle_size subtitle_size; /* size of subtitles */ - int vertical_offset; - - osd_object_t *osd; - - cmml_anchor_t current_anchor; -} spucmml_decoder_t; - - -static void video_frame_format_change_callback (void *user_data, const xine_event_t *event); - - -static void update_font_size (spucmml_decoder_t *this) { - static int sizes[SUBTITLE_SIZE_NUM][4] = { - { 16, 16, 16, 20 }, /* SUBTITLE_SIZE_SMALL */ - { 16, 16, 20, 24 }, /* SUBTITLE_SIZE_NORMAL */ - { 16, 20, 24, 32 }, /* SUBTITLE_SIZE_LARGE */ - }; - - int *vec = sizes[this->subtitle_size]; - int y; - - if( this->cached_width >= 512 ) - this->font_size = vec[3]; - else if( this->cached_width >= 384 ) - this->font_size = vec[2]; - else if( this->cached_width >= 320 ) - this->font_size = vec[1]; - else - this->font_size = vec[0]; - - this->line_height = this->font_size + 10; - - y = this->cached_height - (SUB_MAX_TEXT * this->line_height) - 5; - - if(((y - this->vertical_offset) >= 0) && ((y - this->vertical_offset) <= this->cached_height)) - y -= this->vertical_offset; - - /* TODO: we should move this stuff below into another function */ - - if (this->osd) - this->stream->osd_renderer->free_object (this->osd); - - llprintf (LOG_OSD, - "pre new_object: osd=%p, osd_renderer=%p, width=%d, height=%d\n", - this->osd, - this->stream->osd_renderer, - this->cached_width, - SUB_MAX_TEXT * this->line_height); - - this->osd = this->stream->osd_renderer->new_object (this->stream->osd_renderer, - this->cached_width, SUB_MAX_TEXT * this->line_height); - - llprintf (LOG_OSD, "post new_object: osd is %p\n", this->osd); - - if(this->stream->osd_renderer) { - this->stream->osd_renderer->set_font (this->osd, this->font, this->font_size); - this->stream->osd_renderer->set_position (this->osd, 0, y); - } -} - -static int get_width(spucmml_decoder_t *this, char* text) { - size_t i=0; - int width=0,w,dummy; - char letter[2]={0, 0}; - - while (i<=strlen(text)) { - switch (text[i]) { - case '<': - if (!strncmp("", text+i, 3)) { - /*Do somethink to enable BOLD typeface*/ - i=i+3; - break; - } else if (!strncmp("", text+i, 3)) { - /*Do somethink to disable BOLD typeface*/ - i=i+4; - break; - } else if (!strncmp("", text+i, 3)) { - /*Do somethink to enable italics typeface*/ - i=i+3; - break; - } else if (!strncmp("", text+i, 3)) { - /*Do somethink to disable italics typeface*/ - i=i+4; - break; - } else if (!strncmp("", text+i, 3)) { - /*Do somethink to disable typing - fixme - no teststreams*/ - i=i+6; - break; - } else if (!strncmp("", text+i, 3)) { - /*Do somethink to enable typing - fixme - no teststreams*/ - i=i+7; - break; - } - default: - letter[0]=text[i]; - this->stream->osd_renderer->get_text_size(this->osd, letter, &w, &dummy); - width=width+w; - i++; - } - } - - llprintf(LOG_WIDTH, "get_width returning width of %d\n", width); - - return width; -} - -static void render_line(spucmml_decoder_t *this, int x, int y, char* text) { - size_t i=0; - int w,dummy; - char letter[2]={0,0}; - - while (i<=strlen(text)) { - letter[0]=text[i]; - this->stream->osd_renderer->render_text(this->osd, x, y, letter, OSD_TEXT1); - this->stream->osd_renderer->get_text_size(this->osd, letter, &w, &dummy); - x=x+w; - i++; - } -} - -static void draw_subtitle(spucmml_decoder_t *this, int64_t sub_start) { - - int line, y; - int font_size; - - this->stream->osd_renderer->filled_rect (this->osd, 0, 0, - this->cached_width-1, this->line_height * SUB_MAX_TEXT - 1, 0); - - y = (SUB_MAX_TEXT - this->lines) * this->line_height; - font_size = this->font_size; - this->stream->osd_renderer->set_encoding(this->osd, this->class->src_encoding); - - for (line=0; linelines; line++) { - int w,x; - while(1) { - w=get_width( this, this->text[line]); - x = (this->cached_width - w) / 2; - - if( w > this->cached_width && font_size > 16 ) { - font_size -= 4; - this->stream->osd_renderer->set_font (this->osd, this->font, font_size); - } else { - break; - } - } - render_line(this, x, y + line*this->line_height, this->text[line]); - } - - if( font_size != this->font_size ) - this->stream->osd_renderer->set_font (this->osd, this->font, this->font_size); - - - this->stream->osd_renderer->set_text_palette (this->osd, -1, OSD_TEXT1); - this->stream->osd_renderer->show (this->osd, sub_start); - - llprintf (LOG_SCHEDULING, - "spucmml: scheduling subtitle >%s< at %"PRId64", current time is %"PRId64"\n", - this->text[0], sub_start, - this->stream->xine->clock->get_current_time (this->stream->xine->clock)); -} - -static void spudec_decode_data (spu_decoder_t *this_gen, buf_element_t *buf) { - - spucmml_decoder_t *this = (spucmml_decoder_t *) this_gen; - char *str; - - xml_node_t *packet_xml_root; - char * anchor_text = NULL; - - lprintf("CMML packet seen\n"); - - str = (char *) buf->content; - - /* parse the CMML */ - - xml_parser_init (str, strlen (str), XML_PARSER_CASE_INSENSITIVE); - if (xml_parser_build_tree(&packet_xml_root) != XML_PARSER_OK) { - lprintf ("warning: invalid XML packet detected in CMML track\n"); - return; - } - - if (strcasecmp(packet_xml_root->name, "head") == 0) { - /* found a ... packet: need to parse the title */ - - xml_node_t *title_node; - - /* iterate through children trying to find the title node */ - - for (title_node = packet_xml_root->child; title_node != NULL; title_node = title_node->next) { - - if (strcasecmp (title_node->name, "title") == 0) { - /* found a title node */ - - xine_event_t uevent; - char *title; - int title_len; - - title = title_node->data; - - if (title) - { - xine_ui_data_t data; - /* found a non-empty title */ - lprintf ("found title: \"%s\"\n", title); - - /* set xine meta-info */ - _x_meta_info_set(this->stream, XINE_META_INFO_TITLE, strdup(title)); - - /* and push out a new event signifying the title update on the event - * queue */ - title_len = strlen(title) + 1; - memcpy(data.str, title, title_len); - data.str_len = title_len; - - uevent.type = XINE_EVENT_UI_SET_TITLE; - uevent.stream = this->stream; - uevent.data = &data; - uevent.data_length = sizeof(data); - xine_event_send(this->stream, &uevent); - } - } - } - } else if (strcasecmp(packet_xml_root->name, "clip") == 0) { - /* found a ... packet: search for the in it */ - xml_node_t *clip_node; - - /* iterate through each tag contained in the tag to look for */ - - for (clip_node = packet_xml_root->child; clip_node != NULL; clip_node = clip_node->next) { - - if (strcasecmp (clip_node->name, "a") == 0) { - xml_property_t *href_property; - - /* found the tag: grab its value and its href property */ - - if (clip_node->data) - anchor_text = strdup (clip_node->data); - - for (href_property = clip_node->props; href_property != NULL; href_property = href_property->next) { - if (strcasecmp (href_property->name, "href") == 0) { - /* found the href property */ - char *href = href_property->value; - - if (href) { - lprintf ("found href: \"%s\"\n", href); - this->current_anchor.href = strdup(href); - } - } - } - } - } - } - - /* finish here if we don't have to process any anchor text */ - if (!anchor_text) - return; - - /* how many lines does the anchor text take up? */ - this->lines=0; - { - int i = 0; - while (*anchor_text) { - if (*anchor_text == '\r' || *anchor_text == '\n') { - if (i) { - /* match a newline and there are chars on the current line ... */ - this->text[ this->lines ][i] = '\0'; - this->lines++; - i = 0; - } - } else { - /* found a normal (non-line-ending) character */ - this->text[ this->lines ][i] = *anchor_text; - if (itext[ this->lines ][i] = '\0'; - this->lines++; - } - } - - /* initialize decoder if needed */ - if( !this->cached_width || !this->cached_height || !this->cached_img_duration || !this->osd ) { - if( this->stream->video_out->status(this->stream->video_out, NULL, - &this->cached_width, &this->cached_height, &this->cached_img_duration )) { - if( this->cached_width && this->cached_height && this->cached_img_duration ) { - lprintf("this->stream->osd_renderer is %p\n", this->stream->osd_renderer); - } - } - } - - update_font_size (this); - - if( this->osd ) { - draw_subtitle(this, buf->pts); - return; - } else { - lprintf ("libspucmml: no osd\n"); - } - - return; -} - -static void video_frame_format_change_callback (void *user_data, const xine_event_t *event) -{ - /* this doesn't do anything for now: it's a start at attempting to display - * CMML clips which occur at 0 seconds into the track. see - * - * http://marc.theaimsgroup.com/?l=xine-devel&m=109202443013890&w=2 - * - * for a description of the problem. */ - - switch (event->type) { - case XINE_EVENT_FRAME_FORMAT_CHANGE: - lprintf("video_frame_format_change_callback called!\n"); - break; - default: - lprintf("video_frame_format_change_callback called with unknown event %d\n", event->type); - break; - } -} - -static void spudec_reset (spu_decoder_t *this_gen) { - spucmml_decoder_t *this = (spucmml_decoder_t *) this_gen; - - this->cached_width = this->cached_height = 0; -} - -static void spudec_discontinuity (spu_decoder_t *this_gen) { - /* do nothing */ -} - -static void spudec_dispose (spu_decoder_t *this_gen) { - spucmml_decoder_t *this = (spucmml_decoder_t *) this_gen; - - if (this->event_queue) - xine_event_dispose_queue (this->event_queue); - - if (this->osd) { - this->stream->osd_renderer->free_object (this->osd); - this->osd = NULL; - } - free(this); -} - -static void update_vertical_offset(void *this_gen, xine_cfg_entry_t *entry) -{ - spucmml_decoder_t *this = (spucmml_decoder_t *)this_gen; - - this->vertical_offset = entry->num_value; - update_font_size(this); -} - -static void update_osd_font(void *this_gen, xine_cfg_entry_t *entry) -{ - spucmml_decoder_t *this = (spucmml_decoder_t *)this_gen; - - this->font = entry->str_value; - - if( this->stream->osd_renderer ) - this->stream->osd_renderer->set_font (this->osd, this->font, this->font_size); -} - -static spu_decoder_t *spucmml_class_open_plugin (spu_decoder_class_t *class_gen, xine_stream_t *stream) { - - spucmml_class_t *class = (spucmml_class_t *)class_gen; - spucmml_decoder_t *this ; - - this = (spucmml_decoder_t *) xine_xmalloc (sizeof (spucmml_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->spu_decoder.dispose = spudec_dispose; - - this->class = class; - this->stream = stream; - - this->event_queue = xine_event_new_queue (this->stream); - xine_event_create_listener_thread (this->event_queue, - video_frame_format_change_callback, - this); - - this->font_size = 24; - this->subtitle_size = 1; - - this->font = class->xine->config->register_string(class->xine->config, - "subtitles.separate.font", - "sans", - _("font for external subtitles"), - NULL, 0, update_osd_font, this); - - this->vertical_offset = class->xine->config->register_num(class->xine->config, - "subtitles.separate.vertical_offset", - 0, - _("subtitle vertical offset (relative window size)"), - NULL, 0, update_vertical_offset, this); - - this->current_anchor.href = NULL; - - lprintf ("video_out is at %p\n", this->stream->video_out); - - return (spu_decoder_t *) this; -} - -static void update_src_encoding(void *this_gen, xine_cfg_entry_t *entry) -{ - spucmml_class_t *this = (spucmml_class_t *)this_gen; - - this->src_encoding = entry->str_value; - printf("libspucmml: spu_src_encoding = %s\n", this->src_encoding ); -} - -static void *init_spu_decoder_plugin (xine_t *xine, void *data) { - - spucmml_class_t *this ; - - this = (spucmml_class_t *) xine_xmalloc (sizeof (spucmml_class_t)); - - this->class.open_plugin = spucmml_class_open_plugin; - this->class.identifier = "spucmml"; - this->class.description = N_("CMML subtitle decoder plugin"); - this->class.dispose = default_spu_decoder_class_dispose; - - this->xine = xine; - - this->src_encoding = xine->config->register_string(xine->config, - "subtitles.separate.src_encoding", - "iso-8859-1", - _("encoding of subtitles"), - NULL, 10, update_src_encoding, this); - - return &this->class; -} - - -/* plugin catalog information */ -static uint32_t supported_types[] = { BUF_SPU_CMML, 0 }; - -static const decoder_info_t spudec_info = { - supported_types, /* supported types */ - 1 /* priority */ -}; - -const plugin_info_t xine_plugin_info[] EXPORTED = { - /* type, API, "name", version, special_info, init_function */ - { PLUGIN_SPU_DECODER, 17, "spucmml", XINE_VERSION_CODE, &spudec_info, &init_spu_decoder_plugin }, - { PLUGIN_NONE, 0, "", 0, NULL, NULL } -}; - diff --git a/src/libspudec/Makefile.am b/src/libspudec/Makefile.am deleted file mode 100644 index 337428652..000000000 --- a/src/libspudec/Makefile.am +++ /dev/null @@ -1,21 +0,0 @@ -include $(top_srcdir)/misc/Makefile.common - -AM_CFLAGS = $(DEFAULT_OCFLAGS) $(VISIBILITY_FLAG) -AM_CPPFLAGS = -I$(top_srcdir)/src/input/libdvdnav -AM_LDFLAGS = $(xineplug_ldflags) - -noinst_HEADERS = spudec.h - -xineplug_LTLIBRARIES = xineplug_decode_spu.la - -if WITH_EXTERNAL_DVDNAV -external_dvdnav_libs = $(DVDNAV_LIBS) -internal_dvdnav_sources = -else -external_dvdnav_libs = -internal_dvdnav_sources = nav_read.c -endif - -xineplug_decode_spu_la_SOURCES = $(internal_dvdnav_sources) spudec.c xine_spu_decoder.c -xineplug_decode_spu_la_LIBADD = $(XINE_LIB) $(external_dvdnav_libs) $(PTHREAD_LIBS) -xineplug_decode_spu_la_CFLAGS = $(AM_CFLAGS) $(DVDNAV_CFLAGS) diff --git a/src/libspudec/nav_read.c b/src/libspudec/nav_read.c deleted file mode 100644 index 5244bfdd6..000000000 --- a/src/libspudec/nav_read.c +++ /dev/null @@ -1 +0,0 @@ -#include "../input/libdvdnav/nav_read.c" diff --git a/src/libspudec/spudec.c b/src/libspudec/spudec.c deleted file mode 100644 index 13136a53f..000000000 --- a/src/libspudec/spudec.c +++ /dev/null @@ -1,1026 +0,0 @@ -/* - * Copyright (C) 2002-2004 the xine project - * - * Copyright (C) James Courtier-Dutton James@superbug.demon.co.uk - July 2001 - * - * spu.c - converts DVD subtitles to an XPM image - * - * Mostly based on hard work by: - * - * Copyright (C) 2000 Samuel Hocevar - * and Michel Lespinasse - * - * Lots of rearranging by: - * Aaron Holtzman - * Thomas Mirlacher - * implemented reassembling - * cleaner implementation of SPU are saving - * overlaying (proof of concept for now) - * ... and yes, it works now with oms - * added tranparency (provided by the SPU hdr) - * changed structures for easy porting to MGAs DVD mode - * This file is part of xine - * This file was originally part of the OMS program. - * - * This program 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, or (at your option) - * any later version. - * - * This program 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; see the file COPYING. If not, write to - * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include "xine-engine/bswap.h" -#ifdef HAVE_DVDNAV -# include -# include -#else -# include "nav_read.h" -# include "nav_print.h" -#endif - -#include "spudec.h" - -/* -#define LOG_DEBUG 1 -#define LOG_BUTTON 1 -#define LOG_NAV 1 -*/ - -static void spudec_do_commands (xine_t *xine, spudec_state_t *state, spudec_seq_t* seq, vo_overlay_t *ovl); -static void spudec_draw_picture (xine_t *xine, spudec_state_t *state, spudec_seq_t* seq, vo_overlay_t *ovl); -static void spudec_discover_clut (xine_t *xine, spudec_state_t *state, vo_overlay_t *ovl); -#ifdef LOG_DEBUG -static void spudec_print_overlay( vo_overlay_t *overlay ); -#endif - -void spudec_decode_nav(spudec_decoder_t *this, buf_element_t *buf) { - uint8_t *p; - uint32_t packet_len; - uint32_t stream_id; - uint32_t header_len; - pci_t pci; - dsi_t dsi; - video_overlay_manager_t *ovl_manager = this->stream->video_out->get_overlay_manager (this->stream->video_out); - - p = buf->content; - if (p[0] || p[1] || (p[2] != 1)) { - xprintf(this->stream->xine, XINE_VERBOSITY_DEBUG, - "libspudec:spudec_decode_nav:nav demux error! %02x %02x %02x (should be 0x000001) \n",p[0],p[1],p[2]); - return; - } - - packet_len = p[4] << 8 | p[5]; - stream_id = p[3]; - - header_len = 6; - p += header_len; - - if (stream_id == 0xbf) { /* Private stream 2 */ -/* int i; - * for(i=0;i<80;i++) { - * printf("%02x ",p[i]); - * } - * printf("\n p[0]=0x%02x\n",p[0]); - */ - if(p[0] == 0x00) { -#ifdef LOG_NAV - printf("libspudec:nav_PCI\n"); -#endif - navRead_PCI(&pci, p+1); -#ifdef LOG_NAV - printf("libspudec:nav:hli_ss=%u, hli_s_ptm=%u, hli_e_ptm=%u, btn_sl_e_ptm=%u pts=%lli\n", - pci.hli.hl_gi.hli_ss, - pci.hli.hl_gi.hli_s_ptm, - pci.hli.hl_gi.hli_e_ptm, - pci.hli.hl_gi.btn_se_e_ptm, - buf->pts); - printf("libspudec:nav:btn_sn/ofn=%u, btn_ns=%u, fosl_btnn=%u, foac_btnn=%u\n", - pci.hli.hl_gi.btn_ofn, pci.hli.hl_gi.btn_ns, - pci.hli.hl_gi.fosl_btnn, pci.hli.hl_gi.foac_btnn); - printf("btngr_ns %d\n", pci.hli.hl_gi.btngr_ns); - printf("btngr%d_dsp_ty 0x%02x\n", 1, pci.hli.hl_gi.btngr1_dsp_ty); - printf("btngr%d_dsp_ty 0x%02x\n", 2, pci.hli.hl_gi.btngr2_dsp_ty); - printf("btngr%d_dsp_ty 0x%02x\n", 3, pci.hli.hl_gi.btngr3_dsp_ty); - //navPrint_PCI(&pci); - //navPrint_PCI_GI(&pci.pci_gi); - //navPrint_NSML_AGLI(&pci.nsml_agli); - //navPrint_HLI(&pci.hli); - //navPrint_HL_GI(&pci.hli.hl_gi, & btngr_ns, & btn_ns); -#endif - } - - p += packet_len; - - /* We should now have a DSI packet. */ - /* We don't need anything from the DSI packet here. */ - if(p[6] == 0x01) { - packet_len = p[4] << 8 | p[5]; - p += 6; -#ifdef LOG_NAV - printf("NAV DSI packet\n"); -#endif - navRead_DSI(&dsi, p+1); - -// self->vobu_start = self->dsi.dsi_gi.nv_pck_lbn; -// self->vobu_length = self->dsi.dsi_gi.vobu_ea; - } - } - - /* NAV packets contain start and end presentation timestamps, which tell the - * application, when the highlight information in the NAV is supposed to be valid. - * We handle these timestamps only in a very stripped-down way: We keep a list - * of NAV packets (or better: the PCI part of them), tagged with a VPTS timestamp - * telling, when the NAV should be processed. However, we only enqueue a new node - * into this list, when we receive new highlight information during an already - * showing menu. This happens very rarerly on common DVDs, so it is of low impact. - * And we only check for processing of queued entries at some prominent - * locations in this SPU decoder. Since presentation timestamps rarely solve a real - * purpose on most DVDs, this is ok compared to the full-blown solution, which would - * require a separate thread managing the queue all the time. */ - pthread_mutex_lock(&this->nav_pci_lock); - switch (pci.hli.hl_gi.hli_ss) { - case 0: - /* No Highlight information for this VOBU */ - if ( this->pci_cur.pci.hli.hl_gi.hli_ss == 1) { - /* Hide menu spu between menus */ -#ifdef LOG_BUTTON - printf("libspudec:nav:SHOULD HIDE SPU here\n"); -#endif - if( this->menu_handle < 0 ) { - this->menu_handle = ovl_manager->get_handle(ovl_manager,1); - } - if( this->menu_handle >= 0 ) { - this->event.object.handle = this->menu_handle; - this->event.event_type = OVERLAY_EVENT_HIDE; - /* hide menu right now */ - this->event.vpts = 0; - ovl_manager->add_event(ovl_manager, (void *)&this->event); - } else { - xprintf(this->stream->xine, XINE_VERBOSITY_DEBUG, "libspudec: No video_overlay handles left for menu\n"); - } - } - spudec_clear_nav_list(this); - xine_fast_memcpy(&this->pci_cur.pci, &pci, sizeof(pci_t)); - /* incoming SPUs will be plain subtitles */ - this->event.object.object_type = 0; - if (this->button_filter) { - /* we possibly had buttons before, so we update the UI info */ - xine_event_t event; - xine_ui_data_t data; - - event.type = XINE_EVENT_UI_NUM_BUTTONS; - event.data = &data; - event.data_length = sizeof(data); - data.num_buttons = 0; - - xine_event_send(this->stream, &event); - } - this->button_filter=0; - - break; - case 1: - /* All New Highlight information for this VOBU */ - if (this->pci_cur.pci.hli.hl_gi.hli_ss != 0 && - pci.hli.hl_gi.hli_s_ptm > this->pci_cur.pci.hli.hl_gi.hli_s_ptm) { - pci_node_t *node = &this->pci_cur; -#ifdef LOG_DEBUG - printf("libspudec: allocating new PCI node for hli_s_ptm %d\n", pci.hli.hl_gi.hli_s_ptm); -#endif - /* append PCI at the end of the list */ - while (node->next) node = node->next; - node->next = (pci_node_t *)xine_xmalloc(sizeof(pci_node_t)); - node->next->vpts = this->stream->metronom->got_spu_packet(this->stream->metronom, pci.hli.hl_gi.hli_s_ptm); - node->next->next = NULL; - xine_fast_memcpy(&node->next->pci, &pci, sizeof(pci_t)); - } else { - spudec_clear_nav_list(this); - /* menu ahead, remember PCI for later use */ - xine_fast_memcpy(&this->pci_cur.pci, &pci, sizeof(pci_t)); - spudec_process_nav(this); - } - break; - case 2: - /* Use Highlight information from previous VOBU */ - if (this->pci_cur.next) { - /* apply changes to last enqueued NAV */ - pci_node_t *node = this->pci_cur.next; - while (node->next) node = node->next; - node->pci.pci_gi.vobu_s_ptm = pci.pci_gi.vobu_s_ptm; - node->pci.pci_gi.vobu_e_ptm = pci.pci_gi.vobu_e_ptm; - node->pci.pci_gi.vobu_se_e_ptm = pci.pci_gi.vobu_se_e_ptm; - spudec_update_nav(this); - } else { - this->pci_cur.pci.pci_gi.vobu_s_ptm = pci.pci_gi.vobu_s_ptm; - this->pci_cur.pci.pci_gi.vobu_e_ptm = pci.pci_gi.vobu_e_ptm; - this->pci_cur.pci.pci_gi.vobu_se_e_ptm = pci.pci_gi.vobu_se_e_ptm; - } - break; - case 3: - /* Use Highlight information from previous VOBU except commands, which come from this VOBU */ - if (this->pci_cur.next) { - /* apply changes to last enqueued NAV */ - pci_node_t *node = this->pci_cur.next; - while (node->next) node = node->next; - node->pci.pci_gi.vobu_s_ptm = pci.pci_gi.vobu_s_ptm; - node->pci.pci_gi.vobu_e_ptm = pci.pci_gi.vobu_e_ptm; - node->pci.pci_gi.vobu_se_e_ptm = pci.pci_gi.vobu_se_e_ptm; - /* FIXME: Add command copying here */ - spudec_update_nav(this); - } else { - this->pci_cur.pci.pci_gi.vobu_s_ptm = pci.pci_gi.vobu_s_ptm; - this->pci_cur.pci.pci_gi.vobu_e_ptm = pci.pci_gi.vobu_e_ptm; - this->pci_cur.pci.pci_gi.vobu_se_e_ptm = pci.pci_gi.vobu_se_e_ptm; - /* FIXME: Add command copying here */ - } - break; - default: - xprintf(this->stream->xine, XINE_VERBOSITY_DEBUG, - "libspudec: unknown pci.hli.hl_gi.hli_ss = %d\n", pci.hli.hl_gi.hli_ss ); - break; - } - pthread_mutex_unlock(&this->nav_pci_lock); - return; -} - -void spudec_clear_nav_list(spudec_decoder_t *this) -{ - while (this->pci_cur.next) { - pci_node_t *node = this->pci_cur.next->next; - free(this->pci_cur.next); - this->pci_cur.next = node; - } - /* invalidate current timestamp */ - this->pci_cur.pci.hli.hl_gi.hli_s_ptm = (uint32_t)-1; -} - -void spudec_update_nav(spudec_decoder_t *this) -{ - metronom_clock_t *clock = this->stream->xine->clock; - - if (this->pci_cur.next && this->pci_cur.next->vpts <= clock->get_current_time(clock)) { - pci_node_t *node = this->pci_cur.next; - xine_fast_memcpy(&this->pci_cur, this->pci_cur.next, sizeof(pci_node_t)); - spudec_process_nav(this); - free(node); - } -} - -void spudec_process_nav(spudec_decoder_t *this) -{ - /* incoming SPUs will be menus */ - this->event.object.object_type = 1; - if (!this->button_filter) { - /* we possibly entered a menu, so we update the UI button info */ - xine_event_t event; - xine_ui_data_t data; - - event.type = XINE_EVENT_UI_NUM_BUTTONS; - event.data = &data; - event.data_length = sizeof(data); - data.num_buttons = this->pci_cur.pci.hli.hl_gi.btn_ns; - - xine_event_send(this->stream, &event); - } - this->button_filter=1; -} - -void spudec_reassembly (xine_t *xine, spudec_seq_t *seq, uint8_t *pkt_data, u_int pkt_len) -{ -#ifdef LOG_DEBUG - printf ("libspudec: seq->complete = %d\n", seq->complete); - printf("libspudec:1: seq->ra_offs = %d, seq->seq_len = %d, seq->buf_len = %d, seq->buf=%p\n", - seq->ra_offs, - seq->seq_len, - seq->buf_len, - seq->buf); -#endif - if (seq->complete) { - seq->seq_len = (((uint32_t)pkt_data[0])<<8) | pkt_data[1]; - seq->cmd_offs = (((uint32_t)pkt_data[2])<<8) | pkt_data[3]; - if (seq->cmd_offs >= seq->seq_len) { - xprintf(xine, XINE_VERBOSITY_DEBUG, "libspudec:faulty stream\n"); - seq->broken = 1; - } - if (seq->buf_len < seq->seq_len) { - seq->buf_len = seq->seq_len; -#ifdef LOG_DEBUG - printf ("spu: MALLOC1: seq->buf %p, len=%d\n", seq->buf,seq->buf_len); -#endif - if (seq->buf) { - free(seq->buf); - seq->buf = NULL; - } - seq->buf = malloc(seq->buf_len); -#ifdef LOG_DEBUG - printf ("spu: MALLOC2: seq->buf %p, len=%d\n", seq->buf,seq->buf_len); -#endif - - } - seq->ra_offs = 0; - -#ifdef LOG_DEBUG - printf ("spu: buf_len: %d\n", seq->buf_len); - printf ("spu: cmd_off: %d\n", seq->cmd_offs); -#endif - } - -#ifdef LOG_DEBUG - printf("libspudec:2: seq->ra_offs = %d, seq->seq_len = %d, seq->buf_len = %d, seq->buf=%p\n", - seq->ra_offs, - seq->seq_len, - seq->buf_len, - seq->buf); -#endif - if (seq->ra_offs < seq->seq_len) { - if (seq->ra_offs + pkt_len > seq->seq_len) - pkt_len = seq->seq_len - seq->ra_offs; - memcpy (seq->buf + seq->ra_offs, pkt_data, pkt_len); - seq->ra_offs += pkt_len; - } else { - xprintf(xine, XINE_VERBOSITY_DEBUG, "libspudec:faulty stream\n"); - seq->broken = 1; - } - - if (seq->ra_offs == seq->seq_len) { - seq->finished = 0; - seq->complete = 1; - return; /* sequence ready */ - } - seq->complete = 0; - return; -} - -void spudec_process (spudec_decoder_t *this, int stream_id) { - spudec_seq_t *cur_seq; - video_overlay_manager_t *ovl_manager = this->stream->video_out->get_overlay_manager (this->stream->video_out); - int pending = 1; - cur_seq = &this->spudec_stream_state[stream_id].ra_seq; - -#ifdef LOG_DEBUG - printf ("spu: Found SPU from stream %d pts=%lli vpts=%lli\n",stream_id, - this->spudec_stream_state[stream_id].pts, - this->spudec_stream_state[stream_id].vpts); -#endif - this->state.cmd_ptr = cur_seq->buf + cur_seq->cmd_offs; - this->state.modified = 1; /* Only draw picture if = 1 on first event of SPU */ - this->state.visible = OVERLAY_EVENT_SHOW; - this->state.forced_display = 0; /* 0 - No value, 1 - Forced Display. */ - this->state.delay = 0; - cur_seq->finished=0; - - do { - if (!(cur_seq->finished) ) { - pci_node_t *node; - - /* spu_channel is now set based on whether we are in the menu or not. */ - /* Bit 7 is set if only forced display SPUs should be shown */ - if ( (this->stream->spu_channel & 0x1f) != stream_id ) { -#ifdef LOG_DEBUG - printf ("spu: Dropping SPU channel %d. Not selected stream_id\n", stream_id); -#endif - return; - } - /* parse SPU command sequence, this will update forced_display, so it must come - * before the check for it */ - spudec_do_commands(this->stream->xine, &this->state, cur_seq, &this->overlay); - /* FIXME: Check for Forced-display or subtitle stream - * For subtitles, open event. - * For menus, store it for later. - */ - if (cur_seq->broken) { - xprintf(this->stream->xine, XINE_VERBOSITY_DEBUG, "libspudec: dropping broken SPU\n"); - cur_seq->broken = 0; - return; - } - if ( (this->state.forced_display == 0) && (this->stream->spu_channel & 0x80) ) { -#ifdef LOG_DEBUG - printf ("spu: Dropping SPU channel %d. Only allow forced display SPUs\n", stream_id); -#endif - return; - } - -#ifdef LOG_DEBUG - spudec_print_overlay( &this->overlay ); - printf ("spu: forced display:%s\n", this->state.forced_display ? "Yes" : "No" ); -#endif - pthread_mutex_lock(&this->nav_pci_lock); - /* search for a PCI that matches this SPU's PTS */ - for (node = &this->pci_cur; node; node = node->next) - if (node->pci.hli.hl_gi.hli_s_ptm == this->spudec_stream_state[stream_id].pts) - break; - if (node) { - if (this->state.visible == OVERLAY_EVENT_HIDE) { - /* menus are hidden via nav packet decoding, not here */ - /* FIXME: James is not sure about this solution and may want to look this over. - * I'm commiting it, because I haven't found a disc it breaks, but it fixes - * some instead. Michael Roitzsch */ - pthread_mutex_unlock(&this->nav_pci_lock); - continue; - } - if (node->pci.hli.hl_gi.fosl_btnn > 0) { - xine_event_t event; - - this->buttonN = node->pci.hli.hl_gi.fosl_btnn; - event.type = XINE_EVENT_INPUT_BUTTON_FORCE; - event.stream = this->stream; - event.data = &this->buttonN; - event.data_length = sizeof(this->buttonN); - xine_event_send(this->stream, &event); - } -#ifdef LOG_BUTTON - fprintf(stderr, "libspudec:Full Overlay\n"); -#endif - if (!spudec_copy_nav_to_overlay(this->stream->xine, - &node->pci, this->state.clut, - this->buttonN, 0, &this->overlay, &this->overlay)) { - /* current button does not exist -> use another one */ - xine_event_t event; - - if (this->buttonN > node->pci.hli.hl_gi.btn_ns) - this->buttonN = node->pci.hli.hl_gi.btn_ns; - else - this->buttonN = 1; - event.type = XINE_EVENT_INPUT_BUTTON_FORCE; - event.stream = this->stream; - event.data = &this->buttonN; - event.data_length = sizeof(this->buttonN); - xine_event_send(this->stream, &event); - spudec_copy_nav_to_overlay(this->stream->xine, - &node->pci, this->state.clut, - this->buttonN, 0, &this->overlay, &this->overlay); - } - } else { - /* Subtitle and not a menu button */ - int i; - for (i = 0;i < 4; i++) { - this->overlay.hili_color[i] = this->overlay.color[i]; - this->overlay.hili_trans[i] = this->overlay.trans[i]; - } - } - pthread_mutex_unlock(&this->nav_pci_lock); - - if ((this->state.modified) ) { - spudec_draw_picture(this->stream->xine, &this->state, cur_seq, &this->overlay); - } - - if (this->state.need_clut) { - spudec_discover_clut(this->stream->xine, &this->state, &this->overlay); - } - - if (this->state.vobsub) { - int width, height; - int64_t duration; - - /* - * vobsubs are usually played with a scaled-down stream (not full DVD - * resolution), therefore we should try to realign it. - */ - - this->stream->video_out->status(this->stream->video_out, NULL, - &width, &height, &duration ); - - this->overlay.x = (width - this->overlay.width) / 2; - this->overlay.y = height - this->overlay.height; - } - - /* Subtitle */ - if( this->menu_handle < 0 ) { - this->menu_handle = ovl_manager->get_handle(ovl_manager,1); - } - - if( this->menu_handle < 0 ) { - xprintf(this->stream->xine, XINE_VERBOSITY_DEBUG, - "libspudec: No video_overlay handles left for menu\n"); - return; - } - this->event.object.handle = this->menu_handle; - this->event.object.pts = this->spudec_stream_state[stream_id].pts; - - xine_fast_memcpy(this->event.object.overlay, - &this->overlay, - sizeof(vo_overlay_t)); - this->overlay.rle=NULL; - /* For force display menus */ - //if ( !(this->state.visible) ) { - // this->state.visible = OVERLAY_EVENT_SHOW; - //} - - this->event.event_type = this->state.visible; - /* - printf("spu event %d handle: %d vpts: %lli\n", this->event.event_type, - this->event.object.handle, this->event.vpts ); - */ - - this->event.vpts = this->spudec_stream_state[stream_id].vpts+(this->state.delay*1000); - - /* Keep all the events in the correct order. */ - /* This corrects for errors during estimation around discontinuity */ - if( this->event.vpts < this->last_event_vpts ) { - this->event.vpts = this->last_event_vpts + 1; - } - this->last_event_vpts = this->event.vpts; - -#ifdef LOG_BUTTON - fprintf(stderr, "libspudec: add_event type=%d : current time=%lld, spu vpts=%lli\n", - this->event.event_type, - this->stream->xine->clock->get_current_time(this->stream->xine->clock), - this->event.vpts); -#endif - ovl_manager->add_event(ovl_manager, (void *)&this->event); - } else { - pending = 0; - } - } while (pending); - -} - -#define CMD_SPU_FORCE_DISPLAY 0x00 -#define CMD_SPU_SHOW 0x01 -#define CMD_SPU_HIDE 0x02 -#define CMD_SPU_SET_PALETTE 0x03 -#define CMD_SPU_SET_ALPHA 0x04 -#define CMD_SPU_SET_SIZE 0x05 -#define CMD_SPU_SET_PXD_OFFSET 0x06 -#define CMD_SPU_WIPE 0x07 /* Not currently implemented */ -#define CMD_SPU_EOF 0xff - -static void spudec_do_commands(xine_t *xine, spudec_state_t *state, spudec_seq_t* seq, vo_overlay_t *ovl) -{ - uint8_t *buf = state->cmd_ptr; - uint8_t *next_seq; - int32_t param_length; - -#ifdef LOG_DEBUG - printf ("spu: SPU DO COMMANDS\n"); -#endif - - state->delay = (buf[0] << 8) + buf[1]; -#ifdef LOG_DEBUG - printf ("spu: \tdelay=%d\n",state->delay); -#endif - next_seq = seq->buf + (buf[2] << 8) + buf[3]; - buf += 4; -#ifdef LOG_DEBUG - printf ("spu: \tnext_seq=%d\n",next_seq - seq->buf); -#endif - -/* if next equals current, this is the last one - */ - if (state->cmd_ptr >= next_seq) - next_seq = seq->buf + seq->seq_len; /* allow to run until end */ - - state->cmd_ptr = next_seq; - - while (buf < next_seq && *buf != CMD_SPU_EOF) { - switch (*buf) { - case CMD_SPU_SHOW: /* show subpicture */ -#ifdef LOG_DEBUG - printf ("spu: \tshow subpicture\n"); -#endif - state->visible = OVERLAY_EVENT_SHOW; - buf++; - break; - - case CMD_SPU_HIDE: /* hide subpicture */ -#ifdef LOG_DEBUG - printf ("spu: \thide subpicture\n"); -#endif - state->visible = OVERLAY_EVENT_HIDE; - buf++; - break; - - case CMD_SPU_SET_PALETTE: { /* CLUT */ - spudec_clut_t *clut = (spudec_clut_t *) (buf+1); - - state->cur_colors[3] = clut->entry0; - state->cur_colors[2] = clut->entry1; - state->cur_colors[1] = clut->entry2; - state->cur_colors[0] = clut->entry3; - -/* This is a bit out of context for now */ - ovl->color[3] = state->clut[clut->entry0]; - ovl->color[2] = state->clut[clut->entry1]; - ovl->color[1] = state->clut[clut->entry2]; - ovl->color[0] = state->clut[clut->entry3]; - -#ifdef LOG_DEBUG - printf ("spu: \tclut [%x %x %x %x]\n", - ovl->color[0], ovl->color[1], ovl->color[2], ovl->color[3]); - printf ("spu: \tclut base [%x %x %x %x]\n", - clut->entry0, clut->entry1, clut->entry2, clut->entry3); -#endif - state->modified = 1; - buf += 3; - break; - } - case CMD_SPU_SET_ALPHA: { /* transparency palette */ - spudec_clut_t *trans = (spudec_clut_t *) (buf+1); -/* This should go into state for now */ - - ovl->trans[3] = trans->entry0; - ovl->trans[2] = trans->entry1; - ovl->trans[1] = trans->entry2; - ovl->trans[0] = trans->entry3; - -#ifdef LOG_DEBUG - printf ("spu: \ttrans [%d %d %d %d]\n", - ovl->trans[0], ovl->trans[1], ovl->trans[2], ovl->trans[3]); -#endif - state->modified = 1; - buf += 3; - break; - } - - case CMD_SPU_SET_SIZE: /* image coordinates */ -/* state->o_left = (buf[1] << 4) | (buf[2] >> 4); - state->o_right = (((buf[2] & 0x0f) << 8) | buf[3]); - - state->o_top = (buf[4] << 4) | (buf[5] >> 4); - state->o_bottom = (((buf[5] & 0x0f) << 8) | buf[6]); - */ - ovl->x = (buf[1] << 4) | (buf[2] >> 4); - ovl->y = (buf[4] << 4) | (buf[5] >> 4); - ovl->width = (((buf[2] & 0x0f) << 8) | buf[3]) - ovl->x + 1; - ovl->height = (((buf[5] & 0x0f) << 8) | buf[6]) - ovl->y + 1; - ovl->hili_top = -1; - ovl->hili_bottom = -1; - ovl->hili_left = -1; - ovl->hili_right = -1; - -#ifdef LOG_DEBUG - printf ("spu: \tx = %d y = %d width = %d height = %d\n", - ovl->x, ovl->y, ovl->width, ovl->height ); -#endif - state->modified = 1; - buf += 7; - break; - - case CMD_SPU_SET_PXD_OFFSET: /* image top[0] field / image bottom[1] field*/ - state->field_offs[0] = (((u_int)buf[1]) << 8) | buf[2]; - state->field_offs[1] = (((u_int)buf[3]) << 8) | buf[4]; - -#ifdef LOG_DEBUG - printf ("spu: \toffset[0] = %d offset[1] = %d\n", - state->field_offs[0], state->field_offs[1]); -#endif - - if ((state->field_offs[0] >= seq->seq_len) || - (state->field_offs[1] >= seq->seq_len)) { - xprintf(xine, XINE_VERBOSITY_DEBUG, "libspudec:faulty stream\n"); - seq->broken = 1; - } - state->modified = 1; - buf += 5; - break; - - case CMD_SPU_WIPE: -#ifdef LOG_DEBUG - printf ("libspudec: \tSPU_WIPE not implemented yet\n"); -#endif - param_length = (buf[1] << 8) | (buf[2]); - buf += 1 + param_length; - break; - - case CMD_SPU_FORCE_DISPLAY: -#ifdef LOG_DEBUG - printf ("libspudec: \tForce Display/Menu\n"); -#endif - state->forced_display = 1; - buf++; - break; - - default: - xprintf(xine, XINE_VERBOSITY_DEBUG, "libspudec: unknown seqence command (%02x)\n", buf[0]); - /* FIXME: SPU should be dropped, and buffers resynced */ - buf = next_seq; - seq->broken = 1; - break; - } - } - - if (next_seq >= seq->buf + seq->seq_len) - seq->finished = 1; /* last sub-sequence */ -} - -/* FIXME: Get rid of all these static values */ -static uint8_t *bit_ptr[2]; -static int field; // which field we are currently decoding -static int put_x, put_y; - -static u_int get_bits (u_int bits) -{ - static u_int data; - static u_int bits_left; - u_int ret = 0; - - if (!bits) { /* for realignment to next byte */ - bits_left = 0; - } - - while (bits) { - if (bits > bits_left) { - ret |= data << (bits - bits_left); - bits -= bits_left; - - data = *bit_ptr[field]++; - bits_left = 8; - } else { - bits_left -= bits; - ret |= data >> (bits_left); - data &= (1 << bits_left) - 1; - bits = 0; - } - } - - return ret; -} - -static int spudec_next_line (vo_overlay_t *spu) -{ - get_bits (0); // byte align rle data - - put_x = 0; - put_y++; - field ^= 1; // Toggle fields - - if (put_y >= spu->height) { -#ifdef LOG_DEBUG - printf ("spu: put_y >= spu->height\n"); -#endif - return -1; - } - return 0; -} - -static void spudec_draw_picture (xine_t *xine, spudec_state_t *state, spudec_seq_t* seq, vo_overlay_t *ovl) -{ - rle_elem_t *rle; - field = 0; - bit_ptr[0] = seq->buf + state->field_offs[0]; - bit_ptr[1] = seq->buf + state->field_offs[1]; - put_x = put_y = 0; - get_bits (0); /* Reset/init bit code */ - -/* ovl->x = state->o_left; - * ovl->y = state->o_top; - * ovl->width = state->o_right - state->o_left + 1; - * ovl->height = state->o_bottom - state->o_top + 1; - - * ovl->hili_top = 0; - * ovl->hili_bottom = ovl->height - 1; - * ovl->hili_left = 0; - * ovl->hili_right = ovl->width - 1; - */ - - /* allocate for the worst case: - * - both fields running to the very end - * - 2 RLE elements per byte meaning single pixel RLE - */ - ovl->data_size = ((seq->cmd_offs - state->field_offs[0]) + - (seq->cmd_offs - state->field_offs[1])) * 2 * sizeof(rle_elem_t); - - if (ovl->rle) { - xprintf (xine, XINE_VERBOSITY_DEBUG, - "libspudec: spudec_draw_picture: ovl->rle is not empty!!!! It should be!!! " - "You should never see this message.\n"); - free(ovl->rle); - ovl->rle=NULL; - } - ovl->rle = malloc(ovl->data_size); - - state->modified = 0; /* mark as already processed */ - rle = ovl->rle; -#ifdef LOG_DEBUG - printf ("libspudec: Draw RLE=%p\n",rle); -#endif - - while (bit_ptr[1] < seq->buf + seq->cmd_offs) { - u_int len; - u_int vlc; - - vlc = get_bits (4); - if (vlc < 0x0004) { - vlc = (vlc << 4) | get_bits (4); - if (vlc < 0x0010) { - vlc = (vlc << 4) | get_bits (4); - if (vlc < 0x0040) { - vlc = (vlc << 4) | get_bits (4); - } - } - } - - len = vlc >> 2; - - /* if len == 0 -> end sequence - fill to end of line */ - if (len == 0) - len = ovl->width - put_x; - - rle->len = len; - rle->color = vlc & 0x03; - rle++; - put_x += len; - - if (put_x >= ovl->width) { - if (spudec_next_line (ovl) < 0) - break; - } - } - - ovl->num_rle = rle - ovl->rle; - ovl->rgb_clut = 0; - ovl->unscaled = 0; -#ifdef LOG_DEBUG - printf ("spu: Num RLE=%d\n",ovl->num_rle); - printf ("spu: Date size=%d\n",ovl->data_size); - printf ("spu: sizeof RLE=%d\n",sizeof(rle_elem_t)); -#endif -} - -/* Heuristic to discover the colors used by the subtitles - and assign a "readable" pallete to them. - Currently looks for sequence of border-fg-border or - border1-border2-fg-border2-border1. - MINFOUND is the number of ocurrences threshold. -*/ -#define MINFOUND 20 -static void spudec_discover_clut(xine_t *xine, spudec_state_t *state, vo_overlay_t *ovl) -{ - int bg,c; - int seqcolor[10]; - int n,i; - rle_elem_t *rle; - - int found[2][16]; - - static clut_t text_clut[] = { - CLUT_Y_CR_CB_INIT(0x80, 0x90, 0x80), - CLUT_Y_CR_CB_INIT(0x00, 0x90, 0x00), - CLUT_Y_CR_CB_INIT(0xff, 0x90, 0x00) - }; - - memset(found,0,sizeof(found)); - rle = ovl->rle; - - /* this seems to be a problem somewhere else, - why rle is null? */ - if( !rle ) - return; - - /* suppose the first and last pixels are bg */ - if( rle[0].color != rle[ovl->num_rle-1].color ) - return; - - bg = rle[0].color; - - i = 0; - for( n = 0; n < ovl->num_rle; n++ ) - { - c = rle[n].color; - - if( c == bg ) - { - if( i == 3 && seqcolor[1] == seqcolor[3] ) - { - found[0][seqcolor[2]]++; - if( found[0][seqcolor[2]] > MINFOUND ) - { - memcpy(&state->clut[state->cur_colors[seqcolor[1]]], &text_clut[1], - sizeof(clut_t)); - memcpy(&state->clut[state->cur_colors[seqcolor[2]]], &text_clut[2], - sizeof(clut_t)); - ovl->color[seqcolor[1]] = state->clut[state->cur_colors[seqcolor[1]]]; - ovl->color[seqcolor[2]] = state->clut[state->cur_colors[seqcolor[2]]]; - state->need_clut = 0; - break; - } - } - if( i == 5 && seqcolor[1] == seqcolor[5] - && seqcolor[2] == seqcolor[4] ) - { - found[1][seqcolor[3]]++; - if( found[1][seqcolor[3]] > MINFOUND ) - { - memcpy(&state->clut[state->cur_colors[seqcolor[1]]], &text_clut[0], - sizeof(clut_t)); - memcpy(&state->clut[state->cur_colors[seqcolor[2]]], &text_clut[1], - sizeof(clut_t)); - memcpy(&state->clut[state->cur_colors[seqcolor[3]]], &text_clut[2], - sizeof(clut_t)); - ovl->color[seqcolor[1]] = state->clut[state->cur_colors[seqcolor[1]]]; - ovl->color[seqcolor[2]] = state->clut[state->cur_colors[seqcolor[2]]]; - ovl->color[seqcolor[3]] = state->clut[state->cur_colors[seqcolor[3]]]; - state->need_clut = 0; - break; - } - } - i = 0; - seqcolor[i] = c; - } - else if ( i < 6 ) - { - i++; - seqcolor[i] = c; - } - } -} - -#ifdef LOG_DEBUG -static void spudec_print_overlay( vo_overlay_t *ovl ) { - printf ("spu: OVERLAY to show\n"); - printf ("spu: \tx = %d y = %d width = %d height = %d\n", - ovl->x, ovl->y, ovl->width, ovl->height ); - printf ("spu: \tclut [%x %x %x %x]\n", - ovl->color[0], ovl->color[1], ovl->color[2], ovl->color[3]); - printf ("spu: \ttrans [%d %d %d %d]\n", - ovl->trans[0], ovl->trans[1], ovl->trans[2], ovl->trans[3]); - printf ("spu: \tclip top=%d bottom=%d left=%d right=%d\n", - ovl->hili_top, ovl->hili_bottom, ovl->hili_left, ovl->hili_right); - printf ("spu: \tclip_clut [%x %x %x %x]\n", - ovl->hili_color[0], ovl->hili_color[1], ovl->hili_color[2], ovl->hili_color[3]); - printf ("spu: \thili_trans [%d %d %d %d]\n", - ovl->hili_trans[0], ovl->hili_trans[1], ovl->hili_trans[2], ovl->hili_trans[3]); - return; -} -#endif - -int spudec_copy_nav_to_overlay(xine_t *xine, pci_t* nav_pci, uint32_t* clut, - int32_t button, int32_t mode, vo_overlay_t * overlay, vo_overlay_t * base ) { - btni_t *button_ptr = NULL; - unsigned int btns_per_group; - int i; - - if((button <= 0) || (button > nav_pci->hli.hl_gi.btn_ns)) - return 0; - - btns_per_group = 36 / nav_pci->hli.hl_gi.btngr_ns; - - /* choose button group: we can always use a normal 4:3 or widescreen button group - * as long as xine blends the overlay before scaling the image to its aspect */ - if (!button_ptr && nav_pci->hli.hl_gi.btngr_ns >= 1 && !(nav_pci->hli.hl_gi.btngr1_dsp_ty & 6)) - button_ptr = &nav_pci->hli.btnit[0 * btns_per_group + button - 1]; - if (!button_ptr && nav_pci->hli.hl_gi.btngr_ns >= 2 && !(nav_pci->hli.hl_gi.btngr2_dsp_ty & 6)) - button_ptr = &nav_pci->hli.btnit[1 * btns_per_group + button - 1]; - if (!button_ptr && nav_pci->hli.hl_gi.btngr_ns >= 3 && !(nav_pci->hli.hl_gi.btngr3_dsp_ty & 6)) - button_ptr = &nav_pci->hli.btnit[2 * btns_per_group + button - 1]; - if (!button_ptr) { - xprintf(xine, XINE_VERBOSITY_DEBUG, - "libspudec: No suitable menu button group found, using group 1.\n"); - button_ptr = &nav_pci->hli.btnit[button - 1]; - } - - /* button areas in the nav packet are in screen coordinates, - * overlay clipping areas are in overlay coordinates; - * therefore we must subtract the display coordinates of the underlying overlay */ - overlay->hili_left = (button_ptr->x_start > base->x) ? (button_ptr->x_start - base->x) : 0; - overlay->hili_top = (button_ptr->y_start > base->y) ? (button_ptr->y_start - base->y) : 0; - overlay->hili_right = (button_ptr->x_end > base->x) ? (button_ptr->x_end - base->x) : 0; - overlay->hili_bottom = (button_ptr->y_end > base->y) ? (button_ptr->y_end - base->y) : 0; - if(button_ptr->btn_coln != 0) { -#ifdef LOG_BUTTON - fprintf(stderr, "libspudec: normal button clut\n"); -#endif - for (i = 0;i < 4; i++) { - overlay->hili_color[i] = clut[0xf & (nav_pci->hli.btn_colit.btn_coli[button_ptr->btn_coln-1][mode] >> (16 + 4*i))]; - overlay->hili_trans[i] = 0xf & (nav_pci->hli.btn_colit.btn_coli[button_ptr->btn_coln-1][mode] >> (4*i)); - } - } else { -#ifdef LOG_BUTTON - fprintf(stderr, "libspudec: abnormal button clut\n"); -#endif - for (i = 0;i < 4; i++) { -#ifdef LOG_BUTTON - printf("libspudec:btn_coln = 0, hili_color = color\n"); -#endif - overlay->hili_color[i] = overlay->color[i]; - overlay->hili_trans[i] = overlay->trans[i]; - } - } - - /* spudec_print_overlay( overlay ); */ -#ifdef LOG_BUTTON - printf("libspudec:xine_decoder.c:NAV to SPU pts match!\n"); -#endif - - return 1; -} diff --git a/src/libspudec/spudec.h b/src/libspudec/spudec.h deleted file mode 100644 index 1e7d80596..000000000 --- a/src/libspudec/spudec.h +++ /dev/null @@ -1,142 +0,0 @@ -/* - * Copyright (C) 2000-2004 the xine project - * - * Copyright (C) James Courtier-Dutton James@superbug.demon.co.uk - July 2001 - * - * 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 - * - * This file was originally part of the OMS program. - */ - -#ifndef __SPU_H__ -#define __SPU_H__ - -#ifdef HAVE_CONFIG_H -#include -#endif - -#include -#include -#include -#ifdef HAVE_DVDNAV -# include -#else -# include "nav_types.h" -#endif - -#define NUM_SEQ_BUFFERS 50 -#define MAX_STREAMS 32 - -typedef struct spudec_clut_struct { -#ifdef WORDS_BIGENDIAN - uint8_t entry0 : 4; - uint8_t entry1 : 4; - uint8_t entry2 : 4; - uint8_t entry3 : 4; -#else - uint8_t entry1 : 4; - uint8_t entry0 : 4; - uint8_t entry3 : 4; - uint8_t entry2 : 4; -#endif -} spudec_clut_t; - -typedef struct { - uint8_t *buf; - uint32_t ra_offs; /* reassembly offset */ - uint32_t seq_len; - uint32_t buf_len; - uint32_t cmd_offs; - int64_t pts; /* Base PTS of this sequence */ - int32_t finished; /* Has this control sequence been finished? */ - uint32_t complete; /* Has this reassembly been finished? */ - uint32_t broken; /* this SPU is broken and should be dropped */ -} spudec_seq_t; - -typedef struct { - uint8_t *cmd_ptr; - - uint32_t field_offs[2]; - int32_t b_top, o_top; - int32_t b_bottom, o_bottom; - int32_t b_left, o_left; - int32_t b_right, o_right; - - int32_t modified; /* Was the sub-picture modified? */ - int32_t visible; /* Must the sub-picture be shown? */ - int32_t forced_display; /* This overlay is a menu */ - int32_t delay; /* Delay in 90Khz / 1000 */ - int32_t need_clut; /* doesn't have the right clut yet */ - int32_t cur_colors[4];/* current 4 colors been used */ - int32_t vobsub; /* vobsub must be aligned to bottom */ - - uint32_t clut[16]; -} spudec_state_t; - -typedef struct spudec_stream_state_s { - spudec_seq_t ra_seq; - spudec_state_t state; - int64_t vpts; - int64_t pts; - int32_t overlay_handle; -} spudec_stream_state_t; - -typedef struct { - spu_decoder_class_t decoder_class; -} spudec_class_t; - -typedef struct pci_node_s pci_node_t; -struct pci_node_s { - pci_t pci; - uint64_t vpts; - pci_node_t *next; -}; - -typedef struct spudec_decoder_s { - spu_decoder_t spu_decoder; - - spudec_class_t *class; - xine_stream_t *stream; - spudec_stream_state_t spudec_stream_state[MAX_STREAMS]; - - video_overlay_event_t event; - video_overlay_object_t object; - int32_t menu_handle; - - spudec_state_t state; - - vo_overlay_t overlay; - int ovl_caps; - int output_open; - pthread_mutex_t nav_pci_lock; - pci_node_t pci_cur; - uint32_t buttonN; /* Current button number for highlights */ - int32_t button_filter; /* Allow highlight changes or not */ - int64_t last_event_vpts; -} spudec_decoder_t; - -void spudec_reassembly (xine_t *xine, spudec_seq_t *seq, uint8_t *pkt_data, u_int pkt_len); -void spudec_process( spudec_decoder_t *this, int stream_id); -/* the nav functions must be called with the nav_pci_lock held */ -void spudec_decode_nav( spudec_decoder_t *this, buf_element_t *buf); -void spudec_clear_nav_list(spudec_decoder_t *this); -void spudec_update_nav(spudec_decoder_t *this); -void spudec_process_nav(spudec_decoder_t *this); -int spudec_copy_nav_to_overlay(xine_t *xine, pci_t* nav_pci, uint32_t* clut, int32_t button, int32_t mode, - vo_overlay_t * overlay, vo_overlay_t * base ); - -#endif diff --git a/src/libspudec/xine_spu_decoder.c b/src/libspudec/xine_spu_decoder.c deleted file mode 100644 index e36b39fc8..000000000 --- a/src/libspudec/xine_spu_decoder.c +++ /dev/null @@ -1,381 +0,0 @@ -/* - * Copyright (C) 2000-2004 the xine project - * - * Copyright (C) James Courtier-Dutton James@superbug.demon.co.uk - July 2001 - * - * 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 - * - * stuff needed to turn libspu into a xine decoder plugin - */ - -#include -#include -#include -#include -#include -#include - -#include -#include -#include "xine-engine/bswap.h" -#include -#ifdef HAVE_DVDNAV -# include -# include -#else -# include "nav_read.h" -# include "nav_types.h" -#endif - -#include "spudec.h" - -/* -#define LOG_DEBUG 1 -#define LOG_BUTTON 1 -*/ - -static const clut_t default_clut[] = { - CLUT_Y_CR_CB_INIT(0x00, 0x80, 0x80), - CLUT_Y_CR_CB_INIT(0xbf, 0x80, 0x80), - CLUT_Y_CR_CB_INIT(0x10, 0x80, 0x80), - CLUT_Y_CR_CB_INIT(0x28, 0x6d, 0xef), - CLUT_Y_CR_CB_INIT(0x51, 0xef, 0x5a), - CLUT_Y_CR_CB_INIT(0xbf, 0x80, 0x80), - CLUT_Y_CR_CB_INIT(0x36, 0x80, 0x80), - CLUT_Y_CR_CB_INIT(0x28, 0x6d, 0xef), - CLUT_Y_CR_CB_INIT(0xbf, 0x80, 0x80), - CLUT_Y_CR_CB_INIT(0x51, 0x80, 0x80), - CLUT_Y_CR_CB_INIT(0xbf, 0x80, 0x80), - CLUT_Y_CR_CB_INIT(0x10, 0x80, 0x80), - CLUT_Y_CR_CB_INIT(0x28, 0x6d, 0xef), - CLUT_Y_CR_CB_INIT(0x5c, 0x80, 0x80), - CLUT_Y_CR_CB_INIT(0xbf, 0x80, 0x80), - CLUT_Y_CR_CB_INIT(0x1c, 0x80, 0x80), - CLUT_Y_CR_CB_INIT(0x28, 0x6d, 0xef) -}; - -static void spudec_decode_data (spu_decoder_t *this_gen, buf_element_t *buf) { - uint32_t stream_id; - spudec_seq_t *cur_seq; - spudec_decoder_t *this = (spudec_decoder_t *) this_gen; - stream_id = buf->type & 0x1f ; - cur_seq = &this->spudec_stream_state[stream_id].ra_seq; - -#ifdef LOG_DEBUG - printf("libspudec:got buffer type = %x\n", buf->type); -#endif - - /* check, if we need to process the next PCI from the list */ - pthread_mutex_lock(&this->nav_pci_lock); - spudec_update_nav(this); - pthread_mutex_unlock(&this->nav_pci_lock); - - if ( (buf->type & 0xffff0000) != BUF_SPU_DVD || - !(buf->decoder_flags & BUF_FLAG_SPECIAL) || - buf->decoder_info[1] != BUF_SPECIAL_SPU_DVD_SUBTYPE ) - return; - - if ( buf->decoder_info[2] == SPU_DVD_SUBTYPE_CLUT ) { -#ifdef LOG_DEBUG - printf("libspudec: SPU CLUT\n"); -#endif - if (buf->content[0]) { /* cheap endianess detection */ - xine_fast_memcpy(this->state.clut, buf->content, sizeof(uint32_t)*16); - } else { - int i; - uint32_t *clut = (uint32_t*) buf->content; - for (i = 0; i < 16; i++) - this->state.clut[i] = bswap_32(clut[i]); - } - this->state.need_clut = 0; - return; - } - - if ( buf->decoder_info[2] == SPU_DVD_SUBTYPE_NAV ) { -#ifdef LOG_DEBUG - printf("libspudec:got nav packet 1\n"); -#endif - spudec_decode_nav(this,buf); - return; - } - - if ( buf->decoder_info[2] == SPU_DVD_SUBTYPE_VOBSUB_PACKAGE ) { - this->state.vobsub = 1; - } - -#ifdef LOG_DEBUG - printf("libspudec:got buffer type = %x\n", buf->type); -#endif - if (buf->decoder_flags & BUF_FLAG_PREVIEW) /* skip preview data */ - return; - - if (buf->pts) { - metronom_t *metronom = this->stream->metronom; - int64_t vpts = metronom->got_spu_packet(metronom, buf->pts); - - this->spudec_stream_state[stream_id].vpts = vpts; /* Show timer */ - this->spudec_stream_state[stream_id].pts = buf->pts; /* Required to match up with NAV packets */ - } - - spudec_reassembly(this->stream->xine, - &this->spudec_stream_state[stream_id].ra_seq, buf->content, buf->size); - if(this->spudec_stream_state[stream_id].ra_seq.complete == 1) { - if(this->spudec_stream_state[stream_id].ra_seq.broken) { - xprintf(this->stream->xine, XINE_VERBOSITY_DEBUG, "libspudec: dropping broken SPU\n"); - this->spudec_stream_state[stream_id].ra_seq.broken = 0; - } else - spudec_process(this,stream_id); - } -} - -static void spudec_reset (spu_decoder_t *this_gen) { - spudec_decoder_t *this = (spudec_decoder_t *) this_gen; - video_overlay_manager_t *ovl_manager = this->stream->video_out->get_overlay_manager (this->stream->video_out); - int i; - - if( this->menu_handle >= 0 ) - ovl_manager->free_handle(ovl_manager, - this->menu_handle); - this->menu_handle = -1; - - for (i=0; i < MAX_STREAMS; i++) { - if( this->spudec_stream_state[i].overlay_handle >= 0 ) - ovl_manager->free_handle(ovl_manager, - this->spudec_stream_state[i].overlay_handle); - this->spudec_stream_state[i].overlay_handle = -1; - this->spudec_stream_state[i].ra_seq.complete = 1; - this->spudec_stream_state[i].ra_seq.broken = 0; - } - - pthread_mutex_lock(&this->nav_pci_lock); - spudec_clear_nav_list(this); - pthread_mutex_unlock(&this->nav_pci_lock); -} - -static void spudec_discontinuity (spu_decoder_t *this_gen) { - spudec_decoder_t *this = (spudec_decoder_t *) this_gen; - - pthread_mutex_lock(&this->nav_pci_lock); - spudec_clear_nav_list(this); - pthread_mutex_unlock(&this->nav_pci_lock); -} - - -static void spudec_dispose (spu_decoder_t *this_gen) { - - spudec_decoder_t *this = (spudec_decoder_t *) this_gen; - int i; - video_overlay_manager_t *ovl_manager = this->stream->video_out->get_overlay_manager (this->stream->video_out); - - if( this->menu_handle >= 0 ) - ovl_manager->free_handle(ovl_manager, - this->menu_handle); - this->menu_handle = -1; - - for (i=0; i < MAX_STREAMS; i++) { - if( this->spudec_stream_state[i].overlay_handle >= 0 ) - ovl_manager->free_handle(ovl_manager, - this->spudec_stream_state[i].overlay_handle); - this->spudec_stream_state[i].overlay_handle = -1; - free (this->spudec_stream_state[i].ra_seq.buf); - } - - spudec_clear_nav_list(this); - pthread_mutex_destroy(&this->nav_pci_lock); - - free (this->event.object.overlay); - free (this); -} - -/* gets the current already correctly processed nav_pci info */ -/* This is not perfectly in sync with the display, but all the same, */ -/* much closer than doing it at the input stage. */ -/* returns a bool for error/success.*/ -static int spudec_get_interact_info (spu_decoder_t *this_gen, void *data) { - spudec_decoder_t *this = (spudec_decoder_t *) this_gen; - /*printf("get_interact_info() called\n");*/ - if (!this || !data) - return 0; - - /*printf("get_interact_info() coping nav_pci\n");*/ - pthread_mutex_lock(&this->nav_pci_lock); - spudec_update_nav(this); - memcpy(data, &this->pci_cur.pci, sizeof(pci_t) ); - pthread_mutex_unlock(&this->nav_pci_lock); - return 1; - -} - -static void spudec_set_button (spu_decoder_t *this_gen, int32_t button, int32_t show) { - spudec_decoder_t *this = (spudec_decoder_t *) this_gen; - /* This function will move to video_overlay - * when video_overlay does menus */ - - video_overlay_manager_t *ovl_manager; - video_overlay_event_t *overlay_event = NULL; - vo_overlay_t *overlay = NULL; - overlay_event = xine_xmalloc (sizeof(video_overlay_event_t)); - - overlay = xine_xmalloc (sizeof(vo_overlay_t)); - /* FIXME: Watch out for threads. We should really put a lock on this - * because events is a different thread than decode_data */ - - if( this->menu_handle < 0 ) { - if (this->stream->video_out) { - ovl_manager = this->stream->video_out->get_overlay_manager (this->stream->video_out); - this->menu_handle = ovl_manager->get_handle(ovl_manager,1); - } - } -#ifdef LOG_BUTTON - printf ("libspudec:xine_decoder.c:spudec_event_listener:this=%p\n",this); - printf ("libspudec:xine_decoder.c:spudec_event_listener:this->menu_handle=%d\n",this->menu_handle); -#endif - if(this->menu_handle < 0) { - xprintf(this->stream->xine, XINE_VERBOSITY_DEBUG, - "Menu handle alloc failed. No more overlays objects available. Only %d at once please.", - MAX_OBJECTS); - free(overlay_event); - free(overlay); - return; - } - - if (show > 0) { -#ifdef LOG_NAV - fprintf (stderr,"libspudec:xine_decoder.c:spudec_event_listener:buttonN = %u show=%d\n", - button, - show); -#endif - this->buttonN = button; - if (this->button_filter != 1) { -#ifdef LOG_BUTTON - fprintf (stdout,"libspudec:xine_decoder.c:spudec_event_listener:buttonN updates not allowed\n"); -#endif - /* Only update highlight is the menu will let us */ - free(overlay_event); - free(overlay); - return; - } - if (show == 2) { - this->button_filter = 2; - } - pthread_mutex_lock(&this->nav_pci_lock); - spudec_update_nav(this); - overlay_event->object.handle = this->menu_handle; - overlay_event->object.pts = this->pci_cur.pci.hli.hl_gi.hli_s_ptm; - overlay_event->object.overlay=overlay; - overlay_event->event_type = OVERLAY_EVENT_MENU_BUTTON; -#ifdef LOG_BUTTON - fprintf(stderr, "libspudec:Button Overlay\n"); -#endif - spudec_copy_nav_to_overlay(this->stream->xine, &this->pci_cur.pci, this->state.clut, - this->buttonN, show-1, overlay, &this->overlay ); - pthread_mutex_unlock(&this->nav_pci_lock); - } else { - xprintf(this->stream->xine, XINE_VERBOSITY_DEBUG, - "libspudec:xine_decoder.c:spudec_event_listener:HIDE ????\n"); - printf("We dropped out here for some reason"); - _x_abort(); - overlay_event->object.handle = this->menu_handle; - overlay_event->event_type = OVERLAY_EVENT_HIDE; - } - overlay_event->vpts = 0; - if (this->stream->video_out) { - ovl_manager = this->stream->video_out->get_overlay_manager (this->stream->video_out); -#ifdef LOG_BUTTON - fprintf(stderr, "libspudec: add_event type=%d : current time=%lld, spu vpts=%lli\n", - overlay_event->event_type, - this->stream->xine->clock->get_current_time(this->stream->xine->clock), - overlay_event->vpts); -#endif - ovl_manager->add_event (ovl_manager, (void *)overlay_event); - free(overlay_event); - free(overlay); - } else { - free(overlay_event); - free(overlay); - } - return; -} - -static spu_decoder_t *open_plugin (spu_decoder_class_t *class_gen, xine_stream_t *stream) { - - spudec_decoder_t *this ; - int i; - - this = (spudec_decoder_t *) xine_xmalloc (sizeof (spudec_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 = spudec_get_interact_info; - this->spu_decoder.set_button = spudec_set_button; - this->stream = stream; - this->class = (spudec_class_t *) class_gen; - - this->menu_handle = -1; - this->buttonN = 1; - this->event.object.overlay = xine_xmalloc(sizeof(vo_overlay_t)); - - pthread_mutex_init(&this->nav_pci_lock, NULL); - this->pci_cur.pci.hli.hl_gi.hli_ss = 0; - this->pci_cur.next = NULL; - - this->ovl_caps = stream->video_out->get_capabilities(stream->video_out); - this->output_open = 0; - this->last_event_vpts = 0; - for (i=0; i < MAX_STREAMS; i++) { - this->spudec_stream_state[i].ra_seq.complete = 1; - this->spudec_stream_state[i].overlay_handle = -1; - } - -/* FIXME:Do we really need a default clut? */ - xine_fast_memcpy(this->state.clut, default_clut, sizeof(this->state.clut)); - this->state.need_clut = 1; - this->state.vobsub = 0; - - return &this->spu_decoder; -} - -static void *init_plugin (xine_t *xine, void *data) { - - spudec_class_t *this; - - this = (spudec_class_t *) xine_xmalloc (sizeof (spudec_class_t)); - - this->decoder_class.open_plugin = open_plugin; - this->decoder_class.identifier = "spudec"; - this->decoder_class.description = N_("DVD/VOB SPU decoder plugin"); - this->decoder_class.dispose = default_spu_decoder_class_dispose; - - lprintf ("libspudec:init_plugin called\n"); - return this; -} - -/* plugin catalog information */ -static uint32_t supported_types[] = { BUF_SPU_DVD, 0 }; - -static const decoder_info_t dec_info_data = { - supported_types, /* supported types */ - 5 /* priority */ -}; - -const plugin_info_t xine_plugin_info[] EXPORTED = { - /* type, API, "name", version, special_info, init_function */ - { PLUGIN_SPU_DECODER, 17, "spudec", XINE_VERSION_CODE, &dec_info_data, &init_plugin }, - { PLUGIN_NONE, 0, "", 0, NULL, NULL } -}; diff --git a/src/libspudvb/Makefile.am b/src/libspudvb/Makefile.am deleted file mode 100644 index 1adb154db..000000000 --- a/src/libspudvb/Makefile.am +++ /dev/null @@ -1,9 +0,0 @@ -include $(top_srcdir)/misc/Makefile.common - -AM_CFLAGS = $(DEFAULT_OCFLAGS) $(VISIBILITY_FLAG) -AM_LDFLAGS = $(xineplug_ldflags) - -xineplug_LTLIBRARIES = xineplug_decode_spudvb.la - -xineplug_decode_spudvb_la_SOURCES = xine_spudvb_decoder.c -xineplug_decode_spudvb_la_LIBADD = $(XINE_LIB) $(PTHREAD_LIBS) $(LTLIBINTL) diff --git a/src/libspudvb/xine_spudvb_decoder.c b/src/libspudvb/xine_spudvb_decoder.c deleted file mode 100644 index 9008260f7..000000000 --- a/src/libspudvb/xine_spudvb_decoder.c +++ /dev/null @@ -1,999 +0,0 @@ -/* - * Copyright (C) 2004 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 - * - * DVB Subtitle decoder (ETS 300 743) - * (c) 2004 Mike Lampard - * based on the application dvbsub by Dave Chapman - * - * TODO: - * - Implement support for teletext based subtitles - */ - -#include "pthread.h" -#include -#include -#include -#include -#define MAX_REGIONS 7 - -/*#define LOG 1*/ - -typedef struct { - int x, y; - unsigned char is_visible; -} visible_region_t; - -typedef struct { - int page_time_out; - int page_version_number; - int page_state; - int page_id; - visible_region_t regions[MAX_REGIONS]; -} page_t; - -typedef struct { - int width, height; - int empty; - int depth; - int CLUT_id; - int objects_start; - int objects_end; - unsigned int object_pos[65536]; - unsigned char *img; - osd_object_t *osd; -} region_t; - -typedef struct { -/* dvbsub stuff */ - int x; - int y; - unsigned int curr_obj; - unsigned int curr_reg[64]; - uint8_t *buf; - int i; - int nibble_flag; - int in_scanline; - page_t page; - region_t regions[MAX_REGIONS]; - clut_t colours[MAX_REGIONS*256]; - unsigned char trans[MAX_REGIONS*256]; -} dvbsub_func_t; - -typedef struct dvb_spu_class_s { - spu_decoder_class_t class; - xine_t *xine; -} dvb_spu_class_t; - -typedef struct dvb_spu_decoder_s { - spu_decoder_t spu_decoder; - - dvb_spu_class_t *class; - xine_stream_t *stream; - - spu_dvb_descriptor_t *spu_descriptor; - - /* dvbsub_osd_mutex should be locked around all calls to this->osd_renderer->show() - and this->osd_renderer->hide() */ - pthread_mutex_t dvbsub_osd_mutex; - - char *pes_pkt; - char *pes_pkt_wrptr; - unsigned int pes_pkt_size; - - uint64_t pts; - uint64_t vpts; - uint64_t end_vpts; - - pthread_t dvbsub_timer_thread; - struct timespec dvbsub_hide_timeout; - pthread_cond_t dvbsub_restart_timeout; - dvbsub_func_t *dvbsub; - int show; -} dvb_spu_decoder_t; - - -static void update_osd(dvb_spu_decoder_t *this, int region_id) -{ - dvbsub_func_t *dvbsub = this->dvbsub; - region_t *reg = &dvbsub->regions[region_id]; - - if ( !reg->img ) { - if ( reg->osd ) { - pthread_mutex_lock( &this->dvbsub_osd_mutex ); - this->stream->osd_renderer->free_object( reg->osd ); - reg->osd = NULL; - pthread_mutex_unlock( &this->dvbsub_osd_mutex ); - } - return; - } - - if ( reg->osd ) { - if ( reg->width!=reg->osd->width || reg->height!=reg->osd->height ) { - pthread_mutex_lock( &this->dvbsub_osd_mutex ); - this->stream->osd_renderer->free_object( reg->osd ); - reg->osd = NULL; - pthread_mutex_unlock( &this->dvbsub_osd_mutex ); - } - } - - if ( !reg->osd ) - reg->osd = this->stream->osd_renderer->new_object( this->stream->osd_renderer, reg->width, reg->height ); -} - -static void update_region (dvb_spu_decoder_t * this, int region_id, int region_width, int region_height, int fill, int fill_color) -{ - - dvbsub_func_t *dvbsub = this->dvbsub; - region_t *reg = &dvbsub->regions[region_id]; - page_t *page = &dvbsub->page; - - /* reject invalid sizes and set some limits ! */ - if ( region_width<=0 || region_height<=0 || region_width>720 || region_height>576 ) { - if ( reg->img ) { - free( reg->img ); - reg->img = NULL; - } -#ifdef LOG - printf("SPUDVB: rejected region %d = %dx%d\n", region_id, region_width, region_height ); -#endif - return; - } - - if ( reg->width*reg->heightimg ) { - free( reg->img ); - reg->img = NULL; - } - } - - if ( !reg->img ) { - if ( !(reg->img=xine_xmalloc(region_width*region_height)) ) { - lprintf( "can't allocate mem for region %d\n", region_id ); - return; - } - fill_color = 15; - fill = 1; - } - - if ( fill ) { - memset( reg->img, fill_color, region_width*region_height ); - reg->empty = 1; -#ifdef LOG - printf("SPUDVB : FILL REGION %d\n", region_id); -#endif - } - reg->width = region_width; - reg->height = region_height; - page->regions[region_id].is_visible = 1; -} - - -static void do_plot (dvb_spu_decoder_t * this, int r, int x, int y, unsigned char pixel) -{ - int i; - dvbsub_func_t *dvbsub = this->dvbsub; - - i = (y * dvbsub->regions[r].width) + x; - /* do some clipping */ - if ( i<(dvbsub->regions[r].width*dvbsub->regions[r].height) ) { - dvbsub->regions[r].img[i] = pixel; - dvbsub->regions[r].empty = 0; - } -} - -static void plot (dvb_spu_decoder_t * this, int r, int run_length, unsigned char pixel) -{ - - dvbsub_func_t *dvbsub = this->dvbsub; - - int x2 = dvbsub->x + run_length; - - while (dvbsub->x < x2) { - do_plot (this, r, dvbsub->x, dvbsub->y, pixel); - dvbsub->x++; - } -} - -static unsigned char next_nibble (dvb_spu_decoder_t * this) -{ - unsigned char x; - dvbsub_func_t *dvbsub = this->dvbsub; - - if (dvbsub->nibble_flag == 0) { - x = (dvbsub->buf[dvbsub->i] & 0xf0) >> 4; - dvbsub->nibble_flag = 1; - } - else { - x = (dvbsub->buf[dvbsub->i++] & 0x0f); - dvbsub->nibble_flag = 0; - } - return (x); -} - -static void decode_4bit_pixel_code_string (dvb_spu_decoder_t * this, int r, int object_id, int ofs, int n) -{ - int next_bits, switch_1, switch_2, switch_3, run_length, pixel_code; - - dvbsub_func_t *dvbsub = this->dvbsub; - - int bits; - unsigned int data; - int j; - - if (dvbsub->in_scanline == 0) { - dvbsub->in_scanline = 1; - } - dvbsub->nibble_flag = 0; - j = dvbsub->i + n; - while (dvbsub->i < j) { - - bits = 0; - pixel_code = 0; - next_bits = next_nibble (this); - - if (next_bits != 0) { - pixel_code = next_bits; - plot (this, r, 1, pixel_code); - bits += 4; - } - else { - bits += 4; - data = next_nibble (this); - switch_1 = (data & 0x08) >> 3; - bits++; - if (switch_1 == 0) { - run_length = (data & 0x07); - bits += 3; - if (run_length != 0) { - plot (this, r, run_length + 2, pixel_code); - } - else { - break; - } - } - else { - switch_2 = (data & 0x04) >> 2; - bits++; - if (switch_2 == 0) { - run_length = (data & 0x03); - bits += 2; - pixel_code = next_nibble (this); - bits += 4; - plot (this, r, run_length + 4, pixel_code); - } - else { - switch_3 = (data & 0x03); - bits += 2; - switch (switch_3) { - case 0: - plot (this, r, 1, pixel_code); - break; - case 1: - plot (this, r, 2, pixel_code); - break; - case 2: - run_length = next_nibble (this); - bits += 4; - pixel_code = next_nibble (this); - bits += 4; - plot (this, r, run_length + 9, pixel_code); - break; - case 3: - run_length = next_nibble (this); - run_length = (run_length << 4) | next_nibble (this); - bits += 8; - pixel_code = next_nibble (this); - bits += 4; - plot (this, r, run_length + 25, pixel_code); - } - } - } - } - - } - if (dvbsub->nibble_flag == 1) { - dvbsub->i++; - dvbsub->nibble_flag = 0; - } -} - -static void recalculate_trans (dvb_spu_decoder_t *this) -{ - dvbsub_func_t *const dvbsub = this->dvbsub; - xine_spu_opacity_t opacity; - int i; - - _x_spu_get_opacity (this->stream->xine, &opacity); - for (i = 0; i < MAX_REGIONS * 256; ++i) { - /* ETSI-300-743 says "full transparency if Y == 0". */ - if (dvbsub->colours[i].y == 0) - dvbsub->trans[i] = 0; - else { - int v = _x_spu_calculate_opacity (&dvbsub->colours[i], dvbsub->colours[i].foo, &opacity); - dvbsub->trans[i] = v * 14 / 255 + 1; - } - } - -} - -static void set_clut(dvb_spu_decoder_t *this,int CLUT_id,int CLUT_entry_id,int Y_value, int Cr_value, int Cb_value, int T_value) { - - dvbsub_func_t *dvbsub = this->dvbsub; - - if ((CLUT_id>=MAX_REGIONS) || (CLUT_entry_id>15)) { - return; - } - - dvbsub->colours[(CLUT_id*256)+CLUT_entry_id].y=Y_value; - dvbsub->colours[(CLUT_id*256)+CLUT_entry_id].cr=Cr_value; - dvbsub->colours[(CLUT_id*256)+CLUT_entry_id].cb=Cb_value; - dvbsub->colours[(CLUT_id*256)+CLUT_entry_id].foo = T_value; -} - -static void process_CLUT_definition_segment(dvb_spu_decoder_t *this) { - int page_id, - segment_length, - CLUT_id, - CLUT_version_number; - - int CLUT_entry_id, - CLUT_flag_8_bit, - CLUT_flag_4_bit, - CLUT_flag_2_bit, - full_range_flag, - Y_value, - Cr_value, - Cb_value, - T_value; - dvbsub_func_t *dvbsub = this->dvbsub; - - int j; - - page_id=(dvbsub->buf[dvbsub->i]<<8)|dvbsub->buf[dvbsub->i+1]; dvbsub->i+=2; - segment_length=(dvbsub->buf[dvbsub->i]<<8)|dvbsub->buf[dvbsub->i+1]; dvbsub->i+=2; - j=dvbsub->i+segment_length; - - CLUT_id=dvbsub->buf[dvbsub->i++]; - CLUT_version_number=(dvbsub->buf[dvbsub->i]&0xf0)>>4; - dvbsub->i++; - - while (dvbsub->i < j) { - CLUT_entry_id=dvbsub->buf[dvbsub->i++]; - - CLUT_flag_2_bit=(dvbsub->buf[dvbsub->i]&0x80)>>7; - CLUT_flag_4_bit=(dvbsub->buf[dvbsub->i]&0x40)>>6; - CLUT_flag_8_bit=(dvbsub->buf[dvbsub->i]&0x20)>>5; - full_range_flag=dvbsub->buf[dvbsub->i]&1; - dvbsub->i++; - - if (full_range_flag==1) { - Y_value=dvbsub->buf[dvbsub->i++]; - Cr_value=dvbsub->buf[dvbsub->i++]; - Cb_value=dvbsub->buf[dvbsub->i++]; - T_value=dvbsub->buf[dvbsub->i++]; - } else { - Y_value = dvbsub->buf[dvbsub->i] & 0xfc; - Cr_value = (dvbsub->buf[dvbsub->i] << 6 | dvbsub->buf[dvbsub->i + 1] >> 2) & 0xf0; - Cb_value = (dvbsub->buf[dvbsub->i + 1] << 2) & 0xf0; - T_value = (dvbsub->buf[dvbsub->i + 1] & 3) * 0x55; /* expand only this one to full range! */ - dvbsub->i+=2; - } - set_clut(this, CLUT_id,CLUT_entry_id,Y_value,Cr_value,Cb_value,T_value); - } -} - -static void process_pixel_data_sub_block (dvb_spu_decoder_t * this, int r, int o, int ofs, int n) -{ - int data_type; - int j; - - dvbsub_func_t *dvbsub = this->dvbsub; - - j = dvbsub->i + n; - - dvbsub->x = (dvbsub->regions[r].object_pos[o]) >> 16; - dvbsub->y = ((dvbsub->regions[r].object_pos[o]) & 0xffff) + ofs; - while (dvbsub->i < j) { - data_type = dvbsub->buf[dvbsub->i++]; - - switch (data_type) { - case 0: - dvbsub->i++; - case 0x11: - decode_4bit_pixel_code_string (this, r, o, ofs, n - 1); - break; - case 0xf0: - dvbsub->in_scanline = 0; - dvbsub->x = (dvbsub->regions[r].object_pos[o]) >> 16; - dvbsub->y += 2; - break; - default: - lprintf ("unimplemented data_type %02x in pixel_data_sub_block\n", data_type); - } - } - - dvbsub->i = j; -} - -static void process_page_composition_segment (dvb_spu_decoder_t * this) -{ - int segment_length; - int region_id, region_x, region_y; - int j; - int r; - dvbsub_func_t *dvbsub = this->dvbsub; - - dvbsub->page.page_id = (dvbsub->buf[dvbsub->i] << 8) | dvbsub->buf[dvbsub->i + 1]; - dvbsub->i += 2; - segment_length = (dvbsub->buf[dvbsub->i] << 8) | dvbsub->buf[dvbsub->i + 1]; - dvbsub->i += 2; - - j = dvbsub->i + segment_length; - - dvbsub->page.page_time_out = dvbsub->buf[dvbsub->i++]; - - dvbsub->page.page_version_number = (dvbsub->buf[dvbsub->i] & 0xf0) >> 4; - dvbsub->page.page_state = (dvbsub->buf[dvbsub->i] & 0x0c) >> 2; - dvbsub->i++; - if (dvbsub->page.page_state==2) { - for (r=0; rpage.regions[r].is_visible = 0; - } - else if ( dvbsub->page.page_state!=0 && dvbsub->page.page_state!=1 ) { - return; - } - - while (dvbsub->i < j) { - region_id = dvbsub->buf[dvbsub->i++]; - dvbsub->i++; /* reserved */ - region_x = (dvbsub->buf[dvbsub->i] << 8) | dvbsub->buf[dvbsub->i + 1]; - dvbsub->i += 2; - region_y = (dvbsub->buf[dvbsub->i] << 8) | dvbsub->buf[dvbsub->i + 1]; - dvbsub->i += 2; - - dvbsub->page.regions[region_id].x = region_x; - dvbsub->page.regions[region_id].y = region_y; - } -} - - -static void process_region_composition_segment (dvb_spu_decoder_t * this) -{ - int segment_length, - region_id, - region_version_number, - region_fill_flag, region_width, region_height, region_level_of_compatibility, region_depth, CLUT_id, region_8_bit_pixel_code, region_4_bit_pixel_code, region_2_bit_pixel_code; - int object_id, object_type, object_provider_flag, object_x, object_y, foreground_pixel_code, background_pixel_code; - int j; - int o; - dvbsub_func_t *dvbsub = this->dvbsub; - - dvbsub->page.page_id = (dvbsub->buf[dvbsub->i] << 8) | dvbsub->buf[dvbsub->i + 1]; - dvbsub->i += 2; - segment_length = (dvbsub->buf[dvbsub->i] << 8) | dvbsub->buf[dvbsub->i + 1]; - dvbsub->i += 2; - j = dvbsub->i + segment_length; - - region_id = dvbsub->buf[dvbsub->i++]; - region_version_number = (dvbsub->buf[dvbsub->i] & 0xf0) >> 4; - region_fill_flag = (dvbsub->buf[dvbsub->i] & 0x08) >> 3; - dvbsub->i++; - region_width = (dvbsub->buf[dvbsub->i] << 8) | dvbsub->buf[dvbsub->i + 1]; - dvbsub->i += 2; - region_height = (dvbsub->buf[dvbsub->i] << 8) | dvbsub->buf[dvbsub->i + 1]; - dvbsub->i += 2; - region_level_of_compatibility = (dvbsub->buf[dvbsub->i] & 0xe0) >> 5; - region_depth = (dvbsub->buf[dvbsub->i] & 0x1c) >> 2; - dvbsub->i++; - CLUT_id = dvbsub->buf[dvbsub->i++]; - region_8_bit_pixel_code = dvbsub->buf[dvbsub->i++]; - region_4_bit_pixel_code = (dvbsub->buf[dvbsub->i] & 0xf0) >> 4; - region_2_bit_pixel_code = (dvbsub->buf[dvbsub->i] & 0x0c) >> 2; - dvbsub->i++; - - if(region_id>=MAX_REGIONS) - return; - - /* Check if region size has changed and fill background. */ - update_region (this, region_id, region_width, region_height, region_fill_flag, region_4_bit_pixel_code); - if ( CLUT_idregions[region_id].CLUT_id = CLUT_id; - - dvbsub->regions[region_id].objects_start = dvbsub->i; - dvbsub->regions[region_id].objects_end = j; - - for (o = 0; o < 65536; o++) { - dvbsub->regions[region_id].object_pos[o] = 0xffffffff; - } - - while (dvbsub->i < j) { - object_id = (dvbsub->buf[dvbsub->i] << 8) | dvbsub->buf[dvbsub->i + 1]; - dvbsub->i += 2; - object_type = (dvbsub->buf[dvbsub->i] & 0xc0) >> 6; - object_provider_flag = (dvbsub->buf[dvbsub->i] & 0x30) >> 4; - object_x = ((dvbsub->buf[dvbsub->i] & 0x0f) << 8) | dvbsub->buf[dvbsub->i + 1]; - dvbsub->i += 2; - object_y = ((dvbsub->buf[dvbsub->i] & 0x0f) << 8) | dvbsub->buf[dvbsub->i + 1]; - dvbsub->i += 2; - - dvbsub->regions[region_id].object_pos[object_id] = (object_x << 16) | object_y; - - if ((object_type == 0x01) || (object_type == 0x02)) { - foreground_pixel_code = dvbsub->buf[dvbsub->i++]; - background_pixel_code = dvbsub->buf[dvbsub->i++]; - } - } - -} - -static void process_object_data_segment (dvb_spu_decoder_t * this) -{ - int segment_length, object_id, object_version_number, object_coding_method, non_modifying_colour_flag; - - int top_field_data_block_length, bottom_field_data_block_length; - - dvbsub_func_t *dvbsub = this->dvbsub; - - int j; - int old_i; - int r; - - dvbsub->page.page_id = (dvbsub->buf[dvbsub->i] << 8) | dvbsub->buf[dvbsub->i + 1]; - dvbsub->i += 2; - segment_length = (dvbsub->buf[dvbsub->i] << 8) | dvbsub->buf[dvbsub->i + 1]; - dvbsub->i += 2; - j = dvbsub->i + segment_length; - - object_id = (dvbsub->buf[dvbsub->i] << 8) | dvbsub->buf[dvbsub->i + 1]; - dvbsub->i += 2; - dvbsub->curr_obj = object_id; - object_version_number = (dvbsub->buf[dvbsub->i] & 0xf0) >> 4; - object_coding_method = (dvbsub->buf[dvbsub->i] & 0x0c) >> 2; - non_modifying_colour_flag = (dvbsub->buf[dvbsub->i] & 0x02) >> 1; - dvbsub->i++; - - old_i = dvbsub->i; - for (r = 0; r < MAX_REGIONS; r++) { - - /* If this object is in this region... */ - if (dvbsub->regions[r].img) { - if (dvbsub->regions[r].object_pos[object_id] != 0xffffffff) { - dvbsub->i = old_i; - if (object_coding_method == 0) { - top_field_data_block_length = (dvbsub->buf[dvbsub->i] << 8) | dvbsub->buf[dvbsub->i + 1]; - dvbsub->i += 2; - bottom_field_data_block_length = (dvbsub->buf[dvbsub->i] << 8) | dvbsub->buf[dvbsub->i + 1]; - dvbsub->i += 2; - - process_pixel_data_sub_block (this, r, object_id, 0, top_field_data_block_length); - - process_pixel_data_sub_block (this, r, object_id, 1, bottom_field_data_block_length); - } - } - } - } -} - -static void unlock_mutex_cancellation_func(void *mutex_gen) -{ - pthread_mutex_t *mutex = (pthread_mutex_t*) mutex_gen; - pthread_mutex_unlock(mutex); -} - -/* Thread routine that checks for subtitle timeout periodically. - To avoid unexpected subtitle hiding, calls to this->stream->osd_renderer->show() - should be in blocks like: - - pthread_mutex_lock(&this->dvbsub_osd_mutex); - this->stream->osd_renderer->show(...); - this->dvbsub_hide_timeout.tv_sec = time(NULL) + timeout value; - pthread_cond_signal(&this->dvbsub_restart_timeout); - pthread_mutex_unlock(&this->dvbsub_osd_mutex); - - This ensures that the timeout is changed with the lock held, and - that the thread is signalled to pick up the new timeout. -*/ -static void* dvbsub_timer_func(void *this_gen) -{ - dvb_spu_decoder_t *this = (dvb_spu_decoder_t *) this_gen; - pthread_mutex_lock(&this->dvbsub_osd_mutex); - int i; - - /* If we're cancelled via pthread_cancel, unlock the mutex */ - pthread_cleanup_push(unlock_mutex_cancellation_func, &this->dvbsub_osd_mutex); - - while(1) - { - /* Record the current timeout, and wait - note that pthread_cond_timedwait - will unlock the mutex on entry, and lock it on exit */ - struct timespec timeout = this->dvbsub_hide_timeout; - int result = pthread_cond_timedwait(&this->dvbsub_restart_timeout, - &this->dvbsub_osd_mutex, - &this->dvbsub_hide_timeout); - if(result == ETIMEDOUT && - timeout.tv_sec == this->dvbsub_hide_timeout.tv_sec && - timeout.tv_nsec == this->dvbsub_hide_timeout.tv_nsec) - { - /* We timed out, and no-one changed the timeout underneath us. - Hide the OSD, then wait until we're signalled. */ - if(this && this->stream && this->stream->osd_renderer) - { - for ( i=0; idvbsub->regions[i].osd ) { - this->stream->osd_renderer->hide( this->dvbsub->regions[i].osd, 0 ); -#ifdef LOG - printf("SPUDVB: thread hiding = %d\n",i); -#endif - } - } - } - pthread_cond_wait(&this->dvbsub_restart_timeout, &this->dvbsub_osd_mutex); - } - } - - pthread_cleanup_pop(1); - return NULL; -} - -static void downscale_region_image( region_t *reg, unsigned char *dest, int dest_width ) -{ - float i, k, inc=reg->width/(float)dest_width; - int j; - for ( j=0; jheight; j++ ) { - for ( i=0,k=0; iwidth && kimg[(j*reg->width)+(int)i]; - } - } -} - -static void draw_subtitles (dvb_spu_decoder_t * this) -{ - int r; - int display=0; - int64_t dum; - int dest_width=0, dest_height, reg_width; - this->stream->video_out->status(this->stream->video_out, NULL, &dest_width, &dest_height, &dum); - unsigned char tmp[dest_width*576]; - unsigned char *reg; - - if ( !dest_width ) - return; - - /* render all regions onto the page */ - - for ( r=0; rdvbsub->page.regions[r].is_visible ) - display++; - } - if ( !display ) - return; - - for (r = 0; r < MAX_REGIONS; r++) { - if (this->dvbsub->regions[r].img) { - if (this->dvbsub->page.regions[r].is_visible && !this->dvbsub->regions[r].empty) { - update_osd( this, r ); - if ( !this->dvbsub->regions[r].osd ) - continue; - /* clear osd */ - this->stream->osd_renderer->clear( this->dvbsub->regions[r].osd ); - if (this->dvbsub->regions[r].width>dest_width) { - downscale_region_image(&this->dvbsub->regions[r], tmp, dest_width); - reg = tmp; - reg_width = dest_width; - } - else { - reg = this->dvbsub->regions[r].img; - reg_width = this->dvbsub->regions[r].width; - } - this->stream->osd_renderer->set_palette( this->dvbsub->regions[r].osd, (uint32_t*)(&this->dvbsub->colours[this->dvbsub->regions[r].CLUT_id*256]), &this->dvbsub->trans[this->dvbsub->regions[r].CLUT_id*256]); - this->stream->osd_renderer->draw_bitmap( this->dvbsub->regions[r].osd, reg, 0, 0, reg_width, this->dvbsub->regions[r].height, NULL ); - } - } - } - - pthread_mutex_lock(&this->dvbsub_osd_mutex); -#ifdef LOG - printf("SPUDVB: this->vpts=%llu\n",this->vpts); -#endif - for ( r=0; rdvbsub->page.regions[r].is_visible && this->dvbsub->regions[r].osd && !this->dvbsub->regions[r].empty ) { - this->stream->osd_renderer->set_position( this->dvbsub->regions[r].osd, this->dvbsub->page.regions[r].x, this->dvbsub->page.regions[r].y ); - this->stream->osd_renderer->show( this->dvbsub->regions[r].osd, this->vpts ); -#ifdef LOG - printf("SPUDVB: show region = %d\n",r); -#endif - } - else { - if ( this->dvbsub->regions[r].osd ) { - this->stream->osd_renderer->hide( this->dvbsub->regions[r].osd, this->vpts ); -#ifdef LOG - printf("SPUDVB: hide region = %d\n",r); -#endif - } - } - } - this->dvbsub_hide_timeout.tv_nsec = 0; - this->dvbsub_hide_timeout.tv_sec = time(NULL) + this->dvbsub->page.page_time_out; -#ifdef LOG - printf("SPUDVB: page_time_out %d\n",this->dvbsub->page.page_time_out); -#endif - pthread_cond_signal(&this->dvbsub_restart_timeout); - pthread_mutex_unlock(&this->dvbsub_osd_mutex); -} - - -static void spudec_decode_data (spu_decoder_t * this_gen, buf_element_t * buf) -{ - dvb_spu_decoder_t *this = (dvb_spu_decoder_t *) this_gen; - int new_i; - int data_identifier, subtitle_stream_id; - int segment_length, segment_type; - int PES_header_data_length; - int PES_packet_length; - int i; - - if((buf->type & 0xffff0000)!=BUF_SPU_DVB) - return; - - if (buf->decoder_flags & BUF_FLAG_SPECIAL) { - if (buf->decoder_info[1] == BUF_SPECIAL_SPU_DVB_DESCRIPTOR) { - if (buf->decoder_info[2] == 0) { - /* Hide the osd - note that if the timeout thread times out, it'll rehide, which is harmless */ - pthread_mutex_lock(&this->dvbsub_osd_mutex); - for ( i=0; idvbsub->regions[i].osd ) - this->stream->osd_renderer->hide( this->dvbsub->regions[i].osd, 0 ); - } - pthread_mutex_unlock(&this->dvbsub_osd_mutex); - } - else { - xine_fast_memcpy (this->spu_descriptor, buf->decoder_info_ptr[2], buf->decoder_info[2]); - } - } - return; - } - else { - if (buf->decoder_info[2]) { - memset (this->pes_pkt, 0xff, 64*1024); - this->pes_pkt_wrptr = this->pes_pkt; - this->pes_pkt_size = buf->decoder_info[2]; - this->pts = buf->pts; - - xine_fast_memcpy (this->pes_pkt, buf->content, buf->size); - this->pes_pkt_wrptr += buf->size; - } - else { - if (this->pes_pkt && (this->pes_pkt_wrptr != this->pes_pkt)) { - xine_fast_memcpy (this->pes_pkt_wrptr, buf->content, buf->size); - this->pes_pkt_wrptr += buf->size; - } - } - } - /* don't ask metronom for a vpts but rather do the calculation - * because buf->pts could be too far in future and metronom won't accept - * further backwards pts (see metronom_got_spu_packet) */ - if (buf->pts) { - metronom_t *metronom = this->stream->metronom; - int64_t vpts_offset = metronom->get_option( metronom, METRONOM_VPTS_OFFSET ); - int64_t spu_offset = metronom->get_option( metronom, METRONOM_SPU_OFFSET ); - int64_t vpts = (int64_t)(buf->pts)+vpts_offset+spu_offset; - metronom_clock_t *clock = this->stream->xine->clock; - int64_t curvpts = clock->get_current_time( clock ); - /* if buf->pts is unreliable, show page asap (better than nothing) */ -#ifdef LOG - printf("SPUDVB: spu_vpts=%lld - current_vpts=%lld\n", vpts, curvpts); -#endif - if ( vpts<=curvpts || (vpts-curvpts)>(5*90000) ) - this->vpts = 0; - else - this->vpts = vpts; - } - - /* process the pes section */ - - PES_packet_length = this->pes_pkt_size; - - this->dvbsub->buf = this->pes_pkt; - - PES_header_data_length = 0; - this->dvbsub->i = 0; - - data_identifier = this->dvbsub->buf[this->dvbsub->i++]; - subtitle_stream_id = this->dvbsub->buf[this->dvbsub->i++]; - - while (this->dvbsub->i <= (PES_packet_length)) { - /* SUBTITLING SEGMENT */ - this->dvbsub->i++; - segment_type = this->dvbsub->buf[this->dvbsub->i++]; - - this->dvbsub->page.page_id = (this->dvbsub->buf[this->dvbsub->i] << 8) | this->dvbsub->buf[this->dvbsub->i + 1]; - segment_length = (this->dvbsub->buf[this->dvbsub->i + 2] << 8) | this->dvbsub->buf[this->dvbsub->i + 3]; - new_i = this->dvbsub->i + segment_length + 4; - - /* only process complete segments */ - if(new_i > (this->pes_pkt_wrptr - this->pes_pkt)) - break; - /* verify we've the right segment */ - if(this->dvbsub->page.page_id==this->spu_descriptor->comp_page_id){ - /* SEGMENT_DATA_FIELD */ - switch (segment_type & 0xff) { - case 0x10: - process_page_composition_segment (this); - break; - case 0x11: - process_region_composition_segment (this); - break; - case 0x12: - process_CLUT_definition_segment(this); - break; - case 0x13: - process_object_data_segment (this); - break; - case 0x80: /* Page is now completely rendered */ - recalculate_trans(this); - draw_subtitles( this ); - break; - default: - return; - break; - } - } - this->dvbsub->i = new_i; - } - - return; -} - -static void spudec_reset (spu_decoder_t * this_gen) -{ - dvb_spu_decoder_t *this = (dvb_spu_decoder_t *) this_gen; - int i; - - /* Hide the osd - if the timeout thread times out, it'll rehide harmlessly */ - pthread_mutex_lock(&this->dvbsub_osd_mutex); - for ( i=0; idvbsub->regions[i].osd ) - this->stream->osd_renderer->hide(this->dvbsub->regions[i].osd, 0); - } - pthread_mutex_unlock(&this->dvbsub_osd_mutex); - -} - -static void spudec_discontinuity (spu_decoder_t * this_gen) -{ - /* do nothing */ -} - -static void spudec_dispose (spu_decoder_t * this_gen) -{ - dvb_spu_decoder_t *this = (dvb_spu_decoder_t *) this_gen; - int i; - - pthread_cancel(this->dvbsub_timer_thread); - pthread_join(this->dvbsub_timer_thread, NULL); - pthread_mutex_destroy(&this->dvbsub_osd_mutex); - pthread_cond_destroy(&this->dvbsub_restart_timeout); - - if(this->spu_descriptor){ - free(this->spu_descriptor); - this->spu_descriptor=NULL; - } - - for ( i=0; idvbsub->regions[i].img ) - free( this->dvbsub->regions[i].img ); - if ( this->dvbsub->regions[i].osd ) - this->stream->osd_renderer->free_object( this->dvbsub->regions[i].osd ); - } - - if (this->pes_pkt) - free (this->pes_pkt); - - if (this->dvbsub) - free (this->dvbsub); - - free (this); -} - -static spu_decoder_t *dvb_spu_class_open_plugin (spu_decoder_class_t * class_gen, xine_stream_t * stream) -{ - - int i; - dvb_spu_decoder_t *this; - dvb_spu_class_t *class = (dvb_spu_class_t *) class_gen; - - this = (dvb_spu_decoder_t *) xine_xmalloc (sizeof (dvb_spu_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->class = class; - this->stream = stream; - - this->pes_pkt = xine_xmalloc (1024*65); - this->spu_descriptor = xine_xmalloc(sizeof(spu_dvb_descriptor_t)); - - this->dvbsub = xine_xmalloc (sizeof (dvbsub_func_t)); - - for (i = 0; i < MAX_REGIONS; i++) { - this->dvbsub->page.regions[i].is_visible = 0; - this->dvbsub->regions[i].img = NULL; - this->dvbsub->regions[i].osd = NULL; - this->dvbsub->regions[i].CLUT_id = 0; - } - - { - xine_spu_opacity_t opacity; - static const clut_t black = { 0, 0, 0, 0 }; - int t; - - _x_spu_get_opacity (this->stream->xine, &opacity); - t = _x_spu_calculate_opacity (&black, 0, &opacity); - - for (i = 0; i < MAX_REGIONS * 256; i++) - this->dvbsub->colours[i].foo = t; - } - - pthread_mutex_init(&this->dvbsub_osd_mutex, NULL); - pthread_cond_init(&this->dvbsub_restart_timeout, NULL); - this->dvbsub_hide_timeout.tv_nsec = 0; - this->dvbsub_hide_timeout.tv_sec = time(NULL); - pthread_create(&this->dvbsub_timer_thread, NULL, dvbsub_timer_func, this); - - return (spu_decoder_t *) this; -} - -static void *init_spu_decoder_plugin (xine_t * xine, void *data) -{ - - dvb_spu_class_t *this; - this = (dvb_spu_class_t *) xine_xmalloc (sizeof (dvb_spu_class_t)); - - this->class.open_plugin = dvb_spu_class_open_plugin; - this->class.identifier = "spudvb"; - this->class.description = N_("DVB subtitle decoder plugin"); - this->class.dispose = default_spu_decoder_class_dispose; - - this->xine = xine; - - return &this->class; -} - - -/* plugin catalog information */ -static uint32_t supported_types[] = { BUF_SPU_DVB, 0 }; - -static const decoder_info_t spudec_info = { - supported_types, /* supported types */ - 1 /* priority */ -}; - -const plugin_info_t xine_plugin_info[] EXPORTED = { -/* type, API, "name", version, special_info, init_function */ - {PLUGIN_SPU_DECODER, 17, "spudvb", XINE_VERSION_CODE, &spudec_info, - &init_spu_decoder_plugin}, - {PLUGIN_NONE, 0, "", 0, NULL, NULL} -}; diff --git a/src/libsputext/Makefile.am b/src/libsputext/Makefile.am deleted file mode 100644 index 14c6f2323..000000000 --- a/src/libsputext/Makefile.am +++ /dev/null @@ -1,12 +0,0 @@ -include $(top_srcdir)/misc/Makefile.common - -AM_CFLAGS = $(DEFAULT_OCFLAGS) $(VISIBILITY_FLAG) -AM_LDFLAGS = $(xineplug_ldflags) - -xineplug_LTLIBRARIES = xineplug_decode_sputext.la xineplug_dmx_sputext.la - -xineplug_dmx_sputext_la_SOURCES = demux_sputext.c -xineplug_dmx_sputext_la_LIBADD = $(XINE_LIB) $(LTLIBINTL) - -xineplug_decode_sputext_la_SOURCES = xine_sputext_decoder.c -xineplug_decode_sputext_la_LIBADD = $(XINE_LIB) $(LTLIBINTL) diff --git a/src/libsputext/demux_sputext.c b/src/libsputext/demux_sputext.c deleted file mode 100644 index 270894f50..000000000 --- a/src/libsputext/demux_sputext.c +++ /dev/null @@ -1,1489 +0,0 @@ -/* - * 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 - * - * code based on old libsputext/xine_decoder.c - * - * code based on mplayer module: - * - * Subtitle reader with format autodetection - * - * Written by laaz - * Some code cleanup & realloc() by A'rpi/ESP-team - * dunnowhat sub format by szabi - */ - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#include -#include -#include -#include -#include -#include -#include -#include - -#define LOG_MODULE "demux_sputext" -#define LOG_VERBOSE -/* -#define LOG -*/ - -#include -#include -#include - -#define ERR (void *)-1 -#define SUB_MAX_TEXT 5 -#define SUB_BUFSIZE 1024 -#define LINE_LEN 1000 -#define LINE_LEN_QUOT "1000" - -/* - * Demuxer typedefs - */ - -typedef struct { - - int lines; - - long start; /* csecs */ - long end; /* csecs */ - - char *text[SUB_MAX_TEXT]; - -} subtitle_t; - - -typedef struct { - - demux_plugin_t demux_plugin; - xine_stream_t *stream; - input_plugin_t *input; - - int status; - - char buf[SUB_BUFSIZE]; - off_t buflen; - - float mpsub_position; - - int uses_time; - int errs; - subtitle_t *subtitles; - int num; /* number of subtitle structs */ - int cur; /* current subtitle */ - int format; /* constants see below */ - char next_line[SUB_BUFSIZE]; /* a buffer for next line read from file */ - -} demux_sputext_t; - -typedef struct demux_sputext_class_s { - - demux_class_t demux_class; - - int max_timeout; /* default timeout of hidding subtitles */ - -} demux_sputext_class_t; - -/* - * Demuxer code start - */ - -#define FORMAT_UNKNOWN -1 -#define FORMAT_MICRODVD 0 -#define FORMAT_SUBRIP 1 -#define FORMAT_SUBVIEWER 2 -#define FORMAT_SAMI 3 -#define FORMAT_VPLAYER 4 -#define FORMAT_RT 5 -#define FORMAT_SSA 6 /* Sub Station Alpha */ -#define FORMAT_PJS 7 -#define FORMAT_MPSUB 8 -#define FORMAT_AQTITLE 9 -#define FORMAT_JACOBSUB 10 -#define FORMAT_SUBVIEWER2 11 -#define FORMAT_SUBRIP09 12 -#define FORMAT_MPL2 13 /*Mplayer sub 2 ?*/ - -static int eol(char p) { - return (p=='\r' || p=='\n' || p=='\0'); -} - -static inline void trail_space(char *s) { - int i; - while (isspace(*s)) { - char *copy = s; - do { - copy[0] = copy[1]; - copy++; - } while(*copy); - } - i = strlen(s) - 1; - while (i > 0 && isspace(s[i])) - s[i--] = '\0'; -} - -/* - * Reimplementation of fgets() using the input->read() method. - */ -static char *read_line_from_input(demux_sputext_t *this, char *line, off_t len) { - off_t nread = 0; - char *s; - int linelen; - - if ((len - this->buflen) > 512) { - if((nread = this->input->read(this->input, - &this->buf[this->buflen], len - this->buflen)) < 0) { - xprintf(this->stream->xine, XINE_VERBOSITY_DEBUG, "read failed.\n"); - return NULL; - } - } - - this->buflen += nread; - this->buf[this->buflen] = '\0'; - - s = strchr(this->buf, '\n'); - - if (line && (s || this->buflen)) { - - linelen = s ? (s - this->buf) + 1 : this->buflen; - - memcpy(line, this->buf, linelen); - line[linelen] = '\0'; - - memmove(this->buf, &this->buf[linelen], SUB_BUFSIZE - linelen); - this->buflen -= linelen; - - return line; - } - - return NULL; -} - - -static subtitle_t *sub_read_line_sami(demux_sputext_t *this, subtitle_t *current) { - - static char line[LINE_LEN + 1]; - static char *s = NULL; - char text[LINE_LEN + 1], *p, *q; - int state; - - p = NULL; - current->lines = current->start = 0; - current->end = -1; - state = 0; - - /* read the first line */ - if (!s) - if (!(s = read_line_from_input(this, line, LINE_LEN))) return 0; - - do { - switch (state) { - - case 0: /* find "START=" */ - s = strstr (s, "Start="); - if (s) { - current->start = strtol (s + 6, &s, 0) / 10; - state = 1; continue; - } - break; - - case 1: /* find "" */ - if ((s = strchr (s, '>'))) { s++; state = 3; p = text; continue; } - break; - - case 3: /* get all text until '<' appears */ - if (*s == '\0') { break; } - else if (*s == '<') { state = 4; } - else if (!strncasecmp (s, " ", 6)) { *p++ = ' '; s += 6; } - else if (*s == '\r') { s++; } - else if (!strncasecmp (s, "
", 4) || *s == '\n') { - *p = '\0'; p = text; trail_space (text); - if (text[0] != '\0') - current->text[current->lines++] = strdup (text); - if (*s == '\n') s++; else s += 4; - } - else *p++ = *s++; - continue; - - case 4: /* get current->end or skip */ - q = strstr (s, "Start="); - if (q) { - current->end = strtol (q + 6, &q, 0) / 10 - 1; - *p = '\0'; trail_space (text); - if (text[0] != '\0') - current->text[current->lines++] = strdup (text); - if (current->lines > 0) { state = 99; break; } - state = 0; continue; - } - s = strchr (s, '>'); - if (s) { s++; state = 3; continue; } - break; - } - - /* read next line */ - if (state != 99 && !(s = read_line_from_input (this, line, LINE_LEN))) - return 0; - - } while (state != 99); - - return current; -} - - -static char *sub_readtext(char *source, char **dest) { - int len=0; - char *p=source; - - while ( !eol(*p) && *p!= '|' ) { - p++,len++; - } - - *dest= (char *)xine_xmalloc (len+1); - if (!dest) - return ERR; - - strncpy(*dest, source, len); - (*dest)[len]=0; - - while (*p=='\r' || *p=='\n' || *p=='|') - p++; - - if (*p) return p; /* not-last text field */ - else return NULL; /* last text field */ -} - -static subtitle_t *sub_read_line_microdvd(demux_sputext_t *this, subtitle_t *current) { - - char line[LINE_LEN + 1]; - char line2[LINE_LEN + 1]; - char *p, *next; - int i; - - memset (current, 0, sizeof(subtitle_t)); - - current->end=-1; - do { - if (!read_line_from_input (this, line, LINE_LEN)) return NULL; - } while ((sscanf (line, "{%ld}{}%" LINE_LEN_QUOT "[^\r\n]", &(current->start), line2) !=2) && - (sscanf (line, "{%ld}{%ld}%" LINE_LEN_QUOT "[^\r\n]", &(current->start), &(current->end),line2) !=3) - ); - - p=line2; - - next=p, i=0; - while ((next =sub_readtext (next, &(current->text[i])))) { - if (current->text[i]==ERR) return ERR; - i++; - if (i>=SUB_MAX_TEXT) { - xprintf (this->stream->xine, XINE_VERBOSITY_DEBUG, "Too many lines in a subtitle\n"); - current->lines=i; - return current; - } - } - current->lines= ++i; - - return current; -} - -static subtitle_t *sub_read_line_subviewer(demux_sputext_t *this, subtitle_t *current) { - - char line[LINE_LEN + 1]; - int a1,a2,a3,a4,b1,b2,b3,b4; - char *p=NULL, *q=NULL; - int len; - - memset (current, 0, sizeof(subtitle_t)); - - while (1) { - if (!read_line_from_input(this, line, LINE_LEN)) return NULL; - if (sscanf (line, "%d:%d:%d.%d,%d:%d:%d.%d",&a1,&a2,&a3,&a4,&b1,&b2,&b3,&b4) < 8) { - if (sscanf (line, "%d:%d:%d,%d,%d:%d:%d,%d",&a1,&a2,&a3,&a4,&b1,&b2,&b3,&b4) < 8) - continue; - } - current->start = a1*360000+a2*6000+a3*100+a4; - current->end = b1*360000+b2*6000+b3*100+b4; - - if (!read_line_from_input(this, line, LINE_LEN)) - return NULL; - - p=q=line; - for (current->lines=1; current->lines <= SUB_MAX_TEXT; current->lines++) { - for (q=p,len=0; *p && *p!='\r' && *p!='\n' && *p!='|' && strncasecmp(p,"[br]",4); p++,len++); - current->text[current->lines-1]=(char *)xine_xmalloc (len+1); - if (!current->text[current->lines-1]) return ERR; - strncpy (current->text[current->lines-1], q, len); - current->text[current->lines-1][len]='\0'; - if (!*p || *p=='\r' || *p=='\n') break; - if (*p=='[') while (*p++!=']'); - if (*p=='|') p++; - } - if (current->lines > SUB_MAX_TEXT) current->lines = SUB_MAX_TEXT; - break; - } - return current; -} - -static subtitle_t *sub_read_line_subrip(demux_sputext_t *this,subtitle_t *current) { - char line[LINE_LEN + 1]; - int a1,a2,a3,a4,b1,b2,b3,b4; - int i,end_sub; - - memset(current,0,sizeof(subtitle_t)); - do { - if(!read_line_from_input(this,line,LINE_LEN)) - return NULL; - i = sscanf(line,"%d:%d:%d%*[,.]%d --> %d:%d:%d%*[,.]%d",&a1,&a2,&a3,&a4,&b1,&b2,&b3,&b4); - } while(i < 8); - current->start = a1*360000+a2*6000+a3*100+a4/10; - current->end = b1*360000+b2*6000+b3*100+b4/10; - i=0; - end_sub=0; - do { - char *p; /* pointer to the curently read char */ - char temp_line[SUB_BUFSIZE]; /* subtitle line that will be transfered to current->text[i] */ - int temp_index; /* ... and its index wich 'points' to the first EMPTY place -> last read char is at temp_index-1 if temp_index>0 */ - temp_line[SUB_BUFSIZE-1]='\0'; /* just in case... */ - if(!read_line_from_input(this,line,LINE_LEN)) { - if(i) - break; /* if something was read, transmit it */ - else - return NULL; /* if not, repport EOF */ - } - for(temp_index=0,p=line;*p!='\0' && !end_sub && temp_index0) { - if(temp_index==SUB_BUFSIZE) - xprintf (this->stream->xine, XINE_VERBOSITY_DEBUG, "Too many characters in a subtitle line\n"); - if(temp_line[temp_index-1]=='\0' || temp_index==SUB_BUFSIZE) { - if(temp_index>1) { /* more than 1 char (including '\0') -> that is a valid one */ - current->text[i]=(char *)xine_xmalloc(temp_index); - if(!current->text[i]) - return ERR; - strncpy(current->text[i],temp_line,temp_index); /* temp_index<=SUB_BUFSIZE is always true here */ - i++; - temp_index=0; - } else - end_sub=1; - } - } - } - } while(i=SUB_MAX_TEXT) - xprintf (this->stream->xine, XINE_VERBOSITY_DEBUG, "Too many lines in a subtitle\n"); - current->lines=i; - return current; -} - -static subtitle_t *sub_read_line_vplayer(demux_sputext_t *this,subtitle_t *current) { - char line[LINE_LEN + 1]; - int a1,a2,a3,b1,b2,b3; - char *p=NULL, *next, *p2; - int i; - - memset (current, 0, sizeof(subtitle_t)); - - while (!current->text[0]) { - if( this->next_line[0] == '\0' ) { /* if the buffer is empty.... */ - if( !read_line_from_input(this, line, LINE_LEN) ) return NULL; - } else { - /* ... get the current line from buffer. */ - strncpy( line, this->next_line, LINE_LEN); - line[LINE_LEN] = '\0'; /* I'm scared. This makes me feel better. */ - this->next_line[0] = '\0'; /* mark the buffer as empty. */ - } - /* Initialize buffer with next line */ - if( ! read_line_from_input( this, this->next_line, LINE_LEN) ) { - this->next_line[0] = '\0'; - return NULL; - } - if( (sscanf( line, "%d:%d:%d:", &a1, &a2, &a3) < 3) || - (sscanf( this->next_line, "%d:%d:%d:", &b1, &b2, &b3) < 3) ) - continue; - current->start = a1*360000+a2*6000+a3*100; - current->end = b1*360000+b2*6000+b3*100; - if ((current->end - current->start) > LINE_LEN) - current->end = current->start + LINE_LEN; /* not too long though. */ - /* teraz czas na wkopiowanie stringu */ - p=line; - /* finds the body of the subtitle_t */ - for (i=0; i<3; i++){ - p2=strchr( p, ':'); - if( p2 == NULL ) break; - p=p2+1; - } - - next=p; - i=0; - while( (next = sub_readtext( next, &(current->text[i]))) ) { - if (current->text[i]==ERR) - return ERR; - i++; - if (i>=SUB_MAX_TEXT) { - xprintf (this->stream->xine, XINE_VERBOSITY_DEBUG, "Too many lines in a subtitle\n"); - current->lines=i; - return current; - } - } - current->lines=++i; - } - return current; -} - -static subtitle_t *sub_read_line_rt(demux_sputext_t *this,subtitle_t *current) { - /* - * TODO: This format uses quite rich (sub/super)set of xhtml - * I couldn't check it since DTD is not included. - * WARNING: full XML parses can be required for proper parsing - */ - char line[LINE_LEN + 1]; - int a1,a2,a3,a4,b1,b2,b3,b4; - char *p=NULL,*next=NULL; - int i,len,plen; - - memset (current, 0, sizeof(subtitle_t)); - - while (!current->text[0]) { - if (!read_line_from_input(this, line, LINE_LEN)) return NULL; - /* - * TODO: it seems that format of time is not easily determined, it may be 1:12, 1:12.0 or 0:1:12.0 - * to describe the same moment in time. Maybe there are even more formats in use. - */ - if ((len=sscanf (line, "
in it */ + xml_node_t *clip_node; + + /* iterate through each tag contained in the tag to look for */ + + for (clip_node = packet_xml_root->child; clip_node != NULL; clip_node = clip_node->next) { + + if (strcasecmp (clip_node->name, "a") == 0) { + xml_property_t *href_property; + + /* found the tag: grab its value and its href property */ + + if (clip_node->data) + anchor_text = strdup (clip_node->data); + + for (href_property = clip_node->props; href_property != NULL; href_property = href_property->next) { + if (strcasecmp (href_property->name, "href") == 0) { + /* found the href property */ + char *href = href_property->value; + + if (href) { + lprintf ("found href: \"%s\"\n", href); + this->current_anchor.href = strdup(href); + } + } + } + } + } + } + + /* finish here if we don't have to process any anchor text */ + if (!anchor_text) + return; + + /* how many lines does the anchor text take up? */ + this->lines=0; + { + int i = 0; + while (*anchor_text) { + if (*anchor_text == '\r' || *anchor_text == '\n') { + if (i) { + /* match a newline and there are chars on the current line ... */ + this->text[ this->lines ][i] = '\0'; + this->lines++; + i = 0; + } + } else { + /* found a normal (non-line-ending) character */ + this->text[ this->lines ][i] = *anchor_text; + if (itext[ this->lines ][i] = '\0'; + this->lines++; + } + } + + /* initialize decoder if needed */ + if( !this->cached_width || !this->cached_height || !this->cached_img_duration || !this->osd ) { + if( this->stream->video_out->status(this->stream->video_out, NULL, + &this->cached_width, &this->cached_height, &this->cached_img_duration )) { + if( this->cached_width && this->cached_height && this->cached_img_duration ) { + lprintf("this->stream->osd_renderer is %p\n", this->stream->osd_renderer); + } + } + } + + update_font_size (this); + + if( this->osd ) { + draw_subtitle(this, buf->pts); + return; + } else { + lprintf ("libspucmml: no osd\n"); + } + + return; +} + +static void video_frame_format_change_callback (void *user_data, const xine_event_t *event) +{ + /* this doesn't do anything for now: it's a start at attempting to display + * CMML clips which occur at 0 seconds into the track. see + * + * http://marc.theaimsgroup.com/?l=xine-devel&m=109202443013890&w=2 + * + * for a description of the problem. */ + + switch (event->type) { + case XINE_EVENT_FRAME_FORMAT_CHANGE: + lprintf("video_frame_format_change_callback called!\n"); + break; + default: + lprintf("video_frame_format_change_callback called with unknown event %d\n", event->type); + break; + } +} + +static void spudec_reset (spu_decoder_t *this_gen) { + spucmml_decoder_t *this = (spucmml_decoder_t *) this_gen; + + this->cached_width = this->cached_height = 0; +} + +static void spudec_discontinuity (spu_decoder_t *this_gen) { + /* do nothing */ +} + +static void spudec_dispose (spu_decoder_t *this_gen) { + spucmml_decoder_t *this = (spucmml_decoder_t *) this_gen; + + if (this->event_queue) + xine_event_dispose_queue (this->event_queue); + + if (this->osd) { + this->stream->osd_renderer->free_object (this->osd); + this->osd = NULL; + } + free(this); +} + +static void update_vertical_offset(void *this_gen, xine_cfg_entry_t *entry) +{ + spucmml_decoder_t *this = (spucmml_decoder_t *)this_gen; + + this->vertical_offset = entry->num_value; + update_font_size(this); +} + +static void update_osd_font(void *this_gen, xine_cfg_entry_t *entry) +{ + spucmml_decoder_t *this = (spucmml_decoder_t *)this_gen; + + this->font = entry->str_value; + + if( this->stream->osd_renderer ) + this->stream->osd_renderer->set_font (this->osd, this->font, this->font_size); +} + +static spu_decoder_t *spucmml_class_open_plugin (spu_decoder_class_t *class_gen, xine_stream_t *stream) { + + spucmml_class_t *class = (spucmml_class_t *)class_gen; + spucmml_decoder_t *this ; + + this = (spucmml_decoder_t *) xine_xmalloc (sizeof (spucmml_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->spu_decoder.dispose = spudec_dispose; + + this->class = class; + this->stream = stream; + + this->event_queue = xine_event_new_queue (this->stream); + xine_event_create_listener_thread (this->event_queue, + video_frame_format_change_callback, + this); + + this->font_size = 24; + this->subtitle_size = 1; + + this->font = class->xine->config->register_string(class->xine->config, + "subtitles.separate.font", + "sans", + _("font for external subtitles"), + NULL, 0, update_osd_font, this); + + this->vertical_offset = class->xine->config->register_num(class->xine->config, + "subtitles.separate.vertical_offset", + 0, + _("subtitle vertical offset (relative window size)"), + NULL, 0, update_vertical_offset, this); + + this->current_anchor.href = NULL; + + lprintf ("video_out is at %p\n", this->stream->video_out); + + return (spu_decoder_t *) this; +} + +static void update_src_encoding(void *this_gen, xine_cfg_entry_t *entry) +{ + spucmml_class_t *this = (spucmml_class_t *)this_gen; + + this->src_encoding = entry->str_value; + printf("libspucmml: spu_src_encoding = %s\n", this->src_encoding ); +} + +static void *init_spu_decoder_plugin (xine_t *xine, void *data) { + + spucmml_class_t *this ; + + this = (spucmml_class_t *) xine_xmalloc (sizeof (spucmml_class_t)); + + this->class.open_plugin = spucmml_class_open_plugin; + this->class.identifier = "spucmml"; + this->class.description = N_("CMML subtitle decoder plugin"); + this->class.dispose = default_spu_decoder_class_dispose; + + this->xine = xine; + + this->src_encoding = xine->config->register_string(xine->config, + "subtitles.separate.src_encoding", + "iso-8859-1", + _("encoding of subtitles"), + NULL, 10, update_src_encoding, this); + + return &this->class; +} + + +/* plugin catalog information */ +static uint32_t supported_types[] = { BUF_SPU_CMML, 0 }; + +static const decoder_info_t spudec_info = { + supported_types, /* supported types */ + 1 /* priority */ +}; + +const plugin_info_t xine_plugin_info[] EXPORTED = { + /* type, API, "name", version, special_info, init_function */ + { PLUGIN_SPU_DECODER, 17, "spucmml", XINE_VERSION_CODE, &spudec_info, &init_spu_decoder_plugin }, + { PLUGIN_NONE, 0, "", 0, NULL, NULL } +}; + diff --git a/src/spu_dec/nav_read.c b/src/spu_dec/nav_read.c new file mode 100644 index 000000000..5244bfdd6 --- /dev/null +++ b/src/spu_dec/nav_read.c @@ -0,0 +1 @@ +#include "../input/libdvdnav/nav_read.c" diff --git a/src/spu_dec/spu_decoder.c b/src/spu_dec/spu_decoder.c new file mode 100644 index 000000000..e36b39fc8 --- /dev/null +++ b/src/spu_dec/spu_decoder.c @@ -0,0 +1,381 @@ +/* + * Copyright (C) 2000-2004 the xine project + * + * Copyright (C) James Courtier-Dutton James@superbug.demon.co.uk - July 2001 + * + * 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 + * + * stuff needed to turn libspu into a xine decoder plugin + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include "xine-engine/bswap.h" +#include +#ifdef HAVE_DVDNAV +# include +# include +#else +# include "nav_read.h" +# include "nav_types.h" +#endif + +#include "spudec.h" + +/* +#define LOG_DEBUG 1 +#define LOG_BUTTON 1 +*/ + +static const clut_t default_clut[] = { + CLUT_Y_CR_CB_INIT(0x00, 0x80, 0x80), + CLUT_Y_CR_CB_INIT(0xbf, 0x80, 0x80), + CLUT_Y_CR_CB_INIT(0x10, 0x80, 0x80), + CLUT_Y_CR_CB_INIT(0x28, 0x6d, 0xef), + CLUT_Y_CR_CB_INIT(0x51, 0xef, 0x5a), + CLUT_Y_CR_CB_INIT(0xbf, 0x80, 0x80), + CLUT_Y_CR_CB_INIT(0x36, 0x80, 0x80), + CLUT_Y_CR_CB_INIT(0x28, 0x6d, 0xef), + CLUT_Y_CR_CB_INIT(0xbf, 0x80, 0x80), + CLUT_Y_CR_CB_INIT(0x51, 0x80, 0x80), + CLUT_Y_CR_CB_INIT(0xbf, 0x80, 0x80), + CLUT_Y_CR_CB_INIT(0x10, 0x80, 0x80), + CLUT_Y_CR_CB_INIT(0x28, 0x6d, 0xef), + CLUT_Y_CR_CB_INIT(0x5c, 0x80, 0x80), + CLUT_Y_CR_CB_INIT(0xbf, 0x80, 0x80), + CLUT_Y_CR_CB_INIT(0x1c, 0x80, 0x80), + CLUT_Y_CR_CB_INIT(0x28, 0x6d, 0xef) +}; + +static void spudec_decode_data (spu_decoder_t *this_gen, buf_element_t *buf) { + uint32_t stream_id; + spudec_seq_t *cur_seq; + spudec_decoder_t *this = (spudec_decoder_t *) this_gen; + stream_id = buf->type & 0x1f ; + cur_seq = &this->spudec_stream_state[stream_id].ra_seq; + +#ifdef LOG_DEBUG + printf("libspudec:got buffer type = %x\n", buf->type); +#endif + + /* check, if we need to process the next PCI from the list */ + pthread_mutex_lock(&this->nav_pci_lock); + spudec_update_nav(this); + pthread_mutex_unlock(&this->nav_pci_lock); + + if ( (buf->type & 0xffff0000) != BUF_SPU_DVD || + !(buf->decoder_flags & BUF_FLAG_SPECIAL) || + buf->decoder_info[1] != BUF_SPECIAL_SPU_DVD_SUBTYPE ) + return; + + if ( buf->decoder_info[2] == SPU_DVD_SUBTYPE_CLUT ) { +#ifdef LOG_DEBUG + printf("libspudec: SPU CLUT\n"); +#endif + if (buf->content[0]) { /* cheap endianess detection */ + xine_fast_memcpy(this->state.clut, buf->content, sizeof(uint32_t)*16); + } else { + int i; + uint32_t *clut = (uint32_t*) buf->content; + for (i = 0; i < 16; i++) + this->state.clut[i] = bswap_32(clut[i]); + } + this->state.need_clut = 0; + return; + } + + if ( buf->decoder_info[2] == SPU_DVD_SUBTYPE_NAV ) { +#ifdef LOG_DEBUG + printf("libspudec:got nav packet 1\n"); +#endif + spudec_decode_nav(this,buf); + return; + } + + if ( buf->decoder_info[2] == SPU_DVD_SUBTYPE_VOBSUB_PACKAGE ) { + this->state.vobsub = 1; + } + +#ifdef LOG_DEBUG + printf("libspudec:got buffer type = %x\n", buf->type); +#endif + if (buf->decoder_flags & BUF_FLAG_PREVIEW) /* skip preview data */ + return; + + if (buf->pts) { + metronom_t *metronom = this->stream->metronom; + int64_t vpts = metronom->got_spu_packet(metronom, buf->pts); + + this->spudec_stream_state[stream_id].vpts = vpts; /* Show timer */ + this->spudec_stream_state[stream_id].pts = buf->pts; /* Required to match up with NAV packets */ + } + + spudec_reassembly(this->stream->xine, + &this->spudec_stream_state[stream_id].ra_seq, buf->content, buf->size); + if(this->spudec_stream_state[stream_id].ra_seq.complete == 1) { + if(this->spudec_stream_state[stream_id].ra_seq.broken) { + xprintf(this->stream->xine, XINE_VERBOSITY_DEBUG, "libspudec: dropping broken SPU\n"); + this->spudec_stream_state[stream_id].ra_seq.broken = 0; + } else + spudec_process(this,stream_id); + } +} + +static void spudec_reset (spu_decoder_t *this_gen) { + spudec_decoder_t *this = (spudec_decoder_t *) this_gen; + video_overlay_manager_t *ovl_manager = this->stream->video_out->get_overlay_manager (this->stream->video_out); + int i; + + if( this->menu_handle >= 0 ) + ovl_manager->free_handle(ovl_manager, + this->menu_handle); + this->menu_handle = -1; + + for (i=0; i < MAX_STREAMS; i++) { + if( this->spudec_stream_state[i].overlay_handle >= 0 ) + ovl_manager->free_handle(ovl_manager, + this->spudec_stream_state[i].overlay_handle); + this->spudec_stream_state[i].overlay_handle = -1; + this->spudec_stream_state[i].ra_seq.complete = 1; + this->spudec_stream_state[i].ra_seq.broken = 0; + } + + pthread_mutex_lock(&this->nav_pci_lock); + spudec_clear_nav_list(this); + pthread_mutex_unlock(&this->nav_pci_lock); +} + +static void spudec_discontinuity (spu_decoder_t *this_gen) { + spudec_decoder_t *this = (spudec_decoder_t *) this_gen; + + pthread_mutex_lock(&this->nav_pci_lock); + spudec_clear_nav_list(this); + pthread_mutex_unlock(&this->nav_pci_lock); +} + + +static void spudec_dispose (spu_decoder_t *this_gen) { + + spudec_decoder_t *this = (spudec_decoder_t *) this_gen; + int i; + video_overlay_manager_t *ovl_manager = this->stream->video_out->get_overlay_manager (this->stream->video_out); + + if( this->menu_handle >= 0 ) + ovl_manager->free_handle(ovl_manager, + this->menu_handle); + this->menu_handle = -1; + + for (i=0; i < MAX_STREAMS; i++) { + if( this->spudec_stream_state[i].overlay_handle >= 0 ) + ovl_manager->free_handle(ovl_manager, + this->spudec_stream_state[i].overlay_handle); + this->spudec_stream_state[i].overlay_handle = -1; + free (this->spudec_stream_state[i].ra_seq.buf); + } + + spudec_clear_nav_list(this); + pthread_mutex_destroy(&this->nav_pci_lock); + + free (this->event.object.overlay); + free (this); +} + +/* gets the current already correctly processed nav_pci info */ +/* This is not perfectly in sync with the display, but all the same, */ +/* much closer than doing it at the input stage. */ +/* returns a bool for error/success.*/ +static int spudec_get_interact_info (spu_decoder_t *this_gen, void *data) { + spudec_decoder_t *this = (spudec_decoder_t *) this_gen; + /*printf("get_interact_info() called\n");*/ + if (!this || !data) + return 0; + + /*printf("get_interact_info() coping nav_pci\n");*/ + pthread_mutex_lock(&this->nav_pci_lock); + spudec_update_nav(this); + memcpy(data, &this->pci_cur.pci, sizeof(pci_t) ); + pthread_mutex_unlock(&this->nav_pci_lock); + return 1; + +} + +static void spudec_set_button (spu_decoder_t *this_gen, int32_t button, int32_t show) { + spudec_decoder_t *this = (spudec_decoder_t *) this_gen; + /* This function will move to video_overlay + * when video_overlay does menus */ + + video_overlay_manager_t *ovl_manager; + video_overlay_event_t *overlay_event = NULL; + vo_overlay_t *overlay = NULL; + overlay_event = xine_xmalloc (sizeof(video_overlay_event_t)); + + overlay = xine_xmalloc (sizeof(vo_overlay_t)); + /* FIXME: Watch out for threads. We should really put a lock on this + * because events is a different thread than decode_data */ + + if( this->menu_handle < 0 ) { + if (this->stream->video_out) { + ovl_manager = this->stream->video_out->get_overlay_manager (this->stream->video_out); + this->menu_handle = ovl_manager->get_handle(ovl_manager,1); + } + } +#ifdef LOG_BUTTON + printf ("libspudec:xine_decoder.c:spudec_event_listener:this=%p\n",this); + printf ("libspudec:xine_decoder.c:spudec_event_listener:this->menu_handle=%d\n",this->menu_handle); +#endif + if(this->menu_handle < 0) { + xprintf(this->stream->xine, XINE_VERBOSITY_DEBUG, + "Menu handle alloc failed. No more overlays objects available. Only %d at once please.", + MAX_OBJECTS); + free(overlay_event); + free(overlay); + return; + } + + if (show > 0) { +#ifdef LOG_NAV + fprintf (stderr,"libspudec:xine_decoder.c:spudec_event_listener:buttonN = %u show=%d\n", + button, + show); +#endif + this->buttonN = button; + if (this->button_filter != 1) { +#ifdef LOG_BUTTON + fprintf (stdout,"libspudec:xine_decoder.c:spudec_event_listener:buttonN updates not allowed\n"); +#endif + /* Only update highlight is the menu will let us */ + free(overlay_event); + free(overlay); + return; + } + if (show == 2) { + this->button_filter = 2; + } + pthread_mutex_lock(&this->nav_pci_lock); + spudec_update_nav(this); + overlay_event->object.handle = this->menu_handle; + overlay_event->object.pts = this->pci_cur.pci.hli.hl_gi.hli_s_ptm; + overlay_event->object.overlay=overlay; + overlay_event->event_type = OVERLAY_EVENT_MENU_BUTTON; +#ifdef LOG_BUTTON + fprintf(stderr, "libspudec:Button Overlay\n"); +#endif + spudec_copy_nav_to_overlay(this->stream->xine, &this->pci_cur.pci, this->state.clut, + this->buttonN, show-1, overlay, &this->overlay ); + pthread_mutex_unlock(&this->nav_pci_lock); + } else { + xprintf(this->stream->xine, XINE_VERBOSITY_DEBUG, + "libspudec:xine_decoder.c:spudec_event_listener:HIDE ????\n"); + printf("We dropped out here for some reason"); + _x_abort(); + overlay_event->object.handle = this->menu_handle; + overlay_event->event_type = OVERLAY_EVENT_HIDE; + } + overlay_event->vpts = 0; + if (this->stream->video_out) { + ovl_manager = this->stream->video_out->get_overlay_manager (this->stream->video_out); +#ifdef LOG_BUTTON + fprintf(stderr, "libspudec: add_event type=%d : current time=%lld, spu vpts=%lli\n", + overlay_event->event_type, + this->stream->xine->clock->get_current_time(this->stream->xine->clock), + overlay_event->vpts); +#endif + ovl_manager->add_event (ovl_manager, (void *)overlay_event); + free(overlay_event); + free(overlay); + } else { + free(overlay_event); + free(overlay); + } + return; +} + +static spu_decoder_t *open_plugin (spu_decoder_class_t *class_gen, xine_stream_t *stream) { + + spudec_decoder_t *this ; + int i; + + this = (spudec_decoder_t *) xine_xmalloc (sizeof (spudec_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 = spudec_get_interact_info; + this->spu_decoder.set_button = spudec_set_button; + this->stream = stream; + this->class = (spudec_class_t *) class_gen; + + this->menu_handle = -1; + this->buttonN = 1; + this->event.object.overlay = xine_xmalloc(sizeof(vo_overlay_t)); + + pthread_mutex_init(&this->nav_pci_lock, NULL); + this->pci_cur.pci.hli.hl_gi.hli_ss = 0; + this->pci_cur.next = NULL; + + this->ovl_caps = stream->video_out->get_capabilities(stream->video_out); + this->output_open = 0; + this->last_event_vpts = 0; + for (i=0; i < MAX_STREAMS; i++) { + this->spudec_stream_state[i].ra_seq.complete = 1; + this->spudec_stream_state[i].overlay_handle = -1; + } + +/* FIXME:Do we really need a default clut? */ + xine_fast_memcpy(this->state.clut, default_clut, sizeof(this->state.clut)); + this->state.need_clut = 1; + this->state.vobsub = 0; + + return &this->spu_decoder; +} + +static void *init_plugin (xine_t *xine, void *data) { + + spudec_class_t *this; + + this = (spudec_class_t *) xine_xmalloc (sizeof (spudec_class_t)); + + this->decoder_class.open_plugin = open_plugin; + this->decoder_class.identifier = "spudec"; + this->decoder_class.description = N_("DVD/VOB SPU decoder plugin"); + this->decoder_class.dispose = default_spu_decoder_class_dispose; + + lprintf ("libspudec:init_plugin called\n"); + return this; +} + +/* plugin catalog information */ +static uint32_t supported_types[] = { BUF_SPU_DVD, 0 }; + +static const decoder_info_t dec_info_data = { + supported_types, /* supported types */ + 5 /* priority */ +}; + +const plugin_info_t xine_plugin_info[] EXPORTED = { + /* type, API, "name", version, special_info, init_function */ + { PLUGIN_SPU_DECODER, 17, "spudec", XINE_VERSION_CODE, &dec_info_data, &init_plugin }, + { PLUGIN_NONE, 0, "", 0, NULL, NULL } +}; diff --git a/src/spu_dec/spudec.c b/src/spu_dec/spudec.c new file mode 100644 index 000000000..13136a53f --- /dev/null +++ b/src/spu_dec/spudec.c @@ -0,0 +1,1026 @@ +/* + * Copyright (C) 2002-2004 the xine project + * + * Copyright (C) James Courtier-Dutton James@superbug.demon.co.uk - July 2001 + * + * spu.c - converts DVD subtitles to an XPM image + * + * Mostly based on hard work by: + * + * Copyright (C) 2000 Samuel Hocevar + * and Michel Lespinasse + * + * Lots of rearranging by: + * Aaron Holtzman + * Thomas Mirlacher + * implemented reassembling + * cleaner implementation of SPU are saving + * overlaying (proof of concept for now) + * ... and yes, it works now with oms + * added tranparency (provided by the SPU hdr) + * changed structures for easy porting to MGAs DVD mode + * This file is part of xine + * This file was originally part of the OMS program. + * + * This program 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, or (at your option) + * any later version. + * + * This program 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; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include "xine-engine/bswap.h" +#ifdef HAVE_DVDNAV +# include +# include +#else +# include "nav_read.h" +# include "nav_print.h" +#endif + +#include "spudec.h" + +/* +#define LOG_DEBUG 1 +#define LOG_BUTTON 1 +#define LOG_NAV 1 +*/ + +static void spudec_do_commands (xine_t *xine, spudec_state_t *state, spudec_seq_t* seq, vo_overlay_t *ovl); +static void spudec_draw_picture (xine_t *xine, spudec_state_t *state, spudec_seq_t* seq, vo_overlay_t *ovl); +static void spudec_discover_clut (xine_t *xine, spudec_state_t *state, vo_overlay_t *ovl); +#ifdef LOG_DEBUG +static void spudec_print_overlay( vo_overlay_t *overlay ); +#endif + +void spudec_decode_nav(spudec_decoder_t *this, buf_element_t *buf) { + uint8_t *p; + uint32_t packet_len; + uint32_t stream_id; + uint32_t header_len; + pci_t pci; + dsi_t dsi; + video_overlay_manager_t *ovl_manager = this->stream->video_out->get_overlay_manager (this->stream->video_out); + + p = buf->content; + if (p[0] || p[1] || (p[2] != 1)) { + xprintf(this->stream->xine, XINE_VERBOSITY_DEBUG, + "libspudec:spudec_decode_nav:nav demux error! %02x %02x %02x (should be 0x000001) \n",p[0],p[1],p[2]); + return; + } + + packet_len = p[4] << 8 | p[5]; + stream_id = p[3]; + + header_len = 6; + p += header_len; + + if (stream_id == 0xbf) { /* Private stream 2 */ +/* int i; + * for(i=0;i<80;i++) { + * printf("%02x ",p[i]); + * } + * printf("\n p[0]=0x%02x\n",p[0]); + */ + if(p[0] == 0x00) { +#ifdef LOG_NAV + printf("libspudec:nav_PCI\n"); +#endif + navRead_PCI(&pci, p+1); +#ifdef LOG_NAV + printf("libspudec:nav:hli_ss=%u, hli_s_ptm=%u, hli_e_ptm=%u, btn_sl_e_ptm=%u pts=%lli\n", + pci.hli.hl_gi.hli_ss, + pci.hli.hl_gi.hli_s_ptm, + pci.hli.hl_gi.hli_e_ptm, + pci.hli.hl_gi.btn_se_e_ptm, + buf->pts); + printf("libspudec:nav:btn_sn/ofn=%u, btn_ns=%u, fosl_btnn=%u, foac_btnn=%u\n", + pci.hli.hl_gi.btn_ofn, pci.hli.hl_gi.btn_ns, + pci.hli.hl_gi.fosl_btnn, pci.hli.hl_gi.foac_btnn); + printf("btngr_ns %d\n", pci.hli.hl_gi.btngr_ns); + printf("btngr%d_dsp_ty 0x%02x\n", 1, pci.hli.hl_gi.btngr1_dsp_ty); + printf("btngr%d_dsp_ty 0x%02x\n", 2, pci.hli.hl_gi.btngr2_dsp_ty); + printf("btngr%d_dsp_ty 0x%02x\n", 3, pci.hli.hl_gi.btngr3_dsp_ty); + //navPrint_PCI(&pci); + //navPrint_PCI_GI(&pci.pci_gi); + //navPrint_NSML_AGLI(&pci.nsml_agli); + //navPrint_HLI(&pci.hli); + //navPrint_HL_GI(&pci.hli.hl_gi, & btngr_ns, & btn_ns); +#endif + } + + p += packet_len; + + /* We should now have a DSI packet. */ + /* We don't need anything from the DSI packet here. */ + if(p[6] == 0x01) { + packet_len = p[4] << 8 | p[5]; + p += 6; +#ifdef LOG_NAV + printf("NAV DSI packet\n"); +#endif + navRead_DSI(&dsi, p+1); + +// self->vobu_start = self->dsi.dsi_gi.nv_pck_lbn; +// self->vobu_length = self->dsi.dsi_gi.vobu_ea; + } + } + + /* NAV packets contain start and end presentation timestamps, which tell the + * application, when the highlight information in the NAV is supposed to be valid. + * We handle these timestamps only in a very stripped-down way: We keep a list + * of NAV packets (or better: the PCI part of them), tagged with a VPTS timestamp + * telling, when the NAV should be processed. However, we only enqueue a new node + * into this list, when we receive new highlight information during an already + * showing menu. This happens very rarerly on common DVDs, so it is of low impact. + * And we only check for processing of queued entries at some prominent + * locations in this SPU decoder. Since presentation timestamps rarely solve a real + * purpose on most DVDs, this is ok compared to the full-blown solution, which would + * require a separate thread managing the queue all the time. */ + pthread_mutex_lock(&this->nav_pci_lock); + switch (pci.hli.hl_gi.hli_ss) { + case 0: + /* No Highlight information for this VOBU */ + if ( this->pci_cur.pci.hli.hl_gi.hli_ss == 1) { + /* Hide menu spu between menus */ +#ifdef LOG_BUTTON + printf("libspudec:nav:SHOULD HIDE SPU here\n"); +#endif + if( this->menu_handle < 0 ) { + this->menu_handle = ovl_manager->get_handle(ovl_manager,1); + } + if( this->menu_handle >= 0 ) { + this->event.object.handle = this->menu_handle; + this->event.event_type = OVERLAY_EVENT_HIDE; + /* hide menu right now */ + this->event.vpts = 0; + ovl_manager->add_event(ovl_manager, (void *)&this->event); + } else { + xprintf(this->stream->xine, XINE_VERBOSITY_DEBUG, "libspudec: No video_overlay handles left for menu\n"); + } + } + spudec_clear_nav_list(this); + xine_fast_memcpy(&this->pci_cur.pci, &pci, sizeof(pci_t)); + /* incoming SPUs will be plain subtitles */ + this->event.object.object_type = 0; + if (this->button_filter) { + /* we possibly had buttons before, so we update the UI info */ + xine_event_t event; + xine_ui_data_t data; + + event.type = XINE_EVENT_UI_NUM_BUTTONS; + event.data = &data; + event.data_length = sizeof(data); + data.num_buttons = 0; + + xine_event_send(this->stream, &event); + } + this->button_filter=0; + + break; + case 1: + /* All New Highlight information for this VOBU */ + if (this->pci_cur.pci.hli.hl_gi.hli_ss != 0 && + pci.hli.hl_gi.hli_s_ptm > this->pci_cur.pci.hli.hl_gi.hli_s_ptm) { + pci_node_t *node = &this->pci_cur; +#ifdef LOG_DEBUG + printf("libspudec: allocating new PCI node for hli_s_ptm %d\n", pci.hli.hl_gi.hli_s_ptm); +#endif + /* append PCI at the end of the list */ + while (node->next) node = node->next; + node->next = (pci_node_t *)xine_xmalloc(sizeof(pci_node_t)); + node->next->vpts = this->stream->metronom->got_spu_packet(this->stream->metronom, pci.hli.hl_gi.hli_s_ptm); + node->next->next = NULL; + xine_fast_memcpy(&node->next->pci, &pci, sizeof(pci_t)); + } else { + spudec_clear_nav_list(this); + /* menu ahead, remember PCI for later use */ + xine_fast_memcpy(&this->pci_cur.pci, &pci, sizeof(pci_t)); + spudec_process_nav(this); + } + break; + case 2: + /* Use Highlight information from previous VOBU */ + if (this->pci_cur.next) { + /* apply changes to last enqueued NAV */ + pci_node_t *node = this->pci_cur.next; + while (node->next) node = node->next; + node->pci.pci_gi.vobu_s_ptm = pci.pci_gi.vobu_s_ptm; + node->pci.pci_gi.vobu_e_ptm = pci.pci_gi.vobu_e_ptm; + node->pci.pci_gi.vobu_se_e_ptm = pci.pci_gi.vobu_se_e_ptm; + spudec_update_nav(this); + } else { + this->pci_cur.pci.pci_gi.vobu_s_ptm = pci.pci_gi.vobu_s_ptm; + this->pci_cur.pci.pci_gi.vobu_e_ptm = pci.pci_gi.vobu_e_ptm; + this->pci_cur.pci.pci_gi.vobu_se_e_ptm = pci.pci_gi.vobu_se_e_ptm; + } + break; + case 3: + /* Use Highlight information from previous VOBU except commands, which come from this VOBU */ + if (this->pci_cur.next) { + /* apply changes to last enqueued NAV */ + pci_node_t *node = this->pci_cur.next; + while (node->next) node = node->next; + node->pci.pci_gi.vobu_s_ptm = pci.pci_gi.vobu_s_ptm; + node->pci.pci_gi.vobu_e_ptm = pci.pci_gi.vobu_e_ptm; + node->pci.pci_gi.vobu_se_e_ptm = pci.pci_gi.vobu_se_e_ptm; + /* FIXME: Add command copying here */ + spudec_update_nav(this); + } else { + this->pci_cur.pci.pci_gi.vobu_s_ptm = pci.pci_gi.vobu_s_ptm; + this->pci_cur.pci.pci_gi.vobu_e_ptm = pci.pci_gi.vobu_e_ptm; + this->pci_cur.pci.pci_gi.vobu_se_e_ptm = pci.pci_gi.vobu_se_e_ptm; + /* FIXME: Add command copying here */ + } + break; + default: + xprintf(this->stream->xine, XINE_VERBOSITY_DEBUG, + "libspudec: unknown pci.hli.hl_gi.hli_ss = %d\n", pci.hli.hl_gi.hli_ss ); + break; + } + pthread_mutex_unlock(&this->nav_pci_lock); + return; +} + +void spudec_clear_nav_list(spudec_decoder_t *this) +{ + while (this->pci_cur.next) { + pci_node_t *node = this->pci_cur.next->next; + free(this->pci_cur.next); + this->pci_cur.next = node; + } + /* invalidate current timestamp */ + this->pci_cur.pci.hli.hl_gi.hli_s_ptm = (uint32_t)-1; +} + +void spudec_update_nav(spudec_decoder_t *this) +{ + metronom_clock_t *clock = this->stream->xine->clock; + + if (this->pci_cur.next && this->pci_cur.next->vpts <= clock->get_current_time(clock)) { + pci_node_t *node = this->pci_cur.next; + xine_fast_memcpy(&this->pci_cur, this->pci_cur.next, sizeof(pci_node_t)); + spudec_process_nav(this); + free(node); + } +} + +void spudec_process_nav(spudec_decoder_t *this) +{ + /* incoming SPUs will be menus */ + this->event.object.object_type = 1; + if (!this->button_filter) { + /* we possibly entered a menu, so we update the UI button info */ + xine_event_t event; + xine_ui_data_t data; + + event.type = XINE_EVENT_UI_NUM_BUTTONS; + event.data = &data; + event.data_length = sizeof(data); + data.num_buttons = this->pci_cur.pci.hli.hl_gi.btn_ns; + + xine_event_send(this->stream, &event); + } + this->button_filter=1; +} + +void spudec_reassembly (xine_t *xine, spudec_seq_t *seq, uint8_t *pkt_data, u_int pkt_len) +{ +#ifdef LOG_DEBUG + printf ("libspudec: seq->complete = %d\n", seq->complete); + printf("libspudec:1: seq->ra_offs = %d, seq->seq_len = %d, seq->buf_len = %d, seq->buf=%p\n", + seq->ra_offs, + seq->seq_len, + seq->buf_len, + seq->buf); +#endif + if (seq->complete) { + seq->seq_len = (((uint32_t)pkt_data[0])<<8) | pkt_data[1]; + seq->cmd_offs = (((uint32_t)pkt_data[2])<<8) | pkt_data[3]; + if (seq->cmd_offs >= seq->seq_len) { + xprintf(xine, XINE_VERBOSITY_DEBUG, "libspudec:faulty stream\n"); + seq->broken = 1; + } + if (seq->buf_len < seq->seq_len) { + seq->buf_len = seq->seq_len; +#ifdef LOG_DEBUG + printf ("spu: MALLOC1: seq->buf %p, len=%d\n", seq->buf,seq->buf_len); +#endif + if (seq->buf) { + free(seq->buf); + seq->buf = NULL; + } + seq->buf = malloc(seq->buf_len); +#ifdef LOG_DEBUG + printf ("spu: MALLOC2: seq->buf %p, len=%d\n", seq->buf,seq->buf_len); +#endif + + } + seq->ra_offs = 0; + +#ifdef LOG_DEBUG + printf ("spu: buf_len: %d\n", seq->buf_len); + printf ("spu: cmd_off: %d\n", seq->cmd_offs); +#endif + } + +#ifdef LOG_DEBUG + printf("libspudec:2: seq->ra_offs = %d, seq->seq_len = %d, seq->buf_len = %d, seq->buf=%p\n", + seq->ra_offs, + seq->seq_len, + seq->buf_len, + seq->buf); +#endif + if (seq->ra_offs < seq->seq_len) { + if (seq->ra_offs + pkt_len > seq->seq_len) + pkt_len = seq->seq_len - seq->ra_offs; + memcpy (seq->buf + seq->ra_offs, pkt_data, pkt_len); + seq->ra_offs += pkt_len; + } else { + xprintf(xine, XINE_VERBOSITY_DEBUG, "libspudec:faulty stream\n"); + seq->broken = 1; + } + + if (seq->ra_offs == seq->seq_len) { + seq->finished = 0; + seq->complete = 1; + return; /* sequence ready */ + } + seq->complete = 0; + return; +} + +void spudec_process (spudec_decoder_t *this, int stream_id) { + spudec_seq_t *cur_seq; + video_overlay_manager_t *ovl_manager = this->stream->video_out->get_overlay_manager (this->stream->video_out); + int pending = 1; + cur_seq = &this->spudec_stream_state[stream_id].ra_seq; + +#ifdef LOG_DEBUG + printf ("spu: Found SPU from stream %d pts=%lli vpts=%lli\n",stream_id, + this->spudec_stream_state[stream_id].pts, + this->spudec_stream_state[stream_id].vpts); +#endif + this->state.cmd_ptr = cur_seq->buf + cur_seq->cmd_offs; + this->state.modified = 1; /* Only draw picture if = 1 on first event of SPU */ + this->state.visible = OVERLAY_EVENT_SHOW; + this->state.forced_display = 0; /* 0 - No value, 1 - Forced Display. */ + this->state.delay = 0; + cur_seq->finished=0; + + do { + if (!(cur_seq->finished) ) { + pci_node_t *node; + + /* spu_channel is now set based on whether we are in the menu or not. */ + /* Bit 7 is set if only forced display SPUs should be shown */ + if ( (this->stream->spu_channel & 0x1f) != stream_id ) { +#ifdef LOG_DEBUG + printf ("spu: Dropping SPU channel %d. Not selected stream_id\n", stream_id); +#endif + return; + } + /* parse SPU command sequence, this will update forced_display, so it must come + * before the check for it */ + spudec_do_commands(this->stream->xine, &this->state, cur_seq, &this->overlay); + /* FIXME: Check for Forced-display or subtitle stream + * For subtitles, open event. + * For menus, store it for later. + */ + if (cur_seq->broken) { + xprintf(this->stream->xine, XINE_VERBOSITY_DEBUG, "libspudec: dropping broken SPU\n"); + cur_seq->broken = 0; + return; + } + if ( (this->state.forced_display == 0) && (this->stream->spu_channel & 0x80) ) { +#ifdef LOG_DEBUG + printf ("spu: Dropping SPU channel %d. Only allow forced display SPUs\n", stream_id); +#endif + return; + } + +#ifdef LOG_DEBUG + spudec_print_overlay( &this->overlay ); + printf ("spu: forced display:%s\n", this->state.forced_display ? "Yes" : "No" ); +#endif + pthread_mutex_lock(&this->nav_pci_lock); + /* search for a PCI that matches this SPU's PTS */ + for (node = &this->pci_cur; node; node = node->next) + if (node->pci.hli.hl_gi.hli_s_ptm == this->spudec_stream_state[stream_id].pts) + break; + if (node) { + if (this->state.visible == OVERLAY_EVENT_HIDE) { + /* menus are hidden via nav packet decoding, not here */ + /* FIXME: James is not sure about this solution and may want to look this over. + * I'm commiting it, because I haven't found a disc it breaks, but it fixes + * some instead. Michael Roitzsch */ + pthread_mutex_unlock(&this->nav_pci_lock); + continue; + } + if (node->pci.hli.hl_gi.fosl_btnn > 0) { + xine_event_t event; + + this->buttonN = node->pci.hli.hl_gi.fosl_btnn; + event.type = XINE_EVENT_INPUT_BUTTON_FORCE; + event.stream = this->stream; + event.data = &this->buttonN; + event.data_length = sizeof(this->buttonN); + xine_event_send(this->stream, &event); + } +#ifdef LOG_BUTTON + fprintf(stderr, "libspudec:Full Overlay\n"); +#endif + if (!spudec_copy_nav_to_overlay(this->stream->xine, + &node->pci, this->state.clut, + this->buttonN, 0, &this->overlay, &this->overlay)) { + /* current button does not exist -> use another one */ + xine_event_t event; + + if (this->buttonN > node->pci.hli.hl_gi.btn_ns) + this->buttonN = node->pci.hli.hl_gi.btn_ns; + else + this->buttonN = 1; + event.type = XINE_EVENT_INPUT_BUTTON_FORCE; + event.stream = this->stream; + event.data = &this->buttonN; + event.data_length = sizeof(this->buttonN); + xine_event_send(this->stream, &event); + spudec_copy_nav_to_overlay(this->stream->xine, + &node->pci, this->state.clut, + this->buttonN, 0, &this->overlay, &this->overlay); + } + } else { + /* Subtitle and not a menu button */ + int i; + for (i = 0;i < 4; i++) { + this->overlay.hili_color[i] = this->overlay.color[i]; + this->overlay.hili_trans[i] = this->overlay.trans[i]; + } + } + pthread_mutex_unlock(&this->nav_pci_lock); + + if ((this->state.modified) ) { + spudec_draw_picture(this->stream->xine, &this->state, cur_seq, &this->overlay); + } + + if (this->state.need_clut) { + spudec_discover_clut(this->stream->xine, &this->state, &this->overlay); + } + + if (this->state.vobsub) { + int width, height; + int64_t duration; + + /* + * vobsubs are usually played with a scaled-down stream (not full DVD + * resolution), therefore we should try to realign it. + */ + + this->stream->video_out->status(this->stream->video_out, NULL, + &width, &height, &duration ); + + this->overlay.x = (width - this->overlay.width) / 2; + this->overlay.y = height - this->overlay.height; + } + + /* Subtitle */ + if( this->menu_handle < 0 ) { + this->menu_handle = ovl_manager->get_handle(ovl_manager,1); + } + + if( this->menu_handle < 0 ) { + xprintf(this->stream->xine, XINE_VERBOSITY_DEBUG, + "libspudec: No video_overlay handles left for menu\n"); + return; + } + this->event.object.handle = this->menu_handle; + this->event.object.pts = this->spudec_stream_state[stream_id].pts; + + xine_fast_memcpy(this->event.object.overlay, + &this->overlay, + sizeof(vo_overlay_t)); + this->overlay.rle=NULL; + /* For force display menus */ + //if ( !(this->state.visible) ) { + // this->state.visible = OVERLAY_EVENT_SHOW; + //} + + this->event.event_type = this->state.visible; + /* + printf("spu event %d handle: %d vpts: %lli\n", this->event.event_type, + this->event.object.handle, this->event.vpts ); + */ + + this->event.vpts = this->spudec_stream_state[stream_id].vpts+(this->state.delay*1000); + + /* Keep all the events in the correct order. */ + /* This corrects for errors during estimation around discontinuity */ + if( this->event.vpts < this->last_event_vpts ) { + this->event.vpts = this->last_event_vpts + 1; + } + this->last_event_vpts = this->event.vpts; + +#ifdef LOG_BUTTON + fprintf(stderr, "libspudec: add_event type=%d : current time=%lld, spu vpts=%lli\n", + this->event.event_type, + this->stream->xine->clock->get_current_time(this->stream->xine->clock), + this->event.vpts); +#endif + ovl_manager->add_event(ovl_manager, (void *)&this->event); + } else { + pending = 0; + } + } while (pending); + +} + +#define CMD_SPU_FORCE_DISPLAY 0x00 +#define CMD_SPU_SHOW 0x01 +#define CMD_SPU_HIDE 0x02 +#define CMD_SPU_SET_PALETTE 0x03 +#define CMD_SPU_SET_ALPHA 0x04 +#define CMD_SPU_SET_SIZE 0x05 +#define CMD_SPU_SET_PXD_OFFSET 0x06 +#define CMD_SPU_WIPE 0x07 /* Not currently implemented */ +#define CMD_SPU_EOF 0xff + +static void spudec_do_commands(xine_t *xine, spudec_state_t *state, spudec_seq_t* seq, vo_overlay_t *ovl) +{ + uint8_t *buf = state->cmd_ptr; + uint8_t *next_seq; + int32_t param_length; + +#ifdef LOG_DEBUG + printf ("spu: SPU DO COMMANDS\n"); +#endif + + state->delay = (buf[0] << 8) + buf[1]; +#ifdef LOG_DEBUG + printf ("spu: \tdelay=%d\n",state->delay); +#endif + next_seq = seq->buf + (buf[2] << 8) + buf[3]; + buf += 4; +#ifdef LOG_DEBUG + printf ("spu: \tnext_seq=%d\n",next_seq - seq->buf); +#endif + +/* if next equals current, this is the last one + */ + if (state->cmd_ptr >= next_seq) + next_seq = seq->buf + seq->seq_len; /* allow to run until end */ + + state->cmd_ptr = next_seq; + + while (buf < next_seq && *buf != CMD_SPU_EOF) { + switch (*buf) { + case CMD_SPU_SHOW: /* show subpicture */ +#ifdef LOG_DEBUG + printf ("spu: \tshow subpicture\n"); +#endif + state->visible = OVERLAY_EVENT_SHOW; + buf++; + break; + + case CMD_SPU_HIDE: /* hide subpicture */ +#ifdef LOG_DEBUG + printf ("spu: \thide subpicture\n"); +#endif + state->visible = OVERLAY_EVENT_HIDE; + buf++; + break; + + case CMD_SPU_SET_PALETTE: { /* CLUT */ + spudec_clut_t *clut = (spudec_clut_t *) (buf+1); + + state->cur_colors[3] = clut->entry0; + state->cur_colors[2] = clut->entry1; + state->cur_colors[1] = clut->entry2; + state->cur_colors[0] = clut->entry3; + +/* This is a bit out of context for now */ + ovl->color[3] = state->clut[clut->entry0]; + ovl->color[2] = state->clut[clut->entry1]; + ovl->color[1] = state->clut[clut->entry2]; + ovl->color[0] = state->clut[clut->entry3]; + +#ifdef LOG_DEBUG + printf ("spu: \tclut [%x %x %x %x]\n", + ovl->color[0], ovl->color[1], ovl->color[2], ovl->color[3]); + printf ("spu: \tclut base [%x %x %x %x]\n", + clut->entry0, clut->entry1, clut->entry2, clut->entry3); +#endif + state->modified = 1; + buf += 3; + break; + } + case CMD_SPU_SET_ALPHA: { /* transparency palette */ + spudec_clut_t *trans = (spudec_clut_t *) (buf+1); +/* This should go into state for now */ + + ovl->trans[3] = trans->entry0; + ovl->trans[2] = trans->entry1; + ovl->trans[1] = trans->entry2; + ovl->trans[0] = trans->entry3; + +#ifdef LOG_DEBUG + printf ("spu: \ttrans [%d %d %d %d]\n", + ovl->trans[0], ovl->trans[1], ovl->trans[2], ovl->trans[3]); +#endif + state->modified = 1; + buf += 3; + break; + } + + case CMD_SPU_SET_SIZE: /* image coordinates */ +/* state->o_left = (buf[1] << 4) | (buf[2] >> 4); + state->o_right = (((buf[2] & 0x0f) << 8) | buf[3]); + + state->o_top = (buf[4] << 4) | (buf[5] >> 4); + state->o_bottom = (((buf[5] & 0x0f) << 8) | buf[6]); + */ + ovl->x = (buf[1] << 4) | (buf[2] >> 4); + ovl->y = (buf[4] << 4) | (buf[5] >> 4); + ovl->width = (((buf[2] & 0x0f) << 8) | buf[3]) - ovl->x + 1; + ovl->height = (((buf[5] & 0x0f) << 8) | buf[6]) - ovl->y + 1; + ovl->hili_top = -1; + ovl->hili_bottom = -1; + ovl->hili_left = -1; + ovl->hili_right = -1; + +#ifdef LOG_DEBUG + printf ("spu: \tx = %d y = %d width = %d height = %d\n", + ovl->x, ovl->y, ovl->width, ovl->height ); +#endif + state->modified = 1; + buf += 7; + break; + + case CMD_SPU_SET_PXD_OFFSET: /* image top[0] field / image bottom[1] field*/ + state->field_offs[0] = (((u_int)buf[1]) << 8) | buf[2]; + state->field_offs[1] = (((u_int)buf[3]) << 8) | buf[4]; + +#ifdef LOG_DEBUG + printf ("spu: \toffset[0] = %d offset[1] = %d\n", + state->field_offs[0], state->field_offs[1]); +#endif + + if ((state->field_offs[0] >= seq->seq_len) || + (state->field_offs[1] >= seq->seq_len)) { + xprintf(xine, XINE_VERBOSITY_DEBUG, "libspudec:faulty stream\n"); + seq->broken = 1; + } + state->modified = 1; + buf += 5; + break; + + case CMD_SPU_WIPE: +#ifdef LOG_DEBUG + printf ("libspudec: \tSPU_WIPE not implemented yet\n"); +#endif + param_length = (buf[1] << 8) | (buf[2]); + buf += 1 + param_length; + break; + + case CMD_SPU_FORCE_DISPLAY: +#ifdef LOG_DEBUG + printf ("libspudec: \tForce Display/Menu\n"); +#endif + state->forced_display = 1; + buf++; + break; + + default: + xprintf(xine, XINE_VERBOSITY_DEBUG, "libspudec: unknown seqence command (%02x)\n", buf[0]); + /* FIXME: SPU should be dropped, and buffers resynced */ + buf = next_seq; + seq->broken = 1; + break; + } + } + + if (next_seq >= seq->buf + seq->seq_len) + seq->finished = 1; /* last sub-sequence */ +} + +/* FIXME: Get rid of all these static values */ +static uint8_t *bit_ptr[2]; +static int field; // which field we are currently decoding +static int put_x, put_y; + +static u_int get_bits (u_int bits) +{ + static u_int data; + static u_int bits_left; + u_int ret = 0; + + if (!bits) { /* for realignment to next byte */ + bits_left = 0; + } + + while (bits) { + if (bits > bits_left) { + ret |= data << (bits - bits_left); + bits -= bits_left; + + data = *bit_ptr[field]++; + bits_left = 8; + } else { + bits_left -= bits; + ret |= data >> (bits_left); + data &= (1 << bits_left) - 1; + bits = 0; + } + } + + return ret; +} + +static int spudec_next_line (vo_overlay_t *spu) +{ + get_bits (0); // byte align rle data + + put_x = 0; + put_y++; + field ^= 1; // Toggle fields + + if (put_y >= spu->height) { +#ifdef LOG_DEBUG + printf ("spu: put_y >= spu->height\n"); +#endif + return -1; + } + return 0; +} + +static void spudec_draw_picture (xine_t *xine, spudec_state_t *state, spudec_seq_t* seq, vo_overlay_t *ovl) +{ + rle_elem_t *rle; + field = 0; + bit_ptr[0] = seq->buf + state->field_offs[0]; + bit_ptr[1] = seq->buf + state->field_offs[1]; + put_x = put_y = 0; + get_bits (0); /* Reset/init bit code */ + +/* ovl->x = state->o_left; + * ovl->y = state->o_top; + * ovl->width = state->o_right - state->o_left + 1; + * ovl->height = state->o_bottom - state->o_top + 1; + + * ovl->hili_top = 0; + * ovl->hili_bottom = ovl->height - 1; + * ovl->hili_left = 0; + * ovl->hili_right = ovl->width - 1; + */ + + /* allocate for the worst case: + * - both fields running to the very end + * - 2 RLE elements per byte meaning single pixel RLE + */ + ovl->data_size = ((seq->cmd_offs - state->field_offs[0]) + + (seq->cmd_offs - state->field_offs[1])) * 2 * sizeof(rle_elem_t); + + if (ovl->rle) { + xprintf (xine, XINE_VERBOSITY_DEBUG, + "libspudec: spudec_draw_picture: ovl->rle is not empty!!!! It should be!!! " + "You should never see this message.\n"); + free(ovl->rle); + ovl->rle=NULL; + } + ovl->rle = malloc(ovl->data_size); + + state->modified = 0; /* mark as already processed */ + rle = ovl->rle; +#ifdef LOG_DEBUG + printf ("libspudec: Draw RLE=%p\n",rle); +#endif + + while (bit_ptr[1] < seq->buf + seq->cmd_offs) { + u_int len; + u_int vlc; + + vlc = get_bits (4); + if (vlc < 0x0004) { + vlc = (vlc << 4) | get_bits (4); + if (vlc < 0x0010) { + vlc = (vlc << 4) | get_bits (4); + if (vlc < 0x0040) { + vlc = (vlc << 4) | get_bits (4); + } + } + } + + len = vlc >> 2; + + /* if len == 0 -> end sequence - fill to end of line */ + if (len == 0) + len = ovl->width - put_x; + + rle->len = len; + rle->color = vlc & 0x03; + rle++; + put_x += len; + + if (put_x >= ovl->width) { + if (spudec_next_line (ovl) < 0) + break; + } + } + + ovl->num_rle = rle - ovl->rle; + ovl->rgb_clut = 0; + ovl->unscaled = 0; +#ifdef LOG_DEBUG + printf ("spu: Num RLE=%d\n",ovl->num_rle); + printf ("spu: Date size=%d\n",ovl->data_size); + printf ("spu: sizeof RLE=%d\n",sizeof(rle_elem_t)); +#endif +} + +/* Heuristic to discover the colors used by the subtitles + and assign a "readable" pallete to them. + Currently looks for sequence of border-fg-border or + border1-border2-fg-border2-border1. + MINFOUND is the number of ocurrences threshold. +*/ +#define MINFOUND 20 +static void spudec_discover_clut(xine_t *xine, spudec_state_t *state, vo_overlay_t *ovl) +{ + int bg,c; + int seqcolor[10]; + int n,i; + rle_elem_t *rle; + + int found[2][16]; + + static clut_t text_clut[] = { + CLUT_Y_CR_CB_INIT(0x80, 0x90, 0x80), + CLUT_Y_CR_CB_INIT(0x00, 0x90, 0x00), + CLUT_Y_CR_CB_INIT(0xff, 0x90, 0x00) + }; + + memset(found,0,sizeof(found)); + rle = ovl->rle; + + /* this seems to be a problem somewhere else, + why rle is null? */ + if( !rle ) + return; + + /* suppose the first and last pixels are bg */ + if( rle[0].color != rle[ovl->num_rle-1].color ) + return; + + bg = rle[0].color; + + i = 0; + for( n = 0; n < ovl->num_rle; n++ ) + { + c = rle[n].color; + + if( c == bg ) + { + if( i == 3 && seqcolor[1] == seqcolor[3] ) + { + found[0][seqcolor[2]]++; + if( found[0][seqcolor[2]] > MINFOUND ) + { + memcpy(&state->clut[state->cur_colors[seqcolor[1]]], &text_clut[1], + sizeof(clut_t)); + memcpy(&state->clut[state->cur_colors[seqcolor[2]]], &text_clut[2], + sizeof(clut_t)); + ovl->color[seqcolor[1]] = state->clut[state->cur_colors[seqcolor[1]]]; + ovl->color[seqcolor[2]] = state->clut[state->cur_colors[seqcolor[2]]]; + state->need_clut = 0; + break; + } + } + if( i == 5 && seqcolor[1] == seqcolor[5] + && seqcolor[2] == seqcolor[4] ) + { + found[1][seqcolor[3]]++; + if( found[1][seqcolor[3]] > MINFOUND ) + { + memcpy(&state->clut[state->cur_colors[seqcolor[1]]], &text_clut[0], + sizeof(clut_t)); + memcpy(&state->clut[state->cur_colors[seqcolor[2]]], &text_clut[1], + sizeof(clut_t)); + memcpy(&state->clut[state->cur_colors[seqcolor[3]]], &text_clut[2], + sizeof(clut_t)); + ovl->color[seqcolor[1]] = state->clut[state->cur_colors[seqcolor[1]]]; + ovl->color[seqcolor[2]] = state->clut[state->cur_colors[seqcolor[2]]]; + ovl->color[seqcolor[3]] = state->clut[state->cur_colors[seqcolor[3]]]; + state->need_clut = 0; + break; + } + } + i = 0; + seqcolor[i] = c; + } + else if ( i < 6 ) + { + i++; + seqcolor[i] = c; + } + } +} + +#ifdef LOG_DEBUG +static void spudec_print_overlay( vo_overlay_t *ovl ) { + printf ("spu: OVERLAY to show\n"); + printf ("spu: \tx = %d y = %d width = %d height = %d\n", + ovl->x, ovl->y, ovl->width, ovl->height ); + printf ("spu: \tclut [%x %x %x %x]\n", + ovl->color[0], ovl->color[1], ovl->color[2], ovl->color[3]); + printf ("spu: \ttrans [%d %d %d %d]\n", + ovl->trans[0], ovl->trans[1], ovl->trans[2], ovl->trans[3]); + printf ("spu: \tclip top=%d bottom=%d left=%d right=%d\n", + ovl->hili_top, ovl->hili_bottom, ovl->hili_left, ovl->hili_right); + printf ("spu: \tclip_clut [%x %x %x %x]\n", + ovl->hili_color[0], ovl->hili_color[1], ovl->hili_color[2], ovl->hili_color[3]); + printf ("spu: \thili_trans [%d %d %d %d]\n", + ovl->hili_trans[0], ovl->hili_trans[1], ovl->hili_trans[2], ovl->hili_trans[3]); + return; +} +#endif + +int spudec_copy_nav_to_overlay(xine_t *xine, pci_t* nav_pci, uint32_t* clut, + int32_t button, int32_t mode, vo_overlay_t * overlay, vo_overlay_t * base ) { + btni_t *button_ptr = NULL; + unsigned int btns_per_group; + int i; + + if((button <= 0) || (button > nav_pci->hli.hl_gi.btn_ns)) + return 0; + + btns_per_group = 36 / nav_pci->hli.hl_gi.btngr_ns; + + /* choose button group: we can always use a normal 4:3 or widescreen button group + * as long as xine blends the overlay before scaling the image to its aspect */ + if (!button_ptr && nav_pci->hli.hl_gi.btngr_ns >= 1 && !(nav_pci->hli.hl_gi.btngr1_dsp_ty & 6)) + button_ptr = &nav_pci->hli.btnit[0 * btns_per_group + button - 1]; + if (!button_ptr && nav_pci->hli.hl_gi.btngr_ns >= 2 && !(nav_pci->hli.hl_gi.btngr2_dsp_ty & 6)) + button_ptr = &nav_pci->hli.btnit[1 * btns_per_group + button - 1]; + if (!button_ptr && nav_pci->hli.hl_gi.btngr_ns >= 3 && !(nav_pci->hli.hl_gi.btngr3_dsp_ty & 6)) + button_ptr = &nav_pci->hli.btnit[2 * btns_per_group + button - 1]; + if (!button_ptr) { + xprintf(xine, XINE_VERBOSITY_DEBUG, + "libspudec: No suitable menu button group found, using group 1.\n"); + button_ptr = &nav_pci->hli.btnit[button - 1]; + } + + /* button areas in the nav packet are in screen coordinates, + * overlay clipping areas are in overlay coordinates; + * therefore we must subtract the display coordinates of the underlying overlay */ + overlay->hili_left = (button_ptr->x_start > base->x) ? (button_ptr->x_start - base->x) : 0; + overlay->hili_top = (button_ptr->y_start > base->y) ? (button_ptr->y_start - base->y) : 0; + overlay->hili_right = (button_ptr->x_end > base->x) ? (button_ptr->x_end - base->x) : 0; + overlay->hili_bottom = (button_ptr->y_end > base->y) ? (button_ptr->y_end - base->y) : 0; + if(button_ptr->btn_coln != 0) { +#ifdef LOG_BUTTON + fprintf(stderr, "libspudec: normal button clut\n"); +#endif + for (i = 0;i < 4; i++) { + overlay->hili_color[i] = clut[0xf & (nav_pci->hli.btn_colit.btn_coli[button_ptr->btn_coln-1][mode] >> (16 + 4*i))]; + overlay->hili_trans[i] = 0xf & (nav_pci->hli.btn_colit.btn_coli[button_ptr->btn_coln-1][mode] >> (4*i)); + } + } else { +#ifdef LOG_BUTTON + fprintf(stderr, "libspudec: abnormal button clut\n"); +#endif + for (i = 0;i < 4; i++) { +#ifdef LOG_BUTTON + printf("libspudec:btn_coln = 0, hili_color = color\n"); +#endif + overlay->hili_color[i] = overlay->color[i]; + overlay->hili_trans[i] = overlay->trans[i]; + } + } + + /* spudec_print_overlay( overlay ); */ +#ifdef LOG_BUTTON + printf("libspudec:xine_decoder.c:NAV to SPU pts match!\n"); +#endif + + return 1; +} diff --git a/src/spu_dec/spudec.h b/src/spu_dec/spudec.h new file mode 100644 index 000000000..1e7d80596 --- /dev/null +++ b/src/spu_dec/spudec.h @@ -0,0 +1,142 @@ +/* + * Copyright (C) 2000-2004 the xine project + * + * Copyright (C) James Courtier-Dutton James@superbug.demon.co.uk - July 2001 + * + * 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 + * + * This file was originally part of the OMS program. + */ + +#ifndef __SPU_H__ +#define __SPU_H__ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#ifdef HAVE_DVDNAV +# include +#else +# include "nav_types.h" +#endif + +#define NUM_SEQ_BUFFERS 50 +#define MAX_STREAMS 32 + +typedef struct spudec_clut_struct { +#ifdef WORDS_BIGENDIAN + uint8_t entry0 : 4; + uint8_t entry1 : 4; + uint8_t entry2 : 4; + uint8_t entry3 : 4; +#else + uint8_t entry1 : 4; + uint8_t entry0 : 4; + uint8_t entry3 : 4; + uint8_t entry2 : 4; +#endif +} spudec_clut_t; + +typedef struct { + uint8_t *buf; + uint32_t ra_offs; /* reassembly offset */ + uint32_t seq_len; + uint32_t buf_len; + uint32_t cmd_offs; + int64_t pts; /* Base PTS of this sequence */ + int32_t finished; /* Has this control sequence been finished? */ + uint32_t complete; /* Has this reassembly been finished? */ + uint32_t broken; /* this SPU is broken and should be dropped */ +} spudec_seq_t; + +typedef struct { + uint8_t *cmd_ptr; + + uint32_t field_offs[2]; + int32_t b_top, o_top; + int32_t b_bottom, o_bottom; + int32_t b_left, o_left; + int32_t b_right, o_right; + + int32_t modified; /* Was the sub-picture modified? */ + int32_t visible; /* Must the sub-picture be shown? */ + int32_t forced_display; /* This overlay is a menu */ + int32_t delay; /* Delay in 90Khz / 1000 */ + int32_t need_clut; /* doesn't have the right clut yet */ + int32_t cur_colors[4];/* current 4 colors been used */ + int32_t vobsub; /* vobsub must be aligned to bottom */ + + uint32_t clut[16]; +} spudec_state_t; + +typedef struct spudec_stream_state_s { + spudec_seq_t ra_seq; + spudec_state_t state; + int64_t vpts; + int64_t pts; + int32_t overlay_handle; +} spudec_stream_state_t; + +typedef struct { + spu_decoder_class_t decoder_class; +} spudec_class_t; + +typedef struct pci_node_s pci_node_t; +struct pci_node_s { + pci_t pci; + uint64_t vpts; + pci_node_t *next; +}; + +typedef struct spudec_decoder_s { + spu_decoder_t spu_decoder; + + spudec_class_t *class; + xine_stream_t *stream; + spudec_stream_state_t spudec_stream_state[MAX_STREAMS]; + + video_overlay_event_t event; + video_overlay_object_t object; + int32_t menu_handle; + + spudec_state_t state; + + vo_overlay_t overlay; + int ovl_caps; + int output_open; + pthread_mutex_t nav_pci_lock; + pci_node_t pci_cur; + uint32_t buttonN; /* Current button number for highlights */ + int32_t button_filter; /* Allow highlight changes or not */ + int64_t last_event_vpts; +} spudec_decoder_t; + +void spudec_reassembly (xine_t *xine, spudec_seq_t *seq, uint8_t *pkt_data, u_int pkt_len); +void spudec_process( spudec_decoder_t *this, int stream_id); +/* the nav functions must be called with the nav_pci_lock held */ +void spudec_decode_nav( spudec_decoder_t *this, buf_element_t *buf); +void spudec_clear_nav_list(spudec_decoder_t *this); +void spudec_update_nav(spudec_decoder_t *this); +void spudec_process_nav(spudec_decoder_t *this); +int spudec_copy_nav_to_overlay(xine_t *xine, pci_t* nav_pci, uint32_t* clut, int32_t button, int32_t mode, + vo_overlay_t * overlay, vo_overlay_t * base ); + +#endif diff --git a/src/spu_dec/spudvb_decoder.c b/src/spu_dec/spudvb_decoder.c new file mode 100644 index 000000000..9008260f7 --- /dev/null +++ b/src/spu_dec/spudvb_decoder.c @@ -0,0 +1,999 @@ +/* + * Copyright (C) 2004 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 + * + * DVB Subtitle decoder (ETS 300 743) + * (c) 2004 Mike Lampard + * based on the application dvbsub by Dave Chapman + * + * TODO: + * - Implement support for teletext based subtitles + */ + +#include "pthread.h" +#include +#include +#include +#include +#define MAX_REGIONS 7 + +/*#define LOG 1*/ + +typedef struct { + int x, y; + unsigned char is_visible; +} visible_region_t; + +typedef struct { + int page_time_out; + int page_version_number; + int page_state; + int page_id; + visible_region_t regions[MAX_REGIONS]; +} page_t; + +typedef struct { + int width, height; + int empty; + int depth; + int CLUT_id; + int objects_start; + int objects_end; + unsigned int object_pos[65536]; + unsigned char *img; + osd_object_t *osd; +} region_t; + +typedef struct { +/* dvbsub stuff */ + int x; + int y; + unsigned int curr_obj; + unsigned int curr_reg[64]; + uint8_t *buf; + int i; + int nibble_flag; + int in_scanline; + page_t page; + region_t regions[MAX_REGIONS]; + clut_t colours[MAX_REGIONS*256]; + unsigned char trans[MAX_REGIONS*256]; +} dvbsub_func_t; + +typedef struct dvb_spu_class_s { + spu_decoder_class_t class; + xine_t *xine; +} dvb_spu_class_t; + +typedef struct dvb_spu_decoder_s { + spu_decoder_t spu_decoder; + + dvb_spu_class_t *class; + xine_stream_t *stream; + + spu_dvb_descriptor_t *spu_descriptor; + + /* dvbsub_osd_mutex should be locked around all calls to this->osd_renderer->show() + and this->osd_renderer->hide() */ + pthread_mutex_t dvbsub_osd_mutex; + + char *pes_pkt; + char *pes_pkt_wrptr; + unsigned int pes_pkt_size; + + uint64_t pts; + uint64_t vpts; + uint64_t end_vpts; + + pthread_t dvbsub_timer_thread; + struct timespec dvbsub_hide_timeout; + pthread_cond_t dvbsub_restart_timeout; + dvbsub_func_t *dvbsub; + int show; +} dvb_spu_decoder_t; + + +static void update_osd(dvb_spu_decoder_t *this, int region_id) +{ + dvbsub_func_t *dvbsub = this->dvbsub; + region_t *reg = &dvbsub->regions[region_id]; + + if ( !reg->img ) { + if ( reg->osd ) { + pthread_mutex_lock( &this->dvbsub_osd_mutex ); + this->stream->osd_renderer->free_object( reg->osd ); + reg->osd = NULL; + pthread_mutex_unlock( &this->dvbsub_osd_mutex ); + } + return; + } + + if ( reg->osd ) { + if ( reg->width!=reg->osd->width || reg->height!=reg->osd->height ) { + pthread_mutex_lock( &this->dvbsub_osd_mutex ); + this->stream->osd_renderer->free_object( reg->osd ); + reg->osd = NULL; + pthread_mutex_unlock( &this->dvbsub_osd_mutex ); + } + } + + if ( !reg->osd ) + reg->osd = this->stream->osd_renderer->new_object( this->stream->osd_renderer, reg->width, reg->height ); +} + +static void update_region (dvb_spu_decoder_t * this, int region_id, int region_width, int region_height, int fill, int fill_color) +{ + + dvbsub_func_t *dvbsub = this->dvbsub; + region_t *reg = &dvbsub->regions[region_id]; + page_t *page = &dvbsub->page; + + /* reject invalid sizes and set some limits ! */ + if ( region_width<=0 || region_height<=0 || region_width>720 || region_height>576 ) { + if ( reg->img ) { + free( reg->img ); + reg->img = NULL; + } +#ifdef LOG + printf("SPUDVB: rejected region %d = %dx%d\n", region_id, region_width, region_height ); +#endif + return; + } + + if ( reg->width*reg->heightimg ) { + free( reg->img ); + reg->img = NULL; + } + } + + if ( !reg->img ) { + if ( !(reg->img=xine_xmalloc(region_width*region_height)) ) { + lprintf( "can't allocate mem for region %d\n", region_id ); + return; + } + fill_color = 15; + fill = 1; + } + + if ( fill ) { + memset( reg->img, fill_color, region_width*region_height ); + reg->empty = 1; +#ifdef LOG + printf("SPUDVB : FILL REGION %d\n", region_id); +#endif + } + reg->width = region_width; + reg->height = region_height; + page->regions[region_id].is_visible = 1; +} + + +static void do_plot (dvb_spu_decoder_t * this, int r, int x, int y, unsigned char pixel) +{ + int i; + dvbsub_func_t *dvbsub = this->dvbsub; + + i = (y * dvbsub->regions[r].width) + x; + /* do some clipping */ + if ( i<(dvbsub->regions[r].width*dvbsub->regions[r].height) ) { + dvbsub->regions[r].img[i] = pixel; + dvbsub->regions[r].empty = 0; + } +} + +static void plot (dvb_spu_decoder_t * this, int r, int run_length, unsigned char pixel) +{ + + dvbsub_func_t *dvbsub = this->dvbsub; + + int x2 = dvbsub->x + run_length; + + while (dvbsub->x < x2) { + do_plot (this, r, dvbsub->x, dvbsub->y, pixel); + dvbsub->x++; + } +} + +static unsigned char next_nibble (dvb_spu_decoder_t * this) +{ + unsigned char x; + dvbsub_func_t *dvbsub = this->dvbsub; + + if (dvbsub->nibble_flag == 0) { + x = (dvbsub->buf[dvbsub->i] & 0xf0) >> 4; + dvbsub->nibble_flag = 1; + } + else { + x = (dvbsub->buf[dvbsub->i++] & 0x0f); + dvbsub->nibble_flag = 0; + } + return (x); +} + +static void decode_4bit_pixel_code_string (dvb_spu_decoder_t * this, int r, int object_id, int ofs, int n) +{ + int next_bits, switch_1, switch_2, switch_3, run_length, pixel_code; + + dvbsub_func_t *dvbsub = this->dvbsub; + + int bits; + unsigned int data; + int j; + + if (dvbsub->in_scanline == 0) { + dvbsub->in_scanline = 1; + } + dvbsub->nibble_flag = 0; + j = dvbsub->i + n; + while (dvbsub->i < j) { + + bits = 0; + pixel_code = 0; + next_bits = next_nibble (this); + + if (next_bits != 0) { + pixel_code = next_bits; + plot (this, r, 1, pixel_code); + bits += 4; + } + else { + bits += 4; + data = next_nibble (this); + switch_1 = (data & 0x08) >> 3; + bits++; + if (switch_1 == 0) { + run_length = (data & 0x07); + bits += 3; + if (run_length != 0) { + plot (this, r, run_length + 2, pixel_code); + } + else { + break; + } + } + else { + switch_2 = (data & 0x04) >> 2; + bits++; + if (switch_2 == 0) { + run_length = (data & 0x03); + bits += 2; + pixel_code = next_nibble (this); + bits += 4; + plot (this, r, run_length + 4, pixel_code); + } + else { + switch_3 = (data & 0x03); + bits += 2; + switch (switch_3) { + case 0: + plot (this, r, 1, pixel_code); + break; + case 1: + plot (this, r, 2, pixel_code); + break; + case 2: + run_length = next_nibble (this); + bits += 4; + pixel_code = next_nibble (this); + bits += 4; + plot (this, r, run_length + 9, pixel_code); + break; + case 3: + run_length = next_nibble (this); + run_length = (run_length << 4) | next_nibble (this); + bits += 8; + pixel_code = next_nibble (this); + bits += 4; + plot (this, r, run_length + 25, pixel_code); + } + } + } + } + + } + if (dvbsub->nibble_flag == 1) { + dvbsub->i++; + dvbsub->nibble_flag = 0; + } +} + +static void recalculate_trans (dvb_spu_decoder_t *this) +{ + dvbsub_func_t *const dvbsub = this->dvbsub; + xine_spu_opacity_t opacity; + int i; + + _x_spu_get_opacity (this->stream->xine, &opacity); + for (i = 0; i < MAX_REGIONS * 256; ++i) { + /* ETSI-300-743 says "full transparency if Y == 0". */ + if (dvbsub->colours[i].y == 0) + dvbsub->trans[i] = 0; + else { + int v = _x_spu_calculate_opacity (&dvbsub->colours[i], dvbsub->colours[i].foo, &opacity); + dvbsub->trans[i] = v * 14 / 255 + 1; + } + } + +} + +static void set_clut(dvb_spu_decoder_t *this,int CLUT_id,int CLUT_entry_id,int Y_value, int Cr_value, int Cb_value, int T_value) { + + dvbsub_func_t *dvbsub = this->dvbsub; + + if ((CLUT_id>=MAX_REGIONS) || (CLUT_entry_id>15)) { + return; + } + + dvbsub->colours[(CLUT_id*256)+CLUT_entry_id].y=Y_value; + dvbsub->colours[(CLUT_id*256)+CLUT_entry_id].cr=Cr_value; + dvbsub->colours[(CLUT_id*256)+CLUT_entry_id].cb=Cb_value; + dvbsub->colours[(CLUT_id*256)+CLUT_entry_id].foo = T_value; +} + +static void process_CLUT_definition_segment(dvb_spu_decoder_t *this) { + int page_id, + segment_length, + CLUT_id, + CLUT_version_number; + + int CLUT_entry_id, + CLUT_flag_8_bit, + CLUT_flag_4_bit, + CLUT_flag_2_bit, + full_range_flag, + Y_value, + Cr_value, + Cb_value, + T_value; + dvbsub_func_t *dvbsub = this->dvbsub; + + int j; + + page_id=(dvbsub->buf[dvbsub->i]<<8)|dvbsub->buf[dvbsub->i+1]; dvbsub->i+=2; + segment_length=(dvbsub->buf[dvbsub->i]<<8)|dvbsub->buf[dvbsub->i+1]; dvbsub->i+=2; + j=dvbsub->i+segment_length; + + CLUT_id=dvbsub->buf[dvbsub->i++]; + CLUT_version_number=(dvbsub->buf[dvbsub->i]&0xf0)>>4; + dvbsub->i++; + + while (dvbsub->i < j) { + CLUT_entry_id=dvbsub->buf[dvbsub->i++]; + + CLUT_flag_2_bit=(dvbsub->buf[dvbsub->i]&0x80)>>7; + CLUT_flag_4_bit=(dvbsub->buf[dvbsub->i]&0x40)>>6; + CLUT_flag_8_bit=(dvbsub->buf[dvbsub->i]&0x20)>>5; + full_range_flag=dvbsub->buf[dvbsub->i]&1; + dvbsub->i++; + + if (full_range_flag==1) { + Y_value=dvbsub->buf[dvbsub->i++]; + Cr_value=dvbsub->buf[dvbsub->i++]; + Cb_value=dvbsub->buf[dvbsub->i++]; + T_value=dvbsub->buf[dvbsub->i++]; + } else { + Y_value = dvbsub->buf[dvbsub->i] & 0xfc; + Cr_value = (dvbsub->buf[dvbsub->i] << 6 | dvbsub->buf[dvbsub->i + 1] >> 2) & 0xf0; + Cb_value = (dvbsub->buf[dvbsub->i + 1] << 2) & 0xf0; + T_value = (dvbsub->buf[dvbsub->i + 1] & 3) * 0x55; /* expand only this one to full range! */ + dvbsub->i+=2; + } + set_clut(this, CLUT_id,CLUT_entry_id,Y_value,Cr_value,Cb_value,T_value); + } +} + +static void process_pixel_data_sub_block (dvb_spu_decoder_t * this, int r, int o, int ofs, int n) +{ + int data_type; + int j; + + dvbsub_func_t *dvbsub = this->dvbsub; + + j = dvbsub->i + n; + + dvbsub->x = (dvbsub->regions[r].object_pos[o]) >> 16; + dvbsub->y = ((dvbsub->regions[r].object_pos[o]) & 0xffff) + ofs; + while (dvbsub->i < j) { + data_type = dvbsub->buf[dvbsub->i++]; + + switch (data_type) { + case 0: + dvbsub->i++; + case 0x11: + decode_4bit_pixel_code_string (this, r, o, ofs, n - 1); + break; + case 0xf0: + dvbsub->in_scanline = 0; + dvbsub->x = (dvbsub->regions[r].object_pos[o]) >> 16; + dvbsub->y += 2; + break; + default: + lprintf ("unimplemented data_type %02x in pixel_data_sub_block\n", data_type); + } + } + + dvbsub->i = j; +} + +static void process_page_composition_segment (dvb_spu_decoder_t * this) +{ + int segment_length; + int region_id, region_x, region_y; + int j; + int r; + dvbsub_func_t *dvbsub = this->dvbsub; + + dvbsub->page.page_id = (dvbsub->buf[dvbsub->i] << 8) | dvbsub->buf[dvbsub->i + 1]; + dvbsub->i += 2; + segment_length = (dvbsub->buf[dvbsub->i] << 8) | dvbsub->buf[dvbsub->i + 1]; + dvbsub->i += 2; + + j = dvbsub->i + segment_length; + + dvbsub->page.page_time_out = dvbsub->buf[dvbsub->i++]; + + dvbsub->page.page_version_number = (dvbsub->buf[dvbsub->i] & 0xf0) >> 4; + dvbsub->page.page_state = (dvbsub->buf[dvbsub->i] & 0x0c) >> 2; + dvbsub->i++; + if (dvbsub->page.page_state==2) { + for (r=0; rpage.regions[r].is_visible = 0; + } + else if ( dvbsub->page.page_state!=0 && dvbsub->page.page_state!=1 ) { + return; + } + + while (dvbsub->i < j) { + region_id = dvbsub->buf[dvbsub->i++]; + dvbsub->i++; /* reserved */ + region_x = (dvbsub->buf[dvbsub->i] << 8) | dvbsub->buf[dvbsub->i + 1]; + dvbsub->i += 2; + region_y = (dvbsub->buf[dvbsub->i] << 8) | dvbsub->buf[dvbsub->i + 1]; + dvbsub->i += 2; + + dvbsub->page.regions[region_id].x = region_x; + dvbsub->page.regions[region_id].y = region_y; + } +} + + +static void process_region_composition_segment (dvb_spu_decoder_t * this) +{ + int segment_length, + region_id, + region_version_number, + region_fill_flag, region_width, region_height, region_level_of_compatibility, region_depth, CLUT_id, region_8_bit_pixel_code, region_4_bit_pixel_code, region_2_bit_pixel_code; + int object_id, object_type, object_provider_flag, object_x, object_y, foreground_pixel_code, background_pixel_code; + int j; + int o; + dvbsub_func_t *dvbsub = this->dvbsub; + + dvbsub->page.page_id = (dvbsub->buf[dvbsub->i] << 8) | dvbsub->buf[dvbsub->i + 1]; + dvbsub->i += 2; + segment_length = (dvbsub->buf[dvbsub->i] << 8) | dvbsub->buf[dvbsub->i + 1]; + dvbsub->i += 2; + j = dvbsub->i + segment_length; + + region_id = dvbsub->buf[dvbsub->i++]; + region_version_number = (dvbsub->buf[dvbsub->i] & 0xf0) >> 4; + region_fill_flag = (dvbsub->buf[dvbsub->i] & 0x08) >> 3; + dvbsub->i++; + region_width = (dvbsub->buf[dvbsub->i] << 8) | dvbsub->buf[dvbsub->i + 1]; + dvbsub->i += 2; + region_height = (dvbsub->buf[dvbsub->i] << 8) | dvbsub->buf[dvbsub->i + 1]; + dvbsub->i += 2; + region_level_of_compatibility = (dvbsub->buf[dvbsub->i] & 0xe0) >> 5; + region_depth = (dvbsub->buf[dvbsub->i] & 0x1c) >> 2; + dvbsub->i++; + CLUT_id = dvbsub->buf[dvbsub->i++]; + region_8_bit_pixel_code = dvbsub->buf[dvbsub->i++]; + region_4_bit_pixel_code = (dvbsub->buf[dvbsub->i] & 0xf0) >> 4; + region_2_bit_pixel_code = (dvbsub->buf[dvbsub->i] & 0x0c) >> 2; + dvbsub->i++; + + if(region_id>=MAX_REGIONS) + return; + + /* Check if region size has changed and fill background. */ + update_region (this, region_id, region_width, region_height, region_fill_flag, region_4_bit_pixel_code); + if ( CLUT_idregions[region_id].CLUT_id = CLUT_id; + + dvbsub->regions[region_id].objects_start = dvbsub->i; + dvbsub->regions[region_id].objects_end = j; + + for (o = 0; o < 65536; o++) { + dvbsub->regions[region_id].object_pos[o] = 0xffffffff; + } + + while (dvbsub->i < j) { + object_id = (dvbsub->buf[dvbsub->i] << 8) | dvbsub->buf[dvbsub->i + 1]; + dvbsub->i += 2; + object_type = (dvbsub->buf[dvbsub->i] & 0xc0) >> 6; + object_provider_flag = (dvbsub->buf[dvbsub->i] & 0x30) >> 4; + object_x = ((dvbsub->buf[dvbsub->i] & 0x0f) << 8) | dvbsub->buf[dvbsub->i + 1]; + dvbsub->i += 2; + object_y = ((dvbsub->buf[dvbsub->i] & 0x0f) << 8) | dvbsub->buf[dvbsub->i + 1]; + dvbsub->i += 2; + + dvbsub->regions[region_id].object_pos[object_id] = (object_x << 16) | object_y; + + if ((object_type == 0x01) || (object_type == 0x02)) { + foreground_pixel_code = dvbsub->buf[dvbsub->i++]; + background_pixel_code = dvbsub->buf[dvbsub->i++]; + } + } + +} + +static void process_object_data_segment (dvb_spu_decoder_t * this) +{ + int segment_length, object_id, object_version_number, object_coding_method, non_modifying_colour_flag; + + int top_field_data_block_length, bottom_field_data_block_length; + + dvbsub_func_t *dvbsub = this->dvbsub; + + int j; + int old_i; + int r; + + dvbsub->page.page_id = (dvbsub->buf[dvbsub->i] << 8) | dvbsub->buf[dvbsub->i + 1]; + dvbsub->i += 2; + segment_length = (dvbsub->buf[dvbsub->i] << 8) | dvbsub->buf[dvbsub->i + 1]; + dvbsub->i += 2; + j = dvbsub->i + segment_length; + + object_id = (dvbsub->buf[dvbsub->i] << 8) | dvbsub->buf[dvbsub->i + 1]; + dvbsub->i += 2; + dvbsub->curr_obj = object_id; + object_version_number = (dvbsub->buf[dvbsub->i] & 0xf0) >> 4; + object_coding_method = (dvbsub->buf[dvbsub->i] & 0x0c) >> 2; + non_modifying_colour_flag = (dvbsub->buf[dvbsub->i] & 0x02) >> 1; + dvbsub->i++; + + old_i = dvbsub->i; + for (r = 0; r < MAX_REGIONS; r++) { + + /* If this object is in this region... */ + if (dvbsub->regions[r].img) { + if (dvbsub->regions[r].object_pos[object_id] != 0xffffffff) { + dvbsub->i = old_i; + if (object_coding_method == 0) { + top_field_data_block_length = (dvbsub->buf[dvbsub->i] << 8) | dvbsub->buf[dvbsub->i + 1]; + dvbsub->i += 2; + bottom_field_data_block_length = (dvbsub->buf[dvbsub->i] << 8) | dvbsub->buf[dvbsub->i + 1]; + dvbsub->i += 2; + + process_pixel_data_sub_block (this, r, object_id, 0, top_field_data_block_length); + + process_pixel_data_sub_block (this, r, object_id, 1, bottom_field_data_block_length); + } + } + } + } +} + +static void unlock_mutex_cancellation_func(void *mutex_gen) +{ + pthread_mutex_t *mutex = (pthread_mutex_t*) mutex_gen; + pthread_mutex_unlock(mutex); +} + +/* Thread routine that checks for subtitle timeout periodically. + To avoid unexpected subtitle hiding, calls to this->stream->osd_renderer->show() + should be in blocks like: + + pthread_mutex_lock(&this->dvbsub_osd_mutex); + this->stream->osd_renderer->show(...); + this->dvbsub_hide_timeout.tv_sec = time(NULL) + timeout value; + pthread_cond_signal(&this->dvbsub_restart_timeout); + pthread_mutex_unlock(&this->dvbsub_osd_mutex); + + This ensures that the timeout is changed with the lock held, and + that the thread is signalled to pick up the new timeout. +*/ +static void* dvbsub_timer_func(void *this_gen) +{ + dvb_spu_decoder_t *this = (dvb_spu_decoder_t *) this_gen; + pthread_mutex_lock(&this->dvbsub_osd_mutex); + int i; + + /* If we're cancelled via pthread_cancel, unlock the mutex */ + pthread_cleanup_push(unlock_mutex_cancellation_func, &this->dvbsub_osd_mutex); + + while(1) + { + /* Record the current timeout, and wait - note that pthread_cond_timedwait + will unlock the mutex on entry, and lock it on exit */ + struct timespec timeout = this->dvbsub_hide_timeout; + int result = pthread_cond_timedwait(&this->dvbsub_restart_timeout, + &this->dvbsub_osd_mutex, + &this->dvbsub_hide_timeout); + if(result == ETIMEDOUT && + timeout.tv_sec == this->dvbsub_hide_timeout.tv_sec && + timeout.tv_nsec == this->dvbsub_hide_timeout.tv_nsec) + { + /* We timed out, and no-one changed the timeout underneath us. + Hide the OSD, then wait until we're signalled. */ + if(this && this->stream && this->stream->osd_renderer) + { + for ( i=0; idvbsub->regions[i].osd ) { + this->stream->osd_renderer->hide( this->dvbsub->regions[i].osd, 0 ); +#ifdef LOG + printf("SPUDVB: thread hiding = %d\n",i); +#endif + } + } + } + pthread_cond_wait(&this->dvbsub_restart_timeout, &this->dvbsub_osd_mutex); + } + } + + pthread_cleanup_pop(1); + return NULL; +} + +static void downscale_region_image( region_t *reg, unsigned char *dest, int dest_width ) +{ + float i, k, inc=reg->width/(float)dest_width; + int j; + for ( j=0; jheight; j++ ) { + for ( i=0,k=0; iwidth && kimg[(j*reg->width)+(int)i]; + } + } +} + +static void draw_subtitles (dvb_spu_decoder_t * this) +{ + int r; + int display=0; + int64_t dum; + int dest_width=0, dest_height, reg_width; + this->stream->video_out->status(this->stream->video_out, NULL, &dest_width, &dest_height, &dum); + unsigned char tmp[dest_width*576]; + unsigned char *reg; + + if ( !dest_width ) + return; + + /* render all regions onto the page */ + + for ( r=0; rdvbsub->page.regions[r].is_visible ) + display++; + } + if ( !display ) + return; + + for (r = 0; r < MAX_REGIONS; r++) { + if (this->dvbsub->regions[r].img) { + if (this->dvbsub->page.regions[r].is_visible && !this->dvbsub->regions[r].empty) { + update_osd( this, r ); + if ( !this->dvbsub->regions[r].osd ) + continue; + /* clear osd */ + this->stream->osd_renderer->clear( this->dvbsub->regions[r].osd ); + if (this->dvbsub->regions[r].width>dest_width) { + downscale_region_image(&this->dvbsub->regions[r], tmp, dest_width); + reg = tmp; + reg_width = dest_width; + } + else { + reg = this->dvbsub->regions[r].img; + reg_width = this->dvbsub->regions[r].width; + } + this->stream->osd_renderer->set_palette( this->dvbsub->regions[r].osd, (uint32_t*)(&this->dvbsub->colours[this->dvbsub->regions[r].CLUT_id*256]), &this->dvbsub->trans[this->dvbsub->regions[r].CLUT_id*256]); + this->stream->osd_renderer->draw_bitmap( this->dvbsub->regions[r].osd, reg, 0, 0, reg_width, this->dvbsub->regions[r].height, NULL ); + } + } + } + + pthread_mutex_lock(&this->dvbsub_osd_mutex); +#ifdef LOG + printf("SPUDVB: this->vpts=%llu\n",this->vpts); +#endif + for ( r=0; rdvbsub->page.regions[r].is_visible && this->dvbsub->regions[r].osd && !this->dvbsub->regions[r].empty ) { + this->stream->osd_renderer->set_position( this->dvbsub->regions[r].osd, this->dvbsub->page.regions[r].x, this->dvbsub->page.regions[r].y ); + this->stream->osd_renderer->show( this->dvbsub->regions[r].osd, this->vpts ); +#ifdef LOG + printf("SPUDVB: show region = %d\n",r); +#endif + } + else { + if ( this->dvbsub->regions[r].osd ) { + this->stream->osd_renderer->hide( this->dvbsub->regions[r].osd, this->vpts ); +#ifdef LOG + printf("SPUDVB: hide region = %d\n",r); +#endif + } + } + } + this->dvbsub_hide_timeout.tv_nsec = 0; + this->dvbsub_hide_timeout.tv_sec = time(NULL) + this->dvbsub->page.page_time_out; +#ifdef LOG + printf("SPUDVB: page_time_out %d\n",this->dvbsub->page.page_time_out); +#endif + pthread_cond_signal(&this->dvbsub_restart_timeout); + pthread_mutex_unlock(&this->dvbsub_osd_mutex); +} + + +static void spudec_decode_data (spu_decoder_t * this_gen, buf_element_t * buf) +{ + dvb_spu_decoder_t *this = (dvb_spu_decoder_t *) this_gen; + int new_i; + int data_identifier, subtitle_stream_id; + int segment_length, segment_type; + int PES_header_data_length; + int PES_packet_length; + int i; + + if((buf->type & 0xffff0000)!=BUF_SPU_DVB) + return; + + if (buf->decoder_flags & BUF_FLAG_SPECIAL) { + if (buf->decoder_info[1] == BUF_SPECIAL_SPU_DVB_DESCRIPTOR) { + if (buf->decoder_info[2] == 0) { + /* Hide the osd - note that if the timeout thread times out, it'll rehide, which is harmless */ + pthread_mutex_lock(&this->dvbsub_osd_mutex); + for ( i=0; idvbsub->regions[i].osd ) + this->stream->osd_renderer->hide( this->dvbsub->regions[i].osd, 0 ); + } + pthread_mutex_unlock(&this->dvbsub_osd_mutex); + } + else { + xine_fast_memcpy (this->spu_descriptor, buf->decoder_info_ptr[2], buf->decoder_info[2]); + } + } + return; + } + else { + if (buf->decoder_info[2]) { + memset (this->pes_pkt, 0xff, 64*1024); + this->pes_pkt_wrptr = this->pes_pkt; + this->pes_pkt_size = buf->decoder_info[2]; + this->pts = buf->pts; + + xine_fast_memcpy (this->pes_pkt, buf->content, buf->size); + this->pes_pkt_wrptr += buf->size; + } + else { + if (this->pes_pkt && (this->pes_pkt_wrptr != this->pes_pkt)) { + xine_fast_memcpy (this->pes_pkt_wrptr, buf->content, buf->size); + this->pes_pkt_wrptr += buf->size; + } + } + } + /* don't ask metronom for a vpts but rather do the calculation + * because buf->pts could be too far in future and metronom won't accept + * further backwards pts (see metronom_got_spu_packet) */ + if (buf->pts) { + metronom_t *metronom = this->stream->metronom; + int64_t vpts_offset = metronom->get_option( metronom, METRONOM_VPTS_OFFSET ); + int64_t spu_offset = metronom->get_option( metronom, METRONOM_SPU_OFFSET ); + int64_t vpts = (int64_t)(buf->pts)+vpts_offset+spu_offset; + metronom_clock_t *clock = this->stream->xine->clock; + int64_t curvpts = clock->get_current_time( clock ); + /* if buf->pts is unreliable, show page asap (better than nothing) */ +#ifdef LOG + printf("SPUDVB: spu_vpts=%lld - current_vpts=%lld\n", vpts, curvpts); +#endif + if ( vpts<=curvpts || (vpts-curvpts)>(5*90000) ) + this->vpts = 0; + else + this->vpts = vpts; + } + + /* process the pes section */ + + PES_packet_length = this->pes_pkt_size; + + this->dvbsub->buf = this->pes_pkt; + + PES_header_data_length = 0; + this->dvbsub->i = 0; + + data_identifier = this->dvbsub->buf[this->dvbsub->i++]; + subtitle_stream_id = this->dvbsub->buf[this->dvbsub->i++]; + + while (this->dvbsub->i <= (PES_packet_length)) { + /* SUBTITLING SEGMENT */ + this->dvbsub->i++; + segment_type = this->dvbsub->buf[this->dvbsub->i++]; + + this->dvbsub->page.page_id = (this->dvbsub->buf[this->dvbsub->i] << 8) | this->dvbsub->buf[this->dvbsub->i + 1]; + segment_length = (this->dvbsub->buf[this->dvbsub->i + 2] << 8) | this->dvbsub->buf[this->dvbsub->i + 3]; + new_i = this->dvbsub->i + segment_length + 4; + + /* only process complete segments */ + if(new_i > (this->pes_pkt_wrptr - this->pes_pkt)) + break; + /* verify we've the right segment */ + if(this->dvbsub->page.page_id==this->spu_descriptor->comp_page_id){ + /* SEGMENT_DATA_FIELD */ + switch (segment_type & 0xff) { + case 0x10: + process_page_composition_segment (this); + break; + case 0x11: + process_region_composition_segment (this); + break; + case 0x12: + process_CLUT_definition_segment(this); + break; + case 0x13: + process_object_data_segment (this); + break; + case 0x80: /* Page is now completely rendered */ + recalculate_trans(this); + draw_subtitles( this ); + break; + default: + return; + break; + } + } + this->dvbsub->i = new_i; + } + + return; +} + +static void spudec_reset (spu_decoder_t * this_gen) +{ + dvb_spu_decoder_t *this = (dvb_spu_decoder_t *) this_gen; + int i; + + /* Hide the osd - if the timeout thread times out, it'll rehide harmlessly */ + pthread_mutex_lock(&this->dvbsub_osd_mutex); + for ( i=0; idvbsub->regions[i].osd ) + this->stream->osd_renderer->hide(this->dvbsub->regions[i].osd, 0); + } + pthread_mutex_unlock(&this->dvbsub_osd_mutex); + +} + +static void spudec_discontinuity (spu_decoder_t * this_gen) +{ + /* do nothing */ +} + +static void spudec_dispose (spu_decoder_t * this_gen) +{ + dvb_spu_decoder_t *this = (dvb_spu_decoder_t *) this_gen; + int i; + + pthread_cancel(this->dvbsub_timer_thread); + pthread_join(this->dvbsub_timer_thread, NULL); + pthread_mutex_destroy(&this->dvbsub_osd_mutex); + pthread_cond_destroy(&this->dvbsub_restart_timeout); + + if(this->spu_descriptor){ + free(this->spu_descriptor); + this->spu_descriptor=NULL; + } + + for ( i=0; idvbsub->regions[i].img ) + free( this->dvbsub->regions[i].img ); + if ( this->dvbsub->regions[i].osd ) + this->stream->osd_renderer->free_object( this->dvbsub->regions[i].osd ); + } + + if (this->pes_pkt) + free (this->pes_pkt); + + if (this->dvbsub) + free (this->dvbsub); + + free (this); +} + +static spu_decoder_t *dvb_spu_class_open_plugin (spu_decoder_class_t * class_gen, xine_stream_t * stream) +{ + + int i; + dvb_spu_decoder_t *this; + dvb_spu_class_t *class = (dvb_spu_class_t *) class_gen; + + this = (dvb_spu_decoder_t *) xine_xmalloc (sizeof (dvb_spu_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->class = class; + this->stream = stream; + + this->pes_pkt = xine_xmalloc (1024*65); + this->spu_descriptor = xine_xmalloc(sizeof(spu_dvb_descriptor_t)); + + this->dvbsub = xine_xmalloc (sizeof (dvbsub_func_t)); + + for (i = 0; i < MAX_REGIONS; i++) { + this->dvbsub->page.regions[i].is_visible = 0; + this->dvbsub->regions[i].img = NULL; + this->dvbsub->regions[i].osd = NULL; + this->dvbsub->regions[i].CLUT_id = 0; + } + + { + xine_spu_opacity_t opacity; + static const clut_t black = { 0, 0, 0, 0 }; + int t; + + _x_spu_get_opacity (this->stream->xine, &opacity); + t = _x_spu_calculate_opacity (&black, 0, &opacity); + + for (i = 0; i < MAX_REGIONS * 256; i++) + this->dvbsub->colours[i].foo = t; + } + + pthread_mutex_init(&this->dvbsub_osd_mutex, NULL); + pthread_cond_init(&this->dvbsub_restart_timeout, NULL); + this->dvbsub_hide_timeout.tv_nsec = 0; + this->dvbsub_hide_timeout.tv_sec = time(NULL); + pthread_create(&this->dvbsub_timer_thread, NULL, dvbsub_timer_func, this); + + return (spu_decoder_t *) this; +} + +static void *init_spu_decoder_plugin (xine_t * xine, void *data) +{ + + dvb_spu_class_t *this; + this = (dvb_spu_class_t *) xine_xmalloc (sizeof (dvb_spu_class_t)); + + this->class.open_plugin = dvb_spu_class_open_plugin; + this->class.identifier = "spudvb"; + this->class.description = N_("DVB subtitle decoder plugin"); + this->class.dispose = default_spu_decoder_class_dispose; + + this->xine = xine; + + return &this->class; +} + + +/* plugin catalog information */ +static uint32_t supported_types[] = { BUF_SPU_DVB, 0 }; + +static const decoder_info_t spudec_info = { + supported_types, /* supported types */ + 1 /* priority */ +}; + +const plugin_info_t xine_plugin_info[] EXPORTED = { +/* type, API, "name", version, special_info, init_function */ + {PLUGIN_SPU_DECODER, 17, "spudvb", XINE_VERSION_CODE, &spudec_info, + &init_spu_decoder_plugin}, + {PLUGIN_NONE, 0, "", 0, NULL, NULL} +}; diff --git a/src/spu_dec/sputext_decoder.c b/src/spu_dec/sputext_decoder.c new file mode 100644 index 000000000..12d1986bb --- /dev/null +++ b/src/spu_dec/sputext_decoder.c @@ -0,0 +1,992 @@ +/* + * Copyright (C) 2000-2004 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 + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#define LOG_MODULE "libsputext" +#define LOG_VERBOSE +/* +#define LOG +*/ + +#include +#include +#include +#include + +#define SUB_MAX_TEXT 5 /* lines */ +#define SUB_BUFSIZE 256 /* chars per line */ + +#define rgb2yuv(R,G,B) ((((((66*R+129*G+25*B+128)>>8)+16)<<8)|(((112*R-94*G-18*B+128)>>8)+128))<<8|(((-38*R-74*G+112*B+128)>>8)+128)) + +static uint32_t sub_palette[22]={ +/* RED */ + rgb2yuv(0,0,0), + rgb2yuv(0,0,0), + rgb2yuv(0,0,0), + rgb2yuv(0,0,0), + rgb2yuv(0,0,0), + rgb2yuv(0,0,0), + rgb2yuv(0,0,0), + rgb2yuv(50,10,10), + rgb2yuv(120,20,20), + rgb2yuv(185,50,50), + rgb2yuv(255,70,70), +/* BLUE */ + rgb2yuv(0,0,0), + rgb2yuv(0,0,0), + rgb2yuv(0,0,0), + rgb2yuv(0,0,0), + rgb2yuv(0,0,0), + rgb2yuv(0,0,0), + rgb2yuv(0,0,0), + rgb2yuv(0,30,50), + rgb2yuv(0,90,120), + rgb2yuv(0,140,185), + rgb2yuv(0,170,255) +}; + +static uint8_t sub_trans[22]={ + 0, 0, 3, 6, 8, 10, 12, 14, 15, 15, 15, + 0, 0, 3, 6, 8, 10, 12, 14, 15, 15, 15 +}; + +typedef enum { + SUBTITLE_SIZE_TINY = 0, + SUBTITLE_SIZE_SMALL, + SUBTITLE_SIZE_NORMAL, + SUBTITLE_SIZE_LARGE, + SUBTITLE_SIZE_VERY_LARGE, + SUBTITLE_SIZE_HUGE, + + SUBTITLE_SIZE_NUM /* number of values in enum */ +} subtitle_size; + +#define FONTNAME_SIZE 100 + +typedef struct sputext_class_s { + spu_decoder_class_t class; + + subtitle_size subtitle_size; /* size of subtitles */ + int vertical_offset; + char font[FONTNAME_SIZE]; /* subtitle font */ +#ifdef HAVE_FT2 + char font_ft[FILENAME_MAX]; /* subtitle font */ + int use_font_ft; /* use Freetype */ +#endif + char *src_encoding; /* encoding of subtitle file */ + int use_unscaled; /* use unscaled OSD if possible */ + + xine_t *xine; + +} sputext_class_t; + + +typedef struct sputext_decoder_s { + spu_decoder_t spu_decoder; + + sputext_class_t *class; + xine_stream_t *stream; + + int ogm; + int lines; + char text[SUB_MAX_TEXT][SUB_BUFSIZE]; + + /* below 3 variables are the same from class. use to detect + * when something changes. + */ + subtitle_size subtitle_size; /* size of subtitles */ + int vertical_offset; + char font[FILENAME_MAX]; /* subtitle font */ + char *buf_encoding; /* encoding of subtitle buffer */ + + int width; /* frame width */ + int height; /* frame height */ + int font_size; + int line_height; + int started; + int finished; + + osd_renderer_t *renderer; + osd_object_t *osd; + int current_osd_text; + uint32_t spu_palette[OVL_PALETTE_SIZE]; + uint8_t spu_trans[OVL_PALETTE_SIZE]; + + int64_t img_duration; + int64_t last_subtitle_end; /* no new subtitle before this vpts */ + int unscaled; /* use unscaled OSD */ + + int last_lines; /* number of lines of the previous subtitle */ +} sputext_decoder_t; + +static inline char *get_font (sputext_class_t *class) +{ +#ifdef HAVE_FT2 + return class->use_font_ft ? class->font_ft : class->font; +#else + return class->font; +#endif +} + +static void update_font_size (sputext_decoder_t *this, int force_update) { + static int sizes[SUBTITLE_SIZE_NUM] = { 16, 20, 24, 32, 48, 64 }; + + int y; + + if ((this->subtitle_size != this->class->subtitle_size) || + (this->vertical_offset != this->class->vertical_offset) || + force_update) { + + this->subtitle_size = this->class->subtitle_size; + this->vertical_offset = this->class->vertical_offset; + this->last_lines = 0; + + this->font_size = sizes[this->class->subtitle_size]; + + this->line_height = this->font_size + 10; + + y = this->height - (SUB_MAX_TEXT * this->line_height) - 5; + + if(((y - this->class->vertical_offset) >= 0) && ((y - this->class->vertical_offset) <= this->height)) + y -= this->class->vertical_offset; + + if( this->osd ) + this->renderer->free_object (this->osd); + + lprintf("new osd object, width %d, height %d*%d\n", this->width, SUB_MAX_TEXT, this->line_height); + this->osd = this->renderer->new_object (this->renderer, + this->width, + SUB_MAX_TEXT * this->line_height); + + this->renderer->set_font (this->osd, get_font (this->class), this->font_size); + this->renderer->set_position (this->osd, 0, y); + } +} + +static void update_output_size (sputext_decoder_t *this) { + int unscaled; + + unscaled = this->class->use_unscaled && + (this->stream->video_out->get_capabilities(this->stream->video_out) & + VO_CAP_UNSCALED_OVERLAY); + + if( unscaled != this->unscaled ) { + this->unscaled = unscaled; + this->width = 0; /* force update */ + } + + /* initialize decoder if needed */ + if( this->unscaled ) { + if( this->width != this->stream->video_out->get_property(this->stream->video_out, + VO_PROP_WINDOW_WIDTH) || + this->height != this->stream->video_out->get_property(this->stream->video_out, + VO_PROP_WINDOW_HEIGHT) || + !this->img_duration || !this->osd ) { + + int width = 0, height = 0; /* dummy */ + + this->stream->video_out->status(this->stream->video_out, NULL, + &width, &height, &this->img_duration ); + if( width && height ) { + + this->width = this->stream->video_out->get_property(this->stream->video_out, + VO_PROP_WINDOW_WIDTH); + this->height = this->stream->video_out->get_property(this->stream->video_out, + VO_PROP_WINDOW_HEIGHT); + + if(!this->osd || (this->width && this->height)) { + this->renderer = this->stream->osd_renderer; + + update_font_size (this, 1); + } + } + } + } else { + if( !this->width || !this->height || !this->img_duration || !this->osd ) { + + this->width = 0; + this->height = 0; + + this->stream->video_out->status(this->stream->video_out, NULL, + &this->width, &this->height, &this->img_duration ); + + if(!this->osd || ( this->width && this->height)) { + this->renderer = this->stream->osd_renderer; + + update_font_size (this, 1); + } + } + } +} + +static int parse_utf8_size(const void *buf) +{ + const uint8_t *c = buf; + if ( c[0]<0x80 ) + return 1; + + if( c[1]==0 ) + return 1; + if ( (c[0]>=0xC2 && c[0]<=0xDF) && (c[1]>=0x80 && c[1]<=0xBF) ) + return 2; + + if( c[2]==0 ) + return 2; + else if ( c[0]==0xE0 && (c[1]>=0xA0 && c[1]<=0xBF) && (c[2]>=0x80 && c[1]<=0xBF) ) + return 3; + else if ( (c[0]>=0xE1 && c[0]<=0xEC) && (c[1]>=0x80 && c[1]<=0xBF) && (c[2]>=0x80 && c[1]<=0xBF) ) + return 3; + else if ( c[0]==0xED && (c[1]>=0x80 && c[1]<=0x9F) && (c[2]>=0x80 && c[1]<=0xBF) ) + return 3; + else if ( c[0]==0xEF && (c[1]>=0xA4 && c[1]<=0xBF) && (c[2]>=0x80 && c[1]<=0xBF) ) + return 3; + else + return 1; +} + +static int ogm_render_line_internal(sputext_decoder_t *this, int x, int y, const char *text, int render) +{ + int i = 0, w, dummy; + char letter[5]={0, 0, 0, 0, 0}; + const char *encoding = this->buf_encoding ? this->buf_encoding + : this->class->src_encoding; + int shift, isutf8 = !strcmp(encoding, "utf-8"); + size_t length = strlen (text); + + while (i <= length) { + switch (text[i]) { + case '<': + if (!strncmp("", text+i, 3)) { + /* enable Bold color */ + if (render) + this->current_osd_text = OSD_TEXT2; + i=i+3; + break; + } else if (!strncmp("", text+i, 4)) { + /* disable BOLD */ + if (render) + this->current_osd_text = OSD_TEXT1; + i=i+4; + break; + } else if (!strncmp("", text+i, 3)) { + /* enable italics color */ + if (render) + this->current_osd_text = OSD_TEXT3; + i=i+3; + break; + } else if (!strncmp("", text+i, 4)) { + /* disable italics */ + if (render) + this->current_osd_text = OSD_TEXT1; + i=i+4; + break; + } else if (!strncmp("", text+i, 6)) { + /*Do somethink to disable typing + fixme - no teststreams*/ + i=i+6; + break; + } else if (!strncmp("", text+i, 7)) { + /*Do somethink to enable typing + fixme - no teststreams*/ + i=i+7; + break; + } + default: + shift = isutf8 ? parse_utf8_size (&text[i]) : 1; + memcpy(letter,&text[i],shift); + letter[shift]=0; + + if (render) + this->renderer->render_text(this->osd, x, y, letter, this->current_osd_text); + this->renderer->get_text_size(this->osd, letter, &w, &dummy); + x=x+w; + i+=shift; + } + } + return x; +} + +static inline int ogm_get_width(sputext_decoder_t *this, char* text) { + return ogm_render_line_internal (this, 0, 0, text, 0); +} + +static inline void ogm_render_line(sputext_decoder_t *this, int x, int y, char* text) { + ogm_render_line_internal (this, x, y, text, 1); +} + +static void draw_subtitle(sputext_decoder_t *this, int64_t sub_start, int64_t sub_end ) { + + int line, y; + int font_size; + char *font; + + _x_assert(this->renderer != NULL); + if ( ! this->renderer ) + return; + + update_font_size(this, 0); + + font = get_font (this->class); + if( strcmp(this->font, font) ) { + strncpy(this->font, font, FILENAME_MAX); + this->font[FILENAME_MAX - 1] = '\0'; + this->renderer->set_font (this->osd, font, this->font_size); + } + + font_size = this->font_size; + if (this->buf_encoding) + this->renderer->set_encoding(this->osd, this->buf_encoding); + else + this->renderer->set_encoding(this->osd, this->class->src_encoding); + + for (line = 0; line < this->lines; line++) /* first, check lenghts and word-wrap if needed */ + { + int w; + w = ogm_get_width( this, this->text[line]); + if( w > this->width ) { /* line is too long */ + int chunks=(int)(w/this->width)+(w%this->width?1:0); + if( this->lines+chunks <= SUB_MAX_TEXT && chunks>1 ) { /* try adding newlines while keeping existing ones */ + int a; + xprintf(this->stream->xine, XINE_VERBOSITY_DEBUG,"Partial subtitle line splitting in %i chunks\n",chunks); + for(a=this->lines-1;a>=0;a--) { + if(a>line) /* lines after the too-long one */ + memcpy(this->text[a+chunks-1],this->text[a],SUB_BUFSIZE); + else if(a==line) { /* line to be splitted */ + int b,len=strlen(this->text[line]); + char *p=this->text[line]; + for(b=0;btext[line+b],p,SUB_BUFSIZE); + this->text[line+b][SUB_BUFSIZE - 1] = '\0'; + } else { + for(c=p+(int)(len/chunks)+(len%chunks?1:0);*c!=' ' && c>p && c!='\0';c--); + if(*c==' ') { + *c='\0'; + if(b) { /* we are reading something that has to be moved to another line */ + strncpy(this->text[line+b],p,SUB_BUFSIZE); + this->text[line+b][SUB_BUFSIZE - 1] = '\0'; + } + p=c+1; + } + } + } + } + } + this->lines+=chunks-1; + } else { /* regenerate all the lines to find something that better fits */ + char buf[SUB_BUFSIZE*SUB_MAX_TEXT]; + int a,w,chunks; + buf[0]='\0'; + for(a=0;alines;a++) { + if(a) { + int len=strlen(buf); + buf[len]=' '; + buf[len+1]='\0'; + } + strcat(buf,this->text[a]); + } + w = ogm_get_width( this, buf); + chunks=(int)(w/this->width)+(w%this->width?1:0); + xprintf (this->stream->xine, XINE_VERBOSITY_DEBUG, "Complete subtitle line splitting in %i chunks\n",chunks); + if(chunks<=SUB_MAX_TEXT) {/* if the length is over than SUB_MAX_TEXT*this->width nothing can be done */ + int b,len=strlen(buf); + char *p=buf; + for(b=0;btext[b],p,SUB_BUFSIZE); + this->text[b][SUB_BUFSIZE - 1] = '\0'; + } else { + for(c=p+(int)(len/chunks)+(len%chunks?1:0);*c!=' ' && c>p && c!='\0';c--); + if(*c==' ') { + *c='\0'; + strncpy(this->text[b],p,SUB_BUFSIZE); + this->text[b][SUB_BUFSIZE - 1] = '\0'; + p=c+1; + } + } + } + this->lines=chunks; + } else + xprintf (this->stream->xine, XINE_VERBOSITY_DEBUG, "Subtitle too long to be splited\n"); + line=this->lines; + } + } + } + + font_size = this->font_size; + if (this->buf_encoding) + this->renderer->set_encoding(this->osd, this->buf_encoding); + else + this->renderer->set_encoding(this->osd, this->class->src_encoding); + + for (line = 0; line < this->lines; line++) /* first, check lenghts and word-wrap if needed */ + { + int w; + w = ogm_get_width( this, this->text[line]); + if( w > this->width ) { /* line is too long */ + int chunks=(int)(w/this->width)+(w%this->width?1:0); + if( this->lines+chunks <= SUB_MAX_TEXT && chunks>1 ) { /* try adding newlines while keeping existing ones */ + int a; + xprintf(this->stream->xine, XINE_VERBOSITY_DEBUG,"Partial subtitle line splitting in %i chunks\n",chunks); + for(a=this->lines-1;a>=0;a--) { + if(a>line) /* lines after the too-long one */ + memcpy(this->text[a+chunks-1],this->text[a],SUB_BUFSIZE); + else if(a==line) { /* line to be splitted */ + int b,len=strlen(this->text[line]); + char *p=this->text[line]; + for(b=0;btext[line+b],p,SUB_BUFSIZE); + this->text[line+b][SUB_BUFSIZE - 1] = '\0'; + } else { + for(c=p+(int)(len/chunks)+(len%chunks?1:0);*c!=' ' && c>p && c!='\0';c--); + if(*c==' ') { + *c='\0'; + if(b) { /* we are reading something that has to be moved to another line */ + strncpy(this->text[line+b],p,SUB_BUFSIZE); + this->text[line+b][SUB_BUFSIZE - 1] = '\0'; + } + p=c+1; + } + } + } + } + } + this->lines+=chunks-1; + } else { /* regenerate all the lines to find something that better fits */ + char buf[SUB_BUFSIZE*SUB_MAX_TEXT]; + int a,w,chunks; + buf[0]='\0'; + for(a=0;alines;a++) { + if(a) { + int len=strlen(buf); + buf[len]=' '; + buf[len+1]='\0'; + } + strcat(buf,this->text[a]); + } + w = ogm_get_width( this, buf); + chunks=(int)(w/this->width)+(w%this->width?1:0); + xprintf (this->stream->xine, XINE_VERBOSITY_DEBUG, "Complete subtitle line splitting in %i chunks\n",chunks); + if(chunks<=SUB_MAX_TEXT) {/* if the length is over than SUB_MAX_TEXT*this->width nothing can be done */ + int b,len=strlen(buf); + char *p=buf; + for(b=0;btext[b],p,SUB_BUFSIZE); + this->text[b][SUB_BUFSIZE - 1] = '\0'; + } else { + for(c=p+(int)(len/chunks)+(len%chunks?1:0);*c!=' ' && c>p && c!='\0';c--); + if(*c==' ') { + *c='\0'; + strncpy(this->text[b],p,SUB_BUFSIZE); + this->text[b][SUB_BUFSIZE - 1] = '\0'; + p=c+1; + } + } + } + this->lines=chunks; + } else + xprintf (this->stream->xine, XINE_VERBOSITY_DEBUG, "Subtitle too long to be splited\n"); + line=this->lines; + } + } + } + + if (this->last_lines) + this->renderer->filled_rect (this->osd, 0, this->line_height * (SUB_MAX_TEXT - this->last_lines), + this->width - 1, this->line_height * SUB_MAX_TEXT - 1, 0); + this->last_lines = this->lines; + y = (SUB_MAX_TEXT - this->lines) * this->line_height; + + for (line = 0; line < this->lines; line++) { + int w, x; + + while(1) { + w = ogm_get_width( this, this->text[line]); + x = (this->width - w) / 2; + + if( w > this->width && font_size > 16 ) { + font_size -= 4; + this->renderer->set_font (this->osd, get_font (this->class), font_size); + } else { + break; + } + } + + ogm_render_line(this, x, y + line*this->line_height, this->text[line]); + } + + if( font_size != this->font_size ) + this->renderer->set_font (this->osd, get_font (this->class), this->font_size); + + if( this->last_subtitle_end && sub_start < this->last_subtitle_end ) { + sub_start = this->last_subtitle_end; + } + this->last_subtitle_end = sub_end; + + this->renderer->set_text_palette (this->osd, -1, OSD_TEXT1); + this->renderer->get_palette(this->osd, this->spu_palette, this->spu_trans); + /* append some colors for colored typeface tag */ + memcpy(this->spu_palette+OSD_TEXT2, sub_palette, sizeof(sub_palette)); + memcpy(this->spu_trans+OSD_TEXT2, sub_trans, sizeof(sub_trans)); + this->renderer->set_palette(this->osd, this->spu_palette, this->spu_trans); + + if (this->unscaled) + this->renderer->show_unscaled (this->osd, sub_start); + else + this->renderer->show (this->osd, sub_start); + + this->renderer->hide (this->osd, sub_end); + + lprintf ("scheduling subtitle >%s< at %"PRId64" until %"PRId64", current time is %"PRId64"\n", + this->text[0], sub_start, sub_end, + this->stream->xine->clock->get_current_time (this->stream->xine->clock)); +} + + +static void spudec_decode_data (spu_decoder_t *this_gen, buf_element_t *buf) { + + sputext_decoder_t *this = (sputext_decoder_t *) this_gen; + int uses_time; + int32_t start, end, diff; + int64_t start_vpts, end_vpts; + int64_t spu_offset; + int i; + uint32_t *val; + char *str; + extra_info_t extra_info; + int master_status, slave_status; + int vo_discard; + + /* filter unwanted streams */ + if (buf->decoder_flags & BUF_FLAG_HEADER) { + return; + } + if (buf->decoder_flags & BUF_FLAG_PREVIEW) + return; + + if ((this->stream->spu_channel & 0x1f) != (buf->type & 0x1f)) + return; + + if ( (buf->decoder_flags & BUF_FLAG_SPECIAL) && + (buf->decoder_info[1] == BUF_SPECIAL_CHARSET_ENCODING) ) + this->buf_encoding = buf->decoder_info_ptr[2]; + else + this->buf_encoding = NULL; + + this->current_osd_text = OSD_TEXT1; + + if( (buf->type & 0xFFFF0000) == BUF_SPU_OGM ) { + + this->ogm = 1; + uses_time = 1; + val = (uint32_t * )buf->content; + start = *val++; + end = *val++; + str = (char *)val; + + if (!*str) return; + /* Empty ogm packets (as created by ogmmux) clears out old messages. We already respect the end time. */ + + this->lines = 0; + + i = 0; + while (*str && (this->lines < SUB_MAX_TEXT) && (i < SUB_BUFSIZE)) { + if (*str == '\r' || *str == '\n') { + if (i) { + this->text[ this->lines ][i] = 0; + this->lines++; + i = 0; + } + } else { + this->text[ this->lines ][i] = *str; + if (i < SUB_BUFSIZE-1) + i++; + } + str++; + } + if (i == SUB_BUFSIZE) + i--; + + if (i) { + this->text[ this->lines ][i] = 0; + this->lines++; + } + + } else { + + this->ogm = 0; + val = (uint32_t * )buf->content; + + this->lines = *val++; + uses_time = *val++; + start = *val++; + end = *val++; + str = (char *)val; + for (i = 0; i < this->lines; i++, str += strlen(str) + 1) { + strncpy( this->text[i], str, SUB_BUFSIZE - 1); + this->text[i][SUB_BUFSIZE - 1] = '\0'; + } + + } + + xprintf(this->class->xine, XINE_VERBOSITY_DEBUG, + "libsputext: decoder data [%s]\n", this->text[0]); + xprintf(this->class->xine, XINE_VERBOSITY_DEBUG, + "libsputext: mode %d timing %d->%d\n", uses_time, start, end); + + if( end <= start ) { + xprintf(this->class->xine, XINE_VERBOSITY_DEBUG, + "libsputext: discarding subtitle with invalid timing\n"); + return; + } + + spu_offset = this->stream->master->metronom->get_option (this->stream->master->metronom, + METRONOM_SPU_OFFSET); + if( uses_time ) { + start += (spu_offset / 90); + end += (spu_offset / 90); + } else { + if( this->osd && this->img_duration ) { + start += spu_offset / this->img_duration; + end += spu_offset / this->img_duration; + } + } + + while( !this->finished ) { + + master_status = xine_get_status (this->stream->master); + slave_status = xine_get_status (this->stream); + vo_discard = this->stream->video_out->get_property(this->stream->video_out, + VO_PROP_DISCARD_FRAMES); + + _x_get_current_info (this->stream->master, &extra_info, sizeof(extra_info) ); + + lprintf("master: %d slave: %d input_normpos: %d vo_discard: %d\n", + master_status, slave_status, extra_info.input_normpos, vo_discard); + + if( !this->started && (master_status == XINE_STATUS_PLAY && + slave_status == XINE_STATUS_PLAY && + extra_info.input_normpos) ) { + lprintf("started\n"); + + this->width = this->height = 0; + + update_output_size( this ); + if( this->width && this->height ) { + this->started = 1; + } + } + + if( this->started ) { + + if( master_status != XINE_STATUS_PLAY || + slave_status != XINE_STATUS_PLAY || + vo_discard ) { + lprintf("finished\n"); + + this->width = this->height = 0; + this->finished = 1; + return; + } + + if( this->osd ) { + + /* try to use frame number mode */ + if( !uses_time && extra_info.frame_number ) { + + diff = end - extra_info.frame_number; + + /* discard old subtitles */ + if( diff < 0 ) { + xprintf(this->class->xine, XINE_VERBOSITY_DEBUG, + "libsputext: discarding old subtitle\n"); + return; + } + + diff = start - extra_info.frame_number; + + start_vpts = extra_info.vpts + diff * this->img_duration; + end_vpts = start_vpts + (end-start) * this->img_duration; + + } else { + + if( !uses_time ) { + start = start * this->img_duration / 90; + end = end * this->img_duration / 90; + uses_time = 1; + } + + diff = end - extra_info.input_time; + + /* discard old subtitles */ + if( diff < 0 ) { + xprintf(this->class->xine, XINE_VERBOSITY_DEBUG, + "libsputext: discarding old subtitle\n"); + return; + } + + diff = start - extra_info.input_time; + + start_vpts = extra_info.vpts + diff * 90; + end_vpts = start_vpts + (end-start) * 90; + } + + _x_spu_decoder_sleep(this->stream, start_vpts); + update_output_size( this ); + draw_subtitle(this, start_vpts, end_vpts); + + return; + } + } + + if (_x_spu_decoder_sleep(this->stream, 0)) + xine_usec_sleep (50000); + else + return; + } +} + + +static void spudec_reset (spu_decoder_t *this_gen) { + sputext_decoder_t *this = (sputext_decoder_t *) this_gen; + + lprintf("i guess we just seeked\n"); + this->width = this->height = 0; + this->started = this->finished = 0; + this->last_subtitle_end = 0; +} + +static void spudec_discontinuity (spu_decoder_t *this_gen) { + /* sputext_decoder_t *this = (sputext_decoder_t *) this_gen; */ + +} + +static void spudec_dispose (spu_decoder_t *this_gen) { + sputext_decoder_t *this = (sputext_decoder_t *) this_gen; + + if (this->osd) { + this->renderer->free_object (this->osd); + this->osd = NULL; + } + free(this); +} + +static void update_vertical_offset(void *class_gen, xine_cfg_entry_t *entry) +{ + sputext_class_t *class = (sputext_class_t *)class_gen; + + class->vertical_offset = entry->num_value; +} + +static void update_osd_font(void *class_gen, xine_cfg_entry_t *entry) +{ + sputext_class_t *class = (sputext_class_t *)class_gen; + + strncpy(class->font, entry->str_value, FONTNAME_SIZE); + class->font[FONTNAME_SIZE - 1] = '\0'; + + xprintf(class->xine, XINE_VERBOSITY_DEBUG, "libsputext: spu_font = %s\n", class->font ); +} + +#ifdef HAVE_FT2 +static void update_osd_font_ft(void *class_gen, xine_cfg_entry_t *entry) +{ + sputext_class_t *class = (sputext_class_t *)class_gen; + + strncpy(class->font_ft, entry->str_value, FILENAME_MAX); + class->font_ft[FILENAME_MAX - 1] = '\0'; + + xprintf(class->xine, XINE_VERBOSITY_DEBUG, "libsputext: spu_font_ft = %s\n", class->font_ft); +} + +static void update_osd_use_font_ft(void *class_gen, xine_cfg_entry_t *entry) +{ + sputext_class_t *class = (sputext_class_t *)class_gen; + + class->use_font_ft = entry->num_value; + + xprintf(class->xine, XINE_VERBOSITY_DEBUG, "libsputext: spu_use_font_ft = %d\n", class->use_font_ft); +} +#endif + +static void update_subtitle_size(void *class_gen, xine_cfg_entry_t *entry) +{ + sputext_class_t *class = (sputext_class_t *)class_gen; + + class->subtitle_size = entry->num_value; +} + +static void update_use_unscaled(void *class_gen, xine_cfg_entry_t *entry) +{ + sputext_class_t *class = (sputext_class_t *)class_gen; + + class->use_unscaled = entry->num_value; +} + +static spu_decoder_t *sputext_class_open_plugin (spu_decoder_class_t *class_gen, xine_stream_t *stream) { + + sputext_class_t *class = (sputext_class_t *)class_gen; + sputext_decoder_t *this ; + + this = (sputext_decoder_t *) xine_xmalloc (sizeof (sputext_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.get_interact_info = NULL; + this->spu_decoder.set_button = NULL; + this->spu_decoder.dispose = spudec_dispose; + + this->class = class; + this->stream = stream; + + return (spu_decoder_t *) this; +} + +static void sputext_class_dispose (spu_decoder_class_t *class_gen) { + sputext_class_t *this = (sputext_class_t *)class_gen; + + this->xine->config->unregister_callback(this->xine->config, + "subtitles.separate.src_encoding"); + this->xine->config->unregister_callback(this->xine->config, + "subtitles.separate.subtitle_size"); + this->xine->config->unregister_callback(this->xine->config, + "subtitles.separate.vertical_offset"); + this->xine->config->unregister_callback(this->xine->config, + "subtitles.separate.use_unscaled_osd"); + free (this); +} + +static void update_src_encoding(void *class_gen, xine_cfg_entry_t *entry) +{ + sputext_class_t *class = (sputext_class_t *)class_gen; + + class->src_encoding = entry->str_value; + xprintf(class->xine, XINE_VERBOSITY_DEBUG, "libsputext: spu_src_encoding = %s\n", class->src_encoding ); +} + +static void *init_spu_decoder_plugin (xine_t *xine, void *data) { + + static const char *const subtitle_size_strings[] = { + "tiny", "small", "normal", "large", "very large", "huge", NULL + }; + sputext_class_t *this ; + + lprintf("init class\n"); + + this = (sputext_class_t *) xine_xmalloc (sizeof (sputext_class_t)); + + this->class.open_plugin = sputext_class_open_plugin; + this->class.identifier = "sputext"; + this->class.description = N_("external subtitle decoder plugin"); + this->class.dispose = sputext_class_dispose; + + this->xine = xine; + + this->subtitle_size = xine->config->register_enum(xine->config, + "subtitles.separate.subtitle_size", + 1, + subtitle_size_strings, + _("subtitle size"), + _("You can adjust the subtitle size here. The setting will " + "be evaluated relative to the window size."), + 0, update_subtitle_size, this); + this->vertical_offset = xine->config->register_num(xine->config, + "subtitles.separate.vertical_offset", + 0, + _("subtitle vertical offset"), + _("You can adjust the vertical position of the subtitle. " + "The setting will be evaluated relative to the window size."), + 0, update_vertical_offset, this); + strncpy(this->font, xine->config->register_string(xine->config, + "subtitles.separate.font", + "sans", + _("font for subtitles"), + _("A font from the xine font directory to be used for the " + "subtitle text."), + 10, update_osd_font, this), FONTNAME_SIZE); + this->font[FONTNAME_SIZE - 1] = '\0'; +#ifdef HAVE_FT2 + strncpy(this->font_ft, xine->config->register_filename(xine->config, + "subtitles.separate.font_freetype", + "", XINE_CONFIG_STRING_IS_FILENAME, + _("font for subtitles"), + _("An outline font file (e.g. a .ttf) to be used for the subtitle text."), + 10, update_osd_font_ft, this), FILENAME_MAX); + this->font_ft[FILENAME_MAX - 1] = '\0'; + this->use_font_ft = xine->config->register_bool(xine->config, + "subtitles.separate.font_use_freetype", + 0, + _("whether to use a freetype font"), + NULL, + 10, update_osd_use_font_ft, this); +#endif + this->src_encoding = xine->config->register_string(xine->config, + "subtitles.separate.src_encoding", + xine_guess_spu_encoding(), + _("encoding of the subtitles"), + _("The encoding of the subtitle text in the stream. This setting " + "is used to render non-ASCII characters correctly. If non-ASCII " + "characters are not displayed as you expect, ask the " + "creator of the subtitles what encoding was used."), + 10, update_src_encoding, this); + this->use_unscaled = xine->config->register_bool(xine->config, + "subtitles.separate.use_unscaled_osd", + 1, + _("use unscaled OSD if possible"), + _("The unscaled OSD will be rendered independently of the video " + "frame and will always be sharp, even if the video is magnified. " + "This will look better, but does not work with all graphics " + "hardware. The alternative is the scaled OSD, which will become " + "blurry, if you enlarge a low resolution video to fullscreen, but " + "it works with all graphics cards."), + 10, update_use_unscaled, this); + + return &this->class; +} + + +/* plugin catalog information */ +static uint32_t supported_types[] = { BUF_SPU_TEXT, BUF_SPU_OGM, 0 }; + +static const decoder_info_t spudec_info = { + supported_types, /* supported types */ + 1 /* priority */ +}; + +const plugin_info_t xine_plugin_info[] EXPORTED = { + /* type, API, "name", version, special_info, init_function */ + { PLUGIN_SPU_DECODER | PLUGIN_MUST_PRELOAD, 17, "sputext", XINE_VERSION_CODE, &spudec_info, &init_spu_decoder_plugin }, + { PLUGIN_NONE, 0, "", 0, NULL, NULL } +}; diff --git a/src/spu_dec/sputext_demuxer.c b/src/spu_dec/sputext_demuxer.c new file mode 100644 index 000000000..270894f50 --- /dev/null +++ b/src/spu_dec/sputext_demuxer.c @@ -0,0 +1,1489 @@ +/* + * 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 + * + * code based on old libsputext/xine_decoder.c + * + * code based on mplayer module: + * + * Subtitle reader with format autodetection + * + * Written by laaz + * Some code cleanup & realloc() by A'rpi/ESP-team + * dunnowhat sub format by szabi + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include +#include +#include +#include +#include +#include + +#define LOG_MODULE "demux_sputext" +#define LOG_VERBOSE +/* +#define LOG +*/ + +#include +#include +#include + +#define ERR (void *)-1 +#define SUB_MAX_TEXT 5 +#define SUB_BUFSIZE 1024 +#define LINE_LEN 1000 +#define LINE_LEN_QUOT "1000" + +/* + * Demuxer typedefs + */ + +typedef struct { + + int lines; + + long start; /* csecs */ + long end; /* csecs */ + + char *text[SUB_MAX_TEXT]; + +} subtitle_t; + + +typedef struct { + + demux_plugin_t demux_plugin; + xine_stream_t *stream; + input_plugin_t *input; + + int status; + + char buf[SUB_BUFSIZE]; + off_t buflen; + + float mpsub_position; + + int uses_time; + int errs; + subtitle_t *subtitles; + int num; /* number of subtitle structs */ + int cur; /* current subtitle */ + int format; /* constants see below */ + char next_line[SUB_BUFSIZE]; /* a buffer for next line read from file */ + +} demux_sputext_t; + +typedef struct demux_sputext_class_s { + + demux_class_t demux_class; + + int max_timeout; /* default timeout of hidding subtitles */ + +} demux_sputext_class_t; + +/* + * Demuxer code start + */ + +#define FORMAT_UNKNOWN -1 +#define FORMAT_MICRODVD 0 +#define FORMAT_SUBRIP 1 +#define FORMAT_SUBVIEWER 2 +#define FORMAT_SAMI 3 +#define FORMAT_VPLAYER 4 +#define FORMAT_RT 5 +#define FORMAT_SSA 6 /* Sub Station Alpha */ +#define FORMAT_PJS 7 +#define FORMAT_MPSUB 8 +#define FORMAT_AQTITLE 9 +#define FORMAT_JACOBSUB 10 +#define FORMAT_SUBVIEWER2 11 +#define FORMAT_SUBRIP09 12 +#define FORMAT_MPL2 13 /*Mplayer sub 2 ?*/ + +static int eol(char p) { + return (p=='\r' || p=='\n' || p=='\0'); +} + +static inline void trail_space(char *s) { + int i; + while (isspace(*s)) { + char *copy = s; + do { + copy[0] = copy[1]; + copy++; + } while(*copy); + } + i = strlen(s) - 1; + while (i > 0 && isspace(s[i])) + s[i--] = '\0'; +} + +/* + * Reimplementation of fgets() using the input->read() method. + */ +static char *read_line_from_input(demux_sputext_t *this, char *line, off_t len) { + off_t nread = 0; + char *s; + int linelen; + + if ((len - this->buflen) > 512) { + if((nread = this->input->read(this->input, + &this->buf[this->buflen], len - this->buflen)) < 0) { + xprintf(this->stream->xine, XINE_VERBOSITY_DEBUG, "read failed.\n"); + return NULL; + } + } + + this->buflen += nread; + this->buf[this->buflen] = '\0'; + + s = strchr(this->buf, '\n'); + + if (line && (s || this->buflen)) { + + linelen = s ? (s - this->buf) + 1 : this->buflen; + + memcpy(line, this->buf, linelen); + line[linelen] = '\0'; + + memmove(this->buf, &this->buf[linelen], SUB_BUFSIZE - linelen); + this->buflen -= linelen; + + return line; + } + + return NULL; +} + + +static subtitle_t *sub_read_line_sami(demux_sputext_t *this, subtitle_t *current) { + + static char line[LINE_LEN + 1]; + static char *s = NULL; + char text[LINE_LEN + 1], *p, *q; + int state; + + p = NULL; + current->lines = current->start = 0; + current->end = -1; + state = 0; + + /* read the first line */ + if (!s) + if (!(s = read_line_from_input(this, line, LINE_LEN))) return 0; + + do { + switch (state) { + + case 0: /* find "START=" */ + s = strstr (s, "Start="); + if (s) { + current->start = strtol (s + 6, &s, 0) / 10; + state = 1; continue; + } + break; + + case 1: /* find "" */ + if ((s = strchr (s, '>'))) { s++; state = 3; p = text; continue; } + break; + + case 3: /* get all text until '<' appears */ + if (*s == '\0') { break; } + else if (*s == '<') { state = 4; } + else if (!strncasecmp (s, " ", 6)) { *p++ = ' '; s += 6; } + else if (*s == '\r') { s++; } + else if (!strncasecmp (s, "
", 4) || *s == '\n') { + *p = '\0'; p = text; trail_space (text); + if (text[0] != '\0') + current->text[current->lines++] = strdup (text); + if (*s == '\n') s++; else s += 4; + } + else *p++ = *s++; + continue; + + case 4: /* get current->end or skip */ + q = strstr (s, "Start="); + if (q) { + current->end = strtol (q + 6, &q, 0) / 10 - 1; + *p = '\0'; trail_space (text); + if (text[0] != '\0') + current->text[current->lines++] = strdup (text); + if (current->lines > 0) { state = 99; break; } + state = 0; continue; + } + s = strchr (s, '>'); + if (s) { s++; state = 3; continue; } + break; + } + + /* read next line */ + if (state != 99 && !(s = read_line_from_input (this, line, LINE_LEN))) + return 0; + + } while (state != 99); + + return current; +} + + +static char *sub_readtext(char *source, char **dest) { + int len=0; + char *p=source; + + while ( !eol(*p) && *p!= '|' ) { + p++,len++; + } + + *dest= (char *)xine_xmalloc (len+1); + if (!dest) + return ERR; + + strncpy(*dest, source, len); + (*dest)[len]=0; + + while (*p=='\r' || *p=='\n' || *p=='|') + p++; + + if (*p) return p; /* not-last text field */ + else return NULL; /* last text field */ +} + +static subtitle_t *sub_read_line_microdvd(demux_sputext_t *this, subtitle_t *current) { + + char line[LINE_LEN + 1]; + char line2[LINE_LEN + 1]; + char *p, *next; + int i; + + memset (current, 0, sizeof(subtitle_t)); + + current->end=-1; + do { + if (!read_line_from_input (this, line, LINE_LEN)) return NULL; + } while ((sscanf (line, "{%ld}{}%" LINE_LEN_QUOT "[^\r\n]", &(current->start), line2) !=2) && + (sscanf (line, "{%ld}{%ld}%" LINE_LEN_QUOT "[^\r\n]", &(current->start), &(current->end),line2) !=3) + ); + + p=line2; + + next=p, i=0; + while ((next =sub_readtext (next, &(current->text[i])))) { + if (current->text[i]==ERR) return ERR; + i++; + if (i>=SUB_MAX_TEXT) { + xprintf (this->stream->xine, XINE_VERBOSITY_DEBUG, "Too many lines in a subtitle\n"); + current->lines=i; + return current; + } + } + current->lines= ++i; + + return current; +} + +static subtitle_t *sub_read_line_subviewer(demux_sputext_t *this, subtitle_t *current) { + + char line[LINE_LEN + 1]; + int a1,a2,a3,a4,b1,b2,b3,b4; + char *p=NULL, *q=NULL; + int len; + + memset (current, 0, sizeof(subtitle_t)); + + while (1) { + if (!read_line_from_input(this, line, LINE_LEN)) return NULL; + if (sscanf (line, "%d:%d:%d.%d,%d:%d:%d.%d",&a1,&a2,&a3,&a4,&b1,&b2,&b3,&b4) < 8) { + if (sscanf (line, "%d:%d:%d,%d,%d:%d:%d,%d",&a1,&a2,&a3,&a4,&b1,&b2,&b3,&b4) < 8) + continue; + } + current->start = a1*360000+a2*6000+a3*100+a4; + current->end = b1*360000+b2*6000+b3*100+b4; + + if (!read_line_from_input(this, line, LINE_LEN)) + return NULL; + + p=q=line; + for (current->lines=1; current->lines <= SUB_MAX_TEXT; current->lines++) { + for (q=p,len=0; *p && *p!='\r' && *p!='\n' && *p!='|' && strncasecmp(p,"[br]",4); p++,len++); + current->text[current->lines-1]=(char *)xine_xmalloc (len+1); + if (!current->text[current->lines-1]) return ERR; + strncpy (current->text[current->lines-1], q, len); + current->text[current->lines-1][len]='\0'; + if (!*p || *p=='\r' || *p=='\n') break; + if (*p=='[') while (*p++!=']'); + if (*p=='|') p++; + } + if (current->lines > SUB_MAX_TEXT) current->lines = SUB_MAX_TEXT; + break; + } + return current; +} + +static subtitle_t *sub_read_line_subrip(demux_sputext_t *this,subtitle_t *current) { + char line[LINE_LEN + 1]; + int a1,a2,a3,a4,b1,b2,b3,b4; + int i,end_sub; + + memset(current,0,sizeof(subtitle_t)); + do { + if(!read_line_from_input(this,line,LINE_LEN)) + return NULL; + i = sscanf(line,"%d:%d:%d%*[,.]%d --> %d:%d:%d%*[,.]%d",&a1,&a2,&a3,&a4,&b1,&b2,&b3,&b4); + } while(i < 8); + current->start = a1*360000+a2*6000+a3*100+a4/10; + current->end = b1*360000+b2*6000+b3*100+b4/10; + i=0; + end_sub=0; + do { + char *p; /* pointer to the curently read char */ + char temp_line[SUB_BUFSIZE]; /* subtitle line that will be transfered to current->text[i] */ + int temp_index; /* ... and its index wich 'points' to the first EMPTY place -> last read char is at temp_index-1 if temp_index>0 */ + temp_line[SUB_BUFSIZE-1]='\0'; /* just in case... */ + if(!read_line_from_input(this,line,LINE_LEN)) { + if(i) + break; /* if something was read, transmit it */ + else + return NULL; /* if not, repport EOF */ + } + for(temp_index=0,p=line;*p!='\0' && !end_sub && temp_index0) { + if(temp_index==SUB_BUFSIZE) + xprintf (this->stream->xine, XINE_VERBOSITY_DEBUG, "Too many characters in a subtitle line\n"); + if(temp_line[temp_index-1]=='\0' || temp_index==SUB_BUFSIZE) { + if(temp_index>1) { /* more than 1 char (including '\0') -> that is a valid one */ + current->text[i]=(char *)xine_xmalloc(temp_index); + if(!current->text[i]) + return ERR; + strncpy(current->text[i],temp_line,temp_index); /* temp_index<=SUB_BUFSIZE is always true here */ + i++; + temp_index=0; + } else + end_sub=1; + } + } + } + } while(i=SUB_MAX_TEXT) + xprintf (this->stream->xine, XINE_VERBOSITY_DEBUG, "Too many lines in a subtitle\n"); + current->lines=i; + return current; +} + +static subtitle_t *sub_read_line_vplayer(demux_sputext_t *this,subtitle_t *current) { + char line[LINE_LEN + 1]; + int a1,a2,a3,b1,b2,b3; + char *p=NULL, *next, *p2; + int i; + + memset (current, 0, sizeof(subtitle_t)); + + while (!current->text[0]) { + if( this->next_line[0] == '\0' ) { /* if the buffer is empty.... */ + if( !read_line_from_input(this, line, LINE_LEN) ) return NULL; + } else { + /* ... get the current line from buffer. */ + strncpy( line, this->next_line, LINE_LEN); + line[LINE_LEN] = '\0'; /* I'm scared. This makes me feel better. */ + this->next_line[0] = '\0'; /* mark the buffer as empty. */ + } + /* Initialize buffer with next line */ + if( ! read_line_from_input( this, this->next_line, LINE_LEN) ) { + this->next_line[0] = '\0'; + return NULL; + } + if( (sscanf( line, "%d:%d:%d:", &a1, &a2, &a3) < 3) || + (sscanf( this->next_line, "%d:%d:%d:", &b1, &b2, &b3) < 3) ) + continue; + current->start = a1*360000+a2*6000+a3*100; + current->end = b1*360000+b2*6000+b3*100; + if ((current->end - current->start) > LINE_LEN) + current->end = current->start + LINE_LEN; /* not too long though. */ + /* teraz czas na wkopiowanie stringu */ + p=line; + /* finds the body of the subtitle_t */ + for (i=0; i<3; i++){ + p2=strchr( p, ':'); + if( p2 == NULL ) break; + p=p2+1; + } + + next=p; + i=0; + while( (next = sub_readtext( next, &(current->text[i]))) ) { + if (current->text[i]==ERR) + return ERR; + i++; + if (i>=SUB_MAX_TEXT) { + xprintf (this->stream->xine, XINE_VERBOSITY_DEBUG, "Too many lines in a subtitle\n"); + current->lines=i; + return current; + } + } + current->lines=++i; + } + return current; +} + +static subtitle_t *sub_read_line_rt(demux_sputext_t *this,subtitle_t *current) { + /* + * TODO: This format uses quite rich (sub/super)set of xhtml + * I couldn't check it since DTD is not included. + * WARNING: full XML parses can be required for proper parsing + */ + char line[LINE_LEN + 1]; + int a1,a2,a3,a4,b1,b2,b3,b4; + char *p=NULL,*next=NULL; + int i,len,plen; + + memset (current, 0, sizeof(subtitle_t)); + + while (!current->text[0]) { + if (!read_line_from_input(this, line, LINE_LEN)) return NULL; + /* + * TODO: it seems that format of time is not easily determined, it may be 1:12, 1:12.0 or 0:1:12.0 + * to describe the same moment in time. Maybe there are even more formats in use. + */ + if ((len=sscanf (line, "