HTML调取摄像头拍照事例代码,扩展常见直播方案
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>摄像头拍照保存</title>
<style>
*{box-sizing:border-box;margin:0;padding:0;font-family:system-ui;}
body{padding:20px;max-width:800px;margin:0 auto;}
#video{width:100%;border:1px solid #ccc;background:#000;transform:scaleX(-1);/*前置预览镜像*/}
#canvas{display:none;}
#preview{margin:15px 0;max-width:100%;border:1px solid #ccc;}
.btns{margin:15px 0;display:flex;gap:10px;flex-wrap:wrap;}
button{padding:10px 18px;font-size:16px;cursor:pointer;}
</style>
</head>
<body>
<h3>摄像头拍照</h3>
<video id="video" autoplay playsinline></video>
<canvas id="canvas"></canvas>
<div class="btns">
<button id="startBtn">开启摄像头</button>
<button id="stopBtn">关闭摄像头</button>
<button id="takeBtn">拍照</button>
<button id="saveBtn">保存图片</button>
</div>
<img id="preview" alt="预览图">
<script>
const video = document.getElementById('video');
const canvas = document.getElementById('canvas');
const ctx = canvas.getContext('2d');
const preview = document.getElementById('preview');
const startBtn = document.getElementById('startBtn');
const stopBtn = document.getElementById('stopBtn');
const takeBtn = document.getElementById('takeBtn');
const saveBtn = document.getElementById('saveBtn');
let mediaStream = null;
let imgUrl = '';
// 开启摄像头
startBtn.onclick = async () => {
try{
mediaStream = await navigator.mediaDevices.getUserMedia({
video:{facingMode:'user'}
});
video.srcObject = mediaStream;
}catch(err){
alert('无法打开摄像头,请授予摄像头权限:' + err.message);
console.error(err);
}
};
// 关闭摄像头
stopBtn.onclick = ()=>{
if(mediaStream){
mediaStream.getTracks().forEach(track=>track.stop());
mediaStream = null;
video.srcObject = null;
}
}
// 拍照,前置画面镜像翻转
takeBtn.onclick = () => {
if(!mediaStream){
alert("请先开启摄像头");
return;
}
if(!video.videoWidth){
alert("视频还未就绪,请稍等");
return;
}
canvas.width = video.videoWidth;
canvas.height = video.videoHeight;
// 水平翻转画布,拍出来和预览一样镜像
ctx.save();
ctx.scale(-1, 1);
ctx.drawImage(video, -canvas.width, 0, canvas.width, canvas.height);
ctx.restore();
imgUrl = canvas.toDataURL('image/png');
preview.src = imgUrl;
};
// 保存下载图片
saveBtn.onclick = () => {
if(!imgUrl){
alert('请先拍照');
return;
}
const a = document.createElement('a');
a.href = imgUrl;
a.download = `photo_${Date.now()}.png`;
a.click();
};
</script>
</body>
</html>
摄像头视频流怎么推送到后端,做直播视频流
网页摄像头推流到后端做直播:完整技术方案
浏览器端采集摄像头,向后端推送视频流,主流分两大类:
- WebRTC(推荐低延迟直播,实时互动,延迟<500ms)
- MediaRecorder + HTTP上传(简单HLS分片直播,延迟高,5~20秒,上手简单)
前置基础:还是用 navigator.mediaDevices.getUserMedia 获取摄像头MediaStream,和前面拍照网页是同一个媒体源。方案1:MediaRecorder + HTTP分片上传(简易直播,适合入门)
原理:浏览器拿到摄像头流,用MediaRecorder编码成小块视频片段(blob),通过fetch持续POST发给后端;后端接收片段,转封装为HLS,前端用video.js播放。
- 优点:技术栈简单,不需要复杂的信令服务器;后端用Node/Python/PHP都能写接收接口。
- 缺点:延迟高,不适合实时互动,适合监控、低要求直播。
前端核心代码片段
let mediaStream;
let mediaRecorder;
const chunkList = [];
// 获取摄像头
mediaStream = await navigator.mediaDevices.getUserMedia({
video: { facingMode: 'user', width: {ideal:1280}, height:{ideal:720} },
audio: true // 同时采集音频
});
// 实例化录制器,编码格式
mediaRecorder = new MediaRecorder(mediaStream, {
mimeType: 'video/webm;codecs=vp9,opus'
});
// 每500ms生成一块视频分片
mediaRecorder.ondataavailable = async (e) => {
if(e.data.size > 0){
const formData = new FormData();
formData.append('videoChunk', e.data);
// POST上传分片到后端接口
await fetch('/api/upload-chunk', {
method: 'POST',
body: formData
})
}
}
mediaRecorder.start(500); // 每500ms触发一次ondataavailable后端职责:
- 接收webm分片
- 把分片追加到视频文件,或者交给ffmpeg转码,生成HLS m3u8 + ts切片
- 对外提供HLS地址给播放器观看
限制:浏览器编码格式有限,大多输出webm;如果要mp4,浏览器原生MediaRecorder不支持mp4实时流式输出。
方案2:WebRTC(低延迟实时直播,首选)
WebRTC可以直接把摄像头MediaStream实时推流到后端,延迟极低。
两种部署模式:
模式A:SFU 媒体转发服务器(最常用直播方案)
浏览器(推流端) ↔ SFU服务器 ↔ 多个观看浏览器
- 浏览器采集摄像头流,通过SDP信令,把RTP音视频包发给SFU
- SFU负责接收、转发视频流给所有观众,不做转码(性能好)
- 常用开源SFU:Mediasoup、Janus、LiveKit、Pion
流程:
- 前端getUserMedia拿到mediaStream
- 创建RTCPeerConnection
- 将摄像头track添加到peer连接
- 通过websocket作为信令通道,交换SDP、ICE候选
- 建立P2P/服务器中转,RTP数据包持续推送到后端SFU
// WebRTC极简前端示例
const pc = new RTCPeerConnection({
iceServers: [{urls: 'stun:stun.l.google.com:19302'}]
});
// 将摄像头流加入peer连接
mediaStream.getTracks().forEach(track=>pc.addTrack(track, mediaStream));
// 创建offer,通过websocket发给后端信令服务器
const offer = await pc.createOffer();
await pc.setLocalDescription(offer);
// 发送offer到后端websocket信令
ws.send(JSON.stringify({type:'offer', sdp:offer.sdp}));模式B:P2P直连
浏览器直接和观看端P2P,后端只做信令转发,不转发媒体。
缺点:不适合多人直播,网络穿透失败率高,生产直播很少用。
方案3:WebRTC 推流到RTMP服务器(传统直播流)
很多后端直播服务是RTMP(Nginx+rtmp-module,SRS),浏览器本身不原生支持RTMP。
两个解决办法:
- 前端WebRTC推流 → 后端SRS/Mediasoup接收WebRTC,内部转RTMP,推到CDN。(推荐,SRS原生支持WebRTC转RTMP)
- 用wasm在浏览器内把MediaStream编码成H.264,封装RTMP,这个方案前端CPU占用很高,不推荐生产。
SRS 是国内最常用开源直播服务器:支持 WebRTC 推流、转RTMP、转HLS,部署简单。
关键概念区分
- MediaStream:摄像头原始未编码音视频帧(就是你拍照网页里的流),只能在浏览器内部使用,不能直接发给后端,必须编码打包。
- MediaRecorder:浏览器内置编码器,把MediaStream编码成webm片段。
- WebRTC RTP:实时编码,小包传输,低延迟。
- RTMP:传统直播协议,浏览器不原生支持,多用于CDN。
选型建议
- 低延迟互动直播、远程监控、实时画面:WebRTC + SRS / LiveKit
- 简单监控,允许10秒以上延迟,快速开发:MediaRecorder分片上传 + ffmpeg转HLS
- 需要对接阿里云/腾讯云直播CDN:前端WebRTC推SRS,SRS转RTMP推CDN
常见坑
- 必须HTTPS/localhost,否则getUserMedia无法打开摄像头。
- WebRTC需要信令服务器(websocket),媒体流和信令是两条独立通道。
- 带宽:1080p30视频推流,至少需要4Mbps上行带宽。
- 编码:浏览器支持H.264 / VP8 / VP9,不同浏览器支持不一样。
- 高DPI屏幕不影响视频流本身,视频流分辨率由getUserMedia的constraints指定,不是屏幕分辨率。
拓展:和你之前拍照代码的关联
getUserMedia拿到的MediaStream,既可以给video预览、canvas截图,也可以直接喂给MediaRecorder或者RTCPeerConnection做推流,同一个媒体源复用。
版权属于:Joyber
本文链接:https://blog.qqvbc.com/default/HTML.html
转载时须注明出处及本声明