移动端H5视频和音频的一些坑

830 阅读3分钟

浏览器环境:微信浏览器!!!只兼容了微信浏览器,safari等其他的浏览器不知道,没测试!!。

一、视频播放问题

需求:当用户进入页面时,视频当作背景,自动循环播放。

1. 视频全屏显示

<video
    webkit-playsinline="true"
    id="activeVideo"
    playsinline="true"
    x5-video-player-type="h5-page"
    class="video example_video"
    :controls="false"
    :muted="true"
    :autoplay="true"
    loop
    x5-video-player-fullscreen="false"
    src=""
/>
//视频资源通过接口动态获取
const res: any = await API.getVirtualAvatar(payload);
let restVideo = document.querySelector("#restVideo") as any;
let activeVideo = document.querySelector("#activeVideo") as any;
restVideo.src = res.data.data.rest_url;
activeVideo.src = res.data.data.interactive_url;
//隐藏视频控件按钮
//全屏按钮
video.example_video::-webkit-media-controls-fullscreen-button {
    display: none;
}
//播放按钮
video.example_video::-webkit-media-controls-play-button {
    display: none;
}
//进度条
video.example_video::-webkit-media-controls-timeline {
    display: none;
}
//观看的当前时间
video.example_video::-webkit-media-controls-current-time-display {
    display: none;
}
//剩余时间
video.example_video::-webkit-media-controls-time-remaining-display {
    display: none;
}
//音量按钮
video.example_video::-webkit-media-controls-mute-button {
    display: none;
}
video.example_video::-webkit-media-controls-toggle-closed-captions-button {
    display: none;
}
//音量的控制条
video.example_video::-webkit-media-controls-volume-slider {
    display: none;
}
//所有控件
video.example_video::-webkit-media-controls-enclosure {
    display: none;
}

PS:在电脑端上的谷歌浏览器上看,已经完美的满足了需求,但是从移动端打开发现,视频压根不会自动播放。

1692606994947.jpg 1692607053380.png

原因:为节约用户流量,移动端不允许视频自动播放,一定需要和用户交互后才能调用视频播放api。 解决办法:既然如此,那我只能加一个弹窗引导用户点击,然后播放视频了。。。

2. 解决移动端视频不能自动播放的问题

  • IOS端可以通过 微信内置浏览器私有接口WeixinJSBridge 去实现 已进入页面视频自动播放的操作。
if ((window as any).WeixinJSBridge) {
    WeixinJSBridge.invoke("getNetworkType", {}, function (e) {
        document.querySelector(".video").play()
    });     
 } 
  • 安卓端用WeixinJSBridge无效,一定要加一个弹窗和用户交互后才能播放视频。

1692607764917.jpg

当你满意的交付需求后,测试又提bug了。。。。。

1692607912361.png1692607912361.png1692607912361.png1692607912361.png1692607912361.png1692607912361.png

二、IOS端:将页面切换到后台后再回来视频就不动了

ps:幸好安卓没有这个问题哈哈

//监听页面显示隐藏
 window.addEventListener("visibilitychange", () => {
     if(ios){
         if ((window as any).WeixinJSBridge) {
            WeixinJSBridge.invoke("getNetworkType", {}, function (e) {
                document.querySelector(".video").play()
            });     
         } 
     }
 });

1692608316328.jpg1692608316328.jpg1692608316328.jpg

二、音频——当页面切换到后台再切回前台后,音频播放无效问题

上源代码

let audioContext=ref(null)
const play = () => {
// api: 获取音频流
 const ttsResult = await API.textToVoice(params).data.data;
 ttsBuffer.value += window.atob(ttsResult);
 if (!audioContext.value) {
        audioContext.value = new AudioContext({
            sampleRate: 10000
        });
    }
    let sourceCache = new Set();
    let audioData = ttsBuffer.value.slice(ttsBufferOffset.value);
    ttsBufferOffset.value += audioData.length;
    if (audioData.length > 0) {
        const outputS16 = base64ToS16(audioData);
        const output = transS16ToF32(outputS16);
        const audioBuffer = audioContext.value.createBuffer(
            1,
            output.length,
            8000
        );
        audioBuffer.copyToChannel(new Float32Array(output), 0, 0);
        bufferSource.value = audioContext.value.createBufferSource();
        bufferSource.value.buffer = audioBuffer;
        bufferSource.value.connect(audioContext.value.destination);
        console.log("开始播放");
        bufferSource.value.start();
        sourceCache.add(bufferSource.value); // Tips:缓存住 source,防止被GC掉,GC掉的话音频会中断
        $eventBus.emit("switchVideo", true);
        bufferSource.value.onended = () => {
            console.log("结束播放-onended");
            isVoiceing.value = false;
            $eventBus.emit("switchVideo", false);
            sourceCache.delete(bufferSource.value); // Tips:播放完之后,再清掉source缓存
        };
    } else {
        isVoiceing.value = true;
        $eventBus.emit("switchVideo", true);
    }
};
function base64ToS16(base64AudioData) {
    const outputArray = new Uint8Array(base64AudioData.length);
    for (let i = 0; i < base64AudioData.length; ++i) {
        outputArray[i] = base64AudioData.charCodeAt(i);
    }
    return new Int16Array(new DataView(outputArray.buffer).buffer);
}

function transS16ToF32(input) {
    let tmpData = [];
    for (let i = 0; i < input.length; i++) {
        let d = input[i] < 0 ? input[i] / 0x8000 : input[i] / 0x7fff;
        tmpData.push(d);
    }
    return new Float32Array(tmpData);
}

问题描述:当页面切换到后台后,然后进入其他的app,再回来切回来后,点击音频播放按钮paly(); 音频不播放了。 解决方案:在页面隐藏后,将audioContext指向null

const initVisibilityEvent = () => {
    window.addEventListener("visibilitychange", () => {
        if (document.visibilityState !== "visible") {
            console.log("页面隐藏");
            audioContext.value = null;
        }
    });
};
onMounted(() => {
    initVisibilityEvent();
});
  1. 移动设备的音频自动暂停:当页面切换到后台或用户切换到其他应用时,大多数移动浏览器会自动暂停所有音频播放以节省资源和电量。
  2. AudioContext 的状态:Web Audio API 的 AudioContext 在被暂停后不会自动恢复。当页面重新变为可见时,AudioContext 仍然处于 suspended 状态。
  3. 直接调用 play() 无效:在这种状态下直接调用 play() 方法可能不会生效,因为音频上下文没有被正确恢复。
const initVisibilityEvent = () => {
    window.addEventListener("visibilitychange", async () => {
        if (document.visibilityState !== "visible") {
            console.log("页面隐藏");
            // 不需要设为null,只需暂停
            if (audioContext.value) {
                await audioContext.value.suspend();
            }
        } else {
            console.log("页面可见");
            // 页面重新可见时恢复AudioContext
            if (audioContext.value) {
                try {
                    await audioContext.value.resume();
                } catch (e) {
                    console.error("恢复AudioContext失败:", e);
                    // 如果恢复失败,可以创建新的
                    audioContext.value = new (window.AudioContext || window.webkitAudioContext)();
                }
            }
        }
    });
};