
Teilen Sie:
Manik ist ein leitender Software-Ingenieur. Er arbeitet gerne mit Entwicklern zusammen und entwirft APIs. Wenn er nicht gerade APIs oder SDKs entwickelt, kann man ihn bei Konferenzen und Meetups als Redner antreffen.
Verbindung von WebRTC und PSTN mit OpenTok und Nexmo
Lesedauer: 4 Minuten
Nexmo hat kürzlich angekündigt SIP-Verbindung angekündigt, das es ermöglicht, WebRTC-Endpunkte mit der Nexmo Voice API zu verbinden. Diese Funktion ermöglicht es PSTN-Nutzern, sich in eine OpenTok-Videositzung einzuwählen.
In diesem Beitrag werden wir eine Echtzeit-Video-Webanwendung mit OpenTok erstellen und PSTN-Benutzer mithilfe von SIP Connect und der Voice API mit ihr verbinden.
Voraussetzungen
Bevor wir beginnen, vergewissern Sie sich bitte, dass Sie über die folgenden Informationen verfügen:
Vonage API-Konto
Um dieses Tutorial durchzuführen, benötigen Sie ein Vonage API-Konto. Wenn Sie noch keines haben, können Sie sich noch heute anmelden und mit einem kostenlosen Guthaben beginnen. Sobald Sie ein Konto haben, finden Sie Ihren API-Schlüssel und Ihr API-Geheimnis oben auf dem Vonage-API-Dashboard.
Erste Schritte
Bitte erstellen Sie ein TokBox API Projekt, denn Sie benötigen ein apiKey und apiSecret um die Echtzeit-Voice und -Video zur Web-App hinzuzufügen. Zusätzlich zu den TokBox-Anmeldeinformationen müssen Sie eine Nexmo Voice-Anwendung erstellen und Webhooks für Ereignisse und Antworten festlegen.
Machen Sie sich jetzt keine Gedanken darüber, denn wir werden sie erklären, während wir mit ihnen arbeiten. Zu guter Letzt müssen Sie eine virtuelle Nexmo-Nummer und alle eingehenden Anrufe an diese Nummer an die von Ihnen erstellte Anwendung weiterleiten.
Übersicht

Beispiel-Code
Um zu beginnen, klonen Sie bitte das opentok-nexmo-sip Repository und wechseln Sie in das Dial-In-Konferenz Verzeichnis.
Im Projektverzeichnis finden Sie eine config.example.js Datei. Kopieren Sie bitte den Inhalt dieser Datei in eine neue Datei mit dem Namen config.js.
Stellen Sie sicher, dass Sie die TokBox- und Nexmo-Anmeldeinformationen, die Sie zuvor erstellt haben, in die
config.jsDatei hinzufügen, da wir sie für die App verwenden werden.
Client-seitiger Code
In diesem Fall verwenden wir JavaScript für das Web, aber Sie können die gleichen Concepts mit OpenTok verwenden iOS, Androidund Windows SDKs.
Wie Sie unten sehen können, ist in der opentok.js Datei, die sich im Ordner public/js befindet, initialisieren wir eine Sitzung durch den Aufruf der initSession Methode für das OT Objekt. Anschließend erstellen wir ein Publisher-Objekt mit der Methode initPublisher Methode.
Anschließend werden die folgenden Sitzungsereignisse festgelegt:
streamCreatedstreamDestroyedsessionConnected
Diese Ereignisse werden ausgelöst, wenn ein Stream erstellt wird, wenn ein Stream zerstört wird oder wenn der Client eine Verbindung zur Sitzung herstellt.
Nach dem Einstellen der Ereignis-Listener stellen wir eine Verbindung zur Sitzung her, indem wir das Token und einen Error-Handler übergeben. Der Error-Handler wird verwendet, um sicherzustellen, dass beim Versuch, eine Verbindung mit der Sitzung herzustellen, keine Fehler aufgetreten sind. Wenn in unserer Anwendung ein Fehler auftritt, protokollieren wir ihn in der Konsole, aber in einer Produktionsanwendung sollten wir ein UI-Element anzeigen und versuchen, die Verbindung erneut herzustellen.
const session = OT.initSession(apiKey, sessionId);
const publisher = OT.initPublisher('publisher');
session.on({
streamCreated: (event) => {
const subscriberClassName = `subscriber-${event.stream.streamId}`;
const subscriber = document.createElement('div');
subscriber.setAttribute('id', subscriberClassName);
document.getElementById('subscribers').appendChild(subscriber);
session.subscribe(event.stream, subscriberClassName);
},
streamDestroyed: (event) => {
console.log(`Stream ${event.stream.name} ended because ${event.reason}.`);
},
sessionConnected: (event) => {
session.publish(publisher);
},
});
session.connect(token, (error) => {
if (error) {
console.log('error connecting to session');
}
});
Diese opentok.js Datei wird in die Ansicht importiert, die sich in views/index.ejs Datei.
Zusätzlich zu diesem Code erstellen wir auch einige Schaltflächen, die API-Anfragen an den App-Server auslösen, um sich über SIP auszuwählen.
In dieser Anwendung wird diese Ansicht von unserem Server gerendert, aber Sie können sie nach Belieben darstellen. Um den Code zu sehen, in dem diese Ansicht gerendert wird, sehen Sie sich bitte den folgenden Link.
Serverseitiger Code
Nachdem wir nun unseren Client eingerichtet haben, schauen wir uns den Server-Code.
Sie werden feststellen, dass wir Folgendes importieren express, opentok, und body-parser Pakete importieren. Wir verwenden Express.js für unseren Server, das OpenTok Node SDKund Body-Parser Bibliothek, die zum Parsen des Textes eingehender Anfragen verwendet wird.
Beachten Sie, dass wir auch die
configdie wir in der Dateiconfig.jsDatei festgelegt haben.
Anschließend erstellen wir die folgenden Endpunkte:
/room/:roomId/dial-out/hang-up/nexmo-answer/nexmo-dtmf/nexmo-events
Der /room/:roomId Pfad ist der Hauptpfad für unsere Anwendung. Er rendert die index.ejs Ansicht mit den entsprechenden OpenTok-Anmeldeinformationen. Wenn dies geschieht, stellt der App-Server eine Anfrage an OpenTok, um eine Sitzung zu erstellen, die dann mit einem Sitzungsobjekt antwortet, das die sessionId.
Die sessionId wird dann verwendet, um ein OpenTok-Token zu erzeugen. Wir generieren dann einen 4-stelligen Pin-Code und ordnen diesen dem sessionId und den Zimmernamen. Dies ist wichtig, da wir diesen Code benötigen, um den sessionId wenn der PSTN-Benutzer sich einwählt. Wir haben auch eine Logik hinzugefügt, um die Benutzer in dieselbe Sitzung zu bringen, wenn sie eine Anfrage mit derselben roomId stellen.
/**
* When the room/:roomId request is made, either a template is rendered is served with the
* sessionid, token, pinCode, roomId, and apiKey.
*/
app.get('/room/:roomId', (req, res) => {
const { roomId } = req.params;
let pinCode;
if (app.get(roomId)) {
const sessionId = app.get(roomId);
const token = generateToken(sessionId);
pinCode = app.get(sessionId);
renderRoom(res, sessionId, token, roomId, pinCode);
} else {
pinCode = generatePin();
OT.createSession({
mediaMode: 'routed',
}, (error, session) => {
if (error) {
return res.send('There was an error').status(500);
}
const { sessionId } = session;
const token = generateToken(sessionId);
app.set(roomId, sessionId);
app.set(pinCode, sessionId);
renderRoom(res, sessionId, token, roomId, pinCode);
});
}
});
Auswählen
Um sich zum SIP-Endpunkt einzuwählen, stellt der Browser eine Anfrage an den /dial-out Endpunkt und der Server generiert ein Token und verwendet unsere Nexmo-Anmeldedaten (API-Schlüssel und API-Geheimnis) zusammen mit der SIP-URI (sip:lvn@sip.nexmo.com), um eine Anfrage an OpenTok zu stellen, um sich für die Sitzung auszuwählen. Wenn dies gelingt, erhalten wir Verbindungsinformationen über den Callback für die SIP-Teilnehmer.
/**
* When the dial-out get request is made, the dial method of the OpenTok Dial API is invoked
*/
app.get('/dial-out', (req, res) => {
const { roomId } = req.query;
const { conferenceNumber } = config;
const sipTokenData = `{"sip":true, "role":"client", "name":"'${conferenceNumber}'"}`;
const sessionId = app.get(roomId); // grabbing the sessionId from the mapping we created earlier
const token = generateToken(sessionId, sipTokenData);
const options = setSipOptions();
const sipUri = `sip:${conferenceNumber}@sip.nexmo.com;transport=tls`;
OT.dial(sessionId, token, sipUri, options, (error, sipCall) => {
if (error) {
res.status(500).send('There was an error dialing out');
} else {
app.set(conferenceNumber + roomId, sipCall.connectionId);
res.json(sipCall);
}
});
});
Die Einwahl in die Nexmo Voice API über SIP löst den Ereignis-Webhook, /nexmo-events in unserem Fall, für jede Statusänderung, d.h. started, ringing, etc.
app.get('/nexmo-events', (req, res) => {
console.log('call event', req.query);
res.status(200).send();
});
Zusätzlich zur Ereignis-URL wird ein Antwort-Webhook aufgerufen, damit der Anwendungsserver Voice API mitteilen kann, was mit dem Anruf geschehen soll.
Die Voice API erwartet dies im Format einer NCCO, einem JSON-Objekt, in dem der Anwendungsserver die Aktion(en) angibt.
In unserem Fall wird der App-Server die action als conversation und verwendet die sessionId als den Namen der conversation. Wir bestimmen die sessionId entweder anhand der SIP-Header, die OpenTok bei der Anwahl hinzufügt, oder durch den vierstelligen Pin-Code, den der Nutzer per DTMF.
app.get('/nexmo-answer', (req, res) => {
const { serverUrl } = config;
const ncco = [];
if (req.query['SipHeader_X-OpenTok-SessionId']) {
ncco.push({
action: 'conversation',
name: req.query['SipHeader_X-OpenTok-SessionId'],
});
} else {
ncco.push(
{
action: 'talk',
text: 'Please enter a a pin code to join the session'
},
{
action: 'input',
eventUrl: [`${serverUrl}/nexmo-dtmf`]
}
)
}
res.json(ncco);
});
Im obigen Code haben wir eine bedingte Anweisung, die die Kopfzeilen überprüft, um die sessionId. Wenn in diesem Fall keine SIP-Header vorhanden sind, können wir davon ausgehen, dass sich ein PSTN-Benutzer einwählt, sodass wir ihn zur Eingabe einer PIN auffordern können. Wir tun dies, indem wir die Anweisung action auf talk mit einigen text. Der App-Server teilt der Voice API auch mit, dass wir den dtmf Code über den /nexmo-dtmf Webhook.
Aufforderung zur Eingabe einer PIN
Wenn der /nexmo-dtmf Webhook aufgerufen wird, prüfen wir auf den dtmf Code im Anforderungskörper und suchen den sessionId basierend auf dem Mapping. So stellen wir sicher, dass wir die WebRTC- und PSTN-Benutzer im selben Gespräch miteinander verbinden. Auf diese Weise können Sie auch dieselbe virtuelle Telefonnummer mit mehreren Pincodes wiederverwenden, um gleichzeitige Konferenzen zu ermöglichen.
app.post('/nexmo-dtmf', (req, res) => {
const { dtmf } = req.body;
let sessionId;
if (app.get(dtmf)) {
sessionId = app.get(dtmf);
}
const ncco = [
{
action: 'conversation',
name: sessionId,
}];
res.json(ncco);
});
Wir haben einen /hang-up Endpunkt, an dem wir die forceDisconnect Methode auf das OpenTok Objekt verwenden können, um die Verbindung zum SIP-Teilnehmer zu trennen.
/**
* When the hang-up get request is made, the forceDisconnect method of the OpenTok API is invoked
*/
app.get('/hang-up', (req, res) => {
const { roomId } = req.query;
const { conferenceNumber } = config;
if (app.get(roomId) + app.get(conferenceNumber + roomId)) {
const sessionId = app.get(roomId);
const connectionId = app.get(conferenceNumber + roomId);
OT.forceDisconnect(sessionId, connectionId, (error) => {
if (error) {
res.status(500).send('There was an error hanging up');
} else {
res.status(200).send('Ok');
}
});
} else {
res.status(400).send('There was an error hanging up');
}
});
Zum Schluss geben wir einen Port an und starten den Express-Server:
const port = process.env.PORT || '3000';
app.listen(port, () => console.log(`listening on port ${port}`));
Schlussfolgerung
In diesem Beitrag haben wir die Überbrückung einer OpenTok-Sitzung mit PSTN-Benutzern mithilfe von SIP Connect und der Nexmo Voice API behandelt. Um den vollständigen Code mit anderen SIP-Beispielen zu sehen, besuchen Sie bitte das opentok-nexmo-sip Repo.
