如何防止“ play()请求被调用pause()中断”错误?


107

我创建了一个网站,如果用户单击该网站,它将播放声音。为了防止声音重叠,我必须添加代码:

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

但是,这会导致错误: The play() request was interrupted by a call to pause()
每次声音事件在另一个触发器之后立即触发时都会出现。声音仍然可以正常播放,但我想防止此错误消息不断弹出。有任何想法吗?


是错误,还是更多通知?
dandavis '16

这是一个错误,这里是完整的错误:未捕获(按承诺)DOMException:play()请求被对pause()的调用中断了。
Owen M

7
这是一个较新的错误,请不必担心:bugs.chromium.org/p/chromium/issues/detail?id=593273
dandavis

3
一个简单的解决方法是不彼此通话或暂停播放。改用媒体事件来确定何时暂停或播放。范例:onCanPlay()。所有这些答案都是肮脏的骇客。
马丁·道森

1
我有相反的问题。我试图n.play() 然后 n.pause()。为此,此Web基础知识文章介绍了该解决方案。tl; dr:n.play().then(() => { n.pause()})
totymedli

Answers:


84

我最近也遇到了这个问题-这可能是play()和之间的竞争条件pause()。看来这里有对此问题的参考,或与之相关的东西

正如@Patrick指出的那样,pause它不会返回诺言(或其他任何东西),因此上述解决方案将行不通。尽管MDN pause()在的Media ElementsWC3草案中没有上的文档,但它说:

media.pause()

将暂停的属性设置为true,必要时加载媒体资源。

因此,还可以paused在其超时回调中检查属性。

基于这个很好的SO答案,您可以通过以下方法检查视频是否正在播放(或没有在播放),因此可以安全地触发play()而不出错。

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

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

否则,@Patrick答案应该起作用。


1
就像@ regency-software所说的那样,pause方法不会返回Promise(.pause()上没有文档),而只是“未定义”。因此,此解决方案仅适用于play()然后pause()情况。
Splact

感谢您提供的信息-我更新了答案以反映@regency软件发布的内容,这是正确的,并且他也有一个可行的解决方案:-)。
JohnnyCoder

您是如何到达150毫秒的等待时间的?似乎是一个chrome问题:bugs.chromium.org/p/chromium/issues/detail?id=593273
connect2krish

有关超时原因,请参见@Regency Software答案。我扩展了他们的答案,这是一个很好的解决方案。另外,我的答案引用了您在答案开头提供的链接。
JohnnyCoder

这不适用于流视频。如果我已初始化WebRTC视频,则控制台仍然会出现异常。
斯蒂芬·雅科文科

70

经过数小时的搜索和工作,我找到了完美的解决方案

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

之后,您可以尽快切换播放/暂停它将正常工作


1
这就是我解决问题的方式。我觉得这比依靠超时是一个更好的解决方案
Malcor

@Malcor谢谢您,这将为所有人解决问题
Nebojsa Sapic

有人可以在这个答案上悬赏,以更快地投票吗?
AddingColor '17

1
为什么未将其标记为正确答案?绝对这是一个解决方案,而不是超时的黑客。
jeron-diovis '17

15
为什么需要两个变量?我并不是说这是一个不好的解决方案。只是想尝试一下。
benevolentprof

20

我遇到了这个问题,并且遇到一种情况,我需要先打pause()然后再打play(),但是当使用pause()。then()时,我不确定。

我发现,如果我在暂停后150毫秒开始播放,则可以解决此问题。(希望Google尽快修复)

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

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

如果是150ms?我尝试了几种不同的价值观,并出于自己的目的决定了这一点。我的目标是Chrome和Android,它可以满足我的应用程序的需求。它可能会更低。
帕特里克

3
因此它几乎是随机值,因为与您的展示次数无关。感谢您的澄清!
y_nk '16

我很好奇,您是否使用同一元素播放多种声音?那将解释负载问题;尝试通过同一元素播放另一声音时,仍然加载一个声音。延迟“可能”阻止了它,尽管它笨拙地解决了问题,并且可以使用较长/较短的延迟来获得更好/最佳的体验。即时清除和重新创建元素是我的SOP,它跳过了许多音频/视频和内存泄漏。


8
不应使用任意的setTimeout持续时间来解决竞争条件。
Gadget Blaster


5

试试吧

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


2

根据您所需解决方案的复杂程度,这可能会有用:

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

这有点矫kill过正,但是在特殊情况下处理Promise时会有所帮助。


2

解决方案这里提出要么没有工作或我在哪里大,所以我一直在寻找别的东西,发现通过@dighan提出的解决方案bountysource.com/issues/

所以这是解决我的问题的代码:

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

它仍然会向控制台抛出错误,但是至少正在播放视频:)


1

如我所知,这也许是一个更好的解决方案。Spec说如@JohnnyCoder所引用:

media.pause()

将暂停的属性设置为true,必要时加载媒体资源。

->加载它

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

指示媒体的就绪状态HAVE_ENOUGH_DATA = 4

基本上,如果尚未加载视频,则仅加载它。由于未加载视频,因此发生了我提到的错误。也许比使用超时更好。


1

删除了所有错误:(打字稿)

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

1

对于实时流媒体,我面临着同样的问题。而我的解决方法是 确保从html video TAG中删除“自动播放”, 然后使用以下代码播放。

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
很好的解决方案!
朱纳德·穆赫塔尔

1

Chrome会返回最新版本的Promise。否则,只需:

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

1

我有同样的问题,最后我通过以下方法解决:

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

1

这段代码为我修复了!

@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

我已经用下面的代码修复了它:

要播放时,请使用以下命令:

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

同样,当您想暂停时:

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

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

0

如果原因是您的视频下载速度非常慢且视频尚未缓冲,则可以使用以下另一种解决方案:

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


0

看起来很多程序员都遇到了这个问题。解决方案应该非常简单。媒体元素Promise从动作中返回,因此

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

应该可以


0

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

所有新的浏览器支持的视频都只能在静音的情况下自动播放,因此请输入以下内容

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

如果您的网站使用https运行,则视频URL应与SSL状态匹配,然后视频URL也应为https且与HTTP相同


0

干净简单的解决方案:

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

0

原因一-在不等待播放承诺的情况下呼叫暂停即可解决

对于这种情况,答案太多了,因此,我将仅参考针对该问题的最佳文档:

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

原因二-当标签未聚焦时调用播放

在这种情况下,浏览器可能会play通过调用来中断pause在选项卡未聚焦时。为了节省活动选项卡的资源。

因此,您可以在调用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();

-1

我遇到了同样的问题,并通过动态添加autoplay属性而不是使用来解决了该问题play()。这样,浏览器就可以在不遇到竞争条件的情况下进行播放。


-1

尝试通过play()在结束时调用来使自动播放的视频循环播放,超时解决方法对我不起作用(无论超时时间长短)。

但是我发现,通过在结束时用jQuery复制/替换视频,它可以正常循环。

例如:

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

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

我正在使用Chrome 54.0.2840.59(64位)/ OS X 10.11.6


-1

我认为他们更新了html5视频并弃用了一些编解码器。删除编解码器后,它对我有用。

在以下示例中:

<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

当您看到错误时,Uncaught (in promise)这仅表示您需要使用来处理诺言。.catch()在这种情况下,.play()将返回诺言。您可以决定是要记录消息,运行某些代码还是不执行任何操作,但是只要有.catch()错误,该错误就会消失。

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

它没有兑现诺言
Martin Dawson

@MartinMazzaDawson它确实返回了承诺。如果您在问题中看到xxx的澄清注释,他会说:“这是一个错误,这是完整的错误:未捕获(承诺)DOMException:play()请求被对pause()的调用中断了。”
seantomburke

-5

我使用了一个技巧来解决这个问题。定义一个全局变量var audio;

并在功能检查中

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

并在停止功能中

audio.pause();
audio = undefined;

所以下一个电话 audio.play,音频将从当前时间“ 0”开始就绪

我用了

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

但这没用。谢谢。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.