mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-11-27 12:11:13 +00:00
camerabin: implement video preview creation
Completes preview creation mechanism to be able to provide a preview image for video clips too.
This commit is contained in:
parent
52e314ef81
commit
fe70981023
4 changed files with 126 additions and 63 deletions
|
@ -59,24 +59,20 @@ create_element (const gchar * factory_name, const gchar * elem_name,
|
|||
|
||||
/**
|
||||
* gst_camerabin_preview_create_pipeline:
|
||||
* @camera: camerabin object
|
||||
* @caps: pointer to the caps used in pipeline
|
||||
*
|
||||
* Create a preview converter pipeline.
|
||||
* Create a preview converter pipeline that outputs the format defined in
|
||||
* @caps parameter.
|
||||
*
|
||||
* Returns: TRUE if pipeline was constructed, otherwise FALSE.
|
||||
* Returns: New pipeline, or NULL if error occured.
|
||||
*/
|
||||
gboolean
|
||||
gst_camerabin_preview_create_pipeline (GstCameraBin * camera)
|
||||
GstElement *
|
||||
gst_camerabin_preview_create_pipeline (GstCameraBin * camera, GstCaps * caps)
|
||||
{
|
||||
GstElement *src, *csp, *filter, *vscale, *sink;
|
||||
GstElement *pipe, *src, *csp, *filter, *vscale, *sink;
|
||||
GError *error = NULL;
|
||||
|
||||
if (!camera->preview_caps) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* Destroy old pipeline, if any */
|
||||
gst_camerabin_preview_destroy_pipeline (camera);
|
||||
g_return_val_if_fail (caps != NULL, FALSE);
|
||||
|
||||
GST_DEBUG ("creating elements");
|
||||
|
||||
|
@ -87,17 +83,19 @@ gst_camerabin_preview_create_pipeline (GstCameraBin * camera)
|
|||
!create_element ("fakesink", "prev_sink", &sink, &error))
|
||||
goto no_elements;
|
||||
|
||||
camera->preview_pipeline = gst_pipeline_new ("preview-pipeline");
|
||||
if (camera->preview_pipeline == NULL)
|
||||
/* We have multiple pipelines created by using this function, so we can't
|
||||
* give a name to them. Another way would to ensure the uniqueness of the
|
||||
* name here*/
|
||||
pipe = gst_pipeline_new (NULL);
|
||||
if (pipe == NULL)
|
||||
goto no_pipeline;
|
||||
|
||||
GST_DEBUG ("adding elements");
|
||||
gst_bin_add_many (GST_BIN (camera->preview_pipeline),
|
||||
src, csp, filter, vscale, sink, NULL);
|
||||
gst_bin_add_many (GST_BIN (pipe), src, csp, filter, vscale, sink, NULL);
|
||||
|
||||
GST_DEBUG ("preview format is: %" GST_PTR_FORMAT, camera->preview_caps);
|
||||
GST_DEBUG ("preview format is: %" GST_PTR_FORMAT, caps);
|
||||
|
||||
g_object_set (filter, "caps", camera->preview_caps, NULL);
|
||||
g_object_set (filter, "caps", caps, NULL);
|
||||
g_object_set (sink, "preroll-queue-len", 1, "signal-handoffs", TRUE, NULL);
|
||||
g_object_set (vscale, "method", 0, NULL);
|
||||
|
||||
|
@ -118,20 +116,20 @@ gst_camerabin_preview_create_pipeline (GstCameraBin * camera)
|
|||
if (!gst_element_link_pads (filter, "src", sink, "sink"))
|
||||
return FALSE;
|
||||
|
||||
return TRUE;
|
||||
return pipe;
|
||||
|
||||
/* ERRORS */
|
||||
no_elements:
|
||||
{
|
||||
g_warning ("Could not make preview pipeline: %s", error->message);
|
||||
g_error_free (error);
|
||||
return FALSE;
|
||||
return NULL;
|
||||
}
|
||||
no_pipeline:
|
||||
{
|
||||
g_warning ("Could not make preview pipeline: %s",
|
||||
"no pipeline (unknown error)");
|
||||
return FALSE;
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -139,23 +137,25 @@ no_pipeline:
|
|||
/**
|
||||
* gst_camerabin_preview_destroy_pipeline:
|
||||
* @camera: camerabin object
|
||||
* @pipeline: the pipeline to be destroyed
|
||||
*
|
||||
* Destroy preview converter pipeline.
|
||||
*/
|
||||
void
|
||||
gst_camerabin_preview_destroy_pipeline (GstCameraBin * camera)
|
||||
gst_camerabin_preview_destroy_pipeline (GstCameraBin * camera,
|
||||
GstElement * pipeline)
|
||||
{
|
||||
if (camera->preview_pipeline) {
|
||||
gst_element_set_state (camera->preview_pipeline, GST_STATE_NULL);
|
||||
gst_object_unref (camera->preview_pipeline);
|
||||
camera->preview_pipeline = NULL;
|
||||
}
|
||||
g_return_if_fail (pipeline != NULL);
|
||||
|
||||
gst_element_set_state (pipeline, GST_STATE_NULL);
|
||||
gst_object_unref (pipeline);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* gst_camerabin_preview_convert:
|
||||
* @camera: camerabin object
|
||||
* @pipeline: preview pipeline to use
|
||||
* @buf: #GstBuffer that contains the frame to be converted
|
||||
*
|
||||
* Create a preview image of the given frame.
|
||||
|
@ -163,7 +163,8 @@ gst_camerabin_preview_destroy_pipeline (GstCameraBin * camera)
|
|||
* Returns: converted preview image, or NULL if operation failed.
|
||||
*/
|
||||
GstBuffer *
|
||||
gst_camerabin_preview_convert (GstCameraBin * camera, GstBuffer * buf)
|
||||
gst_camerabin_preview_convert (GstCameraBin * camera,
|
||||
GstElement * pipeline, GstBuffer * buf)
|
||||
{
|
||||
GstMessage *msg;
|
||||
GstBuffer *result = NULL;
|
||||
|
@ -175,13 +176,13 @@ gst_camerabin_preview_convert (GstCameraBin * camera, GstBuffer * buf)
|
|||
|
||||
g_return_val_if_fail (GST_BUFFER_CAPS (buf) != NULL, NULL);
|
||||
|
||||
if (camera->preview_pipeline == NULL) {
|
||||
if (pipeline == NULL) {
|
||||
GST_WARNING ("pipeline is NULL");
|
||||
goto no_pipeline;
|
||||
}
|
||||
|
||||
src = gst_bin_get_by_name (GST_BIN (camera->preview_pipeline), "prev_src");
|
||||
sink = gst_bin_get_by_name (GST_BIN (camera->preview_pipeline), "prev_sink");
|
||||
src = gst_bin_get_by_name (GST_BIN (pipeline), "prev_src");
|
||||
sink = gst_bin_get_by_name (GST_BIN (pipeline), "prev_sink");
|
||||
|
||||
if (!src || !sink) {
|
||||
GST_WARNING ("pipeline doesn't have src / sink elements");
|
||||
|
@ -199,12 +200,12 @@ gst_camerabin_preview_convert (GstCameraBin * camera, GstBuffer * buf)
|
|||
|
||||
GST_DEBUG ("running conversion pipeline, source is: %" GST_PTR_FORMAT,
|
||||
GST_BUFFER_CAPS (buf));
|
||||
gst_element_set_state (camera->preview_pipeline, GST_STATE_PLAYING);
|
||||
gst_element_set_state (pipeline, GST_STATE_PLAYING);
|
||||
|
||||
g_signal_emit_by_name (src, "push-buffer", buf, &fret);
|
||||
|
||||
/* TODO: do we need to use a bus poll, can we just register a callback to the bus? */
|
||||
bus = gst_element_get_bus (camera->preview_pipeline);
|
||||
bus = gst_element_get_bus (pipeline);
|
||||
msg =
|
||||
gst_bus_poll (bus, GST_MESSAGE_ERROR | GST_MESSAGE_EOS, 25 * GST_SECOND);
|
||||
|
||||
|
@ -245,7 +246,7 @@ gst_camerabin_preview_convert (GstCameraBin * camera, GstBuffer * buf)
|
|||
|
||||
g_signal_handlers_disconnect_by_func (sink, G_CALLBACK (save_result),
|
||||
&result);
|
||||
gst_element_set_state (camera->preview_pipeline, GST_STATE_READY);
|
||||
gst_element_set_state (pipeline, GST_STATE_READY);
|
||||
|
||||
GST_BUFFER_FLAGS (buf) = bflags;
|
||||
|
||||
|
|
|
@ -26,12 +26,14 @@
|
|||
#include "gstcamerabin.h"
|
||||
|
||||
G_BEGIN_DECLS
|
||||
gboolean gst_camerabin_preview_create_pipeline (GstCameraBin * camera);
|
||||
GstElement * gst_camerabin_preview_create_pipeline (GstCameraBin * camera,
|
||||
GstCaps * caps);
|
||||
|
||||
void gst_camerabin_preview_destroy_pipeline (GstCameraBin * camera);
|
||||
void gst_camerabin_preview_destroy_pipeline (GstCameraBin * camera,
|
||||
GstElement * pipeline);
|
||||
|
||||
GstBuffer *gst_camerabin_preview_convert (GstCameraBin * camera,
|
||||
GstBuffer * buf);
|
||||
GstElement * pipeline, GstBuffer * buf);
|
||||
|
||||
G_END_DECLS
|
||||
#endif /* __CAMERABINPREVIEW_H__ */
|
||||
|
|
|
@ -91,6 +91,22 @@
|
|||
* </para>
|
||||
* </refsect2>
|
||||
* <refsect2>
|
||||
* <title>Video and image previews</title>
|
||||
* <para>
|
||||
* GstCameraBin contains "preview-caps" property, which is used to determine
|
||||
* whether the application wants a preview image of the captured picture or
|
||||
* video. When set, a GstMessage named "preview-image" will be sent. This
|
||||
* message will contain a GstBuffer holding the preview image, converted
|
||||
* to a format defined by those preview caps. The ownership of the preview
|
||||
* image is kept in GstCameraBin, so application should ref the preview buffer
|
||||
* object if it needs to use it elsewhere than in message handler.
|
||||
*
|
||||
* Defining preview caps is done by selecting the capturing mode first and
|
||||
* then setting the property. Camerabin remembers caps separately for both
|
||||
* modes, so it is not necessary to set the caps again after changing the mode.
|
||||
* </para>
|
||||
* </refsect2>
|
||||
* <refsect2>
|
||||
* <note>
|
||||
* <para>
|
||||
* Since the muxers tested so far have problems with discontinous buffers, QoS
|
||||
|
@ -876,18 +892,12 @@ camerabin_dispose_elements (GstCameraBin * camera)
|
|||
}
|
||||
|
||||
/* Free caps */
|
||||
if (camera->image_capture_caps) {
|
||||
gst_caps_replace (&camera->image_capture_caps, NULL);
|
||||
}
|
||||
if (camera->view_finder_caps) {
|
||||
gst_caps_replace (&camera->view_finder_caps, NULL);
|
||||
}
|
||||
if (camera->allowed_caps) {
|
||||
gst_caps_replace (&camera->allowed_caps, NULL);
|
||||
}
|
||||
if (camera->preview_caps) {
|
||||
gst_caps_replace (&camera->preview_caps, NULL);
|
||||
}
|
||||
gst_caps_replace (&camera->image_capture_caps, NULL);
|
||||
gst_caps_replace (&camera->view_finder_caps, NULL);
|
||||
gst_caps_replace (&camera->allowed_caps, NULL);
|
||||
gst_caps_replace (&camera->preview_caps, NULL);
|
||||
gst_caps_replace (&camera->video_preview_caps, NULL);
|
||||
gst_buffer_replace (&camera->video_preview_buffer, NULL);
|
||||
|
||||
if (camera->event_tags) {
|
||||
gst_tag_list_free (camera->event_tags);
|
||||
|
@ -1678,6 +1688,7 @@ image_pad_blocked (GstPad * pad, gboolean blocked, gpointer user_data)
|
|||
static gboolean
|
||||
gst_camerabin_send_preview (GstCameraBin * camera, GstBuffer * buffer)
|
||||
{
|
||||
GstElement *pipeline;
|
||||
GstBuffer *prev = NULL;
|
||||
GstStructure *s;
|
||||
GstMessage *msg;
|
||||
|
@ -1685,7 +1696,9 @@ gst_camerabin_send_preview (GstCameraBin * camera, GstBuffer * buffer)
|
|||
|
||||
GST_DEBUG_OBJECT (camera, "creating preview");
|
||||
|
||||
prev = gst_camerabin_preview_convert (camera, buffer);
|
||||
pipeline = (camera->mode == MODE_IMAGE) ?
|
||||
camera->preview_pipeline : camera->video_preview_pipeline;
|
||||
prev = gst_camerabin_preview_convert (camera, pipeline, buffer);
|
||||
|
||||
GST_DEBUG_OBJECT (camera, "preview created: %p", prev);
|
||||
|
||||
|
@ -1789,6 +1802,13 @@ gst_camerabin_have_vid_buffer (GstPad * pad, GstBuffer * buffer,
|
|||
gboolean ret = TRUE;
|
||||
GST_LOG ("got video buffer %p with size %d",
|
||||
buffer, GST_BUFFER_SIZE (buffer));
|
||||
|
||||
if (camera->video_preview_caps &&
|
||||
!camera->video_preview_buffer && !camera->stop_requested) {
|
||||
GST_DEBUG ("storing video preview %p", buffer);
|
||||
camera->video_preview_buffer = gst_buffer_copy (buffer);
|
||||
}
|
||||
|
||||
if (G_UNLIKELY (camera->stop_requested)) {
|
||||
gst_camerabin_send_video_eos (camera);
|
||||
ret = FALSE; /* Drop buffer */
|
||||
|
@ -1972,6 +1992,12 @@ gst_camerabin_do_stop (GstCameraBin * camera)
|
|||
GST_DEBUG_OBJECT (camera, "mark stop");
|
||||
camera->stop_requested = TRUE;
|
||||
|
||||
if (camera->video_preview_caps && camera->video_preview_buffer) {
|
||||
gst_camerabin_send_preview (camera, camera->video_preview_buffer);
|
||||
gst_buffer_unref (camera->video_preview_buffer);
|
||||
camera->video_preview_buffer = NULL;
|
||||
}
|
||||
|
||||
/* Take special care when stopping paused video capture */
|
||||
if ((camera->active_bin == camera->vidbin) && camera->paused) {
|
||||
/* Send eos event to video bin before setting it to playing */
|
||||
|
@ -2709,6 +2735,8 @@ gst_camerabin_init (GstCameraBin * camera, GstCameraBinClass * gclass)
|
|||
camera->pad_src_vid = NULL;
|
||||
camera->pad_view_vid = NULL;
|
||||
|
||||
camera->video_preview_buffer = NULL;
|
||||
|
||||
/* source elements */
|
||||
camera->src_vid_src = NULL;
|
||||
camera->src_filter = NULL;
|
||||
|
@ -2753,7 +2781,15 @@ gst_camerabin_dispose (GObject * object)
|
|||
gst_element_set_state (camera->vidbin, GST_STATE_NULL);
|
||||
gst_object_unref (camera->vidbin);
|
||||
|
||||
gst_camerabin_preview_destroy_pipeline (camera);
|
||||
if (camera->preview_pipeline) {
|
||||
gst_camerabin_preview_destroy_pipeline (camera, camera->preview_pipeline);
|
||||
camera->preview_pipeline = NULL;
|
||||
}
|
||||
if (camera->video_preview_pipeline) {
|
||||
gst_camerabin_preview_destroy_pipeline (camera,
|
||||
camera->video_preview_pipeline);
|
||||
camera->video_preview_pipeline = NULL;
|
||||
}
|
||||
|
||||
camerabin_destroy_elements (camera);
|
||||
camerabin_dispose_elements (camera);
|
||||
|
@ -2878,24 +2914,39 @@ gst_camerabin_set_property (GObject * object, guint prop_id,
|
|||
}
|
||||
break;
|
||||
case ARG_PREVIEW_CAPS:
|
||||
/* Currently camerabin only provides preview for images, so we don't
|
||||
* even handle video mode */
|
||||
{
|
||||
GstElement **prev_pipe = NULL;
|
||||
GstCaps **prev_caps = NULL;
|
||||
GstCaps *new_caps = NULL;
|
||||
|
||||
if (camera->mode == MODE_IMAGE) {
|
||||
GstCaps *new_caps = NULL;
|
||||
prev_pipe = &camera->preview_pipeline;
|
||||
prev_caps = &camera->preview_caps;
|
||||
} else if (camera->mode == MODE_VIDEO) {
|
||||
prev_pipe = &camera->video_preview_pipeline;
|
||||
prev_caps = &camera->video_preview_caps;
|
||||
}
|
||||
|
||||
new_caps = (GstCaps *) gst_value_get_caps (value);
|
||||
new_caps = (GstCaps *) gst_value_get_caps (value);
|
||||
|
||||
if (prev_caps && !gst_caps_is_equal (*prev_caps, new_caps)) {
|
||||
GST_DEBUG_OBJECT (camera,
|
||||
"setting preview caps: %" GST_PTR_FORMAT " -> %" GST_PTR_FORMAT,
|
||||
camera->preview_caps, new_caps);
|
||||
"setting preview caps: %" GST_PTR_FORMAT, new_caps);
|
||||
if (*prev_pipe) {
|
||||
gst_camerabin_preview_destroy_pipeline (camera, *prev_pipe);
|
||||
*prev_pipe = NULL;
|
||||
}
|
||||
GST_OBJECT_LOCK (camera);
|
||||
gst_caps_replace (prev_caps, new_caps);
|
||||
GST_OBJECT_UNLOCK (camera);
|
||||
|
||||
if (!gst_caps_is_equal (camera->preview_caps, new_caps)) {
|
||||
GST_OBJECT_LOCK (camera);
|
||||
gst_caps_replace (&camera->preview_caps, new_caps);
|
||||
GST_OBJECT_UNLOCK (camera);
|
||||
gst_camerabin_preview_create_pipeline (camera);
|
||||
if (new_caps && !gst_caps_is_any (new_caps) &&
|
||||
!gst_caps_is_empty (new_caps)) {
|
||||
*prev_pipe = gst_camerabin_preview_create_pipeline (camera, new_caps);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||
break;
|
||||
|
@ -2968,7 +3019,10 @@ gst_camerabin_get_property (GObject * object, guint prop_id,
|
|||
gst_value_set_caps (value, camera->view_finder_caps);
|
||||
break;
|
||||
case ARG_PREVIEW_CAPS:
|
||||
gst_value_set_caps (value, camera->preview_caps);
|
||||
if (camera->mode == MODE_IMAGE)
|
||||
gst_value_set_caps (value, camera->preview_caps);
|
||||
else if (camera->mode == MODE_VIDEO)
|
||||
gst_value_set_caps (value, camera->video_preview_caps);
|
||||
break;
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||
|
|
|
@ -87,6 +87,9 @@ struct _GstCameraBin
|
|||
/* Caps used to create preview image */
|
||||
GstCaps *preview_caps;
|
||||
|
||||
/* Caps used to create video preview image */
|
||||
GstCaps *video_preview_caps;
|
||||
|
||||
/* The digital zoom (from 100% to 1000%) */
|
||||
gint zoom;
|
||||
|
||||
|
@ -110,6 +113,9 @@ struct _GstCameraBin
|
|||
GstElement *vidbin; /* bin that holds video capturing elements */
|
||||
GstElement *active_bin; /* image or video bin that is currently in use */
|
||||
GstElement *preview_pipeline; /* pipeline for creating preview images */
|
||||
GstElement *video_preview_pipeline; /* pipeline for creating video preview image */
|
||||
|
||||
GstBuffer *video_preview_buffer; /* buffer for storing video preview */
|
||||
|
||||
/* source elements */
|
||||
GstElement *src_vid_src;
|
||||
|
|
Loading…
Reference in a new issue