Trying to get WebRtc work only for one side. i have media stream server, on which installed Oven Media Engine. Signalling is turned on, and tested via Oven Media Player, everything works as expected. but when trying to reach that content via NativeScript or via any TypeScript free code, stucking on handling "answer" and the result is missing of RemoteVideoStream !
onTrack(event) - is not getting RemoteVideo whatever i do and try...
P.S - first steps in WebRtc, and maybe not figuring out all the logic of that solution, all examples are based on two way connections / call and answers, all i want to do is just get that stream and play without any local streams.
Tried to implement this diagram - 'https://airensoft.gitbook.io/ovenmediaengine/streaming/webrtc-publishing#signalling-protocol' - but unsuccess
Stream Server - 'ws://3.123.0.22:3333/app/test_o' - its turned on and signalling
Oven Media Player - 'http://demo.ovenplayer.com'
import {TNSRTCTrackEvent,TNSRTCPeerConnection,TNSRTCSessionDescription,TNSRTCIceCandidate} from'nativescript-webrtc-plugin';
import { LoggerService } from '../../services';
require('nativescript-websockets');
export class TestTnsRtc {
public webSocket: any;
public connection: any = null;
constructor(private readonly _logger: LoggerService) {
this.connection = new TNSRTCPeerConnection();
this.connection.onIceCandidate((candidate: TNSRTCIceCandidate) => {
this._logger.log('onIceCandidate -> candidate');
const createdCandidate: any = {
candidate: candidate.candidate,
sdp: candidate.sdp,
sdpMid: candidate.sdpMid,
sdpMLineIndex: candidate.sdpMLineIndex,
serverUrl: candidate.serverUrl
};
this._addIceCandidate([createdCandidate]);
});
this.connection.onTrack((track: TNSRTCTrackEvent) => {
if (track.streams) {
this._logger.log('onTrack -> Executed', track.streams[0]);
// this.remoteView.srcObject = track.streams[0];
}
});
this._createSocketConnection();
}
private _createSocketConnection(): void {
const mySocket: any = new WebSocket('ws://3.123.0.22:3333/app/test_o');
mySocket.addEventListener('open', (evt: any) => {
this._logger.log('WebSocket Connected !');
this.webSocket = evt.target;
this._emitSocket({ command: 'request_offer' });
});
mySocket.addEventListener('message', (evt: any) => {
this._logger.log('WebSocket got MESSAGE with type: ', JSON.parse(evt.data).command);
if (JSON.parse(evt.data).command === 'offer') {
const offer: any = {
id: JSON.parse(evt.data).id,
sdp: JSON.parse(evt.data).sdp,
candidates: JSON.parse(evt.data).candidates
};
this._answerOnOffer(offer);
}
});
mySocket.addEventListener('close', (evt: any) => {
this._logger.log(`WebSocket CLOSED with code: ${evt.code} && reason: ${evt.reason}`);
});
mySocket.addEventListener('error', (evt: any) => {
this._logger.log(`WebSocket got ERROR: ${evt.error}`);
});
}
private _answerOnOffer(offer: any): void {
if (!this.connection || !offer) return;
this.connection
.setRemoteDescription(new TNSRTCSessionDescription(offer.sdp.type, offer.sdp.sdp))
.then(() => {
this._addIceCandidate(offer.candidates);
this.connection
.createAnswer({})
.then((sdp: TNSRTCSessionDescription) => {
this._setLocalDescription(sdp, offer.id);
})
.catch((e: any) => {
this._logger.log(`createAnswer : Error -> ${e}`);
});
})
.catch((error: any) => {
this._logger.log(`setRemoteDescription : Error -> ${error}`);
});
}
private _setLocalDescription(sdp: TNSRTCSessionDescription, offerId: any): void {
if (this.connection == null) return;
this.connection
.setLocalDescription(new TNSRTCSessionDescription(sdp.type, sdp.sdp))
.then(() => {
this._logger.log('setLocalDescription Done with offer id: ', offerId);
this._emitSocket({
command: 'answer',
id: offerId,
sdp: { type: 'answer', sdp: sdp.sdp },
candidates: []
});
})
.catch((error: any) => {
this._logger.log(`setLocalDescription : Error -> ${error}`);
});
}
private _addIceCandidate(iceCandidates: TNSRTCIceCandidate[]): void {
for (let iceCandidate of iceCandidates) {
this.connection.addIceCandidate(iceCandidate);
}
}
private _emitSocket(command: any): void {
if (!this.webSocket) {
return;
}
this.webSocket.send(JSON.stringify(command));
}
}
Updated Version
I've tried to implement method [A] - and cut lot of code to simplify as possible.
mySocket.addEventListener('open', (evt: any) => {
evt.target.send(JSON.stringify({ command: 'request_offer' }));
this._webSocket = evt.target;
});
mySocket.addEventListener('message', (evt: any) => {
// if type is answer, calling handler
this._handleOffer(JSON.parse(evt.data));
});
private _handleOffer(offerObject: any): void {
const config: TNSRTCConfiguration = new TNSRTCConfiguration({
iceServers: [new TNSRTCIceServer(['stun:stun.l.google.com:19302'])]
});
const peerConnection: TNSRTCPeerConnection = new TNSRTCPeerConnection(config);
const offer: any = {
command: offerObject.command,
id: offerObject.id,
code: offerObject.code,
peerId: offerObject.peer_id,
sdp: offerObject.sdp,
candidates: offerObject.candidates
};
for (const ice of offer.candidates) {
ice.candidate = this._updateIceIp(ice);
peerConnection.addIceCandidate(ice);
}
const remoteDescription: TNSRTCSessionDescription = new TNSRTCSessionDescription(offer.sdp.type, offer.sdp.sdp);
peerConnection
.setRemoteDescription(remoteDescription)
.then(() => {
peerConnection
.createAnswer({})
.then((sdp: TNSRTCSessionDescription) => {
const localDescription: TNSRTCSessionDescription = new TNSRTCSessionDescription(sdp.type, sdp.sdp);
peerConnection
.setLocalDescription(localDescription)
.then(() => {
const generatedAnswer: any = JSON.stringify({
id: offer.id,
peer_id: offer.peerId,
command: 'answer',
sdp: { type: sdp.type, sdp: sdp.sdp }
});
this._webSocket.send(generatedAnswer);
})
.catch((err: any) => {
this._logger.log(`setLocalDescription -> catch: ${err}`);
});
})
.catch((err: any) => {
this._logger.log(`createAnswer -> catch: ${err}`);
});
})
.catch((err: any) => {
this._logger.log(`setRemoteDescription -> catch: ${err}`);
});
peerConnection.onIceCandidate((candidate: TNSRTCIceCandidate) => {
this._logger.log('EVENT -> onIceCandidate');
if (candidate && candidate.candidate) {
const iceCandidate: any = {
candidate: candidate.candidate,
sdpMid: candidate.sdpMid,
sdpMLineIndex: candidate.sdpMLineIndex
};
const sendData: any = JSON.stringify({
id: offer.id,
peer_id: offer.peerId,
command: 'candidate',
candidates: [iceCandidate]
});
this._webSocket.send(sendData);
}
});
peerConnection.onSignalingStateChange(() => {
this._logger.log('EVENT -> onSignalingStateChange', peerConnection.connectionState);
});
peerConnection.onIceGatheringStateChange(() => {
this._logger.log('EVENT -> onIceGatheringStateChange', peerConnection.connectionState);
});
peerConnection.onConnectionStateChange(() => {
this._logger.log('EVENT -> onConnectionStateChange', peerConnection.connectionState);
});
peerConnection.onTrack((track: TNSRTCTrackEvent) => {
this._logger.log('EVENT -> onTrack');
});
}
private _updateIceIp(candidate: TNSRTCIceCandidate): string {
if (candidate.candidate) {
const ipRegex: RegExp = new RegExp(
/\b(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\b/,
'gi'
);
const ipOfCandidate: any = candidate.candidate.match(ipRegex)[0];
return candidate.candidate.replace(ipOfCandidate, '3.123.0.22');
}
}
- here is offer response from OME - https://prnt.sc/tvccti
- .setRemoteDescription - success
- .createAnswer - success
- .setLocalDescription - success
- here is candidates from .onIceCandidate event, which i'm sending to OME server - https://prnt.sc/tvcg9d
- and here is my logs output - https://prnt.sc/tvch52
my peerConnection.connectionState is always "new", and .onTrack() event invokes before points 4 and 5 whith empty body.