What are the best ways to conceptualize the benefits of WebRTC?

I encountered a peculiar issue with the abstraction of the WebRTC offer generation process. It appears that the incoming ice candidates fail to reach the null candidate. While I have been able to generate offers successfully using similar code in the past, my abstracted version only reaches 12 candidates compared to the original 20. This discrepancy is baffling, as the code seems nearly identical but the abstracted version fails even on the same browser.

Here is the original working code:

    var localConn = new webkitRTCPeerConnection({'iceServers':[{...}]});
    var remoteConn = new webkitRTCPeerConnection({'iceServers':[{...}]});

    function initMedia(localView, callback){
        navigator.getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia;
        var constraints = {audio:true,video:true};
        navigator.getUserMedia(constraints, successStream, errorStream);
        //onSuccess and Error functions
        function successStream(stream){
            window.stream = stream;
            if(window.URL){
                $('#'+localView).attr('src',window.URL.createObjectURL(stream));
            } else {
                $('#'+localView).attr('src',stream);
            }
            localConn.addStream(stream);
            remoteConn.addStream(stream);
            console.log('local Stream: '+ stream.id);
            callback();     //-> goes on to create new offer
        }
        function errorStream(error){
            console.log('navigator.getUserMedia error: ', error);
        }
    }

    //function that generates offer and sends it out to a callback
    function newOffer(callback){
        console.log('creating new offer');

        localConn.createOffer(function (sessionDescription){
            localConn.setLocalDescription(sessionDescription);
        }, function(error){
            console.log('Error setting local description: '+error);
        });
        createOffer();
        //gather ICE with a callback to handle/send generated offer
        function createOffer(){
            localConn.onicecandidate = function(iceEvent){
                console.log('gathering local ice');        //ice events fired (20 total)
                //upon gathering all local ice
                if(iceEvent.candidate === null){
                    console.log('local ice gathered');    //success
                    var offer = {'type': localConn.localDescription.type,
                                 'sdp': localConn.localDescription.sdp};
                    offer = JSON.stringify(offer);
                    console.log('offer created');
                    callback(offer);
                }  
            }
        }
    }

Here is the updated version with functions for abstraction (not receiving null ice candidate):

//general rtc vars
var localConn = new webkitRTCPeerConnection({'iceServers':[{'url':'stun:stun.1.google.com:19302'}]});
var remoteConn = new webkitRTCPeerConnection({'iceServers':[{'url':'stun:stun.1.google.com:19302'}]});
//var mediaStream;
var channel;

//creates a stream from webcam
//@params function streamHandle(stream)
function initStream(streamHandle){
    navigator.getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia;
    var constraints = {audio:true,video:true};
    navigator.getUserMedia(constraints, successStream, errorStream);
    //onSuccess and Error functions
    function successStream(stream){
        window.stream = stream;
        console.log('TreRTC: Local Stream-'+ stream.id);
        //mediaStream = stream;
        localConn.addStream(stream);    
        remoteConn.addStream(stream);   
        streamHandle(stream);    
    }
    function errorStream(error){
        console.log('navigator.getUserMedia error: ', error);
    }
}

//creates an offer to be sent
//@params Stream stream (from getusermedia)
//@return string offer
function createOffer(stream){
    console.log('TreRTC: Creating Offer');
    
    localConn.createOffer(function (sessionDescription){
        localConn.setLocalDescription(sessionDescription);
    }, function(error){
        console.log('Error setting local description: '+error);
    });

    localConn.onicecandidate = function(iceEvt){
        console.log('TreRTC: ICE in');            
        if(iceEvt.candidate === null){
            console.log('TreRTC: ICE gathered');    
            var offer = {'type': localConn.localDescription.type,
                         'sdp': localConn.localDescription.sdp};
            offer = JSON.stringify(offer);
            console.log('TreRTC: Offer initialized');
            return offer;    
        }    
    }
}

Answer №1

streamHandle(stream);    //included in createOffer function

There may be a timing issue between your createOffer and initStream functions finishing, as they are asynchronous processes (although it's hard to confirm without seeing more of the code).

If you're looking to streamline WebRTC usage, consider moving away from the old legacy API and opting for RTCPeerConnection's new promise-based methods. Promises offer better control over handling race conditions like this compared to callbacks.

Another option is to utilize the onnegotiationneeded callback to trigger negotiation, potentially resolving this issue (however, be cautious of a bug in Chrome).

Below is an example of a local connection setup (try it out on JSFiddle using Chrome):

var pc1 = new RTCPeerConnection(), pc2 = new RTCPeerConnection();

navigator.mediaDevices.getUserMedia({video: true, audio: true})
  .then(stream => pc1.addStream(video1.srcObject = stream))
  .catch(e => console.log(e));

pc1.onicecandidate = e => pc2.addIceCandidate(e.candidate);
pc2.onicecandidate = e => pc1.addIceCandidate(e.candidate);

pc2.ontrack = e => video2.srcObject = e.streams[0];
pc1.oniceconnectionstatechange = e => console.log(pc1.iceConnectionState);
pc1.onnegotiationneeded = e =>
  pc1.createOffer().then(d => pc1.setLocalDescription(d))
  .then(() => pc2.setRemoteDescription(pc1.localDescription))
  .then(() => pc2.createAnswer()).then(d => pc2.setLocalDescription(d))
  .then(() => pc1.setRemoteDescription(pc2.localDescription))
  .catch(e => console.log(e));
<video id="video1" width="160" height="120" autoplay muted></video>
<video id="video2" width="160" height="120" autoplay></video>
<script src="https://webrtc.github.io/adapter/adapter-latest.js"></script>

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

Exploring the compatibility of Next.js with jest for utilizing third-party ESM npm packages

Caught between the proverbial rock and a hard place. My app was built using: t3-stack: v6.2.1 - T3 stack Next.js: v12.3.1 jest: v29.3.1 Followed Next.js documentation for setting up jest with Rust Compiler at https://nextjs.org/docs/testing#setting-up-j ...

Using getStaticProps in Next.js to retrieve menu data and seamlessly integrate it into the layout

I have integrated Sanity.io as a backend for my project, specifically managing the data for my main menu in parent/children object arrays. While I am able to successfully fetch this data, I prefer to utilize getStaticProps to ensure that my site remains c ...

When using create-react-app, the value of 'process.env.NODE_ENV' can be accessed as either a string or a process object during runtime

Have you come across code that looks like this: if(process.env.NODE_ENV === 'development') { // Perform operations specific to DEVELOPMENT mode } Similarly, you might see process.env.NODE_ENV === 'production. When we run npm run ...

Checking authentication globally using Vue.js

In the main blade file, I have the following code snippet: <script> window.App = {!! json_encode([ 'csrfToken' => csrf_token(), 'user' => Auth::user(), 'signedIn' => Auth::check() ...

Interpret data from an external JSON file into a HighCharts chart using JavaScript

Currently, I am exploring the process of incorporating HighCharts Chart data directly within the actual webpage. Below is the complete code snippet: <!DOCTYPE html> <html><head> <script type="text/javascript" src="http://code.jquery. ...

"Experience the power of utilizing TypeScript with the seamless compatibility provided by the

I'm attempting to utilize jsymal.safeDump(somedata). So far, I've executed npm install --save-dev @types/js-yaml I've also configured my tsconfig file as: { "compilerOptions": { "types": [ "cypress" ...

What is the best way to compare two date strings with the format dd/mm/yyyy using JavaScript?

When attempting to compare a "Date" type of data with an "Any" type of data, the comparison is not functioning as expected. The date is retrieved in the following code: var today = new Date(); var dd = String(today.getDate()).padStart(2, '0'); v ...

External API data is shown in the browser console but appears as undefined on the page

Can you please lend me a helping hand? I am facing a critical issue while attempting to retrieve data from an external API using axios in NextJS (Reactjs)/TypeScript through getServerSideProps. The data fetching is successful, and the JSON is returned on t ...

Error in Express Post Request: Headers cannot be modified after being sent to the client

I am a beginner in Node.js and I am facing some challenges while working on an app for learning purposes. I encountered the following issue: Error: Can't render headers after they are sent to the client. I am unsure of how to resolve it. C:\Us ...

Vue.js displaying the error message: "The maximum size of the call stack has been exceeded"

Could you assist me in resolving this issue? router.js routes: [{ path: "", component: () => import("@/layouts/full-page/FullPage.vue"), children: [{ path: "/pages/login", name: "page-login", component: () => import("@/views/pages ...

What is the speed of communication for Web Worker messages?

One thing I've been pondering is whether transmission to or from a web worker could potentially create a bottleneck. Should we send messages every time an event is triggered, or should we be mindful and try to minimize communication between the two? ...

When the return false statement is included, the form fails to submit and instead refreshes on the current page

I recently discussed an issue regarding triggering a dialog box before confirming a submit action. Unfortunately, after implementing this, the document no longer submits when expected. Instead, it just remains on the same page with the same settings. I h ...

Is there a method to incorporate a scroll event into the ng-multi-selectdropdown, a npm package?

Query: I need to incorporate a scroll event in the given html code that triggers when the div is scrolled. However, I am facing issues with implementing a scroll event that works when the elements are being scrolled. <ng-mult ...

Is it possible to customize the MongoDB Collection before loading the web application by fetching data asynchronously using Promises?

I am currently working with MongoDB and NodeJS, trying to preload my Collection customers every time the site is loaded. The process involves emptying the collection, populating it with empty Documents, and then replacing them with real data fetched from a ...

Uploading a file with AngularJS and storing it in a database

I have been attempting to implement ngFileUpload in order to upload images and store them in a database – specifically, a mongoLab database that accepts JSON objects which can be posted using this syntax: $http.post('myMongoName/myDb/myCollection/ ...

"Converting an object to a JSON string using URLSearchParams: A step-by

I am currently working on a piece of code that retrieves all the input types from a form const form = document.querySelector('form'); const data = new URLSearchParams(new FormData(form).entries()); My main concern is how to convert the above ...

Sending data using Ajax to the server-side code in ASP.NET

Struggling to successfully pass a series of values through Ajax to a code-behind method in order to insert the data into a database table. However, encountering issues where string variables are being received as empty strings and int variables as 0. The ...

React UseEffect - Triggering only when data has been updated

In my current situation, I am facing a dilemma with the useEffect hook. I need it to not run on the initial render, but only when specific data is rendered, such as home.matchsPlayed and home.winHome. This is what my code looks like: useEffect(() => ...

Identifying when a user has inputted incorrect $routeparams

How can I restrict user input to only "id" as a query parameter in the URL? $scope.$on('$routeUpdate', function () { var id = $routeParams.id //check if user has entered any params other than "id". //if yes do someting }); I want ...

How can we automatically delete a message in DiscordJS after a certain amount of time has passed?

I am inquiring about DiscordJS and have a specific question. My query is regarding how to correctly remove a message sent by the bot on Discord. Admittedly, I am new to DiscordJS so please bear with me if this is a basic question. I appreciate all respo ...