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

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