Capture Video on iOS devices using the MediaRecorder API and display it using HTML5 Video Player

Issue:

  • I am facing an issue where I cannot record video or get a video stream from the camera on iOS through my Angular web application built using ng build.

Investigation:

  • In my investigation, I explored various websites that discuss Apple iOS features such as websockets, MediaRecorder, and getUserDevices().
  • I also looked into the specifications and version support for these features. All documentation I came across indicated that the MediaRecorder is an experimental feature that requires manual activation.

Other:

  • Interestingly, my code functions properly on Android Chrome, Desktop Chrome, Opera (PC/Mobile), and Firefox (PC/Mobile).

Unfortunately, I am unsure of what steps to take next as the documentation suggests that iOS does not support the recording API.

  • It's worth noting that my site uses HTTPS with valid certificates.

If anyone has any tips, resources, links, or sample code to share, it would be greatly appreciated.

Answer №1

IOS MediaRecorder Update for 2022

Safari 14.5 now supports the MediaRecorder API.
Check out this code snippet demonstrating how to use MediaRecorder along with getUserMedia().

    initiateVideoRecordingForIOS: function() {
        console.log("Setting up the recorder");
        let self = this;
        this.data = [];

        if (MediaRecorder.isTypeSupported('video/mp4')) {
            // IOS exclusively uses mp4 format, no support for webm
            var options = {mimeType: 'video/mp4', videoBitsPerSecond : 1000000};
        } else {
            // For non-IOS devices, stick to video/webm
            console.error("ERROR: Is this really an IOS device??");
            var options = {mimeType: 'video/webm'};
        }

        // Remember, a canvas element is required instead of a video element
        let stream = document.getElementById('self-canvas').captureStream(15);
        this.recorder = new MediaRecorder(stream, options);

        this.recorder.ondataavailable = function(evt) {
            if (evt.data && evt.data.size > 0) {
                self.data.push(evt.data);
                console.log('Chunk size: ' + evt.data.size);
            }
        }

        this.recorder.onstop = function(evt) {
            console.log('Stopping recorder');
            var blob = new Blob(self.data, {type: "video/mp4"});
            // Handle the blob accordingly
        }
        this.recorder.start(1000);
        looper();
    },

Note that IOS lacks support for video.captureStream() and video/webm. Utilize a video element to bridge

navigator.mediaDevices.getUserMedia()
and a canvas element for MediaRecorder's functioning.

Moreover, IOS restricts declaring only one getUserMedia() stream simultaneously. Make sure to set up the video element or stream globally as this.video or this.localStream for easy access by other elements.

this.localStream = await navigator.mediaDevices.getUserMedia({
    audio: false,
    video: {width: 430, height: 430, facingMode: "user"}
});

let self = this;
self.video = document.createElement('video');
self.video.srcObject = this.localStream;
const canvas = document.getElementById("self-canvas");
const ctx = canvas.getContext('2d');

function draw_2DCanvas(){
    ctx.clearRect(0,0, canvas.width, canvas.height);
        ctx.drawImage(self.video, 0, 0, 430, 430);
    }
    requestAnimationFrame(draw_2DCanvas);
}
draw_2DCanvas();

On other devices, simplifying the process using video.captureStream() without necessitating a canvas element is viable.
Generally, opt for video/webm.

Crucial Reminder
If no editing of the video is intended, direct transmission of localStream to MediaRecorder works seamlessly, enabling audio recording as well.

this.recorder = new MediaRecorder(this.localStream, options);

Answer №2

It seems like you may have already found a solution to your problem.

Unfortunately, with your current setup, Safari and any iOS browser won't be supported since they all run on Webkit/Safari engines.

If you want more information on compatibility with MediaRecorder, check out this link.

There are workarounds available, but it might not be practical to expect customers to enable certain flags just for your website :)

- Doug

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

The wrapper is failing to extend the full height of the entire page

Initially, I thought setting the height of my wrapping div to 100% would be a quick task that I could complete in five minutes. However, it ended up taking me hours, so now I'm here hoping someone can assist me with this commonly asked question. The ...

Showcasing a dynamically created JSON document on a basic web page

Looking for a solution to display the contents of a result.json file generated by my Java tool on a simple website. I am aware that accessing local files directly with JavaScript is not possible, so seeking alternative methods that do not involve using a w ...

Learn the art of bypassing TypeScript errors using @ts-ignore!

I recently encountered an issue when trying to import a pure JavaScript library into a TypeScript project, resulting in the error message: Could not find a declaration file for module xxx. After some research, I learned that this error can be suppressed u ...

The switch statement is not functioning properly when the button is pressed

I am trying to trigger an alert inside a switch statement when I click a button, but for some reason, it is not working. Even if I simply have an alert in the previous function, there is no output on my website. What could be causing this issue? Here is ...

Preserving the HTML tag structure in lxml - What is the best method?

Here is a url link that I have: I am attempting to extract the main body content of this news page using LXML with xpath: //article, however it seems to include content beyond the body tag As LXML modifies the HTML structure upon initialization, I am see ...

Describe a Typescript Interface Value as a collection of strings or any input provided as an argument

Uncertain if the question title is accurate - currently developing react components within a library that has specific predefined values for certain properties. However, additional values may need to be added on a per usage basis. So far, this setup is fun ...

What is the reason for encountering a TypeScript error when using a union type?

interface Bird { age:string, eat:()=>any } interface Fish { age:string, swim:()=>any } type Pet = Fish | Bird; everything looks good so far, defining a Pet type const pet:Pet={ age:"sd", eat:()=>{} } when trying to return ...

Expanding the `div` element to visually occupy the entire vertical space

I'm facing a design challenge with my webpage layout. I have a fixed header and footer, but I need the content section to dynamically adjust its height between the two. The goal is to fill the remaining vertical space with a background-image in the co ...

The issue with jQuery trigger not passing the value of a radio button has been

I have implemented a radio button (Yes, No) and an input text box on my form. If the user selects 'No', the input field is disabled and jQuery triggers a change event for AJAX form submission. $('#form-id').change(function(){ .... }); ...

What is the most strategic way to conceal this overlay element?

Currently, the website I'm developing features a series of navigation elements at the top such as "Products" and "Company." Upon hovering over the Products link, an overlay displays a list of products with clickable links. Positioned at the top of the ...

Dynamic scrolling text for overflowing text display

Here's a scenario I'm facing: Let's say I have three div tags, each 100 pixels wide: <--- DIV WIDTH ---> Text in div 1 Text in div two, it overflows Text in div three <--- DIV WIDTH ---> Currently, I've set the following C ...

Transitioning an NX environment to integrate ESM

My NX-based monorepo is quite extensive, consisting of half a dozen apps, frontend, backend, and dozens of libs. Currently, everything is set up to use commonjs module types, as that's what the NX generators have always produced. However, many librar ...

The PHP random number generator appears to be malfunctioning when being compared to the $_POST[] variable

In this section, random numbers are generated and converted to strings. These string values are then used in the HTML. $num1 = mt_rand(1, 9); $num2 = mt_rand(1, 9); $sum = $num1 + $num2; $str1 = (string) $num1; $str2 = (string) $num2; The following code ...

Loading tab content on click using jQuery

These tabs have a chrome-like appearance. When the first tab (Facebook) is clicked, it shows Facebook content. Clicking on the second tab (Twitter) displays the content of the first tab. Tabs three, four, and five show their respective contents without any ...

Obtained a ZIP file via CodeSandbox that contains a Vue.JS webpage. However, the index.html file yielded an

I have created my first ever Vue.JS / Vue2Leaflet app. It runs perfectly fine on codesandbox.io. However, when I download the ZIP file and try to open the index.html file, it appears blank. Is there something specific that needs to be done to make it work, ...

What is the best way to position two divs side by side at the bottom of the

When trying to align my second div with the first one, I face an issue. If the text above the first blue box is longer, the second div appears to be outside the line of the box. Changing the relative position doesn't solve this problem either. When th ...

What methods are available for altering state in Server Actions in NextJS 13?

Struggling to Implement State Change in NextJS 13 Server Actions I have encountered a challenge with altering state within the execution of server actions in NextJS 13. The scenario involves an actions.ts file located at the root of the app directory. Cur ...

Guide on merging non-modular JavaScript files into a single file with webpack

I am trying to bundle a non-modular JS file that uses jQuery and registers a method on $.fn. This JS must be placed behind jQuery after bundling. Here is an example of the structure of this JS file: (function($){ $.fn.splitPane = ... }(JQuery) If y ...

How to show specific images by clicking on particular items in Ionic 3 on a separate page

Can someone assist me with displaying specific images from a slider gallery? I am struggling to figure out how to show only the January edition when clicking on it in eighteen.html, while hiding the February and March editions. It has been 2 days of unsucc ...

From JSON object to HTML table: converting structured data into a user

I'm currently facing a challenge when trying to iterate through JSON data retrieved from an API and display it in an HTML table. After parsing the original JSON, I accessed its elements as follows: JSON (data retrieved from API): {"PrekesID" ...