From ad556efb2a866d02196bd719b657dbca0c7f2529 Mon Sep 17 00:00:00 2001 From: Thomas Vander Stichele Date: Fri, 2 Jun 2006 10:28:32 +0000 Subject: [PATCH] gst/gdp/gstgdppay.c: add crc-header and crc-payload properties don't error out on some things that are recoverable Original commit message from CVS: * gst/gdp/gstgdppay.c: (gst_gdp_pay_class_init), (gst_gdp_pay_init), (gst_gdp_buffer_from_caps), (gst_gdp_pay_buffer_from_buffer), (gst_gdp_buffer_from_event), (gst_gdp_pay_reset_streamheader), (gst_gdp_pay_chain), (gst_gdp_pay_sink_event), (gst_gdp_pay_set_property), (gst_gdp_pay_get_property): add crc-header and crc-payload properties don't error out on some things that are recoverable * tests/check/elements/gdppay.c: (GST_START_TEST), (gdppay_suite): add test for crc --- gst/gdp/gstgdppay.c | 123 ++++++++++++++++++++++++++++------ tests/check/elements/gdppay.c | 92 ++++++++++++++++++++++++- 2 files changed, 195 insertions(+), 20 deletions(-) diff --git a/gst/gdp/gstgdppay.c b/gst/gdp/gstgdppay.c index f498477481..48c2a5ef6f 100644 --- a/gst/gdp/gstgdppay.c +++ b/gst/gdp/gstgdppay.c @@ -19,6 +19,7 @@ /** * SECTION:element-gdppay + * @see_also: gdpdepay * * * @@ -43,12 +44,6 @@ GST_ELEMENT_DETAILS ("GDP Payloader", "Payloads GStreamer Data Protocol buffers", "Thomas Vander Stichele "); -enum -{ - PROP_0, - /* FILL ME */ -}; - static GstStaticPadTemplate gdp_pay_sink_template = GST_STATIC_PAD_TEMPLATE ("sink", GST_PAD_SINK, @@ -64,6 +59,16 @@ GST_STATIC_PAD_TEMPLATE ("src", GST_DEBUG_CATEGORY (gst_gdp_pay_debug); #define GST_CAT_DEFAULT gst_gdp_pay_debug +#define DEFAULT_CRC_HEADER TRUE +#define DEFAULT_CRC_PAYLOAD FALSE + +enum +{ + PROP_0, + PROP_CRC_HEADER, + PROP_CRC_PAYLOAD, +}; + #define _do_init(x) \ GST_DEBUG_CATEGORY_INIT (gst_gdp_pay_debug, "gdppay", 0, \ "GDP payloader"); @@ -76,6 +81,11 @@ static gboolean gst_gdp_pay_sink_event (GstPad * pad, GstEvent * event); static GstStateChangeReturn gst_gdp_pay_change_state (GstElement * element, GstStateChange transition); +static void gst_gdp_pay_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec); +static void gst_gdp_pay_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * pspec); + static void gst_gdp_pay_dispose (GObject * gobject); static void @@ -102,8 +112,19 @@ gst_gdp_pay_class_init (GstGDPPayClass * klass) parent_class = g_type_class_peek_parent (klass); + gobject_class->set_property = GST_DEBUG_FUNCPTR (gst_gdp_pay_set_property); + gobject_class->get_property = GST_DEBUG_FUNCPTR (gst_gdp_pay_get_property); gobject_class->dispose = GST_DEBUG_FUNCPTR (gst_gdp_pay_dispose); gstelement_class->change_state = GST_DEBUG_FUNCPTR (gst_gdp_pay_change_state); + + g_object_class_install_property (gobject_class, PROP_CRC_HEADER, + g_param_spec_boolean ("crc-header", "CRC Header", + "Calculate and store a CRC checksum on the header", + DEFAULT_CRC_HEADER, G_PARAM_READWRITE)); + g_object_class_install_property (gobject_class, PROP_CRC_PAYLOAD, + g_param_spec_boolean ("crc-payload", "CRC Payload", + "Calculate and store a CRC checksum on the payload", + DEFAULT_CRC_PAYLOAD, G_PARAM_READWRITE)); } static void @@ -122,6 +143,10 @@ gst_gdp_pay_init (GstGDPPay * gdppay, GstGDPPayClass * g_class) gst_element_add_pad (GST_ELEMENT (gdppay), gdppay->srcpad); gdppay->offset = 0; + + gdppay->crc_header = DEFAULT_CRC_HEADER; + gdppay->crc_payload = DEFAULT_CRC_PAYLOAD; + gdppay->header_flag = gdppay->crc_header | gdppay->crc_payload; } static void @@ -157,7 +182,8 @@ gst_gdp_buffer_from_caps (GstGDPPay * this, GstCaps * caps) guint8 *header, *payload; guint len; - if (!gst_dp_packet_from_caps (caps, 0, &len, &header, &payload)) { + if (!gst_dp_packet_from_caps (caps, this->header_flag, &len, &header, + &payload)) { GST_WARNING_OBJECT (this, "could not create GDP header from caps"); return NULL; } @@ -182,7 +208,7 @@ gst_gdp_pay_buffer_from_buffer (GstGDPPay * this, GstBuffer * buffer) guint8 *header; guint len; - if (!gst_dp_header_from_buffer (buffer, 0, &len, &header)) { + if (!gst_dp_header_from_buffer (buffer, this->header_flag, &len, &header)) { GST_WARNING_OBJECT (this, "could not create GDP header from buffer"); return NULL; } @@ -205,8 +231,10 @@ gst_gdp_buffer_from_event (GstGDPPay * this, GstEvent * event) guint8 *header, *payload; guint len; - if (!gst_dp_packet_from_event (event, 0, &len, &header, &payload)) { - GST_WARNING_OBJECT (this, "could not create GDP header from event"); + if (!gst_dp_packet_from_event (event, this->header_flag, &len, &header, + &payload)) { + GST_WARNING_OBJECT (this, "could not create GDP header from event %s (%d)", + gst_event_type_get_name (event->type), event->type); return NULL; } @@ -270,7 +298,7 @@ gst_gdp_pay_reset_streamheader (GstGDPPay * this) sh = gst_structure_get_value (structure, "streamheader"); buffers = g_value_peek_pointer (sh); GST_DEBUG_OBJECT (this, - "Need to serialize %d incoming streamheader buffers on our streamheader", + "Need to serialize %d incoming streamheader buffers on ours", buffers->len); for (i = 0; i < buffers->len; ++i) { GValue *bufval; @@ -390,17 +418,23 @@ gst_gdp_pay_chain (GstPad * pad, GstBuffer * buffer) outbuffer = gst_gdp_buffer_from_event (this, event); gst_event_unref (event); + /* GDP 0.2 doesn't know about new-segment, so this is not fatal */ if (!outbuffer) { - GST_ELEMENT_ERROR (this, STREAM, ENCODE, (NULL), + GST_ELEMENT_WARNING (this, STREAM, ENCODE, (NULL), ("Could not create GDP buffer from new segment event")); +/* ret = GST_FLOW_ERROR; goto done; - } +*/ + } else { - gst_gdp_stamp_buffer (this, outbuffer); - GST_BUFFER_TIMESTAMP (outbuffer) = GST_BUFFER_TIMESTAMP (buffer); - GST_BUFFER_DURATION (outbuffer) = 0; - this->new_segment_buf = outbuffer; + gst_gdp_stamp_buffer (this, outbuffer); + GST_BUFFER_TIMESTAMP (outbuffer) = GST_BUFFER_TIMESTAMP (buffer); + GST_BUFFER_DURATION (outbuffer) = 0; + GST_DEBUG_OBJECT (this, "Storing buffer %p as new_segment_buf", + outbuffer); + this->new_segment_buf = outbuffer; + } } /* make sure we've received caps before */ @@ -464,11 +498,14 @@ gst_gdp_pay_sink_event (GstPad * pad, GstEvent * event) GstFlowReturn flowret; gboolean ret = TRUE; + GST_DEBUG_OBJECT (this, "received event %s (%d)", + gst_event_type_get_name (event->type), event->type); + /* now turn the event into a buffer */ outbuffer = gst_gdp_buffer_from_event (this, event); if (!outbuffer) { - GST_ELEMENT_ERROR (this, STREAM, ENCODE, (NULL), - ("Could not create GDP buffer from event")); + GST_ELEMENT_WARNING (this, STREAM, ENCODE, (NULL), + ("Could not create GDP buffer from received event")); ret = FALSE; goto done; } @@ -479,9 +516,11 @@ gst_gdp_pay_sink_event (GstPad * pad, GstEvent * event) /* if we got a new segment, we should put it on our streamheader, * and not send it on */ if (GST_EVENT_TYPE (event) == GST_EVENT_NEWSEGMENT) { + GST_DEBUG_OBJECT (this, "received new_segment event"); if (this->new_segment_buf) { gst_buffer_unref (this->new_segment_buf); } + GST_DEBUG_OBJECT (this, "Storing buffer %p as new_segment_buf", outbuffer); this->new_segment_buf = outbuffer; gst_gdp_pay_reset_streamheader (this); } else { @@ -506,6 +545,52 @@ done: return ret; } +static void +gst_gdp_pay_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec) +{ + GstGDPPay *this; + + g_return_if_fail (GST_IS_GDP_PAY (object)); + this = GST_GDP_PAY (object); + + switch (prop_id) { + case PROP_CRC_HEADER: + this->crc_header = g_value_get_boolean (value); + this->header_flag = this->crc_header | this->crc_payload; + break; + case PROP_CRC_PAYLOAD: + this->crc_payload = g_value_get_boolean (value); + this->header_flag = this->crc_header | this->crc_payload; + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gst_gdp_pay_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * pspec) +{ + GstGDPPay *this; + + g_return_if_fail (GST_IS_GDP_PAY (object)); + this = GST_GDP_PAY (object); + + switch (prop_id) { + case PROP_CRC_HEADER: + g_value_set_boolean (value, this->crc_header); + break; + case PROP_CRC_PAYLOAD: + g_value_set_boolean (value, this->crc_payload); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + static GstStateChangeReturn gst_gdp_pay_change_state (GstElement * element, GstStateChange transition) { diff --git a/tests/check/elements/gdppay.c b/tests/check/elements/gdppay.c index 6c11759893..8d8712dd4c 100644 --- a/tests/check/elements/gdppay.c +++ b/tests/check/elements/gdppay.c @@ -86,7 +86,6 @@ cleanup_gdppay (GstElement * gdppay) gst_check_teardown_element (gdppay); } - GST_START_TEST (test_audio) { GstCaps *caps; @@ -448,6 +447,96 @@ GST_START_TEST (test_first_no_new_segment) GST_END_TEST; +GST_START_TEST (test_crc) +{ + GstCaps *caps; + GstElement *gdppay; + GstBuffer *inbuffer, *outbuffer; + GstEvent *event; + gchar *caps_string; + gint length; + guint16 crc_calculated, crc_read; + + gdppay = setup_gdppay (); + g_object_set (gdppay, "crc-header", TRUE, NULL); + + fail_unless (gst_element_set_state (gdppay, + GST_STATE_PLAYING) == GST_STATE_CHANGE_SUCCESS, + "could not set to playing"); + + GST_DEBUG ("new segment"); + event = + gst_event_new_new_segment (FALSE, 1.0, GST_FORMAT_TIME, 0, GST_SECOND, 0); + fail_unless (gst_pad_push_event (mysrcpad, event)); + + /* no buffer should be pushed yet, waiting for caps */ + fail_unless_equals_int (g_list_length (buffers), 0); + + GST_DEBUG ("first buffer"); + inbuffer = gst_buffer_new_and_alloc (4); + caps = gst_caps_from_string (AUDIO_CAPS_STRING); + gst_buffer_set_caps (inbuffer, caps); + caps_string = gst_caps_to_string (caps); + + ASSERT_BUFFER_REFCOUNT (inbuffer, "inbuffer", 1); + + /* pushing gives away my reference */ + fail_unless (gst_pad_push (mysrcpad, inbuffer) == GST_FLOW_OK); + + /* we should have three buffers now */ + fail_unless_equals_int (g_list_length (buffers), 3); + + /* first buffer is the serialized new_segment event; + * the element also holds a ref to it */ + fail_if ((outbuffer = (GstBuffer *) buffers->data) == NULL); + buffers = g_list_remove (buffers, outbuffer); + ASSERT_BUFFER_REFCOUNT (outbuffer, "outbuffer", 2); + + /* verify the header checksum */ + /* CRC's start at 58 in the header */ + crc_calculated = gst_dp_crc (GST_BUFFER_DATA (outbuffer), 58); + crc_read = GST_READ_UINT16_BE (GST_BUFFER_DATA (outbuffer) + 58); + fail_unless_equals_int (crc_calculated, crc_read); + + /* change a byte in the header and verify that the checksum now fails */ + GST_BUFFER_DATA (outbuffer)[0] = 0xff; + crc_calculated = gst_dp_crc (GST_BUFFER_DATA (outbuffer), 58); + fail_if (crc_calculated == crc_read, + "Introducing a byte error in the header should make the checksum fail"); + + gst_buffer_unref (outbuffer); + + /* second buffer is the serialized caps; + * the element also holds a ref to it */ + fail_if ((outbuffer = (GstBuffer *) buffers->data) == NULL); + buffers = g_list_remove (buffers, outbuffer); + ASSERT_BUFFER_REFCOUNT (outbuffer, "outbuffer", 2); + length = GST_DP_HEADER_LENGTH + (strlen (caps_string) + 1); + fail_unless_equals_int (GST_BUFFER_SIZE (outbuffer), length); + gst_buffer_unref (outbuffer); + + /* the third buffer is the GDP buffer for our pushed buffer */ + fail_if ((outbuffer = (GstBuffer *) buffers->data) == NULL); + buffers = g_list_remove (buffers, outbuffer); + ASSERT_BUFFER_REFCOUNT (outbuffer, "outbuffer", 1); + length = GST_DP_HEADER_LENGTH + 4; + fail_unless_equals_int (GST_BUFFER_SIZE (outbuffer), length); + gst_buffer_unref (outbuffer); + + fail_unless (gst_element_set_state (gdppay, + GST_STATE_NULL) == GST_STATE_CHANGE_SUCCESS, "could not set to null"); + + gst_caps_unref (caps); + g_free (caps_string); + g_list_foreach (buffers, (GFunc) gst_mini_object_unref, NULL); + g_list_free (buffers); + buffers = NULL; + ASSERT_OBJECT_REFCOUNT (gdppay, "gdppay", 1); + gst_object_unref (gdppay); +} + +GST_END_TEST; + Suite * gdppay_suite (void) @@ -460,6 +549,7 @@ gdppay_suite (void) tcase_add_test (tc_chain, test_first_no_caps); tcase_add_test (tc_chain, test_first_no_new_segment); tcase_add_test (tc_chain, test_streamheader); + tcase_add_test (tc_chain, test_crc); return s; }