mirror of
https://github.com/hensm/fx_cast.git
synced 2026-06-08 08:39:59 +00:00
Improve cast client module receiver messages handling
This commit is contained in:
@@ -5,9 +5,9 @@ import { Channel } from "castv2";
|
|||||||
import { sendMessage } from "../../lib/nativeMessaging";
|
import { sendMessage } from "../../lib/nativeMessaging";
|
||||||
|
|
||||||
import { ReceiverDevice } from "../../types";
|
import { ReceiverDevice } from "../../types";
|
||||||
import { ReceiverApplication, ReceiverMessage, SenderMessage } from "./types";
|
import { ReceiverMessage } from "./types";
|
||||||
|
|
||||||
import CastClient, { NS_CONNECTION, NS_HEARTBEAT, NS_RECEIVER } from "./client";
|
import CastClient, { NS_CONNECTION, NS_HEARTBEAT } from "./client";
|
||||||
|
|
||||||
type OnSessionCreatedCallback = (sessionId: string) => void;
|
type OnSessionCreatedCallback = (sessionId: string) => void;
|
||||||
|
|
||||||
@@ -15,10 +15,6 @@ export default class Session extends CastClient {
|
|||||||
// Assigned by the receiver once the session is established
|
// Assigned by the receiver once the session is established
|
||||||
public sessionId?: string;
|
public sessionId?: string;
|
||||||
|
|
||||||
// Platform messaging
|
|
||||||
private receiverChannel?: Channel;
|
|
||||||
private receiverRequestId = 0;
|
|
||||||
|
|
||||||
// Receiver app messaging
|
// Receiver app messaging
|
||||||
private transportId?: string;
|
private transportId?: string;
|
||||||
private transportConnection?: Channel;
|
private transportConnection?: Channel;
|
||||||
@@ -64,7 +60,7 @@ export default class Session extends CastClient {
|
|||||||
* request response.
|
* request response.
|
||||||
*/
|
*/
|
||||||
if (!this.sessionId) {
|
if (!this.sessionId) {
|
||||||
// Launch message response only
|
// Match request ID on the response to the launch request ID.
|
||||||
if (message.requestId !== this.launchRequestId) {
|
if (message.requestId !== this.launchRequestId) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -159,18 +155,6 @@ export default class Session extends CastClient {
|
|||||||
channel.send(message);
|
channel.send(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
sendReceiverMessage(message: DistributiveOmit<SenderMessage, "requestId">) {
|
|
||||||
if (!this.receiverChannel) {
|
|
||||||
this.receiverChannel = this.createChannel(NS_RECEIVER);
|
|
||||||
this.receiverChannel.on("message", this.onReceiverMessage);
|
|
||||||
}
|
|
||||||
|
|
||||||
const requestId = this.receiverRequestId++;
|
|
||||||
this.receiverChannel?.send({ ...message, requestId });
|
|
||||||
|
|
||||||
return requestId;
|
|
||||||
}
|
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private appId: string,
|
private appId: string,
|
||||||
private receiverDevice: ReceiverDevice,
|
private receiverDevice: ReceiverDevice,
|
||||||
@@ -178,6 +162,25 @@ export default class Session extends CastClient {
|
|||||||
) {
|
) {
|
||||||
super();
|
super();
|
||||||
|
|
||||||
|
super.connect(receiverDevice.host, {
|
||||||
|
onHeartbeat: () => {
|
||||||
|
// Include transport heartbeat with platform heartbeat
|
||||||
|
if (this.transportHeartbeat) {
|
||||||
|
this.transportHeartbeat.send({ type: "PING" });
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onReceiverMessage: message => {
|
||||||
|
this.onReceiverMessage(message);
|
||||||
|
}
|
||||||
|
}).then(() => {
|
||||||
|
// Send a launch request and store the request ID for reference
|
||||||
|
this.launchRequestId = this.sendReceiverMessage({
|
||||||
|
type: "LAUNCH",
|
||||||
|
appId: this.appId
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Handle client connection closed
|
||||||
this.client.on("close", () => {
|
this.client.on("close", () => {
|
||||||
if (this.sessionId) {
|
if (this.sessionId) {
|
||||||
sendMessage({
|
sendMessage({
|
||||||
@@ -186,19 +189,5 @@ export default class Session extends CastClient {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
super.connect(receiverDevice.host, {
|
|
||||||
onHeartbeat: () => {
|
|
||||||
// Include transport heartbeat with platform heartbeat
|
|
||||||
if (this.transportHeartbeat) {
|
|
||||||
this.transportHeartbeat.send({ type: "PING" });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}).then(() => {
|
|
||||||
this.launchRequestId = this.sendReceiverMessage({
|
|
||||||
type: "LAUNCH",
|
|
||||||
appId: this.appId
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,8 @@
|
|||||||
|
|
||||||
import { Channel, Client } from "castv2";
|
import { Channel, Client } from "castv2";
|
||||||
|
|
||||||
|
import { ReceiverMessage, SenderMessage } from "./types";
|
||||||
|
|
||||||
export const NS_CONNECTION = "urn:x-cast:com.google.cast.tp.connection";
|
export const NS_CONNECTION = "urn:x-cast:com.google.cast.tp.connection";
|
||||||
export const NS_HEARTBEAT = "urn:x-cast:com.google.cast.tp.heartbeat";
|
export const NS_HEARTBEAT = "urn:x-cast:com.google.cast.tp.heartbeat";
|
||||||
export const NS_RECEIVER = "urn:x-cast:com.google.cast.receiver";
|
export const NS_RECEIVER = "urn:x-cast:com.google.cast.receiver";
|
||||||
@@ -11,6 +13,7 @@ const HEARTBEAT_INTERVAL_MS = 5000;
|
|||||||
|
|
||||||
interface CastClientConnectOptions {
|
interface CastClientConnectOptions {
|
||||||
port?: number;
|
port?: number;
|
||||||
|
onReceiverMessage?: (message: ReceiverMessage) => void;
|
||||||
onHeartbeat?: () => void;
|
onHeartbeat?: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -21,6 +24,10 @@ export default class CastClient {
|
|||||||
protected heartbeatChannel?: Channel;
|
protected heartbeatChannel?: Channel;
|
||||||
protected heartbeatIntervalId?: NodeJS.Timeout;
|
protected heartbeatIntervalId?: NodeJS.Timeout;
|
||||||
|
|
||||||
|
// Platform messaging
|
||||||
|
private receiverChannel?: Channel;
|
||||||
|
private receiverRequestId = 0;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
protected sourceId = "sender-0",
|
protected sourceId = "sender-0",
|
||||||
protected destinationId = "receiver-0"
|
protected destinationId = "receiver-0"
|
||||||
@@ -43,6 +50,18 @@ export default class CastClient {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sends a message on the receiver channel with the correct
|
||||||
|
* request ID.
|
||||||
|
*/
|
||||||
|
sendReceiverMessage(message: DistributiveOmit<SenderMessage, "requestId">) {
|
||||||
|
if (!this.receiverChannel) return;
|
||||||
|
|
||||||
|
const requestId = this.receiverRequestId++;
|
||||||
|
this.receiverChannel.send({ ...message, requestId });
|
||||||
|
return requestId;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Connects to a cast receiver at a given host, returning a
|
* Connects to a cast receiver at a given host, returning a
|
||||||
* promise that resolves once the client is connected.
|
* promise that resolves once the client is connected.
|
||||||
@@ -57,25 +76,33 @@ export default class CastClient {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const connectOpts = {
|
this.client.connect(
|
||||||
host,
|
{
|
||||||
port: options?.port ?? DEFAULT_PORT
|
host,
|
||||||
};
|
port: options?.port ?? DEFAULT_PORT
|
||||||
|
},
|
||||||
|
// On connection callback
|
||||||
|
() => {
|
||||||
|
this.connectionChannel = this.createChannel(NS_CONNECTION);
|
||||||
|
this.heartbeatChannel = this.createChannel(NS_HEARTBEAT);
|
||||||
|
|
||||||
this.client.connect(connectOpts, () => {
|
// Handle receiver messages
|
||||||
this.connectionChannel = this.createChannel(NS_CONNECTION);
|
this.receiverChannel = this.createChannel(NS_RECEIVER);
|
||||||
this.heartbeatChannel = this.createChannel(NS_HEARTBEAT);
|
this.receiverChannel.on("message", message => {
|
||||||
|
options?.onReceiverMessage?.(message);
|
||||||
|
});
|
||||||
|
|
||||||
this.connectionChannel.send({ type: "CONNECT" });
|
this.connectionChannel.send({ type: "CONNECT" });
|
||||||
this.heartbeatChannel.send({ type: "PING" });
|
this.heartbeatChannel.send({ type: "PING" });
|
||||||
|
|
||||||
this.heartbeatIntervalId = setInterval(() => {
|
this.heartbeatIntervalId = setInterval(() => {
|
||||||
this.heartbeatChannel?.send({ type: "PING" });
|
this.heartbeatChannel?.send({ type: "PING" });
|
||||||
options?.onHeartbeat?.();
|
options?.onHeartbeat?.();
|
||||||
}, HEARTBEAT_INTERVAL_MS);
|
}, HEARTBEAT_INTERVAL_MS);
|
||||||
|
|
||||||
resolve();
|
resolve();
|
||||||
});
|
}
|
||||||
|
);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
import CastClient, { NS_RECEIVER } from "./client";
|
import CastClient from "./client";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
MediaStatus,
|
MediaStatus,
|
||||||
@@ -27,13 +27,12 @@ export default class Remote extends CastClient {
|
|||||||
|
|
||||||
constructor(private host: string, private options?: CastRemoteOptions) {
|
constructor(private host: string, private options?: CastRemoteOptions) {
|
||||||
super();
|
super();
|
||||||
this.connect(host).then(() => {
|
super.connect(host, {
|
||||||
// Request receiver status
|
onReceiverMessage: message => {
|
||||||
const receiverChannel = this.createChannel(NS_RECEIVER);
|
|
||||||
receiverChannel.on("message", message => {
|
|
||||||
this.onReceiverMessage(message);
|
this.onReceiverMessage(message);
|
||||||
});
|
}
|
||||||
receiverChannel.send({ type: "GET_STATUS", requestId: 1 });
|
}).then(() => {
|
||||||
|
this.sendReceiverMessage({ type: "GET_STATUS" });
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -47,7 +46,7 @@ export default class Remote extends CastClient {
|
|||||||
* On initial connection, a `GET_STATUS` message is sent that
|
* On initial connection, a `GET_STATUS` message is sent that
|
||||||
* results in a `RECEIVER_STATUS` response. If an application
|
* results in a `RECEIVER_STATUS` response. If an application
|
||||||
* is running, get the transport ID and make a connection to
|
* is running, get the transport ID and make a connection to
|
||||||
* fetch media status updates.
|
* receive media status updates.
|
||||||
*/
|
*/
|
||||||
private onReceiverMessage(message: ReceiverMessage) {
|
private onReceiverMessage(message: ReceiverMessage) {
|
||||||
if (message.type !== "RECEIVER_STATUS") {
|
if (message.type !== "RECEIVER_STATUS") {
|
||||||
@@ -62,7 +61,7 @@ export default class Remote extends CastClient {
|
|||||||
this.options?.onApplicationClose?.();
|
this.options?.onApplicationClose?.();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update status before possible transport init
|
// Update status before possible transport init
|
||||||
this.options?.onReceiverStatusUpdate?.(message.status);
|
this.options?.onReceiverStatusUpdate?.(message.status);
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user