I am implementing webrtc for peerconnection but not working. when i debug this code then i saw it is not coming in
theirConnection.onaddstream = function (e) {
theirVideo.src = window.URL.createObjectURL(e.stream);
};
block. please let me know where i have done wrong? I am using this on my local system and with one webcam only.
function startPeerConnection(stream) {
var configuration = {
"iceServers": [{ "url": "stun:127.0.0.1:8080" }]
};
yourConnection = new mozRTCPeerConnection(configuration);
theirConnection = new mozRTCPeerConnection(configuration);
// Setup stream listening
theirConnection.onaddstream = function (e) {
theirVideo.src = window.URL.createObjectURL(e.stream);
};
// Setup ice handling
yourConnection.onicecandidate = function (event) {
if (event.candidate) {
theirConnection.addIceCandidate(new RTCIceCandidate(event.candidate));
}
};
theirConnection.onicecandidate = function (event) {
if (event.candidate) {
yourConnection.addIceCandidate(new RTCIceCandidate(event.candidate));
}
};
// Begin the offer
yourConnection.createOffer(function (offer) {
yourConnection.setLocalDescription(offer);
theirConnection.setRemoteDescription(offer);
theirConnection.createAnswer(function (offer) {
theirConnection.setLocalDescription(offer);
yourConnection.setRemoteDescription(offer);
});
});
}
You're calling the promise methods. The problem is literally that you're not checking for errors.
Specifically: The legacy callback methods you seek expect at least two arguments: a success callback, and a failure callback.
Without a failure callback, WebIDL name-overloading calls the newer promise methods instead, which take no callbacks and return a promise instead.
Either pass in the required error callbacks everywhere, e.g.:
yourConnection.createOffer(offer => { ... }, err => console.log(err));
Or better, avoid the legacy methods altogether:
function startPeerConnection(stream) {
var config = { iceServers: [{ urls: "stun:127.0.0.1:8080" }] };
yours = new RTCPeerConnection(config);
theirs = new RTCPeerConnection(config);
theirs.onaddstream = e => theirVideo.srcObject = e.stream;
yours.onicecandidate = e => theirs.addIceCandidate(e.candidate);
theirs.onicecandidate = e => yours.addIceCandidate(e.candidate);
yours.addStream(stream); // <-- don't forget
return yours.createOffer()
.then(offer => Promise.all([yours.setLocalDescription(offer),
theirs.setRemoteDescription(offer)]))
.then(() => theirs.createAnswer())
.then(answer => Promise.all([theirs.setLocalDescription(answer),
yours.setRemoteDescription(answer)]));
}
navigator.mediaDevices.getUserMedia({video: true, audio: true})
.then(stream => startPeerConnection(stream))
.catch(e => console.log(e));
<video id="theirVideo" height="120" width="160" autoplay></video>
<script src="https://webrtc.github.io/adapter/adapter-latest.js"></script>
And always check for errors. A createOffer error told me you'd forgotten .addStream(stream).
The promise API is available natively in Firefox, and through adapter.js in Chrome.
Related
I have a very basic Express.js app which I use Jest and Supertest to test. The routes are not set up until the database is connected:
class App {
public app: express.Application;
public mainRoutes: Util = new Util();
constructor() {
this.app = express();
AppDataSource.initialize()
.then(() => {
// add routes which rely on the database
this.mainRoutes.routes(this.app);
})
.catch((error) => console.log(error));
}
}
export default new App().app;
Here is my test:
describe("Util", function () {
test("should return pong object", async () => {
const res = await request(app).get("/ping");
expect(res.statusCode).toEqual(200);
expect(res.body).toEqual({ message: "pong" });
});
});
Since I put in the promise, this has been 404ing. I can't add async to the constructor. I tried refactoring the class to separate the connection with setting up the routes, but it didn't seem to help.
This works:
test("should return pong object", async () => {
setTimeout(async () => {
const res = await request(app).get("/ping");
expect(res.statusCode).toEqual(200);
expect(res.body).toEqual({ message: "pong" });
}, 1000);
});
But obviously I don't want to add a setTimeout. How is this usually done? I am new to testing.
Just remove the setTimeout() and await the call to the application. You should be initializing the application in the beforeAll() method, which I assume you have, to get the application up and running in the testing space. You should also mock your database connection, so you can fake the data you want back, and not have to wait for the external database to actually be available.
// Create a mock for your database, and have it return whatever you need
import <your-database-class> = require('database');
jest.mock('database', () => {
...
});
describe("Util", function () {
beforeAll(async () => {
app = await <whatever you do to launch your application>
});
test('should be defined', () => {
expect(app).toBeDefined();
});
test("should return pong object", async () => {
const res = await request(app).get("/ping");
expect(res.statusCode).toEqual(200);
expect(res.body).toEqual({ message: "pong" });
});
});
I am trying to transfer a video streaming from one browser to another with WebRTC and socket.io. It works just fine in the same network. No image is getting through across different ones.
I use socket-io as a signal server. I register two browsers in a "room" and then start sending signals.
The code which is executed in the browser from which the streaming is sent:
function joinRoom(room) {
if (room === '') {
alert('Please type a room ID')
} else {
data = { room: room};
socket.emit('join', data);
}
}
// SOCKET EVENT CALLBACKS =====================================================
socket.on('room_created', async () => {
console.log('Socket event callback: room_created')
await setLocalStream(mediaConstraints)
socket.emit('startc', {room: roomId, clientip: clientip})
isRoomCreator = true
})
socket.on('full_room', () => {
console.log('Socket event callback: full_room')
alert('The room is full, please try another one')
})
socket.on('startc', async () => {
console.log('Socket event callback: start_call')
if (isRoomCreator) {
rtcPeerConnection = new RTCPeerConnection(iceServers)
addLocalTracks(rtcPeerConnection)
rtcPeerConnection.ontrack = setRemoteStream
rtcPeerConnection.onicecandidate = sendIceCandidate
await createOffer(rtcPeerConnection)
}
})
socket.on('offer', async (event) => {
console.log('Socket event callback: offer')
if (!isRoomCreator) {
rtcPeerConnection = new RTCPeerConnection(iceServers)
addLocalTracks(rtcPeerConnection)
rtcPeerConnection.ontrack = setRemoteStream
rtcPeerConnection.onicecandidate = sendIceCandidate
rtcPeerConnection.setRemoteDescription(new RTCSessionDescription(event))
await createAnswer(rtcPeerConnection)
}
})
socket.on('answer', (event) => {
console.log('answer');
console.log('Socket event callback: webrtc_answer')
rtcPeerConnection.setRemoteDescription(new RTCSessionDescription(event))
})
socket.on('webrtc_ice_candidate', (event) => {
console.log('Socket event callback: webrtc_ice_candidate')
// ICE candidate configuration.
var candidate = new RTCIceCandidate({
sdpMLineIndex: event.label,
candidate: event.candidate,
})
rtcPeerConnection.addIceCandidate(candidate)
})
The code that accepts the streamed media (vuejs):
socket.on("offer", (data) => {
this.$emit("closeWaitingToConnect");
this.createAnswer(data);
});
joinMeToRoom() {
console.log("joinToRoom: ", this.room);
this.$socket.emit("join", this.room);
}, //joinMeToRoom ()
createAnswer: function(event) {
var roomId = this.room.room;
let sessionDescription
this.peer.ontrack = this.setRemoteStream
this.peer.onicecandidate = this.sendIceCandidate
this.peer.setRemoteDescription(new RTCSessionDescription(event))
try {
sessionDescription = this.peer.createAnswer().then((answer) => {
var anwer =
console.log('sessionDescription');
console.log(answer);
this.$socket.emit('answer', {
type: 'webrtc_answer',
sdp: answer,
sessionDescription: JSON.stringify(answer),
roomId,
})
return this.peer.setLocalDescription(answer)
});
} catch (error) {
console.error('cae: '+error)
}
},
getScreenPosition() {
const right = this.$refs.screen.getBoundingClientRect().right;
const bottom = this.$refs.screen.getBoundingClientRect().bottom;
return { bottom: bottom, right: right };
},
setRemoteStream(event) {
console.log('event setRemoteStream');
console.log(event);
var stream_screen = document.querySelector("video");
stream_screen.srcObject = event.streams[0];
stream_screen.play();
var remoteStream = event.stream
},
I have setup my own TURN server and tried paid versions. Still can't get the stream across different networks.
What am I missing?
The error message:
WARN Possible Unhandled Promise Rejection (id: 1):
Error: INVALID_STATE_ERR
send#http://localhost:8081/index.bundle?platform=android&dev=true&minify=false&app=com.dcgymappfrontend&modulesOnly=false&runModule=true:31745:26
initialiseWebsocket#http://localhost:8081/index.bundle?platform=android&dev=true&minify=false&app=com.dcgymappfrontend&modulesOnly=false&runModule=true:100544:21
loadUserData$#http://localhost:8081/index.bundle?platform=android&dev=true&minify=false&app=com.dcgymappfrontend&modulesOnly=false&runModule=true:100610:40
tryCatch#http://localhost:8081/index.bundle?platform=android&dev=true&minify=false&app=com.dcgymappfrontend&modulesOnly=false&runModule=true:7739:23
invoke#http://localhost:8081/index.bundle?platform=android&dev=true&minify=false&app=com.dcgymappfrontend&modulesOnly=false&runModule=true:7912:32
tryCatch#http://localhost:8081/index.bundle?platform=android&dev=true&minify=false&app=com.dcgymappfrontend&modulesOnly=false&runModule=true:7739:23
invoke#http://localhost:8081/index.bundle?platform=android&dev=true&minify=false&app=com.dcgymappfrontend&modulesOnly=false&runModule=true:7812:30
http://localhost:8081/index.bundle?platform=android&dev=true&minify=false&app=com.dcgymappfrontend&modulesOnly=false&runModule=true:7822:21
tryCallOne#http://localhost:8081/index.bundle?platform=android&dev=true&minify=false&app=com.dcgymappfrontend&modulesOnly=false&runModule=true:28596:16
http://localhost:8081/index.bundle?platform=android&dev=true&minify=false&app=com.dcgymappfrontend&modulesOnly=false&runModule=true:28697:27
_callTimer#http://localhost:8081/index.bundle?platform=android&dev=true&minify=false&app=com.dcgymappfrontend&modulesOnly=false&runModule=true:29113:17
_callImmediatesPass#http://localhost:8081/index.bundle?platform=android&dev=true&minify=false&app=com.dcgymappfrontend&modulesOnly=false&runModule=true:29152:17
callImmediates#http://localhost:8081/index.bundle?platform=android&dev=true&minify=false&app=com.dcgymappfrontend&modulesOnly=false&runModule=true:29370:33
__callImmediates#http://localhost:8081/index.bundle?platform=android&dev=true&minify=false&app=com.dcgymappfrontend&modulesOnly=false&runModule=true:3279:35
http://localhost:8081/index.bundle?platform=android&dev=true&minify=false&app=com.dcgymappfrontend&modulesOnly=false&runModule=true:3057:34
__guard#http://localhost:8081/index.bundle?platform=android&dev=true&minify=false&app=com.dcgymappfrontend&modulesOnly=false&runModule=true:3262:15
flushedQueue#http://localhost:8081/index.bundle?platform=android&dev=true&minify=false&app=com.dcgymappfrontend&modulesOnly=false&runModule=true:3056:21
flushedQueue#[native code]
invokeCallbackAndReturnFlushedQueue#[native code]
The useEffect that is being accused of being a problem:
React.useEffect(() => {
// Fetch the token from storage then navigate to our appropriate place
const loadUserData = async () => {
let userData;
try {
userData = await retrieveUserData();
} catch (e) {}
if(userData){
dispatch({ type: 'RESTORE_USER_DATA', userData: userData });
getChatData(userData, setChats, dispatch);
if(userData && !websocketInitialised){
console.log('web init called from *load user data*')
setWebsocketInitialised(true)
initialiseWebsocket(userData);
}
}
else{
dispatch({ type: 'RESTORE_USER_DATA_FAILED'});
}
};
loadUserData();
}, []);
The initialliseWebsocket function
function initialiseWebsocket(userData){
console.log('sending websocket initialisation data.');
websocket.send(JSON.stringify({
'action': 'init',
'data' : {'token': userData.token}
}));
}
the useState that is used above
const [websocketInitialised, setWebsocketInitialised] = React.useState(false);
async function getChatData(userData, setChats, dispatch){
console.log("fetching chat data");
// if we fail to download chat data, pull the old one from FS
const loadOldChatData = async () => {
let chats;
try {
chats = await retrieveChats();
} catch (e) {}
if(chats){
setChats(chats);
console.log("loaded cached chat data") ;
}
else{
setChats([]);
}
};
const onSuccess = (response) => {
if(response['chats']){
storeChats(response['chats']);
setChats(response['chats']);
console.log("chat data synced");
}
else{
loadOldChatData();
}
};
const onFailure = (response) => {
loadOldChatData();
};
fetch(Settings.siteUrl + '/messenger/get_chats/', {
method: "GET",
headers: {
"Content-type": "application/json; charset=UTF-8",
"Authorization": "Token " + userData.token
},
})
.then(response => response.json())
.then(response => {onSuccess(response)})
.catch(response => {onFailure(response)})
}
retrieveUseData() is most likely not the problem as this only started occuring after I added the other code.
Am I not supposed to use states like this or am I supposed to use the async key worked on functions? I tried that but I still have the same issue. You can see on the 4 line of the errors it mentions the 'initialiseWebsocket' function. I am guessing that is the route cause. I assume the solution will be some async version of it...
This error tell us that you didn't or forget to handle error from async code.
I refectory your code a bit, Tell me if you got any error message from console.log(error);
React.useEffect(() => {
// Fetch the token from storage then navigate to our appropriate place
(async () => {
try {
let userData = await retrieveUserData();
dispatch({ type: 'RESTORE_USER_DATA', userData });
await getChatData(userData, setChats, dispatch);
if (websocketInitialised) return;
console.log('web init called from *load user data*')
setWebsocketInitialised(true)
initialiseWebsocket(userData);
} catch (error) {
console.log(error);
dispatch({ type: 'RESTORE_USER_DATA_FAILED' });
}
})();
}, []);
And you should rename getChatData to setChatData, I also simplify those code also...
async function getChatData(userData, setChats, _dispatch) {
try {
let response = await fetch(Settings.siteUrl + '/messenger/get_chats/', {
method: "GET",
headers: {
"Content-type": "application/json; charset=UTF-8",
"Authorization": "Token " + userData.token
},
}),
data = await response.json(),
chats = data['chats'];
if (!chats?.length) throw "empty chat data, pull the old one from FS";
storeChats(chats);
setChats(chats);
} catch (_) {
// if we fail to download chat data, pull the old one from FS
await retrieveChats()
.then(chats => setChats(chats))
.catch(() => setChats([]))
}
}
"I don't really understand what you are doing with the async stuff."
async/await is just syntax sugar of promise, It allow you to work with async operation in a synchronous manner, some rules of async/await
In other to use await keyword, you need an async function.
you can make any function asynchronous, just by adding async keyword
async function always return promise
Lets see an example:
let delay = (ms, msg, bool) => new Promise((res, rej) => setTimeout(!bool ? res : rej , ms,msg));
This helper function create a promise for our example, it take 3 arguments, it take millisecond as 1st arg, to delay, 2rd is the message as payload. 3nd is Boolean; it true, then it will reject.
let delay = (ms, msg, bool) => new Promise((res, rej) => setTimeout(!bool ? res : rej, ms, msg));
let log = console.log;
async function myAsyncFn() {
let hello = await delay(100, "hello,");
let world = await delay(300, " world!");
// we use a symbol '#' to indicate that, its from `myAsyncFn`
log("#" , hello + world, "printed from async operation");
}
myAsyncFn();
log("As you can see that, this message print first");
// we are creating an async function and called immediately, In other to use `await keyword`
(async () => {
try {
let resolved = await delay(300,"resolved");
console.log(">" , `it ${resolved}!`);
// this will reject and catch via `try/catch` block;
let _ = await delay(600, "Error", true);
log("It will not print!");
// ...
} catch (error) {
log(">" , `we can catch "${error}" with try/catch, as like any sync code!`);
}
})()
As you can see that with async/await its look like everything is synchronous right? even everything execute asynchronously!
You just need to use await keyword to make every async operation synchronous.
I have a subscriber redis client instance that is performing a callback when entries in the db expire.. I tried adding an initial unsubscribe call to remove previous any existing listeners, but it does not seem to be working:
const setOnExpire = (onExpire) => {
client.config('set', 'notify-keyspace-events', 'Ex', () => {
subscriber.unsubscribe('__keyevent#0__:expired', 0); // <-- this does not seem to be doing what I was hoping it would...
subscriber.subscribe('__keyevent#0__:expired', () => {
subscriber.on('message', function (channel, key) {
onExpire(key);
});
});
});
};
setOnExpire(() => { console.log('foo'); });
setOnExpire(() => { console.log('bar'); }); // my intention is to replace the callback that logs "foo"
client.hmsetAsync(someKey, someAttrs).then(() => {
client.expireAsync(someKey, 5);
});
I run this, hoping to only see "bar" get logged when the record expires in 5 seconds, however instead, I see "foo" and "bar."
How can I properly remove the pre-existing subscriber.on('message') listeners?
If I understand your question correctly. I think this is not a Redis related problem, it's just an application-level problem. You only need to call subscriber.subscribe once to set up a subscription. You want to support only one callback, so store that callback internally. And every time setOnExpire get called, just replace the callback with a new one. I'm not a JavaScript expert, bellow code snippet works fine on my computer:
var redis = require("redis");
var bluebird = require('bluebird');
bluebird.promisifyAll(redis);
var client = redis.createClient();
var subscriber = redis.createClient();
const setOnExpire = function() {
var notify_on = false;
var promise;
var callback = function(key) { };
return (onExpire) => {
if (notify_on) {
promise.then(()=> {
callback = onExpire;
});
} else {
promise = new Promise((resolve, reject) => {
notify_on = true;
client.config('set', 'notify-keyspace-events', 'Ex', () => {
resolve();
});
});
promise.then(() => {
subscriber.subscribe('__keyevent#0__:expired', () => {
subscriber.on('message', function (channel, key) {
callback(key);
});
});
});
}
};
}();
setOnExpire(() => { console.log('foo'); });
setOnExpire(() => { console.log('bar'); }); // my intention is to replace the callback that logs "foo"
client.hmsetAsync('hello', 'yesl', 'thankyou').then(() => {
client.expireAsync('hello', 5);
});
I wrote a unit test for some Axios calls in my component. I verified the success path, where the call resolves successfully, but I am not able to verify the failure path, where the call rejects. How do I use mocks to verify this?
Here's a snippet of my FetchImage.vue component:
methods: {
preparedFetch() {
axios.get(this.imageurl).then(result => {
this.imageInformation.title = result.data.title;
this.imageInformation.copyright = result.data.copyright;
this.imageInformation.detailExplanation = result.data.explanation;
this.imageInformation.date = result.data.date;
this.imageInformation.urlinfo = result.data.url;
this.resultArrived = true;
this.$emit('imagefetched',this.imageInformation);
})
.catch( error => {
this.errorMessage = "Information not found";
this.resultArrived = true;
});
}
}
And my test for when the call rejects (for an invalid URL):
describe('Invalid response',async () => {
beforeEach(() => {
axios.get.mockClear();
axios.get.mockReturnValue(Promise.reject({}));
});
it('Invalid URL verfication', async () => {
// Given
const result = {
errorMessage : "Information not found",
resultArrived : true,
fetchStatus : true
};
// Fetch the error result
axios.get.mockReturnValue(Promise.resolve(result));
const fetchwrapper = mount(FetchImage);
fetchwrapper.vm.imageurl = "https://invalid.request.gov";
fetchwrapper.vm.preparedFetch();
await fetchwrapper.vm.$nextTick();
// Validate the result
expect(axios.get).not.toHaveBeenCalledWith('https://api.nasa.gov/planetary/apod?api_key=vME6LAMD7IhEiy7rDmjfIaG6MhiKbu1MNIqxtqd1');
expect(axios.get).toHaveBeenCalledWith("https://invalid.request.gov");
expect(axios.get).toHaveBeenCalledTimes(1);
expect(fetchwrapper.vm.errorMessage.length).not.toEqual(0);
expect(fetchwrapper.vm.errorMessage).toBe("Information not found");
});
});
Your catch block isn't running because the mock return value is using Promise.resolve() when it actually should be Promise.reject():
describe('Invalid response',async () => {
it('Invalid URL verfication', async () => {
// axios.get.mockReturnValue(Promise.resolve(result)); // DON'T DO THIS
axios.get.mockReturnValue(Promise.reject(result));
});
});
You have to reject the value by using the built-in jest method.
describe('Invalid response', async () => {
it('Invalid URL verfication', async () => {
axios.get.mockRejectedValue(result);
});
});