[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 를 사용하면 좀 더 쉽게 동영상 보기 화면을 만들 수가 있으니 참고하기 바란다.

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.