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