/* * Farstream - Farstream Stream * * Copyright 2007 Collabora Ltd. * @author: Philippe Kalaf * Copyright 2007 Nokia Corp. * * fs-stream.c - A Farstream Stream gobject (base implementation) * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /** * SECTION:fs-stream * @short_description: A stream in a session in a conference * * This object is the base implementation of a Farstream Stream. It * needs to be derived and implemented by a Farstream conference GStreamer * element. A Farstream Stream is a media stream originating from a * #FsParticipant inside a #FsSession. In fact, a #FsStream instance is * obtained by adding a participant into a session using * fs_session_new_stream(). * * * This will communicate asynchronous events to the user through #GstMessage * of type #GST_MESSAGE_ELEMENT sent over the #GstBus. * * The "<literal>farstream-new-local-candidate</literal>" message * |[ * "stream" #FsStream The stream that emits the message * "candidate" #FsCandidate The new candidate * ]| * * This message is emitted when a new local candidate is discovered. * * * The "<literal>farstream-local-candidates-prepared</literal>" message * |[ * "stream" #FsStream The stream that emits the message * ]| * * This signal is emitted when all local candidates have been * prepared, an ICE implementation would send its SDP offer or answer. * * * The "<literal>farstream-new-active-candidate-pair</literal>" message * |[ * "stream" #FsStream The stream that emits the message * "local-candidate" #FsCandidate Local candidate being used * "remote-candidate" #FsCandidate Remote candidate being used * ]| * * This message is emitted when there is a new active candidate pair that has * been established. This is specially useful for ICE where the active * candidate pair can change automatically due to network conditions. The user * must not modify the candidates and must copy them if he wants to use them * outside the callback scope. This message is emitted once per component. * * * The "<literal>farstream-recv-codecs-changed</literal>" message * |[ * "stream" #FsStream The stream that emits the message * "codecs" #FsCodecGList A #GList of #FsCodec * ]| * * This message is emitted when the content of the * #FsStream:current-recv-codecs property changes. It is normally emitted * right after the #FsStream::src-pad-added signal only if that codec was not * previously received in this stream, but it can also be emitted if the pad * already exists, but the source material that will come to it is different. * The list of new recv-codecs is included in the message * * * The "<literal>farstream-component-state-changed</literal>" message * |[ * "stream" #FsStream The stream that emits the message * "component" #guint The component whose state changed * "state" #FsStreamState The new state of the component * ]| * * This message is emitted the state of a component of a stream changes. * * * */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "fs-stream.h" #include #include "fs-session.h" #include "fs-codec.h" #include "fs-candidate.h" #include "fs-stream-transmitter.h" #include "fs-conference.h" #include "fs-enumtypes.h" #include "fs-private.h" /* Signals */ enum { ERROR_SIGNAL, SRC_PAD_ADDED, LAST_SIGNAL }; /* props */ enum { PROP_0, PROP_REMOTE_CODECS, PROP_NEGOTIATED_CODECS, PROP_CURRENT_RECV_CODECS, PROP_DIRECTION, PROP_PARTICIPANT, PROP_SESSION, PROP_DECRYPTION_PARAMETERS, PROP_REQUIRE_ENCRYPTION }; struct _FsStreamPrivate { GMutex mutex; GList *src_pads; guint32 src_pads_cookie; }; #define FS_STREAM_GET_PRIVATE(o) \ (G_TYPE_INSTANCE_GET_PRIVATE ((o), FS_TYPE_STREAM, FsStreamPrivate)) G_DEFINE_ABSTRACT_TYPE(FsStream, fs_stream, G_TYPE_OBJECT) static void fs_stream_constructed (GObject *obj); static void fs_stream_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec); static void fs_stream_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec); static void fs_stream_finalize (GObject *obj); static void fs_stream_pad_removed (FsStream *stream, GstPad *pad); static guint signals[LAST_SIGNAL] = { 0 }; #define FS_STREAM_LOCK(self) g_mutex_lock(&(self)->priv->mutex) #define FS_STREAM_UNLOCK(self) g_mutex_unlock(&(self)->priv->mutex) static void fs_stream_class_init (FsStreamClass *klass) { GObjectClass *gobject_class; gobject_class = (GObjectClass *) klass; gobject_class->set_property = fs_stream_set_property; gobject_class->get_property = fs_stream_get_property; gobject_class->finalize = fs_stream_finalize; gobject_class->constructed = fs_stream_constructed; /** * FsStream:remote-codecs: (type GLib.List(FsCodec)) (transfer full) * * This is the list of remote codecs for this stream. They must be set by the * user as soon as they are known using fs_stream_set_remote_codecs() * (generally through external signaling). It is a #GList of #FsCodec. */ g_object_class_install_property (gobject_class, PROP_REMOTE_CODECS, g_param_spec_boxed ("remote-codecs", "List of remote codecs", "A GList of FsCodecs of the remote codecs", FS_TYPE_CODEC_LIST, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); /** * FsStream:negotiated-codecs: (type GLib.List(FsCodec)) (transfer full) * * This is the list of negotiatied codecs, it is the same list as the list * of #FsCodec from the parent #FsSession, except that the codec config data * has been replaced with the data from the remote codecs for this stream. * This is the list of #FsCodec used to receive data from this stream. * It is a #GList of #FsCodec. */ g_object_class_install_property (gobject_class, PROP_NEGOTIATED_CODECS, g_param_spec_boxed ("negotiated-codecs", "List of remote codecs", "A GList of FsCodecs of the negotiated codecs for this stream", FS_TYPE_CODEC_LIST, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); /** * FsStream:current-recv-codecs: (type GLib.List(FsCodec)) (transfer full) * * This is the list of codecs that have been received by this stream. * The user must free the list if fs_codec_list_destroy(). * The "farstream-recv-codecs-changed" message is send on the #GstBus * when the value of this property changes. * It is normally emitted right after #FsStream::src-pad-added * only if that codec was not previously received in this stream, but it can * also be emitted if the pad already exists, but the source material that * will come to it is different. */ g_object_class_install_property (gobject_class, PROP_CURRENT_RECV_CODECS, g_param_spec_boxed ("current-recv-codecs", "The codecs currently being received", "A GList of FsCodec representing the codecs that have been received", FS_TYPE_CODEC_LIST, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); /** * FsStream:direction: * * The direction of the stream. This property is set initially as a parameter * to the fs_session_new_stream() function. It can be changed later if * required by setting this property. * */ g_object_class_install_property (gobject_class, PROP_DIRECTION, g_param_spec_flags ("direction", "The direction of the stream", "An enum to set and get the direction of the stream", FS_TYPE_STREAM_DIRECTION, FS_DIRECTION_NONE, G_PARAM_CONSTRUCT | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); /** * FsStream:participant: * * The #FsParticipant for this stream. This property is a construct param and * is read-only construction. * */ g_object_class_install_property (gobject_class, PROP_PARTICIPANT, g_param_spec_object ("participant", "The participant of the stream", "An FsParticipant represented by the stream", FS_TYPE_PARTICIPANT, G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); /** * FsStream:session: * * The #FsSession for this stream. This property is a construct param and * is read-only construction. * */ g_object_class_install_property (gobject_class, PROP_SESSION, g_param_spec_object ("session", "The session of the stream", "An FsSession represented by the stream", FS_TYPE_SESSION, G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); /** * FsStream:decryption-parameters: * * Retrieves previously set decryption parameters */ g_object_class_install_property (gobject_class, PROP_DECRYPTION_PARAMETERS, g_param_spec_boxed ("decryption-parameters", "Decryption parameters", "Parameters used to decrypt the stream", GST_TYPE_STRUCTURE, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); /** * FsStream:require-encryption: * * If set to TRUE, only encrypted content will be accepted on this * stream. */ g_object_class_install_property (gobject_class, PROP_REQUIRE_ENCRYPTION, g_param_spec_boolean ("require-encryption", "Require Encryption", "If TRUE, only encrypted content will be accepted", FALSE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); /** * FsStream::error: * @self: #FsStream that emitted the signal * @errorno: The number of the error * @error_msg: Error message to be displayed to user * * This signal is emitted in any error condition * */ signals[ERROR_SIGNAL] = g_signal_new ("error", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 2, FS_TYPE_ERROR, G_TYPE_STRING); /** * FsStream::src-pad-added: * @self: #FsStream that emitted the signal * @pad: #GstPad of the new source pad * @codec: #FsCodec of the codec being received on the new source pad * * This signal is emitted when a new gst source pad has been created for a * specific codec being received. There will be a different source pad for * each codec that is received. The user must ref the #GstPad if he wants to * keep it. The user should not modify the #FsCodec and must copy it if he * wants to use it outside the callback scope. * * This signal is not emitted on the main thread, but on GStreamer's streaming * thread! * */ signals[SRC_PAD_ADDED] = g_signal_new ("src-pad-added", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 2, GST_TYPE_PAD, FS_TYPE_CODEC); g_type_class_add_private (klass, sizeof (FsStreamPrivate)); } static void fs_stream_init (FsStream *self) { /* member init */ self->priv = FS_STREAM_GET_PRIVATE (self); g_mutex_init (&self->priv->mutex); } static void fs_stream_constructed (GObject *obj) { FsStream *stream = FS_STREAM (obj); FsSession *session; FsConference *conference; g_object_get (stream, "session", &session, NULL); g_object_get (session, "conference", &conference, NULL); g_signal_connect_object (conference, "pad-removed", G_CALLBACK (fs_stream_pad_removed), obj, G_CONNECT_SWAPPED); g_object_unref (session); g_object_unref (conference); } static void fs_stream_finalize (GObject *obj) { FsStream *stream = FS_STREAM (obj); g_list_free_full (stream->priv->src_pads, gst_object_unref); g_mutex_clear (&stream->priv->mutex); G_OBJECT_CLASS (fs_stream_parent_class)->finalize (obj); } static void fs_stream_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) { switch (prop_id) { case PROP_DECRYPTION_PARAMETERS: g_value_set_boxed (value, NULL); /* Not having parameters is valid, in this case set nothing */ break; case PROP_REQUIRE_ENCRYPTION: g_value_set_boxed (value, FALSE); break; default: GST_WARNING ("Subclass %s of FsStream does not override the %s property" " getter", G_OBJECT_TYPE_NAME(object), g_param_spec_get_name (pspec)); break; } } static void fs_stream_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { GST_WARNING ("Subclass %s of FsStream does not override the %s property" " setter", G_OBJECT_TYPE_NAME(object), g_param_spec_get_name (pspec)); } /** * fs_stream_add_remote_candidates: * @stream: an #FsStream * @candidates: (element-type FsCandidate): an #GList of #FsCandidate * representing the remote candidates * @error: location of a #GError, or %NULL if no error occured * * This function adds remote candidates. Any new candidates are * added to the list. The candidates will be used to establish a connection * with the peer. A copy will be made so the user must free the * passed candidate using fs_candidate_destroy() when done. * * Return value: TRUE if the candidate was valid, FALSE otherwise */ gboolean fs_stream_add_remote_candidates (FsStream *stream, GList *candidates, GError **error) { FsStreamClass *klass; g_return_val_if_fail (stream, FALSE); g_return_val_if_fail (FS_IS_STREAM (stream), FALSE); klass = FS_STREAM_GET_CLASS (stream); if (klass->add_remote_candidates) { return klass->add_remote_candidates (stream, candidates, error); } else { g_set_error (error, FS_ERROR, FS_ERROR_NOT_IMPLEMENTED, "add_remote_candidate not defined in class"); } return FALSE; } /** * fs_stream_force_remote_candidates: * @stream: a #FsStream * @remote_candidates: (element-type FsCandidate): * a #GList of #FsCandidate to force * @error: location of a #GError, or %NULL if no error occured * * This function forces data to be sent immediately to the selected remote * candidate, by-passing any connectivity checks. There should be at most * one candidate per component. * * Returns: %TRUE if the candidates could be forced, %FALSE otherwise */ gboolean fs_stream_force_remote_candidates (FsStream *stream, GList *remote_candidates, GError **error) { FsStreamClass *klass; g_return_val_if_fail (stream, FALSE); g_return_val_if_fail (FS_IS_STREAM (stream), FALSE); klass = FS_STREAM_GET_CLASS (stream); if (klass->force_remote_candidates) { return klass->force_remote_candidates (stream, remote_candidates, error); } else { g_set_error (error, FS_ERROR, FS_ERROR_NOT_IMPLEMENTED, "force_remote_candidates not defined in class"); } return FALSE; } /** * fs_stream_set_remote_codecs: * @stream: a #FsStream * @remote_codecs: (element-type FsCodec): 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. */ gboolean fs_stream_set_remote_codecs (FsStream *stream, GList *remote_codecs, GError **error) { FsStreamClass *klass; g_return_val_if_fail (stream, FALSE); g_return_val_if_fail (FS_IS_STREAM (stream), FALSE); klass = FS_STREAM_GET_CLASS (stream); if (klass->set_remote_codecs) { return klass->set_remote_codecs (stream, remote_codecs, error); } else { g_set_error (error, FS_ERROR, FS_ERROR_NOT_IMPLEMENTED, "set_remote_codecs not defined in class"); } return FALSE; } /** * fs_stream_add_id: * @stream: a #FsStream * @id: The id to add to the stream * * This function is used to add data identifiers that allow the * plugin to recognize packets that are meant for id. For example, in RTP, * one would set the SSRCs that are expected. * * Depending on the protocol, one may be able to add more than one ID * to a stream (in RTP you can have multiple SSRCs in a stream). * If a protocol supports only one id, adding a new one will overwrite it. * If an ID was already set on a stream, adding it to another stream will * override the previdous decision. * * For most protocols, calling this function is optional as the incoming data * can be matched with a stream by its source IP address. This is mostly useful * if one is using multicast or is behind a muxer server. */ void fs_stream_add_id (FsStream *stream, guint id) { FsStreamClass *klass; g_return_if_fail (stream); g_return_if_fail (FS_IS_STREAM (stream)); klass = FS_STREAM_GET_CLASS (stream); if (klass->add_id) klass->add_id (stream, id); } /** * fs_stream_emit_error: * @stream: #FsStream on which to emit the error signal * @error_no: The number of the error * @error_msg: Error message to be displayed to user * * This function emits the #FsStream::error" signal, it should only be * called by subclasses. */ void fs_stream_emit_error (FsStream *stream, gint error_no, const gchar *error_msg) { g_signal_emit (stream, signals[ERROR_SIGNAL], 0, error_no, error_msg); } static void fs_stream_pad_removed (FsStream *stream, GstPad *pad) { GList *item; FS_STREAM_LOCK (stream); item = g_list_find (stream->priv->src_pads, pad); if (item) { stream->priv->src_pads = g_list_delete_link (stream->priv->src_pads, item); gst_object_unref (pad); stream->priv->src_pads_cookie++; } FS_STREAM_UNLOCK (stream); } /** * fs_stream_emit_src_pad_added: * @stream: #FsStream on which to emit the src-pad-added signal * @pad: the #GstPad that this #FsStream has created * @codec: The #FsCodec for this pad * * Emits the #FsStream::src-pad-added" signal, it should only be * called by subclasses. */ void fs_stream_emit_src_pad_added (FsStream *stream, GstPad *pad, FsCodec *codec) { FS_STREAM_LOCK (stream); g_assert (!g_list_find (stream->priv->src_pads, pad)); stream->priv->src_pads = g_list_prepend (stream->priv->src_pads, gst_object_ref (pad)); stream->priv->src_pads_cookie++; FS_STREAM_UNLOCK (stream); g_signal_emit (stream, signals[SRC_PAD_ADDED], 0, pad, codec); } /** * fs_stream_iterate_src_pads: * @stream: a #FsStream * * Creates a #GstIterator that can be used to iterate the src pads of this * stream. These are the pads that were announced by #FsStream:src-pad-added * and are still valid. * * Returns: (transfer full): The #GstIterator */ GstIterator * fs_stream_iterate_src_pads (FsStream *stream) { return gst_iterator_new_list (GST_TYPE_PAD, &stream->priv->mutex, &stream->priv->src_pads_cookie, &stream->priv->src_pads, G_OBJECT (stream), NULL); } /** * fs_stream_set_transmitter: * @stream: a #FsStream * @transmitter: Name of the type of transmitter to use for this stream * @stream_transmitter_n_parameters: Number of parametrs passed to the stream * transmitter * @stream_transmitter_parameters: (array length=stream_transmitter_n_parameters) (allow-none): * an array of n_parameters #GParameter struct that will be passed * to the newly-create #FsStreamTransmitter * @error: location of a #GError, or %NULL if no error occured * * Set the transmitter to use for this stream. This function will only succeed * once. * * The parameters correspond to the varios GObject properties of the * selected stream transmitter. * * Returns: %TRUE if the transmitter could be set, %FALSE otherwise */ gboolean fs_stream_set_transmitter (FsStream *stream, const gchar *transmitter, GParameter *stream_transmitter_parameters, guint stream_transmitter_n_parameters, GError **error) { FsStreamClass *klass; g_return_val_if_fail (stream, FALSE); g_return_val_if_fail (FS_IS_STREAM (stream), FALSE); klass = FS_STREAM_GET_CLASS (stream); if (klass->set_transmitter) return klass->set_transmitter (stream, transmitter, stream_transmitter_parameters, stream_transmitter_n_parameters, error); g_set_error (error, FS_ERROR, FS_ERROR_NOT_IMPLEMENTED, "set_transmitter not defined in class"); return FALSE; } /** * fs_stream_set_transmitter_ht: * @stream: a #FsStream * @transmitter: Name of the type of transmitter to use for this stream * @stream_transmitter_parameters: (element-type utf8 GValue) (allow-none): * A #GHashTable of string->GValue containing the parameters. * @error: location of a #GError, or %NULL if no error occured * * Set the transmitter to use for this stream. This function will only succeed * once. * * The parameters correspond to the varios GObject properties of the * selected stream transmitter. * * This is the same as fs_stream_set_transmitter() except that the parameters * are passed in a #GHashTable to make it more friendly to GObject introspection * * Returns: %TRUE if the transmitter could be set, %FALSE otherwise */ gboolean fs_stream_set_transmitter_ht (FsStream *stream, const gchar *transmitter, GHashTable *stream_transmitter_parameters, GError **error) { GParameter *params = NULL; gboolean ret = FALSE; guint n_params = 0; guint i = 0; if (stream_transmitter_parameters && g_hash_table_size (stream_transmitter_parameters) != 0) { FsSession *session = NULL; GType st_type; GObjectClass *st_class = NULL; GHashTableIter iter; gpointer key, value; n_params = g_hash_table_size (stream_transmitter_parameters); g_object_get (stream, "session", &session, NULL); if (!session) { g_set_error_literal (error, FS_ERROR, FS_ERROR_DISPOSED, "Stream has already been disposed"); return FALSE; } st_type = fs_session_get_stream_transmitter_type (session, transmitter); g_object_unref (session); if (st_type == 0) { g_set_error (error, FS_ERROR, FS_ERROR_INVALID_ARGUMENTS, "Unknown transmitter: %s", transmitter); return FALSE; } params = g_new0 (GParameter, n_params); st_class = g_type_class_ref (st_type); g_hash_table_iter_init (&iter, stream_transmitter_parameters); while (g_hash_table_iter_next (&iter, &key, &value)) { GParamSpec *spec; gchar *name = key; const GValue *v = value; spec = g_object_class_find_property (st_class, name); if (!spec) { g_set_error (error, FS_ERROR, FS_ERROR_INVALID_ARGUMENTS, "Unknown argument %s for transmitter %s", name, transmitter); goto end; } params[i].name = name; g_value_init (¶ms[i].value, G_PARAM_SPEC_VALUE_TYPE(spec)); if (!g_value_transform (v, ¶ms[i].value)) { g_set_error (error, FS_ERROR, FS_ERROR_INVALID_ARGUMENTS, "Invalid type of argument %s for transmitter %s", name, transmitter); goto end; } i++; } } ret = fs_stream_set_transmitter (stream, transmitter, params, n_params, error); end: for (i = 0; i < n_params; i++) g_value_unset (¶ms[i].value); g_free (params); return ret; } /** * fs_stream_set_decryption_parameters: * @stream: a #FsStream * @parameters: (transfer none): a #GstStructure containing the decryption * parameters * @error: the location where to store a #GError or %NULL * * Sets decryption parameters. The exact parameters depend on the type of * plugin being used. * * Returns: %TRUE if the decryption parameters could be set, %FALSE otherwise * Since: UNRELEASED */ gboolean fs_stream_set_decryption_parameters (FsStream *stream, GstStructure *parameters, GError **error) { FsStreamClass *klass; g_return_val_if_fail (stream, FALSE); g_return_val_if_fail (FS_IS_STREAM (stream), FALSE); klass = FS_STREAM_GET_CLASS (stream); if (klass->set_decryption_parameters) return klass->set_decryption_parameters (stream, parameters, error); g_set_error (error, FS_ERROR, FS_ERROR_NOT_IMPLEMENTED, "Does not support decryption"); return FALSE; } /** * fs_stream_destroy: * @stream: a #FsStream * * This will cause the stream to remove all links to other objects and to * remove itself from the #FsSession. Once a #FsStream has been destroyed, it * can not be used anymore. * * It is strongly recommended to call this function from the main thread because * releasing the application's reference to a stream. */ void fs_stream_destroy (FsStream *stream) { g_return_if_fail (stream); g_return_if_fail (FS_IS_STREAM (stream)); g_object_run_dispose (G_OBJECT (stream)); } static gboolean check_message (GstMessage *message, FsStream *stream, const gchar *message_name) { const GstStructure *s; const GValue *value; FsStream *message_stream; if (GST_MESSAGE_TYPE (message) != GST_MESSAGE_ELEMENT) return FALSE; s = gst_message_get_structure (message); if (!gst_structure_has_name (s, message_name)) return FALSE; value = gst_structure_get_value (s, "stream"); if (!value || !G_VALUE_HOLDS (value, FS_TYPE_STREAM)) return FALSE; message_stream = g_value_get_object (value); if (stream != message_stream) return FALSE; return TRUE; } /** * fs_stream_parse_new_local_candidate: * @stream: a #FsStream to match against the message * @message: a #GstMessage to parse * @candidate: (out) (transfer none): Returns the #FsCandidate in the message * if not %NULL. * * Parses a "farstream-new-local-candidate" message and checks if it matches * the @stream parameters. * * Returns: %TRUE if the message matches the stream and is valid. */ gboolean fs_stream_parse_new_local_candidate (FsStream *stream, GstMessage *message, FsCandidate **candidate) { const GstStructure *s; const GValue *value; g_return_val_if_fail (stream != NULL, FALSE); if (!check_message (message, stream, "farstream-new-local-candidate")) return FALSE; s = gst_message_get_structure (message); value = gst_structure_get_value (s, "candidate"); if (!value || !G_VALUE_HOLDS (value, FS_TYPE_CANDIDATE)) return FALSE; if (candidate) *candidate = g_value_get_boxed (value); return TRUE; } /** * fs_stream_parse_local_candidates_prepared: * @stream: a #FsStream to match against the message * @message: a #GstMessage to parse * * Parses a "farstream-local-candidates-prepared" message and checks if it matches * the @stream parameters. * * Returns: %TRUE if the message matches the stream and is valid. */ gboolean fs_stream_parse_local_candidates_prepared (FsStream *stream, GstMessage *message) { g_return_val_if_fail (stream != NULL, FALSE); return check_message (message, stream, "farstream-local-candidates-prepared"); } /** * fs_stream_parse_new_active_candidate_pair: * @stream: a #FsStream to match against the message * @message: a #GstMessage to parse * @local_candidate: (out) (transfer none): Returns the local #FsCandidate in * the message if not %NULL. * @remote_candidate: (out) (transfer none): Returns the remote #FsCandidate in * the message if not %NULL. * * Parses a "farstream-new-active-candidate-pair" message and checks * if it matches the @stream parameters. * * Returns: %TRUE if the message matches the stream and is valid. */ gboolean fs_stream_parse_new_active_candidate_pair (FsStream *stream, GstMessage *message, FsCandidate **local_candidate, FsCandidate **remote_candidate) { const GstStructure *s; const GValue *value; g_return_val_if_fail (stream != NULL, FALSE); if (!check_message (message, stream, "farstream-new-active-candidate-pair")) return FALSE; s = gst_message_get_structure (message); value = gst_structure_get_value (s, "local-candidate"); if (!value || !G_VALUE_HOLDS (value, FS_TYPE_CANDIDATE)) return FALSE; if (local_candidate) *local_candidate = g_value_get_boxed (value); value = gst_structure_get_value (s, "remote-candidate"); if (!value || !G_VALUE_HOLDS (value, FS_TYPE_CANDIDATE)) return FALSE; if (remote_candidate) *remote_candidate = g_value_get_boxed (value); return TRUE; } /** * fs_stream_parse_recv_codecs_changed: * @stream: a #FsStream to match against the message * @message: a #GstMessage to parse * @codecs: (out) (transfer none) (element-type FsCodec): * Returns a #GList of #FsCodec of the message if not %NULL * * Parses a "farstream-recv-codecs-changed" message and checks if it matches * the @stream parameters. * * Returns: %TRUE if the message matches the stream and is valid. */ gboolean fs_stream_parse_recv_codecs_changed (FsStream *stream, GstMessage *message, GList **codecs) { const GstStructure *s; const GValue *value; g_return_val_if_fail (stream != NULL, FALSE); if (!check_message (message, stream, "farstream-recv-codecs-changed")) return FALSE; s = gst_message_get_structure (message); value = gst_structure_get_value (s, "codecs"); if (!value || !G_VALUE_HOLDS (value, FS_TYPE_CODEC_LIST)) return FALSE; if (codecs) *codecs = g_value_get_boxed (value); return TRUE; } /** * fs_stream_parse_component_state_changed: * @stream: a #FsStream to match against the message * @message: a #GstMessage to parse * @component: (out): Returns the component from the #GstMessage if not %NULL * @state: (out): Returns the #FsStreamState from the #GstMessage if not %NULL * * Parses a "farstream-component-state-changed" message and checks if it matches * the @stream parameters. * * Returns: %TRUE if the message matches the stream and is valid. */ gboolean fs_stream_parse_component_state_changed (FsStream *stream, GstMessage *message, guint *component, FsStreamState *state) { const GstStructure *s; const GValue *value; g_return_val_if_fail (stream != NULL, FALSE); if (!check_message (message, stream, "farstream-component-state-changed")) return FALSE; s = gst_message_get_structure (message); value = gst_structure_get_value (s, "component"); if (!value || !G_VALUE_HOLDS (value, G_TYPE_UINT)) return FALSE; if (component) *component = g_value_get_uint (value); value = gst_structure_get_value (s, "state"); if (!value || !G_VALUE_HOLDS (value, G_TYPE_ENUM)) return FALSE; if (state) *state = g_value_get_enum (value); return TRUE; }