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