var vcamSendMediaConnection = []; var vcamRecvMediaConnections = []; function getPeerConfig() { return { host: "rtcpeer.kuku.lu", port: 9000, secure: true, path: "/", config: { 'iceServers': [ //{ 'urls': 'stun:stun.l.google.com:19302' } {"urls": "stun:turn.kuku.lu:9010"}, {"urls": "turn:turn.kuku.lu:9010?transport=tcp", "username": "aquapal", "credential": "aquapal"}, {"urls": "turn:turn.kuku.lu:9010?transport=udp", "username": "aquapal", "credential": "aquapal"} ], 'sdpSemantics': 'unified-plan' }, debug: 3 }; } function vcamSendInit(_callback) { navigator.mediaDevices.getUserMedia( { audio: true, video: true } ).then(function(stream) { console.log("getUserMedia(): success"); navigator.mediaDevices.enumerateDevices().then(function(devices) { // 成功時 var _devices = { "audio": [], "video": [] }; devices.forEach(function(device) { console.log(device.label); if (device.kind == "audioinput") { _devices["audio"].push({"id": device.deviceId, "name": device.label}); } if (device.kind == "videoinput") { _devices["video"].push({"id": device.deviceId, "name": device.label}); } }); if (_devices["audio"].length >= 1 || _devices["video"].length >= 1) { if (_callback) { _callback({"success": true, "devices": _devices}); } } else { if (_callback) { _callback({"error": true, "reason": "noDevice"}); } } stream.getTracks().forEach(track => track.stop()); }).catch(function(err) { // エラー発生時 console.error('enumerateDevices(): ERROR:', err); if (_callback) { _callback({"error": true, "reason": "enumerateDevices"}); } }); }).catch(function(err){ // エラー時 console.error('getUserMedia(): ERROR:', err); if (_callback) { _callback({"error": true, "reason": "getUserMedia"}); } }); } var localVideo = false; function vcamSendStart(_config, _callback, _callback_done) { console.log("vcamSendStart: ", _config); (async function main() { localVideo = $(_config["localVideo"]).get(0); let localStream = new MediaStream(); if (_config["deviceAudio"] || _config["deviceVideo"]) { var _mediaconfig = {}; if (_config["deviceAudio"]) { _mediaconfig["audio"] = { enabled: true, echoCancellation: true, deviceId: {exact: _config["deviceAudio"]} }; } if (_config["deviceVideo"]) { _mediaconfig["video"] = { enabled: true, deviceId: {exact: _config["deviceVideo"]}, width: { min: 480, ideal: 1280, max: 1920 } }; } const cameraStream = await navigator.mediaDevices .getUserMedia(_mediaconfig) .catch(function(e){ console.error(e); vcamSendEnd(); _callback({"error": true, "reason": "getUserMedia"}); }); cameraStream.getTracks()[0].addEventListener('ended', () => { vcamSendEnd(); _callback_done(); }) cameraStream.getTracks().forEach(track => localStream.addTrack(track)); } if (_config["displayAudio"] || _config["displayVideo"]) { var _mediaconfig = {}; if (_config["displayAudio"]) { _mediaconfig["audio"] = true; } if (_config["displayVideo"]) { _mediaconfig["video"] = { width: { max: 1920 }, height: { max: 1280 } }; } const displayMedia = await navigator.mediaDevices .getDisplayMedia(_mediaconfig) .catch(function(e){ console.error(e); vcamSendEnd(); _callback({"error": true, "reason": "getDisplayMedia"}); }); displayMedia.getTracks()[0].addEventListener('ended', () => { vcamSendEnd(); _callback_done(); }) displayMedia.getTracks().forEach(track => localStream.addTrack(track)); } localVideo.muted = true; localVideo.srcObject = localStream; localVideo.playsInline = true; await localVideo.play().catch(console.error); console.log("vcamSend: Peer作成"); const peer = new Peer(null, getPeerConfig()); peer.once('open', function(id){ console.log("vcamSend: open"); _callback({"success": true, "sender_id": id}); }); peer.on('connection', function(conn) { console.log("vcamSend: Recv("+conn.peer+")からのconnection: ", conn); var recv_id = conn.peer; var call = peer.call(recv_id, localStream); vcamSendMediaConnection[recv_id] = call; call.once("close", function(){ console.log("vcamSend: close: 視聴者が停止した: "+recv_id); delete vcamSendMediaConnection[recv_id]; }); }); peer.on('disconnected', function(){ console.log("vcamSend: disconnect: peer自体が切断したので再接続します"); setTimeout(function(){ if (peer) peer.reconnect(); }, 1000); }); peer.on('close', function(){ console.log("vcamSend: close"); }); })(); } function vcamSendEnd() { try { localVideo.srcObject.getTracks().forEach(track => track.stop()); localVideo.srcObject = null; } catch(e) { console.error(e); } for (var i in vcamSendMediaConnection) { try { vcamSendMediaConnection[i].close(true); } catch(e) { console.error(e); } } } function vcamRecv(_config, _callback_start, _callback_end) { var recv_id = ""; var stop = false; var error_reason = false; var timer_timeoutPlaying; (async function main() { const remoteVideo = $(_config["removeVideo"]).get(0); //視聴先専用のpeerを作成する console.log("vcamRecv: Peer作成(相手: "+_config["sender_id"]+")"); const peer = new Peer(null, getPeerConfig()); /* function vcamRecvStart(_config, _callback_start, _callback_end) { if (!peer.open) { console.log("peer.open = close"); setTimeout(function(){ vcamRecvStart(_config, _callback_start, _callback_end); }, 250); return; } //vcamRecvMediaConnections[_config["sender_id"]] = peer.call(_config["sender_id"]); //vcamRecvMediaConnections[_config["sender_id"]] = peer.connect(_config["sender_id"]); vcamRecvMediaConnections[_config["sender_id"]].on('open', function() { conn.on('data', function(data) { console.log('Received', data); }); conn.send('test'); }); vcamRecvMediaConnections[_config["sender_id"]].on('stream', async stream => { if (_config["mute"]) { remoteVideo.muted = true; } else { remoteVideo.muted = false; } remoteVideo.srcObject = stream; remoteVideo.playsInline = true; //remoteVideo.controls = true; await remoteVideo.play().then(function(){ if (_callback_start) { if (_config["mute"]) { _callback_start({"mute": true, "mute-manual": true}); } else { _callback_start({"mute": false}); } } }).catch(error => { console.error(error); remoteVideo.muted = true; remoteVideo.play().then(function(){ if (_callback_start) { _callback_start({"mute": true}); } }).catch(error => { console.error(error); vcamRecvEnd(_config["sender_id"]); if (_callback_end) { _callback_end(); } }); }); }); vcamRecvMediaConnections[_config["sender_id"]].once('close', () => { remoteVideo.srcObject.getTracks().forEach(track => track.stop()); remoteVideo.srcObject = null; vcamRecvEnd(_config["sender_id"]); if (_callback_end) { _callback_end(); } }); } */ //視聴先専用peerが開いた peer.once('open', function(_id){ recv_id = _id; console.log("vcamRecv: sender_id(remote): "+_config["sender_id"]+" / recv_id(my):"+recv_id); //データコネクションを開く(開いたら相手からcallが来る) var conn = peer.connect(_config["sender_id"]); conn.on('open', function() { conn.on('data', function(data) { }); }); }); //視聴先からcallが来た peer.on('call', function(call){ console.log("vcamRecv: Send("+call.peer+")からのcall: ", call); call.answer(); vcamRecvMediaConnections[call.peer] = call; //callに含まれるストリームを再生する call.once("stream", async stream => { console.log("vcamRecv: Send("+call.peer+")からのstream"); console.log("vcamRecv: config: ", _config); remoteVideo.srcObject = stream; if (_config["mute"]) { remoteVideo.muted = true; } else { remoteVideo.muted = false; } remoteVideo.playsInline = true; //remoteVideo.controls = true; timer_timeoutPlaying = setTimeout(function(){ console.log("vcamRecv: Send("+call.peer+")からのstreamが正常に再生できない"); error_reason = "failstart"; call.close(); }, 5000); //5sec remoteVideo.addEventListener("playing", function () { console.log("vcamRecv: Send("+call.peer+")からのstreamが再生開始"); clearTimeout(timer_timeoutPlaying); }); await remoteVideo.play().then(function(){ if (_callback_start) { if (_config["mute"]) { _callback_start({"mute": true, "mute-manual": true}); } else { _callback_start({"mute": false}); } } }).catch(async error => { console.error(error); remoteVideo.muted = true; remoteVideo.play().then(function(){ if (_callback_start) { _callback_start({"mute": true}); } }).catch(error => { console.error(error); }); }); }); call.on('error', function(e){ console.log("vcamRecv: call: error: ", e); }); //視聴先が配信やめた call.once('close', function(){ console.log("vcamRecv: Send("+call.peer+")がcloseしました"); try { remoteVideo.srcObject.getTracks().forEach(track => track.stop()); remoteVideo.srcObject = null; } catch(e) { console.error(e); } vcamRecvEnd(call.peer); if (_callback_end) { _callback_end(error_reason); } stop = true; peer.destroy(); }); }); //peerでエラー peer.on('error', function(e){ console.error("vcamRecv: ERROR: ", e); }); peer.on('disconnected', function(){ if (stop) return; console.log("vcamRecv: disconnect: peer自体が切断したので再接続します"); setTimeout(function(){ if (peer) peer.reconnect(); }, 1000); }); peer.on('close', function(){ console.log("vcamRecv: close"); }); //vcamRecvStart(_config, _callback_start, _callback_end); })(); } function vcamRecvEnd(_sender_id) { vcamRecvMediaConnections[_sender_id].close(true); }