appkit  1.5.1
RoadNarrows Robotics Application Kit
CameraGst.cxx
Go to the documentation of this file.
1 ////////////////////////////////////////////////////////////////////////////////
2 //
3 // Package: RoadNarrows Robotics Application Tool Kit
4 //
5 // Library: librnr_cam
6 //
7 // File: CameraGst.cxx
8 //
9 /*! \file
10  *
11  * $LastChangedDate: 2013-07-15 11:45:50 -0600 (Mon, 15 Jul 2013) $
12  * $Rev: 3131 $
13  *
14  * \brief GStreamer video and still image camera class implementation.
15  *
16  * \author Robin Knight (robin.knight@roadnarrows.com)
17  * \author Daniel Packard (daniel@roadnarrows.com)
18  *
19  * \par Copyright
20  * \h_copy 2012-2018. RoadNarrows LLC.\n
21  * http://www.roadnarrows.com\n
22  * All Rights Reserved
23  */
24 /*
25  * @EulaBegin@
26  *
27  * Permission is hereby granted, without written agreement and without
28  * license or royalty fees, to use, copy, modify, and distribute this
29  * software and its documentation for any purpose, provided that
30  * (1) The above copyright notice and the following two paragraphs
31  * appear in all copies of the source code and (2) redistributions
32  * including binaries reproduces these notices in the supporting
33  * documentation. Substantial modifications to this software may be
34  * copyrighted by their authors and need not follow the licensing terms
35  * described here, provided that the new terms are clearly indicated in
36  * all files where they apply.
37  *
38  * IN NO EVENT SHALL THE AUTHOR, ROADNARROWS LLC, OR ANY MEMBERS/EMPLOYEES
39  * OF ROADNARROW LLC OR DISTRIBUTORS OF THIS SOFTWARE BE LIABLE TO ANY
40  * PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL
41  * DAMAGES ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION,
42  * EVEN IF THE AUTHORS OR ANY OF THE ABOVE PARTIES HAVE BEEN ADVISED OF
43  * THE POSSIBILITY OF SUCH DAMAGE.
44  *
45  * THE AUTHOR AND ROADNARROWS LLC SPECIFICALLY DISCLAIM ANY WARRANTIES,
46  * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
47  * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON AN
48  * "AS IS" BASIS, AND THE AUTHORS AND DISTRIBUTORS HAVE NO OBLIGATION TO
49  * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
50  *
51  * @EulaEnd@
52  */
53 ////////////////////////////////////////////////////////////////////////////////
54 
55 #include <stdio.h>
56 #include <stdlib.h>
57 
58 #include "rnr/rnrconfig.h"
59 #include "rnr/log.h"
60 
61 #include "rnr/appkit/Camera.h"
62 #include "rnr/appkit/CameraGst.h"
63 
64 #include "opencv/cv.h"
65 #include "opencv/highgui.h"
66 
67 #include <gst/interfaces/xoverlay.h>
68 #include <gst/gst.h>
69 
70 #include <gtk/gtk.h>
71 #include <gdk/gdkx.h>
72 
73 using namespace std;
74 using namespace rnr;
75 using namespace cv;
76 
77 
78 //-----------------------------------------------------------------------------
79 // GStreamer Derived Camera Class
80 //-----------------------------------------------------------------------------
81 
82 CameraGst::CameraGst(const std::string &strVideoDevName,
83  const CamRes &resVideo,
84  const CamRes &resImage) :
85  Camera(strVideoDevName, resVideo, resImage)
86 {
87  makePipeline();
88  makeTmpFile();
89 }
90 
92 {
93  stopVideo();
94 
95  g_object_unref(G_OBJECT(m_pPipeline));
96 
97  if( m_bufTmpName[0] != 0 )
98  {
99  remove(m_bufTmpName);
100  }
101 }
102 
103 int CameraGst::startVideo(const CamRes &resVideo)
104 {
105  CamRes res = resVideo;
106 
107  // default is the current video resolution
108  if( isEqResolution(res, CamResDft) )
109  {
110  res = m_resVideo;
111  }
112 
113  // start video with the given resolution
114  if( !isCameraRunning() )
115  {
116  setCameraResolution(res);
117  startPipeline();
118  }
119 
120  // resolution is different from current setting
121  else if( !isEqResolution(res, m_resCurrent) )
122  {
123  stopPipeline();
124  setCameraResolution(res);
125  startPipeline();
126  }
127 
128  // camera already running at target resolution
129  else
130  {
131  LOGDIAG3("Camera already started with target resolution.");
132  }
133 
135 
136  LOGDIAG3("Capturing %dx%d video.", m_resVideo.width, m_resVideo.height);
137 
138  return OK;
139 }
140 
142 {
143  if( !isCameraRunning() )
144  {
145  LOGDIAG3("Video capture already stoped.");
146  }
147 
148  else
149  {
150  stopPipeline();
151  LOGDIAG3("Video capturing stopped.");
152  }
153 
154  return OK;
155 }
156 
157 int CameraGst::grabFrame(Mat &frame)
158 {
159  if( !isCameraRunning() )
160  {
161  LOGERROR("No video capture pipeline started.");
162  return RC_ERROR;
163  }
164 
165  // grab a frame
166  else
167  {
168  return clickImage(frame, m_resVideo);
169  }
170 }
171 
172 int CameraGst::clickImage(Mat &img, const CamRes &resImage)
173 {
174  CamRes resVideoPrev = m_resVideo;
175  bool bCamWasRunning = isCameraRunning();
176  CamRes res = resImage;
177  uint_t usecSleep = 10000; // 10 milliseconds
178  uint_t usecMax = 10000000; // 10 seconds
179  uint_t usec;
180  int rc;
181 
182  if( m_bTakingImage || (m_nSignalId != 0) )
183  {
184  LOGDIAG3("Image capture already in progress.");
185  return RC_ERROR;
186  }
187 
188  m_bTakingImage = true;
189 
190  // default is the current image resolution
191  if( isEqResolution(res, CamResDft) )
192  {
193  res = m_resImage;
194  }
195 
196  // new resolution
197  if( !isEqResolution(res, m_resCurrent) )
198  {
199  stopPipeline();
200  setCameraResolution(res);
201  }
202 
203  // start camera
204  if( !isCameraRunning() )
205  {
206  startPipeline();
207  }
208 
209  m_resImage = res;
210 
211  // take the photo by connecting the handoff signal
212  m_nSignalId = g_signal_connect(G_OBJECT(m_pElemImgSink),
213  "handoff",
214  G_CALLBACK(clickCb),
215  this);
216 
217  //
218  // Block wait with timeout
219  //
220  for(usec = 0; (usec < usecMax) && (m_nSignalId != 0); usec += usecSleep)
221  {
222  gtk_main_iteration_do(FALSE);
223 
224  if( m_nSignalId != 0 )
225  {
226  usleep(usecSleep);
227  }
228  }
229 
230  // timed out
231  if( m_nSignalId != 0 )
232  {
233  // g_signal_handler_disconnect(G_OBJECT(m_pElemImgSink), m_nSignalId);
234  m_nSignalId = 0;
235  LOGERROR("Timedout taking still image.");
236  rc = RC_ERROR;
237  }
238 
239  // got an image
240  else
241  {
242  img = imread(m_bufTmpName, CV_LOAD_IMAGE_COLOR);
243  LOGDIAG3("Took a %dx%d still image.", img.cols, img.rows);
244  rc = OK;
245  }
246 
247  // video was running
248  if( bCamWasRunning )
249  {
250  // restart at different resolution
252  {
254  }
255  // else live running at current resolution
256  }
257 
258  // video only started to take image - stop
259  else
260  {
261  stopVideo();
262  }
263 
264  m_bTakingImage = false;
265 
266  return rc;
267 }
268 
269 void CameraGst::clickCb(GstElement *element,
270  GstBuffer *buffer,
271  GstPad *pad,
272  void *user_data)
273 {
274  CameraGst *pCam = (CameraGst *)user_data;
275 
276  GstCaps *caps;
277  const GstStructure *structure;
278  int width, height, stride;
279  GdkPixbuf *pixbuf;
280  const int bits_per_pixel = 8;
281  guchar *data;
282 
283  caps = gst_buffer_get_caps(buffer);
284  structure = gst_caps_get_structure(caps, 0);
285 
286  gst_structure_get_int(structure, "width", &width);
287  gst_structure_get_int(structure, "height", &height);
288 
289  stride = buffer->size / height;
290 
291  //
292  // Only copy the data if we're giving away a pixbuf, not if we're throwing
293  // everything away straight away
294  //
295  if (pCam->m_bufTmpName[0] != 0 )
296  {
297  data = NULL;
298  }
299  else
300  {
301  data = (guchar *)g_memdup(GST_BUFFER_DATA(buffer), buffer->size);
302  }
303 
304  pixbuf = gdk_pixbuf_new_from_data(data? data: GST_BUFFER_DATA(buffer),
305  GDK_COLORSPACE_RGB,
306  FALSE, bits_per_pixel, width, height, stride,
307  data? (GdkPixbufDestroyNotify)g_free: NULL, NULL);
308 
309  g_signal_handler_disconnect(G_OBJECT(pCam->m_pElemImgSink),
310  pCam->m_nSignalId);
311 
312  if (pCam->m_bufTmpName[0] != 0 )
313  {
314  gdk_pixbuf_save(pixbuf, pCam->m_bufTmpName, "jpeg", NULL, NULL);
315  g_object_unref(G_OBJECT(pixbuf));
316  }
317 
318  pCam->m_resImage.width = width;
319  pCam->m_resImage.height = height;
320 
321  pCam->m_nSignalId = 0;
322 }
323 
325 {
326 }
327 
329 {
330  bool bWasRunning;
331  GstCaps *caps;
332 
333  // camera pipeline must be stopped before applying capabilities
334  if( (bWasRunning = isCameraRunning()) )
335  {
336  stopPipeline();
337  }
338 
339  caps = gst_caps_new_simple("video/x-raw-yuv",
340  "width", G_TYPE_INT, res.width,
341  "height", G_TYPE_INT, res.height,
342  NULL);
343 
344  g_object_set(G_OBJECT(m_pElemCamFilter), "caps", caps, NULL);
345  gst_caps_unref(caps);
346 
347  m_resCurrent = res;
348 
349  if( bWasRunning )
350  {
351  startPipeline();
352  }
353 
354  return m_resCurrent;
355 }
356 
357 void CameraGst::setXid(gulong uXid)
358 {
359  m_uVidWinXid = uXid;
360 
361  //g_object_set(m_pElemVidGenSrc, "xid", m_uVidWinXid, NULL);
362 }
363 
365 {
366  if( !m_bCameraRunning )
367  {
368  gst_element_set_state(m_pPipeline, GST_STATE_PLAYING);
369  m_bCameraRunning = true;
370  }
371 }
372 
374 {
375  if( m_bCameraRunning )
376  {
377  gst_element_set_state(m_pPipeline, GST_STATE_NULL);
378  m_bCameraRunning = false;
379  }
380 }
381 
383 {
384  const char *sFactoryName;
385  GstElement *pElemVidSrc;
386  GstBus *bus;
387  gboolean ok;
388  int rc;
389 
390  m_pPipeline = NULL;
391  m_pBinCameraSrc = NULL;
392  m_pElemCamFilter = NULL;
393  m_pBinVideoSink = NULL;
394  m_pElemVidText = NULL;
395  m_pBinImageSink = NULL;
396  m_pElemImgSink = NULL;
397  m_nSignalId = 0;
398  m_uVidWinXid = 0;
399 
400  gst_init(NULL, NULL);
401 
402  m_pPipeline = gst_pipeline_new("pipeline");
403 
404  if( m_pPipeline == NULL )
405  {
406  LOGERROR("Failed to create new gstreamer pipeline.");
407  m_bFatal = true;
408  return RC_ERROR;
409  }
410 
411  if( (rc = makeCameraSrcBin()) < 0 )
412  {
413  LOGERROR("Failed to create gstreamer camera source bin.");
414  m_bFatal = true;
415  return rc;
416  }
417 
418  if( (rc = makeVideoSinkBin()) < 0 )
419  {
420  LOGERROR("Failed to create gstreamer video display sink bin.");
421  m_bFatal = true;
422  return rc;
423  }
424 
425  if( (rc = makeImageSinkBin()) < 0 )
426  {
427  LOGERROR("Failed to create gstreamer still image sink bin.");
428  m_bFatal = true;
429  return rc;
430  }
431 
432  gst_bin_add_many(GST_BIN(m_pPipeline),
433  m_pBinCameraSrc,
434  m_pBinVideoSink,
435  m_pBinImageSink,
436  NULL);
437 
438  ok = gst_element_link_many(
439  m_pBinCameraSrc,
440  m_pBinVideoSink,
441  m_pBinImageSink,
442  NULL);
443 
444  if( !ok )
445  {
446  LOGERROR("Failed to link gstreamer pipeline.");
447  m_bFatal = true;
448  return RC_ERROR;
449  }
450 
451  //
452  // Set up pipeline bus async message callback and synchronous handler
453  //
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);
459 
460  return OK;
461 }
462 
464 {
465  const char *sFactoryName;
466  GstElement *pElemCamSrc;
467  GstElement *pElemCamIdentity;
468  GstCaps *caps;
469  GstPad *pad;
470  gboolean ok;
471 
472  m_pBinCameraSrc = gst_bin_new("camera_bin");
473 
474  if( m_pBinCameraSrc == NULL )
475  {
476  LOGERROR("Failed to create new gstreamer camera bin.");
477  m_bFatal = true;
478  return RC_ERROR;
479  }
480 
481  //
482  // Source element
483  //
484  sFactoryName = "v4l2src";
485  pElemCamSrc = gst_element_factory_make(sFactoryName, "cam_src");
486 
487  if( pElemCamSrc == NULL )
488  {
489  LOGERROR("Failed to create gstreamer element: %s.", sFactoryName);
490  m_bFatal = true;
491  return RC_ERROR;
492  }
493 
494  g_object_set(G_OBJECT(pElemCamSrc),
495  "device", m_strVideoDevName.c_str(),
496  NULL);
497 
498  //
499  // Filter Element
500  //
501  sFactoryName = "capsfilter";
502  m_pElemCamFilter = gst_element_factory_make(sFactoryName, "cam_caps");
503 
504  if( m_pElemCamFilter == NULL )
505  {
506  LOGERROR("Failed to create gstreamer element: %s.", sFactoryName);
507  m_bFatal = true;
508  return RC_ERROR;
509  }
510 
511  caps = gst_caps_new_simple("video/x-raw-yuv",
512  "width", G_TYPE_INT, m_resVideo.width,
513  "height", G_TYPE_INT, m_resVideo.height,
514  NULL);
515 
516  g_object_set(G_OBJECT(m_pElemCamFilter), "caps", caps, NULL);
517  gst_caps_unref(caps);
518 
520 
521  //
522  // Identity element
523  //
524  sFactoryName = "identity";
525  pElemCamIdentity = gst_element_factory_make(sFactoryName, "cam_identity");
526 
527  if( pElemCamIdentity == NULL )
528  {
529  LOGERROR("Failed to create gstreamer element: %s.", sFactoryName);
530  m_bFatal = true;
531  return RC_ERROR;
532  }
533 
534  gst_bin_add_many(GST_BIN(m_pBinCameraSrc),
535  pElemCamSrc,
536  m_pElemCamFilter,
537  pElemCamIdentity,
538  NULL);
539 
540  ok = gst_element_link_many(
541  pElemCamSrc,
542  m_pElemCamFilter,
543  pElemCamIdentity,
544  NULL);
545 
546  if( !ok )
547  {
548  LOGERROR("Failed to link camera gstreamer elements.");
549  m_bFatal = true;
550  return RC_ERROR;
551  }
552 
553  //
554  // Add souce ghostpad since bins normally do not have pads.
555  //
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));
559 
560  return OK;
561 }
562 
564 {
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;
573  GstCaps *caps;
574  GstPad *pad;
575  gboolean ok;
576 
577  m_pBinVideoSink = gst_bin_new("video_bin");
578 
579  if( m_pBinVideoSink == NULL )
580  {
581  LOGERROR("Failed to create new gstreamer video bin.");
582  m_bFatal = true;
583  return RC_ERROR;
584  }
585 
586  //
587  // Tee element
588  //
589  sFactoryName = "tee";
590  pElemVidTee = gst_element_factory_make(sFactoryName, "vid_tee");
591 
592  if( pElemVidTee == NULL )
593  {
594  LOGERROR("Failed to create gstreamer element: %s.", sFactoryName);
595  m_bFatal = true;
596  return RC_ERROR;
597  }
598 
599  //
600  // Save queue element
601  //
602  sFactoryName = "queue";
603  pElemVidSaveQueue = gst_element_factory_make(sFactoryName, "vid_save_queue");
604 
605  if( pElemVidSaveQueue == NULL )
606  {
607  LOGERROR("Failed to create gstreamer element: %s.", sFactoryName);
608  m_bFatal = true;
609  return RC_ERROR;
610  }
611 
612  g_object_set(G_OBJECT(pElemVidSaveQueue),
613  "max-size-buffers", 2,
614  NULL);
615 
616  //
617  // Video display queue element
618  //
619  sFactoryName = "queue";
620  pElemVidDispQueue = gst_element_factory_make(sFactoryName, "vid_disp_queue");
621 
622  if( pElemVidDispQueue == NULL )
623  {
624  LOGERROR("Failed to create gstreamer element: %s.", sFactoryName);
625  m_bFatal = true;
626  return RC_ERROR;
627  }
628 
629  //
630  // Scale element
631  //
632  sFactoryName = "videoscale";
633  pElemVidScale = gst_element_factory_make(sFactoryName, "vid_scale");
634 
635  if( pElemVidScale == NULL )
636  {
637  LOGERROR("Failed to create gstreamer element: %s.", sFactoryName);
638  m_bFatal = true;
639  return RC_ERROR;
640  }
641 
642  // use bilinear scaling
643  g_object_set(pElemVidScale, "method", 1, NULL);
644 
645  //
646  // Filter Element
647  //
648  sFactoryName = "capsfilter";
649  pElemVidFilter = gst_element_factory_make(sFactoryName, "vid_caps");
650 
651  if( pElemVidFilter == NULL )
652  {
653  LOGERROR("Failed to create gstreamer element: %s.", sFactoryName);
654  m_bFatal = true;
655  return RC_ERROR;
656  }
657 
658  caps = gst_caps_new_simple("video/x-raw-yuv",
659  "width", G_TYPE_INT, 320,
660  "height", G_TYPE_INT, 240,
661  NULL);
662 
663  g_object_set(G_OBJECT(pElemVidFilter), "caps", caps, NULL);
664  gst_caps_unref(caps);
665 
666  //
667  // Text overlay element
668  //
669  sFactoryName = "textoverlay";
670  m_pElemVidText = gst_element_factory_make(sFactoryName, "vid_text");
671 
672  if( m_pElemVidText == NULL )
673  {
674  LOGERROR("Failed to create gstreamer element: %s.", sFactoryName);
675  m_bFatal = true;
676  return RC_ERROR;
677  }
678 
679  // no text
680  g_object_set(m_pElemVidText,
681  "text", "",
682  "font-desc", "Aria 32",
683  NULL);
684 
685  //
686  // Color space element
687  //
688  sFactoryName = "ffmpegcolorspace";
689  pElemVidFfmpeg = gst_element_factory_make(sFactoryName, "vid_color");
690 
691  if( pElemVidFfmpeg == NULL )
692  {
693  LOGERROR("Failed to create gstreamer element: %s.", sFactoryName);
694  m_bFatal = true;
695  return RC_ERROR;
696  }
697 
698  //
699  // Display sink element
700  //
701  sFactoryName = "gconfvideosink";
702  m_pElemVidSink = gst_element_factory_make(sFactoryName, "vid_sink");
703 
704  if( m_pElemVidSink == NULL )
705  {
706  LOGERROR("Failed to create gstreamer element: %s.", sFactoryName);
707  m_bFatal = true;
708  return RC_ERROR;
709  }
710 
711  gst_bin_add_many(GST_BIN(m_pBinVideoSink),
712  pElemVidTee,
713  pElemVidSaveQueue,
714  pElemVidDispQueue,
715  pElemVidScale,
716  pElemVidFilter,
717  m_pElemVidText,
718  pElemVidFfmpeg,
719  m_pElemVidSink,
720  NULL);
721 
722  ok = gst_element_link_many(
723  pElemVidTee,
724  pElemVidSaveQueue,
725  NULL);
726 
727  if( !ok )
728  {
729  LOGERROR("Failed to link video tee save gstreamer elements.");
730  m_bFatal = true;
731  return RC_ERROR;
732  }
733 
734  ok = gst_element_link_many(
735  pElemVidTee,
736  pElemVidDispQueue,
737  pElemVidScale,
738  pElemVidFilter,
739  m_pElemVidText,
740  pElemVidFfmpeg,
741  m_pElemVidSink,
742  NULL);
743 
744  if( !ok )
745  {
746  LOGERROR("Failed to link video tee display gstreamer elements.");
747  m_bFatal = true;
748  return RC_ERROR;
749  }
750 
751  //
752  // Add sink ghostpad since bins normally do not have pads.
753  //
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));
757 
758  //
759  // Add source ghostpad to save stream
760  //
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));
764 
765  return OK;
766 }
767 
769 {
770  const char *sFactoryName;
771  GstElement *pElemImgFfmpeg;
772  GstCaps *caps;
773  GstPad *pad;
774  gboolean ok;
775 
776  m_pBinImageSink = gst_bin_new("image_bin");
777 
778  if( m_pBinImageSink == NULL )
779  {
780  LOGERROR("Failed to create new gstreamer image bin.");
781  m_bFatal = true;
782  return RC_ERROR;
783  }
784 
785  //
786  // Colorspace element
787  //
788  sFactoryName = "ffmpegcolorspace";
789  pElemImgFfmpeg = gst_element_factory_make(sFactoryName, "img_color");
790 
791  if( pElemImgFfmpeg == NULL )
792  {
793  LOGERROR("Failed to create gstreamer element: %s.", sFactoryName);
794  m_bFatal = true;
795  return RC_ERROR;
796  }
797 
798  //
799  // Sink element
800  //
801  sFactoryName = "fakesink";
802  m_pElemImgSink = gst_element_factory_make(sFactoryName, "img_sink");
803 
804  if( m_pElemImgSink == NULL )
805  {
806  LOGERROR("Failed to create gstreamer element: %s.", sFactoryName);
807  m_bFatal = true;
808  return RC_ERROR;
809  }
810 
811  g_object_set(G_OBJECT(m_pElemImgSink),
812  "signal-handoffs", TRUE,
813  NULL);
814 
815  //
816  // Add elements to the pipeline
817  //
818  gst_bin_add_many(GST_BIN(m_pBinImageSink),
819  pElemImgFfmpeg,
820  m_pElemImgSink,
821  NULL);
822 
823  caps = gst_caps_new_simple("video/x-raw-rgb",
824  "bpp", G_TYPE_INT, 24,
825  "depth", G_TYPE_INT, 24,
826  NULL);
827  ok = gst_element_link_filtered(pElemImgFfmpeg, m_pElemImgSink, caps);
828 
829  if( !ok )
830  {
831  LOGERROR("Failed to set filtered link.");
832  m_bFatal = true;
833  return RC_ERROR;
834  }
835 
836  gst_caps_unref(caps);
837 
838 
839  //
840  // Add sink ghostpad since bins normally do not have pads.
841  //
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));
845 
846  return OK;
847 }
848 
849 #if 0 // EXAMPLE
850 void CameraGst::changeSink(GstElement *pBinNewSink)
851 {
852  GstElement *pBinOldSink;
853  GstElement *pBinNewSink;
854 
855  if( eNewMode == m_eCameraMode )
856  {
857  return;
858  }
859 
860  gst_element_set_state(m_pPipeline, GST_STATE_NULL);
861 
862  if( eNewMode == CameraModePhoto )
863  {
864  pBinOldSink = m_pBinVideoSink;
865  pBinNewSink = m_pBinImageSink;
866  }
867  else
868  {
869  pBinOldSink = m_pBinImageSink;
870  pBinNewSink = m_pBinVideoSink;
871  }
872 
873  gst_element_unlink(m_pBinCameraSrc, pBinOldSink);
874  gst_object_ref(pBinOldSink);
875  gst_bin_remove(GST_BIN(m_pPipeline), pBinOldSink);
876 
877  gst_bin_add(GST_BIN(m_pPipeline), pBinNewSink);
878  gst_element_link(m_pBinCameraSrc, pBinNewSink);
879 
880  m_eCameraMode = eNewMode;
881 
882  LOGDIAG3("%s Mode.", (m_eCameraMode == CameraModePhoto? "Photo": "Video"));
883 }
884 #endif
885 
886 void CameraGst::gstBusMsgCb(GstBus *bus,
887  GstMessage *message,
888  void *user_data)
889 {
890  CameraGst *pCam = (CameraGst *)user_data;
891 
892  switch( GST_MESSAGE_TYPE(message) )
893  {
894  case GST_MESSAGE_EOS:
895  LOGDIAG3("GST: End of stream.");
896  break;
897 
898  case GST_MESSAGE_ERROR:
899  {
900  gchar *debug;
901  GError *error;
902 
903  gst_message_parse_error(message, &error, &debug);
904  g_free(debug);
905  LOGERROR("GST: %s.", error->message);
906  g_error_free(error);
907  }
908  break;
909 
910  default:
911  //fprintf(stderr, "DBG: GST: busmsg=%u\n", GST_MESSAGE_TYPE(message));
912  break;
913  }
914 }
915 
916 GstBusSyncReply CameraGst::gstBusSyncHandler(GstBus *bus,
917  GstMessage *message,
918  gpointer user_data)
919 {
920  CameraGst *pCam = (CameraGst *)user_data;
921 
922  if( GST_MESSAGE_TYPE(message) == GST_MESSAGE_EOS )
923  {
924  LOGDIAG3("GST: Sync: End of stream.");
925  return GST_BUS_PASS;
926  }
927 
928  // ignore anything but 'prepare-xwindow-id' element messages
929  if( GST_MESSAGE_TYPE(message) != GST_MESSAGE_ELEMENT )
930  {
931  return GST_BUS_PASS;
932  }
933 
934  if( !gst_structure_has_name(message->structure, "prepare-xwindow-id") )
935  {
936  return GST_BUS_PASS;
937  }
938 
939  if( pCam->m_uVidWinXid != 0 )
940  {
941  GstXOverlay *xoverlay;
942 
943  xoverlay = GST_X_OVERLAY(GST_MESSAGE_SRC(message));
944 
945  if( g_object_class_find_property(G_OBJECT_GET_CLASS(xoverlay),
946  "force-aspect-ratio") )
947  {
948  g_object_set(G_OBJECT(xoverlay), "force-aspect-ratio", TRUE, NULL);
949  }
950 
951  // GST_MESSAGE_SRC (message) will be the video sink element
952  gst_x_overlay_set_xwindow_id(xoverlay, pCam->m_uVidWinXid);
953  // new method in 10.31
954  // gst_x_overlay_set_window_handle(xoverlay, pCam->m_uVidWinXid);
955  }
956  else
957  {
958  LOGERROR("Should have obtained video_window_xid by now!");
959  }
960 
961  gst_message_unref(message);
962 
963  return GST_BUS_DROP;
964 }
965 
966 #if 0
967 gint64 CameraGst::onBlockVideoSelector(GstElement *element,
968  gpointer user_data)
969 {
970  //fprintf(stderr, "DBG: onBlock\n");
971  return 0;
972 }
973 
974 gint64 CameraGst::onSwitchVideoSelector(GstElement *element,
975  GstPad *pad,
976  gint64 stop_time,
977  gint64 start_time,
978  gpointer user_data)
979 {
980  //fprintf(stderr, "DBG: onSwitch\n");
981  return 0;
982 }
983 #endif
984 
986 {
987  string strX("XXXXXX"); // pattern to be replaced
988  string strSuffix(".jpg"); // suffix
989  int fd; // file descriptor
990 
991  sprintf(m_bufTmpName, "/tmp/camegst-%s%s", strX.c_str(), strSuffix.c_str());
992 
993 #ifdef HAVE_MKSTEMPS
994  if( (fd = mkstemps(m_bufTmpName, strSuffix.length())) < 0 )
995  {
996  LOGERROR("mkstemps(%s, %zu) failed.\n", m_bufTmpName, strSuffix.length());
997  m_bufTmpName[0] = 0;
998  m_bFatal = true;
999  }
1000 
1001 #else
1002  char tmp[PATH_MAX];
1003 
1004  sprintf(tmp, "/tmp/camgst-%s", strX.c_str());
1005 
1006  if( (fd = mkstemp(tmp)) < 0 )
1007  {
1008  LOGERROR("mkstemp(%s) failed.\n", tmp);
1009  m_bufTmpName[0] = 0;
1010  m_bFatal = true;
1011  }
1012 
1013  else
1014  {
1015  rename(tmp, m_bufTmpName);
1016  }
1017 #endif
1018 
1019  if( fd >= 0 )
1020  {
1021  close(fd);
1022  }
1023 }
void stopPipeline()
Stop the camera and the pipeline.
Definition: CameraGst.cxx:373
virtual ~CameraGst()
Destructor.
Definition: CameraGst.cxx:91
static bool isEqResolution(const CamRes &res1, const CamRes &res2)
Check is two camera resolutions are equal.
Definition: Camera.h:257
int makePipeline()
Make gstreamer video pipeline.
Definition: CameraGst.cxx:382
CamRes m_resCurrent
current camera resolution
Definition: Camera.h:295
virtual int startVideo(const CamRes &resVideo=CamResDft)
Start the camera streaming video.
Definition: CameraCv.cxx:89
Video and still image camera base class.
GstElement * m_pElemImgSink
still image sink
Definition: CameraGst.h:178
const CamRes CamResDft
default resolution
Definition: Camera.h:89
gulong m_uVidWinXid
overlay window X-windows id
Definition: CameraGst.h:180
bool m_bTakingImage
taking an image is [not] finished
Definition: Camera.h:299
CamRes m_resVideo
current video resolution
Definition: Camera.h:296
int makeCameraSrcBin()
Video source bin.
Definition: CameraGst.cxx:463
bool m_bCameraRunning
camera is [not] on and running video
Definition: Camera.h:298
virtual void autoFocus()
Auto-focus camera.
Definition: CameraGst.cxx:324
Camera base class.
Definition: Camera.h:125
char m_bufTmpName[PATH_MAX]
temporary file name buffer
Definition: CameraGst.h:181
virtual int clickImage(cv::Mat &img, const CamRes &resImage=CamResDft)
Take a still image.
Definition: CameraCv.cxx:188
virtual CamRes setCameraResolution(const CamRes &res)
Set the camera resolution in either video or still image mode.
Definition: CameraGst.cxx:328
virtual CamRes setCameraResolution(const CamRes &res)
Set the camera resolution in either video or still image mode.
Definition: CameraCv.cxx:265
virtual int stopVideo()
Stop the camera from streaming video.
Definition: CameraCv.cxx:144
Camera resolution structure.
Definition: Camera.h:79
int m_nSignalId
camera still image callback signal
Definition: CameraGst.h:179
void makeTmpFile()
Make unique temporary file.
Definition: CameraGst.cxx:985
int width
width in pixels
Definition: Camera.h:81
virtual int startVideo(const CamRes &resVideo=CamResDft)
Start the camera streaming video.
Definition: CameraGst.cxx:103
void setXid(gulong uXid)
Set X identifier associated with widget receiving streaming video.
Definition: CameraGst.cxx:357
GStreamer implementation of the camera class. The video is streamed via a Gstreamer/GTK callback mech...
Definition: CameraGst.h:84
int height
height in pixels
Definition: Camera.h:82
CamRes m_resImage
current still image resolution
Definition: Camera.h:297
virtual int clickImage(cv::Mat &img, const CamRes &resImage=CamResDft)
Take a still image.
Definition: CameraGst.cxx:172
std::string m_strVideoDevName
video device name
Definition: Camera.h:293
int makeVideoSinkBin()
Video sink bin.
Definition: CameraGst.cxx:563
bool isCameraRunning() const
Test if the camera is on and running.
Definition: Camera.h:224
void startPipeline()
Start the camera and the pipeline.
Definition: CameraGst.cxx:364
int makeImageSinkBin()
Image sink bin.
Definition: CameraGst.cxx:768
RoadNarrows Robotics.
Definition: Camera.h:74
virtual int grabFrame(cv::Mat &frame)
Grab a image frame from the video stream.
Definition: CameraGst.cxx:157
static void gstBusMsgCb(GstBus *bus, GstMessage *message, gpointer user_data)
Gstream pipeline synchronous bus handler.
Definition: CameraGst.cxx:886
virtual int stopVideo()
Stop the camera from streaming video.
Definition: CameraGst.cxx:141
static void clickCb(GstElement *element, GstBuffer *buffer, GstPad *pad, void *user_data)
Still image capture asynchronouse callback.
Definition: CameraGst.cxx:269
Gstreamer video and still image camera class.
bool m_bFatal
camera instance is in a fatal state
Definition: Camera.h:300