Exemplo n.º 1
0
    def _connect_impl(self):

        # Use default values for connection json. If we want to changes values
        # (or add new info), we just need to add them in req (using json format)
        # For instance:
        req = bytes('{ "%s": "%s", "%s": "%s", "%s": "%s"}' % (
            "arstream2_client_stream_port", PDRAW_LOCAL_STREAM_PORT,
            "arstream2_client_control_port", PDRAW_LOCAL_CONTROL_PORT,
            "arstream2_supported_metadata_version", "1"), 'utf-8')
        device_id = b""

        device_conn_cfg = od.struct_arsdk_device_conn_cfg(
            ctypes.create_string_buffer(b"arsdk-ng"), ctypes.create_string_buffer(b"desktop"),
            ctypes.create_string_buffer(bytes(device_id)), ctypes.create_string_buffer(req))

        # Send connection command
        self._connect_future = Future(self._thread_loop)
        res = od.arsdk_device_connect(
            self._device.arsdk_device,
            device_conn_cfg,
            self._device_cbs_cfg,
            self._thread_loop.pomp_loop
        )
        if res != 0:
            self.logger.error(f"Error while connecting: {res}")
            self._connect_future.set_result(False)
        else:
            self.logger.info("Connection in progress...")
        return self._connect_future
Exemplo n.º 2
0
    def _send_command_impl(self, message, args, quiet=False):
        """
        Must be run from the pomp loop
        """

        argv = message._encode_args(*args.values())

        # Check if we are already sending a command.
        # if it the case, wait for the lock to be released
        # Define an Arsdk structure
        cmd = od.struct_arsdk_cmd()

        # Find the description of the command in libarsdk.so
        command_desc = od.struct_arsdk_cmd_desc.in_dll(
            od._libraries["libarsdk.so"], message.g_arsdk_cmd_desc)  # pylint: disable=E1101

        # argv is an array of struct_arsdk_value
        argc = argv._length_
        # Encode the command
        res = od.arsdk_cmd_enc_argv(ctypes.pointer(cmd),
                                    ctypes.pointer(command_desc), argc, argv)

        if res != 0:
            self.logger.error(
                f"Error while encoding command {message.fullName}: {res}")
        else:
            self.logger.debug(f"Command {message.fullName} has been encoded")

        # cmd_itf must exist to send command
        if self._cmd_itf is None:
            raise RuntimeError(
                "[sendcmd] Error cmd interface seems to be destroyed")

        # Send the command
        send_command_future = Future(self._thread_loop)
        send_status_userdata = ctypes.pointer(
            ctypes.py_object((send_command_future, message, args)))
        self._send_status_userdata[id(
            send_command_future)] = send_status_userdata
        res = od.arsdk_cmd_itf_send(self._cmd_itf, ctypes.pointer(cmd),
                                    self._send_status, send_status_userdata)

        if res != 0:
            self.logger.error(f"Error while sending command: {res}")
            send_command_future.set_result(False)
            return send_command_future

        event = ArsdkMessageEvent(message, args)
        # Update the currently monitored expectations
        self._scheduler.process_event(event)
        log_msg = f"{event} has been sent to the device"
        if quiet:
            self.logger.debug(log_msg)
        else:
            self.logger.info(log_msg)

        return send_command_future
Exemplo n.º 3
0
 def _ready_to_play(self, pdraw, demuxer, ready, userdata):
     self.logger.info(f"_ready_to_play({ready}) called")
     self._is_ready_to_play = bool(ready)
     if self._is_ready_to_play:
         if self._play_resp_future.done():
             self._play_resp_future = Future(self.pdraw_thread_loop)
         self._play_impl()
         if self.start_callback is not None:
             self.callbacks_thread_loop.run_async(self.start_callback)
     else:
         if self.end_callback is not None:
             self.callbacks_thread_loop.run_async(self.end_callback)
Exemplo n.º 4
0
 def close(self):
     """
     Close a playing or paused video stream session
     """
     if self.state is PdrawState.Closed:
         f = Future(self.pdraw_thread_loop)
         f.set_result(True)
     if self.state in (PdrawState.Opened, PdrawState.Paused,
                       PdrawState.Playing, PdrawState.Error):
         self.logger.debug(f"pdraw closing from the {self.state} state")
         if self._close_resp_future.done():
             self._close_resp_future = Future(self.pdraw_thread_loop)
             self._close_resp_future.add_done_callback(
                 self._on_close_resp_done)
         f = self._close_resp_future
         self.state = PdrawState.Closing
         self.pdraw_thread_loop.run_async(self._close_stream)
     elif self.state is not PdrawState.Closing:
         f = Future(self.pdraw_thread_loop)
         f.set_result(False)
     else:
         f = self._close_resp_future
     return f
Exemplo n.º 5
0
    def async_disconnect(self, *, timeout=5):
        """
        Disconnects current device (if any)
        Blocks until it is done or abandoned

        :rtype: bool
        """
        if not self.connected:
            f = Future(self._thread_loop)
            f.set_result(True)
            self.logger.debug("Already disconnected")
            return f

        if self._connected_future is not None:
            f = Future(self._thread_loop)
            f.set_result(False)
            self.logger.warning(
                "Cannot disconnect while a connection is in progress")
            return f

        self.logger.debug("We are not disconnected yet")
        disconnected = self._thread_loop.run_async(self._disconnection_impl)
        return disconnected
Exemplo n.º 6
0
    def _disconnection_impl(self):
        f = Future(self._thread_loop)
        if not self.connected:
            return f.set_result(True)

        self._disconnect_future = f
        res = od.arsdk_device_disconnect(self._device.arsdk_device)
        if res != 0:
            self.logger.error(
                f"Error while disconnecting from device: {self._ip_addr} ({res})")
            self._disconnect_future.set_result(False)
        else:
            self.logger.info(
                f"disconnected from device: {self._ip_addr}")
        return self._disconnect_future
Exemplo n.º 7
0
    def pause(self):
        """
        Pause the currently playing video
        """
        if self.pdraw is None:
            self.logger.error("Error Pdraw interface seems to be destroyed")
            self._pause_resp_future.set_result(False)
            return self._pause_resp_future

        if self._pause_resp_future.done():
            self._pause_resp_future = Future(self.pdraw_thread_loop)
        if self.state is PdrawState.Playing:
            self.pdraw_thread_loop.run_async(self._pause_impl)
        elif self.state is PdrawState.Closed:
            # Pause an closed stream is OK
            self._pause_resp_future.set_result(True)
        else:
            self.logger.error(
                f"Cannot pause stream from the {self.state} state")
            self._pause_resp_future.set_result(False)
        return self._pause_resp_future
Exemplo n.º 8
0
    def _open_stream(self):
        """
        Opening pdraw stream using an rtsp url
        """
        self._open_resp_future = Future(self.pdraw_thread_loop)
        if self.state not in (PdrawState.Error, PdrawState.Stopped,
                              PdrawState.Closed, PdrawState.Created):
            self.logger.warning(f"Cannot open stream from {self.state}")
            self._open_resp_future.set_result(False)
            return self._open_resp_future

        self.state = PdrawState.Opening
        if not self.pdraw and not self._pdraw_new():
            self._open_resp_future.set_result(False)
            return self._open_resp_future

        ret = self._open_url()

        if not ret:
            self._open_resp_future.set_result(False)

        return self._open_resp_future
Exemplo n.º 9
0
 def _stop(self):
     if self._stop_resp_future.done():
         self._stop_resp_future = Future(self.pdraw_thread_loop)
     if not self.pdraw:
         self._stop_resp_future.set_result(True)
         return self._stop_resp_future
     res = od.pdraw_stop(self.pdraw)
     if res != 0:
         self.logger.error(f"cannot stop pdraw session {res}")
         self._stop_resp_future.set_result(False)
     if self.callbacks_thread_loop.stop():
         self.logger.info("pdraw callbacks thread loop stopped")
     # cleanup some FDs from the callbacks thread loop that might
     # have been lost
     for stream in self.streams.values():
         if stream['video_queue_event'] is not None:
             self.logger.warning(
                 "cleanup leftover pdraw callbacks eventfds")
             self.callbacks_thread_loop.remove_event_from_loop(
                 stream['video_queue_event'])
             stream['video_queue_event'] = None
     return self._stop_resp_future
Exemplo n.º 10
0
    def async_connect(self, *, timeout=None, later=False, retry=1):
        if timeout is None:
            timeout = 6 * retry

        # If not already connected to a device
        if self.connected:
            f = Future(self._thread_loop)
            f.set_result(True)
            return f

        if self._connected_future is None:
            if not later:
                self._connected_future = self._thread_loop.run_async(
                    self._do_connect, timeout, retry)
            else:
                self._connected_future = self._thread_loop.run_later(
                    self._do_connect, timeout, retry)

        def _on_connect_done(f):
            self._connected_future = None
        self._connected_future.add_done_callback(_on_connect_done)

        return self._connected_future
Exemplo n.º 11
0
    def __init__(
        self,
        name=None,
        device_name=None,
        server_addr=None,
        buffer_queue_size=8,
        pdraw_thread_loop=None,
    ):
        """
        :param name: (optional) pdraw client name (used by Olympe logs)
        :type name: str
        :param device_name: (optional) the drone device name
            (used by Olympe logs)
        :type device_name: str
        :param buffer_queue_size: (optional) video buffer queue size
            (defaults to 8)
        :type buffer_queue_size: int
        """

        super().__init__(name, device_name, "pdraw")

        if pdraw_thread_loop is None:
            self.own_pdraw_thread_loop = True
            self.pdraw_thread_loop = PompLoopThread(self.logger)
            self.pdraw_thread_loop.start()
        else:
            self.own_pdraw_thread_loop = False
            self.pdraw_thread_loop = pdraw_thread_loop

        self.callbacks_thread_loop = PompLoopThread(
            self.logger, parent=self.pdraw_thread_loop)
        self.callbacks_thread_loop.start()
        self.buffer_queue_size = buffer_queue_size
        self.pomp_loop = self.pdraw_thread_loop.pomp_loop

        self._open_resp_future = Future(self.pdraw_thread_loop)
        self._close_resp_future = Future(self.pdraw_thread_loop)
        self._close_resp_future.add_done_callback(self._on_close_resp_done)
        self._play_resp_future = Future(self.pdraw_thread_loop)
        self._pause_resp_future = Future(self.pdraw_thread_loop)
        self._stop_resp_future = Future(self.pdraw_thread_loop)
        self._state = PdrawState.Created
        self._state_lock = threading.Lock()
        self._state_wait_events = {k: list() for k in PdrawState}

        self.pdraw = od.POINTER_T(od.struct_pdraw)()
        self.pdraw_demuxer = od.POINTER_T(od.struct_pdraw_demuxer)()
        self.streams = defaultdict(StreamFactory)
        self.session_metadata = {}

        self.outfiles = {
            od.VDEF_FRAME_TYPE_CODED: {
                'data': None,
                'meta': None,
                'info': None,
            },
            od.VDEF_FRAME_TYPE_RAW: {
                'data': None,
                'meta': None,
                'info': None,
            },
        }

        self.frame_callbacks = {
            (od.VDEF_FRAME_TYPE_CODED, od.VDEF_CODED_DATA_FORMAT_AVCC): None,
            (od.VDEF_FRAME_TYPE_CODED, od.VDEF_CODED_DATA_FORMAT_BYTE_STREAM):
            None,
            (od.VDEF_FRAME_TYPE_RAW, None): None,
        }
        self.start_callback = None
        self.end_callback = None
        self.flush_callbacks = {
            od.VDEF_FRAME_TYPE_CODED: None,
            od.VDEF_FRAME_TYPE_RAW: None,
        }

        self.url = None
        if server_addr is None:
            server_addr = "192.168.42.1"
        self.server_addr = server_addr
        self.resource_name = "live"
        self.media_name = None

        self.demuxer_cbs = od.struct_pdraw_demuxer_cbs.bind({
            "open_resp":
            self._open_resp,
            "close_resp":
            self._close_resp,
            "unrecoverable_error":
            self._unrecoverable_error,
            "ready_to_play":
            self._ready_to_play,
            "play_resp":
            self._play_resp,
            "pause_resp":
            self._pause_resp,
            "seek_resp":
            self._seek_resp,
            "select_media":
            self._select_media,
            "end_of_range":
            self._end_of_range,
        })
        self.pdraw_cbs = od.struct_pdraw_cbs.bind({
            "socket_created": self._socket_created,
            "media_added": self._media_added,
            "media_removed": self._media_removed,
            "stop_resp": self.stop_resp,
        })

        self.video_sink_vt = {
            od.VDEF_FRAME_TYPE_CODED: _CodedVideoSink,
            od.VDEF_FRAME_TYPE_RAW: _RawVideoSink,
        }

        self.pdraw_thread_loop.register_cleanup(self._dispose)
Exemplo n.º 12
0
    def play(self,
             url=None,
             media_name="DefaultVideo",
             resource_name="live",
             timeout=5):
        """
        Play a video

        By default, open and play a live video streaming session available
        from rtsp://192.168.42.1/live where "192.168.42.1" is the default IP
        address of a physical (Anafi) drone. The default is equivalent to
        `Pdraw.play(url="rtsp://192.168.42.1/live")`

        For a the live video streaming from a **simulated drone**, you have to
        specify the default simulated drone IP address (10.202.0.1) instead:
        `Pdraw.play(url="rtsp://10.202.0.1/live")`.

        The `url` parameter can also point to a local file example:
        `Pdraw.play(url="file://~/Videos/100000010001.MP4")`.

        :param url: rtsp or local file video URL
        :type url: str
        :param media_name: name of the media/track (defaults to "DefaultVideo").
            If the provided media name is not available from the requested video
            stream, the default media is selected instead.
        :type media_name: str

        """
        if self.pdraw is None:
            self.logger.error("Error Pdraw interface seems to be destroyed")
            self._play_resp_future.set_result(False)
            return self._play_resp_future

        if self.state in (PdrawState.Opening, PdrawState.Closing):
            self.logger.error(
                f"Cannot play stream from the {self.state} state")
            f = Future(self.pdraw_thread_loop)
            f.set_result(False)
            return f

        self.resource_name = resource_name
        self.media_name = media_name

        if url is None:
            self.url = b"rtsp://%s/%s" % (self.server_addr.encode(),
                                          self.resource_name.encode())
        else:
            if isinstance(url, bytes):
                url = url.decode('utf-8')
            if url.startswith('file://'):
                url = url[7:]
            if url.startswith('~/'):
                url = os.path.expanduser(url)
            url = os.path.expandvars(url)
            url = url.encode('utf-8')
            self.url = url

        # reset session metadata from any previous session
        self.session_metadata = {}
        self.streams = defaultdict(StreamFactory)

        self._open_output_files()
        if self._play_resp_future.done():
            self._play_resp_future = Future(self.pdraw_thread_loop)
        if self.state in (PdrawState.Created, PdrawState.Closed):
            open_resp_future = self.pdraw_thread_loop.run_async(
                self._open_stream)
            f = self.pdraw_thread_loop.complete_futures(
                open_resp_future, self._play_resp_future)
        else:
            f = self._play_resp_future
            self.pdraw_thread_loop.run_async(self._play_impl)
        if timeout > 0:
            return f.result_or_cancel(timeout=timeout)
        else:
            return f