pyqt-multimedia · v1.0.0 · 2026-07-08 · sha256 c2cecc63c43d62c5

pyqt-multimedia v1.0.0A

Immutable. This exact content is served forever at /api/v1/blob/c2cecc63c43d62c5.

---
name: pyqt-multimedia
description: "PyQt/PySide multimedia - audio playback, video playback, camera, audio recording, media player"
metadata:
  author: mte90
  version: 1.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.

## 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
- **QMediaRecorder** - Audio/video recording

---

## 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

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()
        
        # 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)
        
        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
```

---

## Camera Capture

### Display Camera Feed

```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:
            print("No cameras available")
            return
            
        # Create camera
        self.camera = QCamera(self.cameras[0])
        
        # Video widget
        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)
        
    def closeEvent(self, event):
        self.camera.stop()
        super().closeEvent(event)
```

### Capture Photo

```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

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()
        
        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

```python
from PyQt6.QtMultimedia import QCamera, QMediaCaptureSession, QMediaRecorder
from PyQt6.QtMultimediaWidgets import QVideoWidget
from PyQt6.QtWidgets import QWidget, QVBoxLayout, QPushButton, QLabel

class VideoRecorder(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)

```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 audio/video codecs
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)
```

---

## Best Practices

### Audio/Video Capture

```python
# ✅ GOOD: Check availability first
from PyQt6.QtMultimedia import QMediaDevices

if not QMediaDevices.audioInputs():
    print("No microphone available")
    return

# ✅ GOOD: 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

```python
# ✅ GOOD: Clean up resources
def closeEvent(self, event):
    self.player.stop()
    self.recorder.stop()
    self.camera.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

---

## 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
```