Comment éviter l'erreur «La requête play () a été interrompue par un appel à pause ()»?

107

J'ai créé un site Web où si l'utilisateur clique, il joue un son. Pour éviter que le son ne se chevauche, j'ai dû ajouter le code:

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

Mais cela provoque l'erreur: se The play() request was interrupted by a call to pause()
produire chaque fois que l'événement sonore est déclenché juste après un autre déclencheur. Les sons jouent toujours bien, mais je veux éviter que ce message d'erreur n'apparaisse constamment. Des idées?

Owen M
la source
est-ce une erreur ou plutôt un avis?
dandavis
C'est une erreur, voici l'erreur complète: Uncaught (in promise) DOMException: La requête play () a été interrompue par un appel à pause ().
Owen M
7
c'est un bogue plus récent, ne vous inquiétez pas: bugs.chromium.org/p/chromium/issues/detail?id=593273
dandavis
3
La solution simple pour cela est de ne pas appeler la lecture ou de faire une pause juste après l'autre. Utilisez plutôt les événements multimédias pour déterminer quand mettre en pause ou lire. Exemple: onCanPlay(). Toutes ces réponses sont de sales hacks.
Martin Dawson
1
J'ai eu le problème inverse. J'ai essayé n.play() alors n.pause() . Pour cela, cet article Web Fundamentals décrit la solution. tl; dr:n.play().then(() => { n.pause()})
totymedli

Réponses:

84

J'ai également rencontré ce problème récemment - cela pourrait être une condition de concurrence entre play()et pause(). Il semble qu'il y ait une référence à ce problème, ou à quelque chose lié ici .

Comme le souligne @Patrick , pausene renvoie pas de promesse (ou quoi que ce soit), donc la solution ci-dessus ne fonctionnera pas. Bien que MDN n'ait pas de documentation sur pause(), dans le brouillon WC3 pour Media Elements , il est dit:

media.pause ()

Définit l'attribut paused sur true, chargeant la ressource multimédia si nécessaire.

Ainsi, on peut également vérifier l' pausedattribut dans leur rappel de délai d'expiration.

Sur la base de cette excellente réponse SO , voici un moyen de vérifier si la vidéo est (ou non) réellement lue, afin de pouvoir déclencher une lecture () en toute sécurité sans l'erreur.

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

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

Sinon, la réponse de @Patrick devrait fonctionner.

JohnnyCoder
la source
1
Comme le dit @ regency-software, la méthode pause ne renvoie pas de promesse (il n'y a pas de documentation sur .pause ()), mais juste "indéfini". Cette solution ne peut donc être appliquée que sur les cas play () puis pause ().
Splact le
Merci pour l'info - j'ai mis à jour ma réponse pour refléter ce que le logiciel @regency a publié, ce qui était correct, et il avait également une solution de travail :-).
JohnnyCoder
comment êtes-vous arrivé à 150ms comme temps d'attente? semble être un problème de chrome: bugs.chromium.org/p/chromium/issues/detail?id=593273
connect2krish
Voir @Regency Software de réponse pour la raison du délai d'attente. J'ai développé leur réponse, ce qui était une bonne solution. De plus, ma réponse fait référence au lien que vous avez fourni au début de la réponse.
JohnnyCoder
Cela ne fonctionnerait pas pour la vidéo en streaming. Si j'initialise la vidéo WebRTC, cela donne toujours une exception à la console.
Stepan Yakovenko
70

Après des heures de recherche et de travail, j'ai trouvé la solution parfaite .

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

Après cela, vous pouvez basculer lecture / pause aussi vite que possible, cela fonctionnera correctement .

Nebojsa Sapic
la source
1
C'est ainsi que j'ai résolu le problème. Je pense que c'est une bien meilleure solution que de compter sur un délai d'attente
Malcor
@Malcor Merci, cela résoudra le problème pour tout le monde
Nebojsa Sapic
Certains pourraient-ils s'il vous plaît mettre une prime sur cette réponse pour la faire voter plus rapidement?
AddingColor
1
Pourquoi ce n'est pas marqué comme réponse correcte? C'est définitivement une solution, pas un hack avec des délais d'attente.
jeron-diovis
15
Pourquoi deux variables sont-elles nécessaires? Je ne dis pas que c'est une mauvaise solution. J'essaye juste de le faire.
benevolentprof
20

J'ai rencontré ce problème, et j'ai un cas où je devais appuyer sur pause () puis play () mais lorsque j'utilise pause (). Then () je deviens indéfini.

J'ai constaté que si je commençais à jouer 150 ms après une pause, le problème était résolu. (Espérons que Google corrige bientôt)

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

//Avoid the Promise Error
setTimeout(function () {      
   playerMP3.play();
}, 150);
Patrick
la source
Si vous voulez dire les 150ms? J'ai essayé quelques valeurs différentes et je me suis installé là-dessus pour mon objectif. Je ciblais Chrome et Android et cela répondait aux besoins de mon application. Il peut être plus bas.
Patrick
3
c'est donc une valeur presque aléatoire car non liée à autre chose que votre impl. Merci pour la clarification !
y_nk
Je suis curieux d'utiliser le même élément pour jouer plusieurs sons? Cela expliquerait le problème de la charge; un son toujours chargé tout en essayant de jouer un autre son à travers le même élément. Un délai "pourrait" l'arrêter bien que ce soit une solution maladroite et des délais plus longs / plus courts pourraient être utilisés pour une meilleure / meilleure expérience. Effacer et recréer des éléments à la volée a été ma SOP qui ignore un certain nombre de fuites audio / vidéo et mémoire.
arbre
1
ressemble à un bug de chrome: bugs.chromium.org/p/chromium/issues/detail?id=593273
connect2krish
8
Les durées arbitraires setTimeout ne doivent pas être utilisées pour résoudre les conditions de concurrence.
Gadget Blaster
5

l'essayer

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

Cette solution m'a aidé:

n.cloneNode(true).play();
Dmitry Kovganov
la source
2

En fonction de la complexité de votre solution, cela peut être 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);
    }
}

C'est un peu exagéré, mais cela aide lors de la gestion d'une promesse dans des scénarios uniques.

Le crayon bleu
la source
2

Les solutions proposées ici ne fonctionnaient pas pour moi ou étaient trop importantes, alors je cherchais autre chose et j'ai trouvé la solution proposée par @dighan sur bountysource.com/issues/

Voici donc le code qui a résolu mon problème:

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

Il jette toujours une erreur dans la console, mais au moins la vidéo est en cours de lecture :)

Sharpey
la source
1

Peut-être une meilleure solution pour cela comme je l'ai compris. Spec dit comme cité de @JohnnyCoder:

media.pause ()

Définit l'attribut paused sur true, chargeant la ressource multimédia si nécessaire.

-> le chargement

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

indique l'état de disponibilité du média HAVE_ENOUGH_DATA = 4

En gros, ne chargez la vidéo que si elle n'est pas déjà chargée. Une erreur mentionnée s'est produite pour moi, car la vidéo n'a pas été chargée. Peut-être mieux que d'utiliser un timeout.

Christoph Eberl
la source
1

supprimé toutes les erreurs: (dactylographié)

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

Avec la diffusion en direct, j'étais confronté au même problème. et ma solution est la suivante. À partir du TAG vidéo html, assurez-vous de supprimer "autoplay" et utilisez ce code ci-dessous pour jouer.

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;
                    }
                }
            });
        }
Solutions Neutro
la source
1
Excellente solution !!
Junaid Mukhtar
1

Chrome renvoie une promesse dans les dernières versions. Sinon, simplement:

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

J'ai le même problème, enfin je résous par:

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

Ce morceau de code corrigé pour moi!

Code modifié 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
la source
1

Je l'ai corrigé avec un peu de code ci-dessous:

Lorsque vous voulez jouer, utilisez ce qui suit:

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

De même, lorsque vous souhaitez mettre en pause:

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

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

Voici une autre solution si la raison en est que le téléchargement de votre vidéo est très lent et que la vidéo n'a pas été mise en mémoire tampon:

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

Taysky
la source
0

Il semble que de nombreux programmeurs aient rencontré ce problème. une solution doit être assez simple. l'élément multimédia revient Promisedes actions

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

devrait faire l'affaire

pery mimon
la source
0

voici une solution du 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
la source
0

Toutes les nouvelles vidéos de support de navigateur doivent être lues automatiquement avec le son uniquement, veuillez donc mettre quelque chose comme ça

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

L'URL de la vidéo doit correspondre à l'état SSL si votre site fonctionne avec https, l'URL de la vidéo doit également être en https et de même pour HTTP

Shobhit Verma
la source
0

La solution la plus propre et la plus simple :

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

Raison n ° 1 - Appeler pause sans attendre que la promesse de lecture se résout

trop de réponses pour ce scénario, je vais donc me référer au meilleur document pour ce problème:

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

Deuxième raison - appeler la lecture lorsque l'onglet n'est pas focalisé

Dans ce cas, le navigateur peut interrompre le playen appelant pauselorsque l'onglet n'est pas actif. afin d'économiser des ressources pour l'onglet actif.

Vous pouvez donc simplement attendre que l'onglet se concentre avant d'appeler play:


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
la source
-1

J'ai rencontré le même problème et je l'ai résolu en ajoutant dynamiquement l' autoplayattribut plutôt qu'en utilisant play(). De cette façon, le navigateur a compris pour jouer sans se heurter à la condition de concurrence.

Jannis Hell
la source
-1

En essayant d'obtenir une vidéo en lecture automatique en boucle en l'appelant play()à la fin, la solution de contournement du délai d'attente n'a pas fonctionné pour moi (quelle que soit la durée du délai).

Mais j'ai découvert qu'en clonant / remplaçant la vidéo par jQuery à la fin, elle bouclerait correctement.

Par exemple:

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

et

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

J'utilise Chrome 54.0.2840.59 (64 bits) / OS X 10.11.6

Silvain
la source
-1

Je pense qu'ils ont mis à jour la vidéo html5 et ont désapprouvé certains codecs. Cela a fonctionné pour moi après avoir supprimé les codecs.

Dans l'exemple ci-dessous:

<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
la source
-1

Lorsque vous voyez une erreur avec Uncaught (in promise)Ceci signifie simplement que vous devez gérer la promesse avec un .catch()Dans ce cas, .play()renvoie une promesse. Vous pouvez décider si vous souhaitez enregistrer un message, exécuter du code ou ne rien faire, mais tant que vous avez, .catch()l'erreur disparaîtra.

var n = new Audio();
n.pause();
n.currentTime = 0;
n.play().catch(function(e) {
  // console.log('There was an error', e);
});
seantomburke
la source
ça ne retourne pas une promesse
Martin Dawson
@MartinMazzaDawson Cela renvoie une promesse. Si vous regardez le commentaire de clarification de xxx dans la question, il dit "C'est une erreur, voici l'erreur complète: Uncaught (in promise) DOMException: La requête play () a été interrompue par un appel à pause ()."
seantomburke
-5

J'ai utilisé une astuce pour contrer ce problème. Définissez une variable globale var audio;

et dans le contrôle de fonctionnement

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

et dans la fonction d'arrêt

audio.pause();
audio = undefined;

donc le prochain appel de audio.play, l'audio sera prêt à partir de '0' currentTime

j'ai utilisé

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

mais cela n'a pas fonctionné. Merci.

Gippy Aulakh
la source