WebRTC Shim added
This commit is contained in:
		
							parent
							
								
									c7a82c00f2
								
							
						
					
					
						commit
						1f0b90e332
					
				| 
						 | 
				
			
			@ -22,6 +22,12 @@ app.get("/script",(request, response)=>{
 | 
			
		|||
app.get("/index.js.map",(request, response)=>{
 | 
			
		||||
    response.sendFile(resolve("./script/index.js.map"))
 | 
			
		||||
});
 | 
			
		||||
app.get("/webrtc.js",(request, response)=>{
 | 
			
		||||
    response.sendFile(resolve("./script/webrtc.js"))
 | 
			
		||||
});
 | 
			
		||||
app.get("/webrtc.adapter.js",(request, response)=>{
 | 
			
		||||
    response.sendFile(resolve("./script/webrtc.adapter.js"))
 | 
			
		||||
});
 | 
			
		||||
app.get("*",(request, response)=>{
 | 
			
		||||
    response.sendFile(resolve("./script/status.xml"))
 | 
			
		||||
});
 | 
			
		||||
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							| 
						 | 
				
			
			@ -0,0 +1,401 @@
 | 
			
		|||
function WebRTC()
 | 
			
		||||
{
 | 
			
		||||
    this.id = null;
 | 
			
		||||
    this.active = false;
 | 
			
		||||
    this.connectionStatus = "";
 | 
			
		||||
    this.iceStatus = "";
 | 
			
		||||
    this.gatheringStatus = "";
 | 
			
		||||
    this.signalingStatus = "";
 | 
			
		||||
    this.rtc = new RTCPeerConnection({
 | 
			
		||||
        iceCandidatePoolSize: 0,
 | 
			
		||||
        iceTransportPolicy:"all",
 | 
			
		||||
        rtcpMuxPolicy:"require",
 | 
			
		||||
        iceServers:[{
 | 
			
		||||
            urls: "stun:stun.l.google.com:19302"
 | 
			
		||||
        },{
 | 
			
		||||
            urls: "stun:stun1.l.google.com:19302"
 | 
			
		||||
        },{
 | 
			
		||||
            urls: "stun:stun2.l.google.com:19302"
 | 
			
		||||
        },{
 | 
			
		||||
            urls: "stun:stun3.l.google.com:19302"
 | 
			
		||||
        },{
 | 
			
		||||
            urls: "stun:stun4.l.google.com:19302"
 | 
			
		||||
        }]
 | 
			
		||||
    });
 | 
			
		||||
    /**
 | 
			
		||||
     * @type {Map<string, {stream:MediaStream?,id:string,name:string}>}
 | 
			
		||||
     */
 | 
			
		||||
    this.recaivingStream = new Map();
 | 
			
		||||
    /**
 | 
			
		||||
     * @type {Map<string, {stream:MediaStream?,id:string,name:string}>}
 | 
			
		||||
     */
 | 
			
		||||
    this.sendingStream = new Map();
 | 
			
		||||
    this.rtc.addEventListener("connectionstatechange",(...args)=>{
 | 
			
		||||
        this.eventConnectionState(...args);
 | 
			
		||||
    })
 | 
			
		||||
    this.rtc.addEventListener("icecandidate",(...args)=>{
 | 
			
		||||
        this.eventIcecandidate(...args);
 | 
			
		||||
    })
 | 
			
		||||
    this.rtc.addEventListener("iceconnectionstatechange",(...args)=>{
 | 
			
		||||
        this.eventICEConnectionState(...args);
 | 
			
		||||
    })
 | 
			
		||||
    this.rtc.addEventListener("icegatheringstatechange",(...args)=>{
 | 
			
		||||
        this.eventICEGatherinState(...args);
 | 
			
		||||
    })
 | 
			
		||||
    this.rtc.addEventListener("negotiationneeded",(...args)=>{
 | 
			
		||||
        this.eventNogationNeeded(...args);
 | 
			
		||||
    })
 | 
			
		||||
    this.rtc.addEventListener("signalingstatechange",(...args)=>{
 | 
			
		||||
        this.eventSignalingState(...args);
 | 
			
		||||
    })
 | 
			
		||||
    this.rtc.addEventListener("track",(...args)=>{
 | 
			
		||||
        this.eventTrack(...args);
 | 
			
		||||
    })
 | 
			
		||||
    this.rtc.addEventListener("datachannel",(...args)=>{
 | 
			
		||||
        this.eventDatachannel(...args);
 | 
			
		||||
    })
 | 
			
		||||
    let events = {};
 | 
			
		||||
    /**
 | 
			
		||||
     * @param {Function} callback 
 | 
			
		||||
     */
 | 
			
		||||
    this.addEventListener = function(event,callback){
 | 
			
		||||
        (events[event] || (events[event]=[])).push(callback);
 | 
			
		||||
    };
 | 
			
		||||
    this.on = this.addEventListener;
 | 
			
		||||
    this.dispatch = async (event,...args) => {
 | 
			
		||||
        if(events[event]) for (const callback of events[event]) {
 | 
			
		||||
            await callback(...args)
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    this.emit = this.dispatch;
 | 
			
		||||
    /**
 | 
			
		||||
     * @type {RTCDataChannel}
 | 
			
		||||
     */
 | 
			
		||||
    this.channel = null;
 | 
			
		||||
 | 
			
		||||
    this.on('input',async (data)=>{
 | 
			
		||||
        switch(data.type)
 | 
			
		||||
        {
 | 
			
		||||
            case "icecandidate":{
 | 
			
		||||
                await this.rtc.addIceCandidate(new RTCIceCandidate(data.value));
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
            case "offer":{
 | 
			
		||||
                await this.rtc.setRemoteDescription(new RTCSessionDescription(data.value));
 | 
			
		||||
                let answer = await this.rtc.createAnswer({
 | 
			
		||||
                    offerToReceiveAudio: true,
 | 
			
		||||
                    offerToReceiveVideo: true
 | 
			
		||||
                })
 | 
			
		||||
                await this.rtc.setLocalDescription(answer);
 | 
			
		||||
                this.send({
 | 
			
		||||
                    type: 'answer',
 | 
			
		||||
                    value: answer
 | 
			
		||||
                });
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
            case "answer":{
 | 
			
		||||
                await this.rtc.setRemoteDescription(new RTCSessionDescription(data.value))
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
            case "streamInfo":{
 | 
			
		||||
                let {id,value} = data;
 | 
			
		||||
                if(!this.recaivingStream.has(id))
 | 
			
		||||
                {
 | 
			
		||||
                    this.recaivingStream.set(id,{
 | 
			
		||||
                        stream: null
 | 
			
		||||
                    });
 | 
			
		||||
                };
 | 
			
		||||
                Object.assign(this.recaivingStream.get(id), value);
 | 
			
		||||
                this.send({
 | 
			
		||||
                    type:'streamAccept',
 | 
			
		||||
                    id
 | 
			
		||||
                })
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
            case "streamRemoved":{
 | 
			
		||||
                let {id} = data;
 | 
			
		||||
                this.emit('stream:stopped', this.recaivingStream.get(id));
 | 
			
		||||
                this.sendingStream.delete(id);
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
            case "streamAccept":{
 | 
			
		||||
                let {id} = data;
 | 
			
		||||
                let {stream} = this.sendingStream.get(id);
 | 
			
		||||
                let senders = [];
 | 
			
		||||
                for (const track of stream.getTracks()) {
 | 
			
		||||
                    senders.push(this.rtc.addTrack(track, stream));
 | 
			
		||||
                };
 | 
			
		||||
                stream.senders = senders;
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
            case "message":{
 | 
			
		||||
                this.emit('message', data.payload);
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    })
 | 
			
		||||
};
 | 
			
		||||
WebRTC.channels = new Map();
 | 
			
		||||
WebRTC.prototype.connect = function(object){
 | 
			
		||||
    if(!this.channel)
 | 
			
		||||
    {
 | 
			
		||||
        this.createDefaultDataChannel();
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
WebRTC.prototype.sendMessage = function(object){
 | 
			
		||||
    this.send({
 | 
			
		||||
        type:'message',
 | 
			
		||||
        payload: object
 | 
			
		||||
    });
 | 
			
		||||
};
 | 
			
		||||
WebRTC.prototype.createDefaultDataChannel = function(){
 | 
			
		||||
    let dt = this.rtc.createDataChannel(":default:",{
 | 
			
		||||
        ordered: true
 | 
			
		||||
    });
 | 
			
		||||
    dt.addEventListener("open",()=>{
 | 
			
		||||
        this.channel = dt;
 | 
			
		||||
        console.log(...rtcLabel, this.id, dt.label + ' veri kanalı açıldı');
 | 
			
		||||
        WebRTC.channels.set(this.id, this);
 | 
			
		||||
    });
 | 
			
		||||
    dt.addEventListener("message",({data})=>{
 | 
			
		||||
        let pack = JSON.parse(data);
 | 
			
		||||
        console.log(...rtcLabel, this.id, dt.label + ' P2P Pack ', pack);
 | 
			
		||||
        this.emit('input', pack);
 | 
			
		||||
    })
 | 
			
		||||
    dt.addEventListener("close",()=>{
 | 
			
		||||
        this.channel = null;
 | 
			
		||||
        console.log(...rtcLabel, this.id, dt.label + ' veri kanalı kapandı');
 | 
			
		||||
    })
 | 
			
		||||
};
 | 
			
		||||
WebRTC.prototype.destroy = function(){
 | 
			
		||||
    this.active = false;
 | 
			
		||||
    if(this.channel)
 | 
			
		||||
    {
 | 
			
		||||
        this.channel.close();
 | 
			
		||||
        this.channel = null;
 | 
			
		||||
    }
 | 
			
		||||
    if(this.rtc)
 | 
			
		||||
    {
 | 
			
		||||
        this.rtc.close();
 | 
			
		||||
        this.rtc = null;
 | 
			
		||||
    };
 | 
			
		||||
    this.emit('disconnected');
 | 
			
		||||
    WebRTC.channels.delete(this.id);
 | 
			
		||||
}
 | 
			
		||||
/**
 | 
			
		||||
 * 
 | 
			
		||||
 * @param {RTCDataChannelEvent} event 
 | 
			
		||||
 */
 | 
			
		||||
WebRTC.prototype.eventDatachannel = function(event){
 | 
			
		||||
    console.log(...rtcLabel, this.id, event.channel.label + ' veri kanalı açıldı');
 | 
			
		||||
    if(event.channel.label == ':default:'){
 | 
			
		||||
        WebRTC.channels.set(this.id, this);
 | 
			
		||||
        this.channel = event.channel
 | 
			
		||||
    }
 | 
			
		||||
    event.channel.addEventListener("message",({data})=>{
 | 
			
		||||
        let pack = JSON.parse(data);
 | 
			
		||||
        console.log(...rtcLabel, this.id, event.channel.label + ' P2P Pack ', pack);
 | 
			
		||||
        this.emit('input', pack);
 | 
			
		||||
    })
 | 
			
		||||
    event.channel.addEventListener("close",()=>{
 | 
			
		||||
        this.channel = null;
 | 
			
		||||
        WebRTC.channels.delete(this.id);
 | 
			
		||||
        WebRTC.requireGC = true;
 | 
			
		||||
        console.log(...rtcLabel, this.id, event.channel.label + ' veri kanalı kapandı');
 | 
			
		||||
    })
 | 
			
		||||
};
 | 
			
		||||
WebRTC.requireGC = false;
 | 
			
		||||
setInterval(()=>{
 | 
			
		||||
    if(WebRTC.requireGC == false) return;
 | 
			
		||||
    let img = document.createElement("img");
 | 
			
		||||
    img.src = window.URL.createObjectURL(new Blob([new ArrayBuffer(5e+7)]));
 | 
			
		||||
    img.onerror = function() {
 | 
			
		||||
        window.URL.revokeObjectURL(this.src);
 | 
			
		||||
        img = null;
 | 
			
		||||
        console.log("WebRTC Pool connections garbage connection microtask completed")
 | 
			
		||||
    };
 | 
			
		||||
    WebRTC.requireGC = false;
 | 
			
		||||
}, 3000)
 | 
			
		||||
WebRTC.prototype.send = function(object){
 | 
			
		||||
    if(this.channel?.readyState == "open")
 | 
			
		||||
    {
 | 
			
		||||
        this.channel.send(JSON.stringify(object));
 | 
			
		||||
    }else{
 | 
			
		||||
        this.emit('output', object);
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
let rtcLabel = ["%cRTC","color:red;font-weight:bold"];
 | 
			
		||||
WebRTC.prototype.eventConnectionState = function(){
 | 
			
		||||
    this.connectionStatus = this.rtc.connectionState;
 | 
			
		||||
    if(this.connectionStatus == 'connected')
 | 
			
		||||
    {
 | 
			
		||||
        if(this.active == false)
 | 
			
		||||
        {
 | 
			
		||||
            this.emit('connected');
 | 
			
		||||
            this.active = true;
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
    if(this.connectionStatus == 'failed' || this.connectionStatus == "disconnected" || this.connectionStatus == "closed")
 | 
			
		||||
    {
 | 
			
		||||
        if(this.active)
 | 
			
		||||
        {
 | 
			
		||||
            this.destroy();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    console.log(...rtcLabel, this.id, "connectionStatus", this.connectionStatus)
 | 
			
		||||
};
 | 
			
		||||
/**
 | 
			
		||||
 * 
 | 
			
		||||
 * @param {RTCPeerConnectionIceEvent} event 
 | 
			
		||||
 */
 | 
			
		||||
WebRTC.prototype.eventIcecandidate = function(event){
 | 
			
		||||
    console.log(...rtcLabel, this.id, 'ice created');
 | 
			
		||||
    if(event.candidate)
 | 
			
		||||
    {
 | 
			
		||||
        this.send({
 | 
			
		||||
            type:'icecandidate',
 | 
			
		||||
            value: event.candidate
 | 
			
		||||
        })
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
WebRTC.prototype.eventICEConnectionState = function(){
 | 
			
		||||
    this.iceStatus = this.rtc.iceConnectionState;
 | 
			
		||||
    console.log(...rtcLabel, this.id, "iceStatus",this.iceStatus)
 | 
			
		||||
};
 | 
			
		||||
WebRTC.prototype.eventICEGatherinState = function(){
 | 
			
		||||
    this.gatheringStatus = this.rtc.iceGatheringState;
 | 
			
		||||
    console.log(...rtcLabel, this.id, "gatheringStatus",this.gatheringStatus)
 | 
			
		||||
};
 | 
			
		||||
WebRTC.prototype.eventNogationNeeded = async function(){
 | 
			
		||||
    console.log(...rtcLabel, this.id, "requested nogation");
 | 
			
		||||
    let offer = await this.rtc.createOffer({
 | 
			
		||||
        iceRestart: true,
 | 
			
		||||
        offerToReceiveAudio: true,
 | 
			
		||||
        offerToReceiveVideo: true
 | 
			
		||||
    });
 | 
			
		||||
    await this.rtc.setLocalDescription(offer);
 | 
			
		||||
    this.send({
 | 
			
		||||
        type: 'offer',
 | 
			
		||||
        value: offer
 | 
			
		||||
    });
 | 
			
		||||
};
 | 
			
		||||
WebRTC.prototype.eventSignalingState = function(){
 | 
			
		||||
    this.signalingStatus = this.rtc.signalingState;
 | 
			
		||||
    console.log(...rtcLabel, this.id, "signalingStatus",this.signalingStatus)
 | 
			
		||||
};
 | 
			
		||||
/**
 | 
			
		||||
 * @param {RTCTrackEvent} event
 | 
			
		||||
 */
 | 
			
		||||
WebRTC.prototype.eventTrack = function(event){
 | 
			
		||||
    if(event.streams.length)
 | 
			
		||||
    {
 | 
			
		||||
        for (const stream of event.streams) {
 | 
			
		||||
            if(this.recaivingStream.get(stream.id).stream == null)
 | 
			
		||||
            {
 | 
			
		||||
                this.recaivingStream.get(stream.id).stream = stream;
 | 
			
		||||
                this.emit('stream:added', this.recaivingStream.get(stream.id));
 | 
			
		||||
            }else{
 | 
			
		||||
                this.recaivingStream.get(stream.id).stream = stream;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
/**
 | 
			
		||||
 * @param {MediaStream} stream
 | 
			
		||||
 * @param {string} name
 | 
			
		||||
 * @param {any} info
 | 
			
		||||
 */
 | 
			
		||||
WebRTC.prototype.sendStream = function(stream,name,info){
 | 
			
		||||
    this.send({
 | 
			
		||||
        type: 'streamInfo',
 | 
			
		||||
        id: stream.id,
 | 
			
		||||
        value: {
 | 
			
		||||
            ...info,
 | 
			
		||||
            name: name
 | 
			
		||||
        }
 | 
			
		||||
    });
 | 
			
		||||
    this.sendingStream.set(stream.id,{
 | 
			
		||||
        ...info,
 | 
			
		||||
        name: name,
 | 
			
		||||
        stream
 | 
			
		||||
    });
 | 
			
		||||
};
 | 
			
		||||
/**
 | 
			
		||||
 * @param {MediaStream} stream
 | 
			
		||||
 * @param {string} name
 | 
			
		||||
 * @param {any} info
 | 
			
		||||
 */
 | 
			
		||||
WebRTC.prototype.stopStream = function(_stream){
 | 
			
		||||
    if(this.connectionStatus != 'connected'){
 | 
			
		||||
        return
 | 
			
		||||
    }
 | 
			
		||||
    if(this.sendingStream.has(_stream.id))
 | 
			
		||||
    {
 | 
			
		||||
        let {stream} = this.sendingStream.get(_stream.id);
 | 
			
		||||
 | 
			
		||||
        for (const track of stream.getTracks()) {
 | 
			
		||||
            for (const RTCPSender of this.rtc.getSenders()) {
 | 
			
		||||
                if(RTCPSender.track?.id == track.id)
 | 
			
		||||
                {
 | 
			
		||||
                    this.rtc.removeTrack(RTCPSender);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    
 | 
			
		||||
        this.send({
 | 
			
		||||
            type: 'streamRemoved',
 | 
			
		||||
            id: stream.id
 | 
			
		||||
        });
 | 
			
		||||
        this.sendingStream.delete(_stream.id)
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
/**
 | 
			
		||||
 * @param {string} name
 | 
			
		||||
 * @param {any} info
 | 
			
		||||
 */
 | 
			
		||||
WebRTC.prototype.stopAllStreams = function(){
 | 
			
		||||
    if(this.connectionStatus != 'connected'){
 | 
			
		||||
        return
 | 
			
		||||
    }
 | 
			
		||||
    for (const [id, {stream}] of  this.sendingStream) {
 | 
			
		||||
        for (const track of stream.getTracks()) {
 | 
			
		||||
            for (const RTCPSender of this.rtc.getSenders()) {
 | 
			
		||||
                if(RTCPSender.track?.id == track.id)
 | 
			
		||||
                {
 | 
			
		||||
                    this.rtc.removeTrack(RTCPSender);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        this.send({
 | 
			
		||||
            type: 'streamRemoved',
 | 
			
		||||
            id: stream.id
 | 
			
		||||
        });
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    this.sendingStream.clear();
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
WebRTC.getCamera = async (options) => {
 | 
			
		||||
    return navigator.mediaDevices.getUserMedia({
 | 
			
		||||
        video: options || {
 | 
			
		||||
            frameRate: 10,
 | 
			
		||||
            width: 640,
 | 
			
		||||
            height: 480
 | 
			
		||||
        }
 | 
			
		||||
    })
 | 
			
		||||
}
 | 
			
		||||
WebRTC.getMicrophone = async (options) => {
 | 
			
		||||
    return navigator.mediaDevices.getUserMedia({
 | 
			
		||||
        audio: options || {
 | 
			
		||||
            channelCount: 1,
 | 
			
		||||
            sampleRate: 16000,
 | 
			
		||||
            sampleSize: 16,
 | 
			
		||||
            volume: 1
 | 
			
		||||
        }
 | 
			
		||||
    })
 | 
			
		||||
}
 | 
			
		||||
WebRTC.getDisplay = async (videoOptions) => {
 | 
			
		||||
    return navigator.mediaDevices.getDisplayMedia({
 | 
			
		||||
        video: videoOptions || true
 | 
			
		||||
    })
 | 
			
		||||
}
 | 
			
		||||
		Loading…
	
		Reference in New Issue