<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: MaximKomlev</title>
    <description>The latest articles on DEV Community by MaximKomlev (@maximkomlev).</description>
    <link>https://dev.to/maximkomlev</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4221181%2F244fbfeb-34a0-413c-bda0-22d7abd19e02.jpeg</url>
      <title>DEV Community: MaximKomlev</title>
      <link>https://dev.to/maximkomlev</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/maximkomlev"/>
    <language>en</language>
    <item>
      <title>Playing MPEG-DASH with Native AVPlayer on iOS — Building UPlayer</title>
      <dc:creator>MaximKomlev</dc:creator>
      <pubDate>Sun, 01 Nov 2026 21:29:24 +0000</pubDate>
      <link>https://dev.to/maximkomlev/playing-mpeg-dash-with-native-avplayer-on-ios-building-uplayer-38lb</link>
      <guid>https://dev.to/maximkomlev/playing-mpeg-dash-with-native-avplayer-on-ios-building-uplayer-38lb</guid>
      <description>&lt;p&gt;&lt;em&gt;How I built an open-source Swift framework that converts MPEG-DASH into an AVPlayer-compatible HLS representation directly on the client.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Apple's &lt;code&gt;AVPlayer&lt;/code&gt; provides a powerful foundation for media playback on iOS. It supports hardware-accelerated decoding, adaptive streaming, Picture in Picture, and integration with native playback controls.&lt;/p&gt;

&lt;p&gt;However, there is one important limitation: &lt;strong&gt;AVPlayer does not natively support MPEG-DASH.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;For applications that need to play DASH streams, the usual options are to integrate another playback engine or introduce server-side conversion to HLS.&lt;/p&gt;

&lt;p&gt;I wanted to explore a different approach:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What if we could adapt MPEG-DASH streams to AVPlayer without replacing the native playback engine or modifying the streaming backend?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;That experiment became &lt;a href="https://github.com/MaximKomlev/UPlayer" rel="noopener noreferrer"&gt;UPlayer&lt;/a&gt;, an open-source Swift framework for MPEG-DASH, HLS, and MP4 playback.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Idea: Convert DASH to HLS on the Client
&lt;/h2&gt;

&lt;p&gt;MPEG-DASH and HLS solve similar problems. Both describe media representations and segments that a player can retrieve over HTTP.&lt;/p&gt;

&lt;p&gt;However, their manifest formats and playback integration differ.&lt;/p&gt;

&lt;p&gt;DASH typically uses an XML-based Media Presentation Description (MPD), while HLS uses M3U8 playlists.&lt;/p&gt;

&lt;p&gt;Instead of decoding and rendering media independently, UPlayer translates the DASH manifest and media references into an HLS representation that AVPlayer can understand.&lt;/p&gt;

&lt;p&gt;The conversion happens inside the application.&lt;/p&gt;

&lt;p&gt;This allows UPlayer to retain native playback functionality while introducing a separate layer responsible for media compatibility.&lt;/p&gt;

&lt;h2&gt;
  
  
  Architecture: A Virtual Streaming Server Inside the Application
&lt;/h2&gt;

&lt;p&gt;The central architectural idea is to move part of the streaming-server functionality into the client.&lt;/p&gt;

&lt;p&gt;UPlayer downloads and parses manifests, prepares media representations, generates virtual HLS playlists, and resolves requests through a custom &lt;code&gt;AVAssetResourceLoader&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The overall architecture looks like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F9b64mbo65gqqs4462go2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F9b64mbo65gqqs4462go2.png" alt="UPlayer Architecture" width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The generated HLS representation is virtual. UPlayer does not need to launch a local HTTP server or require the backend to provide an additional HLS stream.&lt;/p&gt;

&lt;p&gt;When the media is compatible with Apple's playback stack, the original encoded video segments can be reused without decoding and re-encoding them.&lt;/p&gt;

&lt;p&gt;This is an important distinction: &lt;strong&gt;UPlayer adapts the streaming representation rather than implementing its own video decoder or renderer.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  A Modular Processing Pipeline
&lt;/h2&gt;

&lt;p&gt;I wanted the media-processing architecture to remain extensible rather than becoming a large, tightly coupled player implementation.&lt;/p&gt;

&lt;p&gt;UPlayer uses independent processors coordinated by &lt;code&gt;UPlayerAssetProcessorsQueue&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Each processor performs a specific operation on a shared media asset.&lt;/p&gt;

&lt;p&gt;Some of the main components include:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Component&lt;/th&gt;
&lt;th&gt;Responsibility&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;UPlayerMetadataDownloader&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Downloads media metadata and identifies the source type&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;UPlayerMPDParser&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Parses MPEG-DASH manifests&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;UPlayerThumbnailDownloader&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Processes thumbnail metadata and sprite resources&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;UPlayerHLSGenerator&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Generates HLS playlists from DASH segment templates&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;UPlayerSegmentBaseHLSGenerator&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Handles byte-range-based DASH representations&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;UPlayerMPDToMP4Resolver&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Provides an MP4 fallback path&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;UPlayerAVAssetResourceLoader&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Serves virtual media resources to AVPlayer&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Processors share a common interface:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight swift"&gt;&lt;code&gt;&lt;span class="kd"&gt;protocol&lt;/span&gt; &lt;span class="kt"&gt;UPlayerAssetProcessorProtocol&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;func&lt;/span&gt; &lt;span class="nf"&gt;process&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="nv"&gt;asset&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;UPlayerAssetProtocol&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;AnyPublisher&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;UPlayerAssetProtocol&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;Error&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Using Combine allows the processing stages to be composed while maintaining a consistent asset representation.&lt;/p&gt;

&lt;p&gt;The benefit is that individual stages can evolve independently.&lt;/p&gt;

&lt;p&gt;For example, supporting another DASH segment addressing mode does not require redesigning the playback engine.&lt;/p&gt;

&lt;h2&gt;
  
  
  Connecting Virtual HLS to AVPlayer
&lt;/h2&gt;

&lt;p&gt;Generating an HLS playlist is only part of the problem.&lt;/p&gt;

&lt;p&gt;AVPlayer must also be able to request the playlist and its referenced media resources.&lt;/p&gt;

&lt;p&gt;UPlayer uses a custom URL scheme:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;uplayer://...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These requests are handled by &lt;code&gt;UPlayerAVAssetResourceLoader&lt;/code&gt;, which maps the virtual resources to generated playlists, downloaded media, or cached content.&lt;/p&gt;

&lt;p&gt;Conceptually, the process looks like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fbw1okbz381on2jk08ddd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fbw1okbz381on2jk08ddd.png" alt="Modular Processing Flow diagram" width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This separation is central to the design.&lt;/p&gt;

&lt;p&gt;UPlayer manages media preparation and resource resolution. AVPlayer remains responsible for playback, decoding, timing, and rendering.&lt;/p&gt;

&lt;h2&gt;
  
  
  Supporting Live MPEG-DASH
&lt;/h2&gt;

&lt;p&gt;Video-on-demand content has a relatively stable segment timeline.&lt;/p&gt;

&lt;p&gt;Live streaming is more complicated because the available media window changes continuously.&lt;/p&gt;

&lt;p&gt;UPlayer includes support for dynamic MPDs and live playlist generation.&lt;/p&gt;

&lt;p&gt;The live processing logic handles:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;MPD refresh based on manifest update information.&lt;/li&gt;
&lt;li&gt;Sliding HLS playlist windows.&lt;/li&gt;
&lt;li&gt;Playlist merging to maintain continuity.&lt;/li&gt;
&lt;li&gt;Live-edge positioning with a configurable segment offset.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The objective is to maintain a continuously updated HLS representation as the underlying DASH stream advances.&lt;/p&gt;

&lt;p&gt;This is especially important because simply regenerating a playlist without accounting for segment continuity can disrupt playback.&lt;/p&gt;

&lt;h2&gt;
  
  
  Caching and Offline Playback
&lt;/h2&gt;

&lt;p&gt;Media caching is another important part of the architecture.&lt;/p&gt;

&lt;p&gt;UPlayer can cache manifests and media resources, allowing downloaded content to be reused across playback requests.&lt;/p&gt;

&lt;p&gt;Caching is integrated with resource loading, so AVPlayer can consume previously downloaded data without requiring a separate player implementation.&lt;/p&gt;

&lt;p&gt;This provides several practical benefits:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Reduced repeated network downloads.&lt;/li&gt;
&lt;li&gt;Faster access to already cached media.&lt;/li&gt;
&lt;li&gt;Prefetching of media resources.&lt;/li&gt;
&lt;li&gt;Offline playback when the necessary content is fully cached.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The cache also supports lifecycle management, including size limits, expiration policies, and least-recently-used eviction.&lt;/p&gt;

&lt;h2&gt;
  
  
  On-Demand G.711 to AAC Transcoding
&lt;/h2&gt;

&lt;p&gt;Not every media source uses an audio format that AVPlayer can consume in the required streaming configuration.&lt;/p&gt;

&lt;p&gt;One example is G.711, commonly encountered in communication and camera streaming systems.&lt;/p&gt;

&lt;p&gt;UPlayer supports pluggable audio transcoding, including converting G.711 into AAC when required.&lt;/p&gt;

&lt;p&gt;The processing flow is straightforward:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fk4bqb4qcn2a4jgporha9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fk4bqb4qcn2a4jgporha9.png" alt="UPlayer Architecture" width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The important architectural decision is that transcoding is optional and performed only when needed.&lt;/p&gt;

&lt;p&gt;Compatible audio does not need to pass through an unnecessary transcoding stage.&lt;/p&gt;

&lt;p&gt;The transcoding implementation is also separate from the main playback pipeline, allowing additional formats to be supported independently.&lt;/p&gt;

&lt;h2&gt;
  
  
  Thumbnail Previews and Media Export
&lt;/h2&gt;

&lt;p&gt;UPlayer also includes features beyond basic playback.&lt;/p&gt;

&lt;p&gt;For DASH sources containing image adaptation sets, the framework can process tiled thumbnail sprites and provide previews during timeline scrubbing.&lt;/p&gt;

&lt;p&gt;The repository also includes &lt;code&gt;UPlayerMediaExporter&lt;/code&gt;, which supports exporting processed DASH, HLS, and MP4 media to local files.&lt;/p&gt;

&lt;p&gt;For supported sources, the exporter can download media segments, handle audio compatibility when necessary, and mux the resulting tracks into an MP4 file.&lt;/p&gt;

&lt;p&gt;These capabilities reuse the same general principle: process media at the application layer while avoiding unnecessary decoding or transcoding.&lt;/p&gt;

&lt;h2&gt;
  
  
  Using UPlayer in Swift
&lt;/h2&gt;

&lt;p&gt;The framework exposes a simple playback interface.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight swift"&gt;&lt;code&gt;&lt;span class="kd"&gt;import&lt;/span&gt; &lt;span class="kt"&gt;UPlayer&lt;/span&gt;

&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;player&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;UPlayer&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;URL&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nv"&gt;string&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"https://example.com/manifest.mpd"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;

&lt;span class="n"&gt;player&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;play&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Playback can be stopped with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight swift"&gt;&lt;code&gt;&lt;span class="n"&gt;player&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stop&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Applications can also use &lt;code&gt;UPlayerDelegate&lt;/code&gt; to observe playback-related events and integrate the framework with custom rendering interfaces.&lt;/p&gt;

&lt;p&gt;The goal is to keep application-level usage straightforward while hiding the complexity of manifest parsing, playlist generation, and resource resolution.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Keep AVPlayer?
&lt;/h2&gt;

&lt;p&gt;The main motivation behind this project was not to build another media player.&lt;/p&gt;

&lt;p&gt;It was to preserve Apple's native playback capabilities while supporting streaming formats that AVPlayer does not directly understand.&lt;/p&gt;

&lt;p&gt;By keeping AVPlayer as the playback engine, applications can continue using:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Hardware-accelerated video decoding.&lt;/li&gt;
&lt;li&gt;Native Picture in Picture.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;AVPlayerViewController&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;System playback and background-media integration.&lt;/li&gt;
&lt;li&gt;Custom playback interfaces built around AVFoundation.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The media-processing layer can evolve independently from the native player.&lt;/p&gt;

&lt;p&gt;That separation is what makes this architecture interesting to me.&lt;/p&gt;

&lt;h2&gt;
  
  
  Final Thoughts
&lt;/h2&gt;

&lt;p&gt;UPlayer began as an experiment in adapting media formats rather than replacing playback technology.&lt;/p&gt;

&lt;p&gt;By translating MPEG-DASH into a virtual HLS representation, the framework makes it possible to use native AVPlayer for DASH playback without requiring server-side conversion.&lt;/p&gt;

&lt;p&gt;The modular architecture also provides room for features such as live playlist management, persistent caching, thumbnail processing, audio transcoding, and media export.&lt;/p&gt;

&lt;p&gt;If you're working with AVFoundation, MPEG-DASH, HLS, or custom media pipelines on Apple platforms, you may find the implementation useful.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Source code:&lt;/strong&gt; &lt;a href="https://github.com/MaximKomlev/UPlayer" rel="noopener noreferrer"&gt;UPlayer on GitHub&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The project is open source, and feedback, contributions, and technical discussions are welcome.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Originally published on &lt;a href="https://medium.com/@maxkomlev/building-a-native-mpeg-dash-player-for-ios-without-third-party-playback-engines-b76b52ec1d54" rel="noopener noreferrer"&gt;Medium&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>iso</category>
      <category>video</category>
      <category>avplayer</category>
      <category>mpegdash</category>
    </item>
  </channel>
</rss>
