summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
m---------common0
-rw-r--r--ext/resindvd/gstmpegdemux.c40
-rw-r--r--gst/dvdspu/Makefile.am4
-rw-r--r--gst/dvdspu/gstdvdspu-render.c515
-rw-r--r--gst/dvdspu/gstdvdspu.c827
-rw-r--r--gst/dvdspu/gstdvdspu.h132
-rw-r--r--gst/dvdspu/gstspu-common.h56
-rw-r--r--gst/dvdspu/gstspu-pgs.c762
-rw-r--r--gst/dvdspu/gstspu-pgs.h106
-rw-r--r--gst/dvdspu/gstspu-vobsub-render.c536
-rw-r--r--gst/dvdspu/gstspu-vobsub.c513
-rw-r--r--gst/dvdspu/gstspu-vobsub.h110
-rw-r--r--gst/mpegdemux/gstmpegdefs.h2
-rw-r--r--gst/mpegdemux/gstmpegdemux.c134
-rw-r--r--gst/mpegdemux/gstmpegdemux.h5
-rw-r--r--gst/mpegdemux/gstmpegtsdemux.c28
-rw-r--r--gst/mpegdemux/gstmpegtsdemux.h1
-rw-r--r--gst/mpegdemux/gstpesfilter.h2
-rw-r--r--gst/rtpmanager/rtpsource.c5
-rw-r--r--tests/check/Makefile.am2
20 files changed, 2609 insertions, 1171 deletions
diff --git a/common b/common
-Subproject 6ab11d17cb8e4d1ed755da7accac9630d567a09
+Subproject c57272152d5c40617fab305d839db1b1fefe0ea
diff --git a/ext/resindvd/gstmpegdemux.c b/ext/resindvd/gstmpegdemux.c
index b360bdcc..5ab26107 100644
--- a/ext/resindvd/gstmpegdemux.c
+++ b/ext/resindvd/gstmpegdemux.c
@@ -495,8 +495,10 @@ gst_flups_demux_send_segment_updates (GstFluPSDemux * demux,
if (stream->last_ts + stream->segment_thresh < new_time) {
#if 0
g_print ("Segment update to pad %s time %" GST_TIME_FORMAT " stop now %"
- GST_TIME_FORMAT "\n", GST_PAD_NAME (stream->pad),
- GST_TIME_ARGS (new_time), GST_TIME_ARGS (demux->src_segment.stop));
+ GST_TIME_FORMAT " last_stop %" GST_TIME_FORMAT "\n",
+ GST_PAD_NAME (stream->pad), GST_TIME_ARGS (new_time),
+ GST_TIME_ARGS (demux->src_segment.stop),
+ GST_TIME_ARGS (demux->src_segment.last_stop));
#endif
GST_DEBUG_OBJECT (demux,
"Segment update to pad %s time %" GST_TIME_FORMAT,
@@ -524,6 +526,10 @@ gst_flups_demux_send_segment_close (GstFluPSDemux * demux)
{
gint id;
GstEvent *event = NULL;
+ GstClockTime stop = demux->src_segment.stop;
+
+ if (demux->src_segment.last_stop != -1 && demux->src_segment.last_stop > stop)
+ stop = demux->src_segment.last_stop;
for (id = 0; id < GST_FLUPS_DEMUX_MAX_STREAMS; id++) {
GstFluPSStream *stream = demux->streams[id];
@@ -534,23 +540,23 @@ gst_flups_demux_send_segment_close (GstFluPSDemux * demux)
if (stream->last_seg_start != GST_CLOCK_TIME_NONE &&
stream->last_seg_start > start)
start = stream->last_seg_start;
+
#if 0
g_print ("Segment close to pad %s start %" GST_TIME_FORMAT
" stop %" GST_TIME_FORMAT "\n",
GST_PAD_NAME (stream->pad), GST_TIME_ARGS (start),
- GST_TIME_ARGS (demux->src_segment.stop));
+ GST_TIME_ARGS (stop));
#endif
- if (start > demux->src_segment.stop) {
+ if (start > stop) {
g_print ("Problem on pad %s with start %" GST_TIME_FORMAT " > stop %"
GST_TIME_FORMAT "\n",
gst_object_get_name (GST_OBJECT (stream->pad)),
- GST_TIME_ARGS (start), GST_TIME_ARGS (demux->src_segment.stop));
+ GST_TIME_ARGS (start), GST_TIME_ARGS (stop));
}
event = gst_event_new_new_segment_full (TRUE,
demux->src_segment.rate, demux->src_segment.applied_rate,
GST_FORMAT_TIME, start,
- demux->src_segment.stop,
- demux->src_segment.time + (start - demux->src_segment.start));
+ stop, demux->src_segment.time + (start - demux->src_segment.start));
if (event)
gst_pad_push_event (stream->pad, event);
}
@@ -566,7 +572,7 @@ gst_flups_demux_send_event (GstFluPSDemux * demux, GstEvent * event)
for (id = 0; id < GST_FLUPS_DEMUX_MAX_STREAMS; id++) {
GstFluPSStream *stream = demux->streams[id];
- if (stream && !stream->notlinked) {
+ if (stream) {
(void) gst_event_ref (event);
if (!gst_pad_push_event (stream->pad, event)) {
@@ -733,6 +739,8 @@ gst_flups_demux_handle_dvd_event (GstFluPSDemux * demux, GstEvent * event)
"event", G_TYPE_STRING, "select-pad", NULL);
GstEvent *sel_event =
gst_event_new_custom (GST_EVENT_CUSTOM_DOWNSTREAM, s);
+
+ temp->notlinked = FALSE;
gst_pad_push_event (temp->pad, sel_event);
gst_event_ref (event);
@@ -872,8 +880,12 @@ gst_flups_demux_sink_event (GstPad * pad, GstEvent * event)
else
demux->scr_adjust = -GSTTIME_TO_MPEGTIME (-adjust);
- if (stop != -1)
+ if (stop != -1) {
stop = start + dur;
+ if (demux->src_segment.last_stop != -1
+ && demux->src_segment.last_stop > stop)
+ stop = demux->src_segment.last_stop;
+ }
GST_DEBUG_OBJECT (demux,
"sending new segment: update %d rate %g format %d, start: %"
@@ -1150,7 +1162,7 @@ gst_flups_demux_reset_psm (GstFluPSDemux * demux)
FILL_TYPE (0x40, 0x7f, -1);
FILL_TYPE (0x80, 0x87, ST_PS_AUDIO_AC3);
FILL_TYPE (0x88, 0x9f, ST_PS_AUDIO_DTS);
- FILL_TYPE (0xa0, 0xbf, ST_PS_AUDIO_LPCM);
+ FILL_TYPE (0xa0, 0xaf, ST_PS_AUDIO_LPCM);
FILL_TYPE (0xbd, 0xbd, -1);
FILL_TYPE (0xc0, 0xdf, ST_AUDIO_MPEG1);
FILL_TYPE (0xe0, 0xef, ST_GST_VIDEO_MPEG1_OR_2);
@@ -1362,12 +1374,8 @@ gst_flups_demux_parse_pack_start (GstFluPSDemux * demux)
new_time = MPEGTIME_TO_GSTTIME (scr_adjusted);
if (new_time != GST_CLOCK_TIME_NONE) {
// g_print ("SCR now %" GST_TIME_FORMAT "\n", GST_TIME_ARGS (new_time));
- if (new_time > GST_SECOND / 2)
- new_time -= GST_SECOND / 2;
- else
- new_time = 0;
+ gst_segment_set_last_stop (&demux->src_segment, GST_FORMAT_TIME, new_time);
gst_flups_demux_send_segment_updates (demux, new_time);
- demux->src_segment.last_stop = new_time;
}
/* Reset the bytes_since_scr value to count the data remaining in the
@@ -1861,7 +1869,7 @@ need_data:
}
}
-static gboolean
+static inline gboolean
gst_flups_demux_is_pes_sync (guint32 sync)
{
return ((sync & 0xfc) == 0xbc) ||
diff --git a/gst/dvdspu/Makefile.am b/gst/dvdspu/Makefile.am
index 4d75f76c..07a66357 100644
--- a/gst/dvdspu/Makefile.am
+++ b/gst/dvdspu/Makefile.am
@@ -1,13 +1,13 @@
plugin_LTLIBRARIES = libgstdvdspu.la
-libgstdvdspu_la_SOURCES = gstdvdspu.c gstdvdspu-render.c
+libgstdvdspu_la_SOURCES = gstdvdspu.c gstdvdspu-render.c gstspu-vobsub.c gstspu-vobsub-render.c gstspu-pgs.c
libgstdvdspu_la_CFLAGS = $(GST_CFLAGS)
libgstdvdspu_la_LIBADD = $(GST_LIBS)
libgstdvdspu_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS)
libgstdvdspu_la_LIBTOOLFLAGS = --tag=disable-static
-noinst_HEADERS = gstdvdspu.h
+noinst_HEADERS = gstdvdspu.h gstspu-pgs.h gstspu-vobsub.h
EXTRA_DIST = Notes.txt
diff --git a/gst/dvdspu/gstdvdspu-render.c b/gst/dvdspu/gstdvdspu-render.c
index a8dadee2..7731aed4 100644
--- a/gst/dvdspu/gstdvdspu-render.c
+++ b/gst/dvdspu/gstdvdspu-render.c
@@ -1,5 +1,6 @@
/* GStreamer DVD Sub-Picture Unit
* Copyright (C) 2007 Fluendo S.A. <info@fluendo.com>
+ * Copyright (C) 2009 Jan Schmidt <thaytan@noraisin.net>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
@@ -29,338 +30,38 @@
GST_DEBUG_CATEGORY_EXTERN (dvdspu_debug);
#define GST_CAT_DEFAULT dvdspu_debug
-static void
-dvdspu_recalc_palette (GstDVDSpu * dvdspu,
- SpuColour * dest, guint8 * idx, guint8 * alpha)
-{
- SpuState *state = &dvdspu->spu_state;
- gint i;
-
- for (i = 0; i < 4; i++, dest++) {
- guint32 col = state->current_clut[idx[i]];
-
- dest->Y = (guint16) ((col >> 16) & 0xff) * alpha[i];
- /* U/V are stored as V/U in the clut words, so switch them */
- dest->U = (guint16) (col & 0xff) * alpha[i];
- dest->V = (guint16) ((col >> 8) & 0xff) * alpha[i];
- dest->A = alpha[i];
- }
-}
-
-/* Recalculate the main, HL & ChgCol palettes */
-static void
-dvdspu_update_palettes (GstDVDSpu * dvdspu, SpuState * state)
-{
- gint16 l, c;
- guint8 index[4]; /* Indices for the palette */
- guint8 alpha[4]; /* Alpha values the palette */
-
- if (state->main_pal_dirty) {
- dvdspu_recalc_palette (dvdspu, state->main_pal, state->main_idx,
- state->main_alpha);
-
- /* Need to refresh the hl_ctrl info copies of the main palette too */
- memcpy (state->hl_ctrl_i.pix_ctrl_i[0].pal_cache, state->main_pal,
- 4 * sizeof (SpuColour));
- memcpy (state->hl_ctrl_i.pix_ctrl_i[2].pal_cache, state->main_pal,
- 4 * sizeof (SpuColour));
-
- state->main_pal_dirty = FALSE;
- }
-
- if (state->hl_pal_dirty) {
- dvdspu_recalc_palette (dvdspu, state->hl_ctrl_i.pix_ctrl_i[1].pal_cache,
- state->hl_idx, state->hl_alpha);
- state->hl_pal_dirty = FALSE;
- }
-
- /* Update the offset positions for the highlight region */
- if (state->hl_rect.top != -1) {
- state->hl_ctrl_i.top = state->hl_rect.top;
- state->hl_ctrl_i.bottom = state->hl_rect.bottom;
- state->hl_ctrl_i.n_changes = 3;
- state->hl_ctrl_i.pix_ctrl_i[0].left = 0;
- state->hl_ctrl_i.pix_ctrl_i[1].left = state->hl_rect.left;
- state->hl_ctrl_i.pix_ctrl_i[2].left = state->hl_rect.right + 1;
- }
-
- if (state->line_ctrl_i_pal_dirty) {
- GST_LOG_OBJECT (dvdspu, "Updating chg-col-con palettes");
- for (l = 0; l < state->n_line_ctrl_i; l++) {
- SpuLineCtrlI *cur_line_ctrl = state->line_ctrl_i + l;
-
- for (c = 0; c < cur_line_ctrl->n_changes; c++) {
- SpuPixCtrlI *cur = cur_line_ctrl->pix_ctrl_i + c;
-
- index[3] = (cur->palette >> 28) & 0x0f;
- index[2] = (cur->palette >> 24) & 0x0f;
- index[1] = (cur->palette >> 20) & 0x0f;
- index[0] = (cur->palette >> 16) & 0x0f;
-
- alpha[3] = (cur->palette >> 12) & 0x0f;
- alpha[2] = (cur->palette >> 8) & 0x0f;
- alpha[1] = (cur->palette >> 4) & 0x0f;
- alpha[0] = (cur->palette) & 0x0f;
- dvdspu_recalc_palette (dvdspu, cur->pal_cache, index, alpha);
- }
- }
- state->line_ctrl_i_pal_dirty = FALSE;
- }
-}
-
-static void
-dvdspu_clear_comp_buffers (SpuState * state)
+void
+gstspu_clear_comp_buffers (SpuState * state)
{
- /* The area to clear is the line inside the disp_rect, each entry 2 bytes,
+ /* The area to clear is the line inside the disp_rect, each entry 4 bytes,
* of the sub-sampled UV planes. */
- gint16 left = state->disp_rect.left / 2;
- gint16 right = state->disp_rect.right / 2;
- gint16 uv_width = 2 * (right - left + 1);
+ gint16 left = state->comp_left / 2;
+ gint16 right = state->comp_right / 2;
+ gint16 uv_width = sizeof (guint32) * (right - left + 1);
memset (state->comp_bufs[0] + left, 0, uv_width);
memset (state->comp_bufs[1] + left, 0, uv_width);
memset (state->comp_bufs[2] + left, 0, uv_width);
-
- state->comp_last_x[0] = -1;
- state->comp_last_x[1] = -1;
-}
-
-static inline guint8
-dvdspu_get_nibble (SpuState * state, guint16 * rle_offset)
-{
- guint8 ret;
-
- if (G_UNLIKELY (*rle_offset >= state->max_offset))
- return 0; /* Overran the buffer */
-
- ret = GST_BUFFER_DATA (state->pix_buf)[(*rle_offset) / 2];
-
- /* If the offset is even, we shift the answer down 4 bits, otherwise not */
- if (*rle_offset & 0x01)
- ret &= 0x0f;
- else
- ret = ret >> 4;
-
- (*rle_offset)++;
- return ret;
}
-static guint16
-dvdspu_get_rle_code (SpuState * state, guint16 * rle_offset)
-{
- guint16 code;
-
- code = dvdspu_get_nibble (state, rle_offset);
- if (code < 0x4) { /* 4 .. f */
- code = (code << 4) | dvdspu_get_nibble (state, rle_offset);
- if (code < 0x10) { /* 1x .. 3x */
- code = (code << 4) | dvdspu_get_nibble (state, rle_offset);
- if (code < 0x40) { /* 04x .. 0fx */
- code = (code << 4) | dvdspu_get_nibble (state, rle_offset);
- }
- }
- }
- return code;
-}
-
-static inline void
-dvdspu_draw_rle_run (SpuState * state, gint16 x, gint16 end, SpuColour * colour)
-{
-#if 0
- GST_LOG ("Y: %d x: %d end %d col %d %d %d %d",
- state->cur_Y, x, end, colour->Y, colour->U, colour->V, colour->A);
-#endif
-
- if (colour->A != 0) {
- guint8 inv_A = 0xf - colour->A;
-
- /* FIXME: This could be more efficient */
- while (x < end) {
- state->out_Y[x] = (inv_A * state->out_Y[x] + colour->Y) / 0xf;
- state->out_U[x / 2] += colour->U;
- state->out_V[x / 2] += colour->V;
- state->out_A[x / 2] += colour->A;
- x++;
- }
- /* Update the compositing buffer so we know how much to blend later */
- *(state->comp_last_x_ptr) = end;
- }
-}
-
-static inline gint16
-rle_end_x (guint16 rle_code, gint16 x, gint16 end)
-{
- /* run length = rle_code >> 2 */
- if (G_UNLIKELY (((rle_code >> 2) == 0)))
- return end;
- else
- return MIN (end, x + (rle_code >> 2));
-}
-
-static void dvdspu_render_line_with_chgcol (SpuState * state,
- guint8 * planes[3], guint16 * rle_offset);
-static gboolean dvdspu_update_chgcol (SpuState * state);
-
-static void
-dvdspu_render_line (SpuState * state, guint8 * planes[3], guint16 * rle_offset)
-{
- gint16 x, next_x, end, rle_code;
- SpuColour *colour;
-
- /* Check for special case of chg_col info to use (either highlight or
- * ChgCol command */
- if (state->cur_chg_col != NULL) {
- if (dvdspu_update_chgcol (state)) {
- /* Check the top & bottom, because we might not be within the region yet */
- if (state->cur_Y >= state->cur_chg_col->top &&
- state->cur_Y <= state->cur_chg_col->bottom) {
- dvdspu_render_line_with_chgcol (state, planes, rle_offset);
- return;
- }
- }
- }
-
- /* No special case. Render as normal */
-
- /* Set up our output pointers */
- state->out_Y = planes[0];
- state->out_U = state->comp_bufs[0];
- state->out_V = state->comp_bufs[1];
- state->out_A = state->comp_bufs[2];
- /* We always need to start our RLE decoding byte_aligned */
- *rle_offset = GST_ROUND_UP_2 (*rle_offset);
-
- x = state->disp_rect.left;
- end = state->disp_rect.right + 1;
- while (x < end) {
- rle_code = dvdspu_get_rle_code (state, rle_offset);
- colour = &state->main_pal[rle_code & 3];
- next_x = rle_end_x (rle_code, x, end);
- /* Now draw the run between [x,next_x) */
- dvdspu_draw_rle_run (state, x, next_x, colour);
- x = next_x;
- }
-}
-
-static gboolean
-dvdspu_update_chgcol (SpuState * state)
-{
- if (state->cur_chg_col == NULL)
- return FALSE;
-
- if (state->cur_Y <= state->cur_chg_col->bottom)
- return TRUE;
-
- while (state->cur_chg_col < state->cur_chg_col_end) {
- if (state->cur_Y >= state->cur_chg_col->top &&
- state->cur_Y <= state->cur_chg_col->bottom) {
-#if 0
- g_print ("Stopped @ entry %d with top %d bottom %d, cur_y %d",
- (gint16) (state->cur_chg_col - state->line_ctrl_i),
- state->cur_chg_col->top, state->cur_chg_col->bottom, y);
-#endif
- return TRUE;
- }
- state->cur_chg_col++;
- }
-
- /* Finished all our cur_chg_col entries. Use the main palette from here on */
- state->cur_chg_col = NULL;
- return FALSE;
-}
-
-static void
-dvdspu_render_line_with_chgcol (SpuState * state, guint8 * planes[3],
- guint16 * rle_offset)
-{
- SpuLineCtrlI *chg_col = state->cur_chg_col;
-
- gint16 x, next_x, disp_end, rle_code, run_end;
- SpuColour *colour;
- SpuPixCtrlI *cur_pix_ctrl;
- SpuPixCtrlI *next_pix_ctrl;
- SpuPixCtrlI *end_pix_ctrl;
- SpuPixCtrlI dummy_pix_ctrl;
- gint16 cur_reg_end;
- gint i;
-
- state->out_Y = planes[0];
- state->out_U = state->comp_bufs[0];
- state->out_V = state->comp_bufs[1];
- state->out_A = state->comp_bufs[2];
-
- /* We always need to start our RLE decoding byte_aligned */
- *rle_offset = GST_ROUND_UP_2 (*rle_offset);
-
- /* Our run will cover the display rect */
- x = state->disp_rect.left;
- disp_end = state->disp_rect.right + 1;
-
- /* Work out the first pixel control info, which may point to the dummy entry if
- * the global palette/alpha need using initally */
- cur_pix_ctrl = chg_col->pix_ctrl_i;
- end_pix_ctrl = chg_col->pix_ctrl_i + chg_col->n_changes;
-
- if (cur_pix_ctrl->left != 0) {
- next_pix_ctrl = cur_pix_ctrl;
- cur_pix_ctrl = &dummy_pix_ctrl;
- for (i = 0; i < 4; i++) /* Copy the main palette to our dummy entry */
- dummy_pix_ctrl.pal_cache[i] = state->main_pal[i];
- } else {
- next_pix_ctrl = cur_pix_ctrl + 1;
- }
- if (next_pix_ctrl < end_pix_ctrl)
- cur_reg_end = next_pix_ctrl->left;
- else
- cur_reg_end = disp_end;
-
- /* Render stuff */
- while (x < disp_end) {
- rle_code = dvdspu_get_rle_code (state, rle_offset);
- next_x = rle_end_x (rle_code, x, disp_end);
-
- /* Now draw the run between [x,next_x), crossing palette regions as needed */
- while (x < next_x) {
- run_end = MIN (next_x, cur_reg_end);
-
- if (G_LIKELY (x < run_end)) {
- colour = &cur_pix_ctrl->pal_cache[rle_code & 3];
- dvdspu_draw_rle_run (state, x, run_end, colour);
- x = run_end;
- }
-
- if (x >= cur_reg_end) {
- /* Advance to next region */
- cur_pix_ctrl = next_pix_ctrl;
- next_pix_ctrl++;
-
- if (next_pix_ctrl < end_pix_ctrl)
- cur_reg_end = next_pix_ctrl->left;
- else
- cur_reg_end = disp_end;
- }
- }
- }
-}
-
-static void
-dvdspu_blend_comp_buffers (SpuState * state, guint8 * planes[3])
+void
+gstspu_blend_comp_buffers (SpuState * state, guint8 * planes[3])
{
gint16 uv_end;
gint16 left, x;
guint8 *out_U;
guint8 *out_V;
- guint16 *in_U;
- guint16 *in_V;
- guint16 *in_A;
- gint16 comp_last_x = MAX (state->comp_last_x[0], state->comp_last_x[1]);
+ guint32 *in_U;
+ guint32 *in_V;
+ guint32 *in_A;
+ gint16 comp_last_x = state->comp_right;
- if (comp_last_x < state->disp_rect.left)
+ if (comp_last_x < state->comp_left)
return; /* Didn't draw in the comp buffers, nothing to do... */
#if 0
- GST_LOG ("Blending comp buffers from disp_rect.left %d to x=%d",
- state->disp_rect.left, comp_last_x);
+ GST_LOG ("Blending comp buffers from x=%d to x=%d",
+ state->comp_left, state->comp_right);
#endif
/* Set up the output pointers */
@@ -376,188 +77,18 @@ dvdspu_blend_comp_buffers (SpuState * state, guint8 * planes[3])
* drawn in the render_line function, divided by 2 (rounding up) to account
* for UV sub-sampling */
uv_end = (comp_last_x + 1) / 2;
- left = state->disp_rect.left / 2;
+ left = state->comp_left / 2;
for (x = left; x < uv_end; x++) {
- guint16 tmp;
- guint16 inv_A = (4 * 0xf) - in_A[x];
-
+ guint32 tmp;
/* Each entry in the compositing buffer is 4 summed pixels, so the
- * inverse alpha is (4 * 0x0f) - in_A[x] */
+ * inverse alpha is (4 * 0xff) - in_A[x] */
+ guint16 inv_A = (4 * 0xff) - in_A[x];
+
tmp = in_U[x] + inv_A * out_U[x];
- out_U[x] = (guint8) (tmp / (4 * 0xf));
+ out_U[x] = (guint8) (tmp / (4 * 0xff));
tmp = in_V[x] + inv_A * out_V[x];
- out_V[x] = (guint8) (tmp / (4 * 0xf));
- }
-}
-
-void
-gst_dvd_spu_render_spu (GstDVDSpu * dvdspu, GstBuffer * buf)
-{
- SpuState *state = &dvdspu->spu_state;
- guint8 *planes[3]; /* YUV frame pointers */
- gint y, last_y;
-
- /* Set up our initial state */
- if (G_UNLIKELY (state->pix_buf == NULL))
- return;
-
- /* Store the start of each plane */
- planes[0] = GST_BUFFER_DATA (buf);
- planes[1] = planes[0] + (state->Y_height * state->Y_stride);
- planes[2] = planes[1] + (state->UV_height * state->UV_stride);
-
- /* Sanity check */
- g_return_if_fail (planes[2] + (state->UV_height * state->UV_stride) <=
- GST_BUFFER_DATA (buf) + GST_BUFFER_SIZE (buf));
-
- GST_DEBUG ("Rendering SPU. disp_rect %d,%d to %d,%d. hl_rect %d,%d to %d,%d",
- state->disp_rect.left, state->disp_rect.top,
- state->disp_rect.right, state->disp_rect.bottom,
- state->hl_rect.left, state->hl_rect.top,
- state->hl_rect.right, state->hl_rect.bottom);
-
- GST_DEBUG ("vid_disp %d,%d", state->vid_width, state->vid_height);
-
- /* When reading RLE data, we track the offset in nibbles... */
- state->cur_offsets[0] = state->pix_data[0] * 2;
- state->cur_offsets[1] = state->pix_data[1] * 2;
- state->max_offset = GST_BUFFER_SIZE (state->pix_buf) * 2;
-
- /* Update all the palette caches */
- dvdspu_update_palettes (dvdspu, state);
-
- /* Set up HL or Change Color & Contrast rect tracking */
- if (state->hl_rect.top != -1) {
- state->cur_chg_col = &state->hl_ctrl_i;
- state->cur_chg_col_end = state->cur_chg_col + 1;
- } else if (state->n_line_ctrl_i > 0) {
- state->cur_chg_col = state->line_ctrl_i;
- state->cur_chg_col_end = state->cur_chg_col + state->n_line_ctrl_i;
- } else
- state->cur_chg_col = NULL;
-
- /* We start rendering from the first line of the display rect */
- y = state->disp_rect.top;
- /* start_y is always an even number and we render lines in pairs from there,
- * accumulating 2 lines of chroma then blending it. We might need to render a
- * single line at the end if the display rect ends on an even line too. */
- last_y = (state->disp_rect.bottom - 1) & ~(0x01);
-
- /* center the image when display rectangle exceeds the video width */
- if (state->vid_width < state->disp_rect.right) {
- gint diff, disp_width;
-
- disp_width = state->disp_rect.left - state->disp_rect.right;
- diff = (disp_width - state->vid_width) / 2;
-
- /* fixme, this is not used yet */
- state->clip_rect.left = state->disp_rect.left + diff;
- state->clip_rect.right = state->disp_rect.right - diff;
-
- GST_DEBUG ("clipping width to %d,%d", state->clip_rect.left,
- state->clip_rect.right);
- } else {
- state->clip_rect.left = state->disp_rect.left;
- state->clip_rect.right = state->disp_rect.right;
- }
-
- /* for the height, chop off the bottom bits of the diplay rectangle because we
- * assume the picture is in the lower part. We should better check where it
- * is and do something more clever. */
- state->clip_rect.bottom = state->disp_rect.bottom;
- if (state->vid_height < state->disp_rect.bottom) {
- state->clip_rect.top = state->disp_rect.bottom - state->vid_height;
- GST_DEBUG ("clipping height to %d,%d", state->clip_rect.top,
- state->clip_rect.bottom);
- } else {
- state->clip_rect.top = state->disp_rect.top;
- /* Update our plane references to the first line of the disp_rect */
- planes[0] += state->Y_stride * y;
- planes[1] += state->UV_stride * (y / 2);
- planes[2] += state->UV_stride * (y / 2);
- }
-
- for (state->cur_Y = y; state->cur_Y <= last_y; state->cur_Y++) {
- gboolean clip;
-
- clip = (state->cur_Y < state->clip_rect.top
- || state->cur_Y > state->clip_rect.bottom);
-
- /* Reset the compositing buffer */
- dvdspu_clear_comp_buffers (state);
- /* Render even line */
- state->comp_last_x_ptr = state->comp_last_x;
- dvdspu_render_line (state, planes, &state->cur_offsets[0]);
- if (!clip) {
- /* Advance the luminance output pointer */
- planes[0] += state->Y_stride;
- }
- state->cur_Y++;
-
- /* Render odd line */
- state->comp_last_x_ptr = state->comp_last_x + 1;
- dvdspu_render_line (state, planes, &state->cur_offsets[1]);
- /* Blend the accumulated UV compositing buffers onto the output */
- dvdspu_blend_comp_buffers (state, planes);
-
- if (!clip) {
- /* Update all the output pointers */
- planes[0] += state->Y_stride;
- planes[1] += state->UV_stride;
- planes[2] += state->UV_stride;
- }
- }
- if (state->cur_Y == state->disp_rect.bottom) {
- g_assert ((state->disp_rect.bottom & 0x01) == 0);
-
- /* Render a remaining lone last even line. y already has the correct value
- * after the above loop exited. */
- dvdspu_clear_comp_buffers (state);
- state->comp_last_x_ptr = state->comp_last_x;
- dvdspu_render_line (state, planes, &state->cur_offsets[0]);
- dvdspu_blend_comp_buffers (state, planes);
+ out_V[x] = (guint8) (tmp / (4 * 0xff));
}
-
- /* for debugging purposes, draw a faint rectangle at the edges of the disp_rect */
-#if 0
- do {
- guint8 *cur;
- gint16 pos;
-
- cur = GST_BUFFER_DATA (buf) + state->Y_stride * state->disp_rect.top;
- for (pos = state->disp_rect.left + 1; pos < state->disp_rect.right; pos++)
- cur[pos] = (cur[pos] / 2) + 0x8;
- cur = GST_BUFFER_DATA (buf) + state->Y_stride * state->disp_rect.bottom;
- for (pos = state->disp_rect.left + 1; pos < state->disp_rect.right; pos++)
- cur[pos] = (cur[pos] / 2) + 0x8;
- cur = GST_BUFFER_DATA (buf) + state->Y_stride * state->disp_rect.top;
- for (pos = state->disp_rect.top; pos <= state->disp_rect.bottom; pos++) {
- cur[state->disp_rect.left] = (cur[state->disp_rect.left] / 2) + 0x8;
- cur[state->disp_rect.right] = (cur[state->disp_rect.right] / 2) + 0x8;
- cur += state->Y_stride;
- }
- } while (0);
-#endif
- /* For debugging purposes, draw a faint rectangle around the highlight rect */
-#if 0
- if (state->hl_rect.top != -1) {
- guint8 *cur;
- gint16 pos;
-
- cur = GST_BUFFER_DATA (buf) + state->Y_stride * state->hl_rect.top;
- for (pos = state->hl_rect.left + 1; pos < state->hl_rect.right; pos++)
- cur[pos] = (cur[pos] / 2) + 0x8;
- cur = GST_BUFFER_DATA (buf) + state->Y_stride * state->hl_rect.bottom;
- for (pos = state->hl_rect.left + 1; pos < state->hl_rect.right; pos++)
- cur[pos] = (cur[pos] / 2) + 0x8;
- cur = GST_BUFFER_DATA (buf) + state->Y_stride * state->hl_rect.top;
- for (pos = state->hl_rect.top; pos <= state->hl_rect.bottom; pos++) {
- cur[state->hl_rect.left] = (cur[state->hl_rect.left] / 2) + 0x8;
- cur[state->hl_rect.right] = (cur[state->hl_rect.right] / 2) + 0x8;
- cur += state->Y_stride;
- }
- }
-#endif
}
diff --git a/gst/dvdspu/gstdvdspu.c b/gst/dvdspu/gstdvdspu.c
index 703c405b..f5fce4bc 100644
--- a/gst/dvdspu/gstdvdspu.c
+++ b/gst/dvdspu/gstdvdspu.c
@@ -32,22 +32,17 @@
# include <config.h>
#endif
+#include <gst/gst-i18n-plugin.h>
+
#include <string.h>
#include <gst/gst.h>
#include "gstdvdspu.h"
-#define DUMP_DCSQ 0
-
-extern void gst_dvd_spu_render_spu (GstDVDSpu * dvdspu, GstBuffer * buf);
-
GST_DEBUG_CATEGORY (dvdspu_debug);
#define GST_CAT_DEFAULT dvdspu_debug
-/* Convert an STM offset in the SPU sequence to a GStreamer timestamp */
-#define STM_TO_GST(stm) ((GST_MSECOND * 1024 * (stm)) / 90)
-
/* Filter signals and args */
enum
{
@@ -73,12 +68,19 @@ static GstStaticPadTemplate src_factory = GST_STATIC_PAD_TEMPLATE ("src",
);
static GstStaticPadTemplate subpic_sink_factory =
-GST_STATIC_PAD_TEMPLATE ("subpicture",
+ GST_STATIC_PAD_TEMPLATE ("subpicture",
GST_PAD_SINK,
GST_PAD_ALWAYS,
- GST_STATIC_CAPS ("video/x-dvd-subpicture")
+ GST_STATIC_CAPS ("video/x-dvd-subpicture; subpicture/x-pgs")
);
+static const guint32 default_clut[16] = {
+ 0xb48080, 0x248080, 0x628080, 0xd78080,
+ 0x808080, 0x808080, 0x808080, 0x808080,
+ 0x808080, 0x808080, 0x808080, 0x808080,
+ 0x808080, 0x808080, 0x808080, 0x808080
+};
+
GST_BOILERPLATE (GstDVDSpu, gst_dvd_spu, GstElement, GST_TYPE_ELEMENT);
static void gst_dvd_spu_dispose (GObject * object);
@@ -99,22 +101,26 @@ static void gst_dvd_spu_redraw_still (GstDVDSpu * dvdspu, gboolean force);
static void gst_dvd_spu_check_still_updates (GstDVDSpu * dvdspu);
static GstFlowReturn gst_dvd_spu_subpic_chain (GstPad * pad, GstBuffer * buf);
static gboolean gst_dvd_spu_subpic_event (GstPad * pad, GstEvent * event);
+static gboolean gst_dvd_spu_subpic_set_caps (GstPad * pad, GstCaps * caps);
static void gst_dvd_spu_clear (GstDVDSpu * dvdspu);
-static void gst_dvd_spu_flush_spu_info (GstDVDSpu * dvdspu);
+static void gst_dvd_spu_flush_spu_info (GstDVDSpu * dvdspu,
+ gboolean process_events);
static void gst_dvd_spu_advance_spu (GstDVDSpu * dvdspu, GstClockTime new_ts);
+static void gstspu_render (GstDVDSpu * dvdspu, GstBuffer * buf);
static GstFlowReturn
dvdspu_handle_vid_buffer (GstDVDSpu * dvdspu, GstBuffer * buf);
+static void gst_dvd_spu_handle_dvd_event (GstDVDSpu * dvdspu, GstEvent * event);
static void
gst_dvd_spu_base_init (gpointer gclass)
{
static GstElementDetails element_details =
- GST_ELEMENT_DETAILS ("Fluendo DVD Player Sub-picture Overlay",
- "Mixer/Video/Overlay/DVD",
- "Parses the DVD Sub-Picture command stream and renders the SPU overlay "
+ GST_ELEMENT_DETAILS ("GStreamer Sub-picture Overlay",
+ "Mixer/Video/Overlay/DVD/Bluray",
+ "Parses Sub-Picture command streams and renders the SPU overlay "
"onto the video as it passes through",
- "Jan Schmidt <jan@fluendo.com>");
+ "Jan Schmidt <thaytan@noraisin.net>");
GstElementClass *element_class = GST_ELEMENT_CLASS (gclass);
gst_element_class_add_pad_template (element_class,
@@ -164,7 +170,8 @@ gst_dvd_spu_init (GstDVDSpu * dvdspu, GstDVDSpuClass * gclass)
gst_pad_new_from_static_template (&subpic_sink_factory, "subpicture");
gst_pad_set_chain_function (dvdspu->subpic_sinkpad, gst_dvd_spu_subpic_chain);
gst_pad_set_event_function (dvdspu->subpic_sinkpad, gst_dvd_spu_subpic_event);
- gst_pad_use_fixed_caps (dvdspu->subpic_sinkpad);
+ gst_pad_set_setcaps_function (dvdspu->subpic_sinkpad,
+ gst_dvd_spu_subpic_set_caps);
gst_element_add_pad (GST_ELEMENT (dvdspu), dvdspu->videosinkpad);
gst_element_add_pad (GST_ELEMENT (dvdspu), dvdspu->subpic_sinkpad);
@@ -179,7 +186,10 @@ gst_dvd_spu_init (GstDVDSpu * dvdspu, GstDVDSpuClass * gclass)
static void
gst_dvd_spu_clear (GstDVDSpu * dvdspu)
{
- gst_dvd_spu_flush_spu_info (dvdspu);
+ gst_dvd_spu_flush_spu_info (dvdspu, FALSE);
+ gst_segment_init (&dvdspu->subp_seg, GST_FORMAT_UNDEFINED);
+
+ dvdspu->spu_input_type = SPU_INPUT_TYPE_NONE;
gst_buffer_replace (&dvdspu->ref_frame, NULL);
gst_buffer_replace (&dvdspu->pending_frame, NULL);
@@ -223,15 +233,14 @@ gst_dvd_spu_finalize (GObject * object)
/* With SPU lock held, clear the queue of SPU packets */
static void
-gst_dvd_spu_flush_spu_info (GstDVDSpu * dvdspu)
+gst_dvd_spu_flush_spu_info (GstDVDSpu * dvdspu, gboolean keep_events)
{
SpuPacket *packet;
SpuState *state = &dvdspu->spu_state;
+ GQueue tmp_q = G_QUEUE_INIT;
GST_INFO_OBJECT (dvdspu, "Flushing SPU information");
- gst_segment_init (&dvdspu->subp_seg, GST_FORMAT_UNDEFINED);
-
if (dvdspu->partial_spu) {
gst_buffer_unref (dvdspu->partial_spu);
dvdspu->partial_spu = NULL;
@@ -239,38 +248,37 @@ gst_dvd_spu_flush_spu_info (GstDVDSpu * dvdspu)
packet = (SpuPacket *) g_queue_pop_head (dvdspu->pending_spus);
while (packet != NULL) {
- if (packet->buf)
+ if (packet->buf) {
gst_buffer_unref (packet->buf);
- if (packet->event)
- gst_event_unref (packet->event);
- g_free (packet);
+ g_assert (packet->event == NULL);
+ g_free (packet);
+ } else if (packet->event) {
+ if (keep_events) {
+ g_queue_push_tail (&tmp_q, packet);
+ } else {
+ gst_event_unref (packet->event);
+ g_free (packet);
+ }
+ }
packet = (SpuPacket *) g_queue_pop_head (dvdspu->pending_spus);
}
+ /* Push anything we decided to keep back onto the pending_spus list */
+ for (packet = g_queue_pop_head (&tmp_q); packet != NULL;
+ packet = g_queue_pop_head (&tmp_q))
+ g_queue_push_tail (dvdspu->pending_spus, packet);
- if (state->buf) {
- gst_buffer_unref (state->buf);
- state->buf = NULL;
- }
- if (state->pix_buf) {
- gst_buffer_unref (state->pix_buf);
- state->pix_buf = NULL;
- }
-
- state->base_ts = state->next_ts = GST_CLOCK_TIME_NONE;
state->flags &= ~(SPU_STATE_FLAGS_MASK);
- state->pix_data[0] = 0;
- state->pix_data[1] = 0;
-
- state->hl_rect.top = -1;
- state->hl_rect.bottom = -1;
+ state->next_ts = GST_CLOCK_TIME_NONE;
- state->disp_rect.top = -1;
- state->disp_rect.bottom = -1;
-
- state->n_line_ctrl_i = 0;
- if (state->line_ctrl_i != NULL) {
- g_free (state->line_ctrl_i);
- state->line_ctrl_i = NULL;
+ switch (dvdspu->spu_input_type) {
+ case SPU_INPUT_TYPE_VOBSUB:
+ gstspu_vobsub_flush (dvdspu);
+ break;
+ case SPU_INPUT_TYPE_PGS:
+ gstspu_pgs_flush (dvdspu);
+ break;
+ default:
+ break;
}
}
@@ -342,7 +350,7 @@ gst_dvd_spu_video_set_caps (GstPad * pad, GstCaps * caps)
state->UV_stride = GST_ROUND_UP_4 (state->Y_stride / 2);
for (i = 0; i < 3; i++) {
state->comp_bufs[i] = g_realloc (state->comp_bufs[i],
- sizeof (guint16) * state->UV_stride);
+ sizeof (guint32) * state->UV_stride);
}
}
DVD_SPU_UNLOCK (dvdspu);
@@ -614,7 +622,7 @@ dvdspu_handle_vid_buffer (GstDVDSpu * dvdspu, GstBuffer * buf)
/* Render the SPU overlay onto the buffer */
buf = gst_buffer_make_writable (buf);
- gst_dvd_spu_render_spu (dvdspu, buf);
+ gstspu_render (dvdspu, buf);
} else {
if (using_ref == FALSE) {
/* Not going to draw anything on this frame, just store a reference
@@ -643,6 +651,22 @@ no_ref_frame:
return GST_FLOW_OK;
}
+
+static void
+gstspu_render (GstDVDSpu * dvdspu, GstBuffer * buf)
+{
+ switch (dvdspu->spu_input_type) {
+ case SPU_INPUT_TYPE_VOBSUB:
+ gstspu_vobsub_render (dvdspu, buf);
+ break;
+ case SPU_INPUT_TYPE_PGS:
+ gstspu_pgs_render (dvdspu, buf);
+ break;
+ default:
+ break;
+ }
+}
+
/* With SPU LOCK */
static void
gst_dvd_spu_redraw_still (GstDVDSpu * dvdspu, gboolean force)
@@ -666,7 +690,7 @@ gst_dvd_spu_redraw_still (GstDVDSpu * dvdspu, gboolean force)
GST_BUFFER_DURATION (buf) = GST_CLOCK_TIME_NONE;
/* Render the SPU overlay onto the buffer */
- gst_dvd_spu_render_spu (dvdspu, buf);
+ gstspu_render (dvdspu, buf);
gst_buffer_replace (&dvdspu->pending_frame, buf);
gst_buffer_unref (buf);
} else if (force) {
@@ -690,392 +714,47 @@ gst_dvd_spu_redraw_still (GstDVDSpu * dvdspu, gboolean force)
}
static void
-gst_dvd_spu_parse_chg_colcon (GstDVDSpu * dvdspu, guint8 * data, guint8 * end)
-{
- SpuState *state = &dvdspu->spu_state;
- guint8 *cur;
- gint16 n_entries;
- gint16 i;
-
- /* Clear any existing chg colcon info */
- state->n_line_ctrl_i = 0;
- if (state->line_ctrl_i != NULL) {
- g_free (state->line_ctrl_i);
- state->line_ctrl_i = NULL;
- }
- GST_DEBUG_OBJECT (dvdspu, "Change Color & Contrast. Pixel data = %d bytes",
- (gint16) (end - data));
-
- /* Count the number of entries we'll need */
- n_entries = 0;
- for (cur = data; cur < end;) {
- guint8 n_changes;
- guint32 code;
-
- if (cur + 4 > end)
- break;
-
- code = GST_READ_UINT32_BE (cur);
- if (code == 0x0fffffff)
- break; /* Termination code */
-
- n_changes = CLAMP ((cur[2] >> 4), 1, 8);
- cur += 4 + (6 * n_changes);
-
- if (cur > end)
- break; /* Invalid entry overrunning buffer */
-
- n_entries++;
- }
-
- state->n_line_ctrl_i = n_entries;
- state->line_ctrl_i = g_new (SpuLineCtrlI, n_entries);
-
- cur = data;
- for (i = 0; i < n_entries; i++) {
- SpuLineCtrlI *cur_line_ctrl = state->line_ctrl_i + i;
- guint8 n_changes = CLAMP ((cur[2] >> 4), 1, 8);
- guint8 c;
-
- cur_line_ctrl->n_changes = n_changes;
- cur_line_ctrl->top = ((cur[0] << 8) & 0x300) | cur[1];
- cur_line_ctrl->bottom = ((cur[2] << 8) & 0x300) | cur[3];
-
- GST_LOG_OBJECT (dvdspu, "ChgColcon Entry %d Top: %d Bottom: %d Changes: %d",
- i, cur_line_ctrl->top, cur_line_ctrl->bottom, n_changes);
- cur += 4;
-
- for (c = 0; c < n_changes; c++) {
- SpuPixCtrlI *cur_pix_ctrl = cur_line_ctrl->pix_ctrl_i + c;
-
- cur_pix_ctrl->left = ((cur[0] << 8) & 0x300) | cur[1];
- cur_pix_ctrl->palette = GST_READ_UINT32_BE (cur + 2);
- GST_LOG_OBJECT (dvdspu, " %d: left: %d palette 0x%x", c,
- cur_pix_ctrl->left, cur_pix_ctrl->palette);
- cur += 6;
- }
- }
-}
-
-static void
-gst_dvd_spu_exec_cmd_blk (GstDVDSpu * dvdspu, guint8 * data, guint8 * end)
-{
- SpuState *state = &dvdspu->spu_state;
-
- while (data < end) {
- guint8 cmd;
-
- cmd = data[0];
-
- switch (cmd) {
- case SPU_CMD_FSTA_DSP:
- GST_DEBUG_OBJECT (dvdspu, " Forced Display");
- state->flags |= SPU_STATE_FORCED_DSP;
- data += 1;
- break;
- case SPU_CMD_DSP:
- GST_DEBUG_OBJECT (dvdspu, " Display On");
- state->flags |= SPU_STATE_DISPLAY;
- data += 1;
- break;
- case SPU_CMD_STP_DSP:
- GST_DEBUG_OBJECT (dvdspu, " Display Off");
- state->flags &= ~(SPU_STATE_FORCED_DSP | SPU_STATE_DISPLAY);
- data += 1;
- break;
- case SPU_CMD_SET_COLOR:{
- if (G_UNLIKELY (data + 3 >= end))
- return; /* Invalid SET_COLOR cmd at the end of the blk */
-
- state->main_idx[3] = data[1] >> 4;
- state->main_idx[2] = data[1] & 0x0f;
- state->main_idx[1] = data[2] >> 4;
- state->main_idx[0] = data[2] & 0x0f;
-
- state->main_pal_dirty = TRUE;
-
- GST_DEBUG_OBJECT (dvdspu,
- " Set Color bg %u pattern %u emph-1 %u emph-2 %u",
- state->main_idx[0], state->main_idx[1], state->main_idx[2],
- state->main_idx[3]);
- data += 3;
- break;
- }
- case SPU_CMD_SET_ALPHA:{
- if (G_UNLIKELY (data + 3 >= end))
- return; /* Invalid SET_ALPHA cmd at the end of the blk */
-
- state->main_alpha[3] = data[1] >> 4;
- state->main_alpha[2] = data[1] & 0x0f;
- state->main_alpha[1] = data[2] >> 4;
- state->main_alpha[0] = data[2] & 0x0f;
-
- state->main_pal_dirty = TRUE;
-
- GST_DEBUG_OBJECT (dvdspu,
- " Set Alpha bg %u pattern %u emph-1 %u emph-2 %u",
- state->main_alpha[0], state->main_alpha[1], state->main_alpha[2],
- state->main_alpha[3]);
- data += 3;
- break;
- }
- case SPU_CMD_SET_DAREA:{
- SpuRect *r = &state->disp_rect;
-
- if (G_UNLIKELY (data + 7 >= end))
- return; /* Invalid SET_DAREA cmd at the end of the blk */
-
- r->top = ((data[4] & 0x3f) << 4) | ((data[5] & 0xe0) >> 4);
- r->left = ((data[1] & 0x3f) << 4) | ((data[2] & 0xf0) >> 4);
- r->right = ((data[2] & 0x03) << 8) | data[3];
- r->bottom = ((data[5] & 0x03) << 8) | data[6];
-
- GST_DEBUG_OBJECT (dvdspu,
- " Set Display Area top %u left %u bottom %u right %u", r->top,
- r->left, r->bottom, r->right);
-
- data += 7;
- break;
- }
- case SPU_CMD_DSPXA:{
- if (G_UNLIKELY (data + 5 >= end))
- return; /* Invalid SET_DSPXE cmd at the end of the blk */
-
- state->pix_data[0] = GST_READ_UINT16_BE (data + 1);
- state->pix_data[1] = GST_READ_UINT16_BE (data + 3);
- /* Store a reference to the current command buffer, as that's where
- * we'll need to take our pixel data from */
- gst_buffer_replace (&state->pix_buf, state->buf);
-
- GST_DEBUG_OBJECT (dvdspu, " Set Pixel Data Offsets top: %u bot: %u",
- state->pix_data[0], state->pix_data[1]);
-
- data += 5;
- break;
- }
- case SPU_CMD_CHG_COLCON:{
- guint16 field_size;
-
- GST_DEBUG_OBJECT (dvdspu, " Set Color & Contrast Change");
- if (G_UNLIKELY (data + 3 >= end))
- return; /* Invalid CHG_COLCON cmd at the end of the blk */
-
- data++;
- field_size = GST_READ_UINT16_BE (data);
-
- if (G_UNLIKELY (data + field_size >= end))
- return; /* Invalid CHG_COLCON cmd at the end of the blk */
-
- gst_dvd_spu_parse_chg_colcon (dvdspu, data + 2, data + field_size);
- state->line_ctrl_i_pal_dirty = TRUE;
- data += field_size;
- break;
- }
- case SPU_CMD_END:
- default:
- GST_DEBUG_OBJECT (dvdspu, " END");
- data = end;
- break;
- }
- }
-}
-
-static void
-gst_dvd_spu_finish_spu_buf (GstDVDSpu * dvdspu)
-{
- SpuState *state = &dvdspu->spu_state;
-
- state->next_ts = state->base_ts = GST_CLOCK_TIME_NONE;
- gst_buffer_replace (&state->buf, NULL);
-
- GST_DEBUG_OBJECT (dvdspu, "Finished SPU buffer");
-}
-
-static gboolean
-gst_dvd_spu_setup_cmd_blk (GstDVDSpu * dvdspu, guint16 cmd_blk_offset,
- guint8 * start, guint8 * end)
-{
- SpuState *state = &dvdspu->spu_state;
- guint16 delay;
- guint8 *cmd_blk = start + cmd_blk_offset;
-
- if (G_UNLIKELY (cmd_blk + 5 >= end))
- return FALSE; /* No valid command block to read */
-
- delay = GST_READ_UINT16_BE (cmd_blk);
- state->next_ts = state->base_ts + STM_TO_GST (delay);
- state->cur_cmd_blk = cmd_blk_offset;
-
- GST_DEBUG_OBJECT (dvdspu, "Setup CMD Block @ %u with TS %" GST_TIME_FORMAT,
- state->cur_cmd_blk, GST_TIME_ARGS (state->next_ts));
- return TRUE;
-}
-
-static void
-gst_dvd_spu_handle_new_spu_buf (GstDVDSpu * dvdspu, SpuPacket * packet)
-{
- guint8 *start, *end;
- SpuState *state = &dvdspu->spu_state;
-
- if (G_UNLIKELY (GST_BUFFER_SIZE (packet->buf) < 4))
- goto invalid;
-
- if (state->buf != NULL) {
- gst_buffer_unref (state->buf);
- state->buf = NULL;
- }
- state->buf = packet->buf;
- state->base_ts = packet->event_ts;
-
- start = GST_BUFFER_DATA (state->buf);
- end = start + GST_BUFFER_SIZE (state->buf);
-
- /* Configure the first command block in this buffer as our initial blk */
- state->cur_cmd_blk = GST_READ_UINT16_BE (start + 2);
- gst_dvd_spu_setup_cmd_blk (dvdspu, state->cur_cmd_blk, start, end);
- /* Clear existing chg-colcon info */
- state->n_line_ctrl_i = 0;
- if (state->line_ctrl_i != NULL) {
- g_free (state->line_ctrl_i);
- state->line_ctrl_i = NULL;
- }
- return;
-
-invalid:
- /* Invalid buffer */
- gst_dvd_spu_finish_spu_buf (dvdspu);
-}
-
-static void
gst_dvd_spu_handle_dvd_event (GstDVDSpu * dvdspu, GstEvent * event)
{
- const gchar *event_type;
const GstStructure *structure = gst_event_get_structure (event);
- SpuState *state = &dvdspu->spu_state;
+ const gchar *event_type = gst_structure_get_string (structure, "event");
gboolean hl_change = FALSE;
- event_type = gst_structure_get_string (structure, "event");
GST_INFO_OBJECT (dvdspu, "DVD event of type %s on subp pad OOB=%d",
event_type, (GST_EVENT_TYPE (event) == GST_EVENT_CUSTOM_DOWNSTREAM_OOB));
- if (strcmp (event_type, "dvd-spu-clut-change") == 0) {
- gchar prop_name[32];
- gint i;
- gint entry;
-
- for (i = 0; i < 16; i++) {
- g_snprintf (prop_name, 32, "clut%02d", i);
- if (!gst_structure_get_int (structure, prop_name, &entry))
- entry = 0;
- state->current_clut[i] = (guint32) entry;
- }
-
- state->main_pal_dirty = TRUE;
- state->hl_pal_dirty = TRUE;
- state->line_ctrl_i_pal_dirty = TRUE;
- hl_change = TRUE;
- } else if (strcmp (event_type, "dvd-spu-highlight") == 0) {
- gint val;
-
- if (gst_structure_get_int (structure, "palette", &val)) {
- state->hl_idx[3] = ((guint32) (val) >> 28) & 0x0f;
- state->hl_idx[2] = ((guint32) (val) >> 24) & 0x0f;
- state->hl_idx[1] = ((guint32) (val) >> 20) & 0x0f;
- state->hl_idx[0] = ((guint32) (val) >> 16) & 0x0f;
-
- state->hl_alpha[3] = ((guint32) (val) >> 12) & 0x0f;
- state->hl_alpha[2] = ((guint32) (val) >> 8) & 0x0f;
- state->hl_alpha[1] = ((guint32) (val) >> 4) & 0x0f;
- state->hl_alpha[0] = ((guint32) (val) >> 0) & 0x0f;
-
- state->hl_pal_dirty = TRUE;
- }
- if (gst_structure_get_int (structure, "sx", &val))
- state->hl_rect.left = (gint16) val;
- if (gst_structure_get_int (structure, "sy", &val))
- state->hl_rect.top = (gint16) val;
- if (gst_structure_get_int (structure, "ex", &val))
- state->hl_rect.right = (gint16) val;
- if (gst_structure_get_int (structure, "ey", &val))
- state->hl_rect.bottom = (gint16) val;
-
- GST_INFO_OBJECT (dvdspu, "Highlight rect is now (%d,%d) to (%d,%d)",
- state->hl_rect.left, state->hl_rect.top,
- state->hl_rect.right, state->hl_rect.bottom);
- hl_change = TRUE;
- } else if (strcmp (event_type, "dvd-spu-reset-highlight") == 0) {
- if (state->hl_rect.top != -1 || state->hl_rect.bottom != -1)
- hl_change = TRUE;
- state->hl_rect.top = -1;
- state->hl_rect.bottom = -1;
- GST_INFO_OBJECT (dvdspu, "Highlight off");
- } else if (strcmp (event_type, "dvd-set-subpicture-track") == 0) {
- gboolean forced_only;
-
- if (gst_structure_get_boolean (structure, "forced-only", &forced_only)) {
- gboolean was_forced = (state->flags & SPU_STATE_FORCED_ONLY);
-
- if (forced_only)
- state->flags |= SPU_STATE_FORCED_ONLY;
- else
- state->flags &= ~(SPU_STATE_FORCED_ONLY);
-
- if ((was_forced && !forced_only) || (!was_forced && forced_only))
- hl_change = TRUE;
- }
+ switch (dvdspu->spu_input_type) {
+ case SPU_INPUT_TYPE_VOBSUB:
+ hl_change = gstspu_vobsub_handle_dvd_event (dvdspu, event);
+ break;
+ case SPU_INPUT_TYPE_PGS:
+ hl_change = gstspu_pgs_handle_dvd_event (dvdspu, event);
+ break;
+ default:
+ break;
}
- if (hl_change && (state->flags & SPU_STATE_STILL_FRAME)) {
+ if (hl_change && (dvdspu->spu_state.flags & SPU_STATE_STILL_FRAME)) {
gst_dvd_spu_redraw_still (dvdspu, FALSE);
}
-
- gst_event_unref (event);
}
-#if DUMP_DCSQ
-static void
-gst_dvd_spu_dump_dcsq (GstDVDSpu * dvdspu,
- GstClockTime start_ts, GstBuffer * spu_buf)
+static gboolean
+gstspu_execute_event (GstDVDSpu * dvdspu)
{
- guint16 cmd_blk_offset;
- guint16 next_blk;
- guint8 *start, *end;
-
- start = GST_BUFFER_DATA (spu_buf);
- end = start + GST_BUFFER_SIZE (spu_buf);
-
- g_return_if_fail (start != NULL);
-
- /* First command */
- next_blk = GST_READ_UINT16_BE (start + 2);
- cmd_blk_offset = 0;
-
- /* Loop through all commands */
- g_print ("SPU begins @ %" GST_TIME_FORMAT " offset %u\n",
- GST_TIME_ARGS (start_ts), next_blk);
-
- while (cmd_blk_offset != next_blk) {
- guint8 *data;
- GstClockTime cmd_blk_ts;
-
- cmd_blk_offset = next_blk;
-
- if (G_UNLIKELY (start + cmd_blk_offset + 5 >= end))
- break; /* No valid command to read */
-
- data = start + cmd_blk_offset;
-
- cmd_blk_ts = start_ts + STM_TO_GST (GST_READ_UINT16_BE (data));
- next_blk = GST_READ_UINT16_BE (data + 2);
-
- g_print ("Cmd Blk @ offset %u next %u ts %" GST_TIME_FORMAT "\n",
- cmd_blk_offset, next_blk, GST_TIME_ARGS (cmd_blk_ts));
-
- data += 4;
- gst_dvd_spu_exec_cmd_blk (dvdspu, data, end);
+ switch (dvdspu->spu_input_type) {
+ case SPU_INPUT_TYPE_VOBSUB:
+ return gstspu_vobsub_execute_event (dvdspu);
+ break;
+ case SPU_INPUT_TYPE_PGS:
+ return gstspu_pgs_execute_event (dvdspu);
+ break;
+ default:
+ g_assert_not_reached ();
+ break;
}
+ return FALSE;
}
-#endif
/* Advance the SPU packet/command queue to a time. new_ts is in running time */
static void
@@ -1083,11 +762,15 @@ gst_dvd_spu_advance_spu (GstDVDSpu * dvdspu, GstClockTime new_ts)
{
SpuState *state = &dvdspu->spu_state;
+ if (G_UNLIKELY (dvdspu->spu_input_type == SPU_INPUT_TYPE_NONE))
+ return;
+
while (state->next_ts == GST_CLOCK_TIME_NONE || state->next_ts <= new_ts) {
- guint8 *start, *cmd_blk, *end;
- guint16 next_blk;
+ GST_DEBUG_OBJECT (dvdspu,
+ "Advancing SPU from TS %" GST_TIME_FORMAT " to %" GST_TIME_FORMAT,
+ GST_TIME_ARGS (state->next_ts), GST_TIME_ARGS (new_ts));
- if (state->buf == NULL) {
+ if (!gstspu_execute_event (dvdspu)) {
GstClockTime vid_run_ts;
/* No current command buffer, try and get one */
@@ -1108,51 +791,25 @@ gst_dvd_spu_advance_spu (GstDVDSpu * dvdspu, GstClockTime new_ts)
packet->buf ? "buffer" : "event");
if (packet->buf) {
-#if DUMP_DCSQ
- gst_dvd_spu_dump_dcsq (dvdspu, packet->event_ts, packet->buf);
-#endif
- gst_dvd_spu_handle_new_spu_buf (dvdspu, packet);
- }
- if (packet->event)
+ switch (dvdspu->spu_input_type) {
+ case SPU_INPUT_TYPE_VOBSUB:
+ gstspu_vobsub_handle_new_buf (dvdspu, packet->event_ts,
+ packet->buf);
+ break;
+ case SPU_INPUT_TYPE_PGS:
+ gstspu_pgs_handle_new_buf (dvdspu, packet->event_ts, packet->buf);
+ break;
+ default:
+ g_assert_not_reached ();
+ break;
+ }
+ g_assert (packet->event == NULL);
+ } else if (packet->event)
gst_dvd_spu_handle_dvd_event (dvdspu, packet->event);
g_free (packet);
continue;
}
-
- GST_DEBUG_OBJECT (dvdspu,
- "Advancing SPU from TS %" GST_TIME_FORMAT " to %" GST_TIME_FORMAT,
- GST_TIME_ARGS (state->next_ts), GST_TIME_ARGS (new_ts));
-
- /* If we get here, we have an SPU buffer, and it's time to process the
- * next cmd */
- g_assert (state->buf != NULL);
-
- GST_DEBUG_OBJECT (dvdspu, "Executing cmd blk with TS %" GST_TIME_FORMAT
- " @ offset %u", GST_TIME_ARGS (state->next_ts), state->cur_cmd_blk);
-
- start = GST_BUFFER_DATA (state->buf);
- end = start + GST_BUFFER_SIZE (state->buf);
-
- cmd_blk = start + state->cur_cmd_blk;
-
- if (G_UNLIKELY (cmd_blk + 5 >= end)) {
- /* Invalid. Finish the buffer and loop again */
- gst_dvd_spu_finish_spu_buf (dvdspu);
- continue;
- }
-
- gst_dvd_spu_exec_cmd_blk (dvdspu, cmd_blk + 4, end);
-
- next_blk = GST_READ_UINT16_BE (cmd_blk + 2);
- if (next_blk != state->cur_cmd_blk) {
- /* Advance to the next block of commands */
- gst_dvd_spu_setup_cmd_blk (dvdspu, next_blk, start, end);
- } else {
- /* Next Block points to the current block, so we're finished with this
- * SPU buffer */
- gst_dvd_spu_finish_spu_buf (dvdspu);
- }
}
}
@@ -1178,10 +835,66 @@ gst_dvd_spu_check_still_updates (GstDVDSpu * dvdspu)
}
}
+static void
+submit_new_spu_packet (GstDVDSpu * dvdspu, GstBuffer * buf)
+{
+ SpuPacket *spu_packet;
+ GstClockTime ts;
+ GstClockTime run_ts = GST_CLOCK_TIME_NONE;
+
+ GST_DEBUG_OBJECT (dvdspu,
+ "Complete subpicture buffer of %u bytes with TS %" GST_TIME_FORMAT,
+ GST_BUFFER_SIZE (buf), GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buf)));
+
+ /* Decide whether to pass this buffer through to the rendering code */
+ ts = GST_BUFFER_TIMESTAMP (buf);
+ if (GST_CLOCK_TIME_IS_VALID (ts)) {
+ if (ts < (GstClockTime) dvdspu->subp_seg.start) {
+ GstClockTimeDiff diff = dvdspu->subp_seg.start - ts;
+
+ /* Buffer starts before segment, see if we can calculate a running time */
+ run_ts =
+ gst_segment_to_running_time (&dvdspu->subp_seg, GST_FORMAT_TIME,
+ dvdspu->subp_seg.start);
+ if (run_ts >= (GstClockTime) diff)
+ run_ts -= diff;
+ else
+ run_ts = GST_CLOCK_TIME_NONE; /* No running time possible for this subpic */
+ } else {
+ /* TS within segment, convert to running time */
+ run_ts =
+ gst_segment_to_running_time (&dvdspu->subp_seg, GST_FORMAT_TIME, ts);
+ }
+ }
+
+ if (GST_CLOCK_TIME_IS_VALID (run_ts)) {
+ /* Complete SPU packet, push it onto the queue for processing when
+ * video packets come past */
+ spu_packet = g_new0 (SpuPacket, 1);
+ spu_packet->buf = buf;
+
+ /* Store the activation time of this buffer in running time */
+ spu_packet->event_ts = run_ts;
+ GST_INFO_OBJECT (dvdspu,
+ "Pushing SPU buf with TS %" GST_TIME_FORMAT " running time %"
+ GST_TIME_FORMAT, GST_TIME_ARGS (ts),
+ GST_TIME_ARGS (spu_packet->event_ts));
+
+ g_queue_push_tail (dvdspu->pending_spus, spu_packet);
+
+ /* In a still frame condition, advance the SPU to make sure the state is
+ * up to date */
+ gst_dvd_spu_check_still_updates (dvdspu);
+ } else {
+ gst_buffer_unref (buf);
+ }
+}
+
static GstFlowReturn
gst_dvd_spu_subpic_chain (GstPad * pad, GstBuffer * buf)
{
GstDVDSpu *dvdspu = (GstDVDSpu *) (gst_object_get_parent (GST_OBJECT (pad)));
+ GstFlowReturn ret = GST_FLOW_OK;
g_return_val_if_fail (dvdspu != NULL, GST_FLOW_ERROR);
@@ -1196,7 +909,15 @@ gst_dvd_spu_subpic_chain (GstPad * pad, GstBuffer * buf)
GST_BUFFER_TIMESTAMP (buf));
}
+ if (GST_BUFFER_IS_DISCONT (buf) && dvdspu->partial_spu) {
+ gst_buffer_unref (dvdspu->partial_spu);
+ dvdspu->partial_spu = NULL;
+ }
+
if (dvdspu->partial_spu != NULL) {
+ if (GST_BUFFER_TIMESTAMP_IS_VALID (buf))
+ GST_WARNING_OBJECT (dvdspu,
+ "Joining subpicture buffer with timestamp to previous");
dvdspu->partial_spu = gst_buffer_join (dvdspu->partial_spu, buf);
} else {
/* If we don't yet have a buffer, wait for one with a timestamp,
@@ -1207,85 +928,88 @@ gst_dvd_spu_subpic_chain (GstPad * pad, GstBuffer * buf)
gst_buffer_unref (buf);
}
- if (dvdspu->partial_spu != NULL && GST_BUFFER_SIZE (dvdspu->partial_spu) > 4) {
- guint16 packet_size;
- guint8 *data;
-
- data = GST_BUFFER_DATA (dvdspu->partial_spu);
- packet_size = GST_READ_UINT16_BE (data);
-
- if (packet_size == GST_BUFFER_SIZE (dvdspu->partial_spu)) {
- SpuPacket *spu_packet;
- GstClockTime ts;
- GstClockTime run_ts = GST_CLOCK_TIME_NONE;
-
- GST_DEBUG_OBJECT (dvdspu,
- "Complete subpicture buffer of %u bytes with TS %" GST_TIME_FORMAT,
- GST_BUFFER_SIZE (dvdspu->partial_spu),
- GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (dvdspu->partial_spu)));
-
- /* Decide whether to pass this buffer through to the rendering code */
- ts = GST_BUFFER_TIMESTAMP (dvdspu->partial_spu);
- if (GST_CLOCK_TIME_IS_VALID (ts)) {
- if (ts < (GstClockTime) dvdspu->subp_seg.start) {
- GstClockTimeDiff diff = dvdspu->subp_seg.start - ts;
-
- /* Buffer starts before segment, see if we can calculate a running time */
- run_ts =
- gst_segment_to_running_time (&dvdspu->subp_seg, GST_FORMAT_TIME,
- dvdspu->subp_seg.start);
- if (run_ts >= (GstClockTime) diff)
- run_ts -= diff;
- else
- run_ts = GST_CLOCK_TIME_NONE; /* No running time possible for this subpic */
+ if (dvdspu->partial_spu == NULL)
+ goto done;
+
+ switch (dvdspu->spu_input_type) {
+ case SPU_INPUT_TYPE_VOBSUB:
+ if (GST_BUFFER_SIZE (dvdspu->partial_spu) > 4) {
+ guint16 packet_size;
+ guint8 *data;
+
+ data = GST_BUFFER_DATA (dvdspu->partial_spu);
+ packet_size = GST_READ_UINT16_BE (data);
+
+ if (packet_size == GST_BUFFER_SIZE (dvdspu->partial_spu)) {
+ submit_new_spu_packet (dvdspu, dvdspu->partial_spu);
+ dvdspu->partial_spu = NULL;
+ } else if (packet_size < GST_BUFFER_SIZE (dvdspu->partial_spu)) {
+ /* Somehow we collected too much - something is wrong. Drop the
+ * packet entirely and wait for a new one */
+ GST_DEBUG_OBJECT (dvdspu, "Discarding invalid SPU buffer of size %u",
+ GST_BUFFER_SIZE (dvdspu->partial_spu));
+
+ gst_buffer_unref (dvdspu->partial_spu);
+ dvdspu->partial_spu = NULL;
} else {
- /* TS within segment, convert to running time */
- run_ts =
- gst_segment_to_running_time (&dvdspu->subp_seg, GST_FORMAT_TIME,
- ts);
+ GST_LOG_OBJECT (dvdspu,
+ "SPU buffer claims to be of size %u. Collected %u so far.",
+ packet_size, GST_BUFFER_SIZE (dvdspu->partial_spu));
+ }
+ }
+ break;
+ case SPU_INPUT_TYPE_PGS:{
+ /* Collect until we have a command buffer that ends exactly at the size
+ * we've collected */
+ guint8 packet_type;
+ guint16 packet_size;
+ guint8 *data = GST_BUFFER_DATA (dvdspu->partial_spu);
+ guint8 *end = data + GST_BUFFER_SIZE (dvdspu->partial_spu);
+
+ /* FIXME: There's no need to walk the command set each time. We can set a
+ * marker and resume where we left off next time */
+ /* FIXME: Move the packet parsing and sanity checking into the format-specific modules */
+ while (data != end) {
+ if (data + 3 > end)
+ break;
+ packet_type = *data++;
+ packet_size = GST_READ_UINT16_BE (data);
+ data += 2;
+ if (data + packet_size > end)
+ break;
+ data += packet_size;
+ /* 0x80 is the END command for PGS packets */
+ if (packet_type == 0x80 && data != end) {
+ /* Extra cruft on the end of the packet -> assume invalid */
+ gst_buffer_unref (dvdspu->partial_spu);
+ dvdspu->partial_spu = NULL;
+ break;
}
}
- if (GST_CLOCK_TIME_IS_VALID (run_ts)) {
- /* Complete SPU packet, push it onto the queue for processing when
- * video packets come past */
- spu_packet = g_new0 (SpuPacket, 1);
- spu_packet->buf = dvdspu->partial_spu;
-
- /* Store the activation time of this buffer in running time */
- spu_packet->event_ts =
- gst_segment_to_running_time (&dvdspu->subp_seg, GST_FORMAT_TIME,
- ts);
- GST_INFO_OBJECT (dvdspu,
- "Pushing SPU buf with TS %" GST_TIME_FORMAT " running time %"
- GST_TIME_FORMAT, GST_TIME_ARGS (ts),
- GST_TIME_ARGS (spu_packet->event_ts));
-
- g_queue_push_tail (dvdspu->pending_spus, spu_packet);
- dvdspu->partial_spu = NULL;
-
- /* In a still frame condition, advance the SPU to make sure the state is
- * up to date */
- gst_dvd_spu_check_still_updates (dvdspu);
- } else {
- gst_buffer_unref (dvdspu->partial_spu);
+ if (dvdspu->partial_spu && data == end) {
+ GST_DEBUG_OBJECT (dvdspu,
+ "Have complete PGS packet of size %u. Enqueueing.",
+ GST_BUFFER_SIZE (dvdspu->partial_spu));
+ submit_new_spu_packet (dvdspu, dvdspu->partial_spu);
dvdspu->partial_spu = NULL;
}
- } else if (packet_size < GST_BUFFER_SIZE (dvdspu->partial_spu)) {
- /* Somehow we collected too much - something is wrong. Drop the
- * packet entirely and wait for a new one */
- GST_DEBUG_OBJECT (dvdspu, "Discarding invalid SPU buffer of size %u",
- GST_BUFFER_SIZE (dvdspu->partial_spu));
-
- gst_buffer_unref (dvdspu->partial_spu);
- dvdspu->partial_spu = NULL;
+ break;
}
+ default:
+ GST_ERROR_OBJECT (dvdspu, "Input type not configured before SPU passing");
+ goto caps_not_set;
}
+done:
DVD_SPU_UNLOCK (dvdspu);
-
gst_object_unref (dvdspu);
- return GST_FLOW_OK;
+ return ret;
+caps_not_set:
+ GST_ELEMENT_ERROR (dvdspu, RESOURCE, NO_SPACE_LEFT,
+ (_("Subpicture format was not configured before data flow")), (NULL));
+ ret = GST_FLOW_ERROR;
+ goto done;
}
static gboolean
@@ -1313,7 +1037,8 @@ gst_dvd_spu_subpic_event (GstPad * pad, GstEvent * event)
DVD_SPU_LOCK (dvdspu);
if (GST_EVENT_IS_SERIALIZED (event)) {
SpuPacket *spu_packet = g_new0 (SpuPacket, 1);
-
+ GST_DEBUG_OBJECT (dvdspu,
+ "Enqueueing DVD event on subpicture pad for later");
spu_packet->event = event;
g_queue_push_tail (dvdspu->pending_spus, spu_packet);
} else {
@@ -1374,8 +1099,11 @@ gst_dvd_spu_subpic_event (GstPad * pad, GstEvent * event)
GST_TIME_ARGS (stop), GST_TIME_ARGS (time));
DVD_SPU_LOCK (dvdspu);
+
gst_segment_set_newsegment_full (&dvdspu->subp_seg, update, rate, arate,
format, start, stop, time);
+ GST_LOG_OBJECT (dvdspu, "Subpicture segment now: %" GST_SEGMENT_FORMAT,
+ &dvdspu->subp_seg);
DVD_SPU_UNLOCK (dvdspu);
gst_event_unref (event);
@@ -1385,8 +1113,10 @@ gst_dvd_spu_subpic_event (GstPad * pad, GstEvent * event)
gst_event_unref (event);
goto done;
case GST_EVENT_FLUSH_STOP:
+ GST_DEBUG_OBJECT (dvdspu, "Have flush-stop event on SPU pad");
DVD_SPU_LOCK (dvdspu);
- gst_dvd_spu_flush_spu_info (dvdspu);
+ gst_segment_init (&dvdspu->subp_seg, GST_FORMAT_UNDEFINED);
+ gst_dvd_spu_flush_spu_info (dvdspu, TRUE);
DVD_SPU_UNLOCK (dvdspu);
/* We don't forward flushes on the spu pad */
@@ -1409,6 +1139,39 @@ done:
return res;
}
+static gboolean
+gst_dvd_spu_subpic_set_caps (GstPad * pad, GstCaps * caps)
+{
+ GstDVDSpu *dvdspu = GST_DVD_SPU (gst_pad_get_parent (pad));
+ gboolean res = FALSE;
+ GstStructure *s;
+ SpuInputType input_type;
+
+ s = gst_caps_get_structure (caps, 0);
+
+ if (gst_structure_has_name (s, "video/x-dvd-subpicture")) {
+ input_type = SPU_INPUT_TYPE_VOBSUB;
+ } else if (gst_structure_has_name (s, "subpicture/x-pgs")) {
+ input_type = SPU_INPUT_TYPE_PGS;
+ } else {
+ goto done;
+ }
+
+ DVD_SPU_LOCK (dvdspu);
+ if (dvdspu->spu_input_type != input_type) {
+ GST_INFO_OBJECT (dvdspu, "Incoming SPU packet type changed to %u",
+ input_type);
+ dvdspu->spu_input_type = input_type;
+ gst_dvd_spu_flush_spu_info (dvdspu, TRUE);
+ }
+
+ DVD_SPU_UNLOCK (dvdspu);
+ res = TRUE;
+done:
+ gst_object_unref (dvdspu);
+ return res;
+}
+
static GstStateChangeReturn
gst_dvd_spu_change_state (GstElement * element, GstStateChange transition)
{
@@ -1433,8 +1196,8 @@ gst_dvd_spu_change_state (GstElement * element, GstStateChange transition)
gboolean
gst_dvd_spu_plugin_init (GstPlugin * plugin)
{
- GST_DEBUG_CATEGORY_INIT (dvdspu_debug, "gstdvdspu",
- 0, "DVD Sub-picture Overlay decoder/renderer");
+ GST_DEBUG_CATEGORY_INIT (dvdspu_debug, "gstspu",
+ 0, "Sub-picture Overlay decoder/renderer");
return gst_element_register (plugin, "dvdspu",
GST_RANK_NONE, GST_TYPE_DVD_SPU);
diff --git a/gst/dvdspu/gstdvdspu.h b/gst/dvdspu/gstdvdspu.h
index dfc51f9e..22b48d1d 100644
--- a/gst/dvdspu/gstdvdspu.h
+++ b/gst/dvdspu/gstdvdspu.h
@@ -16,11 +16,15 @@
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
-#ifndef __DVD_SPU_H__
-#define __DVD_SPU_H__
+#ifndef __GST_DVD_SPU_H__
+#define __GST_DVD_SPU_H__
#include <gst/gst.h>
+#include "gstspu-common.h"
+#include "gstspu-vobsub.h"
+#include "gstspu-pgs.h"
+
G_BEGIN_DECLS
#define GST_TYPE_DVD_SPU \
@@ -37,63 +41,16 @@ G_BEGIN_DECLS
#define DVD_SPU_LOCK(s) g_mutex_lock ((s)->spu_lock);
#define DVD_SPU_UNLOCK(s) g_mutex_unlock ((s)->spu_lock);
-typedef struct _GstDVDSpu GstDVDSpu;
typedef struct _GstDVDSpuClass GstDVDSpuClass;
-typedef struct SpuRect SpuRect;
-typedef struct SpuPixCtrlI SpuPixCtrlI;
-typedef struct SpuLineCtrlI SpuLineCtrlI;
-typedef struct SpuColour SpuColour;
typedef enum SpuStateFlags SpuStateFlags;
-typedef struct SpuState SpuState;
+typedef enum SpuInputType SpuInputType;
typedef struct SpuPacket SpuPacket;
-typedef enum SpuCmd SpuCmd;
-
-/* Describe the limits of a rectangle */
-struct SpuRect {
- gint16 left;
- gint16 top;
- gint16 right;
- gint16 bottom;
-};
-
-/* Store a pre-multiplied colour value. The YUV fields hold the YUV values
- * multiplied by the 8-bit alpha, to save computing it while rendering */
-struct SpuColour {
- guint16 Y;
- guint16 U;
- guint16 V;
- guint8 A;
-};
-
-/* Pixel Control Info from a Change Color Contrast command */
-struct SpuPixCtrlI {
- gint16 left;
- guint32 palette;
-
- /* Pre-multiplied palette values, updated as
- * needed */
- SpuColour pal_cache[4];
-};
-
-struct SpuLineCtrlI {
- guint8 n_changes; /* 1 to 8 */
- SpuPixCtrlI pix_ctrl_i[8];
-
- gint16 top;
- gint16 bottom;
-};
-enum SpuCmd {
- SPU_CMD_FSTA_DSP = 0x00, /* Forced Display */
- SPU_CMD_DSP = 0x01, /* Display Start */
- SPU_CMD_STP_DSP = 0x02, /* Display Off */
- SPU_CMD_SET_COLOR = 0x03, /* Set the color indexes for the palette */
- SPU_CMD_SET_ALPHA = 0x04, /* Set the alpha indexes for the palette */
- SPU_CMD_SET_DAREA = 0x05, /* Set the display area for the SPU */
- SPU_CMD_DSPXA = 0x06, /* Pixel data addresses */
- SPU_CMD_CHG_COLCON = 0x07, /* Change Color & Contrast */
- SPU_CMD_END = 0xff
+enum SpuInputType {
+ SPU_INPUT_TYPE_NONE = 0x00,
+ SPU_INPUT_TYPE_VOBSUB = 0x01,
+ SPU_INPUT_TYPE_PGS = 0x02
};
enum SpuStateFlags {
@@ -110,69 +67,19 @@ enum SpuStateFlags {
struct SpuState {
GstClockTime next_ts; /* Next event TS in running time */
-
- GstClockTime base_ts; /* base TS for cmd blk delays in running time */
- GstBuffer *buf; /* Current SPU packet we're executing commands from */
- guint16 cur_cmd_blk; /* Offset into the buf for the current cmd block */
-
SpuStateFlags flags;
-
- /* Top + Bottom field offsets in the buffer. 0 = not set */
- guint16 pix_data[2];
- GstBuffer *pix_buf; /* Current SPU packet the pix_data references */
-
- SpuRect disp_rect;
- SpuRect clip_rect;
- SpuRect hl_rect;
-
- guint32 current_clut[16]; /* Colour lookup table from incoming events */
-
- guint8 main_idx[4]; /* Indices for current main palette */
- guint8 main_alpha[4]; /* Alpha values for main palette */
-
- guint8 hl_idx[4]; /* Indices for current highlight palette */
- guint8 hl_alpha[4]; /* Alpha values for highlight palette */
-
- /* Pre-multiplied colour palette for the main palette */
- SpuColour main_pal[4];
- gboolean main_pal_dirty;
-
- /* Line control info for rendering the highlight palette */
- SpuLineCtrlI hl_ctrl_i;
- gboolean hl_pal_dirty; /* Indicates that the HL palette info needs refreshing */
-
- /* LineCtrlI Info from a Change Color & Contrast command */
- SpuLineCtrlI *line_ctrl_i;
- gint16 n_line_ctrl_i;
- gboolean line_ctrl_i_pal_dirty; /* Indicates that the palettes for the line_ctrl_i
- * need recalculating */
-
- /* Rendering state vars below */
- guint16 *comp_bufs[3]; /* Compositing buffers for U+V & A */
- gint16 comp_last_x[2]; /* Maximum X values we rendered into the comp buffer (odd & even) */
- gint16 *comp_last_x_ptr; /* Ptr to the current comp_last_x value to be updated by the render */
+
+ gint fps_n, fps_d;
gint16 vid_width, vid_height;
gint16 Y_stride, UV_stride;
gint16 Y_height, UV_height;
- gint fps_n, fps_d;
-
- /* Current Y Position */
- gint16 cur_Y;
-
- /* Current offset in nibbles into the pix_data */
- guint16 cur_offsets[2];
- guint16 max_offset;
-
- /* current ChgColCon Line Info */
- SpuLineCtrlI *cur_chg_col;
- SpuLineCtrlI *cur_chg_col_end;
+ guint32 *comp_bufs[3]; /* Compositing buffers for U+V & A */
+ guint16 comp_left;
+ guint16 comp_right;
- /* Output position tracking */
- guint8 *out_Y;
- guint16 *out_U;
- guint16 *out_V;
- guint16 *out_A;
+ SpuVobsubState vobsub;
+ SpuPgsState pgs;
};
/* Structure used to store the queue of pending SPU packets. The start_ts is
@@ -198,6 +105,7 @@ struct _GstDVDSpu {
GstSegment subp_seg;
SpuState spu_state;
+ SpuInputType spu_input_type;
/* GQueue of SpuBuf structures */
GQueue *pending_spus;
@@ -221,4 +129,4 @@ GType gst_dvd_spu_get_type (void);
G_END_DECLS
-#endif /* __DVD_SPU_H__ */
+#endif /* __GST_DVD_SPU_H__ */
diff --git a/gst/dvdspu/gstspu-common.h b/gst/dvdspu/gstspu-common.h
new file mode 100644
index 00000000..206e8820
--- /dev/null
+++ b/gst/dvdspu/gstspu-common.h
@@ -0,0 +1,56 @@
+/* GStreamer DVD Sub-Picture Unit
+ * Copyright (C) 2007 Fluendo S.A. <info@fluendo.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+#ifndef __GSTSPU_COMMON_H__
+#define __GSTSPU_COMMON_H__
+
+#include <glib.h>
+
+G_BEGIN_DECLS
+
+/* FIXME: Move this back to gstdvdspu.h when the renderers no longer use it: */
+typedef struct _GstDVDSpu GstDVDSpu;
+
+typedef struct SpuState SpuState;
+typedef struct SpuColour SpuColour;
+typedef struct SpuRect SpuRect;
+
+/* Describe the limits of a rectangle */
+struct SpuRect {
+ gint16 left;
+ gint16 top;
+ gint16 right;
+ gint16 bottom;
+};
+
+/* Store a pre-multiplied colour value. The YUV fields hold the YUV values
+ * multiplied by the 8-bit alpha, to save computing it while rendering */
+struct SpuColour {
+ guint16 Y;
+ guint16 U;
+ guint16 V;
+ guint8 A;
+};
+
+void gstspu_clear_comp_buffers (SpuState * state);
+void gstspu_blend_comp_buffers (SpuState * state, guint8 * planes[3]);
+
+
+G_END_DECLS
+
+#endif /* __GSTSPU_COMMON_H__ */
diff --git a/gst/dvdspu/gstspu-pgs.c b/gst/dvdspu/gstspu-pgs.c
new file mode 100644
index 00000000..d1d4de18
--- /dev/null
+++ b/gst/dvdspu/gstspu-pgs.c
@@ -0,0 +1,762 @@
+/* GStreamer Sub-Picture Unit - PGS handling
+ * Copyright (C) 2009 Jan Schmidt <thaytan@noraisin.net>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <string.h>
+#include <gst/gst.h>
+
+#include "gstdvdspu.h"
+#include "gstspu-pgs.h"
+
+const struct PgsFrameRateEntry
+{
+ guint8 id;
+ guint fps_n;
+ guint fps_d;
+} PgsFrameRates[] = {
+ {
+ 64, 30000, 1001} /* 29.97 FPS */
+};
+
+typedef enum PgsCommandType PgsCommandType;
+
+enum PgsCommandType
+{
+ PGS_COMMAND_SET_PALETTE = 0x14,
+ PGS_COMMAND_SET_OBJECT_DATA = 0x15,
+ PGS_COMMAND_PRESENTATION_SEGMENT = 0x16,
+ PGS_COMMAND_SET_WINDOW = 0x17,
+ PGS_COMMAND_INTERACTIVE_SEGMENT = 0x18,
+
+ PGS_COMMAND_END_DISPLAY = 0x80,
+
+ PGS_COMMAND_INVALID = 0xFFFF
+};
+
+static gint gstspu_exec_pgs_buffer (GstDVDSpu * dvdspu, GstBuffer * buf);
+
+#define DUMP_CMDS 0
+#define DUMP_FULL_IMAGE 0
+#define DUMP_FULL_PALETTE 0
+
+#if DUMP_CMDS
+#define PGS_DUMP(...) g_print(__VA_ARGS__)
+#else
+#define PGS_DUMP(...)
+#endif
+
+static void
+dump_bytes (guint8 * data, guint16 len)
+{
+ gint i;
+
+ /* Dump the numbers */
+ for (i = 0; i < len; i++) {
+ PGS_DUMP ("0x%02x ", data[i]);
+ if (!((i + 1) % 16))
+ PGS_DUMP ("\n");
+ }
+ if (len > 0 && (i % 16))
+ PGS_DUMP ("\n");
+}
+
+static void
+dump_rle_data (GstDVDSpu * dvdspu, guint8 * data, guint32 len)
+{
+ guint8 *end = data + len;
+ guint16 obj_w, obj_h;
+ guint x = 0;
+
+ if (data + 4 > end)
+ return;
+
+ /* RLE data: */
+ obj_w = GST_READ_UINT16_BE (data);
+ obj_h = GST_READ_UINT16_BE (data + 2);
+ data += 4;
+ PGS_DUMP ("RLE image is %ux%u\n", obj_w, obj_h);
+
+ while (data < end) {
+ guint8 pal_id;
+ guint16 run_len;
+
+ pal_id = *data++;
+ if (pal_id != 0) {
+ // PGS_DUMP ("data 0x%02x\n", data[0]);
+ run_len = 1;
+ } else {
+ if (data + 1 > end)
+ return;
+ switch (data[0] & 0xC0) {
+ case 0x00:
+ //PGS_DUMP ("data 0x%02x\n", data[0]);
+ run_len = (data[0] & 0x3f);
+ data++;
+ break;
+ case 0x40:
+ if (data + 2 > end)
+ return;
+ //PGS_DUMP ("data 0x%02x 0x%02x\n", data[0], data[1]);
+ run_len = ((data[0] << 8) | data[1]) & 0x3fff;
+ data += 2;
+ break;
+ case 0x80:
+ if (data + 2 > end)
+ return;
+ //PGS_DUMP ("data 0x%02x 0x%02x\n", data[0], data[1]);
+ run_len = (data[0] & 0x3f);
+ pal_id = data[1];
+ data += 2;
+ break;
+ case 0xC0:
+ if (data + 3 > end)
+ return;
+ //PGS_DUMP ("data 0x%02x 0x%02x 0x%02x\n", data[0], data[1], data[2]);
+ run_len = ((data[0] << 8) | data[1]) & 0x3fff;
+ pal_id = data[2];
+ data += 3;
+ break;
+ }
+ }
+
+#if DUMP_FULL_IMAGE
+ {
+ gint i;
+#if 1
+ if (dvdspu->spu_state.pgs.palette[pal_id].A) {
+ guint8 val = dvdspu->spu_state.pgs.palette[pal_id].A;
+ for (i = 0; i < run_len; i++)
+ PGS_DUMP ("%02x ", val);
+ } else {
+ for (i = 0; i < run_len; i++)
+ PGS_DUMP (" ");
+ }
+ if (!run_len || (x + run_len) > obj_w)
+ PGS_DUMP ("\n");
+#else
+ PGS_DUMP ("Run x: %d pix: %d col: %d\n", x, run_len, pal_id);
+#endif
+ }
+#endif
+
+ x += run_len;
+ if (!run_len || x > obj_w)
+ x = 0;
+ };
+
+ PGS_DUMP ("\n");
+}
+
+static void
+pgs_composition_object_render (PgsCompositionObject * obj, SpuState * state,
+ GstBuffer * dest_buf)
+{
+ SpuColour *colour;
+ guint8 *planes[3]; /* YUV frame pointers */
+ guint8 *data, *end;
+ guint16 obj_w, obj_h;
+ guint x, y, i, max_x;
+
+ if (G_UNLIKELY (obj->rle_data == NULL || obj->rle_data_size == 0
+ || obj->rle_data_used != obj->rle_data_size))
+ return;
+
+ data = obj->rle_data;
+ end = data + obj->rle_data_used;
+
+ if (data + 4 > end)
+ return;
+
+ /* FIXME: Calculate and use the cropping window for the output, as the
+ * intersection of the crop rectangle for this object (if any) and the
+ * window specified by the object's window_id */
+
+ /* Store the start of each plane */
+ planes[0] = GST_BUFFER_DATA (dest_buf);
+ planes[1] = planes[0] + (state->Y_height * state->Y_stride);
+ planes[2] = planes[1] + (state->UV_height * state->UV_stride);
+
+ /* Sanity check */
+ g_return_if_fail (planes[2] + (state->UV_height * state->UV_stride) <=
+ GST_BUFFER_DATA (dest_buf) + GST_BUFFER_SIZE (dest_buf));
+
+ x = obj->x;
+ y = obj->y;
+
+ planes[0] += state->Y_stride * y;
+ planes[1] += state->UV_stride * (y / 2);
+ planes[2] += state->UV_stride * (y / 2);
+
+ /* RLE data: */
+ obj_w = GST_READ_UINT16_BE (data);
+ obj_h = GST_READ_UINT16_BE (data + 2);
+ data += 4;
+
+ max_x = x + obj_w;
+
+ state->comp_left = x;
+ state->comp_right = max_x;
+
+ gstspu_clear_comp_buffers (state);
+
+ while (data < end) {
+ guint8 pal_id;
+ guint16 run_len;
+
+ pal_id = *data++;
+ if (pal_id != 0) {
+ run_len = 1;
+ } else {
+ if (data + 1 > end)
+ return;
+ switch (data[0] & 0xC0) {
+ case 0x00:
+ run_len = (data[0] & 0x3f);
+ data++;
+ break;
+ case 0x40:
+ if (data + 2 > end)
+ return;
+ run_len = ((data[0] << 8) | data[1]) & 0x3fff;
+ data += 2;
+ break;
+ case 0x80:
+ if (data + 2 > end)
+ return;
+ run_len = (data[0] & 0x3f);
+ pal_id = data[1];
+ data += 2;
+ break;
+ case 0xC0:
+ if (data + 3 > end)
+ return;
+ run_len = ((data[0] << 8) | data[1]) & 0x3fff;
+ pal_id = data[2];
+ data += 3;
+ break;
+ }
+ }
+
+ colour = &state->pgs.palette[pal_id];
+ if (colour->A) {
+ guint32 inv_A = 0xff - colour->A;
+
+ for (i = 0; i < run_len; i++) {
+ planes[0][x] = (inv_A * planes[0][x] + colour->Y) / 0xff;
+
+ state->comp_bufs[0][x / 2] += colour->U;
+ state->comp_bufs[1][x / 2] += colour->V;
+ state->comp_bufs[2][x / 2] += colour->A;
+ x++;
+ }
+ } else {
+ x += run_len;
+ }
+
+ if (!run_len || x > max_x) {
+ x = state->pgs.win_x;
+ planes[0] += state->Y_stride;
+
+ if (y % 2) {
+ gstspu_blend_comp_buffers (state, planes);
+ gstspu_clear_comp_buffers (state);
+
+ planes[1] += state->UV_stride;
+ planes[2] += state->UV_stride;
+ }
+ y++;
+ }
+ }
+
+ if (y % 2)
+ gstspu_blend_comp_buffers (state, planes);
+}
+
+static void
+pgs_composition_object_clear (PgsCompositionObject * obj)
+{
+ if (obj->rle_data) {
+ g_free (obj->rle_data);
+ obj->rle_data = NULL;
+ }
+ obj->rle_data_size = obj->rle_data_used = 0;
+}
+
+static void
+pgs_presentation_segment_set_object_count (PgsPresentationSegment * ps,
+ guint8 n_objects)
+{
+ if (ps->objects == NULL) {
+ ps->objects =
+ g_array_sized_new (FALSE, TRUE, sizeof (PgsCompositionObject),
+ n_objects);
+ g_array_set_size (ps->objects, n_objects);
+ return;
+ }
+
+ /* Clear memory in any extraneous objects */
+ if (ps->objects->len > n_objects) {
+ guint i;
+ for (i = n_objects; i < ps->objects->len; i++) {
+ PgsCompositionObject *cur =
+ &g_array_index (ps->objects, PgsCompositionObject, i);
+ pgs_composition_object_clear (cur);
+ }
+ }
+
+ g_array_set_size (ps->objects, n_objects);
+
+ if (n_objects == 0) {
+ g_array_free (ps->objects, TRUE);
+ ps->objects = NULL;
+ }
+}
+
+static PgsCompositionObject *
+pgs_presentation_segment_find_object (PgsPresentationSegment * ps,
+ guint16 obj_id)
+{
+ guint i;
+ if (ps->objects == NULL)
+ return NULL;
+
+ for (i = 0; i < ps->objects->len; i++) {
+ PgsCompositionObject *cur =
+ &g_array_index (ps->objects, PgsCompositionObject, i);
+ if (cur->id == obj_id)
+ return cur;
+ }
+
+ return NULL;
+}
+
+static int
+parse_presentation_segment (GstDVDSpu * dvdspu, guint8 type, guint8 * payload,
+ guint16 len)
+{
+ guint8 *end = payload + len;
+ PgsPresentationSegment *ps = &dvdspu->spu_state.pgs.pres_seg;
+ guint8 n_objects, palette_id;
+ gint i;
+
+ /* Parse video descriptor */
+ if (payload + 5 > end)
+ return 0;
+
+ ps->vid_w = GST_READ_UINT16_BE (payload);
+ ps->vid_h = GST_READ_UINT16_BE (payload + 2);
+ ps->vid_fps_code = payload[4];
+ payload += 5;
+
+ /* Parse composition descriptor */
+ if (payload + 3 > end)
+ return 0;
+ ps->composition_no = GST_READ_UINT16_BE (payload);
+ ps->composition_state = payload[2];
+ payload += 3;
+
+ /* Parse other bits */
+ if (payload + 3 > end)
+ return 0;
+
+ ps->flags = payload[0];
+
+ palette_id = payload[1];
+ n_objects = payload[2];
+ payload += 3;
+
+ if (ps->flags & PGS_PRES_SEGMENT_FLAG_UPDATE_PALETTE)
+ ps->palette_id = palette_id;
+
+ PGS_DUMP ("Video width %u height %u fps code %u\n", ps->vid_w, ps->vid_h,
+ ps->vid_fps_code);
+ PGS_DUMP
+ ("Composition num %u state 0x%02x flags 0x%02x palette id %u n_objects %u\n",
+ ps->composition_no, ps->composition_state, ps->flags, ps->palette_id,
+ n_objects);
+
+ pgs_presentation_segment_set_object_count (ps, n_objects);
+
+ for (i = 0; i < (gint) n_objects; i++) {
+ PgsCompositionObject *obj =
+ &g_array_index (ps->objects, PgsCompositionObject, i);
+
+ if (payload + 8 > end)
+ break;
+ obj->id = GST_READ_UINT16_BE (payload);
+ obj->win_id = payload[2];
+ obj->flags = payload[3];
+ obj->x = GST_READ_UINT16_BE (payload + 4);
+ obj->y = GST_READ_UINT16_BE (payload + 6);
+ obj->rle_data_size = obj->rle_data_used = 0;
+
+ payload += 8;
+
+ PGS_DUMP ("Composition object %d Object ID %u Window ID %u flags 0x%02x "
+ "x %u y %u\n", i, obj->id, obj->win_id, obj->flags, obj->x, obj->y);
+
+ if (obj->flags & PGS_COMPOSITION_OBJECT_FLAG_CROPPED) {
+ if (payload + 8 > end)
+ break;
+
+ obj->crop_x = GST_READ_UINT16_BE (payload);
+ obj->crop_y = GST_READ_UINT16_BE (payload + 2);
+ obj->crop_w = GST_READ_UINT16_BE (payload + 4);
+ obj->crop_h = GST_READ_UINT16_BE (payload + 6);
+
+ payload += 8;
+
+ PGS_DUMP ("Cropping window x %u y %u w %u h %u\n",
+ obj->crop_x, obj->crop_y, obj->crop_w, obj->crop_h);
+ }
+
+ if (obj->flags & ~(PGS_COMPOSITION_OBJECT_FLAG_CROPPED |
+ PGS_COMPOSITION_OBJECT_FLAG_FORCED))
+ g_warning ("PGS Composition Object has unknown flags: 0x%02x",
+ obj->flags);
+ }
+
+ if (payload != end) {
+ g_warning ("PGS Composition Object: %ld bytes not consumed", end - payload);
+ dump_bytes (payload, end - payload);
+ }
+
+ return 0;
+}
+
+static int
+parse_set_palette (GstDVDSpu * dvdspu, guint8 type, guint8 * payload,
+ guint16 len)
+{
+ SpuState *state = &dvdspu->spu_state;
+
+ const gint PGS_PALETTE_ENTRY_SIZE = 5;
+ guint8 *end = payload + len;
+ guint8 palette_id;
+ guint8 palette_version;
+ gint n_entries, i;
+
+ if (len < 2) /* Palette command too short */
+ return 0;
+ palette_id = payload[0];
+ palette_version = payload[1];
+ payload += 2;
+
+ n_entries = (len - 2) / PGS_PALETTE_ENTRY_SIZE;
+
+ PGS_DUMP ("Palette ID %u version %u. %d entries\n",
+ palette_id, palette_version, n_entries);
+ for (i = 0; i < 256; i++)
+ state->pgs.palette[i].A = 0;
+ for (i = 0; i < n_entries; i++) {
+ guint8 n, Y, U, V, A;
+ n = payload[0];
+ Y = payload[1];
+ U = payload[2];
+ V = payload[3];
+ A = payload[4];
+
+#if DUMP_FULL_PALETTE
+ PGS_DUMP ("Entry %3d: Y %3d U %3d V %3d A %3d ", n, Y, U, V, A);
+ if (((i + 1) % 2) == 0)
+ PGS_DUMP ("\n");
+#endif
+
+ /* Premultiply the palette entries by the alpha */
+ state->pgs.palette[n].Y = Y * A;
+ state->pgs.palette[n].U = U * A;
+ state->pgs.palette[n].V = V * A;
+ state->pgs.palette[n].A = A;
+
+ payload += PGS_PALETTE_ENTRY_SIZE;
+ }
+
+#if DUMP_FULL_PALETTE
+ if (n_entries > 0 && (i % 2))
+ PGS_DUMP ("\n");
+#endif
+
+ if (payload != end) {
+ g_warning ("PGS Set Palette: %ld bytes not consumed", end - payload);
+ dump_bytes (payload, end - payload);
+ }
+
+ return 0;
+}
+
+static int
+parse_set_window (GstDVDSpu * dvdspu, guint8 type, guint8 * payload,
+ guint16 len)
+{
+ SpuState *state = &dvdspu->spu_state;
+ guint8 *end = payload + len;
+ guint8 win_id, win_ver;
+
+ if (payload + 10 > end)
+ return 0;
+
+ dump_bytes (payload, len);
+
+ /* FIXME: This is just a guess as to what the numbers mean: */
+ win_id = payload[0];
+ win_ver = payload[1];
+ state->pgs.win_x = GST_READ_UINT16_BE (payload + 2);
+ state->pgs.win_y = GST_READ_UINT16_BE (payload + 4);
+ state->pgs.win_w = GST_READ_UINT16_BE (payload + 6);
+ state->pgs.win_h = GST_READ_UINT16_BE (payload + 8);
+ payload += 10;
+
+ PGS_DUMP ("Win ID %u version %d x %d y %d w %d h %d\n",
+ win_id, win_ver, state->pgs.win_x, state->pgs.win_y, state->pgs.win_w,
+ state->pgs.win_h);
+
+ if (payload != end) {
+ g_warning ("PGS Set Window: %ld bytes not consumed", end - payload);
+ dump_bytes (payload, end - payload);
+ }
+
+ return 0;
+}
+
+static int
+parse_set_object_data (GstDVDSpu * dvdspu, guint8 type, guint8 * payload,
+ guint16 len)
+{
+ SpuPgsState *pgs_state = &dvdspu->spu_state.pgs;
+ PgsCompositionObject *obj;
+ guint8 *end = payload + len;
+ guint16 obj_id;
+ guint8 obj_ver, flags;
+
+ if (payload + 4 > end)
+ return 0;
+
+ obj_id = GST_READ_UINT16_BE (payload);
+ obj_ver = payload[2];
+ flags = payload[3];
+ payload += 4;
+
+ obj = pgs_presentation_segment_find_object (&(pgs_state->pres_seg), obj_id);
+
+ PGS_DUMP ("Object ID %d ver %u flags 0x%02x\n", obj_id, obj_ver, flags);
+
+ if (flags & PGS_OBJECT_UPDATE_FLAG_START_RLE) {
+ obj->rle_data_ver = obj_ver;
+
+ if (payload + 3 > end)
+ return 0;
+
+ obj->rle_data_size = GST_READ_UINT24_BE (payload);
+ payload += 3;
+
+ PGS_DUMP ("%d bytes of RLE data, of %d bytes total.\n",
+ end - payload, obj->rle_data_size);
+
+ obj->rle_data = g_realloc (obj->rle_data, obj->rle_data_size);
+ obj->rle_data_used = end - payload;
+ memcpy (obj->rle_data, payload, end - payload);
+ payload = end;
+ } else {
+ PGS_DUMP ("%d bytes of additional RLE data\n", end - payload);
+ /* Check that the data chunk is for this object version, and fits in the buffer */
+ if (obj->rle_data_ver == obj_ver &&
+ obj->rle_data_used + end - payload <= obj->rle_data_size) {
+
+ memcpy (obj->rle_data + obj->rle_data_used, payload, end - payload);
+ obj->rle_data_used += end - payload;
+ payload = end;
+ }
+ }
+
+ if (obj->rle_data_size == obj->rle_data_used)
+ dump_rle_data (dvdspu, obj->rle_data, obj->rle_data_size);
+
+ if (payload != end) {
+ g_warning ("PGS Set Object Data: %ld bytes not consumed", end - payload);
+ dump_bytes (payload, end - payload);
+ }
+
+ return 0;
+}
+
+static int
+parse_pgs_packet (GstDVDSpu * dvdspu, guint8 type, guint8 * payload,
+ guint16 len)
+{
+ SpuPgsState *pgs_state = &dvdspu->spu_state.pgs;
+ int ret = 0;
+
+ if (!pgs_state->in_presentation_segment
+ && type != PGS_COMMAND_PRESENTATION_SEGMENT) {
+ PGS_DUMP ("Expected BEGIN PRESENTATION SEGMENT command. "
+ "Got command type 0x%02x len %u. Skipping\n", type, len);
+ return 0;
+ }
+
+ switch (type) {
+ case PGS_COMMAND_PRESENTATION_SEGMENT:
+ PGS_DUMP ("*******************************************\n"
+ "Begin PRESENTATION_SEGMENT (0x%02x) packet len %u\n", type, len);
+ pgs_state->in_presentation_segment =
+ pgs_state->have_presentation_segment = TRUE;
+ ret = parse_presentation_segment (dvdspu, type, payload, len);
+ break;
+ case PGS_COMMAND_SET_OBJECT_DATA:
+ PGS_DUMP ("*** Set Object Data (0x%02x) packet len %u\n", type, len);
+ ret = parse_set_object_data (dvdspu, type, payload, len);
+ break;
+ case PGS_COMMAND_SET_PALETTE:
+ PGS_DUMP ("*** Set Palette (0x%02x) packet len %u\n", type, len);
+ ret = parse_set_palette (dvdspu, type, payload, len);
+ break;
+ case PGS_COMMAND_SET_WINDOW:
+ PGS_DUMP ("*** Set Window command (0x%02x) packet len %u\n", type, len);
+ ret = parse_set_window (dvdspu, type, payload, len);
+ break;
+ case PGS_COMMAND_INTERACTIVE_SEGMENT:
+ PGS_DUMP ("*** Interactive Segment command(0x%02x) packet len %u\n",
+ type, len);
+ dump_bytes (payload, len);
+ break;
+ case PGS_COMMAND_END_DISPLAY:
+ PGS_DUMP ("*** End Display command (0x%02x) packet len %u\n", type,
+ len);
+ pgs_state->in_presentation_segment = FALSE;
+ break;
+ default:
+ g_warning ("Unknown PGS command: type 0x%02x len %u", type, len);
+ dump_bytes (payload, len);
+ break;
+ }
+ PGS_DUMP ("\n");
+
+ return ret;
+}
+
+gint
+gstspu_exec_pgs_buffer (GstDVDSpu * dvdspu, GstBuffer * buf)
+{
+ guint8 *pos, *end;
+ guint8 type;
+ guint16 packet_len;
+
+ pos = GST_BUFFER_DATA (buf);
+ end = pos + GST_BUFFER_SIZE (buf);
+
+ /* Need at least 3 bytes */
+ if (pos + 3 > end) {
+ PGS_DUMP ("Not enough bytes to be a PGS packet\n");
+ return -1;
+ }
+
+ PGS_DUMP ("Begin dumping command buffer of size %u ts %" GST_TIME_FORMAT "\n",
+ end - pos, GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buf)));
+ do {
+ type = *pos++;
+ packet_len = GST_READ_UINT16_BE (pos);
+ pos += 2;
+
+ if (pos + packet_len > end) {
+ PGS_DUMP ("Invalid packet length %u (only have %u bytes)\n", packet_len,
+ end - pos);
+ return -1;
+ }
+
+ if (parse_pgs_packet (dvdspu, type, pos, packet_len))
+ return -1;
+
+ pos += packet_len;
+ } while (pos + 3 <= end);
+
+ PGS_DUMP ("End dumping command buffer with %u bytes remaining\n", end - pos);
+ return (pos - GST_BUFFER_DATA (buf));
+}
+
+void
+gstspu_pgs_handle_new_buf (GstDVDSpu * dvdspu, GstClockTime event_ts,
+ GstBuffer * buf)
+{
+ SpuState *state = &dvdspu->spu_state;
+
+ state->next_ts = event_ts;
+ state->pgs.pending_cmd = buf;
+}
+
+gboolean
+gstspu_pgs_execute_event (GstDVDSpu * dvdspu)
+{
+ SpuState *state = &dvdspu->spu_state;
+
+ if (state->pgs.pending_cmd) {
+ gstspu_exec_pgs_buffer (dvdspu, state->pgs.pending_cmd);
+ gst_buffer_unref (state->pgs.pending_cmd);
+ state->pgs.pending_cmd = NULL;
+ }
+
+ state->next_ts = GST_CLOCK_TIME_NONE;
+
+ state->flags &= ~SPU_STATE_DISPLAY;
+ if (state->pgs.have_presentation_segment) {
+ if (state->pgs.pres_seg.objects && state->pgs.pres_seg.objects->len > 0)
+ state->flags |= SPU_STATE_DISPLAY;
+ }
+ return FALSE;
+}
+
+void
+gstspu_pgs_render (GstDVDSpu * dvdspu, GstBuffer * buf)
+{
+ SpuState *state = &dvdspu->spu_state;
+ PgsPresentationSegment *ps = &state->pgs.pres_seg;
+ guint i;
+
+ if (ps->objects == NULL)
+ return;
+
+ for (i = 0; i < ps->objects->len; i++) {
+ PgsCompositionObject *cur =
+ &g_array_index (ps->objects, PgsCompositionObject, i);
+ pgs_composition_object_render (cur, state, buf);
+ }
+}
+
+gboolean
+gstspu_pgs_handle_dvd_event (GstDVDSpu * dvdspu, GstEvent * event)
+{
+ return FALSE;
+}
+
+void
+gstspu_pgs_flush (GstDVDSpu * dvdspu)
+{
+ SpuPgsState *pgs_state = &dvdspu->spu_state.pgs;
+
+ if (pgs_state->pending_cmd) {
+ gst_buffer_unref (pgs_state->pending_cmd);
+ pgs_state->pending_cmd = NULL;
+ }
+
+ pgs_state->have_presentation_segment = FALSE;
+ pgs_state->in_presentation_segment = FALSE;
+ pgs_presentation_segment_set_object_count (&pgs_state->pres_seg, 0);
+
+ pgs_state->win_x = pgs_state->win_y = pgs_state->win_w = pgs_state->win_h = 0;
+}
diff --git a/gst/dvdspu/gstspu-pgs.h b/gst/dvdspu/gstspu-pgs.h
new file mode 100644
index 00000000..164f4d81
--- /dev/null
+++ b/gst/dvdspu/gstspu-pgs.h
@@ -0,0 +1,106 @@
+/* GStreamer Sub-Picture Unit - PGS handling
+ * Copyright (C) 2009 Jan Schmidt <thaytan@noraisin.net>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __GSTSPU_PGS_H__
+#define __GSTSPU_PGS_H__
+
+#include "gstspu-common.h"
+
+typedef struct SpuPgsState SpuPgsState;
+typedef enum PgsCompositionObjectFlags PgsCompositionObjectFlags;
+typedef enum PgsPresentationSegmentFlags PgsPresentationSegmentFlags;
+typedef enum PgsObjectUpdateFlags PgsObjectUpdateFlags;
+
+typedef struct PgsPresentationSegment PgsPresentationSegment;
+typedef struct PgsCompositionObject PgsCompositionObject;
+
+enum PgsPresentationSegmentFlags
+{
+ PGS_PRES_SEGMENT_FLAG_UPDATE_PALETTE = 0x80
+};
+
+enum PgsCompositionObjectFlags
+{
+ PGS_COMPOSITION_OBJECT_FLAG_CROPPED = 0x80,
+ PGS_COMPOSITION_OBJECT_FLAG_FORCED = 0x40
+};
+
+enum PgsObjectUpdateFlags
+{
+ /* Set in an object_update if this is the beginning of new RLE data.
+ * If not set, the data is a continuation to be appended */
+ PGS_OBJECT_UPDATE_FLAG_START_RLE = 0x80,
+ PGS_OBJECT_UPDATE_FLAG_END_RLE = 0x40 /* This one is a guess */
+};
+
+struct PgsPresentationSegment
+{
+ guint16 composition_no;
+ guint8 composition_state;
+
+ PgsPresentationSegmentFlags flags;
+
+ guint8 palette_id;
+
+ guint16 vid_w, vid_h;
+ guint8 vid_fps_code;
+
+ GArray *objects;
+};
+
+struct PgsCompositionObject
+{
+ guint16 id;
+ guint8 version;
+ PgsCompositionObjectFlags flags;
+
+ guint8 win_id;
+
+ guint8 rle_data_ver;
+ guint8 *rle_data;
+ guint32 rle_data_size;
+ guint32 rle_data_used;
+
+ /* Top left corner of this object */
+ guint16 x, y;
+
+ /* Only valid if PGS_COMPOSITION_OBJECT_FLAG_CROPPED is set */
+ guint16 crop_x, crop_y, crop_w, crop_h;
+};
+
+struct SpuPgsState {
+ GstBuffer *pending_cmd;
+
+ gboolean in_presentation_segment;
+ gboolean have_presentation_segment;
+
+ PgsPresentationSegment pres_seg;
+
+ SpuColour palette[256];
+
+ guint16 win_x, win_y, win_w, win_h;
+};
+
+void gstspu_pgs_handle_new_buf (GstDVDSpu * dvdspu, GstClockTime event_ts, GstBuffer *buf);
+gboolean gstspu_pgs_execute_event (GstDVDSpu *dvdspu);
+void gstspu_pgs_render (GstDVDSpu *dvdspu, GstBuffer *buf);
+gboolean gstspu_pgs_handle_dvd_event (GstDVDSpu *dvdspu, GstEvent *event);
+void gstspu_pgs_flush (GstDVDSpu *dvdspu);
+
+#endif
diff --git a/gst/dvdspu/gstspu-vobsub-render.c b/gst/dvdspu/gstspu-vobsub-render.c
new file mode 100644
index 00000000..07abff22
--- /dev/null
+++ b/gst/dvdspu/gstspu-vobsub-render.c
@@ -0,0 +1,536 @@
+/* GStreamer DVD Sub-Picture Unit
+ * Copyright (C) 2007 Fluendo S.A. <info@fluendo.com>
+ * Copyright (C) 2009 Jan Schmidt <thaytan@noraisin.net>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <string.h>
+
+#include <gst/gst.h>
+
+#include "gstdvdspu.h"
+
+GST_DEBUG_CATEGORY_EXTERN (dvdspu_debug);
+#define GST_CAT_DEFAULT dvdspu_debug
+
+static void
+gstspu_vobsub_recalc_palette (GstDVDSpu * dvdspu,
+ SpuColour * dest, guint8 * idx, guint8 * alpha)
+{
+ SpuState *state = &dvdspu->spu_state;
+ gint i;
+
+ for (i = 0; i < 4; i++, dest++) {
+ guint32 col = state->vobsub.current_clut[idx[i]];
+
+ /* Convert incoming 4-bit alpha to 8 bit for blending */
+ dest->A = (alpha[i] << 4) | alpha[i];
+ dest->Y = ((guint16) ((col >> 16) & 0xff)) * dest->A;
+ /* U/V are stored as V/U in the clut words, so switch them */
+ dest->V = ((guint16) ((col >> 8) & 0xff)) * dest->A;
+ dest->U = ((guint16) (col & 0xff)) * dest->A;
+ }
+}
+
+/* Recalculate the main, HL & ChgCol palettes */
+static void
+gstspu_vobsub_update_palettes (GstDVDSpu * dvdspu, SpuState * state)
+{
+ guint8 index[4]; /* Indices for the palette */
+ guint8 alpha[4]; /* Alpha values the palette */
+
+ if (state->vobsub.main_pal_dirty) {
+ gstspu_vobsub_recalc_palette (dvdspu, state->vobsub.main_pal,
+ state->vobsub.main_idx, state->vobsub.main_alpha);
+
+ /* Need to refresh the hl_ctrl info copies of the main palette too */
+ memcpy (state->vobsub.hl_ctrl_i.pix_ctrl_i[0].pal_cache,
+ state->vobsub.main_pal, 4 * sizeof (SpuColour));
+ memcpy (state->vobsub.hl_ctrl_i.pix_ctrl_i[2].pal_cache,
+ state->vobsub.main_pal, 4 * sizeof (SpuColour));
+
+ state->vobsub.main_pal_dirty = FALSE;
+ }
+
+ if (state->vobsub.hl_pal_dirty) {
+ gstspu_vobsub_recalc_palette (dvdspu,
+ state->vobsub.hl_ctrl_i.pix_ctrl_i[1].pal_cache, state->vobsub.hl_idx,
+ state->vobsub.hl_alpha);
+ state->vobsub.hl_pal_dirty = FALSE;
+ }
+
+ /* Update the offset positions for the highlight region */
+ if (state->vobsub.hl_rect.top != -1) {
+ state->vobsub.hl_ctrl_i.top = state->vobsub.hl_rect.top;
+ state->vobsub.hl_ctrl_i.bottom = state->vobsub.hl_rect.bottom;
+ state->vobsub.hl_ctrl_i.n_changes = 3;
+ state->vobsub.hl_ctrl_i.pix_ctrl_i[0].left = 0;
+ state->vobsub.hl_ctrl_i.pix_ctrl_i[1].left = state->vobsub.hl_rect.left;
+ state->vobsub.hl_ctrl_i.pix_ctrl_i[2].left =
+ state->vobsub.hl_rect.right + 1;
+ }
+
+ if (state->vobsub.line_ctrl_i_pal_dirty) {
+ gint16 l, c;
+ GST_LOG_OBJECT (dvdspu, "Updating chg-col-con palettes");
+ for (l = 0; l < state->vobsub.n_line_ctrl_i; l++) {
+ SpuVobsubLineCtrlI *cur_line_ctrl = state->vobsub.line_ctrl_i + l;
+
+ for (c = 0; c < cur_line_ctrl->n_changes; c++) {
+ SpuVobsubPixCtrlI *cur = cur_line_ctrl->pix_ctrl_i + c;
+
+ index[3] = (cur->palette >> 28) & 0x0f;
+ index[2] = (cur->palette >> 24) & 0x0f;
+ index[1] = (cur->palette >> 20) & 0x0f;
+ index[0] = (cur->palette >> 16) & 0x0f;
+
+ alpha[3] = (cur->palette >> 12) & 0x0f;
+ alpha[2] = (cur->palette >> 8) & 0x0f;
+ alpha[1] = (cur->palette >> 4) & 0x0f;
+ alpha[0] = (cur->palette) & 0x0f;
+ gstspu_vobsub_recalc_palette (dvdspu, cur->pal_cache, index, alpha);
+ }
+ }
+ state->vobsub.line_ctrl_i_pal_dirty = FALSE;
+ }
+}
+
+static inline guint8
+gstspu_vobsub_get_nibble (SpuState * state, guint16 * rle_offset)
+{
+ guint8 ret;
+
+ if (G_UNLIKELY (*rle_offset >= state->vobsub.max_offset))
+ return 0; /* Overran the buffer */
+
+ ret = GST_BUFFER_DATA (state->vobsub.pix_buf)[(*rle_offset) / 2];
+
+ /* If the offset is even, we shift the answer down 4 bits, otherwise not */
+ if (*rle_offset & 0x01)
+ ret &= 0x0f;
+ else
+ ret = ret >> 4;
+
+ (*rle_offset)++;
+ return ret;
+}
+
+static guint16
+gstspu_vobsub_get_rle_code (SpuState * state, guint16 * rle_offset)
+{
+ guint16 code;
+
+ code = gstspu_vobsub_get_nibble (state, rle_offset);
+ if (code < 0x4) { /* 4 .. f */
+ code = (code << 4) | gstspu_vobsub_get_nibble (state, rle_offset);
+ if (code < 0x10) { /* 1x .. 3x */
+ code = (code << 4) | gstspu_vobsub_get_nibble (state, rle_offset);
+ if (code < 0x40) { /* 04x .. 0fx */
+ code = (code << 4) | gstspu_vobsub_get_nibble (state, rle_offset);
+ }
+ }
+ }
+ return code;
+}
+
+static inline void
+gstspu_vobsub_draw_rle_run (SpuState * state, gint16 x, gint16 end,
+ SpuColour * colour)
+{
+#if 0
+ GST_LOG ("Y: %d x: %d end %d col %d %d %d %d",
+ state->vobsub.cur_Y, x, end, colour->Y, colour->U, colour->V, colour->A);
+#endif
+
+ if (colour->A != 0) {
+ guint32 inv_A = 0xff - colour->A;
+
+ /* FIXME: This could be more efficient */
+ while (x < end) {
+ state->vobsub.out_Y[x] =
+ (inv_A * state->vobsub.out_Y[x] + colour->Y) / 0xff;
+ state->vobsub.out_U[x / 2] += colour->U;
+ state->vobsub.out_V[x / 2] += colour->V;
+ state->vobsub.out_A[x / 2] += colour->A;
+ x++;
+ }
+ /* Update the compositing buffer so we know how much to blend later */
+ *(state->vobsub.comp_last_x_ptr) = end;
+ }
+}
+
+static inline gint16
+rle_end_x (guint16 rle_code, gint16 x, gint16 end)
+{
+ /* run length = rle_code >> 2 */
+ if (G_UNLIKELY (((rle_code >> 2) == 0)))
+ return end;
+ else
+ return MIN (end, x + (rle_code >> 2));
+}
+
+static void gstspu_vobsub_render_line_with_chgcol (SpuState * state,
+ guint8 * planes[3], guint16 * rle_offset);
+static gboolean gstspu_vobsub_update_chgcol (SpuState * state);
+
+static void
+gstspu_vobsub_render_line (SpuState * state, guint8 * planes[3],
+ guint16 * rle_offset)
+{
+ gint16 x, next_x, end, rle_code;
+ SpuColour *colour;
+
+ /* Check for special case of chg_col info to use (either highlight or
+ * ChgCol command */
+ if (state->vobsub.cur_chg_col != NULL) {
+ if (gstspu_vobsub_update_chgcol (state)) {
+ /* Check the top & bottom, because we might not be within the region yet */
+ if (state->vobsub.cur_Y >= state->vobsub.cur_chg_col->top &&
+ state->vobsub.cur_Y <= state->vobsub.cur_chg_col->bottom) {
+ gstspu_vobsub_render_line_with_chgcol (state, planes, rle_offset);
+ return;
+ }
+ }
+ }
+
+ /* No special case. Render as normal */
+
+ /* Set up our output pointers */
+ state->vobsub.out_Y = planes[0];
+ state->vobsub.out_U = state->comp_bufs[0];
+ state->vobsub.out_V = state->comp_bufs[1];
+ state->vobsub.out_A = state->comp_bufs[2];
+ /* We always need to start our RLE decoding byte_aligned */
+ *rle_offset = GST_ROUND_UP_2 (*rle_offset);
+
+ x = state->vobsub.disp_rect.left;
+ end = state->vobsub.disp_rect.right + 1;
+ while (x < end) {
+ rle_code = gstspu_vobsub_get_rle_code (state, rle_offset);
+ colour = &state->vobsub.main_pal[rle_code & 3];
+ next_x = rle_end_x (rle_code, x, end);
+ /* Now draw the run between [x,next_x) */
+ gstspu_vobsub_draw_rle_run (state, x, next_x, colour);
+ x = next_x;
+ }
+}
+
+static gboolean
+gstspu_vobsub_update_chgcol (SpuState * state)
+{
+ if (state->vobsub.cur_chg_col == NULL)
+ return FALSE;
+
+ if (state->vobsub.cur_Y <= state->vobsub.cur_chg_col->bottom)
+ return TRUE;
+
+ while (state->vobsub.cur_chg_col < state->vobsub.cur_chg_col_end) {
+ if (state->vobsub.cur_Y >= state->vobsub.cur_chg_col->top &&
+ state->vobsub.cur_Y <= state->vobsub.cur_chg_col->bottom) {
+#if 0
+ g_print ("Stopped @ entry %d with top %d bottom %d, cur_y %d",
+ (gint16) (state->vobsub.cur_chg_col - state->vobsub.line_ctrl_i),
+ state->vobsub.cur_chg_col->top, state->vobsub.cur_chg_col->bottom, y);
+#endif
+ return TRUE;
+ }
+ state->vobsub.cur_chg_col++;
+ }
+
+ /* Finished all our cur_chg_col entries. Use the main palette from here on */
+ state->vobsub.cur_chg_col = NULL;
+ return FALSE;
+}
+
+static void
+gstspu_vobsub_render_line_with_chgcol (SpuState * state, guint8 * planes[3],
+ guint16 * rle_offset)
+{
+ SpuVobsubLineCtrlI *chg_col = state->vobsub.cur_chg_col;
+
+ gint16 x, next_x, disp_end, rle_code, run_end;
+ SpuColour *colour;
+ SpuVobsubPixCtrlI *cur_pix_ctrl;
+ SpuVobsubPixCtrlI *next_pix_ctrl;
+ SpuVobsubPixCtrlI *end_pix_ctrl;
+ SpuVobsubPixCtrlI dummy_pix_ctrl;
+ gint16 cur_reg_end;
+ gint i;
+
+ state->vobsub.out_Y = planes[0];
+ state->vobsub.out_U = state->comp_bufs[0];
+ state->vobsub.out_V = state->comp_bufs[1];
+ state->vobsub.out_A = state->comp_bufs[2];
+
+ /* We always need to start our RLE decoding byte_aligned */
+ *rle_offset = GST_ROUND_UP_2 (*rle_offset);
+
+ /* Our run will cover the display rect */
+ x = state->vobsub.disp_rect.left;
+ disp_end = state->vobsub.disp_rect.right + 1;
+
+ /* Work out the first pixel control info, which may point to the dummy entry if
+ * the global palette/alpha need using initally */
+ cur_pix_ctrl = chg_col->pix_ctrl_i;
+ end_pix_ctrl = chg_col->pix_ctrl_i + chg_col->n_changes;
+
+ if (cur_pix_ctrl->left != 0) {
+ next_pix_ctrl = cur_pix_ctrl;
+ cur_pix_ctrl = &dummy_pix_ctrl;
+ for (i = 0; i < 4; i++) /* Copy the main palette to our dummy entry */
+ dummy_pix_ctrl.pal_cache[i] = state->vobsub.main_pal[i];
+ } else {
+ next_pix_ctrl = cur_pix_ctrl + 1;
+ }
+ if (next_pix_ctrl < end_pix_ctrl)
+ cur_reg_end = next_pix_ctrl->left;
+ else
+ cur_reg_end = disp_end;
+
+ /* Render stuff */
+ while (x < disp_end) {
+ rle_code = gstspu_vobsub_get_rle_code (state, rle_offset);
+ next_x = rle_end_x (rle_code, x, disp_end);
+
+ /* Now draw the run between [x,next_x), crossing palette regions as needed */
+ while (x < next_x) {
+ run_end = MIN (next_x, cur_reg_end);
+
+ if (G_LIKELY (x < run_end)) {
+ colour = &cur_pix_ctrl->pal_cache[rle_code & 3];
+ gstspu_vobsub_draw_rle_run (state, x, run_end, colour);
+ x = run_end;
+ }
+
+ if (x >= cur_reg_end) {
+ /* Advance to next region */
+ cur_pix_ctrl = next_pix_ctrl;
+ next_pix_ctrl++;
+
+ if (next_pix_ctrl < end_pix_ctrl)
+ cur_reg_end = next_pix_ctrl->left;
+ else
+ cur_reg_end = disp_end;
+ }
+ }
+ }
+}
+
+static void
+gstspu_vobsub_blend_comp_buffers (SpuState * state, guint8 * planes[3])
+{
+ state->comp_left = state->vobsub.disp_rect.left;
+ state->comp_right =
+ MAX (state->vobsub.comp_last_x[0], state->vobsub.comp_last_x[1]);
+
+ gstspu_blend_comp_buffers (state, planes);
+}
+
+void
+gstspu_vobsub_clear_comp_buffers (SpuState * state)
+{
+ state->comp_left = state->vobsub.disp_rect.left;
+ state->comp_right = state->vobsub.disp_rect.right;
+
+ gstspu_clear_comp_buffers (state);
+
+ state->vobsub.comp_last_x[0] = -1;
+ state->vobsub.comp_last_x[1] = -1;
+}
+
+void
+gstspu_vobsub_render (GstDVDSpu * dvdspu, GstBuffer * buf)
+{
+ SpuState *state = &dvdspu->spu_state;
+ guint8 *planes[3]; /* YUV frame pointers */
+ gint y, last_y;
+
+ /* Set up our initial state */
+ if (G_UNLIKELY (state->vobsub.pix_buf == NULL))
+ return;
+
+ /* Store the start of each plane */
+ planes[0] = GST_BUFFER_DATA (buf);
+ planes[1] = planes[0] + (state->Y_height * state->Y_stride);
+ planes[2] = planes[1] + (state->UV_height * state->UV_stride);
+
+ /* Sanity check */
+ g_return_if_fail (planes[2] + (state->UV_height * state->UV_stride) <=
+ GST_BUFFER_DATA (buf) + GST_BUFFER_SIZE (buf));
+
+ GST_DEBUG ("Rendering SPU. disp_rect %d,%d to %d,%d. hl_rect %d,%d to %d,%d",
+ state->vobsub.disp_rect.left, state->vobsub.disp_rect.top,
+ state->vobsub.disp_rect.right, state->vobsub.disp_rect.bottom,
+ state->vobsub.hl_rect.left, state->vobsub.hl_rect.top,
+ state->vobsub.hl_rect.right, state->vobsub.hl_rect.bottom);
+
+ GST_DEBUG ("vid_disp %d,%d", state->vid_width, state->vid_height);
+
+ /* When reading RLE data, we track the offset in nibbles... */
+ state->vobsub.cur_offsets[0] = state->vobsub.pix_data[0] * 2;
+ state->vobsub.cur_offsets[1] = state->vobsub.pix_data[1] * 2;
+ state->vobsub.max_offset = GST_BUFFER_SIZE (state->vobsub.pix_buf) * 2;
+
+ /* Update all the palette caches */
+ gstspu_vobsub_update_palettes (dvdspu, state);
+
+ /* Set up HL or Change Color & Contrast rect tracking */
+ if (state->vobsub.hl_rect.top != -1) {
+ state->vobsub.cur_chg_col = &state->vobsub.hl_ctrl_i;
+ state->vobsub.cur_chg_col_end = state->vobsub.cur_chg_col + 1;
+ } else if (state->vobsub.n_line_ctrl_i > 0) {
+ state->vobsub.cur_chg_col = state->vobsub.line_ctrl_i;
+ state->vobsub.cur_chg_col_end =
+ state->vobsub.cur_chg_col + state->vobsub.n_line_ctrl_i;
+ } else
+ state->vobsub.cur_chg_col = NULL;
+
+ /* We start rendering from the first line of the display rect */
+ y = state->vobsub.disp_rect.top;
+ /* start_y is always an even number and we render lines in pairs from there,
+ * accumulating 2 lines of chroma then blending it. We might need to render a
+ * single line at the end if the display rect ends on an even line too. */
+ last_y = (state->vobsub.disp_rect.bottom - 1) & ~(0x01);
+
+ /* center the image when display rectangle exceeds the video width */
+ if (state->vid_width < state->vobsub.disp_rect.right) {
+ gint diff, disp_width;
+
+ disp_width = state->vobsub.disp_rect.left - state->vobsub.disp_rect.right;
+ diff = (disp_width - state->vid_width) / 2;
+
+ /* fixme, this is not used yet */
+ state->vobsub.clip_rect.left = state->vobsub.disp_rect.left + diff;
+ state->vobsub.clip_rect.right = state->vobsub.disp_rect.right - diff;
+
+ GST_DEBUG ("clipping width to %d,%d", state->vobsub.clip_rect.left,
+ state->vobsub.clip_rect.right);
+ } else {
+ state->vobsub.clip_rect.left = state->vobsub.disp_rect.left;
+ state->vobsub.clip_rect.right = state->vobsub.disp_rect.right;
+ }
+
+ /* for the height, chop off the bottom bits of the diplay rectangle because we
+ * assume the picture is in the lower part. We should better check where it
+ * is and do something more clever. */
+ state->vobsub.clip_rect.bottom = state->vobsub.disp_rect.bottom;
+ if (state->vid_height < state->vobsub.disp_rect.bottom) {
+ state->vobsub.clip_rect.top =
+ state->vobsub.disp_rect.bottom - state->vid_height;
+ GST_DEBUG ("clipping height to %d,%d", state->vobsub.clip_rect.top,
+ state->vobsub.clip_rect.bottom);
+ } else {
+ state->vobsub.clip_rect.top = state->vobsub.disp_rect.top;
+ /* Update our plane references to the first line of the disp_rect */
+ planes[0] += state->Y_stride * y;
+ planes[1] += state->UV_stride * (y / 2);
+ planes[2] += state->UV_stride * (y / 2);
+ }
+
+ for (state->vobsub.cur_Y = y; state->vobsub.cur_Y <= last_y;
+ state->vobsub.cur_Y++) {
+ gboolean clip;
+
+ clip = (state->vobsub.cur_Y < state->vobsub.clip_rect.top
+ || state->vobsub.cur_Y > state->vobsub.clip_rect.bottom);
+
+ /* Reset the compositing buffer */
+ gstspu_clear_comp_buffers (state);
+ /* Render even line */
+ state->vobsub.comp_last_x_ptr = state->vobsub.comp_last_x;
+ gstspu_vobsub_render_line (state, planes, &state->vobsub.cur_offsets[0]);
+ if (!clip) {
+ /* Advance the luminance output pointer */
+ planes[0] += state->Y_stride;
+ }
+ state->vobsub.cur_Y++;
+
+ /* Render odd line */
+ state->vobsub.comp_last_x_ptr = state->vobsub.comp_last_x + 1;
+ gstspu_vobsub_render_line (state, planes, &state->vobsub.cur_offsets[1]);
+ /* Blend the accumulated UV compositing buffers onto the output */
+ gstspu_vobsub_blend_comp_buffers (state, planes);
+
+ if (!clip) {
+ /* Update all the output pointers */
+ planes[0] += state->Y_stride;
+ planes[1] += state->UV_stride;
+ planes[2] += state->UV_stride;
+ }
+ }
+ if (state->vobsub.cur_Y == state->vobsub.disp_rect.bottom) {
+ g_assert ((state->vobsub.disp_rect.bottom & 0x01) == 0);
+
+ /* Render a remaining lone last even line. y already has the correct value
+ * after the above loop exited. */
+ gstspu_clear_comp_buffers (state);
+ state->vobsub.comp_last_x_ptr = state->vobsub.comp_last_x;
+ gstspu_vobsub_render_line (state, planes, &state->vobsub.cur_offsets[0]);
+ gstspu_vobsub_blend_comp_buffers (state, planes);
+ }
+
+ /* for debugging purposes, draw a faint rectangle at the edges of the disp_rect */
+#if 0
+ do {
+ guint8 *cur;
+ gint16 pos;
+
+ cur = GST_BUFFER_DATA (buf) + state->Y_stride * state->vobsub.disp_rect.top;
+ for (pos = state->vobsub.disp_rect.left + 1;
+ pos < state->vobsub.disp_rect.right; pos++)
+ cur[pos] = (cur[pos] / 2) + 0x8;
+ cur =
+ GST_BUFFER_DATA (buf) +
+ state->Y_stride * state->vobsub.disp_rect.bottom;
+ for (pos = state->vobsub.disp_rect.left + 1;
+ pos < state->vobsub.disp_rect.right; pos++)
+ cur[pos] = (cur[pos] / 2) + 0x8;
+ cur = GST_BUFFER_DATA (buf) + state->Y_stride * state->vobsub.disp_rect.top;
+ for (pos = state->vobsub.disp_rect.top;
+ pos <= state->vobsub.disp_rect.bottom; pos++) {
+ cur[state->vobsub.disp_rect.left] =
+ (cur[state->vobsub.disp_rect.left] / 2) + 0x8;
+ cur[state->vobsub.disp_rect.right] =
+ (cur[state->vobsub.disp_rect.right] / 2) + 0x8;
+ cur += state->Y_stride;
+ }
+ } while (0);
+#endif
+ /* For debugging purposes, draw a faint rectangle around the highlight rect */
+#if 0
+ if (state->hl_rect.top != -1) {
+ guint8 *cur;
+ gint16 pos;
+
+ cur = GST_BUFFER_DATA (buf) + state->Y_stride * state->hl_rect.top;
+ for (pos = state->hl_rect.left + 1; pos < state->hl_rect.right; pos++)
+ cur[pos] = (cur[pos] / 2) + 0x8;
+ cur = GST_BUFFER_DATA (buf) + state->Y_stride * state->hl_rect.bottom;
+ for (pos = state->hl_rect.left + 1; pos < state->hl_rect.right; pos++)
+ cur[pos] = (cur[pos] / 2) + 0x8;
+ cur = GST_BUFFER_DATA (buf) + state->Y_stride * state->hl_rect.top;
+ for (pos = state->hl_rect.top; pos <= state->hl_rect.bottom; pos++) {
+ cur[state->hl_rect.left] = (cur[state->hl_rect.left] / 2) + 0x8;
+ cur[state->hl_rect.right] = (cur[state->hl_rect.right] / 2) + 0x8;
+ cur += state->Y_stride;
+ }
+ }
+#endif
+}
diff --git a/gst/dvdspu/gstspu-vobsub.c b/gst/dvdspu/gstspu-vobsub.c
new file mode 100644
index 00000000..1757feb7
--- /dev/null
+++ b/gst/dvdspu/gstspu-vobsub.c
@@ -0,0 +1,513 @@
+/* GStreamer Sub-Picture Unit - VobSub/DVD handling
+ * Copyright (C) 2009 Jan Schmidt <thaytan@noraisin.net>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <string.h>
+
+#include <gst/gst.h>
+
+#include "gstdvdspu.h"
+#include "gstspu-vobsub.h"
+
+GST_DEBUG_CATEGORY_EXTERN (dvdspu_debug);
+#define GST_CAT_DEFAULT dvdspu_debug
+
+/* Define to dump out a text description of the incoming SPU commands */
+#define DUMP_DCSQ 0
+
+/* Convert an STM offset in the SPU sequence to a GStreamer timestamp */
+#define STM_TO_GST(stm) ((GST_MSECOND * 1024 * (stm)) / 90)
+
+typedef enum SpuVobsubCmd SpuVobsubCmd;
+
+enum SpuVobsubCmd
+{
+ SPU_CMD_FSTA_DSP = 0x00, /* Forced Display */
+ SPU_CMD_DSP = 0x01, /* Display Start */
+ SPU_CMD_STP_DSP = 0x02, /* Display Off */
+ SPU_CMD_SET_COLOR = 0x03, /* Set the color indexes for the palette */
+ SPU_CMD_SET_ALPHA = 0x04, /* Set the alpha indexes for the palette */
+ SPU_CMD_SET_DAREA = 0x05, /* Set the display area for the SPU */
+ SPU_CMD_DSPXA = 0x06, /* Pixel data addresses */
+ SPU_CMD_CHG_COLCON = 0x07, /* Change Color & Contrast */
+ SPU_CMD_END = 0xff
+};
+
+static void
+gst_dvd_spu_parse_chg_colcon (GstDVDSpu * dvdspu, guint8 * data, guint8 * end)
+{
+ SpuState *state = &dvdspu->spu_state;
+ guint8 *cur;
+ gint16 n_entries;
+ gint16 i;
+
+ /* Clear any existing chg colcon info */
+ state->vobsub.n_line_ctrl_i = 0;
+ if (state->vobsub.line_ctrl_i != NULL) {
+ g_free (state->vobsub.line_ctrl_i);
+ state->vobsub.line_ctrl_i = NULL;
+ }
+ GST_DEBUG_OBJECT (dvdspu, "Change Color & Contrast. Pixel data = %d bytes",
+ (gint16) (end - data));
+
+ /* Count the number of entries we'll need */
+ n_entries = 0;
+ for (cur = data; cur < end;) {
+ guint8 n_changes;
+ guint32 code;
+
+ if (cur + 4 > end)
+ break;
+
+ code = GST_READ_UINT32_BE (cur);
+ if (code == 0x0fffffff)
+ break; /* Termination code */
+
+ n_changes = CLAMP ((cur[2] >> 4), 1, 8);
+ cur += 4 + (6 * n_changes);
+
+ if (cur > end)
+ break; /* Invalid entry overrunning buffer */
+
+ n_entries++;
+ }
+
+ state->vobsub.n_line_ctrl_i = n_entries;
+ state->vobsub.line_ctrl_i = g_new (SpuVobsubLineCtrlI, n_entries);
+
+ cur = data;
+ for (i = 0; i < n_entries; i++) {
+ SpuVobsubLineCtrlI *cur_line_ctrl = state->vobsub.line_ctrl_i + i;
+ guint8 n_changes = CLAMP ((cur[2] >> 4), 1, 8);
+ guint8 c;
+
+ cur_line_ctrl->n_changes = n_changes;
+ cur_line_ctrl->top = ((cur[0] << 8) & 0x300) | cur[1];
+ cur_line_ctrl->bottom = ((cur[2] << 8) & 0x300) | cur[3];
+
+ GST_LOG_OBJECT (dvdspu, "ChgColcon Entry %d Top: %d Bottom: %d Changes: %d",
+ i, cur_line_ctrl->top, cur_line_ctrl->bottom, n_changes);
+ cur += 4;
+
+ for (c = 0; c < n_changes; c++) {
+ SpuVobsubPixCtrlI *cur_pix_ctrl = cur_line_ctrl->pix_ctrl_i + c;
+
+ cur_pix_ctrl->left = ((cur[0] << 8) & 0x300) | cur[1];
+ cur_pix_ctrl->palette = GST_READ_UINT32_BE (cur + 2);
+ GST_LOG_OBJECT (dvdspu, " %d: left: %d palette 0x%x", c,
+ cur_pix_ctrl->left, cur_pix_ctrl->palette);
+ cur += 6;
+ }
+ }
+}
+
+static void
+gst_dvd_spu_exec_cmd_blk (GstDVDSpu * dvdspu, guint8 * data, guint8 * end)
+{
+ SpuState *state = &dvdspu->spu_state;
+
+ while (data < end) {
+ guint8 cmd;
+
+ cmd = data[0];
+
+ switch (cmd) {
+ case SPU_CMD_FSTA_DSP:
+ GST_DEBUG_OBJECT (dvdspu, " Forced Display");
+ state->flags |= SPU_STATE_FORCED_DSP;
+ data += 1;
+ break;
+ case SPU_CMD_DSP:
+ GST_DEBUG_OBJECT (dvdspu, " Display On");
+ state->flags |= SPU_STATE_DISPLAY;
+ data += 1;
+ break;
+ case SPU_CMD_STP_DSP:
+ GST_DEBUG_OBJECT (dvdspu, " Display Off");
+ state->flags &= ~(SPU_STATE_FORCED_DSP | SPU_STATE_DISPLAY);
+ data += 1;
+ break;
+ case SPU_CMD_SET_COLOR:{
+ if (G_UNLIKELY (data + 3 >= end))
+ return; /* Invalid SET_COLOR cmd at the end of the blk */
+
+ state->vobsub.main_idx[3] = data[1] >> 4;
+ state->vobsub.main_idx[2] = data[1] & 0x0f;
+ state->vobsub.main_idx[1] = data[2] >> 4;
+ state->vobsub.main_idx[0] = data[2] & 0x0f;
+
+ state->vobsub.main_pal_dirty = TRUE;
+
+ GST_DEBUG_OBJECT (dvdspu,
+ " Set Color bg %u pattern %u emph-1 %u emph-2 %u",
+ state->vobsub.main_idx[0], state->vobsub.main_idx[1],
+ state->vobsub.main_idx[2], state->vobsub.main_idx[3]);
+ data += 3;
+ break;
+ }
+ case SPU_CMD_SET_ALPHA:{
+ if (G_UNLIKELY (data + 3 >= end))
+ return; /* Invalid SET_ALPHA cmd at the end of the blk */
+
+ state->vobsub.main_alpha[3] = data[1] >> 4;
+ state->vobsub.main_alpha[2] = data[1] & 0x0f;
+ state->vobsub.main_alpha[1] = data[2] >> 4;
+ state->vobsub.main_alpha[0] = data[2] & 0x0f;
+
+ state->vobsub.main_pal_dirty = TRUE;
+
+ GST_DEBUG_OBJECT (dvdspu,
+ " Set Alpha bg %u pattern %u emph-1 %u emph-2 %u",
+ state->vobsub.main_alpha[0], state->vobsub.main_alpha[1],
+ state->vobsub.main_alpha[2], state->vobsub.main_alpha[3]);
+ data += 3;
+ break;
+ }
+ case SPU_CMD_SET_DAREA:{
+ SpuRect *r = &state->vobsub.disp_rect;
+
+ if (G_UNLIKELY (data + 7 >= end))
+ return; /* Invalid SET_DAREA cmd at the end of the blk */
+
+ r->top = ((data[4] & 0x3f) << 4) | ((data[5] & 0xe0) >> 4);
+ r->left = ((data[1] & 0x3f) << 4) | ((data[2] & 0xf0) >> 4);
+ r->right = ((data[2] & 0x03) << 8) | data[3];
+ r->bottom = ((data[5] & 0x03) << 8) | data[6];
+
+ GST_DEBUG_OBJECT (dvdspu,
+ " Set Display Area top %u left %u bottom %u right %u", r->top,
+ r->left, r->bottom, r->right);
+
+ data += 7;
+ break;
+ }
+ case SPU_CMD_DSPXA:{
+ if (G_UNLIKELY (data + 5 >= end))
+ return; /* Invalid SET_DSPXE cmd at the end of the blk */
+
+ state->vobsub.pix_data[0] = GST_READ_UINT16_BE (data + 1);
+ state->vobsub.pix_data[1] = GST_READ_UINT16_BE (data + 3);
+ /* Store a reference to the current command buffer, as that's where
+ * we'll need to take our pixel data from */
+ gst_buffer_replace (&state->vobsub.pix_buf, state->vobsub.buf);
+
+ GST_DEBUG_OBJECT (dvdspu, " Set Pixel Data Offsets top: %u bot: %u",
+ state->vobsub.pix_data[0], state->vobsub.pix_data[1]);
+
+ data += 5;
+ break;
+ }
+ case SPU_CMD_CHG_COLCON:{
+ guint16 field_size;
+
+ GST_DEBUG_OBJECT (dvdspu, " Set Color & Contrast Change");
+ if (G_UNLIKELY (data + 3 >= end))
+ return; /* Invalid CHG_COLCON cmd at the end of the blk */
+
+ data++;
+ field_size = GST_READ_UINT16_BE (data);
+
+ if (G_UNLIKELY (data + field_size >= end))
+ return; /* Invalid CHG_COLCON cmd at the end of the blk */
+
+ gst_dvd_spu_parse_chg_colcon (dvdspu, data + 2, data + field_size);
+ state->vobsub.line_ctrl_i_pal_dirty = TRUE;
+ data += field_size;
+ break;
+ }
+ case SPU_CMD_END:
+ default:
+ GST_DEBUG_OBJECT (dvdspu, " END");
+ data = end;
+ break;
+ }
+ }
+}
+
+static void
+gst_dvd_spu_finish_spu_buf (GstDVDSpu * dvdspu)
+{
+ SpuState *state = &dvdspu->spu_state;
+
+ state->next_ts = state->vobsub.base_ts = GST_CLOCK_TIME_NONE;
+ gst_buffer_replace (&state->vobsub.buf, NULL);
+
+ GST_DEBUG_OBJECT (dvdspu, "Finished SPU buffer");
+}
+
+static gboolean
+gst_dvd_spu_setup_cmd_blk (GstDVDSpu * dvdspu, guint16 cmd_blk_offset,
+ guint8 * start, guint8 * end)
+{
+ SpuState *state = &dvdspu->spu_state;
+ guint16 delay;
+ guint8 *cmd_blk = start + cmd_blk_offset;
+
+ if (G_UNLIKELY (cmd_blk + 5 >= end))
+ return FALSE; /* No valid command block to read */
+
+ delay = GST_READ_UINT16_BE (cmd_blk);
+ state->next_ts = state->vobsub.base_ts + STM_TO_GST (delay);
+ state->vobsub.cur_cmd_blk = cmd_blk_offset;
+
+ GST_DEBUG_OBJECT (dvdspu, "Setup CMD Block @ %u with TS %" GST_TIME_FORMAT,
+ state->vobsub.cur_cmd_blk, GST_TIME_ARGS (state->next_ts));
+ return TRUE;
+}
+
+#if DUMP_DCSQ
+static void
+gst_dvd_spu_dump_dcsq (GstDVDSpu * dvdspu,
+ GstClockTime start_ts, GstBuffer * spu_buf)
+{
+ guint16 cmd_blk_offset;
+ guint16 next_blk;
+ guint8 *start, *end;
+
+ start = GST_BUFFER_DATA (spu_buf);
+ end = start + GST_BUFFER_SIZE (spu_buf);
+
+ g_return_if_fail (start != NULL);
+
+ /* First command */
+ next_blk = GST_READ_UINT16_BE (start + 2);
+ cmd_blk_offset = 0;
+
+ /* Loop through all commands */
+ g_print ("SPU begins @ %" GST_TIME_FORMAT " offset %u\n",
+ GST_TIME_ARGS (start_ts), next_blk);
+
+ while (cmd_blk_offset != next_blk) {
+ guint8 *data;
+ GstClockTime cmd_blk_ts;
+
+ cmd_blk_offset = next_blk;
+
+ if (G_UNLIKELY (start + cmd_blk_offset + 5 >= end))
+ break; /* No valid command to read */
+
+ data = start + cmd_blk_offset;
+
+ cmd_blk_ts = start_ts + STM_TO_GST (GST_READ_UINT16_BE (data));
+ next_blk = GST_READ_UINT16_BE (data + 2);
+
+ g_print ("Cmd Blk @ offset %u next %u ts %" GST_TIME_FORMAT "\n",
+ cmd_blk_offset, next_blk, GST_TIME_ARGS (cmd_blk_ts));
+
+ data += 4;
+ gst_dvd_spu_exec_cmd_blk (dvdspu, data, end);
+ }
+}
+#endif
+
+void
+gstspu_vobsub_handle_new_buf (GstDVDSpu * dvdspu, GstClockTime event_ts,
+ GstBuffer * buf)
+{
+ guint8 *start, *end;
+ SpuState *state = &dvdspu->spu_state;
+
+#if DUMP_DCSQ
+ gst_dvd_spu_dump_dcsq (dvdspu, event_ts, buf);
+#endif
+
+ if (G_UNLIKELY (GST_BUFFER_SIZE (buf) < 4))
+ goto invalid;
+
+ if (state->vobsub.buf != NULL) {
+ gst_buffer_unref (state->vobsub.buf);
+ state->vobsub.buf = NULL;
+ }
+ state->vobsub.buf = buf;
+ state->vobsub.base_ts = event_ts;
+
+ start = GST_BUFFER_DATA (state->vobsub.buf);
+ end = start + GST_BUFFER_SIZE (state->vobsub.buf);
+
+ /* Configure the first command block in this buffer as our initial blk */
+ state->vobsub.cur_cmd_blk = GST_READ_UINT16_BE (start + 2);
+ gst_dvd_spu_setup_cmd_blk (dvdspu, state->vobsub.cur_cmd_blk, start, end);
+ /* Clear existing chg-colcon info */
+ state->vobsub.n_line_ctrl_i = 0;
+ if (state->vobsub.line_ctrl_i != NULL) {
+ g_free (state->vobsub.line_ctrl_i);
+ state->vobsub.line_ctrl_i = NULL;
+ }
+ return;
+
+invalid:
+ /* Invalid buffer */
+ gst_dvd_spu_finish_spu_buf (dvdspu);
+}
+
+gboolean
+gstspu_vobsub_execute_event (GstDVDSpu * dvdspu)
+{
+ guint8 *start, *cmd_blk, *end;
+ guint16 next_blk;
+ SpuState *state = &dvdspu->spu_state;
+
+ if (state->vobsub.buf == NULL)
+ return FALSE;
+
+ GST_DEBUG_OBJECT (dvdspu, "Executing cmd blk with TS %" GST_TIME_FORMAT
+ " @ offset %u", GST_TIME_ARGS (state->next_ts),
+ state->vobsub.cur_cmd_blk);
+
+ start = GST_BUFFER_DATA (state->vobsub.buf);
+ end = start + GST_BUFFER_SIZE (state->vobsub.buf);
+
+ cmd_blk = start + state->vobsub.cur_cmd_blk;
+
+ if (G_UNLIKELY (cmd_blk + 5 >= end)) {
+ /* Invalid. Finish the buffer and loop again */
+ gst_dvd_spu_finish_spu_buf (dvdspu);
+ return FALSE;
+ }
+
+ gst_dvd_spu_exec_cmd_blk (dvdspu, cmd_blk + 4, end);
+
+ next_blk = GST_READ_UINT16_BE (cmd_blk + 2);
+ if (next_blk != state->vobsub.cur_cmd_blk) {
+ /* Advance to the next block of commands */
+ gst_dvd_spu_setup_cmd_blk (dvdspu, next_blk, start, end);
+ } else {
+ /* Next Block points to the current block, so we're finished with this
+ * SPU buffer */
+ gst_dvd_spu_finish_spu_buf (dvdspu);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+gboolean
+gstspu_vobsub_handle_dvd_event (GstDVDSpu * dvdspu, GstEvent * event)
+{
+ const gchar *event_type;
+ const GstStructure *structure = gst_event_get_structure (event);
+ SpuState *state = &dvdspu->spu_state;
+ gboolean hl_change = FALSE;
+
+ event_type = gst_structure_get_string (structure, "event");
+
+ if (strcmp (event_type, "dvd-spu-clut-change") == 0) {
+ gchar prop_name[32];
+ gint i;
+ gint entry;
+
+ for (i = 0; i < 16; i++) {
+ g_snprintf (prop_name, 32, "clut%02d", i);
+ if (!gst_structure_get_int (structure, prop_name, &entry))
+ entry = 0;
+ state->vobsub.current_clut[i] = (guint32) entry;
+ }
+
+ state->vobsub.main_pal_dirty = TRUE;
+ state->vobsub.hl_pal_dirty = TRUE;
+ state->vobsub.line_ctrl_i_pal_dirty = TRUE;
+ hl_change = TRUE;
+ } else if (strcmp (event_type, "dvd-spu-highlight") == 0) {
+ gint val;
+
+ if (gst_structure_get_int (structure, "palette", &val)) {
+ state->vobsub.hl_idx[3] = ((guint32) (val) >> 28) & 0x0f;
+ state->vobsub.hl_idx[2] = ((guint32) (val) >> 24) & 0x0f;
+ state->vobsub.hl_idx[1] = ((guint32) (val) >> 20) & 0x0f;
+ state->vobsub.hl_idx[0] = ((guint32) (val) >> 16) & 0x0f;
+
+ state->vobsub.hl_alpha[3] = ((guint32) (val) >> 12) & 0x0f;
+ state->vobsub.hl_alpha[2] = ((guint32) (val) >> 8) & 0x0f;
+ state->vobsub.hl_alpha[1] = ((guint32) (val) >> 4) & 0x0f;
+ state->vobsub.hl_alpha[0] = ((guint32) (val) >> 0) & 0x0f;
+
+ state->vobsub.hl_pal_dirty = TRUE;
+ }
+ if (gst_structure_get_int (structure, "sx", &val))
+ state->vobsub.hl_rect.left = (gint16) val;
+ if (gst_structure_get_int (structure, "sy", &val))
+ state->vobsub.hl_rect.top = (gint16) val;
+ if (gst_structure_get_int (structure, "ex", &val))
+ state->vobsub.hl_rect.right = (gint16) val;
+ if (gst_structure_get_int (structure, "ey", &val))
+ state->vobsub.hl_rect.bottom = (gint16) val;
+
+ GST_INFO_OBJECT (dvdspu, "Highlight rect is now (%d,%d) to (%d,%d)",
+ state->vobsub.hl_rect.left, state->vobsub.hl_rect.top,
+ state->vobsub.hl_rect.right, state->vobsub.hl_rect.bottom);
+ hl_change = TRUE;
+ } else if (strcmp (event_type, "dvd-spu-reset-highlight") == 0) {
+ if (state->vobsub.hl_rect.top != -1 || state->vobsub.hl_rect.bottom != -1)
+ hl_change = TRUE;
+ state->vobsub.hl_rect.top = -1;
+ state->vobsub.hl_rect.bottom = -1;
+ GST_INFO_OBJECT (dvdspu, "Highlight off");
+ } else if (strcmp (event_type, "dvd-set-subpicture-track") == 0) {
+ gboolean forced_only;
+
+ if (gst_structure_get_boolean (structure, "forced-only", &forced_only)) {
+ gboolean was_forced = (state->flags & SPU_STATE_FORCED_ONLY);
+
+ if (forced_only)
+ state->flags |= SPU_STATE_FORCED_ONLY;
+ else
+ state->flags &= ~(SPU_STATE_FORCED_ONLY);
+
+ if (was_forced != forced_only)
+ hl_change = TRUE;
+ }
+ }
+
+ gst_event_unref (event);
+
+ return hl_change;
+}
+
+void
+gstspu_vobsub_flush (GstDVDSpu * dvdspu)
+{
+ SpuState *state = &dvdspu->spu_state;
+
+ if (state->vobsub.buf) {
+ gst_buffer_unref (state->vobsub.buf);
+ state->vobsub.buf = NULL;
+ }
+ if (state->vobsub.pix_buf) {
+ gst_buffer_unref (state->vobsub.pix_buf);
+ state->vobsub.pix_buf = NULL;
+ }
+
+ state->vobsub.base_ts = GST_CLOCK_TIME_NONE;
+ state->vobsub.pix_data[0] = 0;
+ state->vobsub.pix_data[1] = 0;
+
+ state->vobsub.hl_rect.top = -1;
+ state->vobsub.hl_rect.bottom = -1;
+
+ state->vobsub.disp_rect.top = -1;
+ state->vobsub.disp_rect.bottom = -1;
+
+ state->vobsub.n_line_ctrl_i = 0;
+ if (state->vobsub.line_ctrl_i != NULL) {
+ g_free (state->vobsub.line_ctrl_i);
+ state->vobsub.line_ctrl_i = NULL;
+ }
+}
diff --git a/gst/dvdspu/gstspu-vobsub.h b/gst/dvdspu/gstspu-vobsub.h
new file mode 100644
index 00000000..9b4196bc
--- /dev/null
+++ b/gst/dvdspu/gstspu-vobsub.h
@@ -0,0 +1,110 @@
+/* GStreamer Sub-Picture Unit - VobSub/DVD handling
+ * Copyright (C) 2009 Jan Schmidt <thaytan@noraisin.net>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __GSTSPU_VOBSUB_H__
+#define __GSTSPU_VOBSUB_H__
+
+#include "gstspu-common.h"
+
+typedef struct SpuVobsubState SpuVobsubState;
+typedef struct SpuVobsubPixCtrlI SpuVobsubPixCtrlI;
+typedef struct SpuVobsubLineCtrlI SpuVobsubLineCtrlI;
+
+/* Pixel Control Info from a Change Color Contrast command */
+struct SpuVobsubPixCtrlI {
+ gint16 left;
+ guint32 palette;
+
+ /* Pre-multiplied palette values, updated as
+ * needed */
+ SpuColour pal_cache[4];
+};
+
+struct SpuVobsubLineCtrlI {
+ guint8 n_changes; /* 1 to 8 */
+ SpuVobsubPixCtrlI pix_ctrl_i[8];
+
+ gint16 top;
+ gint16 bottom;
+};
+
+struct SpuVobsubState {
+ GstClockTime base_ts; /* base TS for cmd blk delays in running time */
+ GstBuffer *buf; /* Current SPU packet we're executing commands from */
+ guint16 cur_cmd_blk; /* Offset into the buf for the current cmd block */
+
+ /* Top + Bottom field offsets in the buffer. 0 = not set */
+ guint16 pix_data[2];
+ GstBuffer *pix_buf; /* Current SPU packet the pix_data references */
+
+ SpuRect disp_rect;
+ SpuRect clip_rect;
+ SpuRect hl_rect;
+
+ guint32 current_clut[16]; /* Colour lookup table from incoming events */
+
+ guint8 main_idx[4]; /* Indices for current main palette */
+ guint8 main_alpha[4]; /* Alpha values for main palette */
+
+ guint8 hl_idx[4]; /* Indices for current highlight palette */
+ guint8 hl_alpha[4]; /* Alpha values for highlight palette */
+
+ /* Pre-multiplied colour palette for the main palette */
+ SpuColour main_pal[4];
+ gboolean main_pal_dirty;
+
+ /* Line control info for rendering the highlight palette */
+ SpuVobsubLineCtrlI hl_ctrl_i;
+ gboolean hl_pal_dirty; /* Indicates that the HL palette info needs refreshing */
+
+ /* LineCtrlI Info from a Change Color & Contrast command */
+ SpuVobsubLineCtrlI *line_ctrl_i;
+ gint16 n_line_ctrl_i;
+ gboolean line_ctrl_i_pal_dirty; /* Indicates that the palettes for the line_ctrl_i
+ * need recalculating */
+
+ /* Rendering state vars below */
+ gint16 comp_last_x[2]; /* Maximum X values we rendered into the comp buffer (odd & even) */
+ gint16 *comp_last_x_ptr; /* Ptr to the current comp_last_x value to be updated by the render */
+
+ /* Current Y Position */
+ gint16 cur_Y;
+
+ /* Current offset in nibbles into the pix_data */
+ guint16 cur_offsets[2];
+ guint16 max_offset;
+
+ /* current ChgColCon Line Info */
+ SpuVobsubLineCtrlI *cur_chg_col;
+ SpuVobsubLineCtrlI *cur_chg_col_end;
+
+ /* Output position tracking */
+ guint8 *out_Y;
+ guint32 *out_U;
+ guint32 *out_V;
+ guint32 *out_A;
+};
+
+void gstspu_vobsub_handle_new_buf (GstDVDSpu * dvdspu, GstClockTime event_ts, GstBuffer *buf);
+gboolean gstspu_vobsub_execute_event (GstDVDSpu *dvdspu);
+void gstspu_vobsub_render (GstDVDSpu *dvdspu, GstBuffer *buf);
+gboolean gstspu_vobsub_handle_dvd_event (GstDVDSpu *dvdspu, GstEvent *event);
+void gstspu_vobsub_flush (GstDVDSpu *dvdspu);
+
+#endif
diff --git a/gst/mpegdemux/gstmpegdefs.h b/gst/mpegdemux/gstmpegdefs.h
index d63667d8..7ad1e25c 100644
--- a/gst/mpegdemux/gstmpegdefs.h
+++ b/gst/mpegdemux/gstmpegdefs.h
@@ -173,6 +173,8 @@
#define ST_HDV_PRIVATE_A0 0xa0
#define ST_HDV_PRIVATE_A1 0xa1
#define ST_PS_DVD_SUBPICTURE 0xff
+/* Blu-ray PGS subpictures */
+#define ST_BD_PGS_SUBPICTURE 0x90
/* Un-official time-code stream */
#define ST_PS_TIMECODE 0xd2
diff --git a/gst/mpegdemux/gstmpegdemux.c b/gst/mpegdemux/gstmpegdemux.c
index 439bb563..ce1d0978 100644
--- a/gst/mpegdemux/gstmpegdemux.c
+++ b/gst/mpegdemux/gstmpegdemux.c
@@ -39,6 +39,7 @@
* Fluendo, S.L. All Rights Reserved.
*
* Contributor(s): Wim Taymans <wim@fluendo.com>
+ * Jan Schmidt <thaytan@noraisin.net>
*/
#ifdef HAVE_CONFIG_H
@@ -56,6 +57,9 @@
#define SCAN_SCR_SZ 12
#define SCAN_PTS_SZ 80
+#define SEGMENT_THRESHOLD (300*GST_MSECOND)
+#define VIDEO_SEGMENT_THRESHOLD (500*GST_MSECOND)
+
typedef enum
{
SCAN_SCR,
@@ -177,6 +181,13 @@ static GstStaticPadTemplate audio_template =
"audio/x-private1-ac3;" "audio/x-private1-dts;" "audio/ac3")
);
+static GstStaticPadTemplate subpicture_template =
+GST_STATIC_PAD_TEMPLATE ("subpicture_%02x",
+ GST_PAD_SRC,
+ GST_PAD_SOMETIMES,
+ GST_STATIC_CAPS ("video/x-dvd-subpicture")
+ );
+
static GstStaticPadTemplate private_template =
GST_STATIC_PAD_TEMPLATE ("private_%d",
GST_PAD_SRC,
@@ -209,6 +220,10 @@ static inline gboolean gst_flups_demux_scan_forward_ts (GstFluPSDemux * demux,
static inline gboolean gst_flups_demux_scan_backward_ts (GstFluPSDemux * demux,
guint64 * pos, SCAN_MODE mode, guint64 * rts);
+static void gst_flups_demux_send_segment_updates (GstFluPSDemux * demux,
+ GstClockTime new_time);
+static void gst_flups_demux_clear_times (GstFluPSDemux * demux);
+
static GstElementClass *parent_class = NULL;
/*static guint gst_flups_demux_signals[LAST_SIGNAL] = { 0 };*/
@@ -250,10 +265,14 @@ gst_flups_demux_base_init (GstFluPSDemuxClass * klass)
klass->sink_template = gst_static_pad_template_get (&sink_template);
klass->video_template = gst_static_pad_template_get (&video_template);
klass->audio_template = gst_static_pad_template_get (&audio_template);
+ klass->subpicture_template =
+ gst_static_pad_template_get (&subpicture_template);
klass->private_template = gst_static_pad_template_get (&private_template);
gst_element_class_add_pad_template (element_class, klass->video_template);
gst_element_class_add_pad_template (element_class, klass->audio_template);
+ gst_element_class_add_pad_template (element_class,
+ klass->subpicture_template);
gst_element_class_add_pad_template (element_class, klass->private_template);
gst_element_class_add_pad_template (element_class, klass->sink_template);
@@ -340,6 +359,7 @@ gst_flups_demux_create_stream (GstFluPSDemux * demux, gint id, gint stream_type)
gchar *name;
GstFluPSDemuxClass *klass = GST_FLUPS_DEMUX_GET_CLASS (demux);
GstCaps *caps;
+ GstClockTime threshold = SEGMENT_THRESHOLD;
name = NULL;
template = NULL;
@@ -368,6 +388,7 @@ gst_flups_demux_create_stream (GstFluPSDemux * demux, gint id, gint stream_type)
caps = gst_caps_new_simple ("video/mpeg",
"mpegversion", G_TYPE_INT, mpeg_version,
"systemstream", G_TYPE_BOOLEAN, FALSE, NULL);
+ threshold = VIDEO_SEGMENT_THRESHOLD;
break;
}
case ST_AUDIO_MPEG1:
@@ -387,6 +408,7 @@ gst_flups_demux_create_stream (GstFluPSDemux * demux, gint id, gint stream_type)
template = klass->video_template;
name = g_strdup_printf ("video_%02x", id);
caps = gst_caps_new_simple ("video/x-h264", NULL);
+ threshold = VIDEO_SEGMENT_THRESHOLD;
break;
case ST_PS_AUDIO_AC3:
template = klass->audio_template;
@@ -404,6 +426,9 @@ gst_flups_demux_create_stream (GstFluPSDemux * demux, gint id, gint stream_type)
caps = gst_caps_new_simple ("audio/x-private1-lpcm", NULL);
break;
case ST_PS_DVD_SUBPICTURE:
+ template = klass->subpicture_template;
+ name = g_strdup_printf ("subpicture_%02x", id);
+ caps = gst_caps_new_simple ("video/x-dvd-subpicture", NULL);
break;
case ST_GST_AUDIO_RAWA52:
template = klass->audio_template;
@@ -424,6 +449,7 @@ gst_flups_demux_create_stream (GstFluPSDemux * demux, gint id, gint stream_type)
stream->notlinked = FALSE;
stream->type = stream_type;
stream->pad = gst_pad_new_from_template (template, name);
+ stream->segment_thresh = threshold;
gst_pad_set_event_function (stream->pad,
GST_DEBUG_FUNCPTR (gst_flups_demux_src_event));
gst_pad_set_query_function (stream->pad,
@@ -559,6 +585,20 @@ gst_flups_demux_send_data (GstFluPSDemux * demux, GstFluPSStream * stream,
GST_TIME_ARGS (demux->src_segment.last_stop),
GST_TIME_ARGS (MPEGTIME_TO_GSTTIME (demux->current_scr)));
+ if (demux->src_segment.last_stop != GST_CLOCK_TIME_NONE) {
+ GstClockTime new_time = demux->base_time + demux->src_segment.last_stop;
+
+ if (stream->last_ts == GST_CLOCK_TIME_NONE || stream->last_ts < new_time) {
+#if 0
+ g_print ("last_ts update on pad %s to time %" GST_TIME_FORMAT "\n",
+ GST_PAD_NAME (stream->pad), GST_TIME_ARGS (cur_scr_time));
+#endif
+ stream->last_ts = new_time;
+ }
+
+ gst_flups_demux_send_segment_updates (demux, new_time);
+ }
+
/* Set the buffer discont flag, and clear discont state on the stream */
if (stream->discont) {
GST_DEBUG_OBJECT (demux, "marking discont buffer");
@@ -731,12 +771,76 @@ gst_flups_demux_flush (GstFluPSDemux * demux)
gst_adapter_clear (demux->adapter);
gst_adapter_clear (demux->rev_adapter);
gst_pes_filter_drain (&demux->filter);
+ gst_flups_demux_clear_times (demux);
demux->adapter_offset = G_MAXUINT64;
demux->current_scr = G_MAXUINT64;
demux->bytes_since_scr = 0;
}
static void
+gst_flups_demux_clear_times (GstFluPSDemux * demux)
+{
+ gint id;
+
+ /* Clear the last ts for all streams */
+ for (id = 0; id < GST_FLUPS_DEMUX_MAX_STREAMS; id++) {
+ GstFluPSStream *stream = demux->streams[id];
+
+ if (stream) {
+ stream->last_seg_start = stream->last_ts = GST_CLOCK_TIME_NONE;
+ }
+ }
+}
+
+static void
+gst_flups_demux_send_segment_updates (GstFluPSDemux * demux,
+ GstClockTime new_time)
+{
+ /* Advance all lagging streams by sending a segment update */
+ gint id;
+ GstEvent *event = NULL;
+
+ /* FIXME: Handle reverse playback */
+
+ if (new_time > demux->src_segment.stop)
+ return;
+
+ for (id = 0; id < GST_FLUPS_DEMUX_MAX_STREAMS; id++) {
+ GstFluPSStream *stream = demux->streams[id];
+
+ if (stream) {
+ if (stream->last_ts == GST_CLOCK_TIME_NONE ||
+ stream->last_ts < demux->src_segment.start)
+ stream->last_ts = demux->src_segment.start;
+ if (stream->last_ts + stream->segment_thresh < new_time) {
+#if 0
+ g_print ("Segment update to pad %s time %" GST_TIME_FORMAT " stop now %"
+ GST_TIME_FORMAT "\n", GST_PAD_NAME (stream->pad),
+ GST_TIME_ARGS (new_time), GST_TIME_ARGS (demux->src_segment.stop));
+#endif
+ GST_DEBUG_OBJECT (demux,
+ "Segment update to pad %s time %" GST_TIME_FORMAT,
+ GST_PAD_NAME (stream->pad), GST_TIME_ARGS (new_time));
+ if (event == NULL) {
+ event = gst_event_new_new_segment_full (TRUE,
+ demux->src_segment.rate, demux->src_segment.applied_rate,
+ GST_FORMAT_TIME, new_time,
+ demux->src_segment.stop,
+ demux->src_segment.time + (new_time - demux->src_segment.start));
+ }
+ gst_event_ref (event);
+ gst_pad_push_event (stream->pad, event);
+ stream->last_seg_start = stream->last_ts = new_time;
+ stream->need_segment = FALSE;
+ }
+ }
+ }
+
+ if (event)
+ gst_event_unref (event);
+}
+
+static void
gst_flups_demux_close_segment (GstFluPSDemux * demux)
{
gint id;
@@ -747,6 +851,10 @@ gst_flups_demux_close_segment (GstFluPSDemux * demux)
&demux->src_segment);
#endif
+ /* FIXME: Need to send a different segment-close to each pad where the
+ * last_seg_start != clock_time_none, as that indicates a sparse-stream
+ * event was sent there */
+
/* Close the current segment for a linear playback */
if (demux->src_segment.rate >= 0) {
/* for forward playback, we played from start to last_stop */
@@ -1306,7 +1414,7 @@ gst_flups_demux_reset_psm (GstFluPSDemux * demux)
FILL_TYPE (0x40, 0x7f, -1);
FILL_TYPE (0x80, 0x87, ST_PS_AUDIO_AC3);
FILL_TYPE (0x88, 0x9f, ST_PS_AUDIO_DTS);
- FILL_TYPE (0xa0, 0xbf, ST_PS_AUDIO_LPCM);
+ FILL_TYPE (0xa0, 0xaf, ST_PS_AUDIO_LPCM);
FILL_TYPE (0xbd, 0xbd, -1);
FILL_TYPE (0xc0, 0xdf, ST_AUDIO_MPEG1);
FILL_TYPE (0xe0, 0xef, ST_GST_VIDEO_MPEG1_OR_2);
@@ -1839,18 +1947,26 @@ gst_flups_demux_data_cb (GstPESFilter * filter, gboolean first,
}
if (G_LIKELY (stream_type == -1)) {
- /* new id */
+ /* new id is in the first byte */
id = data[offset++];
- /* Number of audio frames in this packet */
- nframes = data[offset++];
-
- GST_DEBUG_OBJECT (demux, "private type 0x%02x, %d frames", id,
- nframes);
-
- datalen -= 2;
+ datalen--;
/* and remap */
stream_type = demux->psm[id];
+
+ /* Now, if it's a subpicture stream - no more, otherwise
+ * take the first byte too, since it's the frame count in audio
+ * streams and our backwards compat convention is to strip it off */
+ if (stream_type != ST_PS_DVD_SUBPICTURE) {
+ /* Number of audio frames in this packet */
+ nframes = data[offset++];
+ datalen--;
+ GST_DEBUG_OBJECT (demux, "private type 0x%02x, %d frames", id,
+ nframes);
+ } else {
+ GST_DEBUG_OBJECT (demux, "private type 0x%02x, stream type %d", id,
+ stream_type);
+ }
}
}
if (stream_type == -1)
diff --git a/gst/mpegdemux/gstmpegdemux.h b/gst/mpegdemux/gstmpegdemux.h
index ef175f76..29b3d5e5 100644
--- a/gst/mpegdemux/gstmpegdemux.h
+++ b/gst/mpegdemux/gstmpegdemux.h
@@ -86,6 +86,10 @@ struct _GstFluPSStream
gint type;
gint size_bound;
+ GstClockTime segment_thresh;
+ GstClockTime last_seg_start;
+ GstClockTime last_ts;
+
gboolean discont;
gboolean notlinked;
gboolean need_segment;
@@ -149,6 +153,7 @@ struct _GstFluPSDemuxClass
GstPadTemplate *sink_template;
GstPadTemplate *video_template;
GstPadTemplate *audio_template;
+ GstPadTemplate *subpicture_template;
GstPadTemplate *private_template;
};
diff --git a/gst/mpegdemux/gstmpegtsdemux.c b/gst/mpegdemux/gstmpegtsdemux.c
index cd64a295..ef0de2c8 100644
--- a/gst/mpegdemux/gstmpegtsdemux.c
+++ b/gst/mpegdemux/gstmpegtsdemux.c
@@ -157,6 +157,10 @@ enum
"audio/x-dts" \
)
+/* Can also use the subpicture pads for text subtitles? */
+#define SUBPICTURE_CAPS \
+ GST_STATIC_CAPS ("subpicture/x-pgs; video/x-dvd-subpicture")
+
static GstStaticPadTemplate sink_template = GST_STATIC_PAD_TEMPLATE ("sink",
GST_PAD_SINK,
GST_PAD_ALWAYS,
@@ -175,6 +179,12 @@ GST_STATIC_PAD_TEMPLATE ("audio_%04x",
GST_PAD_SOMETIMES,
AUDIO_CAPS);
+static GstStaticPadTemplate subpicture_template =
+GST_STATIC_PAD_TEMPLATE ("subpicture_%04x",
+ GST_PAD_SRC,
+ GST_PAD_SOMETIMES,
+ SUBPICTURE_CAPS);
+
static GstStaticPadTemplate private_template =
GST_STATIC_PAD_TEMPLATE ("private_%04x",
GST_PAD_SRC,
@@ -250,10 +260,14 @@ gst_mpegts_demux_base_init (GstMpegTSDemuxClass * klass)
klass->sink_template = gst_static_pad_template_get (&sink_template);
klass->video_template = gst_static_pad_template_get (&video_template);
klass->audio_template = gst_static_pad_template_get (&audio_template);
+ klass->subpicture_template =
+ gst_static_pad_template_get (&subpicture_template);
klass->private_template = gst_static_pad_template_get (&private_template);
gst_element_class_add_pad_template (element_class, klass->video_template);
gst_element_class_add_pad_template (element_class, klass->audio_template);
+ gst_element_class_add_pad_template (element_class,
+ klass->subpicture_template);
gst_element_class_add_pad_template (element_class, klass->private_template);
gst_element_class_add_pad_template (element_class, klass->sink_template);
@@ -675,6 +689,14 @@ gst_mpegts_demux_fill_stream (GstMpegTSStream * stream, guint8 id,
caps = gst_caps_new_simple ("audio/x-lpcm", NULL);
break;
case ST_PS_DVD_SUBPICTURE:
+ template = klass->subpicture_template;
+ name = g_strdup_printf ("subpicture_%04x", stream->PID);
+ caps = gst_caps_new_simple ("video/x-dvd-subpicture", NULL);
+ break;
+ case ST_BD_PGS_SUBPICTURE:
+ template = klass->subpicture_template;
+ name = g_strdup_printf ("subpicture_%04x", stream->PID);
+ caps = gst_caps_new_simple ("subpicture/x-pgs", NULL);
break;
default:
break;
@@ -992,8 +1014,8 @@ gst_mpegts_demux_data_cb (GstPESFilter * filter, gboolean first,
goto unknown_type;
GST_DEBUG_OBJECT (demux,
- "New stream 0x%04x of type %d with caps %" GST_PTR_FORMAT, stream->PID,
- stream->stream_type, GST_PAD_CAPS (stream->pad));
+ "New stream 0x%04x of type 0x%02x with caps %" GST_PTR_FORMAT,
+ stream->PID, stream->stream_type, GST_PAD_CAPS (stream->pad));
srcpad = stream->pad;
@@ -1017,7 +1039,7 @@ gst_mpegts_demux_data_cb (GstPESFilter * filter, gboolean first,
unknown_type:
{
GST_DEBUG_OBJECT (demux, "got unknown stream id 0x%02x, type 0x%02x",
- filter->id, filter->type);
+ filter->id, stream->stream_type);
gst_buffer_unref (buffer);
return gst_mpegts_demux_combine_flows (demux, stream, GST_FLOW_NOT_LINKED);
}
diff --git a/gst/mpegdemux/gstmpegtsdemux.h b/gst/mpegdemux/gstmpegtsdemux.h
index dad2b023..fde68926 100644
--- a/gst/mpegdemux/gstmpegtsdemux.h
+++ b/gst/mpegdemux/gstmpegtsdemux.h
@@ -228,6 +228,7 @@ struct _GstMpegTSDemuxClass {
GstPadTemplate * sink_template;
GstPadTemplate * video_template;
GstPadTemplate * audio_template;
+ GstPadTemplate * subpicture_template;
GstPadTemplate * private_template;
};
diff --git a/gst/mpegdemux/gstpesfilter.h b/gst/mpegdemux/gstpesfilter.h
index b35d8746..ccc8461d 100644
--- a/gst/mpegdemux/gstpesfilter.h
+++ b/gst/mpegdemux/gstpesfilter.h
@@ -84,8 +84,6 @@ struct _GstPESFilter {
gboolean unbounded_packet;
guint16 length;
- guint8 type;
-
gint64 pts;
gint64 dts;
};
diff --git a/gst/rtpmanager/rtpsource.c b/gst/rtpmanager/rtpsource.c
index 6a3dc604..355526ee 100644
--- a/gst/rtpmanager/rtpsource.c
+++ b/gst/rtpmanager/rtpsource.c
@@ -201,7 +201,8 @@ make_address_string (GstNetAddress * addr, gchar * dest, gulong n)
gst_netaddress_get_ip4_address (addr, &address, &port);
g_snprintf (dest, n, "%d.%d.%d.%d:%d", (address >> 24) & 0xff,
- (address >> 16) & 0xff, (address >> 8) & 0xff, address & 0xff, port);
+ (address >> 16) & 0xff, (address >> 8) & 0xff, address & 0xff,
+ g_ntohs (port));
break;
}
case GST_NET_TYPE_IP6:
@@ -216,7 +217,7 @@ make_address_string (GstNetAddress * addr, gchar * dest, gulong n)
(address[4] << 8) | address[5], (address[6] << 8) | address[7],
(address[8] << 8) | address[9], (address[10] << 8) | address[11],
(address[12] << 8) | address[13], (address[14] << 8) | address[15],
- port);
+ g_ntohs (port));
break;
}
default:
diff --git a/tests/check/Makefile.am b/tests/check/Makefile.am
index bf216c12..393e032a 100644
--- a/tests/check/Makefile.am
+++ b/tests/check/Makefile.am
@@ -13,7 +13,7 @@ TESTS_ENVIRONMENT = \
$(REGISTRY_ENVIRONMENT) \
GST_PLUGIN_SYSTEM_PATH= \
GST_PLUGIN_PATH=$(top_builddir)/gst:$(top_builddir)/sys:$(top_builddir)/ext:$(top_builddir)/../gst-ffmpeg/ext/ffmpeg:$(top_builddir)/../gst-plugins-good/gst:$(top_builddir)/../gst-plugins-good/sys:$(top_builddir)/../gst-plugins-good/ext:$(top_builddir)/../gst-plugins-ugly/gst:$(top_builddir)/../gst-plugins-ugly/sys:$(top_builddir)/../gst-plugins-ugly/ext:$(GSTPB_PLUGINS_DIR):$(GST_PLUGINS_DIR) \
- STATE_IGNORE_ELEMENTS="alsaspdifsink apexsink cdaudio dc1394src dccpclientsrc dccpclientsink dccpserversrc dccpserversink dvbsrc dvbbasebin dfbvideosink festival nassink rsndvdbin sdlaudiosink sdlvideosink vcdsrc rfbsrc"
+ STATE_IGNORE_ELEMENTS="alsaspdifsink apexsink camerabin cdaudio dc1394src dccpclientsrc dccpclientsink dccpserversrc dccpserversink dvbsrc dvbbasebin dfbvideosink festival nassink rsndvdbin sdlaudiosink sdlvideosink vcdsrc rfbsrc"
plugindir = $(libdir)/gstreamer-@GST_MAJORMINOR@