UPlayer is a modular streaming pipeline that enables MPEG-DASH playback on iOS by dynamically converting MPD manifests into HLS playlists compatible with AVPlayer.
It supports:
- β DASH β HLS conversion (SegmentTemplate, SegmentBase, live & VOD)
- β Custom AVAssetResourceLoader for virtual HLS (uplayer://)
- β Live streaming with sliding window
- β Thumbnail sprite parsing & scrubbing preview support
- β Modular Combine-based processing pipeline
- β Audio + Video adaptation sets
- β MP4 fallback (SegmentBase / progressive)
- β HLS and MP4 playback
Works with AVPlayer Seamless DASH playback via HLS translation
- VOD MPD (static)
- Live MPD (dynamic)
- SegmentTemplate (duration / timeline)
- SegmentBase (SIDX / byte-range)
- Multi-representation (adaptive bitrate)
- π Live Streaming
- MPD polling (minimumUpdatePeriod)
- Sliding HLS window generation
- Live edge control (stay N segments behind)
- Playlist merge strategy for continuity
- πΌ Thumbnail Scrubbing
- Parses DASH image adaptation sets
- Supports tiled JPEG sprites
- Efficient caching (sprite + cropped image)
- Custom preview rendering for scrub UI
Pipeline is built using Combine processors:
URL
β
UPlayerMetadataDownloader
β
UPlayerMPDParser
β
UPlayerThumbnailDownloader
β
UPlayerHLSGenerator / UPlayerSegmentBaseHLSGenerator / UPlayerMPDToMP4Resolver (fallback)
β
UPlayerAVAssetResourceLoader (uplayer://)
β
AVPlayer
Each processor implements:
protocol UPlayerAssetProcessorProtocol {
func process(asset: UPlayerAssetProtocol) -> AnyPublisher<UPlayerAssetProtocol, Error>
}| Processor | Purpose |
|---|---|
| UPlayerMetadataDownloader | Downloads MPD or detects media type |
| UPlayerMPDParser | Parses DASH manifest |
| UPlayerThumbnailDownloader | Extracts + downloads thumbnail sprites |
| UPlayerHLSGenerator | Generates HLS (SegmentTemplate) |
| UPlayerSegmentBaseHLSGenerator | Generates byte-range HLS |
| UPlayerMPDToMP4Resolver | Fallback to MP4 |
AVPlayer AVPlayerItem UPlayerAVAssetResourceLoader
Custom scheme:
uplayer://...Handles:
- MPD refresh
- Playlist regeneration
- Segment merging
- Create player let player = UPlayer()
- Optionally assign a delegate (UPlayerDelegate) to monitor playback activity, and provide a rendering view when using a custom player controller. player.delegate = self
- Play DASH URL uPlayer.play(url: URL(string: "https://example.com/manifest.mpd")!)
- Stop playback uPlayer.stop()
if let cue = asset.thumbnailMetadata?.cue(for: scrubTime),
let image = asset.thumbnailMetadata?.image(for: cue) {
previewImageView.image = image
}AVPlayer Behavior AVPlayerViewController does NOT guarantee thumbnail preview Use custom UI for scrubbing thumbnails Live Streams Must continuously update playlists Otherwise you get: Playlist File unchanged for longer than 1.5 * target duration Pause Handling
For live streams:
Short pause β keep refreshing MPD Long pause β detach player: player.replaceCurrentItem(with: nil)
β SegmentTemplate β SegmentBase β Live MPD β Thumbnail Tracks
UPlayer supports automatic audio transcoding for DASH streams when the original audio codec is not compatible with AVPlayer.
This is critical because iOS AVPlayer supports only a limited set of audio codecs, primarily:
- β mp4a.40.2 (AAC-LC)
- β mp4a.40.5 (HE-AAC)
- β mp4a.40.29 (HE-AAC v2)
- β Unsupported Audio Codecs
Examples:
- ec-3 (Dolby Digital Plus)
- ac-3
- g711 (pcma, pcmu)
- non-standard or malformed mp4a.*
1. Detection (HLS Generator)
During DASH β HLS conversion:
Audio representations are analyzed Codec is normalized (e.g. mp4a.40.02 β mp4a.40.2) If unsupported β transcoding is enabled
2. URL Rewriting
Unsupported audio segments are rewritten to a custom scheme:
uplayer://example.com/audio/seg_1.m4s?mode=audio-transcode&codec=ec-3
This allows interception by:
UPlayerAVAssetResourceLoader
3. Resource Loader Interception
When AVPlayer requests the segment:
mode=audio-transcode
the loader:
Converts URL β original HTTPS URL Downloads original segment Extracts audio samples (if needed) Transcodes audio β AAC Returns data to AVPlayer
4. Transcoding Pipeline
Original segment (m4s/mp4)
β
(optional) MP4 demux
β
Audio samples (e.g. G.711 / EC-3)
β
Decode β PCM
β
Encode β AAC (LC)
β
Wrap β ADTS
β
Return to AVPlayer
5. HLS Master Playlist Adjustment
When transcoding is enabled:
CODECS="mp4a.40.2"
Even if original codec was:
ec-3 / pcma / pcmu
πΉ HLS Generators
UPlayerHLSGenerator UPlayerSegmentBaseHLSGenerator
Responsible for:
detecting unsupported audio rewriting URLs to uplayer://
πΉ Resource Loader UPlayerAVAssetResourceLoader
Handles:
intercepting custom scheme downloading original media invoking transcoder returning transformed data
πΉ Transcoder UPlayerG711ToAACTranscoder UPlayerAACADTSEncoder
Responsibilities:
- decode source audio β PCM
- encode PCM β AAC
- output ADTS stream
1. ADTS vs fMP4
Transcoded audio uses:
AAC + ADTS
So:
β Do NOT include #EXT-X-MAP β Use .aac segments
2. Codec Consistency
The codec declared in HLS must match transcoder output:
mp4a.40.2
Mismatch causes:
β no audio β playback stalls
3. Performance Considerations
Transcoding is CPU-intensive:
happens per segment may increase startup latency may require caching
Recommended:
cache transcoded segments reuse decoded PCM when possible
4. Live Streams
For live DASH:
transcoding happens continuously ensure: sliding window HLS playlists segment caching stable timestamps
| Codec | Action |
|---|---|
| mp4a.40.2 | pass-through |
| mp4a.40.5 | pass-through |
| mp4a.40.29 | pass-through |
| ec-3 | transcode |
| ac-3 | transcode |
| pcma / pcmu | transcode |
| unknown | pass-through |
UPlayer ensures compatibility by:
Detecting unsupported audio codecs Routing them through a custom pipeline Transcoding to AAC Presenting a clean HLS stream to AVPlayer
No DRM support (CENC) No subtitles yet No native AVPlayer thumbnail rendering Requires custom UI for scrubbing preview
LL-HLS support DRM (FairPlay / Widevine mapping) Subtitle tracks Smart bitrate selection Disk cache for thumbnails & segments
Maxim Komleu
MIT License