From 190d54b096669608776af76d656975b013a472dc Mon Sep 17 00:00:00 2001 From: Lasse Laukkanen Date: Tue, 8 Sep 2009 09:13:07 +0300 Subject: [PATCH] camerabin: refactor cropping and adapting captured images --- gst/camerabin/gstcamerabin.c | 237 ++++++++++++++++++----------------- gst/camerabin/gstcamerabin.h | 18 ++- 2 files changed, 137 insertions(+), 118 deletions(-) diff --git a/gst/camerabin/gstcamerabin.c b/gst/camerabin/gstcamerabin.c index 79a17167ee..c7af7a4919 100644 --- a/gst/camerabin/gstcamerabin.c +++ b/gst/camerabin/gstcamerabin.c @@ -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 diff --git a/gst/camerabin/gstcamerabin.h b/gst/camerabin/gstcamerabin.h index ef8a9a122f..58350c3aae 100644 --- a/gst/camerabin/gstcamerabin.h +++ b/gst/camerabin/gstcamerabin.h @@ -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__ */