mirror of
https://gitlab.freedesktop.org/pipewire/pipewire.git
synced 2025-11-03 09:01:54 -05:00
src -> pinos and fix include paths
Rename src to pinos and fix all the include paths so that they contain pinos/ to avoid conflicts
This commit is contained in:
parent
f4bd013dce
commit
cdb2028f9b
57 changed files with 77 additions and 104 deletions
3
src/.gitignore
vendored
3
src/.gitignore
vendored
|
|
@ -1,3 +0,0 @@
|
|||
pinos
|
||||
test-client
|
||||
pinos-monitor
|
||||
268
src/Makefile.am
268
src/Makefile.am
|
|
@ -1,268 +0,0 @@
|
|||
# This file is part of Pinos.
|
||||
#
|
||||
# Copyright 2015 Wim Taymans <wim.taymans@gmail.com>
|
||||
#
|
||||
# Pinos is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Lesser General Public License as published by
|
||||
# the Free Software Foundation; either version 2 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# Pinos 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
|
||||
# General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser General Public License
|
||||
# along with Pinos; if not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
###################################
|
||||
# Extra directories #
|
||||
###################################
|
||||
|
||||
pinosincludedir=$(includedir)/pinos/client
|
||||
pinosserverincludedir=$(includedir)/pinos/server
|
||||
pinoslibexecdir=$(libexecdir)/pinos/client
|
||||
xdgautostartdir=$(sysconfdir)/xdg/autostart
|
||||
dbuspolicydir=$(sysconfdir)/dbus-1/system.d
|
||||
|
||||
###################################
|
||||
# Compiler/linker flags #
|
||||
###################################
|
||||
|
||||
AM_CPPFLAGS = \
|
||||
-I$(top_srcdir)/src \
|
||||
-I$(top_srcdir)/src/modules \
|
||||
-I$(top_builddir)/src/modules \
|
||||
-DPINOS_SRCDIR=\"$(abs_srcdir)\" \
|
||||
-DPINOS_BUILDDIR=\"$(abs_builddir)\"
|
||||
AM_CFLAGS = $(GLIB_CFLAGS) $(GST_CFLAGS)
|
||||
AM_CXXFLAGS = $(AM_CFLAGS)
|
||||
SERVER_CFLAGS = -D__INCLUDED_FROM_PINOS
|
||||
|
||||
AM_LIBADD = $(GLIB_LIBS) $(INTLLIBS) $(GST_LIBS)
|
||||
AM_LDADD = $(GLIB_LIBS) $(GST_LIBS) $(INTLLIBS)
|
||||
AM_LDFLAGS = $(NODELETE_LDFLAGS)
|
||||
|
||||
FOREIGN_CFLAGS = -w
|
||||
|
||||
###################################
|
||||
# Extra files #
|
||||
###################################
|
||||
|
||||
EXTRA_DIST = \
|
||||
daemon/pinos-system.conf \
|
||||
daemon/pinos.desktop.in \
|
||||
dbus/org.pinos.xml \
|
||||
client/private.h
|
||||
|
||||
dbuspolicy_DATA = \
|
||||
daemon/pinos-system.conf
|
||||
|
||||
xdgautostart_in_files = \
|
||||
daemon/pinos.desktop.in
|
||||
xdgautostart_DATA = $(xdgautostart_in_files:.desktop.in=.desktop)
|
||||
@INTLTOOL_DESKTOP_RULE@
|
||||
|
||||
###################################
|
||||
# Includes #
|
||||
###################################
|
||||
|
||||
enumtypesincludes = client/context.h \
|
||||
client/introspect.h \
|
||||
client/stream.h \
|
||||
client/subscribe.h
|
||||
|
||||
client/enumtypes.h: $(enumtypesincludes)
|
||||
$(AM_V_GEN)$(GLIB_MKENUMS) \
|
||||
--fhead "#ifndef __PINOS_ENUM_TYPES_H__\n#define __PINOS_ENUM_TYPES_H__\n\n#include <glib-object.h>\n\nG_BEGIN_DECLS\n" \
|
||||
--fprod "\n/* enumerations from \"@filename@\" */\n" \
|
||||
--vhead "GType @enum_name@_get_type (void);\n#define PINOS_TYPE_@ENUMSHORT@ (@enum_name@_get_type())\n" \
|
||||
--ftail "G_END_DECLS\n\n#endif /* __PINOS_ENUM_TYPES_H__ */" \
|
||||
$^ > client/enumtypes.h
|
||||
|
||||
client/enumtypes.c: $(enumtypesincludes) client/enumtypes.h
|
||||
$(AM_V_GEN)$(GLIB_MKENUMS) \
|
||||
--fhead "#include \"enumtypes.h\"\n#include <client/pinos.h>\n#define C_ENUM(v) ((gint) v)\n#define C_FLAGS(v) ((guint) v)\n " \
|
||||
--fprod "\n/* enumerations from \"@filename@\" */" \
|
||||
--vhead "GType\n@enum_name@_get_type (void)\n{\n static gsize id = 0;\n static const G@Type@Value values[] = {" \
|
||||
--vprod " { C_@TYPE@(@VALUENAME@), \"@VALUENAME@\", \"@valuenick@\" }," \
|
||||
--vtail " { 0, NULL, NULL }\n };\n\n if (g_once_init_enter (&id)) {\n GType tmp = g_@type@_register_static (\"@EnumName@\", values);\n g_once_init_leave (&id, tmp);\n }\n\n return (GType) id;\n}" \
|
||||
$^ > client/enumtypes.c
|
||||
|
||||
|
||||
dbus/org-pinos.c: dbus/org-pinos.h
|
||||
dbus/org-pinos.h: dbus/org.pinos.xml
|
||||
$(AM_V_GEN) $(GDBUS_CODEGEN) \
|
||||
--interface-prefix org.pinos. \
|
||||
--generate-c-code dbus/org-pinos \
|
||||
--generate-docbook ../doc/org-pinos \
|
||||
--c-namespace Pinos dbus/org.pinos.xml \
|
||||
--c-generate-object-manager
|
||||
|
||||
|
||||
built_header_make = client/enumtypes.h dbus/org-pinos.h
|
||||
built_source_make = client/enumtypes.c dbus/org-pinos.c
|
||||
|
||||
BUILT_SOURCES = $(built_header_make) \
|
||||
$(built_source_make)
|
||||
|
||||
CLEANFILES = $(built_header_make) $(built_source_make)
|
||||
|
||||
###################################
|
||||
# Main daemon #
|
||||
###################################
|
||||
|
||||
bin_PROGRAMS = pinos
|
||||
|
||||
pinos_SOURCES = \
|
||||
daemon/main.c
|
||||
|
||||
pinos_CFLAGS = $(AM_CFLAGS)
|
||||
pinos_LDADD = $(AM_LDADD) libpinoscore-@PINOS_MAJORMINOR@.la libpinos-@PINOS_MAJORMINOR@.la $(LIBLTDL)
|
||||
# This is needed because automake doesn't properly expand the foreach below
|
||||
pinos_DEPENDENCIES = libpinoscore-@PINOS_MAJORMINOR@.la libpinos-@PINOS_MAJORMINOR@.la
|
||||
pinos_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS) $(IMMEDIATE_LDFLAGS)
|
||||
|
||||
###################################
|
||||
# Test programs #
|
||||
###################################
|
||||
noinst_LTLIBRARIES =
|
||||
|
||||
TESTS_default =
|
||||
|
||||
TESTS_norun = test-client
|
||||
|
||||
# These tests need a running pinos daemon
|
||||
TESTS_daemon =
|
||||
|
||||
noinst_PROGRAMS = $(TESTS_default) $(TESTS_norun) $(TESTS_daemon)
|
||||
|
||||
test_client_SOURCES = tests/test-client.c
|
||||
test_client_CFLAGS = $(AM_CFLAGS)
|
||||
test_client_LDADD = $(AM_LDADD) libpinos-@PINOS_MAJORMINOR@.la
|
||||
test_client_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS)
|
||||
|
||||
###################################
|
||||
# Tools programs #
|
||||
###################################
|
||||
|
||||
bin_PROGRAMS += \
|
||||
pinos-monitor
|
||||
|
||||
pinos_monitor_SOURCES = tools/pinos-monitor.c
|
||||
pinos_monitor_CFLAGS = $(AM_CFLAGS)
|
||||
pinos_monitor_LDADD = $(AM_LDADD) libpinos-@PINOS_MAJORMINOR@.la
|
||||
pinos_monitor_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS)
|
||||
|
||||
###################################
|
||||
# Client library #
|
||||
###################################
|
||||
|
||||
pinosgstsource = gst/gstpinospay.h gst/gstpinospay.c \
|
||||
gst/gstpinosdepay.h gst/gstpinosdepay.c \
|
||||
gst/gsttmpfileallocator.h gst/gsttmpfileallocator.c
|
||||
|
||||
pinosinclude_HEADERS = \
|
||||
client/pinos.h \
|
||||
client/buffer.h \
|
||||
client/context.h \
|
||||
client/enumtypes.h \
|
||||
client/introspect.h \
|
||||
client/mainloop.h \
|
||||
client/properties.h \
|
||||
client/stream.h \
|
||||
client/subscribe.h
|
||||
|
||||
lib_LTLIBRARIES = \
|
||||
libpinos-@PINOS_MAJORMINOR@.la
|
||||
|
||||
# Public interface
|
||||
libpinos_@PINOS_MAJORMINOR@_la_SOURCES = \
|
||||
client/buffer.h client/buffer.c \
|
||||
client/context.h client/context.c \
|
||||
client/enumtypes.h client/enumtypes.c \
|
||||
client/introspect.h client/introspect.c \
|
||||
client/mainloop.h client/mainloop.c \
|
||||
client/properties.h client/properties.c \
|
||||
client/stream.h client/stream.c \
|
||||
client/pinos.c client/pinos.h \
|
||||
client/subscribe.c client/subscribe.h \
|
||||
$(pinosgstsource)
|
||||
|
||||
|
||||
libpinos_@PINOS_MAJORMINOR@_la_CFLAGS = $(AM_CFLAGS) $(GST_CFLAGS)
|
||||
libpinos_@PINOS_MAJORMINOR@_la_LDFLAGS = $(AM_LDFLAGS) -avoid-version
|
||||
libpinos_@PINOS_MAJORMINOR@_la_LIBADD = $(AM_LIBADD) $(LTLIBICONV) $(GST_LIBS)
|
||||
|
||||
###################################
|
||||
# Daemon core library #
|
||||
###################################
|
||||
|
||||
lib_LTLIBRARIES += libpinoscore-@PINOS_MAJORMINOR@.la
|
||||
|
||||
# Pure core stuff
|
||||
libpinoscore_@PINOS_MAJORMINOR@_la_SOURCES = \
|
||||
server/client.c server/client.h \
|
||||
server/daemon.c server/daemon.h \
|
||||
server/source.c server/source.h \
|
||||
server/client-source.c server/client-source.h \
|
||||
server/source-output.c server/source-output.h \
|
||||
modules/gst/gst-manager.c modules/gst/gst-manager.h \
|
||||
modules/gst/gst-source.c modules/gst/gst-source.h \
|
||||
dbus/org-pinos.c dbus/org-pinos.h
|
||||
|
||||
libpinoscore_@PINOS_MAJORMINOR@_la_CFLAGS = $(AM_CFLAGS) $(SERVER_CFLAGS)
|
||||
libpinoscore_@PINOS_MAJORMINOR@_la_LDFLAGS = $(AM_LDFLAGS) -avoid-version
|
||||
libpinoscore_@PINOS_MAJORMINOR@_la_LIBADD = $(AM_LIBADD) $(LIBLTDL) $(LTLIBICONV) \
|
||||
libpinos-@PINOS_MAJORMINOR@.la
|
||||
|
||||
###################################
|
||||
# GStreamer Plugin #
|
||||
###################################
|
||||
|
||||
plugindir = $(libdir)/gstreamer-1.0
|
||||
|
||||
plugin_LTLIBRARIES = libgstpinos.la
|
||||
|
||||
libgstpinos_la_SOURCES = \
|
||||
gst/gstpinos.c \
|
||||
gst/gstpinospay.c \
|
||||
gst/gstpinosdepay.c \
|
||||
gst/gstpinosdeviceprovider.c \
|
||||
gst/gstpinossrc.c \
|
||||
gst/gstpinossink.c
|
||||
|
||||
libgstpinos_la_CFLAGS = $(GST_PLUGINS_BASE_CFLAGS) $(GST_BASE_CFLAGS) $(GST_CFLAGS) $(GLIB_CFLAGS)
|
||||
libgstpinos_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS)
|
||||
libgstpinos_la_LIBADD = $(GST_BASE_LIBS) $(GST_LIBS) $(GLIB_LIBS) $(LIBM) -lgstvideo-1.0 \
|
||||
libpinos-@PINOS_MAJORMINOR@.la libpinoscore-@PINOS_MAJORMINOR@.la
|
||||
libgstpinos_la_LIBTOOLFLAGS = $(GST_PLUGIN_LIBTOOLFLAGS)
|
||||
|
||||
noinst_HEADERS = gst/gstpinossrc.h gst/gstpinossink.h gst/gstpinospay.h \
|
||||
gst/gstpinosdepay.h gst/gstpinosdeviceprovider.h
|
||||
|
||||
###################################
|
||||
# Some minor stuff #
|
||||
###################################
|
||||
|
||||
CLEANFILES += daemon/pinos.desktop
|
||||
DISTCLEANFILES =
|
||||
|
||||
install-exec-hook:
|
||||
rm -f $(DESTDIR)$(modlibexecdir)/*.la
|
||||
|
||||
uninstall-hook:
|
||||
rm -f $(DESTDIR)$(modlibexecdir)/*.so
|
||||
|
||||
# Automatically generate linker version script. We use the same one for all public .sos
|
||||
update-map-file:
|
||||
( echo "PINOS_0 {" ; \
|
||||
echo "global:" ; \
|
||||
ctags -I PV_GCC_MALLOC,PV_GCC_ALLOC_SIZE2,PV_GCC_ALLOC_SIZE,PV_GCC_PURE,PV_GCC_CONST,PV_GCC_DEPRECATED,PV_GCC_PRINTF_ATTR -f - --c-kinds=p $(pinosinclude_HEADERS) | awk '/^pa_/ { print $$1 ";" }' | sort ; \
|
||||
echo "local:" ; \
|
||||
echo "*;" ; \
|
||||
echo "};" ) > $(srcdir)/map-file
|
||||
|
||||
update-all: update-map-file
|
||||
|
||||
.PHONY: update-all update-map-file coverage
|
||||
2
src/client/.gitignore
vendored
2
src/client/.gitignore
vendored
|
|
@ -1,2 +0,0 @@
|
|||
enumtypes.c
|
||||
enumtypes.h
|
||||
|
|
@ -1,644 +0,0 @@
|
|||
/* Pinos
|
||||
* Copyright (C) 2015 Wim Taymans <wim.taymans@gmail.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include <sys/socket.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <gio/gio.h>
|
||||
#include <gio/gunixfdmessage.h>
|
||||
|
||||
#include "client/properties.h"
|
||||
#include "client/context.h"
|
||||
#include "client/buffer.h"
|
||||
#include "client/private.h"
|
||||
|
||||
G_STATIC_ASSERT (sizeof (PinosStackBuffer) <= sizeof (PinosBuffer));
|
||||
|
||||
/**
|
||||
* pinos_buffer_init_data:
|
||||
* @buffer: a #PinosBuffer
|
||||
* @data: data
|
||||
* @size: size of @data
|
||||
* @message: (transfer full): a #GSocketControlMessage
|
||||
*
|
||||
* Initialize @buffer with @data and @size and @message. @data and size
|
||||
* must not be modified until pinos_buffer_clear() is called.
|
||||
*
|
||||
* Ownership is taken of @message.
|
||||
*/
|
||||
void
|
||||
pinos_buffer_init_data (PinosBuffer *buffer,
|
||||
gpointer data,
|
||||
gsize size,
|
||||
GSocketControlMessage *message)
|
||||
{
|
||||
PinosStackBuffer *sb = PSB (buffer);
|
||||
|
||||
sb->magic = PSB_MAGIC;
|
||||
sb->data = data;
|
||||
sb->size = size;
|
||||
sb->allocated_size = 0;
|
||||
sb->message = message;
|
||||
}
|
||||
|
||||
void
|
||||
pinos_buffer_clear (PinosBuffer *buffer)
|
||||
{
|
||||
PinosStackBuffer *sb = PSB (buffer);
|
||||
|
||||
g_return_if_fail (is_valid_buffer (buffer));
|
||||
|
||||
sb->magic = 0;
|
||||
if (sb->allocated_size)
|
||||
g_free (sb->data);
|
||||
sb->size = 0;
|
||||
sb->allocated_size = 0;
|
||||
g_clear_object (&sb->message);
|
||||
}
|
||||
|
||||
/**
|
||||
* pinos_buffer_get_version
|
||||
* @buffer: a #PinosBuffer
|
||||
*
|
||||
* Get the buffer version
|
||||
*
|
||||
* Returns: the buffer version.
|
||||
*/
|
||||
guint32
|
||||
pinos_buffer_get_version (PinosBuffer *buffer)
|
||||
{
|
||||
PinosStackBuffer *sb = PSB (buffer);
|
||||
PinosStackHeader *hdr;
|
||||
|
||||
g_return_val_if_fail (is_valid_buffer (buffer), -1);
|
||||
|
||||
hdr = sb->data;
|
||||
|
||||
return hdr->version;
|
||||
}
|
||||
|
||||
/**
|
||||
* pinos_buffer_get_fd:
|
||||
* @buffer: a #PinosBuffer
|
||||
* @index: an index
|
||||
* @error: a #GError or %NULL
|
||||
*
|
||||
* Get the file descriptor at @index in @buffer.
|
||||
*
|
||||
* Returns: a file descriptor ar @index in @buffer. The file descriptor is
|
||||
* duplicated using dup() and set as close-on-exec before being returned.
|
||||
* You must call close() on it when you are done. -1 is returned on error and
|
||||
* @error is set.
|
||||
*/
|
||||
int
|
||||
pinos_buffer_get_fd (PinosBuffer *buffer, gint index, GError **error)
|
||||
{
|
||||
PinosStackBuffer *sb = PSB (buffer);
|
||||
GUnixFDList *fds;
|
||||
|
||||
g_return_val_if_fail (is_valid_buffer (buffer), -1);
|
||||
g_return_val_if_fail (sb->message != NULL, -1);
|
||||
|
||||
if (g_socket_control_message_get_msg_type (sb->message) != SCM_RIGHTS)
|
||||
goto not_found;
|
||||
|
||||
fds = g_unix_fd_message_get_fd_list (G_UNIX_FD_MESSAGE (sb->message));
|
||||
if (fds == NULL)
|
||||
goto not_found;
|
||||
|
||||
if (g_unix_fd_list_get_length (fds) <= index)
|
||||
goto not_found;
|
||||
|
||||
return g_unix_fd_list_get (fds, index, error);
|
||||
|
||||
/* ERRORS */
|
||||
not_found:
|
||||
{
|
||||
if (error)
|
||||
*error = g_error_new (G_IO_ERROR,
|
||||
G_IO_ERROR_NOT_FOUND,
|
||||
"Buffer does not have any fd at index %d", index);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* pinos_buffer_steal:
|
||||
* @buffer: a #PinosBuffer
|
||||
* @size: output size
|
||||
* @message: output #GSocketControlMessage
|
||||
*
|
||||
* Take the data and control message from @buffer.
|
||||
*
|
||||
* Returns: the data of @buffer.
|
||||
*/
|
||||
gpointer
|
||||
pinos_buffer_steal (PinosBuffer *buffer,
|
||||
gsize *size,
|
||||
GSocketControlMessage **message)
|
||||
{
|
||||
PinosStackBuffer *sb = PSB (buffer);
|
||||
gpointer data;
|
||||
|
||||
g_return_val_if_fail (is_valid_buffer (buffer), 0);
|
||||
|
||||
if (size)
|
||||
*size = sb->size;
|
||||
if (message)
|
||||
*message = sb->message;
|
||||
|
||||
data = sb->data;
|
||||
|
||||
sb->magic = 0;
|
||||
sb->data = NULL;
|
||||
sb->size = 0;
|
||||
sb->allocated_size = 0;
|
||||
sb->message = NULL;
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
/**
|
||||
* PinosBufferIter:
|
||||
*
|
||||
* #PinosBufferIter is an opaque data structure and can only be accessed
|
||||
* using the following functions.
|
||||
*/
|
||||
struct stack_iter {
|
||||
gsize magic;
|
||||
guint32 version;
|
||||
PinosStackBuffer *buffer;
|
||||
gsize offset;
|
||||
|
||||
PinosPacketType type;
|
||||
gsize size;
|
||||
gpointer data;
|
||||
|
||||
guint item;
|
||||
};
|
||||
|
||||
G_STATIC_ASSERT (sizeof (struct stack_iter) <= sizeof (PinosBufferIter));
|
||||
|
||||
#define PPSI(i) ((struct stack_iter *) (i))
|
||||
#define PPSI_MAGIC ((gsize) 6739527471u)
|
||||
#define is_valid_iter(i) (i != NULL && \
|
||||
PPSI(i)->magic == PPSI_MAGIC)
|
||||
|
||||
/**
|
||||
* pinos_buffer_iter_init:
|
||||
* @iter: a #PinosBufferIter
|
||||
* @buffer: a #PinosBuffer
|
||||
*
|
||||
* Initialize @iter to iterate the packets in @buffer.
|
||||
*/
|
||||
void
|
||||
pinos_buffer_iter_init_full (PinosBufferIter *iter,
|
||||
PinosBuffer *buffer,
|
||||
guint32 version)
|
||||
{
|
||||
struct stack_iter *si = PPSI (iter);
|
||||
|
||||
g_return_if_fail (iter != NULL);
|
||||
g_return_if_fail (is_valid_buffer (buffer));
|
||||
|
||||
si->magic = PPSI_MAGIC;
|
||||
si->version = version;
|
||||
si->buffer = PSB (buffer);
|
||||
si->offset = 0;
|
||||
si->type = PINOS_PACKET_TYPE_INVALID;
|
||||
si->size = sizeof (PinosStackHeader);
|
||||
si->data = NULL;
|
||||
si->item = 0;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
read_length (guint8 * data, guint size, gsize * length, gsize * skip)
|
||||
{
|
||||
gsize len, offset;
|
||||
guint8 b;
|
||||
|
||||
/* start reading the length, we need this to skip to the data later */
|
||||
len = offset = 0;
|
||||
do {
|
||||
if (offset >= size)
|
||||
return FALSE;
|
||||
b = data[offset++];
|
||||
len = (len << 7) | (b & 0x7f);
|
||||
} while (b & 0x80);
|
||||
|
||||
/* check remaining buffer size */
|
||||
if (size - offset < len)
|
||||
return FALSE;
|
||||
|
||||
*length = len;
|
||||
*skip = offset;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* pinos_buffer_iter_next:
|
||||
* @iter: a #PinosBufferIter
|
||||
*
|
||||
* Move to the next packet in @iter.
|
||||
*
|
||||
* Returns: %TRUE if more packets are available.
|
||||
*/
|
||||
gboolean
|
||||
pinos_buffer_iter_next (PinosBufferIter *iter)
|
||||
{
|
||||
struct stack_iter *si = PPSI (iter);
|
||||
gsize len, size, skip;
|
||||
guint8 *data;
|
||||
|
||||
g_return_val_if_fail (is_valid_iter (iter), FALSE);
|
||||
|
||||
/* move to next packet */
|
||||
si->offset += si->size;
|
||||
|
||||
/* now read packet */
|
||||
data = si->buffer->data;
|
||||
size = si->buffer->size;
|
||||
if (si->offset >= size)
|
||||
return FALSE;
|
||||
|
||||
data += si->offset;
|
||||
size -= si->offset;
|
||||
|
||||
if (size < 1)
|
||||
return FALSE;
|
||||
|
||||
si->type = *data;
|
||||
|
||||
data++;
|
||||
size--;
|
||||
|
||||
if (!read_length (data, size, &len, &skip))
|
||||
return FALSE;
|
||||
|
||||
si->size = len;
|
||||
si->data = data + skip;
|
||||
si->offset += 1 + skip;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
PinosPacketType
|
||||
pinos_buffer_iter_get_type (PinosBufferIter *iter)
|
||||
{
|
||||
struct stack_iter *si = PPSI (iter);
|
||||
|
||||
g_return_val_if_fail (is_valid_iter (iter), PINOS_PACKET_TYPE_INVALID);
|
||||
|
||||
return si->type;
|
||||
}
|
||||
|
||||
gpointer
|
||||
pinos_buffer_iter_get_data (PinosBufferIter *iter, gsize *size)
|
||||
{
|
||||
struct stack_iter *si = PPSI (iter);
|
||||
|
||||
g_return_val_if_fail (is_valid_iter (iter), NULL);
|
||||
|
||||
if (size)
|
||||
*size = si->size;
|
||||
|
||||
return si->data;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* PinosBufferBuilder:
|
||||
* @buffer: owner #PinosBuffer
|
||||
*/
|
||||
struct stack_builder {
|
||||
gsize magic;
|
||||
|
||||
PinosStackHeader *sh;
|
||||
PinosStackBuffer buf;
|
||||
|
||||
PinosPacketType type;
|
||||
gsize offset;
|
||||
|
||||
guint n_fds;
|
||||
};
|
||||
|
||||
G_STATIC_ASSERT (sizeof (struct stack_builder) <= sizeof (PinosBufferBuilder));
|
||||
|
||||
#define PPSB(b) ((struct stack_builder *) (b))
|
||||
#define PPSB_MAGIC ((gsize) 8103647428u)
|
||||
#define is_valid_builder(b) (b != NULL && \
|
||||
PPSB(b)->magic == PPSB_MAGIC)
|
||||
|
||||
|
||||
/**
|
||||
* pinos_buffer_builder_init_full:
|
||||
* @builder: a #PinosBufferBuilder
|
||||
* @version: a version
|
||||
*
|
||||
* Initialize a stack allocated @builder and set the @version.
|
||||
*/
|
||||
void
|
||||
pinos_buffer_builder_init_full (PinosBufferBuilder *builder,
|
||||
guint32 version)
|
||||
{
|
||||
struct stack_builder *sb = PPSB (builder);
|
||||
PinosStackHeader *sh;
|
||||
|
||||
g_return_if_fail (builder != NULL);
|
||||
|
||||
sb->magic = PPSB_MAGIC;
|
||||
sb->buf.allocated_size = sizeof (PinosStackHeader) + 128;
|
||||
sb->buf.data = g_malloc (sb->buf.allocated_size);
|
||||
sb->buf.size = sizeof (PinosStackHeader);
|
||||
sb->buf.message = NULL;
|
||||
|
||||
sh = sb->sh = sb->buf.data;
|
||||
sh->version = version;
|
||||
sh->length = 0;
|
||||
|
||||
sb->type = 0;
|
||||
sb->offset = 0;
|
||||
sb->n_fds = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* pinos_buffer_builder_clear:
|
||||
* @builder: a #PinosBufferBuilder
|
||||
*
|
||||
* Clear the memory used by @builder. This can be used to abort building the
|
||||
* buffer.
|
||||
*
|
||||
* @builder becomes invalid after this function and can be reused with
|
||||
* pinos_buffer_builder_init()
|
||||
*/
|
||||
void
|
||||
pinos_buffer_builder_clear (PinosBufferBuilder *builder)
|
||||
{
|
||||
struct stack_builder *sb = PPSB (builder);
|
||||
|
||||
g_return_if_fail (is_valid_builder (builder));
|
||||
|
||||
sb->magic = 0;
|
||||
g_free (sb->buf.data);
|
||||
}
|
||||
|
||||
/**
|
||||
* pinos_buffer_builder_end:
|
||||
* @builder: a #PinosBufferBuilder
|
||||
* @buffer: a #PinosBuffer
|
||||
*
|
||||
* Ends the building process and fills @buffer with the constructed
|
||||
* #PinosBuffer.
|
||||
*
|
||||
* @builder becomes invalid after this function and can be reused with
|
||||
* pinos_buffer_builder_init()
|
||||
*/
|
||||
void
|
||||
pinos_buffer_builder_end (PinosBufferBuilder *builder,
|
||||
PinosBuffer *buffer)
|
||||
{
|
||||
struct stack_builder *sb = PPSB (builder);
|
||||
PinosStackBuffer *sbuf = PSB (buffer);
|
||||
|
||||
g_return_if_fail (is_valid_builder (builder));
|
||||
g_return_if_fail (buffer != NULL);
|
||||
|
||||
sb->sh->length = sb->buf.size - sizeof (PinosStackHeader);
|
||||
|
||||
sbuf->magic = PSB_MAGIC;
|
||||
sbuf->data = sb->buf.data;
|
||||
sbuf->size = sb->buf.size;
|
||||
sbuf->allocated_size = sb->buf.allocated_size;
|
||||
sbuf->message = sb->buf.message;
|
||||
|
||||
sb->buf.data = NULL;
|
||||
sb->buf.size = 0;
|
||||
sb->buf.allocated_size = 0;
|
||||
sb->buf.message = NULL;
|
||||
sb->magic = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* pinos_buffer_builder_add_fd:
|
||||
* @builder: a #PinosBufferBuilder
|
||||
* @fd: a valid fd
|
||||
* @error: a #GError or %NULL
|
||||
*
|
||||
* Add the file descriptor @fd to @builder.
|
||||
*
|
||||
* Returns: the index of the file descriptor in @builder. The file descriptor
|
||||
* is duplicated using dup(). You keep your copy of the descriptor and the copy
|
||||
* contained in @buffer will be closed when @buffer is freed.
|
||||
* -1 is returned on error and @error is set.
|
||||
*/
|
||||
gint
|
||||
pinos_buffer_builder_add_fd (PinosBufferBuilder *builder,
|
||||
int fd,
|
||||
GError **error)
|
||||
{
|
||||
struct stack_builder *sb = PPSB (builder);
|
||||
|
||||
g_return_val_if_fail (is_valid_builder (builder), -1);
|
||||
g_return_val_if_fail (fd > 0, -1);
|
||||
|
||||
if (sb->buf.message == NULL)
|
||||
sb->buf.message = g_unix_fd_message_new ();
|
||||
|
||||
if (!g_unix_fd_message_append_fd ((GUnixFDMessage*)sb->buf.message, fd, error))
|
||||
return -1;
|
||||
|
||||
return sb->n_fds++;
|
||||
}
|
||||
|
||||
static gpointer
|
||||
builder_ensure_size (struct stack_builder *sb, gsize size)
|
||||
{
|
||||
if (sb->buf.size + size > sb->buf.allocated_size) {
|
||||
sb->buf.allocated_size = sb->buf.size + MAX (size, 1024);
|
||||
sb->buf.data = g_realloc (sb->buf.data, sb->buf.allocated_size);
|
||||
}
|
||||
return (guint8 *) sb->buf.data + sb->buf.size;
|
||||
}
|
||||
|
||||
static gpointer
|
||||
builder_add_packet (struct stack_builder *sb, PinosPacketType type, gsize size)
|
||||
{
|
||||
guint8 *p;
|
||||
guint plen;
|
||||
|
||||
plen = 1;
|
||||
while (size >> (7 * plen))
|
||||
plen++;
|
||||
|
||||
/* 1 for type, plen for size and size for payload */
|
||||
p = builder_ensure_size (sb, 1 + plen + size);
|
||||
|
||||
sb->type = type;
|
||||
sb->offset = sb->buf.size;
|
||||
sb->buf.size += 1 + plen + size;
|
||||
|
||||
*p++ = type;
|
||||
/* write length */
|
||||
while (plen) {
|
||||
plen--;
|
||||
*p++ = ((plen > 0) ? 0x80 : 0) | ((size >> (7 * plen)) & 0x7f);
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
/* header packets */
|
||||
/**
|
||||
* pinos_buffer_iter_get_header:
|
||||
* @iter: a #PinosBufferIter
|
||||
* @header: a #PinosPacketHeader
|
||||
*
|
||||
* Get the #PinosPacketHeader. @iter must be positioned on a packet of
|
||||
* type #PINOS_PACKET_TYPE_HEADER
|
||||
*
|
||||
* Returns: %TRUE if @header contains valid data.
|
||||
*/
|
||||
gboolean
|
||||
pinos_buffer_iter_parse_header (PinosBufferIter *iter,
|
||||
PinosPacketHeader *header)
|
||||
{
|
||||
struct stack_iter *si = PPSI (iter);
|
||||
|
||||
g_return_val_if_fail (is_valid_iter (iter), FALSE);
|
||||
g_return_val_if_fail (si->type == PINOS_PACKET_TYPE_HEADER, FALSE);
|
||||
|
||||
if (si->size < sizeof (PinosPacketHeader))
|
||||
return FALSE;
|
||||
|
||||
*header = *((PinosPacketHeader *) si->data);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* pinos_buffer_builder_add_header:
|
||||
* @builder: a #PinosBufferBuilder
|
||||
* @header: a #PinosPacketHeader
|
||||
*
|
||||
* Add a #PINOS_PACKET_TYPE_HEADER to @builder with data from @header.
|
||||
*
|
||||
* Returns: %TRUE on success.
|
||||
*/
|
||||
gboolean
|
||||
pinos_buffer_builder_add_header (PinosBufferBuilder *builder,
|
||||
PinosPacketHeader *header)
|
||||
{
|
||||
struct stack_builder *sb = PPSB (builder);
|
||||
PinosPacketHeader *h;
|
||||
|
||||
g_return_val_if_fail (is_valid_builder (builder), FALSE);
|
||||
|
||||
h = builder_add_packet (sb, PINOS_PACKET_TYPE_HEADER, sizeof (PinosPacketHeader));
|
||||
*h = *header;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/* fd-payload packets */
|
||||
/**
|
||||
* pinos_buffer_iter_get_fd_payload:
|
||||
* @iter: a #PinosBufferIter
|
||||
* @payload: a #PinosPacketFDPayload
|
||||
*
|
||||
* Get the #PinosPacketFDPayload. @iter must be positioned on a packet of
|
||||
* type #PINOS_PACKET_TYPE_FD_PAYLOAD
|
||||
*
|
||||
* Returns: %TRUE if @payload contains valid data.
|
||||
*/
|
||||
gboolean
|
||||
pinos_buffer_iter_parse_fd_payload (PinosBufferIter *iter,
|
||||
PinosPacketFDPayload *payload)
|
||||
{
|
||||
struct stack_iter *si = PPSI (iter);
|
||||
|
||||
g_return_val_if_fail (is_valid_iter (iter), FALSE);
|
||||
g_return_val_if_fail (si->type == PINOS_PACKET_TYPE_FD_PAYLOAD, FALSE);
|
||||
|
||||
if (si->size < sizeof (PinosPacketFDPayload))
|
||||
return FALSE;
|
||||
|
||||
*payload = *((PinosPacketFDPayload *) si->data);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* pinos_buffer_builder_add_fd_payload:
|
||||
* @builder: a #PinosBufferBuilder
|
||||
* @payload: a #PinosPacketFDPayload
|
||||
*
|
||||
* Add a #PINOS_PACKET_TYPE_FD_PAYLOAD to @builder with data from @payload.
|
||||
*
|
||||
* Returns: %TRUE on success.
|
||||
*/
|
||||
gboolean
|
||||
pinos_buffer_builder_add_fd_payload (PinosBufferBuilder *builder,
|
||||
PinosPacketFDPayload *payload)
|
||||
{
|
||||
struct stack_builder *sb = PPSB (builder);
|
||||
PinosPacketFDPayload *p;
|
||||
|
||||
g_return_val_if_fail (is_valid_builder (builder), FALSE);
|
||||
g_return_val_if_fail (payload->size > 0, FALSE);
|
||||
|
||||
p = builder_add_packet (sb, PINOS_PACKET_TYPE_FD_PAYLOAD, sizeof (PinosPacketFDPayload));
|
||||
*p = *payload;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
gboolean
|
||||
pinos_buffer_iter_parse_release_fd_payload (PinosBufferIter *iter,
|
||||
PinosPacketReleaseFDPayload *payload)
|
||||
{
|
||||
struct stack_iter *si = PPSI (iter);
|
||||
|
||||
g_return_val_if_fail (is_valid_iter (iter), FALSE);
|
||||
g_return_val_if_fail (si->type == PINOS_PACKET_TYPE_RELEASE_FD_PAYLOAD, FALSE);
|
||||
|
||||
if (si->size < sizeof (PinosPacketReleaseFDPayload))
|
||||
return FALSE;
|
||||
|
||||
*payload = *((PinosPacketReleaseFDPayload *) si->data);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
gboolean
|
||||
pinos_buffer_builder_add_release_fd_payload (PinosBufferBuilder *builder,
|
||||
PinosPacketReleaseFDPayload *payload)
|
||||
{
|
||||
struct stack_builder *sb = PPSB (builder);
|
||||
PinosPacketReleaseFDPayload *p;
|
||||
|
||||
g_return_val_if_fail (is_valid_builder (builder), FALSE);
|
||||
|
||||
p = builder_add_packet (sb,
|
||||
PINOS_PACKET_TYPE_RELEASE_FD_PAYLOAD,
|
||||
sizeof (PinosPacketReleaseFDPayload));
|
||||
*p = *payload;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
|
@ -1,220 +0,0 @@
|
|||
/* Pinos
|
||||
* Copyright (C) 2015 Wim Taymans <wim.taymans@gmail.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#ifndef __PINOS_BUFFER_H__
|
||||
#define __PINOS_BUFFER_H__
|
||||
|
||||
#include <glib-object.h>
|
||||
#include <gio/gio.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
typedef struct _PinosBuffer PinosBuffer;
|
||||
typedef struct _PinosBufferIter PinosBufferIter;
|
||||
typedef struct _PinosBufferBuilder PinosBufferBuilder;
|
||||
|
||||
#define PINOS_BUFFER_VERSION 0
|
||||
|
||||
struct _PinosBuffer {
|
||||
/*< private >*/
|
||||
gsize x[16];
|
||||
};
|
||||
|
||||
void pinos_buffer_init_data (PinosBuffer *buffer,
|
||||
gpointer data,
|
||||
gsize size,
|
||||
GSocketControlMessage *message);
|
||||
|
||||
void pinos_buffer_clear (PinosBuffer *buffer);
|
||||
|
||||
guint32 pinos_buffer_get_version (PinosBuffer *buffer);
|
||||
int pinos_buffer_get_fd (PinosBuffer *buffer,
|
||||
gint index,
|
||||
GError **error);
|
||||
|
||||
gpointer pinos_buffer_steal (PinosBuffer *buffer,
|
||||
gsize *size,
|
||||
GSocketControlMessage **message);
|
||||
|
||||
|
||||
/**
|
||||
* PinosPacketType:
|
||||
* @PINOS_PACKET_TYPE_INVALID: invalid packet type, ignore
|
||||
* @PINOS_PACKET_TYPE_CONTINUATION: continuation packet, used internally to send
|
||||
* commands using a shared memory region.
|
||||
* @PINOS_PACKET_TYPE_HEADER: common packet header
|
||||
* @PINOS_PACKET_TYPE_FD_PAYLOAD: packet contains fd-payload. An fd-payload contains
|
||||
* the media data as a file descriptor
|
||||
* @PINOS_PACKET_TYPE_RELEASE_FD_PAYLOAD: packet contains release fd-payload. Notifies
|
||||
* that a previously received fd-payload is no longer in use.
|
||||
* @PINOS_PACKET_TYPE_FORMAT_CHANGE: a format change.
|
||||
* @PINOS_PACKET_TYPE_PROPERTY_CHANGE: one or more property changes.
|
||||
*
|
||||
* The possible packet types.
|
||||
*/
|
||||
typedef enum {
|
||||
PINOS_PACKET_TYPE_INVALID = 0,
|
||||
|
||||
PINOS_PACKET_TYPE_CONTINUATION = 1,
|
||||
PINOS_PACKET_TYPE_HEADER = 2,
|
||||
PINOS_PACKET_TYPE_FD_PAYLOAD = 3,
|
||||
PINOS_PACKET_TYPE_RELEASE_FD_PAYLOAD = 4,
|
||||
PINOS_PACKET_TYPE_FORMAT_CHANGE = 5,
|
||||
PINOS_PACKET_TYPE_PROPERTY_CHANGE = 6,
|
||||
} PinosPacketType;
|
||||
|
||||
|
||||
/* iterating packets */
|
||||
struct _PinosBufferIter {
|
||||
/*< private >*/
|
||||
gsize x[16];
|
||||
};
|
||||
|
||||
void pinos_buffer_iter_init_full (PinosBufferIter *iter,
|
||||
PinosBuffer *buffer,
|
||||
guint32 version);
|
||||
#define pinos_buffer_iter_init(i,b) pinos_buffer_iter_init_full(i,b, PINOS_BUFFER_VERSION);
|
||||
|
||||
gboolean pinos_buffer_iter_next (PinosBufferIter *iter);
|
||||
|
||||
PinosPacketType pinos_buffer_iter_get_type (PinosBufferIter *iter);
|
||||
gpointer pinos_buffer_iter_get_data (PinosBufferIter *iter, gsize *size);
|
||||
|
||||
/**
|
||||
* PinosBufferBuilder:
|
||||
*/
|
||||
struct _PinosBufferBuilder {
|
||||
/*< private >*/
|
||||
gsize x[16];
|
||||
};
|
||||
|
||||
void pinos_buffer_builder_init_full (PinosBufferBuilder *builder,
|
||||
guint32 version);
|
||||
#define pinos_buffer_builder_init(b) pinos_buffer_builder_init_full(b, PINOS_BUFFER_VERSION);
|
||||
|
||||
void pinos_buffer_builder_clear (PinosBufferBuilder *builder);
|
||||
void pinos_buffer_builder_end (PinosBufferBuilder *builder,
|
||||
PinosBuffer *buffer);
|
||||
|
||||
gint pinos_buffer_builder_add_fd (PinosBufferBuilder *builder,
|
||||
int fd,
|
||||
GError **error);
|
||||
/* header packets */
|
||||
/**
|
||||
* PinosPacketHeader
|
||||
* @flags: header flags
|
||||
* @seq: sequence number
|
||||
* @pts: presentation timestamp in nanoseconds
|
||||
* @dts_offset: offset to presentation timestamp in nanoseconds to get decode timestamp
|
||||
*
|
||||
* A Packet that contains the header.
|
||||
*/
|
||||
typedef struct {
|
||||
guint32 flags;
|
||||
guint32 seq;
|
||||
gint64 pts;
|
||||
gint64 dts_offset;
|
||||
} PinosPacketHeader;
|
||||
|
||||
gboolean pinos_buffer_iter_parse_header (PinosBufferIter *iter,
|
||||
PinosPacketHeader *header);
|
||||
gboolean pinos_buffer_builder_add_header (PinosBufferBuilder *builder,
|
||||
PinosPacketHeader *header);
|
||||
|
||||
/* fd-payload packets */
|
||||
/**
|
||||
* PinosPacketFDPayload:
|
||||
* @id: the unique id of this payload
|
||||
* @fd_index: the index of the fd with the data
|
||||
* @offset: the offset of the data
|
||||
* @size: the size of the data
|
||||
*
|
||||
* A Packet that contains data in an fd at @fd_index at @offset and with
|
||||
* @size.
|
||||
*/
|
||||
typedef struct {
|
||||
guint32 id;
|
||||
gint32 fd_index;
|
||||
guint64 offset;
|
||||
guint64 size;
|
||||
} PinosPacketFDPayload;
|
||||
|
||||
gboolean pinos_buffer_iter_parse_fd_payload (PinosBufferIter *iter,
|
||||
PinosPacketFDPayload *payload);
|
||||
gboolean pinos_buffer_builder_add_fd_payload (PinosBufferBuilder *builder,
|
||||
PinosPacketFDPayload *payload);
|
||||
|
||||
/* release fd-payload packets */
|
||||
/**
|
||||
* PinosPacketReleaseFDPayload:
|
||||
* @id: the unique id of the fd-payload to release
|
||||
*
|
||||
* Release the payload with @id
|
||||
*/
|
||||
typedef struct {
|
||||
guint32 id;
|
||||
} PinosPacketReleaseFDPayload;
|
||||
|
||||
gboolean pinos_buffer_iter_parse_release_fd_payload (PinosBufferIter *iter,
|
||||
PinosPacketReleaseFDPayload *payload);
|
||||
gboolean pinos_buffer_builder_add_release_fd_payload (PinosBufferBuilder *builder,
|
||||
PinosPacketReleaseFDPayload *payload);
|
||||
|
||||
|
||||
/* format change packets */
|
||||
/**
|
||||
* PinosPacketFormatChange:
|
||||
* @id: the id of the new format
|
||||
* @format: the new format
|
||||
*
|
||||
* A new format.
|
||||
*/
|
||||
typedef struct {
|
||||
guint8 id;
|
||||
gchar *format;
|
||||
} PinosPacketFormatChange;
|
||||
|
||||
gboolean pinos_buffer_iter_parse_format_change (PinosBufferIter *iter,
|
||||
PinosPacketFormatChange *payload);
|
||||
gboolean pinos_buffer_builder_add_format_change (PinosBufferBuilder *builder,
|
||||
PinosPacketFormatChange *payload);
|
||||
|
||||
|
||||
/* property change packets */
|
||||
/**
|
||||
* PinosPacketPropertyChange:
|
||||
* @key: the key of the property
|
||||
* @value: the new value
|
||||
*
|
||||
* A new property change.
|
||||
*/
|
||||
typedef struct {
|
||||
gchar *key;
|
||||
gchar *value;
|
||||
} PinosPacketPropertyChange;
|
||||
|
||||
gboolean pinos_buffer_iter_parse_property_change (PinosBufferIter *iter,
|
||||
guint idx,
|
||||
PinosPacketPropertyChange *payload);
|
||||
gboolean pinos_buffer_builder_add_property_change (PinosBufferBuilder *builder,
|
||||
PinosPacketPropertyChange *payload);
|
||||
|
||||
|
||||
#endif /* __PINOS_BUFFER_H__ */
|
||||
|
||||
|
|
@ -1,755 +0,0 @@
|
|||
/* Pinos
|
||||
* Copyright (C) 2015 Wim Taymans <wim.taymans@gmail.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include "client/pinos.h"
|
||||
|
||||
#include "client/context.h"
|
||||
#include "client/enumtypes.h"
|
||||
#include "client/subscribe.h"
|
||||
|
||||
#include "client/private.h"
|
||||
|
||||
#define PINOS_CONTEXT_GET_PRIVATE(obj) \
|
||||
(G_TYPE_INSTANCE_GET_PRIVATE ((obj), PINOS_TYPE_CONTEXT, PinosContextPrivate))
|
||||
|
||||
G_DEFINE_TYPE (PinosContext, pinos_context, G_TYPE_OBJECT);
|
||||
|
||||
static void subscription_state (GObject *object, GParamSpec *pspec, gpointer user_data);
|
||||
static void subscription_cb (PinosSubscribe *subscribe,
|
||||
PinosSubscriptionEvent event,
|
||||
PinosSubscriptionFlags flags,
|
||||
GDBusProxy *object,
|
||||
gpointer user_data);
|
||||
|
||||
enum
|
||||
{
|
||||
PROP_0,
|
||||
PROP_MAIN_CONTEXT,
|
||||
PROP_NAME,
|
||||
PROP_PROPERTIES,
|
||||
PROP_STATE,
|
||||
PROP_CONNECTION,
|
||||
PROP_SUBSCRIPTION_MASK,
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
SIGNAL_SUBSCRIPTION_EVENT,
|
||||
LAST_SIGNAL
|
||||
};
|
||||
|
||||
static guint signals[LAST_SIGNAL] = { 0 };
|
||||
|
||||
static void
|
||||
pinos_context_get_property (GObject *_object,
|
||||
guint prop_id,
|
||||
GValue *value,
|
||||
GParamSpec *pspec)
|
||||
{
|
||||
PinosContext *context = PINOS_CONTEXT (_object);
|
||||
PinosContextPrivate *priv = context->priv;
|
||||
|
||||
switch (prop_id) {
|
||||
case PROP_MAIN_CONTEXT:
|
||||
g_value_set_boxed (value, priv->context);
|
||||
break;
|
||||
|
||||
case PROP_NAME:
|
||||
g_value_set_string (value, priv->name);
|
||||
break;
|
||||
|
||||
case PROP_PROPERTIES:
|
||||
g_value_set_boxed (value, priv->properties);
|
||||
break;
|
||||
|
||||
case PROP_STATE:
|
||||
g_value_set_enum (value, priv->state);
|
||||
break;
|
||||
|
||||
case PROP_CONNECTION:
|
||||
g_value_set_object (value, priv->connection);
|
||||
break;
|
||||
|
||||
case PROP_SUBSCRIPTION_MASK:
|
||||
g_value_set_flags (value, priv->subscription_mask);
|
||||
break;
|
||||
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (context, prop_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
pinos_context_set_property (GObject *_object,
|
||||
guint prop_id,
|
||||
const GValue *value,
|
||||
GParamSpec *pspec)
|
||||
{
|
||||
PinosContext *context = PINOS_CONTEXT (_object);
|
||||
PinosContextPrivate *priv = context->priv;
|
||||
|
||||
switch (prop_id) {
|
||||
case PROP_MAIN_CONTEXT:
|
||||
priv->context = g_value_dup_boxed (value);
|
||||
break;
|
||||
|
||||
case PROP_NAME:
|
||||
g_free (priv->name);
|
||||
priv->name = g_value_dup_string (value);
|
||||
break;
|
||||
|
||||
case PROP_PROPERTIES:
|
||||
if (priv->properties)
|
||||
pinos_properties_free (priv->properties);
|
||||
priv->properties = g_value_dup_boxed (value);
|
||||
break;
|
||||
|
||||
case PROP_SUBSCRIPTION_MASK:
|
||||
priv->subscription_mask = g_value_get_flags (value);
|
||||
break;
|
||||
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (context, prop_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
pinos_context_finalize (GObject * object)
|
||||
{
|
||||
PinosContext *context = PINOS_CONTEXT (object);
|
||||
PinosContextPrivate *priv = context->priv;
|
||||
|
||||
g_debug ("free context %p", context);
|
||||
|
||||
if (priv->id)
|
||||
g_bus_unwatch_name(priv->id);
|
||||
|
||||
g_clear_pointer (&priv->context, g_main_context_unref);
|
||||
g_free (priv->name);
|
||||
if (priv->properties)
|
||||
pinos_properties_free (priv->properties);
|
||||
|
||||
g_list_free (priv->sources);
|
||||
g_list_free (priv->clients);
|
||||
g_list_free (priv->source_outputs);
|
||||
g_clear_object (&priv->subscribe);
|
||||
g_clear_error (&priv->error);
|
||||
|
||||
G_OBJECT_CLASS (pinos_context_parent_class)->finalize (object);
|
||||
}
|
||||
|
||||
static void
|
||||
pinos_context_class_init (PinosContextClass * klass)
|
||||
{
|
||||
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
|
||||
|
||||
g_type_class_add_private (klass, sizeof (PinosContextPrivate));
|
||||
|
||||
gobject_class->finalize = pinos_context_finalize;
|
||||
gobject_class->set_property = pinos_context_set_property;
|
||||
gobject_class->get_property = pinos_context_get_property;
|
||||
|
||||
/**
|
||||
* PinosContext:main-context
|
||||
*
|
||||
* The main context to use
|
||||
*/
|
||||
g_object_class_install_property (gobject_class,
|
||||
PROP_MAIN_CONTEXT,
|
||||
g_param_spec_boxed ("main-context",
|
||||
"Main Context",
|
||||
"The main context to use",
|
||||
G_TYPE_MAIN_CONTEXT,
|
||||
G_PARAM_READWRITE |
|
||||
G_PARAM_CONSTRUCT_ONLY |
|
||||
G_PARAM_STATIC_STRINGS));
|
||||
/**
|
||||
* PinosContext:name
|
||||
*
|
||||
* The application name of the context.
|
||||
*/
|
||||
g_object_class_install_property (gobject_class,
|
||||
PROP_NAME,
|
||||
g_param_spec_string ("name",
|
||||
"Name",
|
||||
"The application name",
|
||||
NULL,
|
||||
G_PARAM_READWRITE |
|
||||
G_PARAM_STATIC_STRINGS));
|
||||
/**
|
||||
* PinosContext:properties
|
||||
*
|
||||
* Properties of the context.
|
||||
*/
|
||||
g_object_class_install_property (gobject_class,
|
||||
PROP_PROPERTIES,
|
||||
g_param_spec_boxed ("properties",
|
||||
"Properties",
|
||||
"Extra properties",
|
||||
PINOS_TYPE_PROPERTIES,
|
||||
G_PARAM_READWRITE |
|
||||
G_PARAM_STATIC_STRINGS));
|
||||
/**
|
||||
* PinosContext:state
|
||||
*
|
||||
* The state of the context.
|
||||
*/
|
||||
g_object_class_install_property (gobject_class,
|
||||
PROP_STATE,
|
||||
g_param_spec_enum ("state",
|
||||
"State",
|
||||
"The context state",
|
||||
PINOS_TYPE_CONTEXT_STATE,
|
||||
PINOS_CONTEXT_STATE_UNCONNECTED,
|
||||
G_PARAM_READABLE |
|
||||
G_PARAM_STATIC_STRINGS));
|
||||
/**
|
||||
* PinosContext:connection
|
||||
*
|
||||
* The connection of the context.
|
||||
*/
|
||||
g_object_class_install_property (gobject_class,
|
||||
PROP_CONNECTION,
|
||||
g_param_spec_object ("connection",
|
||||
"Connection",
|
||||
"The DBus connection",
|
||||
G_TYPE_DBUS_CONNECTION,
|
||||
G_PARAM_READABLE |
|
||||
G_PARAM_STATIC_STRINGS));
|
||||
/**
|
||||
* PinosContext:subscription-mask
|
||||
*
|
||||
* The subscription mask
|
||||
*/
|
||||
g_object_class_install_property (gobject_class,
|
||||
PROP_SUBSCRIPTION_MASK,
|
||||
g_param_spec_flags ("subscription-mask",
|
||||
"Subscription Mask",
|
||||
"The object to receive subscription events of",
|
||||
PINOS_TYPE_SUBSCRIPTION_FLAGS,
|
||||
0,
|
||||
G_PARAM_READWRITE |
|
||||
G_PARAM_STATIC_STRINGS));
|
||||
/**
|
||||
* PinosContext:subscription-event
|
||||
* @subscribe: The #PinosContext emitting the signal.
|
||||
* @event: A #PinosSubscriptionEvent
|
||||
* @flags: #PinosSubscriptionFlags indicating the object
|
||||
* @object: the GDBusProxy object
|
||||
*
|
||||
* Notify about a new object that was added/removed/modified.
|
||||
*/
|
||||
signals[SIGNAL_SUBSCRIPTION_EVENT] = g_signal_new ("subscription-event",
|
||||
G_TYPE_FROM_CLASS (klass),
|
||||
G_SIGNAL_RUN_LAST,
|
||||
0,
|
||||
NULL,
|
||||
NULL,
|
||||
g_cclosure_marshal_generic,
|
||||
G_TYPE_NONE,
|
||||
3,
|
||||
PINOS_TYPE_SUBSCRIPTION_EVENT,
|
||||
PINOS_TYPE_SUBSCRIPTION_FLAGS,
|
||||
G_TYPE_DBUS_PROXY);
|
||||
|
||||
}
|
||||
|
||||
static void
|
||||
pinos_context_init (PinosContext * context)
|
||||
{
|
||||
PinosContextPrivate *priv = context->priv = PINOS_CONTEXT_GET_PRIVATE (context);
|
||||
|
||||
g_debug ("new context %p", context);
|
||||
|
||||
priv->state = PINOS_CONTEXT_STATE_UNCONNECTED;
|
||||
|
||||
priv->subscribe = pinos_subscribe_new ();
|
||||
g_object_set (priv->subscribe,
|
||||
"subscription-mask", PINOS_SUBSCRIPTION_FLAGS_ALL,
|
||||
NULL);
|
||||
g_signal_connect (priv->subscribe,
|
||||
"subscription-event",
|
||||
(GCallback) subscription_cb,
|
||||
context);
|
||||
g_signal_connect (priv->subscribe,
|
||||
"notify::state",
|
||||
(GCallback) subscription_state,
|
||||
context);
|
||||
}
|
||||
|
||||
/**
|
||||
* pinos_context_state_as_string:
|
||||
* @state: a #PinosContextState
|
||||
*
|
||||
* Return the string representation of @state.
|
||||
*
|
||||
* Returns: the string representation of @state.
|
||||
*/
|
||||
const gchar *
|
||||
pinos_context_state_as_string (PinosContextState state)
|
||||
{
|
||||
GEnumValue *val;
|
||||
|
||||
val = g_enum_get_value (G_ENUM_CLASS (g_type_class_ref (PINOS_TYPE_CONTEXT_STATE)),
|
||||
state);
|
||||
|
||||
return val == NULL ? "invalid-state" : val->value_nick;
|
||||
}
|
||||
|
||||
/**
|
||||
* pinos_context_new:
|
||||
* @context: a #GMainContext to run in
|
||||
* @name: an application name
|
||||
* @properties: (transfer full): optional properties
|
||||
*
|
||||
* Make a new unconnected #PinosContext
|
||||
*
|
||||
* Returns: a new unconnected #PinosContext
|
||||
*/
|
||||
PinosContext *
|
||||
pinos_context_new (GMainContext *context,
|
||||
const gchar *name,
|
||||
PinosProperties *properties)
|
||||
{
|
||||
PinosContext *ctx;
|
||||
|
||||
g_return_val_if_fail (name != NULL, NULL);
|
||||
|
||||
if (properties == NULL)
|
||||
properties = pinos_properties_new ("application.name", name, NULL);
|
||||
|
||||
pinos_fill_context_properties (properties);
|
||||
|
||||
ctx = g_object_new (PINOS_TYPE_CONTEXT,
|
||||
"main-context", context,
|
||||
"name", name,
|
||||
"properties", properties,
|
||||
NULL);
|
||||
|
||||
pinos_properties_free (properties);
|
||||
|
||||
return ctx;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
do_notify_state (PinosContext *context)
|
||||
{
|
||||
g_object_notify (G_OBJECT (context), "state");
|
||||
g_object_unref (context);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static void
|
||||
context_set_state (PinosContext *context,
|
||||
PinosContextState state,
|
||||
GError *error)
|
||||
{
|
||||
if (context->priv->state != state) {
|
||||
if (error) {
|
||||
g_clear_error (&context->priv->error);
|
||||
context->priv->error = error;
|
||||
}
|
||||
context->priv->state = state;
|
||||
g_main_context_invoke (context->priv->context,
|
||||
(GSourceFunc) do_notify_state,
|
||||
g_object_ref (context));
|
||||
} else {
|
||||
if (error)
|
||||
g_error_free (error);
|
||||
}
|
||||
}
|
||||
static void
|
||||
on_client_proxy (GObject *source_object,
|
||||
GAsyncResult *res,
|
||||
gpointer user_data)
|
||||
{
|
||||
PinosContext *context = user_data;
|
||||
PinosContextPrivate *priv = context->priv;
|
||||
GError *error = NULL;
|
||||
|
||||
priv->client = pinos_subscribe_get_proxy_finish (priv->subscribe,
|
||||
res,
|
||||
&error);
|
||||
if (priv->client == NULL)
|
||||
goto client_failed;
|
||||
|
||||
context_set_state (context, PINOS_CONTEXT_STATE_READY, NULL);
|
||||
|
||||
return;
|
||||
|
||||
client_failed:
|
||||
{
|
||||
g_warning ("failed to get client proxy: %s", error->message);
|
||||
context_set_state (context, PINOS_STREAM_STATE_ERROR, error);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
on_client_connected (GObject *source_object,
|
||||
GAsyncResult *res,
|
||||
gpointer user_data)
|
||||
{
|
||||
PinosContext *context = user_data;
|
||||
PinosContextPrivate *priv = context->priv;
|
||||
GVariant *ret;
|
||||
GError *error = NULL;
|
||||
const gchar *client_path;
|
||||
|
||||
ret = g_dbus_proxy_call_finish (priv->daemon, res, &error);
|
||||
if (ret == NULL) {
|
||||
g_warning ("failed to connect client: %s", error->message);
|
||||
context_set_state (context, PINOS_CONTEXT_STATE_ERROR, error);
|
||||
return;
|
||||
}
|
||||
|
||||
g_variant_get (ret, "(&o)", &client_path);
|
||||
|
||||
pinos_subscribe_get_proxy (priv->subscribe,
|
||||
PINOS_DBUS_SERVICE,
|
||||
client_path,
|
||||
"org.pinos.Client1",
|
||||
NULL,
|
||||
on_client_proxy,
|
||||
context);
|
||||
g_variant_unref (ret);
|
||||
}
|
||||
|
||||
static void
|
||||
on_daemon_connected (GObject *source_object,
|
||||
GAsyncResult *res,
|
||||
gpointer user_data)
|
||||
{
|
||||
PinosContext *context = user_data;
|
||||
PinosContextPrivate *priv = context->priv;
|
||||
GVariant *variant;
|
||||
|
||||
context_set_state (context, PINOS_CONTEXT_STATE_REGISTERING, NULL);
|
||||
|
||||
variant = pinos_properties_to_variant (priv->properties);
|
||||
|
||||
g_dbus_proxy_call (priv->daemon,
|
||||
"ConnectClient",
|
||||
g_variant_new ("(@a{sv})", variant),
|
||||
G_DBUS_CALL_FLAGS_NONE,
|
||||
-1,
|
||||
NULL,
|
||||
on_client_connected,
|
||||
context);
|
||||
}
|
||||
|
||||
static void
|
||||
subscription_cb (PinosSubscribe *subscribe,
|
||||
PinosSubscriptionEvent event,
|
||||
PinosSubscriptionFlags flags,
|
||||
GDBusProxy *object,
|
||||
gpointer user_data)
|
||||
{
|
||||
PinosContext *context = user_data;
|
||||
PinosContextPrivate *priv = context->priv;
|
||||
|
||||
switch (flags) {
|
||||
case PINOS_SUBSCRIPTION_FLAG_DAEMON:
|
||||
priv->daemon = g_object_ref (object);
|
||||
break;
|
||||
|
||||
case PINOS_SUBSCRIPTION_FLAG_CLIENT:
|
||||
if (event == PINOS_SUBSCRIPTION_EVENT_NEW) {
|
||||
priv->clients = g_list_prepend (priv->clients, object);
|
||||
} else if (event == PINOS_SUBSCRIPTION_EVENT_REMOVE) {
|
||||
priv->clients = g_list_remove (priv->clients, object);
|
||||
|
||||
if (object == priv->client && !priv->disconnecting) {
|
||||
context_set_state (context,
|
||||
PINOS_CONTEXT_STATE_ERROR,
|
||||
g_error_new_literal (G_IO_ERROR,
|
||||
G_IO_ERROR_CLOSED,
|
||||
"Client disappeared"));
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case PINOS_SUBSCRIPTION_FLAG_SOURCE:
|
||||
if (event == PINOS_SUBSCRIPTION_EVENT_NEW)
|
||||
priv->sources = g_list_prepend (priv->sources, object);
|
||||
else if (event == PINOS_SUBSCRIPTION_EVENT_REMOVE)
|
||||
priv->sources = g_list_remove (priv->sources, object);
|
||||
break;
|
||||
|
||||
case PINOS_SUBSCRIPTION_FLAG_SOURCE_OUTPUT:
|
||||
if (event == PINOS_SUBSCRIPTION_EVENT_NEW)
|
||||
priv->source_outputs = g_list_prepend (priv->source_outputs, object);
|
||||
else if (event == PINOS_SUBSCRIPTION_EVENT_REMOVE)
|
||||
priv->source_outputs = g_list_remove (priv->source_outputs, object);
|
||||
break;
|
||||
}
|
||||
|
||||
if (flags & priv->subscription_mask)
|
||||
g_signal_emit (context,
|
||||
signals[SIGNAL_SUBSCRIPTION_EVENT],
|
||||
0,
|
||||
event,
|
||||
flags,
|
||||
object);
|
||||
|
||||
}
|
||||
|
||||
static void
|
||||
subscription_state (GObject *object,
|
||||
GParamSpec *pspec,
|
||||
gpointer user_data)
|
||||
{
|
||||
PinosContext *context = user_data;
|
||||
PinosContextPrivate *priv = context->priv;
|
||||
PinosSubscriptionState state;
|
||||
|
||||
g_assert (object == G_OBJECT (priv->subscribe));
|
||||
|
||||
state = pinos_subscribe_get_state (priv->subscribe);
|
||||
|
||||
switch (state) {
|
||||
case PINOS_SUBSCRIPTION_STATE_READY:
|
||||
on_daemon_connected (NULL, NULL, context);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static void
|
||||
on_name_appeared (GDBusConnection *connection,
|
||||
const gchar *name,
|
||||
const gchar *name_owner,
|
||||
gpointer user_data)
|
||||
{
|
||||
PinosContext *context = user_data;
|
||||
PinosContextPrivate *priv = context->priv;
|
||||
|
||||
priv->connection = connection;
|
||||
|
||||
g_object_set (priv->subscribe, "connection", priv->connection,
|
||||
"service", name, NULL);
|
||||
}
|
||||
|
||||
static void
|
||||
on_name_vanished (GDBusConnection *connection,
|
||||
const gchar *name,
|
||||
gpointer user_data)
|
||||
{
|
||||
PinosContext *context = user_data;
|
||||
PinosContextPrivate *priv = context->priv;
|
||||
|
||||
priv->connection = connection;
|
||||
|
||||
g_object_set (priv->subscribe, "connection", connection, NULL);
|
||||
|
||||
if (priv->flags & PINOS_CONTEXT_FLAGS_NOFAIL) {
|
||||
context_set_state (context, PINOS_CONTEXT_STATE_CONNECTING, NULL);
|
||||
} else {
|
||||
context_set_state (context,
|
||||
PINOS_CONTEXT_STATE_ERROR,
|
||||
g_error_new_literal (G_IO_ERROR,
|
||||
G_IO_ERROR_CLOSED,
|
||||
"Connection closed"));
|
||||
}
|
||||
}
|
||||
|
||||
static gboolean
|
||||
do_connect (PinosContext *context)
|
||||
{
|
||||
PinosContextPrivate *priv = context->priv;
|
||||
GBusNameWatcherFlags nw_flags;
|
||||
|
||||
nw_flags = G_BUS_NAME_WATCHER_FLAGS_NONE;
|
||||
if (!(priv->flags & PINOS_CONTEXT_FLAGS_NOAUTOSPAWN))
|
||||
nw_flags = G_BUS_NAME_WATCHER_FLAGS_AUTO_START;
|
||||
|
||||
priv->id = g_bus_watch_name (G_BUS_TYPE_SESSION,
|
||||
PINOS_DBUS_SERVICE,
|
||||
nw_flags,
|
||||
on_name_appeared,
|
||||
on_name_vanished,
|
||||
context,
|
||||
NULL);
|
||||
g_object_unref (context);
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/**
|
||||
* pinos_context_connect:
|
||||
* @context: a #PinosContext
|
||||
* @flags: #PinosContextFlags
|
||||
*
|
||||
* Connect to the daemon with @flags
|
||||
*
|
||||
* Returns: %TRUE on success.
|
||||
*/
|
||||
gboolean
|
||||
pinos_context_connect (PinosContext *context,
|
||||
PinosContextFlags flags)
|
||||
{
|
||||
PinosContextPrivate *priv;
|
||||
|
||||
g_return_val_if_fail (PINOS_IS_CONTEXT (context), FALSE);
|
||||
|
||||
priv = context->priv;
|
||||
g_return_val_if_fail (priv->connection == NULL, FALSE);
|
||||
|
||||
priv->flags = flags;
|
||||
|
||||
context_set_state (context, PINOS_CONTEXT_STATE_CONNECTING, NULL);
|
||||
g_main_context_invoke (priv->context,
|
||||
(GSourceFunc) do_connect,
|
||||
g_object_ref (context));
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
finish_client_disconnect (PinosContext *context)
|
||||
{
|
||||
PinosContextPrivate *priv = context->priv;
|
||||
|
||||
g_clear_object (&priv->client);
|
||||
g_clear_object (&priv->daemon);
|
||||
if (priv->id) {
|
||||
g_bus_unwatch_name(priv->id);
|
||||
priv->id = 0;
|
||||
}
|
||||
|
||||
context_set_state (context, PINOS_CONTEXT_STATE_UNCONNECTED, NULL);
|
||||
}
|
||||
|
||||
static void
|
||||
on_client_disconnected (GObject *source_object,
|
||||
GAsyncResult *res,
|
||||
gpointer user_data)
|
||||
{
|
||||
PinosContext *context = user_data;
|
||||
PinosContextPrivate *priv = context->priv;
|
||||
GError *error = NULL;
|
||||
GVariant *ret;
|
||||
|
||||
priv->disconnecting = FALSE;
|
||||
|
||||
ret = g_dbus_proxy_call_finish (priv->client, res, &error);
|
||||
if (ret == NULL) {
|
||||
g_warning ("failed to disconnect client: %s", error->message);
|
||||
context_set_state (context, PINOS_CONTEXT_STATE_ERROR, error);
|
||||
g_object_unref (context);
|
||||
return;
|
||||
}
|
||||
g_variant_unref (ret);
|
||||
|
||||
finish_client_disconnect (context);
|
||||
g_object_unref (context);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
do_disconnect (PinosContext *context)
|
||||
{
|
||||
PinosContextPrivate *priv = context->priv;
|
||||
|
||||
g_dbus_proxy_call (priv->client,
|
||||
"Disconnect",
|
||||
g_variant_new ("()"),
|
||||
G_DBUS_CALL_FLAGS_NONE,
|
||||
-1,
|
||||
NULL,
|
||||
on_client_disconnected,
|
||||
context);
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/**
|
||||
* pinos_context_disconnect:
|
||||
* @context: a #PinosContext
|
||||
*
|
||||
* Disonnect from the daemon.
|
||||
*
|
||||
* Returns: %TRUE on success.
|
||||
*/
|
||||
gboolean
|
||||
pinos_context_disconnect (PinosContext *context)
|
||||
{
|
||||
PinosContextPrivate *priv;
|
||||
|
||||
g_return_val_if_fail (PINOS_IS_CONTEXT (context), FALSE);
|
||||
|
||||
priv = context->priv;
|
||||
g_return_val_if_fail (!priv->disconnecting, FALSE);
|
||||
|
||||
if (priv->client == NULL) {
|
||||
finish_client_disconnect (context);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
priv->disconnecting = TRUE;
|
||||
|
||||
g_main_context_invoke (priv->context,
|
||||
(GSourceFunc) do_disconnect,
|
||||
g_object_ref (context));
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* pinos_context_get_state:
|
||||
* @context: a #PinosContext
|
||||
*
|
||||
* Get the state of @context.
|
||||
*
|
||||
* Returns: the state of @context
|
||||
*/
|
||||
PinosContextState
|
||||
pinos_context_get_state (PinosContext *context)
|
||||
{
|
||||
PinosContextPrivate *priv;
|
||||
|
||||
g_return_val_if_fail (PINOS_IS_CONTEXT (context), PINOS_CONTEXT_STATE_ERROR);
|
||||
priv = context->priv;
|
||||
|
||||
return priv->state;
|
||||
}
|
||||
|
||||
/**
|
||||
* pinos_context_get_error:
|
||||
* @context: a #PinosContext
|
||||
*
|
||||
* Get the current error of @context or %NULL when the context state
|
||||
* is not #PINOS_CONTEXT_STATE_ERROR
|
||||
*
|
||||
* Returns: the last error or %NULL
|
||||
*/
|
||||
const GError *
|
||||
pinos_context_get_error (PinosContext *context)
|
||||
{
|
||||
PinosContextPrivate *priv;
|
||||
|
||||
g_return_val_if_fail (PINOS_IS_CONTEXT (context), NULL);
|
||||
priv = context->priv;
|
||||
|
||||
return priv->error;
|
||||
}
|
||||
|
|
@ -1,115 +0,0 @@
|
|||
/* Pinos
|
||||
* Copyright (C) 2015 Wim Taymans <wim.taymans@gmail.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#ifndef __PINOS_CONTEXT_H__
|
||||
#define __PINOS_CONTEXT_H__
|
||||
|
||||
#include <glib-object.h>
|
||||
|
||||
#include <client/subscribe.h>
|
||||
#include <client/properties.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define PINOS_TYPE_CONTEXT (pinos_context_get_type ())
|
||||
#define PINOS_IS_CONTEXT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), PINOS_TYPE_CONTEXT))
|
||||
#define PINOS_IS_CONTEXT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), PINOS_TYPE_CONTEXT))
|
||||
#define PINOS_CONTEXT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), PINOS_TYPE_CONTEXT, PinosContextClass))
|
||||
#define PINOS_CONTEXT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), PINOS_TYPE_CONTEXT, PinosContext))
|
||||
#define PINOS_CONTEXT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), PINOS_TYPE_CONTEXT, PinosContextClass))
|
||||
#define PINOS_CONTEXT_CAST(obj) ((PinosContext*)(obj))
|
||||
#define PINOS_CONTEXT_CLASS_CAST(klass) ((PinosContextClass*)(klass))
|
||||
|
||||
typedef struct _PinosContext PinosContext;
|
||||
typedef struct _PinosContextClass PinosContextClass;
|
||||
typedef struct _PinosContextPrivate PinosContextPrivate;
|
||||
|
||||
/**
|
||||
* PinosContextFlags:
|
||||
* @PINOS_CONTEXT_FLAGS_NONE: no flags
|
||||
* @PINOS_CONTEXT_FLAGS_NOAUTOSPAWN: disable autostart of the daemon
|
||||
* @PINOS_CONTEXT_FLAGS_NOFAIL: Don't fail if the daemon is not available,
|
||||
* instead enter PINOS_CONTEXT_CONNECTING state and wait for the daemon
|
||||
* to appear.
|
||||
*
|
||||
* Context flags passed to pinos_context_connect()
|
||||
*/
|
||||
typedef enum {
|
||||
PINOS_CONTEXT_FLAGS_NONE = 0,
|
||||
PINOS_CONTEXT_FLAGS_NOAUTOSPAWN = (1 << 0),
|
||||
PINOS_CONTEXT_FLAGS_NOFAIL = (1 << 1)
|
||||
} PinosContextFlags;
|
||||
|
||||
/**
|
||||
* PinosContextState:
|
||||
* @PINOS_CONTEXT_STATE_UNCONNECTED: not connected
|
||||
* @PINOS_CONTEXT_STATE_CONNECTING: connecting to daemon
|
||||
* @PINOS_CONTEXT_STATE_REGISTERING: registering with daemon
|
||||
* @PINOS_CONTEXT_STATE_READY: context is ready
|
||||
* @PINOS_CONTEXT_STATE_ERROR: context is in error
|
||||
*
|
||||
* The state of a #PinosContext
|
||||
*/
|
||||
typedef enum {
|
||||
PINOS_CONTEXT_STATE_ERROR = -1,
|
||||
PINOS_CONTEXT_STATE_UNCONNECTED = 0,
|
||||
PINOS_CONTEXT_STATE_CONNECTING = 1,
|
||||
PINOS_CONTEXT_STATE_REGISTERING = 2,
|
||||
PINOS_CONTEXT_STATE_READY = 3,
|
||||
} PinosContextState;
|
||||
|
||||
const gchar * pinos_context_state_as_string (PinosContextState state);
|
||||
|
||||
/**
|
||||
* PinosContext:
|
||||
*
|
||||
* Pinos context object class.
|
||||
*/
|
||||
struct _PinosContext {
|
||||
GObject object;
|
||||
|
||||
PinosContextPrivate *priv;
|
||||
};
|
||||
|
||||
/**
|
||||
* PinosContextClass:
|
||||
*
|
||||
* Pinos context object class.
|
||||
*/
|
||||
struct _PinosContextClass {
|
||||
GObjectClass parent_class;
|
||||
};
|
||||
|
||||
/* normal GObject stuff */
|
||||
GType pinos_context_get_type (void);
|
||||
|
||||
PinosContext * pinos_context_new (GMainContext *ctx,
|
||||
const gchar *name,
|
||||
PinosProperties *properties);
|
||||
|
||||
gboolean pinos_context_connect (PinosContext *context, PinosContextFlags flags);
|
||||
gboolean pinos_context_disconnect (PinosContext *context);
|
||||
|
||||
PinosContextState pinos_context_get_state (PinosContext *context);
|
||||
const GError * pinos_context_get_error (PinosContext *context);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __PINOS_CONTEXT_H__ */
|
||||
|
||||
|
|
@ -1,537 +0,0 @@
|
|||
/* Pinos
|
||||
* Copyright (C) 2015 Wim Taymans <wim.taymans@gmail.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "client/pinos.h"
|
||||
|
||||
#include "client/context.h"
|
||||
#include "client/enumtypes.h"
|
||||
#include "client/subscribe.h"
|
||||
|
||||
#include "client/private.h"
|
||||
|
||||
/**
|
||||
* pinos_context_info_finish:
|
||||
* @object: a #GObject
|
||||
* @res: a #GAsyncResult
|
||||
* @error: location to place an error
|
||||
*
|
||||
* Call this function in the introspection GAsyncReadyCallback function
|
||||
* to get the final result of the operation.
|
||||
*
|
||||
* Returns: %TRUE if the lookup was successful. If %FALSE is returned, @error
|
||||
* will contain more details.
|
||||
*/
|
||||
gboolean
|
||||
pinos_context_info_finish (GObject *object,
|
||||
GAsyncResult *res,
|
||||
GError **error)
|
||||
{
|
||||
g_return_val_if_fail (g_task_is_valid (res, object), FALSE);
|
||||
|
||||
return g_task_propagate_boolean (G_TASK (res), error);
|
||||
}
|
||||
|
||||
#define SET_STRING(name, field, idx) \
|
||||
G_STMT_START { \
|
||||
GVariant *variant; \
|
||||
if (!changed || g_hash_table_contains (changed, name)) \
|
||||
info->change_mask |= 1 << idx; \
|
||||
if ((variant = g_dbus_proxy_get_cached_property (G_DBUS_PROXY (proxy), name))) { \
|
||||
info->field = g_variant_get_string (variant, NULL); \
|
||||
g_variant_unref (variant); \
|
||||
} else { \
|
||||
info->field = "Unknown"; \
|
||||
} \
|
||||
} G_STMT_END
|
||||
|
||||
#define SET_UINT32(name, field, idx, def) \
|
||||
G_STMT_START { \
|
||||
GVariant *variant; \
|
||||
if (!changed || g_hash_table_contains (changed, name)) \
|
||||
info->change_mask |= 1 << idx; \
|
||||
if ((variant = g_dbus_proxy_get_cached_property (G_DBUS_PROXY (proxy), name))) { \
|
||||
info->field = g_variant_get_uint32 (variant); \
|
||||
g_variant_unref (variant); \
|
||||
} else { \
|
||||
info->field = def; \
|
||||
} \
|
||||
} G_STMT_END
|
||||
|
||||
#define SET_PROPERTIES(name, field, idx) \
|
||||
G_STMT_START { \
|
||||
GVariant *variant; \
|
||||
if (!changed || g_hash_table_contains (changed, name)) \
|
||||
info->change_mask |= 1 << idx; \
|
||||
if ((variant = g_dbus_proxy_get_cached_property (G_DBUS_PROXY (proxy), name))) { \
|
||||
info->field = pinos_properties_from_variant (variant); \
|
||||
g_variant_unref (variant); \
|
||||
} else { \
|
||||
info->field = NULL; \
|
||||
} \
|
||||
} G_STMT_END
|
||||
|
||||
#define SET_BYTES(name, field, idx) \
|
||||
G_STMT_START { \
|
||||
GVariant *variant; \
|
||||
if (!changed || g_hash_table_contains (changed, name)) \
|
||||
info->change_mask |= 1 << idx; \
|
||||
if ((variant = g_dbus_proxy_get_cached_property (G_DBUS_PROXY (proxy), name))) { \
|
||||
gsize len; \
|
||||
gchar *bytes = g_variant_dup_string (variant, &len); \
|
||||
info->field = g_bytes_new_take (bytes, len +1); \
|
||||
g_variant_unref (variant); \
|
||||
} else { \
|
||||
info->field = NULL; \
|
||||
} \
|
||||
} G_STMT_END
|
||||
|
||||
static void
|
||||
daemon_fill_info (PinosDaemonInfo *info, GDBusProxy *proxy)
|
||||
{
|
||||
GHashTable *changed = g_object_get_data (G_OBJECT (proxy), "pinos-changed-properties");
|
||||
|
||||
info->id = proxy;
|
||||
info->daemon_path = g_dbus_proxy_get_object_path (proxy);
|
||||
|
||||
info->change_mask = 0;
|
||||
SET_STRING ("UserName", user_name, 0);
|
||||
SET_STRING ("HostName", host_name, 1);
|
||||
SET_STRING ("Version", version, 2);
|
||||
SET_STRING ("Name", name, 3);
|
||||
SET_UINT32 ("Cookie", cookie, 4, 0);
|
||||
SET_PROPERTIES ("Properties", properties, 5);
|
||||
|
||||
if (changed)
|
||||
g_hash_table_remove_all (changed);
|
||||
}
|
||||
|
||||
static void
|
||||
daemon_clear_info (PinosDaemonInfo *info)
|
||||
{
|
||||
if (info->properties)
|
||||
pinos_properties_free (info->properties);
|
||||
}
|
||||
|
||||
/**
|
||||
* pinos_context_get_daemon_info:
|
||||
* @context: a #PinosContext
|
||||
* @flags: extra flags
|
||||
* @cb: a callback
|
||||
* @cancelable: a #GCancellable
|
||||
* @callback: a #GAsyncReadyCallback to call when the operation is finished
|
||||
* @user_data: user data passed to @cb
|
||||
*
|
||||
* Get the information of the daemon @context is connected to.
|
||||
*/
|
||||
void
|
||||
pinos_context_get_daemon_info (PinosContext *context,
|
||||
PinosDaemonInfoFlags flags,
|
||||
PinosDaemonInfoCallback cb,
|
||||
GCancellable *cancellable,
|
||||
GAsyncReadyCallback callback,
|
||||
gpointer user_data)
|
||||
{
|
||||
PinosDaemonInfo info;
|
||||
GTask *task;
|
||||
|
||||
g_return_if_fail (PINOS_IS_CONTEXT (context));
|
||||
g_return_if_fail (cb != NULL);
|
||||
|
||||
task = g_task_new (context, cancellable, callback, user_data);
|
||||
|
||||
daemon_fill_info (&info, context->priv->daemon);
|
||||
cb (context, &info, user_data);
|
||||
daemon_clear_info (&info);
|
||||
|
||||
g_task_return_boolean (task, TRUE);
|
||||
g_object_unref (task);
|
||||
}
|
||||
|
||||
static void
|
||||
client_fill_info (PinosClientInfo *info, GDBusProxy *proxy)
|
||||
{
|
||||
GHashTable *changed = g_object_get_data (G_OBJECT (proxy), "pinos-changed-properties");
|
||||
|
||||
info->id = proxy;
|
||||
info->client_path = g_dbus_proxy_get_object_path (proxy);
|
||||
|
||||
info->change_mask = 0;
|
||||
SET_STRING ("Name", name, 0);
|
||||
SET_PROPERTIES ("Properties", properties, 1);
|
||||
|
||||
if (changed)
|
||||
g_hash_table_remove_all (changed);
|
||||
}
|
||||
|
||||
static void
|
||||
client_clear_info (PinosClientInfo *info)
|
||||
{
|
||||
if (info->properties)
|
||||
pinos_properties_free (info->properties);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* pinos_context_list_client_info:
|
||||
* @context: a connected #PinosContext
|
||||
* @flags: extra #PinosClientInfoFlags
|
||||
* @cb: a #PinosClientInfoCallback
|
||||
* @cancelable: a #GCancellable
|
||||
* @callback: a #GAsyncReadyCallback to call when the operation is finished
|
||||
* @user_data: user data passed to @cb
|
||||
*
|
||||
* Call @cb for each client.
|
||||
*/
|
||||
void
|
||||
pinos_context_list_client_info (PinosContext *context,
|
||||
PinosClientInfoFlags flags,
|
||||
PinosClientInfoCallback cb,
|
||||
GCancellable *cancellable,
|
||||
GAsyncReadyCallback callback,
|
||||
gpointer user_data)
|
||||
{
|
||||
PinosContextPrivate *priv;
|
||||
GList *walk;
|
||||
GTask *task;
|
||||
|
||||
g_return_if_fail (PINOS_IS_CONTEXT (context));
|
||||
g_return_if_fail (cb != NULL);
|
||||
|
||||
task = g_task_new (context, cancellable, callback, user_data);
|
||||
|
||||
priv = context->priv;
|
||||
|
||||
for (walk = priv->clients; walk; walk = g_list_next (walk)) {
|
||||
GDBusProxy *proxy = walk->data;
|
||||
PinosClientInfo info;
|
||||
|
||||
client_fill_info (&info, proxy);
|
||||
cb (context, &info, user_data);
|
||||
client_clear_info (&info);
|
||||
}
|
||||
|
||||
g_task_return_boolean (task, TRUE);
|
||||
g_object_unref (task);
|
||||
}
|
||||
|
||||
/**
|
||||
* pinos_context_get_client_info_by_id:
|
||||
* @context: a connected #PinosContext
|
||||
* @id: a client id
|
||||
* @flags: extra #PinosClientInfoFlags
|
||||
* @cb: a #PinosClientInfoCallback
|
||||
* @cancelable: a #GCancellable
|
||||
* @callback: a #GAsyncReadyCallback to call when the operation is finished
|
||||
* @user_data: user data passed to @cb
|
||||
*
|
||||
* Call @cb for the client with @id.
|
||||
*/
|
||||
void
|
||||
pinos_context_get_client_info_by_id (PinosContext *context,
|
||||
gpointer id,
|
||||
PinosClientInfoFlags flags,
|
||||
PinosClientInfoCallback cb,
|
||||
GCancellable *cancellable,
|
||||
GAsyncReadyCallback callback,
|
||||
gpointer user_data)
|
||||
{
|
||||
PinosClientInfo info;
|
||||
GDBusProxy *proxy;
|
||||
GTask *task;
|
||||
|
||||
g_return_if_fail (PINOS_IS_CONTEXT (context));
|
||||
g_return_if_fail (id != NULL);
|
||||
g_return_if_fail (cb != NULL);
|
||||
|
||||
task = g_task_new (context, cancellable, callback, user_data);
|
||||
|
||||
proxy = G_DBUS_PROXY (id);
|
||||
|
||||
client_fill_info (&info, proxy);
|
||||
cb (context, &info, user_data);
|
||||
client_clear_info (&info);
|
||||
|
||||
g_task_return_boolean (task, TRUE);
|
||||
g_object_unref (task);
|
||||
}
|
||||
|
||||
/**
|
||||
* pinos_source_state_as_string:
|
||||
* @state: a #PinosSourceState
|
||||
*
|
||||
* Return the string representation of @state.
|
||||
*
|
||||
* Returns: the string representation of @state.
|
||||
*/
|
||||
const gchar *
|
||||
pinos_source_state_as_string (PinosSourceState state)
|
||||
{
|
||||
GEnumValue *val;
|
||||
|
||||
val = g_enum_get_value (G_ENUM_CLASS (g_type_class_ref (PINOS_TYPE_SOURCE_STATE)),
|
||||
state);
|
||||
|
||||
return val == NULL ? "invalid-state" : val->value_nick;
|
||||
}
|
||||
|
||||
static void
|
||||
source_fill_info (PinosSourceInfo *info, GDBusProxy *proxy)
|
||||
{
|
||||
GHashTable *changed = g_object_get_data (G_OBJECT (proxy), "pinos-changed-properties");
|
||||
|
||||
info->id = proxy;
|
||||
info->source_path = g_dbus_proxy_get_object_path (proxy);
|
||||
|
||||
info->change_mask = 0;
|
||||
SET_STRING ("Name", name, 0);
|
||||
SET_PROPERTIES ("Properties", properties, 1);
|
||||
SET_UINT32 ("State", state, 2, PINOS_SOURCE_STATE_ERROR);
|
||||
SET_BYTES ("PossibleFormats", possible_formats, 3);
|
||||
|
||||
if (changed)
|
||||
g_hash_table_remove_all (changed);
|
||||
}
|
||||
|
||||
static void
|
||||
source_clear_info (PinosSourceInfo *info)
|
||||
{
|
||||
if (info->properties)
|
||||
pinos_properties_free (info->properties);
|
||||
if (info->possible_formats)
|
||||
g_bytes_unref (info->possible_formats);
|
||||
}
|
||||
|
||||
/**
|
||||
* pinos_context_list_source_info:
|
||||
* @context: a connected #PinosContext
|
||||
* @flags: extra #PinosSourceInfoFlags
|
||||
* @cb: a #PinosSourceInfoCallback
|
||||
* @cancelable: a #GCancellable
|
||||
* @callback: a #GAsyncReadyCallback to call when the operation is finished
|
||||
* @user_data: user data passed to @cb
|
||||
*
|
||||
* Call @cb for each source.
|
||||
*/
|
||||
void
|
||||
pinos_context_list_source_info (PinosContext *context,
|
||||
PinosSourceInfoFlags flags,
|
||||
PinosSourceInfoCallback cb,
|
||||
GCancellable *cancellable,
|
||||
GAsyncReadyCallback callback,
|
||||
gpointer user_data)
|
||||
{
|
||||
PinosContextPrivate *priv;
|
||||
GList *walk;
|
||||
GTask *task;
|
||||
|
||||
g_return_if_fail (PINOS_IS_CONTEXT (context));
|
||||
g_return_if_fail (cb != NULL);
|
||||
|
||||
task = g_task_new (context, cancellable, callback, user_data);
|
||||
|
||||
priv = context->priv;
|
||||
|
||||
for (walk = priv->sources; walk; walk = g_list_next (walk)) {
|
||||
GDBusProxy *proxy = walk->data;
|
||||
PinosSourceInfo info;
|
||||
|
||||
source_fill_info (&info, proxy);
|
||||
cb (context, &info, user_data);
|
||||
source_clear_info (&info);
|
||||
}
|
||||
|
||||
g_task_return_boolean (task, TRUE);
|
||||
g_object_unref (task);
|
||||
}
|
||||
|
||||
/**
|
||||
* pinos_context_get_source_info_by_id:
|
||||
* @context: a connected #PinosContext
|
||||
* @id: a source id
|
||||
* @flags: extra #PinosSourceInfoFlags
|
||||
* @cb: a #PinosSourceInfoCallback
|
||||
* @cancelable: a #GCancellable
|
||||
* @callback: a #GAsyncReadyCallback to call when the operation is finished
|
||||
* @user_data: user data passed to @cb
|
||||
*
|
||||
* Call @cb for the source with @id.
|
||||
*/
|
||||
void
|
||||
pinos_context_get_source_info_by_id (PinosContext *context,
|
||||
gpointer id,
|
||||
PinosSourceInfoFlags flags,
|
||||
PinosSourceInfoCallback cb,
|
||||
GCancellable *cancellable,
|
||||
GAsyncReadyCallback callback,
|
||||
gpointer user_data)
|
||||
{
|
||||
PinosSourceInfo info;
|
||||
GDBusProxy *proxy;
|
||||
GTask *task;
|
||||
|
||||
g_return_if_fail (PINOS_IS_CONTEXT (context));
|
||||
g_return_if_fail (id != NULL);
|
||||
g_return_if_fail (cb != NULL);
|
||||
|
||||
task = g_task_new (context, cancellable, callback, user_data);
|
||||
|
||||
proxy = G_DBUS_PROXY (id);
|
||||
|
||||
source_fill_info (&info, proxy);
|
||||
cb (context, &info, user_data);
|
||||
source_clear_info (&info);
|
||||
|
||||
g_task_return_boolean (task, TRUE);
|
||||
g_object_unref (task);
|
||||
}
|
||||
|
||||
/**
|
||||
* pinos_source_output_state_as_string:
|
||||
* @state: a #PinosSourceOutputState
|
||||
*
|
||||
* Return the string representation of @state.
|
||||
*
|
||||
* Returns: the string representation of @state.
|
||||
*/
|
||||
const gchar *
|
||||
pinos_source_output_state_as_string (PinosSourceOutputState state)
|
||||
{
|
||||
GEnumValue *val;
|
||||
|
||||
val = g_enum_get_value (G_ENUM_CLASS (g_type_class_ref (PINOS_TYPE_SOURCE_OUTPUT_STATE)),
|
||||
state);
|
||||
|
||||
return val == NULL ? "invalid-state" : val->value_nick;
|
||||
}
|
||||
|
||||
static void
|
||||
source_output_fill_info (PinosSourceOutputInfo *info, GDBusProxy *proxy)
|
||||
{
|
||||
GHashTable *changed = g_object_get_data (G_OBJECT (proxy), "pinos-changed-properties");
|
||||
|
||||
info->id = proxy;
|
||||
info->output_path = g_dbus_proxy_get_object_path (proxy);
|
||||
|
||||
info->change_mask = 0;
|
||||
SET_STRING ("Client", client_path, 0);
|
||||
SET_STRING ("Source", source_path, 1);
|
||||
SET_BYTES ("PossibleFormats", possible_formats, 2);
|
||||
SET_UINT32 ("State", state, 3, PINOS_SOURCE_OUTPUT_STATE_ERROR);
|
||||
SET_BYTES ("Format", format, 4);
|
||||
SET_PROPERTIES ("Properties", properties, 5);
|
||||
|
||||
if (changed)
|
||||
g_hash_table_remove_all (changed);
|
||||
}
|
||||
|
||||
static void
|
||||
source_output_clear_info (PinosSourceOutputInfo *info)
|
||||
{
|
||||
if (info->possible_formats)
|
||||
g_bytes_unref (info->possible_formats);
|
||||
if (info->properties)
|
||||
pinos_properties_free (info->properties);
|
||||
}
|
||||
|
||||
/**
|
||||
* pinos_context_list_source_output_info:
|
||||
* @context: a connected #PinosContext
|
||||
* @flags: extra #PinosSourceOutputInfoFlags
|
||||
* @cb: a #PinosSourceOutputInfoCallback
|
||||
* @cancelable: a #GCancellable
|
||||
* @callback: a #GAsyncReadyCallback to call when the operation is finished
|
||||
* @user_data: user data passed to @cb
|
||||
*
|
||||
* Call @cb for each source-output.
|
||||
*/
|
||||
void
|
||||
pinos_context_list_source_output_info (PinosContext *context,
|
||||
PinosSourceOutputInfoFlags flags,
|
||||
PinosSourceOutputInfoCallback cb,
|
||||
GCancellable *cancellable,
|
||||
GAsyncReadyCallback callback,
|
||||
gpointer user_data)
|
||||
{
|
||||
PinosContextPrivate *priv;
|
||||
GList *walk;
|
||||
GTask *task;
|
||||
|
||||
g_return_if_fail (PINOS_IS_CONTEXT (context));
|
||||
g_return_if_fail (cb != NULL);
|
||||
|
||||
task = g_task_new (context, cancellable, callback, user_data);
|
||||
|
||||
priv = context->priv;
|
||||
|
||||
for (walk = priv->source_outputs; walk; walk = g_list_next (walk)) {
|
||||
GDBusProxy *proxy = walk->data;
|
||||
PinosSourceOutputInfo info;
|
||||
|
||||
source_output_fill_info (&info, proxy);
|
||||
cb (context, &info, user_data);
|
||||
source_output_clear_info (&info);
|
||||
}
|
||||
|
||||
g_task_return_boolean (task, TRUE);
|
||||
g_object_unref (task);
|
||||
}
|
||||
|
||||
/**
|
||||
* pinos_context_get_source_output_info_by_id:
|
||||
* @context: a connected #PinosContext
|
||||
* @id: a source output id
|
||||
* @flags: extra #PinosSourceOutputInfoFlags
|
||||
* @cb: a #PinosSourceOutputInfoCallback
|
||||
* @cancelable: a #GCancellable
|
||||
* @callback: a #GAsyncReadyCallback to call when the operation is finished
|
||||
* @user_data: user data passed to @cb
|
||||
*
|
||||
* Call @cb for the source output with @id.
|
||||
*/
|
||||
void
|
||||
pinos_context_get_source_output_info_by_id (PinosContext *context,
|
||||
gpointer id,
|
||||
PinosSourceOutputInfoFlags flags,
|
||||
PinosSourceOutputInfoCallback cb,
|
||||
GCancellable *cancellable,
|
||||
GAsyncReadyCallback callback,
|
||||
gpointer user_data)
|
||||
{
|
||||
PinosSourceOutputInfo info;
|
||||
GDBusProxy *proxy;
|
||||
GTask *task;
|
||||
|
||||
g_return_if_fail (PINOS_IS_CONTEXT (context));
|
||||
g_return_if_fail (id != NULL);
|
||||
g_return_if_fail (cb != NULL);
|
||||
|
||||
task = g_task_new (context, cancellable, callback, user_data);
|
||||
|
||||
proxy = G_DBUS_PROXY (id);
|
||||
|
||||
source_output_fill_info (&info, proxy);
|
||||
cb (context, &info, user_data);
|
||||
source_output_clear_info (&info);
|
||||
|
||||
g_task_return_boolean (task, TRUE);
|
||||
g_object_unref (task);
|
||||
}
|
||||
|
||||
|
|
@ -1,314 +0,0 @@
|
|||
/* Pinos
|
||||
* Copyright (C) 2015 Wim Taymans <wim.taymans@gmail.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#ifndef __PINOS_INTROSPECT_H__
|
||||
#define __PINOS_INTROSPECT_H__
|
||||
|
||||
#include <gio/gio.h>
|
||||
#include <glib-object.h>
|
||||
|
||||
#include <client/context.h>
|
||||
#include <client/properties.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
gboolean pinos_context_info_finish (GObject *object,
|
||||
GAsyncResult *res,
|
||||
GError **error);
|
||||
|
||||
/**
|
||||
* PinosDaemonInfo:
|
||||
* @id: generic id of the daemon
|
||||
* @daemon-path: unique path of the daemon
|
||||
* @change_mask: bitfield of changed fields since last call
|
||||
* @user_name: name of the user that started the daemon
|
||||
* @host_name: name of the machine the daemon is running on
|
||||
* @version: version of the daemon
|
||||
* @name: name of the daemon
|
||||
* @cookie: a random cookie for identifying this instance of Pinos
|
||||
* @properties: extra properties
|
||||
*
|
||||
* The daemon information. Extra information can be added in later
|
||||
* versions.
|
||||
*/
|
||||
typedef struct {
|
||||
gpointer id;
|
||||
const char *daemon_path;
|
||||
guint64 change_mask;
|
||||
const char *user_name;
|
||||
const char *host_name;
|
||||
const char *version;
|
||||
const char *name;
|
||||
guint32 cookie;
|
||||
PinosProperties *properties;
|
||||
} PinosDaemonInfo;
|
||||
|
||||
/**PinosDaemonInfoFlags:
|
||||
* @PINOS_DAEMON_INFO_FLAGS_NONE: no flags
|
||||
*
|
||||
* Extra flags that can be passed to pinos_context_get_daemon_info()
|
||||
*/
|
||||
typedef enum {
|
||||
PINOS_DAEMON_INFO_FLAGS_NONE = 0,
|
||||
} PinosDaemonInfoFlags;
|
||||
|
||||
/**
|
||||
* PinosDaemonInfoCallback:
|
||||
* @c: a #PinosContext
|
||||
* @info: a #PinosDaemonInfo
|
||||
* @user_data: user data
|
||||
*
|
||||
* Callback with information about the Pinos daemon in @info.
|
||||
*/
|
||||
typedef void (*PinosDaemonInfoCallback) (PinosContext *c,
|
||||
const PinosDaemonInfo *info,
|
||||
gpointer user_data);
|
||||
|
||||
void pinos_context_get_daemon_info (PinosContext *context,
|
||||
PinosDaemonInfoFlags flags,
|
||||
PinosDaemonInfoCallback cb,
|
||||
GCancellable *cancellable,
|
||||
GAsyncReadyCallback callback,
|
||||
gpointer user_data);
|
||||
|
||||
/**
|
||||
* PinosClientInfo:
|
||||
* @id: generic id of the client
|
||||
* @client_path: unique path of the client
|
||||
* @change_mask: bitfield of changed fields since last call
|
||||
* @name: name of client
|
||||
* @properties: extra properties
|
||||
*
|
||||
* The client information. Extra information can be added in later
|
||||
* versions.
|
||||
*/
|
||||
typedef struct {
|
||||
gpointer id;
|
||||
const char *client_path;
|
||||
guint64 change_mask;
|
||||
const char *name;
|
||||
PinosProperties *properties;
|
||||
} PinosClientInfo;
|
||||
|
||||
/**
|
||||
* PinosClientInfoFlags:
|
||||
* @PINOS_CLIENT_INFO_FLAGS_NONE: no flags
|
||||
*
|
||||
* Extra flags for pinos_context_list_client_info() and
|
||||
* pinos_context_get_client_info_by_id().
|
||||
*/
|
||||
typedef enum {
|
||||
PINOS_CLIENT_INFO_FLAGS_NONE = 0,
|
||||
} PinosClientInfoFlags;
|
||||
|
||||
/**
|
||||
* PinosClientInfoCallback:
|
||||
* @c: a #PinosContext
|
||||
* @info: a #PinosClientInfo
|
||||
* @user_data: user data
|
||||
*
|
||||
* Callback with information about the Pinos client in @info.
|
||||
*/
|
||||
typedef void (*PinosClientInfoCallback) (PinosContext *c,
|
||||
const PinosClientInfo *info,
|
||||
gpointer user_data);
|
||||
|
||||
void pinos_context_list_client_info (PinosContext *context,
|
||||
PinosClientInfoFlags flags,
|
||||
PinosClientInfoCallback cb,
|
||||
GCancellable *cancellable,
|
||||
GAsyncReadyCallback callback,
|
||||
gpointer user_data);
|
||||
void pinos_context_get_client_info_by_id (PinosContext *context,
|
||||
gpointer id,
|
||||
PinosClientInfoFlags flags,
|
||||
PinosClientInfoCallback cb,
|
||||
GCancellable *cancellable,
|
||||
GAsyncReadyCallback callback,
|
||||
gpointer user_data);
|
||||
|
||||
/**
|
||||
* PinosSourceState:
|
||||
* @PINOS_SOURCE_STATE_ERROR: the source is in error
|
||||
* @PINOS_SOURCE_STATE_SUSPENDED: the source is suspended, the device might
|
||||
* be closed
|
||||
* @PINOS_SOURCE_STATE_INITIALIZING: the source is initializing, the device is
|
||||
* being opened and the capabilities are queried
|
||||
* @PINOS_SOURCE_STATE_IDLE: the source is running but there is no active
|
||||
* source-output
|
||||
* @PINOS_SOURCE_STATE_RUNNING: the source is running
|
||||
*
|
||||
* The different source states
|
||||
*/
|
||||
typedef enum {
|
||||
PINOS_SOURCE_STATE_ERROR = -1,
|
||||
PINOS_SOURCE_STATE_SUSPENDED = 0,
|
||||
PINOS_SOURCE_STATE_INITIALIZING = 1,
|
||||
PINOS_SOURCE_STATE_IDLE = 2,
|
||||
PINOS_SOURCE_STATE_RUNNING = 3,
|
||||
} PinosSourceState;
|
||||
|
||||
const gchar * pinos_source_state_as_string (PinosSourceState state);
|
||||
|
||||
/**
|
||||
* PinosSourceInfo:
|
||||
* @id: generic id of the source
|
||||
* @source_path: the unique path of the source, suitable for connecting
|
||||
* @change_mask: bitfield of changed fields since last call
|
||||
* @name: name the source, suitable for display
|
||||
* @properties: the properties of the source
|
||||
* @state: the current state of the source
|
||||
* @possible formats: the possible formats this source can produce
|
||||
*
|
||||
* The source information. Extra information can be added in later
|
||||
* versions.
|
||||
*/
|
||||
typedef struct {
|
||||
gpointer id;
|
||||
const char *source_path;
|
||||
guint64 change_mask;
|
||||
const char *name;
|
||||
PinosProperties *properties;
|
||||
PinosSourceState state;
|
||||
GBytes *possible_formats;
|
||||
} PinosSourceInfo;
|
||||
|
||||
/**
|
||||
* PinosSourceInfoFlags:
|
||||
* @PINOS_SOURCE_INFO_FLAGS_NONE: no flags
|
||||
* @PINOS_SOURCE_INFO_FLAGS_FORMATS: include formats
|
||||
*
|
||||
* Extra flags to pass to pinos_context_get_source_info_list.
|
||||
*/
|
||||
typedef enum {
|
||||
PINOS_SOURCE_INFO_FLAGS_NONE = 0,
|
||||
PINOS_SOURCE_INFO_FLAGS_FORMATS = (1 << 0)
|
||||
} PinosSourceInfoFlags;
|
||||
|
||||
/**
|
||||
* PinosSourceInfoCallback:
|
||||
* @c: a #PinosContext
|
||||
* @info: a #PinosSourceInfo
|
||||
* @user_data: user data
|
||||
*
|
||||
* Callback with information about the Pinos source in @info.
|
||||
*/
|
||||
typedef void (*PinosSourceInfoCallback) (PinosContext *c,
|
||||
const PinosSourceInfo *info,
|
||||
gpointer user_data);
|
||||
|
||||
void pinos_context_list_source_info (PinosContext *context,
|
||||
PinosSourceInfoFlags flags,
|
||||
PinosSourceInfoCallback cb,
|
||||
GCancellable *cancellable,
|
||||
GAsyncReadyCallback callback,
|
||||
gpointer user_data);
|
||||
void pinos_context_get_source_info_by_id (PinosContext *context,
|
||||
gpointer id,
|
||||
PinosSourceInfoFlags flags,
|
||||
PinosSourceInfoCallback cb,
|
||||
GCancellable *cancellable,
|
||||
GAsyncReadyCallback callback,
|
||||
gpointer user_data);
|
||||
|
||||
/**
|
||||
* PinosSourceState:
|
||||
* @PINOS_SOURCE_OUTPUT_STATE_ERROR: the source output is in error
|
||||
* @PINOS_SOURCE_OUTPUT_STATE_IDLE: the source output is idle
|
||||
* @PINOS_SOURCE_OUTPUT_STATE_STARTING: the source output is starting
|
||||
* @PINOS_SOURCE_OUTPUT_STATE_STREAMING: the source output is streaming
|
||||
*
|
||||
* The different source output states
|
||||
*/
|
||||
typedef enum {
|
||||
PINOS_SOURCE_OUTPUT_STATE_ERROR = -1,
|
||||
PINOS_SOURCE_OUTPUT_STATE_IDLE = 0,
|
||||
PINOS_SOURCE_OUTPUT_STATE_STARTING = 1,
|
||||
PINOS_SOURCE_OUTPUT_STATE_STREAMING = 2,
|
||||
} PinosSourceOutputState;
|
||||
|
||||
const gchar * pinos_source_output_state_as_string (PinosSourceOutputState state);
|
||||
|
||||
/**
|
||||
* PinosSourceOutputInfo:
|
||||
* @id: generic id of the output
|
||||
* @path: the unique path of the output
|
||||
* @change_mask: bitfield of changed fields since last call
|
||||
* @client_path: the owner client
|
||||
* @source_path: the source path
|
||||
* @possible_formats: the possible formats
|
||||
* @state: the state
|
||||
* @format: when streaming, the current format
|
||||
* @properties: the properties of the source
|
||||
*
|
||||
* The source information. Extra information can be added in later
|
||||
* versions.
|
||||
*/
|
||||
typedef struct {
|
||||
gpointer id;
|
||||
const char *output_path;
|
||||
guint64 change_mask;
|
||||
const char *client_path;
|
||||
const char *source_path;
|
||||
GBytes *possible_formats;
|
||||
PinosSourceOutputState state;
|
||||
GBytes *format;
|
||||
PinosProperties *properties;
|
||||
} PinosSourceOutputInfo;
|
||||
|
||||
/**
|
||||
* PinosSourceOutputInfoFlags:
|
||||
* @PINOS_SOURCE_OUTPUT_INFO_FLAGS_NONE: no flags
|
||||
*
|
||||
* Extra flags to pass to pinos_context_list_source_output_info() and
|
||||
* pinos_context_get_source_output_info_by_id().
|
||||
*/
|
||||
typedef enum {
|
||||
PINOS_SOURCE_OUTPUT_INFO_FLAGS_NONE = 0,
|
||||
} PinosSourceOutputInfoFlags;
|
||||
|
||||
/**
|
||||
* PinosSourceOutputInfoCallback:
|
||||
* @c: a #PinosContext
|
||||
* @info: a #PinosSourceOutputInfo
|
||||
* @user_data: user data
|
||||
*
|
||||
* Callback with information about the Pinos source output in @info.
|
||||
*/
|
||||
typedef void (*PinosSourceOutputInfoCallback) (PinosContext *c,
|
||||
const PinosSourceOutputInfo *info,
|
||||
gpointer user_data);
|
||||
|
||||
void pinos_context_list_source_output_info (PinosContext *context,
|
||||
PinosSourceOutputInfoFlags flags,
|
||||
PinosSourceOutputInfoCallback cb,
|
||||
GCancellable *cancellable,
|
||||
GAsyncReadyCallback callback,
|
||||
gpointer user_data);
|
||||
void pinos_context_get_source_output_info_by_id (PinosContext *context,
|
||||
gpointer id,
|
||||
PinosSourceOutputInfoFlags flags,
|
||||
PinosSourceOutputInfoCallback cb,
|
||||
GCancellable *cancellable,
|
||||
GAsyncReadyCallback callback,
|
||||
gpointer user_data);
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __PINOS_INTROSPECT_H__ */
|
||||
|
||||
|
|
@ -1,450 +0,0 @@
|
|||
/* Pinos
|
||||
* Copyright (C) 2015 Wim Taymans <wim.taymans@gmail.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include "mainloop.h"
|
||||
|
||||
struct _PinosMainLoopPrivate
|
||||
{
|
||||
GMainContext *maincontext;
|
||||
GMainLoop *mainloop;
|
||||
|
||||
gchar *name;
|
||||
|
||||
GPollFunc poll_func;
|
||||
|
||||
GMutex lock;
|
||||
GCond cond;
|
||||
GCond accept_cond;
|
||||
GThread *thread;
|
||||
|
||||
gint n_waiting;
|
||||
gint n_waiting_for_accept;
|
||||
};
|
||||
|
||||
#define PINOS_MAIN_LOOP_GET_PRIVATE(obj) \
|
||||
(G_TYPE_INSTANCE_GET_PRIVATE ((obj), PINOS_TYPE_MAIN_LOOP, PinosMainLoopPrivate))
|
||||
|
||||
G_DEFINE_TYPE (PinosMainLoop, pinos_main_loop, G_TYPE_OBJECT);
|
||||
|
||||
enum
|
||||
{
|
||||
PROP_0,
|
||||
PROP_MAIN_CONTEXT,
|
||||
PROP_NAME,
|
||||
PROP_MAIN_LOOP,
|
||||
};
|
||||
|
||||
static void
|
||||
pinos_main_loop_get_property (GObject *_object,
|
||||
guint prop_id,
|
||||
GValue *value,
|
||||
GParamSpec *pspec)
|
||||
{
|
||||
PinosMainLoop *loop = PINOS_MAIN_LOOP (_object);
|
||||
PinosMainLoopPrivate *priv = loop->priv;
|
||||
|
||||
switch (prop_id) {
|
||||
case PROP_MAIN_CONTEXT:
|
||||
g_value_set_boxed (value, priv->maincontext);
|
||||
break;
|
||||
|
||||
case PROP_NAME:
|
||||
g_value_set_string (value, priv->name);
|
||||
break;
|
||||
|
||||
case PROP_MAIN_LOOP:
|
||||
g_value_set_boxed (value, priv->mainloop);
|
||||
break;
|
||||
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (loop, prop_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
pinos_main_loop_set_property (GObject *_object,
|
||||
guint prop_id,
|
||||
const GValue *value,
|
||||
GParamSpec *pspec)
|
||||
{
|
||||
PinosMainLoop *loop = PINOS_MAIN_LOOP (_object);
|
||||
PinosMainLoopPrivate *priv = loop->priv;
|
||||
|
||||
switch (prop_id) {
|
||||
case PROP_MAIN_CONTEXT:
|
||||
priv->maincontext = g_value_dup_boxed (value);
|
||||
break;
|
||||
|
||||
case PROP_NAME:
|
||||
priv->name = g_value_dup_string (value);
|
||||
break;
|
||||
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (loop, prop_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
pinos_main_loop_constructed (GObject * object)
|
||||
{
|
||||
PinosMainLoop *loop = PINOS_MAIN_LOOP (object);
|
||||
PinosMainLoopPrivate *priv = loop->priv;
|
||||
|
||||
priv->mainloop = g_main_loop_new (priv->maincontext, FALSE);
|
||||
|
||||
G_OBJECT_CLASS (pinos_main_loop_parent_class)->constructed (object);
|
||||
}
|
||||
|
||||
static void
|
||||
pinos_main_loop_finalize (GObject * object)
|
||||
{
|
||||
PinosMainLoop *loop = PINOS_MAIN_LOOP (object);
|
||||
PinosMainLoopPrivate *priv = loop->priv;
|
||||
|
||||
if (priv->maincontext)
|
||||
g_main_context_unref (priv->maincontext);
|
||||
g_main_loop_unref (priv->mainloop);
|
||||
|
||||
g_free (priv->name);
|
||||
g_mutex_clear (&priv->lock);
|
||||
g_cond_clear (&priv->cond);
|
||||
g_cond_clear (&priv->accept_cond);
|
||||
|
||||
G_OBJECT_CLASS (pinos_main_loop_parent_class)->finalize (object);
|
||||
}
|
||||
|
||||
static void
|
||||
pinos_main_loop_class_init (PinosMainLoopClass * klass)
|
||||
{
|
||||
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
|
||||
|
||||
g_type_class_add_private (klass, sizeof (PinosMainLoopPrivate));
|
||||
|
||||
gobject_class->constructed = pinos_main_loop_constructed;
|
||||
gobject_class->finalize = pinos_main_loop_finalize;
|
||||
gobject_class->set_property = pinos_main_loop_set_property;
|
||||
gobject_class->get_property = pinos_main_loop_get_property;
|
||||
|
||||
/**
|
||||
* PinosMainLoop:main-context
|
||||
*
|
||||
* The GMainContext of the loop.
|
||||
*/
|
||||
g_object_class_install_property (gobject_class,
|
||||
PROP_MAIN_CONTEXT,
|
||||
g_param_spec_boxed ("main-context",
|
||||
"Main Context",
|
||||
"The GMainContext of the loop",
|
||||
G_TYPE_MAIN_CONTEXT,
|
||||
G_PARAM_READWRITE |
|
||||
G_PARAM_CONSTRUCT_ONLY |
|
||||
G_PARAM_STATIC_STRINGS));
|
||||
/**
|
||||
* PinosMainLoop:name
|
||||
*
|
||||
* The name of the loop as specified at construction time.
|
||||
*/
|
||||
g_object_class_install_property (gobject_class,
|
||||
PROP_NAME,
|
||||
g_param_spec_string ("name",
|
||||
"Name",
|
||||
"The name of the loop thread",
|
||||
NULL,
|
||||
G_PARAM_READWRITE |
|
||||
G_PARAM_CONSTRUCT_ONLY |
|
||||
G_PARAM_STATIC_STRINGS));
|
||||
/**
|
||||
* PinosMainLoop:main-loop
|
||||
*
|
||||
* The GMainLoop of the loop.
|
||||
*/
|
||||
g_object_class_install_property (gobject_class,
|
||||
PROP_MAIN_LOOP,
|
||||
g_param_spec_boxed ("main-loop",
|
||||
"Main Loop",
|
||||
"The GMainLoop",
|
||||
G_TYPE_MAIN_LOOP,
|
||||
G_PARAM_READABLE |
|
||||
G_PARAM_STATIC_STRINGS));
|
||||
}
|
||||
|
||||
static void
|
||||
pinos_main_loop_init (PinosMainLoop * loop)
|
||||
{
|
||||
PinosMainLoopPrivate *priv = loop->priv = PINOS_MAIN_LOOP_GET_PRIVATE (loop);
|
||||
|
||||
g_mutex_init (&priv->lock);
|
||||
g_cond_init (&priv->cond);
|
||||
g_cond_init (&priv->accept_cond);
|
||||
}
|
||||
|
||||
/**
|
||||
* pinos_main_loop_new:
|
||||
* @context: a #GMainContext
|
||||
* @name: a thread name
|
||||
*
|
||||
* Make a new #PinosMainLoop that will run a mainloop on @context in
|
||||
* a thread with @name.
|
||||
*
|
||||
* Returns: a #PinosMainLoop
|
||||
*/
|
||||
PinosMainLoop *
|
||||
pinos_main_loop_new (GMainContext * context, const gchar *name)
|
||||
{
|
||||
PinosMainLoop *loop;
|
||||
|
||||
loop = g_object_new (PINOS_TYPE_MAIN_LOOP,
|
||||
"main-context", context,
|
||||
"name", name,
|
||||
NULL);
|
||||
return loop;
|
||||
}
|
||||
|
||||
/**
|
||||
* pinos_main_loop_get_impl:
|
||||
* @loop: a #PinosMainLoop
|
||||
*
|
||||
* Get the #GMainLoop used by @loop.
|
||||
*
|
||||
* Returns: the #GMainLoop used by @loop. It remains valid as long as
|
||||
* @loop is valid.
|
||||
*/
|
||||
GMainLoop *
|
||||
pinos_main_loop_get_impl (PinosMainLoop *loop)
|
||||
{
|
||||
PinosMainLoopPrivate *priv;
|
||||
|
||||
g_return_val_if_fail (PINOS_IS_MAIN_LOOP (loop), NULL);
|
||||
|
||||
priv = loop->priv;
|
||||
|
||||
return priv->mainloop;
|
||||
}
|
||||
|
||||
static GPrivate loop_key;
|
||||
|
||||
static gint
|
||||
do_poll (GPollFD *ufds, guint nfsd, gint timeout_)
|
||||
{
|
||||
gint res;
|
||||
PinosMainLoop *loop = g_private_get (&loop_key);
|
||||
PinosMainLoopPrivate *priv = loop->priv;
|
||||
|
||||
g_mutex_unlock (&priv->lock);
|
||||
res = priv->poll_func (ufds, nfsd, timeout_);
|
||||
g_mutex_lock (&priv->lock);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static gpointer
|
||||
handle_mainloop (PinosMainLoop *loop)
|
||||
{
|
||||
PinosMainLoopPrivate *priv = loop->priv;
|
||||
|
||||
g_mutex_lock (&priv->lock);
|
||||
g_private_set (&loop_key, loop);
|
||||
|
||||
priv->poll_func = g_main_context_get_poll_func (priv->maincontext);
|
||||
g_main_context_set_poll_func (priv->maincontext, do_poll);
|
||||
|
||||
g_main_context_push_thread_default (priv->maincontext);
|
||||
g_main_loop_run (priv->mainloop);
|
||||
g_main_context_pop_thread_default (priv->maincontext);
|
||||
|
||||
g_main_context_set_poll_func (priv->maincontext, priv->poll_func);
|
||||
|
||||
g_mutex_unlock (&priv->lock);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* pinos_main_loop_start:
|
||||
* @loop: a #PinosMainLoop
|
||||
* @error: am optional #GError
|
||||
*
|
||||
* Start the thread to handle @loop.
|
||||
*
|
||||
* Returns: %TRUE on success. %FALSE will be returned when an error occured
|
||||
* and @error will contain more information.
|
||||
*/
|
||||
gboolean
|
||||
pinos_main_loop_start (PinosMainLoop *loop, GError **error)
|
||||
{
|
||||
PinosMainLoopPrivate *priv;
|
||||
|
||||
g_return_val_if_fail (PINOS_IS_MAIN_LOOP (loop), FALSE);
|
||||
priv = loop->priv;
|
||||
g_return_val_if_fail (priv->thread == NULL, FALSE);
|
||||
|
||||
priv->thread = g_thread_try_new (priv->name, (GThreadFunc) handle_mainloop, loop, error);
|
||||
|
||||
return priv->thread != NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* pinos_main_loop_stop:
|
||||
* @loop: a #PinosMainLoop
|
||||
*
|
||||
* Quit the main loop and stop its thread.
|
||||
*/
|
||||
void
|
||||
pinos_main_loop_stop (PinosMainLoop *loop)
|
||||
{
|
||||
PinosMainLoopPrivate *priv;
|
||||
|
||||
g_return_if_fail (PINOS_IS_MAIN_LOOP (loop));
|
||||
priv = loop->priv;
|
||||
|
||||
g_return_if_fail (priv->thread != NULL);
|
||||
g_return_if_fail (!pinos_main_loop_in_thread (loop));
|
||||
|
||||
g_mutex_lock (&priv->lock);
|
||||
g_main_loop_quit (priv->mainloop);
|
||||
g_mutex_unlock (&priv->lock);
|
||||
|
||||
g_thread_join (priv->thread);
|
||||
priv->thread = NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* pinos_main_loop_lock:
|
||||
* @loop: a #PinosMainLoop
|
||||
*
|
||||
* Lock the mutex associated with @loop.
|
||||
*/
|
||||
void
|
||||
pinos_main_loop_lock (PinosMainLoop *loop)
|
||||
{
|
||||
PinosMainLoopPrivate *priv;
|
||||
|
||||
g_return_if_fail (PINOS_IS_MAIN_LOOP (loop));
|
||||
priv = loop->priv;
|
||||
g_return_if_fail (!pinos_main_loop_in_thread (loop));
|
||||
|
||||
g_mutex_lock (&priv->lock);
|
||||
}
|
||||
|
||||
/**
|
||||
* pinos_main_loop_unlock:
|
||||
* @loop: a #PinosMainLoop
|
||||
*
|
||||
* Unlock the mutex associated with @loop.
|
||||
*/
|
||||
void
|
||||
pinos_main_loop_unlock (PinosMainLoop *loop)
|
||||
{
|
||||
PinosMainLoopPrivate *priv;
|
||||
|
||||
g_return_if_fail (PINOS_IS_MAIN_LOOP (loop));
|
||||
priv = loop->priv;
|
||||
g_return_if_fail (!pinos_main_loop_in_thread (loop));
|
||||
|
||||
g_mutex_unlock (&priv->lock);
|
||||
}
|
||||
|
||||
/**
|
||||
* pinos_main_loop_signal:
|
||||
* @loop: a #PinosMainLoop
|
||||
*
|
||||
* Signal the main thread of @loop. If @wait_for_accept is %TRUE,
|
||||
* this function waits until pinos_main_loop_accept() is called.
|
||||
*/
|
||||
void
|
||||
pinos_main_loop_signal (PinosMainLoop *loop, gboolean wait_for_accept)
|
||||
{
|
||||
PinosMainLoopPrivate *priv;
|
||||
|
||||
g_return_if_fail (PINOS_IS_MAIN_LOOP (loop));
|
||||
priv = loop->priv;
|
||||
|
||||
if (priv->n_waiting > 0)
|
||||
g_cond_broadcast (&priv->cond);
|
||||
|
||||
if (wait_for_accept) {
|
||||
priv->n_waiting_for_accept++;
|
||||
|
||||
while (priv->n_waiting_for_accept > 0)
|
||||
g_cond_wait (&priv->accept_cond, &priv->lock);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* pinos_main_loop_wait:
|
||||
* @loop: a #PinosMainLoop
|
||||
*
|
||||
* Wait for the loop thread to call pinos_main_loop_signal().
|
||||
*/
|
||||
void
|
||||
pinos_main_loop_wait (PinosMainLoop *loop)
|
||||
{
|
||||
PinosMainLoopPrivate *priv;
|
||||
|
||||
g_return_if_fail (PINOS_IS_MAIN_LOOP (loop));
|
||||
priv = loop->priv;
|
||||
g_return_if_fail (!pinos_main_loop_in_thread (loop));
|
||||
|
||||
priv->n_waiting ++;
|
||||
|
||||
g_cond_wait (&priv->cond, &priv->lock);
|
||||
|
||||
g_assert (priv->n_waiting > 0);
|
||||
priv->n_waiting --;
|
||||
}
|
||||
|
||||
/**
|
||||
* pinos_main_loop_accept:
|
||||
* @loop: a #PinosMainLoop
|
||||
*
|
||||
* Signal the loop thread waiting for accept with pinos_main_loop_signal().
|
||||
*/
|
||||
void
|
||||
pinos_main_loop_accept (PinosMainLoop *loop)
|
||||
{
|
||||
PinosMainLoopPrivate *priv;
|
||||
|
||||
g_return_if_fail (PINOS_IS_MAIN_LOOP (loop));
|
||||
priv = loop->priv;
|
||||
g_return_if_fail (!pinos_main_loop_in_thread (loop));
|
||||
|
||||
g_assert (priv->n_waiting_for_accept > 0);
|
||||
priv->n_waiting_for_accept--;
|
||||
|
||||
g_cond_signal (&priv->accept_cond);
|
||||
}
|
||||
|
||||
/**
|
||||
* pinos_main_loop_in_thread:
|
||||
* @loop: a #PinosMainLoop
|
||||
*
|
||||
* Check if we are inside the thread of @loop.
|
||||
*
|
||||
* Returns: %TRUE when called inside the thread of @loop.
|
||||
*/
|
||||
gboolean
|
||||
pinos_main_loop_in_thread (PinosMainLoop *loop)
|
||||
{
|
||||
g_return_val_if_fail (PINOS_IS_MAIN_LOOP (loop), FALSE);
|
||||
|
||||
return g_thread_self() == loop->priv->thread;
|
||||
}
|
||||
|
|
@ -1,85 +0,0 @@
|
|||
/* Pinos
|
||||
* Copyright (C) 2015 Wim Taymans <wim.taymans@gmail.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#ifndef __PINOS_MAIN_LOOP_H__
|
||||
#define __PINOS_MAIN_LOOP_H__
|
||||
|
||||
#include <glib-object.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define PINOS_TYPE_MAIN_LOOP (pinos_main_loop_get_type ())
|
||||
#define PINOS_IS_MAIN_LOOP(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), PINOS_TYPE_MAIN_LOOP))
|
||||
#define PINOS_IS_MAIN_LOOP_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), PINOS_TYPE_MAIN_LOOP))
|
||||
#define PINOS_MAIN_LOOP_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), PINOS_TYPE_MAIN_LOOP, PinosMainLoopClass))
|
||||
#define PINOS_MAIN_LOOP(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), PINOS_TYPE_MAIN_LOOP, PinosMainLoop))
|
||||
#define PINOS_MAIN_LOOP_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), PINOS_TYPE_MAIN_LOOP, PinosMainLoopClass))
|
||||
#define PINOS_MAIN_LOOP_CAST(obj) ((PinosMainLoop*)(obj))
|
||||
#define PINOS_MAIN_LOOP_CLASS_CAST(klass) ((PinosMainLoopClass*)(klass))
|
||||
|
||||
typedef struct _PinosMainLoop PinosMainLoop;
|
||||
typedef struct _PinosMainLoopClass PinosMainLoopClass;
|
||||
typedef struct _PinosMainLoopPrivate PinosMainLoopPrivate;
|
||||
|
||||
|
||||
/**
|
||||
* PinosMainLoop:
|
||||
*
|
||||
* Pinos main loop object class.
|
||||
*/
|
||||
struct _PinosMainLoop {
|
||||
GObject object;
|
||||
|
||||
PinosMainLoopPrivate *priv;
|
||||
};
|
||||
|
||||
/**
|
||||
* PinosMainLoopClass:
|
||||
*
|
||||
* Pinos main loop object class.
|
||||
*/
|
||||
struct _PinosMainLoopClass {
|
||||
GObjectClass parent_class;
|
||||
};
|
||||
|
||||
/* normal GObject stuff */
|
||||
GType pinos_main_loop_get_type (void);
|
||||
|
||||
PinosMainLoop * pinos_main_loop_new (GMainContext * context,
|
||||
const gchar *name);
|
||||
|
||||
GMainLoop * pinos_main_loop_get_impl (PinosMainLoop *loop);
|
||||
|
||||
gboolean pinos_main_loop_start (PinosMainLoop *loop, GError **error);
|
||||
void pinos_main_loop_stop (PinosMainLoop *loop);
|
||||
|
||||
void pinos_main_loop_lock (PinosMainLoop *loop);
|
||||
void pinos_main_loop_unlock (PinosMainLoop *loop);
|
||||
|
||||
void pinos_main_loop_wait (PinosMainLoop *loop);
|
||||
void pinos_main_loop_signal (PinosMainLoop *loop, gboolean wait_for_accept);
|
||||
void pinos_main_loop_accept (PinosMainLoop *loop);
|
||||
|
||||
gboolean pinos_main_loop_in_thread (PinosMainLoop *loop);
|
||||
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __PINOS_MAIN_LOOP_H__ */
|
||||
|
||||
|
|
@ -1,95 +0,0 @@
|
|||
/* Pinos
|
||||
* Copyright (C) 2015 Wim Taymans <wim.taymans@gmail.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include <gst/gst.h>
|
||||
|
||||
#include "client/pinos.h"
|
||||
|
||||
/**
|
||||
* pinos_init:
|
||||
* @argc: pointer to argc
|
||||
* @argv: pointer to argv
|
||||
*
|
||||
* initialize the pinos system, parse and modify any parameters given
|
||||
* by @argc and @argv.
|
||||
*/
|
||||
void
|
||||
pinos_init (int *argc, char **argv[])
|
||||
{
|
||||
gst_init (argc, argv);
|
||||
}
|
||||
|
||||
/**
|
||||
* pinos_client_name:
|
||||
*
|
||||
* Make a new pinos client name that can be used to construct a context.
|
||||
*/
|
||||
gchar *
|
||||
pinos_client_name (void)
|
||||
{
|
||||
const char *c;
|
||||
|
||||
if ((c = g_get_application_name ()))
|
||||
return g_strdup (c);
|
||||
else if ((c = g_get_prgname ()))
|
||||
return g_strdup (c);
|
||||
else
|
||||
return g_strdup_printf ("pinos-pid-%lu", (gulong) getpid ());
|
||||
}
|
||||
|
||||
void
|
||||
pinos_fill_context_properties (PinosProperties *properties)
|
||||
{
|
||||
g_return_if_fail (properties != NULL);
|
||||
|
||||
if (!pinos_properties_get (properties, "application.name"))
|
||||
pinos_properties_set (properties, "application.name", g_get_application_name ());
|
||||
|
||||
if (!pinos_properties_get (properties, "application.prgname"))
|
||||
pinos_properties_set (properties, "application.prgname", g_get_prgname ());
|
||||
|
||||
if (!pinos_properties_get (properties, "application.language")) {
|
||||
const gchar *str = g_getenv ("LANG");
|
||||
if (str)
|
||||
pinos_properties_set (properties, "application.language", str);
|
||||
}
|
||||
if (!pinos_properties_get (properties, "application.process.id")) {
|
||||
gchar *str = g_strdup_printf ("%lu", (gulong) getpid());
|
||||
pinos_properties_set (properties, "application.process.id", str);
|
||||
g_free (str);
|
||||
}
|
||||
if (!pinos_properties_get (properties, "application.process.user"))
|
||||
pinos_properties_set (properties, "application.process.user", g_get_user_name ());
|
||||
|
||||
if (!pinos_properties_get (properties, "application.process.host"))
|
||||
pinos_properties_set (properties, "application.process.host", g_get_host_name ());
|
||||
|
||||
if (!pinos_properties_get (properties, "application.process.session_id")) {
|
||||
const gchar *str = g_getenv ("XDG_SESSION_ID");
|
||||
if (str)
|
||||
pinos_properties_set (properties, "application.process.session_id", str);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
pinos_fill_stream_properties (PinosProperties *properties)
|
||||
{
|
||||
g_return_if_fail (properties != NULL);
|
||||
}
|
||||
|
||||
|
|
@ -1,44 +0,0 @@
|
|||
/* Pinos
|
||||
* Copyright (C) 2015 Wim Taymans <wim.taymans@gmail.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#ifndef __PINOS_H__
|
||||
#define __PINOS_H__
|
||||
|
||||
#include <client/buffer.h>
|
||||
#include <client/context.h>
|
||||
#include <client/introspect.h>
|
||||
#include <client/mainloop.h>
|
||||
#include <client/properties.h>
|
||||
#include <client/stream.h>
|
||||
#include <client/subscribe.h>
|
||||
|
||||
#define PINOS_DBUS_SERVICE "org.pinos"
|
||||
#define PINOS_DBUS_OBJECT_PREFIX "/org/pinos"
|
||||
#define PINOS_DBUS_OBJECT_SERVER PINOS_DBUS_OBJECT_PREFIX "/server"
|
||||
#define PINOS_DBUS_OBJECT_SOURCE PINOS_DBUS_OBJECT_PREFIX "/source"
|
||||
#define PINOS_DBUS_OBJECT_CLIENT PINOS_DBUS_OBJECT_PREFIX "/client"
|
||||
|
||||
void pinos_init (int *argc, char **argv[]);
|
||||
|
||||
gchar *pinos_client_name (void);
|
||||
|
||||
void pinos_fill_context_properties (PinosProperties *properties);
|
||||
void pinos_fill_stream_properties (PinosProperties *properties);
|
||||
|
||||
#endif /* __PINOS_H__ */
|
||||
|
|
@ -1,76 +0,0 @@
|
|||
/* Pinos
|
||||
* Copyright (C) 2015 Wim Taymans <wim.taymans@gmail.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
struct _PinosContextPrivate
|
||||
{
|
||||
GMainContext *context;
|
||||
|
||||
gchar *name;
|
||||
PinosProperties *properties;
|
||||
|
||||
guint id;
|
||||
GDBusConnection *connection;
|
||||
|
||||
PinosContextFlags flags;
|
||||
|
||||
PinosContextState state;
|
||||
GError *error;
|
||||
|
||||
GDBusProxy *daemon;
|
||||
GDBusProxy *client;
|
||||
gboolean disconnecting;
|
||||
|
||||
PinosSubscriptionFlags subscription_mask;
|
||||
PinosSubscribe *subscribe;
|
||||
|
||||
GList *clients;
|
||||
GList *sources;
|
||||
GList *source_outputs;
|
||||
};
|
||||
|
||||
void pinos_subscribe_get_proxy (PinosSubscribe *subscribe,
|
||||
const gchar *name,
|
||||
const gchar *object_path,
|
||||
const gchar *interface_name,
|
||||
GCancellable *cancellable,
|
||||
GAsyncReadyCallback callback,
|
||||
gpointer user_data);
|
||||
GDBusProxy * pinos_subscribe_get_proxy_finish (PinosSubscribe *subscribe,
|
||||
GAsyncResult *res,
|
||||
GError **error);
|
||||
|
||||
|
||||
typedef struct {
|
||||
guint32 version;
|
||||
guint32 length;
|
||||
} PinosStackHeader;
|
||||
|
||||
typedef struct {
|
||||
gsize allocated_size;
|
||||
gsize size;
|
||||
gpointer data;
|
||||
GSocketControlMessage *message;
|
||||
gsize magic;
|
||||
} PinosStackBuffer;
|
||||
|
||||
#define PSB(b) ((PinosStackBuffer *) (b))
|
||||
#define PSB_MAGIC ((gsize) 5493683301u)
|
||||
#define is_valid_buffer(b) (b != NULL && \
|
||||
PSB(b)->magic == PSB_MAGIC)
|
||||
|
||||
|
|
@ -1,311 +0,0 @@
|
|||
/* Pinos
|
||||
* Copyright (C) 2015 Wim Taymans <wim.taymans@gmail.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include <glib-object.h>
|
||||
|
||||
#include "client/properties.h"
|
||||
|
||||
struct _PinosProperties {
|
||||
GHashTable *hashtable;
|
||||
};
|
||||
|
||||
static void
|
||||
copy_func (const gchar *key, const gchar *value, GHashTable *copy)
|
||||
{
|
||||
g_hash_table_insert (copy, g_strdup (key), g_strdup (value));
|
||||
}
|
||||
|
||||
/**
|
||||
* pinos_properties_new:
|
||||
* @key: first key
|
||||
* @...: value
|
||||
*
|
||||
* Make a new #PinosProperties with given, NULL-terminated key/value pairs.
|
||||
*
|
||||
* Returns: a new #PinosProperties
|
||||
*/
|
||||
PinosProperties *
|
||||
pinos_properties_new (const gchar *key, ...)
|
||||
{
|
||||
PinosProperties *props;
|
||||
va_list varargs;
|
||||
const gchar *value;
|
||||
|
||||
props = g_new (PinosProperties, 1);
|
||||
props->hashtable = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
|
||||
|
||||
va_start (varargs, key);
|
||||
while (key != NULL) {
|
||||
value = va_arg (varargs, gchar *);
|
||||
copy_func (key, value, props->hashtable);
|
||||
key = va_arg (varargs, gchar *);
|
||||
}
|
||||
va_end (varargs);
|
||||
|
||||
return props;
|
||||
}
|
||||
|
||||
/**
|
||||
* pinos_properties_copy:
|
||||
* @properties: a #PinosProperties
|
||||
*
|
||||
* Make a copy of @properties.
|
||||
*
|
||||
* Returns: a copy of @properties
|
||||
*/
|
||||
PinosProperties *
|
||||
pinos_properties_copy (PinosProperties *properties)
|
||||
{
|
||||
PinosProperties *copy;
|
||||
|
||||
g_return_val_if_fail (properties != NULL, NULL);
|
||||
|
||||
copy = pinos_properties_new (NULL, NULL);
|
||||
g_hash_table_foreach (properties->hashtable, (GHFunc) copy_func, copy->hashtable);
|
||||
|
||||
return copy;
|
||||
}
|
||||
|
||||
/**
|
||||
* pinos_properties_free:
|
||||
* @properties: a #PinosProperties
|
||||
*
|
||||
* Free @properties
|
||||
*/
|
||||
void
|
||||
pinos_properties_free (PinosProperties *properties)
|
||||
{
|
||||
g_return_if_fail (properties != NULL);
|
||||
|
||||
g_hash_table_unref (properties->hashtable);
|
||||
g_free (properties);
|
||||
}
|
||||
|
||||
/**
|
||||
* pinos_properties_set:
|
||||
* @properties: a #PinosProperties
|
||||
* @key: a key
|
||||
* @value: a value
|
||||
*
|
||||
* Set the property in @properties with @key to @value. Any previous value
|
||||
* of @key will be overwritten.
|
||||
*/
|
||||
void
|
||||
pinos_properties_set (PinosProperties *properties,
|
||||
const gchar *key,
|
||||
const gchar *value)
|
||||
{
|
||||
g_return_if_fail (properties != NULL);
|
||||
g_return_if_fail (key != NULL);
|
||||
g_return_if_fail (value != NULL);
|
||||
|
||||
g_hash_table_replace (properties->hashtable, g_strdup (key), g_strdup (value));
|
||||
}
|
||||
|
||||
/**
|
||||
* pinos_properties_setf:
|
||||
* @properties: a #PinosProperties
|
||||
* @key: a key
|
||||
* @format: a value
|
||||
* @...: extra arguments
|
||||
*
|
||||
* Set the property in @properties with @key to the value in printf style @format
|
||||
* Any previous value of @key will be overwritten.
|
||||
*/
|
||||
void
|
||||
pinos_properties_setf (PinosProperties *properties,
|
||||
const gchar *key,
|
||||
const gchar *format,
|
||||
...)
|
||||
{
|
||||
va_list varargs;
|
||||
|
||||
g_return_if_fail (properties != NULL);
|
||||
g_return_if_fail (key != NULL);
|
||||
g_return_if_fail (format != NULL);
|
||||
|
||||
va_start (varargs, format);
|
||||
g_hash_table_replace (properties->hashtable,
|
||||
g_strdup (key),
|
||||
g_strdup_vprintf (format, varargs));
|
||||
va_end (varargs);
|
||||
}
|
||||
|
||||
/**
|
||||
* pinos_properties_get:
|
||||
* @properties: a #PinosProperties
|
||||
* @key: a key
|
||||
*
|
||||
* Get the property in @properties with @key.
|
||||
*
|
||||
* Returns: the property for @key or %NULL when the key was not found
|
||||
*/
|
||||
const gchar *
|
||||
pinos_properties_get (PinosProperties *properties,
|
||||
const gchar *key)
|
||||
{
|
||||
g_return_val_if_fail (properties != NULL, NULL);
|
||||
g_return_val_if_fail (key != NULL, NULL);
|
||||
|
||||
return g_hash_table_lookup (properties->hashtable, key);
|
||||
}
|
||||
|
||||
/**
|
||||
* pinos_properties_remove:
|
||||
* @properties: a #PinosProperties
|
||||
* @key: a key
|
||||
*
|
||||
* Remove the property in @properties with @key.
|
||||
*/
|
||||
void
|
||||
pinos_properties_remove (PinosProperties *properties,
|
||||
const gchar *key)
|
||||
{
|
||||
g_return_if_fail (properties != NULL);
|
||||
g_return_if_fail (key != NULL);
|
||||
|
||||
g_hash_table_remove (properties->hashtable, key);
|
||||
}
|
||||
|
||||
/**
|
||||
* pinos_properties_iterate:
|
||||
* @properties: a #PinosProperties
|
||||
* @state: state
|
||||
*
|
||||
* Iterate over @properties, returning each key in turn. @state should point
|
||||
* to a pointer holding %NULL to get the first element and will be updated
|
||||
* after each iteration. When %NULL is returned, all elements have been
|
||||
* iterated.
|
||||
*
|
||||
* Aborting the iteration before %NULL is returned might cause memory leaks.
|
||||
*
|
||||
* Returns: The next key or %NULL when there are no more keys to iterate.
|
||||
*/
|
||||
const gchar *
|
||||
pinos_properties_iterate (PinosProperties *properties,
|
||||
gpointer *state)
|
||||
{
|
||||
static gpointer dummy = GINT_TO_POINTER (1);
|
||||
const gchar *res = NULL;
|
||||
GList *items;
|
||||
|
||||
g_return_val_if_fail (properties != NULL, NULL);
|
||||
g_return_val_if_fail (state != NULL, NULL);
|
||||
|
||||
if (*state == dummy)
|
||||
return NULL;
|
||||
|
||||
if (*state == NULL) {
|
||||
items = g_hash_table_get_keys (properties->hashtable);
|
||||
} else {
|
||||
items = *state;
|
||||
}
|
||||
|
||||
if (items) {
|
||||
res = items->data;
|
||||
items = g_list_delete_link (items, items);
|
||||
if (items == NULL)
|
||||
items = dummy;
|
||||
}
|
||||
*state = items;
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static void
|
||||
add_to_variant (const gchar *key, const gchar *value, GVariantBuilder *b)
|
||||
{
|
||||
g_variant_builder_add (b, "{sv}", key, g_variant_new_string (value));
|
||||
}
|
||||
|
||||
/**
|
||||
* pinos_properties_init_builder:
|
||||
* @properties: a #PinosProperties
|
||||
* @builder: a #GVariantBuilder
|
||||
*
|
||||
* Initialize the @builder of type a{sv} and add @properties to it.
|
||||
*
|
||||
* Returns: %TRUE if @builder could be initialized.
|
||||
*/
|
||||
gboolean
|
||||
pinos_properties_init_builder (PinosProperties *properties,
|
||||
GVariantBuilder *builder)
|
||||
{
|
||||
g_return_val_if_fail (properties != NULL, FALSE);
|
||||
g_return_val_if_fail (builder != NULL, FALSE);
|
||||
|
||||
g_variant_builder_init (builder, G_VARIANT_TYPE ("a{sv}"));
|
||||
g_hash_table_foreach (properties->hashtable, (GHFunc) add_to_variant, builder);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* pinos_properties_to_variant:
|
||||
* @properties: a #PinosProperties
|
||||
*
|
||||
* Convert @properties to a #GVariant of type a{sv}
|
||||
*
|
||||
* Returns: a new #GVariant of @properties. use g_variant_unref() after
|
||||
* use.
|
||||
*/
|
||||
GVariant *
|
||||
pinos_properties_to_variant (PinosProperties *properties)
|
||||
{
|
||||
GVariantBuilder builder;
|
||||
|
||||
if (!pinos_properties_init_builder (properties, &builder))
|
||||
return NULL;
|
||||
|
||||
return g_variant_builder_end (&builder);
|
||||
}
|
||||
|
||||
/**
|
||||
* pinos_properties_from_variant:
|
||||
* @variant: a #GVariant
|
||||
*
|
||||
* Convert @variant to a #PinosProperties
|
||||
*
|
||||
* Returns: a new #PinosProperties of @variant. use pinos_properties_free()
|
||||
* after use.
|
||||
*/
|
||||
PinosProperties *
|
||||
pinos_properties_from_variant (GVariant *variant)
|
||||
{
|
||||
PinosProperties *props;
|
||||
GVariantIter iter;
|
||||
GVariant *value;
|
||||
gchar *key;
|
||||
|
||||
g_return_val_if_fail (variant != NULL, NULL);
|
||||
|
||||
props = pinos_properties_new (NULL, NULL);
|
||||
|
||||
g_variant_iter_init (&iter, variant);
|
||||
while (g_variant_iter_loop (&iter, "{sv}", &key, &value))
|
||||
g_hash_table_replace (props->hashtable,
|
||||
g_strdup (key),
|
||||
g_variant_dup_string (value, NULL));
|
||||
|
||||
return props;
|
||||
}
|
||||
|
||||
G_DEFINE_BOXED_TYPE (PinosProperties, pinos_properties,
|
||||
pinos_properties_copy, pinos_properties_free);
|
||||
|
||||
|
|
@ -1,60 +0,0 @@
|
|||
/* Pinos
|
||||
* Copyright (C) 2015 Wim Taymans <wim.taymans@gmail.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#ifndef __PINOS_PROPERTIES_H__
|
||||
#define __PINOS_PROPERTIES_H__
|
||||
|
||||
#include <glib.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
typedef struct _PinosProperties PinosProperties;
|
||||
|
||||
#define PINOS_TYPE_PROPERTIES (pinos_properties_get_type())
|
||||
GType pinos_properties_get_type (void);
|
||||
|
||||
PinosProperties * pinos_properties_new (const gchar *key, ...) G_GNUC_NULL_TERMINATED;
|
||||
PinosProperties * pinos_properties_copy (PinosProperties *properties);
|
||||
void pinos_properties_free (PinosProperties *properties);
|
||||
|
||||
void pinos_properties_set (PinosProperties *properties,
|
||||
const gchar *key,
|
||||
const gchar *value);
|
||||
void pinos_properties_setf (PinosProperties *properties,
|
||||
const gchar *key,
|
||||
const gchar *format,
|
||||
...) G_GNUC_PRINTF (3, 4);
|
||||
const gchar * pinos_properties_get (PinosProperties *properties,
|
||||
const gchar *key);
|
||||
void pinos_properties_remove (PinosProperties *properties,
|
||||
const gchar *key);
|
||||
|
||||
const gchar * pinos_properties_iterate (PinosProperties *properties,
|
||||
gpointer *state);
|
||||
|
||||
gboolean pinos_properties_init_builder (PinosProperties *properties,
|
||||
GVariantBuilder *builder);
|
||||
GVariant * pinos_properties_to_variant (PinosProperties *properties);
|
||||
PinosProperties * pinos_properties_from_variant (GVariant *variant);
|
||||
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __PINOS_PROPERTIES_H__ */
|
||||
|
||||
1254
src/client/stream.c
1254
src/client/stream.c
File diff suppressed because it is too large
Load diff
|
|
@ -1,116 +0,0 @@
|
|||
/* Pinos
|
||||
* Copyright (C) 2015 Wim Taymans <wim.taymans@gmail.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#ifndef __PINOS_STREAM_H__
|
||||
#define __PINOS_STREAM_H__
|
||||
|
||||
#include <glib-object.h>
|
||||
|
||||
#include <client/buffer.h>
|
||||
#include <client/context.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define PINOS_TYPE_STREAM (pinos_stream_get_type ())
|
||||
#define PINOS_IS_STREAM(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), PINOS_TYPE_STREAM))
|
||||
#define PINOS_IS_STREAM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), PINOS_TYPE_STREAM))
|
||||
#define PINOS_STREAM_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), PINOS_TYPE_STREAM, PinosStreamClass))
|
||||
#define PINOS_STREAM(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), PINOS_TYPE_STREAM, PinosStream))
|
||||
#define PINOS_STREAM_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), PINOS_TYPE_STREAM, PinosStreamClass))
|
||||
#define PINOS_STREAM_CAST(obj) ((PinosStream*)(obj))
|
||||
#define PINOS_STREAM_CLASS_CAST(klass) ((PinosStreamClass*)(klass))
|
||||
|
||||
typedef struct _PinosStream PinosStream;
|
||||
typedef struct _PinosStreamClass PinosStreamClass;
|
||||
typedef struct _PinosStreamPrivate PinosStreamPrivate;
|
||||
|
||||
typedef enum {
|
||||
PINOS_STREAM_STATE_ERROR = -1,
|
||||
PINOS_STREAM_STATE_UNCONNECTED = 0,
|
||||
PINOS_STREAM_STATE_CONNECTING = 1,
|
||||
PINOS_STREAM_STATE_READY = 2,
|
||||
PINOS_STREAM_STATE_STARTING = 3,
|
||||
PINOS_STREAM_STATE_STREAMING = 4
|
||||
} PinosStreamState;
|
||||
|
||||
const gchar * pinos_stream_state_as_string (PinosStreamState state);
|
||||
|
||||
typedef enum {
|
||||
PINOS_STREAM_FLAGS_NONE = 0,
|
||||
} PinosStreamFlags;
|
||||
|
||||
typedef enum {
|
||||
PINOS_STREAM_MODE_SOCKET = 0,
|
||||
PINOS_STREAM_MODE_BUFFER = 1,
|
||||
} PinosStreamMode;
|
||||
|
||||
/**
|
||||
* PinosStream:
|
||||
*
|
||||
* Pinos stream object class.
|
||||
*/
|
||||
struct _PinosStream {
|
||||
GObject object;
|
||||
|
||||
PinosStreamPrivate *priv;
|
||||
};
|
||||
|
||||
/**
|
||||
* PinosStreamClass:
|
||||
*
|
||||
* Pinos stream object class.
|
||||
*/
|
||||
struct _PinosStreamClass {
|
||||
GObjectClass parent_class;
|
||||
};
|
||||
|
||||
/* normal GObject stuff */
|
||||
GType pinos_stream_get_type (void);
|
||||
|
||||
|
||||
PinosStream * pinos_stream_new (PinosContext *context,
|
||||
const gchar *name,
|
||||
PinosProperties *props);
|
||||
|
||||
PinosStreamState pinos_stream_get_state (PinosStream *stream);
|
||||
const GError * pinos_stream_get_error (PinosStream *stream);
|
||||
|
||||
gboolean pinos_stream_connect_capture (PinosStream *stream,
|
||||
const gchar *source_path,
|
||||
PinosStreamFlags flags,
|
||||
GBytes *accepted_formats);
|
||||
gboolean pinos_stream_connect_provide (PinosStream *stream,
|
||||
PinosStreamFlags flags,
|
||||
GBytes *possible_formats);
|
||||
gboolean pinos_stream_disconnect (PinosStream *stream);
|
||||
|
||||
gboolean pinos_stream_start (PinosStream *stream,
|
||||
GBytes *format,
|
||||
PinosStreamMode mode);
|
||||
gboolean pinos_stream_stop (PinosStream *stream);
|
||||
|
||||
gboolean pinos_stream_peek_buffer (PinosStream *stream,
|
||||
PinosBuffer **buffer);
|
||||
gboolean pinos_stream_send_buffer (PinosStream *stream,
|
||||
PinosBuffer *buffer);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __PINOS_STREAM_H__ */
|
||||
|
||||
|
|
@ -1,790 +0,0 @@
|
|||
/* Pinos
|
||||
* Copyright (C) 2015 Wim Taymans <wim.taymans@gmail.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include <gio/gio.h>
|
||||
|
||||
#include "client/pinos.h"
|
||||
#include "client/enumtypes.h"
|
||||
#include "client/private.h"
|
||||
|
||||
struct _PinosSubscribePrivate
|
||||
{
|
||||
gchar *service;
|
||||
PinosSubscriptionFlags subscription_mask;
|
||||
|
||||
GDBusConnection *connection;
|
||||
GCancellable *cancellable;
|
||||
|
||||
GDBusProxy *manager_proxy;
|
||||
guint owner_id;
|
||||
guint signal_id;
|
||||
|
||||
guint pending_proxies;
|
||||
GList *objects;
|
||||
|
||||
PinosSubscriptionState state;
|
||||
GError *error;
|
||||
};
|
||||
|
||||
typedef struct
|
||||
{
|
||||
PinosSubscribe *subscribe;
|
||||
gchar *sender_name;
|
||||
gchar *object_path;
|
||||
gchar *interface_name;
|
||||
gboolean pending;
|
||||
GDBusProxy *proxy;
|
||||
guint prop_id;
|
||||
GList *tasks;
|
||||
gboolean removed;
|
||||
} PinosObjectData;
|
||||
|
||||
|
||||
#define PINOS_SUBSCRIBE_GET_PRIVATE(obj) \
|
||||
(G_TYPE_INSTANCE_GET_PRIVATE ((obj), PINOS_TYPE_SUBSCRIBE, PinosSubscribePrivate))
|
||||
|
||||
G_DEFINE_TYPE (PinosSubscribe, pinos_subscribe, G_TYPE_OBJECT);
|
||||
|
||||
enum
|
||||
{
|
||||
PROP_0,
|
||||
PROP_CONNECTION,
|
||||
PROP_SERVICE,
|
||||
PROP_SUBSCRIPTION_MASK,
|
||||
PROP_STATE,
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
SIGNAL_SUBSCRIPTION_EVENT,
|
||||
LAST_SIGNAL
|
||||
};
|
||||
|
||||
static guint signals[LAST_SIGNAL] = { 0 };
|
||||
|
||||
static void
|
||||
subscription_set_state (PinosSubscribe *subscribe,
|
||||
PinosSubscriptionState state)
|
||||
{
|
||||
PinosSubscribePrivate *priv = subscribe->priv;
|
||||
|
||||
if (state != priv->state) {
|
||||
priv->state = state;
|
||||
g_object_notify (G_OBJECT (subscribe), "state");
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
notify_event (PinosSubscribe *subscribe,
|
||||
PinosObjectData *data,
|
||||
PinosSubscriptionEvent event)
|
||||
{
|
||||
const gchar *interface_name;
|
||||
PinosSubscriptionFlags flags = 0;
|
||||
|
||||
interface_name = g_dbus_proxy_get_interface_name (data->proxy);
|
||||
if (g_strcmp0 (interface_name, "org.pinos.Daemon1") == 0) {
|
||||
flags = PINOS_SUBSCRIPTION_FLAG_DAEMON;
|
||||
}
|
||||
else if (g_strcmp0 (interface_name, "org.pinos.Client1") == 0) {
|
||||
flags = PINOS_SUBSCRIPTION_FLAG_CLIENT;
|
||||
}
|
||||
else if (g_strcmp0 (interface_name, "org.pinos.Source1") == 0) {
|
||||
flags = PINOS_SUBSCRIPTION_FLAG_SOURCE;
|
||||
}
|
||||
else if (g_strcmp0 (interface_name, "org.pinos.SourceOutput1") == 0) {
|
||||
flags = PINOS_SUBSCRIPTION_FLAG_SOURCE_OUTPUT;
|
||||
}
|
||||
g_signal_emit (subscribe, signals[SIGNAL_SUBSCRIPTION_EVENT], 0,
|
||||
event, flags, data->proxy);
|
||||
}
|
||||
|
||||
static void
|
||||
on_proxy_properties_changed (GDBusProxy *proxy,
|
||||
GVariant *changed_properties,
|
||||
GStrv invalidated_properties,
|
||||
gpointer user_data)
|
||||
{
|
||||
PinosObjectData *data = user_data;
|
||||
GVariantIter iter;
|
||||
GVariant *value;
|
||||
gchar *key;
|
||||
GHashTable *props;
|
||||
gboolean need_notify = FALSE;
|
||||
|
||||
props = g_object_get_data (G_OBJECT (proxy), "pinos-changed-properties");
|
||||
|
||||
g_variant_iter_init (&iter, changed_properties);
|
||||
while (g_variant_iter_loop (&iter, "{sv}", &key, &value)) {
|
||||
if (!g_hash_table_contains (props, key))
|
||||
g_hash_table_add (props, g_strdup (key));
|
||||
need_notify = TRUE;
|
||||
}
|
||||
if (need_notify)
|
||||
notify_event (data->subscribe, data, PINOS_SUBSCRIPTION_EVENT_CHANGE);
|
||||
}
|
||||
|
||||
static void
|
||||
object_data_free (PinosObjectData *data)
|
||||
{
|
||||
g_signal_handler_disconnect (data->proxy, data->prop_id);
|
||||
g_object_unref (data->proxy);
|
||||
g_free (data->sender_name);
|
||||
g_free (data->object_path);
|
||||
g_free (data->interface_name);
|
||||
g_free (data);
|
||||
}
|
||||
|
||||
static void
|
||||
remove_data (PinosSubscribe *subscribe,
|
||||
PinosObjectData *data)
|
||||
{
|
||||
if (data->pending) {
|
||||
data->removed = TRUE;
|
||||
} else {
|
||||
GHashTable *props = g_object_get_data (G_OBJECT (data->proxy), "pinos-changed-properties");
|
||||
|
||||
g_hash_table_remove_all (props);
|
||||
notify_event (subscribe, data, PINOS_SUBSCRIPTION_EVENT_REMOVE);
|
||||
object_data_free (data);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
remove_all_data (PinosSubscribe *subscribe)
|
||||
{
|
||||
PinosSubscribePrivate *priv = subscribe->priv;
|
||||
GList *walk;
|
||||
|
||||
for (walk = priv->objects; walk; walk = g_list_next (walk)) {
|
||||
PinosObjectData *data = walk->data;
|
||||
remove_data (subscribe, data);
|
||||
}
|
||||
g_list_free (priv->objects);
|
||||
priv->objects = NULL;
|
||||
}
|
||||
|
||||
static void
|
||||
on_proxy_created (GObject *source_object,
|
||||
GAsyncResult *res,
|
||||
gpointer user_data)
|
||||
{
|
||||
PinosObjectData *data = user_data;
|
||||
PinosSubscribe *subscribe = data->subscribe;
|
||||
PinosSubscribePrivate *priv = subscribe->priv;
|
||||
GError *error = NULL;
|
||||
GList *walk;
|
||||
|
||||
data->pending = FALSE;
|
||||
|
||||
data->proxy = g_dbus_proxy_new_finish (res, &error);
|
||||
if (data->proxy == NULL) {
|
||||
priv->objects = g_list_remove (priv->objects, data);
|
||||
g_warning ("could not create proxy: %s", error->message);
|
||||
subscription_set_state (subscribe, PINOS_SUBSCRIPTION_STATE_ERROR);
|
||||
priv->error = error;
|
||||
return;
|
||||
}
|
||||
|
||||
data->prop_id = g_signal_connect (data->proxy,
|
||||
"g-properties-changed",
|
||||
(GCallback) on_proxy_properties_changed,
|
||||
data);
|
||||
|
||||
notify_event (subscribe, data, PINOS_SUBSCRIPTION_EVENT_NEW);
|
||||
|
||||
g_object_set_data_full (G_OBJECT (data->proxy),
|
||||
"pinos-changed-properties",
|
||||
g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL),
|
||||
(GDestroyNotify) g_hash_table_unref);
|
||||
|
||||
for (walk = data->tasks; walk; walk = g_list_next (walk)) {
|
||||
GTask *task = walk->data;
|
||||
g_task_return_pointer (task, g_object_ref (data->proxy), g_object_unref);
|
||||
g_object_unref (task);
|
||||
}
|
||||
g_list_free (data->tasks);
|
||||
data->tasks = NULL;
|
||||
|
||||
if (--priv->pending_proxies == 0)
|
||||
subscription_set_state (subscribe, PINOS_SUBSCRIPTION_STATE_READY);
|
||||
|
||||
if (data->removed) {
|
||||
priv->objects = g_list_remove (priv->objects, data);
|
||||
remove_data (subscribe, data);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
add_interface (PinosSubscribe *subscribe,
|
||||
const gchar *object_path,
|
||||
const gchar *interface_name,
|
||||
GVariant *properties)
|
||||
{
|
||||
PinosSubscribePrivate *priv = subscribe->priv;
|
||||
PinosObjectData *data;
|
||||
|
||||
data = g_new0 (PinosObjectData, 1);
|
||||
data->subscribe = subscribe;
|
||||
data->sender_name = g_strdup (priv->service);
|
||||
data->object_path = g_strdup (object_path);
|
||||
data->interface_name = g_strdup (interface_name);
|
||||
data->pending = TRUE;
|
||||
|
||||
priv->objects = g_list_prepend (priv->objects, data);
|
||||
priv->pending_proxies++;
|
||||
|
||||
g_dbus_proxy_new (priv->connection,
|
||||
G_DBUS_PROXY_FLAGS_NONE,
|
||||
NULL, /* GDBusInterfaceInfo* */
|
||||
priv->service,
|
||||
object_path,
|
||||
interface_name,
|
||||
priv->cancellable,
|
||||
on_proxy_created,
|
||||
data);
|
||||
}
|
||||
|
||||
static void
|
||||
remove_interface (PinosSubscribe *subscribe,
|
||||
const gchar *object_path,
|
||||
const gchar *interface_name)
|
||||
{
|
||||
PinosSubscribePrivate *priv = subscribe->priv;
|
||||
GList *walk;
|
||||
|
||||
for (walk = priv->objects; walk; walk = g_list_next (walk)) {
|
||||
PinosObjectData *data = walk->data;
|
||||
|
||||
if (g_strcmp0 (data->object_path, object_path) == 0 &&
|
||||
g_strcmp0 (data->interface_name, interface_name) == 0) {
|
||||
priv->objects = g_list_remove (priv->objects, data);
|
||||
remove_data (subscribe, data);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
add_ifaces_and_properties (PinosSubscribe *subscribe,
|
||||
const gchar *object_path,
|
||||
GVariant *ifaces_and_properties)
|
||||
{
|
||||
GVariantIter iter;
|
||||
const gchar *interface_name;
|
||||
GVariant *properties;
|
||||
|
||||
g_variant_iter_init (&iter, ifaces_and_properties);
|
||||
while (g_variant_iter_next (&iter,
|
||||
"{&s@a{sv}}",
|
||||
&interface_name,
|
||||
&properties)) {
|
||||
|
||||
add_interface (subscribe, object_path, interface_name, properties);
|
||||
|
||||
g_variant_unref (properties);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
remove_ifaces (PinosSubscribe *subscribe,
|
||||
const gchar *object_path,
|
||||
const gchar **ifaces)
|
||||
{
|
||||
while (*ifaces) {
|
||||
remove_interface (subscribe, object_path, *ifaces);
|
||||
|
||||
ifaces++;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
on_manager_proxy_signal (GDBusProxy *proxy,
|
||||
const gchar *sender_name,
|
||||
const gchar *signal_name,
|
||||
GVariant *parameters,
|
||||
gpointer user_data)
|
||||
{
|
||||
PinosSubscribe *subscribe = user_data;
|
||||
const gchar *object_path;
|
||||
|
||||
if (g_strcmp0 (signal_name, "InterfacesAdded") == 0) {
|
||||
GVariant *ifaces_and_properties;
|
||||
|
||||
g_variant_get (parameters,
|
||||
"(&o@a{sa{sv}})",
|
||||
&object_path,
|
||||
&ifaces_and_properties);
|
||||
|
||||
add_ifaces_and_properties (subscribe, object_path, ifaces_and_properties);
|
||||
|
||||
g_variant_unref (ifaces_and_properties);
|
||||
} else if (g_strcmp0 (signal_name, "InterfacesRemoved") == 0) {
|
||||
const gchar **ifaces;
|
||||
g_variant_get (parameters,
|
||||
"(&o^a&s)",
|
||||
&object_path,
|
||||
&ifaces);
|
||||
|
||||
remove_ifaces (subscribe, object_path, ifaces);
|
||||
|
||||
g_free (ifaces);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
on_managed_objects_ready (GObject *source_object,
|
||||
GAsyncResult *res,
|
||||
gpointer user_data)
|
||||
{
|
||||
PinosSubscribe *subscribe = user_data;
|
||||
PinosSubscribePrivate *priv = subscribe->priv;
|
||||
GError *error = NULL;
|
||||
GVariant *objects;
|
||||
GVariant *arg0;
|
||||
const gchar *object_path;
|
||||
GVariant *ifaces_and_properties;
|
||||
GVariantIter object_iter;
|
||||
|
||||
objects = g_dbus_proxy_call_finish (priv->manager_proxy, res, &error);
|
||||
if (objects == NULL) {
|
||||
g_warning ("could not get objects: %s", error->message);
|
||||
subscription_set_state (subscribe, PINOS_SUBSCRIPTION_STATE_ERROR);
|
||||
priv->error = error;
|
||||
return;
|
||||
}
|
||||
|
||||
arg0 = g_variant_get_child_value (objects, 0);
|
||||
g_variant_iter_init (&object_iter, arg0);
|
||||
while (g_variant_iter_next (&object_iter,
|
||||
"{&o@a{sa{sv}}}",
|
||||
&object_path,
|
||||
&ifaces_and_properties)) {
|
||||
|
||||
add_ifaces_and_properties (subscribe, object_path, ifaces_and_properties);
|
||||
|
||||
g_variant_unref (ifaces_and_properties);
|
||||
}
|
||||
g_variant_unref (arg0);
|
||||
g_variant_unref (objects);
|
||||
|
||||
if (priv->pending_proxies == 0)
|
||||
subscription_set_state (subscribe, PINOS_SUBSCRIPTION_STATE_READY);
|
||||
}
|
||||
|
||||
static void
|
||||
manager_proxy_appeared (PinosSubscribe *subscribe)
|
||||
{
|
||||
PinosSubscribePrivate *priv = subscribe->priv;
|
||||
|
||||
g_dbus_proxy_call (priv->manager_proxy,
|
||||
"GetManagedObjects",
|
||||
NULL, /* parameters */
|
||||
G_DBUS_CALL_FLAGS_NONE,
|
||||
-1,
|
||||
priv->cancellable,
|
||||
on_managed_objects_ready,
|
||||
subscribe);
|
||||
}
|
||||
|
||||
static void
|
||||
manager_proxy_disappeared (PinosSubscribe *subscribe)
|
||||
{
|
||||
remove_all_data (subscribe);
|
||||
}
|
||||
|
||||
static void
|
||||
on_manager_proxy_name_owner (GObject *object,
|
||||
GParamSpec *pspec,
|
||||
gpointer user_data)
|
||||
{
|
||||
PinosSubscribe *subscribe = user_data;
|
||||
PinosSubscribePrivate *priv = subscribe->priv;
|
||||
gchar *name_owner;
|
||||
|
||||
g_object_get (priv->manager_proxy, "g-name-owner", &name_owner, NULL);
|
||||
|
||||
if (name_owner) {
|
||||
manager_proxy_appeared (subscribe);
|
||||
g_free (name_owner);
|
||||
} else {
|
||||
manager_proxy_disappeared (subscribe);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
connect_client_signals (PinosSubscribe *subscribe)
|
||||
{
|
||||
PinosSubscribePrivate *priv = subscribe->priv;
|
||||
|
||||
priv->owner_id = g_signal_connect (priv->manager_proxy, "notify::g-name-owner",
|
||||
(GCallback) on_manager_proxy_name_owner, subscribe);
|
||||
|
||||
priv->signal_id = g_signal_connect (priv->manager_proxy, "g-signal",
|
||||
(GCallback) on_manager_proxy_signal, subscribe);
|
||||
}
|
||||
|
||||
static void
|
||||
on_manager_proxy_ready (GObject *source_object,
|
||||
GAsyncResult *res,
|
||||
gpointer user_data)
|
||||
{
|
||||
PinosSubscribe *subscribe = user_data;
|
||||
PinosSubscribePrivate *priv = subscribe->priv;
|
||||
GError *error = NULL;
|
||||
|
||||
priv->manager_proxy = g_dbus_proxy_new_finish (res, &error);
|
||||
if (priv->manager_proxy == NULL)
|
||||
goto manager_error;
|
||||
|
||||
connect_client_signals (subscribe);
|
||||
|
||||
on_manager_proxy_name_owner (G_OBJECT (priv->manager_proxy), NULL, subscribe);
|
||||
g_object_unref (subscribe);
|
||||
|
||||
return;
|
||||
|
||||
/* ERRORS */
|
||||
manager_error:
|
||||
{
|
||||
g_warning ("could not create client manager: %s", error->message);
|
||||
subscription_set_state (subscribe, PINOS_SUBSCRIPTION_STATE_ERROR);
|
||||
priv->error = error;
|
||||
g_object_unref (subscribe);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
install_subscription (PinosSubscribe *subscribe)
|
||||
{
|
||||
PinosSubscribePrivate *priv = subscribe->priv;
|
||||
|
||||
subscription_set_state (subscribe, PINOS_SUBSCRIPTION_STATE_CONNECTING);
|
||||
|
||||
g_dbus_proxy_new (priv->connection,
|
||||
G_DBUS_PROXY_FLAGS_NONE,
|
||||
NULL, /* GDBusInterfaceInfo* */
|
||||
priv->service,
|
||||
PINOS_DBUS_OBJECT_PREFIX,
|
||||
"org.freedesktop.DBus.ObjectManager",
|
||||
priv->cancellable,
|
||||
on_manager_proxy_ready,
|
||||
g_object_ref (subscribe));
|
||||
}
|
||||
|
||||
static void
|
||||
uninstall_subscription (PinosSubscribe *subscribe)
|
||||
{
|
||||
PinosSubscribePrivate *priv = subscribe->priv;
|
||||
|
||||
if (priv->manager_proxy) {
|
||||
g_signal_handler_disconnect (priv->manager_proxy, priv->owner_id);
|
||||
g_signal_handler_disconnect (priv->manager_proxy, priv->signal_id);
|
||||
g_clear_object (&priv->manager_proxy);
|
||||
}
|
||||
g_clear_error (&priv->error);
|
||||
subscription_set_state (subscribe, PINOS_SUBSCRIPTION_STATE_UNCONNECTED);
|
||||
}
|
||||
|
||||
static void
|
||||
pinos_subscribe_get_property (GObject *_object,
|
||||
guint prop_id,
|
||||
GValue *value,
|
||||
GParamSpec *pspec)
|
||||
{
|
||||
PinosSubscribe *subscribe = PINOS_SUBSCRIBE (_object);
|
||||
PinosSubscribePrivate *priv = subscribe->priv;
|
||||
|
||||
switch (prop_id) {
|
||||
case PROP_CONNECTION:
|
||||
g_value_set_object (value, priv->connection);
|
||||
break;
|
||||
|
||||
case PROP_SERVICE:
|
||||
g_value_set_string (value, priv->service);
|
||||
break;
|
||||
|
||||
case PROP_SUBSCRIPTION_MASK:
|
||||
g_value_set_flags (value, priv->subscription_mask);
|
||||
break;
|
||||
|
||||
case PROP_STATE:
|
||||
g_value_set_enum (value, priv->state);
|
||||
break;
|
||||
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (subscribe, prop_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
pinos_subscribe_set_property (GObject *_object,
|
||||
guint prop_id,
|
||||
const GValue *value,
|
||||
GParamSpec *pspec)
|
||||
{
|
||||
PinosSubscribe *subscribe = PINOS_SUBSCRIBE (_object);
|
||||
PinosSubscribePrivate *priv = subscribe->priv;
|
||||
|
||||
switch (prop_id) {
|
||||
case PROP_CONNECTION:
|
||||
{
|
||||
uninstall_subscription (subscribe);
|
||||
if (priv->connection)
|
||||
g_object_unref (priv->connection);
|
||||
priv->connection = g_value_dup_object (value);
|
||||
if (priv->connection)
|
||||
install_subscription (subscribe);
|
||||
break;
|
||||
}
|
||||
|
||||
case PROP_SERVICE:
|
||||
g_free (priv->service);
|
||||
priv->service = g_value_dup_string (value);
|
||||
break;
|
||||
|
||||
case PROP_SUBSCRIPTION_MASK:
|
||||
priv->subscription_mask = g_value_get_flags (value);
|
||||
break;
|
||||
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (subscribe, prop_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
pinos_subscribe_finalize (GObject * object)
|
||||
{
|
||||
PinosSubscribe *subscribe = PINOS_SUBSCRIBE (object);
|
||||
PinosSubscribePrivate *priv = subscribe->priv;
|
||||
|
||||
remove_all_data (subscribe);
|
||||
|
||||
g_cancellable_cancel (priv->cancellable);
|
||||
if (priv->manager_proxy)
|
||||
g_object_unref (priv->manager_proxy);
|
||||
g_object_unref (priv->cancellable);
|
||||
g_free (priv->service);
|
||||
|
||||
G_OBJECT_CLASS (pinos_subscribe_parent_class)->finalize (object);
|
||||
}
|
||||
|
||||
static void
|
||||
pinos_subscribe_class_init (PinosSubscribeClass * klass)
|
||||
{
|
||||
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
|
||||
|
||||
g_type_class_add_private (klass, sizeof (PinosSubscribePrivate));
|
||||
|
||||
gobject_class->finalize = pinos_subscribe_finalize;
|
||||
gobject_class->set_property = pinos_subscribe_set_property;
|
||||
gobject_class->get_property = pinos_subscribe_get_property;
|
||||
|
||||
/**
|
||||
* PinosSubscribe:connection
|
||||
*
|
||||
* The connection of the subscribe.
|
||||
*/
|
||||
g_object_class_install_property (gobject_class,
|
||||
PROP_CONNECTION,
|
||||
g_param_spec_object ("connection",
|
||||
"Connection",
|
||||
"The DBus connection",
|
||||
G_TYPE_DBUS_CONNECTION,
|
||||
G_PARAM_READWRITE |
|
||||
G_PARAM_STATIC_STRINGS));
|
||||
/**
|
||||
* PinosSubscribe:service
|
||||
*
|
||||
* The service of the subscribe.
|
||||
*/
|
||||
g_object_class_install_property (gobject_class,
|
||||
PROP_SERVICE,
|
||||
g_param_spec_string ("service",
|
||||
"Service",
|
||||
"The service",
|
||||
PINOS_DBUS_SERVICE,
|
||||
G_PARAM_READWRITE |
|
||||
G_PARAM_STATIC_STRINGS));
|
||||
/**
|
||||
* PinosSubscribe:subscription-mask
|
||||
*
|
||||
* A mask for what object notifications will be signaled with
|
||||
* PinosSubscribe:subscription-event
|
||||
*/
|
||||
g_object_class_install_property (gobject_class,
|
||||
PROP_SUBSCRIPTION_MASK,
|
||||
g_param_spec_flags ("subscription-mask",
|
||||
"Subscription Mask",
|
||||
"The object to receive subscription events of",
|
||||
PINOS_TYPE_SUBSCRIPTION_FLAGS,
|
||||
0,
|
||||
G_PARAM_READWRITE |
|
||||
G_PARAM_STATIC_STRINGS));
|
||||
/**
|
||||
* PinosSubscribe:state
|
||||
*
|
||||
* The state of the subscription
|
||||
*/
|
||||
g_object_class_install_property (gobject_class,
|
||||
PROP_STATE,
|
||||
g_param_spec_enum ("state",
|
||||
"State",
|
||||
"The state",
|
||||
PINOS_TYPE_SUBSCRIPTION_STATE,
|
||||
PINOS_SUBSCRIPTION_STATE_UNCONNECTED,
|
||||
G_PARAM_READABLE |
|
||||
G_PARAM_STATIC_STRINGS));
|
||||
/**
|
||||
* PinosSubscribe:subscription-event
|
||||
* @subscribe: The #PinosSubscribe emitting the signal.
|
||||
* @event: A #PinosSubscriptionEvent
|
||||
* @flags: #PinosSubscriptionFlags indicating the object
|
||||
* @id: the unique and opaque object id
|
||||
*
|
||||
* Notify about a new object that was added/removed/modified.
|
||||
*/
|
||||
signals[SIGNAL_SUBSCRIPTION_EVENT] = g_signal_new ("subscription-event",
|
||||
G_TYPE_FROM_CLASS (klass),
|
||||
G_SIGNAL_RUN_LAST,
|
||||
0,
|
||||
NULL,
|
||||
NULL,
|
||||
g_cclosure_marshal_generic,
|
||||
G_TYPE_NONE,
|
||||
3,
|
||||
PINOS_TYPE_SUBSCRIPTION_EVENT,
|
||||
PINOS_TYPE_SUBSCRIPTION_FLAGS,
|
||||
G_TYPE_POINTER);
|
||||
}
|
||||
|
||||
static void
|
||||
pinos_subscribe_init (PinosSubscribe * subscribe)
|
||||
{
|
||||
PinosSubscribePrivate *priv = subscribe->priv = PINOS_SUBSCRIBE_GET_PRIVATE (subscribe);
|
||||
|
||||
priv->service = g_strdup (PINOS_DBUS_SERVICE);
|
||||
priv->state = PINOS_SUBSCRIPTION_STATE_UNCONNECTED;
|
||||
priv->cancellable = g_cancellable_new ();
|
||||
}
|
||||
|
||||
/**
|
||||
* pinos_subscribe_new:
|
||||
* @name: an application name
|
||||
* @properties: optional properties
|
||||
*
|
||||
* Make a new unconnected #PinosSubscribe
|
||||
*
|
||||
* Returns: a new unconnected #PinosSubscribe
|
||||
*/
|
||||
PinosSubscribe *
|
||||
pinos_subscribe_new (void)
|
||||
{
|
||||
return g_object_new (PINOS_TYPE_SUBSCRIBE, NULL);
|
||||
}
|
||||
|
||||
PinosSubscriptionState
|
||||
pinos_subscribe_get_state (PinosSubscribe *subscribe)
|
||||
{
|
||||
PinosSubscribePrivate *priv;
|
||||
|
||||
g_return_val_if_fail (PINOS_IS_SUBSCRIBE (subscribe), PINOS_SUBSCRIPTION_STATE_ERROR);
|
||||
priv = subscribe->priv;
|
||||
|
||||
return priv->state;
|
||||
}
|
||||
|
||||
GError *
|
||||
pinos_subscribe_get_error (PinosSubscribe *subscribe)
|
||||
{
|
||||
PinosSubscribePrivate *priv;
|
||||
|
||||
g_return_val_if_fail (PINOS_IS_SUBSCRIBE (subscribe), NULL);
|
||||
priv = subscribe->priv;
|
||||
|
||||
return priv->error;
|
||||
}
|
||||
|
||||
static gint
|
||||
compare_data (PinosObjectData *data,
|
||||
const gchar *name,
|
||||
const gchar *object_path,
|
||||
const gchar *interface_name)
|
||||
{
|
||||
gint res;
|
||||
|
||||
if ((res = g_strcmp0 (data->sender_name, name)) != 0)
|
||||
return res;
|
||||
|
||||
if ((res = g_strcmp0 (data->object_path, object_path)) != 0)
|
||||
return res;
|
||||
|
||||
return g_strcmp0 (data->interface_name, interface_name);
|
||||
}
|
||||
|
||||
void
|
||||
pinos_subscribe_get_proxy (PinosSubscribe *subscribe,
|
||||
const gchar *name,
|
||||
const gchar *object_path,
|
||||
const gchar *interface_name,
|
||||
GCancellable *cancellable,
|
||||
GAsyncReadyCallback callback,
|
||||
gpointer user_data)
|
||||
{
|
||||
PinosSubscribePrivate *priv;
|
||||
GList *walk;
|
||||
|
||||
g_return_if_fail (PINOS_IS_SUBSCRIBE (subscribe));
|
||||
priv = subscribe->priv;
|
||||
|
||||
for (walk = priv->objects; walk; walk = g_list_next (walk)) {
|
||||
PinosObjectData *data = walk->data;
|
||||
|
||||
if (compare_data (data, name, object_path, interface_name) == 0) {
|
||||
GTask *task;
|
||||
|
||||
task = g_task_new (subscribe,
|
||||
cancellable,
|
||||
callback,
|
||||
user_data);
|
||||
|
||||
if (data->pending) {
|
||||
data->tasks = g_list_prepend (data->tasks, task);
|
||||
} else if (data->proxy) {
|
||||
g_task_return_pointer (task, g_object_ref (data->proxy), g_object_unref);
|
||||
} else {
|
||||
g_task_return_error (task, NULL);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
GDBusProxy *
|
||||
pinos_subscribe_get_proxy_finish (PinosSubscribe *subscribe,
|
||||
GAsyncResult *res,
|
||||
GError **error)
|
||||
{
|
||||
return g_task_propagate_pointer (G_TASK (res), error);
|
||||
}
|
||||
|
||||
|
|
@ -1,93 +0,0 @@
|
|||
/* Pinos
|
||||
* Copyright (C) 2015 Wim Taymans <wim.taymans@gmail.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#ifndef __PINOS_SUBSCRIBE_H__
|
||||
#define __PINOS_SUBSCRIBE_H__
|
||||
|
||||
#include <glib-object.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define PINOS_TYPE_SUBSCRIBE (pinos_subscribe_get_type ())
|
||||
#define PINOS_IS_SUBSCRIBE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), PINOS_TYPE_SUBSCRIBE))
|
||||
#define PINOS_IS_SUBSCRIBE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), PINOS_TYPE_SUBSCRIBE))
|
||||
#define PINOS_SUBSCRIBE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), PINOS_TYPE_SUBSCRIBE, PinosSubscribeClass))
|
||||
#define PINOS_SUBSCRIBE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), PINOS_TYPE_SUBSCRIBE, PinosSubscribe))
|
||||
#define PINOS_SUBSCRIBE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), PINOS_TYPE_SUBSCRIBE, PinosSubscribeClass))
|
||||
#define PINOS_SUBSCRIBE_CAST(obj) ((PinosSubscribe*)(obj))
|
||||
#define PINOS_SUBSCRIBE_CLASS_CAST(klass) ((PinosSubscribeClass*)(klass))
|
||||
|
||||
typedef struct _PinosSubscribe PinosSubscribe;
|
||||
typedef struct _PinosSubscribeClass PinosSubscribeClass;
|
||||
typedef struct _PinosSubscribePrivate PinosSubscribePrivate;
|
||||
|
||||
typedef enum {
|
||||
PINOS_SUBSCRIPTION_STATE_UNCONNECTED = 0,
|
||||
PINOS_SUBSCRIPTION_STATE_CONNECTING = 1,
|
||||
PINOS_SUBSCRIPTION_STATE_READY = 2,
|
||||
PINOS_SUBSCRIPTION_STATE_ERROR = 3,
|
||||
} PinosSubscriptionState;
|
||||
|
||||
typedef enum {
|
||||
PINOS_SUBSCRIPTION_FLAG_DAEMON = (1 << 0),
|
||||
PINOS_SUBSCRIPTION_FLAG_CLIENT = (1 << 1),
|
||||
PINOS_SUBSCRIPTION_FLAG_SOURCE = (1 << 2),
|
||||
PINOS_SUBSCRIPTION_FLAG_SOURCE_OUTPUT = (1 << 3),
|
||||
} PinosSubscriptionFlags;
|
||||
|
||||
#define PINOS_SUBSCRIPTION_FLAGS_ALL 0xf
|
||||
|
||||
typedef enum {
|
||||
PINOS_SUBSCRIPTION_EVENT_NEW = 0,
|
||||
PINOS_SUBSCRIPTION_EVENT_CHANGE = 1,
|
||||
PINOS_SUBSCRIPTION_EVENT_REMOVE = 2,
|
||||
} PinosSubscriptionEvent;
|
||||
|
||||
/**
|
||||
* PinosSubscribe:
|
||||
*
|
||||
* Pinos subscribe object class.
|
||||
*/
|
||||
struct _PinosSubscribe {
|
||||
GObject object;
|
||||
|
||||
PinosSubscribePrivate *priv;
|
||||
};
|
||||
|
||||
/**
|
||||
* PinosSubscribeClass:
|
||||
*
|
||||
* Pinos subscribe object class.
|
||||
*/
|
||||
struct _PinosSubscribeClass {
|
||||
GObjectClass parent_class;
|
||||
};
|
||||
|
||||
/* normal GObject stuff */
|
||||
GType pinos_subscribe_get_type (void);
|
||||
|
||||
PinosSubscribe * pinos_subscribe_new (void);
|
||||
|
||||
PinosSubscriptionState pinos_subscribe_get_state (PinosSubscribe *subscribe);
|
||||
GError * pinos_subscribe_get_error (PinosSubscribe *subscribe);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __PINOS_SUBSCRIBE_H__ */
|
||||
|
||||
|
|
@ -1,50 +0,0 @@
|
|||
/* Pinos
|
||||
* Copyright (C) 2015 Wim Taymans <wim.taymans@gmail.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include <gio/gio.h>
|
||||
#include <gst/gst.h>
|
||||
|
||||
#include <client/pinos.h>
|
||||
#include <server/daemon.h>
|
||||
#include <modules/gst/gst-manager.h>
|
||||
|
||||
gint
|
||||
main (gint argc, gchar *argv[])
|
||||
{
|
||||
PinosDaemon *daemon;
|
||||
GMainLoop *loop;
|
||||
PinosProperties *props;
|
||||
|
||||
pinos_init (&argc, &argv);
|
||||
|
||||
loop = g_main_loop_new (NULL, FALSE);
|
||||
|
||||
props = pinos_properties_new ("test", "test", NULL);
|
||||
daemon = pinos_daemon_new (props);
|
||||
|
||||
pinos_gst_manager_new (daemon);
|
||||
pinos_daemon_start (daemon);
|
||||
|
||||
g_main_loop_run (loop);
|
||||
|
||||
g_main_loop_unref (loop);
|
||||
g_object_unref (daemon);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -1,31 +0,0 @@
|
|||
<?xml version="1.0"?><!--*-nxml-*-->
|
||||
<!DOCTYPE busconfig PUBLIC "-//freedesktop//DTD D-BUS Bus Configuration 1.0//EN"
|
||||
"http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd">
|
||||
|
||||
<!--
|
||||
This file is part of Pinos.
|
||||
|
||||
Pinos is free software; you can redistribute it and/or modify it
|
||||
under the terms of the GNU Lesser General Public License as
|
||||
published by the Free Software Foundation; either version 2.1 of the
|
||||
License, or (at your option) any later version.
|
||||
|
||||
Pinos 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 Lesser General
|
||||
Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with Pinos; if not, see <http://www.gnu.org/licenses/>.
|
||||
-->
|
||||
|
||||
<busconfig>
|
||||
|
||||
<!-- System-wide Pinos runs as 'pinos' user. This fragment is
|
||||
not necessary for user Pinos instances. -->
|
||||
|
||||
<policy user="pinos">
|
||||
<allow own="org.pinos"/>
|
||||
</policy>
|
||||
|
||||
</busconfig>
|
||||
|
|
@ -1,11 +0,0 @@
|
|||
[Desktop Entry]
|
||||
Version=1.0
|
||||
_Name=Pinos Media System
|
||||
_Comment=Start the Pinos Media System
|
||||
Exec=pinos
|
||||
Terminal=false
|
||||
Type=Application
|
||||
Categories=
|
||||
GenericName=
|
||||
X-GNOME-Autostart-Phase=Initialization
|
||||
X-KDE-autostart-phase=1
|
||||
2
src/dbus/.gitignore
vendored
2
src/dbus/.gitignore
vendored
|
|
@ -1,2 +0,0 @@
|
|||
org-pinos.c
|
||||
org-pinos.h
|
||||
|
|
@ -1,191 +0,0 @@
|
|||
<!DOCTYPE node PUBLIC
|
||||
"-//freedesktop//DTD D-BUS Object Introspection 1.0//EN"
|
||||
"http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd" >
|
||||
<node xmlns:doc="http://www.freedesktop.org/dbus/1.0/doc.dtd">
|
||||
<!--
|
||||
org.pinos.Daemon1:
|
||||
@short_description: Main interface for the pinos daemon
|
||||
|
||||
Interface to get properties from the pinos daemon and to
|
||||
establish a client connection.
|
||||
-->
|
||||
<interface name='org.pinos.Daemon1'>
|
||||
<!-- UserName: Name of the user that started the daemon -->
|
||||
<property name='UserName' type='s' access='read' />
|
||||
<!-- HostName: Name of the machine the daemon is running on -->
|
||||
<property name='HostName' type='s' access='read' />
|
||||
<!-- Version: Version of the daemon -->
|
||||
<property name='Version' type='s' access='read' />
|
||||
<!-- Name: Name of the daemon -->
|
||||
<property name='Name' type='s' access='read' />
|
||||
<!-- Cookie: A random cookie for identifying this instance of Pinos -->
|
||||
<property name='Cookie' type='u' access='read' />
|
||||
<!-- Properties: Extra properties of the daemon -->
|
||||
<property name='Properties' type='a{sv}' access='read' />
|
||||
<!-- ConnectClient:
|
||||
@properties: properties of the client
|
||||
@client: a new client object
|
||||
|
||||
Register a client to the pinos daemon. A new client object
|
||||
will be generated that should be used to perform any other
|
||||
action on the server.
|
||||
-->
|
||||
<method name='ConnectClient'>
|
||||
<arg type='a{sv}' name='properties' direction='in'/>
|
||||
<arg type='o' name='client' direction='out'/>
|
||||
</method>
|
||||
</interface>
|
||||
|
||||
<!--
|
||||
org.pinos.Client1:
|
||||
@short_description: Main client interface
|
||||
|
||||
Interface obtained after connecting a client and allows for
|
||||
obtaining an output object from a source.
|
||||
-->
|
||||
<interface name='org.pinos.Client1'>
|
||||
<!-- Name: Name of the client -->
|
||||
<property name='Name' type='s' access='read' />
|
||||
<!-- Name: Properties of the client -->
|
||||
<property name='Properties' type='a{sv}' access='read' />
|
||||
<!-- Disconnect:
|
||||
|
||||
Disconnect the client from the server.
|
||||
-->
|
||||
<method name='Disconnect'/>
|
||||
<!-- CreateSourceOutput:
|
||||
@source: the Source1 object path or / for default
|
||||
@accepted_formats: the formats that can be accepted
|
||||
@properties: extra properties
|
||||
@output: the SourceOutput1 object path
|
||||
|
||||
Create a new output for @source with given @incaps
|
||||
-->
|
||||
<method name='CreateSourceOutput'>
|
||||
<arg type='s' name='source' direction='in'/>
|
||||
<arg type='s' name='accepted_formats' direction='in'/>
|
||||
<arg type='a{sv}' name='properties' direction='in'/>
|
||||
<arg type='o' name='output' direction='out'/>
|
||||
</method>
|
||||
<!-- CreateSourceInput:
|
||||
@possible_formats: the formats that can be provided
|
||||
@properties: extra properties
|
||||
@input: the SourceInput1 object path
|
||||
|
||||
Create a new source and input object with given @incaps
|
||||
-->
|
||||
<method name='CreateSourceInput'>
|
||||
<arg type='s' name='possible_formats' direction='in'/>
|
||||
<arg type='a{sv}' name='properties' direction='in'/>
|
||||
<arg type='o' name='input' direction='out'/>
|
||||
</method>
|
||||
</interface>
|
||||
|
||||
<!--
|
||||
org.pinos.Source1:
|
||||
@short_description: Main source interface
|
||||
|
||||
A source is an object that can provide media content.
|
||||
-->
|
||||
<interface name='org.pinos.Source1'>
|
||||
<!-- Name: the name of the source -->
|
||||
<property name='Name' type='s' access='read' />
|
||||
<!-- Properties: extra source properties -->
|
||||
<property name='Properties' type='a{sv}' access='read' />
|
||||
<!-- state: state of the source
|
||||
-1 = the source is in error
|
||||
0 = the source is suspended, this means the device is closed
|
||||
1 = the source is initializing
|
||||
2 = the source is idle, this means the device is opened but
|
||||
no source-output is consuming the data
|
||||
3 = the source is running
|
||||
-->
|
||||
<property name='State' type='u' access='read' />
|
||||
<!-- PossibleFormats:
|
||||
|
||||
The all possible formats of this source.
|
||||
-->
|
||||
<property name='PossibleFormats' type='s' access='read' />
|
||||
</interface>
|
||||
|
||||
<!--
|
||||
org.pinos.SourceOutput1:
|
||||
@short_description: Interface for source output
|
||||
|
||||
This interface is used to control the output of a source and
|
||||
start/stop the media transport.
|
||||
-->
|
||||
<interface name='org.pinos.SourceOutput1'>
|
||||
<!-- Client: the owner client of this source output -->
|
||||
<property name='Client' type='o' access='read' />
|
||||
<!-- Source: the source of this source output -->
|
||||
<property name='Source' type='o' access='read' />
|
||||
<!-- PossibleFormats:
|
||||
all possible formats of the source output. This is filtered
|
||||
against the accepted_formats when creating the source output.
|
||||
-->
|
||||
<property name='PossibleFormats' type='s' access='read' />
|
||||
|
||||
<!-- Properties: extra source output properties -->
|
||||
<property name='Properties' type='a{sv}' access='read' />
|
||||
|
||||
<!-- state: state of the source output
|
||||
-1 = the source output is in error
|
||||
0 = the source output is idle
|
||||
1 = the source output is starting
|
||||
2 = the source output is streaming
|
||||
-->
|
||||
<property name='State' type='u' access='read' />
|
||||
<!-- Format: the current streaming format -->
|
||||
<property name='Format' type='s' access='read' />
|
||||
|
||||
<!-- Start:
|
||||
@requested_format: requested formats
|
||||
@fd: output file descriptor
|
||||
@format: output format
|
||||
@properties: output properties
|
||||
|
||||
Start the datatransfer of the source with @requested_format.
|
||||
|
||||
The result is a file descriptor that can be used to get metadata
|
||||
and media. @format contains the final media format and @properties
|
||||
the extra properties that describe the media format.
|
||||
-->
|
||||
<method name='Start'>
|
||||
<arg type='s' name='requested_format' direction='in'/>
|
||||
<arg type='h' name='fd' direction='out'/>
|
||||
<arg type='s' name='format' direction='out'/>
|
||||
<arg type='a{sv}' name='properties' direction='out'/>
|
||||
</method>
|
||||
<!-- Stop:
|
||||
|
||||
Stop data transport
|
||||
-->
|
||||
<method name='Stop'/>
|
||||
<!-- Remove:
|
||||
|
||||
Remove the source output
|
||||
-->
|
||||
<method name='Remove'/>
|
||||
|
||||
<!-- This is a possible push based API. It would require a dbus call
|
||||
for each media packet, which is probably not a good idea right now.
|
||||
We would probably also want something more lightweight for sending
|
||||
the properties with each fd.
|
||||
|
||||
<method name='Start'>
|
||||
<arg type='a{sv}' name='props' direction='in'/>
|
||||
<arg type='a{sv}' name='props' direction='out'/>
|
||||
</method>
|
||||
<method name='Stop'>
|
||||
</method>
|
||||
<signal name='NewData'>
|
||||
<arg type='h' name='data' direction='out'/>
|
||||
<arg type='a{sv}' name='props' direction='out'/>
|
||||
</signal>
|
||||
<method name='FreeData'>
|
||||
<arg type='h' name='data' direction='in'/>
|
||||
</method>
|
||||
-->
|
||||
</interface>
|
||||
</node>
|
||||
|
|
@ -1,68 +0,0 @@
|
|||
/* GStreamer
|
||||
* Copyright (C) <2015> Wim Taymans <wim.taymans@gmail.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
/**
|
||||
* SECTION:element-pinossrc
|
||||
*
|
||||
* <refsect2>
|
||||
* <title>Example launch line</title>
|
||||
* |[
|
||||
* gst-launch -v pinossrc ! ximagesink
|
||||
* ]| Shows pinos output in an X window.
|
||||
* </refsect2>
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include "gstpinossrc.h"
|
||||
#include "gstpinossink.h"
|
||||
#include "gstpinosdeviceprovider.h"
|
||||
#include "gstpinospay.h"
|
||||
#include "gstpinosdepay.h"
|
||||
|
||||
GST_DEBUG_CATEGORY (pinos_debug);
|
||||
|
||||
static gboolean
|
||||
plugin_init (GstPlugin * plugin)
|
||||
{
|
||||
gst_element_register (plugin, "pinospay", GST_RANK_NONE,
|
||||
GST_TYPE_PINOS_PAY);
|
||||
gst_element_register (plugin, "pinosdepay", GST_RANK_NONE,
|
||||
GST_TYPE_PINOS_DEPAY);
|
||||
gst_element_register (plugin, "pinossrc", GST_RANK_PRIMARY + 1,
|
||||
GST_TYPE_PINOS_SRC);
|
||||
gst_element_register (plugin, "pinossink", GST_RANK_NONE,
|
||||
GST_TYPE_PINOS_SINK);
|
||||
|
||||
if (!gst_device_provider_register (plugin, "pinosdeviceprovider",
|
||||
GST_RANK_PRIMARY + 1, GST_TYPE_PINOS_DEVICE_PROVIDER))
|
||||
return FALSE;
|
||||
|
||||
GST_DEBUG_CATEGORY_INIT (pinos_debug, "pinos", 0, "Pinos elements");
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
|
||||
GST_VERSION_MINOR,
|
||||
pinos,
|
||||
"Uses pinos to handle media streams",
|
||||
plugin_init, VERSION, "LGPL", "pinos", "pinos.org")
|
||||
|
|
@ -1,206 +0,0 @@
|
|||
/* GStreamer
|
||||
* Copyright (C) 2014 William Manley <will@williammanley.net>
|
||||
* (C) 2015 Wim Taymans <wim.taymans@gmail.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 51 Franklin Street, Suite 500,
|
||||
* Boston, MA 02110-1335, USA.
|
||||
*/
|
||||
/**
|
||||
* SECTION:element-gstpinosdepay
|
||||
*
|
||||
* The pinosdepay element does FIXME stuff.
|
||||
*
|
||||
* <refsect2>
|
||||
* <title>Example launch line</title>
|
||||
* |[
|
||||
* gst-launch -v fakesrc ! pinosdepay ! FIXME ! fakesink
|
||||
* ]|
|
||||
* FIXME Describe what the pipeline does.
|
||||
* </refsect2>
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include "gstpinosdepay.h"
|
||||
#include <gst/net/gstnetcontrolmessagemeta.h>
|
||||
|
||||
#include <gst/gst.h>
|
||||
#include <gst/base/gstbasetransform.h>
|
||||
#include <gst/allocators/gstfdmemory.h>
|
||||
#include <gio/gunixfdmessage.h>
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <string.h>
|
||||
#include <sys/socket.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <client/pinos.h>
|
||||
|
||||
GST_DEBUG_CATEGORY_STATIC (gst_pinos_depay_debug_category);
|
||||
#define GST_CAT_DEFAULT gst_pinos_depay_debug_category
|
||||
|
||||
/* prototypes */
|
||||
|
||||
|
||||
/* pad templates */
|
||||
static GstStaticPadTemplate gst_pinos_depay_src_template =
|
||||
GST_STATIC_PAD_TEMPLATE ("src",
|
||||
GST_PAD_SRC,
|
||||
GST_PAD_ALWAYS,
|
||||
GST_STATIC_CAPS_ANY);
|
||||
|
||||
static GstStaticPadTemplate gst_pinos_depay_sink_template =
|
||||
GST_STATIC_PAD_TEMPLATE ("sink",
|
||||
GST_PAD_SINK,
|
||||
GST_PAD_ALWAYS,
|
||||
GST_STATIC_CAPS ("application/x-pinos"));
|
||||
|
||||
|
||||
/* class initialization */
|
||||
|
||||
G_DEFINE_TYPE_WITH_CODE (GstPinosDepay, gst_pinos_depay, GST_TYPE_ELEMENT,
|
||||
GST_DEBUG_CATEGORY_INIT (gst_pinos_depay_debug_category, "pinosdepay", 0,
|
||||
"debug category for pinosdepay element"));
|
||||
|
||||
static GstFlowReturn
|
||||
gst_pinos_depay_chain (GstPad *pad, GstObject * parent, GstBuffer * buffer)
|
||||
{
|
||||
GstPinosDepay *depay = GST_PINOS_DEPAY (parent);
|
||||
GstBuffer *outbuf;
|
||||
GstMapInfo info;
|
||||
PinosBuffer pbuf;
|
||||
PinosBufferIter it;
|
||||
GstNetControlMessageMeta * meta;
|
||||
GSocketControlMessage *msg = NULL;
|
||||
GError *err = NULL;
|
||||
|
||||
meta = ((GstNetControlMessageMeta*) gst_buffer_get_meta (
|
||||
buffer, GST_NET_CONTROL_MESSAGE_META_API_TYPE));
|
||||
if (meta) {
|
||||
msg = g_object_ref (meta->message);
|
||||
gst_buffer_remove_meta (buffer, (GstMeta *) meta);
|
||||
meta = NULL;
|
||||
}
|
||||
|
||||
if (msg == NULL) {
|
||||
gst_buffer_unref (buffer);
|
||||
return GST_FLOW_OK;
|
||||
}
|
||||
|
||||
outbuf = gst_buffer_new ();
|
||||
|
||||
gst_buffer_map (buffer, &info, GST_MAP_READ);
|
||||
pinos_buffer_init_data (&pbuf, info.data, info.size, msg);
|
||||
|
||||
pinos_buffer_iter_init (&it, &pbuf);
|
||||
while (pinos_buffer_iter_next (&it)) {
|
||||
switch (pinos_buffer_iter_get_type (&it)) {
|
||||
case PINOS_PACKET_TYPE_HEADER:
|
||||
{
|
||||
PinosPacketHeader hdr;
|
||||
|
||||
if (!pinos_buffer_iter_parse_header (&it, &hdr))
|
||||
goto error;
|
||||
|
||||
GST_BUFFER_OFFSET (outbuf) = hdr.seq;
|
||||
break;
|
||||
}
|
||||
case PINOS_PACKET_TYPE_FD_PAYLOAD:
|
||||
{
|
||||
GstMemory *fdmem = NULL;
|
||||
PinosPacketFDPayload p;
|
||||
int fd;
|
||||
|
||||
if (!pinos_buffer_iter_parse_fd_payload (&it, &p))
|
||||
goto error;
|
||||
fd = pinos_buffer_get_fd (&pbuf, p.fd_index, &err);
|
||||
if (fd == -1)
|
||||
goto error;
|
||||
|
||||
fdmem = gst_fd_allocator_alloc (depay->fd_allocator, fd,
|
||||
p.offset + p.size, GST_FD_MEMORY_FLAG_NONE);
|
||||
gst_memory_resize (fdmem, p.offset, p.size);
|
||||
gst_buffer_append_memory (outbuf, fdmem);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
pinos_buffer_clear (&pbuf);
|
||||
gst_buffer_unmap (buffer, &info);
|
||||
gst_buffer_unref (buffer);
|
||||
|
||||
return gst_pad_push (depay->srcpad, outbuf);
|
||||
|
||||
error:
|
||||
{
|
||||
GST_ELEMENT_ERROR (depay, RESOURCE, SETTINGS, (NULL),
|
||||
("can't get fd: %s", err->message));
|
||||
g_clear_error (&err);
|
||||
gst_buffer_unref (outbuf);
|
||||
return GST_FLOW_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gst_pinos_depay_finalize (GObject * object)
|
||||
{
|
||||
GstPinosDepay *depay = GST_PINOS_DEPAY (object);
|
||||
|
||||
GST_DEBUG_OBJECT (depay, "finalize");
|
||||
|
||||
g_object_unref (depay->fd_allocator);
|
||||
|
||||
G_OBJECT_CLASS (gst_pinos_depay_parent_class)->finalize (object);
|
||||
}
|
||||
|
||||
static void
|
||||
gst_pinos_depay_class_init (GstPinosDepayClass * klass)
|
||||
{
|
||||
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
|
||||
GstElementClass *element_class =
|
||||
GST_ELEMENT_CLASS (klass);
|
||||
|
||||
/* Setting up pads and setting metadata should be moved to
|
||||
base_class_init if you intend to subclass this class. */
|
||||
gst_element_class_add_pad_template (element_class,
|
||||
gst_static_pad_template_get (&gst_pinos_depay_src_template));
|
||||
gst_element_class_add_pad_template (element_class,
|
||||
gst_static_pad_template_get (&gst_pinos_depay_sink_template));
|
||||
|
||||
gst_element_class_set_static_metadata (element_class,
|
||||
"Pinos Deplayloder", "Generic",
|
||||
"Pinos Depayloader for zero-copy IPC via Pinos",
|
||||
"Wim Taymans <wim.taymans@gmail.com>");
|
||||
|
||||
gobject_class->finalize = gst_pinos_depay_finalize;
|
||||
}
|
||||
|
||||
static void
|
||||
gst_pinos_depay_init (GstPinosDepay * depay)
|
||||
{
|
||||
depay->srcpad = gst_pad_new_from_static_template (&gst_pinos_depay_src_template, "src");
|
||||
gst_element_add_pad (GST_ELEMENT (depay), depay->srcpad);
|
||||
|
||||
depay->sinkpad = gst_pad_new_from_static_template (&gst_pinos_depay_sink_template, "sink");
|
||||
gst_pad_set_chain_function (depay->sinkpad, gst_pinos_depay_chain);
|
||||
gst_element_add_pad (GST_ELEMENT (depay), depay->sinkpad);
|
||||
|
||||
depay->fd_allocator = gst_fd_allocator_new ();
|
||||
}
|
||||
|
||||
|
|
@ -1,54 +0,0 @@
|
|||
/* GStreamer
|
||||
* Copyright (C) 2014 William Manley <will@williammanley.net>
|
||||
* Copyright (C) 2015 Wim Taymans <wim.taymans@gmail.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#ifndef _GST_PINOS_DEPAY_H_
|
||||
#define _GST_PINOS_DEPAY_H_
|
||||
|
||||
#include <gst/gst.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define GST_TYPE_PINOS_DEPAY (gst_pinos_depay_get_type())
|
||||
#define GST_PINOS_DEPAY(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_PINOS_DEPAY,GstPinosDepay))
|
||||
#define GST_PINOS_DEPAY_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_PINOS_DEPAY,GstPinosDepayClass))
|
||||
#define GST_IS_PINOS_DEPAY(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_PINOS_DEPAY))
|
||||
#define GST_IS_PINOS_DEPAY_CLASS(obj) (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_PINOS_DEPAY))
|
||||
|
||||
typedef struct _GstPinosDepay GstPinosDepay;
|
||||
typedef struct _GstPinosDepayClass GstPinosDepayClass;
|
||||
|
||||
struct _GstPinosDepay
|
||||
{
|
||||
GstElement parent;
|
||||
|
||||
GstPad *srcpad, *sinkpad;
|
||||
GstAllocator *fd_allocator;
|
||||
};
|
||||
|
||||
struct _GstPinosDepayClass
|
||||
{
|
||||
GstElementClass parent_class;
|
||||
};
|
||||
|
||||
GType gst_pinos_depay_get_type (void);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif
|
||||
|
|
@ -1,646 +0,0 @@
|
|||
/* GStreamer
|
||||
* Copyright (C) 2012 Olivier Crete <olivier.crete@collabora.com>
|
||||
* (C) 2015 Wim Taymans <wim.taymans@gmail.com>
|
||||
*
|
||||
* pinosdeviceprovider.c: pinos device probing and monitoring
|
||||
*
|
||||
* 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., 59 Temple Place - Suite 330,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include <gst/gst.h>
|
||||
|
||||
#include "gstpinosdeviceprovider.h"
|
||||
#include "gstpinossrc.h"
|
||||
#include "gstpinossink.h"
|
||||
|
||||
GST_DEBUG_CATEGORY_EXTERN (pinos_debug);
|
||||
#define GST_CAT_DEFAULT pinos_debug
|
||||
|
||||
G_DEFINE_TYPE (GstPinosDevice, gst_pinos_device, GST_TYPE_DEVICE);
|
||||
|
||||
enum
|
||||
{
|
||||
PROP_PATH = 1,
|
||||
};
|
||||
|
||||
static GstDevice *
|
||||
gst_pinos_device_new (gpointer id, const gchar * device_name,
|
||||
GstCaps * caps, const gchar * path, const gchar *klass,
|
||||
GstPinosDeviceType type, GstStructure *props)
|
||||
{
|
||||
GstPinosDevice *gstdev;
|
||||
const gchar *element = NULL;
|
||||
|
||||
g_return_val_if_fail (device_name, NULL);
|
||||
g_return_val_if_fail (path, NULL);
|
||||
g_return_val_if_fail (caps, NULL);
|
||||
|
||||
switch (type) {
|
||||
case GST_PINOS_DEVICE_TYPE_SOURCE:
|
||||
element = "pinossrc";
|
||||
break;
|
||||
case GST_PINOS_DEVICE_TYPE_SINK:
|
||||
element = "pinossink";
|
||||
break;
|
||||
default:
|
||||
g_assert_not_reached ();
|
||||
break;
|
||||
}
|
||||
|
||||
gstdev = g_object_new (GST_TYPE_PINOS_DEVICE,
|
||||
"display-name", device_name, "caps", caps, "device-class", klass,
|
||||
"path", path, "properties", props, NULL);
|
||||
|
||||
gstdev->id = id;
|
||||
gstdev->type = type;
|
||||
gstdev->element = element;
|
||||
|
||||
return GST_DEVICE (gstdev);
|
||||
}
|
||||
|
||||
static GstElement *
|
||||
gst_pinos_device_create_element (GstDevice * device, const gchar * name)
|
||||
{
|
||||
GstPinosDevice *pinos_dev = GST_PINOS_DEVICE (device);
|
||||
GstElement *elem;
|
||||
|
||||
elem = gst_element_factory_make (pinos_dev->element, name);
|
||||
g_object_set (elem, "path", pinos_dev->path, NULL);
|
||||
|
||||
return elem;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gst_pinos_device_reconfigure_element (GstDevice * device, GstElement * element)
|
||||
{
|
||||
GstPinosDevice *pinos_dev = GST_PINOS_DEVICE (device);
|
||||
|
||||
if (!strcmp (pinos_dev->element, "pinossrc")) {
|
||||
if (!GST_IS_PINOS_SRC (element))
|
||||
return FALSE;
|
||||
} else if (!strcmp (pinos_dev->element, "pinossink")) {
|
||||
if (!GST_IS_PINOS_SINK (element))
|
||||
return FALSE;
|
||||
} else {
|
||||
g_assert_not_reached ();
|
||||
}
|
||||
|
||||
g_object_set (element, "path", pinos_dev->path, NULL);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
gst_pinos_device_get_property (GObject * object, guint prop_id,
|
||||
GValue * value, GParamSpec * pspec)
|
||||
{
|
||||
GstPinosDevice *device;
|
||||
|
||||
device = GST_PINOS_DEVICE_CAST (object);
|
||||
|
||||
switch (prop_id) {
|
||||
case PROP_PATH:
|
||||
g_value_set_string (value, device->path);
|
||||
break;
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gst_pinos_device_set_property (GObject * object, guint prop_id,
|
||||
const GValue * value, GParamSpec * pspec)
|
||||
{
|
||||
GstPinosDevice *device;
|
||||
|
||||
device = GST_PINOS_DEVICE_CAST (object);
|
||||
|
||||
switch (prop_id) {
|
||||
case PROP_PATH:
|
||||
device->path = g_value_dup_string (value);
|
||||
break;
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gst_pinos_device_finalize (GObject * object)
|
||||
{
|
||||
GstPinosDevice *device = GST_PINOS_DEVICE (object);
|
||||
|
||||
g_free (device->path);
|
||||
|
||||
G_OBJECT_CLASS (gst_pinos_device_parent_class)->finalize (object);
|
||||
}
|
||||
|
||||
static void
|
||||
gst_pinos_device_class_init (GstPinosDeviceClass * klass)
|
||||
{
|
||||
GstDeviceClass *dev_class = GST_DEVICE_CLASS (klass);
|
||||
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
||||
|
||||
dev_class->create_element = gst_pinos_device_create_element;
|
||||
dev_class->reconfigure_element = gst_pinos_device_reconfigure_element;
|
||||
|
||||
object_class->get_property = gst_pinos_device_get_property;
|
||||
object_class->set_property = gst_pinos_device_set_property;
|
||||
object_class->finalize = gst_pinos_device_finalize;
|
||||
|
||||
g_object_class_install_property (object_class, PROP_PATH,
|
||||
g_param_spec_string ("path", "Path",
|
||||
"The internal path of the Pinos device", "",
|
||||
G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
|
||||
}
|
||||
|
||||
static void
|
||||
gst_pinos_device_init (GstPinosDevice * device)
|
||||
{
|
||||
}
|
||||
|
||||
G_DEFINE_TYPE (GstPinosDeviceProvider, gst_pinos_device_provider,
|
||||
GST_TYPE_DEVICE_PROVIDER);
|
||||
|
||||
enum
|
||||
{
|
||||
PROP_0,
|
||||
PROP_CLIENT_NAME,
|
||||
PROP_LAST
|
||||
};
|
||||
|
||||
static GstDevice *
|
||||
new_source (const PinosSourceInfo *info)
|
||||
{
|
||||
GstCaps *caps;
|
||||
GstStructure *props;
|
||||
gpointer state = NULL;
|
||||
const gchar *klass;
|
||||
|
||||
if (info->possible_formats)
|
||||
caps = gst_caps_from_string (g_bytes_get_data (info->possible_formats, NULL));
|
||||
else
|
||||
caps = gst_caps_new_any();
|
||||
|
||||
|
||||
props = gst_structure_new_empty ("pinos-proplist");
|
||||
|
||||
while (TRUE) {
|
||||
const char *key, *val;
|
||||
|
||||
if (!(key = pinos_properties_iterate (info->properties, &state)))
|
||||
break;
|
||||
|
||||
val = pinos_properties_get (info->properties, key);
|
||||
gst_structure_set (props, key, G_TYPE_STRING, val, NULL);
|
||||
}
|
||||
|
||||
klass = pinos_properties_get (info->properties, "gstreamer.device.class");
|
||||
if (klass == NULL)
|
||||
klass = "unknown/unknown";
|
||||
|
||||
return gst_pinos_device_new (info->id,
|
||||
info->name,
|
||||
caps,
|
||||
info->source_path,
|
||||
klass,
|
||||
GST_PINOS_DEVICE_TYPE_SOURCE,
|
||||
props);
|
||||
}
|
||||
|
||||
static void
|
||||
get_source_info_cb (PinosContext *context,
|
||||
const PinosSourceInfo *info,
|
||||
gpointer user_data)
|
||||
{
|
||||
GstPinosDeviceProvider *self = user_data;
|
||||
GstDevice *dev;
|
||||
|
||||
dev = new_source (info);
|
||||
if (dev)
|
||||
gst_device_provider_device_add (GST_DEVICE_PROVIDER (self), dev);
|
||||
}
|
||||
|
||||
static GstPinosDevice *
|
||||
find_device (GstDeviceProvider *provider, gpointer id)
|
||||
{
|
||||
GList *item;
|
||||
GstPinosDevice *dev = NULL;
|
||||
|
||||
GST_OBJECT_LOCK (provider);
|
||||
for (item = provider->devices; item; item = item->next) {
|
||||
dev = item->data;
|
||||
if (dev->id == id) {
|
||||
gst_object_ref (dev);
|
||||
break;
|
||||
}
|
||||
dev = NULL;
|
||||
}
|
||||
GST_OBJECT_UNLOCK (provider);
|
||||
|
||||
return dev;
|
||||
}
|
||||
|
||||
static void
|
||||
context_subscribe_cb (PinosContext *context,
|
||||
PinosSubscriptionEvent type,
|
||||
PinosSubscriptionFlags flags,
|
||||
gpointer id,
|
||||
gpointer user_data)
|
||||
{
|
||||
GstPinosDeviceProvider *self = user_data;
|
||||
GstDeviceProvider *provider = user_data;
|
||||
GstPinosDevice *dev;
|
||||
|
||||
if (flags != PINOS_SUBSCRIPTION_FLAG_SOURCE)
|
||||
return;
|
||||
|
||||
dev = find_device (provider, id);
|
||||
|
||||
if (type == PINOS_SUBSCRIPTION_EVENT_NEW) {
|
||||
if (flags == PINOS_SUBSCRIPTION_FLAG_SOURCE && dev == NULL)
|
||||
pinos_context_get_source_info_by_id (context,
|
||||
id,
|
||||
PINOS_SOURCE_INFO_FLAGS_FORMATS,
|
||||
get_source_info_cb,
|
||||
NULL,
|
||||
NULL,
|
||||
self);
|
||||
} else if (type == PINOS_SUBSCRIPTION_EVENT_REMOVE) {
|
||||
if (flags == PINOS_SUBSCRIPTION_FLAG_SOURCE && dev != NULL) {
|
||||
gst_device_provider_device_remove (GST_DEVICE_PROVIDER (self),
|
||||
GST_DEVICE (dev));
|
||||
}
|
||||
}
|
||||
if (dev)
|
||||
gst_object_unref (dev);
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
gboolean end;
|
||||
GList **devices;
|
||||
} InfoData;
|
||||
|
||||
static void
|
||||
list_source_info_cb (PinosContext *c,
|
||||
const PinosSourceInfo *info,
|
||||
gpointer user_data)
|
||||
{
|
||||
InfoData *data = user_data;
|
||||
|
||||
*data->devices = g_list_prepend (*data->devices, gst_object_ref_sink (new_source (info)));
|
||||
}
|
||||
|
||||
static void
|
||||
list_source_info_end_cb (GObject *source_object,
|
||||
GAsyncResult *res,
|
||||
gpointer user_data)
|
||||
{
|
||||
InfoData *data = user_data;
|
||||
GError *error = NULL;
|
||||
|
||||
if (!pinos_context_info_finish (source_object, res, &error)) {
|
||||
GST_WARNING_OBJECT (source_object, "failed to list sources: %s", error->message);
|
||||
g_clear_error (&error);
|
||||
}
|
||||
data->end = TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
get_daemon_info_cb (PinosContext *c, const PinosDaemonInfo *info, gpointer user_data)
|
||||
{
|
||||
GstDeviceProvider *provider = user_data;
|
||||
const gchar *value;
|
||||
|
||||
value = pinos_properties_get (info->properties, "gstreamer.deviceproviders");
|
||||
if (value) {
|
||||
gchar **providers = g_strsplit (value, ",", -1);
|
||||
gint i;
|
||||
|
||||
GST_DEBUG_OBJECT (provider, "have hidden providers: %s", value);
|
||||
|
||||
for (i = 0; providers[i]; i++) {
|
||||
gst_device_provider_hide_provider (provider, providers[i]);
|
||||
}
|
||||
g_strfreev (providers);
|
||||
}
|
||||
}
|
||||
|
||||
static GList *
|
||||
gst_pinos_device_provider_probe (GstDeviceProvider * provider)
|
||||
{
|
||||
GstPinosDeviceProvider *self = GST_PINOS_DEVICE_PROVIDER (provider);
|
||||
GMainContext *m = NULL;
|
||||
PinosContext *c = NULL;
|
||||
InfoData data;
|
||||
|
||||
GST_DEBUG_OBJECT (self, "starting probe");
|
||||
|
||||
if (!(m = g_main_context_new ()))
|
||||
return NULL;
|
||||
|
||||
if (!(c = pinos_context_new (m, self->client_name, NULL)))
|
||||
goto failed;
|
||||
|
||||
g_main_context_push_thread_default (m);
|
||||
|
||||
pinos_context_connect (c, PINOS_CONTEXT_FLAGS_NONE);
|
||||
|
||||
for (;;) {
|
||||
PinosContextState state;
|
||||
|
||||
state = pinos_context_get_state (c);
|
||||
|
||||
if (state <= 0) {
|
||||
GST_ERROR_OBJECT (self, "Failed to connect: %s",
|
||||
pinos_context_get_error (c)->message);
|
||||
goto failed;
|
||||
}
|
||||
|
||||
if (state == PINOS_CONTEXT_STATE_READY)
|
||||
break;
|
||||
|
||||
/* Wait until something happens */
|
||||
g_main_context_iteration (m, TRUE);
|
||||
}
|
||||
GST_DEBUG_OBJECT (self, "connected");
|
||||
|
||||
pinos_context_get_daemon_info (c,
|
||||
PINOS_DAEMON_INFO_FLAGS_NONE,
|
||||
get_daemon_info_cb,
|
||||
NULL,
|
||||
NULL,
|
||||
self);
|
||||
|
||||
|
||||
data.end = FALSE;
|
||||
data.devices = NULL;
|
||||
pinos_context_list_source_info (c,
|
||||
PINOS_SOURCE_INFO_FLAGS_FORMATS,
|
||||
list_source_info_cb,
|
||||
NULL,
|
||||
list_source_info_end_cb,
|
||||
&data);
|
||||
for (;;) {
|
||||
if (pinos_context_get_state (c) <= 0)
|
||||
break;
|
||||
if (data.end)
|
||||
break;
|
||||
g_main_context_iteration (m, TRUE);
|
||||
}
|
||||
|
||||
pinos_context_disconnect (c);
|
||||
g_clear_object (&c);
|
||||
|
||||
g_main_context_pop_thread_default (m);
|
||||
g_main_context_unref (m);
|
||||
|
||||
return *data.devices;
|
||||
|
||||
failed:
|
||||
g_main_context_pop_thread_default (m);
|
||||
g_main_context_unref (m);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void
|
||||
context_state_notify (GObject *gobject,
|
||||
GParamSpec *pspec,
|
||||
gpointer user_data)
|
||||
{
|
||||
GstPinosDeviceProvider *self = user_data;
|
||||
PinosContext *context = PINOS_CONTEXT (gobject);
|
||||
PinosContextState state;
|
||||
|
||||
state= pinos_context_get_state (context);
|
||||
|
||||
GST_DEBUG ("got context state %d", state);
|
||||
|
||||
switch (state) {
|
||||
case PINOS_CONTEXT_STATE_CONNECTING:
|
||||
case PINOS_CONTEXT_STATE_REGISTERING:
|
||||
break;
|
||||
case PINOS_CONTEXT_STATE_UNCONNECTED:
|
||||
case PINOS_CONTEXT_STATE_READY:
|
||||
break;
|
||||
case PINOS_CONTEXT_STATE_ERROR:
|
||||
GST_ERROR_OBJECT (self, "context error: %s",
|
||||
pinos_context_get_error (context)->message);
|
||||
break;
|
||||
}
|
||||
pinos_main_loop_signal (self->loop, FALSE);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gst_pinos_device_provider_start (GstDeviceProvider * provider)
|
||||
{
|
||||
GstPinosDeviceProvider *self = GST_PINOS_DEVICE_PROVIDER (provider);
|
||||
GError *error = NULL;
|
||||
GMainContext *c;
|
||||
|
||||
GST_DEBUG_OBJECT (self, "starting provider");
|
||||
|
||||
c = g_main_context_new ();
|
||||
|
||||
if (!(self->loop = pinos_main_loop_new (c, "pinos-device-monitor"))) {
|
||||
GST_ERROR_OBJECT (self, "Could not create pinos mainloop");
|
||||
goto failed;
|
||||
}
|
||||
|
||||
if (!pinos_main_loop_start (self->loop, &error)) {
|
||||
GST_ERROR_OBJECT (self, "Could not start pinos mainloop: %s", error->message);
|
||||
g_clear_object (&self->loop);
|
||||
goto failed;
|
||||
}
|
||||
|
||||
pinos_main_loop_lock (self->loop);
|
||||
|
||||
if (!(self->context = pinos_context_new (c, self->client_name, NULL))) {
|
||||
GST_ERROR_OBJECT (self, "Failed to create context");
|
||||
pinos_main_loop_unlock (self->loop);
|
||||
pinos_main_loop_stop (self->loop);
|
||||
g_clear_object (&self->loop);
|
||||
goto failed;
|
||||
}
|
||||
|
||||
g_signal_connect (self->context,
|
||||
"notify::state",
|
||||
(GCallback) context_state_notify,
|
||||
self);
|
||||
|
||||
g_object_set (self->context,
|
||||
"subscription-mask", PINOS_SUBSCRIPTION_FLAGS_ALL,
|
||||
NULL);
|
||||
g_signal_connect (self->context,
|
||||
"subscription-event",
|
||||
(GCallback) context_subscribe_cb,
|
||||
self);
|
||||
|
||||
pinos_context_connect (self->context, PINOS_CONTEXT_FLAGS_NONE);
|
||||
for (;;) {
|
||||
PinosContextState state;
|
||||
|
||||
state = pinos_context_get_state (self->context);
|
||||
|
||||
if (state <= 0) {
|
||||
GST_WARNING_OBJECT (self, "Failed to connect: %s",
|
||||
pinos_context_get_error (self->context)->message);
|
||||
goto not_running;
|
||||
}
|
||||
|
||||
if (state == PINOS_CONTEXT_STATE_READY)
|
||||
break;
|
||||
|
||||
/* Wait until something happens */
|
||||
pinos_main_loop_wait (self->loop);
|
||||
}
|
||||
GST_DEBUG_OBJECT (self, "connected");
|
||||
pinos_context_get_daemon_info (self->context,
|
||||
PINOS_DAEMON_INFO_FLAGS_NONE,
|
||||
get_daemon_info_cb,
|
||||
NULL,
|
||||
NULL,
|
||||
self);
|
||||
pinos_main_loop_unlock (self->loop);
|
||||
|
||||
g_main_context_unref (c);
|
||||
|
||||
return TRUE;
|
||||
|
||||
failed:
|
||||
{
|
||||
g_main_context_unref (c);
|
||||
return FALSE;
|
||||
}
|
||||
not_running:
|
||||
{
|
||||
pinos_main_loop_unlock (self->loop);
|
||||
pinos_main_loop_stop (self->loop);
|
||||
g_clear_object (&self->context);
|
||||
g_clear_object (&self->loop);
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gst_pinos_device_provider_stop (GstDeviceProvider * provider)
|
||||
{
|
||||
GstPinosDeviceProvider *self = GST_PINOS_DEVICE_PROVIDER (provider);
|
||||
|
||||
if (self->context) {
|
||||
pinos_context_disconnect (self->context);
|
||||
}
|
||||
if (self->loop) {
|
||||
pinos_main_loop_stop (self->loop);
|
||||
}
|
||||
g_clear_object (&self->context);
|
||||
g_clear_object (&self->loop);
|
||||
}
|
||||
|
||||
static void
|
||||
gst_pinos_device_provider_set_property (GObject * object,
|
||||
guint prop_id, const GValue * value, GParamSpec * pspec)
|
||||
{
|
||||
GstPinosDeviceProvider *self = GST_PINOS_DEVICE_PROVIDER (object);
|
||||
|
||||
switch (prop_id) {
|
||||
case PROP_CLIENT_NAME:
|
||||
g_free (self->client_name);
|
||||
if (!g_value_get_string (value)) {
|
||||
GST_WARNING_OBJECT (self,
|
||||
"Empty Pinos client name not allowed. "
|
||||
"Resetting to default value");
|
||||
self->client_name = pinos_client_name ();
|
||||
} else
|
||||
self->client_name = g_value_dup_string (value);
|
||||
break;
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gst_pinos_device_provider_get_property (GObject * object,
|
||||
guint prop_id, GValue * value, GParamSpec * pspec)
|
||||
{
|
||||
GstPinosDeviceProvider *self = GST_PINOS_DEVICE_PROVIDER (object);
|
||||
|
||||
switch (prop_id) {
|
||||
case PROP_CLIENT_NAME:
|
||||
g_value_set_string (value, self->client_name);
|
||||
break;
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gst_pinos_device_provider_finalize (GObject * object)
|
||||
{
|
||||
GstPinosDeviceProvider *self = GST_PINOS_DEVICE_PROVIDER (object);
|
||||
|
||||
g_free (self->client_name);
|
||||
|
||||
G_OBJECT_CLASS (gst_pinos_device_provider_parent_class)->finalize (object);
|
||||
}
|
||||
|
||||
static void
|
||||
gst_pinos_device_provider_class_init (GstPinosDeviceProviderClass * klass)
|
||||
{
|
||||
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
|
||||
GstDeviceProviderClass *dm_class = GST_DEVICE_PROVIDER_CLASS (klass);
|
||||
gchar *client_name;
|
||||
|
||||
gobject_class->set_property = gst_pinos_device_provider_set_property;
|
||||
gobject_class->get_property = gst_pinos_device_provider_get_property;
|
||||
gobject_class->finalize = gst_pinos_device_provider_finalize;
|
||||
|
||||
dm_class->probe = gst_pinos_device_provider_probe;
|
||||
dm_class->start = gst_pinos_device_provider_start;
|
||||
dm_class->stop = gst_pinos_device_provider_stop;
|
||||
|
||||
client_name = pinos_client_name ();
|
||||
g_object_class_install_property (gobject_class,
|
||||
PROP_CLIENT_NAME,
|
||||
g_param_spec_string ("client-name", "Client Name",
|
||||
"The Pinos client_name_to_use", client_name,
|
||||
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS |
|
||||
GST_PARAM_MUTABLE_READY));
|
||||
g_free (client_name);
|
||||
|
||||
gst_device_provider_class_set_static_metadata (dm_class,
|
||||
"Pinos Device Provider", "Sink/Source/Audio/Video",
|
||||
"List and provide Pinos source and sink devices",
|
||||
"Wim Taymans <wim.taymans@gmail.com>");
|
||||
}
|
||||
|
||||
static void
|
||||
gst_pinos_device_provider_init (GstPinosDeviceProvider * self)
|
||||
{
|
||||
self->client_name = pinos_client_name ();
|
||||
}
|
||||
|
||||
|
|
@ -1,98 +0,0 @@
|
|||
/* GStreamer
|
||||
* Copyright (C) 2012 Olivier Crete <olivier.crete@collabora.com>
|
||||
* (C) 2015 Wim Taymans <wim.taymans@gmail.com>
|
||||
*
|
||||
* pinosdeviceprovider.h: Device probing and monitoring
|
||||
*
|
||||
* 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., 59 Temple Place - Suite 330,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
|
||||
#ifndef __GST_PINOS_DEVICE_PROVIDER_H__
|
||||
#define __GST_PINOS_DEVICE_PROVIDER_H__
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include <client/pinos.h>
|
||||
|
||||
#include <gst/gst.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
typedef struct _GstPinosDevice GstPinosDevice;
|
||||
typedef struct _GstPinosDeviceClass GstPinosDeviceClass;
|
||||
|
||||
#define GST_TYPE_PINOS_DEVICE (gst_pinos_device_get_type())
|
||||
#define GST_IS_PINOS_DEVICE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_PINOS_DEVICE))
|
||||
#define GST_IS_PINOS_DEVICE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GST_TYPE_PINOS_DEVICE))
|
||||
#define GST_PINOS_DEVICE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_PINOS_DEVICE, GstPinosDeviceClass))
|
||||
#define GST_PINOS_DEVICE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_PINOS_DEVICE, GstPinosDevice))
|
||||
#define GST_PINOS_DEVICE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GST_TYPE_DEVICE, GstPinosDeviceClass))
|
||||
#define GST_PINOS_DEVICE_CAST(obj) ((GstPinosDevice *)(obj))
|
||||
|
||||
typedef enum {
|
||||
GST_PINOS_DEVICE_TYPE_SOURCE,
|
||||
GST_PINOS_DEVICE_TYPE_SINK,
|
||||
} GstPinosDeviceType;
|
||||
|
||||
struct _GstPinosDevice {
|
||||
GstDevice parent;
|
||||
|
||||
GstPinosDeviceType type;
|
||||
gpointer id;
|
||||
gchar *path;
|
||||
const gchar *element;
|
||||
};
|
||||
|
||||
struct _GstPinosDeviceClass {
|
||||
GstDeviceClass parent_class;
|
||||
};
|
||||
|
||||
GType gst_pinos_device_get_type (void);
|
||||
|
||||
typedef struct _GstPinosDeviceProvider GstPinosDeviceProvider;
|
||||
typedef struct _GstPinosDeviceProviderClass GstPinosDeviceProviderClass;
|
||||
|
||||
#define GST_TYPE_PINOS_DEVICE_PROVIDER (gst_pinos_device_provider_get_type())
|
||||
#define GST_IS_PINOS_DEVICE_PROVIDER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_PINOS_DEVICE_PROVIDER))
|
||||
#define GST_IS_PINOS_DEVICE_PROVIDER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GST_TYPE_PINOS_DEVICE_PROVIDER))
|
||||
#define GST_PINOS_DEVICE_PROVIDER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_PINOS_DEVICE_PROVIDER, GstPinosDeviceProviderClass))
|
||||
#define GST_PINOS_DEVICE_PROVIDER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_PINOS_DEVICE_PROVIDER, GstPinosDeviceProvider))
|
||||
#define GST_PINOS_DEVICE_PROVIDER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GST_TYPE_DEVICE_PROVIDER, GstPinosDeviceProviderClass))
|
||||
#define GST_PINOS_DEVICE_PROVIDER_CAST(obj) ((GstPinosDeviceProvider *)(obj))
|
||||
|
||||
struct _GstPinosDeviceProvider {
|
||||
GstDeviceProvider parent;
|
||||
|
||||
gchar *client_name;
|
||||
|
||||
GMainContext *maincontext;
|
||||
PinosMainLoop *loop;
|
||||
|
||||
PinosContext *context;
|
||||
};
|
||||
|
||||
struct _GstPinosDeviceProviderClass {
|
||||
GstDeviceProviderClass parent_class;
|
||||
};
|
||||
|
||||
GType gst_pinos_device_provider_get_type (void);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __GST_PINOS_DEVICE_PROVIDER_H__ */
|
||||
|
|
@ -1,521 +0,0 @@
|
|||
/* GStreamer
|
||||
* Copyright (C) 2014 William Manley <will@williammanley.net>
|
||||
*
|
||||
* 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 Street, Suite 500,
|
||||
* Boston, MA 02110-1335, USA.
|
||||
*/
|
||||
/**
|
||||
* SECTION:element-gstpinospay
|
||||
*
|
||||
* The pinospay element converts regular GStreamer buffers into the format
|
||||
* expected by Pinos.
|
||||
*
|
||||
* <refsect2>
|
||||
* <title>Example launch line</title>
|
||||
* |[
|
||||
* gst-launch-1.0 -v videotestsrc ! video/x-raw,format=RGB,width=1920,height=1080 \
|
||||
* ! pinospay ! fdsink fd=1 \
|
||||
* | gst-launch-1.0 fdsrc fd=0 ! fddepay \
|
||||
* ! video/x-raw,format=RGB,width=1920,height=1080 ! autovideosink
|
||||
* ]|
|
||||
* Video frames are created in the first gst-launch-1.0 process and displayed
|
||||
* by the second with no copying.
|
||||
* </refsect2>
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include <gst/gst.h>
|
||||
#include <gst/allocators/gstfdmemory.h>
|
||||
#include <gst/base/gstbasetransform.h>
|
||||
#include "gstpinospay.h"
|
||||
#include "gsttmpfileallocator.h"
|
||||
|
||||
#include <gst/net/gstnetcontrolmessagemeta.h>
|
||||
#include <gio/gunixfdmessage.h>
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <client/pinos.h>
|
||||
|
||||
GST_DEBUG_CATEGORY_STATIC (gst_pinos_pay_debug_category);
|
||||
#define GST_CAT_DEFAULT gst_pinos_pay_debug_category
|
||||
|
||||
static GQuark fdids_quark;
|
||||
static GQuark orig_buffer_quark;
|
||||
|
||||
/* prototypes */
|
||||
|
||||
/* pad templates */
|
||||
static GstStaticPadTemplate gst_pinos_pay_src_template =
|
||||
GST_STATIC_PAD_TEMPLATE ("src",
|
||||
GST_PAD_SRC,
|
||||
GST_PAD_ALWAYS,
|
||||
GST_STATIC_CAPS ("application/x-pinos"));
|
||||
|
||||
static GstStaticPadTemplate gst_pinos_pay_sink_template =
|
||||
GST_STATIC_PAD_TEMPLATE ("sink",
|
||||
GST_PAD_SINK,
|
||||
GST_PAD_ALWAYS,
|
||||
GST_STATIC_CAPS_ANY);
|
||||
|
||||
/* class initialization */
|
||||
|
||||
G_DEFINE_TYPE_WITH_CODE (GstPinosPay, gst_pinos_pay, GST_TYPE_ELEMENT,
|
||||
GST_DEBUG_CATEGORY_INIT (gst_pinos_pay_debug_category, "pinospay", 0,
|
||||
"debug category for pinospay element"));
|
||||
|
||||
/* propose allocation query parameters for input buffers */
|
||||
static gboolean
|
||||
gst_pinos_pay_query (GstPad * pad, GstObject * parent, GstQuery * query)
|
||||
{
|
||||
GstPinosPay *pay = GST_PINOS_PAY (parent);
|
||||
gboolean res = FALSE;
|
||||
|
||||
switch (GST_QUERY_TYPE (query)) {
|
||||
case GST_QUERY_ALLOCATION:
|
||||
{
|
||||
GST_DEBUG_OBJECT (pay, "propose_allocation");
|
||||
gst_query_add_allocation_param (query, pay->allocator, NULL);
|
||||
res = TRUE;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
res = gst_pad_query_default (pad, parent, query);
|
||||
break;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
do_allocation (GstPinosPay *pay, GstCaps *caps)
|
||||
{
|
||||
GstQuery *query;
|
||||
|
||||
GST_DEBUG_OBJECT (pay, "doing allocation query");
|
||||
query = gst_query_new_allocation (caps, TRUE);
|
||||
if (!gst_pad_peer_query (pay->srcpad, query)) {
|
||||
/* not a problem, just debug a little */
|
||||
GST_DEBUG_OBJECT (pay, "peer ALLOCATION query failed");
|
||||
}
|
||||
if (!gst_query_find_allocation_meta (query,
|
||||
GST_NET_CONTROL_MESSAGE_META_API_TYPE, NULL))
|
||||
goto no_meta;
|
||||
|
||||
return TRUE;
|
||||
|
||||
/* ERRORS */
|
||||
no_meta:
|
||||
{
|
||||
GST_ELEMENT_ERROR (pay, STREAM, FORMAT,
|
||||
("Incompatible downstream element"),
|
||||
("The downstream element does not handle control-message metadata API"));
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gst_pinos_pay_sink_event (GstPad * pad, GstObject * parent, GstEvent * event)
|
||||
{
|
||||
GstPinosPay *pay = GST_PINOS_PAY (parent);
|
||||
gboolean res = FALSE;
|
||||
|
||||
switch (GST_EVENT_TYPE (event)) {
|
||||
case GST_EVENT_CAPS:
|
||||
{
|
||||
GstCaps *caps;
|
||||
GstStructure *str;
|
||||
|
||||
gst_event_parse_caps (event, &caps);
|
||||
str = gst_caps_get_structure (caps, 0);
|
||||
pay->pinos_input = gst_structure_has_name (str, "application/x-pinos");
|
||||
gst_event_unref (event);
|
||||
|
||||
caps = gst_caps_new_empty_simple ("application/x-pinos");
|
||||
res = gst_pad_push_event (pay->srcpad, gst_event_new_caps (caps));
|
||||
gst_caps_unref (caps);
|
||||
|
||||
if (res)
|
||||
/* now negotiate the allocation */
|
||||
res = do_allocation (pay, caps);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
res = gst_pad_event_default (pad, parent, event);
|
||||
break;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
static void
|
||||
client_buffer_sent (GstPinosPay *pay, GstBuffer *buffer,
|
||||
GObject *obj)
|
||||
{
|
||||
GArray *fdids;
|
||||
GHashTable *hash;
|
||||
guint i;
|
||||
|
||||
fdids = gst_mini_object_get_qdata (GST_MINI_OBJECT_CAST (buffer), fdids_quark);
|
||||
if (fdids == NULL)
|
||||
return;
|
||||
|
||||
/* we keep a hashtable of fd ids on the sender object (usually GSocket) itself,
|
||||
* when the object is destroyed, we automatically also release the refcounts */
|
||||
hash = g_object_get_qdata (obj, fdids_quark);
|
||||
if (hash == NULL) {
|
||||
hash = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL,
|
||||
(GDestroyNotify) gst_buffer_unref);
|
||||
g_object_set_qdata_full (obj, fdids_quark, hash,
|
||||
(GDestroyNotify) g_hash_table_unref);
|
||||
}
|
||||
|
||||
for (i = 0; i < fdids->len; i++) {
|
||||
gint id = g_array_index (fdids, guint32, i);
|
||||
GST_LOG ("%p: fd index %d, increment refcount of buffer %p", hash, id, buffer);
|
||||
g_hash_table_insert (hash, GINT_TO_POINTER (id), gst_buffer_ref (buffer));
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
client_buffer_received (GstPinosPay *pay, GstBuffer *buffer,
|
||||
GObject *obj)
|
||||
{
|
||||
PinosBuffer pbuf;
|
||||
PinosBufferIter it;
|
||||
GstMapInfo info;
|
||||
GHashTable *hash;
|
||||
|
||||
hash = g_object_get_qdata (obj, fdids_quark);
|
||||
if (hash == NULL)
|
||||
return;
|
||||
|
||||
gst_buffer_map (buffer, &info, GST_MAP_READ);
|
||||
pinos_buffer_init_data (&pbuf, info.data, info.size, NULL);
|
||||
pinos_buffer_iter_init (&it, &pbuf);
|
||||
while (pinos_buffer_iter_next (&it)) {
|
||||
switch (pinos_buffer_iter_get_type (&it)) {
|
||||
case PINOS_PACKET_TYPE_RELEASE_FD_PAYLOAD:
|
||||
{
|
||||
PinosPacketReleaseFDPayload p;
|
||||
gint id;
|
||||
|
||||
if (!pinos_buffer_iter_parse_release_fd_payload (&it, &p))
|
||||
continue;
|
||||
|
||||
id = p.id;
|
||||
|
||||
GST_LOG ("%p: fd index %d is released", hash, id);
|
||||
g_assert (g_hash_table_remove (hash, GINT_TO_POINTER (id)));
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
gst_buffer_unmap (buffer, &info);
|
||||
pinos_buffer_clear (&pbuf);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gst_pinos_pay_src_event (GstPad * pad, GstObject * parent, GstEvent * event)
|
||||
{
|
||||
GstPinosPay *pay = GST_PINOS_PAY (parent);
|
||||
gboolean res = FALSE;
|
||||
|
||||
switch (GST_EVENT_TYPE (event)) {
|
||||
case GST_EVENT_CUSTOM_UPSTREAM:
|
||||
{
|
||||
if (gst_event_has_name (event, "GstNetworkMessageDispatched")) {
|
||||
const GstStructure *str = gst_event_get_structure (event);
|
||||
GstBuffer *buf;
|
||||
GObject *obj;
|
||||
|
||||
gst_structure_get (str, "object", G_TYPE_OBJECT, &obj,
|
||||
"buffer", GST_TYPE_BUFFER, &buf, NULL);
|
||||
|
||||
client_buffer_sent (pay, buf, obj);
|
||||
gst_buffer_unref (buf);
|
||||
g_object_unref (obj);
|
||||
}
|
||||
else if (gst_event_has_name (event, "GstNetworkMessage")) {
|
||||
const GstStructure *str = gst_event_get_structure (event);
|
||||
GstBuffer *buf;
|
||||
GObject *obj;
|
||||
|
||||
gst_structure_get (str, "object", G_TYPE_OBJECT, &obj,
|
||||
"buffer", GST_TYPE_BUFFER, &buf, NULL);
|
||||
|
||||
client_buffer_received (pay, buf, obj);
|
||||
gst_buffer_unref (buf);
|
||||
g_object_unref (obj);
|
||||
}
|
||||
gst_event_unref (event);
|
||||
res = TRUE;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
res = gst_pad_event_default (pad, parent, event);
|
||||
break;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
static GstMemory *
|
||||
gst_pinos_pay_get_fd_memory (GstPinosPay * tmpfilepay, GstBuffer * buffer, gboolean *tmpfile)
|
||||
{
|
||||
GstMemory *mem = NULL;
|
||||
|
||||
if (gst_buffer_n_memory (buffer) == 1
|
||||
&& gst_is_fd_memory (gst_buffer_peek_memory (buffer, 0))) {
|
||||
mem = gst_buffer_get_memory (buffer, 0);
|
||||
*tmpfile = gst_is_tmpfile_memory (mem);
|
||||
} else {
|
||||
GstMapInfo info;
|
||||
GstAllocationParams params = {0, 0, 0, 0, { NULL, }};
|
||||
gsize size = gst_buffer_get_size (buffer);
|
||||
GST_INFO_OBJECT (tmpfilepay, "Buffer cannot be payloaded without copying");
|
||||
mem = gst_allocator_alloc (tmpfilepay->allocator, size, ¶ms);
|
||||
if (!gst_memory_map (mem, &info, GST_MAP_WRITE))
|
||||
return NULL;
|
||||
gst_buffer_extract (buffer, 0, info.data, size);
|
||||
gst_memory_unmap (mem, &info);
|
||||
*tmpfile = TRUE;
|
||||
}
|
||||
return mem;
|
||||
}
|
||||
|
||||
static void
|
||||
release_fds (GstPinosPay *pay, GstBuffer *buffer)
|
||||
{
|
||||
GArray *fdids;
|
||||
guint i;
|
||||
PinosBufferBuilder b;
|
||||
PinosPacketReleaseFDPayload r;
|
||||
PinosBuffer pbuf;
|
||||
gsize size;
|
||||
gpointer data;
|
||||
GstBuffer *outbuf;
|
||||
GstEvent *ev;
|
||||
|
||||
fdids = gst_mini_object_steal_qdata (GST_MINI_OBJECT_CAST (buffer),
|
||||
fdids_quark);
|
||||
if (fdids == NULL)
|
||||
return;
|
||||
|
||||
pinos_buffer_builder_init (&b);
|
||||
|
||||
for (i = 0; i < fdids->len; i++) {
|
||||
r.id = g_array_index (fdids, guint32, i);
|
||||
GST_LOG ("release fd %d", r.id);
|
||||
pinos_buffer_builder_add_release_fd_payload (&b, &r);
|
||||
}
|
||||
pinos_buffer_builder_end (&b, &pbuf);
|
||||
g_array_unref (fdids);
|
||||
|
||||
data = pinos_buffer_steal (&pbuf, &size, NULL);
|
||||
|
||||
outbuf = gst_buffer_new_wrapped (data, size);
|
||||
ev = gst_event_new_custom (GST_EVENT_CUSTOM_UPSTREAM,
|
||||
gst_structure_new ("GstNetworkMessage",
|
||||
"object", G_TYPE_OBJECT, pay,
|
||||
"buffer", GST_TYPE_BUFFER, outbuf, NULL));
|
||||
gst_buffer_unref (outbuf);
|
||||
|
||||
gst_pad_push_event (pay->sinkpad, ev);
|
||||
}
|
||||
|
||||
static GstFlowReturn
|
||||
gst_pinos_pay_chain_pinos (GstPinosPay *pay, GstBuffer * buffer)
|
||||
{
|
||||
GstMapInfo info;
|
||||
PinosBuffer pbuf;
|
||||
PinosBufferIter it;
|
||||
GArray *fdids;
|
||||
|
||||
fdids = g_array_new (FALSE, FALSE, sizeof (guint32));
|
||||
|
||||
gst_buffer_map (buffer, &info, GST_MAP_READ);
|
||||
pinos_buffer_init_data (&pbuf, info.data, info.size, NULL);
|
||||
pinos_buffer_iter_init (&it, &pbuf);
|
||||
while (pinos_buffer_iter_next (&it)) {
|
||||
switch (pinos_buffer_iter_get_type (&it)) {
|
||||
case PINOS_PACKET_TYPE_FD_PAYLOAD:
|
||||
{
|
||||
PinosPacketFDPayload p;
|
||||
|
||||
if (!pinos_buffer_iter_parse_fd_payload (&it, &p))
|
||||
continue;
|
||||
|
||||
GST_LOG ("track fd index %d", p.id);
|
||||
g_array_append_val (fdids, p.id);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
gst_buffer_unmap (buffer, &info);
|
||||
pinos_buffer_clear (&pbuf);
|
||||
|
||||
gst_mini_object_set_qdata (GST_MINI_OBJECT_CAST (buffer),
|
||||
fdids_quark, fdids, NULL);
|
||||
gst_mini_object_weak_ref (GST_MINI_OBJECT_CAST (buffer),
|
||||
(GstMiniObjectNotify) release_fds, pay);
|
||||
|
||||
return gst_pad_push (pay->srcpad, buffer);
|
||||
}
|
||||
|
||||
static GstFlowReturn
|
||||
gst_pinos_pay_chain_other (GstPinosPay *pay, GstBuffer * buffer)
|
||||
{
|
||||
GstMemory *fdmem = NULL;
|
||||
GError *err = NULL;
|
||||
GstBuffer *outbuf;
|
||||
PinosBuffer pbuf;
|
||||
PinosBufferBuilder builder;
|
||||
PinosPacketHeader hdr;
|
||||
PinosPacketFDPayload p;
|
||||
gsize size;
|
||||
gpointer data;
|
||||
GSocketControlMessage *msg;
|
||||
gboolean tmpfile = TRUE;
|
||||
|
||||
hdr.flags = 0;
|
||||
hdr.seq = GST_BUFFER_OFFSET (buffer);
|
||||
hdr.pts = GST_BUFFER_PTS (buffer) + GST_ELEMENT_CAST (pay)->base_time;
|
||||
hdr.dts_offset = 0;
|
||||
|
||||
pinos_buffer_builder_init (&builder);
|
||||
pinos_buffer_builder_add_header (&builder, &hdr);
|
||||
|
||||
fdmem = gst_pinos_pay_get_fd_memory (pay, buffer, &tmpfile);
|
||||
p.fd_index = pinos_buffer_builder_add_fd (&builder, gst_fd_memory_get_fd (fdmem), &err);
|
||||
if (p.fd_index == -1)
|
||||
goto add_fd_failed;
|
||||
p.id = pay->id_counter++;
|
||||
p.offset = fdmem->offset;
|
||||
p.size = fdmem->size;
|
||||
pinos_buffer_builder_add_fd_payload (&builder, &p);
|
||||
|
||||
pinos_buffer_builder_end (&builder, &pbuf);
|
||||
gst_memory_unref(fdmem);
|
||||
fdmem = NULL;
|
||||
|
||||
data = pinos_buffer_steal (&pbuf, &size, &msg);
|
||||
|
||||
outbuf = gst_buffer_new_wrapped (data, size);
|
||||
GST_BUFFER_PTS (outbuf) = GST_BUFFER_PTS (buffer);
|
||||
GST_BUFFER_DTS (outbuf) = GST_BUFFER_DTS (buffer);
|
||||
GST_BUFFER_DURATION (outbuf) = GST_BUFFER_DURATION (buffer);
|
||||
GST_BUFFER_OFFSET (outbuf) = GST_BUFFER_OFFSET (buffer);
|
||||
GST_BUFFER_OFFSET_END (outbuf) = GST_BUFFER_OFFSET_END (buffer);
|
||||
|
||||
if (!tmpfile) {
|
||||
GArray *fdids;
|
||||
/* we are using the original buffer fd in the control message, we need
|
||||
* to make sure it is not reused before everyone is finished with it.
|
||||
* We tag the output buffer with the array of fds in it and the original
|
||||
* buffer (to keep it alive). All clients that receive the fd will
|
||||
* increment outbuf refcount, all clients that do release-fd on the fd
|
||||
* will decrease the refcount again. */
|
||||
fdids = g_array_new (FALSE, FALSE, sizeof (guint32));
|
||||
g_array_append_val (fdids, p.id);
|
||||
gst_mini_object_set_qdata (GST_MINI_OBJECT_CAST (outbuf),
|
||||
fdids_quark, fdids, (GDestroyNotify) g_array_unref);
|
||||
gst_mini_object_set_qdata (GST_MINI_OBJECT_CAST (outbuf),
|
||||
orig_buffer_quark, buffer, (GDestroyNotify) gst_buffer_unref);
|
||||
} else {
|
||||
gst_buffer_unref (buffer);
|
||||
}
|
||||
|
||||
gst_buffer_add_net_control_message_meta (outbuf, msg);
|
||||
g_object_unref (msg);
|
||||
|
||||
return gst_pad_push (pay->srcpad, outbuf);
|
||||
|
||||
/* ERRORS */
|
||||
add_fd_failed:
|
||||
{
|
||||
GST_WARNING_OBJECT (pay, "Adding fd failed: %s", err->message);
|
||||
gst_memory_unref(fdmem);
|
||||
g_clear_error (&err);
|
||||
|
||||
return GST_FLOW_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
static GstFlowReturn
|
||||
gst_pinos_pay_chain (GstPad * pad, GstObject * parent, GstBuffer * buffer)
|
||||
{
|
||||
GstPinosPay *pay = GST_PINOS_PAY (parent);
|
||||
|
||||
if (pay->pinos_input)
|
||||
return gst_pinos_pay_chain_pinos (pay, buffer);
|
||||
else
|
||||
return gst_pinos_pay_chain_other (pay, buffer);
|
||||
}
|
||||
static void
|
||||
gst_pinos_pay_finalize (GObject * object)
|
||||
{
|
||||
GstPinosPay *pay = GST_PINOS_PAY (object);
|
||||
|
||||
GST_DEBUG_OBJECT (pay, "finalize");
|
||||
|
||||
g_object_unref (pay->allocator);
|
||||
|
||||
G_OBJECT_CLASS (gst_pinos_pay_parent_class)->finalize (object);
|
||||
}
|
||||
|
||||
static void
|
||||
gst_pinos_pay_init (GstPinosPay * pay)
|
||||
{
|
||||
pay->srcpad = gst_pad_new_from_static_template (&gst_pinos_pay_src_template, "src");
|
||||
gst_pad_set_event_function (pay->srcpad, gst_pinos_pay_src_event);
|
||||
gst_element_add_pad (GST_ELEMENT (pay), pay->srcpad);
|
||||
|
||||
pay->sinkpad = gst_pad_new_from_static_template (&gst_pinos_pay_sink_template, "sink");
|
||||
gst_pad_set_chain_function (pay->sinkpad, gst_pinos_pay_chain);
|
||||
gst_pad_set_event_function (pay->sinkpad, gst_pinos_pay_sink_event);
|
||||
gst_pad_set_query_function (pay->sinkpad, gst_pinos_pay_query);
|
||||
gst_element_add_pad (GST_ELEMENT (pay), pay->sinkpad);
|
||||
|
||||
pay->allocator = gst_tmpfile_allocator_new ();
|
||||
}
|
||||
|
||||
static void
|
||||
gst_pinos_pay_class_init (GstPinosPayClass * klass)
|
||||
{
|
||||
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
|
||||
GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
|
||||
|
||||
gst_element_class_add_pad_template (element_class,
|
||||
gst_static_pad_template_get (&gst_pinos_pay_src_template));
|
||||
gst_element_class_add_pad_template (element_class,
|
||||
gst_static_pad_template_get (&gst_pinos_pay_sink_template));
|
||||
|
||||
gst_element_class_set_static_metadata (element_class,
|
||||
"Pinos Payloader", "Generic",
|
||||
"Pinos Payloader for zero-copy IPC with Pinos",
|
||||
"Wim Taymans <wim.taymans@gmail.com>");
|
||||
|
||||
gobject_class->finalize = gst_pinos_pay_finalize;
|
||||
|
||||
fdids_quark = g_quark_from_static_string ("GstPinosPayFDIds");
|
||||
orig_buffer_quark = g_quark_from_static_string ("GstPinosPayOrigBuffer");
|
||||
}
|
||||
|
|
@ -1,58 +0,0 @@
|
|||
/* GStreamer
|
||||
* Copyright (C) 2014 William Manley <will@williammanley.net>
|
||||
* (C) 2015 Wim Taymans <wim.taymans@gmail.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#ifndef _GST_PINOS_PAY_H_
|
||||
#define _GST_PINOS_PAY_H_
|
||||
|
||||
#include <gst/gst.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define GST_TYPE_PINOS_PAY (gst_pinos_pay_get_type())
|
||||
#define GST_PINOS_PAY(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_PINOS_PAY,GstPinosPay))
|
||||
#define GST_PINOS_PAY_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_PINOS_PAY,GstPinosPayClass))
|
||||
#define GST_IS_PINOS_PAY(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_PINOS_PAY))
|
||||
#define GST_IS_PINOS_PAY_CLASS(obj) (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_PINOS_PAY))
|
||||
|
||||
typedef struct _GstPinosPay GstPinosPay;
|
||||
typedef struct _GstPinosPayClass GstPinosPayClass;
|
||||
|
||||
struct _GstPinosPay
|
||||
{
|
||||
GstElement parent;
|
||||
|
||||
gboolean pinos_input;
|
||||
GstPad *srcpad, *sinkpad;
|
||||
|
||||
GstAllocator *allocator;
|
||||
|
||||
guint32 id_counter;
|
||||
};
|
||||
|
||||
struct _GstPinosPayClass
|
||||
{
|
||||
GstElementClass parent_class;
|
||||
};
|
||||
|
||||
GType gst_pinos_pay_get_type (void);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* _GST_PINOS_PAY_H_ */
|
||||
|
|
@ -1,689 +0,0 @@
|
|||
/* GStreamer
|
||||
* Copyright (C) <2015> Wim Taymans <wim.taymans@gmail.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
/**
|
||||
* SECTION:element-pinossink
|
||||
*
|
||||
* <refsect2>
|
||||
* <title>Example launch line</title>
|
||||
* |[
|
||||
* gst-launch -v videotestsrc ! pinossink
|
||||
* ]| Sends a test video source to pinos
|
||||
* </refsect2>
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
#include "gstpinossink.h"
|
||||
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/socket.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <gio/gunixfdmessage.h>
|
||||
#include <gst/allocators/gstfdmemory.h>
|
||||
|
||||
#include "gsttmpfileallocator.h"
|
||||
|
||||
|
||||
GST_DEBUG_CATEGORY_STATIC (pinos_sink_debug);
|
||||
#define GST_CAT_DEFAULT pinos_sink_debug
|
||||
|
||||
enum
|
||||
{
|
||||
PROP_0,
|
||||
PROP_CLIENT_NAME,
|
||||
PROP_STREAM_PROPERTIES
|
||||
};
|
||||
|
||||
|
||||
#define PINOSS_VIDEO_CAPS GST_VIDEO_CAPS_MAKE (GST_VIDEO_FORMATS_ALL)
|
||||
|
||||
static GstStaticPadTemplate gst_pinos_sink_template =
|
||||
GST_STATIC_PAD_TEMPLATE ("sink",
|
||||
GST_PAD_SINK,
|
||||
GST_PAD_ALWAYS,
|
||||
GST_STATIC_CAPS_ANY
|
||||
);
|
||||
|
||||
#define gst_pinos_sink_parent_class parent_class
|
||||
G_DEFINE_TYPE (GstPinosSink, gst_pinos_sink, GST_TYPE_BASE_SINK);
|
||||
|
||||
static void gst_pinos_sink_set_property (GObject * object, guint prop_id,
|
||||
const GValue * value, GParamSpec * pspec);
|
||||
static void gst_pinos_sink_get_property (GObject * object, guint prop_id,
|
||||
GValue * value, GParamSpec * pspec);
|
||||
|
||||
static GstStateChangeReturn
|
||||
gst_pinos_sink_change_state (GstElement * element, GstStateChange transition);
|
||||
|
||||
static GstCaps *gst_pinos_sink_getcaps (GstBaseSink * bsink, GstCaps * filter);
|
||||
static gboolean gst_pinos_sink_setcaps (GstBaseSink * bsink, GstCaps * caps);
|
||||
static GstCaps *gst_pinos_sink_sink_fixate (GstBaseSink * bsink,
|
||||
GstCaps * caps);
|
||||
|
||||
static GstFlowReturn gst_pinos_sink_render (GstBaseSink * psink,
|
||||
GstBuffer * buffer);
|
||||
static gboolean gst_pinos_sink_start (GstBaseSink * basesink);
|
||||
static gboolean gst_pinos_sink_stop (GstBaseSink * basesink);
|
||||
|
||||
static void
|
||||
gst_pinos_sink_finalize (GObject * object)
|
||||
{
|
||||
GstPinosSink *pinossink = GST_PINOS_SINK (object);
|
||||
|
||||
if (pinossink->properties)
|
||||
gst_structure_free (pinossink->properties);
|
||||
g_hash_table_unref (pinossink->fdids);
|
||||
g_object_unref (pinossink->allocator);
|
||||
g_free (pinossink->client_name);
|
||||
|
||||
G_OBJECT_CLASS (parent_class)->finalize (object);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gst_pinos_sink_propose_allocation (GstBaseSink * bsink, GstQuery * query)
|
||||
{
|
||||
GstPinosSink *pinossink = GST_PINOS_SINK (bsink);
|
||||
|
||||
gst_query_add_allocation_param (query, pinossink->allocator, NULL);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
gst_pinos_sink_class_init (GstPinosSinkClass * klass)
|
||||
{
|
||||
GObjectClass *gobject_class;
|
||||
GstElementClass *gstelement_class;
|
||||
GstBaseSinkClass *gstbasesink_class;
|
||||
|
||||
gobject_class = (GObjectClass *) klass;
|
||||
gstelement_class = (GstElementClass *) klass;
|
||||
gstbasesink_class = (GstBaseSinkClass *) klass;
|
||||
|
||||
gobject_class->finalize = gst_pinos_sink_finalize;
|
||||
gobject_class->set_property = gst_pinos_sink_set_property;
|
||||
gobject_class->get_property = gst_pinos_sink_get_property;
|
||||
|
||||
g_object_class_install_property (gobject_class,
|
||||
PROP_CLIENT_NAME,
|
||||
g_param_spec_string ("client-name",
|
||||
"Client Name",
|
||||
"The client name to use (NULL = default)",
|
||||
NULL,
|
||||
G_PARAM_READWRITE |
|
||||
G_PARAM_STATIC_STRINGS));
|
||||
|
||||
g_object_class_install_property (gobject_class,
|
||||
PROP_STREAM_PROPERTIES,
|
||||
g_param_spec_boxed ("stream-properties",
|
||||
"stream properties",
|
||||
"list of pinos stream properties",
|
||||
GST_TYPE_STRUCTURE,
|
||||
G_PARAM_READWRITE |
|
||||
G_PARAM_STATIC_STRINGS));
|
||||
|
||||
gstelement_class->change_state = gst_pinos_sink_change_state;
|
||||
|
||||
gst_element_class_set_static_metadata (gstelement_class,
|
||||
"Pinos sink", "Sink/Video",
|
||||
"Send video to pinos", "Wim Taymans <wim.taymans@gmail.com>");
|
||||
|
||||
gst_element_class_add_pad_template (gstelement_class,
|
||||
gst_static_pad_template_get (&gst_pinos_sink_template));
|
||||
|
||||
gstbasesink_class->get_caps = gst_pinos_sink_getcaps;
|
||||
gstbasesink_class->set_caps = gst_pinos_sink_setcaps;
|
||||
gstbasesink_class->fixate = gst_pinos_sink_sink_fixate;
|
||||
gstbasesink_class->propose_allocation = gst_pinos_sink_propose_allocation;
|
||||
gstbasesink_class->start = gst_pinos_sink_start;
|
||||
gstbasesink_class->stop = gst_pinos_sink_stop;
|
||||
gstbasesink_class->render = gst_pinos_sink_render;
|
||||
|
||||
GST_DEBUG_CATEGORY_INIT (pinos_sink_debug, "pinossink", 0,
|
||||
"Pinos Sink");
|
||||
}
|
||||
|
||||
static void
|
||||
gst_pinos_sink_init (GstPinosSink * sink)
|
||||
{
|
||||
sink->allocator = gst_tmpfile_allocator_new ();
|
||||
sink->client_name = pinos_client_name();
|
||||
|
||||
sink->fdids = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL,
|
||||
(GDestroyNotify) gst_buffer_unref);
|
||||
}
|
||||
|
||||
static GstCaps *
|
||||
gst_pinos_sink_sink_fixate (GstBaseSink * bsink, GstCaps * caps)
|
||||
{
|
||||
GstStructure *structure;
|
||||
|
||||
caps = gst_caps_make_writable (caps);
|
||||
|
||||
structure = gst_caps_get_structure (caps, 0);
|
||||
|
||||
if (gst_structure_has_name (structure, "video/x-raw")) {
|
||||
gst_structure_fixate_field_nearest_int (structure, "width", 320);
|
||||
gst_structure_fixate_field_nearest_int (structure, "height", 240);
|
||||
gst_structure_fixate_field_nearest_fraction (structure, "framerate", 30, 1);
|
||||
|
||||
if (gst_structure_has_field (structure, "pixel-aspect-ratio"))
|
||||
gst_structure_fixate_field_nearest_fraction (structure,
|
||||
"pixel-aspect-ratio", 1, 1);
|
||||
else
|
||||
gst_structure_set (structure, "pixel-aspect-ratio", GST_TYPE_FRACTION, 1, 1,
|
||||
NULL);
|
||||
|
||||
if (gst_structure_has_field (structure, "colorimetry"))
|
||||
gst_structure_fixate_field_string (structure, "colorimetry", "bt601");
|
||||
if (gst_structure_has_field (structure, "chroma-site"))
|
||||
gst_structure_fixate_field_string (structure, "chroma-site", "mpeg2");
|
||||
|
||||
if (gst_structure_has_field (structure, "interlace-mode"))
|
||||
gst_structure_fixate_field_string (structure, "interlace-mode",
|
||||
"progressive");
|
||||
else
|
||||
gst_structure_set (structure, "interlace-mode", G_TYPE_STRING,
|
||||
"progressive", NULL);
|
||||
} else if (gst_structure_has_name (structure, "audio/x-raw")) {
|
||||
gst_structure_fixate_field_string (structure, "format", "S16LE");
|
||||
gst_structure_fixate_field_nearest_int (structure, "channels", 2);
|
||||
gst_structure_fixate_field_nearest_int (structure, "rate", 44100);
|
||||
}
|
||||
|
||||
caps = GST_BASE_SINK_CLASS (parent_class)->fixate (bsink, caps);
|
||||
|
||||
return caps;
|
||||
}
|
||||
|
||||
static void
|
||||
gst_pinos_sink_set_property (GObject * object, guint prop_id,
|
||||
const GValue * value, GParamSpec * pspec)
|
||||
{
|
||||
GstPinosSink *pinossink = GST_PINOS_SINK (object);
|
||||
|
||||
switch (prop_id) {
|
||||
case PROP_CLIENT_NAME:
|
||||
g_free (pinossink->client_name);
|
||||
pinossink->client_name = g_value_dup_string (value);
|
||||
break;
|
||||
|
||||
case PROP_STREAM_PROPERTIES:
|
||||
if (pinossink->properties)
|
||||
gst_structure_free (pinossink->properties);
|
||||
pinossink->properties =
|
||||
gst_structure_copy (gst_value_get_structure (value));
|
||||
break;
|
||||
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gst_pinos_sink_get_property (GObject * object, guint prop_id,
|
||||
GValue * value, GParamSpec * pspec)
|
||||
{
|
||||
GstPinosSink *pinossink = GST_PINOS_SINK (object);
|
||||
|
||||
switch (prop_id) {
|
||||
case PROP_CLIENT_NAME:
|
||||
g_value_set_string (value, pinossink->client_name);
|
||||
break;
|
||||
|
||||
case PROP_STREAM_PROPERTIES:
|
||||
gst_value_set_structure (value, pinossink->properties);
|
||||
break;
|
||||
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
on_new_buffer (GObject *gobject,
|
||||
gpointer user_data)
|
||||
{
|
||||
GstPinosSink *pinossink = user_data;
|
||||
PinosBuffer *pbuf;
|
||||
PinosBufferIter it;
|
||||
|
||||
GST_LOG_OBJECT (pinossink, "got new buffer");
|
||||
if (!pinos_stream_peek_buffer (pinossink->stream, &pbuf)) {
|
||||
g_warning ("failed to capture buffer");
|
||||
return;
|
||||
}
|
||||
|
||||
pinos_buffer_iter_init (&it, pbuf);
|
||||
while (pinos_buffer_iter_next (&it)) {
|
||||
switch (pinos_buffer_iter_get_type (&it)) {
|
||||
case PINOS_PACKET_TYPE_RELEASE_FD_PAYLOAD:
|
||||
{
|
||||
PinosPacketReleaseFDPayload p;
|
||||
|
||||
if (!pinos_buffer_iter_parse_release_fd_payload (&it, &p))
|
||||
continue;
|
||||
|
||||
GST_LOG ("fd index %d is released", p.id);
|
||||
g_hash_table_remove (pinossink->fdids, GINT_TO_POINTER (p.id));
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
on_stream_notify (GObject *gobject,
|
||||
GParamSpec *pspec,
|
||||
gpointer user_data)
|
||||
{
|
||||
PinosStreamState state;
|
||||
PinosStream *stream = PINOS_STREAM (gobject);
|
||||
GstPinosSink *pinossink = user_data;
|
||||
|
||||
state = pinos_stream_get_state (stream);
|
||||
GST_DEBUG ("got stream state %d", state);
|
||||
|
||||
switch (state) {
|
||||
case PINOS_STREAM_STATE_UNCONNECTED:
|
||||
case PINOS_STREAM_STATE_CONNECTING:
|
||||
case PINOS_STREAM_STATE_STARTING:
|
||||
case PINOS_STREAM_STATE_STREAMING:
|
||||
case PINOS_STREAM_STATE_READY:
|
||||
break;
|
||||
case PINOS_STREAM_STATE_ERROR:
|
||||
GST_ELEMENT_ERROR (pinossink, RESOURCE, FAILED,
|
||||
("stream error: %s",
|
||||
pinos_stream_get_error (stream)->message), (NULL));
|
||||
break;
|
||||
}
|
||||
pinos_main_loop_signal (pinossink->loop, FALSE);
|
||||
}
|
||||
|
||||
static GstCaps *
|
||||
gst_pinos_sink_getcaps (GstBaseSink * bsink, GstCaps * filter)
|
||||
{
|
||||
return GST_BASE_SINK_CLASS (parent_class)->get_caps (bsink, filter);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
copy_properties (GQuark field_id,
|
||||
const GValue *value,
|
||||
gpointer user_data)
|
||||
{
|
||||
PinosProperties *properties = user_data;
|
||||
|
||||
if (G_VALUE_HOLDS_STRING (value))
|
||||
pinos_properties_set (properties,
|
||||
g_quark_to_string (field_id),
|
||||
g_value_get_string (value));
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gst_pinos_sink_setcaps (GstBaseSink * bsink, GstCaps * caps)
|
||||
{
|
||||
GstPinosSink *pinossink;
|
||||
gchar *str;
|
||||
GBytes *format;
|
||||
PinosProperties *props;
|
||||
|
||||
pinossink = GST_PINOS_SINK (bsink);
|
||||
|
||||
str = gst_caps_to_string (caps);
|
||||
format = g_bytes_new_take (str, strlen (str) + 1);
|
||||
|
||||
if (pinossink->properties) {
|
||||
props = pinos_properties_new (NULL, NULL);
|
||||
gst_structure_foreach (pinossink->properties, copy_properties, props);
|
||||
} else {
|
||||
props = NULL;
|
||||
}
|
||||
|
||||
pinos_main_loop_lock (pinossink->loop);
|
||||
pinossink->stream = pinos_stream_new (pinossink->ctx, pinossink->client_name, props);
|
||||
g_signal_connect (pinossink->stream, "notify::state", (GCallback) on_stream_notify, pinossink);
|
||||
g_signal_connect (pinossink->stream, "new-buffer", (GCallback) on_new_buffer, pinossink);
|
||||
|
||||
pinos_stream_connect_provide (pinossink->stream, 0, g_bytes_ref (format));
|
||||
|
||||
while (TRUE) {
|
||||
PinosStreamState state = pinos_stream_get_state (pinossink->stream);
|
||||
|
||||
if (state == PINOS_STREAM_STATE_READY)
|
||||
break;
|
||||
|
||||
if (state == PINOS_STREAM_STATE_ERROR)
|
||||
goto connect_error;
|
||||
|
||||
pinos_main_loop_wait (pinossink->loop);
|
||||
}
|
||||
|
||||
pinos_stream_start (pinossink->stream, format, PINOS_STREAM_MODE_BUFFER);
|
||||
|
||||
while (TRUE) {
|
||||
PinosStreamState state = pinos_stream_get_state (pinossink->stream);
|
||||
|
||||
if (state == PINOS_STREAM_STATE_STREAMING)
|
||||
break;
|
||||
|
||||
if (state == PINOS_STREAM_STATE_ERROR)
|
||||
goto connect_error;
|
||||
|
||||
pinos_main_loop_wait (pinossink->loop);
|
||||
}
|
||||
pinos_main_loop_unlock (pinossink->loop);
|
||||
|
||||
pinossink->negotiated = TRUE;
|
||||
|
||||
return TRUE;
|
||||
|
||||
connect_error:
|
||||
{
|
||||
pinos_main_loop_unlock (pinossink->loop);
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
static GstFlowReturn
|
||||
gst_pinos_sink_render (GstBaseSink * bsink, GstBuffer * buffer)
|
||||
{
|
||||
GstPinosSink *pinossink;
|
||||
PinosBuffer pbuf;
|
||||
PinosBufferBuilder builder;
|
||||
GstMemory *mem = NULL;
|
||||
GstClockTime pts, dts, base;
|
||||
PinosPacketHeader hdr;
|
||||
PinosPacketFDPayload p;
|
||||
gsize size;
|
||||
GError *err = NULL;
|
||||
gboolean tmpfile, res;
|
||||
|
||||
pinossink = GST_PINOS_SINK (bsink);
|
||||
|
||||
if (!pinossink->negotiated)
|
||||
goto not_negotiated;
|
||||
|
||||
base = GST_ELEMENT_CAST (bsink)->base_time;
|
||||
|
||||
pts = GST_BUFFER_PTS (buffer);
|
||||
dts = GST_BUFFER_DTS (buffer);
|
||||
if (!GST_CLOCK_TIME_IS_VALID (pts))
|
||||
pts = dts;
|
||||
else if (!GST_CLOCK_TIME_IS_VALID (dts))
|
||||
dts = pts;
|
||||
|
||||
hdr.flags = 0;
|
||||
hdr.seq = GST_BUFFER_OFFSET (buffer);
|
||||
hdr.pts = GST_CLOCK_TIME_IS_VALID (pts) ? pts + base : base;
|
||||
hdr.dts_offset = GST_CLOCK_TIME_IS_VALID (dts) && GST_CLOCK_TIME_IS_VALID (pts) ? pts - dts : 0;
|
||||
|
||||
size = gst_buffer_get_size (buffer);
|
||||
|
||||
if (gst_buffer_n_memory (buffer) == 1
|
||||
&& gst_is_fd_memory (gst_buffer_peek_memory (buffer, 0))) {
|
||||
mem = gst_buffer_get_memory (buffer, 0);
|
||||
tmpfile = gst_is_tmpfile_memory (mem);
|
||||
} else {
|
||||
GstMapInfo minfo;
|
||||
GstAllocationParams params = {0, 0, 0, 0, { NULL, }};
|
||||
|
||||
GST_INFO_OBJECT (bsink, "Buffer cannot be payloaded without copying");
|
||||
|
||||
mem = gst_allocator_alloc (pinossink->allocator, size, ¶ms);
|
||||
if (!gst_memory_map (mem, &minfo, GST_MAP_WRITE))
|
||||
goto map_error;
|
||||
gst_buffer_extract (buffer, 0, minfo.data, size);
|
||||
gst_memory_unmap (mem, &minfo);
|
||||
tmpfile = TRUE;
|
||||
}
|
||||
|
||||
pinos_buffer_builder_init (&builder);
|
||||
pinos_buffer_builder_add_header (&builder, &hdr);
|
||||
|
||||
p.fd_index = pinos_buffer_builder_add_fd (&builder, gst_fd_memory_get_fd (mem), &err);
|
||||
if (p.fd_index == -1)
|
||||
goto add_fd_failed;
|
||||
p.id = pinossink->id_counter++;
|
||||
p.offset = 0;
|
||||
p.size = size;
|
||||
pinos_buffer_builder_add_fd_payload (&builder, &p);
|
||||
pinos_buffer_builder_end (&builder, &pbuf);
|
||||
|
||||
gst_memory_unref (mem);
|
||||
|
||||
pinos_main_loop_lock (pinossink->loop);
|
||||
if (pinos_stream_get_state (pinossink->stream) != PINOS_STREAM_STATE_STREAMING)
|
||||
goto streaming_error;
|
||||
res = pinos_stream_send_buffer (pinossink->stream, &pbuf);
|
||||
pinos_buffer_clear (&pbuf);
|
||||
pinos_main_loop_unlock (pinossink->loop);
|
||||
|
||||
if (res && !tmpfile) {
|
||||
/* keep the buffer around until we get the release fd message */
|
||||
g_hash_table_insert (pinossink->fdids, GINT_TO_POINTER (p.id), gst_buffer_ref (buffer));
|
||||
}
|
||||
return GST_FLOW_OK;
|
||||
|
||||
not_negotiated:
|
||||
{
|
||||
return GST_FLOW_NOT_NEGOTIATED;
|
||||
}
|
||||
map_error:
|
||||
{
|
||||
GST_ELEMENT_ERROR (pinossink, RESOURCE, FAILED,
|
||||
("failed to map buffer"), (NULL));
|
||||
return GST_FLOW_ERROR;
|
||||
}
|
||||
add_fd_failed:
|
||||
{
|
||||
GST_ELEMENT_ERROR (pinossink, RESOURCE, FAILED,
|
||||
("failed to add fd: %s", err->message), (NULL));
|
||||
pinos_buffer_builder_clear (&builder);
|
||||
return GST_FLOW_ERROR;
|
||||
}
|
||||
streaming_error:
|
||||
{
|
||||
pinos_main_loop_unlock (pinossink->loop);
|
||||
return GST_FLOW_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gst_pinos_sink_start (GstBaseSink * basesink)
|
||||
{
|
||||
GstPinosSink *sink = GST_PINOS_SINK (basesink);
|
||||
|
||||
sink->negotiated = FALSE;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gst_pinos_sink_stop (GstBaseSink * basesink)
|
||||
{
|
||||
GstPinosSink *sink = GST_PINOS_SINK (basesink);
|
||||
|
||||
sink->negotiated = FALSE;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
on_context_notify (GObject *gobject,
|
||||
GParamSpec *pspec,
|
||||
gpointer user_data)
|
||||
{
|
||||
GstPinosSink *pinossink = user_data;
|
||||
PinosContext *ctx = PINOS_CONTEXT (gobject);
|
||||
PinosContextState state;
|
||||
|
||||
state = pinos_context_get_state (ctx);
|
||||
GST_DEBUG ("got context state %d", state);
|
||||
|
||||
switch (state) {
|
||||
case PINOS_CONTEXT_STATE_UNCONNECTED:
|
||||
case PINOS_CONTEXT_STATE_CONNECTING:
|
||||
case PINOS_CONTEXT_STATE_REGISTERING:
|
||||
case PINOS_CONTEXT_STATE_READY:
|
||||
break;
|
||||
case PINOS_CONTEXT_STATE_ERROR:
|
||||
GST_ELEMENT_ERROR (pinossink, RESOURCE, FAILED,
|
||||
("context error: %s",
|
||||
pinos_context_get_error (pinossink->ctx)->message), (NULL));
|
||||
break;
|
||||
}
|
||||
pinos_main_loop_signal (pinossink->loop, FALSE);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gst_pinos_sink_open (GstPinosSink * pinossink)
|
||||
{
|
||||
GError *error = NULL;
|
||||
|
||||
pinossink->context = g_main_context_new ();
|
||||
GST_DEBUG ("context %p", pinossink->context);
|
||||
|
||||
pinossink->loop = pinos_main_loop_new (pinossink->context, "pinos-sink-loop");
|
||||
if (!pinos_main_loop_start (pinossink->loop, &error))
|
||||
goto mainloop_error;
|
||||
|
||||
pinos_main_loop_lock (pinossink->loop);
|
||||
pinossink->ctx = pinos_context_new (pinossink->context, g_get_application_name (), NULL);
|
||||
g_signal_connect (pinossink->ctx, "notify::state", (GCallback) on_context_notify, pinossink);
|
||||
|
||||
pinos_context_connect(pinossink->ctx, PINOS_CONTEXT_FLAGS_NONE);
|
||||
|
||||
while (TRUE) {
|
||||
PinosContextState state = pinos_context_get_state (pinossink->ctx);
|
||||
|
||||
if (state == PINOS_CONTEXT_STATE_READY)
|
||||
break;
|
||||
|
||||
if (state == PINOS_CONTEXT_STATE_ERROR)
|
||||
goto connect_error;
|
||||
|
||||
pinos_main_loop_wait (pinossink->loop);
|
||||
}
|
||||
pinos_main_loop_unlock (pinossink->loop);
|
||||
|
||||
return TRUE;
|
||||
|
||||
/* ERRORS */
|
||||
mainloop_error:
|
||||
{
|
||||
GST_ELEMENT_ERROR (pinossink, RESOURCE, FAILED,
|
||||
("Failed to start mainloop: %s", error->message), (NULL));
|
||||
return FALSE;
|
||||
}
|
||||
connect_error:
|
||||
{
|
||||
pinos_main_loop_unlock (pinossink->loop);
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gst_pinos_sink_close (GstPinosSink * pinossink)
|
||||
{
|
||||
pinos_main_loop_lock (pinossink->loop);
|
||||
if (pinossink->stream) {
|
||||
pinos_stream_disconnect (pinossink->stream);
|
||||
}
|
||||
if (pinossink->ctx) {
|
||||
pinos_context_disconnect (pinossink->ctx);
|
||||
|
||||
while (TRUE) {
|
||||
PinosContextState state = pinos_context_get_state (pinossink->ctx);
|
||||
|
||||
if (state == PINOS_CONTEXT_STATE_UNCONNECTED)
|
||||
break;
|
||||
|
||||
if (state == PINOS_CONTEXT_STATE_ERROR)
|
||||
break;
|
||||
|
||||
pinos_main_loop_wait (pinossink->loop);
|
||||
}
|
||||
}
|
||||
pinos_main_loop_unlock (pinossink->loop);
|
||||
|
||||
pinos_main_loop_stop (pinossink->loop);
|
||||
g_clear_object (&pinossink->loop);
|
||||
g_clear_object (&pinossink->stream);
|
||||
g_clear_object (&pinossink->ctx);
|
||||
g_main_context_unref (pinossink->context);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static GstStateChangeReturn
|
||||
gst_pinos_sink_change_state (GstElement * element, GstStateChange transition)
|
||||
{
|
||||
GstStateChangeReturn ret;
|
||||
GstPinosSink *this = GST_PINOS_SINK_CAST (element);
|
||||
|
||||
switch (transition) {
|
||||
case GST_STATE_CHANGE_NULL_TO_READY:
|
||||
if (!gst_pinos_sink_open (this))
|
||||
goto open_failed;
|
||||
break;
|
||||
case GST_STATE_CHANGE_READY_TO_PAUSED:
|
||||
break;
|
||||
case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
|
||||
/* uncork and start recording */
|
||||
break;
|
||||
case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
|
||||
/* stop recording ASAP by corking */
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
|
||||
|
||||
switch (transition) {
|
||||
case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
|
||||
break;
|
||||
case GST_STATE_CHANGE_PAUSED_TO_READY:
|
||||
g_hash_table_remove_all (this->fdids);
|
||||
break;
|
||||
case GST_STATE_CHANGE_READY_TO_NULL:
|
||||
g_hash_table_remove_all (this->fdids);
|
||||
gst_pinos_sink_close (this);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return ret;
|
||||
|
||||
/* ERRORS */
|
||||
open_failed:
|
||||
{
|
||||
return GST_STATE_CHANGE_FAILURE;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,79 +0,0 @@
|
|||
/* GStreamer
|
||||
* Copyright (C) <2015> Wim Taymans <wim.taymans@gmail.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#ifndef __GST_PINOS_SINK_H__
|
||||
#define __GST_PINOS_SINK_H__
|
||||
|
||||
#include <gst/gst.h>
|
||||
#include <gst/base/gstbasesink.h>
|
||||
|
||||
#include <client/pinos.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define GST_TYPE_PINOS_SINK \
|
||||
(gst_pinos_sink_get_type())
|
||||
#define GST_PINOS_SINK(obj) \
|
||||
(G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_PINOS_SINK,GstPinosSink))
|
||||
#define GST_PINOS_SINK_CLASS(klass) \
|
||||
(G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_PINOS_SINK,GstPinosSinkClass))
|
||||
#define GST_IS_PINOS_SINK(obj) \
|
||||
(G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_PINOS_SINK))
|
||||
#define GST_IS_PINOS_SINK_CLASS(klass) \
|
||||
(G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_PINOS_SINK))
|
||||
#define GST_PINOS_SINK_CAST(obj) \
|
||||
((GstPinosSink *) (obj))
|
||||
|
||||
typedef struct _GstPinosSink GstPinosSink;
|
||||
typedef struct _GstPinosSinkClass GstPinosSinkClass;
|
||||
|
||||
/**
|
||||
* GstPinosSink:
|
||||
*
|
||||
* Opaque data structure.
|
||||
*/
|
||||
struct _GstPinosSink {
|
||||
GstBaseSink element;
|
||||
|
||||
/*< private >*/
|
||||
gchar *client_name;
|
||||
|
||||
/* video state */
|
||||
gboolean negotiated;
|
||||
|
||||
GMainContext *context;
|
||||
PinosMainLoop *loop;
|
||||
PinosContext *ctx;
|
||||
PinosStream *stream;
|
||||
GstAllocator *allocator;
|
||||
GstStructure *properties;
|
||||
|
||||
guint32 id_counter;
|
||||
GHashTable *fdids;
|
||||
};
|
||||
|
||||
struct _GstPinosSinkClass {
|
||||
GstBaseSinkClass parent_class;
|
||||
};
|
||||
|
||||
GType gst_pinos_sink_get_type (void);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __GST_PINOS_SINK_H__ */
|
||||
|
|
@ -1,933 +0,0 @@
|
|||
/* GStreamer
|
||||
* Copyright (C) <2015> Wim Taymans <wim.taymans@gmail.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
/**
|
||||
* SECTION:element-pinossrc
|
||||
*
|
||||
* <refsect2>
|
||||
* <title>Example launch line</title>
|
||||
* |[
|
||||
* gst-launch -v pinossrc ! videoconvert ! ximagesink
|
||||
* ]| Shows pinos output in an X window.
|
||||
* </refsect2>
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
#include "gstpinossrc.h"
|
||||
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/socket.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <gio/gunixfdmessage.h>
|
||||
#include <gst/net/gstnetclientclock.h>
|
||||
#include <gst/allocators/gstfdmemory.h>
|
||||
|
||||
|
||||
static GQuark fdpayload_data_quark;
|
||||
|
||||
GST_DEBUG_CATEGORY_STATIC (pinos_src_debug);
|
||||
#define GST_CAT_DEFAULT pinos_src_debug
|
||||
|
||||
enum
|
||||
{
|
||||
PROP_0,
|
||||
PROP_PATH,
|
||||
PROP_CLIENT_NAME,
|
||||
PROP_STREAM_PROPERTIES,
|
||||
};
|
||||
|
||||
|
||||
#define PINOSS_VIDEO_CAPS GST_VIDEO_CAPS_MAKE (GST_VIDEO_FORMATS_ALL)
|
||||
|
||||
static GstStaticPadTemplate gst_pinos_src_template =
|
||||
GST_STATIC_PAD_TEMPLATE ("src",
|
||||
GST_PAD_SRC,
|
||||
GST_PAD_ALWAYS,
|
||||
GST_STATIC_CAPS_ANY
|
||||
);
|
||||
|
||||
#define gst_pinos_src_parent_class parent_class
|
||||
G_DEFINE_TYPE (GstPinosSrc, gst_pinos_src, GST_TYPE_PUSH_SRC);
|
||||
|
||||
static GstStateChangeReturn
|
||||
gst_pinos_src_change_state (GstElement * element, GstStateChange transition);
|
||||
|
||||
static gboolean gst_pinos_src_negotiate (GstBaseSrc * basesrc);
|
||||
static GstCaps *gst_pinos_src_getcaps (GstBaseSrc * bsrc, GstCaps * filter);
|
||||
static GstCaps *gst_pinos_src_src_fixate (GstBaseSrc * bsrc,
|
||||
GstCaps * caps);
|
||||
|
||||
static GstFlowReturn gst_pinos_src_create (GstPushSrc * psrc,
|
||||
GstBuffer ** buffer);
|
||||
static gboolean gst_pinos_src_unlock (GstBaseSrc * basesrc);
|
||||
static gboolean gst_pinos_src_unlock_stop (GstBaseSrc * basesrc);
|
||||
static gboolean gst_pinos_src_start (GstBaseSrc * basesrc);
|
||||
static gboolean gst_pinos_src_stop (GstBaseSrc * basesrc);
|
||||
|
||||
static void
|
||||
gst_pinos_src_set_property (GObject * object, guint prop_id,
|
||||
const GValue * value, GParamSpec * pspec)
|
||||
{
|
||||
GstPinosSrc *pinossrc = GST_PINOS_SRC (object);
|
||||
|
||||
switch (prop_id) {
|
||||
case PROP_PATH:
|
||||
g_free (pinossrc->path);
|
||||
pinossrc->path = g_value_dup_string (value);
|
||||
break;
|
||||
|
||||
case PROP_CLIENT_NAME:
|
||||
g_free (pinossrc->client_name);
|
||||
pinossrc->client_name = g_value_dup_string (value);
|
||||
break;
|
||||
|
||||
case PROP_STREAM_PROPERTIES:
|
||||
if (pinossrc->properties)
|
||||
gst_structure_free (pinossrc->properties);
|
||||
pinossrc->properties =
|
||||
gst_structure_copy (gst_value_get_structure (value));
|
||||
break;
|
||||
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gst_pinos_src_get_property (GObject * object, guint prop_id,
|
||||
GValue * value, GParamSpec * pspec)
|
||||
{
|
||||
GstPinosSrc *pinossrc = GST_PINOS_SRC (object);
|
||||
|
||||
switch (prop_id) {
|
||||
case PROP_PATH:
|
||||
g_value_set_string (value, pinossrc->path);
|
||||
break;
|
||||
|
||||
case PROP_CLIENT_NAME:
|
||||
g_value_set_string (value, pinossrc->client_name);
|
||||
break;
|
||||
|
||||
case PROP_STREAM_PROPERTIES:
|
||||
gst_value_set_structure (value, pinossrc->properties);
|
||||
break;
|
||||
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static GstClock *
|
||||
gst_pinos_src_provide_clock (GstElement * elem)
|
||||
{
|
||||
GstPinosSrc *pinossrc = GST_PINOS_SRC (elem);
|
||||
GstClock *clock;
|
||||
|
||||
GST_OBJECT_LOCK (pinossrc);
|
||||
if (!GST_OBJECT_FLAG_IS_SET (pinossrc, GST_ELEMENT_FLAG_PROVIDE_CLOCK))
|
||||
goto clock_disabled;
|
||||
|
||||
if (pinossrc->clock)
|
||||
clock = GST_CLOCK_CAST (gst_object_ref (pinossrc->clock));
|
||||
else
|
||||
clock = NULL;
|
||||
GST_OBJECT_UNLOCK (pinossrc);
|
||||
|
||||
return clock;
|
||||
|
||||
/* ERRORS */
|
||||
clock_disabled:
|
||||
{
|
||||
GST_DEBUG_OBJECT (pinossrc, "clock provide disabled");
|
||||
GST_OBJECT_UNLOCK (pinossrc);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gst_pinos_src_finalize (GObject * object)
|
||||
{
|
||||
GstPinosSrc *pinossrc = GST_PINOS_SRC (object);
|
||||
|
||||
if (pinossrc->properties)
|
||||
gst_structure_free (pinossrc->properties);
|
||||
g_object_unref (pinossrc->fd_allocator);
|
||||
if (pinossrc->clock)
|
||||
gst_object_unref (pinossrc->clock);
|
||||
g_free (pinossrc->path);
|
||||
g_free (pinossrc->client_name);
|
||||
|
||||
G_OBJECT_CLASS (parent_class)->finalize (object);
|
||||
}
|
||||
|
||||
static void
|
||||
gst_pinos_src_class_init (GstPinosSrcClass * klass)
|
||||
{
|
||||
GObjectClass *gobject_class;
|
||||
GstElementClass *gstelement_class;
|
||||
GstBaseSrcClass *gstbasesrc_class;
|
||||
GstPushSrcClass *gstpushsrc_class;
|
||||
|
||||
gobject_class = (GObjectClass *) klass;
|
||||
gstelement_class = (GstElementClass *) klass;
|
||||
gstbasesrc_class = (GstBaseSrcClass *) klass;
|
||||
gstpushsrc_class = (GstPushSrcClass *) klass;
|
||||
|
||||
gobject_class->finalize = gst_pinos_src_finalize;
|
||||
gobject_class->set_property = gst_pinos_src_set_property;
|
||||
gobject_class->get_property = gst_pinos_src_get_property;
|
||||
|
||||
g_object_class_install_property (gobject_class,
|
||||
PROP_PATH,
|
||||
g_param_spec_string ("path",
|
||||
"Path",
|
||||
"The source path to connect to (NULL = default)",
|
||||
NULL,
|
||||
G_PARAM_READWRITE |
|
||||
G_PARAM_STATIC_STRINGS));
|
||||
|
||||
g_object_class_install_property (gobject_class,
|
||||
PROP_CLIENT_NAME,
|
||||
g_param_spec_string ("client-name",
|
||||
"Client Name",
|
||||
"The client name to use (NULL = default)",
|
||||
NULL,
|
||||
G_PARAM_READWRITE |
|
||||
G_PARAM_STATIC_STRINGS));
|
||||
|
||||
g_object_class_install_property (gobject_class,
|
||||
PROP_STREAM_PROPERTIES,
|
||||
g_param_spec_boxed ("stream-properties",
|
||||
"stream properties",
|
||||
"list of pinos stream properties",
|
||||
GST_TYPE_STRUCTURE,
|
||||
G_PARAM_READWRITE |
|
||||
G_PARAM_STATIC_STRINGS));
|
||||
|
||||
gstelement_class->provide_clock = gst_pinos_src_provide_clock;
|
||||
gstelement_class->change_state = gst_pinos_src_change_state;
|
||||
|
||||
gst_element_class_set_static_metadata (gstelement_class,
|
||||
"Pinos source", "Source/Video",
|
||||
"Uses pinos to create video", "Wim Taymans <wim.taymans@gmail.com>");
|
||||
|
||||
gst_element_class_add_pad_template (gstelement_class,
|
||||
gst_static_pad_template_get (&gst_pinos_src_template));
|
||||
|
||||
gstbasesrc_class->negotiate = gst_pinos_src_negotiate;
|
||||
gstbasesrc_class->get_caps = gst_pinos_src_getcaps;
|
||||
gstbasesrc_class->fixate = gst_pinos_src_src_fixate;
|
||||
gstbasesrc_class->unlock = gst_pinos_src_unlock;
|
||||
gstbasesrc_class->unlock_stop = gst_pinos_src_unlock_stop;
|
||||
gstbasesrc_class->start = gst_pinos_src_start;
|
||||
gstbasesrc_class->stop = gst_pinos_src_stop;
|
||||
|
||||
gstpushsrc_class->create = gst_pinos_src_create;
|
||||
|
||||
GST_DEBUG_CATEGORY_INIT (pinos_src_debug, "pinossrc", 0,
|
||||
"Pinos Source");
|
||||
|
||||
fdpayload_data_quark = g_quark_from_static_string ("GstPinosSrcFDPayloadQuark");
|
||||
}
|
||||
|
||||
static void
|
||||
gst_pinos_src_init (GstPinosSrc * src)
|
||||
{
|
||||
/* we operate in time */
|
||||
gst_base_src_set_format (GST_BASE_SRC (src), GST_FORMAT_TIME);
|
||||
gst_base_src_set_live (GST_BASE_SRC (src), TRUE);
|
||||
|
||||
GST_OBJECT_FLAG_SET (src, GST_ELEMENT_FLAG_PROVIDE_CLOCK);
|
||||
|
||||
src->fd_allocator = gst_fd_allocator_new ();
|
||||
src->client_name = pinos_client_name ();
|
||||
}
|
||||
|
||||
static GstCaps *
|
||||
gst_pinos_src_src_fixate (GstBaseSrc * bsrc, GstCaps * caps)
|
||||
{
|
||||
GstStructure *structure;
|
||||
|
||||
caps = gst_caps_make_writable (caps);
|
||||
|
||||
structure = gst_caps_get_structure (caps, 0);
|
||||
|
||||
if (gst_structure_has_name (structure, "video/x-raw")) {
|
||||
gst_structure_fixate_field_nearest_int (structure, "width", 320);
|
||||
gst_structure_fixate_field_nearest_int (structure, "height", 240);
|
||||
gst_structure_fixate_field_nearest_fraction (structure, "framerate", 30, 1);
|
||||
|
||||
if (gst_structure_has_field (structure, "pixel-aspect-ratio"))
|
||||
gst_structure_fixate_field_nearest_fraction (structure,
|
||||
"pixel-aspect-ratio", 1, 1);
|
||||
else
|
||||
gst_structure_set (structure, "pixel-aspect-ratio", GST_TYPE_FRACTION, 1, 1,
|
||||
NULL);
|
||||
|
||||
if (gst_structure_has_field (structure, "colorimetry"))
|
||||
gst_structure_fixate_field_string (structure, "colorimetry", "bt601");
|
||||
if (gst_structure_has_field (structure, "chroma-site"))
|
||||
gst_structure_fixate_field_string (structure, "chroma-site", "mpeg2");
|
||||
|
||||
if (gst_structure_has_field (structure, "interlace-mode"))
|
||||
gst_structure_fixate_field_string (structure, "interlace-mode",
|
||||
"progressive");
|
||||
else
|
||||
gst_structure_set (structure, "interlace-mode", G_TYPE_STRING,
|
||||
"progressive", NULL);
|
||||
} else if (gst_structure_has_name (structure, "audio/x-raw")) {
|
||||
gst_structure_fixate_field_string (structure, "format", "S16LE");
|
||||
gst_structure_fixate_field_nearest_int (structure, "channels", 2);
|
||||
gst_structure_fixate_field_nearest_int (structure, "rate", 44100);
|
||||
}
|
||||
|
||||
caps = GST_BASE_SRC_CLASS (parent_class)->fixate (bsrc, caps);
|
||||
|
||||
return caps;
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
GstPinosSrc *src;
|
||||
PinosPacketFDPayload p;
|
||||
} FDPayloadData;
|
||||
|
||||
static void
|
||||
fdpayload_data_destroy (gpointer user_data)
|
||||
{
|
||||
FDPayloadData *data = user_data;
|
||||
GstPinosSrc *pinossrc = data->src;
|
||||
PinosBufferBuilder b;
|
||||
PinosPacketReleaseFDPayload r;
|
||||
PinosBuffer pbuf;
|
||||
|
||||
r.id = data->p.id;
|
||||
|
||||
GST_DEBUG_OBJECT (pinossrc, "destroy %d", r.id);
|
||||
|
||||
pinos_buffer_builder_init (&b);
|
||||
pinos_buffer_builder_add_release_fd_payload (&b, &r);
|
||||
pinos_buffer_builder_end (&b, &pbuf);
|
||||
|
||||
GST_OBJECT_LOCK (pinossrc);
|
||||
if (pinossrc->stream_state == PINOS_STREAM_STATE_STREAMING) {
|
||||
GST_DEBUG_OBJECT (pinossrc, "send release-fd for %d", r.id);
|
||||
pinos_stream_send_buffer (pinossrc->stream, &pbuf);
|
||||
}
|
||||
GST_OBJECT_UNLOCK (pinossrc);
|
||||
|
||||
pinos_buffer_clear (&pbuf);
|
||||
|
||||
gst_object_unref (pinossrc);
|
||||
g_slice_free (FDPayloadData, data);
|
||||
}
|
||||
|
||||
static void
|
||||
on_new_buffer (GObject *gobject,
|
||||
gpointer user_data)
|
||||
{
|
||||
GstPinosSrc *pinossrc = user_data;
|
||||
PinosBuffer *pbuf;
|
||||
PinosBufferIter it;
|
||||
GstBuffer *buf;
|
||||
GError *error = NULL;
|
||||
|
||||
GST_LOG_OBJECT (pinossrc, "got new buffer");
|
||||
if (!pinos_stream_peek_buffer (pinossrc->stream, &pbuf)) {
|
||||
g_warning ("failed to capture buffer");
|
||||
return;
|
||||
}
|
||||
|
||||
buf = gst_buffer_new ();
|
||||
|
||||
pinos_buffer_iter_init (&it, pbuf);
|
||||
while (pinos_buffer_iter_next (&it)) {
|
||||
switch (pinos_buffer_iter_get_type (&it)) {
|
||||
case PINOS_PACKET_TYPE_HEADER:
|
||||
{
|
||||
PinosPacketHeader hdr;
|
||||
|
||||
if (!pinos_buffer_iter_parse_header (&it, &hdr))
|
||||
goto no_fds;
|
||||
|
||||
if (GST_CLOCK_TIME_IS_VALID (hdr.pts)) {
|
||||
if (hdr.pts > GST_ELEMENT_CAST (pinossrc)->base_time)
|
||||
GST_BUFFER_PTS (buf) = hdr.pts - GST_ELEMENT_CAST (pinossrc)->base_time;
|
||||
|
||||
if (GST_BUFFER_PTS (buf) + hdr.dts_offset > 0)
|
||||
GST_BUFFER_DTS (buf) = GST_BUFFER_PTS (buf) + hdr.dts_offset;
|
||||
}
|
||||
GST_BUFFER_OFFSET (buf) = hdr.seq;
|
||||
break;
|
||||
}
|
||||
case PINOS_PACKET_TYPE_FD_PAYLOAD:
|
||||
{
|
||||
GstMemory *fdmem = NULL;
|
||||
FDPayloadData data;
|
||||
int fd;
|
||||
|
||||
if (!pinos_buffer_iter_parse_fd_payload (&it, &data.p))
|
||||
goto no_fds;
|
||||
GST_DEBUG ("got fd payload id %d", data.p.id);
|
||||
fd = pinos_buffer_get_fd (pbuf, data.p.fd_index, &error);
|
||||
if (fd == -1)
|
||||
goto no_fds;
|
||||
|
||||
fdmem = gst_fd_allocator_alloc (pinossrc->fd_allocator, fd,
|
||||
data.p.offset + data.p.size, GST_FD_MEMORY_FLAG_NONE);
|
||||
gst_memory_resize (fdmem, data.p.offset, data.p.size);
|
||||
gst_buffer_append_memory (buf, fdmem);
|
||||
|
||||
data.src = gst_object_ref (pinossrc);
|
||||
gst_mini_object_set_qdata (GST_MINI_OBJECT_CAST (fdmem),
|
||||
fdpayload_data_quark,
|
||||
g_slice_dup (FDPayloadData, &data),
|
||||
fdpayload_data_destroy);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (pinossrc->current)
|
||||
gst_buffer_unref (pinossrc->current);
|
||||
pinossrc->current = buf;
|
||||
|
||||
pinos_main_loop_signal (pinossrc->loop, FALSE);
|
||||
|
||||
return;
|
||||
|
||||
/* ERRORS */
|
||||
no_fds:
|
||||
{
|
||||
gst_buffer_unref (buf);
|
||||
GST_ELEMENT_ERROR (pinossrc, RESOURCE, FAILED,
|
||||
("buffer error: %s", error->message), (NULL));
|
||||
pinos_main_loop_signal (pinossrc->loop, FALSE);
|
||||
return;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static void
|
||||
on_stream_notify (GObject *gobject,
|
||||
GParamSpec *pspec,
|
||||
gpointer user_data)
|
||||
{
|
||||
GstPinosSrc *pinossrc = user_data;
|
||||
PinosStreamState state = pinos_stream_get_state (pinossrc->stream);
|
||||
|
||||
GST_DEBUG ("got stream state %d", state);
|
||||
|
||||
GST_OBJECT_LOCK (pinossrc);
|
||||
pinossrc->stream_state = state;
|
||||
GST_OBJECT_UNLOCK (pinossrc);
|
||||
|
||||
switch (state) {
|
||||
case PINOS_STREAM_STATE_UNCONNECTED:
|
||||
case PINOS_STREAM_STATE_CONNECTING:
|
||||
case PINOS_STREAM_STATE_STARTING:
|
||||
case PINOS_STREAM_STATE_STREAMING:
|
||||
case PINOS_STREAM_STATE_READY:
|
||||
break;
|
||||
case PINOS_STREAM_STATE_ERROR:
|
||||
GST_ELEMENT_ERROR (pinossrc, RESOURCE, FAILED,
|
||||
("stream error: %s",
|
||||
pinos_stream_get_error (pinossrc->stream)->message), (NULL));
|
||||
break;
|
||||
}
|
||||
pinos_main_loop_signal (pinossrc->loop, FALSE);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gst_pinos_src_stream_start (GstPinosSrc *pinossrc, GstCaps * caps)
|
||||
{
|
||||
gchar *str;
|
||||
GBytes *format;
|
||||
gboolean res;
|
||||
|
||||
str = gst_caps_to_string (caps);
|
||||
format = g_bytes_new_take (str, strlen (str) + 1);
|
||||
|
||||
pinos_main_loop_lock (pinossrc->loop);
|
||||
res = pinos_stream_start (pinossrc->stream, format, PINOS_STREAM_MODE_BUFFER);
|
||||
while (TRUE) {
|
||||
PinosStreamState state = pinos_stream_get_state (pinossrc->stream);
|
||||
|
||||
if (state == PINOS_STREAM_STATE_STREAMING)
|
||||
break;
|
||||
|
||||
if (state == PINOS_STREAM_STATE_ERROR)
|
||||
goto start_error;
|
||||
|
||||
pinos_main_loop_wait (pinossrc->loop);
|
||||
}
|
||||
pinos_main_loop_unlock (pinossrc->loop);
|
||||
|
||||
return res;
|
||||
|
||||
start_error:
|
||||
{
|
||||
GST_DEBUG_OBJECT (pinossrc, "error starting stream");
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
parse_clock_info (GstPinosSrc *pinossrc)
|
||||
{
|
||||
PinosProperties *props;
|
||||
const gchar *var;
|
||||
|
||||
g_object_get (pinossrc->stream, "properties", &props, NULL);
|
||||
|
||||
var = pinos_properties_get (props, "pinos.clock.type");
|
||||
if (var == NULL)
|
||||
return;
|
||||
|
||||
GST_DEBUG_OBJECT (pinossrc, "got clock type %s", var);
|
||||
if (strcmp (var, "gst.net.time.provider") == 0) {
|
||||
const gchar *address;
|
||||
gint port;
|
||||
|
||||
address = pinos_properties_get (props, "pinos.clock.address");
|
||||
port = atoi (pinos_properties_get (props, "pinos.clock.port"));
|
||||
|
||||
GST_DEBUG_OBJECT (pinossrc, "making net clock for %s:%d", address, port);
|
||||
if (pinossrc->clock)
|
||||
gst_object_unref (pinossrc->clock);
|
||||
pinossrc->clock = gst_net_client_clock_new ("pinosclock", address, port, 0);
|
||||
}
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gst_pinos_src_negotiate (GstBaseSrc * basesrc)
|
||||
{
|
||||
GstPinosSrc *pinossrc = GST_PINOS_SRC (basesrc);
|
||||
GstCaps *thiscaps;
|
||||
GstCaps *caps = NULL;
|
||||
GstCaps *peercaps = NULL;
|
||||
gboolean result = FALSE;
|
||||
|
||||
/* first see what is possible on our source pad */
|
||||
thiscaps = gst_pad_query_caps (GST_BASE_SRC_PAD (basesrc), NULL);
|
||||
GST_DEBUG_OBJECT (basesrc, "caps of src: %" GST_PTR_FORMAT, thiscaps);
|
||||
/* nothing or anything is allowed, we're done */
|
||||
if (thiscaps == NULL)
|
||||
goto no_nego_needed;
|
||||
|
||||
if (G_UNLIKELY (gst_caps_is_empty (thiscaps)))
|
||||
goto no_caps;
|
||||
|
||||
/* get the peer caps */
|
||||
peercaps = gst_pad_peer_query_caps (GST_BASE_SRC_PAD (basesrc), thiscaps);
|
||||
GST_DEBUG_OBJECT (basesrc, "caps of peer: %" GST_PTR_FORMAT, peercaps);
|
||||
if (peercaps) {
|
||||
/* The result is already a subset of our caps */
|
||||
caps = peercaps;
|
||||
gst_caps_unref (thiscaps);
|
||||
} else {
|
||||
/* no peer, work with our own caps then */
|
||||
caps = thiscaps;
|
||||
}
|
||||
if (caps && !gst_caps_is_empty (caps)) {
|
||||
GBytes *accepted, *possible;
|
||||
gchar *str;
|
||||
|
||||
GST_DEBUG_OBJECT (basesrc, "have caps: %" GST_PTR_FORMAT, caps);
|
||||
|
||||
/* open a connection with these caps */
|
||||
str = gst_caps_to_string (caps);
|
||||
accepted = g_bytes_new_take (str, strlen (str) + 1);
|
||||
|
||||
/* first disconnect */
|
||||
pinos_main_loop_lock (pinossrc->loop);
|
||||
if (pinos_stream_get_state (pinossrc->stream) != PINOS_STREAM_STATE_UNCONNECTED) {
|
||||
GST_DEBUG_OBJECT (basesrc, "disconnect capture");
|
||||
pinos_stream_disconnect (pinossrc->stream);
|
||||
while (TRUE) {
|
||||
PinosStreamState state = pinos_stream_get_state (pinossrc->stream);
|
||||
|
||||
if (state == PINOS_STREAM_STATE_UNCONNECTED)
|
||||
break;
|
||||
|
||||
if (state == PINOS_STREAM_STATE_ERROR) {
|
||||
g_bytes_unref (accepted);
|
||||
goto connect_error;
|
||||
}
|
||||
|
||||
pinos_main_loop_wait (pinossrc->loop);
|
||||
}
|
||||
}
|
||||
|
||||
GST_DEBUG_OBJECT (basesrc, "connect capture with path %s", pinossrc->path);
|
||||
pinos_stream_connect_capture (pinossrc->stream, pinossrc->path, 0, accepted);
|
||||
|
||||
while (TRUE) {
|
||||
PinosStreamState state = pinos_stream_get_state (pinossrc->stream);
|
||||
|
||||
if (state == PINOS_STREAM_STATE_READY)
|
||||
break;
|
||||
|
||||
if (state == PINOS_STREAM_STATE_ERROR)
|
||||
goto connect_error;
|
||||
|
||||
pinos_main_loop_wait (pinossrc->loop);
|
||||
}
|
||||
pinos_main_loop_unlock (pinossrc->loop);
|
||||
|
||||
parse_clock_info (pinossrc);
|
||||
|
||||
g_object_get (pinossrc->stream, "possible-formats", &possible, NULL);
|
||||
if (possible) {
|
||||
GstCaps *newcaps;
|
||||
|
||||
newcaps = gst_caps_from_string (g_bytes_get_data (possible, NULL));
|
||||
if (newcaps)
|
||||
caps = newcaps;
|
||||
|
||||
g_bytes_unref (possible);
|
||||
}
|
||||
|
||||
|
||||
/* now fixate */
|
||||
GST_DEBUG_OBJECT (basesrc, "server fixated caps: %" GST_PTR_FORMAT, caps);
|
||||
if (gst_caps_is_any (caps)) {
|
||||
GST_DEBUG_OBJECT (basesrc, "any caps, we stop");
|
||||
/* hmm, still anything, so element can do anything and
|
||||
* nego is not needed */
|
||||
result = TRUE;
|
||||
} else {
|
||||
caps = gst_pinos_src_src_fixate (basesrc, caps);
|
||||
GST_DEBUG_OBJECT (basesrc, "fixated to: %" GST_PTR_FORMAT, caps);
|
||||
if (gst_caps_is_fixed (caps)) {
|
||||
/* yay, fixed caps, use those then, it's possible that the subclass does
|
||||
* not accept this caps after all and we have to fail. */
|
||||
result = gst_base_src_set_caps (basesrc, caps);
|
||||
if (result) {
|
||||
result = gst_pinos_src_stream_start (pinossrc, caps);
|
||||
}
|
||||
}
|
||||
}
|
||||
gst_caps_unref (caps);
|
||||
} else {
|
||||
if (caps)
|
||||
gst_caps_unref (caps);
|
||||
GST_DEBUG_OBJECT (basesrc, "no common caps");
|
||||
}
|
||||
pinossrc->negotiated = result;
|
||||
|
||||
return result;
|
||||
|
||||
no_nego_needed:
|
||||
{
|
||||
GST_DEBUG_OBJECT (basesrc, "no negotiation needed");
|
||||
if (thiscaps)
|
||||
gst_caps_unref (thiscaps);
|
||||
return TRUE;
|
||||
}
|
||||
no_caps:
|
||||
{
|
||||
GST_ELEMENT_ERROR (basesrc, STREAM, FORMAT,
|
||||
("No supported formats found"),
|
||||
("This element did not produce valid caps"));
|
||||
if (thiscaps)
|
||||
gst_caps_unref (thiscaps);
|
||||
return TRUE;
|
||||
}
|
||||
connect_error:
|
||||
{
|
||||
pinos_main_loop_unlock (pinossrc->loop);
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
static GstCaps *
|
||||
gst_pinos_src_getcaps (GstBaseSrc * bsrc, GstCaps * filter)
|
||||
{
|
||||
return GST_BASE_SRC_CLASS (parent_class)->get_caps (bsrc, filter);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gst_pinos_src_unlock (GstBaseSrc * basesrc)
|
||||
{
|
||||
GstPinosSrc *pinossrc = GST_PINOS_SRC (basesrc);
|
||||
|
||||
pinos_main_loop_lock (pinossrc->loop);
|
||||
GST_DEBUG_OBJECT (pinossrc, "setting flushing");
|
||||
pinossrc->flushing = TRUE;
|
||||
pinos_main_loop_signal (pinossrc->loop, FALSE);
|
||||
pinos_main_loop_unlock (pinossrc->loop);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gst_pinos_src_unlock_stop (GstBaseSrc * basesrc)
|
||||
{
|
||||
GstPinosSrc *pinossrc = GST_PINOS_SRC (basesrc);
|
||||
|
||||
pinos_main_loop_lock (pinossrc->loop);
|
||||
GST_DEBUG_OBJECT (pinossrc, "unsetting flushing");
|
||||
pinossrc->flushing = FALSE;
|
||||
pinos_main_loop_unlock (pinossrc->loop);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static GstFlowReturn
|
||||
gst_pinos_src_create (GstPushSrc * psrc, GstBuffer ** buffer)
|
||||
{
|
||||
GstPinosSrc *pinossrc;
|
||||
|
||||
pinossrc = GST_PINOS_SRC (psrc);
|
||||
|
||||
if (!pinossrc->negotiated)
|
||||
goto not_negotiated;
|
||||
|
||||
pinos_main_loop_lock (pinossrc->loop);
|
||||
while (TRUE) {
|
||||
PinosStreamState state;
|
||||
|
||||
if (pinossrc->flushing)
|
||||
goto streaming_stopped;
|
||||
|
||||
pinos_main_loop_wait (pinossrc->loop);
|
||||
|
||||
state = pinos_stream_get_state (pinossrc->stream);
|
||||
if (state == PINOS_STREAM_STATE_ERROR)
|
||||
goto streaming_error;
|
||||
|
||||
if (state != PINOS_STREAM_STATE_STREAMING)
|
||||
goto streaming_stopped;
|
||||
|
||||
if (pinossrc->current != NULL)
|
||||
break;
|
||||
}
|
||||
pinos_main_loop_unlock (pinossrc->loop);
|
||||
|
||||
*buffer = pinossrc->current;
|
||||
pinossrc->current = NULL;
|
||||
|
||||
return GST_FLOW_OK;
|
||||
|
||||
not_negotiated:
|
||||
{
|
||||
return GST_FLOW_NOT_NEGOTIATED;
|
||||
}
|
||||
streaming_error:
|
||||
{
|
||||
pinos_main_loop_unlock (pinossrc->loop);
|
||||
return GST_FLOW_ERROR;
|
||||
}
|
||||
streaming_stopped:
|
||||
{
|
||||
pinos_main_loop_unlock (pinossrc->loop);
|
||||
return GST_FLOW_FLUSHING;
|
||||
}
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gst_pinos_src_start (GstBaseSrc * basesrc)
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gst_pinos_src_stop (GstBaseSrc * basesrc)
|
||||
{
|
||||
GstPinosSrc *pinossrc;
|
||||
|
||||
pinossrc = GST_PINOS_SRC (basesrc);
|
||||
if (pinossrc->current)
|
||||
gst_buffer_unref (pinossrc->current);
|
||||
pinossrc->current = NULL;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
on_context_notify (GObject *gobject,
|
||||
GParamSpec *pspec,
|
||||
gpointer user_data)
|
||||
{
|
||||
GstPinosSrc *pinossrc = user_data;
|
||||
PinosContextState state = pinos_context_get_state (pinossrc->ctx);
|
||||
|
||||
GST_DEBUG ("got context state %d", state);
|
||||
|
||||
switch (state) {
|
||||
case PINOS_CONTEXT_STATE_UNCONNECTED:
|
||||
case PINOS_CONTEXT_STATE_CONNECTING:
|
||||
case PINOS_CONTEXT_STATE_REGISTERING:
|
||||
case PINOS_CONTEXT_STATE_READY:
|
||||
break;
|
||||
case PINOS_CONTEXT_STATE_ERROR:
|
||||
GST_ELEMENT_ERROR (pinossrc, RESOURCE, FAILED,
|
||||
("context error: %s",
|
||||
pinos_context_get_error (pinossrc->ctx)->message), (NULL));
|
||||
break;
|
||||
}
|
||||
pinos_main_loop_signal (pinossrc->loop, FALSE);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
copy_properties (GQuark field_id,
|
||||
const GValue *value,
|
||||
gpointer user_data)
|
||||
{
|
||||
PinosProperties *properties = user_data;
|
||||
|
||||
if (G_VALUE_HOLDS_STRING (value))
|
||||
pinos_properties_set (properties,
|
||||
g_quark_to_string (field_id),
|
||||
g_value_get_string (value));
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gst_pinos_src_open (GstPinosSrc * pinossrc)
|
||||
{
|
||||
GError *error = NULL;
|
||||
PinosProperties *props;
|
||||
|
||||
pinossrc->context = g_main_context_new ();
|
||||
GST_DEBUG ("context %p", pinossrc->context);
|
||||
|
||||
pinossrc->loop = pinos_main_loop_new (pinossrc->context, "pinos-main-loop");
|
||||
if (!pinos_main_loop_start (pinossrc->loop, &error))
|
||||
goto mainloop_failed;
|
||||
|
||||
pinos_main_loop_lock (pinossrc->loop);
|
||||
pinossrc->ctx = pinos_context_new (pinossrc->context, g_get_application_name (), NULL);
|
||||
g_signal_connect (pinossrc->ctx, "notify::state", (GCallback) on_context_notify, pinossrc);
|
||||
|
||||
pinos_context_connect (pinossrc->ctx, PINOS_CONTEXT_FLAGS_NONE);
|
||||
|
||||
while (TRUE) {
|
||||
PinosContextState state = pinos_context_get_state (pinossrc->ctx);
|
||||
|
||||
if (state == PINOS_CONTEXT_STATE_READY)
|
||||
break;
|
||||
|
||||
if (state == PINOS_CONTEXT_STATE_ERROR)
|
||||
goto connect_error;
|
||||
|
||||
pinos_main_loop_wait (pinossrc->loop);
|
||||
}
|
||||
|
||||
if (pinossrc->properties) {
|
||||
props = pinos_properties_new (NULL, NULL);
|
||||
gst_structure_foreach (pinossrc->properties, copy_properties, props);
|
||||
} else {
|
||||
props = NULL;
|
||||
}
|
||||
|
||||
pinossrc->stream = pinos_stream_new (pinossrc->ctx, pinossrc->client_name, props);
|
||||
g_signal_connect (pinossrc->stream, "notify::state", (GCallback) on_stream_notify, pinossrc);
|
||||
g_signal_connect (pinossrc->stream, "new-buffer", (GCallback) on_new_buffer, pinossrc);
|
||||
pinos_main_loop_unlock (pinossrc->loop);
|
||||
|
||||
return TRUE;
|
||||
|
||||
/* ERRORS */
|
||||
mainloop_failed:
|
||||
{
|
||||
GST_ELEMENT_ERROR (pinossrc, RESOURCE, FAILED,
|
||||
("mainloop error: %s", error->message), (NULL));
|
||||
return FALSE;
|
||||
}
|
||||
connect_error:
|
||||
{
|
||||
pinos_main_loop_unlock (pinossrc->loop);
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gst_pinos_src_close (GstPinosSrc * pinossrc)
|
||||
{
|
||||
pinos_main_loop_stop (pinossrc->loop);
|
||||
g_clear_object (&pinossrc->loop);
|
||||
g_clear_object (&pinossrc->ctx);
|
||||
g_main_context_unref (pinossrc->context);
|
||||
GST_OBJECT_LOCK (pinossrc);
|
||||
pinossrc->stream_state = PINOS_STREAM_STATE_UNCONNECTED;
|
||||
g_clear_object (&pinossrc->stream);
|
||||
GST_OBJECT_UNLOCK (pinossrc);
|
||||
|
||||
if (pinossrc->current)
|
||||
gst_buffer_unref (pinossrc->current);
|
||||
pinossrc->current = NULL;
|
||||
if (pinossrc->clock)
|
||||
gst_object_unref (pinossrc->clock);
|
||||
pinossrc->clock = NULL;
|
||||
}
|
||||
|
||||
static GstStateChangeReturn
|
||||
gst_pinos_src_change_state (GstElement * element, GstStateChange transition)
|
||||
{
|
||||
GstStateChangeReturn ret;
|
||||
GstPinosSrc *this = GST_PINOS_SRC_CAST (element);
|
||||
|
||||
switch (transition) {
|
||||
case GST_STATE_CHANGE_NULL_TO_READY:
|
||||
if (!gst_pinos_src_open (this))
|
||||
goto open_failed;
|
||||
break;
|
||||
case GST_STATE_CHANGE_READY_TO_PAUSED:
|
||||
break;
|
||||
case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
|
||||
/* uncork and start recording */
|
||||
break;
|
||||
case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
|
||||
/* stop recording ASAP by corking */
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
|
||||
|
||||
switch (transition) {
|
||||
case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
|
||||
break;
|
||||
case GST_STATE_CHANGE_PAUSED_TO_READY:
|
||||
this->negotiated = FALSE;
|
||||
break;
|
||||
case GST_STATE_CHANGE_READY_TO_NULL:
|
||||
gst_pinos_src_close (this);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return ret;
|
||||
|
||||
/* ERRORS */
|
||||
open_failed:
|
||||
{
|
||||
return GST_STATE_CHANGE_FAILURE;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,81 +0,0 @@
|
|||
/* GStreamer
|
||||
* Copyright (C) <2015> Wim Taymans <wim.taymans@gmail.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#ifndef __GST_PINOS_SRC_H__
|
||||
#define __GST_PINOS_SRC_H__
|
||||
|
||||
#include <gst/gst.h>
|
||||
#include <gst/base/gstpushsrc.h>
|
||||
|
||||
#include <client/pinos.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define GST_TYPE_PINOS_SRC \
|
||||
(gst_pinos_src_get_type())
|
||||
#define GST_PINOS_SRC(obj) \
|
||||
(G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_PINOS_SRC,GstPinosSrc))
|
||||
#define GST_PINOS_SRC_CLASS(klass) \
|
||||
(G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_PINOS_SRC,GstPinosSrcClass))
|
||||
#define GST_IS_PINOS_SRC(obj) \
|
||||
(G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_PINOS_SRC))
|
||||
#define GST_IS_PINOS_SRC_CLASS(klass) \
|
||||
(G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_PINOS_SRC))
|
||||
#define GST_PINOS_SRC_CAST(obj) \
|
||||
((GstPinosSrc *) (obj))
|
||||
|
||||
typedef struct _GstPinosSrc GstPinosSrc;
|
||||
typedef struct _GstPinosSrcClass GstPinosSrcClass;
|
||||
|
||||
/**
|
||||
* GstPinosSrc:
|
||||
*
|
||||
* Opaque data structure.
|
||||
*/
|
||||
struct _GstPinosSrc {
|
||||
GstPushSrc element;
|
||||
|
||||
/*< private >*/
|
||||
gchar *path;
|
||||
gchar *client_name;
|
||||
|
||||
gboolean negotiated;
|
||||
gboolean flushing;
|
||||
|
||||
GMainContext *context;
|
||||
PinosMainLoop *loop;
|
||||
PinosContext *ctx;
|
||||
PinosStream *stream;
|
||||
PinosStreamState stream_state;
|
||||
GstAllocator *fd_allocator;
|
||||
GstStructure *properties;
|
||||
|
||||
GstBuffer *current;
|
||||
GstClock *clock;
|
||||
};
|
||||
|
||||
struct _GstPinosSrcClass {
|
||||
GstPushSrcClass parent_class;
|
||||
};
|
||||
|
||||
GType gst_pinos_src_get_type (void);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __GST_PINOS_SRC_H__ */
|
||||
|
|
@ -1,145 +0,0 @@
|
|||
/* GStreamer
|
||||
* Copyright (C) 2014 William Manley <will@williammanley.net>
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#define _GNU_SOURCE
|
||||
|
||||
#include "gsttmpfileallocator.h"
|
||||
#include <gst/allocators/gstfdmemory.h>
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#define PAGE_ALIGN 4095
|
||||
|
||||
#define GST_TYPE_TMPFILE_ALLOCATOR (gst_tmpfile_allocator_get_type ())
|
||||
|
||||
typedef struct
|
||||
{
|
||||
GstFdAllocator parent;
|
||||
} GstTmpFileAllocator;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
GstFdAllocatorClass parent_class;
|
||||
} GstTmpFileAllocatorClass;
|
||||
|
||||
GType gst_tmpfile_allocator_get_type (void);
|
||||
G_DEFINE_TYPE (GstTmpFileAllocator, gst_tmpfile_allocator, GST_TYPE_FD_ALLOCATOR);
|
||||
|
||||
static int
|
||||
tmpfile_create (GstTmpFileAllocator * allocator, gsize size)
|
||||
{
|
||||
char filename[] = "/dev/shm/tmpfilepay.XXXXXX";
|
||||
int fd, result;
|
||||
|
||||
GST_DEBUG_OBJECT (allocator, "tmpfile_create");
|
||||
|
||||
fd = mkostemp (filename, O_CLOEXEC);
|
||||
if (fd == -1) {
|
||||
GST_WARNING_OBJECT (allocator, "Failed to create temporary file: %s",
|
||||
strerror (errno));
|
||||
return -1;
|
||||
}
|
||||
unlink (filename);
|
||||
|
||||
result = ftruncate (fd, size);
|
||||
if (result == -1) {
|
||||
GST_WARNING_OBJECT (allocator, "Failed to resize temporary file: %s",
|
||||
strerror (errno));
|
||||
close (fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return fd;
|
||||
}
|
||||
|
||||
|
||||
inline static gsize
|
||||
pad (gsize off, gsize align)
|
||||
{
|
||||
return (off + align) / (align + 1) * (align + 1);
|
||||
}
|
||||
|
||||
static GstMemory *
|
||||
gst_tmpfile_allocator_alloc (GstAllocator * allocator, gsize size,
|
||||
GstAllocationParams * params)
|
||||
{
|
||||
GstTmpFileAllocator *alloc = (GstTmpFileAllocator *) allocator;
|
||||
GstMemory *mem;
|
||||
int fd;
|
||||
gsize maxsize;
|
||||
|
||||
g_return_val_if_fail (params != NULL, NULL);
|
||||
|
||||
maxsize =
|
||||
pad (size + pad (params->prefix, params->align) + params->padding,
|
||||
PAGE_ALIGN);
|
||||
|
||||
fd = tmpfile_create (alloc, maxsize);
|
||||
if (fd < 0)
|
||||
return NULL;
|
||||
|
||||
mem = gst_fd_allocator_alloc (allocator, fd, maxsize, GST_FD_MEMORY_FLAG_NONE);
|
||||
gst_memory_resize (mem, pad (params->prefix, params->align), size);
|
||||
|
||||
return mem;
|
||||
}
|
||||
|
||||
static void
|
||||
gst_tmpfile_allocator_class_init (GstTmpFileAllocatorClass * klass)
|
||||
{
|
||||
GstAllocatorClass *allocator_class;
|
||||
|
||||
allocator_class = (GstAllocatorClass *) klass;
|
||||
|
||||
allocator_class->alloc = gst_tmpfile_allocator_alloc;
|
||||
}
|
||||
|
||||
static void
|
||||
gst_tmpfile_allocator_init (GstTmpFileAllocator * allocator)
|
||||
{
|
||||
GstAllocator *alloc = GST_ALLOCATOR_CAST (allocator);
|
||||
|
||||
alloc->mem_type = GST_ALLOCATOR_TMPFILE;
|
||||
|
||||
GST_OBJECT_FLAG_UNSET (allocator, GST_ALLOCATOR_FLAG_CUSTOM_ALLOC);
|
||||
}
|
||||
|
||||
GstAllocator *
|
||||
gst_tmpfile_allocator_new (void)
|
||||
{
|
||||
return g_object_new (GST_TYPE_TMPFILE_ALLOCATOR, NULL);
|
||||
}
|
||||
|
||||
gint
|
||||
gst_tmpfile_memory_get_fd (GstMemory * mem)
|
||||
{
|
||||
g_return_val_if_fail (gst_is_tmpfile_memory (mem), -1);
|
||||
|
||||
return gst_fd_memory_get_fd (mem);
|
||||
}
|
||||
|
||||
gboolean
|
||||
gst_is_tmpfile_memory (GstMemory * mem)
|
||||
{
|
||||
return gst_memory_is_type (mem, GST_ALLOCATOR_TMPFILE);
|
||||
}
|
||||
|
||||
|
|
@ -1,37 +0,0 @@
|
|||
/* GStreamer
|
||||
* Copyright (C) 2014 William Manley <will@williammanley.net>
|
||||
*
|
||||
* 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_TMPFILE_ALLOCATOR_H_
|
||||
#define _GST_TMPFILE_ALLOCATOR_H_
|
||||
|
||||
#include <gst/gst.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define GST_ALLOCATOR_TMPFILE "tmpfile"
|
||||
|
||||
|
||||
/* Allocator that allocates memory from a file stored on a tmpfs */
|
||||
GstAllocator* gst_tmpfile_allocator_new (void);
|
||||
|
||||
gint gst_tmpfile_memory_get_fd (GstMemory * mem);
|
||||
gboolean gst_is_tmpfile_memory (GstMemory * mem);
|
||||
|
||||
G_END_DECLS
|
||||
#endif /* _GST_TMPFILE_ALLOCATOR_H_ */
|
||||
|
|
@ -1,308 +0,0 @@
|
|||
/* Pinos
|
||||
* Copyright (C) 2015 Wim Taymans <wim.taymans@gmail.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include <gst/gst.h>
|
||||
#include <gio/gio.h>
|
||||
|
||||
#include "gst-manager.h"
|
||||
#include "gst-source.h"
|
||||
|
||||
#define PINOS_GST_MANAGER_GET_PRIVATE(obj) \
|
||||
(G_TYPE_INSTANCE_GET_PRIVATE ((obj), PINOS_TYPE_GST_MANAGER, PinosGstManagerPrivate))
|
||||
|
||||
struct _PinosGstManagerPrivate
|
||||
{
|
||||
PinosDaemon *daemon;
|
||||
|
||||
GstDeviceMonitor *monitor;
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
PROP_0,
|
||||
PROP_DAEMON
|
||||
};
|
||||
|
||||
G_DEFINE_TYPE (PinosGstManager, pinos_gst_manager, G_TYPE_OBJECT);
|
||||
|
||||
static gboolean
|
||||
copy_properties (GQuark field_id,
|
||||
const GValue *value,
|
||||
gpointer user_data)
|
||||
{
|
||||
PinosProperties *properties = user_data;
|
||||
|
||||
if (G_VALUE_HOLDS_STRING (value))
|
||||
pinos_properties_set (properties,
|
||||
g_quark_to_string (field_id),
|
||||
g_value_get_string (value));
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
device_added (PinosGstManager *manager,
|
||||
GstDevice *device)
|
||||
{
|
||||
PinosGstManagerPrivate *priv = manager->priv;
|
||||
gchar *name;
|
||||
GstElement *element;
|
||||
PinosSource *source;
|
||||
GstStructure *p;
|
||||
PinosProperties *properties;
|
||||
GstCaps *caps;
|
||||
|
||||
name = gst_device_get_display_name (device);
|
||||
if (strcmp (name, "gst") == 0)
|
||||
return;
|
||||
|
||||
caps = gst_device_get_caps (device);
|
||||
|
||||
g_print("Device added: %s\n", name);
|
||||
|
||||
properties = pinos_properties_new (NULL, NULL);
|
||||
if ((p = gst_device_get_properties (device)))
|
||||
gst_structure_foreach (p, copy_properties, properties);
|
||||
|
||||
pinos_properties_set (properties,
|
||||
"gstreamer.device.class",
|
||||
gst_device_get_device_class (device));
|
||||
|
||||
element = gst_device_create_element (device, NULL);
|
||||
source = pinos_gst_source_new (priv->daemon,
|
||||
name,
|
||||
properties,
|
||||
element,
|
||||
caps);
|
||||
g_object_set_data (G_OBJECT (device), "PinosSource", source);
|
||||
|
||||
gst_caps_unref (caps);
|
||||
g_free (name);
|
||||
}
|
||||
|
||||
static void
|
||||
device_removed (PinosGstManager *manager,
|
||||
GstDevice *device)
|
||||
{
|
||||
gchar *name;
|
||||
PinosSource *source;
|
||||
|
||||
name = gst_device_get_display_name (device);
|
||||
if (strcmp (name, "gst") == 0)
|
||||
return;
|
||||
|
||||
g_print("Device removed: %s\n", name);
|
||||
|
||||
source = g_object_steal_data (G_OBJECT (device), "PinosSource");
|
||||
g_object_unref (source);
|
||||
g_free (name);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
bus_handler (GstBus *bus,
|
||||
GstMessage *message,
|
||||
gpointer user_data)
|
||||
{
|
||||
PinosGstManager *manager = user_data;
|
||||
GstDevice *device;
|
||||
|
||||
switch (GST_MESSAGE_TYPE (message)) {
|
||||
case GST_MESSAGE_DEVICE_ADDED:
|
||||
gst_message_parse_device_added (message, &device);
|
||||
device_added (manager, device);
|
||||
break;
|
||||
case GST_MESSAGE_DEVICE_REMOVED:
|
||||
gst_message_parse_device_removed (message, &device);
|
||||
device_removed (manager, device);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
disable_pinos_provider (PinosGstManager *manager)
|
||||
{
|
||||
GList *factories = NULL;
|
||||
|
||||
factories = gst_device_provider_factory_list_get_device_providers (1);
|
||||
|
||||
while (factories) {
|
||||
GstDeviceProviderFactory *factory = factories->data;
|
||||
|
||||
if (strcmp (GST_OBJECT_NAME (factory), "pinosdeviceprovider") == 0) {
|
||||
gst_plugin_feature_set_rank (GST_PLUGIN_FEATURE (factory), 0);
|
||||
}
|
||||
factories = g_list_remove (factories, factory);
|
||||
gst_object_unref (factory);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static void
|
||||
start_monitor (PinosGstManager *manager)
|
||||
{
|
||||
PinosGstManagerPrivate *priv = manager->priv;
|
||||
GstBus *bus;
|
||||
GList *devices;
|
||||
gchar **providers;
|
||||
gchar *provided;
|
||||
PinosProperties *props;
|
||||
|
||||
disable_pinos_provider (manager);
|
||||
|
||||
priv->monitor = gst_device_monitor_new ();
|
||||
|
||||
bus = gst_device_monitor_get_bus (priv->monitor);
|
||||
gst_bus_add_watch (bus, bus_handler, manager);
|
||||
gst_object_unref (bus);
|
||||
|
||||
gst_device_monitor_add_filter (priv->monitor, "Video/Source", NULL);
|
||||
gst_device_monitor_add_filter (priv->monitor, "Audio/Source", NULL);
|
||||
gst_device_monitor_start (priv->monitor);
|
||||
|
||||
providers = gst_device_monitor_get_providers (priv->monitor);
|
||||
provided = g_strjoinv (",", providers);
|
||||
g_strfreev (providers);
|
||||
|
||||
g_object_get (priv->daemon, "properties", &props, NULL);
|
||||
pinos_properties_set (props, "gstreamer.deviceproviders", provided);
|
||||
g_object_set (priv->daemon, "properties", props, NULL);
|
||||
pinos_properties_free (props);
|
||||
|
||||
g_free (provided);
|
||||
|
||||
devices = gst_device_monitor_get_devices (priv->monitor);
|
||||
while (devices != NULL) {
|
||||
GstDevice *device = devices->data;
|
||||
|
||||
device_added (manager, device);
|
||||
gst_object_unref (device);
|
||||
devices = g_list_remove_link (devices, devices);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
stop_monitor (PinosGstManager *manager)
|
||||
{
|
||||
PinosGstManagerPrivate *priv = manager->priv;
|
||||
|
||||
if (priv->monitor) {
|
||||
gst_device_monitor_stop (priv->monitor);
|
||||
g_object_unref (priv->monitor);
|
||||
priv->monitor = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
pinos_gst_manager_get_property (GObject *object,
|
||||
guint prop_id,
|
||||
GValue *value,
|
||||
GParamSpec *pspec)
|
||||
{
|
||||
PinosGstManager *manager = PINOS_GST_MANAGER (object);
|
||||
PinosGstManagerPrivate *priv = manager->priv;
|
||||
|
||||
switch (prop_id) {
|
||||
case PROP_DAEMON:
|
||||
g_value_set_object (value, priv->daemon);
|
||||
break;
|
||||
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
pinos_gst_manager_set_property (GObject *object,
|
||||
guint prop_id,
|
||||
const GValue *value,
|
||||
GParamSpec *pspec)
|
||||
{
|
||||
PinosGstManager *manager = PINOS_GST_MANAGER (object);
|
||||
PinosGstManagerPrivate *priv = manager->priv;
|
||||
|
||||
switch (prop_id) {
|
||||
case PROP_DAEMON:
|
||||
priv->daemon = g_value_dup_object (value);
|
||||
break;
|
||||
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gst_manager_constructed (GObject * object)
|
||||
{
|
||||
PinosGstManager *manager = PINOS_GST_MANAGER (object);
|
||||
|
||||
start_monitor (manager);
|
||||
|
||||
G_OBJECT_CLASS (pinos_gst_manager_parent_class)->constructed (object);
|
||||
}
|
||||
|
||||
static void
|
||||
gst_manager_finalize (GObject * object)
|
||||
{
|
||||
PinosGstManager *manager = PINOS_GST_MANAGER (object);
|
||||
|
||||
stop_monitor (manager);
|
||||
|
||||
G_OBJECT_CLASS (pinos_gst_manager_parent_class)->finalize (object);
|
||||
}
|
||||
|
||||
static void
|
||||
pinos_gst_manager_class_init (PinosGstManagerClass * klass)
|
||||
{
|
||||
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
|
||||
|
||||
g_type_class_add_private (klass, sizeof (PinosGstManagerPrivate));
|
||||
|
||||
gobject_class->constructed = gst_manager_constructed;
|
||||
gobject_class->finalize = gst_manager_finalize;
|
||||
gobject_class->set_property = pinos_gst_manager_set_property;
|
||||
gobject_class->get_property = pinos_gst_manager_get_property;
|
||||
|
||||
g_object_class_install_property (gobject_class,
|
||||
PROP_DAEMON,
|
||||
g_param_spec_object ("daemon",
|
||||
"Daemon",
|
||||
"The daemon",
|
||||
PINOS_TYPE_DAEMON,
|
||||
G_PARAM_READWRITE |
|
||||
G_PARAM_CONSTRUCT_ONLY |
|
||||
G_PARAM_STATIC_STRINGS));
|
||||
}
|
||||
|
||||
static void
|
||||
pinos_gst_manager_init (PinosGstManager * manager)
|
||||
{
|
||||
manager->priv = PINOS_GST_MANAGER_GET_PRIVATE (manager);
|
||||
}
|
||||
|
||||
PinosGstManager *
|
||||
pinos_gst_manager_new (PinosDaemon *daemon)
|
||||
{
|
||||
return g_object_new (PINOS_TYPE_GST_MANAGER, "daemon", daemon, NULL);
|
||||
}
|
||||
|
|
@ -1,60 +0,0 @@
|
|||
/* Pinos
|
||||
* Copyright (C) 2015 Wim Taymans <wim.taymans@gmail.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#ifndef __PINOS_GST_MANAGER_H__
|
||||
#define __PINOS_GST_MANAGER_H__
|
||||
|
||||
#include <glib-object.h>
|
||||
|
||||
#include <client/pinos.h>
|
||||
#include <server/daemon.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define PINOS_TYPE_GST_MANAGER (pinos_gst_manager_get_type ())
|
||||
#define PINOS_IS_GST_MANAGER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), PINOS_TYPE_GST_MANAGER))
|
||||
#define PINOS_IS_GST_MANAGER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), PINOS_TYPE_GST_MANAGER))
|
||||
#define PINOS_GST_MANAGER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), PINOS_TYPE_GST_MANAGER, PinosGstManagerClass))
|
||||
#define PINOS_GST_MANAGER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), PINOS_TYPE_GST_MANAGER, PinosGstManager))
|
||||
#define PINOS_GST_MANAGER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), PINOS_TYPE_GST_MANAGER, PinosGstManagerClass))
|
||||
#define PINOS_GST_MANAGER_CAST(obj) ((PinosGstManager*)(obj))
|
||||
#define PINOS_GST_MANAGER_CLASS_CAST(klass) ((PinosGstManagerClass*)(klass))
|
||||
|
||||
typedef struct _PinosGstManager PinosGstManager;
|
||||
typedef struct _PinosGstManagerClass PinosGstManagerClass;
|
||||
typedef struct _PinosGstManagerPrivate PinosGstManagerPrivate;
|
||||
|
||||
struct _PinosGstManager {
|
||||
GObject object;
|
||||
|
||||
PinosGstManagerPrivate *priv;
|
||||
};
|
||||
|
||||
struct _PinosGstManagerClass {
|
||||
GObjectClass parent_class;
|
||||
};
|
||||
|
||||
GType pinos_gst_manager_get_type (void);
|
||||
|
||||
PinosGstManager * pinos_gst_manager_new (PinosDaemon *daemon);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __PINOS_GST_MANAGER_H__ */
|
||||
|
||||
|
|
@ -1,564 +0,0 @@
|
|||
/* Pinos
|
||||
* Copyright (C) 2015 Wim Taymans <wim.taymans@gmail.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include <gst/gst.h>
|
||||
#include <gio/gio.h>
|
||||
|
||||
#include <gst/net/net.h>
|
||||
|
||||
#include "gst-source.h"
|
||||
|
||||
#define PINOS_GST_SOURCE_GET_PRIVATE(obj) \
|
||||
(G_TYPE_INSTANCE_GET_PRIVATE ((obj), PINOS_TYPE_GST_SOURCE, PinosGstSourcePrivate))
|
||||
|
||||
struct _PinosGstSourcePrivate
|
||||
{
|
||||
GstElement *pipeline;
|
||||
GstElement *element;
|
||||
GstElement *filter;
|
||||
GstElement *sink;
|
||||
|
||||
GstCaps *possible_formats;
|
||||
|
||||
GstNetTimeProvider *provider;
|
||||
|
||||
PinosProperties *props;
|
||||
|
||||
gint n_outputs;
|
||||
};
|
||||
|
||||
enum {
|
||||
PROP_0,
|
||||
PROP_ELEMENT,
|
||||
PROP_POSSIBLE_FORMATS
|
||||
};
|
||||
|
||||
G_DEFINE_TYPE (PinosGstSource, pinos_gst_source, PINOS_TYPE_SOURCE);
|
||||
|
||||
static gboolean
|
||||
bus_handler (GstBus *bus,
|
||||
GstMessage *message,
|
||||
gpointer user_data)
|
||||
{
|
||||
PinosSource *source = user_data;
|
||||
PinosGstSourcePrivate *priv = PINOS_GST_SOURCE (source)->priv;
|
||||
|
||||
switch (GST_MESSAGE_TYPE (message)) {
|
||||
case GST_MESSAGE_ERROR:
|
||||
{
|
||||
GError *error;
|
||||
gchar *debug;
|
||||
|
||||
gst_message_parse_error (message, &error, &debug);
|
||||
g_warning ("got error %s (%s)\n", error->message, debug);
|
||||
g_free (debug);
|
||||
|
||||
pinos_source_report_error (source, error);
|
||||
gst_element_set_state (priv->pipeline, GST_STATE_NULL);
|
||||
break;
|
||||
}
|
||||
case GST_MESSAGE_NEW_CLOCK:
|
||||
{
|
||||
GstClock *clock;
|
||||
PinosProperties *props;
|
||||
|
||||
gst_message_parse_new_clock (message, &clock);
|
||||
GST_INFO ("got new clock %s", GST_OBJECT_NAME (clock));
|
||||
|
||||
g_object_get (source, "properties", &props, NULL);
|
||||
pinos_properties_set (props, "gst.pipeline.clock", GST_OBJECT_NAME (clock));
|
||||
g_object_set (source, "properties", props, NULL);
|
||||
pinos_properties_free (props);
|
||||
break;
|
||||
}
|
||||
case GST_MESSAGE_CLOCK_LOST:
|
||||
{
|
||||
GstClock *clock;
|
||||
PinosProperties *props;
|
||||
|
||||
gst_message_parse_new_clock (message, &clock);
|
||||
GST_INFO ("clock lost %s", GST_OBJECT_NAME (clock));
|
||||
|
||||
g_object_get (source, "properties", &props, NULL);
|
||||
pinos_properties_remove (props, "gst.pipeline.clock");
|
||||
g_object_set (source, "properties", props, NULL);
|
||||
pinos_properties_free (props);
|
||||
|
||||
gst_element_set_state (priv->pipeline, GST_STATE_PAUSED);
|
||||
gst_element_set_state (priv->pipeline, GST_STATE_PLAYING);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
setup_pipeline (PinosGstSource *source, GError **error)
|
||||
{
|
||||
PinosGstSourcePrivate *priv = source->priv;
|
||||
GstBus *bus;
|
||||
GstElement *elem;
|
||||
|
||||
priv->pipeline = gst_pipeline_new (NULL);
|
||||
|
||||
gst_bin_add (GST_BIN (priv->pipeline), priv->element);
|
||||
|
||||
priv->filter = gst_element_factory_make ("capsfilter", NULL);
|
||||
gst_bin_add (GST_BIN (priv->pipeline), priv->filter);
|
||||
gst_element_link (priv->element, priv->filter);
|
||||
|
||||
elem = gst_element_factory_make ("pinospay", NULL);
|
||||
gst_bin_add (GST_BIN (priv->pipeline), elem);
|
||||
gst_element_link (priv->filter, elem);
|
||||
|
||||
priv->sink = gst_element_factory_make ("multisocketsink", NULL);
|
||||
g_object_set (priv->sink, "buffers-max", 2,
|
||||
"buffers-soft-max", 1,
|
||||
"recover-policy", 1, /* latest */
|
||||
"sync-method", 0, /* latest */
|
||||
"sync", TRUE,
|
||||
"enable-last-sample", FALSE,
|
||||
"send-dispatched", TRUE,
|
||||
"send-messages", TRUE,
|
||||
NULL);
|
||||
|
||||
gst_bin_add (GST_BIN (priv->pipeline), priv->sink);
|
||||
gst_element_link (elem, priv->sink);
|
||||
|
||||
bus = gst_pipeline_get_bus (GST_PIPELINE (priv->pipeline));
|
||||
gst_bus_add_watch (bus, bus_handler, source);
|
||||
gst_object_unref (bus);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
destroy_pipeline (PinosGstSource *source)
|
||||
{
|
||||
PinosGstSourcePrivate *priv = source->priv;
|
||||
|
||||
if (priv->pipeline) {
|
||||
gst_element_set_state (priv->pipeline, GST_STATE_NULL);
|
||||
gst_object_unref (priv->pipeline);
|
||||
priv->pipeline = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static gboolean
|
||||
start_pipeline (PinosGstSource *source, GError **error)
|
||||
{
|
||||
PinosGstSourcePrivate *priv = source->priv;
|
||||
GstCaps *res;
|
||||
GstQuery *query;
|
||||
GstClock *clock;
|
||||
gchar *address;
|
||||
gint port;
|
||||
GstStateChangeReturn ret;
|
||||
|
||||
ret = gst_element_set_state (priv->pipeline, GST_STATE_PAUSED);
|
||||
if (ret == GST_STATE_CHANGE_FAILURE)
|
||||
goto paused_failed;
|
||||
|
||||
ret = gst_element_get_state (priv->pipeline, NULL, NULL, -1);
|
||||
if (ret == GST_STATE_CHANGE_FAILURE)
|
||||
goto paused_failed;
|
||||
|
||||
query = gst_query_new_caps (NULL);
|
||||
gst_element_query (priv->element, query);
|
||||
gst_query_parse_caps_result (query, &res);
|
||||
priv->possible_formats = gst_caps_ref (res);
|
||||
gst_query_unref (query);
|
||||
|
||||
clock = gst_pipeline_get_clock (GST_PIPELINE (priv->pipeline));
|
||||
|
||||
if (priv->provider)
|
||||
g_object_unref (priv->provider);
|
||||
priv->provider = gst_net_time_provider_new (clock, NULL, 0);
|
||||
|
||||
g_object_get (priv->provider, "address", &address, "port", &port, NULL);
|
||||
|
||||
pinos_properties_set (priv->props, "pinos.clock.type", "gst.net.time.provider");
|
||||
pinos_properties_set (priv->props, "pinos.clock.source", GST_OBJECT_NAME (clock));
|
||||
pinos_properties_set (priv->props, "pinos.clock.address", address);
|
||||
pinos_properties_setf (priv->props, "pinos.clock.port", "%d", port);
|
||||
|
||||
g_free (address);
|
||||
gst_object_unref (clock);
|
||||
|
||||
return TRUE;
|
||||
|
||||
/* ERRORS */
|
||||
paused_failed:
|
||||
{
|
||||
GST_ERROR_OBJECT (source, "failed state change");
|
||||
gst_element_set_state (priv->pipeline, GST_STATE_NULL);
|
||||
if (error)
|
||||
*error = g_error_new (G_IO_ERROR,
|
||||
G_IO_ERROR_FAILED,
|
||||
"Failed to start pipeline");
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
static gboolean
|
||||
set_state (PinosSource *source,
|
||||
PinosSourceState state)
|
||||
{
|
||||
PinosGstSourcePrivate *priv = PINOS_GST_SOURCE (source)->priv;
|
||||
|
||||
switch (state) {
|
||||
case PINOS_SOURCE_STATE_SUSPENDED:
|
||||
gst_element_set_state (priv->pipeline, GST_STATE_NULL);
|
||||
break;
|
||||
|
||||
case PINOS_SOURCE_STATE_INITIALIZING:
|
||||
gst_element_set_state (priv->pipeline, GST_STATE_READY);
|
||||
break;
|
||||
|
||||
case PINOS_SOURCE_STATE_IDLE:
|
||||
gst_element_set_state (priv->pipeline, GST_STATE_PAUSED);
|
||||
break;
|
||||
|
||||
case PINOS_SOURCE_STATE_RUNNING:
|
||||
gst_element_set_state (priv->pipeline, GST_STATE_PLAYING);
|
||||
break;
|
||||
|
||||
case PINOS_SOURCE_STATE_ERROR:
|
||||
break;
|
||||
}
|
||||
pinos_source_update_state (source, state);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static GBytes *
|
||||
get_formats (PinosSource *source,
|
||||
GBytes *filter,
|
||||
GError **error)
|
||||
{
|
||||
PinosGstSourcePrivate *priv = PINOS_GST_SOURCE (source)->priv;
|
||||
GstCaps *caps, *cfilter;
|
||||
gchar *str;
|
||||
|
||||
if (filter) {
|
||||
cfilter = gst_caps_from_string (g_bytes_get_data (filter, NULL));
|
||||
if (cfilter == NULL)
|
||||
goto invalid_filter;
|
||||
|
||||
caps = gst_caps_intersect (priv->possible_formats, cfilter);
|
||||
gst_caps_unref (cfilter);
|
||||
|
||||
if (caps == NULL)
|
||||
goto no_formats;
|
||||
|
||||
} else {
|
||||
caps = gst_caps_ref (priv->possible_formats);
|
||||
}
|
||||
g_object_get (priv->filter, "caps", &cfilter, NULL);
|
||||
if (cfilter != NULL) {
|
||||
GstCaps *t = caps;
|
||||
|
||||
caps = gst_caps_intersect (t, cfilter);
|
||||
gst_caps_unref (cfilter);
|
||||
gst_caps_unref (t);
|
||||
}
|
||||
if (gst_caps_is_empty (caps)) {
|
||||
gst_caps_unref (caps);
|
||||
goto no_formats;
|
||||
}
|
||||
|
||||
str = gst_caps_to_string (caps);
|
||||
gst_caps_unref (caps);
|
||||
|
||||
return g_bytes_new_take (str, strlen (str) + 1);
|
||||
|
||||
invalid_filter:
|
||||
{
|
||||
if (error)
|
||||
*error = g_error_new (G_IO_ERROR,
|
||||
G_IO_ERROR_INVALID_ARGUMENT,
|
||||
"Invalid filter received");
|
||||
return NULL;
|
||||
}
|
||||
no_formats:
|
||||
{
|
||||
if (error)
|
||||
*error = g_error_new (G_IO_ERROR,
|
||||
G_IO_ERROR_NOT_FOUND,
|
||||
"No compatible format found");
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
on_socket_notify (GObject *gobject,
|
||||
GParamSpec *pspec,
|
||||
gpointer user_data)
|
||||
{
|
||||
PinosGstSource *source = user_data;
|
||||
PinosGstSourcePrivate *priv = source->priv;
|
||||
GSocket *socket;
|
||||
guint num_handles;
|
||||
GstCaps *caps;
|
||||
GBytes *requested_format, *format = NULL;
|
||||
gchar *str;
|
||||
|
||||
g_object_get (gobject, "socket", &socket, NULL);
|
||||
|
||||
if (socket == NULL) {
|
||||
GSocket *prev_socket = g_object_steal_data (gobject, "last-socket");
|
||||
if (prev_socket) {
|
||||
g_signal_emit_by_name (priv->sink, "remove", prev_socket);
|
||||
g_object_unref (prev_socket);
|
||||
}
|
||||
} else {
|
||||
pinos_source_report_busy (PINOS_SOURCE (source));
|
||||
g_signal_emit_by_name (priv->sink, "add", socket);
|
||||
g_object_set_data_full (gobject, "last-socket", socket, g_object_unref);
|
||||
}
|
||||
|
||||
g_object_get (priv->sink, "num-handles", &num_handles, NULL);
|
||||
if (num_handles == 0) {
|
||||
pinos_source_report_idle (PINOS_SOURCE (source));
|
||||
g_object_set (priv->filter, "caps", NULL, NULL);
|
||||
|
||||
str = gst_caps_to_string (priv->possible_formats);
|
||||
format = g_bytes_new_take (str, strlen (str) + 1);
|
||||
} else if (socket) {
|
||||
/* what client requested */
|
||||
g_object_get (gobject, "requested-format", &requested_format, NULL);
|
||||
g_assert (requested_format != NULL);
|
||||
|
||||
if (num_handles == 1) {
|
||||
/* first client, we set the requested format as the format */
|
||||
format = requested_format;
|
||||
|
||||
/* set on the filter */
|
||||
caps = gst_caps_from_string (g_bytes_get_data (format, NULL));
|
||||
g_assert (caps != NULL);
|
||||
g_object_set (priv->filter, "caps", caps, NULL);
|
||||
gst_caps_unref (caps);
|
||||
} else {
|
||||
/* we already have a client, format is whatever is configured already */
|
||||
g_bytes_unref (requested_format);
|
||||
|
||||
g_object_get (priv->filter, "caps", &caps, NULL);
|
||||
str = gst_caps_to_string (caps);
|
||||
format = g_bytes_new_take (str, strlen (str) + 1);
|
||||
gst_caps_unref (caps);
|
||||
}
|
||||
/* this is what we use as the final format for the output */
|
||||
g_object_set (gobject, "format", format, NULL);
|
||||
}
|
||||
if (format) {
|
||||
pinos_source_update_possible_formats (PINOS_SOURCE (source), format);
|
||||
g_bytes_unref (format);
|
||||
}
|
||||
}
|
||||
|
||||
static PinosSourceOutput *
|
||||
create_source_output (PinosSource *source,
|
||||
const gchar *client_path,
|
||||
GBytes *format_filter,
|
||||
PinosProperties *props,
|
||||
const gchar *prefix,
|
||||
GError **error)
|
||||
{
|
||||
PinosGstSource *s = PINOS_GST_SOURCE (source);
|
||||
PinosGstSourcePrivate *priv = s->priv;
|
||||
PinosSourceOutput *output;
|
||||
gpointer state = NULL;
|
||||
const char *key, *val;
|
||||
|
||||
if (priv->n_outputs == 0) {
|
||||
if (!start_pipeline (s, error))
|
||||
return NULL;
|
||||
}
|
||||
|
||||
while ((key = pinos_properties_iterate (priv->props, &state))) {
|
||||
val = pinos_properties_get (priv->props, key);
|
||||
pinos_properties_set (props, key, val);
|
||||
}
|
||||
|
||||
output = PINOS_SOURCE_CLASS (pinos_gst_source_parent_class)
|
||||
->create_source_output (source,
|
||||
client_path,
|
||||
format_filter,
|
||||
props,
|
||||
prefix,
|
||||
error);
|
||||
if (output == NULL)
|
||||
return NULL;
|
||||
|
||||
g_signal_connect (output,
|
||||
"notify::socket",
|
||||
(GCallback) on_socket_notify,
|
||||
source);
|
||||
|
||||
priv->n_outputs++;
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
release_source_output (PinosSource *source,
|
||||
PinosSourceOutput *output)
|
||||
{
|
||||
return PINOS_SOURCE_CLASS (pinos_gst_source_parent_class)
|
||||
->release_source_output (source, output);
|
||||
}
|
||||
|
||||
static void
|
||||
get_property (GObject *object,
|
||||
guint prop_id,
|
||||
GValue *value,
|
||||
GParamSpec *pspec)
|
||||
{
|
||||
PinosGstSource *source = PINOS_GST_SOURCE (object);
|
||||
PinosGstSourcePrivate *priv = source->priv;
|
||||
|
||||
switch (prop_id) {
|
||||
case PROP_ELEMENT:
|
||||
g_value_set_object (value, priv->element);
|
||||
break;
|
||||
|
||||
case PROP_POSSIBLE_FORMATS:
|
||||
g_value_set_boxed (value, priv->possible_formats);
|
||||
break;
|
||||
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
set_property (GObject *object,
|
||||
guint prop_id,
|
||||
const GValue *value,
|
||||
GParamSpec *pspec)
|
||||
{
|
||||
PinosGstSource *source = PINOS_GST_SOURCE (object);
|
||||
PinosGstSourcePrivate *priv = source->priv;
|
||||
|
||||
switch (prop_id) {
|
||||
case PROP_ELEMENT:
|
||||
priv->element = g_value_dup_object (value);
|
||||
break;
|
||||
|
||||
case PROP_POSSIBLE_FORMATS:
|
||||
priv->possible_formats = g_value_dup_boxed (value);
|
||||
break;
|
||||
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
source_constructed (GObject * object)
|
||||
{
|
||||
PinosGstSource *source = PINOS_GST_SOURCE (object);
|
||||
|
||||
setup_pipeline (source, NULL);
|
||||
|
||||
G_OBJECT_CLASS (pinos_gst_source_parent_class)->constructed (object);
|
||||
}
|
||||
|
||||
static void
|
||||
source_finalize (GObject * object)
|
||||
{
|
||||
PinosGstSource *source = PINOS_GST_SOURCE (object);
|
||||
PinosGstSourcePrivate *priv = source->priv;
|
||||
|
||||
destroy_pipeline (source);
|
||||
pinos_properties_free (priv->props);
|
||||
|
||||
G_OBJECT_CLASS (pinos_gst_source_parent_class)->finalize (object);
|
||||
}
|
||||
|
||||
static void
|
||||
pinos_gst_source_class_init (PinosGstSourceClass * klass)
|
||||
{
|
||||
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
|
||||
PinosSourceClass *source_class = PINOS_SOURCE_CLASS (klass);
|
||||
|
||||
g_type_class_add_private (klass, sizeof (PinosGstSourcePrivate));
|
||||
|
||||
gobject_class->constructed = source_constructed;
|
||||
gobject_class->finalize = source_finalize;
|
||||
gobject_class->get_property = get_property;
|
||||
gobject_class->set_property = set_property;
|
||||
|
||||
g_object_class_install_property (gobject_class,
|
||||
PROP_ELEMENT,
|
||||
g_param_spec_object ("element",
|
||||
"Element",
|
||||
"The element",
|
||||
GST_TYPE_ELEMENT,
|
||||
G_PARAM_READWRITE |
|
||||
G_PARAM_CONSTRUCT_ONLY |
|
||||
G_PARAM_STATIC_STRINGS));
|
||||
g_object_class_install_property (gobject_class,
|
||||
PROP_POSSIBLE_FORMATS,
|
||||
g_param_spec_boxed ("possible-formats",
|
||||
"Possible Formats",
|
||||
"The possible formats",
|
||||
GST_TYPE_CAPS,
|
||||
G_PARAM_READWRITE |
|
||||
G_PARAM_CONSTRUCT_ONLY |
|
||||
G_PARAM_STATIC_STRINGS));
|
||||
|
||||
source_class->get_formats = get_formats;
|
||||
source_class->set_state = set_state;
|
||||
source_class->create_source_output = create_source_output;
|
||||
source_class->release_source_output = release_source_output;
|
||||
}
|
||||
|
||||
static void
|
||||
pinos_gst_source_init (PinosGstSource * source)
|
||||
{
|
||||
PinosGstSourcePrivate *priv;
|
||||
|
||||
priv = source->priv = PINOS_GST_SOURCE_GET_PRIVATE (source);
|
||||
priv->props = pinos_properties_new (NULL, NULL);
|
||||
}
|
||||
|
||||
PinosSource *
|
||||
pinos_gst_source_new (PinosDaemon *daemon,
|
||||
const gchar *name,
|
||||
PinosProperties *properties,
|
||||
GstElement *element,
|
||||
GstCaps *caps)
|
||||
{
|
||||
PinosSource *source;
|
||||
|
||||
source = g_object_new (PINOS_TYPE_GST_SOURCE,
|
||||
"daemon", daemon,
|
||||
"name", name,
|
||||
"properties", properties,
|
||||
"element", element,
|
||||
"possible-formats", caps,
|
||||
NULL);
|
||||
|
||||
return source;
|
||||
}
|
||||
|
|
@ -1,65 +0,0 @@
|
|||
/* Pinos
|
||||
* Copyright (C) 2015 Wim Taymans <wim.taymans@gmail.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#ifndef __PINOS_GST_SOURCE_H__
|
||||
#define __PINOS_GST_SOURCE_H__
|
||||
|
||||
#include <glib-object.h>
|
||||
|
||||
#include <client/pinos.h>
|
||||
#include <server/daemon.h>
|
||||
#include <server/source.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define PINOS_TYPE_GST_SOURCE (pinos_gst_source_get_type ())
|
||||
#define PINOS_IS_GST_SOURCE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), PINOS_TYPE_GST_SOURCE))
|
||||
#define PINOS_IS_GST_SOURCE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), PINOS_TYPE_GST_SOURCE))
|
||||
#define PINOS_GST_SOURCE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), PINOS_TYPE_GST_SOURCE, PinosGstSourceClass))
|
||||
#define PINOS_GST_SOURCE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), PINOS_TYPE_GST_SOURCE, PinosGstSource))
|
||||
#define PINOS_GST_SOURCE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), PINOS_TYPE_GST_SOURCE, PinosGstSourceClass))
|
||||
#define PINOS_GST_SOURCE_CAST(obj) ((PinosGstSource*)(obj))
|
||||
#define PINOS_GST_SOURCE_CLASS_CAST(klass) ((PinosGstSourceClass*)(klass))
|
||||
|
||||
typedef struct _PinosGstSource PinosGstSource;
|
||||
typedef struct _PinosGstSourceClass PinosGstSourceClass;
|
||||
typedef struct _PinosGstSourcePrivate PinosGstSourcePrivate;
|
||||
|
||||
struct _PinosGstSource {
|
||||
PinosSource object;
|
||||
|
||||
PinosGstSourcePrivate *priv;
|
||||
};
|
||||
|
||||
struct _PinosGstSourceClass {
|
||||
PinosSourceClass parent_class;
|
||||
};
|
||||
|
||||
GType pinos_gst_source_get_type (void);
|
||||
|
||||
PinosSource * pinos_gst_source_new (PinosDaemon *daemon,
|
||||
const gchar *name,
|
||||
PinosProperties *properties,
|
||||
GstElement *element,
|
||||
GstCaps *caps);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __PINOS_GST_SOURCE_H__ */
|
||||
|
||||
|
|
@ -1,454 +0,0 @@
|
|||
/* Pinos
|
||||
* Copyright (C) 2015 Wim Taymans <wim.taymans@gmail.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include <gst/gst.h>
|
||||
#include <gio/gio.h>
|
||||
|
||||
#include <server/daemon.h>
|
||||
#include <server/client-source.h>
|
||||
|
||||
#define PINOS_CLIENT_SOURCE_GET_PRIVATE(obj) \
|
||||
(G_TYPE_INSTANCE_GET_PRIVATE ((obj), PINOS_TYPE_CLIENT_SOURCE, PinosClientSourcePrivate))
|
||||
|
||||
struct _PinosClientSourcePrivate
|
||||
{
|
||||
GstElement *pipeline;
|
||||
GstElement *src;
|
||||
GstElement *sink;
|
||||
guint id;
|
||||
|
||||
GstCaps *format;
|
||||
GSocket *socket;
|
||||
|
||||
GBytes *possible_formats;
|
||||
|
||||
PinosSourceOutput *input;
|
||||
};
|
||||
|
||||
G_DEFINE_TYPE (PinosClientSource, pinos_client_source, PINOS_TYPE_SOURCE);
|
||||
|
||||
enum
|
||||
{
|
||||
PROP_0,
|
||||
PROP_POSSIBLE_FORMATS
|
||||
};
|
||||
|
||||
static void
|
||||
client_source_get_property (GObject *_object,
|
||||
guint prop_id,
|
||||
GValue *value,
|
||||
GParamSpec *pspec)
|
||||
{
|
||||
PinosClientSource *source = PINOS_CLIENT_SOURCE (_object);
|
||||
PinosClientSourcePrivate *priv = source->priv;
|
||||
|
||||
switch (prop_id) {
|
||||
case PROP_POSSIBLE_FORMATS:
|
||||
g_value_set_boxed (value, priv->possible_formats);
|
||||
break;
|
||||
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (source, prop_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
client_source_set_property (GObject *_object,
|
||||
guint prop_id,
|
||||
const GValue *value,
|
||||
GParamSpec *pspec)
|
||||
{
|
||||
PinosClientSource *source = PINOS_CLIENT_SOURCE (_object);
|
||||
PinosClientSourcePrivate *priv = source->priv;
|
||||
|
||||
switch (prop_id) {
|
||||
case PROP_POSSIBLE_FORMATS:
|
||||
if (priv->possible_formats)
|
||||
g_bytes_unref (priv->possible_formats);
|
||||
priv->possible_formats = g_value_dup_boxed (value);
|
||||
pinos_source_update_possible_formats (PINOS_SOURCE (source),
|
||||
priv->possible_formats);
|
||||
break;
|
||||
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (source, prop_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static gboolean
|
||||
bus_handler (GstBus *bus,
|
||||
GstMessage *message,
|
||||
gpointer user_data)
|
||||
{
|
||||
PinosSource *source = user_data;
|
||||
PinosClientSourcePrivate *priv = PINOS_CLIENT_SOURCE (source)->priv;
|
||||
|
||||
switch (GST_MESSAGE_TYPE (message)) {
|
||||
case GST_MESSAGE_ERROR:
|
||||
{
|
||||
GError *error;
|
||||
gchar *debug;
|
||||
|
||||
gst_message_parse_error (message, &error, &debug);
|
||||
g_warning ("got error %s (%s)\n", error->message, debug);
|
||||
g_free (debug);
|
||||
|
||||
pinos_source_report_error (source, error);
|
||||
gst_element_set_state (priv->pipeline, GST_STATE_NULL);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
setup_pipeline (PinosClientSource *source)
|
||||
{
|
||||
PinosClientSourcePrivate *priv = source->priv;
|
||||
GstBus *bus;
|
||||
|
||||
priv->pipeline = gst_parse_launch ("socketsrc "
|
||||
"name=src "
|
||||
"caps=application/x-pinos "
|
||||
"send-messages=true ! "
|
||||
"pinospay ! "
|
||||
"multisocketsink "
|
||||
"buffers-max=2 "
|
||||
"buffers-soft-max=1 "
|
||||
"recover-policy=latest "
|
||||
"sync-method=latest "
|
||||
"name=sink "
|
||||
"sync=true "
|
||||
"enable-last-sample=false "
|
||||
"send-messages=true "
|
||||
"send-dispatched=true",
|
||||
NULL);
|
||||
priv->sink = gst_bin_get_by_name (GST_BIN (priv->pipeline), "sink");
|
||||
priv->src = gst_bin_get_by_name (GST_BIN (priv->pipeline), "src");
|
||||
|
||||
bus = gst_pipeline_get_bus (GST_PIPELINE (priv->pipeline));
|
||||
priv->id = gst_bus_add_watch (bus, bus_handler, source);
|
||||
gst_object_unref (bus);
|
||||
}
|
||||
|
||||
static GstCaps *
|
||||
collect_caps (PinosSource *source,
|
||||
GstCaps *filter)
|
||||
{
|
||||
PinosClientSourcePrivate *priv = PINOS_CLIENT_SOURCE (source)->priv;
|
||||
|
||||
if (priv->format)
|
||||
return gst_caps_ref (priv->format);
|
||||
else
|
||||
return gst_caps_new_any ();
|
||||
}
|
||||
|
||||
static gboolean
|
||||
client_set_state (PinosSource *source,
|
||||
PinosSourceState state)
|
||||
{
|
||||
PinosClientSourcePrivate *priv = PINOS_CLIENT_SOURCE (source)->priv;
|
||||
|
||||
switch (state) {
|
||||
case PINOS_SOURCE_STATE_SUSPENDED:
|
||||
gst_element_set_state (priv->pipeline, GST_STATE_NULL);
|
||||
break;
|
||||
|
||||
case PINOS_SOURCE_STATE_INITIALIZING:
|
||||
gst_element_set_state (priv->pipeline, GST_STATE_READY);
|
||||
break;
|
||||
|
||||
case PINOS_SOURCE_STATE_IDLE:
|
||||
gst_element_set_state (priv->pipeline, GST_STATE_PAUSED);
|
||||
break;
|
||||
|
||||
case PINOS_SOURCE_STATE_RUNNING:
|
||||
gst_element_set_state (priv->pipeline, GST_STATE_PLAYING);
|
||||
break;
|
||||
|
||||
case PINOS_SOURCE_STATE_ERROR:
|
||||
break;
|
||||
}
|
||||
pinos_source_update_state (source, state);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static GBytes *
|
||||
client_get_formats (PinosSource *source,
|
||||
GBytes *filter,
|
||||
GError **error)
|
||||
{
|
||||
GstCaps *caps, *cfilter;
|
||||
gchar *str;
|
||||
|
||||
if (filter) {
|
||||
cfilter = gst_caps_from_string (g_bytes_get_data (filter, NULL));
|
||||
if (cfilter == NULL)
|
||||
goto invalid_filter;
|
||||
} else {
|
||||
cfilter = NULL;
|
||||
}
|
||||
|
||||
caps = collect_caps (source, cfilter);
|
||||
if (caps == NULL)
|
||||
goto no_format;
|
||||
|
||||
str = gst_caps_to_string (caps);
|
||||
|
||||
return g_bytes_new_take (str, strlen (str) + 1);
|
||||
|
||||
invalid_filter:
|
||||
{
|
||||
if (error)
|
||||
*error = g_error_new (G_IO_ERROR,
|
||||
G_IO_ERROR_INVALID_ARGUMENT,
|
||||
"Invalid filter received");
|
||||
return NULL;
|
||||
}
|
||||
no_format:
|
||||
{
|
||||
if (error)
|
||||
*error = g_error_new (G_IO_ERROR,
|
||||
G_IO_ERROR_NOT_FOUND,
|
||||
"No compatible format found");
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
on_socket_notify (GObject *gobject,
|
||||
GParamSpec *pspec,
|
||||
gpointer user_data)
|
||||
{
|
||||
PinosClientSource *source = user_data;
|
||||
PinosClientSourcePrivate *priv = source->priv;
|
||||
GSocket *socket;
|
||||
guint num_handles;
|
||||
|
||||
g_object_get (gobject, "socket", &socket, NULL);
|
||||
|
||||
if (socket == NULL) {
|
||||
GSocket *prev_socket = g_object_steal_data (gobject, "last-socket");
|
||||
if (prev_socket) {
|
||||
g_signal_emit_by_name (priv->sink, "remove", prev_socket);
|
||||
g_object_unref (prev_socket);
|
||||
}
|
||||
} else {
|
||||
g_signal_emit_by_name (priv->sink, "add", socket);
|
||||
g_object_set_data_full (gobject, "last-socket", socket, g_object_unref);
|
||||
}
|
||||
|
||||
g_object_get (priv->sink, "num-handles", &num_handles, NULL);
|
||||
if (num_handles > 0 && socket) {
|
||||
GBytes *format;
|
||||
|
||||
/* suggest what we provide */
|
||||
g_object_get (priv->input, "format", &format, NULL);
|
||||
g_object_set (gobject, "format", format, NULL);
|
||||
g_bytes_unref (format);
|
||||
}
|
||||
}
|
||||
|
||||
static PinosSourceOutput *
|
||||
client_create_source_output (PinosSource *source,
|
||||
const gchar *client_path,
|
||||
GBytes *format_filter,
|
||||
PinosProperties *props,
|
||||
const gchar *prefix,
|
||||
GError **error)
|
||||
{
|
||||
PinosClientSourcePrivate *priv = PINOS_CLIENT_SOURCE (source)->priv;
|
||||
PinosSourceOutput *output;
|
||||
|
||||
/* propose format of input */
|
||||
g_object_get (priv->input, "format", &format_filter, NULL);
|
||||
|
||||
output = PINOS_SOURCE_CLASS (pinos_client_source_parent_class)
|
||||
->create_source_output (source,
|
||||
client_path,
|
||||
format_filter,
|
||||
props,
|
||||
prefix,
|
||||
error);
|
||||
|
||||
if (output == NULL)
|
||||
return NULL;
|
||||
|
||||
g_signal_connect (output, "notify::socket", (GCallback) on_socket_notify, source);
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
client_release_source_output (PinosSource *source,
|
||||
PinosSourceOutput *output)
|
||||
{
|
||||
return PINOS_SOURCE_CLASS (pinos_client_source_parent_class)->release_source_output (source, output);
|
||||
}
|
||||
|
||||
static void
|
||||
client_source_dispose (GObject * object)
|
||||
{
|
||||
PinosClientSourcePrivate *priv = PINOS_CLIENT_SOURCE (object)->priv;
|
||||
|
||||
g_source_remove (priv->id);
|
||||
gst_element_set_state (priv->pipeline, GST_STATE_NULL);
|
||||
|
||||
G_OBJECT_CLASS (pinos_client_source_parent_class)->dispose (object);
|
||||
}
|
||||
|
||||
static void
|
||||
client_source_finalize (GObject * object)
|
||||
{
|
||||
PinosClientSourcePrivate *priv = PINOS_CLIENT_SOURCE (object)->priv;
|
||||
|
||||
g_clear_object (&priv->input);
|
||||
g_clear_object (&priv->sink);
|
||||
g_clear_object (&priv->src);
|
||||
g_clear_object (&priv->pipeline);
|
||||
|
||||
gst_caps_replace (&priv->format, NULL);
|
||||
|
||||
G_OBJECT_CLASS (pinos_client_source_parent_class)->finalize (object);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
on_input_socket_notify (GObject *gobject,
|
||||
GParamSpec *pspec,
|
||||
gpointer user_data)
|
||||
{
|
||||
PinosClientSource *source = user_data;
|
||||
PinosClientSourcePrivate *priv = source->priv;
|
||||
GSocket *socket;
|
||||
GBytes *requested_format;
|
||||
GstCaps *caps;
|
||||
|
||||
g_object_get (gobject, "socket", &socket, NULL);
|
||||
|
||||
if (socket) {
|
||||
/* requested format is final format */
|
||||
g_object_get (gobject, "requested-format", &requested_format, NULL);
|
||||
g_assert (requested_format != NULL);
|
||||
g_object_set (gobject, "format", requested_format, NULL);
|
||||
|
||||
/* and set as the current format */
|
||||
caps = gst_caps_from_string (g_bytes_get_data (requested_format, NULL));
|
||||
g_assert (caps != NULL);
|
||||
gst_caps_replace (&priv->format, caps);
|
||||
g_bytes_unref (requested_format);
|
||||
} else {
|
||||
gst_caps_replace (&priv->format, NULL);
|
||||
}
|
||||
g_object_set (priv->src, "socket", socket, NULL);
|
||||
|
||||
if (socket)
|
||||
gst_element_set_state (priv->pipeline, GST_STATE_PLAYING);
|
||||
else
|
||||
gst_element_set_state (priv->pipeline, GST_STATE_READY);
|
||||
}
|
||||
|
||||
PinosSourceOutput *
|
||||
pinos_client_source_get_source_input (PinosClientSource *source,
|
||||
const gchar *client_path,
|
||||
GBytes *format_filter,
|
||||
PinosProperties *props,
|
||||
const gchar *prefix,
|
||||
GError **error)
|
||||
{
|
||||
PinosClientSourcePrivate *priv;
|
||||
|
||||
g_return_val_if_fail (PINOS_IS_CLIENT_SOURCE (source), NULL);
|
||||
priv = source->priv;
|
||||
|
||||
if (priv->input == NULL) {
|
||||
GstCaps *caps = gst_caps_from_string (g_bytes_get_data (format_filter, NULL));
|
||||
|
||||
gst_caps_replace (&priv->format, caps);
|
||||
|
||||
priv->input = PINOS_SOURCE_CLASS (pinos_client_source_parent_class)
|
||||
->create_source_output (PINOS_SOURCE (source),
|
||||
client_path,
|
||||
format_filter,
|
||||
props,
|
||||
prefix,
|
||||
error);
|
||||
if (priv->input == NULL)
|
||||
return NULL;
|
||||
|
||||
g_signal_connect (priv->input, "notify::socket", (GCallback) on_input_socket_notify, source);
|
||||
}
|
||||
return g_object_ref (priv->input);
|
||||
}
|
||||
|
||||
static void
|
||||
pinos_client_source_class_init (PinosClientSourceClass * klass)
|
||||
{
|
||||
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
|
||||
PinosSourceClass *source_class = PINOS_SOURCE_CLASS (klass);
|
||||
|
||||
g_type_class_add_private (klass, sizeof (PinosClientSourcePrivate));
|
||||
|
||||
gobject_class->dispose = client_source_dispose;
|
||||
gobject_class->finalize = client_source_finalize;
|
||||
|
||||
gobject_class->get_property = client_source_get_property;
|
||||
gobject_class->set_property = client_source_set_property;
|
||||
|
||||
g_object_class_install_property (gobject_class,
|
||||
PROP_POSSIBLE_FORMATS,
|
||||
g_param_spec_boxed ("possible-formats",
|
||||
"Possible Format",
|
||||
"The possible formats of the stream",
|
||||
G_TYPE_BYTES,
|
||||
G_PARAM_READWRITE |
|
||||
G_PARAM_CONSTRUCT |
|
||||
G_PARAM_STATIC_STRINGS));
|
||||
|
||||
source_class->get_formats = client_get_formats;
|
||||
source_class->set_state = client_set_state;
|
||||
source_class->create_source_output = client_create_source_output;
|
||||
source_class->release_source_output = client_release_source_output;
|
||||
}
|
||||
|
||||
static void
|
||||
pinos_client_source_init (PinosClientSource * source)
|
||||
{
|
||||
source->priv = PINOS_CLIENT_SOURCE_GET_PRIVATE (source);
|
||||
|
||||
setup_pipeline (source);
|
||||
}
|
||||
|
||||
PinosSource *
|
||||
pinos_client_source_new (PinosDaemon *daemon,
|
||||
GBytes *possible_formats)
|
||||
{
|
||||
return g_object_new (PINOS_TYPE_CLIENT_SOURCE,
|
||||
"daemon", daemon,
|
||||
"name", "client-source",
|
||||
"possible-formats", possible_formats,
|
||||
NULL);
|
||||
}
|
||||
|
|
@ -1,78 +0,0 @@
|
|||
/* Pinos
|
||||
* Copyright (C) 2015 Wim Taymans <wim.taymans@gmail.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#ifndef __PINOS_CLIENT_SOURCE_H__
|
||||
#define __PINOS_CLIENT_SOURCE_H__
|
||||
|
||||
#include <glib-object.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
typedef struct _PinosClientSource PinosClientSource;
|
||||
typedef struct _PinosClientSourceClass PinosClientSourceClass;
|
||||
typedef struct _PinosClientSourcePrivate PinosClientSourcePrivate;
|
||||
|
||||
#include <server/source.h>
|
||||
|
||||
#define PINOS_TYPE_CLIENT_SOURCE (pinos_client_source_get_type ())
|
||||
#define PINOS_IS_CLIENT_SOURCE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), PINOS_TYPE_SOURCE))
|
||||
#define PINOS_IS_CLIENT_SOURCE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), PINOS_TYPE_SOURCE))
|
||||
#define PINOS_CLIENT_SOURCE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), PINOS_TYPE_SOURCE, PinosClientSourceClass))
|
||||
#define PINOS_CLIENT_SOURCE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), PINOS_TYPE_SOURCE, PinosClientSource))
|
||||
#define PINOS_CLIENT_SOURCE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), PINOS_TYPE_SOURCE, PinosClientSourceClass))
|
||||
#define PINOS_CLIENT_SOURCE_CAST(obj) ((PinosClientSource*)(obj))
|
||||
#define PINOS_CLIENT_SOURCE_CLASS_CAST(klass) ((PinosClientSourceClass*)(klass))
|
||||
|
||||
/**
|
||||
* PinosClientSource:
|
||||
*
|
||||
* Pinos client source object class.
|
||||
*/
|
||||
struct _PinosClientSource {
|
||||
PinosSource object;
|
||||
|
||||
PinosClientSourcePrivate *priv;
|
||||
};
|
||||
|
||||
/**
|
||||
* PinosClientSourceClass:
|
||||
*
|
||||
* Pinos client source object class.
|
||||
*/
|
||||
struct _PinosClientSourceClass {
|
||||
PinosSourceClass parent_class;
|
||||
};
|
||||
|
||||
/* normal GObject stuff */
|
||||
GType pinos_client_source_get_type (void);
|
||||
|
||||
PinosSource * pinos_client_source_new (PinosDaemon *daemon,
|
||||
GBytes *possible_formats);
|
||||
|
||||
PinosSourceOutput * pinos_client_source_get_source_input (PinosClientSource *source,
|
||||
const gchar *client_path,
|
||||
GBytes *format_filter,
|
||||
PinosProperties *props,
|
||||
const gchar *prefix,
|
||||
GError **error);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __PINOS_CLIENT_SOURCE_H__ */
|
||||
|
||||
|
|
@ -1,534 +0,0 @@
|
|||
/* Pinos
|
||||
* Copyright (C) 2015 Wim Taymans <wim.taymans@gmail.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include "client/pinos.h"
|
||||
|
||||
#include "client/enumtypes.h"
|
||||
|
||||
#include "server/client.h"
|
||||
#include "server/client-source.h"
|
||||
|
||||
#include "dbus/org-pinos.h"
|
||||
|
||||
struct _PinosClientPrivate
|
||||
{
|
||||
PinosDaemon *daemon;
|
||||
gchar *sender;
|
||||
gchar *object_path;
|
||||
PinosProperties *properties;
|
||||
|
||||
PinosClient1 *client1;
|
||||
|
||||
GList *outputs;
|
||||
};
|
||||
|
||||
#define PINOS_CLIENT_GET_PRIVATE(obj) \
|
||||
(G_TYPE_INSTANCE_GET_PRIVATE ((obj), PINOS_TYPE_CLIENT, PinosClientPrivate))
|
||||
|
||||
G_DEFINE_TYPE (PinosClient, pinos_client, G_TYPE_OBJECT);
|
||||
|
||||
enum
|
||||
{
|
||||
PROP_0,
|
||||
PROP_DAEMON,
|
||||
PROP_SENDER,
|
||||
PROP_OBJECT_PATH,
|
||||
PROP_PROPERTIES,
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
SIGNAL_DISCONNECT,
|
||||
LAST_SIGNAL
|
||||
};
|
||||
|
||||
static guint signals[LAST_SIGNAL] = { 0 };
|
||||
|
||||
static void
|
||||
pinos_client_get_property (GObject *_object,
|
||||
guint prop_id,
|
||||
GValue *value,
|
||||
GParamSpec *pspec)
|
||||
{
|
||||
PinosClient *client = PINOS_CLIENT (_object);
|
||||
PinosClientPrivate *priv = client->priv;
|
||||
|
||||
switch (prop_id) {
|
||||
case PROP_DAEMON:
|
||||
g_value_set_object (value, priv->daemon);
|
||||
break;
|
||||
|
||||
case PROP_SENDER:
|
||||
g_value_set_string (value, priv->sender);
|
||||
break;
|
||||
|
||||
case PROP_OBJECT_PATH:
|
||||
g_value_set_string (value, priv->object_path);
|
||||
break;
|
||||
|
||||
case PROP_PROPERTIES:
|
||||
g_value_set_boxed (value, priv->properties);
|
||||
break;
|
||||
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (client, prop_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
pinos_client_set_property (GObject *_object,
|
||||
guint prop_id,
|
||||
const GValue *value,
|
||||
GParamSpec *pspec)
|
||||
{
|
||||
PinosClient *client = PINOS_CLIENT (_object);
|
||||
PinosClientPrivate *priv = client->priv;
|
||||
|
||||
switch (prop_id) {
|
||||
case PROP_DAEMON:
|
||||
priv->daemon = g_value_dup_object (value);
|
||||
break;
|
||||
|
||||
case PROP_SENDER:
|
||||
priv->sender = g_value_dup_string (value);
|
||||
break;
|
||||
|
||||
case PROP_OBJECT_PATH:
|
||||
priv->object_path = g_value_dup_string (value);
|
||||
break;
|
||||
|
||||
case PROP_PROPERTIES:
|
||||
if (priv->properties)
|
||||
pinos_properties_free (priv->properties);
|
||||
priv->properties = g_value_dup_boxed (value);
|
||||
break;
|
||||
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (client, prop_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
handle_remove_source_output (PinosSourceOutput *output,
|
||||
gpointer user_data)
|
||||
{
|
||||
PinosClient *client = user_data;
|
||||
PinosClientPrivate *priv = client->priv;
|
||||
|
||||
priv->outputs = g_list_remove (priv->outputs, output);
|
||||
g_object_unref (output);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
handle_create_source_output (PinosClient1 *interface,
|
||||
GDBusMethodInvocation *invocation,
|
||||
const gchar *arg_source,
|
||||
const gchar *arg_accepted_formats,
|
||||
GVariant *arg_properties,
|
||||
gpointer user_data)
|
||||
{
|
||||
PinosClient *client = user_data;
|
||||
PinosClientPrivate *priv = client->priv;
|
||||
PinosSource *source;
|
||||
PinosSourceOutput *output;
|
||||
const gchar *object_path, *sender;
|
||||
GBytes *formats;
|
||||
PinosProperties *props;
|
||||
GError *error = NULL;
|
||||
|
||||
sender = g_dbus_method_invocation_get_sender (invocation);
|
||||
if (g_strcmp0 (pinos_client_get_sender (client), sender) != 0)
|
||||
goto not_allowed;
|
||||
|
||||
formats = g_bytes_new (arg_accepted_formats, strlen (arg_accepted_formats) + 1);
|
||||
props = pinos_properties_from_variant (arg_properties);
|
||||
|
||||
source = pinos_daemon_find_source (priv->daemon,
|
||||
arg_source,
|
||||
props,
|
||||
formats,
|
||||
&error);
|
||||
if (source == NULL)
|
||||
goto no_source;
|
||||
|
||||
output = pinos_source_create_source_output (source,
|
||||
priv->object_path,
|
||||
formats,
|
||||
props,
|
||||
priv->object_path,
|
||||
&error);
|
||||
if (output == NULL)
|
||||
goto no_output;
|
||||
|
||||
priv->outputs = g_list_prepend (priv->outputs, output);
|
||||
|
||||
g_signal_connect (output,
|
||||
"remove",
|
||||
(GCallback) handle_remove_source_output,
|
||||
client);
|
||||
|
||||
object_path = pinos_source_output_get_object_path (output);
|
||||
g_dbus_method_invocation_return_value (invocation,
|
||||
g_variant_new ("(o)", object_path));
|
||||
|
||||
return TRUE;
|
||||
|
||||
/* ERRORS */
|
||||
not_allowed:
|
||||
{
|
||||
g_dbus_method_invocation_return_dbus_error (invocation,
|
||||
"org.pinos.Error", "not client owner");
|
||||
return TRUE;
|
||||
}
|
||||
no_source:
|
||||
{
|
||||
g_dbus_method_invocation_return_gerror (invocation, error);
|
||||
g_clear_error (&error);
|
||||
g_bytes_unref (formats);
|
||||
return TRUE;
|
||||
}
|
||||
no_output:
|
||||
{
|
||||
g_dbus_method_invocation_return_gerror (invocation, error);
|
||||
g_clear_error (&error);
|
||||
g_bytes_unref (formats);
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
static gboolean
|
||||
handle_create_source_input (PinosClient1 *interface,
|
||||
GDBusMethodInvocation *invocation,
|
||||
const gchar *arg_possible_formats,
|
||||
GVariant *arg_properties,
|
||||
gpointer user_data)
|
||||
{
|
||||
PinosClient *client = user_data;
|
||||
PinosClientPrivate *priv = client->priv;
|
||||
PinosSource *source;
|
||||
PinosSourceOutput *input;
|
||||
const gchar *source_input_path, *sender;
|
||||
GBytes *formats;
|
||||
GError *error = NULL;
|
||||
PinosProperties *props;
|
||||
|
||||
sender = g_dbus_method_invocation_get_sender (invocation);
|
||||
if (g_strcmp0 (pinos_client_get_sender (client), sender) != 0)
|
||||
goto not_allowed;
|
||||
|
||||
formats = g_bytes_new (arg_possible_formats, strlen (arg_possible_formats) + 1);
|
||||
props = pinos_properties_from_variant (arg_properties);
|
||||
|
||||
source = pinos_client_source_new (priv->daemon, formats);
|
||||
if (source == NULL)
|
||||
goto no_source;
|
||||
|
||||
g_object_set_data_full (G_OBJECT (client),
|
||||
pinos_source_get_object_path (PINOS_SOURCE (source)),
|
||||
source,
|
||||
g_object_unref);
|
||||
|
||||
sender = g_dbus_method_invocation_get_sender (invocation);
|
||||
|
||||
input = pinos_client_source_get_source_input (PINOS_CLIENT_SOURCE (source),
|
||||
priv->object_path,
|
||||
formats,
|
||||
props,
|
||||
priv->object_path,
|
||||
&error);
|
||||
if (input == NULL)
|
||||
goto no_input;
|
||||
|
||||
source_input_path = pinos_source_output_get_object_path (input);
|
||||
|
||||
priv->outputs = g_list_prepend (priv->outputs, input);
|
||||
|
||||
g_signal_connect (input,
|
||||
"remove",
|
||||
(GCallback) handle_remove_source_output,
|
||||
client);
|
||||
|
||||
g_dbus_method_invocation_return_value (invocation,
|
||||
g_variant_new ("(o)",
|
||||
source_input_path));
|
||||
|
||||
return TRUE;
|
||||
|
||||
/* ERRORS */
|
||||
not_allowed:
|
||||
{
|
||||
g_dbus_method_invocation_return_dbus_error (invocation,
|
||||
"org.pinos.Error", "not client owner");
|
||||
return TRUE;
|
||||
}
|
||||
no_source:
|
||||
{
|
||||
g_dbus_method_invocation_return_dbus_error (invocation,
|
||||
"org.pinos.Error", "Can't create source");
|
||||
g_bytes_unref (formats);
|
||||
pinos_properties_free (props);
|
||||
return TRUE;
|
||||
}
|
||||
no_input:
|
||||
{
|
||||
g_dbus_method_invocation_return_gerror (invocation, error);
|
||||
g_clear_error (&error);
|
||||
g_bytes_unref (formats);
|
||||
pinos_properties_free (props);
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
static gboolean
|
||||
handle_disconnect (PinosClient1 *interface,
|
||||
GDBusMethodInvocation *invocation,
|
||||
gpointer user_data)
|
||||
{
|
||||
PinosClient *client = user_data;
|
||||
|
||||
g_signal_emit (client, signals[SIGNAL_DISCONNECT], 0, NULL);
|
||||
|
||||
g_dbus_method_invocation_return_value (invocation,
|
||||
g_variant_new ("()"));
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
client_register_object (PinosClient *client,
|
||||
const gchar *prefix)
|
||||
{
|
||||
PinosClientPrivate *priv = client->priv;
|
||||
PinosDaemon *daemon = priv->daemon;
|
||||
PinosObjectSkeleton *skel;
|
||||
gchar *name;
|
||||
|
||||
name = g_strdup_printf ("%s/client", prefix);
|
||||
skel = pinos_object_skeleton_new (name);
|
||||
g_free (name);
|
||||
|
||||
priv->client1 = pinos_client1_skeleton_new ();
|
||||
pinos_client1_set_name (priv->client1, priv->sender);
|
||||
pinos_client1_set_properties (priv->client1, pinos_properties_to_variant (priv->properties));
|
||||
g_signal_connect (priv->client1, "handle-create-source-output",
|
||||
(GCallback) handle_create_source_output,
|
||||
client);
|
||||
g_signal_connect (priv->client1, "handle-create-source-input",
|
||||
(GCallback) handle_create_source_input,
|
||||
client);
|
||||
g_signal_connect (priv->client1, "handle-disconnect",
|
||||
(GCallback) handle_disconnect,
|
||||
client);
|
||||
pinos_object_skeleton_set_client1 (skel, priv->client1);
|
||||
|
||||
g_free (priv->object_path);
|
||||
priv->object_path = pinos_daemon_export_uniquely (daemon, G_DBUS_OBJECT_SKELETON (skel));
|
||||
}
|
||||
|
||||
static void
|
||||
client_unregister_object (PinosClient *client)
|
||||
{
|
||||
PinosClientPrivate *priv = client->priv;
|
||||
PinosDaemon *daemon = priv->daemon;
|
||||
|
||||
g_clear_object (&priv->client1);
|
||||
|
||||
pinos_daemon_unexport (daemon, priv->object_path);
|
||||
g_free (priv->object_path);
|
||||
}
|
||||
|
||||
static void
|
||||
do_remove_output (PinosSourceOutput *output,
|
||||
PinosClient *client)
|
||||
{
|
||||
pinos_source_output_remove (output);
|
||||
}
|
||||
|
||||
static void
|
||||
pinos_client_dispose (GObject * object)
|
||||
{
|
||||
PinosClient *client = PINOS_CLIENT (object);
|
||||
PinosClientPrivate *priv = client->priv;
|
||||
|
||||
g_list_foreach (priv->outputs, (GFunc) do_remove_output, client);
|
||||
client_unregister_object (client);
|
||||
|
||||
G_OBJECT_CLASS (pinos_client_parent_class)->dispose (object);
|
||||
}
|
||||
static void
|
||||
pinos_client_finalize (GObject * object)
|
||||
{
|
||||
PinosClient *client = PINOS_CLIENT (object);
|
||||
PinosClientPrivate *priv = client->priv;
|
||||
|
||||
if (priv->properties)
|
||||
pinos_properties_free (priv->properties);
|
||||
|
||||
G_OBJECT_CLASS (pinos_client_parent_class)->finalize (object);
|
||||
}
|
||||
|
||||
static void
|
||||
pinos_client_constructed (GObject * object)
|
||||
{
|
||||
PinosClient *client = PINOS_CLIENT (object);
|
||||
PinosClientPrivate *priv = client->priv;
|
||||
|
||||
client_register_object (client, priv->object_path);
|
||||
|
||||
G_OBJECT_CLASS (pinos_client_parent_class)->constructed (object);
|
||||
}
|
||||
|
||||
static void
|
||||
pinos_client_class_init (PinosClientClass * klass)
|
||||
{
|
||||
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
|
||||
|
||||
g_type_class_add_private (klass, sizeof (PinosClientPrivate));
|
||||
|
||||
gobject_class->constructed = pinos_client_constructed;
|
||||
gobject_class->dispose = pinos_client_dispose;
|
||||
gobject_class->finalize = pinos_client_finalize;
|
||||
gobject_class->set_property = pinos_client_set_property;
|
||||
gobject_class->get_property = pinos_client_get_property;
|
||||
|
||||
g_object_class_install_property (gobject_class,
|
||||
PROP_DAEMON,
|
||||
g_param_spec_object ("daemon",
|
||||
"Daemon",
|
||||
"The daemon",
|
||||
PINOS_TYPE_DAEMON,
|
||||
G_PARAM_READWRITE |
|
||||
G_PARAM_CONSTRUCT_ONLY |
|
||||
G_PARAM_STATIC_STRINGS));
|
||||
|
||||
g_object_class_install_property (gobject_class,
|
||||
PROP_SENDER,
|
||||
g_param_spec_string ("sender",
|
||||
"Sender",
|
||||
"The sender",
|
||||
NULL,
|
||||
G_PARAM_READWRITE |
|
||||
G_PARAM_CONSTRUCT_ONLY |
|
||||
G_PARAM_STATIC_STRINGS));
|
||||
|
||||
g_object_class_install_property (gobject_class,
|
||||
PROP_OBJECT_PATH,
|
||||
g_param_spec_string ("object-path",
|
||||
"Object Path",
|
||||
"The object path",
|
||||
NULL,
|
||||
G_PARAM_READWRITE |
|
||||
G_PARAM_CONSTRUCT_ONLY |
|
||||
G_PARAM_STATIC_STRINGS));
|
||||
g_object_class_install_property (gobject_class,
|
||||
PROP_PROPERTIES,
|
||||
g_param_spec_boxed ("properties",
|
||||
"Properties",
|
||||
"Client properties",
|
||||
PINOS_TYPE_PROPERTIES,
|
||||
G_PARAM_READWRITE |
|
||||
G_PARAM_CONSTRUCT |
|
||||
G_PARAM_STATIC_STRINGS));
|
||||
|
||||
signals[SIGNAL_DISCONNECT] = g_signal_new ("disconnect",
|
||||
G_TYPE_FROM_CLASS (klass),
|
||||
G_SIGNAL_RUN_LAST,
|
||||
0,
|
||||
NULL,
|
||||
NULL,
|
||||
g_cclosure_marshal_generic,
|
||||
G_TYPE_NONE,
|
||||
0,
|
||||
G_TYPE_NONE);
|
||||
|
||||
}
|
||||
|
||||
static void
|
||||
pinos_client_init (PinosClient * client)
|
||||
{
|
||||
client->priv = PINOS_CLIENT_GET_PRIVATE (client);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* pinos_client_new:
|
||||
* @daemon: a #PinosDaemon
|
||||
* @sender: the sender id
|
||||
* @prefix: a prefix
|
||||
* @properties: extra client properties
|
||||
*
|
||||
* Make a new #PinosClient object and register it to @daemon under the @prefix.
|
||||
*
|
||||
* Returns: a new #PinosClient
|
||||
*/
|
||||
PinosClient *
|
||||
pinos_client_new (PinosDaemon *daemon,
|
||||
const gchar *sender,
|
||||
const gchar *prefix,
|
||||
PinosProperties *properties)
|
||||
{
|
||||
g_return_val_if_fail (PINOS_IS_DAEMON (daemon), NULL);
|
||||
g_return_val_if_fail (g_variant_is_object_path (prefix), NULL);
|
||||
|
||||
return g_object_new (PINOS_TYPE_CLIENT, "daemon", daemon,
|
||||
"sender", sender,
|
||||
"object-path", prefix,
|
||||
"properties", properties,
|
||||
NULL);
|
||||
}
|
||||
|
||||
/**
|
||||
* pinos_client_get_sender:
|
||||
* @client: a #PinosClient
|
||||
*
|
||||
* Get the sender of @client.
|
||||
*
|
||||
* Returns: the sender of @client
|
||||
*/
|
||||
const gchar *
|
||||
pinos_client_get_sender (PinosClient *client)
|
||||
{
|
||||
PinosClientPrivate *priv;
|
||||
|
||||
g_return_val_if_fail (PINOS_IS_CLIENT (client), NULL);
|
||||
priv = client->priv;
|
||||
|
||||
return priv->sender;
|
||||
}
|
||||
|
||||
/**
|
||||
* pinos_client_get_object_path:
|
||||
* @client: a #PinosClient
|
||||
*
|
||||
* Get the object path of @client.
|
||||
*
|
||||
* Returns: the object path of @client
|
||||
*/
|
||||
const gchar *
|
||||
pinos_client_get_object_path (PinosClient *client)
|
||||
{
|
||||
PinosClientPrivate *priv;
|
||||
|
||||
g_return_val_if_fail (PINOS_IS_CLIENT (client), NULL);
|
||||
priv = client->priv;
|
||||
|
||||
return priv->object_path;
|
||||
}
|
||||
|
||||
|
|
@ -1,76 +0,0 @@
|
|||
/* Pinos
|
||||
* Copyright (C) 2015 Wim Taymans <wim.taymans@gmail.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#ifndef __PINOS_CLIENT_H__
|
||||
#define __PINOS_CLIENT_H__
|
||||
|
||||
#include <glib-object.h>
|
||||
|
||||
#include <server/daemon.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define PINOS_TYPE_CLIENT (pinos_client_get_type ())
|
||||
#define PINOS_IS_CLIENT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), PINOS_TYPE_CLIENT))
|
||||
#define PINOS_IS_CLIENT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), PINOS_TYPE_CLIENT))
|
||||
#define PINOS_CLIENT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), PINOS_TYPE_CLIENT, PinosClientClass))
|
||||
#define PINOS_CLIENT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), PINOS_TYPE_CLIENT, PinosClient))
|
||||
#define PINOS_CLIENT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), PINOS_TYPE_CLIENT, PinosClientClass))
|
||||
#define PINOS_CLIENT_CAST(obj) ((PinosClient*)(obj))
|
||||
#define PINOS_CLIENT_CLASS_CAST(klass) ((PinosClientClass*)(klass))
|
||||
|
||||
typedef struct _PinosClient PinosClient;
|
||||
typedef struct _PinosClientClass PinosClientClass;
|
||||
typedef struct _PinosClientPrivate PinosClientPrivate;
|
||||
|
||||
/**
|
||||
* PinosClient:
|
||||
*
|
||||
* Pinos client object class.
|
||||
*/
|
||||
struct _PinosClient {
|
||||
GObject object;
|
||||
|
||||
PinosClientPrivate *priv;
|
||||
};
|
||||
|
||||
/**
|
||||
* PinosClientClass:
|
||||
*
|
||||
* Pinos client object class.
|
||||
*/
|
||||
struct _PinosClientClass {
|
||||
GObjectClass parent_class;
|
||||
};
|
||||
|
||||
/* normal GObject stuff */
|
||||
GType pinos_client_get_type (void);
|
||||
|
||||
PinosClient * pinos_client_new (PinosDaemon *daemon,
|
||||
const gchar *sender,
|
||||
const gchar *prefix,
|
||||
PinosProperties *properties);
|
||||
|
||||
const gchar * pinos_client_get_sender (PinosClient *client);
|
||||
const gchar * pinos_client_get_object_path (PinosClient *client);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __PINOS_CLIENT_H__ */
|
||||
|
||||
|
|
@ -1,515 +0,0 @@
|
|||
/* Pinos
|
||||
* Copyright (C) 2015 Wim Taymans <wim.taymans@gmail.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include <gio/gio.h>
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "client/pinos.h"
|
||||
|
||||
#include "server/daemon.h"
|
||||
#include "server/client.h"
|
||||
|
||||
#include "dbus/org-pinos.h"
|
||||
|
||||
#define PINOS_DAEMON_GET_PRIVATE(obj) \
|
||||
(G_TYPE_INSTANCE_GET_PRIVATE ((obj), PINOS_TYPE_DAEMON, PinosDaemonPrivate))
|
||||
|
||||
struct _PinosDaemonPrivate
|
||||
{
|
||||
guint id;
|
||||
GDBusConnection *connection;
|
||||
GDBusObjectManagerServer *server_manager;
|
||||
|
||||
GList *sources;
|
||||
|
||||
GHashTable *senders;
|
||||
|
||||
PinosProperties *properties;
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
PROP_0,
|
||||
PROP_PROPERTIES,
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
guint id;
|
||||
gchar *sender;
|
||||
PinosDaemon *daemon;
|
||||
GList *objects;
|
||||
} SenderData;
|
||||
|
||||
static void
|
||||
client_name_appeared_handler (GDBusConnection *connection,
|
||||
const gchar *name,
|
||||
const gchar *name_owner,
|
||||
gpointer user_data)
|
||||
{
|
||||
SenderData *data = user_data;
|
||||
PinosDaemonPrivate *priv = data->daemon->priv;
|
||||
|
||||
g_hash_table_insert (priv->senders, data->sender, data);
|
||||
|
||||
if (!g_strcmp0 (name, g_dbus_connection_get_unique_name (connection)))
|
||||
return;
|
||||
}
|
||||
|
||||
static void
|
||||
client_name_vanished_handler (GDBusConnection *connection,
|
||||
const gchar *name,
|
||||
gpointer user_data)
|
||||
{
|
||||
SenderData *data = user_data;
|
||||
|
||||
g_bus_unwatch_name (data->id);
|
||||
}
|
||||
|
||||
static void
|
||||
data_free (SenderData *data)
|
||||
{
|
||||
g_list_free_full (data->objects, g_object_unref);
|
||||
g_hash_table_remove (data->daemon->priv->senders, data->sender);
|
||||
g_free (data->sender);
|
||||
g_free (data);
|
||||
}
|
||||
|
||||
static SenderData *
|
||||
sender_data_new (PinosDaemon *daemon,
|
||||
const gchar *sender)
|
||||
{
|
||||
PinosDaemonPrivate *priv = daemon->priv;
|
||||
SenderData *data;
|
||||
|
||||
data = g_new0 (SenderData, 1);
|
||||
data->daemon = daemon;
|
||||
data->sender = g_strdup (sender);
|
||||
|
||||
data->id = g_bus_watch_name_on_connection (priv->connection,
|
||||
sender,
|
||||
G_BUS_NAME_WATCHER_FLAGS_NONE,
|
||||
client_name_appeared_handler,
|
||||
client_name_vanished_handler,
|
||||
data,
|
||||
(GDestroyNotify) data_free);
|
||||
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
static void
|
||||
handle_disconnect_client (PinosClient *client,
|
||||
gpointer user_data)
|
||||
{
|
||||
PinosDaemon *daemon = user_data;
|
||||
PinosDaemonPrivate *priv = daemon->priv;
|
||||
const gchar *sender;
|
||||
SenderData *data;
|
||||
|
||||
sender = pinos_client_get_sender (client);
|
||||
|
||||
data = g_hash_table_lookup (priv->senders, sender);
|
||||
if (data == NULL)
|
||||
return;
|
||||
|
||||
data->objects = g_list_remove (data->objects, client);
|
||||
g_object_unref (client);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
handle_connect_client (PinosDaemon1 *interface,
|
||||
GDBusMethodInvocation *invocation,
|
||||
GVariant *arg_properties,
|
||||
gpointer user_data)
|
||||
{
|
||||
PinosDaemon *daemon = user_data;
|
||||
PinosDaemonPrivate *priv = daemon->priv;
|
||||
PinosClient *client;
|
||||
const gchar *sender, *object_path;
|
||||
SenderData *data;
|
||||
PinosProperties *props;
|
||||
|
||||
sender = g_dbus_method_invocation_get_sender (invocation);
|
||||
|
||||
props = pinos_properties_from_variant (arg_properties);
|
||||
client = pinos_client_new (daemon, sender, PINOS_DBUS_OBJECT_PREFIX, props);
|
||||
pinos_properties_free (props);
|
||||
|
||||
g_signal_connect (client, "disconnect", (GCallback) handle_disconnect_client, daemon);
|
||||
|
||||
data = g_hash_table_lookup (priv->senders, sender);
|
||||
if (data == NULL)
|
||||
data = sender_data_new (daemon, sender);
|
||||
|
||||
data->objects = g_list_prepend (data->objects, client);
|
||||
|
||||
object_path = pinos_client_get_object_path (client);
|
||||
g_dbus_method_invocation_return_value (invocation,
|
||||
g_variant_new ("(o)", object_path));
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
export_server_object (PinosDaemon *daemon,
|
||||
GDBusObjectManagerServer *manager)
|
||||
{
|
||||
PinosDaemonPrivate *priv = daemon->priv;
|
||||
PinosObjectSkeleton *skel;
|
||||
|
||||
skel = pinos_object_skeleton_new (PINOS_DBUS_OBJECT_SERVER);
|
||||
{
|
||||
PinosDaemon1 *iface;
|
||||
|
||||
iface = pinos_daemon1_skeleton_new ();
|
||||
g_signal_connect (iface, "handle-connect-client", (GCallback) handle_connect_client, daemon);
|
||||
pinos_daemon1_set_user_name (iface, g_get_user_name ());
|
||||
pinos_daemon1_set_host_name (iface, g_get_host_name ());
|
||||
pinos_daemon1_set_version (iface, PACKAGE_VERSION);
|
||||
pinos_daemon1_set_name (iface, PACKAGE_NAME);
|
||||
pinos_daemon1_set_cookie (iface, g_random_int());
|
||||
pinos_daemon1_set_properties (iface, pinos_properties_to_variant (priv->properties));
|
||||
pinos_object_skeleton_set_daemon1 (skel, iface);
|
||||
g_object_unref (iface);
|
||||
}
|
||||
g_dbus_object_manager_server_export (manager, G_DBUS_OBJECT_SKELETON (skel));
|
||||
g_object_unref (skel);
|
||||
}
|
||||
|
||||
static void
|
||||
bus_acquired_handler (GDBusConnection *connection,
|
||||
const gchar *name,
|
||||
gpointer user_data)
|
||||
{
|
||||
PinosDaemon *daemon = user_data;
|
||||
PinosDaemonPrivate *priv = daemon->priv;
|
||||
GDBusObjectManagerServer *manager = priv->server_manager;
|
||||
|
||||
priv->connection = connection;
|
||||
|
||||
export_server_object (daemon, manager);
|
||||
|
||||
g_dbus_object_manager_server_set_connection (manager, connection);
|
||||
}
|
||||
|
||||
static void
|
||||
name_acquired_handler (GDBusConnection *connection,
|
||||
const gchar *name,
|
||||
gpointer user_data)
|
||||
{
|
||||
}
|
||||
|
||||
static void
|
||||
name_lost_handler (GDBusConnection *connection,
|
||||
const gchar *name,
|
||||
gpointer user_data)
|
||||
{
|
||||
PinosDaemon *daemon = user_data;
|
||||
PinosDaemonPrivate *priv = daemon->priv;
|
||||
GDBusObjectManagerServer *manager = priv->server_manager;
|
||||
|
||||
g_dbus_object_manager_server_unexport (manager, PINOS_DBUS_OBJECT_SERVER);
|
||||
g_dbus_object_manager_server_set_connection (manager, connection);
|
||||
priv->connection = connection;
|
||||
}
|
||||
|
||||
/**
|
||||
* pinos_daemon_new:
|
||||
* @properties: #PinosProperties
|
||||
*
|
||||
* Make a new #PinosDaemon object with given @properties
|
||||
*
|
||||
* Returns: a new #PinosDaemon
|
||||
*/
|
||||
PinosDaemon *
|
||||
pinos_daemon_new (PinosProperties *properties)
|
||||
{
|
||||
return g_object_new (PINOS_TYPE_DAEMON, "properties", properties, NULL);
|
||||
}
|
||||
|
||||
/**
|
||||
* pinos_daemon_start:
|
||||
* @daemon: a #PinosDaemon
|
||||
*
|
||||
* Start the @daemon.
|
||||
*/
|
||||
void
|
||||
pinos_daemon_start (PinosDaemon *daemon)
|
||||
{
|
||||
PinosDaemonPrivate *priv;
|
||||
|
||||
g_return_if_fail (PINOS_IS_DAEMON (daemon));
|
||||
|
||||
priv = daemon->priv;
|
||||
g_return_if_fail (priv->id == 0);
|
||||
|
||||
priv->id = g_bus_own_name (G_BUS_TYPE_SESSION,
|
||||
PINOS_DBUS_SERVICE,
|
||||
G_BUS_NAME_OWNER_FLAGS_REPLACE,
|
||||
bus_acquired_handler,
|
||||
name_acquired_handler,
|
||||
name_lost_handler,
|
||||
daemon,
|
||||
NULL);
|
||||
}
|
||||
|
||||
/**
|
||||
* pinos_daemon_stop:
|
||||
* @daemon: a #PinosDaemon
|
||||
*
|
||||
* Stop the @daemon.
|
||||
*/
|
||||
void
|
||||
pinos_daemon_stop (PinosDaemon *daemon)
|
||||
{
|
||||
PinosDaemonPrivate *priv = daemon->priv;
|
||||
|
||||
g_return_if_fail (PINOS_IS_DAEMON (daemon));
|
||||
|
||||
if (priv->id != 0) {
|
||||
g_bus_unown_name (priv->id);
|
||||
priv->id = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* pinos_daemon_export_uniquely:
|
||||
* @daemon: a #PinosDaemon
|
||||
* @skel: a #GDBusObjectSkeleton
|
||||
*
|
||||
* Export @skel with @daemon with a unique name
|
||||
*
|
||||
* Returns: the unique named used to export @skel.
|
||||
*/
|
||||
gchar *
|
||||
pinos_daemon_export_uniquely (PinosDaemon *daemon,
|
||||
GDBusObjectSkeleton *skel)
|
||||
{
|
||||
g_return_val_if_fail (PINOS_IS_DAEMON (daemon), NULL);
|
||||
g_return_val_if_fail (G_IS_DBUS_OBJECT_SKELETON (skel), NULL);
|
||||
|
||||
g_dbus_object_manager_server_export_uniquely (daemon->priv->server_manager, skel);
|
||||
|
||||
return g_strdup (g_dbus_object_get_object_path (G_DBUS_OBJECT (skel)));
|
||||
}
|
||||
|
||||
/**
|
||||
* pinos_daemon_unexport:
|
||||
* @daemon: a #PinosDaemon
|
||||
* @object_path: an object path
|
||||
*
|
||||
* Unexport the object on @object_path
|
||||
*/
|
||||
void
|
||||
pinos_daemon_unexport (PinosDaemon *daemon,
|
||||
const gchar *object_path)
|
||||
{
|
||||
g_return_if_fail (PINOS_IS_DAEMON (daemon));
|
||||
g_return_if_fail (g_variant_is_object_path (object_path));
|
||||
|
||||
g_dbus_object_manager_server_unexport (daemon->priv->server_manager, object_path);
|
||||
}
|
||||
|
||||
/**
|
||||
* pinos_daemon_add_source:
|
||||
* @daemon: a #PinosDaemon
|
||||
* @source: a #PinosSource
|
||||
*
|
||||
* Add @source to @daemon.
|
||||
*/
|
||||
void
|
||||
pinos_daemon_add_source (PinosDaemon *daemon,
|
||||
PinosSource *source)
|
||||
{
|
||||
PinosDaemonPrivate *priv;
|
||||
|
||||
g_return_if_fail (PINOS_IS_DAEMON (daemon));
|
||||
g_return_if_fail (PINOS_IS_SOURCE (source));
|
||||
priv = daemon->priv;
|
||||
|
||||
priv->sources = g_list_prepend (priv->sources, source);
|
||||
}
|
||||
|
||||
/**
|
||||
* pinos_daemon_remove_source:
|
||||
* @daemon: a #PinosDaemon
|
||||
* @source: a #PinosSource
|
||||
*
|
||||
* Remove @source from @daemon.
|
||||
*/
|
||||
void
|
||||
pinos_daemon_remove_source (PinosDaemon *daemon,
|
||||
PinosSource *source)
|
||||
{
|
||||
PinosDaemonPrivate *priv;
|
||||
|
||||
g_return_if_fail (PINOS_IS_DAEMON (daemon));
|
||||
g_return_if_fail (PINOS_IS_SOURCE (source));
|
||||
priv = daemon->priv;
|
||||
|
||||
priv->sources = g_list_remove (priv->sources, source);
|
||||
}
|
||||
|
||||
/**
|
||||
* pinos_daemon_find_source:
|
||||
* @daemon: a #PinosDaemon
|
||||
* @name: a source name
|
||||
* @props: source properties
|
||||
* @format_filter: a format filter
|
||||
* @error: location for an error
|
||||
*
|
||||
* Find the best source in @daemon that matches the given parameters.
|
||||
*
|
||||
* Returns: a #PinosSource or %NULL when no source could be found.
|
||||
*/
|
||||
PinosSource *
|
||||
pinos_daemon_find_source (PinosDaemon *daemon,
|
||||
const gchar *name,
|
||||
PinosProperties *props,
|
||||
GBytes *format_filter,
|
||||
GError **error)
|
||||
{
|
||||
PinosDaemonPrivate *priv;
|
||||
PinosSource *best = NULL;
|
||||
GList *walk;
|
||||
|
||||
g_return_val_if_fail (PINOS_IS_DAEMON (daemon), NULL);
|
||||
priv = daemon->priv;
|
||||
|
||||
for (walk = priv->sources; walk; walk = g_list_next (walk)) {
|
||||
PinosSource *s = walk->data;
|
||||
|
||||
if (name == NULL) {
|
||||
best = s;
|
||||
break;
|
||||
}
|
||||
else if (g_str_has_suffix (pinos_source_get_object_path (s), name))
|
||||
best = s;
|
||||
}
|
||||
|
||||
if (best == NULL) {
|
||||
if (error)
|
||||
*error = g_error_new (G_IO_ERROR,
|
||||
G_IO_ERROR_NOT_FOUND,
|
||||
"Source not found");
|
||||
}
|
||||
return best;
|
||||
}
|
||||
|
||||
|
||||
G_DEFINE_TYPE (PinosDaemon, pinos_daemon, G_TYPE_OBJECT);
|
||||
|
||||
static void
|
||||
pinos_daemon_get_property (GObject *_object,
|
||||
guint prop_id,
|
||||
GValue *value,
|
||||
GParamSpec *pspec)
|
||||
{
|
||||
PinosDaemon *daemon = PINOS_DAEMON (_object);
|
||||
PinosDaemonPrivate *priv = daemon->priv;
|
||||
|
||||
switch (prop_id) {
|
||||
case PROP_PROPERTIES:
|
||||
g_value_set_boxed (value, priv->properties);
|
||||
break;
|
||||
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (daemon, prop_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
pinos_daemon_set_property (GObject *_object,
|
||||
guint prop_id,
|
||||
const GValue *value,
|
||||
GParamSpec *pspec)
|
||||
{
|
||||
PinosDaemon *daemon = PINOS_DAEMON (_object);
|
||||
PinosDaemonPrivate *priv = daemon->priv;
|
||||
|
||||
switch (prop_id) {
|
||||
case PROP_PROPERTIES:
|
||||
if (priv->properties)
|
||||
pinos_properties_free (priv->properties);
|
||||
priv->properties = g_value_dup_boxed (value);
|
||||
break;
|
||||
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (daemon, prop_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
pinos_daemon_dispose (GObject * object)
|
||||
{
|
||||
PinosDaemon *daemon = PINOS_DAEMON_CAST (object);
|
||||
|
||||
pinos_daemon_stop (daemon);
|
||||
|
||||
G_OBJECT_CLASS (pinos_daemon_parent_class)->dispose (object);
|
||||
}
|
||||
|
||||
static void
|
||||
pinos_daemon_finalize (GObject * object)
|
||||
{
|
||||
PinosDaemon *daemon = PINOS_DAEMON_CAST (object);
|
||||
PinosDaemonPrivate *priv = daemon->priv;
|
||||
|
||||
g_clear_object (&priv->server_manager);
|
||||
|
||||
G_OBJECT_CLASS (pinos_daemon_parent_class)->finalize (object);
|
||||
}
|
||||
|
||||
static void
|
||||
pinos_daemon_class_init (PinosDaemonClass * klass)
|
||||
{
|
||||
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
|
||||
|
||||
g_type_class_add_private (klass, sizeof (PinosDaemonPrivate));
|
||||
|
||||
gobject_class->dispose = pinos_daemon_dispose;
|
||||
gobject_class->finalize = pinos_daemon_finalize;
|
||||
|
||||
gobject_class->set_property = pinos_daemon_set_property;
|
||||
gobject_class->get_property = pinos_daemon_get_property;
|
||||
|
||||
g_object_class_install_property (gobject_class,
|
||||
PROP_PROPERTIES,
|
||||
g_param_spec_boxed ("properties",
|
||||
"Properties",
|
||||
"Client properties",
|
||||
PINOS_TYPE_PROPERTIES,
|
||||
G_PARAM_READWRITE |
|
||||
G_PARAM_CONSTRUCT |
|
||||
G_PARAM_STATIC_STRINGS));
|
||||
|
||||
}
|
||||
|
||||
static void
|
||||
pinos_daemon_init (PinosDaemon * daemon)
|
||||
{
|
||||
PinosDaemonPrivate *priv = daemon->priv = PINOS_DAEMON_GET_PRIVATE (daemon);
|
||||
|
||||
priv->server_manager = g_dbus_object_manager_server_new (PINOS_DBUS_OBJECT_PREFIX);
|
||||
priv->senders = g_hash_table_new (g_str_hash, g_str_equal);
|
||||
}
|
||||
|
||||
|
|
@ -1,85 +0,0 @@
|
|||
/* Pinos
|
||||
* Copyright (C) 2015 Wim Taymans <wim.taymans@gmail.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#ifndef __PINOS_DAEMON_H__
|
||||
#define __PINOS_DAEMON_H__
|
||||
|
||||
#include <glib-object.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define PINOS_TYPE_DAEMON (pinos_daemon_get_type ())
|
||||
#define PINOS_IS_DAEMON(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), PINOS_TYPE_DAEMON))
|
||||
#define PINOS_IS_DAEMON_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), PINOS_TYPE_DAEMON))
|
||||
#define PINOS_DAEMON_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), PINOS_TYPE_DAEMON, PinosDaemonClass))
|
||||
#define PINOS_DAEMON(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), PINOS_TYPE_DAEMON, PinosDaemon))
|
||||
#define PINOS_DAEMON_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), PINOS_TYPE_DAEMON, PinosDaemonClass))
|
||||
#define PINOS_DAEMON_CAST(obj) ((PinosDaemon*)(obj))
|
||||
#define PINOS_DAEMON_CLASS_CAST(klass) ((PinosDaemonClass*)(klass))
|
||||
|
||||
typedef struct _PinosDaemon PinosDaemon;
|
||||
typedef struct _PinosDaemonClass PinosDaemonClass;
|
||||
typedef struct _PinosDaemonPrivate PinosDaemonPrivate;
|
||||
|
||||
#include <server/source.h>
|
||||
#include <client/properties.h>
|
||||
|
||||
/**
|
||||
* PinosDaemon:
|
||||
*
|
||||
* Pinos daemon object class.
|
||||
*/
|
||||
struct _PinosDaemon {
|
||||
GObject object;
|
||||
|
||||
PinosDaemonPrivate *priv;
|
||||
};
|
||||
|
||||
/**
|
||||
* PinosDaemonClass:
|
||||
*
|
||||
* Pinos daemon object class.
|
||||
*/
|
||||
struct _PinosDaemonClass {
|
||||
GObjectClass parent_class;
|
||||
};
|
||||
|
||||
/* normal GObject stuff */
|
||||
GType pinos_daemon_get_type (void);
|
||||
|
||||
PinosDaemon * pinos_daemon_new (PinosProperties *properties);
|
||||
|
||||
void pinos_daemon_start (PinosDaemon *daemon);
|
||||
void pinos_daemon_stop (PinosDaemon *daemon);
|
||||
|
||||
gchar * pinos_daemon_export_uniquely (PinosDaemon *daemon, GDBusObjectSkeleton *skel);
|
||||
void pinos_daemon_unexport (PinosDaemon *daemon, const gchar *name);
|
||||
|
||||
void pinos_daemon_add_source (PinosDaemon *daemon, PinosSource *source);
|
||||
void pinos_daemon_remove_source (PinosDaemon *daemon, PinosSource *source);
|
||||
PinosSource * pinos_daemon_find_source (PinosDaemon *daemon,
|
||||
const gchar *name,
|
||||
PinosProperties *props,
|
||||
GBytes *format_filter,
|
||||
GError **error);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __PINOS_DAEMON_H__ */
|
||||
|
||||
|
|
@ -1,509 +0,0 @@
|
|||
/* Pinos
|
||||
* Copyright (C) 2015 Wim Taymans <wim.taymans@gmail.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include <sys/socket.h>
|
||||
|
||||
#include <gio/gunixfdlist.h>
|
||||
|
||||
#include "client/enumtypes.h"
|
||||
|
||||
#include "server/daemon.h"
|
||||
#include "server/source-output.h"
|
||||
|
||||
#include "dbus/org-pinos.h"
|
||||
|
||||
struct _PinosSourceOutputPrivate
|
||||
{
|
||||
PinosDaemon *daemon;
|
||||
PinosSourceOutput1 *iface;
|
||||
|
||||
gchar *object_path;
|
||||
gchar *client_path;
|
||||
gchar *source_path;
|
||||
|
||||
GBytes *possible_formats;
|
||||
PinosProperties *properties;
|
||||
GBytes *requested_format;
|
||||
PinosSourceOutputState state;
|
||||
GBytes *format;
|
||||
|
||||
GSocket *socket;
|
||||
};
|
||||
|
||||
#define PINOS_SOURCE_OUTPUT_GET_PRIVATE(obj) \
|
||||
(G_TYPE_INSTANCE_GET_PRIVATE ((obj), PINOS_TYPE_SOURCE_OUTPUT, PinosSourceOutputPrivate))
|
||||
|
||||
G_DEFINE_TYPE (PinosSourceOutput, pinos_source_output, G_TYPE_OBJECT);
|
||||
|
||||
enum
|
||||
{
|
||||
PROP_0,
|
||||
PROP_DAEMON,
|
||||
PROP_OBJECT_PATH,
|
||||
PROP_CLIENT_PATH,
|
||||
PROP_SOURCE_PATH,
|
||||
PROP_POSSIBLE_FORMATS,
|
||||
PROP_PROPERTIES,
|
||||
PROP_REQUESTED_FORMAT,
|
||||
PROP_FORMAT,
|
||||
PROP_SOCKET,
|
||||
PROP_STATE,
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
SIGNAL_REMOVE,
|
||||
LAST_SIGNAL
|
||||
};
|
||||
|
||||
static guint signals[LAST_SIGNAL] = { 0 };
|
||||
|
||||
static void
|
||||
pinos_source_output_get_property (GObject *_object,
|
||||
guint prop_id,
|
||||
GValue *value,
|
||||
GParamSpec *pspec)
|
||||
{
|
||||
PinosSourceOutput *output = PINOS_SOURCE_OUTPUT (_object);
|
||||
PinosSourceOutputPrivate *priv = output->priv;
|
||||
|
||||
switch (prop_id) {
|
||||
case PROP_DAEMON:
|
||||
g_value_set_object (value, priv->daemon);
|
||||
break;
|
||||
|
||||
case PROP_OBJECT_PATH:
|
||||
g_value_set_string (value, priv->object_path);
|
||||
break;
|
||||
|
||||
case PROP_CLIENT_PATH:
|
||||
g_value_set_string (value, priv->client_path);
|
||||
break;
|
||||
|
||||
case PROP_SOURCE_PATH:
|
||||
g_value_set_string (value, priv->source_path);
|
||||
break;
|
||||
|
||||
case PROP_POSSIBLE_FORMATS:
|
||||
g_value_set_boxed (value, priv->possible_formats);
|
||||
break;
|
||||
|
||||
case PROP_PROPERTIES:
|
||||
g_value_set_boxed (value, priv->properties);
|
||||
break;
|
||||
|
||||
case PROP_REQUESTED_FORMAT:
|
||||
g_value_set_boxed (value, priv->requested_format);
|
||||
break;
|
||||
|
||||
case PROP_FORMAT:
|
||||
g_value_set_boxed (value, priv->format);
|
||||
break;
|
||||
|
||||
case PROP_SOCKET:
|
||||
g_value_set_object (value, priv->socket);
|
||||
break;
|
||||
|
||||
case PROP_STATE:
|
||||
g_value_set_uint (value, priv->state);
|
||||
break;
|
||||
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (output, prop_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
pinos_source_output_set_property (GObject *_object,
|
||||
guint prop_id,
|
||||
const GValue *value,
|
||||
GParamSpec *pspec)
|
||||
{
|
||||
PinosSourceOutput *output = PINOS_SOURCE_OUTPUT (_object);
|
||||
PinosSourceOutputPrivate *priv = output->priv;
|
||||
|
||||
switch (prop_id) {
|
||||
case PROP_DAEMON:
|
||||
priv->daemon = g_value_dup_object (value);
|
||||
break;
|
||||
|
||||
case PROP_OBJECT_PATH:
|
||||
priv->object_path = g_value_dup_string (value);
|
||||
break;
|
||||
|
||||
case PROP_CLIENT_PATH:
|
||||
priv->client_path = g_value_dup_string (value);
|
||||
g_object_set (priv->iface, "client", priv->client_path, NULL);
|
||||
break;
|
||||
|
||||
case PROP_SOURCE_PATH:
|
||||
priv->source_path = g_value_dup_string (value);
|
||||
g_object_set (priv->iface, "source", priv->source_path, NULL);
|
||||
break;
|
||||
|
||||
case PROP_POSSIBLE_FORMATS:
|
||||
if (priv->possible_formats)
|
||||
g_bytes_unref (priv->possible_formats);
|
||||
priv->possible_formats = g_value_dup_boxed (value);
|
||||
g_object_set (priv->iface, "possible-formats",
|
||||
g_bytes_get_data (priv->possible_formats, NULL), NULL);
|
||||
break;
|
||||
|
||||
case PROP_PROPERTIES:
|
||||
if (priv->properties)
|
||||
pinos_properties_free (priv->properties);
|
||||
priv->properties = g_value_dup_boxed (value);
|
||||
g_object_set (priv->iface, "properties",
|
||||
pinos_properties_to_variant (priv->properties), NULL);
|
||||
break;
|
||||
|
||||
case PROP_FORMAT:
|
||||
if (priv->format)
|
||||
g_bytes_unref (priv->format);
|
||||
priv->format = g_value_dup_boxed (value);
|
||||
break;
|
||||
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (output, prop_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static gboolean
|
||||
handle_start (PinosSourceOutput1 *interface,
|
||||
GDBusMethodInvocation *invocation,
|
||||
const gchar *arg_requested_format,
|
||||
gpointer user_data)
|
||||
{
|
||||
PinosSourceOutput *output = user_data;
|
||||
PinosSourceOutputPrivate *priv = output->priv;
|
||||
GUnixFDList *fdlist;
|
||||
gint fd[2];
|
||||
|
||||
priv->state = PINOS_SOURCE_OUTPUT_STATE_STARTING;
|
||||
|
||||
priv->requested_format = g_bytes_new (arg_requested_format,
|
||||
strlen (arg_requested_format) + 1);
|
||||
|
||||
socketpair (AF_UNIX, SOCK_STREAM, 0, fd);
|
||||
priv->socket = g_socket_new_from_fd (fd[0], NULL);
|
||||
g_object_notify (G_OBJECT (output), "socket");
|
||||
|
||||
/* the notify of the socket above should configure the format */
|
||||
if (priv->format == NULL)
|
||||
goto no_format;
|
||||
|
||||
priv->state = PINOS_SOURCE_OUTPUT_STATE_STREAMING;
|
||||
|
||||
fdlist = g_unix_fd_list_new ();
|
||||
g_unix_fd_list_append (fdlist, fd[1], NULL);
|
||||
|
||||
g_dbus_method_invocation_return_value_with_unix_fd_list (invocation,
|
||||
g_variant_new ("(hs@a{sv})",
|
||||
0,
|
||||
g_bytes_get_data (priv->format, NULL),
|
||||
pinos_properties_to_variant (priv->properties)),
|
||||
fdlist);
|
||||
|
||||
g_object_set (priv->iface,
|
||||
"format", g_bytes_get_data (priv->format, NULL),
|
||||
"state", priv->state,
|
||||
NULL);
|
||||
|
||||
return TRUE;
|
||||
|
||||
/* error */
|
||||
no_format:
|
||||
{
|
||||
g_dbus_method_invocation_return_dbus_error (invocation,
|
||||
"org.pinos.Error", "No format");
|
||||
close (fd[0]);
|
||||
close (fd[1]);
|
||||
g_clear_pointer (&priv->requested_format, g_bytes_unref);
|
||||
g_clear_object (&priv->socket);
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
clear_socket (PinosSourceOutput *output)
|
||||
{
|
||||
PinosSourceOutputPrivate *priv = output->priv;
|
||||
|
||||
g_clear_object (&priv->socket);
|
||||
g_clear_pointer (&priv->requested_format, g_bytes_unref);
|
||||
g_clear_pointer (&priv->format, g_bytes_unref);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
stop_transfer (PinosSourceOutput *output)
|
||||
{
|
||||
PinosSourceOutputPrivate *priv = output->priv;
|
||||
|
||||
if (priv->socket) {
|
||||
clear_socket (output);
|
||||
g_object_notify (G_OBJECT (output), "socket");
|
||||
}
|
||||
priv->state = PINOS_SOURCE_OUTPUT_STATE_IDLE;
|
||||
g_object_set (priv->iface,
|
||||
"state", priv->state,
|
||||
NULL);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
handle_stop (PinosSourceOutput1 *interface,
|
||||
GDBusMethodInvocation *invocation,
|
||||
gpointer user_data)
|
||||
{
|
||||
PinosSourceOutput *output = user_data;
|
||||
|
||||
stop_transfer (output);
|
||||
|
||||
g_dbus_method_invocation_return_value (invocation, NULL);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
handle_remove (PinosSourceOutput1 *interface,
|
||||
GDBusMethodInvocation *invocation,
|
||||
gpointer user_data)
|
||||
{
|
||||
PinosSourceOutput *output = user_data;
|
||||
|
||||
stop_transfer (output);
|
||||
|
||||
g_signal_emit (output, signals[SIGNAL_REMOVE], 0, NULL);
|
||||
|
||||
g_dbus_method_invocation_return_value (invocation, NULL);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
output_register_object (PinosSourceOutput *output,
|
||||
const gchar *prefix)
|
||||
{
|
||||
PinosSourceOutputPrivate *priv = output->priv;
|
||||
PinosObjectSkeleton *skel;
|
||||
gchar *name;
|
||||
|
||||
name = g_strdup_printf ("%s/output", prefix);
|
||||
skel = pinos_object_skeleton_new (name);
|
||||
g_free (name);
|
||||
|
||||
pinos_object_skeleton_set_source_output1 (skel, priv->iface);
|
||||
|
||||
g_free (priv->object_path);
|
||||
priv->object_path = pinos_daemon_export_uniquely (priv->daemon, G_DBUS_OBJECT_SKELETON (skel));
|
||||
}
|
||||
|
||||
static void
|
||||
output_unregister_object (PinosSourceOutput *output)
|
||||
{
|
||||
PinosSourceOutputPrivate *priv = output->priv;
|
||||
|
||||
pinos_daemon_unexport (priv->daemon, priv->object_path);
|
||||
}
|
||||
|
||||
static void
|
||||
pinos_source_output_dispose (GObject * object)
|
||||
{
|
||||
PinosSourceOutput *output = PINOS_SOURCE_OUTPUT (object);
|
||||
|
||||
clear_socket (output);
|
||||
output_unregister_object (output);
|
||||
|
||||
G_OBJECT_CLASS (pinos_source_output_parent_class)->dispose (object);
|
||||
}
|
||||
|
||||
static void
|
||||
pinos_source_output_finalize (GObject * object)
|
||||
{
|
||||
PinosSourceOutput *output = PINOS_SOURCE_OUTPUT (object);
|
||||
PinosSourceOutputPrivate *priv = output->priv;
|
||||
|
||||
g_clear_object (&priv->daemon);
|
||||
g_clear_object (&priv->iface);
|
||||
g_free (priv->client_path);
|
||||
g_free (priv->object_path);
|
||||
g_free (priv->source_path);
|
||||
|
||||
G_OBJECT_CLASS (pinos_source_output_parent_class)->finalize (object);
|
||||
}
|
||||
|
||||
static void
|
||||
pinos_source_output_constructed (GObject * object)
|
||||
{
|
||||
PinosSourceOutput *output = PINOS_SOURCE_OUTPUT (object);
|
||||
PinosSourceOutputPrivate *priv = output->priv;
|
||||
|
||||
output_register_object (output, priv->object_path);
|
||||
|
||||
G_OBJECT_CLASS (pinos_source_output_parent_class)->constructed (object);
|
||||
}
|
||||
|
||||
static void
|
||||
pinos_source_output_class_init (PinosSourceOutputClass * klass)
|
||||
{
|
||||
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
|
||||
|
||||
g_type_class_add_private (klass, sizeof (PinosSourceOutputPrivate));
|
||||
|
||||
gobject_class->constructed = pinos_source_output_constructed;
|
||||
gobject_class->dispose = pinos_source_output_dispose;
|
||||
gobject_class->finalize = pinos_source_output_finalize;
|
||||
gobject_class->set_property = pinos_source_output_set_property;
|
||||
gobject_class->get_property = pinos_source_output_get_property;
|
||||
|
||||
g_object_class_install_property (gobject_class,
|
||||
PROP_DAEMON,
|
||||
g_param_spec_object ("daemon",
|
||||
"Daemon",
|
||||
"The Daemon",
|
||||
PINOS_TYPE_DAEMON,
|
||||
G_PARAM_READWRITE |
|
||||
G_PARAM_CONSTRUCT_ONLY |
|
||||
G_PARAM_STATIC_STRINGS));
|
||||
|
||||
g_object_class_install_property (gobject_class,
|
||||
PROP_OBJECT_PATH,
|
||||
g_param_spec_string ("object-path",
|
||||
"Object Path",
|
||||
"The object path",
|
||||
NULL,
|
||||
G_PARAM_READWRITE |
|
||||
G_PARAM_CONSTRUCT_ONLY |
|
||||
G_PARAM_STATIC_STRINGS));
|
||||
|
||||
g_object_class_install_property (gobject_class,
|
||||
PROP_CLIENT_PATH,
|
||||
g_param_spec_string ("client-path",
|
||||
"Client Path",
|
||||
"The client object path",
|
||||
NULL,
|
||||
G_PARAM_READWRITE |
|
||||
G_PARAM_CONSTRUCT_ONLY |
|
||||
G_PARAM_STATIC_STRINGS));
|
||||
|
||||
g_object_class_install_property (gobject_class,
|
||||
PROP_SOURCE_PATH,
|
||||
g_param_spec_string ("source-path",
|
||||
"Source Path",
|
||||
"The source object path",
|
||||
NULL,
|
||||
G_PARAM_READWRITE |
|
||||
G_PARAM_CONSTRUCT_ONLY |
|
||||
G_PARAM_STATIC_STRINGS));
|
||||
|
||||
g_object_class_install_property (gobject_class,
|
||||
PROP_POSSIBLE_FORMATS,
|
||||
g_param_spec_boxed ("possible-formats",
|
||||
"Possible Formats",
|
||||
"The possbile formats of the stream",
|
||||
G_TYPE_BYTES,
|
||||
G_PARAM_READWRITE |
|
||||
G_PARAM_CONSTRUCT |
|
||||
G_PARAM_STATIC_STRINGS));
|
||||
|
||||
g_object_class_install_property (gobject_class,
|
||||
PROP_PROPERTIES,
|
||||
g_param_spec_boxed ("properties",
|
||||
"Properties",
|
||||
"Extra properties of the stream",
|
||||
PINOS_TYPE_PROPERTIES,
|
||||
G_PARAM_READWRITE |
|
||||
G_PARAM_CONSTRUCT |
|
||||
G_PARAM_STATIC_STRINGS));
|
||||
|
||||
g_object_class_install_property (gobject_class,
|
||||
PROP_REQUESTED_FORMAT,
|
||||
g_param_spec_boxed ("requested-format",
|
||||
"Requested Format",
|
||||
"The requested format of the stream",
|
||||
G_TYPE_BYTES,
|
||||
G_PARAM_READABLE |
|
||||
G_PARAM_STATIC_STRINGS));
|
||||
|
||||
g_object_class_install_property (gobject_class,
|
||||
PROP_FORMAT,
|
||||
g_param_spec_boxed ("format",
|
||||
"Format",
|
||||
"The format of the stream",
|
||||
G_TYPE_BYTES,
|
||||
G_PARAM_READWRITE |
|
||||
G_PARAM_STATIC_STRINGS));
|
||||
|
||||
g_object_class_install_property (gobject_class,
|
||||
PROP_SOCKET,
|
||||
g_param_spec_object ("socket",
|
||||
"Socket",
|
||||
"The socket with data",
|
||||
G_TYPE_SOCKET,
|
||||
G_PARAM_READABLE |
|
||||
G_PARAM_STATIC_STRINGS));
|
||||
|
||||
signals[SIGNAL_REMOVE] = g_signal_new ("remove",
|
||||
G_TYPE_FROM_CLASS (klass),
|
||||
G_SIGNAL_RUN_LAST,
|
||||
0,
|
||||
NULL,
|
||||
NULL,
|
||||
g_cclosure_marshal_generic,
|
||||
G_TYPE_NONE,
|
||||
0,
|
||||
G_TYPE_NONE);
|
||||
}
|
||||
|
||||
static void
|
||||
pinos_source_output_init (PinosSourceOutput * output)
|
||||
{
|
||||
PinosSourceOutputPrivate *priv = output->priv = PINOS_SOURCE_OUTPUT_GET_PRIVATE (output);
|
||||
|
||||
priv->iface = pinos_source_output1_skeleton_new ();
|
||||
g_signal_connect (priv->iface, "handle-start", (GCallback) handle_start, output);
|
||||
g_signal_connect (priv->iface, "handle-stop", (GCallback) handle_stop, output);
|
||||
g_signal_connect (priv->iface, "handle-remove", (GCallback) handle_remove, output);
|
||||
|
||||
priv->state = PINOS_SOURCE_OUTPUT_STATE_IDLE;
|
||||
g_object_set (priv->iface, "state", priv->state, NULL);
|
||||
}
|
||||
|
||||
void
|
||||
pinos_source_output_remove (PinosSourceOutput *output)
|
||||
{
|
||||
stop_transfer (output);
|
||||
|
||||
g_signal_emit (output, signals[SIGNAL_REMOVE], 0, NULL);
|
||||
}
|
||||
|
||||
const gchar *
|
||||
pinos_source_output_get_object_path (PinosSourceOutput *output)
|
||||
{
|
||||
PinosSourceOutputPrivate *priv;
|
||||
|
||||
g_return_val_if_fail (PINOS_IS_SOURCE_OUTPUT (output), NULL);
|
||||
priv = output->priv;
|
||||
|
||||
return priv->object_path;
|
||||
}
|
||||
|
||||
|
|
@ -1,70 +0,0 @@
|
|||
/* Pinos
|
||||
* Copyright (C) 2015 Wim Taymans <wim.taymans@gmail.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#ifndef __PINOS_SOURCE_OUTPUT_H__
|
||||
#define __PINOS_SOURCE_OUTPUT_H__
|
||||
|
||||
#include <glib-object.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define PINOS_TYPE_SOURCE_OUTPUT (pinos_source_output_get_type ())
|
||||
#define PINOS_IS_SOURCE_OUTPUT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), PINOS_TYPE_SOURCE_OUTPUT))
|
||||
#define PINOS_IS_SOURCE_OUTPUT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), PINOS_TYPE_SOURCE_OUTPUT))
|
||||
#define PINOS_SOURCE_OUTPUT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), PINOS_TYPE_SOURCE_OUTPUT, PinosSourceOutputClass))
|
||||
#define PINOS_SOURCE_OUTPUT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), PINOS_TYPE_SOURCE_OUTPUT, PinosSourceOutput))
|
||||
#define PINOS_SOURCE_OUTPUT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), PINOS_TYPE_SOURCE_OUTPUT, PinosSourceOutputClass))
|
||||
#define PINOS_SOURCE_OUTPUT_CAST(obj) ((PinosSourceOutput*)(obj))
|
||||
#define PINOS_SOURCE_OUTPUT_CLASS_CAST(klass) ((PinosSourceOutputClass*)(klass))
|
||||
|
||||
typedef struct _PinosSourceOutput PinosSourceOutput;
|
||||
typedef struct _PinosSourceOutputClass PinosSourceOutputClass;
|
||||
typedef struct _PinosSourceOutputPrivate PinosSourceOutputPrivate;
|
||||
|
||||
/**
|
||||
* PinosSourceOutput:
|
||||
*
|
||||
* Pinos source output object class.
|
||||
*/
|
||||
struct _PinosSourceOutput {
|
||||
GObject object;
|
||||
|
||||
PinosSourceOutputPrivate *priv;
|
||||
};
|
||||
|
||||
/**
|
||||
* PinosSourceOutputClass:
|
||||
*
|
||||
* Pinos source output object class.
|
||||
*/
|
||||
struct _PinosSourceOutputClass {
|
||||
GObjectClass parent_class;
|
||||
};
|
||||
|
||||
/* normal GObject stuff */
|
||||
GType pinos_source_output_get_type (void);
|
||||
|
||||
void pinos_source_output_remove (PinosSourceOutput *output);
|
||||
|
||||
const gchar * pinos_source_output_get_object_path (PinosSourceOutput *output);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __PINOS_SOURCE_OUTPUT_H__ */
|
||||
|
||||
|
|
@ -1,582 +0,0 @@
|
|||
/* Pinos
|
||||
* Copyright (C) 2015 Wim Taymans <wim.taymans@gmail.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include <gio/gio.h>
|
||||
|
||||
#include "client/pinos.h"
|
||||
#include "client/enumtypes.h"
|
||||
|
||||
#include "server/source.h"
|
||||
#include "server/daemon.h"
|
||||
|
||||
#include "dbus/org-pinos.h"
|
||||
|
||||
|
||||
#define PINOS_SOURCE_GET_PRIVATE(obj) \
|
||||
(G_TYPE_INSTANCE_GET_PRIVATE ((obj), PINOS_TYPE_SOURCE, PinosSourcePrivate))
|
||||
|
||||
struct _PinosSourcePrivate
|
||||
{
|
||||
PinosDaemon *daemon;
|
||||
PinosSource1 *iface;
|
||||
gchar *object_path;
|
||||
|
||||
gchar *name;
|
||||
PinosProperties *properties;
|
||||
|
||||
PinosSourceState state;
|
||||
GError *error;
|
||||
guint idle_timeout;
|
||||
|
||||
GList *outputs;
|
||||
};
|
||||
|
||||
G_DEFINE_ABSTRACT_TYPE (PinosSource, pinos_source, G_TYPE_OBJECT);
|
||||
|
||||
enum
|
||||
{
|
||||
PROP_0,
|
||||
PROP_DAEMON,
|
||||
PROP_OBJECT_PATH,
|
||||
PROP_NAME,
|
||||
PROP_STATE,
|
||||
PROP_PROPERTIES
|
||||
};
|
||||
|
||||
static void
|
||||
pinos_source_get_property (GObject *_object,
|
||||
guint prop_id,
|
||||
GValue *value,
|
||||
GParamSpec *pspec)
|
||||
{
|
||||
PinosSource *source = PINOS_SOURCE (_object);
|
||||
PinosSourcePrivate *priv = source->priv;
|
||||
|
||||
switch (prop_id) {
|
||||
case PROP_DAEMON:
|
||||
g_value_set_object (value, priv->daemon);
|
||||
break;
|
||||
|
||||
case PROP_OBJECT_PATH:
|
||||
g_value_set_string (value, priv->object_path);
|
||||
break;
|
||||
|
||||
case PROP_NAME:
|
||||
g_value_set_string (value, priv->name);
|
||||
break;
|
||||
|
||||
case PROP_STATE:
|
||||
g_value_set_enum (value, priv->state);
|
||||
break;
|
||||
|
||||
case PROP_PROPERTIES:
|
||||
g_value_set_boxed (value, priv->properties);
|
||||
break;
|
||||
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (source, prop_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
pinos_source_set_property (GObject *_object,
|
||||
guint prop_id,
|
||||
const GValue *value,
|
||||
GParamSpec *pspec)
|
||||
{
|
||||
PinosSource *source = PINOS_SOURCE (_object);
|
||||
PinosSourcePrivate *priv = source->priv;
|
||||
|
||||
switch (prop_id) {
|
||||
case PROP_DAEMON:
|
||||
priv->daemon = g_value_dup_object (value);
|
||||
break;
|
||||
|
||||
case PROP_OBJECT_PATH:
|
||||
g_free (priv->object_path);
|
||||
priv->object_path = g_value_dup_string (value);
|
||||
break;
|
||||
|
||||
case PROP_NAME:
|
||||
g_free (priv->name);
|
||||
priv->name = g_value_dup_string (value);
|
||||
break;
|
||||
|
||||
case PROP_PROPERTIES:
|
||||
if (priv->properties)
|
||||
pinos_properties_free (priv->properties);
|
||||
priv->properties = g_value_dup_boxed (value);
|
||||
if (priv->iface)
|
||||
g_object_set (priv->iface,
|
||||
"properties", priv->properties ?
|
||||
pinos_properties_to_variant (priv->properties) : NULL,
|
||||
NULL);
|
||||
break;
|
||||
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (source, prop_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
source_register_object (PinosSource *source)
|
||||
{
|
||||
PinosSourcePrivate *priv = source->priv;
|
||||
PinosDaemon *daemon = priv->daemon;
|
||||
PinosObjectSkeleton *skel;
|
||||
GBytes *formats;
|
||||
GVariant *variant;
|
||||
|
||||
formats = pinos_source_get_formats (source, NULL, NULL);
|
||||
|
||||
skel = pinos_object_skeleton_new (PINOS_DBUS_OBJECT_SOURCE);
|
||||
|
||||
if (priv->properties)
|
||||
variant = pinos_properties_to_variant (priv->properties);
|
||||
else
|
||||
variant = NULL;
|
||||
|
||||
priv->iface = pinos_source1_skeleton_new ();
|
||||
g_object_set (priv->iface, "name", priv->name,
|
||||
"state", priv->state,
|
||||
"properties", variant,
|
||||
"possible-formats", g_bytes_get_data (formats, NULL),
|
||||
NULL);
|
||||
pinos_object_skeleton_set_source1 (skel, priv->iface);
|
||||
g_bytes_unref (formats);
|
||||
|
||||
g_free (priv->object_path);
|
||||
priv->object_path = pinos_daemon_export_uniquely (daemon, G_DBUS_OBJECT_SKELETON (skel));
|
||||
pinos_daemon_add_source (daemon, source);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
static void
|
||||
source_unregister_object (PinosSource *source)
|
||||
{
|
||||
PinosSourcePrivate *priv = source->priv;
|
||||
|
||||
pinos_daemon_remove_source (priv->daemon, source);
|
||||
pinos_daemon_unexport (priv->daemon, priv->object_path);
|
||||
g_clear_object (&priv->iface);
|
||||
}
|
||||
|
||||
static void
|
||||
pinos_source_constructed (GObject * object)
|
||||
{
|
||||
PinosSource *source = PINOS_SOURCE (object);
|
||||
|
||||
source_register_object (source);
|
||||
|
||||
G_OBJECT_CLASS (pinos_source_parent_class)->constructed (object);
|
||||
}
|
||||
|
||||
static void
|
||||
do_remove_output (PinosSourceOutput *output,
|
||||
gpointer user_data)
|
||||
{
|
||||
pinos_source_output_remove (output);
|
||||
}
|
||||
|
||||
static void
|
||||
pinos_source_dispose (GObject * object)
|
||||
{
|
||||
PinosSource *source = PINOS_SOURCE (object);
|
||||
PinosSourcePrivate *priv = source->priv;
|
||||
|
||||
g_list_foreach (priv->outputs, (GFunc) do_remove_output, source);
|
||||
source_unregister_object (source);
|
||||
|
||||
G_OBJECT_CLASS (pinos_source_parent_class)->dispose (object);
|
||||
}
|
||||
|
||||
static void
|
||||
pinos_source_finalize (GObject * object)
|
||||
{
|
||||
PinosSource *source = PINOS_SOURCE (object);
|
||||
PinosSourcePrivate *priv = source->priv;
|
||||
|
||||
g_free (priv->object_path);
|
||||
g_free (priv->name);
|
||||
if (priv->properties)
|
||||
pinos_properties_free (priv->properties);
|
||||
|
||||
G_OBJECT_CLASS (pinos_source_parent_class)->finalize (object);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
default_set_state (PinosSource *source,
|
||||
PinosSourceState state)
|
||||
{
|
||||
pinos_source_update_state (source, state);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
handle_remove_output (PinosSourceOutput *output,
|
||||
gpointer user_data)
|
||||
{
|
||||
PinosSource *source = user_data;
|
||||
|
||||
pinos_source_release_source_output (source, output);
|
||||
}
|
||||
|
||||
static PinosSourceOutput *
|
||||
default_create_source_output (PinosSource *source,
|
||||
const gchar *client_path,
|
||||
GBytes *format_filter,
|
||||
PinosProperties *props,
|
||||
const gchar *prefix,
|
||||
GError **error)
|
||||
{
|
||||
PinosSourcePrivate *priv = source->priv;
|
||||
PinosSourceOutput *output;
|
||||
GBytes *possible_formats;
|
||||
|
||||
possible_formats = pinos_source_get_formats (source, format_filter, error);
|
||||
if (possible_formats == NULL)
|
||||
return NULL;
|
||||
|
||||
output = g_object_new (PINOS_TYPE_SOURCE_OUTPUT, "daemon", priv->daemon,
|
||||
"object-path", prefix,
|
||||
"client-path", client_path,
|
||||
"source-path", priv->object_path,
|
||||
"possible-formats", possible_formats,
|
||||
"properties", props,
|
||||
NULL);
|
||||
g_bytes_unref (possible_formats);
|
||||
|
||||
if (output == NULL)
|
||||
goto no_output;
|
||||
|
||||
g_signal_connect (output,
|
||||
"remove",
|
||||
(GCallback) handle_remove_output,
|
||||
source);
|
||||
|
||||
priv->outputs = g_list_prepend (priv->outputs, output);
|
||||
|
||||
return g_object_ref (output);
|
||||
|
||||
/* ERRORS */
|
||||
no_output:
|
||||
{
|
||||
if (error)
|
||||
*error = g_error_new (G_IO_ERROR,
|
||||
G_IO_ERROR_FAILED,
|
||||
"Could not create a source output");
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static gboolean
|
||||
default_release_source_output (PinosSource *source,
|
||||
PinosSourceOutput *output)
|
||||
{
|
||||
PinosSourcePrivate *priv = source->priv;
|
||||
GList *find;
|
||||
|
||||
find = g_list_find (priv->outputs, output);
|
||||
if (find == NULL)
|
||||
return FALSE;
|
||||
|
||||
priv->outputs = g_list_delete_link (priv->outputs, find);
|
||||
g_object_unref (output);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
pinos_source_class_init (PinosSourceClass * klass)
|
||||
{
|
||||
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
|
||||
|
||||
g_type_class_add_private (klass, sizeof (PinosSourcePrivate));
|
||||
|
||||
gobject_class->constructed = pinos_source_constructed;
|
||||
gobject_class->dispose = pinos_source_dispose;
|
||||
gobject_class->finalize = pinos_source_finalize;
|
||||
gobject_class->set_property = pinos_source_set_property;
|
||||
gobject_class->get_property = pinos_source_get_property;
|
||||
|
||||
g_object_class_install_property (gobject_class,
|
||||
PROP_DAEMON,
|
||||
g_param_spec_object ("daemon",
|
||||
"Daemon",
|
||||
"The Daemon",
|
||||
PINOS_TYPE_DAEMON,
|
||||
G_PARAM_READWRITE |
|
||||
G_PARAM_CONSTRUCT_ONLY |
|
||||
G_PARAM_STATIC_STRINGS));
|
||||
|
||||
g_object_class_install_property (gobject_class,
|
||||
PROP_OBJECT_PATH,
|
||||
g_param_spec_string ("object-path",
|
||||
"Object Path",
|
||||
"The object path",
|
||||
NULL,
|
||||
G_PARAM_READWRITE |
|
||||
G_PARAM_CONSTRUCT_ONLY |
|
||||
G_PARAM_STATIC_STRINGS));
|
||||
|
||||
g_object_class_install_property (gobject_class,
|
||||
PROP_NAME,
|
||||
g_param_spec_string ("name",
|
||||
"Name",
|
||||
"The source name",
|
||||
NULL,
|
||||
G_PARAM_READWRITE |
|
||||
G_PARAM_CONSTRUCT_ONLY |
|
||||
G_PARAM_STATIC_STRINGS));
|
||||
|
||||
g_object_class_install_property (gobject_class,
|
||||
PROP_STATE,
|
||||
g_param_spec_enum ("state",
|
||||
"State",
|
||||
"The state of the source",
|
||||
PINOS_TYPE_SOURCE_STATE,
|
||||
PINOS_SOURCE_STATE_SUSPENDED,
|
||||
G_PARAM_READABLE |
|
||||
G_PARAM_STATIC_STRINGS));
|
||||
|
||||
g_object_class_install_property (gobject_class,
|
||||
PROP_PROPERTIES,
|
||||
g_param_spec_boxed ("properties",
|
||||
"Properties",
|
||||
"The properties of the source",
|
||||
PINOS_TYPE_PROPERTIES,
|
||||
G_PARAM_READWRITE |
|
||||
G_PARAM_CONSTRUCT |
|
||||
G_PARAM_STATIC_STRINGS));
|
||||
|
||||
|
||||
klass->set_state = default_set_state;
|
||||
klass->create_source_output = default_create_source_output;
|
||||
klass->release_source_output = default_release_source_output;
|
||||
}
|
||||
|
||||
static void
|
||||
pinos_source_init (PinosSource * source)
|
||||
{
|
||||
PinosSourcePrivate *priv = source->priv = PINOS_SOURCE_GET_PRIVATE (source);
|
||||
|
||||
priv->state = PINOS_SOURCE_STATE_SUSPENDED;
|
||||
}
|
||||
|
||||
GBytes *
|
||||
pinos_source_get_formats (PinosSource *source,
|
||||
GBytes *filter,
|
||||
GError **error)
|
||||
{
|
||||
PinosSourceClass *klass;
|
||||
GBytes *res;
|
||||
|
||||
g_return_val_if_fail (PINOS_IS_SOURCE (source), NULL);
|
||||
|
||||
klass = PINOS_SOURCE_GET_CLASS (source);
|
||||
|
||||
if (klass->get_formats)
|
||||
res = klass->get_formats (source, filter, error);
|
||||
else {
|
||||
res = NULL;
|
||||
if (error)
|
||||
*error = g_error_new (G_IO_ERROR,
|
||||
G_IO_ERROR_NOT_SUPPORTED,
|
||||
"Format query is not supported");
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
static void
|
||||
remove_idle_timeout (PinosSource *source)
|
||||
{
|
||||
PinosSourcePrivate *priv = source->priv;
|
||||
|
||||
if (priv->idle_timeout) {
|
||||
g_source_remove (priv->idle_timeout);
|
||||
priv->idle_timeout = 0;
|
||||
}
|
||||
}
|
||||
|
||||
gboolean
|
||||
pinos_source_set_state (PinosSource *source,
|
||||
PinosSourceState state)
|
||||
{
|
||||
PinosSourceClass *klass;
|
||||
gboolean res;
|
||||
|
||||
g_return_val_if_fail (PINOS_IS_SOURCE (source), FALSE);
|
||||
|
||||
klass = PINOS_SOURCE_GET_CLASS (source);
|
||||
|
||||
remove_idle_timeout (source);
|
||||
|
||||
if (klass->set_state)
|
||||
res = klass->set_state (source, state);
|
||||
else
|
||||
res = FALSE;
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
void
|
||||
pinos_source_update_state (PinosSource *source,
|
||||
PinosSourceState state)
|
||||
{
|
||||
PinosSourcePrivate *priv;
|
||||
|
||||
g_return_if_fail (PINOS_IS_SOURCE (source));
|
||||
priv = source->priv;
|
||||
|
||||
if (priv->state != state) {
|
||||
priv->state = state;
|
||||
pinos_source1_set_state (priv->iface, state);
|
||||
g_object_notify (G_OBJECT (source), "state");
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
pinos_source_report_error (PinosSource *source,
|
||||
GError *error)
|
||||
{
|
||||
PinosSourcePrivate *priv;
|
||||
|
||||
g_return_if_fail (PINOS_IS_SOURCE (source));
|
||||
priv = source->priv;
|
||||
|
||||
g_clear_error (&priv->error);
|
||||
remove_idle_timeout (source);
|
||||
priv->error = error;
|
||||
priv->state = PINOS_SOURCE_STATE_ERROR;
|
||||
g_debug ("got error state %s", error->message);
|
||||
pinos_source1_set_state (priv->iface, priv->state);
|
||||
g_object_notify (G_OBJECT (source), "state");
|
||||
}
|
||||
|
||||
static gboolean
|
||||
idle_timeout (PinosSource *source)
|
||||
{
|
||||
PinosSourcePrivate *priv = source->priv;
|
||||
|
||||
priv->idle_timeout = 0;
|
||||
pinos_source_set_state (source, PINOS_SOURCE_STATE_SUSPENDED);
|
||||
|
||||
return G_SOURCE_REMOVE;
|
||||
}
|
||||
|
||||
void
|
||||
pinos_source_report_idle (PinosSource *source)
|
||||
{
|
||||
PinosSourcePrivate *priv;
|
||||
|
||||
g_return_if_fail (PINOS_IS_SOURCE (source));
|
||||
priv = source->priv;
|
||||
|
||||
pinos_source_set_state (source, PINOS_SOURCE_STATE_IDLE);
|
||||
|
||||
priv->idle_timeout = g_timeout_add_seconds (3,
|
||||
(GSourceFunc) idle_timeout,
|
||||
source);
|
||||
}
|
||||
|
||||
void
|
||||
pinos_source_report_busy (PinosSource *source)
|
||||
{
|
||||
g_return_if_fail (PINOS_IS_SOURCE (source));
|
||||
|
||||
pinos_source_set_state (source, PINOS_SOURCE_STATE_RUNNING);
|
||||
}
|
||||
|
||||
void
|
||||
pinos_source_update_possible_formats (PinosSource *source, GBytes *formats)
|
||||
{
|
||||
PinosSourcePrivate *priv;
|
||||
|
||||
g_return_if_fail (PINOS_IS_SOURCE (source));
|
||||
priv = source->priv;
|
||||
|
||||
if (priv->iface)
|
||||
g_object_set (priv->iface, "possible-formats",
|
||||
g_bytes_get_data (formats, NULL),
|
||||
NULL);
|
||||
}
|
||||
|
||||
PinosSourceOutput *
|
||||
pinos_source_create_source_output (PinosSource *source,
|
||||
const gchar *client_path,
|
||||
GBytes *format_filter,
|
||||
PinosProperties *props,
|
||||
const gchar *prefix,
|
||||
GError **error)
|
||||
{
|
||||
PinosSourceClass *klass;
|
||||
PinosSourceOutput *res;
|
||||
|
||||
g_return_val_if_fail (PINOS_IS_SOURCE (source), NULL);
|
||||
|
||||
klass = PINOS_SOURCE_GET_CLASS (source);
|
||||
|
||||
if (klass->create_source_output) {
|
||||
res = klass->create_source_output (source, client_path, format_filter, props, prefix, error);
|
||||
} else {
|
||||
if (error) {
|
||||
*error = g_error_new (G_IO_ERROR,
|
||||
G_IO_ERROR_NOT_SUPPORTED,
|
||||
"Create SourceOutput not implemented");
|
||||
}
|
||||
res = NULL;
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
gboolean
|
||||
pinos_source_release_source_output (PinosSource *source,
|
||||
PinosSourceOutput *output)
|
||||
{
|
||||
PinosSourceClass *klass;
|
||||
gboolean res;
|
||||
|
||||
g_return_val_if_fail (PINOS_IS_SOURCE (source), FALSE);
|
||||
g_return_val_if_fail (PINOS_IS_SOURCE_OUTPUT (output), FALSE);
|
||||
|
||||
klass = PINOS_SOURCE_GET_CLASS (source);
|
||||
|
||||
if (klass->release_source_output)
|
||||
res = klass->release_source_output (source, output);
|
||||
else
|
||||
res = FALSE;
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
const gchar *
|
||||
pinos_source_get_object_path (PinosSource *source)
|
||||
{
|
||||
PinosSourcePrivate *priv;
|
||||
|
||||
g_return_val_if_fail (PINOS_IS_SOURCE (source), NULL);
|
||||
priv = source->priv;
|
||||
|
||||
return priv->object_path;
|
||||
}
|
||||
|
||||
|
|
@ -1,111 +0,0 @@
|
|||
/* Pinos
|
||||
* Copyright (C) 2015 Wim Taymans <wim.taymans@gmail.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#ifndef __PINOS_SOURCE_H__
|
||||
#define __PINOS_SOURCE_H__
|
||||
|
||||
#include <glib-object.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
typedef struct _PinosSource PinosSource;
|
||||
typedef struct _PinosSourceClass PinosSourceClass;
|
||||
typedef struct _PinosSourcePrivate PinosSourcePrivate;
|
||||
|
||||
#include <client/introspect.h>
|
||||
#include <server/source-output.h>
|
||||
|
||||
#define PINOS_TYPE_SOURCE (pinos_source_get_type ())
|
||||
#define PINOS_IS_SOURCE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), PINOS_TYPE_SOURCE))
|
||||
#define PINOS_IS_SOURCE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), PINOS_TYPE_SOURCE))
|
||||
#define PINOS_SOURCE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), PINOS_TYPE_SOURCE, PinosSourceClass))
|
||||
#define PINOS_SOURCE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), PINOS_TYPE_SOURCE, PinosSource))
|
||||
#define PINOS_SOURCE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), PINOS_TYPE_SOURCE, PinosSourceClass))
|
||||
#define PINOS_SOURCE_CAST(obj) ((PinosSource*)(obj))
|
||||
#define PINOS_SOURCE_CLASS_CAST(klass) ((PinosSourceClass*)(klass))
|
||||
|
||||
/**
|
||||
* PinosSource:
|
||||
*
|
||||
* Pinos source object class.
|
||||
*/
|
||||
struct _PinosSource {
|
||||
GObject object;
|
||||
|
||||
PinosSourcePrivate *priv;
|
||||
};
|
||||
|
||||
/**
|
||||
* PinosSourceClass:
|
||||
* @get_formats: called to get a list of supported formats from the source
|
||||
* @set_state: called to change the current state of the source
|
||||
* @create_source_output: called to create a new source-output object
|
||||
* @release_source_output: called to release a source-output object
|
||||
*
|
||||
* Pinos source object class.
|
||||
*/
|
||||
struct _PinosSourceClass {
|
||||
GObjectClass parent_class;
|
||||
|
||||
GBytes * (*get_formats) (PinosSource *source,
|
||||
GBytes *filter,
|
||||
GError **error);
|
||||
|
||||
gboolean (*set_state) (PinosSource *source, PinosSourceState);
|
||||
|
||||
PinosSourceOutput * (*create_source_output) (PinosSource *source,
|
||||
const gchar *client_path,
|
||||
GBytes *format_filter,
|
||||
PinosProperties *props,
|
||||
const gchar *prefix,
|
||||
GError **error);
|
||||
gboolean (*release_source_output) (PinosSource *source,
|
||||
PinosSourceOutput *output);
|
||||
};
|
||||
|
||||
/* normal GObject stuff */
|
||||
GType pinos_source_get_type (void);
|
||||
|
||||
const gchar * pinos_source_get_object_path (PinosSource *source);
|
||||
|
||||
GBytes * pinos_source_get_formats (PinosSource *source,
|
||||
GBytes *filter,
|
||||
GError **error);
|
||||
|
||||
gboolean pinos_source_set_state (PinosSource *source, PinosSourceState state);
|
||||
void pinos_source_update_state (PinosSource *source, PinosSourceState state);
|
||||
void pinos_source_report_error (PinosSource *source, GError *error);
|
||||
void pinos_source_report_idle (PinosSource *source);
|
||||
void pinos_source_report_busy (PinosSource *source);
|
||||
|
||||
void pinos_source_update_possible_formats (PinosSource *source, GBytes *formats);
|
||||
|
||||
PinosSourceOutput * pinos_source_create_source_output (PinosSource *source,
|
||||
const gchar *client_path,
|
||||
GBytes *format_filter,
|
||||
PinosProperties *props,
|
||||
const gchar *prefix,
|
||||
GError **error);
|
||||
gboolean pinos_source_release_source_output (PinosSource *source,
|
||||
PinosSourceOutput *output);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __PINOS_SOURCE_H__ */
|
||||
|
||||
|
|
@ -1,173 +0,0 @@
|
|||
/* Pinos
|
||||
* Copyright (C) 2015 Wim Taymans <wim.taymans@gmail.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include <gst/gst.h>
|
||||
#include <gio/gio.h>
|
||||
|
||||
#include <client/pinos.h>
|
||||
|
||||
#define ANY_CAPS "ANY"
|
||||
|
||||
static GMainLoop *loop;
|
||||
|
||||
static void
|
||||
on_socket_notify (GObject *gobject,
|
||||
GParamSpec *pspec,
|
||||
gpointer user_data)
|
||||
{
|
||||
GSocket *socket;
|
||||
GstElement *pipeline, *src, *filter;
|
||||
GBytes *format;
|
||||
GstCaps *caps;
|
||||
GError *error = NULL;
|
||||
|
||||
pipeline = gst_parse_launch ("socketsrc name=src ! pinosdepay ! capsfilter name=filter ! videoconvert ! xvimagesink", &error);
|
||||
if (error != NULL) {
|
||||
g_warning ("error creating pipeline: %s", error->message);
|
||||
g_clear_error (&error);
|
||||
g_assert (pipeline != NULL);
|
||||
}
|
||||
|
||||
/* configure socket in the socketsrc */
|
||||
g_object_get (gobject, "socket", &socket, NULL);
|
||||
g_print ("got socket %p\n", socket);
|
||||
src = gst_bin_get_by_name (GST_BIN (pipeline), "src");
|
||||
g_object_set (src, "socket", socket, NULL);
|
||||
|
||||
/* configure format as capsfilter */
|
||||
g_object_get (gobject, "format", &format, NULL);
|
||||
caps = gst_caps_from_string (g_bytes_get_data (format, NULL));
|
||||
g_bytes_unref (format);
|
||||
filter = gst_bin_get_by_name (GST_BIN (pipeline), "filter");
|
||||
g_object_set (filter, "caps", caps, NULL);
|
||||
gst_caps_unref (caps);
|
||||
|
||||
/* and set to playing */
|
||||
gst_element_set_state (pipeline, GST_STATE_PLAYING);
|
||||
}
|
||||
|
||||
static void
|
||||
on_stream_notify (GObject *gobject,
|
||||
GParamSpec *pspec,
|
||||
gpointer user_data)
|
||||
{
|
||||
PinosStreamState state;
|
||||
PinosStream *s = user_data;
|
||||
|
||||
g_object_get (gobject, "state", &state, NULL);
|
||||
g_print ("got stream state %d\n", state);
|
||||
|
||||
switch (state) {
|
||||
case PINOS_STREAM_STATE_ERROR:
|
||||
g_main_loop_quit (loop);
|
||||
break;
|
||||
|
||||
case PINOS_STREAM_STATE_READY:
|
||||
{
|
||||
GBytes *possible, *format;
|
||||
GstCaps *caps;
|
||||
GstStructure *structure;
|
||||
gchar *str;
|
||||
|
||||
g_object_get (s, "possible-formats", &possible, NULL);
|
||||
|
||||
caps = gst_caps_from_string (g_bytes_get_data (possible, NULL));
|
||||
|
||||
structure = gst_caps_get_structure (caps, 0);
|
||||
|
||||
/* set some reasonable defaults */
|
||||
if (gst_structure_has_field (structure, "width"))
|
||||
gst_structure_fixate_field_nearest_int (structure, "width", 320);
|
||||
if (gst_structure_has_field (structure, "height"))
|
||||
gst_structure_fixate_field_nearest_int (structure, "height", 240);
|
||||
if (gst_structure_has_field (structure, "framerate"))
|
||||
gst_structure_fixate_field_nearest_fraction (structure, "framerate", 30, 1);
|
||||
|
||||
/* use random fixation otherwise */
|
||||
caps = gst_caps_fixate (caps);
|
||||
str = gst_caps_to_string (caps);
|
||||
gst_caps_unref (caps);
|
||||
|
||||
format = g_bytes_new_static (str, strlen (str) + 1);
|
||||
pinos_stream_start (s, format, PINOS_STREAM_MODE_SOCKET);
|
||||
break;
|
||||
}
|
||||
|
||||
case PINOS_STREAM_STATE_STREAMING:
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
on_state_notify (GObject *gobject,
|
||||
GParamSpec *pspec,
|
||||
gpointer user_data)
|
||||
{
|
||||
PinosContextState state;
|
||||
PinosContext *c = user_data;
|
||||
|
||||
g_object_get (gobject, "state", &state, NULL);
|
||||
g_print ("got context state %d\n", state);
|
||||
|
||||
switch (state) {
|
||||
case PINOS_CONTEXT_STATE_ERROR:
|
||||
g_main_loop_quit (loop);
|
||||
break;
|
||||
case PINOS_CONTEXT_STATE_READY:
|
||||
{
|
||||
PinosStream *stream;
|
||||
GBytes *format;
|
||||
|
||||
stream = pinos_stream_new (c, "test", NULL);
|
||||
g_signal_connect (stream, "notify::state", (GCallback) on_stream_notify, stream);
|
||||
g_signal_connect (stream, "notify::socket", (GCallback) on_socket_notify, stream);
|
||||
|
||||
format = g_bytes_new_static (ANY_CAPS, strlen (ANY_CAPS) + 1);
|
||||
pinos_stream_connect_capture (stream, NULL, 0, format);
|
||||
g_bytes_unref (format);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
gint
|
||||
main (gint argc, gchar *argv[])
|
||||
{
|
||||
PinosContext *c;
|
||||
|
||||
pinos_init (&argc, &argv);
|
||||
|
||||
loop = g_main_loop_new (NULL, FALSE);
|
||||
|
||||
c = pinos_context_new (NULL, "test-client", NULL);
|
||||
g_signal_connect (c, "notify::state", (GCallback) on_state_notify, c);
|
||||
pinos_context_connect(c, PINOS_CONTEXT_FLAGS_NONE);
|
||||
|
||||
g_main_loop_run (loop);
|
||||
|
||||
g_object_unref (c);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -1,259 +0,0 @@
|
|||
/* Pinos
|
||||
* Copyright (C) 2015 Wim Taymans <wim.taymans@gmail.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include <gst/gst.h>
|
||||
|
||||
#include <client/pinos.h>
|
||||
|
||||
static GMainLoop *loop;
|
||||
|
||||
static gboolean
|
||||
print_field (GQuark field, const GValue * value, gpointer user_data)
|
||||
{
|
||||
gchar *str = gst_value_serialize (value);
|
||||
|
||||
g_print ("\t\t%15s: %s\n", g_quark_to_string (field), str);
|
||||
g_free (str);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
print_formats (const gchar *name, GBytes *formats, gchar mark)
|
||||
{
|
||||
GstCaps *caps = gst_caps_from_string (g_bytes_get_data (formats, NULL));
|
||||
guint i;
|
||||
|
||||
g_print ("%c\t%s:\n", mark, name);
|
||||
|
||||
if (gst_caps_is_any (caps)) {
|
||||
g_print ("\t\tANY\n");
|
||||
return;
|
||||
}
|
||||
if (gst_caps_is_empty (caps)) {
|
||||
g_print ("\t\tEMPTY\n");
|
||||
return;
|
||||
}
|
||||
for (i = 0; i < gst_caps_get_size (caps); i++) {
|
||||
GstStructure *structure = gst_caps_get_structure (caps, i);
|
||||
GstCapsFeatures *features = gst_caps_get_features (caps, i);
|
||||
|
||||
if (features && (gst_caps_features_is_any (features) ||
|
||||
!gst_caps_features_is_equal (features,
|
||||
GST_CAPS_FEATURES_MEMORY_SYSTEM_MEMORY))) {
|
||||
gchar *features_string = gst_caps_features_to_string (features);
|
||||
|
||||
g_print ("\t\t%s(%s)\n", gst_structure_get_name (structure),
|
||||
features_string);
|
||||
g_free (features_string);
|
||||
} else {
|
||||
g_print ("\t\t%s\n", gst_structure_get_name (structure));
|
||||
}
|
||||
gst_structure_foreach (structure, print_field, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
print_properties (PinosProperties *props, gchar mark)
|
||||
{
|
||||
gpointer state = NULL;
|
||||
const gchar *key;
|
||||
|
||||
g_print ("%c\tproperties:\n", mark);
|
||||
while ((key = pinos_properties_iterate (props, &state))) {
|
||||
g_print ("\t\t%s = \"%s\"\n", key, pinos_properties_get (props, key));
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
info_ready (GObject *o, GAsyncResult *res, gpointer user_data)
|
||||
{
|
||||
GError *error = NULL;
|
||||
|
||||
if (!pinos_context_info_finish (o, res, &error)) {
|
||||
g_printerr ("introspection failure: %s\n", error->message);
|
||||
g_clear_error (&error);
|
||||
}
|
||||
}
|
||||
|
||||
#define MARK_CHANGE(f) ((print_mark && ((info)->change_mask & (1 << (f)))) ? '*' : ' ')
|
||||
|
||||
static void
|
||||
dump_daemon_info (PinosContext *c, const PinosDaemonInfo *info, gpointer user_data)
|
||||
{
|
||||
gboolean print_mark = *((gboolean *) user_data);
|
||||
|
||||
g_print ("\tid: %p\n", info->id);
|
||||
g_print ("\tdaemon-path: \"%s\"\n", info->daemon_path);
|
||||
g_print ("%c\tuser-name: \"%s\"\n", MARK_CHANGE (0), info->user_name);
|
||||
g_print ("%c\thost-name: \"%s\"\n", MARK_CHANGE (1), info->host_name);
|
||||
g_print ("%c\tversion: \"%s\"\n", MARK_CHANGE (2), info->version);
|
||||
g_print ("%c\tname: \"%s\"\n", MARK_CHANGE (3), info->name);
|
||||
g_print ("%c\tcookie: %d\n", MARK_CHANGE (4), info->cookie);
|
||||
print_properties (info->properties, MARK_CHANGE (5));
|
||||
}
|
||||
|
||||
static void
|
||||
dump_client_info (PinosContext *c, const PinosClientInfo *info, gpointer user_data)
|
||||
{
|
||||
gboolean print_mark = *((gboolean *) user_data);
|
||||
|
||||
g_print ("\tid: %p\n", info->id);
|
||||
g_print ("\tclient-path: \"%s\"\n", info->client_path);
|
||||
g_print ("%c\tname: \"%s\"\n", MARK_CHANGE (0), info->name);
|
||||
print_properties (info->properties, MARK_CHANGE (1));
|
||||
}
|
||||
|
||||
static void
|
||||
dump_source_info (PinosContext *c, const PinosSourceInfo *info, gpointer user_data)
|
||||
{
|
||||
gboolean print_mark = *((gboolean *) user_data);
|
||||
|
||||
g_print ("\tid: %p\n", info->id);
|
||||
g_print ("\tsource-path: \"%s\"\n", info->source_path);
|
||||
g_print ("%c\tname: \"%s\"\n", MARK_CHANGE (0), info->name);
|
||||
print_properties (info->properties, MARK_CHANGE (1));
|
||||
g_print ("%c\tstate: \"%s\"\n", MARK_CHANGE (2), pinos_source_state_as_string (info->state));
|
||||
print_formats ("possible formats", info->possible_formats, MARK_CHANGE (3));
|
||||
}
|
||||
|
||||
static void
|
||||
dump_source_output_info (PinosContext *c, const PinosSourceOutputInfo *info, gpointer user_data)
|
||||
{
|
||||
gboolean print_mark = *((gboolean *) user_data);
|
||||
|
||||
g_print ("\tid: %p\n", info->id);
|
||||
g_print ("\toutput-path: \"%s\"\n", info->output_path);
|
||||
g_print ("%c\tclient-path: \"%s\"\n", MARK_CHANGE (0), info->client_path);
|
||||
g_print ("%c\tsource-path: \"%s\"\n", MARK_CHANGE (1), info->source_path);
|
||||
print_formats ("possible-formats", info->possible_formats, MARK_CHANGE (2));
|
||||
g_print ("%c\tstate: \"%s\"\n", MARK_CHANGE (3), pinos_source_output_state_as_string (info->state));
|
||||
print_formats ("format", info->format, MARK_CHANGE (4));
|
||||
print_properties (info->properties, MARK_CHANGE (5));
|
||||
}
|
||||
|
||||
static void
|
||||
dump_object (PinosContext *context, gpointer id, PinosSubscriptionFlags flags,
|
||||
gboolean print_mark)
|
||||
{
|
||||
if (flags & PINOS_SUBSCRIPTION_FLAG_DAEMON) {
|
||||
pinos_context_get_daemon_info (context,
|
||||
PINOS_DAEMON_INFO_FLAGS_NONE,
|
||||
dump_daemon_info,
|
||||
NULL,
|
||||
info_ready,
|
||||
&print_mark);
|
||||
}
|
||||
else if (flags & PINOS_SUBSCRIPTION_FLAG_CLIENT) {
|
||||
pinos_context_get_client_info_by_id (context,
|
||||
id,
|
||||
PINOS_CLIENT_INFO_FLAGS_NONE,
|
||||
dump_client_info,
|
||||
NULL,
|
||||
info_ready,
|
||||
&print_mark);
|
||||
}
|
||||
else if (flags & PINOS_SUBSCRIPTION_FLAG_SOURCE) {
|
||||
pinos_context_get_source_info_by_id (context,
|
||||
id,
|
||||
PINOS_SOURCE_INFO_FLAGS_FORMATS,
|
||||
dump_source_info,
|
||||
NULL,
|
||||
info_ready,
|
||||
&print_mark);
|
||||
}
|
||||
else if (flags & PINOS_SUBSCRIPTION_FLAG_SOURCE_OUTPUT) {
|
||||
pinos_context_get_source_output_info_by_id (context,
|
||||
id,
|
||||
PINOS_SOURCE_OUTPUT_INFO_FLAGS_NONE,
|
||||
dump_source_output_info,
|
||||
NULL,
|
||||
info_ready,
|
||||
&print_mark);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
subscription_cb (PinosContext *context,
|
||||
PinosSubscriptionEvent type,
|
||||
PinosSubscriptionFlags flags,
|
||||
gpointer id,
|
||||
gpointer user_data)
|
||||
{
|
||||
switch (type) {
|
||||
case PINOS_SUBSCRIPTION_EVENT_NEW:
|
||||
g_print ("added:\n");
|
||||
dump_object (context, id, flags, FALSE);
|
||||
break;
|
||||
|
||||
case PINOS_SUBSCRIPTION_EVENT_CHANGE:
|
||||
g_print ("changed:\n");
|
||||
dump_object (context, id, flags, TRUE);
|
||||
break;
|
||||
|
||||
case PINOS_SUBSCRIPTION_EVENT_REMOVE:
|
||||
g_print ("removed:\n");
|
||||
dump_object (context, id, flags, FALSE);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
on_state_notify (GObject *gobject,
|
||||
GParamSpec *pspec,
|
||||
gpointer user_data)
|
||||
{
|
||||
PinosContextState state;
|
||||
PinosContext *c = PINOS_CONTEXT (gobject);
|
||||
const GError *error;
|
||||
|
||||
g_object_get (c, "state", &state, NULL);
|
||||
|
||||
switch (state) {
|
||||
case PINOS_CONTEXT_STATE_ERROR:
|
||||
error = pinos_context_get_error (c);
|
||||
g_print ("context error: %s\n", error->message);
|
||||
break;
|
||||
|
||||
default:
|
||||
g_print ("context state: \"%s\"\n", pinos_context_state_as_string (state));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
gint
|
||||
main (gint argc, gchar *argv[])
|
||||
{
|
||||
PinosContext *c;
|
||||
|
||||
pinos_init (&argc, &argv);
|
||||
|
||||
loop = g_main_loop_new (NULL, FALSE);
|
||||
|
||||
c = pinos_context_new (NULL, "pinos-monitor", NULL);
|
||||
g_signal_connect (c, "notify::state", (GCallback) on_state_notify, c);
|
||||
g_object_set (c, "subscription-mask", PINOS_SUBSCRIPTION_FLAGS_ALL, NULL);
|
||||
g_signal_connect (c, "subscription-event", (GCallback) subscription_cb, NULL);
|
||||
pinos_context_connect(c, PINOS_CONTEXT_FLAGS_NOFAIL);
|
||||
|
||||
g_main_loop_run (loop);
|
||||
|
||||
g_object_unref (c);
|
||||
|
||||
return 0;
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue