---
title: Media Operations Manual
url: https://doc.liz6.com/en/devops/media-ops
locale: en
area: devops
tags:
- devops
date: 2026-06-30
modified: 2026-07-16
description: Media Operations Manual Audio/video-related operations, including PipeWire management, volume control, device routing, recording, and troubleshooting. Architect…
---

# Media Operations Manual

Audio/video-related operations, including PipeWire management, volume control, device routing, recording, and troubleshooting.

## Architecture Overview

<svg viewBox="0 0 720 340" xmlns="http://www.w3.org/2000/svg" font-family="-apple-system,'Source Han Sans CN','Microsoft YaHei',sans-serif" role="img" aria-label="PipeWire Audio Architecture: Data flow from applications to hardware, and the position of the WirePlumber session policy layer and pipewire-pulse compatibility layer">
  <defs><marker id="mopah" markerWidth="10" markerHeight="8" refX="8" refY="3" orient="auto"><path d="M0,0 L8,3 L0,6 Z" fill="#475569"/></marker></defs>
  <rect width="720" height="340" fill="#ffffff"/>
  <text x="360" y="28" text-anchor="middle" font-size="17" font-weight="700" fill="#1f2933">PipeWire Architecture: Data flows from apps to hardware; WirePlumber/pipewire-pulse are management layers</text>
  <rect x="123" y="55" width="70" height="34" rx="6" fill="#e2e8f0"/>
  <text x="158" y="76" text-anchor="middle" font-size="12" font-weight="700" fill="#334155">App</text>
  <line x1="193" y1="72" x2="209" y2="72" stroke="#475569" stroke-width="1.6" marker-end="url(#mopah)"/>
  <rect x="211" y="55" width="170" height="34" rx="6" fill="#4f46e5"/>
  <text x="296" y="76" text-anchor="middle" font-size="11" font-weight="700" fill="#ffffff">PipeWire (media.session)</text>
  <line x1="381" y1="72" x2="397" y2="72" stroke="#475569" stroke-width="1.6" marker-end="url(#mopah)"/>
  <rect x="399" y="55" width="110" height="34" rx="6" fill="#e2e8f0"/>
  <text x="454" y="76" text-anchor="middle" font-size="12" font-weight="700" fill="#334155">ALSA Driver</text>
  <line x1="509" y1="72" x2="525" y2="72" stroke="#475569" stroke-width="1.6" marker-end="url(#mopah)"/>
  <rect x="527" y="55" width="70" height="34" rx="6" fill="#e2e8f0"/>
  <text x="562" y="76" text-anchor="middle" font-size="12" font-weight="700" fill="#334155">Hardware</text>
  <line x1="296" y1="89" x2="296" y2="121" stroke="#475569" stroke-width="1.6" marker-end="url(#mopah)"/>
  <rect x="96" y="123" width="400" height="48" rx="8" fill="#f0fdfa" stroke="#99f6e4"/>
  <text x="296" y="143" text-anchor="middle" font-size="13" font-weight="700" fill="#115e59">WirePlumber</text>
  <text x="296" y="161" text-anchor="middle" font-size="11" fill="#0f766e">Session policy manager, replacing pipewire-media-session</text>
  <line x1="296" y1="171" x2="296" y2="203" stroke="#475569" stroke-width="1.6" marker-end="url(#mopah)"/>
  <rect x="96" y="205" width="400" height="48" rx="8" fill="#ffedd5"/>
  <text x="296" y="225" text-anchor="middle" font-size="13" font-weight="700" fill="#9a3412">pipewire-pulse</text>
  <text x="296" y="243" text-anchor="middle" font-size="11" fill="#c2410c">PulseAudio compatibility layer for legacy apps</text>
  <rect x="40" y="273" width="640" height="48" rx="8" fill="#eef2ff" stroke="#c7d2fe"/>
  <text x="360" y="293" text-anchor="middle" font-size="12.5" fill="#3730a3">Audio/video data flows along the top row (App → PipeWire → ALSA → Hardware); WirePlumber manages routing policies, pipewire-pulse is the compatibility layer.</text>
  <text x="360" y="311" text-anchor="middle" font-size="12.5" fill="#3730a3">pactl operates pipewire-pulse, wpctl operates the native PipeWire layer — first determine which layer is being used when troubleshooting.</text>
</svg>

Key Concepts:
- **PipeWire** = Unified audio/video service, replacing both PulseAudio and JACK
- **WirePlumber** = Device hot-plug/auto-routing policy (e.g., when headphones are plugged in or HDMI output is switched)
- **pipewire-pulse** = PulseAudio compatibility layer, allowing PA apps to use PipeWire transparently
- **pactl** operates pipewire-pulse, **wpctl** operates the native PipeWire layer

## Status Checks

```bash
# Service status
systemctl --user status pipewire wireplumber pipewire-pulse

# PipeWire native info
pw-cli info                          # PipeWire service summary
pw-dump                              # Full dump (nodes/ports/links)
pw-top                               # Real-time performance monitoring (similar to htop)
pw-cli list-objects                  # List all objects

# WirePlumber devices
wpctl status                         # Device overview (sinks/sources/default devices)
wpctl inspect <id>                   # View details of a specific device/node

# PulseAudio compatibility layer
pactl info                           # PulseAudio service info
pactl list sinks                     # List all audio outputs
pactl list sources                   # List all audio inputs (including monitors)
pactl list cards                     # Sound card info (profile/ports)
pactl list sink-inputs               # Currently playing audio streams
pactl list modules                   # Loaded modules

# Hardware
aplay -l                             # List ALSA playback devices
arecord -l                           # List ALSA recording devices
lspci | grep -i audio                # PCI audio devices
cat /proc/asound/cards               # Sound cards from kernel perspective
```

## Volume Control

```bash
# wpctl (Recommended, native PipeWire)
wpctl set-volume @DEFAULT_AUDIO_SINK@ 5%+      # Default output +5%
wpctl set-volume @DEFAULT_AUDIO_SINK@ 5%-      # Default output -5%
wpctl set-volume @DEFAULT_AUDIO_SINK@ 0.5      # Set to 50%
wpctl set-mute @DEFAULT_AUDIO_SINK@ toggle     # Toggle mute
wpctl set-volume <sink-id> 0.75                # Set specific sink to 75%

# pactl (PulseAudio compatibility layer)
pactl set-sink-volume @DEFAULT_SINK@ +5%
pactl set-sink-volume @DEFAULT_SINK@ 50%
pactl set-sink-mute @DEFAULT_SINK@ toggle

# amixer (ALSA low-level, direct hardware control)
amixer scontrols                      # List simple mixer controls
amixer sget Master                    # View Master volume
amixer sset Master 80%                # Set Master to 80%
amixer sset Master 5%+                # Master +5%
amixer sset Master toggle             # Toggle mute
amixer sset Headphone 70%
```

## Audio Routing (Switch Output Device)

```bash
# List available sinks and switch
wpctl status                          # Note the target sink ID
wpctl set-default <sink-id>           # Set as default output
wpctl inspect <sink-id>               # Confirm switch took effect

# Migrate currently playing stream to a specific sink
pactl list sink-inputs                # Note the sink-input ID
pactl move-sink-input <input-id> <sink-id>

# Switch to a specific device (by name)
SINK=$(pactl list sinks short | grep -i "hdmi\|analog\|usb" | head -1 | awk '{print $1}')
pactl set-default-sink "$SINK"

# View sound card profiles (analog/digital/HDMI switching)
pactl list cards
pactl set-card-profile <card-name> output:analog-stereo
pactl set-card-profile <card-name> output:hdmi-stereo
pactl set-card-profile <card-name> output:iec958-stereo  # SPDIF
```

## Restarting WirePlumber / PipeWire

```bash
# Restart the entire audio stack (soft restart, apps won't notice)
systemctl --user restart pipewire pipewire-pulse wireplumber

# Restart WirePlumber alone (refreshes device list)
systemctl --user restart wireplumber

# Full reset (kill then start)
systemctl --user stop pipewire pipewire-pulse wireplumber
sleep 1
systemctl --user start pipewire wireplumber pipewire-pulse
```

## Troubleshooting

```bash
# 1. Check if there is any audio output
paplay /usr/share/sounds/alsa/Front_Center.wav    # Test tone
speaker-test -t sine -f 440 -l 1                   # 440Hz sine wave (bypasses PA/PW)

# 2. Check if pipewire-pulse is frozen (common: Chrome video freezes)
pactl info 2>&1                                    # Check for errors/timeouts
paplay /usr/share/sounds/alsa/Front_Center.wav     # If exit code is 124, pipewire-pulse is frozen
# Fix: systemctl --user restart pipewire-pulse

# 3. Check if any process is exclusively locking the sound card
fuser /dev/snd/*                                   # Who is using audio devices
lsof /dev/snd/*                                    # More detailed view

# 4. Check if ALSA layer is working normally
aplay -D hw:0,0 /usr/share/sounds/alsa/Front_Center.wav  # Bypass PipeWire and play directly
speaker-test -D hw:0,0 -t sine -f 440 -l 1
# If ALSA plays fine but PipeWire has no sound → PipeWire layer issue
# If ALSA also reports errors → Driver/hardware issue

# 5. USB/HDMI hot-plug not responding
wpctl status                          # Check if device appears
systemctl --user restart wireplumber  # Force refresh device list
journalctl --user -u wireplumber -f   # Check WirePlumber logs

# 6. Bluetooth audio
bluetoothctl connect XX:XX:XX:XX:XX:XX
# After connecting, WirePlumber automatically creates a BT sink, visible via wpctl status
# If not appearing: systemctl --user restart wireplumber

# 7. Multiple simultaneous outputs (if needed)
pactl load-module module-combine-sink   # Create a combined sink to output to all devices simultaneously
# Unload: pactl unload-module module-combine-sink
```

### Common Symptoms Quick Reference

| Symptom | Possible Cause | Command |
|------|---------|------|
| Chrome video progress bar stuck, buffering | pipewire-pulse frozen | `systemctl --user restart pipewire-pulse` |
| No sound at all | Wrong default sink | `wpctl status` to check default sink |
| Headphones not auto-switching | WirePlumber didn't react | `systemctl --user restart wireplumber` |
| Some apps play sound, others don't | Different sinks used | `pactl list sink-inputs` to check routing |
| Bluetooth headset connected but no sound | BT profile dropped | `pactl list cards` to check profile |

## Recording

```bash
# Screen recording (PipeWire + wf-recorder, native Wayland)
wf-recorder -f output.mp4                    # Record full screen
wf-recorder -g "$(slurp)" -f output.mp4      # Record selected area
wf-recorder --audio -f output.mp4            # With audio (requires pipewire)
wf-recorder --audio=HDMI -f output.mp4       # Specify audio source

# Audio recording (PipeWire)
pw-record output.wav                          # Record from default input device
pw-record --target=<node-id> output.wav       # Specify input source

# PulseAudio compatibility
parecord output.wav                           # Record from default mic
parecord --device=<source> output.wav         # Specify input source
parec --device=<sink>.monitor output.wav      # Record system output (loopback)

# Screenshots (Wayland/Hyprland)
grim screenshot.png                           # Full screen screenshot
grim -g "$(slurp)" screenshot.png             # Selected area screenshot
grim - | wl-copy                              # Screenshot to clipboard
```

## Volume/Media Keys

Hyprland media key binding example (`hyprland.lua`):

```lua
-- Volume control (wpctl)
hl.dsp.bind({"SUPER"}, "XF86AudioRaiseVolume",  function() hl.exec("wpctl set-volume @DEFAULT_AUDIO_SINK@ 5%+") end)
hl.dsp.bind({"SUPER"}, "XF86AudioLowerVolume",  function() hl.exec("wpctl set-volume @DEFAULT_AUDIO_SINK@ 5%-") end)
hl.dsp.bind({"SUPER"}, "XF86AudioMute",         function() hl.exec("wpctl set-mute @DEFAULT_AUDIO_SINK@ toggle") end)
hl.dsp.bind({"SUPER"}, "XF86AudioMicMute",      function() hl.exec("wpctl set-mute @DEFAULT_AUDIO_SOURCE@ toggle") end)

-- Playback control (playerctl, requires installation)
hl.dsp.bind({}, "XF86AudioPlay",  function() hl.exec("playerctl play-pause") end)
hl.dsp.bind({}, "XF86AudioNext",  function() hl.exec("playerctl next") end)
hl.dsp.bind({}, "XF86AudioPrev",  function() hl.exec("playerctl previous") end)
```

Other `playerctl` usages:
```bash
playerctl play-pause                  # Control current player
playerctl -p spotify next             # Specify player
playerctl -a play-pause               # All players
playerctl -l                          # List controllable players
playerctl metadata                    # Current song info
playerctl metadata --format '{{artist}} - {{title}}'
```

## Video/Codec Information

```bash
ffprobe video.mp4                     # Full video info
ffprobe -v quiet -show_streams video.mp4 | grep codec  # Only codecs
ffprobe -v quiet -show_entries stream=codec_name,width,height video.mp4

# Check hardware codec support
vainfo                                # VA-API (Intel/AMD)
vulkaninfo | grep -i video            # Vulkan Video extension
ffmpeg -encoders | grep vaapi         # Available VA-API encoders
```

## Firmware

```bash
# AMD GPU firmware (codec/display)
ls /lib/firmware/amdgpu/              # gc_*, psp_*, sdma_*, vcn_*, etc.
dmesg | grep -i "firmware\|amdgpu"    # Firmware loading logs

# SOF (Sound Open Firmware, Intel audio DSP)
dmesg | grep -i sof
ls /lib/firmware/intel/sof/
```
