static void gst_dtls_enc_get_property (GObject * object, guint prop_id, GValue * value, GParamSpec * pspec) { GstDtlsEnc *self = GST_DTLS_ENC (object); switch (prop_id) { case PROP_CONNECTION_ID: g_value_set_string (value, self->connection_id); break; case PROP_IS_CLIENT: g_value_set_boolean (value, self->is_client); break; case PROP_ENCODER_KEY: g_value_set_boxed (value, self->encoder_key); break; case PROP_SRTP_CIPHER: g_value_set_uint (value, self->srtp_cipher); break; case PROP_SRTP_AUTH: g_value_set_uint (value, self->srtp_auth); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (self, prop_id, pspec); } }
static GstFlowReturn sink_chain (GstPad * pad, GstObject * parent, GstBuffer * buffer) { GstDtlsEnc *self = GST_DTLS_ENC (parent); GstMapInfo map_info; gint ret; gst_buffer_map (buffer, &map_info, GST_MAP_READ); if (map_info.size) { ret = gst_dtls_connection_send (self->connection, map_info.data, map_info.size); if (ret != map_info.size) { GST_WARNING_OBJECT (self, "error sending data: %d B were written, expected value was %" G_GSIZE_FORMAT " B", ret, map_info.size); } } gst_buffer_unmap (buffer, &map_info); gst_buffer_unref (buffer); return GST_FLOW_OK; }
static void gst_dtls_enc_finalize (GObject * object) { GstDtlsEnc *self = GST_DTLS_ENC (object); if (self->encoder_key) { gst_buffer_unref (self->encoder_key); self->encoder_key = NULL; } if (self->connection_id) { g_free (self->connection_id); self->connection_id = NULL; } g_mutex_lock (&self->queue_lock); g_queue_foreach (&self->queue, (GFunc) gst_buffer_unref, NULL); g_queue_clear (&self->queue); g_mutex_unlock (&self->queue_lock); g_mutex_clear (&self->queue_lock); g_cond_clear (&self->queue_cond_add); GST_LOG_OBJECT (self, "finalized"); G_OBJECT_CLASS (parent_class)->finalize (object); }
static void gst_dtls_enc_set_property (GObject * object, guint prop_id, const GValue * value, GParamSpec * pspec) { GstDtlsEnc *self = GST_DTLS_ENC (object); switch (prop_id) { case PROP_CONNECTION_ID: if (self->connection_id != NULL) { g_free (self->connection_id); self->connection_id = NULL; } self->connection_id = g_value_dup_string (value); break; case PROP_IS_CLIENT: self->is_client = g_value_get_boolean (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (self, prop_id, pspec); } }
static gboolean src_activate_mode (GstPad * pad, GstObject * parent, GstPadMode mode, gboolean active) { GstDtlsEnc *self = GST_DTLS_ENC (parent); gboolean success = TRUE; g_return_val_if_fail (mode == GST_PAD_MODE_PUSH, FALSE); if (active) { GST_DEBUG_OBJECT (self, "src pad activating in push mode"); self->flushing = FALSE; self->send_initial_events = TRUE; success = gst_pad_start_task (pad, (GstTaskFunction) src_task_loop, self->src, NULL); if (!success) { GST_WARNING_OBJECT (self, "failed to activate pad task"); } } else { GST_DEBUG_OBJECT (self, "deactivating src pad"); g_mutex_lock (&self->queue_lock); g_queue_foreach (&self->queue, (GFunc) gst_buffer_unref, NULL); g_queue_clear (&self->queue); self->flushing = TRUE; g_cond_signal (&self->queue_cond_add); g_mutex_unlock (&self->queue_lock); success = gst_pad_stop_task (pad); if (!success) { GST_WARNING_OBJECT (self, "failed to deactivate pad task"); } } return success; }
static GstFlowReturn gst_dtls_enc_chain (GstDtlsBase * base, GstBuffer * buffer) { GstDtlsEnc *self = GST_DTLS_ENC (base); gssize ret; GstMapInfo map; GError *error = NULL; if (gst_buffer_get_size (buffer) == 0) { gst_buffer_unref (buffer); return GST_FLOW_OK; } if (!gst_buffer_map (buffer, &map, GST_MAP_READ)) { GST_ELEMENT_ERROR (base, RESOURCE, READ, ("Can't map buffer"), ("Can't map buffer")); gst_buffer_unref (buffer); return GST_FLOW_ERROR; } GST_OBJECT_LOCK (self); self->src_buffer = buffer; self->running_thread = g_thread_self (); GST_OBJECT_UNLOCK (self); ret = g_output_stream_write (g_io_stream_get_output_stream (G_IO_STREAM (base->conn->conn)), map.data, map.size, NULL, &error); g_assert (ret < 0 || ret == map.size); if (ret > 0 && ret != map.size) ret = -10; GST_OBJECT_LOCK (self); self->src_buffer = NULL; GST_OBJECT_UNLOCK (self); gst_buffer_unmap (buffer, &map); gst_buffer_unref (buffer); if (ret > 0) { return GST_FLOW_OK; } else { if (error) { GstFlowReturn flow = GST_FLOW_ERROR; if (error->domain == GST_IO_STREAM_FLOW_RETURN) { flow = error->code; } else { GST_ELEMENT_ERROR (base, LIBRARY, FAILED, ("DTLS encoding failed: %s", error->message), ("DTLS encoding failed: %s", error->message)); } g_clear_error (&error); return flow; } else { GST_ELEMENT_ERROR (base, LIBRARY, FAILED, ("Unknown encoding error"), ("Unknown encoding error")); return GST_FLOW_ERROR; } } }
static void src_task_loop (GstPad * pad) { GstDtlsEnc *self = GST_DTLS_ENC (GST_PAD_PARENT (pad)); GstFlowReturn ret; GstBuffer *buffer; gboolean check_connection_timeout = FALSE; GST_TRACE_OBJECT (self, "src loop: acquiring lock"); g_mutex_lock (&self->queue_lock); GST_TRACE_OBJECT (self, "src loop: acquired lock"); if (self->flushing) { GST_LOG_OBJECT (self, "src task loop entered on inactive pad"); GST_TRACE_OBJECT (self, "src loop: releasing lock"); g_mutex_unlock (&self->queue_lock); return; } while (g_queue_is_empty (&self->queue)) { GST_TRACE_OBJECT (self, "src loop: queue empty, waiting for add"); g_cond_wait (&self->queue_cond_add, &self->queue_lock); GST_TRACE_OBJECT (self, "src loop: add signaled"); if (self->flushing) { GST_LOG_OBJECT (self, "pad inactive, task returning"); GST_TRACE_OBJECT (self, "src loop: releasing lock"); g_mutex_unlock (&self->queue_lock); return; } } GST_TRACE_OBJECT (self, "src loop: queue has element"); buffer = g_queue_pop_head (&self->queue); g_mutex_unlock (&self->queue_lock); if (self->send_initial_events) { GstSegment segment; gchar s_id[32]; GstCaps *caps; self->send_initial_events = FALSE; g_snprintf (s_id, sizeof (s_id), "dtlsenc-%08x", g_random_int ()); gst_pad_push_event (self->src, gst_event_new_stream_start (s_id)); caps = gst_caps_new_empty_simple ("application/x-dtls"); gst_pad_push_event (self->src, gst_event_new_caps (caps)); gst_caps_unref (caps); gst_segment_init (&segment, GST_FORMAT_BYTES); gst_pad_push_event (self->src, gst_event_new_segment (&segment)); check_connection_timeout = TRUE; } GST_TRACE_OBJECT (self, "src loop: releasing lock"); ret = gst_pad_push (self->src, buffer); if (check_connection_timeout) gst_dtls_connection_check_timeout (self->connection); if (G_UNLIKELY (ret != GST_FLOW_OK)) { GST_WARNING_OBJECT (self, "failed to push buffer on src pad: %s", gst_flow_get_name (ret)); } }
static GstStateChangeReturn gst_dtls_enc_change_state (GstElement * element, GstStateChange transition) { GstDtlsEnc *self = GST_DTLS_ENC (element); GstStateChangeReturn ret; switch (transition) { case GST_STATE_CHANGE_NULL_TO_READY: if (self->connection_id) { self->connection = gst_dtls_dec_fetch_connection (self->connection_id); if (!self->connection) { GST_WARNING_OBJECT (self, "invalid connection id: '%s', connection not found or already in use", self->connection_id); return GST_STATE_CHANGE_FAILURE; } g_signal_connect_object (self->connection, "on-encoder-key", G_CALLBACK (on_key_received), self, 0); gst_dtls_connection_set_send_callback (self->connection, g_cclosure_new (G_CALLBACK (on_send_data), self, NULL)); } else { GST_WARNING_OBJECT (self, "trying to change state to ready without connection id"); return GST_STATE_CHANGE_FAILURE; } break; case GST_STATE_CHANGE_PAUSED_TO_READY: GST_DEBUG_OBJECT (self, "stopping connection %s", self->connection_id); gst_dtls_connection_stop (self->connection); break; case GST_STATE_CHANGE_READY_TO_NULL: GST_DEBUG_OBJECT (self, "closing connection %s", self->connection_id); if (self->connection) { gst_dtls_connection_close (self->connection); gst_dtls_connection_set_send_callback (self->connection, NULL); g_object_unref (self->connection); self->connection = NULL; } break; default: break; } ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition); switch (transition) { case GST_STATE_CHANGE_READY_TO_PAUSED: GST_DEBUG_OBJECT (self, "starting connection %s", self->connection_id); gst_dtls_connection_start (self->connection, self->is_client); break; default: break; } return ret; }