
本文详解如何在 Android WebView 中绕过 `mediaPlaybackRequiresUserGesture` 限制,通过 JavaScript 定期调用 `video.play()` 实现摄像头视频流的自动、连续播放,解决“视频仅闪现半秒即暂停”的典型问题。
在 Android WebView 中加载本地 HTML 文件(如 file:///android_asset/camTheme.html)并调用 navigator.getUserMedia 启动摄像头时,即使已正确配置 setMediaPlaybackRequiresUserGesture(false),视频仍常在初始帧后立即暂停——这是由于 Android WebView 对 file:// 协议下媒体自动播放的严格限制(尤其在 Android 9+ 及 Chromium 内核 WebView 中),该限制无法仅靠 Java 层设置完全解除。
✅ 根本原因解析
- setMediaPlaybackRequiresUserGesture(false) 对 file:// 协议下的媒体播放无效(官方文档明确说明其主要影响 http(s):// 资源);
- navigator.getUserMedia 返回的 MediaStream 赋予 <video> 后,首次 play() 成功,但后续因策略限制被静音/暂停;
- 用户触摸 WebView 触发的 play() 是唯一被允许的“合法手势”,故拖拽可恢复播放;
- autoplay="true" muted 仅缓解音频限制,不解除 file:// 下的视频持续播放禁令。
✅ 推荐解决方案:JavaScript 层主动保活
最稳定、兼容性最佳的方式是在 HTML 中使用 setInterval 高频调用 video.play(),强制维持播放状态:
<html allow="camera,microphone">
<head>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Camera Stream</title>
</head>
<body style="margin:0; overflow:hidden;">
<video id="videoElement" autoplay muted playsinline style="width:100vw; height:100vh;"></video>
<script>
const video = document.getElementById('videoElement');
// 兼容各浏览器的 getUserMedia
const getUserMedia = navigator.getUserMedia ||
navigator.webkitGetUserMedia ||
navigator.mozGetUserMedia ||
navigator.msGetUserMedia;
if (!getUserMedia) {
console.error('MediaStream API not supported');
document.body.innerHTML = '<h3>Camera not supported</h3>';
return;
}
// 请求摄像头(默认后置,可按需切换)
getUserMedia({
video: { facingMode: 'environment' },
audio: false
}, stream => {
video.srcObject = stream;
// 关键:每 15ms 尝试播放一次(平衡稳定性与性能)
const playLoop = setInterval(() => {
video.play().catch(e => {
// 忽略“Already playing”等非致命错误
if (e.name !== 'NotSupportedError' && e.name !== 'NotAllowedError') {
console.debug('Auto-play attempt:', e.name);
}
});
}, 15);
// 可选:页面隐藏时暂停以省电
document.addEventListener('visibilitychange', () => {
if (document.hidden) clearInterval(playLoop);
});
}, err => {
console.error('Camera access denied:', err);
document.body.innerHTML = `<h3>Permission denied: ${err.message}</h3>`;
});
</script>
</body>
</html>⚠️ 注意事项与优化建议
- 频率选择:15ms 是实测较优值(10ms 过于激进易触发异常,30ms 可能偶发卡顿);
- 必须添加 playsinline 属性:防止 iOS/iPadOS WebView 全屏播放中断流;
- 移除冗余权限:<uses-permission android:name="android.webkit.PermissionRequest" /> 是非法权限,会导致编译失败,应删除;
-
Java 层精简配置(关键项保留,避免冲突):
WebSettings settings = webView.getSettings(); settings.setJavaScriptEnabled(true); settings.setDomStorageEnabled(true); settings.setAllowFileAccess(true); settings.setAllowContentAccess(true); settings.setAllowFileAccessFromFileURLs(true); settings.setAllowUniversalAccessFromFileURLs(true); // ⚠️ 仅调试用,发布前禁用 settings.setMediaPlaybackRequiresUserGesture(false); // 仍需保留,对 http(s) 有效 webView.setWebChromeClient(new WebChromeClient() { @Override public void onPermissionRequest(PermissionRequest request) { // 仅对 file:// 授予 camera/mic 权限 if ("file:///".equals(request.getOrigin().toString())) { request.grant(new String[]{PermissionRequest.RESOURCE_VIDEO_CAPTURE, PermissionRequest.RESOURCE_AUDIO_CAPTURE}); } else { request.deny(); } } }); - 安全提醒:setAllowUniversalAccessFromFileURLs(true) 存在 XSS 风险,仅用于调试;生产环境应改用 AssetFileDescriptor 或本地 HTTP Server(如 NanoHTTPD)提供 HTML。
✅ 替代方案(进阶)
若需彻底规避 file:// 限制,推荐将 HTML 移至 http://localhost:8080(通过内嵌轻量 HTTP 服务提供),此时 setMediaPlaybackRequiresUserGesture(false) 生效,且无需 setInterval 保活,代码更简洁、性能更优。
综上,setInterval + video.play() 是当前兼容性最强、实施成本最低的解决方案。它不依赖系统版本,无需 NDK 或复杂权限适配,可快速落地于各类 Android WebView 摄像头项目中。










