cбор видео отзывов в браузере без WebRTC
Игорь Шеко
Head of frontend @ Voximplant
with Osoka.io
UGC
Что это такое. Причем тут видео в общем и фронтенд в частности
WebRTC
Что это такое, как оно работает внутри и почему не подходит для UGC
MediaDevices API
Как работать с камерой и микрофоном для записи, с какими проблемами можно столкнуться
# TOC
MediaRecorder API
Непосредственно про запись видео на устройстве. Как это сделать лучше всего и что может пойти не так
Speech Recognition API
Бонусная секция про встроенные бесплатные API распознавания речи в браузере.
UGC это:
# CHAPTER 1
Но сегодня мы поговорим в основном про видео
# CHAPTER 1
# CHAPTER 1
https://nesomnoi.jewish-museum.ru/
# CHAPTER 1
https://notforme.introvert.moscow/
https://osoka.io/
# CHAPTER 2
# CHAPTER 2
User A
User B
Offer SDP
Answer SDP
Media
# CHAPTER 2
https://caniuse.com/rtcpeerconnection
# CHAPTER 2
Качество становится жертвой риалтайму
# CHAPTER 2
Качество становится жертвой риалтайму
30 FPS
20 FPS
3 FPS
# CHAPTER 3
MediaDevices API
# CHAPTER 3
https://caniuse.com/mediarecorder
# CHAPTER 3
const constraints = {
audio: true,
video: true
}
stream = await navigator.mediaDevices.getUserMedia(constraints);
https://webrtc.github.io/samples/
# CHAPTER 3
При стандартном запросе:
# CHAPTER 3
const constraints = {audio: true, video: true}
stream = await navigator.mediaDevices.getUserMedia(constraints);
const devices = await navigatorLink.mediaDevices.enumerateDevices();
deviceInfo.forEach(info => {
const capabilities = info.getCapabilities();
})
{
"aspectRatio": {
"max": 1920,
"min": 0.000925925925925926
},
"deviceId": "8278d69560f5f3bd8b4c87d5900cf041c58380f0738bcac3bf8cb03204bfdac8",
"facingMode": [],
"frameRate": {
"max": 30.000030517578125,
"min": 1
},
"groupId": "9f9a61500830cb6fbbfdecdb5da077ab3dd469b47c26ca767fe89666d2b7ee23",
"height": {
"max": 1080,
"min": 1
},
"resizeMode": [
"none",
"crop-and-scale"
],
"width": {
"max": 1920,
"min": 1
}
}
# CHAPTER 3
const constraints = {
audio: {
deviceId: '7889879785cdf767f986cd865900cf041c58380f0738bcac3bf8cb03204bfdac8',
autoGainControl: { exact: true },
echoCancellation: { exact: true },
noiseSuppression: { exact: true }
},
video: {
deviceId: '8278d69560f5f3bd8b4c87d5900cf041c58380f0738bcac3bf8cb03204bfdac8',
aspectRatio: { exact: 1 },
width: { ideal: 1280 },
height: { exact: 720 }
}
}
stream = await navigator.mediaDevices.getUserMedia(constraints);
# CHAPTER 4
MediaRecorder API
# CHAPTER 4
https://caniuse.com/mediarecorder
# CHAPTER 4
# CHAPTER 4
let recordedChunks = [];
let recorder = new MediaRecorder(mediaStream);
recorder.start();
recorder.ondataavailable = (e) => {
recordedChunks.push(e.data);
}
recorder.onstop = () => {
const blob = new Blob(recordedChunks, { type:recorder.mimeType });
sendBlobToServer(blob);
}
setTimeout(() => { recorder.stop() }, 15000);
# CHAPTER 4
let recordedChunks = [];
let recorder = new MediaRecorder(mediaStream);
recorder.start();
recorder.ondataavailable = (e) => {
recordedChunks.push(e.data);
}
recorder.onstop = () => {
const blob = new Blob(recordedChunks, { type:recorder.mimeType });
sendBlobToServer(blob);
}
setTimeout(() => { recorder.stop() }, 15000);
# CHAPTER 4
Chrome | Firefox | macOS Safari | iOS Safari | |
---|---|---|---|---|
Контейнер | webm | webm | mp4 | mp4 |
Видео кодек | VP8/VP9/H264/AV1 | VP8 | H264 | H264 |
Аудио кодек | Opus @ 48kHz | Vorbis @ 44.1 kHz | Stereo AAC @ 48kHz | Stereo AAC @ 44.1kHz |
# CHAPTER 4
function getSupportedMimeTypes() {
const possibleTypes = [
'video/webm;codecs=h264',
'video/webm;codecs=vp9',
'video/webm',
'video/mp4'
];
return possibleTypes.filter(mimeType => {
return MediaRecorder.isTypeSupported(mimeType);
});
}
# CHAPTER 4
const TIMESLICE = 5000;
recorder.start(TIMESLICE);
recorder.ondataavailable = (e) => {
sendPartToServer(e.data);
}
# CHAPTER 4
# CHAPTER 5
Bonus
# CHAPTER 5
# CHAPTER 5
https://caniuse.com/speech-recognition
# CHAPTER 5
Минусы
# CHAPTER 5
window.SpeechRecognition = window.SpeechRecognition ||window.webkitSpeechRecognition || null;
if(!window.SpeechRecognition) return;
let speech = new window.SpeechRecognition();
speech.onresult = function(event) {
if (event.results.length > 0) {
console.log(event.results[0][0].transcript);
}
}
speech.start();
# CHAPTER 5
let speech = new window.SpeechRecognition();
speech.continuous = true;
speech.lang = 'RU-ru';
speech.interimResults = true;
speech.maxAlternatives = 10;
const window.SpeechGrammarList = window.SpeechGrammarList
|| window.webkitSpeechGrammarList
|| null;
if(window.SpeechGrammarList){
const grammars = new SpeechGrammarList();
grammars.addFromString('старт');
grammars.addFromString('стоп');
speech.grammars = grammars;
}
# CHAPTER 5
let speech = new window.SpeechRecognition();
speech.onaudiostart = ()=>{};
speech.onsoundstart = ()=>{};
speech.onspeechstart = ()=>{};
speech.onspeechend = ()=>{};
speech.onsoundend = ()=>{};
speech.onaudioend = ()=>{};
speech.onnomatch = ()=>{};
speech.onresult = ()=>{};
speech.onerror = ()=>{};
speech.onstart = ()=>{};
speech.onend = ()=>{};
# CHAPTER 5
let speech = new window.SpeechRecognition();
speech.onerror = (ev) => {
console.log(ev.error);
}
# CHAPTER 5
Игорь Шеко
Head of frontend @ Voximplant
telegram
github