| /* |
| * GStreamer - GStreamer SRTP encoder |
| * |
| * Copyright 2009-2011 Collabora Ltd. |
| * @author: Gabriel Millaire <gabriel.millaire@collabora.com> |
| * @author: Olivier Crete <olivier.crete@collabora.com> |
| * |
| * Permission is hereby granted, free of charge, to any person obtaining a |
| * copy of this software and associated documentation files (the "Software"), |
| * to deal in the Software without restriction, including without limitation |
| * the rights to use, copy, modify, merge, publish, distribute, sublicense, |
| * and/or sell copies of the Software, and to permit persons to whom the |
| * Software is furnished to do so, subject to the following conditions: |
| * |
| * The above copyright notice and this permission notice shall be included in |
| * all copies or substantial portions of the Software. |
| * |
| * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
| * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
| * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
| * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
| * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING |
| * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER |
| * DEALINGS IN THE SOFTWARE. |
| * |
| * Alternatively, the contents of this file may be used under the |
| * GNU Lesser General Public License Version 2.1 (the "LGPL"), in |
| * which case the following provisions apply instead of the ones |
| * mentioned above: |
| * |
| * 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. |
| */ |
| |
| /** |
| * SECTION:gst-plugin-bad-plugins-srtpenc |
| * @see_also: srtpdec |
| * |
| * gstrtpenc acts as an encoder that adds security to RTP and RTCP |
| * packets in the form of encryption and authentication. It outs SRTP |
| * and SRTCP. |
| * |
| * An application can request multiple RTP and RTCP pads to protect, |
| * but every sink pad requested must receive packets from the same |
| * source (identical SSRC). If a packet received contains a different |
| * SSRC, a warning is emited and the valid SSRC is forced on the packet. |
| * |
| * This element uses libsrtp library. When receiving the first packet, |
| * the library is initialized with a new stream (based on the SSRC). It |
| * uses the default RTP and RTCP encryption and authentication mechanisms, |
| * unless the user has set the relevant properties first. It also uses |
| * a master key that MUST be set by property (key) at the beginning. The |
| * master key must be of a maximum length of 46 characters (14 characters |
| * for the salt plus the key). The encryption and authentication mecanisms |
| * available are : |
| * |
| * Encryption (properties rtp-cipher and rtcp-cipher) |
| * - AES_ICM 256 bits (maximum security) |
| * - AES_ICM 128 bits (default) |
| * - NULL |
| * |
| * Authentication (properties rtp-auth and rtcp-auth) |
| * - HMAC_SHA1 80 bits (default, maximum protection) |
| * - HMAC_SHA1 32 bits |
| * - NULL |
| * |
| * Note that for SRTP protection, authentication is mandatory (non-null) |
| * if encryption is used (non-null). |
| * |
| * When requested to create a sink pad, a linked source pad is created. |
| * Each packet received is first analysed (checked for valid SSRC) then |
| * its buffer is protected with libsrtp, then pushed on the source pad. |
| * If protection failed or the stream could not be created, the buffer |
| * is dropped and a warning is emitted. The packets pushed on the source |
| * pad are of type 'application/x-srtp' or 'application/x-srtcp'. |
| * |
| * When the maximum usage of the master key is reached, a soft-limit |
| * signal is sent to the user. The user must then set a new master key |
| * by property. If the hard limit is reached, a flag is set and every |
| * subsequent packet is dropped, until a new key is set and the stream |
| * has been updated. |
| * |
| * If a stream is to be shared between multiple clients it is also |
| * possible to request the internal SRTP rollover counter for a given |
| * SSRC. The rollover counter should be then transmitted and used by the |
| * clients to authenticate and decrypt the packets. Failing to do that |
| * the clients will start with a rollover counter of 0 which will |
| * probably be incorrect if the stream has been transmitted for a |
| * while to other clients. |
| */ |
| |
| #ifdef HAVE_CONFIG_H |
| #include <config.h> |
| #endif |
| |
| #include <gst/gst.h> |
| #include <gst/rtp/gstrtpbuffer.h> |
| #include <gst/rtp/gstrtcpbuffer.h> |
| #include <string.h> |
| |
| #include "gstsrtpenc.h" |
| |
| #include "gstsrtp.h" |
| #include "gstsrtp-enumtypes.h" |
| |
| #include <srtp/srtp_priv.h> |
| |
| GST_DEBUG_CATEGORY_STATIC (gst_srtp_enc_debug); |
| #define GST_CAT_DEFAULT gst_srtp_enc_debug |
| |
| /* 128 bit key size: 14 (salt) + 16 */ |
| #define MASTER_128_KEY_SIZE 30 |
| |
| /* 256 bit key size: 14 (salt) + 16 + 16 */ |
| #define MASTER_256_KEY_SIZE 46 |
| |
| /* Properties default values */ |
| #define DEFAULT_MASTER_KEY NULL |
| #define DEFAULT_RTP_CIPHER GST_SRTP_CIPHER_AES_128_ICM |
| #define DEFAULT_RTP_AUTH GST_SRTP_AUTH_HMAC_SHA1_80 |
| #define DEFAULT_RTCP_CIPHER DEFAULT_RTP_CIPHER |
| #define DEFAULT_RTCP_AUTH DEFAULT_RTP_AUTH |
| #define DEFAULT_RANDOM_KEY FALSE |
| #define DEFAULT_REPLAY_WINDOW_SIZE 128 |
| #define DEFAULT_ALLOW_REPEAT_TX FALSE |
| |
| #define HAS_CRYPTO(filter) (filter->rtp_cipher != GST_SRTP_CIPHER_NULL || \ |
| filter->rtcp_cipher != GST_SRTP_CIPHER_NULL || \ |
| filter->rtp_auth != GST_SRTP_AUTH_NULL || \ |
| filter->rtcp_auth != GST_SRTP_AUTH_NULL) |
| |
| /* Filter signals and args */ |
| enum |
| { |
| SIGNAL_SOFT_LIMIT, |
| SIGNAL_GET_ROLLOVER_COUNTER, |
| LAST_SIGNAL |
| }; |
| |
| enum |
| { |
| PROP_0, |
| PROP_MKEY, |
| PROP_RTP_CIPHER, |
| PROP_RTP_AUTH, |
| PROP_RTCP_CIPHER, |
| PROP_RTCP_AUTH, |
| PROP_RANDOM_KEY, |
| PROP_REPLAY_WINDOW_SIZE, |
| PROP_ALLOW_REPEAT_TX, |
| PROP_STATS |
| }; |
| |
| typedef struct ProcessBufferItData |
| { |
| GstSrtpEnc *filter; |
| GstPad *pad; |
| GstBufferList *out_list; |
| gboolean is_rtcp; |
| } ProcessBufferItData; |
| |
| /* the capabilities of the inputs and outputs. |
| * |
| * describe the real formats here. |
| */ |
| static GstStaticPadTemplate rtp_sink_template = |
| GST_STATIC_PAD_TEMPLATE ("rtp_sink_%u", |
| GST_PAD_SINK, |
| GST_PAD_REQUEST, |
| GST_STATIC_CAPS ("application/x-rtp") |
| ); |
| |
| static GstStaticPadTemplate rtp_src_template = |
| GST_STATIC_PAD_TEMPLATE ("rtp_src_%u", |
| GST_PAD_SRC, |
| GST_PAD_SOMETIMES, |
| GST_STATIC_CAPS ("application/x-srtp") |
| ); |
| |
| static GstStaticPadTemplate rtcp_sink_template = |
| GST_STATIC_PAD_TEMPLATE ("rtcp_sink_%u", |
| GST_PAD_SINK, |
| GST_PAD_REQUEST, |
| GST_STATIC_CAPS ("application/x-rtcp") |
| ); |
| |
| static GstStaticPadTemplate rtcp_src_template = |
| GST_STATIC_PAD_TEMPLATE ("rtcp_src_%u", |
| GST_PAD_SRC, |
| GST_PAD_SOMETIMES, |
| GST_STATIC_CAPS ("application/x-srtcp") |
| ); |
| |
| G_DEFINE_TYPE (GstSrtpEnc, gst_srtp_enc, GST_TYPE_ELEMENT); |
| |
| static guint gst_srtp_enc_signals[LAST_SIGNAL] = { 0 }; |
| |
| static void gst_srtp_enc_dispose (GObject * object); |
| |
| static void gst_srtp_enc_set_property (GObject * object, guint prop_id, |
| const GValue * value, GParamSpec * pspec); |
| static void gst_srtp_enc_get_property (GObject * object, guint prop_id, |
| GValue * value, GParamSpec * pspec); |
| |
| static gboolean gst_srtp_enc_sink_query_rtp (GstPad * pad, GstObject * parent, |
| GstQuery * query); |
| static gboolean gst_srtp_enc_sink_query_rtcp (GstPad * pad, GstObject * parent, |
| GstQuery * query); |
| |
| static GstIterator *gst_srtp_enc_iterate_internal_links_rtp (GstPad * pad, |
| GstObject * parent); |
| static GstIterator *gst_srtp_enc_iterate_internal_links_rtcp (GstPad * pad, |
| GstObject * parent); |
| |
| static GstFlowReturn gst_srtp_enc_chain_rtp (GstPad * pad, GstObject * parent, |
| GstBuffer * buf); |
| static GstFlowReturn gst_srtp_enc_chain_rtcp (GstPad * pad, GstObject * parent, |
| GstBuffer * buf); |
| static GstFlowReturn gst_srtp_enc_chain_list_rtp (GstPad * pad, |
| GstObject * parent, GstBufferList * buf); |
| static GstFlowReturn gst_srtp_enc_chain_list_rtcp (GstPad * pad, |
| GstObject * parent, GstBufferList * buf); |
| |
| static gboolean gst_srtp_enc_sink_event_rtp (GstPad * pad, GstObject * parent, |
| GstEvent * event); |
| static gboolean gst_srtp_enc_sink_event_rtcp (GstPad * pad, GstObject * parent, |
| GstEvent * event); |
| |
| static GstStateChangeReturn gst_srtp_enc_change_state (GstElement * element, |
| GstStateChange transition); |
| |
| static GstPad *gst_srtp_enc_request_new_pad (GstElement * element, |
| GstPadTemplate * templ, const gchar * name, const GstCaps * caps); |
| |
| static void gst_srtp_enc_release_pad (GstElement * element, GstPad * pad); |
| |
| |
| /* initialize the srtpenc's class |
| */ |
| static void |
| gst_srtp_enc_class_init (GstSrtpEncClass * klass) |
| { |
| GObjectClass *gobject_class; |
| GstElementClass *gstelement_class; |
| |
| gobject_class = (GObjectClass *) klass; |
| gstelement_class = (GstElementClass *) klass; |
| |
| gst_element_class_add_static_pad_template (gstelement_class, |
| &rtp_src_template); |
| gst_element_class_add_static_pad_template (gstelement_class, |
| &rtp_sink_template); |
| gst_element_class_add_static_pad_template (gstelement_class, |
| &rtcp_src_template); |
| gst_element_class_add_static_pad_template (gstelement_class, |
| &rtcp_sink_template); |
| |
| gst_element_class_set_static_metadata (gstelement_class, "SRTP encoder", |
| "Filter/Network/SRTP", |
| "A SRTP and SRTCP encoder", |
| "Gabriel Millaire <millaire.gabriel@collabora.com>"); |
| |
| |
| /* Install callbacks */ |
| gobject_class->set_property = gst_srtp_enc_set_property; |
| gobject_class->get_property = gst_srtp_enc_get_property; |
| gobject_class->dispose = gst_srtp_enc_dispose; |
| gstelement_class->request_new_pad = |
| GST_DEBUG_FUNCPTR (gst_srtp_enc_request_new_pad); |
| gstelement_class->release_pad = GST_DEBUG_FUNCPTR (gst_srtp_enc_release_pad); |
| gstelement_class->change_state = |
| GST_DEBUG_FUNCPTR (gst_srtp_enc_change_state); |
| |
| /* Install properties */ |
| g_object_class_install_property (gobject_class, PROP_MKEY, |
| g_param_spec_boxed ("key", "Key", "Master key (minimum of " |
| G_STRINGIFY (MASTER_128_KEY_SIZE) " and maximum of " |
| G_STRINGIFY (MASTER_256_KEY_SIZE) " bytes)", |
| GST_TYPE_BUFFER, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | |
| GST_PARAM_MUTABLE_PLAYING)); |
| g_object_class_install_property (gobject_class, PROP_RTP_CIPHER, |
| g_param_spec_enum ("rtp-cipher", "RTP Cipher", "RTP Cipher", |
| GST_TYPE_SRTP_CIPHER_TYPE, DEFAULT_RTP_CIPHER, |
| G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); |
| g_object_class_install_property (gobject_class, PROP_RTP_AUTH, |
| g_param_spec_enum ("rtp-auth", "RTP Authentication", |
| "RTP Authentication", GST_TYPE_SRTP_AUTH_TYPE, DEFAULT_RTP_AUTH, |
| G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); |
| g_object_class_install_property (gobject_class, PROP_RTCP_CIPHER, |
| g_param_spec_enum ("rtcp-cipher", "RTCP Cipher", |
| "RTCP Cipher", GST_TYPE_SRTP_CIPHER_TYPE, DEFAULT_RTCP_CIPHER, |
| G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); |
| g_object_class_install_property (gobject_class, PROP_RTCP_AUTH, |
| g_param_spec_enum ("rtcp-auth", "RTCP Authentication", |
| "RTCP Authentication", GST_TYPE_SRTP_AUTH_TYPE, DEFAULT_RTCP_AUTH, |
| G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); |
| g_object_class_install_property (gobject_class, PROP_RANDOM_KEY, |
| g_param_spec_boolean ("random-key", "Generate random key", |
| "Generate a random key if TRUE", |
| DEFAULT_RANDOM_KEY, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); |
| g_object_class_install_property (gobject_class, PROP_REPLAY_WINDOW_SIZE, |
| g_param_spec_uint ("replay-window-size", "Replay window size", |
| "Size of the replay protection window", |
| 64, 0x8000, DEFAULT_REPLAY_WINDOW_SIZE, |
| G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); |
| g_object_class_install_property (gobject_class, PROP_ALLOW_REPEAT_TX, |
| g_param_spec_boolean ("allow-repeat-tx", |
| "Allow repeat packets transmission", |
| "Whether retransmissions of packets with the same sequence number are allowed" |
| "(Note that such repeated transmissions must have the same RTP payload, " |
| "or a severe security weakness is introduced!)", |
| DEFAULT_ALLOW_REPEAT_TX, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); |
| g_object_class_install_property (gobject_class, PROP_STATS, |
| g_param_spec_boxed ("stats", "Statistics", "Various statistics", |
| GST_TYPE_STRUCTURE, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); |
| |
| /** |
| * GstSrtpEnc::soft-limit: |
| * @gstsrtpenc: the element on which the signal is emitted |
| * |
| * Signal emited when the stream with @ssrc has reached the soft |
| * limit of utilisation of it's master encryption key. User should |
| * provide a new key by setting the #GstSrtpEnc:key property. |
| */ |
| gst_srtp_enc_signals[SIGNAL_SOFT_LIMIT] = |
| g_signal_new ("soft-limit", G_TYPE_FROM_CLASS (klass), |
| G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 0); |
| } |
| |
| |
| /* initialize the new element |
| */ |
| static void |
| gst_srtp_enc_init (GstSrtpEnc * filter) |
| { |
| filter->key_changed = TRUE; |
| filter->first_session = TRUE; |
| filter->key = DEFAULT_MASTER_KEY; |
| filter->rtp_cipher = DEFAULT_RTP_CIPHER; |
| filter->rtp_auth = DEFAULT_RTP_AUTH; |
| filter->rtcp_cipher = DEFAULT_RTCP_CIPHER; |
| filter->rtcp_auth = DEFAULT_RTCP_AUTH; |
| filter->replay_window_size = DEFAULT_REPLAY_WINDOW_SIZE; |
| filter->allow_repeat_tx = DEFAULT_ALLOW_REPEAT_TX; |
| } |
| |
| static guint |
| max_cipher_key_size (GstSrtpEnc * filter) |
| { |
| guint rtp_size, rtcp_size; |
| |
| rtp_size = cipher_key_size (filter->rtp_cipher); |
| rtcp_size = cipher_key_size (filter->rtcp_cipher); |
| |
| return (rtp_size > rtcp_size) ? rtp_size : rtcp_size; |
| } |
| |
| /* Create stream |
| * |
| * Should be called with the filter locked |
| */ |
| static err_status_t |
| gst_srtp_enc_create_session (GstSrtpEnc * filter) |
| { |
| err_status_t ret; |
| srtp_policy_t policy; |
| GstMapInfo map; |
| guchar tmp[1]; |
| |
| memset (&policy, 0, sizeof (srtp_policy_t)); |
| |
| if (HAS_CRYPTO (filter)) { |
| guint expected; |
| gsize keysize; |
| |
| if (filter->key == NULL) { |
| GST_OBJECT_UNLOCK (filter); |
| GST_ELEMENT_ERROR (filter, LIBRARY, SETTINGS, |
| ("Cipher is not NULL, key must be set"), |
| ("Cipher is not NULL, key must be set")); |
| GST_OBJECT_LOCK (filter); |
| return err_status_fail; |
| } |
| |
| expected = max_cipher_key_size (filter); |
| keysize = gst_buffer_get_size (filter->key); |
| |
| if (expected != keysize) { |
| GST_OBJECT_UNLOCK (filter); |
| GST_ELEMENT_ERROR (filter, LIBRARY, SETTINGS, |
| ("Master key size is wrong"), |
| ("Expected master key of %d bytes, but received %" G_GSIZE_FORMAT |
| " bytes", expected, keysize)); |
| GST_OBJECT_LOCK (filter); |
| return err_status_fail; |
| } |
| } |
| |
| GST_DEBUG_OBJECT (filter, "Setting RTP/RTCP policy to %d / %d", |
| filter->rtp_cipher, filter->rtcp_cipher); |
| set_crypto_policy_cipher_auth (filter->rtp_cipher, filter->rtp_auth, |
| &policy.rtp); |
| set_crypto_policy_cipher_auth (filter->rtcp_cipher, filter->rtcp_auth, |
| &policy.rtcp); |
| |
| if (HAS_CRYPTO (filter)) { |
| gst_buffer_map (filter->key, &map, GST_MAP_READ); |
| policy.key = (guchar *) map.data; |
| } else { |
| policy.key = tmp; |
| } |
| |
| policy.ssrc.value = 0; |
| policy.ssrc.type = ssrc_any_outbound; |
| policy.next = NULL; |
| |
| policy.window_size = filter->replay_window_size; |
| policy.allow_repeat_tx = filter->allow_repeat_tx; |
| |
| /* If it is the first stream, create the session |
| * If not, add the stream to the session |
| */ |
| ret = srtp_create (&filter->session, &policy); |
| filter->first_session = FALSE; |
| |
| if (HAS_CRYPTO (filter)) |
| gst_buffer_unmap (filter->key, &map); |
| |
| return ret; |
| } |
| |
| /* Release ressources and set default values |
| */ |
| static void |
| gst_srtp_enc_reset_no_lock (GstSrtpEnc * filter) |
| { |
| if (!filter->first_session) |
| srtp_dealloc (filter->session); |
| |
| filter->first_session = TRUE; |
| filter->key_changed = FALSE; |
| } |
| |
| static void |
| gst_srtp_enc_reset (GstSrtpEnc * filter) |
| { |
| GST_OBJECT_LOCK (filter); |
| gst_srtp_enc_reset_no_lock (filter); |
| GST_OBJECT_UNLOCK (filter); |
| } |
| |
| /* Create sinkpad to receive RTP packets from encers |
| * and a srcpad for the RTP packets |
| */ |
| static GstPad * |
| create_rtp_sink (GstSrtpEnc * filter, const gchar * name) |
| { |
| GstPad *sinkpad, *srcpad; |
| gchar *sinkpadname, *srcpadname; |
| guint nb = 0; |
| |
| GST_DEBUG_OBJECT (filter, "creating RTP sink pad"); |
| sinkpad = gst_pad_new_from_static_template (&rtp_sink_template, name); |
| |
| sinkpadname = gst_pad_get_name (sinkpad); |
| sscanf (sinkpadname, "rtp_sink_%u", &nb); |
| srcpadname = g_strdup_printf ("rtp_src_%u", nb); |
| |
| GST_DEBUG_OBJECT (filter, "creating RTP source pad"); |
| srcpad = gst_pad_new_from_static_template (&rtp_src_template, srcpadname); |
| g_free (srcpadname); |
| g_free (sinkpadname); |
| |
| gst_pad_set_element_private (sinkpad, srcpad); |
| gst_pad_set_element_private (srcpad, sinkpad); |
| |
| gst_pad_set_query_function (sinkpad, |
| GST_DEBUG_FUNCPTR (gst_srtp_enc_sink_query_rtp)); |
| gst_pad_set_iterate_internal_links_function (sinkpad, |
| GST_DEBUG_FUNCPTR (gst_srtp_enc_iterate_internal_links_rtp)); |
| gst_pad_set_chain_function (sinkpad, |
| GST_DEBUG_FUNCPTR (gst_srtp_enc_chain_rtp)); |
| gst_pad_set_chain_list_function (sinkpad, |
| GST_DEBUG_FUNCPTR (gst_srtp_enc_chain_list_rtp)); |
| gst_pad_set_event_function (sinkpad, |
| GST_DEBUG_FUNCPTR (gst_srtp_enc_sink_event_rtp)); |
| gst_pad_set_active (sinkpad, TRUE); |
| gst_element_add_pad (GST_ELEMENT_CAST (filter), sinkpad); |
| |
| gst_pad_set_iterate_internal_links_function (srcpad, |
| GST_DEBUG_FUNCPTR (gst_srtp_enc_iterate_internal_links_rtp)); |
| gst_pad_set_active (srcpad, TRUE); |
| gst_element_add_pad (GST_ELEMENT_CAST (filter), srcpad); |
| |
| return sinkpad; |
| } |
| |
| /* Create sinkpad to receive RTCP packets from encers |
| * and a srcpad for the RTCP packets |
| */ |
| static GstPad * |
| create_rtcp_sink (GstSrtpEnc * filter, const gchar * name) |
| { |
| GstPad *srcpad, *sinkpad; |
| gchar *sinkpadname, *srcpadname; |
| guint nb = 0; |
| |
| GST_DEBUG_OBJECT (filter, "creating RTCP sink pad"); |
| sinkpad = gst_pad_new_from_static_template (&rtcp_sink_template, name); |
| |
| sinkpadname = gst_pad_get_name (sinkpad); |
| sscanf (sinkpadname, "rtcp_sink_%u", &nb); |
| srcpadname = g_strdup_printf ("rtcp_src_%u", nb); |
| |
| GST_DEBUG_OBJECT (filter, "creating RTCP source pad"); |
| srcpad = gst_pad_new_from_static_template (&rtcp_src_template, srcpadname); |
| g_free (srcpadname); |
| g_free (sinkpadname); |
| |
| gst_pad_set_element_private (sinkpad, srcpad); |
| gst_pad_set_element_private (srcpad, sinkpad); |
| |
| gst_pad_set_query_function (sinkpad, |
| GST_DEBUG_FUNCPTR (gst_srtp_enc_sink_query_rtcp)); |
| gst_pad_set_iterate_internal_links_function (sinkpad, |
| GST_DEBUG_FUNCPTR (gst_srtp_enc_iterate_internal_links_rtcp)); |
| gst_pad_set_chain_function (sinkpad, |
| GST_DEBUG_FUNCPTR (gst_srtp_enc_chain_rtcp)); |
| gst_pad_set_chain_list_function (sinkpad, |
| GST_DEBUG_FUNCPTR (gst_srtp_enc_chain_list_rtcp)); |
| gst_pad_set_event_function (sinkpad, |
| GST_DEBUG_FUNCPTR (gst_srtp_enc_sink_event_rtcp)); |
| gst_pad_set_active (sinkpad, TRUE); |
| gst_element_add_pad (GST_ELEMENT_CAST (filter), sinkpad); |
| |
| gst_pad_set_iterate_internal_links_function (srcpad, |
| GST_DEBUG_FUNCPTR (gst_srtp_enc_iterate_internal_links_rtcp)); |
| gst_pad_set_active (srcpad, TRUE); |
| gst_element_add_pad (GST_ELEMENT_CAST (filter), srcpad); |
| |
| return sinkpad; |
| } |
| |
| /* Handling new pad request |
| */ |
| static GstPad * |
| gst_srtp_enc_request_new_pad (GstElement * element, |
| GstPadTemplate * templ, const gchar * name, const GstCaps * caps) |
| { |
| GstElementClass *klass; |
| GstSrtpEnc *filter; |
| |
| filter = GST_SRTP_ENC (element); |
| klass = GST_ELEMENT_GET_CLASS (element); |
| |
| GST_INFO_OBJECT (element, "New pad requested"); |
| |
| if (templ == gst_element_class_get_pad_template (klass, "rtp_sink_%u")) |
| return create_rtp_sink (filter, name); |
| |
| if (templ == gst_element_class_get_pad_template (klass, "rtcp_sink_%u")) |
| return create_rtcp_sink (filter, name); |
| |
| GST_ERROR_OBJECT (element, "Could not find specified template"); |
| return NULL; |
| } |
| |
| /* Dispose |
| */ |
| static void |
| gst_srtp_enc_dispose (GObject * object) |
| { |
| GstSrtpEnc *filter = GST_SRTP_ENC (object); |
| GstIterator *it; |
| GValue val = { 0 }; |
| |
| GST_DEBUG_OBJECT (object, "Dispose..."); |
| |
| it = gst_element_iterate_sink_pads (GST_ELEMENT_CAST (object)); |
| while (gst_iterator_next (it, &val) == GST_ITERATOR_OK) { |
| gst_srtp_enc_release_pad (GST_ELEMENT_CAST (object), |
| g_value_get_object (&val)); |
| g_value_unset (&val); |
| gst_iterator_resync (it); |
| } |
| gst_iterator_free (it); |
| |
| if (filter->key) |
| gst_buffer_unref (filter->key); |
| filter->key = NULL; |
| |
| G_OBJECT_CLASS (gst_srtp_enc_parent_class)->dispose (object); |
| } |
| |
| static GstStructure * |
| gst_srtp_enc_create_stats (GstSrtpEnc * filter) |
| { |
| GstStructure *s; |
| GValue va = G_VALUE_INIT; |
| GValue v = G_VALUE_INIT; |
| |
| s = gst_structure_new_empty ("application/x-srtp-encoder-stats"); |
| |
| g_value_init (&va, GST_TYPE_ARRAY); |
| g_value_init (&v, GST_TYPE_STRUCTURE); |
| |
| if (filter->session) { |
| srtp_stream_t stream = filter->session->stream_list; |
| while (stream) { |
| GstStructure *ss; |
| guint32 ssrc = GUINT32_FROM_BE (stream->ssrc); |
| guint32 roc = stream->rtp_rdbx.index >> 16; |
| |
| ss = gst_structure_new ("application/x-srtp-stream", |
| "ssrc", G_TYPE_UINT, ssrc, "roc", G_TYPE_UINT, roc, NULL); |
| |
| g_value_take_boxed (&v, ss); |
| gst_value_array_append_value (&va, &v); |
| |
| stream = stream->next; |
| } |
| } |
| |
| gst_structure_take_value (s, "streams", &va); |
| g_value_unset (&v); |
| |
| return s; |
| } |
| |
| static void |
| gst_srtp_enc_set_property (GObject * object, guint prop_id, |
| const GValue * value, GParamSpec * pspec) |
| { |
| GstSrtpEnc *filter = GST_SRTP_ENC (object); |
| |
| GST_OBJECT_LOCK (filter); |
| |
| switch (prop_id) { |
| case PROP_MKEY: |
| if (filter->key) |
| gst_buffer_unref (filter->key); |
| filter->key = g_value_dup_boxed (value); |
| filter->key_changed = TRUE; |
| GST_INFO_OBJECT (object, "Set property: key=[%p]", filter->key); |
| break; |
| |
| case PROP_RTP_CIPHER: |
| filter->rtp_cipher = g_value_get_enum (value); |
| GST_INFO_OBJECT (object, "Set property: rtp cipher=%d", |
| filter->rtp_cipher); |
| break; |
| case PROP_RTP_AUTH: |
| filter->rtp_auth = g_value_get_enum (value); |
| GST_INFO_OBJECT (object, "Set property: rtp auth=%d", filter->rtp_auth); |
| break; |
| |
| case PROP_RTCP_CIPHER: |
| filter->rtcp_cipher = g_value_get_enum (value); |
| GST_INFO_OBJECT (object, "Set property: rtcp cipher=%d", |
| filter->rtcp_cipher); |
| break; |
| |
| case PROP_RTCP_AUTH: |
| filter->rtcp_auth = g_value_get_enum (value); |
| GST_INFO_OBJECT (object, "Set property: rtcp auth=%d", filter->rtcp_auth); |
| break; |
| |
| case PROP_RANDOM_KEY: |
| filter->random_key = g_value_get_boolean (value); |
| break; |
| |
| case PROP_REPLAY_WINDOW_SIZE: |
| filter->replay_window_size = g_value_get_uint (value); |
| break; |
| |
| case PROP_ALLOW_REPEAT_TX: |
| filter->allow_repeat_tx = g_value_get_boolean (value); |
| break; |
| |
| default: |
| G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); |
| break; |
| } |
| |
| GST_OBJECT_UNLOCK (filter); |
| } |
| |
| static void |
| gst_srtp_enc_get_property (GObject * object, guint prop_id, |
| GValue * value, GParamSpec * pspec) |
| { |
| GstSrtpEnc *filter = GST_SRTP_ENC (object); |
| GST_OBJECT_LOCK (filter); |
| |
| switch (prop_id) { |
| case PROP_MKEY: |
| if (filter->key) |
| g_value_set_boxed (value, filter->key); |
| break; |
| case PROP_RTP_CIPHER: |
| g_value_set_enum (value, filter->rtp_cipher); |
| break; |
| case PROP_RTCP_CIPHER: |
| g_value_set_enum (value, filter->rtcp_cipher); |
| break; |
| case PROP_RTP_AUTH: |
| g_value_set_enum (value, filter->rtp_auth); |
| break; |
| case PROP_RTCP_AUTH: |
| g_value_set_enum (value, filter->rtcp_auth); |
| break; |
| case PROP_RANDOM_KEY: |
| g_value_set_boolean (value, filter->random_key); |
| break; |
| case PROP_REPLAY_WINDOW_SIZE: |
| g_value_set_uint (value, filter->replay_window_size); |
| break; |
| case PROP_ALLOW_REPEAT_TX: |
| g_value_set_boolean (value, filter->allow_repeat_tx); |
| break; |
| case PROP_STATS: |
| g_value_take_boxed (value, gst_srtp_enc_create_stats (filter)); |
| break; |
| default: |
| G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); |
| break; |
| } |
| |
| GST_OBJECT_UNLOCK (filter); |
| } |
| |
| /* Returns the source pad linked with the sink pad |
| */ |
| static GstPad * |
| get_rtp_other_pad (GstPad * pad) |
| { |
| return GST_PAD (gst_pad_get_element_private (pad)); |
| } |
| |
| /* Release a sink pad and it's linked source pad |
| */ |
| static void |
| gst_srtp_enc_release_pad (GstElement * element, GstPad * sinkpad) |
| { |
| GstPad *srcpad; |
| |
| GST_INFO_OBJECT (element, "Releasing pad %s:%s", |
| GST_DEBUG_PAD_NAME (sinkpad)); |
| |
| srcpad = GST_PAD (gst_pad_get_element_private (sinkpad)); |
| gst_pad_set_element_private (sinkpad, NULL); |
| gst_pad_set_element_private (srcpad, NULL); |
| |
| /* deactivate from source to sink */ |
| gst_pad_set_active (srcpad, FALSE); |
| gst_pad_set_active (sinkpad, FALSE); |
| |
| /* remove pads */ |
| gst_element_remove_pad (element, srcpad); |
| gst_element_remove_pad (element, sinkpad); |
| } |
| |
| /* Common setcaps function |
| * Handles the link with other elements |
| */ |
| static gboolean |
| gst_srtp_enc_sink_setcaps (GstPad * pad, GstSrtpEnc * filter, |
| GstCaps * caps, gboolean is_rtcp) |
| { |
| GstPad *otherpad = NULL; |
| GstStructure *ps = NULL; |
| gboolean ret = FALSE; |
| |
| g_return_val_if_fail (gst_caps_is_fixed (caps), FALSE); |
| |
| caps = gst_caps_copy (caps); |
| |
| ps = gst_caps_get_structure (caps, 0); |
| |
| GST_DEBUG_OBJECT (pad, "Sink caps: %" GST_PTR_FORMAT, caps); |
| |
| if (is_rtcp) |
| gst_structure_set_name (ps, "application/x-srtcp"); |
| else |
| gst_structure_set_name (ps, "application/x-srtp"); |
| |
| GST_OBJECT_LOCK (filter); |
| |
| if (HAS_CRYPTO (filter)) |
| gst_structure_set (ps, "srtp-key", GST_TYPE_BUFFER, filter->key, NULL); |
| |
| /* Add srtp-specific params to source caps */ |
| gst_structure_set (ps, |
| "srtp-cipher", G_TYPE_STRING, |
| enum_nick_from_value (GST_TYPE_SRTP_CIPHER_TYPE, filter->rtp_cipher), |
| "srtp-auth", G_TYPE_STRING, |
| enum_nick_from_value (GST_TYPE_SRTP_AUTH_TYPE, filter->rtp_auth), |
| "srtcp-cipher", G_TYPE_STRING, |
| enum_nick_from_value (GST_TYPE_SRTP_CIPHER_TYPE, filter->rtcp_cipher), |
| "srtcp-auth", G_TYPE_STRING, |
| enum_nick_from_value (GST_TYPE_SRTP_AUTH_TYPE, filter->rtcp_auth), NULL); |
| |
| GST_OBJECT_UNLOCK (filter); |
| |
| GST_DEBUG_OBJECT (pad, "Source caps: %" GST_PTR_FORMAT, caps); |
| |
| /* Set caps on source pad */ |
| otherpad = get_rtp_other_pad (pad); |
| |
| ret = gst_pad_set_caps (otherpad, caps); |
| |
| gst_caps_unref (caps); |
| |
| return ret; |
| } |
| |
| static gboolean |
| gst_srtp_enc_sink_query (GstPad * pad, GstObject * parent, GstQuery * query, |
| gboolean is_rtcp) |
| { |
| switch (GST_QUERY_TYPE (query)) { |
| case GST_QUERY_CAPS: |
| { |
| GstCaps *filter = NULL; |
| GstCaps *other_filter = NULL; |
| GstPad *otherpad; |
| GstCaps *other_caps; |
| GstCaps *ret; |
| GstCaps *template_caps; |
| int i; |
| |
| otherpad = get_rtp_other_pad (pad); |
| |
| gst_query_parse_caps (query, &filter); |
| if (filter) { |
| other_filter = gst_caps_copy (filter); |
| |
| for (i = 0; i < gst_caps_get_size (other_filter); i++) { |
| GstStructure *ps = gst_caps_get_structure (other_filter, i); |
| if (is_rtcp) |
| gst_structure_set_name (ps, "application/x-srtcp"); |
| else |
| gst_structure_set_name (ps, "application/x-srtp"); |
| } |
| } |
| |
| other_caps = gst_pad_peer_query_caps (otherpad, other_filter); |
| |
| if (other_filter) |
| gst_caps_unref (other_filter); |
| |
| if (!other_caps) |
| goto return_template; |
| |
| template_caps = gst_pad_get_pad_template_caps (otherpad); |
| ret = gst_caps_intersect_full (other_caps, template_caps, |
| GST_CAPS_INTERSECT_FIRST); |
| gst_caps_unref (other_caps); |
| gst_caps_unref (template_caps); |
| |
| ret = gst_caps_make_writable (ret); |
| |
| for (i = 0; i < gst_caps_get_size (ret); i++) { |
| GstStructure *ps = gst_caps_get_structure (ret, i); |
| if (is_rtcp) |
| gst_structure_set_name (ps, "application/x-rtcp"); |
| else |
| gst_structure_set_name (ps, "application/x-rtp"); |
| gst_structure_remove_fields (ps, "srtp-key", "srtp-cipher", "srtp-auth", |
| "srtcp-cipher", "srtcp-auth", NULL); |
| } |
| |
| gst_query_set_caps_result (query, ret); |
| gst_caps_unref (ret); |
| return TRUE; |
| return_template: |
| |
| ret = gst_pad_get_pad_template_caps (pad); |
| gst_query_set_caps_result (query, ret); |
| gst_caps_unref (ret); |
| |
| return TRUE; |
| } |
| default: |
| return gst_pad_query_default (pad, parent, query); |
| } |
| } |
| |
| static gboolean |
| gst_srtp_enc_sink_query_rtp (GstPad * pad, GstObject * parent, GstQuery * query) |
| { |
| return gst_srtp_enc_sink_query (pad, parent, query, FALSE); |
| } |
| |
| static gboolean |
| gst_srtp_enc_sink_query_rtcp (GstPad * pad, GstObject * parent, |
| GstQuery * query) |
| { |
| return gst_srtp_enc_sink_query (pad, parent, query, TRUE); |
| } |
| |
| static GstIterator * |
| gst_srtp_enc_iterate_internal_links (GstPad * pad, GstObject * parent, |
| gboolean is_rtcp) |
| { |
| GstSrtpEnc *filter = GST_SRTP_ENC (parent); |
| GstPad *otherpad = NULL; |
| GstIterator *it = NULL; |
| |
| otherpad = get_rtp_other_pad (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 GstIterator * |
| gst_srtp_enc_iterate_internal_links_rtp (GstPad * pad, GstObject * parent) |
| { |
| return gst_srtp_enc_iterate_internal_links (pad, parent, FALSE); |
| } |
| |
| static GstIterator * |
| gst_srtp_enc_iterate_internal_links_rtcp (GstPad * pad, GstObject * parent) |
| { |
| return gst_srtp_enc_iterate_internal_links (pad, parent, TRUE); |
| } |
| |
| |
| static void |
| gst_srtp_enc_replace_random_key (GstSrtpEnc * filter) |
| { |
| guint i; |
| guint key_size; |
| GstMapInfo map; |
| |
| GST_DEBUG_OBJECT (filter, "Generating random key"); |
| |
| if (filter->key) |
| gst_buffer_unref (filter->key); |
| |
| key_size = max_cipher_key_size (filter); |
| |
| filter->key = gst_buffer_new_allocate (NULL, key_size, NULL); |
| |
| gst_buffer_map (filter->key, &map, GST_MAP_WRITE); |
| for (i = 0; i < map.size; i += 4) |
| GST_WRITE_UINT32_BE (map.data + i, g_random_int ()); |
| gst_buffer_unmap (filter->key, &map); |
| |
| filter->key_changed = TRUE; |
| } |
| |
| static GstFlowReturn |
| gst_srtp_enc_check_set_caps (GstSrtpEnc * filter, GstPad * pad, |
| gboolean is_rtcp) |
| { |
| gboolean do_setcaps = FALSE; |
| |
| GST_OBJECT_LOCK (filter); |
| |
| if (filter->key_changed) { |
| gst_srtp_enc_reset_no_lock (filter); |
| do_setcaps = TRUE; |
| } |
| |
| if (filter->first_session) { |
| err_status_t status = gst_srtp_enc_create_session (filter); |
| |
| if (status != err_status_ok) { |
| GST_OBJECT_UNLOCK (filter); |
| GST_ELEMENT_ERROR (filter, LIBRARY, INIT, |
| ("Could not initialize SRTP encoder"), |
| ("Failed to add stream to SRTP encoder (err: %d)", status)); |
| return GST_FLOW_ERROR; |
| } |
| } |
| |
| GST_OBJECT_UNLOCK (filter); |
| |
| /* Update source caps if asked */ |
| if (do_setcaps) { |
| GstCaps *caps; |
| |
| caps = gst_pad_get_current_caps (pad); |
| if (!gst_srtp_enc_sink_setcaps (pad, filter, caps, is_rtcp)) { |
| gst_caps_unref (caps); |
| return GST_FLOW_NOT_NEGOTIATED; |
| } |
| gst_caps_unref (caps); |
| } |
| |
| return GST_FLOW_OK; |
| } |
| |
| static GstBuffer * |
| gst_srtp_enc_process_buffer (GstSrtpEnc * filter, GstPad * pad, |
| GstBuffer * buf, gboolean is_rtcp) |
| { |
| gint size_max, size; |
| GstBuffer *bufout = NULL; |
| GstMapInfo mapout; |
| err_status_t err; |
| |
| /* Create a bigger buffer to add protection */ |
| size = gst_buffer_get_size (buf); |
| size_max = size + SRTP_MAX_TRAILER_LEN + 10; |
| bufout = gst_buffer_new_allocate (NULL, size_max, NULL); |
| |
| gst_buffer_map (bufout, &mapout, GST_MAP_READWRITE); |
| |
| gst_buffer_extract (buf, 0, mapout.data, size); |
| |
| GST_OBJECT_LOCK (filter); |
| |
| gst_srtp_init_event_reporter (); |
| |
| if (is_rtcp) |
| err = srtp_protect_rtcp (filter->session, mapout.data, &size); |
| else |
| err = srtp_protect (filter->session, mapout.data, &size); |
| |
| GST_OBJECT_UNLOCK (filter); |
| |
| gst_buffer_unmap (bufout, &mapout); |
| |
| if (err == err_status_ok) { |
| /* Buffer protected */ |
| gst_buffer_set_size (bufout, size); |
| gst_buffer_copy_into (bufout, buf, GST_BUFFER_COPY_METADATA, 0, -1); |
| |
| GST_LOG_OBJECT (pad, "Encoding %s buffer of size %d", |
| is_rtcp ? "RTCP" : "RTP", size); |
| |
| } else if (err == err_status_key_expired) { |
| |
| GST_ELEMENT_ERROR (GST_ELEMENT_CAST (filter), STREAM, ENCODE, |
| ("Key usage limit has been reached"), |
| ("Unable to protect buffer (hard key usage limit reached)")); |
| goto fail; |
| |
| } else { |
| /* srtp_protect failed */ |
| GST_ELEMENT_ERROR (filter, LIBRARY, FAILED, (NULL), |
| ("Unable to protect buffer (protect failed) code %d", err)); |
| goto fail; |
| } |
| |
| return bufout; |
| |
| fail: |
| gst_buffer_unref (bufout); |
| return NULL; |
| } |
| |
| static GstFlowReturn |
| gst_srtp_enc_chain (GstPad * pad, GstObject * parent, GstBuffer * buf, |
| gboolean is_rtcp) |
| { |
| GstSrtpEnc *filter = GST_SRTP_ENC (parent); |
| GstFlowReturn ret = GST_FLOW_OK; |
| GstPad *otherpad; |
| GstBuffer *bufout = NULL; |
| |
| if ((ret = gst_srtp_enc_check_set_caps (filter, pad, is_rtcp)) != GST_FLOW_OK) { |
| goto out; |
| } |
| |
| GST_OBJECT_LOCK (filter); |
| |
| if (!HAS_CRYPTO (filter)) { |
| GST_OBJECT_UNLOCK (filter); |
| otherpad = get_rtp_other_pad (pad); |
| return gst_pad_push (otherpad, buf); |
| } |
| |
| GST_OBJECT_UNLOCK (filter); |
| |
| if ((bufout = gst_srtp_enc_process_buffer (filter, pad, buf, is_rtcp))) { |
| /* Push buffer to source pad */ |
| otherpad = get_rtp_other_pad (pad); |
| ret = gst_pad_push (otherpad, bufout); |
| bufout = NULL; |
| |
| if (ret != GST_FLOW_OK) |
| goto out; |
| } else { |
| goto fail; |
| } |
| |
| GST_OBJECT_LOCK (filter); |
| |
| if (gst_srtp_get_soft_limit_reached ()) { |
| GST_OBJECT_UNLOCK (filter); |
| g_signal_emit (filter, gst_srtp_enc_signals[SIGNAL_SOFT_LIMIT], 0); |
| GST_OBJECT_LOCK (filter); |
| if (filter->random_key && !filter->key_changed) |
| gst_srtp_enc_replace_random_key (filter); |
| } |
| |
| GST_OBJECT_UNLOCK (filter); |
| |
| out: |
| |
| gst_buffer_unref (buf); |
| |
| return ret; |
| |
| fail: |
| ret = GST_FLOW_ERROR; |
| goto out; |
| } |
| |
| static gboolean |
| process_buffer_it (GstBuffer ** buffer, guint index, gpointer user_data) |
| { |
| ProcessBufferItData *data = user_data; |
| GstBuffer *bufout; |
| |
| if ((bufout = |
| gst_srtp_enc_process_buffer (data->filter, data->pad, *buffer, |
| data->is_rtcp))) { |
| gst_buffer_list_add (data->out_list, bufout); |
| } else { |
| GST_WARNING_OBJECT (data->filter, "Error encoding buffer, dropping"); |
| } |
| |
| return TRUE; |
| } |
| |
| static GstFlowReturn |
| gst_srtp_enc_chain_list (GstPad * pad, GstObject * parent, |
| GstBufferList * buf_list, gboolean is_rtcp) |
| { |
| GstSrtpEnc *filter = GST_SRTP_ENC (parent); |
| GstFlowReturn ret = GST_FLOW_OK; |
| GstPad *otherpad; |
| GstBufferList *out_list = NULL; |
| ProcessBufferItData process_data; |
| |
| GST_LOG_OBJECT (pad, "Buffer chain with list of %d", |
| gst_buffer_list_length (buf_list)); |
| |
| if (!gst_buffer_list_length (buf_list)) |
| goto out; |
| |
| if ((ret = gst_srtp_enc_check_set_caps (filter, pad, is_rtcp)) != GST_FLOW_OK) |
| goto out; |
| |
| GST_OBJECT_LOCK (filter); |
| |
| if (!HAS_CRYPTO (filter)) { |
| GST_OBJECT_UNLOCK (filter); |
| otherpad = get_rtp_other_pad (pad); |
| return gst_pad_push_list (otherpad, buf_list); |
| } |
| |
| GST_OBJECT_UNLOCK (filter); |
| |
| out_list = gst_buffer_list_new (); |
| |
| process_data.filter = filter; |
| process_data.pad = pad; |
| process_data.is_rtcp = is_rtcp; |
| process_data.out_list = out_list; |
| |
| gst_buffer_list_foreach (buf_list, process_buffer_it, &process_data); |
| |
| if (!gst_buffer_list_length (out_list)) { |
| gst_buffer_list_unref (out_list); |
| ret = GST_FLOW_OK; |
| goto out; |
| } |
| |
| /* Push buffer to source pad */ |
| otherpad = get_rtp_other_pad (pad); |
| GST_LOG_OBJECT (pad, "Pushing buffer chain of %d", |
| gst_buffer_list_length (buf_list)); |
| ret = gst_pad_push_list (otherpad, out_list); |
| |
| if (ret != GST_FLOW_OK) { |
| goto out; |
| } |
| |
| GST_OBJECT_LOCK (filter); |
| |
| if (gst_srtp_get_soft_limit_reached ()) { |
| GST_OBJECT_UNLOCK (filter); |
| g_signal_emit (filter, gst_srtp_enc_signals[SIGNAL_SOFT_LIMIT], 0); |
| GST_OBJECT_LOCK (filter); |
| if (filter->random_key && !filter->key_changed) |
| gst_srtp_enc_replace_random_key (filter); |
| } |
| |
| GST_OBJECT_UNLOCK (filter); |
| |
| out: |
| |
| gst_buffer_list_unref (buf_list); |
| |
| return ret; |
| } |
| |
| static GstFlowReturn |
| gst_srtp_enc_chain_rtp (GstPad * pad, GstObject * parent, GstBuffer * buf) |
| { |
| return gst_srtp_enc_chain (pad, parent, buf, FALSE); |
| } |
| |
| static GstFlowReturn |
| gst_srtp_enc_chain_rtcp (GstPad * pad, GstObject * parent, GstBuffer * buf) |
| { |
| return gst_srtp_enc_chain (pad, parent, buf, TRUE); |
| } |
| |
| static GstFlowReturn |
| gst_srtp_enc_chain_list_rtp (GstPad * pad, GstObject * parent, |
| GstBufferList * buf_list) |
| { |
| return gst_srtp_enc_chain_list (pad, parent, buf_list, FALSE); |
| } |
| |
| static GstFlowReturn |
| gst_srtp_enc_chain_list_rtcp (GstPad * pad, GstObject * parent, |
| GstBufferList * buf_list) |
| { |
| return gst_srtp_enc_chain_list (pad, parent, buf_list, TRUE); |
| } |
| |
| |
| /* Change state |
| */ |
| static GstStateChangeReturn |
| gst_srtp_enc_change_state (GstElement * element, GstStateChange transition) |
| { |
| GstStateChangeReturn res; |
| GstSrtpEnc *filter; |
| |
| filter = GST_SRTP_ENC (element); |
| |
| switch (transition) { |
| case GST_STATE_CHANGE_NULL_TO_READY: |
| if (filter->rtp_cipher != GST_SRTP_CIPHER_NULL || |
| filter->rtcp_cipher != GST_SRTP_CIPHER_NULL || |
| filter->rtp_auth != GST_SRTP_AUTH_NULL || |
| filter->rtcp_auth != GST_SRTP_AUTH_NULL) { |
| if (!filter->key) { |
| if (filter->random_key) { |
| gst_srtp_enc_replace_random_key (filter); |
| } else { |
| GST_ERROR_OBJECT (element, "Need a key to get to READY"); |
| return GST_STATE_CHANGE_FAILURE; |
| } |
| } |
| } |
| if ((filter->rtcp_cipher != NULL_CIPHER) |
| && (filter->rtcp_auth == NULL_AUTH)) { |
| GST_ERROR_OBJECT (filter, |
| "RTCP authentication can't be NULL if encryption is not NULL."); |
| return GST_STATE_CHANGE_FAILURE; |
| } |
| GST_OBJECT_LOCK (filter); |
| if (!filter->first_session) |
| gst_srtp_enc_reset_no_lock (filter); |
| GST_OBJECT_UNLOCK (filter); |
| break; |
| case GST_STATE_CHANGE_READY_TO_PAUSED: |
| break; |
| case GST_STATE_CHANGE_PAUSED_TO_PLAYING: |
| break; |
| default: |
| break; |
| } |
| |
| res = GST_ELEMENT_CLASS (gst_srtp_enc_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_enc_reset (filter); |
| break; |
| case GST_STATE_CHANGE_READY_TO_NULL: |
| break; |
| default: |
| break; |
| } |
| |
| return res; |
| } |
| |
| static gboolean |
| gst_srtp_enc_sink_event (GstPad * pad, GstObject * parent, GstEvent * event, |
| gboolean is_rtcp) |
| { |
| GstSrtpEnc *filter = GST_SRTP_ENC (parent); |
| gboolean ret; |
| GstPad *otherpad; |
| |
| otherpad = get_rtp_other_pad (pad); |
| |
| switch (GST_EVENT_TYPE (event)) { |
| case GST_EVENT_FLUSH_STOP: |
| GST_DEBUG_OBJECT (pad, "Encing event Flush stop (%d)", |
| GST_EVENT_TYPE (event)); |
| gst_srtp_enc_reset (filter); |
| ret = gst_pad_push_event (otherpad, event); |
| break; |
| case GST_EVENT_CAPS: |
| { |
| GstCaps *caps; |
| |
| gst_event_parse_caps (event, &caps); |
| ret = gst_srtp_enc_sink_setcaps (pad, filter, caps, is_rtcp); |
| gst_event_unref (event); |
| break; |
| } |
| default: |
| GST_DEBUG_OBJECT (pad, "Encing event default (%d)", |
| GST_EVENT_TYPE (event)); |
| ret = gst_pad_event_default (pad, parent, event); |
| break; |
| } |
| |
| return ret; |
| } |
| |
| static gboolean |
| gst_srtp_enc_sink_event_rtp (GstPad * pad, GstObject * parent, GstEvent * event) |
| { |
| return gst_srtp_enc_sink_event (pad, parent, event, FALSE); |
| } |
| |
| static gboolean |
| gst_srtp_enc_sink_event_rtcp (GstPad * pad, GstObject * parent, |
| GstEvent * event) |
| { |
| return gst_srtp_enc_sink_event (pad, parent, event, TRUE); |
| } |
| |
| /* entry point to initialize the plug-in |
| * initialize the plug-in itself |
| * register the element factories and other features |
| */ |
| gboolean |
| gst_srtp_enc_plugin_init (GstPlugin * srtpenc) |
| { |
| GST_DEBUG_CATEGORY_INIT (gst_srtp_enc_debug, "srtpenc", 0, "SRTP Enc"); |
| |
| return gst_element_register (srtpenc, "srtpenc", GST_RANK_NONE, |
| GST_TYPE_SRTP_ENC); |
| } |