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