Re: FsAppTransmitter / dummy call in nullprpl
David Woodhouse <[email protected]> Sat, 03 Feb 2018 00:45:19 +0000
| Newsgroups | gmane.comp.gnome.gaim.devel,gmane.comp.telephony.farsight.devel |
|---|---|
| Message-ID | <[email protected]> |
On Fri, 2018-02-02 at 13:31 +0000, David Woodhouse wrote:
> Before I do the step of faking RTP myself, I started with purely adding
> rtpopuspay/rtpopusdepay in my pipelines and switching to
> fsrtpconference. And now I'm back to "I am clueless and I don't know
> why this doesn't work mode" and asking for help again... :)
Some progress... attached are a couple of patches against the Pidgin
repository. The first adds fs-app-transmitter and a hack in the null
PRPL which makes a dummy call, and works basically like the code in my
Chime PRPL.
The second converts it to use RTP, and I can get it working with a
couple of hacks.
Firstly, I need to *remove* libgstsrtp.so from my system. Unless I do
that, it fails to work:
0:00:15.265806297 5308 0x5573bd72c830 DEBUG GST_CAPS gstpad.c:2215:gst_pad_link_check_compatible_unlocked:<tee1:src_1> src caps application/x-srtp
0:00:15.265814903 5308 0x5573bd72c830 DEBUG GST_CAPS gstpad.c:2217:gst_pad_link_check_compatible_unlocked:<valve1:sink> sink caps application/x-rtp, media=(string)audio, payload=(int)[ 96, 127 ], clock-rate=(int)48000, encoding-name=(string){ OPUS, X-GST-OPUS-DRAFT-SPITTKA-00 }
0:00:15.265878067 5308 0x5573bd72c830 INFO GST_PADS gstpad.c:2464:gst_pad_link_full: link between tee1:src_1 and valve1:sink failed: no common format
Secondly, I have to upgrade to the latest farstream (I've been testing
on Ubuntu 16.04 with farstream 0.2.7). Unless I do that, I can't find
any way to make OPUS work. Whatever I've put in fs-codecs.conf and
whatever I've put in farstream-{send,recv}-profile properties, I
haven't managed to get it to succeed.
After I've solved those, I'll turn my attention to the fact that
although the nullprpl works with those caveats, I still haven't got my
own code with the "real" GstAppSrc/GstAppSink pipeline working. I get
outbound audio from my mic to the network, but I never see a farstream-
recv-codecs-changed message, and the inbound pipeline isn't set up.
_______________________________________________
Devel mailing list
[email protected]
https://pidgin.im/cgi-bin/mailman/listinfo/devel
0002-Switch-to-RTP.patch
(text/x-patch, 2.4 KB)
From 1f8c566bb506e5db5281c9b23cab3cc7ab22dd21 Mon Sep 17 00:00:00 2001 From: David Woodhouse <[email protected]> Date: Fri, 2 Feb 2018 22:55:26 +0000 Subject: [PATCH 2/2] Switch to RTP --- libpurple/protocols/null/nullprpl.c | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/libpurple/protocols/null/nullprpl.c b/libpurple/protocols/null/nullprpl.c index 7e6cabe..b98c34a 100644 --- a/libpurple/protocols/null/nullprpl.c +++ b/libpurple/protocols/null/nullprpl.c @@ -220,17 +220,17 @@ static void nullprpl_dummy_call(PurplePluginAction *action) PurpleMediaManager *mgr = purple_media_manager_get(); PurpleMedia *media = purple_media_manager_create_media(mgr, acct, - "fsrawconference", + "fsrtpconference", name, TRUE); - const gchar *caps = "audio/x-raw,format=(string)S16LE,layout=(string)interleaved,channels=(int)1"; + const gchar *caps = "application/x-rtp,media=(string)audio,payload=(int)96,encoding-name=(string)OPUS"; purple_media_add_stream(media, sess, name, PURPLE_MEDIA_AUDIO, TRUE, "app", 0, NULL); /* Without the capsfilter (even though there's another capsfilter almost immediately after it in the pipeline, it doesn't work */ - srcpipe = g_strdup_printf("audiotestsrc format=time ! capsfilter caps=%s", caps); - sinkpipe = g_strdup_printf("audioconvert ! filesink location=/tmp/pidgin.s16 async=false"); + srcpipe = g_strdup_printf("audiotestsrc ! opusenc ! rtpopuspay ! capsfilter caps=%s ", caps); + sinkpipe = g_strdup_printf("capsfilter caps=%s ! rtpopusdepay ! opusdec ! filesink location=/tmp/pidgin.s16 async=false", caps); cand = purple_media_candidate_new(NULL, 1, PURPLE_MEDIA_CANDIDATE_TYPE_HOST, @@ -242,7 +242,10 @@ static void nullprpl_dummy_call(PurplePluginAction *action) cands = g_list_append (NULL, cand); codecs = g_list_append(NULL, - purple_media_codec_new(1, caps, PURPLE_MEDIA_AUDIO, 0)); + purple_media_codec_new(96, "OPUS", PURPLE_MEDIA_AUDIO, 0)); + purple_media_codec_add_optional_parameter(codecs->data, "farstream-recv-profile", "rtpopusdepay payload=96 ! opusdec"); + purple_media_codec_add_optional_parameter(codecs->data, "farstream-send-profile", "opusenc bitrate=16000 bitrate-type=vbr ! rtpopuspay payload=96"); + purple_media_add_remote_candidates(media, sess, name, cands); purple_media_set_remote_codecs(media, sess, name, codecs); -- 2.7.4
0001-Dummy-conference-hack-to-nullprpl.patch
(text/x-patch, 52.5 KB)
From becbea54ea43c09698abc3a9264a08a246d73b61 Mon Sep 17 00:00:00 2001 From: David Woodhouse <[email protected]> Date: Thu, 2 Nov 2017 14:55:26 +0000 Subject: [PATCH 1/2] Dummy conference hack to nullprpl --- configure.ac | 2 + libpurple/media/backend-fs2.c | 12 + libpurple/protocols/null/Makefile.am | 14 +- .../protocols/null/fs-app-stream-transmitter.c | 458 ++++++++++ .../protocols/null/fs-app-stream-transmitter.h | 98 +++ libpurple/protocols/null/fs-app-transmitter.c | 924 +++++++++++++++++++++ libpurple/protocols/null/fs-app-transmitter.h | 124 +++ libpurple/protocols/null/nullprpl.c | 60 +- 8 files changed, 1688 insertions(+), 4 deletions(-) create mode 100644 libpurple/protocols/null/fs-app-stream-transmitter.c create mode 100644 libpurple/protocols/null/fs-app-stream-transmitter.h create mode 100644 libpurple/protocols/null/fs-app-transmitter.c create mode 100644 libpurple/protocols/null/fs-app-transmitter.h diff --git a/configure.ac b/configure.ac index 71b9f7b..6ccf603 100644 --- a/configure.ac +++ b/configure.ac @@ -871,6 +871,8 @@ if test "x$enable_farstream" != "xno"; then PKG_CHECK_MODULES(FARSTREAM, [farstream-0.2 >= 0.2.7], [ AC_SUBST(FARSTREAM_CFLAGS) AC_SUBST(FARSTREAM_LIBS) + fsplugindir="$($PKG_CONFIG --variable=pluginsdir farstream-0.2)" + AC_SUBST(fsplugindir, ${fsplugindir}) ], [ enable_farstream="no" ]) diff --git a/libpurple/media/backend-fs2.c b/libpurple/media/backend-fs2.c index 47ba4bc..2edfc84 100644 --- a/libpurple/media/backend-fs2.c +++ b/libpurple/media/backend-fs2.c @@ -994,6 +994,8 @@ gst_handle_message_element(GstBus *bus, GstMessage *msg, static guint level_id = 0; const GstStructure *structure = gst_message_get_structure(msg); + printf("%s %s\n", __func__, gst_structure_get_name(structure)); + if (level_id == 0) level_id = g_signal_lookup("level", PURPLE_TYPE_MEDIA); @@ -1588,6 +1590,7 @@ stream_info_cb(PurpleMedia *media, PurpleMediaInfoType type, gchar *sid, gchar *name, gboolean local, PurpleMediaBackendFs2 *self) { + printf("stream_info_cb %d\n", type); if (type == PURPLE_MEDIA_INFO_ACCEPT && sid != NULL && name != NULL) { PurpleMediaBackendFs2Stream *stream = get_stream(self, sid, name); @@ -2868,6 +2871,15 @@ purple_media_backend_fs2_send_dtmf(PurpleMediaBackend *self, PurpleMediaBackendFs2Session *session; FsDTMFEvent event; + printf("DUMP DOT FILE\n"); + GstElement *pipeline; + PurpleMediaBackendFs2Private *priv = + PURPLE_MEDIA_BACKEND_FS2_GET_PRIVATE(self); + + pipeline = purple_media_manager_get_pipeline( + purple_media_get_manager(priv->media)); + GST_DEBUG_BIN_TO_DOT_FILE(GST_BIN(pipeline), GST_DEBUG_GRAPH_SHOW_ALL, "conference graph"); + g_return_val_if_fail(PURPLE_IS_MEDIA_BACKEND_FS2(self), FALSE); session = get_session(PURPLE_MEDIA_BACKEND_FS2(self), sess_id); diff --git a/libpurple/protocols/null/Makefile.am b/libpurple/protocols/null/Makefile.am index 3bc76ee..07cba59 100644 --- a/libpurple/protocols/null/Makefile.am +++ b/libpurple/protocols/null/Makefile.am @@ -14,10 +14,22 @@ libnull_la_LDFLAGS = -module -avoid-version st = pkg_LTLIBRARIES = libnull.la libnull_la_SOURCES = $(NULLSOURCES) -libnull_la_LIBADD = $(GLIB_LIBS) +libnull_la_LIBADD = $(GLIB_LIBS) $(FARSTREAM_LIBS) + +fsplugin_LTLIBRARIES = libapp-transmitter.la + +libapp_transmitter_la_CPPFLAGS = $(FARSTREAM_CFLAGS) + +libapp_transmitter_la_SOURCES = fs-app-stream-transmitter.c fs-app-stream-transmitter.h \ + fs-app-transmitter.c fs-app-transmitter.h + +libapp_transmitter_la_LDFLAGS = -module -avoid-version -no-undefined + +libapp_transmitter_la_LIBADD = $(FARSTREAM_LIBS) AM_CPPFLAGS = \ -I$(top_srcdir)/libpurple \ -I$(top_builddir)/libpurple \ + $(FARSTREAM_CFLAGS) \ $(GLIB_CFLAGS) \ $(DEBUG_CFLAGS) diff --git a/libpurple/protocols/null/fs-app-stream-transmitter.c b/libpurple/protocols/null/fs-app-stream-transmitter.c new file mode 100644 index 0000000..3ffa934 --- /dev/null +++ b/libpurple/protocols/null/fs-app-stream-transmitter.c @@ -0,0 +1,458 @@ +/* + * Farstream - Farstream Shared Memory Stream Transmitter + * + * Copyright 2009 Collabora Ltd. + * @author: Olivier Crete <[email protected]> + * Copyright 2009 Nokia Corp. + * + * fs-app-stream-transmitter.c - A Farstream GstAppShared memory 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-app-stream-transmitter + * @short_description: A stream transmitter object for application pipelines + * + * The name of this transmitter is "app". + * + * This transmitter is meant to send and receive the data via a pipeline + * described by the application. The pipelines will typically start/end + * in an appsrc/appsink respectively, but a full pipeline description is + * required because codec and other conversions may be necessary when + * used with #FsRawConference, which does not handle those for itself. + * + * The pipelines for send (e.g. to appsink) and receive (e.g. from appsrc) + * are provided in the "ip" and "username" properties of the #FsCandidate, + * respectively. These pipelines are instantiated with + * gst_parse_bin_from_description(). + * + * Typically the application would give a unique name to the appsrc and + * appsink elements, so that gst_bin_get_by_name() can be used to find + * and interact with them. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "fs-app-stream-transmitter.h" +#include "fs-app-transmitter.h" + +#include <farstream/fs-candidate.h> +#include <farstream/fs-conference.h> + +#include <gst/gst.h> + +#include <glib/gstdio.h> + +#include <string.h> +#include <sys/types.h> + +#ifdef HAVE_UNISTD_H +# include <unistd.h> +#endif + +#include <stdlib.h> + +GST_DEBUG_CATEGORY_EXTERN (fs_app_transmitter_debug); +#define GST_CAT_DEFAULT fs_app_transmitter_debug + +/* Signals */ +enum +{ + LAST_SIGNAL +}; + +/* props */ +enum +{ + PROP_0, + PROP_SENDING, +}; + +struct _FsAppStreamTransmitterPrivate +{ + /* 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 + */ + FsAppTransmitter *transmitter; + + GMutex mutex; + + /* Protected by the mutex */ + gboolean sending; + + /* Protected by the mutex */ + FsCandidate **candidates; + + AppSrc **app_src; + AppSink **app_sink; +}; + +#define FS_APP_STREAM_TRANSMITTER_GET_PRIVATE(o) \ + (G_TYPE_INSTANCE_GET_PRIVATE ((o), FS_TYPE_APP_STREAM_TRANSMITTER, \ + FsAppStreamTransmitterPrivate)) + +#define FS_APP_STREAM_TRANSMITTER_LOCK(s) \ + g_mutex_lock (&(s)->priv->mutex) +#define FS_APP_STREAM_TRANSMITTER_UNLOCK(s) \ + g_mutex_unlock (&(s)->priv->mutex) + +static void fs_app_stream_transmitter_class_init (FsAppStreamTransmitterClass *klass); +static void fs_app_stream_transmitter_init (FsAppStreamTransmitter *self); +static void fs_app_stream_transmitter_dispose (GObject *object); +static void fs_app_stream_transmitter_finalize (GObject *object); + +static void fs_app_stream_transmitter_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec); +static void fs_app_stream_transmitter_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec); + +static gboolean fs_app_stream_transmitter_force_remote_candidates ( + FsStreamTransmitter *streamtransmitter, GList *candidates, + GError **error); + +static gboolean +fs_app_stream_transmitter_add_sink (FsAppStreamTransmitter *self, + FsCandidate *candidate, GError **error); + + +static GObjectClass *parent_class = NULL; +// static guint signals[LAST_SIGNAL] = { 0 }; + +static GType type = 0; + +GType +fs_app_stream_transmitter_get_type (void) +{ + return type; +} + +GType +fs_app_stream_transmitter_register_type (FsPlugin *module G_GNUC_UNUSED) +{ + static const GTypeInfo info = { + sizeof (FsAppStreamTransmitterClass), + NULL, + NULL, + (GClassInitFunc) fs_app_stream_transmitter_class_init, + NULL, + NULL, + sizeof (FsAppStreamTransmitter), + 0, + (GInstanceInitFunc) fs_app_stream_transmitter_init + }; + + type = g_type_register_static (FS_TYPE_STREAM_TRANSMITTER, + "FsAppStreamTransmitter", &info, 0); + + return type; +} + +static void +fs_app_stream_transmitter_class_init (FsAppStreamTransmitterClass *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_app_stream_transmitter_set_property; + gobject_class->get_property = fs_app_stream_transmitter_get_property; + + streamtransmitterclass->force_remote_candidates = + fs_app_stream_transmitter_force_remote_candidates; + + g_object_class_override_property (gobject_class, PROP_SENDING, "sending"); + + gobject_class->dispose = fs_app_stream_transmitter_dispose; + gobject_class->finalize = fs_app_stream_transmitter_finalize; + + g_type_class_add_private (klass, sizeof (FsAppStreamTransmitterPrivate)); +} + +static void +fs_app_stream_transmitter_init (FsAppStreamTransmitter *self) +{ + /* member init */ + self->priv = FS_APP_STREAM_TRANSMITTER_GET_PRIVATE (self); + + self->priv->sending = TRUE; + + g_mutex_init (&self->priv->mutex); +} + +static void +fs_app_stream_transmitter_dispose (GObject *object) +{ + FsAppStreamTransmitter *self = FS_APP_STREAM_TRANSMITTER (object); + gint c; /* component_id */ + + for (c = 1; c <= self->priv->transmitter->components; c++) + { + if (self->priv->app_src[c]) + { + fs_app_transmitter_check_app_src (self->priv->transmitter, + self->priv->app_src[c], NULL); + } + self->priv->app_src[c] = NULL; + + if (self->priv->app_sink[c]) + { + fs_app_transmitter_check_app_sink (self->priv->transmitter, + self->priv->app_sink[c], NULL); + } + self->priv->app_sink[c] = NULL; + } + + parent_class->dispose (object); +} + +static void +fs_app_stream_transmitter_finalize (GObject *object) +{ + FsAppStreamTransmitter *self = FS_APP_STREAM_TRANSMITTER (object); + + g_free (self->priv->app_src); + g_free (self->priv->app_sink); + g_mutex_clear (&self->priv->mutex); + + parent_class->finalize (object); +} + +static void +fs_app_stream_transmitter_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + FsAppStreamTransmitter *self = FS_APP_STREAM_TRANSMITTER (object); + + switch (prop_id) + { + case PROP_SENDING: + FS_APP_STREAM_TRANSMITTER_LOCK (self); + g_value_set_boolean (value, self->priv->sending); + FS_APP_STREAM_TRANSMITTER_UNLOCK (self); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +fs_app_stream_transmitter_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + FsAppStreamTransmitter *self = FS_APP_STREAM_TRANSMITTER (object); + + switch (prop_id) { + case PROP_SENDING: + FS_APP_STREAM_TRANSMITTER_LOCK (self); + self->priv->sending = g_value_get_boolean (value); + if (self->priv->app_sink[1]) + fs_app_transmitter_sink_set_sending (self->priv->transmitter, + self->priv->app_sink[1], self->priv->sending); + FS_APP_STREAM_TRANSMITTER_UNLOCK (self); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static gboolean +fs_app_stream_transmitter_build (FsAppStreamTransmitter *self, + GError **error) +{ + self->priv->app_src = g_new0 (AppSrc *, + self->priv->transmitter->components + 1); + self->priv->app_sink = g_new0 (AppSink *, + self->priv->transmitter->components + 1); + + return TRUE; +} + +static void +got_buffer_func (GstBuffer *buffer, guint component, gpointer data) +{ + FsAppStreamTransmitter *self = FS_APP_STREAM_TRANSMITTER_CAST (data); + + g_signal_emit_by_name (self, "known-source-packet-received", component, + buffer); +} + +static void ready_cb (guint component, gchar *pipeline, gpointer data) +{ + FsAppStreamTransmitter *self = FS_APP_STREAM_TRANSMITTER_CAST (data); + FsCandidate *candidate = fs_candidate_new ("", component, + FS_CANDIDATE_TYPE_HOST, FS_NETWORK_PROTOCOL_UDP, pipeline, 0); + + printf ("Emitting new local candidate with pipeline %s: %p\n", pipeline, candidate); + + g_signal_emit_by_name (self, "new-local-candidate", candidate); + g_signal_emit_by_name (self, "local-candidates-prepared"); + + fs_candidate_destroy (candidate); +} + +static gboolean +fs_app_stream_transmitter_add_sink (FsAppStreamTransmitter *self, + FsCandidate *candidate, GError **error) +{ + if (!candidate->ip || !candidate->ip[0]) + return TRUE; + + if (self->priv->app_sink[candidate->component_id]) + { + if (fs_app_transmitter_check_app_sink (self->priv->transmitter, + self->priv->app_sink[candidate->component_id], candidate->ip)) + return TRUE; + self->priv->app_sink[candidate->component_id] = NULL; + } + + self->priv->app_sink[candidate->component_id] = + fs_app_transmitter_get_app_sink (self->priv->transmitter, + candidate->component_id, candidate->ip, ready_cb, self, error); + + if (self->priv->app_sink[candidate->component_id] == NULL) + return FALSE; + + return TRUE; +} + + +static void +disconnected_cb (guint component, gint id, gpointer data) +{ + FsAppStreamTransmitter *self = data; + + g_signal_emit_by_name (self, "state-changed", component, + FS_STREAM_STATE_FAILED); +} + +static gboolean +fs_app_stream_transmitter_force_remote_candidate ( + FsAppStreamTransmitter *self, FsCandidate *candidate, + GError **error) +{ + const gchar *pipeline; + if (!fs_app_stream_transmitter_add_sink (self, candidate, error)) + return FALSE; + + pipeline = candidate->username; + + if (pipeline && pipeline[0]) + { + if (self->priv->app_src[candidate->component_id]) + { + if (fs_app_transmitter_check_app_src (self->priv->transmitter, + self->priv->app_src[candidate->component_id], pipeline)) + return TRUE; + self->priv->app_src[candidate->component_id] = NULL; + } + + self->priv->app_src[candidate->component_id] = + fs_app_transmitter_get_app_src (self->priv->transmitter, + candidate->component_id, pipeline, got_buffer_func, disconnected_cb, + self, error); + + if (self->priv->app_src[candidate->component_id] == NULL) + return FALSE; + } + + return TRUE; +} + +/** + * fs_app_stream_transmitter_force_remote_candidates + */ + +static gboolean +fs_app_stream_transmitter_force_remote_candidates ( + FsStreamTransmitter *streamtransmitter, GList *candidates, + GError **error) +{ + GList *item = NULL; + FsAppStreamTransmitter *self = + FS_APP_STREAM_TRANSMITTER (streamtransmitter); + + for (item = candidates; item; item = g_list_next (item)) + { + FsCandidate *candidate = item->data; + + 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->ip || !candidate->ip[0]) && + (!candidate->username || !candidate->username[0])) + { + g_set_error (error, FS_ERROR, FS_ERROR_INVALID_ARGUMENTS, + "The candidate does not have a SINK pipeline in its ip" + " or a SRC pipeline in its username"); + return FALSE; + } + } + + for (item = candidates; item; item = g_list_next (item)) + if (!fs_app_stream_transmitter_force_remote_candidate (self, + item->data, error)) + return FALSE; + + + return TRUE; +} + + +FsAppStreamTransmitter * +fs_app_stream_transmitter_newv (FsAppTransmitter *transmitter, + guint n_parameters, GParameter *parameters, GError **error) +{ + FsAppStreamTransmitter *streamtransmitter = NULL; + + streamtransmitter = g_object_newv (FS_TYPE_APP_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_app_stream_transmitter_build (streamtransmitter, error)) { + g_object_unref (streamtransmitter); + return NULL; + } + + return streamtransmitter; +} diff --git a/libpurple/protocols/null/fs-app-stream-transmitter.h b/libpurple/protocols/null/fs-app-stream-transmitter.h new file mode 100644 index 0000000..049f453 --- /dev/null +++ b/libpurple/protocols/null/fs-app-stream-transmitter.h @@ -0,0 +1,98 @@ +/* + * Farstream - Farstream Shared Memory Stream Transmitter + * + * Copyright 2007-2008 Collabora Ltd. + * @author: Olivier Crete <[email protected]> + * Copyright 2007-2008 Nokia Corp. + * + * fs-app-stream-transmitter.h - A Farstream Shared Memory 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 + */ + +#ifndef __FS_APP_STREAM_TRANSMITTER_H__ +#define __FS_APP_STREAM_TRANSMITTER_H__ + +#include <glib.h> +#include <glib-object.h> + +#include <farstream/fs-stream-transmitter.h> +#include <farstream/fs-plugin.h> +#include "fs-app-transmitter.h" + +G_BEGIN_DECLS + +/* TYPE MACROS */ +#define FS_TYPE_APP_STREAM_TRANSMITTER \ + (fs_app_stream_transmitter_get_type ()) +#define FS_APP_STREAM_TRANSMITTER(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj), FS_TYPE_APP_STREAM_TRANSMITTER, \ + FsAppStreamTransmitter)) +#define FS_APP_STREAM_TRANSMITTER_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass), FS_TYPE_APP_STREAM_TRANSMITTER, \ + FsAppStreamTransmitterClass)) +#define FS_IS_APP_STREAM_TRANSMITTER(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj), FS_TYPE_APP_STREAM_TRANSMITTER)) +#define FS_IS_APP_STREAM_TRANSMITTER_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE((klass), FS_TYPE_APP_STREAM_TRANSMITTER)) +#define FS_APP_STREAM_TRANSMITTER_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS ((obj), FS_TYPE_APP_STREAM_TRANSMITTER, \ + FsAppStreamTransmitterClass)) +#define FS_APP_STREAM_TRANSMITTER_CAST(obj) ((FsAppStreamTransmitter *) (obj)) + +typedef struct _FsAppStreamTransmitter FsAppStreamTransmitter; +typedef struct _FsAppStreamTransmitterClass FsAppStreamTransmitterClass; +typedef struct _FsAppStreamTransmitterPrivate FsAppStreamTransmitterPrivate; + +/** + * FsAppStreamTransmitterClass: + * @parent_class: Our parent + * + * The Shared Memory stream transmitter class + */ + +struct _FsAppStreamTransmitterClass +{ + FsStreamTransmitterClass parent_class; + + /*virtual functions */ + /*< private >*/ +}; + +/** + * FsAppStreamTransmitter: + * @parent: Parent object + * + * All members are private, access them using methods and properties + */ +struct _FsAppStreamTransmitter +{ + FsStreamTransmitter parent; + + /*< private >*/ + FsAppStreamTransmitterPrivate *priv; +}; + +GType fs_app_stream_transmitter_register_type (FsPlugin *module); + +GType fs_app_stream_transmitter_get_type (void); + +FsAppStreamTransmitter * +fs_app_stream_transmitter_newv (FsAppTransmitter *transmitter, + guint n_parameters, GParameter *parameters, GError **error); + +G_END_DECLS + +#endif /* __FS_APP_STREAM_TRANSMITTER_H__ */ diff --git a/libpurple/protocols/null/fs-app-transmitter.c b/libpurple/protocols/null/fs-app-transmitter.c new file mode 100644 index 0000000..87cb5ec --- /dev/null +++ b/libpurple/protocols/null/fs-app-transmitter.c @@ -0,0 +1,924 @@ +/* + * Farstream - Farstream App UDP Transmitter + * + * Copyright 2007-2008 Collabora Ltd. + * @author: Olivier Crete <[email protected]> + * Copyright 2007-2008 Nokia Corp. + * + * fs-app-transmitter.c - A Farstream appsink/appsrc 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-app-transmitter + * @short_description: A transmitter for appsink/appsrc I/O + * + * This transmitter provides appsink/appsrc I/O + * + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "fs-app-transmitter.h" +#include "fs-app-stream-transmitter.h" + +#include <farstream/fs-conference.h> +#include <farstream/fs-plugin.h> + +#include <string.h> +#include <stdio.h> + +GST_DEBUG_CATEGORY (fs_app_transmitter_debug); +#define GST_CAT_DEFAULT fs_app_transmitter_debug + +/* Signals */ +enum +{ + LAST_SIGNAL +}; + +/* props */ +enum +{ + PROP_0, + PROP_GST_SINK, + PROP_GST_SRC, + PROP_COMPONENTS, + PROP_DO_TIMESTAMP, +}; + +struct _FsAppTransmitterPrivate +{ + /* We hold references to this element */ + GstElement *gst_sink; + GstElement *gst_src; + + /* We don't hold a reference to these elements, they are owned + by the bins */ + /* They are tables of pointers, one per component */ + GstElement **funnels; + GstElement **tees; + + gboolean do_timestamp; +}; + +#define FS_APP_TRANSMITTER_GET_PRIVATE(o) \ + (G_TYPE_INSTANCE_GET_PRIVATE ((o), FS_TYPE_APP_TRANSMITTER, \ + FsAppTransmitterPrivate)) + +static void fs_app_transmitter_class_init ( + FsAppTransmitterClass *klass); +static void fs_app_transmitter_init (FsAppTransmitter *self); +static void fs_app_transmitter_constructed (GObject *object); +static void fs_app_transmitter_dispose (GObject *object); +static void fs_app_transmitter_finalize (GObject *object); + +static void fs_app_transmitter_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec); +static void fs_app_transmitter_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec); + +static FsStreamTransmitter *fs_app_transmitter_new_stream_transmitter ( + FsTransmitter *transmitter, FsParticipant *participant, + guint n_parameters, GParameter *parameters, GError **error); +static GType fs_app_transmitter_get_stream_transmitter_type ( + FsTransmitter *transmitter); + + +static GObjectClass *parent_class = NULL; +//static guint signals[LAST_SIGNAL] = { 0 }; + +/* + * Private bin subclass + */ + +enum { + BIN_SIGNAL_READY, + BIN_SIGNAL_DISCONNECTED, + BIN_LAST_SIGNAL +}; + +static guint bin_signals[BIN_LAST_SIGNAL] = { 0 }; +static GType app_bin_type = 0; +gpointer app_bin_parent_class = NULL; + +typedef struct _FsAppBin +{ + GstBin parent; +} FsAppBin; + +typedef struct _FsAppBinClass +{ + GstBinClass parent_class; +} FsAppBinClass; + +static void fs_app_bin_init (FsAppBin *self) +{ +} + +static GstElement * +fs_app_bin_new (void) +{ + return g_object_new (app_bin_type, NULL); +} + +static void +fs_app_bin_handle_message (GstBin *bin, GstMessage *message) +{ + GstState old, new, pending; + GError *gerror; + gchar *msg; + + switch (GST_MESSAGE_TYPE (message)) + { + case GST_MESSAGE_STATE_CHANGED: + gst_message_parse_state_changed (message, &old, &new, &pending); + + if (old == GST_STATE_PAUSED && new == GST_STATE_PLAYING) + g_signal_emit (bin, bin_signals[BIN_SIGNAL_READY], 0, + GST_MESSAGE_SRC (message)); + break; + case GST_MESSAGE_ERROR: + gst_message_parse_error (message, &gerror, &msg); + + if (g_error_matches (gerror, GST_RESOURCE_ERROR, + GST_RESOURCE_ERROR_READ)) + { + g_signal_emit (bin, bin_signals[BIN_SIGNAL_DISCONNECTED], 0, + GST_MESSAGE_SRC (message)); + gst_message_unref (message); + return; + } + break; + default: + break; + } + + GST_BIN_CLASS (app_bin_parent_class)->handle_message (bin, message); +} + +static void fs_app_bin_class_init (FsAppBinClass *klass) +{ + GstBinClass *bin_class = GST_BIN_CLASS (klass); + + app_bin_parent_class = g_type_class_peek_parent (klass); + + bin_signals[BIN_SIGNAL_READY] = + g_signal_new ("ready", G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, 0, NULL, NULL, + g_cclosure_marshal_VOID__OBJECT, G_TYPE_NONE, 1, GST_TYPE_ELEMENT); + + bin_signals[BIN_SIGNAL_DISCONNECTED] = + g_signal_new ("disconnected", G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, 0, NULL, NULL, + g_cclosure_marshal_VOID__OBJECT, G_TYPE_NONE, 1, GST_TYPE_ELEMENT); + + bin_class->handle_message = GST_DEBUG_FUNCPTR (fs_app_bin_handle_message); +} + +/* + * Lets register the plugin + */ + +static GType type = 0; + +GType +fs_app_transmitter_get_type (void) +{ + g_assert (type); + return type; +} + +GType +fs_app_transmitter_register_type (FsPlugin *module); +GType +fs_app_transmitter_register_type (FsPlugin *module) +{ + static const GTypeInfo info = { + sizeof (FsAppTransmitterClass), + NULL, + NULL, + (GClassInitFunc) fs_app_transmitter_class_init, + NULL, + NULL, + sizeof (FsAppTransmitter), + 0, + (GInstanceInitFunc) fs_app_transmitter_init + }; + + static const GTypeInfo bin_info = { + sizeof (FsAppBinClass), + NULL, + NULL, + (GClassInitFunc) fs_app_bin_class_init, + NULL, + NULL, + sizeof (FsAppBin), + 0, + (GInstanceInitFunc) fs_app_bin_init + }; + + + GST_DEBUG_CATEGORY_INIT (fs_app_transmitter_debug, + "fsapptransmitter", 0, + "Farstream app UDP transmitter"); + + fs_app_stream_transmitter_register_type (module); + + type = g_type_register_static (FS_TYPE_TRANSMITTER, "FsAppTransmitter", + &info, 0); + + app_bin_type = g_type_register_static ( + GST_TYPE_BIN, "FsAppBin", &bin_info, 0); + + return type; +} + +#ifdef _FS_REGISTER_TYPE +FS_INIT_PLUGIN (app, transmitter) +#else /* <= 0.2.7 */ +FS_INIT_PLUGIN (fs_app_transmitter_register_type) +#endif + +static void +fs_app_transmitter_class_init (FsAppTransmitterClass *klass) +{ + GObjectClass *gobject_class = (GObjectClass *) klass; + FsTransmitterClass *transmitter_class = FS_TRANSMITTER_CLASS (klass); + + parent_class = g_type_class_peek_parent (klass); + + gobject_class->set_property = fs_app_transmitter_set_property; + gobject_class->get_property = fs_app_transmitter_get_property; + + gobject_class->constructed = fs_app_transmitter_constructed; + + g_object_class_override_property (gobject_class, PROP_GST_SRC, "gst-src"); + g_object_class_override_property (gobject_class, PROP_GST_SINK, "gst-sink"); + g_object_class_override_property (gobject_class, PROP_COMPONENTS, + "components"); + g_object_class_override_property (gobject_class, PROP_DO_TIMESTAMP, + "do-timestamp"); + + transmitter_class->new_stream_transmitter = + fs_app_transmitter_new_stream_transmitter; + transmitter_class->get_stream_transmitter_type = + fs_app_transmitter_get_stream_transmitter_type; + + gobject_class->dispose = fs_app_transmitter_dispose; + gobject_class->finalize = fs_app_transmitter_finalize; + + g_type_class_add_private (klass, sizeof (FsAppTransmitterPrivate)); +} + +static void +fs_app_transmitter_init (FsAppTransmitter *self) +{ + + /* member init */ + self->priv = FS_APP_TRANSMITTER_GET_PRIVATE (self); + + self->components = 2; + self->priv->do_timestamp = TRUE; +} + +static void +fs_app_transmitter_constructed (GObject *object) +{ + FsAppTransmitter *self = FS_APP_TRANSMITTER_CAST (object); + FsTransmitter *trans = FS_TRANSMITTER_CAST (self); + GstPad *pad = NULL, *pad2 = NULL; + GstPad *ghostpad = NULL; + gchar *padname; + GstPadLinkReturn ret; + int c; /* component_id */ + + + /* We waste one space in order to have the index be the component_id */ + self->priv->funnels = g_new0 (GstElement *, self->components+1); + self->priv->tees = g_new0 (GstElement *, self->components+1); + + /* First we need the src elemnet */ + + self->priv->gst_src = fs_app_bin_new (); + + if (!self->priv->gst_src) { + trans->construction_error = g_error_new (FS_ERROR, + FS_ERROR_CONSTRUCTION, + "Could not build the transmitter src bin"); + return; + } + + gst_object_ref (self->priv->gst_src); + + + /* Second, we do the sink element */ + + self->priv->gst_sink = fs_app_bin_new (); + + if (!self->priv->gst_sink) { + trans->construction_error = g_error_new (FS_ERROR, + FS_ERROR_CONSTRUCTION, + "Could not build the transmitter sink bin"); + return; + } + + g_object_set (G_OBJECT (self->priv->gst_sink), + "async-handling", TRUE, + NULL); + + gst_object_ref (self->priv->gst_sink); + + for (c = 1; c <= self->components; c++) { + GstElement *fakesink = NULL; + + /* Lets create the RTP source funnel */ + + self->priv->funnels[c] = gst_element_factory_make ("funnel", NULL); + + if (!self->priv->funnels[c]) { + trans->construction_error = g_error_new (FS_ERROR, + FS_ERROR_CONSTRUCTION, + "Could not make the funnel element"); + return; + } + + if (!gst_bin_add (GST_BIN (self->priv->gst_src), + self->priv->funnels[c])) { + trans->construction_error = g_error_new (FS_ERROR, + FS_ERROR_CONSTRUCTION, + "Could not add the funnel element to the transmitter src bin"); + } + + pad = gst_element_get_static_pad (self->priv->funnels[c], "src"); + padname = g_strdup_printf ("src_%u", c); + ghostpad = gst_ghost_pad_new (padname, pad); + g_free (padname); + gst_object_unref (pad); + + gst_pad_set_active (ghostpad, TRUE); + gst_element_add_pad (self->priv->gst_src, ghostpad); + + + /* Lets create the RTP sink tee */ + + self->priv->tees[c] = gst_element_factory_make ("tee", NULL); + + if (!self->priv->tees[c]) { + trans->construction_error = g_error_new (FS_ERROR, + FS_ERROR_CONSTRUCTION, + "Could not make the tee element"); + return; + } + + if (!gst_bin_add (GST_BIN (self->priv->gst_sink), + self->priv->tees[c])) { + trans->construction_error = g_error_new (FS_ERROR, + FS_ERROR_CONSTRUCTION, + "Could not add the tee element to the transmitter sink bin"); + } + + pad = gst_element_get_static_pad (self->priv->tees[c], "sink"); + padname = g_strdup_printf ("sink_%u", c); + ghostpad = gst_ghost_pad_new (padname, pad); + g_free (padname); + gst_object_unref (pad); + + gst_pad_set_active (ghostpad, TRUE); + gst_element_add_pad (self->priv->gst_sink, ghostpad); + + fakesink = gst_element_factory_make ("fakesink", NULL); + + if (!fakesink) { + trans->construction_error = g_error_new (FS_ERROR, + FS_ERROR_CONSTRUCTION, + "Could not make the fakesink element"); + return; + } + + g_object_set (fakesink, + "async", FALSE, + "sync" , FALSE, + NULL); + + if (!gst_bin_add (GST_BIN (self->priv->gst_sink), fakesink)) + { + gst_object_unref (fakesink); + trans->construction_error = g_error_new (FS_ERROR, + FS_ERROR_CONSTRUCTION, + "Could not add the fakesink element to the transmitter sink bin"); + return; + } + + pad = gst_element_get_request_pad (self->priv->tees[c], "src_%u"); + pad2 = gst_element_get_static_pad (fakesink, "sink"); + + ret = gst_pad_link (pad, pad2); + + gst_object_unref (pad2); + gst_object_unref (pad); + + if (GST_PAD_LINK_FAILED(ret)) { + trans->construction_error = g_error_new (FS_ERROR, + FS_ERROR_CONSTRUCTION, + "Could not link the tee to the fakesink"); + return; + } + } + + GST_CALL_PARENT (G_OBJECT_CLASS, constructed, (object)); +} + +static void +fs_app_transmitter_dispose (GObject *object) +{ + FsAppTransmitter *self = FS_APP_TRANSMITTER (object); + + if (self->priv->gst_src) { + gst_object_unref (self->priv->gst_src); + self->priv->gst_src = NULL; + } + + if (self->priv->gst_sink) { + gst_object_unref (self->priv->gst_sink); + self->priv->gst_sink = NULL; + } + + parent_class->dispose (object); +} + +static void +fs_app_transmitter_finalize (GObject *object) +{ + FsAppTransmitter *self = FS_APP_TRANSMITTER (object); + + if (self->priv->funnels) { + g_free (self->priv->funnels); + self->priv->funnels = NULL; + } + + if (self->priv->tees) { + g_free (self->priv->tees); + self->priv->tees = NULL; + } + + parent_class->finalize (object); +} + +static void +fs_app_transmitter_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + FsAppTransmitter *self = FS_APP_TRANSMITTER (object); + + switch (prop_id) { + case PROP_GST_SINK: + g_value_set_object (value, self->priv->gst_sink); + break; + case PROP_GST_SRC: + g_value_set_object (value, self->priv->gst_src); + break; + case PROP_COMPONENTS: + g_value_set_uint (value, self->components); + break; + case PROP_DO_TIMESTAMP: + g_value_set_boolean (value, self->priv->do_timestamp); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +fs_app_transmitter_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + FsAppTransmitter *self = FS_APP_TRANSMITTER (object); + + switch (prop_id) { + case PROP_COMPONENTS: + self->components = g_value_get_uint (value); + break; + case PROP_DO_TIMESTAMP: + self->priv->do_timestamp = g_value_get_boolean (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + + +/** + * fs_app_transmitter_new_stream_app_transmitter: + * @transmitter: a #FsTranmitter + * @participant: the #FsParticipant for which the #FsStream using this + * new #FsStreamTransmitter is created + * + * This function will create a new #FsStreamTransmitter element for a + * specific participant for this #FsAppTransmitter + * + * Returns: a new #FsStreamTransmitter + */ + +static FsStreamTransmitter * +fs_app_transmitter_new_stream_transmitter (FsTransmitter *transmitter, + FsParticipant *participant, guint n_parameters, GParameter *parameters, + GError **error) +{ + FsAppTransmitter *self = FS_APP_TRANSMITTER (transmitter); + + printf("New App transmitter\n"); + return FS_STREAM_TRANSMITTER (fs_app_stream_transmitter_newv ( + self, n_parameters, parameters, error)); +} + +static GType +fs_app_transmitter_get_stream_transmitter_type ( + FsTransmitter *transmitter) +{ + return FS_TYPE_APP_STREAM_TRANSMITTER; +} + + +struct _AppSrc { + guint component; + gchar *pipeline; + GstElement *src; + GstPad *funnelpad; + + got_buffer got_buffer_func; + connection disconnected_func; + gpointer cb_data; + gulong buffer_probe; +}; + + +static GstPadProbeReturn +src_buffer_probe_cb (GstPad *pad, GstPadProbeInfo *info, gpointer user_data) +{ + AppSrc *app = user_data; + GstBuffer *buffer = GST_PAD_PROBE_INFO_BUFFER (info); + + app->got_buffer_func (buffer, app->component, app->cb_data); + + return TRUE; +} + + +static void +disconnected_cb (GstBin *bin, GstElement *elem, AppSrc *app) +{ + if (elem != app->src) + return; + + app->disconnected_func (app->component, 0, app->cb_data); +} + + +AppSrc * +fs_app_transmitter_get_app_src (FsAppTransmitter *self, + guint component, + const gchar *pipeline, + got_buffer got_buffer_func, + connection disconnected_func, + gpointer cb_data, + GError **error) +{ + AppSrc *app = g_slice_new0 (AppSrc); + GstElement *elem; + GstPad *pad; + + app->component = component; + app->got_buffer_func = got_buffer_func; + app->disconnected_func = disconnected_func; + app->cb_data = cb_data; + + app->pipeline = g_strdup (pipeline); + + elem = gst_parse_bin_from_description(pipeline, TRUE, NULL); + if (!elem) + { + g_set_error (error, FS_ERROR, FS_ERROR_CONSTRUCTION, + "Could not make appsrc"); + goto error; + } + + if (app->disconnected_func) + g_signal_connect (self->priv->gst_src, "disconnected", + G_CALLBACK (disconnected_cb), app); + + if (!gst_bin_add (GST_BIN (self->priv->gst_src), elem)) + { + g_set_error (error, FS_ERROR, FS_ERROR_CONSTRUCTION, + "Could not add recvonly filter to bin"); + gst_object_unref (elem); + goto error; + } + + app->src = elem; + + app->funnelpad = gst_element_get_request_pad (self->priv->funnels[component], + "sink_%u"); + + if (!app->funnelpad) + { + g_set_error (error, FS_ERROR, FS_ERROR_CONSTRUCTION, + "Could not get funnelpad"); + goto error; + } + + pad = gst_element_get_static_pad (app->src, "src"); + if (GST_PAD_LINK_FAILED (gst_pad_link (pad, app->funnelpad))) + { + g_set_error (error, FS_ERROR, FS_ERROR_CONSTRUCTION, "Could not link tee" + " and valve"); + gst_object_unref (pad); + goto error; + } + + gst_object_unref (pad); + + if (got_buffer_func) + app->buffer_probe = gst_pad_add_probe (app->funnelpad, + GST_PAD_PROBE_TYPE_BUFFER, + src_buffer_probe_cb, app, NULL); + + if (!gst_element_sync_state_with_parent (app->src)) + { + g_set_error (error, FS_ERROR, FS_ERROR_CONSTRUCTION, + "Could not sync the state of the new appsrc with its parent"); + goto error; + } + + return app; + + error: + fs_app_transmitter_check_app_src (self, app, NULL); + return NULL; +} + +/* + * Returns: %TRUE if the pipeline is the same, other %FALSE and freeds the AppSrc + */ + +gboolean +fs_app_transmitter_check_app_src (FsAppTransmitter *self, AppSrc *app, + const gchar *pipeline) +{ + if (pipeline && !strcmp (pipeline, app->pipeline)) + return TRUE; + + if (app->buffer_probe) + gst_pad_remove_probe (app->funnelpad, app->buffer_probe); + app->buffer_probe = 0; + + if (app->funnelpad) { + gst_element_release_request_pad (self->priv->funnels[app->component], + app->funnelpad); + gst_object_unref (app->funnelpad); + } + app->funnelpad = NULL; + + if (app->src) + { + gst_element_set_locked_state (app->src, TRUE); + gst_element_set_state (app->src, GST_STATE_NULL); + gst_bin_remove (GST_BIN (self->priv->gst_src), app->src); + } + app->src = NULL; + + g_free (app->pipeline); + g_slice_free (AppSrc, app); + + return FALSE; +} + + + +struct _AppSink { + guint component; + gchar *pipeline; + GstElement *sink; + GstElement *recvonly_filter; + GstPad *teepad; + + ready ready_func; + gpointer cb_data; +}; + + +static void +ready_cb (GstBin *bin, GstElement *elem, AppSink *app) +{ + if (elem != app->sink) + return; + + app->ready_func (app->component, app->pipeline, app->cb_data); +} + +AppSink * +fs_app_transmitter_get_app_sink (FsAppTransmitter *self, + guint component, + const gchar *pipeline, + ready ready_func, + gpointer cb_data, + GError **error) +{ + AppSink *app = g_slice_new0 (AppSink); + GstElement *elem; + GstPad *pad; + + GST_DEBUG ("Trying to add app sink for c:%u pipeline '%s'", component, pipeline); + + app->component = component; + + app->pipeline = g_strdup (pipeline); + + app->ready_func = ready_func; + app->cb_data = cb_data; + + /* First add the sink */ + + elem = gst_parse_bin_from_description(pipeline, TRUE, NULL); + if (!elem) + { + g_set_error (error, FS_ERROR, FS_ERROR_CONSTRUCTION, + "Could not make appsink"); + goto error; + } + g_object_set (elem, + "async", FALSE, + "sync" , FALSE, + NULL); + + if (ready_func) + g_signal_connect (self->priv->gst_sink, "ready", G_CALLBACK (ready_cb), + app); + + if (!gst_bin_add (GST_BIN (self->priv->gst_sink), elem)) + { + g_set_error (error, FS_ERROR, FS_ERROR_CONSTRUCTION, + "Could not add appsink to bin"); + gst_object_unref (elem); + goto error; + } + + app->sink = elem; + + /* Second add the recvonly filter */ + + elem = gst_element_factory_make ("valve", NULL); + if (!elem) + { + g_set_error (error, FS_ERROR, FS_ERROR_CONSTRUCTION, + "Could not make valve"); + goto error; + } + + if (!gst_bin_add (GST_BIN (self->priv->gst_sink), elem)) + { + g_set_error (error, FS_ERROR, FS_ERROR_CONSTRUCTION, + "Could not add recvonly filter to bin"); + gst_object_unref (elem); + goto error; + } + + app->recvonly_filter = elem; + + /* Third connect these */ + + if (!gst_element_link (app->recvonly_filter, app->sink)) + { + g_set_error (error, FS_ERROR, FS_ERROR_CONSTRUCTION, + "Could not link recvonly filter and appsink"); + GST_DEBUG_BIN_TO_DOT_FILE(GST_BIN(self->priv->gst_sink), GST_DEBUG_GRAPH_SHOW_ALL, "nolink"); + + goto error; + } + + if (!gst_element_sync_state_with_parent (app->sink)) + { + g_set_error (error, FS_ERROR, FS_ERROR_CONSTRUCTION, + "Could not sync the state of the new appsink with its parent"); + goto error; + } + + if (!gst_element_sync_state_with_parent (app->recvonly_filter)) + { + g_set_error (error, FS_ERROR, FS_ERROR_CONSTRUCTION, + "Could not sync the state of the new recvonly filter with its parent"); + goto error; + } + + app->teepad = gst_element_get_request_pad (self->priv->tees[component], + "src_%u"); + + if (!app->teepad) + { + g_set_error (error, FS_ERROR, FS_ERROR_CONSTRUCTION, + "Could not get teepad"); + goto error; + } + + pad = gst_element_get_static_pad (app->recvonly_filter, "sink"); + if (GST_PAD_LINK_FAILED (gst_pad_link (app->teepad, pad))) + { + g_set_error (error, FS_ERROR, FS_ERROR_CONSTRUCTION, "Could not link tee" + " and valve"); + gst_object_unref (pad); + goto error; + } + gst_object_unref (pad); + + return app; + + error: + fs_app_transmitter_check_app_sink (self, app, NULL); + + return NULL; +} + +gboolean +fs_app_transmitter_check_app_sink (FsAppTransmitter *self, AppSink *app, + const gchar *pipeline) +{ + if (pipeline && !strcmp (pipeline, app->pipeline)) + return TRUE; + + if (pipeline) + GST_DEBUG ("Replacing app pipeline '%s' with '%s'", app->pipeline, pipeline); + else + GST_DEBUG ("Freeing app pipeline '%s'", app->pipeline); + + if (app->teepad) + { + gst_element_release_request_pad (self->priv->tees[app->component], + app->teepad); + gst_object_unref (app->teepad); + } + app->teepad = NULL; + + if (app->sink) + { + gst_element_set_locked_state (app->sink, TRUE); + gst_element_set_state (app->sink, GST_STATE_NULL); + gst_bin_remove (GST_BIN (self->priv->gst_sink), app->sink); + } + app->sink = NULL; + + if (app->recvonly_filter) + { + gst_element_set_locked_state (app->recvonly_filter, TRUE); + gst_element_set_state (app->recvonly_filter, GST_STATE_NULL); + gst_bin_remove (GST_BIN (self->priv->gst_sink), app->recvonly_filter); + } + app->recvonly_filter = NULL; + + g_free (app->pipeline); + g_slice_free (AppSink, app); + + return FALSE; +} + + +void +fs_app_transmitter_sink_set_sending (FsAppTransmitter *self, AppSink *app, + gboolean sending) +{ + GObjectClass *klass = G_OBJECT_GET_CLASS (app->recvonly_filter); + + if (g_object_class_find_property (klass, "drop")) + g_object_set (app->recvonly_filter, "drop", !sending, NULL); + + if (sending) + gst_element_send_event (app->sink, + gst_event_new_custom (GST_EVENT_CUSTOM_UPSTREAM, + gst_structure_new ("GstForceKeyUnit", + "all-headers", G_TYPE_BOOLEAN, TRUE, + NULL))); +} diff --git a/libpurple/protocols/null/fs-app-transmitter.h b/libpurple/protocols/null/fs-app-transmitter.h new file mode 100644 index 0000000..2bfdadf --- /dev/null +++ b/libpurple/protocols/null/fs-app-transmitter.h @@ -0,0 +1,124 @@ +/* + * Farstream - Farstream Shared Memory Transmitter + * + * Copyright 2007-2008 Collabora Ltd. + * @author: Olivier Crete <[email protected]> + * Copyright 2007-2008 Nokia Corp. + * + * fs-app-transmitter.h - A Farstream Shared Memory 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 + */ + +#ifndef __FS_APP_TRANSMITTER_H__ +#define __FS_APP_TRANSMITTER_H__ + +#include <farstream/fs-transmitter.h> + +#include <gst/gst.h> + +G_BEGIN_DECLS + +/* TYPE MACROS */ +#define FS_TYPE_APP_TRANSMITTER \ + (fs_app_transmitter_get_type ()) +#define FS_APP_TRANSMITTER(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj), FS_TYPE_APP_TRANSMITTER, \ + FsAppTransmitter)) +#define FS_APP_TRANSMITTER_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass), FS_TYPE_APP_TRANSMITTER, \ + FsAppTransmitterClass)) +#define FS_IS_APP_TRANSMITTER(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj), FS_TYPE_APP_TRANSMITTER)) +#define FS_IS_APP_TRANSMITTER_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE((klass), FS_TYPE_APP_TRANSMITTER)) +#define FS_APP_TRANSMITTER_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS ((obj), FS_TYPE_APP_TRANSMITTER, \ + FsAppTransmitterClass)) +#define FS_APP_TRANSMITTER_CAST(obj) ((FsAppTransmitter *) (obj)) + +typedef struct _FsAppTransmitter FsAppTransmitter; +typedef struct _FsAppTransmitterClass FsAppTransmitterClass; +typedef struct _FsAppTransmitterPrivate FsAppTransmitterPrivate; + +/** + * FsAppTransmitterClass: + * @parent_class: Our parent + * + * The Shared Memory transmitter class + */ + +struct _FsAppTransmitterClass +{ + FsTransmitterClass parent_class; +}; + +/** + * FsAppTransmitter: + * @parent: Parent object + * + * All members are private, access them using methods and properties + */ +struct _FsAppTransmitter +{ + FsTransmitter parent; + + /* The number of components (READONLY) */ + gint components; + + /*< private >*/ + FsAppTransmitterPrivate *priv; +}; + +GType fs_app_transmitter_get_type (void); + +typedef struct _AppSrc AppSrc; +typedef struct _AppSink AppSink; + +typedef void (*got_buffer) (GstBuffer *buffer, guint component, gpointer data); +typedef void (*ready) (guint component, gchar *path, gpointer data); +typedef void (*connection) (guint component, gint id, gpointer data); + +AppSrc *fs_app_transmitter_get_app_src (FsAppTransmitter *self, + guint component, + const gchar *path, + got_buffer got_buffer_func, + connection disconnected_func, + gpointer cb_data, + GError **error); + +gboolean fs_app_transmitter_check_app_src (FsAppTransmitter *self, + AppSrc *app, + const gchar *path); + +AppSink *fs_app_transmitter_get_app_sink (FsAppTransmitter *self, + guint component, + const gchar *path, + ready ready_func, + gpointer cb_data, + GError **error); + +gboolean fs_app_transmitter_check_app_sink (FsAppTransmitter *self, + AppSink *app, + const gchar *path); + +void fs_app_transmitter_sink_set_sending (FsAppTransmitter *self, + AppSink *app, gboolean sending); + + + +G_END_DECLS + +#endif /* __FS_APP_TRANSMITTER_H__ */ diff --git a/libpurple/protocols/null/nullprpl.c b/libpurple/protocols/null/nullprpl.c index e2d8662..7e6cabe 100644 --- a/libpurple/protocols/null/nullprpl.c +++ b/libpurple/protocols/null/nullprpl.c @@ -204,14 +204,64 @@ static void nullprpl_input_user_info(PurplePluginAction *action) purple_account_request_change_user_info(acct); } +#include <media.h> +#include <media-gst.h> +#include <mediamanager.h> + +static void nullprpl_dummy_call(PurplePluginAction *action) +{ + PurpleConnection *gc = (PurpleConnection *)action->context; + PurpleAccount *acct = purple_connection_get_account(gc); + PurpleMediaCandidate *cand; + GList *cands, *codecs; + const gchar *sess = "dummy session"; + const gchar *name = "Dummy call"; + gchar *srcpipe, *sinkpipe; + PurpleMediaManager *mgr = purple_media_manager_get(); + PurpleMedia *media = purple_media_manager_create_media(mgr, + acct, + "fsrawconference", + name, + TRUE); + const gchar *caps = "audio/x-raw,format=(string)S16LE,layout=(string)interleaved,channels=(int)1"; + + purple_media_add_stream(media, sess, name, + PURPLE_MEDIA_AUDIO, TRUE, + "app", 0, NULL); + /* Without the capsfilter (even though there's another capsfilter almost immediately after it in the pipeline, it doesn't work */ + srcpipe = g_strdup_printf("audiotestsrc format=time ! capsfilter caps=%s", caps); + sinkpipe = g_strdup_printf("audioconvert ! filesink location=/tmp/pidgin.s16 async=false"); + + cand = purple_media_candidate_new(NULL, 1, + PURPLE_MEDIA_CANDIDATE_TYPE_HOST, + PURPLE_MEDIA_NETWORK_PROTOCOL_UDP, + sinkpipe, 0); + g_object_set(cand, "username", srcpipe, NULL); + g_free(sinkpipe); + g_free(srcpipe); + + cands = g_list_append (NULL, cand); + codecs = g_list_append(NULL, + purple_media_codec_new(1, caps, PURPLE_MEDIA_AUDIO, 0)); + purple_media_add_remote_candidates(media, sess, name, cands); + purple_media_set_remote_codecs(media, sess, name, codecs); + + purple_media_stream_info(media, PURPLE_MEDIA_INFO_ACCEPT, sess, name, FALSE); + GST_DEBUG_BIN_TO_DOT_FILE(GST_BIN(purple_media_manager_get_pipeline(mgr)), GST_DEBUG_GRAPH_SHOW_ALL, "dummy conference graph"); +} + + /* this is set to the actions member of the PurplePluginInfo struct at the * bottom. */ static GList *nullprpl_actions(PurplePlugin *plugin, gpointer context) { - PurplePluginAction *action = purple_plugin_action_new( - _("Set User Info..."), nullprpl_input_user_info); - return g_list_append(NULL, action); + GList *l = NULL; + l = g_list_append(l, purple_plugin_action_new( + _("Set User Info..."), nullprpl_input_user_info)); + l = g_list_append(l, purple_plugin_action_new( + _("Dummy audio call..."), nullprpl_dummy_call)); + return l; } @@ -1126,6 +1176,10 @@ static PurplePluginProtocolInfo prpl_info = NULL /* add_buddies_with_invite */ }; +#include "fs-app-transmitter.h" +#include <farstream/fs-plugin.h> +extern GType fs_app_transmitter_register_type(void *); + static void nullprpl_init(PurplePlugin *plugin) { /* see accountopt.h for information about user splits and protocol options */ -- 2.7.4
smime.p7s
(application/x-pkcs7-signature, 5.1 KB) - not displayed