Como evitar o erro “A solicitação play () foi interrompida por uma chamada para pausar ()”?

107

Fiz um site onde se o usuário clicar, toca um som. Para evitar que o som se sobreponha, eu tive que adicionar o código:

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

Mas isso causa o erro: The play() request was interrupted by a call to pause()
Acontece toda vez que o evento de som é acionado logo após outro acionador. O som ainda toca bem, mas quero evitar que essa mensagem de erro apareça constantemente. Alguma ideia?

Owen M
fonte
é um erro ou mais um aviso?
dandavis
É um erro, aqui está o erro completo: Não capturado (em promessa) DOMException: A solicitação play () foi interrompida por uma chamada para pause ().
Owen M
7
é um bug mais recente, não se preocupe com ele: bugs.chromium.org/p/chromium/issues/detail?id=593273
dandavis
3
A solução simples para isso é simplesmente não chamar play ou pausar um após o outro. Use os eventos de mídia para determinar quando pausar ou reproduzir. Exemplo: onCanPlay(). Todas essas respostas são truques sujos.
Martin Dawson
1
Eu tive o problema oposto. Eu tentei n.play() então n.pause() . Para isso, este artigo de Fundamentos da Web descreve a solução. tl; dr:n.play().then(() => { n.pause()})
totymedli

Respostas:

84

Também encontrei este problema recentemente - pode ser uma condição de corrida entre play()e pause(). Parece que há uma referência a esse problema ou algo relacionado aqui .

Como @Patrick aponta, pausenão retorna uma promessa (ou nada), então a solução acima não funcionará. Embora o MDN não tenha documentos pause(), no rascunho WC3 para elementos de mídia , ele diz:

media.pause ()

Define o atributo paused como true, carregando o recurso de mídia, se necessário.

Portanto, também se pode verificar o pausedatributo em seu retorno de chamada de tempo limite.

Com base nesta ótima resposta do SO , aqui está uma maneira de verificar se o vídeo está (ou não) realmente reproduzindo, para que possa acionar com segurança uma reprodução () sem o erro.

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

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

Caso contrário, a resposta de @Patrick deve funcionar.

JohnnyCoder
fonte
1
Como @ regency-software diz, o método de pausa não retorna uma promessa (não há documentos em .pause ()), mas apenas "indefinido". Portanto, essa solução pode ser aplicada apenas nos casos play () e depois pause ().
Splact
Obrigado pela informação - Eu atualizei minha resposta para refletir o que o software @regency postou, que estava correto, e ele também tinha uma solução de trabalho :-).
JohnnyCoder
como você chegou a 150ms como tempo de espera? parece ser um problema de cromo: bugs.chromium.org/p/chromium/issues/detail?id=593273
connect2krish
Consulte a resposta da @Regency Software para saber o motivo do tempo limite. Ampliei a resposta, que foi uma boa solução. Além disso, minha resposta faz referência ao link que você forneceu no início da resposta.
JohnnyCoder
Isso não funcionaria para streaming de vídeo. Se eu tiver inicializado o vídeo WebRTC, ainda haverá exceção para o console.
Stepan Yakovenko
70

Após horas de pesquisa e trabalho, encontrei a solução perfeita .

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

Depois disso, você pode alternar entre reproduzir / pausar o mais rápido possível e funcionará corretamente .

Nebojsa Sapic
fonte
1
Foi assim que resolvi o problema. Acho que esta é uma solução muito melhor do que contar com um tempo limite
Malcor
@Malcor Obrigado, isso vai resolver o problema para todos
Nebojsa Sapic
Alguém poderia, por favor, colocar uma recompensa nesta resposta para que ela seja votada mais rapidamente?
Cor
1
Por que não está marcada como resposta correta? Definitivamente, essa é uma solução, não um hack com tempo limite.
jeron-diovis
15
Por que duas variáveis ​​são necessárias? Não estou dizendo que essa seja uma solução ruim. Apenas tentando grocar.
benevolentprof
20

Eu encontrei esse problema e tive um caso em que precisava clicar em pause () e depois em play (), mas ao usar pause (). Then () fico indefinido.

Descobri que se iniciasse a reprodução 150 ms após a pausa, o problema seria resolvido. (Esperançosamente o Google corrige em breve)

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

//Avoid the Promise Error
setTimeout(function () {      
   playerMP3.play();
}, 150);
Patrick
fonte
Se você quer dizer 150ms? Tentei alguns valores diferentes e me decidi por isso. Meu objetivo era o Chrome e o Android e ele atendia às necessidades do meu aplicativo. Pode ser mais baixo.
Patrick
3
portanto, é um valor quase aleatório, pois não está relacionado a nada além do seu impl. obrigado pelo esclarecimento!
y_nk
Estou curioso para saber se você está usando o mesmo elemento para reproduzir vários sons? Isso explicaria o problema de carga; um som ainda carregado ao tentar reproduzir outro som através do mesmo elemento. Um atraso "poderia" pará-lo, embora fosse uma correção desajeitada, e atrasos mais longos / mais curtos poderiam ser usados ​​para uma experiência melhor / melhor. Limpar e recriar elementos em tempo real tem sido meu SOP, que ignora uma série de vazamentos de áudio / vídeo e memória.
árvore
1
parece um bug do Chrome: bugs.chromium.org/p/chromium/issues/detail?id=593273
connect2krish
8
Durações setTimeout arbitrárias não devem ser usadas para resolver condições de corrida.
Gadget Blaster de
5

tente

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

Esta solução me ajudou:

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

Dependendo de quão complexa você deseja sua solução, isso pode 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);
    }
}

Isso é um pouco exagerado, mas ajuda ao lidar com uma promessa em cenários únicos.

O lápis azul
fonte
2

As soluções propostas aqui não funcionaram para mim ou foram muito grandes, então eu estava procurando por outra coisa e encontrei a solução proposta por @dighan em bountysource.com/issues/

Então, aqui está o código que resolveu meu problema:

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

Ainda gera um erro no console, mas pelo menos o vídeo está sendo reproduzido :)

Sharpey
fonte
1

Talvez uma solução melhor para isso, conforme descobri. Spec diz conforme citado em @JohnnyCoder:

media.pause ()

Define o atributo paused como true, carregando o recurso de mídia, se necessário.

-> carregando

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

indica o estado de prontidão da mídia HAVE_ENOUGH_DATA = 4

Basicamente, só carregue o vídeo se ainda não estiver carregado. Ocorreu um erro mencionado para mim, porque o vídeo não foi carregado. Talvez seja melhor do que usar um tempo limite.

Christoph Eberl
fonte
1

removeu todos os erros: (texto digitado)

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

Com a transmissão ao vivo, eu estava enfrentando o mesmo problema. e minha correção é esta. No vídeo html TAG, certifique-se de remover "autoplay" e use o código abaixo para jogar.

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;
                    }
                }
            });
        }
Soluções Neutro
fonte
1
Ótima solução !!
Junaid Mukhtar
1

Chrome retorna uma promessa nas versões mais recentes. Caso contrário, simplesmente:

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

Tenho o mesmo problema, finalmente resolvo por:

video.src = 'xxxxx';
video.load();
setTimeout(function() {
  video.play();
}, 0);
líquenbulireno
fonte
1

Este código foi consertado para mim!

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

Eu consertei com alguns códigos abaixo:

Quando quiser jogar, use o seguinte:

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

Da mesma forma, quando você quiser fazer uma pausa:

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

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

Aqui está outra solução se o motivo for que o download do seu vídeo é muito lento e o vídeo não foi armazenado em buffer:

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

Taysky
fonte
0

Parece que muitos programadores encontraram esse problema. uma solução deve ser bastante simples. elemento de mídia retorna Promisede ações, então

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

deve fazer o truque

Pery Mimon
fonte
0

aqui está uma solução do blog 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)
}
stackFish
fonte
0

Todos os novos vídeos de suporte do navegador serão reproduzidos automaticamente, mas sem som, portanto, coloque algo como este

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

O URL do vídeo deve corresponder ao status SSL se o seu site estiver rodando com https, então o URL do vídeo também deve ser https e o mesmo para HTTP

Shobhit Verma
fonte
0

A solução mais limpa e simples :

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

Razão um - chamar a pausa sem esperar que a promessa de reprodução seja resolvida

muitas respostas para este cenário, então irei me referir apenas ao melhor documento para esse problema:

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

Razão dois - chamar o jogo quando a guia não está focada

Nesse caso, o navegador pode interromper o playchamandopause quando a guia não está em foco. para economizar recursos para a guia ativa.

Portanto, você pode apenas esperar que a guia seja focada antes de chamar o jogo:


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

Eu encontrei o mesmo problema e resolvi adicionando dinamicamente o autoplayatributo em vez de usar play(). Dessa forma, o navegador decidiu jogar sem entrar na condição de corrida.

Jannis Hell
fonte
-1

Tentando fazer um vídeo de reprodução automática entrar em loop chamando play()quando ele termina, a solução alternativa de tempo limite não funcionou para mim (por maior que seja o tempo limite).

Mas descobri que, ao clonar / substituir o vídeo por jQuery ao terminar, ele faria um loop adequado.

Por exemplo:

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

Estou usando o Chrome 54.0.2840.59 (64 bits) / OS X 10.11.6

Silvain
fonte
-1

Acho que eles atualizaram o vídeo html5 e descontinuaram alguns codecs. Funcionou para mim depois de remover os codecs.

No exemplo abaixo:

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

Quando você vê um erro com Uncaught (in promise)Isso significa apenas que você precisa lidar com a promessa com um .catch()Neste caso, .play()retorna uma promessa. Você pode decidir se deseja registrar uma mensagem, executar algum código ou não fazer nada, mas enquanto tiver o, .catch()o erro desaparecerá.

var n = new Audio();
n.pause();
n.currentTime = 0;
n.play().catch(function(e) {
  // console.log('There was an error', e);
});
Seantomburke
fonte
não retorna uma promessa
Martin Dawson
@MartinMazzaDawson Ele retorna uma promessa. Se você olhar o comentário esclarecedor de xxx na pergunta, ele diz "É um erro, aqui está o erro completo: Não capturado (em promessa) DOMException: A solicitação play () foi interrompida por uma chamada para pause ()."
seantomburke
-5

Eu usei um truque para combater esse problema. Defina uma variável global var audio;

e na verificação de função

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

e na função de parada

audio.pause();
audio = undefined;

então a próxima chamada de audio.play , o áudio estará pronto a partir de '0' currentTime

eu usei

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

mas não funcionou. Obrigado.

Gippy Aulakh
fonte