Jak zapobiec błędowi „Żądanie play () zostało przerwane przez wywołanie pause ()”?

107

Zrobiłem stronę internetową, na której jeśli użytkownik kliknie, odtwarza dźwięk. Aby dźwięk nie nakładał się, musiałem dodać kod:

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

Ale to powoduje błąd: The play() request was interrupted by a call to pause()
pojawia się za każdym razem, gdy zdarzenie dźwiękowe jest wyzwalane zaraz po kolejnym wyzwoleniu. Dźwięki nadal działają dobrze, ale chcę zapobiec ciągłemu wyświetlaniu tego komunikatu o błędzie. Jakieś pomysły?

Owen M.
źródło
czy to jest błąd, czy raczej powiadomienie?
dandavis
To błąd, tutaj jest pełny błąd: Uncaught (w obietnicy) DOMException: Żądanie play () zostało przerwane przez wywołanie pause ().
Owen M
7
to nowszy błąd, nie martw się o to: bugs.chromium.org/p/chromium/issues/detail?id=593273
dandavis
3
Prostym rozwiązaniem tego problemu jest po prostu nie wywoływanie odtwarzania lub pauzowania zaraz po sobie. Zamiast tego użyj zdarzeń multimedialnych, aby określić, kiedy wstrzymać lub odtworzyć. Przykład: onCanPlay(). Wszystkie te odpowiedzi to brudne hacki.
Martin Dawson
1
Miałem odwrotny problem. Próbowałem n.play() wtedy n.pause() . W tym celu w artykule z podstaw pracy w sieci opisano rozwiązanie. tl; dr:n.play().then(() => { n.pause()})
totymedli

Odpowiedzi:

84

Niedawno spotkałem się z tym problemem - może to być stan wyścigu między play()a pause(). Wygląda na to, że jest tam odniesienie do tego problemu lub coś związanego z tym tutaj .

Jak wskazuje @Patrick , pausenie zwraca obietnicy (ani niczego), więc powyższe rozwiązanie nie zadziała. Chociaż MDN nie ma włączonej dokumentacji pause(), w wersji roboczej WC3 dla Media Elements jest napisane:

media.pause ()

Ustawia paused atrybut na true, ładując zasób multimedialny, jeśli to konieczne.

Można więc również sprawdzić pausedatrybut w wywołaniu zwrotnym limitu czasu.

W oparciu o tę świetną odpowiedź SO , oto sposób na sprawdzenie, czy wideo jest (lub nie jest) naprawdę odtwarzane, dzięki czemu można bezpiecznie uruchomić play () bez błędu.

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

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

W przeciwnym razie, @Patrick „s odpowiedź powinna działać.

JohnnyCoder
źródło
1
Jak mówi @ regency-software, metoda pause nie zwraca Promise (nie ma dokumentów na .pause ()), a jedynie „niezdefiniowane”. Więc to rozwiązanie można zastosować tylko w przypadkach play (), a następnie pause ().
Splact,
Dzięki za informacje - zaktualizowałem moją odpowiedź, aby odzwierciedlić to, co opublikowało oprogramowanie @regency, które było poprawne, a on również miał działające rozwiązanie :-).
JohnnyCoder
jak doszedłeś do 150 ms jako czasu oczekiwania? wydaje się, że jest to problem z Chrome: bugs.chromium.org/p/chromium/issues/detail?id=593273
connect2krish
Zobacz @Regency Software jest odpowiedź na przyczyny timeout. Rozszerzyłem ich odpowiedź, która była dobrym rozwiązaniem. Moja odpowiedź również odnosi się do linku, który podałeś na początku odpowiedzi.
JohnnyCoder
To nie zadziała w przypadku przesyłania strumieniowego wideo. Jeśli mam inicjalizację wideo WebRTC, nadal stanowi to wyjątek dla konsoli.
Stepan Yakovenko
70

Po godzinach poszukiwań i pracy znalazłem idealne rozwiązanie .

// 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();
    }
}

Następnie możesz przełączać odtwarzanie / pauzę tak szybko, jak to możliwe, będzie działać poprawnie .

Nebojsa Sapic
źródło
1
W ten sposób osiągnąłem problem. Uważam, że jest to znacznie lepsze rozwiązanie niż poleganie na przekroczeniu limitu czasu
Malcor
@Malcor Dziękuję, to rozwiąże problem dla wszystkich
Nebojsa Sapic
Czy ktoś mógłby wyznaczyć nagrodę za tę odpowiedź, aby przyspieszyć jej głosowanie?
AddingColor
1
Dlaczego nie jest oznaczona jako poprawna odpowiedź? Zdecydowanie jest to rozwiązanie, a nie hack z limitami czasu.
jeron-diovis
15
Dlaczego potrzebne są dwie zmienne? Nie mówię, że to złe rozwiązanie. Po prostu próbuję to zrozumieć.
benevolentprof
20

Napotkałem ten problem i mam przypadek, w którym musiałem nacisnąć pause (), a następnie grać (), ale podczas używania pause (). Then () jestem niezdefiniowany.

Okazało się, że jeśli zacząłem grać 150 ms po wstrzymaniu, rozwiązało to problem. (Mam nadzieję, że Google wkrótce naprawi)

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

//Avoid the Promise Error
setTimeout(function () {      
   playerMP3.play();
}, 150);
Patrick
źródło
Jeśli masz na myśli 150 ms? Wypróbowałem kilka różnych wartości i zdecydowałem się na to dla swojego celu. Miałem na celu Chrome i Androida i pasował do potrzeb mojej aplikacji. Może być niższa.
Patrick
3
więc jest to prawie przypadkowa wartość, ponieważ nie jest związana z niczym innym niż twój impl. Dziękuję za wyjaśnienie !
y_nk
Jestem ciekawy, czy używasz tego samego elementu do odtwarzania wielu dźwięków? To by wyjaśniało kwestię obciążenia; jeden dźwięk wciąż jest ładowany podczas próby odtworzenia innego dźwięku przez ten sam element. Opóźnienie „mogłoby” go zatrzymać, chociaż jest to niewygodne rozwiązanie, a dłuższe / krótsze opóźnienia można wykorzystać do lepszego / najlepszego doświadczenia. Czyszczenie i odtwarzanie elementów w locie to mój SOP, który pomija wiele wycieków audio / wideo i pamięci.
drzewo
1
wygląda jak błąd w Chrome: bugs.chromium.org/p/chromium/issues/detail?id=593273
connect2krish
8
Arbitralne czasy setTimeout nie powinny być używane do rozwiązywania warunków wyścigu.
Gadget Blaster
5

Spróbuj

n.pause();
n.currentTime = 0;
var nopromise = {
   catch : new Function()
};
(n.play() || nopromise).catch(function(){}); ;
gest
źródło
3

To rozwiązanie pomogło mi:

n.cloneNode(true).play();
Dmitry Kovganov
źródło
2

W zależności od tego, jak skomplikowane chcesz rozwiązanie, może to być przydatne:

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);
    }
}

To trochę przesada, ale pomaga w radzeniu sobie z obietnicą w wyjątkowych sytuacjach.

Niebieska Kredka
źródło
2

Zaproponowane tutaj rozwiązania albo u mnie nie działały, albo gdzie były za duże, więc szukałem czegoś innego i znalazłem rozwiązanie zaproponowane przez @dighan na bountysource.com/issues/

Oto kod, który rozwiązał mój problem:

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

Nadal wyrzuca błąd do konsoli, ale przynajmniej wideo jest odtwarzane :)

Sharpey
źródło
1

Może lepsze rozwiązanie tego, jak się domyśliłem. Spec mówi, jak cytowano z @JohnnyCoder:

media.pause ()

Ustawia paused atrybut na true, ładując zasób multimedialny, jeśli to konieczne.

-> ładowanie

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

wskazuje stan gotowości nośnika HAVE_ENOUGH_DATA = 4

Zasadniczo ładuj wideo tylko wtedy, gdy nie jest jeszcze załadowane. Wystąpił wspomniany błąd, ponieważ wideo nie zostało załadowane. Może lepiej niż używanie limitu czasu.

Christoph Eberl
źródło
1

usunięto wszystkie błędy: (maszynopis)

audio.addEventListener('canplay', () => {
    audio.play();
    audio.pause();
    audio.removeEventListener('canplay');
}); 
ryanrain
źródło
1

W przypadku transmisji na żywo miałem ten sam problem. i moja poprawka jest taka. Z tagu wideo html pamiętaj, aby usunąć „autoodtwarzanie” i użyć poniższego kodu do odtwarzania.

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;
                    }
                }
            });
        }
Rozwiązania Neutro
źródło
1
Świetne rozwiązanie !!
Junaid Mukhtar
1

Chrome zwraca obietnicę w najnowszych wersjach. W przeciwnym razie po prostu:

        n.pause();
        n.currentTime = 0;
        setTimeout(function() {n.play()}, 0);
David
źródło
1

Mam ten sam problem, ostatecznie rozwiązuję przez:

video.src = 'xxxxx';
video.load();
setTimeout(function() {
  video.play();
}, 0);
lichenbuliren
źródło
1

Ten fragment kodu został dla mnie naprawiony!

Zmodyfikowany kod @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();
                }
            };
        };
       }
      });
Eliotjse
źródło
1

Naprawiłem to za pomocą kodu poniżej:

Jeśli chcesz zagrać, użyj:

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

Podobnie, gdy chcesz wstrzymać:

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

video_play.on('canplay', function() {
  video_play.trigger('pause');
});
Duc Nguyen
źródło
0

Oto inne rozwiązanie, jeśli powodem jest to, że pobieranie wideo jest bardzo wolne, a wideo nie zostało zbuforowane:

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

Taysky
źródło
0

Wygląda na to, że wielu programistów napotkało ten problem. rozwiązanie powinno być dość proste. element media powraca Promisez akcji tzw

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

powinien załatwić sprawę

pery mimon
źródło
0

oto rozwiązanie z bloga Google:

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)
}
stackFish
źródło
0

Wszystkie nowe filmy obsługują przeglądarkę, aby były automatycznie odtwarzane z wyciszeniem, więc proszę umieścić coś takiego

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

Adres URL wideo powinien być zgodny ze stanem SSL, jeśli Twoja witryna korzysta z protokołu https, a następnie adres URL wideo powinien również mieć format https i taki sam dla protokołu HTTP

Shobhit Verma
źródło
0

Najczystszych i najprostsze rozwiązanie:

var p = video.play();
if (p !== undefined) p.catch(function(){});
lapin
źródło
0

Powód pierwszy - wywołanie pauzy bez czekania na rozwiązanie obietnicy gry

zbyt wiele odpowiedzi na ten scenariusz, więc odwołam się do najlepszego dokumentu dotyczącego tego problemu:

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

Powód drugi - wywołanie gry, gdy karta nie jest aktywna

W takim przypadku przeglądarka może przerwać playwywołaniepause gdy karta nie jest aktywna. w celu oszczędzania zasobów dla aktywnej karty.

Możesz więc po prostu poczekać, aż zakładka zostanie aktywowana, zanim wywołasz grę:


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
źródło
-1

Napotkałem ten sam problem i rozwiązałem go, dynamicznie dodając autoplayatrybut, zamiast używać play(). W ten sposób przeglądarka zorientowała się, że gra bez uruchamiania wyścigu.

Jannis Hell
źródło
-1

Próbując spowodować, aby automatycznie odtwarzany film zapętlał się, dzwoniąc play()po jego zakończeniu, obejście limitu czasu nie zadziałało (niezależnie od tego, jak długi jest limit czasu).

Ale odkryłem, że klonując / zastępując wideo jQuery po zakończeniu, zapętli się poprawnie.

Na przykład:

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

i

$(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);
});

Używam Chrome 54.0.2840.59 (64-bitowy) / OS X 10.11.6

Silvain
źródło
-1

Myślę, że zaktualizowali wideo HTML5 i wycofali niektóre kodeki. U mnie zadziałało po usunięciu kodeków.

W poniższym przykładzie:

<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>

Amr
źródło
-1

Kiedy widzisz błąd z Uncaught (in promise)To oznacza po prostu, że musisz obsłużyć obietnicę za pomocą .catch()W tym przypadku .play()zwraca obietnicę. Możesz zdecydować, czy chcesz zarejestrować wiadomość, uruchomić kod, czy nic nie robić, ale dopóki masz .catch()błąd, błąd zniknie.

var n = new Audio();
n.pause();
n.currentTime = 0;
n.play().catch(function(e) {
  // console.log('There was an error', e);
});
seantomburke
źródło
nie zwraca obietnicy
Martin Dawson
@MartinMazzaDawson Zwraca obietnicę. Jeśli spojrzysz na wyjaśniający komentarz xxx w pytaniu, które mówi: „To błąd, oto pełny błąd: Nieprzechwycony (obiecany) DOMException: Żądanie play () zostało przerwane przez wywołanie pause ()”.
seantomburke,
-5

Użyłem sztuczki, aby przeciwdziałać temu problemowi. Zdefiniuj zmienną globalną var audio;

i podczas kontroli działania

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

i w funkcji stop

audio.pause();
audio = undefined;

więc następne połączenie audio.play audio będzie gotowe od „0” currentTime

użyłem

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

ale to nie zadziałało. Dzięki.

Gippy Aulakh
źródło