You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

iOS Safari中Google Nest Cam Battery的WebRTC直播流无法加载问题

问题

我需要获取新款Google Nest Cam Battery的直播流,采用设备支持的WebRTC协议实现。该方案在安卓设备及PC端均可正常运行,但在iOS Safari中无法加载直播流。尝试过使用RTCPeerConnection、Simplepeer,也引入了adapter JS,但均无效果。

排查时在代码中添加alert后,能弹出alert“6”,但无法弹出“7”,说明在执行const data = await response.json();时解析响应JSON出现问题。

使用的核心代码如下:

<video id="webCamVideo" controls autoplay playsinline loop width="80%" /><br>

<script src="webrtc.github.io/adapter/adapter-latest.js"></script>
<script type="text/javascript">
        const configuration = {
            iceServers: [
                {
                    urls: [
                        'stun:stun1.l.google.com:19302',
                        'stun:stun2.l.google.com:19302',
                    ],
                },
            ],
            iceCandidatePoolSize: 10,
        };

        let peerConnection = null;
        let remoteStream = null;

        async function startFeed() {
            peerConnection = new window.RTCPeerConnection(configuration);
            const token = '<?php echo($_COOKIE["acto"]); ?>';
            const projectid = "<?php echo($project_id); ?>";
            const deviceid = "<?php echo($deviceid); ?>";

            remoteStream = new MediaStream();
            const videoElement = document.querySelector('#webCamVideo');
            if (videoElement) {
                videoElement.srcObject = remoteStream;
            }

            peerConnection.ontrack = event => {
                event.streams[0].getTracks().forEach(track => {
                    remoteStream.addTrack(track);
                });
            };

            if (peerConnection.createDataChannel) {
                peerConnection.createDataChannel("dataSendChannel");
            }

            try {
                const offer = await peerConnection.createOffer({
                    offerToReceiveAudio: true,
                    offerToReceiveVideo: true
                });
                await peerConnection.setLocalDescription(offer);

                // 尝试修正Safari的offer参数问题
                let offerSdp = offer.sdp;
                offerSdp = offerSdp.replace('a=sendrecv', 'a=recvonly');

                const url = "https://smartdevicemanagement.googleapis.com/v1/enterprises/" + projectid + "/devices/" + deviceid + ":executeCommand";
                const options = {
                    method: 'POST',
                    headers: {
                        'Content-Type': 'application/json',
                        'Authorization': 'Bearer ' + token
                    },
                    body: JSON.stringify({
                        "command": "sdm.devices.commands.CameraLiveStream.GenerateWebRtcStream",
                        "params": {
                            "offerSdp": offerSdp
                        }
                    })
                };
                const response = await fetch(url, options);

                // 此处解析JSON失败
                const data = await response.json();
                let answerSdp = data.results.answerSdp;

                const answer = new window.RTCSessionDescription({ type: 'answer', sdp: answerSdp });
                await peerConnection.setRemoteDescription(answer);
            } catch (error) {
                console.error("Error starting feed:", error);
            }
        }

        window.addEventListener('load', startFeed);
</script>

解决方案

针对iOS Safari中JSON解析失败的问题,按以下步骤排查修复:

1. 确认响应内容是否为合法JSON

在获取response后,先打印响应状态和原始文本,排查是否返回了错误页面而非预期JSON:

const response = await fetch(url, options);
// 添加以下调试代码
console.log('响应状态码:', response.status);
const responseText = await response.text();
console.log('响应原始文本:', responseText);
// 再尝试解析JSON
const data = JSON.parse(responseText);

如果打印出的是HTML错误页面(比如401未授权、403禁止访问),说明请求本身失败,而非JSON解析问题。

2. 手动处理HTTP错误状态

fetch仅在网络故障时才会抛出异常,HTTP错误(如4xx/5xx)不会自动reject,需手动判断:

const response = await fetch(url, options);
if (!response.ok) {
  const errorText = await response.text();
  throw new Error(`请求失败,状态码: ${response.status},响应内容: ${errorText}`);
}
const data = await response.json();

这样错误会被catch块捕获,能直接看到具体失败原因。

3. 修正Safari的SDP替换逻辑

当前仅替换了第一个a=sendrecv,但Safari生成的SDP中可能存在多个该字段,需全局替换:

// 把原替换代码改为全局正则替换
offerSdp = offerSdp.replace(/a=sendrecv/g, 'a=recvonly');

确保所有媒体流方向都被设置为recvonly,避免Google API返回错误响应。

4. 检查iOS Safari的Token有效性

iOS Safari的Cookie策略更严格,可能导致$_COOKIE["acto"]获取到的Token无效或为空。在代码中打印Token值确认:

const token = '<?php echo($_COOKIE["acto"]); ?>';
console.log('当前Token:', token);

如果Token为空或无效,需调整Cookie的跨域/存储策略,确保iOS Safari能正确获取到授权Token。

5. 适配Safari的Data Channel创建时机

部分版本的Safari对createDataChannel的调用时机敏感,建议在setLocalDescription成功后再创建数据通道:

await peerConnection.setLocalDescription(offer);
// 移到此处创建数据通道
if (peerConnection.createDataChannel) {
    peerConnection.createDataChannel("dataSendChannel");
}

内容的提问来源于stack exchange,提问作者Eren TURKOGLU

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.09 06:41:32