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:
Wim Taymans 2016-02-01 15:40:48 +01:00
parent f4bd013dce
commit cdb2028f9b
57 changed files with 77 additions and 104 deletions

3
src/.gitignore vendored
View file

@ -1,3 +0,0 @@
pinos
test-client
pinos-monitor

View file

@ -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

View file

@ -1,2 +0,0 @@
enumtypes.c
enumtypes.h

View file

@ -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;
}

View file

@ -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__ */

View file

@ -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;
}

View file

@ -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__ */

View file

@ -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);
}

View file

@ -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__ */

View file

@ -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;
}

View file

@ -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__ */

View file

@ -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);
}

View file

@ -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__ */

View file

@ -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)

View file

@ -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);

View file

@ -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__ */

File diff suppressed because it is too large Load diff

View file

@ -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__ */

View file

@ -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);
}

View file

@ -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__ */

View file

@ -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;
}

View file

@ -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>

View file

@ -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
View file

@ -1,2 +0,0 @@
org-pinos.c
org-pinos.h

View file

@ -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>

View file

@ -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")

View file

@ -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 ();
}

View file

@ -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

View file

@ -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 ();
}

View file

@ -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__ */

View file

@ -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, &params);
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");
}

View file

@ -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_ */

View file

@ -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, &params);
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;
}
}

View file

@ -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__ */

View file

@ -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;
}
}

View file

@ -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__ */

View file

@ -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);
}

View file

@ -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_ */

View file

@ -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);
}

View file

@ -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__ */

View file

@ -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;
}

View file

@ -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__ */

View file

@ -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);
}

View file

@ -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__ */

View file

@ -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;
}

View file

@ -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__ */

View file

@ -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);
}

View file

@ -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__ */

View file

@ -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;
}

View file

@ -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__ */

View file

@ -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;
}

View file

@ -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__ */

View file

@ -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;
}

View file

@ -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;
}