#ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #include #include #include #include #include #include "utility.h" #include "pinesrcbin.h" #define GST_TYPE_PINESRCBIN (gst_pinesrcbin_get_type()) #define GST_PINESRCBIN(obj) \ (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_PINESRCBIN, GstPineSrcBin)) #define GST_PINE64SRCBIN_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST ((klass), GST_TYPE_PINESRCBIN, GstPineSrcBinClass)) #define GST_IS_PINESRCBIN(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_PINESRCBIN)) #define GST_IS_PINESRCBIN_CLASS(klass) \ (G_TYPE_CHECK_CLASS_TYPE ((klass), GST_TYPE_PINESRCBIN)) #define GST_PINESRCBIN_GET_CLASS(obj) \ (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_PINESRCBIN, GstPineSrcBinClass)) #define gst_pinesrcbin_parent_class parent_class #define DEFAULT_SYNC TRUE enum { PROP_0, PROP_CAPS, PROP_SYNC, PROP_NUM_BUFFERS, }; typedef struct _GstPineSrcBinClass { GstBinClass parent_class; /*< private > */ /* virtual methods for subclasses */ void (*configure) (GstPineSrcBin * self, GstElement * kid); GstElement *(*create_fake_element) (GstPineSrcBin * bin); } GstPineSrcBinClass; G_DEFINE_TYPE (GstPineSrcBin, gst_pinesrcbin, GST_TYPE_BIN); static void gst_pinesrcbin_clear_kid (GstPineSrcBin * self) { if (self->kid) { gst_element_set_state (self->kid, GST_STATE_NULL); gst_element_set_state (self->filter, GST_STATE_NULL); gst_bin_remove_many (GST_BIN (self), self->kid, self->filter, NULL); self->kid = NULL; self->filter = NULL; } } static GstElement * gst_pinesrcbin_create_fake_element_default (GstPineSrcBin * self) { GstElement *fake; gchar dummy_factory[10], dummy_name[20]; sprintf (dummy_factory, "fake%s", self->type_klass_lc); sprintf (dummy_name, "fake-%s-%s", self->media_klass_lc, self->type_klass_lc); fake = gst_element_factory_make (dummy_factory, dummy_name); g_object_set (fake, "sync", self->sync, NULL); return fake; } static GstElement * gst_pinesrcbin_create_fake_element (GstPineSrcBin * self) { GstPineSrcBinClass *klass = GST_PINESRCBIN_GET_CLASS (self); GstElement *fake; if (klass->create_fake_element) fake = klass->create_fake_element (self); else fake = gst_pinesrcbin_create_fake_element_default (self); return fake; } static gboolean gst_pinesrcbin_attach_ghost_pad (GstPineSrcBin * self) { GstPad *target = gst_element_get_static_pad (self->filter, self->type_klass_lc); gboolean res = gst_ghost_pad_set_target (GST_GHOST_PAD (self->pad), target); gst_object_unref (target); return res; } static void gst_pinesrcbin_reset (GstPineSrcBin * self) { gst_pinesrcbin_clear_kid (self); /* placeholder element */ // self->kid = gst_pinesrcbin_create_fake_element (self); self->kid = gst_pinesrcbin_create_fake_element (self); self->filter = gst_element_factory_make ("capsfilter", NULL); g_object_set (self->filter, "sync", self->sync, NULL); g_object_set (self->filter, "caps", self->filter_caps, NULL); gst_bin_add_many (GST_BIN (self), self->kid, self->filter, NULL); gst_element_link(self->kid, self->filter); gst_pinesrcbin_attach_ghost_pad (self); } static GstStaticCaps raw_video_caps = GST_STATIC_CAPS ("video/x-raw"); static void gst_pinesrcbin_constructed (GObject * object) { int width, height; const char *format; struct v4l2_fract interval; GstPineSrcBin *self = GST_PINESRCBIN (object); if (G_OBJECT_CLASS (parent_class)->constructed) G_OBJECT_CLASS (parent_class)->constructed (object); self->type_klass = (self->flag == GST_ELEMENT_FLAG_SINK) ? "Sink" : "Source"; self->filter_caps = gst_static_caps_get (&raw_video_caps); gst_pinesrcbin_get_gst_caps_data("rear", &width, &height, &format, &interval); self->filter_caps = gst_caps_new_simple ("video/x-raw", "format", G_TYPE_STRING, format, /* we "flip" interval to get frame rate */ "framerate", GST_TYPE_FRACTION, interval.denominator, interval.numerator, "width", G_TYPE_INT, width, "height", G_TYPE_INT, height, NULL); self->type_klass_lc = (self->flag == GST_ELEMENT_FLAG_SINK) ? "sink" : "src"; self->media_klass_lc = "video"; self->pad = gst_ghost_pad_new_no_target (self->type_klass_lc, (self->flag == GST_ELEMENT_FLAG_SINK) ? GST_PAD_SINK : GST_PAD_SRC); gst_element_add_pad (GST_ELEMENT (self), self->pad); gst_pinesrcbin_reset (self); GST_OBJECT_FLAG_SET (self, self->flag); gst_bin_set_suppressed_flags (GST_BIN (self), GST_ELEMENT_FLAG_SOURCE | GST_ELEMENT_FLAG_SINK); } static void gst_pinesrcbin_dispose (GObject * object) { GstPineSrcBin *self = GST_PINESRCBIN (object); gst_pinesrcbin_clear_kid (self); if (self->filter_caps) gst_caps_unref (self->filter_caps); self->filter_caps = NULL; G_OBJECT_CLASS (parent_class)->dispose ((GObject *) self); } static void gst_pinesrcbin_set_property (GObject * object, guint prop_id, const GValue * value, GParamSpec * pspec) { GstPineSrcBin *self = GST_PINESRCBIN (object); switch (prop_id) { case PROP_CAPS: if (self->filter_caps) gst_caps_unref (self->filter_caps); self->filter_caps = gst_caps_copy (gst_value_get_caps (value)); break; case PROP_SYNC: self->sync = g_value_get_boolean (value); if (self->kid && self->has_sync) g_object_set_property (G_OBJECT (self->kid), pspec->name, value); break; case PROP_NUM_BUFFERS: self->num_buffers = g_value_get_int (value); if (self->kid) g_object_set_property (G_OBJECT (self->kid), pspec->name, value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void gst_pinesrcbin_get_property (GObject * object, guint prop_id, GValue * value, GParamSpec * pspec) { GstPineSrcBin *self = GST_PINESRCBIN (object); switch (prop_id) { case PROP_CAPS: gst_value_set_caps (value, self->filter_caps); break; case PROP_SYNC: g_value_set_boolean (value, self->sync); break; case PROP_NUM_BUFFERS: g_value_set_int (value, self->num_buffers); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static gboolean gst_pinesrcbin_detect (GstPineSrcBin * self) { GstElement *kid, *filter; GstPad *target; GstPineSrcBinClass *klass = GST_PINESRCBIN_GET_CLASS (self); gst_pinesrcbin_clear_kid (self); GST_DEBUG_OBJECT (self, "Creating v4l2src element"); kid = gst_element_factory_make ("v4l2src", NULL); GST_DEBUG_OBJECT (self, "Creating capsfilter element"); filter = gst_element_factory_make ("capsfilter", "capsfilter1"); #if 0 /* find element */ GST_DEBUG_OBJECT (self, "Creating new kid"); if (!(kid = gst_pinesrcbin_find_best (self))) #endif if (!kid || !filter) goto no_sink; GST_DEBUG_OBJECT (self, "Setting fixed pad caps"); target = gst_element_get_static_pad (kid, self->type_klass_lc); gst_pad_use_fixed_caps (target); gst_pad_set_caps(target, self->filter_caps); GST_DEBUG_OBJECT (self, "Setting default properties for v4l2src element"); self->has_sync = g_object_class_find_property (G_OBJECT_GET_CLASS (kid), "sync") != NULL; if (self->has_sync) g_object_set (G_OBJECT (kid), "sync", self->sync, NULL); g_object_set (G_OBJECT (kid), "device", self->devnode_path, NULL); g_object_set (G_OBJECT (kid), "num-buffers", self->num_buffers, NULL); if (klass->configure) { klass->configure (self, kid); } self->kid = kid; self->filter = filter; GST_DEBUG_OBJECT (self, "Creating bin"); gst_bin_add_many (GST_BIN (self), kid, filter, NULL); if (!gst_element_link (kid, filter)) g_error("failed to link elements to filter\n"); GST_DEBUG_OBJECT (self, "Configuring elements states"); /* Ensure the child is brought up to the right state to match the parent. */ if (GST_STATE (self->kid) < GST_STATE (self)) gst_element_set_state (self->kid, GST_STATE (self)); if (GST_STATE (filter) < GST_STATE (self)) gst_element_set_state (filter, GST_STATE (self)); GST_DEBUG_OBJECT (self, "Setting filter caps"); g_object_set(filter, "caps", self->filter_caps, NULL); GST_DEBUG_OBJECT (self, "Setting up ghost pad"); /* attach ghost pad */ GST_DEBUG_OBJECT (self, "Re-assigning ghostpad"); if (!gst_pinesrcbin_attach_ghost_pad (self)) goto target_failed; GST_DEBUG_OBJECT (self, "done changing auto %s %s", self->media_klass_lc, self->type_klass_lc); return TRUE; /* ERRORS */ no_sink: { GST_ELEMENT_ERROR (self, LIBRARY, INIT, (NULL), ("Failed to find a supported sink")); return FALSE; } target_failed: { GST_ELEMENT_ERROR (self, LIBRARY, INIT, (NULL), ("Failed to set target pad")); return FALSE; } } static GstStateChangeReturn gst_pinesrcbin_change_state (GstElement * element, GstStateChange transition) { GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS; GstPineSrcBin *sink = GST_PINESRCBIN (element); switch (transition) { case GST_STATE_CHANGE_NULL_TO_READY: if (!gst_pinesrcbin_detect (sink)) return GST_STATE_CHANGE_FAILURE; 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_READY_TO_NULL: gst_pinesrcbin_reset (sink); break; default: break; } return ret; } static void gst_pinesrcbin_class_init (GstPineSrcBinClass * klass) { GObjectClass *gobject_class; GstElementClass *eklass; gobject_class = G_OBJECT_CLASS (klass); eklass = GST_ELEMENT_CLASS (klass); gobject_class->constructed = gst_pinesrcbin_constructed; gobject_class->dispose = gst_pinesrcbin_dispose; gobject_class->set_property = gst_pinesrcbin_set_property; gobject_class->get_property = gst_pinesrcbin_get_property; eklass->change_state = GST_DEBUG_FUNCPTR (gst_pinesrcbin_change_state); g_object_class_install_property (gobject_class, PROP_SYNC, g_param_spec_boolean ("sync", "Sync", "Sync on the clock", DEFAULT_SYNC, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); g_object_class_install_property (gobject_class, PROP_NUM_BUFFERS, g_param_spec_int ("num-buffers", "Num-buffers", "Number of buffers until EOS, -1 = unlimited", -1, INT_MAX, 1000, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); } GST_DEBUG_CATEGORY (pinesrcbin_debug); static void gst_pinesrcbin_init (GstPineSrcBin * self) { GST_DEBUG_OBJECT (self, "Starting device configuration"); gst_pinesrcbin_configure_device(self, "rear"); self->sync = DEFAULT_SYNC; self->num_buffers = 1000; } static gboolean plugin_init (GstPlugin * plugin) { GST_DEBUG_CATEGORY_INIT (pinesrcbin_debug, "pinesrcbin", 0, "pinephone v4lsrc camera wrapper"); return gst_element_register (plugin, "pinesrcbin", GST_RANK_NONE, GST_TYPE_PINESRCBIN); } GST_PLUGIN_DEFINE (GST_VERSION_MAJOR, GST_VERSION_MINOR, pinesrcbin, "pinephone v4lsrc camera wrapper", plugin_init, "0.0", GST_LICENSE, "pinesrcbin", "omp")