[PHP] HTTP와 PHP를 이용한 html5 video 스트리밍 구현
일단 제목이 무척 거창하다. 필요에 의해서 검색을 하다가 발견한 내용인데 html5에서 지원하는 video 태그를 이용해 비디오 스트리밍을 제공할 때 진행바를 움직이면 일부 브라우저에서 작동이 멈추는 오류가 있어 이를 해결하기 위해 작성한 코드라고 한다. 나도 마침 스트리밍 기능이 필요해서 코드를 사용해봤다.
출처 : Stream videos to HTML5 video container using HTTP & PHP
그런데 나의 경우 위의 코드를 사용했을 때 Header 정보에서 제공하는 Content-Length 정보가 실제 파일 사이즈와 달라 오류가 발생해 동영상 재상이 되지 않는 문제가 있었다. 그래서 Content-Length header 정보를 추가하는 부분의 코드를 약간 수정해서 사용하니 이상없이 동영상이 재생됐다. 수정한 코드는 아래와 같다.
<?php
set_time_limit(0);
include_once('./_common.php');
// Clears the cache and prevent unwanted output
ob_clean();
@ini_set('error_reporting', E_ALL & ~ E_NOTICE);
@apache_setenv('no-gzip', 1);
@ini_set('zlib.output_compression', 'Off');
// 동영상정보
$sql = " select file from movie_info where mv_id = '$mv_id' ";
$row = sql_fetch($sql);
if(!$row)
die('동영상정보가 존재하지 않습니다.');
// Stream videos to HTML5 video container using HTTP & PHP
// http://licson.net/post/stream-videos-php/
$file = './data/movie/'.$row['file']; // The media file's location
if(!is_file($file))
die('파일이 존재하지 않습니다. 사이트 운영자에게 문의해 주십시오.');
$mime = "application/octet-stream"; // The MIME type of the file, this should be replaced with your own.
$size = filesize($file); // The size of the file
// Send the content type header
header('Content-type: ' . $mime);
// Check if it's a HTTP range request
if(isset($_SERVER['HTTP_RANGE'])){
// Parse the range header to get the byte offset
$ranges = array_map(
'intval', // Parse the parts into integer
explode(
'-', // The range separator
substr($_SERVER['HTTP_RANGE'], 6) // Skip the `bytes=` part of the header
)
);
// If the last range param is empty, it means the EOF (End of File)
if(!$ranges[1]){
$ranges[1] = $size - 1;
}
// Send the appropriate headers
header('HTTP/1.1 206 Partial Content');
header('Accept-Ranges: bytes');
// Content-Length 가 맞지 않는 오류가 발생하여 주석처리 후 아래 코드로 대체
//header('Content-Length: ' . ($ranges[1] - $ranges[0])); // The size of the range
header('Content-Length: ' . $size);
// Send the ranges we offered
header(
sprintf(
'Content-Range: bytes %d-%d/%d', // The header format
$ranges[0], // The start range
$ranges[1], // The end range
$size // Total size of the file
)
);
// It's time to output the file
$f = fopen($file, 'rb'); // Open the file in binary mode
$chunkSize = 8192; // The size of each chunk to output
// Seek to the requested start range
fseek($f, $ranges[0]);
// Start outputting the data
while(true){
// Check if we have outputted all the data requested
if(ftell($f) >= $ranges[1]){
break;
}
// Output the data
echo fread($f, $chunkSize);
// Flush the buffer immediately
@ob_flush();
flush();
}
}
else {
// It's not a range request, output the file anyway
header('Content-Length: ' . $size);
// Read the file
@readfile($file);
// and flush the buffer
@ob_flush();
flush();
}
?>
위 코드는 서버에 저장된 파일을 스트리밍해주는 코드일 뿐이며 브라우저에서 동영상을 보여주기 위해서는 아래와 같은 코드를 사용해서 페이지를 작성해줘야 한다.
<video src="http://example.com/movie.php?mv_id=1234" width="720" height="480" controls="controls" preload="none"></video>
이렇게 했을 경우 동영상 파일의 직접적인 URL을 노출하지 않고도 동영상을 사용자에게 보여줄 수 있다. 추가로 MediaElement.js 를 사용하면 좀 더 쉽게 동영상 보기 화면을 만들 수가 있으니 참고하기 바란다.
오류가 난다고 했던 사이즈 부분에서 헤더를 보면 HTTP/1.1 206 Partial Content로 되어있고..
아이폰이나 일부 기기 또는 브라우져에서 통째로 받지 않고 일정구간을 호출하여 받는 부분이 있는데, 이런 경우에 사이즈를 파일 사이즈로 대체하게되면 실제로 전송되는 사이즈와 맞지 않아 오류가 발생하는 이슈가 있어서.. 요청한 구간의 크기를 헤더로 셋팅해야하는데.. 문제 없던가요?
안녕하세요.
2014년도 포스트로 그 당시에는 아이폰을 사용하지 않아서 테스트 자체를 하지 못했습니다.
최신의 브라우저에서는 오류가 발생할 수도 있을 것 같습니다. 해당 코드는 더 이상 사용하지 않고 있기 때문에 더 이상의 유지보수는 불가능한 점 양해바랍니다.