Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • Users
  • Groups
  • Search
  • Get Qt Extensions
  • Unsolved
Collapse
Brand Logo
  1. Home
  2. Qt Development
  3. Qt for Python
  4. Pyside 6.5.1.1 - QmediaPlayer setPosition locks theapp
Forum Updated to NodeBB v4.3 + New Features

Pyside 6.5.1.1 - QmediaPlayer setPosition locks theapp

Scheduled Pinned Locked Moved Solved Qt for Python
4 Posts 1 Posters 413 Views
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • O Offline
    O Offline
    Old_Man_Down_The_Road
    wrote on last edited by
    #1
    This post is deleted!
    O 1 Reply Last reply
    0
    • O Offline
      O Offline
      Old_Man_Down_The_Road
      wrote on last edited by
      #4

      The original problem was QmediaPlayer.setPosition led to the app locking up eventually.

      The code below has passed testing which the original code would not. Does not necessarily pass the "fixed yeah" stage as I am suspicious still.

      So what was done:?

      All media_player methods were bundled into one class
      _Video_Player

      Class Video_Handler was adjusted accordingly

      The key part is in the Video_Handler _setup_media_player method. In the snippet below you will note the two sleep statements , one after moving to thread and one after thread.start. With these no app lockups have yet to be observed.

      Hope this helps some one!

      self._media_player.moveToThread(self._thread)
      
              sleep(
                  0.1
              )  # Note: These are important, without them setPosition in the media player sometimes  locks the app
      
              media_player_thread = self._media_player.thread()
              is_in_main_thread = media_player_thread == qtC.QThread.currentThread()
              print(f"Is in main thread: {is_in_main_thread}")
      
              self._thread.start()
              sleep(
                  0.5
              )  # Note: These are important, without them setPosition in the media player sometimes  locks the app
      
      class _Video_Player(qtC.QObject):
          """
          Implements a customer video player object
          """
      
          current_frame_handler = qtC.Signal(int)
          duration_changed_handler = qtC.Signal(int)
          frame_changed_handler = qtC.Signal(qtM.QVideoFrame)
          is_available_handler = qtC.Signal(bool)
          media_status_changed_handler = qtC.Signal(qtM.QMediaPlayer.MediaStatus)
          pause_handler = qtC.Signal()
          play_handler = qtC.Signal()
          position_changed_handler = qtC.Signal(int)
          seekable_changed_handler = qtC.Signal(bool)
          set_position_handler = qtC.Signal(int)
          stop_handler = qtC.Signal()
      
          def __init__(self, parent: qtC.QObject | None, input_file: str) -> None:
              """
              Sets up the video_player object for use
      
              Args:
                  parent (qtC.QObject | None): Set the parent of the object
                  input_file (str): Set the source file of the media player
      
              """
              assert parent is None or isinstance(
                  parent, qtC.QObject
              ), f"{parent =} must be None or a qtC.QObject"
              assert (
                  isinstance(input_file, str) and input_file.strip() != ""
              ), f"{input_file =} must be a non-empty str"
      
              super().__init__(parent)
      
              self._current_position = -1
              self._video_sink = qtM.QVideoSink()
              self._audio_output = qtM.QAudioOutput()
      
              self._media_player = qtM.QMediaPlayer()
              self._media_player.setVideoSink(self._video_sink)
              self._media_player.setAudioOutput(self._audio_output)
              self._audio_output.setVolume(1)
      
              # Set input source video file
              self._media_player.setSource(qtC.QUrl.fromLocalFile(input_file))
      
              # Hook up signals
              self._video_sink.videoFrameChanged.connect(self._frame_handler)
              self._media_player.durationChanged.connect(self._duration_changed)
              self._media_player.positionChanged.connect(self._position_changed)
              self._media_player.errorOccurred.connect(self._player_error)
              self._media_player.mediaStatusChanged.connect(self._media_status_change)
              self._media_player.seekableChanged.connect(self._seekable_changed)
              self.frame_changed_handler.connect(self._video_sink.setVideoFrame)
              self.is_available_handler.connect(self._media_player.isAvailable)
              self.play_handler.connect(self._media_player.play)
              self.set_position_handler.connect(self.seek)
              self.pause_handler.connect(self._media_player.pause)
              self.stop_handler.connect(self.stop)
      
          @qtC.Slot()
          def _duration_changed(self, duration: int) -> None:
              """Handles a video duration change
      
              Args:
                  duration (int): The length of the video
              """
              self.duration_changed_handler.emit(duration)
      
          @qtC.Slot()
          def _frame_handler(self, frame: qtM.QVideoFrame) -> None:
              """Handles the video frame changing signal
      
              Args:
                  frame (qtM.QVideoFrame): THe video frame to be displayed
              """
              self.frame_changed_handler.emit(frame)
      
          @qtC.Slot()
          def _position_changed(self, position_milliseconds: int) -> None:
              """
              Handles the position changing signal
      
              Args:
                  position_milliseconds (int): The current position of the media player in milliseconds.
              """
              self.position_changed_handler.emit(position_milliseconds)
      
          @qtC.Slot(qtM.QMediaPlayer.Error, str)
          def _player_error(self, error, error_string):
              """Called when the media player encounters an error."""
              print(f"Error: {error} - {error_string}")
      
          def available(self) -> bool:
              """
              Returns whether the media player is available
      
              Returns:
                  bool: True if Available, False if noe
      
              """
      
              return self._media_player.isAvailable()
      
          def current_frame(self) -> int:
              return self._media_player.position()
      
          @qtC.Slot()
          def _media_status_change(self, media_status: qtM.QMediaPlayer.mediaStatus) -> None:
              """Signals the state of the media has changed
      
              Args:
                  media_status (qtM.QMediaPlayer.mediaStatus): The status of the media player
              """
              self.media_status_changed_handler.emit(media_status)
      
          @qtC.Slot()
          def _seekable_changed(self, seekable: bool) -> None:
              """
              Signals the seekable status has changed
      
              Args:
                  seekable (bool): True if the media player is seekable, False otherwise.
              """
              self.seekable_changed_handler.emot(seekable)
      
          @qtC.Slot()
          def seek(self, position: int) -> None:
              """
              Seeks to a position
      
              Args:
                  position (int): THe position in milliseconds to move to
              """
              if self._current_position != position:
                  if (
                      self._media_player.isSeekable()
                      and self._media_player.mediaStatus()
                      == qtM.QMediaPlayer.MediaStatus.BufferedMedia
                  ):
                      self._current_position = position
                      self._media_player.setPosition(position)
      
          def state(self) -> str:
              playback_state = self._media_player.playbackState()
      
              if playback_state == qtM.QMediaPlayer.PlaybackState.PlayingState:
                  return "playing"
              elif playback_state == qtM.QMediaPlayer.PlaybackState.PausedState:
                  return "paused"
              elif playback_state == qtM.QMediaPlayer.PlaybackState.StoppedState:
                  return "stop"
      
          def stop(self):
              self._media_player.stop()
              self._media_player.setVideoSink(None)
              self._media_player.setAudioOutput(None)
      
      
      @dataclasses.dataclass
      class Video_Handler:
          aspect_ratio: str
          input_file: str
          output_edit_folder: str
          encoding_info: Encoding_Details
          video_display: qtg.Label
          video_slider: qtg.Slider
          frame_display: qtg.LCD
          display_width: int
          display_height: int
          update_slider: bool = True
          source_state: Literal[
              "NoMedia",
              "Loading",
              "Loaded",
              "Stalled",
              "Buffering",
              "Buffered",
              "EndOfMedia",
              "InvalidMedia",
          ] = "NoMedia"
          state_handler: Callable = None
      
          # Private instance variables
          _frame_count: int = 0
          _frame_rate: float = 25  # Default to 25 frames per second
          _frame_width: int = 720
          _frame_height: int = 576
          _current_frame: int = -1
      
          def __post_init__(self) -> None:
              """Sets-up the instance"""
              assert isinstance(self.aspect_ratio, str) and self.aspect_ratio in (
                  sys_consts.AR169,
                  sys_consts.AR43,
              ), f"{self.aspect_ratio=}. Must be a AR169 | AR43"
      
              assert (
                  isinstance(self.input_file, str) and self.input_file.strip() != ""
              ), f"{self.input_file=}. Must be a non-empty str"
      
              assert (
                  isinstance(self.output_edit_folder, str)
                  and self.output_edit_folder.strip() != ""
              ), f"{self.output_edit_folder=}. Must be a non-empty str"
      
              assert isinstance(
                  self.encoding_info, Encoding_Details
              ), f"{self.encoding_info=}. Must be an instance of Encoding_Details"
      
              assert isinstance(
                  self.video_display, qtg.Label
              ), f"{self.video_display=}. Must be a qtg.Label"
      
              assert isinstance(
                  self.video_slider, qtg.Slider
              ), f"{self.video_slider=}. Must be a qtg.Slider"
      
              assert isinstance(
                  self.frame_display, qtg.LCD
              ), f"{self.frame_display=}. Must be a qtg.Slider"
      
              assert isinstance(
                  self.display_width, int
              ), f"{self.display_width=}. Must be an int"
      
              assert isinstance(
                  self.display_height, int
              ), f"{self.display_height=}. Must be an int"
      
              assert isinstance(
                  self.update_slider, bool
              ), f"{self.update_slider=}. Must be a bool"
      
              self._frame_width = self.encoding_info.video_width
              self._frame_height = self.encoding_info.video_height
              self._frame_rate = self.encoding_info.video_frame_rate
              self._frame_count = self.encoding_info.video_frame_count
      
              self._setup_media_player()
      
          def _setup_media_player(self):
              """Sets up the media_player instance"""
              self._media_player = _Video_Player(parent=None, input_file=self.input_file)
      
              self._media_player.frame_changed_handler.connect(self._frame_handler)
              self._media_player.is_available_handler.connect(self.available)
              self._media_player.media_status_changed_handler.connect(
                  self._media_status_change
              )
              self._media_player.position_changed_handler.connect(self._position_changed)
      
              self._thread = qtC.QThread()
              self._media_player.moveToThread(self._thread)
      
              sleep(
                  0.1
              )  # Note: These are important, without them setPosition in the media player sometimes  locks the app
      
              media_player_thread = self._media_player.thread()
              is_in_main_thread = media_player_thread == qtC.QThread.currentThread()
              print(f"Is in main thread: {is_in_main_thread}")
      
              self._thread.start()
              sleep(
                  0.5
              )  # Note: These are important, without them setPosition in the media player sometimes  locks the app
      
          @qtC.Slot()
          def _frame_handler(self, frame: qtM.QVideoFrame) -> None:
              """Handles displaying the video frame
      
              Args:
                  frame (qtM.QVideoFrame): THe video frame to be displayed
              """
              if frame.isValid():
                  image = frame.toImage().scaled(self.display_width, self.display_height)
                  pixmap = qtG.QPixmap.fromImage(image)
      
                  if shiboken6.isValid(
                      self.video_display.guiwidget_get
                  ):  # Should not need this check but on shutdown I sometimes got the  dreaded C++ object deleted error
                      self.video_display.guiwidget_get.setPixmap(pixmap)
      
          def _media_status_change(self, media_status: qtM.QMediaPlayer.mediaStatus) -> None:
              """When the status of the media player changes this method sets the source_state var and calls the
              state_handler if provided.
      
              Args:
                  media_status (qtM.QMediaPlayer.mediaStatus): The status of the media player
              """
              match media_status:
                  case qtM.QMediaPlayer.MediaStatus.NoMedia:
                      self.source_state = "NoMedia"
                  case qtM.QMediaPlayer.MediaStatus.LoadingMedia:
                      self.source_state = "Loading"
                  case qtM.QMediaPlayer.MediaStatus.LoadedMedia:
                      self.source_state = "Loaded"
                  case qtM.QMediaPlayer.MediaStatus.StalledMedia:
                      self.source_state = "Stalled"
                  case qtM.QMediaPlayer.MediaStatus.BufferingMedia:
                      self.source_state = "Buffering"
                  case qtM.QMediaPlayer.MediaStatus.BufferedMedia:
                      self.source_state = "Buffered"
                  case qtM.QMediaPlayer.MediaStatus.EndOfMedia:
                      self.source_state = "EndOfMedia"
                  case qtM.QMediaPlayer.MediaStatus.InvalidMedia:
                      self.source_state = "InvalidMedia"
      
              if self.state_handler and isinstance(self.state_handler, Callable):
                  self.state_handler()
      
          @qtC.Slot()
          def _position_changed(self, position_milliseconds: int) -> None:
              """
              A method that is called when the position of the media player changes.
              Converts the current position in milliseconds to the corresponding frame number,
              updates the video slider if necessary, and emits a signal indicating that the position has changed.
      
              Args:
                  position_milliseconds (int): The current position of the media player in milliseconds.
              """
              frame_number = int(position_milliseconds * self._frame_rate // 1000)
      
              if self.update_slider and self.video_slider is not None:
                  self.video_slider.value_set(frame_number)
              self.frame_display.value_set(frame_number)
      
          def get_current_frame(self) -> int:
              """
              Returns the current frame number based on the current position of the media player and the frame rate of the video.
      
              Returns:
                  int: The current frame number.
              """
              return int(self._media_player.current_frame() * self._frame_rate // 1000)
      
          def available(self) -> bool:
              """Checks if the media player is supported on the platform
      
              Returns:
                  bool: True if the media player is supported, False otherwise.
              """
              return self._media_player.available()
      
          def play(self) -> None:
              """
              Starts playing the media.
              """
              self._media_player.play_handler.emit()
              return None
      
          def pause(self) -> None:
              """
              Pauses the media.
              """
              self._media_player.pause_handler.emit()
      
          def seek(self, frame: int) -> None:
              """
              Seeks to the specified frame number.
      
              Args:
                  frame (int): The frame number to seek to.
              """
      
              if self._current_frame != frame:
                  state = self._media_player.state()
      
                  if state == "playing":
                      self._media_player.pause_handler.emit()
                      sleep(0.2)
      
                  self._current_frame = frame
                  time_offset = int((1000 / self._frame_rate) * frame)
                  self._media_player.set_position_handler.emit(time_offset)
      
                  if state == "playing":
                      pass
                      # self._media_player.play_handler.emit() # Leads to stuttering video sometimes
      
          def shutdown(self) -> None:
              """
              Stops playing the media and releases the player's resources.
              """
              if self._media_player is not None:
                  self._media_player.stop_handler.emit()
      
                  if self._thread and self._thread.isRunning():
                      self._thread.quit()
                      self._thread.wait()
                      self._thread.deleteLater()
                      self._thread = None
      
              return None
      
          def state(self) -> str:
              """
              Returns the current playback state of the media player.
      
              Returns:
                  str: The current playback state
                      - "playing": The media player is currently playing.
                      - "paused": The media player is currently paused.
                      - "stop": The media player is currently stopped.
              """
              return self._media_player.state()
      
      1 Reply Last reply
      0
      • O Old_Man_Down_The_Road

        This post is deleted!

        O Offline
        O Offline
        Old_Man_Down_The_Road
        wrote on last edited by
        #2
        This post is deleted!
        1 Reply Last reply
        0
        • O Offline
          O Offline
          Old_Man_Down_The_Road
          wrote on last edited by
          #3
          This post is deleted!
          1 Reply Last reply
          0
          • O Offline
            O Offline
            Old_Man_Down_The_Road
            wrote on last edited by
            #4

            The original problem was QmediaPlayer.setPosition led to the app locking up eventually.

            The code below has passed testing which the original code would not. Does not necessarily pass the "fixed yeah" stage as I am suspicious still.

            So what was done:?

            All media_player methods were bundled into one class
            _Video_Player

            Class Video_Handler was adjusted accordingly

            The key part is in the Video_Handler _setup_media_player method. In the snippet below you will note the two sleep statements , one after moving to thread and one after thread.start. With these no app lockups have yet to be observed.

            Hope this helps some one!

            self._media_player.moveToThread(self._thread)
            
                    sleep(
                        0.1
                    )  # Note: These are important, without them setPosition in the media player sometimes  locks the app
            
                    media_player_thread = self._media_player.thread()
                    is_in_main_thread = media_player_thread == qtC.QThread.currentThread()
                    print(f"Is in main thread: {is_in_main_thread}")
            
                    self._thread.start()
                    sleep(
                        0.5
                    )  # Note: These are important, without them setPosition in the media player sometimes  locks the app
            
            class _Video_Player(qtC.QObject):
                """
                Implements a customer video player object
                """
            
                current_frame_handler = qtC.Signal(int)
                duration_changed_handler = qtC.Signal(int)
                frame_changed_handler = qtC.Signal(qtM.QVideoFrame)
                is_available_handler = qtC.Signal(bool)
                media_status_changed_handler = qtC.Signal(qtM.QMediaPlayer.MediaStatus)
                pause_handler = qtC.Signal()
                play_handler = qtC.Signal()
                position_changed_handler = qtC.Signal(int)
                seekable_changed_handler = qtC.Signal(bool)
                set_position_handler = qtC.Signal(int)
                stop_handler = qtC.Signal()
            
                def __init__(self, parent: qtC.QObject | None, input_file: str) -> None:
                    """
                    Sets up the video_player object for use
            
                    Args:
                        parent (qtC.QObject | None): Set the parent of the object
                        input_file (str): Set the source file of the media player
            
                    """
                    assert parent is None or isinstance(
                        parent, qtC.QObject
                    ), f"{parent =} must be None or a qtC.QObject"
                    assert (
                        isinstance(input_file, str) and input_file.strip() != ""
                    ), f"{input_file =} must be a non-empty str"
            
                    super().__init__(parent)
            
                    self._current_position = -1
                    self._video_sink = qtM.QVideoSink()
                    self._audio_output = qtM.QAudioOutput()
            
                    self._media_player = qtM.QMediaPlayer()
                    self._media_player.setVideoSink(self._video_sink)
                    self._media_player.setAudioOutput(self._audio_output)
                    self._audio_output.setVolume(1)
            
                    # Set input source video file
                    self._media_player.setSource(qtC.QUrl.fromLocalFile(input_file))
            
                    # Hook up signals
                    self._video_sink.videoFrameChanged.connect(self._frame_handler)
                    self._media_player.durationChanged.connect(self._duration_changed)
                    self._media_player.positionChanged.connect(self._position_changed)
                    self._media_player.errorOccurred.connect(self._player_error)
                    self._media_player.mediaStatusChanged.connect(self._media_status_change)
                    self._media_player.seekableChanged.connect(self._seekable_changed)
                    self.frame_changed_handler.connect(self._video_sink.setVideoFrame)
                    self.is_available_handler.connect(self._media_player.isAvailable)
                    self.play_handler.connect(self._media_player.play)
                    self.set_position_handler.connect(self.seek)
                    self.pause_handler.connect(self._media_player.pause)
                    self.stop_handler.connect(self.stop)
            
                @qtC.Slot()
                def _duration_changed(self, duration: int) -> None:
                    """Handles a video duration change
            
                    Args:
                        duration (int): The length of the video
                    """
                    self.duration_changed_handler.emit(duration)
            
                @qtC.Slot()
                def _frame_handler(self, frame: qtM.QVideoFrame) -> None:
                    """Handles the video frame changing signal
            
                    Args:
                        frame (qtM.QVideoFrame): THe video frame to be displayed
                    """
                    self.frame_changed_handler.emit(frame)
            
                @qtC.Slot()
                def _position_changed(self, position_milliseconds: int) -> None:
                    """
                    Handles the position changing signal
            
                    Args:
                        position_milliseconds (int): The current position of the media player in milliseconds.
                    """
                    self.position_changed_handler.emit(position_milliseconds)
            
                @qtC.Slot(qtM.QMediaPlayer.Error, str)
                def _player_error(self, error, error_string):
                    """Called when the media player encounters an error."""
                    print(f"Error: {error} - {error_string}")
            
                def available(self) -> bool:
                    """
                    Returns whether the media player is available
            
                    Returns:
                        bool: True if Available, False if noe
            
                    """
            
                    return self._media_player.isAvailable()
            
                def current_frame(self) -> int:
                    return self._media_player.position()
            
                @qtC.Slot()
                def _media_status_change(self, media_status: qtM.QMediaPlayer.mediaStatus) -> None:
                    """Signals the state of the media has changed
            
                    Args:
                        media_status (qtM.QMediaPlayer.mediaStatus): The status of the media player
                    """
                    self.media_status_changed_handler.emit(media_status)
            
                @qtC.Slot()
                def _seekable_changed(self, seekable: bool) -> None:
                    """
                    Signals the seekable status has changed
            
                    Args:
                        seekable (bool): True if the media player is seekable, False otherwise.
                    """
                    self.seekable_changed_handler.emot(seekable)
            
                @qtC.Slot()
                def seek(self, position: int) -> None:
                    """
                    Seeks to a position
            
                    Args:
                        position (int): THe position in milliseconds to move to
                    """
                    if self._current_position != position:
                        if (
                            self._media_player.isSeekable()
                            and self._media_player.mediaStatus()
                            == qtM.QMediaPlayer.MediaStatus.BufferedMedia
                        ):
                            self._current_position = position
                            self._media_player.setPosition(position)
            
                def state(self) -> str:
                    playback_state = self._media_player.playbackState()
            
                    if playback_state == qtM.QMediaPlayer.PlaybackState.PlayingState:
                        return "playing"
                    elif playback_state == qtM.QMediaPlayer.PlaybackState.PausedState:
                        return "paused"
                    elif playback_state == qtM.QMediaPlayer.PlaybackState.StoppedState:
                        return "stop"
            
                def stop(self):
                    self._media_player.stop()
                    self._media_player.setVideoSink(None)
                    self._media_player.setAudioOutput(None)
            
            
            @dataclasses.dataclass
            class Video_Handler:
                aspect_ratio: str
                input_file: str
                output_edit_folder: str
                encoding_info: Encoding_Details
                video_display: qtg.Label
                video_slider: qtg.Slider
                frame_display: qtg.LCD
                display_width: int
                display_height: int
                update_slider: bool = True
                source_state: Literal[
                    "NoMedia",
                    "Loading",
                    "Loaded",
                    "Stalled",
                    "Buffering",
                    "Buffered",
                    "EndOfMedia",
                    "InvalidMedia",
                ] = "NoMedia"
                state_handler: Callable = None
            
                # Private instance variables
                _frame_count: int = 0
                _frame_rate: float = 25  # Default to 25 frames per second
                _frame_width: int = 720
                _frame_height: int = 576
                _current_frame: int = -1
            
                def __post_init__(self) -> None:
                    """Sets-up the instance"""
                    assert isinstance(self.aspect_ratio, str) and self.aspect_ratio in (
                        sys_consts.AR169,
                        sys_consts.AR43,
                    ), f"{self.aspect_ratio=}. Must be a AR169 | AR43"
            
                    assert (
                        isinstance(self.input_file, str) and self.input_file.strip() != ""
                    ), f"{self.input_file=}. Must be a non-empty str"
            
                    assert (
                        isinstance(self.output_edit_folder, str)
                        and self.output_edit_folder.strip() != ""
                    ), f"{self.output_edit_folder=}. Must be a non-empty str"
            
                    assert isinstance(
                        self.encoding_info, Encoding_Details
                    ), f"{self.encoding_info=}. Must be an instance of Encoding_Details"
            
                    assert isinstance(
                        self.video_display, qtg.Label
                    ), f"{self.video_display=}. Must be a qtg.Label"
            
                    assert isinstance(
                        self.video_slider, qtg.Slider
                    ), f"{self.video_slider=}. Must be a qtg.Slider"
            
                    assert isinstance(
                        self.frame_display, qtg.LCD
                    ), f"{self.frame_display=}. Must be a qtg.Slider"
            
                    assert isinstance(
                        self.display_width, int
                    ), f"{self.display_width=}. Must be an int"
            
                    assert isinstance(
                        self.display_height, int
                    ), f"{self.display_height=}. Must be an int"
            
                    assert isinstance(
                        self.update_slider, bool
                    ), f"{self.update_slider=}. Must be a bool"
            
                    self._frame_width = self.encoding_info.video_width
                    self._frame_height = self.encoding_info.video_height
                    self._frame_rate = self.encoding_info.video_frame_rate
                    self._frame_count = self.encoding_info.video_frame_count
            
                    self._setup_media_player()
            
                def _setup_media_player(self):
                    """Sets up the media_player instance"""
                    self._media_player = _Video_Player(parent=None, input_file=self.input_file)
            
                    self._media_player.frame_changed_handler.connect(self._frame_handler)
                    self._media_player.is_available_handler.connect(self.available)
                    self._media_player.media_status_changed_handler.connect(
                        self._media_status_change
                    )
                    self._media_player.position_changed_handler.connect(self._position_changed)
            
                    self._thread = qtC.QThread()
                    self._media_player.moveToThread(self._thread)
            
                    sleep(
                        0.1
                    )  # Note: These are important, without them setPosition in the media player sometimes  locks the app
            
                    media_player_thread = self._media_player.thread()
                    is_in_main_thread = media_player_thread == qtC.QThread.currentThread()
                    print(f"Is in main thread: {is_in_main_thread}")
            
                    self._thread.start()
                    sleep(
                        0.5
                    )  # Note: These are important, without them setPosition in the media player sometimes  locks the app
            
                @qtC.Slot()
                def _frame_handler(self, frame: qtM.QVideoFrame) -> None:
                    """Handles displaying the video frame
            
                    Args:
                        frame (qtM.QVideoFrame): THe video frame to be displayed
                    """
                    if frame.isValid():
                        image = frame.toImage().scaled(self.display_width, self.display_height)
                        pixmap = qtG.QPixmap.fromImage(image)
            
                        if shiboken6.isValid(
                            self.video_display.guiwidget_get
                        ):  # Should not need this check but on shutdown I sometimes got the  dreaded C++ object deleted error
                            self.video_display.guiwidget_get.setPixmap(pixmap)
            
                def _media_status_change(self, media_status: qtM.QMediaPlayer.mediaStatus) -> None:
                    """When the status of the media player changes this method sets the source_state var and calls the
                    state_handler if provided.
            
                    Args:
                        media_status (qtM.QMediaPlayer.mediaStatus): The status of the media player
                    """
                    match media_status:
                        case qtM.QMediaPlayer.MediaStatus.NoMedia:
                            self.source_state = "NoMedia"
                        case qtM.QMediaPlayer.MediaStatus.LoadingMedia:
                            self.source_state = "Loading"
                        case qtM.QMediaPlayer.MediaStatus.LoadedMedia:
                            self.source_state = "Loaded"
                        case qtM.QMediaPlayer.MediaStatus.StalledMedia:
                            self.source_state = "Stalled"
                        case qtM.QMediaPlayer.MediaStatus.BufferingMedia:
                            self.source_state = "Buffering"
                        case qtM.QMediaPlayer.MediaStatus.BufferedMedia:
                            self.source_state = "Buffered"
                        case qtM.QMediaPlayer.MediaStatus.EndOfMedia:
                            self.source_state = "EndOfMedia"
                        case qtM.QMediaPlayer.MediaStatus.InvalidMedia:
                            self.source_state = "InvalidMedia"
            
                    if self.state_handler and isinstance(self.state_handler, Callable):
                        self.state_handler()
            
                @qtC.Slot()
                def _position_changed(self, position_milliseconds: int) -> None:
                    """
                    A method that is called when the position of the media player changes.
                    Converts the current position in milliseconds to the corresponding frame number,
                    updates the video slider if necessary, and emits a signal indicating that the position has changed.
            
                    Args:
                        position_milliseconds (int): The current position of the media player in milliseconds.
                    """
                    frame_number = int(position_milliseconds * self._frame_rate // 1000)
            
                    if self.update_slider and self.video_slider is not None:
                        self.video_slider.value_set(frame_number)
                    self.frame_display.value_set(frame_number)
            
                def get_current_frame(self) -> int:
                    """
                    Returns the current frame number based on the current position of the media player and the frame rate of the video.
            
                    Returns:
                        int: The current frame number.
                    """
                    return int(self._media_player.current_frame() * self._frame_rate // 1000)
            
                def available(self) -> bool:
                    """Checks if the media player is supported on the platform
            
                    Returns:
                        bool: True if the media player is supported, False otherwise.
                    """
                    return self._media_player.available()
            
                def play(self) -> None:
                    """
                    Starts playing the media.
                    """
                    self._media_player.play_handler.emit()
                    return None
            
                def pause(self) -> None:
                    """
                    Pauses the media.
                    """
                    self._media_player.pause_handler.emit()
            
                def seek(self, frame: int) -> None:
                    """
                    Seeks to the specified frame number.
            
                    Args:
                        frame (int): The frame number to seek to.
                    """
            
                    if self._current_frame != frame:
                        state = self._media_player.state()
            
                        if state == "playing":
                            self._media_player.pause_handler.emit()
                            sleep(0.2)
            
                        self._current_frame = frame
                        time_offset = int((1000 / self._frame_rate) * frame)
                        self._media_player.set_position_handler.emit(time_offset)
            
                        if state == "playing":
                            pass
                            # self._media_player.play_handler.emit() # Leads to stuttering video sometimes
            
                def shutdown(self) -> None:
                    """
                    Stops playing the media and releases the player's resources.
                    """
                    if self._media_player is not None:
                        self._media_player.stop_handler.emit()
            
                        if self._thread and self._thread.isRunning():
                            self._thread.quit()
                            self._thread.wait()
                            self._thread.deleteLater()
                            self._thread = None
            
                    return None
            
                def state(self) -> str:
                    """
                    Returns the current playback state of the media player.
            
                    Returns:
                        str: The current playback state
                            - "playing": The media player is currently playing.
                            - "paused": The media player is currently paused.
                            - "stop": The media player is currently stopped.
                    """
                    return self._media_player.state()
            
            1 Reply Last reply
            0
            • O Old_Man_Down_The_Road has marked this topic as solved on

            • Login

            • Login or register to search.
            • First post
              Last post
            0
            • Categories
            • Recent
            • Tags
            • Popular
            • Users
            • Groups
            • Search
            • Get Qt Extensions
            • Unsolved