This commit is contained in:
hensm
2022-09-08 16:41:02 +01:00
parent e3bf3b7590
commit d6de5dae63
7 changed files with 100 additions and 46 deletions

View File

@@ -355,6 +355,15 @@
"description": "Media stop on unload checkbox label."
},
"optionsMediaMirroringEnabled": {
"message": "Enable media mirroring",
"description": "Media mirroring enabled checkbox label."
},
"optionsMediaMirroringEnabledDescription": {
"message": "Use a screen mirroring connection as a fallback to cast certain kinds of video content.",
"description": "Media mirroring enabled option description."
},
"optionsLocalMediaCategoryName": {
"message": "Local media casting",
"description": "Options page local media category title."

View File

@@ -398,6 +398,12 @@ const castManager = new (class {
}
try {
const mirroringAppId = await options.get("mirroringAppId");
const requestedMediaType =
sessionRequest.appId === mirroringAppId
? ReceiverSelectorMediaType.Screen
: ReceiverSelectorMediaType.App;
const selection = await getReceiverSelection({
castInstance: instance
});
@@ -416,7 +422,7 @@ const castManager = new (class {
* been changed, we need to cancel the current
* sender and switch it out for the right one.
*/
if (selection.mediaType !== ReceiverSelectorMediaType.App) {
if (selection.mediaType !== requestedMediaType) {
instance.contentPort.postMessage({
subject: "cast:sessionRequestCancelled"
});

View File

@@ -29,7 +29,7 @@ const whitelistChildMenuPatterns = new Map<MenuId, string>();
export async function initMenus() {
logger.info("init (menus)");
const opts = await options.getAll();
let opts = await options.getAll();
// Global "Cast..." menu item
menuIdCast = browser.menus.create({
@@ -39,15 +39,25 @@ export async function initMenus() {
icons: { "16": "icons/icon.svg" } // browser_action context
});
function createCastMediaMenu() {
let targetUrlPatterns: Optional<string[]>;
if (!opts.mediaMirroringEnabled) {
targetUrlPatterns = opts.localMediaEnabled
? URL_PATTERNS_ALL
: URL_PATTERNS_REMOTE;
}
return browser.menus.create({
id: "media",
contexts: ["audio", "video", "image"],
title: _("contextCast"),
visible: opts.mediaEnabled,
targetUrlPatterns: targetUrlPatterns
});
}
// <video>/<audio> "Cast..." context menu item
menuIdCastMedia = browser.menus.create({
contexts: ["audio", "video", "image"],
title: _("contextCast"),
visible: opts.mediaEnabled,
targetUrlPatterns: opts.localMediaEnabled
? URL_PATTERNS_ALL
: URL_PATTERNS_REMOTE
});
menuIdCastMedia = createCastMediaMenu();
menuIdWhitelist = browser.menus.create({
contexts: ["browser_action"],
@@ -118,20 +128,21 @@ export async function initMenus() {
options.addEventListener("changed", async ev => {
const alteredOpts = ev.detail;
const newOpts = await options.getAll();
opts = await options.getAll();
if (menuIdCastMedia && alteredOpts.includes("mediaEnabled")) {
browser.menus.update(menuIdCastMedia, {
visible: newOpts.mediaEnabled
visible: opts.mediaEnabled
});
}
if (menuIdCastMedia && alteredOpts.includes("localMediaEnabled")) {
browser.menus.update(menuIdCastMedia, {
targetUrlPatterns: newOpts.localMediaEnabled
? URL_PATTERNS_ALL
: URL_PATTERNS_REMOTE
});
if (
menuIdCastMedia &&
(alteredOpts.includes("localMediaEnabled") ||
alteredOpts.includes("mediaMirroringEnabled"))
) {
browser.menus.remove(menuIdCastMedia);
menuIdCastMedia = createCastMediaMenu();
}
});
}
@@ -183,20 +194,18 @@ async function onMenuClicked(
}
case menuIdCastMedia:
if (info.srcUrl) {
await browser.tabs.executeScript(tab.id, {
code: stringify`
window.mediaUrl = ${info.srcUrl};
window.targetElementId = ${info.targetElementId};
`,
frameId: info.frameId
});
await browser.tabs.executeScript(tab.id, {
code: stringify`
window.mediaUrl = ${info.srcUrl};
window.targetElementId = ${info.targetElementId};
`,
frameId: info.frameId
});
await browser.tabs.executeScript(tab.id, {
file: "cast/senders/media.js",
frameId: info.frameId
});
}
await browser.tabs.executeScript(tab.id, {
file: "cast/senders/media.js",
frameId: info.frameId
});
break;
}
}

View File

@@ -9,6 +9,7 @@ import type Session from "../sdk/Session";
import type Media from "../sdk/media/Media";
import cast, { ensureInit, CastPort } from "../export";
import MirroringSender from "./mirroring";
const logger = new Logger("fx_cast [media sender]");
@@ -359,8 +360,18 @@ if (window.location.protocol !== "moz-extension:") {
) as HTMLMediaElement;
}
new MediaSender({
mediaUrl: window_.mediaUrl,
mediaElement
});
if (window_.mediaUrl) {
new MediaSender({
mediaUrl: window_.mediaUrl,
mediaElement
});
} else {
const mirroringSender = new MirroringSender({
onSessionCreated() {
mirroringSender.createMirroringConnection(
(mediaElement as any).mozCaptureStream() as MediaStream
);
}
});
}
}

View File

@@ -19,17 +19,17 @@ type MirroringAppMessage =
| { subject: "close" };
interface MirroringSenderOpts {
receiverDevice: ReceiverDevice;
onSessionCreated: () => void;
onMirroringConnected: () => void;
onMirroringStopped: () => void;
receiverDevice?: ReceiverDevice;
onSessionCreated?: () => void;
onMirroringConnected?: () => void;
onMirroringStopped?: () => void;
}
export default class MirroringSender {
private receiverDevice: ReceiverDevice;
private sessionCreatedCallback: () => void;
private mirroringConnectedCallback: () => void;
private mirroringStoppedCallback: () => void;
private receiverDevice?: ReceiverDevice;
private sessionCreatedCallback?: () => void;
private mirroringConnectedCallback?: () => void;
private mirroringStoppedCallback?: () => void;
private session?: Session;
private wasSessionRequested = false;
@@ -96,7 +96,7 @@ export default class MirroringSender {
cast.requestSession(
session => {
this.session = session;
this.sessionCreatedCallback();
this.sessionCreatedCallback?.();
},
err => {
logger.error("Session request failed", err);
@@ -114,7 +114,7 @@ export default class MirroringSender {
this.peerConnection?.close();
this.session?.stop();
this.mirroringStoppedCallback();
this.mirroringStoppedCallback?.();
}
async createMirroringConnection(stream: MediaStream) {
@@ -157,7 +157,7 @@ export default class MirroringSender {
return;
}
this.mirroringConnectedCallback();
this.mirroringConnectedCallback?.();
applyParameters();
});

View File

@@ -25,6 +25,8 @@ export interface Options {
localMediaEnabled: boolean;
/** HTTP server port for local media. */
localMediaServerPort: number;
/** Media stream mirroring for when flinging not possible. */
mediaMirroringEnabled: boolean;
/** Screen mirroring casting. */
mirroringEnabled: boolean;
@@ -85,6 +87,7 @@ export default {
mediaStopOnUnload: false,
localMediaEnabled: true,
localMediaServerPort: 9555,
mediaMirroringEnabled: true,
mirroringEnabled: false,
mirroringAppId: MIRRORING_APP_ID,

View File

@@ -139,6 +139,22 @@
</label>
</div>
<div class="option option--inline">
<div class="option__control">
<input
id="mediaMirroringEnabled"
type="checkbox"
bind:checked={opts.mediaMirroringEnabled}
/>
</div>
<label class="option__label" for="mediaMirroringEnabled">
{_("optionsMediaMirroringEnabled")}
</label>
<div class="option__description">
{_("optionsMediaMirroringEnabledDescription")}
</div>
</div>
<hr />
<div class="option option--inline">