I encountered an issue when trying to jump into a stream from a media recorder using a media source with

When the video observing client is loaded before the webcam client, everything runs smoothly. However, if the order is reversed or there is any interruption in the stream (such as refreshing either client), the Media Source will close and the stream will fail.

It seems that the video initially needs initialization headers to start properly, and when the stream is interrupted midstream, these headers are not received. I am unsure of how to add such headers to the webm file.

I attempted to change the sequence mode on the source buffer without success. Restarting the video recorder did work, but having multiple observing clients with the recorder restarting upon reconnection is not ideal.

Camera Client

main();
function main() {
    if (hasGetUserMedia()) {
        const constraints = {
            video: {
                facingMode: 'environment',
                frameRate: {
                    ideal: 10,
                    max: 15
                }
            },
            audio: true
        };

        navigator.mediaDevices.getUserMedia(constraints).
        then(stream => {
            setupRecorder(stream);
        });
    }
}

function setupRecorder(stream) {
    let mediaRecorder = new MediaRecorder(stream, {
        mimeType: 'video/webm; codecs="opus, vp9"'
    });

    mediaRecorder.ondataavailable = e => {
        var blob = e.data;
        socket.emit('video', blob);
    }

    mediaRecorder.start(500);
}

The server simply broadcasts whatever it receives.

Observing Client

var sourceBuffer;
var queue = [];
var mediaSource = new MediaSource();
mediaSource.addEventListener('sourceopen', sourceOpen, false);
main();

socket.on('stream', data => {
    if (mediaSource.readyState == "open") {
        if (sourceBuffer.updating || queue.length > 0) {
            queue.push(data.video);
        } else {
            sourceBuffer.appendBuffer(data.video);
        }
    }
});

function main() {
    videoElement = document.querySelector('#video');
    videoElement.src = URL.createObjectURL(mediaSource);
}

function sourceOpen(e) {
    console.log('open');
    sourceBuffer = mediaSource.addSourceBuffer('video/webm; codecs="opus, vp9"');
    sourceBuffer.addEventListener('updateend', () => {
        console.log(sourceBuffer.updating, mediaSource.readyState);

        if (queue.length > 0 && !sourceBuffer.updating) {
            sourceBuffer.appendBuffer(queue.shift());
        }
    });
}

So, the code does work in a functioning manner, although not correctly. The issue likely lies with either the MediaRecorder or MediaSource rather than the server or socket sending.

Answer №1

My belief is that when the video starts playing, it requires initialization headers to begin properly. However, if the stream is interrupted and read in the middle, those headers may never be received.

Exactly!

To fix this issue, it's essential to understand the WebM format. WebM is essentially a subset of Matroska (MKV). Matroska serves as a schema for storing media in EBML, which is a binary file format capable of housing various blocks, similar to binary XML.

You can utilize tools like EBML Viewer to examine WebM files and refer to the Matroska specifications for better comprehension. For instance:

https://i.sstatic.net/ahn42.png

This shows an inspection of a pre-recorded WebM file that plays seamlessly in browsers. It demonstrates nested elements within the file.

In every WebM file, there are two primary elements - EBML defining the binary file and Segment containing everything else.

Within Segment, some crucial elements include Tracks listing audio and video tracks, and Info providing details about the muxer.

Following the metadata, you encounter Cluster, Cluster, Cluster, etc. These segments are where you can segment a WebM stream, ensuring each starts with a keyframe.

Your code should do the following:

  • Save data before the first Cluster as "initialization data".
  • Split on Cluster after that.

During playback:

  • Load the saved "initialization data" initially.
  • Continue loading Clusters afterwards from any point in the stream.

Note the importance of having keyframes at the start of each cluster. Configuring MediaRecorder might not achieve this, necessitating remuxing or even re-encoding server-side. Read more here: Encoding FFMPEG to MPEG-DASH – or WebM with Keyframe Clusters – for MediaSource API

using media source with socket.io

It's worth noting that MediaSource isn't mandatory for this. Socket.IO is also not required. Outputting data over a standard HTTP stream suffices, directly playable in a <video> element. (Feel free to use MediaSource for additional control, but it's not compulsory.)

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

Issue with applying z-index property to an element using jQuery .css method

When using jQuery to select elements and apply CSS, I have the following code... $(".items div").not(".active").css({"color":"green","background":"red","z-index:":"-9"}); <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js" ...

Accessing data attributes using jQuery and the class selector

I'm looking for a way to trigger an onClick event for an entire class of elements and access their individual data attributes within the function. Typically, I would use the following method: $(".classy").click(function(){ console.log("Replace th ...

The resolve in Angular UI-Router is not being properly injected into the controller

As a Java developer venturing into the world of Angular and Express, I am encountering challenges along the way. To gain hands-on experience, I am attempting to create a small application using these technologies. The Goal: I have provided a password rese ...

Big calendar experience with React

Currently, I am developing a calendar page as part of my group project. Utilizing react big calendar for this purpose, I am facing an issue where the text displayed in the popup window needs to remain unchanged by the user. With multiple events on the ca ...

Error Message: ES5 mandates the use of 'new' with Constructor Map

Below is the code snippet: export class ExtendedMap<T, U> extends Map { constructor() { super(); } toggle(key: T, value: U) { if (this.has(key)) { super.delete(key); ...

Tips for implementing fresh css when text changes

Does anyone know how to dynamically update the CSS of text that has been changed using useState()? I've managed to change the text, but need help with updating the styling. JS: const [verifyAttendance, setViewAttendance] = useState("Verify Class Atte ...

Implement a new feature in TypeORM that allows adding an array of objects to an

For my project, I require icons to be structured as an array of objects, similar to the format below: [ { "id": 1, "title": "Title text", "icons": [ { "icon": "icon-1.png", "main": tru ...

The canvas is either displayed solely on the first image of the slider or attached to the bottom of every image

While attempting to implement a feature where clicking on an image would draw a circle on a canvas overlaying it, I encountered an issue. When applying cnvs.style.position = 'absolute';, all canvas elements stack on top of each other instead of b ...

retrieve the text content from the clicked anchor (a) element along with its sibling

Could someone assist me with getting text on the <a> element, similar to what is shown in the plunker? When I click on it for the first time, the text string displays correctly. However, if I click again, it shows multiple times. I'm unsure of h ...

Tips for eliminating duplicate entries in ag grid using Angular

Is there a way to eliminate the recurring assetCode entries in ag grid? The PRN and PRN1 values seem to be repeating unnecessarily. Check out the code below: list.component.ts ngOnInit() { this.rowData.push( { 'code': 'Machi ...

Guide on transforming an array into a collection of objects

Could someone please help me with converting this array? const myArr = ['lorem', 'ipsum', 'dolor', 'sit', 'amet'] I want to transform it into an object structure like this: { lorem:{ ipsum:{ ...

The Bootstrap validator.js isn't working in sync with my PHP code

Seeking help as an amateur coder, struggling to implement form validation using validator.js The form has ceased functioning after adding validation. I suspect the issue lies within 'data: $('#send-form').serialize()', perhaps due to i ...

What is the method for configuring environment variables in the Lumber framework?

Installing Lumber CLI npm install -g lumber-cli -s Next, lumber generate "adminpanel_test" --connection-url "mysql://root@localhost:3306/admin-dev" --ssl "false" --application-host "localhost" --application-port "3310" Error: lumber is not recognized a ...

Exploring the functionality of asynchronous useEffect

My component is designed to fetch data from an API using the useEffect hook upon mounting. I am currently facing issues with testing the rendering of the fetched data, as the asynchronous nature of the hook causes the component to not update in time for th ...

Tips on updating the text within an anchor element

Is there a way to update the text 'save' to 'edit' in this HTML snippet by utilizing jQuery? <a id="personalSave" href="#" class="SaveText"> <span class="FloatLeft">&lsaquo;</span> save <span class="FloatRight ...

Design a dynamic backdrop using three.js

Hey, I'm currently working on incorporating a background image into my Three JS project. Although the code is functioning properly, the background isn't being displayed. Here is my init function: function init(){ // Creating a new scene scene ...

Using JavaScript to trigger a PHP script execution

I'm facing an issue with a server-side PHP script that connects to a database and retrieves data. It seems to work fine in a separate browser, but I can't seem to make it run properly from a script within HTML. Here's the code snippet causi ...

Incorporating information into the output of an HTTP request with Angular 7

I have a list of asset IDs (assetIDs) and I need to fetch data using these IDs. Each HTTP request returns one or more datasets. My goal is to include the request ID in each dataset and then return the data. The process of fetching and returning the data i ...

What is the best way to position an icon to the left of the text?

My code is as follows: <link href="https://maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css" rel="stylesheet"/> <span> <i class="fa fa-check"></i> This text is just a test and repeating to ...

Perform calculations in JavaScript by extracting specific values from HTML select elements to create a currency converter

Would you like to create a currency converter on your website? You can achieve this by selecting different currencies from an HTML select tag and then multiplying them with an input value using JavaScript. function getInputValue(){ // Selecting the ...