[rygel] core,tests: Merge GstResponse into HTTPResponse



commit f3ce150d16d03f865792bf849b2c5c1cfc695484
Author: Zeeshan Ali (Khattak) <zeeshanak gnome org>
Date:   Sat Apr 9 03:27:27 2011 +0300

    core,tests: Merge GstResponse into HTTPResponse

 po/POTFILES.in                                     |    1 -
 po/POTFILES.skip                                   |    4 +-
 src/rygel/Makefile.am                              |    1 -
 src/rygel/rygel-http-gst-response.vala             |  230 --------------------
 src/rygel/rygel-http-gst-sink.vala                 |    4 +-
 src/rygel/rygel-http-identity-handler.vala         |    2 +-
 src/rygel/rygel-http-response.vala                 |  203 +++++++++++++++++-
 src/rygel/rygel-http-transcode-handler.vala        |    2 +-
 tests/Makefile.am                                  |   10 +-
 tests/rygel-http-gst-response-test.vala            |  164 --------------
 tests/rygel-http-gst-response.vala                 |    1 -
 tests/rygel-http-response-test.vala                |  132 +++++++++++-
 tests/rygel-http-response-test_gst-response.vala   |    1 -
 ..._gst-response.vala => rygel-http-response.vala} |    0
 ...vala => rygel-state-machine_http-response.vala} |    0
 15 files changed, 332 insertions(+), 423 deletions(-)
---
diff --git a/po/POTFILES.in b/po/POTFILES.in
index 34ab9e1..d7fec05 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -100,7 +100,6 @@ 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-http-gst-response.vala
 src/rygel/rygel-log-handler.vala
 src/rygel/rygel-logical-expression.vala
 src/rygel/rygel-main.vala
diff --git a/po/POTFILES.skip b/po/POTFILES.skip
index 4730ba2..1cac003 100644
--- a/po/POTFILES.skip
+++ b/po/POTFILES.skip
@@ -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-http-gst-response.c
+src/rygel/rygel-http-response.c
 src/rygel/rygel-log-handler.c
 src/rygel/rygel-main.c
 src/rygel/rygel-media-container.c
@@ -100,7 +100,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-http-gst-response.c
+tests/rygel-http-response.c
 tests/rygel-http-byte-seek_http-get.c
 tests/rygel-http-get.c
 tests/rygel-http-item-uri_http-get.c
diff --git a/src/rygel/Makefile.am b/src/rygel/Makefile.am
index 32d1b78..73c7c69 100644
--- a/src/rygel/Makefile.am
+++ b/src/rygel/Makefile.am
@@ -52,7 +52,6 @@ VAPI_SOURCE_FILES = \
 	rygel-http-byte-seek.vala \
 	rygel-http-time-seek.vala \
 	rygel-http-response.vala \
-	rygel-http-gst-response.vala \
 	rygel-http-gst-sink.vala \
 	rygel-resource-info.vala \
 	rygel-icon-info.vala \
diff --git a/src/rygel/rygel-http-gst-sink.vala b/src/rygel/rygel-http-gst-sink.vala
index f70fc98..330f88f 100644
--- a/src/rygel/rygel-http-gst-sink.vala
+++ b/src/rygel/rygel-http-gst-sink.vala
@@ -32,7 +32,7 @@ internal class Rygel.HTTPGstSink : BaseSink {
 
     public Cancellable cancellable;
 
-    private unowned HTTPGstResponse response;
+    private unowned HTTPResponse response;
     private int priority;
 
     private int64 chunks_buffered;
@@ -51,7 +51,7 @@ internal class Rygel.HTTPGstSink : BaseSink {
         add_pad_template (template);
     }
 
-    public HTTPGstSink (HTTPGstResponse response) {
+    public HTTPGstSink (HTTPResponse response) {
         this.chunks_buffered = 0;
         this.bytes_sent = 0;
         this.max_bytes = int64.MAX;
diff --git a/src/rygel/rygel-http-identity-handler.vala b/src/rygel/rygel-http-identity-handler.vala
index cd28b73..00dc465 100644
--- a/src/rygel/rygel-http-identity-handler.vala
+++ b/src/rygel/rygel-http-identity-handler.vala
@@ -88,6 +88,6 @@ internal class Rygel.HTTPIdentityHandler : Rygel.HTTPGetHandler {
             throw new HTTPRequestError.NOT_FOUND (_("Not found"));
         }
 
-        return new HTTPGstResponse (request, this, src);
+        return new HTTPResponse (request, this, src);
     }
 }
diff --git a/src/rygel/rygel-http-response.vala b/src/rygel/rygel-http-response.vala
index 28bf86c..98a4026 100644
--- a/src/rygel/rygel-http-response.vala
+++ b/src/rygel/rygel-http-response.vala
@@ -22,14 +22,17 @@
  */
 
 using Gst;
+using Soup;
 
-internal abstract class Rygel.HTTPResponse : GLib.Object, Rygel.StateMachine {
+internal class Rygel.HTTPResponse : GLib.Object, Rygel.StateMachine {
     public Soup.Server server { get; private set; }
     public Soup.Message msg;
 
     public Cancellable cancellable { get; set; }
 
-    protected SourceFunc run_continue;
+    public HTTPSeek seek;
+
+    private SourceFunc run_continue;
     private int _priority = -1;
     public int priority {
         get {
@@ -54,30 +57,49 @@ internal abstract class Rygel.HTTPResponse : GLib.Object, Rygel.StateMachine {
         }
     }
 
+    private Pipeline pipeline;
+    private uint bus_watch_id;
+
     public HTTPResponse (HTTPGet        request,
                          HTTPGetHandler request_handler,
-                         bool           partial) throws Error {
+                         Element        src) throws Error {
         this.server = request.server;
         this.msg = request.msg;
         this.cancellable = request_handler.cancellable;
+        this.seek = request.seek;
 
-        if (partial) {
+        if (this.seek != null && this.seek.length < this.seek.total_length) {
             this.msg.set_status (Soup.KnownStatusCode.PARTIAL_CONTENT);
         } else {
             this.msg.set_status (Soup.KnownStatusCode.OK);
         }
 
-        this.msg.response_body.set_accumulate (false);
+        if (this.seek != null && this.seek is HTTPByteSeek) {
+            this.msg.response_headers.set_encoding (Encoding.CONTENT_LENGTH);
+        } else {
+            this.msg.response_headers.set_encoding (Encoding.EOF);
+        }
 
         if (this.cancellable != null) {
             this.cancellable.cancelled.connect (this.on_cancelled);
         }
+
+        this.msg.response_body.set_accumulate (false);
+
+        this.prepare_pipeline ("RygelHTTPGstResponse", src);
     }
 
-    public abstract async void run ();
+    public async void run () {
+        // Only bother attempting to seek if the offset is greater than zero.
+        if (this.seek != null && this.seek.start > 0) {
+            this.pipeline.set_state (State.PAUSED);
+        } else {
+            this.pipeline.set_state (State.PLAYING);
+        }
+
+        this.run_continue = run.callback;
 
-    private void on_cancelled (Cancellable cancellable) {
-        this.end (true, Soup.KnownStatusCode.CANCELLED);
+        yield;
     }
 
     public void push_data (uint8[] data) {
@@ -87,6 +109,19 @@ internal abstract class Rygel.HTTPResponse : GLib.Object, Rygel.StateMachine {
     }
 
     public virtual void end (bool aborted, uint status) {
+        var sink = this.pipeline.get_by_name (HTTPGstSink.NAME) as HTTPGstSink;
+        sink.cancellable.cancel ();
+
+        this.pipeline.set_state (State.NULL);
+        Source.remove (this.bus_watch_id);
+
+        var encoding = this.msg.response_headers.get_encoding ();
+
+        if (!aborted && encoding != Encoding.CONTENT_LENGTH) {
+            this.msg.response_body.complete ();
+            this.server.unpause_message (this.msg);
+        }
+
         if (this.run_continue != null) {
             this.run_continue ();
         }
@@ -97,4 +132,156 @@ internal abstract class Rygel.HTTPResponse : GLib.Object, Rygel.StateMachine {
 
         this.completed ();
     }
+
+    private void on_cancelled (Cancellable cancellable) {
+        this.end (true, Soup.KnownStatusCode.CANCELLED);
+    }
+
+    private void prepare_pipeline (string name,
+                                   Element src) throws Error {
+        var sink = new HTTPGstSink (this);
+
+        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 ();
+        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 ();
+
+        var sink = this.pipeline.get_by_name (HTTPGstSink.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, 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, KnownStatusCode.NONE);
+            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;
+            }
+
+            if (this.seek != null && this.seek.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.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 (this.priority, () => {
+                    this.end (false, KnownStatusCode.NONE);
+
+                    return false;
+                });
+        }
+
+        return ret;
+    }
+
+    private bool perform_seek () {
+        var stop_type = Gst.SeekType.NONE;
+        Format format;
+
+        if (this.seek is HTTPTimeSeek) {
+            format = Format.TIME;
+
+            if (this.seek.stop > 0) {
+                stop_type = Gst.SeekType.SET;
+            }
+        } else {
+            format = Format.BYTES;
+        }
+
+        if (!this.pipeline.seek (1.0,
+                                 format,
+                                 SeekFlags.FLUSH | SeekFlags.ACCURATE,
+                                 Gst.SeekType.SET,
+                                 this.seek.start,
+                                 stop_type,
+                                 this.seek.stop)) {
+            warning (_("Failed to seek to offset %lld"), this.seek.start);
+
+            this.end (false, KnownStatusCode.REQUESTED_RANGE_NOT_SATISFIABLE);
+
+            return false;
+        }
+
+        return true;
+    }
 }
diff --git a/src/rygel/rygel-http-transcode-handler.vala b/src/rygel/rygel-http-transcode-handler.vala
index 1478879..b1122fc 100644
--- a/src/rygel/rygel-http-transcode-handler.vala
+++ b/src/rygel/rygel-http-transcode-handler.vala
@@ -59,7 +59,7 @@ internal class Rygel.HTTPTranscodeHandler : HTTPGetHandler {
         try {
             src = this.transcoder.create_source (item, src);
 
-            return new HTTPGstResponse (request, this, src);
+            return new HTTPResponse (request, this, src);
         } catch (GLib.Error err) {
             throw new HTTPRequestError.NOT_FOUND (err.message);
         }
diff --git a/tests/Makefile.am b/tests/Makefile.am
index 6fa71e7..e01705e 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -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-http-gst-response-test \
+		 rygel-http-response-test \
 		 rygel-http-byte-seek-test \
 		 rygel-http-time-seek-test \
 		 rygel-http-get-test \
@@ -38,11 +38,9 @@ TESTS = $(check_PROGRAMS)
 rygel_http_item_uri_test_SOURCES = rygel-http-item-uri-test.vala \
                                    rygel-http-item-uri.vala
 
-rygel_http_gst_response_test_SOURCES = rygel-http-gst-response-test.vala \
-				   rygel-http-gst-response.vala \
-                                   rygel-http-response_gst-response.vala \
-                                   rygel-http-response-test_gst-response.vala \
-				   rygel-state-machine_gst-response.vala \
+rygel_http_response_test_SOURCES = rygel-http-response-test.vala \
+				   rygel-http-response.vala \
+				   rygel-state-machine_http-response.vala \
 				   rygel-http-gst-sink.vala \
 				   rygel-gst-utils.vala
 
diff --git a/tests/rygel-http-response-test.vala b/tests/rygel-http-response-test.vala
index c3e6d8b..0a8e50a 100644
--- a/tests/rygel-http-response-test.vala
+++ b/tests/rygel-http-response-test.vala
@@ -21,16 +21,19 @@
  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  */
 
-// This module contains the common code between the test cases for
-// HTTPResponse subclasses.
 using Soup;
+using Gst;
 
 public errordomain Rygel.TestError {
     SKIP = 77,
     TIMEOUT
 }
 
-public abstract class Rygel.HTTPResponseTest : GLib.Object {
+public errordomain Rygel.HTTPRequestError {
+    NOT_FOUND = Soup.KnownStatusCode.NOT_FOUND
+}
+
+public class Rygel.HTTPResponseTest : GLib.Object {
     public const long MAX_BYTES = 102400;
 
     protected HTTPServer server;
@@ -39,11 +42,33 @@ public abstract class Rygel.HTTPResponseTest : GLib.Object {
     private bool server_done;
     private bool client_done;
 
+    private MediaItem item;
+
     private MainLoop main_loop;
 
     protected Cancellable cancellable;
     private Error error;
 
+    public static int main (string[] args) {
+        Gst.init (ref args);
+
+        try {
+            var test = new HTTPResponseTest.complete ();
+            test.run ();
+
+            test = new HTTPResponseTest.abort ();
+            test.run ();
+        } catch (TestError.SKIP error) {
+            return error.code;
+        } catch (Error error) {
+            critical ("%s", error.message);
+
+            return -1;
+        }
+
+        return 0;
+    }
+
     public HTTPResponseTest (Cancellable? cancellable = null) throws Error {
         this.cancellable = cancellable;
 
@@ -57,10 +82,14 @@ public abstract class Rygel.HTTPResponseTest : GLib.Object {
 
     public HTTPResponseTest.complete () throws Error {
         this ();
+
+        this.item = new MediaItem.fixed_size ();
     }
 
     public HTTPResponseTest.abort () throws Error {
         this (new Cancellable ());
+
+        this.item = new MediaItem ();
     }
 
     public virtual void run () throws Error {
@@ -82,8 +111,24 @@ public abstract class Rygel.HTTPResponseTest : GLib.Object {
         }
     }
 
-    internal abstract HTTPResponse create_response (Soup.Message msg)
-                                                    throws Error;
+    private HTTPResponse create_response (Soup.Message msg) throws Error {
+        var seek = null as HTTPSeek;
+
+        if (!this.item.is_live_stream ()) {
+            seek = new HTTPByteSeek (0, MAX_BYTES - 1, this.item.size);
+            msg.response_headers.set_content_length (seek.length);
+        }
+
+        var request = new HTTPGet (this.server.context.server,
+                                   msg,
+                                   this.item,
+                                   seek,
+                                   this.cancellable);
+        var handler = new HTTPGetHandler (this.cancellable);
+        var src = this.item.create_stream_source ();
+
+        return new HTTPResponse (request, handler, src);
+    }
 
     private void on_client_completed (StateMachine client) {
         if (this.server_done) {
@@ -247,3 +292,80 @@ public class Rygel.HTTPSeek : GLib.Object {
         this.length = stop - start + 1;
     }
 }
+
+public class Rygel.HTTPByteSeek : Rygel.HTTPSeek {
+    public HTTPByteSeek (int64 start, int64 stop, int64 total_length) {
+        base (start, stop, total_length);
+    }
+}
+
+public class Rygel.HTTPTimeSeek : Rygel.HTTPSeek {
+    public HTTPTimeSeek (int64 start, int64 stop, int64 total_length) {
+        base (start, stop, total_length);
+    }
+}
+
+public class Rygel.HTTPGet : GLib.Object {
+    public Soup.Server server;
+    public Soup.Message msg;
+
+    public Cancellable cancellable;
+
+    public MediaItem item;
+
+    internal HTTPSeek seek;
+
+    public HTTPGet (Soup.Server  server,
+                    Soup.Message msg,
+                    MediaItem    item,
+                    HTTPSeek?    seek,
+                    Cancellable? cancellable) {
+        this.server = server;
+        this.msg = msg;
+        this.item = item;
+        this.seek = seek;
+        this.cancellable = cancellable;
+    }
+}
+
+public class Rygel.HTTPGetHandler : GLib.Object {
+    public Cancellable cancellable;
+
+    public HTTPGetHandler (Cancellable? cancellable) {
+        this.cancellable = cancellable;
+    }
+}
+
+public class Rygel.MediaItem {
+    private static const long BLOCK_SIZE = HTTPResponseTest.MAX_BYTES / 16 + 1;
+    private static const long MAX_BUFFERS = 25;
+
+    public int64 size {
+        get {
+            return MAX_BUFFERS * BLOCK_SIZE;
+        }
+    }
+
+    private dynamic Element src;
+
+    public MediaItem () {
+        this.src = GstUtils.create_element ("fakesrc", null);
+        this.src.sizetype = 2; // fixed
+    }
+
+    public MediaItem.fixed_size () {
+        this ();
+
+        this.src.blocksize = BLOCK_SIZE;
+        this.src.num_buffers = MAX_BUFFERS;
+        this.src.sizemax = MAX_BUFFERS * BLOCK_SIZE;
+    }
+
+    public Element? create_stream_source () {
+        return this.src;
+    }
+
+    public bool is_live_stream () {
+        return ((int) this.src.num_buffers) < 0;
+    }
+}
diff --git a/tests/rygel-http-response_gst-response.vala b/tests/rygel-http-response.vala
similarity index 100%
rename from tests/rygel-http-response_gst-response.vala
rename to tests/rygel-http-response.vala
diff --git a/tests/rygel-state-machine_gst-response.vala b/tests/rygel-state-machine_http-response.vala
similarity index 100%
rename from tests/rygel-state-machine_gst-response.vala
rename to tests/rygel-state-machine_http-response.vala



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]