From 2c01ccfb4198e331e8904efb8636844d851df426 Mon Sep 17 00:00:00 2001 From: Jeongmo Yang Date: Tue, 5 Jan 2016 11:27:54 +0900 Subject: [PATCH] [tizenipc] Add new plugins - tizenipcsink, tizenipcsrc Change-Id: I6e3045adddfc546f52333fc83d35ea08389a2892 Signed-off-by: Jeongmo Yang --- Makefile.am | 4 + configure.ac | 14 + packaging/gst-plugins-tizen.spec | 2 +- tizenipc/Makefile.am | 1 + tizenipc/src/Makefile.am | 37 ++ tizenipc/src/gsttizenipcsink.c | 1082 ++++++++++++++++++++++++++++++++++++++ tizenipc/src/gsttizenipcsink.h | 108 ++++ tizenipc/src/gsttizenipcsrc.c | 931 ++++++++++++++++++++++++++++++++ tizenipc/src/gsttizenipcsrc.h | 102 ++++ 9 files changed, 2280 insertions(+), 1 deletion(-) create mode 100644 tizenipc/Makefile.am create mode 100644 tizenipc/src/Makefile.am create mode 100644 tizenipc/src/gsttizenipcsink.c create mode 100644 tizenipc/src/gsttizenipcsink.h create mode 100644 tizenipc/src/gsttizenipcsrc.c create mode 100644 tizenipc/src/gsttizenipcsrc.h diff --git a/Makefile.am b/Makefile.am index 0b60630..b24db2f 100644 --- a/Makefile.am +++ b/Makefile.am @@ -61,6 +61,10 @@ if GST_TIZEN_USE_DRMDECRYPTOR SUBDIRS += drmdecryptor endif +if GST_TIZEN_USE_TIZENIPC +SUBDIRS += tizenipc +endif + #if GST_TIZEN_USE_WAYLANDSINK #SUBDIRS += waylandsink #endif diff --git a/configure.ac b/configure.ac index 1425242..8508a31 100644 --- a/configure.ac +++ b/configure.ac @@ -498,6 +498,18 @@ PKG_CHECK_MODULES(WAYLAND, wayland-client >= 1.4.0 wayland-tbm-client tizen-exte AC_PATH_PROG([wayland_scanner], [wayland-scanner]) fi +dnl use tizenipc-------------------------------------------------------------------------- +AC_ARG_ENABLE(tizenipc, AC_HELP_STRING([--enable-tizenipc], [using tizenipc]), + [ + case "${enableval}" in + yes) GST_TIZEN_USE_TIZENIPC=yes ;; + no) GST_TIZEN_USE_TIZENIPC=no ;; + *) AC_MSG_ERROR(bad value ${enableval} for --enable-tizenipc) ;; + esac + ], + [GST_TIZEN_USE_TIZENIPC=yes]) +AM_CONDITIONAL(GST_TIZEN_USE_TIZENIPC, test "x$GST_TIZEN_USE_TIZENIPC" = "xyes") + AC_OUTPUT( Makefile common/Makefile @@ -530,4 +542,6 @@ drmdecryptor/Makefile drmdecryptor/src/Makefile waylandsink/Makefile waylandsink/src/Makefile +tizenipc/Makefile +tizenipc/src/Makefile ) diff --git a/packaging/gst-plugins-tizen.spec b/packaging/gst-plugins-tizen.spec index 4628f62..b75a9e3 100644 --- a/packaging/gst-plugins-tizen.spec +++ b/packaging/gst-plugins-tizen.spec @@ -5,7 +5,7 @@ Name: gst-plugins-tizen Version: 1.0.0 Summary: GStreamer tizen plugins (common) -Release: 18 +Release: 19 Group: Multimedia/Framework Url: http://gstreamer.freedesktop.org/ License: LGPL-2.1+ diff --git a/tizenipc/Makefile.am b/tizenipc/Makefile.am new file mode 100644 index 0000000..af437a6 --- /dev/null +++ b/tizenipc/Makefile.am @@ -0,0 +1 @@ +SUBDIRS = src diff --git a/tizenipc/src/Makefile.am b/tizenipc/src/Makefile.am new file mode 100644 index 0000000..c0bbd25 --- /dev/null +++ b/tizenipc/src/Makefile.am @@ -0,0 +1,37 @@ +# plugindir is set in configure + +############################################################################## +# change libgstplugin.la to something more suitable, e.g. libmysomething.la # +############################################################################## +plugin_LTLIBRARIES = libgsttizenipcsink.la libgsttizenipcsrc.la + +############################################################################## +# for the next set of variables, rename the prefix if you renamed the .la, # +# e.g. libgstplugin_la_SOURCES => libmysomething_la_SOURCES # +# libgstplugin_la_CFLAGS => libmysomething_la_CFLAGS # +# libgstplugin_la_LIBADD => libmysomething_la_LIBADD # +# libgstplugin_la_LDFLAGS => libmysomething_la_LDFLAGS # +############################################################################## + +# sources used to compile this plug-in +libgsttizenipcsink_la_SOURCES = gsttizenipcsink.c + +# flags used to compile this plugin +# add other _CFLAGS and _LIBS as needed +libgsttizenipcsink_la_CFLAGS = $(GST_CFLAGS) $(GST_PLUGINS_BASE_CFLAGS) $(GST_BASE_CFLAGS) $(TBM_CFLAGS) $(MMCOMMON_CFLAGS) +libgsttizenipcsink_la_LIBADD = $(GST_LIBS) $(GST_PLUGINS_BASE_LIBS) $(GST_BASE_LIBS) $(TBM_LIBS) +libgsttizenipcsink_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS) + +# sources used to compile this plug-in +libgsttizenipcsrc_la_SOURCES = gsttizenipcsrc.c + +# flags used to compile this plugin +# add other _CFLAGS and _LIBS as needed +libgsttizenipcsrc_la_CFLAGS = $(GST_CFLAGS) $(GST_PLUGINS_BASE_CFLAGS) $(GST_BASE_CFLAGS) $(TBM_CFLAGS) $(MMCOMMON_CFLAGS) +libgsttizenipcsrc_la_LIBADD = $(GST_LIBS) $(GST_PLUGINS_BASE_LIBS) $(GST_BASE_LIBS) $(TBM_LIBS) +libgsttizenipcsrc_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS) + +# headers we need but don't want installed +noinst_HEADERS = gsttizenipcsink.h \ + gsttizenipcsrc.h + diff --git a/tizenipc/src/gsttizenipcsink.c b/tizenipc/src/gsttizenipcsink.c new file mode 100644 index 0000000..a2771e6 --- /dev/null +++ b/tizenipc/src/gsttizenipcsink.c @@ -0,0 +1,1082 @@ +/* + * GStreamer Tizen IPC sink + * + * Copyright (C) 2015 Jeongmo Yang + * + * 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, Fifth Floor, + * Boston, MA 02110-1301 USA. + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "gsttizenipcsink.h" + +#define DEFAULT_SOCKET_PATH "/tmp/tizenipc.0" +#define DEFAULT_SHM_PATH "/tizenipcshm" +#define DEFAULT_PERMISSIONS (S_IRUSR|S_IWUSR|S_IRGRP) +#define DEFAULT_BACKLOG 5 +#define CLIENT_RESPONSE_TIMEOUT (G_TIME_SPAN_MILLISECOND * 200) +#define BUFFER_WAIT_TIMEOUT (G_TIME_SPAN_MILLISECOND * 3000) + +GST_DEBUG_CATEGORY(gst_debug_tizenipc_sink); + +#define GST_CAT_DEFAULT gst_debug_tizenipc_sink + +static GstStaticPadTemplate sinktemplate = \ + GST_STATIC_PAD_TEMPLATE("sink", + GST_PAD_SINK, + GST_PAD_ALWAYS, + GST_STATIC_CAPS_ANY); + +#define gst_tizenipc_sink_parent_class parent_class +G_DEFINE_TYPE(GstTizenipcSink, gst_tizenipc_sink, GST_TYPE_BASE_SINK); + +/* signals */ +enum { + SIGNAL_CLIENT_CONNECTED, + SIGNAL_CLIENT_DISCONNECTED, + LAST_SIGNAL +}; + +/* properties */ +enum { + PROP_0, + PROP_SOCKET_PATH, + PROP_PERMISSIONS +}; + + + +static gboolean _prepare_tizenipc_sink(GstTizenipcSink *self, guint shm_size); +static gboolean _add_buffer_to_list(GstTizenipcSink *self, GstBuffer *buf, int *tbm_key); +static gboolean _remove_buffer_from_list(GstTizenipcSink *self, int *tbm_key); + +static void gst_tizenipc_sink_finalize(GObject *object); +static void gst_tizenipc_sink_set_property(GObject *object, guint prop_id, + const GValue *value, GParamSpec *pspec); +static void gst_tizenipc_sink_get_property(GObject *object, guint prop_id, + GValue *value, GParamSpec *pspec); +static gboolean gst_tizenipc_sink_start(GstBaseSink *bsink); +static gboolean gst_tizenipc_sink_stop(GstBaseSink *bsink); +static GstFlowReturn gst_tizenipc_sink_render(GstBaseSink *bsink, GstBuffer *buf); +static gboolean gst_tizenipc_sink_event(GstBaseSink *bsink, GstEvent *event); +static gboolean gst_tizenipc_sink_unlock(GstBaseSink *bsink); +static gboolean gst_tizenipc_sink_unlock_stop(GstBaseSink *bsink); + +static gpointer _gst_poll_thread_func(gpointer data); + +static guint signals[LAST_SIGNAL] = {0, 0}; + + + + +static gboolean _prepare_tizenipc_sink(GstTizenipcSink *self, guint shm_size) +{ + int i = 0; + int flags = 0; + struct sockaddr_un addr_un; + struct sockaddr *address = NULL; + socklen_t address_len = 0; + gchar shm_path[32] = {'\0',}; + + if (self == NULL) { + GST_ERROR("NULL handle"); + return FALSE; + } + + GST_INFO_OBJECT(self, "start - shared memory size %u", shm_size); + + /* open socket */ + self->socket_fd = socket(PF_UNIX, SOCK_STREAM, 0); + if (self->socket_fd < 0) { + GST_ERROR_OBJECT(self, "failed to create socket"); + return FALSE; + } + + flags = fcntl(self->socket_fd, F_GETFL, NULL); + if (flags < 0) { + GST_ERROR_OBJECT(self, "failed to fcntl F_GETFL"); + goto _FAILED; + } + + /* set non-block mode */ + if (fcntl (self->socket_fd, F_SETFL, flags|O_NONBLOCK) < 0) { + GST_ERROR_OBJECT(self, "failed to fcntl F_SETFL"); + goto _FAILED; + } + + memset(&addr_un, 0x0, sizeof(addr_un)); + + addr_un.sun_family = AF_UNIX; + strncpy(addr_un.sun_path, self->socket_path, sizeof(addr_un.sun_path) - 1); + + address = (struct sockaddr *)(&addr_un); + address_len = sizeof(addr_un); + + unlink(self->socket_path); + + /* bind socket */ + while (bind(self->socket_fd, address, address_len) < 0) { + if (errno != EADDRINUSE) { + GST_ERROR_OBJECT(self, "failed to bind. errno %d", errno); + goto _FAILED; + } + + if (i > 256) { + GST_ERROR_OBJECT(self, "no more free socket name"); + goto _FAILED; + } + + snprintf(addr_un.sun_path, sizeof(addr_un.sun_path), "%s.%d", self->socket_path, i); + i++; + + unlink(addr_un.sun_path); + } + + if (self->socket_path_result) { + g_free(self->socket_path_result); + self->socket_path_result = NULL; + } + + self->socket_path_result = g_strdup(addr_un.sun_path); + if (self->socket_path_result == NULL) { + GST_ERROR_OBJECT(self, "failed to copy string %s", addr_un.sun_path); + goto _FAILED; + } + + if (chmod(self->socket_path_result, self->permissions) < 0) { + GST_ERROR_OBJECT(self, "failed to chmod %s - %d", addr_un.sun_path, self->permissions); + goto _FAILED; + } + + if (listen(self->socket_fd, DEFAULT_BACKLOG) < 0) { + GST_ERROR_OBJECT(self, "failed to listen"); + goto _FAILED; + } + + /* create shared memory */ + i = 0; + do { + snprintf(shm_path, 32, "%s.%d", DEFAULT_SHM_PATH, i++); + self->shm_fd = shm_open(shm_path, O_RDWR|O_CREAT|O_EXCL, self->permissions); + } while (self->shm_fd < 0 && errno == EEXIST); + + if (self->shm_fd < 0) { + GST_ERROR_OBJECT(self, "failed to open shared memory [%s], errno [%d]", shm_path, errno); + goto _FAILED; + } + + if (self->shm_path) { + g_free(self->shm_path); + self->shm_path = NULL; + } + + self->shm_path = g_strdup(shm_path); + if (self->shm_path == NULL) { + GST_ERROR_OBJECT(self, "failed to copy shared memory path"); + goto _FAILED; + } + + if (ftruncate(self->shm_fd, shm_size) < 0) { + GST_ERROR_OBJECT(self, "failed to resize shm to %d", shm_size); + goto _FAILED; + } + + self->shm_mapped_area = (gchar *)mmap(NULL, + shm_size, + PROT_READ | PROT_WRITE, + MAP_SHARED, + self->shm_fd, + 0); + if (self->shm_mapped_area == MAP_FAILED) { + GST_ERROR_OBJECT(self, "failed to mmap for shared memory"); + goto _FAILED; + } + + self->shm_mapped_size = shm_size; + + /* create gst poll and thread for poll */ + self->poll = gst_poll_new(TRUE); + if (self->poll == NULL) { + GST_ERROR_OBJECT(self, "failed to create gst poll"); + goto _FAILED; + } + gst_poll_fd_init(&self->pollfd); + self->pollfd.fd = self->socket_fd; + gst_poll_add_fd(self->poll, &self->pollfd); + gst_poll_fd_ctl_read(self->poll, &self->pollfd, TRUE); + + self->poll_thread_run = TRUE; + self->poll_thread = g_thread_try_new("gsttizenipcsink_poll_thread", + _gst_poll_thread_func, + self, + NULL); + if (self->poll_thread == NULL) { + GST_ERROR_OBJECT(self, "failed to create thread for gst poll"); + self->poll_thread_run = FALSE; + goto _FAILED; + } + + GST_INFO_OBJECT(self, "done - shm %p", self->shm_mapped_area); + + return TRUE; + +_FAILED: + if (self->poll) { + gst_poll_free(self->poll); + self->poll = NULL; + } + + if (self->shm_fd > -1) { + close(self->shm_fd); + self->shm_fd = -1; + } + + if (self->socket_fd > -1) { + close(self->socket_fd); + self->socket_fd = -1; + } + + return FALSE; +} + + +static gboolean _add_buffer_to_list(GstTizenipcSink *self, GstBuffer *buf, int *tbm_key) +{ + int i = 0; + int j = 0; + GstTizenipcBuffer *sended_buffer = NULL; + + if (self == NULL || buf == NULL || tbm_key == NULL) { + GST_ERROR("NULL parameter %p, %p, %p", self, buf, tbm_key); + return FALSE; + } + + g_mutex_lock(&self->buffer_lock); + + sended_buffer = self->sended_buffer; + + for (i = 0 ; i < GST_TIZENIPC_BUFFER_MAX ; i++) { + /* find empty space */ + if (sended_buffer[i].gst_buf == NULL) { + self->sended_buffer_count++; + + GST_DEBUG_OBJECT(self, "insert buffer(key[0] %d) to index %d, count %d", + tbm_key[0], i, self->sended_buffer_count); + + /* ref gst buffer and set tbm key */ + gst_buffer_ref(buf); + sended_buffer[i].gst_buf = buf; + + for (j = 0 ; j < MM_VIDEO_BUFFER_PLANE_MAX ; j++) { + if (tbm_key[j] > 0) { + sended_buffer[i].tbm_key[j] = tbm_key[j]; + } else { + break; + } + } + + g_mutex_unlock(&self->buffer_lock); + + return TRUE; + } + } + + g_mutex_unlock(&self->buffer_lock); + + GST_WARNING_OBJECT(self, "should not be reached here. no space to keep buffer"); + + return FALSE; +} + + +static gboolean _remove_buffer_from_list(GstTizenipcSink *self, int *tbm_key) +{ + int i = 0; + GstTizenipcBuffer *sended_buffer = NULL; + + if (self == NULL || tbm_key == NULL) { + GST_ERROR("NULL parameter %p, %p", self, tbm_key); + return FALSE; + } + + g_mutex_lock(&self->buffer_lock); + + sended_buffer = self->sended_buffer; + + for (i = 0 ; i < GST_TIZENIPC_BUFFER_MAX ; i++) { + /* find matched buffer info */ + if (sended_buffer[i].tbm_key[0] == tbm_key[0] && + sended_buffer[i].tbm_key[1] == tbm_key[1] && + sended_buffer[i].tbm_key[2] == tbm_key[2] && + sended_buffer[i].tbm_key[3] == tbm_key[3]) { + /* remove buffer info and unref gst buffer */ + self->sended_buffer_count--; + + GST_DEBUG_OBJECT(self, "gst buffer %p for key[0] %d, count %d", + sended_buffer[i].gst_buf, tbm_key[0], self->sended_buffer_count); + + if (sended_buffer[i].gst_buf) { + gst_buffer_unref(sended_buffer[i].gst_buf); + sended_buffer[i].gst_buf = NULL; + } else { + GST_WARNING_OBJECT(self, "no gst buffer for key[0] %d", tbm_key[0]); + } + + sended_buffer[i].tbm_key[0] = 0; + sended_buffer[i].tbm_key[1] = 0; + sended_buffer[i].tbm_key[2] = 0; + sended_buffer[i].tbm_key[3] = 0; + + g_cond_signal(&self->buffer_cond); + g_mutex_unlock(&self->buffer_lock); + + return TRUE; + } + } + + g_cond_signal(&self->buffer_cond); + g_mutex_unlock(&self->buffer_lock); + + GST_WARNING_OBJECT(self, "could not find matched buffer for tbm_key[0] %d", tbm_key[0]); + + return FALSE; +} + + + +/* ---------------------- */ +/* MAIN METHODS */ +/* ---------------------- */ + +static void gst_tizenipc_sink_init(GstTizenipcSink *self) +{ + g_mutex_init(&self->buffer_lock); + g_cond_init(&self->buffer_cond); + g_mutex_init(&self->ipc_lock); + g_cond_init(&self->ipc_cond); + + self->socket_fd = -1; + self->client_fd = -1; + self->shm_fd = -1; + self->shm_mapped_area = MAP_FAILED; + self->is_connected = FALSE; + self->permissions = DEFAULT_PERMISSIONS; + self->socket_path = g_strdup(DEFAULT_SOCKET_PATH); + if (self->socket_path == NULL) { + GST_ERROR_OBJECT(self, "failed to dup socket path [%s]", DEFAULT_SOCKET_PATH); + } + self->sended_buffer = g_new0(GstTizenipcBuffer, GST_TIZENIPC_BUFFER_MAX); + if (self->sended_buffer == NULL) { + GST_ERROR_OBJECT(self, "failed to alloc sended_buffer"); + } + + return; +} + +static void gst_tizenipc_sink_class_init(GstTizenipcSinkClass *klass) +{ + GObjectClass *gobject_class = NULL; + GstElementClass *gstelement_class = NULL; + GstBaseSinkClass *gstbasesink_class = NULL; + GParamSpec *pspec = NULL; + + if (klass == NULL) { + GST_ERROR("NULL klass"); + return; + } + + gobject_class = (GObjectClass *)klass; + gstelement_class = (GstElementClass *)klass; + gstbasesink_class = (GstBaseSinkClass *)klass; + + parent_class = g_type_class_peek_parent(klass); + + gobject_class->finalize = gst_tizenipc_sink_finalize; + gobject_class->set_property = gst_tizenipc_sink_set_property; + gobject_class->get_property = gst_tizenipc_sink_get_property; + + gstbasesink_class->start = GST_DEBUG_FUNCPTR(gst_tizenipc_sink_start); + gstbasesink_class->stop = GST_DEBUG_FUNCPTR(gst_tizenipc_sink_stop); + gstbasesink_class->render = GST_DEBUG_FUNCPTR(gst_tizenipc_sink_render); + gstbasesink_class->event = GST_DEBUG_FUNCPTR(gst_tizenipc_sink_event); + gstbasesink_class->unlock = GST_DEBUG_FUNCPTR(gst_tizenipc_sink_unlock); + gstbasesink_class->unlock_stop = GST_DEBUG_FUNCPTR(gst_tizenipc_sink_unlock_stop); + + /* property */ + pspec = g_param_spec_string("socket-path", + "Path to the control socket", + "The path to the control socket used to handle IPC", + NULL, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); + if (pspec) { + g_object_class_install_property(gobject_class, PROP_SOCKET_PATH, pspec); + } else { + GST_ERROR("failed to get pspec for \"socket-path\""); + } + + pspec = g_param_spec_uint("permissions", + "Permissions for the IPC", + "Permissions for the IPC", + 0, 07777, DEFAULT_PERMISSIONS, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); + if (pspec) { + g_object_class_install_property (gobject_class, PROP_PERMISSIONS, pspec); + } else { + GST_ERROR("failed to get pspec for \"permissions\""); + } + + /* signal */ + signals[SIGNAL_CLIENT_CONNECTED] = \ + g_signal_new("client-connected", + GST_TYPE_TIZENIPC_SINK, G_SIGNAL_RUN_LAST, 0, NULL, NULL, + g_cclosure_marshal_VOID__INT, G_TYPE_NONE, 1, G_TYPE_INT); + + signals[SIGNAL_CLIENT_DISCONNECTED] = \ + g_signal_new("client-disconnected", + GST_TYPE_TIZENIPC_SINK, G_SIGNAL_RUN_LAST, 0, NULL, NULL, + g_cclosure_marshal_VOID__INT, G_TYPE_NONE, 1, G_TYPE_INT); + + gst_element_class_add_pad_template(gstelement_class, + gst_static_pad_template_get(&sinktemplate)); + + gst_element_class_set_static_metadata(gstelement_class, + "Tizen IPC Sink", + "Sink", + "Send data via IPC to the tizenipcsrc", + "Jeongmo Yang "); + + return; +} + + +static void gst_tizenipc_sink_finalize(GObject *object) +{ + GstTizenipcSink *self = NULL; + + if (object == NULL) { + GST_ERROR("NULL object"); + return; + } + + self = GST_TIZENIPC_SINK(object); + if (self == NULL) { + GST_ERROR_OBJECT(object, "failed to cast to GST_TIZENIPC_SINK with %p", object); + return; + } + + g_mutex_clear(&self->ipc_lock); + g_cond_clear(&self->ipc_cond); + g_mutex_clear(&self->buffer_lock); + g_cond_clear(&self->buffer_cond); + + if (self->socket_path) { + g_free(self->socket_path); + self->socket_path = NULL; + } + + if (self->socket_path_result) { + g_free(self->socket_path_result); + self->socket_path_result = NULL; + } + + if (self->shm_path) { + g_free(self->shm_path); + self->shm_path = NULL; + } + + G_OBJECT_CLASS(parent_class)->finalize(object); + + return; +} + + +static void gst_tizenipc_sink_set_property(GObject *object, guint prop_id, + const GValue *value, GParamSpec *pspec) +{ + GstTizenipcSink *self = NULL; + + if (object == NULL) { + GST_ERROR("NULL object"); + return; + } + + self = GST_TIZENIPC_SINK(object); + if (self == NULL) { + GST_ERROR_OBJECT(object, "failed to cast to GST_TIZENIPC_SINK with %p", object); + return; + } + + GST_OBJECT_LOCK(object); + + switch (prop_id) { + case PROP_SOCKET_PATH: + { + gchar *temp_string = g_value_dup_string(value); + if (temp_string) { + if (self->socket_path) { + g_free(self->socket_path); + self->socket_path = NULL; + } + self->socket_path = temp_string; + } else { + GST_ERROR_OBJECT(object, "failed to copy string [%s]", g_value_get_string(value)); + } + break; + } + case PROP_PERMISSIONS: + self->permissions = g_value_get_uint(value); + break; + default: + GST_WARNING_OBJECT(object, "unknown property id [%d]", prop_id);; + break; + } + + GST_OBJECT_UNLOCK(object); + + return; +} + + +static void gst_tizenipc_sink_get_property(GObject *object, guint prop_id, + GValue *value, GParamSpec *pspec) +{ + GstTizenipcSink *self = NULL; + + if (object == NULL) { + GST_ERROR("NULL object"); + return; + } + + self = GST_TIZENIPC_SINK(object); + if (self == NULL) { + GST_ERROR_OBJECT(object, "failed to cast to GST_TIZENIPC_SINK with %p", object); + return; + } + + GST_OBJECT_LOCK(object); + + switch (prop_id) { + case PROP_SOCKET_PATH: + g_value_set_string(value, self->socket_path); + break; + case PROP_PERMISSIONS: + g_value_set_uint(value, self->permissions); + break; + default: + GST_WARNING_OBJECT(object, "unknown property id [%d]", prop_id);; + break; + } + + GST_OBJECT_UNLOCK(object); + + return; +} + + +static gboolean gst_tizenipc_sink_start(GstBaseSink *bsink) +{ + GstTizenipcSink *self = NULL; + + if (bsink == NULL) { + GST_ERROR("NULL bsink"); + return FALSE; + } + + self = GST_TIZENIPC_SINK(bsink); + if (self == NULL) { + GST_ERROR_OBJECT(bsink, "failed to cast to GST_TIZENIPC_SINK with %p", bsink); + return FALSE; + } + + /* check socket path and buffer list */ + if (self->socket_path == NULL || + self->sended_buffer == NULL) { + GST_ERROR_OBJECT(self, "socket path[%p] or sended buffer [%p] is NULL", + self->socket_path, self->sended_buffer); + return FALSE; + } + + /* create socket and shared memory for sending buffer */ + if (!_prepare_tizenipc_sink(self, sizeof(MMVideoBuffer) + sizeof(int)*MM_VIDEO_BUFFER_PLANE_MAX)) { + GST_ERROR_OBJECT(self, "prepare failed"); + return FALSE; + } + + return TRUE; +} + + +static gboolean gst_tizenipc_sink_stop(GstBaseSink *bsink) +{ + GstTizenipcSink *self = NULL; + gint64 wait_end_time = 0; + + if (bsink == NULL) { + GST_ERROR("NULL bsink"); + return FALSE; + } + + self = GST_TIZENIPC_SINK(bsink); + if (self == NULL) { + GST_ERROR_OBJECT(bsink, "failed to cast to GST_TIZENIPC_SINK with %p", bsink); + return FALSE; + } + + GST_INFO_OBJECT(self, "start"); + + /* stop poll thread */ + self->poll_thread_run = FALSE; + if (self->poll) { + gst_poll_set_flushing(self->poll, TRUE); + } + + if (self->poll_thread) { + GST_INFO_OBJECT(self, "join poll thread %p", self->poll_thread); + + g_thread_join(self->poll_thread); + self->poll_thread = NULL; + } else { + GST_WARNING_OBJECT(self, "no poll thread"); + } + + /* wait for sended buffer */ + g_mutex_lock(&self->buffer_lock); + + while (self->sended_buffer_count > 0) { + wait_end_time = g_get_monotonic_time () + BUFFER_WAIT_TIMEOUT; + if (!g_cond_wait_until(&self->ipc_cond, &self->ipc_lock, wait_end_time)) { + GST_WARNING_OBJECT(self, "wait timeout - current count %d", + self->sended_buffer_count); + break; + } else { + GST_WARNING_OBJECT(self, "signal received - current count %d", + self->sended_buffer_count); + } + } + + g_mutex_unlock(&self->buffer_lock); + + /* close client */ + if (self->client_fd >= 0) { + GST_INFO_OBJECT(self, "close client fd %d", self->client_fd); + + shutdown(self->client_fd, SHUT_RDWR); + close(self->client_fd); + g_signal_emit(self, signals[SIGNAL_CLIENT_DISCONNECTED], 0, self->client_fd); + self->client_fd = -1; + } else { + GST_WARNING_OBJECT(self, "no client"); + } + + /* release shared memory */ + if (self->shm_fd >= 0) { + if (self->shm_mapped_area != MAP_FAILED) { + munmap(self->shm_mapped_area, self->shm_mapped_size); + self->shm_mapped_area = MAP_FAILED; + self->shm_mapped_size = 0; + } + + close(self->shm_fd); + self->shm_fd = -1; + + if (self->shm_path) { + shm_unlink(self->shm_path); + g_free(self->shm_path); + self->shm_path = NULL; + } + } + + /* release gst poll */ + if (self->poll) { + GST_INFO_OBJECT(self, "close gst poll %p", self->poll); + + gst_poll_free(self->poll); + self->poll = NULL; + } else { + GST_WARNING_OBJECT(self, "no gst poll"); + } + + /* close socket */ + if (self->socket_fd >= 0) { + GST_INFO_OBJECT(self, "close main socket %d", self->socket_fd); + + shutdown(self->socket_fd, SHUT_RDWR); + close(self->socket_fd); + self->socket_fd = -1; + } else { + GST_WARNING_OBJECT(self, "socket is not opened"); + } + + if (self->socket_path_result) { + unlink(self->socket_path_result); + } + + GST_INFO_OBJECT(self, "done"); + + return TRUE; +} + + +static GstFlowReturn gst_tizenipc_sink_render(GstBaseSink *bsink, GstBuffer *buf) +{ + GstTizenipcSink *self = NULL; + GstTizenipcMessage msg = {0, }; + MMVideoBuffer *mm_buf = NULL; + GstMemory *mm_buf_memory = NULL; + GstMapInfo map_info = GST_MAP_INFO_INIT; + gint64 wait_end_time = 0; + int tbm_key[MM_VIDEO_BUFFER_PLANE_MAX] = {0, }; + int i = 0; + + if (bsink == NULL) { + GST_ERROR("NULL bsink"); + return GST_FLOW_ERROR; + } + + self = GST_TIZENIPC_SINK(bsink); + if (self == NULL) { + GST_ERROR_OBJECT(bsink, "failed to cast to GST_TIZENIPC_SINK with %p", bsink); + return GST_FLOW_ERROR; + } + + if (buf == NULL) { + GST_ERROR_OBJECT(self, "NULL buffer"); + return GST_FLOW_ERROR; + } + + g_mutex_lock(&self->ipc_lock); + + if (self->client_fd < 0) { + GST_WARNING_OBJECT(self, "no client is connected"); + goto _SKIP_BUFFER; + } + + /* get mm_buf from gst buffer */ + if (gst_buffer_n_memory(buf) <= 1) { + GST_WARNING_OBJECT(self, "invalid memory number %d", gst_buffer_n_memory(buf)); + goto _SKIP_BUFFER; + } + + mm_buf_memory = gst_buffer_peek_memory(buf, 1); + if (mm_buf_memory == NULL) { + GST_WARNING_OBJECT(self, "failed to peek memory 1 for %p", buf); + goto _SKIP_BUFFER; + } + + if (gst_memory_map(mm_buf_memory, &map_info, GST_MAP_READ) == FALSE) { + GST_WARNING_OBJECT(self, "failed to map memory %p", mm_buf_memory); + goto _SKIP_BUFFER; + } + + mm_buf = (MMVideoBuffer *)map_info.data; + + gst_memory_unmap(mm_buf_memory, &map_info); + + if (mm_buf == NULL) { + GST_WARNING_OBJECT(self, "NULL mm_buf"); + goto _SKIP_BUFFER; + } + + GST_LOG_OBJECT(self, "MMVideoBuffer info - %p, num handle %d", + mm_buf, mm_buf->handle_num); + + /* export bo to pass buffer to client process */ + for (i = 0 ; i < mm_buf->handle_num ; i++) { + if (mm_buf->handle.bo[i]) { + tbm_key[i] = tbm_bo_export(mm_buf->handle.bo[i]); + GST_LOG_OBJECT(self, "export tbm key[index:%d] %d", i, tbm_key[i]); + if (tbm_key[i] <= 0) { + GST_ERROR_OBJECT(self, "failed to export bo[%d] %p", i, mm_buf->handle.bo[i]); + goto _SKIP_BUFFER; + } + } else { + break; + } + } + + /* keep and send buffer */ + if (_add_buffer_to_list(self, buf, tbm_key) == FALSE) { + GST_ERROR_OBJECT(self, "failed to add to list for buffer %p and key[0] %d", buf, tbm_key[0]); + goto _SKIP_BUFFER; + } + + /* set command type and size */ + msg.type = TIZEN_IPC_BUFFER_NEW; + msg.size = sizeof(MMVideoBuffer) + sizeof(tbm_key); + + /* copy zero copy info to shared memory */ + memcpy(self->shm_mapped_area, mm_buf, sizeof(MMVideoBuffer)); + memcpy(self->shm_mapped_area + sizeof(MMVideoBuffer), tbm_key, sizeof(tbm_key)); + + /* send data */ + if (send(self->client_fd, &msg, sizeof(GstTizenipcMessage), MSG_NOSIGNAL) != sizeof(GstTizenipcMessage)) { + GST_ERROR_OBJECT(self, "failed to send buffer to src"); + goto _SKIP_BUFFER_AFTER_ADD_TO_LIST; + } + + /* wait for client's response */ + GST_LOG_OBJECT(self, "Wait for client's response"); + + wait_end_time = g_get_monotonic_time () + CLIENT_RESPONSE_TIMEOUT; + + if (!g_cond_wait_until(&self->ipc_cond, &self->ipc_lock, wait_end_time)) { + GST_ERROR_OBJECT(self, "response wait timeout[%lld usec]", CLIENT_RESPONSE_TIMEOUT); + g_mutex_unlock(&self->ipc_lock); + goto _SKIP_BUFFER_AFTER_ADD_TO_LIST; + } else { + GST_LOG_OBJECT(self, "response received."); + } + + g_mutex_unlock(&self->ipc_lock); + + return GST_FLOW_OK; + +_SKIP_BUFFER_AFTER_ADD_TO_LIST: + _remove_buffer_from_list(self, tbm_key); + +_SKIP_BUFFER: + g_mutex_unlock(&self->ipc_lock); + + return GST_FLOW_OK; +} + + +static gboolean gst_tizenipc_sink_event(GstBaseSink *bsink, GstEvent *event) +{ + GstTizenipcSink *self = NULL; + + if (bsink == NULL) { + GST_ERROR("NULL object"); + return FALSE; + } + + self = GST_TIZENIPC_SINK(bsink); + if (self == NULL) { + GST_ERROR_OBJECT(bsink, "failed to cast to GST_TIZENIPC_SINK with %p", bsink); + return FALSE; + } + + switch (GST_EVENT_TYPE(event)) { + case GST_EVENT_EOS: + /* wait for sended buffer */ + break; + default: + break; + } + + return GST_BASE_SINK_CLASS(parent_class)->event(bsink, event); +} + + +static gboolean gst_tizenipc_sink_unlock(GstBaseSink *bsink) +{ + return TRUE; +} + + +static gboolean gst_tizenipc_sink_unlock_stop(GstBaseSink *bsink) +{ + return TRUE; +} + + +static gpointer _gst_poll_thread_func(gpointer data) +{ + GstTizenipcSink *self = NULL; + GstClockTime timeout = GST_CLOCK_TIME_NONE; + + if (data == NULL) { + GST_ERROR("NULL data"); + return NULL; + } + + self = GST_TIZENIPC_SINK(data); + if (self == NULL) { + GST_ERROR("failed to cast GST_TIZENIPC_SINK"); + return NULL; + } + + GST_INFO_OBJECT(self, "start"); + + while (self->poll_thread_run) { + if (gst_poll_wait(self->poll, timeout) < 0) { + GST_ERROR_OBJECT(self, "failed to wait gst poll. errno %d", errno); + return NULL; + } + + timeout = GST_CLOCK_TIME_NONE; + + if (self->poll_thread_run == FALSE) { + GST_INFO_OBJECT(self, "stop poll thread"); + return NULL; + } + + if (gst_poll_fd_has_closed(self->poll, &self->pollfd)) { + GST_ERROR_OBJECT(self, "failed to read from socket fd. It's closed."); + return NULL; + } + + if (gst_poll_fd_has_error(self->poll, &self->pollfd)) { + GST_ERROR_OBJECT(self, "failed to read from socket fd. It has error."); + return NULL; + } + + if (gst_poll_fd_can_read(self->poll, &self->pollfd)) { + GstTizenipcMessage msg = {0, }; + + /* connect client */ + self->client_fd = accept(self->socket_fd, NULL, NULL); + if (self->client_fd < 0) { + GST_ERROR_OBJECT(self, "can not connect client"); + continue; + } + + GST_INFO_OBJECT(self, "client accpeted : fd %d", self->client_fd); + + /* send shard memory path */ + msg.type = TIZEN_IPC_SHM_PATH; + msg.size = strlen(self->shm_path) + 1; + if (send(self->client_fd, &msg, sizeof(GstTizenipcMessage), MSG_NOSIGNAL) != sizeof(GstTizenipcMessage)) { + GST_ERROR_OBJECT(self, "failed to send shard memory path 1"); + close(self->client_fd); + self->client_fd = -1; + continue; + } + + if (send(self->client_fd, self->shm_path, strlen(self->shm_path) + 1, MSG_NOSIGNAL) != (strlen(self->shm_path) + 1)) { + GST_ERROR_OBJECT(self, "failed to send shard memory path 2"); + close(self->client_fd); + self->client_fd = -1; + continue; + } + + GST_INFO_OBJECT(self, "send shm path done - %s", self->shm_path); + + gst_poll_fd_init(&self->client_pollfd); + self->client_pollfd.fd = self->client_fd; + gst_poll_add_fd(self->poll, &self->client_pollfd); + gst_poll_fd_ctl_read(self->poll, &self->client_pollfd, TRUE); + + g_signal_emit(self, signals[SIGNAL_CLIENT_CONNECTED], 0, self->client_pollfd.fd); + timeout = 0; + continue; + } + + if (self->client_fd > -1) { + if (gst_poll_fd_has_closed(self->poll, &self->client_pollfd)) { + GST_WARNING_OBJECT(self, "client is gone, closing"); + goto close_client; + } + + if (gst_poll_fd_has_error(self->poll, &self->client_pollfd)) { + GST_WARNING_OBJECT(self, "client fd has error, closing"); + goto close_client; + } + + /* handle message from client */ + if (gst_poll_fd_can_read(self->poll, &self->client_pollfd)) { + GstTizenipcMessage msg = {0, }; + int *tbm_key = NULL; + + if (recv(self->client_fd, &msg, sizeof(GstTizenipcMessage), MSG_DONTWAIT) != sizeof(GstTizenipcMessage)) { + GST_ERROR_OBJECT(self, "failed to receive message from client, closing"); + goto close_client; + } + + switch (msg.type) { + case TIZEN_IPC_BUFFER_NEW: + GST_WARNING_OBJECT(self, "BUFFER_NEW???"); + break; + case TIZEN_IPC_BUFFER_RECEIVED: + GST_LOG_OBJECT(self, "response message received"); + g_mutex_lock(&self->ipc_lock); + g_cond_signal(&self->ipc_cond); + g_mutex_unlock(&self->ipc_lock); + break; + case TIZEN_IPC_BUFFER_RELEASE: + tbm_key = msg.tbm_key; + + GST_LOG_OBJECT(self, "BUFFER_RELEASE : tbm key %d %d %d %d", + tbm_key[0], tbm_key[1], tbm_key[2], tbm_key[3]); + + _remove_buffer_from_list(self, tbm_key); + break; + default: + GST_WARNING_OBJECT(self, "unknown type of message : %d", msg.type); + break; + } + } + + continue; + + close_client: + g_mutex_lock(&self->ipc_lock); + + GST_INFO_OBJECT(self, "close client fd %d", self->client_fd); + + gst_poll_remove_fd(self->poll, &self->client_pollfd); + close(self->client_fd); + self->client_fd = -1; + + g_mutex_unlock(&self->ipc_lock); + + g_signal_emit(self, signals[SIGNAL_CLIENT_DISCONNECTED], 0, self->client_pollfd.fd); + + continue; + } + } + + GST_INFO_OBJECT(self, "end"); + + return NULL; +} + + +static gboolean plugin_init(GstPlugin *plugin) +{ + if (!gst_element_register(plugin, + "tizenipcsink", + GST_RANK_PRIMARY, + GST_TYPE_TIZENIPC_SINK)) { + return FALSE; + } + + GST_DEBUG_CATEGORY_INIT(gst_debug_tizenipc_sink, + "tizenipcsink", + 0, + "Tizen IPC sink element"); + + return TRUE; +} + +GST_PLUGIN_DEFINE(GST_VERSION_MAJOR, + GST_VERSION_MINOR, + tizenipcsink, + "Tizen IPC sink to deliver multimedia video buffer", + plugin_init, VERSION, GST_LICENSE, + "Samsung Electronics Co", "http://www.samsung.com") diff --git a/tizenipc/src/gsttizenipcsink.h b/tizenipc/src/gsttizenipcsink.h new file mode 100644 index 0000000..8c0ba8b --- /dev/null +++ b/tizenipc/src/gsttizenipcsink.h @@ -0,0 +1,108 @@ +/* + * GStreamer Tizen IPC sink + * + * Copyright (C) 2015 Jeongmo Yang + * + * 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, Fifth Floor, + * Boston, MA 02110-1301 USA. + */ + +#ifndef __GST_TIZEN_IPC_SINK_H__ +#define __GST_TIZEN_IPC_SINK_H__ + +#include +#include +#include +#include + +G_BEGIN_DECLS + +#define GST_TYPE_TIZENIPC_SINK (gst_tizenipc_sink_get_type()) +#define GST_TIZENIPC_SINK(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), GST_TYPE_TIZENIPC_SINK, GstTizenipcSink)) +#define GST_TIZENIPC_SINK_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), GST_TYPE_TIZENIPC_SINK, GstTizenipcSinkClass)) +#define GST_IS_TIZENIPC_SINK(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_TIZENIPC_SINK)) +#define GST_IS_TIZENIPC_SINK_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), GST_TYPE_TIZENIPC_SINK)) +#define GST_TIZENIPC_SINK_GET_CLASS(inst) (G_TYPE_INSTANCE_GET_CLASS((inst), GST_TYPE_TIZENIPC_SINK, GstTizenipcSinkClass)) + +#define GST_TIZENIPC_BUFFER_MAX 30 + +typedef struct _GstTizenipcSink GstTizenipcSink; +typedef struct _GstTizenipcSinkClass GstTizenipcSinkClass; +typedef struct _GstTizenipcBuffer GstTizenipcBuffer; +typedef struct _GstTizenipcMessage GstTizenipcMessage; + +struct _GstTizenipcSink { + GstBaseSink parent; + + /* ipc */ + int socket_fd; + int shm_fd; + GThread *poll_thread; + gboolean poll_thread_run; + gboolean is_connected; + GstPoll *poll; + GstPollFD pollfd; + int client_fd; + GstPollFD client_pollfd; + gchar *socket_path_result; + gchar *shm_path; + gchar *shm_mapped_area; + gint shm_mapped_size; + GMutex ipc_lock; + GCond ipc_cond; + + /* Property */ + gchar *socket_path; + guint permissions; + + /* buffer management */ + GstTizenipcBuffer *sended_buffer; + guint sended_buffer_count; + GMutex buffer_lock; + GCond buffer_cond; +}; + +struct _GstTizenipcSinkClass { + GstBaseSinkClass parent_class; +}; + +struct _GstTizenipcBuffer { + GstBuffer *gst_buf; + guint tbm_key[MM_VIDEO_BUFFER_PLANE_MAX]; +}; + + +enum { + TIZEN_IPC_SHM_PATH = 0, + TIZEN_IPC_BUFFER_NEW, + TIZEN_IPC_BUFFER_RECEIVED, + TIZEN_IPC_BUFFER_RELEASE +}; + +struct _GstTizenipcMessage { + int type; + union { + int size; + int tbm_key[MM_VIDEO_BUFFER_PLANE_MAX]; + }; +}; + +GType +gst_tizenipc_sink_get_type (void) + G_GNUC_CONST; + +G_END_DECLS + +#endif /* __GST_TIZEN_IPC_SINK_H__ */ diff --git a/tizenipc/src/gsttizenipcsrc.c b/tizenipc/src/gsttizenipcsrc.c new file mode 100644 index 0000000..bb3aab0 --- /dev/null +++ b/tizenipc/src/gsttizenipcsrc.c @@ -0,0 +1,931 @@ +/* + * GStreamer Tizen IPC source + * + * Copyright (C) 2015 Jeongmo Yang + * + * 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, Fifth Floor, + * Boston, MA 02110-1301 USA. + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "gsttizenipcsrc.h" + +#define DEFAULT_SOCKET_PATH "/tmp/tizenipc.0" +#define DEFAULT_SHM_PATH "/tizenipcshm" +#define DEFAULT_PERMISSIONS (S_IRUSR|S_IWUSR|S_IRGRP) +#define DEFAULT_BACKLOG 5 +#define CLIENT_RESPONSE_TIMEOUT (G_TIME_SPAN_MILLISECOND * 200) +#define BUFFER_WAIT_TIMEOUT (G_TIME_SPAN_MILLISECOND * 3000) + +GST_DEBUG_CATEGORY(gst_debug_tizenipc_src); + +#define GST_CAT_DEFAULT gst_debug_tizenipc_src + +static GstStaticPadTemplate srctemplate = \ + GST_STATIC_PAD_TEMPLATE("src", + GST_PAD_SRC, + GST_PAD_ALWAYS, + GST_STATIC_CAPS_ANY); + +#define gst_tizenipc_src_parent_class parent_class +G_DEFINE_TYPE(GstTizenipcSrc, gst_tizenipc_src, GST_TYPE_PUSH_SRC); + +/* signals */ +enum { + LAST_SIGNAL +}; + +/* properties */ +enum { + PROP_0, + PROP_SOCKET_PATH, + PROP_IS_LIVE +}; + + + +static void gst_tizenipc_src_finalize(GObject *object); +static void gst_tizenipc_src_set_property(GObject *object, guint prop_id, + const GValue *value, GParamSpec *pspec); +static void gst_tizenipc_src_get_property(GObject *object, guint prop_id, + GValue *value, GParamSpec *pspec); +static gboolean gst_tizenipc_src_start(GstBaseSrc *bsrc); +static gboolean gst_tizenipc_src_stop(GstBaseSrc *bsrc); +static GstFlowReturn gst_tizenipc_src_create(GstPushSrc *psrc, GstBuffer **outbuf); +static gboolean gst_tizenipc_src_unlock(GstBaseSrc *bsrc); +static gboolean gst_tizenipc_src_unlock_stop(GstBaseSrc *bsrc); +static GstStateChangeReturn gst_tizenipc_src_change_state(GstElement *element, GstStateChange transition); +static void gst_tizenipc_src_buffer_finalize(GstTizenipcSrcBuffer *buffer); + + + +static gboolean _tizenipc_src_prepare_to_read(GstTizenipcSrc *self) +{ + struct sockaddr_un addr_un; + struct sockaddr *address = NULL; + socklen_t address_len = 0; + int flags = 0; + + if (self == NULL) { + GST_ERROR("NULL instance"); + return FALSE; + } + + if (self->socket_path == NULL) { + GST_ERROR_OBJECT(self, "socket path is NULL"); + return FALSE; + } + + if (self->bufmgr == NULL) { + GST_ERROR_OBJECT(self, "tbm bufmgr is not initialized"); + return FALSE; + } + + GST_INFO_OBJECT(self, "start"); + + /* socket connection */ + self->socket_fd = socket(PF_UNIX, SOCK_STREAM, 0); + if (self->socket_fd < 0) { + GST_ERROR_OBJECT(self, "failed to open socket"); + goto _PREPARE_FAILED; + } + + flags = fcntl(self->socket_fd, F_GETFL, 0); + if (flags < 0) { + GST_ERROR_OBJECT(self, "failed to fcntl F_GETFL for socket fd %d", self->socket_fd); + goto _PREPARE_FAILED; + } + + if (fcntl(self->socket_fd, F_SETFL, flags|FD_CLOEXEC) < 0) { + GST_ERROR_OBJECT(self, "failed to fcntl F_SETFL FD_CLOEXEC for socket fd %d", self->socket_fd); + goto _PREPARE_FAILED; + } + + addr_un.sun_family = AF_UNIX; + strncpy(addr_un.sun_path, self->socket_path, sizeof(addr_un.sun_path)-1); + + address = (struct sockaddr *)(&addr_un); + address_len = sizeof(addr_un); + + if (connect(self->socket_fd, address, address_len) < 0) { + GST_ERROR_OBJECT(self, "failed to connect for socket fd %d", self->socket_fd); + goto _PREPARE_FAILED; + } + + /* gst poll init */ + gst_poll_set_flushing(self->poll, FALSE); + gst_poll_fd_init(&self->pollfd); + self->pollfd.fd = self->socket_fd; + gst_poll_add_fd(self->poll, &self->pollfd); + gst_poll_fd_ctl_read(self->poll, &self->pollfd, TRUE); + + GST_INFO_OBJECT(self, "done - socket fd %d", self->socket_fd); + + return TRUE; + +_PREPARE_FAILED: + if (self->socket_fd >= 0) { + shutdown(self->socket_fd, SHUT_RDWR); + close(self->socket_fd); + self->socket_fd = -1; + } + + if (self->socket_path) { + unlink(self->socket_path); + } + + return FALSE; +} + + +static gboolean _tizenipc_src_stop_to_read(GstTizenipcSrc *self) +{ + gint64 wait_end_time = 0; + + if (self == NULL) { + GST_ERROR("NULL instance"); + return FALSE; + } + + GST_INFO_OBJECT(self, "start - socket fd %d, live buffer count %d", + self->socket_fd, self->live_buffer_count); + + /* wait for buffers */ + g_mutex_lock(&self->buffer_lock); + + while (self->live_buffer_count > 0) { + wait_end_time = g_get_monotonic_time () + BUFFER_WAIT_TIMEOUT; + if (!g_cond_wait_until(&self->buffer_cond, &self->buffer_lock, wait_end_time)) { + GST_WARNING_OBJECT(self, "wait timeout - current count %d", + self->live_buffer_count); + break; + } else { + GST_WARNING_OBJECT(self, "signal received - current count %d", + self->live_buffer_count); + } + } + + g_mutex_unlock(&self->buffer_lock); + + if (self->socket_fd >= 0) { + shutdown(self->socket_fd, SHUT_RDWR); + close(self->socket_fd); + self->socket_fd = -1; + } + + if (self->socket_path) { + unlink(self->socket_path); + } + + if (self->shm_mapped_area) { + munmap(self->shm_mapped_area, self->shm_mapped_size); + self->shm_mapped_area = MAP_FAILED; + } + + if (self->shm_fd) { + close(self->shm_fd); + self->shm_fd = -1; + } + + GST_INFO_OBJECT(self, "done"); + + return TRUE; +} + + +/* ---------------------- */ +/* MAIN METHODS */ +/* ---------------------- */ + +static void gst_tizenipc_src_init(GstTizenipcSrc *self) +{ + g_mutex_init(&self->buffer_lock); + g_cond_init(&self->buffer_cond); + + self->socket_fd = -1; + self->shm_fd = -1; + self->shm_mapped_area = MAP_FAILED; + self->bufmgr = tbm_bufmgr_init(-1); + self->socket_path = g_strdup(DEFAULT_SOCKET_PATH); + if (self->socket_path == NULL) { + GST_ERROR_OBJECT(self, "failed to dup socket path [%s]", DEFAULT_SOCKET_PATH); + } + self->poll = gst_poll_new(TRUE); + if (self->poll == NULL) { + GST_ERROR_OBJECT(self, "failed to get gst poll"); + } else { + gst_poll_fd_init(&self->pollfd); + } + + return; +} + +static void gst_tizenipc_src_class_init(GstTizenipcSrcClass *klass) +{ + GObjectClass *gobject_class = NULL; + GstElementClass *gstelement_class = NULL; + GstBaseSrcClass *gstbasesrc_class = NULL; + GstPushSrcClass *gstpush_src_class = NULL; + GParamSpec *pspec = NULL; + + gobject_class = (GObjectClass *)klass; + gstelement_class = (GstElementClass *)klass; + gstbasesrc_class = (GstBaseSrcClass *)klass; + gstpush_src_class = (GstPushSrcClass *)klass; + + gobject_class->set_property = gst_tizenipc_src_set_property; + gobject_class->get_property = gst_tizenipc_src_get_property; + gobject_class->finalize = gst_tizenipc_src_finalize; + + gstelement_class->change_state = gst_tizenipc_src_change_state; + + gstbasesrc_class->start = GST_DEBUG_FUNCPTR(gst_tizenipc_src_start); + gstbasesrc_class->stop = GST_DEBUG_FUNCPTR(gst_tizenipc_src_stop); + gstbasesrc_class->unlock = GST_DEBUG_FUNCPTR(gst_tizenipc_src_unlock); + gstbasesrc_class->unlock_stop = GST_DEBUG_FUNCPTR(gst_tizenipc_src_unlock_stop); + + gstpush_src_class->create = gst_tizenipc_src_create; + + /* property */ + pspec = g_param_spec_string("socket-path", + "Path to the control socket", + "The path to the control socket used to handle IPC", + NULL, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); + if (pspec) { + g_object_class_install_property(gobject_class, PROP_SOCKET_PATH, pspec); + } else { + GST_ERROR("failed to get pspec for \"socket-path\""); + } + + pspec = g_param_spec_boolean ("is-live", "Is this a live source", + "True if the element cannot produce data in PAUSED", + FALSE, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); + if (pspec) { + g_object_class_install_property (gobject_class, PROP_IS_LIVE, pspec); + } else { + GST_ERROR("failed to get pspec for \"is-live\""); + } + + gst_element_class_add_pad_template(gstelement_class, + gst_static_pad_template_get(&srctemplate)); + + gst_element_class_set_static_metadata(gstelement_class, + "Tizen IPC Source", + "Source", + "Receive data via IPC from the tizenipcsink", + "Jeongmo Yang "); + + return; +} + + +static void gst_tizenipc_src_finalize(GObject *object) +{ + GstTizenipcSrc *self = NULL; + + if (object == NULL) { + GST_ERROR("NULL object"); + return; + } + + self = GST_TIZENIPC_SRC(object); + if (self == NULL) { + GST_ERROR_OBJECT(object, "failed to cast to GST_TIZENIPC_SRC with %p", object); + return; + } + + GST_INFO_OBJECT(self, "start"); + + g_mutex_clear(&self->buffer_lock); + g_cond_clear(&self->buffer_cond); + + if (self->socket_path) { + g_free(self->socket_path); + self->socket_path = NULL; + } + + if (self->shm_path) { + g_free(self->shm_path); + self->shm_path = NULL; + } + + if (self->poll) { + gst_poll_free(self->poll); + self->poll = NULL; + } + + if (self->bufmgr) { + tbm_bufmgr_deinit(self->bufmgr); + self->bufmgr = NULL; + } + + GST_INFO_OBJECT(self, "done"); + + G_OBJECT_CLASS(parent_class)->finalize(object); + + return; +} + + +static void gst_tizenipc_src_set_property(GObject *object, guint prop_id, + const GValue *value, GParamSpec *pspec) +{ + GstTizenipcSrc *self = NULL; + + if (object == NULL) { + GST_ERROR("NULL object"); + return; + } + + self = GST_TIZENIPC_SRC(object); + if (self == NULL) { + GST_ERROR_OBJECT(object, "failed to cast to GST_TIZENIPC_SRC with %p", object); + return; + } + + switch (prop_id) { + case PROP_SOCKET_PATH: + { + gchar *temp_string = NULL; + + GST_OBJECT_LOCK(object); + + temp_string = g_value_dup_string(value); + if (temp_string) { + if (self->socket_path) { + g_free(self->socket_path); + self->socket_path = NULL; + } + self->socket_path = temp_string; + } else { + GST_ERROR_OBJECT(object, "failed to copy string [%s]", g_value_get_string(value)); + } + + GST_OBJECT_UNLOCK(object); + + break; + } + case PROP_IS_LIVE: + { + gboolean is_live = g_value_get_boolean(value); + GST_INFO_OBJECT(object, "set is-live %d", is_live); + gst_base_src_set_live(GST_BASE_SRC(object), is_live); + break; + } + default: + GST_WARNING_OBJECT(object, "unknown property id [%d]", prop_id);; + break; + } + + return; +} + + +static void gst_tizenipc_src_get_property(GObject *object, guint prop_id, + GValue *value, GParamSpec *pspec) +{ + GstTizenipcSrc *self = NULL; + + if (object == NULL) { + GST_ERROR("NULL object"); + return; + } + + self = GST_TIZENIPC_SRC(object); + if (self == NULL) { + GST_ERROR_OBJECT(object, "failed to cast to GST_TIZENIPC_SRC with %p", object); + return; + } + + switch (prop_id) { + case PROP_SOCKET_PATH: + GST_OBJECT_LOCK(object); + g_value_set_string(value, self->socket_path); + GST_OBJECT_UNLOCK(object); + break; + case PROP_IS_LIVE: + g_value_set_boolean(value, gst_base_src_is_live(GST_BASE_SRC(object))); + break; + default: + GST_WARNING_OBJECT(object, "unknown property id [%d]", prop_id);; + break; + } + + return; +} + + +static gboolean gst_tizenipc_src_start(GstBaseSrc *bsrc) +{ + GstTizenipcSrc *self = NULL; + gboolean is_live = FALSE; + + if (bsrc == NULL) { + GST_ERROR("NULL bsrc"); + return FALSE; + } + + self = GST_TIZENIPC_SRC(bsrc); + if (self == NULL) { + GST_ERROR_OBJECT(bsrc, "failed to cast to GST_TIZENIPC_SRC with %p", bsrc); + return FALSE; + } + + is_live = gst_base_src_is_live(bsrc); + + GST_INFO_OBJECT(bsrc, "is_live : %d", is_live); + + if (is_live) + return TRUE; + else + return _tizenipc_src_prepare_to_read(self); +} + + +static gboolean gst_tizenipc_src_stop(GstBaseSrc *bsrc) +{ + GstTizenipcSrc *self = NULL; + gboolean is_live = FALSE; + + if (bsrc == NULL) { + GST_ERROR("NULL bsrc"); + return FALSE; + } + + self = GST_TIZENIPC_SRC(bsrc); + if (self == NULL) { + GST_ERROR_OBJECT(bsrc, "failed to cast to GST_TIZENIPC_SRC with %p", bsrc); + return FALSE; + } + + is_live = gst_base_src_is_live(bsrc); + + GST_INFO_OBJECT(bsrc, "is_live : %d", is_live); + + if (is_live) + return TRUE; + else + return _tizenipc_src_stop_to_read(self); +} + + +static void gst_tizenipc_src_buffer_finalize(GstTizenipcSrcBuffer *ipc_buf) +{ + GstTizenipcSrc *self = NULL; + GstTizenipcMessage send_msg = {0,}; + MMVideoBuffer *mm_buf = NULL; + int i = 0; + int send_len = 0; + + if (ipc_buf == NULL) { + GST_ERROR("NULL ipc_buf"); + return; + } + + self = ipc_buf->self; + mm_buf = ipc_buf->mm_buf; + + /* send message to sink for current tbm key */ + if (self->socket_fd > -1) { + send_msg.type = TIZEN_IPC_BUFFER_RELEASE; + memcpy(send_msg.tbm_key, ipc_buf->tbm_key, sizeof(int) * MM_VIDEO_BUFFER_PLANE_MAX); + send_len = send(self->socket_fd, &send_msg, sizeof(GstTizenipcMessage), MSG_NOSIGNAL); + if (send_len != sizeof(GstTizenipcMessage)) { + GST_ERROR_OBJECT(self, "failed to send BUFFER_RELEASE message"); + } + } else { + GST_ERROR_OBJECT(self, "invalid socket fd %d", self->socket_fd); + } + + if (mm_buf) { + for (i = 0 ; i < mm_buf->handle_num ; i++) { + if (mm_buf->handle.bo[i]) { + tbm_bo_unref(mm_buf->handle.bo[i]); + mm_buf->handle.bo[i] = NULL; + } else { + break; + } + } + + free(mm_buf); + mm_buf = NULL; + } + + /* send buffer signal */ + g_mutex_lock(&self->buffer_lock); + + GST_DEBUG_OBJECT(self, "live buffer(tbm key[0] %d) count %d -> %d", + ipc_buf->tbm_key[0], self->live_buffer_count, self->live_buffer_count-1); + + self->live_buffer_count--; + g_cond_signal(&self->buffer_cond); + + g_mutex_unlock(&self->buffer_lock); + + if (self) { + gst_object_unref(self); + self = NULL; + } + + free(ipc_buf); + ipc_buf = NULL; + + return; +} + + +static GstFlowReturn gst_tizenipc_src_create(GstPushSrc *psrc, GstBuffer **outbuf) +{ + GstTizenipcSrc *self = NULL; + GstTizenipcSrcBuffer *ipc_buf = NULL; + GstTizenipcMessage recv_msg = {0,}; + GstTizenipcMessage send_msg = {0,}; + MMVideoBuffer *mm_buf = NULL; + GstBuffer *gst_buf = NULL; + GstMemory *gst_memory = NULL; + int i = 0; + int recv_len = 0; + int send_len = 0; + + if (psrc == NULL) { + GST_ERROR("NULL psrc"); + return GST_FLOW_ERROR; + } + + self = GST_TIZENIPC_SRC(psrc); + if (self == NULL) { + GST_ERROR_OBJECT(psrc, "failed to cast to GST_TIZENIPC_SRC with %p", psrc); + return GST_FLOW_ERROR; + } + + if (outbuf == NULL) { + GST_ERROR_OBJECT(self, "NULL buffer pointer"); + return GST_FLOW_ERROR; + } + +again: + if (gst_poll_wait (self->poll, GST_CLOCK_TIME_NONE) < 0) { + if (errno == EBUSY) + return GST_FLOW_FLUSHING; + GST_ELEMENT_ERROR(self, RESOURCE, READ, ("Failed to read from sink"), + ("Poll failed on fd: %s", strerror (errno))); + return GST_FLOW_ERROR; + } + + if (gst_poll_fd_has_closed (self->poll, &self->pollfd)) { + GST_ELEMENT_ERROR(self, RESOURCE, READ, ("Failed to read from sink"), + ("Control socket has closed")); + return GST_FLOW_ERROR; + } + + if (gst_poll_fd_has_error (self->poll, &self->pollfd)) { + GST_ELEMENT_ERROR(self, RESOURCE, READ, ("Failed to read from sink"), + ("Control socket has error")); + return GST_FLOW_ERROR; + } + + if (gst_poll_fd_can_read (self->poll, &self->pollfd)) { + GST_LOG_OBJECT(self, "Reading from sink"); + + GST_OBJECT_LOCK(self); + + /* receive message from sink */ + recv_len = recv(self->socket_fd, &recv_msg, sizeof(GstTizenipcMessage), MSG_DONTWAIT); + + GST_OBJECT_UNLOCK(self); + + if (recv_len != sizeof(GstTizenipcMessage)) { + GST_ERROR_OBJECT(self, "failed to receive message from sink %d : %d", + recv_len, sizeof(GstTizenipcMessage)); + return GST_FLOW_ERROR; + } + + /* handle message */ + if (recv_msg.type == TIZEN_IPC_BUFFER_NEW) { + /* get new buffer from sink */ + if (self->shm_mapped_area == MAP_FAILED) { + GST_ERROR_OBJECT(self, "shared memory is not mapped"); + return GST_FLOW_ERROR; + } + + mm_buf = (MMVideoBuffer *)malloc(sizeof(MMVideoBuffer)); + if (mm_buf) { + memcpy(mm_buf, self->shm_mapped_area, sizeof(MMVideoBuffer)); + memcpy(send_msg.tbm_key, self->shm_mapped_area + sizeof(MMVideoBuffer), sizeof(int) * MM_VIDEO_BUFFER_PLANE_MAX); + + for (i = 0 ; i < MM_VIDEO_BUFFER_PLANE_MAX ; i++) { + if (send_msg.tbm_key[i] > 0) { + tbm_bo_handle bo_handle = {0, }; + + GST_LOG_OBJECT(self, "received tbm key[%d] %d", i, send_msg.tbm_key[i]); + + /* import bo from tbm key */ + mm_buf->handle.bo[i] = tbm_bo_import(self->bufmgr, send_msg.tbm_key[i]); + if (mm_buf->handle.bo[i] == NULL) { + GST_ERROR_OBJECT(self, "failed to import bo for tbm key %d", send_msg.tbm_key[i]); + break; + } + + /* get user address */ + bo_handle = tbm_bo_get_handle(mm_buf->handle.bo[i], TBM_DEVICE_CPU); + if (bo_handle.ptr == NULL) { + GST_ERROR_OBJECT(self, "failed to get user address for bo %p, key %d", + mm_buf->handle.bo[i], send_msg.tbm_key[i]); + break; + } + mm_buf->data[i] = bo_handle.ptr; + } else { + break; + } + } + } else { + GST_ERROR_OBJECT(self, "failed to alloc MMVideoBuffer"); + } + + /* send received message */ + send_msg.type = TIZEN_IPC_BUFFER_RECEIVED; + + GST_OBJECT_LOCK(self); + + send_len = send(self->socket_fd, &send_msg, sizeof(GstTizenipcMessage), MSG_NOSIGNAL); + + GST_OBJECT_UNLOCK(self); + + if (send_len != sizeof(GstTizenipcMessage)) { + GST_ERROR_OBJECT(self, "failed to send RECEIVED message"); + } + } else if (recv_msg.type == TIZEN_IPC_SHM_PATH) { + gchar shm_path[32] = {'\0',}; + int shm_size = sizeof(MMVideoBuffer) + (sizeof(int) * MM_VIDEO_BUFFER_PLANE_MAX); + + /* get shm path */ + recv_len = recv(self->socket_fd, shm_path, recv_msg.size, 0); + if (recv_len != recv_msg.size) { + GST_ERROR_OBJECT(self, "failed to receive message from sink %d : %d", + recv_len, recv_msg.size); + return GST_FLOW_ERROR; + } + + GST_INFO_OBJECT(self, "shm path from sink [%s]", shm_path); + + if (self->shm_path) { + g_free(self->shm_path); + self->shm_path = NULL; + } + + self->shm_path = g_strdup(shm_path); + if (self->shm_path == NULL) { + GST_ERROR_OBJECT(self, "failed to copy shm path string [%s]", shm_path); + return GST_FLOW_ERROR; + } + + /* open shared memory */ + self->shm_fd = shm_open(self->shm_path, O_RDONLY, shm_size); + if (self->shm_fd < 0) { + GST_ERROR_OBJECT(self, "failed to open shared memory for shm path [%s], size %d", + self->shm_path, shm_size); + return GST_FLOW_ERROR; + } + + GST_INFO_OBJECT(self, "opened shm fd %d", self->shm_fd); + + self->shm_mapped_area = mmap(NULL, + shm_size, + PROT_READ, + MAP_SHARED, + self->shm_fd, + 0); + if (self->shm_mapped_area == MAP_FAILED) { + GST_ERROR_OBJECT(self, "failed to mmap shared memory for fd %d", self->shm_fd); + close(self->shm_fd); + self->shm_fd = -1; + return GST_FLOW_ERROR; + } + + self->shm_mapped_size = shm_size; + + GST_INFO_OBJECT(self, "mapped shared memory address %p, size %d", + self->shm_mapped_area, shm_size); + goto again; + } else { + GST_WARNING_OBJECT(self, "unknown message type %d", recv_msg.type); + goto again; + } + } + + if (mm_buf == NULL) { + GST_ERROR_OBJECT(self, "NULL mm_buf"); + return GST_FLOW_ERROR; + } + + /* make gst buffer with mm_buf */ + gst_buf = gst_buffer_new(); + if (gst_buf == NULL) { + GST_ERROR_OBJECT(self, "failed to create gst buffer"); + goto _CREATE_FAILED; + } + + /* default memory */ + gst_memory = gst_memory_new_wrapped(0, + mm_buf->data[0], + mm_buf->size[0], + 0, + mm_buf->size[0], + NULL, + NULL); + if (gst_memory == NULL) { + GST_ERROR_OBJECT(self, "failed to create default gst memory"); + goto _CREATE_FAILED; + } + + gst_buffer_append_memory(gst_buf, gst_memory); + gst_memory = NULL; + + /* mm_buf memory */ + gst_memory = gst_memory_new_wrapped(0, + mm_buf, + sizeof(MMVideoBuffer), + 0, + sizeof(MMVideoBuffer), + mm_buf, + NULL); + if (gst_memory == NULL) { + GST_ERROR_OBJECT(self, "failed to create gst memory for mm_buf"); + goto _CREATE_FAILED; + } + + gst_buffer_append_memory(gst_buf, gst_memory); + gst_memory = NULL; + + /* ipc_buf memory */ + ipc_buf = (GstTizenipcSrcBuffer *)malloc(sizeof(GstTizenipcSrcBuffer)); + if (ipc_buf == NULL) { + GST_ERROR_OBJECT(self, "failed to create GstTizenipcsrcBuffer"); + goto _CREATE_FAILED; + } + + ipc_buf->self = gst_object_ref(self); + ipc_buf->gst_buf = gst_buf; + ipc_buf->mm_buf = mm_buf; + memcpy(ipc_buf->tbm_key, send_msg.tbm_key, sizeof(int) * MM_VIDEO_BUFFER_PLANE_MAX); + + gst_memory = gst_memory_new_wrapped(0, + ipc_buf, + sizeof(GstTizenipcSrcBuffer), + 0, + sizeof(GstTizenipcSrcBuffer), + ipc_buf, + (GDestroyNotify)gst_tizenipc_src_buffer_finalize); + if (gst_memory == NULL) { + GST_ERROR_OBJECT(self, "failed to create gst memory for ipc_buf"); + goto _CREATE_FAILED; + } + + gst_buffer_append_memory(gst_buf, gst_memory); + gst_memory = NULL; + + g_mutex_lock(&self->buffer_lock); + self->live_buffer_count++; + GST_DEBUG_OBJECT(self, "gst buffer %p, live count %d", gst_buf, self->live_buffer_count); + g_mutex_unlock(&self->buffer_lock); + + *outbuf = gst_buf; + + return GST_FLOW_OK; + +_CREATE_FAILED: + if (ipc_buf) { + free(ipc_buf); + ipc_buf = NULL; + } + + if (mm_buf) { + free(mm_buf); + mm_buf = NULL; + } + + if (gst_memory) { + gst_memory_unref(gst_memory); + gst_memory = NULL; + } + + if (gst_buf) { + gst_buffer_unref(gst_buf); + gst_buf = NULL; + } + + return GST_FLOW_ERROR; +} + + +static gboolean gst_tizenipc_src_unlock(GstBaseSrc *bsrc) +{ + return TRUE; +} + + +static gboolean gst_tizenipc_src_unlock_stop(GstBaseSrc *bsrc) +{ + return TRUE; +} + + +static GstStateChangeReturn gst_tizenipc_src_change_state(GstElement *element, GstStateChange transition) +{ + GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS; + GstTizenipcSrc *self = NULL; + gboolean is_live = FALSE; + + if (element == NULL) { + GST_ERROR("NULL element"); + return FALSE; + } + + self = GST_TIZENIPC_SRC(element); + if (self == NULL) { + GST_ERROR_OBJECT(element, "failed to cast to GST_TIZENIPC_SRC with %p", element); + return FALSE; + } + + is_live = gst_base_src_is_live(GST_BASE_SRC(element)); + + GST_INFO_OBJECT(self, "transition %d - is_live %d", transition, is_live); + + switch (transition) { + case GST_STATE_CHANGE_READY_TO_PAUSED: + if (is_live) + if (!_tizenipc_src_prepare_to_read(self)) + return GST_STATE_CHANGE_FAILURE; + default: + break; + } + + ret = GST_ELEMENT_CLASS(parent_class)->change_state(element, transition); + if (ret == GST_STATE_CHANGE_FAILURE) + return ret; + + switch (transition) { + case GST_STATE_CHANGE_PAUSED_TO_READY: + if (is_live) + if (!_tizenipc_src_stop_to_read(self)) + return GST_STATE_CHANGE_FAILURE; + default: + break; + } + + return ret; +} + + +static gboolean plugin_init(GstPlugin *plugin) +{ + if (!gst_element_register(plugin, + "tizenipcsrc", + GST_RANK_PRIMARY, + GST_TYPE_TIZENIPC_SRC)) { + return FALSE; + } + + GST_DEBUG_CATEGORY_INIT(gst_debug_tizenipc_src, + "tizenipcsrc", + 0, + "Tizen IPC source element"); + + return TRUE; +} + + +GST_PLUGIN_DEFINE(GST_VERSION_MAJOR, + GST_VERSION_MINOR, + tizenipcsrc, + "Tizen IPC source to receive multimedia video buffer", + plugin_init, VERSION, GST_LICENSE, + "Samsung Electronics Co", "http://www.samsung.com") diff --git a/tizenipc/src/gsttizenipcsrc.h b/tizenipc/src/gsttizenipcsrc.h new file mode 100644 index 0000000..70b1be3 --- /dev/null +++ b/tizenipc/src/gsttizenipcsrc.h @@ -0,0 +1,102 @@ +/* + * GStreamer Tizen IPC src + * + * Copyright (C) 2015 Jeongmo Yang + * + * 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, Fifth Floor, + * Boston, MA 02110-1301 USA. + */ + +#ifndef __GST_TIZEN_IPC_SRC_H__ +#define __GST_TIZEN_IPC_SRC_H__ + +#include +#include +#include +#include +#include + +G_BEGIN_DECLS + +#define GST_TYPE_TIZENIPC_SRC (gst_tizenipc_src_get_type()) +#define GST_TIZENIPC_SRC(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), GST_TYPE_TIZENIPC_SRC, GstTizenipcSrc)) +#define GST_TIZENIPC_SRC_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), GST_TYPE_TIZENIPC_SRC, GstTizenipcSrcClass)) +#define GST_IS_TIZENIPC_SRC(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_TIZENIPC_SRC)) +#define GST_IS_TIZENIPC_SRC_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), GST_TYPE_TIZENIPC_SRC)) +#define GST_TIZENIPC_SRC_GET_CLASS(inst) (G_TYPE_INSTANCE_GET_CLASS((inst), GST_TYPE_TIZENIPC_SRC, GstTizenipcSrcClass)) + +#define GST_TIZENIPC_BUFFER_MAX 30 + +typedef struct _GstTizenipcSrc GstTizenipcSrc; +typedef struct _GstTizenipcSrcClass GstTizenipcSrcClass; +typedef struct _GstTizenipcSrcBuffer GstTizenipcSrcBuffer; +typedef struct _GstTizenipcMessage GstTizenipcMessage; + +struct _GstTizenipcSrc { + GstPushSrc parent; + + /* ipc */ + int socket_fd; + int shm_fd; + GstPoll *poll; + GstPollFD pollfd; + gchar *shm_path; + gchar *shm_mapped_area; + gint shm_mapped_size; + + /* Property */ + gchar *socket_path; + + /* buffer management */ + tbm_bufmgr bufmgr; + guint live_buffer_count; + GMutex buffer_lock; + GCond buffer_cond; +}; + +struct _GstTizenipcSrcClass { + GstPushSrcClass parent_class; +}; + +struct _GstTizenipcSrcBuffer { + GstBuffer *gst_buf; + MMVideoBuffer *mm_buf; + int tbm_key[MM_VIDEO_BUFFER_PLANE_MAX]; + GstTizenipcSrc *self; +}; + + +enum { + TIZEN_IPC_SHM_PATH = 0, + TIZEN_IPC_BUFFER_NEW, + TIZEN_IPC_BUFFER_RECEIVED, + TIZEN_IPC_BUFFER_RELEASE +}; + +struct _GstTizenipcMessage { + int type; + union { + int size; + int tbm_key[MM_VIDEO_BUFFER_PLANE_MAX]; + }; +}; + +GType +gst_tizenipc_src_get_type (void) + G_GNUC_CONST; + +G_END_DECLS + +#endif /* __GST_TIZEN_IPC_SRC_H__ */ -- 2.7.4