mirror of
https://github.com/hensm/fx_cast.git
synced 2026-06-08 08:39:59 +00:00
WIP
This commit is contained in:
@@ -355,6 +355,15 @@
|
|||||||
"description": "Media stop on unload checkbox label."
|
"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": {
|
"optionsLocalMediaCategoryName": {
|
||||||
"message": "Local media casting",
|
"message": "Local media casting",
|
||||||
"description": "Options page local media category title."
|
"description": "Options page local media category title."
|
||||||
|
|||||||
@@ -398,6 +398,12 @@ const castManager = new (class {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
const mirroringAppId = await options.get("mirroringAppId");
|
||||||
|
const requestedMediaType =
|
||||||
|
sessionRequest.appId === mirroringAppId
|
||||||
|
? ReceiverSelectorMediaType.Screen
|
||||||
|
: ReceiverSelectorMediaType.App;
|
||||||
|
|
||||||
const selection = await getReceiverSelection({
|
const selection = await getReceiverSelection({
|
||||||
castInstance: instance
|
castInstance: instance
|
||||||
});
|
});
|
||||||
@@ -416,7 +422,7 @@ const castManager = new (class {
|
|||||||
* been changed, we need to cancel the current
|
* been changed, we need to cancel the current
|
||||||
* sender and switch it out for the right one.
|
* sender and switch it out for the right one.
|
||||||
*/
|
*/
|
||||||
if (selection.mediaType !== ReceiverSelectorMediaType.App) {
|
if (selection.mediaType !== requestedMediaType) {
|
||||||
instance.contentPort.postMessage({
|
instance.contentPort.postMessage({
|
||||||
subject: "cast:sessionRequestCancelled"
|
subject: "cast:sessionRequestCancelled"
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ const whitelistChildMenuPatterns = new Map<MenuId, string>();
|
|||||||
export async function initMenus() {
|
export async function initMenus() {
|
||||||
logger.info("init (menus)");
|
logger.info("init (menus)");
|
||||||
|
|
||||||
const opts = await options.getAll();
|
let opts = await options.getAll();
|
||||||
|
|
||||||
// Global "Cast..." menu item
|
// Global "Cast..." menu item
|
||||||
menuIdCast = browser.menus.create({
|
menuIdCast = browser.menus.create({
|
||||||
@@ -39,15 +39,25 @@ export async function initMenus() {
|
|||||||
icons: { "16": "icons/icon.svg" } // browser_action context
|
icons: { "16": "icons/icon.svg" } // browser_action context
|
||||||
});
|
});
|
||||||
|
|
||||||
// <video>/<audio> "Cast..." context menu item
|
function createCastMediaMenu() {
|
||||||
menuIdCastMedia = browser.menus.create({
|
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"],
|
contexts: ["audio", "video", "image"],
|
||||||
title: _("contextCast"),
|
title: _("contextCast"),
|
||||||
visible: opts.mediaEnabled,
|
visible: opts.mediaEnabled,
|
||||||
targetUrlPatterns: opts.localMediaEnabled
|
targetUrlPatterns: targetUrlPatterns
|
||||||
? URL_PATTERNS_ALL
|
|
||||||
: URL_PATTERNS_REMOTE
|
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// <video>/<audio> "Cast..." context menu item
|
||||||
|
menuIdCastMedia = createCastMediaMenu();
|
||||||
|
|
||||||
menuIdWhitelist = browser.menus.create({
|
menuIdWhitelist = browser.menus.create({
|
||||||
contexts: ["browser_action"],
|
contexts: ["browser_action"],
|
||||||
@@ -118,20 +128,21 @@ export async function initMenus() {
|
|||||||
|
|
||||||
options.addEventListener("changed", async ev => {
|
options.addEventListener("changed", async ev => {
|
||||||
const alteredOpts = ev.detail;
|
const alteredOpts = ev.detail;
|
||||||
const newOpts = await options.getAll();
|
opts = await options.getAll();
|
||||||
|
|
||||||
if (menuIdCastMedia && alteredOpts.includes("mediaEnabled")) {
|
if (menuIdCastMedia && alteredOpts.includes("mediaEnabled")) {
|
||||||
browser.menus.update(menuIdCastMedia, {
|
browser.menus.update(menuIdCastMedia, {
|
||||||
visible: newOpts.mediaEnabled
|
visible: opts.mediaEnabled
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (menuIdCastMedia && alteredOpts.includes("localMediaEnabled")) {
|
if (
|
||||||
browser.menus.update(menuIdCastMedia, {
|
menuIdCastMedia &&
|
||||||
targetUrlPatterns: newOpts.localMediaEnabled
|
(alteredOpts.includes("localMediaEnabled") ||
|
||||||
? URL_PATTERNS_ALL
|
alteredOpts.includes("mediaMirroringEnabled"))
|
||||||
: URL_PATTERNS_REMOTE
|
) {
|
||||||
});
|
browser.menus.remove(menuIdCastMedia);
|
||||||
|
menuIdCastMedia = createCastMediaMenu();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -183,7 +194,6 @@ async function onMenuClicked(
|
|||||||
}
|
}
|
||||||
|
|
||||||
case menuIdCastMedia:
|
case menuIdCastMedia:
|
||||||
if (info.srcUrl) {
|
|
||||||
await browser.tabs.executeScript(tab.id, {
|
await browser.tabs.executeScript(tab.id, {
|
||||||
code: stringify`
|
code: stringify`
|
||||||
window.mediaUrl = ${info.srcUrl};
|
window.mediaUrl = ${info.srcUrl};
|
||||||
@@ -196,7 +206,6 @@ async function onMenuClicked(
|
|||||||
file: "cast/senders/media.js",
|
file: "cast/senders/media.js",
|
||||||
frameId: info.frameId
|
frameId: info.frameId
|
||||||
});
|
});
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import type Session from "../sdk/Session";
|
|||||||
import type Media from "../sdk/media/Media";
|
import type Media from "../sdk/media/Media";
|
||||||
|
|
||||||
import cast, { ensureInit, CastPort } from "../export";
|
import cast, { ensureInit, CastPort } from "../export";
|
||||||
|
import MirroringSender from "./mirroring";
|
||||||
|
|
||||||
const logger = new Logger("fx_cast [media sender]");
|
const logger = new Logger("fx_cast [media sender]");
|
||||||
|
|
||||||
@@ -359,8 +360,18 @@ if (window.location.protocol !== "moz-extension:") {
|
|||||||
) as HTMLMediaElement;
|
) as HTMLMediaElement;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (window_.mediaUrl) {
|
||||||
new MediaSender({
|
new MediaSender({
|
||||||
mediaUrl: window_.mediaUrl,
|
mediaUrl: window_.mediaUrl,
|
||||||
mediaElement
|
mediaElement
|
||||||
});
|
});
|
||||||
|
} else {
|
||||||
|
const mirroringSender = new MirroringSender({
|
||||||
|
onSessionCreated() {
|
||||||
|
mirroringSender.createMirroringConnection(
|
||||||
|
(mediaElement as any).mozCaptureStream() as MediaStream
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,17 +19,17 @@ type MirroringAppMessage =
|
|||||||
| { subject: "close" };
|
| { subject: "close" };
|
||||||
|
|
||||||
interface MirroringSenderOpts {
|
interface MirroringSenderOpts {
|
||||||
receiverDevice: ReceiverDevice;
|
receiverDevice?: ReceiverDevice;
|
||||||
onSessionCreated: () => void;
|
onSessionCreated?: () => void;
|
||||||
onMirroringConnected: () => void;
|
onMirroringConnected?: () => void;
|
||||||
onMirroringStopped: () => void;
|
onMirroringStopped?: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default class MirroringSender {
|
export default class MirroringSender {
|
||||||
private receiverDevice: ReceiverDevice;
|
private receiverDevice?: ReceiverDevice;
|
||||||
private sessionCreatedCallback: () => void;
|
private sessionCreatedCallback?: () => void;
|
||||||
private mirroringConnectedCallback: () => void;
|
private mirroringConnectedCallback?: () => void;
|
||||||
private mirroringStoppedCallback: () => void;
|
private mirroringStoppedCallback?: () => void;
|
||||||
|
|
||||||
private session?: Session;
|
private session?: Session;
|
||||||
private wasSessionRequested = false;
|
private wasSessionRequested = false;
|
||||||
@@ -96,7 +96,7 @@ export default class MirroringSender {
|
|||||||
cast.requestSession(
|
cast.requestSession(
|
||||||
session => {
|
session => {
|
||||||
this.session = session;
|
this.session = session;
|
||||||
this.sessionCreatedCallback();
|
this.sessionCreatedCallback?.();
|
||||||
},
|
},
|
||||||
err => {
|
err => {
|
||||||
logger.error("Session request failed", err);
|
logger.error("Session request failed", err);
|
||||||
@@ -114,7 +114,7 @@ export default class MirroringSender {
|
|||||||
this.peerConnection?.close();
|
this.peerConnection?.close();
|
||||||
this.session?.stop();
|
this.session?.stop();
|
||||||
|
|
||||||
this.mirroringStoppedCallback();
|
this.mirroringStoppedCallback?.();
|
||||||
}
|
}
|
||||||
|
|
||||||
async createMirroringConnection(stream: MediaStream) {
|
async createMirroringConnection(stream: MediaStream) {
|
||||||
@@ -157,7 +157,7 @@ export default class MirroringSender {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.mirroringConnectedCallback();
|
this.mirroringConnectedCallback?.();
|
||||||
applyParameters();
|
applyParameters();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -25,6 +25,8 @@ export interface Options {
|
|||||||
localMediaEnabled: boolean;
|
localMediaEnabled: boolean;
|
||||||
/** HTTP server port for local media. */
|
/** HTTP server port for local media. */
|
||||||
localMediaServerPort: number;
|
localMediaServerPort: number;
|
||||||
|
/** Media stream mirroring for when flinging not possible. */
|
||||||
|
mediaMirroringEnabled: boolean;
|
||||||
|
|
||||||
/** Screen mirroring casting. */
|
/** Screen mirroring casting. */
|
||||||
mirroringEnabled: boolean;
|
mirroringEnabled: boolean;
|
||||||
@@ -85,6 +87,7 @@ export default {
|
|||||||
mediaStopOnUnload: false,
|
mediaStopOnUnload: false,
|
||||||
localMediaEnabled: true,
|
localMediaEnabled: true,
|
||||||
localMediaServerPort: 9555,
|
localMediaServerPort: 9555,
|
||||||
|
mediaMirroringEnabled: true,
|
||||||
|
|
||||||
mirroringEnabled: false,
|
mirroringEnabled: false,
|
||||||
mirroringAppId: MIRRORING_APP_ID,
|
mirroringAppId: MIRRORING_APP_ID,
|
||||||
|
|||||||
@@ -139,6 +139,22 @@
|
|||||||
</label>
|
</label>
|
||||||
</div>
|
</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 />
|
<hr />
|
||||||
|
|
||||||
<div class="option option--inline">
|
<div class="option option--inline">
|
||||||
|
|||||||
Reference in New Issue
Block a user