ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • CSS3와 jQuery로 커스텀 HTML5 비디오 플레이어 만들기
    카테고리 없음 2021. 4. 29. 09:00

    이것도 리서치하면서 번역한 것


    CSS3와 jQuery로 커스텀 HTML5 비디오 플레이어 만들기

    원문: Building a Custom HTML5 Video Player with CSS3 and jQuery by Ionuț Colceriu, 28 July 2010

    도입부

    이미 대부분의 최신 버전 브라우저에서 HTML5 <video> 요소를 지원하고 있습니다. 심지어 IE 역시 버전 9에서 지원 중입니다. 브라우저가 자체적으로 비디오를 관장함에 따라 (이에 대해서는 Bruce Lawson이 HTML5 비디오 안내 글에 잘 설명해 두었습니다) 생기는 여러 장점이 있으므로, 많은 개발자들이 최대한 일찍 사용을 시도해보리라 예상합니다. 아직 넘어야 할 몇 가지 장벽이 남아있기는 한데, 대부분은 오페라/파이어폭스와 IE/사파리 브라우저 간의 지원 코덱 불일치 때문에 발생하는 것들입니다. 그러나 최근 구글에서 VP8 코덱을 배포하였고, WebM 프로젝트가 출범하기 직전이기 때문에 그다지 오랫동안 문제가 되지는 않을 것입니다. 오페라, 파이어폭스, 크롬, IE9 모두 최종 빌드나 개발자용 빌드에서 VP8을 지원하거나 지원하겠다고 선언이라도 해 둔 상태이며, 플래시에서도 VP8 재생이 가능해질 것입니다. 즉, 대부분의 브라우저에서 <video> 요소에서 재생할 소스를 한가지 버전으로만 가지고 있어도 되는 쪽으로 상황이 바뀐다는 얘기입니다. 브라우저에서 webM을 지원하지 않는 경우에는 플래시 플레이어를 사용하면 됩니다.

    또 다른 주요 장벽은 바로 커스텀 HTML5 <video> 플레이어 제작입니다. 아직까지 플래시만 사용해서 커스텀 플레이어와 관련된 문제를 해결하는 쪽이 편리합니다. 플래시 IDE의 강력한 기능 덕분에 커스텀 비디오 플레이어 컴포넌트를 만들기 위한 편리한 인터페이스를 사용할 수 있기 때문입니다. 만약 HTML5 <video> 요소에서 사용할 커스텀 비디오 플레이어를 만들고 싶다면, 이와 관련된 HTML5, CSS3, 자바스크립트 뿐만 아니라 플레이어에 적용하고 싶은 다른 웹 표준 기술 관련 코드도 직접 짜야 합니다!

    그런 면에서 이 글이 도움이 될 것입니다. 본 글은 손쉽게 커스터마이징 가능한 HTML5 <video> 플레이어 제작에 대해 알아보는 시리즈의 첫 번째 글입니다. 컨트롤 타입을 선택하고 상황에 따라 적용 CSS를 변경할 수 있는 간단한 jQuery 플러그인으로 패키징하는 방법까지 다룹니다. 이 글의 목차는 다음과 같습니다.

    1. 비디오 컨트롤
    2. 컨트롤 기본 마크업
    3. jQuery 플러그인으로 플레이어 패키징하기
    4. 룩앤필
    5. 플레이어 테마 설정

    DOM 조작을 쉽게 하기 위해 jQuery를 사용하겠습니다. 그리고 재생 시점 탐색에 필요한 슬라이더 컨트롤과 볼륨 레벨 설정 변경 영역에는 jQuery UI를 사용하겠습니다. 추후에도 얼마든지 재사용이 가능하도록 jQuery 플러그인으로 플레이어 전체를 감쌀 것입니다.

    비디오 컨트롤

    프로 웹 디자이너들은 대부분 브라우저에 상관 없이 항상 똑같은 모습을 유지하는 비디오 플레이어를 만들고 싶어 합니다. 파이어폭스와 크롬의 플레이어는 미니멀한 디자인을 보여주는 데 비해 오페라와 사파리의 플레어는 좀 더 반짝이는 컨트롤을 제공하는 등 브라우저마다 플레이어 생김새가 제각각 입니다. (그림 1에 브라우저 별 컨트롤이 나와있습니다.) 만약 모든 브라우저에 걸쳐 컨트롤 생김새를 일관성있게 나타나도록 하고 싶고, 웹 페이지 디자인에도 어울리게 만들고 싶다면 컨트롤을 밑바닥부터 직접 만들어야 합니다. 생각보다는 어렵지 않는 일입니다.

    ![](https://dev.opera.com/articles/custom-html5-video-player-with-css3-and-jquery/native-video-controls.jpg)
    그림 1: 브라우저 마다 각기 다른 외관을 가지는 기본 비디오 컨트롤

    컨트롤 기본 마크업

    일단 비디오 컨트롤의 뼈대가 될 마크업 부터 작성해야 합니다. 재생/일시 정지 버튼, 탐색바, 타이머, 볼륨 버튼과 슬라이더를 만들어야 합니다. <video> 요소 다음에 컨트롤 마크업을 넣도록 하겠습니다. 그리고 전체를 한번 감싸주도록 요소를 하나 사용하겠습니다.

    <div class="ghinda-video-controls">
        <a class="ghinda-video-play" title="Play/Pause"></a>
        <div class="ghinda-video-seek"></div>
        <div class="ghinda-video-timer">00:00</div>
        <div class="ghinda-volume-box">
            <div class="ghinda-volume-slider"></div>
            <a class="ghinda-volume-button" title="Mute/Unmute"></a>
        </div>
    </div>

    하나의 페이지에서 비디오 플레이어를 여러 개 띄울 필요가 있을 때, 코드를 재사용할 수 있도록 모든 요소에 ID 대신 클래스 속성을 사용하였습니다.

    jQuery 플러그인으로 플레이어 패키징하기

    마크업 작성을 마쳤으니, 이제는 이를 미디어 요소 API와 연결하여 비디오 조작을 할 수 있도록 만드는 작업을 할 차례입니다. 미리 말했듯이 여러 요소에서 쉽게 재사용 할 수 있도록 플레이어를 jQuery 플러그인으로 패키징 할 예정입니다.

    글쓴이 메모: 여러분이 이미 jQuery 플러그인과 자바스크립트에 대한 기본 지식을 가지고 있다 가정하고 글을 진행하겠습니다. 그래서 여기서는 스크립트에 대한 설명은 간략하게만 하겠습니다. 만약 이 두 주제에 대해 좀 더 알고 싶다면 Craig Buckler의 jQuery 플러그인 개발 방법 튜토리얼을 참고하시거나, 오페라 웹 표준 커리큘럼 중 자바스크립트 섹션을 참고해주세요.

    $.fn.gVideo = function(options) {
        // 요소 순회를 하기 전에 메인 옵션 빌드
        var defaults = {
            theme: 'simpledark',
            childtheme: ''
        };
        var options = $.extend(defaults, options);
        // 매칭되는 요소를 찾아서 포맷 재설정
        return this.each(function() {
            var $gVideo = $(this);
            // HTML 구조 생성
            // 메인 래퍼
            var $video_wrap = $('<div></div>').addClass('ghinda-video-player').addClass(options.theme).addClass(options.childtheme);
            // 컨트롤 래퍼
            var $video_controls = $('<div class="ghinda-video-controls"><a class="ghinda-video-play" title="Play/Pause"></a><div class="ghinda-video-seek"></div><div class="ghinda-video-timer">00:00</div><div class="ghinda-volume-box"><div class="ghinda-volume-slider"></div><a class="ghinda-volume-button" title="Mute/Unmute"></a></div></div>');
            $gVideo.wrap($video_wrap);
            $gVideo.after($video_controls);
        });
    };  

    여기서는 jQuery로 비디오 플레이어 마크업(플레이어 ‘자체’가 아니라)을 동적으로 생성합니다. 그리고 스크립트 로드가 완료되면 controls 속성이 제거되도록 했습니다. 만약에 이런 조치를 취해두지 않았는데 만약 사용자가 자바스크립트 사용을 막아둔 상태라면 비디오 요소 자체 컨트롤은 물론이거니와 커스텀 컨트롤의 쓸모가 없어지기 때문입니다. 스크립트 로드가 실패하는 경우를 대비해 controls 속성이 보이도록 설정한 후, 오직 스크립트 로드가 완료된 후에만 커스텀 컨트롤을 사용하도록 이를 제거하는 편이 더 합리적인 선택인 것 같습니다.

    다음으로 컨트롤의 각 요소를 변수에 담아 두어서 이벤트 리스너를 추가할 수 있도록 합니다.

    // 새로 생성한 요소를 변수에 할당합니다
    var $video_container = $gVideo.parent('.ghinda-video-player');
    var $video_controls = $('.ghinda-video-controls', $video_container);
    var $ghinda_play_btn = $('.ghinda-video-play', $video_container);
    var $ghinda_video_seek = $('.ghinda-video-seek', $video_container);
    var $ghinda_video_timer = $('.ghinda-video-timer', $video_container);
    var $ghinda_volume = $('.ghinda-volume-slider', $video_container);
    var $ghinda_volume_btn = $('.ghinda-volume-button', $video_container);
    
    $video_controls.hide(); // 컨트롤은 숨겨둡니다

    각각의 컨트롤은 클래스를 사용해 지정해 두었습니다. 모든 준비가 완료되기 전까지 컨트롤은 숨겨두도록 하겠습니다.

    그리고 이제는 재생/일시정치 컨트롤을 다뤄 볼 차례입니다.

    var gPlay = function() {
        if($gVideo.attr('paused') == false) {
            $gVideo[0].pause();
        } else {
            $gVideo[0].play();
        }
    };
    
    $ghinda_play_btn.click(gPlay);
    $gVideo.click(gPlay);
    
    $gVideo.bind('play', function() {
        $ghinda_play_btn.addClass('ghinda-paused-button');
    });
    
    $gVideo.bind('pause', function() {
        $ghinda_play_btn.removeClass('ghinda-paused-button');
    });
    
    $gVideo.bind('ended', function() {
        $ghinda_play_btn.removeClass('ghinda-paused-button');
    });

    대부분의 브라우저가 비디오 요소 우클릭 후 나타나는 컨텍스트 메뉴에서 2차 비디오 컨트롤을 제공합니다. 여기서 나타나는 컨트롤도 커스텀 컨트롤에 넣을 예정이므로, 사용자가 부가 컨트롤을 활성화 한다면 자칫 우리가 만든 커스텀 컨트롤이 깨질 위험이 있습니다. 이 상황을 피하기 위해 재생/일시정지 버튼 자체에 이벤트를 붙일 것이며, 비디오 플레이어에는 “재생”, “일시 정지”, “끝났음” 리스너를 붙이겠습니다.

    버튼의 외관을 변경하기 위해서 클래스를 추가하고 제거하는데, 이는 비디오의 상태(재생중 혹은 일시정지)에 따라서 바뀌도록 하겠습니다.

    탐색 슬라이더는 jQuery UI 슬라이더 컴포넌트를 써서 만들겠습니다.

    var createSeek = function() {
        if($gVideo.attr('readyState')) {
            var video_duration = $gVideo.attr('duration');
            $ghinda_video_seek.slider({
                value: 0,
                step: 0.01,
                orientation: 'horizontal',
                range: 'min',
                max: video_duration,
                animate: true,
                slide: function(){
                    seeksliding = true;
                },
                stop:function(e,ui){
                    seeksliding = false;
                    $gVideo.attr('currentTime', ui.value);
                }
            });
            $video_controls.show();
        } else {
            setTimeout(createSeek, 150);
        }
    };
    
    createSeek();

    보다시피 비디오의 readyState(준비 상태)를 계속 읽어 들이는 재귀 함수를 작성했습니다. 폴링 방식을 적용하여 비디오가 준비될 때까지 그 상태를 계속해서 체크합니다. 이렇게 하지 않으면 비디오 재생 시간을 알 수 없기 때문에 슬라이더를 만들 수가 없습니다. 비디오 준비가 끝나면 슬라이더를 초기화 한 후에 컨트롤을 노출합니다.

    다음으로 타이머를 만들어서 비디오 요소의 timeupdate 리스너에 가져다 붙이겠습니다.

    var gTimeFormat = function(seconds){
        var m = Math.floor(seconds / 60) < 10 ? '0' + Math.floor(seconds / 60) : Math.floor(seconds / 60);
        var s = Math.floor(seconds - (m * 60)) < 10 ? '0' + Math.floor(seconds - (m * 60)) : Math.floor(seconds - (m * 60));
        return m + ':' + s;
    };
    
    var seekUpdate = function() {
        var currenttime = $gVideo.attr('currentTime');
        if(!seeksliding) $ghinda_video_seek.slider('value', currenttime);
        $ghinda_video_timer.text(gTimeFormat(currenttime));
    };
    
    $gVideo.bind('timeupdate', seekUpdate);

    seekUpdate 함수를 써서 비디오의 currentTime 속성을 얻어오고 gTimeFormat 함수를 써서 얻은 값의 형식을 변환합니다.

    볼륨 컨트롤에도 jQuery UI 슬라이더를 사용하겠습니다. 비디오 음소거 및 음소거 해제 버튼은 직접 만든 함수를 사용해서 제어하겠습니다.

    $ghinda_volume.slider({
        value: 1,
        orientation: 'vertical',
        range: 'min',
        max: 1,
        step: 0.05,
        animate: true,
        slide:function(e,ui){
            $gVideo.attr('muted', false);
            video_volume = ui.value;
            $gVideo.attr('volume', ui.value);
        }
    });
    
    var muteVolume = function() {
        if($gVideo.attr('muted')==true) {
            $gVideo.attr('muted', false);
            $ghinda_volume.slider('value', video_volume);
    
            $ghinda_volume_btn.removeClass('ghinda-volume-mute');
        } else {
            $gVideo.attr('muted', true);
            $ghinda_volume.slider('value', '0');
    
            $ghinda_volume_btn.addClass('ghinda-volume-mute');
        };
    };
    
    $ghinda_volume_btn.click(muteVolume);

    이제 커스텀 컨트롤 세팅을 마치기도 했거니와 브라우저 기본 컨트롤 대신 이를 사용하고 싶기도 하므로 <video>에서 controls 속성을 제거하도록 하겠습니다.

    $gVideo.removeAttr('controls’);

    플러그인 생성이 끝났으므로 아무 비디오 요소에서 이를 아래와 같이 호출할 수 있습니다.

    $('video').gVideo();

    위 코드를 통해 비디오 요소에 플러그인이 생성됩니다.

    룩앤필(Look and Feel)

    자 이제 재미있는 일을 해 볼 차례인데, 바로 비디오 플레이어의 룩앤필 부분입니다. 플러그인 준비가 다 된 상태에서 약간의 CSS만 더해준다면 아주 손쉽게 컨트롤 커스터마이징이 가능합니다. 알고 계실 테지만 지금까지는 컨트롤에 아무 스타일도 입히지 않은 상태입니다. CSS3를 사용해서 플레이어와 관련된 커스터마이징을 전부 해보도록 하겠습니다.

    제일 먼저, 메인 비디오 플레이어 컨테이너에 스타일을 입혀보겠습니다. 여기서 해주는 스타일링이 플레이어의 메인 GUI(혹은 chrome이라고도 함)가 될 것입니다.

    
    .ghinda-video-player {
        float: left;
        padding: 10px;
        border: 5px solid #61625d;
    
        -moz-border-radius: 5px; /* FF1+ */
        -ms-border-radius: 5px; /* IE future proofing */
        -webkit-border-radius: 5px; /* Saf3+, Chrome */
        border-radius: 5px; /* Opera 10.5, IE 9 */
    
        background: #000000;
        background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0, #313131),color-stop(1, #000000)); /* Saf4+, Chrome */
        background-image: -moz-linear-gradient(top, #313131, #000000); /* FF3.6 */
        background-image: linear-gradient(#313131, #000000);
    
        box-shadow: inset 0 15px 35px #535353;
    }

    메인 컨테이너를 왼쪽으로 플로팅시켜 두었는데, 이는 컨테이너가 플레이어의 가로 너비로 꽉 차게 확장되는 것을 방지하기 위함입니다. 대신 실제 비디오 요소의 너비에 제약을 받도록 했습니다. 광을 좀 내보려고 그라디언트를 추가했고, 테두리도 좀 둥글게 만들었습니다. 오페라 브라우저에서는 아직 그라디언트를 지원하지 않기 때문에(이 글을 쓰는 시점에서는 버전 10.60이 최신 버전입니다), 이를 흉내내서 보여주기 위해 안쪽 박스 그림자도 추가했습니다.

    다음으로는 모든 컨트롤을 수평으로 정렬하기 위해 왼쪽 플로팅을 적용합니다. 멋진 마우스 호버 효과를 만들어 내기 위해 재생/일시정지와 볼륨 음소거/음소거 해제 버튼에 투명도와 트랜지션을 적용하겠습니다.

    .ghinda-video-play {
        display: block;
        width: 22px;
        height: 22px;
        margin-right: 15px;
        background: url(../images/play-icon.png) no-repeat;
    
        opacity: 0.7;
    
        -webkit-transition: all 0.2s ease-in-out; /* Safari and Chrome */
        -moz-transition: all 0.2s ease-in-out; /* Firefox */
        -ms-transition: all 0.2s ease-in-out; /* IE future proofing */
        -o-transition: all 0.2s ease-in-out; /* Opera */
        transition: all 0.2s ease-in-out;
    }
    
    .ghinda-paused-button {
        background: url(../images/pause-icon.png) no-repeat;
    }
    
    .ghinda-video-play:hover {
        opacity: 1;
    }

    자바스크립트 파트를 잘 보셨다면 비디오 상태(재생중/일시정지)에 따라 재생/일시정지 버튼에 클래스를 추가 및 제거 되도록 한 것이 기억 날 것입니다. 바로 그 이유 때문에 ghida-paused-button 클래스가 ghinda-video-play 클래스의 배경 속성을 덮어쓰도록 했습니다.

    이젠 슬라이더 차례입니다. 알고 계실 테지만 우리는 탐색 바와 볼륨 레벨을 둘 다 jQuery UI 슬라이더 컨트롤로 만들었습니다. 이 컴포넌트와 관련된 고유 스타일은 jQuery UI 스타일시트에 정의가 되어 있으나, 이를 완전히 다른 코드로 덮어써서 플레이어의 나머지 부분과 슬라이더의 외관이 잘 어울려 보이도록 만들겠습니다.

    .ghinda-video-seek .ui-slider-handle {
        width: 15px;
        height: 15px;
        border: 1px solid #333;
        top: -4px;
    
        -webkit-border-radius:10px;
        -moz-border-radius:10px;
        -ms-border-radius:10px;
        border-radius:10px;
    
        background: #e6e6e6;
        background-image: -webkit-gradient(linear,left top,left bottom,color-stop(0, #e6e6e6),color-stop(1, #d5d5d5));
        background-image: -moz-linear-gradient(top, #e6e6e6, #d5d5d5);
        background-image: linear-gradient(#e6e6e6, #d5d5d5);
    
        box-shadow: inset 0 -3px 3px #d5d5d5;
    }
    
    .ghinda-video-seek .ui-slider-handle.ui-state-hover {
        background: #fff;
    }
    
    .ghinda-video-seek .ui-slider-range {
        -moz-border-radius:15px;
        -ms-border-radius:15px;
        -webkit-border-radius:15px;
        border-radius:15px;
    
        background: #4cbae8;
        background-image: -webkit-gradient(linear,left top,left bottom,color-stop(0, #4cbae8),color-stop(1, #39a2ce));
        background-image: -moz-linear-gradient(top, #4cbae8, #39a2ce);
        background-image: linear-gradient(#4cbae8, #39a2ce);
    
        box-shadow: inset 0 -3px 3px #39a2ce;
    }

    지금은 볼륨 슬라이더가 볼륨 버튼 옆에 위치해 있으면서 시종일관 보이는 상태입니다. 슬라이더의 노출 상태 기본값을 비노출로 바꾸고 음소거/비음소거 버튼 위에 마우스를 가져다 댈때만 노출되도록 바꾸겠습니다. 좀 더 역동적이고 깔끔해 보이도록 말입니다. 여기서도 트랜지션을 사용하도록 하겠습니다.

    .ghinda-volume-box {
        height: 30px;
    
        -moz-transition: all 0.1s ease-in-out; /* Firefox */
        -ms-transition: all 0.1s ease-in-out; /* IE future proofing */
        -o-transition: all 0.2s ease-in-out; /* Opera */
        -webkit-transition: all 0.1s ease-in-out; /* Safari and Chrome */
        transition: all 0.1s ease-in-out;
    }
    
    .ghinda-volume-box:hover {
        height: 135px;
        padding-top: 5px;
    }
    
    .ghinda-volume-slider {
        visibility: hidden;
        opacity: 0;
    
        -moz-transition: all 0.1s ease-in-out; /* Firefox */
        -ms-transition: all 0.1s ease-in-out; /* IE future proofing */
        -o-transition: all 0.1s ease-in-out; /* Opera */
        -webkit-transition: all 0.1s ease-in-out; /* Safari and Chrome */
        transition: all 0.1s ease-in-out;
    }
    
    .ghinda-volume-box:hover .ghinda-volume-slider {
        position: relative;
        visibility: visible;
        opacity: 1;
    }

    볼륨 슬라이더는 기본적으로 숨겨져 있는 상태이고, 볼륨 버튼의 너비와 어울리는 고정 높이값을 볼륨 컨테이너의 높이로 주었습니다. 그리고 두 요소 모두 트랜지션을 적용했습니다.

    볼륨 버튼 위에 마우스를 가져다대면, 이미 지정해 둔 트랜지션 속성에 따라 높이가 늘어납니다. 그리고 나서 자손 선택자인 .ghinda-volume-box:hover .ghinda-volume-slider를 사용해서 볼륨 슬라이더가 나타나도록 하겠습니다.

    CSS 기본 지식과 신규 CSS3 속성 몇개만 사용했을 뿐인데 벌써 멋진 플레이어 인터페이스 만들기가 끝났네요. 인터페이스는 그림2와 같이 생겼습니다.

    ![](https://dev.opera.com/articles/custom-html5-video-player-with-css3-and-jquery/dark-player-shot.jpg)
    그림 2: 직접 만든 플레이어 인터페이스

    플레이어 테마 설정

    jQuery 플러그인을 만들 때 몇가지 디폴트 옵션을 정의 했던 것을 기억하시고 계실 것입니다. 옵션 이름은 themechildtheme인데, 이는 플러그인 호출 시에 변경할 수 있고 원하는 대로 커스텀 테마를 손쉽게 적용할 수 있습니다.

    테마를 바꾼다는 것은 각각의 컨트롤 하나하나 마다 CSS 선언을 완전히 다시 하는 것입니다. 반대로 자식 테마를 바꾸면 현재 테마 CSS를 기반으로 선언이 이루어지는데, “부모” 테마 스타일에 추가로 선언을 하거나 이미 존재하는 스타일 선언을 덮어쓰는 것입니다.

    jQuery 플러그인 호출 시 두가지 옵션 모두 써주어도 되고, 하나만 써줘도 됩니다.

    $('video').gVideo({
        childtheme:'smalldark'
    });

    위의 코드에서는 smalldark 자식 테마를 옵션값으로 넣어서 플러그인을 호출합니다. 이로 인해 기본 부모 테마가 설정된 다음 그 위에 자식 테마가 적용이 되며, 부모 테마 스타일 규칙 중 적은 부분이 덮어써지게 됩니다. 그림 3에 SmallDark 테마가 적용된 플레이어 모습이 나와있습니다.

    ![](https://dev.opera.com/articles/custom-html5-video-player-with-css3-and-jquery/smalldark-child-theme.jpg)
    그림 3: 실제 적용 된 SmallDark 테마

    최종 라이브 비디오 플레이어 예시에 두 테마를 모두 적용해 두었으니 확인해 보시면 됩니다.

    HTML5 <video>와 관련 기술을 사용해 실제 개발에서 응용할 수 있는 수준의 jQuery 플러그인을 만들어 보았습니다. Acorn 미디어 플레이어 깃허브 저장소에서 최신 버전 플레이어를 볼 수 있고, 개발 진행 과정도 살펴보실 수 있습니다. 코드는 계속 활발하게 유지보수 되고 있으며 최신 버전으로 유지중입니다.

    정리

    HTML5 비디오, 자바스크립트, CSS3를 사용해 우리만의 커스텀 비디오 플레이어를 만드는 일은 꽤 쉽습니다. 오직 컨트롤의 실제적인 기능을 구현하는 데에만 자바스크립트를 사용하고, 플레이어의 룩앤필과 관련된 모든 부분은 CSS3를 통해 구현한다면 매우 활용성 높고 손쉽게 커스터마이징 할 수 있는 플레이어 소스를 만들어낼 수 있습니다.

    HTML5 비디오 관련 자료

Designed by Tistory.