首页 文章

保持两个YouTube视频彼此同步

提问于
浏览
34

我在同一页面上嵌入了两个相同的YouTube视频 . 我希望他们两个都同步,这是我的要求/说明:

  • 两个视频必须同时开始

  • 当用户播放/暂停视频时,其他视频也会这样做

  • 这很容易通过API

  • 当一个视频缓冲其他停止等待时,并在它们都准备就绪时启动

  • 我只需要一个视频的音频

  • 同步精度不一定是毫秒完美,只是可靠

  • 一个视频将用作背景视频

  • 此视频会略微模糊(使用CSS3模糊),因此质量不是非常必要

我尝试使用YouTube JS API来监听播放器状态更改并尝试保持两个视频同步,但它并不像我希望的那样可靠 . 我将在下面发布部分代码 .

需要注意的是,一个视频看起来比另一个大,所以YouTube可能会提供更高质量的视频 .

因为我使用CSS3模糊,所以我只能使用最近的Webkit浏览器,所以单独使用这些(而不是FF / IE)的解决方案不是问题 .

My question is this ,对于上述要求,有没有办法让这两个视频保持同步?我确实考虑过是否可以使用画布API来播放视频,但经过研究发现,这是不可能的 .

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;

}

}

1 回答

  • 29

    你的项目很有趣,这就是我决定尝试帮助你的原因 . 我从来没有使用过youtube API,但我尝试了一些编码,这可能是你的开始 .

    所以这里是我尝试的代码,它似乎工作得很好,它肯定需要一些改进(我没有尝试计算两个播放视频之间的偏移,但让他们取消静音显示不匹配,这听起来合法)

    开始了 :

    让我们从一些HTML基础知识开始

    <!DOCTYPE html>
    <html>
        <head>
    

    我们为前景播放器添加绝对定位,使其覆盖播放背景视频的播放器(用于测试)

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

    这里使用jQuery淡入/淡出玩家(你会在下面看到原因),但你可以使用基本的JS

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

    <iframes>(和视频播放器)将替换这些<div>标签 .

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

    此代码异步加载IFrame Player API代码 .

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

    下载API代码后,此功能会创建<iframes>(和YouTube播放器) . 请注意回调函数:onPlayer1Ready和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;
    

    所以现在是代码中有趣的部分 . 同步项目中的主要问题与视频在启动之前需要缓冲的事实有关 . 实际上,API没有提供任何直观的功能来预加载视频(由于带宽问题(客户端和服务器端) . 所以我们必须有点棘手 .
    预加载视频的步骤如下:

    • 隐藏播放器,以便用户看不到后续步骤);

    • 使播放器静音( player.mute():Void );

    • 模拟时间轴中的跳转以开始缓冲( player.seekTo(seconds:Number, allowSeekAhead:Boolean):Void );

    • 等待状态更改事件等于 YT.PlayerState.PLAYING ;

    • 暂停视频( player.pauseVideo():Void );

    • 使用 player.seekTo(seconds:Number, allowSeekAhead:Boolean):Void 回放视频;

    • 取消静音播放器( player.unMute():Void );

    • 显示播放器 .

    你必须预先加载你的两个视频 .

    var preloading1 = false;
            var preloading2 = false;
    

    当视频播放器准备就绪时,API将调用这些功能 .

    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
            }
    

    当后台视频播放器的状态发生变化时,API会调用此函数 .

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

    当前景视频播放器的状态发生变化时,API会调用此函数 .

    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() {
    

    这是代码的一部分,行为奇怪 . 取消注释下面的行会使同步非常糟糕,但是如果你发表评论,则必须在PLAY按钮上单击 twice BUT 同步看起来会更好 .

    //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>
    

    请注意,此代码可能不会计算视图 .

    如果你想要没有解释的代码,你可以去这里:http://jsfiddle.net/QtBlueWaffle/r8gvX/1/

    2016 Update Live Preview

    希望这可以帮助 .

相关问题