seek.c 63.1 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
/* GStreamer
 *
 * seek.c: seeking sample application
 *
 * Copyright (C) 2005 Wim Taymans <wim@fluendo.com>
 *               2006 Stefan Kost <ensonic@users.sf.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.
 */
23 24 25 26 27

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

28
#include <stdlib.h>
29
#include <math.h>
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
30 31 32 33 34
#include <glib.h>
#include <gtk/gtk.h>
#include <gst/gst.h>
#include <string.h>

35 36
#include <gdk/gdk.h>
#if defined (GDK_WINDOWING_X11)
37
#include <gdk/gdkx.h>
38
#elif defined (GDK_WINDOWING_WIN32)
39
#include <gdk/gdkwin32.h>
40
#endif
41

42 43
#include <gst/interfaces/xoverlay.h>

44 45 46 47 48 49 50
#if (!GTK_CHECK_VERSION(2, 23, 0) || GTK_CHECK_VERSION(2, 90, 0)) && !GTK_CHECK_VERSION(2, 91, 1)
#define gtk_combo_box_text_new gtk_combo_box_new_text
#define gtk_combo_box_text_append_text gtk_combo_box_append_text
#define gtk_combo_box_text_remove gtk_combo_box_remove_text
#define GTK_COMBO_BOX_TEXT GTK_COMBO_BOX
#endif

51
GST_DEBUG_CATEGORY_STATIC (seek_debug);
52 53
#define GST_CAT_DEFAULT (seek_debug)

54 55 56 57 58 59 60 61
#if !GTK_CHECK_VERSION (2, 17, 7)
static void
gtk_widget_get_allocation (GtkWidget * w, GtkAllocation * a)
{
  *a = w->allocation;
}
#endif

62 63
/* configuration */

64
#define SOURCE "filesrc"
65

66 67
static gchar *opt_audiosink_str;        /* NULL */
static gchar *opt_videosink_str;        /* NULL */
68

69
#define FILL_INTERVAL 100
70 71
//#define UPDATE_INTERVAL 500
//#define UPDATE_INTERVAL 100
72
#define UPDATE_INTERVAL 40
73 74 75 76 77 78 79

/* number of milliseconds to play for after a seek */
#define SCRUB_TIME 100

/* timeout for gst_element_get_state() after a seek */
#define SEEK_TIMEOUT 40 * GST_MSECOND

80 81
#define DEFAULT_VIDEO_HEIGHT 300

82 83 84
/* the state to go to when stop is pressed */
#define STOP_STATE      GST_STATE_READY

85
#define N_GRAD 1000.0
86

87
static GList *seekable_elements = NULL;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
88

89 90
static gboolean accurate_seek = FALSE;
static gboolean keyframe_seek = FALSE;
91
static gboolean loop_seek = FALSE;
92
static gboolean flush_seek = TRUE;
93 94
static gboolean scrub = TRUE;
static gboolean play_scrub = FALSE;
95
static gboolean skip_seek = FALSE;
96
static gdouble rate = 1.0;
97

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
98
static GstElement *pipeline;
99 100
static gint pipeline_type;
static const gchar *pipeline_spec;
101 102
static gint64 position = -1;
static gint64 duration = -1;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
103
static GtkAdjustment *adjustment;
104 105
static GtkWidget *hscale, *statusbar;
static guint status_id = 0;
106
static gboolean stats = FALSE;
107
static gboolean verbose = FALSE;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
108

109 110
static gboolean is_live = FALSE;
static gboolean buffering = FALSE;
111 112
static GstBufferingMode mode;
static gint64 buffering_left;
113
static GstState state = GST_STATE_NULL;
114
static guint update_id = 0;
115 116
static guint seek_timeout_id = 0;
static gulong changed_id;
117
static guint fill_id = 0;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
118

119
static gint n_video = 0, n_audio = 0, n_text = 0;
120
static gboolean need_streams = TRUE;
121
static GtkWidget *video_combo, *audio_combo, *text_combo, *vis_combo;
122
static GtkWidget *vis_checkbox, *video_checkbox, *audio_checkbox;
123
static GtkWidget *text_checkbox, *mute_checkbox, *volume_spinbutton;
124
static GtkWidget *skip_checkbox, *video_window, *download_checkbox;
125
static GtkWidget *buffer_checkbox, *rate_spinbutton;
Wim Taymans's avatar
Wim Taymans committed
126 127

static GStaticMutex state_mutex = G_STATIC_MUTEX_INIT;
128

129
static GtkWidget *format_combo, *step_amount_spinbutton, *step_rate_spinbutton;
Wim Taymans's avatar
Wim Taymans committed
130 131 132
static GtkWidget *shuttle_checkbox, *step_button;
static GtkWidget *shuttle_hscale;
static GtkAdjustment *shuttle_adjustment;
133 134

static GList *paths = NULL, *l = NULL;
135

136 137 138 139 140 141 142 143 144
/* we keep an array of the visualisation entries so that we can easily switch
 * with the combo box index. */
typedef struct
{
  GstElementFactory *factory;
} VisEntry;

static GArray *vis_entries;

145
static void clear_streams (GstElement * pipeline);
146 147
static void volume_notify_cb (GstElement * pipeline, GParamSpec * arg,
    gpointer user_dat);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
148

149
/* pipeline construction */
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
150 151 152

typedef struct
{
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
153 154 155
  const gchar *padname;
  GstPad *target;
  GstElement *bin;
156 157
}
dyn_link;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
158

159
static GstElement *
160
gst_element_factory_make_or_warn (const gchar * type, const gchar * name)
161 162 163 164
{
  GstElement *element = gst_element_factory_make (type, name);

  if (!element) {
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
165
    g_warning ("Failed to create element %s of type %s", name, type);
166 167 168 169 170
  }

  return element;
}

171 172 173 174 175 176
static void
playerbin_set_uri (GstElement * player, const gchar * location)
{
  gchar *uri;

  /* Add "file://" prefix for convenience */
177 178 179
  if (g_str_has_prefix (location, "/") || !gst_uri_is_valid (location)) {
    uri = gst_filename_to_uri (location, NULL);
    g_print ("Setting URI: %s\n", uri);
180 181 182
    g_object_set (G_OBJECT (player), "uri", uri, NULL);
    g_free (uri);
  } else {
183
    g_print ("Setting URI: %s\n", location);
184 185 186 187
    g_object_set (G_OBJECT (player), "uri", location, NULL);
  }
}

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
188
static GstElement *
189
construct_playbin (const gchar * name, const gchar * location)
Wim Taymans's avatar
Wim Taymans committed
190
{
191
  GstElement *player;
192
  GstElement *avsink;
193

194
  player = gst_element_factory_make (name, "player");
195 196
  g_assert (player);

197
  playerbin_set_uri (player, location);
198 199 200

  seekable_elements = g_list_prepend (seekable_elements, player);

201 202 203 204 205 206 207 208
  avsink = gst_element_factory_make_or_warn (opt_audiosink_str, "a_sink");
  if (avsink)
    g_object_set (player, "audio-sink", avsink, NULL);

  avsink = gst_element_factory_make_or_warn (opt_videosink_str, "v_sink");
  if (avsink)
    g_object_set (player, "video-sink", avsink, NULL);

209
  return player;
Wim Taymans's avatar
Wim Taymans committed
210 211
}

212
static GstElement *
213
make_playbin_pipeline (const gchar * location)
214
{
215
  GstElement *pipeline = construct_playbin ("playbin", location);
216

217
  /* FIXME: this is not triggered, playbin is not forwarding it from the sink */
218 219 220
  g_signal_connect (pipeline, "notify::volume", G_CALLBACK (volume_notify_cb),
      NULL);
  return pipeline;
221 222
}

223
#ifndef GST_DISABLE_PARSE
224 225 226 227
static GstElement *
make_parselaunch_pipeline (const gchar * description)
{
  GstElement *pipeline;
228
  GError *error = NULL;
229 230 231 232 233 234 235

  pipeline = gst_parse_launch (description, &error);

  seekable_elements = g_list_prepend (seekable_elements, pipeline);

  return pipeline;
}
236
#endif
237

238 239
typedef struct
{
240
  const gchar *name;
241 242 243 244 245
  GstElement *(*func) (const gchar * location);
}
Pipeline;

static Pipeline pipelines[] = {
246
  {"playbin", make_playbin_pipeline},
247 248 249 250 251 252 253 254 255 256
#ifndef GST_DISABLE_PARSE
  {"parse-launch", make_parselaunch_pipeline},
#endif
  {NULL, NULL},
};

#define NUM_TYPES       ((sizeof (pipelines) / sizeof (Pipeline)) - 1)

/* ui callbacks and helpers */

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
257 258
static gchar *
format_value (GtkScale * scale, gdouble value)
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
259
{
260 261 262 263
  gint64 real;
  gint64 seconds;
  gint64 subseconds;

Wim Taymans's avatar
Wim Taymans committed
264
  real = value * duration / N_GRAD;
265
  seconds = (gint64) real / GST_SECOND;
Wim Taymans's avatar
Wim Taymans committed
266
  subseconds = (gint64) real / (GST_SECOND / N_GRAD);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
267

268 269
  return g_strdup_printf ("%02" G_GINT64_FORMAT ":%02" G_GINT64_FORMAT ":%02"
      G_GINT64_FORMAT, seconds / 60, seconds % 60, subseconds % 100);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
270 271
}

Wim Taymans's avatar
Wim Taymans committed
272 273 274 275 276 277 278

static gchar *
shuttle_format_value (GtkScale * scale, gdouble value)
{
  return g_strdup_printf ("%0.*g", gtk_scale_get_digits (scale), value);
}

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
279 280 281 282
typedef struct
{
  const gchar *name;
  const GstFormat format;
283 284
}
seek_format;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
285

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
286 287 288 289 290 291
static seek_format seek_formats[] = {
  {"tim", GST_FORMAT_TIME},
  {"byt", GST_FORMAT_BYTES},
  {"buf", GST_FORMAT_BUFFERS},
  {"def", GST_FORMAT_DEFAULT},
  {NULL, 0},
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
292 293 294
};

G_GNUC_UNUSED static void
295
query_positions_elems (void)
296 297 298 299 300 301 302 303 304
{
  GList *walk = seekable_elements;

  while (walk) {
    GstElement *element = GST_ELEMENT (walk->data);
    gint i = 0;

    g_print ("positions %8.8s: ", GST_ELEMENT_NAME (element));
    while (seek_formats[i].name) {
Wim Taymans's avatar
Wim Taymans committed
305
      gint64 position, total;
306 307 308
      GstFormat format;

      format = seek_formats[i].format;
Wim Taymans's avatar
Wim Taymans committed
309

Wim Taymans's avatar
Wim Taymans committed
310 311
      if (gst_element_query_position (element, &format, &position) &&
          gst_element_query_duration (element, &format, &total)) {
Wim Taymans's avatar
Wim Taymans committed
312 313
        g_print ("%s %13" G_GINT64_FORMAT " / %13" G_GINT64_FORMAT " | ",
            seek_formats[i].name, position, total);
314
      } else {
Wim Taymans's avatar
Wim Taymans committed
315 316
        g_print ("%s %13.13s / %13.13s | ", seek_formats[i].name, "*NA*",
            "*NA*");
317 318 319 320 321 322 323 324 325
      }
      i++;
    }
    g_print (" %s\n", GST_ELEMENT_NAME (element));

    walk = g_list_next (walk);
  }
}

326 327 328 329
static gboolean start_seek (GtkWidget * widget, GdkEventButton * event,
    gpointer user_data);
static gboolean stop_seek (GtkWidget * widget, GdkEventButton * event,
    gpointer user_data);
330
static void seek_cb (GtkWidget * widget);
331 332 333 334

static void
set_scale (gdouble value)
{
335 336 337 338
  g_signal_handlers_block_by_func (hscale, (void *) start_seek,
      (void *) pipeline);
  g_signal_handlers_block_by_func (hscale, (void *) stop_seek,
      (void *) pipeline);
339
  g_signal_handlers_block_by_func (hscale, (void *) seek_cb, (void *) pipeline);
340
  gtk_adjustment_set_value (adjustment, value);
341 342 343 344
  g_signal_handlers_unblock_by_func (hscale, (void *) start_seek,
      (void *) pipeline);
  g_signal_handlers_unblock_by_func (hscale, (void *) stop_seek,
      (void *) pipeline);
345 346
  g_signal_handlers_unblock_by_func (hscale, (void *) seek_cb,
      (void *) pipeline);
347 348 349
  gtk_widget_queue_draw (hscale);
}

350 351 352
static gboolean
update_fill (gpointer data)
{
353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386
  if (seekable_elements) {
    GstElement *element = GST_ELEMENT (seekable_elements->data);
    GstQuery *query;

    query = gst_query_new_buffering (GST_FORMAT_PERCENT);
    if (gst_element_query (element, query)) {
      gint64 start, stop, buffering_total;
      GstFormat format;
      gdouble fill;
      gboolean busy;
      gint percent;
      GstBufferingMode mode;
      gint avg_in, avg_out;
      gint64 buffering_left;

      gst_query_parse_buffering_percent (query, &busy, &percent);
      gst_query_parse_buffering_range (query, &format, &start, &stop,
          &buffering_total);
      gst_query_parse_buffering_stats (query, &mode, &avg_in, &avg_out,
          &buffering_left);

      /* note that we could start the playback when buffering_left < remaining
       * playback time */
      GST_DEBUG ("buffering total %" G_GINT64_FORMAT " ms, left %"
          G_GINT64_FORMAT " ms", buffering_total, buffering_left);
      GST_DEBUG ("start %" G_GINT64_FORMAT ", stop %" G_GINT64_FORMAT,
          start, stop);

      if (stop != -1)
        fill = N_GRAD * stop / GST_FORMAT_PERCENT_MAX;
      else
        fill = N_GRAD;

      gtk_range_set_fill_level (GTK_RANGE (hscale), fill);
387
    }
388
    gst_query_unref (query);
389 390 391 392
  }
  return TRUE;
}

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
393
static gboolean
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
394
update_scale (gpointer data)
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
395
{
396
  GstFormat format = GST_FORMAT_TIME;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
397

398 399
  //position = 0;
  //duration = 0;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
400

401 402
  if (seekable_elements) {
    GstElement *element = GST_ELEMENT (seekable_elements->data);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
403

404 405
    gst_element_query_position (element, &format, &position);
    gst_element_query_duration (element, &format, &duration);
406
  }
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
407

408
  if (stats) {
409
    query_positions_elems ();
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
410
  }
411

412 413
  if (position >= duration)
    duration = position;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
414 415

  if (duration > 0) {
416
    set_scale (position * N_GRAD / duration);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
417 418
  }

419 420 421 422 423
  /* FIXME: see make_playerbin2_pipeline() and volume_notify_cb() */
  if (pipeline_type == 16) {
    g_object_notify (G_OBJECT (pipeline), "volume");
  }

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
424 425 426
  return TRUE;
}

427
static void do_seek (GtkWidget * widget);
428
static void connect_bus_signals (GstElement * pipeline);
429
static void set_update_scale (gboolean active);
430
static void set_update_fill (gboolean active);
431

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
432
static gboolean
433
end_scrub (GtkWidget * widget)
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
434
{
435
  GST_DEBUG ("end scrub, PAUSE");
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
436
  gst_element_set_state (pipeline, GST_STATE_PAUSED);
437
  seek_timeout_id = 0;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
438 439 440 441

  return FALSE;
}

442 443
static gboolean
send_event (GstEvent * event)
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
444
{
445
  gboolean res = FALSE;
446
  GList *walk = seekable_elements;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
447

448 449
  while (walk) {
    GstElement *seekable = GST_ELEMENT (walk->data);
450

451
    GST_DEBUG ("send event on element %s", GST_ELEMENT_NAME (seekable));
452

453 454
    gst_event_ref (event);
    res = gst_element_send_event (seekable, event);
455

456
    walk = g_list_next (walk);
457
  }
458 459 460 461 462 463 464 465 466 467 468 469
  gst_event_unref (event);
  return res;
}

static void
do_seek (GtkWidget * widget)
{
  gint64 real;
  gboolean res = FALSE;
  GstEvent *s_event;
  GstSeekFlags flags;

470 471
  real = gtk_range_get_value (GTK_RANGE (widget)) * duration / N_GRAD;

472 473
  GST_DEBUG ("value=%f, real=%" G_GINT64_FORMAT,
      gtk_range_get_value (GTK_RANGE (widget)), real);
474

475 476 477
  flags = 0;
  if (flush_seek)
    flags |= GST_SEEK_FLAG_FLUSH;
478 479 480 481 482 483
  if (accurate_seek)
    flags |= GST_SEEK_FLAG_ACCURATE;
  if (keyframe_seek)
    flags |= GST_SEEK_FLAG_KEY_UNIT;
  if (loop_seek)
    flags |= GST_SEEK_FLAG_SEGMENT;
484 485
  if (skip_seek)
    flags |= GST_SEEK_FLAG_SKIP;
486

487 488
  if (rate >= 0) {
    s_event = gst_event_new_seek (rate,
489 490
        GST_FORMAT_TIME, flags, GST_SEEK_TYPE_SET, real, GST_SEEK_TYPE_SET,
        GST_CLOCK_TIME_NONE);
491 492
    GST_DEBUG ("seek with rate %lf to %" GST_TIME_FORMAT " / %" GST_TIME_FORMAT,
        rate, GST_TIME_ARGS (real), GST_TIME_ARGS (duration));
493 494
  } else {
    s_event = gst_event_new_seek (rate,
495 496
        GST_FORMAT_TIME, flags, GST_SEEK_TYPE_SET, G_GINT64_CONSTANT (0),
        GST_SEEK_TYPE_SET, real);
497 498
    GST_DEBUG ("seek with rate %lf to %" GST_TIME_FORMAT " / %" GST_TIME_FORMAT,
        rate, GST_TIME_ARGS (0), GST_TIME_ARGS (real));
499
  }
500 501

  res = send_event (s_event);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
502

503
  if (res) {
504
    if (flush_seek) {
505
      gst_element_get_state (GST_ELEMENT (pipeline), NULL, NULL, SEEK_TIMEOUT);
506 507
    } else {
      set_update_scale (TRUE);
508
    }
509
  } else {
510
    g_print ("seek failed\n");
511 512
    set_update_scale (TRUE);
  }
513 514 515 516 517 518
}

static void
seek_cb (GtkWidget * widget)
{
  /* If the timer hasn't expired yet, then the pipeline is running */
519
  if (play_scrub && seek_timeout_id != 0) {
520
    GST_DEBUG ("do scrub seek, PAUSED");
521 522 523
    gst_element_set_state (pipeline, GST_STATE_PAUSED);
  }

524
  GST_DEBUG ("do seek");
525 526
  do_seek (widget);

527
  if (play_scrub) {
528
    GST_DEBUG ("do scrub seek, PLAYING");
529
    gst_element_set_state (pipeline, GST_STATE_PLAYING);
530

531 532 533 534
    if (seek_timeout_id == 0) {
      seek_timeout_id =
          g_timeout_add (SCRUB_TIME, (GSourceFunc) end_scrub, widget);
    }
535 536 537
  }
}

538 539 540 541 542 543 544 545
static void
set_update_fill (gboolean active)
{
  GST_DEBUG ("fill scale is %d", active);

  if (active) {
    if (fill_id == 0) {
      fill_id =
546
          g_timeout_add (FILL_INTERVAL, (GSourceFunc) update_fill, pipeline);
547 548 549 550 551 552 553 554 555
    }
  } else {
    if (fill_id) {
      g_source_remove (fill_id);
      fill_id = 0;
    }
  }
}

556 557 558
static void
set_update_scale (gboolean active)
{
559 560 561

  GST_DEBUG ("update scale is %d", active);

562 563 564
  if (active) {
    if (update_id == 0) {
      update_id =
565
          g_timeout_add (UPDATE_INTERVAL, (GSourceFunc) update_scale, pipeline);
566 567 568 569 570 571 572 573 574
    }
  } else {
    if (update_id) {
      g_source_remove (update_id);
      update_id = 0;
    }
  }
}

575 576 577
static gboolean
start_seek (GtkWidget * widget, GdkEventButton * event, gpointer user_data)
{
578 579 580
  if (event->type != GDK_BUTTON_PRESS)
    return FALSE;

581 582
  set_update_scale (FALSE);

583
  if (state == GST_STATE_PLAYING && flush_seek && scrub) {
584
    GST_DEBUG ("start scrub seek, PAUSE");
585
    gst_element_set_state (pipeline, GST_STATE_PAUSED);
586
  }
587

588
  if (changed_id == 0 && flush_seek && scrub) {
589 590 591
    changed_id =
        g_signal_connect (hscale, "value_changed", G_CALLBACK (seek_cb),
        pipeline);
592 593 594 595 596 597
  }

  return FALSE;
}

static gboolean
598
stop_seek (GtkWidget * widget, GdkEventButton * event, gpointer user_data)
599
{
600
  if (changed_id) {
601
    g_signal_handler_disconnect (hscale, changed_id);
602 603 604
    changed_id = 0;
  }

605
  if (!flush_seek || !scrub) {
606
    GST_DEBUG ("do final seek");
607 608 609
    do_seek (widget);
  }

610
  if (seek_timeout_id != 0) {
611
    g_source_remove (seek_timeout_id);
612
    seek_timeout_id = 0;
613 614 615 616 617 618
    /* Still scrubbing, so the pipeline is playing, see if we need PAUSED
     * instead. */
    if (state == GST_STATE_PAUSED) {
      GST_DEBUG ("stop scrub seek, PAUSED");
      gst_element_set_state (pipeline, GST_STATE_PAUSED);
    }
619
  } else {
620
    if (state == GST_STATE_PLAYING) {
621
      GST_DEBUG ("stop scrub seek, PLAYING");
622
      gst_element_set_state (pipeline, GST_STATE_PLAYING);
623
    }
624
  }
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
625 626 627 628 629 630 631

  return FALSE;
}

static void
play_cb (GtkButton * button, gpointer data)
{
632
  GstStateChangeReturn ret;
633 634

  if (state != GST_STATE_PLAYING) {
Wim Taymans's avatar
Wim Taymans committed
635
    g_print ("PLAY pipeline\n");
636
    gtk_statusbar_pop (GTK_STATUSBAR (statusbar), status_id);
637

638 639 640 641 642 643 644 645 646 647
    ret = gst_element_set_state (pipeline, GST_STATE_PLAYING);
    switch (ret) {
      case GST_STATE_CHANGE_FAILURE:
        goto failed;
      case GST_STATE_CHANGE_NO_PREROLL:
        is_live = TRUE;
        break;
      default:
        break;
    }
648
    state = GST_STATE_PLAYING;
649
    gtk_statusbar_push (GTK_STATUSBAR (statusbar), status_id, "Playing");
650
  }
Wim Taymans's avatar
Wim Taymans committed
651

652 653 654 655 656
  return;

failed:
  {
    g_print ("PLAY failed\n");
657
    gtk_statusbar_push (GTK_STATUSBAR (statusbar), status_id, "Play failed");
658
  }
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
659 660 661 662 663
}

static void
pause_cb (GtkButton * button, gpointer data)
{
Wim Taymans's avatar
Wim Taymans committed
664
  g_static_mutex_lock (&state_mutex);
665
  if (state != GST_STATE_PAUSED) {
666 667
    GstStateChangeReturn ret;

668
    gtk_statusbar_pop (GTK_STATUSBAR (statusbar), status_id);
Wim Taymans's avatar
Wim Taymans committed
669
    g_print ("PAUSE pipeline\n");
670
    ret = gst_element_set_state (pipeline, GST_STATE_PAUSED);
671 672 673 674 675 676 677 678 679
    switch (ret) {
      case GST_STATE_CHANGE_FAILURE:
        goto failed;
      case GST_STATE_CHANGE_NO_PREROLL:
        is_live = TRUE;
        break;
      default:
        break;
    }
680 681

    state = GST_STATE_PAUSED;
682
    gtk_statusbar_push (GTK_STATUSBAR (statusbar), status_id, "Paused");
683
  }
Wim Taymans's avatar
Wim Taymans committed
684 685
  g_static_mutex_unlock (&state_mutex);

686 687 688 689
  return;

failed:
  {
Wim Taymans's avatar
Wim Taymans committed
690
    g_static_mutex_unlock (&state_mutex);
691
    g_print ("PAUSE failed\n");
692
    gtk_statusbar_push (GTK_STATUSBAR (statusbar), status_id, "Pause failed");
693
  }
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
694 695 696 697 698
}

static void
stop_cb (GtkButton * button, gpointer data)
{
699
  if (state != STOP_STATE) {
700 701
    GstStateChangeReturn ret;

Wim Taymans's avatar
Wim Taymans committed
702
    g_print ("READY pipeline\n");
703 704
    gtk_statusbar_pop (GTK_STATUSBAR (statusbar), status_id);

Wim Taymans's avatar
Wim Taymans committed
705
    g_static_mutex_lock (&state_mutex);
706
    ret = gst_element_set_state (pipeline, STOP_STATE);
707 708 709
    if (ret == GST_STATE_CHANGE_FAILURE)
      goto failed;

710
    state = STOP_STATE;
711
    gtk_statusbar_push (GTK_STATUSBAR (statusbar), status_id, "Stopped");
712

713 714
    is_live = FALSE;
    buffering = FALSE;
715 716
    set_update_scale (FALSE);
    set_scale (0.0);
717
    set_update_fill (FALSE);
718

719 720
    if (pipeline_type == 16)
      clear_streams (pipeline);
Wim Taymans's avatar
Wim Taymans committed
721
    g_static_mutex_unlock (&state_mutex);
722

723
#if 0
724 725 726 727 728 729 730
    /* if one uses parse_launch, play, stop and play again it fails as all the
     * pads after the demuxer can't be reconnected
     */
    if (!strcmp (pipelines[pipeline_type].name, "parse-launch")) {
      gst_element_set_state (pipeline, GST_STATE_NULL);
      gst_object_unref (pipeline);

731 732 733
      g_list_free (seekable_elements);
      seekable_elements = NULL;

734 735
      pipeline = pipelines[pipeline_type].func (pipeline_spec);
      g_assert (pipeline);
736
      gst_element_set_state (pipeline, STOP_STATE);
737
      connect_bus_signals (pipeline);
738
    }
739
#endif
740 741 742 743 744
  }
  return;

failed:
  {
Wim Taymans's avatar
Wim Taymans committed
745
    g_static_mutex_unlock (&state_mutex);
746
    g_print ("STOP failed\n");
747
    gtk_statusbar_push (GTK_STATUSBAR (statusbar), status_id, "Stop failed");
748
  }
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
749 750
}

751 752 753 754 755 756 757 758 759 760 761 762
static void
accurate_toggle_cb (GtkToggleButton * button, GstPipeline * pipeline)
{
  accurate_seek = gtk_toggle_button_get_active (button);
}

static void
key_toggle_cb (GtkToggleButton * button, GstPipeline * pipeline)
{
  keyframe_seek = gtk_toggle_button_get_active (button);
}

763 764 765 766
static void
loop_toggle_cb (GtkToggleButton * button, GstPipeline * pipeline)
{
  loop_seek = gtk_toggle_button_get_active (button);
767 768 769
  if (state == GST_STATE_PLAYING) {
    do_seek (hscale);
  }
770 771
}

772 773 774 775 776 777
static void
flush_toggle_cb (GtkToggleButton * button, GstPipeline * pipeline)
{
  flush_seek = gtk_toggle_button_get_active (button);
}

778 779 780 781 782 783
static void
scrub_toggle_cb (GtkToggleButton * button, GstPipeline * pipeline)
{
  scrub = gtk_toggle_button_get_active (button);
}

784 785 786 787 788
static void
play_scrub_toggle_cb (GtkToggleButton * button, GstPipeline * pipeline)
{
  play_scrub = gtk_toggle_button_get_active (button);
}
789

790 791 792 793 794 795 796 797 798
static void
skip_toggle_cb (GtkToggleButton * button, GstPipeline * pipeline)
{
  skip_seek = gtk_toggle_button_get_active (button);
  if (state == GST_STATE_PLAYING) {
    do_seek (hscale);
  }
}

799 800 801
static void
rate_spinbutton_changed_cb (GtkSpinButton * button, GstPipeline * pipeline)
{
802 803 804 805
  gboolean res = FALSE;
  GstEvent *s_event;
  GstSeekFlags flags;

806
  rate = gtk_spin_button_get_value (button);
807

Wim Taymans's avatar
Wim Taymans committed
808 809
  GST_DEBUG ("rate changed to %lf", rate);

810 811 812 813 814
  flags = 0;
  if (flush_seek)
    flags |= GST_SEEK_FLAG_FLUSH;
  if (loop_seek)
    flags |= GST_SEEK_FLAG_SEGMENT;
815 816 817 818
  if (accurate_seek)
    flags |= GST_SEEK_FLAG_ACCURATE;
  if (keyframe_seek)
    flags |= GST_SEEK_FLAG_KEY_UNIT;
819 820
  if (skip_seek)
    flags |= GST_SEEK_FLAG_SKIP;
821

Wim Taymans's avatar
Wim Taymans committed
822
  if (rate >= 0.0) {
823 824
    s_event = gst_event_new_seek (rate,
        GST_FORMAT_TIME, flags, GST_SEEK_TYPE_SET, position,
825
        GST_SEEK_TYPE_SET, GST_CLOCK_TIME_NONE);
826 827
  } else {
    s_event = gst_event_new_seek (rate,
828 829
        GST_FORMAT_TIME, flags, GST_SEEK_TYPE_SET, G_GINT64_CONSTANT (0),
        GST_SEEK_TYPE_SET, position);
830
  }
831 832 833 834 835 836 837 838 839

  res = send_event (s_event);

  if (res) {
    if (flush_seek) {
      gst_element_get_state (GST_ELEMENT (pipeline), NULL, NULL, SEEK_TIMEOUT);
    }
  } else
    g_print ("seek failed\n");
840
}
841

842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857
static void
update_flag (GstPipeline * pipeline, gint num, gboolean state)
{
  gint flags;

  g_object_get (pipeline, "flags", &flags, NULL);
  if (state)
    flags |= (1 << num);
  else
    flags &= ~(1 << num);
  g_object_set (pipeline, "flags", flags, NULL);
}

static void
vis_toggle_cb (GtkToggleButton * button, GstPipeline * pipeline)
{
858 859 860 861 862
  gboolean state;

  state = gtk_toggle_button_get_active (button);
  update_flag (pipeline, 3, state);
  gtk_widget_set_sensitive (vis_combo, state);
863 864 865 866 867
}

static void
audio_toggle_cb (GtkToggleButton * button, GstPipeline * pipeline)
{
868 869 870 871 872
  gboolean state;

  state = gtk_toggle_button_get_active (button);
  update_flag (pipeline, 1, state);
  gtk_widget_set_sensitive (audio_combo, state);
873 874 875 876 877
}

static void
video_toggle_cb (GtkToggleButton * button, GstPipeline * pipeline)
{
878 879 880 881 882
  gboolean state;

  state = gtk_toggle_button_get_active (button);
  update_flag (pipeline, 0, state);
  gtk_widget_set_sensitive (video_combo, state);
883 884 885 886 887
}

static void
text_toggle_cb (GtkToggleButton * button, GstPipeline * pipeline)
{
888 889 890 891 892
  gboolean state;

  state = gtk_toggle_button_get_active (button);
  update_flag (pipeline, 2, state);
  gtk_widget_set_sensitive (text_combo, state);
893 894
}

895 896 897 898 899 900 901 902 903
static void
mute_toggle_cb (GtkToggleButton * button, GstPipeline * pipeline)
{
  gboolean mute;

  mute = gtk_toggle_button_get_active (button);
  g_object_set (pipeline, "mute", mute, NULL);
}

904 905 906 907 908 909 910 911 912
static void
download_toggle_cb (GtkToggleButton * button, GstPipeline * pipeline)
{
  gboolean state;

  state = gtk_toggle_button_get_active (button);
  update_flag (pipeline, 7, state);
}