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 * &vidsrc_template);
55 * gst_element_class_add_static_pad_template (gstelement_class,
56 * &imgsrc_template);
57 * gst_element_class_add_static_pad_template (gstelement_class,
58 * &vfsrc_template);
59 * // see #GstElementDetails
60 * gst_element_class_set_details (gstelement_class, &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