<Sample Html5 Video>

Source: http://html5demos.com/video

현재시작총 재생 시간

0

0

0

<참고 자료>

- HTML5 Video: https://www.w3.org/2010/05/video/mediaevents.html

- http://eroi.com/ideas/tracking-video-with-google-analytics-part-3-of-3-html5-edition/


<GA 이벤트 구성>

 Event Category 

 Event Action 

 Event Label

Event Value 

 Memo

 HTML5 Video

 Play

 Video Title

 1

 연속 재생할 때 발생하는 이벤트 제외

 HTML5 Video

 Pause

 Video Titile

 1

 연속 재생할 때 발생하는 이벤트 제외

 HTML5 Video

 Seek

 Video Titile

 1

 반복 발생하는 이벤트 제외

 HTML5 Video

 Mute

 Video Titile

 1

 
 HTML5 Video

 Unmute 

 Video Titile 1 
 HTML5 Video Error + Msg Video Titile 1 
 HTML5 Video

 PlayTime

 Video Titile

 순 재생시간

 
 HTML5 Video

 PlayTimeRate

 Video Titile

 순 재생시간 비율

 

실제로 동영상을 재생한 시간만 계산하기 위해 동영상의 play, pause 이벤트와 windows.beforeunload 이벤트 이용

<스크립트>


  (function(){
    // 반복재생(loop)할 경우 이벤트 발생 지점과 동영상 마지막 부분 비교용.
    // 차이가 mindiff(초) 보다 작으면 끝까지 재생한 것으로 처리.
    // mindiff가 너무 작으면 동영상 끝부분 검출이 안될 수 있음.
    var mindiff = 0.5;
    // mute와 같은 수준의 볼륨 정의.
    var minsound = 0.05;
    // 각 동영상에 대한 정보 저장 객체.
    var videos_status = {};    
    var videos = document.getElementsByTagName('video');
    
    function eventHandler(e){ 
      switch(e.type) {     
        // 동영상 정보 로드 후 정보 저장.
        case 'loadeddata':
          // 동영상 총 길이(초).
          videos_status[e.target.id].duration = e.target.duration;
          // 동영상 초기 상태(play, pause, seek).
          videos_status[e.target.id].playstatus = (e.target.paused == true) ? 'pause' : 'play';
          // 동영상 음소거 여부.
          videos_status[e.target.id].muted    = e.target.muted;          
          videos_status[e.target.id].unmuted  = !e.target.muted;
          break;
        case 'timeupdate':           
          // 동영상의 현재 재생 지점(초) 저장. 재생 중 페이지를 떠날 때 총 재생 시간 계산에 사용.
          videos_status[e.target.id].current = e.target.currentTime;
          // 반복재생(loop) 상태인지?
          if (e.target.loop == true) {
              // 동영상 끝에 도달 했는지?
              if (videos_status[e.target.id].loopones == true && Math.abs(e.target.duration - e.target.currentTime) < mindiff) {
                  // 테스트용.
                  console.log('loop ended');
                  document.getElementById('eventlog').innerHTML += '>loop ended ';
                  // 총 재생시간 업데이트. 재생 시작 시간 초기화. 동영상 끝 검출(loopones) 끔.
                  videos_status[e.target.id].totalplaytime += (e.target.duration - videos_status[e.target.id].startpos);
                  videos_status[e.target.id].startpos = 0;
                  videos_status[e.target.id].loopones = false;
              }
              // 동영상을 처음부터 다시 재생 하는지? 동영상 끝 검출(loopones) 켬.
              if (e.target.currentTime < mindiff) videos_status[e.target.id].loopones = true;
          }          
          break;
        case 'play':
          // seek 할 때 발생하는 play 이벤트 제외 필요. 동영상 재생 중 seek하면 pause -> seeked -> play 순으로 이벤트 발생.
          // 현재는 분석 할 때 순수 play 횟수는 play - seek로 계산.          

          // 동영상 재생 상태를 play로 변경.
          videos_status[e.target.id].playstatus = 'play';          
          // 재생 시작 시점을 현재 시점으로 설정.
          videos_status[e.target.id].startpos = e.target.currentTime;

          // 반복재생(loop) 상태에서 발생하는 play 이벤트 제외함. 
          // 이 때문에 동영상 마지막 부분 직전에서 재생을 클릭해도 GA 이벤트 발생하지 않음.
          if ((e.target.loop == false) || (Math.abs(e.target.duration - e.target.currentTime) > mindiff)) {
            dataLayer.push({
              'event': 'Html5VideoEvent',
              'eventCategory': 'HTML5 Video',
              'eventAction': 'Play',
              'eventLabel': videos_status[e.target.id].videotitle,
              'eventValue' : 1
            });
          }
          break;
        case 'pause':
          // seek 할 때 발생하는 pause 이벤트 제외 필요. 
          // 현재는 분석 할 때 순수 pause 횟수는 pause - seek로 계산.

          // 동영상 재생 상태를 pause로 변경.
          videos_status[e.target.id].playstatus = 'pause';          
          
          // 동영상 재생시점과 현재 시점 간 오류가 있는지?
          if (videos_status[e.target.id].startpos > e.target.currentTime) {
            console.log('error : startpos = ' + videos_status[e.target.id].startpos + ', currentTime = ' + e.target.currentTime);
          }
          // 총 재생시간 업데이트.
          videos_status[e.target.id].totalplaytime += (e.target.currentTime - videos_status[e.target.id].startpos);          
          if ((e.target.loop == true) && (Math.abs(e.target.duration - e.target.currentTime) < mindiff)) {
            // 반복재생(loop) 상태에서 발생하는 pause 이벤트 제외함. 
            // 이 때문에 동영상 마지막 부분 직전에서 멈춤을 클릭해도 GA 이벤트 발생하지 않음.                      
          } else {
            // 반복재생(loop) 상태가 아니거나, 현재 재생시점이 mindiff보다 큼 경우만 pause GA 이벤트 발생.
            // 테스트용.
            dataLayer.push({
              'event': 'Html5VideoEvent',
              'eventCategory': 'HTML5 Video',
              'eventAction': 'Pause',
              'eventLabel': videos_status[e.target.id].videotitle,
              'eventValue': 1
            });
          }
          break;
        case 'seeked':           
          // 연속해서 발생한 seek 이벤트 제외. 반복재생(loop) 상태에서 동영상 처음으로 돌아갈 때 발생하는 seeked 이벤트 제외함. 
          // 중지(pause) 상태에서 발생하는 seek도 한번만 발생됨.
          if ((videos_status[e.target.id].playstatus != 'seek') && (videos_status[e.target.id].loopones == true && Math.abs(e.target.duration - e.target.currentTime) > mindiff)) {
            dataLayer.push({
              'event': 'Html5VideoEvent',
              'eventCategory': 'HTML5 Video',
              'eventAction': 'Seek',
              'eventLabel': videos_status[e.target.id].videotitle,
              'eventValue' : 1
            });
            // 동영상 재생 상태를 seek로 변경.
            videos_status[e.target.id].playstatus = 'seek';
          }
          break;
        case 'volumechange':                    
          // 음소거 또는 최소 볼륨(minsound) 미만으로 변경할 때 한 번만 GA 이벤트 발생. (unmute에서 mute로 변경 시 1회 발생)
          if ((videos_status[e.target.id].muted == false) && (e.target.muted == true || e.target.volume < minsound)) {
            videos_status[e.target.id].muted = true;
            videos_status[e.target.id].unmuted = false;
            dataLayer.push({
              'event': 'Html5VideoEvent',
              'eventCategory': 'HTML5 Video',
              'eventAction': 'Mute',
              'eventLabel': videos_status[e.target.id].videotitle,
              'eventValue' : 1
            });
          }
          // 음소거 해제 또는 최소 볼륨(minsound) 이상으로 변경할 때 한 번만 GA 이벤트 발생. (mute에서 unmute로 변경 시 1회 발생)
          if ((videos_status[e.target.id].unmuted == false) && (e.target.muted == false) && (e.target.volume >= minsound)) {
            videos_status[e.target.id].muted = false;
            videos_status[e.target.id].unmuted = true;
            dataLayer.push({
              'event': 'Html5VideoEvent',
              'eventCategory': 'HTML5 Video',
              'eventAction': 'Unmute',
              'eventLabel': videos_status[e.target.id].videotitle,
              'eventValue' : 1
            });
          }
          break;
        case 'error':          
          dataLayer.push({
            'event': 'Html5VideoEvent',
            'eventCategory': 'HTML5 Video',
            'eventAction': 'Error - ' + e.target.error,
            'eventLabel': videos_status[e.target.id].videotitle,
            'eventValue' : 1
          });
          break;
        default:
          break;
      }
    }
    
    function onbeforeunloadHandler(e) {
      for (var v in videos_status) {
        // 페이지를 떠나려고 할 때 동영상이 재생 중이라면 총 재생시간 업데이트.
        if (videos_status[v].playstatus == 'play') {
           videos_status[v].totalplaytime += (videos_status[v].current - videos_status[v].startpos);
        }
        // 재생한 동영상의 총 재생시간 GA 이벤트 발생.
        if (videos_status[v].totalplaytime > 0) {
          // 총 재생시간
          console.log('onbeforeunloadHandler');
          document.getElementById('eventlog').innerHTML += '> onbeforeunloadHandler ';
          dataLayer.push({
            'event': 'Html5VideoEvent',
            'eventCategory': 'HTML5 Video',
            'eventAction': 'PlayTime',
            'eventLabel': videos_status[v].videotitle,
            'eventValue' : Math.round(videos_status[v].totalplaytime)
          });
          // 총 재생시간 비율. 100%가 동영상의 전체 부분을 봤다는 뜻은 아님. 10초 길이 동영상의 0~1초 부분을 10번 봐도 100%가 됨.
          dataLayer.push({
            'event': 'Html5VideoEvent',
            'eventCategory': 'HTML5 Video',
            'eventAction': 'PlayTimeRate',
            'eventLabel': videos_status[v].videotitle,
            'eventValue' : Math.round(100 * videos_status[v].totalplaytime / videos_status[v].duration)
          });
        }
      }
    }
    
    for (var i = 0; i < videos.length; i++) {
      var videoTagId;
      var videoTitle;
      
      // 동영상의 ID 설정.
      if (!videos[i].getAttribute('id')) {
        videoTagId = 'html5_video_' + Math.random().toString(36).slice(2);
        videos[i].setAttribute('id', videoTagId);
      }
      else {
        videoTagId = videos[i].getAttribute('id');
      }
      // 동영상의 제목 설정.
      if (!videos[i].getAttribute('data-title')) {
        videoTitle = videos[i].currentSrc;
      } else {
        videoTitle = videos[i].getAttribute('data-title');  
      }      
      // 동영상 정보 초기화.
      videos_status[videoTagId] = {};
      videos_status[videoTagId].startpos = 0;
      videos_status[videoTagId].current = 0;
      videos_status[videoTagId].playpos = 0;
      videos_status[videoTagId].totalplaytime = 0;
      videos_status[videoTagId].loopones = true;
      videos_status[videoTagId].videotitle = videoTitle;        
      
      // 동영상의 이벤트 핸들러 설정.
      videos[i].addEventListener("loadeddata", eventHandler, false); 
      videos[i].addEventListener("play", eventHandler, false); 
      videos[i].addEventListener("pause", eventHandler, false); 
      videos[i].addEventListener("seeked", eventHandler, false); 
      videos[i].addEventListener("timeupdate", eventHandler, false);
      videos[i].addEventListener("volumechange", eventHandler, false);
      videos[i].addEventListener("error", eventHandler, false);
    } 
    // 페이지의 이벤트 핸들러 설정.beforeunload
    window.addEventListener("beforeunload", onbeforeunloadHandler, false);
  })();

Posted by 값진인생
,