Improve cast client module receiver messages handling

This commit is contained in:
hensm
2022-03-15 09:34:10 +00:00
parent 3030f75590
commit 3c175a740d
3 changed files with 72 additions and 57 deletions

View File

@@ -5,9 +5,9 @@ import { Channel } from "castv2";
import { sendMessage } from "../../lib/nativeMessaging";
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;
@@ -15,10 +15,6 @@ export default class Session extends CastClient {
// Assigned by the receiver once the session is established
public sessionId?: string;
// Platform messaging
private receiverChannel?: Channel;
private receiverRequestId = 0;
// Receiver app messaging
private transportId?: string;
private transportConnection?: Channel;
@@ -64,7 +60,7 @@ export default class Session extends CastClient {
* request response.
*/
if (!this.sessionId) {
// Launch message response only
// Match request ID on the response to the launch request ID.
if (message.requestId !== this.launchRequestId) {
break;
}
@@ -159,18 +155,6 @@ export default class Session extends CastClient {
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(
private appId: string,
private receiverDevice: ReceiverDevice,
@@ -178,6 +162,25 @@ export default class Session extends CastClient {
) {
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", () => {
if (this.sessionId) {
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
});
});
}
}

View File

@@ -2,6 +2,8 @@
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_HEARTBEAT = "urn:x-cast:com.google.cast.tp.heartbeat";
export const NS_RECEIVER = "urn:x-cast:com.google.cast.receiver";
@@ -11,6 +13,7 @@ const HEARTBEAT_INTERVAL_MS = 5000;
interface CastClientConnectOptions {
port?: number;
onReceiverMessage?: (message: ReceiverMessage) => void;
onHeartbeat?: () => void;
}
@@ -21,6 +24,10 @@ export default class CastClient {
protected heartbeatChannel?: Channel;
protected heartbeatIntervalId?: NodeJS.Timeout;
// Platform messaging
private receiverChannel?: Channel;
private receiverRequestId = 0;
constructor(
protected sourceId = "sender-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
* promise that resolves once the client is connected.
@@ -57,25 +76,33 @@ export default class CastClient {
}
});
const connectOpts = {
host,
port: options?.port ?? DEFAULT_PORT
};
this.client.connect(
{
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, () => {
this.connectionChannel = this.createChannel(NS_CONNECTION);
this.heartbeatChannel = this.createChannel(NS_HEARTBEAT);
// Handle receiver messages
this.receiverChannel = this.createChannel(NS_RECEIVER);
this.receiverChannel.on("message", message => {
options?.onReceiverMessage?.(message);
});
this.connectionChannel.send({ type: "CONNECT" });
this.heartbeatChannel.send({ type: "PING" });
this.connectionChannel.send({ type: "CONNECT" });
this.heartbeatChannel.send({ type: "PING" });
this.heartbeatIntervalId = setInterval(() => {
this.heartbeatChannel?.send({ type: "PING" });
options?.onHeartbeat?.();
}, HEARTBEAT_INTERVAL_MS);
this.heartbeatIntervalId = setInterval(() => {
this.heartbeatChannel?.send({ type: "PING" });
options?.onHeartbeat?.();
}, HEARTBEAT_INTERVAL_MS);
resolve();
});
resolve();
}
);
});
}

View File

@@ -1,6 +1,6 @@
"use strict";
import CastClient, { NS_RECEIVER } from "./client";
import CastClient from "./client";
import {
MediaStatus,
@@ -27,13 +27,12 @@ export default class Remote extends CastClient {
constructor(private host: string, private options?: CastRemoteOptions) {
super();
this.connect(host).then(() => {
// Request receiver status
const receiverChannel = this.createChannel(NS_RECEIVER);
receiverChannel.on("message", message => {
super.connect(host, {
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
* results in a `RECEIVER_STATUS` response. If an application
* is running, get the transport ID and make a connection to
* fetch media status updates.
* receive media status updates.
*/
private onReceiverMessage(message: ReceiverMessage) {
if (message.type !== "RECEIVER_STATUS") {
@@ -62,7 +61,7 @@ export default class Remote extends CastClient {
this.options?.onApplicationClose?.();
}
}
// Update status before possible transport init
this.options?.onReceiverStatusUpdate?.(message.status);