35 votes

Garder deux vidéos YouTube synchronisées entre elles

J'en ai deux identiques vidéos YouTube intégrées sur la même page. Je voudrais à la fois être synchronisés, voici mes exigences / remarques:

  • Les deux vidéos doivent démarrer en même temps
  • Lorsqu'une vidéo est en lecture / mise en pause par l'utilisateur de l'autre vidéo de la même
    • C'est très simple via l'API
  • Quand une vidéo en mémoire tampon les autres s'arrête pour attendre, et démarre quand ils sont tous les deux prêts
  • Je n'ai besoin que de l'audio d'une vidéo
  • Synchronisation de précision ne doit pas être milliseconde parfait, fiable
  • Une vidéo va être utilisé comme arrière-plan de la vidéo
    • Cette vidéo va être un peu floue (à l'aide de CSS3 flou), donc la qualité n'est pas super essentiel

J'ai essayé d'utiliser le YouTube API JS pour écouter joueur changements d'état et de tenter de garder les deux vidéos dans la synchro, cependant, il n'était pas fiable comme je l'avais espéré. Je vais poster la partie du code pour ce ci-dessous.

Une mise en garde, c'est qu'une vidéo va apparaître plus grand que les autres, de sorte que le YouTube pourrait fournir une meilleure qualité vidéo pour ça.

Parce que je suis en utilisant CSS3 flou, je ne peux que l'utilisation récente des navigateurs Webkit, donc une solution qui fonctionne sur ces seuls (et pas FF/IE) n'est pas un problème.

Ma question est la suivante, pour les exigences ci-dessus, est-il possible de garder ces deux vidéos dans la synchro? J'ai fait examiner s'il était possible d'utiliser l'API de canevas à "redessiner" la vidéo, mais après des recherches pensé que cela n'allait pas être possible.

buffering = false;

var buffer_control = function(buffering_video, sibling_video, state) {

switch ( state ) {

    case 1: // play

        if ( buffering === true ) {

            console.error('restarting after buffer');

            // pause both videos, we want to make sure they are both at the same time
            buffering_video.pauseVideo();
            sibling_video.pauseVideo();

            // get the current time of the video being buffered...
            var current_time = buffering_video.getCurrentTime();

            // ... and pull the sibling video back to that time
            sibling_video.seekTo(current_time, true);

            // finally, play both videos
            buffering_video.playVideo();
            sibling_video.playVideo();

            // unset the buffering flag
            buffering = false;

        }

        break;

    case 3: // buffering


        console.error('buffering');

        // set the buffering flag
        buffering = true;

        // pause the sibling video
        sibling_video.pauseVideo();

        break;

}

}

31voto

ThisIsSparta Points 809

Votre projet est plutôt intéressant, c'est pourquoi j'ai décidé d'essayer de vous aider. Je n'ai jamais utilisé l'API youtube, mais j'ai essayé quelques-uns de codage et c'est peut être un début pour vous.

Voici donc le code que j'ai essayé et il semble fonctionner assez bien , il a certainement besoin de quelques améliorations ( je n'ai pas essayé de calculer le décalage entre les deux vidéos, mais de les laisser activer montre l'inadéquation et il semble légitime)

Ici, nous allons :

Nous allons commencer avec quelques bases du html

<!DOCTYPE html>
<html>
    <head>

Nous ajoutons un positionnement absolu pour le premier plan de joueur donc il se superpose à la seule lecture de la vidéo d'arrière-plan (pour les tests)

        <style>
            #player2{position:absolute;left:195px;top:100px;}
        </style>
    </head>
<body>

jQuery est utilisé ici pour fade in/out, les joueurs (vous allez voir pourquoi ci-dessous), mais vous pouvez utiliser de base de JS

    <script src="jquery-1.10.2.min.js"></script>

Les < iframe> (et les lecteurs vidéo) remplacera ces balises < div>.

    <div id="player1"></div>    <!-- Background video player -->
    <div id="player2"></div>    <!-- Foreground video player -->

    <script>

Ce code charge de l'IFrame Lecteur de code API asynchrone.

        var tag = document.createElement('script');

        tag.src = "https://www.youtube.com/iframe_api";
        var firstScriptTag = document.getElementsByTagName('script')[0];
        firstScriptTag.parentNode.insertBefore(tag, firstScriptTag);

Cette fonction crée l' < iframe> (et YouTube joueurs), d'après le code de l'API de téléchargements. Remarque les fonctions de rappel : onPlayer1Ready et onPlayer1StateChange

        var player1;
        var player2;
        function onYouTubeIframeAPIReady() {
            player1 = new YT.Player('player1', {
                                  height: '780',
                                  width: '1280',
                                  videoId: 'M7lc1UVf-VE',
                                  playerVars: { 'autoplay': 0, 'controls': 0 },
                                  events: {
                                        'onReady': onPlayer1Ready,
                                        'onStateChange': onPlayer1StateChange
                                  }
                             });
            player2 = new YT.Player('player2', {
                                  height: '390',
                                  width: '640',
                                  videoId: 'M7lc1UVf-VE',
                                  events: {
                                       'onReady': onPlayer2Ready,
                                       'onStateChange': onPlayer2StateChange
                                  }
                              });
        }


        var player1Ready = false;
        var player2Ready = false;

Alors c'est maintenant la partie intéressante du code. Le principal problème dans votre projet de synchronisation est lié au fait que les vidéos doivent être mis en mémoire tampon avant de les lancer. En fait l'API ne pas fournir tout type intuitionnal fonction pour charger les vidéos (en raison de problèmes de bande passante (côté client et serveur). Nous devons donc obtenir un peu délicate.
Les étapes pour le chargement d'une vidéo sont les suivants:

  • Masquer les joueurs pour les prochaines étapes ne sont pas visibles pour l'utilisateur);
  • Couper le joueur ( player.mute():Void );
  • Émuler un saut dans la timeline pour démarrer la mise en mémoire tampon ( player.seekTo(seconds:Number, allowSeekAhead:Boolean):Void );
  • Attendre un changement d'état d'événement égale à YT.PlayerState.PLAYING;
  • Mettre la vidéo en Pause ( player.pauseVideo():Void );
  • Rembobiner la vidéo avec player.seekTo(seconds:Number, allowSeekAhead:Boolean):Void ;
  • Réactiver le joueur ( player.unMute():Void );
  • Montrer le joueur.

Vous avez pour précharger vos deux vidéos.

        var preloading1 = false;
        var preloading2 = false;

L'API va appeler ces fonctions lors de la vidéo, les joueurs sont prêts.

        function onPlayer1Ready(event) 
        {
            player1Ready = true;
            preloading1 = true;       // Flag the player 1 preloading
            player1.mute();           // Mute the player 1
            $( "#player1" ).hide();   // Hide it
            player1.seekTo(1);        // Start the preloading and wait a state change event
        }

        function onPlayer2Ready(event) {
            player2Ready = true;      // The foreground video player is not preloaded here
        }

Les appels d'API de cette fonction lors de l'arrière-plan de la vidéo du joueur changements d'état.

        function onPlayer1StateChange(event) 
        {
            if (event.data == YT.PlayerState.PLAYING ) {
                if(preloading1)
                {
                    prompt("Background ready");     // For testing
                    player1.pauseVideo();           // Pause the video
                    player1.seekTo(0);              // Rewind
                    player1.unMute();           // Comment this after test
                    $( "#player1" ).show();         // Show the player
                    preloading1 = false;

                    player2Ready = true;
                    preloading2 = true;             // Flag for foreground video preloading
                    player2.mute();
                    //$( "#player2" ).hide();
                    player2.seekTo(1);              // Start buffering and wait the event
                }
                else
                    player2.playVideo();            // If not preloading link the 2 players PLAY events
            }

            else if (event.data == YT.PlayerState.PAUSED ) {
                if(!preloading1)
                    player2.pauseVideo();           // If not preloading link the 2 players PAUSE events
            }
            else if (event.data == YT.PlayerState.BUFFERING ) {
                if(!preloading1)
                {
                    player2.pauseVideo();           // If not preloading link the 2 players BUFFERING events
                }
            }
            else if (event.data == YT.PlayerState.CUED ) {
                if(!preloading1)
                    player2.pauseVideo();           // If not preloading link the 2 players CUEING events
            }
            else if (event.data == YT.PlayerState.ENDED ) {
                player2.stopVideo();                // If not preloading link the 2 players ENDING events
            }
        }

Les appels d'API de cette fonction lors de l'avant-plan de la vidéo du joueur changements d'état.

        function onPlayer2StateChange(event) {
            if (event.data == YT.PlayerState.PLAYING ) {
                if(preloading2)
                {
                    //prompt("Foreground ready");
                    player2.pauseVideo();           // Pause the video
                    player2.seekTo(0);              // Rewind
                    player2.unMute();               // Unmute
                    preloading2 = false;

                    $( "#player2" ).show(50, function() {

Voici une partie du code qui agit étrangement. Décommentant la ligne ci-dessous feront l'sync assez mauvais,mais si vous en commentaire, vous devez cliquer deux fois sur le bouton de lecture , MAIS la synchronisation sera bien meilleur.

                       //player2.playVideo();
                    });
                }
                else
                    player1.playVideo();
            }
            else if (event.data == YT.PlayerState.PAUSED ) {
                if(/*!preloading1 &&*/ !preloading2)
                    player1.pauseVideo();
            }
            else if (event.data == YT.PlayerState.BUFFERING ) {
                if(!preloading2)
                {
                    player1.pauseVideo();
                    //player1.seekTo(... // Correct the offset here
                }
            }
            else if (event.data == YT.PlayerState.CUED ) {
                if(!preloading2)
                    player1.pauseVideo();
            }
            else if (event.data == YT.PlayerState.ENDED ) {
                player1.stopVideo();
            }
        }


        </script>
    </body>
</html>

Notez que les points de vue peuvent ne pas être comptés avec ce code.

Si vous voulez le code sans les explications, vous pouvez aller ici : http://jsfiddle.net/QtBlueWaffle/r8gvX/1/

Espérons que cette aide.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X