gst_plugins_base/tests/examples/seek/scrubby.c
changeset 0 0e761a78d257
equal deleted inserted replaced
-1:000000000000 0:0e761a78d257
       
     1 /*
       
     2 * Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
       
     3 * All rights reserved.
       
     4 * This component and the accompanying materials are made available
       
     5 * under the terms of "Eclipse Public License v1.0"
       
     6 * which accompanies this distribution, and is available
       
     7 * at the URL "http://www.eclipse.org/legal/epl-v10.html".
       
     8 *
       
     9 * Initial Contributors:
       
    10 * Nokia Corporation - initial contribution.
       
    11 *
       
    12 * Contributors:
       
    13 *
       
    14 * Description: 
       
    15 *
       
    16 */
       
    17 #include <stdlib.h>
       
    18 #include <glib.h>
       
    19 #include <gtk/gtk.h>
       
    20 #include <gst/gst.h>
       
    21 #include <string.h>
       
    22 
       
    23 GST_DEBUG_CATEGORY_STATIC (scrubby_debug);
       
    24 #define GST_CAT_DEFAULT (scrubby_debug)
       
    25 
       
    26 static GstElement *pipeline;
       
    27 static gint64 position;
       
    28 static gint64 duration;
       
    29 static GtkAdjustment *adjustment;
       
    30 static GtkWidget *hscale;
       
    31 static GtkAdjustment *sadjustment;
       
    32 static GtkWidget *shscale;
       
    33 static gboolean verbose = FALSE;
       
    34 
       
    35 static guint bus_watch = 0;
       
    36 static guint update_id = 0;
       
    37 static guint changed_id = 0;
       
    38 static guint schanged_id = 0;
       
    39 
       
    40 //#define SOURCE "filesrc"
       
    41 #define SOURCE "gnomevfssrc"
       
    42 #define ASINK "alsasink"
       
    43 //#define ASINK "osssink"
       
    44 #define VSINK "xvimagesink"
       
    45 //#define VSINK "ximagesink"
       
    46 //#define VSINK "aasink"
       
    47 //#define VSINK "cacasink"
       
    48 
       
    49 #define RANGE_PREC 10000
       
    50 #define SEGMENT_LEN 100
       
    51 #define UPDATE_INTERVAL 500
       
    52 
       
    53 static gdouble prev_range = -1.0;
       
    54 static GstClockTime prev_time = GST_CLOCK_TIME_NONE;
       
    55 static gdouble cur_range;
       
    56 static GstClockTime cur_time;
       
    57 static GstClockTimeDiff diff;
       
    58 static gdouble cur_speed = 1.0;
       
    59 
       
    60 typedef struct
       
    61 {
       
    62   const gchar *padname;
       
    63   GstPad *target;
       
    64   GstElement *bin;
       
    65 }
       
    66 dyn_link;
       
    67 
       
    68 static GstElement *
       
    69 gst_element_factory_make_or_warn (gchar * type, gchar * name)
       
    70 {
       
    71   GstElement *element = gst_element_factory_make (type, name);
       
    72 
       
    73   if (!element) {
       
    74     g_warning ("Failed to create element %s of type %s", name, type);
       
    75   }
       
    76 
       
    77   return element;
       
    78 }
       
    79 
       
    80 static void
       
    81 dynamic_link (GstPadTemplate * templ, GstPad * newpad, gpointer data)
       
    82 {
       
    83   dyn_link *connect = (dyn_link *) data;
       
    84 
       
    85   if (connect->padname == NULL ||
       
    86       !strcmp (gst_pad_get_name (newpad), connect->padname)) {
       
    87     if (connect->bin)
       
    88       gst_bin_add (GST_BIN (pipeline), connect->bin);
       
    89     gst_pad_link (newpad, connect->target);
       
    90   }
       
    91 }
       
    92 
       
    93 static void
       
    94 setup_dynamic_link (GstElement * element, const gchar * padname,
       
    95     GstPad * target, GstElement * bin)
       
    96 {
       
    97   dyn_link *connect;
       
    98 
       
    99   connect = g_new0 (dyn_link, 1);
       
   100   connect->padname = g_strdup (padname);
       
   101   connect->target = target;
       
   102   connect->bin = bin;
       
   103 
       
   104   g_signal_connect (G_OBJECT (element), "pad-added", G_CALLBACK (dynamic_link),
       
   105       connect);
       
   106 }
       
   107 
       
   108 static GstElement *
       
   109 make_wav_pipeline (const gchar * location)
       
   110 {
       
   111   GstElement *pipeline;
       
   112   GstElement *src, *decoder, *audiosink;
       
   113 
       
   114   pipeline = gst_pipeline_new ("app");
       
   115 
       
   116   src = gst_element_factory_make_or_warn (SOURCE, "src");
       
   117   decoder = gst_element_factory_make_or_warn ("wavparse", "decoder");
       
   118   audiosink = gst_element_factory_make_or_warn (ASINK, "sink");
       
   119 
       
   120   g_object_set (G_OBJECT (src), "location", location, NULL);
       
   121 
       
   122   gst_bin_add (GST_BIN (pipeline), src);
       
   123   gst_bin_add (GST_BIN (pipeline), decoder);
       
   124   gst_bin_add (GST_BIN (pipeline), audiosink);
       
   125 
       
   126   gst_element_link (src, decoder);
       
   127 
       
   128   setup_dynamic_link (decoder, "src", gst_element_get_pad (audiosink, "sink"),
       
   129       NULL);
       
   130 
       
   131   return pipeline;
       
   132 }
       
   133 
       
   134 static GstElement *
       
   135 make_playerbin_pipeline (const gchar * location)
       
   136 {
       
   137   GstElement *player;
       
   138 
       
   139   player = gst_element_factory_make ("playbin", "player");
       
   140   g_assert (player);
       
   141 
       
   142   g_object_set (G_OBJECT (player), "uri", location, NULL);
       
   143 
       
   144   return player;
       
   145 }
       
   146 
       
   147 static gchar *
       
   148 format_value (GtkScale * scale, gdouble value)
       
   149 {
       
   150   gint64 real;
       
   151   gint64 seconds;
       
   152   gint64 subseconds;
       
   153 
       
   154   real = value * duration / RANGE_PREC;
       
   155   seconds = (gint64) real / GST_SECOND;
       
   156   subseconds = (gint64) real / (GST_SECOND / RANGE_PREC);
       
   157 
       
   158   return g_strdup_printf ("%02" G_GINT64_FORMAT ":%02" G_GINT64_FORMAT ":%02"
       
   159       G_GINT64_FORMAT, seconds / 60, seconds % 60, subseconds % 100);
       
   160 }
       
   161 
       
   162 static gboolean
       
   163 update_scale (gpointer data)
       
   164 {
       
   165   GstFormat format;
       
   166 
       
   167   position = 0;
       
   168   duration = 0;
       
   169 
       
   170   format = GST_FORMAT_TIME;
       
   171 
       
   172   gst_element_query_position (pipeline, &format, &position);
       
   173   gst_element_query_duration (pipeline, &format, &duration);
       
   174 
       
   175   if (position >= duration)
       
   176     duration = position;
       
   177 
       
   178   if (duration > 0) {
       
   179     gtk_adjustment_set_value (adjustment,
       
   180         position * (gdouble) RANGE_PREC / duration);
       
   181     gtk_widget_queue_draw (hscale);
       
   182   }
       
   183 
       
   184   return TRUE;
       
   185 }
       
   186 
       
   187 static void
       
   188 speed_cb (GtkWidget * widget)
       
   189 {
       
   190   GstEvent *s_event;
       
   191   gboolean res;
       
   192 
       
   193   GST_DEBUG ("speed change");
       
   194   cur_speed = gtk_range_get_value (GTK_RANGE (widget));
       
   195 
       
   196   if (cur_speed == 0.0)
       
   197     return;
       
   198 
       
   199   s_event = gst_event_new_seek (cur_speed,
       
   200       GST_FORMAT_TIME, 0, GST_SEEK_TYPE_NONE, -1, GST_SEEK_TYPE_NONE, -1);
       
   201 
       
   202   res = gst_element_send_event (pipeline, s_event);
       
   203   if (!res)
       
   204     g_print ("speed change failed\n");
       
   205 }
       
   206 
       
   207 static gboolean do_seek (GtkWidget * widget, gboolean flush, gboolean segment);
       
   208 
       
   209 static void
       
   210 seek_cb (GtkWidget * widget)
       
   211 {
       
   212   if (changed_id) {
       
   213     GST_DEBUG ("seek because of slider move");
       
   214 
       
   215     if (do_seek (widget, TRUE, TRUE)) {
       
   216       g_source_remove (changed_id);
       
   217       changed_id = 0;
       
   218     }
       
   219   }
       
   220 }
       
   221 
       
   222 static gboolean
       
   223 do_seek (GtkWidget * widget, gboolean flush, gboolean segment)
       
   224 {
       
   225   gint64 start, stop;
       
   226   gboolean res = FALSE;
       
   227   GstEvent *s_event;
       
   228   gdouble rate;
       
   229   GTimeVal tv;
       
   230   gboolean valid;
       
   231   gdouble new_range;
       
   232 
       
   233   if (segment)
       
   234     new_range = gtk_range_get_value (GTK_RANGE (widget));
       
   235   else {
       
   236     new_range = (gdouble) RANGE_PREC;
       
   237     cur_time = -1;
       
   238   }
       
   239 
       
   240   valid = prev_time != -1;
       
   241 
       
   242   GST_DEBUG ("flush %d, segment %d, valid %d", flush, segment, valid);
       
   243 
       
   244   if (new_range == cur_range)
       
   245     return FALSE;
       
   246 
       
   247   prev_time = cur_time;
       
   248   prev_range = cur_range;
       
   249 
       
   250   cur_range = new_range;
       
   251 
       
   252   g_get_current_time (&tv);
       
   253   cur_time = GST_TIMEVAL_TO_TIME (tv);
       
   254 
       
   255   if (!valid)
       
   256     return FALSE;
       
   257 
       
   258   GST_DEBUG ("cur:  %lf, %" GST_TIME_FORMAT, cur_range,
       
   259       GST_TIME_ARGS (cur_time));
       
   260   GST_DEBUG ("prev: %lf, %" GST_TIME_FORMAT, prev_range,
       
   261       GST_TIME_ARGS (prev_time));
       
   262 
       
   263   diff = cur_time - prev_time;
       
   264 
       
   265   GST_DEBUG ("diff: %" GST_TIME_FORMAT, GST_TIME_ARGS (diff));
       
   266 
       
   267   start = prev_range * duration / RANGE_PREC;
       
   268   /* play 50 milliseconds */
       
   269   stop = segment ? cur_range * duration / RANGE_PREC : duration;
       
   270 
       
   271   if (start == stop)
       
   272     return FALSE;
       
   273 
       
   274   if (segment)
       
   275     rate = (stop - start) / (gdouble) diff;
       
   276   else
       
   277     rate = cur_speed;
       
   278 
       
   279   if (start > stop) {
       
   280     gint64 tmp;
       
   281 
       
   282     tmp = start;
       
   283     start = stop;
       
   284     stop = tmp;
       
   285   }
       
   286 
       
   287   if (rate == 0.0)
       
   288     return TRUE;
       
   289 
       
   290   GST_DEBUG ("seek to %" GST_TIME_FORMAT " -- %" GST_TIME_FORMAT ", rate %lf"
       
   291       " on element %s",
       
   292       GST_TIME_ARGS (start), GST_TIME_ARGS (stop), rate,
       
   293       GST_ELEMENT_NAME (pipeline));
       
   294 
       
   295   s_event = gst_event_new_seek (rate,
       
   296       GST_FORMAT_TIME,
       
   297       (flush ? GST_SEEK_FLAG_FLUSH : 0) |
       
   298       (segment ? GST_SEEK_FLAG_SEGMENT : 0),
       
   299       GST_SEEK_TYPE_SET, start, GST_SEEK_TYPE_SET, stop);
       
   300 
       
   301   res = gst_element_send_event (pipeline, s_event);
       
   302   if (!res)
       
   303     g_print ("seek failed\n");
       
   304 
       
   305   gst_element_get_state (pipeline, NULL, NULL, GST_CLOCK_TIME_NONE);
       
   306 
       
   307   return TRUE;
       
   308 }
       
   309 
       
   310 static gboolean
       
   311 start_seek (GtkWidget * widget, GdkEventButton * event, gpointer user_data)
       
   312 {
       
   313   if (update_id) {
       
   314     g_source_remove (update_id);
       
   315     update_id = 0;
       
   316   }
       
   317 
       
   318   if (changed_id == 0) {
       
   319     changed_id = gtk_signal_connect (GTK_OBJECT (hscale),
       
   320         "value_changed", G_CALLBACK (seek_cb), pipeline);
       
   321   }
       
   322 
       
   323   GST_DEBUG ("start seek");
       
   324 
       
   325   return FALSE;
       
   326 }
       
   327 
       
   328 static gboolean
       
   329 stop_seek (GtkWidget * widget, gpointer user_data)
       
   330 {
       
   331   update_id =
       
   332       g_timeout_add (UPDATE_INTERVAL, (GtkFunction) update_scale, pipeline);
       
   333 
       
   334   GST_DEBUG ("stop seek");
       
   335 
       
   336   if (changed_id) {
       
   337     g_source_remove (changed_id);
       
   338     changed_id = 0;
       
   339   }
       
   340 
       
   341   do_seek (hscale, FALSE, FALSE);
       
   342 
       
   343   return FALSE;
       
   344 }
       
   345 
       
   346 static void
       
   347 play_cb (GtkButton * button, gpointer data)
       
   348 {
       
   349   GstState state;
       
   350 
       
   351   gst_element_get_state (pipeline, &state, NULL, GST_CLOCK_TIME_NONE);
       
   352   if (state != GST_STATE_PLAYING) {
       
   353     g_print ("PLAY pipeline\n");
       
   354     gst_element_set_state (pipeline, GST_STATE_PAUSED);
       
   355     gst_element_get_state (pipeline, NULL, NULL, GST_CLOCK_TIME_NONE);
       
   356     gst_element_set_state (pipeline, GST_STATE_PLAYING);
       
   357     update_id =
       
   358         g_timeout_add (UPDATE_INTERVAL, (GtkFunction) update_scale, pipeline);
       
   359   }
       
   360 }
       
   361 
       
   362 static void
       
   363 pause_cb (GtkButton * button, gpointer data)
       
   364 {
       
   365   GstState state;
       
   366 
       
   367   gst_element_get_state (pipeline, &state, NULL, GST_CLOCK_TIME_NONE);
       
   368   if (state != GST_STATE_PAUSED) {
       
   369     g_print ("PAUSE pipeline\n");
       
   370     gst_element_set_state (pipeline, GST_STATE_PAUSED);
       
   371     g_source_remove (update_id);
       
   372   }
       
   373 }
       
   374 
       
   375 static void
       
   376 stop_cb (GtkButton * button, gpointer data)
       
   377 {
       
   378   GstState state;
       
   379 
       
   380   gst_element_get_state (pipeline, &state, NULL, GST_CLOCK_TIME_NONE);
       
   381   if (state != GST_STATE_READY) {
       
   382     g_print ("READY pipeline\n");
       
   383     gst_element_set_state (pipeline, GST_STATE_READY);
       
   384     /* position and speed return to their default values */
       
   385     gtk_adjustment_set_value (adjustment, 0.0);
       
   386     gtk_adjustment_set_value (sadjustment, 1.0);
       
   387     g_source_remove (update_id);
       
   388   }
       
   389 }
       
   390 
       
   391 static void
       
   392 print_message (GstMessage * message)
       
   393 {
       
   394   const GstStructure *s;
       
   395 
       
   396   s = gst_message_get_structure (message);
       
   397   g_print ("Got Message from element \"%s\"\n",
       
   398       GST_STR_NULL (GST_ELEMENT_NAME (GST_MESSAGE_SRC (message))));
       
   399 
       
   400   if (s) {
       
   401     gchar *sstr;
       
   402 
       
   403     sstr = gst_structure_to_string (s);
       
   404     g_print ("%s\n", sstr);
       
   405     g_free (sstr);
       
   406   }
       
   407 }
       
   408 
       
   409 static gboolean
       
   410 bus_message (GstBus * bus, GstMessage * message, gpointer data)
       
   411 {
       
   412   switch (GST_MESSAGE_TYPE (message)) {
       
   413     case GST_MESSAGE_EOS:
       
   414       g_print ("EOS\n");
       
   415       break;
       
   416     case GST_MESSAGE_ERROR:
       
   417     case GST_MESSAGE_WARNING:
       
   418       print_message (message);
       
   419       break;
       
   420     case GST_MESSAGE_SEGMENT_START:
       
   421       break;
       
   422     case GST_MESSAGE_SEGMENT_DONE:
       
   423       GST_DEBUG ("segment_done, doing next seek");
       
   424       if (!do_seek (hscale, FALSE, update_id == 0)) {
       
   425         if (changed_id == 0) {
       
   426           changed_id = gtk_signal_connect (GTK_OBJECT (hscale),
       
   427               "value_changed", G_CALLBACK (seek_cb), pipeline);
       
   428         }
       
   429       }
       
   430       break;
       
   431     default:
       
   432       break;
       
   433   }
       
   434 
       
   435   return TRUE;
       
   436 }
       
   437 
       
   438 typedef struct
       
   439 {
       
   440   gchar *name;
       
   441   GstElement *(*func) (const gchar * location);
       
   442 }
       
   443 Pipeline;
       
   444 
       
   445 static Pipeline pipelines[] = {
       
   446   {"wav", make_wav_pipeline},
       
   447   {"playerbin", make_playerbin_pipeline},
       
   448   {NULL, NULL},
       
   449 };
       
   450 
       
   451 #define NUM_TYPES       ((sizeof (pipelines) / sizeof (Pipeline)) - 1)
       
   452 
       
   453 static void
       
   454 print_usage (int argc, char **argv)
       
   455 {
       
   456   gint i;
       
   457 
       
   458   g_print ("usage: %s <type> <filename>\n", argv[0]);
       
   459   g_print ("   possible types:\n");
       
   460 
       
   461   for (i = 0; i < NUM_TYPES; i++) {
       
   462     g_print ("     %d = %s\n", i, pipelines[i].name);
       
   463   }
       
   464 }
       
   465 
       
   466 int
       
   467 main (int argc, char **argv)
       
   468 {
       
   469   GtkWidget *window, *hbox, *vbox, *play_button, *pause_button, *stop_button;
       
   470   GstBus *bus;
       
   471   GOptionEntry options[] = {
       
   472     {"verbose", 'v', 0, G_OPTION_ARG_NONE, &verbose,
       
   473         "Verbose properties", NULL},
       
   474     {NULL}
       
   475   };
       
   476   gint type;
       
   477   GOptionContext *ctx;
       
   478   GError *err = NULL;
       
   479 
       
   480   if (!g_thread_supported ())
       
   481     g_thread_init (NULL);
       
   482 
       
   483   ctx = g_option_context_new ("seek");
       
   484   g_option_context_add_main_entries (ctx, options, NULL);
       
   485   g_option_context_add_group (ctx, gst_init_get_option_group ());
       
   486 
       
   487   if (!g_option_context_parse (ctx, &argc, &argv, &err)) {
       
   488     g_print ("Error initializing: %s\n", err->message);
       
   489     exit (1);
       
   490   }
       
   491 
       
   492   GST_DEBUG_CATEGORY_INIT (scrubby_debug, "scrubby", 0, "scrubby example");
       
   493 
       
   494   gtk_init (&argc, &argv);
       
   495 
       
   496   if (argc != 3) {
       
   497     print_usage (argc, argv);
       
   498     exit (-1);
       
   499   }
       
   500 
       
   501   type = atoi (argv[1]);
       
   502 
       
   503   if (type < 0 || type >= NUM_TYPES) {
       
   504     print_usage (argc, argv);
       
   505     exit (-1);
       
   506   }
       
   507 
       
   508   pipeline = pipelines[type].func (argv[2]);
       
   509   g_assert (pipeline);
       
   510 
       
   511   /* initialize gui elements ... */
       
   512   window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
       
   513   hbox = gtk_hbox_new (FALSE, 0);
       
   514   vbox = gtk_vbox_new (FALSE, 0);
       
   515   play_button = gtk_button_new_with_label ("play");
       
   516   pause_button = gtk_button_new_with_label ("pause");
       
   517   stop_button = gtk_button_new_with_label ("stop");
       
   518 
       
   519   adjustment =
       
   520       GTK_ADJUSTMENT (gtk_adjustment_new (0.0, 0.0, (gdouble) RANGE_PREC, 0.1,
       
   521           1.0, 1.0));
       
   522   hscale = gtk_hscale_new (adjustment);
       
   523   gtk_scale_set_digits (GTK_SCALE (hscale), 2);
       
   524   gtk_range_set_update_policy (GTK_RANGE (hscale), GTK_UPDATE_CONTINUOUS);
       
   525 
       
   526   sadjustment =
       
   527       GTK_ADJUSTMENT (gtk_adjustment_new (1.0, 0.0, 5.0, 0.1, 1.0, 0.0));
       
   528   shscale = gtk_hscale_new (sadjustment);
       
   529   gtk_scale_set_digits (GTK_SCALE (shscale), 2);
       
   530   gtk_range_set_update_policy (GTK_RANGE (shscale), GTK_UPDATE_CONTINUOUS);
       
   531 
       
   532   schanged_id = gtk_signal_connect (GTK_OBJECT (shscale),
       
   533       "value_changed", G_CALLBACK (speed_cb), pipeline);
       
   534 
       
   535   gtk_signal_connect (GTK_OBJECT (hscale),
       
   536       "button_press_event", G_CALLBACK (start_seek), pipeline);
       
   537   gtk_signal_connect (GTK_OBJECT (hscale),
       
   538       "button_release_event", G_CALLBACK (stop_seek), pipeline);
       
   539   gtk_signal_connect (GTK_OBJECT (hscale),
       
   540       "format_value", G_CALLBACK (format_value), pipeline);
       
   541 
       
   542   /* do the packing stuff ... */
       
   543   gtk_window_set_default_size (GTK_WINDOW (window), 96, 96);
       
   544   gtk_container_add (GTK_CONTAINER (window), vbox);
       
   545   gtk_container_add (GTK_CONTAINER (vbox), hbox);
       
   546   gtk_box_pack_start (GTK_BOX (hbox), play_button, FALSE, FALSE, 2);
       
   547   gtk_box_pack_start (GTK_BOX (hbox), pause_button, FALSE, FALSE, 2);
       
   548   gtk_box_pack_start (GTK_BOX (hbox), stop_button, FALSE, FALSE, 2);
       
   549   gtk_box_pack_start (GTK_BOX (vbox), hscale, TRUE, TRUE, 2);
       
   550   gtk_box_pack_start (GTK_BOX (vbox), shscale, TRUE, TRUE, 2);
       
   551 
       
   552   /* connect things ... */
       
   553   g_signal_connect (G_OBJECT (play_button), "clicked", G_CALLBACK (play_cb),
       
   554       pipeline);
       
   555   g_signal_connect (G_OBJECT (pause_button), "clicked", G_CALLBACK (pause_cb),
       
   556       pipeline);
       
   557   g_signal_connect (G_OBJECT (stop_button), "clicked", G_CALLBACK (stop_cb),
       
   558       pipeline);
       
   559   g_signal_connect (G_OBJECT (window), "delete_event", gtk_main_quit, NULL);
       
   560 
       
   561   /* show the gui. */
       
   562   gtk_widget_show_all (window);
       
   563 
       
   564   if (verbose) {
       
   565     g_signal_connect (pipeline, "deep_notify",
       
   566         G_CALLBACK (gst_object_default_deep_notify), NULL);
       
   567   }
       
   568   bus = gst_pipeline_get_bus (GST_PIPELINE (pipeline));
       
   569   g_assert (bus);
       
   570 
       
   571   bus_watch = gst_bus_add_watch_full (bus,
       
   572       G_PRIORITY_HIGH, bus_message, pipeline, NULL);
       
   573 
       
   574   gtk_main ();
       
   575 
       
   576   g_print ("NULL pipeline\n");
       
   577   gst_element_set_state (pipeline, GST_STATE_NULL);
       
   578 
       
   579   g_print ("free pipeline\n");
       
   580   gst_object_unref (pipeline);
       
   581 
       
   582   return 0;
       
   583 }