diff --git a/gst/dvdspu/Makefile.am b/gst/dvdspu/Makefile.am index 4d75f76c41..2edd6e5e75 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 45e64cd048..b340f92fa9 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); + switch (dvdspu->spu_input_type) { + case SPU_INPUT_TYPE_VOBSUB: + if (GST_BUFFER_SIZE (dvdspu->partial_spu) > 4) { + guint16 packet_size; + guint8 *data; - if (packet_size == GST_BUFFER_SIZE (dvdspu->partial_spu)) { - SpuPacket *spu_packet; - GstClockTime ts; - GstClockTime run_ts = GST_CLOCK_TIME_NONE; + data = GST_BUFFER_DATA (dvdspu->partial_spu); + packet_size = GST_READ_UINT16_BE (data); - 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))); + 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)); - /* 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 */ + 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 dfc51f9e93..1bbc7d3af0 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 0000000000..de6a4151e7 --- /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 0000000000..db94307108 --- /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