core,test,i18n: Rename LiveResponse to HTTPGstResponse
authorZeeshan Ali (Khattak) <zeeshanak@gnome.org>
Fri, 14 Jan 2011 21:07:00 +0000 (23:07 +0200)
committerZeeshan Ali (Khattak) <zeeshanak@gnome.org>
Fri, 14 Jan 2011 23:55:38 +0000 (01:55 +0200)
po/POTFILES.in
po/POTFILES.skip
src/rygel/Makefile.am
src/rygel/rygel-http-gst-response.vala [new file with mode: 0644]
src/rygel/rygel-http-identity-handler.vala
src/rygel/rygel-http-transcode-handler.vala
tests/Makefile.am
tests/rygel-http-gst-response-test.vala [moved from tests/rygel-live-response-test.vala with 75% similarity]
tests/rygel-http-gst-response.vala [moved from src/rygel/rygel-live-response.vala with 98% similarity]
tests/rygel-live-response.vala [deleted symlink]

index 96badf2..a169d54 100644 (file)
@@ -100,7 +100,7 @@ src/rygel/rygel-item-creator.vala
 src/rygel/rygel-item-destroyer.vala
 src/rygel/rygel-l16-transcoder-bin.vala
 src/rygel/rygel-l16-transcoder.vala
-src/rygel/rygel-live-response.vala
+src/rygel/rygel-http-gst-response.vala
 src/rygel/rygel-log-handler.vala
 src/rygel/rygel-logical-expression.vala
 src/rygel/rygel-main.vala
index 709ba2d..2fba594 100644 (file)
@@ -59,7 +59,7 @@ src/rygel/rygel-import-resource.c
 src/rygel/rygel-item-creator.c
 src/rygel/rygel-item-destroyer.c
 src/rygel/rygel-l16-transcoder-bin.c
-src/rygel/rygel-live-response.c
+src/rygel/rygel-http-gst-response.c
 src/rygel/rygel-log-handler.c
 src/rygel/rygel-main.c
 src/rygel/rygel-media-container.c
@@ -101,7 +101,7 @@ tests/rygel-gst-utils.c
 tests/rygel-http-byte-seek.c
 tests/rygel-http-item-uri.c
 tests/rygel-http-time-seek.c
-tests/rygel-live-response.c
+tests/rygel-http-gst-response.c
 tests/rygel-seekable-response.c
 tests/rygel-http-byte-seek_http-get.c
 tests/rygel-http-get.c
index c779988..769419f 100644 (file)
@@ -69,7 +69,7 @@ VAPI_SOURCE_FILES = rygel-configuration.vala \
                    rygel-http-byte-seek.vala \
                    rygel-http-time-seek.vala \
                    rygel-http-response.vala \
-                   rygel-live-response.vala \
+                   rygel-http-gst-response.vala \
                    rygel-seekable-response.vala \
                    rygel-resource-info.vala \
                    rygel-icon-info.vala \
diff --git a/src/rygel/rygel-http-gst-response.vala b/src/rygel/rygel-http-gst-response.vala
new file mode 100644 (file)
index 0000000..f15660e
--- /dev/null
@@ -0,0 +1,269 @@
+/*
+ * Copyright (C) 2008 Zeeshan Ali (Khattak) <zeeshanak@gnome.org>.
+ * Copyright (C) 2008,2011 Nokia Corporation.
+ *
+ * Author: Zeeshan Ali (Khattak) <zeeshanak@gnome.org>
+ *                               <zeeshan.ali@nokia.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 GUPnP;
+using Gst;
+
+internal class Rygel.HTTPGstResponse : Rygel.HTTPResponse {
+    private const string SINK_NAME = "fakesink";
+    // High and low threshold for number of buffered chunks
+    private const uint MAX_BUFFERED_CHUNKS = 32;
+    private const uint MIN_BUFFERED_CHUNKS = 4;
+
+    private Pipeline pipeline;
+
+    private HTTPSeek time_range;
+
+    private int64 buffered;
+    private bool out_of_sync;
+
+    public HTTPGstResponse (Soup.Server  server,
+                            Soup.Message msg,
+                            string       name,
+                            Element      src,
+                            HTTPSeek?    time_range,
+                            Cancellable? cancellable) throws Error {
+        base (server, msg, false, cancellable);
+
+        this.msg.response_headers.set_encoding (Soup.Encoding.EOF);
+
+        this.prepare_pipeline (name, src);
+        this.time_range = time_range;
+
+        this.buffered = 0;
+        this.out_of_sync = false;
+    }
+
+    public override async void run () {
+        this.msg.wrote_chunk.connect (this.on_wrote_chunk);
+
+        // Only bother attempting to seek if the offset is greater than zero.
+        if (this.time_range != null && this.time_range.start > 0) {
+            this.pipeline.set_state (State.PAUSED);
+        } else {
+            this.pipeline.set_state (State.PLAYING);
+        }
+
+        this.run_continue = run.callback;
+
+        yield;
+    }
+
+    public override void end (bool aborted, uint status) {
+        this.pipeline.set_state (State.NULL);
+        this.msg.wrote_chunk.disconnect (this.on_wrote_chunk);
+
+        if (!aborted) {
+            this.msg.response_body.complete ();
+            this.server.unpause_message (this.msg);
+        }
+
+        base.end (aborted, status);
+    }
+
+    private void prepare_pipeline (string name,
+                                   Element src) throws Error {
+        dynamic Element sink = ElementFactory.make ("fakesink", SINK_NAME);
+
+        if (sink == null) {
+            // 'fakesink' should not be translated
+            throw new GstError.MISSING_PLUGIN (_("Plugin 'fakesink' missing"));
+        }
+
+        sink.signal_handoffs = true;
+        sink.handoff.connect (this.on_new_buffer);
+
+        this.pipeline = new Pipeline (name);
+        assert (this.pipeline != null);
+
+        this.pipeline.add_many (src, sink);
+
+        if (src.numpads == 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 ();
+        bus.add_watch (bus_handler);
+    }
+
+    private void src_pad_added (Element src,
+                                Pad     src_pad) {
+        var caps = src_pad.get_caps ();
+
+        var sink = this.pipeline.get_by_name (SINK_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.end (false, Soup.KnownStatusCode.NONE);
+                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.end (false, Soup.KnownStatusCode.NONE);
+            return;
+        }
+
+        if (depay != null) {
+            depay.sync_state_with_parent ();
+        }
+    }
+
+    private void on_new_buffer (Element sink,
+                                Buffer  buffer,
+                                Pad     pad) {
+        Idle.add_full (Priority.HIGH_IDLE,
+                       () => {
+            if (this.cancellable.is_cancelled ()) {
+                return false;
+            }
+
+            this.push_data (buffer.data);
+            this.buffered++;
+
+            if (this.buffered > MAX_BUFFERED_CHUNKS) {
+                // Client is either not reading (Paused) or not fast enough
+                this.pipeline.set_state (State.PAUSED);
+                this.out_of_sync = true;
+            }
+
+            return false;
+        });
+    }
+
+    private void on_wrote_chunk (Soup.Message msg) {
+        this.buffered--;
+
+        if (out_of_sync && this.buffered < MIN_BUFFERED_CHUNKS) {
+            this.pipeline.set_state (State.PLAYING);
+            this.out_of_sync = false;
+        }
+    }
+
+    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;
+            }
+
+            if (this.time_range != null && this.time_range.start > 0) {
+                State old_state;
+                State new_state;
+
+                message.parse_state_changed (out old_state,
+                                             out new_state,
+                                             null);
+
+                if (old_state == State.READY && new_state == State.PAUSED) {
+                    if (this.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.HIGH_IDLE,
+                               () => {
+                    this.end (false, Soup.KnownStatusCode.NONE);
+
+                    return false;
+                });
+        }
+
+        return ret;
+    }
+
+    private bool seek () {
+        Gst.SeekType stop_type;
+
+        if (this.time_range.stop > 0) {
+            stop_type = Gst.SeekType.SET;
+        } else {
+            stop_type = Gst.SeekType.NONE;
+        }
+
+        if (!this.pipeline.seek (1.0,
+                                 Format.TIME,
+                                 SeekFlags.FLUSH,
+                                 Gst.SeekType.SET,
+                                 this.time_range.start,
+                                 stop_type,
+                                 this.time_range.stop)) {
+            warning (_("Failed to seek to offset %lld"), this.time_range.start);
+
+            this.end (false,
+                      Soup.KnownStatusCode.REQUESTED_RANGE_NOT_SATISFIABLE);
+
+            return false;
+        }
+
+        return true;
+    }
+}
+
index 1dc9c23..1ba10f8 100644 (file)
@@ -97,12 +97,12 @@ internal class Rygel.HTTPIdentityHandler : Rygel.HTTPGetHandler {
                 throw new HTTPRequestError.NOT_FOUND (_("Not found"));
             }
 
-            return new LiveResponse (request.server,
-                                     request.msg,
-                                     "RygelLiveResponse",
-                                     src,
-                                     request.seek,
-                                     this.cancellable);
+            return new HTTPGstResponse (request.server,
+                                        request.msg,
+                                        "RygelHTTPGstResponse",
+                                        src,
+                                        request.seek,
+                                        this.cancellable);
         } else {
             if (item.uris.size == 0) {
                 throw new HTTPRequestError.NOT_FOUND
index 0e4e791..f85363f 100644 (file)
@@ -59,12 +59,12 @@ internal class Rygel.HTTPTranscodeHandler : HTTPGetHandler {
         try {
             src = this.transcoder.create_source (item, src);
 
-            return new LiveResponse (request.server,
-                                     request.msg,
-                                     "RygelLiveResponse",
-                                     src,
-                                     request.seek,
-                                     this.cancellable);
+            return new HTTPGstResponse (request.server,
+                                        request.msg,
+                                        "RygelHTTPGstResponse",
+                                        src,
+                                        request.seek,
+                                        this.cancellable);
         } catch (GLib.Error err) {
             throw new HTTPRequestError.NOT_FOUND (err.message);
         }
index 11136a1..152f0ea 100644 (file)
@@ -26,7 +26,7 @@ AM_VALAFLAGS = --disable-warnings --thread \
                --pkg gio-2.0 --pkg gee-1.0 -g
 
 check_PROGRAMS = rygel-http-item-uri-test \
-                rygel-live-response-test \
+                rygel-http-gst-response-test \
                 rygel-seekable-response-test \
                 rygel-http-byte-seek-test \
                 rygel-http-time-seek-test \
@@ -39,8 +39,8 @@ TESTS = $(check_PROGRAMS)
 rygel_http_item_uri_test_SOURCES = rygel-http-item-uri-test.vala \
                                    rygel-http-item-uri.vala
 
-rygel_live_response_test_SOURCES = rygel-live-response-test.vala \
-                                  rygel-live-response.vala \
+rygel_http_gst_response_test_SOURCES = rygel-http-gst-response-test.vala \
+                                  rygel-http-gst-response.vala \
                                    rygel-http-response_live-response.vala \
                                    rygel-http-response-test_live-response.vala \
                                   rygel-state-machine_live-response.vala \
similarity index 75%
rename from tests/rygel-live-response-test.vala
rename to tests/rygel-http-gst-response-test.vala
index 078cc55..0b3217c 100644 (file)
@@ -24,7 +24,7 @@
 using Soup;
 using Gst;
 
-public class Rygel.LiveResponseTest : Rygel.HTTPResponseTest {
+public class Rygel.HTTPGstResponseTest : Rygel.HTTPResponseTest {
     private static const long BLOCK_SIZE = MAX_BYTES / 16;
     private static const long MAX_BUFFERS = MAX_BYTES / BLOCK_SIZE;
 
@@ -34,10 +34,10 @@ public class Rygel.LiveResponseTest : Rygel.HTTPResponseTest {
         Gst.init (ref args);
 
         try {
-            var test = new LiveResponseTest.complete ();
+            var test = new HTTPGstResponseTest.complete ();
             test.run ();
 
-            test = new LiveResponseTest.abort ();
+            test = new HTTPGstResponseTest.abort ();
             test.run ();
         } catch (TestError.SKIP error) {
             return error.code;
@@ -54,24 +54,24 @@ public class Rygel.LiveResponseTest : Rygel.HTTPResponseTest {
         this.src = GstUtils.create_element ("audiotestsrc", null);
     }
 
-    private LiveResponseTest.complete () throws Error {
+    private HTTPGstResponseTest.complete () throws Error {
         base.complete ();
 
         this.src.blocksize = BLOCK_SIZE;
         this.src.num_buffers = MAX_BUFFERS;
     }
 
-    private LiveResponseTest.abort () throws Error {
+    private HTTPGstResponseTest.abort () throws Error {
         base.abort ();
     }
 
     internal override HTTPResponse create_response (Soup.Message msg)
                                                      throws Error {
-        return new LiveResponse (this.server.context.server,
-                                 msg,
-                                 "TestingLiveResponse",
-                                 this.src,
-                                 null,
-                                 this.cancellable);
+        return new HTTPGstResponse (this.server.context.server,
+                                    msg,
+                                    "TestingHTTPGstResponse",
+                                    this.src,
+                                    null,
+                                    this.cancellable);
     }
 }
similarity index 98%
rename from src/rygel/rygel-live-response.vala
rename to tests/rygel-http-gst-response.vala
index 51481d9..6581084 100644 (file)
@@ -25,7 +25,7 @@
 using GUPnP;
 using Gst;
 
-internal class Rygel.LiveResponse : Rygel.HTTPResponse {
+internal class Rygel.HTTPGstResponse : Rygel.HTTPResponse {
     private const string SINK_NAME = "fakesink";
     // High and low threshold for number of buffered chunks
     private const uint MAX_BUFFERED_CHUNKS = 32;
@@ -38,7 +38,7 @@ internal class Rygel.LiveResponse : Rygel.HTTPResponse {
     private int64 buffered;
     private bool out_of_sync;
 
-    public LiveResponse (Soup.Server  server,
+    public HTTPGstResponse (Soup.Server  server,
                          Soup.Message msg,
                          string       name,
                          Element      src,
diff --git a/tests/rygel-live-response.vala b/tests/rygel-live-response.vala
deleted file mode 120000 (symlink)
index 0fc760f..0000000
+++ /dev/null
@@ -1 +0,0 @@
-../src/rygel/rygel-live-response.vala
\ No newline at end of file