(function (root, factory) { if (typeof define === 'function' && define.amd) { define(["converse"], factory); } else { factory(converse); } }(this, function (converse) { var _converse, html, __, button, myJid, myself, me, model, pcSpeak, audioStream, occupantId, screencastDlg, whipAvailable; converse.plugins.add("screencast", { 'dependencies': [], 'initialize': function () { _converse = this._converse; html = converse.env.html; __ = _converse.__; class ScreencastDialog extends _converse.exports.BaseModal { initialize() { super.initialize(); this.listenTo(this.model, "change", () => this.requestUpdate()); this.addEventListener('shown.bs.modal', async () => { const key = this.model.get("key"); const nick = this.model.get("nick"); this.querySelector('.modal-title').innerHTML = nick; this.querySelector('.modal-body').innerHTML = __("Please wait...."); const pcListen = new RTCPeerConnection(); pcListen.addTransceiver('audio', { direction: 'recvonly' }) pcListen.addTransceiver('video', { direction: 'recvonly' }) pcListen.oniceconnectionstatechange = () => { console.debug("oniceconnectionstatechange listen", pcListen.iceConnectionState); } let audioStream; pcListen.ontrack = (event) => { console.debug("ontrack listen ", event.streams, event); if (event.track.kind == "video") { const ele = document.createElement("video"); ele.setAttribute("autoplay", true); ele.setAttribute("controls", true); ele.style = "width: 100%; height: 100%"; audioStream = event.streams[0]; ele.srcObject = audioStream; const parent = this.querySelector('.modal-body'); if (parent) { parent.innerHTML = ""; parent.appendChild(ele); } } } this.addEventListener("blur", (event) => { console.debug("screencast dialog lost focus"); setTimeout(() => { if (!converse.env.u.isVisible(screencastDlg)) { if (audioStream) audioStream.getTracks().forEach(track => track.stop()); pcListen.close(); } }, 2000); }) const offer = await pcListen.createOffer(); pcListen.setLocalDescription(offer); console.debug('handleStream - whep offer', offer.sdp); const res = await _converse.api.sendIQ(converse.env.$iq({type: 'set'}).c('whep', {key, xmlns: 'urn:xmpp:whep:0'}).c('sdp', offer.sdp)); console.debug('whep set response', res); const answer = res.querySelector('sdp').innerHTML; pcListen.setRemoteDescription({sdp: answer, type: 'answer'}); console.debug('whep answer', answer); }); } renderModal() { return html`
`; } }; _converse.api.elements.define('converse-pade-screencast-dialog', ScreencastDialog); _converse.api.listen.on('getToolbarButtons', function(toolbar_el, buttons) { console.debug("screencast - getToolbarButtons", whipAvailable); if (toolbar_el.model.get("type") === "chatroom" && whipAvailable) { style = "width:18px; height:18px; fill:var(--muc-color);"; buttons.push(html` `); } return buttons; }); _converse.api.listen.on('connected', async function() { console.debug("screencast - connected"); myJid = await _converse.api.connection.get().jid; myself = converse.env.Strophe.getBareJidFromJid(myJid); me = converse.env.Strophe.getNodeFromJid(myJid); const features = await _converse.api.disco.getFeatures(await _converse.api.connection.get().domain); console.debug("connected features", features); features.each(feature => { const fieldname = feature.get('var'); console.debug("connected feature", fieldname); if (fieldname == "urn:xmpp:whip:0") whipAvailable = true; }); if (!whipAvailable) { const view = _converse.chatboxviews.get(model.get("from_muc")); const button = view.querySelector(".plugin-screencast"); if (button) button.style.display = 'none'; } }); _converse.api.listen.on('parseMessage', (stanza, attrs) => { return parseStanza(stanza, attrs); }); _converse.api.listen.on('parseMUCMessage', (stanza, attrs) => { return parseStanza(stanza, attrs); }); console.debug("screencast plugin is ready"); } }); var performScreenCast = function(ev) { ev.stopPropagation(); ev.preventDefault(); const toolbar_el = converse.env.utils.ancestor(ev.target, 'converse-chat-toolbar'); button = toolbar_el.querySelector('.plugin-screencast'); model = toolbar_el.model; const occupant = model.getOwnOccupant(); const occupants = model.getOccupantsSortedBy('occupant_id'); if (!button.classList.contains('blink_me')) { occupantId = occupant.get("occupant_id"); if (!occupantId) occupantId = occupant.get("id"); console.debug("performScreenCast", model, occupantId, occupants); navigator.mediaDevices.getDisplayMedia({audio: true, video: true }).then(async stream => { audioStream = stream; console.debug("performScreenCast", stream); stream.getVideoTracks()[0].addEventListener('ended', () => { console.debug('The user has ended sharing the screen'); stopStream(); sendStopMessage(); }); pcSpeak = new RTCPeerConnection(); pcSpeak.oniceconnectionstatechange = () => { console.debug("oniceconnectionstatechange speak", pcSpeak.iceConnectionState); } pcSpeak.ontrack = function (event) { console.debug("ontrack speak", event.streams, event); } stream.getTracks().forEach(t => { pcSpeak.addTransceiver(t, {direction: 'sendonly'}) }) const offer = await pcSpeak.createOffer(); pcSpeak.setLocalDescription(offer); const res = await _converse.api.sendIQ(converse.env.$iq({type: 'set'}).c('whip', {xmlns: 'urn:xmpp:whip:0', key: occupantId}).c('sdp', offer.sdp)); const answer = res.querySelector('sdp').innerHTML; pcSpeak.setRemoteDescription({sdp: answer, type: 'answer'}); console.debug('whip answer', answer); button.classList.add('blink_me'); sendStartMessage(); }, error => { handleError(error) }); } else { stopStream(); sendStopMessage(); } } var sendStartMessage = function sendMessage() { const message = "/me started streaming"; const type = (model.get('type') == 'chatroom') ? 'groupchat' : 'chat'; const target = (model.get('type') == 'chatbox') ? model.get('jid') : (model.get('type') == 'chatroom' ? model.get('jid') : model.get('from')); const msg = converse.env.stx`