Translate

2015년 4월 15일 수요일

[Youtube] Player API 를 활용한 유튜브 동영상 컨트롤



다음이나 네이버와 같이 자체적으로 스트리밍 서비스를 하지 않고서는 대부분의 회사는 Google 에서 운영하는 Youtube 에 동영상을 많이 올려서 홍보를 많이 한다.

최근 Youtube 의 동영상을 제어해야 할 일이 있어서 알아본 것을 정리하려 한다.




Youtube 동영상을 컨트롤 할 수 있는 Player API 는 다음과 같다.

1. Android API
2. IFrame API
3. Javascript API - Deprecated
4. Flash API (AS3) - Deprecated
5. iOS API
1004lucifer
나머지 API 이름은 직관적이다보니 따로 설명을 안해도 되지만
2, 3번에 대해서 차이점을 간단히 짚어보고 IFrame API 에 대해 설명하려 한다.





IFrame API 와 JavaScript API 의 차이점

IFrame API
  - Youtube 에서 Flash를 지원하지 않는 모바일 기기에 Flash 대신 HTML5 를 사용하여 플레이어를 게재한다.
  - Internet Explorer 7 은 지원하지 않는다.

JavaScript API
  - 모든 항목이 올바르게 표시되려면 Flash Player 10.1 이상 설치되어야 한다.
    (Flash Player 필수)

1004lucifer
보여지는 타겟에 따라서 사용하는 API가 나뉘어지게 된다.
데스크톱: JavaScript API 를 사용해서 브라우저 호환성을 높인다.
모바일: Flash 대신 HTML5를 사용하여 사용자의 접근성을 높인다.




===========================
2019.06.16 내용 업데이트
- IFrame, Javascript API가 통합되었다. 이제는 Javascript API를 사용하더라도 자동으로 IFrame 이 생성이 된다.

- 아래의 데모(jsfiddle)의 Result를 클릭 시 작동 모습을 볼 수 있다.
(현재 크롬에서 Result에서 기능이 작동 안하는 경우가 종종 있는데.. 다른 탭으로 갔다가 다시 Result로 돌아오면 정상적으로 작동된다.
Youtube 공식 샘플사이트(링크)도 문제가 있는걸로 봐서는 현재 버전의 크롬에 문제가 있는게 아닐까 생각이 들며, 추후 개선되지 않을까 싶다.
음소거(mute) 설정을 하게되면 문제가 없는데 아래의 크롬 autoplay 기능이 추가되면서 버그가 발생한게 아닐까 싶다.)

- 크롬 autoplay 정책이 변경되었다. (2018년 04월)
https://developers.google.com/web/updates/2017/09/autoplay-policy-changes#iframe
 1) 음소거된 자동재생은 무조건 허용됨.
 1) iframe 사용시 allow="autoplay; fullscreen" 속성을 추가하면 소리있는 자동재생 및 풀스크린 허용됨.
============================



IFrame API 사용방법

참조: https://developers.google.com/youtube/iframe_api_reference

* 플레이어 매개변수는 아래 URL을 참고하면 된다.
 - https://developers.google.com/youtube/player_parameters?playerVersion=HTML5&hl=ko

1. <iframe> 태그에 이벤트 연동
  - iframe 태그의 src에 enablejsapi 값을 꼭 넣어주어야 한다. (한참 삽질을..;;)

<!--
User: 1004lucifer
Date: 2015. 4. 14.
-->
<!DOCTYPE html>
<html>
<head lang="en">
    <meta charset="UTF-8">
    <title></title>
</head>
<body>
    <!-- 주의: src링크에 enablejsapi=1 을 꼭 넣어주어야 한다. -->
    <iframe id="gangnamStyleIframe" width="560" height="315" src="https://www.youtube.com/embed/9bZkp7q19f0?rel=0&enablejsapi=1" frameborder="0" allow="autoplay; fullscreen" allowfullscreen></iframe>

    <script type="text/javascript">
        /**
         * Youtube 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);

        /**
         * onYouTubeIframeAPIReady 함수는 필수로 구현해야 한다.
         * 플레이어 API에 대한 JavaScript 다운로드 완료 시 API가 이 함수 호출한다.
         * 페이지 로드 시 표시할 플레이어 개체를 만들어야 한다.
         */
        var player;
        function onYouTubeIframeAPIReady() {
            player = new YT.Player('gangnamStyleIframe', {
//                height: '315',            // <iframe> 태그 지정시 필요없음
//                width: '560',             // <iframe> 태그 지정시 필요없음
//                videoId: '9bZkp7q19f0',   // <iframe> 태그 지정시 필요없음
//                playerVars: {             // <iframe> 태그 지정시 필요없음
//                    controls: '2'
//                },
                events: {
                    'onReady': onPlayerReady,               // 플레이어 로드가 완료되고 API 호출을 받을 준비가 될 때마다 실행
                    'onStateChange': onPlayerStateChange    // 플레이어의 상태가 변경될 때마다 실행
                }
            });
        }
        function onPlayerReady(event) {
            console.log('onPlayerReady 실행');

            // 플레이어 자동실행 (주의: 모바일에서는 자동실행되지 않음)
            event.target.playVideo();
        }
        var playerState;
        function onPlayerStateChange(event) {
            playerState = event.data == YT.PlayerState.ENDED ? '종료됨' :
                    event.data == YT.PlayerState.PLAYING ? '재생 중' :
                    event.data == YT.PlayerState.PAUSED ? '일시중지 됨' :
                    event.data == YT.PlayerState.BUFFERING ? '버퍼링 중' :
                    event.data == YT.PlayerState.CUED ? '재생준비 완료됨' :
                    event.data == -1 ? '시작되지 않음' : '예외';

            console.log('onPlayerStateChange 실행: ' + playerState);
        }
    </script>
</body>
</html>







1004lucifer
2. <iframe> 태그를 <div> 태그로 치환하는 방법

<!--
User: 1004lucifer
Date: 2015. 4. 14.
-->
<!DOCTYPE html>
<html>
<head lang="en">
    <meta charset="UTF-8">
    <title></title>
</head>
<body>
    <div id="gangnamStyleIframe"></div>

    <script type="text/javascript">
        /**
         * Youtube 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);

        /**
         * onYouTubeIframeAPIReady 함수는 필수로 구현해야 한다.
         * 플레이어 API에 대한 JavaScript 다운로드 완료 시 API가 이 함수 호출한다.
         * 페이지 로드 시 표시할 플레이어 개체를 만들어야 한다.
         */
        var player;
        function onYouTubeIframeAPIReady() {
            player = new YT.Player('gangnamStyleIframe', {
                height: '315',            // <iframe> 태그 지정시 필요없음
                width: '560',             // <iframe> 태그 지정시 필요없음
                videoId: '9bZkp7q19f0',   // <iframe> 태그 지정시 필요없음
                playerVars: {             // <iframe> 태그 지정시 필요없음
                    controls: '2'
                },
                events: {
                    'onReady': onPlayerReady,               // 플레이어 로드가 완료되고 API 호출을 받을 준비가 될 때마다 실행
                    'onStateChange': onPlayerStateChange    // 플레이어의 상태가 변경될 때마다 실행
                }
            });
        }
        function onPlayerReady(event) {
            console.log('onPlayerReady 실행');

            // 플레이어 자동실행 (주의: 모바일에서는 자동실행되지 않음)
            event.target.playVideo();
        }
        var playerState;
        function onPlayerStateChange(event) {
            playerState = event.data == YT.PlayerState.ENDED ? '종료됨' :
                    event.data == YT.PlayerState.PLAYING ? '재생 중' :
                    event.data == YT.PlayerState.PAUSED ? '일시중지 됨' :
                    event.data == YT.PlayerState.BUFFERING ? '버퍼링 중' :
                    event.data == YT.PlayerState.CUED ? '재생준비 완료됨' :
                    event.data == -1 ? '시작되지 않음' : '예외';

            console.log('onPlayerStateChange 실행: ' + playerState);
        }
    </script>
</body>
</html>










위의 기능을 기본으로 응용을 하면 아래와 같이 제작이 가능하다.

<!--
User: 1004lucifer
Date: 2015. 4. 14.
-->
<!DOCTYPE html>
<html>
<head lang="en">
    <meta charset="UTF-8">
    <title></title>
</head>
<body>
    <div id="gangnamStyleIframe"></div>
    <br/><br/>
    <button type="button" onclick="playYoutube();">Play</button>
    <button type="button" onclick="pauseYoutube();">Pause</button>
    <button type="button" onclick="stopYoutube();">Stop</button>

    <script type="text/javascript">
        /**
         * Youtube 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);

        /**
         * onYouTubeIframeAPIReady 함수는 필수로 구현해야 한다.
         * 플레이어 API에 대한 JavaScript 다운로드 완료 시 API가 이 함수 호출한다.
         * 페이지 로드 시 표시할 플레이어 개체를 만들어야 한다.
         */
        var player;
        function onYouTubeIframeAPIReady() {
            player = new YT.Player('gangnamStyleIframe', {
                height: '315',            // <iframe> 태그 지정시 필요없음
                width: '560',             // <iframe> 태그 지정시 필요없음
                videoId: '9bZkp7q19f0',   // <iframe> 태그 지정시 필요없음
                playerVars: {             // <iframe> 태그 지정시 필요없음
                    controls: '2'
                },
                events: {
                    'onReady': onPlayerReady,               // 플레이어 로드가 완료되고 API 호출을 받을 준비가 될 때마다 실행
                    'onStateChange': onPlayerStateChange    // 플레이어의 상태가 변경될 때마다 실행
                }
            });
        }
        function onPlayerReady(event) {
            console.log('onPlayerReady 실행');
            // 플레이어 자동실행 (주의: 모바일에서는 자동실행되지 않음)
//            event.target.playVideo();
        }
        var playerState;
        function onPlayerStateChange(event) {
            playerState = event.data == YT.PlayerState.ENDED ? '종료됨' :
                    event.data == YT.PlayerState.PLAYING ? '재생 중' :
                    event.data == YT.PlayerState.PAUSED ? '일시중지 됨' :
                    event.data == YT.PlayerState.BUFFERING ? '버퍼링 중' :
                    event.data == YT.PlayerState.CUED ? '재생준비 완료됨' :
                    event.data == -1 ? '시작되지 않음' : '예외';

            console.log('onPlayerStateChange 실행: ' + playerState);

            // 재생여부를 통계로 쌓는다.
            collectPlayCount(event.data);
        }

        function playYoutube() {
            // 플레이어 자동실행 (주의: 모바일에서는 자동실행되지 않음)
            player.playVideo();
        }
        function pauseYoutube() {
            player.pauseVideo();
        }
        function stopYoutube() {
            player.seekTo(0, true);     // 영상의 시간을 0초로 이동시킨다.
            player.stopVideo();
        }
        var played = false;
        function collectPlayCount(data) {
            if (data == YT.PlayerState.PLAYING && played == false) {
                // todo statistics
                played = true;
                console.log('statistics');
            }
        }
    </script>
</body>
</html>






1004lucifer
고급기능
 - 플레이할 영상을 대기열에 넣어서 차례로 플레이가 가능하다.

<!--
User: 1004lucifer
Date: 2015. 4. 14.
-->
<!DOCTYPE html>
<html>
<head lang="en">
    <meta charset="UTF-8">
    <title></title>
</head>
<body>
    <div id="gangnamStyleIframe"></div>
    <br/><br/>
    <button type="button" onclick="playYoutube();">Play</button>
    <button type="button" onclick="pauseYoutube();">Pause</button>
    <button type="button" onclick="stopYoutube();">Stop</button>
    <button type="button" onclick="changeVideoAndStart();">ChangeVideo And Start</button>
    <button type="button" onclick="changeVideoObjectAndStart();">ChangeVideoObject And Start</button>
    <button type="button" onclick="changeVideoListAndStart();">ChangeVideoList And Start</button>
    <button type="button" onclick="changeVideoListObjectAndStart();">ChangeVideoListObject(Video IDs) And Start</button>
    <button type="button" onclick="changeVideoListObjectAndStart2();">ChangeVideoListObject(playlist ID) And Start</button>

    <script type="text/javascript">
        /**
         * Youtube API 로드
         */
        var tag = document.createElement('script');
        tag.src = "http://www.youtube.com/iframe_api";
        var firstScriptTag = document.getElementsByTagName('script')[0];
        firstScriptTag.parentNode.insertBefore(tag, firstScriptTag);

        /**
         * onYouTubeIframeAPIReady 함수는 필수로 구현해야 한다.
         * 플레이어 API에 대한 JavaScript 다운로드 완료 시 API가 이 함수 호출한다.
         * 페이지 로드 시 표시할 플레이어 개체를 만들어야 한다.
         */
        var player;
        function onYouTubeIframeAPIReady() {
            player = new YT.Player('gangnamStyleIframe', {
                height: '315',            // <iframe> 태그 지정시 필요없음
                width: '560',             // <iframe> 태그 지정시 필요없음
                videoId: '9bZkp7q19f0',   // <iframe> 태그 지정시 필요없음
                playerVars: {             // <iframe> 태그 지정시 필요없음
                    controls: '2'
                },
                events: {
                    'onReady': onPlayerReady,               // 플레이어 로드가 완료되고 API 호출을 받을 준비가 될 때마다 실행
                    'onStateChange': onPlayerStateChange    // 플레이어의 상태가 변경될 때마다 실행
                }
            });
        }
        function onPlayerReady(event) {
            console.log('onPlayerReady 실행');
        }
        var playerState;
        function onPlayerStateChange(event) {
            playerState = event.data == YT.PlayerState.ENDED ? '종료됨' :
                    event.data == YT.PlayerState.PLAYING ? '재생 중' :
                    event.data == YT.PlayerState.PAUSED ? '일시중지 됨' :
                    event.data == YT.PlayerState.BUFFERING ? '버퍼링 중' :
                    event.data == YT.PlayerState.CUED ? '재생준비 완료됨' :
                    event.data == -1 ? '시작되지 않음' : '예외';

            console.log('onPlayerStateChange 실행: ' + playerState);

            // 재생여부를 통계로 쌓는다.
            collectPlayCount(event.data);
        }

        function playYoutube() {
            // 플레이어 자동실행 (주의: 모바일에서는 자동실행되지 않음)
            player.playVideo();
        }
        function pauseYoutube() {
            player.pauseVideo();
        }
        function stopYoutube() {
            player.seekTo(0, true);     // 영상의 시간을 0초로 이동시킨다.
            player.stopVideo();
        }
        var played = false;
        function collectPlayCount(data) {
            if (data == YT.PlayerState.PLAYING && played == false) {
                // todo statistics
                played = true;
                console.log('statistics');
            }
        }

        /**
         * loadVideoById 함수는 지정한 동영상을 로드하고 재생한다.
         * 인수구문: loadVideoByUrl(mediaContentUrl:String, startSeconds:Number, suggestedQuality:String):Void
         * 개체구문: loadVideoByUrl({mediaContentUrl:String, startSeconds:Number, endSeconds:Number, suggestedQuality:String}):Void
         * loadVideoById 함수 뿐만 아니라 다른 대체적인 함수들도 개체구문이 기능이 더 많다.
         */
        function changeVideoAndStart() {
            player.loadVideoById("iCkYw3cRwLo", 0, "large");
        }
        function changeVideoObjectAndStart() {
            // 0초부터 10초까지 재생을 시킨다.
            player.loadVideoById({
                'videoId': 'bHQqvYy5KYo',
                'startSeconds': 0,
                'endSeconds': 10
            });
        }

        /**
         * loadPlaylist 함수는 지정한 재생목록을 로드하고 재생한다.
         * 인수구문: loadPlaylist(playlist:String|Array, index:Number, startSeconds:Number, suggestedQuality:String):Void
         * 개체구문: loadPlaylist({list:String, listType:String, index:Number, startSeconds:Number, suggestedQuality:String}):Void
         * [주의: 개체구문의 loadPlaylist 함수에서의 재생목록ID 와 동영상ID 의 사용방법이 다르다.]
         */
        function changeVideoListAndStart() {
            player.loadPlaylist(['wcLNteez3c4', 'LOsNP2D2kSA', 'rX372ZwXOEM'], 0, 0, 'large');
        }
        function changeVideoListObjectAndStart() {
            player.loadPlaylist({
                'playlist': ['9HPiBJBCOq8', 'Mp4D0oHEnjc', '8y1D8KGtHfQ', 'jEEF_50sBrI'],
                'listType': 'playlist',
                'index': 0,
                'startSeconds': 0,
                'suggestedQuality': 'small'
            });
        }
        function changeVideoListObjectAndStart2() {
            player.loadPlaylist({
                'list': 'UUPW9TMt0le6orPKdDwLR93w',
                'listType': 'playlist',
                'index': 0,
                'startSeconds': 0,
                'suggestedQuality': 'small'
            });
        }

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






PS.
API 문서에 loadPlaylist 에 대한 설명 올바르지 않다.
링크: LoadPlaylist function issues of the IFrame API (IFrame API 의 loadPlaylist 함수 이슈)
1004lucifer


 자동재생 및 스크립트 재생
 - 모바일에서는 HTML5 사용자 상호작용(터치)이 없으면 자동재생을 지원하지 않는다고 한다. (링크)

========================
 2019.06.16 추가 (Android 에서 테스트)
 - 현재 크롬에서는 음소거(mute) 설정 시 모바일에서도 자동재생이 가능하다.
  1) 일단 재생을 시키고 스크립트로 player.unMute() 로 음소거를 해제시키니 사용자의 상호작용이 없는 상태에서는 영상이 중지되었다.
  2) touchstart/touchend 이벤트가 발생 시 음소거 해제를 시키도록 테스트해봤는데, touchmove 이벤트가 발생하면 역시 영상이 중지되었다.

결국엔 화면을 터치만하면 소리가 나며 정상적으로 재생되지만 스크롤을 하게되면 영상이 멈추게되어 아직까지 우회할 수 있는 방법은 찾지 못했다.
========================




댓글 없음 :

댓글 쓰기