mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-11-20 08:41:07 +00:00
gstspu: Implement PGS rendering and alpha blending
Refactor the DVD subpicture compositing, switching it to 8-bit alpha calculations. Reuse some of the resulting code to implement PGS subpicture blending. Implement parsing and collecting of composition objects properly, but assuming a single active window and colour palette for now. I need more PGS samples.
This commit is contained in:
parent
7e20e3be45
commit
b68a05dbfa
10 changed files with 1529 additions and 969 deletions
|
@ -1,7 +1,7 @@
|
||||||
|
|
||||||
plugin_LTLIBRARIES = libgstdvdspu.la
|
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_CFLAGS = $(GST_CFLAGS)
|
||||||
libgstdvdspu_la_LIBADD = $(GST_LIBS)
|
libgstdvdspu_la_LIBADD = $(GST_LIBS)
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
/* GStreamer DVD Sub-Picture Unit
|
/* GStreamer DVD Sub-Picture Unit
|
||||||
* Copyright (C) 2007 Fluendo S.A. <info@fluendo.com>
|
* Copyright (C) 2007 Fluendo S.A. <info@fluendo.com>
|
||||||
|
* Copyright (C) 2009 Jan Schmidt <thaytan@noraisin.net>
|
||||||
*
|
*
|
||||||
* This library is free software; you can redistribute it and/or
|
* This library is free software; you can redistribute it and/or
|
||||||
* modify it under the terms of the GNU Library General Public
|
* modify it under the terms of the GNU Library General Public
|
||||||
|
@ -29,338 +30,38 @@
|
||||||
GST_DEBUG_CATEGORY_EXTERN (dvdspu_debug);
|
GST_DEBUG_CATEGORY_EXTERN (dvdspu_debug);
|
||||||
#define GST_CAT_DEFAULT dvdspu_debug
|
#define GST_CAT_DEFAULT dvdspu_debug
|
||||||
|
|
||||||
static void
|
void
|
||||||
dvdspu_recalc_palette (GstDVDSpu * dvdspu,
|
gstspu_clear_comp_buffers (SpuState * state)
|
||||||
SpuColour * dest, guint8 * idx, guint8 * alpha)
|
|
||||||
{
|
{
|
||||||
SpuState *state = &dvdspu->spu_state;
|
/* The area to clear is the line inside the disp_rect, each entry 4 bytes,
|
||||||
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,
|
|
||||||
* of the sub-sampled UV planes. */
|
* of the sub-sampled UV planes. */
|
||||||
gint16 left = state->disp_rect.left / 2;
|
gint16 left = state->comp_left / 2;
|
||||||
gint16 right = state->disp_rect.right / 2;
|
gint16 right = state->comp_right / 2;
|
||||||
gint16 uv_width = 2 * (right - left + 1);
|
gint16 uv_width = sizeof (guint32) * (right - left + 1);
|
||||||
|
|
||||||
memset (state->comp_bufs[0] + left, 0, uv_width);
|
memset (state->comp_bufs[0] + left, 0, uv_width);
|
||||||
memset (state->comp_bufs[1] + left, 0, uv_width);
|
memset (state->comp_bufs[1] + left, 0, uv_width);
|
||||||
memset (state->comp_bufs[2] + 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
|
void
|
||||||
dvdspu_get_nibble (SpuState * state, guint16 * rle_offset)
|
gstspu_blend_comp_buffers (SpuState * state, guint8 * planes[3])
|
||||||
{
|
|
||||||
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])
|
|
||||||
{
|
{
|
||||||
gint16 uv_end;
|
gint16 uv_end;
|
||||||
gint16 left, x;
|
gint16 left, x;
|
||||||
guint8 *out_U;
|
guint8 *out_U;
|
||||||
guint8 *out_V;
|
guint8 *out_V;
|
||||||
guint16 *in_U;
|
guint32 *in_U;
|
||||||
guint16 *in_V;
|
guint32 *in_V;
|
||||||
guint16 *in_A;
|
guint32 *in_A;
|
||||||
gint16 comp_last_x = MAX (state->comp_last_x[0], state->comp_last_x[1]);
|
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... */
|
return; /* Didn't draw in the comp buffers, nothing to do... */
|
||||||
|
|
||||||
#if 0
|
#if 0
|
||||||
GST_LOG ("Blending comp buffers from disp_rect.left %d to x=%d",
|
GST_LOG ("Blending comp buffers from x=%d to x=%d",
|
||||||
state->disp_rect.left, comp_last_x);
|
state->comp_left, state->comp_right);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* Set up the output pointers */
|
/* 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
|
* drawn in the render_line function, divided by 2 (rounding up) to account
|
||||||
* for UV sub-sampling */
|
* for UV sub-sampling */
|
||||||
uv_end = (comp_last_x + 1) / 2;
|
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++) {
|
for (x = left; x < uv_end; x++) {
|
||||||
guint16 tmp;
|
guint32 tmp;
|
||||||
guint16 inv_A = (4 * 0xf) - in_A[x];
|
|
||||||
|
|
||||||
/* Each entry in the compositing buffer is 4 summed pixels, so the
|
/* 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];
|
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];
|
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
|
|
||||||
}
|
|
||||||
|
|
|
@ -39,10 +39,6 @@
|
||||||
#include <gst/gst.h>
|
#include <gst/gst.h>
|
||||||
|
|
||||||
#include "gstdvdspu.h"
|
#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);
|
GST_DEBUG_CATEGORY (dvdspu_debug);
|
||||||
#define GST_CAT_DEFAULT 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,
|
static void gst_dvd_spu_flush_spu_info (GstDVDSpu * dvdspu,
|
||||||
gboolean process_events);
|
gboolean process_events);
|
||||||
static void gst_dvd_spu_advance_spu (GstDVDSpu * dvdspu, GstClockTime new_ts);
|
static void gst_dvd_spu_advance_spu (GstDVDSpu * dvdspu, GstClockTime new_ts);
|
||||||
|
static void gstspu_render (GstDVDSpu * dvdspu, GstBuffer * buf);
|
||||||
static GstFlowReturn
|
static GstFlowReturn
|
||||||
dvdspu_handle_vid_buffer (GstDVDSpu * dvdspu, GstBuffer * buf);
|
dvdspu_handle_vid_buffer (GstDVDSpu * dvdspu, GstBuffer * buf);
|
||||||
static void gst_dvd_spu_handle_dvd_event (GstDVDSpu * dvdspu, GstEvent * event);
|
static void gst_dvd_spu_handle_dvd_event (GstDVDSpu * dvdspu, GstEvent * event);
|
||||||
|
@ -119,11 +116,11 @@ static void
|
||||||
gst_dvd_spu_base_init (gpointer gclass)
|
gst_dvd_spu_base_init (gpointer gclass)
|
||||||
{
|
{
|
||||||
static GstElementDetails element_details =
|
static GstElementDetails element_details =
|
||||||
GST_ELEMENT_DETAILS ("Fluendo DVD Player Sub-picture Overlay",
|
GST_ELEMENT_DETAILS ("GStreamer Sub-picture Overlay",
|
||||||
"Mixer/Video/Overlay/DVD",
|
"Mixer/Video/Overlay/DVD/Bluray",
|
||||||
"Parses the DVD Sub-Picture command stream and renders the SPU overlay "
|
"Parses Sub-Picture command streams and renders the SPU overlay "
|
||||||
"onto the video as it passes through",
|
"onto the video as it passes through",
|
||||||
"Jan Schmidt <jan@fluendo.com>");
|
"Jan Schmidt <thaytan@noraisin.net>");
|
||||||
GstElementClass *element_class = GST_ELEMENT_CLASS (gclass);
|
GstElementClass *element_class = GST_ELEMENT_CLASS (gclass);
|
||||||
|
|
||||||
gst_element_class_add_pad_template (element_class,
|
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_dvd_spu_flush_spu_info (dvdspu, FALSE);
|
||||||
gst_segment_init (&dvdspu->subp_seg, GST_FORMAT_UNDEFINED);
|
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->ref_frame, NULL);
|
||||||
gst_buffer_replace (&dvdspu->pending_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 */
|
/* With SPU lock held, clear the queue of SPU packets */
|
||||||
static void
|
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;
|
SpuPacket *packet;
|
||||||
SpuState *state = &dvdspu->spu_state;
|
SpuState *state = &dvdspu->spu_state;
|
||||||
|
GQueue tmp_q = G_QUEUE_INIT;
|
||||||
|
|
||||||
GST_INFO_OBJECT (dvdspu, "Flushing SPU information");
|
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) {
|
if (packet->buf) {
|
||||||
gst_buffer_unref (packet->buf);
|
gst_buffer_unref (packet->buf);
|
||||||
g_assert (packet->event == NULL);
|
g_assert (packet->event == NULL);
|
||||||
|
g_free (packet);
|
||||||
} else if (packet->event) {
|
} else if (packet->event) {
|
||||||
if (process_events)
|
if (keep_events) {
|
||||||
gst_dvd_spu_handle_dvd_event (dvdspu, packet->event);
|
g_queue_push_tail (&tmp_q, packet);
|
||||||
else
|
} else {
|
||||||
gst_event_unref (packet->event);
|
gst_event_unref (packet->event);
|
||||||
|
g_free (packet);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
g_free (packet);
|
|
||||||
packet = (SpuPacket *) g_queue_pop_head (dvdspu->pending_spus);
|
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->flags &= ~(SPU_STATE_FLAGS_MASK);
|
||||||
state->pix_data[0] = 0;
|
state->next_ts = GST_CLOCK_TIME_NONE;
|
||||||
state->pix_data[1] = 0;
|
|
||||||
|
|
||||||
state->hl_rect.top = -1;
|
switch (dvdspu->spu_input_type) {
|
||||||
state->hl_rect.bottom = -1;
|
case SPU_INPUT_TYPE_VOBSUB:
|
||||||
|
gstspu_vobsub_flush (dvdspu);
|
||||||
state->disp_rect.top = -1;
|
break;
|
||||||
state->disp_rect.bottom = -1;
|
case SPU_INPUT_TYPE_PGS:
|
||||||
|
gstspu_pgs_flush (dvdspu);
|
||||||
state->n_line_ctrl_i = 0;
|
break;
|
||||||
if (state->line_ctrl_i != NULL) {
|
default:
|
||||||
g_free (state->line_ctrl_i);
|
break;
|
||||||
state->line_ctrl_i = NULL;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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);
|
state->UV_stride = GST_ROUND_UP_4 (state->Y_stride / 2);
|
||||||
for (i = 0; i < 3; i++) {
|
for (i = 0; i < 3; i++) {
|
||||||
state->comp_bufs[i] = g_realloc (state->comp_bufs[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);
|
DVD_SPU_UNLOCK (dvdspu);
|
||||||
|
@ -629,7 +622,7 @@ dvdspu_handle_vid_buffer (GstDVDSpu * dvdspu, GstBuffer * buf)
|
||||||
/* Render the SPU overlay onto the buffer */
|
/* Render the SPU overlay onto the buffer */
|
||||||
buf = gst_buffer_make_writable (buf);
|
buf = gst_buffer_make_writable (buf);
|
||||||
|
|
||||||
gst_dvd_spu_render_spu (dvdspu, buf);
|
gstspu_render (dvdspu, buf);
|
||||||
} else {
|
} else {
|
||||||
if (using_ref == FALSE) {
|
if (using_ref == FALSE) {
|
||||||
/* Not going to draw anything on this frame, just store a reference
|
/* Not going to draw anything on this frame, just store a reference
|
||||||
|
@ -658,6 +651,22 @@ no_ref_frame:
|
||||||
return GST_FLOW_OK;
|
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 */
|
/* With SPU LOCK */
|
||||||
static void
|
static void
|
||||||
gst_dvd_spu_redraw_still (GstDVDSpu * dvdspu, gboolean force)
|
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;
|
GST_BUFFER_DURATION (buf) = GST_CLOCK_TIME_NONE;
|
||||||
|
|
||||||
/* Render the SPU overlay onto the buffer */
|
/* 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_replace (&dvdspu->pending_frame, buf);
|
||||||
gst_buffer_unref (buf);
|
gst_buffer_unref (buf);
|
||||||
} else if (force) {
|
} else if (force) {
|
||||||
|
@ -707,87 +716,44 @@ gst_dvd_spu_redraw_still (GstDVDSpu * dvdspu, gboolean force)
|
||||||
static void
|
static void
|
||||||
gst_dvd_spu_handle_dvd_event (GstDVDSpu * dvdspu, GstEvent * event)
|
gst_dvd_spu_handle_dvd_event (GstDVDSpu * dvdspu, GstEvent * event)
|
||||||
{
|
{
|
||||||
const gchar *event_type;
|
|
||||||
const GstStructure *structure = gst_event_get_structure (event);
|
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;
|
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",
|
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));
|
event_type, (GST_EVENT_TYPE (event) == GST_EVENT_CUSTOM_DOWNSTREAM_OOB));
|
||||||
|
|
||||||
if (strcmp (event_type, "dvd-spu-clut-change") == 0) {
|
switch (dvdspu->spu_input_type) {
|
||||||
gchar prop_name[32];
|
case SPU_INPUT_TYPE_VOBSUB:
|
||||||
gint i;
|
hl_change = gstspu_vobsub_handle_dvd_event (dvdspu, event);
|
||||||
gint entry;
|
break;
|
||||||
|
case SPU_INPUT_TYPE_PGS:
|
||||||
for (i = 0; i < 16; i++) {
|
hl_change = gstspu_pgs_handle_dvd_event (dvdspu, event);
|
||||||
g_snprintf (prop_name, 32, "clut%02d", i);
|
break;
|
||||||
if (!gst_structure_get_int (structure, prop_name, &entry))
|
default:
|
||||||
entry = 0;
|
break;
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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_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 */
|
/* 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;
|
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) {
|
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;
|
GstClockTime vid_run_ts;
|
||||||
|
|
||||||
/* No current command buffer, try and get one */
|
/* 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) {
|
if (packet->buf) {
|
||||||
switch (dvdspu->spu_input_type) {
|
switch (dvdspu->spu_input_type) {
|
||||||
case SPU_INPUT_TYPE_VOBSUB:
|
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;
|
break;
|
||||||
case SPU_INPUT_TYPE_PGS:
|
case SPU_INPUT_TYPE_PGS:
|
||||||
gstspu_dump_pgs_buffer (packet->buf);
|
gstspu_pgs_handle_new_buf (dvdspu, packet->event_ts, packet->buf);
|
||||||
gst_buffer_unref (packet->buf);
|
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
g_assert_not_reached ();
|
g_assert_not_reached ();
|
||||||
|
@ -837,16 +810,6 @@ gst_dvd_spu_advance_spu (GstDVDSpu * dvdspu, GstClockTime new_ts)
|
||||||
g_free (packet);
|
g_free (packet);
|
||||||
continue;
|
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
|
/* 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 */
|
* 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) {
|
while (data != end) {
|
||||||
if (data + 3 > end)
|
if (data + 3 > end)
|
||||||
break;
|
break;
|
||||||
|
@ -1014,7 +978,8 @@ gst_dvd_spu_subpic_chain (GstPad * pad, GstBuffer * buf)
|
||||||
if (data + packet_size > end)
|
if (data + packet_size > end)
|
||||||
break;
|
break;
|
||||||
data += packet_size;
|
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 */
|
/* Extra cruft on the end of the packet -> assume invalid */
|
||||||
gst_buffer_unref (dvdspu->partial_spu);
|
gst_buffer_unref (dvdspu->partial_spu);
|
||||||
dvdspu->partial_spu = NULL;
|
dvdspu->partial_spu = NULL;
|
||||||
|
@ -1023,7 +988,8 @@ gst_dvd_spu_subpic_chain (GstPad * pad, GstBuffer * buf)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (dvdspu->partial_spu && data == end) {
|
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));
|
GST_BUFFER_SIZE (dvdspu->partial_spu));
|
||||||
submit_new_spu_packet (dvdspu, dvdspu->partial_spu);
|
submit_new_spu_packet (dvdspu, dvdspu->partial_spu);
|
||||||
dvdspu->partial_spu = NULL;
|
dvdspu->partial_spu = NULL;
|
||||||
|
@ -1071,7 +1037,8 @@ gst_dvd_spu_subpic_event (GstPad * pad, GstEvent * event)
|
||||||
DVD_SPU_LOCK (dvdspu);
|
DVD_SPU_LOCK (dvdspu);
|
||||||
if (GST_EVENT_IS_SERIALIZED (event)) {
|
if (GST_EVENT_IS_SERIALIZED (event)) {
|
||||||
SpuPacket *spu_packet = g_new0 (SpuPacket, 1);
|
SpuPacket *spu_packet = g_new0 (SpuPacket, 1);
|
||||||
|
GST_DEBUG_OBJECT (dvdspu,
|
||||||
|
"Enqueueing DVD event on subpicture pad for later");
|
||||||
spu_packet->event = event;
|
spu_packet->event = event;
|
||||||
g_queue_push_tail (dvdspu->pending_spus, spu_packet);
|
g_queue_push_tail (dvdspu->pending_spus, spu_packet);
|
||||||
} else {
|
} else {
|
||||||
|
@ -1146,6 +1113,7 @@ gst_dvd_spu_subpic_event (GstPad * pad, GstEvent * event)
|
||||||
gst_event_unref (event);
|
gst_event_unref (event);
|
||||||
goto done;
|
goto done;
|
||||||
case GST_EVENT_FLUSH_STOP:
|
case GST_EVENT_FLUSH_STOP:
|
||||||
|
GST_DEBUG_OBJECT (dvdspu, "Have flush-stop event on SPU pad");
|
||||||
DVD_SPU_LOCK (dvdspu);
|
DVD_SPU_LOCK (dvdspu);
|
||||||
gst_segment_init (&dvdspu->subp_seg, GST_FORMAT_UNDEFINED);
|
gst_segment_init (&dvdspu->subp_seg, GST_FORMAT_UNDEFINED);
|
||||||
gst_dvd_spu_flush_spu_info (dvdspu, TRUE);
|
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) {
|
if (dvdspu->spu_input_type != input_type) {
|
||||||
GST_INFO_OBJECT (dvdspu, "Incoming SPU packet type changed to %u",
|
GST_INFO_OBJECT (dvdspu, "Incoming SPU packet type changed to %u",
|
||||||
input_type);
|
input_type);
|
||||||
gst_dvd_spu_flush_spu_info (dvdspu, TRUE);
|
|
||||||
dvdspu->spu_input_type = input_type;
|
dvdspu->spu_input_type = input_type;
|
||||||
|
gst_dvd_spu_flush_spu_info (dvdspu, TRUE);
|
||||||
}
|
}
|
||||||
|
|
||||||
DVD_SPU_UNLOCK (dvdspu);
|
DVD_SPU_UNLOCK (dvdspu);
|
||||||
|
@ -1228,8 +1196,8 @@ gst_dvd_spu_change_state (GstElement * element, GstStateChange transition)
|
||||||
gboolean
|
gboolean
|
||||||
gst_dvd_spu_plugin_init (GstPlugin * plugin)
|
gst_dvd_spu_plugin_init (GstPlugin * plugin)
|
||||||
{
|
{
|
||||||
GST_DEBUG_CATEGORY_INIT (dvdspu_debug, "gstdvdspu",
|
GST_DEBUG_CATEGORY_INIT (dvdspu_debug, "gstspu",
|
||||||
0, "DVD Sub-picture Overlay decoder/renderer");
|
0, "Sub-picture Overlay decoder/renderer");
|
||||||
|
|
||||||
return gst_element_register (plugin, "dvdspu",
|
return gst_element_register (plugin, "dvdspu",
|
||||||
GST_RANK_NONE, GST_TYPE_DVD_SPU);
|
GST_RANK_NONE, GST_TYPE_DVD_SPU);
|
||||||
|
|
|
@ -16,11 +16,15 @@
|
||||||
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||||
* Boston, MA 02111-1307, USA.
|
* Boston, MA 02111-1307, USA.
|
||||||
*/
|
*/
|
||||||
#ifndef __DVD_SPU_H__
|
#ifndef __GST_DVD_SPU_H__
|
||||||
#define __DVD_SPU_H__
|
#define __GST_DVD_SPU_H__
|
||||||
|
|
||||||
#include <gst/gst.h>
|
#include <gst/gst.h>
|
||||||
|
|
||||||
|
#include "gstspu-common.h"
|
||||||
|
#include "gstspu-vobsub.h"
|
||||||
|
#include "gstspu-pgs.h"
|
||||||
|
|
||||||
G_BEGIN_DECLS
|
G_BEGIN_DECLS
|
||||||
|
|
||||||
#define GST_TYPE_DVD_SPU \
|
#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_LOCK(s) g_mutex_lock ((s)->spu_lock);
|
||||||
#define DVD_SPU_UNLOCK(s) g_mutex_unlock ((s)->spu_lock);
|
#define DVD_SPU_UNLOCK(s) g_mutex_unlock ((s)->spu_lock);
|
||||||
|
|
||||||
typedef struct _GstDVDSpu GstDVDSpu;
|
|
||||||
typedef struct _GstDVDSpuClass GstDVDSpuClass;
|
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 SpuStateFlags SpuStateFlags;
|
||||||
typedef enum SpuInputType SpuInputType;
|
typedef enum SpuInputType SpuInputType;
|
||||||
typedef struct SpuState SpuState;
|
|
||||||
typedef struct SpuPacket SpuPacket;
|
typedef struct SpuPacket SpuPacket;
|
||||||
typedef enum SpuCmd SpuCmd;
|
|
||||||
|
|
||||||
/* Describe the limits of a rectangle */
|
enum SpuInputType {
|
||||||
struct SpuRect {
|
SPU_INPUT_TYPE_NONE = 0x00,
|
||||||
gint16 left;
|
SPU_INPUT_TYPE_VOBSUB = 0x01,
|
||||||
gint16 top;
|
SPU_INPUT_TYPE_PGS = 0x02
|
||||||
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 SpuStateFlags {
|
enum SpuStateFlags {
|
||||||
|
@ -107,79 +63,23 @@ enum SpuStateFlags {
|
||||||
SPU_STATE_FORCED_ONLY = 0x100
|
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)
|
#define SPU_STATE_FLAGS_MASK (0xff)
|
||||||
|
|
||||||
struct SpuState {
|
struct SpuState {
|
||||||
GstClockTime next_ts; /* Next event TS in running time */
|
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;
|
SpuStateFlags flags;
|
||||||
|
|
||||||
/* Top + Bottom field offsets in the buffer. 0 = not set */
|
gint fps_n, fps_d;
|
||||||
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 */
|
|
||||||
gint16 vid_width, vid_height;
|
gint16 vid_width, vid_height;
|
||||||
gint16 Y_stride, UV_stride;
|
gint16 Y_stride, UV_stride;
|
||||||
gint16 Y_height, UV_height;
|
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 */
|
SpuVobsubState vobsub;
|
||||||
gint16 cur_Y;
|
SpuPgsState pgs;
|
||||||
|
|
||||||
/* 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;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Structure used to store the queue of pending SPU packets. The start_ts is
|
/* 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
|
G_END_DECLS
|
||||||
|
|
||||||
#endif /* __DVD_SPU_H__ */
|
#endif /* __GST_DVD_SPU_H__ */
|
||||||
|
|
56
gst/dvdspu/gstspu-common.h
Normal file
56
gst/dvdspu/gstspu-common.h
Normal file
|
@ -0,0 +1,56 @@
|
||||||
|
/* GStreamer DVD Sub-Picture Unit
|
||||||
|
* Copyright (C) 2007 Fluendo S.A. <info@fluendo.com>
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Library General Public
|
||||||
|
* License as published by the Free Software Foundation; either
|
||||||
|
* version 2 of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This library is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Library General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Library General Public
|
||||||
|
* License along with this library; if not, write to the
|
||||||
|
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||||
|
* Boston, MA 02111-1307, USA.
|
||||||
|
*/
|
||||||
|
#ifndef __GSTSPU_COMMON_H__
|
||||||
|
#define __GSTSPU_COMMON_H__
|
||||||
|
|
||||||
|
#include <glib.h>
|
||||||
|
|
||||||
|
G_BEGIN_DECLS
|
||||||
|
|
||||||
|
/* FIXME: Move this back to gstdvdspu.h when the renderers no longer use it: */
|
||||||
|
typedef struct _GstDVDSpu GstDVDSpu;
|
||||||
|
|
||||||
|
typedef struct SpuState SpuState;
|
||||||
|
typedef struct SpuColour SpuColour;
|
||||||
|
typedef struct SpuRect SpuRect;
|
||||||
|
|
||||||
|
/* Describe the limits of a rectangle */
|
||||||
|
struct SpuRect {
|
||||||
|
gint16 left;
|
||||||
|
gint16 top;
|
||||||
|
gint16 right;
|
||||||
|
gint16 bottom;
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Store a pre-multiplied colour value. The YUV fields hold the YUV values
|
||||||
|
* multiplied by the 8-bit alpha, to save computing it while rendering */
|
||||||
|
struct SpuColour {
|
||||||
|
guint16 Y;
|
||||||
|
guint16 U;
|
||||||
|
guint16 V;
|
||||||
|
guint8 A;
|
||||||
|
};
|
||||||
|
|
||||||
|
void gstspu_clear_comp_buffers (SpuState * state);
|
||||||
|
void gstspu_blend_comp_buffers (SpuState * state, guint8 * planes[3]);
|
||||||
|
|
||||||
|
|
||||||
|
G_END_DECLS
|
||||||
|
|
||||||
|
#endif /* __GSTSPU_COMMON_H__ */
|
|
@ -23,6 +23,7 @@
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <gst/gst.h>
|
#include <gst/gst.h>
|
||||||
|
|
||||||
|
#include "gstdvdspu.h"
|
||||||
#include "gstspu-pgs.h"
|
#include "gstspu-pgs.h"
|
||||||
|
|
||||||
const struct PgsFrameRateEntry
|
const struct PgsFrameRateEntry
|
||||||
|
@ -35,12 +36,32 @@ const struct PgsFrameRateEntry
|
||||||
64, 30000, 1001} /* 29.97 FPS */
|
64, 30000, 1001} /* 29.97 FPS */
|
||||||
};
|
};
|
||||||
|
|
||||||
gboolean in_presentation_segment = FALSE;
|
typedef enum PgsCommandType PgsCommandType;
|
||||||
guint8 *rle_data = NULL;
|
|
||||||
guint32 rle_data_size = 0, rle_data_used = 0;
|
|
||||||
PgsPaletteEntry palette[256];
|
|
||||||
|
|
||||||
|
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_IMAGE 0
|
||||||
|
#define DUMP_FULL_PALETTE 0
|
||||||
|
|
||||||
|
#if DUMP_CMDS
|
||||||
|
#define PGS_DUMP(...) g_print(__VA_ARGS__)
|
||||||
|
#else
|
||||||
|
#define PGS_DUMP(...)
|
||||||
|
#endif
|
||||||
|
|
||||||
static void
|
static void
|
||||||
dump_bytes (guint8 * data, guint16 len)
|
dump_bytes (guint8 * data, guint16 len)
|
||||||
|
@ -49,19 +70,20 @@ dump_bytes (guint8 * data, guint16 len)
|
||||||
|
|
||||||
/* Dump the numbers */
|
/* Dump the numbers */
|
||||||
for (i = 0; i < len; i++) {
|
for (i = 0; i < len; i++) {
|
||||||
g_print ("0x%02x ", data[i]);
|
PGS_DUMP ("0x%02x ", data[i]);
|
||||||
if (!((i + 1) % 16))
|
if (!((i + 1) % 16))
|
||||||
g_print ("\n");
|
PGS_DUMP ("\n");
|
||||||
}
|
}
|
||||||
if (len > 0 && (i % 16))
|
if (len > 0 && (i % 16))
|
||||||
g_print ("\n");
|
PGS_DUMP ("\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
dump_rle_data (guint8 * data, guint32 len)
|
dump_rle_data (GstDVDSpu * dvdspu, guint8 * data, guint32 len)
|
||||||
{
|
{
|
||||||
guint8 *end = data + len;
|
guint8 *end = data + len;
|
||||||
guint16 obj_w, obj_h;
|
guint16 obj_w, obj_h;
|
||||||
|
guint x = 0;
|
||||||
|
|
||||||
if (data + 4 > end)
|
if (data + 4 > end)
|
||||||
return;
|
return;
|
||||||
|
@ -70,42 +92,36 @@ dump_rle_data (guint8 * data, guint32 len)
|
||||||
obj_w = GST_READ_UINT16_BE (data);
|
obj_w = GST_READ_UINT16_BE (data);
|
||||||
obj_h = GST_READ_UINT16_BE (data + 2);
|
obj_h = GST_READ_UINT16_BE (data + 2);
|
||||||
data += 4;
|
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) {
|
while (data < end) {
|
||||||
guint8 pal_id;
|
guint8 pal_id;
|
||||||
guint16 run_len;
|
guint16 run_len;
|
||||||
|
|
||||||
if (data[0] != 0) {
|
pal_id = *data++;
|
||||||
// g_print ("data 0x%02x\n", data[0]);
|
if (pal_id != 0) {
|
||||||
pal_id = *data++;
|
// PGS_DUMP ("data 0x%02x\n", data[0]);
|
||||||
run_len = 1;
|
run_len = 1;
|
||||||
} else {
|
} else {
|
||||||
data++;
|
|
||||||
|
|
||||||
if (data + 1 > end)
|
if (data + 1 > end)
|
||||||
return;
|
return;
|
||||||
switch (data[0] & 0xC0) {
|
switch (data[0] & 0xC0) {
|
||||||
case 0x00:
|
case 0x00:
|
||||||
//g_print ("data 0x%02x\n", data[0]);
|
//PGS_DUMP ("data 0x%02x\n", data[0]);
|
||||||
run_len = (data[0] & 0x3f);
|
run_len = (data[0] & 0x3f);
|
||||||
if (run_len > 0)
|
|
||||||
pal_id = 0;
|
|
||||||
data++;
|
data++;
|
||||||
break;
|
break;
|
||||||
case 0x40:
|
case 0x40:
|
||||||
if (data + 2 > end)
|
if (data + 2 > end)
|
||||||
return;
|
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;
|
run_len = ((data[0] << 8) | data[1]) & 0x3fff;
|
||||||
if (run_len > 0)
|
|
||||||
pal_id = 0;
|
|
||||||
data += 2;
|
data += 2;
|
||||||
break;
|
break;
|
||||||
case 0x80:
|
case 0x80:
|
||||||
if (data + 2 > end)
|
if (data + 2 > end)
|
||||||
return;
|
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);
|
run_len = (data[0] & 0x3f);
|
||||||
pal_id = data[1];
|
pal_id = data[1];
|
||||||
data += 2;
|
data += 2;
|
||||||
|
@ -113,7 +129,7 @@ dump_rle_data (guint8 * data, guint32 len)
|
||||||
case 0xC0:
|
case 0xC0:
|
||||||
if (data + 3 > end)
|
if (data + 3 > end)
|
||||||
return;
|
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;
|
run_len = ((data[0] << 8) | data[1]) & 0x3fff;
|
||||||
pal_id = data[2];
|
pal_id = data[2];
|
||||||
data += 3;
|
data += 3;
|
||||||
|
@ -124,113 +140,302 @@ dump_rle_data (guint8 * data, guint32 len)
|
||||||
#if DUMP_FULL_IMAGE
|
#if DUMP_FULL_IMAGE
|
||||||
{
|
{
|
||||||
gint i;
|
gint i;
|
||||||
guint x = 0;
|
|
||||||
#if 1
|
#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++)
|
for (i = 0; i < run_len; i++)
|
||||||
g_print ("%02x ", pal_id);
|
PGS_DUMP ("%02x ", val);
|
||||||
} else {
|
} else {
|
||||||
for (i = 0; i < run_len; i++)
|
for (i = 0; i < run_len; i++)
|
||||||
g_print (" ");
|
PGS_DUMP (" ");
|
||||||
}
|
|
||||||
x += run_len;
|
|
||||||
if (!run_len || x > obj_w) {
|
|
||||||
g_print ("\n");
|
|
||||||
x = 0;
|
|
||||||
}
|
}
|
||||||
|
if (!run_len || (x + run_len) > obj_w)
|
||||||
|
PGS_DUMP ("\n");
|
||||||
#else
|
#else
|
||||||
g_print ("Run x: %d pix: %d col: %d\n", x, run_len, pal_id);
|
PGS_DUMP ("Run x: %d pix: %d col: %d\n", x, run_len, pal_id);
|
||||||
x += run_len;
|
|
||||||
if (x >= obj_w)
|
|
||||||
x = 0;
|
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
#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
|
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;
|
guint8 *end = payload + len;
|
||||||
guint16 vid_w, vid_h;
|
PgsPresentationSegment *ps = &dvdspu->spu_state.pgs.pres_seg;
|
||||||
gint8 vid_fps;
|
guint8 n_objects, palette_id;
|
||||||
guint16 composition_desc_no;
|
|
||||||
guint8 composition_desc_state;
|
|
||||||
guint8 pres_seg_flags;
|
|
||||||
guint8 palette_id;
|
|
||||||
guint8 n_objects;
|
|
||||||
gint i;
|
gint i;
|
||||||
|
|
||||||
/* Parse video descriptor */
|
/* Parse video descriptor */
|
||||||
if (payload + 5 > end)
|
if (payload + 5 > end)
|
||||||
return 0;
|
return 0;
|
||||||
vid_w = GST_READ_UINT16_BE (payload);
|
|
||||||
vid_h = GST_READ_UINT16_BE (payload + 2);
|
ps->vid_w = GST_READ_UINT16_BE (payload);
|
||||||
vid_fps = payload[4];
|
ps->vid_h = GST_READ_UINT16_BE (payload + 2);
|
||||||
|
ps->vid_fps_code = payload[4];
|
||||||
payload += 5;
|
payload += 5;
|
||||||
|
|
||||||
/* Parse composition descriptor */
|
/* Parse composition descriptor */
|
||||||
if (payload + 3 > end)
|
if (payload + 3 > end)
|
||||||
return 0;
|
return 0;
|
||||||
composition_desc_no = GST_READ_UINT16_BE (payload);
|
ps->composition_no = GST_READ_UINT16_BE (payload);
|
||||||
composition_desc_state = payload[2];
|
ps->composition_state = payload[2];
|
||||||
payload += 3;
|
payload += 3;
|
||||||
|
|
||||||
/* Parse other bits */
|
/* Parse other bits */
|
||||||
if (payload + 3 > end)
|
if (payload + 3 > end)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
pres_seg_flags = payload[0];
|
ps->flags = payload[0];
|
||||||
|
|
||||||
palette_id = payload[1];
|
palette_id = payload[1];
|
||||||
n_objects = payload[2];
|
n_objects = payload[2];
|
||||||
payload += 3;
|
payload += 3;
|
||||||
|
|
||||||
g_print ("Video width %u height %u fps code %u\n", vid_w, vid_h, vid_fps);
|
if (ps->flags & PGS_PRES_SEGMENT_FLAG_UPDATE_PALETTE)
|
||||||
g_print
|
ps->palette_id = palette_id;
|
||||||
("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,
|
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);
|
n_objects);
|
||||||
|
|
||||||
|
pgs_presentation_segment_set_object_count (ps, n_objects);
|
||||||
|
|
||||||
for (i = 0; i < (gint) n_objects; i++) {
|
for (i = 0; i < (gint) n_objects; i++) {
|
||||||
guint16 obj_id;
|
PgsCompositionObject *obj =
|
||||||
guint8 win_id;
|
&g_array_index (ps->objects, PgsCompositionObject, i);
|
||||||
guint8 obj_flags;
|
|
||||||
guint16 x, y;
|
|
||||||
|
|
||||||
if (payload + 8 > end)
|
if (payload + 8 > end)
|
||||||
break;
|
break;
|
||||||
obj_id = GST_READ_UINT16_BE (payload);
|
obj->id = GST_READ_UINT16_BE (payload);
|
||||||
win_id = payload[2];
|
obj->win_id = payload[2];
|
||||||
obj_flags = payload[3];
|
obj->flags = payload[3];
|
||||||
x = GST_READ_UINT16_BE (payload + 4);
|
obj->x = GST_READ_UINT16_BE (payload + 4);
|
||||||
y = GST_READ_UINT16_BE (payload + 6);
|
obj->y = GST_READ_UINT16_BE (payload + 6);
|
||||||
|
obj->rle_data_size = obj->rle_data_used = 0;
|
||||||
|
|
||||||
payload += 8;
|
payload += 8;
|
||||||
|
|
||||||
g_print ("Composition object %d Object ID %u Window ID %u flags 0x%02x "
|
PGS_DUMP ("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);
|
"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) {
|
if (obj->flags & PGS_COMPOSITION_OBJECT_FLAG_CROPPED) {
|
||||||
guint16 crop_x, crop_y, crop_w, crop_h;
|
|
||||||
if (payload + 8 > end)
|
if (payload + 8 > end)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
crop_x = GST_READ_UINT16_BE (payload);
|
obj->crop_x = GST_READ_UINT16_BE (payload);
|
||||||
crop_y = GST_READ_UINT16_BE (payload + 2);
|
obj->crop_y = GST_READ_UINT16_BE (payload + 2);
|
||||||
crop_w = GST_READ_UINT16_BE (payload + 4);
|
obj->crop_w = GST_READ_UINT16_BE (payload + 4);
|
||||||
crop_h = GST_READ_UINT16_BE (payload + 6);
|
obj->crop_h = GST_READ_UINT16_BE (payload + 6);
|
||||||
|
|
||||||
payload += 8;
|
payload += 8;
|
||||||
|
|
||||||
g_print ("Cropping window x %u y %u w %u h %u\n",
|
PGS_DUMP ("Cropping window x %u y %u w %u h %u\n",
|
||||||
crop_x, crop_y, crop_w, crop_h);
|
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) {
|
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);
|
dump_bytes (payload, end - payload);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -238,8 +443,11 @@ parse_presentation_segment (guint8 type, guint8 * payload, guint16 len)
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
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;
|
const gint PGS_PALETTE_ENTRY_SIZE = 5;
|
||||||
guint8 *end = payload + len;
|
guint8 *end = payload + len;
|
||||||
guint8 palette_id;
|
guint8 palette_id;
|
||||||
|
@ -254,33 +462,40 @@ parse_set_palette (guint8 type, guint8 * payload, guint16 len)
|
||||||
|
|
||||||
n_entries = (len - 2) / PGS_PALETTE_ENTRY_SIZE;
|
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);
|
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++) {
|
for (i = 0; i < n_entries; i++) {
|
||||||
guint8 n, Y, Cb, Cr, A;
|
guint8 n, Y, U, V, A;
|
||||||
n = payload[0];
|
n = payload[0];
|
||||||
palette[n].n = n;
|
Y = payload[1];
|
||||||
palette[n].Y = Y = payload[1];
|
U = payload[2];
|
||||||
palette[n].Cb = Cb = payload[2];
|
V = payload[3];
|
||||||
palette[n].Cr = Cr = payload[3];
|
A = payload[4];
|
||||||
palette[n].A = 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)
|
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;
|
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))
|
if (n_entries > 0 && (i % 2))
|
||||||
g_print ("\n");
|
PGS_DUMP ("\n");
|
||||||
|
#endif
|
||||||
|
|
||||||
if (payload != end) {
|
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);
|
dump_bytes (payload, end - payload);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -288,11 +503,12 @@ parse_set_palette (guint8 type, guint8 * payload, guint16 len)
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
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 *end = payload + len;
|
||||||
guint8 win_id, win_ver;
|
guint8 win_id, win_ver;
|
||||||
guint16 x, y, w, h;
|
|
||||||
|
|
||||||
if (payload + 10 > end)
|
if (payload + 10 > end)
|
||||||
return 0;
|
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: */
|
/* FIXME: This is just a guess as to what the numbers mean: */
|
||||||
win_id = payload[0];
|
win_id = payload[0];
|
||||||
win_ver = payload[1];
|
win_ver = payload[1];
|
||||||
x = GST_READ_UINT16_BE (payload + 2);
|
state->pgs.win_x = GST_READ_UINT16_BE (payload + 2);
|
||||||
y = GST_READ_UINT16_BE (payload + 4);
|
state->pgs.win_y = GST_READ_UINT16_BE (payload + 4);
|
||||||
w = GST_READ_UINT16_BE (payload + 6);
|
state->pgs.win_w = GST_READ_UINT16_BE (payload + 6);
|
||||||
h = GST_READ_UINT16_BE (payload + 8);
|
state->pgs.win_h = GST_READ_UINT16_BE (payload + 8);
|
||||||
payload += 10;
|
payload += 10;
|
||||||
|
|
||||||
g_print ("Win ID %u version %d x %d y %d w %d h %d\n",
|
PGS_DUMP ("Win ID %u version %d x %d y %d w %d h %d\n",
|
||||||
win_id, win_ver, x, y, w, h);
|
win_id, win_ver, state->pgs.win_x, state->pgs.win_y, state->pgs.win_w,
|
||||||
|
state->pgs.win_h);
|
||||||
|
|
||||||
if (payload != end) {
|
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);
|
dump_bytes (payload, end - payload);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -320,51 +537,60 @@ parse_set_window (guint8 type, guint8 * payload, guint16 len)
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
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;
|
guint8 *end = payload + len;
|
||||||
guint16 obj_id;
|
guint16 obj_id;
|
||||||
guint8 obj_ver, obj_flags;
|
guint8 obj_ver, flags;
|
||||||
|
|
||||||
if (payload + 4 > end)
|
if (payload + 4 > end)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
obj_id = GST_READ_UINT16_BE (payload);
|
obj_id = GST_READ_UINT16_BE (payload);
|
||||||
obj_ver = payload[2];
|
obj_ver = payload[2];
|
||||||
obj_flags = payload[3];
|
flags = payload[3];
|
||||||
payload += 4;
|
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)
|
if (payload + 3 > end)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
rle_data_size = GST_READ_UINT24_BE (payload);
|
obj->rle_data_size = GST_READ_UINT24_BE (payload);
|
||||||
payload += 3;
|
payload += 3;
|
||||||
|
|
||||||
g_print ("%d bytes of RLE data, of %d bytes total.\n",
|
PGS_DUMP ("%d bytes of RLE data, of %d bytes total.\n",
|
||||||
end - payload, rle_data_size);
|
end - payload, obj->rle_data_size);
|
||||||
|
|
||||||
rle_data = g_realloc (rle_data, rle_data_size);
|
obj->rle_data = g_realloc (obj->rle_data, obj->rle_data_size);
|
||||||
rle_data_used = end - payload;
|
obj->rle_data_used = end - payload;
|
||||||
memcpy (rle_data, payload, end - payload);
|
memcpy (obj->rle_data, payload, end - payload);
|
||||||
payload = end;
|
payload = end;
|
||||||
} else {
|
} else {
|
||||||
g_print ("%d bytes of additional RLE data\n", end - payload);
|
PGS_DUMP ("%d bytes of additional RLE data\n", end - payload);
|
||||||
if (rle_data_size < rle_data_used + end - payload)
|
/* Check that the data chunk is for this object version, and fits in the buffer */
|
||||||
return 0;
|
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);
|
memcpy (obj->rle_data + obj->rle_data_used, payload, end - payload);
|
||||||
rle_data_used += end - payload;
|
obj->rle_data_used += end - payload;
|
||||||
payload = end;
|
payload = end;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (rle_data_size == rle_data_used)
|
if (obj->rle_data_size == obj->rle_data_used)
|
||||||
dump_rle_data (rle_data, rle_data_size);
|
dump_rle_data (dvdspu, obj->rle_data, obj->rle_data_size);
|
||||||
|
|
||||||
if (payload != end) {
|
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);
|
dump_bytes (payload, end - payload);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -372,56 +598,61 @@ parse_set_object_data (guint8 type, guint8 * payload, guint16 len)
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
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;
|
int ret = 0;
|
||||||
|
|
||||||
if (!in_presentation_segment && type != PGS_COMMAND_PRESENTATION_SEGMENT) {
|
if (!pgs_state->in_presentation_segment
|
||||||
g_print ("Expected BEGIN PRESENTATION SEGMENT command. "
|
&& type != PGS_COMMAND_PRESENTATION_SEGMENT) {
|
||||||
|
PGS_DUMP ("Expected BEGIN PRESENTATION SEGMENT command. "
|
||||||
"Got command type 0x%02x len %u. Skipping\n", type, len);
|
"Got command type 0x%02x len %u. Skipping\n", type, len);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case PGS_COMMAND_PRESENTATION_SEGMENT:
|
case PGS_COMMAND_PRESENTATION_SEGMENT:
|
||||||
g_print ("*******************************************\n"
|
PGS_DUMP ("*******************************************\n"
|
||||||
"Begin PRESENTATION_SEGMENT (0x%02x) packet len %u\n", type, len);
|
"Begin PRESENTATION_SEGMENT (0x%02x) packet len %u\n", type, len);
|
||||||
in_presentation_segment = TRUE;
|
pgs_state->in_presentation_segment =
|
||||||
ret = parse_presentation_segment (type, payload, len);
|
pgs_state->have_presentation_segment = TRUE;
|
||||||
|
ret = parse_presentation_segment (dvdspu, type, payload, len);
|
||||||
break;
|
break;
|
||||||
case PGS_COMMAND_SET_OBJECT_DATA:
|
case PGS_COMMAND_SET_OBJECT_DATA:
|
||||||
g_print ("*** Set Object Data (0x%02x) packet len %u\n", type, len);
|
PGS_DUMP ("*** Set Object Data (0x%02x) packet len %u\n", type, len);
|
||||||
ret = parse_set_object_data (type, payload, len);
|
ret = parse_set_object_data (dvdspu, type, payload, len);
|
||||||
break;
|
break;
|
||||||
case PGS_COMMAND_SET_PALETTE:
|
case PGS_COMMAND_SET_PALETTE:
|
||||||
g_print ("*** Set Palette (0x%02x) packet len %u\n", type, len);
|
PGS_DUMP ("*** Set Palette (0x%02x) packet len %u\n", type, len);
|
||||||
ret = parse_set_palette (type, payload, len);
|
ret = parse_set_palette (dvdspu, type, payload, len);
|
||||||
break;
|
break;
|
||||||
case PGS_COMMAND_SET_WINDOW:
|
case PGS_COMMAND_SET_WINDOW:
|
||||||
g_print ("*** Set Window command (0x%02x) packet len %u\n", type, len);
|
PGS_DUMP ("*** Set Window command (0x%02x) packet len %u\n", type, len);
|
||||||
ret = parse_set_window (type, payload, len);
|
ret = parse_set_window (dvdspu, type, payload, len);
|
||||||
break;
|
break;
|
||||||
case PGS_COMMAND_INTERACTIVE_SEGMENT:
|
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);
|
type, len);
|
||||||
dump_bytes (payload, len);
|
dump_bytes (payload, len);
|
||||||
break;
|
break;
|
||||||
case PGS_COMMAND_END_DISPLAY:
|
case PGS_COMMAND_END_DISPLAY:
|
||||||
g_print ("*** End Display command (0x%02x) packet len %u\n", type, len);
|
PGS_DUMP ("*** End Display command (0x%02x) packet len %u\n", type,
|
||||||
in_presentation_segment = FALSE;
|
len);
|
||||||
|
pgs_state->in_presentation_segment = FALSE;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
g_print ("*** Unknown command: type 0x%02x len %u. Skipping\n", type,
|
g_warning ("Unknown PGS command: type 0x%02x len %u", type, len);
|
||||||
len);
|
dump_bytes (payload, len);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
g_print ("\n");
|
PGS_DUMP ("\n");
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
gint
|
gint
|
||||||
gstspu_dump_pgs_buffer (GstBuffer * buf)
|
gstspu_exec_pgs_buffer (GstDVDSpu * dvdspu, GstBuffer * buf)
|
||||||
{
|
{
|
||||||
guint8 *pos, *end;
|
guint8 *pos, *end;
|
||||||
guint8 type;
|
guint8 type;
|
||||||
|
@ -432,11 +663,11 @@ gstspu_dump_pgs_buffer (GstBuffer * buf)
|
||||||
|
|
||||||
/* Need at least 3 bytes */
|
/* Need at least 3 bytes */
|
||||||
if (pos + 3 > end) {
|
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;
|
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)));
|
end - pos, GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buf)));
|
||||||
do {
|
do {
|
||||||
type = *pos++;
|
type = *pos++;
|
||||||
|
@ -444,17 +675,88 @@ gstspu_dump_pgs_buffer (GstBuffer * buf)
|
||||||
pos += 2;
|
pos += 2;
|
||||||
|
|
||||||
if (pos + packet_len > end) {
|
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);
|
end - pos);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (parse_pgs_packet (type, pos, packet_len))
|
if (parse_pgs_packet (dvdspu, type, pos, packet_len))
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
pos += packet_len;
|
pos += packet_len;
|
||||||
} while (pos + 3 <= end);
|
} 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));
|
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;
|
||||||
|
}
|
||||||
|
|
|
@ -20,41 +20,87 @@
|
||||||
#ifndef __GSTSPU_PGS_H__
|
#ifndef __GSTSPU_PGS_H__
|
||||||
#define __GSTSPU_PGS_H__
|
#define __GSTSPU_PGS_H__
|
||||||
|
|
||||||
typedef enum PgsCommandType {
|
#include "gstspu-common.h"
|
||||||
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,
|
typedef struct SpuPgsState SpuPgsState;
|
||||||
|
typedef enum PgsCompositionObjectFlags PgsCompositionObjectFlags;
|
||||||
|
typedef enum PgsPresentationSegmentFlags PgsPresentationSegmentFlags;
|
||||||
|
typedef enum PgsObjectUpdateFlags PgsObjectUpdateFlags;
|
||||||
|
|
||||||
PGS_COMMAND_INVALID = 0xFFFF
|
typedef struct PgsPresentationSegment PgsPresentationSegment;
|
||||||
} PgsCommandType;
|
typedef struct PgsCompositionObject PgsCompositionObject;
|
||||||
|
|
||||||
typedef enum PgsPresSegmentFlags {
|
enum PgsPresentationSegmentFlags
|
||||||
PGS_PRES_SEGMENT_FLAG_UPDATE_PALETTE = 0x80
|
{
|
||||||
} PgsPresSegmentFlags;
|
PGS_PRES_SEGMENT_FLAG_UPDATE_PALETTE = 0x80
|
||||||
|
};
|
||||||
|
|
||||||
typedef enum PgsCompObjectFlags {
|
enum PgsCompositionObjectFlags
|
||||||
PGS_COMP_OBJECT_FLAG_CROPPED = 0x80,
|
{
|
||||||
PGS_COMP_OBJECT_FLAG_FORCED = 0x40
|
PGS_COMPOSITION_OBJECT_FLAG_CROPPED = 0x80,
|
||||||
} PgsCompObjectFlags;
|
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.
|
/* 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 */
|
* If not set, the data is a continuation to be appended */
|
||||||
PGS_OBJECT_UPDATE_FLAG_START_RLE = 0x80
|
PGS_OBJECT_UPDATE_FLAG_START_RLE = 0x80,
|
||||||
} PgsObjectUpdateFlags;
|
PGS_OBJECT_UPDATE_FLAG_END_RLE = 0x40 /* This one is a guess */
|
||||||
|
};
|
||||||
|
|
||||||
typedef struct PgsPaletteEntry {
|
struct PgsPresentationSegment
|
||||||
guint8 n;
|
{
|
||||||
guint8 Y;
|
guint16 composition_no;
|
||||||
guint8 Cb;
|
guint8 composition_state;
|
||||||
guint8 Cr;
|
|
||||||
guint8 A;
|
|
||||||
} PgsPaletteEntry;
|
|
||||||
|
|
||||||
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
|
#endif
|
||||||
|
|
536
gst/dvdspu/gstspu-vobsub-render.c
Normal file
536
gst/dvdspu/gstspu-vobsub-render.c
Normal file
|
@ -0,0 +1,536 @@
|
||||||
|
/* GStreamer DVD Sub-Picture Unit
|
||||||
|
* Copyright (C) 2007 Fluendo S.A. <info@fluendo.com>
|
||||||
|
* Copyright (C) 2009 Jan Schmidt <thaytan@noraisin.net>
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Library General Public
|
||||||
|
* License as published by the Free Software Foundation; either
|
||||||
|
* version 2 of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This library is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Library General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Library General Public
|
||||||
|
* License along with this library; if not, write to the
|
||||||
|
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||||
|
* Boston, MA 02111-1307, USA.
|
||||||
|
*/
|
||||||
|
#ifdef HAVE_CONFIG_H
|
||||||
|
# include <config.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include <gst/gst.h>
|
||||||
|
|
||||||
|
#include "gstdvdspu.h"
|
||||||
|
|
||||||
|
GST_DEBUG_CATEGORY_EXTERN (dvdspu_debug);
|
||||||
|
#define GST_CAT_DEFAULT dvdspu_debug
|
||||||
|
|
||||||
|
static void
|
||||||
|
gstspu_vobsub_recalc_palette (GstDVDSpu * dvdspu,
|
||||||
|
SpuColour * dest, guint8 * idx, guint8 * alpha)
|
||||||
|
{
|
||||||
|
SpuState *state = &dvdspu->spu_state;
|
||||||
|
gint i;
|
||||||
|
|
||||||
|
for (i = 0; i < 4; i++, dest++) {
|
||||||
|
guint32 col = state->vobsub.current_clut[idx[i]];
|
||||||
|
|
||||||
|
/* Convert incoming 4-bit alpha to 8 bit for blending */
|
||||||
|
dest->A = (alpha[i] << 4) | alpha[i];
|
||||||
|
dest->Y = ((guint16) ((col >> 16) & 0xff)) * dest->A;
|
||||||
|
/* U/V are stored as V/U in the clut words, so switch them */
|
||||||
|
dest->V = ((guint16) ((col >> 8) & 0xff)) * dest->A;
|
||||||
|
dest->U = ((guint16) (col & 0xff)) * dest->A;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Recalculate the main, HL & ChgCol palettes */
|
||||||
|
static void
|
||||||
|
gstspu_vobsub_update_palettes (GstDVDSpu * dvdspu, SpuState * state)
|
||||||
|
{
|
||||||
|
guint8 index[4]; /* Indices for the palette */
|
||||||
|
guint8 alpha[4]; /* Alpha values the palette */
|
||||||
|
|
||||||
|
if (state->vobsub.main_pal_dirty) {
|
||||||
|
gstspu_vobsub_recalc_palette (dvdspu, state->vobsub.main_pal,
|
||||||
|
state->vobsub.main_idx, state->vobsub.main_alpha);
|
||||||
|
|
||||||
|
/* Need to refresh the hl_ctrl info copies of the main palette too */
|
||||||
|
memcpy (state->vobsub.hl_ctrl_i.pix_ctrl_i[0].pal_cache,
|
||||||
|
state->vobsub.main_pal, 4 * sizeof (SpuColour));
|
||||||
|
memcpy (state->vobsub.hl_ctrl_i.pix_ctrl_i[2].pal_cache,
|
||||||
|
state->vobsub.main_pal, 4 * sizeof (SpuColour));
|
||||||
|
|
||||||
|
state->vobsub.main_pal_dirty = FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (state->vobsub.hl_pal_dirty) {
|
||||||
|
gstspu_vobsub_recalc_palette (dvdspu,
|
||||||
|
state->vobsub.hl_ctrl_i.pix_ctrl_i[1].pal_cache, state->vobsub.hl_idx,
|
||||||
|
state->vobsub.hl_alpha);
|
||||||
|
state->vobsub.hl_pal_dirty = FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Update the offset positions for the highlight region */
|
||||||
|
if (state->vobsub.hl_rect.top != -1) {
|
||||||
|
state->vobsub.hl_ctrl_i.top = state->vobsub.hl_rect.top;
|
||||||
|
state->vobsub.hl_ctrl_i.bottom = state->vobsub.hl_rect.bottom;
|
||||||
|
state->vobsub.hl_ctrl_i.n_changes = 3;
|
||||||
|
state->vobsub.hl_ctrl_i.pix_ctrl_i[0].left = 0;
|
||||||
|
state->vobsub.hl_ctrl_i.pix_ctrl_i[1].left = state->vobsub.hl_rect.left;
|
||||||
|
state->vobsub.hl_ctrl_i.pix_ctrl_i[2].left =
|
||||||
|
state->vobsub.hl_rect.right + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (state->vobsub.line_ctrl_i_pal_dirty) {
|
||||||
|
gint16 l, c;
|
||||||
|
GST_LOG_OBJECT (dvdspu, "Updating chg-col-con palettes");
|
||||||
|
for (l = 0; l < state->vobsub.n_line_ctrl_i; l++) {
|
||||||
|
SpuVobsubLineCtrlI *cur_line_ctrl = state->vobsub.line_ctrl_i + l;
|
||||||
|
|
||||||
|
for (c = 0; c < cur_line_ctrl->n_changes; c++) {
|
||||||
|
SpuVobsubPixCtrlI *cur = cur_line_ctrl->pix_ctrl_i + c;
|
||||||
|
|
||||||
|
index[3] = (cur->palette >> 28) & 0x0f;
|
||||||
|
index[2] = (cur->palette >> 24) & 0x0f;
|
||||||
|
index[1] = (cur->palette >> 20) & 0x0f;
|
||||||
|
index[0] = (cur->palette >> 16) & 0x0f;
|
||||||
|
|
||||||
|
alpha[3] = (cur->palette >> 12) & 0x0f;
|
||||||
|
alpha[2] = (cur->palette >> 8) & 0x0f;
|
||||||
|
alpha[1] = (cur->palette >> 4) & 0x0f;
|
||||||
|
alpha[0] = (cur->palette) & 0x0f;
|
||||||
|
gstspu_vobsub_recalc_palette (dvdspu, cur->pal_cache, index, alpha);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
state->vobsub.line_ctrl_i_pal_dirty = FALSE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline guint8
|
||||||
|
gstspu_vobsub_get_nibble (SpuState * state, guint16 * rle_offset)
|
||||||
|
{
|
||||||
|
guint8 ret;
|
||||||
|
|
||||||
|
if (G_UNLIKELY (*rle_offset >= state->vobsub.max_offset))
|
||||||
|
return 0; /* Overran the buffer */
|
||||||
|
|
||||||
|
ret = GST_BUFFER_DATA (state->vobsub.pix_buf)[(*rle_offset) / 2];
|
||||||
|
|
||||||
|
/* If the offset is even, we shift the answer down 4 bits, otherwise not */
|
||||||
|
if (*rle_offset & 0x01)
|
||||||
|
ret &= 0x0f;
|
||||||
|
else
|
||||||
|
ret = ret >> 4;
|
||||||
|
|
||||||
|
(*rle_offset)++;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static guint16
|
||||||
|
gstspu_vobsub_get_rle_code (SpuState * state, guint16 * rle_offset)
|
||||||
|
{
|
||||||
|
guint16 code;
|
||||||
|
|
||||||
|
code = gstspu_vobsub_get_nibble (state, rle_offset);
|
||||||
|
if (code < 0x4) { /* 4 .. f */
|
||||||
|
code = (code << 4) | gstspu_vobsub_get_nibble (state, rle_offset);
|
||||||
|
if (code < 0x10) { /* 1x .. 3x */
|
||||||
|
code = (code << 4) | gstspu_vobsub_get_nibble (state, rle_offset);
|
||||||
|
if (code < 0x40) { /* 04x .. 0fx */
|
||||||
|
code = (code << 4) | gstspu_vobsub_get_nibble (state, rle_offset);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return code;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void
|
||||||
|
gstspu_vobsub_draw_rle_run (SpuState * state, gint16 x, gint16 end,
|
||||||
|
SpuColour * colour)
|
||||||
|
{
|
||||||
|
#if 0
|
||||||
|
GST_LOG ("Y: %d x: %d end %d col %d %d %d %d",
|
||||||
|
state->vobsub.cur_Y, x, end, colour->Y, colour->U, colour->V, colour->A);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (colour->A != 0) {
|
||||||
|
guint32 inv_A = 0xff - colour->A;
|
||||||
|
|
||||||
|
/* FIXME: This could be more efficient */
|
||||||
|
while (x < end) {
|
||||||
|
state->vobsub.out_Y[x] =
|
||||||
|
(inv_A * state->vobsub.out_Y[x] + colour->Y) / 0xff;
|
||||||
|
state->vobsub.out_U[x / 2] += colour->U;
|
||||||
|
state->vobsub.out_V[x / 2] += colour->V;
|
||||||
|
state->vobsub.out_A[x / 2] += colour->A;
|
||||||
|
x++;
|
||||||
|
}
|
||||||
|
/* Update the compositing buffer so we know how much to blend later */
|
||||||
|
*(state->vobsub.comp_last_x_ptr) = end;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline gint16
|
||||||
|
rle_end_x (guint16 rle_code, gint16 x, gint16 end)
|
||||||
|
{
|
||||||
|
/* run length = rle_code >> 2 */
|
||||||
|
if (G_UNLIKELY (((rle_code >> 2) == 0)))
|
||||||
|
return end;
|
||||||
|
else
|
||||||
|
return MIN (end, x + (rle_code >> 2));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void gstspu_vobsub_render_line_with_chgcol (SpuState * state,
|
||||||
|
guint8 * planes[3], guint16 * rle_offset);
|
||||||
|
static gboolean gstspu_vobsub_update_chgcol (SpuState * state);
|
||||||
|
|
||||||
|
static void
|
||||||
|
gstspu_vobsub_render_line (SpuState * state, guint8 * planes[3],
|
||||||
|
guint16 * rle_offset)
|
||||||
|
{
|
||||||
|
gint16 x, next_x, end, rle_code;
|
||||||
|
SpuColour *colour;
|
||||||
|
|
||||||
|
/* Check for special case of chg_col info to use (either highlight or
|
||||||
|
* ChgCol command */
|
||||||
|
if (state->vobsub.cur_chg_col != NULL) {
|
||||||
|
if (gstspu_vobsub_update_chgcol (state)) {
|
||||||
|
/* Check the top & bottom, because we might not be within the region yet */
|
||||||
|
if (state->vobsub.cur_Y >= state->vobsub.cur_chg_col->top &&
|
||||||
|
state->vobsub.cur_Y <= state->vobsub.cur_chg_col->bottom) {
|
||||||
|
gstspu_vobsub_render_line_with_chgcol (state, planes, rle_offset);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* No special case. Render as normal */
|
||||||
|
|
||||||
|
/* Set up our output pointers */
|
||||||
|
state->vobsub.out_Y = planes[0];
|
||||||
|
state->vobsub.out_U = state->comp_bufs[0];
|
||||||
|
state->vobsub.out_V = state->comp_bufs[1];
|
||||||
|
state->vobsub.out_A = state->comp_bufs[2];
|
||||||
|
/* We always need to start our RLE decoding byte_aligned */
|
||||||
|
*rle_offset = GST_ROUND_UP_2 (*rle_offset);
|
||||||
|
|
||||||
|
x = state->vobsub.disp_rect.left;
|
||||||
|
end = state->vobsub.disp_rect.right + 1;
|
||||||
|
while (x < end) {
|
||||||
|
rle_code = gstspu_vobsub_get_rle_code (state, rle_offset);
|
||||||
|
colour = &state->vobsub.main_pal[rle_code & 3];
|
||||||
|
next_x = rle_end_x (rle_code, x, end);
|
||||||
|
/* Now draw the run between [x,next_x) */
|
||||||
|
gstspu_vobsub_draw_rle_run (state, x, next_x, colour);
|
||||||
|
x = next_x;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
gstspu_vobsub_update_chgcol (SpuState * state)
|
||||||
|
{
|
||||||
|
if (state->vobsub.cur_chg_col == NULL)
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
if (state->vobsub.cur_Y <= state->vobsub.cur_chg_col->bottom)
|
||||||
|
return TRUE;
|
||||||
|
|
||||||
|
while (state->vobsub.cur_chg_col < state->vobsub.cur_chg_col_end) {
|
||||||
|
if (state->vobsub.cur_Y >= state->vobsub.cur_chg_col->top &&
|
||||||
|
state->vobsub.cur_Y <= state->vobsub.cur_chg_col->bottom) {
|
||||||
|
#if 0
|
||||||
|
g_print ("Stopped @ entry %d with top %d bottom %d, cur_y %d",
|
||||||
|
(gint16) (state->vobsub.cur_chg_col - state->vobsub.line_ctrl_i),
|
||||||
|
state->vobsub.cur_chg_col->top, state->vobsub.cur_chg_col->bottom, y);
|
||||||
|
#endif
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
state->vobsub.cur_chg_col++;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Finished all our cur_chg_col entries. Use the main palette from here on */
|
||||||
|
state->vobsub.cur_chg_col = NULL;
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
gstspu_vobsub_render_line_with_chgcol (SpuState * state, guint8 * planes[3],
|
||||||
|
guint16 * rle_offset)
|
||||||
|
{
|
||||||
|
SpuVobsubLineCtrlI *chg_col = state->vobsub.cur_chg_col;
|
||||||
|
|
||||||
|
gint16 x, next_x, disp_end, rle_code, run_end;
|
||||||
|
SpuColour *colour;
|
||||||
|
SpuVobsubPixCtrlI *cur_pix_ctrl;
|
||||||
|
SpuVobsubPixCtrlI *next_pix_ctrl;
|
||||||
|
SpuVobsubPixCtrlI *end_pix_ctrl;
|
||||||
|
SpuVobsubPixCtrlI dummy_pix_ctrl;
|
||||||
|
gint16 cur_reg_end;
|
||||||
|
gint i;
|
||||||
|
|
||||||
|
state->vobsub.out_Y = planes[0];
|
||||||
|
state->vobsub.out_U = state->comp_bufs[0];
|
||||||
|
state->vobsub.out_V = state->comp_bufs[1];
|
||||||
|
state->vobsub.out_A = state->comp_bufs[2];
|
||||||
|
|
||||||
|
/* We always need to start our RLE decoding byte_aligned */
|
||||||
|
*rle_offset = GST_ROUND_UP_2 (*rle_offset);
|
||||||
|
|
||||||
|
/* Our run will cover the display rect */
|
||||||
|
x = state->vobsub.disp_rect.left;
|
||||||
|
disp_end = state->vobsub.disp_rect.right + 1;
|
||||||
|
|
||||||
|
/* Work out the first pixel control info, which may point to the dummy entry if
|
||||||
|
* the global palette/alpha need using initally */
|
||||||
|
cur_pix_ctrl = chg_col->pix_ctrl_i;
|
||||||
|
end_pix_ctrl = chg_col->pix_ctrl_i + chg_col->n_changes;
|
||||||
|
|
||||||
|
if (cur_pix_ctrl->left != 0) {
|
||||||
|
next_pix_ctrl = cur_pix_ctrl;
|
||||||
|
cur_pix_ctrl = &dummy_pix_ctrl;
|
||||||
|
for (i = 0; i < 4; i++) /* Copy the main palette to our dummy entry */
|
||||||
|
dummy_pix_ctrl.pal_cache[i] = state->vobsub.main_pal[i];
|
||||||
|
} else {
|
||||||
|
next_pix_ctrl = cur_pix_ctrl + 1;
|
||||||
|
}
|
||||||
|
if (next_pix_ctrl < end_pix_ctrl)
|
||||||
|
cur_reg_end = next_pix_ctrl->left;
|
||||||
|
else
|
||||||
|
cur_reg_end = disp_end;
|
||||||
|
|
||||||
|
/* Render stuff */
|
||||||
|
while (x < disp_end) {
|
||||||
|
rle_code = gstspu_vobsub_get_rle_code (state, rle_offset);
|
||||||
|
next_x = rle_end_x (rle_code, x, disp_end);
|
||||||
|
|
||||||
|
/* Now draw the run between [x,next_x), crossing palette regions as needed */
|
||||||
|
while (x < next_x) {
|
||||||
|
run_end = MIN (next_x, cur_reg_end);
|
||||||
|
|
||||||
|
if (G_LIKELY (x < run_end)) {
|
||||||
|
colour = &cur_pix_ctrl->pal_cache[rle_code & 3];
|
||||||
|
gstspu_vobsub_draw_rle_run (state, x, run_end, colour);
|
||||||
|
x = run_end;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (x >= cur_reg_end) {
|
||||||
|
/* Advance to next region */
|
||||||
|
cur_pix_ctrl = next_pix_ctrl;
|
||||||
|
next_pix_ctrl++;
|
||||||
|
|
||||||
|
if (next_pix_ctrl < end_pix_ctrl)
|
||||||
|
cur_reg_end = next_pix_ctrl->left;
|
||||||
|
else
|
||||||
|
cur_reg_end = disp_end;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
gstspu_vobsub_blend_comp_buffers (SpuState * state, guint8 * planes[3])
|
||||||
|
{
|
||||||
|
state->comp_left = state->vobsub.disp_rect.left;
|
||||||
|
state->comp_right =
|
||||||
|
MAX (state->vobsub.comp_last_x[0], state->vobsub.comp_last_x[1]);
|
||||||
|
|
||||||
|
gstspu_blend_comp_buffers (state, planes);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
gstspu_vobsub_clear_comp_buffers (SpuState * state)
|
||||||
|
{
|
||||||
|
state->comp_left = state->vobsub.disp_rect.left;
|
||||||
|
state->comp_right = state->vobsub.disp_rect.right;
|
||||||
|
|
||||||
|
gstspu_clear_comp_buffers (state);
|
||||||
|
|
||||||
|
state->vobsub.comp_last_x[0] = -1;
|
||||||
|
state->vobsub.comp_last_x[1] = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
gstspu_vobsub_render (GstDVDSpu * dvdspu, GstBuffer * buf)
|
||||||
|
{
|
||||||
|
SpuState *state = &dvdspu->spu_state;
|
||||||
|
guint8 *planes[3]; /* YUV frame pointers */
|
||||||
|
gint y, last_y;
|
||||||
|
|
||||||
|
/* Set up our initial state */
|
||||||
|
if (G_UNLIKELY (state->vobsub.pix_buf == NULL))
|
||||||
|
return;
|
||||||
|
|
||||||
|
/* Store the start of each plane */
|
||||||
|
planes[0] = GST_BUFFER_DATA (buf);
|
||||||
|
planes[1] = planes[0] + (state->Y_height * state->Y_stride);
|
||||||
|
planes[2] = planes[1] + (state->UV_height * state->UV_stride);
|
||||||
|
|
||||||
|
/* Sanity check */
|
||||||
|
g_return_if_fail (planes[2] + (state->UV_height * state->UV_stride) <=
|
||||||
|
GST_BUFFER_DATA (buf) + GST_BUFFER_SIZE (buf));
|
||||||
|
|
||||||
|
GST_DEBUG ("Rendering SPU. disp_rect %d,%d to %d,%d. hl_rect %d,%d to %d,%d",
|
||||||
|
state->vobsub.disp_rect.left, state->vobsub.disp_rect.top,
|
||||||
|
state->vobsub.disp_rect.right, state->vobsub.disp_rect.bottom,
|
||||||
|
state->vobsub.hl_rect.left, state->vobsub.hl_rect.top,
|
||||||
|
state->vobsub.hl_rect.right, state->vobsub.hl_rect.bottom);
|
||||||
|
|
||||||
|
GST_DEBUG ("vid_disp %d,%d", state->vid_width, state->vid_height);
|
||||||
|
|
||||||
|
/* When reading RLE data, we track the offset in nibbles... */
|
||||||
|
state->vobsub.cur_offsets[0] = state->vobsub.pix_data[0] * 2;
|
||||||
|
state->vobsub.cur_offsets[1] = state->vobsub.pix_data[1] * 2;
|
||||||
|
state->vobsub.max_offset = GST_BUFFER_SIZE (state->vobsub.pix_buf) * 2;
|
||||||
|
|
||||||
|
/* Update all the palette caches */
|
||||||
|
gstspu_vobsub_update_palettes (dvdspu, state);
|
||||||
|
|
||||||
|
/* Set up HL or Change Color & Contrast rect tracking */
|
||||||
|
if (state->vobsub.hl_rect.top != -1) {
|
||||||
|
state->vobsub.cur_chg_col = &state->vobsub.hl_ctrl_i;
|
||||||
|
state->vobsub.cur_chg_col_end = state->vobsub.cur_chg_col + 1;
|
||||||
|
} else if (state->vobsub.n_line_ctrl_i > 0) {
|
||||||
|
state->vobsub.cur_chg_col = state->vobsub.line_ctrl_i;
|
||||||
|
state->vobsub.cur_chg_col_end =
|
||||||
|
state->vobsub.cur_chg_col + state->vobsub.n_line_ctrl_i;
|
||||||
|
} else
|
||||||
|
state->vobsub.cur_chg_col = NULL;
|
||||||
|
|
||||||
|
/* We start rendering from the first line of the display rect */
|
||||||
|
y = state->vobsub.disp_rect.top;
|
||||||
|
/* start_y is always an even number and we render lines in pairs from there,
|
||||||
|
* accumulating 2 lines of chroma then blending it. We might need to render a
|
||||||
|
* single line at the end if the display rect ends on an even line too. */
|
||||||
|
last_y = (state->vobsub.disp_rect.bottom - 1) & ~(0x01);
|
||||||
|
|
||||||
|
/* center the image when display rectangle exceeds the video width */
|
||||||
|
if (state->vid_width < state->vobsub.disp_rect.right) {
|
||||||
|
gint diff, disp_width;
|
||||||
|
|
||||||
|
disp_width = state->vobsub.disp_rect.left - state->vobsub.disp_rect.right;
|
||||||
|
diff = (disp_width - state->vid_width) / 2;
|
||||||
|
|
||||||
|
/* fixme, this is not used yet */
|
||||||
|
state->vobsub.clip_rect.left = state->vobsub.disp_rect.left + diff;
|
||||||
|
state->vobsub.clip_rect.right = state->vobsub.disp_rect.right - diff;
|
||||||
|
|
||||||
|
GST_DEBUG ("clipping width to %d,%d", state->vobsub.clip_rect.left,
|
||||||
|
state->vobsub.clip_rect.right);
|
||||||
|
} else {
|
||||||
|
state->vobsub.clip_rect.left = state->vobsub.disp_rect.left;
|
||||||
|
state->vobsub.clip_rect.right = state->vobsub.disp_rect.right;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* for the height, chop off the bottom bits of the diplay rectangle because we
|
||||||
|
* assume the picture is in the lower part. We should better check where it
|
||||||
|
* is and do something more clever. */
|
||||||
|
state->vobsub.clip_rect.bottom = state->vobsub.disp_rect.bottom;
|
||||||
|
if (state->vid_height < state->vobsub.disp_rect.bottom) {
|
||||||
|
state->vobsub.clip_rect.top =
|
||||||
|
state->vobsub.disp_rect.bottom - state->vid_height;
|
||||||
|
GST_DEBUG ("clipping height to %d,%d", state->vobsub.clip_rect.top,
|
||||||
|
state->vobsub.clip_rect.bottom);
|
||||||
|
} else {
|
||||||
|
state->vobsub.clip_rect.top = state->vobsub.disp_rect.top;
|
||||||
|
/* Update our plane references to the first line of the disp_rect */
|
||||||
|
planes[0] += state->Y_stride * y;
|
||||||
|
planes[1] += state->UV_stride * (y / 2);
|
||||||
|
planes[2] += state->UV_stride * (y / 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (state->vobsub.cur_Y = y; state->vobsub.cur_Y <= last_y;
|
||||||
|
state->vobsub.cur_Y++) {
|
||||||
|
gboolean clip;
|
||||||
|
|
||||||
|
clip = (state->vobsub.cur_Y < state->vobsub.clip_rect.top
|
||||||
|
|| state->vobsub.cur_Y > state->vobsub.clip_rect.bottom);
|
||||||
|
|
||||||
|
/* Reset the compositing buffer */
|
||||||
|
gstspu_clear_comp_buffers (state);
|
||||||
|
/* Render even line */
|
||||||
|
state->vobsub.comp_last_x_ptr = state->vobsub.comp_last_x;
|
||||||
|
gstspu_vobsub_render_line (state, planes, &state->vobsub.cur_offsets[0]);
|
||||||
|
if (!clip) {
|
||||||
|
/* Advance the luminance output pointer */
|
||||||
|
planes[0] += state->Y_stride;
|
||||||
|
}
|
||||||
|
state->vobsub.cur_Y++;
|
||||||
|
|
||||||
|
/* Render odd line */
|
||||||
|
state->vobsub.comp_last_x_ptr = state->vobsub.comp_last_x + 1;
|
||||||
|
gstspu_vobsub_render_line (state, planes, &state->vobsub.cur_offsets[1]);
|
||||||
|
/* Blend the accumulated UV compositing buffers onto the output */
|
||||||
|
gstspu_vobsub_blend_comp_buffers (state, planes);
|
||||||
|
|
||||||
|
if (!clip) {
|
||||||
|
/* Update all the output pointers */
|
||||||
|
planes[0] += state->Y_stride;
|
||||||
|
planes[1] += state->UV_stride;
|
||||||
|
planes[2] += state->UV_stride;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (state->vobsub.cur_Y == state->vobsub.disp_rect.bottom) {
|
||||||
|
g_assert ((state->vobsub.disp_rect.bottom & 0x01) == 0);
|
||||||
|
|
||||||
|
/* Render a remaining lone last even line. y already has the correct value
|
||||||
|
* after the above loop exited. */
|
||||||
|
gstspu_clear_comp_buffers (state);
|
||||||
|
state->vobsub.comp_last_x_ptr = state->vobsub.comp_last_x;
|
||||||
|
gstspu_vobsub_render_line (state, planes, &state->vobsub.cur_offsets[0]);
|
||||||
|
gstspu_vobsub_blend_comp_buffers (state, planes);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* for debugging purposes, draw a faint rectangle at the edges of the disp_rect */
|
||||||
|
#if 0
|
||||||
|
do {
|
||||||
|
guint8 *cur;
|
||||||
|
gint16 pos;
|
||||||
|
|
||||||
|
cur = GST_BUFFER_DATA (buf) + state->Y_stride * state->vobsub.disp_rect.top;
|
||||||
|
for (pos = state->vobsub.disp_rect.left + 1;
|
||||||
|
pos < state->vobsub.disp_rect.right; pos++)
|
||||||
|
cur[pos] = (cur[pos] / 2) + 0x8;
|
||||||
|
cur =
|
||||||
|
GST_BUFFER_DATA (buf) +
|
||||||
|
state->Y_stride * state->vobsub.disp_rect.bottom;
|
||||||
|
for (pos = state->vobsub.disp_rect.left + 1;
|
||||||
|
pos < state->vobsub.disp_rect.right; pos++)
|
||||||
|
cur[pos] = (cur[pos] / 2) + 0x8;
|
||||||
|
cur = GST_BUFFER_DATA (buf) + state->Y_stride * state->vobsub.disp_rect.top;
|
||||||
|
for (pos = state->vobsub.disp_rect.top;
|
||||||
|
pos <= state->vobsub.disp_rect.bottom; pos++) {
|
||||||
|
cur[state->vobsub.disp_rect.left] =
|
||||||
|
(cur[state->vobsub.disp_rect.left] / 2) + 0x8;
|
||||||
|
cur[state->vobsub.disp_rect.right] =
|
||||||
|
(cur[state->vobsub.disp_rect.right] / 2) + 0x8;
|
||||||
|
cur += state->Y_stride;
|
||||||
|
}
|
||||||
|
} while (0);
|
||||||
|
#endif
|
||||||
|
/* For debugging purposes, draw a faint rectangle around the highlight rect */
|
||||||
|
#if 0
|
||||||
|
if (state->hl_rect.top != -1) {
|
||||||
|
guint8 *cur;
|
||||||
|
gint16 pos;
|
||||||
|
|
||||||
|
cur = GST_BUFFER_DATA (buf) + state->Y_stride * state->hl_rect.top;
|
||||||
|
for (pos = state->hl_rect.left + 1; pos < state->hl_rect.right; pos++)
|
||||||
|
cur[pos] = (cur[pos] / 2) + 0x8;
|
||||||
|
cur = GST_BUFFER_DATA (buf) + state->Y_stride * state->hl_rect.bottom;
|
||||||
|
for (pos = state->hl_rect.left + 1; pos < state->hl_rect.right; pos++)
|
||||||
|
cur[pos] = (cur[pos] / 2) + 0x8;
|
||||||
|
cur = GST_BUFFER_DATA (buf) + state->Y_stride * state->hl_rect.top;
|
||||||
|
for (pos = state->hl_rect.top; pos <= state->hl_rect.bottom; pos++) {
|
||||||
|
cur[state->hl_rect.left] = (cur[state->hl_rect.left] / 2) + 0x8;
|
||||||
|
cur[state->hl_rect.right] = (cur[state->hl_rect.right] / 2) + 0x8;
|
||||||
|
cur += state->Y_stride;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
|
@ -36,6 +36,21 @@ GST_DEBUG_CATEGORY_EXTERN (dvdspu_debug);
|
||||||
/* Convert an STM offset in the SPU sequence to a GStreamer timestamp */
|
/* Convert an STM offset in the SPU sequence to a GStreamer timestamp */
|
||||||
#define STM_TO_GST(stm) ((GST_MSECOND * 1024 * (stm)) / 90)
|
#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
|
static void
|
||||||
gst_dvd_spu_parse_chg_colcon (GstDVDSpu * dvdspu, guint8 * data, guint8 * end)
|
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;
|
gint16 i;
|
||||||
|
|
||||||
/* Clear any existing chg colcon info */
|
/* Clear any existing chg colcon info */
|
||||||
state->n_line_ctrl_i = 0;
|
state->vobsub.n_line_ctrl_i = 0;
|
||||||
if (state->line_ctrl_i != NULL) {
|
if (state->vobsub.line_ctrl_i != NULL) {
|
||||||
g_free (state->line_ctrl_i);
|
g_free (state->vobsub.line_ctrl_i);
|
||||||
state->line_ctrl_i = NULL;
|
state->vobsub.line_ctrl_i = NULL;
|
||||||
}
|
}
|
||||||
GST_DEBUG_OBJECT (dvdspu, "Change Color & Contrast. Pixel data = %d bytes",
|
GST_DEBUG_OBJECT (dvdspu, "Change Color & Contrast. Pixel data = %d bytes",
|
||||||
(gint16) (end - data));
|
(gint16) (end - data));
|
||||||
|
@ -75,12 +90,12 @@ gst_dvd_spu_parse_chg_colcon (GstDVDSpu * dvdspu, guint8 * data, guint8 * end)
|
||||||
n_entries++;
|
n_entries++;
|
||||||
}
|
}
|
||||||
|
|
||||||
state->n_line_ctrl_i = n_entries;
|
state->vobsub.n_line_ctrl_i = n_entries;
|
||||||
state->line_ctrl_i = g_new (SpuLineCtrlI, n_entries);
|
state->vobsub.line_ctrl_i = g_new (SpuVobsubLineCtrlI, n_entries);
|
||||||
|
|
||||||
cur = data;
|
cur = data;
|
||||||
for (i = 0; i < n_entries; i++) {
|
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 n_changes = CLAMP ((cur[2] >> 4), 1, 8);
|
||||||
guint8 c;
|
guint8 c;
|
||||||
|
|
||||||
|
@ -93,7 +108,7 @@ gst_dvd_spu_parse_chg_colcon (GstDVDSpu * dvdspu, guint8 * data, guint8 * end)
|
||||||
cur += 4;
|
cur += 4;
|
||||||
|
|
||||||
for (c = 0; c < n_changes; c++) {
|
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->left = ((cur[0] << 8) & 0x300) | cur[1];
|
||||||
cur_pix_ctrl->palette = GST_READ_UINT32_BE (cur + 2);
|
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))
|
if (G_UNLIKELY (data + 3 >= end))
|
||||||
return; /* Invalid SET_COLOR cmd at the end of the blk */
|
return; /* Invalid SET_COLOR cmd at the end of the blk */
|
||||||
|
|
||||||
state->main_idx[3] = data[1] >> 4;
|
state->vobsub.main_idx[3] = data[1] >> 4;
|
||||||
state->main_idx[2] = data[1] & 0x0f;
|
state->vobsub.main_idx[2] = data[1] & 0x0f;
|
||||||
state->main_idx[1] = data[2] >> 4;
|
state->vobsub.main_idx[1] = data[2] >> 4;
|
||||||
state->main_idx[0] = data[2] & 0x0f;
|
state->vobsub.main_idx[0] = data[2] & 0x0f;
|
||||||
|
|
||||||
state->main_pal_dirty = TRUE;
|
state->vobsub.main_pal_dirty = TRUE;
|
||||||
|
|
||||||
GST_DEBUG_OBJECT (dvdspu,
|
GST_DEBUG_OBJECT (dvdspu,
|
||||||
" Set Color bg %u pattern %u emph-1 %u emph-2 %u",
|
" 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->vobsub.main_idx[0], state->vobsub.main_idx[1],
|
||||||
state->main_idx[3]);
|
state->vobsub.main_idx[2], state->vobsub.main_idx[3]);
|
||||||
data += 3;
|
data += 3;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -152,22 +167,22 @@ gst_dvd_spu_exec_cmd_blk (GstDVDSpu * dvdspu, guint8 * data, guint8 * end)
|
||||||
if (G_UNLIKELY (data + 3 >= end))
|
if (G_UNLIKELY (data + 3 >= end))
|
||||||
return; /* Invalid SET_ALPHA cmd at the end of the blk */
|
return; /* Invalid SET_ALPHA cmd at the end of the blk */
|
||||||
|
|
||||||
state->main_alpha[3] = data[1] >> 4;
|
state->vobsub.main_alpha[3] = data[1] >> 4;
|
||||||
state->main_alpha[2] = data[1] & 0x0f;
|
state->vobsub.main_alpha[2] = data[1] & 0x0f;
|
||||||
state->main_alpha[1] = data[2] >> 4;
|
state->vobsub.main_alpha[1] = data[2] >> 4;
|
||||||
state->main_alpha[0] = data[2] & 0x0f;
|
state->vobsub.main_alpha[0] = data[2] & 0x0f;
|
||||||
|
|
||||||
state->main_pal_dirty = TRUE;
|
state->vobsub.main_pal_dirty = TRUE;
|
||||||
|
|
||||||
GST_DEBUG_OBJECT (dvdspu,
|
GST_DEBUG_OBJECT (dvdspu,
|
||||||
" Set Alpha bg %u pattern %u emph-1 %u emph-2 %u",
|
" 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->vobsub.main_alpha[0], state->vobsub.main_alpha[1],
|
||||||
state->main_alpha[3]);
|
state->vobsub.main_alpha[2], state->vobsub.main_alpha[3]);
|
||||||
data += 3;
|
data += 3;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case SPU_CMD_SET_DAREA:{
|
case SPU_CMD_SET_DAREA:{
|
||||||
SpuRect *r = &state->disp_rect;
|
SpuRect *r = &state->vobsub.disp_rect;
|
||||||
|
|
||||||
if (G_UNLIKELY (data + 7 >= end))
|
if (G_UNLIKELY (data + 7 >= end))
|
||||||
return; /* Invalid SET_DAREA cmd at the end of the blk */
|
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))
|
if (G_UNLIKELY (data + 5 >= end))
|
||||||
return; /* Invalid SET_DSPXE cmd at the end of the blk */
|
return; /* Invalid SET_DSPXE cmd at the end of the blk */
|
||||||
|
|
||||||
state->pix_data[0] = GST_READ_UINT16_BE (data + 1);
|
state->vobsub.pix_data[0] = GST_READ_UINT16_BE (data + 1);
|
||||||
state->pix_data[1] = GST_READ_UINT16_BE (data + 3);
|
state->vobsub.pix_data[1] = GST_READ_UINT16_BE (data + 3);
|
||||||
/* Store a reference to the current command buffer, as that's where
|
/* Store a reference to the current command buffer, as that's where
|
||||||
* we'll need to take our pixel data from */
|
* 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",
|
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;
|
data += 5;
|
||||||
break;
|
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 */
|
return; /* Invalid CHG_COLCON cmd at the end of the blk */
|
||||||
|
|
||||||
gst_dvd_spu_parse_chg_colcon (dvdspu, data + 2, data + field_size);
|
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;
|
data += field_size;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -232,8 +247,8 @@ gst_dvd_spu_finish_spu_buf (GstDVDSpu * dvdspu)
|
||||||
{
|
{
|
||||||
SpuState *state = &dvdspu->spu_state;
|
SpuState *state = &dvdspu->spu_state;
|
||||||
|
|
||||||
state->next_ts = state->base_ts = GST_CLOCK_TIME_NONE;
|
state->next_ts = state->vobsub.base_ts = GST_CLOCK_TIME_NONE;
|
||||||
gst_buffer_replace (&state->buf, NULL);
|
gst_buffer_replace (&state->vobsub.buf, NULL);
|
||||||
|
|
||||||
GST_DEBUG_OBJECT (dvdspu, "Finished SPU buffer");
|
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 */
|
return FALSE; /* No valid command block to read */
|
||||||
|
|
||||||
delay = GST_READ_UINT16_BE (cmd_blk);
|
delay = GST_READ_UINT16_BE (cmd_blk);
|
||||||
state->next_ts = state->base_ts + STM_TO_GST (delay);
|
state->next_ts = state->vobsub.base_ts + STM_TO_GST (delay);
|
||||||
state->cur_cmd_blk = cmd_blk_offset;
|
state->vobsub.cur_cmd_blk = cmd_blk_offset;
|
||||||
|
|
||||||
GST_DEBUG_OBJECT (dvdspu, "Setup CMD Block @ %u with TS %" GST_TIME_FORMAT,
|
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;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -304,36 +319,37 @@ gst_dvd_spu_dump_dcsq (GstDVDSpu * dvdspu,
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
void
|
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;
|
guint8 *start, *end;
|
||||||
SpuState *state = &dvdspu->spu_state;
|
SpuState *state = &dvdspu->spu_state;
|
||||||
|
|
||||||
#if DUMP_DCSQ
|
#if DUMP_DCSQ
|
||||||
gst_dvd_spu_dump_dcsq (dvdspu, packet->event_ts, packet->buf);
|
gst_dvd_spu_dump_dcsq (dvdspu, event_ts, buf);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (G_UNLIKELY (GST_BUFFER_SIZE (packet->buf) < 4))
|
if (G_UNLIKELY (GST_BUFFER_SIZE (buf) < 4))
|
||||||
goto invalid;
|
goto invalid;
|
||||||
|
|
||||||
if (state->buf != NULL) {
|
if (state->vobsub.buf != NULL) {
|
||||||
gst_buffer_unref (state->buf);
|
gst_buffer_unref (state->vobsub.buf);
|
||||||
state->buf = NULL;
|
state->vobsub.buf = NULL;
|
||||||
}
|
}
|
||||||
state->buf = packet->buf;
|
state->vobsub.buf = buf;
|
||||||
state->base_ts = packet->event_ts;
|
state->vobsub.base_ts = event_ts;
|
||||||
|
|
||||||
start = GST_BUFFER_DATA (state->buf);
|
start = GST_BUFFER_DATA (state->vobsub.buf);
|
||||||
end = start + GST_BUFFER_SIZE (state->buf);
|
end = start + GST_BUFFER_SIZE (state->vobsub.buf);
|
||||||
|
|
||||||
/* Configure the first command block in this buffer as our initial blk */
|
/* Configure the first command block in this buffer as our initial blk */
|
||||||
state->cur_cmd_blk = GST_READ_UINT16_BE (start + 2);
|
state->vobsub.cur_cmd_blk = GST_READ_UINT16_BE (start + 2);
|
||||||
gst_dvd_spu_setup_cmd_blk (dvdspu, state->cur_cmd_blk, start, end);
|
gst_dvd_spu_setup_cmd_blk (dvdspu, state->vobsub.cur_cmd_blk, start, end);
|
||||||
/* Clear existing chg-colcon info */
|
/* Clear existing chg-colcon info */
|
||||||
state->n_line_ctrl_i = 0;
|
state->vobsub.n_line_ctrl_i = 0;
|
||||||
if (state->line_ctrl_i != NULL) {
|
if (state->vobsub.line_ctrl_i != NULL) {
|
||||||
g_free (state->line_ctrl_i);
|
g_free (state->vobsub.line_ctrl_i);
|
||||||
state->line_ctrl_i = NULL;
|
state->vobsub.line_ctrl_i = NULL;
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
@ -342,36 +358,156 @@ invalid:
|
||||||
gst_dvd_spu_finish_spu_buf (dvdspu);
|
gst_dvd_spu_finish_spu_buf (dvdspu);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
gboolean
|
||||||
gst_dvdspu_vobsub_execute_event (GstDVDSpu * dvdspu)
|
gstspu_vobsub_execute_event (GstDVDSpu * dvdspu)
|
||||||
{
|
{
|
||||||
guint8 *start, *cmd_blk, *end;
|
guint8 *start, *cmd_blk, *end;
|
||||||
guint16 next_blk;
|
guint16 next_blk;
|
||||||
SpuState *state = &dvdspu->spu_state;
|
SpuState *state = &dvdspu->spu_state;
|
||||||
|
|
||||||
|
if (state->vobsub.buf == NULL)
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
GST_DEBUG_OBJECT (dvdspu, "Executing cmd blk with TS %" GST_TIME_FORMAT
|
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);
|
start = GST_BUFFER_DATA (state->vobsub.buf);
|
||||||
end = start + GST_BUFFER_SIZE (state->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)) {
|
if (G_UNLIKELY (cmd_blk + 5 >= end)) {
|
||||||
/* Invalid. Finish the buffer and loop again */
|
/* Invalid. Finish the buffer and loop again */
|
||||||
gst_dvd_spu_finish_spu_buf (dvdspu);
|
gst_dvd_spu_finish_spu_buf (dvdspu);
|
||||||
return;
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
gst_dvd_spu_exec_cmd_blk (dvdspu, cmd_blk + 4, end);
|
gst_dvd_spu_exec_cmd_blk (dvdspu, cmd_blk + 4, end);
|
||||||
|
|
||||||
next_blk = GST_READ_UINT16_BE (cmd_blk + 2);
|
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 */
|
/* Advance to the next block of commands */
|
||||||
gst_dvd_spu_setup_cmd_blk (dvdspu, next_blk, start, end);
|
gst_dvd_spu_setup_cmd_blk (dvdspu, next_blk, start, end);
|
||||||
} else {
|
} else {
|
||||||
/* Next Block points to the current block, so we're finished with this
|
/* Next Block points to the current block, so we're finished with this
|
||||||
* SPU buffer */
|
* SPU buffer */
|
||||||
gst_dvd_spu_finish_spu_buf (dvdspu);
|
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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,10 +16,95 @@
|
||||||
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||||
* Boston, MA 02111-1307, USA.
|
* Boston, MA 02111-1307, USA.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef __GSTSPU_VOBSUB_H__
|
#ifndef __GSTSPU_VOBSUB_H__
|
||||||
#define __GSTSPU_VOBSUB_H__
|
#define __GSTSPU_VOBSUB_H__
|
||||||
|
|
||||||
void gst_dvd_spu_handle_new_vobsub_buf (GstDVDSpu * dvdspu, SpuPacket * packet);
|
#include "gstspu-common.h"
|
||||||
void gst_dvdspu_vobsub_execute_event (GstDVDSpu *dvdspu);
|
|
||||||
|
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
|
#endif
|
||||||
|
|
Loading…
Reference in a new issue