diff options
Diffstat (limited to 'tests/icles/gdkpixbufsink-test.c')
| -rw-r--r-- | tests/icles/gdkpixbufsink-test.c | 358 | 
1 files changed, 358 insertions, 0 deletions
| diff --git a/tests/icles/gdkpixbufsink-test.c b/tests/icles/gdkpixbufsink-test.c new file mode 100644 index 00000000..d23b1828 --- /dev/null +++ b/tests/icles/gdkpixbufsink-test.c @@ -0,0 +1,358 @@ +/* GStreamer interactive test for the gdkpixbufsink element + * Copyright (C) 2008 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 + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include <gst/gst.h> +#include <gtk/gtk.h> + +typedef struct +{ +  GstElement *pipe; +  GstElement *sink; +  gboolean got_video; + +  GtkWidget *win; +  GtkWidget *img; +  GtkWidget *slider; +  GtkWidget *accurate_cb; + +  gboolean accurate;            /* whether to try accurate seeks */ + +  gint64 cur_pos; +  gboolean prerolled; +} AppInfo; + +static void seek_to (AppInfo * info, gdouble percent); + +static GstElement * +create_element (const gchar * factory_name) +{ +  GstElement *element; + +  element = gst_element_factory_make (factory_name, NULL); + +  if (element == NULL) +    g_error ("Failed to create '%s' element", factory_name); + +  return element; +} + +static void +new_decoded_pad (GstElement * dec, GstPad * new_pad, gboolean last, +    AppInfo * info) +{ +  const gchar *sname; +  GstElement *csp, *scale, *filter; +  GstStructure *s; +  GstCaps *caps; +  GstPad *sinkpad; + +  /* already found a video stream? */ +  if (info->got_video) +    return; + +  /* FIXME: is this racy or does decodebin2 make sure caps are always +   * negotiated at this point? */ +  caps = gst_pad_get_caps (new_pad); +  g_return_if_fail (caps != NULL); + +  s = gst_caps_get_structure (caps, 0); +  sname = gst_structure_get_name (s); +  if (!g_str_has_prefix (sname, "video/x-raw-")) +    goto not_video; + +  csp = create_element ("ffmpegcolorspace"); +  scale = create_element ("videoscale"); +  filter = create_element ("capsfilter"); +  info->sink = create_element ("gdkpixbufsink"); +  g_object_set (info->sink, "qos", FALSE, "max-lateness", (gint64) - 1, NULL); + +  gst_bin_add_many (GST_BIN (info->pipe), csp, scale, filter, info->sink, NULL); + +  sinkpad = gst_element_get_static_pad (csp, "sink"); +  if (GST_PAD_LINK_FAILED (gst_pad_link (new_pad, sinkpad))) +    g_error ("Can't link new decoded pad to ffmpegcolorspace's sink pad"); +  gst_object_unref (sinkpad); + +  if (!gst_element_link (csp, scale)) +    g_error ("Can't link ffmpegcolorspace to videoscale"); +  if (!gst_element_link (scale, filter)) +    g_error ("Can't link videoscale to capsfilter"); +  if (!gst_element_link (filter, info->sink)) +    g_error ("Can't link capsfilter to gdkpixbufsink"); + +  gst_element_set_state (info->sink, GST_STATE_PAUSED); +  gst_element_set_state (filter, GST_STATE_PAUSED); +  gst_element_set_state (scale, GST_STATE_PAUSED); +  gst_element_set_state (csp, GST_STATE_PAUSED); + +  info->got_video = TRUE; +  return; + +not_video: +  { +    if (last) { +      g_error ("This file does not contain a video track, or you do not have " +          "the necessary decoder(s) installed"); +    } +  } +} + +static void +bus_message_cb (GstBus * bus, GstMessage * msg, AppInfo * info) +{ +  switch (GST_MESSAGE_TYPE (msg)) { +    case GST_MESSAGE_ASYNC_DONE:{ +      GstFormat fmt = GST_FORMAT_TIME; + +      /* only interested in async-done messages from the top-level pipeline */ +      if (msg->src != GST_OBJECT_CAST (info->pipe)) +        break; + +      if (!info->prerolled) { +        /* make slider visible if it's not visible already */ +        gtk_widget_show (info->slider); + +        /* initial frame is often black, so seek to beginning plus a bit */ +        seek_to (info, 0.001); +        info->prerolled = TRUE; +      } + +      /* update position */ +      if (!gst_element_query_position (info->pipe, &fmt, &info->cur_pos)) +        info->cur_pos = -1; +      break; +    } +    case GST_MESSAGE_ELEMENT:{ +      const GValue *val; +      GdkPixbuf *pixbuf = NULL; + +      /* only interested in element messages from our gdkpixbufsink */ +      if (msg->src != GST_OBJECT_CAST (info->sink)) +        break; + +      /* only interested in these two messages */ +      if (!gst_structure_has_name (msg->structure, "preroll-pixbuf") && +          !gst_structure_has_name (msg->structure, "pixbuf")) { +        break; +      } + +      g_print ("pixbuf\n"); +      val = gst_structure_get_value (msg->structure, "pixbuf"); +      g_return_if_fail (val != NULL); + +      pixbuf = g_value_dup_object (val); +      gtk_image_set_from_pixbuf (GTK_IMAGE (info->img), pixbuf); +      g_object_unref (pixbuf); +      break; +    } +    case GST_MESSAGE_ERROR:{ +      GError *err = NULL; +      gchar *dbg = NULL; + +      gst_message_parse_error (msg, &err, &dbg); +      g_error ("Error: %s\n%s\n", err->message, (dbg) ? dbg : ""); +      g_error_free (err); +      g_free (dbg); +      break; +    } +    default: +      break; +  } +} + +static gboolean +create_pipeline (AppInfo * info, const gchar * filename) +{ +  GstElement *src, *dec; +  GstBus *bus; + +  info->pipe = gst_pipeline_new ("pipeline"); +  src = create_element ("filesrc"); +  g_object_set (src, "location", filename, NULL); + +  dec = create_element ("decodebin2"); + +  gst_bin_add_many (GST_BIN (info->pipe), src, dec, NULL); +  if (!gst_element_link (src, dec)) +    g_error ("Can't link filesrc to decodebin2"); + +  g_signal_connect (dec, "new-decoded-pad", G_CALLBACK (new_decoded_pad), info); + +  /* set up bus */ +  bus = gst_element_get_bus (info->pipe); +  gst_bus_add_signal_watch (bus); +  g_signal_connect (bus, "message", G_CALLBACK (bus_message_cb), info); +  gst_object_unref (bus); + +  return TRUE; +} + +static void +seek_to (AppInfo * info, gdouble percent) +{ +  GstSeekFlags seek_flags; +  GstFormat fmt = GST_FORMAT_TIME; +  gint64 seek_pos, dur = -1; + +  if (!gst_element_query_duration (info->pipe, &fmt, &dur) || dur <= 0) { +    g_printerr ("Could not query duration\n"); +    return; +  } + +  seek_pos = gst_gdouble_to_guint64 (gst_guint64_to_gdouble (dur) * percent); +  g_print ("Seeking to %" GST_TIME_FORMAT ", accurate: %d\n", +      GST_TIME_ARGS (seek_pos), info->accurate); + +  seek_flags = GST_SEEK_FLAG_FLUSH; + +  if (info->accurate) +    seek_flags |= GST_SEEK_FLAG_ACCURATE; +  else +    seek_flags |= GST_SEEK_FLAG_KEY_UNIT; + +  if (!gst_element_seek_simple (info->pipe, GST_FORMAT_TIME, seek_flags, +          seek_pos)) { +    g_printerr ("Seek failed.\n"); +    return; +  } +} + +static void +slider_cb (GtkRange * range, AppInfo * info) +{ +  gdouble val; + +  val = gtk_range_get_value (range); +  seek_to (info, val); +} + +static gchar * +slider_format_value_cb (GtkScale * scale, gdouble value, AppInfo * info) +{ +  gchar s[64]; + +  if (info->cur_pos < 0) +    return g_strdup_printf ("%0.1g%%", value * 100.0); + +  g_snprintf (s, 64, "%" GST_TIME_FORMAT, GST_TIME_ARGS (info->cur_pos)); +  s[10] = '\0'; +  return g_strdup (s); +} + +static void +accurate_toggled_cb (GtkToggleButton * toggle, AppInfo * info) +{ +  info->accurate = gtk_toggle_button_get_active (toggle); +} + +static void +run_gui (const gchar * filename) +{ +  GtkWidget *vbox, *hbox; +  AppInfo *info; + +  info = g_new0 (AppInfo, 1); + +  /* create pipeline */ +  if (!create_pipeline (info, filename)) +    goto done; + +  /* create window */ +  info->win = gtk_window_new (GTK_WINDOW_TOPLEVEL); +  g_signal_connect (info->win, "delete-event", G_CALLBACK (gtk_main_quit), +      NULL); + +  vbox = gtk_vbox_new (FALSE, 6); +  gtk_container_set_border_width (GTK_CONTAINER (vbox), 12); +  gtk_container_add (GTK_CONTAINER (info->win), vbox); + +  info->img = gtk_image_new (); +  gtk_box_pack_start (GTK_BOX (vbox), info->img, FALSE, FALSE, 6); + +  hbox = gtk_hbox_new (FALSE, 6); +  gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 6); + +  info->accurate_cb = gtk_check_button_new_with_label ("accurate seek " +      "(might not work reliably with all demuxers)"); +  gtk_box_pack_start (GTK_BOX (hbox), info->accurate_cb, FALSE, FALSE, 6); +  g_signal_connect (info->accurate_cb, "toggled", +      G_CALLBACK (accurate_toggled_cb), info); + +  info->slider = gtk_hscale_new_with_range (0.0, 1.0, 0.001); +  gtk_box_pack_start (GTK_BOX (vbox), info->slider, FALSE, FALSE, 6); +  g_signal_connect (info->slider, "value-changed", +      G_CALLBACK (slider_cb), info); +  g_signal_connect (info->slider, "format-value", +      G_CALLBACK (slider_format_value_cb), info); + +  /* and go! */ +  gst_element_set_state (info->pipe, GST_STATE_PAUSED); + +  gtk_widget_show_all (info->win); +  gtk_widget_hide (info->slider);       /* hide until we're prerolled */ +  gtk_main (); + +done: + +  g_free (info); +} + +static gchar **filenames = NULL; + +int +main (int argc, char **argv) +{ +  static const GOptionEntry test_goptions[] = { +    {G_OPTION_REMAINING, 0, 0, G_OPTION_ARG_FILENAME_ARRAY, &filenames, NULL}, +    {NULL, '\0', 0, 0, NULL, NULL, NULL} +  }; +  GOptionContext *ctx; +  GError *opt_err = NULL; + +  if (!g_thread_supported ()) +    g_thread_init (NULL); + +  gtk_init (&argc, &argv); + +  /* command line option parsing */ +  ctx = g_option_context_new (" VIDEOFILE"); +  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); +    return -1; +  } + +  if (filenames == NULL || filenames[0] == NULL || filenames[0][0] == '\0') { +    g_printerr ("Please specify a path to a video file\n\n"); +    return -1; +  } + +  run_gui (filenames[0]); + +  g_free (filenames); +  g_option_context_free (ctx); + +  return 0; +} | 
