Autoplaying inline videos on iPhone iOS 10 using Angular

Starting with iOS 10, Safari on mobile (iPhone and iPad) started allowing developers to autoplay videos inline provided the video you are trying to play meets certain conditions. Before that, your video would not auto play inline, and when it would play, it would play full screen only. These changes were in place to prevent “websites that auto-play with sound”, if you’ve ever browsed to a site on your phone and an annoying video ad started playing, inline video autoplay is probably to blame for it but there are other legitimate uses for the inline video HTML attribute.

Video as Animated GIF

GIFs are cool we all agree, but behind the scenes they are HUGE objects and in terms of performance, GIF is definitely not the best format for the web.

It’s not uncommon for GIFs to tip the scales at several megabytes, depending on quality, frame rate, and length. If you’re trying to improve loading performance for your site and help users reduce data usage, animated GIF just isn’t compatible with that goal.

One way that you can remediate this issue is to convert these huges files to compressed lean videos which maintains a great resolution while saving huge amount of data for your users. 

Muted Autoplay

In short for a video to play inline on mobile browsers, it needs the following key attributes to be added to the video tag (depending on the mobile platform). 

  • autoplay
  • muted
  • playsinline

Only the first two are necessary for Chrome For Android but if you’re dealing with Safari, you’ll need to add the playsinline attribute which was introduced in iOS 10. A cross browser compatible code would look like:

<video src="/video.mp4" loop muted playsinline poster="/poster.png">

</video>

On most recent browsers and platforms, this would render your video inline autoplaying, with the loop attribute making your video essentially act as a GIF. 

It Doesn’t Work!

I highlighted most in the last sentence of the previous paragraph because you will most certain run into cases where it just doesn’t work. My specific case was iOS 10 and 11 iPhones running an app rendering an Angular app using a custom WebView. The video tag in that app would respect the autoplay attribute as soon as the user interacted with the page, as specified in the iOS technical guidelines for autoplay but would simply ignore the playsinline attribute and play the video full screen. It took quite a bit of Googling and trial and error to finally get it working but if you run into the same error, here are things to check for to get you up and running:

webView.configuration.allowsInlineMediaPlayback = true

if #available(iOS 10.0, *) {
webConfiguration.mediaTypesRequiringUserActionForPlayback = []
} else {
// Fallback on earlier versions
webConfiguration.mediaPlaybackRequiresUserAction = false
}

The muted attribute problem

With all these steps above enabled, I was still unable to play a video inline on an iPhone SE running iOS 10 no matter what I tried. This post brought to my attention the fact that as stated not only does the video NEEDS to be muted in order for the autoplay attribute to be taken in account, but the muted attribute itself might not work as advertised on all browsers and this unlocked the issue for me. Inspecting the video element showed me that even though the muted attribute was set in the HTML, the DOM element showed muted=false . I doubled checked with FFMpeg to make sure that indeed my mp4 file did not include an audio track and it did not! The only logical deduction was that something was broken at the browser level, prevent the muted attribute from being properly applied in the DOM. In the end a combination of using iphone-inline-video and dynamically triggering the play attribute gave me a cross-browser compatible way of playing videos inline. My final Angular code looked something like

# In my component.html

<video #gif loop muted playsinline [src]="gifUrl">
</video>



# in component.ts
import enableInlineVideo from 'iphone-inline-video';
@ViewChild('gif', { read: ElementRef }) gif: ElementRef;
ngAfterViewInit() {
const video: HTMLVideoElement = this.gif.nativeElement;
// Crucial to mute the video before for Safari autoplay to work
video.muted = true;
// Use iphone-inline-video library here after muting the video
enableInlineVideo(video);
video.oncanplay = () => {
video.muted = true;
};
const promise = video.play();
if (promise !== undefined) {
promise
.catch(err => {
// Auto-play was prevented
console.log('Autoplay was prevented');
})
.then(() => {
// Auto-play started
console.log('Autoplay started');
});
}
}

If you run into a

NotAllowedError (DOM Exception 35): The request is not allowed by the user agent or the platform in the current context, possibly because the user denied permission.

This is definitely an indication that autoplay was prevented by the browser, more than likely because of an issue with the muted attribute. Use the solution listed above and see if it solves your issue.