I use WebRTC to connected 2 Chrome Browsers. I create offer on the first and send it via signalR to second client like this :
function initiate_call() {
callerPeerConn = new RTCPeerConnection(peerConnCfg);
callerPeerConn.ontrack = function (event) {
console.log('caller recived new stream');
remoteVideo.srcObject = event.streams[0];
console.log(event);
}
navigator.mediaDevices.getUserMedia({ audio: true, video: true })
.then(function (stream) {
localVideo.srcObject = stream;
for (const track of stream.getTracks()) {
callerPeerConn.addTrack(track, stream);
}
return callerPeerConn.createOffer();
})
.then(
function (offer) {
var off = new RTCSessionDescription(offer);
callerPeerConn.setLocalDescription(
new RTCSessionDescription(off),
function () {
// invite to video chat
console.log('send offer');
},
function (err) {
console.log(err.message);
}
)
});
}
When my second browser getting offer he use setLocalDescription and try to create answer than send it to caller like that :
function accept_send_answer(){
calleePeerConn = new RTCPeerConnection(peerConnCfg);
calleePeerConn.ontrack = function (event) {
console.log('callee accept offer and got streams');
remoteVideo.srcObject = event.streams[0];
}
calleePeerConn.setRemoteDescription(offer)
.then(function () {
return navigator.mediaDevices.getUserMedia({ audio: true, video: true });
})
.then(function (stream) {
localVideo.srcObject = stream;
for (const track of stream.getTracks()) {
calleePeerConn.addTrack(track, stream);
}
return calleePeerConn.createAnswer();
})
.then(function (answer) {
// sending answer
console.log("sending ansfer");
var remote_streams = calleePeerConn.getRemoteStreams();
var local_streams = calleePeerConn.getLocalStreams();
console.log("callee remote streams");
console.log(remote_streams);
console.log("callee local streams");
console.log(local_streams);
})
.catch(function (err) {
console.log(err.message);
});
}
UPD
After I changed my code, following the advice of a respected #jib, my local and remote streams on both sides was successfully added to RTCPeerConnection object. I successfully obtain following messanges in console : caller recived new stream and callee accept offer and got streams as well. Last problem is - why this code not working :
calleePeerConn.ontrack = function (event) {
console.log('callee accept offer and got streams');
remoteVideo.srcObject = event.streams[0];
}
video not played.
First, addStream and onaddstream are deprecated, and won't work in other browsers. Use addTrack and ontrack instead.
Second, timing.
You're calling peerConn.createOffer() before peerConn.addStream(stream) so tracks are not picked up.
Same with peerConn.createAnswer() before peerConn.addStream(stream).
Lastly, mixing callbacks and promises confuses the order of things here. Try:
const peerConn = new RTCPeerConnection(peerConnCfg);
peerConn.ontrack = function (event) {
alert('new stream added! ' + event.streams[0]);
}
function initiate_call() {
navigator.mediaDevices.getUserMedia({audio: true, video: true})
.then(function (stream) {
localVideo.srcObject = stream;
for (const track of stream.getTracks()) {
peerConn.addTrack(track, stream);
}
return peerConn.createOffer();
})
.then(function (offer) {
// signaling and invite
return peerConn.setLocalDescription(off);
})
.catch(function (err) {
console.log(err.message);
});
}
function accept_send_answer(offer) {
peerConn.setRemoteDescription(offer)
.then(function () {
return navigator.mediaDevices.getUserMedia({audio: true, video: true});
})
.then(function (stream) {
video.srcObject = stream;
for (const track of stream.getTracks()) {
peerConn.addTrack(track, stream);
}
return peerConn.createAnswer();
})
.then(function (answer) {
//signaling to caller and send answer
return peerConn.setLocalDescription(answer);
})
.catch(function (err) {
console.log(err.message);
});
}
Note that your code (and my reply) still lack critical pieces: ice candidate exchange, and you're not showing your setRemoteDescription(answer) code to complete the negotiation loop.
Note that most examples tend to use the same JS on both sides, like e.g. this working fiddle using iframe postMessage for signaling.
Related
I'm currently doing a bot that receives a youtube url, and technically it must process the video and convert it into mp3. Problem is that the stream is starting before the url is assigned so it returns an error
videoUrl = ""; //this is determined by the last bot.onText function, thats where its asigned
var saveLocation = "";
function saveName(){
return new Promise((resolve) => getInfo(videoUrl).then(info => {
saveLocation = "./"+info.items[0].title+".mp3";
resolve();
}))
}
stream = ytdl(videoUrl) //Problem is that this doesn't wait for that assignment to finish so videoUrl is empty, and I'm not sure how to implement an async there that awaits for the resolution
async function convert(){
const data = await saveName();
new ffmpeg({ source: stream, nolog: true }).toFormat('mp3').audioBitrate(320).on('end', function() {
console.log('file has been converted successfully');
})
.on('error', function(err) {
console.log('an error happened: ' + err.message);
})
.saveToFile(saveLocation);
}
bot.onText(/^(http(s)??\:\/\/)?(www\.)?((youtube\.com\/watch\?v=)|(youtu.be\/))([a-zA-Z0-9\-_])+/gm, (msg) => {
bot.sendMessage(msg.chat.id, msg.text)
videoUrl = msg.text; //this is where the asignmenet should happen
convert();
});
This is my interpretation of how the await should work for the stream, but it doesn't work properly
videoUrl = "";
var saveLocation = "";
function saveName(){
return new Promise((resolve) => getInfo(videoUrl).then(info => {
saveLocation = "./"+info.items[0].title+".mp3";
resolve();
}))
}
async function streaming(){ //made it async
const data = await saveName();
stream = ytdl(videoUrl)
}
async function convert(){
const data = await saveName();
streaming(); //it should be resolved by the time saveName is processed, so it shold start the stream, but it wont
new ffmpeg({ source: stream, nolog: true }).toFormat('mp3').audioBitrate(320).on('end', function() {
console.log('file has been converted successfully');
})
.on('error', function(err) {
console.log('an error happened: ' + err.message);
})
.saveToFile(saveLocation);
}
bot.onText(/^(http(s)??\:\/\/)?(www\.)?((youtube\.com\/watch\?v=)|(youtu.be\/))([a-zA-Z0-9\-_])+/gm, (msg) => {
bot.sendMessage(msg.chat.id, msg.text)
videoUrl = msg.text;
convert();
});
Firstly, in saveName you resolve your return promise to undefined. It should be resolve(saveLocation);.
Secondly, if you want stream to be created after videoUrl is set, then simply move the stream construction line below the videoUrl assignment line.
// change this:
stream = ytdl(videoUrl); // obviously videoUrl will be undefined.
...
bot.onText(..., (msg) => {
bot.sendMessage(msg.chat.id, msg.text)
videoUrl = msg.text; //this is where the asignmenet should happen
convert();
});
// into:
...
bot.onText(..., (msg) => {
bot.sendMessage(msg.chat.id, msg.text)
videoUrl = msg.text; //this is where the asignmenet should happen
stream = ytdl(videoUrl)
convert();
});
I'm trying to do object detection, using tensorflow-js, on the webcam stream
Below is my code:
let model;
var modelPromise = new Promise(function(resolve, reject) {
// Load the model.
model = cocoSsd.load();
$(".progress-bar").hide();
if (model) {
resolve("model loaded!");
}
else {
reject(Error("problem loading model"));
}
});
const video = document.querySelector("#vid");
var camPromise = new Promise(function(resolve, reject) {
if('mediaDevices' in navigator && 'getUserMedia' in navigator.mediaDevices){
navigator.mediaDevices.getUserMedia({ audio: false, video: true })
.then(function (stream) {
video.srcObject = stream;
})
.catch(function (err){
alert(JSON.stringify(error));
});
}
});
// create function to detect objects in the image
const detection = (vid, mod) => {
console.log("hi");
mod.detect(vid).then(predictions => {
//drawBBox(predictions); --> write function for this!
console.log(predictions);
});
requestAnimationFrame(() => detection(vid, mod));
};
Promise.all([modelPromise, camPromise])
.then(values => detection(video, model))
.catch(error => console.error(error));
The webcam stream is generated and displayed, but no predictions are being generated by cocoSsd; am I doing something wrong here?
hello to all I am new in this I have all almost done,but just on my initiator for say something I don't know the way for receiver the remote video stream after receiver the answer, can some one help me please ?
this is my client
$(document).ready(() => {
const configuration = {
iceServers: [{ url: 'stun:stun2.1.google.com:19302' }]
}
var peerConection = null;
// var btnCall = $('body #call');
var list = $('#mylist');
var TitlePrint = $('#titleUser');
var localVideo = document.getElementById('local');
var remoteVideo = document.getElementById('remote');
var userid = null;
var socket = io();
socket.on('connect', () => {
userid = socket.id
TitlePrint.text(userid);
});
socket.on('users', data => {
var users = [];
list.empty();
for (let index = 0; index < data.user.length; index++) {
if (data.user[index] != userid) {
users.push(`<button id="call" class="list-group-item list-group-item-action" data-ids="${data.user[index]}">${data.user[index]}</button>`);
}
}
if (users.length != 0) {
list.html(users);
} else {
list.html(`<div class="list-group-item"> Any users connected! </div>`);
}
});
$('body').on('click', '#call', function () {
let toId = $(this).attr('data-ids');
socket.emit('initiator', { initiatorid: userid, receiverid: toId });
});
socket.on('initiator', data => {
peerConection = createRTC(socket);
if (data.initiatorid === userid) {
console.log('this is the initiator');
initiateSignaling(socket, peerConection, data.receiverid, data.initiatorid);
} else {
console.log('this is the receiver');
prepareToReceiveOffer(socket, peerConection, data.initiatorid, data.receiverid);
}
});
// =============== HELPERS =====================//
function createRTC(socket) {
console.log('createRTC')
var peerConection = new RTCPeerConnection(configuration);
peerConection.onicecandidate = (e) => {
if (e.candidate) {
console.log('emit candidate')
socket.emit('send-candidate', e.candidate);
}
}
socket.on('receiver-candidate', (candidate) => {
peerConection.addIceCandidate(candidate);
});
return peerConection;
}
function initiateSignaling(socket, peerConection, targetID, from) {
navigator.mediaDevices.getUserMedia({ video: true, audio: false }).then((stream) => {
stream.getTracks().forEach(function (track) {
peerConection.addTrack(track, stream);
});
localVideo.srcObject = stream;
peerConection.createOffer().then(function (offer) {
return peerConection.setLocalDescription(offer);
})
.then(function () {
socket.emit('send-offer', {
from: from,
target: targetID,
type: "send-offer",
sdp: peerConection.localDescription
});
})
.catch(function (reason) {
console.log('error on create offer', reason);
});
})
socket.on('receiver-answer', (answer) => {
console.log(answer);
peerConection.setRemoteDescription(answer.sdp);
peerConection.ontrack = function (event) {
remoteVideo.srcObject = event.streams[0];
};
});
}
function prepareToReceiveOffer(socket, peerConection, targetID, from) {
socket.on('receiver-offer', (offer) => {
console.log(offer);
peerConection.setRemoteDescription(offer.sdp);
peerConection.createAnswer().then(function (answer) {
return peerConection.setLocalDescription(answer);
})
.then(function () {
socket.emit('send-answer', {
from: from,
target: targetID,
type: "send-answer",
sdp: peerConection.localDescription
});
});
peerConection.ontrack = function (event) {
remoteVideo.srcObject = event.streams[0];
};
navigator.mediaDevices.getUserMedia({ video: true, audio: false }).then((stream) => {
localVideo.srcObject = stream;
})
});
}
});
I just using socket.io I am handle the offer and answer then on my socket server I just set like this
socket.on('initiator', (init) => {
console.log(init);
io.to('video').emit('initiator', init);
});
socket.on('send-offer', offer => {
console.log('sending offer', offer);
socket.broadcast.emit('receiver-offer', offer);
});
socket.on('send-answer', answer => {
console.log('sending answer', answer);
socket.broadcast.emit('receiver-answer', answer);
});
socket.on('send-candidate', candidate => {
console.log(candidate);
socket.broadcast.emit('receiver-candidate',candidate);
});
I am get video remote on my receiver from the initiator but not in the initiator, I don't know what I miss for getting the remote video thanks so mush guys
The initiator calls addTrack(), but not the receiver doesn't, so this is only sending media one way.
In prepareToReceiveOffer you call getUserMedia() but never add resulting tracks to the peer connection. If you want a two-way call, it needs to call addTrack() as part of the offer/answer negotiation.
Just be sure to call getUserMedia() after setRemoteDescription to not miss ICE candidates:
function prepareToReceiveOffer(socket, peerConection, targetID, from) {
socket.on('receiver-offer', (offer) => {
console.log(offer);
peerConection.setRemoteDescription(offer.sdp)
.then(() => navigator.mediaDevices.getUserMedia({video: true, audio: false}))
.then(stream => {
localVideo.srcObject = stream;
for (const track of stream.getTracks()) {
peerConection.addTrack(track, stream);
}
return peerConection.createAnswer();
})
.then(function (answer) {
return peerConection.setLocalDescription(answer);
})
.then(function () {
socket.emit('send-answer', {
from: from,
target: targetID,
type: "send-answer",
sdp: peerConection.localDescription
});
})
.catch(err => console.log(err.message));
peerConection.ontrack = function (event) {
remoteVideo.srcObject = event.streams[0];
};
});
}
I get resolve it, in this particular case I my initiateSignaling function when I receiver the offer I has to include this
socket.on('receiver-answer', (answer) => {
console.log(answer);
peerConection.setRemoteDescription(answer.sdp)
.then(function () {
return navigator.mediaDevices.getUserMedia({video:true, audio: false});
})
.then(function (stream) {
return peerConection.addStream(stream);
})
peerConection.ontrack = function (event) {
remoteVideo.srcObject = event.streams[0];
};
});
now is working for me
I am following this tutorial to make webrtc work. I would like to return the streams I get in resolve() to the component I call this getDrone() from. I created a returnStreamArray() function as a child promise which I tried to add as a then() to the stream to add it to the array, however I am stuck. I would appreciate any directions.
export default function getDrone() {
return new Promise((resolve, reject) => {
const drone = new Scaledrone('fBAkzbVAOmA2wbU0');
const roomName = 'observable-room';
const configuration = {
iceServers: [
{
urls: 'stun:stun.l.google.com:19302'
}
]
};
let room;
let pc;
const streamArr = [];
...
drone.on('open', error => {
if (error) {
console.error(error);
}
room = drone.subscribe(roomName);
room.on('open', error => {
if (error) {
onError(error);
}
});
..
..
pc.ontrack = event => {
const stream = event.streams[0];
streamArr.push(stream);
};
navigator.mediaDevices
.getUserMedia({
audio: true,
video: true
})
.then(stream => {
streamArr.push(stream);
stream.getTracks().forEach(track => pc.addTrack(track, stream));
}, onError)
.then(returnStreamArray => {
console.log(stream);
});
..
..
});
}
function returnStreamArray() {
return Promise.resolve({
streamArray: streamArr
});
}
I am trying to write a program to get a zip file from s3, unzip it, then upload it to S3.
But I found two exceptions that I can not catch.
1. StreamContentLengthMismatch: Stream content length mismatch. Received 980323883 of 5770104761 bytes. This occurs irregularly.
2. NoSuchKey: The specified key does not exist. This happens when I input the wrong key.
When these two exceptions occur, this program crashes.
I'd like to catch and handle these two exceptions correctly.
I want to prevent a crash.
const unzipUpload = () => {
return new Promise((resolve, reject) => {
let rStream = s3.getObject({Bucket: 'bucket', Key: 'hoge/hoge.zip'})
.createReadStream()
.pipe(unzip.Parse())
.on('entry', function (entry) {
if(entry.path.match(/__MACOSX/) == null){
// pause
if(currentFileCount - uploadedFileCount > 10) rStream.pause()
currentFileCount += 1
var fileName = entry.path;
let up = entry.pipe(uploadFromStream(s3,fileName))
up.on('uploaded', e => {
uploadedFileCount += 1
console.log(currentFileCount, uploadedFileCount)
//resume
if(currentFileCount - uploadedFileCount <= 10) rStream.resume()
if(uploadedFileCount === allFileCount) resolve()
entry.autodrain()
}).on('error', e => {
reject()
})
}
}).on('error', e => {
console.log("unzip error")
reject()
}).on('finish', e => {
allFileCount = currentFileCount
})
rStream.on('error', e=> {
console.log(e)
reject(e)
})
})
}
function uploadFromStream(s3,fileName) {
var pass = new stream.PassThrough();
var params = {Bucket: "bucket", Key: "hoge/unzip/" + fileName, Body: pass};
let request = s3.upload(params, function(err, data) {
if(err) pass.emit('error')
if(!err) pass.emit('uploaded')
})
request.on('httpUploadProgress', progress => {
console.log(progress)
})
return pass
}
This is the library I use when unzipping.
https://github.com/mhr3/unzip-stream
Help me!!
If you'd like to catch the NoSuchKey error thrown by createReadStream you have 2 options:
Check if key exists before reading it.
Catch error from stream
First:
s3.getObjectMetadata(key)
.promise()
.then(() => {
// This will not throw error anymore
s3.getObject().createReadStream();
})
.catch(error => {
if (error.statusCode === 404) {
// Catching NoSuchKey
}
});
The only case when you won't catch error if file was deleted in a split second, between parsing response from getObjectMetadata and running createReadStream
Second:
s3.getObject().createReadStream().on('error', error => {
// Catching NoSuchKey & StreamContentLengthMismatch
});
This is a more generic approach and will catch all other errors, like network problems.
You need to listen for the emitted error earlier. Your error handler is only looking for errors during the unzip part.
A simplified version of your script.
s3.getObject(params)
.createReadStream()
.on('error', (e) => {
// handle aws s3 error from createReadStream
})
.pipe(unzip)
.on('data', (data) => {
// retrieve data
})
.on('end', () => {
// stream has ended
})
.on('error', (e) => {
// handle error from unzip
});
This way, you do not need to make an additional call to AWS to find out if out if it exists.
You can listen to events (like error, data, finish) in the stream you are receiving back. Read more on events
function getObjectStream (filePath) {
return s3.getObject({
Bucket: bucket,
Key: filePath
}).createReadStream()
}
let readStream = getObjectStream('/path/to/file.zip')
readStream.on('error', function (error) {
// Handle your error here.
})
Tested for "No Key" error.
it('should not be able to get stream of unavailable object', function (done) {
let filePath = 'file_not_available.zip'
let readStream = s3.getObjectStream(filePath)
readStream.on('error', function (error) {
expect(error instanceof Error).to.equal(true)
expect(error.message).to.equal('The specified key does not exist.')
done()
})
})
Tested for success.
it('should be able to get stream of available object', function (done) {
let filePath = 'test.zip'
let receivedBytes = 0
let readStream = s3.getObjectStream(filePath)
readStream.on('error', function (error) {
expect(error).to.equal(undefined)
})
readStream.on('data', function (data) {
receivedBytes += data.length
})
readStream.on('finish', function () {
expect(receivedBytes).to.equal(3774)
done()
})
})
To prevent a crash, you need to asynchronously listen to the object's head metadata, where it does not return the whole object, which will take less time. Try this one!
isObjectErrorExists = async functions () => {
try {
const s3bucket = {
secret key: '',
client id: ''
}
const params = {
Bucket: 'your bucket name',
Key: 'path to object'
};
await s3bucket.headObject(params).promise(); // adding promise will let you add await to listen to process untill it completes.
return true;
} catch (err) {
return false; // headObject threw error.
}
throw new Error(err.message);
}
}
public yourFunction = async() => {
if (await this.isObjectErrorExists()) {
s3Bucket.getObject().createReadStream(); // works smoothly
}
}