Come prevenire l'errore "La richiesta play () è stata interrotta da una chiamata a pause ()"?


107

Ho creato un sito web in cui se l'utente fa clic suona un suono. Per evitare che il suono si sovrapponesse, ho dovuto aggiungere il codice:

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

Ma questo causa l'errore: The play() request was interrupted by a call to pause()
apparire ogni volta che l'evento sonoro viene attivato subito dopo un altro trigger. I suoni continuano a funzionare bene, ma voglio evitare che questo messaggio di errore compaia costantemente. Qualche idea?


è un errore o più di un avviso?
dandavis

È un errore, ecco l'errore completo: Uncaught (in promise) DOMException: La richiesta play () è stata interrotta da una chiamata a pause ().
Owen M

7
è un bug più recente, non preoccuparti: bugs.chromium.org/p/chromium/issues/detail?id=593273
dandavis

3
La soluzione semplice per questo è semplicemente non chiamare la riproduzione o la pausa uno dopo l'altro. Utilizza invece gli eventi multimediali per determinare quando mettere in pausa o riprodurre. Esempio: onCanPlay(). Tutte queste risposte sono sporche truffe.
Martin Dawson

1
Ho avuto il problema opposto. n.play() Allora ci ho provato n.pause(). Per questo, questo articolo sui Web Fundamentals descrive la soluzione. tl; dr:n.play().then(() => { n.pause()})
totymedli

Risposte:


84

Ho riscontrato anche questo problema di recente: potrebbe essere una condizione di gara tra play()e pause(). Sembra che ci sia un riferimento a questo problema o qualcosa di correlato qui .

Come sottolinea @Patrick , pausenon restituisce una promessa (o altro), quindi la soluzione di cui sopra non funzionerà. Sebbene MDN non abbia documenti pause(), nella bozza WC3 per Media Elements , dice:

media.pause ()

Imposta l'attributo paused su true, caricando la risorsa multimediale se necessario.

Quindi si potrebbe anche controllare l' pausedattributo nel callback di timeout.

Sulla base di questa ottima risposta SO , ecco un modo per verificare se il video è (o non è) veramente riprodotto, in modo da poter attivare in sicurezza una riproduzione () senza l'errore.

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

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

Altrimenti, la risposta di @Patrick dovrebbe funzionare.


1
Come dice @ regency-software, il metodo pause non restituisce una promessa (non ci sono documenti su .pause ()), ma solo "undefined". Quindi questa soluzione può essere applicata solo nei casi play () e poi pause ().
Splact

Grazie per le informazioni - ho aggiornato la mia risposta per riflettere ciò che il software @regency ha pubblicato, che era corretto, e aveva anche una soluzione funzionante :-).
JohnnyCoder

come sei arrivato a 150ms come tempo di attesa? sembra essere un problema di Chrome: bugs.chromium.org/p/chromium/issues/detail?id=593273
connect2krish

Vedere la risposta di @Regency Software per il motivo del timeout. Ho ampliato la loro risposta, che era una buona soluzione. Inoltre, la mia risposta fa riferimento al collegamento che hai fornito all'inizio della risposta.
JohnnyCoder

Questo non funzionerebbe per lo streaming di video. Se ho inizializzato il video WebRTC, questo fa ancora eccezione alla console.
Stepan Yakovenko

70

Dopo ore di ricerca e lavoro, ho trovato la soluzione perfetta .

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

Dopodiché, puoi attivare la riproduzione / pausa il più velocemente possibile, funzionerà correttamente .


1
È così che ho risolto il problema. Penso che questa sia una soluzione molto migliore rispetto a fare affidamento su un timeout
Malcor

@Malcor Grazie, questo risolverà il problema per tutti
Nebojsa Sapic

Alcuni potrebbero, per favore, mettere una taglia su questa risposta per ottenerla più velocemente?
AddingColor

1
Perché non è contrassegnata come risposta corretta? Sicuramente questa è una soluzione, non un trucco con timeout.
jeron-diovis

15
Perché sono necessarie due variabili? Non sto dicendo che questa sia una cattiva soluzione. Sto solo cercando di farcela.
benevolentprof

20

Ho riscontrato questo problema e ho un caso in cui dovevo premere pause () e poi play (), ma quando uso pause (). Then () divento indefinito.

Ho scoperto che se avessi iniziato a giocare 150 ms dopo la pausa, il problema veniva risolto. (Si spera che Google risolva presto)

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

//Avoid the Promise Error
setTimeout(function () {      
   playerMP3.play();
}, 150);

Se intendi i 150 ms? Ho provato alcuni valori diversi e ho deciso su questo per il mio scopo. Stavo prendendo di mira Chrome e Android e si adattava alle esigenze della mia app. Potrebbe essere più basso.
Patrick,

3
quindi è un valore quasi casuale in quanto non è correlato a nient'altro che al tuo impl. grazie per il chiarimento !
y_nk

Sono curioso di usare lo stesso elemento per riprodurre più suoni? Ciò spiegherebbe il problema del carico; un suono è ancora caricato mentre si cerca di riprodurne un altro attraverso lo stesso elemento. Un ritardo "potrebbe" fermarlo anche se è una soluzione complicata e ritardi più lunghi / più brevi potrebbero essere utilizzati per una migliore / migliore esperienza. Cancellare e ricreare elementi al volo è stato il mio SOP che salta una serie di perdite di audio / video e memoria.
albero


8
Le durate arbitrarie di setTimeout non dovrebbero essere usate per risolvere le race condition.
Gadget Blaster


5

Provalo

n.pause();
n.currentTime = 0;
var nopromise = {
   catch : new Function()
};
(n.play() || nopromise).catch(function(){}); ;


2

A seconda di quanto complessa desideri la tua soluzione, questo può essere utile:

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

Questo è un po 'eccessivo, ma aiuta quando si gestisce una promessa in scenari unici.


2

Le soluzioni proposte qui non hanno funzionato per me o erano troppo grandi, quindi stavo cercando qualcos'altro e ho trovato la soluzione proposta da @dighan su bountysource.com/issues/

Quindi ecco il codice che ha risolto il mio problema:

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

Genera ancora un errore nella console, ma almeno il video è in riproduzione :)


1

Forse una soluzione migliore per questo come ho capito. Spec dice come citato da @JohnnyCoder:

media.pause ()

Imposta l'attributo paused su true, caricando la risorsa multimediale se necessario.

-> caricandolo

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

indica lo stato di disponibilità del supporto HAVE_ENOUGH_DATA = 4

Fondamentalmente carica il video solo se non è già caricato. Si è verificato un errore menzionato per me, perché il video non è stato caricato. Forse meglio che usare un timeout.


1

rimossi tutti gli errori: (dattiloscritto)

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

1

Con il live streaming stavo affrontando lo stesso problema. e la mia soluzione è questa. Dal tag video html assicurati di rimuovere "autoplay" e usa questo codice qui sotto per giocare.

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
Ottima soluzione !!
Junaid Mukhtar

1

Chrome restituisce una promessa nelle versioni più recenti. Altrimenti, semplicemente:

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

1

Ho lo stesso problema, finalmente risolvo con:

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

1

Questo pezzo di codice è stato risolto per me!

Codice modificato di @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

L'ho risolto con un po 'di codice qui sotto:

Quando vuoi giocare, usa quanto segue:

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

Allo stesso modo, quando vuoi mettere in pausa:

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

video_play.on('canplay', function() {
  video_play.trigger('pause');
});

0

Ecco un'altra soluzione se il motivo è che il download del video è molto lento e il video non è stato bufferizzato:

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


0

Sembra che molti programmatori abbiano riscontrato questo problema. una soluzione dovrebbe essere abbastanza semplice. elemento multimediale di ritorno Promisedalle azioni così

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

dovrebbe fare il trucco


0

ecco una soluzione dal blog di 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

Tutti i nuovi video di supporto del browser devono essere riprodotti automaticamente con l'audio solo disattivato, quindi per favore metti qualcosa del genere

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

L'URL del video deve corrispondere allo stato SSL se il tuo sito è in esecuzione con https, anche l'URL del video deve essere in https e lo stesso per HTTP


0

La soluzione più pulita e semplice :

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

0

Motivo uno: chiamare la pausa senza attendere che la promessa di gioco si risolva

troppe risposte per questo scenario, quindi mi limiterò a fare riferimento al miglior documento per quel problema:

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

Motivo due: chiamare il gioco quando la scheda non è focalizzata

In questo caso il browser potrebbe interrompere la playchiamatapause quando la scheda non è attiva. per risparmiare risorse per la scheda attiva.

Quindi potresti semplicemente aspettare che la scheda sia focalizzata prima di chiamare il gioco:


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

Mi sono imbattuto nello stesso problema e l'ho risolto aggiungendo dinamicamente l' autoplayattributo anziché utilizzare play(). In questo modo il browser ha capito di giocare senza incappare nella condizione di gara.


-1

Cercando di riprodurre in loop un video a riproduzione automatica chiamando play()quando finisce, la soluzione del timeout non ha funzionato per me (per quanto lungo sia il timeout).

Ma ho scoperto che clonando / sostituendo il video con jQuery quando terminava, si sarebbe riprodotto correttamente.

Per esempio:

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

e

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

Utilizzo Chrome 54.0.2840.59 (64 bit) / OS X 10.11.6


-1

Penso che abbiano aggiornato il video html5 e deprecato alcuni codec. Ha funzionato per me dopo aver rimosso i codec.

Nell'esempio seguente:

<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

Quando vedi un errore con Uncaught (in promise)Questo significa solo che devi gestire la promessa con una .catch()In questo caso, .play()restituisce una promessa. Puoi decidere se vuoi registrare un messaggio, eseguire del codice o non fare nulla, ma finché hai .catch()l'errore scomparirà.

var n = new Audio();
n.pause();
n.currentTime = 0;
n.play().catch(function(e) {
  // console.log('There was an error', e);
});

non restituisce una promessa
Martin Dawson

@MartinMazzaDawson Restituisce una promessa. Se guardi il commento chiarificatore di xxx nella domanda, dice "È un errore, ecco l'errore completo: DOMException non rilevata (in promessa): la richiesta play () è stata interrotta da una chiamata a pause ()".
seantomburke

-5

Ho usato un trucco per contrastare questo problema. Definisci una variabile globale var audio;

e nel controllo funzionale

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

e nella funzione di arresto

audio.pause();
audio = undefined;

quindi la prossima chiamata di audio.play , l'audio sarà pronto da '0' currentTime

ero solito

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

ma non ha funzionato. Grazie.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.