implement gui/tui
[gst-scaletempo-demo-rj.git] / src / play.c
blob2058efa3e461520d63ca4f7adbef8463c9047beb
1 /* play.c
2 * Copyright (C) 2008 Rov Juvano <rovjuvano@users.sourceforge.net>
4 * This program is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation, either version 3 of the License, or
7 * (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 #include "play.h"
20 static gfloat scale = 1.0;
21 static gboolean ignore_state_changed;
23 gboolean
24 change_rate (GstElement * pipeline, gfloat rate, gboolean abs)
26 if (!rate)
27 return pause(pipeline);
29 scale = abs?rate:scale*rate;
30 gint64 pos;
31 GstFormat fmt = GST_FORMAT_TIME;
32 if ( !gst_element_query_position (pipeline, &fmt, &pos) ) {
33 g_print ("position query failed!\n"); // make DEBUG
34 return 0;
36 gboolean res = gst_element_seek (pipeline, scale,
37 GST_FORMAT_TIME, GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_ACCURATE,
38 GST_SEEK_TYPE_SET, pos,
39 GST_SEEK_TYPE_NONE, GST_CLOCK_TIME_NONE);
40 if (res) {
41 ignore_state_changed = TRUE;
43 return res;
46 static gboolean (*change_rate_around)(GstElement * pipeline, gfloat rate, gboolean abs) = change_rate;
48 void
49 set_change_rate_around (gboolean (*func)(GstElement * pipeline, gfloat rate, gboolean abs))
50 { change_rate_around = func; }
53 static void (*rate_changed_cb)(GstElement * object, gdouble new_rate, gpointer data) = NULL;
55 static gboolean
56 rate_changed_handler (GstElement * object, gdouble new_rate, gpointer data)
58 if (rate_changed_cb)
59 rate_changed_cb(object, new_rate, data);
60 return TRUE;
63 void
64 set_rate_changed_cb (void (*func)(GstElement * object, gdouble new_rate, gpointer data))
65 { rate_changed_cb = func; }
67 gboolean
68 seek (GstElement * pipeline, gint seconds, gboolean abs)
70 gint64 new_pos;
71 if (abs) {
72 new_pos = seconds * GST_SECOND;
73 } else {
74 gint64 pos;
75 GstFormat fmt = GST_FORMAT_TIME;
76 if ( !gst_element_query_position (pipeline, &fmt, &pos) ) {
77 g_print ("position query failed!\n"); // make DEBUG
78 return 0;
80 new_pos = pos + seconds * GST_SECOND;
83 if (new_pos < 0)
84 new_pos = 0;
85 gboolean res = gst_element_seek (pipeline, scale,
86 GST_FORMAT_TIME, GST_SEEK_FLAG_FLUSH,
87 GST_SEEK_TYPE_SET, new_pos,
88 GST_SEEK_TYPE_NONE, GST_CLOCK_TIME_NONE);
89 if (res) {
90 ignore_state_changed = TRUE;
92 return res;
95 static gboolean (*seek_around)(GstElement * pipeline, gint seconds, gboolean abs) = seek;
97 void
98 set_seek_around (gboolean (*func)(GstElement * pipeline, gint seconds, gboolean abs))
99 { seek_around = func; }
102 gboolean
103 play (GstElement * pipeline)
105 if (GST_STATE (pipeline) != GST_STATE_PLAYING) {
106 GstStateChangeReturn ret = gst_element_set_state (pipeline, GST_STATE_PLAYING);
107 if (ret == GST_STATE_CHANGE_FAILURE) {
108 return 0;
111 return 1;
114 static gboolean (*play_around)(GstElement * pipeline) = play;
116 void
117 set_play_around (gboolean (*func)(GstElement * pipeline))
118 { play_around = func; }
121 gboolean
122 pause (GstElement * pipeline)
124 if (GST_STATE (pipeline) != GST_STATE_PAUSED) {
125 GstStateChangeReturn ret = gst_element_set_state (pipeline, GST_STATE_PAUSED);
126 if (ret == GST_STATE_CHANGE_FAILURE) {
127 return 0;
130 return 1;
133 static gboolean (*pause_around)(GstElement * pipeline) = pause;
134 void
136 set_pause_around (gboolean (*func)(GstElement * pipeline))
137 { pause_around = func; }
140 gboolean
141 play_pause (GstElement * pipeline) {
142 if (GST_STATE (pipeline) == GST_STATE_PLAYING)
143 return pause_around(pipeline);
144 else
145 return play_around(pipeline);
148 static gboolean (*play_pause_around)(GstElement * pipeline) = play_pause;
149 void
151 set_play_pause_around (gboolean (*func)(GstElement * pipeline))
152 { play_pause_around = func; }
155 static void noop1 (gpointer t) {}
157 static void (*playing_started_cb)(GstElement * pipeline) = (void (*)(GstElement * pipeline))noop1;
159 void
160 set_playing_started_cb (void (*func)(GstElement * pipeline))
161 { playing_started_cb = func; }
163 static void (*playing_paused_cb)(GstElement * pipeline) = (void (*)(GstElement * pipeline))noop1;
165 void
166 set_playing_paused_cb (void (*func)(GstElement * pipeline))
167 { playing_paused_cb = func; }
170 static void stop (GstElement * pipeline) { exit(1); }
172 static void (*stop_cb)(GstElement * pipeline) = stop;
174 void
175 set_stop_cb(void (*func)(GstElement * pipeline))
176 { stop_cb = func; }
179 static void (*unknown_key_cb)(const gchar * key) = (void (*)(const gchar * key))noop1;
180 void set_unknown_key_cb (void(*func)(const gchar * key))
181 { unknown_key_cb = func; }
184 gboolean
185 get_position (GstElement * pipeline, gint64 * pos) {
186 GstFormat time_format = GST_FORMAT_TIME;
187 return gst_element_query_position (pipeline, &time_format, pos);
190 gboolean
191 get_duration (GstElement * pipeline, gint64 * dur) {
192 GstFormat time_format = GST_FORMAT_TIME;
193 return gst_element_query_duration (pipeline, &time_format, dur);
197 static gboolean is_shift_down = 0;
199 static GstElement * get_elder(GstPad * pad) {
200 GstElement *elder = GST_PAD_PARENT(pad);
201 GstElement *gp;
202 while ( ( gp = GST_ELEMENT_PARENT (elder) ) ) {
203 elder = gp;
205 return elder;
208 static gboolean
209 nav_event_cb (GstPad *pad, GstEvent * event)
211 if (GST_EVENT_TYPE (event) == GST_EVENT_NAVIGATION) {
212 const GstStructure *s = gst_event_get_structure (event);
213 const gchar *type = gst_structure_get_string (s, "event");
215 if (!g_str_has_prefix (type, "key-"))
216 return TRUE;
218 GstElement * pipeline = get_elder(pad);
219 const gchar *key = gst_structure_get_string (s, "key");
221 if (g_str_equal (type, "key-release")) {
222 if (g_str_equal (key, "bracketright"))
223 change_rate_around(pipeline, (is_shift_down) ? 2 : 1.059463094352953, FALSE);
224 else if (g_str_equal (key, "bracketleft"))
225 change_rate_around(pipeline, (is_shift_down) ? .5 : 0.9438743126816935, FALSE);
226 else if (g_str_equal (key, "BackSpace")) change_rate_around(pipeline, 1, TRUE);
227 else if (g_str_equal (key, "Right")) seek_around(pipeline, 5, FALSE);
228 else if (g_str_equal (key, "Down")) seek_around(pipeline, 30, FALSE);
229 else if (g_str_equal (key, "Left")) seek_around(pipeline, -5, FALSE);
230 else if (g_str_equal (key, "Up")) seek_around(pipeline, -30, FALSE);
231 else if (g_str_equal (key, "0")) seek_around(pipeline, 0, TRUE);
232 else if (g_str_has_prefix (key, "Shift")) is_shift_down = 0;
233 else if (g_str_equal (key, "space")) play_pause_around(pipeline);
234 else if (g_str_equal (key, "q")) stop_cb(pipeline);
235 else unknown_key_cb(key);
237 else if (g_str_equal (type, "key-press")) {
238 if (g_str_has_prefix (key, "Shift")) is_shift_down = 1;
242 return 1;
245 #define MAKE_ELEMENT(line, var, type, name) \
246 if ( !(var = gst_element_factory_make (type, name) ) ) { \
247 g_print ("element could not be created: %s/%s\n", type, name); \
248 return NULL; \
250 if (line) gst_bin_add (GST_BIN (line), var);
252 #define LINK_ELEMENTS(src, sink) \
253 if (!gst_element_link (src, sink)) { \
254 g_warning ("Failed to link elements: %s -> %s", \
255 GST_ELEMENT_NAME (src), GST_ELEMENT_NAME (sink) ); \
256 return NULL; \
259 static void
260 state_changed_cb (GstBus * bus, GstMessage * message, GstElement * pipeline)
262 if ( GST_ELEMENT (GST_MESSAGE_SRC (message)) != pipeline )
263 return;
266 const GstStructure *s = gst_message_get_structure (message);
267 g_print ("\nmessage from \"%s\" (%s): ",
268 GST_STR_NULL (gst_element_get_name (GST_MESSAGE_SRC (message))),
269 gst_message_type_get_name (GST_MESSAGE_TYPE (message)));
270 if (s) {
271 gchar *sstr = gst_structure_to_string (s);
272 g_print ("%s\n", sstr);
273 g_free (sstr);
274 } else {
275 g_print ("no message details\n");
279 GstState old, new, pending;
280 gst_message_parse_state_changed (message, &old, &new, &pending);
282 if (pending == GST_STATE_VOID_PENDING) {
283 if (ignore_state_changed) {
284 ignore_state_changed = FALSE;
285 } else if (new == GST_STATE_PAUSED) {
286 playing_paused_cb (pipeline);
287 } else if (new == GST_STATE_PLAYING) {
288 playing_started_cb(pipeline);
293 GstElement *
294 build_pipeline (const gchar * uri, const gchar * filter_name)
296 g_print ("Trying to play %s ...\n", uri);
297 g_print (" filter: %s\n", filter_name);
299 GstElement *playbin, *vsink;
300 MAKE_ELEMENT(NULL, playbin, "playbin", "playbin");
301 MAKE_ELEMENT(NULL, vsink, "gconfvideosink", "vsink");
302 g_object_set (G_OBJECT (playbin), "uri", uri, NULL);
303 g_object_set (G_OBJECT (playbin), "video_sink", vsink, NULL);
305 GstElement *audioline = gst_bin_new ("audioline");
306 GstElement *filter, *format, *resample, *asink;
307 MAKE_ELEMENT(audioline, filter, filter_name, "filter");
308 MAKE_ELEMENT(audioline, format, "audioconvert", "format");
309 MAKE_ELEMENT(audioline, resample, "audioresample", "resample");
310 MAKE_ELEMENT(audioline, asink, "gconfaudiosink", "audio_sink");
311 LINK_ELEMENTS(filter, format);
312 LINK_ELEMENTS(format, resample);
313 LINK_ELEMENTS(resample, asink);
315 if (g_signal_lookup ("rate-changed", G_OBJECT_TYPE (filter))) {
316 g_signal_connect (G_OBJECT (filter), "rate-changed", G_CALLBACK (rate_changed_handler), NULL);
317 } else {
318 g_print (" ** Warning: filter does not emit rate-change: UI will not display rate\n");
321 GstPad * ghostpad = gst_element_get_pad (filter, "sink");
322 gst_element_add_pad (audioline, gst_ghost_pad_new ("sink", ghostpad));
323 gst_object_unref (ghostpad);
324 g_object_set (G_OBJECT (playbin), "audio-sink", audioline, NULL);
326 gst_pad_add_event_probe(gst_element_get_pad(vsink, "sink"), G_CALLBACK (nav_event_cb), NULL);
328 GstBus *bus = gst_pipeline_get_bus (GST_PIPELINE (playbin));
329 gst_bus_add_signal_watch (bus);
330 g_signal_connect (bus, "message::state-changed", G_CALLBACK (state_changed_cb), playbin);
331 gst_object_unref (bus);
332 return playbin;