static gboolean gst_wrapper_camera_bin_src_set_mode (GstBaseCameraSrc * bcamsrc, GstCameraBinMode mode) { GstPhotography *photography = gst_base_camera_src_get_photography (bcamsrc); GstWrapperCameraBinSrc *self = GST_WRAPPER_CAMERA_BIN_SRC (bcamsrc); self->mode = mode; if (self->output_selector) { if (mode == MODE_IMAGE) { g_object_set (self->output_selector, "active-pad", self->outsel_imgpad, NULL); } else { g_object_set (self->output_selector, "active-pad", self->outsel_vidpad, NULL); } } if (photography) { if (g_object_class_find_property (G_OBJECT_GET_CLASS (photography), "capture-mode")) { g_object_set (G_OBJECT (photography), "capture-mode", mode, NULL); } } else { gst_wrapper_camera_bin_reset_video_src_caps (self, NULL); } return TRUE; }
static GstStateChangeReturn gst_wrapper_camera_bin_src_change_state (GstElement * element, GstStateChange trans) { GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS; GstWrapperCameraBinSrc *self = GST_WRAPPER_CAMERA_BIN_SRC (element); ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, trans); if (ret == GST_STATE_CHANGE_FAILURE) goto end; switch (trans) { case GST_STATE_CHANGE_PAUSED_TO_READY: self->video_renegotiate = TRUE; self->image_renegotiate = TRUE; self->drop_newseg = FALSE; break; case GST_STATE_CHANGE_READY_TO_NULL: break; case GST_STATE_CHANGE_NULL_TO_READY: break; default: break; } end: return ret; }
static void gst_wrapper_camera_bin_src_dispose (GObject * object) { GstWrapperCameraBinSrc *self = GST_WRAPPER_CAMERA_BIN_SRC (object); if (self->outsel_imgpad) { gst_object_unref (self->outsel_imgpad); self->outsel_imgpad = NULL; } if (self->outsel_vidpad) { gst_object_unref (self->outsel_vidpad); self->outsel_vidpad = NULL; } if (self->app_vid_src) { gst_object_unref (self->app_vid_src); self->app_vid_src = NULL; } if (self->app_vid_filter) { gst_object_unref (self->app_vid_filter); self->app_vid_filter = NULL; } gst_caps_replace (&self->image_capture_caps, NULL); G_OBJECT_CLASS (parent_class)->dispose (object); }
static gboolean gst_wrapper_camera_bin_src_set_mode (GstBaseCameraSrc * bcamsrc, GstCameraBinMode mode) { GstPhotography *photography = (GstPhotography *) gst_bin_get_by_interface (GST_BIN_CAST (bcamsrc), GST_TYPE_PHOTOGRAPHY); GstWrapperCameraBinSrc *self = GST_WRAPPER_CAMERA_BIN_SRC (bcamsrc); if (self->output_selector) { if (mode == MODE_IMAGE) { self->image_renegotiate = TRUE; g_object_set (self->output_selector, "active-pad", self->outsel_imgpad, NULL); } else { self->video_renegotiate = TRUE; g_object_set (self->output_selector, "active-pad", self->outsel_vidpad, NULL); } } self->mode = mode; if (photography) { if (g_object_class_find_property (G_OBJECT_GET_CLASS (photography), "capture-mode")) { g_object_set (G_OBJECT (photography), "capture-mode", mode, NULL); } } else { GstCaps *anycaps = gst_caps_new_any (); gst_wrapper_camera_bin_reset_video_src_caps (self, anycaps); gst_caps_unref (anycaps); } return TRUE; }
static void gst_wrapper_camera_bin_src_set_property (GObject * object, guint prop_id, const GValue * value, GParamSpec * pspec) { GstWrapperCameraBinSrc *self = GST_WRAPPER_CAMERA_BIN_SRC (object); switch (prop_id) { case PROP_VIDEO_SRC: if (GST_STATE (self) != GST_STATE_NULL) { GST_ELEMENT_ERROR (self, CORE, FAILED, ("camerasrc must be in NULL state when setting the video source element"), (NULL)); } else { if (self->app_vid_src) gst_object_unref (self->app_vid_src); self->app_vid_src = g_value_get_object (value); if (self->app_vid_src) gst_object_ref (self->app_vid_src); } break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (self, prop_id, pspec); break; } }
/** * gst_wrapper_camera_bin_src_imgsrc_probe: * * Buffer probe called before sending each buffer to image queue. */ static gboolean gst_wrapper_camera_bin_src_imgsrc_probe (GstPad * pad, GstBuffer * buffer, gpointer data) { GstWrapperCameraBinSrc *self = GST_WRAPPER_CAMERA_BIN_SRC (data); GstBaseCameraSrc *camerasrc = GST_BASE_CAMERA_SRC (data); gboolean ret = FALSE; GST_LOG_OBJECT (self, "Image probe, mode %d, capture count %d", camerasrc->mode, self->image_capture_count); g_mutex_lock (camerasrc->capturing_mutex); if (self->image_capture_count > 0) { ret = TRUE; self->image_capture_count--; /* post preview */ /* TODO This can likely be optimized if the viewfinder caps is the same as * the preview caps, avoiding another scaling of the same buffer. */ GST_DEBUG_OBJECT (self, "Posting preview for image"); gst_base_camera_src_post_preview (camerasrc, buffer); if (self->image_capture_count == 0) { gst_base_camera_src_finish_capture (camerasrc); } } g_mutex_unlock (camerasrc->capturing_mutex); return ret; }
static void gst_wrapper_camera_bin_src_dispose (GObject * object) { GstWrapperCameraBinSrc *self = GST_WRAPPER_CAMERA_BIN_SRC (object); if (self->app_vid_src) { gst_object_unref (self->app_vid_src); self->app_vid_src = NULL; } G_OBJECT_CLASS (parent_class)->dispose (object); }
/** * img_capture_prepared: * @data: camerasrc object * @caps: caps describing the prepared image format * * Callback which is called after image capture has been prepared. */ static void img_capture_prepared (gpointer data, GstCaps * caps) { GstWrapperCameraBinSrc *self = GST_WRAPPER_CAMERA_BIN_SRC (data); GST_INFO_OBJECT (self, "image capture prepared"); /* It is possible we are about to get something else that we requested */ if (!gst_caps_is_equal (self->image_capture_caps, caps)) { adapt_image_capture (self, caps); } else { set_capsfilter_caps (self, self->image_capture_caps); } }
/** * gst_wrapper_camera_bin_src_vidsrc_probe: * * Buffer probe called before sending each buffer to image queue. */ static gboolean gst_wrapper_camera_bin_src_vidsrc_probe (GstPad * pad, GstBuffer * buffer, gpointer data) { GstWrapperCameraBinSrc *self = GST_WRAPPER_CAMERA_BIN_SRC (data); GstBaseCameraSrc *camerasrc = GST_BASE_CAMERA_SRC_CAST (self); gboolean ret = FALSE; GST_LOG_OBJECT (self, "Video probe, mode %d, capture status %d", camerasrc->mode, self->video_rec_status); /* TODO do we want to lock for every buffer? */ /* * Note that we can use gst_pad_push_event here because we are a buffer * probe. */ /* TODO shouldn't access this directly */ g_mutex_lock (camerasrc->capturing_mutex); if (self->video_rec_status == GST_VIDEO_RECORDING_STATUS_DONE) { /* NOP */ } else if (self->video_rec_status == GST_VIDEO_RECORDING_STATUS_STARTING) { GstClockTime ts; GST_DEBUG_OBJECT (self, "Starting video recording"); self->video_rec_status = GST_VIDEO_RECORDING_STATUS_RUNNING; ts = GST_BUFFER_TIMESTAMP (buffer); if (!GST_CLOCK_TIME_IS_VALID (ts)) ts = 0; gst_pad_push_event (self->vidsrc, gst_event_new_new_segment (FALSE, 1.0, GST_FORMAT_TIME, ts, -1, 0)); /* post preview */ GST_DEBUG_OBJECT (self, "Posting preview for video"); gst_base_camera_src_post_preview (camerasrc, buffer); ret = TRUE; } else if (self->video_rec_status == GST_VIDEO_RECORDING_STATUS_FINISHING) { /* send eos */ GST_DEBUG_OBJECT (self, "Finishing video recording, pushing eos"); gst_pad_push_event (pad, gst_event_new_eos ()); self->video_rec_status = GST_VIDEO_RECORDING_STATUS_DONE; gst_base_camera_src_finish_capture (camerasrc); } else { ret = TRUE; } g_mutex_unlock (camerasrc->capturing_mutex); return ret; }
static void gst_wrapper_camera_bin_src_set_zoom (GstBaseCameraSrc * bcamsrc, gfloat zoom) { GstWrapperCameraBinSrc *self = GST_WRAPPER_CAMERA_BIN_SRC (bcamsrc); GST_INFO_OBJECT (self, "setting zoom %f", zoom); if (set_videosrc_zoom (self, zoom)) { set_element_zoom (self, ZOOM_1X); GST_INFO_OBJECT (self, "zoom set using videosrc"); } else if (set_element_zoom (self, zoom)) { GST_INFO_OBJECT (self, "zoom set using gst elements"); } else { GST_INFO_OBJECT (self, "setting zoom failed"); } }
static gboolean gst_wrapper_camera_bin_src_src_event (GstPad * pad, GstObject * parent, GstEvent * event) { gboolean ret = TRUE; GstWrapperCameraBinSrc *self = GST_WRAPPER_CAMERA_BIN_SRC (parent); GstPad *upstream_pad = NULL; GST_DEBUG_OBJECT (self, "Handling event %p %" GST_PTR_FORMAT, event, event); switch (GST_EVENT_TYPE (event)) { case GST_EVENT_RECONFIGURE: if (pad == self->imgsrc) { GST_DEBUG_OBJECT (self, "Image mode reconfigure event received"); self->image_renegotiate = TRUE; } else if (pad == self->vidsrc) { GST_DEBUG_OBJECT (self, "Video mode reconfigure event received"); self->video_renegotiate = TRUE; } if (pad == self->imgsrc || pad == self->vidsrc) { gst_event_unref (event); return ret; } break; default: break; } if (pad == self->imgsrc) { upstream_pad = self->outsel_imgpad; } else if (pad == self->vidsrc) { upstream_pad = self->outsel_vidpad; } if (upstream_pad) { ret = gst_pad_send_event (upstream_pad, event); } else { GST_WARNING_OBJECT (self, "Event caught that doesn't have an upstream pad -" "this shouldn't be possible!"); gst_event_unref (event); ret = FALSE; } return ret; }
static void gst_wrapper_camera_bin_src_caps_cb (GObject * gobject, GParamSpec * pspec, gpointer user_data) { GstBaseCameraSrc *bcamsrc = GST_BASE_CAMERA_SRC (user_data); GstWrapperCameraBinSrc *self = GST_WRAPPER_CAMERA_BIN_SRC (user_data); GstPad *src_caps_src_pad; GstCaps *caps = NULL; GstStructure *in_st = NULL; /* get the new caps that were set on the capsfilter that configures the * source */ src_caps_src_pad = gst_element_get_static_pad (self->src_filter, "src"); caps = gst_pad_query_caps (src_caps_src_pad, NULL); gst_object_unref (src_caps_src_pad); GST_DEBUG_OBJECT (self, "src-filter caps changed to %s", gst_caps_to_string (caps)); if (gst_caps_get_size (caps)) { in_st = gst_caps_get_structure (caps, 0); if (in_st) { gst_structure_get_int (in_st, "width", &bcamsrc->width); gst_structure_get_int (in_st, "height", &bcamsrc->height); GST_DEBUG_OBJECT (self, "Source dimensions now: %dx%d", bcamsrc->width, bcamsrc->height); } } /* Update zoom */ gst_base_camera_src_setup_zoom (bcamsrc); /* Update post-zoom capsfilter */ if (self->src_zoom_filter) { GstCaps *filtercaps; g_object_get (G_OBJECT (self->src_zoom_filter), "caps", &filtercaps, NULL); if (!gst_caps_is_equal (filtercaps, caps)) g_object_set (G_OBJECT (self->src_zoom_filter), "caps", caps, NULL); gst_caps_unref (filtercaps); } /* drop our ref on the caps */ gst_caps_unref (caps); };
static void gst_wrapper_camera_bin_src_get_property (GObject * object, guint prop_id, GValue * value, GParamSpec * pspec) { GstWrapperCameraBinSrc *self = GST_WRAPPER_CAMERA_BIN_SRC (object); switch (prop_id) { case PROP_VIDEO_SRC: if (self->src_vid_src) g_value_set_object (value, self->src_vid_src); else g_value_set_object (value, self->app_vid_src); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (self, prop_id, pspec); break; } }
static void gst_wrapper_camera_bin_src_caps_cb (GstPad * pad, GParamSpec * pspec, gpointer user_data) { GstBaseCameraSrc *bcamsrc = GST_BASE_CAMERA_SRC (user_data); GstWrapperCameraBinSrc *self = GST_WRAPPER_CAMERA_BIN_SRC (user_data); GstCaps *caps; GstStructure *in_st = NULL; caps = gst_pad_get_current_caps (pad); GST_DEBUG_OBJECT (self, "src-filter caps changed to %" GST_PTR_FORMAT, caps); if (caps && gst_caps_get_size (caps)) { in_st = gst_caps_get_structure (caps, 0); if (in_st) { gst_structure_get_int (in_st, "width", &bcamsrc->width); gst_structure_get_int (in_st, "height", &bcamsrc->height); GST_DEBUG_OBJECT (self, "Source dimensions now: %dx%d", bcamsrc->width, bcamsrc->height); } } /* Update zoom */ gst_base_camera_src_setup_zoom (bcamsrc); /* Update post-zoom capsfilter */ if (self->src_zoom_filter) { GstCaps *filtercaps; g_object_get (G_OBJECT (self->src_zoom_filter), "caps", &filtercaps, NULL); if (caps != filtercaps && (caps == NULL || filtercaps == NULL || !gst_caps_is_equal (filtercaps, caps))) g_object_set (G_OBJECT (self->src_zoom_filter), "caps", caps, NULL); if (filtercaps) gst_caps_unref (filtercaps); } if (caps) gst_caps_unref (caps); };
static void gst_wrapper_camera_bin_src_stop_capture (GstBaseCameraSrc * camerasrc) { GstWrapperCameraBinSrc *src = GST_WRAPPER_CAMERA_BIN_SRC (camerasrc); /* TODO shoud we access this directly? Maybe a macro is better? */ if (src->mode == MODE_VIDEO) { if (src->video_rec_status == GST_VIDEO_RECORDING_STATUS_STARTING) { GST_DEBUG_OBJECT (src, "Aborting, had not started recording"); src->video_rec_status = GST_VIDEO_RECORDING_STATUS_DONE; } else if (src->video_rec_status == GST_VIDEO_RECORDING_STATUS_RUNNING) { GST_DEBUG_OBJECT (src, "Marking video recording as finishing"); src->video_rec_status = GST_VIDEO_RECORDING_STATUS_FINISHING; } } else { src->image_capture_count = 0; } }
static gboolean gst_wrapper_camera_bin_src_start_capture (GstBaseCameraSrc * camerasrc) { GstWrapperCameraBinSrc *src = GST_WRAPPER_CAMERA_BIN_SRC (camerasrc); /* TODO should we access this directly? Maybe a macro is better? */ if (src->mode == MODE_IMAGE) { start_image_capture (src); src->image_capture_count = 1; } else if (src->mode == MODE_VIDEO) { GstCaps *caps = NULL; if (src->video_renegotiate) { GstCaps *anycaps = gst_caps_new_any (); g_mutex_unlock (&camerasrc->capturing_mutex); gst_wrapper_camera_bin_reset_video_src_caps (src, anycaps); g_mutex_lock (&camerasrc->capturing_mutex); /* clean capsfilter caps so they don't interfere here */ g_object_set (src->src_filter, "caps", NULL, NULL); if (src->src_zoom_filter) g_object_set (src->src_zoom_filter, "caps", NULL, NULL); GST_DEBUG_OBJECT (src, "Getting allowed videosrc caps"); caps = gst_pad_get_allowed_caps (src->vidsrc); GST_DEBUG_OBJECT (src, "Video src caps %" GST_PTR_FORMAT, caps); src->video_renegotiate = FALSE; g_mutex_unlock (&camerasrc->capturing_mutex); gst_wrapper_camera_bin_reset_video_src_caps (src, caps); g_mutex_lock (&camerasrc->capturing_mutex); gst_caps_unref (caps); gst_caps_unref (anycaps); } if (src->video_rec_status == GST_VIDEO_RECORDING_STATUS_DONE) { src->video_rec_status = GST_VIDEO_RECORDING_STATUS_STARTING; } } else { g_assert_not_reached (); return FALSE; } return TRUE; }
static gboolean gst_wrapper_camera_bin_src_event (GstPad * pad, GstEvent * event) { GstWrapperCameraBinSrc *src = GST_WRAPPER_CAMERA_BIN_SRC (GST_PAD_PARENT (pad)); const GstStructure *structure; structure = gst_event_get_structure (event); if (structure && gst_structure_has_name (structure, "renegotiate")) { GST_DEBUG_OBJECT (src, "Received renegotiate on pad %s", GST_PAD_NAME (pad)); if (pad == src->imgsrc) { src->image_renegotiate = TRUE; } else if (pad == src->vidsrc) { src->video_renegotiate = TRUE; } } return src->srcpad_event_func (pad, event); }
/** * gst_wrapper_camera_bin_src_imgsrc_probe: * * Buffer probe called before sending each buffer to image queue. */ static gboolean gst_wrapper_camera_bin_src_imgsrc_probe (GstPad * pad, GstBuffer * buffer, gpointer data) { GstWrapperCameraBinSrc *self = GST_WRAPPER_CAMERA_BIN_SRC (data); GstBaseCameraSrc *camerasrc = GST_BASE_CAMERA_SRC (data); gboolean ret = FALSE; GST_LOG_OBJECT (self, "Image probe, mode %d, capture count %d", camerasrc->mode, self->image_capture_count); g_mutex_lock (camerasrc->capturing_mutex); if (self->image_capture_count > 0) { ret = TRUE; self->image_capture_count--; if (self->image_capture_count == 0) { gst_base_camera_src_finish_capture (camerasrc); } } g_mutex_unlock (camerasrc->capturing_mutex); return ret; }
/** * gst_wrapper_camera_bin_src_imgsrc_probe: * * Buffer probe called before sending each buffer to image queue. */ static GstPadProbeReturn gst_wrapper_camera_bin_src_imgsrc_probe (GstPad * pad, GstPadProbeInfo * info, gpointer data) { GstWrapperCameraBinSrc *self = GST_WRAPPER_CAMERA_BIN_SRC (data); GstBaseCameraSrc *camerasrc = GST_BASE_CAMERA_SRC (data); GstBuffer *buffer = GST_BUFFER (info->data); GstPadProbeReturn ret = GST_PAD_PROBE_DROP; GST_LOG_OBJECT (self, "Image probe, mode %d, capture count %d bufsize: %" G_GSIZE_FORMAT, camerasrc->mode, self->image_capture_count, gst_buffer_get_size (buffer)); g_mutex_lock (&camerasrc->capturing_mutex); if (self->image_capture_count > 0) { GstSample *sample; GstCaps *caps; ret = GST_PAD_PROBE_OK; self->image_capture_count--; /* post preview */ /* TODO This can likely be optimized if the viewfinder caps is the same as * the preview caps, avoiding another scaling of the same buffer. */ GST_DEBUG_OBJECT (self, "Posting preview for image"); caps = gst_pad_get_current_caps (pad); sample = gst_sample_new (buffer, caps, NULL, NULL); gst_base_camera_src_post_preview (camerasrc, sample); gst_caps_unref (caps); gst_sample_unref (sample); if (self->image_capture_count == 0) { gst_base_camera_src_finish_capture (camerasrc); } } g_mutex_unlock (&camerasrc->capturing_mutex); return ret; }
/** * gst_wrapper_camera_bin_src_construct_pipeline: * @bcamsrc: camerasrc object * * This function creates and links the elements of the camerasrc bin * videosrc ! cspconv ! srcfilter ! cspconv ! capsfilter ! crop ! scale ! \ * capsfilter ! tee name=t * t. ! ... (viewfinder pad) * t. ! output-selector name=outsel * outsel. ! (image pad) * outsel. ! (video pad) * * Returns: TRUE, if elements were successfully created, FALSE otherwise */ static gboolean gst_wrapper_camera_bin_src_construct_pipeline (GstBaseCameraSrc * bcamsrc) { GstWrapperCameraBinSrc *self = GST_WRAPPER_CAMERA_BIN_SRC (bcamsrc); GstBin *cbin = GST_BIN (bcamsrc); GstElement *tee; GstElement *filter_csp; GstElement *src_csp; GstElement *capsfilter; gboolean ret = FALSE; GstPad *vf_pad; GstPad *tee_capture_pad; GstPad *src_caps_src_pad; if (!self->elements_created) { GST_DEBUG_OBJECT (self, "constructing pipeline"); /* Add application set or default video src element */ if (!(self->src_vid_src = gst_camerabin_setup_default_element (cbin, self->app_vid_src, "autovideosrc", DEFAULT_VIDEOSRC, "camerasrc-real-src"))) { self->src_vid_src = NULL; goto done; } else { if (!gst_camerabin_add_element (cbin, self->src_vid_src)) { goto done; } } /* we lost the reference */ self->app_vid_src = NULL; /* we listen for changes to max-zoom in the video src so that * we can proxy them to the basecamerasrc property */ if (g_object_class_find_property (G_OBJECT_GET_CLASS (bcamsrc), "max-zoom")) { g_signal_connect (G_OBJECT (self->src_vid_src), "notify::max-zoom", (GCallback) gst_wrapper_camera_bin_src_max_zoom_cb, bcamsrc); } /* add a buffer probe to the src elemento to drop EOS from READY->NULL */ { GstPad *pad; pad = gst_element_get_static_pad (self->src_vid_src, "src"); self->src_event_probe_id = gst_pad_add_event_probe (pad, (GCallback) gst_wrapper_camera_src_src_event_probe, self); gst_object_unref (pad); } if (!gst_camerabin_create_and_add_element (cbin, "ffmpegcolorspace", "src-colorspace")) goto done; if (!(self->src_filter = gst_camerabin_create_and_add_element (cbin, "capsfilter", "src-capsfilter"))) goto done; /* attach to notify::caps on the first capsfilter and use a callback * to recalculate the zoom properties when these caps change and to * propagate the caps to the second capsfilter */ src_caps_src_pad = gst_element_get_static_pad (self->src_filter, "src"); g_signal_connect (src_caps_src_pad, "notify::caps", G_CALLBACK (gst_wrapper_camera_bin_src_caps_cb), self); gst_object_unref (src_caps_src_pad); if (!(self->src_zoom_crop = gst_camerabin_create_and_add_element (cbin, "videocrop", "zoom-crop"))) goto done; if (!(self->src_zoom_scale = gst_camerabin_create_and_add_element (cbin, "videoscale", "zoom-scale"))) goto done; if (!(self->src_zoom_filter = gst_camerabin_create_and_add_element (cbin, "capsfilter", "zoom-capsfilter"))) goto done; if (!(tee = gst_camerabin_create_and_add_element (cbin, "tee", "camerasrc-tee"))) goto done; /* viewfinder pad */ vf_pad = gst_element_get_request_pad (tee, "src%d"); g_object_set (tee, "alloc-pad", vf_pad, NULL); gst_ghost_pad_set_target (GST_GHOST_PAD (self->vfsrc), vf_pad); gst_object_unref (vf_pad); /* image/video pad from tee */ tee_capture_pad = gst_element_get_request_pad (tee, "src%d"); self->output_selector = gst_element_factory_make ("output-selector", "outsel"); g_object_set (self->output_selector, "pad-negotiation-mode", 0, NULL); gst_bin_add (GST_BIN (self), self->output_selector); { GstPad *pad = gst_element_get_static_pad (self->output_selector, "sink"); /* check return TODO */ gst_pad_link (tee_capture_pad, pad); gst_object_unref (pad); } gst_object_unref (tee_capture_pad); /* Create the 2 output pads for video and image */ self->outsel_vidpad = gst_element_get_request_pad (self->output_selector, "src%d"); self->outsel_imgpad = gst_element_get_request_pad (self->output_selector, "src%d"); g_assert (self->outsel_vidpad != NULL); g_assert (self->outsel_imgpad != NULL); gst_pad_add_buffer_probe (self->outsel_imgpad, G_CALLBACK (gst_wrapper_camera_bin_src_imgsrc_probe), self); gst_pad_add_buffer_probe (self->outsel_vidpad, G_CALLBACK (gst_wrapper_camera_bin_src_vidsrc_probe), self); gst_ghost_pad_set_target (GST_GHOST_PAD (self->imgsrc), self->outsel_imgpad); gst_ghost_pad_set_target (GST_GHOST_PAD (self->vidsrc), self->outsel_vidpad); if (bcamsrc->mode == MODE_IMAGE) { g_object_set (self->output_selector, "active-pad", self->outsel_imgpad, NULL); } else { g_object_set (self->output_selector, "active-pad", self->outsel_vidpad, NULL); } gst_pad_set_active (self->vfsrc, TRUE); gst_pad_set_active (self->imgsrc, TRUE); /* XXX ??? */ gst_pad_set_active (self->vidsrc, TRUE); /* XXX ??? */ } /* Do this even if pipeline is constructed */ if (self->video_filter) { /* check if we need to replace the current one */ if (self->video_filter != self->app_vid_filter) { gst_bin_remove (cbin, self->video_filter); gst_object_unref (self->video_filter); self->video_filter = NULL; filter_csp = gst_bin_get_by_name (cbin, "filter-colorspace"); gst_bin_remove (cbin, filter_csp); gst_object_unref (filter_csp); filter_csp = NULL; } } if (!self->video_filter) { if (self->app_vid_filter) { self->video_filter = gst_object_ref (self->app_vid_filter); filter_csp = gst_element_factory_make ("ffmpegcolorspace", "filter-colorspace"); gst_bin_add_many (cbin, self->video_filter, filter_csp, NULL); src_csp = gst_bin_get_by_name (cbin, "src-colorspace"); capsfilter = gst_bin_get_by_name (cbin, "src-capsfilter"); if (gst_pad_is_linked (gst_element_get_static_pad (src_csp, "src"))) gst_element_unlink (src_csp, capsfilter); if (!gst_element_link_many (src_csp, self->video_filter, filter_csp, capsfilter, NULL)) goto done; } } ret = TRUE; self->elements_created = TRUE; done: return ret; }
/** * gst_wrapper_camera_bin_src_construct_pipeline: * @bcamsrc: camerasrc object * * This function creates and links the elements of the camerasrc bin * videosrc ! cspconv ! srcfilter ! cspconv ! capsfilter ! crop ! scale ! \ * capsfilter ! tee name=t * t. ! ... (viewfinder pad) * t. ! output-selector name=outsel * outsel. ! (image pad) * outsel. ! (video pad) * * Returns: TRUE, if elements were successfully created, FALSE otherwise */ static gboolean gst_wrapper_camera_bin_src_construct_pipeline (GstBaseCameraSrc * bcamsrc) { GstWrapperCameraBinSrc *self = GST_WRAPPER_CAMERA_BIN_SRC (bcamsrc); GstBin *cbin = GST_BIN (bcamsrc); GstElement *tee; GstElement *filter_csp; GstElement *src_csp; GstElement *capsfilter; gboolean ret = FALSE; GstPad *vf_pad; GstPad *tee_capture_pad; GstPad *src_caps_src_pad; /* checks and adds a new video src if needed */ if (!check_and_replace_src (self)) goto done; if (!self->elements_created) { GST_DEBUG_OBJECT (self, "constructing pipeline"); if (!gst_camerabin_create_and_add_element (cbin, "videoconvert", "src-videoconvert")) goto done; if (self->app_vid_filter) { self->video_filter = gst_object_ref (self->app_vid_filter); if (!gst_camerabin_add_element (cbin, self->video_filter)) goto done; if (!gst_camerabin_create_and_add_element (cbin, "videoconvert", "filter-videoconvert")) goto done; } if (!(self->src_filter = gst_camerabin_create_and_add_element (cbin, "capsfilter", "src-capsfilter"))) goto done; /* attach to notify::caps on the first capsfilter and use a callback * to recalculate the zoom properties when these caps change and to * propagate the caps to the second capsfilter */ src_caps_src_pad = gst_element_get_static_pad (self->src_filter, "src"); g_signal_connect (src_caps_src_pad, "notify::caps", G_CALLBACK (gst_wrapper_camera_bin_src_caps_cb), self); gst_object_unref (src_caps_src_pad); if (!(self->src_zoom_crop = gst_camerabin_create_and_add_element (cbin, "videocrop", "zoom-crop"))) goto done; if (!(self->src_zoom_scale = gst_camerabin_create_and_add_element (cbin, "videoscale", "zoom-scale"))) goto done; if (!(self->src_zoom_filter = gst_camerabin_create_and_add_element (cbin, "capsfilter", "zoom-capsfilter"))) goto done; if (!(tee = gst_camerabin_create_and_add_element (cbin, "tee", "camerasrc-tee"))) goto done; /* viewfinder pad */ vf_pad = gst_element_get_request_pad (tee, "src_%u"); g_object_set (tee, "alloc-pad", vf_pad, NULL); gst_ghost_pad_set_target (GST_GHOST_PAD (self->vfsrc), vf_pad); gst_object_unref (vf_pad); /* image/video pad from tee */ tee_capture_pad = gst_element_get_request_pad (tee, "src_%u"); self->output_selector = gst_element_factory_make ("output-selector", "outsel"); g_object_set (self->output_selector, "pad-negotiation-mode", 2, NULL); gst_bin_add (GST_BIN (self), self->output_selector); { GstPad *pad = gst_element_get_static_pad (self->output_selector, "sink"); /* check return TODO */ gst_pad_link (tee_capture_pad, pad); gst_object_unref (pad); } gst_object_unref (tee_capture_pad); /* Create the 2 output pads for video and image */ self->outsel_vidpad = gst_element_get_request_pad (self->output_selector, "src_%u"); self->outsel_imgpad = gst_element_get_request_pad (self->output_selector, "src_%u"); g_assert (self->outsel_vidpad != NULL); g_assert (self->outsel_imgpad != NULL); gst_pad_add_probe (self->outsel_imgpad, GST_PAD_PROBE_TYPE_BUFFER, gst_wrapper_camera_bin_src_imgsrc_probe, gst_object_ref (self), gst_object_unref); gst_pad_add_probe (self->outsel_vidpad, GST_PAD_PROBE_TYPE_BUFFER, gst_wrapper_camera_bin_src_vidsrc_probe, gst_object_ref (self), gst_object_unref); gst_ghost_pad_set_target (GST_GHOST_PAD (self->imgsrc), self->outsel_imgpad); gst_ghost_pad_set_target (GST_GHOST_PAD (self->vidsrc), self->outsel_vidpad); if (bcamsrc->mode == MODE_IMAGE) { g_object_set (self->output_selector, "active-pad", self->outsel_imgpad, NULL); } else { g_object_set (self->output_selector, "active-pad", self->outsel_vidpad, NULL); } gst_pad_set_active (self->vfsrc, TRUE); gst_pad_set_active (self->imgsrc, TRUE); /* XXX ??? */ gst_pad_set_active (self->vidsrc, TRUE); /* XXX ??? */ } /* Do this even if pipeline is constructed */ if (self->video_filter) { /* check if we need to replace the current one */ if (self->video_filter != self->app_vid_filter) { gst_bin_remove (cbin, self->video_filter); gst_object_unref (self->video_filter); self->video_filter = NULL; filter_csp = gst_bin_get_by_name (cbin, "filter-videoconvert"); gst_bin_remove (cbin, filter_csp); gst_object_unref (filter_csp); filter_csp = NULL; } } if (!self->video_filter) { if (self->app_vid_filter) { self->video_filter = gst_object_ref (self->app_vid_filter); filter_csp = gst_element_factory_make ("videoconvert", "filter-videoconvert"); gst_bin_add_many (cbin, self->video_filter, filter_csp, NULL); src_csp = gst_bin_get_by_name (cbin, "src-videoconvert"); capsfilter = gst_bin_get_by_name (cbin, "src-capsfilter"); if (gst_pad_is_linked (gst_element_get_static_pad (src_csp, "src"))) gst_element_unlink (src_csp, capsfilter); if (!gst_element_link_many (src_csp, self->video_filter, filter_csp, capsfilter, NULL)) goto done; } } ret = TRUE; self->elements_created = TRUE; done: return ret; }
/** * gst_wrapper_camera_bin_src_vidsrc_probe: * * Buffer probe called before sending each buffer to image queue. */ static GstPadProbeReturn gst_wrapper_camera_bin_src_vidsrc_probe (GstPad * pad, GstPadProbeInfo * info, gpointer data) { GstWrapperCameraBinSrc *self = GST_WRAPPER_CAMERA_BIN_SRC (data); GstBaseCameraSrc *camerasrc = GST_BASE_CAMERA_SRC_CAST (self); GstPadProbeReturn ret = GST_PAD_PROBE_DROP; GstBuffer *buffer = GST_BUFFER (info->data); GST_LOG_OBJECT (self, "Video probe, mode %d, capture status %d", camerasrc->mode, self->video_rec_status); /* TODO do we want to lock for every buffer? */ /* * Note that we can use gst_pad_push_event here because we are a buffer * probe. */ /* TODO shouldn't access this directly */ g_mutex_lock (&camerasrc->capturing_mutex); if (self->video_rec_status == GST_VIDEO_RECORDING_STATUS_DONE) { /* NOP */ } else if (self->video_rec_status == GST_VIDEO_RECORDING_STATUS_STARTING) { GstClockTime ts; GstSegment segment; GstCaps *caps; GstSample *sample; GST_DEBUG_OBJECT (self, "Starting video recording"); self->video_rec_status = GST_VIDEO_RECORDING_STATUS_RUNNING; ts = GST_BUFFER_TIMESTAMP (buffer); if (!GST_CLOCK_TIME_IS_VALID (ts)) ts = 0; gst_segment_init (&segment, GST_FORMAT_TIME); segment.start = ts; gst_pad_push_event (self->vidsrc, gst_event_new_segment (&segment)); /* post preview */ GST_DEBUG_OBJECT (self, "Posting preview for video"); caps = gst_pad_get_current_caps (pad); sample = gst_sample_new (buffer, caps, NULL, NULL); gst_base_camera_src_post_preview (camerasrc, sample); gst_caps_unref (caps); gst_sample_unref (sample); ret = GST_PAD_PROBE_OK; } else if (self->video_rec_status == GST_VIDEO_RECORDING_STATUS_FINISHING) { GstPad *peer; /* send eos */ GST_DEBUG_OBJECT (self, "Finishing video recording, pushing eos"); peer = gst_pad_get_peer (self->vidsrc); if (peer) { /* send to the peer as we don't want our pads with eos flag */ gst_pad_send_event (peer, gst_event_new_eos ()); gst_object_unref (peer); } else { GST_WARNING_OBJECT (camerasrc, "No peer pad for vidsrc"); } self->video_rec_status = GST_VIDEO_RECORDING_STATUS_DONE; gst_base_camera_src_finish_capture (camerasrc); } else { ret = GST_PAD_PROBE_OK; } g_mutex_unlock (&camerasrc->capturing_mutex); return ret; }
/** * gst_wrapper_camera_bin_src_construct_pipeline: * @bcamsrc: camerasrc object * * This function creates and links the elements of the camerasrc bin * videosrc ! cspconv ! capsfilter ! crop ! scale ! capsfilter ! tee name=t ! * t. ! ... (viewfinder pad) * t. ! output-selector name=outsel * outsel. ! (image pad) * outsel. ! (video pad) * * Returns: TRUE, if elements were successfully created, FALSE otherwise */ static gboolean gst_wrapper_camera_bin_src_construct_pipeline (GstBaseCameraSrc * bcamsrc) { GstWrapperCameraBinSrc *self = GST_WRAPPER_CAMERA_BIN_SRC (bcamsrc); GstBin *cbin = GST_BIN (bcamsrc); GstElement *tee; gboolean ret = FALSE; GstElement *videoscale; GstPad *vf_pad; GstPad *tee_capture_pad; if (self->elements_created) return TRUE; GST_DEBUG_OBJECT (self, "constructing pipeline"); /* Add application set or default video src element */ if (!(self->src_vid_src = gst_camerabin_setup_default_element (cbin, self->app_vid_src, "autovideosrc", DEFAULT_VIDEOSRC))) { self->src_vid_src = NULL; goto done; } else { if (!gst_camerabin_add_element (cbin, self->src_vid_src)) { goto done; } } /* we lost the reference */ self->app_vid_src = NULL; /* add a buffer probe to the src elemento to drop EOS from READY->NULL */ { GstPad *pad; pad = gst_element_get_static_pad (self->src_vid_src, "src"); self->src_event_probe_id = gst_pad_add_event_probe (pad, (GCallback) src_event_probe, self); gst_object_unref (pad); } if (!gst_camerabin_create_and_add_element (cbin, "ffmpegcolorspace")) goto done; if (!(self->src_filter = gst_camerabin_create_and_add_element (cbin, "capsfilter"))) goto done; if (!(self->src_zoom_crop = gst_camerabin_create_and_add_element (cbin, "videocrop"))) goto done; if (!(self->src_zoom_scale = gst_camerabin_create_and_add_element (cbin, "videoscale"))) goto done; if (!(self->src_zoom_filter = gst_camerabin_create_and_add_element (cbin, "capsfilter"))) goto done; if (!(tee = gst_camerabin_create_and_add_element (cbin, "tee"))) goto done; /* viewfinder pad */ vf_pad = gst_element_get_request_pad (tee, "src%d"); g_object_set (tee, "alloc-pad", vf_pad, NULL); gst_object_unref (vf_pad); /* the viewfinder should always work, so we add some converters to it */ if (!gst_camerabin_create_and_add_element (cbin, "ffmpegcolorspace")) goto done; if (!(videoscale = gst_camerabin_create_and_add_element (cbin, "videoscale"))) goto done; /* image/video pad from tee */ tee_capture_pad = gst_element_get_request_pad (tee, "src%d"); self->output_selector = gst_element_factory_make ("output-selector", "outsel"); gst_bin_add (GST_BIN (self), self->output_selector); { GstPad *pad = gst_element_get_static_pad (self->output_selector, "sink"); /* check return TODO */ gst_pad_link (tee_capture_pad, pad); gst_object_unref (pad); } gst_object_unref (tee_capture_pad); /* Create the 2 output pads for video and image */ self->outsel_vidpad = gst_element_get_request_pad (self->output_selector, "src%d"); self->outsel_imgpad = gst_element_get_request_pad (self->output_selector, "src%d"); g_assert (self->outsel_vidpad != NULL); g_assert (self->outsel_imgpad != NULL); gst_pad_add_buffer_probe (self->outsel_imgpad, G_CALLBACK (gst_wrapper_camera_bin_src_imgsrc_probe), self); gst_pad_add_buffer_probe (self->outsel_vidpad, G_CALLBACK (gst_wrapper_camera_bin_src_vidsrc_probe), self); gst_ghost_pad_set_target (GST_GHOST_PAD (self->imgsrc), self->outsel_imgpad); gst_ghost_pad_set_target (GST_GHOST_PAD (self->vidsrc), self->outsel_vidpad); if (bcamsrc->mode == MODE_IMAGE) { g_object_set (self->output_selector, "active-pad", self->outsel_imgpad, NULL); } else { g_object_set (self->output_selector, "active-pad", self->outsel_vidpad, NULL); } /* hook-up the vf ghostpad */ vf_pad = gst_element_get_static_pad (videoscale, "src"); gst_ghost_pad_set_target (GST_GHOST_PAD (self->vfsrc), vf_pad); gst_object_unref (vf_pad); gst_pad_set_active (self->vfsrc, TRUE); gst_pad_set_active (self->imgsrc, TRUE); /* XXX ??? */ gst_pad_set_active (self->vidsrc, TRUE); /* XXX ??? */ ret = TRUE; self->elements_created = TRUE; done: return ret; }
static GstCaps * gst_wrapper_camera_bin_src_get_allowed_input_caps (GstBaseCameraSrc * bcamsrc) { GstWrapperCameraBinSrc *self = GST_WRAPPER_CAMERA_BIN_SRC (bcamsrc); GstCaps *caps = NULL; GstPad *pad = NULL, *peer_pad = NULL; GstState state; GstElement *videosrc; videosrc = self->src_vid_src ? self->src_vid_src : self->app_vid_src; if (!videosrc) { GST_WARNING_OBJECT (self, "no videosrc, can't get allowed caps"); goto failed; } if (self->allowed_caps) { GST_DEBUG_OBJECT (self, "returning cached caps"); goto done; } pad = gst_element_get_static_pad (videosrc, "src"); if (!pad) { GST_WARNING_OBJECT (self, "no srcpad in videosrc"); goto failed; } state = GST_STATE (videosrc); /* Make this function work also in NULL state */ if (state == GST_STATE_NULL) { GST_DEBUG_OBJECT (self, "setting videosrc to ready temporarily"); peer_pad = gst_pad_get_peer (pad); if (peer_pad) { gst_pad_unlink (pad, peer_pad); } /* Set videosrc to READY to open video device */ gst_element_set_locked_state (videosrc, TRUE); gst_element_set_state (videosrc, GST_STATE_READY); } self->allowed_caps = gst_pad_get_caps (pad); /* Restore state and re-link if necessary */ if (state == GST_STATE_NULL) { GST_DEBUG_OBJECT (self, "restoring videosrc state %d", state); /* Reset videosrc to NULL state, some drivers seem to need this */ gst_element_set_state (videosrc, GST_STATE_NULL); if (peer_pad) { gst_pad_link (pad, peer_pad); gst_object_unref (peer_pad); } gst_element_set_locked_state (videosrc, FALSE); } gst_object_unref (pad); done: if (self->allowed_caps) { caps = gst_caps_copy (self->allowed_caps); } GST_DEBUG_OBJECT (self, "allowed caps:%" GST_PTR_FORMAT, caps); failed: return caps; }