Skip to content

Instantly share code, notes, and snippets.

@baptx
Last active April 26, 2024 08:21
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save baptx/8a1549d91996a37378faca159b3adc17 to your computer and use it in GitHub Desktop.
Save baptx/8a1549d91996a37378faca159b3adc17 to your computer and use it in GitHub Desktop.
WebRTC and video blob record (compatible with DRM and any videos like Facebook / Instagram stories)
/* Customized code to allow WebRTC record on existing page or HTML5 video blob download (based on https://github.com/webrtc/samples/blob/409d5631f38f2bdc4dafb5275d1bc77738fbb1ba/src/content/getusermedia/record/js/main.js)
* Recording will start when executing this script and you will have to execute manually startDownload() function when needed in web console with or without stopRecording()
* Recording should ideally start before playing video manually and in case controls are hidden, you can record from the start with a command like document.getElementsByTagName("video")[0].currentTime = 0
* By default, the script targets the first video element document.getElementsByTagName("video")[0] but you can change the code if needed.
*/
// If there is an error due to DRM, capture the stream by executing the following 2 lines before playing / loading the video:
var video = document.getElementsByTagName("video")[0];
var stream = video.captureStream ? video.captureStream() : video.mozCaptureStream();
var mediaSource = new MediaSource();
mediaSource.addEventListener('sourceopen', handleSourceOpen, false);
var mediaRecorder;
var recordedBlobs;
var sourceBuffer;
function startDownload() {
var blob = new Blob(recordedBlobs, {type: 'video/webm'});
var url = window.URL.createObjectURL(blob);
var a = document.createElement('a');
a.style.display = 'none';
a.href = url;
a.download = 'test.webm';
document.body.appendChild(a);
a.click();
setTimeout(() => {
document.body.removeChild(a);
window.URL.revokeObjectURL(url);
}, 100);
}
function handleSourceOpen(event) {
console.log('MediaSource opened');
sourceBuffer = mediaSource.addSourceBuffer('video/webm; codecs="vp8"');
console.log('Source buffer: ', sourceBuffer);
}
function handleDataAvailable(event) {
console.log('handleDataAvailable', event);
if (event.data && event.data.size > 0) {
recordedBlobs.push(event.data);
}
}
function startRecording() {
recordedBlobs = [];
try {
mediaRecorder = new MediaRecorder(stream);
} catch (e) {
console.error('Exception while creating MediaRecorder:', e);
return;
}
mediaRecorder.onstop = (event) => {
console.log('Recorder stopped: ', event);
console.log('Recorded Blobs: ', recordedBlobs);
};
mediaRecorder.ondataavailable = handleDataAvailable;
mediaRecorder.start(1000); // collect 1000ms of data (to be able to download every second even if recording is not stopped)
console.log('MediaRecorder started', mediaRecorder);
}
function stopRecording() {
mediaRecorder.stop();
}
startRecording();
@dreamflasher
Copy link

Thank you @baptx ! Is this still working? I do the following steps:

  1. navigate to https://integration.widevine.com/player
  2. press play button
  3. insert document.getElementsByTagName("video")[0].currentTime = 0; before startRecording(); in your script
  4. run your script in FF console
  5. observe NS_ERROR_FAILURE

Am I doing something wrong or do we need a patched browser by now?

@baptx
Copy link
Author

baptx commented Oct 16, 2023

@dreamflasher Hi, yes I tested today and it is still working with the latest Firefox 118.0.2 64-bit Linux version from Mozilla website. I talked about the DRM issue and workaround on BrowserWorks/Waterfox#1895 and added more details in the comment BrowserWorks/Waterfox#1895 (comment).
So basically, what you have to do with a video using DRM on https://integration.widevine.com/player is:

  • Capture the stream by executing this code before playing the video:
var video = document.getElementsByTagName("video")[0];
var stream = video.captureStream ? video.captureStream() : video.mozCaptureStream();
  • Load the video with the play button next to "Content Url" (also pause it and go to the beginning if you want to record from the start). You can use the buttons of the video because the code document.getElementsByTagName("video")[0].currentTime = 0 is only useful when controls are hidden.
  • Execute the script without the 2 lines of code you already executed.
  • Play the video (with the play button from the video, not the one we used to load the video previously).
  • Execute startDownload() when you like or at the end of the video if you want it complete.

Now I moved the 2 lines of code to capture the stream at the beginning of the script so it is easier to configure or copy the part you need.

@dreamflasher
Copy link

Thank you very much @baptx ! That works like a charm.
Unfortunately, Firefox does not request the highest resolution at all times (after some time it switches to a lower resolution, idk why, also it switches between different resolutions). Chrome does not show this behavior, so I tried to build Chromium with the necessary changes.
For future reference I am leaving here how to build Chromium so that Widevine is supported:
/src/.gclient needs to be changed like this: "custom_vars": {"checkout_pgo_profiles": True,},
and /src/out/Default/args.gn needs to have these settings:

proprietary_codecs = true
enable_widevine = true
is_official_build=true
ffmpeg_branding="Chrome"

This file needs to be edited with an editor, because the args command doesn't deal with the required quotes around Chrome.

Then follow the guide by @baptx to modify https://chromium.googlesource.com/chromium/src/third_party/+/master/blink/renderer/modules/mediacapturefromelement/html_media_element_capture.cc#296

And now I am stuck :D Now chrome allows to record, but there are no bytes coming in :(

@baptx
Copy link
Author

baptx commented Mar 7, 2024

@dreamflasher Do you mean no bytes are coming in Chrome or Chromium? Did you try executing my script without modifying it?
If you removed the DRM capture restriction from Chromium source code, you don't need to execute the 2 lines of code mentioned previously separately, you can execute the script as a whole when the video is playing.
I never tried compiling Chromium but I will give it a try when I have more time.
To enable DRM in Chromium, you may also need to install Widevine:
https://github.com/proprietary/chromium-widevine

@dreamflasher
Copy link

dreamflasher commented Mar 7, 2024

@baptx yes, I tried your script unmodified and that doesn't work. No bytes are coming in.

@baptx
Copy link
Author

baptx commented Apr 14, 2024

@dreamflasher To get the best video resolution with Firefox, a solution for me on Facebook stories was to detach the Developer Tools window in a separate window. If necessary, replay the video to get the best resolution.
When I tested playing a video (without recording it) with Chromium on https://integration.widevine.com/player, it behaved like Firefox, by default it may not get the best resolution and this may change based on your Internet connection speed. Some players like the one from the Widevine website allow to select the resolution instead of the automatic selection.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment