¿Cómo evitar el error "La solicitud play () fue interrumpida por una llamada a pause ()"?

107

Creé un sitio web donde si el usuario hace clic, reproduce un sonido. Para evitar que el sonido se superponga, tuve que agregar el código:

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

Pero eso causa el error: The play() request was interrupted by a call to pause()
aparece cada vez que se activa el evento de sonido justo después de otro disparador. Los sonidos aún se reproducen bien, pero quiero evitar que este mensaje de error aparezca constantemente. ¿Algunas ideas?

Owen M
fuente
¿Es un error o más bien un aviso?
dandavis
Es un error, aquí está el error completo: Uncaught (en promesa) DOMException: La solicitud play () fue interrumpida por una llamada a pause ().
Owen M
7
es un error más nuevo, no se preocupe: bugs.chromium.org/p/chromium/issues/detail?id=593273
dandavis
3
La solución simple para esto es simplemente no llamar al juego o pausar uno después del otro. En su lugar, utilice los eventos multimedia para determinar cuándo hacer una pausa o reproducir. Ejemplo: onCanPlay(). Todas estas respuestas son trucos sucios.
Martin Dawson
1
Yo tuve el problema contrario. Intenté n.play() entonces n.pause() . Para eso, este artículo de Web Fundamentals describe la solución. tl; dr:n.play().then(() => { n.pause()})
totymedli

Respuestas:

84

También me he encontrado con este problema recientemente; esto podría ser una condición de carrera entre play()y pause(). Parece que hay una referencia a este problema o algo relacionado aquí .

Como señala @Patrick , pauseno devuelve una promesa (ni nada), por lo que la solución anterior no funcionará. Si bien MDN no tiene documentos pause(), en el borrador de WC3 para Media Elements , dice:

media.pause ()

Establece el atributo de pausa en verdadero, cargando el recurso multimedia si es necesario.

Por lo que también se puede verificar el pausedatributo en su devolución de llamada de tiempo de espera.

Con base en esta excelente respuesta SO , aquí hay una forma en que puede verificar si el video se está reproduciendo (o no) realmente, para que pueda activar una reproducción () de manera segura sin el error.

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

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

De lo contrario, la respuesta de @Patrick debería funcionar.

JohnnyCoder
fuente
1
Como dice @ regency-software, el método de pausa no devuelve una Promesa (no hay documentos en .pause ()), sino simplemente "indefinido". Por lo tanto, esta solución solo se puede aplicar en los casos play () y luego pause ().
Splact
Gracias por la información: actualicé mi respuesta para reflejar lo que publicó el software @regency, que era correcto, y él también tenía una solución funcional :-).
JohnnyCoder
¿Cómo llegaste a 150 ms como tiempo de espera? parece ser un problema de Chrome: bugs.chromium.org/p/chromium/issues/detail?id=593273
connect2krish
Vea la respuesta de @Regency Software para conocer el motivo del tiempo de espera. Amplié su respuesta, que fue una buena solución. Además, mi respuesta hace referencia al enlace que proporcionó al principio de la respuesta.
JohnnyCoder
Esto no funcionaría para la transmisión de video. Si inicializo el video WebRTC, esto aún da una excepción a la consola.
Stepan Yakovenko
70

Después de horas de búsqueda y trabajo, encontré la solución perfecta .

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

Después de eso, puede alternar reproducción / pausa tan rápido como pueda, funcionará correctamente .

Nebojsa Sapic
fuente
1
Así logré el problema. Siento que esta es una solución mucho mejor que depender de un tiempo de espera
Malcor
@Malcor Gracias, esto solucionará el problema para todos
Nebojsa Sapic
¿Podrían algunos poner una recompensa por esta respuesta para obtener una votación a favor más rápido?
AddingColor
1
¿Por qué no está marcado como respuesta correcta? Definitivamente esta es una solución, no un truco con tiempos de espera.
jeron-diovis
15
¿Por qué son necesarias dos variables? No digo que sea una mala solución. Solo intento asimilarlo.
benevolentprof
20

He encontrado este problema y tengo un caso en el que necesitaba presionar pausa () y luego reproducir () pero cuando uso pause (). Luego () me quedo indefinido.

Descubrí que si comenzaba a jugar 150 ms después de la pausa, se resolvía el problema. (Con suerte, Google lo soluciona pronto)

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

//Avoid the Promise Error
setTimeout(function () {      
   playerMP3.play();
}, 150);
Patricio
fuente
¿Si te refieres a los 150ms? Probé algunos valores diferentes y me decidí por esto para mi propósito. Apuntaba a Chrome y Android y se ajustaba a las necesidades de mi aplicación. Puede ser más bajo.
Patrick
3
por lo que es un valor casi aleatorio ya que no está relacionado con nada más que su impl. gracias por la aclaración !
y_nk
Tengo curiosidad, ¿estás usando el mismo elemento para reproducir varios sonidos? Eso explicaría el problema de la carga; un sonido aún se carga mientras intenta reproducir otro sonido a través del mismo elemento. Un retraso "podría" detenerlo, aunque es una solución complicada y los retrasos más largos / más cortos podrían usarse para una mejor / mejor experiencia. Limpiar y recrear elementos sobre la marcha ha sido mi SOP que omite una serie de pérdidas de audio / video y de memoria.
árbol
1
parece un error de Chrome: bugs.chromium.org/p/chromium/issues/detail?id=593273
connect2krish
8
Las duraciones de setTimeout arbitrarias no deben usarse para resolver condiciones de carrera.
Gadget Blaster
5

intentalo

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

Esta solución me ayudó:

n.cloneNode(true).play();
Dmitry Kovganov
fuente
2

Dependiendo de qué tan compleja desee su solución, esto puede ser útil:

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

Esto es un poco exagerado, pero ayuda cuando se maneja una Promesa en escenarios únicos.

El crayón azul
fuente
2

Las soluciones propuestas aquí no funcionaron para mí o eran demasiado grandes, así que estaba buscando algo más y encontré la solución propuesta por @dighan en bountysource.com/issues/

Entonces aquí está el código que resolvió mi problema:

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

Todavía arroja un error en la consola, pero al menos el video se está reproduciendo :)

Sharpey
fuente
1

Tal vez una mejor solución para esto, como descubrí. Spec dice como se cita de @JohnnyCoder:

media.pause ()

Establece el atributo de pausa en verdadero, cargando el recurso multimedia si es necesario.

-> cargarlo

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

indica el estado de preparación de los medios HAVE_ENOUGH_DATA = 4

Básicamente, solo cargue el video si aún no está cargado. Se produjo un error mencionado porque el video no se cargó. Quizás sea mejor que usar un tiempo de espera.

Christoph Eberl
fuente
1

eliminado todos los errores: (mecanografiado)

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

Con la transmisión en vivo, me enfrentaba al mismo problema. y mi solución es esta. Desde la etiqueta de video html, asegúrese de eliminar la "reproducción automática" y use el siguiente código para jugar.

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;
                    }
                }
            });
        }
Soluciones Neutro
fuente
1
Gran solucion !!
Junaid Mukhtar
1

Chrome devuelve una promesa en las versiones más recientes. De lo contrario, simplemente:

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

Tengo el mismo problema, finalmente lo resuelvo:

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

¡Este fragmento de código se arregló para mí!

Código modificado de @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
fuente
1

Lo he arreglado con un código a continuación:

Cuando quieras jugar, usa lo siguiente:

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

Del mismo modo, cuando quieras hacer una pausa:

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

video_play.on('canplay', function() {
  video_play.trigger('pause');
});
Duc Nguyen
fuente
0

Aquí hay otra solución si la razón es que la descarga de su video es muy lenta y el video no se ha almacenado en búfer:

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

Taysky
fuente
0

Parece que muchos programadores encontraron este problema. una solución debería ser bastante simple. retorno de elementos de medios Promisede acciones

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

debería hacer el truco

pery mimon
fuente
0

aquí hay una solución del blog de 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)
}
apilar pescado
fuente
0

Todos los nuevos videos de soporte del navegador se reproducirán automáticamente con solo estar silenciados, así que coloque algo como esto

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

La URL del video debe coincidir con el estado de SSL si su sitio se ejecuta con https, entonces la URL del video también debe estar en https y lo mismo para HTTP

Shobhit Verma
fuente
0

La solución más limpia y sencilla :

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

Razón uno: llamar a pausa sin esperar a que se resuelva la promesa de juego

demasiadas respuestas para este escenario, por lo que solo me referiré al mejor documento para ese problema:

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

Segunda razón: llamar al juego cuando la pestaña no está enfocada

En este caso, el navegador podría interrumpir el playllamando pausecuando la pestaña no está enfocada. para ahorrar recursos para la pestaña activa.

Por lo tanto, puede esperar a que la pestaña se enfoque antes de llamar al juego:


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

Me encontré con el mismo problema y lo resolví agregando dinámicamente el autoplayatributo en lugar de usarlo play(). De esa forma, el navegador se dio cuenta de que podía jugar sin encontrarse con la condición de carrera.

Jannis Hell
fuente
-1

Al intentar que un video de reproducción automática se repita llamando play()cuando finaliza, la solución de tiempo de espera no funcionó para mí (por muy largo que sea el tiempo de espera).

Pero descubrí que al clonar / reemplazar el video con jQuery cuando termina, se repite correctamente.

Por ejemplo:

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

y

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

Estoy usando Chrome 54.0.2840.59 (64 bits) / OS X 10.11.6

Silvain
fuente
-1

Creo que actualizaron el video html5 y desaprobaron algunos códecs. Me funcionó después de eliminar los códecs.

En el siguiente ejemplo:

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

Cuando ve un error con Uncaught (in promise)Esto solo significa que debe manejar la promesa con un .catch()En este caso, .play()devuelve una promesa. Puede decidir si desea registrar un mensaje, ejecutar algún código o no hacer nada, pero mientras lo tenga, .catch()el error desaparecerá.

var n = new Audio();
n.pause();
n.currentTime = 0;
n.play().catch(function(e) {
  // console.log('There was an error', e);
});
seantomburke
fuente
no devuelve una promesa
Martin Dawson
@MartinMazzaDawson Devuelve una promesa. Si miras el comentario aclaratorio de xxx en la pregunta, dice "Es un error, aquí está el error completo: No detectado (en promesa) DOMException: la solicitud play () fue interrumpida por una llamada a pause ()".
seantomburke
-5

He utilizado un truco para contrarrestar este problema. Defina una variable global var audio;

y en la verificación de funcionamiento

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

y en la función de parada

audio.pause();
audio = undefined;

por lo que la próxima llamada de audio.playaudio estará lista desde '0' currentTime

solía

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

pero no funcionó. Gracias.

Gippy Aulakh
fuente