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 <d...@schleef.org> + * + * 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 <d...@schleef.org>, Wim Taymans <wim.taym...@gmail.com"); + +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 <d...@schleef.org> + * + * 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 Playerstage-commit@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/playerstage-commit