• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * GStreamer
3  * Copyright (C) 2010 Texas Instruments, Inc
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Library General Public
7  * License as published by the Free Software Foundation; either
8  * version 2 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Library General Public License for more details.
14  *
15  * You should have received a copy of the GNU Library General Public
16  * License along with this library; if not, write to the
17  * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
18  * Boston, MA 02110-1301, USA.
19  */
20 
21 
22 
23 /**
24  * SECTION:element-basecamerasrc
25  *
26  * Base class for the camera source bin used by camerabin for capture.
27  * Sophisticated camera hardware can derive from this baseclass and map the
28  * features to this interface.
29  *
30  * The design mandates that the subclasses implement the following features and
31  * behaviour:
32  *
33  * * 3 pads: viewfinder, image capture, video capture
34  *
35  * During `construct_pipeline()` vmethod a subclass can add several elements into
36  * the bin and expose 3 srcs pads as ghostpads implementing the 3 pad templates.
37  *
38  * However the subclass is responsible for adding the pad templates for the
39  * source pads and they must be named "vidsrc", "imgsrc" and "vfsrc". The pad
40  * templates should be installed in the subclass' class_init function, like so:
41  * |[
42  * static void
43  * my_element_class_init (GstMyElementClass *klass)
44  * {
45  *   GstElementClass *gstelement_class = GST_ELEMENT_CLASS (klass);
46  *   // pad templates should be a #GstStaticPadTemplate with direction
47  *   // #GST_PAD_SRC and name "vidsrc", "imgsrc" and "vfsrc"
48  *   gst_element_class_add_static_pad_template (gstelement_class,
49  *       &vidsrc_template);
50  *   gst_element_class_add_static_pad_template (gstelement_class,
51  *       &imgsrc_template);
52  *   gst_element_class_add_static_pad_template (gstelement_class,
53  *       &vfsrc_template);
54  *   // see #GstElementDetails
55  *   gst_element_class_set_details (gstelement_class, &details);
56  * }
57  * ]|
58  *
59  * It is also possible to add regular pads from the subclass and implement the
60  * dataflow methods on these pads. This way all functionality can be implemented
61  * directly in the subclass without extra elements.
62  *
63  * The src will receive the capture mode from `GstCameraBin2` on the
64  * #GstBaseCameraSrc:mode property. Possible capture modes are defined in
65  * #GstCameraBinMode.
66  */
67 
68 
69 #ifdef HAVE_CONFIG_H
70 #  include <config.h>
71 #endif
72 
73 #include <gst/glib-compat-private.h>
74 #include "gstbasecamerasrc.h"
75 
76 #define DEFAULT_WIDTH 640
77 #define DEFAULT_HEIGHT 480
78 #define DEFAULT_CAPTURE_WIDTH 800
79 #define DEFAULT_CAPTURE_HEIGHT 600
80 #define DEFAULT_FPS_N 0         /* makes it use the default */
81 #define DEFAULT_FPS_D 1
82 #define DEFAULT_ZOOM MIN_ZOOM
83 
84 enum
85 {
86   PROP_0,
87   PROP_MODE,
88   PROP_ZOOM,
89   PROP_MAX_ZOOM,
90   PROP_READY_FOR_CAPTURE,
91   PROP_POST_PREVIEW,
92   PROP_PREVIEW_CAPS,
93   PROP_PREVIEW_FILTER,
94   PROP_AUTO_START
95 };
96 
97 enum
98 {
99   /* action signals */
100   START_CAPTURE_SIGNAL,
101   STOP_CAPTURE_SIGNAL,
102   /* emit signals */
103   LAST_SIGNAL
104 };
105 
106 #define DEFAULT_POST_PREVIEW TRUE
107 #define DEFAULT_AUTO_START FALSE
108 
109 static guint basecamerasrc_signals[LAST_SIGNAL];
110 
111 GST_DEBUG_CATEGORY (base_camera_src_debug);
112 #define GST_CAT_DEFAULT base_camera_src_debug
113 
114 #define parent_class gst_base_camera_src_parent_class
115 G_DEFINE_TYPE (GstBaseCameraSrc, gst_base_camera_src, GST_TYPE_BIN);
116 
117 
118 /* NOTE: we could provide a vmethod for derived class to overload to provide
119  * it's own implementation of interface..  but in all cases I can think of at
120  * moment, either the camerasrc itself, or some element within the bin, will
121  * be implementing the interface..
122  */
123 
124 /**
125  * gst_base_camera_src_set_mode:
126  * @self: the camerasrc bin
127  * @mode: the mode
128  *
129  * Set the chosen #GstCameraBinMode capture mode.
130  */
131 gboolean
gst_base_camera_src_set_mode(GstBaseCameraSrc * self,GstCameraBinMode mode)132 gst_base_camera_src_set_mode (GstBaseCameraSrc * self, GstCameraBinMode mode)
133 {
134   GstBaseCameraSrcClass *bclass = GST_BASE_CAMERA_SRC_GET_CLASS (self);
135 
136   g_return_val_if_fail (bclass->set_mode, FALSE);
137 
138   if (bclass->set_mode (self, mode)) {
139     self->mode = mode;
140     return TRUE;
141   }
142   return FALSE;
143 }
144 
145 /**
146  * gst_base_camera_src_setup_zoom:
147  * @self: camerasrc object
148  *
149  * Apply zoom configured to camerabin to capture.
150  */
151 void
gst_base_camera_src_setup_zoom(GstBaseCameraSrc * self)152 gst_base_camera_src_setup_zoom (GstBaseCameraSrc * self)
153 {
154   GstBaseCameraSrcClass *bclass = GST_BASE_CAMERA_SRC_GET_CLASS (self);
155 
156   g_return_if_fail (self->zoom);
157   g_return_if_fail (bclass->set_zoom);
158 
159   bclass->set_zoom (self, self->zoom);
160 }
161 
162 /**
163  * gst_base_camera_src_setup_preview:
164  * @self: camerasrc bin
165  * @preview_caps: preview caps to set
166  *
167  * Apply preview caps to preview pipeline and to video source.
168  */
169 void
gst_base_camera_src_setup_preview(GstBaseCameraSrc * self,GstCaps * preview_caps)170 gst_base_camera_src_setup_preview (GstBaseCameraSrc * self,
171     GstCaps * preview_caps)
172 {
173   GstBaseCameraSrcClass *bclass = GST_BASE_CAMERA_SRC_GET_CLASS (self);
174 
175   if (self->preview_pipeline) {
176     GST_DEBUG_OBJECT (self,
177         "Setting preview pipeline caps %" GST_PTR_FORMAT, self->preview_caps);
178     gst_camerabin_preview_set_caps (self->preview_pipeline, preview_caps);
179   }
180 
181   if (bclass->set_preview)
182     bclass->set_preview (self, preview_caps);
183 }
184 
185 static void
gst_base_camera_src_start_capture(GstBaseCameraSrc * src)186 gst_base_camera_src_start_capture (GstBaseCameraSrc * src)
187 {
188   GstBaseCameraSrcClass *klass = GST_BASE_CAMERA_SRC_GET_CLASS (src);
189 
190   g_return_if_fail (klass->start_capture != NULL);
191 
192   GST_DEBUG_OBJECT (src, "Starting capture");
193 
194   g_mutex_lock (&src->capturing_mutex);
195   if (src->capturing) {
196     GST_WARNING_OBJECT (src, "Capturing already ongoing");
197     g_mutex_unlock (&src->capturing_mutex);
198 
199     /* post a warning to notify camerabin2 that the capture failed */
200     GST_ELEMENT_WARNING (src, RESOURCE, BUSY, (NULL), (NULL));
201     return;
202   }
203 
204   src->capturing = TRUE;
205   g_object_notify (G_OBJECT (src), "ready-for-capture");
206   if (klass->start_capture (src)) {
207     GST_DEBUG_OBJECT (src, "Capture started");
208   } else {
209     src->capturing = FALSE;
210     g_object_notify (G_OBJECT (src), "ready-for-capture");
211     GST_WARNING_OBJECT (src, "Failed to start capture");
212   }
213   g_mutex_unlock (&src->capturing_mutex);
214 }
215 
216 static void
gst_base_camera_src_stop_capture(GstBaseCameraSrc * src)217 gst_base_camera_src_stop_capture (GstBaseCameraSrc * src)
218 {
219   GstBaseCameraSrcClass *klass = GST_BASE_CAMERA_SRC_GET_CLASS (src);
220 
221   g_return_if_fail (klass->stop_capture != NULL);
222 
223   g_mutex_lock (&src->capturing_mutex);
224   if (!src->capturing) {
225     GST_DEBUG_OBJECT (src, "No ongoing capture");
226     g_mutex_unlock (&src->capturing_mutex);
227     return;
228   }
229   klass->stop_capture (src);
230   g_mutex_unlock (&src->capturing_mutex);
231 }
232 
233 void
gst_base_camera_src_finish_capture(GstBaseCameraSrc * self)234 gst_base_camera_src_finish_capture (GstBaseCameraSrc * self)
235 {
236   GST_DEBUG_OBJECT (self, "Finishing capture");
237   g_return_if_fail (self->capturing);
238   self->capturing = FALSE;
239   g_object_notify (G_OBJECT (self), "ready-for-capture");
240 }
241 
242 static void
gst_base_camera_src_dispose(GObject * object)243 gst_base_camera_src_dispose (GObject * object)
244 {
245   GstBaseCameraSrc *src = GST_BASE_CAMERA_SRC_CAST (object);
246 
247   g_mutex_clear (&src->capturing_mutex);
248 
249   if (src->preview_pipeline) {
250     gst_camerabin_destroy_preview_pipeline (src->preview_pipeline);
251     src->preview_pipeline = NULL;
252   }
253 
254   if (src->preview_caps)
255     gst_caps_replace (&src->preview_caps, NULL);
256 
257   if (src->preview_filter) {
258     gst_object_unref (src->preview_filter);
259     src->preview_filter = NULL;
260   }
261 
262   G_OBJECT_CLASS (parent_class)->dispose (object);
263 }
264 
265 static void
gst_base_camera_src_finalize(GstBaseCameraSrc * self)266 gst_base_camera_src_finalize (GstBaseCameraSrc * self)
267 {
268   G_OBJECT_CLASS (parent_class)->finalize ((GObject *) (self));
269 }
270 
271 static void
gst_base_camera_src_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)272 gst_base_camera_src_set_property (GObject * object,
273     guint prop_id, const GValue * value, GParamSpec * pspec)
274 {
275   GstBaseCameraSrc *self = GST_BASE_CAMERA_SRC (object);
276 
277   switch (prop_id) {
278     case PROP_MODE:
279       gst_base_camera_src_set_mode (GST_BASE_CAMERA_SRC (self),
280           g_value_get_enum (value));
281       break;
282     case PROP_ZOOM:{
283       self->zoom = g_value_get_float (value);
284       /* limit to max-zoom */
285       if (self->zoom > self->max_zoom) {
286         GST_DEBUG_OBJECT (self, "Clipping zoom %f to max-zoom %f", self->zoom,
287             self->max_zoom);
288         self->zoom = self->max_zoom;
289       }
290       /* does not set it if in NULL, the src is not created yet */
291       if (GST_STATE (self) != GST_STATE_NULL)
292         gst_base_camera_src_setup_zoom (self);
293       break;
294     }
295     case PROP_POST_PREVIEW:
296       self->post_preview = g_value_get_boolean (value);
297       break;
298     case PROP_PREVIEW_CAPS:{
299       GstCaps *new_caps;
300 
301       new_caps = (GstCaps *) gst_value_get_caps (value);
302       if (new_caps == NULL) {
303         new_caps = gst_caps_new_any ();
304       } else {
305         new_caps = gst_caps_ref (new_caps);
306       }
307 
308       if (!gst_caps_is_equal (self->preview_caps, new_caps)) {
309         gst_caps_replace (&self->preview_caps, new_caps);
310         gst_base_camera_src_setup_preview (self, new_caps);
311       } else {
312         GST_DEBUG_OBJECT (self, "New preview caps equal current preview caps");
313       }
314       gst_caps_unref (new_caps);
315     }
316       break;
317     case PROP_PREVIEW_FILTER:
318       if (self->preview_filter)
319         gst_object_unref (self->preview_filter);
320       self->preview_filter = g_value_dup_object (value);
321       if (!gst_camerabin_preview_set_filter (self->preview_pipeline,
322               self->preview_filter)) {
323         GST_WARNING_OBJECT (self,
324             "Cannot change preview filter, is element in NULL state?");
325       }
326       break;
327     case PROP_AUTO_START:
328       self->auto_start = g_value_get_boolean (value);
329       break;
330     default:
331       G_OBJECT_WARN_INVALID_PROPERTY_ID (self, prop_id, pspec);
332       break;
333   }
334 }
335 
336 static void
gst_base_camera_src_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)337 gst_base_camera_src_get_property (GObject * object,
338     guint prop_id, GValue * value, GParamSpec * pspec)
339 {
340   GstBaseCameraSrc *self = GST_BASE_CAMERA_SRC (object);
341 
342   switch (prop_id) {
343     case PROP_MODE:
344       g_value_set_enum (value, self->mode);
345       break;
346     case PROP_READY_FOR_CAPTURE:
347       g_value_set_boolean (value, !self->capturing);
348       break;
349     case PROP_ZOOM:
350       g_value_set_float (value, self->zoom);
351       break;
352     case PROP_MAX_ZOOM:
353       g_value_set_float (value, self->max_zoom);
354       break;
355     case PROP_POST_PREVIEW:
356       g_value_set_boolean (value, self->post_preview);
357       break;
358     case PROP_PREVIEW_CAPS:
359       if (self->preview_caps)
360         gst_value_set_caps (value, self->preview_caps);
361       break;
362     case PROP_PREVIEW_FILTER:
363       if (self->preview_filter)
364         g_value_set_object (value, self->preview_filter);
365       break;
366     case PROP_AUTO_START:
367       g_value_set_boolean (value, self->auto_start);
368       break;
369     default:
370       G_OBJECT_WARN_INVALID_PROPERTY_ID (self, prop_id, pspec);
371       break;
372   }
373 }
374 
375 static gboolean
construct_pipeline(GstBaseCameraSrc * self)376 construct_pipeline (GstBaseCameraSrc * self)
377 {
378   GstBaseCameraSrcClass *bclass = GST_BASE_CAMERA_SRC_GET_CLASS (self);
379 
380   if (bclass->construct_pipeline) {
381     if (!bclass->construct_pipeline (self)) {
382       GST_ERROR_OBJECT (self, "pipeline construction failed");
383       return FALSE;
384     }
385   }
386 
387   return TRUE;
388 }
389 
390 static gboolean
setup_pipeline(GstBaseCameraSrc * self)391 setup_pipeline (GstBaseCameraSrc * self)
392 {
393   GstBaseCameraSrcClass *bclass = GST_BASE_CAMERA_SRC_GET_CLASS (self);
394   if (bclass->setup_pipeline)
395     return bclass->setup_pipeline (self);
396   return TRUE;
397 }
398 
399 static GstStateChangeReturn
gst_base_camera_src_change_state(GstElement * element,GstStateChange transition)400 gst_base_camera_src_change_state (GstElement * element,
401     GstStateChange transition)
402 {
403   GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
404   GstBaseCameraSrc *self = GST_BASE_CAMERA_SRC (element);
405 
406   GST_DEBUG_OBJECT (self, "%d -> %d",
407       GST_STATE_TRANSITION_CURRENT (transition),
408       GST_STATE_TRANSITION_NEXT (transition));
409 
410   switch (transition) {
411     case GST_STATE_CHANGE_NULL_TO_READY:
412       if (!construct_pipeline (self))
413         return GST_STATE_CHANGE_FAILURE;
414 
415       if (self->preview_pipeline == NULL) {
416         /* failed to create preview pipeline, fail state change */
417         return GST_STATE_CHANGE_FAILURE;
418       }
419 
420       if (self->preview_caps) {
421         GST_DEBUG_OBJECT (self,
422             "Setting preview pipeline caps %" GST_PTR_FORMAT,
423             self->preview_caps);
424         gst_camerabin_preview_set_caps (self->preview_pipeline,
425             self->preview_caps);
426       }
427       break;
428     case GST_STATE_CHANGE_READY_TO_PAUSED:
429       if (!setup_pipeline (self))
430         return GST_STATE_CHANGE_FAILURE;
431       /* without this the preview pipeline will not post buffer
432        * messages on the pipeline */
433       gst_element_set_state (self->preview_pipeline->pipeline,
434           GST_STATE_PLAYING);
435       if (self->auto_start)
436         g_signal_emit_by_name (G_OBJECT (self), "start-capture", NULL);
437       break;
438     default:
439       break;
440   }
441 
442   ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
443 
444   switch (transition) {
445     case GST_STATE_CHANGE_PAUSED_TO_READY:
446       gst_element_set_state (self->preview_pipeline->pipeline, GST_STATE_READY);
447       if (self->auto_start)
448         g_signal_emit_by_name (G_OBJECT (self), "stop-capture", NULL);
449       break;
450     case GST_STATE_CHANGE_READY_TO_NULL:
451       gst_element_set_state (self->preview_pipeline->pipeline, GST_STATE_NULL);
452       break;
453     default:
454       break;
455   }
456 
457   return ret;
458 }
459 
460 static void
gst_base_camera_src_class_init(GstBaseCameraSrcClass * klass)461 gst_base_camera_src_class_init (GstBaseCameraSrcClass * klass)
462 {
463   GObjectClass *gobject_class;
464   GstElementClass *gstelement_class;
465 
466   GST_DEBUG_CATEGORY_INIT (base_camera_src_debug, "base_camera_src", 0,
467       "Base camera src");
468 
469   gobject_class = G_OBJECT_CLASS (klass);
470   gstelement_class = GST_ELEMENT_CLASS (klass);
471 
472   gobject_class->dispose = gst_base_camera_src_dispose;
473   gobject_class->finalize = (GObjectFinalizeFunc) gst_base_camera_src_finalize;
474   gobject_class->set_property = gst_base_camera_src_set_property;
475   gobject_class->get_property = gst_base_camera_src_get_property;
476 
477   g_object_class_install_property (gobject_class, PROP_MODE,
478       g_param_spec_enum ("mode", "Mode",
479           "The capture mode (still image capture or video recording)",
480           GST_TYPE_CAMERABIN_MODE, MODE_IMAGE,
481           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
482 
483   g_object_class_install_property (gobject_class, PROP_ZOOM,
484       g_param_spec_float ("zoom", "Zoom",
485           "Digital zoom factor (e.g. 1.5 means 1.5x)", MIN_ZOOM, G_MAXFLOAT,
486           DEFAULT_ZOOM, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
487 
488   g_object_class_install_property (gobject_class, PROP_MAX_ZOOM,
489       g_param_spec_float ("max-zoom", "Maximum zoom level (note: may change "
490           "depending on resolution/implementation)",
491           "Digital zoom factor (e.g. 1.5 means 1.5x)", MIN_ZOOM, G_MAXFLOAT,
492           MAX_ZOOM, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
493 
494   /**
495    * GstBaseCameraSrc:post-previews:
496    *
497    * When %TRUE, preview images should be posted to the bus when
498    * captures are made
499    */
500   g_object_class_install_property (gobject_class, PROP_POST_PREVIEW,
501       g_param_spec_boolean ("post-previews", "Post Previews",
502           "If capture preview images should be posted to the bus",
503           DEFAULT_POST_PREVIEW, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
504 
505   g_object_class_install_property (gobject_class, PROP_PREVIEW_CAPS,
506       g_param_spec_boxed ("preview-caps", "Preview caps",
507           "The caps of the preview image to be posted (NULL means ANY)",
508           GST_TYPE_CAPS, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
509 
510   g_object_class_install_property (gobject_class, PROP_PREVIEW_FILTER,
511       g_param_spec_object ("preview-filter", "Preview filter",
512           "A custom preview filter to process preview image data",
513           GST_TYPE_ELEMENT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
514 
515   g_object_class_install_property (gobject_class, PROP_AUTO_START,
516       g_param_spec_boolean ("auto-start", "Auto start capture",
517           "Automatically starts capture when going to the PAUSED state",
518           DEFAULT_AUTO_START, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
519 
520   /**
521    * GstBaseCameraSrc:ready-for-capture:
522    *
523    * When TRUE new capture can be prepared. If FALSE capturing is ongoing
524    * and starting a new capture immediately is not possible.
525    *
526    * Note that calling start-capture from the notify callback of this property
527    * will cause a deadlock. If you need to react like this on the notify
528    * function, please schedule a new thread to do it. If you're using glib's
529    * mainloop you can use g_idle_add() for example.
530    */
531   g_object_class_install_property (gobject_class, PROP_READY_FOR_CAPTURE,
532       g_param_spec_boolean ("ready-for-capture", "Ready for capture",
533           "Informs this element is ready for starting another capture",
534           TRUE, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
535 
536 
537   /* Signals */
538   basecamerasrc_signals[START_CAPTURE_SIGNAL] =
539       g_signal_new_class_handler ("start-capture",
540       G_TYPE_FROM_CLASS (klass),
541       G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
542       G_CALLBACK (gst_base_camera_src_start_capture),
543       NULL, NULL, NULL, G_TYPE_NONE, 0);
544 
545   basecamerasrc_signals[STOP_CAPTURE_SIGNAL] =
546       g_signal_new_class_handler ("stop-capture",
547       G_TYPE_FROM_CLASS (klass),
548       G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
549       G_CALLBACK (gst_base_camera_src_stop_capture),
550       NULL, NULL, NULL, G_TYPE_NONE, 0);
551 
552   gstelement_class->change_state = gst_base_camera_src_change_state;
553 
554   gst_element_class_set_static_metadata (gstelement_class,
555       "Base class for camerabin src bin", "Source/Video",
556       "Abstracts capture device for camerabin2", "Rob Clark <rob@ti.com>");
557 }
558 
559 static void
gst_base_camera_src_init(GstBaseCameraSrc * self)560 gst_base_camera_src_init (GstBaseCameraSrc * self)
561 {
562   self->width = DEFAULT_WIDTH;
563   self->height = DEFAULT_HEIGHT;
564   self->zoom = DEFAULT_ZOOM;
565   self->max_zoom = MAX_ZOOM;
566   self->mode = MODE_IMAGE;
567 
568   self->auto_start = DEFAULT_AUTO_START;
569   self->capturing = FALSE;
570   g_mutex_init (&self->capturing_mutex);
571 
572   self->post_preview = DEFAULT_POST_PREVIEW;
573   self->preview_caps = gst_caps_new_any ();
574 
575   self->preview_pipeline =
576       gst_camerabin_create_preview_pipeline (GST_ELEMENT_CAST (self), NULL);
577 }
578 
579 void
gst_base_camera_src_post_preview(GstBaseCameraSrc * self,GstSample * sample)580 gst_base_camera_src_post_preview (GstBaseCameraSrc * self, GstSample * sample)
581 {
582   if (self->post_preview) {
583     gst_camerabin_preview_pipeline_post (self->preview_pipeline, sample);
584   } else {
585     GST_DEBUG_OBJECT (self, "Previews not enabled, not posting");
586   }
587 }
588