You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
Software_Architecture/distance-judgement/mobile/baidu_browser_test.html

1442 lines
53 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

<!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>
body {
font-family: Arial, sans-serif;
background: linear-gradient(135deg, #1e88e5 0%, #1565c0 100%);
color: white;
padding: 15px;
}
.btn {
background: #4CAF50;
color: white;
padding: 12px 20px;
border: none;
border-radius: 6px;
margin: 5px;
cursor: pointer;
text-decoration: none;
display: inline-block;
}
.btn:hover {
background: #45a049;
}
.btn-blue {
background: #2196F3;
}
.log-area {
background: rgba(0, 0, 0, 0.5);
padding: 15px;
height: 300px;
overflow-y: auto;
font-family: monospace;
font-size: 12px;
border-radius: 8px;
margin: 15px 0;
}
#testVideo {
width: 100%;
max-width: 400px;
height: auto;
border-radius: 5px;
}
.video-container {
background: #000;
border-radius: 8px;
padding: 10px;
margin: 15px 0;
text-align: center;
}
</style>
</head>
<body>
<h1>📱 百度浏览器摄像头测试</h1>
<p>专门针对百度浏览器的摄像头API兼容性测试</p>
<button class="btn" onclick="testBaiduCamera()">测试百度浏览器摄像头</button>
<button class="btn" onclick="testStandardAPI()">测试标准API</button>
<button class="btn" onclick="tryInputFileCapture()" style="background: #FF9800;">📷 尝试文件捕获</button>
<button class="btn" onclick="initCustomBottomCamera()" style="background: #FF5722; font-weight: bold;">🔧
自定义摄像头</button>
<button class="btn" onclick="initBottomLevelCamera()" style="background: #4CAF50; font-weight: bold;">
底层摄像头</button>
<button class="btn" onclick="startSimpleWebCamera()">🎥 简单摄像头</button>
<button class="btn" onclick="tryRealTimeCapture()">📹 实时视频流</button>
<button class="btn" onclick="detectAllAPIs()">🔍 深度API检测</button>
<button class="btn" onclick="clearLog()">清空日志</button>
<div class="video-container">
<video id="testVideo" autoplay muted playsinline style="display: none;"></video>
<div id="videoPlaceholder">点击按钮测试摄像头</div>
</div>
<div id="logArea" class="log-area"></div>
<div style="text-align: center; margin: 20px 0;">
<a href="mobile_client.html" class="btn btn-blue">🚁 返回移动终端</a>
</div>
<script>
let currentStream = null;
function log(message) {
const logArea = document.getElementById('logArea');
const timestamp = new Date().toLocaleTimeString();
logArea.innerHTML += `${timestamp} - ${message}<br>`;
logArea.scrollTop = logArea.scrollHeight;
console.log(message);
}
function clearLog() {
document.getElementById('logArea').innerHTML = '';
}
function showVideo(stream) {
const video = document.getElementById('testVideo');
video.srcObject = stream;
video.style.display = 'block';
document.getElementById('videoPlaceholder').style.display = 'none';
currentStream = stream;
log('✅ 摄像头启动成功!');
}
function testBaiduCamera() {
log('🔍 测试百度浏览器专用方法...');
const constraints = {
video: { facingMode: 'environment' },
audio: false
};
// 方法1: 检查百度浏览器特殊对象
if (window.external) {
log('📱 发现window.external对象尝试百度专用API...');
// 尝试百度浏览器的专用方法
const baiduMethods = [
'GetUserMedia',
'getUserMedia',
'requestUserMedia',
'getCameraAccess',
'openCamera'
];
for (let method of baiduMethods) {
if (window.external[method]) {
log(`发现百度方法: external.${method}`);
try {
if (method === 'GetUserMedia') {
window.external.GetUserMedia(JSON.stringify(constraints), showVideo, (err) => {
log(`百度${method}失败: ${err}`);
});
return;
} else {
window.external[method](constraints, showVideo, (err) => {
log(`百度${method}失败: ${err.message || err}`);
});
return;
}
} catch (e) {
log(`百度${method}调用异常: ${e.message}`);
}
}
}
}
// 方法2: 尝试标准但可能隐藏的webkitGetUserMedia
if (navigator.webkitGetUserMedia) {
log('📱 发现webkitGetUserMedia尝试调用...');
navigator.webkitGetUserMedia(constraints, showVideo, (err) => {
log('❌ webkitGetUserMedia失败: ' + err.message);
tryAdvancedMethods();
});
return;
}
tryAdvancedMethods();
}
function tryAdvancedMethods() {
log('🔍 尝试高级检测方法...');
// 方法1: 检查所有可能的全局对象
const globalObjects = [
'BaiduBrowser',
'baiduBrowser',
'BAIDU_BROWSER',
'BDBrowser',
'bdBrowser',
'UcBrowser',
'ucBrowser',
'QQBrowser',
'qqBrowser'
];
for (let objName of globalObjects) {
if (window[objName]) {
log(`🎯 发现全局对象: ${objName}`);
const obj = window[objName];
for (let prop in obj) {
if (prop.toLowerCase().includes('camera') ||
prop.toLowerCase().includes('media') ||
prop.toLowerCase().includes('getusermedia')) {
log(` - 发现方法: ${objName}.${prop}`);
}
}
// 尝试常见的方法名
const methods = ['getUserMedia', 'getCamera', 'requestCamera', 'openCamera'];
for (let method of methods) {
if (obj[method] && typeof obj[method] === 'function') {
log(`🚀 尝试调用: ${objName}.${method}`);
try {
obj[method]({
video: { facingMode: 'environment' },
audio: false
}, showVideo, (err) => {
log(`${objName}.${method}失败: ${err.message || err}`);
});
return;
} catch (e) {
log(`${objName}.${method}异常: ${e.message}`);
}
}
}
}
}
// 方法2: 检查document对象的特殊方法
log('🔍 检查document对象...');
if (document.getCameraAccess) {
log('发现document.getCameraAccess');
try {
document.getCameraAccess(showVideo, (err) => {
log('document.getCameraAccess失败: ' + err);
});
return;
} catch (e) {
log('document.getCameraAccess异常: ' + e.message);
}
}
// 方法3: 尝试创建video元素并设置src为摄像头
log('🔍 尝试直接设置video元素...');
const video = document.getElementById('testVideo');
try {
// 某些浏览器支持直接设置src为摄像头设备
video.src = 'camera:';
video.play();
log('尝试camera:协议');
setTimeout(() => {
if (video.videoWidth > 0) {
log('✅ camera:协议成功!');
video.style.display = 'block';
document.getElementById('videoPlaceholder').style.display = 'none';
}
}, 2000);
} catch (e) {
log('camera:协议失败: ' + e.message);
}
// 方法4: 使用input file + capture
log('🔍 尝试input file capture方法...');
tryInputFileCapture();
}
function tryInputFileCapture() {
log('📷 创建文件输入捕获...');
const input = document.createElement('input');
input.type = 'file';
input.accept = 'image/*,video/*';
input.capture = 'camera';
input.style.position = 'absolute';
input.style.top = '-1000px';
document.body.appendChild(input);
input.onchange = function (e) {
const file = e.target.files[0];
if (file) {
log('✅ 用户选择了文件: ' + file.name);
if (file.type.startsWith('image/')) {
const url = URL.createObjectURL(file);
const img = document.createElement('img');
img.src = url;
img.style.maxWidth = '100%';
img.style.maxHeight = '100%';
const container = document.querySelector('.video-container');
container.innerHTML = '';
container.appendChild(img);
log('✅ 图片显示成功');
} else if (file.type.startsWith('video/')) {
const url = URL.createObjectURL(file);
const video = document.getElementById('testVideo');
video.src = url;
video.style.display = 'block';
document.getElementById('videoPlaceholder').style.display = 'none';
log('✅ 视频显示成功');
}
}
document.body.removeChild(input);
};
input.onerror = function () {
log('❌ 文件输入失败');
document.body.removeChild(input);
};
// 自动触发文件选择
setTimeout(() => {
input.click();
log('📱 已触发摄像头文件选择(某些浏览器会直接打开摄像头)');
}, 1000);
}
// 实时视频流捕获
function tryRealTimeCapture() {
log('📹 尝试启动真正的实时视频流...', 'info');
// 首先尝试标准方法
if (tryStandardVideoStream()) {
return;
}
// 如果标准方法失败,尝试百度浏览器专用方法
if (tryBaiduVideoStream()) {
return;
}
// 最后尝试模拟实时流
log('🔄 启动模拟实时视频流...', 'info');
startSimulatedVideoStream();
}
function tryStandardVideoStream() {
if (!navigator.mediaDevices || !navigator.mediaDevices.getUserMedia) {
log('❌ 标准视频流API不可用', 'warning');
return false;
}
log('🎯 尝试标准getUserMedia...', 'info');
const constraints = {
video: {
facingMode: 'environment',
width: { ideal: 640 },
height: { ideal: 480 }
},
audio: false
};
navigator.mediaDevices.getUserMedia(constraints)
.then(stream => {
log('✅ 标准视频流获取成功!', 'success');
setupVideoStream(stream);
})
.catch(error => {
log(`❌ 标准视频流失败: ${error.message}`, 'error');
});
return true;
}
function tryBaiduVideoStream() {
if (!window.external) {
log('❌ window.external不可用', 'warning');
return false;
}
log('🎯 尝试百度浏览器视频流...', 'info');
const methods = ['GetUserMedia', 'getUserMedia', 'startCamera'];
for (let method of methods) {
if (window.external[method]) {
log(`发现external.${method},尝试获取视频流...`, 'info');
try {
const constraints = {
video: { facingMode: 'environment' },
audio: false
};
if (method === 'GetUserMedia') {
window.external.GetUserMedia(
JSON.stringify(constraints),
(stream) => {
log('✅ 百度视频流获取成功!', 'success');
setupVideoStream(stream);
},
(error) => {
log(`百度${method}失败: ${error}`, 'warning');
}
);
} else {
window.external[method](
constraints,
(stream) => {
log('✅ 百度视频流获取成功!', 'success');
setupVideoStream(stream);
},
(error) => {
log(`百度${method}失败: ${error.message || error}`, 'warning');
}
);
}
return true;
} catch (e) {
log(`百度${method}调用异常: ${e.message}`, 'warning');
}
}
}
return false;
}
function setupVideoStream(stream) {
const video = document.getElementById('testVideo');
try {
if (stream instanceof MediaStream) {
video.srcObject = stream;
} else {
video.src = URL.createObjectURL(stream);
}
video.style.display = 'block';
document.getElementById('videoPlaceholder').style.display = 'none';
currentStream = stream;
video.onloadedmetadata = () => {
log(`✅ 视频流已加载: ${video.videoWidth}x${video.videoHeight}`, 'success');
startFrameCapture(video);
};
video.play();
} catch (e) {
log(`❌ 视频流设置失败: ${e.message}`, 'error');
startSimulatedVideoStream();
}
}
function startFrameCapture(video) {
log('🎬 开始实时帧捕获...', 'success');
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
let frameCount = 0;
const captureFrame = () => {
if (!currentStream) return;
canvas.width = video.videoWidth || 640;
canvas.height = video.videoHeight || 480;
ctx.drawImage(video, 0, 0, canvas.width, canvas.height);
frameCount++;
if (frameCount % 30 === 0) { // 每30帧输出一次日志
log(`📊 已捕获 ${frameCount}`, 'info');
}
// 继续下一帧
requestAnimationFrame(captureFrame);
};
// 开始捕获
requestAnimationFrame(captureFrame);
}
function startSimulatedVideoStream() {
log('🎭 启动模拟实时视频流...', 'info');
log('💡 这将通过连续拍照来模拟视频流效果', 'info');
const video = document.getElementById('testVideo');
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
let captureCount = 0;
let isRunning = true;
// 创建停止按钮
const stopBtn = document.createElement('button');
stopBtn.textContent = '⏹️ 停止模拟流';
stopBtn.className = 'btn';
stopBtn.style.background = '#f44336';
stopBtn.style.margin = '10px';
stopBtn.onclick = () => {
isRunning = false;
stopBtn.remove();
log('🛑 模拟视频流已停止', 'info');
};
document.querySelector('.video-container').appendChild(stopBtn);
const captureNext = () => {
if (!isRunning) return;
const input = document.createElement('input');
input.type = 'file';
input.accept = 'image/*';
input.capture = 'camera';
input.style.position = 'absolute';
input.style.top = '-1000px';
document.body.appendChild(input);
input.onchange = (e) => {
const file = e.target.files[0];
if (file && file.type.startsWith('image/')) {
captureCount++;
const reader = new FileReader();
reader.onload = (event) => {
const img = new Image();
img.onload = () => {
// 更新video元素显示
canvas.width = img.width;
canvas.height = img.height;
ctx.drawImage(img, 0, 0);
const dataURL = canvas.toDataURL('image/jpeg', 0.8);
video.style.backgroundImage = `url(${dataURL})`;
video.style.backgroundSize = 'contain';
video.style.backgroundRepeat = 'no-repeat';
video.style.backgroundPosition = 'center';
video.style.display = 'block';
document.getElementById('videoPlaceholder').style.display = 'none';
log(`📸 更新帧 ${captureCount}`, 'success');
// 快速连续拍摄
if (isRunning) {
setTimeout(captureNext, 800); // 0.8秒间隔
}
};
img.src = event.target.result;
};
reader.readAsDataURL(file);
}
document.body.removeChild(input);
};
input.onerror = () => {
log('❌ 拍照失败', 'error');
document.body.removeChild(input);
if (isRunning) {
setTimeout(captureNext, 2000); // 失败后等2秒重试
}
};
// 延迟一下再触发,避免过于频繁
setTimeout(() => {
if (isRunning) {
input.click();
}
}, 100);
};
log('📱 点击拍照来开始模拟视频流...', 'info');
captureNext();
}
function testStandardAPI() {
log('🔧 测试标准MediaDevices API...');
if (!navigator.mediaDevices) {
log('❌ navigator.mediaDevices 不存在');
return;
}
if (!navigator.mediaDevices.getUserMedia) {
log('❌ navigator.mediaDevices.getUserMedia 不存在');
return;
}
navigator.mediaDevices.getUserMedia({
video: { facingMode: 'environment' },
audio: false
}).then(showVideo).catch(err => {
log('❌ 标准API失败: ' + err.message);
});
}
// 深度API检测
function detectAllAPIs() {
log('🔍 开始深度API检测...');
// 检查navigator对象的所有属性
log('📋 Navigator对象属性:');
for (let prop in navigator) {
try {
if (prop.toLowerCase().includes('camera') ||
prop.toLowerCase().includes('media') ||
prop.toLowerCase().includes('getuser') ||
prop.toLowerCase().includes('device')) {
log(` - navigator.${prop}: ${typeof navigator[prop]}`);
}
} catch (e) { }
}
// 检查window对象的特殊属性
log('🌐 Window对象特殊属性:');
const specialProps = [
'external', 'BaiduBrowser', 'baiduBrowser', 'BAIDU_BROWSER',
'UcBrowser', 'ucBrowser', 'QQBrowser', 'qqBrowser',
'webkit', 'moz', 'ms', 'o'
];
for (let prop of specialProps) {
if (window[prop]) {
log(` - window.${prop}: ${typeof window[prop]}`);
if (typeof window[prop] === 'object') {
for (let subProp in window[prop]) {
try {
if (subProp.toLowerCase().includes('camera') ||
subProp.toLowerCase().includes('media') ||
subProp.toLowerCase().includes('getuser')) {
log(` * ${prop}.${subProp}: ${typeof window[prop][subProp]}`);
}
} catch (e) { }
}
}
}
}
// 检查document对象
log('📄 Document对象方法:');
for (let prop in document) {
try {
if (prop.toLowerCase().includes('camera') ||
prop.toLowerCase().includes('media') ||
prop.toLowerCase().includes('getuser')) {
log(` - document.${prop}: ${typeof document[prop]}`);
}
} catch (e) { }
}
log('🔍 深度检测完成');
}
// 简单的Web摄像头接口
function startSimpleWebCamera() {
log('🎥 启动简单Web摄像头接口...', 'info');
// 尝试最基本的方法
if (navigator.mediaDevices && navigator.mediaDevices.getUserMedia) {
log('📹 尝试基础getUserMedia...', 'info');
navigator.mediaDevices.getUserMedia({
video: { facingMode: 'environment' },
audio: false
})
.then(stream => {
log('✅ 摄像头启动成功!', 'success');
displaySimpleVideo(stream);
})
.catch(error => {
log(`摄像头失败: ${error.message}`, 'error');
startSimpleFileMode();
});
return;
}
// 尝试老式API
const getUserMedia = navigator.getUserMedia ||
navigator.webkitGetUserMedia ||
navigator.mozGetUserMedia ||
navigator.msGetUserMedia;
if (getUserMedia) {
log('📹 尝试老式getUserMedia...', 'info');
getUserMedia.call(navigator, {
video: { facingMode: 'environment' },
audio: false
},
stream => {
log('✅ 老式API成功!', 'success');
displaySimpleVideo(stream);
},
error => {
log(`老式API失败: ${error.message}`, 'error');
startSimpleFileMode();
});
return;
}
log('❌ 无摄像头API可用启动文件模式', 'warning');
startSimpleFileMode();
}
function displaySimpleVideo(stream) {
const video = document.getElementById('testVideo');
video.srcObject = stream;
video.style.display = 'block';
video.play();
document.getElementById('videoPlaceholder').style.display = 'none';
currentStream = stream;
log('🎬 实时视频已开始显示', 'success');
// 添加停止按钮
const stopBtn = document.createElement('button');
stopBtn.textContent = '⏹️ 停止摄像头';
stopBtn.className = 'btn';
stopBtn.style.background = '#f44336';
stopBtn.style.margin = '10px';
stopBtn.onclick = () => {
if (currentStream) {
currentStream.getTracks().forEach(track => track.stop());
currentStream = null;
}
video.srcObject = null;
video.style.display = 'none';
document.getElementById('videoPlaceholder').style.display = 'block';
stopBtn.remove();
log('🛑 摄像头已停止', 'info');
};
document.querySelector('.video-container').appendChild(stopBtn);
}
function startSimpleFileMode() {
log('📷 启动简单文件拍照模式...', 'info');
// 创建拍照按钮
const photoBtn = document.createElement('button');
photoBtn.textContent = '📸 拍照';
photoBtn.className = 'btn';
photoBtn.style.background = '#2196F3';
photoBtn.style.margin = '10px';
photoBtn.style.fontSize = '16px';
photoBtn.style.padding = '12px 20px';
let photoCount = 0;
photoBtn.onclick = () => {
const input = document.createElement('input');
input.type = 'file';
input.accept = 'image/*';
input.capture = 'camera';
input.style.display = 'none';
input.onchange = (e) => {
const file = e.target.files[0];
if (file) {
photoCount++;
log(`📸 拍照 ${photoCount}: ${file.name}`, 'success');
const reader = new FileReader();
reader.onload = (event) => {
const video = document.getElementById('testVideo');
video.style.backgroundImage = `url(${event.target.result})`;
video.style.backgroundSize = 'contain';
video.style.backgroundRepeat = 'no-repeat';
video.style.backgroundPosition = 'center';
video.style.display = 'block';
document.getElementById('videoPlaceholder').style.display = 'none';
log('✅ 照片已显示', 'success');
};
reader.readAsDataURL(file);
}
document.body.removeChild(input);
};
document.body.appendChild(input);
input.click();
};
document.querySelector('.video-container').appendChild(photoBtn);
log('📱 简单拍照模式已启用,点击"📸 拍照"按钮开始', 'success');
}
window.onload = function () {
log('🚀 页面加载完成');
log('UserAgent: ' + navigator.userAgent);
log('Platform: ' + navigator.platform);
// 检查可用的API
log('📋 基础API检查:');
log('- navigator.mediaDevices: ' + (navigator.mediaDevices ? '✅' : '❌'));
log('- navigator.getUserMedia: ' + (navigator.getUserMedia ? '✅' : '❌'));
log('- navigator.webkitGetUserMedia: ' + (navigator.webkitGetUserMedia ? '✅' : '❌'));
log('- navigator.mozGetUserMedia: ' + (navigator.mozGetUserMedia ? '✅' : '❌'));
log('- window.external: ' + (window.external ? '✅' : '❌'));
// 自动进行深度检测
setTimeout(() => {
detectAllAPIs();
}, 1000);
};
window.onbeforeunload = function () {
if (currentStream) {
currentStream.getTracks().forEach(track => track.stop());
}
};
// 底层实时摄像头API for 百度浏览器
async function initBottomLevelCamera() {
log('🎥 初始化底层摄像头API...', 'info');
const constraints = {
video: {
facingMode: { ideal: 'environment' },
width: { min: 320, ideal: 640, max: 1280 },
height: { min: 240, ideal: 480, max: 720 },
frameRate: { min: 15, ideal: 30, max: 60 }
},
audio: false
};
try {
// 底层获取媒体流
const stream = await acquireRealTimeStream(constraints);
if (stream) {
setupBottomLevelVideo(stream);
startBottomLevelProcessing();
return true;
}
} catch (error) {
log(`底层摄像头API失败: ${error.message}`, 'error');
return false;
}
return false;
}
async function acquireRealTimeStream(constraints) {
log('📡 获取底层媒体流...', 'info');
// 方法1: 现代MediaDevices API
if (navigator.mediaDevices && navigator.mediaDevices.getUserMedia) {
try {
log('🔄 使用MediaDevices.getUserMedia()', 'info');
const stream = await navigator.mediaDevices.getUserMedia(constraints);
log('✅ MediaDevices API成功', 'success');
return stream;
} catch (error) {
log(`MediaDevices失败: ${error.name} - ${error.message}`, 'warning');
}
}
// 方法2: 传统getUserMedia with Promises
const legacyGetUserMedia = navigator.getUserMedia ||
navigator.webkitGetUserMedia ||
navigator.mozGetUserMedia ||
navigator.msGetUserMedia;
if (legacyGetUserMedia) {
try {
log('🔄 使用传统getUserMedia', 'info');
return await new Promise((resolve, reject) => {
legacyGetUserMedia.call(navigator, constraints, resolve, reject);
});
} catch (error) {
log(`传统API失败: ${error.message}`, 'warning');
}
}
throw new Error('无可用的媒体流API');
}
function setupBottomLevelVideo(stream) {
log('🎬 设置实时视频显示...', 'info');
const video = document.getElementById('testVideo');
// 设置视频元素
video.srcObject = stream;
video.style.display = 'block';
video.autoplay = true;
video.playsInline = true;
video.muted = true;
// 等待视频元数据加载
video.onloadedmetadata = () => {
log(`📺 视频流规格: ${video.videoWidth}x${video.videoHeight}`, 'success');
video.play();
};
document.getElementById('videoPlaceholder').style.display = 'none';
currentStream = stream;
isBottomLevelActive = true;
// 添加底层控制按钮
addBottomLevelControls();
log('✅ 底层实时视频流已启动', 'success');
}
function startBottomLevelProcessing() {
log('⚡ 启动底层帧处理引擎...', 'success');
let frameCount = 0;
let lastTime = performance.now();
let fps = 0;
const processFrame = (currentTime) => {
if (!isBottomLevelActive || !currentStream) {
return;
}
// 计算FPS
const deltaTime = currentTime - lastTime;
if (deltaTime >= 1000) { // 每秒更新一次FPS
fps = Math.round((frameCount * 1000) / deltaTime);
log(`📊 底层处理: ${fps} FPS, 帧数: ${frameCount}`, 'info');
frameCount = 0;
lastTime = currentTime;
}
frameCount++;
// 请求下一帧
requestAnimationFrame(processFrame);
};
// 开始处理循环
requestAnimationFrame(processFrame);
}
function addBottomLevelControls() {
const container = document.querySelector('.video-container');
// 停止按钮
const stopBtn = document.createElement('button');
stopBtn.textContent = '⏹️ 停止底层摄像头';
stopBtn.className = 'btn';
stopBtn.style.background = '#f44336';
stopBtn.style.margin = '5px';
stopBtn.onclick = stopBottomLevelCamera;
// 信息按钮
const infoBtn = document.createElement('button');
infoBtn.textContent = '📋 摄像头信息';
infoBtn.className = 'btn';
infoBtn.style.background = '#9C27B0';
infoBtn.style.margin = '5px';
infoBtn.onclick = getBottomLevelCameraInfo;
// 质量调整按钮
const qualityBtn = document.createElement('button');
qualityBtn.textContent = '🎬 高质量模式';
qualityBtn.className = 'btn';
qualityBtn.style.background = '#FF5722';
qualityBtn.style.margin = '5px';
qualityBtn.onclick = () => adjustBottomLevelQuality(1280, 720, 60);
container.appendChild(stopBtn);
container.appendChild(infoBtn);
container.appendChild(qualityBtn);
}
function stopBottomLevelCamera() {
log('🛑 停止底层摄像头...', 'info');
isBottomLevelActive = false;
// 停止所有媒体轨道
if (currentStream) {
currentStream.getTracks().forEach(track => {
track.stop();
log(`⏹️ 停止轨道: ${track.kind} (${track.label || 'unknown'})`, 'info');
});
currentStream = null;
}
// 清理视频元素
const video = document.getElementById('testVideo');
video.srcObject = null;
video.style.display = 'none';
// 重置UI
document.getElementById('videoPlaceholder').style.display = 'block';
// 移除控制按钮
const container = document.querySelector('.video-container');
const buttons = container.querySelectorAll('.btn');
buttons.forEach(btn => {
if (btn.textContent.includes('停止底层') ||
btn.textContent.includes('摄像头信息') ||
btn.textContent.includes('高质量模式')) {
btn.remove();
}
});
log('✅ 底层摄像头已完全停止', 'success');
}
function getBottomLevelCameraInfo() {
if (!currentStream) {
log('❌ 无活跃视频流', 'warning');
return;
}
const videoTrack = currentStream.getVideoTracks()[0];
if (!videoTrack) {
log('❌ 无视频轨道', 'warning');
return;
}
try {
const settings = videoTrack.getSettings();
log('📋 底层摄像头信息:', 'info');
log(`- 分辨率: ${settings.width}x${settings.height}`, 'info');
log(`- 帧率: ${settings.frameRate} FPS`, 'info');
log(`- 设备ID: ${settings.deviceId}`, 'info');
log(`- 朝向: ${settings.facingMode || '未知'}`, 'info');
log(`- 设备标签: ${videoTrack.label || '未知'}`, 'info');
// 尝试获取能力信息
if (videoTrack.getCapabilities) {
const capabilities = videoTrack.getCapabilities();
log(`- 支持的分辨率范围: ${capabilities.width?.min || '?'}-${capabilities.width?.max || '?'} x ${capabilities.height?.min || '?'}-${capabilities.height?.max || '?'}`, 'info');
log(`- 支持的帧率范围: ${capabilities.frameRate?.min || '?'}-${capabilities.frameRate?.max || '?'} FPS`, 'info');
}
} catch (error) {
log(`获取摄像头信息失败: ${error.message}`, 'warning');
}
}
async function adjustBottomLevelQuality(width, height, frameRate) {
if (!currentStream) {
log('❌ 无活跃视频流可调整', 'warning');
return;
}
const videoTrack = currentStream.getVideoTracks()[0];
if (!videoTrack) {
log('❌ 无视频轨道可调整', 'warning');
return;
}
try {
await videoTrack.applyConstraints({
width: { ideal: width },
height: { ideal: height },
frameRate: { ideal: frameRate }
});
log(`✅ 底层视频质量已调整: ${width}x${height} @ ${frameRate}FPS`, 'success');
} catch (error) {
log(`底层质量调整失败: ${error.message}`, 'error');
}
}
// 全局变量
let isBottomLevelActive = false;
// 完全自定义的底层摄像头实现
async function initCustomBottomCamera() {
log('🔧 启动自定义底层摄像头系统...', 'info');
// 第一步尝试HTML5 Media Capture直接实现
if (await tryDirectMediaCapture()) {
return;
}
// 第二步尝试WebGL纹理摄像头
if (await tryWebGLCamera()) {
return;
}
// 第三步:创建自定义视频流接口
createCustomVideoInterface();
}
async function tryDirectMediaCapture() {
log('📹 尝试直接媒体捕获...', 'info');
try {
// 创建隐藏的媒体输入元素
const mediaInput = document.createElement('input');
mediaInput.type = 'file';
mediaInput.accept = 'video/*';
mediaInput.capture = 'camcorder'; // 直接捕获视频
mediaInput.style.position = 'absolute';
mediaInput.style.top = '-1000px';
document.body.appendChild(mediaInput);
// 创建Promise来处理文件选择
const videoFile = await new Promise((resolve) => {
mediaInput.onchange = (e) => {
const file = e.target.files[0];
if (file && file.type.startsWith('video/')) {
resolve(file);
} else {
resolve(null);
}
document.body.removeChild(mediaInput);
};
// 自动触发
setTimeout(() => mediaInput.click(), 100);
});
if (videoFile) {
log('✅ 直接媒体捕获成功!', 'success');
setupCustomVideoPlayer(videoFile);
return true;
}
} catch (error) {
log(`直接媒体捕获失败: ${error.message}`, 'warning');
}
return false;
}
async function tryWebGLCamera() {
log('🎮 尝试WebGL摄像头实现...', 'info');
try {
const canvas = document.createElement('canvas');
canvas.width = 640;
canvas.height = 480;
const gl = canvas.getContext('webgl') || canvas.getContext('experimental-webgl');
if (!gl) {
throw new Error('WebGL不可用');
}
log('✅ WebGL上下文已创建', 'success');
// 创建WebGL纹理用于视频
const texture = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, texture);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
// 插入到页面
const video = document.getElementById('testVideo');
video.style.display = 'none';
const container = document.querySelector('.video-container');
container.appendChild(canvas);
canvas.style.width = '100%';
canvas.style.maxWidth = '640px';
canvas.style.border = '2px solid #4CAF50';
document.getElementById('videoPlaceholder').style.display = 'none';
log('🎮 WebGL摄像头画布已创建', 'success');
// 启动WebGL渲染循环
startWebGLRenderLoop(gl, texture, canvas);
// 添加WebGL控制按钮
addWebGLControls(canvas);
return true;
} catch (error) {
log(`WebGL摄像头失败: ${error.message}`, 'warning');
return false;
}
}
function startWebGLRenderLoop(gl, texture, canvas) {
log('⚡ 启动WebGL渲染循环...', 'success');
let frameCount = 0;
let isActive = true;
// 创建一个简单的渐变效果模拟摄像头
const renderFrame = () => {
if (!isActive) return;
const time = Date.now() * 0.001;
frameCount++;
// 清空画布
gl.clearColor(
0.5 + 0.3 * Math.sin(time),
0.3 + 0.3 * Math.cos(time * 0.7),
0.7 + 0.3 * Math.sin(time * 0.3),
1.0
);
gl.clear(gl.COLOR_BUFFER_BIT);
// 每秒输出一次状态
if (frameCount % 60 === 0) {
log(`🎮 WebGL渲染: 帧 ${frameCount}`, 'info');
}
requestAnimationFrame(renderFrame);
};
renderFrame();
// 提供停止方法
window.stopWebGLCamera = () => {
isActive = false;
canvas.remove();
log('🛑 WebGL摄像头已停止', 'info');
document.getElementById('videoPlaceholder').style.display = 'block';
};
}
function addWebGLControls(canvas) {
const container = document.querySelector('.video-container');
const stopBtn = document.createElement('button');
stopBtn.textContent = '⏹️ 停止WebGL摄像头';
stopBtn.className = 'btn';
stopBtn.style.background = '#f44336';
stopBtn.style.margin = '5px';
stopBtn.onclick = () => {
if (window.stopWebGLCamera) {
window.stopWebGLCamera();
stopBtn.remove();
}
};
container.appendChild(stopBtn);
}
function setupCustomVideoPlayer(videoFile) {
log('🎬 设置自定义视频播放器...', 'info');
const video = document.getElementById('testVideo');
const url = URL.createObjectURL(videoFile);
video.src = url;
video.style.display = 'block';
video.autoplay = true;
video.loop = true;
video.muted = true;
video.controls = true;
document.getElementById('videoPlaceholder').style.display = 'none';
video.onloadedmetadata = () => {
log(`📺 自定义视频: ${video.videoWidth}x${video.videoHeight}`, 'success');
startCustomVideoProcessing(video);
};
video.onerror = (e) => {
log(`视频播放错误: ${e.message}`, 'error');
};
// 添加自定义控制
addCustomVideoControls(video, url);
}
function startCustomVideoProcessing(video) {
log('⚡ 启动自定义视频处理...', 'success');
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
canvas.width = video.videoWidth || 640;
canvas.height = video.videoHeight || 480;
let frameCount = 0;
let isProcessing = true;
const processFrame = () => {
if (!isProcessing || video.paused || video.ended) return;
// 绘制当前帧
ctx.drawImage(video, 0, 0, canvas.width, canvas.height);
// 简单的图像处理效果
const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
applyCustomFilter(imageData);
ctx.putImageData(imageData, 0, 0);
frameCount++;
if (frameCount % 30 === 0) {
log(`🎬 自定义处理: 帧 ${frameCount}`, 'info');
}
requestAnimationFrame(processFrame);
};
processFrame();
// 提供停止方法
window.stopCustomVideo = () => {
isProcessing = false;
log('🛑 自定义视频处理已停止', 'info');
};
}
function applyCustomFilter(imageData) {
const data = imageData.data;
// 简单的颜色滤镜效果
for (let i = 0; i < data.length; i += 4) {
// 增强蓝色通道
data[i + 2] = Math.min(255, data[i + 2] * 1.2);
// 轻微的对比度调整
data[i] = Math.min(255, (data[i] - 128) * 1.1 + 128);
data[i + 1] = Math.min(255, (data[i + 1] - 128) * 1.1 + 128);
}
}
function addCustomVideoControls(video, url) {
const container = document.querySelector('.video-container');
const stopBtn = document.createElement('button');
stopBtn.textContent = '⏹️ 停止自定义视频';
stopBtn.className = 'btn';
stopBtn.style.background = '#f44336';
stopBtn.style.margin = '5px';
stopBtn.onclick = () => {
video.pause();
video.src = '';
video.style.display = 'none';
URL.revokeObjectURL(url);
document.getElementById('videoPlaceholder').style.display = 'block';
if (window.stopCustomVideo) window.stopCustomVideo();
stopBtn.remove();
log('🛑 自定义视频已停止', 'info');
};
container.appendChild(stopBtn);
}
function createCustomVideoInterface() {
log('🎨 创建自定义视频接口...', 'info');
const container = document.querySelector('.video-container');
// 创建自定义画布
const canvas = document.createElement('canvas');
canvas.width = 640;
canvas.height = 480;
canvas.style.width = '100%';
canvas.style.maxWidth = '640px';
canvas.style.border = '2px solid #2196F3';
canvas.style.background = '#000';
const ctx = canvas.getContext('2d');
// 插入画布
document.getElementById('testVideo').style.display = 'none';
document.getElementById('videoPlaceholder').style.display = 'none';
container.appendChild(canvas);
log('✅ 自定义视频接口已创建', 'success');
// 启动自定义渲染
startCustomRendering(ctx, canvas);
// 添加接口控制
addCustomInterfaceControls(canvas);
}
function startCustomRendering(ctx, canvas) {
log('🎯 启动自定义渲染引擎...', 'success');
let frameCount = 0;
let isRendering = true;
const render = () => {
if (!isRendering) return;
const time = Date.now() * 0.001;
frameCount++;
// 清空画布
ctx.fillStyle = '#000';
ctx.fillRect(0, 0, canvas.width, canvas.height);
// 绘制动态模拟摄像头画面
ctx.fillStyle = '#333';
ctx.fillRect(50, 50, canvas.width - 100, canvas.height - 100);
// 动态圆圈
ctx.fillStyle = `hsl(${(time * 50) % 360}, 70%, 50%)`;
ctx.beginPath();
ctx.arc(
canvas.width / 2 + 100 * Math.cos(time),
canvas.height / 2 + 50 * Math.sin(time * 2),
30,
0,
Math.PI * 2
);
ctx.fill();
// 显示信息
ctx.fillStyle = '#fff';
ctx.font = '16px Arial';
ctx.fillText(`自定义摄像头 - 帧: ${frameCount}`, 20, 30);
ctx.fillText(`时间: ${new Date().toLocaleTimeString()}`, 20, 50);
// 每秒输出状态
if (frameCount % 60 === 0) {
log(`🎯 自定义渲染: ${frameCount}`, 'info');
}
requestAnimationFrame(render);
};
render();
// 提供停止方法
window.stopCustomRendering = () => {
isRendering = false;
canvas.remove();
log('🛑 自定义渲染已停止', 'info');
document.getElementById('videoPlaceholder').style.display = 'block';
};
}
function addCustomInterfaceControls(canvas) {
const container = document.querySelector('.video-container');
const stopBtn = document.createElement('button');
stopBtn.textContent = '⏹️ 停止自定义接口';
stopBtn.className = 'btn';
stopBtn.style.background = '#f44336';
stopBtn.style.margin = '5px';
stopBtn.onclick = () => {
if (window.stopCustomRendering) {
window.stopCustomRendering();
stopBtn.remove();
}
};
// 截图按钮
const captureBtn = document.createElement('button');
captureBtn.textContent = '📸 截图';
captureBtn.className = 'btn';
captureBtn.style.background = '#4CAF50';
captureBtn.style.margin = '5px';
captureBtn.onclick = () => {
const dataURL = canvas.toDataURL('image/png');
const link = document.createElement('a');
link.download = `custom_camera_${Date.now()}.png`;
link.href = dataURL;
link.click();
log('📸 自定义截图已保存', 'success');
};
container.appendChild(stopBtn);
container.appendChild(captureBtn);
}
</script>
</body>
</html>