Using own stream on renegotiation - janus-gateway

I want to use my own stream when I do a renegotiation. The standard way is to pass the media parameter to the createOffer function. In this case, Janus handles the getUserMedia request.
async switchCamera() {
this.videoCall.createOffer({
media: {
replaceVideo: true
},
success: (jsep) => {
this.videoCall.send({message: {request:"set"}, "jsep": jsep});
},
error: (e) => {
alert(e);
}
});
}
In my case, I want to do it by myself. I've created the following function:
async switchCamera() {
cameraHelper.switchCamera(); // Sets facing mode
this.localStream = await cameraHelper.requestMedia(); // Returns media stream
this.videoCall.createOffer({
stream: this.localStream,
success: (jsep) => {
this.videoCall.send({message: {request:"set"}, "jsep": jsep});
},
error: (e) => {
alert(e);
}
});
}
If I call the switchCamera function, Chrome crashes on the receivers side. The last error message before the crash is the following one:
Uncaught DOMException: Failed to execute 'addTrack' on 'RTCPeerConnection': A sender already exists for the track.
Do I have to execute some other functions before I can add a new stream?

Related

YouTube PlaylistItems API causing syntax errors when using JSON.parse()

I have 2 calls to the YouTube v3 API in my NodeJS code: channels and PlaylistItems. They both return JSON and the response to the first call is parsed just fine, but parsing the response to the second call causes a syntax error. I am uncertain whether it's an error on my side or in the PlaylistItems API endpoint.
Here is my code (taken out irrelevant parts):
// At start of the bot, fetches the latest video which is compared to if an announcement needs to be sent
function setLatestVideo () {
fetchData().then((videoInfo) => {
if (videoInfo.error) return;
latestVideo = videoInfo.items[0].snippet.resourceId.videoId;
});
}
// Fetches data required to check if there is a new video release
async function fetchData () {
let path = `channels?part=contentDetails&id=${config.youtube.channel}&key=${config.youtube.APIkey}`;
const channelContent = await callAPI(path);
path = `playlistItems?part=snippet&maxResults=1&playlistId=${channelContent.items[0].contentDetails.relatedPlaylists.uploads}&key=${config.youtube.APIkey}`;
const videoInfo = await callAPI(path);
return videoInfo;
}
// Template HTTPS get function that interacts with the YouTube API, wrapped in a Promise
function callAPI (path) {
return new Promise((resolve) => {
const options = {
host: 'www.googleapis.com',
path: `/youtube/v3/${path}`
};
https.get(options, (res) => {
if (res.statusCode !== 200) return;
const rawData = [];
res.on('data', (chunk) => rawData.push(chunk));
res.on('end', () => {
try {
resolve(JSON.parse(rawData));
} catch (error) { console.error(`An error occurred parsing the YouTube API response to JSON, ${error}`); }
});
}).on('error', (error) => console.error(`Error occurred while polling YouTube API, ${error}`));
});
}
Examples of errors I'm getting: Unexpected token , in JSON and Unexpected number in JSON
Till ~2 weeks ago this code used to work just fine without throwing any errors, I have no clue what has changed and can't seem to figure it out. What could possibly be causing this?
10 minutes later I figure out the fix! The variable rawData holds a Buffer and looking more into that, I figured that I should probably use Buffer.concat() on rawData before calling JSON.parse() on it. Turns out, that's exactly what was needed.
I'm still unsure how this was causing problems only 6 months after writing this code, but that tends to happen.
Changed code:
res.on('end', () => {
try {
resolve(JSON.parse(Buffer.concat(rawData)));
} catch (error) { console.error(`An error occurred parsing the YouTube API response to JSON, ${error}`); }
});

Stream audio to Dialogflow with chunks from browser

We're doing some experimenting with Dialogflow and we've run into a complete stop for the time being. We're trying to set up a browser client that streams audio in chunks to Dialogflow via the node v2beta1 version of the dialogflow npm package. We followed the example to get it running and it works fine when we use the node server to pick up the sound via extra software (sox), but we want to stream from the browser. So we've set up a small code snippet that picks up the MediaStream from the mic.
When the data event is triggerend we get a chunk (an arraybuffer) that we, in chunks, pass to our node server.
On the server we've followed this example: https://cloud.google.com/dialogflow-enterprise/docs/detect-intent-stream#detect-intent-text-nodejs. The only thing we do different is instead of using pump to chain streams, we just write our chunks to the sessionsClient.
streamingDetectIntent().write({ inputAudio: [chunk] })
During experimentation we received several errors that we solved. But at this point we pass our chunks and receive empty responses, during and at the end.
Is this a valid way of passing audio to dialogflow, or do we really need to set up a stream? We do not want to use the node server as an entry, it needs to be the browser. We will have full control.
Client
import getUserMedia from 'get-user-media-promise';
import MicrophoneStream from 'microphone-stream';
export const startVoiceStream = () => {
const microphoneStream = new MicrophoneStream();
getUserMedia({ video: false, audio: true })
.then(function(micStream) {
microphoneStream.setStream(micStream);
socket.emit('startMicStream');
state.streamingMic = true;
setTimeout(() => {
// Just closing the stream on a timer for now
socket.emit('endMicStream');
}, 5000);
})
.catch(function(error) {
console.log(error);
});
microphoneStream.on('data', function(chunk) {
if (state.streamingMic) {
socket.emit('micStreamData', chunk);
}
});
};
Server code is much longer so I think I'll spare the details, but these are the main parts.
const initialStreamRequest = {
session: sessions.sessionPath,
queryParams: {
session: sessions.sessionPath, //TODO: try to delete
},
queryInput: {
audioConfig: {
audioEncoding: 'AUDIO_ENCODING_LINEAR_16',
sampleRateHertz: '16000',
languageCode: 'en-US',
},
singleUtterance: false
},
};
const startRecognitionStream = socketClient => {
streamIntent = sessions.sessionClient
.streamingDetectIntent()
.on('error', error => {
console.error({ error });
socketClient.emit('streamError', error);
})
.on('data', data => {
socketClient.emit('debug', { message: 'STREAM "ON DATA"', data });
if (data.recognitionResult) {
socketClient.emit(
'playerTranscript',
data.recognitionResult.transcript,
);
console.log(
`#Intermediate transcript : ${data.recognitionResult.transcript}`,
);
} else {
socketClient.emit('streamAudioResponse', data);
}
});
streamIntent.write(initialStreamRequest);
};
socket.on('micStreamData', data => {
if (streamIntent !== null) {
stop = true;
streamIntent.write({ inputAudio: data });
}
});

Twilio Programmable Chat REST API response events

twilioClient.chat.services(service_SID)
.channels
.each(channels => console.log(channels.sid));
From the above code, how can I check if the request is Success or not.
What I tried is :
twilioClient.chat.services(service_SID)
.channels
.each(channels => console.log(channels.sid))
.then(function (err, docs) {
if (err) {
//console.log('error ' + err);
return res.status(500).send('Problem in retrieving channels');
}
res.status(200).json({
message: 'Channels retrieved sucessfully',
docs: docs
});
})
I need something like this to know the response. Did I need to promise?. I dont know about promise yet. Can someone pls provide an example or tutorial.
Twilio developer evangelist here.
When using the each function to map over the remote resource, it's not using a Promise. each expects to work. However, you can provide a function to each that can be called once the request is complete or if there is an error. You can pass that function as the option done in the second argument. Here is how you would do that:
twilioClient.chat.services(service_SID)
.channels
.each((channel => console.log(channel.sid)), { done: error => {
if (error) {
console.error("There was an error loading the channels.", error);
} else {
console.log("All the channels were successfully loaded.")
}
});
If you are looking to load the channels in one go, then each might not be the right function for you. You can also use list which returns the list of channels rather than a channel at a time. For example:
twilioClient.chat.services(service_SID)
.channels
.list({ limit: 50 }, (error, channels) => {
if (error) {
console.error("There was an error loading the channels.", error);
} else {
console.log("Here are your channels: ", channels);
}
});
Let me know if that helps at all.

Send response to client after Promise resolves - Adonis, Node.js

I have some promise
getSomeInfo(data) {
return new Promise((resolve, reject) => {
/* ...some code... */
someObject.getData((err, info) => {
if (info) {
resolve(info)
}
else {
reject("Error")
}
})
})
}
I use this promise and want to send response to client from Controller (AdonisJS):
async create ({ request, response }) {
this.getSomeInfo(data).then(info => {
console.log(info) // It's work, i get the data from promise
response.status(201).json({ // but this is not work
code: 201,
message: "Data received!",
data: info
})
})
}
Why response is not work?
Simply do this.
async create ({ request, response }) {
const info = await this.getSomeInfo(data)
console.log(info)
response.status(201).json({
code: 201,
message: "Data received!",
data: info
})
}
When marking a function as async the function must return a Promise, this can be done explicitly.
async create({ request, response }) {
return this.getSomeInfo(data).then(info => {
console.log(info) // It's work, i get the data from promise
response.status(201).json({ // but this is not work
code: 201,
message: "Data received!",
data: info
})
})
}
Or implicitly using the await keyword.
async create({ request, response }) {
const info = await this.getSomeInfo(data)
console.log(info) // It's work, i get the data from promise
response.status(201).json({ // but this is not work
code: 201,
message: "Data received!",
data: info
})
}
If your console.log(info) inside of create() works and shows the data you want, but the response.status(201).json(...) does not send a response, then I can see the following possibilities:
You've already sent a response to this request (and thus cannot send another one)
The .json() method is having trouble converting info to JSON (perhaps because of circular references) and throwing an exception.
You aren't passing the arguments request and response properly and thus response isn't what it is supposed to be.
You can test for the second case like this:
create ({ request, response }) {
this.getSomeInfo(data).then(info => {
console.log(info) // It's work, i get the data from promise
response.status(201).json({ // but this is not work
code: 201,
message: "Data received!",
data: info
});
}).catch(e => {
console.log("Error in create()", e);
response.sendStatus(500);
});
}
Also, there is no reason for this method to be declared async as you don't show that you're using await or any of the features of an async function.
In the comments, you say that this function is called directly by a router (I assume an Express router). If that's the case, then the function arguments are not declared properly as they come as two separate arguments, not as properties of an object. Change the function declaration to this:
create (request, response) { ... }

Service Stack SSE Javascript Client - Uncaught TypeError

I'm working on a simple SSE javascript client and reviewing the various examples the following should work:
var source = new EventSource(
'/event-stream?channel=siteevent&t=' + new Date().getTime());
source.addEventListener('error', function (e) {
console.log(e);
addEntry({ msg: "ERROR!", cls: "error" });
}, false);
$.ss.eventReceivers = { "document": document };
$(source).handleServerEvents({
handlers: {
onHeartbeat: function (msg, e) { if (console)
console.log("onHeartbeat", msg, e); },
onMessage: function (msg, e) { // fired after every message
console.log(msg);
}
},
receivers: {
//... Register any receivers
}
});
It generates an error. If I add all the handlers they generate more of the same errors.
jquery 2.1.1
Uncaught TypeError: fn.cal is not a function
at EventSource.onMessage (ss-utils.js:566)
jquery 3.1.x
Uncaught TypeError: Cannot assign to read only property 'target' of
object '#'
at Function.r.extend.r.fn.extend (jquery.min.js:2)
at EventSource.onMessage (ss-utils.js:511)
There is a bug in the /js/ss-utils.js included script on line 566. It should be fn.call instead of fn.cal. I'm going to leave this answer in case others are having issues with the js SSE library included with Service Stack (core).

Resources