麦克风和摄像头调用测试edit icon

作者:
lynn-ssk
Fork(复制)
下载
嵌入
BUG反馈
index.html
现在支持上传本地图片了!
index.html
            
            <!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>麦克风和摄像头测试</title>
    <script src="https://cdn.tailwindcss.com"></script>
    <style>
        .camera-container {
            position: relative;
            width: 100%;
            max-width: 640px;
            margin: 0 auto;
        }
        .audio-level-bar {
            height: 20px;
            background-color: #e5e7eb;
            border-radius: 10px;
            overflow: hidden;
            margin-top: 10px;
        }
        .audio-level-fill {
            height: 100%;
            background: linear-gradient(90deg, #3b82f6, #10b981);
            border-radius: 10px;
            transition: width 0.1s ease;
        }
        .status-indicator {
            display: inline-block;
            width: 12px;
            height: 12px;
            border-radius: 50%;
            margin-right: 8px;
        }
        .status-active {
            background-color: #10b981;
            box-shadow: 0 0 8px #10b981;
        }
        .status-inactive {
            background-color: #ef4444;
        }
        .device-item {
            transition: all 0.3s ease;
        }
        .device-item:hover {
            transform: translateY(-2px);
            box-shadow: 0 4px 12px rgba(0,0,0,0.15);
        }
    </style>
</head>
<body class="bg-gray-100 min-h-screen">
    <div class="container mx-auto px-4 py-8">
        <div class="text-center mb-10">
            <h1 class="text-3xl font-bold text-gray-800 mb-2">麦克风和摄像头测试</h1>
            <p class="text-gray-600">测试您的设备音频和视频功能</p>
        </div>

        <!-- 设备状态区域 -->
        <div class="grid grid-cols-1 md:grid-cols-2 gap-6 mb-10">
            <!-- 麦克风状态 -->
            <div class="bg-white rounded-xl shadow-lg p-6 device-item">
                <div class="flex items-center justify-between mb-4">
                    <h2 class="text-xl font-semibold text-gray-800 flex items-center">
                        <span class="status-indicator status-inactive" id="mic-status"></span>
                        麦克风
                    </h2>
                    <span class="text-sm text-gray-500" id="mic-status-text">未检测到</span>
                </div>
                
                <div class="mb-4">
                    <div class="flex justify-between text-sm text-gray-600 mb-1">
                        <span>音量强度</span>
                        <span id="mic-volume-value">0%</span>
                    </div>
                    <div class="audio-level-bar">
                        <div class="audio-level-fill" id="mic-level-bar" style="width: 0%"></div>
                    </div>
                </div>
                
                <div class="space-y-2">
                    <button id="start-mic-btn" class="w-full bg-blue-500 hover:bg-blue-600 text-white py-2 px-4 rounded-lg transition duration-200">
                        开始测试麦克风
                    </button>
                    <button id="stop-mic-btn" class="w-full bg-red-500 hover:bg-red-600 text-white py-2 px-4 rounded-lg transition duration-200 hidden">
                        停止测试
                    </button>
                </div>
            </div>

            <!-- 摄像头状态 -->
            <div class="bg-white rounded-xl shadow-lg p-6 device-item">
                <div class="flex items-center justify-between mb-4">
                    <h2 class="text-xl font-semibold text-gray-800 flex items-center">
                        <span class="status-indicator status-inactive" id="camera-status"></span>
                        摄像头
                    </h2>
                    <span class="text-sm text-gray-500" id="camera-status-text">未检测到</span>
                </div>
                
                <div class="camera-container">
                    <video id="camera-preview" autoplay muted playsinline class="w-full h-64 object-cover rounded-lg bg-gray-200 border border-gray-300"></video>
                    <div class="absolute inset-0 flex items-center justify-center pointer-events-none">
                        <div id="camera-placeholder" class="text-gray-500 text-center">
                            <svg xmlns="http://www.w3.org/2000/svg" class="h-12 w-12 mx-auto mb-2 opacity-50" fill="none" viewBox="0 0 24 24" stroke="currentColor">
                                <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 10l4.553-2.276A1 1 0 0121 8.618v6.764a1 1 0 01-1.447.894L15 14M5 18h8a2 2 0 002-2V8a2 2 0 00-2-2H5a2 2 0 00-2 2v8a2 2 0 002 2z" />
                            </svg>
                            <p>摄像头预览</p>
                        </div>
                    </div>
                </div>
                
                <div class="mt-4 space-y-2">
                    <button id="start-camera-btn" class="w-full bg-blue-500 hover:bg-blue-600 text-white py-2 px-4 rounded-lg transition duration-200">
                        开始测试摄像头
                    </button>
                    <button id="stop-camera-btn" class="w-full bg-red-500 hover:bg-red-600 text-white py-2 px-4 rounded-lg transition duration-200 hidden">
                        停止测试
                    </button>
                </div>
            </div>
        </div>

        <!-- 设备列表 -->
        <div class="bg-white rounded-xl shadow-lg p-6 mb-10">
            <h2 class="text-xl font-semibold text-gray-800 mb-4">可用设备</h2>
            
            <div class="grid grid-cols-1 md:grid-cols-2 gap-4">
                <div>
                    <h3 class="font-medium text-gray-700 mb-2">音频输入设备</h3>
                    <select id="audio-input-select" class="w-full p-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent">
                        <option value="">选择音频输入设备</option>
                    </select>
                </div>
                
                <div>
                    <h3 class="font-medium text-gray-700 mb-2">视频输入设备</h3>
                    <select id="video-input-select" class="w-full p-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent">
                        <option value="">选择视频输入设备</option>
                    </select>
                </div>
            </div>
        </div>

        <!-- 测试结果 -->
        <div class="bg-white rounded-xl shadow-lg p-6">
            <h2 class="text-xl font-semibold text-gray-800 mb-4">测试结果</h2>
            <div id="test-results" class="prose max-w-none">
                <p class="text-gray-600">点击上方按钮开始测试您的设备。</p>
            </div>
        </div>
    </div>

    <script>
        // 全局变量
        let mediaStream = null;
        let audioContext = null;
        let analyser = null;
        let microphone = null;
        let animationFrameId = null;

        // DOM 元素
        const startMicBtn = document.getElementById('start-mic-btn');
        const stopMicBtn = document.getElementById('stop-mic-btn');
        const startCameraBtn = document.getElementById('start-camera-btn');
        const stopCameraBtn = document.getElementById('stop-camera-btn');
        const cameraPreview = document.getElementById('camera-preview');
        const micLevelBar = document.getElementById('mic-level-bar');
        const micVolumeValue = document.getElementById('mic-volume-value');
        const micStatus = document.getElementById('mic-status');
        const micStatusText = document.getElementById('mic-status-text');
        const cameraStatus = document.getElementById('camera-status');
        const cameraStatusText = document.getElementById('camera-status-text');
        const testResults = document.getElementById('test-results');
        const audioInputSelect = document.getElementById('audio-input-select');
        const videoInputSelect = document.getElementById('video-input-select');
        const cameraPlaceholder = document.getElementById('camera-placeholder');

        // 初始化设备列表
        async function initDeviceList() {
            try {
                const devices = await navigator.mediaDevices.enumerateDevices();
                const audioInputs = devices.filter(device => device.kind === 'audioinput');
                const videoInputs = devices.filter(device => device.kind === 'videoinput');

                // 清空现有选项
                audioInputSelect.innerHTML = '<option value="">选择音频输入设备</option>';
                videoInputSelect.innerHTML = '<option value="">选择视频输入设备</option>';

                // 添加音频输入设备
                audioInputs.forEach(device => {
                    const option = document.createElement('option');
                    option.value = device.deviceId;
                    option.textContent = device.label || `麦克风 ${audioInputSelect.children.length}`;
                    audioInputSelect.appendChild(option);
                });

                // 添加视频输入设备
                videoInputs.forEach(device => {
                    const option = document.createElement('option');
                    option.value = device.deviceId;
                    option.textContent = device.label || `摄像头 ${videoInputSelect.children.length}`;
                    videoInputSelect.appendChild(option);
                });
            } catch (error) {
                console.error('获取设备列表失败:', error);
                testResults.innerHTML = `<p class="text-red-500">无法获取设备列表: ${error.message}</p>`;
            }
        }

        // 更新麦克风音量显示
        function updateMicLevel() {
            if (!analyser) return;

            const dataArray = new Uint8Array(analyser.frequencyBinCount);
            analyser.getByteFrequencyData(dataArray);
            
            // 计算平均值
            let sum = 0;
            for (let i = 0; i < dataArray.length; i++) {
                sum += dataArray[i];
            }
            const average = sum / dataArray.length;
            
            // 更新UI
            const volumePercent = Math.round(average / 2.55); // 转换为百分比
            micVolumeValue.textContent = `${volumePercent}%`;
            micLevelBar.style.width = `${volumePercent}%`;
            
            // 递归调用以持续更新
            animationFrameId = requestAnimationFrame(updateMicLevel);
        }

        // 开始麦克风测试
        async function startMicrophoneTest() {
            try {
                // 停止之前的流
                stopMicrophoneTest();
                
                // 获取设备ID
                const deviceId = audioInputSelect.value || undefined;
                const constraints = {
                    audio: deviceId ? { deviceId: { exact: deviceId } } : true,
                    video: false
                };

                // 获取媒体流
                mediaStream = await navigator.mediaDevices.getUserMedia(constraints);
                
                // 创建音频上下文和分析器
                audioContext = new (window.AudioContext || window.webkitAudioContext)();
                analyser = audioContext.createAnalyser();
                microphone = audioContext.createMediaStreamSource(mediaStream);
                microphone.connect(analyser);
                
                // 启动音量监控
                updateMicLevel();
                
                // 更新UI状态
                micStatus.className = 'status-indicator status-active';
                micStatusText.textContent = '正在使用';
                startMicBtn.classList.add('hidden');
                stopMicBtn.classList.remove('hidden');
                
                testResults.innerHTML = `
                    <div class="text-green-600">
                        <p><strong>✓ 麦克风测试成功</strong></p>
                        <p>已成功访问麦克风设备</p>
                        ${deviceId ? `<p>设备: ${audioInputSelect.options[audioInputSelect.selectedIndex].text}</p>` : ''}
                    </div>
                `;
            } catch (error) {
                console.error('麦克风测试失败:', error);
                testResults.innerHTML = `
                    <div class="text-red-600">
                        <p><strong>✗ 麦克风测试失败</strong></p>
                        <p>${error.message}</p>
                    </div>
                `;
                
                // 恢复按钮状态
                startMicBtn.classList.remove('hidden');
                stopMicBtn.classList.add('hidden');
            }
        }

        // 停止麦克风测试
        function stopMicrophoneTest() {
            if (animationFrameId) {
                cancelAnimationFrame(animationFrameId);
            }
            
            if (mediaStream) {
                mediaStream.getTracks().forEach(track => track.stop());
                mediaStream = null;
            }
            
            if (microphone) {
                microphone.disconnect();
                microphone = null;
            }
            
            if (analyser) {
                analyser.disconnect();
                analyser = null;
            }
            
            if (audioContext) {
                audioContext.close();
                audioContext = null;
            }
            
            // 更新UI状态
            micStatus.className = 'status-indicator status-inactive';
            micStatusText.textContent = '未检测到';
            startMicBtn.classList.remove('hidden');
            stopMicBtn.classList.add('hidden');
            micVolumeValue.textContent = '0%';
            micLevelBar.style.width = '0%';
        }

        // 开始摄像头测试
        async function startCameraTest() {
            try {
                // 停止之前的流
                stopCameraTest();
                
                // 获取设备ID
                const deviceId = videoInputSelect.value || undefined;
                const constraints = {
                    audio: false,
                    video: deviceId ? { deviceId: { exact: deviceId } } : true
                };

                // 获取媒体流
                mediaStream = await navigator.mediaDevices.getUserMedia(constraints);
                
                // 设置视频预览
                cameraPreview.srcObject = mediaStream;
                cameraPlaceholder.classList.add('hidden');
                
                // 更新UI状态
                cameraStatus.className = 'status-indicator status-active';
                cameraStatusText.textContent = '正在使用';
                startCameraBtn.classList.add('hidden');
                stopCameraBtn.classList.remove('hidden');
                
                testResults.innerHTML = `
                    <div class="text-green-600">
                        <p><strong>✓ 摄像头测试成功</strong></p>
                        <p>已成功访问摄像头设备</p>
                        ${deviceId ? `<p>设备: ${videoInputSelect.options[videoInputSelect.selectedIndex].text}</p>` : ''}
                    </div>
                `;
            } catch (error) {
                console.error('摄像头测试失败:', error);
                testResults.innerHTML = `
                    <div class="text-red-600">
                        <p><strong>✗ 摄像头测试失败</strong></p>
                        <p>${error.message}</p>
                    </div>
                `;
                
                // 恢复按钮状态
                startCameraBtn.classList.remove('hidden');
                stopCameraBtn.classList.add('hidden');
            }
        }

        // 停止摄像头测试
        function stopCameraTest() {
            if (mediaStream) {
                mediaStream.getTracks().forEach(track => track.stop());
                mediaStream = null;
            }
            
            // 清除视频预览
            cameraPreview.srcObject = null;
            cameraPlaceholder.classList.remove('hidden');
            
            // 更新UI状态
            cameraStatus.className = 'status-indicator status-inactive';
            cameraStatusText.textContent = '未检测到';
            startCameraBtn.classList.remove('hidden');
            stopCameraBtn.classList.add('hidden');
        }

        // 页面加载完成后初始化
        document.addEventListener('DOMContentLoaded', async () => {
            // 初始化设备列表
            await initDeviceList();
            
            // 绑定事件监听器
            startMicBtn.addEventListener('click', startMicrophoneTest);
            stopMicBtn.addEventListener('click', stopMicrophoneTest);
            startCameraBtn.addEventListener('click', startCameraTest);
            stopCameraBtn.addEventListener('click', stopCameraTest);
            
            // 设备选择变化时清除测试状态
            audioInputSelect.addEventListener('change', () => {
                if (mediaStream && mediaStream.active) {
                    stopMicrophoneTest();
                }
            });
            
            videoInputSelect.addEventListener('change', () => {
                if (mediaStream && mediaStream.active) {
                    stopCameraTest();
                }
            });
            
            // 显示初始信息
            testResults.innerHTML = '<p class="text-gray-600">点击上方按钮开始测试您的设备。</p>';
        });

        // 处理页面可见性变化(防止后台运行导致的问题)
        document.addEventListener('visibilitychange', () => {
            if (document.hidden) {
                // 页面隐藏时停止测试
                if (mediaStream && mediaStream.active) {
                    stopMicrophoneTest();
                    stopCameraTest();
                }
            }
        });
    </script>
</body>
</html>

        
预览
控制台