static GstFlowReturn gst_srtp_dec_chain (GstPad * pad, GstObject * parent, GstBuffer * buf, gboolean is_rtcp) { GstSrtpDec *filter = GST_SRTP_DEC (parent); GstPad *otherpad; GstSrtpDecSsrcStream *stream = NULL; GstFlowReturn ret = GST_FLOW_OK; guint32 ssrc = 0; GST_OBJECT_LOCK (filter); /* Check if this stream exists, if not create a new stream */ if (!(stream = validate_buffer (filter, buf, &ssrc, &is_rtcp))) { GST_OBJECT_UNLOCK (filter); GST_WARNING_OBJECT (filter, "Invalid buffer, dropping"); goto drop_buffer; } if (!STREAM_HAS_CRYPTO (stream)) { GST_OBJECT_UNLOCK (filter); goto push_out; } if (!gst_srtp_dec_decode_buffer (filter, pad, buf, is_rtcp, ssrc)) { GST_OBJECT_UNLOCK (filter); goto drop_buffer; } GST_OBJECT_UNLOCK (filter); /* If all is well, we may have reached soft limit */ if (gst_srtp_get_soft_limit_reached ()) request_key_with_signal (filter, ssrc, SIGNAL_SOFT_LIMIT); push_out: /* Push buffer to source pad */ if (is_rtcp) { otherpad = filter->rtcp_srcpad; if (!filter->rtcp_has_segment) gst_srtp_dec_push_early_events (filter, filter->rtcp_srcpad, filter->rtp_srcpad, TRUE); } else { otherpad = filter->rtp_srcpad; if (!filter->rtp_has_segment) gst_srtp_dec_push_early_events (filter, filter->rtp_srcpad, filter->rtcp_srcpad, FALSE); } ret = gst_pad_push (otherpad, buf); return ret; drop_buffer: /* Drop buffer, except if gst_pad_push returned OK or an error */ gst_buffer_unref (buf); return ret; }
static gboolean gst_srtp_dec_sink_event_rtcp (GstPad * pad, GstObject * parent, GstEvent * event) { gboolean ret; GstCaps *caps; GstSrtpDec *filter = GST_SRTP_DEC (parent); switch (GST_EVENT_TYPE (event)) { case GST_EVENT_CAPS: gst_event_parse_caps (event, &caps); ret = gst_srtp_dec_sink_setcaps (pad, parent, caps, TRUE); gst_event_unref (event); return ret; case GST_EVENT_SEGMENT: /* Make sure to send a caps event downstream before the segment event, * even if upstream didn't */ if (!gst_pad_has_current_caps (filter->rtcp_srcpad)) { GstCaps *caps = gst_caps_new_empty_simple ("application/x-rtcp"); gst_pad_set_caps (filter->rtcp_srcpad, caps); gst_caps_unref (caps); } filter->rtcp_has_segment = TRUE; break; case GST_EVENT_FLUSH_STOP: filter->rtcp_has_segment = FALSE; break; default: break; } return gst_pad_event_default (pad, parent, event); }
static gboolean gst_srtp_dec_sink_setcaps (GstPad * pad, GstObject * parent, GstCaps * caps, gboolean is_rtcp) { GstSrtpDec *filter = GST_SRTP_DEC (parent); GstPad *otherpad; GstStructure *ps; gboolean ret = FALSE; g_return_val_if_fail (gst_caps_is_fixed (caps), FALSE); ps = gst_caps_get_structure (caps, 0); if (gst_structure_has_field_typed (ps, "ssrc", G_TYPE_UINT) && gst_structure_has_field_typed (ps, "roc", G_TYPE_UINT) && gst_structure_has_field_typed (ps, "srtp-cipher", G_TYPE_STRING) && gst_structure_has_field_typed (ps, "srtp-auth", G_TYPE_STRING) && gst_structure_has_field_typed (ps, "srtcp-cipher", G_TYPE_STRING) && gst_structure_has_field_typed (ps, "srtcp-auth", G_TYPE_STRING)) { guint ssrc; gst_structure_get_uint (ps, "ssrc", &ssrc); if (!update_session_stream_from_caps (filter, ssrc, caps)) { GST_WARNING_OBJECT (pad, "Could not create session from pad caps: %" GST_PTR_FORMAT, caps); return FALSE; } } caps = gst_caps_copy (caps); ps = gst_caps_get_structure (caps, 0); gst_structure_remove_fields (ps, "srtp-key", "srtp-cipher", "srtp-auth", "srtcp-cipher", "srtcp-auth", NULL); if (is_rtcp) gst_structure_set_name (ps, "application/x-rtcp"); else gst_structure_set_name (ps, "application/x-rtp"); otherpad = gst_pad_get_element_private (pad); ret = gst_pad_set_caps (otherpad, caps); gst_caps_unref (caps); return ret; }
static void gst_srtp_dec_get_property (GObject * object, guint prop_id, GValue * value, GParamSpec * pspec) { GstSrtpDec *filter = GST_SRTP_DEC (object); GST_OBJECT_LOCK (filter); switch (prop_id) { case PROP_REPLAY_WINDOW_SIZE: g_value_set_uint (value, filter->replay_window_size); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } GST_OBJECT_UNLOCK (filter); }
static GstStateChangeReturn gst_srtp_dec_change_state (GstElement * element, GstStateChange transition) { GstStateChangeReturn res; GstSrtpDec *filter; filter = GST_SRTP_DEC (element); GST_OBJECT_LOCK (filter); switch (transition) { case GST_STATE_CHANGE_READY_TO_PAUSED: filter->streams = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, (GDestroyNotify) free_stream); filter->rtp_has_segment = FALSE; filter->rtcp_has_segment = FALSE; break; case GST_STATE_CHANGE_PAUSED_TO_PLAYING: break; default: break; } GST_OBJECT_UNLOCK (filter); res = GST_ELEMENT_CLASS (gst_srtp_dec_parent_class)->change_state (element, transition); switch (transition) { case GST_STATE_CHANGE_PLAYING_TO_PAUSED: break; case GST_STATE_CHANGE_PAUSED_TO_READY: gst_srtp_dec_clear_streams (filter); g_hash_table_unref (filter->streams); filter->streams = NULL; break; case GST_STATE_CHANGE_READY_TO_NULL: break; default: break; } return res; }
static gboolean gst_srtp_dec_sink_event_rtp (GstPad * pad, GstObject * parent, GstEvent * event) { GstCaps *caps; GstSrtpDec *filter = GST_SRTP_DEC (parent); switch (GST_EVENT_TYPE (event)) { case GST_EVENT_CAPS: gst_event_parse_caps (event, &caps); return gst_srtp_dec_sink_setcaps (pad, parent, caps, FALSE); case GST_EVENT_SEGMENT: filter->rtp_has_segment = TRUE; break; case GST_EVENT_FLUSH_STOP: filter->rtp_has_segment = FALSE; break; default: break; } return gst_pad_event_default (pad, parent, event); }
static GstIterator * gst_srtp_dec_iterate_internal_links (GstPad * pad, GstObject * parent, gboolean is_rtcp) { GstSrtpDec *filter = GST_SRTP_DEC (parent); GstPad *otherpad = NULL; GstIterator *it = NULL; otherpad = (GstPad *) gst_pad_get_element_private (pad); if (otherpad) { GValue val = { 0 }; g_value_init (&val, GST_TYPE_PAD); g_value_set_object (&val, otherpad); it = gst_iterator_new_single (GST_TYPE_PAD, &val); g_value_unset (&val); } else { GST_ELEMENT_ERROR (GST_ELEMENT_CAST (filter), CORE, PAD, (NULL), ("Unable to get linked pad")); } return it; }
static GstFlowReturn gst_srtp_dec_chain (GstPad * pad, GstObject * parent, GstBuffer * buf, gboolean is_rtcp) { GstSrtpDec *filter = GST_SRTP_DEC (parent); GstPad *otherpad; err_status_t err = err_status_ok; GstSrtpDecSsrcStream *stream = NULL; GstFlowReturn ret = GST_FLOW_OK; gint size; guint32 ssrc = 0; GstMapInfo map; GST_OBJECT_LOCK (filter); /* Check if this stream exists, if not create a new stream */ if (!(stream = validate_buffer (filter, buf, &ssrc, &is_rtcp))) { GST_OBJECT_UNLOCK (filter); GST_WARNING_OBJECT (filter, "Invalid buffer, dropping"); goto drop_buffer; } if (!STREAM_HAS_CRYPTO (stream)) { GST_OBJECT_UNLOCK (filter); goto push_out; } GST_LOG_OBJECT (pad, "Received %s buffer of size %" G_GSIZE_FORMAT " with SSRC = %u", is_rtcp ? "RTCP" : "RTP", gst_buffer_get_size (buf), ssrc); /* Change buffer to remove protection */ buf = gst_buffer_make_writable (buf); unprotect: gst_buffer_map (buf, &map, GST_MAP_READWRITE); size = map.size; gst_srtp_init_event_reporter (); if (is_rtcp) err = srtp_unprotect_rtcp (filter->session, map.data, &size); else { /* If ROC has changed, we know we need to set the initial RTP * sequence number too. */ if (filter->roc_changed) { srtp_stream_t stream; stream = srtp_get_stream (filter->session, htonl (ssrc)); if (stream) { guint16 seqnum = 0; GstRTPBuffer rtpbuf = GST_RTP_BUFFER_INIT; gst_rtp_buffer_map (buf, GST_MAP_READ, &rtpbuf); seqnum = gst_rtp_buffer_get_seq (&rtpbuf); gst_rtp_buffer_unmap (&rtpbuf); /* We finally add the RTP sequence number to the current * rollover counter. */ stream->rtp_rdbx.index &= ~0xFFFF; stream->rtp_rdbx.index |= seqnum; } filter->roc_changed = FALSE; } err = srtp_unprotect (filter->session, map.data, &size); } gst_buffer_unmap (buf, &map); GST_OBJECT_UNLOCK (filter); if (err != err_status_ok) { GST_WARNING_OBJECT (pad, "Unable to unprotect buffer (unprotect failed code %d)", err); /* Signal user depending on type of error */ switch (err) { case err_status_key_expired: GST_OBJECT_LOCK (filter); /* Update stream */ if (find_stream_by_ssrc (filter, ssrc)) { GST_OBJECT_UNLOCK (filter); if (request_key_with_signal (filter, ssrc, SIGNAL_HARD_LIMIT)) { GST_OBJECT_LOCK (filter); goto unprotect; } else { GST_WARNING_OBJECT (filter, "Hard limit reached, no new key, " "dropping"); } } else { GST_WARNING_OBJECT (filter, "Could not find matching stream, " "dropping"); } break; case err_status_auth_fail: GST_WARNING_OBJECT (filter, "Error authentication packet, dropping"); break; case err_status_cipher_fail: GST_WARNING_OBJECT (filter, "Error while decrypting packet, dropping"); break; default: GST_WARNING_OBJECT (filter, "Other error, dropping"); break; } goto drop_buffer; } gst_buffer_set_size (buf, size); /* If all is well, we may have reached soft limit */ if (gst_srtp_get_soft_limit_reached ()) request_key_with_signal (filter, ssrc, SIGNAL_SOFT_LIMIT); push_out: /* Push buffer to source pad */ if (is_rtcp) { otherpad = filter->rtcp_srcpad; if (!filter->rtcp_has_segment) gst_srtp_dec_push_early_events (filter, filter->rtcp_srcpad, filter->rtp_srcpad, TRUE); } else { otherpad = filter->rtp_srcpad; if (!filter->rtp_has_segment) gst_srtp_dec_push_early_events (filter, filter->rtp_srcpad, filter->rtcp_srcpad, FALSE); } ret = gst_pad_push (otherpad, buf); return ret; drop_buffer: /* Drop buffer, except if gst_pad_push returned OK or an error */ gst_buffer_unref (buf); return ret; }