srt: Introduce SRT source and sink

SRT[0] is an open source transport technology[1] that optimizes
streaming performance across unpredictable networks.

Although SRT is based on UDP, it works like connection-oriented
protocol. However, it doesn't mean that the SRT server or client
is necessarily to link to a receiver or a sender so, here, the
pairs of source and sink elements are introduced.

 - srtserversink: SRT server to feed SRT stream
 - srtclientsrc:  SRT client to get SRT stream from srtserversink

 - srtclientsink: SRT client to send SRT stream
 - srtserversrc:  SRT server to listen from srtclientsink

[0] https://github.com/Haivision/srt
[1] http://www.srtalliance.org/

https://bugzilla.gnome.org/show_bug.cgi?id=785730
This commit is contained in:
Justin Kim 2017-07-31 14:38:34 +09:00 committed by Olivier Crête
parent 07819afda4
commit f78be9d698
19 changed files with 2926 additions and 0 deletions

View file

@ -2533,6 +2533,15 @@ AG_GST_CHECK_FEATURE(LIBMMS, [mms protocol library], libmms, [
]) ])
AC_SUBST(LIBMMS_LIBS) AC_SUBST(LIBMMS_LIBS)
dnl *** libsrt ***
translit(dnm, m, l) AM_CONDITIONAL(USE_SRT, true)
AG_GST_CHECK_FEATURE(SRT, [srt library], srt, [
PKG_CHECK_MODULES(SRT, libsrt, HAVE_SRT="yes",
AG_GST_CHECK_LIBHEADER(SRT, srt, srt_startup, , srt/srt.h, SRT_LIBS="-lsrt")
)
AC_SUBST(SRT_LIBS)
AC_SUBST(SRT_CFLAGS)
])
dnl *** libsrtp *** dnl *** libsrtp ***
translit(dnm, m, l) AM_CONDITIONAL(USE_SRTP, true) translit(dnm, m, l) AM_CONDITIONAL(USE_SRTP, true)
@ -3793,6 +3802,7 @@ ext/smoothstreaming/Makefile
ext/sndfile/Makefile ext/sndfile/Makefile
ext/soundtouch/Makefile ext/soundtouch/Makefile
ext/spandsp/Makefile ext/spandsp/Makefile
ext/srt/Makefile
ext/srtp/Makefile ext/srtp/Makefile
ext/teletextdec/Makefile ext/teletextdec/Makefile
ext/gme/Makefile ext/gme/Makefile

View file

@ -328,6 +328,12 @@ else
SPC_DIR= SPC_DIR=
endif endif
if USE_SRT
SRT_DIR=srt
else
SRT_DIR=
endif
if USE_SRTP if USE_SRTP
SRTP_DIR=srtp SRTP_DIR=srtp
else else
@ -457,6 +463,7 @@ SUBDIRS=\
$(SPANDSP_DIR) \ $(SPANDSP_DIR) \
$(GME_DIR) \ $(GME_DIR) \
$(SPC_DIR) \ $(SPC_DIR) \
$(SRT_DIR) \
$(SRTP_DIR) \ $(SRTP_DIR) \
$(TELETEXTDEC_DIR) \ $(TELETEXTDEC_DIR) \
$(WILDMIDI_DIR) \ $(WILDMIDI_DIR) \
@ -522,6 +529,7 @@ DIST_SUBDIRS = \
soundtouch \ soundtouch \
spandsp \ spandsp \
spc \ spc \
srt \
srtp \ srtp \
gme \ gme \
teletextdec \ teletextdec \

View file

@ -54,6 +54,7 @@ if cc.get_id() != 'msvc'
subdir('spandsp') subdir('spandsp')
endif endif
#subdir('spc') #subdir('spc')
subdir('srt')
subdir('srtp') subdir('srtp')
#subdir('teletextdec') #subdir('teletextdec')
#subdir('wildmidi') #subdir('wildmidi')

40
ext/srt/Makefile.am Normal file
View file

@ -0,0 +1,40 @@
plugin_LTLIBRARIES = libgstsrt.la
libgstsrt_la_SOURCES = \
gstsrt.c \
gstsrtbasesrc.c \
gstsrtclientsrc.c \
gstsrtserversrc.c \
gstsrtbasesink.c \
gstsrtclientsink.c \
gstsrtserversink.c \
$(NULL)
libgstsrt_la_CFLAGS = \
$(GST_PLUGINS_BASE_CFLAGS) \
$(GST_CFLAGS) \
$(GIO_CFLAGS) \
$(SRT_CFLAGS) \
$(NULL)
libgstsrt_la_LIBADD = \
$(GST_PLUGINS_BASE_LIBS) \
$(GST_LIBS) \
$(GIO_LIBS) \
-lgstbase-1.0 \
$(SRT_LIBS) \
$(NULL)
libgstsrt_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS)
CLEANFILES = $(BUILT_SOURCES)
noinst_HEADERS = \
gstsrtbasesink.h \
gstsrtclientsink.h \
gstsrtserversrc.h \
gstsrtbasesrc.h \
gstsrtclientsrc.h \
gstsrtserversink.h
include $(top_srcdir)/common/gst-glib-gen.mak

184
ext/srt/gstsrt.c Normal file
View file

@ -0,0 +1,184 @@
/* GStreamer
* Copyright (C) 2017, Collabora Ltd.
* Author:Justin Kim <justin.kim@collabora.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.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "gstsrt.h"
#include "gstsrtclientsrc.h"
#include "gstsrtserversrc.h"
#include "gstsrtclientsink.h"
#include "gstsrtserversink.h"
#define GST_CAT_DEFAULT gst_debug_srt
GST_DEBUG_CATEGORY (GST_CAT_DEFAULT);
SRTSOCKET
gst_srt_client_connect (GstElement * elem, int sender,
const gchar * host, guint16 port, int rendez_vous,
const gchar * bind_address, guint16 bind_port, int latency,
GSocketAddress ** socket_address, gint * poll_id)
{
SRTSOCKET sock;
GError *error = NULL;
gpointer sa;
size_t sa_len;
*socket_address = g_inet_socket_address_new_from_string (host, port);
if (*socket_address == NULL) {
GST_ELEMENT_ERROR (elem, RESOURCE, OPEN_READ, ("Invalid host"),
("Failed to parse host"));
goto failed;
}
sa_len = g_socket_address_get_native_size (*socket_address);
sa = g_alloca (sa_len);
if (!g_socket_address_to_native (*socket_address, sa, sa_len, &error)) {
GST_ELEMENT_ERROR (elem, RESOURCE, OPEN_READ, ("Invalid address"),
("cannot resolve address (reason: %s)", error->message));
goto failed;
}
sock = srt_socket (g_socket_address_get_family (*socket_address), SOCK_DGRAM,
0);
if (sock == SRT_ERROR) {
GST_ELEMENT_ERROR (elem, LIBRARY, INIT, (NULL),
("failed to create SRT socket (reason: %s)", srt_getlasterror_str ()));
goto failed;
}
/* Make sure TSBPD mode is enable (SRT mode) */
srt_setsockopt (sock, 0, SRTO_TSBPDMODE, &(int) {
1}, sizeof (int));
/* This is a sink, we're always a receiver */
srt_setsockopt (sock, 0, SRTO_SENDER, &sender, sizeof (int));
srt_setsockopt (sock, 0, SRTO_TSBPDDELAY, &latency, sizeof (int));
srt_setsockopt (sock, 0, SRTO_RENDEZVOUS, &rendez_vous, sizeof (int));
if (bind_address || bind_port || rendez_vous) {
gpointer bsa;
size_t bsa_len;
GSocketAddress *b_socket_address = NULL;
if (bind_address == NULL)
bind_address = "0.0.0.0";
if (rendez_vous)
bind_port = port;
b_socket_address = g_inet_socket_address_new_from_string (bind_address,
bind_port);
if (b_socket_address == NULL) {
GST_ELEMENT_ERROR (elem, RESOURCE, OPEN_READ, ("Invalid bind address"),
("Failed to parse bind address: %s:%d", bind_address, bind_port));
goto failed;
}
bsa_len = g_socket_address_get_native_size (b_socket_address);
bsa = g_alloca (bsa_len);
if (!g_socket_address_to_native (b_socket_address, bsa, bsa_len, &error)) {
GST_ELEMENT_ERROR (elem, RESOURCE, OPEN_READ, ("Invalid bind address"),
("Can't parse bind address to sockaddr: %s", error->message));
g_clear_object (&b_socket_address);
goto failed;
}
g_clear_object (&b_socket_address);
if (srt_bind (sock, bsa, bsa_len) == SRT_ERROR) {
GST_ELEMENT_ERROR (elem, RESOURCE, OPEN_READ,
("Can't bind to address"),
("Can't bind to %s:%d (reason: %s)", bind_address, bind_port,
srt_getlasterror_str ()));
goto failed;
}
}
*poll_id = srt_epoll_create ();
if (*poll_id == -1) {
GST_ELEMENT_ERROR (elem, LIBRARY, INIT, (NULL),
("failed to create poll id for SRT socket (reason: %s)",
srt_getlasterror_str ()));
goto failed;
}
srt_epoll_add_usock (*poll_id, sock, &(int) {
SRT_EPOLL_OUT});
if (srt_connect (sock, sa, sa_len) == SRT_ERROR) {
GST_ELEMENT_ERROR (elem, RESOURCE, OPEN_READ, ("Connection error"),
("failed to connect to host (reason: %s)", srt_getlasterror_str ()));
goto failed;
}
return sock;
failed:
if (*poll_id != SRT_ERROR) {
srt_epoll_release (*poll_id);
*poll_id = SRT_ERROR;
}
if (sock != SRT_INVALID_SOCK) {
srt_close (sock);
sock = SRT_INVALID_SOCK;
}
g_clear_error (&error);
g_clear_object (socket_address);
return SRT_INVALID_SOCK;
}
static gboolean
plugin_init (GstPlugin * plugin)
{
GST_DEBUG_CATEGORY_INIT (GST_CAT_DEFAULT, "srt", 0, "SRT Common code");
if (!gst_element_register (plugin, "srtclientsrc", GST_RANK_PRIMARY,
GST_TYPE_SRT_CLIENT_SRC))
return FALSE;
if (!gst_element_register (plugin, "srtserversrc", GST_RANK_PRIMARY,
GST_TYPE_SRT_SERVER_SRC))
return FALSE;
if (!gst_element_register (plugin, "srtclientsink", GST_RANK_PRIMARY,
GST_TYPE_SRT_CLIENT_SINK))
return FALSE;
if (!gst_element_register (plugin, "srtserversink", GST_RANK_PRIMARY,
GST_TYPE_SRT_SERVER_SINK))
return FALSE;
return TRUE;
}
GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
GST_VERSION_MINOR,
srt,
"transfer data via SRT",
plugin_init, VERSION, GST_LICENSE, GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN);

46
ext/srt/gstsrt.h Normal file
View file

@ -0,0 +1,46 @@
/* GStreamer
* Copyright (C) 2017, Collabora Ltd.
* Author: Olivier Crete <olivier.crete@collabora.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_SRT_H__
#define __GST_SRT_H__
#include <gst/gst.h>
#include <gio/gio.h>
#include <srt/srt.h>
#define SRT_URI_SCHEME "srt"
#define SRT_DEFAULT_PORT 7001
#define SRT_DEFAULT_HOST "127.0.0.1"
#define SRT_DEFAULT_URI SRT_URI_SCHEME"://"SRT_DEFAULT_HOST":"G_STRINGIFY(SRT_DEFAULT_PORT)
#define SRT_DEFAULT_LATENCY 125
G_BEGIN_DECLS
SRTSOCKET
gst_srt_client_connect (GstElement * elem, int sender,
const gchar * host, guint16 port, int rendez_vous,
const gchar * bind_address, guint16 bind_port, int latency,
GSocketAddress ** socket_address, gint * poll_id);
G_END_DECLS
#endif /* __GST_SRT_H__ */

302
ext/srt/gstsrtbasesink.c Normal file
View file

@ -0,0 +1,302 @@
/* GStreamer SRT plugin based on libsrt
* Copyright (C) 2017, Collabora Ltd.
* Author:Justin Kim <justin.kim@collabora.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.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "gstsrtserversink.h"
#include "gstsrt.h"
#include <srt/srt.h>
#include <netinet/in.h>
#define SRT_DEFAULT_POLL_TIMEOUT -1
#define GST_CAT_DEFAULT gst_debug_srt_base_sink
GST_DEBUG_CATEGORY (GST_CAT_DEFAULT);
enum
{
PROP_URI = 1,
PROP_LATENCY,
/*< private > */
PROP_LAST
};
static GParamSpec *properties[PROP_LAST];
static void gst_srt_base_sink_uri_handler_init (gpointer g_iface,
gpointer iface_data);
static gchar *gst_srt_base_sink_uri_get_uri (GstURIHandler * handler);
static gboolean gst_srt_base_sink_uri_set_uri (GstURIHandler * handler,
const gchar * uri, GError ** error);
#define gst_srt_base_sink_parent_class parent_class
G_DEFINE_ABSTRACT_TYPE_WITH_CODE (GstSRTBaseSink, gst_srt_base_sink,
GST_TYPE_BASE_SINK,
G_IMPLEMENT_INTERFACE (GST_TYPE_URI_HANDLER,
gst_srt_base_sink_uri_handler_init)
GST_DEBUG_CATEGORY_INIT (GST_CAT_DEFAULT, "srtbasesink", 0,
"SRT Base Sink"));
static void
gst_srt_base_sink_get_property (GObject * object,
guint prop_id, GValue * value, GParamSpec * pspec)
{
GstSRTBaseSink *self = GST_SRT_BASE_SINK (object);
switch (prop_id) {
case PROP_URI:
if (self->uri != NULL) {
gchar *uri_str = gst_srt_base_sink_uri_get_uri (GST_URI_HANDLER (self));
g_value_take_string (value, uri_str);
}
break;
case PROP_LATENCY:
g_value_set_int (value, self->latency);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
gst_srt_base_sink_set_property (GObject * object,
guint prop_id, const GValue * value, GParamSpec * pspec)
{
GstSRTBaseSink *self = GST_SRT_BASE_SINK (object);
switch (prop_id) {
case PROP_URI:
gst_srt_base_sink_uri_set_uri (GST_URI_HANDLER (self),
g_value_get_string (value), NULL);
break;
case PROP_LATENCY:
self->latency = g_value_get_int (value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
gst_srt_base_sink_finalize (GObject * object)
{
GstSRTBaseSink *self = GST_SRT_BASE_SINK (object);
g_clear_pointer (&self->uri, gst_uri_unref);
G_OBJECT_CLASS (parent_class)->finalize (object);
}
static GstFlowReturn
gst_srt_base_sink_render (GstBaseSink * sink, GstBuffer * buffer)
{
GstSRTBaseSink *self = GST_SRT_BASE_SINK (sink);
GstMapInfo info;
GstSRTBaseSinkClass *bclass = GST_SRT_BASE_SINK_GET_CLASS (sink);
GstFlowReturn ret = GST_FLOW_OK;
GST_TRACE_OBJECT (self, "sending buffer %p, offset %"
G_GINT64_FORMAT ", offset_end %" G_GINT64_FORMAT
", timestamp %" GST_TIME_FORMAT ", duration %" GST_TIME_FORMAT
", size %" G_GSIZE_FORMAT,
buffer, GST_BUFFER_OFFSET (buffer),
GST_BUFFER_OFFSET_END (buffer),
GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buffer)),
GST_TIME_ARGS (GST_BUFFER_DURATION (buffer)),
gst_buffer_get_size (buffer));
if (!gst_buffer_map (buffer, &info, GST_MAP_READ)) {
GST_ELEMENT_ERROR (self, RESOURCE, READ,
("Could not map the input stream"), (NULL));
return GST_FLOW_ERROR;
}
if (!bclass->send_buffer (self, &info))
ret = GST_FLOW_ERROR;
gst_buffer_unmap (buffer, &info);
return ret;
}
static void
gst_srt_base_sink_class_init (GstSRTBaseSinkClass * klass)
{
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
GstBaseSinkClass *gstbasesink_class = GST_BASE_SINK_CLASS (klass);
gobject_class->set_property = gst_srt_base_sink_set_property;
gobject_class->get_property = gst_srt_base_sink_get_property;
gobject_class->finalize = gst_srt_base_sink_finalize;
/**
* GstSRTBaseSink:uri:
*
* The URI used by SRT Connection.
*/
properties[PROP_URI] = g_param_spec_string ("uri", "URI",
"URI in the form of srt://address:port", SRT_DEFAULT_URI,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
properties[PROP_LATENCY] =
g_param_spec_int ("latency", "latency",
"Minimum latency (milliseconds)", 0,
G_MAXINT32, SRT_DEFAULT_LATENCY,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
g_object_class_install_properties (gobject_class, PROP_LAST, properties);
gstbasesink_class->render = GST_DEBUG_FUNCPTR (gst_srt_base_sink_render);
}
static void
gst_srt_base_sink_init (GstSRTBaseSink * self)
{
self->uri = gst_uri_from_string (SRT_DEFAULT_URI);
self->queued_buffers = NULL;
self->latency = SRT_DEFAULT_LATENCY;
}
static GstURIType
gst_srt_base_sink_uri_get_type (GType type)
{
return GST_URI_SINK;
}
static const gchar *const *
gst_srt_base_sink_uri_get_protocols (GType type)
{
static const gchar *protocols[] = { SRT_URI_SCHEME, NULL };
return protocols;
}
static gchar *
gst_srt_base_sink_uri_get_uri (GstURIHandler * handler)
{
gchar *uri_str;
GstSRTBaseSink *self = GST_SRT_BASE_SINK (handler);
GST_OBJECT_LOCK (self);
uri_str = gst_uri_to_string (self->uri);
GST_OBJECT_UNLOCK (self);
return uri_str;
}
static gboolean
gst_srt_base_sink_uri_set_uri (GstURIHandler * handler,
const gchar * uri, GError ** error)
{
GstSRTBaseSink *self = GST_SRT_BASE_SINK (handler);
gboolean ret = TRUE;
GstUri *parsed_uri = gst_uri_from_string (uri);
GST_TRACE_OBJECT (self, "Requested URI=%s", uri);
if (g_strcmp0 (gst_uri_get_scheme (parsed_uri), SRT_URI_SCHEME) != 0) {
g_set_error (error, GST_URI_ERROR, GST_URI_ERROR_BAD_URI,
"Invalid SRT URI scheme");
ret = FALSE;
goto out;
}
GST_OBJECT_LOCK (self);
g_clear_pointer (&self->uri, gst_uri_unref);
self->uri = gst_uri_ref (parsed_uri);
GST_OBJECT_UNLOCK (self);
out:
g_clear_pointer (&parsed_uri, gst_uri_unref);
return ret;
}
static void
gst_srt_base_sink_uri_handler_init (gpointer g_iface, gpointer iface_data)
{
GstURIHandlerInterface *iface = (GstURIHandlerInterface *) g_iface;
iface->get_type = gst_srt_base_sink_uri_get_type;
iface->get_protocols = gst_srt_base_sink_uri_get_protocols;
iface->get_uri = gst_srt_base_sink_uri_get_uri;
iface->set_uri = gst_srt_base_sink_uri_set_uri;
}
GstStructure *
gst_srt_base_sink_get_stats (GSocketAddress * sockaddr, SRTSOCKET sock)
{
SRT_TRACEBSTATS stats;
int ret;
GValue v = G_VALUE_INIT;
GstStructure *s;
if (sock == SRT_INVALID_SOCK || sockaddr == NULL)
return gst_structure_new_empty ("application/x-srt-statistics");
s = gst_structure_new ("application/x-srt-statistics",
"sockaddr", G_TYPE_SOCKET_ADDRESS, sockaddr, NULL);
ret = srt_bstats (sock, &stats, 0);
if (ret >= 0) {
gst_structure_set (s,
/* number of sent data packets, including retransmissions */
"packets-sent", G_TYPE_INT64, stats.pktSent,
/* number of lost packets (sender side) */
"packets-sent-lost", G_TYPE_INT, stats.pktSndLoss,
/* number of retransmitted packets */
"packets-retransmitted", G_TYPE_INT, stats.pktRetrans,
/* number of received ACK packets */
"packet-ack-received", G_TYPE_INT, stats.pktRecvACK,
/* number of received NAK packets */
"packet-nack-received", G_TYPE_INT, stats.pktRecvNAK,
/* time duration when UDT is sending data (idle time exclusive) */
"send-duration-us", G_TYPE_INT64, stats.usSndDuration,
/* number of sent data bytes, including retransmissions */
"bytes-sent", G_TYPE_UINT64, stats.byteSent,
/* number of retransmitted bytes */
"bytes-retransmitted", G_TYPE_UINT64, stats.byteRetrans,
/* number of too-late-to-send dropped bytes */
"bytes-sent-dropped", G_TYPE_UINT64, stats.byteSndDrop,
/* number of too-late-to-send dropped packets */
"packets-sent-dropped", G_TYPE_INT, stats.pktSndDrop,
/* sending rate in Mb/s */
"send-rate-mbps", G_TYPE_DOUBLE, stats.msRTT,
/* estimated bandwidth, in Mb/s */
"bandwidth-mbps", G_TYPE_DOUBLE, stats.mbpsBandwidth,
/* busy sending time (i.e., idle time exclusive) */
"send-duration-us", G_TYPE_UINT64, stats.usSndDuration,
"rtt-ms", G_TYPE_DOUBLE, stats.msRTT,
"negotiated-latency-ms", G_TYPE_INT, stats.msSndTsbPdDelay, NULL);
}
g_value_init (&v, G_TYPE_STRING);
g_value_take_string (&v,
g_socket_connectable_to_string (G_SOCKET_CONNECTABLE (sockaddr)));
gst_structure_take_value (s, "sockaddr-str", &v);
return s;
}

73
ext/srt/gstsrtbasesink.h Normal file
View file

@ -0,0 +1,73 @@
/* GStreamer
* Copyright (C) 2017, Collabora Ltd.
* Author:Justin Kim <justin.kim@collabora.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_SRT_BASE_SINK_H__
#define __GST_SRT_BASE_SINK_H__
#include <gst/gst.h>
#include <gst/base/gstbasesink.h>
#include <gio/gio.h>
#include <srt/srt.h>
G_BEGIN_DECLS
#define GST_TYPE_SRT_BASE_SINK (gst_srt_base_sink_get_type ())
#define GST_IS_SRT_BASE_SINK(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_SRT_BASE_SINK))
#define GST_IS_SRT_BASE_SINK_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GST_TYPE_SRT_BASE_SINK))
#define GST_SRT_BASE_SINK_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_SRT_BASE_SINK, GstSRTBaseSinkClass))
#define GST_SRT_BASE_SINK(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_SRT_BASE_SINK, GstSRTBaseSink))
#define GST_SRT_BASE_SINK_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GST_TYPE_SRT_BASE_SINK, GstSRTBaseSinkClass))
#define GST_SRT_BASE_SINK_CAST(obj) ((GstSRTBaseSink*)(obj))
#define GST_SRT_BASE_SINK_CLASS_CAST(klass) ((GstSRTBaseSinkClass*)(klass))
typedef struct _GstSRTBaseSink GstSRTBaseSink;
typedef struct _GstSRTBaseSinkClass GstSRTBaseSinkClass;
struct _GstSRTBaseSink {
GstBaseSink parent;
GstUri *uri;
GList *queued_buffers;
gint latency;
/*< private >*/
gpointer _gst_reserved[GST_PADDING];
};
struct _GstSRTBaseSinkClass {
GstBaseSinkClass parent_class;
/* ask the subclass to send a buffer */
gboolean (*send_buffer) (GstSRTBaseSink *self, const GstMapInfo *mapinfo);
gpointer _gst_reserved[GST_PADDING_LARGE];
};
GST_EXPORT
GType gst_srt_base_sink_get_type (void);
GstStructure * gst_srt_base_sink_get_stats (GSocketAddress *sockaddr,
SRTSOCKET sock);
G_END_DECLS
#endif /* __GST_SRT_BASE_SINK_H__ */

262
ext/srt/gstsrtbasesrc.c Normal file
View file

@ -0,0 +1,262 @@
/* GStreamer SRT plugin based on libsrt
* Copyright (C) 2017, Collabora Ltd.
* Author:Justin Kim <justin.kim@collabora.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.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "gstsrtbasesrc.h"
#include "gstsrt.h"
#include <srt/srt.h>
#include <gio/gio.h>
#include <netinet/in.h>
#define GST_CAT_DEFAULT gst_debug_srt_base_src
GST_DEBUG_CATEGORY (GST_CAT_DEFAULT);
enum
{
PROP_URI = 1,
PROP_CAPS,
PROP_LATENCY,
/*< private > */
PROP_LAST
};
static GParamSpec *properties[PROP_LAST];
static void gst_srt_base_src_uri_handler_init (gpointer g_iface,
gpointer iface_data);
static gchar *gst_srt_base_src_uri_get_uri (GstURIHandler * handler);
static gboolean gst_srt_base_src_uri_set_uri (GstURIHandler * handler,
const gchar * uri, GError ** error);
#define gst_srt_base_src_parent_class parent_class
G_DEFINE_ABSTRACT_TYPE_WITH_CODE (GstSRTBaseSrc, gst_srt_base_src,
GST_TYPE_PUSH_SRC, G_IMPLEMENT_INTERFACE (GST_TYPE_URI_HANDLER,
gst_srt_base_src_uri_handler_init)
GST_DEBUG_CATEGORY_INIT (GST_CAT_DEFAULT, "srtbasesrc", 0,
"SRT Base Source"));
static void
gst_srt_base_src_get_property (GObject * object,
guint prop_id, GValue * value, GParamSpec * pspec)
{
GstSRTBaseSrc *self = GST_SRT_BASE_SRC (object);
switch (prop_id) {
case PROP_URI:
if (self->uri != NULL) {
gchar *uri_str = gst_srt_base_src_uri_get_uri (GST_URI_HANDLER (self));
g_value_take_string (value, uri_str);
}
break;
case PROP_CAPS:
GST_OBJECT_LOCK (self);
gst_value_set_caps (value, self->caps);
GST_OBJECT_UNLOCK (self);
break;
case PROP_LATENCY:
g_value_set_int (value, self->latency);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
gst_srt_base_src_set_property (GObject * object,
guint prop_id, const GValue * value, GParamSpec * pspec)
{
GstSRTBaseSrc *self = GST_SRT_BASE_SRC (object);
switch (prop_id) {
case PROP_URI:
gst_srt_base_src_uri_set_uri (GST_URI_HANDLER (self),
g_value_get_string (value), NULL);
break;
case PROP_CAPS:
GST_OBJECT_LOCK (self);
g_clear_pointer (&self->caps, gst_caps_unref);
self->caps = gst_caps_copy (gst_value_get_caps (value));
GST_OBJECT_UNLOCK (self);
break;
case PROP_LATENCY:
self->latency = g_value_get_int (value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
gst_srt_base_src_finalize (GObject * object)
{
GstSRTBaseSrc *self = GST_SRT_BASE_SRC (object);
g_clear_pointer (&self->uri, gst_uri_unref);
g_clear_pointer (&self->caps, gst_caps_unref);
G_OBJECT_CLASS (parent_class)->finalize (object);
}
static GstCaps *
gst_srt_base_src_get_caps (GstBaseSrc * src, GstCaps * filter)
{
GstSRTBaseSrc *self = GST_SRT_BASE_SRC (src);
GstCaps *result, *caps = NULL;
GST_OBJECT_LOCK (self);
if (self->caps != NULL) {
caps = gst_caps_ref (self->caps);
}
GST_OBJECT_UNLOCK (self);
if (caps) {
if (filter) {
result = gst_caps_intersect_full (filter, caps, GST_CAPS_INTERSECT_FIRST);
gst_caps_unref (caps);
} else {
result = caps;
}
} else {
result = (filter) ? gst_caps_ref (filter) : gst_caps_new_any ();
}
return result;
}
static void
gst_srt_base_src_class_init (GstSRTBaseSrcClass * klass)
{
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
GstBaseSrcClass *gstbasesrc_class = GST_BASE_SRC_CLASS (klass);
gobject_class->set_property = gst_srt_base_src_set_property;
gobject_class->get_property = gst_srt_base_src_get_property;
gobject_class->finalize = gst_srt_base_src_finalize;
/**
* GstSRTBaseSrc:uri:
*
* The URI used by SRT Connection.
*/
properties[PROP_URI] = g_param_spec_string ("uri", "URI",
"URI in the form of srt://address:port", SRT_DEFAULT_URI,
G_PARAM_READWRITE | GST_PARAM_MUTABLE_READY | G_PARAM_STATIC_STRINGS);
/**
* GstSRTBaseSrc:caps:
*
* The Caps used by the source pad.
*/
properties[PROP_CAPS] =
g_param_spec_boxed ("caps", "Caps", "The caps of the source pad",
GST_TYPE_CAPS, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
properties[PROP_LATENCY] =
g_param_spec_int ("latency", "latency",
"Minimum latency (milliseconds)", 0,
G_MAXINT32, SRT_DEFAULT_LATENCY,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
g_object_class_install_properties (gobject_class, PROP_LAST, properties);
gstbasesrc_class->get_caps = GST_DEBUG_FUNCPTR (gst_srt_base_src_get_caps);
}
static void
gst_srt_base_src_init (GstSRTBaseSrc * self)
{
gst_srt_base_src_uri_set_uri (GST_URI_HANDLER (self), SRT_DEFAULT_URI, NULL);
gst_base_src_set_format (GST_BASE_SRC (self), GST_FORMAT_TIME);
gst_base_src_set_live (GST_BASE_SRC (self), TRUE);
self->latency = SRT_DEFAULT_LATENCY;
}
static GstURIType
gst_srt_base_src_uri_get_type (GType type)
{
return GST_URI_SRC;
}
static const gchar *const *
gst_srt_base_src_uri_get_protocols (GType type)
{
static const gchar *protocols[] = { SRT_URI_SCHEME, NULL };
return protocols;
}
static gchar *
gst_srt_base_src_uri_get_uri (GstURIHandler * handler)
{
gchar *uri_str;
GstSRTBaseSrc *self = GST_SRT_BASE_SRC (handler);
GST_OBJECT_LOCK (self);
uri_str = gst_uri_to_string (self->uri);
GST_OBJECT_UNLOCK (self);
return uri_str;
}
static gboolean
gst_srt_base_src_uri_set_uri (GstURIHandler * handler,
const gchar * uri, GError ** error)
{
GstSRTBaseSrc *self = GST_SRT_BASE_SRC (handler);
gboolean ret = TRUE;
GstUri *parsed_uri = gst_uri_from_string (uri);
if (g_strcmp0 (gst_uri_get_scheme (parsed_uri), SRT_URI_SCHEME) != 0) {
g_set_error (error, GST_URI_ERROR, GST_URI_ERROR_BAD_URI,
"Invalid SRT URI scheme");
ret = FALSE;
goto out;
}
GST_OBJECT_LOCK (self);
g_clear_pointer (&self->uri, gst_uri_unref);
self->uri = gst_uri_ref (parsed_uri);
GST_OBJECT_UNLOCK (self);
out:
g_clear_pointer (&parsed_uri, gst_uri_unref);
return ret;
}
static void
gst_srt_base_src_uri_handler_init (gpointer g_iface, gpointer iface_data)
{
GstURIHandlerInterface *iface = (GstURIHandlerInterface *) g_iface;
iface->get_type = gst_srt_base_src_uri_get_type;
iface->get_protocols = gst_srt_base_src_uri_get_protocols;
iface->get_uri = gst_srt_base_src_uri_get_uri;
iface->set_uri = gst_srt_base_src_uri_set_uri;
}

63
ext/srt/gstsrtbasesrc.h Normal file
View file

@ -0,0 +1,63 @@
/* GStreamer
* Copyright (C) 2017, Collabora Ltd.
* Author:Justin Kim <justin.kim@collabora.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_SRT_BASE_SRC_H__
#define __GST_SRT_BASE_SRC_H__
#include <gst/gst.h>
#include <gst/base/gstpushsrc.h>
G_BEGIN_DECLS
#define GST_TYPE_SRT_BASE_SRC (gst_srt_base_src_get_type ())
#define GST_IS_SRT_BASE_SRC(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_SRT_BASE_SRC))
#define GST_IS_SRT_BASE_SRC_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GST_TYPE_SRT_BASE_SRC))
#define GST_SRT_BASE_SRC_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_SRT_BASE_SRC, GstSRTBaseSrcClass))
#define GST_SRT_BASE_SRC(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_SRT_BASE_SRC, GstSRTBaseSrc))
#define GST_SRT_BASE_SRC_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GST_TYPE_SRT_BASE_SRC, GstSRTBaseSrcClass))
#define GST_SRT_BASE_SRC_CAST(obj) ((GstSRTBaseSrc*)(obj))
#define GST_SRT_BASE_SRC_CLASS_CAST(klass) ((GstSRTBaseSrcClass*)(klass))
typedef struct _GstSRTBaseSrc GstSRTBaseSrc;
typedef struct _GstSRTBaseSrcClass GstSRTBaseSrcClass;
struct _GstSRTBaseSrc {
GstPushSrc parent;
GstUri *uri;
GstCaps *caps;
gint latency;
/*< private >*/
gpointer _gst_reserved[GST_PADDING];
};
struct _GstSRTBaseSrcClass {
GstPushSrcClass parent_class;
gpointer _gst_reserved[GST_PADDING_LARGE];
};
GST_EXPORT
GType gst_srt_base_src_get_type (void);
G_END_DECLS
#endif /* __GST_SRT_BASE_SRC_H__ */

268
ext/srt/gstsrtclientsink.c Normal file
View file

@ -0,0 +1,268 @@
/* GStreamer SRT plugin based on libsrt
* Copyright (C) 2017, Collabora Ltd.
* Author:Justin Kim <justin.kim@collabora.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.
*/
/**
* SECTION:element-srtserversink
* @title: srtserversink
*
* srtserversink is a network sink that sends <ulink url="http://www.srtalliance.org/">SRT</ulink>
* packets to the network. Although SRT is an UDP-based protocol, srtserversink works like
* a server socket of connection-oriented protocol.
*
* <refsect2>
* <title>Examples</title>
* |[
* gst-launch-1.0 -v audiotestsrc ! srtserversink
* ]| This pipeline shows how to serve SRT packets through the default port.
* |[
* gst-launch-1.0 -v audiotestsrc ! srtserversink uri=srt://192.168.1.10:8888/ rendez-vous=1
* ]| This pipeline shows how to serve SRT packets to 192.168.1.10 port 8888 using the rendez-vous mode.
* </refsect2>
*
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "gstsrtclientsink.h"
#include "gstsrt.h"
#include <srt/srt.h>
#include <gio/gio.h>
#define SRT_DEFAULT_POLL_TIMEOUT -1
static GstStaticPadTemplate sink_template = GST_STATIC_PAD_TEMPLATE ("sink",
GST_PAD_SINK,
GST_PAD_ALWAYS,
GST_STATIC_CAPS_ANY);
#define GST_CAT_DEFAULT gst_debug_srt_client_sink
GST_DEBUG_CATEGORY (GST_CAT_DEFAULT);
struct _GstSRTClientSinkPrivate
{
SRTSOCKET sock;
GSocketAddress *sockaddr;
gint poll_id;
gint poll_timeout;
gboolean rendez_vous;
gchar *bind_address;
guint16 bind_port;
};
#define GST_SRT_CLIENT_SINK_GET_PRIVATE(obj) \
(G_TYPE_INSTANCE_GET_PRIVATE ((obj), GST_TYPE_SRT_CLIENT_SINK, GstSRTClientSinkPrivate))
enum
{
PROP_POLL_TIMEOUT = 1,
PROP_BIND_ADDRESS,
PROP_BIND_PORT,
PROP_RENDEZ_VOUS,
PROP_STATS,
/*< private > */
PROP_LAST
};
static GParamSpec *properties[PROP_LAST];
#define gst_srt_client_sink_parent_class parent_class
G_DEFINE_TYPE_WITH_CODE (GstSRTClientSink, gst_srt_client_sink,
GST_TYPE_SRT_BASE_SINK, G_ADD_PRIVATE (GstSRTClientSink)
GST_DEBUG_CATEGORY_INIT (GST_CAT_DEFAULT, "srtclientsink", 0,
"SRT Client Sink"));
static void
gst_srt_client_sink_get_property (GObject * object,
guint prop_id, GValue * value, GParamSpec * pspec)
{
GstSRTClientSink *self = GST_SRT_CLIENT_SINK (object);
GstSRTClientSinkPrivate *priv = GST_SRT_CLIENT_SINK_GET_PRIVATE (self);
switch (prop_id) {
case PROP_POLL_TIMEOUT:
g_value_set_int (value, priv->poll_timeout);
break;
case PROP_BIND_PORT:
g_value_set_int (value, priv->rendez_vous);
break;
case PROP_BIND_ADDRESS:
g_value_set_string (value, priv->bind_address);
break;
case PROP_RENDEZ_VOUS:
g_value_set_boolean (value, priv->bind_port);
break;
case PROP_STATS:
g_value_take_boxed (value, gst_srt_base_sink_get_stats (priv->sockaddr,
priv->sock));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
gst_srt_client_sink_set_property (GObject * object,
guint prop_id, const GValue * value, GParamSpec * pspec)
{
GstSRTClientSink *self = GST_SRT_CLIENT_SINK (object);
GstSRTClientSinkPrivate *priv = GST_SRT_CLIENT_SINK_GET_PRIVATE (self);
switch (prop_id) {
case PROP_POLL_TIMEOUT:
priv->poll_timeout = g_value_get_int (value);
break;
case PROP_BIND_ADDRESS:
g_free (priv->bind_address);
priv->bind_address = g_value_dup_string (value);
break;
case PROP_BIND_PORT:
priv->bind_port = g_value_get_int (value);
break;
case PROP_RENDEZ_VOUS:
priv->rendez_vous = g_value_get_boolean (value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static gboolean
gst_srt_client_sink_start (GstBaseSink * sink)
{
GstSRTClientSink *self = GST_SRT_CLIENT_SINK (sink);
GstSRTClientSinkPrivate *priv = GST_SRT_CLIENT_SINK_GET_PRIVATE (self);
GstSRTBaseSink *base = GST_SRT_BASE_SINK (sink);
GstUri *uri = gst_uri_ref (GST_SRT_BASE_SINK (self)->uri);
priv->sock = gst_srt_client_connect (GST_ELEMENT (sink), FALSE,
gst_uri_get_host (uri), gst_uri_get_port (uri), priv->rendez_vous,
priv->bind_address, priv->bind_port, base->latency,
&priv->sockaddr, &priv->poll_id);
g_clear_pointer (&uri, gst_uri_unref);
return (priv->sock != SRT_INVALID_SOCK);
}
static gboolean
gst_srt_client_sink_send_buffer (GstSRTBaseSink * sink,
const GstMapInfo * mapinfo)
{
GstSRTClientSink *self = GST_SRT_CLIENT_SINK (sink);
GstSRTClientSinkPrivate *priv = GST_SRT_CLIENT_SINK_GET_PRIVATE (self);
if (srt_sendmsg2 (priv->sock, (char *) mapinfo->data, mapinfo->size,
0) == SRT_ERROR) {
GST_ELEMENT_ERROR (self, RESOURCE, WRITE, NULL,
("%s", srt_getlasterror_str ()));
return FALSE;
}
return TRUE;
}
static gboolean
gst_srt_client_sink_stop (GstBaseSink * sink)
{
GstSRTClientSink *self = GST_SRT_CLIENT_SINK (sink);
GstSRTClientSinkPrivate *priv = GST_SRT_CLIENT_SINK_GET_PRIVATE (self);
GST_DEBUG_OBJECT (self, "closing SRT connection");
if (priv->poll_id != SRT_ERROR) {
srt_epoll_remove_usock (priv->poll_id, priv->sock);
srt_epoll_release (priv->poll_id);
priv->poll_id = SRT_ERROR;
}
if (priv->sock != SRT_INVALID_SOCK) {
srt_close (priv->sock);
priv->sock = SRT_INVALID_SOCK;
}
g_clear_object (&priv->sockaddr);
return TRUE;
}
static void
gst_srt_client_sink_class_init (GstSRTClientSinkClass * klass)
{
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
GstElementClass *gstelement_class = GST_ELEMENT_CLASS (klass);
GstBaseSinkClass *gstbasesink_class = GST_BASE_SINK_CLASS (klass);
GstSRTBaseSinkClass *gstsrtbasesink_class = GST_SRT_BASE_SINK_CLASS (klass);
gobject_class->set_property = gst_srt_client_sink_set_property;
gobject_class->get_property = gst_srt_client_sink_get_property;
properties[PROP_POLL_TIMEOUT] =
g_param_spec_int ("poll-timeout", "Poll Timeout",
"Return poll wait after timeout miliseconds (-1 = infinite)", -1,
G_MAXINT32, SRT_DEFAULT_POLL_TIMEOUT,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
properties[PROP_BIND_ADDRESS] =
g_param_spec_string ("bind-address", "Bind Address",
"Address to bind socket to (required for rendez-vous mode) ", NULL,
G_PARAM_READWRITE | GST_PARAM_MUTABLE_READY | G_PARAM_STATIC_STRINGS);
properties[PROP_BIND_PORT] =
g_param_spec_int ("bind-port", "Bind Port",
"Port to bind socket to (Ignored in rendez-vous mode)", 0,
G_MAXUINT16, 0,
G_PARAM_READWRITE | GST_PARAM_MUTABLE_READY | G_PARAM_STATIC_STRINGS);
properties[PROP_RENDEZ_VOUS] =
g_param_spec_boolean ("rendez-vous", "Rendez Vous",
"Work in Rendez-Vous mode instead of client/caller mode", FALSE,
G_PARAM_READWRITE | GST_PARAM_MUTABLE_READY | G_PARAM_STATIC_STRINGS);
properties[PROP_STATS] = g_param_spec_boxed ("stats", "Statistics",
"SRT Statistics", GST_TYPE_STRUCTURE,
G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
g_object_class_install_properties (gobject_class, PROP_LAST, properties);
gst_element_class_add_static_pad_template (gstelement_class, &sink_template);
gst_element_class_set_metadata (gstelement_class,
"SRT client sink", "Sink/Network",
"Send data over the network via SRT",
"Justin Kim <justin.kim@collabora.com>");
gstbasesink_class->start = GST_DEBUG_FUNCPTR (gst_srt_client_sink_start);
gstbasesink_class->stop = GST_DEBUG_FUNCPTR (gst_srt_client_sink_stop);
gstsrtbasesink_class->send_buffer =
GST_DEBUG_FUNCPTR (gst_srt_client_sink_send_buffer);
}
static void
gst_srt_client_sink_init (GstSRTClientSink * self)
{
GstSRTClientSinkPrivate *priv = GST_SRT_CLIENT_SINK_GET_PRIVATE (self);
priv->poll_timeout = SRT_DEFAULT_POLL_TIMEOUT;
}

View file

@ -0,0 +1,59 @@
/* GStreamer
* Copyright (C) 2017, Collabora Ltd.
* Author:Justin Kim <justin.kim@collabora.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_SRT_CLIENT_SINK_H__
#define __GST_SRT_CLIENT_SINK_H__
#include "gstsrtbasesink.h"
G_BEGIN_DECLS
#define GST_TYPE_SRT_CLIENT_SINK (gst_srt_client_sink_get_type ())
#define GST_IS_SRT_CLIENT_SINK(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_SRT_CLIENT_SINK))
#define GST_IS_SRT_CLIENT_SINK_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GST_TYPE_SRT_CLIENT_SINK))
#define GST_SRT_CLIENT_SINK_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_SRT_CLIENT_SINK, GstSRTClientSinkClass))
#define GST_SRT_CLIENT_SINK(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_SRT_CLIENT_SINK, GstSRTClientSink))
#define GST_SRT_CLIENT_SINK_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GST_TYPE_SRT_CLIENT_SINK, GstSRTClientSinkClass))
#define GST_SRT_CLIENT_SINK_CAST(obj) ((GstSRTClientSink*)(obj))
#define GST_SRT_CLIENT_SINK_CLASS_CAST(klass) ((GstSRTClientSinkClass*)(klass))
typedef struct _GstSRTClientSink GstSRTClientSink;
typedef struct _GstSRTClientSinkClass GstSRTClientSinkClass;
typedef struct _GstSRTClientSinkPrivate GstSRTClientSinkPrivate;
struct _GstSRTClientSink {
GstSRTBaseSink parent;
/*< private >*/
gpointer _gst_reserved[GST_PADDING];
};
struct _GstSRTClientSinkClass {
GstSRTBaseSinkClass parent_class;
gpointer _gst_reserved[GST_PADDING_LARGE];
};
GST_EXPORT
GType gst_srt_client_sink_get_type (void);
G_END_DECLS
#endif /* __GST_SRT_CLIENT_SINK_H__ */

339
ext/srt/gstsrtclientsrc.c Normal file
View file

@ -0,0 +1,339 @@
/* GStreamer SRT plugin based on libsrt
* Copyright (C) 2017, Collabora Ltd.
* Author:Justin Kim <justin.kim@collabora.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.
*/
/**
* SECTION:element-srtclientsrc
* @title: srtclientsrc
*
* srtclientsrc is a network source that reads <ulink url="http://www.srtalliance.org/">SRT</ulink>
* packets from the network. Although SRT is a protocol based on UDP, srtclientsrc works like
* a client socket of connection-oriented protocol.
*
* <refsect2>
* <title>Examples</title>
* |[
* gst-launch-1.0 -v srtclientsrc uri="srt://127.0.0.1:7001" ! fakesink
* ]| This pipeline shows how to connect SRT server by setting #GstSRTClientSrc:uri property.
*
* |[
* gst-launch-1.0 -v srtclientsrc uri="srt://192.168.1.10:7001" rendez-vous ! fakesink
* ]| This pipeline shows how to connect SRT server by setting #GstSRTClientSrc:uri property and using the rendez-vous mode.
* </refsect2>
*
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "gstsrtclientsrc.h"
#include <srt/srt.h>
#include <gio/gio.h>
#include "gstsrt.h"
#include <netinet/in.h>
static GstStaticPadTemplate src_template = GST_STATIC_PAD_TEMPLATE ("src",
GST_PAD_SRC,
GST_PAD_ALWAYS,
GST_STATIC_CAPS_ANY);
#define GST_CAT_DEFAULT gst_debug_srt_client_src
GST_DEBUG_CATEGORY (GST_CAT_DEFAULT);
struct _GstSRTClientSrcPrivate
{
SRTSOCKET sock;
gint poll_id;
gint poll_timeout;
gboolean rendez_vous;
gchar *bind_address;
guint16 bind_port;
};
#define GST_SRT_CLIENT_SRC_GET_PRIVATE(obj) \
(G_TYPE_INSTANCE_GET_PRIVATE ((obj), GST_TYPE_SRT_CLIENT_SRC, GstSRTClientSrcPrivate))
#define SRT_DEFAULT_POLL_TIMEOUT -1
enum
{
PROP_POLL_TIMEOUT = 1,
PROP_BIND_ADDRESS,
PROP_BIND_PORT,
PROP_RENDEZ_VOUS,
/*< private > */
PROP_LAST
};
static GParamSpec *properties[PROP_LAST + 1];
#define gst_srt_client_src_parent_class parent_class
G_DEFINE_TYPE_WITH_CODE (GstSRTClientSrc, gst_srt_client_src,
GST_TYPE_SRT_BASE_SRC, G_ADD_PRIVATE (GstSRTClientSrc)
GST_DEBUG_CATEGORY_INIT (GST_CAT_DEFAULT, "srtclientsrc", 0,
"SRT Client Source"));
static void
gst_srt_client_src_get_property (GObject * object,
guint prop_id, GValue * value, GParamSpec * pspec)
{
GstSRTClientSrc *self = GST_SRT_CLIENT_SRC (object);
GstSRTClientSrcPrivate *priv = GST_SRT_CLIENT_SRC_GET_PRIVATE (self);
switch (prop_id) {
case PROP_POLL_TIMEOUT:
g_value_set_int (value, priv->poll_timeout);
break;
case PROP_BIND_PORT:
g_value_set_int (value, priv->rendez_vous);
break;
case PROP_BIND_ADDRESS:
g_value_set_string (value, priv->bind_address);
break;
case PROP_RENDEZ_VOUS:
g_value_set_boolean (value, priv->bind_port);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
gst_srt_client_src_set_property (GObject * object,
guint prop_id, const GValue * value, GParamSpec * pspec)
{
GstSRTBaseSrc *self = GST_SRT_BASE_SRC (object);
GstSRTClientSrcPrivate *priv = GST_SRT_CLIENT_SRC_GET_PRIVATE (self);
switch (prop_id) {
case PROP_POLL_TIMEOUT:
priv->poll_timeout = g_value_get_int (value);
break;
case PROP_BIND_ADDRESS:
g_free (priv->bind_address);
priv->bind_address = g_value_dup_string (value);
break;
case PROP_BIND_PORT:
priv->bind_port = g_value_get_int (value);
break;
case PROP_RENDEZ_VOUS:
priv->rendez_vous = g_value_get_boolean (value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
gst_srt_client_src_finalize (GObject * object)
{
GstSRTClientSrc *self = GST_SRT_CLIENT_SRC (object);
GstSRTClientSrcPrivate *priv = GST_SRT_CLIENT_SRC_GET_PRIVATE (self);
if (priv->poll_id != SRT_ERROR) {
srt_epoll_release (priv->poll_id);
priv->poll_id = SRT_ERROR;
}
if (priv->sock != SRT_INVALID_SOCK) {
srt_close (priv->sock);
priv->sock = SRT_INVALID_SOCK;
}
g_free (priv->bind_address);
G_OBJECT_CLASS (parent_class)->finalize (object);
}
static GstFlowReturn
gst_srt_client_src_fill (GstPushSrc * src, GstBuffer * outbuf)
{
GstSRTClientSrc *self = GST_SRT_CLIENT_SRC (src);
GstSRTClientSrcPrivate *priv = GST_SRT_CLIENT_SRC_GET_PRIVATE (self);
GstFlowReturn ret = GST_FLOW_OK;
GstMapInfo info;
SRTSOCKET ready[2];
gint recv_len;
if (srt_epoll_wait (priv->poll_id, 0, 0, ready, &(int) {
2}, priv->poll_timeout, 0, 0, 0, 0) == -1) {
/* Assuming that timeout error is normal */
if (srt_getlasterror (NULL) != SRT_ETIMEOUT) {
GST_ELEMENT_ERROR (src, RESOURCE, READ,
(NULL), ("srt_epoll_wait error: %s", srt_getlasterror_str ()));
ret = GST_FLOW_ERROR;
}
srt_clearlasterror ();
goto out;
}
if (!gst_buffer_map (outbuf, &info, GST_MAP_WRITE)) {
GST_ELEMENT_ERROR (src, RESOURCE, READ,
("Could not map the buffer for writing "), (NULL));
ret = GST_FLOW_ERROR;
goto out;
}
recv_len = srt_recvmsg (priv->sock, (char *) info.data,
gst_buffer_get_size (outbuf));
gst_buffer_unmap (outbuf, &info);
if (recv_len == SRT_ERROR) {
GST_ELEMENT_ERROR (src, RESOURCE, READ,
(NULL), ("srt_recvmsg error: %s", srt_getlasterror_str ()));
ret = GST_FLOW_ERROR;
goto out;
} else if (recv_len == 0) {
ret = GST_FLOW_EOS;
goto out;
}
GST_BUFFER_PTS (outbuf) =
gst_clock_get_time (GST_ELEMENT_CLOCK (src)) -
GST_ELEMENT_CAST (src)->base_time;
gst_buffer_resize (outbuf, 0, recv_len);
GST_LOG_OBJECT (src,
"filled buffer from _get of size %" G_GSIZE_FORMAT ", ts %"
GST_TIME_FORMAT ", dur %" GST_TIME_FORMAT
", offset %" G_GINT64_FORMAT ", offset_end %" G_GINT64_FORMAT,
gst_buffer_get_size (outbuf),
GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (outbuf)),
GST_TIME_ARGS (GST_BUFFER_DURATION (outbuf)),
GST_BUFFER_OFFSET (outbuf), GST_BUFFER_OFFSET_END (outbuf));
out:
return ret;
}
static gboolean
gst_srt_client_src_start (GstBaseSrc * src)
{
GstSRTClientSrc *self = GST_SRT_CLIENT_SRC (src);
GstSRTClientSrcPrivate *priv = GST_SRT_CLIENT_SRC_GET_PRIVATE (self);
GstSRTBaseSrc *base = GST_SRT_BASE_SRC (src);
GstUri *uri = gst_uri_ref (base->uri);
GSocketAddress *socket_address = NULL;
priv->sock = gst_srt_client_connect (GST_ELEMENT (src), FALSE,
gst_uri_get_host (uri), gst_uri_get_port (uri), priv->rendez_vous,
priv->bind_address, priv->bind_port, base->latency,
&socket_address, &priv->poll_id);
g_clear_object (&socket_address);
g_clear_pointer (&uri, gst_uri_unref);
return (priv->sock != SRT_INVALID_SOCK);
}
static gboolean
gst_srt_client_src_stop (GstBaseSrc * src)
{
GstSRTClientSrc *self = GST_SRT_CLIENT_SRC (src);
GstSRTClientSrcPrivate *priv = GST_SRT_CLIENT_SRC_GET_PRIVATE (self);
if (priv->poll_id != SRT_ERROR) {
if (priv->sock != SRT_INVALID_SOCK)
srt_epoll_remove_usock (priv->poll_id, priv->sock);
srt_epoll_release (priv->poll_id);
}
priv->poll_id = SRT_ERROR;
GST_DEBUG_OBJECT (self, "closing SRT connection");
if (priv->sock != SRT_INVALID_SOCK)
srt_close (priv->sock);
priv->sock = SRT_INVALID_SOCK;
return TRUE;
}
static void
gst_srt_client_src_class_init (GstSRTClientSrcClass * klass)
{
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
GstElementClass *gstelement_class = GST_ELEMENT_CLASS (klass);
GstBaseSrcClass *gstbasesrc_class = GST_BASE_SRC_CLASS (klass);
GstPushSrcClass *gstpushsrc_class = GST_PUSH_SRC_CLASS (klass);
gobject_class->set_property = gst_srt_client_src_set_property;
gobject_class->get_property = gst_srt_client_src_get_property;
gobject_class->finalize = gst_srt_client_src_finalize;
/**
* GstSRTClientSrc:poll-timeout:
*
* The timeout(ms) value when polling SRT socket.
*/
properties[PROP_POLL_TIMEOUT] =
g_param_spec_int ("poll-timeout", "Poll timeout",
"Return poll wait after timeout miliseconds (-1 = infinite)", -1,
G_MAXINT32, SRT_DEFAULT_POLL_TIMEOUT,
G_PARAM_READWRITE | GST_PARAM_MUTABLE_READY | G_PARAM_STATIC_STRINGS);
properties[PROP_BIND_ADDRESS] =
g_param_spec_string ("bind-address", "Bind Address",
"Address to bind socket to (required for rendez-vous mode) ", NULL,
G_PARAM_READWRITE | GST_PARAM_MUTABLE_READY | G_PARAM_STATIC_STRINGS);
properties[PROP_BIND_PORT] =
g_param_spec_int ("bind-port", "Bind Port",
"Port to bind socket to (Ignored in rendez-vous mode)", 0,
G_MAXUINT16, 0,
G_PARAM_READWRITE | GST_PARAM_MUTABLE_READY | G_PARAM_STATIC_STRINGS);
properties[PROP_RENDEZ_VOUS] =
g_param_spec_boolean ("rendez-vous", "Rendez Vous",
"Work in Rendez-Vous mode instead of client/caller mode", FALSE,
G_PARAM_READWRITE | GST_PARAM_MUTABLE_READY | G_PARAM_STATIC_STRINGS);
g_object_class_install_properties (gobject_class, PROP_LAST, properties);
gst_element_class_add_static_pad_template (gstelement_class, &src_template);
gst_element_class_set_metadata (gstelement_class,
"SRT client source", "Source/Network",
"Receive data over the network via SRT",
"Justin Kim <justin.kim@collabora.com>");
gstbasesrc_class->start = GST_DEBUG_FUNCPTR (gst_srt_client_src_start);
gstbasesrc_class->stop = GST_DEBUG_FUNCPTR (gst_srt_client_src_stop);
gstpushsrc_class->fill = GST_DEBUG_FUNCPTR (gst_srt_client_src_fill);
}
static void
gst_srt_client_src_init (GstSRTClientSrc * self)
{
GstSRTClientSrcPrivate *priv = GST_SRT_CLIENT_SRC_GET_PRIVATE (self);
priv->sock = SRT_INVALID_SOCK;
priv->poll_id = SRT_ERROR;
priv->poll_timeout = SRT_DEFAULT_POLL_TIMEOUT;
priv->rendez_vous = FALSE;
priv->bind_address = NULL;
priv->bind_port = 0;
}

59
ext/srt/gstsrtclientsrc.h Normal file
View file

@ -0,0 +1,59 @@
/* GStreamer
* Copyright (C) 2017, Collabora Ltd.
* Author:Justin Kim <justin.kim@collabora.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_SRT_CLIENT_SRC_H__
#define __GST_SRT_CLIENT_SRC_H__
#include "gstsrtbasesrc.h"
G_BEGIN_DECLS
#define GST_TYPE_SRT_CLIENT_SRC (gst_srt_client_src_get_type ())
#define GST_IS_SRT_CLIENT_SRC(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_SRT_CLIENT_SRC))
#define GST_IS_SRT_CLIENT_SRC_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GST_TYPE_SRT_CLIENT_SRC))
#define GST_SRT_CLIENT_SRC_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_SRT_CLIENT_SRC, GstSRTClientSrcClass))
#define GST_SRT_CLIENT_SRC(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_SRT_CLIENT_SRC, GstSRTClientSrc))
#define GST_SRT_CLIENT_SRC_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GST_TYPE_SRT_CLIENT_SRC, GstSRTClientSrcClass))
#define GST_SRT_CLIENT_SRC_CAST(obj) ((GstSRTClientSrc*)(obj))
#define GST_SRT_CLIENT_SRC_CLASS_CAST(klass) ((GstSRTClientSrcClass*)(klass))
typedef struct _GstSRTClientSrc GstSRTClientSrc;
typedef struct _GstSRTClientSrcClass GstSRTClientSrcClass;
typedef struct _GstSRTClientSrcPrivate GstSRTClientSrcPrivate;
struct _GstSRTClientSrc {
GstSRTBaseSrc parent;
/*< private >*/
gpointer _gst_reserved[GST_PADDING];
};
struct _GstSRTClientSrcClass {
GstSRTBaseSrcClass parent_class;
gpointer _gst_reserved[GST_PADDING_LARGE];
};
GST_EXPORT
GType gst_srt_client_src_get_type (void);
G_END_DECLS
#endif /* __GST_SRT_CLIENT_SRC_H__ */

555
ext/srt/gstsrtserversink.c Normal file
View file

@ -0,0 +1,555 @@
/* GStreamer SRT plugin based on libsrt
* Copyright (C) 2017, Collabora Ltd.
* Author:Justin Kim <justin.kim@collabora.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.
*/
/**
* SECTION:element-srtserversink
* @title: srtserversink
*
* srtserversink is a network sink that sends <ulink url="http://www.srtalliance.org/">SRT</ulink>
* packets to the network. Although SRT is an UDP-based protocol, srtserversink works like
* a server socket of connection-oriented protocol.
*
* <refsect2>
* <title>Examples</title>
* |[
* gst-launch-1.0 -v audiotestsrc ! srtserversink
* ]| This pipeline shows how to serve SRT packets through the default port.
* </refsect2>
*
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "gstsrtserversink.h"
#include "gstsrt.h"
#include <srt/srt.h>
#include <gio/gio.h>
#define SRT_DEFAULT_POLL_TIMEOUT -1
static GstStaticPadTemplate sink_template = GST_STATIC_PAD_TEMPLATE ("sink",
GST_PAD_SINK,
GST_PAD_ALWAYS,
GST_STATIC_CAPS_ANY);
#define GST_CAT_DEFAULT gst_debug_srt_server_sink
GST_DEBUG_CATEGORY (GST_CAT_DEFAULT);
struct _GstSRTServerSinkPrivate
{
gboolean cancelled;
SRTSOCKET sock;
gint poll_id;
gint poll_timeout;
GMainLoop *loop;
GMainContext *context;
GSource *server_source;
GThread *thread;
GList *clients;
};
#define GST_SRT_SERVER_SINK_GET_PRIVATE(obj) \
(G_TYPE_INSTANCE_GET_PRIVATE ((obj), GST_TYPE_SRT_SERVER_SINK, GstSRTServerSinkPrivate))
enum
{
PROP_POLL_TIMEOUT = 1,
PROP_STATS,
/*< private > */
PROP_LAST
};
static GParamSpec *properties[PROP_LAST];
enum
{
SIG_CLIENT_ADDED,
SIG_CLIENT_REMOVED,
LAST_SIGNAL
};
static guint signals[LAST_SIGNAL] = { 0 };
#define gst_srt_server_sink_parent_class parent_class
G_DEFINE_TYPE_WITH_CODE (GstSRTServerSink, gst_srt_server_sink,
GST_TYPE_SRT_BASE_SINK, G_ADD_PRIVATE (GstSRTServerSink)
GST_DEBUG_CATEGORY_INIT (GST_CAT_DEFAULT, "srtserversink", 0,
"SRT Server Sink"));
typedef struct
{
int sock;
GSocketAddress *sockaddr;
} SRTClient;
static SRTClient *
srt_client_new (void)
{
SRTClient *client = g_new0 (SRTClient, 1);
client->sock = SRT_INVALID_SOCK;
return client;
}
static void
srt_client_free (SRTClient * client)
{
g_return_if_fail (client != NULL);
g_clear_object (&client->sockaddr);
if (client->sock != SRT_INVALID_SOCK) {
srt_close (client->sock);
}
g_free (client);
}
static void
srt_emit_client_removed (SRTClient * client, gpointer user_data)
{
GstSRTServerSink *self = GST_SRT_SERVER_SINK (user_data);
g_return_if_fail (client != NULL && GST_IS_SRT_SERVER_SINK (self));
g_signal_emit (self, signals[SIG_CLIENT_REMOVED], 0, client->sock,
client->sockaddr);
}
static void
gst_srt_server_sink_get_property (GObject * object,
guint prop_id, GValue * value, GParamSpec * pspec)
{
GstSRTServerSink *self = GST_SRT_SERVER_SINK (object);
GstSRTServerSinkPrivate *priv = GST_SRT_SERVER_SINK_GET_PRIVATE (self);
switch (prop_id) {
case PROP_POLL_TIMEOUT:
g_value_set_int (value, priv->poll_timeout);
break;
case PROP_STATS:
{
GList *item;
GST_OBJECT_LOCK (self);
for (item = priv->clients; item; item = item->next) {
SRTClient *client = item->data;
GValue tmp = G_VALUE_INIT;
g_value_init (&tmp, GST_TYPE_STRUCTURE);
g_value_take_boxed (&tmp, gst_srt_base_sink_get_stats (client->sockaddr,
client->sock));
gst_value_array_append_and_take_value (value, &tmp);
}
GST_OBJECT_UNLOCK (self);
break;
}
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
gst_srt_server_sink_set_property (GObject * object,
guint prop_id, const GValue * value, GParamSpec * pspec)
{
GstSRTServerSink *self = GST_SRT_SERVER_SINK (object);
GstSRTServerSinkPrivate *priv = GST_SRT_SERVER_SINK_GET_PRIVATE (self);
switch (prop_id) {
case PROP_POLL_TIMEOUT:
priv->poll_timeout = g_value_get_int (value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static gboolean
idle_listen_callback (gpointer data)
{
GstSRTServerSink *self = GST_SRT_SERVER_SINK (data);
GstSRTServerSinkPrivate *priv = GST_SRT_SERVER_SINK_GET_PRIVATE (self);
gboolean ret = TRUE;
SRTClient *client;
SRTSOCKET ready[2];
struct sockaddr sa;
int sa_len;
if (srt_epoll_wait (priv->poll_id, ready, &(int) {
2}, 0, 0, priv->poll_timeout, 0, 0, 0, 0) == -1) {
int srt_errno = srt_getlasterror (NULL);
if (srt_errno != SRT_ETIMEOUT) {
GST_ELEMENT_ERROR (self, RESOURCE, FAILED,
("SRT error: %s", srt_getlasterror_str ()), (NULL));
ret = FALSE;
goto out;
}
/* Mimicking cancellable */
if (srt_errno == SRT_ETIMEOUT && priv->cancelled) {
GST_DEBUG_OBJECT (self, "Cancelled waiting for client");
ret = FALSE;
goto out;
}
}
client = srt_client_new ();
client->sock = srt_accept (priv->sock, &sa, &sa_len);
if (client->sock == SRT_INVALID_SOCK) {
GST_WARNING_OBJECT (self, "detected invalid SRT client socket (reason: %s)",
srt_getlasterror_str ());
srt_clearlasterror ();
srt_client_free (client);
ret = FALSE;
goto out;
}
client->sockaddr = g_socket_address_new_from_native (&sa, sa_len);
GST_OBJECT_LOCK (self);
priv->clients = g_list_append (priv->clients, client);
GST_OBJECT_UNLOCK (self);
g_signal_emit (self, signals[SIG_CLIENT_ADDED], 0, client->sock,
client->sockaddr);
GST_DEBUG_OBJECT (self, "client added");
out:
return ret;
}
static gpointer
thread_func (gpointer data)
{
GstSRTServerSink *self = GST_SRT_SERVER_SINK (data);
GstSRTServerSinkPrivate *priv = GST_SRT_SERVER_SINK_GET_PRIVATE (self);
g_main_loop_run (priv->loop);
return NULL;
}
static gboolean
gst_srt_server_sink_start (GstBaseSink * sink)
{
GstSRTServerSink *self = GST_SRT_SERVER_SINK (sink);
GstSRTServerSinkPrivate *priv = GST_SRT_SERVER_SINK_GET_PRIVATE (self);
GstSRTBaseSink *base = GST_SRT_BASE_SINK (sink);
GstUri *uri = gst_uri_ref (GST_SRT_BASE_SINK (self)->uri);
GSocketAddress *socket_address = NULL;
GError *error = NULL;
gboolean ret = TRUE;
struct sockaddr sa;
size_t sa_len;
const gchar *host;
int lat = base->latency;
if (gst_uri_get_port (uri) == GST_URI_NO_PORT) {
GST_ELEMENT_ERROR (sink, RESOURCE, OPEN_WRITE, NULL, (("Invalid port")));
return FALSE;
}
host = gst_uri_get_host (uri);
if (host == NULL) {
GInetAddress *any = g_inet_address_new_any (G_SOCKET_FAMILY_IPV4);
socket_address = g_inet_socket_address_new (any, gst_uri_get_port (uri));
g_object_unref (any);
} else {
socket_address =
g_inet_socket_address_new_from_string (host, gst_uri_get_port (uri));
}
if (socket_address == NULL) {
GST_WARNING_OBJECT (self,
"failed to extract host or port from the given URI");
goto failed;
}
sa_len = g_socket_address_get_native_size (socket_address);
if (!g_socket_address_to_native (socket_address, &sa, sa_len, &error)) {
GST_WARNING_OBJECT (self, "cannot resolve address (reason: %s)",
error->message);
goto failed;
}
priv->sock = srt_socket (sa.sa_family, SOCK_DGRAM, 0);
if (priv->sock == SRT_INVALID_SOCK) {
GST_WARNING_OBJECT (self, "failed to create SRT socket (reason: %s)",
srt_getlasterror_str ());
goto failed;
}
/* Make SRT non-blocking */
srt_setsockopt (priv->sock, 0, SRTO_SNDSYN, &(int) {
0}, sizeof (int));
/* Make sure TSBPD mode is enable (SRT mode) */
srt_setsockopt (priv->sock, 0, SRTO_TSBPDMODE, &(int) {
1}, sizeof (int));
/* This is a sink, we're always a sender */
srt_setsockopt (priv->sock, 0, SRTO_SENDER, &(int) {
1}, sizeof (int));
srt_setsockopt (priv->sock, 0, SRTO_TSBPDDELAY, &lat, sizeof (int));
priv->poll_id = srt_epoll_create ();
if (priv->poll_id == -1) {
GST_WARNING_OBJECT (self,
"failed to create poll id for SRT socket (reason: %s)",
srt_getlasterror_str ());
goto failed;
}
srt_epoll_add_usock (priv->poll_id, priv->sock, &(int) {
SRT_EPOLL_IN});
if (srt_bind (priv->sock, &sa, sa_len) == SRT_ERROR) {
GST_WARNING_OBJECT (self, "failed to bind SRT server socket (reason: %s)",
srt_getlasterror_str ());
goto failed;
}
if (srt_listen (priv->sock, 1) == SRT_ERROR) {
GST_WARNING_OBJECT (self, "failed to listen SRT socket (reason: %s)",
srt_getlasterror_str ());
goto failed;
}
priv->context = g_main_context_new ();
priv->server_source = g_idle_source_new ();
g_source_set_callback (priv->server_source,
(GSourceFunc) idle_listen_callback, gst_object_ref (self),
(GDestroyNotify) gst_object_unref);
g_source_attach (priv->server_source, priv->context);
priv->loop = g_main_loop_new (priv->context, TRUE);
priv->thread = g_thread_try_new ("srtserversink", thread_func, self, &error);
if (error != NULL) {
GST_WARNING_OBJECT (self, "failed to create thread (reason: %s)",
error->message);
ret = FALSE;
}
g_clear_pointer (&uri, gst_uri_unref);
g_clear_object (&socket_address);
return ret;
failed:
if (priv->poll_id != SRT_ERROR) {
srt_epoll_release (priv->poll_id);
priv->poll_id = SRT_ERROR;
}
if (priv->sock != SRT_INVALID_SOCK) {
srt_close (priv->sock);
priv->sock = SRT_INVALID_SOCK;
}
g_clear_error (&error);
g_clear_pointer (&uri, gst_uri_unref);
g_clear_object (&socket_address);
return FALSE;
}
static gboolean
gst_srt_server_sink_send_buffer (GstSRTBaseSink * sink,
const GstMapInfo * mapinfo)
{
GstSRTServerSink *self = GST_SRT_SERVER_SINK (sink);
GstSRTServerSinkPrivate *priv = GST_SRT_SERVER_SINK_GET_PRIVATE (self);
GList *clients = priv->clients;
GST_OBJECT_LOCK (sink);
while (clients != NULL) {
SRTClient *client = clients->data;
clients = clients->next;
if (srt_sendmsg2 (client->sock, (char *) mapinfo->data, mapinfo->size,
0) == SRT_ERROR) {
GST_WARNING_OBJECT (self, "%s", srt_getlasterror_str ());
priv->clients = g_list_remove (priv->clients, client);
GST_OBJECT_UNLOCK (sink);
g_signal_emit (self, signals[SIG_CLIENT_REMOVED], 0, client->sock,
client->sockaddr);
srt_client_free (client);
GST_OBJECT_LOCK (sink);
}
}
GST_OBJECT_UNLOCK (sink);
return TRUE;
}
static gboolean
gst_srt_server_sink_stop (GstBaseSink * sink)
{
GstSRTServerSink *self = GST_SRT_SERVER_SINK (sink);
GstSRTServerSinkPrivate *priv = GST_SRT_SERVER_SINK_GET_PRIVATE (self);
gboolean ret = TRUE;
GList *clients;
GST_DEBUG_OBJECT (self, "closing client sockets");
GST_OBJECT_LOCK (sink);
clients = priv->clients;
priv->clients = NULL;
GST_OBJECT_UNLOCK (sink);
g_list_foreach (clients, (GFunc) srt_emit_client_removed, self);
g_list_free_full (clients, (GDestroyNotify) srt_client_free);
GST_DEBUG_OBJECT (self, "closing SRT connection");
srt_epoll_remove_usock (priv->poll_id, priv->sock);
srt_epoll_release (priv->poll_id);
srt_close (priv->sock);
if (priv->loop) {
g_main_loop_quit (priv->loop);
g_thread_join (priv->thread);
g_clear_pointer (&priv->loop, g_main_loop_unref);
g_clear_pointer (&priv->thread, g_thread_unref);
}
if (priv->server_source) {
g_source_destroy (priv->server_source);
g_clear_pointer (&priv->server_source, g_source_unref);
}
g_clear_pointer (&priv->context, g_main_context_unref);
return ret;
}
static gboolean
gst_srt_server_sink_unlock (GstBaseSink * sink)
{
GstSRTServerSink *self = GST_SRT_SERVER_SINK (sink);
GstSRTServerSinkPrivate *priv = GST_SRT_SERVER_SINK_GET_PRIVATE (self);
priv->cancelled = TRUE;
return TRUE;
}
static gboolean
gst_srt_server_sink_unlock_stop (GstBaseSink * sink)
{
GstSRTServerSink *self = GST_SRT_SERVER_SINK (sink);
GstSRTServerSinkPrivate *priv = GST_SRT_SERVER_SINK_GET_PRIVATE (self);
priv->cancelled = FALSE;
return TRUE;
}
static void
gst_srt_server_sink_class_init (GstSRTServerSinkClass * klass)
{
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
GstElementClass *gstelement_class = GST_ELEMENT_CLASS (klass);
GstBaseSinkClass *gstbasesink_class = GST_BASE_SINK_CLASS (klass);
GstSRTBaseSinkClass *gstsrtbasesink_class = GST_SRT_BASE_SINK_CLASS (klass);
gobject_class->set_property = gst_srt_server_sink_set_property;
gobject_class->get_property = gst_srt_server_sink_get_property;
properties[PROP_POLL_TIMEOUT] =
g_param_spec_int ("poll-timeout", "Poll Timeout",
"Return poll wait after timeout miliseconds (-1 = infinite)", -1,
G_MAXINT32, SRT_DEFAULT_POLL_TIMEOUT,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
properties[PROP_STATS] = gst_param_spec_array ("stats", "Statistics",
"Array of GstStructures containing SRT statistics",
g_param_spec_boxed ("stats", "Statistics",
"Statistics for one client", GST_TYPE_STRUCTURE,
G_PARAM_READABLE | G_PARAM_STATIC_STRINGS),
G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
g_object_class_install_properties (gobject_class, PROP_LAST, properties);
/**
* GstSRTServerSink::client-added:
* @gstsrtserversink: the srtserversink element that emitted this signal
* @sock: the client socket descriptor that was added to srtserversink
* @addr: the pointer of "struct sockaddr" that describes the @sock
* @addr_len: the length of @addr
*
* The given socket descriptor was added to srtserversink.
*/
signals[SIG_CLIENT_ADDED] =
g_signal_new ("client-added", G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstSRTServerSinkClass, client_added),
NULL, NULL, g_cclosure_marshal_generic, G_TYPE_NONE,
2, G_TYPE_INT, G_TYPE_SOCKET_ADDRESS);
/**
* GstSRTServerSink::client-removed:
* @gstsrtserversink: the srtserversink element that emitted this signal
* @sock: the client socket descriptor that was added to srtserversink
* @addr: the pointer of "struct sockaddr" that describes the @sock
* @addr_len: the length of @addr
*
* The given socket descriptor was removed from srtserversink.
*/
signals[SIG_CLIENT_REMOVED] =
g_signal_new ("client-removed", G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstSRTServerSinkClass,
client_removed), NULL, NULL, g_cclosure_marshal_generic, G_TYPE_NONE,
2, G_TYPE_INT, G_TYPE_SOCKET_ADDRESS);
gst_element_class_add_static_pad_template (gstelement_class, &sink_template);
gst_element_class_set_metadata (gstelement_class,
"SRT server sink", "Sink/Network",
"Send data over the network via SRT",
"Justin Kim <justin.kim@collabora.com>");
gstbasesink_class->start = GST_DEBUG_FUNCPTR (gst_srt_server_sink_start);
gstbasesink_class->stop = GST_DEBUG_FUNCPTR (gst_srt_server_sink_stop);
gstbasesink_class->unlock = GST_DEBUG_FUNCPTR (gst_srt_server_sink_unlock);
gstbasesink_class->unlock_stop =
GST_DEBUG_FUNCPTR (gst_srt_server_sink_unlock_stop);
gstsrtbasesink_class->send_buffer =
GST_DEBUG_FUNCPTR (gst_srt_server_sink_send_buffer);
}
static void
gst_srt_server_sink_init (GstSRTServerSink * self)
{
GstSRTServerSinkPrivate *priv = GST_SRT_SERVER_SINK_GET_PRIVATE (self);
priv->poll_timeout = SRT_DEFAULT_POLL_TIMEOUT;
}

View file

@ -0,0 +1,63 @@
/* GStreamer
* Copyright (C) 2017, Collabora Ltd.
* Author:Justin Kim <justin.kim@collabora.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_SRT_SERVER_SINK_H__
#define __GST_SRT_SERVER_SINK_H__
#include "gstsrtbasesink.h"
#include <sys/socket.h>
G_BEGIN_DECLS
#define GST_TYPE_SRT_SERVER_SINK (gst_srt_server_sink_get_type ())
#define GST_IS_SRT_SERVER_SINK(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_SRT_SERVER_SINK))
#define GST_IS_SRT_SERVER_SINK_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GST_TYPE_SRT_SERVER_SINK))
#define GST_SRT_SERVER_SINK_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_SRT_SERVER_SINK, GstSRTServerSinkClass))
#define GST_SRT_SERVER_SINK(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_SRT_SERVER_SINK, GstSRTServerSink))
#define GST_SRT_SERVER_SINK_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GST_TYPE_SRT_SERVER_SINK, GstSRTServerSinkClass))
#define GST_SRT_SERVER_SINK_CAST(obj) ((GstSRTServerSink*)(obj))
#define GST_SRT_SERVER_SINK_CLASS_CAST(klass) ((GstSRTServerSinkClass*)(klass))
typedef struct _GstSRTServerSink GstSRTServerSink;
typedef struct _GstSRTServerSinkClass GstSRTServerSinkClass;
typedef struct _GstSRTServerSinkPrivate GstSRTServerSinkPrivate;
struct _GstSRTServerSink {
GstSRTBaseSink parent;
/*< private >*/
gpointer _gst_reserved[GST_PADDING];
};
struct _GstSRTServerSinkClass {
GstSRTBaseSinkClass parent_class;
void (*client_added) (GstSRTServerSink *self, int sock, struct sockaddr *addr, int addr_len);
void (*client_removed) (GstSRTServerSink *self, int sock, struct sockaddr *addr, int addr_len);
gpointer _gst_reserved[GST_PADDING_LARGE];
};
GST_EXPORT
GType gst_srt_server_sink_get_type (void);
G_END_DECLS
#endif /* __GST_SRT_SERVER_SINK_H__ */

504
ext/srt/gstsrtserversrc.c Normal file
View file

@ -0,0 +1,504 @@
/* GStreamer SRT plugin based on libsrt
* Copyright (C) 2017, Collabora Ltd.
* Author:Justin Kim <justin.kim@collabora.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.
*/
/**
* SECTION:element-srtserversrc
* @title: srtserversrc
*
* srtserversrc is a network source that reads <ulink url="http://www.srtalliance.org/">SRT</ulink>
* packets from the network. Although SRT is a protocol based on UDP, srtserversrc works like
* a server socket of connection-oriented protocol, but it accepts to only one client connection.
*
* <refsect2>
* <title>Examples</title>
* |[
* gst-launch-1.0 -v srtserversrc uri="srt://:7001" ! fakesink
* ]| This pipeline shows how to bind SRT server by setting #GstSRTServerSrc:uri property.
* </refsect2>
*
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "gstsrtserversrc.h"
#include "gstsrt.h"
#include <srt/srt.h>
#include <gio/gio.h>
#define SRT_DEFAULT_POLL_TIMEOUT 100
static GstStaticPadTemplate src_template = GST_STATIC_PAD_TEMPLATE ("src",
GST_PAD_SRC,
GST_PAD_ALWAYS,
GST_STATIC_CAPS_ANY);
#define GST_CAT_DEFAULT gst_debug_srt_server_src
GST_DEBUG_CATEGORY (GST_CAT_DEFAULT);
struct _GstSRTServerSrcPrivate
{
SRTSOCKET sock;
SRTSOCKET client_sock;
GSocketAddress *client_sockaddr;
gint poll_id;
gint poll_timeout;
gboolean has_client;
gboolean cancelled;
};
#define GST_SRT_SERVER_SRC_GET_PRIVATE(obj) \
(G_TYPE_INSTANCE_GET_PRIVATE ((obj), GST_TYPE_SRT_SERVER_SRC, GstSRTServerSrcPrivate))
enum
{
PROP_POLL_TIMEOUT = 1,
/*< private > */
PROP_LAST
};
static GParamSpec *properties[PROP_LAST];
enum
{
SIG_CLIENT_ADDED,
SIG_CLIENT_CLOSED,
LAST_SIGNAL
};
static guint signals[LAST_SIGNAL] = { 0 };
#define gst_srt_server_src_parent_class parent_class
G_DEFINE_TYPE_WITH_CODE (GstSRTServerSrc, gst_srt_server_src,
GST_TYPE_SRT_BASE_SRC, G_ADD_PRIVATE (GstSRTServerSrc)
GST_DEBUG_CATEGORY_INIT (GST_CAT_DEFAULT, "srtserversrc", 0,
"SRT Server Source"));
static void
gst_srt_server_src_get_property (GObject * object,
guint prop_id, GValue * value, GParamSpec * pspec)
{
GstSRTServerSrc *self = GST_SRT_SERVER_SRC (object);
GstSRTServerSrcPrivate *priv = GST_SRT_SERVER_SRC_GET_PRIVATE (self);
switch (prop_id) {
case PROP_POLL_TIMEOUT:
g_value_set_int (value, priv->poll_timeout);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
gst_srt_server_src_set_property (GObject * object,
guint prop_id, const GValue * value, GParamSpec * pspec)
{
GstSRTServerSrc *self = GST_SRT_SERVER_SRC (object);
GstSRTServerSrcPrivate *priv = GST_SRT_SERVER_SRC_GET_PRIVATE (self);
switch (prop_id) {
case PROP_POLL_TIMEOUT:
priv->poll_timeout = g_value_get_int (value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
gst_srt_server_src_finalize (GObject * object)
{
GstSRTServerSrc *self = GST_SRT_SERVER_SRC (object);
GstSRTServerSrcPrivate *priv = GST_SRT_SERVER_SRC_GET_PRIVATE (self);
if (priv->poll_id != SRT_ERROR) {
srt_epoll_release (priv->poll_id);
priv->poll_id = SRT_ERROR;
}
if (priv->sock != SRT_ERROR) {
srt_close (priv->sock);
priv->sock = SRT_ERROR;
}
G_OBJECT_CLASS (parent_class)->finalize (object);
}
static GstFlowReturn
gst_srt_server_src_fill (GstPushSrc * src, GstBuffer * outbuf)
{
GstSRTServerSrc *self = GST_SRT_SERVER_SRC (src);
GstSRTServerSrcPrivate *priv = GST_SRT_SERVER_SRC_GET_PRIVATE (self);
GstFlowReturn ret = GST_FLOW_OK;
GstMapInfo info;
SRTSOCKET ready[2];
gint recv_len;
struct sockaddr client_sa;
size_t client_sa_len;
while (!priv->has_client) {
GST_DEBUG_OBJECT (self, "poll wait (timeout: %d)", priv->poll_timeout);
/* Make SRT server socket non-blocking */
srt_setsockopt (priv->sock, 0, SRTO_SNDSYN, &(int) {
0}, sizeof (int));
if (srt_epoll_wait (priv->poll_id, ready, &(int) {
2}, 0, 0, priv->poll_timeout, 0, 0, 0, 0) == -1) {
int srt_errno = srt_getlasterror (NULL);
/* Assuming that timeout error is normal */
if (srt_errno != SRT_ETIMEOUT) {
GST_ELEMENT_ERROR (src, RESOURCE, FAILED,
("SRT error: %s", srt_getlasterror_str ()), (NULL));
return GST_FLOW_ERROR;
}
/* Mimicking cancellable */
if (srt_errno == SRT_ETIMEOUT && priv->cancelled) {
GST_DEBUG_OBJECT (self, "Cancelled waiting for client");
return GST_FLOW_FLUSHING;
}
continue;
}
priv->client_sock =
srt_accept (priv->sock, &client_sa, (int *) &client_sa_len);
GST_DEBUG_OBJECT (self, "checking client sock");
if (priv->client_sock == SRT_INVALID_SOCK) {
GST_WARNING_OBJECT (self,
"detected invalid SRT client socket (reason: %s)",
srt_getlasterror_str ());
srt_clearlasterror ();
} else {
priv->has_client = TRUE;
g_clear_object (&priv->client_sockaddr);
priv->client_sockaddr = g_socket_address_new_from_native (&client_sa,
client_sa_len);
g_signal_emit (self, signals[SIG_CLIENT_ADDED], 0,
priv->client_sock, priv->client_sockaddr);
}
}
GST_DEBUG_OBJECT (self, "filling buffer");
if (!gst_buffer_map (outbuf, &info, GST_MAP_WRITE)) {
GST_ELEMENT_ERROR (src, RESOURCE, WRITE,
("Could not map the output stream"), (NULL));
ret = GST_FLOW_ERROR;
goto out;
}
recv_len = srt_recvmsg (priv->client_sock, (char *) info.data,
gst_buffer_get_size (outbuf));
gst_buffer_unmap (outbuf, &info);
if (recv_len == SRT_ERROR) {
GST_WARNING_OBJECT (self, "%s", srt_getlasterror_str ());
g_signal_emit (self, signals[SIG_CLIENT_CLOSED], 0,
priv->client_sock, priv->client_sockaddr);
srt_close (priv->client_sock);
priv->client_sock = SRT_INVALID_SOCK;
g_clear_object (&priv->client_sockaddr);
priv->has_client = FALSE;
gst_buffer_resize (outbuf, 0, 0);
ret = GST_FLOW_OK;
goto out;
} else if (recv_len == 0) {
ret = GST_FLOW_EOS;
goto out;
}
GST_BUFFER_PTS (outbuf) =
gst_clock_get_time (GST_ELEMENT_CLOCK (src)) -
GST_ELEMENT_CAST (src)->base_time;
gst_buffer_resize (outbuf, 0, recv_len);
GST_LOG_OBJECT (src,
"filled buffer from _get of size %" G_GSIZE_FORMAT ", ts %"
GST_TIME_FORMAT ", dur %" GST_TIME_FORMAT
", offset %" G_GINT64_FORMAT ", offset_end %" G_GINT64_FORMAT,
gst_buffer_get_size (outbuf),
GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (outbuf)),
GST_TIME_ARGS (GST_BUFFER_DURATION (outbuf)),
GST_BUFFER_OFFSET (outbuf), GST_BUFFER_OFFSET_END (outbuf));
out:
return ret;
}
static gboolean
gst_srt_server_src_start (GstBaseSrc * src)
{
GstSRTServerSrc *self = GST_SRT_SERVER_SRC (src);
GstSRTServerSrcPrivate *priv = GST_SRT_SERVER_SRC_GET_PRIVATE (self);
GstSRTBaseSrc *base = GST_SRT_BASE_SRC (src);
GstUri *uri = gst_uri_ref (base->uri);
GError *error = NULL;
struct sockaddr sa;
size_t sa_len;
GSocketAddress *socket_address;
const gchar *host;
int lat = base->latency;
if (gst_uri_get_port (uri) == GST_URI_NO_PORT) {
GST_ELEMENT_ERROR (src, RESOURCE, OPEN_WRITE, NULL, (("Invalid port")));
return FALSE;
}
host = gst_uri_get_host (uri);
if (host == NULL) {
GInetAddress *any = g_inet_address_new_any (G_SOCKET_FAMILY_IPV4);
socket_address = g_inet_socket_address_new (any, gst_uri_get_port (uri));
g_object_unref (any);
} else {
socket_address =
g_inet_socket_address_new_from_string (host, gst_uri_get_port (uri));
}
if (socket_address == NULL) {
GST_ELEMENT_ERROR (self, RESOURCE, OPEN_READ, ("Invalid URI"),
("failed to extract host or port from the given URI"));
goto failed;
}
sa_len = g_socket_address_get_native_size (socket_address);
if (!g_socket_address_to_native (socket_address, &sa, sa_len, &error)) {
GST_ELEMENT_ERROR (self, RESOURCE, OPEN_READ, ("Invalid URI"),
("cannot resolve address (reason: %s)", error->message));
goto failed;
}
priv->sock = srt_socket (sa.sa_family, SOCK_DGRAM, 0);
if (priv->sock == SRT_ERROR) {
GST_ELEMENT_ERROR (self, LIBRARY, INIT, (NULL),
("failed to create poll id for SRT socket (reason: %s)",
srt_getlasterror_str ()));
goto failed;
}
/* Make sure TSBPD mode is enable (SRT mode) */
srt_setsockopt (priv->sock, 0, SRTO_TSBPDMODE, &(int) {
1}, sizeof (int));
/* This is a sink, we're always a receiver */
srt_setsockopt (priv->sock, 0, SRTO_SENDER, &(int) {
0}, sizeof (int));
srt_setsockopt (priv->sock, 0, SRTO_TSBPDDELAY, &lat, sizeof (int));
priv->poll_id = srt_epoll_create ();
if (priv->poll_id == -1) {
GST_ELEMENT_ERROR (self, LIBRARY, INIT, (NULL),
("failed to create poll id for SRT socket (reason: %s)",
srt_getlasterror_str ()));
goto failed;
}
srt_epoll_add_usock (priv->poll_id, priv->sock, &(int) {
SRT_EPOLL_IN});
if (srt_bind (priv->sock, &sa, sa_len) == SRT_ERROR) {
GST_ELEMENT_ERROR (self, RESOURCE, OPEN_READ, (NULL),
("failed to bind SRT server socket (reason: %s)",
srt_getlasterror_str ()));
goto failed;
}
if (srt_listen (priv->sock, 1) == SRT_ERROR) {
GST_ELEMENT_ERROR (self, RESOURCE, OPEN_READ, (NULL),
("failed to listen SRT socket (reason: %s)", srt_getlasterror_str ()));
goto failed;
}
g_clear_pointer (&uri, gst_uri_unref);
g_clear_object (&socket_address);
return TRUE;
failed:
if (priv->poll_id != SRT_ERROR) {
srt_epoll_release (priv->poll_id);
priv->poll_id = SRT_ERROR;
}
if (priv->sock != SRT_ERROR) {
srt_close (priv->sock);
priv->sock = SRT_ERROR;
}
g_clear_error (&error);
g_clear_pointer (&uri, gst_uri_unref);
g_clear_object (&socket_address);
return FALSE;
}
static gboolean
gst_srt_server_src_stop (GstBaseSrc * src)
{
GstSRTServerSrc *self = GST_SRT_SERVER_SRC (src);
GstSRTServerSrcPrivate *priv = GST_SRT_SERVER_SRC_GET_PRIVATE (self);
if (priv->client_sock != SRT_INVALID_SOCK) {
g_signal_emit (self, signals[SIG_CLIENT_ADDED], 0,
priv->client_sock, priv->client_sockaddr);
srt_close (priv->client_sock);
g_clear_object (&priv->client_sockaddr);
priv->client_sock = SRT_INVALID_SOCK;
priv->has_client = FALSE;
}
if (priv->poll_id != SRT_ERROR) {
srt_epoll_remove_usock (priv->poll_id, priv->sock);
srt_epoll_release (priv->poll_id);
priv->poll_id = SRT_ERROR;
}
if (priv->sock != SRT_INVALID_SOCK) {
GST_DEBUG_OBJECT (self, "closing SRT connection");
srt_close (priv->sock);
priv->sock = SRT_INVALID_SOCK;
}
priv->cancelled = FALSE;
return TRUE;
}
static gboolean
gst_srt_server_src_unlock (GstBaseSrc * src)
{
GstSRTServerSrc *self = GST_SRT_SERVER_SRC (src);
GstSRTServerSrcPrivate *priv = GST_SRT_SERVER_SRC_GET_PRIVATE (self);
priv->cancelled = TRUE;
return TRUE;
}
static gboolean
gst_srt_server_src_unlock_stop (GstBaseSrc * src)
{
GstSRTServerSrc *self = GST_SRT_SERVER_SRC (src);
GstSRTServerSrcPrivate *priv = GST_SRT_SERVER_SRC_GET_PRIVATE (self);
priv->cancelled = FALSE;
return TRUE;
}
static void
gst_srt_server_src_class_init (GstSRTServerSrcClass * klass)
{
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
GstElementClass *gstelement_class = GST_ELEMENT_CLASS (klass);
GstBaseSrcClass *gstbasesrc_class = GST_BASE_SRC_CLASS (klass);
GstPushSrcClass *gstpushsrc_class = GST_PUSH_SRC_CLASS (klass);
gobject_class->set_property = gst_srt_server_src_set_property;
gobject_class->get_property = gst_srt_server_src_get_property;
gobject_class->finalize = gst_srt_server_src_finalize;
/**
* GstSRTServerSrc:poll-timeout:
*
* The timeout(ms) value when polling SRT socket. For #GstSRTServerSrc,
* this value shouldn't be set as -1 (infinite) because "srt_epoll_wait"
* isn't cancellable unless closing the socket.
*/
properties[PROP_POLL_TIMEOUT] =
g_param_spec_int ("poll-timeout", "Poll timeout",
"Return poll wait after timeout miliseconds", 0, G_MAXINT32,
SRT_DEFAULT_POLL_TIMEOUT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
g_object_class_install_properties (gobject_class, PROP_LAST, properties);
/**
* GstSRTServerSrc::client-added:
* @gstsrtserversrc: the srtserversrc element that emitted this signal
* @sock: the client socket descriptor that was added to srtserversrc
* @addr: the pointer of "struct sockaddr" that describes the @sock
* @addr_len: the length of @addr
*
* The given socket descriptor was added to srtserversrc.
*/
signals[SIG_CLIENT_ADDED] =
g_signal_new ("client-added", G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstSRTServerSrcClass, client_added),
NULL, NULL, g_cclosure_marshal_generic, G_TYPE_NONE,
2, G_TYPE_INT, G_TYPE_SOCKET_ADDRESS);
/**
* GstSRTServerSrc::client-closed:
* @gstsrtserversrc: the srtserversrc element that emitted this signal
* @sock: the client socket descriptor that was added to srtserversrc
* @addr: the pointer of "struct sockaddr" that describes the @sock
* @addr_len: the length of @addr
*
* The given socket descriptor was closed.
*/
signals[SIG_CLIENT_CLOSED] =
g_signal_new ("client-closed", G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstSRTServerSrcClass, client_closed),
NULL, NULL, g_cclosure_marshal_generic, G_TYPE_NONE,
2, G_TYPE_INT, G_TYPE_SOCKET_ADDRESS);
gst_element_class_add_static_pad_template (gstelement_class, &src_template);
gst_element_class_set_metadata (gstelement_class,
"SRT Server source", "Source/Network",
"Receive data over the network via SRT",
"Justin Kim <justin.kim@collabora.com>");
gstbasesrc_class->start = GST_DEBUG_FUNCPTR (gst_srt_server_src_start);
gstbasesrc_class->stop = GST_DEBUG_FUNCPTR (gst_srt_server_src_stop);
gstbasesrc_class->unlock = GST_DEBUG_FUNCPTR (gst_srt_server_src_unlock);
gstbasesrc_class->unlock_stop =
GST_DEBUG_FUNCPTR (gst_srt_server_src_unlock_stop);
gstpushsrc_class->fill = GST_DEBUG_FUNCPTR (gst_srt_server_src_fill);
}
static void
gst_srt_server_src_init (GstSRTServerSrc * self)
{
GstSRTServerSrcPrivate *priv = GST_SRT_SERVER_SRC_GET_PRIVATE (self);
priv->sock = SRT_INVALID_SOCK;
priv->client_sock = SRT_INVALID_SOCK;
priv->poll_id = SRT_ERROR;
priv->poll_timeout = SRT_DEFAULT_POLL_TIMEOUT;
}

63
ext/srt/gstsrtserversrc.h Normal file
View file

@ -0,0 +1,63 @@
/* GStreamer
* Copyright (C) 2017, Collabora Ltd.
* Author:Justin Kim <justin.kim@collabora.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_SRT_SERVER_SRC_H__
#define __GST_SRT_SERVER_SRC_H__
#include "gstsrtbasesrc.h"
#include <sys/socket.h>
G_BEGIN_DECLS
#define GST_TYPE_SRT_SERVER_SRC (gst_srt_server_src_get_type ())
#define GST_IS_SRT_SERVER_SRC(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_SRT_SERVER_SRC))
#define GST_IS_SRT_SERVER_SRC_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GST_TYPE_SRT_SERVER_SRC))
#define GST_SRT_SERVER_SRC_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_SRT_SERVER_SRC, GstSRTServerSrcClass))
#define GST_SRT_SERVER_SRC(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_SRT_SERVER_SRC, GstSRTServerSrc))
#define GST_SRT_SERVER_SRC_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GST_TYPE_SRT_SERVER_SRC, GstSRTServerSrcClass))
#define GST_SRT_SERVER_SRC_CAST(obj) ((GstSRTServerSrc*)(obj))
#define GST_SRT_SERVER_SRC_CLASS_CAST(klass) ((GstSRTServerSrcClass*)(klass))
typedef struct _GstSRTServerSrc GstSRTServerSrc;
typedef struct _GstSRTServerSrcClass GstSRTServerSrcClass;
typedef struct _GstSRTServerSrcPrivate GstSRTServerSrcPrivate;
struct _GstSRTServerSrc {
GstSRTBaseSrc parent;
/*< private >*/
gpointer _gst_reserved[GST_PADDING];
};
struct _GstSRTServerSrcClass {
GstSRTBaseSrcClass parent_class;
void (*client_added) (GstSRTServerSrc *self, int sock, struct sockaddr *addr, int addr_len);
void (*client_closed) (GstSRTServerSrc *self, int sock, struct sockaddr *addr, int addr_len);
gpointer _gst_reserved[GST_PADDING_LARGE];
};
GST_EXPORT
GType gst_srt_server_src_get_type (void);
G_END_DECLS
#endif /* __GST_SRT_SERVER_SRC_H__ */

27
ext/srt/meson.build Normal file
View file

@ -0,0 +1,27 @@
srt_sources = [
'gstsrt.c',
'gstsrtbasesrc.c',
'gstsrtclientsrc.c',
'gstsrtserversrc.c',
'gstsrtbasesink.c',
'gstsrtclientsink.c',
'gstsrtserversink.c',
]
srt_dep = dependency('libsrt', required : false)
if not srt_dep.found() and cc.has_header_symbol('srt/srt.h', 'srt_startup')
srt_dep = cc.find_library('srt', required : false)
endif
if srt_dep.found()
gstsrt = library('gstsrt',
srt_sources,
c_args : gst_plugins_bad_args,
link_args : noseh_link_args,
include_directories : [configinc, libsinc],
dependencies : [gstbase_dep, gio_dep, srt_dep],
install : true,
install_dir : plugins_install_dir,
)
endif