@corewire/airplay 0.0.0 → 1.0.0-next.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +30 -1
- package/dist/chunk-w6R9maHv.mjs +18 -0
- package/dist/describe-CAEDGJLu.mjs +12457 -0
- package/dist/identifiers-CsXXbYkW.mjs +67 -0
- package/dist/index-BlcOBfSz.d.mts +87 -0
- package/dist/index-YqIv0vCR.d.mts +135 -0
- package/dist/index.d.mts +105 -0
- package/dist/index.mjs +3637 -0
- package/dist/mrp.d.mts +687 -0
- package/dist/mrp.mjs +2102 -0
- package/dist/raop.d.mts +344 -0
- package/dist/raop.mjs +1248 -0
- package/dist/rtsp-BWFaYW5H.mjs +374 -0
- package/dist/rtsp.d.mts +2 -0
- package/dist/rtsp.mjs +3 -0
- package/dist/session-oDF9A9as.d.mts +13915 -0
- package/package.json +72 -18
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { randomBytes } from "node:crypto";
|
|
2
|
+
|
|
3
|
+
//#region src/audio/sourceSession.ts
|
|
4
|
+
/** Owns one use of a source. Stop settles readers even when a custom source cannot cancel its read. */
|
|
5
|
+
var AudioSourceSession = class {
|
|
6
|
+
#source;
|
|
7
|
+
#readers = /* @__PURE__ */ new Set();
|
|
8
|
+
#starting;
|
|
9
|
+
#stopping;
|
|
10
|
+
#stopped = false;
|
|
11
|
+
constructor(source) {
|
|
12
|
+
this.#source = source;
|
|
13
|
+
}
|
|
14
|
+
get duration() {
|
|
15
|
+
return this.#source.duration;
|
|
16
|
+
}
|
|
17
|
+
get stopped() {
|
|
18
|
+
return this.#stopped;
|
|
19
|
+
}
|
|
20
|
+
start() {
|
|
21
|
+
if (this.#stopped) return Promise.resolve();
|
|
22
|
+
return this.#starting ??= Promise.resolve().then(() => this.#stopped ? void 0 : this.#source.start());
|
|
23
|
+
}
|
|
24
|
+
async readFrames(count) {
|
|
25
|
+
if (this.#stopped) return null;
|
|
26
|
+
return new Promise((resolve, reject) => {
|
|
27
|
+
const done = (value) => {
|
|
28
|
+
this.#readers.delete(done);
|
|
29
|
+
resolve(this.#stopped ? null : value);
|
|
30
|
+
};
|
|
31
|
+
this.#readers.add(done);
|
|
32
|
+
Promise.resolve().then(() => this.#stopped ? null : this.#source.readFrames(count)).then(done, (error) => {
|
|
33
|
+
this.#readers.delete(done);
|
|
34
|
+
reject(error);
|
|
35
|
+
});
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
stop() {
|
|
39
|
+
this.#stopped = true;
|
|
40
|
+
for (const reader of this.#readers) reader(null);
|
|
41
|
+
return this.#stopping ??= Promise.resolve(this.#starting).catch(() => {}).then(() => this.#source.stop());
|
|
42
|
+
}
|
|
43
|
+
async reset() {
|
|
44
|
+
throw new Error("Create a new audio session after resetting the source.");
|
|
45
|
+
}
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
//#endregion
|
|
49
|
+
//#region src/identifiers.ts
|
|
50
|
+
/**
|
|
51
|
+
* Generates a random DACP Active-Remote identifier for the RTSP headers: a
|
|
52
|
+
* 32-bit unsigned integer as a decimal string.
|
|
53
|
+
*/
|
|
54
|
+
function generateActiveRemoteId() {
|
|
55
|
+
return randomBytes(4).readUInt32BE(0).toString(10);
|
|
56
|
+
}
|
|
57
|
+
/** Generates a random DACP-ID: a 64-bit integer as an uppercase hex string. */
|
|
58
|
+
function generateDacpId() {
|
|
59
|
+
return randomBytes(8).toString("hex").toUpperCase();
|
|
60
|
+
}
|
|
61
|
+
/** Generates a random RTSP session identifier: a 32-bit unsigned integer as a decimal string. */
|
|
62
|
+
function generateSessionId() {
|
|
63
|
+
return randomBytes(4).readUInt32BE(0).toString(10);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
//#endregion
|
|
67
|
+
export { AudioSourceSession as i, generateDacpId as n, generateSessionId as r, generateActiveRemoteId as t };
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { Connection, Context } from "@corewire/core/transport";
|
|
2
|
+
|
|
3
|
+
//#region src/rtsp/encoding.d.ts
|
|
4
|
+
/** The RTSP and HTTP methods of the Apple protocols. */
|
|
5
|
+
type Method = "ANNOUNCE" | "FLUSH" | "FLUSHBUFFERED" | "GET" | "GET_PARAMETER" | "OPTIONS" | "POST" | "PUT" | "RECORD" | "SETUP" | "SET_PARAMETER" | "TEARDOWN";
|
|
6
|
+
type RtspRequest = {
|
|
7
|
+
/** Headers with capitalized names. */readonly headers: Record<string, string>;
|
|
8
|
+
readonly method: Method; /** Request target, for example `/info` or `/pair-setup`. */
|
|
9
|
+
readonly path: string; /** Empty when the request has no body. */
|
|
10
|
+
readonly body: Buffer; /** Bytes the request took from the input buffer, headers and body. */
|
|
11
|
+
readonly requestLength: number;
|
|
12
|
+
};
|
|
13
|
+
type RtspResponse = {
|
|
14
|
+
readonly response: Response; /** Bytes the response took from the input buffer, headers and body. */
|
|
15
|
+
readonly responseLength: number;
|
|
16
|
+
};
|
|
17
|
+
type BuildResponseOptions = {
|
|
18
|
+
readonly status: number;
|
|
19
|
+
readonly statusText: string; /** Extra headers; Content-Length is set from the body. */
|
|
20
|
+
readonly headers?: Record<string, string | number>;
|
|
21
|
+
readonly body?: Buffer; /** Protocol of the status line, `'RTSP/1.0'` by default. */
|
|
22
|
+
readonly protocol?: "RTSP/1.0" | "HTTP/1.1";
|
|
23
|
+
};
|
|
24
|
+
/** Serializes an RTSP or HTTP response, with `Content-Length` set from the body. */
|
|
25
|
+
declare function buildResponse(options: BuildResponseOptions): Buffer;
|
|
26
|
+
/**
|
|
27
|
+
* Parses an RTSP or HTTP request from the start of a buffer that fills as
|
|
28
|
+
* TCP data arrives.
|
|
29
|
+
*
|
|
30
|
+
* @returns The request, or `null` while the headers or body are incomplete.
|
|
31
|
+
* @throws Error if the request line is malformed.
|
|
32
|
+
*/
|
|
33
|
+
declare function parseRequest(buffer: Buffer): RtspRequest | null;
|
|
34
|
+
/**
|
|
35
|
+
* Parses an RTSP or HTTP response from the start of a buffer that fills as
|
|
36
|
+
* TCP data arrives.
|
|
37
|
+
*
|
|
38
|
+
* @returns The response, or `null` while the headers or body are incomplete.
|
|
39
|
+
* @throws Error if the status line is malformed.
|
|
40
|
+
*/
|
|
41
|
+
declare function parseResponse(buffer: Buffer): RtspResponse | null;
|
|
42
|
+
//#endregion
|
|
43
|
+
//#region src/rtsp/client.d.ts
|
|
44
|
+
type ExchangeOptions = {
|
|
45
|
+
/** Content-Type header; `application/x-apple-binary-plist` for a plain object body. */contentType?: string; /** Extra headers, merged after the default headers. */
|
|
46
|
+
headers?: Record<string, string>; /** A plain object goes as binary plist; strings and buffers go as they are. */
|
|
47
|
+
body?: Buffer | string | Record<string, unknown>; /** Resolves a non-OK response instead of rejecting with {@link InvalidResponseError}. */
|
|
48
|
+
allowError?: boolean; /** Protocol of the request line, `'RTSP/1.0'` by default. */
|
|
49
|
+
protocol?: "RTSP/1.0" | "HTTP/1.1"; /** Response timeout in milliseconds, `HTTP_TIMEOUT` by default. */
|
|
50
|
+
timeout?: number;
|
|
51
|
+
};
|
|
52
|
+
/**
|
|
53
|
+
* RTSP over TCP with CSeq-based response tracking and binary-plist serialization of object bodies.
|
|
54
|
+
* Keeps ciphertext and plaintext separate to handle partial TCP delivery.
|
|
55
|
+
* Override {@link transformIncoming}, {@link transformOutgoing} and {@link getDefaultHeaders} for encryption and protocol headers.
|
|
56
|
+
*/
|
|
57
|
+
declare class RtspClient extends Connection<{}> {
|
|
58
|
+
#private;
|
|
59
|
+
constructor(context: Context, address: string, port: number);
|
|
60
|
+
/**
|
|
61
|
+
* Headers on every request, after CSeq and before the headers of
|
|
62
|
+
* {@link ExchangeOptions.headers}. A subclass adds its session headers
|
|
63
|
+
* here, such as DACP-ID and Active-Remote.
|
|
64
|
+
*/
|
|
65
|
+
protected getDefaultHeaders(): Record<string, string | number>;
|
|
66
|
+
/**
|
|
67
|
+
* Decrypts incoming TCP data before parsing; a subclass overrides it.
|
|
68
|
+
*
|
|
69
|
+
* @returns The plaintext, with the consumed byte count when not all input
|
|
70
|
+
* was used, or `false` until a complete encrypted block arrived.
|
|
71
|
+
*/
|
|
72
|
+
protected transformIncoming(data: Buffer): Buffer | false | {
|
|
73
|
+
data: Buffer;
|
|
74
|
+
consumed: number;
|
|
75
|
+
};
|
|
76
|
+
/** Encrypts a formatted request before it goes to the socket; a subclass overrides it. */
|
|
77
|
+
protected transformOutgoing(data: Buffer): Buffer;
|
|
78
|
+
/**
|
|
79
|
+
* Sends a request with CSeq tracking and a response timeout. Object bodies become binary plists.
|
|
80
|
+
*
|
|
81
|
+
* @throws TimeoutError if no response arrives in time.
|
|
82
|
+
* @throws InvalidResponseError for non-OK status unless `allowError` is set.
|
|
83
|
+
*/
|
|
84
|
+
protected exchange(method: Method, path: string, options?: ExchangeOptions): Promise<Response>;
|
|
85
|
+
}
|
|
86
|
+
//#endregion
|
|
87
|
+
export { RtspRequest as a, parseRequest as c, Method as i, parseResponse as l, RtspClient as n, RtspResponse as o, BuildResponseOptions as r, buildResponse as s, ExchangeOptions as t };
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
import { Logger } from "@corewire/core";
|
|
2
|
+
import { RemoteInfo } from "node:dgram";
|
|
3
|
+
|
|
4
|
+
//#region src/audio/source.d.ts
|
|
5
|
+
/**
|
|
6
|
+
* Interleaved 44100 Hz stereo PCM, signed 16-bit big-endian, with complete frames.
|
|
7
|
+
* Stream owners start the source once and stop it on completion, cancellation or failure.
|
|
8
|
+
*/
|
|
9
|
+
interface AudioSource {
|
|
10
|
+
/** Total duration in seconds. */
|
|
11
|
+
get duration(): number;
|
|
12
|
+
/**
|
|
13
|
+
* Reads up to `count` frames.
|
|
14
|
+
*
|
|
15
|
+
* @returns The PCM data, or null once the source is exhausted.
|
|
16
|
+
*/
|
|
17
|
+
readFrames(count: number): Promise<Buffer | null>;
|
|
18
|
+
/** Rewinds the source so it can be read again. */
|
|
19
|
+
reset(): Promise<void>;
|
|
20
|
+
/** Starts the underlying decoders or streams. */
|
|
21
|
+
start(): Promise<void>;
|
|
22
|
+
/** Stops the source and releases its resources. */
|
|
23
|
+
stop(): Promise<void>;
|
|
24
|
+
}
|
|
25
|
+
//#endregion
|
|
26
|
+
//#region src/timing/ntp.d.ts
|
|
27
|
+
/**
|
|
28
|
+
* NTP timing server for AirPlay audio synchronization.
|
|
29
|
+
*
|
|
30
|
+
* During audio streaming, Apple devices send NTP timing requests to sync their
|
|
31
|
+
* clock with the controller. The server answers with the current wall-clock
|
|
32
|
+
* time, from which the device derives the playback offset for multiroom audio.
|
|
33
|
+
* It uses `Date.now()`, not the monotonic `process.hrtime`, because NTP
|
|
34
|
+
* timestamps are anchored to the Unix epoch.
|
|
35
|
+
*/
|
|
36
|
+
declare class TimingServer {
|
|
37
|
+
#private;
|
|
38
|
+
/** The UDP port the timing server is listening on, or 0 if not yet bound. */
|
|
39
|
+
get port(): number;
|
|
40
|
+
/** @param options.logger - Receives the errors of the server; a `timing-server` logger when omitted. */
|
|
41
|
+
constructor(options?: {
|
|
42
|
+
readonly logger?: Logger;
|
|
43
|
+
});
|
|
44
|
+
/** Closes the UDP socket and resets the port. */
|
|
45
|
+
close(): void;
|
|
46
|
+
/**
|
|
47
|
+
* Binds the UDP socket to a random free port and starts answering NTP
|
|
48
|
+
* timing requests. Rejects when the socket fails to bind.
|
|
49
|
+
*/
|
|
50
|
+
listen(): Promise<void>;
|
|
51
|
+
onError(err: Error): void;
|
|
52
|
+
/**
|
|
53
|
+
* Answers an NTP timing request. The reference timestamp echoes the send
|
|
54
|
+
* time of the request; receive and send time are both now.
|
|
55
|
+
*/
|
|
56
|
+
onMessage(data: Buffer, info: RemoteInfo): void;
|
|
57
|
+
}
|
|
58
|
+
//#endregion
|
|
59
|
+
//#region src/timing/ptp/index.d.ts
|
|
60
|
+
/**
|
|
61
|
+
* State of the PTP master.
|
|
62
|
+
*
|
|
63
|
+
* - `idle`: the sockets are not bound yet.
|
|
64
|
+
* - `master`: grandmaster, with the Sync and Announce cadences running.
|
|
65
|
+
* - `stopped`: yielded to a peer with a higher priority, or stopped.
|
|
66
|
+
*/
|
|
67
|
+
type PtpMasterState = "idle" | "master" | "stopped";
|
|
68
|
+
/**
|
|
69
|
+
* IEEE 1588 (PTPv2) grandmaster for AirPlay 2 HomePods.
|
|
70
|
+
*
|
|
71
|
+
* A HomePod with `SupportsPTP` (feature bit 41) expects the sender to be the
|
|
72
|
+
* PTP grandmaster. The master binds the two PTP UDP ports, sends Sync and
|
|
73
|
+
* Announce at the macOS rates and answers DelayReq with DelayResp. When a peer
|
|
74
|
+
* with a higher priority announces itself, it sends a Signaling message with
|
|
75
|
+
* `logMessageInterval = 0x7E` and stops.
|
|
76
|
+
*
|
|
77
|
+
* After that yield, audio timestamps keep using the local wall clock; there
|
|
78
|
+
* is no PTP slave. HomePods accept this as long as the Sync messages stop.
|
|
79
|
+
*/
|
|
80
|
+
declare class PtpMaster {
|
|
81
|
+
#private;
|
|
82
|
+
/** UDP port for PTP event messages: 319, or an ephemeral port as fallback. */
|
|
83
|
+
get eventPort(): number;
|
|
84
|
+
/** UDP port for PTP general messages: 320, or an ephemeral port as fallback. */
|
|
85
|
+
get generalPort(): number;
|
|
86
|
+
get state(): PtpMasterState;
|
|
87
|
+
/** Our 8-byte EUI-64 clock identity, as advertised to peers. */
|
|
88
|
+
get clockIdentity(): Buffer;
|
|
89
|
+
/** Number of Sync messages sent to the peer. */
|
|
90
|
+
get syncsSent(): number;
|
|
91
|
+
/** Number of Announce messages sent to the peer. */
|
|
92
|
+
get announcesSent(): number;
|
|
93
|
+
/** Number of DelayReq messages received from any peer. */
|
|
94
|
+
get delayReqsReceived(): number;
|
|
95
|
+
/** Number of DelayResp messages sent in reply. */
|
|
96
|
+
get delayRespsSent(): number;
|
|
97
|
+
/** Number of Announce messages received from peers. */
|
|
98
|
+
get announcesReceived(): number;
|
|
99
|
+
/**
|
|
100
|
+
* @param peerAddress - IPv4 address of the receiver, the destination of Sync, FollowUp and Announce.
|
|
101
|
+
*/
|
|
102
|
+
constructor(peerAddress: string);
|
|
103
|
+
/**
|
|
104
|
+
* Binds both PTP sockets and starts acting as the grandmaster. It prefers
|
|
105
|
+
* the privileged ports 319 and 320 and falls back to ephemeral ports on
|
|
106
|
+
* `EACCES`, so it runs on Linux without root. Some HomePods reject PTP on
|
|
107
|
+
* non-standard ports.
|
|
108
|
+
*
|
|
109
|
+
* @returns The bound event and general ports.
|
|
110
|
+
*/
|
|
111
|
+
start(): Promise<{
|
|
112
|
+
eventPort: number;
|
|
113
|
+
generalPort: number;
|
|
114
|
+
}>;
|
|
115
|
+
/**
|
|
116
|
+
* Stops the cadence timers and closes both sockets. Safe to call more than once.
|
|
117
|
+
*/
|
|
118
|
+
stop(): void;
|
|
119
|
+
/**
|
|
120
|
+
* Answers a DelayReq on the event port with a DelayResp that carries our
|
|
121
|
+
* receive timestamp. A master ignores the other event messages.
|
|
122
|
+
*/
|
|
123
|
+
onEventPacket(buf: Buffer, info: RemoteInfo): void;
|
|
124
|
+
/**
|
|
125
|
+
* Handles an Announce on the general port. When the peer has a strictly
|
|
126
|
+
* better priority1 than ours, the master yields and stops.
|
|
127
|
+
*/
|
|
128
|
+
onGeneralPacket(buf: Buffer, info: RemoteInfo): void;
|
|
129
|
+
/** Logs the error only; {@link stop} owns the socket lifecycle. */
|
|
130
|
+
onEventError(err: Error): void;
|
|
131
|
+
/** Logs the error only; {@link stop} owns the socket lifecycle. */
|
|
132
|
+
onGeneralError(err: Error): void;
|
|
133
|
+
}
|
|
134
|
+
//#endregion
|
|
135
|
+
export { TimingServer as n, AudioSource as r, PtpMaster as t };
|
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import { n as TimingServer, r as AudioSource, t as PtpMaster } from "./index-YqIv0vCR.mjs";
|
|
2
|
+
import { $ as EventChannel, Q as UrlPlayback, Z as PlaybackInfo, a as PairOptions, at as ControlChannel, c as RemoteControlOptions, d as DataChannel, et as EventChannelEventMap, f as DataChannelEventMap, i as ConnectOptions, it as AudioStreamStats, l as SessionCloseReason, n as connect, nt as PlayQueueState, o as PlayUrlOptions, r as AirPlayTarget, rt as AudioPlayback, s as RemoteControlChannels, t as AirPlaySession, tt as PlayQueueCommand, u as StreamAudioOptions } from "./session-oDF9A9as.mjs";
|
|
3
|
+
import { Context } from "@corewire/core/transport";
|
|
4
|
+
import { AccessoryCredentials, AccessoryKeys, PairSetup } from "@corewire/core/pairing";
|
|
5
|
+
import { DiscoverOptions, DiscoveryResult } from "@corewire/core/discovery";
|
|
6
|
+
|
|
7
|
+
//#region src/pairing.d.ts
|
|
8
|
+
/**
|
|
9
|
+
* Starts a PIN pairing: connects, fetches `/info` and asks the receiver to show its PIN. Finish
|
|
10
|
+
* with {@link PinPairing.pin}, which runs pair-setup M1 to M6 and returns the long-term credentials.
|
|
11
|
+
*
|
|
12
|
+
* @throws {PairingError} When the receiver refuses to start pairing.
|
|
13
|
+
*/
|
|
14
|
+
declare function pair(target: AirPlayTarget, options?: PairOptions): Promise<PinPairing>;
|
|
15
|
+
/** A PIN pairing whose PIN the receiver shows now. One connection, used once. */
|
|
16
|
+
declare class PinPairing {
|
|
17
|
+
#private;
|
|
18
|
+
/** Aborts when the pairing connection closes. */
|
|
19
|
+
get signal(): AbortSignal;
|
|
20
|
+
constructor(control: ControlChannel, setup: PairSetup);
|
|
21
|
+
/**
|
|
22
|
+
* Runs pair-setup M1 to M6 with the PIN from the screen. A wrong PIN leaves the connection
|
|
23
|
+
* open; call {@link close} when giving up.
|
|
24
|
+
*
|
|
25
|
+
* @throws {PairingError} When a step fails or the PIN is wrong.
|
|
26
|
+
*/
|
|
27
|
+
pin(code: string): Promise<AccessoryCredentials>;
|
|
28
|
+
/** Closes the pairing connection. */
|
|
29
|
+
close(): void;
|
|
30
|
+
}
|
|
31
|
+
//#endregion
|
|
32
|
+
//#region src/airplayFeatures.d.ts
|
|
33
|
+
/**
|
|
34
|
+
* AirPlay feature flags as bigint bits of the `features` field in the mDNS TXT
|
|
35
|
+
* record. Each flag is a capability the device advertises.
|
|
36
|
+
*/
|
|
37
|
+
declare const AirPlayFeatureFlags: Record<string, bigint>;
|
|
38
|
+
type AirPlayFeatureFlagName = string;
|
|
39
|
+
/** The pairing an AirPlay device requires before it accepts a connection. */
|
|
40
|
+
type PairingRequirement = "none" | "pin" | "transient" | "homekit";
|
|
41
|
+
declare function hasFeatureFlag(features: bigint, flag: bigint): boolean;
|
|
42
|
+
/** Names of the flags set in the bitmask, for debugging and diagnostics. */
|
|
43
|
+
declare function describeFlags(features: bigint): AirPlayFeatureFlagName[];
|
|
44
|
+
/**
|
|
45
|
+
* Reads the AirPlay version from the mDNS TXT record. The flag
|
|
46
|
+
* SupportsUnifiedMediaControl or SupportsCoreUtilsPairingAndEncryption means
|
|
47
|
+
* AirPlay 2.
|
|
48
|
+
*/
|
|
49
|
+
declare function getProtocolVersion(txt: Record<string, string>): 1 | 2;
|
|
50
|
+
/**
|
|
51
|
+
* Reads the pairing requirement from the feature and status flags of the
|
|
52
|
+
* mDNS TXT record. The order of precedence is HomeKit, PIN required,
|
|
53
|
+
* transient (system) pairing, legacy PIN, none.
|
|
54
|
+
*/
|
|
55
|
+
declare function getPairingRequirement(txt: Record<string, string>): PairingRequirement;
|
|
56
|
+
/** True when the `pw` TXT field or the password bit of the `sf` status flags is set. */
|
|
57
|
+
declare function isPasswordRequired(txt: Record<string, string>): boolean;
|
|
58
|
+
/**
|
|
59
|
+
* True when the device has the SupportsHangdogRemoteControl flag, which it
|
|
60
|
+
* needs to receive HID events. In practice only an Apple TV has it.
|
|
61
|
+
*/
|
|
62
|
+
declare function isRemoteControlSupported(txt: Record<string, string>): boolean;
|
|
63
|
+
//#endregion
|
|
64
|
+
//#region src/const.d.ts
|
|
65
|
+
/** Default bytes per sample per channel, for 16-bit PCM. */
|
|
66
|
+
declare const AUDIO_BYTES_PER_CHANNEL = 2;
|
|
67
|
+
/** Default number of audio channels, for stereo. */
|
|
68
|
+
declare const AUDIO_CHANNELS = 2;
|
|
69
|
+
/** Default sample rate in Hz, for CD quality. */
|
|
70
|
+
declare const AUDIO_SAMPLE_RATE = 44100;
|
|
71
|
+
//#endregion
|
|
72
|
+
//#region src/discovery.d.ts
|
|
73
|
+
declare const AIRPLAY_SERVICE = "_airplay._tcp.local";
|
|
74
|
+
/** An AirPlay service with the feature mask from its TXT record, when that parses. */
|
|
75
|
+
type AirPlayDiscoveryResult = DiscoveryResult & {
|
|
76
|
+
readonly features?: bigint;
|
|
77
|
+
};
|
|
78
|
+
/** Finds the AirPlay receivers on the local network. */
|
|
79
|
+
declare function discover(options?: DiscoverOptions): Promise<AirPlayDiscoveryResult[]>;
|
|
80
|
+
/** The connect target for a discovered receiver. */
|
|
81
|
+
declare function toTarget(result: DiscoveryResult): AirPlayTarget;
|
|
82
|
+
/** Adds the parsed feature mask of the `features` (or `ft`) TXT value. */
|
|
83
|
+
declare function withFeatures(result: DiscoveryResult): AirPlayDiscoveryResult;
|
|
84
|
+
//#endregion
|
|
85
|
+
//#region src/features.d.ts
|
|
86
|
+
/**
|
|
87
|
+
* Parses the `features` TXT value of an mDNS record: one hex value, or two comma-separated 32-bit
|
|
88
|
+
* hex values (low,high) that form a 64-bit mask.
|
|
89
|
+
*
|
|
90
|
+
* @throws If the value has neither form.
|
|
91
|
+
*/
|
|
92
|
+
declare function parseFeatures(features: string): bigint;
|
|
93
|
+
//#endregion
|
|
94
|
+
//#region src/identifiers.d.ts
|
|
95
|
+
/**
|
|
96
|
+
* Generates a random DACP Active-Remote identifier for the RTSP headers: a
|
|
97
|
+
* 32-bit unsigned integer as a decimal string.
|
|
98
|
+
*/
|
|
99
|
+
declare function generateActiveRemoteId(): string;
|
|
100
|
+
/** Generates a random DACP-ID: a 64-bit integer as an uppercase hex string. */
|
|
101
|
+
declare function generateDacpId(): string;
|
|
102
|
+
/** Generates a random RTSP session identifier: a 32-bit unsigned integer as a decimal string. */
|
|
103
|
+
declare function generateSessionId(): string;
|
|
104
|
+
//#endregion
|
|
105
|
+
export { AIRPLAY_SERVICE, AUDIO_BYTES_PER_CHANNEL, AUDIO_CHANNELS, AUDIO_SAMPLE_RATE, type AirPlayDiscoveryResult, type AirPlayFeatureFlagName, AirPlayFeatureFlags, AirPlaySession, type AirPlayTarget, AudioPlayback, type AudioSource, type AudioStreamStats, type ConnectOptions, DataChannel, type DataChannelEventMap, EventChannel, type EventChannelEventMap, type PairOptions, type PairingRequirement, PinPairing, type PlayQueueCommand, type PlayQueueState, type PlayUrlOptions, type PlaybackInfo, PtpMaster, type RemoteControlChannels, type RemoteControlOptions, type SessionCloseReason, type StreamAudioOptions, TimingServer, UrlPlayback, connect, describeFlags, discover, generateActiveRemoteId, generateDacpId, generateSessionId, getPairingRequirement, getProtocolVersion, hasFeatureFlag, isPasswordRequired, isRemoteControlSupported, pair, parseFeatures, toTarget, withFeatures };
|