camerabin: refactor cropping and adapting captured images

This commit is contained in:
Lasse Laukkanen 2009-09-08 09:13:07 +03:00 committed by Stefan Kost
parent 341d91520a
commit 190d54b096
2 changed files with 137 additions and 118 deletions

View file

@ -296,6 +296,8 @@ static void
gst_camerabin_update_aspect_filter (GstCameraBin * camera, GstCaps * new_caps);
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
@ -1028,10 +1030,13 @@ gst_camerabin_set_videosrc_zoom (GstCameraBin * camera, gint zoom)
static gboolean
gst_camerabin_set_element_zoom (GstCameraBin * camera, gint zoom)
{
gint w2_crop = 0;
gint h2_crop = 0;
gint w2_crop = 0, h2_crop = 0;
GstPad *pad_zoom_sink = NULL;
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) {
/* 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;
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
* path on videoscale */
w2_crop &= 0xFFFE;
left &= 0xFFFE;
}
pad_zoom_sink = gst_element_get_static_pad (camera->src_zoom_crop, "sink");
GST_INFO_OBJECT (camera,
"sw cropping: left:%d, right:%d, top:%d, bottom:%d", w2_crop, w2_crop,
h2_crop, h2_crop);
"sw cropping: left:%d, right:%d, top:%d, bottom:%d", left, right, top,
bottom);
GST_PAD_STREAM_LOCK (pad_zoom_sink);
g_object_set (camera->src_zoom_crop, "left", w2_crop, "right", w2_crop,
"top", h2_crop, "bottom", h2_crop, NULL);
g_object_set (camera->src_zoom_crop, "left", left, "right", right, "top",
top, "bottom", bottom, NULL);
GST_PAD_STREAM_UNLOCK (pad_zoom_sink);
gst_object_unref (pad_zoom_sink);
ret = TRUE;
@ -1277,7 +1287,6 @@ gst_camerabin_get_internal_tags (GstCameraBin * camera)
GstColorBalanceChannel *channel;
gint min_value, max_value, mid_value, cur_value;
if (camera->active_bin == camera->vidbin) {
/* FIXME: check if internal video tag setting is needed */
goto done;
@ -1415,76 +1424,6 @@ gst_camerabin_set_capsfilter_caps (GstCameraBin * camera, GstCaps * new_caps)
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:
* @data: camerabin object
@ -1496,41 +1435,16 @@ static void
img_capture_prepared (gpointer data, GstCaps * caps)
{
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");
/* It is possible we are about to get something else that we requested */
if (!gst_caps_is_equal (camera->image_capture_caps, caps)) {
adapt = TRUE;
/* If capture preparation has added new fields to requested caps,
we need to copy them */
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));
gst_camerabin_adapt_image_capture (camera, caps);
} else {
gst_camerabin_set_capsfilter_caps (camera, camera->image_capture_caps);
}
/* 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,
"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_width && camera->image_height) {
if (camera->image_capture_width && camera->image_capture_height) {
/* Resolution is set, but it isn't in use yet */
gst_camerabin_set_image_capture_caps (camera, camera->image_width,
camera->image_height);
gst_camerabin_set_image_capture_caps (camera,
camera->image_capture_width, camera->image_capture_height);
} else {
/* Capture resolution not set. Use viewfinder resolution */
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,
"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_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
*/
@ -2739,8 +2746,12 @@ gst_camerabin_init (GstCameraBin * camera, GstCameraBinClass * gclass)
camera->height = DEFAULT_HEIGHT;
camera->fps_n = DEFAULT_FPS_N;
camera->fps_d = DEFAULT_FPS_D;
camera->image_width = 0;
camera->image_height = 0;
camera->image_capture_width = 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 ();
@ -3448,8 +3459,8 @@ gst_camerabin_user_image_res (GstCameraBin * camera, gint width, gint height)
gst_camerabin_set_image_capture_caps (camera, width, height);
/* These will be used in _start_image_capture() function */
camera->image_width = width;
camera->image_height = height;
camera->image_capture_width = width;
camera->image_capture_height = height;
}
/* entry point to initialize the plug-in

View file

@ -64,15 +64,16 @@ struct _GstCameraBin
gboolean stop_requested; /* TRUE if capturing stop needed */
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 height;
/* Frames per second configured to camerabin */
gint fps_n;
gint fps_d;
/* Image capture resolution */
gint image_width;
gint image_height;
gint image_capture_width;
gint image_capture_height;
/* Image tags are collected here first before sending to imgbin */
GstTagList *event_tags;
@ -147,6 +148,13 @@ struct _GstCameraBin
/* Buffer probe id for captured image handling */
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) */
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);
G_END_DECLS
#endif /* #ifndef __GST_CAMERABIN_H__ */
#endif /* #ifndef __GST_CAMERABIN_H__ */