1 /*
2 * GStreamer
3 * Copyright (C) 2008 Nokia Corporation <multimedia@maemo.org>
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 * SECTION:camerabingeneral
23 * @short_description: helper functions for #GstCameraBin and it's modules
24 *
25 * Common helper functions for #GstCameraBin, #GstCameraBinImage and
26 * #GstCameraBinVideo.
27 *
28 */
29 #ifdef HAVE_CONFIG_H
30 #include "config.h"
31 #endif
32
33 #include <gst/app/gstappsrc.h>
34 #include <gst/app/gstappsink.h>
35 #include <gst/glib-compat-private.h>
36 #include "gstcamerabinpreview.h"
37 #include "gstbasecamerasrc.h"
38
39 GST_DEBUG_CATEGORY_EXTERN (base_camera_src_debug);
40 #define GST_CAT_DEFAULT base_camera_src_debug
41
42 static void _gst_camerabin_preview_set_caps (GstCameraBinPreviewPipelineData *
43 preview, GstCaps * caps);
44
45 static gboolean
bus_callback(GstBus * bus,GstMessage * message,gpointer user_data)46 bus_callback (GstBus * bus, GstMessage * message, gpointer user_data)
47 {
48 switch (GST_MESSAGE_TYPE (message)) {
49 case GST_MESSAGE_ERROR:{
50 GError *err;
51 GstCameraBinPreviewPipelineData *data;
52
53 data = user_data;
54
55 gst_message_parse_error (message, &err, NULL);
56 GST_WARNING ("Error from preview pipeline: %s", err->message);
57 g_error_free (err);
58
59 /* TODO Not sure if we should post an Error or Warning here */
60 GST_ELEMENT_ERROR (data, CORE, FAILED,
61 ("fatal error in preview pipeline, disposing the pipeline"), (NULL));
62
63 /* Possible error situations:
64 * 1) cond_wait pending. prevent deadlock by signalling the cond
65 * 2) preview_pipeline_post called with new buffer to handle. returns
66 * because data->pipeline is set to null
67 * 3) new preview caps incoming. returns because data->pipeline is null
68 */
69
70 if (data->pipeline) {
71 gst_element_set_state (data->pipeline, GST_STATE_NULL);
72 gst_object_unref (data->pipeline);
73 data->pipeline = NULL;
74 }
75
76 g_cond_signal (&data->processing_cond);
77
78 break;
79 }
80 default:
81 break;
82 }
83 return TRUE;
84 }
85
86 static GstFlowReturn
gst_camerabin_preview_pipeline_new_sample(GstAppSink * appsink,gpointer user_data)87 gst_camerabin_preview_pipeline_new_sample (GstAppSink * appsink,
88 gpointer user_data)
89 {
90 GstSample *sample;
91 GstStructure *s;
92 GstMessage *msg;
93 GstCameraBinPreviewPipelineData *data;
94
95 data = user_data;
96
97 sample = gst_app_sink_pull_sample (appsink);
98 s = gst_structure_new (GST_BASE_CAMERA_SRC_PREVIEW_MESSAGE_NAME,
99 "sample", GST_TYPE_SAMPLE, sample, NULL);
100 gst_sample_unref (sample);
101 msg = gst_message_new_element (GST_OBJECT (data->element), s);
102
103 GST_DEBUG_OBJECT (data->element, "sending message with preview image");
104 if (gst_element_post_message (data->element, msg) == FALSE) {
105 GST_WARNING_OBJECT (data->element,
106 "This element has no bus, therefore no message sent!");
107 }
108
109 g_mutex_lock (&data->processing_lock);
110
111 data->processing--;
112 if (data->processing == 0)
113 g_cond_signal (&data->processing_cond);
114
115 g_mutex_unlock (&data->processing_lock);
116
117 return GST_FLOW_OK;
118 }
119
120 /**
121 * gst_camerabin_create_preview_pipeline:
122 * @element: Owner of this pipeline
123 * @filter: Custom filter to process preview data (an extra ref is taken)
124 *
125 * Creates a new previewing pipeline that can receive buffers
126 * to be posted as camerabin preview messages for @element
127 *
128 * Returns: The newly created #GstCameraBinPreviewPipelineData
129 */
130 GstCameraBinPreviewPipelineData *
gst_camerabin_create_preview_pipeline(GstElement * element,GstElement * filter)131 gst_camerabin_create_preview_pipeline (GstElement * element,
132 GstElement * filter)
133 {
134 GstCameraBinPreviewPipelineData *data;
135 GstElement *csp;
136 GstElement *vscale;
137 gboolean added = FALSE;
138 gboolean linkfail = FALSE;
139 GstBus *bus;
140 GstAppSinkCallbacks callbacks = { 0, };
141
142 data = g_new0 (GstCameraBinPreviewPipelineData, 1);
143
144 data->pipeline = gst_pipeline_new ("preview-pipeline");
145 data->appsrc = gst_element_factory_make ("appsrc", "preview-appsrc");
146 data->appsink = gst_element_factory_make ("appsink", "preview-appsink");
147 csp = gst_element_factory_make ("videoconvert", "preview-vconv");
148 vscale = gst_element_factory_make ("videoscale", "preview-vscale");
149
150 if (!data->appsrc || !data->appsink || !csp || !vscale) {
151 goto error;
152 }
153
154 g_object_set (data->appsrc, "emit-signals", FALSE, NULL);
155 g_object_set (data->appsink, "sync", FALSE, "enable-last-sample",
156 FALSE, NULL);
157
158 gst_bin_add_many (GST_BIN (data->pipeline), data->appsrc,
159 data->appsink, csp, vscale, NULL);
160 if (filter)
161 gst_bin_add (GST_BIN (data->pipeline), gst_object_ref (filter));
162 added = TRUE;
163
164 if (filter) {
165 linkfail |=
166 GST_PAD_LINK_FAILED (gst_element_link_pads_full (data->appsrc, "src",
167 filter, NULL, GST_PAD_LINK_CHECK_NOTHING));
168 linkfail |=
169 GST_PAD_LINK_FAILED (gst_element_link_pads_full (filter, NULL,
170 vscale, "sink", GST_PAD_LINK_CHECK_CAPS));
171 } else {
172 linkfail |=
173 GST_PAD_LINK_FAILED (gst_element_link_pads_full (data->appsrc, "src",
174 vscale, "sink", GST_PAD_LINK_CHECK_NOTHING));
175 }
176
177 linkfail |=
178 GST_PAD_LINK_FAILED (gst_element_link_pads_full (vscale, "src", csp,
179 "sink", GST_PAD_LINK_CHECK_NOTHING));
180 linkfail |=
181 GST_PAD_LINK_FAILED (gst_element_link_pads_full (csp, "src",
182 data->appsink, "sink", GST_PAD_LINK_CHECK_NOTHING));
183
184 if (linkfail) {
185 GST_WARNING ("Failed to link preview pipeline elements");
186 goto error;
187 }
188
189 callbacks.new_sample = gst_camerabin_preview_pipeline_new_sample;
190 gst_app_sink_set_callbacks ((GstAppSink *) data->appsink, &callbacks, data,
191 NULL);
192
193 bus = gst_pipeline_get_bus (GST_PIPELINE (data->pipeline));
194 gst_bus_add_watch (bus, bus_callback, data);
195 gst_object_unref (bus);
196
197 g_object_set (data->appsink, "sync", FALSE, NULL);
198
199 data->element = element;
200 data->filter = filter;
201 data->vscale = vscale;
202
203 g_mutex_init (&data->processing_lock);
204 g_cond_init (&data->processing_cond);
205
206 data->pending_preview_caps = NULL;
207 data->processing = 0;
208
209 return data;
210 error:
211 GST_WARNING ("Failed to create camerabin's preview pipeline");
212 if (!added) {
213 if (csp)
214 gst_object_unref (csp);
215 if (vscale)
216 gst_object_unref (vscale);
217 if (data->appsrc)
218 gst_object_unref (data->appsrc);
219 if (data->appsink)
220 gst_object_unref (data->appsink);
221 }
222 gst_camerabin_destroy_preview_pipeline (data);
223 return NULL;
224 }
225
226 /**
227 * gst_camerabin_destroy_preview_pipeline:
228 * @preview: the #GstCameraBinPreviewPipelineData
229 *
230 * Frees a #GstCameraBinPreviewPipelineData
231 */
232 void
gst_camerabin_destroy_preview_pipeline(GstCameraBinPreviewPipelineData * preview)233 gst_camerabin_destroy_preview_pipeline (GstCameraBinPreviewPipelineData *
234 preview)
235 {
236 g_return_if_fail (preview != NULL);
237
238 g_mutex_clear (&preview->processing_lock);
239 g_cond_clear (&preview->processing_cond);
240
241 if (preview->pipeline) {
242 GstBus *bus;
243
244 gst_element_set_state (preview->pipeline, GST_STATE_NULL);
245
246 bus = gst_pipeline_get_bus (GST_PIPELINE (preview->pipeline));
247 gst_bus_remove_watch (bus);
248 gst_object_unref (bus);
249
250 gst_object_unref (preview->pipeline);
251 }
252 g_free (preview);
253 }
254
255 /**
256 * gst_camerabin_preview_pipeline_post:
257 * @preview: the #GstCameraBinPreviewPipelineData
258 * @sample: the sample to be posted as a preview
259 *
260 * Converts the @sample to the desired format and posts the preview
261 * message to the bus.
262 *
263 * Returns: %TRUE on success
264 */
265 gboolean
gst_camerabin_preview_pipeline_post(GstCameraBinPreviewPipelineData * preview,GstSample * sample)266 gst_camerabin_preview_pipeline_post (GstCameraBinPreviewPipelineData * preview,
267 GstSample * sample)
268 {
269 g_return_val_if_fail (preview != NULL, FALSE);
270 g_return_val_if_fail (preview->pipeline != NULL, FALSE);
271 g_return_val_if_fail (sample, FALSE);
272
273 g_mutex_lock (&preview->processing_lock);
274 g_return_val_if_fail (preview->pipeline != NULL, FALSE);
275
276 if (preview->pending_preview_caps) {
277 if (preview->processing > 0) {
278 g_cond_wait (&preview->processing_cond, &preview->processing_lock);
279 }
280 _gst_camerabin_preview_set_caps (preview, preview->pending_preview_caps);
281 gst_caps_replace (&preview->pending_preview_caps, NULL);
282 }
283
284 preview->processing++;
285
286 g_object_set (preview->appsrc, "caps", gst_sample_get_caps (sample), NULL);
287 gst_app_src_push_buffer ((GstAppSrc *) preview->appsrc,
288 gst_buffer_ref (gst_sample_get_buffer (sample)));
289
290 g_mutex_unlock (&preview->processing_lock);
291
292 return TRUE;
293 }
294
295 static void
_gst_camerabin_preview_set_caps(GstCameraBinPreviewPipelineData * preview,GstCaps * caps)296 _gst_camerabin_preview_set_caps (GstCameraBinPreviewPipelineData * preview,
297 GstCaps * caps)
298 {
299 GstState state, pending;
300 GstStateChangeReturn ret;
301
302 g_return_if_fail (preview != NULL);
303 g_return_if_fail (preview->pipeline != NULL);
304
305 ret = gst_element_get_state (preview->pipeline, &state, &pending, 0);
306 if (ret == GST_STATE_CHANGE_FAILURE) {
307 /* make it try again */
308 state = GST_STATE_PLAYING;
309 pending = GST_STATE_VOID_PENDING;
310 }
311 gst_element_set_state (preview->pipeline, GST_STATE_NULL);
312 g_object_set (preview->appsink, "caps", caps, NULL);
313 if (pending != GST_STATE_VOID_PENDING)
314 state = pending;
315 gst_element_set_state (preview->pipeline, state);
316 }
317
318 /**
319 * gst_camerabin_preview_set_caps:
320 * @preview: the #GstCameraBinPreviewPipelineData
321 * @caps: the #GstCaps to be set (a new ref will be taken)
322 *
323 * The caps that preview buffers should have when posted
324 * on the bus
325 */
326 void
gst_camerabin_preview_set_caps(GstCameraBinPreviewPipelineData * preview,GstCaps * caps)327 gst_camerabin_preview_set_caps (GstCameraBinPreviewPipelineData * preview,
328 GstCaps * caps)
329 {
330 g_return_if_fail (preview != NULL);
331
332 g_mutex_lock (&preview->processing_lock);
333
334 if (preview->processing == 0) {
335 _gst_camerabin_preview_set_caps (preview, caps);
336 } else {
337 GST_DEBUG ("Preview pipeline busy, storing new caps as pending");
338 gst_caps_replace (&preview->pending_preview_caps, caps);
339 }
340 g_mutex_unlock (&preview->processing_lock);
341 }
342
343 /**
344 * gst_camerabin_preview_set_filter:
345 * @preview: the #GstCameraBinPreviewPipelineData
346 * @filter: Custom filter to process preview data (an extra ref is taken)
347 *
348 * Set the filter element into preview pipeline.
349 *
350 * Returns: %TRUE on success
351 */
352 gboolean
gst_camerabin_preview_set_filter(GstCameraBinPreviewPipelineData * preview,GstElement * filter)353 gst_camerabin_preview_set_filter (GstCameraBinPreviewPipelineData * preview,
354 GstElement * filter)
355 {
356 gboolean ret = TRUE;
357 GstState current;
358
359 g_return_val_if_fail (preview != NULL, FALSE);
360
361 GST_DEBUG ("Preview pipeline setting new filter %p", filter);
362
363 g_mutex_lock (&preview->processing_lock);
364
365 gst_element_get_state (preview->pipeline, ¤t, NULL, 0);
366
367 if (preview->processing == 0 && current == GST_STATE_NULL) {
368 gboolean linkfail = FALSE;
369
370 if (preview->filter) {
371 /* Unlink and remove old filter */
372 gst_element_unlink (preview->appsrc, preview->filter);
373 gst_element_unlink (preview->filter, preview->vscale);
374 gst_bin_remove (GST_BIN (preview->pipeline), preview->filter);
375 } else {
376 /* Make room for filter by breaking the link between appsrc and vcale */
377 gst_element_unlink (preview->appsrc, preview->vscale);
378 }
379
380 if (filter) {
381 /* Add and link the new filter between appsrc and vscale */
382 gst_bin_add (GST_BIN (preview->pipeline), gst_object_ref (filter));
383
384 linkfail |=
385 GST_PAD_LINK_FAILED (gst_element_link_pads_full (preview->appsrc,
386 "src", filter, NULL, GST_PAD_LINK_CHECK_NOTHING));
387
388 linkfail |=
389 GST_PAD_LINK_FAILED (gst_element_link_pads_full (filter, NULL,
390 preview->vscale, "sink", GST_PAD_LINK_CHECK_CAPS));
391 } else {
392 /* No filter was given. Just link the appsrc to vscale directly */
393 linkfail |=
394 GST_PAD_LINK_FAILED (gst_element_link_pads_full (preview->appsrc,
395 "src", preview->vscale, "sink", GST_PAD_LINK_CHECK_NOTHING));
396 }
397
398 if (linkfail) {
399 GST_WARNING ("Linking the filter to pipeline failed");
400 ret = FALSE;
401 } else {
402 GST_DEBUG ("Linking the filter to pipeline successful");
403 preview->filter = filter;
404 }
405 } else {
406 GST_WARNING ("Cannot change filter when pipeline is running");
407 ret = FALSE;
408 }
409 g_mutex_unlock (&preview->processing_lock);
410
411 return ret;
412 }
413