web-dev-qa-db-ja.com

2つのYouTube動画を互いに同期させる

同じページに2つの同一のYouTubeビデオが埋め込まれています。両方を同期させたいのですが、これが私の要件/メモです。

  • 両方の動画を同時に開始する必要があります
  • ユーザーが動画を再生/一時停止すると、他の動画も同じように動作します[.____]。
    • これはAPIを介して非常に簡単です
  • 一方のビデオがバッファリングされると、もう一方は待機を停止し、両方の準備ができたときに開始します
  • 1つの動画の音声のみが必要です
  • 同期の精度はミリ秒単位である必要はなく、信頼できるだけです。
  • 1本の動画が背景動画として使用されます
    • このビデオは(CSS3ブラーを使用して)わずかにぼやけるので、品質はそれほど重要ではありません

YouTube JS APIを使用してプレーヤーの状態の変化をリッスンし、両方のビデオの同期を維持しようとしましたが、期待したほど信頼性がありませんでした。このためのコードの一部を以下に掲載します。

注意点の1つは、一方のビデオがもう一方のビデオよりも大きく表示されるため、YouTubeがそのために高品質のビデオを提供する可能性があることです。

私はCSS3ぼかしを使用しているため、最近のWebkitブラウザーしか使用できないため、これらだけで(FF/IEではなく)機能するソリューションは問題ありません。

私の質問はこれです、上記の要件について、これら2つのビデオの同期を維持する方法はありますか? canvas 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;

}

}
36
Ben Everard

あなたのプロジェクトはちょっと面白いので、私はあなたを助けようと決心しました。私はyoutubeAPIを使用したことがありませんが、いくつかのコーディングを試しました。それはあなたにとっての始まりかもしれません。

だから私が試したコードはここにあり、それはかなりうまくいくようです、確かにいくつかの改善が必要です(私は2つの再生されたビデオ間のオフセットを計算しようとしませんでしたが、ミュートを解除するとミスマッチを示し、合法に聞こえます)

さあ行こう :

いくつかの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 PlayerAPIコードを非同期でロードします。

_        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);
  • プレーヤーを表示します。

2つのビデオをプリロードする必要があります。

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

これは奇妙に動作するコードの一部です。以下の行のコメントを外すと同期がかなり悪くなりますが、コメントする場合は、[再生]ボタンの2回をクリックする必要があります[〜#〜]しかし[〜#〜]同期はずっと良く見えるでしょう。

_                       //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年更新ライブプレビュー

お役に立てれば。

31
ThisIsSparta