summaryrefslogtreecommitdiffstats
path: root/gst/dvdspu/gstspu-pgs.c
diff options
context:
space:
mode:
Diffstat (limited to 'gst/dvdspu/gstspu-pgs.c')
-rw-r--r--gst/dvdspu/gstspu-pgs.c762
1 files changed, 762 insertions, 0 deletions
diff --git a/gst/dvdspu/gstspu-pgs.c b/gst/dvdspu/gstspu-pgs.c
new file mode 100644
index 00000000..d1d4de18
--- /dev/null
+++ b/gst/dvdspu/gstspu-pgs.c
@@ -0,0 +1,762 @@
+/* GStreamer Sub-Picture Unit - PGS handling
+ * Copyright (C) 2009 Jan Schmidt <thaytan@noraisin.net>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <string.h>
+#include <gst/gst.h>
+
+#include "gstdvdspu.h"
+#include "gstspu-pgs.h"
+
+const struct PgsFrameRateEntry
+{
+ guint8 id;
+ guint fps_n;
+ guint fps_d;
+} PgsFrameRates[] = {
+ {
+ 64, 30000, 1001} /* 29.97 FPS */
+};
+
+typedef enum PgsCommandType PgsCommandType;
+
+enum PgsCommandType
+{
+ PGS_COMMAND_SET_PALETTE = 0x14,
+ PGS_COMMAND_SET_OBJECT_DATA = 0x15,
+ PGS_COMMAND_PRESENTATION_SEGMENT = 0x16,
+ PGS_COMMAND_SET_WINDOW = 0x17,
+ PGS_COMMAND_INTERACTIVE_SEGMENT = 0x18,
+
+ PGS_COMMAND_END_DISPLAY = 0x80,
+
+ PGS_COMMAND_INVALID = 0xFFFF
+};
+
+static gint gstspu_exec_pgs_buffer (GstDVDSpu * dvdspu, GstBuffer * buf);
+
+#define DUMP_CMDS 0
+#define DUMP_FULL_IMAGE 0
+#define DUMP_FULL_PALETTE 0
+
+#if DUMP_CMDS
+#define PGS_DUMP(...) g_print(__VA_ARGS__)
+#else
+#define PGS_DUMP(...)
+#endif
+
+static void
+dump_bytes (guint8 * data, guint16 len)
+{
+ gint i;
+
+ /* Dump the numbers */
+ for (i = 0; i < len; i++) {
+ PGS_DUMP ("0x%02x ", data[i]);
+ if (!((i + 1) % 16))
+ PGS_DUMP ("\n");
+ }
+ if (len > 0 && (i % 16))
+ PGS_DUMP ("\n");
+}
+
+static void
+dump_rle_data (GstDVDSpu * dvdspu, guint8 * data, guint32 len)
+{
+ guint8 *end = data + len;
+ guint16 obj_w, obj_h;
+ guint x = 0;
+
+ if (data + 4 > end)
+ return;
+
+ /* RLE data: */
+ obj_w = GST_READ_UINT16_BE (data);
+ obj_h = GST_READ_UINT16_BE (data + 2);
+ data += 4;
+ PGS_DUMP ("RLE image is %ux%u\n", obj_w, obj_h);
+
+ while (data < end) {
+ guint8 pal_id;
+ guint16 run_len;
+
+ pal_id = *data++;
+ if (pal_id != 0) {
+ // PGS_DUMP ("data 0x%02x\n", data[0]);
+ run_len = 1;
+ } else {
+ if (data + 1 > end)
+ return;
+ switch (data[0] & 0xC0) {
+ case 0x00:
+ //PGS_DUMP ("data 0x%02x\n", data[0]);
+ run_len = (data[0] & 0x3f);
+ data++;
+ break;
+ case 0x40:
+ if (data + 2 > end)
+ return;
+ //PGS_DUMP ("data 0x%02x 0x%02x\n", data[0], data[1]);
+ run_len = ((data[0] << 8) | data[1]) & 0x3fff;
+ data += 2;
+ break;
+ case 0x80:
+ if (data + 2 > end)
+ return;
+ //PGS_DUMP ("data 0x%02x 0x%02x\n", data[0], data[1]);
+ run_len = (data[0] & 0x3f);
+ pal_id = data[1];
+ data += 2;
+ break;
+ case 0xC0:
+ if (data + 3 > end)
+ return;
+ //PGS_DUMP ("data 0x%02x 0x%02x 0x%02x\n", data[0], data[1], data[2]);
+ run_len = ((data[0] << 8) | data[1]) & 0x3fff;
+ pal_id = data[2];
+ data += 3;
+ break;
+ }
+ }
+
+#if DUMP_FULL_IMAGE
+ {
+ gint i;
+#if 1
+ if (dvdspu->spu_state.pgs.palette[pal_id].A) {
+ guint8 val = dvdspu->spu_state.pgs.palette[pal_id].A;
+ for (i = 0; i < run_len; i++)
+ PGS_DUMP ("%02x ", val);
+ } else {
+ for (i = 0; i < run_len; i++)
+ PGS_DUMP (" ");
+ }
+ if (!run_len || (x + run_len) > obj_w)
+ PGS_DUMP ("\n");
+#else
+ PGS_DUMP ("Run x: %d pix: %d col: %d\n", x, run_len, pal_id);
+#endif
+ }
+#endif
+
+ x += run_len;
+ if (!run_len || x > obj_w)
+ x = 0;
+ };
+
+ PGS_DUMP ("\n");
+}
+
+static void
+pgs_composition_object_render (PgsCompositionObject * obj, SpuState * state,
+ GstBuffer * dest_buf)
+{
+ SpuColour *colour;
+ guint8 *planes[3]; /* YUV frame pointers */
+ guint8 *data, *end;
+ guint16 obj_w, obj_h;
+ guint x, y, i, max_x;
+
+ if (G_UNLIKELY (obj->rle_data == NULL || obj->rle_data_size == 0
+ || obj->rle_data_used != obj->rle_data_size))
+ return;
+
+ data = obj->rle_data;
+ end = data + obj->rle_data_used;
+
+ if (data + 4 > end)
+ return;
+
+ /* FIXME: Calculate and use the cropping window for the output, as the
+ * intersection of the crop rectangle for this object (if any) and the
+ * window specified by the object's window_id */
+
+ /* Store the start of each plane */
+ planes[0] = GST_BUFFER_DATA (dest_buf);
+ planes[1] = planes[0] + (state->Y_height * state->Y_stride);
+ planes[2] = planes[1] + (state->UV_height * state->UV_stride);
+
+ /* Sanity check */
+ g_return_if_fail (planes[2] + (state->UV_height * state->UV_stride) <=
+ GST_BUFFER_DATA (dest_buf) + GST_BUFFER_SIZE (dest_buf));
+
+ x = obj->x;
+ y = obj->y;
+
+ planes[0] += state->Y_stride * y;
+ planes[1] += state->UV_stride * (y / 2);
+ planes[2] += state->UV_stride * (y / 2);
+
+ /* RLE data: */
+ obj_w = GST_READ_UINT16_BE (data);
+ obj_h = GST_READ_UINT16_BE (data + 2);
+ data += 4;
+
+ max_x = x + obj_w;
+
+ state->comp_left = x;
+ state->comp_right = max_x;
+
+ gstspu_clear_comp_buffers (state);
+
+ while (data < end) {
+ guint8 pal_id;
+ guint16 run_len;
+
+ pal_id = *data++;
+ if (pal_id != 0) {
+ run_len = 1;
+ } else {
+ if (data + 1 > end)
+ return;
+ switch (data[0] & 0xC0) {
+ case 0x00:
+ run_len = (data[0] & 0x3f);
+ data++;
+ break;
+ case 0x40:
+ if (data + 2 > end)
+ return;
+ run_len = ((data[0] << 8) | data[1]) & 0x3fff;
+ data += 2;
+ break;
+ case 0x80:
+ if (data + 2 > end)
+ return;
+ run_len = (data[0] & 0x3f);
+ pal_id = data[1];
+ data += 2;
+ break;
+ case 0xC0:
+ if (data + 3 > end)
+ return;
+ run_len = ((data[0] << 8) | data[1]) & 0x3fff;
+ pal_id = data[2];
+ data += 3;
+ break;
+ }
+ }
+
+ colour = &state->pgs.palette[pal_id];
+ if (colour->A) {
+ guint32 inv_A = 0xff - colour->A;
+
+ for (i = 0; i < run_len; i++) {
+ planes[0][x] = (inv_A * planes[0][x] + colour->Y) / 0xff;
+
+ state->comp_bufs[0][x / 2] += colour->U;
+ state->comp_bufs[1][x / 2] += colour->V;
+ state->comp_bufs[2][x / 2] += colour->A;
+ x++;
+ }
+ } else {
+ x += run_len;
+ }
+
+ if (!run_len || x > max_x) {
+ x = state->pgs.win_x;
+ planes[0] += state->Y_stride;
+
+ if (y % 2) {
+ gstspu_blend_comp_buffers (state, planes);
+ gstspu_clear_comp_buffers (state);
+
+ planes[1] += state->UV_stride;
+ planes[2] += state->UV_stride;
+ }
+ y++;
+ }
+ }
+
+ if (y % 2)
+ gstspu_blend_comp_buffers (state, planes);
+}
+
+static void
+pgs_composition_object_clear (PgsCompositionObject * obj)
+{
+ if (obj->rle_data) {
+ g_free (obj->rle_data);
+ obj->rle_data = NULL;
+ }
+ obj->rle_data_size = obj->rle_data_used = 0;
+}
+
+static void
+pgs_presentation_segment_set_object_count (PgsPresentationSegment * ps,
+ guint8 n_objects)
+{
+ if (ps->objects == NULL) {
+ ps->objects =
+ g_array_sized_new (FALSE, TRUE, sizeof (PgsCompositionObject),
+ n_objects);
+ g_array_set_size (ps->objects, n_objects);
+ return;
+ }
+
+ /* Clear memory in any extraneous objects */
+ if (ps->objects->len > n_objects) {
+ guint i;
+ for (i = n_objects; i < ps->objects->len; i++) {
+ PgsCompositionObject *cur =
+ &g_array_index (ps->objects, PgsCompositionObject, i);
+ pgs_composition_object_clear (cur);
+ }
+ }
+
+ g_array_set_size (ps->objects, n_objects);
+
+ if (n_objects == 0) {
+ g_array_free (ps->objects, TRUE);
+ ps->objects = NULL;
+ }
+}
+
+static PgsCompositionObject *
+pgs_presentation_segment_find_object (PgsPresentationSegment * ps,
+ guint16 obj_id)
+{
+ guint i;
+ if (ps->objects == NULL)
+ return NULL;
+
+ for (i = 0; i < ps->objects->len; i++) {
+ PgsCompositionObject *cur =
+ &g_array_index (ps->objects, PgsCompositionObject, i);
+ if (cur->id == obj_id)
+ return cur;
+ }
+
+ return NULL;
+}
+
+static int
+parse_presentation_segment (GstDVDSpu * dvdspu, guint8 type, guint8 * payload,
+ guint16 len)
+{
+ guint8 *end = payload + len;
+ PgsPresentationSegment *ps = &dvdspu->spu_state.pgs.pres_seg;
+ guint8 n_objects, palette_id;
+ gint i;
+
+ /* Parse video descriptor */
+ if (payload + 5 > end)
+ return 0;
+
+ ps->vid_w = GST_READ_UINT16_BE (payload);
+ ps->vid_h = GST_READ_UINT16_BE (payload + 2);
+ ps->vid_fps_code = payload[4];
+ payload += 5;
+
+ /* Parse composition descriptor */
+ if (payload + 3 > end)
+ return 0;
+ ps->composition_no = GST_READ_UINT16_BE (payload);
+ ps->composition_state = payload[2];
+ payload += 3;
+
+ /* Parse other bits */
+ if (payload + 3 > end)
+ return 0;
+
+ ps->flags = payload[0];
+
+ palette_id = payload[1];
+ n_objects = payload[2];
+ payload += 3;
+
+ if (ps->flags & PGS_PRES_SEGMENT_FLAG_UPDATE_PALETTE)
+ ps->palette_id = palette_id;
+
+ PGS_DUMP ("Video width %u height %u fps code %u\n", ps->vid_w, ps->vid_h,
+ ps->vid_fps_code);
+ PGS_DUMP
+ ("Composition num %u state 0x%02x flags 0x%02x palette id %u n_objects %u\n",
+ ps->composition_no, ps->composition_state, ps->flags, ps->palette_id,
+ n_objects);
+
+ pgs_presentation_segment_set_object_count (ps, n_objects);
+
+ for (i = 0; i < (gint) n_objects; i++) {
+ PgsCompositionObject *obj =
+ &g_array_index (ps->objects, PgsCompositionObject, i);
+
+ if (payload + 8 > end)
+ break;
+ obj->id = GST_READ_UINT16_BE (payload);
+ obj->win_id = payload[2];
+ obj->flags = payload[3];
+ obj->x = GST_READ_UINT16_BE (payload + 4);
+ obj->y = GST_READ_UINT16_BE (payload + 6);
+ obj->rle_data_size = obj->rle_data_used = 0;
+
+ payload += 8;
+
+ PGS_DUMP ("Composition object %d Object ID %u Window ID %u flags 0x%02x "
+ "x %u y %u\n", i, obj->id, obj->win_id, obj->flags, obj->x, obj->y);
+
+ if (obj->flags & PGS_COMPOSITION_OBJECT_FLAG_CROPPED) {
+ if (payload + 8 > end)
+ break;
+
+ obj->crop_x = GST_READ_UINT16_BE (payload);
+ obj->crop_y = GST_READ_UINT16_BE (payload + 2);
+ obj->crop_w = GST_READ_UINT16_BE (payload + 4);
+ obj->crop_h = GST_READ_UINT16_BE (payload + 6);
+
+ payload += 8;
+
+ PGS_DUMP ("Cropping window x %u y %u w %u h %u\n",
+ obj->crop_x, obj->crop_y, obj->crop_w, obj->crop_h);
+ }
+
+ if (obj->flags & ~(PGS_COMPOSITION_OBJECT_FLAG_CROPPED |
+ PGS_COMPOSITION_OBJECT_FLAG_FORCED))
+ g_warning ("PGS Composition Object has unknown flags: 0x%02x",
+ obj->flags);
+ }
+
+ if (payload != end) {
+ g_warning ("PGS Composition Object: %ld bytes not consumed", end - payload);
+ dump_bytes (payload, end - payload);
+ }
+
+ return 0;
+}
+
+static int
+parse_set_palette (GstDVDSpu * dvdspu, guint8 type, guint8 * payload,
+ guint16 len)
+{
+ SpuState *state = &dvdspu->spu_state;
+
+ const gint PGS_PALETTE_ENTRY_SIZE = 5;
+ guint8 *end = payload + len;
+ guint8 palette_id;
+ guint8 palette_version;
+ gint n_entries, i;
+
+ if (len < 2) /* Palette command too short */
+ return 0;
+ palette_id = payload[0];
+ palette_version = payload[1];
+ payload += 2;
+
+ n_entries = (len - 2) / PGS_PALETTE_ENTRY_SIZE;
+
+ PGS_DUMP ("Palette ID %u version %u. %d entries\n",
+ palette_id, palette_version, n_entries);
+ for (i = 0; i < 256; i++)
+ state->pgs.palette[i].A = 0;
+ for (i = 0; i < n_entries; i++) {
+ guint8 n, Y, U, V, A;
+ n = payload[0];
+ Y = payload[1];
+ U = payload[2];
+ V = payload[3];
+ A = payload[4];
+
+#if DUMP_FULL_PALETTE
+ PGS_DUMP ("Entry %3d: Y %3d U %3d V %3d A %3d ", n, Y, U, V, A);
+ if (((i + 1) % 2) == 0)
+ PGS_DUMP ("\n");
+#endif
+
+ /* Premultiply the palette entries by the alpha */
+ state->pgs.palette[n].Y = Y * A;
+ state->pgs.palette[n].U = U * A;
+ state->pgs.palette[n].V = V * A;
+ state->pgs.palette[n].A = A;
+
+ payload += PGS_PALETTE_ENTRY_SIZE;
+ }
+
+#if DUMP_FULL_PALETTE
+ if (n_entries > 0 && (i % 2))
+ PGS_DUMP ("\n");
+#endif
+
+ if (payload != end) {
+ g_warning ("PGS Set Palette: %ld bytes not consumed", end - payload);
+ dump_bytes (payload, end - payload);
+ }
+
+ return 0;
+}
+
+static int
+parse_set_window (GstDVDSpu * dvdspu, guint8 type, guint8 * payload,
+ guint16 len)
+{
+ SpuState *state = &dvdspu->spu_state;
+ guint8 *end = payload + len;
+ guint8 win_id, win_ver;
+
+ if (payload + 10 > end)
+ return 0;
+
+ dump_bytes (payload, len);
+
+ /* FIXME: This is just a guess as to what the numbers mean: */
+ win_id = payload[0];
+ win_ver = payload[1];
+ state->pgs.win_x = GST_READ_UINT16_BE (payload + 2);
+ state->pgs.win_y = GST_READ_UINT16_BE (payload + 4);
+ state->pgs.win_w = GST_READ_UINT16_BE (payload + 6);
+ state->pgs.win_h = GST_READ_UINT16_BE (payload + 8);
+ payload += 10;
+
+ PGS_DUMP ("Win ID %u version %d x %d y %d w %d h %d\n",
+ win_id, win_ver, state->pgs.win_x, state->pgs.win_y, state->pgs.win_w,
+ state->pgs.win_h);
+
+ if (payload != end) {
+ g_warning ("PGS Set Window: %ld bytes not consumed", end - payload);
+ dump_bytes (payload, end - payload);
+ }
+
+ return 0;
+}
+
+static int
+parse_set_object_data (GstDVDSpu * dvdspu, guint8 type, guint8 * payload,
+ guint16 len)
+{
+ SpuPgsState *pgs_state = &dvdspu->spu_state.pgs;
+ PgsCompositionObject *obj;
+ guint8 *end = payload + len;
+ guint16 obj_id;
+ guint8 obj_ver, flags;
+
+ if (payload + 4 > end)
+ return 0;
+
+ obj_id = GST_READ_UINT16_BE (payload);
+ obj_ver = payload[2];
+ flags = payload[3];
+ payload += 4;
+
+ obj = pgs_presentation_segment_find_object (&(pgs_state->pres_seg), obj_id);
+
+ PGS_DUMP ("Object ID %d ver %u flags 0x%02x\n", obj_id, obj_ver, flags);
+
+ if (flags & PGS_OBJECT_UPDATE_FLAG_START_RLE) {
+ obj->rle_data_ver = obj_ver;
+
+ if (payload + 3 > end)
+ return 0;
+
+ obj->rle_data_size = GST_READ_UINT24_BE (payload);
+ payload += 3;
+
+ PGS_DUMP ("%d bytes of RLE data, of %d bytes total.\n",
+ end - payload, obj->rle_data_size);
+
+ obj->rle_data = g_realloc (obj->rle_data, obj->rle_data_size);
+ obj->rle_data_used = end - payload;
+ memcpy (obj->rle_data, payload, end - payload);
+ payload = end;
+ } else {
+ PGS_DUMP ("%d bytes of additional RLE data\n", end - payload);
+ /* Check that the data chunk is for this object version, and fits in the buffer */
+ if (obj->rle_data_ver == obj_ver &&
+ obj->rle_data_used + end - payload <= obj->rle_data_size) {
+
+ memcpy (obj->rle_data + obj->rle_data_used, payload, end - payload);
+ obj->rle_data_used += end - payload;
+ payload = end;
+ }
+ }
+
+ if (obj->rle_data_size == obj->rle_data_used)
+ dump_rle_data (dvdspu, obj->rle_data, obj->rle_data_size);
+
+ if (payload != end) {
+ g_warning ("PGS Set Object Data: %ld bytes not consumed", end - payload);
+ dump_bytes (payload, end - payload);
+ }
+
+ return 0;
+}
+
+static int
+parse_pgs_packet (GstDVDSpu * dvdspu, guint8 type, guint8 * payload,
+ guint16 len)
+{
+ SpuPgsState *pgs_state = &dvdspu->spu_state.pgs;
+ int ret = 0;
+
+ if (!pgs_state->in_presentation_segment
+ && type != PGS_COMMAND_PRESENTATION_SEGMENT) {
+ PGS_DUMP ("Expected BEGIN PRESENTATION SEGMENT command. "
+ "Got command type 0x%02x len %u. Skipping\n", type, len);
+ return 0;
+ }
+
+ switch (type) {
+ case PGS_COMMAND_PRESENTATION_SEGMENT:
+ PGS_DUMP ("*******************************************\n"
+ "Begin PRESENTATION_SEGMENT (0x%02x) packet len %u\n", type, len);
+ pgs_state->in_presentation_segment =
+ pgs_state->have_presentation_segment = TRUE;
+ ret = parse_presentation_segment (dvdspu, type, payload, len);
+ break;
+ case PGS_COMMAND_SET_OBJECT_DATA:
+ PGS_DUMP ("*** Set Object Data (0x%02x) packet len %u\n", type, len);
+ ret = parse_set_object_data (dvdspu, type, payload, len);
+ break;
+ case PGS_COMMAND_SET_PALETTE:
+ PGS_DUMP ("*** Set Palette (0x%02x) packet len %u\n", type, len);
+ ret = parse_set_palette (dvdspu, type, payload, len);
+ break;
+ case PGS_COMMAND_SET_WINDOW:
+ PGS_DUMP ("*** Set Window command (0x%02x) packet len %u\n", type, len);
+ ret = parse_set_window (dvdspu, type, payload, len);
+ break;
+ case PGS_COMMAND_INTERACTIVE_SEGMENT:
+ PGS_DUMP ("*** Interactive Segment command(0x%02x) packet len %u\n",
+ type, len);
+ dump_bytes (payload, len);
+ break;
+ case PGS_COMMAND_END_DISPLAY:
+ PGS_DUMP ("*** End Display command (0x%02x) packet len %u\n", type,
+ len);
+ pgs_state->in_presentation_segment = FALSE;
+ break;
+ default:
+ g_warning ("Unknown PGS command: type 0x%02x len %u", type, len);
+ dump_bytes (payload, len);
+ break;
+ }
+ PGS_DUMP ("\n");
+
+ return ret;
+}
+
+gint
+gstspu_exec_pgs_buffer (GstDVDSpu * dvdspu, GstBuffer * buf)
+{
+ guint8 *pos, *end;
+ guint8 type;
+ guint16 packet_len;
+
+ pos = GST_BUFFER_DATA (buf);
+ end = pos + GST_BUFFER_SIZE (buf);
+
+ /* Need at least 3 bytes */
+ if (pos + 3 > end) {
+ PGS_DUMP ("Not enough bytes to be a PGS packet\n");
+ return -1;
+ }
+
+ PGS_DUMP ("Begin dumping command buffer of size %u ts %" GST_TIME_FORMAT "\n",
+ end - pos, GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buf)));
+ do {
+ type = *pos++;
+ packet_len = GST_READ_UINT16_BE (pos);
+ pos += 2;
+
+ if (pos + packet_len > end) {
+ PGS_DUMP ("Invalid packet length %u (only have %u bytes)\n", packet_len,
+ end - pos);
+ return -1;
+ }
+
+ if (parse_pgs_packet (dvdspu, type, pos, packet_len))
+ return -1;
+
+ pos += packet_len;
+ } while (pos + 3 <= end);
+
+ PGS_DUMP ("End dumping command buffer with %u bytes remaining\n", end - pos);
+ return (pos - GST_BUFFER_DATA (buf));
+}
+
+void
+gstspu_pgs_handle_new_buf (GstDVDSpu * dvdspu, GstClockTime event_ts,
+ GstBuffer * buf)
+{
+ SpuState *state = &dvdspu->spu_state;
+
+ state->next_ts = event_ts;
+ state->pgs.pending_cmd = buf;
+}
+
+gboolean
+gstspu_pgs_execute_event (GstDVDSpu * dvdspu)
+{
+ SpuState *state = &dvdspu->spu_state;
+
+ if (state->pgs.pending_cmd) {
+ gstspu_exec_pgs_buffer (dvdspu, state->pgs.pending_cmd);
+ gst_buffer_unref (state->pgs.pending_cmd);
+ state->pgs.pending_cmd = NULL;
+ }
+
+ state->next_ts = GST_CLOCK_TIME_NONE;
+
+ state->flags &= ~SPU_STATE_DISPLAY;
+ if (state->pgs.have_presentation_segment) {
+ if (state->pgs.pres_seg.objects && state->pgs.pres_seg.objects->len > 0)
+ state->flags |= SPU_STATE_DISPLAY;
+ }
+ return FALSE;
+}
+
+void
+gstspu_pgs_render (GstDVDSpu * dvdspu, GstBuffer * buf)
+{
+ SpuState *state = &dvdspu->spu_state;
+ PgsPresentationSegment *ps = &state->pgs.pres_seg;
+ guint i;
+
+ if (ps->objects == NULL)
+ return;
+
+ for (i = 0; i < ps->objects->len; i++) {
+ PgsCompositionObject *cur =
+ &g_array_index (ps->objects, PgsCompositionObject, i);
+ pgs_composition_object_render (cur, state, buf);
+ }
+}
+
+gboolean
+gstspu_pgs_handle_dvd_event (GstDVDSpu * dvdspu, GstEvent * event)
+{
+ return FALSE;
+}
+
+void
+gstspu_pgs_flush (GstDVDSpu * dvdspu)
+{
+ SpuPgsState *pgs_state = &dvdspu->spu_state.pgs;
+
+ if (pgs_state->pending_cmd) {
+ gst_buffer_unref (pgs_state->pending_cmd);
+ pgs_state->pending_cmd = NULL;
+ }
+
+ pgs_state->have_presentation_segment = FALSE;
+ pgs_state->in_presentation_segment = FALSE;
+ pgs_presentation_segment_set_object_count (&pgs_state->pres_seg, 0);
+
+ pgs_state->win_x = pgs_state->win_y = pgs_state->win_w = pgs_state->win_h = 0;
+}