You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

139 lines
3.9 KiB
TypeScript

// WebRTC Sdp Player
export default class sdpPlayer {
private pc: any;
public player: any;
private statsTimeoutHandler: any;
private statsFun: Function = new Function();
private startFun: Function = new Function();
private errorFun: Function = (e)=>{
clearInterval(this.statsTimeoutHandler);
};
public setView(id: string): void {
this.player = document.getElementById(id);
}
/**
* video play
* @param url .sdp video url
*/
public start(url: string): void {
if (url === '' && !this.player) {
cc.error("need url and set player ID");
this.errorFun(`need url and set player ID`); // cahnge to error Fun
return;
}
// cc.log('Start play');
const offerSdpOption = {
offerToReceiveAudio: true,
offerToReceiveVideo: true
};
this.pc = new RTCPeerConnection();
this.pc.addTransceiver("audio", { direction: "recvonly" });
this.pc.addTransceiver("video", { direction: "recvonly" });
this.pc.oniceconnectionstatechange = this.onIceStateChange(this.pc);
this.pc.stream = new MediaStream();
this.pc.ontrack = this.gotRemoteStream.bind(this);
this.pc.createOffer(offerSdpOption).then((offer) => {
this.pc.setLocalDescription(offer);
const xmlhttp = new XMLHttpRequest(); // new HttpRequest instance
xmlhttp.timeout = 6000;
xmlhttp.onerror = this.errorFun.bind(this); // xmlerror to error Fun
xmlhttp.ontimeout = this.errorFun.bind(this); // timeout to error Fun
xmlhttp.onload = () => {
if (xmlhttp.status == 200) {
const data = JSON.parse(xmlhttp.responseText);
const remoteSdp = data.remoteSdp;
this.pc.setRemoteDescription(
new RTCSessionDescription(remoteSdp),
() => {
cc.log("setRemoteDescription success!");
},
(e) => {
stop();
this.errorFun(e.message); // cahnge to error Fun
cc.log("setRemoteDescription failed, message:" + e.message);
}
);
this.startFun();
this.onStatus();
} else {
this.errorFun(`xml status: ${xmlhttp.status}`); // cahnge to error Fun
}
};
xmlhttp.open("POST", url);
xmlhttp.send(JSON.stringify({ localSdp: offer }));
}).catch((reason) => {
this.errorFun(reason); // cahnge to error Fun
});
}
private onIceStateChange(pc): void {
if (pc) {
if (pc.iceConnectionState == 'disconnected') stop();
cc.log('ICE state: ' + pc.iceConnectionState);
}
}
private gotRemoteStream(m): void {
cc.log("ontrack, kind:" + m.track.kind);
this.pc.stream.addTrack(m.track);
this.player.srcObject = this.pc.stream;
}
// video status event timer
private onStatus(): void {
var checkInterval = 1000; // check every 1s (do not use lower values)
this.statsTimeoutHandler = setInterval(this.statsFun, checkInterval)
}
/**
* video event listener
* @param event start,stats,error
* @param fn listener callback function
*/
public on(event: "start" | "stats" | "error", fn: Function): void {
this[event + "Fun"] = (event === "error") ? (e) => {
clearInterval(this.statsTimeoutHandler);
fn(e);
} : fn;
}
/** video voice (support max and min voice only) */
public setVolume(num: Number): void {
if (this.player) {
if (num == 0) this.player.muted = true;
else this.player.muted = false;
}
}
/** video stop */
public stop(): void {
clearInterval(this.statsTimeoutHandler);
try {
if (this.player) {
const tracks = this.player.srcObject?.getTracks();
this.player.load();
if (tracks) {
tracks.forEach(track => track.stop());
this.pc?.removeStream(this.player.srcObject);
}
}
} catch(ex) {
cc.log(ex);
}
this.pc?.close();
this.pc = null;
}
/** auto play active (need user touch active) */
public autoPlayActive(): void {
if (this.player) {
this.player.muted = false;
this.player.controls = false;
this.player.autoplay = true;
this.player.playsinline = true;
}
}
}