Пожалуйста, может кто-нибудь просветить меня, почему это дважды отправляет запрос POST? NB: Перед отправкой второго запроса (те же самые данные) существует задержка примерно в 2 минуты.
Код использует GetUserMedia() для отображения веб-канала пользователя и немедленно копирует снимок на холст. Затем он получает URL-адрес данных, прежде чем использовать jquery для отправки его на сервер. Проблема, с которой я сталкиваюсь, заключается в том, что второй почтовый запрос выполняется с теми же данными примерно через две минуты после первого. Я не могу, на всю жизнь, выяснить причину проблемы - пожалуйста, помогите!
// The width and height of the captured photo. We will set the
// width to the value defined here, but the height will be
// calculated based on the aspect ratio of the input stream.
var width = 320; // We will scale the photo width to this
var height = 0; // This will be computed based on the input stream
// |streaming| indicates whether or not we're currently streaming
// video from the camera. Obviously, we start at false.
var streaming = false;
// The various HTML elements we need to configure or control. These
// will be set by the startup() function.
var video = null;
var canvas = null;
var track = null;
var video = document.getElementById('video');
var canvas = document.getElementById('canvas');
var photo = document.getElementById('photo');
var kill = null;
// get feed
navigator.mediaDevices.getUserMedia({ video: true, audio: false })
.then(function (stream) {
video.srcObject = stream;
track = stream.getTracks()[0]; // if only one media track
video.play();
})
.catch(function (err) {
console.info("An error occurred: " + err);
});
video.addEventListener('canplay', function (ev) { // when recieving stream
if (kill === null) {
if (!streaming) {
height = video.videoHeight / (video.videoWidth / width);
if (isNaN(height)) { // // Firefox currently has a bug
height = width / (4 / 3);
}
video.setAttribute('width', width); // make sure video and canvas html is correct size
video.setAttribute('height', height);
canvas.setAttribute('width', width);
canvas.setAttribute('height', height);
streaming = true;
ev.preventDefault();
var context = canvas.getContext('2d');
if (width && height) {
canvas.width = width;
canvas.height = height;
context.drawImage(video, 0, 0, width, height);
// !!!!!!!!!!!!!!!!!!!
$.post('/recv', {
img: canvas.toDataURL()
});
track.stop();
kill = 1;
ev.target.removeEventListener(ev.type, arguments.callee);
return false;
// !!!!!!!!!!!!!!!!!!!
}
} else {
return;
}
}
}, false);<!DOCTYPE html>
<html>
<head>
<title>webrtc snapper</title>
<script src = "https://code.jquery.com/jquery-3.4.1.min.js"
integrity = "sha256-CSXorXvZcTkaix6Yvo6HppcZGetbYMGWSFlBw8HfCJo = " crossorigin = "anonymous"></script>
</head>
<body>
<div class = "camera">
<video id = "video"></video>
</div>
<canvas id = "canvas">
</canvas>
<div class = "output">
<img id = "photo">
</div>
<script src = "capture.js">
</script>
</body>
</html>Спасибо за ваш комментарий. Пожалуйста, не могли бы вы указать предлагаемые изменения, поскольку я не совсем уверен, что вы имеете в виду. вызов API GUM завершается до того, как прослушиватель событий будет добавлен в «видео» ... Я новичок в js, я, вероятно, сделал что-то нелепое.
Поместите прослушиватель событий в then из getUserMedia.
Никакой разницы...
Это просто закомментированная область, которая вызывается дважды, или это больше похоже на то, что весь addEventListener запускается дважды?
Я не совсем уверен. К вашему сведению, я клонировал код с github, связанный с эта страница, так что он должен быть довольно хорошим. Оказывается, даже когда я возвращаюсь к исходному коду с единственным изменением, заключающимся в добавлении сообщения jquery, оно все равно отправляется дважды.
Что произойдет, если вы используете событие loadedmetadata вместо canplay?
проблема в .POST... Неважно, что отправлено, оно всегда дублируется:/
Логически это может произойти только в том случае, если canplay имеет дублирующую привязку, она запускается снова (до отмены привязки) или если снова вызывается вся страница. Показанный код не показывает ни одну из этих ситуаций, поэтому, скорее всего, он находится где-то еще в кодовой базе. Используя инструменты разработчика Chrome, проверьте элемент video, перейдите на вкладку «Прослушиватели событий» и посмотрите, что связано с canplay.
На данный момент это вся кодовая база, кроме простой серверной части expressjs для получения того, что xhr'd в / recv ... Я проверил инструменты chrome dev, и на вкладке прослушивателей событий ничего не отображается. Одна вещь, которая может быть полезной, заключается в том, что сразу после второго запроса к «/recv» на консоли Chrome отображается сообщение об ошибке, которое гласит: «jquery-3.4.1.min.js:2 POST локальный хост: 3000/прием net:: ОШИБКА_EMPTY_RESPONSE"



![Безумие обратных вызовов в javascript [JS]](https://i.imgur.com/WsjO6zJb.png)


Событие canplay сложное, оно может воспроизводиться более одного раза, например, при изменении текущего времени браузеру нужно загрузить больше данных либо из кеша, либо из сети. Это может вызвать событие canplay.
Для ее решения есть два пути.
Один - как сказано в ответе выше, использовать флаг. Я не поддерживаю это, так как смешивание с флагами может усложнить ваш код, сделать его трудным для чтения и обслуживания.
Второй - отписаться от события:
video.bind('canplay', function (ev) {
// your logic ....
video.unbind('canplay')
}
Начиная с jquery 3.0 и далее:
video.on('canplay', function (ev) {
// your logic ....
video.off('canplay')
}
Приведенный выше код чище, его легче читать и поддерживать.
В конце концов, вам решать, что лучше всего соответствует вашим потребностям.
Спасибо за ваш ответ: до того, как я написал здесь, я сделал тот же вывод, поэтому приятно видеть, что мы на одной странице. Я реализовал предложение с флагом, потому что это казалось самым быстрым вариантом для кого-то вроде меня с такими ограниченными знаниями JS. Пожалуйста, посмотрите на переменную kill - я неправильно ее реализовал? Спасибо :)
Хорошо, я добавил "video.removeEventListener('canplay', function(){return});" в строке, следующей за .POST, и я все еще испытываю ту же проблему с дублированием. ('off' и 'unbind' не могут быть применены - я думаю, это потому, что видео не было выбрано с помощью jquery... Я полагаю, что removeeventlistener выполняет ту же работу?...)
Да, в дело пойдет, попробуй :)
В приведенном выше коде он уже есть как ev.target.removeEventListener(ev.type, arguments.callee); Проблема была на моем сервере. Тем не менее, спасибо за вашу публикацию.
после получения POST. Кажется, я забыл добавить это, и это вызывало репост JQUERY (или что-то подобное, потому что секция post в экспрессе вызывалась дважды).
Спасибо за ваши ответы.
Возможно, вам нужно добавить
Promiseк своим функциям, поскольку вы настраиваете свой слушатель, прежде чем закончить выполнение функцииgetUserMedia(). Поэтому ваша функция слушателя запускается снова и снова после завершения выполненияgetUserMedia().