summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--gst/dvdspu/Makefile.am4
-rw-r--r--gst/dvdspu/gstdvdspu.c216
-rw-r--r--gst/dvdspu/gstdvdspu.h8
-rw-r--r--gst/dvdspu/gstspu-pgs.c434
-rw-r--r--gst/dvdspu/gstspu-pgs.h60
5 files changed, 648 insertions, 74 deletions
diff --git a/gst/dvdspu/Makefile.am b/gst/dvdspu/Makefile.am
index 4d75f76c..2edd6e5e 100644
--- a/gst/dvdspu/Makefile.am
+++ b/gst/dvdspu/Makefile.am
@@ -1,13 +1,13 @@
plugin_LTLIBRARIES = libgstdvdspu.la
-libgstdvdspu_la_SOURCES = gstdvdspu.c gstdvdspu-render.c
+libgstdvdspu_la_SOURCES = gstdvdspu.c gstdvdspu-render.c gstspu-pgs.c
libgstdvdspu_la_CFLAGS = $(GST_CFLAGS)
libgstdvdspu_la_LIBADD = $(GST_LIBS)
libgstdvdspu_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS)
libgstdvdspu_la_LIBTOOLFLAGS = --tag=disable-static
-noinst_HEADERS = gstdvdspu.h
+noinst_HEADERS = gstdvdspu.h gstspu-pgs.h
EXTRA_DIST = Notes.txt
diff --git a/gst/dvdspu/gstdvdspu.c b/gst/dvdspu/gstdvdspu.c
index 45e64cd0..b340f92f 100644
--- a/gst/dvdspu/gstdvdspu.c
+++ b/gst/dvdspu/gstdvdspu.c
@@ -32,11 +32,14 @@
# include <config.h>
#endif
+#include <gst/gst-i18n-plugin.h>
+
#include <string.h>
#include <gst/gst.h>
#include "gstdvdspu.h"
+#include "gstspu-pgs.h"
#define DUMP_DCSQ 0
@@ -73,10 +76,10 @@ static GstStaticPadTemplate src_factory = GST_STATIC_PAD_TEMPLATE ("src",
);
static GstStaticPadTemplate subpic_sink_factory =
-GST_STATIC_PAD_TEMPLATE ("subpicture",
+ GST_STATIC_PAD_TEMPLATE ("subpicture",
GST_PAD_SINK,
GST_PAD_ALWAYS,
- GST_STATIC_CAPS ("video/x-dvd-subpicture")
+ GST_STATIC_CAPS ("video/x-dvd-subpicture; subpicture/x-pgs")
);
static const guint32 default_clut[16] = {
@@ -106,6 +109,7 @@ static void gst_dvd_spu_redraw_still (GstDVDSpu * dvdspu, gboolean force);
static void gst_dvd_spu_check_still_updates (GstDVDSpu * dvdspu);
static GstFlowReturn gst_dvd_spu_subpic_chain (GstPad * pad, GstBuffer * buf);
static gboolean gst_dvd_spu_subpic_event (GstPad * pad, GstEvent * event);
+static gboolean gst_dvd_spu_subpic_set_caps (GstPad * pad, GstCaps * caps);
static void gst_dvd_spu_clear (GstDVDSpu * dvdspu);
static void gst_dvd_spu_flush_spu_info (GstDVDSpu * dvdspu,
@@ -173,7 +177,8 @@ gst_dvd_spu_init (GstDVDSpu * dvdspu, GstDVDSpuClass * gclass)
gst_pad_new_from_static_template (&subpic_sink_factory, "subpicture");
gst_pad_set_chain_function (dvdspu->subpic_sinkpad, gst_dvd_spu_subpic_chain);
gst_pad_set_event_function (dvdspu->subpic_sinkpad, gst_dvd_spu_subpic_event);
- gst_pad_use_fixed_caps (dvdspu->subpic_sinkpad);
+ gst_pad_set_setcaps_function (dvdspu->subpic_sinkpad,
+ gst_dvd_spu_subpic_set_caps);
gst_element_add_pad (GST_ELEMENT (dvdspu), dvdspu->videosinkpad);
gst_element_add_pad (GST_ELEMENT (dvdspu), dvdspu->subpic_sinkpad);
@@ -1192,10 +1197,66 @@ gst_dvd_spu_check_still_updates (GstDVDSpu * dvdspu)
}
}
+static void
+submit_new_spu_packet (GstDVDSpu * dvdspu, GstBuffer * buf)
+{
+ SpuPacket *spu_packet;
+ GstClockTime ts;
+ GstClockTime run_ts = GST_CLOCK_TIME_NONE;
+
+ GST_DEBUG_OBJECT (dvdspu,
+ "Complete subpicture buffer of %u bytes with TS %" GST_TIME_FORMAT,
+ GST_BUFFER_SIZE (buf), GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buf)));
+
+ /* Decide whether to pass this buffer through to the rendering code */
+ ts = GST_BUFFER_TIMESTAMP (buf);
+ if (GST_CLOCK_TIME_IS_VALID (ts)) {
+ if (ts < (GstClockTime) dvdspu->subp_seg.start) {
+ GstClockTimeDiff diff = dvdspu->subp_seg.start - ts;
+
+ /* Buffer starts before segment, see if we can calculate a running time */
+ run_ts =
+ gst_segment_to_running_time (&dvdspu->subp_seg, GST_FORMAT_TIME,
+ dvdspu->subp_seg.start);
+ if (run_ts >= (GstClockTime) diff)
+ run_ts -= diff;
+ else
+ run_ts = GST_CLOCK_TIME_NONE; /* No running time possible for this subpic */
+ } else {
+ /* TS within segment, convert to running time */
+ run_ts =
+ gst_segment_to_running_time (&dvdspu->subp_seg, GST_FORMAT_TIME, ts);
+ }
+ }
+
+ if (GST_CLOCK_TIME_IS_VALID (run_ts)) {
+ /* Complete SPU packet, push it onto the queue for processing when
+ * video packets come past */
+ spu_packet = g_new0 (SpuPacket, 1);
+ spu_packet->buf = buf;
+
+ /* Store the activation time of this buffer in running time */
+ spu_packet->event_ts = run_ts;
+ GST_INFO_OBJECT (dvdspu,
+ "Pushing SPU buf with TS %" GST_TIME_FORMAT " running time %"
+ GST_TIME_FORMAT, GST_TIME_ARGS (ts),
+ GST_TIME_ARGS (spu_packet->event_ts));
+
+ g_queue_push_tail (dvdspu->pending_spus, spu_packet);
+
+ /* In a still frame condition, advance the SPU to make sure the state is
+ * up to date */
+ gst_dvd_spu_check_still_updates (dvdspu);
+ } else {
+ gst_buffer_unref (buf);
+ }
+}
+
static GstFlowReturn
gst_dvd_spu_subpic_chain (GstPad * pad, GstBuffer * buf)
{
GstDVDSpu *dvdspu = (GstDVDSpu *) (gst_object_get_parent (GST_OBJECT (pad)));
+ GstFlowReturn ret = GST_FLOW_OK;
g_return_val_if_fail (dvdspu != NULL, GST_FLOW_ERROR);
@@ -1210,6 +1271,11 @@ gst_dvd_spu_subpic_chain (GstPad * pad, GstBuffer * buf)
GST_BUFFER_TIMESTAMP (buf));
}
+ if (GST_BUFFER_IS_DISCONT (buf) && dvdspu->partial_spu) {
+ gst_buffer_unref (dvdspu->partial_spu);
+ dvdspu->partial_spu = NULL;
+ }
+
if (dvdspu->partial_spu != NULL) {
dvdspu->partial_spu = gst_buffer_join (dvdspu->partial_spu, buf);
} else {
@@ -1221,85 +1287,55 @@ gst_dvd_spu_subpic_chain (GstPad * pad, GstBuffer * buf)
gst_buffer_unref (buf);
}
- if (dvdspu->partial_spu != NULL && GST_BUFFER_SIZE (dvdspu->partial_spu) > 4) {
- guint16 packet_size;
- guint8 *data;
+ if (dvdspu->partial_spu == NULL)
+ goto done;
- data = GST_BUFFER_DATA (dvdspu->partial_spu);
- packet_size = GST_READ_UINT16_BE (data);
-
- if (packet_size == GST_BUFFER_SIZE (dvdspu->partial_spu)) {
- SpuPacket *spu_packet;
- GstClockTime ts;
- GstClockTime run_ts = GST_CLOCK_TIME_NONE;
-
- GST_DEBUG_OBJECT (dvdspu,
- "Complete subpicture buffer of %u bytes with TS %" GST_TIME_FORMAT,
- GST_BUFFER_SIZE (dvdspu->partial_spu),
- GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (dvdspu->partial_spu)));
-
- /* Decide whether to pass this buffer through to the rendering code */
- ts = GST_BUFFER_TIMESTAMP (dvdspu->partial_spu);
- if (GST_CLOCK_TIME_IS_VALID (ts)) {
- if (ts < (GstClockTime) dvdspu->subp_seg.start) {
- GstClockTimeDiff diff = dvdspu->subp_seg.start - ts;
-
- /* Buffer starts before segment, see if we can calculate a running time */
- run_ts =
- gst_segment_to_running_time (&dvdspu->subp_seg, GST_FORMAT_TIME,
- dvdspu->subp_seg.start);
- if (run_ts >= (GstClockTime) diff)
- run_ts -= diff;
- else
- run_ts = GST_CLOCK_TIME_NONE; /* No running time possible for this subpic */
+ switch (dvdspu->spu_input_type) {
+ case SPU_INPUT_TYPE_VOBSUB:
+ if (GST_BUFFER_SIZE (dvdspu->partial_spu) > 4) {
+ guint16 packet_size;
+ guint8 *data;
+
+ data = GST_BUFFER_DATA (dvdspu->partial_spu);
+ packet_size = GST_READ_UINT16_BE (data);
+
+ if (packet_size == GST_BUFFER_SIZE (dvdspu->partial_spu)) {
+ submit_new_spu_packet (dvdspu, dvdspu->partial_spu);
+ dvdspu->partial_spu = NULL;
+ } else if (packet_size < GST_BUFFER_SIZE (dvdspu->partial_spu)) {
+ /* Somehow we collected too much - something is wrong. Drop the
+ * packet entirely and wait for a new one */
+ GST_DEBUG_OBJECT (dvdspu, "Discarding invalid SPU buffer of size %u",
+ GST_BUFFER_SIZE (dvdspu->partial_spu));
+
+ gst_buffer_unref (dvdspu->partial_spu);
+ dvdspu->partial_spu = NULL;
} else {
- /* TS within segment, convert to running time */
- run_ts =
- gst_segment_to_running_time (&dvdspu->subp_seg, GST_FORMAT_TIME,
- ts);
+ GST_LOG_OBJECT (dvdspu,
+ "SPU buffer claims to be of size %u. Collected %u so far.",
+ packet_size, GST_BUFFER_SIZE (dvdspu->partial_spu));
}
}
-
- if (GST_CLOCK_TIME_IS_VALID (run_ts)) {
- /* Complete SPU packet, push it onto the queue for processing when
- * video packets come past */
- spu_packet = g_new0 (SpuPacket, 1);
- spu_packet->buf = dvdspu->partial_spu;
-
- /* Store the activation time of this buffer in running time */
- spu_packet->event_ts =
- gst_segment_to_running_time (&dvdspu->subp_seg, GST_FORMAT_TIME,
- ts);
- GST_INFO_OBJECT (dvdspu,
- "Pushing SPU buf with TS %" GST_TIME_FORMAT " running time %"
- GST_TIME_FORMAT, GST_TIME_ARGS (ts),
- GST_TIME_ARGS (spu_packet->event_ts));
-
- g_queue_push_tail (dvdspu->pending_spus, spu_packet);
- dvdspu->partial_spu = NULL;
-
- /* In a still frame condition, advance the SPU to make sure the state is
- * up to date */
- gst_dvd_spu_check_still_updates (dvdspu);
- } else {
- gst_buffer_unref (dvdspu->partial_spu);
- dvdspu->partial_spu = NULL;
- }
- } else if (packet_size < GST_BUFFER_SIZE (dvdspu->partial_spu)) {
- /* Somehow we collected too much - something is wrong. Drop the
- * packet entirely and wait for a new one */
- GST_DEBUG_OBJECT (dvdspu, "Discarding invalid SPU buffer of size %u",
- GST_BUFFER_SIZE (dvdspu->partial_spu));
-
+ break;
+ case SPU_INPUT_TYPE_PGS:
+ gstspu_dump_pgs_buffer (dvdspu->partial_spu);
gst_buffer_unref (dvdspu->partial_spu);
dvdspu->partial_spu = NULL;
- }
+ break;
+ default:
+ GST_ERROR_OBJECT (dvdspu, "Input type not configured before SPU passing");
+ goto caps_not_set;
}
+done:
DVD_SPU_UNLOCK (dvdspu);
-
gst_object_unref (dvdspu);
- return GST_FLOW_OK;
+ return ret;
+caps_not_set:
+ GST_ELEMENT_ERROR (dvdspu, RESOURCE, NO_SPACE_LEFT,
+ (_("Subpicture format was not configured before data flow")), (NULL));
+ ret = GST_FLOW_ERROR;
+ goto done;
}
static gboolean
@@ -1388,8 +1424,11 @@ gst_dvd_spu_subpic_event (GstPad * pad, GstEvent * event)
GST_TIME_ARGS (stop), GST_TIME_ARGS (time));
DVD_SPU_LOCK (dvdspu);
+
gst_segment_set_newsegment_full (&dvdspu->subp_seg, update, rate, arate,
format, start, stop, time);
+ GST_LOG_OBJECT (dvdspu, "Subpicture segment now: %" GST_SEGMENT_FORMAT,
+ &dvdspu->subp_seg);
DVD_SPU_UNLOCK (dvdspu);
gst_event_unref (event);
@@ -1424,6 +1463,39 @@ done:
return res;
}
+static gboolean
+gst_dvd_spu_subpic_set_caps (GstPad * pad, GstCaps * caps)
+{
+ GstDVDSpu *dvdspu = GST_DVD_SPU (gst_pad_get_parent (pad));
+ gboolean res = FALSE;
+ GstStructure *s;
+ SpuInputType input_type;
+
+ s = gst_caps_get_structure (caps, 0);
+
+ if (gst_structure_has_name (s, "video/x-dvd-subpicture")) {
+ input_type = SPU_INPUT_TYPE_VOBSUB;
+ } else if (gst_structure_has_name (s, "subpicture/x-pgs")) {
+ input_type = SPU_INPUT_TYPE_PGS;
+ } else {
+ goto done;
+ }
+
+ DVD_SPU_LOCK (dvdspu);
+ if (dvdspu->spu_input_type != input_type) {
+ GST_INFO_OBJECT (dvdspu, "Incoming SPU packet type changed to %u",
+ input_type);
+ gst_dvd_spu_flush_spu_info (dvdspu, TRUE);
+ dvdspu->spu_input_type = input_type;
+ }
+
+ DVD_SPU_UNLOCK (dvdspu);
+ res = TRUE;
+done:
+ gst_object_unref (dvdspu);
+ return res;
+}
+
static GstStateChangeReturn
gst_dvd_spu_change_state (GstElement * element, GstStateChange transition)
{
diff --git a/gst/dvdspu/gstdvdspu.h b/gst/dvdspu/gstdvdspu.h
index dfc51f9e..1bbc7d3a 100644
--- a/gst/dvdspu/gstdvdspu.h
+++ b/gst/dvdspu/gstdvdspu.h
@@ -45,6 +45,7 @@ typedef struct SpuPixCtrlI SpuPixCtrlI;
typedef struct SpuLineCtrlI SpuLineCtrlI;
typedef struct SpuColour SpuColour;
typedef enum SpuStateFlags SpuStateFlags;
+typedef enum SpuInputType SpuInputType;
typedef struct SpuState SpuState;
typedef struct SpuPacket SpuPacket;
typedef enum SpuCmd SpuCmd;
@@ -106,6 +107,12 @@ enum SpuStateFlags {
SPU_STATE_FORCED_ONLY = 0x100
};
+enum SpuInputType {
+ SPU_INPUT_TYPE_NONE = 0x00,
+ SPU_INPUT_TYPE_VOBSUB = 0x01,
+ SPU_INPUT_TYPE_PGS = 0x02
+};
+
#define SPU_STATE_FLAGS_MASK (0xff)
struct SpuState {
@@ -198,6 +205,7 @@ struct _GstDVDSpu {
GstSegment subp_seg;
SpuState spu_state;
+ SpuInputType spu_input_type;
/* GQueue of SpuBuf structures */
GQueue *pending_spus;
diff --git a/gst/dvdspu/gstspu-pgs.c b/gst/dvdspu/gstspu-pgs.c
new file mode 100644
index 00000000..de6a4151
--- /dev/null
+++ b/gst/dvdspu/gstspu-pgs.c
@@ -0,0 +1,434 @@
+#include <fcntl.h>
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+
+#include <glib.h>
+#include <gst/gst.h>
+
+#include "gstspu-pgs.h"
+
+const struct PgsFrameRateEntry
+{
+ guint8 id;
+ guint fps_n;
+ guint fps_d;
+} PgsFrameRates[] = {
+ {
+ 64, 30000, 1001} /* 29.97 FPS */
+};
+
+gboolean in_presentation_segment = FALSE;
+guint8 *rle_data = NULL;
+guint32 rle_data_size = 0, rle_data_used = 0;
+PgsPaletteEntry palette[256];
+
+static void
+dump_bytes (guint8 * data, guint16 len)
+{
+ gint i;
+
+ /* Dump the numbers */
+ for (i = 0; i < len; i++) {
+ g_print ("0x%02x ", data[i]);
+ if (!((i + 1) % 16))
+ g_print ("\n");
+ }
+ if (len > 0 && (i % 16))
+ g_print ("\n");
+}
+
+static void
+dump_rle_data (guint8 * data, guint32 len)
+{
+ guint8 *end = data + len;
+ guint16 obj_w, obj_h;
+ gint i;
+ 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;
+ g_print ("RLE image is %ux%u\n", obj_w, obj_h);
+
+ while (data < end) {
+ guint8 pal_id;
+ guint16 run_len;
+
+ if (data[0] != 0) {
+ // g_print ("data 0x%02x\n", data[0]);
+ pal_id = *data++;
+ run_len = 1;
+ } else {
+ data++;
+
+ if (data + 1 > end)
+ return;
+ switch (data[0] & 0xC0) {
+ case 0x00:
+ //g_print ("data 0x%02x\n", data[0]);
+ run_len = (data[0] & 0x3f);
+ if (run_len > 0)
+ pal_id = 0;
+ data++;
+ break;
+ case 0x40:
+ if (data + 2 > end)
+ return;
+ //g_print ("data 0x%02x 0x%02x\n", data[0], data[1]);
+ run_len = ((data[0] << 8) | data[1]) & 0x3fff;
+ if (run_len > 0)
+ pal_id = 0;
+ data += 2;
+ break;
+ case 0x80:
+ if (data + 2 > end)
+ return;
+ //g_print ("data 0x%02x 0x%02x\n", data[0], data[1]);
+ run_len = (data[0] & 0x3f);
+ pal_id = data[1];
+ data += 2;
+ break;
+ case 0xC0:
+ if (data + 3 > end)
+ return;
+ //g_print ("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 1
+ if (palette[pal_id].A) {
+ for (i = 0; i < run_len; i++)
+ g_print ("%02x ", pal_id);
+ } else {
+ for (i = 0; i < run_len; i++)
+ g_print (" ");
+ }
+ x += run_len;
+ if (!run_len || x > obj_w) {
+ g_print ("\n");
+ x = 0;
+ }
+#else
+ g_print ("Run x: %d pix: %d col: %d\n", x, run_len, pal_id);
+ x += run_len;
+ if (x >= obj_w)
+ x = 0;
+#endif
+
+ };
+
+ g_print ("\n");
+}
+
+static int
+parse_presentation_segment (guint8 type, guint8 * payload, guint16 len)
+{
+ guint8 *end = payload + len;
+ guint16 vid_w, vid_h;
+ gint8 vid_fps;
+ guint16 composition_desc_no;
+ guint8 composition_desc_state;
+ guint8 pres_seg_flags;
+ guint8 palette_id;
+ guint8 n_objects;
+ gint i;
+
+ /* Parse video descriptor */
+ if (payload + 5 > end)
+ return 0;
+ vid_w = GST_READ_UINT16_BE (payload);
+ vid_h = GST_READ_UINT16_BE (payload + 2);
+ vid_fps = payload[4];
+ payload += 5;
+
+ /* Parse composition descriptor */
+ if (payload + 3 > end)
+ return 0;
+ composition_desc_no = GST_READ_UINT16_BE (payload);
+ composition_desc_state = payload[2];
+ payload += 3;
+
+ /* Parse other bits */
+ if (payload + 3 > end)
+ return 0;
+
+ pres_seg_flags = payload[0];
+ palette_id = payload[1];
+ n_objects = payload[2];
+ payload += 3;
+
+ g_print ("Video width %u height %u fps code %u\n", vid_w, vid_h, vid_fps);
+ g_print
+ ("Composition num %u state %u flags 0x%02x palette id %u n_objects %u\n",
+ composition_desc_no, composition_desc_state, pres_seg_flags, palette_id,
+ n_objects);
+
+ for (i = 0; i < (gint) n_objects; i++) {
+ guint16 obj_id;
+ guint8 win_id;
+ guint8 obj_flags;
+ guint16 x, y;
+
+ if (payload + 8 > end)
+ break;
+ obj_id = GST_READ_UINT16_BE (payload);
+ win_id = payload[2];
+ obj_flags = payload[3];
+ x = GST_READ_UINT16_BE (payload + 4);
+ y = GST_READ_UINT16_BE (payload + 6);
+ payload += 8;
+
+ g_print ("Composition object %d Object ID %u Window ID %u flags 0x%02x "
+ "x %u y %u\n", i, obj_id, win_id, obj_flags, x, y);
+
+ if (obj_flags & PGS_COMP_OBJECT_FLAG_CROPPED) {
+ guint16 crop_x, crop_y, crop_w, crop_h;
+ if (payload + 8 > end)
+ break;
+
+ crop_x = GST_READ_UINT16_BE (payload);
+ crop_y = GST_READ_UINT16_BE (payload + 2);
+ crop_w = GST_READ_UINT16_BE (payload + 4);
+ crop_h = GST_READ_UINT16_BE (payload + 6);
+ payload += 8;
+
+ g_print ("Cropping window x %u y %u w %u h %u\n",
+ crop_x, crop_y, crop_w, crop_h);
+ }
+ }
+
+ if (payload != end) {
+ g_print ("%u bytes left over:\n", end - payload);
+ dump_bytes (payload, end - payload);
+ }
+
+ return 0;
+}
+
+static int
+parse_set_palette (guint8 type, guint8 * payload, guint16 len)
+{
+ 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;
+
+ g_print ("Palette ID %u version %u. %d entries\n",
+ palette_id, palette_version, n_entries);
+ for (i = 0; i < n_entries; i++) {
+ guint8 n, Y, Cb, Cr, A;
+ n = payload[0];
+ palette[n].n = n;
+ palette[n].Y = Y = payload[1];
+ palette[n].Cb = Cb = payload[2];
+ palette[n].Cr = Cr = payload[3];
+ palette[n].A = A = payload[4];
+
+ g_print ("Entry %3d: Y %3d Cb %3d Cr %3d A %3d ", n, Y, Cb, Cr, A);
+ if (((i + 1) % 2) == 0)
+ g_print ("\n");
+
+ payload += PGS_PALETTE_ENTRY_SIZE;
+ }
+ for (i = n_entries; i < 256; i++) {
+ palette[i].n = i;
+ palette[i].A = 0;
+ }
+
+ if (n_entries > 0 && (i % 2))
+ g_print ("\n");
+
+ if (payload != end) {
+ g_print ("%u bytes left over:\n", end - payload);
+ dump_bytes (payload, end - payload);
+ }
+
+ return 0;
+}
+
+static int
+parse_set_window (guint8 type, guint8 * payload, guint16 len)
+{
+ guint8 *end = payload + len;
+ guint8 win_id, win_ver;
+ guint16 x, y, w, h;
+
+ 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];
+ x = GST_READ_UINT16_BE (payload + 2);
+ y = GST_READ_UINT16_BE (payload + 4);
+ w = GST_READ_UINT16_BE (payload + 6);
+ h = GST_READ_UINT16_BE (payload + 8);
+ payload += 10;
+
+ g_print ("Win ID %u version %d x %d y %d w %d h %d\n",
+ win_id, win_ver, x, y, w, h);
+
+ if (payload != end) {
+ g_print ("%u bytes left over:\n", end - payload);
+ dump_bytes (payload, end - payload);
+ }
+
+ return 0;
+}
+
+static int
+parse_set_object_data (guint8 type, guint8 * payload, guint16 len)
+{
+ guint8 *end = payload + len;
+ guint16 obj_id;
+ guint8 obj_ver, obj_flags;
+
+ if (payload + 4 > end)
+ return 0;
+ obj_id = GST_READ_UINT16_BE (payload);
+ obj_ver = payload[2];
+ obj_flags = payload[3];
+ payload += 4;
+
+ g_print ("Object ID %d ver %u flags 0x%02x\n", obj_id, obj_ver, obj_flags);
+
+ if (obj_flags & PGS_OBJECT_UPDATE_FLAG_START_RLE) {
+
+ if (payload + 3 > end)
+ return 0;
+
+ rle_data_size = GST_READ_UINT24_BE (payload);
+ payload += 3;
+
+ g_print ("%d bytes of RLE data, of %d bytes total.\n",
+ end - payload, rle_data_size);
+
+ rle_data = g_realloc (rle_data, rle_data_size);
+ rle_data_used = end - payload;
+ memcpy (rle_data, payload, end - payload);
+ payload = end;
+ } else {
+ g_print ("%d bytes of additional RLE data\n", end - payload);
+ if (rle_data_size < rle_data_used + end - payload)
+ return 0;
+
+ memcpy (rle_data + rle_data_used, payload, end - payload);
+ rle_data_used += end - payload;
+ payload = end;
+ }
+
+ if (rle_data_size == rle_data_used)
+ dump_rle_data (rle_data, rle_data_size);
+
+ if (payload != end) {
+ g_print ("%u bytes left over:\n", end - payload);
+ dump_bytes (payload, end - payload);
+ }
+
+ return 0;
+}
+
+static int
+parse_pgs_packet (guint8 type, guint8 * payload, guint16 len)
+{
+ int ret = 0;
+
+ if (!in_presentation_segment && type != PGS_COMMAND_PRESENTATION_SEGMENT) {
+ g_print ("Expected BEGIN PRESENTATION SEGMENT command. "
+ "Got command type 0x%02x len %u. Skipping\n", type, len);
+ return 0;
+ }
+
+ switch (type) {
+ case PGS_COMMAND_PRESENTATION_SEGMENT:
+ g_print ("*******************************************\n"
+ "Begin PRESENTATION_SEGMENT (0x%02x) packet len %u\n", type, len);
+ in_presentation_segment = TRUE;
+ ret = parse_presentation_segment (type, payload, len);
+ break;
+ case PGS_COMMAND_SET_OBJECT_DATA:
+ g_print ("*** Set Object Data (0x%02x) packet len %u\n", type, len);
+ ret = parse_set_object_data (type, payload, len);
+ break;
+ case PGS_COMMAND_SET_PALETTE:
+ g_print ("*** Set Palette (0x%02x) packet len %u\n", type, len);
+ ret = parse_set_palette (type, payload, len);
+ break;
+ case PGS_COMMAND_SET_WINDOW:
+ g_print ("*** Set Window command (0x%02x) packet len %u\n", type, len);
+ ret = parse_set_window (type, payload, len);
+ break;
+ case PGS_COMMAND_INTERACTIVE_SEGMENT:
+ g_print ("*** Interactive Segment command(0x%02x) packet len %u\n",
+ type, len);
+ dump_bytes (payload, len);
+ break;
+ case PGS_COMMAND_END_DISPLAY:
+ g_print ("*** End Display command (0x%02x) packet len %u\n", type, len);
+ in_presentation_segment = FALSE;
+ break;
+ default:
+ g_print ("*** Unknown command: type 0x%02x len %u. Skipping\n", type,
+ len);
+ break;
+ }
+ g_print ("\n");
+
+ return ret;
+}
+
+gint
+gstspu_dump_pgs_buffer (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) {
+ g_print ("Not enough bytes to be a PGS packet\n");
+ return -1;
+ }
+
+ do {
+ type = *pos++;
+ packet_len = GST_READ_UINT16_BE (pos);
+ pos += 2;
+
+ if (pos + packet_len > end) {
+ g_print ("Invalid packet length %u (only have %u bytes)\n", packet_len,
+ end - pos);
+ return -1;
+ }
+
+ if (parse_pgs_packet (type, pos, packet_len))
+ return -1;
+
+ pos += packet_len;
+ } while (pos + 3 <= end);
+
+ return (pos - GST_BUFFER_DATA (buf));
+}
diff --git a/gst/dvdspu/gstspu-pgs.h b/gst/dvdspu/gstspu-pgs.h
new file mode 100644
index 00000000..db943071
--- /dev/null
+++ b/gst/dvdspu/gstspu-pgs.h
@@ -0,0 +1,60 @@
+/* GStreamer Sub-Picture Unit - PGS handling
+ * Copyright (C) 2009 Jan Schmidt <thaytan@noraisin.net>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __GSTSPU_PGS_H__
+#define __GSTSPU_PGS_H__
+
+typedef enum PgsCommandType {
+ PGS_COMMAND_SET_PALETTE = 0x14,
+ PGS_COMMAND_SET_OBJECT_DATA = 0x15,
+ PGS_COMMAND_PRESENTATION_SEGMENT = 0x16,
+ PGS_COMMAND_SET_WINDOW = 0x17,
+ PGS_COMMAND_INTERACTIVE_SEGMENT = 0x18,
+
+ PGS_COMMAND_END_DISPLAY = 0x80,
+
+ PGS_COMMAND_INVALID = 0xFFFF
+} PgsCommandType;
+
+typedef enum PgsPresSegmentFlags {
+ PGS_PRES_SEGMENT_FLAG_UPDATE_PALETTE = 0x80
+} PgsPresSegmentFlags;
+
+typedef enum PgsCompObjectFlags {
+ PGS_COMP_OBJECT_FLAG_CROPPED = 0x80,
+ PGS_COMP_OBJECT_FLAG_FORCED = 0x40
+} PgsCompObjectFlags;
+
+typedef enum PgsObjectUpdateFlags {
+ /* Set in an object_update if this is the beginning of new RLE data.
+ * If not set, the data is a continuation to be appended */
+ PGS_OBJECT_UPDATE_FLAG_START_RLE = 0x80
+} PgsObjectUpdateFlags;
+
+typedef struct PgsPaletteEntry {
+ guint8 n;
+ guint8 Y;
+ guint8 Cb;
+ guint8 Cr;
+ guint8 A;
+} PgsPaletteEntry;
+
+gint gstspu_dump_pgs_buffer (GstBuffer *buf);
+
+#endif