2
* Copyright (C) 2012 Canonical Ltd
4
* This program is free software: you can redistribute it and/or modify
5
* it under the terms of the GNU General Public License version 3 as
6
* published by the Free Software Foundation.
8
* This program is distributed in the hope that it will be useful,
9
* but WITHOUT ANY WARRANTY; without even the implied warranty of
10
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11
* GNU General Public License for more details.
13
* You should have received a copy of the GNU General Public License
14
* along with this program. If not, see <http://www.gnu.org/licenses/>.
16
* Authored by Pawel Stolowski <pawel.stolowski@canonical.com>
21
namespace PreviewPlayer
23
errordomain PreviewPlayerError
28
public class PreviewPlayer
30
// Track states; keep in sync with libunity - TrackState enum
31
private enum TrackState
38
private static uint64 GST_STATE_QUERY_TIMEOUT = 1000000;
40
// gstreamer - playbin plugin flags; run 'gst-inspect-0.10 playbin2' for more flags & description
41
private static int GST_PLAYBIN_AUDIO = 0x02;
42
private static int GST_PLAYBIN_SOFT_VOLUME = 0x10;
44
private Gst.Pipeline gst_pipeline;
45
private Gst.Element gst_playbin;
46
private Gst.Element gst_sink;
47
private Gst.Bus gst_bus;
48
private uint timer_id;
51
public signal void progress (string uri, uint32 state, double value);
53
public PreviewPlayer () throws PreviewPlayerError
59
internal void init_gst () throws PreviewPlayerError
61
gst_pipeline = new Gst.Pipeline ("preview-player-pipeline");
62
gst_playbin = Gst.ElementFactory.make ("playbin2", "playbin2");
63
gst_sink = Gst.ElementFactory.make ("pulsesink", "sink");
66
gst_sink = Gst.ElementFactory.make ("alsasink", "sink");
69
throw new PreviewPlayerError.GST_INIT_FAILED ("Can't create backend sink");
73
gst_playbin.set_property ("audio-sink", gst_sink);
74
gst_playbin.set_property ("flags", GST_PLAYBIN_AUDIO | GST_PLAYBIN_SOFT_VOLUME);
76
gst_pipeline.add (gst_playbin);
78
gst_bus = gst_pipeline.get_bus ();
79
gst_bus.add_watch_full (GLib.Priority.DEFAULT, gst_bus_message_cb);
82
internal bool gst_bus_message_cb (Gst.Bus bus, Gst.Message message)
84
if (message.type == Gst.MessageType.EOS)
86
gst_pipeline.set_state (Gst.State.READY);
87
progress (uri, TrackState.PLAYING, 1.0f);
92
private bool progress_report_cb ()
94
Gst.State current_state;
95
Gst.State pending_state;
97
var status = gst_pipeline.get_state (out current_state, out pending_state, GST_STATE_QUERY_TIMEOUT);
99
if (status != Gst.StateChangeReturn.FAILURE)
101
if (current_state == Gst.State.PLAYING || pending_state == Gst.State.PLAYING)
103
progress (uri, TrackState.PLAYING, gst_progress ());
106
if (current_state == Gst.State.PAUSED || pending_state == Gst.State.PAUSED)
110
progress (uri, TrackState.STOPPED, 0.0f);
116
private double gst_progress ()
121
var fmt = Gst.Format.TIME;
122
if (gst_pipeline.query_position (ref fmt, out pos))
124
if (gst_pipeline.query_duration (ref fmt, out dur))
128
return pos / (double)dur;
135
private bool is_playing ()
137
Gst.State current_state;
138
Gst.State pending_state;
140
var status = gst_pipeline.get_state (out current_state, out pending_state, GST_STATE_QUERY_TIMEOUT);
141
return status != Gst.StateChangeReturn.FAILURE && (current_state == Gst.State.PLAYING || pending_state == Gst.State.PLAYING);
144
public void play (string uri)
146
// resume playback for same uri
149
Gst.State current_state;
150
Gst.State pending_state;
152
var status = gst_pipeline.get_state (out current_state, out pending_state, GST_STATE_QUERY_TIMEOUT);
153
if (status != Gst.StateChangeReturn.FAILURE)
155
if (current_state == Gst.State.PAUSED || pending_state == Gst.State.PAUSED)
157
gst_pipeline.set_state (Gst.State.PLAYING);
158
progress (uri, TrackState.PLAYING, gst_progress ());
164
// start new playback
167
GLib.Source.remove(timer_id);
170
gst_pipeline.set_state (Gst.State.NULL);
171
gst_playbin.set_property ("uri", uri);
172
gst_pipeline.set_state (Gst.State.PLAYING);
174
timer_id = GLib.Timeout.add_seconds (1, progress_report_cb);
179
Gst.State current_state;
180
Gst.State pending_state;
182
var status = gst_pipeline.get_state (out current_state, out pending_state, GST_STATE_QUERY_TIMEOUT);
183
if (status != Gst.StateChangeReturn.FAILURE)
185
if (current_state == Gst.State.PAUSED || current_state == Gst.State.PLAYING ||
186
pending_state == Gst.State.PAUSED || pending_state == Gst.State.PLAYING)
188
gst_pipeline.set_state (Gst.State.READY);
189
progress (uri, TrackState.STOPPED, 0.0f);
198
gst_pipeline.set_state (Gst.State.PAUSED);
199
progress (uri, TrackState.PAUSED, gst_progress ());
203
public void resume ()
205
Gst.State current_state;
206
Gst.State pending_state;
208
var status = gst_pipeline.get_state (out current_state, out pending_state, GST_STATE_QUERY_TIMEOUT);
209
if (status != Gst.StateChangeReturn.FAILURE)
211
if (current_state == Gst.State.PAUSED || pending_state == Gst.State.PAUSED)
213
gst_pipeline.set_state (Gst.State.PLAYING);
214
progress (uri, TrackState.PLAYING, gst_progress ());
219
public void pause_resume()
221
Gst.State current_state;
222
Gst.State pending_state;
224
var status = gst_pipeline.get_state (out current_state, out pending_state, GST_STATE_QUERY_TIMEOUT);
226
if (status != Gst.StateChangeReturn.FAILURE)
228
TrackState track_state = TrackState.STOPPED;
229
Gst.State gst_state = Gst.State.NULL;
231
if (current_state == Gst.State.PLAYING || pending_state == Gst.State.PLAYING)
233
track_state = TrackState.PAUSED;
234
gst_state = Gst.State.PAUSED;
235
gst_pipeline.set_state (Gst.State.PAUSED);
237
else if (current_state == Gst.State.PAUSED || pending_state == Gst.State.PAUSED)
239
track_state = TrackState.PLAYING;
240
gst_state = Gst.State.PLAYING;
243
if (gst_state != Gst.State.NULL)
245
gst_pipeline.set_state (gst_state);
246
progress (uri, track_state, gst_progress ());
251
public HashTable<string, Variant> get_video_file_props (string uri)
253
var props = new HashTable<string, Variant> (str_hash, str_equal);
257
var discoverer = new Gst.Discoverer (2 * 1000000000); //2 seconds
258
var discoverer_info = discoverer.discover_uri (uri);
259
var video_streams = discoverer_info.get_video_streams ();
261
// note: the container may have multiple video and audio streams, we just get properties of the first one
262
if (video_streams != null && video_streams.length () > 0)
264
var vstream = video_streams.nth_data (0);
265
props.insert ("width", new GLib.Variant.uint32 (vstream.get_width ()));
266
props.insert ("height", new GLib.Variant.uint32 (vstream.get_height ()));
267
props.insert ("codec", new GLib.Variant.string (Gst.pb_utils_get_codec_description (vstream.get_caps ())));
272
stderr.printf ("Failed to get video file properties for '%s': '%s'\n", uri, e.message);
b'\\ No newline at end of file'