gecko/mobile/android/modules/TabMirror.jsm

154 lines
4.4 KiB
JavaScript

/* -*- Mode: js; js-indent-level: 2; indent-tabs-mode: nil; tab-width: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
const { classes: Cc, interfaces: Ci, utils: Cu } = Components;
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/Messaging.jsm");
const CONFIG = { iceServers: [{ "urls": ["stun:stun.services.mozilla.com"] }] };
let log = Cu.import("resource://gre/modules/AndroidLog.jsm",
{}).AndroidLog.d.bind(null, "TabMirror");
let failure = function(x) {
log("ERROR: " + JSON.stringify(x));
};
let TabMirror = function(deviceId, window) {
this.deviceId = deviceId;
// Save mozRTCSessionDescription and mozRTCIceCandidate for later when the window object is not available.
this.RTCSessionDescription = window.mozRTCSessionDescription;
this.RTCIceCandidate = window.mozRTCIceCandidate;
Services.obs.addObserver((aSubject, aTopic, aData) => this._processMessage(aData), "MediaPlayer:Response", false);
this._sendMessage({ start: true });
this._window = window;
this._pc = new window.mozRTCPeerConnection(CONFIG, {});
if (!this._pc) {
throw "Failure creating Webrtc object";
}
};
TabMirror.prototype = {
_window: null,
_screenSize: { width: 1280, height: 720 },
_pc: null,
_start: function() {
this._pc.onicecandidate = this._onIceCandidate.bind(this);
let windowId = this._window.BrowserApp.selectedBrowser.contentWindow.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowUtils).outerWindowID;
let constraints = {
video: {
mediaSource: "browser",
browserWindow: windowId,
scrollWithPage: true,
advanced: [
{ width: { min: 0, max: this._screenSize.width },
height: { min: 0, max: this._screenSize.height }
},
{ aspectRatio: this._screenSize.width / this._screenSize.height }
]
}
};
this._window.navigator.mozGetUserMedia(constraints, this._onGumSuccess.bind(this), this._onGumFailure.bind(this));
},
_processMessage: function(data) {
if (!data) {
return;
}
let msg = JSON.parse(data);
if (!msg) {
return;
}
if (msg.sdp && msg.type === "answer") {
this._processAnswer(msg);
} else if (msg.type == "size") {
if (msg.height) {
this._screenSize.height = msg.height;
}
if (msg.width) {
this._screenSize.width = msg.width;
}
this._start();
} else if (msg.candidate) {
this._processIceCandidate(msg);
} else {
log("dropping unrecognized message: " + JSON.stringify(msg));
}
},
// Signaling methods
_processAnswer: function(msg) {
this._pc.setRemoteDescription(new this.RTCSessionDescription(msg),
this._setRemoteAnswerSuccess.bind(this), failure);
},
_processIceCandidate: function(msg) {
// WebRTC generates a warning if the success and fail callbacks are not passed in.
this._pc.addIceCandidate(new this.RTCIceCandidate(msg), () => log("Ice Candiated added successfuly"), () => log("Failed to add Ice Candidate"));
},
_setRemoteAnswerSuccess: function() {
},
_setLocalSuccessOffer: function(sdp) {
this._sendMessage(sdp);
},
_createOfferSuccess: function(sdp) {
this._pc.setLocalDescription(sdp, () => this._setLocalSuccessOffer(sdp), failure);
},
_onIceCandidate: function (msg) {
log("NEW Ice Candidate: " + JSON.stringify(msg.candidate));
this._sendMessage(msg.candidate);
},
_ready: function() {
this._pc.createOffer(this._createOfferSuccess.bind(this), failure);
},
_onGumSuccess: function(stream){
this._pc.addStream(stream);
this._ready();
},
_onGumFailure: function() {
log("Could not get video stream");
this._pc.close();
},
_sendMessage: function(msg) {
if (this.deviceId) {
let obj = {
type: "MediaPlayer:Message",
id: this.deviceId,
data: JSON.stringify(msg)
};
Messaging.sendRequest(obj);
}
},
stop: function() {
if (this.deviceId) {
let obj = {
type: "MediaPlayer:End",
id: this.deviceId
};
Services.androidBridge.handleGeckoMessage(obj);
}
},
};
this.EXPORTED_SYMBOLS = ["TabMirror"];