From 4e847cb4acd356112f56a101132d8438abe9fa03 Mon Sep 17 00:00:00 2001 From: Jan Schmidt Date: Wed, 20 May 2009 08:55:40 +0100 Subject: dvdspu: Add simple PGS handler that dumps the packet info Add setcaps logic on the subpicture sink pad for configuring which subpicture format is arriving. Add the first piece of PGS subpicture handling by dumping the stream contents out to the terminal as the packets arrive. Add some more debug. Don't calculate the running time for our subpicture packets twice, once is enough. --- gst/dvdspu/Makefile.am | 4 +- gst/dvdspu/gstdvdspu.c | 216 ++++++++++++++++-------- gst/dvdspu/gstdvdspu.h | 8 + gst/dvdspu/gstspu-pgs.c | 434 ++++++++++++++++++++++++++++++++++++++++++++++++ gst/dvdspu/gstspu-pgs.h | 60 +++++++ 5 files changed, 648 insertions(+), 74 deletions(-) create mode 100644 gst/dvdspu/gstspu-pgs.c create mode 100644 gst/dvdspu/gstspu-pgs.h (limited to 'gst/dvdspu') 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 #endif +#include + #include #include #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 +#include +#include +#include + +#include +#include + +#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 + * + * 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 -- cgit v1.2.1