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/>.
20 static gfloat scale
= 1.0;
21 static gboolean ignore_state_changed
;
24 change_rate (GstElement
* pipeline
, gfloat rate
, gboolean abs
)
27 return pause(pipeline
);
29 scale
= abs
?rate
:scale
*rate
;
31 GstFormat fmt
= GST_FORMAT_TIME
;
32 if ( !gst_element_query_position (pipeline
, &fmt
, &pos
) ) {
33 g_print ("position query failed!\n"); // make DEBUG
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
);
41 ignore_state_changed
= TRUE
;
46 static gboolean (*change_rate_around
)(GstElement
* pipeline
, gfloat rate
, gboolean abs
) = change_rate
;
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
;
56 rate_changed_handler (GstElement
* object
, gdouble new_rate
, gpointer data
)
59 rate_changed_cb(object
, new_rate
, data
);
64 set_rate_changed_cb (void (*func
)(GstElement
* object
, gdouble new_rate
, gpointer data
))
65 { rate_changed_cb
= func
; }
68 seek (GstElement
* pipeline
, gint seconds
, gboolean abs
)
72 new_pos
= seconds
* GST_SECOND
;
75 GstFormat fmt
= GST_FORMAT_TIME
;
76 if ( !gst_element_query_position (pipeline
, &fmt
, &pos
) ) {
77 g_print ("position query failed!\n"); // make DEBUG
80 new_pos
= pos
+ seconds
* GST_SECOND
;
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
);
90 ignore_state_changed
= TRUE
;
95 static gboolean (*seek_around
)(GstElement
* pipeline
, gint seconds
, gboolean abs
) = seek
;
98 set_seek_around (gboolean (*func
)(GstElement
* pipeline
, gint seconds
, gboolean abs
))
99 { seek_around
= func
; }
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
) {
114 static gboolean (*play_around
)(GstElement
* pipeline
) = play
;
117 set_play_around (gboolean (*func
)(GstElement
* pipeline
))
118 { play_around
= func
; }
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
) {
133 static gboolean (*pause_around
)(GstElement
* pipeline
) = pause
;
136 set_pause_around (gboolean (*func
)(GstElement
* pipeline
))
137 { pause_around
= func
; }
141 play_pause (GstElement
* pipeline
) {
142 if (GST_STATE (pipeline
) == GST_STATE_PLAYING
)
143 return pause_around(pipeline
);
145 return play_around(pipeline
);
148 static gboolean (*play_pause_around
)(GstElement
* pipeline
) = play_pause
;
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
;
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
;
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
;
175 set_stop_cb(void (*func
)(GstElement
* pipeline
))
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
; }
185 get_position (GstElement
* pipeline
, gint64
* pos
) {
186 GstFormat time_format
= GST_FORMAT_TIME
;
187 return gst_element_query_position (pipeline
, &time_format
, pos
);
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
);
202 while ( ( gp
= GST_ELEMENT_PARENT (elder
) ) ) {
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-"))
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;
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); \
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) ); \
260 state_changed_cb (GstBus
* bus
, GstMessage
* message
, GstElement
* pipeline
)
262 if ( GST_ELEMENT (GST_MESSAGE_SRC (message
)) != pipeline
)
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)));
271 gchar *sstr = gst_structure_to_string (s);
272 g_print ("%s\n", sstr);
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
);
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
);
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
);