]> www.infradead.org Git - pidgin-chime.git/commitdiff
Add Chime-specific RTP payload handling
authorDavid Woodhouse <dwmw@amazon.co.uk>
Thu, 22 Feb 2018 08:33:14 +0000 (08:33 +0000)
committerDavid Woodhouse <dwmw@amazon.co.uk>
Thu, 22 Feb 2018 08:33:14 +0000 (08:33 +0000)
RFC7587 defines the clock-rate for the OPUS payload format to be 48000.
This is painful since we're really quite like to be able to transpose
timestamps between the RTP and on-the-wire Chime protocol (which uses
16000) without conversion. It was OK having to multiply by three for the
appsrc, but dividing by three in the appsink would have overflow issues
and we'd have to reconstitute the high bits somehow.

By defining our own payload format we can work around that, and also
force it to encode only a single channel. That one might well be a bug
in the existing gstopuspay; if the output caps include 'stereo=0' it
*isn't* enforcing 'channels=1' on its input caps.

Both of the above could probably be fixed up in the OPUS payloaders
upstream, and once this code is public we can have a look at doing that.

For now though, having our own CHIME payload helps it work on Farstream
0.2.7 on Ubuntu 16.04, which can't cope with payloaders that have
multiple names (OPUS, X-GST-OPUS-DRAFT-SPITTKA-00). So it'll do for now
until we can get more traction on upstreaming the required fixes.

Makefile.am
chat.c
chime-call-audio.c
configure.ac
gst-chime/Makefile.am [new file with mode: 0644]
gst-chime/gstchime.c [new file with mode: 0644]
gst-chime/gstrtpchimedepay.c [new file with mode: 0644]
gst-chime/gstrtpchimedepay.h [new file with mode: 0644]
gst-chime/gstrtpchimepay.c [new file with mode: 0644]
gst-chime/gstrtpchimepay.h [new file with mode: 0644]

index cab192614da1a4cec30dd0063f2e7efada5125f3..816e13774117b469fd3fc92c7235b7132b4e5969 100644 (file)
@@ -5,6 +5,8 @@ if BUILD_EVOPLUGIN
 SUBDIRS += evolution-plugin
 endif
 
+SUBDIRS += gst-chime
+
 AM_CPPFLAGS = @WFLAGS@
 
 plugin_LTLIBRARIES = libchime.la
diff --git a/chat.c b/chat.c
index 2cef5d2901babb8231d7712fbaa57a10e2c0abf9..d11845e01db0b97e79359f5d7aa23a17173073e0 100644 (file)
--- a/chat.c
+++ b/chat.c
@@ -273,7 +273,7 @@ static void on_audio_state(ChimeCall *call, ChimeAudioState audio_state, struct
                                                                name,
                                                                TRUE);
                if (chat->media) {
-                       const gchar *caps = "application/x-srtp,media=(string)audio,clock-rate=(int)48000,payload=(int)96,encoding-name=(string)OPUS,stereo=(string)0";
+                       const gchar *caps = "application/x-srtp,media=(string)audio,clock-rate=(int)16000,payload=(int)97,encoding-name=(string)CHIME,stereo=(string)0";
 
                        gboolean r = purple_media_add_stream(chat->media, "chime", name,
                                                             PURPLE_MEDIA_AUDIO, TRUE,
@@ -294,14 +294,16 @@ static void on_audio_state(ChimeCall *call, ChimeAudioState audio_state, struct
 
                        GList *cands = g_list_append (NULL, cand);
                        GList *codecs = g_list_append(NULL,
-                                                     purple_media_codec_new(96, "OPUS", PURPLE_MEDIA_AUDIO, 0));
+                                                     purple_media_codec_new(97, "CHIME", PURPLE_MEDIA_AUDIO, 0));
                        //                      g_object_set(codecs->data, "channels", 1, NULL);
                        //                      purple_media_codec_add_optional_parameter(codecs->data, "farstream-recv-profile", "rtpopusdepay ! opusdec");
                        //                      purple_media_codec_add_optional_parameter(codecs->data, "farstream-send-profile", "opusenc bitrate=16000 bitrate-type=vbr dtx=1 ! rtpopuspay");
+                       #if 0
                        purple_media_codec_add_optional_parameter(codecs->data, "sprop-stereo", "0");
                        purple_media_codec_add_optional_parameter(codecs->data, "stereo", "0");
                        purple_media_codec_add_optional_parameter(codecs->data, "usedtx", "1");
                        purple_media_codec_add_optional_parameter(codecs->data, "maxplaybackrate", "16000");
+                       #endif
                        purple_media_add_remote_candidates(chat->media, "chime", name, cands);
                        purple_media_set_remote_codecs(chat->media, "chime", name, codecs);
 
index 5f4eaee5870ba04b6df3a6c8eac5b9944016e74d..46069c8324d841d1ce88dd4b231030ffa4692d60 100644 (file)
@@ -72,9 +72,9 @@ static gboolean audio_receive_rt_msg(ChimeCallAudio *audio, gconstpointer pkt, g
                                if (gst_rtp_buffer_map(buffer, GST_MAP_WRITE, &rtp)) {
 
                                        gst_rtp_buffer_set_ssrc(&rtp, 0x12345678);
-                                       gst_rtp_buffer_set_payload_type(&rtp, 96);
+                                       gst_rtp_buffer_set_payload_type(&rtp, 97);
                                        gst_rtp_buffer_set_seq(&rtp, msg->audio->seq);
-                                       gst_rtp_buffer_set_timestamp(&rtp, msg->audio->sample_time * 3);
+                                       gst_rtp_buffer_set_timestamp(&rtp, msg->audio->sample_time);
                                        gst_rtp_buffer_unmap(&rtp);
 
                                        gst_buffer_fill(buffer, gst_rtp_buffer_calc_header_len(0),
@@ -157,7 +157,7 @@ static void do_send_rt_packet(ChimeCallAudio *audio, GstBuffer *buffer)
                dur = GST_BUFFER_DURATION(buffer);
 
                nr_samples = GST_BUFFER_DURATION(buffer) / NS_PER_SAMPLE;
-               printf("buf dts %ld pts %ld dur %ld samples %d\n", dts, pts, dur, nr_samples);
+//             printf("buf dts %ld pts %ld dur %ld samples %d\n", dts, pts, dur, nr_samples);
 
                if (audio->next_dts && dts > audio->next_dts) {
                        /* We skipped some. */
@@ -165,11 +165,11 @@ static void do_send_rt_packet(ChimeCallAudio *audio, GstBuffer *buffer)
                }
                audio->next_dts = dts + dur;
                if (audio->state == CHIME_AUDIO_STATE_AUDIO) {
-                       printf ("State %d, send audio\n", audio->state);
+//                     printf ("State %d, send audio\n", audio->state);
                        audio->audio_msg.audio.len = gst_rtp_buffer_get_payload_len(&rtp);
                        audio->audio_msg.audio.data = gst_rtp_buffer_get_payload(&rtp);
                } else {
-                       printf ("State %d, send no audio\n", audio->state);
+//                     printf ("State %d, send no audio\n", audio->state);
                        audio->audio_msg.audio.len = 0;
                }
        } else {
index 8556bdccd262a1056c39b31569a9a3a1bac504ab..58a6fcc9d946e12d408675b78f2ac2e72dde6e26 100644 (file)
@@ -69,6 +69,9 @@ PKG_CHECK_MODULES(MARKDOWN, [libmarkdown], [],
                                   AC_ERROR([Could not build against libmarkdown])])
                   LIBS="$oldLIBS"])
 
+gstplugindir="$($PKG_CONFIG --variable=pluginsdir gstreamer-1.0)"
+AC_SUBST(gstplugindir, ${gstplugindir})
+
 AC_PATH_PROG(MSGFMT, msgfmt)
 if test "$MSGFMT" = ""; then
    AC_MSG_ERROR([msgfmt could not be found. Try configuring with --disable-nls])
@@ -114,5 +117,6 @@ AC_CONFIG_FILES([
        pixmaps/48/Makefile
        evolution-plugin/Makefile
        fs-app-transmitter/Makefile
+       gst-chime/Makefile
        ])
 AC_OUTPUT
diff --git a/gst-chime/Makefile.am b/gst-chime/Makefile.am
new file mode 100644 (file)
index 0000000..c1b3308
--- /dev/null
@@ -0,0 +1,14 @@
+gstplugin_LTLIBRARIES = libgstchime.la
+
+libgstchime_la_CFLAGS = $(GSTREAMER_CFLAGS)
+libgstchime_la_LIBADD = $(GSTREAMER_LIBS)
+libgstchime_la_LDFLAGS = -module -avoid-version -no-undefined
+
+libgstchime_la_SOURCES =       \
+       gstchime.c              \
+       gstrtpchimepay.c        \
+       gstrtpchimedepay.c
+
+noinst_HEADERS =               \
+       gstrtpchimepay.h        \
+       gstrtpchimedepay.h
diff --git a/gst-chime/gstchime.c b/gst-chime/gstchime.c
new file mode 100644 (file)
index 0000000..0b3b4b9
--- /dev/null
@@ -0,0 +1,44 @@
+/* GStreamer
+ * Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "gstrtpchimedepay.h"
+#include "gstrtpchimepay.h"
+
+static gboolean
+plugin_init (GstPlugin * plugin)
+{
+  if (!gst_rtp_chime_depay_plugin_init (plugin))
+    return FALSE;
+
+  if (!gst_rtp_chime_pay_plugin_init (plugin))
+    return FALSE;
+
+
+  return TRUE;
+}
+
+GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
+    GST_VERSION_MINOR,
+    chime,
+    "Chime RTP plugin",
+    plugin_init, VERSION, "LGPL", "Pidgin Chime plugin", "http://localhost/");
diff --git a/gst-chime/gstrtpchimedepay.c b/gst-chime/gstrtpchimedepay.c
new file mode 100644 (file)
index 0000000..58c90a3
--- /dev/null
@@ -0,0 +1,179 @@
+/*
+ * Chime Depayloader Gst Element
+ *
+ *   @author: Danilo Cesar Lemes de Paula <danilo.cesar@collabora.co.uk>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#  include "config.h"
+#endif
+
+#include <string.h>
+#include <stdlib.h>
+#include <gst/rtp/gstrtpbuffer.h>
+#include <gst/audio/audio.h>
+#include "gstrtpchimedepay.h"
+
+GST_DEBUG_CATEGORY_STATIC (rtpchimedepay_debug);
+#define GST_CAT_DEFAULT (rtpchimedepay_debug)
+
+static GstStaticPadTemplate gst_rtp_chime_depay_sink_template =
+GST_STATIC_PAD_TEMPLATE ("sink",
+    GST_PAD_SINK,
+    GST_PAD_ALWAYS,
+    GST_STATIC_CAPS ("application/x-rtp, "
+        "media = (string) \"audio\", "
+        "payload = (int) " GST_RTP_PAYLOAD_DYNAMIC_STRING ","
+        "clock-rate = (int) 16000, "
+        "encoding-name = (string) \"CHIME\"")
+    );
+
+static GstStaticPadTemplate gst_rtp_chime_depay_src_template =
+GST_STATIC_PAD_TEMPLATE ("src",
+    GST_PAD_SRC,
+    GST_PAD_ALWAYS,
+    GST_STATIC_CAPS ("audio/x-opus, channel-mapping-family = (int) 0")
+    );
+
+static GstBuffer *gst_rtp_chime_depay_process (GstRTPBaseDepayload * depayload,
+    GstRTPBuffer * rtp_buffer);
+static gboolean gst_rtp_chime_depay_setcaps (GstRTPBaseDepayload * depayload,
+    GstCaps * caps);
+
+G_DEFINE_TYPE (GstRTPChimeDepay, gst_rtp_chime_depay,
+    GST_TYPE_RTP_BASE_DEPAYLOAD);
+
+static void
+gst_rtp_chime_depay_class_init (GstRTPChimeDepayClass * klass)
+{
+  GstRTPBaseDepayloadClass *gstbasertpdepayload_class;
+  GstElementClass *element_class;
+
+  element_class = GST_ELEMENT_CLASS (klass);
+  gstbasertpdepayload_class = (GstRTPBaseDepayloadClass *) klass;
+
+  gst_element_class_add_pad_template (element_class,
+      gst_static_pad_template_get (&gst_rtp_chime_depay_src_template));
+  gst_element_class_add_pad_template (element_class,
+      gst_static_pad_template_get (&gst_rtp_chime_depay_sink_template));
+  gst_element_class_set_static_metadata (element_class,
+      "RTP Chime packet depayloader", "Codec/Depayloader/Network/RTP",
+      "Extracts Chime audio from RTP packets",
+      "Danilo Cesar Lemes de Paula <danilo.cesar@collabora.co.uk>");
+
+  gstbasertpdepayload_class->process_rtp_packet = gst_rtp_chime_depay_process;
+  gstbasertpdepayload_class->set_caps = gst_rtp_chime_depay_setcaps;
+
+  GST_DEBUG_CATEGORY_INIT (rtpchimedepay_debug, "rtpchimedepay", 0,
+      "Chime RTP Depayloader");
+}
+
+static void
+gst_rtp_chime_depay_init (GstRTPChimeDepay * rtpchimedepay)
+{
+
+}
+
+static gboolean
+gst_rtp_chime_depay_setcaps (GstRTPBaseDepayload * depayload, GstCaps * caps)
+{
+  GstCaps *srccaps;
+  GstStructure *s;
+  gboolean ret;
+  const gchar *sprop_stereo, *sprop_maxcapturerate;
+
+  srccaps =
+      gst_caps_new_simple ("audio/x-opus", "channel-mapping-family", G_TYPE_INT,
+      0, NULL);
+
+  s = gst_caps_get_structure (caps, 0);
+  if ((sprop_stereo = gst_structure_get_string (s, "sprop-stereo"))) {
+    if (strcmp (sprop_stereo, "0") == 0)
+      gst_caps_set_simple (srccaps, "channels", G_TYPE_INT, 1, NULL);
+    else if (strcmp (sprop_stereo, "1") == 0)
+      gst_caps_set_simple (srccaps, "channels", G_TYPE_INT, 2, NULL);
+    else
+      GST_WARNING_OBJECT (depayload, "Unknown sprop-stereo value '%s'",
+          sprop_stereo);
+  }
+
+  if ((sprop_maxcapturerate =
+          gst_structure_get_string (s, "sprop-maxcapturerate"))) {
+    gulong rate;
+    gchar *tailptr;
+
+    rate = strtoul (sprop_maxcapturerate, &tailptr, 10);
+    if (rate > INT_MAX || *tailptr != '\0') {
+      GST_WARNING_OBJECT (depayload,
+          "Failed to parse sprop-maxcapturerate value '%s'",
+          sprop_maxcapturerate);
+    } else {
+      gst_caps_set_simple (srccaps, "rate", G_TYPE_INT, rate, NULL);
+    }
+  }
+
+  ret = gst_pad_set_caps (GST_RTP_BASE_DEPAYLOAD_SRCPAD (depayload), srccaps);
+
+  GST_DEBUG_OBJECT (depayload,
+      "set caps on source: %" GST_PTR_FORMAT " (ret=%d)", srccaps, ret);
+  gst_caps_unref (srccaps);
+
+  depayload->clock_rate = 16000;
+
+  return ret;
+}
+
+static gboolean
+foreach_metadata (GstBuffer * inbuf, GstMeta ** meta, gpointer user_data)
+{
+  GstRTPChimeDepay *depay = user_data;
+  const GstMetaInfo *info = (*meta)->info;
+  const gchar *const *tags = gst_meta_api_type_get_tags (info->api);
+
+  if (!tags || (g_strv_length ((gchar **) tags) == 1
+          && gst_meta_api_type_has_tag (info->api,
+              g_quark_from_string (GST_META_TAG_AUDIO_STR)))) {
+    GST_DEBUG_OBJECT (depay, "keeping metadata %s", g_type_name (info->api));
+  } else {
+    GST_DEBUG_OBJECT (depay, "dropping metadata %s", g_type_name (info->api));
+    *meta = NULL;
+  }
+
+  return TRUE;
+}
+
+static GstBuffer *
+gst_rtp_chime_depay_process (GstRTPBaseDepayload * depayload,
+    GstRTPBuffer * rtp_buffer)
+{
+  GstBuffer *outbuf;
+
+  outbuf = gst_rtp_buffer_get_payload_buffer (rtp_buffer);
+
+  /* Filter away all metas that are not sensible to copy */
+  gst_buffer_foreach_meta (outbuf, foreach_metadata, depayload);
+
+  return outbuf;
+}
+
+gboolean
+gst_rtp_chime_depay_plugin_init (GstPlugin * plugin)
+{
+  return gst_element_register (plugin, "rtpchimedepay",
+      GST_RANK_PRIMARY, GST_TYPE_RTP_CHIME_DEPAY);
+}
diff --git a/gst-chime/gstrtpchimedepay.h b/gst-chime/gstrtpchimedepay.h
new file mode 100644 (file)
index 0000000..aca4275
--- /dev/null
@@ -0,0 +1,59 @@
+/*
+ * Chime Depayloader Gst Element
+ *
+ *   @author: Danilo Cesar Lemes de Paula <danilo.eu@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifndef __GST_RTP_CHIME_DEPAY_H__
+#define __GST_RTP_CHIME_DEPAY_H__
+
+#include <gst/gst.h>
+#include <gst/rtp/gstrtpbasedepayload.h>
+
+G_BEGIN_DECLS typedef struct _GstRTPChimeDepay GstRTPChimeDepay;
+typedef struct _GstRTPChimeDepayClass GstRTPChimeDepayClass;
+
+#define GST_TYPE_RTP_CHIME_DEPAY \
+  (gst_rtp_chime_depay_get_type())
+#define GST_RTP_CHIME_DEPAY(obj) \
+  (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_RTP_CHIME_DEPAY,GstRTPChimeDepay))
+#define GST_RTP_CHIME_DEPAY_CLASS(klass) \
+  (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_RTP_CHIME_DEPAY,GstRTPChimeDepayClass))
+#define GST_IS_RTP_CHIME_DEPAY(obj) \
+  (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_RTP_CHIME_DEPAY))
+#define GST_IS_RTP_CHIME_DEPAY_CLASS(klass) \
+  (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_RTP_CHIME_DEPAY))
+
+
+struct _GstRTPChimeDepay
+{
+  GstRTPBaseDepayload depayload;
+
+};
+
+struct _GstRTPChimeDepayClass
+{
+  GstRTPBaseDepayloadClass parent_class;
+};
+
+GType gst_rtp_chime_depay_get_type (void);
+
+gboolean gst_rtp_chime_depay_plugin_init (GstPlugin * plugin);
+
+G_END_DECLS
+#endif /* __GST_RTP_CHIME_DEPAY_H__ */
diff --git a/gst-chime/gstrtpchimepay.c b/gst-chime/gstrtpchimepay.c
new file mode 100644 (file)
index 0000000..bdcba15
--- /dev/null
@@ -0,0 +1,285 @@
+/*
+ * Chime Payloader Gst Element
+ *
+ *   @author: Danilo Cesar Lemes de Paula <danilo.cesar@collabora.co.uk>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#  include "config.h"
+#endif
+
+#include <string.h>
+
+#include <gst/rtp/gstrtpbuffer.h>
+#include <gst/audio/audio.h>
+
+#include "gstrtpchimepay.h"
+
+GST_DEBUG_CATEGORY_STATIC (rtpchimepay_debug);
+#define GST_CAT_DEFAULT (rtpchimepay_debug)
+
+
+static GstStaticPadTemplate gst_rtp_chime_pay_sink_template =
+GST_STATIC_PAD_TEMPLATE ("sink",
+    GST_PAD_SINK,
+    GST_PAD_ALWAYS,
+    GST_STATIC_CAPS
+    ("audio/x-opus, channels = (int)1, channel-mapping-family = (int) 0")
+    );
+
+static GstStaticPadTemplate gst_rtp_chime_pay_src_template =
+GST_STATIC_PAD_TEMPLATE ("src",
+    GST_PAD_SRC,
+    GST_PAD_ALWAYS,
+    GST_STATIC_CAPS ("application/x-rtp, "
+        "media = (string) \"audio\", "
+        "payload = (int) " GST_RTP_PAYLOAD_DYNAMIC_STRING ", "
+        "clock-rate = (int) 16000, "
+        "encoding-params = (string) \"2\", "
+        "encoding-name = (string) \"CHIME\"")
+    );
+
+static gboolean gst_rtp_chime_pay_setcaps (GstRTPBasePayload * payload,
+    GstCaps * caps);
+static GstCaps *gst_rtp_chime_pay_getcaps (GstRTPBasePayload * payload,
+    GstPad * pad, GstCaps * filter);
+static GstFlowReturn gst_rtp_chime_pay_handle_buffer (GstRTPBasePayload *
+    payload, GstBuffer * buffer);
+
+G_DEFINE_TYPE (GstRtpCHIMEPay, gst_rtp_chime_pay, GST_TYPE_RTP_BASE_PAYLOAD);
+
+static void
+gst_rtp_chime_pay_class_init (GstRtpCHIMEPayClass * klass)
+{
+  GstRTPBasePayloadClass *gstbasertppayload_class;
+  GstElementClass *element_class;
+
+  gstbasertppayload_class = (GstRTPBasePayloadClass *) klass;
+  element_class = GST_ELEMENT_CLASS (klass);
+
+  gstbasertppayload_class->set_caps = gst_rtp_chime_pay_setcaps;
+  gstbasertppayload_class->get_caps = gst_rtp_chime_pay_getcaps;
+  gstbasertppayload_class->handle_buffer = gst_rtp_chime_pay_handle_buffer;
+
+  gst_element_class_add_pad_template (element_class,
+      gst_static_pad_template_get (&gst_rtp_chime_pay_src_template));
+  gst_element_class_add_pad_template (element_class,
+      gst_static_pad_template_get (&gst_rtp_chime_pay_sink_template));
+
+  gst_element_class_set_static_metadata (element_class,
+      "RTP Chime payloader",
+      "Codec/Payloader/Network/RTP",
+      "Puts Chime audio in RTP packets",
+      "Danilo Cesar Lemes de Paula <danilo.cesar@collabora.co.uk>");
+
+  GST_DEBUG_CATEGORY_INIT (rtpchimepay_debug, "rtpchimepay", 0,
+      "Chime RTP Payloader");
+}
+
+static void
+gst_rtp_chime_pay_init (GstRtpCHIMEPay * rtpchimepay)
+{
+}
+
+static gboolean
+gst_rtp_chime_pay_setcaps (GstRTPBasePayload * payload, GstCaps * caps)
+{
+  gboolean res;
+  GstCaps *src_caps;
+  GstStructure *s;
+  char *encoding_name;
+  gint channels, rate;
+  const char *sprop_stereo = NULL;
+  char *sprop_maxcapturerate = NULL;
+
+  src_caps = gst_pad_get_allowed_caps (GST_RTP_BASE_PAYLOAD_SRCPAD (payload));
+  if (src_caps) {
+    src_caps = gst_caps_make_writable (src_caps);
+    src_caps = gst_caps_truncate (src_caps);
+    s = gst_caps_get_structure (src_caps, 0);
+    gst_structure_fixate_field_string (s, "encoding-name", "CHIME");
+    encoding_name = g_strdup (gst_structure_get_string (s, "encoding-name"));
+    gst_caps_unref (src_caps);
+  } else {
+    encoding_name = g_strdup ("CHIME");
+  }
+
+  s = gst_caps_get_structure (caps, 0);
+  if (gst_structure_get_int (s, "channels", &channels)) {
+    if (channels > 2) {
+      GST_ERROR_OBJECT (payload,
+          "More than 2 channels with channel-mapping-family=0 is invalid");
+      return FALSE;
+    } else if (channels == 2) {
+      sprop_stereo = "1";
+    } else {
+      sprop_stereo = "0";
+    }
+  }
+
+  if (gst_structure_get_int (s, "rate", &rate)) {
+    sprop_maxcapturerate = g_strdup_printf ("%d", rate);
+  }
+
+  gst_rtp_base_payload_set_options (payload, "audio", FALSE,
+      encoding_name, 16000);
+  g_free (encoding_name);
+
+  if (sprop_maxcapturerate && sprop_stereo) {
+    res =
+        gst_rtp_base_payload_set_outcaps (payload, "sprop-maxcapturerate",
+        G_TYPE_STRING, sprop_maxcapturerate, "sprop-stereo", G_TYPE_STRING,
+        sprop_stereo, NULL);
+  } else if (sprop_maxcapturerate) {
+    res =
+        gst_rtp_base_payload_set_outcaps (payload, "sprop-maxcapturerate",
+        G_TYPE_STRING, sprop_maxcapturerate, NULL);
+  } else if (sprop_stereo) {
+    res =
+        gst_rtp_base_payload_set_outcaps (payload, "sprop-stereo",
+        G_TYPE_STRING, sprop_stereo, NULL);
+  } else {
+    res = gst_rtp_base_payload_set_outcaps (payload, NULL);
+  }
+
+  g_free (sprop_maxcapturerate);
+
+  return res;
+}
+
+typedef struct
+{
+  GstRtpCHIMEPay *pay;
+  GstBuffer *outbuf;
+} CopyMetaData;
+
+static gboolean
+foreach_metadata (GstBuffer * inbuf, GstMeta ** meta, gpointer user_data)
+{
+  CopyMetaData *data = user_data;
+  GstRtpCHIMEPay *pay = data->pay;
+  GstBuffer *outbuf = data->outbuf;
+  const GstMetaInfo *info = (*meta)->info;
+  const gchar *const *tags = gst_meta_api_type_get_tags (info->api);
+
+  if (!tags || (g_strv_length ((gchar **) tags) == 1
+          && gst_meta_api_type_has_tag (info->api,
+              g_quark_from_string (GST_META_TAG_AUDIO_STR)))) {
+    GstMetaTransformCopy copy_data = { FALSE, 0, -1 };
+    GST_DEBUG_OBJECT (pay, "copy metadata %s", g_type_name (info->api));
+    /* simply copy then */
+    info->transform_func (outbuf, *meta, inbuf,
+        _gst_meta_transform_copy, &copy_data);
+  } else {
+    GST_DEBUG_OBJECT (pay, "not copying metadata %s", g_type_name (info->api));
+  }
+
+  return TRUE;
+}
+
+static GstFlowReturn
+gst_rtp_chime_pay_handle_buffer (GstRTPBasePayload * basepayload,
+    GstBuffer * buffer)
+{
+  GstBuffer *outbuf;
+  GstClockTime pts, dts, duration;
+  CopyMetaData data;
+
+  pts = GST_BUFFER_PTS (buffer);
+  dts = GST_BUFFER_DTS (buffer);
+  duration = GST_BUFFER_DURATION (buffer);
+
+  outbuf = gst_rtp_buffer_new_allocate (0, 0, 0);
+  data.pay = GST_RTP_CHIME_PAY (basepayload);
+  data.outbuf = outbuf;
+  gst_buffer_foreach_meta (buffer, foreach_metadata, &data);
+  outbuf = gst_buffer_append (outbuf, buffer);
+
+  GST_BUFFER_PTS (outbuf) = pts;
+  GST_BUFFER_DTS (outbuf) = dts;
+  GST_BUFFER_DURATION (outbuf) = duration;
+
+  /* Push out */
+  return gst_rtp_base_payload_push (basepayload, outbuf);
+}
+
+static GstCaps *
+gst_rtp_chime_pay_getcaps (GstRTPBasePayload * payload,
+    GstPad * pad, GstCaps * filter)
+{
+  GstCaps *caps, *peercaps, *tcaps;
+  GstStructure *s;
+  const gchar *stereo;
+
+  if (pad == GST_RTP_BASE_PAYLOAD_SRCPAD (payload))
+    return
+        GST_RTP_BASE_PAYLOAD_CLASS (gst_rtp_chime_pay_parent_class)->get_caps
+        (payload, pad, filter);
+
+  tcaps = gst_pad_get_pad_template_caps (GST_RTP_BASE_PAYLOAD_SRCPAD (payload));
+  peercaps = gst_pad_peer_query_caps (GST_RTP_BASE_PAYLOAD_SRCPAD (payload),
+      tcaps);
+  gst_caps_unref (tcaps);
+  if (!peercaps)
+    return
+        GST_RTP_BASE_PAYLOAD_CLASS (gst_rtp_chime_pay_parent_class)->get_caps
+        (payload, pad, filter);
+
+  if (gst_caps_is_empty (peercaps))
+    return peercaps;
+
+  caps = gst_pad_get_pad_template_caps (GST_RTP_BASE_PAYLOAD_SINKPAD (payload));
+
+  s = gst_caps_get_structure (peercaps, 0);
+  stereo = gst_structure_get_string (s, "stereo");
+  if (stereo != NULL) {
+    caps = gst_caps_make_writable (caps);
+
+    if (!strcmp (stereo, "1")) {
+      GstCaps *caps2 = gst_caps_copy (caps);
+
+      gst_caps_set_simple (caps, "channels", G_TYPE_INT, 2, NULL);
+      gst_caps_set_simple (caps2, "channels", G_TYPE_INT, 1, NULL);
+      caps = gst_caps_merge (caps, caps2);
+    } else if (!strcmp (stereo, "0")) {
+      GstCaps *caps2 = gst_caps_copy (caps);
+
+      gst_caps_set_simple (caps, "channels", G_TYPE_INT, 1, NULL);
+      gst_caps_set_simple (caps2, "channels", G_TYPE_INT, 2, NULL);
+      caps = gst_caps_merge (caps, caps2);
+    }
+  }
+  gst_caps_unref (peercaps);
+
+  if (filter) {
+    GstCaps *tmp = gst_caps_intersect_full (caps, filter,
+        GST_CAPS_INTERSECT_FIRST);
+    gst_caps_unref (caps);
+    caps = tmp;
+  }
+
+  GST_DEBUG_OBJECT (payload, "Returning caps: %" GST_PTR_FORMAT, caps);
+  return caps;
+}
+
+gboolean
+gst_rtp_chime_pay_plugin_init (GstPlugin * plugin)
+{
+  return gst_element_register (plugin, "rtpchimepay",
+      GST_RANK_PRIMARY, GST_TYPE_RTP_CHIME_PAY);
+}
diff --git a/gst-chime/gstrtpchimepay.h b/gst-chime/gstrtpchimepay.h
new file mode 100644 (file)
index 0000000..ea0dcc2
--- /dev/null
@@ -0,0 +1,60 @@
+/*
+ * Chime Payloader Gst Element
+ *
+ *   @author: Danilo Cesar Lemes de Paula <danilo.eu@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifndef __GST_RTP_CHIME_PAY_H__
+#define __GST_RTP_CHIME_PAY_H__
+
+#include <gst/gst.h>
+#include <gst/rtp/gstrtpbasepayload.h>
+
+G_BEGIN_DECLS
+
+#define GST_TYPE_RTP_CHIME_PAY \
+  (gst_rtp_chime_pay_get_type())
+#define GST_RTP_CHIME_PAY(obj) \
+  (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_RTP_CHIME_PAY,GstRtpCHIMEPay))
+#define GST_RTP_CHIME_PAY_CLASS(klass) \
+  (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_RTP_CHIME_PAY,GstRtpCHIMEPayClass))
+#define GST_IS_RTP_CHIME_PAY(obj) \
+  (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_RTP_CHIME_PAY))
+#define GST_IS_RTP_CHIME_PAY_CLASS(klass) \
+  (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_RTP_CHIME_PAY))
+
+typedef struct _GstRtpCHIMEPay GstRtpCHIMEPay;
+typedef struct _GstRtpCHIMEPayClass GstRtpCHIMEPayClass;
+
+struct _GstRtpCHIMEPay
+{
+  GstRTPBasePayload payload;
+};
+
+struct _GstRtpCHIMEPayClass
+{
+  GstRTPBasePayloadClass parent_class;
+};
+
+GType gst_rtp_chime_pay_get_type (void);
+
+gboolean gst_rtp_chime_pay_plugin_init (GstPlugin * plugin);
+
+G_END_DECLS
+
+#endif /* __GST_RTP_CHIME_PAY_H__ */