{"id":28088,"date":"2022-08-10T11:00:30","date_gmt":"2022-08-10T11:00:30","guid":{"rendered":"http:\/\/record-videos-with-mediastream-recording-api"},"modified":"2025-02-16T03:02:17","modified_gmt":"2025-02-16T11:02:17","slug":"record-videos-with-mediastream-recording-api","status":"publish","type":"post","link":"https:\/\/cloudinary.com\/blog\/guest_post\/record-videos-with-mediastream-recording-api\/","title":{"rendered":"Record Videos with MediaStream Recording API"},"content":{"rendered":"<div class=\"wp-block-cloudinary-markdown \"><p>The MediaStream Recording API  makes it possible to capture data generated by a <a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/API\/MediaStream\">MediaStream<\/a> or <a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/API\/HTMLMediaElement\">HTMLMediaElement<\/a> object for analysis, processing, or saving to disk.\nThe API has just one interface called <code>MediaRecorder<\/code>. MediaRecorder gets the media data from a <code>MediaStream<\/code> and delivers it for processing. In this post, we\u2019ll also learn the process of recording a stream.\nFor better context, Nimbus, a google chrome extension makes use of the MediaStream Recording API to record video with audio and save the recording file to disk.\nIn this post, we will be building an application that allows us record videos with JavaScript and MediaStream Recording API.<\/p>\n<h2>Pre-requisites<\/h2>\n<p>To flow along with this tutorial, you\u2019ll need to have the following<\/p>\n<ul>\n<li>JavaScript and React Knowledge<\/li>\n<li>A code editor (preferably VS Code)<\/li>\n<li>Live Server extension on your code editor<\/li>\n<\/ul>\n<p>The complete code and demo is on <a href=\"https:\/\/codesandbox.io\/embed\/beautiful-ptolemy-y9jjj2?fontsize=14&amp;hidenavigation=1&amp;theme=dark\">Codesandbox<\/a>.<\/p>\n<\/div>\n  \n  <div class=\"wp-block-cloudinary-code-sandbox \">\n    <iframe\n      src=\"https:\/\/codesandbox.io\/embed\/beautiful-ptolemy-y9jjj2?theme=dark&amp;codemirror=1&amp;highlights=&amp;editorsize=50&amp;fontsize=14&amp;expanddevtools=0&amp;hidedevtools=0&amp;eslint=0&amp;forcerefresh=0&amp;hidenavigation=0&amp;initialpath=%2F&amp;module=&amp;moduleview=0&amp;previewwindow=&amp;view=&amp;runonclick=1\"\n      height=\"500\"\n      style=\"width: 100%;\"\n      title=\"beautiful-ptolemy-y9jjj2?\"\n      loading=\"lazy\"\n      allow=\"accelerometer; ambient-light-sensor; camera; encrypted-media; geolocation; gyroscope; hid; microphone; midi; payment; usb; vr; xr-spatial-tracking\"\n      sandbox=\"allow-forms allow-modals allow-popups allow-presentation allow-same-origin allow-scripts\"\n    ><\/iframe>\n  <\/div>\n\n  <div class=\"wp-block-cloudinary-markdown \"><h2>Building the Media Recording app with JavaScript<\/h2>\n<p>Let\u2019s start by creating a folder, i\u2019ll call mine <strong>Video Recoding App.<\/strong> Open the folder on your code editor and create an <code>index.html<\/code>, <code>main.js<\/code> and <code>styles.css<\/code> files. Add these lines of code to the <code>index.html<\/code>:<\/p>\n<pre class=\"js-syntax-highlighted\" aria-describedby=\"shcb-language-1\" data-shcb-language-name=\"HTML, XML\" data-shcb-language-slug=\"xml\"><span><code class=\"hljs language-xml shcb-wrap-lines\">\/\/index.html\n<span class=\"hljs-meta\">&lt;!DOCTYPE <span class=\"hljs-meta-keyword\">html<\/span>&gt;<\/span>\n<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">html<\/span> <span class=\"hljs-attr\">lang<\/span>=<span class=\"hljs-string\">\"en\"<\/span>&gt;<\/span>\n    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">head<\/span>&gt;<\/span>\n        <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">meta<\/span> <span class=\"hljs-attr\">charset<\/span>=<span class=\"hljs-string\">\"UTF-8\"<\/span>&gt;<\/span>\n        <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">meta<\/span> <span class=\"hljs-attr\">http-equiv<\/span>=<span class=\"hljs-string\">\"X-UA-Compatible\"<\/span> <span class=\"hljs-attr\">content<\/span>=<span class=\"hljs-string\">\"IE=edge\"<\/span>&gt;<\/span>\n        <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">meta<\/span> <span class=\"hljs-attr\">name<\/span>=<span class=\"hljs-string\">\"viewport\"<\/span> <span class=\"hljs-attr\">content<\/span>=<span class=\"hljs-string\">\"width=device-width, initial-scale=1.0\"<\/span>&gt;<\/span>\n        <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">title<\/span>&gt;<\/span> Video Recording app<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">title<\/span>&gt;<\/span>\n        <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">link<\/span> <span class=\"hljs-attr\">rel<\/span>=<span class=\"hljs-string\">\"stylesheet\"<\/span> <span class=\"hljs-attr\">href<\/span>=<span class=\"hljs-string\">\"styles.css\"<\/span>&gt;<\/span>\n    <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">head<\/span>&gt;<\/span>\n    \n    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">body<\/span>&gt;<\/span>\n        <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">class<\/span>=<span class=\"hljs-string\">\"container\"<\/span>&gt;<\/span>\n            <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">class<\/span>=<span class=\"hljs-string\">\"preview\"<\/span>&gt;<\/span>\n                <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">h2<\/span>&gt;<\/span>Preview<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">h2<\/span>&gt;<\/span>\n                <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">video<\/span> <span class=\"hljs-attr\">id<\/span>=<span class=\"hljs-string\">\"preview\"<\/span> <span class=\"hljs-attr\">width<\/span>=<span class=\"hljs-string\">\"160\"<\/span> <span class=\"hljs-attr\">height<\/span>=<span class=\"hljs-string\">\"120\"<\/span> <span class=\"hljs-attr\">autoplay<\/span> <span class=\"hljs-attr\">muted<\/span>&gt;<\/span><span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">video<\/span>&gt;<\/span>\n                <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">button<\/span> <span class=\"hljs-attr\">id<\/span>=<span class=\"hljs-string\">\"startButton\"<\/span> <span class=\"hljs-attr\">class<\/span>=<span class=\"hljs-string\">\"button\"<\/span>&gt;<\/span>\n                    Start\n                <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">button<\/span>&gt;<\/span>\n            <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n            <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">class<\/span>=<span class=\"hljs-string\">\"recorded\"<\/span>&gt;<\/span>\n                <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">h2<\/span>&gt;<\/span>Recording<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">h2<\/span>&gt;<\/span>\n                <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">video<\/span> <span class=\"hljs-attr\">id<\/span>=<span class=\"hljs-string\">\"recording\"<\/span> <span class=\"hljs-attr\">width<\/span>=<span class=\"hljs-string\">\"160\"<\/span> <span class=\"hljs-attr\">height<\/span>=<span class=\"hljs-string\">\"120\"<\/span> <span class=\"hljs-attr\">controls<\/span>&gt;<\/span><span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">video<\/span>&gt;<\/span>\n                <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">button<\/span> <span class=\"hljs-attr\">id<\/span>=<span class=\"hljs-string\">\"stopButton\"<\/span> <span class=\"hljs-attr\">class<\/span>=<span class=\"hljs-string\">\"button\"<\/span>&gt;<\/span>\n                    Stop\n                <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">button<\/span>&gt;<\/span>\n            <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n            <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">button<\/span> <span class=\"hljs-attr\">id<\/span>=<span class=\"hljs-string\">\"downloadButton\"<\/span> <span class=\"hljs-attr\">class<\/span>=<span class=\"hljs-string\">\"button\"<\/span>&gt;<\/span>\n                Download\n            <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">button<\/span>&gt;<\/span>\n        <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n        <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">id<\/span>=<span class=\"hljs-string\">\"log\"<\/span>&gt;<\/span><span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n    \n        <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">script<\/span> <span class=\"hljs-attr\">src<\/span>=<span class=\"hljs-string\">\"main.js\"<\/span>&gt;<\/span><span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">script<\/span>&gt;<\/span>\n    <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">body<\/span>&gt;<\/span>\n<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">html<\/span>&gt;<\/span>\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-1\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">HTML, XML<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">xml<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n<p>Here, we have a container with two divs. The first div has the video tag with autoplay and muted set to true. There\u2019s also a start button with an id of <code>startButton<\/code>. When you click on the button, it\u2019ll request camera and audio access, then start recording with the video element. We\u2019ll write the logic for this soon. The second div tag also has a video element but this video will be the recorded video. The controls attribute enable the video with all the media controls including play, pause, volume,  e.t.c. We will use the stop button to stop recording the video.\nWe also have a div with id of <code>log<\/code>, this will log information in the page.<\/p>\n<p>Let\u2019s go ahead and write the logic. Add these lines of code to your <code>main.js<\/code><\/p>\n<pre class=\"js-syntax-highlighted\" aria-describedby=\"shcb-language-2\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript shcb-wrap-lines\"><span class=\"hljs-comment\">\/\/main.js<\/span>\n<span class=\"hljs-keyword\">const<\/span> preview = <span class=\"hljs-built_in\">document<\/span>.getElementById(<span class=\"hljs-string\">\"preview\"<\/span>);\n<span class=\"hljs-keyword\">const<\/span> recording = <span class=\"hljs-built_in\">document<\/span>.getElementById(<span class=\"hljs-string\">\"recording\"<\/span>);\n<span class=\"hljs-keyword\">const<\/span> startButton = <span class=\"hljs-built_in\">document<\/span>.getElementById(<span class=\"hljs-string\">\"startButton\"<\/span>);\n<span class=\"hljs-keyword\">const<\/span> stopButton = <span class=\"hljs-built_in\">document<\/span>.getElementById(<span class=\"hljs-string\">\"stopButton\"<\/span>);\n<span class=\"hljs-keyword\">const<\/span> downloadButton = <span class=\"hljs-built_in\">document<\/span>.getElementById(<span class=\"hljs-string\">\"downloadButton\"<\/span>);\n<span class=\"hljs-keyword\">const<\/span> logElement = <span class=\"hljs-built_in\">document<\/span>.getElementById(<span class=\"hljs-string\">\"log\"<\/span>);\n<span class=\"hljs-keyword\">const<\/span> recordingTimeMS = <span class=\"hljs-number\">10000<\/span>;\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-2\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n<p>Here, we define some global variables. Most worthy to take note of is the <code>recordingTimeMS<\/code> variable, this is the recording time we set for our videos, so when it gets to 10seconds, the video automatically stops recording.\nAdd these lines of code:<\/p>\n<pre class=\"js-syntax-highlighted\" aria-describedby=\"shcb-language-3\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript shcb-wrap-lines\"><span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">log<\/span>(<span class=\"hljs-params\">msg<\/span>) <\/span>{\n    logElement.innerHTML += msg + <span class=\"hljs-string\">\"\\n\"<\/span>;\n}\n\n<span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">wait<\/span>(<span class=\"hljs-params\">delayInMS<\/span>) <\/span>{\n    <span class=\"hljs-keyword\">return<\/span> <span class=\"hljs-keyword\">new<\/span> <span class=\"hljs-built_in\">Promise<\/span>(<span class=\"hljs-function\"><span class=\"hljs-params\">resolve<\/span> =&gt;<\/span> setTimeout(resolve, delayInMS));\n}\n\n<span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">formatBytes<\/span>(<span class=\"hljs-params\">bytes, decimals = <span class=\"hljs-number\">2<\/span><\/span>) <\/span>{\n    <span class=\"hljs-keyword\">if<\/span> (bytes === <span class=\"hljs-number\">0<\/span>) <span class=\"hljs-keyword\">return<\/span> <span class=\"hljs-string\">'0 Bytes'<\/span>;\n        <span class=\"hljs-keyword\">const<\/span> k = <span class=\"hljs-number\">1024<\/span>;\n        <span class=\"hljs-keyword\">const<\/span> dm = decimals &lt; <span class=\"hljs-number\">0<\/span> ? <span class=\"hljs-number\">0<\/span> : decimals;\n        <span class=\"hljs-keyword\">const<\/span> sizes = &#91;<span class=\"hljs-string\">'Bytes'<\/span>, <span class=\"hljs-string\">'KB'<\/span>, <span class=\"hljs-string\">'MB'<\/span>, <span class=\"hljs-string\">'GB'<\/span>, <span class=\"hljs-string\">'TB'<\/span>, <span class=\"hljs-string\">'PB'<\/span>, <span class=\"hljs-string\">'EB'<\/span>, <span class=\"hljs-string\">'ZB'<\/span>, <span class=\"hljs-string\">'YB'<\/span>];\n        <span class=\"hljs-keyword\">const<\/span> i = <span class=\"hljs-built_in\">Math<\/span>.floor(<span class=\"hljs-built_in\">Math<\/span>.log(bytes) \/ <span class=\"hljs-built_in\">Math<\/span>.log(k));\n        <span class=\"hljs-keyword\">return<\/span> <span class=\"hljs-built_in\">parseFloat<\/span>((bytes \/ <span class=\"hljs-built_in\">Math<\/span>.pow(k, i)).toFixed(dm)) + <span class=\"hljs-string\">' '<\/span> + sizes&#91;i];\n}\n    \n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-3\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n<p>Here, we create 3 utility functions. First the log function, will log information to the webpage. The wait function will return a Promise which resolves once the specified number of milliseconds have elapsed. Finally, the formatBytes will convert the recorded size in bytes to kilobytes, megabytes, e.t.c.<\/p>\n<p>Let\u2019s move on to write the function that starts recording a video. Add these lines of code:<\/p>\n<pre class=\"js-syntax-highlighted\" aria-describedby=\"shcb-language-4\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript shcb-wrap-lines\"><span class=\"hljs-comment\">\/\/main.js<\/span>\n\n<span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">startRecording<\/span>(<span class=\"hljs-params\">stream, lengthInMS<\/span>) <\/span>{\n    <span class=\"hljs-keyword\">let<\/span> recorder = <span class=\"hljs-keyword\">new<\/span> MediaRecorder(stream);\n    <span class=\"hljs-keyword\">let<\/span> data = &#91;];\n    recorder.ondataavailable = <span class=\"hljs-function\"><span class=\"hljs-params\">event<\/span> =&gt;<\/span> data.push(event.data);\n    recorder.start();\n    log(recorder.state + <span class=\"hljs-string\">\" for \"<\/span> + (lengthInMS \/ <span class=\"hljs-number\">1000<\/span>) + <span class=\"hljs-string\">\" seconds...\"<\/span>);\n    <span class=\"hljs-keyword\">let<\/span> stopped = <span class=\"hljs-keyword\">new<\/span> <span class=\"hljs-built_in\">Promise<\/span>(<span class=\"hljs-function\">(<span class=\"hljs-params\">resolve, reject<\/span>) =&gt;<\/span> {\n        recorder.onstop = resolve;\n        recorder.onerror = <span class=\"hljs-function\"><span class=\"hljs-params\">event<\/span> =&gt;<\/span> reject(event.name);\n    });\n    \n    <span class=\"hljs-keyword\">let<\/span> recorded = wait(lengthInMS).then(\n        <span class=\"hljs-function\"><span class=\"hljs-params\">()<\/span> =&gt;<\/span> recorder.state == <span class=\"hljs-string\">\"recording\"<\/span> &amp;&amp; recorder.stop()\n    );\n    \n    <span class=\"hljs-keyword\">return<\/span> <span class=\"hljs-built_in\">Promise<\/span>.all(&#91;\n        stopped,\n        recorded\n    ])\n    .then(<span class=\"hljs-function\"><span class=\"hljs-params\">()<\/span> =&gt;<\/span> data);\n}\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-4\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n<p>Here, the <code>startRecording()<\/code> function takes two input parameters: a stream(which represents the MediaStream) to record from <strong>and<\/strong> <strong>t<\/strong>he length in milliseconds of the recording to make. Next, we instantiate the MediaRecorder (remember this is the interface to MediaStream API) that will handle recording the input stream.\nNext, we assign an empty array to the data variable.\nNext, we set the <code>ondataavailable<\/code> event which is fired when the MediaRecorder delivers media data to our application for its use.  It returns a <code>Blob<\/code> object that contains the data. <code>recorder.start()<\/code> starts the recording process.\nNext, we create a new Promise called stopped. The Promise resolves when the <code>onstop<\/code> event is triggered, and it\u2019s rejected when the <code>onerror<\/code> event is called.\nNext, we create another Promise called recorded. It resolves when the assigned number of milliseconds elapses.\nFinally, we create a Promise that is fulfilled when the two Promises <code>stopped<\/code> and <code>recorded<\/code> resolves. On resolution, we output the data.\nBelow, the <code>startRecording()<\/code> function, add these lines of code:<\/p>\n<pre class=\"js-syntax-highlighted\" aria-describedby=\"shcb-language-5\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript shcb-wrap-lines\"><span class=\"hljs-comment\">\/\/main.js<\/span>\n<span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">stop<\/span>(<span class=\"hljs-params\">stream<\/span>) <\/span>{\n    stream.getTracks().forEach(<span class=\"hljs-function\"><span class=\"hljs-params\">track<\/span> =&gt;<\/span> track.stop());\n}\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-5\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n<p>This function stops the input media from recording. If the camera is on, it turns it off too.<\/p>\n<p>When you click on the Start button what happens?  add these lines of code to implement the functionality:<\/p>\n<pre class=\"js-syntax-highlighted\" aria-describedby=\"shcb-language-6\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript shcb-wrap-lines\"><span class=\"hljs-comment\">\/\/main.js<\/span>\n\nstartButton.addEventListener(<span class=\"hljs-string\">\"click\"<\/span>, <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> (<span class=\"hljs-params\"><\/span>) <\/span>{\n    navigator.mediaDevices.getUserMedia({\n        <span class=\"hljs-attr\">video<\/span>: <span class=\"hljs-literal\">true<\/span>,\n        <span class=\"hljs-attr\">audio<\/span>: <span class=\"hljs-literal\">true<\/span>\n    }).then(<span class=\"hljs-function\"><span class=\"hljs-params\">stream<\/span> =&gt;<\/span> {\n        preview.srcObject = stream;\n        downloadButton.href = stream;\n        preview.captureStream = preview.captureStream || preview.mozCaptureStream;\n        <span class=\"hljs-keyword\">return<\/span> <span class=\"hljs-keyword\">new<\/span> <span class=\"hljs-built_in\">Promise<\/span>(<span class=\"hljs-function\"><span class=\"hljs-params\">resolve<\/span> =&gt;<\/span> preview.onplaying = resolve);\n    }).then(<span class=\"hljs-function\"><span class=\"hljs-params\">()<\/span> =&gt;<\/span> startRecording(preview.captureStream(), recordingTimeMS))\n        .then(<span class=\"hljs-function\"><span class=\"hljs-params\">recordedChunks<\/span> =&gt;<\/span> {\n            <span class=\"hljs-keyword\">let<\/span> recordedBlob = <span class=\"hljs-keyword\">new<\/span> Blob(recordedChunks, { <span class=\"hljs-attr\">type<\/span>: <span class=\"hljs-string\">\"video\/webm\"<\/span> });\n            recording.src = URL.createObjectURL(recordedBlob);\n            downloadButton.href = recording.src;\n            downloadButton.download = <span class=\"hljs-string\">\"RecordedVideo.webm\"<\/span>;\n            log(<span class=\"hljs-string\">`Your video is <span class=\"hljs-subst\">${formatBytes(recordedBlob.size)}<\/span>`<\/span>);\n            <span class=\"hljs-built_in\">console<\/span>.log(recordedBlob.size)\n        })\n        .catch(log);\n}, <span class=\"hljs-literal\">false<\/span>);\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-6\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n<p>Let\u2019s go through the moving parts of this code snippet.\nFirst, we get permissions from the user to have access to audio, and video using <code>naviagator.mediaDevices.getUserMedia<\/code>.\nThe <code>getUserMedia<\/code> returns a Promise, on resolution, we assign the input <code>stream<\/code> to preview <code>&lt;video&gt;<\/code>  <code>srcObject<\/code>. This allows the video captured by the camera to be displayed in the <code>&lt;video id=``&quot;``preview``&quot;``&gt;<\/code> box.\nAfter that, a new Promise resolves when the preview video starts to play.\nWhen the video starts to play, we invoke the <code>startsRecording()<\/code> and pass two arguments; the preview video stream as the source media to be recorded, and <code>recordingTimeMS<\/code> as the number of milliseconds of media to record.\nNext, we merge the <code>recordedChunks<\/code> (which is the array of media data Blobs) with <code>mimeType<\/code> of <code>video\/webm<\/code>. We then set the <code>src<\/code> attribute of the recorded video to <code>URL.createObjectURL<\/code> and pass the <code>recordedBlob<\/code> as argument. <code>[URL.createObjectURL()](https:\/\/docs.w3cub.com\/dom\/url\/createobjecturl)<\/code> is used to create an URL that references the blob. We assign the newly created URL to the <code>href<\/code> attribute of the download button.\nThen, we set the <code>download<\/code> attribute of the download button to download a <code>RecordedVideo.webm<\/code> file whenever we click on the button.<\/p>\n<p>Finally, let\u2019s add the functionality for when we click on the stop button. Add these lines of code:<\/p>\n<pre class=\"js-syntax-highlighted\" aria-describedby=\"shcb-language-7\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript shcb-wrap-lines\"><span class=\"hljs-comment\">\/\/main.js<\/span>\nstopButton.addEventListener(<span class=\"hljs-string\">\"click\"<\/span>, <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> (<span class=\"hljs-params\"><\/span>) <\/span>{\n    stop(preview.srcObject);\n}, <span class=\"hljs-literal\">false<\/span>);\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-7\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n<p>Go ahead to add these lines of css code in your <code>styles.css<\/code><\/p>\n<pre class=\"js-syntax-highlighted\" aria-describedby=\"shcb-language-8\" data-shcb-language-name=\"PHP\" data-shcb-language-slug=\"php\"><span><code class=\"hljs language-php shcb-wrap-lines\"><span class=\"hljs-comment\">\/\/styles.css<\/span>\n@import url(<span class=\"hljs-string\">'https:\/\/fonts.googleapis.com\/css2?family=Karla:wght@300;400;500&amp;display=swap'<\/span>);\n    \nbody {\n    width: <span class=\"hljs-number\">1200<\/span>px;\n    margin: auto;\n    font-family: <span class=\"hljs-string\">'Karla'<\/span>, sans-serif;\n    background: <span class=\"hljs-comment\">#9ebded;<\/span>\n}\n    \n.container {\n    display: grid;\n    grid-template-columns: <span class=\"hljs-number\">1<\/span>fr <span class=\"hljs-number\">1<\/span>fr;\n    grid-gap: <span class=\"hljs-number\">2<\/span>rem;\n    place-content: center;\n    height: <span class=\"hljs-number\">100<\/span>vh;\n    background: <span class=\"hljs-comment\">#9ebded;<\/span>\n}\n\n.preview, .recorded {\n    margin: auto;\n}\n\nvideo {\n    width: <span class=\"hljs-number\">100<\/span>%;\n    height: <span class=\"hljs-number\">100<\/span>%;\n}\n\nbutton, .button {\n    padding: <span class=\"hljs-number\">0.5<\/span>rem <span class=\"hljs-number\">3<\/span>rem;\n    background: <span class=\"hljs-comment\">#fff;<\/span>\n    border: none;\n    font-size: <span class=\"hljs-number\">1<\/span>rem;\n    cursor: pointer;\n}\n\n<span class=\"hljs-comment\">#startButton, h2 {<\/span>\n    margin-left: <span class=\"hljs-number\">2<\/span>rem\n}\n\n<span class=\"hljs-comment\">#downloadButton {<\/span>\n    margin: auto;\n}\n\n<span class=\"hljs-comment\">#log {<\/span>\n    color: <span class=\"hljs-comment\">#fff;<\/span>\n    margin-bottom: <span class=\"hljs-number\">3<\/span>rem;\n}\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-8\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">PHP<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">php<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n<p>Awesome!\nNow start your app by running Live Server on your code editor. Navigate to your browser, you should have something like this:<\/p>\n<p><a href=\"https:\/\/www.dropbox.com\/s\/29tmoy3shhxuk0m\/videoRecord.webm?dl=0\">https:\/\/www.dropbox.com\/s\/29tmoy3shhxuk0m\/videoRecord.webm?dl=0<\/a><\/p>\n<p>I had already given permission to audio and video so i didn\u2019t get the prompt in this video.<\/p>\n<h2>Building the Media Recording app with React<\/h2>\n<p>Let\u2019s go ahead and implement the recording app with React. We will be using a React library <code>[**use-screen-recorder**](https:\/\/github.com\/ishan-chhabra\/use-screen-recorder)<\/code> that wraps the MediaStream Recording API nicely into a Hook.<\/p>\n<pre class=\"js-syntax-highlighted\" aria-describedby=\"shcb-language-9\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript shcb-wrap-lines\"><span class=\"hljs-comment\">\/\/javascript<\/span>\n    \n<span class=\"hljs-keyword\">import<\/span> * <span class=\"hljs-keyword\">as<\/span> React <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\"react\"<\/span>;\n<span class=\"hljs-keyword\">import<\/span> useScreenRecorder <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\"use-screen-recorder\"<\/span>;\n\n<span class=\"hljs-keyword\">export<\/span> <span class=\"hljs-keyword\">default<\/span> <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">MediaController<\/span>(<span class=\"hljs-params\"><\/span>) <\/span>{\n    <span class=\"hljs-keyword\">const<\/span> {\n        blobUrl,\n        pauseRecording,\n        resetRecording,\n        resumeRecording,\n        startRecording,\n        status,\n        stopRecording,\n    } = useScreenRecorder();\n    \n    <span class=\"hljs-keyword\">return<\/span> (\n        <span class=\"xml\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n            <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">video<\/span> <span class=\"hljs-attr\">src<\/span>=<span class=\"hljs-string\">{blobUrl}<\/span> <span class=\"hljs-attr\">controls<\/span> <span class=\"hljs-attr\">autoplay<\/span> \/&gt;<\/span>\n            <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">small<\/span>&gt;<\/span>Status: {status}<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">small<\/span>&gt;<\/span>\n            <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">button<\/span> <span class=\"hljs-attr\">onClick<\/span>=<span class=\"hljs-string\">{startRecording}<\/span>&gt;<\/span>Start Recording<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">button<\/span>&gt;<\/span>\n            <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">button<\/span> <span class=\"hljs-attr\">onClick<\/span>=<span class=\"hljs-string\">{stopRecording}<\/span>&gt;<\/span>Stop Recording<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">button<\/span>&gt;<\/span>\n            <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">button<\/span> <span class=\"hljs-attr\">onClick<\/span>=<span class=\"hljs-string\">{pauseRecording}<\/span>&gt;<\/span>Pause Recording<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">button<\/span>&gt;<\/span>\n            <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">button<\/span> <span class=\"hljs-attr\">onClick<\/span>=<span class=\"hljs-string\">{resumeRecording}<\/span>&gt;<\/span>Resume Recording<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">button<\/span>&gt;<\/span>\n            <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">button<\/span> <span class=\"hljs-attr\">onClick<\/span>=<span class=\"hljs-string\">{resetRecording}<\/span>&gt;<\/span>Reset Recording<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">button<\/span>&gt;<\/span>\n        <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span><\/span>\n    );\n};\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-9\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n<p>Check this <a href=\"https:\/\/github.com\/ishan-chhabra\/use-screen-recorder\/tree\/main\/example\">example<\/a> to see how to use this hook with React.<\/p>\n<h2>Conclusion<\/h2>\n<p>In this tutorial, we learned about the MediaStream Recording API,  and we went on to build a JavaScript media recorder application using the API. We also got to see how to implement similar functionality in React using the <code>use-screen-recorder<\/code> hook. I hope you\u2019ve learned something new from this.<\/p>\n<h2>Further Reading<\/h2>\n<ul>\n<li>\n<a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/API\/MediaStream_Recording_API\">MediaStream Recording API MDN<\/a>\n<\/li>\n<li>\n<a href=\"https:\/\/addpipe.com\/media-recorder-api-demo-audio\/\">Recording audio with the MediaStream Recorder API<\/a>\n<\/li>\n<li>\n<a href=\"https:\/\/webrtc.github.io\/samples\/src\/content\/getusermedia\/record\/\">WebRTC samples MediaRecorder<\/a>\n<\/li>\n<li>\n<a href=\"https:\/\/docs.w3cub.com\/dom\/mediastream_recording_api\/recording_a_media_element\"><strong>Recording a media element<\/strong><\/a>\n<\/li>\n<\/ul>\n<p>Happy Coding!<\/p>\n<\/div>","protected":false},"excerpt":{"rendered":"","protected":false},"author":41,"featured_media":28089,"comment_status":"closed","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"_cloudinary_featured_overwrite":false,"footnotes":""},"categories":[1],"tags":[134,177,393,371,303],"class_list":["post-28088","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-uncategorized","tag-guest-post","tag-javascript","tag-streaming","tag-under-review","tag-video"],"acf":[],"yoast_head":"<!-- This site is optimized with the Yoast SEO Premium plugin v25.6 (Yoast SEO v26.9) - https:\/\/yoast.com\/product\/yoast-seo-premium-wordpress\/ -->\n<title>Record Videos with MediaStream Recording API<\/title>\n<meta name=\"description\" content=\"The MediaStream Recording API makes it possible to capture data generated by a `[MediaStream](https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/API\/MediaStream)` or `[HTMLMediaElement](https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/API\/HTMLMediaElement)` object for analysis, processing, or saving to disk.\" \/>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/cloudinary.com\/blog\/guest_post\/record-videos-with-mediastream-recording-api\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Record Videos with MediaStream Recording API\" \/>\n<meta property=\"og:description\" content=\"The MediaStream Recording API makes it possible to capture data generated by a `[MediaStream](https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/API\/MediaStream)` or `[HTMLMediaElement](https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/API\/HTMLMediaElement)` object for analysis, processing, or saving to disk.\" \/>\n<meta property=\"og:url\" content=\"https:\/\/cloudinary.com\/blog\/guest_post\/record-videos-with-mediastream-recording-api\/\" \/>\n<meta property=\"og:site_name\" content=\"Cloudinary Blog\" \/>\n<meta property=\"article:published_time\" content=\"2022-08-10T11:00:30+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2025-02-16T11:02:17+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/v1681922310\/Web_Assets\/blog\/36c8bbe62a71c203777ecf68f08ddd32c8f74252-1725x853-1_2808965c59\/36c8bbe62a71c203777ecf68f08ddd32c8f74252-1725x853-1_2808965c59-png?_i=AA\" \/>\n\t<meta property=\"og:image:width\" content=\"1725\" \/>\n\t<meta property=\"og:image:height\" content=\"853\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/png\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"NewsArticle\",\"@id\":\"https:\/\/cloudinary.com\/blog\/guest_post\/record-videos-with-mediastream-recording-api\/#article\",\"isPartOf\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/guest_post\/record-videos-with-mediastream-recording-api\/\"},\"author\":{\"name\":\"\",\"@id\":\"\"},\"headline\":\"Record Videos with MediaStream Recording API\",\"datePublished\":\"2022-08-10T11:00:30+00:00\",\"dateModified\":\"2025-02-16T11:02:17+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/guest_post\/record-videos-with-mediastream-recording-api\/\"},\"wordCount\":6,\"publisher\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/#organization\"},\"image\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/guest_post\/record-videos-with-mediastream-recording-api\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1681922310\/Web_Assets\/blog\/36c8bbe62a71c203777ecf68f08ddd32c8f74252-1725x853-1_2808965c59\/36c8bbe62a71c203777ecf68f08ddd32c8f74252-1725x853-1_2808965c59.png?_i=AA\",\"keywords\":[\"Guest Post\",\"Javascript\",\"Streaming\",\"Under Review\",\"Video\"],\"inLanguage\":\"en-US\",\"copyrightYear\":\"2022\",\"copyrightHolder\":{\"@id\":\"https:\/\/cloudinary.com\/#organization\"}},{\"@type\":\"WebPage\",\"@id\":\"https:\/\/cloudinary.com\/blog\/guest_post\/record-videos-with-mediastream-recording-api\/\",\"url\":\"https:\/\/cloudinary.com\/blog\/guest_post\/record-videos-with-mediastream-recording-api\/\",\"name\":\"Record Videos with MediaStream Recording API\",\"isPartOf\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/guest_post\/record-videos-with-mediastream-recording-api\/#primaryimage\"},\"image\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/guest_post\/record-videos-with-mediastream-recording-api\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1681922310\/Web_Assets\/blog\/36c8bbe62a71c203777ecf68f08ddd32c8f74252-1725x853-1_2808965c59\/36c8bbe62a71c203777ecf68f08ddd32c8f74252-1725x853-1_2808965c59.png?_i=AA\",\"datePublished\":\"2022-08-10T11:00:30+00:00\",\"dateModified\":\"2025-02-16T11:02:17+00:00\",\"description\":\"The MediaStream Recording API makes it possible to capture data generated by a `[MediaStream](https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/API\/MediaStream)` or `[HTMLMediaElement](https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/API\/HTMLMediaElement)` object for analysis, processing, or saving to disk.\",\"breadcrumb\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/guest_post\/record-videos-with-mediastream-recording-api\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/cloudinary.com\/blog\/guest_post\/record-videos-with-mediastream-recording-api\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/cloudinary.com\/blog\/guest_post\/record-videos-with-mediastream-recording-api\/#primaryimage\",\"url\":\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1681922310\/Web_Assets\/blog\/36c8bbe62a71c203777ecf68f08ddd32c8f74252-1725x853-1_2808965c59\/36c8bbe62a71c203777ecf68f08ddd32c8f74252-1725x853-1_2808965c59.png?_i=AA\",\"contentUrl\":\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1681922310\/Web_Assets\/blog\/36c8bbe62a71c203777ecf68f08ddd32c8f74252-1725x853-1_2808965c59\/36c8bbe62a71c203777ecf68f08ddd32c8f74252-1725x853-1_2808965c59.png?_i=AA\",\"width\":1725,\"height\":853},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/cloudinary.com\/blog\/guest_post\/record-videos-with-mediastream-recording-api\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\/\/cloudinary.com\/blog\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Record Videos with MediaStream Recording API\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\/\/cloudinary.com\/blog\/#website\",\"url\":\"https:\/\/cloudinary.com\/blog\/\",\"name\":\"Cloudinary Blog\",\"description\":\"\",\"publisher\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/#organization\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\/\/cloudinary.com\/blog\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":\"Organization\",\"@id\":\"https:\/\/cloudinary.com\/blog\/#organization\",\"name\":\"Cloudinary Blog\",\"url\":\"https:\/\/cloudinary.com\/blog\/\",\"logo\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/cloudinary.com\/blog\/#\/schema\/logo\/image\/\",\"url\":\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1649718331\/Web_Assets\/blog\/cloudinary_logo_for_white_bg_1937437aa7_19374666c7_193742f877\/cloudinary_logo_for_white_bg_1937437aa7_19374666c7_193742f877.png?_i=AA\",\"contentUrl\":\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1649718331\/Web_Assets\/blog\/cloudinary_logo_for_white_bg_1937437aa7_19374666c7_193742f877\/cloudinary_logo_for_white_bg_1937437aa7_19374666c7_193742f877.png?_i=AA\",\"width\":312,\"height\":60,\"caption\":\"Cloudinary Blog\"},\"image\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/#\/schema\/logo\/image\/\"}},{\"@type\":\"Person\",\"@id\":\"\"}]}<\/script>\n<!-- \/ Yoast SEO Premium plugin. -->","yoast_head_json":{"title":"Record Videos with MediaStream Recording API","description":"The MediaStream Recording API makes it possible to capture data generated by a `[MediaStream](https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/API\/MediaStream)` or `[HTMLMediaElement](https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/API\/HTMLMediaElement)` object for analysis, processing, or saving to disk.","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/cloudinary.com\/blog\/guest_post\/record-videos-with-mediastream-recording-api\/","og_locale":"en_US","og_type":"article","og_title":"Record Videos with MediaStream Recording API","og_description":"The MediaStream Recording API makes it possible to capture data generated by a `[MediaStream](https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/API\/MediaStream)` or `[HTMLMediaElement](https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/API\/HTMLMediaElement)` object for analysis, processing, or saving to disk.","og_url":"https:\/\/cloudinary.com\/blog\/guest_post\/record-videos-with-mediastream-recording-api\/","og_site_name":"Cloudinary Blog","article_published_time":"2022-08-10T11:00:30+00:00","article_modified_time":"2025-02-16T11:02:17+00:00","og_image":[{"width":1725,"height":853,"url":"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/v1681922310\/Web_Assets\/blog\/36c8bbe62a71c203777ecf68f08ddd32c8f74252-1725x853-1_2808965c59\/36c8bbe62a71c203777ecf68f08ddd32c8f74252-1725x853-1_2808965c59-png?_i=AA","type":"image\/png"}],"twitter_card":"summary_large_image","schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"NewsArticle","@id":"https:\/\/cloudinary.com\/blog\/guest_post\/record-videos-with-mediastream-recording-api\/#article","isPartOf":{"@id":"https:\/\/cloudinary.com\/blog\/guest_post\/record-videos-with-mediastream-recording-api\/"},"author":{"name":"","@id":""},"headline":"Record Videos with MediaStream Recording API","datePublished":"2022-08-10T11:00:30+00:00","dateModified":"2025-02-16T11:02:17+00:00","mainEntityOfPage":{"@id":"https:\/\/cloudinary.com\/blog\/guest_post\/record-videos-with-mediastream-recording-api\/"},"wordCount":6,"publisher":{"@id":"https:\/\/cloudinary.com\/blog\/#organization"},"image":{"@id":"https:\/\/cloudinary.com\/blog\/guest_post\/record-videos-with-mediastream-recording-api\/#primaryimage"},"thumbnailUrl":"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1681922310\/Web_Assets\/blog\/36c8bbe62a71c203777ecf68f08ddd32c8f74252-1725x853-1_2808965c59\/36c8bbe62a71c203777ecf68f08ddd32c8f74252-1725x853-1_2808965c59.png?_i=AA","keywords":["Guest Post","Javascript","Streaming","Under Review","Video"],"inLanguage":"en-US","copyrightYear":"2022","copyrightHolder":{"@id":"https:\/\/cloudinary.com\/#organization"}},{"@type":"WebPage","@id":"https:\/\/cloudinary.com\/blog\/guest_post\/record-videos-with-mediastream-recording-api\/","url":"https:\/\/cloudinary.com\/blog\/guest_post\/record-videos-with-mediastream-recording-api\/","name":"Record Videos with MediaStream Recording API","isPartOf":{"@id":"https:\/\/cloudinary.com\/blog\/#website"},"primaryImageOfPage":{"@id":"https:\/\/cloudinary.com\/blog\/guest_post\/record-videos-with-mediastream-recording-api\/#primaryimage"},"image":{"@id":"https:\/\/cloudinary.com\/blog\/guest_post\/record-videos-with-mediastream-recording-api\/#primaryimage"},"thumbnailUrl":"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1681922310\/Web_Assets\/blog\/36c8bbe62a71c203777ecf68f08ddd32c8f74252-1725x853-1_2808965c59\/36c8bbe62a71c203777ecf68f08ddd32c8f74252-1725x853-1_2808965c59.png?_i=AA","datePublished":"2022-08-10T11:00:30+00:00","dateModified":"2025-02-16T11:02:17+00:00","description":"The MediaStream Recording API makes it possible to capture data generated by a `[MediaStream](https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/API\/MediaStream)` or `[HTMLMediaElement](https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/API\/HTMLMediaElement)` object for analysis, processing, or saving to disk.","breadcrumb":{"@id":"https:\/\/cloudinary.com\/blog\/guest_post\/record-videos-with-mediastream-recording-api\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/cloudinary.com\/blog\/guest_post\/record-videos-with-mediastream-recording-api\/"]}]},{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/cloudinary.com\/blog\/guest_post\/record-videos-with-mediastream-recording-api\/#primaryimage","url":"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1681922310\/Web_Assets\/blog\/36c8bbe62a71c203777ecf68f08ddd32c8f74252-1725x853-1_2808965c59\/36c8bbe62a71c203777ecf68f08ddd32c8f74252-1725x853-1_2808965c59.png?_i=AA","contentUrl":"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1681922310\/Web_Assets\/blog\/36c8bbe62a71c203777ecf68f08ddd32c8f74252-1725x853-1_2808965c59\/36c8bbe62a71c203777ecf68f08ddd32c8f74252-1725x853-1_2808965c59.png?_i=AA","width":1725,"height":853},{"@type":"BreadcrumbList","@id":"https:\/\/cloudinary.com\/blog\/guest_post\/record-videos-with-mediastream-recording-api\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/cloudinary.com\/blog\/"},{"@type":"ListItem","position":2,"name":"Record Videos with MediaStream Recording API"}]},{"@type":"WebSite","@id":"https:\/\/cloudinary.com\/blog\/#website","url":"https:\/\/cloudinary.com\/blog\/","name":"Cloudinary Blog","description":"","publisher":{"@id":"https:\/\/cloudinary.com\/blog\/#organization"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/cloudinary.com\/blog\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":"Organization","@id":"https:\/\/cloudinary.com\/blog\/#organization","name":"Cloudinary Blog","url":"https:\/\/cloudinary.com\/blog\/","logo":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/cloudinary.com\/blog\/#\/schema\/logo\/image\/","url":"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1649718331\/Web_Assets\/blog\/cloudinary_logo_for_white_bg_1937437aa7_19374666c7_193742f877\/cloudinary_logo_for_white_bg_1937437aa7_19374666c7_193742f877.png?_i=AA","contentUrl":"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1649718331\/Web_Assets\/blog\/cloudinary_logo_for_white_bg_1937437aa7_19374666c7_193742f877\/cloudinary_logo_for_white_bg_1937437aa7_19374666c7_193742f877.png?_i=AA","width":312,"height":60,"caption":"Cloudinary Blog"},"image":{"@id":"https:\/\/cloudinary.com\/blog\/#\/schema\/logo\/image\/"}},{"@type":"Person","@id":""}]}},"parsely":{"version":"1.1.0","canonical_url":"https:\/\/cloudinary.com\/blog\/guest_post\/record-videos-with-mediastream-recording-api\/","smart_links":{"inbound":0,"outbound":0},"traffic_boost_suggestions_count":0,"meta":{"@context":"https:\/\/schema.org","@type":"NewsArticle","headline":"Record Videos with MediaStream Recording API","url":"https:\/\/cloudinary.com\/blog\/guest_post\/record-videos-with-mediastream-recording-api\/","mainEntityOfPage":{"@type":"WebPage","@id":"https:\/\/cloudinary.com\/blog\/guest_post\/record-videos-with-mediastream-recording-api\/"},"thumbnailUrl":"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1681922310\/Web_Assets\/blog\/36c8bbe62a71c203777ecf68f08ddd32c8f74252-1725x853-1_2808965c59\/36c8bbe62a71c203777ecf68f08ddd32c8f74252-1725x853-1_2808965c59.png?_i=AA&w=150&h=150&crop=1","image":{"@type":"ImageObject","url":"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1681922310\/Web_Assets\/blog\/36c8bbe62a71c203777ecf68f08ddd32c8f74252-1725x853-1_2808965c59\/36c8bbe62a71c203777ecf68f08ddd32c8f74252-1725x853-1_2808965c59.png?_i=AA"},"articleSection":"Uncategorized","author":[],"creator":[],"publisher":{"@type":"Organization","name":"Cloudinary Blog","logo":"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/v1649718331\/Web_Assets\/blog\/cloudinary_logo_for_white_bg_1937437aa7_19374666c7_193742f877\/cloudinary_logo_for_white_bg_1937437aa7_19374666c7_193742f877.png?_i=AA"},"keywords":["guest post","javascript","streaming","under review","video"],"dateCreated":"2022-08-10T11:00:30Z","datePublished":"2022-08-10T11:00:30Z","dateModified":"2025-02-16T11:02:17Z"},"rendered":"<meta name=\"parsely-title\" content=\"Record Videos with MediaStream Recording API\" \/>\n<meta name=\"parsely-link\" content=\"https:\/\/cloudinary.com\/blog\/guest_post\/record-videos-with-mediastream-recording-api\/\" \/>\n<meta name=\"parsely-type\" content=\"post\" \/>\n<meta name=\"parsely-image-url\" content=\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1681922310\/Web_Assets\/blog\/36c8bbe62a71c203777ecf68f08ddd32c8f74252-1725x853-1_2808965c59\/36c8bbe62a71c203777ecf68f08ddd32c8f74252-1725x853-1_2808965c59.png?_i=AA&w=150&amp;h=150&amp;crop=1\" \/>\n<meta name=\"parsely-pub-date\" content=\"2022-08-10T11:00:30Z\" \/>\n<meta name=\"parsely-section\" content=\"Uncategorized\" \/>\n<meta name=\"parsely-tags\" content=\"guest post,javascript,streaming,under review,video\" \/>","tracker_url":"https:\/\/cdn.parsely.com\/keys\/cloudinary.com\/p.js"},"jetpack_featured_media_url":"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1681922310\/Web_Assets\/blog\/36c8bbe62a71c203777ecf68f08ddd32c8f74252-1725x853-1_2808965c59\/36c8bbe62a71c203777ecf68f08ddd32c8f74252-1725x853-1_2808965c59.png?_i=AA","_links":{"self":[{"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/posts\/28088","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/users\/41"}],"replies":[{"embeddable":true,"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/comments?post=28088"}],"version-history":[{"count":1,"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/posts\/28088\/revisions"}],"predecessor-version":[{"id":36833,"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/posts\/28088\/revisions\/36833"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/media\/28089"}],"wp:attachment":[{"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/media?parent=28088"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/categories?post=28088"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/tags?post=28088"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}