Як запобігти помилці "Запит на програвання () був перерваний викликом до паузи ()"?


107

Я зробив веб-сайт, де, якщо користувач клацне, він відтворює звук. Щоб звук не перекривався, мені довелося додати код:

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

Але це спричиняє помилку: The play() request was interrupted by a call to pause()
з'являтися щоразу, коли звукова подія спрацьовує відразу після іншого тригера. Звуки все ще чудово звучать, але я хочу запобігти появі цього повідомлення про помилку. Якісь ідеї?


це помилка чи більше повідомлення?
dandavis

Це помилка, ось повна помилка: Uncaught (в обіцянні) DOMException: Запит play () був перерваний викликом до паузи ().
Оуен М

7
це новіша помилка, не хвилюйтесь про це: bugs.chromium.org/p/chromium/isissue/detail?id=593273
dandavis

3
Просте виправлення цього полягає в тому, щоб просто не викликати відтворення або паузу відразу один за одним. Замість цього використовуйте медіа-події, щоб визначити, коли потрібно призупинити чи відтворити. Приклад: onCanPlay(). Усі ці відповіді - це брудні хаки.
Мартін Доусон

1
У мене була протилежна проблема. Я намагався n.play() тоді n.pause() . Для цього ця стаття з веб-основ описує рішення. tl; dr:n.play().then(() => { n.pause()})
totymedli

Відповіді:


84

З цим питанням я стикався і нещодавно - це може бути умова перегонів між play()та pause(). Схоже, тут є посилання на це питання або щось, що стосується тут .

Як зазначає @Patrick , pauseне повертає обіцянки (чи нічого), тому вищевказане рішення не працюватиме. У той час як MDN не має документів pause(), у чернеті WC3 для Media Elements йдеться:

media.pause ()

Встановлює атрибут призупиненого значення true, якщо потрібно, завантажуючи медіаресурс.

Тому можна також перевірити pausedатрибут у зворотному режимі зворотного дзвінка.

Виходячи з цієї чудової відповіді ТА , ось як ви можете перевірити, чи відео справді відтворюється (чи ні), тож ви можете сміливо запускати відтворення () без помилки.

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

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

В іншому випадку, @Patrick «S відповідь повинен працювати.


1
Як говорить @ regency-software, метод паузи не повертає Обіцяння (у програмі .pause () немає документів, а просто "не визначено"). Таким чином, це рішення можна застосувати лише у випадках play (), а потім призупинити ().
Сепція

Дякую за інформацію - я оновив свою відповідь, щоб відобразити те, що розміщено програмне забезпечення @regency, яке було правильним, і він також мав робоче рішення :-).
JohnnyCoder

як ви приїхали в 150 мс як час очікування? здається, є хромованим питанням: bugs.chromium.org/p/chromium/isissue/detail?id=593273
connect2krish

Див @Regency Software «s відповідь на причину тайм - ауту. Я розширив їх відповідь, що було хорошим рішенням. Також моя відповідь посилається на посилання, яке ви вказали на початку відповіді.
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 Дякую, це вирішить проблему для всіх
Nebojsa Sapic

Чи можете, будь-хто, будь-ласка, покластися на цю відповідь, щоб швидше її відкликати?
Додавання кольору

1
Чому це не позначено як правильну відповідь? Безумовно, це рішення, а не злом з таймаутами.
jeron-diovis

15
Чому потрібні дві змінні? Я не кажу, що це погане рішення. Тільки намагаюся її обробити.
доброзичливий

20

Я потрапив у цю проблему і маю випадок, коли мені потрібно було натиснути на паузу (), потім грати (), але при використанні паузи (). Тоді () я не визначаю.

Я виявив, що якщо я почав грати через 150 мс після паузи, це вирішило проблему. (Сподіваємось, Google скоро виправить)

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

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

Якщо ви маєте на увазі 150 мс? Я спробував декілька різних значень і вирішив це зробити для своєї мети. Я орієнтувався на Chrome і Android, і це відповідало потребам мого додатка. Це може бути нижчим.
Патрік

3
тому це майже випадкове значення, як ні в чому іншому, крім вашого impl. дякую за роз’яснення!
y_nk

Мені цікаво, ви використовуєте один і той же елемент для відтворення декількох звуків? Це пояснило б питання навантаження; один звук все ще завантажений, намагаючись відтворити інший звук через той самий елемент. Затримка "може" зупинити її, хоча її незграбне виправлення та довші / короткі затримки можуть бути використані для кращого / найкращого досвіду. Очищення та відтворення елементів на льоту було моїм СОП, який пропускає через ряд витоків аудіо / відео та пам'яті.
дерево

1
виглядає як хромована помилка: bugs.chromium.org/p/chromium/isissue/detail?id=593273
connect2krish

8
Довільна тривалість setTimeout не повинна використовуватися для вирішення перегонових умов.
Гаджет Бластер


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

Це трохи непосильне, але допомагає при обробці Обіцянки в унікальних сценаріях.


2

Рішення, запропоновані тут, або не спрацювали для мене, або там, де велике, тож я шукав щось інше і знайшов рішення, запропоноване @dighan на bountysource.com/isissue/

Ось ось код, який вирішив мою проблему:

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-відео 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 повертає Обіцянку в новіших версіях. В іншому випадку просто:

        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>

URL-адреса відео повинна відповідати статусу SSL, якщо ваш сайт працює з https, тоді 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коли вкладка не фокусується. щоб зберегти ресурси для активної вкладки.

Таким чином, ви можете просто зачекати, коли вкладка буде зосереджена, перш ніж викликати гру:


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

це не повертає обіцянки
Мартін Доусон

@MartinMazzaDawson Це дійсно повертає обіцянку. Якщо ви подивитеся на уточнюючий коментар xxx у запитанні, він каже: "Це помилка, ось повна помилка: Uncaught (в обіцянні) DOMException: Запит play () був перерваний викликом до паузи ()."
seantomburke

-5

Я використав хитрість, щоб протистояти цій проблемі. Визначте глобальну змінну var audio;

і в перевірці функцій

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

і у функції зупинки

audio.pause();
audio = undefined;

тому наступний дзвінок audio.playаудіо буде готовий з "0" currentTime

я використав

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

але це не спрацювало. Дякую.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.