c-player.c 30.5 KB
Newer Older
hark's avatar
hark committed
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/* [name], A player for snowmix
 *
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation; either version 2 of the
 * License, or (at your option) any later version.
 *
 * This program 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
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

#include <string.h>
#include "common.h"

hark's avatar
hark committed
22
#ifdef PL
hark's avatar
hark committed
23
#include "playlist.h"
hark's avatar
hark committed
24
25
#endif

hark's avatar
hark committed
26
#include "rtmpbin.h"
hark's avatar
hark committed
27
28
29

#ifdef SNOWBIN
#include "teebin.h"
hark's avatar
hark committed
30
#include "snowbin.h"
hark's avatar
hark committed
31
32
#endif

hark's avatar
hark committed
33
34

// player things
hark's avatar
hark committed
35
/*
hark's avatar
hark committed
36
37
38
39
40
41
42
static const char *playuri = NULL;
static char *feedname = NULL;
static int preview = 0;
static GstStateChangeReturn ret;
static GstBus *bus;
// snowbin or auto
static gchar *sink = "snowbin";
hark's avatar
hark committed
43
*/
hark's avatar
hark committed
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60

//Gtk things
#ifdef GTK
static GtkWidget *feed_label;
static GtkWidget *clock_label;
static char *markup = NULL;
static GtkWidget *dialog, *video_window, *main_hbox;
#ifdef TT
//threadtest
GtkWidget *label_time;
GThread   *thread_time;
GError    *error = NULL;
#endif

#endif

#ifdef TT
hark's avatar
hark committed
61
62
63
64
65
66
67
68
static GSourceFunc set_time (char * markup_time)
{
  
  gtk_label_set_text(GTK_LABEL(label_time), markup_time ); 
  printf("test: %s", markup_time);

}

hark's avatar
hark committed
69
70
static gpointer thread_time_func( gpointer data )
{
hark's avatar
hark committed
71
72
73
    auto int i;
    
    auto gchar * markup_time;
hark's avatar
hark committed
74
75
    while(1)
    {
hark's avatar
hark committed
76
        sleep(1);
hark's avatar
hark committed
77
78
79
        
        i = i + 1;
        
hark's avatar
hark committed
80
81
82
83
        markup_time = g_markup_printf_escaped ("i:%u", i);
// > that is depreciated        gdk_threads_enter();
       gdk_threads_add_idle ((GSourceFunc)set_time,markup_time); 
// > that is depreciated        gdk_threads_leave();
hark's avatar
hark committed
84
85
86
87
88
89
90
91
92
93
94
95
96

        g_free(markup_time);
    }
}
#endif


/* This function is called when the GUI toolkit creates the physical window that will hold the video.
 * At this point we can retrieve its handler (which has a different meaning depending on the windowing system)
 * and pass it to GStreamer through the XOverlay interface. */

#ifdef GTK  
  static void
hark's avatar
hark committed
97
realize_cb (GtkWidget * widget, PlayerData * data)
hark's avatar
hark committed
98
{
hark's avatar
hark committed
99
  auto GdkWindow *window = gtk_widget_get_window (widget);
hark's avatar
hark committed
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
  static guintptr window_handle;

  if (!gdk_window_ensure_native (window))
    g_error ("Couldn't create native window needed for GstXOverlay!");

  /* Retrieve window handler from GDK */
#if defined (GDK_WINDOWING_WIN32)
  window_handle = (guintptr) GDK_WINDOW_HWND (window);
#elif defined (GDK_WINDOWING_QUARTZ)
  window_handle = gdk_quartz_window_get_nsview (window);
#elif defined (GDK_WINDOWING_X11)
  window_handle = GDK_WINDOW_XID (window);
#endif
  /* Pass it to playbin2, which implements XOverlay and will forward it to the video sink */

#ifdef GST1
  gst_video_overlay_set_window_handle (GST_VIDEO_OVERLAY (data->playbin2),
      window_handle);
#else
  gst_x_overlay_set_window_handle (GST_X_OVERLAY (data->playbin2),
      window_handle);
#endif

}
#endif

/* This function is called when the PLAY button is clicked */
#ifdef GTK
  static void
hark's avatar
hark committed
129
play_cb (GtkButton * button, PlayerData * data)
hark's avatar
hark committed
130
131
132
133
134
135
136
137
{

  gst_element_set_state (data->playbin2, GST_STATE_PLAYING);

}

/* This function is called when the PAUSE button is clicked */
  static void
hark's avatar
hark committed
138
pause_cb (GtkButton * button, PlayerData * data)
hark's avatar
hark committed
139
140
141
142
143
144
145
146
{

  gst_element_set_state (data->playbin2, GST_STATE_PAUSED);

}

/* This function is called when the STOP button is clicked */
  static void
hark's avatar
hark committed
147
stop_cb (GtkButton * button, PlayerData * data)
hark's avatar
hark committed
148
149
150
151
152
153
154
155
156
{
  gst_element_set_state (data->playbin2, GST_STATE_READY);
}
#endif

  static void
load_uri (const gchar * uri)
{
  // this would stop playback, so don't do that
hark's avatar
hark committed
157
  //gst_element_set_state (playerData.playbin2, GST_STATE_READY);
hark's avatar
hark committed
158
  // GstStateChangeReturn ret;
hark's avatar
hark committed
159
  g_object_set (pData->playbin2, "uri", uri, NULL);
hark's avatar
hark committed
160
161
162
163

#ifdef GTK
  markup =
    g_markup_printf_escaped
hark's avatar
hark committed
164
    ("<span foreground=\"red\" size=\"x-small\">%s</span>", uri);
hark's avatar
hark committed
165
166
  gtk_label_set_markup (GTK_LABEL (loaded_label), markup);
#endif
hark's avatar
hark committed
167
  //  gst_element_set_state (pData->playbin2, GST_STATE_PLAYING);
hark's avatar
hark committed
168
169
170
171
}

#ifdef GTK
  static void
hark's avatar
hark committed
172
preview_cb (GtkCheckButton * preview_check, PlayerData * data)
hark's avatar
hark committed
173
{
hark's avatar
hark committed
174
    if (data->preview == 0)
hark's avatar
hark committed
175
    {
hark's avatar
hark committed
176
//      g_print ("turning on preview checkcb \n");
hark's avatar
hark committed
177
      teebin_preview_on(data);
hark's avatar
hark committed
178
179
      data->preview = 1;
      return;
hark's avatar
hark committed
180
    }
hark's avatar
hark committed
181
    if (data->preview == 1)
hark's avatar
hark committed
182
    {
hark's avatar
hark committed
183
//      g_print ("turning off preview checkcb \n");
hark's avatar
hark committed
184
      teebin_preview_off(data);
hark's avatar
hark committed
185
186
      data->preview = 0;
      return;
hark's avatar
hark committed
187
188
189
190
191
192
193
    }
}
#endif

/* This function is called when the main window is closed */
#ifdef GTK
  static void
hark's avatar
hark committed
194
delete_event_cb (GtkWidget * widget, GdkEvent * event, PlayerData * data)
hark's avatar
hark committed
195
196
197
198
199
200
201
202
203
{
  stop_cb (NULL, data);
  gtk_main_quit ();
}

/* This function is called everytime the video window needs to be redrawn (due to damage/exposure,
 * rescaling, etc). GStreamer takes care of this in the PAUSED and PLAYING states, otherwise,
 * we simply draw a black rectangle to avoid garbage showing up. */
  static gboolean
hark's avatar
hark committed
204
expose_cb (GtkWidget * widget, GdkEventExpose * event, PlayerData * data)
hark's avatar
hark committed
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
{

  if (data->state < GST_STATE_PAUSED)
  {
    GtkAllocation allocation;
    GdkWindow *window = gtk_widget_get_window (widget);

    cairo_t *cr;

    // Cairo is a 2D graphics library which we use here to clean the video window.
    // It is used by GStreamer for other reasons, so it will always be available to us. 
    gtk_widget_get_allocation (widget, &allocation);
    cr = gdk_cairo_create (window);
    cairo_set_source_rgb (cr, 0, 0, 0);
    cairo_rectangle (cr, 0, 0, allocation.width, allocation.height);
    cairo_fill (cr);
    cairo_destroy (cr);
  }

  return FALSE;
}
#endif


#ifdef GTK
/* This function is called when the slider changes its position. We perform a seek to the
 * new position here. */
  static void
hark's avatar
hark committed
233
slider_cb (GtkRange * range, PlayerData * data)
hark's avatar
hark committed
234
{
hark's avatar
hark committed
235
  auto gdouble value = gtk_range_get_value (GTK_RANGE (data->slider));
hark's avatar
hark committed
236
237
238
239
240
241
242
243
244
  gst_element_seek_simple (data->playbin2, GST_FORMAT_TIME,
      GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_KEY_UNIT,
      (gint64) (value * GST_SECOND));
}
#endif

#ifdef GTK
/* This creates all the GTK+ widgets that compose our application, and registers the callbacks */
  static void
hark's avatar
hark committed
245
create_ui (PlayerData * data)
hark's avatar
hark committed
246
247
248
249
250
251
252
253
254
255
{
  static GtkWidget *main_window;	/* The uppermost window, containing all other windows */


  static GtkWidget *main_box;		/* VBox to hold main_hbox and the controls */
  //  GtkWidget *main_hbox;		/* HBox to hold the video_window and the stream info text widget */
  static GtkWidget *controls;		/* HBox to hold the buttons and the slider */
  static GtkWidget *options;		/* HBox to hold the options and current uri */
  static GtkWidget *play_button, *pause_button, *stop_button, *open_button, *add_button;	/* Buttons */
  static GtkWidget *test_button, *previous_button, *next_button;
hark's avatar
hark committed
256
//  static GtkWidget *preview_check;	/* Checkbuttons */
hark's avatar
hark committed
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273

  // this should be done by playlist
  //  GtkWidget *loop_check;	/* Checkbuttons */


  main_window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
  g_signal_connect (G_OBJECT (main_window), "delete-event",
      G_CALLBACK (delete_event_cb), data);

  video_window = gtk_drawing_area_new ();
  gtk_widget_set_size_request (video_window, 250, 350);
  gtk_widget_set_double_buffered (video_window, FALSE);
  g_signal_connect (video_window, "realize", G_CALLBACK (realize_cb), data);
  g_signal_connect (video_window, "expose_event", G_CALLBACK (expose_cb),
      data);

  /* make the feed label that will change color based on playing state */
hark's avatar
hark committed
274
275

  feed_label = gtk_label_new(NULL); 
hark's avatar
hark committed
276
277
  markup =
    g_markup_printf_escaped
hark's avatar
hark committed
278
279
    ("<span foreground=\"red\" size=\"x-large\">F: %u --</span>", data->feedid);
  gtk_label_set_markup (GTK_LABEL(feed_label), markup);
hark's avatar
hark committed
280
281
  g_free (markup);

hark's avatar
hark committed
282
  loaded_label = gtk_label_new(NULL); 
hark's avatar
hark committed
283
284
  markup =
    g_markup_printf_escaped
hark's avatar
hark committed
285
286
    ("<span foreground=\"red\" size=\"x-small\">currently loaded uri</span>");
  gtk_label_set_markup (GTK_LABEL(loaded_label), markup);
hark's avatar
hark committed
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
  g_free (markup);

  markup =
    g_markup_printf_escaped
    ("<span foreground=\"pink\" size=\"x-large\">clock</span>");
  clock_label = gtk_label_new ("clock");
  gtk_label_set_markup (GTK_LABEL (clock_label), markup);
  g_free (markup);



  play_button = gtk_button_new_from_stock (GTK_STOCK_MEDIA_PLAY);
  g_signal_connect (G_OBJECT (play_button), "clicked", G_CALLBACK (play_cb),
      data);

  pause_button = gtk_button_new_from_stock (GTK_STOCK_MEDIA_PAUSE);
  g_signal_connect (G_OBJECT (pause_button), "clicked", G_CALLBACK (pause_cb),
      data);

  stop_button = gtk_button_new_from_stock (GTK_STOCK_MEDIA_STOP);
  g_signal_connect (G_OBJECT (stop_button), "clicked", G_CALLBACK (stop_cb),
      data);

hark's avatar
hark committed
310
311
  data->preview_check = gtk_check_button_new_with_label ("preview current video");
  g_signal_connect (G_OBJECT (data->preview_check), "toggled",
hark's avatar
hark committed
312
313
      G_CALLBACK (preview_cb), data);

hark's avatar
hark committed
314
//  gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(preview_check), TRUE);
hark's avatar
hark committed
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339

  //  uri_entrybox = gtk_entry_new ();

  data->slider = gtk_hscale_new_with_range (0, 1000, 1);
  gtk_scale_set_draw_value (GTK_SCALE (data->slider), 0);
  data->slider_update_signal_id =
    g_signal_connect (G_OBJECT (data->slider), "value-changed",
        G_CALLBACK (slider_cb), data);

#ifdef TT
label_time = gtk_label_new("Loading ...");
thread_time = g_thread_create( thread_time_func, (gpointer)label_time, FALSE, &error );
#endif

  /* what is this for??? */
  data->streams_list = gtk_text_view_new ();
  gtk_text_view_set_editable (GTK_TEXT_VIEW (data->streams_list), FALSE);

  /* pack all the windows in boxes */
  /* controls */
  controls = gtk_hbox_new (FALSE, 0);
  gtk_box_pack_start (GTK_BOX (controls), feed_label, FALSE, FALSE, 2);
  gtk_box_pack_start (GTK_BOX (controls), play_button, FALSE, FALSE, 2);
  gtk_box_pack_start (GTK_BOX (controls), pause_button, FALSE, FALSE, 2);
  gtk_box_pack_start (GTK_BOX (controls), stop_button, FALSE, FALSE, 2);
hark's avatar
hark committed
340
  gtk_box_pack_start (GTK_BOX (controls), data->preview_check, FALSE, FALSE, 2);
hark's avatar
hark committed
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366

  /* options */
  options = gtk_vbox_new (FALSE, 0);

  gtk_box_pack_start (GTK_BOX (options), video_window, TRUE, TRUE, 2);
  gtk_box_pack_start (GTK_BOX (options), data->slider, TRUE, TRUE, 2);
  gtk_box_pack_start (GTK_BOX (options), clock_label, TRUE, TRUE, 2);
  gtk_box_pack_start (GTK_BOX (options), loaded_label, FALSE, FALSE, 2);
#ifdef TT
  gtk_box_pack_start (GTK_BOX (options), label_time, FALSE, FALSE, 2);
#endif

  // this should be done by playlist
  //  gtk_box_pack_start (GTK_BOX (options), uri_entrybox, FALSE, FALSE, 2);

  /* main hbox */
  main_hbox = gtk_hbox_new (FALSE, 0);
  gtk_box_pack_start (GTK_BOX (main_hbox), data->streams_list, FALSE, FALSE,
      2);

  /* main box */
  main_box = gtk_vbox_new (FALSE, 0);
  gtk_box_pack_start (GTK_BOX (main_box), main_hbox, TRUE, TRUE, 0);
  gtk_box_pack_start (GTK_BOX (main_box), options, FALSE, FALSE, 0);
  gtk_box_pack_start (GTK_BOX (main_box), controls, FALSE, FALSE, 0);

hark's avatar
hark committed
367
#ifdef PL
hark's avatar
hark committed
368
369
  /* init playlist */
  playlist_box = playlist_init_gtk ();
hark's avatar
hark committed
370

hark's avatar
hark committed
371
372
373
374
  // pack the playlist
  gtk_box_pack_start (GTK_BOX (main_box), playlist_box, TRUE, TRUE, 0);

  load_uri (playlist_get_current ());
hark's avatar
hark committed
375
#endif
hark's avatar
hark committed
376
377
378
379
380
381
382
383
384
385
386
  gtk_container_add (GTK_CONTAINER (main_window), main_box);
  gtk_window_set_default_size (GTK_WINDOW (main_window), 640, 480);

  gtk_widget_show_all (main_window);
  gtk_widget_hide(main_hbox);
}
#endif

#ifdef GTK
/* This function is called periodically to refresh the GUI */
  static int
hark's avatar
hark committed
387
refresh_ui (PlayerData * data)
hark's avatar
hark committed
388
{
hark's avatar
hark committed
389
390
391
392
393
  auto GstFormat fmt = GST_FORMAT_TIME;
  auto gint64 current = -1;
  auto char *nicetime;
  auto int sec, min, hour, time;
  //  printf("refresh_ui state: %u paused=%u  \n", data->state, GST_STATE_PAUSED);
hark's avatar
hark committed
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
  /* We do not want to update anything unless we are in the PAUSED or PLAYING states */
  if (data->state < GST_STATE_PAUSED)
    return TRUE;
    /* If we didn't know it yet, query the stream duration */
    if (!GST_CLOCK_TIME_IS_VALID (data->duration))
    {
#ifdef GST1
      if (!gst_element_query_duration
          (data->playbin2, fmt, &data->duration))
#else
        if (!gst_element_query_duration
            (data->playbin2, &fmt, &data->duration))
#endif

        {
          g_printerr ("Could not query current duration.\n");
        }
        else
        {
          /* Set the range of the slider to the clip duration, in SECONDS */
          // g_printf header is not included!
          //g_printf ("slider range:: %li \n", data->duration / GST_SECOND);
          gtk_range_set_range (GTK_RANGE (data->slider), 0,
              (gdouble) data->duration / GST_SECOND);
        }
    }
#ifdef GST1
  if (gst_element_query_position (data->playbin2, fmt, &current))
#else
    if (gst_element_query_position (data->playbin2, &fmt, &current))
#endif
    {
      /* Block the "value-changed" signal, so the slider_cb function is not called
       * (which would trigger a seek the user has not requested) */
      g_signal_handler_block (data->slider, data->slider_update_signal_id);
      /* Set the position of the slider to the current pipeline positoin, in SECONDS */
      gtk_range_set_value (GTK_RANGE (data->slider),
          (gdouble) current / GST_SECOND);


      /* Re-enable the signal */
      g_signal_handler_unblock (data->slider, data->slider_update_signal_id);
      //char *nicetime = "00:00:00";
hark's avatar
hark committed
437
            time = current / GST_SECOND;
hark's avatar
hark committed
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
      hour = time / 3600;
      min = time % 3600 / 60;
      sec = time % 60;
      //sprintf(nicetime, "%u:%u:%u", hour, min, sec);
      asprintf(&nicetime, "%u:%u:%u", hour, min, sec);
      //printf("%s", nicetime);
      markup =
        g_markup_printf_escaped
        ("<span foreground=\"black\" size=\"x-large\">%s</span>", nicetime);
      gtk_label_set_markup (GTK_LABEL (clock_label), markup);


    }
  return TRUE;
}
#endif
  extern int 
player_reset_and_play () 
{
  /* stop playing */
hark's avatar
hark committed
458
  gst_element_set_state (pData->playbin2, GST_STATE_READY);
hark's avatar
hark committed
459

hark's avatar
hark committed
460
#ifdef PL
hark's avatar
hark committed
461
462
  printf("current uri: %s", playlist_get_current());
  load_uri(playlist_get_current());
hark's avatar
hark committed
463
464
#endif
  int ret = 0;
hark's avatar
hark committed
465
  /* Start playing */
hark's avatar
hark committed
466
  ret = gst_element_set_state (pData->playbin2, GST_STATE_PLAYING);
hark's avatar
hark committed
467
468
469
  if (ret == GST_STATE_CHANGE_FAILURE)
  {
    g_printerr ("state change failure in player_reset_and_play\n");
hark's avatar
hark committed
470
//    gst_object_unref (pData->playbin2);
hark's avatar
hark committed
471
472
473
474
475
476
477
478
479
//     return -1;
    return 1;
  }
  return 0;
}


/* This function is called when new metadata is discovered in the stream */
  static void
hark's avatar
hark committed
480
tags_cb (GstElement * playbin2, gint stream, PlayerData * data)
hark's avatar
hark committed
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
{
  /* We are possibly in a GStreamer working thread, so we notify the main
   * thread of this event through a message in the bus */

  // c-player.c:423:10: warning: not enough variable arguments to fit a sentinel [-Wformat=]
  //           NULL)));
  //
  gst_element_post_message (playbin2,
      gst_message_new_application (GST_OBJECT
        (playbin2),
        gst_structure_new 
        ("tags-changed",
         NULL)));
}

/* This function is called when an error message is posted on the bus */
  static void
hark's avatar
hark committed
498
error_cb (GstBus * bus, GstMessage * msg, PlayerData * data)
hark's avatar
hark committed
499
{
hark's avatar
hark committed
500
501
  auto GError *err;
  auto gchar *debug_info;
hark's avatar
hark committed
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519

  /* Print error details on the screen */
  gst_message_parse_error (msg, &err, &debug_info);
  g_printerr ("Error received from element %s: %s\n",
      GST_OBJECT_NAME (msg->src), err->message);
  g_printerr ("Debugging information: %s\n",
      debug_info ? debug_info : "none");
  g_clear_error (&err);
  g_free (debug_info);

  /* Set the pipeline to READY (which stops playback) */
  gst_element_set_state (data->playbin2, GST_STATE_READY);
}

/* This function is called when the stream is almost finished
 * next item in playlist will be loaded now 
 * and something should flash*/
  static void
hark's avatar
hark committed
520
aeos_cb (GstBus * bus, GstMessage * msg, PlayerData * data)
hark's avatar
hark committed
521
522
523
524
525
526
527
528
529
530
{
  g_print ("Almost finished.\n");

  //load next playlist item
  //  load_uri (playlist_get_next ());

}

/* this function is called after source element is setup */
  static void
hark's avatar
hark committed
531
source_cb (GstBus * bus, GstMessage * msg, PlayerData * data)
hark's avatar
hark committed
532
533
534
535
536
537
538
539
540
541
542
{

  g_print ("Source setup.\n");


}


/* This function is called when an End-Of-Stream message is posted on the bus.
 * We just set the pipeline to READY (which stops playback) */
  static void
hark's avatar
hark committed
543
eos_cb (GstBus * bus, GstMessage * msg, PlayerData * data)
hark's avatar
hark committed
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
{
  g_print ("End-Of-Stream reached.\n");
  gst_element_set_state (data->playbin2, GST_STATE_READY);
  // loop should be done in playlist
  //if (loop == 0) load_uri (playlist_get_next ());
  player_reset_and_play();

  //gst_element_set_state (data->playbin2, GST_STATE_PLAYING);

}


/* This function is called when the pipeline changes states. We use it to
 * keep track of the current state. */
  static void
hark's avatar
hark committed
559
state_changed_cb (GstBus * bus, GstMessage * msg, PlayerData * data)
hark's avatar
hark committed
560
{
hark's avatar
hark committed
561
562
563
564
565
  auto GstState old_state, new_state, pending_state;
  auto GstFormat fmt = GST_FORMAT_TIME;
  char feedname[10] = "F:00"; 
  snprintf(&feedname, sizeof(feedname), "F:%u", data->feedid);

hark's avatar
hark committed
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
  gst_message_parse_state_changed (msg, &old_state, &new_state,
      &pending_state);
#ifdef GTK
  refresh_ui(data);
#endif
  if (GST_MESSAGE_SRC (msg) == GST_OBJECT (data->playbin2))
  {
    data->state = new_state;

    //   g_print ("State set to %s\n", gst_element_state_get_name (new_state));

    /* update play state indicators here ..... */

    switch (new_state)
    {

      case GST_STATE_READY:
#ifdef GTK
        markup =
          g_markup_printf_escaped
          ("<span foreground=\"orange\" size=\"x-large\">%s</span>",
           feedname);
        gtk_label_set_markup (GTK_LABEL (feed_label), markup);
#endif
        break;

      case GST_STATE_PLAYING:
#ifdef GTK
        markup =
          g_markup_printf_escaped
          ("<span foreground=\"green\" size=\"x-large\">%s</span>",
           feedname);
        gtk_label_set_markup (GTK_LABEL (feed_label), markup);
#endif
        break;

      case GST_STATE_VOID_PENDING:
#ifdef GTK
        markup =
          g_markup_printf_escaped
          ("<span foreground=\"purple\" size=\"x-large\">%s</span>",
           feedname);
        gtk_label_set_markup (GTK_LABEL (feed_label), markup);
#endif
        break;

      case GST_STATE_NULL:
#ifdef GTK
        markup =
          g_markup_printf_escaped
          ("<span foreground=\"black\" size=\"x-large\">%s</span>",
           feedname);
        gtk_label_set_markup (GTK_LABEL (feed_label), markup);
#endif
        break;



      case GST_STATE_PAUSED:

        /*  query the stream duration, but this is not the perfect place for it */
#ifdef GST1
        if (!gst_element_query_duration
            (data->playbin2, fmt, &data->duration))
#else
          if (!gst_element_query_duration
              (data->playbin2, &fmt, &data->duration))
#endif

          {
            g_printerr ("Could not query current duration.\n");
          }
          else
          {
#ifdef GTK
            /* Set the range of the slider to the clip duration, in SECONDS */
            //g_printf ("state_changed_cb: slider range:: %i \n", data->duration / GST_SECOND);

            gtk_range_set_range (GTK_RANGE (data->slider), 0,
                (gdouble) data->duration / GST_SECOND);
#endif
          }
#ifdef GTK
        markup =
          g_markup_printf_escaped
          ("<span foreground=\"blue\" size=\"x-large\">%s</span>",
           feedname);
        gtk_label_set_markup (GTK_LABEL (feed_label), markup);
#endif
        break;

    }			//switch

    /* refresh ui to see if that makes position bar work ??? */
#ifdef GTK
    refresh_ui (data);
#endif
    if (old_state == GST_STATE_READY && new_state == GST_STATE_PAUSED)
    {
      /* For extra responsiveness, we refresh the GUI as soon as we reach the PAUSED state */
#ifdef GTK
      refresh_ui (data);
#endif
      /* autoplay on loading new file? */
      gst_element_set_state (data->playbin2, GST_STATE_PLAYING);
    }


  }
}

/* Extract metadata from all the streams and write it to the text widget in the GUI */
  static void
hark's avatar
hark committed
679
analyze_streams (PlayerData * data)
hark's avatar
hark committed
680
{
hark's avatar
hark committed
681
682
683
684
685
686
  auto gint i;
  auto GstTagList *tags;
  auto gchar *str, *total_str;
  auto guint rate;
  auto gint n_video, n_audio, n_text;

hark's avatar
hark committed
687
#ifdef GTK
hark's avatar
hark committed
688
  auto GtkTextBuffer *text;
hark's avatar
hark committed
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
  /* Clean current contents of the widget */
  text = gtk_text_view_get_buffer (GTK_TEXT_VIEW (data->streams_list));
  gtk_text_buffer_set_text (text, "", -1);
#endif
  /* Read some properties */
  g_object_get (data->playbin2, "n-video", &n_video, NULL);
  g_object_get (data->playbin2, "n-audio", &n_audio, NULL);
  g_object_get (data->playbin2, "n-text", &n_text, NULL);

  for (i = 0; i < n_video; i++)
  {
    tags = NULL;
    /* Retrieve the stream's video tags */
    g_signal_emit_by_name (data->playbin2, "get-video-tags", i, &tags);
    if (tags)
    {
      total_str = g_strdup_printf ("video stream %d:\n", i);
#ifdef GTK
      gtk_text_buffer_insert_at_cursor (text, total_str, -1);
#endif
      g_free (total_str);
      gst_tag_list_get_string (tags, GST_TAG_VIDEO_CODEC, &str);
      total_str =
        g_strdup_printf ("  codec: %s\n", str ? str : "unknown");
#ifdef GTK
      gtk_text_buffer_insert_at_cursor (text, total_str, -1);
#endif
      g_free (total_str);
      g_free (str);
#ifdef GST1
      gst_tag_list_unref (tags);
#else
      gst_tag_list_free (tags);
#endif

    }
  }

  for (i = 0; i < n_audio; i++)
  {
    tags = NULL;
    /* Retrieve the stream's audio tags */
    g_signal_emit_by_name (data->playbin2, "get-audio-tags", i, &tags);
    if (tags)
    {
      total_str = g_strdup_printf ("\naudio stream %d:\n", i);
#ifdef GTK
      gtk_text_buffer_insert_at_cursor (text, total_str, -1);
#endif
      g_free (total_str);
      if (gst_tag_list_get_string (tags, GST_TAG_AUDIO_CODEC, &str))
      {
        total_str = g_strdup_printf ("  codec: %s\n", str);
hark's avatar
hark committed
742
#ifdef GTK
hark's avatar
hark committed
743
        gtk_text_buffer_insert_at_cursor (text, total_str, -1);
hark's avatar
hark committed
744
#endif
hark's avatar
hark committed
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
        g_free (total_str);
        g_free (str);
      }
      if (gst_tag_list_get_string (tags, GST_TAG_LANGUAGE_CODE, &str))
      {
        total_str = g_strdup_printf ("  language: %s\n", str);
#ifdef GTK
        gtk_text_buffer_insert_at_cursor (text, total_str, -1);
#endif
        g_free (total_str);
        g_free (str);
      }
      if (gst_tag_list_get_uint (tags, GST_TAG_BITRATE, &rate))
      {
        total_str = g_strdup_printf ("  bitrate: %d\n", rate);
#ifdef GTK
        gtk_text_buffer_insert_at_cursor (text, total_str, -1);
#endif

        g_free (total_str);
      }
#ifdef GST1
      gst_tag_list_unref (tags);
#else
      gst_tag_list_free (tags);
#endif
    }
  }

  for (i = 0; i < n_text; i++)
  {
    tags = NULL;
    /* Retrieve the stream's subtitle tags */
    g_signal_emit_by_name (data->playbin2, "get-text-tags", i, &tags);
    if (tags)
    {
      total_str = g_strdup_printf ("\nsubtitle stream %d:\n", i);
#ifdef GTK
      gtk_text_buffer_insert_at_cursor (text, total_str, -1);
#endif
      g_free (total_str);
      if (gst_tag_list_get_string (tags, GST_TAG_LANGUAGE_CODE, &str))
      {
        total_str = g_strdup_printf ("  language: %s\n", str);
#ifdef GTK
        gtk_text_buffer_insert_at_cursor (text, total_str, -1);
#endif
        g_free (total_str);
        g_free (str);
      }
#ifdef GST1
      gst_tag_list_unref (tags);
#else
      gst_tag_list_free (tags);
#endif
    }
  }
}

/* This function is called when an "application" message is posted on the bus.
 * Here we retrieve the message posted by the tags_cb callback */
  static void
hark's avatar
hark committed
807
application_cb (GstBus * bus, GstMessage * msg, PlayerData * data)
hark's avatar
hark committed
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
{
  /* with gst1
   * player-0.10.c: In function ‘application_cb’:
   * player-0.10.c:714:45: error: ‘GstMessage’ has no member named ‘structure’
   *    if (g_strcmp0 (gst_structure_get_name (msg->structure), "tags-changed") ==
   *  */

#ifdef GST1
 /* 
  if (g_strcmp0 (gst_structure_get_name (msg->structure), "tags-changed") ==
      0)
  {
    // If the message is the "tags-changed" (only one we are currently issuing), update
    // the stream info GUI 
    analyze_streams (data);
  }
*/
#endif
}

hark's avatar
hark committed
828
static void init_media(PlayerData *data) 
hark's avatar
hark committed
829
{
hark's avatar
hark committed
830
831
832
833
  GstBus *bus;
  pData->duration = GST_CLOCK_TIME_NONE;
  pData->state = GST_STATE_NULL;
 
hark's avatar
hark committed
834
835
836
  /* Initialize GStreamer */
  gst_init (NULL, NULL);

hark's avatar
hark committed
837

hark's avatar
hark committed
838
 /* Create the elements */
hark's avatar
hark committed
839
#ifdef GST1
hark's avatar
hark committed
840
  pData->playbin2 = gst_element_factory_make ("playbin", "playbin2");
hark's avatar
hark committed
841
#else
hark's avatar
hark committed
842
  pData->playbin2 = gst_element_factory_make ("playbin2", "playbin2");
hark's avatar
hark committed
843
844
#endif

hark's avatar
hark committed
845
846
847
848
//  g_print ("sink: %s \n", sink);
  GstElement *videosink, *audiosink;
  videosink = gst_element_factory_make("xvimagesink", "xvimagesink");
//  videosink = gst_element_factory_make("fakesink", "fakesink");
hark's avatar
hark committed
849
850


hark's avatar
hark committed
851
#ifdef SNOWBIN
hark's avatar
hark committed
852
    /*set snowvideobin as sink */
hark's avatar
hark committed
853
854
    g_object_set (pData->playbin2, "video-sink",
    		    create_teebin(videosink, get_snowvideobin(pData->snow)), NULL);
hark's avatar
hark committed
855
856

    /*set snowaudiobin as sink */
hark's avatar
hark committed
857
    audiosink = get_snowaudiobin(pData->snow);      
hark's avatar
hark committed
858
    g_object_set (pData->playbin2, "audio-sink",audiosink, NULL);
hark's avatar
hark committed
859
#else
hark's avatar
hark committed
860
    
hark's avatar
hark committed
861
862
    g_print ("autosink \n");
    audiosink = gst_element_factory_make ("autoaudiosink", "autoaudiosink");
hark's avatar
hark committed
863
//    audiosink = gst_element_factory_make ("fakesink", "fakesink");
hark's avatar
hark committed
864

hark's avatar
hark committed
865
866
    g_object_set (pData->playbin2, "video-sink", videosink, NULL);
    g_object_set (pData->playbin2, "audio-sink", audiosink, NULL);
hark's avatar
hark committed
867
868
869
    
  g_print (" SNOWBIN is not defined, so we are not using it! \n ");
    
hark's avatar
hark committed
870
871
#endif

hark's avatar
hark committed
872
  if (!pData->playbin2)
hark's avatar
hark committed
873
874
875
876
877
878
879
880
881
  {
    g_printerr ("Not all elements could be created.\n");
    //return -1;
  }

  /* Set the URI to play */
  //g_object_set (globalData.playbin2, "uri", "http://docs.gstreamer.com/media/sintel_trailer-480p.webm", NULL);

  /* Connect to interesting signals in playbin2 */
hark's avatar
hark committed
882
883
884
885
886
887
888
889
890
891
  g_signal_connect (G_OBJECT (pData->playbin2), "video-tags-changed",
      (GCallback) tags_cb, pData);
  g_signal_connect (G_OBJECT (pData->playbin2), "audio-tags-changed",
      (GCallback) tags_cb, pData);
  g_signal_connect (G_OBJECT (pData->playbin2), "text-tags-changed",
      (GCallback) tags_cb, pData);
  g_signal_connect (G_OBJECT (pData->playbin2), "about-to-finish",
      (GCallback) aeos_cb, pData);
  g_signal_connect (G_OBJECT (pData->playbin2), "source-setup",
      (GCallback) source_cb, pData);
hark's avatar
hark committed
892
893

  /* Instruct the bus to emit signals for each received message, and connect to the interesting signals */
hark's avatar
hark committed
894
  bus = gst_element_get_bus (pData->playbin2);
hark's avatar
hark committed
895
896
  gst_bus_add_signal_watch (bus);
  g_signal_connect (G_OBJECT (bus), "message::error", (GCallback) error_cb,
hark's avatar
hark committed
897
      pData);
hark's avatar
hark committed
898
  g_signal_connect (G_OBJECT (bus), "message::eos", (GCallback) eos_cb,
hark's avatar
hark committed
899
      pData);
hark's avatar
hark committed
900
  g_signal_connect (G_OBJECT (bus), "message::state-changed",
hark's avatar
hark committed
901
      (GCallback) state_changed_cb, pData);
hark's avatar
hark committed
902
  g_signal_connect (G_OBJECT (bus), "message::application",
hark's avatar
hark committed
903
      (GCallback) application_cb, pData);
hark's avatar
hark committed
904
  g_signal_connect (G_OBJECT (bus), "message::about-to-finish",
hark's avatar
hark committed
905
      (GCallback) application_cb, pData);
hark's avatar
hark committed
906
907
908
909
  gst_object_unref (bus);


}
hark's avatar
hark committed
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
static void cleanup() {
  // this needs to be called from the main thread!
  
  //global pData
  g_print ("Cleaning up\n");

  g_print ("Deleting pipeline\n");
  gst_element_set_state (pData->playbin2, GST_STATE_NULL);
  gst_object_unref (GST_OBJECT (pData->playbin2));
//  g_source_remove (bus_watch_id);

#ifdef GTK
  gtk_main_quit();
#else
  g_main_loop_unref (pData->loop);
#endif

  free(pData);
  exit(0);
}


static int signal_handler(PlayerData * data) {

    printf(" \n Signal caught: %u \n ", 10);
    cleanup();
}

hark's avatar
hark committed
938
939
940
941
942
  int
main (int argc, char *argv[])
{
  /* deal with command line arguments */
  // feed, filename, 
hark's avatar
hark committed
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
  auto int opt = 0;
//  auto int feedid = 1;
  auto int audiosink = 23;
 g_unix_signal_add(SIGUSR1, (GSourceFunc)signal_handler, pData);
 g_unix_signal_add(SIGTERM, (GSourceFunc)signal_handler, pData);
 g_unix_signal_add(SIGINT, (GSourceFunc)signal_handler, pData);

 //test for valgrind
 malloc(10);

 
 // signal(15, signal_handler);

// signal(9, signal_handler);
// signal(15, signal_handler);
/*
 if (signal(SIGTERM, signal_handler)) {
   fputs("An error occurred while setting a signal handler.\n", stderr);
   return EXIT_FAILURE;
 }
*/
hark's avatar
hark committed
964

hark's avatar
hark committed
965
966
967
  /* Initialize our global data structures */
  pData = (PlayerData*)malloc(sizeof(struct PlayerData));

hark's avatar
hark committed
968
969
970
#ifdef SNOWBIN
  snowbin_init_snowData(pData);
#endif
hark's avatar
hark committed
971
972

  while ((opt = getopt (argc, argv, "i:f:a:s:")) != -1)
hark's avatar
hark committed
973
974
975
  {
    switch (opt)
    {
hark's avatar
hark committed
976
977
978
979
980
981
      // this only gets loaded without playlist
      #ifndef PL
      case 'i':
        pData->playuri = optarg;
        break;
      #endif
hark's avatar
hark committed
982
      case 'f':
hark's avatar
hark committed
983
        pData->feedid = atoi(optarg);
hark's avatar
hark committed
984
985
        break;
      case 's':
hark's avatar
hark committed
986
987
988
989
990
991
992
993
994
995
996
        if (strcmp(optarg,"snowmix") == 0)
        {
          pData->sink = SNOWMIX;

        } else if (strcmp(optarg,"rtmp") == 0)
        {
          pData->sink = RTMP;
        } else if (strcmp(optarg,"fake") == 0)
        {
          pData->sink = FAKE;
        }
hark's avatar
hark committed
997
        break;
hark's avatar
hark committed
998
#ifdef SNOWBIN
hark's avatar
hark committed
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
      case 'a':
        if (strcmp(optarg,"snowmix") == 0)
        {
          pData->snow->audiosink = SNOWMIX;

        } else if (strcmp(optarg,"jack") == 0)
        {
          pData->snow->audiosink = JACK;
        } else if (strcmp(optarg,"auto") == 0)
        {
          pData->snow->audiosink = AUTO;
        } else if (strcmp(optarg,"fake") == 0)
        {
          pData->snow->audiosink = FAKE;
        }
      break;
hark's avatar
hark committed
1015
#endif
hark's avatar
hark committed
1016
1017
1018
1019
1020
1021
1022
      case '?':
        /* Case when user enters the command as
         * $ ./cmd_exe -i
         */
        if (optopt == 'u')
        {
          printf ("\nMissing mandatory input option \n");
hark's avatar
hark committed
1023
          exit(1);
hark's avatar
hark committed
1024
1025
1026
1027
        }
        else
        {
          printf
hark's avatar
hark committed
1028
1029
            ("\nInvalid option received possible options: \n -a audiosink [snowmix|jack|auto|fake]  \n -i playuri \n -f feedid \n -s sink (not working) \n");
            exit(1);
hark's avatar
hark committed
1030
1031
1032
1033
1034
1035
        }
        break;
    }			//switch
  }				//while 


hark's avatar
hark committed
1036
1037


hark's avatar
hark committed
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
#ifdef GTK
#ifdef TT
   if( ! g_thread_supported() )
    g_thread_init( NULL );
    gdk_threads_init();
    gdk_threads_enter();
#endif

  /* Initialize GTK */
  gtk_init (&argc, &argv);
#endif
hark's avatar
hark committed
1049

hark's avatar
hark committed
1050
/* initialize gstreamer */
hark's avatar
hark committed
1051
1052
1053
init_media(pData);


hark's avatar
hark committed
1054
1055
1056

/* Create the GUI */
#ifdef GTK
hark's avatar
hark committed
1057
 create_ui (pData);
hark's avatar
hark committed
1058
1059
1060
1061
1062
#endif
#ifndef PL
 //load url
 load_uri(pData->playuri);
  gst_element_set_state (pData->playbin2, GST_STATE_PLAYING);
hark's avatar
hark committed
1063
1064

#endif
hark's avatar
hark committed
1065

hark's avatar
hark committed
1066
1067
  /* Register a function that GLib will call every second */
#ifdef GTK
hark's avatar
hark committed
1068
  g_timeout_add_seconds (1, (GSourceFunc)refresh_ui, pData);
hark's avatar
hark committed
1069
  /* register a function that will check all items in the playlist for availablitity */
hark's avatar
hark committed
1070
1071
1072
1073
1074
1075
1076
#ifdef PL
  g_timeout_add_seconds (15, (GSourceFunc) playlist_refresh, pData);
#endif

#endif

  gst_element_set_state (pData->playbin2, GST_STATE_PLAYING);
hark's avatar
hark committed
1077
1078
1079
1080
1081
1082
1083
1084
1085

  /* Start the GTK main loop. We will not regain control until gtk_main_quit is called. */
#ifdef GTK
  gtk_main ();

#ifdef TT
  gdk_threads_leave();
#endif
#else
hark's avatar
hark committed
1086
1087
1088
1089
1090
1091
//  printf("using this without gtk is on the todo list");
  pData->loop = g_main_loop_new (NULL, FALSE);
  g_print ("Running...\n");
  g_main_loop_run (pData->loop);


hark's avatar
hark committed
1092
#endif
hark's avatar
hark committed
1093
1094

//  cleanup();
hark's avatar
hark committed
1095
1096
  return 0;
}