videocrop-test.c 10.8 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
/* GStreamer interactive test for the videocrop element
 * Copyright (C) 2006 Tim-Philipp Müller <tim centricular net>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public
 * License along with this library; if not, write to the
Tim-Philipp Müller's avatar
Tim-Philipp Müller committed
16 17
 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
 * Boston, MA 02110-1301, USA.
18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
 */

#ifdef HAVE_CONFIG_H
# include "config.h"
#endif

#include <gst/gst.h>

#include <stdlib.h>
#include <math.h>

GST_DEBUG_CATEGORY_STATIC (videocrop_test_debug);
#define GST_CAT_DEFAULT videocrop_test_debug

#define OUT_WIDTH      640
#define OUT_HEIGHT     480
#define TIME_PER_TEST   10      /* seconds each format is tested */
#define FRAMERATE       15      /* frames per second             */

37
#ifndef DEFAULT_VIDEOSINK
38
#define DEFAULT_VIDEOSINK "xvimagesink"
39
#endif
40

41
static gboolean
42
check_bus_for_errors (GstBus * bus, GstClockTime max_wait_time)
43
{
44 45 46
  GstMessage *msg;

  msg = gst_bus_poll (bus, GST_MESSAGE_ERROR, max_wait_time);
47

48 49 50
  if (msg) {
    GError *err = NULL;
    gchar *debug = NULL;
51

52 53 54 55
    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);
56
    g_clear_error (&err);
57 58 59
    g_free (debug);
    gst_message_unref (msg);
  }
60

61
  return (msg != NULL);
62 63 64
}

static void
65
test_with_caps (GstElement * src, GstElement * videocrop, GstCaps * caps)
66
{
67 68
  GstClockTime time_run;
  GstElement *pipeline;
69
  GTimer *timer;
70
  GstBus *bus;
71 72 73
  GstPad *pad;
  guint hcrop;
  guint vcrop;
74 75 76 77

  /* caps must be writable, we can't check that here though */
  g_assert (GST_CAPS_REFCOUNT_VALUE (caps) == 1);

78 79 80
  timer = g_timer_new ();
  vcrop = 0;
  hcrop = 0;
81

82 83 84 85 86 87 88
  pipeline = GST_ELEMENT (gst_element_get_parent (videocrop));
  g_assert (GST_IS_PIPELINE (pipeline));

  /* at this point the pipeline is in PLAYING state; we only want to capture
   * errors resulting from our on-the-fly changing of the filtercaps */
  bus = gst_pipeline_get_bus (GST_PIPELINE (pipeline));

89
  /* pad to block */
90
  pad = gst_element_get_static_pad (src, "src");
91

92 93
  time_run = 0;
  do {
94
    GstClockTime wait_time, waited_for_block;
95

96
    if (check_bus_for_errors (bus, 0))
97
      break;
98

99 100 101 102 103 104 105 106 107
    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
Wim Taymans's avatar
Wim Taymans committed
108 109
     * the processed buffer?)  FIXME should not be needed */
    /* gst_pad_set_blocked (pad, TRUE); */
110
    g_object_set (videocrop, "left", hcrop, "top", vcrop, NULL);
Wim Taymans's avatar
Wim Taymans committed
111
    /* gst_pad_set_blocked (pad, FALSE); */
112 113 114 115 116 117 118 119 120 121

    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);
    }
122

123 124 125
    time_run += wait_time;
  }
  while (time_run < (TIME_PER_TEST * GST_SECOND));
126

127
  g_timer_destroy (timer);
128
  gst_object_unref (bus);
129
  gst_object_unref (pad);
130
  gst_object_unref (pipeline);
131 132 133 134 135 136 137 138 139 140 141 142
}

/* return a list of caps where we only need to set
 * width and height to get fixed caps */
static GList *
video_crop_get_test_caps (GstElement * videocrop)
{
  const GstCaps *allowed_caps;
  GstPad *srcpad;
  GList *list = NULL;
  guint i;

143
  srcpad = gst_element_get_static_pad (videocrop, "src");
144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173
  g_assert (srcpad != NULL);
  allowed_caps = gst_pad_get_pad_template_caps (srcpad);
  g_assert (allowed_caps != NULL);

  for (i = 0; i < gst_caps_get_size (allowed_caps); ++i) {
    GstStructure *new_structure;
    GstCaps *single_caps;

    single_caps = gst_caps_new_empty ();
    new_structure =
        gst_structure_copy (gst_caps_get_structure (allowed_caps, i));
    gst_structure_set (new_structure, "framerate", GST_TYPE_FRACTION,
        FRAMERATE, 1, NULL);
    gst_structure_remove_field (new_structure, "width");
    gst_structure_remove_field (new_structure, "height");
    gst_caps_append_structure (single_caps, new_structure);

    /* should be fixed without width/height */
    g_assert (gst_caps_is_fixed (single_caps));

    list = g_list_prepend (list, single_caps);
  }

  gst_object_unref (srcpad);

  return list;
}

static gchar *opt_videosink_str;        /* NULL */
static gchar *opt_filtercaps_str;       /* NULL */
174
static gboolean opt_with_videoconvert;  /* FALSE */
175 176 177 178 179 180

int
main (int argc, char **argv)
{
  static const GOptionEntry test_goptions[] = {
    {"videosink", '\0', 0, G_OPTION_ARG_STRING, &opt_videosink_str,
181
        "videosink to use (default: " DEFAULT_VIDEOSINK ")", NULL},
182 183
    {"caps", '\0', 0, G_OPTION_ARG_STRING, &opt_filtercaps_str,
        "filter caps to narrow down formats to test", NULL},
184 185 186
    {"with-videoconvert", '\0', 0, G_OPTION_ARG_NONE,
          &opt_with_videoconvert,
          "whether to add an videoconvert element in front of the sink",
187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203
        NULL},
    {NULL, '\0', 0, 0, NULL, NULL, NULL}
  };
  GOptionContext *ctx;
  GError *opt_err = NULL;

  GstElement *pipeline, *src, *filter1, *crop, *scale, *filter2, *csp, *sink;
  GstCaps *filter_caps = NULL;
  GList *caps_list, *l;

  /* command line option parsing */
  ctx = g_option_context_new ("");
  g_option_context_add_group (ctx, gst_init_get_option_group ());
  g_option_context_add_main_entries (ctx, test_goptions, NULL);

  if (!g_option_context_parse (ctx, &argc, &argv, &opt_err)) {
    g_error ("Error parsing command line options: %s", opt_err->message);
204 205
    g_option_context_free (ctx);
    g_clear_error (&opt_err);
206 207
    return -1;
  }
208
  g_option_context_free (ctx);
209 210 211 212 213 214 215 216 217 218 219 220 221 222 223

  GST_DEBUG_CATEGORY_INIT (videocrop_test_debug, "videocroptest", 0, "vctest");

  pipeline = gst_pipeline_new ("pipeline");
  src = gst_element_factory_make ("videotestsrc", "videotestsrc");
  g_assert (src != NULL);
  filter1 = gst_element_factory_make ("capsfilter", "capsfilter1");
  g_assert (filter1 != NULL);
  crop = gst_element_factory_make ("videocrop", "videocrop");
  g_assert (crop != NULL);
  scale = gst_element_factory_make ("videoscale", "videoscale");
  g_assert (scale != NULL);
  filter2 = gst_element_factory_make ("capsfilter", "capsfilter2");
  g_assert (filter2 != NULL);

224 225 226
  if (opt_with_videoconvert) {
    g_print ("Adding videoconvert\n");
    csp = gst_element_factory_make ("videoconvert", "colorspace");
227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249
  } else {
    csp = gst_element_factory_make ("identity", "colorspace");
  }
  g_assert (csp != NULL);

  if (opt_filtercaps_str) {
    filter_caps = gst_caps_from_string (opt_filtercaps_str);
    if (filter_caps == NULL) {
      g_error ("Invalid filter caps string '%s'", opt_filtercaps_str);
    } else {
      g_print ("Using filter caps '%s'\n", opt_filtercaps_str);
    }
  }

  if (opt_videosink_str) {
    g_print ("Trying videosink '%s' ...", opt_videosink_str);
    sink = gst_element_factory_make (opt_videosink_str, "sink");
    g_print ("%s\n", (sink) ? "ok" : "element couldn't be created");
  } else {
    sink = NULL;
  }

  if (sink == NULL) {
250 251
    g_print ("Trying videosink '%s' ...", DEFAULT_VIDEOSINK);
    sink = gst_element_factory_make (DEFAULT_VIDEOSINK, "sink");
252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282
    g_print ("%s\n", (sink) ? "ok" : "element couldn't be created");
  }
  if (sink == NULL) {
    g_print ("Trying videosink '%s' ...", "xvimagesink");
    sink = gst_element_factory_make ("xvimagesink", "sink");
    g_print ("%s\n", (sink) ? "ok" : "element couldn't be created");
  }
  if (sink == NULL) {
    g_print ("Trying videosink '%s' ...", "ximagesink");
    sink = gst_element_factory_make ("ximagesink", "sink");
    g_print ("%s\n", (sink) ? "ok" : "element couldn't be created");
  }

  g_assert (sink != NULL);

  gst_bin_add_many (GST_BIN (pipeline), src, filter1, crop, scale, filter2,
      csp, sink, NULL);

  if (!gst_element_link (src, filter1))
    g_error ("Failed to link videotestsrc to capsfilter1");

  if (!gst_element_link (filter1, crop))
    g_error ("Failed to link capsfilter1 to videocrop");

  if (!gst_element_link (crop, scale))
    g_error ("Failed to link videocrop to videoscale");

  if (!gst_element_link (scale, filter2))
    g_error ("Failed to link videoscale to capsfilter2");

  if (!gst_element_link (filter2, csp))
283
    g_error ("Failed to link capsfilter2 to videoconvert");
284 285

  if (!gst_element_link (csp, sink))
286
    g_error ("Failed to link videoconvert to video sink");
287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334

  caps_list = video_crop_get_test_caps (crop);
  for (l = caps_list; l != NULL; l = l->next) {
    GstStateChangeReturn ret;
    GstCaps *caps, *out_caps;
    gboolean skip = FALSE;
    gchar *s;

    if (filter_caps) {
      GstCaps *icaps;

      icaps = gst_caps_intersect (filter_caps, GST_CAPS (l->data));
      skip = gst_caps_is_empty (icaps);
      gst_caps_unref (icaps);
    }

    /* this is the size of our window (stays fixed) */
    out_caps = gst_caps_copy (GST_CAPS (l->data));
    gst_structure_set (gst_caps_get_structure (out_caps, 0), "width",
        G_TYPE_INT, OUT_WIDTH, "height", G_TYPE_INT, OUT_HEIGHT, NULL);

    g_object_set (filter2, "caps", out_caps, NULL);

    /* filter1 gets these too to prevent videotestsrc from renegotiating */
    g_object_set (filter1, "caps", out_caps, NULL);
    gst_caps_unref (out_caps);

    caps = gst_caps_copy (GST_CAPS (l->data));
    GST_INFO ("testing format: %" GST_PTR_FORMAT, caps);

    s = gst_caps_to_string (caps);

    if (skip) {
      g_print ("Skipping format: %s\n", s);
      g_free (s);
      continue;
    }

    g_print ("Format: %s\n", s);

    caps = gst_caps_make_writable (caps);

    /* FIXME: check return values */
    ret = gst_element_set_state (pipeline, GST_STATE_PLAYING);
    if (ret != GST_STATE_CHANGE_FAILURE) {
      ret = gst_element_get_state (pipeline, NULL, NULL, -1);

      if (ret != GST_STATE_CHANGE_FAILURE) {
335
        test_with_caps (src, crop, caps);
336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356
      } else {
        g_print ("Format: %s not supported (failed to go to PLAYING)\n", s);
      }
    } else {
      g_print ("Format: %s not supported\n", s);
    }

    gst_element_set_state (pipeline, GST_STATE_NULL);

    gst_caps_unref (caps);
    g_free (s);
  }

  g_list_foreach (caps_list, (GFunc) gst_caps_unref, NULL);
  g_list_free (caps_list);

  gst_element_set_state (pipeline, GST_STATE_NULL);
  gst_object_unref (pipeline);

  return 0;
}