pyqt-multimedia ยท diff
v1.0.0 to v2.0.0
64 added, 1515 removed. Audit A to A.
---
name: pyqt-multimedia
- description: "PyQt/PySide multimedia - audio playback, video playback, camera, audio recording, media player"
+ description: "Use when adding multimedia to PyQt/PySide6 apps - audio and video playback with QMediaPlayer, camera capture with QCamera and QMediaCaptureSession, audio/video recording with QMediaRecorder, GStreamer backend setup, or codec and platform compatibility issues"
metadata:
author: mte90
- version: 1.0.0
+ version: 2.0.0
tags:
- python
- qt
- pyqt
- multimedia
- audio
- video
- camera
- media
---
# PyQt/PySide Multimedia
- Audio and video playback, camera capture, and media processing in PyQt/PySide.
+ Audio and video playback, camera capture, and media recording in PyQt6/PySide6.
## Overview
Qt Multimedia provides classes for audio, video, and camera functionality:
- **QMediaPlayer** - Audio/video playback
- **QVideoWidget** - Video display
- **QAudioOutput** - Audio output management
- **QCamera** - Camera capture
+ - **QMediaCaptureSession** - Camera/recording session management
- **QMediaRecorder** - Audio/video recording
+ - **QMediaFormat** - Encoding format configuration
+ **Backend by platform:**
+ - Linux: GStreamer
+ - Windows: DirectShow/WMF
+ - macOS: AVFoundation
+
---
## Audio Playback
### Basic Audio Player
```python
from PyQt6.QtMultimedia import QMediaPlayer, QAudioOutput
from PyQt6.QtWidgets import QApplication, QPushButton, QVBoxLayout, QWidget, QSlider, QLabel
from PyQt6.QtCore import Qt, QUrl
import sys
class AudioPlayer(QWidget):
def __init__(self):
super().__init__()
- self.setWindowTitle("Audio Player")
-
self.player = QMediaPlayer()
self.audio_output = QAudioOutput()
self.player.setAudioOutput(self.audio_output)
- # UI
self.play_btn = QPushButton("Play")
- self.pause_btn = QPushButton("Pause")
- self.stop_btn = QPushButton("Stop")
- self.label = QLabel("No file loaded")
self.volume_slider = QSlider(Qt.Orientation.Horizontal)
self.volume_slider.setRange(0, 100)
- self.volume_slider.setValue(50)
layout = QVBoxLayout()
- layout.addWidget(self.label)
layout.addWidget(self.play_btn)
- layout.addWidget(self.pause_btn)
- layout.addWidget(self.stop_btn)
layout.addWidget(self.volume_slider)
self.setLayout(layout)
- # Connections
self.play_btn.clicked.connect(self.player.play)
- self.pause_btn.clicked.connect(self.player.pause)
- self.stop_btn.clicked.connect(self.player.stop)
self.volume_slider.valueChanged.connect(
lambda v: self.audio_output.setVolume(v / 100)
)
- self.player.positionChanged.connect(self.update_position)
-
def load_file(self, filepath):
self.player.setSource(QUrl.fromLocalFile(filepath))
- self.label.setText(filepath.split('/')[-1])
-
- def update_position(self, position):
- # position in milliseconds
- seconds = position // 1000
- minutes = seconds // 60
- seconds = seconds % 60
- print(f"{minutes:02d}:{seconds:02d}")
if __name__ == "__main__":
app = QApplication(sys.argv)
window = AudioPlayer()
window.show()
sys.exit(app.exec())
```
- ### Audio Playlist
-
- ```python
- from PyQt6.QtMultimedia import QMediaPlayer, QAudioOutput
- from PyQt6.QtCore import QUrl, QModelIndex
- from PyQt6.QtWidgets import QListView
-
- class PlaylistPlayer:
- def __init__(self, playlist_view: QListView):
- self.player = QMediaPlayer()
- self.audio_output = QAudioOutput()
- self.player.setAudioOutput(self.audio_output)
-
- self.playlist = [] # List of file paths
- self.current_index = -1
-
- def add_to_playlist(self, filepath):
- self.playlist.append(filepath)
-
- def play_index(self, index: int):
- if 0 <= index < len(self.playlist):
- self.current_index = index
- self.player.setSource(QUrl.fromLocalFile(self.playlist[index]))
- self.player.play()
-
- def next(self):
- if self.playlist:
- self.current_index = (self.current_index + 1) % len(self.playlist)
- self.play_index(self.current_index)
-
- def previous(self):
- if self.playlist:
- self.current_index = (self.current_index - 1) % len(self.playlist)
- self.play_index(self.current_index)
- ```
-
---
## Video Playback
### Video Player with Controls
```python
from PyQt6.QtMultimedia import QMediaPlayer, QAudioOutput
from PyQt6.QtMultimediaWidgets import QVideoWidget
from PyQt6.QtWidgets import (
QApplication, QWidget, QVBoxLayout, QHBoxLayout,
QPushButton, QSlider, QLabel
)
- from PyQt6.QtCore import Qt, QUrl, QTimer
- from PyQt6.QtGui import QAction
- import sys
+ from PyQt6.QtCore import Qt, QUrl
class VideoPlayer(QWidget):
def __init__(self):
super().__init__()
- self.setWindowTitle("Video Player")
- self.resize(800, 600)
-
- # Media player
self.player = QMediaPlayer()
self.audio_output = QAudioOutput()
self.player.setAudioOutput(self.audio_output)
- # Video widget
self.video_widget = QVideoWidget()
+ self.player.setVideoOutput(self.video_widget)
- # Controls
self.play_btn = QPushButton("Play")
- self.pause_btn = QPushButton("Pause")
- self.stop_btn = QPushButton("Stop")
-
self.position_slider = QSlider(Qt.Orientation.Horizontal)
- self.position_slider.setRange(0, 0)
-
self.time_label = QLabel("00:00 / 00:00")
- self.volume_slider = QSlider(Qt.Orientation.Horizontal)
- self.volume_slider.setRange(0, 100)
- self.volume_slider.setValue(50)
- # Layout
- control_layout = QHBoxLayout()
- control_layout.addWidget(self.play_btn)
- control_layout.addWidget(self.pause_btn)
- control_layout.addWidget(self.stop_btn)
- control_layout.addWidget(self.position_slider)
- control_layout.addWidget(self.time_label)
- control_layout.addWidget(self.volume_slider)
-
- main_layout = QVBoxLayout()
- main_layout.addWidget(self.video_widget)
- main_layout.addLayout(control_layout)
- self.setLayout(main_layout)
-
- # Connect
- self.player.setVideoOutput(self.video_widget)
+ layout = QVBoxLayout()
+ layout.addWidget(self.video_widget)
+ layout.addWidget(self.play_btn)
+ layout.addWidget(self.position_slider)
+ layout.addWidget(self.time_label)
+ self.setLayout(layout)
self.play_btn.clicked.connect(self.player.play)
- self.pause_btn.clicked.connect(self.player.pause)
- self.stop_btn.clicked.connect(self.stop)
-
self.player.positionChanged.connect(self.position_changed)
self.player.durationChanged.connect(self.duration_changed)
- self.volume_slider.valueChanged.connect(
- lambda v: self.audio_output.setVolume(v / 100)
- )
-
def load_video(self, filepath):
self.player.setSource(QUrl.fromLocalFile(filepath))
- def stop(self):
- self.player.stop()
- self.position_slider.setValue(0)
-
def position_changed(self, position):
self.position_slider.setValue(position)
- self.update_time_label()
def duration_changed(self, duration):
self.position_slider.setRange(0, duration)
- self.update_time_label()
-
- def update_time_label(self):
- pos = self.player.position() // 1000
- dur = self.player.duration() // 1000
-
- pos_m, pos_s = divmod(pos, 60)
- dur_m, dur_s = divmod(dur, 60)
-
- self.time_label.setText(
- f"{pos_m:02d}:{pos_s:02d} / {dur_m:02d}:{dur_s:02d}"
- )
-
- def keyPressEvent(self, event):
- if event.key() == Qt.Key.Key_Space:
- if self.player.playbackState() == QMediaPlayer.PlaybackState.PlayingState:
- self.player.pause()
- else:
- self.player.play()
- elif event.key() == Qt.Key.Key_Left:
- self.player.setPosition(max(0, self.player.position() - 5000))
- elif event.key() == Qt.Key.Key_Right:
- self.player.setPosition(
- min(self.player.duration(), self.player.position() + 5000)
- )
- ```
- ### Fullscreen Video
-
- ```python
- class FullscreenVideoPlayer(VideoPlayer):
- def __init__(self):
- super().__init__()
- self.is_fullscreen = False
- self.video_widget.doubleClicked.connect(self.toggle_fullscreen)
-
- def toggle_fullscreen(self):
- if self.is_fullscreen:
- self.showNormal()
- else:
- self.showFullScreen()
- self.is_fullscreen = not self.is_fullscreen
+ if __name__ == "__main__":
+ app = QApplication(sys.argv)
+ window = VideoPlayer()
+ window.show()
+ sys.exit(app.exec())
```
+ **Key methods:**
+ - `play()`, `pause()`, `stop()` - Playback control
+ - `setPosition(ms)`, `seek(seconds)` - Position control
+ - `playbackState()` - Returns PlayingState, PausedState, or StoppedState
+
---
## Camera Capture
- ### Display Camera Feed
+ ### Camera Viewer
```python
from PyQt6.QtMultimedia import QCamera, QMediaDevices
from PyQt6.QtMultimediaWidgets import QVideoWidget
from PyQt6.QtWidgets import QApplication, QWidget, QVBoxLayout, QPushButton
- from PyQt6.QtCore import Qt
- import sys
class CameraViewer(QWidget):
def __init__(self):
super().__init__()
- self.setWindowTitle("Camera Viewer")
-
- # Get available cameras
- self.cameras = QMediaDevices.videoInputs()
- if not self.cameras:
+ cameras = QMediaDevices.videoInputs()
+ if not cameras:
print("No cameras available")
return
- # Create camera
- self.camera = QCamera(self.cameras[0])
-
- # Video widget
+ self.camera = QCamera(cameras[0])
self.video_widget = QVideoWidget()
- # Buttons
- self.start_btn = QPushButton("Start")
- self.stop_btn = QPushButton("Stop")
-
layout = QVBoxLayout()
layout.addWidget(self.video_widget)
- layout.addWidget(self.start_btn)
- layout.addWidget(self.stop_btn)
self.setLayout(layout)
- # Connect camera to widget
self.camera.setVideoOutput(self.video_widget)
-
- self.start_btn.clicked.connect(self.camera.start)
- self.stop_btn.clicked.connect(self.camera.stop)
+ self.camera.start()
def closeEvent(self, event):
self.camera.stop()
super().closeEvent(event)
```
- ### Capture Photo
+ ### Photo Capture with QMediaCaptureSession
```python
from PyQt6.QtMultimedia import QCamera, QMediaCaptureSession, QImageCapture
from PyQt6.QtMultimediaWidgets import QVideoWidget
- from PyQt6.QtWidgets import QWidget, QVBoxLayout, QPushButton, QLabel
- from PyQt6.QtCore import QUrl
+ from PyQt6.QtWidgets import QWidget, QVBoxLayout, QPushButton
class CameraCapture(QWidget):
def __init__(self):
super().__init__()
- self.setWindowTitle("Camera Capture")
-
self.camera = QCamera()
self.capture_session = QMediaCaptureSession()
self.capture_session.setCamera(self.camera)
self.video_widget = QVideoWidget()
self.capture_session.setVideoOutput(self.video_widget)
- # Image capture
self.image_capture = QImageCapture()
self.capture_session.setImageCapture(self.image_capture)
self.capture_btn = QPushButton("Capture Photo")
- self.preview_label = QLabel()
+ self.capture_btn.clicked.connect(self.capture_photo)
layout = QVBoxLayout()
layout.addWidget(self.video_widget)
layout.addWidget(self.capture_btn)
- layout.addWidget(self.preview_label)
self.setLayout(layout)
- self.capture_btn.clicked.connect(self.capture_photo)
-
self.camera.start()
def capture_photo(self):
self.image_capture.captureToFile()
-
- def handle_captured(self, id, filePath):
- print(f"Photo saved to: {filePath}")
```
- ### Record Video
+ ---
+ ## Audio/Video Recording
+
+ ### Media Recorder
+
```python
from PyQt6.QtMultimedia import QCamera, QMediaCaptureSession, QMediaRecorder
from PyQt6.QtMultimediaWidgets import QVideoWidget
from PyQt6.QtWidgets import QWidget, QVBoxLayout, QPushButton, QLabel
+ from PyQt6.QtCore import QUrl
- class VideoRecorder(QWidget):
+ class MediaRecorder(QWidget):
def __init__(self):
super().__init__()
-
self.camera = QCamera()
self.capture_session = QMediaCaptureSession()
self.capture_session.setCamera(self.camera)
self.video_widget = QVideoWidget()
self.capture_session.setVideoOutput(self.video_widget)
- # Recorder
self.recorder = QMediaRecorder()
self.capture_session.setRecorder(self.recorder)
self.record_btn = QPushButton("Start Recording")
self.status_label = QLabel("Ready")
layout = QVBoxLayout()
layout.addWidget(self.video_widget)
layout.addWidget(self.record_btn)
layout.addWidget(self.status_label)
self.setLayout(layout)
self.record_btn.clicked.connect(self.toggle_recording)
self.recorder.recorderStateChanged.connect(self.update_status)
def toggle_recording(self):
if self.recorder.recorderState() == QMediaRecorder.RecorderState.RecordingState:
self.recorder.stop()
else:
self.recorder.setOutputLocation(QUrl.fromLocalFile("output.mp4"))
self.recorder.record()
def update_status(self, state):
states = {
QMediaRecorder.RecorderState.StoppedState: "Stopped",
QMediaRecorder.RecorderState.RecordingState: "Recording",
QMediaRecorder.RecorderState.PausedState: "Paused"
}
self.status_label.setText(states.get(state, "Unknown"))
```
---
- ## Audio Recording
-
- ### Microphone Input
-
- ```python
- from PyQt6.QtMultimedia import QMediaRecorder, QMediaCaptureSession, QAudioInput
- from PyQt6.QtCore import QUrl, QStandardPaths
- from PyQt6.QtWidgets import QWidget, QVBoxLayout, QPushButton, QLabel
- import os
-
- class AudioRecorder(QWidget):
- def __init__(self):
- super().__init__()
-
- self.recorder = QMediaRecorder()
- self.capture_session = QMediaCaptureSession()
-
- self.audio_input = QAudioInput()
- self.capture_session.setAudioInput(self.audio_input)
- self.capture_session.setRecorder(self.recorder)
-
- self.record_btn = QPushButton("Start Recording")
- self.status_label = QLabel("Ready")
-
- layout = QVBoxLayout()
- layout.addWidget(self.record_btn)
- layout.addWidget(self.status_label)
- self.setLayout(layout)
-
- self.record_btn.clicked.connect(self.toggle_recording)
- self.recorder.recorderStateChanged.connect(self.update_status)
-
- # Default output location
- documents = QStandardPaths.writableLocation(
- QStandardPaths.StandardLocation.MoviesLocation
- )
- self.output_path = os.path.join(documents, "recording.mp3")
-
- def toggle_recording(self):
- if self.recorder.recorderState() == QMediaRecorder.RecorderState.RecordingState:
- self.recorder.stop()
- else:
- self.recorder.setOutputLocation(QUrl.fromLocalFile(self.output_path))
- self.recorder.record()
-
- def update_status(self, state):
- if state == QMediaRecorder.RecorderState.RecordingState:
- self.record_btn.setText("Stop Recording")
- self.status_label.setText("Recording...")
- else:
- self.record_btn.setText("Start Recording")
- self.status_label.setText("Ready")
- ```
-
- ---
-
- ## GStreamer Backend
-
- ### Install GStreamer (Linux)
+ ## GStreamer Backend (Linux)
```bash
- # Ubuntu/Debian
+ # Ubuntu/Debian - core plugins
sudo apt-get install libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev \
gstreamer1.0-plugins-good gstreamer1.0-plugins-bad gstreamer1.0-plugins-ugly
- # For audio/video codecs
+ # Additional codec support
sudo apt-get install gstreamer1.0-libav
```
- ### Install on Windows
-
- PySide6 on Windows typically uses DirectShow or WMF backends. Install K-Lite Codec Pack for additional codec support.
-
---
## Common Issues
- ### No Audio/Video Output
-
- ```python
- # Check available codecs
- from PyQt6.QtMultimedia import QMediaDevices
- print("Audio outputs:", QMediaDevices.audioOutputs())
- print("Video outputs:", QMediaDevices.videoOutputs())
-
- # Check camera
- print("Cameras:", QMediaDevices.videoInputs())
- ```
-
- ### Format Not Supported
-
- ```python
- # Convert using QMediaEncoder with specific codec
- from PyQt6.QtMultimedia import QMediaRecorder, QMediaFormat
-
- recorder = QMediaRecorder()
- recorder.setMediaFormat(QMediaFormat.MediaFormat.MPEG4)
- recorder.setAudioCodec(QMediaFormat.AudioCodec.AAC)
- recorder.setVideoCodec(QMediaFormat.VideoCodec.H264)
- ```
+ | Issue | Solution |
+ |-------|----------|
+ | No audio/video output | Check `QMediaDevices.audioOutputs()` and `videoOutputs()` |
+ | No cameras found | Check `QMediaDevices.videoInputs()` |
+ | Format not supported | Install codec pack (Windows) or GStreamer plugins (Linux) |
+ | Playback fails | Check `player.error()` for specific error code |
---
## Best Practices
- ### Audio/Video Capture
-
```python
- # โ
GOOD: Check availability first
+ # โ
Check device availability first
from PyQt6.QtMultimedia import QMediaDevices
-
if not QMediaDevices.audioInputs():
print("No microphone available")
- return
- # โ
GOOD: Set output location before recording
+ # โ
Set output location before recording
recorder.setOutputLocation(QUrl.fromLocalFile(path))
- recorder.record() # Start after setting location
- ```
-
- ### Playback
-
- ```python
- # โ
GOOD: Check player state
- player.play()
- # Wait for state change signal, don't assume immediate playback
-
- # โ
GOOD: Handle missing codecs
- # Install K-Lite on Windows, gstreamer on Linux
- ```
-
- ### Resource Management
+ recorder.record()
- ```python
- # โ
GOOD: Clean up resources
+ # โ
Clean up on close
def closeEvent(self, event):
- self.player.stop()
- self.recorder.stop()
self.camera.stop()
+ self.recorder.stop()
super().closeEvent(event)
```
- ### Do:
- - Check device availability before use
- - Set output location before recording
- - Clean up on close
+ ---
- ### Don't:
- - Record without checking available disk space
- - Use unsupported formats
- - Forget to stop capture sessions
+ ## Deep Dives
+ For detailed implementations, see the reference files:
+
+ - **Media Pipeline** (`references/media-pipeline.md`) - QMediaPlayer + QVideoWidget, QCamera + QMediaCaptureSession, QMediaRecorder setup
+ - **Codecs & Platforms** (`references/codecs-platforms.md`) - Platform-specific codec support, installation guides, format compatibility
+
---
## References
- **Qt Multimedia**: https://doc.qt.io/qt-6/qtmultimedia-index.html
- **PySide6 Multimedia**: https://doc.qt.io/qt-6/multimedia.html
- **GStreamer**: https://gstreamer.freedesktop.org/
-
- ## Video Playback (PySide6)
-
- ### QMediaPlayer + QVideoWidget
-
- ```python
- from PySide6.QtMultimedia import QMediaPlayer
- from PySide6.QtMultimediaWidgets import QVideoWidget
- from PySide6.QtWidgets import (
- QApplication, QWidget, QVBoxLayout, QHBoxLayout,
- QPushButton, QSlider, QLabel, QMainWindow
- )
- from PySide6.QtCore import Qt, QUrl, QTimer
- from PySide6.QtGui import QAction
- import sys
-
- class VideoPlayer(QMainWindow):
- def __init__(self):
- super().__init__()
- self.setWindowTitle("Video Player")
- self.resize(800, 600)
-
- # Media player
- self.player = QMediaPlayer()
- self.audio_output = QAudioOutput()
- self.player.setAudioOutput(self.audio_output)
-
- # Video widget
- self.video_widget = QVideoWidget()
-
- # Controls
- self.play_btn = QPushButton("โถ Play")
- self.pause_btn = QPushButton("โโ Pause")
- self.stop_btn = QPushButton("โ Stop")
-
- self.position_slider = QSlider(Qt.Orientation.Horizontal)
- self.position_slider.setRange(0, 0)
-
- self.time_label = QLabel("00:00 / 00:00")
- self.volume_slider = QSlider(Qt.Orientation.Horizontal)
- self.volume_slider.setRange(0, 100)
- self.volume_slider.setValue(50)
-
- # Toolbar layout
- toolbar = QHBoxLayout()
- toolbar.addWidget(self.play_btn)
- toolbar.addWidget(self.pause_btn)
- toolbar.addWidget(self.stop_btn)
- toolbar.addWidget(self.position_slider)
- toolbar.addWidget(self.time_label)
- toolbar.addWidget(self.volume_slider)
- toolbar.addStretch()
-
- # Main layout
- main_layout = QVBoxLayout()
- main_layout.addWidget(self.video_widget)
- main_layout.addLayout(toolbar)
- self.setCentralWidget(QWidget())
- self.centralLayout = self.centralWidget().layout()
- self.centralLayout.addLayout(toolbar)
-
- # Signal connections
- self.player.setVideoOutput(self.video_widget)
- self.play_btn.clicked.connect(self.player.play)
- self.pause_btn.clicked.connect(self.player.pause)
- self.stop_btn.clicked.connect(self.player.stop)
-
- self.player.positionChanged.connect(self.position_changed)
- self.player.durationChanged.connect(self.duration_changed)
-
- self.volume_slider.valueChanged.connect(
- lambda v: self.audio_output.setVolume(v / 100)
- )
-
- # Keyboard shortcuts
- self.installEventFilter(self)
-
- def load_video(self, filepath):
- """Load a video file."""
- self.player.setSource(QUrl.fromLocalFile(filepath))
-
- def position_changed(self, position):
- """Update position slider and time display."""
- self.position_slider.setValue(position)
- self.update_time_label()
-
- def duration_changed(self, duration):
- """Update slider range and initial time display."""
- self.position_slider.setRange(0, duration)
- self.update_time_label()
-
- def update_time_label(self):
- """Convert position/duration to MM:SS format."""
- pos = self.player.position() // 1000
- dur = self.player.duration() // 1000 if self.player.duration() > 0 else 0
-
- pos_m, pos_s = divmod(pos, 60)
- dur_m, dur_s = divmod(dur, 60)
-
- self.time_label.setText(
- f"{pos_m:02d}:{pos_s:02d} / {dur_m:02d}:{dur_s:02d}"
- )
-
- def keyPressEvent(self, event):
- """Handle keyboard shortcuts."""
- if event.key() == Qt.Key.Key_Space:
- if self.player.playbackState() == QMediaPlayer.PlaybackState.PlayingState:
- self.player.pause()
- else:
- self.player.play()
- elif event.key() == Qt.Key.Key_Left:
- self.player.setPosition(max(0, self.player.position() - 5000))
- elif event.key() == Qt.Key.Key_Right:
- self.player.setPosition(
- min(self.player.duration(), self.player.position() + 5000)
- )
- elif event.key() == Qt.Key.Key_k:
- self.player.seek(5)
- elif event.key() == Qt.Key.Key_j:
- self.player.seek(-5)
-
- if __name__ == "__main__":
- app = QApplication(sys.argv)
-
- # Set application properties
- app.setApplicationName("Video Player")
- app.setOrganizationName("MyOrg")
-
- window = VideoPlayer()
- window.show()
- sys.exit(app.exec())
- ```
-
- **Controls Reference:**
- | Method | Description |
- |--------|-------------|
- | `play()` | Start playback |
- | `pause()` | Pause playback |
- | `stop()` | Stop playback and reset position |
- | `setPosition(pos)` | Set playback position (milliseconds) |
- | `seek(seconds)` | Seek relative to current position |
- | `playbackState()` | Returns current state (PlayingState, PausedState, StoppedState) |
- | `position()` | Current playback position in milliseconds |
- | `duration()` | Total duration in milliseconds |
-
- **Supported Formats:**
- - **Audio/Video files**: `.mp4`, `.avi`, `.mkv`, `.mov`, `.flv`, `.webm`, `.wmv`
- - **Audio files**: `.mp3`, `.wav`, `.ogg`, `.flac`, `.aac`
- - **Real-time**: Camera feed, network streams (RTSP, HTTP)
-
- Note: Codec support varies by platform. On Linux, ensure GStreamer backends are installed.
-
- ---
-
- ## Camera Access (PySide6)
-
- ### QCamera + QMediaCaptureSession
-
- ```python
- from PySide6.QtMultimedia import QCamera, QMediaDevices, QMediaCaptureSession
- from PySide6.QtMultimediaWidgets import QVideoWidget
- from PySide6.QtWidgets import (
- QApplication, QWidget, QVBoxLayout, QHBoxLayout,
- QPushButton, QLabel, QComboBox, QDialogButtonBox
- )
- from PySide6.QtCore import Qt, QUrl
- import sys
- from typing import Optional
-
- class CameraViewer(QWidget):
- """Camera viewer with live preview."""
-
- def __init__(self):
- super().__init__()
- self.setWindowTitle("Camera Viewer")
- self.setMinimumSize(640, 480)
-
- # Available cameras
- self.cameras = QMediaDevices.videoInputs()
- if not self.cameras:
- self.init_no_camera()
- return
-
- # Selected camera
- self.camera = QCamera(self.cameras[0])
-
- # Video widget for preview
- self.video_widget = QVideoWidget()
-
- # UI components
- self.camera_label = QLabel("No cameras found")
- self.camera_combo = QComboBox()
- self.camera_combo.addItems([f"Camera {i}" for i in range(len(self.cameras))])
- self.start_btn = QPushButton("Start")
- self.stop_btn = QPushButton("Stop")
- self.preview_label = QLabel("Ready")
-
- # Layout
- layout = QVBoxLayout()
-
- # Camera selection
- select_layout = QHBoxLayout()
- select_layout.addWidget(QLabel("Camera:"))
- select_layout.addWidget(self.camera_combo)
- layout.addLayout(select_layout)
-
- # Video preview
- layout.addWidget(self.camera_label)
- layout.addWidget(self.video_widget)
-
- # Controls
- control_layout = QHBoxLayout()
- control_layout.addWidget(self.start_btn)
- control_layout.addWidget(self.stop_btn)
- layout.addLayout(control_layout)
-
- # Status
- layout.addWidget(self.preview_label)
- layout.addStretch()
-
- self.setLayout(layout)
-
- # Setup camera
- self.setup_camera()
-
- # Connect signals
- self.start_btn.clicked.connect(self.start_camera)
- self.stop_btn.clicked.connect(self.stop_camera)
-
- def init_no_camera(self):
- """Initialize UI when no camera is available."""
- self.camera_label.setText("No camera detected")
- self.preview_label.setText("No camera found")
- self.camera_combo.hide()
-
- def setup_camera(self):
- """Configure camera and connect to video output."""
- camera_index = self.camera_combo.currentIndex()
- camera = self.cameras[camera_index]
-
- self.camera = QCamera(camera)
- self.camera.setVideoOutput(self.video_widget)
-
- def start_camera(self):
- """Start camera and enable preview."""
- camera_index = self.camera_combo.currentIndex()
-
- camera = self.cameras[camera_index]
- self.camera = QCamera(camera)
-
- self.camera.setVideoOutput(self.video_widget)
- self.camera.start()
- self.preview_label.setText("Camera active")
- self.start_btn.setText("Restart")
- self.stop_btn.setEnabled(True)
-
- def stop_camera(self):
- """Stop camera and disable preview."""
- self.camera.stop()
- self.preview_label.setText("Camera stopped")
- self.start_btn.setText("Start")
- self.start_btn.setEnabled(False)
- self.stop_btn.setEnabled(False)
-
- def closeEvent(self, event):
- """Clean up camera resources."""
- self.camera.stop()
- super().closeEvent(event)
-
- class CameraCapture(QWidget):
- """Camera with photo capture functionality."""
-
- def __init__(self):
- super().__init__()
- self.setWindowTitle("Camera Capture")
- self.setMinimumSize(640, 480)
-
- self.camera = QCamera()
- self.capture_session = QMediaCaptureSession()
- self.image_capture = QImageCapture()
-
- self.video_widget = QVideoWidget()
- self.capture_session.setVideoOutput(self.video_widget)
-
- self.capture_session.setCamera(self.camera)
- self.capture_session.setImageCapture(self.image_capture)
-
- # Image capture settings
- self.image_capture.setCaptureMode(
- QImageCapture.CaptureMode.Photo
- )
- self.image_capture.setResolution(
- self.image_capture.resolutionHighest()
- )
-
- self.image_capture.setCompressionType(
- QImageCapture.CompressionType.Jpeg
- )
- self.image_capture.setJpegQuality(85)
-
- self.capture_btn = QPushButton("Capture Photo")
- self.capture_btn.setEnabled(False)
- self.status_label = QLabel("Ready")
-
- # Layout
- layout = QVBoxLayout()
- layout.addWidget(self.video_widget)
- layout.addWidget(self.capture_btn)
- layout.addWidget(self.status_label)
- self.setLayout(layout)
-
- # Connect signals
- self.capture_btn.clicked.connect(self.capture_photo)
- self.camera.start()
-
- def capture_photo(self):
- """Capture photo from camera."""
- if self.camera.isRunning():
- self.capture_session.setImageCapture(self.image_capture)
- self.image_capture.triggerCapture(
- self.image_capture.imageSize()
- )
- self.status_label.setText("Capturing...")
-
- # Handle captured image
- self.image_capture.imageReady.connect(self.on_image_ready)
- else:
- self.status_label.setText("Camera not running")
-
- def on_image_ready(self, id: int, filePath: str):
- """Callback when image is captured."""
- self.status_label.setText(f"Photo saved: {filePath}")
- self.capture_btn.setEnabled(True)
-
- # Display captured image
- from PySide6.QtGui import QPixmap, QImage
- pixmap = QPixmap(filePath)
- self.video_widget.setPixmap(pixmap.scaled(
- self.video_widget.size(),
- Qt.AspectRatioMode.KeepAspectRatio,
- Qt.TransformationMode.SmoothTransformation
- ))
-
- if __name__ == "__main__":
- app = QApplication(sys.argv)
-
- window = CameraViewer()
- window.show()
- sys.exit(app.exec())
- ```
-
- **Camera Device Enumeration:**
-
- ```python
- from PySide6.QtMultimedia import QMediaDevices
-
- # Get all video input devices (cameras)
- cameras = QMediaDevices.videoInputs()
- print(f"Available cameras: {len(cameras)}")
- for i, camera in enumerate(cameras):
- print(f" Camera {i}: {camera}")
-
- # Get all video output devices (displays)
- video_outputs = QMediaDevices.videoOutputs()
-
- # Get audio input devices (microphones)
- audio_inputs = QMediaDevices.audioInputs()
-
- # Get audio output devices (speakers)
- audio_outputs = QMediaDevices.audioOutputs()
- ```
-
- **Photo Capture Options:**
-
- ```python
- # Set capture mode
- self.image_capture.setCaptureMode(
- QImageCapture.CaptureMode.Photo
- )
-
- # Set resolution
- self.image_capture.setResolution(
- QImageCapture.Resolution.QVGA # or QualityBestResolution
- )
-
- # Set compression
- self.image_capture.setCompressionType(
- QImageCapture.CompressionType.Jpeg
- )
- self.image_capture.setJpegQuality(85)
-
- # Set custom capture dimensions
- self.image_capture.setCaptureSize(640, 480)
-
- # Manual capture trigger
- self.image_capture.triggerCapture(QSize(640, 480))
- ```
-
- ---
-
- ## Media Recording
-
- ### QMediaRecorder Setup
-
- ```python
- from PySide6.QtMultimedia import (
- QMediaRecorder, QMediaCaptureSession, QCamera, QMediaDevices
- )
- from PySide6.QtMultimediaWidgets import QVideoWidget
- from PySide6.QtWidgets import (
- QApplication, QWidget, QVBoxLayout, QHBoxLayout,
- QPushButton, QLabel, QFileDialog
- )
- from PySide6.QtCore import QUrl, QStandardPaths
- from PySide6.QtGui import QDesktopServices
- import os
- import sys
-
- class MediaRecorder(QWidget):
- """Complete media recording with audio/video support."""
-
- def __init__(self):
- super().__init__()
- self.setWindowTitle("Media Recorder")
- self.setMinimumSize(800, 600)
-
- # Camera setup
- self.camera = QCamera(QMediaDevices.videoInputs()[0])
- self.capture_session = QMediaCaptureSession()
- self.capture_session.setCamera(self.camera)
- self.capture_session.setVideoOutput(self.video_widget)
-
- # Video widget
- self.video_widget = QVideoWidget()
-
- # Recorder
- self.recorder = QMediaRecorder()
- self.capture_session.setRecorder(self.recorder)
-
- # UI components
- self.record_btn = QPushButton("Start Recording")
- self.record_btn.setEnabled(False)
- self.stop_btn = QPushButton("Stop Recording")
- self.stop_btn.setEnabled(False)
-
- self.status_label = QLabel("Ready")
- self.progress_label = QLabel("")
-
- self.recorder.recorderStateChanged.connect(self.update_status)
- self.recorder.durationChanged.connect(self.duration_changed)
-
- # Layout
- layout = QVBoxLayout()
- layout.addWidget(self.video_widget)
-
- control_layout = QHBoxLayout()
- control_layout.addWidget(self.record_btn)
- control_layout.addWidget(self.stop_btn)
- control_layout.addWidget(self.progress_label)
- layout.addLayout(control_layout)
-
- layout.addWidget(self.status_label)
- layout.addStretch()
-
- self.setLayout(layout)
-
- # Setup recorder defaults
- self.setup_recorder_defaults()
-
- # Connect signals
- self.record_btn.clicked.connect(self.toggle_recording)
- self.stop_btn.clicked.connect(self.stop_recording)
-
- def setup_recorder_defaults(self):
- """Configure default recording settings."""
- # Default output location in Videos folder
- documents = QStandardPaths.writableLocation(
- QStandardPaths.StandardLocation.MoviesLocation
- )
-
- # Get video extension based on codec
- self.output_format = "mp4"
- self.output_path = os.path.join(documents, f"recording_{self._get_timestamp()}.{self.output_format}")
-
- def _get_timestamp(self):
- """Generate timestamp string."""
- from datetime import datetime
- return datetime.now().strftime("%Y%m%d_%H%M%S")
-
- def toggle_recording(self):
- """Toggle recording on/off."""
- state = self.recorder.recorderState()
-
- if state == QMediaRecorder.RecorderState.RecordingState:
- self.recorder.stop()
- else:
- self.recorder.setOutputLocation(QUrl.fromLocalFile(self.output_path))
- self.recorder.record()
-
- def stop_recording(self):
- """Stop current recording."""
- self.recorder.stop()
- self.status_label.setText("Recording stopped")
- self.record_btn.setEnabled(True)
- self.record_btn.setText("Record Again")
- self.stop_btn.setEnabled(False)
-
- def update_status(self, state):
- """Update status based on recorder state."""
- states = {
- QMediaRecorder.RecorderState.StoppedState: "Ready to record",
- QMediaRecorder.RecorderState.RecordingState: "Recording...",
- QMediaRecorder.RecorderState.PausedState: "Paused",
- QMediaRecorder.RecorderState.ErrorState: "Error occurred"
- }
-
- self.status_label.setText(states.get(state, "Unknown"))
-
- def duration_changed(self, duration):
- """Update progress based on recording duration."""
- if duration > 0:
- seconds = duration // 1000
- self.progress_label.setText(f"Recording: {seconds} seconds")
-
- def closeEvent(self, event):
- """Clean up resources."""
- self.recorder.stop()
- self.camera.stop()
- super().closeEvent(event)
-
- if __name__ == "__main__":
- app = QApplication(sys.argv)
-
- window = MediaRecorder()
- window.show()
- sys.exit(app.exec())
- ```
-
- ### Audio Recording Example
-
- ```python
- from PySide6.QtMultimedia import QMediaRecorder, QMediaCaptureSession
- from PySide6.QtCore import QUrl, QStandardPaths, QMimeType
- import os
- import sys
- from PySide6.QtWidgets import (
- QApplication, QWidget, QVBoxLayout, QPushButton, QLabel, QFileDialog
- )
-
- class AudioRecorder(QWidget):
- """Audio recording from microphone."""
-
- def __init__(self):
- super().__init__()
- self.setWindowTitle("Audio Recorder")
-
- self.recorder = QMediaRecorder()
- self.capture_session = QMediaCaptureSession()
-
- # Audio input setup
- audio_inputs = QStandardPaths.writableLocation(
- QStandardPaths.StandardLocation.MoviesLocation
- )
-
- self.status_label = QLabel("Ready")
- self.record_btn = QPushButton("Start Recording")
-
- layout = QVBoxLayout()
- layout.addWidget(self.record_btn)
- layout.addWidget(self.status_label)
- self.setLayout(layout)
-
- self.record_btn.clicked.connect(self.toggle_recording)
- self.recorder.recorderStateChanged.connect(self.update_status)
-
- # Default output
- self.output_path = os.path.join(
- audio_inputs, "recording.wav"
- )
-
- def toggle_recording(self):
- """Toggle recording state."""
- state = self.recorder.recorderState()
-
- if state == QMediaRecorder.RecorderState.RecordingState:
- self.recorder.stop()
- self.status_label.setText("Recording stopped")
- else:
- self.recorder.setOutputLocation(QUrl.fromLocalFile(self.output_path))
- self.recorder.setAudioFormat(
- QMediaRecorder.AudioFormat.WAV,
- 44100,
- 2,
- 2,
- -16,
- -1
- )
- self.recorder.record()
- self.status_label.setText("Recording...")
-
- def update_status(self, state):
- """Update status label."""
- if state == QMediaRecorder.RecorderState.RecordingState:
- self.status_label.setText("Recording...")
- self.record_btn.setText("Stop")
- else:
- self.status_label.setText("Recording saved")
- self.record_btn.setText("Start Recording")
-
- def save_recording(self):
- """Save recording to file dialog."""
- file_path, _ = QFileDialog.getSaveFileName(
- self, "Save Recording", self.output_path,
- "Audio Files (*.wav *.mp3 *.ogg *.flac);;All Files (*)"
- )
- if file_path:
- self.recorder.setOutputLocation(QUrl.fromLocalFile(file_path))
- self.output_path = file_path
-
- if __name__ == "__main__":
- app = QApplication(sys.argv)
- window = AudioRecorder()
- window.show()
- sys.exit(app.exec())
- ```
-
- ### Video Recording Example
-
- ```python
- from PySide6.QtMultimedia import QMediaRecorder, QMediaCaptureSession
- from PySide6.QtCore import QUrl, QStandardPaths
- import os
- import sys
- from PySide6.QtWidgets import (
- QApplication, QWidget, QVBoxLayout, QPushButton, QLabel
- )
-
- class VideoRecorder(QWidget):
- """Video recording with encoding options."""
-
- def __init__(self):
- super().__init__()
- self.setWindowTitle("Video Recorder")
-
- self.camera = QCamera(QStandardPaths.writableLocation(
- QStandardPaths.StandardLocation.VideosLocation
- ) + "camera.mp4")
-
- self.capture_session = QMediaCaptureSession()
- self.capture_session.setCamera(self.camera)
-
- self.recorder = QMediaRecorder()
- self.capture_session.setRecorder(self.recorder)
-
- self.status_label = QLabel("Ready")
- self.record_btn = QPushButton("Start Recording")
-
- layout = QVBoxLayout()
- layout.addWidget(self.record_btn)
- layout.addWidget(self.status_label)
- self.setLayout(layout)
-
- self.record_btn.clicked.connect(self.toggle_recording)
- self.recorder.recorderStateChanged.connect(self.update_status)
-
- def toggle_recording(self):
- """Start/stop video recording."""
- state = self.recorder.recorderState()
-
- if state == QMediaRecorder.RecorderState.RecordingState:
- self.recorder.stop()
- else:
- self.recorder.setMediaFormat(self._get_video_format())
- self.recorder.setVideoCodec(self.recorder.videoFormat().videoCodec())
- self.recorder.setAudioCodec(self.recorder.audioFormat().audioCodec())
- self.recorder.setOutputLocation(QUrl.fromLocalFile("video.mp4"))
- self.recorder.record()
-
- def _get_video_format(self):
- """Get H.264 video format."""
- return QMediaFormat(
- QMediaFormat.MediaFormat.MPEG4,
- self.camera.videoResolution(),
- QMediaFormat.VelocityLevel.Normal,
- 2000000, # 2 Mbps bitrate
- QMediaFormat.ResolutionSelector.Variable
- )
-
- def update_status(self, state):
- """Update status."""
- if state == QMediaRecorder.RecorderState.RecordingState:
- self.status_label.setText("Recording video...")
- else:
- self.status_label.setText("Recording complete")
-
- def save_recording(self):
- """Open save dialog."""
- file_path, _ = QFileDialog.getSaveFileName(
- self, "Save Video", "recording.mp4",
- "Video Files (*.mp4 *.avi *.mkv);;All Files (*)"
- )
- if file_path:
- self.recorder.setOutputLocation(QUrl.fromLocalFile(file_path))
-
- if __name__ == "__main__":
- app = QApplication(sys.argv)
- window = VideoRecorder()
- window.show()
- sys.exit(app.exec())
- ```
-
- ### Encoding Settings
-
- ```python
- from PySide6.QtMultimedia import QMediaFormat, QMediaRecorder
-
- # Configure video format
- video_format = QMediaFormat()
- video_format.setMediaType(QMediaFormat.MediaType.Video)
- video_format.setMediaFormat(QMediaFormat.MediaFormat.MPEG4)
-
- # Set resolution
- video_format.setResolution(1280, 720) # 1280x720
-
- # Set bitrate (bytes per second)
- video_format.setBitRate(2000000) # 2 Mbps
-
- # Set video codec
- video_format.setVideoCodec(
- QMediaFormat.VideoCodec.H264
- )
-
- # Set framerate
- video_format.setFrameRate(30) # 30 FPS
-
- # Create recorder and set format
- recorder = QMediaRecorder()
- recorder.setMediaFormat(video_format)
-
- # Audio format configuration
- audio_format = QMediaFormat()
- audio_format.setMediaType(QMediaFormat.MediaType.Audio)
- audio_format.setMediaFormat(QMediaFormat.MediaFormat.Mp3)
- audio_format.setAudioCodec(QMediaFormat.AudioCodec.AAC)
- audio_format.setBitRate(128000) # 128 kbps
-
- # Full recording setup with encoding
- recorder.setVideoCodec(QMediaFormat.VideoCodec.H264)
- recorder.setAudioCodec(QMediaFormat.AudioCodec.AAC)
- recorder.setVideoFormat(video_format)
- recorder.setAudioFormat(audio_format)
- recorder.setOutputLocation(QUrl.fromLocalFile("output.mp4"))
- recorder.record()
- ```
-
- ---
-
- ## Error Handling
-
- ### QMediaRecorder Error Handling
-
- ```python
- from PySide6.QtMultimedia import QMediaRecorder, QMediaFormat
- from PySide6.QtWidgets import QMessageBox
- import sys
- from PySide6.QtWidgets import QApplication
-
- class ErrorHandlingRecorder(QMediaRecorder):
- """MediaRecorder with comprehensive error handling."""
-
- def __init__(self, parent=None):
- super().__init__(parent)
- self.error_signal = lambda: self.error_occurred()
-
- def error_occurred(self):
- """Handle recorder errors."""
- error_state = self.error()
-
- error_messages = {
- QMediaRecorder.ErrorState.NoError: "No error",
- QMediaRecorder.ErrorState.InvalidRoleError: "Invalid role error",
- QMediaRecorder.ErrorState.InvalidFormatError: "Invalid format error",
- QMediaRecorder.ErrorState.UnsupportedFormatError: "Unsupported format",
- QMediaRecorder.ErrorState.CodecNotAvailableError: "Codec not available",
- QMediaRecorder.ErrorState.UnknownError: "Unknown error"
- }
-
- error_msg = error_messages.get(error_state, "Unknown error")
- QMessageBox.critical(self, "Recording Error", error_msg)
-
- # Reset recorder state
- self.stop()
-
- # Usage with error handling
- recorder = ErrorHandlingRecorder()
-
- # Connect to error signal
- recorder.error.connect(lambda: print("Recorder error occurred"))
-
- # Set invalid format (to trigger error)
- recorder.setMediaFormat(QMediaFormat())
- ```
-
- ### QMediaPlayer Error Signal
-
- ```python
- from PySide6.QtMultimedia import QMediaPlayer
- from PySide6.QtWidgets import QApplication, QWidget, QLabel, QPushButton
- from PySide6.QtCore import QUrl
- import sys
-
- class ErrorHandlingPlayer(QMediaPlayer):
- """MediaPlayer with error handling."""
-
- def __init__(self, parent=None):
- super().__init__(parent)
- self.status_label = QLabel("Ready")
-
- def playback_error(self, error_state):
- """Handle playback errors."""
- errors = {
- QMediaPlayer.ErrorState.NoError: "No error",
- QMediaPlayer.ErrorState.InvalidRoleError: "Invalid role error",
- QMediaPlayer.ErrorState.ResourceNotAvailableError: "Resource not available",
- QMediaPlayer.ErrorState.ResourceCannotBeReadError: "Cannot read resource",
- QMediaPlayer.ErrorState.FormatError: "Format error",
- QMediaPlayer.ErrorState.PlaybackFailedError: "Playback failed",
- QMediaPlayer.ErrorState.UnknownMediaError: "Unknown media error",
- QMediaPlayer.ErrorState.ProtocolError: "Protocol error",
- QMediaPlayer.ErrorState.AbstractError: "Abstract error"
- }
-
- error_msg = errors.get(error_state, "Unknown error")
- self.status_label.setText(f"Error: {error_msg}")
- print(f"MediaPlayer error: {error_msg}")
-
- # Usage
- app = QApplication(sys.argv)
-
- player = ErrorHandlingPlayer()
- player.playbackError.connect(lambda e: print(f"Error: {e}"))
-
- # Load invalid file (to trigger error)
- player.setSource(QUrl.fromLocalFile("invalid_file.mp4"))
- player.play()
-
- sys.exit(app.exec())
- ```
-
- ### QCamera Error Signal
-
- ```python
- from PySide6.QtMultimedia import QCamera, QCameraError
- from PySide6.QtWidgets import QApplication, QWidget, QLabel, QPushButton
- import sys
-
- class ErrorHandlingCamera(QCamera):
- """Camera with error handling."""
-
- def __init__(self, video_input, parent=None):
- super().__init__(video_input, parent)
- self.status_label = QLabel("Ready")
-
- def camera_error(self, error):
- """Handle camera errors."""
- error_codes = {
- QCameraError.NoError: "No error",
- QCameraError.UnsupportedBackendError: "Unsupported backend",
- QCameraError.UnsupportedFormatError: "Unsupported format",
- QCameraError.VideoInputNotFound: "Video input not found",
- QCameraError.ErrorStartingCamera: "Error starting camera",
- QCameraError.ErrorStoppingCamera: "Error stopping camera",
- QCameraError.ErrorReadingProperty: "Error reading property",
- QCameraError.ErrorWritingProperty: "Error writing property",
- QCameraError.FrameAlreadyReleasedError: "Frame already released",
- QCameraError.ErrorResolutionChange: "Error changing resolution",
- QCameraError.ErrorSourceChange: "Error changing source",
- QCameraError.ErrorInvalidState: "Invalid state",
- QCameraError.ErrorImageBufferCreation: "Error creating image buffer",
- QCameraError.ErrorImageBufferRelease: "Error releasing image buffer",
- QCameraError.ErrorInvalidState: "Invalid camera state",
- QCameraError.ErrorAccessingFrameBuffer: "Error accessing frame buffer",
- QCameraError.ErrorRecordingStopped: "Recording stopped"
- }
-
- error_msg = error_codes.get(error, "Unknown error")
- self.status_label.setText(f"Error: {error_msg}")
- print(f"Camera error: {error_msg}")
-
- # Usage
- app = QApplication(sys.argv)
-
- camera = ErrorHandlingCamera(QCameraError)
- camera.start()
- camera.stop()
-
- sys.exit(app.exec())
- ```
-
- ### Common Error Scenarios
-
- ```python
- from PySide6.QtMultimedia import QMediaPlayer, QMediaDevices, QCamera
- from PySide6.QtWidgets import QMessageBox
- from PySide6.QtCore import QUrl
- import sys
-
- class CommonErrorHandler:
- """Handle common multimedia errors."""
-
- @staticmethod
- def check_device_availability():
- """Check if required devices are available."""
- if not QMediaDevices.audioInputs():
- raise RuntimeError("No audio input devices found")
-
- if not QMediaDevices.videoInputs():
- raise RuntimeError("No camera devices found")
-
- @staticmethod
- def check_file_exists(filepath):
- """Verify file exists and is accessible."""
- if not os.path.exists(filepath):
- raise FileNotFoundError(f"File not found: {filepath}")
-
- if not os.path.isfile(filepath):
- raise FileExistsError("Path is not a file")
-
- @staticmethod
- def handle_codec_error(player, error_state):
- """Handle codec-related errors."""
- if error_state == QMediaPlayer.ErrorState.FormatError:
- QMessageBox.information(
- None, "Codec Error",
- "The required codec is not installed.\n\n"
- "Install K-Lite Codec Pack (Windows) or GStreamer (Linux)."
- )
-
- @staticmethod
- def handle_network_error(player, error_state):
- """Handle network stream errors."""
- if error_state in [
- QMediaPlayer.ErrorState.ResourceNotAvailableError,
- QMediaPlayer.ErrorState.ProtocolError
- ]:
- QMessageBox.warning(
- None, "Network Error",
- "Cannot access network stream.\n"
- "Check your network connection and URL."
- )
-
- # Usage
- try:
- CommonErrorHandler.check_device_availability()
- CommonErrorHandler.check_file_exists("video.mp4")
-
- player = QMediaPlayer()
- player.play()
-
- except (RuntimeError, FileNotFoundError, FileExistsError) as e:
- print(f"Setup error: {e}")
- sys.exit(1)
- ```
-
- ---
-
- ## Codec Support
-
- ### Supported Codecs by Platform
-
- #### Linux (GStreamer Backend)
-
- | Codec | Container | Quality | Required Package |
- |-------|-----------|---------|------------------|
- | **Audio** |
- | MP3 | .mp3 | Good | `gstreamer1.0-plugins-good` |
- | WAV | .wav | Excellent | Built-in |
- | OGG | .ogg | Good | `gstreamer1.0-plugins-good` |
- | FLAC | .flac | Excellent | `gstreamer1.0-plugins-good` |
- | AAC | .m4a | Good | `gstreamer1.0-plugins-good` |
- | **Video** |
- | H.264 (AVC) | .mp4, .avi, .mkv | Excellent | `gstreamer1.0-plugins-bad` |
- | H.265 (HEVC) | .mp4 | Good | `gstreamer1.0-libav` |
- | MPEG-4 | .mp4 | Good | Built-in |
- | MPEG-2 | .mpg, .mpeg | Good | `gstreamer1.0-plugins-bad` |
- | VP8 | .webm | Good | `gstreamer1.0-plugins-good` |
- | VP9 | .webm | Good | `gstreamer1.0-plugins-bad` |
- | AV1 | .webm | Excellent | `gstreamer1.0-libav` |
-
- #### Windows (DirectShow/WMF Backend)
-
- | Codec | Container | Quality | Recommended |
- |-------|-----------|---------|-------------|
- | **Audio** |
- | MP3 | .mp3 | Good | K-Lite Codec Pack |
- | WAV | .wav | Excellent | Built-in |
- | AAC | .m4a | Good | K-Lite Codec Pack |
- | WMA | .wma | Good | Built-in |
- | **Video** |
- | H.264 (AVC) | .mp4, .avi | Excellent | K-Lite Codec Pack |
- | H.265 (HEVC) | .mp4 | Good | HEVC Extension |
- | MPEG-4 | .mp4 | Good | Built-in |
- | WMV | .wmv | Excellent | Built-in |
- | AV1 | .webm | Good | AV1 Extensions |
-
- #### macOS (AVFoundation Backend)
-
- | Codec | Container | Quality | Status |
- |-------|-----------|---------|--------|
- | **Audio** |
- | AAC | .m4a | Excellent | Built-in |
- | ALAC | .m4a | Excellent | Built-in |
- | WAV | .wav | Excellent | Built-in |
- | **Video** |
- | H.264 (AVC) | .mp4, .mov | Excellent | Built-in |
- | H.265 (HEVC) | .mp4 | Good | Built-in (Free) |
- | ProRes | .mov | Excellent | Built-in (Professional) |
- | AAC | .m4a | Good | Built-in |
-
- ### Platform-Specific Notes
-
- #### Linux Installation
-
- ```bash
- # Ubuntu/Debian
- sudo apt-get install libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev \
- gstreamer1.0-plugins-good gstreamer1.0-plugins-bad gstreamer1.0-plugins-ugly
-
- # For better codec support
- sudo apt-get install gstreamer1.0-libav gstreamer1.0-clutter
-
- # Fedora
- sudo dnf install gstreamer1-devel gstreamer1-plugins-base-devel \
- gstreamer1-plugins-good gstreamer1-plugins-bad gstreamer1-plugins-ugly
-
- # Arch Linux
- sudo pacman -S gstreamer gst-plugins-good gst-plugins-bad gst-plugins-ugly \
- gst-libav gst-clutter
-
- # Verify installation
- python -c "from PySide6.QtMultimedia import QMediaDevices; \
- print('Audio inputs:', len(QMediaDevices.audioInputs())); \
- print('Video inputs:', len(QMediaDevices.videoInputs()))"
- ```
-
- #### Windows Setup
-
- ```python
- # Check codec support
- from PySide6.QtMultimedia import QMediaDevices
-
- def check_codec_support():
- """Check available codec support."""
- audio_inputs = QMediaDevices.audioInputs()
- video_inputs = QMediaDevices.videoInputs()
-
- print(f"Audio devices: {len(audio_inputs)}")
- print(f"Video devices: {len(video_inputs)}")
-
- # If no devices, install codec pack
- if not audio_inputs and not video_inputs:
- print("No codec support detected!")
- print("Install K-Lite Codec Pack: https://codecguide.com/install_windows_kl.mp")
- ```
-
- **K-Lite Codec Pack (Windows):**
- - **Standard**: Audio codecs + video players
- - **Basic**: Essential codecs (MPEG-4, H.264)
- - **Full**: All codecs including proprietary
-
- #### macOS Notes
-
- No additional installation required. PySide6 uses AVFoundation which includes:
- - All standard codecs (H.264, AAC, ProRes)
- - AirPlay support for video output
- - Camera support via AVFoundation
-
- ### Format Compatibility Reference
-
- ```python
- from PySide6.QtMultimedia import QMediaFormat
-
- # Check format compatibility
- def check_format_compatibility(filepath):
- """Check if format is supported."""
- media = QMediaFormat()
-
- supported_formats = {
- QMediaFormat.MediaFormat.MPEG4: "MPEG-4 (MP4, M4V)",
- QMediaFormat.MediaFormat.Wav: "WAV (WAV)",
- QMediaFormat.MediaFormat.Mp3: "MP3 (MP3)",
- QMediaFormat.MediaFormat.Ogg: "OGG (OGG)",
- QMediaFormat.MediaFormat.Flac: "FLAC",
- QMediaFormat.MediaFormat.Aac: "AAC (M4A)",
- QMediaFormat.MediaFormat.H264: "H.264 Video",
- QMediaFormat.MediaFormat.H265: "H.265/HEVC Video",
- }
-
- return supported_formats
- ```
-
- ### Codec Selection Examples
-
- ```python
- from PySide6.QtMultimedia import QMediaRecorder, QMediaFormat
-
- # Best quality for recording
- def setup_high_quality_recording():
- recorder = QMediaRecorder()
-
- # Video format
- video_format = QMediaFormat()
- video_format.setMediaType(QMediaFormat.MediaType.Video)
- video_format.setMediaFormat(QMediaFormat.MediaFormat.MPEG4)
- video_format.setVideoCodec(QMediaFormat.VideoCodec.H264)
- video_format.setResolution(1920, 1080) # 1080p
- video_format.setBitRate(8000000) # 8 Mbps
- video_format.setFrameRate(60) # 60 FPS
-
- # Audio format
- audio_format = QMediaFormat()
- audio_format.setMediaType(QMediaFormat.MediaType.Audio)
- audio_format.setMediaFormat(QMediaFormat.MediaFormat.Mp3)
- audio_format.setAudioCodec(QMediaFormat.AudioCodec.AAC)
- audio_format.setBitRate(320000) # 320 kbps
- audio_format.setSampleRate(48000)
-
- recorder.setMediaFormat(video_format)
- recorder.setAudioFormat(audio_format)
- return recorder
-
- # Web-compatible recording
- def setup_web_recording():
- recorder = QMediaRecorder()
-
- video_format = QMediaFormat()
- video_format.setMediaFormat(QMediaFormat.MediaFormat.MPEG4)
- video_format.setVideoCodec(QMediaFormat.VideoCodec.H264)
- video_format.setResolution(1280, 720) # 720p for web
- video_format.setBitRate(2000000) # 2 Mbps
- video_format.setFrameRate(30)
-
- audio_format = QMediaFormat()
- audio_format.setMediaFormat(QMediaFormat.MediaFormat.Mp3)
- audio_format.setAudioCodec(QMediaFormat.AudioCodec.AAC)
- audio_format.setBitRate(128000)
-
- recorder.setMediaFormat(video_format)
- recorder.setAudioFormat(audio_format)
- return recorder
-
- # Mobile-optimized recording
- def setup_mobile_recording():
- recorder = QMediaRecorder()
-
- video_format = QMediaFormat()
- video_format.setMediaFormat(QMediaFormat.MediaFormat.MPEG4)
- video_format.setVideoCodec(QMediaFormat.VideoCodec.H264)
- video_format.setResolution(640, 480) # 480p for mobile
- video_format.setBitRate(500000) # 0.5 Mbps
- video_format.setFrameRate(30)
-
- audio_format = QMediaFormat()
- audio_format.setMediaFormat(QMediaFormat.MediaFormat.Mp3)
- audio_format.setAudioCodec(QMediaFormat.AudioCodec.AAC)
- audio_format.setBitRate(96000)
-
- recorder.setMediaFormat(video_format)
- recorder.setAudioFormat(audio_format)
- return recorder
- ```
-
- ### Troubleshooting Codec Issues
-
- ```python
- from PySide6.QtMultimedia import QMediaDevices, QMediaPlayer
- from PySide6.QtWidgets import QMessageBox, QLabel
-
- def diagnose_codec_issues():
- """Diagnose codec-related problems."""
- issues = []
-
- # Check device availability
- if not QMediaDevices.audioInputs():
- issues.append("No audio input devices found")
-
- if not QMediaDevices.videoInputs():
- issues.append("No camera devices found")
-
- # Test playback
- player = QMediaPlayer()
- try:
- # This will fail if codec is missing
- player.play()
- issues.append("Playback test inconclusive")
- except Exception as e:
- issues.append(f"Playback error: {e}")
-
- if issues:
- QMessageBox.warning(
- None, "Codec Issues",
- "Issues detected:\n\n" + "\n".join(issues) + "\n\n"
- "Consider installing codec packs for your platform."
- )
-
- return issues
- ```