| 
							
								 | 
							
							<!DOCTYPE html>
 | 
						
						
						
						
							 | 
							
								 | 
							
							<html lang="zh-CN">
 | 
						
						
						
						
							 | 
							
								 | 
							
							
 | 
						
						
						
						
							 | 
							
								 | 
							
							<head>
 | 
						
						
						
						
							 | 
							
								 | 
							
							    <meta charset="UTF-8">
 | 
						
						
						
						
							 | 
							
								 | 
							
							    <meta name="viewport" content="width=device-width, initial-scale=1.0">
 | 
						
						
						
						
							 | 
							
								 | 
							
							    <title>📷 摄像头权限测试</title>
 | 
						
						
						
						
							 | 
							
								 | 
							
							    <style>
 | 
						
						
						
						
							 | 
							
								 | 
							
							        * {
 | 
						
						
						
						
							 | 
							
								 | 
							
							            margin: 0;
 | 
						
						
						
						
							 | 
							
								 | 
							
							            padding: 0;
 | 
						
						
						
						
							 | 
							
								 | 
							
							            box-sizing: border-box;
 | 
						
						
						
						
							 | 
							
								 | 
							
							        }
 | 
						
						
						
						
							 | 
							
								 | 
							
							
 | 
						
						
						
						
							 | 
							
								 | 
							
							        body {
 | 
						
						
						
						
							 | 
							
								 | 
							
							            font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
 | 
						
						
						
						
							 | 
							
								 | 
							
							            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
 | 
						
						
						
						
							 | 
							
								 | 
							
							            color: white;
 | 
						
						
						
						
							 | 
							
								 | 
							
							            min-height: 100vh;
 | 
						
						
						
						
							 | 
							
								 | 
							
							            padding: 20px;
 | 
						
						
						
						
							 | 
							
								 | 
							
							        }
 | 
						
						
						
						
							 | 
							
								 | 
							
							
 | 
						
						
						
						
							 | 
							
								 | 
							
							        .container {
 | 
						
						
						
						
							 | 
							
								 | 
							
							            max-width: 800px;
 | 
						
						
						
						
							 | 
							
								 | 
							
							            margin: 0 auto;
 | 
						
						
						
						
							 | 
							
								 | 
							
							        }
 | 
						
						
						
						
							 | 
							
								 | 
							
							
 | 
						
						
						
						
							 | 
							
								 | 
							
							        .header {
 | 
						
						
						
						
							 | 
							
								 | 
							
							            text-align: center;
 | 
						
						
						
						
							 | 
							
								 | 
							
							            padding: 20px 0;
 | 
						
						
						
						
							 | 
							
								 | 
							
							            border-bottom: 2px solid rgba(255, 255, 255, 0.2);
 | 
						
						
						
						
							 | 
							
								 | 
							
							            margin-bottom: 30px;
 | 
						
						
						
						
							 | 
							
								 | 
							
							        }
 | 
						
						
						
						
							 | 
							
								 | 
							
							
 | 
						
						
						
						
							 | 
							
								 | 
							
							        .test-section {
 | 
						
						
						
						
							 | 
							
								 | 
							
							            background: rgba(0, 0, 0, 0.3);
 | 
						
						
						
						
							 | 
							
								 | 
							
							            border-radius: 15px;
 | 
						
						
						
						
							 | 
							
								 | 
							
							            padding: 25px;
 | 
						
						
						
						
							 | 
							
								 | 
							
							            margin-bottom: 20px;
 | 
						
						
						
						
							 | 
							
								 | 
							
							        }
 | 
						
						
						
						
							 | 
							
								 | 
							
							
 | 
						
						
						
						
							 | 
							
								 | 
							
							        .test-title {
 | 
						
						
						
						
							 | 
							
								 | 
							
							            font-size: 18px;
 | 
						
						
						
						
							 | 
							
								 | 
							
							            margin-bottom: 15px;
 | 
						
						
						
						
							 | 
							
								 | 
							
							            color: #4CAF50;
 | 
						
						
						
						
							 | 
							
								 | 
							
							        }
 | 
						
						
						
						
							 | 
							
								 | 
							
							
 | 
						
						
						
						
							 | 
							
								 | 
							
							        .test-result {
 | 
						
						
						
						
							 | 
							
								 | 
							
							            margin: 10px 0;
 | 
						
						
						
						
							 | 
							
								 | 
							
							            padding: 10px;
 | 
						
						
						
						
							 | 
							
								 | 
							
							            border-radius: 8px;
 | 
						
						
						
						
							 | 
							
								 | 
							
							            background: rgba(255, 255, 255, 0.1);
 | 
						
						
						
						
							 | 
							
								 | 
							
							        }
 | 
						
						
						
						
							 | 
							
								 | 
							
							
 | 
						
						
						
						
							 | 
							
								 | 
							
							        .btn {
 | 
						
						
						
						
							 | 
							
								 | 
							
							            padding: 12px 24px;
 | 
						
						
						
						
							 | 
							
								 | 
							
							            border: none;
 | 
						
						
						
						
							 | 
							
								 | 
							
							            border-radius: 8px;
 | 
						
						
						
						
							 | 
							
								 | 
							
							            font-size: 16px;
 | 
						
						
						
						
							 | 
							
								 | 
							
							            font-weight: bold;
 | 
						
						
						
						
							 | 
							
								 | 
							
							            cursor: pointer;
 | 
						
						
						
						
							 | 
							
								 | 
							
							            transition: all 0.3s ease;
 | 
						
						
						
						
							 | 
							
								 | 
							
							            margin: 5px;
 | 
						
						
						
						
							 | 
							
								 | 
							
							            background: #4CAF50;
 | 
						
						
						
						
							 | 
							
								 | 
							
							            color: white;
 | 
						
						
						
						
							 | 
							
								 | 
							
							        }
 | 
						
						
						
						
							 | 
							
								 | 
							
							
 | 
						
						
						
						
							 | 
							
								 | 
							
							        .btn:hover {
 | 
						
						
						
						
							 | 
							
								 | 
							
							            background: #45a049;
 | 
						
						
						
						
							 | 
							
								 | 
							
							        }
 | 
						
						
						
						
							 | 
							
								 | 
							
							
 | 
						
						
						
						
							 | 
							
								 | 
							
							        .btn:disabled {
 | 
						
						
						
						
							 | 
							
								 | 
							
							            background: #666;
 | 
						
						
						
						
							 | 
							
								 | 
							
							            cursor: not-allowed;
 | 
						
						
						
						
							 | 
							
								 | 
							
							        }
 | 
						
						
						
						
							 | 
							
								 | 
							
							
 | 
						
						
						
						
							 | 
							
								 | 
							
							        .video-container {
 | 
						
						
						
						
							 | 
							
								 | 
							
							            position: relative;
 | 
						
						
						
						
							 | 
							
								 | 
							
							            background: #000;
 | 
						
						
						
						
							 | 
							
								 | 
							
							            border-radius: 10px;
 | 
						
						
						
						
							 | 
							
								 | 
							
							            overflow: hidden;
 | 
						
						
						
						
							 | 
							
								 | 
							
							            margin: 20px 0;
 | 
						
						
						
						
							 | 
							
								 | 
							
							            max-height: 400px;
 | 
						
						
						
						
							 | 
							
								 | 
							
							        }
 | 
						
						
						
						
							 | 
							
								 | 
							
							
 | 
						
						
						
						
							 | 
							
								 | 
							
							        #testVideo {
 | 
						
						
						
						
							 | 
							
								 | 
							
							            width: 100%;
 | 
						
						
						
						
							 | 
							
								 | 
							
							            height: auto;
 | 
						
						
						
						
							 | 
							
								 | 
							
							            display: block;
 | 
						
						
						
						
							 | 
							
								 | 
							
							        }
 | 
						
						
						
						
							 | 
							
								 | 
							
							
 | 
						
						
						
						
							 | 
							
								 | 
							
							        .log-area {
 | 
						
						
						
						
							 | 
							
								 | 
							
							            background: rgba(0, 0, 0, 0.5);
 | 
						
						
						
						
							 | 
							
								 | 
							
							            border-radius: 10px;
 | 
						
						
						
						
							 | 
							
								 | 
							
							            padding: 15px;
 | 
						
						
						
						
							 | 
							
								 | 
							
							            height: 300px;
 | 
						
						
						
						
							 | 
							
								 | 
							
							            overflow-y: auto;
 | 
						
						
						
						
							 | 
							
								 | 
							
							            font-family: monospace;
 | 
						
						
						
						
							 | 
							
								 | 
							
							            font-size: 14px;
 | 
						
						
						
						
							 | 
							
								 | 
							
							            line-height: 1.6;
 | 
						
						
						
						
							 | 
							
								 | 
							
							        }
 | 
						
						
						
						
							 | 
							
								 | 
							
							
 | 
						
						
						
						
							 | 
							
								 | 
							
							        .log-entry {
 | 
						
						
						
						
							 | 
							
								 | 
							
							            margin-bottom: 5px;
 | 
						
						
						
						
							 | 
							
								 | 
							
							        }
 | 
						
						
						
						
							 | 
							
								 | 
							
							
 | 
						
						
						
						
							 | 
							
								 | 
							
							        .log-success {
 | 
						
						
						
						
							 | 
							
								 | 
							
							            color: #4CAF50;
 | 
						
						
						
						
							 | 
							
								 | 
							
							        }
 | 
						
						
						
						
							 | 
							
								 | 
							
							
 | 
						
						
						
						
							 | 
							
								 | 
							
							        .log-error {
 | 
						
						
						
						
							 | 
							
								 | 
							
							            color: #f44336;
 | 
						
						
						
						
							 | 
							
								 | 
							
							        }
 | 
						
						
						
						
							 | 
							
								 | 
							
							
 | 
						
						
						
						
							 | 
							
								 | 
							
							        .log-warning {
 | 
						
						
						
						
							 | 
							
								 | 
							
							            color: #FF9800;
 | 
						
						
						
						
							 | 
							
								 | 
							
							        }
 | 
						
						
						
						
							 | 
							
								 | 
							
							
 | 
						
						
						
						
							 | 
							
								 | 
							
							        .log-info {
 | 
						
						
						
						
							 | 
							
								 | 
							
							            color: #2196F3;
 | 
						
						
						
						
							 | 
							
								 | 
							
							        }
 | 
						
						
						
						
							 | 
							
								 | 
							
							
 | 
						
						
						
						
							 | 
							
								 | 
							
							        .permission-status {
 | 
						
						
						
						
							 | 
							
								 | 
							
							            display: inline-block;
 | 
						
						
						
						
							 | 
							
								 | 
							
							            padding: 4px 8px;
 | 
						
						
						
						
							 | 
							
								 | 
							
							            border-radius: 4px;
 | 
						
						
						
						
							 | 
							
								 | 
							
							            font-weight: bold;
 | 
						
						
						
						
							 | 
							
								 | 
							
							            margin-left: 10px;
 | 
						
						
						
						
							 | 
							
								 | 
							
							        }
 | 
						
						
						
						
							 | 
							
								 | 
							
							
 | 
						
						
						
						
							 | 
							
								 | 
							
							        .status-granted {
 | 
						
						
						
						
							 | 
							
								 | 
							
							            background: #4CAF50;
 | 
						
						
						
						
							 | 
							
								 | 
							
							        }
 | 
						
						
						
						
							 | 
							
								 | 
							
							
 | 
						
						
						
						
							 | 
							
								 | 
							
							        .status-denied {
 | 
						
						
						
						
							 | 
							
								 | 
							
							            background: #f44336;
 | 
						
						
						
						
							 | 
							
								 | 
							
							        }
 | 
						
						
						
						
							 | 
							
								 | 
							
							
 | 
						
						
						
						
							 | 
							
								 | 
							
							        .status-prompt {
 | 
						
						
						
						
							 | 
							
								 | 
							
							            background: #FF9800;
 | 
						
						
						
						
							 | 
							
								 | 
							
							        }
 | 
						
						
						
						
							 | 
							
								 | 
							
							
 | 
						
						
						
						
							 | 
							
								 | 
							
							        .status-unknown {
 | 
						
						
						
						
							 | 
							
								 | 
							
							            background: #666;
 | 
						
						
						
						
							 | 
							
								 | 
							
							        }
 | 
						
						
						
						
							 | 
							
								 | 
							
							    </style>
 | 
						
						
						
						
							 | 
							
								 | 
							
							</head>
 | 
						
						
						
						
							 | 
							
								 | 
							
							
 | 
						
						
						
						
							 | 
							
								 | 
							
							<body>
 | 
						
						
						
						
							 | 
							
								 | 
							
							    <div class="container">
 | 
						
						
						
						
							 | 
							
								 | 
							
							        <div class="header">
 | 
						
						
						
						
							 | 
							
								 | 
							
							            <h1>📷 摄像头权限测试工具</h1>
 | 
						
						
						
						
							 | 
							
								 | 
							
							            <p>全面测试摄像头权限获取方法的正确性</p>
 | 
						
						
						
						
							 | 
							
								 | 
							
							        </div>
 | 
						
						
						
						
							 | 
							
								 | 
							
							
 | 
						
						
						
						
							 | 
							
								 | 
							
							        <div class="test-section">
 | 
						
						
						
						
							 | 
							
								 | 
							
							            <h3 class="test-title">🔍 1. 浏览器兼容性检查</h3>
 | 
						
						
						
						
							 | 
							
								 | 
							
							            <div id="compatibilityResult" class="test-result">等待测试...</div>
 | 
						
						
						
						
							 | 
							
								 | 
							
							            <button class="btn" onclick="testCompatibility()">开始检查</button>
 | 
						
						
						
						
							 | 
							
								 | 
							
							        </div>
 | 
						
						
						
						
							 | 
							
								 | 
							
							
 | 
						
						
						
						
							 | 
							
								 | 
							
							        <div class="test-section">
 | 
						
						
						
						
							 | 
							
								 | 
							
							            <h3 class="test-title">🔐 2. 权限状态查询</h3>
 | 
						
						
						
						
							 | 
							
								 | 
							
							            <div id="permissionResult" class="test-result">等待测试...</div>
 | 
						
						
						
						
							 | 
							
								 | 
							
							            <button class="btn" onclick="checkPermissionStatus()">检查权限状态</button>
 | 
						
						
						
						
							 | 
							
								 | 
							
							        </div>
 | 
						
						
						
						
							 | 
							
								 | 
							
							
 | 
						
						
						
						
							 | 
							
								 | 
							
							        <div class="test-section">
 | 
						
						
						
						
							 | 
							
								 | 
							
							            <h3 class="test-title">📱 3. 设备枚举测试</h3>
 | 
						
						
						
						
							 | 
							
								 | 
							
							            <div id="deviceResult" class="test-result">等待测试...</div>
 | 
						
						
						
						
							 | 
							
								 | 
							
							            <button class="btn" onclick="enumerateDevices()">枚举设备</button>
 | 
						
						
						
						
							 | 
							
								 | 
							
							        </div>
 | 
						
						
						
						
							 | 
							
								 | 
							
							
 | 
						
						
						
						
							 | 
							
								 | 
							
							        <div class="test-section">
 | 
						
						
						
						
							 | 
							
								 | 
							
							            <h3 class="test-title">🎥 4. 摄像头访问测试</h3>
 | 
						
						
						
						
							 | 
							
								 | 
							
							            <div id="cameraResult" class="test-result">等待测试...</div>
 | 
						
						
						
						
							 | 
							
								 | 
							
							            <div class="video-container" style="display: none;" id="videoContainer">
 | 
						
						
						
						
							 | 
							
								 | 
							
							                <video id="testVideo" autoplay muted playsinline></video>
 | 
						
						
						
						
							 | 
							
								 | 
							
							            </div>
 | 
						
						
						
						
							 | 
							
								 | 
							
							            <button class="btn" onclick="testCameraAccess()">请求摄像头权限</button>
 | 
						
						
						
						
							 | 
							
								 | 
							
							            <button class="btn" onclick="stopCamera()" style="background: #f44336;">停止摄像头</button>
 | 
						
						
						
						
							 | 
							
								 | 
							
							        </div>
 | 
						
						
						
						
							 | 
							
								 | 
							
							
 | 
						
						
						
						
							 | 
							
								 | 
							
							        <div class="test-section">
 | 
						
						
						
						
							 | 
							
								 | 
							
							            <h3 class="test-title">📋 测试日志</h3>
 | 
						
						
						
						
							 | 
							
								 | 
							
							            <div id="logArea" class="log-area"></div>
 | 
						
						
						
						
							 | 
							
								 | 
							
							            <button class="btn" onclick="clearLog()" style="background: #666;">清空日志</button>
 | 
						
						
						
						
							 | 
							
								 | 
							
							        </div>
 | 
						
						
						
						
							 | 
							
								 | 
							
							    </div>
 | 
						
						
						
						
							 | 
							
								 | 
							
							
 | 
						
						
						
						
							 | 
							
								 | 
							
							    <script>
 | 
						
						
						
						
							 | 
							
								 | 
							
							        let currentStream = null;
 | 
						
						
						
						
							 | 
							
								 | 
							
							        let permissionChangeListener = null;
 | 
						
						
						
						
							 | 
							
								 | 
							
							
 | 
						
						
						
						
							 | 
							
								 | 
							
							        function log(message, type = 'info') {
 | 
						
						
						
						
							 | 
							
								 | 
							
							            const logArea = document.getElementById('logArea');
 | 
						
						
						
						
							 | 
							
								 | 
							
							            const timestamp = new Date().toLocaleTimeString();
 | 
						
						
						
						
							 | 
							
								 | 
							
							            const entry = document.createElement('div');
 | 
						
						
						
						
							 | 
							
								 | 
							
							            entry.className = `log-entry log-${type}`;
 | 
						
						
						
						
							 | 
							
								 | 
							
							            entry.textContent = `${timestamp} - ${message}`;
 | 
						
						
						
						
							 | 
							
								 | 
							
							            logArea.appendChild(entry);
 | 
						
						
						
						
							 | 
							
								 | 
							
							            logArea.scrollTop = logArea.scrollHeight;
 | 
						
						
						
						
							 | 
							
								 | 
							
							        }
 | 
						
						
						
						
							 | 
							
								 | 
							
							
 | 
						
						
						
						
							 | 
							
								 | 
							
							        function clearLog() {
 | 
						
						
						
						
							 | 
							
								 | 
							
							            document.getElementById('logArea').innerHTML = '';
 | 
						
						
						
						
							 | 
							
								 | 
							
							        }
 | 
						
						
						
						
							 | 
							
								 | 
							
							
 | 
						
						
						
						
							 | 
							
								 | 
							
							        function updateResult(elementId, content, type = 'info') {
 | 
						
						
						
						
							 | 
							
								 | 
							
							            const element = document.getElementById(elementId);
 | 
						
						
						
						
							 | 
							
								 | 
							
							            element.innerHTML = content;
 | 
						
						
						
						
							 | 
							
								 | 
							
							            element.className = `test-result log-${type}`;
 | 
						
						
						
						
							 | 
							
								 | 
							
							        }
 | 
						
						
						
						
							 | 
							
								 | 
							
							
 | 
						
						
						
						
							 | 
							
								 | 
							
							        // 1. 浏览器兼容性检查
 | 
						
						
						
						
							 | 
							
								 | 
							
							        function testCompatibility() {
 | 
						
						
						
						
							 | 
							
								 | 
							
							            log('开始浏览器兼容性检查...', 'info');
 | 
						
						
						
						
							 | 
							
								 | 
							
							
 | 
						
						
						
						
							 | 
							
								 | 
							
							            const checks = [
 | 
						
						
						
						
							 | 
							
								 | 
							
							                {
 | 
						
						
						
						
							 | 
							
								 | 
							
							                    name: 'MediaDevices API',
 | 
						
						
						
						
							 | 
							
								 | 
							
							                    test: () => !!navigator.mediaDevices,
 | 
						
						
						
						
							 | 
							
								 | 
							
							                    required: true
 | 
						
						
						
						
							 | 
							
								 | 
							
							                },
 | 
						
						
						
						
							 | 
							
								 | 
							
							                {
 | 
						
						
						
						
							 | 
							
								 | 
							
							                    name: 'getUserMedia方法',
 | 
						
						
						
						
							 | 
							
								 | 
							
							                    test: () => !!(navigator.mediaDevices && navigator.mediaDevices.getUserMedia),
 | 
						
						
						
						
							 | 
							
								 | 
							
							                    required: true
 | 
						
						
						
						
							 | 
							
								 | 
							
							                },
 | 
						
						
						
						
							 | 
							
								 | 
							
							                {
 | 
						
						
						
						
							 | 
							
								 | 
							
							                    name: 'enumerateDevices方法',
 | 
						
						
						
						
							 | 
							
								 | 
							
							                    test: () => !!(navigator.mediaDevices && navigator.mediaDevices.enumerateDevices),
 | 
						
						
						
						
							 | 
							
								 | 
							
							                    required: false
 | 
						
						
						
						
							 | 
							
								 | 
							
							                },
 | 
						
						
						
						
							 | 
							
								 | 
							
							                {
 | 
						
						
						
						
							 | 
							
								 | 
							
							                    name: 'Permissions API',
 | 
						
						
						
						
							 | 
							
								 | 
							
							                    test: () => !!navigator.permissions,
 | 
						
						
						
						
							 | 
							
								 | 
							
							                    required: false
 | 
						
						
						
						
							 | 
							
								 | 
							
							                },
 | 
						
						
						
						
							 | 
							
								 | 
							
							                {
 | 
						
						
						
						
							 | 
							
								 | 
							
							                    name: 'HTTPS环境',
 | 
						
						
						
						
							 | 
							
								 | 
							
							                    test: () => location.protocol === 'https:' || location.hostname === 'localhost',
 | 
						
						
						
						
							 | 
							
								 | 
							
							                    required: true
 | 
						
						
						
						
							 | 
							
								 | 
							
							                }
 | 
						
						
						
						
							 | 
							
								 | 
							
							            ];
 | 
						
						
						
						
							 | 
							
								 | 
							
							
 | 
						
						
						
						
							 | 
							
								 | 
							
							            let resultHtml = '<h4>兼容性检查结果:</h4>';
 | 
						
						
						
						
							 | 
							
								 | 
							
							            let allPassed = true;
 | 
						
						
						
						
							 | 
							
								 | 
							
							
 | 
						
						
						
						
							 | 
							
								 | 
							
							            checks.forEach(check => {
 | 
						
						
						
						
							 | 
							
								 | 
							
							                const passed = check.test();
 | 
						
						
						
						
							 | 
							
								 | 
							
							                const status = passed ? '✅' : (check.required ? '❌' : '⚠️');
 | 
						
						
						
						
							 | 
							
								 | 
							
							                const statusText = passed ? '支持' : '不支持';
 | 
						
						
						
						
							 | 
							
								 | 
							
							
 | 
						
						
						
						
							 | 
							
								 | 
							
							                resultHtml += `<div>${status} ${check.name}: ${statusText}</div>`;
 | 
						
						
						
						
							 | 
							
								 | 
							
							
 | 
						
						
						
						
							 | 
							
								 | 
							
							                if (!passed && check.required) {
 | 
						
						
						
						
							 | 
							
								 | 
							
							                    allPassed = false;
 | 
						
						
						
						
							 | 
							
								 | 
							
							                }
 | 
						
						
						
						
							 | 
							
								 | 
							
							
 | 
						
						
						
						
							 | 
							
								 | 
							
							                log(`${check.name}: ${statusText}`, passed ? 'success' : (check.required ? 'error' : 'warning'));
 | 
						
						
						
						
							 | 
							
								 | 
							
							            });
 | 
						
						
						
						
							 | 
							
								 | 
							
							
 | 
						
						
						
						
							 | 
							
								 | 
							
							            if (allPassed) {
 | 
						
						
						
						
							 | 
							
								 | 
							
							                resultHtml += '<div style="color: #4CAF50; margin-top: 10px;"><strong>✅ 浏览器完全兼容摄像头功能</strong></div>';
 | 
						
						
						
						
							 | 
							
								 | 
							
							                log('浏览器兼容性检查通过', 'success');
 | 
						
						
						
						
							 | 
							
								 | 
							
							            } else {
 | 
						
						
						
						
							 | 
							
								 | 
							
							                resultHtml += '<div style="color: #f44336; margin-top: 10px;"><strong>❌ 浏览器不兼容,请使用现代浏览器</strong></div>';
 | 
						
						
						
						
							 | 
							
								 | 
							
							                log('浏览器兼容性检查失败', 'error');
 | 
						
						
						
						
							 | 
							
								 | 
							
							            }
 | 
						
						
						
						
							 | 
							
								 | 
							
							
 | 
						
						
						
						
							 | 
							
								 | 
							
							            updateResult('compatibilityResult', resultHtml, allPassed ? 'success' : 'error');
 | 
						
						
						
						
							 | 
							
								 | 
							
							        }
 | 
						
						
						
						
							 | 
							
								 | 
							
							
 | 
						
						
						
						
							 | 
							
								 | 
							
							        // 2. 权限状态查询
 | 
						
						
						
						
							 | 
							
								 | 
							
							        async function checkPermissionStatus() {
 | 
						
						
						
						
							 | 
							
								 | 
							
							            log('开始权限状态查询...', 'info');
 | 
						
						
						
						
							 | 
							
								 | 
							
							
 | 
						
						
						
						
							 | 
							
								 | 
							
							            try {
 | 
						
						
						
						
							 | 
							
								 | 
							
							                if (!navigator.permissions) {
 | 
						
						
						
						
							 | 
							
								 | 
							
							                    updateResult('permissionResult', '❌ 浏览器不支持权限查询API', 'warning');
 | 
						
						
						
						
							 | 
							
								 | 
							
							                    log('浏览器不支持权限查询API', 'warning');
 | 
						
						
						
						
							 | 
							
								 | 
							
							                    return;
 | 
						
						
						
						
							 | 
							
								 | 
							
							                }
 | 
						
						
						
						
							 | 
							
								 | 
							
							
 | 
						
						
						
						
							 | 
							
								 | 
							
							                const result = await navigator.permissions.query({ name: 'camera' });
 | 
						
						
						
						
							 | 
							
								 | 
							
							                const statusMap = {
 | 
						
						
						
						
							 | 
							
								 | 
							
							                    'granted': { text: '已授权', color: 'success', icon: '✅' },
 | 
						
						
						
						
							 | 
							
								 | 
							
							                    'denied': { text: '已拒绝', color: 'error', icon: '❌' },
 | 
						
						
						
						
							 | 
							
								 | 
							
							                    'prompt': { text: '需要询问', color: 'warning', icon: '⚠️' }
 | 
						
						
						
						
							 | 
							
								 | 
							
							                };
 | 
						
						
						
						
							 | 
							
								 | 
							
							
 | 
						
						
						
						
							 | 
							
								 | 
							
							                const status = statusMap[result.state] || { text: '未知', color: 'info', icon: '❓' };
 | 
						
						
						
						
							 | 
							
								 | 
							
							
 | 
						
						
						
						
							 | 
							
								 | 
							
							                let resultHtml = `
 | 
						
						
						
						
							 | 
							
								 | 
							
							                    <h4>权限状态查询结果:</h4>
 | 
						
						
						
						
							 | 
							
								 | 
							
							                    <div>${status.icon} 摄像头权限状态: <span class="permission-status status-${result.state}">${status.text}</span></div>
 | 
						
						
						
						
							 | 
							
								 | 
							
							                `;
 | 
						
						
						
						
							 | 
							
								 | 
							
							
 | 
						
						
						
						
							 | 
							
								 | 
							
							                // 添加权限变化监听
 | 
						
						
						
						
							 | 
							
								 | 
							
							                if (permissionChangeListener) {
 | 
						
						
						
						
							 | 
							
								 | 
							
							                    result.removeEventListener('change', permissionChangeListener);
 | 
						
						
						
						
							 | 
							
								 | 
							
							                }
 | 
						
						
						
						
							 | 
							
								 | 
							
							
 | 
						
						
						
						
							 | 
							
								 | 
							
							                permissionChangeListener = () => {
 | 
						
						
						
						
							 | 
							
								 | 
							
							                    log(`权限状态变化: ${result.state}`, 'info');
 | 
						
						
						
						
							 | 
							
								 | 
							
							                    checkPermissionStatus(); // 重新检查
 | 
						
						
						
						
							 | 
							
								 | 
							
							                };
 | 
						
						
						
						
							 | 
							
								 | 
							
							
 | 
						
						
						
						
							 | 
							
								 | 
							
							                result.addEventListener('change', permissionChangeListener);
 | 
						
						
						
						
							 | 
							
								 | 
							
							
 | 
						
						
						
						
							 | 
							
								 | 
							
							                resultHtml += '<div style="margin-top: 10px;">✅ 已设置权限变化监听</div>';
 | 
						
						
						
						
							 | 
							
								 | 
							
							
 | 
						
						
						
						
							 | 
							
								 | 
							
							                updateResult('permissionResult', resultHtml, status.color);
 | 
						
						
						
						
							 | 
							
								 | 
							
							                log(`摄像头权限状态: ${result.state}`, status.color);
 | 
						
						
						
						
							 | 
							
								 | 
							
							
 | 
						
						
						
						
							 | 
							
								 | 
							
							            } catch (error) {
 | 
						
						
						
						
							 | 
							
								 | 
							
							                const errorMsg = `权限查询失败: ${error.message}`;
 | 
						
						
						
						
							 | 
							
								 | 
							
							                updateResult('permissionResult', `❌ ${errorMsg}`, 'error');
 | 
						
						
						
						
							 | 
							
								 | 
							
							                log(errorMsg, 'error');
 | 
						
						
						
						
							 | 
							
								 | 
							
							            }
 | 
						
						
						
						
							 | 
							
								 | 
							
							        }
 | 
						
						
						
						
							 | 
							
								 | 
							
							
 | 
						
						
						
						
							 | 
							
								 | 
							
							        // 3. 设备枚举测试
 | 
						
						
						
						
							 | 
							
								 | 
							
							        async function enumerateDevices() {
 | 
						
						
						
						
							 | 
							
								 | 
							
							            log('开始设备枚举测试...', 'info');
 | 
						
						
						
						
							 | 
							
								 | 
							
							
 | 
						
						
						
						
							 | 
							
								 | 
							
							            try {
 | 
						
						
						
						
							 | 
							
								 | 
							
							                if (!navigator.mediaDevices || !navigator.mediaDevices.enumerateDevices) {
 | 
						
						
						
						
							 | 
							
								 | 
							
							                    updateResult('deviceResult', '❌ 浏览器不支持设备枚举功能', 'error');
 | 
						
						
						
						
							 | 
							
								 | 
							
							                    log('浏览器不支持设备枚举功能', 'error');
 | 
						
						
						
						
							 | 
							
								 | 
							
							                    return;
 | 
						
						
						
						
							 | 
							
								 | 
							
							                }
 | 
						
						
						
						
							 | 
							
								 | 
							
							
 | 
						
						
						
						
							 | 
							
								 | 
							
							                const devices = await navigator.mediaDevices.enumerateDevices();
 | 
						
						
						
						
							 | 
							
								 | 
							
							                const videoDevices = devices.filter(device => device.kind === 'videoinput');
 | 
						
						
						
						
							 | 
							
								 | 
							
							
 | 
						
						
						
						
							 | 
							
								 | 
							
							                let resultHtml = `<h4>设备枚举结果:</h4>`;
 | 
						
						
						
						
							 | 
							
								 | 
							
							                resultHtml += `<div>📹 发现 ${videoDevices.length} 个视频设备</div>`;
 | 
						
						
						
						
							 | 
							
								 | 
							
							
 | 
						
						
						
						
							 | 
							
								 | 
							
							                if (videoDevices.length === 0) {
 | 
						
						
						
						
							 | 
							
								 | 
							
							                    resultHtml += '<div style="color: #f44336;">❌ 未找到任何视频设备</div>';
 | 
						
						
						
						
							 | 
							
								 | 
							
							                    log('未找到任何视频设备', 'error');
 | 
						
						
						
						
							 | 
							
								 | 
							
							                } else {
 | 
						
						
						
						
							 | 
							
								 | 
							
							                    videoDevices.forEach((device, index) => {
 | 
						
						
						
						
							 | 
							
								 | 
							
							                        const label = device.label || `摄像头 ${index + 1} (需要权限显示详细信息)`;
 | 
						
						
						
						
							 | 
							
								 | 
							
							                        resultHtml += `<div>📱 设备 ${index + 1}: ${label}</div>`;
 | 
						
						
						
						
							 | 
							
								 | 
							
							                        log(`设备 ${index + 1}: ${label} (ID: ${device.deviceId.substr(0, 8)}...)`, 'info');
 | 
						
						
						
						
							 | 
							
								 | 
							
							                    });
 | 
						
						
						
						
							 | 
							
								 | 
							
							
 | 
						
						
						
						
							 | 
							
								 | 
							
							                    // 检查是否有设备标签
 | 
						
						
						
						
							 | 
							
								 | 
							
							                    const hasLabels = videoDevices.some(device => device.label);
 | 
						
						
						
						
							 | 
							
								 | 
							
							                    if (!hasLabels) {
 | 
						
						
						
						
							 | 
							
								 | 
							
							                        resultHtml += '<div style="color: #FF9800; margin-top: 10px;">⚠️ 设备标签为空,可能需要先获取摄像头权限</div>';
 | 
						
						
						
						
							 | 
							
								 | 
							
							                        log('设备标签为空,需要摄像头权限才能显示详细信息', 'warning');
 | 
						
						
						
						
							 | 
							
								 | 
							
							                    }
 | 
						
						
						
						
							 | 
							
								 | 
							
							                }
 | 
						
						
						
						
							 | 
							
								 | 
							
							
 | 
						
						
						
						
							 | 
							
								 | 
							
							                updateResult('deviceResult', resultHtml, videoDevices.length > 0 ? 'success' : 'error');
 | 
						
						
						
						
							 | 
							
								 | 
							
							                log(`设备枚举完成,找到 ${videoDevices.length} 个视频设备`, 'success');
 | 
						
						
						
						
							 | 
							
								 | 
							
							
 | 
						
						
						
						
							 | 
							
								 | 
							
							            } catch (error) {
 | 
						
						
						
						
							 | 
							
								 | 
							
							                const errorMsg = `设备枚举失败: ${error.message}`;
 | 
						
						
						
						
							 | 
							
								 | 
							
							                updateResult('deviceResult', `❌ ${errorMsg}`, 'error');
 | 
						
						
						
						
							 | 
							
								 | 
							
							                log(errorMsg, 'error');
 | 
						
						
						
						
							 | 
							
								 | 
							
							            }
 | 
						
						
						
						
							 | 
							
								 | 
							
							        }
 | 
						
						
						
						
							 | 
							
								 | 
							
							
 | 
						
						
						
						
							 | 
							
								 | 
							
							        // 4. 摄像头访问测试
 | 
						
						
						
						
							 | 
							
								 | 
							
							        async function testCameraAccess() {
 | 
						
						
						
						
							 | 
							
								 | 
							
							            log('开始摄像头访问测试...', 'info');
 | 
						
						
						
						
							 | 
							
								 | 
							
							
 | 
						
						
						
						
							 | 
							
								 | 
							
							            try {
 | 
						
						
						
						
							 | 
							
								 | 
							
							                // 先停止之前的流
 | 
						
						
						
						
							 | 
							
								 | 
							
							                if (currentStream) {
 | 
						
						
						
						
							 | 
							
								 | 
							
							                    currentStream.getTracks().forEach(track => track.stop());
 | 
						
						
						
						
							 | 
							
								 | 
							
							                    currentStream = null;
 | 
						
						
						
						
							 | 
							
								 | 
							
							                }
 | 
						
						
						
						
							 | 
							
								 | 
							
							
 | 
						
						
						
						
							 | 
							
								 | 
							
							                if (!navigator.mediaDevices || !navigator.mediaDevices.getUserMedia) {
 | 
						
						
						
						
							 | 
							
								 | 
							
							                    throw new Error('浏览器不支持摄像头访问功能');
 | 
						
						
						
						
							 | 
							
								 | 
							
							                }
 | 
						
						
						
						
							 | 
							
								 | 
							
							
 | 
						
						
						
						
							 | 
							
								 | 
							
							                const constraints = {
 | 
						
						
						
						
							 | 
							
								 | 
							
							                    video: {
 | 
						
						
						
						
							 | 
							
								 | 
							
							                        facingMode: 'environment', // 优先使用后置摄像头
 | 
						
						
						
						
							 | 
							
								 | 
							
							                        width: { ideal: 640 },
 | 
						
						
						
						
							 | 
							
								 | 
							
							                        height: { ideal: 480 },
 | 
						
						
						
						
							 | 
							
								 | 
							
							                        frameRate: { ideal: 30, max: 30 }
 | 
						
						
						
						
							 | 
							
								 | 
							
							                    },
 | 
						
						
						
						
							 | 
							
								 | 
							
							                    audio: false
 | 
						
						
						
						
							 | 
							
								 | 
							
							                };
 | 
						
						
						
						
							 | 
							
								 | 
							
							
 | 
						
						
						
						
							 | 
							
								 | 
							
							                log('正在请求摄像头权限...', 'info');
 | 
						
						
						
						
							 | 
							
								 | 
							
							
 | 
						
						
						
						
							 | 
							
								 | 
							
							                currentStream = await navigator.mediaDevices.getUserMedia(constraints);
 | 
						
						
						
						
							 | 
							
								 | 
							
							                const video = document.getElementById('testVideo');
 | 
						
						
						
						
							 | 
							
								 | 
							
							                video.srcObject = currentStream;
 | 
						
						
						
						
							 | 
							
								 | 
							
							
 | 
						
						
						
						
							 | 
							
								 | 
							
							                // 等待视频开始播放
 | 
						
						
						
						
							 | 
							
								 | 
							
							                await new Promise((resolve, reject) => {
 | 
						
						
						
						
							 | 
							
								 | 
							
							                    video.onloadedmetadata = () => {
 | 
						
						
						
						
							 | 
							
								 | 
							
							                        log('视频流准备就绪', 'success');
 | 
						
						
						
						
							 | 
							
								 | 
							
							                        resolve();
 | 
						
						
						
						
							 | 
							
								 | 
							
							                    };
 | 
						
						
						
						
							 | 
							
								 | 
							
							                    video.onerror = reject;
 | 
						
						
						
						
							 | 
							
								 | 
							
							                    setTimeout(() => reject(new Error('视频加载超时')), 10000);
 | 
						
						
						
						
							 | 
							
								 | 
							
							                });
 | 
						
						
						
						
							 | 
							
								 | 
							
							
 | 
						
						
						
						
							 | 
							
								 | 
							
							                // 显示视频容器
 | 
						
						
						
						
							 | 
							
								 | 
							
							                document.getElementById('videoContainer').style.display = 'block';
 | 
						
						
						
						
							 | 
							
								 | 
							
							
 | 
						
						
						
						
							 | 
							
								 | 
							
							                // 获取实际的视频配置
 | 
						
						
						
						
							 | 
							
								 | 
							
							                const track = currentStream.getVideoTracks()[0];
 | 
						
						
						
						
							 | 
							
								 | 
							
							                const settings = track.getSettings();
 | 
						
						
						
						
							 | 
							
								 | 
							
							
 | 
						
						
						
						
							 | 
							
								 | 
							
							                let resultHtml = `
 | 
						
						
						
						
							 | 
							
								 | 
							
							                    <h4>摄像头访问成功!</h4>
 | 
						
						
						
						
							 | 
							
								 | 
							
							                    <div>✅ 摄像头权限已获取</div>
 | 
						
						
						
						
							 | 
							
								 | 
							
							                    <div>📹 分辨率: ${settings.width}x${settings.height}</div>
 | 
						
						
						
						
							 | 
							
								 | 
							
							                    <div>🎯 帧率: ${settings.frameRate}fps</div>
 | 
						
						
						
						
							 | 
							
								 | 
							
							                    <div>📱 设备: ${track.label || '未知设备'}</div>
 | 
						
						
						
						
							 | 
							
								 | 
							
							                `;
 | 
						
						
						
						
							 | 
							
								 | 
							
							
 | 
						
						
						
						
							 | 
							
								 | 
							
							                if (settings.facingMode) {
 | 
						
						
						
						
							 | 
							
								 | 
							
							                    resultHtml += `<div>📷 摄像头方向: ${settings.facingMode}</div>`;
 | 
						
						
						
						
							 | 
							
								 | 
							
							                }
 | 
						
						
						
						
							 | 
							
								 | 
							
							
 | 
						
						
						
						
							 | 
							
								 | 
							
							                updateResult('cameraResult', resultHtml, 'success');
 | 
						
						
						
						
							 | 
							
								 | 
							
							                log(`摄像头访问成功: ${track.label || '未知设备'}`, 'success');
 | 
						
						
						
						
							 | 
							
								 | 
							
							                log(`视频配置: ${settings.width}x${settings.height}@${settings.frameRate}fps`, 'info');
 | 
						
						
						
						
							 | 
							
								 | 
							
							
 | 
						
						
						
						
							 | 
							
								 | 
							
							            } catch (error) {
 | 
						
						
						
						
							 | 
							
								 | 
							
							                let errorMsg = error.message;
 | 
						
						
						
						
							 | 
							
								 | 
							
							                let errorType = 'error';
 | 
						
						
						
						
							 | 
							
								 | 
							
							
 | 
						
						
						
						
							 | 
							
								 | 
							
							                // 详细的错误分析
 | 
						
						
						
						
							 | 
							
								 | 
							
							                if (error.name === 'NotAllowedError') {
 | 
						
						
						
						
							 | 
							
								 | 
							
							                    errorMsg = '摄像头权限被拒绝';
 | 
						
						
						
						
							 | 
							
								 | 
							
							                    errorType = 'error';
 | 
						
						
						
						
							 | 
							
								 | 
							
							                } else if (error.name === 'NotFoundError') {
 | 
						
						
						
						
							 | 
							
								 | 
							
							                    errorMsg = '未找到可用的摄像头设备';
 | 
						
						
						
						
							 | 
							
								 | 
							
							                    errorType = 'error';
 | 
						
						
						
						
							 | 
							
								 | 
							
							                } else if (error.name === 'NotSupportedError') {
 | 
						
						
						
						
							 | 
							
								 | 
							
							                    errorMsg = '浏览器不支持摄像头功能';
 | 
						
						
						
						
							 | 
							
								 | 
							
							                    errorType = 'error';
 | 
						
						
						
						
							 | 
							
								 | 
							
							                } else if (error.name === 'NotReadableError') {
 | 
						
						
						
						
							 | 
							
								 | 
							
							                    errorMsg = '摄像头被其他应用占用';
 | 
						
						
						
						
							 | 
							
								 | 
							
							                    errorType = 'error';
 | 
						
						
						
						
							 | 
							
								 | 
							
							                } else if (error.name === 'OverconstrainedError') {
 | 
						
						
						
						
							 | 
							
								 | 
							
							                    errorMsg = '摄像头不支持请求的配置';
 | 
						
						
						
						
							 | 
							
								 | 
							
							                    errorType = 'warning';
 | 
						
						
						
						
							 | 
							
								 | 
							
							                } else if (error.name === 'SecurityError') {
 | 
						
						
						
						
							 | 
							
								 | 
							
							                    errorMsg = '安全限制:请在HTTPS环境下访问';
 | 
						
						
						
						
							 | 
							
								 | 
							
							                    errorType = 'error';
 | 
						
						
						
						
							 | 
							
								 | 
							
							                }
 | 
						
						
						
						
							 | 
							
								 | 
							
							
 | 
						
						
						
						
							 | 
							
								 | 
							
							                let resultHtml = `<h4>摄像头访问失败</h4><div>❌ ${errorMsg}</div>`;
 | 
						
						
						
						
							 | 
							
								 | 
							
							
 | 
						
						
						
						
							 | 
							
								 | 
							
							                // 提供解决建议
 | 
						
						
						
						
							 | 
							
								 | 
							
							                if (error.name === 'NotAllowedError') {
 | 
						
						
						
						
							 | 
							
								 | 
							
							                    resultHtml += `
 | 
						
						
						
						
							 | 
							
								 | 
							
							                        <div style="margin-top: 10px;">
 | 
						
						
						
						
							 | 
							
								 | 
							
							                            <strong>💡 解决方案:</strong><br>
 | 
						
						
						
						
							 | 
							
								 | 
							
							                            1. 点击浏览器地址栏的摄像头图标或锁图标<br>
 | 
						
						
						
						
							 | 
							
								 | 
							
							                            2. 选择"允许"摄像头权限<br>
 | 
						
						
						
						
							 | 
							
								 | 
							
							                            3. 刷新页面重试
 | 
						
						
						
						
							 | 
							
								 | 
							
							                        </div>
 | 
						
						
						
						
							 | 
							
								 | 
							
							                    `;
 | 
						
						
						
						
							 | 
							
								 | 
							
							                }
 | 
						
						
						
						
							 | 
							
								 | 
							
							
 | 
						
						
						
						
							 | 
							
								 | 
							
							                updateResult('cameraResult', resultHtml, errorType);
 | 
						
						
						
						
							 | 
							
								 | 
							
							                log(`摄像头访问失败: ${errorMsg}`, errorType);
 | 
						
						
						
						
							 | 
							
								 | 
							
							            }
 | 
						
						
						
						
							 | 
							
								 | 
							
							        }
 | 
						
						
						
						
							 | 
							
								 | 
							
							
 | 
						
						
						
						
							 | 
							
								 | 
							
							        // 停止摄像头
 | 
						
						
						
						
							 | 
							
								 | 
							
							        function stopCamera() {
 | 
						
						
						
						
							 | 
							
								 | 
							
							            if (currentStream) {
 | 
						
						
						
						
							 | 
							
								 | 
							
							                currentStream.getTracks().forEach(track => track.stop());
 | 
						
						
						
						
							 | 
							
								 | 
							
							                currentStream = null;
 | 
						
						
						
						
							 | 
							
								 | 
							
							                document.getElementById('videoContainer').style.display = 'none';
 | 
						
						
						
						
							 | 
							
								 | 
							
							
 | 
						
						
						
						
							 | 
							
								 | 
							
							                updateResult('cameraResult', '<h4>摄像头已停止</h4><div>📱 视频流已释放</div>', 'info');
 | 
						
						
						
						
							 | 
							
								 | 
							
							                log('摄像头已停止,视频流已释放', 'info');
 | 
						
						
						
						
							 | 
							
								 | 
							
							            } else {
 | 
						
						
						
						
							 | 
							
								 | 
							
							                log('没有活跃的摄像头流', 'warning');
 | 
						
						
						
						
							 | 
							
								 | 
							
							            }
 | 
						
						
						
						
							 | 
							
								 | 
							
							        }
 | 
						
						
						
						
							 | 
							
								 | 
							
							
 | 
						
						
						
						
							 | 
							
								 | 
							
							        // 页面加载时自动运行兼容性检查
 | 
						
						
						
						
							 | 
							
								 | 
							
							        window.onload = function () {
 | 
						
						
						
						
							 | 
							
								 | 
							
							            log('摄像头权限测试工具已加载', 'success');
 | 
						
						
						
						
							 | 
							
								 | 
							
							            testCompatibility();
 | 
						
						
						
						
							 | 
							
								 | 
							
							        };
 | 
						
						
						
						
							 | 
							
								 | 
							
							
 | 
						
						
						
						
							 | 
							
								 | 
							
							        // 页面卸载时清理资源
 | 
						
						
						
						
							 | 
							
								 | 
							
							        window.onbeforeunload = function () {
 | 
						
						
						
						
							 | 
							
								 | 
							
							            stopCamera();
 | 
						
						
						
						
							 | 
							
								 | 
							
							        };
 | 
						
						
						
						
							 | 
							
								 | 
							
							    </script>
 | 
						
						
						
						
							 | 
							
								 | 
							
							</body>
 | 
						
						
						
						
							 | 
							
								 | 
							
							
 | 
						
						
						
						
							 | 
							
								 | 
							
							</html> |