Files
pinesrcbin/pinesrcbin.c
2021-03-28 22:29:29 +03:00

412 lines
12 KiB
C

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <stdio.h>
#include <string.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <gst/gst.h>
#include <glib.h>
#include <linux/media.h>
#include <linux/videodev2.h>
#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,
PROP_TEST_PATTERN,
PROP_HUE,
};
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;
case PROP_TEST_PATTERN:
self->test_pattern = g_value_get_int (value);
break;
case PROP_HUE:
self->hue = g_value_get_int (value);
g_print("setting prop hue: %d\n", self->hue);
set_camera_control("rear", CONTROL_HUE, self->hue);
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;
case PROP_TEST_PATTERN:
g_value_set_int (value, self->test_pattern);
break;
case PROP_HUE:
g_print("getting prop hue: %d\n", self->hue);
g_value_set_int (value, self->hue);
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);
g_print ("gst_pinesrcbin_detect\n");
GST_DEBUG_OBJECT (self, "gst_pinesrcbin_detect: setting up element");
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;
g_print ("Class init\n");
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, -1,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
g_object_class_install_property (gobject_class, PROP_TEST_PATTERN,
g_param_spec_int ("test-pattern", "Test-pattern",
"Test pattern for camera to display", 0, INT_MAX, 0,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
g_object_class_install_property (gobject_class, PROP_HUE,
g_param_spec_int ("hue", "Hue",
"Set image hue", 0, 65535, 128,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
}
GST_DEBUG_CATEGORY (pinesrcbin_debug);
static void
gst_pinesrcbin_init (GstPineSrcBin * self)
{
g_print ("Plugin init\n");
self->sync = DEFAULT_SYNC;
self->num_buffers = -1;
self->test_pattern = 0;
self->hue = 128;
g_print("initial hue: %d\n", self->hue);
GST_DEBUG_OBJECT (self, "Starting device configuration");
gst_pinesrcbin_configure_device(self, "rear");
}
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")