gst_plugins_good/gst/camerabin/camerabinvideo.c
changeset 2 5505e8908944
equal deleted inserted replaced
1:4c282e7dd6d3 2:5505e8908944
       
     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 }