React app with JsSip library doesn't stream audio - javascript

I'm making an React app that uses JsSip library in order to make and receive calls.
I've made two components, one for making the call, and the other for answering, I'm trying to test them locally, but Answer component doesn't log any incoming calls.
The first one is Phone.tsx component for making calls
import React, { useState, useEffect, useRef } from "react";
import JsSIP from "jssip";
import { OutgoingRTCSessionEvent, UA } from "jssip/lib/UA";
const Phone = () => {
const videoRef = useRef<HTMLVideoElement>(null);
// Create our JsSIP instance and run it:
const socket = new JsSIP.WebSocketInterface("ws://localhost:3000");
const configuration = {
sockets: [socket],
uri: "sip:alice#example.com",
password: "superpassword",
register: true,
};
const userAgent = new JsSIP.UA(configuration);
console.log(userAgent);
userAgent.start();
userAgent.on("connected", function (e) {
console.log("session started");
});
userAgent.on("disconnected", function (e) {
console.log("session ended");
});
userAgent.on("newRTCSession", function (e: OutgoingRTCSessionEvent) {
console.log("RTC session initiated");
});
const getAudio = () => {
navigator.mediaDevices
.getUserMedia({ audio: true })
.then((stream) => {
let audio = audioRef.current;
if (!audio) return;
audio.srcObject = stream;
audio.play();
})
.catch((err) => console.log(err));
};
const handleCall = () => {
getAudio();
const options = {
mediaConstraints: { audio: true, video: false },
};
userAgent.call("sip:bob#example.com", options);
};
return (
<div>
<button onClick={() => handleCall()}>Call</button>
<audio ref={audioRef}></audio>
</div>
);
};
export default Phone;
And the Answer.tsx component for receiving the calls
import React, { useState, useEffect } from "react";
import JsSIP, { UA } from "jssip";
import { IncomingRTCSessionEvent } from "jssip/lib/UA";
const Answer = () => {
const socket = new JsSIP.WebSocketInterface("ws://localhost:3000");
const configuration = {
sockets: [socket],
uri: "sip:bob#example.com",
password: "secret",
};
const userAgent = new JsSIP.UA(configuration);
userAgent.start();
userAgent.on("newRTCSession", (e: IncomingRTCSessionEvent) => {
const session = e.session;
if (session.direction === "incoming") {
console.log("Incoming call from", session.remote_identity.uri.toString());
session.answer({
mediaConstraints: { audio: true, video: false },
});
}
});
return <div>Ready to receive calls!</div>;
};
export default Answer;
I'm running Phone component in one browser, and Answer in another. I'm getting weird audio sounds and echoes once I invoke the getAudio() function, so I'm not sure if it's getting streamed properly to Answer component.

Related

Unable to mock a user defined Axios Class in Javacript/Typescript

I am attaching all the function snippets below. I am using jest to run a unit test on this function but this needs to mock axios. I tried like this :
// TODO - mock axios class instance for skipped Test suites
describe("dateFilters()", () => {
beforeEach(() => {
jest.resetAllMocks();
});
it("Mock Fetch API for Date Options Response", async () => {
const mockFn = jest.fn();
setUpMockResponse(mockFn, mockFetchDateOptionsResponse);
const response = await dateFilters(Workload.WIN32);
expect(mockFn).toHaveBeenCalledTimes(1);
expect(response?.data).toEqual(mockFetchDateOptionsResponse);
});
});
The error I am getting is :
thrown: "Exceeded timeout of 5000 ms for a test.
Use jest.setTimeout(newTimeout) to increase the timeout value, if this is a long-running test."
It seems it is not mocking anything.
All the require function definitons are below:
export const dateFilters = async (platform) => {
const dates = await getKustoResponse({
queryName: platform.toLowerCase().concat("DateFilters"),
platform,
queryParams: {},
});
return dates;
};
export const getKustoResponse = async ({
queryName,
platform,
queryParams,
cluster = "Default",
}: QueryDetail) => {
const dbName = getClusterValue({ platform, cluster, key: "db" });
const url = getClusterValue({ platform, cluster, key: "kustoUrl" });
const postBody = {
db: dbName,
csl: queryParams
? substituteQueryParameters(queries[queryName], queryParams)
: queries[queryName],
};
const apiClient = ApiClient.getInstance();
const response = await apiClient.post(url, postBody, {
headers: {
...kustoApiRequestDefaultConfiguration.headers,
"x-ms-kql-queryName": queryName,
},
timeout: kustoApiRequestDefaultConfiguration.timeout,
});
return response;
};
import Axios, { AxiosInstance } from "axios";
import axiosRetry from "axios-retry";
export class ApiClient {
private static instance: AxiosInstance;
public static getInstance = (): AxiosInstance => {
if (!ApiClient.instance) {
ApiClient.createInstance();
}
return ApiClient.instance;
};
private constructor() {
ApiClient.getInstance();
}
protected static createInstance = () => {
const responseType = "json";
const client = Axios.create({
responseType,
});
axiosRetry(client, apiRetryConfiguration);
client.interceptors.request.use(requestInterceptor);
client.interceptors.response.use(responseInterceptor, errorInterceptor);
ApiClient.instance = client;
};
}
export const requestInterceptor = async (
request: AxiosRequestConfig
): Promise<AxiosRequestConfig> => {
const token = await getKustoToken();
request.headers = { ...request.headers, Authorization: `Bearer ${token}` };
return request;
};
There is no fetch call in your source code. Is it in the apiClient? If so, do this:
jest.spyOn(apiClient, 'post').mockImplementation();
expect(apiClient.post).toHaveBeenCalled();

Correct way of connecting Websocket events to update my React Component

So I'm primarily a C++ backend developer but I'm learning React on the side and I have this really simple file to use websockets.
import React, { useState } from "react";
var serverMessage = "";
var webSocketReady = false;
function connect() {
webSocket = new WebSocket("ws://127.0.0.1:3000/ws")
webSocket.onopen = (event) => {
webSocketReady = true;
};
webSocket.onmessage = function (event) {
serverMessage = JSON.parse(event.data);
};
webSocket.onclose = function (event)
{
webSocketReady = false;
setTimeout(function() {
connect();
}, 1000);
};
webSocket.onerror = function (err)
{
console.log('Socket encountered error: ', err.message, 'Closing socket')
webSocket.close();
};
}
connect();
export default function MyTestComponent({
...props
}) {
const [varThatNeedHooks, setVar] = useState({});
if (!webSocketReady)
{
return (<h1>Could not connect to server retrying ...</h1>);
}
else if (serverMessage == "")
{
return (<h1>Waiting for message from server ...</h1>);
}
else
{
// do stuff with varThatNeedHooks
}
}
I'm looking at the react documentation and I realized this way that I'm connecting the websocket to MyTestComponent has got to be wrong. But I'm not sure what is the "React" way of hooking up the events from the websocket to change what gets rendered. Anyone have any good documents or examples I can follow?
Typically you'd make serverMessage and webSocketReady part of the React state so that when they are updated it triggers a component rerender. Use an useEffect hook to manage the socket instance.
Example:
import React, { useEffect, useState, useRef } from "react";
export default function MyTestComponent({ ...props }) {
const [varThatNeedHooks, setVar] = useState({});
const [serverMessage, setServerMessage] = useState("");
const [webSocketReady, setWebSocketReady] = useState(false);
const [webSocket, setWebSocket] = useState(new WebSocket("ws://127.0.0.1:3000/ws"));
useEffect(() => {
webSocket.onopen = (event) => {
setWebSocketReady(true);
};
webSocket.onmessage = function (event) {
setServerMessage(JSON.parse(event.data));
};
webSocket.onclose = function (event) {
setWebSocketReady(false);
setTimeout(() => {
setWebSocket(new WebSocket("ws://127.0.0.1:3000/ws"));
}, 1000);
};
webSocket.onerror = function (err) {
console.log('Socket encountered error: ', err.message, 'Closing socket');
setWebSocketReady(false);
webSocket.close();
};
return () => {
webSocket.close();
};
}, [webSocket]);
if (!webSocketReady) {
return <h1>Could not connect to server retrying ...</h1>;
} else if (serverMessage == "") {
return <h1>Waiting for message from server ...</h1>;
} else {
// do stuff with varThatNeedHooks
}
}

Why I got only first item | NestJS, WebSocket, Socket.io

I'm developing NestJS app which asks Binance Websocket API for some data. And also created a WebSocket server that sends received data to Front. On the Back side I got all data in console.log. But on the Front I got only the first item. I can't understand what's wrong. Can you help me please?
Coin.gateway.ts
import { MessageBody, SubscribeMessage, WebSocketGateway, WebSocketServer } from '#nestjs/websockets';
import { Server } from 'socket.io';
import { from, of, take, map, Observable } from 'rxjs';
import { Coin } from './classes/coin';
import * as coinlist from './list/coins.json'
#WebSocketGateway(811, {transports: ['websocket', 'polling'], cors: true})
export class CoinGateway {
#WebSocketServer()
server: Server;
#SubscribeMessage('events')
handleMessage(#MessageBody() data: any) {
console.log('data',data)
const coins = new Coin(coinlist, 'usdt', 'miniTicker')
return coins.getCryptoData().pipe(map((c) => {
return c
}))
}
}
Coin.ts
import { GetCryptocurrencies } from "./abstract/get-cryptocurrencies";
import { WebSocket } from "ws";
import { Logger } from "#nestjs/common";
import { Observable } from "rxjs";
export class Coin extends GetCryptocurrencies {
private readonly logger = new Logger(Coin.name)
private baseUrl: string
private url: string
constructor(coin: { name: string, symbol: string }[], pair: string, method: string) {
super(coin, pair, method)
this.baseUrl = 'wss://stream.binance.com:9443/stream?streams='
this.url = coin.map((c) => {
return `${c.symbol.toLowerCase()}${pair}#${method}`
}).join('/')
}
getCryptoData(): any {
const stream$ = new Observable((observer) => {
const ws = new WebSocket(`${this.baseUrl}${this.url}`)
ws.on('open', () => {
this.logger.log('Connection established')
})
ws.onmessage = (msg: any) => {
const message = JSON.parse(msg.data)
observer.next(message)
}
ws.on('close', () => {
this.logger.log('Connection closed')
})
})
return stream$
}
}
Client UI useEffect hook
useEffect(() => {
const socket = io('ws://localhost:811', {transports: ['websocket']})
socket.on('connect', () => {
console.log('Connection established from client')
socket.emit('events', '', (res: any) => {
console.log(res)
})
const engine = socket.io.engine;
console.log(engine.transport.name); // in most cases, prints "polling"
engine.once("upgrade", () => {
// called when the transport is upgraded (i.e. from HTTP long-polling to WebSocket)
console.log(engine.transport.name); // in most cases, prints "websocket"
});
engine.on("packetCreate", ({ type, data }) => {
// called for each packet sent
console.log('Stype', type)
console.log('Sdata', data)
});
})
}, [])
Okay after some hours of researching and I learned that I need just to return this:
return coins.getCryptoData().pipe(map((c) => {
this.server.emit('msg', c)
}))
And receive this message on Front

Axios get request inside setInterval() stops executing after a while even if setInterval() is working properly

I am writing a code to sync youtube videos on 2 different devices(or a different browser) using React and Node.js. In the react code, I am sending get request to the server every second to get the state of the video player. Now, setInterval() is working properly - calling controlPlayer() every second and if I pause or play the video on one device, the video pauses and plays correctly on the other device. But, after a while the axios get request stops executing even though setInterval() calls the function properly - specifically the code inside try block.
import React, { useEffect } from 'react';
import YouTube from 'react-youtube';
import axios from 'axios';
import './App.css';
/*eslint-disable eqeqeq*/
function App() {
var player;
var playerState;
const opts = {
height: '450',
width: '800',
playerVars: {
autoplay: 0,
origin: window.location,
},
};
const onPlayerReady = (event) => {
player = event.target;
}
const onStateChange = async () => {
playerState = player.getPlayerState();
//setPlayerState()
try {
await axios.put('/api/setplayerstate', { playerState });
} catch(error) {
console.log(error);
}
}
const controlPlayer = async () => {
console.log('out');
try {
const { data } = await axios.get('/api/getplayerstate');
playerState = data.playerState;
console.log('in');
if(playerState == 1) {
player.playVideo();
}
else if(playerState == 2) {
player.pauseVideo();
}
else{
player.pauseVideo();
console.log(playerState);
}
} catch(error) {
console.log(error);
}
}
useEffect(() => {
setInterval(controlPlayer, 1000);
}, []);
return (
<div id='player'>
<YouTube
id='video'
videoId='JXeJANDKwDc'
opts={opts}
onReady={onPlayerReady}
onStateChange={onStateChange}
/>
</div>
);
}
export default App;
import express from 'express';
const app = express();
app.use(express.json());
var playerState = -1;
app.put('/api/setplayerstate', (req, res) => {
playerState = req.body.playerState;
});
app.get('/api/getplayerstate', (req, res) => {
console.log(playerState);
res.send({ playerState });
});
app.listen(5000, () => {
console.log('Server started at http://localhost:5000');
});
What am I missing here? Or is there a different logic to implement this?

Unable to get the socket data through binance Websockets

This is my client-side code base. It is working with one of the exchange websockets but not working with this websocket. Any suggestions?
websocket reference: https://github.com/binance-exchange/binance-official-api-docs/blob/master/web-socket-streams.md
import React, { Component, createContext } from "react";
export const Contx = createContext();
export class ConProvider extends Component {
state = {
coins: [],
digCoin: [],
sou: [],
passSocket: undefined
};
componentDidMount() {
this.socketCall();
}
socketCall = () => {
var ws = new WebSocket("wss://stream.binance.com:9443");
var msg = {
"method": "SUBSCRIBE",
"params": "btcusdt#depth",
"id": 1
};
ws.onopen = () => {
ws.send(msg);
};
ws.onmessage = e => {
const value = e.data;
this.setState({
coins: value
});
};
this.setState({
passSocket: ws
});
};
socketClose = () => {
var wss = this.state.passSocket;
wss.close();
};
render() {
console.log(this.state.coins);
// console.log(this.state.coins)
return (
<Contx.Provider
value={{
...this.state,
socketCall: this.socketCall,
socketClose: this.socketClose
}}
>
{this.props.children}
</Contx.Provider>
);
}
}
const ws = new WebSocket('wss://stream.binance.com:9443/ws');
const msg = {
method: 'SUBSCRIBE',
params: ['btcusdt#depth'],
id: 1,
};
ws.onopen = () => {
ws.send(JSON.stringify(msg));
};
Send accepts JSON format, I changed msg to object, passed the array to params and added /ws as mentioned above.
Try with
var ws = new WebSocket("wss://stream.binance.com:9443/ws");

Categories