From bfc09376d1817f16b1db2c7f9adb30b77817a747 Mon Sep 17 00:00:00 2001 From: hensm Date: Fri, 14 Jun 2019 13:39:28 +0100 Subject: [PATCH] Convert mirroringCast sender to typescript --- ext/src/global.d.ts | 27 +++++ ext/src/main.ts | 16 +-- ext/src/senders/mediaCast.ts | 21 +++- .../{mirroringCast.js => mirroringCast.ts} | 101 +++++++++++------- ext/src/shim/cast/index.ts | 20 ++-- ext/src/shim/export.ts | 2 +- ext/webpack.config.js | 2 +- 7 files changed, 123 insertions(+), 66 deletions(-) rename ext/src/senders/{mirroringCast.js => mirroringCast.ts} (60%) diff --git a/ext/src/global.d.ts b/ext/src/global.d.ts index 71ddbec..1334bc9 100644 --- a/ext/src/global.d.ts +++ b/ext/src/global.d.ts @@ -12,6 +12,33 @@ declare interface Object { wrappedJSObject: Object; } +declare interface CanvasRenderingContext2D { + DRAWWINDOW_DRAW_CARET: 0x01; + DRAWWINDOW_DO_NOT_FLUSH: 0x02; + DRAWWINDOW_DRAW_VIEW: 0x04; + DRAWWINDOW_USE_WIDGET_LAYERS: 0x08; + DRAWWINDOW_ASYNC_DECODE_IMAGES: 0x10; + + drawWindow ( + window: Window + , x: number, y: number + , w: number, h: number + , bgColor: string + , flags: number): void; +} + +declare interface HTMLCanvasElement { + captureStream (frameRate?: number): MediaStream; +} + +declare interface MediaTrackConstraints { + mediaSource: "screen" | "window"; +} + +declare interface RTCPeerConnection { + addStream (mediaStream: MediaStream): void; +} + interface CloneIntoOptions { cloneFunctions?: boolean; diff --git a/ext/src/main.ts b/ext/src/main.ts index c8f9169..85705f5 100755 --- a/ext/src/main.ts +++ b/ext/src/main.ts @@ -427,25 +427,17 @@ browser.menus.onClicked.addListener(async (info, tab) => { || info.menuItemId === mediaCastMenuId) { const { frameId } = info; - const mirroringAppId = await options.get("mirroringAppId"); switch (info.menuItemId) { case mirrorCastMenuId: { mirrorCastTabId = tab.id; mirrorCastFrameId = frameId; - // Load cast setup script - await browser.tabs.executeScript(tab.id, { - file: "shim/content.js" - , frameId - }); - await browser.tabs.executeScript(tab.id, { code: ` - var selectedMedia = ${info.pageUrl + window.selectedMedia = ${info.pageUrl ? ReceiverSelectorMediaType.Tab : ReceiverSelectorMediaType.Screen}; - var FX_CAST_RECEIVER_APP_ID = "${mirroringAppId}"; ` , frameId }); @@ -456,12 +448,6 @@ browser.menus.onClicked.addListener(async (info, tab) => { , frameId }); - // Load cast API - await browser.tabs.executeScript(tab.id, { - file: "shim/bundle.js" - , frameId - }); - break; } diff --git a/ext/src/senders/mediaCast.ts b/ext/src/senders/mediaCast.ts index 2df173a..07286b2 100644 --- a/ext/src/senders/mediaCast.ts +++ b/ext/src/senders/mediaCast.ts @@ -55,10 +55,10 @@ function getLocalAddress () { } -async function onRequestSessionSuccess (session_: cast.Session) { +async function onRequestSessionSuccess (newSession: cast.Session) { cast.logMessage("onRequestSessionSuccess"); - session = session_; + session = newSession; let mediaUrl = new URL(srcUrl); const port = options.localMediaServerPort; @@ -163,13 +163,16 @@ async function onRequestSessionSuccess (session_: cast.Session) { , onLoadMediaSuccess , onLoadMediaError); } + function onRequestSessionError () { cast.logMessage("onRequestSessionError"); } -function sessionListener (session: cast.Session) { + +function sessionListener (newSession: cast.Session) { cast.logMessage("sessionListener"); } + function receiverListener (availability: string) { cast.logMessage("receiverListener"); @@ -180,13 +183,16 @@ function receiverListener (availability: string) { } } + function onInitializeSuccess () { cast.logMessage("onInitializeSuccess"); } + function onInitializeError () { cast.logMessage("onInitializeError"); } + function onLoadMediaSuccess (media: cast.media.Media) { cast.logMessage("onLoadMediaSuccess"); @@ -305,38 +311,47 @@ function onLoadMediaSuccess (media: cast.media.Media) { }); } } + function onLoadMediaError () { cast.logMessage("onLoadMediaError"); } + /* play */ function onMediaPlaySuccess () { cast.logMessage("onMediaPlaySuccess"); } + function onMediaPlayError (err: cast.Error) { cast.logMessage("onMediaPlayError"); } + /* pause */ function onMediaPauseSuccess () { cast.logMessage("onMediaPauseSuccess"); } + function onMediaPauseError (err: cast.Error) { cast.logMessage("onMediaPauseError"); } + /* stop */ function onMediaStopSuccess () { cast.logMessage("onMediaStopSuccess"); } + function onMediaStopError (err: cast.Error) { cast.logMessage("onMediaStopError"); } + /* seek */ function onMediaSeekSuccess () { cast.logMessage("onMediaSeekSuccess"); } + function onMediaSeekError (err: cast.Error) { cast.logMessage("onMediaSeekError"); } diff --git a/ext/src/senders/mirroringCast.js b/ext/src/senders/mirroringCast.ts similarity index 60% rename from ext/src/senders/mirroringCast.js rename to ext/src/senders/mirroringCast.ts index 05380ce..dbe7d97 100644 --- a/ext/src/senders/mirroringCast.js +++ b/ext/src/senders/mirroringCast.ts @@ -1,17 +1,24 @@ "use strict"; -import { ReceiverSelectorMediaType } - from "../receiver_selectors/ReceiverSelector": +import options from "../lib/options"; +import cast, { init } from "../shim/export"; + +import { ReceiverSelectorMediaType } + from "../receiver_selectors/ReceiverSelector"; + + +// Variables passed from background +const { selectedMedia } + : { selectedMedia: ReceiverSelectorMediaType } = (window as any); -let chrome; -let logMessage; const FX_CAST_NAMESPACE = "urn:x-cast:fx_cast"; -let session; +let session: cast.Session; let sessionRequested = false; -let pc; +let pc: RTCPeerConnection; + const canvas = document.createElement("canvas"); const ctx = canvas.getContext("2d"); @@ -35,19 +42,24 @@ window.addEventListener("resize", () => { let interval; -function sendMessage (subject, data) { +function sendMessage (subject: string, data: any) { session.sendMessage(FX_CAST_NAMESPACE, { subject , data - }); + }, null, null); } window.addEventListener("beforeunload", () => { - sendMessage("close"); + sendMessage("close", null); }); -async function onRequestSessionSuccess (session_, selectedMedia) { - logMessage("onRequestSessionSuccess"); + +async function onRequestSessionSuccess ( + // tslint:disable-next-line:variable-name + session_: cast.Session + , newSelectedMedia: ReceiverSelectorMediaType) { + + cast.logMessage("onRequestSessionSuccess"); session = session_; @@ -72,8 +84,8 @@ async function onRequestSessionSuccess (session_, selectedMedia) { sendMessage("iceCandidate", ev.candidate); }); - switch (selectedMedia) { - case ReceiverSelectorMediaType.Tab: + switch (newSelectedMedia) { + case ReceiverSelectorMediaType.Tab: { interval = setInterval(() => { ctx.clearRect(0, 0, canvas.width, canvas.height); ctx.drawWindow( @@ -85,15 +97,21 @@ async function onRequestSessionSuccess (session_, selectedMedia) { , "white" // bgColor , ctx.DRAWWINDOW_DRAW_VIEW); // flags }, 1000 / 30); - pc.addStream(canvas.captureStream()); - break; - case ReceiverSelectorMediaType.Screen: - const stream = await navigator.mediaDevices.getUserMedia({ - video: { mediaSource: "window" } - }); - pc.addStream(stream); + pc.addStream(canvas.captureStream()); + break; + } + + case ReceiverSelectorMediaType.Screen: { + const stream = await navigator.mediaDevices.getUserMedia({ + video: { mediaSource: "screen" } + }); + + pc.addStream(stream); + + break; + } } const desc = await pc.createOffer(); @@ -101,19 +119,22 @@ async function onRequestSessionSuccess (session_, selectedMedia) { sendMessage("peerConnectionOffer", desc); } + function onRequestSessionError () { - logMessage("onRequestSessionError"); + cast.logMessage("onRequestSessionError"); } -function sessionListener (session) { - logMessage("sessionListener"); -} -function receiverListener (availability) { - logMessage("receiverListener"); - if (!sessionRequested && availability === chrome.cast.ReceiverAvailability.AVAILABLE) { +function sessionListener (newSession: cast.Session) { + cast.logMessage("sessionListener"); +} +function receiverListener (availability: string) { + cast.logMessage("receiverListener"); + + if (!sessionRequested + && availability === cast.ReceiverAvailability.AVAILABLE) { sessionRequested = true; - chrome.cast.requestSession( + cast.requestSession( onRequestSessionSuccess , onRequestSessionError); } @@ -121,29 +142,31 @@ function receiverListener (availability) { function onInitializeSuccess () { - logMessage("onInitializeSuccess"); + cast.logMessage("onInitializeSuccess"); } function onInitializeError () { - logMessage("onInitializeError"); + cast.logMessage("onInitializeError"); } -window.__onGCastApiAvailable = (loaded, errorInfo) => { - chrome = window.chrome; - logMessage = chrome.cast.logMessage; +init().then(async bridgeInfo => { + if (!bridgeInfo.isVersionCompatible) { + console.error("__onGCastApiAvailable error"); + return; + } - logMessage("__onGCastApiAvailable success"); - const sessionRequest = new chrome.cast.SessionRequest( - FX_CAST_RECEIVER_APP_ID); + const mirroringAppId = await options.get("mirroringAppId"); + const sessionRequest = new cast.SessionRequest(mirroringAppId); - const apiConfig = new chrome.cast.ApiConfig(sessionRequest + const apiConfig = new cast.ApiConfig( + sessionRequest , sessionListener , receiverListener , undefined, undefined , selectedMedia); - chrome.cast.initialize(apiConfig + cast.initialize(apiConfig , onInitializeSuccess , onInitializeError); -} +}); diff --git a/ext/src/shim/cast/index.ts b/ext/src/shim/cast/index.ts index 668b38e..220a6a6 100755 --- a/ext/src/shim/cast/index.ts +++ b/ext/src/shim/cast/index.ts @@ -26,9 +26,10 @@ import { AutoJoinPolicy import * as media from "./media"; -import { requestSession as requestSessionTimeout } from "../timeout"; - +import { ReceiverSelectorMediaType } + from "../../receiver_selectors/ReceiverSelector"; import { onMessage, sendMessageResponse } from "../messageBridge"; +import { requestSession as requestSessionTimeout } from "../timeout"; type ReceiverActionListener = ( @@ -37,7 +38,7 @@ type ReceiverActionListener = ( type RequestSessionSuccessCallback = ( session: Session - , selectedMedia: string) => void; + , selectedMedia: ReceiverSelectorMediaType) => void; type SuccessCallback = () => void; type ErrorCallback = (err: Error_) => void; @@ -95,6 +96,7 @@ export function initialize ( return; } + apiConfig = newApiConfig; sendMessageResponse({ @@ -209,8 +211,10 @@ onMessage(async message => { receiverList.push(receiver); - // Notify listeners of new cast destination - apiConfig.receiverListener(ReceiverAvailability.AVAILABLE); + if (apiConfig) { + // Notify listeners of new cast destination + apiConfig.receiverListener(ReceiverAvailability.AVAILABLE); + } break; } @@ -224,8 +228,10 @@ onMessage(async message => { receiver => receiver.id !== message.data.id); if (receiverList.length === 0) { - apiConfig.receiverListener( - ReceiverAvailability.UNAVAILABLE); + if (apiConfig) { + apiConfig.receiverListener( + ReceiverAvailability.UNAVAILABLE); + } } break; diff --git a/ext/src/shim/export.ts b/ext/src/shim/export.ts index 3534dd0..5f4030d 100644 --- a/ext/src/shim/export.ts +++ b/ext/src/shim/export.ts @@ -27,7 +27,7 @@ export function init (): Promise { resolve(bridgeInfo); } } - }) + }); }); } diff --git a/ext/webpack.config.js b/ext/webpack.config.js index d053e3f..d46c828 100755 --- a/ext/webpack.config.js +++ b/ext/webpack.config.js @@ -21,7 +21,7 @@ module.exports = (env) => ({ // Sender apps , "senders/mediaCast": `${env.includePath}/senders/mediaCast.ts` - , "senders/mirroringCast": `${env.includePath}/senders/mirroringCast.js` + , "senders/mirroringCast": `${env.includePath}/senders/mirroringCast.ts` // Shim entries , "shim/bundle": `${env.includePath}/shim/index.ts`