static GstFlowReturn gst_camerasrc_create (GstPushSrc * src, GstBuffer ** out);
-static void gst_camerasrc_fixate (GstBaseSrc * basesrc, GstCaps * caps);
+static void gst_camerasrc_fixate_main (GstBaseSrc *basesrc, GstCaps * caps);
-static gboolean gst_camerasrc_negotiate (GstBaseSrc * basesrc);
+static gboolean gst_camerasrc_negotiate_main (GstBaseSrc *basesrc);
static void gst_camerasrc_set_property (GObject * object, guint prop_id,
const GValue * value, GParamSpec * pspec);
static void gst_camerasrc_update_max_zoom (GstCameraSrc * camerasrc);
+static gboolean gst_camerasrc_get_caps_info (GstCameraSrc * camerasrc,
+ GstCaps * caps, guint32 * four_cc, guint * w, guint * h,
+ guint * fps_n, guint * fps_d, guint * size);
+
+static gboolean gst_camerasrc_negotiate (GstCameraSrc *src, GstPad *pad);
+
+static void gst_camerasrc_fixate (GstCameraSrc *src, GstCaps * caps);
+
+static void
+gst_camerasrc_apply_timestamp (GstCameraSrc * camerasrc, GstBuffer * buf);
+
/*
*/
static void
return 0;
}
+
+static gboolean
+gst_camerasrc_second_pad_check_get_range(GstPad *pad)
+{
+ return FALSE;
+}
+
+static gboolean
+gst_camerasrc_second_pad_activate_pull(GstPad *pad, gboolean active)
+{
+ return FALSE;
+}
+
+static gboolean
+ensure_second_stream_started(GstCameraSrc *src)
+{
+ GstCameraSrcClass *kclass;
+ gboolean ret;
+
+ if (src->second_started)
+ return TRUE;
+
+ kclass = GST_CAMERA_SRC_GET_CLASS(src);
+
+ /* wait until first(main) stream started */
+ g_mutex_lock(src->second_lock);
+ while(!src->first_started && !src->second_stoped)
+ g_cond_wait(src->first_done_cond, src->second_lock);
+ g_mutex_unlock(src->second_lock);
+
+ if (src->second_stoped)
+ return FALSE;
+
+ ret = kclass->start_second(src, src->second_caps);
+ if (!ret)
+ goto failed;
+
+ src->second_started = TRUE;
+ return TRUE;
+
+failed:
+ return FALSE;
+}
+
+static void
+_second_pad_loop(GstPad *pad)
+{
+ GstCameraSrc *src = GST_CAMERA_SRC(GST_OBJECT_PARENT(pad));
+ GstCameraSrcClass *kclass = GST_CAMERA_SRC_GET_CLASS(src);
+ GstBuffer *buf = NULL;
+ GstFlowReturn ret;
+
+ if(!ensure_second_stream_started(src))
+ goto stop;
+
+ ret = kclass->create_second_buffer(src, &buf);
+
+ if (ret != GST_FLOW_OK || !buf) {
+ GST_WARNING_OBJECT(src, "get second buffer failed.");
+ return;
+ }
+
+ if (!GST_BUFFER_CAPS(buf))
+ gst_buffer_set_caps (buf, src->second_caps);
+
+ if (GST_BUFFER_TIMESTAMP (buf) == GST_CLOCK_TIME_NONE) {
+ gst_camerasrc_apply_timestamp (src, buf);
+ }
+
+ ret = gst_pad_push (pad, buf);
+ if (ret != GST_FLOW_OK) {
+ GST_WARNING_OBJECT(src, "push second buffer failed.");
+ goto stop;
+ }
+ return;
+
+stop:
+ /* TODO, may need stop whole pipeline or send signal */
+ GST_WARNING_OBJECT(src, "second pad need to stop");
+}
+
+static gboolean
+gst_camerasrc_second_pad_activate_push(GstPad *pad, gboolean active)
+{
+ GstCameraSrc *src;
+ GstCameraSrcClass *kclass;
+ gboolean ret = TRUE;
+
+ src = GST_CAMERA_SRC(GST_OBJECT_PARENT(pad));
+ kclass = GST_CAMERA_SRC_GET_CLASS(src);
+
+ g_return_val_if_fail(kclass->open_second, FALSE);
+
+ if (active) {
+ if (!GST_PAD_IS_LINKED(pad))
+ return TRUE; /* do nothing to make main pad works */
+
+ ret = kclass->open_second(src);
+ if (!ret)
+ goto failed;
+
+ ret = gst_camerasrc_negotiate(src, pad);
+ if (!ret)
+ goto failed;
+
+ ret = gst_pad_start_task (pad, (GstTaskFunction)_second_pad_loop, pad);
+ }
+ else {
+ g_mutex_lock(src->second_lock);
+ src->second_stoped = TRUE;
+ g_cond_signal(src->first_done_cond);
+ g_mutex_unlock(src->second_lock);
+
+ gst_pad_stop_task (pad);
+ src->second_started = FALSE;
+
+ if (kclass->close_second)
+ ret = kclass->close_second(src);
+ }
+ return ret;
+
+failed:
+ GST_WARNING_OBJECT(src, "%s second stream failed", (active? "start" : "stop"));
+ return FALSE;
+}
+
+static GstCaps*
+gst_camerasrc_second_pad_getcaps(GstPad *pad)
+{
+ return gst_caps_copy (gst_pad_get_pad_template_caps (pad));
+}
+
+static gboolean
+gst_camerasrc_second_pad_setcaps(GstPad *pad, GstCaps *caps)
+{
+ GstCameraSrc *src;
+ GstCameraSrcClass *bclass;
+ guint w = 0, h = 0;
+ guint32 fourcc = 0;
+ guint fps_n, fps_d;
+ guint size;
+ gboolean ret;
+
+ src = GST_CAMERA_SRC(GST_OBJECT_PARENT(pad));
+ bclass = GST_CAMERA_SRC_GET_CLASS(src);
+
+ g_return_val_if_fail(gst_caps_is_fixed(caps), FALSE);
+
+ if (!bclass->is_second_open (src))
+ return FALSE;
+
+ if (!gst_camerasrc_get_caps_info(src, caps, &fourcc, &w, &h, &fps_n,
+ &fps_d, &size)) {
+ GST_DEBUG_OBJECT (src,
+ "second pad can't get capture info from caps %" GST_PTR_FORMAT, caps);
+ return FALSE;
+ }
+
+ if (src->second_caps) {
+ gst_caps_unref(src->second_caps);
+ GST_DEBUG_OBJECT (src,
+ "second pad remove old caps %" GST_PTR_FORMAT, src->second_caps);
+ }
+ src->second_caps = gst_caps_ref(caps);
+
+ ret = bclass->set_second_capture_mode(src);
+ ret = bclass->set_second_capture(src, &fourcc, &w, &h, &fps_n, &fps_d);
+ if (ret) {
+ src->second_w = w;
+ src->second_h = h;
+ src->second_fps_n = fps_n;
+ src->second_fps_d = fps_d;
+ src->second_fourcc = fourcc;
+ src->second_duration = gst_util_uint64_scale_int (GST_SECOND, fps_d, fps_n);
+ } else
+ goto failed;
+
+ return TRUE;
+
+failed:
+ GST_WARNING_OBJECT(src,
+ "set second pad's caps failed, caps %" GST_PTR_FORMAT, caps);
+ return FALSE;
+}
+
+static void
+gst_camerasrc_second_pad_fixate(GstPad *pad, GstCaps *caps)
+{
+ GstCameraSrc *src = GST_CAMERA_SRC(GST_OBJECT_PARENT(pad));
+ gst_camerasrc_fixate(src, caps);
+}
+
+static GstPadLinkReturn
+gst_camerasrc_second_pad_link(GstPad *pad, GstPad *peer)
+{
+ GstCameraSrc *src = GST_CAMERA_SRC(GST_OBJECT_PARENT(pad));
+ GstPad *main_pad = GST_BASE_SRC_PAD(src);
+
+ /* need link src pad first */
+ if (!gst_pad_is_linked(main_pad))
+ return GST_PAD_LINK_REFUSED;
+
+ return GST_PAD_LINK_OK;
+}
+
/*
*/
static void
basesrc_class->unlock_stop = GST_DEBUG_FUNCPTR (gst_camerasrc_unlock_stop);
basesrc_class->stop = GST_DEBUG_FUNCPTR (gst_camerasrc_stop);
basesrc_class->query = GST_DEBUG_FUNCPTR (gst_camerasrc_query);
- basesrc_class->fixate = GST_DEBUG_FUNCPTR (gst_camerasrc_fixate);
- basesrc_class->negotiate = GST_DEBUG_FUNCPTR (gst_camerasrc_negotiate);
+ basesrc_class->fixate = GST_DEBUG_FUNCPTR (gst_camerasrc_fixate_main);
+ basesrc_class->negotiate = GST_DEBUG_FUNCPTR (gst_camerasrc_negotiate_main);
basesrc_class->event = GST_DEBUG_FUNCPTR (gst_camerasrc_event);
pushsrc_class->create = GST_DEBUG_FUNCPTR (gst_camerasrc_create);
/* Initialize vmethods with default implementations */
gst_camerasrc_default_functions_init (klass);
+
+ GST_DEBUG_REGISTER_FUNCPTR(gst_camerasrc_second_pad_activate_push);
+ GST_DEBUG_REGISTER_FUNCPTR(gst_camerasrc_second_pad_activate_pull);
+ GST_DEBUG_REGISTER_FUNCPTR(gst_camerasrc_second_pad_check_get_range);
+ GST_DEBUG_REGISTER_FUNCPTR(gst_camerasrc_second_pad_getcaps);
+ GST_DEBUG_REGISTER_FUNCPTR(gst_camerasrc_second_pad_setcaps);
+ GST_DEBUG_REGISTER_FUNCPTR(gst_camerasrc_second_pad_fixate);
+ GST_DEBUG_REGISTER_FUNCPTR(gst_camerasrc_second_pad_link);
}
/*
static void
gst_camerasrc_init (GstCameraSrc * camerasrc, GstCameraSrcClass * klass)
{
+ GstPad *second_pad;
+ GstPadTemplate *pad_template;
+
/* number of buffers requested */
camerasrc->always_copy = DEFAULT_PROP_ALWAYS_COPY;
camerasrc->requested_af_mode = AF_NONE_REQUESTED;
camerasrc->maker_note = FALSE;
+
+ /* second pad init */
+ pad_template =
+ gst_element_class_get_pad_template (GST_ELEMENT_CLASS (klass), "second");
+ second_pad = gst_pad_new_from_template (pad_template, "second");
+ gst_pad_set_activatepush_function (second_pad, gst_camerasrc_second_pad_activate_push);
+ gst_pad_set_activatepull_function (second_pad, gst_camerasrc_second_pad_activate_pull);
+ //gst_pad_set_event_function (second_pad, gst_camerasrc_second_pad_event_handler);
+ //gst_pad_set_query_function (second_pad, gst_camerasrc_second_pad_query);
+ gst_pad_set_checkgetrange_function (second_pad, gst_camerasrc_second_pad_check_get_range);
+ //gst_pad_set_getrange_function (second_pad, gst_camerasrc_second_pad_get_range);
+ gst_pad_set_getcaps_function (second_pad, gst_camerasrc_second_pad_getcaps);
+ gst_pad_set_setcaps_function (second_pad, gst_camerasrc_second_pad_setcaps);
+ gst_pad_set_fixatecaps_function (second_pad, gst_camerasrc_second_pad_fixate);
+ //gst_pad_set_link_function (second_pad, gst_camerasrc_second_pad_link);
+
+ GST_CAMERA_SRC_SECOND_PAD(camerasrc) = second_pad;
+ gst_element_add_pad(GST_ELEMENT(camerasrc), second_pad);
+
+ camerasrc->second_w = 0;
+ camerasrc->second_h = 0;
+ camerasrc->second_fps_n = 0;
+ camerasrc->second_fps_d = 0;
+ camerasrc->second_fourcc = 0;
+ camerasrc->second_duration = 0;
+ camerasrc->second_started = FALSE;
+ camerasrc->second_stoped = FALSE;
+ camerasrc->second_lock = g_mutex_new ();
+ camerasrc->first_done_cond = g_cond_new();
+ camerasrc->first_started = FALSE;
}
/*
g_mutex_free (camerasrc->af_lock);
camerasrc->af_lock = NULL;
}
+ if (camerasrc->second_lock) {
+ g_mutex_free(camerasrc->second_lock);
+ camerasrc->second_lock = NULL;
+ }
+ if (camerasrc->first_done_cond) {
+ g_cond_free(camerasrc->first_done_cond);
+ camerasrc->first_done_cond = NULL;
+ }
gst_camerasrc_clear_color_channels (camerasrc);
gst_camerasrc_clear_cameracontrol_channels (camerasrc);
/* this function is a bit of a last resort */
static void
-gst_camerasrc_fixate (GstBaseSrc * basesrc, GstCaps * caps)
+gst_camerasrc_fixate (GstCameraSrc *src, GstCaps * caps)
{
GstStructure *structure;
gint i;
- GST_DEBUG_OBJECT (basesrc, "fixating caps %" GST_PTR_FORMAT, caps);
+ GST_DEBUG_OBJECT (src, "fixating caps %" GST_PTR_FORMAT, caps);
for (i = 0; i < gst_caps_get_size (caps); ++i) {
const GValue *v;
}
}
- GST_DEBUG_OBJECT (basesrc, "fixated caps %" GST_PTR_FORMAT, caps);
+ GST_DEBUG_OBJECT (src, "fixated caps %" GST_PTR_FORMAT, caps);
+}
+
+static void
+gst_camerasrc_fixate_main (GstBaseSrc *basesrc, GstCaps * caps)
+{
+ gst_camerasrc_fixate(GST_CAMERA_SRC_CAST(basesrc), caps);
}
/*
*/
static gboolean
-gst_camerasrc_negotiate (GstBaseSrc * basesrc)
+gst_camerasrc_negotiate (GstCameraSrc *src, GstPad *pad)
{
GstCaps *thiscaps;
gboolean result = FALSE;
/* first see what is possible on our source pad */
- thiscaps = gst_pad_get_caps (GST_BASE_SRC_PAD (basesrc));
- GST_DEBUG_OBJECT (basesrc, "caps of src suppressed on DEBUG (>= 5 to see)");
- GST_LOG_OBJECT (basesrc, "caps of src: %" GST_PTR_FORMAT, thiscaps);
+ thiscaps = gst_pad_get_caps (pad);
+ GST_DEBUG_OBJECT (src, "caps of src suppressed on DEBUG (>= 5 to see)");
+ GST_LOG_OBJECT (src, "caps of src: %" GST_PTR_FORMAT, thiscaps);
/* nothing or anything is allowed, we're done */
if (thiscaps == NULL || gst_caps_is_any (thiscaps))
goto no_nego_needed;
/* get the peer caps */
- peercaps = gst_pad_peer_get_caps (GST_BASE_SRC_PAD (basesrc));
- GST_DEBUG_OBJECT (basesrc, "caps of peer: %" GST_PTR_FORMAT, peercaps);
+ peercaps = gst_pad_peer_get_caps (pad);
+ GST_DEBUG_OBJECT (src, "caps of peer: %" GST_PTR_FORMAT, peercaps);
if (peercaps && !gst_caps_is_any (peercaps)) {
GstCaps *icaps = NULL;
int i;
/* get intersection */
GstCaps *ipcaps = gst_caps_copy_nth (peercaps, i);
- GST_DEBUG_OBJECT (basesrc, "peer: %" GST_PTR_FORMAT, ipcaps);
+ GST_DEBUG_OBJECT (src, "peer: %" GST_PTR_FORMAT, ipcaps);
icaps = gst_caps_intersect (thiscaps, ipcaps);
gst_caps_unref (ipcaps);
icaps = NULL;
}
- GST_DEBUG_OBJECT (basesrc, "intersect: %" GST_PTR_FORMAT, icaps);
+ GST_DEBUG_OBJECT (src, "intersect: %" GST_PTR_FORMAT, icaps);
if (icaps) {
/* If there are multiple intersections pick the one with the smallest
* resolution strictly bigger then the first peer caps */
gst_structure_set (s, "row-stride", GST_TYPE_INT_RANGE, 0, G_MAXINT, NULL);
}
}
- gst_pad_fixate_caps (GST_BASE_SRC_PAD (basesrc), caps);
- GST_DEBUG_OBJECT (basesrc, "fixated to: %" GST_PTR_FORMAT, caps);
+ gst_pad_fixate_caps (pad, caps);
+ GST_DEBUG_OBJECT (src, "fixated to: %" GST_PTR_FORMAT, caps);
if (gst_caps_is_any (caps)) {
/* hmm, still anything, so element can do anything and
result = TRUE;
} else if (gst_caps_is_fixed (caps)) {
/* yay, fixed caps, use those then */
- result = gst_pad_set_caps (GST_BASE_SRC_PAD (basesrc), caps);
- GST_DEBUG_OBJECT (basesrc, "Set caps returned: %d", result);
+ result = gst_pad_set_caps (pad, caps);
+ GST_DEBUG_OBJECT (src, "Set caps returned: %d", result);
}
}
gst_caps_unref (caps);
no_nego_needed:
{
- GST_DEBUG_OBJECT (basesrc, "no negotiation needed");
+ GST_DEBUG_OBJECT (src, "no negotiation needed");
if (thiscaps)
gst_caps_unref (thiscaps);
return TRUE;
}
}
+static gboolean
+gst_camerasrc_negotiate_main (GstBaseSrc *basesrc)
+{
+ GstPad *pad = GST_BASE_SRC_PAD (basesrc);
+ GstCameraSrc *src = GST_CAMERA_SRC_CAST(basesrc);
+
+ g_return_val_if_fail(pad, FALSE);
+
+ return gst_camerasrc_negotiate(src, pad);
+}
/*
*/
GST_TIME_ARGS (camerasrc->duration));
// camerasrc->frame_byte_size = size;
+
+ g_mutex_lock(camerasrc->second_lock);
+ camerasrc->first_started = TRUE;
+ g_cond_signal(camerasrc->first_done_cond);
+ g_mutex_unlock(camerasrc->second_lock);
}
return ret;
camerasrc->photo_capture_phase = GST_CAMERA_VIEWFINDER;
+ g_mutex_lock(camerasrc->second_lock);
+ camerasrc->first_started = FALSE;
+ g_mutex_unlock(camerasrc->second_lock);
+
return TRUE;
}
GST_DEBUG_CATEGORY_EXTERN (gst_v4l2camsrc_debug);
#define GST_CAT_DEFAULT gst_v4l2camsrc_debug
+#define GST_V4L2CAMSRC_SECOND_VIDEO_DEFAULT_NAME "/dev/video1"
+
extern guint gst_camerasrc_signals[CAMERA_IN_LAST_SIGNAL];
static const gint gst_v4l2camsrc_capture_map[] = {
static GstV4l2Buffer *
gst_v4l2camsrc_buffer_new (GstMFLDV4l2CamSrcBufferPool * pool,
- GstCameraSrc * camsrc, guint index, GstCaps * caps)
+ GstMFLDV4l2CamSrc *v4l2camsrc, guint index, GstCaps * caps,
+ gboolean use_mmap, gboolean is_second, guint frame_byte_size,
+ GstPad *pad)
{
- GstMFLDV4l2CamSrc *v4l2camsrc = GST_V4L2CAMSRC (camsrc);
GstV4l2Buffer *ret = NULL;
GstFlowReturn flow_ret;
GstBuffer *buf_cap_signal2 = NULL; /*output main buffer for capture signal*/
struct v4l2_buffer *vbuffer;
ret = (GstV4l2Buffer *) gst_mini_object_new (GST_TYPE_V4L2CAMSRC_BUFFER);
- ret->use_mmap = v4l2camsrc->use_mmap;
+ ret->use_mmap = use_mmap;
vbuffer = ret->vbuffer = g_new0 (struct v4l2_buffer, 1);
GST_LOG ("creating buffer %u, %p in pool %p", index, ret, pool);
ret->pool =
vbuffer->index = index;
vbuffer->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
- if (v4l2camsrc->use_mmap)
+ if (use_mmap)
vbuffer->memory = V4L2_MEMORY_MMAP;
else
vbuffer->memory = V4L2_MEMORY_USERPTR;
- vbuffer->length = v4l2camsrc->frame_byte_size;
+ vbuffer->length = frame_byte_size;
ret->gbuffer = NULL;
if (ioctl (pool->video_fd, VIDIOC_QUERYBUF, vbuffer) < 0)
goto querybuf_failed;
- if (v4l2camsrc->use_mmap) {
+ if (use_mmap) {
void *data;
data = (guint8 *) mmap (0, vbuffer->length,
PROT_READ | PROT_WRITE, MAP_SHARED, pool->video_fd, vbuffer->m.offset);
GST_BUFFER_DATA (ret) = (guint8 *) data;
} else {
- if (v4l2camsrc->capture_mode == GST_CAMERA_SRC_CAPTURE_MODE_STILL) {
+ if (!is_second && v4l2camsrc->capture_mode == GST_CAMERA_SRC_CAPTURE_MODE_STILL) {
/*call signal*/
/* alloc buffer for capture callback */
"height", G_TYPE_INT,v4l2camsrc->capture_h,
NULL);
- GST_LOG_OBJECT (camsrc, "CALL: usrptr callback");
- g_signal_emit( G_OBJECT (camsrc),
+ GST_LOG_OBJECT (v4l2camsrc, "CALL: usrptr callback");
+ g_signal_emit( G_OBJECT (v4l2camsrc),
gst_camerasrc_signals[CAMERA_IN_SIGNAL_STILL_USRPTR_BUFFER],
0,
buf_cap_signal2);
if (GST_BUFFER_DATA(buf_cap_signal2) == NULL)
goto usrptr_alloc_failed;
- GST_LOG_OBJECT (camsrc, "RETURN: usrptr callback: buf=%p, size=%d",
+ GST_LOG_OBJECT (v4l2camsrc, "RETURN: usrptr callback: buf=%p, size=%d",
GST_BUFFER_DATA(buf_cap_signal2), GST_BUFFER_SIZE(buf_cap_signal2));
GST_BUFFER_DATA (ret) = GST_BUFFER_DATA(buf_cap_signal2);
gst_buffer_unref(buf_cap_signal2);
} else {
- if (gst_pad_is_linked (GST_BASE_SRC_PAD (v4l2camsrc))) {
- GST_LOG ("using pad_alloc, size=%d", v4l2camsrc->frame_byte_size);
+ if (gst_pad_is_linked (pad)) {
+ GST_LOG ("using pad_alloc, size=%d", frame_byte_size);
GST_LOG ("ALLOC CAPS: %" GST_PTR_FORMAT, caps);
flow_ret =
- gst_pad_alloc_buffer_and_set_caps (GST_BASE_SRC_PAD (v4l2camsrc), 0LL,
- v4l2camsrc->frame_byte_size, caps, &ret->gbuffer);
+ gst_pad_alloc_buffer_and_set_caps (pad, 0LL,
+ frame_byte_size, caps, &ret->gbuffer);
if (flow_ret != GST_FLOW_OK)
goto pad_alloc_failed;
GST_BUFFER_DATA (ret) = ret->gbuffer->data;
}
}
- GST_BUFFER_SIZE (ret) = v4l2camsrc->frame_byte_size;
+ GST_BUFFER_SIZE (ret) = frame_byte_size;
GST_BUFFER_FLAG_SET (ret, GST_BUFFER_FLAG_READONLY);
gst_buffer_set_caps (GST_BUFFER (ret), caps);
#ifdef USE_MLOCK
GST_DEBUG ("mlocking buffer data");
- if (mlock ((void *) GST_BUFFER_DATA (ret), v4l2camsrc->frame_byte_size) == -1)
+ if (mlock ((void *) GST_BUFFER_DATA (ret), frame_byte_size) == -1)
goto mlock_failed;
#endif
/* mlocking succeeded, now we can set the pointer to vbuffer. The existence
* of this pointer will be used later to determine if the munlock() is
* needed */
- if (!v4l2camsrc->use_mmap)
+ if (!use_mmap)
vbuffer->m.userptr = (unsigned int) GST_BUFFER_DATA (ret);
pool->num_live_buffers = 0;
pool->data_cond = g_cond_new ();
pool->is_vaapi_sharing = FALSE;
+ pool->frame_byte_size = 0;
}
/*
/*
*/
static GstMFLDV4l2CamSrcBufferPool *
-gst_v4l2camsrc_buffer_pool_new (GstCameraSrc * camsrc, gint fd,
- GstCaps * caps)
+gst_v4l2camsrc_buffer_pool_new (GstMFLDV4l2CamSrc *v4l2camsrc, gint fd,
+ guint buf_count, GstCaps * caps,
+ gboolean use_mmap, gboolean is_second,
+ guint frame_byte_size, GstPad *pad)
{
- GstMFLDV4l2CamSrc *v4l2camsrc = GST_V4L2CAMSRC (camsrc);
GstMFLDV4l2CamSrcBufferPool *pool;
gint n;
if (structure && gst_structure_has_name(structure, "video/x-vaapi-sharing"))
pool->is_vaapi_sharing = TRUE;
- pool->buffer_count = v4l2camsrc->num_buffers;
+ pool->buffer_count = buf_count;
pool->buffers = g_new0 (GstV4l2Buffer *, pool->buffer_count);
pool->queued = g_new0 (guint, pool->buffer_count);
+ pool->frame_byte_size = frame_byte_size;
for (n = 0; n < pool->buffer_count; n++) {
- pool->buffers[n] = gst_v4l2camsrc_buffer_new (pool, camsrc, n, caps);
+ pool->buffers[n] = gst_v4l2camsrc_buffer_new (
+ pool, v4l2camsrc, n, caps,
+ use_mmap, is_second, frame_byte_size, pad);
GST_LOG ("buffer ref is %d", GST_MINI_OBJECT_REFCOUNT (pool->buffers[n]));
if (!pool->buffers[n])
goto buffer_new_failed;
("Could not enqueue buffers in device '%s'.",
v4l2camsrc->videodev),
("enqueing buffer %d/%d failed: %s",
- n, v4l2camsrc->num_buffers, g_strerror (errno)));
+ n, pool->buffer_count, g_strerror (errno)));
g_mutex_unlock (pool->lock);
return FALSE;
}
* when they are finalized, so there is no need to wait for them separately */
static gboolean
gst_v4l2_buffer_pool_update (GstMFLDV4l2CamSrcBufferPool * pool,
- GstMFLDV4l2CamSrc * v4l2camsrc)
+ GstMFLDV4l2CamSrc * v4l2camsrc, guint num_buffers)
{
gint n;
gint ref = 0;
}
/* if all the buffers are dequeued, wait */
- if (ref == v4l2camsrc->num_buffers) {
+ if (ref == num_buffers) {
GST_LOG ("no free buffers available");
g_cond_wait (pool->data_cond, pool->lock);
}
g_mutex_unlock (pool->lock);
- return (ref != v4l2camsrc->num_buffers) ? TRUE : FALSE;
+ return (ref != num_buffers) ? TRUE : FALSE;
}
/*
if (pool->buffers[n].m.userptr) {
GST_DEBUG ("munlocking buffer data");
munlock ((void *) pool->buffers[n].m.userptr,
- v4l2camsrc->frame_byte_size);
+ pool->frame_byte_size);
}
#endif
/* we own the ref if the buffer is in pool->buffers; drop it. */
/* wait if all buffers are DQBuf */
g_mutex_lock (v4l2camsrc->device_mutex);
- gst_v4l2_buffer_pool_update (v4l2camsrc->pool, v4l2camsrc);
+ gst_v4l2_buffer_pool_update (v4l2camsrc->pool, v4l2camsrc, v4l2camsrc->num_buffers);
g_mutex_unlock (v4l2camsrc->device_mutex);
memset (&buffer, 0x00, sizeof (buffer));
GST_LOG_OBJECT (v4l2camsrc, "initiating buffer pool");
if (!(v4l2camsrc->pool =
- gst_v4l2camsrc_buffer_pool_new (camsrc, fd, caps)))
+ gst_v4l2camsrc_buffer_pool_new (v4l2camsrc, fd,
+ v4l2camsrc->num_buffers, caps,
+ v4l2camsrc->use_mmap, FALSE,
+ v4l2camsrc->frame_byte_size, GST_BASE_SRC_PAD(v4l2camsrc))))
goto buffer_pool_new_failed;
GST_INFO_OBJECT (v4l2camsrc, "capturing buffers");
v4l2camsrc->is_open = FALSE;
return TRUE;
}
+
+gboolean
+gst_v4l2camsrc_open_second(GstCameraSrc *base)
+{
+ struct stat st;
+ GstPollFD pollfd = GST_POLL_FD_INIT;
+ const gchar *video_name = GST_V4L2CAMSRC_SECOND_VIDEO_DEFAULT_NAME;
+ GstMFLDV4l2CamSrc *src = GST_V4L2CAMSRC_CAST (base);
+
+ GST_DEBUG_OBJECT (src, "Trying to open %s", video_name);
+
+ if (GST_V4L2CAMSRC_IS_SECOND_OPEN(src)) {
+ GST_WARNING("second video already opened");
+ return FALSE;
+ }
+
+ /* check if it is a device */
+ if (stat (video_name, &st) == -1)
+ goto failed;
+
+ if (!S_ISCHR (st.st_mode))
+ goto failed;
+
+ /* open the device */
+ src->second_video_fd =
+ open (video_name, O_RDWR /* | O_NONBLOCK */ );
+
+ if (!GST_V4L2CAMSRC_IS_SECOND_OPEN (src))
+ goto failed;
+
+ pollfd.fd = src->second_video_fd;
+ gst_poll_add_fd (src->second_poll, &pollfd);
+ gst_poll_fd_ctl_read (src->second_poll, &pollfd, TRUE);
+ return TRUE;
+
+failed:
+ GST_WARNING("open second video %s failed", video_name);
+
+
+}
+
+gboolean
+gst_v4l2camsrc_close_second(GstCameraSrc *base)
+{
+ GstMFLDV4l2CamSrc *src = GST_V4L2CAMSRC_CAST (base);
+ const gchar *video_name = GST_V4L2CAMSRC_SECOND_VIDEO_DEFAULT_NAME;
+
+ GstPollFD pollfd = GST_POLL_FD_INIT;
+
+ GST_DEBUG_OBJECT (src, "Trying to close %s", video_name);
+
+ if (!GST_V4L2CAMSRC_IS_SECOND_OPEN(src)) {
+ return TRUE;
+ }
+
+ close (src->second_video_fd);
+ pollfd.fd = src->second_video_fd;
+ gst_poll_remove_fd (src->second_poll, &pollfd);
+ src->second_video_fd = -1;
+
+ return TRUE;
+}
+
+gboolean
+gst_v4l2camsrc_set_second_capture_mode(GstCameraSrc *base)
+{
+ GstMFLDV4l2CamSrc *src = GST_V4L2CAMSRC_CAST (base);
+ struct v4l2_streamparm parm;
+ guint fd = src->second_video_fd;
+ int ret = 0;
+
+ memset(&parm, 0, sizeof(parm));
+ parm.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+
+ g_mutex_lock (src->device_mutex);
+ if ((ret = ioctl(fd, VIDIOC_G_PARM, &parm)) < 0) {
+ GST_DEBUG_OBJECT(src, "Unable to get second video param: %s (%d).",
+ strerror(errno), errno);
+ goto done;
+ }
+ parm.parm.capture.capturemode = CI_MODE_VIDEO;
+
+ if ((ret = ioctl (fd, VIDIOC_S_PARM, &parm)) < 0) {
+ GST_DEBUG_OBJECT(src, "Unable to set second video param: %s (%d).",
+ strerror(errno), errno);
+ }
+
+done:
+ g_mutex_unlock (src->device_mutex);
+ return (ret == 0);
+}
+
+gboolean
+gst_v4l2camsrc_set_second_capture(
+ GstCameraSrc *base,
+ guint32 *pixelformat,
+ guint *width, guint32 *height,
+ guint *fps_n, guint *fps_d
+)
+{
+ GstMFLDV4l2CamSrc *src = GST_V4L2CAMSRC_CAST (base);
+ gint fd = src->second_video_fd;
+ struct v4l2_format format;
+ struct v4l2_streamparm stream;
+
+ GST_DEBUG_OBJECT(src, "try set second capture");
+
+ memset (&format, 0, sizeof (struct v4l2_format));
+ format.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+
+ if (ioctl (fd, VIDIOC_G_FMT, &format) < 0)
+ goto failed;
+
+ format.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ format.fmt.pix.width = *width;
+ format.fmt.pix.height = *height;
+ format.fmt.pix.pixelformat = *pixelformat;
+ format.fmt.pix.field = V4L2_FIELD_ANY;
+
+ if (ioctl (fd, VIDIOC_S_FMT, &format) < 0) {
+ if (errno != EINVAL)
+ goto failed;
+
+ /* try again with progressive video */
+ format.fmt.pix.width = *width;
+ format.fmt.pix.height = *height;
+ format.fmt.pix.pixelformat = *pixelformat;
+ format.fmt.pix.field = V4L2_FIELD_NONE;
+ if (ioctl (fd, VIDIOC_S_FMT, &format) < 0)
+ goto failed;
+ }
+
+ if (format.fmt.pix.width != *width || format.fmt.pix.height != *height
+ || format.fmt.pix.pixelformat != *pixelformat)
+ goto invalid_info;
+
+ memset (&stream, 0x00, sizeof (struct v4l2_streamparm));
+ stream.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ if (ioctl (fd, VIDIOC_G_PARM, &stream) < 0) {
+ GST_DEBUG_OBJECT(src,"second video get param failed");
+ goto done;
+ }
+
+ if (fps_n == NULL || fps_d == NULL) {
+ goto done;
+ }
+
+ /* Note: V4L2 provides the frame interval, we have the frame rate */
+ if (stream.parm.capture.timeperframe.denominator &&
+ fractions_are_equal (stream.parm.capture.timeperframe.numerator,
+ stream.parm.capture.timeperframe.denominator, *fps_d, *fps_n)) {
+ goto done;
+ }
+
+ /* We want to change the frame rate, so check whether we can. Some cheap USB
+ * cameras don't have the capability */
+ if ((stream.parm.capture.capability & V4L2_CAP_TIMEPERFRAME) == 0) {
+ GST_DEBUG_OBJECT (src, "Not setting framerate (second video not supported)");
+ goto done;
+ }
+
+ stream.parm.capture.timeperframe.numerator = *fps_d;
+ stream.parm.capture.timeperframe.denominator = *fps_n;
+
+ /* some cheap USB cam's won't accept any change */
+ if (ioctl (fd, VIDIOC_S_PARM, &stream) < 0) {
+ GST_WARNING_OBJECT (src,
+ "Second video input device did not accept new frame rate setting");
+ goto done;
+ }
+
+done:
+ src->second_frame_byte_size = gst_v4l2camsrc_get_frame_size (*pixelformat,
+ width, height);
+ return TRUE;
+
+invalid_info:
+ GST_ERROR_OBJECT (src,
+ "tried to set format:%" GST_FOURCC_FORMAT " size: %dx%d, "
+ "but failed and device returned format:%" GST_FOURCC_FORMAT "size: %dx%d",
+ GST_FOURCC_ARGS (*pixelformat), *width, *height,
+ GST_FOURCC_ARGS (format.fmt.pix.pixelformat),
+ format.fmt.pix.width,
+ format.fmt.pix.height);
+
+failed:
+ GST_WARNING_OBJECT(src, "set second capture failed");
+ return FALSE;
+}
+
+gboolean
+gst_v4l2camsrc_start_second(GstCameraSrc *base, GstCaps *caps)
+{
+ GstMFLDV4l2CamSrc *src = GST_V4L2CAMSRC_CAST (base);
+ gint fd = src->second_video_fd;
+ struct v4l2_requestbuffers breq;
+ gint type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+
+ memset (&breq, 0, sizeof (breq));
+ breq.count = src->second_num_buffers;
+ breq.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ //if (src->use_mmap)
+ // breq.memory = V4L2_MEMORY_MMAP;
+ //else
+ breq.memory = V4L2_MEMORY_USERPTR;
+
+ if (ioctl (fd, VIDIOC_REQBUFS, &breq) < 0)
+ goto failed;
+
+ GST_DEBUG_OBJECT(src,
+ "Second video request buffer, count:%d, type:%d, memory:%d",
+ breq.count, breq.type, breq.memory);
+
+ if (breq.count < GST_V4L2CAMSRC_MIN_BUFFERS)
+ goto failed;
+
+ if (src->second_num_buffers != breq.count) {
+ GST_WARNING_OBJECT (src, "second video using %u buffers instead", breq.count);
+ src->second_num_buffers = breq.count;
+ }
+
+ /*init second pool*/
+ if (!(src->second_pool =
+ gst_v4l2camsrc_buffer_pool_new (src, fd,
+ src->second_num_buffers, caps,
+ FALSE, TRUE,
+ src->second_frame_byte_size, GST_CAMERA_SRC_SECOND_PAD(src))))
+ goto failed;
+
+ GST_DEBUG_OBJECT (src, "second video capturing buffers");
+
+ if (!gst_v4l2camsrc_buffer_pool_activate (src->second_pool, src))
+ goto failed;
+
+ if (ioctl (fd, VIDIOC_STREAMON, &type) < 0)
+ goto failed;
+
+ GST_DEBUG_OBJECT (src, "second video STREAMON called");
+ return TRUE;
+
+failed:
+ GST_ERROR_OBJECT(src, "second video start failed.");
+ return FALSE;
+}
+
+GstFlowReturn
+gst_v4l2camsrc_create_second_buffer(GstCameraSrc *base, GstBuffer **buf)
+{
+ GstMFLDV4l2CamSrc *src = GST_V4L2CAMSRC_CAST (base);
+ gint fd = src->second_video_fd;
+ struct v4l2_buffer v4l2_buffer;
+ GstV4l2Buffer *pool_buf;
+ guint retry = 5;
+ gint index;
+ gint ret;
+
+ gst_v4l2_buffer_pool_update (src->second_poll, src, src->second_num_buffers);
+
+ for (;;) {
+ ret = gst_poll_wait (src->second_poll, V4L2CAMSRC_POLL_TIMEOUT);
+ if (G_UNLIKELY (ret < 0)) {
+ if (errno == EBUSY)
+ goto failed;
+ if (errno != EAGAIN && errno != EINTR)
+ goto failed;
+ }
+
+ if (G_UNLIKELY (ret == 0)) {
+ goto timeout;
+ }
+ //g_mutex_lock (src->device_mutex);
+ memset(&v4l2_buffer, 0, sizeof(v4l2_buffer));
+ v4l2_buffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ v4l2_buffer.memory = V4L2_MEMORY_USERPTR;
+ ret = ioctl(fd, VIDIOC_DQBUF, &v4l2_buffer);
+ //g_mutex_unlock (src->device_mutex);
+
+ if (ret >= 0)
+ break;
+
+ GST_WARNING_OBJECT (src,
+ "problem grabbing frame %d (ix=%d), trials=%d, pool-ct=%d, buf.flags=%d",
+ v4l2_buffer.sequence, v4l2_buffer.index, retry,
+ GST_MINI_OBJECT_REFCOUNT (src->pool), v4l2_buffer.flags);
+
+ /* if the sync() got interrupted, we can retry */
+ switch (errno) {
+ case EAGAIN:
+ GST_WARNING_OBJECT (src,
+ "Non-blocking I/O has been selected using O_NONBLOCK and"
+ " no buffer was in the outgoing queue. device /dev/video1");
+ break;
+ case EINVAL:
+ goto failed;
+ case ENOMEM:
+ goto failed;
+ case EIO:
+ GST_INFO_OBJECT (src,
+ "VIDIOC_DQBUF failed due to an internal error."
+ " Can also indicate temporary problems like signal loss."
+ " Note the driver might dequeue an (empty) buffer despite"
+ " returning an error, or even stop capturing."
+ " device /dev/video1");
+ /* have we de-queued a buffer ? */
+ break;
+ case EINTR:
+ GST_WARNING_OBJECT (src,
+ "could not sync on a buffer on device /dev/video1");
+ break;
+ default:
+ GST_WARNING_OBJECT (src,
+ "Grabbing frame got interrupted on /dev/video1. No expected reason.");
+ break;
+ }
+
+ if (++retry > 5)
+ goto failed;
+ }
+
+ index = v4l2_buffer.index;
+
+ g_mutex_lock (src->second_pool->lock);
+
+ pool_buf = src->second_pool->buffers[index];
+
+ if (G_UNLIKELY (pool_buf == NULL))
+ goto failed;
+
+ //GST_LOG_OBJECT (src, "grabbed buffer %p at index %d (refct = %d)",
+ // pool_buffer, index, GST_MINI_OBJECT_REFCOUNT (pool_buffer));
+
+ /* ref the buffer and requeue, when if becomes writable again */
+ src->second_pool->buffers[index] = NULL;
+ src->second_pool->num_live_buffers++;
+ src->second_pool->queued[index] = 0;
+
+ g_mutex_unlock (src->second_pool->lock);
+
+ GST_BUFFER_SIZE (pool_buf) = src->second_frame_byte_size;
+
+ if (src->second_pool->is_vaapi_sharing)
+ GST_BUFFER_DATA(pool_buf) = (void*)((GST_V4L2CAMSRC_BUFFER(pool_buf))->gbuffer);
+ *buf = GST_BUFFER_CAST(pool_buf);
+
+ return GST_FLOW_OK;
+
+timeout:
+failed:
+ return GST_FLOW_ERROR;
+}
\ No newline at end of file