// HTTP服务器接收来自ffmpeg的MPEG-TS流
var streamServer = http.createServer( function(request, response) {
request.on('data', function(data){
socketServer.broadcast(data);
});
request.on('end',function(){
console.log('close');
});
}).listen(STREAM_PORT);
ffmpeg -f v4l2 -framerate 25 -video_size 640x480 -i /dev/video0 -f mpegts -codec:v mpeg1video -s 640x480 -b:v 1000k -bf 0 http://127.0.0.1:8081

if (!navigator.mediaDevices || !navigator.mediaDevices.enumerateDevices) {
console.log("不支持 enumerateDevices() .");
return;
}
// 列出相机和麦克风.
navigator.mediaDevices.enumerateDevices()
.then(function (devices) {
devices.forEach(function (device) {
console.log(device.kind + ": " + device.label +
" id = " + device.deviceId);
});
})
.catch(function (err) {
console.log(err.name + ": " + err.message);
});
1. MediaDevices.getUserMedia()

var video = document.createElement('video');
var constraints = {
audio: false,
video: true
};
function successCallback(stream) {
window.stream = stream; //MediaStream对象
video.srcObject = stream;
}
function errorCallback(error) {
console.log('navigator.getUserMedia error: ', error);
}
function getMedia(constraints) {
if (window.stream) {
video.src = null;
window.stream.getVideoTracks()[0].stop();
}
navigator.mediaDevices.getUserMedia(
constraints
).then(
successCallback,
errorCallback
);
}
constraints
比如你想要使用1280x720的摄像头分辨率:
{
audio: true,
video: { width: 1280, height: 720 }
}
{
audio: true,
video: {
width: { min: 1280 },
height: { min: 720 }
}
}
{
audio: true,
video: {
width: { ideal: 1280 },
height: { ideal: 720 }
}
}
当你的设备存在多个摄像头时,可以优先选择前置:
{ audio: true, video: { facingMode: "user" } }
或者强制使用后置摄像头:
{
audio: true,
video: {
facingMode: {
exact: "environment"
}
}
}
任性的你也许只想要某个特定设备,那你需要用
deviceId
来约束,浏览器会优先获取此设备。
{ video: { deviceId: myPreferredCameraDeviceId } }
好了,以上将返回你需要的媒体设备。
常见异常抛出
var promise = navigator.mediaDevices.getUserMedia({
video: true,
audio: false
});
promise.then(function (MediaStream) {
video.srcObject = MediaStream;
}).catch(err => {
if (err.name == 'NotFoundError' || err.name == 'DeviceNotFoundError') {
// 找不到满足请求参数的媒体类型
console.log(err.name, 'require track is missing');
} else if (err.name == 'NotReadableError' || err.name == 'TrackStartError') {
// 设备已经授权,但是某个硬件、浏览器或者网页层面发生的错误导致设备无法被访问
console.error(err.name, 'webcam or mic are already in use');
} else if (err.name == 'OverconstrainedError' || err.name == 'ConstraintNotSatisfiedError') {
// 指定的要求无法被设备满足,此异常是一个类型为OverconstrainedError的对象
console.error(err.name, 'constraints can not be satisfied by avb.device');
} else if (err.name == 'NotAllowedError' || err.name == 'PermissionDeniedError') {
// 用户拒绝了浏览器实例的访问请求
console.error(err.name, 'permission denied in browser');
} else if (err.name == 'TypeError' || err.name == 'TypeError') {
// constraints对象未设置,或者都被设置为false
console.error(err.name, 'empty constraints object');
} else {
// 其他错误
console.error(err.name, 'other errors');
}
});
2. MediaDevices.getDisplayMedia()
navigator.getDisplayMedia({ video: true })
.then(stream => {
// 成功回调的流,将它赋给video元素;
videoElement.srcObject = stream;
}, error => {
console.log("Unable to acquire screen capture", error);
});
constraints
异常
3. getUserMedia与getDisplayMedia比较
其他
function drawImage(drawImageRate) {
context.drawImage(video, 0, 0, width, height);
let base64Image = canvas.toDataURL('image/jpeg', 1); //格式为image/jpeg或image/webp时,从0到1的区间定制图的质量
... //压缩等处理后
window.rws.send(JSON.stringify({ image: base64Image }));
}
window.drawInter = setInterval(drawImage, drawImageRate = 1000);
关于canvas画布转换成img图像,除了base64外,也可以选择Blob格式,因为是二进制的,对后端更加友好。
canvas.toBlob(callback, mimeType, qualityArgument)
这里是关于抽帧的小demo(https://chhxin.github.io/webrtc-demo/)