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

Reply via email to