<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	xmlns:media="http://search.yahoo.com/mrss/"
>

<channel>
	<title>AVPlayer &#8211; Wade Tregaskis</title>
	<atom:link href="https://wadetregaskis.com/tags/avplayer/feed/" rel="self" type="application/rss+xml" />
	<link>https://wadetregaskis.com</link>
	<description></description>
	<lastBuildDate>Tue, 09 Dec 2025 18:31:55 +0000</lastBuildDate>
	<language>en-US</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	

<image>
	<url>https://wadetregaskis.com/wp-content/uploads/2016/03/Stitch-512x512-1-256x256.png</url>
	<title>AVPlayer &#8211; Wade Tregaskis</title>
	<link>https://wadetregaskis.com</link>
	<width>32</width>
	<height>32</height>
</image> 
<site xmlns="com-wordpress:feed-additions:1">226351702</site>	<item>
		<title>Fading audio with AVPlayer</title>
		<link>https://wadetregaskis.com/fading-audio-with-avplayer/</link>
					<comments>https://wadetregaskis.com/fading-audio-with-avplayer/#respond</comments>
		
		<dc:creator><![CDATA[]]></dc:creator>
		<pubDate>Wed, 10 Dec 2025 16:29:00 +0000</pubDate>
				<category><![CDATA[Coding]]></category>
		<category><![CDATA[Howto]]></category>
		<category><![CDATA[audio]]></category>
		<category><![CDATA[AVAudioMix]]></category>
		<category><![CDATA[AVAudioMixInputParameters]]></category>
		<category><![CDATA[AVMutableAudioMix]]></category>
		<category><![CDATA[AVMutableAudioMixInputParameters]]></category>
		<category><![CDATA[AVPlayer]]></category>
		<category><![CDATA[fade]]></category>
		<category><![CDATA[Swift]]></category>
		<guid isPermaLink="false">https://wadetregaskis.com/?p=8630</guid>

					<description><![CDATA[AVPlayer doesn&#8217;t provide a built-in way to fade in or out. I previously described how you achieve a video fade-in (or out) using general CoreAnimation layer animation, as part of making a macOS screen saver. Now let&#8217;s tackle the audio. I&#8217;m not certain what curve this implements, but to my ears it doesn&#8217;t sound quite&#8230; <a class="read-more-link" href="https://wadetregaskis.com/fading-audio-with-avplayer/" data-wpel-link="internal">Read more</a>]]></description>
										<content:encoded><![CDATA[
<p><code>AVPlayer</code> doesn&#8217;t provide a built-in way to fade in or out.  I previously described how you achieve a <em>video</em> fade-in (or out) using general CoreAnimation layer animation, as <a href="https://wadetregaskis.com/how-to-make-a-macos-screen-saver/#Bonus_topic_fading_in" data-wpel-link="internal">part of making a macOS screen saver</a>.  Now let&#8217;s tackle the audio.</p>



<div class="wp-block-kevinbatdorf-code-block-pro padding-disabled" data-code-block-pro-font-family="" style="font-size:.875rem;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><pre class="shiki light-plus" style="background-color: #FFFFFF" tabindex="0"><code><span class="line"><span style="color: #0000FF">extension</span><span style="color: #000000"> </span><span style="color: #267F99">AVPlayer</span><span style="color: #000000"> {</span></span>
<span class="line"><span style="color: #000000">    </span><span style="color: #0000FF">func</span><span style="color: #000000"> </span><span style="color: #795E26">fadeAudio</span><span style="color: #000000">(</span><span style="color: #795E26">from</span><span style="color: #000000"> </span><span style="color: #001080">startVolume</span><span style="color: #000000">: </span><span style="color: #267F99">Float</span><span style="color: #000000">, </span><span style="color: #795E26">to</span><span style="color: #000000"> </span><span style="color: #001080">endVolume</span><span style="color: #000000">: </span><span style="color: #267F99">Float</span><span style="color: #000000">, </span><span style="color: #795E26">duration</span><span style="color: #000000">: </span><span style="color: #267F99">Double</span><span style="color: #000000">) {</span></span>
<span class="line"><span style="color: #000000">        </span><span style="color: #0000FF">let</span><span style="color: #000000"> audioMix = </span><span style="color: #795E26">AVMutableAudioMix</span><span style="color: #000000">()</span></span>
<span class="line"></span>
<span class="line"><span style="color: #000000">        audioMix.</span><span style="color: #001080">inputParameters</span><span style="color: #000000"> = (player.</span><span style="color: #001080">currentItem</span><span style="color: #000000">?.</span><span style="color: #001080">tracks</span><span style="color: #000000"> ?? [])</span></span>
<span class="line"><span style="color: #000000">                                    .</span><span style="color: #795E26">compactMap</span><span style="color: #000000">(\.</span><span style="color: #001080">assetTrack</span><span style="color: #000000">)</span></span>
<span class="line"><span style="color: #000000">                                    .</span><span style="color: #795E26">filter</span><span style="color: #000000">({ </span><span style="color: #0000FF">$0</span><span style="color: #000000">.</span><span style="color: #001080">mediaType</span><span style="color: #000000"> == .</span><span style="color: #001080">audio</span><span style="color: #000000"> })</span></span>
<span class="line"><span style="color: #000000">                                    .</span><span style="color: #795E26">map</span><span style="color: #000000"> { track </span><span style="color: #AF00DB">in</span></span>
<span class="line"><span style="color: #000000">            </span><span style="color: #0000FF">let</span><span style="color: #000000"> currentTime = player.</span><span style="color: #795E26">currentTime</span><span style="color: #000000">()</span></span>
<span class="line"></span>
<span class="line"><span style="color: #000000">            </span><span style="color: #0000FF">let</span><span style="color: #000000"> parameters = </span><span style="color: #795E26">AVMutableAudioMixInputParameters</span><span style="color: #000000">(</span><span style="color: #795E26">track</span><span style="color: #000000">: track)</span></span>
<span class="line"></span>
<span class="line"><span style="color: #000000">            parameters.</span><span style="color: #795E26">setVolumeRamp</span><span style="color: #000000">(</span><span style="color: #795E26">fromStartVolume</span><span style="color: #000000">: startVolume,</span></span>
<span class="line"><span style="color: #000000">                                     </span><span style="color: #795E26">toEndVolume</span><span style="color: #000000">: endVolume,</span></span>
<span class="line"><span style="color: #000000">                                     </span><span style="color: #795E26">timeRange</span><span style="color: #000000">: </span><span style="color: #795E26">CMTimeRange</span><span style="color: #000000">(</span><span style="color: #795E26">start</span><span style="color: #000000">: currentTime,</span></span>
<span class="line"><span style="color: #000000">                                                            </span><span style="color: #795E26">duration</span><span style="color: #000000">: </span><span style="color: #795E26">CMTime</span><span style="color: #000000">(</span><span style="color: #795E26">seconds</span><span style="color: #000000">: duration,</span></span>
<span class="line"><span style="color: #000000">                                                                             </span><span style="color: #795E26">preferredTimescale</span><span style="color: #000000">: currentTime.</span><span style="color: #001080">timeScale</span><span style="color: #000000">)))</span></span>
<span class="line"></span>
<span class="line"><span style="color: #000000">            </span><span style="color: #AF00DB">return</span><span style="color: #000000"> parameters</span></span>
<span class="line"><span style="color: #000000">        }</span></span>
<span class="line"></span>
<span class="line"><span style="color: #000000">        player.</span><span style="color: #001080">currentItem</span><span style="color: #000000">?.</span><span style="color: #001080">audioMix</span><span style="color: #000000"> = audioMix</span></span>
<span class="line"><span style="color: #000000">    }</span></span>
<span class="line"><span style="color: #000000">}</span></span></code></pre></div>



<p>I&#8217;m not certain what curve this implements, but to my ears it doesn&#8217;t sound quite as harsh as a naive linear ramp, so perhaps it&#8217;s an S-curve or similar.</p>



<h2 class="wp-block-heading">Edge cases not handled</h2>



<h3 class="wp-block-heading">Where there&#8217;s less than <code>duration</code> time left in the track(s).</h3>



<p>How you want to handle that might vary depending on context. e.g. you could clamp the duration to the remaining duration (but you have to think about whether your individual tracks all have the same duration, whether they match the duration of the overall playback item, and whether they&#8217;re all aligned within the playback sequence), or wrap it around to the beginning (if you&#8217;re looping), or carry the fade through to the next item (if you&#8217;re playing a sequence of items), etc. Alas this must be left as an exercise to you, the reader.</p>



<h3 class="wp-block-heading">Starting a fade while another one is still in progress.</h3>



<p>It will halt the previous fade, immediately jump to <code>startVolume</code>, and perform the new fade. If you know that a prior fade is in progress you could potentially extrapolate the current volume and start from there instead (though beware of non-linear ramping).</p>



<h3 class="wp-block-heading">If you move the playhead backwards (e.g. skimming, or looped playback).</h3>



<p>The mix will stay in place, and result in wonky volume levels on the subsequent plays through. To work around that, you can add:</p>



<div class="wp-block-kevinbatdorf-code-block-pro padding-disabled" data-code-block-pro-font-family="" style="font-size:.875rem;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><pre class="shiki light-plus" style="background-color: #FFFFFF" tabindex="0"><code><span class="line"><span style="color: #000000">player.</span><span style="color: #001080">currentItem</span><span style="color: #000000">?.</span><span style="color: #001080">audioMix</span><span style="color: #000000"> = </span><span style="color: #0000FF">nil</span></span>
<span class="line"><span style="color: #000000">player.</span><span style="color: #001080">volume</span><span style="color: #000000"> = endVolume</span></span></code></pre></div>



<p>…wherever you restart playback at the beginning or move playback earlier than the end of the fade.</p>



<h2 class="wp-block-heading">Future work?</h2>



<p>You <em>can</em> work around these limitations by doing a timer-based fade (i.e. <code>player.volume += smallIncrement</code> at regular, short intervals). However, the problem with that approach is that it&#8217;s not synchronised to actual playback &#8211; e.g. if the audio is paused, stutters, or faces an initial loading delay, your fade won&#8217;t wait for it, potentially resulting in no fade at all (e.g. it takes five seconds to buffer the audio before playback starts, at which point your five second &#8220;fade&#8221; has run to completion, so your audio starts playing abruptly at full volume).</p>



<p>There&#8217;s very likely a third option that addresses <em>all</em> these shortcomings, but I explored that a bit and concluded that it&#8217;d be a lot more work.  If someone wants to explore that all the way, I&#8217;d be interested to see the result.  But for many purposes the above code is quite sufficient.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://wadetregaskis.com/fading-audio-with-avplayer/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">8630</post-id>	</item>
		<item>
		<title>How to loop video in AVPlayer</title>
		<link>https://wadetregaskis.com/how-to-loop-video-in-avplayer/</link>
					<comments>https://wadetregaskis.com/how-to-loop-video-in-avplayer/#respond</comments>
		
		<dc:creator><![CDATA[]]></dc:creator>
		<pubDate>Tue, 09 Dec 2025 17:42:00 +0000</pubDate>
				<category><![CDATA[Coding]]></category>
		<category><![CDATA[Howto]]></category>
		<category><![CDATA[AVPlayer]]></category>
		<category><![CDATA[AVPlayerItem]]></category>
		<category><![CDATA[AVPlayerItemDidPlayToEndTime]]></category>
		<category><![CDATA[AVPlayerLooper]]></category>
		<category><![CDATA[AVQueuePlayer]]></category>
		<category><![CDATA[Swift]]></category>
		<guid isPermaLink="false">https://wadetregaskis.com/?p=8625</guid>

					<description><![CDATA[This is pretty rudimentary, but apparently our robot overlords need me to write this post because many of them suggested some truly bizarre approaches, some of which don&#8217;t work at all. If you&#8217;re using AVQueuePlayer, then just use AVPlayerLooper. Easy. But if for some reason you want to use AVPlayer specifically (e.g. you need to&#8230; <a class="read-more-link" href="https://wadetregaskis.com/how-to-loop-video-in-avplayer/" data-wpel-link="internal">Read more</a>]]></description>
										<content:encoded><![CDATA[
<p>This is pretty rudimentary, but apparently our robot overlords need me to write this post because many of them suggested some truly bizarre approaches, some of which don&#8217;t work at all.</p>



<p>If you&#8217;re using <code><a href="https://developer.apple.com/documentation/avfoundation/avqueueplayer" data-wpel-link="external" target="_blank" rel="external noopener">AVQueuePlayer</a></code>, then just use <code><a href="https://developer.apple.com/documentation/avfoundation/avplayerlooper" data-wpel-link="external" target="_blank" rel="external noopener">AVPlayerLooper</a></code>.  Easy.  But if for some reason you want to use <code>AVPlayer</code> specifically (e.g. you need to do additional things anyway when playback loops back around), read on.</p>



<p><code>AVPlayer</code> itself doesn&#8217;t really help you here &#8211; it&#8217;s <code>AVPlayerItem</code> that you need to look at, as it has several notifications associated with it that can be very useful &#8211; most relevant here is<a href="https://developer.apple.com/documentation/avfoundation/avplayeritem/didplaytoendtimenotification" data-wpel-link="external" target="_blank" rel="external noopener"> </a>the <a href="https://developer.apple.com/documentation/avfoundation/avplayeritem/didplaytoendtimenotification" data-wpel-link="external" target="_blank" rel="external noopener">didPlayToEndTimeNotification</a>.  Simply observe that and restart the player explicitly, like so:</p>



<div class="wp-block-kevinbatdorf-code-block-pro padding-disabled" data-code-block-pro-font-family="" style="font-size:.875rem;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><pre class="shiki light-plus" style="background-color: #FFFFFF" tabindex="0"><code><span class="line"><span style="color: #0000FF">let</span><span style="color: #000000"> player = </span><span style="color: #795E26">AVPlayer</span><span style="color: #000000">(…)  </span><span style="color: #008000">// You&#39;ll need to keep a reference to this somewhere, for use in the notification handler, as the notification doesn&#39;t provide it.</span></span>
<span class="line"></span>
<span class="line"><span style="color: #000000">void </span><span style="color: #795E26">startPlayer</span><span style="color: #000000">() {  </span><span style="color: #008000">// Or wherever / however you start playback.</span></span>
<span class="line"><span style="color: #000000">    NotificationCenter.</span><span style="color: #001080">default</span><span style="color: #000000">.</span><span style="color: #795E26">addObserver</span><span style="color: #000000">(</span><span style="color: #0000FF">self</span><span style="color: #000000">,</span></span>
<span class="line"><span style="color: #000000">                                           </span><span style="color: #795E26">selector</span><span style="color: #000000">: </span><span style="color: #795E26">#selector</span><span style="color: #000000">(</span><span style="color: #795E26">playedToEnd</span><span style="color: #000000">(_:)),</span></span>
<span class="line"><span style="color: #000000">                                           </span><span style="color: #795E26">name</span><span style="color: #000000">: .</span><span style="color: #001080">AVPlayerItemDidPlayToEndTime</span><span style="color: #000000">,</span></span>
<span class="line"><span style="color: #000000">                                           </span><span style="color: #795E26">object</span><span style="color: #000000">: player.</span><span style="color: #001080">currentItem</span><span style="color: #000000">)</span></span>
<span class="line"></span>
<span class="line"><span style="color: #000000">    player.</span><span style="color: #795E26">play</span><span style="color: #000000">()</span></span>
<span class="line"><span style="color: #000000">} </span></span>
<span class="line"></span>
<span class="line"><span style="color: #0000FF">@objc</span><span style="color: #000000"> </span><span style="color: #0000FF">func</span><span style="color: #000000"> </span><span style="color: #795E26">playedToEnd</span><span style="color: #000000">(</span><span style="color: #795E26">_</span><span style="color: #000000"> </span><span style="color: #001080">notification</span><span style="color: #000000">: Notification) {</span></span>
<span class="line"><span style="color: #000000">    </span><span style="color: #795E26">assert</span><span style="color: #000000">(player.</span><span style="color: #001080">currentItem</span><span style="color: #000000"> == notification.</span><span style="color: #001080">object</span><span style="color: #000000"> as? AVPlayerItem, </span><span style="color: #A31515">&quot;AVPlayer </span><span style="color: #0000FF">\(</span><span style="color: #000000FF">player</span><span style="color: #0000FF">)</span><span style="color: #A31515">&#39;s current item (</span><span style="color: #0000FF">\(</span><span style="color: #000000FF">player.</span><span style="color: #001080">currentItem</span><span style="color: #0000FF">)</span><span style="color: #A31515">) doesn&#39;t match the one in the notification object (</span><span style="color: #0000FF">\(</span><span style="color: #000000FF">notification.</span><span style="color: #001080">object</span><span style="color: #0000FF">)</span><span style="color: #A31515">).&quot;</span><span style="color: #000000">)</span></span>
<span class="line"></span>
<span class="line"><span style="color: #000000">    player.</span><span style="color: #795E26">seek</span><span style="color: #000000">(</span><span style="color: #795E26">to</span><span style="color: #000000">: .</span><span style="color: #001080">zero</span><span style="color: #000000">)</span></span>
<span class="line"><span style="color: #000000">    player.</span><span style="color: #795E26">play</span><span style="color: #000000">()</span></span>
<span class="line"><span style="color: #000000">}</span></span></code></pre></div>



<p>Again, considering using <code>AVQueuePlayer</code> if possible, but the above works too.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://wadetregaskis.com/how-to-loop-video-in-avplayer/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">8625</post-id>	</item>
	</channel>
</rss>
