38

我在同一页面上嵌入了两个相同的 YouTube 视频。我希望它们保持同步,这是我的要求/注意事项:

  • 两个视频必须同时开始
  • 当用户播放/暂停视频时,其他视频也会这样做
    • 这很容易通过 API
  • 当一个视频缓冲另一个停止等待,并在它们都准备好时开始
  • 我只需要一个视频的音频
  • 同步精度不必是毫秒级的完美,只要可靠
  • 一个视频将用作背景视频
    • 此视频会略微模糊(使用 CSS3 模糊),因此质量不是超级关键

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

需要注意的是,一个视频会显得比另一个大,因此 YouTube 可能会为此提供更高质量的视频。

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

我的问题是,对于上述要求,有没有办法让这两个视频保持同步?我确实考虑过是否可以使用画布 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;

}

}
4

1 回答 1

35

你的项目很有趣,这就是我决定尝试帮助你的原因。我从未使用过 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() {

这是代码的一部分,行为奇怪。取消注释下面的行会使同步非常糟糕,但是如果你注释它,你将不得不点击两次播放按钮,同步看起来会更好。

                       //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 更新 实时预览

希望这可以帮助。

于 2013-11-13T23:04:27.957 回答