srt: Integrate server and client element into one

We have srt{client,server}{src,sink} elements in accordance to the
norm of the connection oriented protocols. However, SRT connection
mode can be changed by uri parameters so it requires an integrated
element to handle the parameters.

fix: #740
This commit is contained in:
Justin Kim 2018-11-07 14:47:44 +09:00 committed by Nicolas Dufresne
parent d7ad665d1c
commit 0a350c610d
23 changed files with 2550 additions and 3071 deletions

View file

@ -1,13 +1,9 @@
plugin_LTLIBRARIES = libgstsrt.la plugin_LTLIBRARIES = libgstsrt.la
libgstsrt_la_SOURCES = \ libgstsrt_la_SOURCES = \
gstsrt.c \ gstsrtobject.c \
gstsrtbasesrc.c \ gstsrtsink.c \
gstsrtclientsrc.c \ gstsrtsrc.c \
gstsrtserversrc.c \
gstsrtbasesink.c \
gstsrtclientsink.c \
gstsrtserversink.c \
$(NULL) $(NULL)
libgstsrt_la_CFLAGS = \ libgstsrt_la_CFLAGS = \
@ -30,13 +26,9 @@ libgstsrt_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS)
CLEANFILES = $(BUILT_SOURCES) CLEANFILES = $(BUILT_SOURCES)
noinst_HEADERS = \ noinst_HEADERS = \
gstsrt.h \ gstsrtobject.h \
gstsrtbasesrc.h \ gstsrtsink.h \
gstsrtclientsrc.h \ gstsrtsrc.h \
gstsrtserversrc.h \
gstsrtbasesink.h \
gstsrtclientsink.h \
gstsrtserversink.h \
$(NULL) $(NULL)
include $(top_srcdir)/common/gst-glib-gen.mak include $(top_srcdir)/common/gst-glib-gen.mak

67
ext/srt/gstsrt-enums.h Normal file
View file

@ -0,0 +1,67 @@
/* GStreamer
* Copyright (C) 2018, Collabora Ltd.
* Copyright (C) 2018, SK Telecom, Co., Ltd.
* Author: Jeongseok Kim <jeongseok.kim@sk.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_ENUM_H__
#define __GST_SRT_ENUM_H__
#include <gst/gst.h>
G_BEGIN_DECLS
/**
* GstSRTConnectionMode:
* @GST_SRT_CONNECTION_MODE_NONE: not connected
* @GST_SRT_CONNECTION_MODE_CALLER: The mode to send the connection request like a client
* @GST_SRT_CONNECTION_MODE_LISTENER: The mode to wait for being connected by peer caller
* @GST_SRT_CONNECTION_MODE_RENDEZVOUS: The mode to support one-to-one only connection
*
* SRT connection types.
*/
typedef enum
{
GST_SRT_CONNECTION_MODE_NONE = 0,
GST_SRT_CONNECTION_MODE_CALLER,
GST_SRT_CONNECTION_MODE_LISTENER,
GST_SRT_CONNECTION_MODE_RENDEZVOUS,
} GstSRTConnectionMode;
/**
* GstSRTKeyLengthBits:
* @GST_SRT_KEY_LENGTH_BITS_NO_KEY: no encryption
* @GST_SRT_KEY_LENGTH_BITS_0: no encryption
* @GST_SRT_KEY_LENGTH_BITS_128: 128-bit length
* @GST_SRT_KEY_LENGTH_BITS_192: 192-bit length
* @GST_SRT_KEY_LENGTH_BITS_256: 256-bit length
*
* Crypto key length in bits
*/
typedef enum
{
GST_SRT_KEY_LENGTH_BITS_NO_KEY = 0,
GST_SRT_KEY_LENGTH_BITS_0 = GST_SRT_KEY_LENGTH_BITS_NO_KEY,
GST_SRT_KEY_LENGTH_BITS_128 = 128,
GST_SRT_KEY_LENGTH_BITS_192 = 192,
GST_SRT_KEY_LENGTH_BITS_256 = 256,
} GstSRTKeyLengthBits;
G_END_DECLS
#endif // __GST_SRT_ENUM_H__

View file

@ -22,317 +22,35 @@
#include "config.h" #include "config.h"
#endif #endif
#include "gstsrt.h" #include "gstsrtsrc.h"
#include "gstsrtsink.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);
static GSocketAddress *
gst_srt_socket_address_new (GstElement * elem, const gchar * host, guint16 port)
{
GInetAddress *iaddr = NULL;
GSocketAddress *addr = NULL;
GError *error = NULL;
if (host == NULL) {
iaddr = g_inet_address_new_any (G_SOCKET_FAMILY_IPV4);
} else {
iaddr = g_inet_address_new_from_string (host);
}
if (!iaddr) {
GList *results;
GResolver *resolver = g_resolver_get_default ();
results = g_resolver_lookup_by_name (resolver, host, NULL, &error);
if (!results) {
GST_ERROR_OBJECT (elem, "Failed to resolve %s: %s", host, error->message);
g_object_unref (resolver);
goto failed;
}
iaddr = G_INET_ADDRESS (g_object_ref (results->data));
g_resolver_free_addresses (results);
g_object_unref (resolver);
}
#ifndef GST_DISABLE_GST_DEBUG
{
gchar *ip = g_inet_address_to_string (iaddr);
GST_DEBUG_OBJECT (elem, "IP address for host %s is %s", host, ip);
g_free (ip);
}
#endif
addr = g_inet_socket_address_new (iaddr, port);
g_object_unref (iaddr);
return addr;
failed:
g_clear_error (&error);
return NULL;
}
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, const gchar * passphrase,
int key_length)
{
SRTSOCKET sock = SRT_INVALID_SOCK;
GError *error = NULL;
gpointer sa;
size_t sa_len;
int poll_event = SRT_EPOLL_ERR;
poll_event |= sender ? SRT_EPOLL_OUT : SRT_EPOLL_IN;
if (host == NULL) {
GST_ELEMENT_ERROR (elem, RESOURCE, OPEN_READ, ("Invalid host"),
("Unspecified NULL host"));
goto failed;
}
*socket_address = gst_srt_socket_address_new (elem, 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));
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 (passphrase != NULL && passphrase[0] != '\0') {
srt_setsockopt (sock, 0, SRTO_PASSPHRASE, passphrase, strlen (passphrase));
srt_setsockopt (sock, 0, SRTO_PBKEYLEN, &key_length, 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, &poll_event);
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;
}
SRTSOCKET
gst_srt_server_listen (GstElement * elem, int sender, const gchar * host,
guint16 port, int latency, gint * poll_id, const gchar * passphrase,
int key_length)
{
SRTSOCKET sock = SRT_INVALID_SOCK;
GError *error = NULL;
struct sockaddr sa;
size_t sa_len;
GSocketAddress *addr = NULL;
addr = gst_srt_socket_address_new (elem, host, port);
if (addr == NULL) {
GST_WARNING_OBJECT (elem,
"failed to extract host or port from the given URI");
goto failed;
}
sa_len = g_socket_address_get_native_size (addr);
if (!g_socket_address_to_native (addr, &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 (sa.sa_family, SOCK_DGRAM, 0);
if (sock == SRT_INVALID_SOCK) {
GST_WARNING_OBJECT (elem, "failed to create SRT socket (reason: %s)",
srt_getlasterror_str ());
goto failed;
}
/* Make SRT server socket non-blocking */
/* for non-blocking srt_close() */
srt_setsockopt (sock, 0, SRTO_SNDSYN, &(int) {
0}, sizeof (int));
/* for non-blocking srt_accept() */
srt_setsockopt (sock, 0, SRTO_RCVSYN, &(int) {
0}, sizeof (int));
/* Make sure TSBPD mode is enable (SRT mode) */
srt_setsockopt (sock, 0, SRTO_TSBPDMODE, &(int) {
1}, sizeof (int));
srt_setsockopt (sock, 0, SRTO_SENDER, &sender, sizeof (int));
srt_setsockopt (sock, 0, SRTO_TSBPDDELAY, &latency, sizeof (int));
if (passphrase != NULL && passphrase[0] != '\0') {
srt_setsockopt (sock, 0, SRTO_PASSPHRASE, passphrase, strlen (passphrase));
srt_setsockopt (sock, 0, SRTO_PBKEYLEN, &key_length, sizeof (int));
}
*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_IN | SRT_EPOLL_ERR});
if (srt_bind (sock, &sa, sa_len) == SRT_ERROR) {
GST_WARNING_OBJECT (elem, "failed to bind SRT server socket (reason: %s)",
srt_getlasterror_str ());
goto failed;
}
if (srt_listen (sock, 1) == SRT_ERROR) {
GST_WARNING_OBJECT (elem, "failed to listen SRT socket (reason: %s)",
srt_getlasterror_str ());
goto failed;
}
g_clear_object (&addr);
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 (&addr);
return SRT_INVALID_SOCK;
}
static gboolean static gboolean
plugin_init (GstPlugin * plugin) plugin_init (GstPlugin * plugin)
{ {
GST_DEBUG_CATEGORY_INIT (GST_CAT_DEFAULT, "srt", 0, "SRT Common code"); if (!gst_element_register (plugin, "srtsrc", GST_RANK_PRIMARY,
GST_TYPE_SRT_SRC))
if (!gst_element_register (plugin, "srtclientsrc", GST_RANK_PRIMARY,
GST_TYPE_SRT_CLIENT_SRC))
return FALSE; return FALSE;
if (!gst_element_register (plugin, "srtserversrc", GST_RANK_PRIMARY, if (!gst_element_register (plugin, "srtsink", GST_RANK_PRIMARY,
GST_TYPE_SRT_SERVER_SRC)) GST_TYPE_SRT_SINK))
return FALSE; return FALSE;
if (!gst_element_register (plugin, "srtclientsink", GST_RANK_PRIMARY, /* deprecated */
GST_TYPE_SRT_CLIENT_SINK)) if (!gst_element_register (plugin, "srtclientsrc", GST_RANK_NONE,
GST_TYPE_SRT_SRC))
return FALSE; return FALSE;
if (!gst_element_register (plugin, "srtserversink", GST_RANK_PRIMARY, if (!gst_element_register (plugin, "srtserversrc", GST_RANK_NONE,
GST_TYPE_SRT_SERVER_SINK)) GST_TYPE_SRT_SRC))
return FALSE;
if (!gst_element_register (plugin, "srtclientsink", GST_RANK_NONE,
GST_TYPE_SRT_SINK))
return FALSE;
if (!gst_element_register (plugin, "srtserversink", GST_RANK_NONE,
GST_TYPE_SRT_SINK))
return FALSE; return FALSE;
return TRUE; return TRUE;

View file

@ -1,54 +0,0 @@
/* 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 <gio/gnetworking.h>
#include <srt/srt.h>
#define SRT_URI_SCHEME "srt"
#define SRT_DEFAULT_PORT 7001
#define SRT_DEFAULT_HOST "localhost"
#define SRT_DEFAULT_URI SRT_URI_SCHEME"://"SRT_DEFAULT_HOST":"G_STRINGIFY(SRT_DEFAULT_PORT)
#define SRT_DEFAULT_LATENCY 125
#define SRT_DEFAULT_KEY_LENGTH 16
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,
const gchar * passphrase, int key_length);
SRTSOCKET
gst_srt_server_listen (GstElement * elem, int sender,
const gchar * host, guint16 port, gint latency, gint * poll_id,
const gchar * passphrase, int key_length);
G_END_DECLS
#endif /* __GST_SRT_H__ */

View file

@ -1,441 +0,0 @@
/* 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>
#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,
PROP_PASSPHRASE,
PROP_KEY_LENGTH,
/*< 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;
case PROP_PASSPHRASE:
g_value_set_string (value, self->passphrase);
break;
case PROP_KEY_LENGTH:
g_value_set_int (value, self->key_length);
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;
case PROP_PASSPHRASE:
g_free (self->passphrase);
self->passphrase = g_value_dup_string (value);
break;
case PROP_KEY_LENGTH:
{
gint key_length = g_value_get_int (value);
g_return_if_fail (key_length == 16 || key_length == 24
|| key_length == 32);
self->key_length = key_length;
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->headers, gst_buffer_list_unref);
g_clear_pointer (&self->uri, gst_uri_unref);
g_clear_pointer (&self->passphrase, g_free);
G_OBJECT_CLASS (parent_class)->finalize (object);
}
static gboolean
gst_srt_base_sink_set_caps (GstBaseSink * sink, GstCaps * caps)
{
GstSRTBaseSink *self = GST_SRT_BASE_SINK (sink);
GstStructure *s;
const GValue *streamheader;
GST_DEBUG_OBJECT (self, "setcaps %" GST_PTR_FORMAT, caps);
g_clear_pointer (&self->headers, gst_buffer_list_unref);
s = gst_caps_get_structure (caps, 0);
streamheader = gst_structure_get_value (s, "streamheader");
if (!streamheader) {
GST_DEBUG_OBJECT (self, "'streamheader' field not present");
} else if (GST_VALUE_HOLDS_BUFFER (streamheader)) {
GST_DEBUG_OBJECT (self, "'streamheader' field holds buffer");
self->headers = gst_buffer_list_new_sized (1);
gst_buffer_list_add (self->headers, g_value_dup_boxed (streamheader));
} else if (GST_VALUE_HOLDS_ARRAY (streamheader)) {
guint i, size;
GST_DEBUG_OBJECT (self, "'streamheader' field holds array");
size = gst_value_array_get_size (streamheader);
self->headers = gst_buffer_list_new_sized (size);
for (i = 0; i < size; i++) {
const GValue *v = gst_value_array_get_value (streamheader, i);
if (!GST_VALUE_HOLDS_BUFFER (v)) {
GST_ERROR_OBJECT (self, "'streamheader' item of unexpected type '%s'",
G_VALUE_TYPE_NAME (v));
return FALSE;
}
gst_buffer_list_add (self->headers, g_value_dup_boxed (v));
}
} else {
GST_ERROR_OBJECT (self, "'streamheader' field has unexpected type '%s'",
G_VALUE_TYPE_NAME (streamheader));
return FALSE;
}
GST_DEBUG_OBJECT (self, "Collected streamheaders: %u buffers",
self->headers ? gst_buffer_list_length (self->headers) : 0);
return TRUE;
}
static gboolean
gst_srt_base_sink_stop (GstBaseSink * sink)
{
GstSRTBaseSink *self = GST_SRT_BASE_SINK (sink);
g_clear_pointer (&self->headers, gst_buffer_list_unref);
return TRUE;
}
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;
if (self->headers && GST_BUFFER_FLAG_IS_SET (buffer, GST_BUFFER_FLAG_HEADER)) {
GST_DEBUG_OBJECT (self, "Have streamheaders,"
" ignoring header %" GST_PTR_FORMAT, buffer);
return 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);
properties[PROP_PASSPHRASE] = g_param_spec_string ("passphrase", "Passphrase",
"The password for the encrypted transmission", NULL,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
properties[PROP_KEY_LENGTH] =
g_param_spec_int ("key-length", "key length",
"Crypto key length in bytes{16,24,32}", 16,
32, SRT_DEFAULT_KEY_LENGTH, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
g_object_class_install_properties (gobject_class, PROP_LAST, properties);
gstbasesink_class->set_caps = GST_DEBUG_FUNCPTR (gst_srt_base_sink_set_caps);
gstbasesink_class->stop = GST_DEBUG_FUNCPTR (gst_srt_base_sink_stop);
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->latency = SRT_DEFAULT_LATENCY;
self->passphrase = NULL;
self->key_length = SRT_DEFAULT_KEY_LENGTH;
}
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;
}
gboolean
gst_srt_base_sink_send_headers (GstSRTBaseSink * self,
GstSRTBaseSinkSendCallback send_cb, gpointer user_data)
{
guint size, i;
g_return_val_if_fail (GST_IS_SRT_BASE_SINK (self), FALSE);
g_return_val_if_fail (send_cb, FALSE);
if (!self->headers)
return TRUE;
size = gst_buffer_list_length (self->headers);
GST_DEBUG_OBJECT (self, "Sending %u stream headers", size);
for (i = 0; i < size; i++) {
GstBuffer *buffer = gst_buffer_list_get (self->headers, i);
GstMapInfo info;
gboolean ret;
GST_TRACE_OBJECT (self, "sending header %u %" GST_PTR_FORMAT, i, buffer);
if (!gst_buffer_map (buffer, &info, GST_MAP_READ)) {
GST_ELEMENT_ERROR (self, RESOURCE, READ,
("Could not map the input stream"), (NULL));
return FALSE;
}
ret = send_cb (self, &info, user_data);
gst_buffer_unmap (buffer, &info);
if (!ret)
return FALSE;
}
return TRUE;
}
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;
}

View file

@ -1,81 +0,0 @@
/* 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;
GstBufferList *headers;
gint latency;
gchar *passphrase;
gint key_length;
/*< 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);
typedef gboolean (*GstSRTBaseSinkSendCallback) (GstSRTBaseSink *sink,
const GstMapInfo *mapinfo, gpointer user_data);
gboolean gst_srt_base_sink_send_headers (GstSRTBaseSink *sink,
GstSRTBaseSinkSendCallback send_cb, gpointer user_data);
GstStructure * gst_srt_base_sink_get_stats (GSocketAddress *sockaddr,
SRTSOCKET sock);
G_END_DECLS
#endif /* __GST_SRT_BASE_SINK_H__ */

View file

@ -1,293 +0,0 @@
/* 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>
#define GST_CAT_DEFAULT gst_debug_srt_base_src
GST_DEBUG_CATEGORY (GST_CAT_DEFAULT);
enum
{
PROP_URI = 1,
PROP_CAPS,
PROP_LATENCY,
PROP_PASSPHRASE,
PROP_KEY_LENGTH,
/*< 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;
case PROP_PASSPHRASE:
g_value_set_string (value, self->passphrase);
break;
case PROP_KEY_LENGTH:
g_value_set_int (value, self->key_length);
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;
case PROP_PASSPHRASE:
g_free (self->passphrase);
self->passphrase = g_value_dup_string (value);
break;
case PROP_KEY_LENGTH:
{
gint key_length = g_value_get_int (value);
g_return_if_fail (key_length == 16 || key_length == 24
|| key_length == 32);
self->key_length = key_length;
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_clear_pointer (&self->passphrase, g_free);
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);
properties[PROP_PASSPHRASE] = g_param_spec_string ("passphrase", "Passphrase",
"The password for the encrypted transmission", NULL,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
properties[PROP_KEY_LENGTH] =
g_param_spec_int ("key-length", "key length",
"Crypto key length in bytes{16,24,32}", 16,
32, SRT_DEFAULT_KEY_LENGTH, 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);
gst_base_src_set_do_timestamp (GST_BASE_SRC (self), TRUE);
self->latency = SRT_DEFAULT_LATENCY;
self->key_length = SRT_DEFAULT_KEY_LENGTH;
}
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;
}

View file

@ -1,66 +0,0 @@
/* 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;
gchar *passphrase;
gint key_length;
/*< 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__ */

View file

@ -1,301 +0,0 @@
/* 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;
gboolean sent_headers;
};
#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->bind_port);
break;
case PROP_BIND_ADDRESS:
g_value_set_string (value, priv->bind_address);
break;
case PROP_RENDEZ_VOUS:
g_value_set_boolean (value, priv->rendez_vous);
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 void
gst_srt_client_sink_finalize (GObject * object)
{
GstSRTClientSink *self = GST_SRT_CLIENT_SINK (object);
GstSRTClientSinkPrivate *priv = GST_SRT_CLIENT_SINK_GET_PRIVATE (self);
g_free (priv->bind_address);
G_OBJECT_CLASS (parent_class)->finalize (object);
}
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), TRUE,
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, base->passphrase, base->key_length);
g_clear_pointer (&uri, gst_uri_unref);
return (priv->sock != SRT_INVALID_SOCK);
}
static gboolean
send_buffer_internal (GstSRTBaseSink * sink,
const GstMapInfo * mapinfo, gpointer user_data)
{
SRTSOCKET sock = GPOINTER_TO_INT (user_data);
if (srt_sendmsg2 (sock, (char *) mapinfo->data, mapinfo->size,
0) == SRT_ERROR) {
GST_ELEMENT_ERROR (sink, RESOURCE, WRITE, NULL,
("%s", srt_getlasterror_str ()));
return FALSE;
}
return TRUE;
}
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 (!priv->sent_headers) {
if (!gst_srt_base_sink_send_headers (sink, send_buffer_internal,
GINT_TO_POINTER (priv->sock)))
return FALSE;
priv->sent_headers = TRUE;
}
return send_buffer_internal (sink, mapinfo, GINT_TO_POINTER (priv->sock));
}
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);
priv->sent_headers = FALSE;
return GST_BASE_SINK_CLASS (parent_class)->stop (sink);
}
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;
gobject_class->finalize = gst_srt_client_sink_finalize;
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

@ -1,59 +0,0 @@
/* 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__ */

View file

@ -1,327 +0,0 @@
/* 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"
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->bind_port);
break;
case PROP_BIND_ADDRESS:
g_value_set_string (value, priv->bind_address);
break;
case PROP_RENDEZ_VOUS:
g_value_set_boolean (value, priv->rendez_vous);
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, ready, &(int) {
2}, 0, 0, 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_resize (outbuf, 0, recv_len);
GST_LOG_OBJECT (src, "filled buffer from _get of size %" G_GSIZE_FORMAT,
gst_buffer_get_size (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, base->passphrase, base->key_length);
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;
}

View file

@ -1,59 +0,0 @@
/* 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__ */

1441
ext/srt/gstsrtobject.c Normal file

File diff suppressed because it is too large Load diff

127
ext/srt/gstsrtobject.h Normal file
View file

@ -0,0 +1,127 @@
/* GStreamer
* Copyright (C) 2018, Collabora Ltd.
* Copyright (C) 2018, SK Telecom, Co., Ltd.
* Author: Jeongseok Kim <jeongseok.kim@sk.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_OBJECT_H__
#define __GST_SRT_OBJECT_H__
#include "gstsrt-enums.h"
#include "gstsrt-enumtypes.h"
#include <gio/gio.h>
#include <srt/srt.h>
G_BEGIN_DECLS
#define GST_SRT_DEFAULT_URI_SCHEME "srt"
#define GST_SRT_DEFAULT_PORT 7001
#define GST_SRT_DEFAULT_HOST "127.0.0.1"
#define GST_SRT_DEFAULT_LOCALADDRESS "0.0.0.0"
#define GST_SRT_DEFAULT_URI GST_SRT_DEFAULT_URI_SCHEME"://"GST_SRT_DEFAULT_HOST":"G_STRINGIFY(GST_SRT_DEFAULT_PORT)
#define GST_SRT_DEFAULT_MODE GST_SRT_CONNECTION_MODE_CALLER
#define GST_SRT_DEFAULT_PBKEYLEN GST_SRT_KEY_LENGTH_BITS_128
#define GST_SRT_DEFAULT_POLL_TIMEOUT -1
#define GST_SRT_DEFAULT_LATENCY 125
#define GST_SRT_DEFAULT_MSG_SIZE 1316
#define GST_SRT_DEFAULT_KEY_LENGTH 16
typedef struct _GstSRTObject GstSRTObject;
struct _GstSRTObject
{
GstElement *element;
GstUri *uri;
GstStructure *parameters;
gboolean opened;
SRTSOCKET sock;
gint poll_id;
gboolean sent_headers;
GMutex sock_lock;
GCond sock_cond;
GTask *listener_task;
SRTSOCKET listener_sock;
gint listener_poll_id;
GMainLoop *loop;
GMainContext *context;
GSource *listener_source;
GThread *thread;
GList *callers;
GClosure *caller_added_closure;
GClosure *caller_removed_closure;
};
typedef void (*GstSRTObjectCallerAdded) (int sock, GSocketAddress *addr, GstSRTObject * srtobject);
typedef void (*GstSRTObjectCallerRemoved) (int sock, GSocketAddress *addr, GstSRTObject * srtobject);
GstSRTObject *gst_srt_object_new (GstElement *element);
void gst_srt_object_destroy (GstSRTObject *srtobject);
gboolean gst_srt_object_open (GstSRTObject *srtobject,
GCancellable *cancellable,
GError **error);
gboolean gst_srt_object_open_full (GstSRTObject *srtobject,
GstSRTObjectCallerAdded caller_added_func,
GstSRTObjectCallerRemoved caller_removed_func,
GCancellable *cancellable,
GError **error);
void gst_srt_object_close (GstSRTObject *srtobject);
gboolean gst_srt_object_set_property_helper (GstSRTObject *srtobject,
guint prop_id, const GValue * value,
GParamSpec * pspec);
gboolean gst_srt_object_get_property_helper (GstSRTObject *srtobject,
guint prop_id, GValue * value,
GParamSpec * pspec);
void gst_srt_object_install_properties_helper (GObjectClass *gobject_class);
gboolean gst_srt_object_set_uri (GstSRTObject * srtobject, const gchar *uri, GError ** err);
gssize gst_srt_object_read (GstSRTObject * srtobject,
guint8 *data, gsize size,
GCancellable *cancellable,
GError **err);
gssize gst_srt_object_write (GstSRTObject * srtobject,
GstBufferList * headers,
const GstMapInfo * mapinfo,
GCancellable *cancellable,
GError **err);
void gst_srt_object_wakeup (GstSRTObject * srtobject);
GstStructure *gst_srt_object_get_stats (GstSRTObject * srtobject);
G_END_DECLS
#endif // __GST_SRT_OBJECT_H__

View file

@ -1,516 +0,0 @@
/* 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;
gboolean sent_headers;
} 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);
GError *error = NULL;
gboolean ret = TRUE;
const gchar *host;
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);
priv->sock = gst_srt_server_listen (GST_ELEMENT (self),
TRUE, host, gst_uri_get_port (uri),
base->latency, &priv->poll_id, base->passphrase, base->key_length);
if (priv->sock == SRT_INVALID_SOCK) {
GST_ERROR_OBJECT (sink, "Failed to create srt socket");
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);
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);
return FALSE;
}
static gboolean
send_buffer_internal (GstSRTBaseSink * sink,
const GstMapInfo * mapinfo, gpointer user_data)
{
SRTClient *client = user_data;
if (srt_sendmsg2 (client->sock, (char *) mapinfo->data, mapinfo->size,
0) == SRT_ERROR) {
GST_WARNING_OBJECT (sink, "%s", srt_getlasterror_str ());
return FALSE;
}
return TRUE;
}
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 (!client->sent_headers) {
if (!gst_srt_base_sink_send_headers (sink, send_buffer_internal, client))
goto err;
client->sent_headers = TRUE;
}
if (!send_buffer_internal (sink, mapinfo, client))
goto err;
continue;
err:
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);
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 GST_BASE_SINK_CLASS (parent_class)->stop (sink);
}
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

@ -1,62 +0,0 @@
/* 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"
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, GSocketAddress *addr);
void (*client_removed) (GstSRTServerSink *self, int sock, GSocketAddress *addr);
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__ */

View file

@ -1,426 +0,0 @@
/* 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 <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);
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_resize (outbuf, 0, recv_len);
GST_LOG_OBJECT (src, "filled buffer from _get of size %" G_GSIZE_FORMAT,
gst_buffer_get_size (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);
const gchar *host;
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);
priv->sock = gst_srt_server_listen (GST_ELEMENT (self),
FALSE, host, gst_uri_get_port (uri),
base->latency, &priv->poll_id, base->passphrase, base->key_length);
if (priv->sock == SRT_INVALID_SOCK) {
GST_ERROR_OBJECT (src, "Failed to create srt socket");
goto failed;
}
g_clear_pointer (&uri, gst_uri_unref);
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_pointer (&uri, gst_uri_unref);
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_CLOSED], 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;
}

View file

@ -1,64 +0,0 @@
/* 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 <gio/gio.h>
#include <srt/srt.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, GSocketAddress *addr);
void (*client_closed) (GstSRTServerSrc *self, int sock, GSocketAddress *addr);
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__ */

391
ext/srt/gstsrtsink.c Normal file
View file

@ -0,0 +1,391 @@
/* GStreamer
* Copyright (C) 2018, Collabora Ltd.
* Copyright (C) 2018, SK Telecom, Co., Ltd.
* Author: Jeongseok Kim <jeongseok.kim@sk.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-srtsink
* @title: srtsink
*
* srtsink is a network sink that sends <ulink url="http://www.srtalliance.org/">SRT</ulink>
* packets to the network.
*
* <refsect2>
* <title>Examples</title>
* |[
* gst-launch-1.0 -v audiotestsrc ! srtsink uri://host?mode=caller
* ]| This pipeline shows how to serve SRT packets through the default port.
*
* |[
* gst-launch-1.0 -v audiotestsrc ! srtsink uri://host:port?mode=listener
* ]| This pipeline shows how to wait SRT callers.
* </refsect2>
*
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include "gstsrtsink.h"
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_sink
GST_DEBUG_CATEGORY (GST_CAT_DEFAULT);
enum
{
SIG_CALLER_ADDED,
SIG_CALLER_REMOVED,
LAST_SIGNAL
};
static guint signals[LAST_SIGNAL] = { 0 };
static void gst_srt_sink_uri_handler_init (gpointer g_iface,
gpointer iface_data);
static gchar *gst_srt_sink_uri_get_uri (GstURIHandler * handler);
static gboolean gst_srt_sink_uri_set_uri (GstURIHandler * handler,
const gchar * uri, GError ** error);
#define gst_srt_sink_parent_class parent_class
G_DEFINE_TYPE_WITH_CODE (GstSRTSink, gst_srt_sink,
GST_TYPE_BASE_SINK,
G_IMPLEMENT_INTERFACE (GST_TYPE_URI_HANDLER, gst_srt_sink_uri_handler_init)
GST_DEBUG_CATEGORY_INIT (GST_CAT_DEFAULT, "srtsink", 0, "SRT Sink"));
static void
gst_srt_sink_set_property (GObject * object,
guint prop_id, const GValue * value, GParamSpec * pspec)
{
GstSRTSink *self = GST_SRT_SINK (object);
if (!gst_srt_object_set_property_helper (self->srtobject, prop_id, value,
pspec)) {
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
}
static void
gst_srt_sink_get_property (GObject * object,
guint prop_id, GValue * value, GParamSpec * pspec)
{
GstSRTSink *self = GST_SRT_SINK (object);
if (!gst_srt_object_get_property_helper (self->srtobject, prop_id, value,
pspec)) {
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
}
static void
gst_srt_sink_finalize (GObject * object)
{
GstSRTSink *self = GST_SRT_SINK (object);
g_clear_object (&self->cancellable);
gst_srt_object_destroy (self->srtobject);
G_OBJECT_CLASS (parent_class)->finalize (object);
}
static void
gst_srt_sink_init (GstSRTSink * self)
{
self->srtobject = gst_srt_object_new (GST_ELEMENT (self));
self->cancellable = g_cancellable_new ();
gst_srt_object_set_uri (self->srtobject, GST_SRT_DEFAULT_URI, NULL);
}
static void
gst_srt_sink_caller_added_cb (int sock, GSocketAddress * addr,
GstSRTObject * srtobject)
{
g_signal_emit (srtobject->element, signals[SIG_CALLER_ADDED], 0, sock, addr);
}
static void
gst_srt_sink_caller_removed_cb (int sock, GSocketAddress * addr,
GstSRTObject * srtobject)
{
g_signal_emit (srtobject->element, signals[SIG_CALLER_REMOVED], 0, sock,
addr);
}
static gboolean
gst_srt_sink_start (GstBaseSink * bsink)
{
GstSRTSink *self = GST_SRT_SINK (bsink);
GstSRTConnectionMode connection_mode = GST_SRT_CONNECTION_MODE_NONE;
GError *error = NULL;
gboolean ret = FALSE;
gst_structure_get_enum (self->srtobject->parameters, "mode",
GST_TYPE_SRT_CONNECTION_MODE, (gint *) & connection_mode);
if (connection_mode == GST_SRT_CONNECTION_MODE_LISTENER) {
ret =
gst_srt_object_open_full (self->srtobject, gst_srt_sink_caller_added_cb,
gst_srt_sink_caller_removed_cb, self->cancellable, &error);
} else {
ret = gst_srt_object_open (self->srtobject, self->cancellable, &error);
}
if (!ret) {
GST_WARNING_OBJECT (self, "Failed to open SRT: %s", error->message);
g_clear_error (&error);
}
return ret;
}
static gboolean
gst_srt_sink_stop (GstBaseSink * bsink)
{
GstSRTSink *self = GST_SRT_SINK (bsink);
gst_srt_object_close (self->srtobject);
return TRUE;
}
static GstFlowReturn
gst_srt_sink_render (GstBaseSink * sink, GstBuffer * buffer)
{
GstSRTSink *self = GST_SRT_SINK (sink);
GstFlowReturn ret = GST_FLOW_OK;
GstMapInfo info;
GError *error = NULL;
if (g_cancellable_is_cancelled (self->cancellable)) {
ret = GST_FLOW_FLUSHING;
}
if (self->headers && GST_BUFFER_FLAG_IS_SET (buffer, GST_BUFFER_FLAG_HEADER)) {
GST_DEBUG_OBJECT (self, "Have streamheaders,"
" ignoring header %" GST_PTR_FORMAT, buffer);
return GST_FLOW_OK;
}
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 (gst_srt_object_write (self->srtobject, self->headers, &info,
self->cancellable, &error) < 0) {
ret = GST_FLOW_ERROR;
}
gst_buffer_unmap (buffer, &info);
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));
return ret;
}
static gboolean
gst_srt_sink_unlock (GstBaseSink * bsink)
{
GstSRTSink *self = GST_SRT_SINK (bsink);
g_cancellable_cancel (self->cancellable);
gst_srt_object_wakeup (self->srtobject);
return TRUE;
}
static gboolean
gst_srt_sink_unlock_stop (GstBaseSink * bsink)
{
GstSRTSink *self = GST_SRT_SINK (bsink);
g_cancellable_reset (self->cancellable);
return TRUE;
}
static gboolean
gst_srt_sink_set_caps (GstBaseSink * bsink, GstCaps * caps)
{
GstSRTSink *self = GST_SRT_SINK (bsink);
GstStructure *s;
const GValue *streamheader;
GST_DEBUG_OBJECT (self, "setcaps %" GST_PTR_FORMAT, caps);
g_clear_pointer (&self->headers, gst_buffer_list_unref);
s = gst_caps_get_structure (caps, 0);
streamheader = gst_structure_get_value (s, "streamheader");
if (!streamheader) {
GST_DEBUG_OBJECT (self, "'streamheader' field not present");
} else if (GST_VALUE_HOLDS_BUFFER (streamheader)) {
GST_DEBUG_OBJECT (self, "'streamheader' field holds buffer");
self->headers = gst_buffer_list_new_sized (1);
gst_buffer_list_add (self->headers, g_value_dup_boxed (streamheader));
} else if (GST_VALUE_HOLDS_ARRAY (streamheader)) {
guint i, size;
GST_DEBUG_OBJECT (self, "'streamheader' field holds array");
size = gst_value_array_get_size (streamheader);
self->headers = gst_buffer_list_new_sized (size);
for (i = 0; i < size; i++) {
const GValue *v = gst_value_array_get_value (streamheader, i);
if (!GST_VALUE_HOLDS_BUFFER (v)) {
GST_ERROR_OBJECT (self, "'streamheader' item of unexpected type '%s'",
G_VALUE_TYPE_NAME (v));
return FALSE;
}
gst_buffer_list_add (self->headers, g_value_dup_boxed (v));
}
} else {
GST_ERROR_OBJECT (self, "'streamheader' field has unexpected type '%s'",
G_VALUE_TYPE_NAME (streamheader));
return FALSE;
}
GST_DEBUG_OBJECT (self, "Collected streamheaders: %u buffers",
self->headers ? gst_buffer_list_length (self->headers) : 0);
return TRUE;
}
static void
gst_srt_sink_class_init (GstSRTSinkClass * klass)
{
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
GstElementClass *gstelement_class = GST_ELEMENT_CLASS (klass);
GstBaseSinkClass *gstbasesink_class = GST_BASE_SINK_CLASS (klass);
gobject_class->set_property = gst_srt_sink_set_property;
gobject_class->get_property = gst_srt_sink_get_property;
gobject_class->finalize = gst_srt_sink_finalize;
/**
* GstSRTSink::caller-added:
* @gstsrtsink: the srtsink element that emitted this signal
* @sock: the client socket descriptor that was added to srtsink
* @addr: the #GSocketAddress that describes the @sock
*
* The given socket descriptor was added to srtsink.
*/
signals[SIG_CALLER_ADDED] =
g_signal_new ("caller-added", G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstSRTSinkClass, caller_added),
NULL, NULL, g_cclosure_marshal_generic, G_TYPE_NONE,
2, G_TYPE_INT, G_TYPE_SOCKET_ADDRESS);
/**
* GstSRTSink::caller-removed:
* @gstsrtsink: the srtsink element that emitted this signal
* @sock: the client socket descriptor that was added to srtsink
* @addr: the #GSocketAddress that describes the @sock
*
* The given socket descriptor was removed from srtsink.
*/
signals[SIG_CALLER_REMOVED] =
g_signal_new ("caller-removed", G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstSRTSinkClass,
caller_added), NULL, NULL, g_cclosure_marshal_generic, G_TYPE_NONE,
2, G_TYPE_INT, G_TYPE_SOCKET_ADDRESS);
gst_srt_object_install_properties_helper (gobject_class);
gst_element_class_add_static_pad_template (gstelement_class, &sink_template);
gst_element_class_set_metadata (gstelement_class,
"SRT sink", "Sink/Network",
"Send data over the network via SRT",
"Justin Kim <justin.joy.9to5@gmail.com>");
gstbasesink_class->start = GST_DEBUG_FUNCPTR (gst_srt_sink_start);
gstbasesink_class->stop = GST_DEBUG_FUNCPTR (gst_srt_sink_stop);
gstbasesink_class->render = GST_DEBUG_FUNCPTR (gst_srt_sink_render);
gstbasesink_class->unlock = GST_DEBUG_FUNCPTR (gst_srt_sink_unlock);
gstbasesink_class->unlock_stop = GST_DEBUG_FUNCPTR (gst_srt_sink_unlock_stop);
gstbasesink_class->set_caps = GST_DEBUG_FUNCPTR (gst_srt_sink_set_caps);
}
static GstURIType
gst_srt_sink_uri_get_type (GType type)
{
return GST_URI_SINK;
}
static const gchar *const *
gst_srt_sink_uri_get_protocols (GType type)
{
static const gchar *protocols[] = { GST_SRT_DEFAULT_URI_SCHEME, NULL };
return protocols;
}
static gchar *
gst_srt_sink_uri_get_uri (GstURIHandler * handler)
{
gchar *uri_str;
GstSRTSink *self = GST_SRT_SINK (handler);
GST_OBJECT_LOCK (self);
uri_str = gst_uri_to_string (self->srtobject->uri);
GST_OBJECT_UNLOCK (self);
return uri_str;
}
static gboolean
gst_srt_sink_uri_set_uri (GstURIHandler * handler,
const gchar * uri, GError ** error)
{
GstSRTSink *self = GST_SRT_SINK (handler);
return gst_srt_object_set_uri (self->srtobject, uri, error);
}
static void
gst_srt_sink_uri_handler_init (gpointer g_iface, gpointer iface_data)
{
GstURIHandlerInterface *iface = (GstURIHandlerInterface *) g_iface;
iface->get_type = gst_srt_sink_uri_get_type;
iface->get_protocols = gst_srt_sink_uri_get_protocols;
iface->get_uri = gst_srt_sink_uri_get_uri;
iface->set_uri = gst_srt_sink_uri_set_uri;
}

65
ext/srt/gstsrtsink.h Normal file
View file

@ -0,0 +1,65 @@
/* GStreamer
* Copyright (C) 2018, Collabora Ltd.
* Copyright (C) 2018, SK Telecom, Co., Ltd.
* Author: Jeongseok Kim <jeongseok.kim@sk.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_SINK_H__
#define __GST_SRT_SINK_H__
#include <gst/gst.h>
#include <gst/base/gstbasesink.h>
#include <gio/gio.h>
#include "gstsrtobject.h"
G_BEGIN_DECLS
#define GST_TYPE_SRT_SINK (gst_srt_sink_get_type ())
#define GST_IS_SRT_SINK(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_SRT_SINK))
#define GST_IS_SRT_SINK_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GST_TYPE_SRT_SINK))
#define GST_SRT_SINK_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_SRT_SINK, GstSRTSinkClass))
#define GST_SRT_SINK(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_SRT_SINK, GstSRTSink))
#define GST_SRT_SINK_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GST_TYPE_SRT_SINK, GstSRTSinkClass))
#define GST_SRT_SINK_CAST(obj) ((GstSRTSink*)(obj))
#define GST_SRT_SINK_CLASS_CAST(klass) ((GstSRTSinkClass*)(klass))
typedef struct _GstSRTSink GstSRTSink;
typedef struct _GstSRTSinkClass GstSRTSinkClass;
struct _GstSRTSink {
GstBaseSink parent;
GstBufferList *headers;
GstSRTObject *srtobject;
GCancellable *cancellable;
};
struct _GstSRTSinkClass {
GstBaseSinkClass parent_class;
void (*caller_added) (GstSRTSink *self, int sock, GSocketAddress * addr);
void (*caller_removed) (GstSRTSink *self, int sock, GSocketAddress * addr);
};
GType gst_srt_sink_get_type (void);
G_END_DECLS
#endif // __GST_SRT_SINK_H__

360
ext/srt/gstsrtsrc.c Normal file
View file

@ -0,0 +1,360 @@
/* GStreamer
* Copyright (C) 2018, Collabora Ltd.
* Copyright (C) 2018, SK Telecom, Co., Ltd.
* Author: Jeongseok Kim <jeongseok.kim@sk.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-srtsrc
* @title: srtsrc
*
* srtsrc is a network source that reads <ulink url="http://www.srtalliance.org/">SRT</ulink>
* packets from the network.
*
* <refsect2>
* <title>Examples</title>
* |[
* gst-launch-1.0 -v srtsrc uri="srt://127.0.0.1:7001" ! fakesink
* ]| This pipeline shows how to connect SRT server by setting #GstSRTSrc:uri property.
*
* |[
* gst-launch-1.0 -v srtsrc uri="srt://127.0.0.1:7001?mode=listener" ! fakesink
* ]| This pipeline shows how to wait SRT connection by setting #GstSRTSrc:uri property.
*
* |[
* gst-launch-1.0 -v srtclientsrc uri="srt://192.168.1.10:7001?mode=rendez-vous" ! fakesink
* ]| This pipeline shows how to connect SRT server by setting #GstSRTSrc:uri property and using the rendez-vous mode.
* </refsect2>
*
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include "gstsrtsrc.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_src
GST_DEBUG_CATEGORY (GST_CAT_DEFAULT);
enum
{
SIG_CALLER_ADDED,
SIG_CALLER_REMOVED,
LAST_SIGNAL
};
static guint signals[LAST_SIGNAL] = { 0 };
static void gst_srt_src_uri_handler_init (gpointer g_iface,
gpointer iface_data);
static gchar *gst_srt_src_uri_get_uri (GstURIHandler * handler);
static gboolean gst_srt_src_uri_set_uri (GstURIHandler * handler,
const gchar * uri, GError ** error);
#define gst_srt_src_parent_class parent_class
G_DEFINE_TYPE_WITH_CODE (GstSRTSrc, gst_srt_src,
GST_TYPE_PUSH_SRC,
G_IMPLEMENT_INTERFACE (GST_TYPE_URI_HANDLER, gst_srt_src_uri_handler_init)
GST_DEBUG_CATEGORY_INIT (GST_CAT_DEFAULT, "srtsrc", 0, "SRT Source"));
static void
gst_srt_src_caller_added_cb (int sock, GSocketAddress * addr,
GstSRTObject * srtobject)
{
g_signal_emit (srtobject->element, signals[SIG_CALLER_ADDED], 0, sock, addr);
}
static void
gst_srt_src_caller_removed_cb (int sock, GSocketAddress * addr,
GstSRTObject * srtobject)
{
g_signal_emit (srtobject->element, signals[SIG_CALLER_REMOVED], 0, sock,
addr);
}
static gboolean
gst_srt_src_start (GstBaseSrc * bsrc)
{
GstSRTSrc *self = GST_SRT_SRC (bsrc);
GError *error = NULL;
gboolean ret = FALSE;
GstSRTConnectionMode connection_mode = GST_SRT_CONNECTION_MODE_NONE;
gst_structure_get_enum (self->srtobject->parameters, "mode",
GST_TYPE_SRT_CONNECTION_MODE, (gint *) & connection_mode);
if (connection_mode == GST_SRT_CONNECTION_MODE_LISTENER) {
ret =
gst_srt_object_open_full (self->srtobject, gst_srt_src_caller_added_cb,
gst_srt_src_caller_removed_cb, self->cancellable, &error);
} else {
ret = gst_srt_object_open (self->srtobject, self->cancellable, &error);
}
if (!ret) {
GST_WARNING_OBJECT (self, "Failed to open SRT: %s", error->message);
g_clear_error (&error);
}
return ret;
}
static gboolean
gst_srt_src_stop (GstBaseSrc * bsrc)
{
GstSRTSrc *self = GST_SRT_SRC (bsrc);
gst_srt_object_close (self->srtobject);
return TRUE;
}
static GstFlowReturn
gst_srt_src_fill (GstPushSrc * src, GstBuffer * outbuf)
{
GstSRTSrc *self = GST_SRT_SRC (src);
GstFlowReturn ret = GST_FLOW_OK;
GstMapInfo info;
GError *err = NULL;
gssize recv_len;
if (g_cancellable_is_cancelled (self->cancellable)) {
ret = GST_FLOW_FLUSHING;
}
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 = gst_srt_object_read (self->srtobject, info.data,
gst_buffer_get_size (outbuf), self->cancellable, &err);
gst_buffer_unmap (outbuf, &info);
if (g_cancellable_is_cancelled (self->cancellable)) {
ret = GST_FLOW_FLUSHING;
goto out;
}
if (recv_len < 0) {
GST_ELEMENT_ERROR (src, RESOURCE, READ, (NULL), ("%s", err->message));
ret = GST_FLOW_ERROR;
g_clear_error (&err);
goto out;
} else if (recv_len == 0) {
ret = GST_FLOW_EOS;
goto out;
}
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 void
gst_srt_src_init (GstSRTSrc * self)
{
self->srtobject = gst_srt_object_new (GST_ELEMENT (self));
self->cancellable = g_cancellable_new ();
gst_base_src_set_format (GST_BASE_SRC (self), GST_FORMAT_TIME);
gst_base_src_set_live (GST_BASE_SRC (self), TRUE);
gst_base_src_set_do_timestamp (GST_BASE_SRC (self), TRUE);
gst_srt_object_set_uri (self->srtobject, GST_SRT_DEFAULT_URI, NULL);
}
static void
gst_srt_src_finalize (GObject * object)
{
GstSRTSrc *self = GST_SRT_SRC (object);
g_clear_object (&self->cancellable);
gst_srt_object_destroy (self->srtobject);
G_OBJECT_CLASS (parent_class)->finalize (object);
}
static gboolean
gst_srt_src_unlock (GstBaseSrc * bsrc)
{
GstSRTSrc *self = GST_SRT_SRC (bsrc);
g_cancellable_cancel (self->cancellable);
gst_srt_object_wakeup (self->srtobject);
return TRUE;
}
static gboolean
gst_srt_src_unlock_stop (GstBaseSrc * bsrc)
{
GstSRTSrc *self = GST_SRT_SRC (bsrc);
g_cancellable_reset (self->cancellable);
return TRUE;
}
static void
gst_srt_src_set_property (GObject * object,
guint prop_id, const GValue * value, GParamSpec * pspec)
{
GstSRTSrc *self = GST_SRT_SRC (object);
if (!gst_srt_object_set_property_helper (self->srtobject, prop_id, value,
pspec)) {
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
}
static void
gst_srt_src_get_property (GObject * object,
guint prop_id, GValue * value, GParamSpec * pspec)
{
GstSRTSrc *self = GST_SRT_SRC (object);
if (!gst_srt_object_get_property_helper (self->srtobject, prop_id, value,
pspec)) {
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
}
static void
gst_srt_src_class_init (GstSRTSrcClass * 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_src_set_property;
gobject_class->get_property = gst_srt_src_get_property;
gobject_class->finalize = gst_srt_src_finalize;
/**
* GstSRTSrc::caller-added:
* @gstsrtsink: the srtsink element that emitted this signal
* @sock: the client socket descriptor that was added to srtsink
* @addr: the #GSocketAddress that describes the @sock
*
* The given socket descriptor was added to srtsink.
*/
signals[SIG_CALLER_ADDED] =
g_signal_new ("caller-added", G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstSRTSrcClass, caller_added),
NULL, NULL, g_cclosure_marshal_generic, G_TYPE_NONE,
2, G_TYPE_INT, G_TYPE_SOCKET_ADDRESS);
/**
* GstSRTSrc::caller-removed:
* @gstsrtsink: the srtsink element that emitted this signal
* @sock: the client socket descriptor that was added to srtsink
* @addr: the #GSocketAddress that describes the @sock
*
* The given socket descriptor was removed from srtsink.
*/
signals[SIG_CALLER_REMOVED] =
g_signal_new ("caller-removed", G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstSRTSrcClass,
caller_added), NULL, NULL, g_cclosure_marshal_generic, G_TYPE_NONE,
2, G_TYPE_INT, G_TYPE_SOCKET_ADDRESS);
gst_srt_object_install_properties_helper (gobject_class);
gst_element_class_add_static_pad_template (gstelement_class, &src_template);
gst_element_class_set_metadata (gstelement_class,
"SRT source", "Source/Network",
"Receive data over the network via SRT",
"Justin Kim <justin.joy.9to5@gmail.com>");
gstbasesrc_class->start = GST_DEBUG_FUNCPTR (gst_srt_src_start);
gstbasesrc_class->stop = GST_DEBUG_FUNCPTR (gst_srt_src_stop);
gstbasesrc_class->unlock = GST_DEBUG_FUNCPTR (gst_srt_src_unlock);
gstbasesrc_class->unlock_stop = GST_DEBUG_FUNCPTR (gst_srt_src_unlock_stop);
gstpushsrc_class->fill = GST_DEBUG_FUNCPTR (gst_srt_src_fill);
}
static GstURIType
gst_srt_src_uri_get_type (GType type)
{
return GST_URI_SRC;
}
static const gchar *const *
gst_srt_src_uri_get_protocols (GType type)
{
static const gchar *protocols[] = { GST_SRT_DEFAULT_URI_SCHEME, NULL };
return protocols;
}
static gchar *
gst_srt_src_uri_get_uri (GstURIHandler * handler)
{
gchar *uri_str;
GstSRTSrc *self = GST_SRT_SRC (handler);
GST_OBJECT_LOCK (self);
uri_str = gst_uri_to_string (self->srtobject->uri);
GST_OBJECT_UNLOCK (self);
return uri_str;
}
static gboolean
gst_srt_src_uri_set_uri (GstURIHandler * handler,
const gchar * uri, GError ** error)
{
GstSRTSrc *self = GST_SRT_SRC (handler);
return gst_srt_object_set_uri (self->srtobject, uri, error);
}
static void
gst_srt_src_uri_handler_init (gpointer g_iface, gpointer iface_data)
{
GstURIHandlerInterface *iface = (GstURIHandlerInterface *) g_iface;
iface->get_type = gst_srt_src_uri_get_type;
iface->get_protocols = gst_srt_src_uri_get_protocols;
iface->get_uri = gst_srt_src_uri_get_uri;
iface->set_uri = gst_srt_src_uri_set_uri;
}

65
ext/srt/gstsrtsrc.h Normal file
View file

@ -0,0 +1,65 @@
/* GStreamer
* Copyright (C) 2018, Collabora Ltd.
* Copyright (C) 2018, SK Telecom, Co., Ltd.
* Author: Jeongseok Kim <jeongseok.kim@sk.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_SRC_H__
#define __GST_SRT_SRC_H__
#include <gio/gio.h>
#include <gst/gst.h>
#include <gst/base/gstpushsrc.h>
#include "gstsrtobject.h"
G_BEGIN_DECLS
#define GST_TYPE_SRT_SRC (gst_srt_src_get_type ())
#define GST_IS_SRT_SRC(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_SRT_SRC))
#define GST_IS_SRT_SRC_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GST_TYPE_SRT_SRC))
#define GST_SRT_SRC_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_SRT_SRC, GstSRTSrcClass))
#define GST_SRT_SRC(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_SRT_SRC, GstSRTSrc))
#define GST_SRT_SRC_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GST_TYPE_SRT_SRC, GstSRTSrcClass))
#define GST_SRT_SRC_CAST(obj) ((GstSRTSrc*)(obj))
#define GST_SRT_SRC_CLASS_CAST(klass) ((GstSRTSrcClass*)(klass))
typedef struct _GstSRTSrc GstSRTSrc;
typedef struct _GstSRTSrcClass GstSRTSrcClass;
struct _GstSRTSrc {
GstPushSrc parent;
GstCaps *caps;
GstSRTObject *srtobject;
GCancellable *cancellable;
};
struct _GstSRTSrcClass {
GstPushSrcClass parent_class;
void (*caller_added) (GstSRTSrc *self, int sock, GSocketAddress * addr);
void (*caller_removed) (GstSRTSrc *self, int sock, GSocketAddress * addr);
};
GType gst_srt_src_get_type (void);
G_END_DECLS
#endif // __GST_SRT_SRC_H__

View file

@ -1,11 +1,8 @@
srt_sources = [ srt_sources = [
'gstsrt.c', 'gstsrt.c',
'gstsrtbasesrc.c', 'gstsrtobject.c',
'gstsrtclientsrc.c', 'gstsrtsink.c',
'gstsrtserversrc.c', 'gstsrtsrc.c'
'gstsrtbasesink.c',
'gstsrtclientsink.c',
'gstsrtserversink.c',
] ]
srt_option = get_option('srt') srt_option = get_option('srt')
if srt_option.disabled() if srt_option.disabled()
@ -21,8 +18,13 @@ if not srt_dep.found() and srt_option.enabled()
endif endif
if srt_dep.found() if srt_dep.found()
gstsrt_enums = gnome.mkenums_simple('gstsrt-enumtypes',
sources: ['gstsrt-enums.h'],
decorator : 'G_GNUC_INTERNAL',
install_header: false)
gstsrt = library('gstsrt', gstsrt = library('gstsrt',
srt_sources, srt_sources, gstsrt_enums,
c_args : gst_plugins_bad_args, c_args : gst_plugins_bad_args,
link_args : noseh_link_args, link_args : noseh_link_args,
include_directories : [configinc, libsinc], include_directories : [configinc, libsinc],