mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-11-30 05:31:15 +00:00
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:
parent
d7ad665d1c
commit
0a350c610d
23 changed files with 2550 additions and 3071 deletions
|
@ -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
67
ext/srt/gstsrt-enums.h
Normal 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__
|
320
ext/srt/gstsrt.c
320
ext/srt/gstsrt.c
|
@ -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;
|
||||||
|
|
|
@ -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__ */
|
|
|
@ -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;
|
|
||||||
}
|
|
|
@ -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__ */
|
|
|
@ -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;
|
|
||||||
}
|
|
|
@ -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__ */
|
|
|
@ -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;
|
|
||||||
}
|
|
|
@ -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__ */
|
|
|
@ -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;
|
|
||||||
}
|
|
|
@ -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
1441
ext/srt/gstsrtobject.c
Normal file
File diff suppressed because it is too large
Load diff
127
ext/srt/gstsrtobject.h
Normal file
127
ext/srt/gstsrtobject.h
Normal 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__
|
|
@ -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;
|
|
||||||
}
|
|
|
@ -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__ */
|
|
|
@ -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;
|
|
||||||
}
|
|
|
@ -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
391
ext/srt/gstsrtsink.c
Normal 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
65
ext/srt/gstsrtsink.h
Normal 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
360
ext/srt/gstsrtsrc.c
Normal 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
65
ext/srt/gstsrtsrc.h
Normal 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__
|
|
@ -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],
|
||||||
|
|
Loading…
Reference in a new issue