From 062e75f0eb4f1d79abeded7a1e2aeeb349a4e7e0 Mon Sep 17 00:00:00 2001 From: "Zeeshan Ali (Khattak)" Date: Fri, 14 Jan 2011 23:07:00 +0200 Subject: [PATCH] core,test,i18n: Rename LiveResponse to HTTPGstResponse --- po/POTFILES.in | 2 +- po/POTFILES.skip | 4 +- src/rygel/Makefile.am | 2 +- src/rygel/rygel-http-gst-response.vala | 269 +++++++++++++++++++++ src/rygel/rygel-http-identity-handler.vala | 12 +- src/rygel/rygel-http-transcode-handler.vala | 12 +- tests/Makefile.am | 6 +- ...test.vala => rygel-http-gst-response-test.vala} | 22 +- .../rygel-http-gst-response.vala | 4 +- tests/rygel-live-response.vala | 1 - 10 files changed, 301 insertions(+), 33 deletions(-) create mode 100644 src/rygel/rygel-http-gst-response.vala rename tests/{rygel-live-response-test.vala => rygel-http-gst-response-test.vala} (75%) rename src/rygel/rygel-live-response.vala => tests/rygel-http-gst-response.vala (98%) delete mode 120000 tests/rygel-live-response.vala diff --git a/po/POTFILES.in b/po/POTFILES.in index 96badf2..a169d54 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -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 diff --git a/po/POTFILES.skip b/po/POTFILES.skip index 709ba2d..2fba594 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-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 diff --git a/src/rygel/Makefile.am b/src/rygel/Makefile.am index c779988..769419f 100644 --- a/src/rygel/Makefile.am +++ b/src/rygel/Makefile.am @@ -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 index 0000000..f15660e --- /dev/null +++ b/src/rygel/rygel-http-gst-response.vala @@ -0,0 +1,269 @@ +/* + * Copyright (C) 2008 Zeeshan Ali (Khattak) . + * Copyright (C) 2008,2011 Nokia Corporation. + * + * Author: Zeeshan Ali (Khattak) + * + * + * 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; + } +} + diff --git a/src/rygel/rygel-http-identity-handler.vala b/src/rygel/rygel-http-identity-handler.vala index 1dc9c23..1ba10f8 100644 --- a/src/rygel/rygel-http-identity-handler.vala +++ b/src/rygel/rygel-http-identity-handler.vala @@ -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 diff --git a/src/rygel/rygel-http-transcode-handler.vala b/src/rygel/rygel-http-transcode-handler.vala index 0e4e791..f85363f 100644 --- a/src/rygel/rygel-http-transcode-handler.vala +++ b/src/rygel/rygel-http-transcode-handler.vala @@ -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); } diff --git a/tests/Makefile.am b/tests/Makefile.am index 11136a1..152f0ea 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-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 \ diff --git a/tests/rygel-live-response-test.vala b/tests/rygel-http-gst-response-test.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 --- a/tests/rygel-live-response-test.vala +++ b/tests/rygel-http-gst-response-test.vala @@ -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); } } diff --git a/src/rygel/rygel-live-response.vala b/tests/rygel-http-gst-response.vala similarity index 98% rename from src/rygel/rygel-live-response.vala rename to tests/rygel-http-gst-response.vala index 51481d9..6581084 100644 --- a/src/rygel/rygel-live-response.vala +++ b/tests/rygel-http-gst-response.vala @@ -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 index 0fc760f..0000000 --- a/tests/rygel-live-response.vala +++ /dev/null @@ -1 +0,0 @@ -../src/rygel/rygel-live-response.vala \ No newline at end of file -- 2.7.4