Revision: 8634
http://playerstage.svn.sourceforge.net/playerstage/?rev=8634&view=rev
Author: thjc
Date: 2010-05-03 16:53:53 +0000 (Mon, 03 May 2010)
Log Message:
-----------
Applied patch 2977971: add gstreamer camera driver
Modified Paths:
--------------
code/player/trunk/server/drivers/camera/CMakeLists.txt
Added Paths:
-----------
code/player/trunk/server/drivers/camera/gstreamer/
code/player/trunk/server/drivers/camera/gstreamer/CMakeLists.txt
code/player/trunk/server/drivers/camera/gstreamer/cameragst.cc
code/player/trunk/server/drivers/camera/gstreamer/gstappsink.c
code/player/trunk/server/drivers/camera/gstreamer/gstappsink.h
Modified: code/player/trunk/server/drivers/camera/CMakeLists.txt
===================================================================
--- code/player/trunk/server/drivers/camera/CMakeLists.txt 2010-05-03
16:38:23 UTC (rev 8633)
+++ code/player/trunk/server/drivers/camera/CMakeLists.txt 2010-05-03
16:53:53 UTC (rev 8634)
@@ -4,6 +4,7 @@
ADD_SUBDIRECTORY (camfilter)
ADD_SUBDIRECTORY (compress)
ADD_SUBDIRECTORY (cvcam)
+ADD_SUBDIRECTORY (gstreamer)
ADD_SUBDIRECTORY (imageseq)
ADD_SUBDIRECTORY (sphere)
ADD_SUBDIRECTORY (uvc)
Added: code/player/trunk/server/drivers/camera/gstreamer/CMakeLists.txt
===================================================================
--- code/player/trunk/server/drivers/camera/gstreamer/CMakeLists.txt
(rev 0)
+++ code/player/trunk/server/drivers/camera/gstreamer/CMakeLists.txt
2010-05-03 16:53:53 UTC (rev 8634)
@@ -0,0 +1,7 @@
+PLAYERDRIVER_OPTION (cameragst build_cameragst ON)
+PLAYERDRIVER_REQUIRE_PKG (cameragst build_cameragst gstreamer-base-0.10
cameragst_includeDirs
+ cameragst_libDirs cameragst_linkLibs cameragst_linkFlags cameragst_cFlags)
+PLAYERDRIVER_ADD_DRIVER (cameragst build_cameragst
+ INCLUDEDIRS ${cameragst_includeDirs} LIBDIRS ${cameragst_libDirs} LINKLIBS
${cameragst_linkLibs}
+ LINKFLAGS ${cameragst_linkFlags} CFLAGS ${cameragst_cFlags}
+ SOURCES gstappsink.c cameragst.cc)
Added: code/player/trunk/server/drivers/camera/gstreamer/cameragst.cc
===================================================================
--- code/player/trunk/server/drivers/camera/gstreamer/cameragst.cc
(rev 0)
+++ code/player/trunk/server/drivers/camera/gstreamer/cameragst.cc
2010-05-03 16:53:53 UTC (rev 8634)
@@ -0,0 +1,623 @@
+/*
+ * Player - One Hell of a Robot Server
+ * Copyright (C) 2000 Brian Gerkey et al.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+///////////////////////////////////////////////////////////////////////////
+//
+// Desc: GStreamer camera plugin
+// Author: Piotr Trojanek
+// $Date: 2008-12-09 16:47:18 -0500 (Tue, 09 Dec 2008) $
+// $Id: gstreamplayer.cc 63 2008-12-09 21:47:18Z ptrojane $
+//
+///////////////////////////////////////////////////////////////////////////
+
+/** @ingroup drivers */
+/** @{ */
+/** @defgroup driver_gstreamer gstreamer
+ * @brief Gstreamer driver
+
+This driver takes image data from Gstreamer infrastructure and publish them
+through provided camera interface.
+
+...@par Compile-time dependencies
+
+- GStreamer
+
+...@par Provides
+
+- This driver supports the @ref interface_camera interface.
+
+...@par Requires
+
+- none
+
+...@par Configuration requests
+
+- none
+
+...@par Configuration file options
+
+- source (string)
+ - Default: ""
+ - A string describing GStreamer source.
+
+- trace (bool)
+ - Default: false
+ - Trace gstreamer object allocation.
+
+- jpeg (integer)
+ - Default: 0
+ - If set to 1, expect and publish JPEG images
+
+...@par Example
+
+...@verbatim
+driver
+(
+ name "cameragst"
+ provides ["camera:0"]
+
+ # read/write image sequence
+ # source "multifilesrc location=%05d.jpg ! jpegdec ! ffmpegcolorspace !
queue"
+ # source "v4l2src ! video/x-raw-yuv,width=640,height=480 ! tee name=tee !
ffmpegcolorspace ! jpegenc ! multifilesink location=%05d.jpg tee. ! queue !
ffmpegcolorspace"
+
+ # read/write movie file
+ # source "filesrc location=movie.ogg ! decodebin ! ffmpegcolorspace"
+ # source "v4l2src ! video/x-raw-yuv,width=640,height=480 ! tee name=tee tee.
! ffmpegcolorspace ! theoraenc ! queue ! oggmux name=mux mux. ! queue !
filesink location=movie.ogg tee. ! ffmpegcolorspace ! queue"
+
+ # read data from V4L2 compatibile camera (UVC works too!)
+ # source "v4l2src ! videoscale ! video/x-raw-yuv, width=640, height=480 !
ffmpegcolorspace"
+
+ # test source
+ source "videotestsrc ! timeoverlay ! ffmpegcolorspace"
+
+ trace false
+)
+...@endverbatim
+
+
+...@todo Add support for mono images
+
+...@author Piotr Trojanek
+
+*/
+/** @} */
+
+#include <stddef.h>
+#include <string.h>
+#include <stdlib.h>
+#include <pthread.h>
+#include <time.h>
+#include <assert.h>
+#include <libplayercore/playercore.h>
+
+#include <gst/gst.h>
+#include "gstappsink.h"
+
+////////////////////////////////////////////////////////////////////////////////
+// The class for the driver
+class GStreamerDriver : public ThreadedDriver
+{
+ public:
+
+ // Constructor; need that
+ GStreamerDriver(ConfigFile* cf, int section);
+
+ ~GStreamerDriver();
+
+ // Must implement the following methods.
+ virtual int MainSetup ();
+ virtual void MainQuit ();
+
+ private:
+
+ // Main function for device thread.
+ virtual void Main();
+
+ // GStreamer source description
+ const char *source;
+
+ // GStreamer elements
+ GstElement *pipeline, *launchpipe, *sink;
+
+ // Handle GStreamer bus messages
+ void HandleMessage();
+ int GrabFrame();
+ int RetrieveFrame();
+
+ // Data buffer
+ GstBuffer *buffer;
+
+ // Data to send to server
+ player_camera_data_t data;
+
+ // Retrieved image size
+ size_t image_size;
+
+ int jpeg;
+
+ // Track object allocation
+ gboolean trace;
+
+ static int initialized;
+};
+
+int GStreamerDriver::initialized = 0;
+
+// A factory creation function, declared outside of the class so that it
+// can be invoked without any object context (alternatively, you can
+// declare it static in the class). In this function, we create and return
+// (as a generic Driver*) a pointer to a new instance of this driver.
+Driver*
+GStreamerDriver_Init(ConfigFile* cf, int section)
+{
+ // Create and return a new instance of this driver
+ return((Driver*)(new GStreamerDriver(cf, section)));
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Registers the driver in the driver table. Called from the
+// player_driver_init function that the loader looks for
+void cameragst_Register (DriverTable* table)
+{
+ table->AddDriver ("cameragst", GStreamerDriver_Init);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Constructor. Retrieve options from the configuration file and do any
+// pre-Setup() setup.
+GStreamerDriver::GStreamerDriver(ConfigFile* cf, int section)
+ : ThreadedDriver(cf, section, true, PLAYER_MSGQUEUE_DEFAULT_MAXLEN,
PLAYER_CAMERA_CODE)
+{
+ this->image_size = 0;
+ this->data.image = NULL;
+ // Read an option from the configuration file
+ this->source = cf->ReadString(section, "source", NULL);
+ if (!(this->source))
+ {
+ PLAYER_ERROR("Source not given.");
+ this->SetError(-1);
+ return;
+ }
+ if (!(strlen(this->source) > 0))
+ {
+ PLAYER_ERROR("Source not given.");
+ this->SetError(-1);
+ return;
+ }
+ this->jpeg = cf->ReadInt(section, "jpeg", 0);
+ this->trace = cf->ReadBool(section, "trace", false);
+
+ GStreamerDriver::initialized++;
+ if ((GStreamerDriver::initialized) != 1)
+ {
+ PLAYER_ERROR("Due to the nature of gstream library API, this driver should
be started one per Player server instance.");
+ this->SetError(-1);
+ return;
+ } else gst_init(NULL, NULL);
+
+ if (this->trace) {
+ gst_alloc_trace_set_flags_all (GST_ALLOC_TRACE_LIVE);
+
+ if (!gst_alloc_trace_available ()) {
+ PLAYER_WARN("Trace not available (recompile with trace enabled).");
+ }
+ gst_alloc_trace_print_live ();
+ }
+
+#ifndef GST_PLUGIN_DEFINE_STATIC
+ // according to the documentation this is the way to register a plugin now
+ if (!gst_plugin_register_static(
+ GST_VERSION_MAJOR,
+ GST_VERSION_MINOR,
+ "opencv-appsink", // a unique name of the plugin
+ "Element application sink", // description of the plugin
+ appsink_plugin_init, // pointer to the init function of this plugin
+ "0.1", // version string of the plugin
+ "LGPL", // effective license of plugin
+ "libplayerdrivers", // source module plugin belongs
to
+ "player", // shipped package plugin belongs to
+ "http://playerstage.sourceforge.net" // URL to provider of plugin
+ ))
+ {
+ PLAYER_ERROR("GStreamer plugin register failed\n");
+ this->SetError(-1);
+ return;
+ }
+#endif
+}
+
+GStreamerDriver::~GStreamerDriver()
+{
+ if (GStreamerDriver::initialized == 1) gst_deinit();
+ GStreamerDriver::initialized--;
+
+ if (this->trace) {
+ gst_alloc_trace_print_live ();
+ }
+ if (this->data.image) free(this->data.image);
+}
+
+void GStreamerDriver::HandleMessage()
+{
+ GstBus* bus = gst_element_get_bus(this->pipeline);
+
+ while(gst_bus_have_pending(bus)) {
+
+ GstMessage* msg = gst_bus_pop(bus);
+
+ //printf("Got %s message\n", GST_MESSAGE_TYPE_NAME(msg));
+
+ switch (GST_MESSAGE_TYPE (msg)) {
+ case GST_MESSAGE_STATE_CHANGED:
+ GstState oldstate, newstate, pendstate;
+ gst_message_parse_state_changed(msg, &oldstate,
&newstate, &pendstate);
+
+ /*
+ printf("state changed from %s to %s (%s pending)\n",
+ gst_element_state_get_name(oldstate),
+ gst_element_state_get_name(newstate),
+ gst_element_state_get_name(pendstate)
+ );
+ */
+
+ break;
+ case GST_MESSAGE_ERROR: {
+ GError *err;
+ gchar *debug;
+ gst_message_parse_error(msg, &err, &debug);
+
+ PLAYER_ERROR2("GStreamer Plugin: Embedded video
playback halted; module %s reported: %s",
+ gst_element_get_name(GST_MESSAGE_SRC (msg)),
err->message);
+
+ g_error_free(err);
+ g_free(debug);
+
+ gst_element_set_state(this->pipeline, GST_STATE_NULL);
+
+ break;
+ }
+ case GST_MESSAGE_EOS:
+ PLAYER_WARN("NetStream has reached the end of the
stream.");
+ break;
+ case GST_MESSAGE_ASYNC_DONE:
+ // TODO: pipeline state changed, should be blocking?
+ break;
+ case GST_MESSAGE_NEW_CLOCK:
+ // TODO: something?
+ break;
+ default:
+ PLAYER_WARN1("unhandled message %s\n",
GST_MESSAGE_TYPE_NAME(msg));
+ break;
+ }
+
+ gst_message_unref(msg);
+ }
+
+ gst_object_unref(GST_OBJECT(bus));
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Set up the device. Return 0 if things go well, and -1 otherwise.
+int GStreamerDriver::MainSetup()
+{
+ GError *err = NULL;
+ launchpipe = gst_parse_launch(this->source, &err);
+
+ if (launchpipe == NULL && err) {
+ PLAYER_ERROR1("unable to parse GStreamer pipe: %s", err->message);
+ g_error_free(err);
+ return -1;
+ }
+
+ sink = gst_element_factory_make("player-appsink", NULL);
+ if (!sink) {
+ PLAYER_ERROR("unable to create player-appsink element");
+ return -1;
+ }
+
+ GstCaps *caps = (this->jpeg) ? gst_caps_new_simple("image/jpeg", NULL) :
gst_caps_new_simple("video/x-raw-rgb", NULL);
+ gst_app_sink_set_caps(GST_APP_SINK(sink), caps);
+ gst_caps_unref(caps);
+
+ gst_base_sink_set_sync(GST_BASE_SINK(sink), TRUE);
+
+ PLAYER_WARN1("GST_IS_PIPELINE(launchpipe) = %s", GST_IS_PIPELINE(launchpipe)
? "TRUE" : "FALSE");
+
+ if(GST_IS_PIPELINE(launchpipe)) {
+#ifdef GST_PLUGIN_DEFINE_STATIC
+ // Old API
+ GstPad *outpad = gst_bin_find_unconnected_pad(GST_BIN(launchpipe),
GST_PAD_SRC);
+#else
+ // New API
+ GstPad *outpad = gst_bin_find_unlinked_pad(GST_BIN(launchpipe),
GST_PAD_SRC);
+#endif
+ g_assert(outpad);
+ GstElement *outelement = gst_pad_get_parent_element(outpad);
+ g_assert(outelement);
+ gst_object_unref(outpad);
+
+ pipeline = launchpipe;
+
+ if(!gst_bin_add(GST_BIN(pipeline), sink)) {
+ PLAYER_ERROR("gst_bin_add() failed"); // TODO: do some unref
+ gst_object_unref(outelement);
+ gst_object_unref(pipeline);
+ return -1;
+ }
+
+ if(!gst_element_link(outelement, sink)) {
+ PLAYER_ERROR1("GStreamer: cannot link outelement(\"%s\") -> sink",
gst_element_get_name(outelement));
+ gst_object_unref(outelement);
+ gst_object_unref(pipeline);
+ return -1;
+ }
+
+ gst_object_unref(outelement);
+ } else {
+ pipeline = gst_pipeline_new(NULL);
+ g_assert(pipeline);
+
+ gst_object_unparent(GST_OBJECT(launchpipe));
+
+ gst_bin_add_many(GST_BIN(pipeline), launchpipe, sink, NULL);
+
+ if(!gst_element_link(launchpipe, sink)) {
+ PLAYER_ERROR("GStreamer: cannot link launchpipe -> sink");
+ gst_object_unref(pipeline);
+ return -1;
+ }
+ }
+
+ if(gst_element_set_state(GST_ELEMENT(pipeline), GST_STATE_PAUSED) ==
GST_STATE_CHANGE_FAILURE) {
+ PLAYER_ERROR("GStreamer: unable to set pipeline to paused");
+ gst_object_unref(pipeline);
+ return -1;
+ }
+
+ this->image_size = 0;
+ this->buffer = NULL;
+
+ this->HandleMessage();
+
+ return(0);
+}
+
+
+////////////////////////////////////////////////////////////////////////////////
+// Shutdown the device
+void GStreamerDriver::MainQuit()
+{
+ // Here you would shut the device down by, for example, closing a
+ // serial port.
+ GstState state, pending;
+
+ this->HandleMessage();
+
+ PLAYER_WARN("Setting pipeline to PAUSED ...");
+ gst_element_set_state (pipeline, GST_STATE_PAUSED);
+ gst_element_get_state (pipeline, &state, &pending, GST_CLOCK_TIME_NONE);
+ this->HandleMessage();
+
+ PLAYER_WARN("Setting pipeline to READY ...");
+ gst_element_set_state (pipeline, GST_STATE_READY);
+ gst_element_get_state (pipeline, &state, &pending, GST_CLOCK_TIME_NONE);
+ this->HandleMessage();
+
+ PLAYER_WARN("Setting pipeline to NULL ...");
+ gst_element_set_state (pipeline, GST_STATE_NULL);
+ gst_element_get_state (pipeline, &state, &pending, GST_CLOCK_TIME_NONE);
+ this->HandleMessage();
+
+ if (this->pipeline) {
+ gst_object_unref (GST_OBJECT (this->pipeline));
+ pipeline = NULL;
+ }
+
+ if (this->buffer) {
+ gst_buffer_unref(this->buffer);
+ this->buffer = NULL;
+ }
+
+ this->image_size = 0;
+
+ if (this->data.image) free(this->data.image);
+ this->data.image = NULL;
+}
+
+int GStreamerDriver::GrabFrame()
+{
+ if (!this->pipeline) {
+ return 0;
+ }
+
+ if (gst_app_sink_is_eos(GST_APP_SINK(this->sink))) {
+ PLAYER_WARN("gst_app_sink_is_eos\n");
+ return 0;
+ }
+
+ if (this->buffer) {
+ gst_buffer_unref(this->buffer);
+ this->buffer = NULL;
+ }
+
+ this->HandleMessage();
+
+ if(!gst_app_sink_get_queue_length(GST_APP_SINK(this->sink))) {
+
+ if(gst_element_set_state(GST_ELEMENT(this->pipeline), GST_STATE_PLAYING)
== GST_STATE_CHANGE_FAILURE) {
+ this->HandleMessage();
+ return 0;
+ }
+
+ this->buffer = gst_app_sink_pull_buffer(GST_APP_SINK(this->sink));
+
+ if(gst_element_set_state(GST_ELEMENT(this->pipeline), GST_STATE_PAUSED) ==
GST_STATE_CHANGE_FAILURE) {
+ this->HandleMessage();
+ return 0;
+ }
+ } else {
+ this->buffer = gst_app_sink_peek_buffer(GST_APP_SINK(this->sink));
+ }
+
+ if (!this->buffer) {
+ return 0;
+ }
+
+ return 1;
+}
+
+int GStreamerDriver::RetrieveFrame()
+{
+ size_t desired_size;
+ gint bpp;
+
+ if(!this->buffer)
+ return 0;
+
+ //printf("getting buffercaps\n");
+
+ GstCaps* caps = gst_buffer_get_caps(this->buffer);
+
+ assert(gst_caps_get_size(caps) == 1);
+
+ GstStructure* structure = gst_caps_get_structure(caps, 0);
+
+ if (this->jpeg)
+ {
+ bpp = 24;
+ } else
+ {
+ gint depth, endianness, redmask, greenmask, bluemask;
+ if(!gst_structure_get_int(structure, "bpp", &bpp) ||
+ !gst_structure_get_int(structure, "depth", &depth) ||
+ !gst_structure_get_int(structure, "endianness", &endianness) ||
+ !gst_structure_get_int(structure, "red_mask", &redmask) ||
+ !gst_structure_get_int(structure, "green_mask", &greenmask) ||
+ !gst_structure_get_int(structure, "blue_mask", &bluemask))
+ {
+ PLAYER_ERROR1("missing essential information in buffer caps, %s",
gst_caps_to_string(caps));
+ return 0;
+ }
+ if (!redmask || !greenmask || !bluemask || !depth) return 0;
+ //printf("buffer has %d bpp, depth %d, endianness %d, rgb %x %x %x, %s\n",
bpp, depth, endianness, redmask, greenmask, bluemask, gst_caps_to_string(caps));
+ }
+
+ gint height, width;
+
+ if(!gst_structure_get_int(structure, "width", &width) ||
+ !gst_structure_get_int(structure, "height", &height))
+ return 0;
+
+ desired_size = width * height * (bpp / 8);
+ if (!(desired_size > 0)) return 0;
+ if ((this->image_size) != desired_size)
+ {
+ PLAYER_WARN3("New size: width: %d, height: %d, bpp: %d", width, height,
bpp);
+ if (this->data.image) free(this->data.image);
+ this->data.image = NULL;
+ // PLAYER_WARN3("creating frame %dx%dx%d\n", width, height, bpp);
+
+ this->data.width = width;
+ this->data.height = height;
+
+ // TODO: fix this
+ this->data.bpp = bpp;
+ this->data.fdiv = 0;
+ this->data.format = PLAYER_CAMERA_FORMAT_RGB888;
+ this->data.compression = (this->jpeg) ? PLAYER_CAMERA_COMPRESS_JPEG :
PLAYER_CAMERA_COMPRESS_RAW;
+ this->image_size = desired_size;
+ this->data.image_count = this->image_size;
+
+ this->data.image = reinterpret_cast<uint8_t
*>(malloc(this->data.image_count));
+ if (!(this->data.image))
+ {
+ PLAYER_ERROR("Out of memory");
+ this->image_size = 0;
+ return 0;
+ }
+ }
+
+ gst_caps_unref(caps);
+
+ unsigned char *buffer_data = GST_BUFFER_DATA(this->buffer);
+
+ if (!(this->data.image))
+ {
+ PLAYER_ERROR("NULL image pointer");
+ return 0;
+ }
+ if (this->jpeg)
+ {
+ assert((this->buffer->size) <= (this->image_size));
+ memcpy(this->data.image, buffer_data, this->buffer->size);
+ this->data.image_count = this->buffer->size;
+ } else
+ {
+ memcpy(this->data.image, buffer_data, this->image_size);
+ }
+
+ return !0;
+
+ /*
+ printf("generating shifts\n");
+
+ IplImage *frame = cap->frame;
+ unsigned nbyte = bpp >> 3;
+ unsigned redshift, blueshift, greenshift;
+ unsigned mask = redmask;
+ for(redshift = 0, mask = redmask; (mask & 1) == 0; mask >>= 1, redshift++);
+ for(greenshift = 0, mask = greenmask; (mask & 1) == 0; mask >>= 1,
greenshift++);
+ for(blueshift = 0, mask = bluemask; (mask & 1) == 0; mask >>= 1,
blueshift++);
+
+ printf("shifts: %u %u %u\n", redshift, greenshift, blueshift);
+
+ for(int r = 0; r < frame->height; r++) {
+ for(int c = 0; c < frame->width; c++, data += nbyte) {
+ int at = r * frame->widthStep + c * 3;
+ frame->imageData[at] = ((*((gint *)data)) & redmask) >> redshift;
+ frame->imageData[at+1] = ((*((gint *)data)) & greenmask) >> greenshift;
+ frame->imageData[at+2] = ((*((gint *)data)) & bluemask) >> blueshift;
+ }
+ }
+
+ printf("converted buffer\n");
+
+ gst_buffer_unref(cap->buffer);
+ cap->buffer = 0;
+
+ return cap->frame;
+ */
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Main function for device thread
+void GStreamerDriver::Main()
+{
+ struct timespec tspec;
+
+ while (true)
+ {
+ tspec.tv_sec = 0;
+ tspec.tv_nsec = 1000000;
+ nanosleep(&tspec, NULL);
+ pthread_testcancel();
+ if (this->GrabFrame() && this->RetrieveFrame())
this->Publish(this->device_addr, PLAYER_MSGTYPE_DATA, PLAYER_CAMERA_DATA_STATE,
&this->data);
+ pthread_testcancel();
+ }
+}
Added: code/player/trunk/server/drivers/camera/gstreamer/gstappsink.c
===================================================================
--- code/player/trunk/server/drivers/camera/gstreamer/gstappsink.c
(rev 0)
+++ code/player/trunk/server/drivers/camera/gstreamer/gstappsink.c
2010-05-03 16:53:53 UTC (rev 8634)
@@ -0,0 +1,768 @@
+/* GStreamer
+ * Copyright (C) 2007 David Schleef <[email protected]>
+ *
+ * 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.
+ */
+
+#include <gst/gst.h>
+#include <gst/base/gstbasesink.h>
+#include <gst/gstbuffer.h>
+
+#include <stddef.h>
+#include <string.h>
+
+#include "gstappsink.h"
+
+GST_DEBUG_CATEGORY (app_sink_debug);
+#define GST_CAT_DEFAULT app_sink_debug
+
+static const GstElementDetails app_sink_details =
+GST_ELEMENT_DETAILS ("AppSink",
+ "Generic/Sink",
+ "Allow the application to get access to raw buffer",
+ "David Schleef <[email protected]>, Wim Taymans <[email protected]");
+
+enum
+{
+ /* signals */
+ SIGNAL_EOS,
+ SIGNAL_NEW_PREROLL,
+ SIGNAL_NEW_BUFFER,
+
+ /* acions */
+ SIGNAL_PULL_PREROLL,
+ SIGNAL_PULL_BUFFER,
+
+ LAST_SIGNAL
+};
+
+enum
+{
+ PROP_0,
+ PROP_CAPS,
+ PROP_EOS
+};
+
+static GstStaticPadTemplate gst_app_sink_template =
+GST_STATIC_PAD_TEMPLATE ("sink",
+ GST_PAD_SINK,
+ GST_PAD_ALWAYS,
+ GST_STATIC_CAPS_ANY);
+
+static void gst_app_sink_dispose (GObject * object);
+static void gst_app_sink_finalize (GObject * object);
+
+static void gst_app_sink_set_property (GObject * object, guint prop_id,
+ const GValue * value, GParamSpec * pspec);
+static void gst_app_sink_get_property (GObject * object, guint prop_id,
+ GValue * value, GParamSpec * pspec);
+
+static gboolean gst_app_sink_start (GstBaseSink * psink);
+static gboolean gst_app_sink_stop (GstBaseSink * psink);
+static gboolean gst_app_sink_event (GstBaseSink * sink, GstEvent * event);
+static GstFlowReturn gst_app_sink_preroll (GstBaseSink * psink,
+ GstBuffer * buffer);
+static GstFlowReturn gst_app_sink_render (GstBaseSink * psink,
+ GstBuffer * buffer);
+static GstCaps *gst_app_sink_getcaps (GstBaseSink * psink);
+
+static guint gst_app_sink_signals[LAST_SIGNAL] = { 0 };
+
+GST_BOILERPLATE (GstAppSink, gst_app_sink, GstBaseSink, GST_TYPE_BASE_SINK);
+
+//static
+gboolean
+appsink_plugin_init (GstPlugin * plugin)
+{
+ GST_DEBUG_CATEGORY_INIT (app_sink_debug, "player-appsink", 0, "Application
sink");
+
+ if (!g_thread_supported()) g_thread_init(NULL);
+
+ if (!gst_element_register (plugin, "player-appsink", GST_RANK_PRIMARY,
+ gst_app_sink_get_type ()))
+ return FALSE;
+
+ return TRUE;
+}
+
+#ifdef GST_PLUGIN_DEFINE_STATIC
+#define PACKAGE "player"
+GST_PLUGIN_DEFINE_STATIC (GST_VERSION_MAJOR, GST_VERSION_MINOR,
+ "player-appsink", "Element application sink",
+ appsink_plugin_init, "0.1", "LGPL", "player",
"http://playerstage.sourceforge.net")
+#endif
+
+void
+gst_app_marshal_OBJECT__VOID (GClosure * closure,
+ GValue * return_value,
+ guint n_param_values,
+ const GValue * param_values,
+ gpointer invocation_hint, gpointer marshal_data)
+{
+ typedef GstBuffer *(*GMarshalFunc_OBJECT__VOID) (gpointer data1,
+ gpointer data2);
+ register GMarshalFunc_OBJECT__VOID callback;
+ register GCClosure *cc = (GCClosure *) closure;
+ register gpointer data1, data2;
+ GstBuffer *v_return;
+
+ //printf("appmarshalobject\n");
+
+ g_return_if_fail (return_value != NULL);
+ g_return_if_fail (n_param_values == 1);
+
+ if (G_CCLOSURE_SWAP_DATA (closure)) {
+ data1 = closure->data;
+ data2 = g_value_peek_pointer (param_values + 0);
+ } else {
+ data1 = g_value_peek_pointer (param_values + 0);
+ data2 = closure->data;
+ }
+ callback =
+ (GMarshalFunc_OBJECT__VOID) (marshal_data ? marshal_data : cc->callback);
+
+ v_return = callback (data1, data2);
+
+ gst_value_take_buffer (return_value, v_return);
+}
+
+static void
+gst_app_sink_base_init (gpointer g_class)
+{
+ GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
+
+ //printf("appsinkbaseinit\n");
+
+ GST_DEBUG_CATEGORY_INIT (app_sink_debug, "appsink", 0, "appsink element");
+
+ gst_element_class_set_details (element_class, &app_sink_details);
+
+ gst_element_class_add_pad_template (element_class,
+ gst_static_pad_template_get (&gst_app_sink_template));
+}
+
+static void
+gst_app_sink_class_init (GstAppSinkClass * klass)
+{
+ GObjectClass *gobject_class = (GObjectClass *) klass;
+ GstBaseSinkClass *basesink_class = (GstBaseSinkClass *) klass;
+
+ //printf("appsinkclassinit\n");
+
+ gobject_class->dispose = gst_app_sink_dispose;
+ gobject_class->finalize = gst_app_sink_finalize;
+
+ gobject_class->set_property = gst_app_sink_set_property;
+ gobject_class->get_property = gst_app_sink_get_property;
+
+ g_object_class_install_property (gobject_class, PROP_CAPS,
+ g_param_spec_boxed ("caps", "Caps",
+ "The caps of the sink pad", GST_TYPE_CAPS, G_PARAM_READWRITE));
+
+ g_object_class_install_property (gobject_class, PROP_EOS,
+ g_param_spec_boolean ("eos", "EOS",
+ "Check if the sink is EOS", TRUE, G_PARAM_READABLE));
+
+ /**
+ * GstAppSink::eos:
+ * @appsink: the appsink element that emitted the signal
+ *
+ * Signal that the end-of-stream has been reached.
+ */
+ gst_app_sink_signals[SIGNAL_EOS] =
+ g_signal_new ("eos", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (GstAppSinkClass, eos),
+ NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0, G_TYPE_NONE);
+ /**
+ * GstAppSink::new-preroll:
+ * @appsink: the appsink element that emitted the signal
+ * @buffer: the buffer that caused the preroll
+ *
+ * Signal that a new preroll buffer is available.
+ */
+ gst_app_sink_signals[SIGNAL_NEW_PREROLL] =
+ g_signal_new ("new-preroll", G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (GstAppSinkClass, new_preroll),
+ NULL, NULL, g_cclosure_marshal_VOID__OBJECT, G_TYPE_NONE, 1,
+ GST_TYPE_BUFFER);
+ /**
+ * GstAppSink::new-buffer:
+ * @appsink: the appsink element that emitted the signal
+ * @buffer: the buffer that is available
+ *
+ * Signal that a new buffer is available.
+ */
+ gst_app_sink_signals[SIGNAL_NEW_BUFFER] =
+ g_signal_new ("new-buffer", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (GstAppSinkClass, new_buffer),
+ NULL, NULL, g_cclosure_marshal_VOID__UINT, G_TYPE_NONE, 1,
+ G_TYPE_UINT);
+
+ /**
+ * GstAppSink::pull-preroll:
+ * @appsink: the appsink element to emit this signal on
+ *
+ * Get the last preroll buffer on @appsink.
+ */
+ gst_app_sink_signals[SIGNAL_PULL_PREROLL] =
+ g_signal_new ("pull-preroll", G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstAppSinkClass, pull_preroll), NULL,
+ NULL, gst_app_marshal_OBJECT__VOID, GST_TYPE_BUFFER, 0, G_TYPE_NONE);
+ /**
+ * GstAppSink::pull-buffer:
+ * @appsink: the appsink element to emit this signal on
+ *
+ * Get the next buffer buffer on @appsink.
+ */
+ gst_app_sink_signals[SIGNAL_PULL_PREROLL] =
+ g_signal_new ("pull-buffer", G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (GstAppSinkClass, pull_buffer),
+ NULL, NULL, gst_app_marshal_OBJECT__VOID, GST_TYPE_BUFFER, 0,
+ G_TYPE_NONE);
+
+ basesink_class->start = gst_app_sink_start;
+ basesink_class->stop = gst_app_sink_stop;
+ basesink_class->event = gst_app_sink_event;
+ basesink_class->preroll = gst_app_sink_preroll;
+ basesink_class->render = gst_app_sink_render;
+ basesink_class->get_caps = gst_app_sink_getcaps;
+
+ klass->pull_preroll = gst_app_sink_pull_preroll;
+ klass->pull_buffer = gst_app_sink_pull_buffer;
+}
+
+static void
+gst_app_sink_init (GstAppSink * appsink, GstAppSinkClass * klass)
+{
+ appsink->mutex = g_mutex_new ();
+ appsink->cond = g_cond_new ();
+ appsink->queue = g_queue_new ();
+}
+
+static void
+gst_app_sink_dispose (GObject * obj)
+{
+ GstAppSink *appsink = GST_APP_SINK (obj);
+ GstBuffer *buffer;
+
+ //printf("appsinkdispose\n");
+
+ if (appsink->caps) {
+ gst_caps_unref (appsink->caps);
+ appsink->caps = NULL;
+ }
+ if (appsink->preroll) {
+ gst_buffer_unref (appsink->preroll);
+ appsink->preroll = NULL;
+ }
+ g_mutex_lock (appsink->mutex);
+ while ((buffer = g_queue_pop_head (appsink->queue)))
+ gst_buffer_unref (buffer);
+ g_mutex_unlock (appsink->mutex);
+
+ G_OBJECT_CLASS (parent_class)->dispose (obj);
+}
+
+static void
+gst_app_sink_finalize (GObject * obj)
+{
+ GstAppSink *appsink = GST_APP_SINK (obj);
+
+ g_mutex_free (appsink->mutex);
+ g_cond_free (appsink->cond);
+ g_queue_free (appsink->queue);
+
+ G_OBJECT_CLASS (parent_class)->finalize (obj);
+}
+
+static void
+gst_app_sink_set_property (GObject * object, guint prop_id,
+ const GValue * value, GParamSpec * pspec)
+{
+ GstAppSink *appsink = GST_APP_SINK (object);
+
+ //printf("appsinksetproperty\n");
+
+ switch (prop_id) {
+ case PROP_CAPS:
+ gst_app_sink_set_caps (appsink, gst_value_get_caps (value));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+gst_app_sink_get_property (GObject * object, guint prop_id, GValue * value,
+ GParamSpec * pspec)
+{
+ GstAppSink *appsink = GST_APP_SINK (object);
+
+ //printf("appsinkgetproperty\n");
+
+ switch (prop_id) {
+ case PROP_CAPS:
+ {
+ GstCaps *caps;
+
+ caps = gst_app_sink_get_caps (appsink);
+ gst_value_set_caps (value, caps);
+ if (caps)
+ gst_caps_unref (caps);
+ break;
+ }
+ case PROP_EOS:
+ g_value_set_boolean (value, gst_app_sink_is_eos (appsink));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+gst_app_sink_flush_unlocked (GstAppSink * appsink)
+{
+ GstBuffer *buffer;
+
+ //printf("appsinkflushunlocked\n");
+
+ GST_DEBUG_OBJECT (appsink, "flushing appsink");
+ appsink->is_eos = FALSE;
+ gst_buffer_replace (&appsink->preroll, NULL);
+ while ((buffer = g_queue_pop_head (appsink->queue)))
+ gst_buffer_unref (buffer);
+ g_cond_signal (appsink->cond);
+}
+
+static gboolean
+gst_app_sink_start (GstBaseSink * psink)
+{
+ GstAppSink *appsink = GST_APP_SINK (psink);
+
+ //printf("appsinkstart\n");
+
+ g_mutex_lock (appsink->mutex);
+ appsink->is_eos = FALSE;
+ appsink->started = TRUE;
+ GST_DEBUG_OBJECT (appsink, "starting");
+ g_mutex_unlock (appsink->mutex);
+
+ return TRUE;
+}
+
+static gboolean
+gst_app_sink_stop (GstBaseSink * psink)
+{
+ GstAppSink *appsink = GST_APP_SINK (psink);
+
+ //printf("appsinkstop\n");
+
+ g_mutex_lock (appsink->mutex);
+ GST_DEBUG_OBJECT (appsink, "stopping");
+ appsink->started = FALSE;
+ gst_app_sink_flush_unlocked (appsink);
+ g_mutex_unlock (appsink->mutex);
+
+ return TRUE;
+}
+
+static gboolean
+gst_app_sink_event (GstBaseSink * sink, GstEvent * event)
+{
+ GstAppSink *appsink = GST_APP_SINK (sink);
+
+ //printf("appsinkevent\n");
+
+ switch (event->type) {
+ case GST_EVENT_EOS:
+ g_mutex_lock (appsink->mutex);
+ GST_DEBUG_OBJECT (appsink, "receiving EOS");
+ appsink->is_eos = TRUE;
+ g_cond_signal (appsink->cond);
+ g_mutex_unlock (appsink->mutex);
+ break;
+ case GST_EVENT_FLUSH_START:
+ break;
+ case GST_EVENT_FLUSH_STOP:
+ g_mutex_lock (appsink->mutex);
+ GST_DEBUG_OBJECT (appsink, "received FLUSH_STOP");
+ gst_app_sink_flush_unlocked (appsink);
+ g_mutex_unlock (appsink->mutex);
+ break;
+ default:
+ break;
+ }
+ return TRUE;
+}
+
+static GstFlowReturn
+gst_app_sink_preroll (GstBaseSink * psink, GstBuffer * buffer)
+{
+ GstAppSink *appsink = GST_APP_SINK (psink);
+
+ //printf("appsinkpreroll\n");
+
+ g_mutex_lock (appsink->mutex);
+ GST_DEBUG_OBJECT (appsink, "setting preroll buffer %p", buffer);
+ gst_buffer_replace (&appsink->preroll, buffer);
+ g_cond_signal (appsink->cond);
+ g_mutex_unlock (appsink->mutex);
+
+ g_signal_emit(psink, gst_app_sink_signals[SIGNAL_NEW_PREROLL], 0, buffer);
+
+ return GST_FLOW_OK;
+}
+
+static GstFlowReturn
+gst_app_sink_render (GstBaseSink * psink, GstBuffer * buffer)
+{
+ GstAppSink *appsink = GST_APP_SINK (psink);
+
+ g_mutex_lock (appsink->mutex);
+ GST_DEBUG_OBJECT (appsink, "pushing render buffer %p on queue", buffer);
+ g_queue_push_tail (appsink->queue, gst_buffer_ref (buffer));
+ g_cond_signal (appsink->cond);
+// //printf("appsinkrender, have %d buffers\n",
g_queue_get_length(appsink->queue));
+ g_mutex_unlock (appsink->mutex);
+ g_signal_emit(psink, gst_app_sink_signals[SIGNAL_NEW_BUFFER], 0,
+ g_queue_get_length(appsink->queue));
+
+ return GST_FLOW_OK;
+}
+
+static GstCaps *
+gst_app_sink_getcaps (GstBaseSink * psink)
+{
+ GstCaps *caps;
+
+ //printf("appsinkgetcaps\n");
+
+ GstAppSink *appsink = GST_APP_SINK (psink);
+
+ GST_OBJECT_LOCK (appsink);
+ if ((caps = appsink->caps))
+ gst_caps_ref (caps);
+ GST_DEBUG_OBJECT (appsink, "got caps %" GST_PTR_FORMAT, caps);
+ GST_OBJECT_UNLOCK (appsink);
+
+ return caps;
+}
+
+/* external API */
+
+/**
+ * gst_app_sink_set_caps:
+ * @appsink: a #GstAppSink
+ * @caps: caps to set
+ *
+ * Set the capabilities on the appsink element. This function takes
+ * a copy of the caps structure. After calling this method, the sink will only
+ * accept caps that match @caps. If @caps is non-fixed, you must check the caps
+ * on the buffers to get the actual used caps.
+ */
+void
+gst_app_sink_set_caps (GstAppSink * appsink, const GstCaps * caps)
+{
+ GstCaps *old;
+
+ g_return_if_fail (appsink != NULL);
+ g_return_if_fail (GST_IS_APP_SINK (appsink));
+
+ GST_OBJECT_LOCK (appsink);
+ GST_DEBUG_OBJECT (appsink, "setting caps to %" GST_PTR_FORMAT, caps);
+ old = appsink->caps;
+ if (caps)
+ appsink->caps = gst_caps_copy (caps);
+ else
+ appsink->caps = NULL;
+ if (old)
+ gst_caps_unref (old);
+ GST_OBJECT_UNLOCK (appsink);
+}
+
+/**
+ * gst_app_sink_get_caps:
+ * @appsink: a #GstAppSink
+ *
+ * Get the configured caps on @appsink.
+ *
+ * Returns: the #GstCaps accepted by the sink. gst_caps_unref() after usage.
+ */
+GstCaps *
+gst_app_sink_get_caps (GstAppSink * appsink)
+{
+ GstCaps *caps;
+
+ g_return_val_if_fail (appsink != NULL, NULL);
+ g_return_val_if_fail (GST_IS_APP_SINK (appsink), NULL);
+
+ GST_OBJECT_LOCK (appsink);
+ if ((caps = appsink->caps))
+ gst_caps_ref (caps);
+ GST_DEBUG_OBJECT (appsink, "getting caps of %" GST_PTR_FORMAT, caps);
+ GST_OBJECT_UNLOCK (appsink);
+
+ return caps;
+}
+
+/**
+ * gst_app_sink_is_eos:
+ * @appsink: a #GstAppSink
+ *
+ * Check if @appsink is EOS, which is when no more buffers can be pulled
because
+ * an EOS event was received.
+ *
+ * This function also returns %TRUE when the appsink is not in the PAUSED or
+ * PLAYING state.
+ *
+ * Returns: %TRUE if no more buffers can be pulled and the appsink is EOS.
+ */
+gboolean
+gst_app_sink_is_eos (GstAppSink * appsink)
+{
+ gboolean ret;
+
+ g_return_val_if_fail (appsink != NULL, FALSE);
+ g_return_val_if_fail (GST_IS_APP_SINK (appsink), FALSE);
+
+ g_mutex_lock (appsink->mutex);
+ if (!appsink->started)
+ goto not_started;
+
+ if (appsink->is_eos && g_queue_is_empty (appsink->queue)) {
+ GST_DEBUG_OBJECT (appsink, "we are EOS and the queue is empty");
+ ret = TRUE;
+ } else {
+ GST_DEBUG_OBJECT (appsink, "we are not yet EOS");
+ ret = FALSE;
+ }
+ g_mutex_unlock (appsink->mutex);
+
+ return ret;
+
+not_started:
+ {
+ GST_DEBUG_OBJECT (appsink, "we are stopped, return TRUE");
+ g_mutex_unlock (appsink->mutex);
+ return TRUE;
+ }
+}
+
+/**
+ * gst_app_sink_pull_preroll:
+ * @appsink: a #GstAppSink
+ *
+ * Get the last preroll buffer in @appsink. This was the buffer that caused the
+ * appsink to preroll in the PAUSED state. This buffer can be pulled many times
+ * and remains available to the application even after EOS.
+ *
+ * This function is typically used when dealing with a pipeline in the PAUSED
+ * state. Calling this function after doing a seek will give the buffer right
+ * after the seek position.
+ *
+ * Note that the preroll buffer will also be returned as the first buffer
+ * when calling gst_app_sink_pull_buffer().
+ *
+ * If an EOS event was received before any buffers, this function returns
+ * %NULL. Use gst_app_sink_is_eos () to check for the EOS condition.
+ *
+ * This function blocks until a preroll buffer or EOS is received or the
appsink
+ * element is set to the READY/NULL state.
+ *
+ * Returns: a #GstBuffer or NULL when the appsink is stopped or EOS.
+ */
+GstBuffer *
+gst_app_sink_pull_preroll (GstAppSink * appsink)
+{
+ GstBuffer *buf = NULL;
+
+ //printf("pull_preroll\n");
+
+ g_return_val_if_fail (appsink != NULL, NULL);
+ g_return_val_if_fail (GST_IS_APP_SINK (appsink), NULL);
+
+ g_mutex_lock (appsink->mutex);
+
+ while (TRUE) {
+ GST_DEBUG_OBJECT (appsink, "trying to grab a buffer");
+ if (!appsink->started)
+ goto not_started;
+
+ if (appsink->preroll != NULL)
+ break;
+
+ if (appsink->is_eos)
+ goto eos;
+
+ /* nothing to return, wait */
+ GST_DEBUG_OBJECT (appsink, "waiting for the preroll buffer");
+ g_cond_wait (appsink->cond, appsink->mutex);
+ }
+ buf = gst_buffer_ref (appsink->preroll);
+ GST_DEBUG_OBJECT (appsink, "we have the preroll buffer %p", buf);
+ g_mutex_unlock (appsink->mutex);
+
+ return buf;
+
+ /* special conditions */
+eos:
+ {
+ GST_DEBUG_OBJECT (appsink, "we are EOS, return NULL");
+ g_mutex_unlock (appsink->mutex);
+ return NULL;
+ }
+not_started:
+ {
+ GST_DEBUG_OBJECT (appsink, "we are stopped, return NULL");
+ g_mutex_unlock (appsink->mutex);
+ return NULL;
+ }
+}
+
+/**
+ * gst_app_sink_pull_buffer:
+ * @appsink: a #GstAppSink
+ *
+ * This function blocks until a buffer or EOS becomes available or the appsink
+ * element is set to the READY/NULL state.
+ *
+ * This function will only return buffers when the appsink is in the PLAYING
+ * state. All rendered buffers will be put in a queue so that the application
+ * can pull buffers at its own rate. Note that when the application does not
+ * pull buffers fast enough, the queued buffers could consume a lot of memory,
+ * especially when dealing with raw video frames.
+ *
+ * If an EOS event was received before any buffers, this function returns
+ * %NULL. Use gst_app_sink_is_eos () to check for the EOS condition.
+ *
+ * Returns: a #GstBuffer or NULL when the appsink is stopped or EOS.
+ */
+GstBuffer *
+gst_app_sink_pull_buffer (GstAppSink * appsink)
+{
+ GstBuffer *buf = NULL;
+
+ //printf("pull_buffer\n");
+
+ g_return_val_if_fail (appsink != NULL, NULL);
+ g_return_val_if_fail (GST_IS_APP_SINK (appsink), NULL);
+
+ g_mutex_lock (appsink->mutex);
+
+ while (TRUE) {
+ GST_DEBUG_OBJECT (appsink, "trying to grab a buffer");
+ if (!appsink->started)
+ goto not_started;
+
+ if (!g_queue_is_empty (appsink->queue))
+ break;
+
+ if (appsink->is_eos)
+ goto eos;
+
+ /* nothing to return, wait */
+ GST_DEBUG_OBJECT (appsink, "waiting for a buffer");
+ g_cond_wait (appsink->cond, appsink->mutex);
+ }
+ buf = g_queue_pop_head (appsink->queue);
+ GST_DEBUG_OBJECT (appsink, "we have a buffer %p", buf);
+ g_mutex_unlock (appsink->mutex);
+
+ return buf;
+
+ /* special conditions */
+eos:
+ {
+ GST_DEBUG_OBJECT (appsink, "we are EOS, return NULL");
+ g_mutex_unlock (appsink->mutex);
+ return NULL;
+ }
+not_started:
+ {
+ GST_DEBUG_OBJECT (appsink, "we are stopped, return NULL");
+ g_mutex_unlock (appsink->mutex);
+ return NULL;
+ }
+}
+
+/**
+ * gst_app_sink_peek_buffer:
+ * @appsink: a #GstAppSink
+ *
+ * This function returns a buffer if there is one queued but does not block.
+ *
+ * This function will only return buffers when the appsink is in the PLAYING
+ * state. All rendered buffers will be put in a queue so that the application
+ * can pull buffers at its own rate. Note that when the application does not
+ * pull buffers fast enough, the queued buffers could consume a lot of memory,
+ * especially when dealing with raw video frames.
+ *
+ * If an EOS event was received before any buffers, this function returns
+ * %NULL. Use gst_app_sink_is_eos () to check for the EOS condition.
+ *
+ * Returns: a #GstBuffer or NULL when the appsink is stopped or EOS.
+ */
+GstBuffer *
+gst_app_sink_peek_buffer (GstAppSink * appsink)
+{
+ GstBuffer *buf = NULL;
+
+ //printf("pull_buffer\n");
+
+ g_return_val_if_fail (appsink != NULL, NULL);
+ g_return_val_if_fail (GST_IS_APP_SINK (appsink), NULL);
+
+ g_mutex_lock (appsink->mutex);
+
+ GST_DEBUG_OBJECT (appsink, "trying to grab a buffer");
+ if (!appsink->started)
+ goto not_started;
+
+ if (g_queue_is_empty (appsink->queue))
+ return NULL;
+
+ if (appsink->is_eos)
+ goto eos;
+
+ /* nothing to return, wait */
+ buf = g_queue_pop_head (appsink->queue);
+ GST_DEBUG_OBJECT (appsink, "we have a buffer %p", buf);
+ g_mutex_unlock (appsink->mutex);
+
+ return buf;
+
+ /* special conditions */
+eos:
+ {
+ GST_DEBUG_OBJECT (appsink, "we are EOS, return NULL");
+ g_mutex_unlock (appsink->mutex);
+ return NULL;
+ }
+not_started:
+ {
+ GST_DEBUG_OBJECT (appsink, "we are stopped, return NULL");
+ g_mutex_unlock (appsink->mutex);
+ return NULL;
+ }
+}
+
+guint
+gst_app_sink_get_queue_length (GstAppSink * appsink)
+{
+ return g_queue_get_length (appsink->queue);
+}
Added: code/player/trunk/server/drivers/camera/gstreamer/gstappsink.h
===================================================================
--- code/player/trunk/server/drivers/camera/gstreamer/gstappsink.h
(rev 0)
+++ code/player/trunk/server/drivers/camera/gstreamer/gstappsink.h
2010-05-03 16:53:53 UTC (rev 8634)
@@ -0,0 +1,99 @@
+/* GStreamer
+ * Copyright (C) 2007 David Schleef <[email protected]>
+ *
+ * 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_APP_SINK_H_
+#define _GST_APP_SINK_H_
+
+#include <gst/gst.h>
+#include <gst/base/gstbasesink.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+G_BEGIN_DECLS
+
+#define GST_TYPE_APP_SINK \
+ (gst_app_sink_get_type())
+#define GST_APP_SINK(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_APP_SINK,GstAppSink))
+#define GST_APP_SINK_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_APP_SINK,GstAppSinkClass))
+#define GST_IS_APP_SINK(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_APP_SINK))
+#define GST_IS_APP_SINK_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_APP_SINK))
+
+typedef struct _GstAppSink GstAppSink;
+typedef struct _GstAppSinkClass GstAppSinkClass;
+
+struct _GstAppSink
+{
+ GstBaseSink basesink;
+
+ /*< private >*/
+ GstCaps *caps;
+
+ GCond *cond;
+ GMutex *mutex;
+ GQueue *queue;
+ GstBuffer *preroll;
+ gboolean started;
+ gboolean is_eos;
+};
+
+struct _GstAppSinkClass
+{
+ GstBaseSinkClass basesink_class;
+
+ /* signals */
+ gboolean (*eos) (GstAppSink *sink);
+ gboolean (*new_preroll) (GstAppSink *sink);
+ gboolean (*new_buffer) (GstAppSink *sink);
+
+ /* actions */
+ GstBuffer * (*pull_preroll) (GstAppSink *sink);
+ GstBuffer * (*pull_buffer) (GstAppSink *sink);
+};
+
+GType gst_app_sink_get_type(void);
+
+GST_DEBUG_CATEGORY_EXTERN (app_sink_debug);
+
+void gst_app_sink_set_caps (GstAppSink *appsink, const
GstCaps *caps);
+GstCaps * gst_app_sink_get_caps (GstAppSink *appsink);
+
+gboolean gst_app_sink_is_eos (GstAppSink *appsink);
+
+GstBuffer * gst_app_sink_pull_preroll (GstAppSink *appsink);
+GstBuffer * gst_app_sink_pull_buffer (GstAppSink *appsink);
+GstBuffer * gst_app_sink_peek_buffer (GstAppSink *appsink);
+
+guint gst_app_sink_get_queue_length (GstAppSink *appsink);
+
+gboolean appsink_plugin_init (GstPlugin * plugin);
+
+G_END_DECLS
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
+
This was sent by the SourceForge.net collaborative development platform, the
world's largest Open Source development site.
------------------------------------------------------------------------------
_______________________________________________
Playerstage-commit mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/playerstage-commit