diff --git a/meson.build b/meson.build index 2aed124..43e65eb 100644 --- a/meson.build +++ b/meson.build @@ -37,7 +37,8 @@ configure_file(output : 'config.h', configuration : cdata) # Plugin 1 plugin_sources = [ - 'pinesrcbin.c' + 'pinesrcbin.c', + 'utility.c' ] gstpluginexample = library('gstpinesrcbin', diff --git a/pinesrcbin.c b/pinesrcbin.c index 3a3f916..aa74ff6 100644 --- a/pinesrcbin.c +++ b/pinesrcbin.c @@ -14,6 +14,9 @@ #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)) @@ -32,39 +35,9 @@ enum PROP_0, PROP_CAPS, PROP_SYNC, + PROP_NUM_BUFFERS, }; -struct device_data; -typedef struct _GstPineSrcBin -{ - GstBin parent; - - GList *media_list; - struct device_data *media_if; - /* configuration for subclasses */ - const gchar *media_klass; /* Audio/Video/... */ - GstElementFlags flag; /* GST_ELEMENT_FLAG_{SINK/SOURCE} */ - - /* explicit pointers to stuff used */ - GstPad *pad; - GstCaps *filter_caps; - gboolean sync; - - /* < private > */ - GstElement *kid; - gboolean has_sync; - const gchar *type_klass; /* Source/Sink */ - const gchar *media_klass_lc, *type_klass_lc; /* lower case versions */ - - const struct media_v2_entity *v4l2entity; - const struct media_v2_pad *v4l2pad; - const struct media_v2_interface *v4l2interface; - char devnode_path[260]; - int fd; - gboolean autofocus; - int gain_ctrl; - int gain_max; -} GstPineSrcBin; typedef struct _GstPineSrcBinClass { @@ -79,23 +52,16 @@ typedef struct _GstPineSrcBinClass G_DEFINE_TYPE (GstPineSrcBin, gst_pinesrcbin, GST_TYPE_BIN); -static int -xioctl (int fd, int request, void *arg) -{ - int r; - do { - r = ioctl (fd, request, arg); - } while (r == -1 && errno == EINTR); - return r; -} static void gst_pinesrcbin_clear_kid (GstPineSrcBin * self) { if (self->kid) { gst_element_set_state (self->kid, GST_STATE_NULL); - gst_bin_remove (GST_BIN (self), self->kid); + 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; } } @@ -130,10 +96,9 @@ gst_pinesrcbin_create_fake_element (GstPineSrcBin * self) static gboolean gst_pinesrcbin_attach_ghost_pad (GstPineSrcBin * self) { - GstPad *target = gst_element_get_static_pad (self->kid, self->type_klass_lc); + 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; } @@ -143,8 +108,13 @@ 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); - gst_bin_add (GST_BIN (self), self->kid); + 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); } @@ -153,11 +123,23 @@ 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, @@ -200,6 +182,11 @@ gst_pinesrcbin_set_property (GObject * object, guint prop_id, 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; @@ -219,6 +206,9 @@ gst_pinesrcbin_get_property (GObject * object, guint prop_id, 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; @@ -228,37 +218,59 @@ gst_pinesrcbin_get_property (GObject * object, guint prop_id, static gboolean gst_pinesrcbin_detect (GstPineSrcBin * self) { - GstElement *kid; + 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) + 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_bin_add (GST_BIN (self), kid); + 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)) @@ -330,319 +342,26 @@ gst_pinesrcbin_class_init (GstPineSrcBinClass * klass) 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); -struct device_data -{ - char path[261]; - int fd; - struct media_v2_topology topology; - struct media_device_info info; - - struct media_v2_entity *entities; - size_t num_entities; - struct media_v2_interface *interfaces; - size_t num_interfaces; - struct media_v2_pad *pads; - size_t num_pads; - struct media_v2_link *links; - size_t num_links; -}; -static const struct media_v2_link * -gst_pinesrcbin_get_link_from_entity (struct device_data *dev, uint32_t id) -{ - int i; - for (i = 0; i < dev->num_links; i++) - if (dev->links[i].sink_id == id) - return &dev->links[i]; - return NULL; -} - -static const struct media_v2_interface * -gst_pinesrcbin_get_interface_by_id (struct device_data *dev, uint32_t id) -{ - int i; - for (i = 0; i < dev->num_interfaces; i++) - if (dev->interfaces[i].id == id) - return &dev->interfaces[i]; - return NULL; -} - -static const struct media_v2_interface * -gst_pinesrcbin_get_entity_interface (struct device_data *dev, uint32_t id) -{ - const struct media_v2_link *link = - gst_pinesrcbin_get_link_from_entity (dev, id); - g_print ("entity id: %d\n", id); - if (!link) - return NULL; - g_print ("link id: %d %d\n", link->sink_id, link->source_id); - return gst_pinesrcbin_get_interface_by_id (dev, link->source_id); -} - -static const struct media_v2_pad * -gst_pinesrcbin_get_pad_from_entity (struct device_data *dev, uint32_t id) -{ - int i; - for (i = 0; i < dev->num_pads; i++) { - if (dev->pads[i].entity_id == id) - return &dev->pads[i]; - } - return NULL; -} - -static const struct media_v2_entity * -gst_pinesrcbin_find_media_entity (struct device_data *dev, - const char *driver_name) -{ - int length = strlen (driver_name); - int i; - for (i = 0; i < dev->num_entities; i++) - g_print ("%s: entity: %s:%s\n", dev->path, dev->entities[i].name, - driver_name); - for (i = 0; i < dev->num_entities; i++) { - if (!strncmp (dev->entities[i].name, driver_name, length)) - return &dev->entities[i]; - } - return NULL; -} - -static struct device_data * -gst_pinesrcbin_find_media_device (GstPineSrcBin * self, const char *driver_name) -{ - int length = strlen (driver_name); - GList *l = self->media_list; - while (l) { - struct device_data *dev = l->data; - const struct media_device_info *info = &dev->info; - g_print ("device %s:%s:%s\n", dev->path, info->driver, driver_name); - if (!strncmp (info->driver, driver_name, length)) - return dev; - l = l->next; - } - return NULL; -} - -static gboolean -get_device_path (struct media_v2_intf_devnode devnode, char *path, int length) -{ - char uevent_path[256]; - snprintf (uevent_path, 256, "/sys/dev/char/%d:%d/uevent", devnode.major, - devnode.minor); - - FILE *f = fopen (uevent_path, "r"); - if (!f) { - return FALSE; - } - - char line[512]; - while (fgets (line, 512, f)) { - if (strncmp (line, "DEVNAME=", 8) == 0) { - // Drop newline - int length = strlen (line); - if (line[length - 1] == '\n') - line[length - 1] = '\0'; - - snprintf (path, length, "/dev/%s", line + 8); - fclose (f); - return TRUE; - } - } - - fclose (f); - - return FALSE; -} - -static gboolean -query_autofocus (int fd) -{ - struct v4l2_query_ext_ctrl ctrl = { }; - ctrl.id = V4L2_CID_FOCUS_AUTO; - int rc = xioctl (fd, VIDIOC_QUERY_EXT_CTRL, &ctrl); - if (rc < 0) - return FALSE; - return TRUE; -} - -static void -setup_gain (int fd, int *gain_ctrl, int *gain_max) -{ - struct v4l2_query_ext_ctrl ctrl = { }; - ctrl.id = V4L2_CID_GAIN; - int rc = xioctl (fd, VIDIOC_QUERY_EXT_CTRL, &ctrl); - if (rc >= 0) { - *gain_ctrl = ctrl.id; - *gain_max = ctrl.maximum; - return; - } - ctrl.id = V4L2_CID_ANALOGUE_GAIN; - rc = xioctl (fd, VIDIOC_QUERY_EXT_CTRL, &ctrl); - if (rc >= 0) { - *gain_ctrl = ctrl.id; - *gain_max = ctrl.maximum; - return; - } -} - -static gboolean -camera_control (int fd, uint32_t id, int request, uint32_t * value) -{ - struct v4l2_ext_control ctrl = { }; - ctrl.id = id; - ctrl.value = *value; - - struct v4l2_ext_controls ctrls = { - .ctrl_class = 0, - .which = V4L2_CTRL_WHICH_CUR_VAL, - .count = 1, - .controls = &ctrl, - }; - if (xioctl (fd, request, &ctrls) == -1) { - return TRUE; - } - - *value = ctrl.value; - return FALSE; -} - -static inline const struct media_v2_pad * -get_device_pad (const struct device_data *dev, uint32_t id) -{ - int i; - for (i = 0; i < dev->num_pads; i++) { - if (dev->pads[i].id == id) { - return &dev->pads[i]; - } - } - return NULL; -} - -/* TODO - get media device pad and video device pad and set up link */ -static gboolean -setup_link (const struct device_data *dev, uint32_t source_pad_id, - uint32_t sink_pad_id, gboolean enabled) -{ - const struct media_v2_pad *source_pad = get_device_pad (dev, source_pad_id); - g_return_val_if_fail (source_pad, FALSE); - - const struct media_v2_pad *sink_pad = get_device_pad (dev, sink_pad_id); - g_return_val_if_fail (sink_pad, FALSE); - - struct media_link_desc link = { }; - link.flags = enabled ? MEDIA_LNK_FL_ENABLED : 0; - link.source.entity = source_pad->entity_id; - link.source.index = 0; - link.sink.entity = sink_pad->entity_id; - link.sink.index = 0; - if (xioctl (dev->fd, MEDIA_IOC_SETUP_LINK, &link) == -1) { - g_printerr ("MEDIA_IOC_SETUP_LINK: %d %s", errno, strerror (errno)); - return FALSE; - } - - return TRUE; -} - static void gst_pinesrcbin_init (GstPineSrcBin * self) { - self->media_list = NULL; - struct dirent *dir; - g_print ("starting device scan\n"); - DIR *d = opendir ("/dev"); - while ((dir = readdir (d)) != NULL) { - if (strncmp (dir->d_name, "media", 5) == 0) { - char path[261]; - int fd; - snprintf (path, 261, "/dev/%s", dir->d_name); - fd = open (path, O_RDWR); - g_print ("opened device: %s: fd=%d\n", path, fd); - if (fd >= 0) { - int rc = -1; - struct device_data *dev = g_new0 (struct device_data, 1); - if (!dev) - goto out; - dev->fd = fd; - strncpy (dev->path, path, sizeof (dev->path)); - g_print ("topology x1\n"); - rc = xioctl (dev->fd, MEDIA_IOC_G_TOPOLOGY, &dev->topology); - if (rc < 0) { - g_printerr ("topology x1 error %d %d %s\n", rc, - errno, strerror (errno)); - goto out; - } - dev->entities = - calloc (dev->topology.num_entities, - sizeof (struct media_v2_entity)); - dev->num_entities = dev->topology.num_entities; - dev->interfaces = - calloc (dev->topology.num_interfaces, - sizeof (struct media_v2_interface)); - dev->num_interfaces = dev->topology.num_interfaces; - dev->pads = - calloc (dev->topology.num_pads, sizeof (struct media_v2_pad)); - dev->num_pads = dev->topology.num_pads; - dev->links = - calloc (dev->topology.num_links, sizeof (struct media_v2_link)); - dev->num_links = dev->topology.num_links; - dev->topology.ptr_entities = (uint64_t) dev->entities; - dev->topology.ptr_interfaces = (uint64_t) dev->interfaces; - dev->topology.ptr_pads = (uint64_t) dev->pads; - dev->topology.ptr_links = (uint64_t) dev->links; - g_print ("topology x2\n"); - rc = xioctl (dev->fd, MEDIA_IOC_G_TOPOLOGY, &dev->topology); - if (rc < 0) - goto out; - - g_print ("device info\n"); - rc = xioctl (dev->fd, MEDIA_IOC_DEVICE_INFO, &dev->info); - if (rc < 0) - goto out; - self->media_list = g_list_append (self->media_list, dev); - g_print ("added device %s %s\n", dev->path, dev->info.driver); - out: - if (rc < 0 && dev) - g_free (dev); - } - } - } - self->media_if = gst_pinesrcbin_find_media_device (self, "sun6i-csi"); - if (!self->media_if) - g_printerr ("can't find device sun6i-csi\n"); - self->v4l2entity = - gst_pinesrcbin_find_media_entity (self->media_if, "sun6i-csi"); - if (!self->v4l2entity) - g_printerr ("can't find entity sun6i-csi\n"); - /* This is pad for host controller */ - self->v4l2pad = - gst_pinesrcbin_get_pad_from_entity (self->media_if, self->v4l2entity->id); - if (!self->v4l2pad) - g_printerr ("can't find interface pad sun6i-csi\n"); - self->v4l2interface = - gst_pinesrcbin_get_entity_interface (self->media_if, - self->v4l2entity->id); - if (!self->v4l2interface) - g_printerr ("can't find interface sun6i-csi\n"); - if (!get_device_path (self->v4l2interface->devnode, - self->devnode_path, sizeof (self->devnode_path))) - g_printerr ("Failed to get devnode path\n"); - g_print ("devnode: %s\n", self->devnode_path); - /* TODO: source pad? sink pad? */ -// setup_link(self->media_if, ,self->v4l2interface->pad_id, false); - self->fd = open (self->devnode_path, O_RDWR); - if (self->fd < 0) - g_printerr ("failed to open %s\n", self->devnode_path); - self->autofocus = FALSE; - if (query_autofocus (self->fd)) { - uint32_t enable = 1U; - g_print ("autofocus possible\n"); - self->autofocus = TRUE; - camera_control (self->fd, V4L2_CID_FOCUS_AUTO, VIDIOC_S_EXT_CTRLS, &enable); - } - setup_gain (self->fd, &self->gain_ctrl, &self->gain_max); + GST_DEBUG_OBJECT (self, "Starting device configuration"); + gst_pinesrcbin_configure_device(self, "rear"); self->sync = DEFAULT_SYNC; + self->num_buffers = 1000; } static gboolean diff --git a/pinesrcbin.h b/pinesrcbin.h new file mode 100644 index 0000000..f791ab3 --- /dev/null +++ b/pinesrcbin.h @@ -0,0 +1,36 @@ +#ifndef PINESRCBIN_H +#define PINESRCBIN_H +struct device_data; +typedef struct _GstPineSrcBin +{ + GstBin parent; + + GList *media_list; + struct device_data *media_if; + /* configuration for subclasses */ + const gchar *media_klass; /* Audio/Video/... */ + GstElementFlags flag; /* GST_ELEMENT_FLAG_{SINK/SOURCE} */ + + /* explicit pointers to stuff used */ + GstPad *pad; + GstCaps *filter_caps; + gboolean sync; + + /* < private > */ + GstElement *kid; + GstElement *filter; + gboolean has_sync; + const gchar *type_klass; /* Source/Sink */ + const gchar *media_klass_lc, *type_klass_lc; /* lower case versions */ + + const struct media_v2_entity *v4l2entity; + const struct media_v2_pad *v4l2pad; + const struct media_v2_interface *v4l2interface; + char devnode_path[260]; + int fd; + gboolean autofocus; + /* Buffers till EOS */ + int num_buffers; +} GstPineSrcBin; + +#endif diff --git a/utility.c b/utility.c new file mode 100644 index 0000000..973b772 --- /dev/null +++ b/utility.c @@ -0,0 +1,621 @@ +#include + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include "pinesrcbin.h" +#include "utility.h" + +static const struct media_v2_link * +gst_pinesrcbin_get_link_from_entity(struct device_data *dev, uint32_t id); +static const struct media_v2_interface * +gst_pinesrcbin_get_interface_by_id(struct device_data *dev, uint32_t id); +static const struct media_v2_interface * +gst_pinesrcbin_get_entity_interface(struct device_data *dev, uint32_t id); +static const struct media_v2_pad * +gst_pinesrcbin_get_pad_from_entity(struct device_data *dev, uint32_t id); +static const struct media_v2_entity * +gst_pinesrcbin_find_media_entity(struct device_data *dev, + const char *driver_name); +static struct device_data * +gst_pinesrcbin_find_media_device(GstPineSrcBin *self, const char *driver_name); +static gboolean +get_device_path(struct media_v2_intf_devnode devnode, char *path, int length); +static gboolean +query_autofocus(int fd); +static void +setup_gain(int fd, int *gain_ctrl, int *gain_max); +static gboolean +camera_control(int fd, uint32_t id, int request, uint32_t *value); +static inline const struct media_v2_pad * +get_device_pad(const struct device_data *dev, uint32_t id); +static gboolean +setup_link(const struct device_data *dev, uint32_t source_pad_id, + uint32_t sink_pad_id, gboolean enabled); + +static int alloc_device(struct device_data *dev); +static void disable_all_links(struct device_data *dev); +static void subdev_set_mode(int subdev_fd, int width, + int height, + uint32_t bus_code, + struct v4l2_fract interval); + +struct camera_config; +int get_camera_config_no(const char *conf_name); +static struct camera_config * +get_camera_config(const char *conf_name); + +struct camera_data; +static struct camera_data * +get_camera_data(const char *conf_name); + +static int +xioctl (int fd, int request, void *arg) +{ + int r; + do { + r = ioctl (fd, request, arg); + } while (r == -1 && errno == EINTR); + return r; +} + +struct camera_config { + char conf_name[128]; + char camera_name[128]; + int width; + int height; + uint32_t bus_code_video; + uint32_t bus_code_single; + struct v4l2_fract frame_interval; +}; + +struct camera_data { + struct camera_config *conf; + int subdev_fd; + const struct media_v2_entity *entity; + const struct media_v2_interface *interface; + int gain_ctrl; + int gain_max; + char devnode_path[261]; +}; +struct camera_config configurations[] = { + {"front", "gc2145", 320, 240, MEDIA_BUS_FMT_YUYV8_2X8, MEDIA_BUS_FMT_SBGGR8_1X8, {1, 15}}, + {"rear", "ov5640", 320, 240, MEDIA_BUS_FMT_YUYV8_2X8, MEDIA_BUS_FMT_SBGGR8_1X8, {1, 15}}, +}; +struct camera_data cam_store[] = { + {}, + {} +}; + +struct mediabus_to_gst { + uint32_t bus_code; + const char *gst_format; +}; +static struct mediabus_to_gst gst_format[] = { + {MEDIA_BUS_FMT_SBGGR8_1X8, "BGGR"}, + {MEDIA_BUS_FMT_SGBRG8_1X8, "GBRG"}, + {MEDIA_BUS_FMT_SGRBG8_1X8, "GRBG"}, + {MEDIA_BUS_FMT_SRGGB8_1X8, "RGGB"}, + {MEDIA_BUS_FMT_SBGGR10_1X10, "BGGR10"}, + {MEDIA_BUS_FMT_SGBRG10_1X10, "GBRG10"}, + {MEDIA_BUS_FMT_SGRBG10_1X10, "GRBG10"}, + {MEDIA_BUS_FMT_SRGGB10_1X10, "RGGB10"}, + {MEDIA_BUS_FMT_UYVY8_2X8, "UYVY"}, + {MEDIA_BUS_FMT_YUYV8_2X8, "YUY2"}, +}; +void gst_pinesrcbin_get_gst_caps_data(const char * conf_name, + int *width, int * height, + const char **format, + struct v4l2_fract *interval) +{ + int i; + const struct camera_config *conf = get_camera_config(conf_name); + *width = conf->width; + *height = conf->height; + *format = NULL; + *interval = conf->frame_interval; + for (i = 0; i < sizeof(gst_format)/sizeof(gst_format[0]); i++) { + if (conf->bus_code_video == gst_format[i].bus_code) { + *format = gst_format[i].gst_format; + return; + } + } + g_assert(*format); +} + +static const struct media_v2_link * +gst_pinesrcbin_get_link_from_entity(struct device_data *dev, uint32_t id) +{ + int i; + for (i = 0; i < dev->num_links; i++) + if (dev->links[i].sink_id == id) + return &dev->links[i]; + return NULL; +} + +static const struct media_v2_interface * +gst_pinesrcbin_get_interface_by_id(struct device_data *dev, uint32_t id) +{ + int i; + for (i = 0; i < dev->num_interfaces; i++) + if (dev->interfaces[i].id == id) + return &dev->interfaces[i]; + return NULL; +} + +static const struct media_v2_interface * +gst_pinesrcbin_get_entity_interface(struct device_data *dev, uint32_t id) +{ + const struct media_v2_link *link = + gst_pinesrcbin_get_link_from_entity(dev, id); + g_print("entity id: %d\n", id); + if (!link) + return NULL; + g_print("link id: %d %d\n", link->sink_id, link->source_id); + return gst_pinesrcbin_get_interface_by_id(dev, link->source_id); +} + +static const struct media_v2_pad * +gst_pinesrcbin_get_pad_from_entity(struct device_data *dev, uint32_t id) +{ + int i; + for (i = 0; i < dev->num_pads; i++) + { + if (dev->pads[i].entity_id == id) + return &dev->pads[i]; + } + return NULL; +} + +static const struct media_v2_entity * +gst_pinesrcbin_find_media_entity(struct device_data *dev, + const char *driver_name) +{ + int length = strlen(driver_name); + int i; + for (i = 0; i < dev->num_entities; i++) + g_print("%s: entity: %s:%d:%s\n", dev->path, dev->entities[i].name, + dev->entities[i].id, driver_name); + for (i = 0; i < dev->num_entities; i++) { + if (!strncmp(dev->entities[i].name, driver_name, length)) + return &dev->entities[i]; + } + return NULL; +} + +static void disable_all_links(struct device_data *dev) +{ + int i, j; + for (i = 0; i < dev->num_entities; i++) { + const struct media_v2_pad *sink_pad = + gst_pinesrcbin_get_pad_from_entity (dev, dev->entities[i].id); + if (!sink_pad) + continue; + if (!(sink_pad->flags & MEDIA_PAD_FL_SINK)) + continue; + g_print("sink pad: %d\n", sink_pad->id); + for (j = 0; j < dev->num_entities; j++) { + if (i == j) + continue; + const struct media_v2_pad *source_pad = + gst_pinesrcbin_get_pad_from_entity (dev, dev->entities[j].id); + if (!source_pad) + continue; + g_print("source pad: %d\n", source_pad->id); + if (!(source_pad->flags & MEDIA_PAD_FL_SOURCE)) + continue; + g_print("disabling link %d %d\n", source_pad->entity_id, sink_pad->entity_id); + setup_link(dev, source_pad->id, sink_pad->id, FALSE); + } + } +#if 0 + const struct media_v2_link *plink = + gst_pinesrcbin_get_link_from_entity(dev, + dev->entities[i].id); + if (plink) { + g_print("link %d %08x %08x: %08x\n", plink->id, plink->source_id, plink->sink_id, plink->flags); + if (plink->flags & MEDIA_LNK_FL_ENABLED) { + uint32_t link_type = plink->flags & MEDIA_LNK_FL_LINK_TYPE; + if (link_type == MEDIA_LNK_FL_DATA_LINK) { + g_print("data link %d\n", plink->id); + setup_link(dev, plink->source_id, plink->sink_id, FALSE); + } else if (link_type == MEDIA_LNK_FL_INTERFACE_LINK) { + struct media_v2_interface *iface = st_pinesrcbin_get_interface_by_id(dev, plink->source_id); + g_print("interface link %d entity: %d\n", plink->id, iface->); + continue; + } + const struct media_v2_pad *source_pad, *sink_pad; + g_print("link %d is active\n", plink->id); + source_pad = gst_pinesrcbin_get_pad_from_entity(dev, plink->source_id); + sink_pad = gst_pinesrcbin_get_pad_from_entity(dev, plink->sink_id); + g_assert(source_pad); + g_assert(sink_pad); + setup_link(dev, source_pad->id, sink_pad->id, FALSE); +#if 0 + struct media_link_desc link = {}; + link.flags = 0; + link.source.entity = plink->source_id; + link.source.index = 0; + link.sink.entity = plink->sink_id; + link.sink.index = 0; + if (xioctl(dev->fd, MEDIA_IOC_SETUP_LINK, &link) == -1) + g_printerr("MEDIA_IOC_SETUP_LINK: %d %s", errno, strerror(errno)); +#endif +#if 0 + /* TODO: maintain own links structure */ + else + plink->flags = 0; +#endif + } + } + } +#endif +} + +static struct device_data * +gst_pinesrcbin_find_media_device(GstPineSrcBin *self, const char *driver_name) +{ + int length = strlen(driver_name); + GList *l = self->media_list; + while (l) + { + struct device_data *dev = l->data; + const struct media_device_info *info = &dev->info; + g_print("device %s:%s:%s\n", dev->path, info->driver, driver_name); + if (!strncmp(info->driver, driver_name, length)) + return dev; + l = l->next; + } + return NULL; +} + +static gboolean +get_device_path(struct media_v2_intf_devnode devnode, char *path, int length) +{ + char uevent_path[256]; + snprintf(uevent_path, 256, "/sys/dev/char/%d:%d/uevent", devnode.major, + devnode.minor); + + FILE *f = fopen(uevent_path, "r"); + if (!f) + { + return FALSE; + } + + char line[512]; + while (fgets(line, 512, f)) + { + if (strncmp(line, "DEVNAME=", 8) == 0) + { + // Drop newline + int length = strlen(line); + if (line[length - 1] == '\n') + line[length - 1] = '\0'; + + snprintf(path, length, "/dev/%s", line + 8); + fclose(f); + return TRUE; + } + } + + fclose(f); + + return FALSE; +} + +static gboolean +query_autofocus(int fd) +{ + struct v4l2_query_ext_ctrl ctrl = {}; + ctrl.id = V4L2_CID_FOCUS_AUTO; + int rc = xioctl(fd, VIDIOC_QUERY_EXT_CTRL, &ctrl); + if (rc < 0) + return FALSE; + return TRUE; +} + +static void +setup_gain(int fd, int *gain_ctrl, int *gain_max) +{ + struct v4l2_query_ext_ctrl ctrl = {}; + ctrl.id = V4L2_CID_GAIN; + int rc = xioctl(fd, VIDIOC_QUERY_EXT_CTRL, &ctrl); + if (rc >= 0) + { + *gain_ctrl = ctrl.id; + *gain_max = ctrl.maximum; + return; + } + ctrl.id = V4L2_CID_ANALOGUE_GAIN; + rc = xioctl(fd, VIDIOC_QUERY_EXT_CTRL, &ctrl); + if (rc >= 0) + { + *gain_ctrl = ctrl.id; + *gain_max = ctrl.maximum; + return; + } +} + +static gboolean +camera_control(int fd, uint32_t id, int request, uint32_t *value) +{ + struct v4l2_ext_control ctrl = {}; + ctrl.id = id; + ctrl.value = *value; + + struct v4l2_ext_controls ctrls = { + .ctrl_class = 0, + .which = V4L2_CTRL_WHICH_CUR_VAL, + .count = 1, + .controls = &ctrl, + }; + if (xioctl(fd, request, &ctrls) == -1) + { + return TRUE; + } + + *value = ctrl.value; + return FALSE; +} + +static inline const struct media_v2_pad * +get_device_pad(const struct device_data *dev, uint32_t id) +{ + int i; + for (i = 0; i < dev->num_pads; i++) + { + if (dev->pads[i].id == id) + { + return &dev->pads[i]; + } + } + return NULL; +} + +/* TODO - get media device pad and video device pad and set up link */ +static gboolean +setup_link(const struct device_data *dev, uint32_t source_pad_id, + uint32_t sink_pad_id, gboolean enabled) +{ + const struct media_v2_pad *source_pad = get_device_pad(dev, source_pad_id); + g_return_val_if_fail(source_pad, FALSE); + + const struct media_v2_pad *sink_pad = get_device_pad(dev, sink_pad_id); + g_return_val_if_fail(sink_pad, FALSE); + + struct media_link_desc link = {}; + link.flags = enabled ? MEDIA_LNK_FL_ENABLED : 0; + link.source.entity = source_pad->entity_id; + link.source.index = 0; + link.sink.entity = sink_pad->entity_id; + link.sink.index = 0; + if (xioctl(dev->fd, MEDIA_IOC_SETUP_LINK, &link) == -1) + { + g_printerr("MEDIA_IOC_SETUP_LINK: %d %s", errno, strerror(errno)); + return FALSE; + } + + return TRUE; +} + +static int alloc_device(struct device_data *dev) +{ + int rc = -1; + g_print("topology x1\n"); + rc = xioctl(dev->fd, MEDIA_IOC_G_TOPOLOGY, &dev->topology); + if (rc < 0) + { + g_printerr("topology x1 error %d %d %s\n", rc, + errno, strerror(errno)); + goto out; + } + dev->entities = + calloc(dev->topology.num_entities, + sizeof(struct media_v2_entity)); + dev->num_entities = dev->topology.num_entities; + dev->interfaces = + calloc(dev->topology.num_interfaces, + sizeof(struct media_v2_interface)); + dev->num_interfaces = dev->topology.num_interfaces; + dev->pads = + calloc(dev->topology.num_pads, sizeof(struct media_v2_pad)); + dev->num_pads = dev->topology.num_pads; + dev->links = + calloc(dev->topology.num_links, sizeof(struct media_v2_link)); + dev->num_links = dev->topology.num_links; + dev->topology.ptr_entities = (uint64_t)dev->entities; + dev->topology.ptr_interfaces = (uint64_t)dev->interfaces; + dev->topology.ptr_pads = (uint64_t)dev->pads; + dev->topology.ptr_links = (uint64_t)dev->links; + g_print("topology x2\n"); + rc = xioctl(dev->fd, MEDIA_IOC_G_TOPOLOGY, &dev->topology); + if (rc < 0) + goto out; + + g_print("device info\n"); + rc = xioctl(dev->fd, MEDIA_IOC_DEVICE_INFO, &dev->info); +out: + return rc; +} + +static void subdev_set_mode(int subdev_fd, int width, + int height, + uint32_t bus_code, + struct v4l2_fract frame_interval) +{ + struct v4l2_subdev_frame_interval interval = {}; + struct v4l2_subdev_format fmt = {}; + gboolean frame_is_set; + interval.pad = 0; + interval.interval = frame_interval; + if (xioctl(subdev_fd, + VIDIOC_SUBDEV_S_FRAME_INTERVAL, + &interval) == -1) + g_error("VIDIOC_SUBDEV_S_FRAME_INTERVAL %d %s\n", + errno, strerror(errno)); + frame_is_set = + interval.interval.numerator == + frame_interval.numerator && + interval.interval.denominator == + frame_interval.denominator; + + fmt.pad = 0; + fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE; + fmt.format.width = width; + fmt.format.height = height; + fmt.format.code = + bus_code; + fmt.format.field = V4L2_FIELD_ANY; + if (xioctl(subdev_fd, VIDIOC_SUBDEV_S_FMT, &fmt) == -1) + g_error ("VIDIOC_SUBDEV_S_FMT: %d %s\n", errno, strerror(errno)); + if (!frame_is_set) { + if (xioctl(subdev_fd, + VIDIOC_SUBDEV_S_FRAME_INTERVAL, + &interval) == -1) + g_error("VIDIOC_SUBDEV_S_FRAME_INTERVAL %d %s\n", + errno, strerror(errno)); + + } +} +int +get_camera_config_no(const char *conf_name) +{ + int i; + for (i = 0; + i < sizeof(configurations)/sizeof(configurations[0]); i++) { + struct camera_config *d = &configurations[i]; + if (!strncmp(d->conf_name, conf_name, sizeof(d->conf_name))) + return i; + } + return -1; +} +static struct camera_config * +get_camera_config(const char *conf_name) +{ + int conf = get_camera_config_no(conf_name); + if (conf < 0) + return NULL; + else + return &configurations[conf]; +} +static struct camera_data * +get_camera_data(const char *conf_name) +{ + int conf = get_camera_config_no(conf_name); + if (conf < 0) + return NULL; + else { + cam_store[conf].conf = &configurations[conf]; + return &cam_store[conf]; + } +} +void gst_pinesrcbin_configure_device(GstPineSrcBin * self, const char *conf_name) +{ + struct camera_config *conf; + struct camera_data *cam_data; + self->media_list = NULL; + struct dirent *dir; + conf = get_camera_config(conf_name); + if (!conf) + g_error("invalid camera configuration name %s\n", conf_name); + cam_data = get_camera_data(conf_name); + g_assert(cam_data); + GST_DEBUG_OBJECT (self, "Scanning for media devices"); + DIR *d = opendir ("/dev"); + while ((dir = readdir (d)) != NULL) { + if (strncmp (dir->d_name, "media", 5) == 0) { + char path[261]; + int fd; + snprintf (path, 261, "/dev/%s", dir->d_name); + fd = open (path, O_RDWR); + GST_DEBUG_OBJECT (self, "Opened device: %s: fd=%d\n", path, fd); + if (fd >= 0) { + int rc = -1; + struct device_data *dev = g_new0 (struct device_data, 1); + if (!dev) + goto out; + dev->fd = fd; + strncpy (dev->path, path, sizeof (dev->path)); + rc = alloc_device(dev); + if (rc < 0) + goto out; + self->media_list = g_list_append(self->media_list, dev); + GST_DEBUG_OBJECT (self, "added device %s %s\n", dev->path, dev->info.driver); + out: + if (rc < 0 && dev) + g_free (dev); + } + } + } + /* TODO: configure file */ + self->media_if = gst_pinesrcbin_find_media_device (self, "sun6i-csi"); + if (!self->media_if) + g_error ("can't find device sun6i-csi\n"); + /* sink entity */ + self->v4l2entity = + gst_pinesrcbin_find_media_entity (self->media_if, "sun6i-csi"); + if (!self->v4l2entity) + g_error ("can't find entity sun6i-csi\n"); + GST_DEBUG_OBJECT (self, "sink entity: %d %s\n", self->v4l2entity->id, self->v4l2entity->name); + /* This is sink pad (for host controller) */ + self->v4l2pad = + gst_pinesrcbin_get_pad_from_entity (self->media_if, self->v4l2entity->id); + GST_DEBUG_OBJECT (self, "sink pad: %d\n", self->v4l2pad->id); + if (!self->v4l2pad) + g_error ("can't find interface pad sun6i-csi\n"); + self->v4l2interface = + gst_pinesrcbin_get_entity_interface (self->media_if, + self->v4l2entity->id); + if (!self->v4l2interface) + g_printerr ("can't find interface sun6i-csi\n"); + GST_DEBUG_OBJECT (self, "sink interface: %d\n", self->v4l2interface->id); + if (!get_device_path (self->v4l2interface->devnode, + self->devnode_path, sizeof (self->devnode_path))) + g_error ("Failed to get devnode path\n"); + GST_DEBUG_OBJECT (self, "sink interface devnode path: %s\n", self->devnode_path); + g_print ("devnode: %s\n", self->devnode_path); + /* /dev/video device fd */ + self->fd = open (self->devnode_path, O_RDWR); + if (self->fd < 0) + g_error ("failed to open %s\n", self->devnode_path); + /* TODO: source pad? sink pad? Need to disable active link*/ +// setup_link(self->media_if, ,self->v4l2interface->pad_id, false); + disable_all_links(self->media_if); + cam_data->entity = gst_pinesrcbin_find_media_entity(self->media_if, conf->camera_name); + GST_DEBUG_OBJECT (self, "camera: %s: source interface entity %d\n", conf->camera_name, cam_data->entity->id); + + cam_data->interface = + gst_pinesrcbin_get_entity_interface (self->media_if, + cam_data->entity->id); + GST_DEBUG_OBJECT (self, "camera: %s: source interface %d\n", conf->camera_name, cam_data->interface->id); + if (!get_device_path (cam_data->interface->devnode, + cam_data->devnode_path, sizeof (cam_data->devnode_path))) + g_error ("Failed to get subdevice devnode path for %s\n", conf->camera_name); + GST_DEBUG_OBJECT (self, "source subdevice %s\n", cam_data->devnode_path); + cam_data->subdev_fd = open (cam_data->devnode_path, O_RDWR); + if (cam_data->subdev_fd < 0) + g_error ("Failed to open subdevice: %s\n", cam_data->devnode_path); + + self->autofocus = FALSE; + if (query_autofocus (cam_data->subdev_fd)) { + uint32_t enable = 1U; + g_print ("autofocus possible\n"); + self->autofocus = TRUE; + camera_control (cam_data->subdev_fd, V4L2_CID_FOCUS_AUTO, VIDIOC_S_EXT_CTRLS, &enable); + } + setup_gain (cam_data->subdev_fd, &cam_data->gain_ctrl, &cam_data->gain_max); + /* now configuring camera modes */ + GST_DEBUG_OBJECT (self, "configuring camera modes\n"); + subdev_set_mode(cam_data->subdev_fd, conf->width, + conf->height, conf->bus_code_video, + conf->frame_interval); +} + diff --git a/utility.h b/utility.h new file mode 100644 index 0000000..ee38ad5 --- /dev/null +++ b/utility.h @@ -0,0 +1,26 @@ +#ifndef UTILITY_H +#define UTILITY_H +struct _GstPineSrcBin; +typedef struct _GstPineSrcBin GstPineSrcBin; +struct device_data +{ + char path[261]; + int fd; + struct media_v2_topology topology; + struct media_device_info info; + + struct media_v2_entity *entities; + size_t num_entities; + struct media_v2_interface *interfaces; + size_t num_interfaces; + struct media_v2_pad *pads; + size_t num_pads; + struct media_v2_link *links; + size_t num_links; +}; +void gst_pinesrcbin_configure_device(GstPineSrcBin * self, const char *conf_name); +void gst_pinesrcbin_get_gst_caps_data(const char * conf_name, + int *width, int * height, const char **format, + struct v4l2_fract *interval); + +#endif