본문 바로가기
Lecture/Javascript-기초

php + javascript 를 이용한 html5 video 자막 삽입

by 알 수 없는 사용자 2012. 6. 28.
반응형

사실 자막을 입힌다기보다 레이어를 얹고 레이어의 투명도를 조정한뒤 그 레이어에 문자를 적어넣는 방식입니다. 물론 자바스크립트를 이용한 기술이긴하지만 기존에 setInterval등의 외부에서 작동되는식의 시스템콜을 방법을 활용한것입니다.

일단 자바스크립트만으로 파일을 읽는것 자체가 매우 위험한 요소이기때문에(엑티브x를 이용하지 않기위함) php를 활용하여 자막을 읽습니다. php를 활용한다는것자체가 서버쪽에 부담이 되긴하지만 자막이 그렇게 긴것도 아니고 루프를 한번만 돌면 끝나기때문에 그리 큰 문제는 아닙니다.
먼저 php 코드부분입니다.

 <?

header('Content-Type: text/html; charset= UTF-8');
$id = $_POST["id"];
$smi_name = $_POST["smi_name"];
$filepath = "../User/".$id."/".$smi_name;
echo $filepath;

$fp = fopen($filepath , "r");
if(!$fp){
echo "못읽음";
die;
}
$index = -1; //행의 개수
$col_time = 0; //시간을 담는 행
$col_text = 1; //문자를 담는 행
$text = "";
echo "<textarea id='smi_box'>";
while(!feof($fp)){
$line = fgets($fp , 1024);
$encoding = iconv('EUC-KR','UTF-8',$line)==$line ? 'UTF-8' : 'EUC-KR'; //인코딩 정보 확인
if($encoding == "EUC-KR"){
$line = iconv('EUC-KR' , 'UTF-8' , $line);
//대소문자를 가리지않고 <sync를 찾음
if(stristr($line , "<sync")){
$index++;
$text = "";
$start = strpos($line , "=")+1;
$end = strpos($line , ">");
$time = substr($line , $start , $end-$start);
if(strchr($time , " ")){
$time = substr($time ,0, strpos($time , " "));
}
$smi[$index][$col_time] = $time;
$text = strstr($line , ">");
$smi[$index][$col_text] = substr($text , 1 , strlen($text));
}else{
$smi[$index][$col_text] = $smi[$index][$col_text].$line;
}
}
for($i=0; $i<$index; $i++){
echo "$!".$smi[$i][$col_time]."$/".$smi[$i][$col_text]."\n";
}
echo "</textarea>";
?>


상단에 변수들은 무시하시고 중요한건 $filepath 입니다. 파일 경로만 잘지정해주시면 자막을 가져오는일은 문제가아닙니다.

자막을 가져오는 첫번째 단계는 textarea내부에 자막을 서술합니다. 이렇게한후 자바스크립트로 해당 값을 참조해서 자막을 읽을수 있기때문입니다. 코드는 별로 어려운것이 아니나 인코딩설정부분은 유의깊게 봐주시길 바랍니다. 문서들이 여러개의 인코딩으로 되어있기때문에 인코딩만 잘못하면 한글이 깨지기 쉽상입니다.

textarea에 적히는 자막은 $!시간$/자막\n 으로 적혀지며 이렇게 $! , $/를 주는 이유는 자바스크립트로 이것들을 스플릿하기 위해서입니다. 

이제 자바스크립트 코드부분입니다.

function get_smi(){
var text = document.getElementById("smi_box").value;
smi_ary = text.split("$!");
var smi = new Array(smi_ary.length);

for(var i=1; i<smi_ary.length; i++){
time_text = smi_ary[i].split("$/");
smi[i-1] = new Array(2);
smi[i-1][0] = time_text[0];
smi[i-1][1] = time_text[1];
}
localStorage.setObject("smi" , smi);
}

엘리먼트를 얻어오는 부분은 당연히 textarea가 되겠고 스플릿하는 과정과 스플릿된 시간+자막을 다시한번 스플릿해서 객체에 담는 루틴입니다. 이렇게 해당 객체를 어디서나 사용가능하게 저는 localStorage에 담아두었습니다.
[로컬스토리지는 기본적으로 key-value이므로 객체를 담기위해서는 프로퍼티 확장해야합니다]
<스토리지에 객체를 담을 수 있도록 확장>

Storage.prototype.setObject = function(key , value){
this.setItem(key , JSON.stringify(value));
};
Storage.prototype.getObject = function(key){
return this.getItem(key) && JSON.parse(this.getItem(key));
};


이제 자막을 영상의 시간에 맞추어 재생시키는 일만 남았습니다. 다생스럽게도 video태그에 timeupdate라는 이벤트때문에 아주 쉽게 구현이 가능합니다.... 만, 매번 이벤트가 실행될때마다 자막의 개수만큼 반복문을 돌려야하는 절차가 있기때문에 그닥 좋지 않은 알고리즘임을 먼저 말씀드립니다^^;;

 function stat_smi(){

var subtitle = document.getElementById("subtitle");
subtitle.innerHTML = "";
var video = document.getElementById("player");
video.addEventListener("timeupdate" , function(){
//시간을 밀리세컨드단위까지만 표현해야함.
var time = Math.floor(video.currentTime)*1000;
var smi = localStorage.getObject("smi");
if(!smi) return;
for(var i=0; i<smi.length; i++){
var smi_sec = Math.floor(smi[i][0]/1000)*1000;
if(time == smi_sec){
subtitle.innerHTML = smi[i][1];
}
}
}); 
}

video에 이벤트가 발생될때마다 함수를 실행시키는데 로컬스토리지에서 smi 오브젝트를 가져와서 해당 오브젝트에 시간과 현제 video태그의 재생시간을 밀리세컨드로 비교하여 일치할경우 해당 시간의 자막을 자막 레이어에 입히는 루틴입니다.

이것으로 자막입히기 끝입니다. ㅋ 

자막입힌 영상 모습입니다.