All pastes #749313 Raw Edit

Unnamed

public text v1 · immutable
#749313 ·published 2007-10-25 16:39 UTC
rendered paste body
/* 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.
 */
/* FIXME: remove #if 0 code
 * FIXME: is the flush_seek part after sending the seek still needed?
 *
 */
#include <stdlib.h>
#include <glib.h>
#include <gtk/gtk.h>
#include <gst/gst.h>
#include <string.h>


/* configuration */

#define SOURCE "filesrc"
//#define SOURCE "gnomevfssrc"

#define ASINK "alsasink"
//#define ASINK "osssink"

#define VSINK "xvimagesink"
//#define VSINK "sdlvideosink"
//#define VSINK "ximagesink"
//#define VSINK "aasink"
//#define VSINK "cacasink"

//#define UPDATE_INTERVAL 500
//#define UPDATE_INTERVAL 100
#define UPDATE_INTERVAL 500


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


static GList *seekable_elements = NULL;
static GstElement *src, *decoder, *sink, *queue, *parse, *pipeline, *adcn, *volume, *demux;
static gint pipeline_type;
static const gchar *pipeline_spec;
static gint64 position = -1;
static gint64 duration = -1, s_position=0;
static GtkAdjustment *adjustment;
static GtkWidget *hscale;

static GstState state = GST_STATE_NULL;
static guint update_id = 0;
static gulong changed_id;
GtkWidget *play_button, *pause_button, *stop_button, *rwd_button, *fwd_button;


/* pipeline construction */

typedef struct
{
  const gchar *padname;
  GstPad *target;
  GstElement *bin;
}
dyn_link;

static GstElement *
gst_element_factory_make_or_warn (gchar * type, gchar * name)
{
  GstElement *element = gst_element_factory_make (type, name);

  if (!element) {
    g_warning ("Failed to create element %s of type %s", name, type);
  }

  return element;
}

static void
dynamic_link (GstPadTemplate * templ, GstPad * newpad, gpointer data)
{
  gchar *padname;
  dyn_link *connect = (dyn_link *) data;

  padname = gst_pad_get_name (newpad);

  if (connect->padname == NULL || !strcmp (padname, connect->padname)) {
    if (connect->bin)
      gst_bin_add (GST_BIN (pipeline), connect->bin);
    gst_pad_link (newpad, connect->target);
  }
  g_free (padname);
}

static void
setup_dynamic_link (GstElement * element, const gchar * padname,
    GstPad * target, GstElement * bin)
{
  dyn_link *connect;

  connect = g_new0 (dyn_link, 1);
  connect->padname = g_strdup (padname);
  connect->target = target;
  connect->bin = bin;

  g_signal_connect (G_OBJECT (element), "pad-added", G_CALLBACK (dynamic_link),
      connect);
}


static void new_pad (GstElement *element, GstPad *pad, gpointer data)
{
  GstPad* sinkpad = NULL;  

  sinkpad = gst_element_get_pad(parse, "sink");
  if(sinkpad != NULL)
  {
	gst_pad_link (pad, sinkpad);
    	gst_object_unref (GST_OBJECT(sinkpad));
  }      	
}

static GstElement *
make_mp3_pipeline (const gchar * location)
{

  pipeline = gst_pipeline_new ("app");

  src = gst_element_factory_make_or_warn (SOURCE, "src");
  demux = gst_element_factory_make_or_warn ("id3demux", "demux");
  parse = gst_element_factory_make_or_warn ("mp3parse", "parse");
  decoder = gst_element_factory_make_or_warn ("mad", "decoder");
  adcn = gst_element_factory_make_or_warn ("audioconvert", "adcn");
  volume = gst_element_factory_make_or_warn ("volume", "volume_e");
  queue = gst_element_factory_make_or_warn ("queue", "queue");
  sink = gst_element_factory_make_or_warn (ASINK, "sink");

  seekable_elements = g_list_prepend (seekable_elements, sink);
  seekable_elements = g_list_prepend (seekable_elements, parse);
  seekable_elements = g_list_prepend (seekable_elements, decoder);

  g_object_set (G_OBJECT (src), "location", location, NULL);
  //g_object_set (G_OBJECT (osssink), "fragment", 0x00180008, NULL);

  gst_bin_add (GST_BIN (pipeline), src);
  gst_bin_add (GST_BIN (pipeline), demux);
  gst_bin_add (GST_BIN (pipeline), parse);
  gst_bin_add (GST_BIN (pipeline), decoder);
  gst_bin_add (GST_BIN (pipeline), adcn);
  gst_bin_add (GST_BIN (pipeline), volume);
  //gst_bin_add (GST_BIN (pipeline), queue);
  gst_bin_add (GST_BIN (pipeline), sink);
  g_print("linkinggg\n");
  gst_element_link(src, demux);
  g_signal_connect (demux, "pad-added", G_CALLBACK (new_pad), NULL); 
  gst_element_link (parse, decoder);
  gst_element_link (decoder, adcn);
  gst_element_link (adcn, volume);
  //gst_element_link (volume, queue);
  //gst_element_link (queue, sink);
  gst_element_link(volume, sink);  

  return pipeline;
}



typedef struct
{
  gchar *name;
  GstElement *(*func) (const gchar * location);
}
Pipeline;

static Pipeline pipelines[] = {
  {"mp3", make_mp3_pipeline},
  {NULL, NULL},
};

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

/* ui callbacks and helpers */

static gchar *
format_value (GtkScale * scale, gdouble value)
{ 
  gint64 real;
  gint64 seconds;

  real = value * duration / 100;
  seconds = (gint64) real / GST_SECOND;

  return g_strdup_printf ("%02" G_GINT64_FORMAT ":%02" G_GINT64_FORMAT, seconds / 60, seconds % 60);
  
}

typedef struct
{
  const gchar *name;
  const GstFormat format;
}
seek_format;

static seek_format seek_formats[] = {
  {"tim", GST_FORMAT_TIME},
  {"byt", GST_FORMAT_BYTES},
  {"buf", GST_FORMAT_BUFFERS},
  {"def", GST_FORMAT_DEFAULT},
  {NULL, 0},
};

static void
fwd_cb (GtkWidget * widget);
static void
rwd_cb (GtkWidget * widget);

static void
set_scale (gdouble value)
{
  g_signal_handlers_block_by_func (fwd_button, fwd_cb, pipeline);
  g_signal_handlers_block_by_func (rwd_button, rwd_cb, pipeline);
  gtk_adjustment_set_value (adjustment, value);
  g_signal_handlers_unblock_by_func (fwd_button, fwd_cb, pipeline);
  g_signal_handlers_unblock_by_func (rwd_button, rwd_cb, pipeline);
  gtk_widget_queue_draw (hscale);
}

static void
update_scale (void)
{
  g_print("entryy\n");

  GstFormat format = GST_FORMAT_TIME;

  //GstElement *element = GST_ELEMENT (seekable_elements->data);

  //gst_element_query_position (element, &format, &position);
  //gst_element_query_duration (element, &format, &duration);

  gst_element_query_position (pipeline, &format, &position); 
  gst_element_query_duration (pipeline, &format, &duration);
 
  if (position >= duration)
    duration = position;

  if (duration > 0) {
    set_scale (position * 100.0 / duration);
  }

  g_print("exitt\n");
  
}

static void
set_update_scale (gboolean active)
{
  if (active) {
    if (update_id == 0) { 
      update_id =
          g_timeout_add (UPDATE_INTERVAL, (GtkFunction) update_scale, pipeline);
    }
  } else {
    if (update_id) {
      g_source_remove (update_id);
      update_id = 0;
    }
  }
}


static gboolean
send_event (GstEvent * event)
{
  gboolean res = FALSE;

  GList *walk = seekable_elements;

 /* while (walk) 
  {
	GstElement *seekable = GST_ELEMENT (walk->data);

	gst_event_ref (event);
	res = gst_element_send_event (seekable, event);

	walk = g_list_next (walk);
  }*/

  res = gst_element_send_event (pipeline, event);

 // gst_event_unref (event);
  return res;
}

static void
fwd_cb (GtkWidget * widget)
{
  g_print("%s entry\n", __FUNCTION__);
  gint64 real;
  gboolean res = FALSE;
  GstEvent *s_event;
  GstSeekFlags flags;
  gdouble value;  
  GstState cur, pen;
  GstFormat format = GST_FORMAT_TIME;

  //gst_element_set_state (pipeline, GST_STATE_PAUSED);
  /*while(1)
  {
     gst_element_get_state (GST_ELEMENT (pipeline), &cur, &pen, SEEK_TIMEOUT);
     if(cur == GST_STATE_PAUSED && pen == GST_STATE_VOID_PENDING)
     {
	     g_print("pipeline state changed to paused\n");
             break;
     }
  }*/

  set_update_scale (FALSE);

  update_scale();

  flags = 0;
  flags |= GST_SEEK_FLAG_FLUSH;
  
  gst_element_query_position (pipeline, &format, &position); 
  gst_element_query_duration (pipeline, &format, &duration);

  g_print("before fwd seek %"GST_TIME_FORMAT"\n", GST_TIME_ARGS (position));
  s_position = position;

  real = position + 5 * GST_SECOND;
  if(real >= duration)
	real = duration; 
  s_event = gst_event_new_seek (1.0,
        GST_FORMAT_TIME, flags, GST_SEEK_TYPE_SET, real ,
        GST_SEEK_TYPE_SET, -1);

  res = send_event (s_event);

  if (res) {
      //gst_pipeline_set_new_stream_time (GST_PIPELINE (pipeline), 0);
      /*gst_element_get_state (GST_ELEMENT (pipeline), NULL, NULL, SEEK_TIMEOUT);

      if(state == GST_STATE_PLAYING)
          gst_element_set_state (pipeline, GST_STATE_PLAYING);

      while(1)
      {
         gst_element_get_state (GST_ELEMENT (pipeline), &cur, &pen, SEEK_TIMEOUT);
         if(cur == state && pen == GST_STATE_VOID_PENDING)
	 {
	     g_print("pipeline state changed to playing\n");
             break;
         }
      }*/
	
      GstBus* bus = gst_pipeline_get_bus (GST_PIPELINE (pipeline));
      GstMessage *msg;
      msg=gst_bus_poll(bus, GST_MESSAGE_ASYNC_DONE, -1);
      gst_message_unref(msg);
  
      g_print("async is done\n");

      gst_element_query_position (pipeline, &format, &position); 
      gst_element_query_duration (pipeline, &format, &duration);

      g_print("after fwd seek %"GST_TIME_FORMAT"\n", GST_TIME_ARGS (position));
      if(s_position+ (gint64)4 * GST_SECOND >= position )
      {
           g_print("********************************************mistake %lld %lld\n", s_position, position);
           //exit(1);
      }
      s_position = 0;
      

      //if(state == GST_STATE_PLAYING)
      	//gst_element_set_state (pipeline, GST_STATE_PLAYING);
  } else {
    g_print ("seek failed\n");
    set_update_scale (TRUE);
  }

  g_print("%s exit\n", __FUNCTION__);

}


static void
rwd_cb (GtkWidget * widget)
{
  g_print("%s entry\n", __FUNCTION__);
  gint64 real;
  gboolean res = FALSE;
  GstEvent *s_event;
  GstSeekFlags flags;
  gdouble value;
  GstFormat format = GST_FORMAT_TIME;
  GstState cur, pen;

  //gst_element_set_state (pipeline, GST_STATE_PAUSED);
  /*while(1)
  {
     gst_element_get_state (GST_ELEMENT (pipeline), &cur, &pen, SEEK_TIMEOUT);
     if(cur == GST_STATE_PAUSED && pen == GST_STATE_VOID_PENDING)
     {
	     g_print("pipeline state changed to paused\n");
             break;
     }
  }*/

  set_update_scale (FALSE);

  update_scale();

  gst_element_query_position (pipeline, &format, &position); 
  gst_element_query_duration (pipeline, &format, &duration);

  g_print("before rwd seek %"GST_TIME_FORMAT"\n", GST_TIME_ARGS (position));
  s_position = position;

  flags = 0;
  flags |= GST_SEEK_FLAG_FLUSH;

  real = position - 5 * GST_SECOND; 
  if(real <= 0)
	real = 0;
  s_event = gst_event_new_seek (1.0,
        GST_FORMAT_TIME, flags, GST_SEEK_TYPE_SET, real,
        GST_SEEK_TYPE_SET, -1);

  res = send_event (s_event);

  if (res) {
      //gst_pipeline_set_new_stream_time (GST_PIPELINE (pipeline), 0);
      /*gst_element_get_state (GST_ELEMENT (pipeline), &cur, &pen, SEEK_TIMEOUT);
      
      if(state == GST_STATE_PLAYING)
          gst_element_set_state (pipeline, GST_STATE_PLAYING);

      while(1)
      {
         gst_element_get_state (GST_ELEMENT (pipeline), &cur, &pen, SEEK_TIMEOUT);
         if(cur == state && pen == GST_STATE_VOID_PENDING)
	 {
	     g_print("pipeline state changed to playing\n");
             break;
	 }
      }*/
    
      GstBus* bus = gst_pipeline_get_bus (GST_PIPELINE (pipeline));
      GstMessage *msg;
      msg=gst_bus_poll(bus, GST_MESSAGE_ASYNC_DONE, -1);
      gst_message_unref(msg);
      g_print("async is done\n");

      gst_element_query_position (pipeline, &format, &position);
      gst_element_query_duration (pipeline, &format, &duration);
      
      g_print("after rwd seek %"GST_TIME_FORMAT"\n", GST_TIME_ARGS (position));
      if(position+1000000000 >= s_position )
      {
           g_print("********************************************mistake %lld %lld\n", s_position, position); 
           //exit(1);
      } 
      s_position = 0;
      
      //if(state == GST_STATE_PLAYING)
	//gst_element_set_state (pipeline, GST_STATE_PLAYING);
  } else {
    g_print ("seek failed\n");
    set_update_scale (TRUE);
  }
  g_print("%s exit\n", __FUNCTION__);
}

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

  if (state != GST_STATE_PLAYING) {
    g_print ("PLAY pipeline\n");
    ret = gst_element_set_state (pipeline, GST_STATE_PLAYING);
    if (ret == GST_STATE_CHANGE_FAILURE)
      goto failed;

    state = GST_STATE_PLAYING;
  }
  return;

failed:
  {
    g_print ("PLAY failed\n");
  }
}

static void
pause_cb (GtkButton * button, gpointer data)
{
  if (state != GST_STATE_PAUSED) {
    GstStateChangeReturn ret;

    g_print ("PAUSE pipeline\n");
    ret = gst_element_set_state (pipeline, GST_STATE_PAUSED);
    if (ret == GST_STATE_CHANGE_FAILURE)
      goto failed;

    state = GST_STATE_PAUSED;
  }
  return;

failed:
  {
    g_print ("PAUSE failed\n");
  }
}

static void
stop_cb (GtkButton * button, gpointer data)
{
  if (state != GST_STATE_READY) {
    GstStateChangeReturn ret;

    g_print ("READY pipeline\n");
    ret = gst_element_set_state (pipeline, GST_STATE_READY);
    if (ret == GST_STATE_CHANGE_FAILURE)
      goto failed;

    set_scale (0.0);

    state = GST_STATE_READY;

    /* 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);

      pipeline = pipelines[pipeline_type].func (pipeline_spec);
      g_assert (pipeline);
      gst_element_set_state (pipeline, GST_STATE_READY);
    }
  }
  return;

failed:
  {
    g_print ("STOP failed\n");
  }
}


static void
msg_eos (GstBus * bus, GstMessage * message, GstPipeline * pipeline)
{
  g_print("&&&&&&&&&&&&&&&&&&&GOT AN EOS\n");
  GstStateChangeReturn ret;

  g_print ("READY pipeline\n");
  ret = gst_element_set_state (GST_ELEMENT (pipeline), GST_STATE_READY);
  if (ret == GST_STATE_CHANGE_FAILURE)
    g_print ("READY failed\n");

  set_scale (0.0);

  state = GST_STATE_READY;
}

static void
msg_state_changed (GstBus * bus, GstMessage * message, GstPipeline * pipeline)
{
  const GstStructure *s;

  s = gst_message_get_structure (message);

  /* We only care about state changed on the pipeline */
  if (s && GST_MESSAGE_SRC (message) == GST_OBJECT_CAST (pipeline)) {
    GstState old, new, pending;

    gst_message_parse_state_changed (message, &old, &new, &pending);
    g_print("state changed by  %s , from %s to %s\n", GST_ELEMENT_NAME(GST_MESSAGE_SRC(message)), gst_element_state_get_name(old), gst_element_state_get_name(new));

    /* When state of the pipeline changes to playing we start updating scale */
    if (new == GST_STATE_PLAYING) { 
      set_update_scale (TRUE);
    } else {
      set_update_scale (FALSE);
    }
  }
}

static void
msg_async_done (GstBus * bus, GstMessage * message, GstPipeline * pipeline)
{
  g_print("async done by %s\n", GST_ELEMENT_NAME(GST_MESSAGE_SRC (message)));
}

static void
print_usage (int argc, char **argv)
{
  gint i;

  g_print ("usage: %s <type> <filename>\n", argv[0]);
  g_print ("   possible types:\n");

  for (i = 0; i < NUM_TYPES; i++) {
    g_print ("     %d = %s\n", i, pipelines[i].name);
  }
}

int
main (int argc, char **argv)
{
  GtkWidget *window, *hbox, *vbox; //*flagtable;
  GtkWidget *accurate_checkbox, *key_checkbox, *loop_checkbox, *flush_checkbox;
  GtkWidget *scrub_checkbox, *play_scrub_checkbox, *rate_spinbutton;
  GtkWidget *rate_label;
  GtkTooltips *tips;
  GError *err = NULL;

  if (!g_thread_supported ())
    g_thread_init (NULL);

  gtk_init (&argc, &argv);
  gst_init (&argc, &argv);

  if (argc != 3) {
    print_usage (argc, argv);
    exit (-1);
  }

  pipeline_type = atoi (argv[1]);

  if (pipeline_type < 0 || pipeline_type >= NUM_TYPES) {
    print_usage (argc, argv);
    exit (-1);
  }

  pipeline_spec = argv[2];

  pipeline = pipelines[pipeline_type].func (pipeline_spec);
  g_assert (pipeline);

  /* initialize gui elements ... */
  tips = gtk_tooltips_new ();
  window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
  hbox = gtk_hbox_new (FALSE, 0);
  vbox = gtk_vbox_new (FALSE, 0);
  //flagtable = gtk_table_new (4, 2, FALSE);
  gtk_container_set_border_width (GTK_CONTAINER (vbox), 3);

  /* media controls */
  play_button = gtk_button_new_from_stock (GTK_STOCK_MEDIA_PLAY);
  pause_button = gtk_button_new_from_stock (GTK_STOCK_MEDIA_PAUSE);
  stop_button = gtk_button_new_from_stock (GTK_STOCK_MEDIA_STOP);
  fwd_button =  gtk_button_new_from_stock (GTK_STOCK_MEDIA_FORWARD);
  rwd_button = gtk_button_new_from_stock (GTK_STOCK_MEDIA_REWIND);

  /* seek bar */
  adjustment =
      GTK_ADJUSTMENT (gtk_adjustment_new (0.0, 0.00, 100.0, 0.0, 0.0, 0.0));
  hscale = gtk_hscale_new (adjustment);
  gtk_scale_set_digits (GTK_SCALE (hscale), 2);
  gtk_range_set_update_policy (GTK_RANGE (hscale), GTK_UPDATE_CONTINUOUS);
  gtk_signal_connect (GTK_OBJECT (hscale),
      "format_value", G_CALLBACK (format_value), pipeline);

  /* do the packing stuff ... */
  gtk_window_set_default_size (GTK_WINDOW (window), 250, 96);
  gtk_container_add (GTK_CONTAINER (window), vbox);
  gtk_container_add (GTK_CONTAINER (vbox), hbox);
  gtk_box_pack_start (GTK_BOX (hbox), play_button, FALSE, FALSE, 2);
  gtk_box_pack_start (GTK_BOX (hbox), pause_button, FALSE, FALSE, 2);
  gtk_box_pack_start (GTK_BOX (hbox), stop_button, FALSE, FALSE, 2);
  gtk_box_pack_start (GTK_BOX (hbox), fwd_button, FALSE, FALSE, 2);
  gtk_box_pack_start (GTK_BOX (hbox), rwd_button, FALSE, FALSE, 2);
  //gtk_box_pack_start (GTK_BOX (hbox), flagtable, FALSE, FALSE, 2);
  gtk_box_pack_start (GTK_BOX (vbox), hscale, TRUE, TRUE, 2);

  /* connect things ... */
  g_signal_connect (G_OBJECT (play_button), "clicked", G_CALLBACK (play_cb),
      pipeline);
  g_signal_connect (G_OBJECT (pause_button), "clicked", G_CALLBACK (pause_cb),
      pipeline);
  g_signal_connect (G_OBJECT (stop_button), "clicked", G_CALLBACK (stop_cb),
      pipeline);
  g_signal_connect (G_OBJECT (fwd_button), "clicked", G_CALLBACK (fwd_cb),
      pipeline);
  g_signal_connect (G_OBJECT (rwd_button), "clicked", G_CALLBACK (rwd_cb),
      pipeline);
  g_signal_connect (G_OBJECT (window), "destroy", gtk_main_quit, NULL);

  /* show the gui. */
  gtk_widget_show_all (window);

  {
    GstBus *bus;    

    bus = gst_pipeline_get_bus (GST_PIPELINE (pipeline));
    gst_bus_add_signal_watch_full (bus, G_PRIORITY_HIGH);

    g_signal_connect (bus, "message::eos", (GCallback) msg_eos, pipeline);
    g_signal_connect (bus, "message::state-changed",
        (GCallback) msg_state_changed, pipeline);
    g_signal_connect (bus, "message::async-done",
        (GCallback) msg_async_done, pipeline);
  }

  gtk_main ();

  g_print ("NULL pipeline\n");
  gst_element_set_state (pipeline, GST_STATE_NULL);

  g_print ("free pipeline\n");
  gst_object_unref (pipeline);

  return 0;
}