Стриминг видео mp4(h264), модуль Lighttpd mod_h264_streaming

Категория: / DEV Блог / Сервер (FreeBSD)
Итак, имеем поддомен с выделеным под него ip адресом. Ставится сервер достаточно просто,
перейдем к его конфигурации:

Нам нужно раздавать Mp4 файлы для зарегистрированных пользователей системы.

server.modules           = (
                                "mod_access",
                                "mod_secdownload",
                               "mod_fastcgi",
                                "mod_h264_streaming",
                                "mod_proxy",
                                "mod_redirect",
                                "mod_rewrite",
                                "mod_accesslog" )
server.document-root        = "/home/thumb/lexiclips.com/public_html"
server.errorlog             = "/usr/local/lighttpd/logs/error.log"
index-file.names            = ( "index.php", "index.html",
                                "index.htm", "default.htm" )
 
accesslog.filename = "/usr/local/lighttpd/logs/access.log"
#url.access-deny                = ( "~", ".inc" )
server.port             = 80
server.bind             =  "ip_address"
server.max-keep-alive-requests = 0
server.max-fds = 2048
server.max-write-idle = 30
server.max-worker = 16
 
h264-streaming.extensions = ( ".mp4" )
 
$HTTP["url"] !~ ".(mp4|avi|mpeg|mpg|flv|wmv|php)$"  {
        url.access-deny         = ( "" )
}
 
url.rewrite-once = (
        "^/videos/([^/]+)/(.*)\.mp4\??(start=.*)?" => "/secure/index.php?id=/videos/$1/$2.mp4&$3",
)
 
 
fastcgi.server             = ( ".php" =>
                               ( "localhost" =>
                                 (
                                   "socket" => "/tmp/php-fastcgi.socket",
                                   "bin-path" => "/usr/local/php-cgi/bin/php-cgi",
                                "allow-x-send-file" => "enable"
                                )
                               )
                            )
server.errorlog-use-syslog      = "enable"
accesslog.use-syslog = "disable"


У нас все мувики заворачиваются на файл /secure/index.php

<?php
 
/**
* Uploads checker
*/

 
require "../modules/core/loader.php";
$core = core::get_instance();
 
if (core::lib('auth')->get_user()->is_anonymous()) {
    die('Restricted');
}
else {
   
    // Send file
    $id = functions::request_var('id', '');
   
    $file = loader::get_root() . substr($id, 1);
   
    if (strpos($id, '/videos') !== false
        && ($file = loader::get_root() . substr($id, 1))
        && file_exists($file) && is_readable($file)
    ) {
       
        $mime_type = 'video/H264';
       
        header('Content-disposition: attachment;filename="' . (basename($file)) . '";');
        header('Content-type: ' . $mime_type /*fs::get_mime($file)*/);        
        header('Content-length: ' . filesize($file));        
        header("X-LIGHTTPD-send-file: " . $file);
        die;
    }
    else {
        header(' ', true, 403);
        echo "Restticted";
    }
}


Файл проверяет произвел ли пользователь вход в систему, если да - отдает видео.
Все отлично, за исключением того что не поддерживается навигация (seek) по mp4 файлу.
Скажите кто знает, почему? Ведь судя из описания mod_h264_steraming он должен по параметру start
отдавать ролик со смещением.

Видео конвертировалось по инструкции (http://h264.code-shop.com/trac/wiki/Encoding).

Вот снипет класса работы с видео:

class video_encoder {
 
    protected $ffmpeg_exe   = '/usr/bin/ffmpeg';
    protected $qt_faststart_exe = '/usr/bin/qt-faststart';
    protected $ffmpeg_params;    
    protected $source;
 
 
    /**
    * MP4 h264
    */

    function convert_to_mp4($dst_file, $length = false, $dim = false /*array(320, 240)*/) {
       
        $source = $this->source;
       
        $tmp_file = loader::get_temp() . microtime(true) . '.mp4';
       
        $options = "-vcodec libx264 -b 512k -bf 3 -subq 6 -cmp 256 -refs 5 -qmin 10 \
                   -qmax 51 -qdiff 4 -coder 1 -loop 1 -me hex -me_range 16 -trellis 1 \
                   -flags +mv4 -flags2 +bpyramid+wpred+mixed_refs+brdo+8x8dct \
                   -partitions parti4x4+parti8x8+partp4x4+partp8x8+partb8x8 -g 250 \
                   -keyint_min 25 -sc_threshold 40 -i_qfactor 0.71"
;
 
        if (!empty($dim))
            $options .= " -s {$dim[0]}x{$dim[1]}";
       
        if (!empty($length)) {
            $options .= " -t {$length}";
        }                                                  
                   
        $this->set_params("-y -i \"$source\" -an -pass 1 -threads 2 $options \"$tmp_file\"");
        $this->run();
       
        // libmp3lame
        $acodec = "-acodec libfaac -ar 44100 -ab 96k";
       
        $this->set_params("-y -i \"$source\" {$acodec} -pass 2 -threads 2 $options \"$tmp_file\"");
        $this->run();                          
       
        $this->run_qt_faststart($tmp_file, $dst_file);
       
        @unlink($tmp_file);            
       
        return true;
    }
 
    /**
    * Run    
    */

    protected function run() {            
        $cmd = $this->ffmpeg_exe . ' ' . $this->ffmpeg_params . ' 2>&1';        
        $this->_output = false;
        if ($this->valid_encoder()) {
            $this->_output = shell_exec($cmd);        
        }          
        return $this->_output;
    }
 
    /**
    * qtfast
    */

    protected function run_qt_faststart($in, $out) {
        $cmd = $this->qt_faststart_exe . ' "' . $in . '" "' . $out . '" 2>&1';
       
        // check exists
        preg_match('#^([^\s]+)#', $this->qt_faststart_exe, $matches);
        $check = $matches[1];
        if (file_exists($check) && is_executable($check)) {
             shell_exec($cmd);        
             core::dprint($cmd);
        }    
        else {
            core::dprint('[video_conv] qt_faststart failed', core::E_ERROR);
        }
        return true;
    }    
 
   
    /**
    * Verify all files in right place
    */

    protected function valid_encoder() {
        return (file_exists($this->ffmpeg_exe) && is_executable($this->ffmpeg_exe));
    }
 
}


UPD

В последних версиях ffmpeg'а изменились передаваемые параметы.
Прежний код перестал работать, в часности параметры -loop 1 и -me xxxx.

Рекомендуемый профиль для кодирования Ffmpeg/x264 (profile High, level 3.0) (latest versions of x264).
2-ух уровневый с переменным бит рейтом.

infile ="video.avi"
  tmpfile="video_tmp.mp4"
  outfile="video.mp4"
  options="-vcodec libx264 -b 512k -flags +loop+mv4 -cmp 256 \
           -partitions +parti4x4+parti8x8+partp4x4+partp8x8+partb8x8 \
           -me_method hex -subq 7 -trellis 1 -refs 5 -bf 3 \
           -flags2 +bpyramid+wpred+mixed_refs+dct8x8 -coder 1 -me_range 16 \
          -g 250 -keyint_min 25 -sc_threshold 40 -i_qfactor 0.71 -qmin 10\
           -qmax 51 -qdiff 4"

 
  ffmpeg -y -i "$infile" -an -pass 1 -threads 2 $options "$tmpfile"
 
  ffmpeg -y -i "$infile" -acodec libfaac -ar 44100 -ab 96k -pass 2 -threads 2 $options "$tmpfile"
 
  qt-faststart "$tmpfile" "$outfile"


'qt-faststart' не обязателен к использованию, нужен для индексации файла - возможности использования модулем псевдостримминга.