static gboolean _rtpbin_pad_have_data_callback (GstPad *pad, GstMiniObject *miniobj, gpointer user_data) { FsRtpSubStream *self = FS_RTP_SUB_STREAM (user_data); gboolean ret = TRUE; gboolean remove = FALSE; FsRtpSession *session; if (fs_rtp_session_has_disposed_enter (self->priv->session, NULL)) return FALSE; if (fs_rtp_sub_stream_has_stopped_enter (self)) { fs_rtp_session_has_disposed_exit (self->priv->session); return FALSE; } g_object_ref (self); session = g_object_ref (self->priv->session); FS_RTP_SESSION_LOCK (self->priv->session); if (!self->priv->codecbin || !self->codec || !self->priv->caps) { ret = FALSE; } else if (GST_IS_BUFFER (miniobj)) { if (!gst_caps_is_equal_fixed (GST_BUFFER_CAPS (miniobj), self->priv->caps)) { if (!gst_caps_can_intersect (GST_BUFFER_CAPS (miniobj), self->priv->caps)) ret = FALSE; } else { remove = TRUE; } } if (remove && self->priv->blocking_id) { gst_pad_remove_data_probe (pad, self->priv->blocking_id); self->priv->blocking_id = 0; } FS_RTP_SESSION_UNLOCK (self->priv->session); fs_rtp_sub_stream_has_stopped_exit (self); fs_rtp_session_has_disposed_exit (self->priv->session); g_object_unref (self); g_object_unref (session); return ret; }
static void _substream_unlinked (FsRtpSubStream *substream, gpointer user_data) { FsRtpStream *stream = FS_RTP_STREAM (user_data); FsRtpSession *session = fs_rtp_stream_get_session (stream, NULL); if (!session) return; FS_RTP_SESSION_LOCK (session); stream->substreams = g_list_remove (stream->substreams, substream); FS_RTP_SESSION_UNLOCK (session); fs_rtp_sub_stream_stop (substream); g_object_unref (substream); g_object_unref (session); }
static gboolean _rtpbin_pad_have_data_callback (GstPad *pad, GstMiniObject *miniobj, gpointer user_data) { FsRtpSubStream *self = FS_RTP_SUB_STREAM (user_data); gboolean ret = TRUE; gboolean remove = FALSE; FS_RTP_SESSION_LOCK (self->priv->session); if (!self->priv->codecbin || !self->codec || !self->priv->caps) { ret = FALSE; } else if (GST_IS_BUFFER (miniobj)) { if (!gst_caps_is_equal_fixed (GST_BUFFER_CAPS (miniobj), self->priv->caps)) { GstCaps *intersect = gst_caps_intersect (GST_BUFFER_CAPS (miniobj), self->priv->caps); if (gst_caps_is_empty (intersect)) ret = FALSE; else gst_buffer_set_caps (GST_BUFFER (miniobj), self->priv->caps); gst_caps_unref (intersect); } else { remove = TRUE; } } if (remove && self->priv->blocking_id) { gst_pad_remove_data_probe (pad, self->priv->blocking_id); self->priv->blocking_id = 0; } FS_RTP_SESSION_UNLOCK (self->priv->session); return ret; }
static gboolean fs_rtp_sub_stream_start_no_rtcp_timeout_thread (FsRtpSubStream *self, GError **error) { gboolean res = TRUE; GstClock *sysclock = NULL; sysclock = gst_system_clock_obtain (); if (sysclock == NULL) { g_set_error (error, FS_ERROR, FS_ERROR_INTERNAL, "Could not obtain gst system clock"); return FALSE; } FS_RTP_SESSION_LOCK (self->priv->session); FS_RTP_SUB_STREAM_LOCK(self); self->priv->next_no_rtcp_timeout = gst_clock_get_time (sysclock) + (self->no_rtcp_timeout * GST_MSECOND); gst_object_unref (sysclock); if (self->priv->no_rtcp_timeout_thread == NULL) { /* only create a new thread if the old one was stopped. Otherwise we can * just reuse the currently running one. */ self->priv->no_rtcp_timeout_thread = g_thread_create (no_rtcp_timeout_func, self, TRUE, error); } res = (self->priv->no_rtcp_timeout_thread != NULL); if (res == FALSE && error && *error == NULL) g_set_error (error, FS_ERROR, FS_ERROR_INTERNAL, "Unknown error creating" " thread"); FS_RTP_SUB_STREAM_UNLOCK(self); FS_RTP_SESSION_UNLOCK (self->priv->session); return res; }
static FsStreamTransmitter * fs_rtp_stream_get_stream_transmitter (FsRtpStream *self, GError **error) { FsRtpSession *session = fs_rtp_stream_get_session (self, error); FsStreamTransmitter *st = NULL; if (!session) return NULL; FS_RTP_SESSION_LOCK (session); st = self->priv->stream_transmitter; if (st) g_object_ref (st); FS_RTP_SESSION_UNLOCK (session); if (!st) g_set_error (error, FS_ERROR, FS_ERROR_DISPOSED, "Stream transmitter not set (or stream has been disposed)"); g_object_unref (session); return st; }
static void _substream_codec_changed (FsRtpSubStream *substream, FsRtpStream *stream) { GList *substream_item = NULL; GList *codeclist = NULL; FsRtpSession *session = fs_rtp_stream_get_session (stream, NULL); if (!session) return; FS_RTP_SESSION_LOCK (session); if (!substream->codec) { FS_RTP_SESSION_UNLOCK (session); g_object_unref (session); return; } codeclist = g_list_prepend (NULL, fs_codec_copy (substream->codec)); for (substream_item = stream->substreams; substream_item; substream_item = g_list_next (substream_item)) { FsRtpSubStream *othersubstream = substream_item->data; if (othersubstream != substream) { if (othersubstream->codec) { if (fs_codec_are_equal (substream->codec, othersubstream->codec)) break; if (!_codec_list_has_codec (codeclist, othersubstream->codec)) codeclist = g_list_append (codeclist, fs_codec_copy (othersubstream->codec)); } } } FS_RTP_SESSION_UNLOCK (session); if (substream_item == NULL) { GstElement *conf = NULL; g_object_notify (G_OBJECT (stream), "current-recv-codecs"); g_object_get (session, "conference", &conf, NULL); gst_element_post_message (conf, gst_message_new_element (GST_OBJECT (conf), gst_structure_new ("farstream-recv-codecs-changed", "stream", FS_TYPE_STREAM, stream, "codecs", FS_TYPE_CODEC_LIST, codeclist, NULL))); gst_object_unref (conf); } fs_codec_list_destroy (codeclist); g_object_unref (session); }
/** * fs_rtp_stream_set_remote_codecs: * @stream: an #FsStream * @remote_codecs: a #GList of #FsCodec representing the remote codecs * @error: location of a #GError, or NULL if no error occured * * This function will set the list of remote codecs for this stream. If * the given remote codecs couldn't be negotiated with the list of local * codecs or already negotiated codecs for the corresponding #FsSession, @error * will be set and %FALSE will be returned. The @remote_codecs list will be * copied so it must be free'd using fs_codec_list_destroy() when done. * * Returns: %FALSE if the remote codecs couldn't be set. */ static gboolean fs_rtp_stream_set_remote_codecs (FsStream *stream, GList *remote_codecs, GError **error) { FsRtpStream *self = FS_RTP_STREAM (stream); GList *item = NULL; FsMediaType media_type; FsRtpSession *session = fs_rtp_stream_get_session (self, error); if (!session) return FALSE; if (remote_codecs == NULL) { g_set_error (error, FS_ERROR, FS_ERROR_INVALID_ARGUMENTS, "You can not set NULL remote codecs"); goto error; } g_object_get (session, "media-type", &media_type, NULL); for (item = g_list_first (remote_codecs); item; item = g_list_next (item)) { FsCodec *codec = item->data; if (!codec->encoding_name) { g_set_error (error, FS_ERROR, FS_ERROR_INVALID_ARGUMENTS, "The codec must have an encoding name"); goto error; } if (codec->id < 0 || codec->id > 128) { g_set_error (error, FS_ERROR, FS_ERROR_INVALID_ARGUMENTS, "The codec id must be between 0 ans 128 for %s", codec->encoding_name); goto error; } if (codec->media_type != media_type) { g_set_error (error, FS_ERROR, FS_ERROR_INVALID_ARGUMENTS, "The media type for codec %s is not %s", codec->encoding_name, fs_media_type_to_string (media_type)); goto error; } } if (self->priv->new_remote_codecs_cb (self, remote_codecs, error, self->priv->user_data_for_cb)) { gboolean is_new = TRUE; FS_RTP_SESSION_LOCK (session); if (self->remote_codecs) { is_new = !fs_codec_list_are_equal (self->remote_codecs, remote_codecs); fs_codec_list_destroy (self->remote_codecs); } self->remote_codecs = fs_codec_list_copy (remote_codecs); FS_RTP_SESSION_UNLOCK (session); if (is_new) g_object_notify (G_OBJECT (stream), "remote-codecs"); } else { goto error; } g_object_unref (session); return TRUE; error: g_object_unref (session); return FALSE; }
static void fs_rtp_stream_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { FsRtpStream *self = FS_RTP_STREAM (object); GList *item; switch (prop_id) { case PROP_SESSION: self->priv->session = FS_RTP_SESSION (g_value_dup_object (value)); break; case PROP_PARTICIPANT: self->participant = FS_RTP_PARTICIPANT (g_value_dup_object (value)); break; case PROP_DIRECTION: { FsStreamTransmitter *st = NULL; GList *copy = NULL; FsRtpSession *session = fs_rtp_stream_get_session (self, NULL); FsStreamDirection dir; if (!session) { self->priv->direction = g_value_get_flags (value); return; } FS_RTP_SESSION_LOCK (session); if (self->priv->sending_changed_locked_cb && (self->priv->direction & FS_DIRECTION_SEND) != (g_value_get_flags (value) & FS_DIRECTION_SEND)) self->priv->sending_changed_locked_cb (self, g_value_get_flags (value) & FS_DIRECTION_SEND, self->priv->user_data_for_cb); dir = self->priv->direction = g_value_get_flags (value); FS_RTP_SESSION_UNLOCK (session); st = fs_rtp_stream_get_stream_transmitter (self, NULL); if (st) { g_object_set (self->priv->stream_transmitter, "sending", dir & FS_DIRECTION_SEND, NULL); g_object_unref (st); } FS_RTP_SESSION_LOCK (session); copy = g_list_copy (g_list_first (self->substreams)); g_list_foreach (copy, (GFunc) g_object_ref, NULL); FS_RTP_SESSION_UNLOCK (session); for (item = copy; item; item = g_list_next (item)) g_object_set (G_OBJECT (item->data), "receiving", ((dir & FS_DIRECTION_RECV) != 0), NULL); g_list_foreach (copy, (GFunc) g_object_unref, NULL); g_list_free (copy); g_object_unref (session); } break; case PROP_RTP_HEADER_EXTENSIONS: { FsRtpSession *session = fs_rtp_stream_get_session (self, NULL); if (session) { FS_RTP_SESSION_LOCK (session); fs_rtp_header_extension_list_destroy (self->hdrext); self->hdrext = g_value_dup_boxed (value); FS_RTP_SESSION_UNLOCK (session); /* The callbadck can not fail because it does not change * the codecs */ self->priv->new_remote_codecs_cb (NULL, NULL, NULL, self->priv->user_data_for_cb); g_object_unref (session); } } break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } }
static void fs_rtp_stream_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) { FsRtpStream *self = FS_RTP_STREAM (object); FsRtpSession *session = fs_rtp_stream_get_session (self, NULL); if (!session) return; switch (prop_id) { case PROP_REMOTE_CODECS: FS_RTP_SESSION_LOCK (session); g_value_set_boxed (value, self->remote_codecs); FS_RTP_SESSION_UNLOCK (session); break; case PROP_NEGOTIATED_CODECS: FS_RTP_SESSION_LOCK (session); g_value_set_boxed (value, self->negotiated_codecs); FS_RTP_SESSION_UNLOCK (session); break; case PROP_SESSION: g_value_set_object (value, session); break; case PROP_PARTICIPANT: FS_RTP_SESSION_LOCK (session); g_value_set_object (value, self->participant); FS_RTP_SESSION_UNLOCK (session); break; case PROP_DIRECTION: g_value_set_flags (value, self->priv->direction); break; case PROP_CURRENT_RECV_CODECS: { GList *codeclist = NULL; GList *substream_item; FS_RTP_SESSION_LOCK (session); for (substream_item = g_list_first (self->substreams); substream_item; substream_item = g_list_next (substream_item)) { FsRtpSubStream *substream = substream_item->data; if (substream->codec) { if (!_codec_list_has_codec (codeclist, substream->codec)) codeclist = g_list_append (codeclist, fs_codec_copy (substream->codec)); } } g_value_take_boxed (value, codeclist); FS_RTP_SESSION_UNLOCK (session); } break; case PROP_RTP_HEADER_EXTENSIONS: FS_RTP_SESSION_LOCK (session); g_value_set_boxed (value, self->hdrext); FS_RTP_SESSION_UNLOCK (session); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } g_object_unref (session); }
static void fs_rtp_stream_dispose (GObject *object) { FsRtpStream *self = FS_RTP_STREAM (object); FsStreamTransmitter *st; FsRtpParticipant *participant; FsRtpSession *session = fs_rtp_stream_get_session (self, NULL); if (!session) return; g_mutex_lock (&self->priv->mutex); self->priv->session = NULL; g_mutex_unlock (&self->priv->mutex); FS_RTP_SESSION_LOCK (session); if (self->priv->sending_changed_locked_cb && self->priv->direction & FS_DIRECTION_SEND) self->priv->sending_changed_locked_cb (self, FALSE, self->priv->user_data_for_cb); participant = self->participant; self->participant = NULL; st = self->priv->stream_transmitter; self->priv->stream_transmitter = NULL; if (st) { g_signal_handler_disconnect (st, self->priv->local_candidates_prepared_handler_id); g_signal_handler_disconnect (st, self->priv->new_active_candidate_pair_handler_id); g_signal_handler_disconnect (st, self->priv->new_local_candidate_handler_id); g_signal_handler_disconnect (st, self->priv->error_handler_id); g_signal_handler_disconnect (st, self->priv->known_source_packet_received_handler_id); g_signal_handler_disconnect (st, self->priv->state_changed_handler_id); FS_RTP_SESSION_UNLOCK (session); fs_stream_transmitter_stop (st); g_object_unref (st); FS_RTP_SESSION_LOCK (session); } while (self->substreams) { FsRtpSubStream *substream = self->substreams->data; self->substreams = g_list_remove (self->substreams, substream); FS_RTP_SESSION_UNLOCK (session); g_object_unref (substream); FS_RTP_SESSION_LOCK (session); } FS_RTP_SESSION_UNLOCK (session); g_object_unref (participant); g_object_unref (session); g_object_unref (session); G_OBJECT_CLASS (fs_rtp_stream_parent_class)->dispose (object); }
static gboolean fs_rtp_stream_set_transmitter (FsStream *stream, const gchar *transmitter, GParameter *stream_transmitter_parameters, guint stream_transmitter_n_parameters, GError **error) { FsStreamTransmitter *st = NULL; FsRtpStream *self = FS_RTP_STREAM (stream); FsRtpSession *session = fs_rtp_stream_get_session (self, error); if (!session) return FALSE; FS_RTP_SESSION_LOCK (session); if (self->priv->stream_transmitter) { FS_RTP_SESSION_UNLOCK (session); g_object_unref (session); return FALSE; } FS_RTP_SESSION_UNLOCK (session); st = self->priv->get_new_stream_transmitter_cb (self, FS_PARTICIPANT (self->participant), transmitter, stream_transmitter_parameters, stream_transmitter_n_parameters, error, self->priv->user_data_for_cb); if (!st) { g_object_unref (session); return FALSE; } g_object_set (st, "sending", self->priv->direction & FS_DIRECTION_SEND, NULL); self->priv->local_candidates_prepared_handler_id = g_signal_connect_object (st, "local-candidates-prepared", G_CALLBACK (_local_candidates_prepared), self, 0); self->priv->new_active_candidate_pair_handler_id = g_signal_connect_object (st, "new-active-candidate-pair", G_CALLBACK (_new_active_candidate_pair), self, 0); self->priv->new_local_candidate_handler_id = g_signal_connect_object (st, "new-local-candidate", G_CALLBACK (_new_local_candidate), self, 0); self->priv->error_handler_id = g_signal_connect_object (st, "error", G_CALLBACK (_transmitter_error), self, 0); self->priv->known_source_packet_received_handler_id = g_signal_connect_object (st, "known-source-packet-received", G_CALLBACK (_known_source_packet_received), self, 0); self->priv->state_changed_handler_id = g_signal_connect_object (st, "state-changed", G_CALLBACK (_state_changed), self, 0); FS_RTP_SESSION_LOCK (session); self->priv->stream_transmitter = st; if (self->priv->direction & FS_DIRECTION_SEND) self->priv->sending_changed_locked_cb (self, self->priv->direction & FS_DIRECTION_SEND, self->priv->user_data_for_cb); FS_RTP_SESSION_UNLOCK (session); if (!fs_stream_transmitter_gather_local_candidates (st, error)) { FS_RTP_SESSION_LOCK (session); self->priv->stream_transmitter = NULL; FS_RTP_SESSION_UNLOCK (session); g_object_unref (st); g_object_unref (session); return FALSE; } g_object_unref (session); return TRUE; }
static gboolean fs_rtp_sub_stream_set_codecbin (FsRtpSubStream *substream, FsCodec *codec, GstElement *codecbin, GError **error) { GstCaps *caps = NULL; gchar *tmp; gboolean ret = FALSE; GstPad *pad; if (substream->priv->codecbin) { gst_element_set_locked_state (substream->priv->codecbin, TRUE); if (gst_element_set_state (substream->priv->codecbin, GST_STATE_NULL) != GST_STATE_CHANGE_SUCCESS) { gst_element_set_locked_state (substream->priv->codecbin, FALSE); g_set_error (error, FS_ERROR, FS_ERROR_INTERNAL, "Could not set the codec bin for ssrc %u" " and payload type %d to the state NULL", substream->ssrc, substream->pt); gst_object_unref (codecbin); fs_codec_destroy (codec); return FALSE; } gst_bin_remove (GST_BIN (substream->priv->conference), substream->priv->codecbin); FS_RTP_SESSION_LOCK (substream->priv->session); substream->priv->codecbin = NULL; if (substream->codec) { fs_codec_destroy (substream->codec); substream->codec = NULL; } if (substream->priv->caps) gst_caps_unref (substream->priv->caps); substream->priv->caps = NULL; FS_RTP_SESSION_UNLOCK (substream->priv->session); } if (!gst_bin_add (GST_BIN (substream->priv->conference), codecbin)) { gst_object_unref (codecbin); fs_codec_destroy (codec); g_set_error (error, FS_ERROR, FS_ERROR_CONSTRUCTION, "Could not add the codec bin to the conference"); return FALSE; } if (gst_element_set_state (codecbin, GST_STATE_PLAYING) == GST_STATE_CHANGE_FAILURE) { g_set_error (error, FS_ERROR, FS_ERROR_CONSTRUCTION, "Could not set the codec bin to the playing state"); goto error; } if (!gst_element_link_pads (codecbin, "src", substream->priv->output_valve, "sink")) { g_set_error (error, FS_ERROR, FS_ERROR_CONSTRUCTION, "Could not link the codec bin to the output_valve"); goto error; } if (!gst_element_link_pads (substream->priv->capsfilter, "src", codecbin, "sink")) { g_set_error (error, FS_ERROR, FS_ERROR_CONSTRUCTION, "Could not link the receive capsfilter and the codecbin for pt %d", substream->pt); goto error; } caps = fs_codec_to_gst_caps (codec); tmp = gst_caps_to_string (caps); GST_DEBUG ("Setting caps %s on recv substream", tmp); g_free (tmp); g_object_set (substream->priv->capsfilter, "caps", caps, NULL); pad = gst_element_get_static_pad (codecbin, "sink"); if (!pad) { g_set_error (error, FS_ERROR, FS_ERROR_INTERNAL, "Could not get sink pad" " from codecbin"); goto error; } /* This is a non-error error * Some codecs require config data to start.. so we should just ignore them */ if (!gst_pad_set_caps (pad, caps)) { ret = TRUE; gst_object_unref (pad); gst_caps_unref (caps); GST_DEBUG ("Could not set the caps on the codecbin, waiting on config-data" " for SSRC:%x pt:%d", substream->ssrc, substream->pt); /* We call this to drop all buffers until something comes up */ fs_rtp_sub_stream_add_probe_locked (substream); goto error; } GST_DEBUG ("New recv codec accepted"); gst_object_unref (pad); FS_RTP_SESSION_LOCK (substream->priv->session); substream->priv->caps = caps; substream->priv->codecbin = codecbin; substream->codec = codec; codec = NULL; if (substream->priv->stream && !substream->priv->output_ghostpad) { if (!fs_rtp_sub_stream_add_output_ghostpad_unlock (substream, error)) goto error; } else { FS_RTP_SESSION_UNLOCK (substream->priv->session); g_signal_emit (substream, signals[CODEC_CHANGED], 0); } return TRUE; error: gst_element_set_locked_state (codecbin, TRUE); gst_element_set_state (codecbin, GST_STATE_NULL); gst_bin_remove (GST_BIN (substream->priv->conference), codecbin); fs_codec_destroy (codec); return ret; }
gboolean fs_rtp_sub_stream_add_output_ghostpad_unlock (FsRtpSubStream *substream, GError **error) { GstPad *valve_srcpad; gchar *padname = NULL; GstPad *ghostpad = NULL; FsCodec *codec = NULL; if (fs_rtp_sub_stream_has_stopped_enter (substream)) { FS_RTP_SESSION_UNLOCK (substream->priv->session); return TRUE; } if (substream->priv->adding_output_ghostpad) { FS_RTP_SESSION_UNLOCK (substream->priv->session); goto out; } g_assert (substream->priv->output_ghostpad == NULL); substream->priv->adding_output_ghostpad = TRUE; padname = g_strdup_printf ("src_%u_%u_%d", substream->priv->session->id, substream->ssrc, substream->pt); FS_RTP_SESSION_UNLOCK (substream->priv->session); valve_srcpad = gst_element_get_static_pad (substream->priv->output_valve, "src"); g_assert (valve_srcpad); ghostpad = gst_ghost_pad_new_from_template (padname, valve_srcpad, gst_element_class_get_pad_template ( GST_ELEMENT_GET_CLASS (substream->priv->conference), "src_%d_%d_%d")); gst_object_unref (valve_srcpad); g_free (padname); if (!ghostpad) { g_set_error (error, FS_ERROR, FS_ERROR_CONSTRUCTION, "Could not build ghostpad src_%u_%u_%d", substream->priv->session->id, substream->ssrc, substream->pt); goto error; } if (!gst_pad_set_active (ghostpad, TRUE)) { g_set_error (error, FS_ERROR, FS_ERROR_CONSTRUCTION, "Could not activate the src_%u_%u_%d", substream->priv->session->id, substream->ssrc, substream->pt); gst_object_unref (ghostpad); goto error; } if (!gst_element_add_pad (GST_ELEMENT (substream->priv->conference), ghostpad)) { g_set_error (error, FS_ERROR, FS_ERROR_CONSTRUCTION, "Could add build ghostpad src_%u_%u_%d to the conference", substream->priv->session->id, substream->ssrc, substream->pt); gst_object_unref (ghostpad); goto error; } FS_RTP_SESSION_LOCK (substream->priv->session); substream->priv->output_ghostpad = ghostpad; GST_DEBUG ("Src pad added on substream for ssrc:%X pt:%u " FS_CODEC_FORMAT, substream->ssrc, substream->pt, FS_CODEC_ARGS (substream->codec)); codec = fs_codec_copy (substream->codec); FS_RTP_SESSION_UNLOCK (substream->priv->session); g_signal_emit (substream, signals[SRC_PAD_ADDED], 0, ghostpad, codec); g_signal_emit (substream, signals[CODEC_CHANGED], 0); fs_codec_destroy (codec); g_object_set (substream->priv->output_valve, "drop", FALSE, NULL); out: fs_rtp_sub_stream_has_stopped_exit (substream); return TRUE; error: substream->priv->adding_output_ghostpad = FALSE; fs_rtp_sub_stream_has_stopped_exit (substream); return FALSE; }