web-dev-qa-db-fra.com

Échec d'exécution de la lecture sur 'HTMLMediaElement': l'API ne peut être lancée que par un geste de l'utilisateur

Je crée une page de lecture de musique sur laquelle j'utilise SoundManager 2 pour AngularJs . J'utilise une API distante pour obtenir l'URL d'un morceau à jouer. J'ai amélioré un gestionnaire d'événements de clic 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);
    }
});

Où j'ai ajouté une partie qui appelle scope.loadFunction(song,callback) et après que cette fonction charge une URL de morceau, elle appelle un rappel pour redonner le contrôle à angular-soundmanager2.

Le problème est que sur chrome pour Android je reçois une erreur:

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

cela ne se produit pas si une chanson a une URL depuis le début et que le chargement asynchrone n'est pas utilisé.

Existe-t-il des solutions de contournement?

23
andreybavt

Je devais expérimenter avec Chrome en jouant des sons. Il s'est avéré que même après un geste de l'utilisateur (tel qu'un clic), il attend 1000 ms et si aucun son n'était joué, il lève l'exception ci-dessus. Dans mon cas, le problème venait du chargement d'une URL de piste asynchrone.

Mais il s’est également avéré qu’après la lecture de la première piste, chrome ne se souciait plus de ce délai de 1000 ms et vous pouvez appeler la lecture par programmation avec la durée de votre choix. 

La solution consistait donc à reproduire un micro muet de près de zéro seconde à partir de ressources statiques après le tout premier clic d'un utilisateur sur une piste, puis après chargement de l'URL de la piste souhaitée.

J'espère que ça aide ou que quelqu'un trouve d'autres solutions.

22
andreybavt

Voici comment j'ai intégré cette solution dans soundmanager2:

Créer une fonction:

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

Ensuite, dans la directive play, utilisez: 

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

Dans mon cas, soundManager.play contient un morceau de code qui envoie un appel asynchrone au serveur pour récupérer une URL de piste. Cela causait le problème.

J'espère que cela t'aides

2
andreybavt
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
  webSettings.setMediaPlaybackRequiresUserGesture(false);
}
2
hui 谁与争辉

Similaire à la solution d'andreybavt. Voici comment j'ai résolu le problème… .. J'ai écrit une fonction qui charge la lecture/pause de tous les sons que je veux utiliser. Cette fonction s'appelle onClick. Ce clic est très important. Ce sera le geste de l'utilisateur requis.

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

En jouant et en mettant immédiatement en pause tous les sons que vous ne les entendrez pas, ils seront "chargés après un geste du client" et vous pourrez les appeler/les lire par programme par la suite ...

1
Charles R.