From e87cd0a29a735a536166e0849095ec249c68c2fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Olivier=20Cr=C3=AAte?= Date: Wed, 29 Apr 2009 16:33:46 -0400 Subject: [PATCH] shm: Add shm based sink and src These two elements (shmsink and shmsrc) communicate buffers using POSIX shared memory. They also communicate the caps. The source currently acts as a live source and ignores the timestamps coming from the sink. It also does not transfer the tags. --- configure.ac | 2 + gst/shm/Makefile.am | 13 ++ gst/shm/gstshm.c | 42 +++++ gst/shm/gstshm.h | 64 +++++++ gst/shm/gstshmsink.c | 420 +++++++++++++++++++++++++++++++++++++++++++ gst/shm/gstshmsink.h | 66 +++++++ gst/shm/gstshmsrc.c | 393 ++++++++++++++++++++++++++++++++++++++++ gst/shm/gstshmsrc.h | 67 +++++++ 8 files changed, 1067 insertions(+) create mode 100644 gst/shm/Makefile.am create mode 100644 gst/shm/gstshm.c create mode 100644 gst/shm/gstshm.h create mode 100644 gst/shm/gstshmsink.c create mode 100644 gst/shm/gstshmsink.h create mode 100644 gst/shm/gstshmsrc.c create mode 100644 gst/shm/gstshmsrc.h diff --git a/configure.ac b/configure.ac index 70208fa6d3..8edeb8abc3 100644 --- a/configure.ac +++ b/configure.ac @@ -332,6 +332,7 @@ AG_GST_CHECK_PLUGIN(scaletempo) AG_GST_CHECK_PLUGIN(sdp) AG_GST_CHECK_PLUGIN(segmentclip) AG_GST_CHECK_PLUGIN(selector) +AG_GST_CHECK_PLUGIN(shm) AG_GST_CHECK_PLUGIN(siren) AG_GST_CHECK_PLUGIN(speed) AG_GST_CHECK_PLUGIN(subenc) @@ -1704,6 +1705,7 @@ gst/scaletempo/Makefile gst/sdp/Makefile gst/segmentclip/Makefile gst/selector/Makefile +gst/shm/Makefile gst/siren/Makefile gst/speed/Makefile gst/subenc/Makefile diff --git a/gst/shm/Makefile.am b/gst/shm/Makefile.am new file mode 100644 index 0000000000..84a26c020d --- /dev/null +++ b/gst/shm/Makefile.am @@ -0,0 +1,13 @@ +glib_enum_prefix = gst_shm + +include $(top_srcdir)/common/glib-gen.mak + +plugin_LTLIBRARIES = libgstshm.la + +libgstshm_la_SOURCES = gstshm.c gstshmsrc.c gstshmsink.c +libgstshm_la_CFLAGS = $(GST_CFLAGS) +libgstshm_la_LIBADD = +libgstshm_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS) $(GST_BASE_LIBS) $(GST_PLUGINS_BASE_LIBS) +libgstshm_la_LIBTOOLFLAGS = --tag=disable-static + +noinst_HEADERS = gstshmsrc.h gstshmsink.h diff --git a/gst/shm/gstshm.c b/gst/shm/gstshm.c new file mode 100644 index 0000000000..d57db67551 --- /dev/null +++ b/gst/shm/gstshm.c @@ -0,0 +1,42 @@ +/* GStreamer + * Copyright (C) <2009> Collabora Ltd + * @author: Olivier Crete Nokia Inc + * + * 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 "gstshmsrc.h" +#include "gstshmsink.h" + +static gboolean +plugin_init (GstPlugin * plugin) +{ + return gst_element_register (plugin, "shmsrc", + GST_RANK_NONE, GST_TYPE_SHM_SRC) && + gst_element_register (plugin, "shmsink", + GST_RANK_NONE, GST_TYPE_SHM_SINK); +} + +GST_PLUGIN_DEFINE (GST_VERSION_MAJOR, + GST_VERSION_MINOR, + "shm", + "shared memory sink source", + plugin_init, VERSION, GST_LICENSE, GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN) diff --git a/gst/shm/gstshm.h b/gst/shm/gstshm.h new file mode 100644 index 0000000000..601cfde0a6 --- /dev/null +++ b/gst/shm/gstshm.h @@ -0,0 +1,64 @@ + +/* GStreamer + * Copyright (C) <2009> Collabora Ltd + * @author: Olivier Crete Nokia Inc + * + * 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_SHM_H__ +#define __GST_SHM_H__ + +#include +#include + +G_BEGIN_DECLS +#define GST_SHM_MAX_CAPS_LENGTH (1024) +#define SHM_LOCK(shm) sem_wait (&(shm)->mutex); +#define SHM_UNLOCK(shm) sem_post (&(shm)->mutex); +#define GST_SHM_CAPS_BUFFER(shm) ((shm)->data) +#define GST_SHM_BUFFER(shm) ((shm)->data+(shm)->caps_size) + struct GstShmHeader +{ + sem_t notification; + sem_t mutex; + + guint caps_gen; + guint buffer_gen; + + gint caps_size; + gint buffer_size; + + guint flags; + + GstClockTime timestamp; + GstClockTime duration; + + guint64 offset; + guint64 offset_end; + + gboolean eos; + + gchar data[0]; + /* + * gchar caps_buffer[caps_size]; + * gchar buffer[buffer_size]; + */ +}; + +G_END_DECLS +#endif /* __GST_SHM_H__ */ diff --git a/gst/shm/gstshmsink.c b/gst/shm/gstshmsink.c new file mode 100644 index 0000000000..48332e60dc --- /dev/null +++ b/gst/shm/gstshmsink.c @@ -0,0 +1,420 @@ +/* GStreamer + * Copyright (C) <2009> Collabora Ltd + * @author: Olivier Crete Nokia Inc + * + * 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 "gstshmsink.h" +#include "gstshm.h" + +#include + +#include +#include +#include +#include +#include +#include +#include + +/* signals */ +enum +{ + LAST_SIGNAL +}; + +/* properties */ +enum +{ + PROP_0, + PROP_SHM_NAME, + PROP_PERMS +}; + + +GST_DEBUG_CATEGORY_STATIC (shmsink_debug); + +static const GstElementDetails gst_shm_sink_details = +GST_ELEMENT_DETAILS ("Shared Memory Sink", + "Sink", + "Send data over shared memory to the matching source", + "Olivier Crete "); + +static GstStaticPadTemplate sinktemplate = GST_STATIC_PAD_TEMPLATE ("sink", + GST_PAD_SINK, + GST_PAD_ALWAYS, + GST_STATIC_CAPS_ANY); + +GST_BOILERPLATE (GstShmSink, gst_shm_sink, GstBaseSink, GST_TYPE_BASE_SINK); + +static void gst_shm_sink_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec); +static void gst_shm_sink_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * pspec); + +static GstStateChangeReturn gst_shm_sink_change_state (GstElement * element, + GstStateChange transition); + +static gboolean gst_shm_sink_start (GstBaseSink * bsink); +static gboolean gst_shm_sink_stop (GstBaseSink * bsink); +static gboolean gst_shm_sink_set_caps (GstBaseSink * bsink, GstCaps * caps); +static GstFlowReturn gst_shm_sink_render (GstBaseSink * bsink, GstBuffer * buf); +static gboolean gst_shm_sink_event (GstBaseSink * bsink, GstEvent * event); + +// static guint gst_shm_sink_signals[LAST_SIGNAL] = { 0 }; + +static void +gst_shm_sink_base_init (gpointer g_class) +{ + GstElementClass *element_class = GST_ELEMENT_CLASS (g_class); + + gst_element_class_add_pad_template (element_class, + gst_static_pad_template_get (&sinktemplate)); + + gst_element_class_set_details (element_class, &gst_shm_sink_details); +} + + +static void +gst_shm_sink_init (GstShmSink * self, GstShmSinkClass * g_class) +{ + self->fd = -1; + self->shm_area = MAP_FAILED; +} + +static void +gst_shm_sink_class_init (GstShmSinkClass * klass) +{ + GObjectClass *gobject_class; + GstElementClass *gstelement_class; + GstBaseSinkClass *gstbasesink_class; + + gobject_class = (GObjectClass *) klass; + gstelement_class = (GstElementClass *) klass; + gstbasesink_class = (GstBaseSinkClass *) klass; + + gobject_class->set_property = gst_shm_sink_set_property; + gobject_class->get_property = gst_shm_sink_get_property; + + gstelement_class->change_state = gst_shm_sink_change_state; + + gstbasesink_class->start = gst_shm_sink_start; + gstbasesink_class->stop = gst_shm_sink_stop; + gstbasesink_class->set_caps = gst_shm_sink_set_caps; + gstbasesink_class->render = gst_shm_sink_render; + gstbasesink_class->event = gst_shm_sink_event; + + g_object_class_install_property (gobject_class, PROP_SHM_NAME, + g_param_spec_string ("shm-name", + "Name of the shared memory area", + "The name of the shared memory area that the source can read from", + NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property (gobject_class, PROP_PERMS, + g_param_spec_uint ("perms", + "Permissions on the shm area", + "Permissions to set on the shm area", + 0, 07777, S_IRWXU | S_IRWXG, + G_PARAM_CONSTRUCT | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + GST_DEBUG_CATEGORY_INIT (shmsink_debug, "shmsink", 0, "Shared Memory Sink"); +} + +/* + * Set the value of a property for the server sink. + */ +static void +gst_shm_sink_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec) +{ + GstShmSink *self = GST_SHM_SINK (object); + + switch (prop_id) { + case PROP_SHM_NAME: + GST_OBJECT_LOCK (object); + g_free (self->shm_name); + self->shm_name = g_value_dup_string (value); + GST_OBJECT_UNLOCK (object); + break; + case PROP_PERMS: + self->perms = g_value_get_uint (value); + if (self->fd >= 0) + if (fchmod (self->fd, g_value_get_uint (value))) + GST_WARNING_OBJECT (self, + "Could not set permissions %o on shm area: %s", + g_value_get_uint (value), strerror (errno)); + break; + default: + break; + } +} + +static void +gst_shm_sink_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * pspec) +{ + GstShmSink *self = GST_SHM_SINK (object); + + switch (prop_id) { + case PROP_SHM_NAME: + GST_OBJECT_LOCK (object); + g_value_set_string (value, self->shm_name); + GST_OBJECT_UNLOCK (object); + break; + case PROP_PERMS: + self->perms = g_value_get_uint (value); + if (self->fd >= 0) + fchmod (self->fd, g_value_get_uint (value)); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + + + +static gboolean +gst_shm_sink_start (GstBaseSink * bsink) +{ + GstShmSink *self = GST_SHM_SINK (bsink); + g_return_val_if_fail (self->fd == -1, FALSE); + + GST_OBJECT_LOCK (self); + if (!self->shm_name) { + GST_OBJECT_UNLOCK (self); + GST_ERROR_OBJECT (self, "Must set the name of the shm area first"); + GST_ELEMENT_ERROR (self, RESOURCE, OPEN_READ_WRITE, + ("One must specify the name of the shm area"), + ("shm-name property not set")); + return FALSE; + } + + self->fd = shm_open (self->shm_name, O_RDWR | O_CREAT | O_TRUNC, self->perms); + + if (self->fd < 0) { + GST_OBJECT_UNLOCK (self); + GST_ERROR_OBJECT (self, "Could not open shm area: %s", strerror (errno)); + GST_ELEMENT_ERROR (self, RESOURCE, OPEN_READ_WRITE, + ("Could not open the shm area"), + ("shm_open failed (%d): %s", errno, strerror (errno))); + return FALSE; + } + self->opened_name = g_strdup (self->shm_name); + + GST_OBJECT_UNLOCK (self); + + self->shm_area_len = sizeof (struct GstShmHeader); + + if (ftruncate (self->fd, self->shm_area_len)) { + GST_ERROR_OBJECT (self, + "Could not make shm area large enough for header: %s", + strerror (errno)); + gst_shm_sink_stop (bsink); + GST_ELEMENT_ERROR (self, RESOURCE, OPEN_READ_WRITE, + ("Could not resize memory area"), + ("ftruncate failed (%d): %s", errno, strerror (errno))); + return FALSE; + } + + self->shm_area = mmap (NULL, self->shm_area_len, PROT_READ | PROT_WRITE, + MAP_SHARED, self->fd, 0); + + if (self->shm_area == MAP_FAILED) { + GST_ERROR_OBJECT (self, "Could not map shm area"); + GST_ELEMENT_ERROR (self, RESOURCE, OPEN_READ_WRITE, + ("Could not map memory area"), + ("mmap failed (%d): %s", errno, strerror (errno))); + gst_shm_sink_stop (bsink); + return FALSE; + } + + memset (self->shm_area, 0, self->shm_area_len); + g_assert (sem_init (&self->shm_area->notification, 1, 0) == 0); + g_assert (sem_init (&self->shm_area->mutex, 1, 1) == 0); + + return TRUE; +} + + +static gboolean +gst_shm_sink_stop (GstBaseSink * bsink) +{ + GstShmSink *self = GST_SHM_SINK (bsink); + + if (self->fd >= 0) + close (self->fd); + self->fd = -1; + + if (self->opened_name) { + shm_unlink (self->opened_name); + g_free (self->opened_name); + self->opened_name = NULL; + } + + if (self->shm_area != MAP_FAILED) + munmap (self->shm_area, self->shm_area_len); + self->shm_area_len = 0; + self->shm_area = MAP_FAILED; + + return TRUE; +} + +static gboolean +gst_shm_sink_set_caps (GstBaseSink * bsink, GstCaps * caps) +{ + GstShmSink *self = GST_SHM_SINK (bsink); + + self->caps_gen++; + + return TRUE; +} + +static gboolean +resize_area (GstShmSink * self, size_t desired_length) +{ + if (desired_length <= self->shm_area_len) + return TRUE; + + SHM_UNLOCK (self->shm_area); + + if (munmap (self->shm_area, self->shm_area_len)) { + GST_ERROR_OBJECT (self, "Could not unmap shared area"); + GST_ELEMENT_ERROR (self, RESOURCE, OPEN_READ_WRITE, + ("Could not unmap memory area"), + ("munmap failed (%d): %s", errno, strerror (errno))); + return FALSE; + } + if (ftruncate (self->fd, desired_length)) { + GST_ERROR_OBJECT (self, "Could not resize shared area"); + GST_ELEMENT_ERROR (self, RESOURCE, OPEN_READ_WRITE, + ("Could not resize memory area"), + ("ftruncate failed (%d): %s", errno, strerror (errno))); + return FALSE; + } + self->shm_area = mmap (NULL, desired_length, PROT_READ | PROT_WRITE, + MAP_SHARED, self->fd, 0); + self->shm_area_len = desired_length; + + if (self->shm_area == MAP_FAILED) { + self->shm_area = NULL; + GST_ERROR_OBJECT (self, "Could not remap shared area"); + GST_ELEMENT_ERROR (self, RESOURCE, OPEN_READ_WRITE, + ("Could not map memory area"), + ("mmap failed (%d): %s", errno, strerror (errno))); + return FALSE; + } + + SHM_LOCK (self->shm_area); + + return TRUE; +} + +static GstFlowReturn +gst_shm_sink_render (GstBaseSink * bsink, GstBuffer * buf) +{ + GstShmSink *self = GST_SHM_SINK (bsink); + + g_return_val_if_fail (self->shm_area != MAP_FAILED, GST_FLOW_ERROR); + + SHM_LOCK (self->shm_area); + + if (self->caps_gen != self->shm_area->caps_gen) { + gchar *caps_str = + gst_caps_to_string (GST_PAD_CAPS (GST_BASE_SINK_PAD (bsink))); + guint caps_size = strlen (caps_str) + 1; + + if (!resize_area (self, sizeof (struct GstShmHeader) + + caps_size + GST_BUFFER_SIZE (buf))) { + g_free (caps_str); + return GST_FLOW_ERROR; + } + + self->shm_area->caps_size = caps_size; + + memcpy (GST_SHM_CAPS_BUFFER (self->shm_area), caps_str, caps_size); + g_free (caps_str); + + self->shm_area->caps_gen = self->caps_gen; + } else { + if (!resize_area (self, sizeof (struct GstShmHeader) + + self->shm_area->caps_size + GST_BUFFER_SIZE (buf))) + return GST_FLOW_ERROR; + } + + memcpy (GST_SHM_BUFFER (self->shm_area), GST_BUFFER_DATA (buf), + GST_BUFFER_SIZE (buf)); + + self->shm_area->buffer_size = GST_BUFFER_SIZE (buf); + self->shm_area->buffer_gen++; + + self->shm_area->timestamp = GST_BUFFER_TIMESTAMP (buf); + self->shm_area->duration = GST_BUFFER_DURATION (buf); + self->shm_area->offset = GST_BUFFER_OFFSET (buf); + self->shm_area->offset_end = GST_BUFFER_OFFSET_END (buf); + self->shm_area->flags = GST_BUFFER_FLAGS (buf) & (GST_BUFFER_FLAG_DISCONT | + GST_BUFFER_FLAG_GAP | GST_BUFFER_FLAG_DELTA_UNIT); + + sem_post (&self->shm_area->notification); + + SHM_UNLOCK (self->shm_area); + + return GST_FLOW_OK; +} + +static gboolean +gst_shm_sink_event (GstBaseSink * bsink, GstEvent * event) +{ + GstShmSink *self = GST_SHM_SINK (bsink); + + g_return_val_if_fail (self->shm_area != MAP_FAILED, FALSE); + + if (GST_EVENT_TYPE (event) == GST_EVENT_EOS) { + SHM_LOCK (self->shm_area); + self->shm_area->eos = TRUE; + SHM_UNLOCK (self->shm_area); + } + + return TRUE; +} + +static GstStateChangeReturn +gst_shm_sink_change_state (GstElement * element, GstStateChange transition) +{ + GstShmSink *self = GST_SHM_SINK (element); + + switch (transition) { + case GST_STATE_CHANGE_READY_TO_PAUSED: + g_return_val_if_fail (self->shm_area != MAP_FAILED, + GST_STATE_CHANGE_FAILURE); + SHM_LOCK (self->shm_area); + self->shm_area->eos = FALSE; + SHM_UNLOCK (self->shm_area); + break; + default: + break; + } + + return GST_ELEMENT_CLASS (parent_class)->change_state (element, transition); +} diff --git a/gst/shm/gstshmsink.h b/gst/shm/gstshmsink.h new file mode 100644 index 0000000000..0475e1daad --- /dev/null +++ b/gst/shm/gstshmsink.h @@ -0,0 +1,66 @@ +/* GStreamer + * Copyright (C) <2009> Collabora Ltd + * @author: Olivier Crete Nokia Inc + * + * 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_SHM_SINK_H__ +#define __GST_SHM_SINK_H__ + +#include +#include + +G_BEGIN_DECLS +#define GST_TYPE_SHM_SINK \ + (gst_shm_sink_get_type()) +#define GST_SHM_SINK(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_SHM_SINK,GstShmSink)) +#define GST_SHM_SINK_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_SHM_SINK,GstShmSinkClass)) +#define GST_IS_SHM_SINK(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_SHM_SINK)) +#define GST_IS_SHM_SINK_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_SHM_SINK)) +typedef struct _GstShmSink GstShmSink; +typedef struct _GstShmSinkClass GstShmSinkClass; + +struct _GstShmSink +{ + GstBaseSink element; + + gchar *shm_name; + + int fd; + struct GstShmHeader *shm_area; + size_t shm_area_len; + gchar *opened_name; + + guint perms; + + guint caps_gen; +}; + +struct _GstShmSinkClass +{ + GstBaseSinkClass parent_class; +}; + +GType gst_shm_sink_get_type (void); + +G_END_DECLS +#endif /* __GST_SHM_SINK_H__ */ diff --git a/gst/shm/gstshmsrc.c b/gst/shm/gstshmsrc.c new file mode 100644 index 0000000000..c7bae59ff7 --- /dev/null +++ b/gst/shm/gstshmsrc.c @@ -0,0 +1,393 @@ +/* GStreamer + * Copyright (C) <2009> Collabora Ltd + * @author: Olivier Crete Nokia Inc + * + * 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 "gstshmsrc.h" +#include "gstshm.h" + +#include + +#include +#include +#include +#include +#include +#include +#include + +/* signals */ +enum +{ + LAST_SIGNAL +}; + +/* properties */ +enum +{ + PROP_0, + PROP_SHM_NAME +}; + + +GST_DEBUG_CATEGORY_STATIC (shmsrc_debug); + +static const GstElementDetails gst_shm_src_details = +GST_ELEMENT_DETAILS ("Shared Memory Source", + "Source", + "Receive data from the sharem memory sink", + "Olivier Crete set_property = gst_shm_src_set_property; + gobject_class->get_property = gst_shm_src_get_property; + + gstbasesrc_class->start = gst_shm_src_start; + gstbasesrc_class->stop = gst_shm_src_stop; + gstbasesrc_class->unlock = gst_shm_src_unlock; + gstbasesrc_class->unlock_stop = gst_shm_src_unlock_stop; + + gstpush_src_class->create = gst_shm_src_create; + + g_object_class_install_property (gobject_class, PROP_SHM_NAME, + g_param_spec_string ("shm-name", + "Name of the shared memory area", + "The name of the shared memory area that the source can read from", + NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + GST_DEBUG_CATEGORY_INIT (shmsrc_debug, "shmsrc", 0, "Shared Memory Source"); +} + +static void +gst_shm_src_init (GstShmSrc * self, GstShmSrcClass * g_class) +{ + gst_base_src_set_live (GST_BASE_SRC (self), TRUE); + gst_base_src_set_do_timestamp (GST_BASE_SRC (self), TRUE); + + gst_pad_use_fixed_caps (GST_BASE_SRC_PAD (self)); + + self->fd = -1; + self->shm_area = MAP_FAILED; +} + + +static void +gst_shm_src_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec) +{ + GstShmSrc *self = GST_SHM_SRC (object); + + switch (prop_id) { + case PROP_SHM_NAME: + GST_OBJECT_LOCK (object); + g_free (self->shm_name); + self->shm_name = g_value_dup_string (value); + GST_OBJECT_UNLOCK (object); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gst_shm_src_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * pspec) +{ + GstShmSrc *self = GST_SHM_SRC (object); + + switch (prop_id) { + case PROP_SHM_NAME: + GST_OBJECT_LOCK (object); + g_value_set_string (value, self->shm_name); + GST_OBJECT_UNLOCK (object); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static gboolean +gst_shm_src_start (GstBaseSrc * bsrc) +{ + GstShmSrc *self = GST_SHM_SRC (bsrc); + + g_return_val_if_fail (self->fd == -1, FALSE); + + GST_OBJECT_LOCK (self); + if (!self->shm_name) { + GST_OBJECT_UNLOCK (self); + GST_ERROR_OBJECT (self, "Must set the name of the shm area first"); + GST_ELEMENT_ERROR (self, RESOURCE, OPEN_READ_WRITE, + ("One must specify the name of the shm area"), + ("shm-name property not set")); + return FALSE; + } + + self->fd = shm_open (self->shm_name, O_RDWR, 0); + + if (self->fd < 0) { + GST_OBJECT_UNLOCK (self); + GST_ERROR_OBJECT (self, "Could not open shm area: %s", strerror (errno)); + GST_ELEMENT_ERROR (self, RESOURCE, OPEN_READ_WRITE, + ("Could not open the shm area"), + ("shm_open failed (%d): %s", errno, strerror (errno))); + return FALSE; + } + GST_OBJECT_UNLOCK (self); + + + self->shm_area_len = sizeof (struct GstShmHeader); + + self->shm_area = mmap (NULL, self->shm_area_len, PROT_READ | PROT_WRITE, + MAP_SHARED, self->fd, 0); + + if (self->shm_area == MAP_FAILED) { + GST_ERROR_OBJECT (self, "Could not map shm area"); + GST_ELEMENT_ERROR (self, RESOURCE, OPEN_READ_WRITE, + ("Could not map memory area"), + ("mmap failed (%d): %s", errno, strerror (errno))); + gst_shm_src_stop (bsrc); + return FALSE; + } + + return TRUE; +} + +static gboolean +gst_shm_src_stop (GstBaseSrc * bsrc) +{ + GstShmSrc *self = GST_SHM_SRC (bsrc); + + if (self->fd >= 0) + close (self->fd); + self->fd = -1; + + if (self->shm_area != MAP_FAILED) + munmap (self->shm_area, self->shm_area_len); + self->shm_area_len = 0; + self->shm_area = MAP_FAILED; + + return TRUE; +} + + +static gboolean +resize_area (GstShmSrc * self) +{ + while ((sizeof (struct GstShmHeader) + self->shm_area->caps_size + + self->shm_area->buffer_size) > self->shm_area_len) { + size_t new_size = (sizeof (struct GstShmHeader) + + self->shm_area->caps_size + self->shm_area->buffer_size); + + SHM_UNLOCK (self->shm_area); + if (munmap (self->shm_area, self->shm_area_len)) { + GST_ERROR_OBJECT (self, "Could not unmap shared area"); + GST_ELEMENT_ERROR (self, RESOURCE, OPEN_READ_WRITE, + ("Could not unmap memory area"), + ("munmap failed (%d): %s", errno, strerror (errno))); + return FALSE; + } + + self->shm_area = mmap (NULL, new_size, PROT_READ | PROT_WRITE, + MAP_SHARED, self->fd, 0); + + if (!self->shm_area) { + GST_ERROR_OBJECT (self, "Could not remap shared area"); + GST_ELEMENT_ERROR (self, RESOURCE, OPEN_READ_WRITE, + ("Could not map memory area"), + ("mmap failed (%d): %s", errno, strerror (errno))); + return FALSE; + } + self->shm_area_len = new_size; + SHM_LOCK (self->shm_area); + } + + return TRUE; +} + +static GstFlowReturn +gst_shm_src_create (GstPushSrc * psrc, GstBuffer ** outbuf) +{ + GstShmSrc *self = GST_SHM_SRC (psrc); + + if (self->unlocked) + return GST_FLOW_WRONG_STATE; + + g_return_val_if_fail (self->shm_area != MAP_FAILED, GST_FLOW_ERROR); + + SHM_LOCK (self->shm_area); + + if (self->unlocked) + goto unlocked; + + if (self->shm_area->eos) + goto eos; + + while (self->buffer_gen == self->shm_area->buffer_gen) { + SHM_UNLOCK (self->shm_area); + + if (self->unlocked) + return GST_FLOW_WRONG_STATE; + + GST_LOG_OBJECT (self, "Waiting for next buffer"); + + sem_wait (&self->shm_area->notification); + + if (self->unlocked) + return GST_FLOW_WRONG_STATE; + + SHM_LOCK (self->shm_area); + } + + if (self->unlocked) + goto eos; + + if (!resize_area (self)) { + return GST_FLOW_ERROR; + } + + if (self->caps_gen != self->shm_area->caps_gen) { + GstCaps *caps; + + GST_DEBUG_OBJECT (self, "Got new caps: %s", + GST_SHM_CAPS_BUFFER (self->shm_area)); + + caps = gst_caps_from_string (GST_SHM_CAPS_BUFFER (self->shm_area)); + + self->caps_gen = self->shm_area->caps_gen; + + if (!caps) { + SHM_UNLOCK (self->shm_area); + GST_ERROR_OBJECT (self, "Could not read caps"); + return GST_FLOW_ERROR; + } + + if (!gst_pad_set_caps (GST_BASE_SRC_PAD (psrc), caps)) { + SHM_UNLOCK (self->shm_area); + return GST_FLOW_NOT_NEGOTIATED; + } + } + + GST_LOG_OBJECT (self, "Create new buffer of size %u", + self->shm_area->buffer_size); + + *outbuf = gst_buffer_new_and_alloc (self->shm_area->buffer_size); + + memcpy (GST_BUFFER_DATA (*outbuf), GST_SHM_BUFFER (self->shm_area), + GST_BUFFER_SIZE (*outbuf)); + + // GST_BUFFER_TIMESTAMP (*outbuf) = self->shm_area->timestamp; + GST_BUFFER_DURATION (*outbuf) = self->shm_area->duration; + GST_BUFFER_OFFSET (*outbuf) = self->shm_area->offset; + GST_BUFFER_OFFSET_END (*outbuf) = self->shm_area->offset_end; + GST_BUFFER_FLAGS (*outbuf) = self->shm_area->flags; + + if (self->buffer_gen + 1 != self->shm_area->buffer_gen) { + GST_WARNING_OBJECT (self, "Skipped %u buffers, setting DISCONT flag", + self->shm_area->buffer_gen - self->buffer_gen - 1); + GST_BUFFER_FLAG_SET (*outbuf, GST_BUFFER_FLAG_DISCONT); + } + + self->buffer_gen = self->shm_area->buffer_gen; + + SHM_UNLOCK (self->shm_area); + + gst_buffer_set_caps (*outbuf, GST_PAD_CAPS (GST_BASE_SRC_PAD (psrc))); + + return GST_FLOW_OK; + +eos: + SHM_UNLOCK (self->shm_area); + return GST_FLOW_UNEXPECTED; + +unlocked: + SHM_UNLOCK (self->shm_area); + return GST_FLOW_WRONG_STATE; +} + +static gboolean +gst_shm_src_unlock (GstBaseSrc * bsrc) +{ + GstShmSrc *self = GST_SHM_SRC (bsrc); + + self->unlocked = TRUE; + + if (self->shm_area != MAP_FAILED) + sem_post (&self->shm_area->notification); + + return TRUE; +} + +static gboolean +gst_shm_src_unlock_stop (GstBaseSrc * bsrc) +{ + GstShmSrc *self = GST_SHM_SRC (bsrc); + + self->unlocked = FALSE; + + return TRUE; +} diff --git a/gst/shm/gstshmsrc.h b/gst/shm/gstshmsrc.h new file mode 100644 index 0000000000..8ebe9c1083 --- /dev/null +++ b/gst/shm/gstshmsrc.h @@ -0,0 +1,67 @@ +/* GStreamer + * Copyright (C) <2009> Collabora Ltd + * @author: Olivier Crete Nokia Inc + * + * 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_SHM_SRC_H__ +#define __GST_SHM_SRC_H__ + +#include +#include +#include + +G_BEGIN_DECLS +#define GST_TYPE_SHM_SRC \ + (gst_shm_src_get_type()) +#define GST_SHM_SRC(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_SHM_SRC,GstShmSrc)) +#define GST_SHM_SRC_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_SHM_SRC,GstShmSrcClass)) +#define GST_IS_SHM_SRC(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_SHM_SRC)) +#define GST_IS_SHM_SRC_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_SHM_SRC)) +typedef struct _GstShmSrc GstShmSrc; +typedef struct _GstShmSrcClass GstShmSrcClass; + +struct _GstShmSrc +{ + GstPushSrc element; + + gchar *shm_name; + + int fd; + struct GstShmHeader *shm_area; + size_t shm_area_len; + + guint caps_gen; + guint buffer_gen; + + gboolean unlocked; +}; + +struct _GstShmSrcClass +{ + GstPushSrcClass parent_class; +}; + +GType gst_shm_src_get_type (void); + +G_END_DECLS +#endif /* __GST_SHM_SRC_H__ */