Stream selectors provide fine-grained control over which audio and subtitle streams play for each piece of content. Custom stream selectors use YAML configuration with powerful filtering and conditional logic.
Stream Selector Modes
Channels support three stream selector modes:
public enum ChannelStreamSelectorMode
{
Default = 0,
Custom = 1,
Troubleshooting = 100
}
Reference: ChannelStreamSelectorMode.cs:3-9
Default Mode
Automatic stream selection based on simple rules:
stream_selector_mode: Default
- Selects first audio stream (or matches language if configured)
- Selects subtitles based on subtitle mode
- Suitable for most use cases
Reference: FFmpegLibraryProcessService.cs:147
Custom Mode
Advanced YAML-based stream selection:
stream_selector_mode: Custom
stream_selector: my-selector.yml
Provides complete control over audio and subtitle selection with:
- Language filtering
- Title allowlists/blocklists
- Custom conditions
- Content-based rules
Reference: CustomStreamSelector.cs:14-262
Troubleshooting Mode
Special mode for diagnostics:
stream_selector_mode: Troubleshooting
Used internally for playback troubleshooting with forced stream selection.
Reference: FFmpegLibraryProcessService.cs:167
Custom Stream Selectors
Custom selectors are defined in YAML files stored in the channel stream selectors folder:
~/.local/share/ersatztv/scripts/channel-stream-selectors/
Reference: FileSystemLayout.cs:64,191, CustomStreamSelector.cs:25-27
Selector Structure
Basic selector file structure:
items:
- audio_language:
- eng
- en
subtitle_language:
- eng
- en
- audio_language:
- jpn
- ja
subtitle_language:
- eng
- en
Reference: StreamSelector.cs:3-6, StreamSelectorItem.cs:5-36
Selector Items
Each item defines matching criteria and selection preferences:
public class StreamSelectorItem
{
public List<string> AudioLanguages { get; set; }
public List<string> AudioTitleAllowlist { get; set; }
public List<string> AudioTitleBlocklist { get; set; }
public string AudioCondition { get; set; }
public bool DisableSubtitles { get; set; }
public List<string> SubtitleLanguages { get; set; }
public List<string> SubtitleTitleAllowlist { get; set; }
public List<string> SubtitleTitleBlocklist { get; set; }
public string SubtitleCondition { get; set; }
public string ContentCondition { get; set; }
}
Reference: StreamSelectorItem.cs:5-36
Audio Selection
Configure audio stream selection criteria:
Language Matching
Match audio by language codes:
items:
- audio_language:
- eng
- en
Supports:
- ISO 639-2 (3-letter) codes:
eng, jpn, fra
- ISO 639-1 (2-letter) codes:
en, ja, fr
- Wildcard:
* matches any language
- Glob patterns:
en* matches eng, en-US, etc.
Reference: CustomStreamSelector.cs:67-89
Title Filtering
Filter by audio track title:
Allowlist (must contain one of these):
items:
- audio_title_allowlist:
- commentary
- director
Blocklist (must not contain any of these):
items:
- audio_title_blocklist:
- commentary
- descriptive
Title matching is case-insensitive and uses substring matching.
Reference: CustomStreamSelector.cs:96-111
Audio Conditions
Custom expressions for audio stream properties:
items:
- audio_condition: "channels >= 6"
Available variables:
id - Stream index
title - Track title (lowercase)
lang - Language code (lowercase)
default - Default flag (true/false)
forced - Forced flag (true/false)
codec - Codec name (lowercase)
channels - Channel count
Reference: CustomStreamSelector.cs:264-283
Example conditions:
# Prefer surround sound
audio_condition: "channels >= 6"
# Avoid DTS codec
audio_condition: "codec != 'dts'"
# Default and not commentary
audio_condition: "default == true && !Contains(title, 'commentary')"
# Specific stream ID
audio_condition: "id == 2"
Subtitle Selection
Configure subtitle stream selection:
Language Matching
Match subtitle by language:
items:
- subtitle_language:
- eng
- en
Same wildcard and glob support as audio languages.
Reference: CustomStreamSelector.cs:155-177
Disable Subtitles
Explicitly disable subtitle selection:
items:
- disable_subtitles: true
Useful for conditions where no subtitles should display.
Reference: CustomStreamSelector.cs:142-146
Title Filtering
Filter by subtitle track title:
items:
- subtitle_title_allowlist:
- sdh
- cc
- subtitle_title_blocklist:
- signs
- songs
Reference: CustomStreamSelector.cs:184-199
Subtitle Conditions
Custom expressions for subtitle properties:
items:
- subtitle_condition: "forced == true"
Available variables:
id - Stream index
title - Track title (lowercase)
lang - Language code (lowercase)
default - Default flag (true/false)
forced - Forced flag (true/false)
sdh - SDH flag (true/false)
codec - Codec name (lowercase)
external - External file flag (true/false)
Reference: CustomStreamSelector.cs:285-304
Example conditions:
# Only forced subtitles
subtitle_condition: "forced == true"
# SDH English subtitles
subtitle_condition: "sdh == true && lang == 'eng'"
# External subtitle files only
subtitle_condition: "external == true"
# Prefer default, fallback to forced
subtitle_condition: "default == true || forced == true"
Content Conditions
Apply selector items conditionally based on content context:
items:
- content_condition: "channel_number == '1'"
audio_language:
- eng
Available variables:
channel_number - Channel number (string)
channel_name - Channel name (string)
time_of_day_seconds - Seconds since midnight
day_of_week - Day of week index (0 = first day per locale)
Reference: CustomStreamSelector.cs:307-325, CustomStreamSelector.cs:347-351
Example conditions:
# Different audio for prime time
content_condition: "time_of_day_seconds >= 64800 && time_of_day_seconds <= 82800"
# Weekend schedule
content_condition: "day_of_week == 0 || day_of_week == 6"
# Specific channel
content_condition: "channel_number == '24.1'"
# Channel name pattern
content_condition: "Contains(channel_name, 'Movie')"
Selection Priority
Selector items are evaluated in order:
- First matching item wins
- Items are processed top-to-bottom
- First item where both audio AND subtitle criteria match is selected
- If no item fully matches, no streams selected
Language Priority
Within a language list, earlier languages have priority:
audio_language:
- eng # First choice
- en # Second choice
- und # Fallback
Reference: CustomStreamSelector.cs:67-89
Complete Examples
English Content, Japanese with Subtitles
items:
# English audio: no subtitles
- audio_language:
- eng
- en
disable_subtitles: true
# Japanese audio: English subtitles
- audio_language:
- jpn
- ja
subtitle_language:
- eng
- en
# Other languages: try English subtitles
- audio_language:
- "*"
subtitle_language:
- eng
- en
Surround Sound Preference
items:
# Prefer 5.1+ English audio
- audio_language:
- eng
- en
audio_condition: "channels >= 6"
subtitle_language:
- eng
- en
# Fallback to any English audio
- audio_language:
- eng
- en
subtitle_language:
- eng
- en
Time-Based Selection
items:
# Daytime (6 AM - 10 PM): descriptive audio
- content_condition: "time_of_day_seconds >= 21600 && time_of_day_seconds <= 79200"
audio_title_allowlist:
- descriptive
subtitle_language:
- eng
- en
# Other times: standard audio
- audio_language:
- eng
- en
audio_title_blocklist:
- descriptive
- commentary
Forced Subtitles Only
items:
- audio_language:
- eng
- en
subtitle_condition: "forced == true"
subtitle_language:
- eng
- en
Multi-Channel Configuration
items:
# Movie channel: surround sound, forced subs
- content_condition: "Contains(channel_name, 'Movie')"
audio_condition: "channels >= 6"
subtitle_condition: "forced == true"
# Kids channel: stereo, SDH subtitles
- content_condition: "Contains(channel_name, 'Kids')"
audio_condition: "channels == 2"
subtitle_condition: "sdh == true"
# Default: any English
- audio_language:
- eng
- en
Troubleshooting Selectors
Selector Not Working
Check:
- YAML syntax is valid
- File is in correct directory (
channel-stream-selectors/)
- Channel has
stream_selector_mode: Custom
- Selector file name matches channel configuration
- Item criteria actually match content streams
Wrong Stream Selected
Debug:
- Check item order (first match wins)
- Verify language codes match stream metadata
- Test conditions with simple values first
- Check logs for stream metadata
No Streams Selected
Possible causes:
- No items match the content
- Conditions too restrictive
- Language mismatch
- Content lacks streams matching criteria
Solution: Add fallback item with wildcards:
items:
# ... specific rules ...
# Fallback: any audio, any subtitle
- audio_language:
- "*"
subtitle_language:
- "*"
Selector File Management
Manage selector files through the filesystem:
Create Selector
- Create YAML file in selectors directory
- Define items with selection criteria
- Reference in channel configuration
List Available Selectors
Query available selectors:
GetChannelStreamSelectors query
Reference: GetChannelStreamSelectorsHandler.cs:6-10
Selector Location
Default location:
~/.local/share/ersatztv/scripts/channel-stream-selectors/
Reference: FileSystemLayout.cs:64,191
Expression Evaluation
Conditions evaluate once per content item:
- Keep expressions simple
- Avoid redundant calculations
- Use early items for common cases
Stream Analysis
Stream metadata read from media files:
- First-time analysis cached
- Subsequent plays use cached data
- Performance impact minimal
Best Practices
Organization
Use descriptive selector names:
english-with-forced-subs.yml
multilingual-accessibility.yml
surround-sound-preferred.yml
Fallback Rules
Always include fallback:
items:
# Specific rules...
# Final fallback
- audio_language:
- "*"
Condition Complexity
Prefer simple conditions:
# Good
audio_condition: "channels >= 6"
# Avoid
audio_condition: "(channels >= 6 && codec == 'aac') || (channels >= 2 && codec == 'ac3' && default == true)"
Testing
Test selectors with:
- Variety of content types
- Different language configurations
- Edge cases (missing streams, unusual metadata)
Start with Default mode and only switch to Custom when you need specific stream selection logic.
Stream selector evaluation happens during playout building. Changes to selectors require channel restart to take effect.
Custom stream selectors with no matching items result in no audio/subtitle selection. Always include a fallback item.