static gboolean src_activate_mode(GstPad *pad, GstObject *parent, GstPadMode mode, gboolean active)
{
    GstErDtlsEnc *self = GST_ER_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->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);
        GST_PAD_MODE(pad) = GST_PAD_MODE_NONE;
        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 GstStateChangeReturn gst_er_dtls_enc_change_state(GstElement *element, GstStateChange transition)
{
    GstErDtlsEnc *self = GST_ER_DTLS_ENC(element);
    GstStateChangeReturn ret;

    switch (transition) {
    case GST_STATE_CHANGE_NULL_TO_READY:
        if (self->connection_id) {
            self->connection = gst_er_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);

            er_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_READY_TO_PAUSED:
        GST_DEBUG_OBJECT(self, "starting connection %s", self->connection_id);
        er_dtls_connection_start(self->connection, self->is_client);

        gst_pad_set_active(self->src, TRUE);
        break;
    case GST_STATE_CHANGE_PAUSED_TO_READY:
        GST_DEBUG_OBJECT(self, "stopping connection %s", self->connection_id);
        gst_pad_set_active(self->src, FALSE);

        er_dtls_connection_stop(self->connection);
        break;
    case GST_STATE_CHANGE_READY_TO_NULL:
        GST_DEBUG_OBJECT(self, "closing connection %s", self->connection_id);
        er_dtls_connection_close(self->connection);
        er_dtls_connection_set_send_callback(self->connection, NULL);

        if (self->connection) {
            g_object_unref(self->connection);
            self->connection = NULL;
        }
        break;
    default:
        break;
    }

    ret = GST_ELEMENT_CLASS(parent_class)->change_state(element, transition);

    return ret;
}
static void gst_er_dtls_enc_set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec)
{
    GstErDtlsEnc *self = GST_ER_DTLS_ENC(object);

    switch (prop_id) {
    case PROP_CONNECTION_ID:
        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 GstFlowReturn sink_chain(GstPad *pad, GstObject *parent, GstBuffer *buffer)
{
    GstErDtlsEnc *self = GST_ER_DTLS_ENC(parent);
    GstMapInfo map_info;
    gint ret;

    gst_buffer_map(buffer, &map_info, GST_MAP_READ);

    if (map_info.size) {
        ret = er_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 %zd B",
                ret, map_info.size);
        }
    }

    gst_buffer_unmap(buffer, &map_info);

    gst_buffer_unref(buffer);

    return GST_FLOW_OK;
}
static void gst_er_dtls_enc_get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
{
    GstErDtlsEnc *self = GST_ER_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 void gst_er_dtls_enc_finalize(GObject *object)
{
    GstErDtlsEnc *self = GST_ER_DTLS_ENC(object);

    if (self->encoder_key) {
        gst_buffer_unref(self->encoder_key);
        self->encoder_key = NULL;
    }

    g_mutex_lock(&self->queue_lock);

    g_ptr_array_set_free_func(self->queue, (GDestroyNotify) gst_buffer_unref);
    g_ptr_array_unref(self->queue);
    self->queue = NULL;

    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 src_task_loop(GstPad *pad)
{
    GstErDtlsEnc *self = GST_ER_DTLS_ENC(GST_PAD_PARENT(pad));
    GstFlowReturn ret;
    GstPad *peer;
    gboolean peer_is_active;

    if (!gst_pad_is_active(pad)) {
        GST_LOG_OBJECT(self, "src task loop entered on inactive pad");
        return;
    }

    GST_TRACE_OBJECT(self, "src loop: acquiring lock");
    g_mutex_lock(&self->queue_lock);
    GST_TRACE_OBJECT(self, "src loop: acquired lock");

    while (!self->queue->len) {
        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 (!gst_pad_is_active(pad)) {
            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");

    peer = gst_pad_get_peer(pad);
    peer_is_active = gst_pad_is_active(peer);
    gst_object_unref(peer);

    if (peer_is_active) {
        GstBuffer *buffer;
        gboolean start_connection_timeout = FALSE;

        if (self->send_initial_events) {
          GstSegment segment;
          gchar s_id[32];
          GstCaps *caps;

          g_snprintf (s_id, sizeof (s_id), "erdtlsenc-%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));
          self->send_initial_events = FALSE;
          start_connection_timeout = TRUE;
        }

        buffer = g_ptr_array_remove_index(self->queue, 0);

        GST_TRACE_OBJECT(self, "src loop: releasing lock");
        g_mutex_unlock(&self->queue_lock);

        ret = gst_pad_push(self->src, buffer);
        if (start_connection_timeout)
          er_dtls_connection_start_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));
        }
    } else {
        g_warn_if_reached();
        GST_TRACE_OBJECT(self, "src loop: releasing lock");
        g_mutex_unlock(&self->queue_lock);
    }
}