gst/videocrop/gstvideocrop.c: Fix cropping for packed 4:2:2 formats YUYV/YUY2 and...
authorTim-Philipp Müller <tim@centricular.net>
Sun, 28 Jan 2007 18:28:33 +0000 (18:28 +0000)
committerTim-Philipp Müller <tim@centricular.net>
Sun, 28 Jan 2007 18:28:33 +0000 (18:28 +0000)
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.

gst/videocrop/gstvideocrop.c
tests/icles/videocrop-test.c

index 9dfefc0..7837554 100644 (file)
@@ -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;
index b793837..ecc7b1b 100644 (file)
@@ -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;
+
+  msg = gst_bus_poll (bus, GST_MESSAGE_ERROR, max_wait_time);
 
-  g_object_set (state->videocrop, "left", state->hcrop,
-      "top", state->vcrop, NULL);
+  if (msg) {
+    GError *err = NULL;
+    gchar *debug = NULL;
 
-  ++state->vcrop;
-  ++state->hcrop;
+    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 TRUE;                  /* call us again */
+  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;
 
-    wait_time = GST_SECOND / FRAMERATE;
-    msg = gst_bus_poll (bus, GST_MESSAGE_ERROR, wait_time);
-
-    if (msg) {
-      GError *err = NULL;
-      gchar *debug = NULL;
-
-      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);
+    if (check_bus_for_errors (bus, 0))
       break;
-    }
 
-    if (!tick_cb (&state))
-      break;
+    wait_time = GST_SECOND / FRAMERATE;
+
+    GST_LOG ("hcrop = %3d, vcrop = %3d", vcrop, hcrop);
+
+    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);
+    }
 
     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);
       }