mod_flvx. Передача потокового Flash видео


Недавно на своем блоге Paul Querna выложил собственный модуль к Apache 2 для потоковой передачи Flash видео.
Конфигурация модуля производится очень просто:
AddHandler flv-stream .flv
Сам модуль очень компактный - размер исходного текста всего около 3 Кб. Теперь давайте посмотрим, что и как он делает.

Основа модуля - обработчик хука Handler. Это единственный хук, который обрабатывается в этом модуле. Вот его обработчик:
static int flvx_handler(request_rec *r)
{
if ((!r->handler) ||
(strcmp(r->handler, FLVX_HANDLER))) {
return DECLINED;
}
r->allowed |= (AP_METHOD_BIT << M_GET);
if (r->method_number != M_GET) {
return HTTP_METHOD_NOT_ALLOWED;
}
return drive_flvx(r);
}
В нем происходит две проверки: действительно ли данный запрос должен обрабатываться этим обработчиком (для установления связи "запрос-обработчик" и нужна директива AddHandler) и разрешен ли метод GET.

Затем вызывается главная функция модуля drive_flvx(r), в качестве аргумента которой передается структура request_rec, которая описывает обрабатываемый запрос. (Подробнее о request_rec).
Теперь рассмотрим функцию drive_flvx. Она довольно большая, поэтому описание оформил в виде комментариев:
static int drive_flvx(request_rec *r)
{
apr_finfo_t fi;
apr_bucket_brigade *bb;
apr_off_t offset = 0;
apr_off_t length = 0;
apr_file_t *fp = NULL;
apr_status_t rv = APR_SUCCESS; //APR_SUCCESS = 0 - ошибок нет.
// Получаем информацию о запрошенном flv файле.
// Функция apr_stat возвращает структуру apr_finfo_t
// из которой нам нужен размер файла - поле size.
// С полным описанием этой структуры можно ознакомиться тут.

rv = apr_stat(&fi, r->filename, APR_FINFO_SIZE, r->pool);
if (rv) {
// В случае ошибки передаем обработку ядру
// сервера - вдруг у него что-нибудь да получится :)

return DECLINED;
}
// открываем файл
rv = apr_file_open(&fp, r->filename, APR_READ,
APR_OS_DEFAULT, r->pool);
if (rv) {
// Доступ запрещен -> пишем в лог и возвращаем HTTP код 403 FORBIDDEN.
ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r,
"file permissions deny server access: %s", r->filename);
return HTTP_FORBIDDEN;
}
// Анализируем запрос на наличие смещения (ну а какая
// потоковая передача да без возможности смещения?!!).
// Функцию get_start(r) рассмотрим ниже.

offset = get_start(r);
// Определяем длину блока, необходимого для
// передачи: "длина файла - смещение".

if (offset != 0 && offset < fi.size) {
length = fi.size - offset;
}
else {
length = fi.size;
}
// Создаем бригаду для хранения передаваемых данных
bb = apr_brigade_create(r->pool, r->connection->bucket_alloc);
// Если передача файла требуется не с начала
// (т.е. задано ненулевое смещение), то сперва
// записываем в бригаду заголовок flv
// файла -
FLVX_HEADER, иначе файл не будет
// корректно обработан проигрывателем Flash видео.
if (offset != 0) {
rv = apr_brigade_write(bb, NULL, NULL, FLVX_HEADER, FLVX_HEADER_LEN);
if (rv) {
ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r,
"unable to write flv header in brigade");
return HTTP_INTERNAL_SERVER_ERROR;
}
}
// Затем записываем в бригаду сам файл (или нужный его кусок).
apr_brigade_insert_file(bb, fp, offset, length, r->pool);
// Устанавливаем нужный Content-type
ap_set_content_type(r, "video/x-flv");
// И все. Отправляем подготовленную бригаду вниз
// по цепочке выходных фильтров. Готово.

return ap_pass_brigade(r->output_filters, bb);
}
С основной функцией разобрались. Осталось только посмотреть, как же задается смещение в потоке. Место, с которого надо передавать файл, определяет функция get_start(r):
static apr_off_t get_start(request_rec *r)
{
apr_off_t start = 0;
char *p = NULL;
if (!r->args) {
return start;
}
p = strstr(r->args, "start=");
if (p) {
p = p + 6;
apr_strtoff(&start, p, NULL, 10);
}
return start;
}
Единственное, что она делает, это возвращает значение аргумента start, значение которого определяет смещение в файле в байтах. Например, следующий запрос:
http://somehost/flash-video/clip.flv?start=255
запрашивает файл clip.flv с 266 байта. Если start не задан, то файл передается полностью.
Ну, вот и все. Пользуйтесь на здоровье. Исходный код модуля полностью можно взять отсюда.
И напоследок несколько ссылок по теме потокового Flash видео:

These icons link to social bookmarking sites where readers can share and discover new web pages.
  • News2.ru
  • NewsLand.ru
  • del.icio.us
  • BobrDobr.ru
  • Ma.gnolia
  • Digg
  • Reddit
  • Technorati
  • Slashdot
  • Netscape
  • DZone
  • ThisNext
  • Furl
  • YahooMyWeb
Опубликовано в: Модули Apache Июль 13, 2006

14 Комментариев »

  1. как его можна собрать правильно?

    Комментарий от Andrew — Июль 27, 2006 @ 4:55 pm

  2. Добавляем заголовочные файлы:
    #include “httpd.h”
    #include “http_core.h”
    #include “http_config.h”
    #include “http_protocol.h”
    #include “http_log.h”
    #include “apr_buckets.h”
    и компилим как shared объект.
    Заголовочные файлы можно найти в исходниках/бинарниках Apache

    Комментарий от Администратор — Июль 27, 2006 @ 5:26 pm

  3. сорри… собрал с помощью apxs. только оказалось что оно вроде как не для 2.0.* - с ним апач не стартует, а для 2.2.*

    Комментарий от Andrew — Июль 27, 2006 @ 5:58 pm

  4. Ну да, я тоже сперва попытался загрузить для 2.0, но дело в том, что в этом модуле используется функция apr_brigade_insert_file (вроде из-за нее были проблемы, если не ошибаюсь), а она появилась только в APR-util версии 1.1.0. Не знаю, совместима ли новая APR* с предыдущими версиями Apache. Но, в любом случае можно использовать функцию apr_bucket_file_create. Для примера посмотрите модуль mod_txt: /resources/mod_txt.c

    Комментарий от Администратор — Июль 27, 2006 @ 10:59 pm

  5. At first, I think this mod can help me to handle huge .swf on my web that cause problem to small pc.
    The mod can compile under apache2.0 but cannot resolve several symbols. So, I compile it in apache 2.2
    BUT after no error, seem nothing happen even I try to put ?start=25555 but the whole .flv was sent to the clients.
    Please advise me to what should be doing.
    Thank You,
    supat
    ps. I compile with apxs -i -a -c options.

    Комментарий от supat — Август 13, 2006 @ 5:28 am

  6. FLV-Scrubber 2.0 не работает с mod_flvx. Помогает вот такой патч:
    — mod_flvx.orig 2006-07-11 09:25:59.000000000 +0400
    +++ mod_flvx.c 2007-03-08 04:43:46.000000000 +0300
    @@ -95,6 +95,7 @@
    apr_brigade_insert_file(bb, fp, offset, length, r->pool);
    ap_set_content_type(r, “video/x-flv”);
    + ap_set_content_length(r, length);
    return ap_pass_brigade(r->output_filters, bb);
    }

    Комментарий от juunitaki — Март 8, 2007 @ 4:57 am

  7. При попытке компиляции в XAMPP (LAMP) под ОС Windows XP командой apxs -i -a -c mod_flvx.c получаю собщение об ошибке (запрашивает *.mk файл).
    Где скачать откомпилированный файл как это можно сделать самому?

    Комментарий от Дмирий — Апрель 3, 2007 @ 10:07 pm

  8. А есть у кого-либо уже собранный (откомпиленный) под винду пакетик?
    Если выложите линку, буду очень благодарен…..
    ICQ: 219505427

    Комментарий от Андрей — Декабрь 12, 2007 @ 10:30 pm

  9. И мне пакет пожалуйста спасибо
    ICQ: 349506453

    Комментарий от Миха — Февраль 12, 2008 @ 10:54 pm

  10. и мне тогда уж,если не жалко 351264912

    Комментарий от sergey — Февраль 20, 2008 @ 1:50 am

  11. в модуле допущено ряд ошибок:
    не делается проверка на offset > fi.size

    - if (offset != 0 && offset fi.size) {
    + offset = fi.size;
    + }
    + length = fi.size - offset;

    к длине контента не прибавляется длина заголовка FLV

    - ap_set_content_length(r, length);
    + ap_set_content_length(r, length + FLVX_HEADER_LEN);

    а это точно не помешает
    + ap_update_mtime(r, r->finfo.mtime);
    + ap_set_last_modified(r);

    Комментарий от techman — Март 14, 2008 @ 12:22 pm

  12. пост поломался
    первую часть кода читать так:
    - if (offset != 0 && offset fi.size) {
    + offset = fi.size;
    + }

    Комментарий от techman — Март 14, 2008 @ 12:25 pm

  13. извините за мусор - не смог опубликовать код
    ломается при посте
    суть кода заключается в более тщательной проверке (и выравниванию) offset за выход из диапазона 0 > offset

    Комментарий от techman — Март 14, 2008 @ 12:29 pm

  14. А смещение в потоке можно ещё как-то по-другому задать, а то функция get_start(r) у меня почему-то не выполняется?

    Комментарий от Матвей — Апрель 2, 2009 @ 6:19 pm

Оставить комментарий

You must be logged in to post a comment.

Работает на WordPress