class VideoPlayer(QWidget): def __init__(self, parent=None): super(VideoPlayer, self).__init__(parent) self.mediaPlayer = QMediaPlayer(None, QMediaPlayer.VideoSurface) self.videoItem = QGraphicsVideoItem() self.videoItem.setSize(QSizeF(640, 480)) scene = QGraphicsScene(self) graphicsView = QGraphicsView(scene) scene.addItem(self.videoItem) rotateSlider = QSlider(Qt.Horizontal) rotateSlider.setRange(-180, 180) rotateSlider.setValue(0) rotateSlider.valueChanged.connect(self.rotateVideo) openButton = QPushButton("Open...") openButton.clicked.connect(self.openFile) self.playButton = QPushButton() self.playButton.setEnabled(False) self.playButton.setIcon(self.style().standardIcon(QStyle.SP_MediaPlay)) self.playButton.clicked.connect(self.play) self.positionSlider = QSlider(Qt.Horizontal) self.positionSlider.setRange(0, 0) self.positionSlider.sliderMoved.connect(self.setPosition) controlLayout = QHBoxLayout() controlLayout.setContentsMargins(0, 0, 0, 0) controlLayout.addWidget(openButton) controlLayout.addWidget(self.playButton) controlLayout.addWidget(self.positionSlider) layout = QVBoxLayout() layout.addWidget(graphicsView) layout.addWidget(rotateSlider) layout.addLayout(controlLayout) self.setLayout(layout) self.mediaPlayer.setVideoOutput(self.videoItem) self.mediaPlayer.stateChanged.connect(self.mediaStateChanged) self.mediaPlayer.positionChanged.connect(self.positionChanged) self.mediaPlayer.durationChanged.connect(self.durationChanged) def sizeHint(self): return QSize(800, 600) def openFile(self): fileName, _ = QFileDialog.getOpenFileName(self, "Open Movie", QDir.homePath()) if fileName != '': self.mediaPlayer.setMedia( QMediaContent(QUrl.fromLocalFile(fileName))) self.playButton.setEnabled(True) def play(self): if self.mediaPlayer.state() == QMediaPlayer.PlayingState: self.mediaPlayer.pause() else: self.mediaPlayer.play() def mediaStateChanged(self, state): if self.mediaPlayer.state() == QMediaPlayer.PlayingState: self.playButton.setIcon(self.style().standardIcon( QStyle.SP_MediaPause)) else: self.playButton.setIcon(self.style().standardIcon( QStyle.SP_MediaPlay)) def positionChanged(self, position): self.positionSlider.setValue(position) def durationChanged(self, duration): self.positionSlider.setRange(0, duration) def setPosition(self, position): self.mediaPlayer.setPosition(position) def rotateVideo(self, angle): x = self.videoItem.boundingRect().width() / 2.0 y = self.videoItem.boundingRect().height() / 2.0 self.videoItem.setTransform(QTransform().translate( x, y).rotate(angle).translate(-x, -y))
class VideoPlayer(QWidget): def __init__(self, parent=None): super(VideoPlayer, self).__init__(parent) self.mediaPlayer = QMediaPlayer(None, QMediaPlayer.VideoSurface) self.videoItem = QGraphicsVideoItem() self.videoItem.setSize(QSizeF(640, 480)) scene = QGraphicsScene(self) graphicsView = QGraphicsView(scene) scene.addItem(self.videoItem) rotateSlider = QSlider(Qt.Horizontal) rotateSlider.setRange(-180, 180) rotateSlider.setValue(0) rotateSlider.valueChanged.connect(self.rotateVideo) openButton = QPushButton("Open...") openButton.clicked.connect(self.openFile) self.playButton = QPushButton() self.playButton.setEnabled(False) self.playButton.setIcon(self.style().standardIcon(QStyle.SP_MediaPlay)) self.playButton.clicked.connect(self.play) self.positionSlider = QSlider(Qt.Horizontal) self.positionSlider.setRange(0, 0) self.positionSlider.sliderMoved.connect(self.setPosition) controlLayout = QHBoxLayout() controlLayout.setContentsMargins(0, 0, 0, 0) controlLayout.addWidget(openButton) controlLayout.addWidget(self.playButton) controlLayout.addWidget(self.positionSlider) layout = QVBoxLayout() layout.addWidget(graphicsView) layout.addWidget(rotateSlider) layout.addLayout(controlLayout) self.setLayout(layout) self.mediaPlayer.setVideoOutput(self.videoItem) self.mediaPlayer.stateChanged.connect(self.mediaStateChanged) self.mediaPlayer.positionChanged.connect(self.positionChanged) self.mediaPlayer.durationChanged.connect(self.durationChanged) def sizeHint(self): return QSize(800, 600) def openFile(self): fileName, _ = QFileDialog.getOpenFileName(self, "Open Movie", QDir.homePath()) if fileName != '': self.mediaPlayer.setMedia( QMediaContent(QUrl.fromLocalFile(fileName))) self.playButton.setEnabled(True) def play(self): if self.mediaPlayer.state() == QMediaPlayer.PlayingState: self.mediaPlayer.pause() else: self.mediaPlayer.play() def mediaStateChanged(self, state): if self.mediaPlayer.state() == QMediaPlayer.PlayingState: self.playButton.setIcon( self.style().standardIcon(QStyle.SP_MediaPause)) else: self.playButton.setIcon( self.style().standardIcon(QStyle.SP_MediaPlay)) def positionChanged(self, position): self.positionSlider.setValue(position) def durationChanged(self, duration): self.positionSlider.setRange(0, duration) def setPosition(self, position): self.mediaPlayer.setPosition(position) def rotateVideo(self, angle): x = self.videoItem.boundingRect().width() / 2.0 y = self.videoItem.boundingRect().height() / 2.0 self.videoItem.setTransform( QTransform().translate(x, y).rotate(angle).translate(-x, -y))
class LiveFeedWidget(QGraphicsView): class _State(Enum): Init = "Init" Idle = "Idle" Preparing = "Preparing" WaitingForCapture = "WaitingForCapture" error = pyqtSignal(str) image_captured = pyqtSignal(QImage) initialized = pyqtSignal() def __init__( self, camera: Camera, is_mirrored: bool, mask: Mask, parent=None, ): super().__init__(parent=parent) self._is_mirrored = is_mirrored self._mask = mask self._state = LiveFeedWidget._State.Init self._video_item = QGraphicsVideoItem() # I think this is the size in pixels on the screen? self._video_item.setSize(QSizeF(self._mask.size)) # TODO I think this will not draw centrally? If not should fix this self._video_item.setAspectRatioMode(Qt.KeepAspectRatioByExpanding) if self._is_mirrored: self._video_item.setTransform(QTransform().scale(-1, 1)) self.setMask(self._mask.clip_region) self._scene: OverlayTextGraphicsScene = OverlayTextGraphicsScene( self._mask.size) self._scene.addItem(self._video_item) self.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff) self.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff) self.setScene(self._scene) # Setup camera # self._camera = camera self._camera.setViewfinder(self._video_item) self._camera.setCaptureMode(QCamera.CaptureStillImage) self._camera.error.connect(self._on_camera_error) self._camera.statusChanged.connect(self._on_camera_status_changed) # Setup capture # self._capture = QCameraImageCapture(self._camera) self._capture.setCaptureDestination( QCameraImageCapture.CaptureToBuffer) self._capture.imageCaptured.connect(self._image_captured) self._capture.error.connect(self._on_capture_error) self._camera.start() logger.info("Camera started") def set_overlay_text(self, text: str): self._scene.set_overlay_text(text) def prepare(self): if self._state != LiveFeedWidget._State.Idle: logger.warning("Dropping call to prepare when in state: %s", self._state) else: self._state = LiveFeedWidget._State.Preparing self._camera.searchAndLock() def trigger_capture(self): if self._state != LiveFeedWidget._State.Preparing: logger.warning( "Dropping call to trigger_capture when in state: %s", self._state) else: logger.debug("trigger_capture") self._state = LiveFeedWidget._State.WaitingForCapture self._capture.capture() # noinspection PyPep8Naming def _on_camera_error(self, QCamera_Error: int): self._state = LiveFeedWidget._State.Idle self.error.emit(f"Camera error, code: {QCamera_Error}") # noinspection PyPep8Naming def _on_capture_error(self, p_int=None, QCameraImageCapture_Error=None, p_str=None): self._state = LiveFeedWidget._State.Idle self.error.emit( f"Capture error: {p_int} / {QCameraImageCapture_Error} / {p_str}") def _image_captured(self, id_: int, image: QImage): if self._state != LiveFeedWidget._State.WaitingForCapture: logger.warning("Dropping _image_captured when in state: %s", self._state) self._state = LiveFeedWidget._State.Idle logger.debug("image captured: %s %s", id_, image) self._camera.unlock() # TODO If I don't unload and the reload the camera, gstreamer dies # with CameraBin error: "Internal data stream error.", not sure why # :( self._camera.unload() self.image_captured.emit(_mirror_if_necessary(image, self._is_mirrored)) def reload(self): # See comment on _image_captured self._camera.start() def _on_camera_status_changed(self, status): logger.debug("_on_camera_status_changed: %s", status) if status == QCamera.ActiveStatus: self._state = LiveFeedWidget._State.Idle self.initialized.emit()