After an evaluation, GNOME has moved from Bugzilla to GitLab. Learn more about GitLab.
No new issues can be reported in GNOME Bugzilla anymore.
To report an issue in a GNOME project, go to GNOME GitLab.
Do not go to GNOME Gitlab for: Bluefish, Doxygen, GnuCash, GStreamer, java-gnome, LDTP, NetworkManager, Tomboy.
Bug 357026 - How can be a plugin based on pipeline made?
How can be a plugin based on pipeline made?
Status: RESOLVED NOTABUG
Product: GStreamer
Classification: Platform
Component: gst-plugins
0.10.4
Other All
: Normal normal
: git master
Assigned To: GStreamer Maintainers
GStreamer Maintainers
Depends on:
Blocks:
 
 
Reported: 2006-09-21 07:18 UTC by junghak kim
Modified: 2006-09-22 02:53 UTC
See Also:
GNOME target: ---
GNOME version: 2.5/2.6



Description junghak kim 2006-09-21 07:18:42 UTC
Please describe the problem:
I'm trying to make a plugin to record A/V that is similar to playbin based on pipeline.

At first, I made a simple plugin but it's not working while the plugin has registerd in the gstreamer registry. (It's can be checked by gst-inspect)

Please show my source code and reply why it's not working.

###################### Header File ##########################################
#ifndef __GST_CAMCORDER_BIN_H__
#define __GST_CAMCORDER_BIN_H__

#include <gst/gst.h>

G_BEGIN_DECLS

/* #defines don't like whitespacey bits */
#define GST_TYPE_CAMCORDER_BIN \
  (gst_camcorder_bin_get_type())
#define GST_CAMCORDER_BIN(obj) \
  (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_CAMCORDER_BIN,GstCamcorderBin))
#define GST_CAMCORDER_BIN_CLASS(klass) \
  (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_CAMCORDER_BIN,GstCamcorderBinClass))
#define GST_IS_CAMCORDER_BIN(obj) \
  (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_CAMCORDER_BIN))
#define GST_IS_CAMCORDER_BIN_CLASS(klass) \
  (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_CAMCORDER_BIN))

typedef struct _GstCamcorderBin      GstCamcorderBin;
typedef struct _GstCamcorderBinClass GstCamcorderBinClass;

struct _GstCamcorderBin
{
  GstPipeline    pipeline;

  GstElement *v_enc_bin;
  GstElement *video_src, *video_sink;
  
  /* default from make_element */
  GstPad *sinkpad, *srcpad;
};

struct _GstCamcorderBinClass 
{
  GstPipelineClass parent_class;
};

GType gst_camcorder_bin_get_type (void);

G_END_DECLS

#endif /* __GST_CAMCORDER_BIN_H__ */

#############################################################################
###################### Source File ##########################################


#ifdef HAVE_CONFIG_H
#  include <config.h>
#endif

#include <gst/gst.h>

#include "gstcamcorderbin.h"

GST_DEBUG_CATEGORY_STATIC (gst_camcorder_bin_debug);
#define GST_CAT_DEFAULT gst_camcorder_bin_debug

#define VOLUME_MAX_DOUBLE 4.0
#ifndef GST_HAVE_GLIB_2_8

#define _gst_gvalue_set_gstobject(gvalue,obj)  \
      if (obj != NULL) {                       \
        gst_object_ref (obj);                  \
        g_value_set_object (gvalue, obj);      \
        g_object_unref (obj);                  \
      } else {                                 \
        g_value_set_object (gvalue, NULL);     \
      }
#else
#define _gst_gvalue_set_gstobject(gvalue,obj)  \
      g_value_set_object (gvalue, obj);
#endif

/* Filter signals and args */
enum
{
  /* FILL ME */
  LAST_SIGNAL
};

enum
{
  ARG_0,
  ARG_VIDEO_SRC,
};

static GstStaticPadTemplate sink_factory = GST_STATIC_PAD_TEMPLATE ("sink",
    GST_PAD_SINK,
    GST_PAD_ALWAYS,
    GST_STATIC_CAPS ("ANY")
    );

static GstStaticPadTemplate src_factory = GST_STATIC_PAD_TEMPLATE ("src",
    GST_PAD_SRC,
    GST_PAD_ALWAYS,
    GST_STATIC_CAPS ("ANY")
    );

GST_BOILERPLATE (GstCamcorderBin, gst_camcorder_bin, GstElement,
    GST_TYPE_PIPELINE);

static void gst_camcorder_bin_dispose (GObject * object);

static void gst_camcorder_bin_set_property (GObject * object, guint prop_id,
    const GValue * value, GParamSpec * pspec);
static void gst_camcorder_bin_get_property (GObject * object, guint prop_id,
    GValue * value, GParamSpec * pspec);
    
static GstStateChangeReturn gst_camcorder_bin_change_state (GstElement * element,
    GstStateChange transition);

static void
gst_camcorder_bin_base_init (gpointer gclass)
{
  static GstElementDetails element_details = {
    "camcorder Bin",
    "Generic/Bin/camcorder",
    "A/V Recording from devices and streams",
    "Junghak Kim <kjh5@samsung.com>"
  };
  GstElementClass *element_class = GST_ELEMENT_CLASS (gclass);

  gst_element_class_add_pad_template (element_class,
      gst_static_pad_template_get (&src_factory));
  gst_element_class_add_pad_template (element_class,
      gst_static_pad_template_get (&sink_factory));
  gst_element_class_set_details (element_class, &element_details);
}

/* initialize the plugin's class */
static void
gst_camcorder_bin_class_init(GstCamcorderBinClass* klass)
{
  GObjectClass *gobject_class;
  GstElementClass *gstelement_class;

  gobject_class = (GObjectClass *) klass;
  gstelement_class = (GstElementClass *) klass;
  
  gobject_class->set_property = gst_camcorder_bin_set_property;
  gobject_class->get_property = gst_camcorder_bin_get_property;
  
  g_object_class_install_property (gobject_class, ARG_VIDEO_SRC,
    g_param_spec_object ("video-src", "Video Source",
    "the video input element to use (NULL = default sink)",
    GST_TYPE_ELEMENT, G_PARAM_READWRITE));
    
  gobject_class->dispose = GST_DEBUG_FUNCPTR (gst_camcorder_bin_dispose);
    
  gstelement_class->change_state =
      GST_DEBUG_FUNCPTR (gst_camcorder_bin_change_state);
}

static void
gst_camcorder_bin_init (GstCamcorderBin* camcorder_bin,
    GstCamcorderBinClass* gclass)
{
  camcorder_bin->v_enc_bin = NULL;

  camcorder_bin->video_src = NULL;
  camcorder_bin->video_sink = NULL;
}

static void
gst_camcorder_bin_clean_elements(GstCamcorderBin* camcorder_bin)
{
  if (camcorder_bin->v_enc_bin)
    gst_object_unref(camcorder_bin->v_enc_bin);
  if (camcorder_bin->video_src)
    gst_object_unref(camcorder_bin->video_src);
  if (camcorder_bin->video_sink)
    gst_object_unref(camcorder_bin->video_sink);
}

static void
gst_camcorder_bin_dispose (GObject * object)
{
  GstCamcorderBin *camcorder_bin;

  camcorder_bin = GST_CAMCORDER_BIN(object);
  gst_camcorder_bin_clean_elements(camcorder_bin);

  G_OBJECT_CLASS (parent_class)->dispose (object);
}

static void
gst_camcorder_bin_set_property (GObject * object, guint prop_id,
    const GValue * value, GParamSpec * pspec)
{
  GstCamcorderBin *camcorder_bin = NULL;
  
  g_return_if_fail (GST_IS_CAMCORDER_BIN (object));
  
  camcorder_bin = GST_CAMCORDER_BIN (object);

  switch (prop_id) {
    case ARG_VIDEO_SRC:
      if (camcorder_bin->video_src != NULL)
      {
        gst_object_unref (camcorder_bin->video_src);
      }
      camcorder_bin->video_src = g_value_get_object (value);
      break;

    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
      break;
  }
}

static void
gst_camcorder_bin_get_property (GObject * object, guint prop_id,
    GValue * value, GParamSpec * pspec)
{
  GstCamcorderBin *camcorder_bin = NULL;
  
  g_return_if_fail (GST_IS_CAMCORDER_BIN (object));
  
  camcorder_bin = GST_CAMCORDER_BIN (object);

  switch (prop_id) {
    case ARG_VIDEO_SRC:
      _gst_gvalue_set_gstobject (value, camcorder_bin->video_src);
      break;
      
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
      break;
  }
}

static GstElement*
gen_video_elements(GstCamcorderBin* camcorder_bin)
{
  GstPad *pad;
  GstCaps* caps;

  if (camcorder_bin->v_enc_bin != NULL)
    return camcorder_bin->v_enc_bin;

  camcorder_bin->v_enc_bin = gst_bin_new ("v_enc_bin");
  
  if (camcorder_bin->video_src == NULL)
  {
    camcorder_bin->video_src = gst_element_factory_make ("videotestsrc", "videosrc");
    if (camcorder_bin->video_src == NULL)
    {
      g_printf("%s(%d): Both v4lsrc and videotestsrc elements are missing.\n", __FILE__, __LINE__);
      goto video_err_handler;
    }
    g_object_set(G_OBJECT(camcorder_bin->video_src), "num-buffers", 250, NULL);
  }
  gst_element_set_state (camcorder_bin->video_src, GST_STATE_READY);

  camcorder_bin->video_sink = gst_element_factory_make("sdlvideosink", "video_sink");
  if (camcorder_bin->video_sink == NULL);
  {
    g_printf("%s(%d): Failed to make a sdlvideosink element.\n", __FILE__, __LINE__);
    goto video_err_handler;
  }
  gst_element_set_state (camcorder_bin->video_sink, GST_STATE_READY);

  gst_bin_add_many(GST_BIN(camcorder_bin->v_enc_bin),
    camcorder_bin->video_src,
    camcorder_bin->video_sink,
    NULL);
  
  gst_element_link(camcorder_bin->video_src, camcorder_bin->video_sink);
  
  gst_element_set_state (camcorder_bin->v_enc_bin, GST_STATE_READY);

  return camcorder_bin->v_enc_bin;

video_err_handler:
  gst_camcorder_bin_clean_elements(camcorder_bin);
  return NULL;
}

/* chain function
 * this function does the actual processing
 */
static GstFlowReturn
gst_camcorder_bin_chain (GstPad * pad, GstBuffer * buf)
{
  GstCamcorderBin *camcorder_bin;

  camcorder_bin = GST_CAMCORDER_BIN (GST_OBJECT_PARENT (pad));

  /* just push out the incoming buffer without touching it */
  return gst_pad_push (camcorder_bin->srcpad, buf);
}

static gboolean
gst_camcorder_bin_link(GstCamcorderBin* camcorder_bin)
{
  if (gen_video_elements(camcorder_bin) == NULL)
  {
    g_printf("%s(%d): Failed to create a v_enc_bin.\n", __FILE__, __LINE__);
    return FALSE;
  }
  
  return TRUE;
}

static GstStateChangeReturn
gst_camcorder_bin_change_state (GstElement * element, GstStateChange transition)
{
  GstStateChangeReturn ret;
  GstCamcorderBin *camcorder_bin;

  camcorder_bin = GST_CAMCORDER_BIN (element);

  switch (transition)
  {
  case GST_STATE_CHANGE_NULL_TO_READY:
  	g_printf("@%s(%d): The state is in GST_STATE_CHANGE_NULL_TO_READY.\n", __FILE__, __LINE__);
    if (!gst_camcorder_bin_link(camcorder_bin))
      return GST_STATE_CHANGE_FAILURE;
    break;

  case GST_STATE_CHANGE_READY_TO_NULL:
    g_printf("@%s(%d): The state is in GST_STATE_CHANGE_READY_TO_NULL.\n", __FILE__, __LINE__);
    gst_camcorder_bin_clean_elements(camcorder_bin);
    break;
      
  case GST_STATE_CHANGE_READY_TO_PAUSED:
    g_printf("@%s(%d): The state is in GST_STATE_CHANGE_READY_TO_PAUSED.\n", __FILE__, __LINE__);
    break;
    
  case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
    g_printf("@%s(%d): The state is in GST_STATE_CHANGE_PLAYING_TO_PAUSED.\n", __FILE__, __LINE__);
    gst_element_set_state (camcorder_bin->v_enc_bin, GST_STATE_PAUSED);
    break;
  
  case GST_STATE_CHANGE_PAUSED_TO_READY:
    g_printf("@%s(%d): The state is in GST_STATE_CHANGE_PAUSED_TO_READY.\n", __FILE__, __LINE__);
    if (camcorder_bin->v_enc_bin != NULL)
    {
      gst_camcorder_bin_clean_elements(camcorder_bin);
    }
    break;
      
  default:
    break;
  }

  ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
  if (ret == GST_STATE_CHANGE_FAILURE)
    return ret;

  switch (transition) {
    case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
      break;

    case GST_STATE_CHANGE_PAUSED_TO_READY:
      break;

    default:
      break;
  }

  return ret;
}

static gboolean
plugin_init (GstPlugin * plugin)
{
  /* exchange the strings 'plugin' and 'Template plugin' with your
   * plugin name and description */
  GST_DEBUG_CATEGORY_INIT (gst_camcorder_bin_debug, "camcorderbin",
      0, "camcorder bin");

  return gst_element_register (plugin, "camcorderbin",
      GST_RANK_NONE, GST_TYPE_CAMCORDER_BIN);
}

GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
    GST_VERSION_MINOR,
    "camcorderbin",
    "camcorder bin",
    plugin_init, VERSION, "LGPL", "GStreamer", "http://gstreamer.net/")


Steps to reproduce:
1. make; make install
2. gst-launch camcorderbin
3. You can show the messages below.
Setting pipeline to PAUSED ...
@gstcamcorderbin.c(271): The state is in GST_STATE_CHANGE_NULL_TO_READY.
gstcamcorderbin.c(213): Failed to make a sdlvideosink element.
 
(gst-launch-0.10:14717): GStreamer-CRITICAL **:
Trying to dispose element videosrc, but it is not in the NULL state.
You need to explicitly set elements to the NULL state before
dropping the final reference, to allow them to clean up.
 
gstcamcorderbin.c(253): Failed to create a v_enc_bin.
ERROR: Pipeline doesn't want to pause.
Setting pipeline to NULL ...
FREEING pipeline ...


Actual results:


Expected results:


Does this happen every time?


Other information:
Comment 1 Tim-Philipp Müller 2006-09-21 12:12:11 UTC
Nothing here indicates a bug in GStreamer as far as I can see.

 - do you have the sdl plugin installed? Does
      $ gst-inspect-0.10 sdlvideosink
   work?

 - you need to do gst_element_set_state (element, GST_STATE_NULL)
   before you give up the final reference to an element. This can
   be done directly or indirectly by setting the parent bin/pipeline
   to NULL state, but you need to take care of doing this before
   doing the final unref.

 - also, keep in mind that there is a concept of 'floating' references
   in GStreamer. If you create an element, you own a reference, but if
   you gst_bin_add() it, the bin takes ownership of your reference, so
   if you want to keep your own reference around, you must ref it
   explicitly (didn't check the code in detail, just commenting, since
   the reference counting in the code doesn't look right at first glance)



If you need help writing GStreamer applications or plugins, please subscribe to the gstreamer-devel mailing list and submit your code there (if possible, as stand-alone ready-to-compile application that runs on its own). There will also be more people to help you on that list.


Please either close this report or provide more details where you think the bug in GStreamer is.