58 #include "rnr/rnrconfig.h" 64 #include "opencv/cv.h" 65 #include "opencv/highgui.h" 67 #include <gst/interfaces/xoverlay.h> 82 CameraGst::CameraGst(
const std::string &strVideoDevName,
85 Camera(strVideoDevName, resVideo, resImage)
95 g_object_unref(G_OBJECT(m_pPipeline));
97 if( m_bufTmpName[0] != 0 )
131 LOGDIAG3(
"Camera already started with target resolution.");
145 LOGDIAG3(
"Video capture already stoped.");
151 LOGDIAG3(
"Video capturing stopped.");
161 LOGERROR(
"No video capture pipeline started.");
177 uint_t usecSleep = 10000;
178 uint_t usecMax = 10000000;
184 LOGDIAG3(
"Image capture already in progress.");
212 m_nSignalId = g_signal_connect(G_OBJECT(m_pElemImgSink),
220 for(usec = 0; (usec < usecMax) && (m_nSignalId != 0); usec += usecSleep)
222 gtk_main_iteration_do(FALSE);
224 if( m_nSignalId != 0 )
231 if( m_nSignalId != 0 )
235 LOGERROR(
"Timedout taking still image.");
242 img = imread(m_bufTmpName, CV_LOAD_IMAGE_COLOR);
243 LOGDIAG3(
"Took a %dx%d still image.", img.cols, img.rows);
277 const GstStructure *structure;
278 int width, height, stride;
280 const int bits_per_pixel = 8;
283 caps = gst_buffer_get_caps(buffer);
284 structure = gst_caps_get_structure(caps, 0);
286 gst_structure_get_int(structure,
"width", &width);
287 gst_structure_get_int(structure,
"height", &height);
289 stride = buffer->size / height;
301 data = (guchar *)g_memdup(GST_BUFFER_DATA(buffer), buffer->size);
304 pixbuf = gdk_pixbuf_new_from_data(data? data: GST_BUFFER_DATA(buffer),
306 FALSE, bits_per_pixel, width, height, stride,
307 data? (GdkPixbufDestroyNotify)g_free: NULL, NULL);
314 gdk_pixbuf_save(pixbuf, pCam->
m_bufTmpName,
"jpeg", NULL, NULL);
315 g_object_unref(G_OBJECT(pixbuf));
339 caps = gst_caps_new_simple(
"video/x-raw-yuv",
340 "width", G_TYPE_INT, res.
width,
341 "height", G_TYPE_INT, res.
height,
344 g_object_set(G_OBJECT(m_pElemCamFilter),
"caps", caps, NULL);
345 gst_caps_unref(caps);
368 gst_element_set_state(m_pPipeline, GST_STATE_PLAYING);
377 gst_element_set_state(m_pPipeline, GST_STATE_NULL);
384 const char *sFactoryName;
385 GstElement *pElemVidSrc;
391 m_pBinCameraSrc = NULL;
392 m_pElemCamFilter = NULL;
393 m_pBinVideoSink = NULL;
394 m_pElemVidText = NULL;
395 m_pBinImageSink = NULL;
396 m_pElemImgSink = NULL;
400 gst_init(NULL, NULL);
402 m_pPipeline = gst_pipeline_new(
"pipeline");
404 if( m_pPipeline == NULL )
406 LOGERROR(
"Failed to create new gstreamer pipeline.");
411 if( (rc = makeCameraSrcBin()) < 0 )
413 LOGERROR(
"Failed to create gstreamer camera source bin.");
418 if( (rc = makeVideoSinkBin()) < 0 )
420 LOGERROR(
"Failed to create gstreamer video display sink bin.");
425 if( (rc = makeImageSinkBin()) < 0 )
427 LOGERROR(
"Failed to create gstreamer still image sink bin.");
432 gst_bin_add_many(GST_BIN(m_pPipeline),
438 ok = gst_element_link_many(
446 LOGERROR(
"Failed to link gstreamer pipeline.");
454 bus = gst_pipeline_get_bus(GST_PIPELINE(m_pPipeline));
455 gst_bus_add_signal_watch(bus);
456 g_signal_connect(G_OBJECT(bus),
"message", G_CALLBACK(gstBusMsgCb),
this);
457 gst_bus_set_sync_handler(bus, (GstBusSyncHandler)gstBusSyncHandler,
this);
458 gst_object_unref(bus);
465 const char *sFactoryName;
466 GstElement *pElemCamSrc;
467 GstElement *pElemCamIdentity;
472 m_pBinCameraSrc = gst_bin_new(
"camera_bin");
474 if( m_pBinCameraSrc == NULL )
476 LOGERROR(
"Failed to create new gstreamer camera bin.");
484 sFactoryName =
"v4l2src";
485 pElemCamSrc = gst_element_factory_make(sFactoryName,
"cam_src");
487 if( pElemCamSrc == NULL )
489 LOGERROR(
"Failed to create gstreamer element: %s.", sFactoryName);
494 g_object_set(G_OBJECT(pElemCamSrc),
501 sFactoryName =
"capsfilter";
502 m_pElemCamFilter = gst_element_factory_make(sFactoryName,
"cam_caps");
504 if( m_pElemCamFilter == NULL )
506 LOGERROR(
"Failed to create gstreamer element: %s.", sFactoryName);
511 caps = gst_caps_new_simple(
"video/x-raw-yuv",
516 g_object_set(G_OBJECT(m_pElemCamFilter),
"caps", caps, NULL);
517 gst_caps_unref(caps);
524 sFactoryName =
"identity";
525 pElemCamIdentity = gst_element_factory_make(sFactoryName,
"cam_identity");
527 if( pElemCamIdentity == NULL )
529 LOGERROR(
"Failed to create gstreamer element: %s.", sFactoryName);
534 gst_bin_add_many(GST_BIN(m_pBinCameraSrc),
540 ok = gst_element_link_many(
548 LOGERROR(
"Failed to link camera gstreamer elements.");
556 pad = gst_element_get_pad(pElemCamIdentity,
"src");
557 gst_element_add_pad(m_pBinCameraSrc, gst_ghost_pad_new(
"src", pad));
558 gst_object_unref(GST_OBJECT(pad));
565 const char *sFactoryName;
566 GstElement *pElemVidIdentity;
567 GstElement *pElemVidTee;
568 GstElement *pElemVidSaveQueue;
569 GstElement *pElemVidDispQueue;
570 GstElement *pElemVidScale;
571 GstElement *pElemVidFilter;
572 GstElement *pElemVidFfmpeg;
577 m_pBinVideoSink = gst_bin_new(
"video_bin");
579 if( m_pBinVideoSink == NULL )
581 LOGERROR(
"Failed to create new gstreamer video bin.");
589 sFactoryName =
"tee";
590 pElemVidTee = gst_element_factory_make(sFactoryName,
"vid_tee");
592 if( pElemVidTee == NULL )
594 LOGERROR(
"Failed to create gstreamer element: %s.", sFactoryName);
602 sFactoryName =
"queue";
603 pElemVidSaveQueue = gst_element_factory_make(sFactoryName,
"vid_save_queue");
605 if( pElemVidSaveQueue == NULL )
607 LOGERROR(
"Failed to create gstreamer element: %s.", sFactoryName);
612 g_object_set(G_OBJECT(pElemVidSaveQueue),
613 "max-size-buffers", 2,
619 sFactoryName =
"queue";
620 pElemVidDispQueue = gst_element_factory_make(sFactoryName,
"vid_disp_queue");
622 if( pElemVidDispQueue == NULL )
624 LOGERROR(
"Failed to create gstreamer element: %s.", sFactoryName);
632 sFactoryName =
"videoscale";
633 pElemVidScale = gst_element_factory_make(sFactoryName,
"vid_scale");
635 if( pElemVidScale == NULL )
637 LOGERROR(
"Failed to create gstreamer element: %s.", sFactoryName);
643 g_object_set(pElemVidScale,
"method", 1, NULL);
648 sFactoryName =
"capsfilter";
649 pElemVidFilter = gst_element_factory_make(sFactoryName,
"vid_caps");
651 if( pElemVidFilter == NULL )
653 LOGERROR(
"Failed to create gstreamer element: %s.", sFactoryName);
658 caps = gst_caps_new_simple(
"video/x-raw-yuv",
659 "width", G_TYPE_INT, 320,
660 "height", G_TYPE_INT, 240,
663 g_object_set(G_OBJECT(pElemVidFilter),
"caps", caps, NULL);
664 gst_caps_unref(caps);
669 sFactoryName =
"textoverlay";
670 m_pElemVidText = gst_element_factory_make(sFactoryName,
"vid_text");
672 if( m_pElemVidText == NULL )
674 LOGERROR(
"Failed to create gstreamer element: %s.", sFactoryName);
680 g_object_set(m_pElemVidText,
682 "font-desc",
"Aria 32",
688 sFactoryName =
"ffmpegcolorspace";
689 pElemVidFfmpeg = gst_element_factory_make(sFactoryName,
"vid_color");
691 if( pElemVidFfmpeg == NULL )
693 LOGERROR(
"Failed to create gstreamer element: %s.", sFactoryName);
701 sFactoryName =
"gconfvideosink";
702 m_pElemVidSink = gst_element_factory_make(sFactoryName,
"vid_sink");
704 if( m_pElemVidSink == NULL )
706 LOGERROR(
"Failed to create gstreamer element: %s.", sFactoryName);
711 gst_bin_add_many(GST_BIN(m_pBinVideoSink),
722 ok = gst_element_link_many(
729 LOGERROR(
"Failed to link video tee save gstreamer elements.");
734 ok = gst_element_link_many(
746 LOGERROR(
"Failed to link video tee display gstreamer elements.");
754 pad = gst_element_get_pad(pElemVidTee,
"sink");
755 gst_element_add_pad(m_pBinVideoSink, gst_ghost_pad_new(
"sink", pad));
756 gst_object_unref(GST_OBJECT(pad));
761 pad = gst_element_get_pad(pElemVidSaveQueue,
"src");
762 gst_element_add_pad(m_pBinVideoSink, gst_ghost_pad_new(
"src", pad));
763 gst_object_unref(GST_OBJECT(pad));
770 const char *sFactoryName;
771 GstElement *pElemImgFfmpeg;
776 m_pBinImageSink = gst_bin_new(
"image_bin");
778 if( m_pBinImageSink == NULL )
780 LOGERROR(
"Failed to create new gstreamer image bin.");
788 sFactoryName =
"ffmpegcolorspace";
789 pElemImgFfmpeg = gst_element_factory_make(sFactoryName,
"img_color");
791 if( pElemImgFfmpeg == NULL )
793 LOGERROR(
"Failed to create gstreamer element: %s.", sFactoryName);
801 sFactoryName =
"fakesink";
802 m_pElemImgSink = gst_element_factory_make(sFactoryName,
"img_sink");
804 if( m_pElemImgSink == NULL )
806 LOGERROR(
"Failed to create gstreamer element: %s.", sFactoryName);
811 g_object_set(G_OBJECT(m_pElemImgSink),
812 "signal-handoffs", TRUE,
818 gst_bin_add_many(GST_BIN(m_pBinImageSink),
823 caps = gst_caps_new_simple(
"video/x-raw-rgb",
824 "bpp", G_TYPE_INT, 24,
825 "depth", G_TYPE_INT, 24,
827 ok = gst_element_link_filtered(pElemImgFfmpeg, m_pElemImgSink, caps);
831 LOGERROR(
"Failed to set filtered link.");
836 gst_caps_unref(caps);
842 pad = gst_element_get_pad(pElemImgFfmpeg,
"sink");
843 gst_element_add_pad(m_pBinImageSink, gst_ghost_pad_new(
"sink", pad));
844 gst_object_unref(GST_OBJECT(pad));
850 void CameraGst::changeSink(GstElement *pBinNewSink)
852 GstElement *pBinOldSink;
853 GstElement *pBinNewSink;
855 if( eNewMode == m_eCameraMode )
860 gst_element_set_state(m_pPipeline, GST_STATE_NULL);
862 if( eNewMode == CameraModePhoto )
864 pBinOldSink = m_pBinVideoSink;
865 pBinNewSink = m_pBinImageSink;
869 pBinOldSink = m_pBinImageSink;
870 pBinNewSink = m_pBinVideoSink;
873 gst_element_unlink(m_pBinCameraSrc, pBinOldSink);
874 gst_object_ref(pBinOldSink);
875 gst_bin_remove(GST_BIN(m_pPipeline), pBinOldSink);
877 gst_bin_add(GST_BIN(m_pPipeline), pBinNewSink);
878 gst_element_link(m_pBinCameraSrc, pBinNewSink);
880 m_eCameraMode = eNewMode;
882 LOGDIAG3(
"%s Mode.", (m_eCameraMode == CameraModePhoto?
"Photo":
"Video"));
892 switch( GST_MESSAGE_TYPE(message) )
894 case GST_MESSAGE_EOS:
895 LOGDIAG3(
"GST: End of stream.");
898 case GST_MESSAGE_ERROR:
903 gst_message_parse_error(message, &error, &debug);
905 LOGERROR(
"GST: %s.", error->message);
916 GstBusSyncReply CameraGst::gstBusSyncHandler(GstBus *bus,
922 if( GST_MESSAGE_TYPE(message) == GST_MESSAGE_EOS )
924 LOGDIAG3(
"GST: Sync: End of stream.");
929 if( GST_MESSAGE_TYPE(message) != GST_MESSAGE_ELEMENT )
934 if( !gst_structure_has_name(message->structure,
"prepare-xwindow-id") )
941 GstXOverlay *xoverlay;
943 xoverlay = GST_X_OVERLAY(GST_MESSAGE_SRC(message));
945 if( g_object_class_find_property(G_OBJECT_GET_CLASS(xoverlay),
946 "force-aspect-ratio") )
948 g_object_set(G_OBJECT(xoverlay),
"force-aspect-ratio", TRUE, NULL);
952 gst_x_overlay_set_xwindow_id(xoverlay, pCam->
m_uVidWinXid);
958 LOGERROR(
"Should have obtained video_window_xid by now!");
961 gst_message_unref(message);
967 gint64 CameraGst::onBlockVideoSelector(GstElement *element,
974 gint64 CameraGst::onSwitchVideoSelector(GstElement *element,
987 string strX(
"XXXXXX");
988 string strSuffix(
".jpg");
991 sprintf(m_bufTmpName,
"/tmp/camegst-%s%s", strX.c_str(), strSuffix.c_str());
994 if( (fd = mkstemps(m_bufTmpName, strSuffix.length())) < 0 )
996 LOGERROR(
"mkstemps(%s, %zu) failed.\n", m_bufTmpName, strSuffix.length());
1004 sprintf(tmp,
"/tmp/camgst-%s", strX.c_str());
1006 if( (fd = mkstemp(tmp)) < 0 )
1008 LOGERROR(
"mkstemp(%s) failed.\n", tmp);
1009 m_bufTmpName[0] = 0;
1015 rename(tmp, m_bufTmpName);
void stopPipeline()
Stop the camera and the pipeline.
virtual ~CameraGst()
Destructor.
static bool isEqResolution(const CamRes &res1, const CamRes &res2)
Check is two camera resolutions are equal.
int makePipeline()
Make gstreamer video pipeline.
CamRes m_resCurrent
current camera resolution
virtual int startVideo(const CamRes &resVideo=CamResDft)
Start the camera streaming video.
Video and still image camera base class.
GstElement * m_pElemImgSink
still image sink
const CamRes CamResDft
default resolution
gulong m_uVidWinXid
overlay window X-windows id
bool m_bTakingImage
taking an image is [not] finished
CamRes m_resVideo
current video resolution
int makeCameraSrcBin()
Video source bin.
bool m_bCameraRunning
camera is [not] on and running video
virtual void autoFocus()
Auto-focus camera.
char m_bufTmpName[PATH_MAX]
temporary file name buffer
virtual int clickImage(cv::Mat &img, const CamRes &resImage=CamResDft)
Take a still image.
virtual CamRes setCameraResolution(const CamRes &res)
Set the camera resolution in either video or still image mode.
virtual CamRes setCameraResolution(const CamRes &res)
Set the camera resolution in either video or still image mode.
virtual int stopVideo()
Stop the camera from streaming video.
Camera resolution structure.
int m_nSignalId
camera still image callback signal
void makeTmpFile()
Make unique temporary file.
virtual int startVideo(const CamRes &resVideo=CamResDft)
Start the camera streaming video.
void setXid(gulong uXid)
Set X identifier associated with widget receiving streaming video.
GStreamer implementation of the camera class. The video is streamed via a Gstreamer/GTK callback mech...
int height
height in pixels
CamRes m_resImage
current still image resolution
virtual int clickImage(cv::Mat &img, const CamRes &resImage=CamResDft)
Take a still image.
std::string m_strVideoDevName
video device name
int makeVideoSinkBin()
Video sink bin.
bool isCameraRunning() const
Test if the camera is on and running.
void startPipeline()
Start the camera and the pipeline.
int makeImageSinkBin()
Image sink bin.
virtual int grabFrame(cv::Mat &frame)
Grab a image frame from the video stream.
static void gstBusMsgCb(GstBus *bus, GstMessage *message, gpointer user_data)
Gstream pipeline synchronous bus handler.
virtual int stopVideo()
Stop the camera from streaming video.
static void clickCb(GstElement *element, GstBuffer *buffer, GstPad *pad, void *user_data)
Still image capture asynchronouse callback.
Gstreamer video and still image camera class.
bool m_bFatal
camera instance is in a fatal state