@flyfish-dev/rtsp-player 0.1.23

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.
@@ -0,0 +1,21 @@
1
+ <!doctype html>
2
+ <html lang="zh-CN">
3
+ <head>
4
+ <meta charset="utf-8" />
5
+ <title>RTSP SDK Example</title>
6
+ <style>
7
+ body { margin: 24px; background: #202020; color: #fff; font-family: system-ui, sans-serif; }
8
+ rtsp-player { display: block; width: min(960px, 100%); height: 540px; }
9
+ </style>
10
+ </head>
11
+ <body>
12
+ <rtsp-player
13
+ url="rtsp://9627b0bf2a7b.entrypoint.cloud.wowza.com:1935/app-p5260J38/66abe4b9_stream1"
14
+ transport="auto"
15
+ codec="auto"
16
+ autoplay
17
+ controls></rtsp-player>
18
+
19
+ <script src="../dist/rtsp-player.global.js" data-extension-id="YOUR_CHROME_EXTENSION_ID"></script>
20
+ </body>
21
+ </html>
package/package.json ADDED
@@ -0,0 +1,48 @@
1
+ {
2
+ "name": "@flyfish-dev/rtsp-player",
3
+ "version": "0.1.23",
4
+ "description": "RTSP web component, React component, and Vue component for the RTSP Chrome runtime.",
5
+ "type": "module",
6
+ "sideEffects": false,
7
+ "files": [
8
+ "dist",
9
+ "src",
10
+ "README.md",
11
+ "examples"
12
+ ],
13
+ "exports": {
14
+ ".": {
15
+ "types": "./dist/index.d.ts",
16
+ "import": "./dist/rtsp-player.esm.js"
17
+ },
18
+ "./web": {
19
+ "types": "./dist/index.d.ts",
20
+ "import": "./dist/rtsp-player.esm.js"
21
+ },
22
+ "./react": {
23
+ "types": "./dist/react.d.ts",
24
+ "import": "./dist/react.js"
25
+ },
26
+ "./vue": {
27
+ "types": "./dist/vue.d.ts",
28
+ "import": "./dist/vue.js"
29
+ },
30
+ "./global": "./dist/rtsp-player.global.js"
31
+ },
32
+ "peerDependencies": {
33
+ "react": ">=18",
34
+ "vue": ">=3"
35
+ },
36
+ "peerDependenciesMeta": {
37
+ "react": {
38
+ "optional": true
39
+ },
40
+ "vue": {
41
+ "optional": true
42
+ }
43
+ },
44
+ "scripts": {
45
+ "build": "node ../../scripts/build-sdk.mjs"
46
+ },
47
+ "license": "MIT"
48
+ }
package/src/global.js ADDED
@@ -0,0 +1,246 @@
1
+ (function () {
2
+ const DEFAULT_TAG_NAME = "rtsp-player";
3
+ const DEFAULT_WIDTH = "640px";
4
+ const DEFAULT_HEIGHT = "360px";
5
+ const frameOwners = new WeakMap();
6
+ const config = {
7
+ extensionId: "",
8
+ tagName: DEFAULT_TAG_NAME,
9
+ runtime: "extension",
10
+ };
11
+
12
+ function currentScriptExtensionId() {
13
+ const script = document.currentScript;
14
+ return script && script.dataset ? script.dataset.extensionId || "" : "";
15
+ }
16
+
17
+ function normalizeExtensionId(value) {
18
+ return String(value || "").trim().replace(/^chrome-extension:\/\//, "").replace(/\/.*$/, "");
19
+ }
20
+
21
+ function extensionOrigin(extensionId) {
22
+ const id = normalizeExtensionId(extensionId);
23
+ return id ? `chrome-extension://${id}` : "";
24
+ }
25
+
26
+ function toCssSize(value, fallback) {
27
+ if (value === undefined || value === null || value === "") return fallback;
28
+ const text = String(value);
29
+ return /^\d+$/.test(text) ? `${text}px` : text;
30
+ }
31
+
32
+ function setBooleanAttribute(el, name, enabled) {
33
+ if (enabled === undefined) return;
34
+ if (enabled) el.setAttribute(name, "");
35
+ else el.removeAttribute(name);
36
+ }
37
+
38
+ function configureRTSP(options) {
39
+ options = options || {};
40
+ if (options.extensionId !== undefined) config.extensionId = normalizeExtensionId(options.extensionId);
41
+ if (options.tagName) config.tagName = String(options.tagName);
42
+ if (options.runtime) config.runtime = String(options.runtime);
43
+ return Object.assign({}, config);
44
+ }
45
+
46
+ function updateRTSPPlayer(el, options) {
47
+ options = options || {};
48
+ const map = {
49
+ url: options.url,
50
+ src: options.src,
51
+ width: options.width,
52
+ height: options.height,
53
+ transport: options.transport,
54
+ runtime: options.runtime,
55
+ codec: options.codec,
56
+ "media-transport": options.mediaTransport,
57
+ "rtsp-transport": options.rtspTransport,
58
+ "extension-id": options.extensionId,
59
+ };
60
+ Object.keys(map).forEach((key) => {
61
+ const value = map[key];
62
+ if (value !== undefined && value !== null && value !== "") el.setAttribute(key, String(value));
63
+ });
64
+ setBooleanAttribute(el, "autoplay", options.autoplay);
65
+ setBooleanAttribute(el, "controls", options.controls);
66
+ setBooleanAttribute(el, "muted", options.muted);
67
+ return el;
68
+ }
69
+
70
+ function defineRTSPPlayer(tagName, options) {
71
+ configureRTSP(options);
72
+ const name = String(tagName || config.tagName || DEFAULT_TAG_NAME);
73
+ const existing = customElements.get(name);
74
+ if (existing) return existing;
75
+
76
+ class RTSPPlayerElement extends HTMLElement {
77
+ static get observedAttributes() {
78
+ return ["url", "src", "width", "height", "autoplay", "controls", "muted", "transport", "media-transport", "rtsp-transport", "codec", "runtime", "extension-id"];
79
+ }
80
+
81
+ constructor() {
82
+ super();
83
+ this._iframe = null;
84
+ this._loaded = false;
85
+ this.attachShadow({ mode: "open" });
86
+ }
87
+
88
+ connectedCallback() {
89
+ this._render();
90
+ }
91
+
92
+ disconnectedCallback() {
93
+ if (this._iframe && this._iframe.contentWindow) frameOwners.delete(this._iframe.contentWindow);
94
+ }
95
+
96
+ attributeChangedCallback() {
97
+ this._resize();
98
+ this._sendInit();
99
+ }
100
+
101
+ play(url) {
102
+ if (url) this.setAttribute("url", url);
103
+ this._sendInit();
104
+ }
105
+
106
+ stop() {
107
+ if (this._iframe && this._iframe.contentWindow) {
108
+ this._iframe.contentWindow.postMessage({ type: "RTSP_PLAYER_STOP" }, this._origin());
109
+ }
110
+ }
111
+
112
+ _extensionId() {
113
+ return normalizeExtensionId(
114
+ this.getAttribute("extension-id") ||
115
+ config.extensionId ||
116
+ window.RTSP_EXTENSION_ID ||
117
+ window.RTSP_WEB_PLAYER_EXTENSION_ID ||
118
+ currentScriptExtensionId(),
119
+ );
120
+ }
121
+
122
+ _origin() {
123
+ return extensionOrigin(this._extensionId());
124
+ }
125
+
126
+ _render() {
127
+ if (this._iframe || !this.shadowRoot) return;
128
+ this.shadowRoot.innerHTML = `
129
+ <style>
130
+ :host{display:inline-block;background:#050505;min-width:160px;min-height:90px;contain:content;}
131
+ .rtsp-host{width:100%;height:100%;background:#050505;position:relative;overflow:hidden;border-radius:6px;}
132
+ iframe{width:100%;height:100%;border:0;background:#050505;display:block;}
133
+ .missing{height:100%;min-height:120px;display:flex;align-items:center;justify-content:center;background:#151515;color:#ddd;font:12px system-ui,sans-serif;text-align:center;padding:12px;box-sizing:border-box;}
134
+ .gateway-guide{height:100%;min-height:170px;display:grid;align-content:center;gap:10px;padding:18px;background:#111312;color:#e9ede8;font:13px system-ui,sans-serif;box-sizing:border-box;}
135
+ .gateway-guide strong{color:#f4f5f2;font-size:15px;}
136
+ .gateway-guide span{color:#b6bbb4;line-height:1.5;}
137
+ .gateway-guide a{display:inline-flex;width:max-content;max-width:100%;align-items:center;min-height:34px;padding:0 10px;border:1px solid #435048;border-radius:6px;background:#223423;color:#ecfff0;text-decoration:none;}
138
+ </style>
139
+ <div class="rtsp-host"></div>
140
+ `;
141
+ this._resize();
142
+ const host = this.shadowRoot.querySelector(".rtsp-host");
143
+ const origin = this._origin();
144
+ if (!origin) {
145
+ host.innerHTML = gatewayGuideHTML(
146
+ "需要安装 RTSP Gateway",
147
+ "普通网页不能直接访问 rtsp://。请先安装 RTSP Gateway 与 Chrome 扩展,或在 Electron/Tauri 中使用 desktop runtime bridge。",
148
+ );
149
+ return;
150
+ }
151
+
152
+ const iframe = document.createElement("iframe");
153
+ iframe.src = `${origin}/player/player.html`;
154
+ iframe.allow = "autoplay; fullscreen";
155
+ iframe.referrerPolicy = "no-referrer";
156
+ iframe.addEventListener("load", () => {
157
+ this._loaded = true;
158
+ frameOwners.set(iframe.contentWindow, this);
159
+ this._sendInit();
160
+ });
161
+ host.appendChild(iframe);
162
+ this._iframe = iframe;
163
+ }
164
+
165
+ _resize() {
166
+ this.style.width = toCssSize(this.getAttribute("width") || this.style.width, DEFAULT_WIDTH);
167
+ this.style.height = toCssSize(this.getAttribute("height") || this.style.height, DEFAULT_HEIGHT);
168
+ }
169
+
170
+ _sendInit() {
171
+ const origin = this._origin();
172
+ if (!this._loaded || !this._iframe || !this._iframe.contentWindow || !origin) return;
173
+ this._iframe.contentWindow.postMessage({
174
+ type: "RTSP_PLAYER_INIT",
175
+ url: this.getAttribute("url") || this.getAttribute("src") || "",
176
+ autoplay: this.hasAttribute("autoplay"),
177
+ controls: this.hasAttribute("controls"),
178
+ muted: this.hasAttribute("muted"),
179
+ transport: this.getAttribute("rtsp-transport") || (this.getAttribute("transport") === "tcp" ? "tcp" : "tcp"),
180
+ rtspTransport: this.getAttribute("rtsp-transport") || "tcp",
181
+ mediaTransport: this.getAttribute("media-transport") || this.getAttribute("transport") || "auto",
182
+ codec: this.getAttribute("codec") || "auto",
183
+ }, origin);
184
+ }
185
+ }
186
+
187
+ customElements.define(name, RTSPPlayerElement);
188
+ return RTSPPlayerElement;
189
+ }
190
+
191
+ function createRTSPPlayer(options) {
192
+ options = options || {};
193
+ const tagName = options.tagName || config.tagName || DEFAULT_TAG_NAME;
194
+ defineRTSPPlayer(tagName, options);
195
+ const el = document.createElement(tagName);
196
+ updateRTSPPlayer(el, options);
197
+ return el;
198
+ }
199
+
200
+ function gatewayGuideHTML(title, body) {
201
+ return `
202
+ <div class="gateway-guide">
203
+ <strong>${escapeHTML(title)}</strong>
204
+ <span>${escapeHTML(body)}</span>
205
+ <a href="https://rtsp.flyfish.dev/#docs/web-component-gateway.md" target="_blank" rel="noreferrer">查看 Gateway 安装指引</a>
206
+ </div>
207
+ `;
208
+ }
209
+
210
+ function escapeHTML(value) {
211
+ return String(value).replace(/[&<>"']/g, (ch) => ({
212
+ "&": "&amp;",
213
+ "<": "&lt;",
214
+ ">": "&gt;",
215
+ "\"": "&quot;",
216
+ "'": "&#39;",
217
+ }[ch]));
218
+ }
219
+
220
+ if (!window.__RTSP_PLAYER_MESSAGE_BRIDGE__) {
221
+ window.__RTSP_PLAYER_MESSAGE_BRIDGE__ = true;
222
+ window.addEventListener("message", (event) => {
223
+ const el = event.source ? frameOwners.get(event.source) : null;
224
+ if (!el || !event.data || !event.data.type || !event.data.type.startsWith("RTSP_PLAYER_")) return;
225
+ if (event.origin !== el._origin()) return;
226
+ const type = event.data.type.replace(/^RTSP_PLAYER_/, "").toLowerCase();
227
+ el.dispatchEvent(new CustomEvent(type, {
228
+ detail: event.data,
229
+ bubbles: true,
230
+ composed: true,
231
+ }));
232
+ });
233
+ }
234
+
235
+ const id = window.RTSP_EXTENSION_ID || window.RTSP_WEB_PLAYER_EXTENSION_ID || currentScriptExtensionId();
236
+ if (id) configureRTSP({ extensionId: id });
237
+ defineRTSPPlayer(DEFAULT_TAG_NAME);
238
+
239
+ window.RTSP = Object.assign(window.RTSP || {}, {
240
+ version: "0.1.23",
241
+ configure: configureRTSP,
242
+ definePlayer: defineRTSPPlayer,
243
+ createPlayer: createRTSPPlayer,
244
+ updatePlayer: updateRTSPPlayer,
245
+ });
246
+ })();
package/src/index.d.ts ADDED
@@ -0,0 +1,54 @@
1
+ export interface RTSPConfigureOptions {
2
+ extensionId?: string;
3
+ tagName?: string;
4
+ runtime?: RTSPRuntime;
5
+ }
6
+
7
+ export type RTSPRuntime = "extension" | "desktop" | "auto";
8
+ export type RTSPMediaTransport = "auto" | "webrtc" | "ws-annexb";
9
+ export type RTSPCodec = "auto" | "h264" | "h265";
10
+
11
+ export interface RTSPPlayerOptions extends RTSPConfigureOptions {
12
+ url?: string;
13
+ src?: string;
14
+ width?: string | number;
15
+ height?: string | number;
16
+ autoplay?: boolean;
17
+ controls?: boolean;
18
+ muted?: boolean;
19
+ transport?: RTSPMediaTransport | "tcp" | string;
20
+ rtspTransport?: "tcp" | "udp" | string;
21
+ mediaTransport?: RTSPMediaTransport | string;
22
+ codec?: RTSPCodec | string;
23
+ }
24
+
25
+ export declare const RTSP_PLAYER_VERSION: string;
26
+
27
+ export declare function configureRTSP(options?: RTSPConfigureOptions): {
28
+ extensionId: string;
29
+ tagName: string;
30
+ runtime: RTSPRuntime;
31
+ };
32
+
33
+ export declare function defineRTSPPlayer(
34
+ tagName?: string,
35
+ options?: RTSPConfigureOptions,
36
+ ): CustomElementConstructor | undefined;
37
+
38
+ export declare function probeRTSPCapabilities(codec?: RTSPCodec | string): Promise<{
39
+ desktopRuntime: boolean;
40
+ webcodecs: boolean;
41
+ webrtc: boolean;
42
+ h264WebRTC: boolean;
43
+ h265WebRTC: boolean;
44
+ h264WebCodecs: boolean;
45
+ h265WebCodecs: boolean;
46
+ requestedCodec: RTSPCodec;
47
+ }>;
48
+
49
+ export declare function createRTSPPlayer(options?: RTSPPlayerOptions): HTMLElement;
50
+
51
+ export declare function updateRTSPPlayer<T extends HTMLElement>(
52
+ element: T,
53
+ options?: RTSPPlayerOptions,
54
+ ): T;
package/src/react.d.ts ADDED
@@ -0,0 +1,29 @@
1
+ import type { CSSProperties, ForwardRefExoticComponent, RefAttributes } from "react";
2
+
3
+ export interface RtspPlayerProps {
4
+ extensionId?: string;
5
+ url?: string;
6
+ src?: string;
7
+ width?: string | number;
8
+ height?: string | number;
9
+ autoplay?: boolean;
10
+ controls?: boolean;
11
+ muted?: boolean;
12
+ transport?: "auto" | "webrtc" | "ws-annexb" | "tcp" | string;
13
+ rtspTransport?: "tcp" | "udp" | string;
14
+ mediaTransport?: "auto" | "webrtc" | "ws-annexb" | string;
15
+ codec?: "auto" | "h264" | "h265" | string;
16
+ runtime?: "extension" | "desktop" | "auto";
17
+ tagName?: string;
18
+ className?: string;
19
+ style?: CSSProperties;
20
+ onReady?: (event: CustomEvent) => void;
21
+ onError?: (event: CustomEvent) => void;
22
+ onStarting?: (event: CustomEvent) => void;
23
+ }
24
+
25
+ export declare const RtspPlayer: ForwardRefExoticComponent<
26
+ RtspPlayerProps & RefAttributes<HTMLElement>
27
+ >;
28
+
29
+ export default RtspPlayer;
package/src/react.js ADDED
@@ -0,0 +1,86 @@
1
+ import React, {
2
+ forwardRef,
3
+ useEffect,
4
+ useImperativeHandle,
5
+ useRef,
6
+ } from "react";
7
+ import { configureRTSP, defineRTSPPlayer, updateRTSPPlayer } from "./web.js";
8
+
9
+ export const RtspPlayer = forwardRef(function RtspPlayer(props, ref) {
10
+ const {
11
+ extensionId,
12
+ url,
13
+ src,
14
+ width = 640,
15
+ height = 360,
16
+ autoplay = false,
17
+ controls = true,
18
+ muted = false,
19
+ transport = "auto",
20
+ rtspTransport = "tcp",
21
+ mediaTransport,
22
+ codec = "auto",
23
+ runtime = "extension",
24
+ tagName = "rtsp-player",
25
+ className,
26
+ style,
27
+ onReady,
28
+ onError,
29
+ onStarting,
30
+ ...rest
31
+ } = props;
32
+ const innerRef = useRef(null);
33
+
34
+ useEffect(() => {
35
+ configureRTSP({ extensionId, tagName, runtime });
36
+ defineRTSPPlayer(tagName);
37
+ }, [extensionId, tagName, runtime]);
38
+
39
+ useImperativeHandle(ref, () => innerRef.current, []);
40
+
41
+ useEffect(() => {
42
+ if (!innerRef.current) return;
43
+ updateRTSPPlayer(innerRef.current, {
44
+ extensionId,
45
+ url,
46
+ src,
47
+ width,
48
+ height,
49
+ autoplay,
50
+ controls,
51
+ muted,
52
+ transport,
53
+ rtspTransport,
54
+ mediaTransport,
55
+ codec,
56
+ runtime,
57
+ });
58
+ }, [extensionId, url, src, width, height, autoplay, controls, muted, transport, rtspTransport, mediaTransport, codec, runtime]);
59
+
60
+ useEffect(() => {
61
+ const el = innerRef.current;
62
+ if (!el) return undefined;
63
+ const listeners = [
64
+ ["ready", onReady],
65
+ ["error", onError],
66
+ ["starting", onStarting],
67
+ ];
68
+ for (const [type, handler] of listeners) {
69
+ if (handler) el.addEventListener(type, handler);
70
+ }
71
+ return () => {
72
+ for (const [type, handler] of listeners) {
73
+ if (handler) el.removeEventListener(type, handler);
74
+ }
75
+ };
76
+ }, [onReady, onError, onStarting]);
77
+
78
+ return React.createElement(tagName, {
79
+ ...rest,
80
+ ref: innerRef,
81
+ className,
82
+ style,
83
+ });
84
+ });
85
+
86
+ export default RtspPlayer;
package/src/vue.d.ts ADDED
@@ -0,0 +1,22 @@
1
+ import type { DefineComponent } from "vue";
2
+
3
+ export interface RtspPlayerProps {
4
+ extensionId?: string;
5
+ url?: string;
6
+ src?: string;
7
+ width?: string | number;
8
+ height?: string | number;
9
+ autoplay?: boolean;
10
+ controls?: boolean;
11
+ muted?: boolean;
12
+ transport?: "auto" | "webrtc" | "ws-annexb" | "tcp" | string;
13
+ rtspTransport?: "tcp" | "udp" | string;
14
+ mediaTransport?: "auto" | "webrtc" | "ws-annexb" | string;
15
+ codec?: "auto" | "h264" | "h265" | string;
16
+ runtime?: "extension" | "desktop" | "auto";
17
+ tagName?: string;
18
+ }
19
+
20
+ export declare const RtspPlayer: DefineComponent<RtspPlayerProps>;
21
+
22
+ export default RtspPlayer;
package/src/vue.js ADDED
@@ -0,0 +1,66 @@
1
+ import {
2
+ defineComponent,
3
+ h,
4
+ onBeforeUnmount,
5
+ onMounted,
6
+ ref,
7
+ watch,
8
+ } from "vue";
9
+ import { configureRTSP, defineRTSPPlayer, updateRTSPPlayer } from "./web.js";
10
+
11
+ export const RtspPlayer = defineComponent({
12
+ name: "RtspPlayer",
13
+ props: {
14
+ extensionId: String,
15
+ url: String,
16
+ src: String,
17
+ width: { type: [String, Number], default: 640 },
18
+ height: { type: [String, Number], default: 360 },
19
+ autoplay: { type: Boolean, default: false },
20
+ controls: { type: Boolean, default: true },
21
+ muted: { type: Boolean, default: false },
22
+ transport: { type: String, default: "auto" },
23
+ rtspTransport: { type: String, default: "tcp" },
24
+ mediaTransport: String,
25
+ codec: { type: String, default: "auto" },
26
+ runtime: { type: String, default: "extension" },
27
+ tagName: { type: String, default: "rtsp-player" },
28
+ },
29
+ emits: ["ready", "error", "starting"],
30
+ setup(props, { emit, attrs }) {
31
+ const el = ref(null);
32
+
33
+ const sync = () => {
34
+ if (!el.value) return;
35
+ configureRTSP({ extensionId: props.extensionId, tagName: props.tagName, runtime: props.runtime });
36
+ defineRTSPPlayer(props.tagName);
37
+ updateRTSPPlayer(el.value, props);
38
+ };
39
+
40
+ const onReady = (event) => emit("ready", event);
41
+ const onError = (event) => emit("error", event);
42
+ const onStarting = (event) => emit("starting", event);
43
+
44
+ onMounted(() => {
45
+ sync();
46
+ el.value?.addEventListener("ready", onReady);
47
+ el.value?.addEventListener("error", onError);
48
+ el.value?.addEventListener("starting", onStarting);
49
+ });
50
+
51
+ onBeforeUnmount(() => {
52
+ el.value?.removeEventListener("ready", onReady);
53
+ el.value?.removeEventListener("error", onError);
54
+ el.value?.removeEventListener("starting", onStarting);
55
+ });
56
+
57
+ watch(() => ({ ...props }), sync, { deep: true });
58
+
59
+ return () => h(props.tagName, {
60
+ ...attrs,
61
+ ref: el,
62
+ });
63
+ },
64
+ });
65
+
66
+ export default RtspPlayer;