def __init__(self):
        def _action_continue(val):
            return val not in [
                GstValidate.ActionReturn.ERROR,
                GstValidate.ActionReturn.ERROR_REPORTED
            ]

        def _action_accum(previous, val):
            # we want to always keep any errors propagated
            if val in [
                    GstValidate.ActionReturn.ERROR,
                    GstValidate.ActionReturn.ERROR_REPORTED
            ]:
                return val
            if previous in [
                    GstValidate.ActionReturn.ERROR,
                    GstValidate.ActionReturn.ERROR_REPORTED
            ]:
                return previous

            # we want to always prefer async returns
            if previous in [
                    GstValidate.ActionReturn.ASYNC,
                    GstValidate.ActionReturn.INTERLACED
            ]:
                return previous
            if val in [
                    GstValidate.ActionReturn.ASYNC,
                    GstValidate.ActionReturn.INTERLACED
            ]:
                return val

            return val

        self.action = Signal(_action_continue, _action_accum)
Beispiel #2
0
    def __init__(self, server):
        # server string to connect to.  Passed directly to websockets.connect()
        self.server = server

        # fired after we have connected to the signalling server
        self.wss_connected = Signal()
        # fired every time we receive a message from the signalling server
        self.message = Signal()

        self._init_async()
Beispiel #3
0
class WebRTCSignallingClient(SignallingClientThread):
    """
    Signalling client implementation.  Deals wit session management over the
    signalling protocol.  Sends and receives from a peer.
    """
    def __init__(self, server, id_):
        super().__init__(server)

        self.wss_connected.connect(self._on_connection)
        self.message.connect(self._on_message)
        self.state = SignallingState.NEW
        self._state_observer = StateObserver(self, "state", threading.Condition())

        self.id = id_
        self._peerid = None

        # fired when the hello has been received
        self.connected = Signal()
        # fired when the signalling server responds that the session creation is ok
        self.session_created = Signal()
        # fired on an error
        self.error = Signal()
        # fired when the peer receives some json data
        self.have_json = Signal()

    def _update_state(self, new_state):
        self._state_observer.update (new_state)

    def wait_for_states(self, states):
        return self._state_observer.wait_for (states)

    def hello(self):
        self.send('HELLO ' + str(self.id))
        l.info("sent HELLO")
        self.wait_for_states([SignallingState.HELLO])

    def create_session(self, peerid):
        self._peerid = peerid
        self.send('SESSION {}'.format(self._peerid))
        l.info("sent SESSION")
        self.wait_for_states([SignallingState.SESSION])

    def _on_connection(self):
        self._update_state (SignallingState.OPEN)

    def _on_message(self, message):
        l.debug("received: " + message)
        if message == 'HELLO':
            self._update_state (SignallingState.HELLO)
            self.connected.fire()
        elif message == 'SESSION_OK':
            self._update_state (SignallingState.SESSION)
            self.session_created.fire()
        elif message.startswith('ERROR'):
            self._update_state (SignallingState.ERROR)
            self.error.fire(message)
        else:
            msg = json.loads(message)
            self.have_json.fire(msg)
        return False
Beispiel #4
0
    def __init__(self, server, id_):
        super().__init__(server)

        self.wss_connected.connect(self._on_connection)
        self.message.connect(self._on_message)
        self.state = SignallingState.NEW
        self._state_observer = StateObserver(self, "state", threading.Condition())

        self.id = id_
        self._peerid = None

        # fired when the hello has been received
        self.connected = Signal()
        # fired when the signalling server responds that the session creation is ok
        self.session_created = Signal()
        # fired on an error
        self.error = Signal()
        # fired when the peer receives some json data
        self.have_json = Signal()
Beispiel #5
0
 def __init__(self, element):
     WebRTCObserver.__init__(self)
     self.element = element
     self.signal_handlers = []
     self.signal_handlers.append(
         element.connect("on-negotiation-needed",
                         self._on_negotiation_needed))
     self.signal_handlers.append(
         element.connect("on-ice-candidate", self._on_ice_candidate))
     self.signal_handlers.append(
         element.connect("pad-added", self._on_pad_added))
     self.signal_handlers.append(
         element.connect("on-new-transceiver", self._on_new_transceiver))
     self.signal_handlers.append(
         element.connect("on-data-channel", self._on_data_channel))
     self.negotiation_needed = 0
     self._negotiation_needed_observer = StateObserver(
         self, "negotiation_needed", threading.Condition())
     self.on_negotiation_needed = Signal()
     self.on_ice_candidate = Signal()
     self.on_pad_added = Signal()
     self.on_new_transceiver = Signal()
Beispiel #6
0
    def __init__(self, name, max_threads):
        super().__init__(name=name)

        self.on_progress = Signal('on progress')
        self.on_finish = Signal('on finish')
        self.on_new_task = Signal('new task')

        self.__limit = max_threads if max_threads <= cpu_count(
        ) else cpu_count()
        self.__tasks = Queue()
        self.__size = 0
        self.__active_tasks = []
        self.__internal_state = Event()
        self.__abort = False
        self.__quite = False
        self.__hard_exit = False

        self.start()
Beispiel #7
0
class TaskSheduler(Thread):
    def __init__(self, name, max_threads):
        super().__init__(name=name)

        self.on_progress = Signal('on progress')
        self.on_finish = Signal('on finish')
        self.on_new_task = Signal('new task')

        self.__limit = max_threads if max_threads <= cpu_count(
        ) else cpu_count()
        self.__tasks = Queue()
        self.__size = 0
        self.__active_tasks = []
        self.__internal_state = Event()
        self.__abort = False
        self.__quite = False
        self.__hard_exit = False

        self.start()

    def add_task(self, task):
        self.__tasks.put(task)
        self.__size += 1
        self.__internal_state.set()

    def add_tasks(self, tasks):
        for task in tasks:
            self.__tasks.put(task)

        self.__size += len(tasks)
        self.__internal_state.set()

    def abort(self):
        self.__abort = True

    def quite(self):
        self.__quite = True
        self.__internal_state.set()

    def hard_exit(self):
        self.__hard_exit = True
        self.__internal_state.set()

    def run(self):
        while True:
            self.__internal_state.wait()

            while self.__tasks.unfinished_tasks > 0:
                if self._start_new():
                    self._start_new_task()

                self.__active_tasks = self._get_active_tasks()

                if self.__abort or self.__hard_exit:
                    self.__abort = False
                    break

            self.__size = 0
            self.__tasks = Queue()
            self.__internal_state.clear()
            self.on_finish.fire('Returning to idle')

            if self.__hard_exit or self.__quite:
                break

    def _start_new_task(self):
        task = self.__tasks.get_nowait()
        task.start()

        self.on_new_task.fire(started=task)
        self.__active_tasks.append(task)

    def _get_active_tasks(self):
        active = []
        finished = []

        for task in self.__active_tasks:
            if task.is_alive():
                active.append(task)
            else:
                finished.append(task)
                self.__tasks.task_done()

        if finished:
            self.on_progress.fire(total=self.__size,
                                  pending=self.__tasks.unfinished_tasks,
                                  queue_size=self.__tasks.qsize(),
                                  finished=finished)

        return active

    def _start_new(self):
        if len(self.__active_tasks) <= self.__limit and not self.__tasks.empty(
        ):
            return True
        return False

    def __repr__(self):
        return '<{}(name={}, max_threads={}) at {}>'.format(
            self.__class__.__name__, self.name, self.__limit, hex(id(self)))
Beispiel #8
0
class SignallingClientThread(object):
    """
    Connect to a signalling server
    """
    def __init__(self, server):
        # server string to connect to.  Passed directly to websockets.connect()
        self.server = server

        # fired after we have connected to the signalling server
        self.wss_connected = Signal()
        # fired every time we receive a message from the signalling server
        self.message = Signal()

        self._init_async()

    def _init_async(self):
        self._running = False
        self.conn = None
        self._loop = asyncio.new_event_loop()

        self._thread = AsyncIOThread(self._loop)
        self._thread.start()

        self._loop.call_soon_threadsafe(lambda: asyncio.ensure_future(self._a_loop()))

    async def _a_connect(self):
        # connect to the signalling server
        assert not self.conn
        sslctx = ssl.create_default_context(purpose=ssl.Purpose.CLIENT_AUTH)
        self.conn = await websockets.connect(self.server, ssl=sslctx)

    async def _a_loop(self):
        self._running = True
        l.info('loop started')
        await self._a_connect()
        self.wss_connected.fire()
        assert self.conn
        async for message in self.conn:
            self.message.fire(message)
        l.info('loop exited')

    def send(self, data):
        # send some information to the peer
        async def _a_send():
            await self.conn.send(data)
        self._loop.call_soon_threadsafe(lambda: asyncio.ensure_future(_a_send()))

    def stop(self):
        if self._running == False:
            return

        cond = threading.Condition()

        # asyncio, why you so complicated to stop ?
        tasks = asyncio.all_tasks(self._loop)
        async def _a_stop():
            if self.conn:
                await self.conn.close()
            self.conn = None

            to_wait = [t for t in tasks if not t.done()]
            if to_wait:
                l.info('waiting for ' + str(to_wait))
                done, pending = await asyncio.wait(to_wait)
            with cond:
                l.error('notifying cond')
                cond.notify()
            self._running = False

        with cond:
            self._loop.call_soon_threadsafe(lambda: asyncio.ensure_future(_a_stop()))
            l.error('cond waiting')
            cond.wait()
            l.error('cond waited')
        self._thread.stop_thread()
        self._thread.join()
        l.error('thread joined')
class ActionObserver(object):
    def __init__(self):
        def _action_continue(val):
            return val not in [
                GstValidate.ActionReturn.ERROR,
                GstValidate.ActionReturn.ERROR_REPORTED
            ]

        def _action_accum(previous, val):
            # we want to always keep any errors propagated
            if val in [
                    GstValidate.ActionReturn.ERROR,
                    GstValidate.ActionReturn.ERROR_REPORTED
            ]:
                return val
            if previous in [
                    GstValidate.ActionReturn.ERROR,
                    GstValidate.ActionReturn.ERROR_REPORTED
            ]:
                return previous

            # we want to always prefer async returns
            if previous in [
                    GstValidate.ActionReturn.ASYNC,
                    GstValidate.ActionReturn.INTERLACED
            ]:
                return previous
            if val in [
                    GstValidate.ActionReturn.ASYNC,
                    GstValidate.ActionReturn.INTERLACED
            ]:
                return val

            return val

        self.action = Signal(_action_continue, _action_accum)

    def _action(self, scenario, action):
        l.debug('executing action: ' + str(action.structure))
        return self.action.fire(Actions(action.structure.get_name()), action)

    def register_action_types(observer):
        if not isinstance(observer, ActionObserver):
            raise TypeError

        GstValidate.register_action_type(
            Actions.CREATE_OFFER.value, "webrtc", observer._action, None,
            "Instruct a create-offer to commence",
            GstValidate.ActionTypeFlags.NONE)
        GstValidate.register_action_type(Actions.CREATE_ANSWER.value, "webrtc",
                                         observer._action, None,
                                         "Create answer",
                                         GstValidate.ActionTypeFlags.NONE)
        GstValidate.register_action_type(
            Actions.WAIT_FOR_NEGOTIATION_STATE.value, "webrtc",
            observer._action, None,
            "Wait for a specific negotiation state to be reached",
            GstValidate.ActionTypeFlags.NONE)
        GstValidate.register_action_type(Actions.ADD_STREAM.value, "webrtc",
                                         observer._action, None,
                                         "Add a stream to the webrtcbin",
                                         GstValidate.ActionTypeFlags.NONE)
        GstValidate.register_action_type(
            Actions.ADD_DATA_CHANNEL.value, "webrtc", observer._action, None,
            "Add a data channel to the webrtcbin",
            GstValidate.ActionTypeFlags.NONE)
        GstValidate.register_action_type(
            Actions.SEND_DATA_CHANNEL_STRING.value, "webrtc", observer._action,
            None, "Send a message using a data channel",
            GstValidate.ActionTypeFlags.NONE)
        GstValidate.register_action_type(
            Actions.WAIT_FOR_DATA_CHANNEL_STATE.value, "webrtc",
            observer._action, None, "Wait for data channel to reach state",
            GstValidate.ActionTypeFlags.NONE)
        GstValidate.register_action_type(Actions.CLOSE_DATA_CHANNEL.value,
                                         "webrtc", observer._action, None,
                                         "Close a data channel",
                                         GstValidate.ActionTypeFlags.NONE)
        GstValidate.register_action_type(Actions.WAIT_FOR_DATA_CHANNEL.value,
                                         "webrtc", observer._action, None,
                                         "Wait for a data channel to appear",
                                         GstValidate.ActionTypeFlags.NONE)
        GstValidate.register_action_type(
            Actions.WAIT_FOR_DATA_CHANNEL_STRING.value, "webrtc",
            observer._action, None,
            "Wait for a data channel to receive a message",
            GstValidate.ActionTypeFlags.NONE)
        GstValidate.register_action_type(
            Actions.WAIT_FOR_NEGOTIATION_NEEDED.value, "webrtc",
            observer._action, None,
            "Wait for a the on-negotiation-needed signal to fire",
            GstValidate.ActionTypeFlags.NONE)
        GstValidate.register_action_type(Actions.SET_WEBRTC_OPTIONS.value,
                                         "webrtc", observer._action, None,
                                         "Set some webrtc options",
                                         GstValidate.ActionTypeFlags.NONE)
Beispiel #10
0
class WebRTCBinObserver(WebRTCObserver):
    """
    Observe a webrtcbin element.
    """
    def __init__(self, element):
        WebRTCObserver.__init__(self)
        self.element = element
        self.signal_handlers = []
        self.signal_handlers.append(
            element.connect("on-negotiation-needed",
                            self._on_negotiation_needed))
        self.signal_handlers.append(
            element.connect("on-ice-candidate", self._on_ice_candidate))
        self.signal_handlers.append(
            element.connect("pad-added", self._on_pad_added))
        self.signal_handlers.append(
            element.connect("on-new-transceiver", self._on_new_transceiver))
        self.signal_handlers.append(
            element.connect("on-data-channel", self._on_data_channel))
        self.negotiation_needed = 0
        self._negotiation_needed_observer = StateObserver(
            self, "negotiation_needed", threading.Condition())
        self.on_negotiation_needed = Signal()
        self.on_ice_candidate = Signal()
        self.on_pad_added = Signal()
        self.on_new_transceiver = Signal()

    def _on_negotiation_needed(self, element):
        self.negotiation_needed += 1
        self._negotiation_needed_observer.update(self.negotiation_needed)
        self.on_negotiation_needed.fire()

    def _on_ice_candidate(self, element, mline, candidate):
        self.on_ice_candidate.fire(mline, candidate)

    def _on_pad_added(self, element, pad):
        self.on_pad_added.fire(pad)

    def _on_description_set(self, promise, desc):
        new_state = self._update_negotiation_from_description_state(desc)
        if new_state == NegotiationState.OFFER_SET:
            self.on_offer_set.fire(desc)
        elif new_state == NegotiationState.ANSWER_SET:
            self.on_answer_set.fire(desc)

    def _on_new_transceiver(self, element, transceiver):
        self.on_new_transceiver.fire(transceiver)

    def _on_data_channel(self, element, channel):
        observer = WebRTCBinDataChannelObserver(channel, channel.props.label,
                                                'remote')
        self.add_channel(observer)

    def _update_negotiation_from_description_state(self, desc):
        new_state = None
        if desc.type == GstWebRTC.WebRTCSDPType.OFFER:
            new_state = NegotiationState.OFFER_SET
        elif desc.type == GstWebRTC.WebRTCSDPType.ANSWER:
            new_state = NegotiationState.ANSWER_SET
        assert new_state is not None
        self._update_negotiation_state(new_state)
        return new_state

    def _deepcopy_session_description(self, desc):
        # XXX: passing 'offer' to both a promise and an action signal without
        # a deepcopy will segfault...
        new_sdp = GstSdp.SDPMessage.new()[1]
        GstSdp.sdp_message_parse_buffer(bytes(desc.sdp.as_text().encode()),
                                        new_sdp)
        return GstWebRTC.WebRTCSessionDescription.new(desc.type, new_sdp)

    def _on_offer_created(self, promise, element):
        self._update_negotiation_state(NegotiationState.OFFER_CREATED)
        reply = promise.get_reply()
        offer = reply['offer']

        new_offer = self._deepcopy_session_description(offer)
        promise = Gst.Promise.new_with_change_func(self._on_description_set,
                                                   new_offer)

        new_offer = self._deepcopy_session_description(offer)
        self.element.emit('set-local-description', new_offer, promise)
        self.on_offer_created.fire(offer)

    def _on_answer_created(self, promise, element):
        self._update_negotiation_state(NegotiationState.ANSWER_CREATED)
        reply = promise.get_reply()
        offer = reply['answer']

        new_offer = self._deepcopy_session_description(offer)
        promise = Gst.Promise.new_with_change_func(self._on_description_set,
                                                   new_offer)

        new_offer = self._deepcopy_session_description(offer)
        self.element.emit('set-local-description', new_offer, promise)
        self.on_answer_created.fire(offer)

    def create_offer(self, options=None):
        promise = Gst.Promise.new_with_change_func(self._on_offer_created,
                                                   self.element)
        self.element.emit('create-offer', options, promise)

    def create_answer(self, options=None):
        promise = Gst.Promise.new_with_change_func(self._on_answer_created,
                                                   self.element)
        self.element.emit('create-answer', options, promise)

    def set_remote_description(self, desc):
        promise = Gst.Promise.new_with_change_func(self._on_description_set,
                                                   desc)
        self.element.emit('set-remote-description', desc, promise)

    def add_ice_candidate(self, mline, candidate):
        self.element.emit('add-ice-candidate', mline, candidate)

    def add_data_channel(self, ident):
        channel = self.element.emit('create-data-channel', ident, None)
        observer = WebRTCBinDataChannelObserver(channel, ident, 'local')
        self.add_channel(observer)

    def wait_for_negotiation_needed(self, generation):
        self._negotiation_needed_observer.wait_for((generation, ))