~ubuntu-branches/ubuntu/natty/gst123/natty

« back to all changes in this revision

Viewing changes to src/gst123.cc

  • Committer: Bazaar Package Importer
  • Author(s): أحمد المحمودي (Ahmed El-Mahmoudy), Rogério Brito
  • Date: 2010-05-29 11:23:51 UTC
  • mfrom: (1.1.1 upstream)
  • Revision ID: james.westby@ubuntu.com-20100529112351-z1ffbyinvriludew
Tags: 0.1.0-1
* New upstream release.
* debian/control:
  + Added good & base plugins to Depends
  + Added ffmpeg, ugly & bad plugins to Recommends
  + Added libgtk2.0-dev, libgstreamer-plugins-base0.10-dev to Build-Deps
* debian/copyright:
  + Updated copyrights
  + Updated my email address

[ Rogério Brito ]
* Include a missing comma in the depends field

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
1
/* GST123 - GStreamer based command line media player
2
 
 * Copyright (C) 2006 Stefan Westerfeld
 
2
 * Copyright (C) 2006-2010 Stefan Westerfeld
 
3
 * Copyright (C) 2010 أحمد المحمودي (Ahmed El-Mahmoudy)
3
4
 *
4
5
 * This library is free software; you can redistribute it and/or
5
6
 * modify it under the terms of the GNU Lesser General Public
17
18
 * Boston, MA 02111-1307, USA.
18
19
 */
19
20
#include <gst/gst.h>
 
21
#include <gst/interfaces/xoverlay.h>
 
22
#include <gst/video/video.h>
 
23
#include <gdk/gdkx.h>
 
24
#include <gtk/gtk.h>
20
25
#include <signal.h>
21
26
#include <sys/time.h>
22
27
#include <time.h>
24
29
#include "glib-extra.h"
25
30
#include "config.h"
26
31
#include "terminal.h"
 
32
#include "gtkinterface.h"
 
33
#include "options.h"
27
34
#include <vector>
28
35
#include <string>
29
36
#include <list>
32
39
using std::vector;
33
40
using std::list;
34
41
 
35
 
static Terminal terminal;
 
42
static Terminal     terminal;
 
43
static GtkInterface gtk_interface;
36
44
 
37
45
struct Tags
38
46
{
75
83
  fclose (cols);
76
84
}
77
85
 
78
 
struct Options
79
 
{
80
 
  string        program_name; /* FIXME: what to do with that */
81
 
  bool          verbose;
82
 
  bool          shuffle;
83
 
  list<string>  playlists;
84
 
 
85
 
  Options ();
86
 
  void parse (int *argc_p, char **argv_p[]);
87
 
  static void print_usage ();
88
 
} options;
89
 
 
90
 
Options::Options ()
91
 
{
92
 
  program_name = "gst123";
93
 
  shuffle = false;
94
 
  verbose = false;
 
86
void
 
87
force_aspect_ratio (gpointer element, gpointer userdata)
 
88
{
 
89
  if (g_object_class_find_property (G_OBJECT_GET_CLASS (G_OBJECT (element)), "force-aspect-ratio"))
 
90
    g_object_set (G_OBJECT (element), "force-aspect-ratio", TRUE, NULL);
95
91
}
96
92
 
 
93
struct Options options;
 
94
 
97
95
struct Player : public KeyHandler
98
96
{
99
97
  vector<string> uris;
106
104
  Tags           tags;
107
105
  GstState       last_state;
108
106
 
109
 
  gdouble        unmute_volume;
 
107
  gint           video_size_width;
 
108
  gint           video_size_height;
110
109
 
111
110
  enum
112
111
  {
201
200
  {
202
201
    reset_tags (RESET_ALL_TAGS);
203
202
 
 
203
    /*
 
204
     * if we're playing an audio file, we don't need the GtkInterface, so we hide it here
 
205
     * if we're playing a video file, it will be shown again once GStreamer gets to the
 
206
     * point where it sends a "prepare-xwindow-id" message
 
207
     */
 
208
    gtk_interface.hide();
 
209
 
204
210
    if (options.shuffle)
205
211
      {
206
212
        if (uris.empty())
220
226
        overwrite_time_display ();
221
227
        printf ("\nPlaying %s\n", uri.c_str());
222
228
 
 
229
        gtk_interface.set_title (g_basename (uri.c_str()));
 
230
 
 
231
        video_size_width = 0;
 
232
        video_size_height = 0;
 
233
 
223
234
        gst_element_set_state (playbin, GST_STATE_NULL);
224
235
        g_object_set (G_OBJECT (playbin), "uri", uri.c_str(), NULL);
225
236
        gst_element_set_state (playbin, GST_STATE_PLAYING);
296
307
  void
297
308
  mute_unmute()
298
309
  {
299
 
    gdouble cur_volume;
300
 
    g_object_get (G_OBJECT (playbin), "volume", &cur_volume, NULL);
301
 
 
302
 
    if (cur_volume == 0)
303
 
      {
304
 
        g_object_set (G_OBJECT (playbin), "volume", unmute_volume, NULL);
305
 
      }
306
 
    else
307
 
      {
308
 
        unmute_volume = cur_volume;
309
 
        g_object_set (G_OBJECT (playbin), "volume", 0.0, NULL);
310
 
      }
 
310
    gboolean mute;
 
311
    g_object_get (G_OBJECT (playbin), "mute", &mute, NULL);
 
312
    g_object_set (G_OBJECT (playbin), "mute", !mute, NULL);
 
313
  }
 
314
 
 
315
  void
 
316
  toggle_fullscreen()
 
317
  {
 
318
    gtk_interface.toggle_fullscreen();
 
319
  }
 
320
 
 
321
  void
 
322
  normal_size()
 
323
  {
 
324
    if (video_size_width > 0 && video_size_height > 0)
 
325
      gtk_interface.resize (video_size_width, video_size_height);
 
326
    gtk_interface.unfullscreen();
311
327
  }
312
328
 
313
329
  void
367
383
}
368
384
 
369
385
static void
 
386
caps_set_cb (GObject *pad, GParamSpec *pspec, class Player* player)
 
387
{
 
388
  if (GstCaps *caps = gst_pad_get_negotiated_caps (GST_PAD (pad)))
 
389
    {
 
390
      // get video size (if any)
 
391
      gst_video_get_size (GST_PAD (pad), &player->video_size_width, &player->video_size_height);
 
392
 
 
393
      // resize window to match video size
 
394
      player->normal_size();
 
395
 
 
396
      gst_caps_unref (caps);
 
397
    }
 
398
}
 
399
 
 
400
static void
370
401
collect_element (gpointer element,
371
402
                 gpointer list_ptr)
372
403
{
402
433
my_bus_callback (GstBus * bus, GstMessage * message, gpointer data)
403
434
{
404
435
  Player& player = *(Player *) data;
405
 
 
406
436
  switch (GST_MESSAGE_TYPE (message)) {
407
437
    case GST_MESSAGE_ERROR: {
408
438
      GError *err;
447
477
        player.last_state = state;
448
478
      }
449
479
      break;
 
480
    case GST_MESSAGE_ELEMENT:
 
481
      {
 
482
        if (gst_structure_has_name (message->structure, "prepare-xwindow-id") && gtk_interface.init_ok())
 
483
          {
 
484
            // show gtk window to display video in
 
485
            gtk_interface.show();
 
486
            gst_x_overlay_set_xwindow_id (GST_X_OVERLAY (GST_MESSAGE_SRC (message)),
 
487
                                          GDK_WINDOW_XWINDOW (gtk_interface.window()->window));
 
488
          }
 
489
        else if (gst_structure_has_name (message->structure, "playbin2-stream-changed"))
 
490
          {
 
491
            // try to figure out the video size
 
492
            GstElement *videosink;
 
493
            g_object_get (G_OBJECT (player.playbin), "video-sink", &videosink, NULL);
 
494
            if (videosink && !options.novideo)
 
495
              {
 
496
                // Find an sink element that has "force-aspect-ratio" property & set it
 
497
                // to TRUE:
 
498
                GstIterator *iterator = gst_bin_iterate_sinks (GST_BIN (videosink));
 
499
                gst_iterator_foreach (iterator, force_aspect_ratio, NULL);
 
500
 
 
501
                if (GstPad* pad = gst_element_get_static_pad (videosink, "sink"))
 
502
                  {
 
503
                    if (GstCaps *caps = gst_pad_get_negotiated_caps (pad))
 
504
                      {
 
505
                        caps_set_cb (G_OBJECT (pad), NULL, &player);
 
506
                        gst_caps_unref (caps);
 
507
                      }
 
508
 
 
509
                    g_signal_connect (pad, "notify::caps", G_CALLBACK (caps_set_cb), &player);
 
510
                    gst_object_unref (GST_OBJECT (pad));
 
511
                  }
 
512
                gst_object_unref (GST_OBJECT (videosink));
 
513
              }
 
514
          }
 
515
      }
450
516
    default:
451
517
      /* unhandled message */
452
518
      break;
480
546
  return TRUE;
481
547
}
482
548
 
483
 
 
484
549
static gboolean
485
550
cb_print_position (gpointer *data)
486
551
{
502
567
      if (len > 0)   /* streams (i.e. http) have len == -1 */
503
568
        g_print (" of %01lu:%02lu:%02lu.%02lu", len_min / 60, len_min % 60, tv_len.tv_sec % 60, tv_len.tv_usec / 10000);
504
569
 
 
570
      string status, blanks;
505
571
      // Print [MUTED] if sound is muted:
506
 
      gdouble cur_volume;
507
 
      g_object_get (G_OBJECT (player.playbin), "volume", &cur_volume, NULL);
508
 
 
509
 
      if (cur_volume == 0)
510
 
        g_print (" [MUTED]");
511
 
      else
512
 
        g_print ("        ");
513
 
      g_print ("\r");
 
572
      gboolean mute;
 
573
      g_object_get (G_OBJECT (player.playbin), "mute", &mute, NULL);
 
574
 
 
575
      if (mute)
 
576
        status += " [MUTED]";
 
577
      else
 
578
        blanks += "        ";
 
579
 
 
580
      // Print [PAUSED] if paused:
 
581
      bool pause = (player.last_state == GST_STATE_PAUSED);
 
582
 
 
583
      if (pause)
 
584
        status += " [PAUSED]";
 
585
      else
 
586
        blanks += "         ";
 
587
      g_print ("%s%s\r", status.c_str(), blanks.c_str());
514
588
    }
515
589
 
516
590
  /* call me again */
517
591
  return TRUE;
518
592
}
519
593
 
520
 
static bool
521
 
check_arg (uint         argc,
522
 
           char        *argv[],
523
 
           uint        *nth,
524
 
           const char  *opt,              /* for example: --foo */
525
 
           const char **opt_arg = NULL)   /* if foo needs an argument, pass a pointer to get the argument */
526
 
{
527
 
  g_return_val_if_fail (opt != NULL, false);
528
 
  g_return_val_if_fail (*nth < argc, false);
529
 
 
530
 
  const char *arg = argv[*nth];
531
 
  if (!arg)
532
 
    return false;
533
 
 
534
 
  uint opt_len = strlen (opt);
535
 
  if (strcmp (arg, opt) == 0)
536
 
    {
537
 
      if (opt_arg && *nth + 1 < argc)     /* match foo option with argument: --foo bar */
538
 
        {
539
 
          argv[(*nth)++] = NULL;
540
 
          *opt_arg = argv[*nth];
541
 
          argv[*nth] = NULL;
542
 
          return true;
543
 
        }
544
 
      else if (!opt_arg)                  /* match foo option without argument: --foo */
545
 
        {
546
 
          argv[*nth] = NULL;
547
 
          return true;
548
 
        }
549
 
      /* fall through to error message */
550
 
    }
551
 
  else if (strncmp (arg, opt, opt_len) == 0 && arg[opt_len] == '=')
552
 
    {
553
 
      if (opt_arg)                        /* match foo option with argument: --foo=bar */
554
 
        {
555
 
          *opt_arg = arg + opt_len + 1;
556
 
          argv[*nth] = NULL;
557
 
          return true;
558
 
        }
559
 
      /* fall through to error message */
560
 
    }
561
 
  else
562
 
    return false;
563
 
 
564
 
  Options::print_usage();
565
 
  exit (1);
566
 
}
567
 
 
568
 
void
569
 
Options::parse (int   *argc_p,
570
 
                char **argv_p[])
571
 
{
572
 
  guint argc = *argc_p;
573
 
  gchar **argv = *argv_p;
574
 
  unsigned int i, e;
575
 
 
576
 
  g_return_if_fail (argc >= 0);
577
 
 
578
 
  /*  I am tired of seeing .libs/lt-gst123 all the time,
579
 
   *  but basically this should be done (to allow renaming the binary):
580
 
   *
581
 
  if (argc && argv[0])
582
 
    program_name = argv[0];
583
 
  */
584
 
 
585
 
  for (i = 1; i < argc; i++)
586
 
    {
587
 
      const char *opt_arg;
588
 
      if (strcmp (argv[i], "--help") == 0 ||
589
 
          strcmp (argv[i], "-h") == 0)
590
 
        {
591
 
          print_usage();
592
 
          exit (0);
593
 
        }
594
 
      else if (strcmp (argv[i], "--version") == 0 || strcmp (argv[i], "-v") == 0)
595
 
        {
596
 
          printf ("%s %s\n", program_name.c_str(), VERSION);
597
 
          exit (0);
598
 
        }
599
 
      else if (check_arg (argc, argv, &i, "--verbose"))
600
 
        {
601
 
          verbose = true;
602
 
        }
603
 
      else if (check_arg (argc, argv, &i, "--shuffle") || check_arg (argc, argv, &i, "-z"))
604
 
        {
605
 
          shuffle = true;
606
 
        }
607
 
      else if (check_arg (argc, argv, &i, "--list", &opt_arg) || check_arg (argc, argv, &i, "-@", &opt_arg))
608
 
        {
609
 
          playlists.push_back (opt_arg);
610
 
        }
611
 
    }
612
 
 
613
 
  /* resort argc/argv */
614
 
  e = 1;
615
 
  for (i = 1; i < argc; i++)
616
 
    if (argv[i])
617
 
      {
618
 
        argv[e++] = argv[i];
619
 
        if (i >= e)
620
 
          argv[i] = NULL;
621
 
      }
622
 
  *argc_p = e;
623
 
}
624
 
 
625
 
void
626
 
Options::print_usage ()
627
 
{
628
 
  g_printerr ("usage: %s [ <options> ] <URI> ...\n", options.program_name.c_str());
629
 
  g_printerr ("\n");
630
 
  g_printerr ("options:\n");
631
 
  g_printerr (" -h, --help                  help for %s\n", options.program_name.c_str());
632
 
  g_printerr (" -@, --list <filename>       read playlist of files and URIs from \"filename\"\n");
633
 
  g_printerr (" --version                   print version\n");
634
 
  g_printerr (" --verbose                   print GStreamer pipeline used to play files\n");
635
 
  g_printerr (" -z, --shuffle               play files in pseudo random order\n");
636
 
  g_printerr ("\n");
637
 
}
638
594
 
639
595
static inline bool
640
596
is_directory (const string& path)
679
635
{
680
636
  switch (key)
681
637
    {
682
 
      case Terminal::TERMINAL_KEY_RIGHT:
 
638
      case KEY_HANDLER_RIGHT:
683
639
        relative_seek (10);
684
640
        break;
685
 
      case Terminal::TERMINAL_KEY_LEFT:
 
641
      case KEY_HANDLER_LEFT:
686
642
        relative_seek (-10);
687
643
        break;
688
 
      case Terminal::TERMINAL_KEY_UP:
 
644
      case KEY_HANDLER_UP:
689
645
        relative_seek (60);
690
646
        break;
691
 
      case Terminal::TERMINAL_KEY_DOWN:
 
647
      case KEY_HANDLER_DOWN:
692
648
        relative_seek (-60);
693
649
        break;
694
 
      case Terminal::TERMINAL_KEY_PAGE_UP:
 
650
      case KEY_HANDLER_PAGE_UP:
695
651
        relative_seek (600);
696
652
        break;
697
 
      case Terminal::TERMINAL_KEY_PAGE_DOWN:
 
653
      case KEY_HANDLER_PAGE_DOWN:
698
654
        relative_seek (-600);
699
655
        break;
700
656
      case 'Q':
714
670
      case 'm':
715
671
        mute_unmute();
716
672
        break;
 
673
      case 'F':
 
674
      case 'f':
 
675
        toggle_fullscreen();
 
676
        break;
 
677
      case '1':
 
678
        normal_size();
 
679
        break;
717
680
      case '?':
718
681
        print_keyboard_help();
719
682
        break;
723
686
void
724
687
Player::print_keyboard_help()
725
688
{
 
689
  overwrite_time_display();
 
690
 
726
691
  printf ("\n\n");
727
692
  printf ("==================== gst123 keyboard commands =======================\n");
728
693
  printf ("   cursor left/right    -     seek 10 seconds backwards/forwards\n");
731
696
  printf ("   space                -     toggle pause\n");
732
697
  printf ("   +/-                  -     increase/decrease volume by 10%%\n");
733
698
  printf ("   m                    -     toggle mute/unmute\n");
 
699
  printf ("   f                    -     toggle fullscreen (only for videos)\n");
 
700
  printf ("   1                    -     normal video size (only for videos)\n");
734
701
  printf ("   q                    -     quit gst123\n");
735
702
  printf ("   ?                    -     this help\n");
736
703
  printf ("=====================================================================\n");
758
725
{
759
726
  Player player;
760
727
 
 
728
  /* Setup options */
 
729
  if (!g_thread_supported ())
 
730
    g_thread_init (NULL);
 
731
 
 
732
  options.parse (argc, argv);
 
733
 
761
734
  /* init GStreamer */
762
735
  gst_init (&argc, &argv);
 
736
  gtk_interface.init (&argc, &argv, &player);
 
737
 
763
738
  player.loop = g_main_loop_new (NULL, FALSE);
764
739
 
765
 
  options.parse (&argc, &argv);
766
 
 
767
740
  /* set up */
768
 
  for (int i = 1; i < argc; i++)
769
 
    player.add_uri_or_directory (argv[i]);
 
741
  if (options.uris)
 
742
    {
 
743
      for (int i = 0; options.uris[i]; i++)
 
744
        player.add_uri_or_directory (options.uris[i]);
 
745
    }
770
746
 
771
747
  for (list<string>::iterator pi = options.playlists.begin(); pi != options.playlists.end(); pi++)
772
748
    {
800
776
  /* make sure we have a URI */
801
777
  if (player.uris.empty())
802
778
    {
803
 
      options.print_usage();
 
779
      printf ("%s", options.usage.c_str());
804
780
      return -1;
805
781
    }
806
 
  player.playbin = gst_element_factory_make ("playbin", "play");
 
782
  player.playbin = gst_element_factory_make ("playbin2", "play");
 
783
  if (options.novideo)
 
784
    {
 
785
      GstElement *fakesink = gst_element_factory_make ("fakesink", "novid");
 
786
      g_object_set (G_OBJECT (player.playbin), "video-sink", fakesink, NULL);
 
787
    }
807
788
  gst_bus_add_watch (gst_pipeline_get_bus (GST_PIPELINE (player.playbin)), my_bus_callback, &player);
808
789
  g_timeout_add (130, (GSourceFunc) cb_print_position, &player);
809
790
  signal (SIGINT, sigint_handler);