camerabin: refactor cropping and adapting captured images
This commit is contained in:
parent
341d91520a
commit
190d54b096
@ -296,6 +296,8 @@ static void
|
|||||||
gst_camerabin_update_aspect_filter (GstCameraBin * camera, GstCaps * new_caps);
|
gst_camerabin_update_aspect_filter (GstCameraBin * camera, GstCaps * new_caps);
|
||||||
|
|
||||||
static void gst_camerabin_finish_image_capture (GstCameraBin * camera);
|
static void gst_camerabin_finish_image_capture (GstCameraBin * camera);
|
||||||
|
static void gst_camerabin_adapt_image_capture (GstCameraBin * camera,
|
||||||
|
GstCaps * new_caps);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* GObject callback functions declaration
|
* GObject callback functions declaration
|
||||||
@ -1028,10 +1030,13 @@ gst_camerabin_set_videosrc_zoom (GstCameraBin * camera, gint zoom)
|
|||||||
static gboolean
|
static gboolean
|
||||||
gst_camerabin_set_element_zoom (GstCameraBin * camera, gint zoom)
|
gst_camerabin_set_element_zoom (GstCameraBin * camera, gint zoom)
|
||||||
{
|
{
|
||||||
gint w2_crop = 0;
|
gint w2_crop = 0, h2_crop = 0;
|
||||||
gint h2_crop = 0;
|
|
||||||
GstPad *pad_zoom_sink = NULL;
|
GstPad *pad_zoom_sink = NULL;
|
||||||
gboolean ret = FALSE;
|
gboolean ret = FALSE;
|
||||||
|
gint left = camera->base_crop_left;
|
||||||
|
gint right = camera->base_crop_right;
|
||||||
|
gint top = camera->base_crop_top;
|
||||||
|
gint bottom = camera->base_crop_bottom;
|
||||||
|
|
||||||
if (camera->src_zoom_crop) {
|
if (camera->src_zoom_crop) {
|
||||||
/* Update capsfilters to apply the zoom */
|
/* Update capsfilters to apply the zoom */
|
||||||
@ -1042,20 +1047,25 @@ gst_camerabin_set_element_zoom (GstCameraBin * camera, gint zoom)
|
|||||||
w2_crop = (camera->width - (camera->width * ZOOM_1X / zoom)) / 2;
|
w2_crop = (camera->width - (camera->width * ZOOM_1X / zoom)) / 2;
|
||||||
h2_crop = (camera->height - (camera->height * ZOOM_1X / zoom)) / 2;
|
h2_crop = (camera->height - (camera->height * ZOOM_1X / zoom)) / 2;
|
||||||
|
|
||||||
|
left += w2_crop;
|
||||||
|
right += w2_crop;
|
||||||
|
top += h2_crop;
|
||||||
|
bottom += h2_crop;
|
||||||
|
|
||||||
/* force number of pixels cropped from left to be even, to avoid slow code
|
/* force number of pixels cropped from left to be even, to avoid slow code
|
||||||
* path on videoscale */
|
* path on videoscale */
|
||||||
w2_crop &= 0xFFFE;
|
left &= 0xFFFE;
|
||||||
}
|
}
|
||||||
|
|
||||||
pad_zoom_sink = gst_element_get_static_pad (camera->src_zoom_crop, "sink");
|
pad_zoom_sink = gst_element_get_static_pad (camera->src_zoom_crop, "sink");
|
||||||
|
|
||||||
GST_INFO_OBJECT (camera,
|
GST_INFO_OBJECT (camera,
|
||||||
"sw cropping: left:%d, right:%d, top:%d, bottom:%d", w2_crop, w2_crop,
|
"sw cropping: left:%d, right:%d, top:%d, bottom:%d", left, right, top,
|
||||||
h2_crop, h2_crop);
|
bottom);
|
||||||
|
|
||||||
GST_PAD_STREAM_LOCK (pad_zoom_sink);
|
GST_PAD_STREAM_LOCK (pad_zoom_sink);
|
||||||
g_object_set (camera->src_zoom_crop, "left", w2_crop, "right", w2_crop,
|
g_object_set (camera->src_zoom_crop, "left", left, "right", right, "top",
|
||||||
"top", h2_crop, "bottom", h2_crop, NULL);
|
top, "bottom", bottom, NULL);
|
||||||
GST_PAD_STREAM_UNLOCK (pad_zoom_sink);
|
GST_PAD_STREAM_UNLOCK (pad_zoom_sink);
|
||||||
gst_object_unref (pad_zoom_sink);
|
gst_object_unref (pad_zoom_sink);
|
||||||
ret = TRUE;
|
ret = TRUE;
|
||||||
@ -1277,7 +1287,6 @@ gst_camerabin_get_internal_tags (GstCameraBin * camera)
|
|||||||
GstColorBalanceChannel *channel;
|
GstColorBalanceChannel *channel;
|
||||||
gint min_value, max_value, mid_value, cur_value;
|
gint min_value, max_value, mid_value, cur_value;
|
||||||
|
|
||||||
|
|
||||||
if (camera->active_bin == camera->vidbin) {
|
if (camera->active_bin == camera->vidbin) {
|
||||||
/* FIXME: check if internal video tag setting is needed */
|
/* FIXME: check if internal video tag setting is needed */
|
||||||
goto done;
|
goto done;
|
||||||
@ -1415,76 +1424,6 @@ gst_camerabin_set_capsfilter_caps (GstCameraBin * camera, GstCaps * new_caps)
|
|||||||
GST_INFO_OBJECT (camera, "udpated");
|
GST_INFO_OBJECT (camera, "udpated");
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* gst_camerabin_adapt_video_resolution:
|
|
||||||
* @camera: camerabin object
|
|
||||||
* @caps: caps describing the next incoming buffer format
|
|
||||||
*
|
|
||||||
* This function adjusts capsfilter and crop elements in order to modify
|
|
||||||
* the incoming buffer to the resolution that application requested.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
static void
|
|
||||||
gst_camerabin_adapt_video_resolution (GstCameraBin * camera, GstCaps * caps)
|
|
||||||
{
|
|
||||||
GstStructure *st;
|
|
||||||
gint width = 0, height = 0;
|
|
||||||
GstCaps *filter_caps = NULL;
|
|
||||||
gint top, bottom, left, right, crop;
|
|
||||||
gdouble ratio_w, ratio_h;
|
|
||||||
|
|
||||||
g_return_if_fail (camera->width != 0 && camera->height != 0);
|
|
||||||
|
|
||||||
/* Get width and height from caps */
|
|
||||||
st = gst_caps_get_structure (caps, 0);
|
|
||||||
gst_structure_get_int (st, "width", &width);
|
|
||||||
gst_structure_get_int (st, "height", &height);
|
|
||||||
|
|
||||||
if (width == camera->width && height == camera->height) {
|
|
||||||
GST_DEBUG_OBJECT (camera, "no adaptation with resolution needed");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
GST_DEBUG_OBJECT (camera,
|
|
||||||
"changing %dx%d -> %dx%d filter to %" GST_PTR_FORMAT,
|
|
||||||
camera->width, camera->height, width, height, camera->src_filter);
|
|
||||||
|
|
||||||
/* Apply the width and height to filter caps */
|
|
||||||
g_object_get (G_OBJECT (camera->src_filter), "caps", &filter_caps, NULL);
|
|
||||||
filter_caps = gst_caps_make_writable (filter_caps);
|
|
||||||
gst_caps_set_simple (filter_caps, "width", G_TYPE_INT, width,
|
|
||||||
"height", G_TYPE_INT, height, NULL);
|
|
||||||
g_object_set (G_OBJECT (camera->src_filter), "caps", filter_caps, NULL);
|
|
||||||
gst_caps_unref (filter_caps);
|
|
||||||
|
|
||||||
/* Crop if requested aspect ratio differs from incoming frame aspect ratio */
|
|
||||||
|
|
||||||
/* Don't override original crop values in case we have zoom applied */
|
|
||||||
if (camera->src_zoom_crop) {
|
|
||||||
g_object_get (G_OBJECT (camera->src_zoom_crop), "top", &top, "bottom",
|
|
||||||
&bottom, "left", &left, "right", &right, NULL);
|
|
||||||
|
|
||||||
ratio_w = (gdouble) width / camera->width;
|
|
||||||
ratio_h = (gdouble) height / camera->height;
|
|
||||||
|
|
||||||
if (ratio_w < ratio_h) {
|
|
||||||
crop = height - (camera->height * ratio_w);
|
|
||||||
top += crop / 2;
|
|
||||||
bottom += crop / 2;
|
|
||||||
} else {
|
|
||||||
crop = width - (camera->width * ratio_h);
|
|
||||||
left += crop / 2;
|
|
||||||
right += crop / 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
GST_INFO_OBJECT (camera,
|
|
||||||
"updating crop: left:%d, right:%d, top:%d, bottom:%d", left, right, top,
|
|
||||||
bottom);
|
|
||||||
g_object_set (G_OBJECT (camera->src_zoom_crop), "top", top, "bottom",
|
|
||||||
bottom, "left", left, "right", right, NULL);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* img_capture_prepared:
|
* img_capture_prepared:
|
||||||
* @data: camerabin object
|
* @data: camerabin object
|
||||||
@ -1496,41 +1435,16 @@ static void
|
|||||||
img_capture_prepared (gpointer data, GstCaps * caps)
|
img_capture_prepared (gpointer data, GstCaps * caps)
|
||||||
{
|
{
|
||||||
GstCameraBin *camera = GST_CAMERABIN (data);
|
GstCameraBin *camera = GST_CAMERABIN (data);
|
||||||
GstStructure *st, *new_st;
|
|
||||||
gint i;
|
|
||||||
const gchar *field_name;
|
|
||||||
gboolean adapt = FALSE;
|
|
||||||
|
|
||||||
GST_INFO_OBJECT (camera, "image capture prepared");
|
GST_INFO_OBJECT (camera, "image capture prepared");
|
||||||
|
|
||||||
/* It is possible we are about to get something else that we requested */
|
/* It is possible we are about to get something else that we requested */
|
||||||
if (!gst_caps_is_equal (camera->image_capture_caps, caps)) {
|
if (!gst_caps_is_equal (camera->image_capture_caps, caps)) {
|
||||||
adapt = TRUE;
|
gst_camerabin_adapt_image_capture (camera, caps);
|
||||||
/* If capture preparation has added new fields to requested caps,
|
} else {
|
||||||
we need to copy them */
|
gst_camerabin_set_capsfilter_caps (camera, camera->image_capture_caps);
|
||||||
st = gst_caps_get_structure (camera->image_capture_caps, 0);
|
|
||||||
new_st = gst_structure_copy (st);
|
|
||||||
st = gst_caps_get_structure (caps, 0);
|
|
||||||
for (i = 0; i < gst_structure_n_fields (st); i++) {
|
|
||||||
field_name = gst_structure_nth_field_name (st, i);
|
|
||||||
if (!gst_structure_has_field (new_st, field_name)) {
|
|
||||||
GST_DEBUG_OBJECT (camera, "new field in prepared caps: %s", field_name);
|
|
||||||
gst_structure_set_value (new_st, field_name,
|
|
||||||
gst_structure_get_value (st, field_name));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
gst_caps_replace (&camera->image_capture_caps,
|
|
||||||
gst_caps_new_full (new_st, NULL));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Update capsfilters */
|
|
||||||
gst_camerabin_set_capsfilter_caps (camera, camera->image_capture_caps);
|
|
||||||
|
|
||||||
if (adapt) {
|
|
||||||
/* If incoming buffer resolution is different from what application
|
|
||||||
requested, then we can fix this in camerabin */
|
|
||||||
gst_camerabin_adapt_video_resolution (camera, caps);
|
|
||||||
}
|
|
||||||
g_object_set (G_OBJECT (camera->src_out_sel), "resend-latest", FALSE,
|
g_object_set (G_OBJECT (camera->src_out_sel), "resend-latest", FALSE,
|
||||||
"active-pad", camera->pad_src_img, NULL);
|
"active-pad", camera->pad_src_img, NULL);
|
||||||
}
|
}
|
||||||
@ -1562,10 +1476,10 @@ gst_camerabin_start_image_capture (GstCameraBin * camera)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!camera->image_capture_caps) {
|
if (!camera->image_capture_caps) {
|
||||||
if (camera->image_width && camera->image_height) {
|
if (camera->image_capture_width && camera->image_capture_height) {
|
||||||
/* Resolution is set, but it isn't in use yet */
|
/* Resolution is set, but it isn't in use yet */
|
||||||
gst_camerabin_set_image_capture_caps (camera, camera->image_width,
|
gst_camerabin_set_image_capture_caps (camera,
|
||||||
camera->image_height);
|
camera->image_capture_width, camera->image_capture_height);
|
||||||
} else {
|
} else {
|
||||||
/* Capture resolution not set. Use viewfinder resolution */
|
/* Capture resolution not set. Use viewfinder resolution */
|
||||||
camera->image_capture_caps = gst_caps_copy (camera->view_finder_caps);
|
camera->image_capture_caps = gst_caps_copy (camera->view_finder_caps);
|
||||||
@ -2326,10 +2240,103 @@ gst_camerabin_finish_image_capture (GstCameraBin * camera)
|
|||||||
g_object_set (camera->src_zoom_crop, "left", 0, "right", 0,
|
g_object_set (camera->src_zoom_crop, "left", 0, "right", 0,
|
||||||
"top", 0, "bottom", 0, NULL);
|
"top", 0, "bottom", 0, NULL);
|
||||||
}
|
}
|
||||||
|
camera->base_crop_left = 0;
|
||||||
|
camera->base_crop_right = 0;
|
||||||
|
camera->base_crop_top = 0;
|
||||||
|
camera->base_crop_bottom = 0;
|
||||||
gst_camerabin_set_capsfilter_caps (camera, camera->view_finder_caps);
|
gst_camerabin_set_capsfilter_caps (camera, camera->view_finder_caps);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* gst_camerabin_adapt_image_capture:
|
||||||
|
* @camera: camerabin object
|
||||||
|
* @in_caps: caps object that describes incoming image format
|
||||||
|
*
|
||||||
|
* Adjust capsfilters and crop according image capture caps if necessary.
|
||||||
|
* The captured image format from video source might be different from
|
||||||
|
* what application requested, so we can try to fix that in camerabin.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
gst_camerabin_adapt_image_capture (GstCameraBin * camera, GstCaps * in_caps)
|
||||||
|
{
|
||||||
|
GstStructure *in_st, *new_st, *req_st;
|
||||||
|
gint i, in_width = 0, in_height = 0, req_width = 0, req_height =
|
||||||
|
0, crop = 0;
|
||||||
|
const gchar *field_name;
|
||||||
|
gdouble ratio_w, ratio_h;
|
||||||
|
GstCaps *filter_caps = NULL;
|
||||||
|
|
||||||
|
in_st = gst_caps_get_structure (in_caps, 0);
|
||||||
|
gst_structure_get_int (in_st, "width", &in_width);
|
||||||
|
gst_structure_get_int (in_st, "height", &in_height);
|
||||||
|
|
||||||
|
req_st = gst_caps_get_structure (camera->image_capture_caps, 0);
|
||||||
|
gst_structure_get_int (req_st, "width", &req_width);
|
||||||
|
gst_structure_get_int (req_st, "height", &req_height);
|
||||||
|
|
||||||
|
GST_INFO_OBJECT (camera, "we requested %dx%d, and got %dx%d", req_width,
|
||||||
|
req_height, in_width, in_height);
|
||||||
|
|
||||||
|
/* If new fields have been added, we need to copy them */
|
||||||
|
new_st = gst_structure_copy (req_st);
|
||||||
|
for (i = 0; i < gst_structure_n_fields (in_st); i++) {
|
||||||
|
field_name = gst_structure_nth_field_name (in_st, i);
|
||||||
|
if (!gst_structure_has_field (new_st, field_name)) {
|
||||||
|
GST_DEBUG_OBJECT (camera, "new field in new caps: %s", field_name);
|
||||||
|
gst_structure_set_value (new_st, field_name,
|
||||||
|
gst_structure_get_value (in_st, field_name));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Crop if requested aspect ratio differs from incoming frame aspect ratio */
|
||||||
|
if (camera->src_zoom_crop) {
|
||||||
|
|
||||||
|
ratio_w = (gdouble) in_width / req_width;
|
||||||
|
ratio_h = (gdouble) in_height / req_height;
|
||||||
|
|
||||||
|
if (ratio_w < ratio_h) {
|
||||||
|
crop = in_height - (req_height * ratio_w);
|
||||||
|
camera->base_crop_top = crop / 2;
|
||||||
|
camera->base_crop_bottom = crop / 2;
|
||||||
|
} else {
|
||||||
|
crop = in_width - (req_width * ratio_h);
|
||||||
|
camera->base_crop_left = crop / 2;
|
||||||
|
camera->base_crop_right += crop / 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
GST_INFO_OBJECT (camera,
|
||||||
|
"setting base crop: left:%d, right:%d, top:%d, bottom:%d",
|
||||||
|
camera->base_crop_left, camera->base_crop_right, camera->base_crop_top,
|
||||||
|
camera->base_crop_bottom);
|
||||||
|
g_object_set (G_OBJECT (camera->src_zoom_crop), "top",
|
||||||
|
camera->base_crop_top, "bottom", camera->base_crop_bottom, "left",
|
||||||
|
camera->base_crop_left, "right", camera->base_crop_right, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Update capsfilters */
|
||||||
|
gst_caps_replace (&camera->image_capture_caps,
|
||||||
|
gst_caps_new_full (new_st, NULL));
|
||||||
|
gst_camerabin_set_capsfilter_caps (camera, camera->image_capture_caps);
|
||||||
|
|
||||||
|
/* Adjust the capsfilter before crop and videoscale elements if necessary */
|
||||||
|
if (in_width == camera->width && in_height == camera->height) {
|
||||||
|
GST_DEBUG_OBJECT (camera, "no adaptation with resolution needed");
|
||||||
|
} else {
|
||||||
|
GST_DEBUG_OBJECT (camera,
|
||||||
|
"changing %" GST_PTR_FORMAT " from %dx%d to %dx%d", camera->src_filter,
|
||||||
|
camera->width, camera->height, in_width, in_height);
|
||||||
|
/* Apply the width and height to filter caps */
|
||||||
|
g_object_get (G_OBJECT (camera->src_filter), "caps", &filter_caps, NULL);
|
||||||
|
filter_caps = gst_caps_make_writable (filter_caps);
|
||||||
|
gst_caps_set_simple (filter_caps, "width", G_TYPE_INT, in_width, "height",
|
||||||
|
G_TYPE_INT, in_height, NULL);
|
||||||
|
g_object_set (G_OBJECT (camera->src_filter), "caps", filter_caps, NULL);
|
||||||
|
gst_caps_unref (filter_caps);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* GObject callback functions implementation
|
* GObject callback functions implementation
|
||||||
*/
|
*/
|
||||||
@ -2739,8 +2746,12 @@ gst_camerabin_init (GstCameraBin * camera, GstCameraBinClass * gclass)
|
|||||||
camera->height = DEFAULT_HEIGHT;
|
camera->height = DEFAULT_HEIGHT;
|
||||||
camera->fps_n = DEFAULT_FPS_N;
|
camera->fps_n = DEFAULT_FPS_N;
|
||||||
camera->fps_d = DEFAULT_FPS_D;
|
camera->fps_d = DEFAULT_FPS_D;
|
||||||
camera->image_width = 0;
|
camera->image_capture_width = 0;
|
||||||
camera->image_height = 0;
|
camera->image_capture_height = 0;
|
||||||
|
camera->base_crop_left = 0;
|
||||||
|
camera->base_crop_right = 0;
|
||||||
|
camera->base_crop_top = 0;
|
||||||
|
camera->base_crop_bottom = 0;
|
||||||
|
|
||||||
camera->event_tags = gst_tag_list_new ();
|
camera->event_tags = gst_tag_list_new ();
|
||||||
|
|
||||||
@ -3448,8 +3459,8 @@ gst_camerabin_user_image_res (GstCameraBin * camera, gint width, gint height)
|
|||||||
gst_camerabin_set_image_capture_caps (camera, width, height);
|
gst_camerabin_set_image_capture_caps (camera, width, height);
|
||||||
|
|
||||||
/* These will be used in _start_image_capture() function */
|
/* These will be used in _start_image_capture() function */
|
||||||
camera->image_width = width;
|
camera->image_capture_width = width;
|
||||||
camera->image_height = height;
|
camera->image_capture_height = height;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* entry point to initialize the plug-in
|
/* entry point to initialize the plug-in
|
||||||
|
@ -64,15 +64,16 @@ struct _GstCameraBin
|
|||||||
gboolean stop_requested; /* TRUE if capturing stop needed */
|
gboolean stop_requested; /* TRUE if capturing stop needed */
|
||||||
gboolean paused; /* TRUE if capturing paused */
|
gboolean paused; /* TRUE if capturing paused */
|
||||||
|
|
||||||
/* resolution and frames per second of image captured by v4l2 device */
|
/* Resolution of the buffers configured to camerabin */
|
||||||
gint width;
|
gint width;
|
||||||
gint height;
|
gint height;
|
||||||
|
/* Frames per second configured to camerabin */
|
||||||
gint fps_n;
|
gint fps_n;
|
||||||
gint fps_d;
|
gint fps_d;
|
||||||
|
|
||||||
/* Image capture resolution */
|
/* Image capture resolution */
|
||||||
gint image_width;
|
gint image_capture_width;
|
||||||
gint image_height;
|
gint image_capture_height;
|
||||||
|
|
||||||
/* Image tags are collected here first before sending to imgbin */
|
/* Image tags are collected here first before sending to imgbin */
|
||||||
GstTagList *event_tags;
|
GstTagList *event_tags;
|
||||||
@ -147,6 +148,13 @@ struct _GstCameraBin
|
|||||||
|
|
||||||
/* Buffer probe id for captured image handling */
|
/* Buffer probe id for captured image handling */
|
||||||
gulong image_captured_id;
|
gulong image_captured_id;
|
||||||
|
|
||||||
|
/* Optional base crop for frames. Used to crop frames e.g.
|
||||||
|
due to wrong aspect ratio, before the crop related to zooming. */
|
||||||
|
gint base_crop_top;
|
||||||
|
gint base_crop_bottom;
|
||||||
|
gint base_crop_left;
|
||||||
|
gint base_crop_right;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -169,7 +177,7 @@ struct _GstCameraBinClass
|
|||||||
|
|
||||||
/* signals (callback) */
|
/* signals (callback) */
|
||||||
|
|
||||||
gboolean (*img_done) (GstCameraBin * camera, const gchar * filename);
|
gboolean (*img_done) (GstCameraBin * camera, const gchar * filename);
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -188,4 +196,4 @@ typedef enum
|
|||||||
GType gst_camerabin_get_type (void);
|
GType gst_camerabin_get_type (void);
|
||||||
|
|
||||||
G_END_DECLS
|
G_END_DECLS
|
||||||
#endif /* #ifndef __GST_CAMERABIN_H__ */
|
#endif /* #ifndef __GST_CAMERABIN_H__ */
|
||||||
|
Loading…
x
Reference in New Issue
Block a user