Как предотвратить ошибку «Запрос play () был прерван вызовом pause ()»?

108

Я сделал веб-сайт, на котором, если пользователь нажимает, воспроизводится звук. Чтобы звук не перекрывался, мне пришлось добавить код:

n.pause();
n.currentTime = 0;
n.play();

Но это вызывает ошибку: The play() request was interrupted by a call to pause()
возникает каждый раз, когда звуковое событие запускается сразу после другого запуска. Звуки по-прежнему воспроизводятся нормально, но я хочу, чтобы это сообщение об ошибке не появлялось постоянно. Любые идеи?

Оуэн М
источник
это ошибка или скорее уведомление?
dandavis
Это ошибка, вот полная ошибка: Неперехваченное (в обещании) DOMException: запрос play () был прерван вызовом pause ().
Owen M
7
это более новая ошибка, не беспокойтесь об этом: bugs.chromium.org/p/chromium/issues/detail?id=593273
dandavis
3
Простое решение для этого - просто не вызывать воспроизведение или паузу сразу после друг друга. Вместо этого используйте медиа-события, чтобы определить, когда делать паузу или играть. Пример: onCanPlay(). Все эти ответы - грязные уловки.
Мартин Доусон
1
У меня была противоположная проблема. Я пытался n.play() тогда n.pause() . В этой статье, посвященной основам Интернета, описано решение. tl; dr:n.play().then(() => { n.pause()})
totymedli 05

Ответы:

86

Недавно я столкнулся с этой проблемой - это может быть состояние гонки между play()и pause(). Похоже , что есть ссылка на этот вопрос, или что - то связанный здесь .

Как указывает @Patrick , pauseне возвращает обещание (или что-то еще), поэтому вышеуказанное решение не сработает. Хотя у MDN нет документации pause(), в черновике WC3 для Media Elements сказано:

media.pause ()

Устанавливает для атрибута paused значение true, при необходимости загружая медиаресурс.

Таким образом, можно также проверить pausedатрибут в обратном вызове тайм-аута.

Основываясь на этом замечательном ответе SO , вот способ проверить, действительно ли видео воспроизводится (или нет), поэтому вы можете безопасно запустить play () без ошибки.

var isPlaying = video.currentTime > 0 && !video.paused && !video.ended 
    && video.readyState > 2;

if (!isPlaying) {
  video.play();
}

В противном случае, @Patrick «S ответ должен работать.

JohnnyCoder
источник
1
Как говорит @regency-software, метод pause не возвращает обещание (нет документации по .pause ()), а просто «undefined». Таким образом, это решение может применяться только в случаях play (), затем pause ().
Splact
Спасибо за информацию - я обновил свой ответ, чтобы отразить, какое программное обеспечение @regency опубликовало, что было правильным, и у него также было рабочее решение :-).
JohnnyCoder
как вы пришли к 150 мс в качестве времени ожидания? похоже, проблема с хромом: bugs.chromium.org/p/chromium/issues/detail?id=593273
connect2krish
См @Regency Software «s ответ на причину тайм - аута. Я расширил их ответ, что было хорошим решением. Кроме того, мой ответ ссылается на ссылку, которую вы указали в начале ответа.
JohnnyCoder
Это не сработает для потокового видео. Если я инициализирую видео WebRTC, это все равно является исключением для консоли.
Степан Яковенко
70

После нескольких часов поиска и работы я нашел идеальное решение .

// Initializing values
var isPlaying = true;

// On video playing toggle values
video.onplaying = function() {
    isPlaying = true;
};

// On video pause toggle values
video.onpause = function() {
    isPlaying = false;
};

// Play video function
function playVid() {      
    if (video.paused && !isPlaying) {
        video.play();
    }
} 

// Pause video function
function pauseVid() {     
    if (!video.paused && isPlaying) {
        video.pause();
    }
}

После этого вы можете как можно быстрее переключать воспроизведение / паузу , все будет работать правильно .

Небойша Сапич
источник
1
Вот как я решил проблему. Я считаю, что это гораздо лучшее решение, чем полагаться на тайм-аут
Малкор
@Malcor Спасибо, это решит проблему для всех
Небойша Сапич
Не могли бы вы назначить награду за этот ответ, чтобы быстрее проголосовать за него?
AddingColor
1
Почему он не отмечен как правильный ответ? Однозначно это решение, а не взлом с таймаутами.
jeron-diovis
15
Зачем нужны две переменные? Я не говорю, что это плохое решение. Просто пытаюсь разобраться.
benevolentprof
20

Я столкнулся с этой проблемой, и у меня был случай, когда мне нужно было нажать pause (), затем play (), но при использовании pause (). Then () я получаю undefined.

Я обнаружил, что если я начал играть через 150 мс после паузы, это решило проблему. (Надеюсь, Google скоро исправит)

playerMP3.volume = 0;
playerMP3.pause();

//Avoid the Promise Error
setTimeout(function () {      
   playerMP3.play();
}, 150);
Патрик
источник
Если вы имеете в виду 150 мс? Я попробовал несколько разных значений и остановился на этом для моей цели. Я ориентировался на Chrome и Android, и это соответствовало потребностям моего приложения. Может быть и ниже.
Патрик
3
так что это почти случайное значение, не связанное ни с чем, кроме вашего impl. Спасибо за разъяснения !
y_nk
Мне любопытно, используете ли вы один и тот же элемент для воспроизведения нескольких звуков? Это объяснило бы проблему с нагрузкой; один звук все еще загружен при попытке воспроизвести другой звук через тот же элемент. Задержка «могла» остановить это, хотя это нехорошее исправление, и для лучшего / лучшего опыта можно использовать более длинные / короткие задержки. Очистка и воссоздание элементов на лету - это моя СОП, которая пропускает ряд утечек аудио / видео и памяти.
tree
1
похоже на хромовую ошибку: bugs.chromium.org/p/chromium/issues/detail?id=593273
connect2krish
8
Произвольные длительности setTimeout не должны использоваться для решения условий гонки.
Gadget Blaster
5

попытайся

n.pause();
n.currentTime = 0;
var nopromise = {
   catch : new Function()
};
(n.play() || nopromise).catch(function(){}); ;
жест
источник
3

Мне помогло это решение:

n.cloneNode(true).play();
Дмитрий Ковганов
источник
2

В зависимости от того, насколько сложным вы хотите ваше решение, это может быть полезно:

var currentPromise=false;    //Keeps track of active Promise

function playAudio(n){
    if(!currentPromise){    //normal behavior
        n.pause();
        n.currentTime = 0;
        currentPromise = n.play();    //Calls play. Will store a Promise object in newer versions of chrome;
                                      //stores undefined in other browsers
        if(currentPromise){    //Promise exists
            currentPromise.then(function(){ //handle Promise completion
                promiseComplete(n);
            });
        }
    }else{    //Wait for promise to complete
        //Store additional information to be called
        currentPromise.calledAgain = true;
    }
}

function promiseComplete(n){
    var callAgain = currentPromise.calledAgain;    //get stored information
    currentPromise = false;    //reset currentPromise variable
    if(callAgain){
        playAudio(n);
    }
}

Это немного перебор, но помогает при обработке Promise в уникальных сценариях.

Синий мелок
источник
2

Предлагаемые здесь решения либо не сработали для меня, либо слишком большие, поэтому я искал что-то еще и нашел решение, предложенное @dighan на bountysource.com/issues/

Итак, вот код, который решил мою проблему:

var media = document.getElementById("YourVideo");
const playPromise = media.play();
if (playPromise !== null){
    playPromise.catch(() => { media.play(); })
}

Он по-прежнему выдает ошибку в консоли, но, по крайней мере, видео воспроизводится :)

Шарпи
источник
1

Возможно, это лучшее решение, как я понял. Spec говорит, как цитируется из @JohnnyCoder:

media.pause ()

Устанавливает для атрибута paused значение true, при необходимости загружая медиаресурс.

-> загрузка

if (videoEl.readyState !== 4) {
    videoEl.load();
}
videoEl.play();

указывает состояние готовности носителя HAVE_ENOUGH_DATA = 4

Обычно загружайте видео, только если оно еще не загружено. У меня возникла упомянутая ошибка, потому что видео не было загружено. Может быть, лучше, чем использовать тайм-аут.

Кристоф Эберл
источник
1

убраны все ошибки: (машинопись)

audio.addEventListener('canplay', () => {
    audio.play();
    audio.pause();
    audio.removeEventListener('canplay');
}); 
Райанрейн
источник
1

С прямой трансляцией я столкнулся с той же проблемой. и мое исправление это. Из тега html-видео не забудьте удалить "автовоспроизведение" и использовать этот код ниже для воспроизведения.

if (Hls.isSupported()) {
            var video = document.getElementById('pgVideo');
            var hls = new Hls();
            hls.detachMedia();
            hls.loadSource('http://wpc.1445X.deltacdn.net/801885C/lft/apple/TSONY.m3u8');
            hls.attachMedia(video);
            hls.on(Hls.Events.MANIFEST_PARSED, function () {
                video.play();
            });
            hls.on(Hls.Events.ERROR, function (event, data) {
                if (data.fatal) {
                    switch (data.type) {
                        case Hls.ErrorTypes.NETWORK_ERROR:
                            // when try to recover network error
                            console.log("fatal network error encountered, try to recover");
                            hls.startLoad();
                            break;
                        case Hls.ErrorTypes.MEDIA_ERROR:
                            console.log("fatal media error encountered, try to recover");
                            hls.recoverMediaError();
                            break;
                        default:
                            // when cannot recover
                            hls.destroy();
                            break;
                    }
                }
            });
        }
Neutro Solutions
источник
1
Отличное решение !!
Джунаид Мухтар
1

Chrome возвращает Promise в новейших версиях. В противном случае просто:

        n.pause();
        n.currentTime = 0;
        setTimeout(function() {n.play()}, 0);
Дэвид
источник
1

У меня такая же проблема, наконец, я решаю:

video.src = 'xxxxx';
video.load();
setTimeout(function() {
  video.play();
}, 0);
лишайник
источник
1

Этот фрагмент кода исправлен для меня!

Измененный код @JohnnyCoder

HTML:

 <video id="captureVideoId" muted width="1280" height="768"></video>
                <video controls id="recordedVideoId" muted width="1280" 
 style="display:none;" height="768"></video>

JS:

  var recordedVideo = document.querySelector('video#recordedVideoId');
  var superBuffer = new Blob(recordedBlobs, { type: 'video/webm' });
  recordedVideo.src = window.URL.createObjectURL(superBuffer);
  // workaround for non-seekable video taken from
  // https://bugs.chromium.org/p/chromium/issues/detail?id=642012#c23
  recordedVideo.addEventListener('loadedmetadata', function () {
    if (recordedVideo.duration === Infinity) {
        recordedVideo.currentTime = 1e101;
        recordedVideo.ontimeupdate = function () {
            recordedVideo.currentTime = 0;
            recordedVideo.ontimeupdate = function () {
                delete recordedVideo.ontimeupdate;
                var isPlaying = recordedVideo.currentTime > 0 && 
     !recordedVideo.paused && !recordedVideo.ended && 
      recordedVideo.readyState > 2;
                if (isPlaying) {
                    recordedVideo.play();
                }
            };
        };
       }
      });
Элиотис
источник
1

Я исправил это с помощью кода ниже:

Если вы хотите поиграть, используйте следующее:

var video_play = $('#video-play');
video_play.on('canplay', function() {
 video_play.trigger('play');
});

Точно так же, когда вы хотите сделать паузу:

var video_play = $('#video-play');
video_play.trigger('pause');

video_play.on('canplay', function() {
  video_play.trigger('pause');
});
Дюк Нгуен
источник
0

Вот еще одно решение, если причина в том, что ваше видео загружается очень медленно и видео не буферизуется:

if (videoElement.state.paused) { videoElement.play(); } else if (!isNaN(videoElement.state.duration)) { videoElement.pause(); }

Тайский
источник
0

Похоже, с этой проблемой сталкивались многие программисты. решение должно быть довольно простым. медиа-элемент возвращается Promiseиз действий, поэтому

n.pause().then(function(){
    n.currentTime = 0;
    n.play();
})

должен сделать трюк

Pery Mimon
источник
0

вот решение из блога googler:

var video = document.getElementById('#video')
var promise = video.play()
//chrome version 53+
if(promise){
    promise.then(_=>{
        video.pause()
    })
}else{
    video.addEventListener('canplaythrough', _=>{
        video.pause()
    }, false)
}
стекРыба
источник
0

Все новые браузеры поддерживают автоматическое воспроизведение видео с отключением звука, поэтому, пожалуйста, поставьте что-нибудь вроде этого

<video autoplay muted="muted" loop id="myVideo">
  <source src="https://w.r.glob.net/Coastline-3581.mp4" type="video/mp4">
</video>

URL-адрес видео должен соответствовать статусу SSL, если ваш сайт работает с https, тогда URL-адрес видео также должен быть в https и то же самое для HTTP.

Шобхит Верма
источник
0

Самое чистое и простое решение:

var p = video.play();
if (p !== undefined) p.catch(function(){});
лапин
источник
0

Причина первая - вызов паузы без ожидания разрешения игры

слишком много ответов для этого сценария, поэтому я просто буду ссылаться на лучший документ по этой проблеме:

https://developers.google.com/web/updates/2017/06/play-request-was-interrupted

Причина вторая - вызов play, когда вкладка не сфокусирована

В этом случае браузер может прервать playвызов, pauseкогда вкладка не в фокусе. в целях экономии ресурсов для активной вкладки.

Таким образом, вы можете просто подождать, пока вкладка будет сфокусирована, прежде чем вызывать play:


async function waitForTabFocus() {
  return new Promise((resolve, reject) => {
    const onFocus = () => { resolve(); window.removeEventListener('focus', onFocus) };
    window.addEventListener('focus', onFocus)
  })
}
if (!document.hasFocus()) await this.waitForTabFocus();
videoEl.play();
jony89
источник
-1

Я столкнулся с той же проблемой и решил ее, динамически добавив autoplayатрибут, а не используя play(). Таким образом, браузер научился играть, не попадая в состояние гонки.

Яннис Ад
источник
-1

При попытке зациклить автоматическое воспроизведение видео, вызывая play()его по окончании, обходной путь тайм-аута у меня не сработал (каким бы длинным ни был тайм-аут).

Но я обнаружил, что при клонировании / замене видео на jQuery, когда оно закончится, оно будет правильно зацикливаться.

Например:

<div class="container">
  <video autoplay>
    <source src="video.mp4" type="video/mp4">
  </video>
</div>

и

$(document).ready(function(){
  function bindReplay($video) {
    $video.on('ended', function(e){
      $video.remove();
      $video = $video.clone();
      bindReplay($video);
      $('.container').append($video);
    });
  }
  var $video = $('.container video');
  bindReplay($video);
});

Я использую Chrome 54.0.2840.59 (64-разрядный) / OS X 10.11.6

Сильвен
источник
-1

Думаю, они обновили видео в формате html5 и исключили некоторые кодеки. У меня сработало после удаления кодеков.

В приведенном ниже примере:

<video>
    <source src="sample-clip.mp4" type="video/mp4; codecs='avc1.42E01E, mp4a.40.2'">
    <source src="sample-clip.webm" type="video/webm; codecs='vp8, vorbis'"> 
</video>

    must be changed to

<video>
    <source src="sample-clip.mp4" type="video/mp4">
    <source src="sample-clip.webm" type="video/webm">
</video>

Амр
источник
-1

Когда вы видите ошибку с помощью Uncaught (in promise)This, просто означает, что вам нужно обработать обещание с помощью. .catch()В этом случае .play()возвращает обещание. Вы можете решить, хотите ли вы записать сообщение, запустить какой-то код или ничего не делать, но пока у вас есть .catch()ошибка, ошибка исчезнет.

var n = new Audio();
n.pause();
n.currentTime = 0;
n.play().catch(function(e) {
  // console.log('There was an error', e);
});
Seantomburke
источник
это не возвращает обещание
Мартин Доусон
@MartinMazzaDawson Это действительно возвращает обещание. Если вы посмотрите на поясняющий комментарий xxx в вопросе, он говорит: «Это ошибка, вот полная ошибка: Неперехваченное (в обещании) DOMException: запрос play () был прерван вызовом pause ()».
seantomburke 03
-5

Я воспользовался уловкой, чтобы решить эту проблему. Определите глобальную переменную var audio;

и в функциональной проверке

if(audio === undefined)
{
   audio = new Audio(url);
}

и в функции остановки

audio.pause();
audio = undefined;

поэтому следующий вызов audio.playаудио будет готов с текущего времени '0'

я использовал

audio.pause();
audio.currentTime =0.0; 

но это не сработало. Спасибо.

Гиппи Аулах
источник