From: Wind Yuan Date: Tue, 2 Apr 2013 05:27:15 +0000 (+0800) Subject: dual-stream: implement second stream of /dev/video1 X-Git-Tag: 2.1b_release~3^2~6 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=1218ec1c332abca7156a303af5e21dd9804bb37b;p=adaptation%2Fintel_mfld%2Fgst-plugins-atomisp.git dual-stream: implement second stream of /dev/video1 Change-Id: I822526d1383e33a8aa805d1d48ec61a026fee063 --- diff --git a/gst-libs/gst/camera/gstmfldcamerasrc.c b/gst-libs/gst/camera/gstmfldcamerasrc.c index 995b88a..2e9cc46 100644 --- a/gst-libs/gst/camera/gstmfldcamerasrc.c +++ b/gst-libs/gst/camera/gstmfldcamerasrc.c @@ -299,9 +299,9 @@ static gboolean gst_camerasrc_query (GstBaseSrc * bsrc, GstQuery * query); 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); @@ -322,6 +322,17 @@ static gboolean gst_camerasrc_send_event (GstElement * element, 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 @@ -416,6 +427,211 @@ int gst_camerasrc_send_af_status(GstCameraSrc *camsrc , int state) 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 @@ -583,14 +799,22 @@ gst_camerasrc_class_init (GstCameraSrcClass * klass) 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); } /* @@ -598,6 +822,9 @@ gst_camerasrc_class_init (GstCameraSrcClass * klass) 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; @@ -644,6 +871,36 @@ gst_camerasrc_init (GstCameraSrc * camerasrc, GstCameraSrcClass * klass) 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; } /* @@ -665,6 +922,14 @@ gst_camerasrc_dispose (GObject * object) 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); @@ -859,13 +1124,13 @@ gst_camerasrc_get_property (GObject * object, /* 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; @@ -900,13 +1165,19 @@ gst_camerasrc_fixate (GstBaseSrc * basesrc, GstCaps * caps) } } - 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; @@ -917,16 +1188,16 @@ gst_camerasrc_negotiate (GstBaseSrc * basesrc) 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; @@ -936,7 +1207,7 @@ gst_camerasrc_negotiate (GstBaseSrc * basesrc) /* 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); @@ -948,7 +1219,7 @@ gst_camerasrc_negotiate (GstBaseSrc * basesrc) 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 */ @@ -1008,8 +1279,8 @@ gst_camerasrc_negotiate (GstBaseSrc * basesrc) 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 @@ -1017,8 +1288,8 @@ gst_camerasrc_negotiate (GstBaseSrc * basesrc) 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); @@ -1027,13 +1298,23 @@ gst_camerasrc_negotiate (GstBaseSrc * basesrc) 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); +} /* */ @@ -1374,6 +1655,11 @@ gst_camerasrc_init_from_caps (GstCameraSrc * camerasrc, GstCaps * caps) 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; @@ -1480,6 +1766,10 @@ gst_camerasrc_stop (GstBaseSrc * src) 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; } diff --git a/gst-libs/gst/camera/gstmfldcamerasrc.h b/gst-libs/gst/camera/gstmfldcamerasrc.h index 0d467d4..2632fe3 100644 --- a/gst-libs/gst/camera/gstmfldcamerasrc.h +++ b/gst-libs/gst/camera/gstmfldcamerasrc.h @@ -187,6 +187,7 @@ typedef struct GstCameraSrc3a_window aaa_window; } GstCameraControlInt; +#define GST_CAMERA_SRC_SECOND_PAD(obj) (GST_CAMERA_SRC_CAST(obj)->second_pad) /** * GstCameraSrc: @@ -247,8 +248,21 @@ struct _GstCameraSrc { guint8 requested_af_mode; gboolean maker_note; gboolean enable_torch; -}; + /* Private: second pad*/ + GstPad *second_pad; + GstCaps *second_caps; + gboolean second_started; + gboolean second_stoped; + /* sync up 1st and 2nd streams */ + GMutex *second_lock; + GCond *first_done_cond; + gboolean first_started; + guint second_w, second_h; /* second stream capture frame size */ + guint second_fps_n, second_fps_d; /* second stream framerate if device is open */ + guint32 second_fourcc; /* second stream color format */ + GstClockTime second_duration; /* second stream duration of one frame */ +}; /** * GstCameraSrcClass: @@ -334,6 +348,23 @@ struct _GstCameraSrcClass gboolean (*event) (GstCameraSrc *camsrc, GstEvent *event); + /* second pad functions */ + /* from active push */ + gboolean (*open_second) (GstCameraSrc *camsrc); + gboolean (*is_second_open) (GstCameraSrc *camsrc); + gboolean (*close_second) (GstCameraSrc *camsrc); + + /* from task loop */ + GstFlowReturn (*create_second_buffer) (GstCameraSrc *camsrc, GstBuffer **buf); + + /* from set caps */ + gboolean (*set_second_capture_mode) (GstCameraSrc *camsrc); + gboolean (*set_second_capture) (GstCameraSrc *camsrc, + guint32 *pixelformat, + guint *width, guint32 *height, + guint *fps_n, guint *fps_d); + gboolean (*start_second) (GstCameraSrc *camsrc, GstCaps *caps); + /* FORMER DRIVER-API */ GstPhotoCaps diff --git a/gst/mfldv4l2cam/v4l2camsrc_calls.c b/gst/mfldv4l2cam/v4l2camsrc_calls.c index d4e673c..18d3551 100644 --- a/gst/mfldv4l2cam/v4l2camsrc_calls.c +++ b/gst/mfldv4l2cam/v4l2camsrc_calls.c @@ -52,6 +52,8 @@ 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[] = { @@ -235,16 +237,17 @@ gst_v4l2camsrc_buffer_get_type (void) 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 = @@ -254,18 +257,18 @@ gst_v4l2camsrc_buffer_new (GstMFLDV4l2CamSrcBufferPool * 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); @@ -274,7 +277,7 @@ gst_v4l2camsrc_buffer_new (GstMFLDV4l2CamSrcBufferPool * pool, 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 */ @@ -288,8 +291,8 @@ gst_v4l2camsrc_buffer_new (GstMFLDV4l2CamSrcBufferPool * pool, "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); @@ -297,19 +300,19 @@ gst_v4l2camsrc_buffer_new (GstMFLDV4l2CamSrcBufferPool * pool, 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; @@ -325,20 +328,20 @@ gst_v4l2camsrc_buffer_new (GstMFLDV4l2CamSrcBufferPool * pool, } } - 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); @@ -453,6 +456,7 @@ gst_v4l2camsrc_buffer_pool_init (GstMFLDV4l2CamSrcBufferPool * pool, pool->num_live_buffers = 0; pool->data_cond = g_cond_new (); pool->is_vaapi_sharing = FALSE; + pool->frame_byte_size = 0; } /* @@ -498,10 +502,11 @@ gst_v4l2camsrc_buffer_pool_get_type (void) /* */ 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; @@ -516,12 +521,15 @@ gst_v4l2camsrc_buffer_pool_new (GstCameraSrc * camsrc, gint fd, 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; @@ -589,7 +597,7 @@ queue_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; } @@ -600,7 +608,7 @@ queue_failed: * 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; @@ -615,14 +623,14 @@ gst_v4l2_buffer_pool_update (GstMFLDV4l2CamSrcBufferPool * pool, } /* 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; } /* @@ -657,7 +665,7 @@ gst_v4l2camsrc_buffer_pool_destroy (GstMFLDV4l2CamSrcBufferPool * pool, 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. */ @@ -2185,7 +2193,7 @@ gst_v4l2camsrc_grab_frame (GstCameraSrc * camsrc, GstBuffer ** buf, /* 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)); @@ -2640,7 +2648,10 @@ gst_v4l2camsrc_capture_start (GstCameraSrc * camsrc, GstCaps * caps) 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"); @@ -3850,3 +3861,353 @@ gst_v4l2camsrc_libmfldcam_deinit (GstMFLDV4l2CamSrc * v4l2camsrc) 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 diff --git a/gst/mfldv4l2cam/v4l2camsrc_calls.h b/gst/mfldv4l2cam/v4l2camsrc_calls.h index e20a6ed..70cfcea 100644 --- a/gst/mfldv4l2cam/v4l2camsrc_calls.h +++ b/gst/mfldv4l2cam/v4l2camsrc_calls.h @@ -75,6 +75,10 @@ return FALSE; \ } +#define GST_V4L2CAMSRC_IS_SECOND_OPEN(v4l2camsrc) \ + (v4l2camsrc->second_video_fd != -1) + + #define GST_V4L2CAMSRC_MAX_BUFFERS 16 #define GST_V4L2CAMSRC_MIN_BUFFERS 1 @@ -138,4 +142,24 @@ gst_v4l2camsrc_libmfldcam_get_makernote (GstMFLDV4l2CamSrc * v4l2camsrc, unsigne gboolean gst_v4l2camsrc_libmfldcam_get_focus_posi(GstMFLDV4l2CamSrc * v4l2camsrc, unsigned *posi); +gboolean +gst_v4l2camsrc_open_second(GstCameraSrc *base); + +gboolean +gst_v4l2camsrc_close_second(GstCameraSrc *base); + +gboolean +gst_v4l2camsrc_set_second_capture_mode(GstCameraSrc *base); + +gboolean +gst_v4l2camsrc_set_second_capture(GstCameraSrc *base, guint32 *pixelformat, + guint *width, guint32 *height, + guint *fps_n, guint *fps_d); + +gboolean +gst_v4l2camsrc_start_second(GstCameraSrc *base, GstCaps *caps); + +GstFlowReturn +gst_v4l2camsrc_create_second_buffer(GstCameraSrc *camsrc, GstBuffer **buf); + #endif /* __V4L2CAMSRC_CALLS_H__ */