[farsight2/master] Implement changing the send codec dynamically

Olivier Crête olivier.crete at collabora.co.uk
Tue Dec 23 15:20:04 PST 2008


---
 gst/fsrtpconference/fs-rtp-session.c |  265 ++++++++++++++++++++++++++--------
 1 files changed, 203 insertions(+), 62 deletions(-)

diff --git a/gst/fsrtpconference/fs-rtp-session.c b/gst/fsrtpconference/fs-rtp-session.c
index 398a331..cc26faa 100644
--- a/gst/fsrtpconference/fs-rtp-session.c
+++ b/gst/fsrtpconference/fs-rtp-session.c
@@ -98,10 +98,20 @@ struct _FsRtpSessionPrivate
   GstPad *rtpbin_recv_rtp_sink;
   GstPad *rtpbin_recv_rtcp_sink;
 
+  /* Protected by the session mutex */
+  /* The codec bin is owned implicitely by the Conference bin for us */
+  GstElement *send_codecbin;
+  FsCodec *current_send_codec;
+  FsCodec *requested_send_codec;
+
+  /* Protected by the session mutex */
+  gulong send_blocking_id;
+
   /* These lists are protected by the session mutex */
   GList *streams;
   GList *free_substreams;
 
+  /* The static list of all the blueprints */
   GList *blueprints;
 
   GList *local_codecs_configuration;
@@ -113,9 +123,6 @@ struct _FsRtpSessionPrivate
   GList *negotiated_codecs;
   GHashTable *negotiated_codec_associations;
 
-  /* Protected by the session mutex */
-  FsCodec *requested_send_codec;
-
   GError *construction_error;
 
   GMutex *mutex;
@@ -162,6 +169,10 @@ static gboolean fs_rtp_session_set_send_codec (FsSession *session,
                                                FsCodec *send_codec,
                                                GError **error);
 
+static gboolean fs_rtp_session_verify_send_codec_bin_locked (
+    FsRtpSession *self,
+    GError **error);
+
 
 static FsStreamTransmitter *fs_rtp_session_get_new_stream_transmitter (
   FsRtpSession *self,
@@ -179,9 +190,6 @@ fs_rtp_session_new_recv_codec_bin (FsRtpSession *session,
   GError **error);
 
 
-static gboolean fs_rtp_session_add_send_codec_bin (FsRtpSession *session,
-    GError **error);
-
 
 static GObjectClass *parent_class = NULL;
 
@@ -985,6 +993,7 @@ fs_rtp_session_set_send_codec (FsSession *session, FsCodec *send_codec,
 {
   GList *elem;
   FsRtpSession *self = FS_RTP_SESSION (session);
+  gboolean ret = FALSE;
 
   FS_RTP_SESSION_LOCK (self);
   for (elem = g_list_first (self->priv->negotiated_codecs);
@@ -1000,17 +1009,16 @@ fs_rtp_session_set_send_codec (FsSession *session, FsCodec *send_codec,
 
     self->priv->requested_send_codec = fs_codec_copy (send_codec);
   }
-  FS_RTP_SESSION_UNLOCK (self);
 
-  if (!elem) {
+  if (elem)
+    ret = fs_rtp_session_verify_send_codec_bin_locked (self, error);
+  else
     g_set_error (error, FS_ERROR, FS_ERROR_INVALID_ARGUMENTS,
         "The passed codec is not part of the list of negotiated codecs");
-    return FALSE;
-  }
 
-  /* TODO: We must block to change to change the codecs */
+  FS_RTP_SESSION_UNLOCK (self);
 
-  return TRUE;
+  return ret;
 }
 
 FsRtpSession *
@@ -1395,10 +1403,14 @@ fs_rtp_session_negotiate_codecs (FsRtpSession *session, GList *remote_codecs,
     session->priv->negotiated_codec_associations =
       new_negotiated_codec_associations;
     session->priv->negotiated_codecs = new_negotiated_codecs;
-    FS_RTP_SESSION_UNLOCK (session);
 
-    if (!fs_rtp_session_add_send_codec_bin (session, error))
+    if (!fs_rtp_session_verify_send_codec_bin_locked (session, error))
+    {
+      FS_RTP_SESSION_UNLOCK (session);
       return FALSE;
+    }
+
+    FS_RTP_SESSION_UNLOCK (session);
 
     if (is_new)
       g_signal_emit_by_name (session, "new-negotiated-codec");
@@ -1572,8 +1584,8 @@ _create_ghost_pad (GstElement *current_element, const gchar *padname, GstElement
  */
 
 static GstElement *
-_create_codec_bin (CodecBlueprint *blueprint, FsCodec *codec, const gchar *name,
-  gboolean is_send, GError **error)
+_create_codec_bin (CodecBlueprint *blueprint, const FsCodec *codec,
+    const gchar *name, gboolean is_send, GError **error)
 {
   GList *pipeline_factory = NULL;
   GList *walk = NULL;
@@ -1793,7 +1805,7 @@ fs_rtp_session_select_send_codec_locked (FsRtpSession *session,
   if (!session->priv->negotiated_codecs)
   {
     g_set_error (error, FS_ERROR, FS_ERROR_INTERNAL,
-        "Tried to call fs_rtp_session_build_send_codec_bin before the codec"
+        "Tried to call fs_rtp_session_select_send_codec_bin before the codec"
         " negotiation has taken place");
     return NULL;
   }
@@ -1837,100 +1849,229 @@ fs_rtp_session_select_send_codec_locked (FsRtpSession *session,
   return codec;
 }
 
-/**
- * fs_rtp_session_select_send_codec:
- *
- * This function selects the codec to send using either the user preference
- * or the remote preference (from the negotiation). It is the same as
- * fs_rtp_session_select_send_codec_locked but MT safe.
- *
- * MT safe.
- *
- * Returns: a newly-allocated #FsCodec
- */
-
-static FsCodec *
-fs_rtp_session_select_send_codec (FsRtpSession *session,
-    CodecBlueprint **blueprint,
-    GError **error)
-{
-  FsCodec *codec = NULL;
-
-  FS_RTP_SESSION_LOCK (session);
-  codec = fs_rtp_session_select_send_codec_locked (session, blueprint, error);
-  FS_RTP_SESSION_UNLOCK (session);
-
-  return codec;
-}
-
 
 /**
  * fs_rtp_session_add_send_codec_bin:
+ * @session: a #FsRtpSession
+ * @codec: a #FsCodec
+ * @blueprint: the #CodecBlueprint to use
  *
  * This function creates, adds and links a codec bin for the current send remote
  * codec
  *
  * MT safe.
  *
- * Returns: TRUE on success, FALSE on error
+ * Returns: The new codec bin (or NULL if there is an error)
  */
 
-static gboolean
-fs_rtp_session_add_send_codec_bin (FsRtpSession *session, GError **error)
+static GstElement *
+fs_rtp_session_add_send_codec_bin (FsRtpSession *session,
+    const FsCodec *codec,
+    CodecBlueprint *blueprint,
+    GError **error)
 {
   GstElement *codecbin = NULL;
   gchar *name;
-  gint pt = -1;
-  FsCodec *codec = NULL;
-  CodecBlueprint *blueprint = NULL;
-
-  codec = fs_rtp_session_select_send_codec (session, &blueprint, error);
-  if (!codec)
-    return FALSE;
 
-  name = g_strdup_printf ("send%d", pt);
+  name = g_strdup_printf ("send%d", codec->id);
   codecbin = _create_codec_bin (blueprint, codec, name, TRUE, error);
   g_free (name);
 
   if (!codecbin)
-    return FALSE;
+  {
+    return NULL;
+  }
 
   if (!gst_bin_add (GST_BIN (session->priv->conference), codecbin))
   {
     g_set_error (error, FS_ERROR, FS_ERROR_CONSTRUCTION,
-        "Could not add the send codec bin for pt %d to the pipeline", pt);
+        "Could not add the send codec bin for pt %d to the pipeline",
+        codec->id);
     gst_object_unref (codecbin);
-    return FALSE;
+    return NULL;
   }
 
   if (!gst_element_link_pads (session->priv->media_sink_valve, "src",
           codecbin, "sink"))
   {
     g_set_error (error, FS_ERROR, FS_ERROR_CONSTRUCTION,
-        "Could not link the send valve to the codec bin for pt %d", pt);
+        "Could not link the send valve to the codec bin for pt %d", codec->id);
     goto error;
   }
 
   if (!gst_element_link_pads (codecbin, "src", session->priv->rtpmuxer, NULL))
   {
     g_set_error (error, FS_ERROR, FS_ERROR_CONSTRUCTION,
-        "Could not link the send codec bin for pt %d to the rtp muxer", pt);
+        "Could not link the send codec bin for pt %d to the rtp muxer",
+        codec->id);
     goto error;
   }
 
   if (!gst_element_sync_state_with_parent (codecbin)) {
     g_set_error (error, FS_ERROR, FS_ERROR_CONSTRUCTION,
         "Could not sync the state of the codec bin for pt %d with the state"
-        " of the conference", pt);
+        " of the conference", codec->id);
     goto error;
   }
 
   g_object_set (session->priv->media_sink_valve, "drop", FALSE, NULL);
 
-  return TRUE;
+  return codecbin;
 
  error:
   gst_element_set_state (codecbin, GST_STATE_NULL);
   gst_bin_remove (GST_BIN (session->priv->conference), codecbin);
-  return FALSE;
+  return NULL;
+}
+
+/**
+ * _send_src_pad_have_data_callback:
+ *
+ * This is the pad probe callback on the sink pad of the valve.
+ * It is used to replace the codec bin when the send codec has been changed.
+ *
+ * Its a callback, it returns TRUE to let the data through and FALSE to drop it
+ * (See the "have-data" signal documentation of #GstPad).
+ */
+
+static gboolean
+_send_src_pad_have_data_callback (GstPad *pad, GstMiniObject *miniobj,
+    gpointer user_data)
+{
+  FsRtpSession *self = FS_RTP_SESSION (user_data);
+  FsCodec *codec = NULL;
+  CodecBlueprint *blueprint = NULL;
+  GError *error = NULL;
+  GstElement *codecbin = NULL;
+
+  FS_RTP_SESSION_LOCK (self);
+  codec = fs_rtp_session_select_send_codec_locked(self, &blueprint, &error);
+
+  if (!codec)
+  {
+    fs_session_emit_error (FS_SESSION (self), error->code,
+        "Could not select a new send codec", error->message);
+    goto done;
+  }
+
+  g_clear_error (&error);
+
+  if (fs_codec_are_equal (codec, self->priv->current_send_codec))
+    goto done;
+
+
+  if (gst_element_set_state (self->priv->send_codecbin, GST_STATE_NULL) !=
+      GST_STATE_CHANGE_SUCCESS)
+  {
+    fs_session_emit_error (FS_SESSION (self), FS_ERROR_INTERNAL,
+        "Could not stop the codec bin",
+        "Setting the codec bin to NULL did not succeed" );
+    goto done;
+  }
+
+  gst_bin_remove (GST_BIN (self->priv->conference), self->priv->send_codecbin);
+  self->priv->send_codecbin = NULL;
+
+  fs_codec_destroy (self->priv->current_send_codec);
+  self->priv->send_codecbin = NULL;
+
+
+  codecbin = fs_rtp_session_add_send_codec_bin (self, codec, blueprint,
+      &error);
+
+  if (!codecbin)
+  {
+    fs_session_emit_error (FS_SESSION (self), error->code,
+        "Could not build a new send codec bin", error->message);
+    goto done;
+  }
+
+  self->priv->send_codecbin = codecbin;
+  self->priv->current_send_codec = fs_codec_copy (codec);
+
+ done:
+  if (self->priv->send_blocking_id)
+  {
+    gst_pad_remove_data_probe (pad, self->priv->send_blocking_id);
+    self->priv->send_blocking_id = 0;
+  }
+
+  FS_RTP_SESSION_UNLOCK (self);
+
+  return TRUE;
+
+}
+
+/**
+ * fs_rtp_session_verify_send_codec_bin_locked:
+ *
+ * Verify that the current send codec is still valid and if it is not
+ * do whats required to have the right one be used.
+ *
+ * Must be called with the FsRtpSession lock taken
+ *
+ * Returns: TRUE if it succeeds, FALSE on an error
+ */
+
+static gboolean
+fs_rtp_session_verify_send_codec_bin_locked (FsRtpSession *self, GError **error)
+{
+  FsCodec *codec = NULL;
+  CodecBlueprint *blueprint = NULL;
+  GstElement *codecbin = NULL;
+  gboolean ret = FALSE;
+
+  FS_RTP_SESSION_LOCK (self);
+  codec = fs_rtp_session_select_send_codec_locked(self, &blueprint, error);
+
+  if (!codec)
+    goto done;
+
+  if (self->priv->current_send_codec) {
+    if (fs_codec_are_equal (codec, self->priv->current_send_codec))
+    {
+      ret = TRUE;
+      goto done;
+    }
+
+    /* If we have to change an already made pipeline,
+     * we have to make sure that is it blocked
+     */
+
+    if (!self->priv->send_blocking_id)
+    {
+      GstPad *pad;
+
+      pad = gst_element_get_static_pad (self->priv->media_sink_valve, "src");
+
+      self->priv->send_blocking_id = gst_pad_add_data_probe (pad,
+          G_CALLBACK (_send_src_pad_have_data_callback), self);
+
+      gst_object_unref (pad);
+    }
+  }
+  else
+  {
+    /* The codec does exist yet, lets just create it */
+
+    codecbin = fs_rtp_session_add_send_codec_bin (self, codec, blueprint,
+        error);
+
+    if (codecbin) {
+      self->priv->send_codecbin = codecbin;
+      self->priv->current_send_codec = codec;
+    }
+    else
+    {
+      /* We have an error !! */
+      goto done;
+    }
+  }
+
+  ret = TRUE;
+ done:
+
+  FS_RTP_SESSION_UNLOCK (self);
+
+  return ret;
 }
-- 
1.5.6.5




More information about the farsight-commits mailing list