浏览器拍照
上面一篇是利用mediaStream获取视频流,通过视频流截图实现拍照功能,但是问题是像素和画质较差,对于有较高要求的场景不能满足,因此继续搜索相关资料 ,这方面的权威肯定是MDN,因此在MDN上搜素能够实现拍照的API,终于在搜素过程中发现了一个实验性功能,只有chromium webkit内核的浏览器才支持该功能。
ImageCapture() constructor
这个就是本次要讨论的功能,这个API能够调用相机实现拍照,也是从mediaStream中获取视屏流,但是这个是为了显示当前传感器中的内容,同时通过这个函数可以获取到视频支持的一些参数
navigator.mediaDevices.getUserMedia({video: true})
.then(mediaStream => {
document.querySelector('video').srcObject = mediaStream
const track = mediaStream.getVideoTracks()[0];
imageCapture = new ImageCapture(track);
})
.catch(error => console.log(error));
ImageCapture
ImageCapture的接口如下:
[Exposed=Window]
interface ImageCapture {
constructor(MediaStreamTrack videoTrack);
Promise<Blob> takePhoto(optional PhotoSettings photoSettings = {});
Promise<PhotoCapabilities> getPhotoCapabilities();
Promise<PhotoSettings> getPhotoSettings();
Promise<ImageBitmap> grabFrame();
readonly attribute MediaStreamTrack track;
};
对于拍照来说需要获取的是当前摄像头能够支持的图像以及摄像头设置
PhotoCapabilities
该Promise方法能够返回浏览器支持的返回图像的长宽,红眼消除功能,以及补光模式
dictionary PhotoCapabilities {
RedEyeReduction redEyeReduction;
MediaSettingsRange imageHeight;
MediaSettingsRange imageWidth;
sequence<FillLightMode> fillLightMode;
};
PhotoSetting
获取到相机能够提供的图像参数后,就可以设置需要的图像设置了
dictionary PhotoSettings {
FillLightMode fillLightMode;
double imageHeight;
double imageWidth;
boolean redEyeReduction;
};
MediaSettingsRange
图像能够提供的参数类型
dictionary MediaSettingsRange {
double max;
double min;
double step;
};
RedEyeReduction
enum RedEyeReduction {
"never",
"always",
"controllable"
};
FillLightMode
enum FillLightMode {
"auto",
"off",
"flash"
};
MediaStreamTrack
这个是浏览器中关于音视频的一个重要api,它能够获取到当前浏览器下所有的支持的音视频设备,详细信息可以查看mdn,主要是能够提供关于视屏流的一些参数信息,接口如下:
- MediaTrackCapabilities dictionary
partial dictionary MediaTrackCapabilities {
sequence<DOMString> whiteBalanceMode;
sequence<DOMString> exposureMode;
sequence<DOMString> focusMode;
MediaSettingsRange exposureCompensation;
MediaSettingsRange exposureTime;
MediaSettingsRange colorTemperature;
MediaSettingsRange iso;
MediaSettingsRange brightness;
MediaSettingsRange contrast;
MediaSettingsRange saturation;
MediaSettingsRange sharpness;
MediaSettingsRange focusDistance;
MediaSettingsRange pan;
MediaSettingsRange tilt;
MediaSettingsRange zoom;
boolean torch;
};
- MediaTrackSettings dictionary
调用getSetting()能够返回的设置信息
partial dictionary MediaTrackSettings {
DOMString whiteBalanceMode;
DOMString exposureMode;
DOMString focusMode;
sequence<Point2D> pointsOfInterest;
double exposureCompensation;
double exposureTime;
double colorTemperature;
double iso;
double brightness;
double contrast;
double saturation;
double sharpness;
double focusDistance;
double pan;
double tilt;
double zoom;
boolean torch;
};
TakePhoto(PhotoSetting?)
调用该接口,会进行图像采集(拍照),如果不传递PhotoSetting,将会使用默认设置,如果传递上面设置的参数,相机将会拍出设置的图像,包含Exif信息(如果支持的话)-该方法能够实现全分辨率拍照
- 如果成功,则会返回一个blob对象
demo
<html>
<body>
<video autoplay></video>
<img>
<div>
<input id="pan" title="Pan" type="range" disabled />
<label for="pan">Pan</label>
</div>
<div>
<input id="tilt" title="Tilt" type="range" disabled />
<label for="tilt">Tilt</label>
</div>
<div>
<input id="zoom" title="Zoom" type="range" disabled />
<label for="zoom">Zoom</label>
</div>
<script>
let imageCapture;
async function getMedia() {
try {
const stream = await navigator.mediaDevices.getUserMedia({
video: {pan: true, tilt: true, zoom: true},
});
const video = document.querySelector('video');
video.srcObject = stream;
const [track] = stream.getVideoTracks();
imageCapture = new ImageCapture(track);
const capabilities = track.getCapabilities();
const settings = track.getSettings();
for (const ptz of ['pan', 'tilt', 'zoom']) {
// Check whether pan/tilt/zoom is available or not.
if (!(ptz in settings)) continue;
// Map it to a slider element.
const input = document.getElementById(ptz);
input.min = capabilities[ptz].min;
input.max = capabilities[ptz].max;
input.step = capabilities[ptz].step;
input.value = settings[ptz];
input.disabled = false;
input.oninput = async event => {
try {
// Warning: Chrome requires advanced constraints.
await track.applyConstraints({[ptz]: input.value});
} catch (err) {
console.error("applyConstraints() failed: ", err);
}
};
}
} catch (err) {
console.error(err);
}
}
async function takePhoto() {
try {
const blob = await imageCapture.takePhoto();
console.log("Photo taken: " + blob.type + ", " + blob.size + "B");
const image = document.querySelector('img');
image.src = URL.createObjectURL(blob);
} catch (err) {
console.error("takePhoto() failed: ", err);
}
}
</script>
</body>
</html>
demo2 code pen
<html>
<body>
<canvas></canvas>
<button id="stopButton">Stop frame grab</button>
<script>
async function grabFrames() {
try {
const canvas = document.querySelector('canvas');
const video = document.querySelector('video');
const stream = await navigator.mediaDevices.getUserMedia({video: true});
video.srcObject = stream;
const [track] = stream.getVideoTracks();
try {
const imageCapture = new ImageCapture(track);
stopButton.onclick = () => track.stop();
while (track.readyState == 'live') {
const imgData = await imageCapture.grabFrame();
canvas.width = imgData.width;
canvas.height = imgData.height;
canvas.getContext('2d').drawImage(imgData, 0, 0);
await new Promise(r => setTimeout(r, 1000));
}
} finally {
track.stop();
}
} catch (err) {
console.error(err);
}
}
</script>
</body>
</html>
兼容性
推荐Chrome或者chromium内核的Edge浏览器使用
参考
网络视频编解码器指南
ImageCapture() constructor
MediaStreamTrack
CodePen Home
ImageCapture: Update camera pan, tilt and zoom and takePhoto()
ImageCapture: timed grabFrame()