@ray-js/ray-ipc-player 2.1.1-beta.1 → 2.1.1-beta.3

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.
@@ -22,7 +22,8 @@ interface ErrorOverlayProps {
22
22
  hideFeedBackButton: boolean;
23
23
  isShare: boolean;
24
24
  limitFlow: boolean;
25
- autoPlay: boolean;
25
+ /** 是否开启预览拉流,与 Player 内 previewOn 一致(previewEnabled ?? true) */
26
+ previewOn: boolean;
26
27
  }
27
28
  declare const ErrorOverlay: React.FC<ErrorOverlayProps>;
28
29
  export default ErrorOverlay;
@@ -31,7 +31,7 @@ const ErrorOverlay = _ref => {
31
31
  hideFeedBackButton,
32
32
  isShare,
33
33
  limitFlow,
34
- autoPlay
34
+ previewOn
35
35
  } = _ref;
36
36
  /**
37
37
  * 获取错误展示内容
@@ -54,8 +54,8 @@ const ErrorOverlay = _ref => {
54
54
  return Strings.getLang('ipc_player_traffic_load_online_zero_tip');
55
55
  }
56
56
 
57
- // 未自动播放
58
- if (!autoPlay) {
57
+ // 未开启预览拉流(休眠态提示)
58
+ if (!previewOn) {
59
59
  return Strings.getLang('ipc_player_sleep_tip');
60
60
  }
61
61
  if (!isLowPowerDevice && onlineStatus && privateState) {
@@ -83,7 +83,7 @@ const ErrorOverlay = _ref => {
83
83
  return "".concat(Strings.getLang("ipc_player_error_common"), "(").concat(videoErrCode, ")");
84
84
  }
85
85
  return playStateErrorMsg;
86
- }, [phoneNetworkConnect, onlineStatus, privateState, isLowPowerDevice, lowPowerDeviceOnlineState, lowPrivateOutTimeFlag, device4GIsFreeze, deviceFreezeReason, limitFlow, autoPlay, videoErrCode, playStateErrorMsg]);
86
+ }, [phoneNetworkConnect, onlineStatus, privateState, isLowPowerDevice, lowPowerDeviceOnlineState, lowPrivateOutTimeFlag, device4GIsFreeze, deviceFreezeReason, limitFlow, previewOn, videoErrCode, playStateErrorMsg]);
87
87
 
88
88
  /**
89
89
  * 根据自定义类型获取错误码展示内容
@@ -142,7 +142,7 @@ const ErrorOverlay = _ref => {
142
142
  if (limitFlow) {
143
143
  return Strings.getLang('ipc_player_recharge_flow_btn');
144
144
  }
145
- if (!autoPlay) {
145
+ if (!previewOn) {
146
146
  return Strings.getLang('ipc_player_open_sleep_mode');
147
147
  }
148
148
  if (privateState) {
@@ -194,7 +194,7 @@ const ErrorOverlay = _ref => {
194
194
  default:
195
195
  return false;
196
196
  }
197
- }, [hideSuggestedText, privateState, limitFlow, phoneNetworkConnect, isLowPowerDevice, onlineStatus, lowPowerDeviceOnlineState, isShare, device4GIsFreeze, deviceFreezeReason, hideHelpButton, hideFeedBackButton]);
197
+ }, [hideSuggestedText, privateState, limitFlow, phoneNetworkConnect, isLowPowerDevice, onlineStatus, lowPowerDeviceOnlineState, isShare, device4GIsFreeze, deviceFreezeReason, hideHelpButton, hideFeedBackButton, previewOn]);
198
198
 
199
199
  /**
200
200
  * 跳转4G流量充值页面
@@ -248,11 +248,11 @@ const ErrorOverlay = _ref => {
248
248
  if (limitFlow && onlineStatus) {
249
249
  return 'err-icon-flow-limit';
250
250
  }
251
- if (!autoPlay) {
251
+ if (!previewOn) {
252
252
  return 'err-icon-sleep';
253
253
  }
254
254
  return 'err-icon';
255
- }, [autoPlay, limitFlow, onlineStatus]);
255
+ }, [previewOn, limitFlow, onlineStatus]);
256
256
  if (!isError) return null;
257
257
  return /*#__PURE__*/React.createElement(CoverView, {
258
258
  style: {
@@ -43,7 +43,8 @@ export const usePlayerProps = props => {
43
43
  hideHelpButton = false,
44
44
  hideFeedBackButton = false,
45
45
  playerRoute = '',
46
- defaultAutoPlay = true,
46
+ previewEnabled,
47
+ onPreviewEnabledChange,
47
48
  limitFlow = false,
48
49
  autoWakeUp = false
49
50
  } = props;
@@ -84,7 +85,8 @@ export const usePlayerProps = props => {
84
85
  hideHelpButton,
85
86
  hideFeedBackButton,
86
87
  playerRoute,
87
- defaultAutoPlay,
88
+ previewEnabled,
89
+ onPreviewEnabledChange,
88
90
  limitFlow,
89
91
  autoWakeUp,
90
92
  // 样式相关
package/lib/index.js CHANGED
@@ -54,7 +54,8 @@ const Player = props => {
54
54
  hideHelpButton,
55
55
  hideFeedBackButton,
56
56
  playerRoute,
57
- defaultAutoPlay,
57
+ previewEnabled,
58
+ onPreviewEnabledChange,
58
59
  limitFlow,
59
60
  autoWakeUp,
60
61
  borderRadius,
@@ -95,15 +96,16 @@ const Player = props => {
95
96
  const privateLowPowerDeviceTimeOutRef = useRef(null);
96
97
  const privateWakeOutTimeRef = useRef(null);
97
98
  const privateDeviceWakeNeedPublishTimeRef = useRef(null);
98
- // 内部维护首次组件渲染时,自动播放状态,一旦调用重试,触发拉流后,将其状态设为true
99
- const [autoPlay, setAutoPlay] = useState(defaultAutoPlay);
100
-
101
- // connectChangeRef
99
+ /** 传入 previewEnabled 为受控;未传时 previewOn 为 true(自动拉流) */
100
+ const isPreviewControlled = previewEnabled !== undefined;
101
+ const previewOn = previewEnabled !== null && previewEnabled !== void 0 ? previewEnabled : true;
102
102
  useRef(null);
103
103
  const firstPreviewTimeRef = useRef(false);
104
104
 
105
105
  // 标记是否进入内存缓存/后台(onPause、onHide 设为 true,onShow 还原为 false),拉流时若为 true 则拦截
106
106
  const isInBackgroundRef = useRef(false);
107
+ /** 网络/在线等早于 onShow 触发拉流时 isInBackgroundRef 仍为 true,记一次待回到前台后补拉 */
108
+ const pendingForegroundPullRef = useRef(false);
107
109
 
108
110
  // 自定义计时老低功耗方案设备在线状态, 用于短暂时间报上下线的UI展示兼容 待实现
109
111
  const [lowPowerDeviceOnlineState, setLowPowerDeviceOnlineState] = useState(onlineStatus);
@@ -172,8 +174,104 @@ const Player = props => {
172
174
  });
173
175
  }
174
176
  });
177
+
178
+ /**
179
+ * 实际发起拉流(隐私唤醒 + P2P/预览),与是否受控无关
180
+ */
181
+ const runPullStream = useMemoizedFn(() => {
182
+ phoneNetworkConnect && onlineStatus && privateState && (!isLowPowerDevice || isVirtualDevice) && handleReWakeCamera();
183
+ phoneNetworkConnect && onlineStatus && privateState && isLowPowerDevice && !isVirtualDevice && handleReWakeCameraLowPowerDevice();
184
+ phoneNetworkConnect && onlineStatus && !privateState && _retry();
185
+ });
186
+ const retry = useMemoizedFn(() => {
187
+ console.log('重新拉流启动');
188
+ // 任何重试拉流时, 将播放器状态还原为loading状态
189
+ setVideoErrCode('');
190
+ setPlayState(d => {
191
+ d.loadingState = true;
192
+ d.errorState = false;
193
+ d.errorMsg = '';
194
+ });
195
+ // 排除默认值为"",不等于播放器路由时, 不进行重试拉流
196
+
197
+ if (playerRoute !== '' && playerRoute !== getPlayerRoute()) {
198
+ console.log('不在播放器路由内, 不进行拉流');
199
+ return;
200
+ }
201
+ if (isInBackgroundRef.current) {
202
+ console.log('处于后台/内存缓存,拦截拉流');
203
+ ipcTTTOperatorLog('VID: isInBackground, block retry');
204
+ pendingForegroundPullRef.current = true;
205
+ return;
206
+ }
207
+ pendingForegroundPullRef.current = false;
208
+ if (isPreviewControlled) {
209
+ onPreviewEnabledChange === null || onPreviewEnabledChange === void 0 || onPreviewEnabledChange(true);
210
+ if (previewEnabled) {
211
+ runPullStream();
212
+ }
213
+ return;
214
+ }
215
+ runPullStream();
216
+ });
217
+
218
+ /** 回到前台后补拉:解决 onNetworkStatusChange 等早于 onShow 触发 retry 被拦的问题 */
219
+ const flushPendingForegroundPull = useMemoizedFn(() => {
220
+ if (!pendingForegroundPullRef.current) {
221
+ return;
222
+ }
223
+ if (!previewOn) {
224
+ pendingForegroundPullRef.current = false;
225
+ ipcTTTOperatorLog('VID: flush_pending_cleared_preview_off');
226
+ return;
227
+ }
228
+ if (!phoneNetworkConnect || !playState.initLy || !ipcCtx.current || !onlineStatus || privateState || limitFlow || !previewOn) {
229
+ ipcTTTOperatorLog('VID: flush_pending_skip_not_ready');
230
+ return;
231
+ }
232
+ pendingForegroundPullRef.current = false;
233
+ ipcTTTOperatorLog('VID: flush_pending_foreground_pull');
234
+ if (isPreviewControlled) {
235
+ runPullStream();
236
+ } else {
237
+ retry();
238
+ }
239
+ });
240
+
241
+ /**
242
+ * 命令式开关:受控走 onPreviewEnabledChange;未传 previewEnabled 时 previewOn 恒为 true,仅支持开启拉流(retry)
243
+ */
244
+ const setPreviewEnabledCommand = useMemoizedFn(enabled => {
245
+ if (isPreviewControlled) {
246
+ onPreviewEnabledChange === null || onPreviewEnabledChange === void 0 || onPreviewEnabledChange(enabled);
247
+ return;
248
+ }
249
+ if (enabled) {
250
+ retry();
251
+ }
252
+ });
253
+
254
+ // 创建ipc实例
255
+ const createIpcCtx = () => {
256
+ onCtx && onCtx({
257
+ ctx: ipcCtx.current,
258
+ retry,
259
+ setPreviewEnabled: setPreviewEnabledCommand
260
+ });
261
+ };
175
262
  usePageEvent('onShow', useMemoizedFn(async () => {
263
+ const wasBackground = isInBackgroundRef.current;
176
264
  isInBackgroundRef.current = false;
265
+ if (wasBackground && !ignoreHideStopPreview) {
266
+ setVideoErrCode('');
267
+ if (previewOn) {
268
+ setPlayState(d => {
269
+ d.loadingState = true;
270
+ d.errorState = false;
271
+ d.errorMsg = '';
272
+ });
273
+ }
274
+ }
177
275
  getNetworkType({
178
276
  success: res => {
179
277
  console.log('网络类型', res);
@@ -189,10 +287,11 @@ const Player = props => {
189
287
  return;
190
288
  }
191
289
  // 视图准备就绪
192
- if (phoneNetworkConnect && playState.initLy && ipcCtx.current && onlineStatus && !privateState && !limitFlow && autoPlay) {
290
+ if (phoneNetworkConnect && playState.initLy && ipcCtx.current && onlineStatus && !privateState && !limitFlow && previewOn) {
193
291
  ipcTTTOperatorLog('VID: onShow_event_to_retry');
194
292
  retry();
195
293
  }
294
+ flushPendingForegroundPull();
196
295
  }));
197
296
  usePageEvent('onResize', sizeData => {
198
297
  const {
@@ -209,23 +308,24 @@ const Player = props => {
209
308
  }
210
309
  });
211
310
 
212
- // 针对limitFlow
311
+ // 针对limitFlow / 未拉流:休眠态 UI
213
312
  useEffect(() => {
214
- if (limitFlow || !autoPlay) {
215
- setPlayState(d => {
216
- return _objectSpread(_objectSpread({}, d), {}, {
217
- loadingState: false,
218
- errorState: true,
219
- errorMessage: ''
220
- });
313
+ if (!(limitFlow || !previewOn)) {
314
+ return;
315
+ }
316
+ setPlayState(d => {
317
+ return _objectSpread(_objectSpread({}, d), {}, {
318
+ loadingState: false,
319
+ errorState: true,
320
+ errorMessage: ''
221
321
  });
222
- ipcTTTOperatorLog("limitFlow ~~~~ ".concat(limitFlow, " autoPlay ~~~ ").concat(autoPlay));
223
- if (playState.initLy && ipcCtx.current) {
224
- stopPreview();
225
- disconnect();
226
- }
322
+ });
323
+ ipcTTTOperatorLog("limitFlow ~~~~ ".concat(limitFlow, " previewOn ~~~ ").concat(previewOn));
324
+ if (playState.initLy && ipcCtx.current) {
325
+ stopPreview();
326
+ disconnect();
227
327
  }
228
- }, [limitFlow, autoPlay, playState.initLy, ipcCtx.current]);
328
+ }, [limitFlow, previewOn, playState.initLy, ipcCtx.current]);
229
329
  useEffect(() => {
230
330
  registerDeviceListListener({
231
331
  deviceIdList: [devId],
@@ -484,7 +584,7 @@ const Player = props => {
484
584
  d.errorState = true;
485
585
  d.errorMsg = limitFlow ? Strings.getLang('ipc_player_traffic_load_offline_limit_tip') : Strings.getLang('ipc_player_device_offline');
486
586
  });
487
- } else if (!autoPlay) {
587
+ } else if (!previewOn) {
488
588
  setPlayState(d => {
489
589
  d.errorState = true;
490
590
  d.errorMsg = '设备处于休眠中';
@@ -496,50 +596,24 @@ const Player = props => {
496
596
  });
497
597
  } else if (playState.initLy && !privateState) {
498
598
  ipcTTTOperatorLog("VID: privateState_status_change: ".concat(privateState));
499
- retry();
599
+ // 网络/在线等回调可能早于页面 onShow,此时 isInBackgroundRef 仍为 true,避免 retry 被误拦;等 onShow 再拉流
600
+ if (isInBackgroundRef.current) {
601
+ ipcTTTOperatorLog('VID: skip_pull_in_status_effect_while_background');
602
+ pendingForegroundPullRef.current = true;
603
+ return;
604
+ }
605
+ if (isPreviewControlled) {
606
+ runPullStream();
607
+ } else {
608
+ retry();
609
+ }
500
610
  } else if (playState.initLy && privateState) {
501
611
  stopPreview();
502
612
  // 如果隐私模式为true, 主动推一波流变化的状态码,适配ios不触发connectChange的情况
503
613
  onChangeStreamStatus && onChangeStreamStatus(-1001);
504
614
  }
505
615
  }
506
- }, [phoneNetworkConnect, onlineStatus, playState.initLy, privateState, limitFlow, autoPlay]);
507
- const retry = useMemoizedFn(() => {
508
- console.log('重新拉流启动');
509
- setAutoPlay(true);
510
- // 任何重试拉流时, 将播放器状态还原为loading状态
511
- setVideoErrCode('');
512
- setPlayState(d => {
513
- d.loadingState = true;
514
- d.errorState = false;
515
- d.errorMsg = '';
516
- });
517
- // 排除默认值为"",不等于播放器路由时, 不进行重试拉流
518
-
519
- if (playerRoute !== '' && playerRoute !== getPlayerRoute()) {
520
- console.log('不在播放器路由内, 不进行拉流');
521
- return;
522
- }
523
- if (isInBackgroundRef.current) {
524
- console.log('处于后台/内存缓存,拦截拉流');
525
- ipcTTTOperatorLog('VID: isInBackground, block retry');
526
- return;
527
- }
528
- phoneNetworkConnect && onlineStatus && privateState && (!isLowPowerDevice || isVirtualDevice) && handleReWakeCamera();
529
-
530
- // 低功耗设备隐私模式需单独处理,先进行下发唤醒, 唤醒后再进行下发DP
531
- phoneNetworkConnect && onlineStatus && privateState && isLowPowerDevice && !isVirtualDevice && handleReWakeCameraLowPowerDevice();
532
- phoneNetworkConnect && onlineStatus && !privateState && _retry();
533
- // }, [isLowPowerDevice, phoneNetworkConnect && playState.connectState, privateState, onlineStatus]);
534
- });
535
-
536
- // 创建ipc实例
537
- const createIpcCtx = () => {
538
- onCtx && onCtx({
539
- ctx: ipcCtx.current,
540
- retry
541
- });
542
- };
616
+ }, [phoneNetworkConnect, onlineStatus, playState.initLy, privateState, limitFlow, previewOn, isPreviewControlled, previewEnabled, runPullStream, retry]);
543
617
 
544
618
  // 视图层准备就绪,开始建立连接
545
619
 
@@ -717,7 +791,7 @@ const Player = props => {
717
791
  hideFeedBackButton: hideFeedBackButton,
718
792
  isShare: isShare,
719
793
  limitFlow: limitFlow,
720
- autoPlay: autoPlay
794
+ previewOn: previewOn
721
795
  }), /*#__PURE__*/React.createElement(FullScreenBackButton, {
722
796
  isVisible: screenType === 'landscape' && (playState.loadingState || playState.errorState),
723
797
  getClassName: getClassName,
@@ -98,10 +98,14 @@ export type IProps = {
98
98
  onCameraNotifyWeakNetwork?: (data) => void;
99
99
  /**
100
100
  * @description.en onCtx
101
- * @description.zh 获取ipcPlayer实例ctx以及重试方法retry
101
+ * @description.zh 获取 ipcPlayer 实例 ctxretry,以及 setPreviewEnabled(与 previewEnabled 受控配合或独立命令式开关拉流)
102
102
  * @default (playerRef) => void
103
103
  */
104
- onCtx?: (playerRef) => void;
104
+ onCtx?: (playerRef: {
105
+ ctx: any;
106
+ retry: () => void;
107
+ setPreviewEnabled: (enabled: boolean) => void;
108
+ }) => void;
105
109
  /**
106
110
  * @description.en onInitPreview
107
111
  * @description.zh 初始化预览成功通知
@@ -217,11 +221,17 @@ export type IProps = {
217
221
  */
218
222
  playerRoute?: string;
219
223
  /**
220
- * @description.en Autoplay by default
221
- * @description.zh 默认是否自动播放
222
- * @default true
224
+ * @description.en Whether to pull preview. If omitted, treated as true (previewOn = previewEnabled ?? true). If passed, controlled mode (isPreviewControlled = true); pair with onPreviewEnabledChange for retry/lifecycle.
225
+ * @description.zh 是否拉流;未传等价 true;传入则为受控(与 onPreviewEnabledChange 配合)。
226
+ * @default undefined
227
+ */
228
+ previewEnabled?: boolean;
229
+ /**
230
+ * @description.en Required for controlled previewEnabled when user retries or lifecycle syncs preview state
231
+ * @description.zh 传入 previewEnabled 受控时建议配置:重试开流、hide/pause 与父级状态同步依赖此回调
232
+ * @default undefined
223
233
  */
224
- defaultAutoPlay?: boolean;
234
+ onPreviewEnabledChange?: (enabled: boolean) => void;
225
235
  /**
226
236
  * @description.en Limit flow for the player
227
237
  * @description.zh 播放器是否限流
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ray-js/ray-ipc-player",
3
- "version": "2.1.1-beta.1",
3
+ "version": "2.1.1-beta.3",
4
4
  "description": "ray小程序播放器",
5
5
  "keywords": [
6
6
  "tuya-miniapp",