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;
}