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., 59 Temple Place - Suite 330, |
|
18 * Boston, MA 02111-1307, USA. |
|
19 */ |
|
20 |
|
21 /** |
|
22 * SECTION:camerabinvideo |
|
23 * @short_description: video recording module of #GstCameraBin |
|
24 * |
|
25 * <refsect2> |
|
26 * <para> |
|
27 * |
|
28 * The pipeline for this module is: |
|
29 * |
|
30 * <informalexample> |
|
31 * <programlisting> |
|
32 *----------------------------------------------------------------------------- |
|
33 * audiosrc -> audio_queue -> audioconvert -> volume -> audioenc |
|
34 * > videomux -> filesink |
|
35 * video_queue -> [timeoverlay] -> videoenc |
|
36 * -> [post proc] -> tee < |
|
37 * queue -> |
|
38 *----------------------------------------------------------------------------- |
|
39 * </programlisting> |
|
40 * </informalexample> |
|
41 * |
|
42 * The properties of elements are: |
|
43 * |
|
44 * queue - "leaky", 2 (Leaky on downstream (old buffers)) |
|
45 * |
|
46 * </para> |
|
47 * </refsect2> |
|
48 */ |
|
49 |
|
50 /* |
|
51 * includes |
|
52 */ |
|
53 |
|
54 |
|
55 #include <gst/gst.h> |
|
56 #include "camerabingeneral.h" |
|
57 |
|
58 #include "camerabinvideo.h" |
|
59 |
|
60 #ifdef __SYMBIAN32__ |
|
61 #include <glib_global.h> |
|
62 #endif |
|
63 /* |
|
64 * defines and static global vars |
|
65 */ |
|
66 |
|
67 /* internal element names */ |
|
68 |
|
69 #define DEFAULT_AUD_SRC "pulsesrc" |
|
70 #define DEFAULT_AUD_ENC "vorbisenc" |
|
71 #define DEFAULT_VID_ENC "theoraenc" |
|
72 #define DEFAULT_MUX "oggmux" |
|
73 #define DEFAULT_SINK "filesink" |
|
74 |
|
75 #define USE_AUDIO_CONVERSION 1 |
|
76 |
|
77 enum |
|
78 { |
|
79 PROP_0, |
|
80 PROP_FILENAME |
|
81 }; |
|
82 |
|
83 static void gst_camerabin_video_dispose (GstCameraBinVideo * sink); |
|
84 static void gst_camerabin_video_set_property (GObject * object, guint prop_id, |
|
85 const GValue * value, GParamSpec * pspec); |
|
86 static void gst_camerabin_video_get_property (GObject * object, guint prop_id, |
|
87 GValue * value, GParamSpec * pspec); |
|
88 |
|
89 static GstClock *gst_camerabin_video_provide_clock (GstElement * elem); |
|
90 static GstStateChangeReturn |
|
91 gst_camerabin_video_change_state (GstElement * element, |
|
92 GstStateChange transition); |
|
93 |
|
94 static |
|
95 gboolean camerabin_video_pad_tee_src0_have_buffer (GstPad * pad, |
|
96 GstBuffer * buffer, gpointer u_data); |
|
97 static gboolean camerabin_video_pad_aud_src_have_buffer (GstPad * pad, |
|
98 GstBuffer * buffer, gpointer u_data); |
|
99 static gboolean camerabin_video_sink_have_event (GstPad * pad, GstEvent * event, |
|
100 gpointer u_data); |
|
101 static gboolean gst_camerabin_video_create_elements (GstCameraBinVideo * vid); |
|
102 static void gst_camerabin_video_destroy_elements (GstCameraBinVideo * vid); |
|
103 |
|
104 GST_BOILERPLATE (GstCameraBinVideo, gst_camerabin_video, GstBin, GST_TYPE_BIN); |
|
105 |
|
106 static const GstElementDetails gst_camerabin_video_details = |
|
107 GST_ELEMENT_DETAILS ("Video capture bin for camerabin", |
|
108 "Bin/Video", |
|
109 "Process and store video data", |
|
110 "Edgard Lima <edgard.lima@indt.org.br>\n" |
|
111 "Nokia Corporation <multimedia@maemo.org>"); |
|
112 |
|
113 static GstStaticPadTemplate sink_template = GST_STATIC_PAD_TEMPLATE ("sink", |
|
114 GST_PAD_SINK, |
|
115 GST_PAD_ALWAYS, |
|
116 GST_STATIC_CAPS_ANY); |
|
117 |
|
118 static GstStaticPadTemplate src_template = GST_STATIC_PAD_TEMPLATE ("src", |
|
119 GST_PAD_SRC, |
|
120 GST_PAD_ALWAYS, |
|
121 GST_STATIC_CAPS_ANY); |
|
122 |
|
123 |
|
124 /* GObject methods implementation */ |
|
125 |
|
126 static void |
|
127 gst_camerabin_video_base_init (gpointer klass) |
|
128 { |
|
129 GstElementClass *eklass = GST_ELEMENT_CLASS (klass); |
|
130 |
|
131 gst_element_class_add_pad_template (eklass, |
|
132 gst_static_pad_template_get (&sink_template)); |
|
133 gst_element_class_add_pad_template (eklass, |
|
134 gst_static_pad_template_get (&src_template)); |
|
135 gst_element_class_set_details (eklass, &gst_camerabin_video_details); |
|
136 } |
|
137 |
|
138 static void |
|
139 gst_camerabin_video_class_init (GstCameraBinVideoClass * klass) |
|
140 { |
|
141 GObjectClass *gobject_class; |
|
142 GstElementClass *eklass = GST_ELEMENT_CLASS (klass); |
|
143 |
|
144 gobject_class = G_OBJECT_CLASS (klass); |
|
145 gobject_class->dispose = |
|
146 (GObjectFinalizeFunc) GST_DEBUG_FUNCPTR (gst_camerabin_video_dispose); |
|
147 eklass->change_state = GST_DEBUG_FUNCPTR (gst_camerabin_video_change_state); |
|
148 |
|
149 eklass->provide_clock = GST_DEBUG_FUNCPTR (gst_camerabin_video_provide_clock); |
|
150 |
|
151 gobject_class->set_property = |
|
152 GST_DEBUG_FUNCPTR (gst_camerabin_video_set_property); |
|
153 gobject_class->get_property = |
|
154 GST_DEBUG_FUNCPTR (gst_camerabin_video_get_property); |
|
155 |
|
156 /** |
|
157 * GstCameraBinVideo:filename: |
|
158 * |
|
159 * This property can be used to specify the filename of the video. |
|
160 * |
|
161 **/ |
|
162 g_object_class_install_property (gobject_class, PROP_FILENAME, |
|
163 g_param_spec_string ("filename", "Filename", |
|
164 "Filename of the video to save", NULL, G_PARAM_READWRITE)); |
|
165 } |
|
166 |
|
167 static void |
|
168 gst_camerabin_video_init (GstCameraBinVideo * vid, |
|
169 GstCameraBinVideoClass * g_class) |
|
170 { |
|
171 vid->filename = g_string_new (""); |
|
172 |
|
173 vid->user_post = NULL; |
|
174 vid->user_vid_enc = NULL; |
|
175 vid->user_aud_enc = NULL; |
|
176 vid->user_aud_src = NULL; |
|
177 vid->user_mux = NULL; |
|
178 |
|
179 vid->aud_src = NULL; |
|
180 vid->sink = NULL; |
|
181 vid->tee = NULL; |
|
182 vid->volume = NULL; |
|
183 vid->video_queue = NULL; |
|
184 |
|
185 vid->tee_video_srcpad = NULL; |
|
186 vid->tee_vf_srcpad = NULL; |
|
187 |
|
188 vid->pending_eos = NULL; |
|
189 |
|
190 /* Create src and sink ghost pads */ |
|
191 vid->sinkpad = gst_ghost_pad_new_no_target ("sink", GST_PAD_SINK); |
|
192 gst_element_add_pad (GST_ELEMENT (vid), vid->sinkpad); |
|
193 |
|
194 vid->srcpad = gst_ghost_pad_new_no_target ("src", GST_PAD_SRC); |
|
195 gst_element_add_pad (GST_ELEMENT (vid), vid->srcpad); |
|
196 |
|
197 /* Add probe for handling eos when stopping recording */ |
|
198 gst_pad_add_event_probe (vid->sinkpad, |
|
199 G_CALLBACK (camerabin_video_sink_have_event), vid); |
|
200 } |
|
201 |
|
202 static void |
|
203 gst_camerabin_video_dispose (GstCameraBinVideo * vid) |
|
204 { |
|
205 GST_DEBUG_OBJECT (vid, "disposing"); |
|
206 |
|
207 g_string_free (vid->filename, TRUE); |
|
208 vid->filename = NULL; |
|
209 |
|
210 if (vid->user_post) { |
|
211 gst_object_unref (vid->user_post); |
|
212 vid->user_post = NULL; |
|
213 } |
|
214 |
|
215 if (vid->user_vid_enc) { |
|
216 gst_object_unref (vid->user_vid_enc); |
|
217 vid->user_vid_enc = NULL; |
|
218 } |
|
219 |
|
220 if (vid->user_aud_enc) { |
|
221 gst_object_unref (vid->user_aud_enc); |
|
222 vid->user_aud_enc = NULL; |
|
223 } |
|
224 |
|
225 if (vid->user_aud_src) { |
|
226 gst_object_unref (vid->user_aud_src); |
|
227 vid->user_aud_src = NULL; |
|
228 } |
|
229 |
|
230 if (vid->user_mux) { |
|
231 gst_object_unref (vid->user_mux); |
|
232 vid->user_mux = NULL; |
|
233 } |
|
234 |
|
235 G_OBJECT_CLASS (parent_class)->dispose ((GObject *) vid); |
|
236 } |
|
237 |
|
238 |
|
239 static void |
|
240 gst_camerabin_video_set_property (GObject * object, guint prop_id, |
|
241 const GValue * value, GParamSpec * pspec) |
|
242 { |
|
243 GstCameraBinVideo *bin = GST_CAMERABIN_VIDEO (object); |
|
244 |
|
245 switch (prop_id) { |
|
246 case PROP_FILENAME: |
|
247 g_string_assign (bin->filename, g_value_get_string (value)); |
|
248 if (bin->sink) { |
|
249 g_object_set (G_OBJECT (bin->sink), "location", bin->filename->str, |
|
250 NULL); |
|
251 } else { |
|
252 GST_INFO ("no sink, not setting name yet"); |
|
253 } |
|
254 break; |
|
255 default: |
|
256 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); |
|
257 break; |
|
258 } |
|
259 } |
|
260 |
|
261 static void |
|
262 gst_camerabin_video_get_property (GObject * object, guint prop_id, |
|
263 GValue * value, GParamSpec * pspec) |
|
264 { |
|
265 GstCameraBinVideo *bin = GST_CAMERABIN_VIDEO (object); |
|
266 |
|
267 switch (prop_id) { |
|
268 case PROP_FILENAME: |
|
269 g_value_set_string (value, bin->filename->str); |
|
270 break; |
|
271 default: |
|
272 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); |
|
273 break; |
|
274 } |
|
275 } |
|
276 |
|
277 /* GstElement methods implementation */ |
|
278 |
|
279 static GstClock * |
|
280 gst_camerabin_video_provide_clock (GstElement * elem) |
|
281 { |
|
282 GstElement *aud_src = GST_CAMERABIN_VIDEO (elem)->aud_src; |
|
283 if (aud_src) { |
|
284 return gst_element_provide_clock (aud_src); |
|
285 } else { |
|
286 return NULL; |
|
287 } |
|
288 } |
|
289 |
|
290 static GstStateChangeReturn |
|
291 gst_camerabin_video_change_state (GstElement * element, |
|
292 GstStateChange transition) |
|
293 { |
|
294 GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS; |
|
295 GstCameraBinVideo *vid = GST_CAMERABIN_VIDEO (element); |
|
296 |
|
297 switch (transition) { |
|
298 case GST_STATE_CHANGE_NULL_TO_READY: |
|
299 if (!gst_camerabin_video_create_elements (vid)) { |
|
300 return GST_STATE_CHANGE_FAILURE; |
|
301 } |
|
302 /* Don't change sink to READY yet to allow changing the |
|
303 filename in READY state. */ |
|
304 gst_element_set_locked_state (vid->sink, TRUE); |
|
305 break; |
|
306 case GST_STATE_CHANGE_READY_TO_PAUSED: |
|
307 vid->calculate_adjust_ts_video = TRUE; |
|
308 vid->calculate_adjust_ts_aud = TRUE; |
|
309 g_object_set (G_OBJECT (vid->sink), "async", FALSE, NULL); |
|
310 gst_element_set_locked_state (vid->sink, FALSE); |
|
311 break; |
|
312 case GST_STATE_CHANGE_PAUSED_TO_PLAYING: |
|
313 vid->calculate_adjust_ts_video = TRUE; |
|
314 vid->calculate_adjust_ts_aud = TRUE; |
|
315 break; |
|
316 |
|
317 case GST_STATE_CHANGE_PAUSED_TO_READY: |
|
318 /* Set sink to NULL in order to write the file _now_ */ |
|
319 GST_INFO ("write vid file: %s", vid->filename->str); |
|
320 gst_element_set_locked_state (vid->sink, TRUE); |
|
321 gst_element_set_state (vid->sink, GST_STATE_NULL); |
|
322 break; |
|
323 default: |
|
324 break; |
|
325 } |
|
326 |
|
327 ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition); |
|
328 |
|
329 switch (transition) { |
|
330 case GST_STATE_CHANGE_PAUSED_TO_PLAYING: |
|
331 if (vid->pending_eos) { |
|
332 /* Video bin is still paused, so push eos directly to video queue */ |
|
333 GST_DEBUG_OBJECT (vid, "pushing pending eos"); |
|
334 gst_pad_push_event (vid->tee_video_srcpad, vid->pending_eos); |
|
335 vid->pending_eos = NULL; |
|
336 } |
|
337 break; |
|
338 case GST_STATE_CHANGE_PAUSED_TO_READY: |
|
339 /* Reset counters related to timestamp rewriting */ |
|
340 vid->adjust_ts_video = 0; |
|
341 vid->last_ts_video = 0; |
|
342 vid->adjust_ts_aud = 0; |
|
343 vid->last_ts_aud = 0; |
|
344 |
|
345 if (vid->pending_eos) { |
|
346 gst_event_unref (vid->pending_eos); |
|
347 vid->pending_eos = NULL; |
|
348 } |
|
349 break; |
|
350 case GST_STATE_CHANGE_READY_TO_NULL: |
|
351 gst_camerabin_video_destroy_elements (vid); |
|
352 break; |
|
353 default: |
|
354 break; |
|
355 } |
|
356 |
|
357 return ret; |
|
358 } |
|
359 |
|
360 /* |
|
361 * static helper functions implementation |
|
362 */ |
|
363 |
|
364 /** |
|
365 * camerabin_video_pad_tee_src0_have_buffer: |
|
366 * @pad: tee src pad leading to video encoding |
|
367 * @event: received buffer |
|
368 * @u_data: video bin object |
|
369 * |
|
370 * Buffer probe for rewriting video buffer timestamps. |
|
371 * |
|
372 * Returns: TRUE always |
|
373 */ |
|
374 static gboolean |
|
375 camerabin_video_pad_tee_src0_have_buffer (GstPad * pad, GstBuffer * buffer, |
|
376 gpointer u_data) |
|
377 { |
|
378 GstCameraBinVideo *vid = (GstCameraBinVideo *) u_data; |
|
379 |
|
380 GST_LOG ("buffer in with size %d ts %" GST_TIME_FORMAT, |
|
381 GST_BUFFER_SIZE (buffer), GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buffer))); |
|
382 |
|
383 if (G_UNLIKELY (vid->calculate_adjust_ts_video)) { |
|
384 GstEvent *event; |
|
385 GstObject *tee; |
|
386 GstPad *sinkpad; |
|
387 vid->adjust_ts_video = GST_BUFFER_TIMESTAMP (buffer) - vid->last_ts_video; |
|
388 vid->calculate_adjust_ts_video = FALSE; |
|
389 event = gst_event_new_new_segment (FALSE, 1.0, GST_FORMAT_TIME, |
|
390 0, GST_CLOCK_TIME_NONE, vid->last_ts_video); |
|
391 /* Send the newsegment to both view finder and video bin */ |
|
392 tee = gst_pad_get_parent (pad); |
|
393 sinkpad = gst_element_get_static_pad (GST_ELEMENT (tee), "sink"); |
|
394 gst_pad_send_event (sinkpad, event); |
|
395 gst_object_unref (tee); |
|
396 gst_object_unref (sinkpad); |
|
397 GST_LOG_OBJECT (vid, "vid ts adjustment: %" GST_TIME_FORMAT, |
|
398 GST_TIME_ARGS (vid->adjust_ts_video)); |
|
399 GST_BUFFER_FLAG_SET (buffer, GST_BUFFER_FLAG_DISCONT); |
|
400 } |
|
401 GST_BUFFER_TIMESTAMP (buffer) -= vid->adjust_ts_video; |
|
402 vid->last_ts_video = GST_BUFFER_TIMESTAMP (buffer); |
|
403 if (GST_BUFFER_DURATION_IS_VALID (buffer)) |
|
404 vid->last_ts_video += GST_BUFFER_DURATION (buffer); |
|
405 |
|
406 |
|
407 GST_LOG ("buffer out with size %d ts %" GST_TIME_FORMAT, |
|
408 GST_BUFFER_SIZE (buffer), GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buffer))); |
|
409 return TRUE; |
|
410 } |
|
411 |
|
412 /** |
|
413 * camerabin_video_pad_aud_src_have_buffer: |
|
414 * @pad: audio source src pad |
|
415 * @event: received buffer |
|
416 * @u_data: video bin object |
|
417 * |
|
418 * Buffer probe for rewriting audio buffer timestamps. |
|
419 * |
|
420 * Returns: TRUE always |
|
421 */ |
|
422 static gboolean |
|
423 camerabin_video_pad_aud_src_have_buffer (GstPad * pad, GstBuffer * buffer, |
|
424 gpointer u_data) |
|
425 { |
|
426 GstCameraBinVideo *vid = (GstCameraBinVideo *) u_data; |
|
427 |
|
428 if (vid->calculate_adjust_ts_aud) { |
|
429 GstEvent *event; |
|
430 GstPad *peerpad = NULL; |
|
431 vid->adjust_ts_aud = GST_BUFFER_TIMESTAMP (buffer) - vid->last_ts_aud; |
|
432 vid->calculate_adjust_ts_aud = FALSE; |
|
433 event = gst_event_new_new_segment (FALSE, 1.0, GST_FORMAT_TIME, |
|
434 0, GST_CLOCK_TIME_NONE, vid->last_ts_aud); |
|
435 peerpad = gst_pad_get_peer (pad); |
|
436 if (peerpad) { |
|
437 gst_pad_send_event (peerpad, event); |
|
438 gst_object_unref (peerpad); |
|
439 } |
|
440 GST_LOG_OBJECT (vid, "aud ts adjustment: %" GST_TIME_FORMAT, |
|
441 GST_TIME_ARGS (vid->adjust_ts_aud)); |
|
442 GST_BUFFER_FLAG_SET (buffer, GST_BUFFER_FLAG_DISCONT); |
|
443 } |
|
444 GST_BUFFER_TIMESTAMP (buffer) -= vid->adjust_ts_aud; |
|
445 vid->last_ts_aud = GST_BUFFER_TIMESTAMP (buffer); |
|
446 if (GST_BUFFER_DURATION_IS_VALID (buffer)) |
|
447 vid->last_ts_aud += GST_BUFFER_DURATION (buffer); |
|
448 GST_LOG ("buffer out with size %d ts %" GST_TIME_FORMAT, |
|
449 GST_BUFFER_SIZE (buffer), GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buffer))); |
|
450 return TRUE; |
|
451 } |
|
452 |
|
453 /** |
|
454 * camerabin_video_sink_have_event: |
|
455 * @pad: video bin sink pad |
|
456 * @event: received event |
|
457 * @u_data: video bin object |
|
458 * |
|
459 * Event probe for video bin eos handling. |
|
460 * Copies the eos event to audio branch of video bin. |
|
461 * |
|
462 * Returns: FALSE to drop the event, TRUE otherwise |
|
463 */ |
|
464 static gboolean |
|
465 camerabin_video_sink_have_event (GstPad * pad, GstEvent * event, |
|
466 gpointer u_data) |
|
467 { |
|
468 GstCameraBinVideo *vid = (GstCameraBinVideo *) u_data; |
|
469 gboolean ret = TRUE; |
|
470 |
|
471 GST_DEBUG_OBJECT (vid, "got videobin sink event: %s", |
|
472 GST_EVENT_TYPE_NAME (event)); |
|
473 |
|
474 switch (GST_EVENT_TYPE (event)) { |
|
475 case GST_EVENT_EOS: |
|
476 if (vid->aud_src) { |
|
477 GST_DEBUG_OBJECT (vid, "copying %s to audio branch", |
|
478 GST_EVENT_TYPE_NAME (event)); |
|
479 gst_element_send_event (vid->aud_src, gst_event_copy (event)); |
|
480 } |
|
481 |
|
482 /* If we're paused, we can't pass eos to video now to avoid blocking. |
|
483 Instead send eos when changing to playing next time. */ |
|
484 if (GST_STATE (GST_ELEMENT (vid)) == GST_STATE_PAUSED) { |
|
485 GST_DEBUG_OBJECT (vid, "paused, delay eos sending"); |
|
486 vid->pending_eos = gst_event_ref (event); |
|
487 ret = FALSE; /* Drop the event */ |
|
488 } |
|
489 break; |
|
490 default: |
|
491 break; |
|
492 } |
|
493 return ret; |
|
494 } |
|
495 |
|
496 /** |
|
497 * gst_camerabin_video_create_elements: |
|
498 * @vid: a pointer to #GstCameraBinVideo |
|
499 * |
|
500 * This function creates the needed #GstElements and resources to record videos. |
|
501 * Use gst_camerabin_video_destroy_elements() to free these resources. |
|
502 * |
|
503 * Returns: %TRUE if succeeded or FALSE if failed |
|
504 */ |
|
505 static gboolean |
|
506 gst_camerabin_video_create_elements (GstCameraBinVideo * vid) |
|
507 { |
|
508 GstPad *pad = NULL, *vid_sinkpad = NULL, *vid_srcpad = NULL; |
|
509 GstBin *vidbin = GST_BIN (vid); |
|
510 GstElement *queue = NULL; |
|
511 |
|
512 vid->adjust_ts_video = 0; |
|
513 vid->last_ts_video = 0; |
|
514 vid->calculate_adjust_ts_video = FALSE; |
|
515 |
|
516 vid->adjust_ts_aud = 0; |
|
517 vid->last_ts_aud = 0; |
|
518 vid->calculate_adjust_ts_aud = FALSE; |
|
519 |
|
520 /* Add video post processing element if any */ |
|
521 if (vid->user_post) { |
|
522 if (!gst_camerabin_add_element (vidbin, vid->user_post)) { |
|
523 goto error; |
|
524 } |
|
525 vid_sinkpad = gst_element_get_static_pad (vid->user_post, "sink"); |
|
526 } |
|
527 |
|
528 /* Add tee element */ |
|
529 if (!(vid->tee = gst_camerabin_create_and_add_element (vidbin, "tee"))) { |
|
530 goto error; |
|
531 } |
|
532 |
|
533 /* Set up sink ghost pad for video bin */ |
|
534 if (!vid_sinkpad) { |
|
535 vid_sinkpad = gst_element_get_static_pad (vid->tee, "sink"); |
|
536 } |
|
537 gst_ghost_pad_set_target (GST_GHOST_PAD (vid->sinkpad), vid_sinkpad); |
|
538 |
|
539 |
|
540 /* Add queue element for video */ |
|
541 vid->tee_video_srcpad = gst_element_get_request_pad (vid->tee, "src%d"); |
|
542 if (!(vid->video_queue = |
|
543 gst_camerabin_create_and_add_element (vidbin, "queue"))) { |
|
544 goto error; |
|
545 } |
|
546 |
|
547 /* Add probe for rewriting video timestamps */ |
|
548 gst_pad_add_buffer_probe (vid->tee_video_srcpad, |
|
549 G_CALLBACK (camerabin_video_pad_tee_src0_have_buffer), vid); |
|
550 |
|
551 |
|
552 #ifdef USE_TIMEOVERLAY |
|
553 /* Add timeoverlay element to visualize elapsed time for debugging */ |
|
554 if (!(gst_camerabin_create_and_add_element (vidbin, "timeoverlay"))) { |
|
555 goto error; |
|
556 } |
|
557 #endif |
|
558 |
|
559 /* Add user set or default video encoder element */ |
|
560 if (vid->user_vid_enc) { |
|
561 vid->vid_enc = vid->user_vid_enc; |
|
562 if (!gst_camerabin_add_element (vidbin, vid->vid_enc)) { |
|
563 goto error; |
|
564 } |
|
565 } else if (!(vid->vid_enc = |
|
566 gst_camerabin_create_and_add_element (vidbin, DEFAULT_VID_ENC))) { |
|
567 goto error; |
|
568 } |
|
569 |
|
570 /* Add user set or default muxer element */ |
|
571 if (vid->user_mux) { |
|
572 vid->muxer = vid->user_mux; |
|
573 if (!gst_camerabin_add_element (vidbin, vid->muxer)) { |
|
574 goto error; |
|
575 } |
|
576 } else if (!(vid->muxer = |
|
577 gst_camerabin_create_and_add_element (vidbin, DEFAULT_MUX))) { |
|
578 goto error; |
|
579 } |
|
580 |
|
581 /* Add sink element for storing the video */ |
|
582 if (!(vid->sink = |
|
583 gst_camerabin_create_and_add_element (vidbin, DEFAULT_SINK))) { |
|
584 goto error; |
|
585 } |
|
586 g_object_set (G_OBJECT (vid->sink), "location", vid->filename->str, NULL); |
|
587 |
|
588 |
|
589 /* Add user set or default audio source element */ |
|
590 if (vid->user_aud_src) { |
|
591 vid->aud_src = vid->user_aud_src; |
|
592 if (!gst_camerabin_add_element (vidbin, vid->aud_src)) { |
|
593 goto error; |
|
594 } |
|
595 } else if (!(vid->aud_src = |
|
596 gst_camerabin_create_and_add_element (vidbin, DEFAULT_AUD_SRC))) { |
|
597 goto error; |
|
598 } |
|
599 |
|
600 /* Add queue element for audio */ |
|
601 if (!(queue = gst_camerabin_create_and_add_element (vidbin, "queue"))) { |
|
602 goto error; |
|
603 } |
|
604 queue = NULL; |
|
605 |
|
606 /* Add optional audio conversion and volume elements and |
|
607 raise no errors if adding them fails */ |
|
608 #ifdef USE_AUDIO_CONVERSION |
|
609 if (!gst_camerabin_try_add_element (vidbin, |
|
610 gst_element_factory_make ("audioconvert", NULL))) { |
|
611 GST_WARNING_OBJECT (vid, "unable to add audio conversion element"); |
|
612 /* gst_camerabin_try_add_element() destroyed the element */ |
|
613 } |
|
614 #endif |
|
615 vid->volume = gst_element_factory_make ("volume", NULL); |
|
616 if (!gst_camerabin_try_add_element (vidbin, vid->volume)) { |
|
617 GST_WARNING_OBJECT (vid, "unable to add volume element"); |
|
618 /* gst_camerabin_try_add_element() destroyed the element */ |
|
619 vid->volume = NULL; |
|
620 } |
|
621 |
|
622 /* Add user set or default audio encoder element */ |
|
623 if (vid->user_aud_enc) { |
|
624 vid->aud_enc = vid->user_aud_enc; |
|
625 if (!gst_camerabin_add_element (vidbin, vid->aud_enc)) { |
|
626 goto error; |
|
627 } |
|
628 } else if (!(vid->aud_enc = |
|
629 gst_camerabin_create_and_add_element (vidbin, DEFAULT_AUD_ENC))) { |
|
630 goto error; |
|
631 } |
|
632 |
|
633 /* Link audio part to the muxer */ |
|
634 if (!gst_element_link (vid->aud_enc, vid->muxer)) { |
|
635 GST_ELEMENT_ERROR (vid, CORE, NEGOTIATION, (NULL), |
|
636 ("linking audio encoder and muxer failed")); |
|
637 goto error; |
|
638 } |
|
639 |
|
640 /* Add queue leading out of the video bin and to view finder */ |
|
641 vid->tee_vf_srcpad = gst_element_get_request_pad (vid->tee, "src%d"); |
|
642 if (!(queue = gst_camerabin_create_and_add_element (vidbin, "queue"))) { |
|
643 goto error; |
|
644 } |
|
645 /* Set queue leaky, we don't want to block video encoder feed, but |
|
646 prefer leaking view finder buffers instead. */ |
|
647 g_object_set (G_OBJECT (queue), "leaky", 2, NULL); |
|
648 |
|
649 /* Set up src ghost pad for video bin */ |
|
650 vid_srcpad = gst_element_get_static_pad (queue, "src"); |
|
651 gst_ghost_pad_set_target (GST_GHOST_PAD (vid->srcpad), vid_srcpad); |
|
652 /* Never let video bin eos events reach view finder */ |
|
653 gst_pad_add_event_probe (vid_srcpad, |
|
654 G_CALLBACK (gst_camerabin_drop_eos_probe), vid); |
|
655 |
|
656 pad = gst_element_get_static_pad (vid->aud_src, "src"); |
|
657 gst_pad_add_buffer_probe (pad, |
|
658 G_CALLBACK (camerabin_video_pad_aud_src_have_buffer), vid); |
|
659 gst_object_unref (pad); |
|
660 |
|
661 GST_DEBUG ("created video elements"); |
|
662 |
|
663 return TRUE; |
|
664 |
|
665 error: |
|
666 |
|
667 gst_camerabin_video_destroy_elements (vid); |
|
668 |
|
669 return FALSE; |
|
670 |
|
671 } |
|
672 |
|
673 /** |
|
674 * gst_camerabin_video_destroy_elements: |
|
675 * @vid: a pointer to #GstCameraBinVideo |
|
676 * |
|
677 * This function destroys all the elements created by |
|
678 * gst_camerabin_video_create_elements(). |
|
679 * |
|
680 */ |
|
681 static void |
|
682 gst_camerabin_video_destroy_elements (GstCameraBinVideo * vid) |
|
683 { |
|
684 GST_DEBUG ("destroying video elements"); |
|
685 |
|
686 /* Release tee request pads */ |
|
687 if (vid->tee_video_srcpad) { |
|
688 gst_element_release_request_pad (vid->tee, vid->tee_video_srcpad); |
|
689 vid->tee_video_srcpad = NULL; |
|
690 } |
|
691 if (vid->tee_vf_srcpad) { |
|
692 gst_element_release_request_pad (vid->tee, vid->tee_vf_srcpad); |
|
693 vid->tee_vf_srcpad = NULL; |
|
694 } |
|
695 |
|
696 gst_ghost_pad_set_target (GST_GHOST_PAD (vid->sinkpad), NULL); |
|
697 gst_ghost_pad_set_target (GST_GHOST_PAD (vid->srcpad), NULL); |
|
698 |
|
699 gst_camerabin_remove_elements_from_bin (GST_BIN (vid)); |
|
700 |
|
701 vid->aud_src = NULL; |
|
702 vid->sink = NULL; |
|
703 vid->tee = NULL; |
|
704 vid->volume = NULL; |
|
705 vid->video_queue = NULL; |
|
706 vid->vid_enc = NULL; |
|
707 vid->aud_enc = NULL; |
|
708 vid->muxer = NULL; |
|
709 |
|
710 if (vid->pending_eos) { |
|
711 gst_event_unref (vid->pending_eos); |
|
712 vid->pending_eos = NULL; |
|
713 } |
|
714 |
|
715 return; |
|
716 } |
|
717 |
|
718 /* |
|
719 * Set & get mute and video capture elements |
|
720 */ |
|
721 |
|
722 void |
|
723 gst_camerabin_video_set_mute (GstCameraBinVideo * vid, gboolean mute) |
|
724 { |
|
725 if (vid && vid->volume) { |
|
726 GST_DEBUG_OBJECT (vid, "setting mute %s", mute ? "on" : "off"); |
|
727 g_object_set (vid->volume, "mute", mute, NULL); |
|
728 } |
|
729 } |
|
730 |
|
731 void |
|
732 gst_camerabin_video_set_post (GstCameraBinVideo * vid, GstElement * post) |
|
733 { |
|
734 GstElement **user_post; |
|
735 GST_DEBUG_OBJECT (vid, "setting video post processing: %" GST_PTR_FORMAT, |
|
736 post); |
|
737 GST_OBJECT_LOCK (vid); |
|
738 user_post = &vid->user_post; |
|
739 gst_object_replace ((GstObject **) user_post, GST_OBJECT (post)); |
|
740 GST_OBJECT_UNLOCK (vid); |
|
741 } |
|
742 |
|
743 void |
|
744 gst_camerabin_video_set_video_enc (GstCameraBinVideo * vid, |
|
745 GstElement * video_enc) |
|
746 { |
|
747 GstElement **user_vid_enc; |
|
748 GST_DEBUG_OBJECT (vid, "setting video encoder: %" GST_PTR_FORMAT, video_enc); |
|
749 GST_OBJECT_LOCK (vid); |
|
750 user_vid_enc = &vid->user_vid_enc; |
|
751 gst_object_replace ((GstObject **) user_vid_enc, GST_OBJECT (video_enc)); |
|
752 GST_OBJECT_UNLOCK (vid); |
|
753 } |
|
754 |
|
755 void |
|
756 gst_camerabin_video_set_audio_enc (GstCameraBinVideo * vid, |
|
757 GstElement * audio_enc) |
|
758 { |
|
759 GstElement **user_aud_enc; |
|
760 GST_DEBUG_OBJECT (vid, "setting audio encoder: %" GST_PTR_FORMAT, audio_enc); |
|
761 GST_OBJECT_LOCK (vid); |
|
762 user_aud_enc = &vid->user_aud_enc; |
|
763 gst_object_replace ((GstObject **) user_aud_enc, GST_OBJECT (audio_enc)); |
|
764 GST_OBJECT_UNLOCK (vid); |
|
765 } |
|
766 |
|
767 void |
|
768 gst_camerabin_video_set_muxer (GstCameraBinVideo * vid, GstElement * muxer) |
|
769 { |
|
770 GstElement **user_mux; |
|
771 GST_DEBUG_OBJECT (vid, "setting muxer: %" GST_PTR_FORMAT, muxer); |
|
772 GST_OBJECT_LOCK (vid); |
|
773 user_mux = &vid->user_mux; |
|
774 gst_object_replace ((GstObject **) user_mux, GST_OBJECT (muxer)); |
|
775 GST_OBJECT_UNLOCK (vid); |
|
776 } |
|
777 |
|
778 void |
|
779 gst_camerabin_video_set_audio_src (GstCameraBinVideo * vid, |
|
780 GstElement * audio_src) |
|
781 { |
|
782 GstElement **user_aud_src; |
|
783 GST_DEBUG_OBJECT (vid, "setting audio source: %" GST_PTR_FORMAT, audio_src); |
|
784 GST_OBJECT_LOCK (vid); |
|
785 user_aud_src = &vid->user_aud_src; |
|
786 gst_object_replace ((GstObject **) user_aud_src, GST_OBJECT (audio_src)); |
|
787 GST_OBJECT_UNLOCK (vid); |
|
788 } |
|
789 |
|
790 gboolean |
|
791 gst_camerabin_video_get_mute (GstCameraBinVideo * vid) |
|
792 { |
|
793 gboolean mute = ARG_DEFAULT_MUTE; |
|
794 |
|
795 if (vid && vid->volume) { |
|
796 g_object_get (vid->volume, "mute", &mute, NULL); |
|
797 } |
|
798 return mute; |
|
799 } |
|
800 |
|
801 GstElement * |
|
802 gst_camerabin_video_get_post (GstCameraBinVideo * vid) |
|
803 { |
|
804 return vid->user_post; |
|
805 } |
|
806 |
|
807 GstElement * |
|
808 gst_camerabin_video_get_video_enc (GstCameraBinVideo * vid) |
|
809 { |
|
810 return vid->vid_enc ? vid->vid_enc : vid->user_vid_enc; |
|
811 } |
|
812 |
|
813 GstElement * |
|
814 gst_camerabin_video_get_audio_enc (GstCameraBinVideo * vid) |
|
815 { |
|
816 return vid->aud_enc ? vid->aud_enc : vid->user_aud_enc; |
|
817 } |
|
818 |
|
819 GstElement * |
|
820 gst_camerabin_video_get_muxer (GstCameraBinVideo * vid) |
|
821 { |
|
822 return vid->muxer ? vid->muxer : vid->user_mux; |
|
823 } |
|
824 |
|
825 GstElement * |
|
826 gst_camerabin_video_get_audio_src (GstCameraBinVideo * vid) |
|
827 { |
|
828 return vid->aud_src ? vid->aud_src : vid->user_aud_src; |
|
829 } |
|