You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

Safari使用getUserMedia和MediaRecorder时预览黑屏(Chrome正常)

Safari使用getUserMedia和MediaRecorder时预览黑屏(Chrome正常)

我太懂这种挫败感了——Chrome上跑起来顺风顺水,到了Safari(不管是macOS还是iOS)就给你个黑屏,控制台还没报错,连MediaStream都显示是正常的,简直摸不着头脑!结合你已经尝试的各种方法,我来分享几个Safari特有的坑和解决思路:

1. 明确指定视频轨道约束,避免Safari默认适配问题

Safari对getUserMedia的约束兼容性比Chrome严格,有时候只传video: true会导致轨道虽然激活,但没有正确输出画面。你可以试试给视频轨道加上更具体的约束,比如指定分辨率或前置摄像头:

const stream = await navigator.mediaDevices.getUserMedia({
  video: {
    width: { ideal: 640 },
    height: { ideal: 360 },
    facingMode: 'user' // 明确指定前置摄像头,避免Safari选到非活跃设备
  },
  audio: true
});

2. 调整React中Video元素的属性赋值时机

你现在是在checkPermissions里直接操作videoRef.current,但React的ref挂载时机可能和你赋值的时机有冲突,尤其是在状态切换后渲染video元素的场景。可以把stream的绑定逻辑放到useEffect里,依赖streamRef.currentrecordingState

useEffect(() => {
  const video = videoRef.current;
  const stream = streamRef.current;
  if (!video || !stream || recordingState === 'initial') return;

  // 先重置video状态
  video.srcObject = null;
  video.setAttribute('playsinline', 'true');
  video.setAttribute('webkit-playsinline', 'true');
  video.autoplay = true;
  video.muted = true;
  video.srcObject = stream;

  // 用requestAnimationFrame替代setTimeout,更贴合Safari的渲染周期
  video.onloadedmetadata = () => {
    requestAnimationFrame(() => {
      video.play().catch(err => console.error('Play failed:', err));
    });
  };

  // 清理函数
  return () => {
    video.srcObject = null;
  };
}, [streamRef.current, recordingState]);

这样能确保video元素完全挂载后再绑定stream,避免React渲染时序问题导致的黑屏。

3. 检查视频轨道的readyState状态

有时候Safari返回的MediaStream虽然存在,但视频轨道的readyState可能还没到live状态就被绑定到video上了。你可以监听轨道的onactive事件,确认轨道活跃后再触发play:

const videoTrack = stream.getVideoTracks()[0];
videoTrack.onactive = () => {
  if (videoRef.current) {
    videoRef.current.play().catch(err => console.error('Play on track active failed:', err));
  }
};

4. 避免重复设置Video属性

你在JSX里已经写了<video ref={videoRef} playsInline muted />,又在代码里用setAttribute设置一遍,可能导致Safari的属性解析冲突。建议把所有必要属性直接写在JSX里,保持统一:

<video 
  ref={videoRef} 
  playsInline 
  webkit-playsinline 
  autoplay 
  muted 
  style={{ width: '640px', height: '360px', transform: 'translate3d(0,0,0)' }}
/>

这样就不用在代码里动态设置这些属性了,减少兼容性问题。

5. 排查MediaRecorder的影响(如果录制后也黑屏)

如果有时候录制后的视频也黑屏,可能是Safari的MediaRecorder对轨道的处理有延迟。建议在停止录制时,先确保MediaRecorder完全停止后再处理轨道:

const stopRecording = async () => {
  if (mediaRecorderRef.current) {
    mediaRecorderRef.current.stop();
    // 等待ondataavailable事件触发完成
    await new Promise(resolve => {
      mediaRecorderRef.current!.ondataavailable = resolve;
    });
    // 再停止轨道
    streamRef.current?.getTracks().forEach(track => track.stop());
  }
};

总的来说,Safari的媒体API兼容性细节比Chrome多很多,尤其是在渲染时序和约束处理上。你可以先从明确视频约束和调整React的stream绑定时机入手,这两个是最常见的解决思路。

备注:内容来源于stack exchange,提问作者James Williams

火山引擎 最新活动