1 // VideoInputGst.cpp: Video input processing using Gstreamer.
2 //
3 // Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012
4 // Free Software Foundation, Inc
5 //
6 // This program is free software; you can redistribute it and/or modify
7 // it under the terms of the GNU General Public License as published by
8 // the Free Software Foundation; either version 3 of the License, or
9 // (at your option) any later version.
10 //
11 // This program is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 // GNU General Public License for more details.
15 //
16 // You should have received a copy of the GNU General Public License
17 // along with this program; if not, write to the Free Software
18 // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
19
20 #ifdef HAVE_CONFIG_H
21 #include "gnashconfig.h"
22 #endif
23
24 #include "GlibDeprecated.h"
25
26 #include "VideoInputGst.h"
27 #include "log.h"
28 #include "GstUtil.h"
29 #include "gst/gst.h"
30 #include "rc.h"
31 #include "utility.h"
32 #include <gst/interfaces/propertyprobe.h>
33 #include <vector>
34 #include <cmath>
35
36
37 namespace {
38 //get rc file for webcam selection
39 gnash::RcInitFile& rcfile = gnash::RcInitFile::getDefaultInstance();
40 }
41
42 namespace gnash {
43 namespace media {
44 namespace gst {
45
46 /// \class GnashWebcamPrivate
47 ///
48 /// This class is initialized once a hardware video input device is chosen.
49 /// It is really the workhorse of VideoInputGst. It contains all the important
50 /// Gstreamer elements (element pointers, bins, pipelines, the GMainLoop, etc.)
51 ///
52 class GnashWebcamPrivate
53 {
54 public:
55 /// Constructor for the GnashWebcamPrivate class.
56 GnashWebcamPrivate();
57
58 /// \brief Accessor to set the private _webcamDevice variable in the
59 /// GnashWebcamPrivate class.
60 ///
61 /// @param d A pointer to a GnashWebcam class for the selected input device.
setWebcamDevice(GnashWebcam * d)62 void setWebcamDevice(GnashWebcam *d) {_webcamDevice = d;}
63
64 //FIXME: this should eventually be a private or protected data field
65 //protected:
66
67 /// \var GnashWebcamPrivate::_pipeline
68 ///
69 /// \brief A pointer to the main Gstreamer pipeline that all
70 /// created elements and bins will be dropped into.
71 GstElement *_pipeline;
72
73 /// \var GnashWebcamPrivate::_webcamSourceBin
74 ///
75 /// A pointer to the Gstreamer source bin. This variable is set
76 /// inside of the make_webcamSourceBin() function. The pipeline
77 /// API of this source bin is written as follows:
78 /// videosourcedevice ! capsfilter (ghostpad)
79 GstElement *_webcamSourceBin;
80
81 /// \var GnashWebcamPrivate::_webcamMainBin
82 ///
83 /// A pointer to the Gstreamer main bin. This variable is set
84 /// inside of the make_webcamMainBin() function. The pipeline
85 /// API of the main bin is written as follows:
86 /// tee ! save_queue (ghostpad)
87 ///
88 /// tee ! display_queue (ghostpad)
89 ///
90 /// This basically creates two queues where video stream data sits
91 /// and can be attached (optionally) to a display_bin to show the
92 /// video onscreen or to a save_bin to mux-out the stream and
93 /// save to a file on disk.
94 GstElement *_webcamMainBin;
95
96 /// \var GnashWebcamPrivate::_videoDisplayBin
97 ///
98 /// A pointer to the Gstreamer display bin. This variable is set
99 /// inside of the make_webcam_display_bin() function. The pipeline
100 /// API of the video_display_bin is written as follows:
101 ///
102 /// videoscale ! videosink
103 ///
104 /// This bin is dropped into the webcam_main_bin, but by default
105 /// the connection to display_queue is not made. This means that
106 /// even though the video_display_bin is created, it is not linked
107 /// and thus will not show video to the screen unless you call the
108 /// webcamMakeVideoDisplayLink() function.
109 GstElement *_videoDisplayBin;
110
111 /// \var GnashWebcamPrivate::_videoSaveBin
112 ///
113 /// A pointer to the Gstreamer video_save_bin. This variable is set
114 /// inside of the make_webcam_save_bin() function. The pipeline
115 /// API of the video_save_bin is written as follows:
116 ///
117 /// ffmpegcolorspace ! videorate ! videoscale ! theoraenc ! oggmux ! filesink
118 ///
119 /// This bin is dropped into the webcam_main_bin and is linked automatically
120 /// to the video_save_queue element in the webcam_main_bin
121 /// Note: if you want to save the file in a different format, simply
122 /// link up video scale to a different encoder and muxer.
123 GstElement *_videoSaveBin;
124
125 /// \var GnashWebcamPrivate::_videoSource
126 /// \brief Contains a direct link to the src pad in the video source
127 /// element. This is different from _webcamSourceBin in that
128 /// it points to the video source element INSIDE the bin, not
129 /// the source bin itself.
130 GstElement *_videoSource;
131
132 /// \var GnashWebcamPrivate::_capsFilter
133 /// \brief Contains a direct link to the src pad in the capsfilter
134 /// element.
135 GstElement *_capsFilter;
136
137 /// \var GnashWebcamPrivate:_videoFileSink
138 /// \brief Contains a direct link to the video_file_sink element
139 GstElement *_videoFileSink;
140
141 /// \var GnashWebcamPrivate::_videoEnc
142 /// \brief Contains a direct link to the video encoder element
143 GstElement *_videoEnc;
144
145 /// \var GnashWebcamPrivate::_pipelineIsPlaying
146 /// \brief Boolean value which is changed based on whether or not
147 /// the Gstreamer pipeline status is GST_STATE_PLAYING (true)
148 /// or GST_STATE_NULL (false), GST_STATE_READY (false),
149 /// GST_STATE_PAUSED (false).
150 gboolean _pipelineIsPlaying;
151
152 /// \var GnashWebcamPrivate::_webcamDevice
153 /// \brief Contains a pointer to the original GnashWebcam class
154 /// that was created when enumerating and probing attached
155 /// hardware.
156 GnashWebcam *_webcamDevice;
157
158 /// \var GnashWebcamPrivate::_currentFormat
159 /// \brief Contains a pointer to the WebcamVidFormat data structure
160 /// selected to be used with this pipeline.
161 WebcamVidFormat *_currentFormat;
162
163 /// \var GnashWebcamPrivate::_eosTimeoutId
164 /// \brief This variable is not currently used, but will eventually
165 /// be used as a timeout when networking encapsulation is being
166 /// used.
167 guint _eosTimeoutId;
168 };
169
170 /// \class GnashWebcam
171 ///
172 /// The initial data structure used to store enumerated information about
173 /// attached hardware video input devices. This class is smaller in size
174 /// than the GnashWebcamPrivate class which is initialized once the user
175 /// specifies a hardware input device to use in the gnashrc file.
176 ///
177 class GnashWebcam {
178 public:
179 /// \brief Accessor to retreive a the private _element variable
180 /// from the GnashWebcam class which contains a pointer
181 /// to the video source element.
182 ///
183 /// @return GstElement* to the video source element
getElementPtr()184 GstElement* getElementPtr() {return _element;};
185
186 /// \brief Accessor to set the private _element variable from
187 /// the GnashWebcam class.
188 ///
189 /// @param element The GstElement pointer to the video source element.
setElementPtr(GstElement * element)190 void setElementPtr(GstElement* element) {_element = element;};
191
192 /// \brief Accessor to get the private _devLocation variable from
193 /// the GnashWebcam class.
194 ///
195 /// @return The _devLocation private variable from GnashWebcam class.
getDevLocation()196 gchar* getDevLocation() {return _devLocation;};
197
198 /// \brief Accessor to set the private _devLocation variable from
199 /// the GnashWebcam class.
200 ///
201 /// @param l A gchar* containing the physical location of the video
202 /// input hardware device (e.g. on Linux typically would be set
203 /// to '/dev/video0').
setDevLocation(gchar * l)204 void setDevLocation(gchar *l) {_devLocation = l;};
205
206 /// \brief Accessor to return the private _gstreamerSrc variable
207 /// from the GnashWebcam class.
208 ///
209 /// @return The _gstreamerSrc variable from the GnashWebcam class.
210 /// which should contain the type of the Gstreamer video source
211 /// element (e.g. v4lsrc, v4l2src).
getGstreamerSrc()212 gchar* getGstreamerSrc() {return _gstreamerSrc;};
213
214 /// \brief Accessor to set the private _gstreamerSrc variable
215 /// from the GnashWebcam class.
216 ///
217 /// @param s A gchar* containing the type of the Gstreamer source
218 /// element type (e.g. v4lsrc, v4l2src, etc)
setGstreamerSrc(gchar * s)219 void setGstreamerSrc(gchar *s) {_gstreamerSrc = s;};
220
221 /// \brief Accessor to get the private _productName variable
222 /// from the GnashWebcam class.
223 ///
224 /// @return A gchar* containing the video input's hardware name
225 /// (e.g. Built-In Webcam or Microsoft LifeCam VX500).
getProductName()226 gchar* getProductName() {return _productName;};
227
228 /// \brief Accessor to set the private _productName variable
229 /// from the GnashWebcam class.
230 ///
231 /// @param n A gchar* to the hardware input device's hardware name
232 /// (e.g. Built-In Webcam or Microsoft LifeCam VX500).
setProductName(gchar * n)233 void setProductName(gchar *n) {_productName = n;};
234
235 /// \var GnashWebcam::numVideoFormats
236 /// \brief Contains an integer value representing the number of
237 /// video formats the camera supports (used for iteration
238 /// purposes).
239 gint numVideoFormats;
240
241 /// \var GnashWebcam::videoFormats
242 /// \brief A GArray containing WebcamVidFormat data structures
243 /// (see WebcamVidFormat class documentation for more info).
244 GArray* videoFormats;
245
246 /// \var GnashWebcam::supportedResolutions
247 /// \brief A hash table for easy lookup of resolutions the hardware
248 /// camera supports.
249 GHashTable* supportedResolutions;
250
251 /// Constructor for the GnashWebcam class.
252 GnashWebcam();
253
254 private:
255 /// \var GnashWebcam::_element
256 /// \brief GstElement* which points to the video source
257 /// element.
258 GstElement* _element;
259
260 /// \var GnashWebcam::_devLocation
261 /// \brief Contains the physical location of the webcam device
262 /// (e.g. on Linux typically would be set to /dev/video0).
263 gchar* _devLocation;
264
265 /// \var GnashWebcam::_gstreamerSrc
266 /// \brief Contains a gchar* which describes the gstreamer source
267 /// type (e.g. v4lsrc or v4l2src).
268 gchar* _gstreamerSrc;
269
270 /// \var GnashWebcam::_productName
271 /// \brief Contains a gchar* which describes the name of the hardware
272 /// device (e.g. Built-In Webcam or Microsoft LifeCam VX500).
273 gchar* _productName;
274 };
275
276 void
getNames(std::vector<std::string> & names)277 VideoInputGst::getNames(std::vector<std::string>& names)
278 {
279 // Make sure gst is initialized
280 gst_init(nullptr, nullptr);
281
282 std::vector<GnashWebcam*> cams;
283 // Check for devices
284 findVidDevs(cams);
285
286 for (GnashWebcam* cam : cams) {
287 if (cam) names.push_back(cam->getProductName());
288 }
289 }
290
291
292 //initializes the Gstreamer interface
VideoInputGst()293 VideoInputGst::VideoInputGst()
294 :
295 _activityLevel(-1.0),
296 _bandwidth(16384),
297 _currentFPS(0),
298 _fps(15.0),
299 _height(120),
300 _width(160),
301 _index(0),
302 _motionLevel(50),
303 _motionTimeout(2000),
304 _muted(true),
305 _quality(0)
306 {
307 gst_init(nullptr,nullptr);
308
309 // TODO: there is really no need to store all the cameras, as a
310 // VideoInput class should correspond to one camera.
311 findVidDevs(_vidVect);
312
313 _devSelection = makeWebcamDeviceSelection();
314 //also set _index for actionscript accessibility
315 if (_devSelection < 10) {
316 _index = _devSelection;
317 } else {
318 log_error(_("too high an index value, will cause segfault"));
319 }
320
321 setWebcam(_devSelection);
322 webcamCreateMainBin();
323 webcamCreateDisplayBin();
324 webcamCreateSaveBin();
325 }
326
~VideoInputGst()327 VideoInputGst::~VideoInputGst()
328 {
329 }
330
331 bool
init()332 VideoInputGst::init()
333 {
334 return webcamCreateMainBin() && webcamCreateDisplayBin() &&
335 webcamMakeVideoDisplayLink();
336 }
337
338 void
requestMode(size_t width,size_t height,double fps,bool favorArea)339 VideoInputGst::requestMode(size_t width, size_t height, double fps,
340 bool favorArea)
341 {
342 // TODO: this should select an available height, width and frame rate
343 // depending on the favorArea variable.
344 _width = width;
345 _height = height;
346 _fps = fps;
347
348 UNUSED(favorArea);
349
350 // I don't know what the point is of this. It was previously in Camera_as,
351 // where it certainly shouldn't be.
352 webcamChangeSourceBin();
353
354 }
355
356 // Populates video devices to a vector of GnashWebcam pointers
357 // which contain important information about the hardware camera
358 // inputs available on the machine
359 void
findVidDevs(std::vector<GnashWebcam * > & cameraList)360 VideoInputGst::findVidDevs(std::vector<GnashWebcam*>& cameraList)
361 {
362
363 //find video test sources
364 GstElement *element;
365 element = gst_element_factory_make ("videotestsrc", "vidtestsrc");
366
367 if (element == nullptr) {
368 log_error(_("%s: Could not create video test source."), __FUNCTION__);
369 return;
370 } else {
371 cameraList.push_back(new GnashWebcam);
372 GnashWebcam& cam = *cameraList.back();
373 cam.setElementPtr(element);
374 cam.setGstreamerSrc(g_strdup_printf("videotestsrc"));
375 cam.setProductName(g_strdup_printf("videotest"));
376 }
377
378 #ifdef HAS_GSTREAMER_PLUGINS_BASE
379 //find v4l devices
380 GstPropertyProbe *probe;
381 GValueArray *devarr;
382 element = nullptr;
383
384 element = gst_element_factory_make ("v4lsrc", "v4lvidsrc");
385 if ( ! element ) {
386 log_error(_("%s: Could not create pulsesrc element"), __FUNCTION__);
387 return;
388 }
389 probe = GST_PROPERTY_PROBE (element);
390 if ( ! probe ) {
391 log_error(_("%s: Could not get property probe from pulsesrc element"),
392 __FUNCTION__);
393 return;
394 }
395 devarr = gst_property_probe_probe_and_get_values_name (probe, "device");
396 for (size_t i = 0; devarr != nullptr && i < devarr->n_values; ++i) {
397 GValue *val;
398 gchar *dev_name = nullptr;
399
400 val = g_value_array_get_nth (devarr, i);
401 g_object_set (element, "device", g_value_get_string (val), NULL);
402 gst_element_set_state (element, GST_STATE_PLAYING);
403 g_object_get (element, "device-name", &dev_name, NULL);
404 gst_element_set_state (element, GST_STATE_NULL);
405 if (strcmp(dev_name, "null") == 0) {
406 log_debug("No v4l video sources. Checking for other vid inputs");
407 }
408 else {
409 cameraList.push_back(new GnashWebcam);
410 GnashWebcam& cam = *cameraList.back();
411
412 cam.setElementPtr(element);
413 cam.setGstreamerSrc(g_strdup_printf("v4lsrc"));
414 cam.setProductName(dev_name);
415
416 //set device location information (e.g. /dev/video0)
417 gchar *location;
418 g_object_get (element, "device", &location , NULL);
419 cam.setDevLocation(location);
420 }
421 }
422 if (devarr) {
423 g_value_array_free (devarr);
424 }
425
426 //find v4l2 devices
427 probe = nullptr;
428 devarr = nullptr;
429 element = nullptr;
430
431 element = gst_element_factory_make ("v4l2src", "v4l2vidsrc");
432 probe = GST_PROPERTY_PROBE (element);
433 devarr = gst_property_probe_probe_and_get_values_name (probe, "device");
434 for (size_t i = 0; devarr != nullptr && i < devarr->n_values; ++i) {
435 GValue *val;
436 gchar *dev_name = nullptr;
437
438 val = g_value_array_get_nth (devarr, i);
439 g_object_set (element, "device", g_value_get_string (val), NULL);
440 gst_element_set_state (element, GST_STATE_PLAYING);
441 g_object_get (element, "device-name", &dev_name, NULL);
442 gst_element_set_state (element, GST_STATE_NULL);
443 if (strcmp(dev_name, "null") == 0) {
444 log_debug("no v4l2 video sources found.");
445 }
446 else {
447 cameraList.push_back(new GnashWebcam);
448 GnashWebcam& cam = *cameraList.back();
449 cam.setElementPtr(element);
450 cam.setGstreamerSrc(g_strdup_printf("v4l2src"));
451 cam.setProductName(dev_name);
452
453 //set device location information (e.g. /dev/video0)
454 gchar *location;
455 g_object_get (element, "device", &location , NULL);
456 cam.setDevLocation(location);
457 }
458 }
459 if (devarr) {
460 g_value_array_free (devarr);
461 }
462 #endif
463 }
464
465
466 //called by addSupportedFormat. finds the highest possible framerate
467 //to record at (can be shaped down by a filter for performance)
468 void
findHighestFramerate(WebcamVidFormat * format)469 VideoInputGst::findHighestFramerate(WebcamVidFormat *format)
470 {
471 gint framerate_numerator;
472 gint framerate_denominator;
473 gint i;
474
475 //Select the highest framerate up to less than or equal to 30 Hz
476 framerate_numerator = 1;
477 framerate_denominator = 1;
478 for (i = 0; i < format->numFramerates; i++) {
479 gfloat framerate = format->framerates[i].numerator /
480 format->framerates[i].denominator;
481 if (framerate > ((float) framerate_numerator / framerate_denominator)
482 && framerate <= 30) {
483 framerate_numerator = format->framerates[i].numerator;
484 framerate_denominator = format->framerates[i].denominator;
485 }
486 }
487 //set highest found above
488 format->highestFramerate.numerator = framerate_numerator;
489 format->highestFramerate.denominator = framerate_denominator;
490 }
491
492 //find the framerates at which the selected format can handle input
493 void
getSupportedFramerates(WebcamVidFormat * video_format,GstStructure * structure)494 VideoInputGst::getSupportedFramerates
495 (WebcamVidFormat *video_format, GstStructure *structure)
496 {
497 const GValue *framerates;
498 gint i, j;
499
500 //note that framerates may contain one value, a list, or a range
501 framerates = gst_structure_get_value (structure, "framerate");
502 if (GST_VALUE_HOLDS_FRACTION (const_cast<GValue *>(framerates))) {
503 video_format->numFramerates = 1;
504 video_format->framerates =
505 g_new0 (FramerateFraction, video_format->numFramerates);
506 video_format->framerates[0].numerator =
507 gst_value_get_fraction_numerator (framerates);
508 video_format->framerates[0].denominator =
509 gst_value_get_fraction_denominator (framerates);
510 }
511 else if (GST_VALUE_HOLDS_LIST (const_cast<GValue *>(framerates))) {
512 video_format->numFramerates = gst_value_list_get_size (framerates);
513 video_format->framerates =
514 g_new0 (FramerateFraction, video_format->numFramerates);
515 for (i = 0; i < video_format->numFramerates; i++) {
516 const GValue *value;
517 value = gst_value_list_get_value (framerates, i);
518 video_format->framerates[i].numerator =
519 gst_value_get_fraction_numerator (value);
520 video_format->framerates[i].denominator =
521 gst_value_get_fraction_denominator (value);
522 }
523 }
524 else if (GST_VALUE_HOLDS_FRACTION_RANGE (const_cast<GValue *>(framerates))) {
525 gint numerator_min, denominator_min, numerator_max, denominator_max;
526 const GValue *fraction_range_min;
527 const GValue *fraction_range_max;
528
529 fraction_range_min =
530 gst_value_get_fraction_range_min (framerates);
531 numerator_min =
532 gst_value_get_fraction_numerator (fraction_range_min);
533 denominator_min =
534 gst_value_get_fraction_denominator (fraction_range_min);
535
536 fraction_range_max = gst_value_get_fraction_range_max (framerates);
537 numerator_max =
538 gst_value_get_fraction_numerator (fraction_range_max);
539 denominator_max =
540 gst_value_get_fraction_denominator (fraction_range_max);
541 log_debug ("FractionRange: %d/%d - %d/%d",
542 numerator_min, denominator_min, numerator_max, denominator_max);
543
544 video_format->numFramerates =
545 (numerator_max - numerator_min + 1) *
546 (denominator_max - denominator_min + 1);
547 video_format->framerates =
548 g_new0 (FramerateFraction, video_format->numFramerates);
549 int k = 0;
550 for (i = numerator_min; i <= numerator_max; i++) {
551 for (j = denominator_min; j <= denominator_max; j++) {
552 video_format->framerates[k].numerator = i;
553 video_format->framerates[k].denominator = j;
554 k++;
555 }
556 }
557 }
558 else {
559 g_critical ("GValue type %s, cannot be handled for framerates",
560 G_VALUE_TYPE_NAME (const_cast<GValue *>(framerates)));
561 }
562 }
563
564 //we found a supported framerate and want to add the information to
565 //the GnashWebcam structure
566 void
addSupportedFormat(GnashWebcam * cam,WebcamVidFormat * video_format,GstStructure * format_structure)567 VideoInputGst::addSupportedFormat(GnashWebcam *cam, WebcamVidFormat *video_format,
568 GstStructure *format_structure)
569 {
570 gint i;
571 gchar *resolution;
572
573 getSupportedFramerates(video_format, format_structure);
574 findHighestFramerate(video_format);
575
576 resolution = g_strdup_printf ("%ix%i", video_format->width,
577 video_format->height);
578 i = GPOINTER_TO_INT(g_hash_table_lookup (cam->supportedResolutions, resolution));
579
580 //if i returns a value, maybe this resolution has been added previously?
581 if(i) {
582 WebcamVidFormat *curr_format =
583 &g_array_index(cam->videoFormats, WebcamVidFormat, i - 1);
584 gfloat new_framerate = (float)(video_format->highestFramerate.numerator /
585 video_format->highestFramerate.denominator);
586 gfloat curr_framerate = (float)(curr_format->highestFramerate.numerator /
587 curr_format->highestFramerate.denominator);
588 if (new_framerate > curr_framerate) {
589 log_debug("higher framerate replacing existing format");
590 *curr_format = *video_format;
591 }
592
593 g_free (resolution);
594
595 return;
596 }
597
598 g_array_append_val (cam->videoFormats, *video_format);
599 g_hash_table_insert (cam->supportedResolutions, resolution,
600 GINT_TO_POINTER(cam->numVideoFormats + 1));
601
602 cam->numVideoFormats++;
603 }
604
605 //pulls webcam device selection from gnashrc (will eventually tie into
606 //gui)
607 int
makeWebcamDeviceSelection()608 VideoInputGst::makeWebcamDeviceSelection()
609 {
610 int dev_select;
611 dev_select = rcfile.getWebcamDevice();
612 if (dev_select == -1) {
613 log_debug("%s: No webcam selected in rc file, setting to videotestsource",
614 __FUNCTION__);
615 rcfile.setWebcamDevice(0);
616 dev_select = rcfile.getWebcamDevice();
617 } else {
618 log_debug("Camera %d specified in gnashrc file, using that one.",
619 dev_select);
620 }
621 //make sure that the device selected is actually valid
622
623 const int webcamDevice = rcfile.getWebcamDevice();
624 if (webcamDevice < 0 ||
625 static_cast<size_t>(webcamDevice) >= _vidVect.size()) {
626
627 log_error(_("You have an invalid camera selected. Please "
628 "check your gnashrc file"));
629 exit(EXIT_FAILURE);
630 }
631
632 //set _name value for actionscript
633 _name = _vidVect[dev_select]->getProductName();
634
635 //now that a selection has been made, get capabilities of that device
636 getSelectedCaps(rcfile.getWebcamDevice());
637 return rcfile.getWebcamDevice();
638 }
639
640 //called after a device selection, this starts enumerating the device's
641 //capabilities
642 void
getSelectedCaps(gint dev_select)643 VideoInputGst::getSelectedCaps(gint dev_select)
644 {
645 GstElement *pipeline;
646 gchar *command;
647 GError *error = nullptr;
648 GstStateChangeReturn return_val;
649 GstBus *bus;
650 GstMessage *message;
651
652 GnashWebcam *data_struct = _vidVect[dev_select];
653
654 if (dev_select < 0 ||
655 static_cast<size_t>(dev_select) >= _vidVect.size()) {
656 log_error(_("%s: Passed an invalid argument (not a valid "
657 "dev_select value)"), __FUNCTION__);
658 exit(EXIT_FAILURE);
659 }
660
661 //create tester pipeline to enumerate properties
662 if (dev_select == 0) {
663 command = g_strdup_printf ("%s name=src ! fakesink",
664 data_struct->getGstreamerSrc());
665 }
666 else {
667 command = g_strdup_printf ("%s name=src device=%s ! fakesink",
668 data_struct->getGstreamerSrc(), data_struct->getDevLocation());
669 }
670 pipeline = gst_parse_launch(command, &error);
671 if ((pipeline != nullptr) && (error == nullptr)) {
672 //Wait at most 5 seconds for the pipeline to start playing
673 gst_element_set_state (pipeline, GST_STATE_PLAYING);
674 return_val =
675 gst_element_get_state (pipeline, nullptr, nullptr, 5 * GST_SECOND);
676
677 //errors on bus?
678 bus = gst_element_get_bus (pipeline);
679 message = gst_bus_poll (bus, GST_MESSAGE_ERROR, 0);
680
681 if (GST_IS_OBJECT(bus)){
682 gst_object_unref (bus);
683 } else {
684 log_error(_("%s: Pipeline bus isn't an object for some reason"),
685 __FUNCTION__);
686 }
687
688 //if everything above worked properly, begin probing for values
689 if ((return_val == GST_STATE_CHANGE_SUCCESS) && (message == nullptr)) {
690 GstElement *src;
691 GstPad *pad;
692 GstCaps *caps;
693
694 gst_element_set_state(pipeline, GST_STATE_PAUSED);
695
696 src = gst_bin_get_by_name(GST_BIN(pipeline), "src");
697
698 //get the pad, find the capabilities for probing in supported formats
699 pad = gst_element_get_pad (src, "src");
700 caps = gst_pad_get_caps (pad);
701 if (GST_IS_OBJECT(pad)) {
702 gst_object_unref (pad);
703 } else {
704 log_error(_("%s: Template pad isn't an object for some reason"),
705 __FUNCTION__);
706 }
707 if (dev_select != 0) {
708 getSupportedFormats(data_struct, caps);
709 }
710
711 gst_caps_unref (caps);
712 }
713 gst_element_set_state (pipeline, GST_STATE_NULL);
714 if (GST_IS_OBJECT(pipeline)){
715 gst_object_unref (pipeline);
716 } else {
717 log_error(_("%s: pipeline isn't an object for some reason"),
718 __FUNCTION__);
719 }
720 }
721
722 if (error) {
723 g_error_free (error);
724 }
725 g_free (command);
726 }
727
728 //probe the selected camera for the formats it supports
729 void
getSupportedFormats(GnashWebcam * cam,GstCaps * caps)730 VideoInputGst::getSupportedFormats(GnashWebcam *cam, GstCaps *caps)
731 {
732 gint i;
733 gint num_structs;
734
735 num_structs = gst_caps_get_size (caps);
736
737 for (i=0; i < num_structs; i++) {
738 GstStructure *structure;
739 const GValue *width, *height;
740
741 //this structure is used to probe the source for information
742 structure = gst_caps_get_structure (caps, i);
743
744 //we just want to enumerate raw formats to keep things consistent
745 //so if the strcuture we're currently looking at isn't either of
746 //the standard raw formats, keep iterating through the loop
747 if (!gst_structure_has_name (structure, "video/x-raw-yuv") &&
748 !gst_structure_has_name (structure, "video/x-raw-rgb"))
749 {
750 continue;
751 }
752
753 width = gst_structure_get_value (structure, "width");
754 height = gst_structure_get_value (structure, "height");
755
756 if (G_VALUE_HOLDS_INT (const_cast<GValue *>(width))) {
757 WebcamVidFormat video_format;
758
759 video_format.mimetype =
760 g_strdup (gst_structure_get_name (structure));
761 gst_structure_get_int (structure, "width", &(video_format.width));
762 gst_structure_get_int (structure, "height", &(video_format.height));
763 addSupportedFormat(cam, &video_format, structure);
764 }
765 else if (GST_VALUE_HOLDS_INT_RANGE (const_cast<GValue *>(width))) {
766 int min_width, max_width, min_height, max_height;
767 int cur_width, cur_height;
768
769 min_width = gst_value_get_int_range_min (width);
770 max_width = gst_value_get_int_range_max (width);
771 min_height = gst_value_get_int_range_min (height);
772 max_height = gst_value_get_int_range_max (height);
773
774 cur_width = min_width;
775 cur_height = min_height;
776 while (cur_width <= max_width && cur_height <= max_height) {
777 WebcamVidFormat video_format;
778
779 video_format.mimetype =
780 g_strdup (gst_structure_get_name (structure));
781 video_format.width = cur_width;
782 video_format.height = cur_height;
783 addSupportedFormat(cam, &video_format, structure);
784 cur_width *= 2;
785 cur_height *= 2;
786 }
787
788 cur_width = max_width;
789 cur_height = max_height;
790 while (cur_width > min_width && cur_height > min_height) {
791 WebcamVidFormat video_format;
792
793 video_format.mimetype =
794 g_strdup (gst_structure_get_name (structure));
795 video_format.width = cur_width;
796 video_format.height = cur_height;
797 addSupportedFormat(cam, &video_format, structure);
798 cur_width /= 2;
799 cur_height /= 2;
800 }
801 }
802 else {
803 log_error(_("%s: type %s, cannot be handled for resolution width"),
804 __FUNCTION__, G_VALUE_TYPE_NAME (const_cast<GValue *>(width)));
805 }
806 }
807 }
808
809 //move the selected camera information to a more robust data structure
810 //to store pipeline-ing information
811 bool
setWebcam(size_t dev_select)812 VideoInputGst::setWebcam(size_t dev_select)
813 {
814 assert(dev_select < _vidVect.size());
815
816 GnashWebcamPrivate *webcam = new GnashWebcamPrivate;
817 if (webcam) {
818 webcam->setWebcamDevice(_vidVect[dev_select]);
819 const char* name = _vidVect[dev_select]->getProductName();
820 assert(name);
821 _name = name;
822 _globalWebcam = webcam;
823 } else {
824 log_error(_("%s: was passed a NULL pointer"), __FUNCTION__);
825 }
826 return webcam;
827 }
828
829 //create a bin containing the source and a connector ghostpad
830 gboolean
webcamCreateSourceBin()831 VideoInputGst::webcamCreateSourceBin()
832 {
833 GError *error = nullptr;
834 gchar *command = nullptr;
835
836 GnashWebcamPrivate* webcam = _globalWebcam;
837
838 if(webcam->_webcamDevice == nullptr) {
839 log_debug("%s: You don't have any webcams chosen, using videotestsrc",
840 __FUNCTION__);
841 webcam->_webcamSourceBin = gst_parse_bin_from_description (
842 "videotestsrc name=video_source ! capsfilter name=capsfilter",
843 TRUE, &error);
844 log_debug("Command: videotestsrc name=video_source ! \
845 capsfilter name=capsfilter");
846 }
847 else {
848 WebcamVidFormat *format = nullptr;
849
850 std::ostringstream ss;
851 ss << _width << 'x' << _height;
852 const std::string& res = ss.str();
853
854 //use these resolutions determined above if the camera supports it
855 if (_width != 0 && _height != 0) {
856
857 int i = GPOINTER_TO_INT(g_hash_table_lookup
858 (webcam->_webcamDevice->supportedResolutions, res.c_str()));
859 //the selected res is supported if i
860 if (i) {
861 format = &g_array_index (webcam->_webcamDevice->videoFormats,
862 WebcamVidFormat, i - 1);
863 }
864 }
865
866 //if format didn't get set, something went wrong. try picking
867 //the first supported format and a different supported resolution
868 if (!format) {
869 format = &g_array_index (webcam->_webcamDevice->videoFormats,
870 WebcamVidFormat, 0);
871 for (int i = 1; i < webcam->_webcamDevice->numVideoFormats; ++i) {
872
873 if (g_array_index (webcam->_webcamDevice->videoFormats,
874 WebcamVidFormat, i).width <= format->width){
875 format = &g_array_index (webcam->_webcamDevice->videoFormats,
876 WebcamVidFormat, i);
877 }
878 }
879 }
880
881 webcam->_currentFormat = format;
882
883 //if format isn't set, something is still going wrong, make generic
884 //components and see if they work!
885 if (format == nullptr) {
886 if (error != nullptr) {
887 g_error_free (error);
888 error = nullptr;
889 }
890 webcam->_webcamSourceBin =
891 gst_parse_bin_from_description ("videotestsrc name=video_source",
892 TRUE, &error);
893 webcam->_videoSource =
894 gst_bin_get_by_name (GST_BIN (webcam->_webcamSourceBin),
895 "video_source");
896
897 //if there are still errors, something's up, return out of function
898 if (error != nullptr) {
899 g_error_free (error);
900 return false;
901 }
902 webcam->_capsFilter =
903 gst_bin_get_by_name (GST_BIN (webcam->_webcamSourceBin),
904 "capsfilter");
905 return true;
906 }
907
908 //execution here means we're good to make the pipeline
909 else {
910 //can't reduce this to 80 line limit without causing problems
911 command = g_strdup_printf (
912 "%s name=video_source device=%s ! capsfilter name=capsfilter caps=video/x-raw-rgb,width=%d,height=%d,framerate=%d/%d;video/x-raw-yuv,width=%d,height=%d,framerate=%d/%d",
913 webcam->_webcamDevice->getGstreamerSrc(),
914 webcam->_webcamDevice->getDevLocation(),
915 format->width,
916 format->height,
917 format->highestFramerate.numerator,
918 format->highestFramerate.denominator,
919 format->width,
920 format->height,
921 format->highestFramerate.numerator,
922 format->highestFramerate.denominator);
923
924 //debug
925 log_debug("GstPipeline command is: %s", command);
926
927 webcam->_webcamSourceBin =
928 gst_parse_bin_from_description (command, TRUE, &error);
929 if (webcam->_webcamSourceBin == nullptr) {
930 log_error(_("%s: Creation of the webcam_source_bin failed"),
931 __FUNCTION__);
932 log_error(_("the error was %s"), error->message);
933 return false;
934 }
935
936 //set _currentFps value for actionscript
937 _currentFPS = (format->highestFramerate.numerator /
938 format->highestFramerate.denominator);
939
940 g_free(command);
941
942 webcam->_videoSource =
943 gst_bin_get_by_name (GST_BIN (webcam->_webcamSourceBin),
944 "video_source");
945 webcam->_capsFilter =
946 gst_bin_get_by_name (GST_BIN (webcam->_webcamSourceBin),
947 "capsfilter");
948 return true;
949 }
950 }
951 return true;
952 }
953
954 gboolean
checkForSupportedFramerate(GnashWebcamPrivate * webcam,int fps)955 VideoInputGst::checkForSupportedFramerate(GnashWebcamPrivate *webcam,
956 int fps)
957 {
958
959 if (!webcam) {
960 log_error(_("%s: webcam isn't set!"), __FUNCTION__);
961 }
962 for (int i = 0; i < webcam->_currentFormat->numFramerates; ++i) {
963 int val = std::ceil(static_cast<double>(
964 webcam->_currentFormat->framerates[i].numerator /
965 webcam->_currentFormat->framerates[i].denominator));
966 if (val == fps) {
967 return true;
968 }
969 }
970 return false;
971 }
972
973 gboolean
webcamChangeSourceBin()974 VideoInputGst::webcamChangeSourceBin()
975 {
976 GError *error = nullptr;
977 gchar *command = nullptr;
978
979 assert(_globalWebcam);
980
981 if (_globalWebcam->_pipelineIsPlaying == true) {
982 stop();
983 }
984
985 //delete the old source bin
986 gst_bin_remove(GST_BIN(_globalWebcam->_webcamMainBin),
987 _globalWebcam->_webcamSourceBin);
988 _globalWebcam->_webcamSourceBin = nullptr;
989
990 GnashWebcamPrivate* webcam = _globalWebcam;
991
992 if(webcam->_webcamDevice == nullptr) {
993 log_debug("%s: You don't have any webcams chosen, using videotestsrc",
994 __FUNCTION__);
995 webcam->_webcamSourceBin = gst_parse_bin_from_description (
996 "videotestsrc name=video_source ! capsfilter name=capsfilter",
997 TRUE, &error);
998 log_debug("Command: videotestsrc name=video_source ! \
999 capsfilter name=capsfilter");
1000 }
1001 else {
1002 WebcamVidFormat *format = nullptr;
1003
1004 std::ostringstream ss;
1005 ss << _width << 'x' << _height;
1006 const std::string& res = ss.str();
1007
1008 //use these resolutions determined above if the camera supports it
1009 if (_width != 0 && _height != 0) {
1010
1011 int i = GPOINTER_TO_INT(g_hash_table_lookup
1012 (webcam->_webcamDevice->supportedResolutions, res.c_str()));
1013 //the selected res is supported if i
1014 if (i) {
1015 format = &g_array_index (webcam->_webcamDevice->videoFormats,
1016 WebcamVidFormat, i - 1);
1017 }
1018 }
1019
1020 //if format didn't get set, something went wrong. try picking
1021 //the first supported format and a different supported resolution
1022 if (!format) {
1023 log_error(_("%s: the resolution you chose isn't supported, picking"
1024 " a supported value"), __FUNCTION__);
1025 format = &g_array_index (webcam->_webcamDevice->videoFormats,
1026 WebcamVidFormat, 0);
1027
1028 for (int i = 1; i < webcam->_webcamDevice->numVideoFormats; ++i) {
1029 if (g_array_index (webcam->_webcamDevice->videoFormats,
1030 WebcamVidFormat, i).width <= format->width){
1031 format = &g_array_index (webcam->_webcamDevice->videoFormats,
1032 WebcamVidFormat, i);
1033 }
1034 }
1035 }
1036
1037 //check here to make sure the fps value is supported (only valid for
1038 //non test sources)
1039 if (strcmp(webcam->_webcamDevice->getGstreamerSrc(), "videotestsrc") == 0) {
1040 int newFps = _fps;
1041 if (checkForSupportedFramerate(webcam, newFps)) {
1042 log_debug("checkforsupportedfr returned true");
1043 format->highestFramerate.numerator = newFps;
1044 format->highestFramerate.denominator = 1;
1045 } else {
1046 log_debug("checkforsupportedfr returned false");
1047
1048 //currently chooses the ActionScript default of 15 fps in case
1049 //you pass in an unsupported framerate value
1050 format->highestFramerate.numerator = 15;
1051 format->highestFramerate.denominator = 1;
1052 }
1053 }
1054 webcam->_currentFormat = format;
1055
1056 //if format isn't set, something is still going wrong, make generic
1057 //components and see if they work!
1058 if (format == nullptr) {
1059 if (error != nullptr) {
1060 g_error_free (error);
1061 error = nullptr;
1062 }
1063 webcam->_webcamSourceBin =
1064 gst_parse_bin_from_description ("videotestsrc name=video_source",
1065 TRUE, &error);
1066 webcam->_videoSource =
1067 gst_bin_get_by_name (GST_BIN (webcam->_webcamSourceBin),
1068 "video_source");
1069
1070 //if there are still errors, something's up, return out of function
1071 if (error != nullptr) {
1072 g_error_free (error);
1073 return false;
1074 }
1075 webcam->_capsFilter =
1076 gst_bin_get_by_name (GST_BIN (webcam->_webcamSourceBin),
1077 "capsfilter");
1078 return true;
1079 }
1080
1081 //execution here means we're good to make the pipeline
1082 else {
1083 //can't reduce this to 80 line limit without causing problems
1084 command = g_strdup_printf (
1085 "%s name=video_source device=%s ! capsfilter name=capsfilter caps=video/x-raw-rgb,width=%d,height=%d,framerate=%d/%d;video/x-raw-yuv,width=%d,height=%d,framerate=%d/%d",
1086 webcam->_webcamDevice->getGstreamerSrc(),
1087 webcam->_webcamDevice->getDevLocation(),
1088 format->width,
1089 format->height,
1090 format->highestFramerate.numerator,
1091 format->highestFramerate.denominator,
1092 format->width,
1093 format->height,
1094 format->highestFramerate.numerator,
1095 format->highestFramerate.denominator);
1096
1097 //debug
1098 log_debug ("GstPipeline command is: %s", command);
1099
1100 webcam->_webcamSourceBin =
1101 gst_parse_bin_from_description (command, TRUE, &error);
1102 if (webcam->_webcamSourceBin == nullptr) {
1103 log_error(_("%s: Creation of the webcam_source_bin failed"),
1104 __FUNCTION__);
1105 log_error(_("the error was %s"), error->message);
1106 return false;
1107 }
1108
1109 g_free(command);
1110
1111 //set _currentFps for actionscript
1112 _currentFPS = (format->highestFramerate.numerator /
1113 format->highestFramerate.denominator);
1114
1115 webcam->_videoSource =
1116 gst_bin_get_by_name (GST_BIN (webcam->_webcamSourceBin),
1117 "video_source");
1118 webcam->_capsFilter =
1119 gst_bin_get_by_name (GST_BIN (webcam->_webcamSourceBin),
1120 "capsfilter");
1121
1122 //drop the new source bin back into the main bin
1123 gboolean result;
1124 result = gst_bin_add(GST_BIN(webcam->_webcamMainBin),
1125 webcam->_webcamSourceBin);
1126 if (result != true) {
1127 log_error(_("%s: couldn't drop the sourcebin back into the main bin"),
1128 __FUNCTION__);
1129 return false;
1130 } else {
1131 //get the tee from main bin
1132 GstElement *tee = gst_bin_get_by_name(GST_BIN(webcam->_webcamMainBin),
1133 "tee");
1134 result = gst_element_link(webcam->_webcamSourceBin, tee);
1135 if (result != true) {
1136 log_error(_("%s: couldn't link up sourcebin and tee"), __FUNCTION__);
1137 return false;
1138 } else {
1139 return true;
1140 }
1141 }
1142 }
1143 }
1144 return true;
1145 }
1146
1147 //create a display bin that has ghostpads which allow display-to-screen
1148 //capabilities as well as save-to-file or buffer capabilities (both
1149 //implemented as bin ghostpads)
1150 gboolean
webcamCreateMainBin()1151 VideoInputGst::webcamCreateMainBin()
1152 {
1153 GstElement *tee, *video_display_queue, *save_queue;
1154 gboolean ok;
1155 GstPad *pad;
1156
1157 GnashWebcamPrivate* webcam = _globalWebcam;
1158
1159 //initialize a new GST pipeline
1160 webcam->_pipeline = gst_pipeline_new("pipeline");
1161 assert(webcam->_pipeline);
1162
1163 webcam->_webcamMainBin = gst_bin_new ("webcam_main_bin");
1164 assert(webcam->_webcamMainBin);
1165
1166 ok = webcamCreateSourceBin();
1167 if (ok != true) {
1168 log_error(_("%s: problem creating source bin"), __FUNCTION__);
1169 return false;
1170 }
1171
1172 assert(webcam->_webcamSourceBin);
1173
1174 if ((tee = gst_element_factory_make ("tee", "tee")) == nullptr) {
1175 log_error(_("%s: problem creating tee element"), __FUNCTION__);
1176 return false;
1177 }
1178 if ((save_queue = gst_element_factory_make("queue", "save_queue")) == nullptr) {
1179 log_error(_("%s: problem creating save_queue element"), __FUNCTION__);
1180 return false;
1181 }
1182 if ((video_display_queue =
1183 gst_element_factory_make("queue", "video_display_queue")) == nullptr) {
1184 log_error(_("%s: problem creating video_display_queue element"),
1185 __FUNCTION__);
1186 return false;
1187 }
1188
1189 //add created elements to a bin
1190 gst_bin_add_many (GST_BIN (webcam->_webcamMainBin), webcam->_webcamSourceBin,
1191 tee, save_queue, video_display_queue, NULL);
1192
1193 ok = gst_element_link(webcam->_webcamSourceBin, tee);
1194 if (ok != true) {
1195 log_error(_("%s: couldn't link webcam_source_bin and tee"),
1196 __FUNCTION__);
1197 return false;
1198 }
1199
1200 ok &= gst_element_link_many (tee, save_queue, NULL);
1201 if (ok != true) {
1202 log_error(_("%s: couldn't link tee and save_queue"), __FUNCTION__);
1203 return false;
1204 }
1205
1206 ok &= gst_element_link_many (tee, video_display_queue, NULL);
1207 if (ok != true) {
1208 log_error(_("%s: couldn't link tee and video_display_queue"), __FUNCTION__);
1209 return false;
1210 }
1211
1212 gst_bin_add (GST_BIN(webcam->_pipeline), webcam->_webcamMainBin);
1213
1214 //add ghostpad to save_queue (allows connections between bins)
1215 pad = gst_element_get_pad (save_queue, "src");
1216 if (pad == nullptr) {
1217 log_error(_("%s: couldn't get save_queue_src_pad"), __FUNCTION__);
1218 return false;
1219 }
1220 gst_element_add_pad (webcam->_webcamMainBin,
1221 gst_ghost_pad_new ("save_queue_src", pad));
1222 gst_object_unref (GST_OBJECT (pad));
1223
1224 //add ghostpad to video_display_queue
1225 pad = gst_element_get_pad (video_display_queue, "src");
1226 if (pad == nullptr) {
1227 log_error(_("%s: couldn't get video_display_queue_pad"), __FUNCTION__);
1228 return false;
1229 }
1230 gst_element_add_pad (webcam->_webcamMainBin,
1231 gst_ghost_pad_new ("video_display_queue_src", pad));
1232 gst_object_unref (GST_OBJECT (pad));
1233
1234 assert(webcam->_videoSource);
1235 assert(_devSelection == 0 || webcam->_capsFilter);
1236 assert(_devSelection == 0 || webcam->_currentFormat);
1237
1238 if (!ok) {
1239 log_error(_("%s: Unable to create main pipeline"), __FUNCTION__);
1240 return false;
1241 }
1242 return true;
1243 }
1244
1245 gboolean
webcamCreateDisplayBin()1246 VideoInputGst::webcamCreateDisplayBin()
1247 {
1248 GstElement *video_scale, *video_sink;
1249 gboolean ok;
1250 GstPad *pad;
1251
1252 GnashWebcamPrivate* webcam = _globalWebcam;
1253
1254 webcam->_videoDisplayBin = gst_bin_new("video_display_bin");
1255
1256 if (webcam->_videoDisplayBin == nullptr) {
1257 log_error(_("%s: something went wrong creating the new video_display_bin"),
1258 __FUNCTION__);
1259 return false;
1260 }
1261
1262 if ((video_scale = gst_element_factory_make("videoscale", "video_scale")) == nullptr) {
1263 log_error(_("%s: problem creating video_scale element"), __FUNCTION__);
1264 return false;
1265 }
1266 else {
1267 //set bilinear scaling
1268 g_object_set (video_scale, "method", 1, NULL);
1269 }
1270
1271 if ((video_sink = gst_element_factory_make("autovideosink", "video_sink")) == nullptr) {
1272 log_error(_("%s: problem creating the video_sink element"), __FUNCTION__);
1273 return false;
1274 }
1275
1276 //add created elements to a bin
1277 gst_bin_add_many (GST_BIN (webcam->_videoDisplayBin), video_scale, video_sink, NULL);
1278
1279 ok = gst_element_link_many(video_scale, video_sink, NULL);
1280 if (ok != true) {
1281 log_error(_("%s: something went wrong in linking elements in video_display_bin"),
1282 __FUNCTION__);
1283 return false;
1284 }
1285
1286 //create ghostpad which can be used to connect this bin to the
1287 //video_display_queue src ghostpad
1288 pad = gst_element_get_pad (video_scale, "sink");
1289 gst_element_add_pad (webcam->_videoDisplayBin, gst_ghost_pad_new ("sink", pad));
1290 gst_object_unref (GST_OBJECT (pad));
1291
1292 assert(webcam->_videoDisplayBin);
1293
1294 return true;
1295 }
1296
1297 //make link between display_queue src ghostpad in main_bin and
1298 //the elements necessary to display video to screen (_videoDisplayBin)
1299 gboolean
webcamMakeVideoDisplayLink()1300 VideoInputGst::webcamMakeVideoDisplayLink()
1301 {
1302
1303 GnashWebcamPrivate* webcam = _globalWebcam;
1304
1305 if (gst_bin_get_by_name(GST_BIN(webcam->_pipeline), "video_display_bin") == nullptr) {
1306 gst_object_ref(webcam->_videoDisplayBin);
1307 gst_bin_add (GST_BIN(webcam->_pipeline), webcam->_videoDisplayBin);
1308 }
1309
1310 GstPad *video_display_queue_src, *video_display_bin_sink;
1311
1312 video_display_queue_src = gst_element_get_pad(webcam->_webcamMainBin,
1313 "video_display_queue_src");
1314 video_display_bin_sink = gst_element_get_pad(webcam->_videoDisplayBin,
1315 "sink");
1316
1317 GstPadLinkReturn padreturn;
1318 padreturn = gst_pad_link(video_display_queue_src, video_display_bin_sink);
1319
1320 if (padreturn == GST_PAD_LINK_OK) {
1321 return true;
1322 } else {
1323 log_error(_("something went wrong in the make_video_display_link function"));
1324 return false;
1325 }
1326 }
1327
1328 //break the link that displays the webcam video to the screen
1329 gboolean
webcamBreakVideoDisplayLink()1330 VideoInputGst::webcamBreakVideoDisplayLink()
1331 {
1332 GnashWebcamPrivate* webcam = _globalWebcam;
1333
1334 if (webcam->_pipelineIsPlaying == true) {
1335 GstStateChangeReturn state;
1336 state = gst_element_set_state(webcam->_pipeline, GST_STATE_NULL);
1337 if (state != GST_STATE_CHANGE_FAILURE) {
1338 webcam->_pipelineIsPlaying = false;
1339 } else {
1340 return false;
1341 }
1342 }
1343
1344 gboolean ok;
1345 GstPad *videoDisplayQueueSrc, *videoDisplayBinSink;
1346
1347 videoDisplayQueueSrc = gst_element_get_pad(webcam->_webcamMainBin,
1348 "video_display_queue_src");
1349 videoDisplayBinSink = gst_element_get_pad(webcam->_videoDisplayBin,
1350 "sink");
1351
1352 ok = gst_pad_unlink(videoDisplayQueueSrc, videoDisplayBinSink);
1353
1354 if (ok != true) {
1355 log_error(_("%s: the unlinking of the pads failed"), __FUNCTION__);
1356 return false;
1357 } else {
1358 return true;
1359 }
1360 }
1361
1362 //make link to saveQueue in main bin
1363 gboolean
webcamMakeVideoSaveLink()1364 VideoInputGst::webcamMakeVideoSaveLink()
1365 {
1366 GnashWebcamPrivate* webcam = _globalWebcam;
1367
1368 if (gst_bin_get_by_name(GST_BIN(webcam->_pipeline), "video_save_bin") == nullptr) {
1369 gst_object_ref(webcam->_videoSaveBin);
1370 gst_bin_add(GST_BIN(webcam->_pipeline), webcam->_videoSaveBin);
1371 }
1372
1373 //linking
1374 GstPad *video_save_queue_src, *video_save_sink;
1375
1376 video_save_queue_src = gst_element_get_pad(webcam->_webcamMainBin, "save_queue_src");
1377 video_save_sink = gst_element_get_pad(webcam->_videoSaveBin, "sink");
1378
1379 GstPadLinkReturn padreturn;
1380 padreturn = gst_pad_link(video_save_queue_src, video_save_sink);
1381
1382 if (padreturn == GST_PAD_LINK_OK) {
1383 return true;
1384 } else {
1385 log_error(_("%s: something went wrong in the make_video_display_link function"),
1386 __FUNCTION__);
1387 return false;
1388 }
1389 }
1390
1391 //break link to saveQueue in main bin
1392 gboolean
webcamBreakVideoSaveLink()1393 VideoInputGst::webcamBreakVideoSaveLink()
1394 {
1395 GnashWebcamPrivate* webcam = _globalWebcam;
1396
1397 if (webcam->_pipelineIsPlaying == true) {
1398 GstStateChangeReturn state;
1399 state = gst_element_set_state(webcam->_pipeline, GST_STATE_NULL);
1400 if (state != GST_STATE_CHANGE_FAILURE) {
1401 webcam->_pipelineIsPlaying = false;
1402 } else {
1403 return false;
1404 }
1405 }
1406 gboolean ok;
1407 GstPad *videoSaveQueueSrc, *videoSaveSink;
1408 GstStateChangeReturn state;
1409 videoSaveQueueSrc = gst_element_get_pad(webcam->_webcamMainBin,
1410 "save_queue_src");
1411 videoSaveSink = gst_element_get_pad(webcam->_videoSaveBin, "sink");
1412
1413 ok = gst_pad_unlink(videoSaveQueueSrc, videoSaveSink);
1414 if (ok != true) {
1415 log_error(_("%s: unlink failed"), __FUNCTION__);
1416 return false;
1417 } else {
1418 state = gst_element_set_state(webcam->_videoSaveBin, GST_STATE_NULL);
1419 if (state != GST_STATE_CHANGE_FAILURE) {
1420 ok = gst_bin_remove(GST_BIN(webcam->_pipeline), webcam->_videoSaveBin);
1421 if (ok != true) {
1422 log_error(_("%s: couldn't remove saveBin from pipeline"),
1423 __FUNCTION__);
1424 return false;
1425 } else {
1426 return true;
1427 }
1428 } else {
1429 log_error(_("%s: videoSaveBin state change failed"), __FUNCTION__);
1430 return false;
1431 }
1432 }
1433 }
1434
1435 //create a bin to take the video stream and dump it out to
1436 //an ogg file
1437 gboolean
webcamCreateSaveBin()1438 VideoInputGst::webcamCreateSaveBin()
1439 {
1440 GstElement *video_save_csp, *video_save_rate, *video_save_scale, *video_enc;
1441 GstElement *mux;
1442 GstPad *pad;
1443 gboolean ok;
1444
1445 GnashWebcamPrivate* webcam = _globalWebcam;
1446
1447 webcam->_videoSaveBin = gst_bin_new ("video_save_bin");
1448
1449 if ((video_save_csp =
1450 gst_element_factory_make("ffmpegcolorspace", "video_save_csp"))
1451 == nullptr) {
1452 log_error(_("%s: problem with creating video_save_csp element"),
1453 __FUNCTION__);
1454 return false;
1455 }
1456 if ((video_enc = gst_element_factory_make("theoraenc", "video_enc")) == nullptr) {
1457 log_error(_("%s: problem with creating video_enc element"), __FUNCTION__);
1458 return false;
1459 } else {
1460 g_object_set (video_enc, "keyframe-force", 1, NULL);
1461 }
1462
1463 if ((video_save_rate = gst_element_factory_make("videorate", "video_save_rate")) == nullptr) {
1464 log_error(_("%s: problem with creating video_save_rate element"), __FUNCTION__);
1465 return false;
1466 }
1467 if ((video_save_scale = gst_element_factory_make("videoscale", "video_save_scale")) == nullptr) {
1468 log_error(_("%s: problem with creating video_save_scale element"), __FUNCTION__);
1469 return false;
1470 } else {
1471 //Use bilinear scaling
1472 g_object_set (video_save_scale, "method", 1, NULL);
1473 }
1474 if ((mux = gst_element_factory_make("oggmux", "mux")) == nullptr) {
1475 log_error(_("%s: problem with creating mux element"), __FUNCTION__);
1476 return false;
1477 }
1478 if ((webcam->_videoFileSink = gst_element_factory_make("filesink", "video_file_sink")) == nullptr) {
1479 log_error(_("%s: problem with creating video_file_sink element"), __FUNCTION__);
1480 return false;
1481 } else {
1482 g_object_set(webcam->_videoFileSink, "location", "vidoutput.ogg", NULL);
1483 }
1484
1485 //add created elements to the video_save_bin in the datastructure
1486 gst_bin_add_many (GST_BIN (webcam->_videoSaveBin), video_save_csp,
1487 video_save_rate, video_save_scale, video_enc, mux, webcam->_videoFileSink,
1488 NULL);
1489
1490 //add ghostpad
1491 pad = gst_element_get_pad (video_save_csp, "sink");
1492 gst_element_add_pad (webcam->_videoSaveBin, gst_ghost_pad_new ("sink", pad));
1493 gst_object_unref (GST_OBJECT (pad));
1494
1495 ok = gst_element_link_many (video_save_csp, video_save_rate,
1496 video_save_scale, video_enc, mux, webcam->_videoFileSink, NULL);
1497
1498 if (ok != true) {
1499 log_error(_("%s: there was some problem in linking!"), __FUNCTION__);
1500 }
1501 return true;
1502 }
1503
1504 //to handle messages while the main capture loop is running
1505 gboolean
bus_call(GstBus *,GstMessage * msg,gpointer)1506 bus_call (GstBus * /*bus*/, GstMessage *msg, gpointer /*data*/)
1507 {
1508 switch (GST_MESSAGE_TYPE (msg)) {
1509
1510 case GST_MESSAGE_EOS:
1511 log_debug ("End of stream");
1512 break;
1513
1514 case GST_MESSAGE_ERROR: {
1515 gchar *debug;
1516 GError *error;
1517
1518 gst_message_parse_error (msg, &error, &debug);
1519 g_free (debug);
1520
1521 log_error("%s", error->message);
1522 g_error_free (error);
1523
1524 break;
1525 }
1526 default:
1527 break;
1528 }
1529
1530 return TRUE;
1531 }
1532
1533 //start the pipeline and run the g_main_loop
1534 bool
play()1535 VideoInputGst::play()
1536 {
1537 GnashWebcamPrivate* webcam = _globalWebcam;
1538 assert(_globalWebcam);
1539
1540 GstStateChangeReturn state;
1541 GstBus *bus;
1542 //setup bus to watch pipeline for messages
1543 bus = gst_pipeline_get_bus (GST_PIPELINE (webcam->_pipeline));
1544 gst_bus_add_watch (bus, bus_call, webcam);
1545 gst_object_unref (bus);
1546
1547 state = gst_element_set_state (webcam->_pipeline, GST_STATE_PLAYING);
1548
1549 if (state != GST_STATE_CHANGE_FAILURE) {
1550 webcam->_pipelineIsPlaying = true;
1551 return true;
1552 }
1553
1554 return false;
1555 }
1556
1557 bool
stop()1558 VideoInputGst::stop()
1559 {
1560 GnashWebcamPrivate* webcam = _globalWebcam;
1561 GstStateChangeReturn state;
1562
1563 state = gst_element_set_state (webcam->_pipeline, GST_STATE_NULL);
1564 if (state != GST_STATE_CHANGE_FAILURE) {
1565 webcam->_pipelineIsPlaying = FALSE;
1566 return true;
1567 } else {
1568 return false;
1569 }
1570 }
1571
1572 /// Constructor for the WebcamVidFormat class. This constructor prepares
1573 /// the data structure for data that will come in later. All gint values
1574 /// are initialized to -1 to show that these values have never been set.
1575 ///
WebcamVidFormat()1576 WebcamVidFormat::WebcamVidFormat() {
1577 width = -1;
1578 height = -1;
1579 numFramerates = -1;
1580 framerates = nullptr;
1581 }
1582
1583 /// Default constructor for the FramerateFraction class. This constructor prepares
1584 /// the data structure for data that will come in later. All gint values
1585 /// are initialized to -1 to show that these values have never been set.
FramerateFraction()1586 FramerateFraction::FramerateFraction() {
1587 numerator = -1;
1588 denominator = -1;
1589 }
1590
1591 /// Secondary constructor for the FramerateFraction class. This constructor
1592 /// initialzes the structure with the numerator and denominator values passed
1593 /// to the constructor.
FramerateFraction(gint num,gint denom)1594 FramerateFraction::FramerateFraction(gint num, gint denom) {
1595 numerator = num;
1596 denominator = denom;
1597 }
1598
1599 /// Constructor for the GnashWebcam class. This constructor prepares the data
1600 /// structure for data that will come in later. Also creates a blank hash table
1601 /// and array.
GnashWebcam()1602 GnashWebcam::GnashWebcam() {
1603 setElementPtr(nullptr);
1604 supportedResolutions = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, nullptr);
1605 videoFormats = g_array_new (FALSE, FALSE, sizeof (WebcamVidFormat));
1606 numVideoFormats = 0;
1607 }
1608
1609 /// Constructor that initializes all GnashWebcamPrivate variables to have
1610 /// data dropped in later.
GnashWebcamPrivate()1611 GnashWebcamPrivate::GnashWebcamPrivate() {
1612 _pipeline = nullptr;
1613 _webcamSourceBin = nullptr;
1614 _webcamMainBin = nullptr;
1615 _videoDisplayBin = nullptr;
1616 _videoSaveBin = nullptr;
1617 _videoSource = nullptr;
1618 _capsFilter = nullptr;
1619 _videoFileSink = nullptr;
1620 _videoEnc = nullptr;
1621
1622 _pipelineIsPlaying = false;
1623 _webcamDevice = nullptr;
1624
1625 _currentFormat = nullptr;
1626 _eosTimeoutId = 0;
1627 };
1628
1629
1630 } //gst namespace
1631 } //media namespace
1632 } //gnash namespace
1633