summaryrefslogtreecommitdiff
path: root/src/media-engines/gstreamer/rygel-gst-data-source.vala
diff options
context:
space:
mode:
Diffstat (limited to 'src/media-engines/gstreamer/rygel-gst-data-source.vala')
-rw-r--r--src/media-engines/gstreamer/rygel-gst-data-source.vala243
1 files changed, 243 insertions, 0 deletions
diff --git a/src/media-engines/gstreamer/rygel-gst-data-source.vala b/src/media-engines/gstreamer/rygel-gst-data-source.vala
new file mode 100644
index 00000000..5202d7fa
--- /dev/null
+++ b/src/media-engines/gstreamer/rygel-gst-data-source.vala
@@ -0,0 +1,243 @@
+/*
+ * Copyright (C) 2012 Intel Corporation.
+ *
+ * Author: Jens Georg <jensg@openismus.com>
+ *
+ * This file is part of Rygel.
+ *
+ * Rygel is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * Rygel 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+using Gst;
+
+internal class Rygel.GstDataSource : Rygel.DataSource, GLib.Object {
+ internal dynamic Element src;
+ private Pipeline pipeline;
+ private HTTPSeek seek = null;
+ private GstSink sink;
+ private uint bus_watch_id;
+
+ public GstDataSource (string uri) {
+ this.src = GstUtils.create_source_for_uri (uri);
+ }
+
+ ~GstDataSource () {
+ this.sink.cancellable.cancel ();
+ this.pipeline.set_state (State.NULL);
+ }
+
+ public GstDataSource.from_element (Element element) {
+ this.src = element;
+ }
+
+ public void start (HTTPSeek? offsets) throws Error {
+ this.seek = offsets;
+ this.prepare_pipeline ("RygelGstDataSource", this.src);
+ if (this.seek != null) {
+ this.pipeline.set_state (State.PAUSED);
+ } else {
+ this.pipeline.set_state (State.PLAYING);
+ }
+ }
+
+ public void freeze () {
+ this.sink.freeze ();
+ }
+
+ public void thaw () {
+ this.sink.thaw ();
+ }
+
+ public void stop () {
+ // Unlock eventually frozen sink
+ this.sink.cancellable.cancel ();
+ this.pipeline.set_state (State.NULL);
+ Source.remove (this.bus_watch_id);
+ Idle.add ( () => { this.done (); return false; });
+ }
+
+ private void prepare_pipeline (string name,
+ Element src) throws Error {
+ this.sink = new GstSink (this, this.seek);
+
+ this.pipeline = new Pipeline (name);
+ if (pipeline == null) {
+ throw new DataSourceError.GENERAL
+ (_("Failed to create pipeline"));
+ }
+
+ this.pipeline.add_many (src, sink);
+
+ if (src.numsrcpads == 0) {
+ // Seems source uses dynamic pads, link when pad available
+ src.pad_added.connect (this.src_pad_added);
+ } else {
+ // static pads? easy!
+ if (!src.link (sink)) {
+ throw new GstError.LINK (_("Failed to link %s to %s"),
+ src.name,
+ sink.name);
+ }
+ }
+
+ // Bus handler
+ var bus = this.pipeline.get_bus ();
+ this.bus_watch_id = bus.add_watch (this.bus_handler);
+ }
+
+ private void src_pad_added (Element src, Pad src_pad) {
+ var caps = src_pad.get_caps_reffed ();
+
+ var sink = this.pipeline.get_by_name (GstSink.NAME);
+ Pad sink_pad;
+
+ dynamic Element depay = GstUtils.get_rtp_depayloader (caps);
+ if (depay != null) {
+ this.pipeline.add (depay);
+ if (!depay.link (sink)) {
+ critical (_("Failed to link %s to %s"),
+ depay.name,
+ sink.name);
+ this.done ();
+
+ return;
+ }
+
+ sink_pad = depay.get_compatible_pad (src_pad, caps);
+ } else {
+ sink_pad = sink.get_compatible_pad (src_pad, caps);
+ }
+
+ if (src_pad.link (sink_pad) != PadLinkReturn.OK) {
+ critical (_("Failed to link pad %s to %s"),
+ src_pad.name,
+ sink_pad.name);
+ this.done ();
+
+ return;
+ }
+
+ if (depay != null) {
+ depay.sync_state_with_parent ();
+ }
+ }
+
+ private bool bus_handler (Gst.Bus bus, Gst.Message message) {
+ bool ret = true;
+
+ if (message.type == MessageType.EOS) {
+ ret = false;
+ } else if (message.type == MessageType.STATE_CHANGED) {
+ if (message.src != this.pipeline) {
+ return true;
+ }
+
+ State old_state;
+ State new_state;
+
+ message.parse_state_changed (out old_state,
+ out new_state,
+ null);
+
+ if (old_state == State.NULL && new_state == State.READY) {
+ dynamic Element element = this.pipeline.get_by_name ("muxer");
+ if (element != null) {
+ var name = element.get_factory ().get_name ();
+ // Awesome gross hack, really.
+ if (name == "mp4mux") {
+ element.streamable = true;
+ element.fragment_duration = 1000;
+ }
+ }
+ }
+
+ if (this.seek != null) {
+ if (old_state == State.READY && new_state == State.PAUSED) {
+ if (this.perform_seek ()) {
+ this.pipeline.set_state (State.PLAYING);
+ }
+ }
+ }
+ } else {
+ GLib.Error err;
+ string err_msg;
+
+ if (message.type == MessageType.ERROR) {
+ message.parse_error (out err, out err_msg);
+ critical (_("Error from pipeline %s: %s"),
+ this.pipeline.name,
+ err_msg);
+
+ ret = false;
+ } else if (message.type == MessageType.WARNING) {
+ message.parse_warning (out err, out err_msg);
+ warning (_("Warning from pipeline %s: %s"),
+ this.pipeline.name,
+ err_msg);
+ }
+ }
+
+ if (!ret) {
+ Idle.add_full (Priority.DEFAULT, () => {
+ this.done ();
+
+ return false;
+ });
+ }
+
+ return ret;
+ }
+
+ private bool perform_seek () {
+ var stop_type = Gst.SeekType.NONE;
+ Format format;
+ var flags = SeekFlags.FLUSH;
+ int64 start, stop;
+
+ if (this.seek.seek_type == HTTPSeekType.TIME) {
+ format = Format.TIME;
+ flags |= SeekFlags.KEY_UNIT;
+ start = (this.seek.start) * Gst.USECOND;
+ stop = (this.seek.stop) * Gst.USECOND;
+ } else {
+ format = Format.BYTES;
+ flags |= SeekFlags.ACCURATE;
+ start = this.seek.start;
+ stop = this.seek.stop;
+ }
+
+ if (this.seek.stop > 0) {
+ stop_type = Gst.SeekType.SET;
+ }
+
+ if (!this.pipeline.seek (1.0,
+ format,
+ flags,
+ Gst.SeekType.SET,
+ start,
+ stop_type,
+ stop + 1)) {
+ warning (_("Failed to seek to offsets %lld:%lld"),
+ this.seek.start,
+ this.seek.stop);
+
+ this.error (new DataSourceError.SEEK_FAILED (_("Failed to seek")));
+
+ return false;
+ }
+
+ return true;
+ }
+}