tests: camerabin: add tests for GstPhotography image capture
authorThiago Santos <thiagoss@osg.samsung.com>
Fri, 24 Apr 2015 17:04:55 +0000 (14:04 -0300)
committerThiago Santos <thiagoss@osg.samsung.com>
Fri, 24 Apr 2015 18:12:47 +0000 (15:12 -0300)
GstPhotography enables new paths in wrappercamerabinsrc that allows
the source to be notified about the capture caps and provide an
alternative caps if desired bypassing the negotiation (this doesn't
seem like a good idea these days). To make sure it keeps working
until we remove it from the API in favor of standard caps negotiation
features this test was added.

It adds 3 extra tests with a simple test source that will:
1) Test that capturing with ANY caps work
2) Test that capturing with a fixed caps work
3) Test that capturing with a fixed caps and having the source
   pick a different resolution from GstPhotography API works
   by having wrappercamerabinsrc crop the capture to the final
   requested dimensions

tests/check/elements/camerabin.c

index 8f3d19f..3458cac 100644 (file)
@@ -32,6 +32,8 @@
 #include <gst/video/video.h>
 #include <gst/check/gstcheck.h>
 #include <gst/basecamerabinsrc/gstbasecamerasrc.h>
+#include <gst/base/gstpushsrc.h>
+#include <gst/interfaces/photography.h>
 #include <gst/pbutils/encoding-profile.h>
 
 #define IMAGE_FILENAME "image"
 #define VIDEO_PAD_SUPPORTED_CAPS "video/x-raw, format=RGB, width=600, height=480"
 #define IMAGE_PAD_SUPPORTED_CAPS "video/x-raw, format=RGB, width=800, height=600"
 
+static GstStaticPadTemplate src_template = GST_STATIC_PAD_TEMPLATE ("src",
+    GST_PAD_SRC,
+    GST_PAD_ALWAYS,
+    GST_STATIC_CAPS ("video/x-raw, format=RGB"));
+
 static GstStaticPadTemplate vfsrc_template =
 GST_STATIC_PAD_TEMPLATE (GST_BASE_CAMERA_SRC_VIEWFINDER_PAD_NAME,
     GST_PAD_SRC,
@@ -89,7 +96,6 @@ struct _GstTestCameraSrcClass
 
 GType gst_test_camera_src_get_type (void);
 
-#define gst_test_camera_src_parent_class parent_class
 G_DEFINE_TYPE (GstTestCameraSrc, gst_test_camera_src, GST_TYPE_BASE_CAMERA_SRC);
 
 static gboolean
@@ -197,8 +203,244 @@ gst_test_camera_src_init (GstTestCameraSrc * self)
 
 /* end of custom test camera src element */
 
+/* custom video source element that implements GstPhotography iface */
+
+#define GST_TYPE_TEST_VIDEO_SRC \
+  (gst_test_video_src_get_type())
+#define GST_TEST_VIDEO_SRC(obj) \
+  (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_TEST_VIDEO_SRC,GstTestVideoSrc))
+#define GST_TEST_VIDEO_SRC_CLASS(klass) \
+  (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_TEST_VIDEO_SRC,GstTestVideoSrcClass))
+#define GST_TEST_VIDEO_SRC_CAST(obj) ((GstTestVideoSrc *)obj)
+
+typedef struct _GstTestVideoSrc GstTestVideoSrc;
+typedef struct _GstTestVideoSrcClass GstTestVideoSrcClass;
+struct _GstTestVideoSrc
+{
+  GstPushSrc element;
+
+  gint width, height;
+  GstCaps *caps;
+
+  /* if TRUE, this element will only output resolutions with       *
+   * same width and height (square frames). This allows us testing *
+   * extra cropping feature with GstPhotography interface captures */
+  gboolean enable_resolution_restriction;
+};
+
+struct _GstTestVideoSrcClass
+{
+  GstPushSrcClass parent_class;
+};
+
+GType gst_test_video_src_get_type (void);
+
+enum
+{
+  PROP_0,
+  PROP_WB_MODE,
+  PROP_COLOR_TONE,
+  PROP_SCENE_MODE,
+  PROP_FLASH_MODE,
+  PROP_FLICKER_MODE,
+  PROP_FOCUS_MODE,
+  PROP_CAPABILITIES,
+  PROP_EV_COMP,
+  PROP_ISO_SPEED,
+  PROP_APERTURE,
+  PROP_EXPOSURE_TIME,
+  PROP_IMAGE_PREVIEW_SUPPORTED_CAPS,
+  PROP_IMAGE_CAPTURE_SUPPORTED_CAPS,
+  PROP_ZOOM,
+  PROP_COLOR_TEMPERATURE,
+  PROP_WHITE_POINT,
+  PROP_ANALOG_GAIN,
+  PROP_LENS_FOCUS,
+  PROP_MIN_EXPOSURE_TIME,
+  PROP_MAX_EXPORURE_TIME,
+  PROP_NOISE_REDUCTION
+};
+
+static gboolean
+gst_test_video_src_prepare_for_capture (GstPhotography * photo,
+    GstPhotographyCapturePrepared func, GstCaps * capture_caps,
+    gpointer user_data)
+{
+  GstCaps *caps;
+  GstTestVideoSrc *testvideosrc = GST_TEST_VIDEO_SRC (photo);
+
+  if (testvideosrc->enable_resolution_restriction) {
+    GstStructure *str = gst_caps_get_structure (capture_caps, 0);
+    gint width, height;
+
+    gst_structure_get_int (str, "width", &width);
+    gst_structure_get_int (str, "height", &height);
+
+    width = height = MAX (width, height);
+    str = gst_structure_copy (str);
+    gst_structure_set (str, "width", G_TYPE_INT, width, "height", G_TYPE_INT,
+        height, NULL);
+    caps = gst_caps_new_full (str, NULL);
+    caps = gst_caps_fixate (caps);
+    fail_unless (testvideosrc->caps == NULL);
+    testvideosrc->caps = gst_caps_ref (caps);
+  } else {
+    caps = gst_caps_ref (capture_caps);
+  }
+
+  func (user_data, caps);
+  gst_caps_unref (caps);
+  return TRUE;
+}
+
+static void
+gst_test_video_src_photography_init (gpointer g_iface, gpointer iface_data)
+{
+  GstPhotographyInterface *iface = g_iface;
+
+  iface->prepare_for_capture = gst_test_video_src_prepare_for_capture;
+}
+
+G_DEFINE_TYPE_WITH_CODE (GstTestVideoSrc, gst_test_video_src, GST_TYPE_PUSH_SRC,
+    G_IMPLEMENT_INTERFACE (GST_TYPE_PHOTOGRAPHY,
+        gst_test_video_src_photography_init));
+
+static void
+gst_test_video_src_get_property (GObject * object,
+    guint prop_id, GValue * value, GParamSpec * pspec)
+{
+  /* don't care */
+}
+
+static void
+gst_test_video_src_set_property (GObject * object,
+    guint prop_id, const GValue * value, GParamSpec * pspec)
+{
+  /* don't care */
+}
+
+static gboolean
+gst_test_video_src_set_caps (GstBaseSrc * src, GstCaps * caps)
+{
+  GstTestVideoSrc *self = GST_TEST_VIDEO_SRC (src);
+  GstStructure *structure = gst_caps_get_structure (caps, 0);
+
+
+  fail_unless (gst_structure_get_int (structure, "width", &self->width));
+  fail_unless (gst_structure_get_int (structure, "height", &self->height));
+
+  return TRUE;
+}
+
+static GstFlowReturn
+gst_test_video_src_alloc (GstPushSrc * src, GstBuffer ** buf)
+{
+  GstTestVideoSrc *self = GST_TEST_VIDEO_SRC (src);
+  guint8 *data;
+  gsize data_size;
+
+  if (self->caps) {
+    gst_base_src_set_caps (GST_BASE_SRC (self), self->caps);
+    gst_caps_unref (self->caps);
+    self->caps = NULL;
+  }
+
+  data_size = self->width * self->height * 3;   /* RGB size */
+  data = g_malloc (data_size);
+  *buf = gst_buffer_new_wrapped (data, data_size);
+
+  return GST_FLOW_OK;
+}
+
+static GstFlowReturn
+gst_test_video_src_fill (GstPushSrc * src, GstBuffer * buf)
+{
+  /* NOP */
+  return GST_FLOW_OK;
+}
+
+static void
+gst_test_video_src_class_init (GstTestVideoSrcClass * klass)
+{
+  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+  GstElementClass *gstelement_class = GST_ELEMENT_CLASS (klass);
+  GstBaseSrcClass *gstbasesrc_class = GST_BASE_SRC_CLASS (klass);
+  GstPushSrcClass *gstpushsrc_class = GST_PUSH_SRC_CLASS (klass);
+
+  gst_element_class_set_static_metadata (gstelement_class,
+      "Test Camera Video Src",
+      "Video/Src",
+      "Test camera video src", "Thiago Santos <thiagoss@osg.samsung.com>");
+
+  gst_element_class_add_pad_template (gstelement_class,
+      gst_static_pad_template_get (&src_template));
+
+  gobject_class->get_property = gst_test_video_src_get_property;
+  gobject_class->set_property = gst_test_video_src_set_property;
+
+  gstbasesrc_class->set_caps = gst_test_video_src_set_caps;
+  gstpushsrc_class->alloc = gst_test_video_src_alloc;
+  gstpushsrc_class->fill = gst_test_video_src_fill;
+
+  /* photography interface properties */
+  g_object_class_override_property (gobject_class, PROP_WB_MODE,
+      GST_PHOTOGRAPHY_PROP_WB_MODE);
+  g_object_class_override_property (gobject_class, PROP_COLOR_TONE,
+      GST_PHOTOGRAPHY_PROP_COLOR_TONE);
+  g_object_class_override_property (gobject_class, PROP_SCENE_MODE,
+      GST_PHOTOGRAPHY_PROP_SCENE_MODE);
+  g_object_class_override_property (gobject_class, PROP_FLASH_MODE,
+      GST_PHOTOGRAPHY_PROP_FLASH_MODE);
+  g_object_class_override_property (gobject_class, PROP_FLICKER_MODE,
+      GST_PHOTOGRAPHY_PROP_FLICKER_MODE);
+  g_object_class_override_property (gobject_class, PROP_FOCUS_MODE,
+      GST_PHOTOGRAPHY_PROP_FOCUS_MODE);
+  g_object_class_override_property (gobject_class, PROP_CAPABILITIES,
+      GST_PHOTOGRAPHY_PROP_CAPABILITIES);
+  g_object_class_override_property (gobject_class, PROP_EV_COMP,
+      GST_PHOTOGRAPHY_PROP_EV_COMP);
+  g_object_class_override_property (gobject_class, PROP_ISO_SPEED,
+      GST_PHOTOGRAPHY_PROP_ISO_SPEED);
+  g_object_class_override_property (gobject_class, PROP_APERTURE,
+      GST_PHOTOGRAPHY_PROP_APERTURE);
+  g_object_class_override_property (gobject_class, PROP_EXPOSURE_TIME,
+      GST_PHOTOGRAPHY_PROP_EXPOSURE_TIME);
+  g_object_class_override_property (gobject_class,
+      PROP_IMAGE_PREVIEW_SUPPORTED_CAPS,
+      GST_PHOTOGRAPHY_PROP_IMAGE_PREVIEW_SUPPORTED_CAPS);
+  g_object_class_override_property (gobject_class,
+      PROP_IMAGE_CAPTURE_SUPPORTED_CAPS,
+      GST_PHOTOGRAPHY_PROP_IMAGE_CAPTURE_SUPPORTED_CAPS);
+  g_object_class_override_property (gobject_class, PROP_ZOOM,
+      GST_PHOTOGRAPHY_PROP_ZOOM);
+  g_object_class_override_property (gobject_class, PROP_COLOR_TEMPERATURE,
+      GST_PHOTOGRAPHY_PROP_COLOR_TEMPERATURE);
+  g_object_class_override_property (gobject_class, PROP_WHITE_POINT,
+      GST_PHOTOGRAPHY_PROP_WHITE_POINT);
+  g_object_class_override_property (gobject_class, PROP_ANALOG_GAIN,
+      GST_PHOTOGRAPHY_PROP_ANALOG_GAIN);
+  g_object_class_override_property (gobject_class, PROP_LENS_FOCUS,
+      GST_PHOTOGRAPHY_PROP_LENS_FOCUS);
+  g_object_class_override_property (gobject_class, PROP_MIN_EXPOSURE_TIME,
+      GST_PHOTOGRAPHY_PROP_MIN_EXPOSURE_TIME);
+  g_object_class_override_property (gobject_class, PROP_MAX_EXPORURE_TIME,
+      GST_PHOTOGRAPHY_PROP_MAX_EXPOSURE_TIME);
+  g_object_class_override_property (gobject_class, PROP_NOISE_REDUCTION,
+      GST_PHOTOGRAPHY_PROP_NOISE_REDUCTION);
+}
+
+static void
+gst_test_video_src_init (GstTestVideoSrc * self)
+{
+  gst_base_src_set_format (GST_BASE_SRC (self), GST_FORMAT_TIME);
+}
+
+/* end of custom test camera src element */
+/* end of custom video source element that implements GstPhotography iface */
+
 
 static GstElement *camera;
+static GstElement *testsrc;
 static guint bus_source;
 static GMainLoop *main_loop;
 static gint capture_count = 0;
@@ -395,9 +637,30 @@ extract_jpeg_tags (const gchar * filename, gint num)
 }
 
 static void
-setup_wrappercamerabinsrc_videotestsrc (void)
+setup_camerabin_common (void)
 {
   GstBus *bus;
+  test_id = g_random_int ();
+  bus_source = 0;
+
+  main_loop = g_main_loop_new (NULL, TRUE);
+
+  camera = gst_check_setup_element ("camerabin");
+  fail_unless (camera != NULL, "failed to create camerabin element");
+
+  bus = gst_pipeline_get_bus (GST_PIPELINE (camera));
+  bus_source = gst_bus_add_watch (bus, (GstBusFunc) capture_bus_cb, main_loop);
+  gst_object_unref (bus);
+
+  tags_found = NULL;
+  capture_count = 0;
+  image_filename = make_test_file_name (IMAGE_FILENAME, -1);
+  video_filename = make_test_file_name (VIDEO_FILENAME, -1);
+}
+
+static void
+setup_wrappercamerabinsrc_videotestsrc (void)
+{
   GstElement *vfbin;
   GstElement *fakevideosink;
   GstElement *src;
@@ -406,13 +669,8 @@ setup_wrappercamerabinsrc_videotestsrc (void)
 
   GST_INFO ("init");
 
-  test_id = g_random_int ();
-  bus_source = 0;
-
-  main_loop = g_main_loop_new (NULL, TRUE);
+  setup_camerabin_common ();
 
-  camera = gst_check_setup_element ("camerabin");
-  fail_unless (camera != NULL, "failed to create camerabin element");
   fakevideosink = gst_element_factory_make ("fakesink", NULL);
   fail_unless (fakevideosink != NULL, "failed to create fakesink element");
   src = gst_element_factory_make ("wrappercamerabinsrc", NULL);
@@ -439,14 +697,46 @@ setup_wrappercamerabinsrc_videotestsrc (void)
   gst_object_unref (vfbin);
   gst_object_unref (fakevideosink);
 
-  bus = gst_pipeline_get_bus (GST_PIPELINE (camera));
-  bus_source = gst_bus_add_watch (bus, (GstBusFunc) capture_bus_cb, main_loop);
-  gst_object_unref (bus);
+  GST_INFO ("init finished");
+}
 
-  tags_found = NULL;
-  capture_count = 0;
-  image_filename = make_test_file_name (IMAGE_FILENAME, -1);
-  video_filename = make_test_file_name (VIDEO_FILENAME, -1);
+static void
+setup_test_camerasrc (void)
+{
+  GstElement *vfbin;
+  GstElement *fakevideosink;
+  GstElement *src;
+  GstElement *audiosrc;
+
+  GST_INFO ("init");
+
+  setup_camerabin_common ();
+
+  fakevideosink = gst_element_factory_make ("fakesink", NULL);
+  fail_unless (fakevideosink != NULL, "failed to create fakesink element");
+  src = gst_element_factory_make ("wrappercamerabinsrc", NULL);
+  fail_unless (src != NULL, "failed to create wrappercamerabinsrc element");
+  testsrc = g_object_new (GST_TYPE_TEST_VIDEO_SRC, NULL);
+  fail_unless (testsrc != NULL, "failed to create testvideosrc element");
+  g_object_set (testsrc, "name", "testsrc", NULL);
+  audiosrc = gst_element_factory_make ("audiotestsrc", NULL);
+  fail_unless (audiosrc != NULL, "failed to create audiotestsrc element");
+
+  preview_caps = gst_caps_new_simple ("video/x-raw", "width", G_TYPE_INT,
+      320, "height", G_TYPE_INT, 240, NULL);
+
+  g_object_set (G_OBJECT (audiosrc), "is-live", TRUE, NULL);
+  g_object_set (G_OBJECT (src), "video-source", testsrc, NULL);
+  g_object_set (G_OBJECT (camera), "camera-source", src, "preview-caps",
+      preview_caps, "post-previews", TRUE, "audio-source", audiosrc, NULL);
+  gst_object_unref (src);
+  gst_object_unref (testsrc);
+  gst_object_unref (audiosrc);
+
+  vfbin = gst_bin_get_by_name (GST_BIN (camera), "vf-bin");
+  g_object_set (G_OBJECT (vfbin), "video-sink", fakevideosink, NULL);
+  gst_object_unref (vfbin);
+  gst_object_unref (fakevideosink);
 
   GST_INFO ("init finished");
 }
@@ -1589,6 +1879,39 @@ GST_START_TEST (test_image_location_switching)
 GST_END_TEST;
 
 
+GST_START_TEST (test_photography_iface_image_capture)
+{
+  run_single_image_capture_test (NULL, NULL);
+}
+
+GST_END_TEST;
+
+
+GST_START_TEST (test_photography_iface_image_capture_with_caps)
+{
+  GstCaps *caps = gst_caps_from_string ("video/x-raw, width=800, height=600");
+
+  run_single_image_capture_test (NULL, caps);
+  gst_caps_unref (caps);
+}
+
+GST_END_TEST;
+
+
+GST_START_TEST (test_photography_iface_image_capture_with_caps_and_restriction)
+{
+  GstCaps *caps = gst_caps_from_string ("video/x-raw, width=800, height=600");
+
+  /* the source will actually provide an image with 800x800 resolution */
+  GST_TEST_VIDEO_SRC (testsrc)->enable_resolution_restriction = TRUE;
+
+  run_single_image_capture_test (NULL, caps);
+  gst_caps_unref (caps);
+}
+
+GST_END_TEST;
+
+
 typedef struct _TestCaseDef
 {
   const gchar *name;
@@ -1606,6 +1929,7 @@ camerabin_suite (void)
   Suite *s = suite_create ("camerabin");
   gint i;
   TCase *tc_generic = tcase_create ("generic");
+  TCase *tc_phography_iface = tcase_create ("photography-iface");
 
   jpegenc_factory = gst_element_factory_find ("jpegenc");
   if (jpegenc_factory == NULL) {
@@ -1646,6 +1970,26 @@ camerabin_suite (void)
     tcase_add_test (tc_basic, test_image_location_switching);
   }
 
+  /* This is the GstPhotography interface test case. It was added in 0.10
+   * to make it easy for integrating with hardware and providing lower
+   * delays from action to capture.
+   * There is also has a feature in wrappercamerabinsrc that allows
+   * captures with the interface to have a different(higher) resolution than
+   * requested and wrappercamerabinsrc will crop to the requested one.
+   * This doesn't make sense and seems to be very hardware specific but we
+   * can't simply remove it at this point.
+   *
+   * FIXME 2.0: revisit GstPhotography interface and its interaction with
+   * camerabin */
+  suite_add_tcase (s, tc_phography_iface);
+  tcase_add_checked_fixture (tc_phography_iface, setup_test_camerasrc,
+      teardown);
+  tcase_add_test (tc_phography_iface, test_photography_iface_image_capture);
+  tcase_add_test (tc_phography_iface,
+      test_photography_iface_image_capture_with_caps);
+  tcase_add_test (tc_phography_iface,
+      test_photography_iface_image_capture_with_caps_and_restriction);
+
 end:
   return s;
 }