diff --git a/gst/dvdspu/Makefile.am b/gst/dvdspu/Makefile.am index 20cfe03c3a..07a6635767 100644 --- a/gst/dvdspu/Makefile.am +++ b/gst/dvdspu/Makefile.am @@ -1,7 +1,7 @@ plugin_LTLIBRARIES = libgstdvdspu.la -libgstdvdspu_la_SOURCES = gstdvdspu.c gstdvdspu-render.c gstspu-vobsub.c gstspu-pgs.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) diff --git a/gst/dvdspu/gstdvdspu-render.c b/gst/dvdspu/gstdvdspu-render.c index a8dadee2b8..7731aed41f 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. + * Copyright (C) 2009 Jan Schmidt * * 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) +void +gstspu_clear_comp_buffers (SpuState * state) { - 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) -{ - /* 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)); + out_V[x] = (guint8) (tmp / (4 * 0xff)); } } - -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); - } - - /* 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 c9c116e858..f5fce4bc94 100644 --- a/gst/dvdspu/gstdvdspu.c +++ b/gst/dvdspu/gstdvdspu.c @@ -39,10 +39,6 @@ #include #include "gstdvdspu.h" -#include "gstspu-vobsub.h" -#include "gstspu-pgs.h" - -extern void gst_dvd_spu_render_spu (GstDVDSpu * dvdspu, GstBuffer * buf); GST_DEBUG_CATEGORY (dvdspu_debug); #define GST_CAT_DEFAULT dvdspu_debug @@ -111,6 +107,7 @@ static void gst_dvd_spu_clear (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); @@ -119,11 +116,11 @@ 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 Schmidt "); GstElementClass *element_class = GST_ELEMENT_CLASS (gclass); gst_element_class_add_pad_template (element_class, @@ -191,7 +188,8 @@ gst_dvd_spu_clear (GstDVDSpu * dvdspu) { gst_dvd_spu_flush_spu_info (dvdspu, FALSE); gst_segment_init (&dvdspu->subp_seg, GST_FORMAT_UNDEFINED); - memcpy (dvdspu->spu_state.current_clut, default_clut, sizeof (guint32) * 16); + + dvdspu->spu_input_type = SPU_INPUT_TYPE_NONE; gst_buffer_replace (&dvdspu->ref_frame, NULL); gst_buffer_replace (&dvdspu->pending_frame, NULL); @@ -235,10 +233,11 @@ 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, gboolean process_events) +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"); @@ -252,40 +251,34 @@ gst_dvd_spu_flush_spu_info (GstDVDSpu * dvdspu, gboolean process_events) if (packet->buf) { gst_buffer_unref (packet->buf); g_assert (packet->event == NULL); + g_free (packet); } else if (packet->event) { - if (process_events) - gst_dvd_spu_handle_dvd_event (dvdspu, packet->event); - else + if (keep_events) { + g_queue_push_tail (&tmp_q, packet); + } else { gst_event_unref (packet->event); + g_free (packet); + } } - 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->next_ts = GST_CLOCK_TIME_NONE; - state->hl_rect.top = -1; - state->hl_rect.bottom = -1; - - 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; } } @@ -357,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); @@ -629,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 @@ -658,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) @@ -681,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) { @@ -707,87 +716,44 @@ gst_dvd_spu_redraw_still (GstDVDSpu * dvdspu, gboolean force) 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); +static gboolean +gstspu_execute_event (GstDVDSpu * dvdspu) +{ + 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; } /* Advance the SPU packet/command queue to a time. new_ts is in running time */ @@ -796,8 +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) { - if (state->buf == NULL) { + 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 (!gstspu_execute_event (dvdspu)) { GstClockTime vid_run_ts; /* No current command buffer, try and get one */ @@ -820,11 +793,11 @@ gst_dvd_spu_advance_spu (GstDVDSpu * dvdspu, GstClockTime new_ts) if (packet->buf) { switch (dvdspu->spu_input_type) { case SPU_INPUT_TYPE_VOBSUB: - gst_dvd_spu_handle_new_vobsub_buf (dvdspu, packet); + gstspu_vobsub_handle_new_buf (dvdspu, packet->event_ts, + packet->buf); break; case SPU_INPUT_TYPE_PGS: - gstspu_dump_pgs_buffer (packet->buf); - gst_buffer_unref (packet->buf); + gstspu_pgs_handle_new_buf (dvdspu, packet->event_ts, packet->buf); break; default: g_assert_not_reached (); @@ -837,16 +810,6 @@ gst_dvd_spu_advance_spu (GstDVDSpu * dvdspu, GstClockTime new_ts) 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_dvdspu_vobsub_execute_event (dvdspu); } } @@ -1005,6 +968,7 @@ gst_dvd_spu_subpic_chain (GstPad * pad, GstBuffer * buf) /* 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; @@ -1014,7 +978,8 @@ gst_dvd_spu_subpic_chain (GstPad * pad, GstBuffer * buf) if (data + packet_size > end) break; data += packet_size; - if (packet_type == PGS_COMMAND_END_DISPLAY && data != end) { + /* 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; @@ -1023,7 +988,8 @@ gst_dvd_spu_subpic_chain (GstPad * pad, GstBuffer * buf) } if (dvdspu->partial_spu && data == end) { - g_print ("Complete packet of size %u\n", + 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; @@ -1071,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 { @@ -1146,6 +1113,7 @@ 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_segment_init (&dvdspu->subp_seg, GST_FORMAT_UNDEFINED); gst_dvd_spu_flush_spu_info (dvdspu, TRUE); @@ -1193,8 +1161,8 @@ gst_dvd_spu_subpic_set_caps (GstPad * pad, GstCaps * caps) if (dvdspu->spu_input_type != input_type) { GST_INFO_OBJECT (dvdspu, "Incoming SPU packet type changed to %u", input_type); - gst_dvd_spu_flush_spu_info (dvdspu, TRUE); dvdspu->spu_input_type = input_type; + gst_dvd_spu_flush_spu_info (dvdspu, TRUE); } DVD_SPU_UNLOCK (dvdspu); @@ -1228,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 1bbc7d3af0..22b48d1da5 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 +#include "gstspu-common.h" +#include "gstspu-vobsub.h" +#include "gstspu-pgs.h" + G_BEGIN_DECLS #define GST_TYPE_DVD_SPU \ @@ -37,64 +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 enum SpuInputType SpuInputType; -typedef struct SpuState SpuState; 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 { @@ -107,79 +63,23 @@ enum SpuStateFlags { SPU_STATE_FORCED_ONLY = 0x100 }; -enum SpuInputType { - SPU_INPUT_TYPE_NONE = 0x00, - SPU_INPUT_TYPE_VOBSUB = 0x01, - SPU_INPUT_TYPE_PGS = 0x02 -}; - #define SPU_STATE_FLAGS_MASK (0xff) 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; + guint32 *comp_bufs[3]; /* Compositing buffers for U+V & A */ + guint16 comp_left; + guint16 comp_right; - /* 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; - - /* 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 @@ -229,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 0000000000..206e882037 --- /dev/null +++ b/gst/dvdspu/gstspu-common.h @@ -0,0 +1,56 @@ +/* GStreamer DVD Sub-Picture Unit + * Copyright (C) 2007 Fluendo S.A. + * + * 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 + +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 index b4bbdc339e..a79e694e2c 100644 --- a/gst/dvdspu/gstspu-pgs.c +++ b/gst/dvdspu/gstspu-pgs.c @@ -23,6 +23,7 @@ #include #include +#include "gstdvdspu.h" #include "gstspu-pgs.h" const struct PgsFrameRateEntry @@ -35,12 +36,32 @@ const struct PgsFrameRateEntry 64, 30000, 1001} /* 29.97 FPS */ }; -gboolean in_presentation_segment = FALSE; -guint8 *rle_data = NULL; -guint32 rle_data_size = 0, rle_data_used = 0; -PgsPaletteEntry palette[256]; +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) @@ -49,19 +70,20 @@ dump_bytes (guint8 * data, guint16 len) /* Dump the numbers */ for (i = 0; i < len; i++) { - g_print ("0x%02x ", data[i]); + PGS_DUMP ("0x%02x ", data[i]); if (!((i + 1) % 16)) - g_print ("\n"); + PGS_DUMP ("\n"); } if (len > 0 && (i % 16)) - g_print ("\n"); + PGS_DUMP ("\n"); } static void -dump_rle_data (guint8 * data, guint32 len) +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; @@ -70,42 +92,36 @@ dump_rle_data (guint8 * data, guint32 len) obj_w = GST_READ_UINT16_BE (data); obj_h = GST_READ_UINT16_BE (data + 2); data += 4; - g_print ("RLE image is %ux%u\n", obj_w, obj_h); + PGS_DUMP ("RLE image is %ux%u\n", obj_w, obj_h); while (data < end) { guint8 pal_id; guint16 run_len; - if (data[0] != 0) { - // g_print ("data 0x%02x\n", data[0]); - pal_id = *data++; + pal_id = *data++; + if (pal_id != 0) { + // PGS_DUMP ("data 0x%02x\n", data[0]); run_len = 1; } else { - data++; - if (data + 1 > end) return; switch (data[0] & 0xC0) { case 0x00: - //g_print ("data 0x%02x\n", data[0]); + //PGS_DUMP ("data 0x%02x\n", data[0]); run_len = (data[0] & 0x3f); - if (run_len > 0) - pal_id = 0; data++; break; case 0x40: if (data + 2 > end) return; - //g_print ("data 0x%02x 0x%02x\n", data[0], data[1]); + //PGS_DUMP ("data 0x%02x 0x%02x\n", data[0], data[1]); run_len = ((data[0] << 8) | data[1]) & 0x3fff; - if (run_len > 0) - pal_id = 0; data += 2; break; case 0x80: if (data + 2 > end) return; - //g_print ("data 0x%02x 0x%02x\n", data[0], data[1]); + //PGS_DUMP ("data 0x%02x 0x%02x\n", data[0], data[1]); run_len = (data[0] & 0x3f); pal_id = data[1]; data += 2; @@ -113,7 +129,7 @@ dump_rle_data (guint8 * data, guint32 len) case 0xC0: if (data + 3 > end) return; - //g_print ("data 0x%02x 0x%02x 0x%02x\n", data[0], data[1], data[2]); + //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; @@ -124,113 +140,302 @@ dump_rle_data (guint8 * data, guint32 len) #if DUMP_FULL_IMAGE { gint i; - guint x = 0; #if 1 - if (palette[pal_id].A) { + 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++) - g_print ("%02x ", pal_id); + PGS_DUMP ("%02x ", val); } else { for (i = 0; i < run_len; i++) - g_print (" "); - } - x += run_len; - if (!run_len || x > obj_w) { - g_print ("\n"); - x = 0; + PGS_DUMP (" "); } + if (!run_len || (x + run_len) > obj_w) + PGS_DUMP ("\n"); #else - g_print ("Run x: %d pix: %d col: %d\n", x, run_len, pal_id); - x += run_len; - if (x >= obj_w) - x = 0; + 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; }; - g_print ("\n"); + 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 (guint8 type, guint8 * payload, guint16 len) +parse_presentation_segment (GstDVDSpu * dvdspu, guint8 type, guint8 * payload, + guint16 len) { guint8 *end = payload + len; - guint16 vid_w, vid_h; - gint8 vid_fps; - guint16 composition_desc_no; - guint8 composition_desc_state; - guint8 pres_seg_flags; - guint8 palette_id; - guint8 n_objects; + PgsPresentationSegment *ps = &dvdspu->spu_state.pgs.pres_seg; + guint8 n_objects, palette_id; gint i; /* Parse video descriptor */ if (payload + 5 > end) return 0; - vid_w = GST_READ_UINT16_BE (payload); - vid_h = GST_READ_UINT16_BE (payload + 2); - vid_fps = payload[4]; + + 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; - composition_desc_no = GST_READ_UINT16_BE (payload); - composition_desc_state = payload[2]; + ps->composition_no = GST_READ_UINT16_BE (payload); + ps->composition_state = payload[2]; payload += 3; /* Parse other bits */ if (payload + 3 > end) return 0; - pres_seg_flags = payload[0]; + ps->flags = payload[0]; + palette_id = payload[1]; n_objects = payload[2]; payload += 3; - g_print ("Video width %u height %u fps code %u\n", vid_w, vid_h, vid_fps); - g_print - ("Composition num %u state %u flags 0x%02x palette id %u n_objects %u\n", - composition_desc_no, composition_desc_state, pres_seg_flags, palette_id, + 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++) { - guint16 obj_id; - guint8 win_id; - guint8 obj_flags; - guint16 x, y; + PgsCompositionObject *obj = + &g_array_index (ps->objects, PgsCompositionObject, i); if (payload + 8 > end) break; - obj_id = GST_READ_UINT16_BE (payload); - win_id = payload[2]; - obj_flags = payload[3]; - x = GST_READ_UINT16_BE (payload + 4); - y = GST_READ_UINT16_BE (payload + 6); + 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; - g_print ("Composition object %d Object ID %u Window ID %u flags 0x%02x " - "x %u y %u\n", i, obj_id, win_id, obj_flags, x, y); + 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_COMP_OBJECT_FLAG_CROPPED) { - guint16 crop_x, crop_y, crop_w, crop_h; + if (obj->flags & PGS_COMPOSITION_OBJECT_FLAG_CROPPED) { if (payload + 8 > end) break; - crop_x = GST_READ_UINT16_BE (payload); - crop_y = GST_READ_UINT16_BE (payload + 2); - crop_w = GST_READ_UINT16_BE (payload + 4); - crop_h = GST_READ_UINT16_BE (payload + 6); + 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; - g_print ("Cropping window x %u y %u w %u h %u\n", - crop_x, crop_y, crop_w, crop_h); + 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_print ("%u bytes left over:\n", end - payload); + g_warning ("PGS Composition Object: %d bytes not consumed", end - payload); dump_bytes (payload, end - payload); } @@ -238,8 +443,11 @@ parse_presentation_segment (guint8 type, guint8 * payload, guint16 len) } static int -parse_set_palette (guint8 type, guint8 * payload, guint16 len) +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; @@ -254,33 +462,40 @@ parse_set_palette (guint8 type, guint8 * payload, guint16 len) n_entries = (len - 2) / PGS_PALETTE_ENTRY_SIZE; - g_print ("Palette ID %u version %u. %d entries\n", + 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, Cb, Cr, A; + guint8 n, Y, U, V, A; n = payload[0]; - palette[n].n = n; - palette[n].Y = Y = payload[1]; - palette[n].Cb = Cb = payload[2]; - palette[n].Cr = Cr = payload[3]; - palette[n].A = A = payload[4]; + Y = payload[1]; + U = payload[2]; + V = payload[3]; + A = payload[4]; - g_print ("Entry %3d: Y %3d Cb %3d Cr %3d A %3d ", n, Y, Cb, Cr, A); +#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) - g_print ("\n"); + 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; } - for (i = n_entries; i < 256; i++) { - palette[i].n = i; - palette[i].A = 0; - } +#if DUMP_FULL_PALETTE if (n_entries > 0 && (i % 2)) - g_print ("\n"); + PGS_DUMP ("\n"); +#endif if (payload != end) { - g_print ("%u bytes left over:\n", end - payload); + g_warning ("PGS Set Palette: %d bytes not consumed", end - payload); dump_bytes (payload, end - payload); } @@ -288,11 +503,12 @@ parse_set_palette (guint8 type, guint8 * payload, guint16 len) } static int -parse_set_window (guint8 type, guint8 * payload, guint16 len) +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; - guint16 x, y, w, h; if (payload + 10 > end) return 0; @@ -302,17 +518,18 @@ parse_set_window (guint8 type, guint8 * payload, guint16 len) /* FIXME: This is just a guess as to what the numbers mean: */ win_id = payload[0]; win_ver = payload[1]; - x = GST_READ_UINT16_BE (payload + 2); - y = GST_READ_UINT16_BE (payload + 4); - w = GST_READ_UINT16_BE (payload + 6); - h = GST_READ_UINT16_BE (payload + 8); + 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; - g_print ("Win ID %u version %d x %d y %d w %d h %d\n", - win_id, win_ver, x, y, w, h); + 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_print ("%u bytes left over:\n", end - payload); + g_warning ("PGS Set Window: %d bytes not consumed", end - payload); dump_bytes (payload, end - payload); } @@ -320,51 +537,60 @@ parse_set_window (guint8 type, guint8 * payload, guint16 len) } static int -parse_set_object_data (guint8 type, guint8 * payload, guint16 len) +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, obj_flags; + guint8 obj_ver, flags; if (payload + 4 > end) return 0; + obj_id = GST_READ_UINT16_BE (payload); obj_ver = payload[2]; - obj_flags = payload[3]; + flags = payload[3]; payload += 4; - g_print ("Object ID %d ver %u flags 0x%02x\n", obj_id, obj_ver, obj_flags); + obj = pgs_presentation_segment_find_object (&(pgs_state->pres_seg), obj_id); - if (obj_flags & PGS_OBJECT_UPDATE_FLAG_START_RLE) { + 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; - rle_data_size = GST_READ_UINT24_BE (payload); + obj->rle_data_size = GST_READ_UINT24_BE (payload); payload += 3; - g_print ("%d bytes of RLE data, of %d bytes total.\n", - end - payload, rle_data_size); + PGS_DUMP ("%d bytes of RLE data, of %d bytes total.\n", + end - payload, obj->rle_data_size); - rle_data = g_realloc (rle_data, rle_data_size); - rle_data_used = end - payload; - memcpy (rle_data, payload, end - payload); + 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 { - g_print ("%d bytes of additional RLE data\n", end - payload); - if (rle_data_size < rle_data_used + end - payload) - return 0; + 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 (rle_data + rle_data_used, payload, end - payload); - rle_data_used += end - payload; - payload = end; + memcpy (obj->rle_data + obj->rle_data_used, payload, end - payload); + obj->rle_data_used += end - payload; + payload = end; + } } - if (rle_data_size == rle_data_used) - dump_rle_data (rle_data, rle_data_size); + if (obj->rle_data_size == obj->rle_data_used) + dump_rle_data (dvdspu, obj->rle_data, obj->rle_data_size); if (payload != end) { - g_print ("%u bytes left over:\n", end - payload); + g_warning ("PGS Set Object Data: %d bytes not consumed", end - payload); dump_bytes (payload, end - payload); } @@ -372,56 +598,61 @@ parse_set_object_data (guint8 type, guint8 * payload, guint16 len) } static int -parse_pgs_packet (guint8 type, guint8 * payload, guint16 len) +parse_pgs_packet (GstDVDSpu * dvdspu, guint8 type, guint8 * payload, + guint16 len) { + SpuPgsState *pgs_state = &dvdspu->spu_state.pgs; int ret = 0; - if (!in_presentation_segment && type != PGS_COMMAND_PRESENTATION_SEGMENT) { - g_print ("Expected BEGIN PRESENTATION SEGMENT command. " + 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: - g_print ("*******************************************\n" + PGS_DUMP ("*******************************************\n" "Begin PRESENTATION_SEGMENT (0x%02x) packet len %u\n", type, len); - in_presentation_segment = TRUE; - ret = parse_presentation_segment (type, payload, 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: - g_print ("*** Set Object Data (0x%02x) packet len %u\n", type, len); - ret = parse_set_object_data (type, payload, len); + 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: - g_print ("*** Set Palette (0x%02x) packet len %u\n", type, len); - ret = parse_set_palette (type, payload, len); + 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: - g_print ("*** Set Window command (0x%02x) packet len %u\n", type, len); - ret = parse_set_window (type, payload, len); + 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: - g_print ("*** Interactive Segment command(0x%02x) packet len %u\n", + PGS_DUMP ("*** Interactive Segment command(0x%02x) packet len %u\n", type, len); dump_bytes (payload, len); break; case PGS_COMMAND_END_DISPLAY: - g_print ("*** End Display command (0x%02x) packet len %u\n", type, len); - in_presentation_segment = FALSE; + PGS_DUMP ("*** End Display command (0x%02x) packet len %u\n", type, + len); + pgs_state->in_presentation_segment = FALSE; break; default: - g_print ("*** Unknown command: type 0x%02x len %u. Skipping\n", type, - len); + g_warning ("Unknown PGS command: type 0x%02x len %u", type, len); + dump_bytes (payload, len); break; } - g_print ("\n"); + PGS_DUMP ("\n"); return ret; } gint -gstspu_dump_pgs_buffer (GstBuffer * buf) +gstspu_exec_pgs_buffer (GstDVDSpu * dvdspu, GstBuffer * buf) { guint8 *pos, *end; guint8 type; @@ -432,11 +663,11 @@ gstspu_dump_pgs_buffer (GstBuffer * buf) /* Need at least 3 bytes */ if (pos + 3 > end) { - g_print ("Not enough bytes to be a PGS packet\n"); + PGS_DUMP ("Not enough bytes to be a PGS packet\n"); return -1; } - g_print ("Begin dumping command buffer of size %u ts %" GST_TIME_FORMAT "\n", + 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++; @@ -444,17 +675,88 @@ gstspu_dump_pgs_buffer (GstBuffer * buf) pos += 2; if (pos + packet_len > end) { - g_print ("Invalid packet length %u (only have %u bytes)\n", packet_len, + PGS_DUMP ("Invalid packet length %u (only have %u bytes)\n", packet_len, end - pos); return -1; } - if (parse_pgs_packet (type, pos, packet_len)) + if (parse_pgs_packet (dvdspu, type, pos, packet_len)) return -1; pos += packet_len; } while (pos + 3 <= end); - g_print ("End dumping command buffer with %u bytes remaining\n", end - pos); + 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 index db94307108..164f4d8159 100644 --- a/gst/dvdspu/gstspu-pgs.h +++ b/gst/dvdspu/gstspu-pgs.h @@ -20,41 +20,87 @@ #ifndef __GSTSPU_PGS_H__ #define __GSTSPU_PGS_H__ -typedef 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, +#include "gstspu-common.h" - PGS_COMMAND_END_DISPLAY = 0x80, +typedef struct SpuPgsState SpuPgsState; +typedef enum PgsCompositionObjectFlags PgsCompositionObjectFlags; +typedef enum PgsPresentationSegmentFlags PgsPresentationSegmentFlags; +typedef enum PgsObjectUpdateFlags PgsObjectUpdateFlags; - PGS_COMMAND_INVALID = 0xFFFF -} PgsCommandType; +typedef struct PgsPresentationSegment PgsPresentationSegment; +typedef struct PgsCompositionObject PgsCompositionObject; -typedef enum PgsPresSegmentFlags { - PGS_PRES_SEGMENT_FLAG_UPDATE_PALETTE = 0x80 -} PgsPresSegmentFlags; +enum PgsPresentationSegmentFlags +{ + PGS_PRES_SEGMENT_FLAG_UPDATE_PALETTE = 0x80 +}; -typedef enum PgsCompObjectFlags { - PGS_COMP_OBJECT_FLAG_CROPPED = 0x80, - PGS_COMP_OBJECT_FLAG_FORCED = 0x40 -} PgsCompObjectFlags; +enum PgsCompositionObjectFlags +{ + PGS_COMPOSITION_OBJECT_FLAG_CROPPED = 0x80, + PGS_COMPOSITION_OBJECT_FLAG_FORCED = 0x40 +}; -typedef enum PgsObjectUpdateFlags { +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 -} PgsObjectUpdateFlags; + PGS_OBJECT_UPDATE_FLAG_START_RLE = 0x80, + PGS_OBJECT_UPDATE_FLAG_END_RLE = 0x40 /* This one is a guess */ +}; -typedef struct PgsPaletteEntry { - guint8 n; - guint8 Y; - guint8 Cb; - guint8 Cr; - guint8 A; -} PgsPaletteEntry; +struct PgsPresentationSegment +{ + guint16 composition_no; + guint8 composition_state; -gint gstspu_dump_pgs_buffer (GstBuffer *buf); + 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 0000000000..07abff2220 --- /dev/null +++ b/gst/dvdspu/gstspu-vobsub-render.c @@ -0,0 +1,536 @@ +/* GStreamer DVD Sub-Picture Unit + * Copyright (C) 2007 Fluendo S.A. + * Copyright (C) 2009 Jan Schmidt + * + * 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 +#endif + +#include + +#include + +#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 index c13d9ab026..1757feb705 100644 --- a/gst/dvdspu/gstspu-vobsub.c +++ b/gst/dvdspu/gstspu-vobsub.c @@ -36,6 +36,21 @@ GST_DEBUG_CATEGORY_EXTERN (dvdspu_debug); /* 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) { @@ -45,10 +60,10 @@ gst_dvd_spu_parse_chg_colcon (GstDVDSpu * dvdspu, guint8 * data, guint8 * end) 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; + 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)); @@ -75,12 +90,12 @@ gst_dvd_spu_parse_chg_colcon (GstDVDSpu * dvdspu, guint8 * data, guint8 * end) n_entries++; } - state->n_line_ctrl_i = n_entries; - state->line_ctrl_i = g_new (SpuLineCtrlI, 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++) { - SpuLineCtrlI *cur_line_ctrl = state->line_ctrl_i + i; + SpuVobsubLineCtrlI *cur_line_ctrl = state->vobsub.line_ctrl_i + i; guint8 n_changes = CLAMP ((cur[2] >> 4), 1, 8); guint8 c; @@ -93,7 +108,7 @@ gst_dvd_spu_parse_chg_colcon (GstDVDSpu * dvdspu, guint8 * data, guint8 * end) cur += 4; for (c = 0; c < n_changes; c++) { - SpuPixCtrlI *cur_pix_ctrl = cur_line_ctrl->pix_ctrl_i + 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); @@ -134,17 +149,17 @@ gst_dvd_spu_exec_cmd_blk (GstDVDSpu * dvdspu, guint8 * data, guint8 * end) 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->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->main_pal_dirty = TRUE; + state->vobsub.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]); + state->vobsub.main_idx[0], state->vobsub.main_idx[1], + state->vobsub.main_idx[2], state->vobsub.main_idx[3]); data += 3; break; } @@ -152,22 +167,22 @@ gst_dvd_spu_exec_cmd_blk (GstDVDSpu * dvdspu, guint8 * data, guint8 * end) 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->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->main_pal_dirty = TRUE; + state->vobsub.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]); + 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->disp_rect; + SpuRect *r = &state->vobsub.disp_rect; if (G_UNLIKELY (data + 7 >= end)) return; /* Invalid SET_DAREA cmd at the end of the blk */ @@ -188,14 +203,14 @@ gst_dvd_spu_exec_cmd_blk (GstDVDSpu * dvdspu, guint8 * data, guint8 * end) 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 + 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->pix_buf, state->buf); + gst_buffer_replace (&state->vobsub.pix_buf, state->vobsub.buf); GST_DEBUG_OBJECT (dvdspu, " Set Pixel Data Offsets top: %u bot: %u", - state->pix_data[0], state->pix_data[1]); + state->vobsub.pix_data[0], state->vobsub.pix_data[1]); data += 5; break; @@ -214,7 +229,7 @@ gst_dvd_spu_exec_cmd_blk (GstDVDSpu * dvdspu, guint8 * data, guint8 * 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; + state->vobsub.line_ctrl_i_pal_dirty = TRUE; data += field_size; break; } @@ -232,8 +247,8 @@ 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); + 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"); } @@ -250,11 +265,11 @@ gst_dvd_spu_setup_cmd_blk (GstDVDSpu * dvdspu, guint16 cmd_blk_offset, 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; + 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->cur_cmd_blk, GST_TIME_ARGS (state->next_ts)); + state->vobsub.cur_cmd_blk, GST_TIME_ARGS (state->next_ts)); return TRUE; } @@ -304,36 +319,37 @@ gst_dvd_spu_dump_dcsq (GstDVDSpu * dvdspu, #endif void -gst_dvd_spu_handle_new_vobsub_buf (GstDVDSpu * dvdspu, SpuPacket * packet) +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, packet->event_ts, packet->buf); + gst_dvd_spu_dump_dcsq (dvdspu, event_ts, buf); #endif - if (G_UNLIKELY (GST_BUFFER_SIZE (packet->buf) < 4)) + if (G_UNLIKELY (GST_BUFFER_SIZE (buf) < 4)) goto invalid; - if (state->buf != NULL) { - gst_buffer_unref (state->buf); - state->buf = NULL; + if (state->vobsub.buf != NULL) { + gst_buffer_unref (state->vobsub.buf); + state->vobsub.buf = NULL; } - state->buf = packet->buf; - state->base_ts = packet->event_ts; + state->vobsub.buf = buf; + state->vobsub.base_ts = event_ts; - start = GST_BUFFER_DATA (state->buf); - end = start + GST_BUFFER_SIZE (state->buf); + 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->cur_cmd_blk = GST_READ_UINT16_BE (start + 2); - gst_dvd_spu_setup_cmd_blk (dvdspu, state->cur_cmd_blk, start, end); + 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->n_line_ctrl_i = 0; - if (state->line_ctrl_i != NULL) { - g_free (state->line_ctrl_i); - state->line_ctrl_i = NULL; + 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; @@ -342,36 +358,156 @@ invalid: gst_dvd_spu_finish_spu_buf (dvdspu); } -void -gst_dvdspu_vobsub_execute_event (GstDVDSpu * 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->cur_cmd_blk); + " @ offset %u", GST_TIME_ARGS (state->next_ts), + state->vobsub.cur_cmd_blk); - start = GST_BUFFER_DATA (state->buf); - end = start + GST_BUFFER_SIZE (state->buf); + start = GST_BUFFER_DATA (state->vobsub.buf); + end = start + GST_BUFFER_SIZE (state->vobsub.buf); - cmd_blk = start + state->cur_cmd_blk; + 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; + 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->cur_cmd_blk) { + 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 index f600e7d756..9b4196bc78 100644 --- a/gst/dvdspu/gstspu-vobsub.h +++ b/gst/dvdspu/gstspu-vobsub.h @@ -16,10 +16,95 @@ * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. */ + #ifndef __GSTSPU_VOBSUB_H__ #define __GSTSPU_VOBSUB_H__ -void gst_dvd_spu_handle_new_vobsub_buf (GstDVDSpu * dvdspu, SpuPacket * packet); -void gst_dvdspu_vobsub_execute_event (GstDVDSpu *dvdspu); +#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