javascript 无法在“HTMLMediaElement”上执行“播放”:API 只能由用户手势启动

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/32424775/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-28 15:15:16  来源:igfitidea点击:

Failed to execute 'play' on 'HTMLMediaElement': API can only be initiated by a user gesture

javascriptandroidangularjsgoogle-chromesoundmanager2

提问by andreybavt

I'm making a music playing page, where I use SoundManager 2 for AngularJs. I'm using a remote API to get a song URL to play. I enhanced an angular-soundmanager2 click event handler :

我正在制作一个音乐播放页面,在那里我将SoundManager 2 用于 AngularJs。我正在使用远程 API 来获取要播放的歌曲 URL。我增强了 angular-soundmanager2 单击事件处理程序:

element.bind('click', function () {
    if (angular.isFunction(scope.loadFunction)) {
        scope.loadFunction(scope.song, function () {
            $log.debug('adding song to playlist');
            addToPlaylist(scope.song.playDetails);

        })
    } else {
        $log.debug('adding song to playlist');
        addToPlaylist(scope.song);
    }
});

Where I added a part, that calls scope.loadFunction(song,callback)and after this function loads a song URL it calls a callback to give the control back to angular-soundmanager2.

在我添加了一个部分的地方,它会调用scope.loadFunction(song,callback),在这个函数加载一个歌曲 URL 之后,它会调用一个回调来将控制权交还给 angular-soundmanager2。

The problem is that on chrome for android I get an error :

问题是在 chrome for android 上我收到一个错误:

Failed to execute 'play' on 'HTMLMediaElement': API can only be initiated by a user gesture.

it doesn't happen if a song has a URL from the beginning and async loading isn't used.

如果一首歌从一开始就有一个 URL 并且不使用异步加载,则不会发生这种情况。

Are there any workarounds for it?

有什么解决方法吗?

采纳答案by andreybavt

I had to experiment with Chrome playing sounds. It turned out that even after a user gesture (such as click) it waits for 1000ms and if no sound was played it throws the exception above. In my case the problem was coming from asynchronous track URL loading.

我不得不尝试用 Chrome 播放声音。事实证明,即使在用户手势(例如单击)之后,它也会等待 1000 毫秒,如果没有播放声音,则会引发上述异常。在我的情况下,问题来自异步轨道 URL 加载。

But it also turned out that after the first track is played chrome doesn't care anymore for this 1000ms delay and you can call play programmatically with any timeout you want.

但事实证明,在播放第一首曲目后,chrome 不再关心这 1000 毫秒的延迟,您可以在任何您想要的超时情况下以编程方式调用播放。

So the solution was to play a micro almost zero-second long muted sound from static resources after the very first time a user clicks on a track and after that load a desired track URL.

因此,解决方案是在用户第一次单击曲目并加载所需曲目 URL 后,从静态资源中播放几乎为零秒长的微静音声音。

Hope it helps or someone finds other solutions.

希望它有所帮助或有人找到其他解决方案。

回答by u4327805

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
  webSettings.setMediaPlaybackRequiresUserGesture(false);
}

回答by andreybavt

Here is how I embeded this solution into soundmanager2 :

以下是我将此解决方案嵌入 soundmanager2 的方法:

Create a function :

创建一个函数:

function playMicroTrack(scope, angularPlayer) {
if (!scope.$root.isInitialized) {
    soundManager.createSound({
        id: '-1',
        url: '../../assets/lib/microSound.mp3'
    });
    soundManager.play('-1');
    scope.$root.isInitialized = true;
    }
}

Then inside the play directive use :

然后在播放指令中使用:

ngSoundManager.directive('playAll', ['angularPlayer', '$log',
function (angularPlayer, $log) {
    return {
        restrict: "EA",
        scope: {
            songs: '=playAll'
        },
        link: function (scope, element, attrs) {
            element.bind('click', function (event) {

                playMicroTrack(scope, angularPlayer);

                //first clear the playlist
                angularPlayer.clearPlaylist(function (data) {
                    $log.debug('cleared, ok now add to playlist');
                    //add songs to playlist
                    for (var i = 0; i < scope.songs.length; i++) {
                        angularPlayer.addTrack(scope.songs[i]);
                    }

                    if (attrs.play != 'false') {
                        //play first song
                        angularPlayer.play();
                    }
                });
            });
        }
    };
}
]);

In my case soundManager.play contains a piece of code that sends an async call to server to fetch a track url. This was causing the issue.

就我而言, soundManager.play 包含一段代码,该代码向服务器发送异步调用以获取轨道 url。这导致了这个问题。

Hope this helps

希望这可以帮助

回答by Charles R.

Similar to andreybavt's solution. Here is how I have solved the issue. I wrote a function that loads play/pause all sounds I want to use. This function is called onClick. This click is very important. It will be the User's gesture required.

类似于 andreybavt 的解决方案。这是我解决问题的方法。我写了一个函数来加载播放/暂停所有我想使用的声音。这个函数叫做onClick。这个点击非常重要。这将是用户所需的手势。

$("#myBtn").on("click", function(){
 setSounds();
});

function setSounds() {

  //Play and pause all sounds you want to use
  var audio1 = $("#mus_1")[0];
  var audio2 = $("#mus_2")[0];

  audio1.play();
  audio1.pause();

  audio2.play();
  audio2.pause();
}

By playing and pausing immediately all sounds you will not hear them play, they will be "loaded after a client gesture" and you will be able to call/play them programmatically afterwards. Hope this helps.

通过立即播放和暂停所有声音,您将听不到它们播放,它们将“在客户端手势后加载”,之后您将能够以编程方式调用/播放它们。希望这可以帮助。