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.
SUBDIRS += evolution-plugin
endif
+SUBDIRS += gst-chime
+
AM_CPPFLAGS = @WFLAGS@
plugin_LTLIBRARIES = libchime.la
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,
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);
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),
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. */
}
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 {
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])
pixmaps/48/Makefile
evolution-plugin/Makefile
fs-app-transmitter/Makefile
+ gst-chime/Makefile
])
AC_OUTPUT
--- /dev/null
+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
--- /dev/null
+/* 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/");
--- /dev/null
+/*
+ * 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);
+}
--- /dev/null
+/*
+ * 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__ */
--- /dev/null
+/*
+ * 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, ©_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);
+}
--- /dev/null
+/*
+ * 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__ */