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?


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


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 .


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

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


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


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.


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


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.


1

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

audio.addEventListener('canplay', () => {
    audio.play();
    audio.pause();
    audio.removeEventListener('canplay');
}); 

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

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

1

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

video.src = 'xxxxx';
video.load();
setTimeout(function() {
  video.play();
}, 0);

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

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

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


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ę


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

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


0

Najczystszych i najprostsze rozwiązanie:

var p = video.play();
if (p !== undefined) p.catch(function(){});

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

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


-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


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


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

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.

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.