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