Error simple WebRTC chat on react.js and meteor.js - javascript

I'm trying to make a simple chat using webrtc technology, react.js and meteor.js.
This is client code:
class Rtc extends Component {
constructor(props) {
super(props);
}
componentDidUpdate(){
let localVideo, remoteVideo, peerConnection, localStream;
$('#start').on('click', ()=>{ start(true) });
let id = Meteor.uuid();
localVideo = document.getElementById('localVideo');
remoteVideo = document.getElementById('remoteVideo');
if (!this.props.loadingRtc) {
this.props.messagesRtc.forEach((item, i ,arr)=>{
let signal = JSON.parse(item.text);
if(i == 0)return;
gotMessageFromServer(signal);
});
}
if(navigator.mediaDevices.getUserMedia) {
navigator.mediaDevices.getUserMedia( { video:true, audio:true}).then( ( stream )=> {
localStream = stream;
localVideo.src = window.URL.createObjectURL(stream);
}).catch(errorHandler);
} else { alert('Your browser does not support getUserMedia API'); }
function start(isCaller) {
peerConnection = new RTCPeerConnection( { 'iceServers': [{'urls': 'stun:stun.services.mozilla.com'}, {'urls': 'stun:stun.l.google.com:19302'},]});
peerConnection.onicecandidate = ( e ) => {
console.log('e.candidate', e.candidate);
if(e.candidate != null) {
Meteor.call('addMsgRtc', JSON.stringify({'ice': e.candidate, '_id':id}), id);
}
};
peerConnection.onaddstream = ( e )=>{
remoteVideo.src = window.URL.createObjectURL(e.stream);
};
peerConnection.addStream(localStream);
if(isCaller) {
peerConnection.createOffer().then(createdDescription).catch(errorHandler);
}
}
function gotMessageFromServer(signal) {
if(!peerConnection) start(false);
if(signal._id == id) return;
if(signal.sdp) {
peerConnection.setRemoteDescription(new RTCSessionDescription(signal.sdp)).then(()=> {
if(signal.sdp.type == 'offer') {
peerConnection.createAnswer().then(createdDescription).catch(errorHandler);
}
}).catch(errorHandler);
} else if(signal.ice) {
peerConnection.addIceCandidate(new RTCIceCandidate(signal.ice)).catch(errorHandler);
}
}
function createdDescription(description) {
peerConnection.setLocalDescription(description).then(()=> {
Meteor.call('addMsgRtc', JSON.stringify({'sdp':peerConnection.localDescription, '_id':id}), id);
}).catch(errorHandler);
}
function errorHandler(error) { console.log(error); }
}
render() {
return (
<div>
<video id="localVideo" autoPlay muted style={{width:"40%"}}></video>
<video id="remoteVideo" autoPlay style={{width:"40%"}}></video>
<br/>
<input type="button" id="start" value="Start Video"/>
</div>
);
}
}
export default createContainer( ()=> {
const subscriptionRtc = Meteor.subscribe('rtc');
const loadingRtc = !subscriptionRtc.ready();
return {
loadingRtc:loadingRtc,
messagesRtc: msgRtc.find().fetch(),
};
}, App);
Server code:
export const msgRtc = new Mongo.Collection('rtc');
let messagesRtc = [];
let clients = [];
Meteor.publish('rtc', function wsPub() {
clients.push(this);
_.each(messagesRtc, (message) => {
this.added('rtc', message._id, message);
});
this.ready();
});
Meteor.methods({
'addMsgRtc'(arr, id) {
let newMessage = {_id:id, 'text':arr};
messagesRtc.push(newMessage);
_.each(clients, (client) => {
client.added('rtc', id, newMessage);
});
},
The problem is that why after the initialization of getUserMedia does not want to further this videos and what not so I can't understand. Because it is actually a similar code with the usual websockets like this works and syncs fine.
UPD:
When you click on the button and call the function start.
TypeError: Argument 1 of RTCPeerConnection.addStream is not an object
If websockets to put in then then it turns out another error:
Code:
navigator.mediaDevices.getUserMedia( { video:true, audio:true}).then( ( stream )=> {
localStream = stream;
localVideo.src = window.URL.createObjectURL(stream);
}).catch(errorHandler).then(()=>{
if (!this.props.loadingRtc) {
for(let i of this.props.messagesRtc){
let signal = JSON.parse(i.text);
gotMessageFromServer(signal)
}
}
}).catch(errorHandler);
Error:
DOMException [InvalidStateError: "Cannot set remote offer or answer in
current state have-remote-offer" code: 11 nsresult: 0x8053000b]
app.js:11075:9 DOMException [InvalidStateError: "No outstanding offer"
code: 11 nsresult: 0x8053000b]

Related

i am having a problem with playing an audio blob i get from a microphone. it works for the first time but doesnt seem to work after that

i am trying to add a voice message feature to my chat app and i have a problem on playing the audio that i record.
recordAudio.js
import { onUnmounted } from 'vue'
const constraint = { audio: true }
let chunks = []
function record() {
let mediaRecorder
let stream
function close() {
// console.log(mediaRecorder?.state, 'state man')
if (mediaRecorder && mediaRecorder?.state == 'recording'){
mediaRecorder?.stop()
stream && (
stream.getTracks()
.forEach(track => track.stop())
)
}
}
onUnmounted(() => {
close()
})
async function start() {
if (navigator.mediaDevices.getUserMedia) {
const strm = await navigator.mediaDevices.getUserMedia(constraint)
// console.log(strm)
if (!strm) return false
// console.log('media', mediaRecorder)
stream = strm
mediaRecorder = new MediaRecorder(strm)
mediaRecorder.start(100)
// console.log('listingin for audio')
mediaRecorder.ondataavailable = (e) => {
// console.log(e.data)
chunks.push(e.data)
}
}
return true
}
function stop() {
close()
const blob = new Blob(chunks, { 'type' : 'audio/ogg; codecs=opus' });
console.log(chunks)
chunks = []
// const audioURL = URL.createObjectURL(blob)
return blob
}
return {
start,
stop
}
}
export {
record
}
this is a code fragment in a .vue file
import {record} from '#util/recordAudio.js'
const { start, stop } = record()
async function recordAudio() {
if (!recording.value) {
let res = await start()
res && (recording.value = true)
} else {
stop()
recording.value = false
}
}
function sendAudio() {
let audioBlob = stop()
recording.value = false
console.log(audioBlob)
messages.addMessage({
id: props.chat.id,
message: {
id: 1,
to: 2,
from: 1,
message: audioBlob,
seen: true,
type: 'audio',
createdAt: new Date()
}
})
}
let url = {}
function getObjectUrl(id, message) {
if (!url[id]) {
url[id] = URL.createObjectURL(message)
return url[id]
}
return url[id]
}
//this is the template for the audio
<div v-if='message.type == "audio"'>
<audio controls :src='getObjectUrl(message.id, message.message)'></audio>
</div>
it seems to work for the first time and it doesnt work after.
in firefox i get the warning/error
Media resource blob:http://localhost:5374/861d13c5-533f-4cd7-8608-68eecc7deb4e could not be decoded
Media resource blob:http://localhost:5374/861d13c5-533f-4cd7-8608-68eecc7deb4e could not be decoded, error: Error Code: NS_ERROR_DOM_MEDIA_METADATA_ERR (0x806e0006)
error message

Raspberry PI WebRTC Video Stream via USB Camera on Chromium freezing & blacking out

I have a Raspberry Pi running a Node express server with webRTC. The Raspberry Pi is connected to a web cam. I'm able to view the index.html page and see the video stream, but then it randomly freezes or black out completely. I've tried researching the issue but most solutions refer to changing settings on components that are not part of what I'm using. Help is sincerely appreciated as I've been banging my head for the past few days.
index.html
<!DOCTYPE html>
<html>
<head>
<title>Stream</title>
<meta charset="UTF-8" />
<link href="/styles.css" rel="stylesheet" />
</head>
<body>
<video playsinline autoplay muted></video>
<button id="enable-audio">Enable audio</button>
<script src="/socket.io/socket.io.js"></script>
<script src="/watch.js"></script>
</body>
</html>
broadcast.js
// wip 21 January 2022
const peerConnections = {};
const config = {
iceServers: [
{
"urls": "stun:stun.l.google.com:19302",
},
// {
// "urls": "turn:TURN_IP?transport=tcp",
// "username": "TURN_USERNAME",
// "credential": "TURN_CREDENTIALS"
// }
]
};
const socket = io.connect(window.location.origin);
socket.on("answer", (id, description) => {
peerConnections[id].setRemoteDescription(description);
});
socket.on("watcher", id => {
const peerConnection = new RTCPeerConnection(config);
peerConnections[id] = peerConnection;
let stream = videoElement.srcObject;
stream.getTracks().forEach(track => peerConnection.addTrack(track, stream));
peerConnection.onicecandidate = event => {
if (event.candidate) {
socket.emit("candidate", id, event.candidate);
}
};
peerConnection
.createOffer()
.then(sdp => peerConnection.setLocalDescription(sdp))
.then(() => {
socket.emit("offer", id, peerConnection.localDescription);
});
});
socket.on("candidate", (id, candidate) => {
peerConnections[id].addIceCandidate(new RTCIceCandidate(candidate));
});
socket.on("disconnectPeer", id => {
peerConnections[id].close();
delete peerConnections[id];
});
window.onunload = window.onbeforeunload = () => {
socket.close();
};
// Get camera and microphone
const videoElement = document.querySelector("video");
const audioSelect = document.querySelector("select#audioSource");
const videoSelect = document.querySelector("select#videoSource");
audioSelect.onchange = getStream;
videoSelect.onchange = getStream;
getStream()
.then(getDevices)
.then(gotDevices);
function getDevices() {
return navigator.mediaDevices.enumerateDevices();
}
function gotDevices(deviceInfos) {
window.deviceInfos = deviceInfos;
for (const deviceInfo of deviceInfos) {
const option = document.createElement("option");
option.value = deviceInfo.deviceId;
if (deviceInfo.kind === "audioinput") {
option.text = deviceInfo.label || `Microphone ${audioSelect.length + 1}`;
audioSelect.appendChild(option);
} else if (deviceInfo.kind === "videoinput") {
option.text = deviceInfo.label || `Camera ${videoSelect.length + 1}`;
videoSelect.appendChild(option);
}
}
}
function getStream() {
if (window.stream) {
window.stream.getTracks().forEach(track => {
track.stop();
});
}
const audioSource = audioSelect.value;
const videoSource = videoSelect.value;
const constraints = {
audio: { deviceId: audioSource ? { exact: audioSource } : undefined },
video: { deviceId: videoSource ? { exact: videoSource } : undefined }
};
return navigator.mediaDevices
.getUserMedia(constraints)
.then(gotStream)
.catch(handleError);
}
function gotStream(stream) {
window.stream = stream;
audioSelect.selectedIndex = [...audioSelect.options].findIndex(
option => option.text === stream.getAudioTracks()[0].label
);
videoSelect.selectedIndex = [...videoSelect.options].findIndex(
option => option.text === stream.getVideoTracks()[0].label
);
videoElement.srcObject = stream;
socket.emit("broadcaster");
}
function handleError(error) {
console.error("Error: ", error);
}
watch.js
let peerConnection;
const config = {
iceServers: [
{
"urls": "stun:stun.l.google.com:19302",
},
// {
// "urls": "turn:TURN_IP?transport=tcp",
// "username": "TURN_USERNAME",
// "credential": "TURN_CREDENTIALS"
// }
]
};
const socket = io.connect(window.location.origin);
const video = document.querySelector("video");
const enableAudioButton = document.querySelector("#enable-audio");
enableAudioButton.addEventListener("click", enableAudio)
socket.on("offer", (id, description) => {
peerConnection = new RTCPeerConnection(config);
peerConnection
.setRemoteDescription(description)
.then(() => peerConnection.createAnswer())
.then(sdp => peerConnection.setLocalDescription(sdp))
.then(() => {
socket.emit("answer", id, peerConnection.localDescription);
});
peerConnection.ontrack = event => {
video.srcObject = event.streams[0];
};
peerConnection.onicecandidate = event => {
if (event.candidate) {
socket.emit("candidate", id, event.candidate);
}
};
});
socket.on("candidate", (id, candidate) => {
peerConnection
.addIceCandidate(new RTCIceCandidate(candidate))
.catch(e => console.error(e));
});
socket.on("connect", () => {
socket.emit("watcher");
});
socket.on("broadcaster", () => {
socket.emit("watcher");
});
window.onunload = window.onbeforeunload = () => {
socket.close();
peerConnection.close();
};
function enableAudio() {
console.log("Enabling audio")
video.muted = false;
}

Using Html and Javascript in Flutter

I am using peerJS for screen sharing feature and flutter for web app, I'm able to call javascript functions in flutter but not able to pass values from Javascript and Flutter and make them listen in flutter.
this is my script.js:
const PRE = "Devcom"
const SUF = "Community"
var room_id;
var getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia;
var local_stream;
var screenStream;
var peer = null;
var currentPeer = null
var screenSharing = false
function createRoom() {
console.log("Creating Room")
let room = document.getElementById("room-input").value;
if (room == " " || room == "") {
alert("Please enter room number")
return;
}
room_id = PRE + room + SUF;
peer = new Peer(room_id)
peer.on('open', (id) => {
console.log("Peer Connected with ID: ", id)
hideModal()
getUserMedia({ video: true, audio: true }, (stream) => {
local_stream = stream;
setLocalStream(local_stream)
}, (err) => {
console.log(err)
})
notify("Waiting for peer to join.")
})
peer.on('call', (call) => {
call.answer(local_stream);
call.on('stream', (stream) => {
setRemoteStream(stream)
})
currentPeer = call;
})
}
function setLocalStream(stream) {
let video = document.getElementById("local-video");
video.srcObject = stream;
video.muted = true;
video.play();
}
function setRemoteStream(stream) {
let video = document.getElementById("remote-video");
video.srcObject = stream;
video.play();
}
function hideModal() {
document.getElementById("entry-modal").hidden = true
}
function notify(msg) {
let notification = document.getElementById("notification")
notification.innerHTML = msg
notification.hidden = false
setTimeout(() => {
notification.hidden = true;
}, 3000)
}
function joinRoom() {
console.log("Joining Room")
let room = document.getElementById("room-input").value;
if (room == " " || room == "") {
alert("Please enter room number")
return;
}
room_id = PRE + room + SUF;
hideModal()
peer = new Peer()
peer.on('open', (id) => {
console.log("Connected with Id: " + id)
getUserMedia({ video: true, audio: true }, (stream) => {
local_stream = stream;
setLocalStream(local_stream)
notify("Joining peer")
let call = peer.call(room_id, stream)
call.on('stream', (stream) => {
setRemoteStream(stream);
})
currentPeer = call;
}, (err) => {
console.log(err)
})
})
}
function startScreenShare() {
if (screenSharing) {
stopScreenSharing()
}
navigator.mediaDevices.getDisplayMedia({ video: true }).then((stream) => {
screenStream = stream;
let videoTrack = screenStream.getVideoTracks()[0];
videoTrack.onended = () => {
stopScreenSharing()
}
if (peer) {
let sender = currentPeer.peerConnection.getSenders().find(function (s) {
return s.track.kind == videoTrack.kind;
})
sender.replaceTrack(videoTrack)
screenSharing = true
}
console.log(screenStream)
})
}
function stopScreenSharing() {
if (!screenSharing) return;
let videoTrack = local_stream.getVideoTracks()[0];
if (peer) {
let sender = currentPeer.peerConnection.getSenders().find(function (s) {
return s.track.kind == videoTrack.kind;
})
sender.replaceTrack(videoTrack)
}
screenStream.getTracks().forEach(function (track) {
track.stop();
});
screenSharing = false
}
function alertMessage(text) {
alert(text)
}
window.logger = (flutter_value) => {
console.log({ js_context: this, flutter_value });
}
window.state = {
hello: 'world'
}
I am able to call these javascript functions in my flutter app using:
import 'dart:js' as js;
js.context.callMethod('alertMessage', ['Flutter is calling upon JavaScript!']);
I have webRTC.html file below which works fine with my js file attached above:
<html>
<head>
<title>DEVCOM - Rooms</title>
<link rel="stylesheet" type="text/css" href="styles.css">
</head>
<body>
<h1 class="title">Devcom</h1>
<p id="notification" hidden></p>
<div class="entry-modal" id="entry-modal">
<p>Create or Join Meeting</p>
<input id="room-input" class="room-input" placeholder="Enter Room ID">
<div>
<button onclick="createRoom()">Create Room</button>
<button onclick="joinRoom()">Join Room</button>
</div>
</div>
<div class="meet-area">
<!-- Remote Video Element-->
<video id="remote-video"></video>
<!-- Local Video Element-->
<video id="local-video"></video>
<div class="meet-controls-bar">
<button onclick="startScreenShare()">Screen Share</button>
</div>
</div>
</body>
<script src="https://unpkg.com/peerjs#1.3.1/dist/peerjs.min.js"></script>
<script src="script.js"></script>
</html>
but the problem which I am facing is how can I make a flutter widget listen to javascript like instead of using document.getElementById("room-input").value I want to make javascript listen to flutter widgets and variables rather than html id, values...
or Is there any other way which can make html rendering in flutter web I tried webview_flutter, easy_web_view, flutter_html but nothing worked for me as I want to use it in windows chrome (web) few of earlier mentioned plugins are rather legacy and other are only for android and ios

Why does my simple peer WebRTC work on some android phones but not others

I built a chat react app as a PWA that works on desktops and works on my phone and my wife's phone but not on some other androids and not on Iphones. I was wondering if anyone knew of why that might be. I have also had cases where it get connected but I cannot see video or audio and cases where it connects one time but I can try another day and it wont connect. I thought it may have something to do with the trickle being set to false or maybe I need to update my simple peer API right now I have 9.7 but I see they have a 9.10.
one thought I had is that it works fine for my wife and I because we are on the same network.
import React from "react";
import Peer from "simple-peer";
import SocketContext from "../../context/SocketProvider";
let phoneRinging = new Audio("./phoneRing.mp3")
class VideoChat extends React.Component {
static contextType = SocketContext
constructor(props) {
super(props);
// this.socket = createRef();
this.state = {
yourInfo: this.props.yourInfo,
users: this.props.users,
stream: null,
receivingCall: this.props.receivingCall,
caller: this.props.caller,
callerSignal: this.props.callerSignal,
callAccepted: false,
btnHidden: false
};
}
componentDidMount() {
this.getVideo()
}
getVideo = () => {
const contraints = {
audio: true,
video: true
}
navigator.mediaDevices.getUserMedia(contraints)
.then(stream => {
console.log(stream)
let video = document.getElementById("senderVid");
video.srcObject = stream;
this.setState({ stream: stream })
})
.catch(function (err) {
console.log(err, alert("cannot access your camera"))
});
}
callPeer = (id) => {
const socket = this.context
// this.phoneRingFn()
const peer = new Peer({
initiator: true,
trickle: false,
stream: this.state.stream,
});
console.log(peer)
peer.on("signal", data => {
socket.emit("callUser", { userToCall: id, signalData: data, from: this.state.yourInfo })
console.log("call placed")
})
peer.on("stream", stream => {
let video = document.getElementById("recVid");
video.srcObject = stream;
});
socket.on("callAccepted", signal => {
this.setState({ callAccepted: true })
phoneRinging.pause()
phoneRinging.currentTime = 0;
peer.signal(signal);
})
}
acceptCall = () => {
const socket = this.context
// phoneRinging.pause()
// phoneRinging.currentTime = 0;
this.setState({ callAccepted: true })
console.log("1")
const peer = new Peer({
initiator: false,
trickle: false,
stream: this.state.stream,
});
peer.on("signal", data => {
console.log("2")
console.log(this.state.caller)
socket.emit("acceptCall", { signal: data, to: this.state.caller.socketId })
})
peer.on("stream", stream => {
console.log("3")
let video = document.getElementById("recVid");
video.srcObject = stream;
});
peer.signal(this.state.callerSignal);
this.setState({ btnHidden: true })
}
hangUp = () => {
this.props.callEnded()
document.getElementById("recVid").remove()
}
phoneRingFn = () => {
phoneRinging.play()
console.log("phone ringing ")
let timesPhnRung = 0
let MaxRings = 3
phoneRinging.onplay = function () {
//played counter
timesPhnRung++;
};
phoneRinging.addEventListener("ended", function () {
phoneRinging.currentTime = 0;
if (timesPhnRung < MaxRings) {
phoneRinging.play();
} else {
timesPhnRung = 0;
}
});
}
// peer.on("close",()=>{
// console.log("peerdestroy")
// document.getElementById("recVid").remove()
// peer.destroy();
// })
render() {
console.log(this.state.btnHidden)
console.log(this.props)
let incomingCall;
if (this.state.receivingCall) {
incomingCall = ((this.state.btnHidden === false) ?
<div>
<h1>{this.state.caller.name} is calling you</h1>
<button onClick={this.acceptCall}>Accept</button>
</div> :
<div><h1>call connected</h1></div>
)
}
return (
<div className="videoChatContainer">
<div className="videoWindows">
<div className="senderVidContainer">
sender
<video id="senderVid" className="senderVideo" autoPlay="autoplay" muted></video>
</div>
<div className="receiverVidContainer">
receiver
<video id="recVid" className="receiverVideo" autoPlay="autoplay"></video>
</div>
</div>
<button className="disconnectChat" onClick={() => this.hangUp()}>end call</button>
{ !this.state.receivingCall ? (
<div>{
this.state.users.length ? (
<div>{
this.state.users.map(identify =>
(identify.userid === this.props.friendsPhId) ?
<button onClick={() => this.callPeer(identify.socketId)}>Call {identify.name}</button>
: null
)}
</div>
) : (<h1>Loading</h1>)}
</div>
) : (<div></div>)}
<div>
{incomingCall}
</div>
</div>
)
}
}
export default VideoChat;

Original audio of tab gets muted while using chrome.tabCapture.capture() and MediaRecorder()

when i use chrome.tabCapture.capture() with MediaRecorder API to record stream original audio of tabs which i am capturing gets muted but the audio comes OK in recorded stream, i want the audio in the tab to run normally ....
class Recorder {
constructor(onChunksAvailable) {
this.chunks = [];
this.active = false;
this.callback = onChunksAvailable;
}
start(stream) {
if (this.active) {
throw new Error("recorder is already running");
}
this.recorder = new MediaRecorder(stream, {
mimeType: "audio/webm",
});
this.recorder.onstop = () => {
stream.getAudioTracks()[0].stop();
this.callback([...this.chunks]);
setTimeout(() => {
this.chunks = [];
});
this.active = false;
};
this.recorder.ondataavailable = (event) => this.chunks.push(event.data);
this.active = true;
this.recorder.start();
}
stop() {
if (!this.active) {
throw new Error("recorder is already stop");
} else {
this.recorder.stop();
}
}
}
let rec = new Recorder(async (chunks) => {
//using chunks then to get the stream
});
chrome.tabCapture.capture(
{
audio: true,
video: false,
},
function (stream) {
rec.start(stream);
}
Forgive me for lack of documentation as I last played with these APIs years ago, but MDN has some stuff.
In my case adding these 3 lines to the start function was fixed.
this.context = new AudioContext();
this.stream = this.context.createMediaStreamSource(stream);
this.stream.connect(this.context.destination);
class Recorder {
constructor(onChunksAvailable) {
this.chunks = [];
this.active = false;
this.callback = onChunksAvailable;
this.context = new AudioContext();
}
start(stream) {
if (this.active) {
throw new Error("recorder is already running");
}
// Reconnect the stream to actual output
this.stream = this.context.createMediaStreamSource(stream);
this.stream.connect(this.context.destination);
this.recorder = new MediaRecorder(stream, {
mimeType: "audio/webm",
});
this.recorder.onstop = () => {
stream.getAudioTracks()[0].stop();
this.callback([...this.chunks]);
setTimeout(() => {
this.chunks = [];
});
this.active = false;
};
this.recorder.ondataavailable = (event) => this.chunks.push(event.data);
this.active = true;
this.recorder.start();
}
stop() {
if (!this.active) {
throw new Error("recorder is already stop");
} else {
this.recorder.stop();
}
}
}
let rec = new Recorder(async (chunks) => {
//using chunks then to get the stream
});
chrome.tabCapture.capture(
{
audio: true,
video: false,
},
function (stream) {
rec.start(stream);
})
Sorry for lack of details, but I believe when you start an audio capture it disconnects the stream from the default output (speakers). By creating a secondary MediaStreamSource and connecting it to the default output (AudioContext.destination) you can allow the stream to continue outputting to speakers while being input to your recorder.
Sources
MDN: AudioContext
MDN: MediaStreamSource
Chrome extension I made 2 years ago

Categories