wrplayer 1.0.0 → 1.1.0

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 CHANGED
@@ -2,6 +2,9 @@
2
2
 
3
3
  WRPlayer 是一个统一的 Web 视频播放器 SDK,提供一致的 API 来播放多种流媒体协议(WebRTC、HLS、DASH、MP4 等),并可在原生 HTML、Next.js/React 与 Vue 3 环境中复用同一套核心代码。
4
4
 
5
+ > **团队集成文档**(npm 安装、Vue/React 示例、PTZ/对讲、生产部署):见 [docs/INTEGRATION.md](./docs/INTEGRATION.md)
6
+ > **npm 包**:https://www.npmjs.com/package/wrplayer
7
+
5
8
  ## 特性
6
9
 
7
10
  - **多协议支持**: WebRTC、HLS、DASH、MP4 等
@@ -0,0 +1,303 @@
1
+ import { reactive, ref, watch, computed, nextTick } from 'vue';
2
+ import WRPlayer from '../../core/WRPlayer.js';
3
+ import { useWRPlayer } from './useWRPlayer.js';
4
+
5
+ /**
6
+ * 开箱即用的监控播放组件:内置加载态、错误提示、重连与基础工具栏
7
+ */
8
+ export const WRMonitorView = {
9
+ name: 'WRMonitorView',
10
+ props: {
11
+ /** 播放地址,传入后自动开始连接 */
12
+ url: {
13
+ type: String,
14
+ default: ''
15
+ },
16
+ /** 流类型,默认 webrtc */
17
+ type: {
18
+ type: String,
19
+ default: 'webrtc'
20
+ },
21
+ /** 顶部标题 */
22
+ title: {
23
+ type: String,
24
+ default: '实时监控'
25
+ },
26
+ /** 是否显示底部工具栏(停止 / 重连 / 静音) */
27
+ showToolbar: {
28
+ type: Boolean,
29
+ default: true
30
+ },
31
+ /** 透传给 WRPlayer 的额外配置(ptz、voiceIntercom 等) */
32
+ playerOptions: {
33
+ type: Object,
34
+ default: () => ({})
35
+ }
36
+ },
37
+ emits: ['ready', 'error', 'stop', 'retry'],
38
+ setup(props, { emit, expose }) {
39
+ const isMuted = ref(true);
40
+ const playerConfig = reactive({
41
+ url: '',
42
+ type: props.type,
43
+ autoplay: true,
44
+ recvAudio: false,
45
+ recvVideo: true,
46
+ debug: false
47
+ });
48
+
49
+ const applyPlayerOptions = () => {
50
+ const { url, type, autoplay, recvAudio, recvVideo, debug, ...rest } = props.playerOptions || {};
51
+ Object.assign(playerConfig, {
52
+ type: type ?? props.type,
53
+ autoplay: autoplay ?? true,
54
+ recvAudio: recvAudio ?? false,
55
+ recvVideo: recvVideo ?? true,
56
+ debug: debug ?? false,
57
+ ...rest
58
+ });
59
+ };
60
+
61
+ const syncUrl = (value) => {
62
+ playerConfig.url = value || '';
63
+ };
64
+
65
+ applyPlayerOptions();
66
+ syncUrl(props.url);
67
+
68
+ watch(
69
+ () => props.url,
70
+ (value) => syncUrl(value)
71
+ );
72
+
73
+ watch(
74
+ () => props.type,
75
+ (value) => {
76
+ playerConfig.type = value;
77
+ }
78
+ );
79
+
80
+ watch(
81
+ () => props.playerOptions,
82
+ () => applyPlayerOptions(),
83
+ { deep: true }
84
+ );
85
+
86
+ const {
87
+ elementRef,
88
+ player,
89
+ isReady,
90
+ error,
91
+ stop: stopPlayer,
92
+ ptzAvailable,
93
+ voiceIntercomAvailable,
94
+ ptz,
95
+ voiceIntercom
96
+ } = useWRPlayer(playerConfig);
97
+
98
+ const status = computed(() => {
99
+ if (error.value) return 'error';
100
+ if (isReady.value) return 'playing';
101
+ if (playerConfig.url) return 'loading';
102
+ return 'idle';
103
+ });
104
+
105
+ const statusText = computed(() => {
106
+ switch (status.value) {
107
+ case 'loading': return '正在连接视频流…';
108
+ case 'playing': return '视频流接收正常';
109
+ case 'error': return '视频流连接失败';
110
+ default: return '等待播放地址';
111
+ }
112
+ });
113
+
114
+ watch(isReady, (ready) => {
115
+ if (ready) emit('ready', player.value);
116
+ });
117
+
118
+ watch(error, (err) => {
119
+ if (err) emit('error', err);
120
+ });
121
+
122
+ const handleStop = () => {
123
+ syncUrl('');
124
+ stopPlayer();
125
+ emit('stop');
126
+ };
127
+
128
+ const handleRetry = async () => {
129
+ const current = props.url;
130
+ if (!current) return;
131
+ syncUrl('');
132
+ emit('retry');
133
+ await nextTick();
134
+ syncUrl(current);
135
+ };
136
+
137
+ const toggleMute = () => {
138
+ isMuted.value = !isMuted.value;
139
+ if (elementRef.value) {
140
+ elementRef.value.muted = isMuted.value;
141
+ }
142
+ };
143
+
144
+ expose({
145
+ player,
146
+ ptz,
147
+ voiceIntercom,
148
+ ptzAvailable,
149
+ voiceIntercomAvailable,
150
+ stop: handleStop,
151
+ retry: handleRetry,
152
+ status
153
+ });
154
+
155
+ return {
156
+ elementRef,
157
+ isMuted,
158
+ status,
159
+ statusText,
160
+ error,
161
+ handleStop,
162
+ handleRetry,
163
+ toggleMute
164
+ };
165
+ },
166
+ template: `
167
+ <div class="wr-monitor">
168
+ <div class="wr-monitor__header" v-if="title">
169
+ <span class="wr-monitor__title">{{ title }}</span>
170
+ <span class="wr-monitor__badge" :class="'wr-monitor__badge--' + status">
171
+ {{ statusText }}
172
+ </span>
173
+ </div>
174
+
175
+ <div class="wr-monitor__viewport">
176
+ <video
177
+ ref="elementRef"
178
+ class="wr-monitor__video"
179
+ autoplay
180
+ playsinline
181
+ :muted="isMuted"
182
+ />
183
+
184
+ <div v-if="status === 'loading'" class="wr-monitor__overlay">
185
+ <div class="wr-monitor__spinner"></div>
186
+ <p>正在连接…</p>
187
+ </div>
188
+
189
+ <div v-else-if="status === 'error'" class="wr-monitor__overlay wr-monitor__overlay--error">
190
+ <p>无法连接视频流</p>
191
+ <button type="button" class="wr-monitor__btn" @click="handleRetry">重新连接</button>
192
+ </div>
193
+
194
+ <div v-else-if="status === 'idle'" class="wr-monitor__overlay">
195
+ <p>请传入 url 属性开始播放</p>
196
+ </div>
197
+ </div>
198
+
199
+ <div v-if="showToolbar" class="wr-monitor__toolbar">
200
+ <button type="button" class="wr-monitor__btn" @click="toggleMute">
201
+ {{ isMuted ? '取消静音' : '静音' }}
202
+ </button>
203
+ <button type="button" class="wr-monitor__btn" @click="handleRetry" :disabled="!status || status === 'idle'">
204
+ 重连
205
+ </button>
206
+ <button type="button" class="wr-monitor__btn wr-monitor__btn--danger" @click="handleStop" :disabled="status === 'idle'">
207
+ 停止
208
+ </button>
209
+ </div>
210
+ </div>
211
+ `,
212
+ styles: `
213
+ .wr-monitor {
214
+ font-family: system-ui, -apple-system, sans-serif;
215
+ background: #0f1419;
216
+ border-radius: 8px;
217
+ overflow: hidden;
218
+ color: #e6edf3;
219
+ }
220
+ .wr-monitor__header {
221
+ display: flex;
222
+ align-items: center;
223
+ justify-content: space-between;
224
+ padding: 10px 14px;
225
+ background: #161b22;
226
+ border-bottom: 1px solid #30363d;
227
+ }
228
+ .wr-monitor__title { font-size: 14px; font-weight: 600; }
229
+ .wr-monitor__badge {
230
+ font-size: 12px;
231
+ padding: 2px 8px;
232
+ border-radius: 999px;
233
+ background: #21262d;
234
+ color: #8b949e;
235
+ }
236
+ .wr-monitor__badge--playing { background: #238636; color: #fff; }
237
+ .wr-monitor__badge--loading { background: #9e6a03; color: #fff; }
238
+ .wr-monitor__badge--error { background: #da3633; color: #fff; }
239
+ .wr-monitor__viewport {
240
+ position: relative;
241
+ aspect-ratio: 16 / 9;
242
+ background: #010409;
243
+ }
244
+ .wr-monitor__video {
245
+ width: 100%;
246
+ height: 100%;
247
+ object-fit: contain;
248
+ display: block;
249
+ background: #000;
250
+ }
251
+ .wr-monitor__overlay {
252
+ position: absolute;
253
+ inset: 0;
254
+ display: flex;
255
+ flex-direction: column;
256
+ align-items: center;
257
+ justify-content: center;
258
+ gap: 12px;
259
+ background: rgba(1, 4, 9, 0.72);
260
+ color: #8b949e;
261
+ font-size: 14px;
262
+ }
263
+ .wr-monitor__overlay--error { color: #f85149; }
264
+ .wr-monitor__spinner {
265
+ width: 32px;
266
+ height: 32px;
267
+ border: 3px solid #30363d;
268
+ border-top-color: #58a6ff;
269
+ border-radius: 50%;
270
+ animation: wr-monitor-spin 0.8s linear infinite;
271
+ }
272
+ @keyframes wr-monitor-spin { to { transform: rotate(360deg); } }
273
+ .wr-monitor__toolbar {
274
+ display: flex;
275
+ gap: 8px;
276
+ padding: 10px 14px;
277
+ background: #161b22;
278
+ border-top: 1px solid #30363d;
279
+ }
280
+ .wr-monitor__btn {
281
+ padding: 6px 12px;
282
+ font-size: 13px;
283
+ border: 1px solid #30363d;
284
+ border-radius: 6px;
285
+ background: #21262d;
286
+ color: #e6edf3;
287
+ cursor: pointer;
288
+ }
289
+ .wr-monitor__btn:hover:not(:disabled) { background: #30363d; }
290
+ .wr-monitor__btn:disabled { opacity: 0.45; cursor: not-allowed; }
291
+ .wr-monitor__btn--danger { border-color: #da3633; color: #ff7b72; }
292
+ `
293
+ };
294
+
295
+ // 注入组件样式(仅一次)
296
+ if (typeof document !== 'undefined' && !document.getElementById('wr-monitor-view-styles')) {
297
+ const style = document.createElement('style');
298
+ style.id = 'wr-monitor-view-styles';
299
+ style.textContent = WRMonitorView.styles;
300
+ document.head.appendChild(style);
301
+ }
302
+
303
+ export default WRMonitorView;
@@ -1,5 +1,10 @@
1
- import { ref, onMounted, onUnmounted, watch, computed } from 'vue';
1
+ import { watch } from 'vue';
2
2
  import WRPlayer from '../../core/WRPlayer.js';
3
+ import { useWRPlayer } from './useWRPlayer.js';
4
+ import { WRMonitorView } from './WRMonitorView.js';
5
+
6
+ export { useWRPlayer } from './useWRPlayer.js';
7
+ export { WRMonitorView } from './WRMonitorView.js';
3
8
 
4
9
  const PTZ_EVENTS = [
5
10
  'ptz:move', 'ptz:zoom', 'ptz:focus', 'ptz:iris', 'ptz:speed:change',
@@ -14,150 +19,6 @@ const VOICE_INTERCOM_EVENTS = [
14
19
  'voiceIntercom:localStream', 'voiceIntercom:remoteStream'
15
20
  ];
16
21
 
17
- /**
18
- * Vue 3 Composition API for WRPlayer with PTZ and Voice Intercom support
19
- * @param {Object} config - Player configuration
20
- * @returns {Object} Player instance and utilities
21
- */
22
- export function useWRPlayer(config) {
23
- const elementRef = ref(null);
24
- const player = ref(null);
25
- const isReady = ref(false);
26
- const error = ref(null);
27
- const ptzAvailable = ref(false);
28
- const voiceIntercomAvailable = ref(false);
29
- const voiceIntercomStatus = ref(null);
30
-
31
- const initPlayer = () => {
32
- if (!elementRef.value || !config.url) return;
33
-
34
- try {
35
- const wrPlayer = new WRPlayer({
36
- element: elementRef.value,
37
- ...config
38
- });
39
-
40
- player.value = wrPlayer;
41
- ptzAvailable.value = !!wrPlayer.ptz;
42
- voiceIntercomAvailable.value = !!wrPlayer.voiceIntercom;
43
-
44
- wrPlayer.on('ready', () => {
45
- isReady.value = true;
46
- error.value = null;
47
- });
48
-
49
- wrPlayer.on('error', (errorData) => {
50
- error.value = errorData;
51
- isReady.value = false;
52
- });
53
-
54
- if (wrPlayer.voiceIntercom) {
55
- wrPlayer.on('voiceIntercom:statusChange', (data) => {
56
- voiceIntercomStatus.value = data;
57
- });
58
- }
59
-
60
- } catch (err) {
61
- error.value = { type: 'init_error', details: err };
62
- }
63
- };
64
-
65
- const destroyPlayer = () => {
66
- if (player.value) {
67
- player.value.stop();
68
- player.value = null;
69
- }
70
- isReady.value = false;
71
- error.value = null;
72
- ptzAvailable.value = false;
73
- voiceIntercomAvailable.value = false;
74
- voiceIntercomStatus.value = null;
75
- };
76
-
77
- const play = () => {
78
- if (player.value) {
79
- player.value.play();
80
- }
81
- };
82
-
83
- const pause = () => {
84
- if (player.value) {
85
- player.value.pause();
86
- }
87
- };
88
-
89
- const stop = () => {
90
- if (player.value) {
91
- player.value.stop();
92
- }
93
- };
94
-
95
- const ptz = computed(() => {
96
- if (!ptzAvailable.value || !player.value?.ptz) return null;
97
-
98
- return {
99
- move: (direction, speed) => player.value.ptz.move(direction, speed),
100
- zoom: (direction, speed) => player.value.ptz.zoom(direction, speed),
101
- focus: (direction, speed) => player.value.ptz.focus(direction, speed),
102
- iris: (direction, speed) => player.value.ptz.iris(direction, speed),
103
- stop: () => player.value.ptz.stop(),
104
- setSpeed: (speed) => player.value.ptz.setSpeed(speed),
105
- getSpeed: () => player.value.ptz.getSpeed(),
106
- preset: player.value.ptz.preset,
107
- cruise: player.value.ptz.cruise,
108
- scan: player.value.ptz.scan
109
- };
110
- });
111
-
112
- const voiceIntercom = computed(() => {
113
- if (!voiceIntercomAvailable.value || !player.value?.voiceIntercom) return null;
114
-
115
- return {
116
- start: (deviceId, channelId, mode) =>
117
- player.value.voiceIntercom.startIntercom(deviceId, channelId, mode),
118
- stop: () => player.value.voiceIntercom.stopIntercom(),
119
- setMode: (mode) => player.value.voiceIntercom.setMode(mode),
120
- getStatus: () => player.value.voiceIntercom.getStatus(),
121
- status: voiceIntercomStatus.value
122
- };
123
- });
124
-
125
- watch(
126
- () => [config.url, config.type],
127
- () => {
128
- destroyPlayer();
129
- if (config.url) {
130
- initPlayer();
131
- }
132
- }
133
- );
134
-
135
- onMounted(() => {
136
- if (config.url) {
137
- initPlayer();
138
- }
139
- });
140
-
141
- onUnmounted(() => {
142
- destroyPlayer();
143
- });
144
-
145
- return {
146
- elementRef,
147
- player: computed(() => player.value),
148
- isReady: computed(() => isReady.value),
149
- error: computed(() => error.value),
150
- ptzAvailable: computed(() => ptzAvailable.value),
151
- voiceIntercomAvailable: computed(() => voiceIntercomAvailable.value),
152
- voiceIntercomStatus: computed(() => voiceIntercomStatus.value),
153
- play,
154
- pause,
155
- stop,
156
- ptz,
157
- voiceIntercom
158
- };
159
- }
160
-
161
22
  /**
162
23
  * Vue 3 Component for WRPlayer with PTZ and Voice Intercom support
163
24
  */
@@ -375,4 +236,4 @@ export const WRPlayerVue2 = {
375
236
  `
376
237
  };
377
238
 
378
- export default { useWRPlayer, WRPlayerComponent, WRPlayerVue2 };
239
+ export default { useWRPlayer, WRPlayerComponent, WRPlayerVue2, WRMonitorView };
@@ -0,0 +1,148 @@
1
+ import { ref, onMounted, onUnmounted, watch, computed } from 'vue';
2
+ import WRPlayer from '../../core/WRPlayer.js';
3
+
4
+ /**
5
+ * Vue 3 Composition API for WRPlayer with PTZ and Voice Intercom support
6
+ * @param {Object} config - Player configuration
7
+ * @returns {Object} Player instance and utilities
8
+ */
9
+ export function useWRPlayer(config) {
10
+ const elementRef = ref(null);
11
+ const player = ref(null);
12
+ const isReady = ref(false);
13
+ const error = ref(null);
14
+ const ptzAvailable = ref(false);
15
+ const voiceIntercomAvailable = ref(false);
16
+ const voiceIntercomStatus = ref(null);
17
+
18
+ const initPlayer = () => {
19
+ if (!elementRef.value || !config.url) return;
20
+
21
+ try {
22
+ const wrPlayer = new WRPlayer({
23
+ element: elementRef.value,
24
+ ...config
25
+ });
26
+
27
+ player.value = wrPlayer;
28
+ ptzAvailable.value = !!wrPlayer.ptz;
29
+ voiceIntercomAvailable.value = !!wrPlayer.voiceIntercom;
30
+
31
+ wrPlayer.on('ready', () => {
32
+ isReady.value = true;
33
+ error.value = null;
34
+ });
35
+
36
+ wrPlayer.on('error', (errorData) => {
37
+ error.value = errorData;
38
+ isReady.value = false;
39
+ });
40
+
41
+ if (wrPlayer.voiceIntercom) {
42
+ wrPlayer.on('voiceIntercom:statusChange', (data) => {
43
+ voiceIntercomStatus.value = data;
44
+ });
45
+ }
46
+
47
+ } catch (err) {
48
+ error.value = { type: 'init_error', details: err };
49
+ }
50
+ };
51
+
52
+ const destroyPlayer = () => {
53
+ if (player.value) {
54
+ player.value.stop();
55
+ player.value = null;
56
+ }
57
+ isReady.value = false;
58
+ error.value = null;
59
+ ptzAvailable.value = false;
60
+ voiceIntercomAvailable.value = false;
61
+ voiceIntercomStatus.value = null;
62
+ };
63
+
64
+ const play = () => {
65
+ if (player.value) {
66
+ player.value.play();
67
+ }
68
+ };
69
+
70
+ const pause = () => {
71
+ if (player.value) {
72
+ player.value.pause();
73
+ }
74
+ };
75
+
76
+ const stop = () => {
77
+ if (player.value) {
78
+ player.value.stop();
79
+ }
80
+ };
81
+
82
+ const ptz = computed(() => {
83
+ if (!ptzAvailable.value || !player.value?.ptz) return null;
84
+
85
+ return {
86
+ move: (direction, speed) => player.value.ptz.move(direction, speed),
87
+ zoom: (direction, speed) => player.value.ptz.zoom(direction, speed),
88
+ focus: (direction, speed) => player.value.ptz.focus(direction, speed),
89
+ iris: (direction, speed) => player.value.ptz.iris(direction, speed),
90
+ stop: () => player.value.ptz.stop(),
91
+ setSpeed: (speed) => player.value.ptz.setSpeed(speed),
92
+ getSpeed: () => player.value.ptz.getSpeed(),
93
+ preset: player.value.ptz.preset,
94
+ cruise: player.value.ptz.cruise,
95
+ scan: player.value.ptz.scan
96
+ };
97
+ });
98
+
99
+ const voiceIntercom = computed(() => {
100
+ if (!voiceIntercomAvailable.value || !player.value?.voiceIntercom) return null;
101
+
102
+ return {
103
+ start: (deviceId, channelId, mode) =>
104
+ player.value.voiceIntercom.startIntercom(deviceId, channelId, mode),
105
+ stop: () => player.value.voiceIntercom.stopIntercom(),
106
+ setMode: (mode) => player.value.voiceIntercom.setMode(mode),
107
+ getStatus: () => player.value.voiceIntercom.getStatus(),
108
+ status: voiceIntercomStatus.value
109
+ };
110
+ });
111
+
112
+ watch(
113
+ () => [config.url, config.type],
114
+ () => {
115
+ destroyPlayer();
116
+ if (config.url) {
117
+ initPlayer();
118
+ }
119
+ }
120
+ );
121
+
122
+ onMounted(() => {
123
+ if (config.url) {
124
+ initPlayer();
125
+ }
126
+ });
127
+
128
+ onUnmounted(() => {
129
+ destroyPlayer();
130
+ });
131
+
132
+ return {
133
+ elementRef,
134
+ player: computed(() => player.value),
135
+ isReady: computed(() => isReady.value),
136
+ error: computed(() => error.value),
137
+ ptzAvailable: computed(() => ptzAvailable.value),
138
+ voiceIntercomAvailable: computed(() => voiceIntercomAvailable.value),
139
+ voiceIntercomStatus: computed(() => voiceIntercomStatus.value),
140
+ play,
141
+ pause,
142
+ stop,
143
+ ptz,
144
+ voiceIntercom
145
+ };
146
+ }
147
+
148
+ export default useWRPlayer;
@@ -0,0 +1,70 @@
1
+ /**
2
+ * WVP / ZLM WebRTC 流地址拼接工具
3
+ */
4
+
5
+ /**
6
+ * 拼接 WVP 设备+通道 stream 标识
7
+ * @param {string} deviceId
8
+ * @param {string} channelId
9
+ * @returns {string}
10
+ */
11
+ export function buildWvpStreamKey(deviceId, channelId) {
12
+ return `${deviceId}_${channelId}`;
13
+ }
14
+
15
+ /**
16
+ * 拼接 WebRTC 播放地址(WVP / ZLM 标准格式)
17
+ * @param {Object} options
18
+ * @param {string} options.host - 媒体服务器地址,如 222.222.25.198:6080
19
+ * @param {string} options.stream - 流 ID
20
+ * @param {string} [options.app='rtp'] - app 名称
21
+ * @param {boolean} [options.useSsl=false] - 是否 https
22
+ * @returns {string}
23
+ */
24
+ export function buildWebRtcUrl({ host, stream, app = 'rtp', useSsl = false }) {
25
+ if (!host || !stream) {
26
+ throw new Error('buildWebRtcUrl: host and stream are required');
27
+ }
28
+
29
+ const base = host.replace(/^https?:\/\//, '').replace(/\/$/, '');
30
+ const protocol = useSsl ? 'https' : 'http';
31
+ const params = new URLSearchParams({ app, stream });
32
+
33
+ return `${protocol}://${base}/index/api/webrtc?${params.toString()}`;
34
+ }
35
+
36
+ /**
37
+ * 根据国标 deviceId + channelId 拼接 WebRTC 地址
38
+ * @param {Object} options
39
+ * @param {string} options.host
40
+ * @param {string} options.deviceId
41
+ * @param {string} options.channelId
42
+ * @param {string} [options.app='rtp']
43
+ * @param {boolean} [options.useSsl=false]
44
+ * @returns {string}
45
+ */
46
+ export function buildWebRtcUrlFromDevice({ host, deviceId, channelId, app = 'rtp', useSsl = false }) {
47
+ return buildWebRtcUrl({
48
+ host,
49
+ app,
50
+ useSsl,
51
+ stream: buildWvpStreamKey(deviceId, channelId)
52
+ });
53
+ }
54
+
55
+ /**
56
+ * 清理流地址(去除首尾空格、引号)
57
+ * @param {string} url
58
+ * @returns {string}
59
+ */
60
+ export function normalizeStreamUrl(url) {
61
+ if (!url || typeof url !== 'string') return '';
62
+ return url.trim().replace(/^["']|["']$/g, '');
63
+ }
64
+
65
+ export default {
66
+ buildWvpStreamKey,
67
+ buildWebRtcUrl,
68
+ buildWebRtcUrlFromDevice,
69
+ normalizeStreamUrl
70
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "wrplayer",
3
- "version": "1.0.0",
3
+ "version": "1.1.0",
4
4
  "description": "A unified web video player SDK supporting WebRTC, HLS, DASH and other formats with PTZ camera control",
5
5
  "main": "dist/wrplayer.umd.js",
6
6
  "module": "dist/wrplayer.esm.js",
@@ -49,6 +49,7 @@
49
49
  },
50
50
  "./core": "./core/WRPlayer.js",
51
51
  "./core/ptz": "./core/PTZController.js",
52
+ "./core/streamUrl": "./core/streamUrl.js",
52
53
  "./adapters/react": "./adapters/react/index.js",
53
54
  "./adapters/vue": "./adapters/vue/index.js",
54
55
  "./adapters/vanilla": "./adapters/vanilla/index.js"