Esempio n. 1
0
    def testRenderHTTPTokenAuthorized(self):
        streamer = FakeStreamer(mediumClass=FakeTokenMedium)
        httpauth = HTTPAuthentication(streamer)
        resource = MultiFdSinkStreamingResource(streamer, httpauth)
        # override issuer
        httpauth.setBouncerName('fakebouncer')
        httpauth.setDomain('FakeDomain')

        streamer.caps = True
        self.failUnless(resource.isReady())

        d = defer.Deferred()

        def rightToken(_):
            request = FakeRequest(ip='127.0.0.1', args={'token': 'LETMEIN'})
            return self.deferAssertAuthorized(httpauth, request)

        def rightTokenTwice(_):
            request = FakeRequest(ip='127.0.0.1',
                                  args={'token': ['LETMEIN', 'LETMEIN']})
            return self.deferAssertAuthorized(httpauth, request)

        d.addCallback(rightToken)
        d.addCallback(rightTokenTwice)

        d.callback(None)
        return d
Esempio n. 2
0
    def _setRequestHeaders(self, request):
        MultiFdSinkStreamingResource._setRequestHeaders(self, request)
        if request.serveIcy:
            additionalHeaders = self.streamer.get_icy_headers()

            for header in additionalHeaders:
                request.setHeader(header, additionalHeaders[header])
Esempio n. 3
0
    def testRenderHTTPAllowDefault(self):
        streamer = FakeStreamer(mediumClass=FakeAuthFailingMedium)
        httpauth = HTTPAuthentication(streamer)
        resource = MultiFdSinkStreamingResource(streamer, httpauth)
        httpauth.setBouncerName('fakebouncer')

        streamer.caps = True
        self.failUnless(resource.isReady())

        d = defer.Deferred()

        def wrongToken(_):
            request = FakeRequest(user='******')
            return self.deferAssertUnauthorized(httpauth, request)

        def errorDisallowDefault(_, error):
            streamer.medium.failure = error
            request = FakeRequest(user='******')
            return self.deferAssertInternalServerError(httpauth, request)

        def errorAllowDefault(_, error):
            streamer.medium.failure = error
            httpauth.setAllowDefault(True)
            request = FakeRequest(user='******')
            return self.deferAssertAuthorized(httpauth, request)

        d.addCallback(wrongToken)
        d.addCallback(errorDisallowDefault, errors.NotConnectedError())
        d.addCallback(errorDisallowDefault, errors.UnknownComponentError())
        d.addCallback(errorAllowDefault, errors.NotConnectedError())
        d.addCallback(errorAllowDefault, errors.UnknownComponentError())

        d.callback(None)
        return d
Esempio n. 4
0
    def testRenderHTTPTokenAuthorized(self):
        streamer = FakeStreamer(mediumClass=FakeTokenMedium)
        httpauth = HTTPAuthentication(streamer)
        resource = MultiFdSinkStreamingResource(streamer, httpauth)
        # override issuer
        httpauth.setBouncerName('fakebouncer')
        httpauth.setDomain('FakeDomain')

        streamer.caps = True
        self.failUnless(resource.isReady())

        d = defer.Deferred()

        def rightToken(_):
            request = FakeRequest(ip='127.0.0.1', args={'token': 'LETMEIN'})
            return self.deferAssertAuthorized(httpauth, request)

        def rightTokenTwice(_):
            request = FakeRequest(ip='127.0.0.1',
                args={'token': ['LETMEIN', 'LETMEIN']})
            return self.deferAssertAuthorized(httpauth, request)

        d.addCallback(rightToken)
        d.addCallback(rightTokenTwice)

        d.callback(None)
        return d
Esempio n. 5
0
 def testRenderNotReady(self):
     streamer = FakeStreamer()
     httpauth = HTTPAuthentication(streamer)
     resource = MultiFdSinkStreamingResource(streamer, httpauth)
     self.failIf(resource.isReady())
     status = resource.render(FakeRequest(ip=''))
     self.assertEquals(status, server.NOT_DONE_YET)
Esempio n. 6
0
    def _setRequestHeaders(self, request):
        MultiFdSinkStreamingResource._setRequestHeaders(self, request)
        if request.serveIcy:
            additionalHeaders = self.streamer.get_icy_headers()

            for header in additionalHeaders:
                request.setHeader(header, additionalHeaders[header])
Esempio n. 7
0
    def testRenderHTTPAllowDefault(self):
        streamer = FakeStreamer(mediumClass=FakeAuthFailingMedium)
        httpauth = HTTPAuthentication(streamer)
        resource = MultiFdSinkStreamingResource(streamer, httpauth)
        httpauth.setBouncerName('fakebouncer')

        streamer.caps = True
        self.failUnless(resource.isReady())

        d = defer.Deferred()

        def wrongToken(_):
            request = FakeRequest(user='******')
            return self.deferAssertUnauthorized(httpauth, request)

        def errorDisallowDefault(_, error):
            streamer.medium.failure = error
            request = FakeRequest(user='******')
            return self.deferAssertInternalServerError(httpauth, request)

        def errorAllowDefault(_, error):
            streamer.medium.failure = error
            httpauth.setAllowDefault(True)
            request = FakeRequest(user='******')
            return self.deferAssertAuthorized(httpauth, request)

        d.addCallback(wrongToken)
        d.addCallback(errorDisallowDefault, errors.NotConnectedError())
        d.addCallback(errorDisallowDefault, errors.UnknownComponentError())
        d.addCallback(errorAllowDefault, errors.NotConnectedError())
        d.addCallback(errorAllowDefault, errors.UnknownComponentError())

        d.callback(None)
        return d
Esempio n. 8
0
 def testRenderNotReady(self):
     streamer = FakeStreamer()
     httpauth = HTTPAuthentication(streamer)
     resource = MultiFdSinkStreamingResource(streamer, httpauth)
     self.failIf(resource.isReady())
     status = resource.render(FakeRequest(ip=''))
     self.assertEquals(status, server.NOT_DONE_YET)
Esempio n. 9
0
    def testRenderNew(self):
        streamer = FakeStreamer()
        httpauth = HTTPAuthentication(streamer)
        resource = MultiFdSinkStreamingResource(streamer, httpauth)
        streamer.caps = True
        streamer.mime = 'application/x-ogg'

        request = FakeRequest(ip='127.0.0.1')
        data = resource.render(request)
        self.failUnless(server.NOT_DONE_YET)
Esempio n. 10
0
    def testRenderNew(self):
        streamer = FakeStreamer()
        httpauth = HTTPAuthentication(streamer)
        resource = MultiFdSinkStreamingResource(streamer, httpauth)
        streamer.caps = True
        streamer.mime = 'application/x-ogg'

        request = FakeRequest(ip='127.0.0.1')
        data = resource.render(request)
        self.failUnless(server.NOT_DONE_YET)
Esempio n. 11
0
    def testRenderHTTPAuthUnauthorized(self):
        streamer = FakeStreamer()
        httpauth = HTTPAuthentication(streamer)
        resource = MultiFdSinkStreamingResource(streamer, httpauth)
        httpauth.setBouncerName('fakebouncer')
        httpauth.setDomain('FakeDomain')

        streamer.caps = True
        self.failUnless(resource.isReady())

        request = FakeRequest(ip='127.0.0.1', user='******')
        return self.deferAssertUnauthorized(httpauth, request)
Esempio n. 12
0
    def testRenderHTTPAuthUnauthorized(self):
        streamer = FakeStreamer()
        httpauth = HTTPAuthentication(streamer)
        resource = MultiFdSinkStreamingResource(streamer, httpauth)
        httpauth.setBouncerName('fakebouncer')
        httpauth.setDomain('FakeDomain')

        streamer.caps = True
        self.failUnless(resource.isReady())

        request = FakeRequest(ip='127.0.0.1', user='******')
        return self.deferAssertUnauthorized(httpauth, request)
Esempio n. 13
0
    def testRenderTopStreamer(self):
        # a streamer that is at /a
        root = HTTPRoot()
        site = server.Site(resource=root)

        streamer = FakeStreamer()
        httpauth = HTTPAuthentication(streamer)
        resource = MultiFdSinkStreamingResource(streamer, httpauth)
        root.putChild('a', resource)

        # a request for / should give 404
        log.debug('unittest', 'requesting /, should 404')
        request = FakeRequest(ip='')
        r = site.getResourceFor(request)
        output = r.render(request)
        self.assertEquals(request.response, http.NOT_FOUND)

        # a request for /a should work
        log.debug('unittest', 'requesting /a, should work')
        request = FakeRequest(ip='', postpath=['a'])
        r = site.getResourceFor(request)
        self.assertEquals(r, resource)
        output = r.render(request)
        self.assertEquals(output, server.NOT_DONE_YET)

        # a request for /a/b should give 404
        log.debug('unittest', 'requesting /a/b, should 404')
        request = FakeRequest(ip='', postpath=['a', 'b'])
        r = site.getResourceFor(request)
        output = r.render(request)
        self.assertEquals(request.response, http.NOT_FOUND)
Esempio n. 14
0
    def testRenderReachedMaxClients(self):
        streamer = FakeStreamer()
        httpauth = HTTPAuthentication(streamer)
        resource = MultiFdSinkStreamingResource(streamer, httpauth)
        self.failIf(resource.isReady())
        streamer.caps = True
        self.failUnless(resource.isReady())

        #assert resource.maxAllowedClients() == 974
        resource._requests = ' ' * (resource.maxclients + 1)

        self.failUnless(resource.reachedServerLimits())

        request = FakeRequest(ip='127.0.0.1')
        data = resource.render(request)
        error_code = http.SERVICE_UNAVAILABLE
        self.assertEquals(request.headers.get('content-type', ''), 'text/html')
        self.assertEquals(request.headers.get('server', ''), HTTP_VERSION)
        self.assertEquals(request.response, error_code)

        expected = ERROR_TEMPLATE % {
            'code': error_code,
            'error': http.RESPONSES[error_code]
        }
        self.assertEquals(data, expected)
Esempio n. 15
0
    def testRenderReachedMaxClients(self):
        streamer = FakeStreamer()
        httpauth = HTTPAuthentication(streamer)
        resource = MultiFdSinkStreamingResource(streamer, httpauth)
        self.failIf(resource.isReady())
        streamer.caps = True
        self.failUnless(resource.isReady())

        #assert resource.maxAllowedClients() == 974
        resource._requests = ' ' * (resource.maxclients + 1)

        self.failUnless(resource.reachedServerLimits())

        request = FakeRequest(ip='127.0.0.1')
        data = resource.render(request)
        error_code = http.SERVICE_UNAVAILABLE
        self.assertEquals(request.headers.get('content-type', ''), 'text/html')
        self.assertEquals(request.headers.get('server', ''), HTTP_VERSION)
        self.assertEquals(request.response, error_code)

        expected = ERROR_TEMPLATE % {
            'code': error_code,
            'error': http.RESPONSES[error_code]}
        self.assertEquals(data, expected)
Esempio n. 16
0
    def _render(self, request):
        headerValue = request.getHeader('Icy-MetaData')
        request.serveIcy = (headerValue == '1')

        return MultiFdSinkStreamingResource._render(self, request)
Esempio n. 17
0
    def _render(self, request):
        headerValue = request.getHeader('Icy-MetaData')
        request.serveIcy = (headerValue == '1')

        return MultiFdSinkStreamingResource._render(self, request)
Esempio n. 18
0
 def configure_auth_and_resource(self):
     self.httpauth = http.HTTPAuthentication(self)
     self.resource = MultiFdSinkStreamingResource(self, self.httpauth)
Esempio n. 19
0
class MultifdSinkStreamer(streamer.Streamer, Stats):
    pipe_template = 'multifdsink name=sink ' + \
                                'sync=false ' + \
                                'recover-policy=3'
    defaultSyncMethod = 0

    def setup_burst_mode(self, sink):
        if self.burst_on_connect:
            if self.burst_time and \
                    gstreamer.element_factory_has_property('multifdsink',
                                                           'units-max'):
                self.debug("Configuring burst mode for %f second burst",
                           self.burst_time)
                # Set a burst for configurable minimum time, plus extra to
                # start from a keyframe if needed.
                sink.set_property('sync-method', 4)  # burst-keyframe
                sink.set_property('burst-unit', 2)  # time
                sink.set_property('burst-value',
                                  long(self.burst_time * gst.SECOND))

                # We also want to ensure that we have sufficient data available
                # to satisfy this burst; and an appropriate maximum, all
                # specified in units of time.
                sink.set_property('time-min',
                                  long((self.burst_time + 5) * gst.SECOND))

                sink.set_property('unit-type', 2)  # time
                sink.set_property('units-soft-max',
                                  long((self.burst_time + 8) * gst.SECOND))
                sink.set_property('units-max',
                                  long((self.burst_time + 10) * gst.SECOND))
            elif self.burst_size:
                self.debug("Configuring burst mode for %d kB burst",
                           self.burst_size)
                # If we have a burst-size set, use modern
                # needs-recent-multifdsink behaviour to have complex bursting.
                # In this mode, we burst a configurable minimum, plus extra
                # so we start from a keyframe (or less if we don't have a
                # keyframe available)
                sink.set_property('sync-method', 'burst-keyframe')
                sink.set_property('burst-unit', 'bytes')
                sink.set_property('burst-value', self.burst_size * 1024)

                # To use burst-on-connect, we need to ensure that multifdsink
                # has a minimum amount of data available - assume 512 kB beyond
                # the burst amount so that we should have a keyframe available
                sink.set_property('bytes-min', (self.burst_size + 512) * 1024)

                # And then we need a maximum still further above that - the
                # exact value doesn't matter too much, but we want it
                # reasonably small to limit memory usage. multifdsink doesn't
                # give us much control here, we can only specify the max
                # values in buffers. We assume each buffer is close enough
                # to 4kB - true for asf and ogg, at least
                sink.set_property('buffers-soft-max',
                                  (self.burst_size + 1024) / 4)
                sink.set_property('buffers-max', (self.burst_size + 2048) / 4)

            else:
                # Old behaviour; simple burst-from-latest-keyframe
                self.debug("simple burst-on-connect, setting sync-method 2")
                sink.set_property('sync-method', 2)

                sink.set_property('buffers-soft-max', 250)
                sink.set_property('buffers-max', 500)
        else:
            self.debug("no burst-on-connect, setting sync-method 0")
            sink.set_property('sync-method', self.defaultSyncMethod)

            sink.set_property('buffers-soft-max', 250)
            sink.set_property('buffers-max', 500)

    def parseExtraProperties(self, properties):
        # check how to set client sync mode
        self.burst_on_connect = properties.get('burst-on-connect', False)
        self.burst_size = properties.get('burst-size', 0)
        self.burst_time = properties.get('burst-time', 0.0)

    def _configure_sink(self, sink):
        self.setup_burst_mode(sink)

        if gstreamer.element_factory_has_property('multifdsink',
                                                  'resend-streamheader'):
            sink.set_property('resend-streamheader', False)
        else:
            self.debug("resend-streamheader property not available, "
                       "resending streamheader when it changes in the caps")

        sink.set_property('timeout', self.timeout)

        sink.connect('deep-notify::caps', self._notify_caps_cb)

        # these are made threadsafe using idle_add in the handler
        sink.connect('client-added', self._client_added_handler)

        # We now require a sufficiently recent multifdsink anyway that we can
        # use the new client-fd-removed signal
        sink.connect('client-fd-removed', self._client_fd_removed_cb)
        sink.connect('client-removed', self._client_removed_cb)

        sink.caps = None

    def check_properties(self, props, addMessage):
        streamer.Streamer.check_properties(self, props, addMessage)

        # tcp is where multifdsink is
        version = gstreamer.get_plugin_version('tcp')
        if version < (0, 10, 9, 1):
            m = messages.Error(
                T_(
                    N_("Version %s of the '%s' GStreamer plug-in is too old.\n"
                       ), ".".join(map(str, version)), 'multifdsink'))
            m.add(
                T_(N_("Please upgrade '%s' to version %s."),
                   'gst-plugins-base', '0.10.10'))
            addMessage(m)

    def configure_auth_and_resource(self):
        self.httpauth = http.HTTPAuthentication(self)
        self.resource = MultiFdSinkStreamingResource(self, self.httpauth)

    def configure_pipeline(self, pipeline, properties):
        sink = self.get_element('sink')
        Stats.__init__(self, sink)

        streamer.Streamer.configure_pipeline(self, pipeline, properties)
        self.parseExtraProperties(properties)
        self._configure_sink(sink)

    def _get_root(self):
        root = HTTPRoot()
        # TwistedWeb wants the child path to not include the leading /
        mount = self.mountPoint[1:]
        root.putChild(mount, self.resource)
        return root

    def __repr__(self):
        return '<MultifdSinkStreamer (%s)>' % self.name

    def getMaxClients(self):
        return self.resource.maxclients

    def get_mime(self):
        if self.sinks[0].caps:
            return self.sinks[0].caps[0].get_name()

    def get_content_type(self):
        mime = self.get_mime()
        if mime == 'multipart/x-mixed-replace':
            mime += ";boundary=ThisRandomString"
        return mime

    def add_client(self, fd, request):
        sink = self.get_element('sink')
        sink.emit('add', fd)

    def remove_client(self, fd):
        sink = self.get_element('sink')
        sink.emit('remove', fd)

    def remove_all_clients(self):
        """Remove all the clients.

        Returns a deferred fired once all clients have been removed.
        """
        if self.resource:
            # can be None if we never went happy
            self.debug("Asking for all clients to be removed")
            return self.resource.removeAllClients()

    def _client_added_handler(self, sink, fd):
        self.log('[fd %5d] client_added_handler', fd)
        Stats.clientAdded(self)
        self.update_ui_state()

    def _client_removed_handler(self, sink, fd, reason, stats):
        self.log('[fd %5d] client_removed_handler, reason %s', fd, reason)
        if reason.value_name == 'GST_CLIENT_STATUS_ERROR':
            self.warning('[fd %5d] Client removed because of write error' % fd)

        self.resource.clientRemoved(sink, fd, reason, stats)
        Stats.clientRemoved(self)
        self.update_ui_state()

    ### START OF THREAD-AWARE CODE (called from non-reactor threads)

    def _notify_caps_cb(self, element, pad, param):
        # We store caps in sink objects as
        # each sink might (and will) serve different content-type
        caps = pad.get_negotiated_caps()
        if caps == None:
            return

        caps_str = gstreamer.caps_repr(caps)
        self.debug('Got caps: %s' % caps_str)

        if not element.caps == None:
            self.warning('Already had caps: %s, replacing' % caps_str)

        self.debug('Storing caps: %s' % caps_str)
        element.caps = caps

        reactor.callFromThread(self.update_ui_state)

    # We now use both client-removed and client-fd-removed. We call get-stats
    # from the first callback ('client-removed'), but don't actually start
    # removing the client until we get 'client-fd-removed'. This ensures that
    # there's no window in which multifdsink still knows about the fd,
    # but we've actually closed it, so we no longer get spurious duplicates.
    # this can be called from both application and streaming thread !

    def _client_removed_cb(self, sink, fd, reason):
        stats = sink.emit('get-stats', fd)
        self._pending_removals[fd] = (stats, reason)

    # this can be called from both application and streaming thread !

    def _client_fd_removed_cb(self, sink, fd):
        (stats, reason) = self._pending_removals.pop(fd)

        reactor.callFromThread(self._client_removed_handler, sink, fd, reason,
                               stats)
Esempio n. 20
0
class MultifdSinkStreamer(streamer.Streamer, Stats):
    pipe_template = "multifdsink name=sink " + "sync=false " + "recover-policy=3"
    defaultSyncMethod = 0

    def setup_burst_mode(self, sink):
        if self.burst_on_connect:
            if self.burst_time and gstreamer.element_factory_has_property("multifdsink", "units-max"):
                self.debug("Configuring burst mode for %f second burst", self.burst_time)
                # Set a burst for configurable minimum time, plus extra to
                # start from a keyframe if needed.
                sink.set_property("sync-method", 4)  # burst-keyframe
                sink.set_property("burst-unit", 2)  # time
                sink.set_property("burst-value", long(self.burst_time * Gst.SECOND))

                # We also want to ensure that we have sufficient data available
                # to satisfy this burst; and an appropriate maximum, all
                # specified in units of time.
                sink.set_property("time-min", long((self.burst_time + 5) * Gst.SECOND))

                sink.set_property("unit-type", 2)  # time
                sink.set_property("units-soft-max", long((self.burst_time + 8) * Gst.SECOND))
                sink.set_property("units-max", long((self.burst_time + 10) * Gst.SECOND))
            elif self.burst_size:
                self.debug("Configuring burst mode for %d kB burst", self.burst_size)
                # If we have a burst-size set, use modern
                # needs-recent-multifdsink behaviour to have complex bursting.
                # In this mode, we burst a configurable minimum, plus extra
                # so we start from a keyframe (or less if we don't have a
                # keyframe available)
                sink.set_property("sync-method", "burst-keyframe")
                sink.set_property("burst-unit", "bytes")
                sink.set_property("burst-value", self.burst_size * 1024)

                # To use burst-on-connect, we need to ensure that multifdsink
                # has a minimum amount of data available - assume 512 kB beyond
                # the burst amount so that we should have a keyframe available
                sink.set_property("bytes-min", (self.burst_size + 512) * 1024)

                # And then we need a maximum still further above that - the
                # exact value doesn't matter too much, but we want it
                # reasonably small to limit memory usage. multifdsink doesn't
                # give us much control here, we can only specify the max
                # values in buffers. We assume each buffer is close enough
                # to 4kB - true for asf and ogg, at least
                sink.set_property("buffers-soft-max", (self.burst_size + 1024) / 4)
                sink.set_property("buffers-max", (self.burst_size + 2048) / 4)

            else:
                # Old behaviour; simple burst-from-latest-keyframe
                self.debug("simple burst-on-connect, setting sync-method 2")
                sink.set_property("sync-method", 2)

                sink.set_property("buffers-soft-max", 250)
                sink.set_property("buffers-max", 500)
        else:
            self.debug("no burst-on-connect, setting sync-method 0")
            sink.set_property("sync-method", self.defaultSyncMethod)

            sink.set_property("buffers-soft-max", 250)
            sink.set_property("buffers-max", 500)

    def parseExtraProperties(self, properties):
        # check how to set client sync mode
        self.burst_on_connect = properties.get("burst-on-connect", False)
        self.burst_size = properties.get("burst-size", 0)
        self.burst_time = properties.get("burst-time", 0.0)

    def _configure_sink(self, sink):
        self.setup_burst_mode(sink)

        if gstreamer.element_factory_has_property("multifdsink", "resend-streamheader"):
            sink.set_property("resend-streamheader", False)
        else:
            self.debug(
                "resend-streamheader property not available, " "resending streamheader when it changes in the caps"
            )

        sink.set_property("timeout", self.timeout)

        sink.connect("deep-notify::caps", self._notify_caps_cb)

        # these are made threadsafe using idle_add in the handler
        sink.connect("client-added", self._client_added_handler)

        # We now require a sufficiently recent multifdsink anyway that we can
        # use the new client-fd-removed signal
        sink.connect("client-fd-removed", self._client_fd_removed_cb)
        sink.connect("client-removed", self._client_removed_cb)

        sink.caps = None

    def check_properties(self, props, addMessage):
        streamer.Streamer.check_properties(self, props, addMessage)

        # tcp is where multifdsink is
        version = gstreamer.get_plugin_version("tcp")
        if version < (0, 10, 9, 1):
            m = messages.Error(
                T_(
                    N_("Version %s of the '%s' GStreamer plug-in is too old.\n"),
                    ".".join(map(str, version)),
                    "multifdsink",
                )
            )
            m.add(T_(N_("Please upgrade '%s' to version %s."), "gst-plugins-base", "0.10.10"))
            addMessage(m)

    def configure_auth_and_resource(self):
        self.httpauth = http.HTTPAuthentication(self)
        self.resource = MultiFdSinkStreamingResource(self, self.httpauth)

    def configure_pipeline(self, pipeline, properties):
        sink = self.get_element("sink")
        Stats.__init__(self, sink)

        streamer.Streamer.configure_pipeline(self, pipeline, properties)
        self.parseExtraProperties(properties)
        self._configure_sink(sink)

    def _get_root(self):
        root = HTTPRoot()
        # TwistedWeb wants the child path to not include the leading /
        mount = self.mountPoint[1:]
        root.putChild(mount, self.resource)
        return root

    def __repr__(self):
        return "<MultifdSinkStreamer (%s)>" % self.name

    def getMaxClients(self):
        return self.resource.maxclients

    def get_mime(self):
        if self.sinks[0].caps:
            return self.sinks[0].caps[0].get_name()

    def get_content_type(self):
        mime = self.get_mime()
        if mime == "multipart/x-mixed-replace":
            mime += ";boundary=ThisRandomString"
        return mime

    def add_client(self, fd, request):
        sink = self.get_element("sink")
        sink.emit("add", fd)

    def remove_client(self, fd):
        sink = self.get_element("sink")
        sink.emit("remove", fd)

    def remove_all_clients(self):
        """Remove all the clients.

        Returns a deferred fired once all clients have been removed.
        """
        if self.resource:
            # can be None if we never went happy
            self.debug("Asking for all clients to be removed")
            return self.resource.removeAllClients()

    def _client_added_handler(self, sink, fd):
        self.log("[fd %5d] client_added_handler", fd)
        Stats.clientAdded(self)
        self.update_ui_state()

    def _client_removed_handler(self, sink, fd, reason, stats):
        self.log("[fd %5d] client_removed_handler, reason %s", fd, reason)
        if reason.value_name == "GST_CLIENT_STATUS_ERROR":
            self.warning("[fd %5d] Client removed because of write error" % fd)

        self.resource.clientRemoved(sink, fd, reason, stats)
        Stats.clientRemoved(self)
        self.update_ui_state()

    ### START OF THREAD-AWARE CODE (called from non-reactor threads)

    def _notify_caps_cb(self, element, pad, param):
        # We store caps in sink objects as
        # each sink might (and will) serve different content-type
        caps = pad.get_negotiated_caps()
        if caps == None:
            return

        caps_str = gstreamer.caps_repr(caps)
        self.debug("Got caps: %s" % caps_str)

        if not element.caps == None:
            self.warning("Already had caps: %s, replacing" % caps_str)

        self.debug("Storing caps: %s" % caps_str)
        element.caps = caps

        reactor.callFromThread(self.update_ui_state)

    # We now use both client-removed and client-fd-removed. We call get-stats
    # from the first callback ('client-removed'), but don't actually start
    # removing the client until we get 'client-fd-removed'. This ensures that
    # there's no window in which multifdsink still knows about the fd,
    # but we've actually closed it, so we no longer get spurious duplicates.
    # this can be called from both application and streaming thread !

    def _client_removed_cb(self, sink, fd, reason):
        stats = sink.emit("get-stats", fd)
        self._pending_removals[fd] = (stats, reason)

    # this can be called from both application and streaming thread !

    def _client_fd_removed_cb(self, sink, fd):
        (stats, reason) = self._pending_removals.pop(fd)

        reactor.callFromThread(self._client_removed_handler, sink, fd, reason, stats)
Esempio n. 21
0
 def configure_auth_and_resource(self):
     self.httpauth = http.HTTPAuthentication(self)
     self.resource = MultiFdSinkStreamingResource(self, self.httpauth)