/* * Farstream - Farstream Multicast UDP Stream Transmitter * * Copyright 2007 Collabora Ltd. * @author: Olivier Crete * Copyright 2007 Nokia Corp. * * fs-multicast-stream-transmitter.c - A Farstream Multiast UDP stream transmitter * * 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-multicast-stream-transmitter * @short_description: A stream transmitter object for Multicast UDP * @see_also: #FsRawUdpStreamTransmitter * * The multicast transmitter allows data to be sent over and received from * multicasted UDP on IPv4. * * This stream transmitter never emits local candidates. It will listen * to the port specified in the remote candidate. And will also send to that * port. It accepts only a single remote candidate per component, if a new one * is given, it will replace the previous one for that component. * * The transmitter will only stop sending to a multicast group when all of its * StreamTransmitters that have this multicast group as destination have their * "sending" property set to false. Multiple stream transmitters can point to * the same multicast groups from the same Transmitter (session), and only one * copy of each packet will be received. * * It will only listen to and send from the IP specified in the * prefered-local-candidates. There can be only one preferred candidate per * component. Only the component_id and the ip will be used from the preferred * local candidates, everything else is ignored. * * Packets sent will be looped back (so that other clients on the same session * can be on the same machine. * * The name of this transmitter is "multicast". */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "fs-multicast-stream-transmitter.h" #include "fs-multicast-transmitter.h" #include #include #include #include #include #ifdef HAVE_UNISTD_H # include #endif #ifdef G_OS_WIN32 # include #else /*G_OS_WIN32*/ # include # include # include # include #endif /*G_OS_WIN32*/ GST_DEBUG_CATEGORY_EXTERN (fs_multicast_transmitter_debug); #define GST_CAT_DEFAULT fs_multicast_transmitter_debug /* Signals */ enum { LAST_SIGNAL }; /* props */ enum { PROP_0, PROP_SENDING, PROP_PREFERRED_LOCAL_CANDIDATES }; struct _FsMulticastStreamTransmitterPrivate { gboolean disposed; /* We don't actually hold a ref to this, * But since our parent FsStream can not exist without its parent * FsSession, we should be safe */ FsMulticastTransmitter *transmitter; GMutex mutex; /* Protected by the mutex */ gboolean sending; /* * We have at most of those per component (index 0 is unused) */ FsCandidate **local_candidate; /* Protected by the mutex */ FsCandidate **remote_candidate; /* Protected by the mutex */ UdpSock **udpsocks; GList *preferred_local_candidates; }; #define FS_MULTICAST_STREAM_TRANSMITTER_GET_PRIVATE(o) \ (G_TYPE_INSTANCE_GET_PRIVATE ((o), FS_TYPE_MULTICAST_STREAM_TRANSMITTER, \ FsMulticastStreamTransmitterPrivate)) #define FS_MULTICAST_STREAM_TRANSMITTER_LOCK(s) \ g_mutex_lock (&(s)->priv->mutex) #define FS_MULTICAST_STREAM_TRANSMITTER_UNLOCK(s) \ g_mutex_unlock (&(s)->priv->mutex) static void fs_multicast_stream_transmitter_class_init (FsMulticastStreamTransmitterClass *klass); static void fs_multicast_stream_transmitter_init (FsMulticastStreamTransmitter *self); static void fs_multicast_stream_transmitter_dispose (GObject *object); static void fs_multicast_stream_transmitter_finalize (GObject *object); static void fs_multicast_stream_transmitter_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec); static void fs_multicast_stream_transmitter_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec); static gboolean fs_multicast_stream_transmitter_force_remote_candidates ( FsStreamTransmitter *streamtransmitter, GList *candidates, GError **error); static GObjectClass *parent_class = NULL; // static guint signals[LAST_SIGNAL] = { 0 }; static GType type = 0; GType fs_multicast_stream_transmitter_get_type (void) { return type; } GType fs_multicast_stream_transmitter_register_type (FsPlugin *module) { static const GTypeInfo info = { sizeof (FsMulticastStreamTransmitterClass), NULL, NULL, (GClassInitFunc) fs_multicast_stream_transmitter_class_init, NULL, NULL, sizeof (FsMulticastStreamTransmitter), 0, (GInstanceInitFunc) fs_multicast_stream_transmitter_init }; type = g_type_module_register_type (G_TYPE_MODULE (module), FS_TYPE_STREAM_TRANSMITTER, "FsMulticastStreamTransmitter", &info, 0); return type; } static void fs_multicast_stream_transmitter_class_init (FsMulticastStreamTransmitterClass *klass) { GObjectClass *gobject_class = (GObjectClass *) klass; FsStreamTransmitterClass *streamtransmitterclass = FS_STREAM_TRANSMITTER_CLASS (klass); parent_class = g_type_class_peek_parent (klass); gobject_class->set_property = fs_multicast_stream_transmitter_set_property; gobject_class->get_property = fs_multicast_stream_transmitter_get_property; streamtransmitterclass->force_remote_candidates = fs_multicast_stream_transmitter_force_remote_candidates; g_object_class_override_property (gobject_class, PROP_SENDING, "sending"); g_object_class_override_property (gobject_class, PROP_PREFERRED_LOCAL_CANDIDATES, "preferred-local-candidates"); gobject_class->dispose = fs_multicast_stream_transmitter_dispose; gobject_class->finalize = fs_multicast_stream_transmitter_finalize; g_type_class_add_private (klass, sizeof (FsMulticastStreamTransmitterPrivate)); } static void fs_multicast_stream_transmitter_init (FsMulticastStreamTransmitter *self) { /* member init */ self->priv = FS_MULTICAST_STREAM_TRANSMITTER_GET_PRIVATE (self); self->priv->disposed = FALSE; self->priv->sending = TRUE; g_mutex_init (&self->priv->mutex); } static void fs_multicast_stream_transmitter_dispose (GObject *object) { FsMulticastStreamTransmitter *self = FS_MULTICAST_STREAM_TRANSMITTER (object); if (self->priv->disposed) /* If dispose did already run, return. */ return; if (self->priv->udpsocks) { if (self->priv->udpsocks[1]) { if (self->priv->sending) fs_multicast_transmitter_udpsock_dec_sending ( self->priv->udpsocks[1]); fs_multicast_transmitter_put_udpsock (self->priv->transmitter, self->priv->udpsocks[1], self->priv->remote_candidate[1]->ttl); self->priv->udpsocks[1] = NULL; } } /* Make sure dispose does not run twice. */ self->priv->disposed = TRUE; parent_class->dispose (object); } static void fs_multicast_stream_transmitter_finalize (GObject *object) { FsMulticastStreamTransmitter *self = FS_MULTICAST_STREAM_TRANSMITTER (object); gint c; /* component_id */ if (self->priv->preferred_local_candidates) { fs_candidate_list_destroy (self->priv->preferred_local_candidates); self->priv->preferred_local_candidates = NULL; } if (self->priv->remote_candidate) { for (c = 1; c <= self->priv->transmitter->components; c++) { if (self->priv->remote_candidate[c]) fs_candidate_destroy (self->priv->remote_candidate[c]); self->priv->remote_candidate[c] = NULL; } g_free (self->priv->remote_candidate); self->priv->remote_candidate = NULL; } if (self->priv->local_candidate) { for (c = 1; c <= self->priv->transmitter->components; c++) { if (self->priv->local_candidate[c]) fs_candidate_destroy (self->priv->local_candidate[c]); self->priv->local_candidate[c] = NULL; } g_free (self->priv->local_candidate); self->priv->local_candidate = NULL; } g_free (self->priv->udpsocks); self->priv->udpsocks = NULL; g_mutex_clear (&self->priv->mutex); parent_class->finalize (object); } static void fs_multicast_stream_transmitter_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) { FsMulticastStreamTransmitter *self = FS_MULTICAST_STREAM_TRANSMITTER (object); switch (prop_id) { case PROP_SENDING: FS_MULTICAST_STREAM_TRANSMITTER_LOCK (self); g_value_set_boolean (value, self->priv->sending); FS_MULTICAST_STREAM_TRANSMITTER_UNLOCK (self); break; case PROP_PREFERRED_LOCAL_CANDIDATES: g_value_set_boxed (value, self->priv->preferred_local_candidates); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void fs_multicast_stream_transmitter_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { FsMulticastStreamTransmitter *self = FS_MULTICAST_STREAM_TRANSMITTER (object); switch (prop_id) { case PROP_SENDING: { gboolean old_sending = self->priv->sending; gboolean sending = g_value_get_boolean (value); FS_MULTICAST_STREAM_TRANSMITTER_LOCK (self); self->priv->sending = sending; if (sending != old_sending) if (self->priv->udpsocks[1]) { guint8 ttl = self->priv->remote_candidate[1]->ttl; fs_multicast_transmitter_udpsock_ref (self->priv->transmitter, self->priv->udpsocks[1], ttl); FS_MULTICAST_STREAM_TRANSMITTER_UNLOCK (self); if (sending) fs_multicast_transmitter_udpsock_inc_sending ( self->priv->udpsocks[1]); else fs_multicast_transmitter_udpsock_dec_sending ( self->priv->udpsocks[1]); fs_multicast_transmitter_put_udpsock (self->priv->transmitter, self->priv->udpsocks[1], ttl); FS_MULTICAST_STREAM_TRANSMITTER_LOCK (self); } FS_MULTICAST_STREAM_TRANSMITTER_UNLOCK (self); } break; case PROP_PREFERRED_LOCAL_CANDIDATES: self->priv->preferred_local_candidates = g_value_dup_boxed (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static gboolean fs_multicast_stream_transmitter_build (FsMulticastStreamTransmitter *self, GError **error) { GList *item; gint c; self->priv->udpsocks = g_new0 (UdpSock *, self->priv->transmitter->components + 1); self->priv->local_candidate = g_new0 (FsCandidate *, self->priv->transmitter->components + 1); self->priv->remote_candidate = g_new0 (FsCandidate *, self->priv->transmitter->components + 1); for (item = g_list_first (self->priv->preferred_local_candidates); item; item = g_list_next (item)) { FsCandidate *candidate = item->data; if (candidate->proto != FS_NETWORK_PROTOCOL_UDP) { g_set_error (error, FS_ERROR, FS_ERROR_INVALID_ARGUMENTS, "You set preferred candidate of a type %d that is not" " FS_NETWORK_PROTOCOL_UDP", candidate->proto); return FALSE; } if (candidate->component_id == 0) { g_set_error (error, FS_ERROR, FS_ERROR_INVALID_ARGUMENTS, "Component id 0 is invalid"); return FALSE; } if (candidate->component_id > self->priv->transmitter->components) { g_set_error (error, FS_ERROR, FS_ERROR_INVALID_ARGUMENTS, "You specified an invalid component id %d with is higher" " than the maximum %d", candidate->component_id, self->priv->transmitter->components); return FALSE; } if (self->priv->local_candidate[candidate->component_id]) { g_set_error (error, FS_ERROR, FS_ERROR_INVALID_ARGUMENTS, "You set more than one preferred local candidate for component %u", candidate->component_id); return FALSE; } if (candidate->ip == NULL) { g_set_error (error, FS_ERROR, FS_ERROR_INVALID_ARGUMENTS, "You have not set the local ip address for the preferred candidate" " for this component"); return FALSE; } self->priv->local_candidate[candidate->component_id] = fs_candidate_copy (candidate); } for (c = 1; c <= self->priv->transmitter->components; c++) { if (!self->priv->local_candidate[c]) { self->priv->local_candidate[c] = fs_candidate_new (NULL, c, FS_CANDIDATE_TYPE_MULTICAST, FS_NETWORK_PROTOCOL_UDP, NULL, 0); } } return TRUE; } static gboolean fs_multicast_stream_transmitter_add_remote_candidate ( FsMulticastStreamTransmitter *self, FsCandidate *candidate, GError **error) { UdpSock *newudpsock = NULL; FS_MULTICAST_STREAM_TRANSMITTER_LOCK (self); if (self->priv->remote_candidate[candidate->component_id]) { FsCandidate *old_candidate = self->priv->remote_candidate[candidate->component_id]; if (old_candidate->port == candidate->port && old_candidate->ttl == candidate->ttl && !strcmp (old_candidate->ip, candidate->ip)) { GST_DEBUG ("Re-set the same candidate, ignoring"); FS_MULTICAST_STREAM_TRANSMITTER_UNLOCK (self); return TRUE; } } FS_MULTICAST_STREAM_TRANSMITTER_UNLOCK (self); /* * IMPROVE ME: We should probably check that the candidate's IP * has the format x.x.x.x where x is [0,255] using GRegex, etc * We should also check if the address is in the multicast range */ newudpsock = fs_multicast_transmitter_get_udpsock ( self->priv->transmitter, candidate->component_id, self->priv->local_candidate[candidate->component_id]->ip, candidate->ip, candidate->port, candidate->ttl, candidate->component_id == 1 ? self->priv->sending : TRUE, error); if (!newudpsock) return FALSE; FS_MULTICAST_STREAM_TRANSMITTER_LOCK (self); if (self->priv->udpsocks[candidate->component_id] && candidate->component_id == 1) { if (self->priv->sending) fs_multicast_transmitter_udpsock_dec_sending ( self->priv->udpsocks[candidate->component_id]); fs_multicast_transmitter_put_udpsock (self->priv->transmitter, self->priv->udpsocks[candidate->component_id], self->priv->remote_candidate[candidate->component_id]->ttl); } self->priv->udpsocks[candidate->component_id] = newudpsock; fs_candidate_destroy (self->priv->remote_candidate[candidate->component_id]); self->priv->remote_candidate[candidate->component_id] = fs_candidate_copy (candidate); FS_MULTICAST_STREAM_TRANSMITTER_UNLOCK (self); g_signal_emit_by_name (self, "new-active-candidate-pair", self->priv->local_candidate[candidate->component_id], self->priv->remote_candidate[candidate->component_id]); return TRUE; } static gboolean fs_multicast_stream_transmitter_force_remote_candidates ( FsStreamTransmitter *streamtransmitter, GList *candidates, GError **error) { GList *item = NULL; FsMulticastStreamTransmitter *self = FS_MULTICAST_STREAM_TRANSMITTER (streamtransmitter); for (item = candidates; item; item = g_list_next (item)) { FsCandidate *candidate = item->data; if (candidate->proto != FS_NETWORK_PROTOCOL_UDP) { g_set_error (error, FS_ERROR, FS_ERROR_INVALID_ARGUMENTS, "You set a candidate of a type %d that is not" " FS_NETWORK_PROTOCOL_UDP", candidate->proto); return FALSE; } if (candidate->type != FS_CANDIDATE_TYPE_MULTICAST) { g_set_error (error, FS_ERROR, FS_ERROR_INVALID_ARGUMENTS, "The remote candidate is not of the right type, it should be" " FS_CANDIDATE_TYPE_MULTICAST (%d), but it is %d", FS_CANDIDATE_TYPE_MULTICAST, candidate->type); return FALSE; } if (!candidate->ip) { g_set_error (error, FS_ERROR, FS_ERROR_INVALID_ARGUMENTS, "The candidate passed does not contain a valid ip"); return FALSE; } if (candidate->component_id == 0 || candidate->component_id > self->priv->transmitter->components) { g_set_error (error, FS_ERROR, FS_ERROR_INVALID_ARGUMENTS, "The candidate passed has an invalid component id %u (not in [1,%u])", candidate->component_id, self->priv->transmitter->components); return FALSE; } if (candidate->ttl == 0) { g_set_error (error, FS_ERROR, FS_ERROR_INVALID_ARGUMENTS, "The TTL for IPv4 multicast candidates must not be 0"); return FALSE; } } for (item = candidates; item; item = g_list_next (item)) if (!fs_multicast_stream_transmitter_add_remote_candidate (self, item->data, error)) return FALSE; return TRUE; } FsMulticastStreamTransmitter * fs_multicast_stream_transmitter_newv (FsMulticastTransmitter *transmitter, guint n_parameters, GParameter *parameters, GError **error) { FsMulticastStreamTransmitter *streamtransmitter = NULL; streamtransmitter = g_object_newv (FS_TYPE_MULTICAST_STREAM_TRANSMITTER, n_parameters, parameters); if (!streamtransmitter) { g_set_error (error, FS_ERROR, FS_ERROR_CONSTRUCTION, "Could not build the stream transmitter"); return NULL; } streamtransmitter->priv->transmitter = transmitter; if (!fs_multicast_stream_transmitter_build (streamtransmitter, error)) { g_object_unref (streamtransmitter); return NULL; } return streamtransmitter; }