mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2025-01-10 17:35:59 +00:00
gst/videocrop/gstvideocrop.c: Fix cropping for packed 4:2:2 formats YUYV/YUY2 and UYVY.
Original commit message from CVS: * gst/videocrop/gstvideocrop.c: (gst_video_crop_get_image_details_from_caps), (gst_video_crop_transform_packed_complex): Fix cropping for packed 4:2:2 formats YUYV/YUY2 and UYVY. * tests/icles/videocrop-test.c: (check_bus_for_errors), (test_with_caps), (main): Block streaming thread before changing filter caps while the pipeline is running so that we don't get random not-negotiated errors just because GStreamer can't handle that yet.
This commit is contained in:
parent
75ccb47468
commit
726254bdde
2 changed files with 67 additions and 48 deletions
|
@ -244,8 +244,10 @@ gst_video_crop_get_image_details_from_caps (GstVideoCrop * vcrop,
|
|||
details->stride = GST_ROUND_UP_4 (width * 2);
|
||||
details->size = details->stride * height;
|
||||
if (format == GST_MAKE_FOURCC ('U', 'Y', 'V', 'Y')) {
|
||||
/* UYVY = 4:2:2 - [U0 Y0 V0 Y1] [U2 Y2 V2 Y3] [U4 Y4 V4 Y5] */
|
||||
details->macro_y_off = 1;
|
||||
} else {
|
||||
/* YUYV = 4:2:2 - [Y0 U0 Y1 V0] [Y2 U2 Y3 V2] [Y4 U4 Y5 V4] = YUY2 */
|
||||
details->macro_y_off = 0;
|
||||
}
|
||||
break;
|
||||
|
@ -311,6 +313,8 @@ gst_video_crop_get_unit_size (GstBaseTransform * trans, GstCaps * caps,
|
|||
return TRUE;
|
||||
}
|
||||
|
||||
#define ROUND_DOWN_2(n) ((n)&(~1))
|
||||
|
||||
static void
|
||||
gst_video_crop_transform_packed_complex (GstVideoCrop * vcrop,
|
||||
GstBuffer * inbuf, GstBuffer * outbuf)
|
||||
|
@ -322,27 +326,25 @@ gst_video_crop_transform_packed_complex (GstVideoCrop * vcrop,
|
|||
out_data = GST_BUFFER_DATA (outbuf);
|
||||
|
||||
in_data += vcrop->crop_top * vcrop->in.stride;
|
||||
in_data += vcrop->crop_left * vcrop->in.bytes_per_pixel;
|
||||
|
||||
/* rounding down here so we end up at the start of a macro-pixel and not
|
||||
* in the middle of one */
|
||||
in_data += ROUND_DOWN_2 (vcrop->crop_left) * vcrop->in.bytes_per_pixel;
|
||||
|
||||
dx = vcrop->out.width * vcrop->out.bytes_per_pixel;
|
||||
|
||||
/* UYVY = 4:2:2 - [U0 Y0 V0 Y1] [U2 Y2 V2 Y3] [U4 Y4 V4 Y5]
|
||||
* YUYV = 4:2:2 - [Y0 U0 Y1 V0] [Y2 U2 Y3 V2] [Y4 U4 Y5 V4] = YUY2 */
|
||||
if ((vcrop->crop_left % 2) != 0) {
|
||||
for (i = 0; i < vcrop->out.height; ++i) {
|
||||
gint j;
|
||||
|
||||
memcpy (out_data, in_data, dx);
|
||||
|
||||
/* U/V is horizontally subsampled by a factor of 2, so must fix that up */
|
||||
/* FIXME: this is obviously not quite right */
|
||||
if (vcrop->in.macro_y_off == 0) {
|
||||
for (j = 1; j < vcrop->out.stride; j += 2) {
|
||||
out_data[j] = in_data[j - 1];
|
||||
}
|
||||
} else {
|
||||
for (j = 0; j < vcrop->out.stride /* -2 */ ; j += 2) {
|
||||
out_data[j] = in_data[j + 2];
|
||||
}
|
||||
}
|
||||
/* move just the Y samples one pixel to the left, don't worry about
|
||||
* chroma shift */
|
||||
for (j = vcrop->in.macro_y_off; j < vcrop->out.stride - 2; j += 2)
|
||||
out_data[j] = in_data[j + 2];
|
||||
|
||||
in_data += vcrop->in.stride;
|
||||
out_data += vcrop->out.stride;
|
||||
|
|
|
@ -34,41 +34,46 @@ GST_DEBUG_CATEGORY_STATIC (videocrop_test_debug);
|
|||
#define TIME_PER_TEST 10 /* seconds each format is tested */
|
||||
#define FRAMERATE 15 /* frames per second */
|
||||
|
||||
typedef struct _CropState
|
||||
{
|
||||
GstElement *videocrop;
|
||||
guint hcrop;
|
||||
guint vcrop;
|
||||
} CropState;
|
||||
|
||||
static gboolean
|
||||
tick_cb (CropState * state)
|
||||
check_bus_for_errors (GstBus * bus, GstClockTime max_wait_time)
|
||||
{
|
||||
GST_LOG ("hcrop = %3d, vcrop = %3d", state->vcrop, state->hcrop);
|
||||
GstMessage *msg;
|
||||
|
||||
g_object_set (state->videocrop, "left", state->hcrop,
|
||||
"top", state->vcrop, NULL);
|
||||
msg = gst_bus_poll (bus, GST_MESSAGE_ERROR, max_wait_time);
|
||||
|
||||
++state->vcrop;
|
||||
++state->hcrop;
|
||||
if (msg) {
|
||||
GError *err = NULL;
|
||||
gchar *debug = NULL;
|
||||
|
||||
return TRUE; /* call us again */
|
||||
g_assert (GST_MESSAGE_TYPE (msg) == GST_MESSAGE_ERROR);
|
||||
gst_message_parse_error (msg, &err, &debug);
|
||||
GST_ERROR ("ERROR: %s [%s]", err->message, debug);
|
||||
g_print ("\n===========> ERROR: %s\n%s\n\n", err->message, debug);
|
||||
g_error_free (err);
|
||||
g_free (debug);
|
||||
gst_message_unref (msg);
|
||||
}
|
||||
|
||||
return (msg != NULL);
|
||||
}
|
||||
|
||||
static void
|
||||
test_with_caps (GstElement * videocrop, GstCaps * caps)
|
||||
test_with_caps (GstElement * src, GstElement * videocrop, GstCaps * caps)
|
||||
{
|
||||
GstClockTime time_run;
|
||||
GstElement *pipeline;
|
||||
CropState state;
|
||||
GTimer *timer;
|
||||
GstBus *bus;
|
||||
GstPad *pad;
|
||||
guint hcrop;
|
||||
guint vcrop;
|
||||
|
||||
/* caps must be writable, we can't check that here though */
|
||||
g_assert (GST_CAPS_REFCOUNT_VALUE (caps) == 1);
|
||||
|
||||
state.videocrop = videocrop;
|
||||
state.vcrop = 0;
|
||||
state.hcrop = 0;
|
||||
timer = g_timer_new ();
|
||||
vcrop = 0;
|
||||
hcrop = 0;
|
||||
|
||||
pipeline = GST_ELEMENT (gst_element_get_parent (videocrop));
|
||||
g_assert (GST_IS_PIPELINE (pipeline));
|
||||
|
@ -77,35 +82,47 @@ test_with_caps (GstElement * videocrop, GstCaps * caps)
|
|||
* errors resulting from our on-the-fly changing of the filtercaps */
|
||||
bus = gst_pipeline_get_bus (GST_PIPELINE (pipeline));
|
||||
|
||||
/* pad to block */
|
||||
pad = gst_element_get_pad (src, "src");
|
||||
|
||||
time_run = 0;
|
||||
do {
|
||||
GstClockTime wait_time;
|
||||
GstMessage *msg;
|
||||
GstClockTime wait_time, waited_for_block;
|
||||
|
||||
if (check_bus_for_errors (bus, 0))
|
||||
break;
|
||||
|
||||
wait_time = GST_SECOND / FRAMERATE;
|
||||
msg = gst_bus_poll (bus, GST_MESSAGE_ERROR, wait_time);
|
||||
|
||||
if (msg) {
|
||||
GError *err = NULL;
|
||||
gchar *debug = NULL;
|
||||
GST_LOG ("hcrop = %3d, vcrop = %3d", vcrop, hcrop);
|
||||
|
||||
g_assert (GST_MESSAGE_TYPE (msg) == GST_MESSAGE_ERROR);
|
||||
gst_message_parse_error (msg, &err, &debug);
|
||||
g_print ("\n===========> ERROR: %s\n%s\n\n", err->message, debug);
|
||||
g_error_free (err);
|
||||
g_free (debug);
|
||||
gst_message_unref (msg);
|
||||
break;
|
||||
g_timer_reset (timer);
|
||||
|
||||
/* need to block the streaming thread while changing these properties,
|
||||
* otherwise we might get random not-negotiated errors (when caps are
|
||||
* changed in between upstream calling pad_alloc_buffer() and pushing
|
||||
* the processed buffer?) */
|
||||
gst_pad_set_blocked (pad, TRUE);
|
||||
g_object_set (videocrop, "left", hcrop, "top", vcrop, NULL);
|
||||
gst_pad_set_blocked (pad, FALSE);
|
||||
|
||||
waited_for_block = g_timer_elapsed (timer, NULL) * (double) GST_SECOND;
|
||||
/* GST_LOG ("waited: %" GST_TIME_FORMAT ", frame len: %" GST_TIME_FORMAT,
|
||||
GST_TIME_ARGS (waited_for_block), GST_TIME_ARGS (wait_time)); */
|
||||
++vcrop;
|
||||
++hcrop;
|
||||
|
||||
if (wait_time > waited_for_block) {
|
||||
g_usleep ((wait_time - waited_for_block) / GST_MSECOND);
|
||||
}
|
||||
|
||||
if (!tick_cb (&state))
|
||||
break;
|
||||
|
||||
time_run += wait_time;
|
||||
}
|
||||
while (time_run < (TIME_PER_TEST * GST_SECOND));
|
||||
|
||||
g_timer_destroy (timer);
|
||||
gst_object_unref (bus);
|
||||
gst_object_unref (pad);
|
||||
}
|
||||
|
||||
/* return a list of caps where we only need to set
|
||||
|
@ -313,7 +330,7 @@ main (int argc, char **argv)
|
|||
ret = gst_element_get_state (pipeline, NULL, NULL, -1);
|
||||
|
||||
if (ret != GST_STATE_CHANGE_FAILURE) {
|
||||
test_with_caps (crop, caps);
|
||||
test_with_caps (src, crop, caps);
|
||||
} else {
|
||||
g_print ("Format: %s not supported (failed to go to PLAYING)\n", s);
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue