Пользователь
0,0
рейтинг
12 декабря 2012 в 10:28

Разработка → Сервер онлайн-вещаний на базе nginx

Введение


Привет всем! Несколько месяцев назад на Хабре была опубликована статья «Вещание онлайн-видео с помощью nginx» , в которой Aecktann рассказал о своем опыте внедрения разрабатываемого мной модуля к nginx для вещания видео — nginx-rtmp-module. С тех пор продукт активно развивался и в этой статье я более подробно расскажу о нем.

Вещатель нужен для передачи видео-потока клиенту. Речь идет либо о живом потоке, либо о вещании записанного видео (VOD, Video-on-demand). Существует большое количество технологий вещания видео. Среди них можно выделить традиционные протоколы, такие как RTMP или MPEG-TS, а также появившиеся в последнее время технологии адаптивного вещания поверх HTTP. К последним относятся HLS (Apple), HDS (Adobe), Smooth Streaming (Microsoft), MPEG-DASH. При выборе технологии основным фактором является ее поддержка на клиентской стороне. Именно поэтому вещание в формате RTMP на текущий момент является одним из самых распространенных. Протокол HLS поддерживается устройствами компании Apple, а также некоторыми версиями Android.

Сборка и настройка nginx-rtmp


Для добавления модуля nginx-rtmp к nginx нужно указать его в опции --add-module при конфигурации nginx, как и любой другой модуль.

./configure --add-module=/path/to/nginx-rtmp-module

После сборки и инсталляции нужно добавить секцию rtmp{} в файл конфигурации nginx.conf. Добавлять ее надо в корень конфига. Например:

rtmp {
    server {
        listen 1935;
        application myapp {
            live on;
        }
    }
}

Для многих случаев этой простой конфигурации будет достаточно. В ней задается RTMP-приложение с именем myapp. В это приложение мы позже будем публиковать потоки и проигрывать их из него. У каждого потока также будет свое уникальное имя. Стоит отметить один важный нюанс, касающийся приведенной выше конфигурации. Она верна для того случая, когда число воркеров nginx равно единице (как правило, задается в начале nginx.conf).

worker_processes  1;

Чтобы иметь возможность использовать live вещания с бОльшим числом воркеров, нужно указать директиву rtmp_auto_push on (см. раздел «Воркеры и локальная ретрансляция»).

Публикация и проигрывание живого потока


Для публикации и проигрывания видео можно использовать Flash-проигрыватели (JWPlayer, FlowPlayer, Strobe и т.д.). Однако, для вещания серверных потоков и для тестирования часто используют ffmpeg (и ffplay). Начнем вещание тестового файла test.mp4 следующей командой:

ffmpeg -re -i /var/videos/test.mp4 -c copy -f flv rtmp://localhost/myapp/mystream

Тут надо учесть, что RTMP поддерживает ограниченый набор кодеков, впрочем, такие популярные кодеки, как H264 и AAC, входят в их число. Если кодеки в тестовом файле не совместимы с RTMP, потребуется перекодировка:

ffmpeg -re -i /var/videos/test.mp4 -c:v libx264 -c:a libfaac -ar 44100 -ac 2 -f flv rtmp://localhost/myapp/mystream

Вещать можно как поток из файла, так и из другого источника. Например, если предположить, что по адресу video.example.com/livechannel.ts вещается некий live MPEG-TS поток, то его также можно завернуть в rtmp:

ffmpeg -i http://video.example.com/livechannel.ts -c copy -f flv rtmp://localhost/myapp/mystream

Пример вещания с локальной веб-камеры:

ffmpeg -f video4linux2 -i /dev/video0 -c:v libx264 -an -f flv rtmp://localhost/myapp/mystream

Проиграть поток можно при помощи ffplay следующей командой:

ffplay rtmp://localhost/myapp/mystream

И, наконец, простой пример использования JWPlayer для проигрывания потока из браузера (полностью приведен в директории /test/www модуля):

<script type="text/javascript" src="/jwplayer/jwplayer.js"></script>
<div id="container">Loading the player ...</div>
    <script type="text/javascript">
        jwplayer("container").setup({
        modes: [
            {   type: "flash",
                src: "/jwplayer/player.swf",
                config: {
                    bufferlength: 1,
                    file: "mystream",
                    streamer: "rtmp://localhost/myapp",
                    provider: "rtmp",
                }
            }
        ]
});
</script>


Видео по запросу


Модуль поддерживает вещание видео-файлов в форматах mp4 и flv. Пример настройки:

application vod {
    play /var/videos;
}

При проигрывании, соответственно, надо указывать имена файлов, в остальном все аналогично живому вещанию.

ffplay rtmp://localhost/vod/movie1.mp4
ffplay rtmp://localhost/vod/movie2.flv


Ретрансляция


При построении распределенных систем важно иметь возможность ретрансляции потоков для балансировки нагрузки
по большому числу серверов. Модуль реализует два типа ретрансляции: push и pull. Первый тип ретрансляции
состоит в передаче на удаленный сервер локально публикуемого потока, а второй — в передаче удаленного
потока на локальный сервер. Пример push ретрансляции:

application myapp {
    live on;
    push rtmp://cdn.example.com;
}

В момент, когда начинается публикация по адресу rtmp://localhost/myapp/mystream, создается соединение с удаленным сервером и поток mystream публикуется далее на rtmp://cdn.example.com/myapp/mystream. При прекращении локальной публикации, соединение с cdn.example.com автоматически завершается.

Pull-ретрансляции выполняют обратную операцию:

application myapp {
    live on;
    pull rtmp://cdn.example.com;
}

В этом примере при появлении клиента, желающего локально проиграть поток rtmp://localhost/myapp/mystream, будет создано соединение с rtmp://cdn.example.com/myapp/mytstream и удаленный поток будет ретранслирован на локальный сервер, после чего станет доступным всем локальным клиентам. В тот момент, когда не останется ни одного клиента, соединение будет завершено.

Вещание на мобильные устройства (HLS)


Для вещания на устройста iPhone/iPad, а также на новые версии Android, используется протокол HLS (HTTP Live Streaming).
Протокол был разработан компанией Apple и представляет из себя «нарезанный» на куски MPEG-TS/H264/AAC поток, отдаваемый по HTTP. К потоку прилагается плейлист в формате m3u8. Отдавать HTTP nginx умеет отлично. Значит, надо лишь создать и обновлять плейлист и фрагменты HLS-потока, а также следить за удалением старых фрагментов. Для этого существует модуль nginx-rtmp-hls. Он находится в директории hls, однако не собирается по умолчанию т.к. требует библиотеки libavformat, входящей в пакет ffmpeg. Для сборки nginx с поддержкой HLS, надо добавить этот модуль явно при конфигурации:

./configure --add-module=/path/to/nginx-rtmp-module --add-module=/path/to/nginx-rtmp-module/hls

Так вышло, что некоторое время назад проект ffmpeg был форкнут. И теперь у нас есть два проекта — ffmpeg и avconv, а следовательно, тут же начали возникать проблемы совместимости (вернее, несовместимости) библиотек. Для сборки nginx-rtmp нужен оригинальный ffmpeg. В то же время, некоторые дистрибутивы Linux перешли на использование avconv, который для сборки не подойдет. На этот случай я написал подробную инструкцию.

Для генерации HLS достаточно указать следующие директивы:

application myapp {
    live on;
    hls on;
    hls_path /tmp/hls;
    hls_fragment 5s;
}

Ну и, наконец, в секции http{}, настроить отдачу всего, что связано с HLS:

location /hls {
    root /tmp;
}

Теперь публикуем поток mystream в приложение myapp, а в браузере iPhone набираем в строке адреса example.com/hls/mystream.m3u8. Кроме того, поток можно встроить в html тег video:

<video width="600" height="300" controls="1" autoplay="1" src="http://example.com/hls/mystream.m3u8"></video>

Отмечу, что для проигрывания на iPhone поток должен быть закодирован в H264 и AAC. Если исходный поток не соответствует этим условиям, необходимо настроить перекодирование.

Перекодирование


При вещании видео часто возникает необходимость перекодирования входящего потока в другое качество, либо другие кодеки. Эта задача по своей сути кардинально отличается от раздачи RTMP и, в отличие от последней, связана с высокими нагрузками на CPU, большим и активным потреблением памяти, часто опирается на использование многопоточности и является потенциально нестабильной. По этой причине она не должна включаться в основной процесс сервера, и в идеале должна выполняться отдельным процессом. Стоит отметить, что прекрасный инструмент для решения этой задачи уже существует — это все тот же ffmpeg. Он поддерживает огромное число кодеков, форматов и фильтров, позволяет использовать множество сторонних библиотек. Вместе с тем, он достаточно прост и активно поддерживается сообществом. Модуль nginx-rtmp предоставляет простой интерфейс для использования ffmpeg. Директива exec позволяет запустить внешнее приложение в момент публикации входящего потока. При завершении публикации приложение также принудительно завершается. Кроме того, поддерживается перезапуск запущенного приложения, если оно внезапно завершилось само.

application myapp {
    live on;
    exec ffmpeg -i rtmp://localhost/myapp/$name -c:v flv -c:a -s 32x32 -f flv rtmp://localhost/myapp32x32/$name;
}

application myapp32x32 {
    live on;
}

В этом примере ffmpeg используется для перекодирования входящего видео в Sorenson-H263, изменении его размера до 32х32 и публикации результата в приложение myapp32x32. Можно одновременно задать несколько директив exec, которые будут производить с потоком любые преобразования и публиковать результат в другие приложения как на локальный, так и на удаленный сервер. Директива поддерживает несколько переменных, среди которых $app (имя приложения) и $name (имя потока).

Воркеры и локальная ретрансляция


Как известно, nginx является однопоточным сервером. Для того, чтобы эффективно использовать все ядра современных процессоров, обычно он запускается в несколько воркеров. Обработка HTTP запросов как правило, происходит независимо друг от друга, и лишь в отдельных случаях (как, например, в случае с кешом), требуется осуществлять доступ к общим данным. Такие данные хранятся в разделяемой памяти.

При живом вещании ситуация иная. Все клиентские соединения, проигрывающие поток, очевидным образом зависят от соединения, публикующего этот поток. Использование разделяемой памяти в данном случае неэффективно, излишне трудоемко, привело бы к синхронизации и большой потере производительности. Поэтому для использования нескольких воркеров был реализован механизм внутренних ретрансляций через UNIX-сокеты. Собственно, такие ретрансляции практически ничем не отличаются от обычных внешних push-ретрансляций. Локальные ретрансляции включаются следующей директивой

rtmp_auto_push on;

Указывать ее надо в корневой секции конфигурационного файла. Отмечу, что локальные ретрансляции нужны только для живых вещаний.

Запись


Часто возникает необходимость записи на диск публикуемых потоков. Модуль позволяет записывать как отдельные данные из потока (аудио, видео, ключевые фреймы) так и поток целиком. Можно установить ограничение на размер файла, а также на число записываемых фреймов. Следующий пример включает запись первых 128К каждого потока.

record all;
record_path /tmp/rec;
record_max_size 128K;

Запись происходит в формате flv в директорию /tmp/rec.

Управлять записью можно в ручном режиме, включая и отключая ее при помощи http-запроса. Для этого используется control-модуль. Информацию о нем можно найти на сайте проекта.

Авторизация и бизнес-логика


Во многих случаях требуется ввести ограничения или учет операций публикации и проигрывания видео. Это бывает связано с логикой проекта, в котором он используется. Самый частый подобный случай — необходимость авторизации пользователя перед тем, как дать ему доступ к просмотру видео. Чтобы интегрировать бизнес-логику проекта в вещатель, в модуле реализованы HTTP-колбэки, такие как on_publish и on_play. Серверный код получает всю имеющуюся информацию о клиенте, включая его адрес, имя потока, адрес страницы и т.д. Если возвращается HTTP статус 2xx, то колбек считается завершенным успешно и работа клиента продолжается. В противном случае соединение разрывается.

on_publish http://example.com/check_publisher;
on_play http://example.com/check_player;


Статистика


В каждый момент времени к вашему серверу могут быть подсоединены тысячи клиентов. Естественно, нужен интерфейс, позвляющий увидеть их список, а также все основные характеристики публикуемых или проигрываемых ими потоков. Причем, важно, чтобы эту информацию можно быть как анализировать визуально, так и обрабатывать программно. Такой интерфейс у модуля nginx-rtmp существует. Чтобы его использовать, необходимо в http-секции nginx.conf прописать следующие директивы.

location /stat {
    rtmp_stat all;
    rtmp_stat_stylesheet stat.xsl;
}

location /stat.xsl {
    root /path/to/stat.xsl/dir/;
}

Директива rtmp_stat включает отдачу XML-документа с полным описанием live-клиентов, публикующих или проигрывающих потоки, списком приложений и серверов. Этот документ удобен для программной обработки, но для визуального анализа совершенно не годится. Чтобы иметь возможность просматривать список клиентов в браузере, директивой rtmp_stat_stylesheet задается относительный путь к таблице стилей XML (stat.xsl). Этот файл лежит в корне проекта. Надо настроить nginx на его раздачу по указанному урлу. Результат можно просматривать в браузере.

Существует возможность явно разрывать клиентские соединения. Для этого используется control-модуль, не описанный в статье.

Простое Интернет-радио


С самого начала статьи я постоянно употреблял слово «видео». Конечно, модуль может вещать не только видео, а также и аудио-потоки. Вот простой пример интернет-радиостанции на bash, вещающей mp3-файлы из /var/music. Этот поток может воспроизводить простой JWPlayer, встроенный в веб-страницу.

while true; do
    ffmpeg -re -i "`find /var/music -type f -name '*.mp3'|sort -R|head -n 1`" -vn -c:a libfaac -ar 44100 -ac 2 -f flv rtmp://localhost/myapp/mystream;
done


Совместимость


Модуль совместим со всем основным софтом, работающим с протоколом RTMP, включая FMS/FMLE, Wowza, Wirecast, протестирован с самыми распространенными флеш-проигрывателями JWPlayer, FlowPlayer, StrobeMediaPlayback, а также отлично работает с ffmpeg/avconv и rtmpdump.

Нагрузки


Модуль использует асинхронную однопоточную модель сервера nginx. Это позволяет добиться высокой производительности. Мы используем модуль на машинах Intel Xeon E5320/E5645 в режиме одного воркера. В этом режиме удается достигнуть максимальной пропускной способности имеющихся сетевых карт — 2Gbps. Пользователи модуля подтверждают сохранение этого же соотношения (2Gbps на ядро) в режиме локальной ретрансляции с несколькими воркерами. Практика показывает, что производительность вещателя обычно упирается в сеть, а не в CPU.

Прямых сравнений с другими продуктами я не проводил, однако, «тяжелые» многопоточные FMS, Wowza и Red5, будучи более функциональными, должны, в силу особенностей реализации, существенно проигрывать моему решению по числу одновременно подсоединенных клиентов и нагрузке на CPU. Это подтверждается многими пользователями, проводившими такие сравнения, в т.ч. в уже упомянутой мной статье.

Заключение


В заключение скажу, что модуль распространяется по лицензии BSD. Он собирается и работает под Linux, FreeBSD и MacOS. В статье описана лишь малая часть функционала nginx-rtmp-module. Желающие могут ознакомиться с проектом по ссылкам, приведенным ниже.


Буду рад, если читателям Хабра проект покажется интересным.

Всем спасибо!
Роман Арутюнян @rarutyunyan
карма
82,0
рейтинг 0,0
Реклама помогает поддерживать и развивать наши сервисы

Подробнее
Реклама

Самое читаемое Разработка

Комментарии (99)

  • –5
    здорово что хлс сделан! осталось еще сильверлайт прикрутить и будет универсальный комбайн.
    • +1
      Справедливости ради, универсальным комбайн (при всех его текущих достоинствах) станет, когда RTMPT поддерживать будет. А это случится очень нескоро, если случится вообще. It's not planned for the near future.
  • 0
    А требования к дисковой какие? Сколько иопсов генерится при двухгигабитной отдаче?
    • 0
      Требования к дискам имеют место быть лишь при использовании HLS. Собственно требования напряму. зависят от вашего потока :) Вообще логичнее размещать HLS location в tmpfs.
      • 0
        Да, имелось в виду VoD. При нескольких терабайтах контента в tmpfs не позасовываешь.
        • 0
          Для террабайт, имхо, иные решения нужно использовать…
  • 0
    nginx aio поддерживается?
    • 0
      нет
      • +3
        Жаль. При сильных нагрузках на дисковую помогает реально. Без него как в дисковую упирается сервер, сильно снижается скорость отдачи.
        • 0
          простите, а чем именно aio помогает, если мы упёрлись в диски?
          • 0
            Позволяет не блокировать процесс нгинкса на время операции ввода-вывода.
            Т.е. в случае без аио процесс ждёт выполнения операции чтения или записи с диска, вместо отдачи статики например из озу.
            • 0
              ну aio в linux требует открытия файла с O_DIRECT, так что никакого кеширования в VFS у вас не будет.
              на freebsd с этим лучше.
              • 0
                Кроме того, открытие файла в Linux в любом случае синхронно. При большой иерархии это имеет значение, получите кучу синхронных рандомных сиков. А при синхронном чтении через пейджкеш срабатывает readahead, так что в реальные тормоза не такие уж и большие.
                • 0
                  ну, не каждый open() приводит к сикам. и речь не о синхронном чтении, а о O_DIRECT.

                  The whole notion of «direct IO» is totally braindamaged. Just say no.

                  This is your brain: O
                  This is your brain on O_DIRECT:.

                  Any questions?

                  I should have fought back harder. There really is no valid reason for EVER
                  using O_DIRECT. You need a buffer whatever IO you do, and it might as well
                  be the page cache. There are better ways to control the page cache than
                  play games and think that a page cache isn't necessary.

                  So don't use O_DIRECT. Use things like madvise() and posix_fadvise()
                  instead.

                  Linus
                  • 0
                    Кстати одна дама из команды разработчиков ядра несколько лет назад расшила таки синхронные операции ввода-вывода с использованием т.н. retry-model. Т.е. вместо ожидания появления на странице прочитанных с блочного устройства данных у нее происходил полный возврат с некоторым кодом (готово- не готово). Таким образом, у нее заработали асинхронные операции через пейджкеш. Но ее патч выкинули в конце концов.
              • 0
                Ну я глубоко не копал, но эффект проседания скорости отдачи при 100% нагрузке дисковой наблюдал на практике. Проседало сильно, примерно на 30%. Centos 5.8
            • 0
              проще и правильнее воркеров побольше завести.
              тем более, что при вышеописанной ситуации у вас уже куда большие проблемы имеются и решать надо их(например, через flashcache).
              • 0
                Побольше это сколько? На машине которая проседала было 8 воркеров. Какие там ещё проблемы то? Фешкэш хорош для БД. Но для VOD не очень.
  • 0
    Артур, спасибо как за само решение,
    так и за статью: технология вещания расписана очень позновательно.

    при использовании обязательно возникнут вопросы.
  • 0
    Отличное решение, и очень доступное!

    Скажите, а возможно ли вещать из MJPEG-источника?
    • +4
      Вы про ip-камеру?
      Используйте ffmpeg, который будет читать mjpeg, перекодировать и вещать на сервер.

      Как-то так:
      ffmpeg -i http://camera.example.com/cam.mpjg -c:v libx264 -an -f flv rtmp://localhost/myapp/mystream
      • 0
        Класс, попробую реализовать. А то камеру дёргают 4 клиента одновременно и кладут её…
        • 0
          простое проксирование http в случае с ip камерой скорее всего поможет.
          • 0
            Увы, только вещание — при проксировании на каждого клиента поднимается отдельный поток на камеру — и она падает…
          • 0
            не спасёт. надо вещать в hls и использовать кеширование на уровне nginx.
            • 0
              Ваш HLS нужен только фанбоям одной фирмочки из купертино, для устройств с закругленными уголками. Во всех остальных случаях вещать в HLS не надо.
              • 0
                FYI: hls умеет играть vlc, html5-плеер в android 4, jwplayer. ну и яблочные продукты.

                ваша альтернатива? расскажете про mpeg dash, который чёрт знает когда появится?
                • 0
                  Если какой-то плеер умеет играть HLS, то это не значит, то он больше ничего не умеет. Единственное место, где нужен HLS — это говнодевайсы с закругленными уголками. Во всем остальном мире HLS — это нестандартное решение, которое опционально поддерживают некоторые продукты, причем делают это на свой страх и риск.

                  О альтернативах я кратенько писал: habrahabr.ru/post/153817/

                  Про jwplayer вообще смешно, пойду тоже какую-то хрень на флеше сделаю, может быть ее тоже будут приводить в аргументах.
                  • 0
                    ещё раз: какие альтернативы, если вы вещаете видео и хотите поддержку максимального количества платформ?

                    чтобы нормально работало из-за NAT/HTTP Proxy и дружило с CDN?
  • 0
    Не посдкажите как с помощью этого модуля проще всего транслировать очередь из видео-файлов?
    • +2
      Аналогично примеру с интернет-радио

      for file in /var/videos/*; do 
          ffmpeg -re -i "$file" -c:v libx264 -c:a libfaac -ar 44100 -ac 2 -f flv rtmp://localhost/myapp/mystream;
      done
      
      • 0
        Подскажите, как решить проблему с переходом между файлами? Дело в том, что ffmpeg обрывает соединение в конце файла и модуль шлет плееру NetStream.Play.Stop и как следствие, обрыв вещания. Как сделать непрерывный поток?
        • 0
          play_restart off;

          но это происходит не во всех плеерах
  • 0
    Интересно, планируется ли поддержка воспроизведения VOD через HLS с нарезкой на фрагменты на лету. Как это делает вовза. Было бы здорово.
    • 0
      Я думаю над этим. Но изначально цель была имнно в live вещании. Строго говоря, для VOD/HLS и медиа-сервер не нужен.
      • 0
        Но тем не менее, nginx-rtmp уже и не только лайв и не только rtmp. И как раз этой маленькой фичи не хватает для получения медиакомбайна.
        А на самом деле продукт замечательный, присматриваемся к возможности использовании его на продакшне в CDNе…
      • 0
        Поддерживаю. Очень хочется такую фичу, чтобы была радость для iPad-ов без необходимости хранения дополнительной кучи чанков на сервере.
        • 0
          Меня часто просят об этом. В планах это есть. Осталось найти красивое решение.
          • 0
            Уж лучше стандарт использовать, чем неутвержденный драфт. Это я на rtsp намекаю, который я частично сделал в айскасте, но потом забил.
  • 0
    Очень круто! red5 слишком сложен и тяжел для моих целей, а вот такой модуль — то что нужно. Сейчас переделываю своё приложение и возник вопрос — в пределах одного соединения red5 позволял одновременно производить публикацию live-потока и воспроизводить его же (пытаюсь сделать loopback с вебкамеры, идущий через сервер — для тестовых целей). При использовании данного модуля — публикация проходит, что видно при использовании record, но поток не воспроизводится. Хотел узнать — было ли так задумано. Хотя, мне это не мешает — просто отдаю и принимаю поток через два разных соединения.

    Отличный модуль.
    • 0
      Да, действительно, такой номер не пройдет. Может быть в будущих версиях я реализую это.
      Спасибо
      • 0
        А я долго искал причину, даже засел со сниффером
      • 0
        Очень слоу пост. Тоесть нельзя принимать RTMP поток, писать его в файл и ретрансилировать по HLS?
  • 0
    Так как в гугле меня забанили, а на контакты вы не отвечаете, то спрошу здесь.

    Во-первых, как мне отловить конец стрима?
    Клиент получает NetStream.Play.Start и потом с переменным успехом NetStream.Buffer.Full и NetStream.Buffer.Empty
    События Stop или Unpublish я не получаю, хотя при этом поток-источник может рватся любое количество раз.
    В принципе, в самом флеш-плеере все хорошо, а вот rtmpdump (и мое приложение с librtmp) от такого сходят с ума и не могут доливать поток после того, как тот был возобновлен. Особенно плохо моему приложению, которое не может дождаться конца потока. В принципе, можно было бы выставить таймаут, но это не вариант для живых аудиотрансляций — флеш при пропадании звука (уменьшении амплитуды сигнала меньше порогового уровня) просто перестает отсылать байтики — стрим вроде как и есть, он НЕ закрыт, но потока и нет. Отличить от случая, когда клиент закрыл браузер, я никак не могу.

    Далее, дабы далеко не ходить, сама librtmp иногда сыпит ошибками:
    ERROR: DECODING ERROR, IGNORING BYTES UNTIL NEXT KNOWN PATTERN!
    ERROR: DECODING ERROR, IGNORING BYTES UNTIL NEXT KNOWN PATTERN!
    ERROR: DECODING ERROR, IGNORING BYTES UNTIL NEXT KNOWN PATTERN!
    ERROR: DECODING ERROR, IGNORING BYTES UNTIL NEXT KNOWN PATTERN!
    ERROR: DECODING ERROR, IGNORING BYTES UNTIL NEXT KNOWN PATTERN!
    ERROR: DECODING ERROR, IGNORING BYTES UNTIL NEXT KNOWN PATTERN!
    ERROR: DECODING ERROR, IGNORING BYTES UNTIL NEXT KNOWN PATTERN!
    ERROR: DECODING ERROR, IGNORING BYTES UNTIL NEXT KNOWN PATTERN!
    ERROR: HandleMetadata, error decoding meta data packet

    Возникает вопрос — а сам поток хоть как-то проходит валидацию?
    Возможно это как-то связано с клиентом-транслятором? Или, предположим, с лагами сети?
    В общем, даже заявленный вами rtmpdump, иногда не желает работать.

    В качестве источника пробовал wirecast + xsplit, других софтин для композа изображения и трансляции не знаю (ffmpeg умеет только хватать картинку, но не умеет делать композ изображения). Замечены подобные ошибки были при использовании wirecast, возможно дело в нем, однако вы заявляете совместимость и с ним. Если посоветуете другие бродкастеры — буду благодарен.

    Ну и немного по статье:

    > Директива exec позволяет запустить внешнее приложение в момент публикации входящего потока

    Не знаю как у вас, а вот у меня ffmpeg очень любит долго читать rtmp-поток, а уже потом, переварив примерно 5 метров, начинать его процессинг. Если вещать только голос с микрофона, то это может занять минут 15. Причина тому — размер буффера в функции, где происходит детект потока. Победил написанием своего велосипеда, заполняя структуры потоков вручную, без использования автодетекта от libavcodec/libavformat.

    > ffmpeg -re -i /var/videos/test.mp4 -c copy -f flv rtmp://localhost/myapp/mystream

    А кто занимается таймингом потока в этом случае? По крайней мере раньше, ffmpeg отправлял все что есть на FMS на максимальной скорости, клиент за ним конечно же не успевал. Пришлось написать велосипед, который читал FLV по байтикам, смотрел на таймштамп пакетов и отправлял на сервер с задержкой, синхронизировавшись с локальными часами:

    ffmpeg -i test.mp4 -c copy -f flv — | myStreamer target

    > on_publish example.com/check_publisher;

    А почему нельзя сделать авторизацию внутри сервера? Мне вот крайне неприятно дергать что-то по http, мне как минимум надо поднимать вебсервер и писать веб-приложение для этого. Хотел было в функции ngx_rtmp_live_publish проверять валидность имени потока и выдавать ошибку/переименовывать поток, но не понял как происходит разрегистрация стрима, на этом написание патча остановилась. Не поможете дописать?
    • 0
      Я стараюсь отвечать на все, что получаю.

      1) конец стрима можно отловить как NetStream.Play.Stop. Начиная с версии 0.8 шлется именно эта нотификация.
      Также можно включить NetStream.Play.UnpublishNotify (publish_notify on)

      По поводу ошибок с rtmpdump, попробуйте самую новую версию nginx-rtmp 0.8. Там кое-что исправлено.
      А вообще, если что-то не так, пишите багрепорты, поснифаем поток, посмотрим, что происходит.
      Конкретно на такое еще никто не жаловался.

      Сам поток (в смысле видео и аудио) валидацию, очевидно, не проходит т.к. это фактически равноценно раскодированию.

      2) ffmpeg анализирует часть потока перед тем, как начать нормально работать. Это ему нужно для того, чтобы определить, какие аудио и видео каналы есть в потоке. Если, к примеру, вы вещаете только аудио, он будет ждать до упора, пока не найдет видео. Граница это регулируется настройкой -probesize, поставьте значение поменьше и все будет отрабатывать мгновенно.

      3) Таймингом занимается сам ffmpeg, если ему указать опцию "-re". Она как раз и указана именно для случаев вещания видеофайлов

      4) Логика авторизации может быть разной. Вообще ее, конечно, можно сделать и внутри, но внутри отдельного модуля, а не внутри свободно распространяемого сервера для решения общей задачи. Вы, кстати, можете зарегать хендлер в http{} секции nginx (для этого надо не забыть прописать «notify_method get»). Так вот в этом хендлере и реализовать всю вашу логику, например на lua или даже средствами самого nginx, если логика простая.
      • 0
        Я собирал модуль из репозитория от Nov 16 11:28, события Stop не получал никогда. Судя по блогу, версия 0.8 вышла только в декабре, мне надо пересобрать все. Спасибо.

        Сюда же спишу проблемы с некорректным потоком, просто проверить сейчас не могу.

        Про валидацию я имел в виду размеры пакетов в самом FLV-контейнере, там хоть сигнатур для проверки и не густо, но можно определить по последним 4 байтам целостность потока. Сталкивался с тем, что java-апплет для снятия картинки с экрана иногда забывал дописывать кусочки данных (ну или пишет размер пакета некорректно), пришлось делать костылек.

        Для уменьшения буфера детектирования я выставлял поля max_analyze_duration=4096; и io->max_packet_size=50000, получил минимальную задержку, чего с ffmpeg сделать не получалось (надо правда сказать, что это было достаточно давно, я тогда извращался еще с оригинальным FMS и rtmpd), а вот про опцию -re не знал, спасибо.

        А вот теперь по поводу авторизации. Не знаю, как сильно это вяжется с идеологией проекта, но мне бы хотелось в идеале выставлять параметры всей сессии. Логика примерно такая:
        1. пришел коннект, подключились к application -> я проверяю имя приложения
        2. пришел запрос на публикацию -> я проверяю имя стрима, к примеру клиент шлет 7364589273645987346
        3. я знаю, что этот ID от пользователя ХХХ, что опубликовать его надо как /vasya/videoBlog, а не как /live/7364589273645987346
        4. я знаю, что длительность публикации должна быть не более 10 минут, аудио кодек только неллимосер, общий битрейт не более мегабита, а число клиентов не более 5 штук
        5. если после любой проверки что-то не так — соединение закрывается.
        6. опционально выставить SharedObject с данными, что currentClients=0 и считать в нем зрителей.
        Т.е. в идеале не просто «да» или «нет» получать от логики, а иметь возможность самому заполнить структурку со всеми параметрами, которые только могут быть. Сделать плагинный интерфейс, как к примеру в pureftpd. Но это мои влажные фантазии, поэтому мне проще будет написать патч (но с учетом асинхронной любви я этого не осиляю пока)
        • 0
          Ну и еще немного ошибок, которыми плюется livavcodec:

          [flv @ 0x14a0a40] negative cts, previous timestamps might be wrong
          [flv @ 0x14a0a40] negative cts, previous timestamps might be wrong
          [flv @ 0x14a0a40] negative cts, previous timestamps might be wrong
          [flv @ 0x14a0a40] negative cts, previous timestamps might be wrong
          [flv @ 0x14a0a40] negative cts, previous timestamps might be wrong

          [flv @ 0x237a640] Stream discovered after head already parsed
          [h264 @ 0x7f55ec045b40] Missing reference picture
          [h264 @ 0x7f55ec045b40] decode_slice_header error
          [h264 @ 0x7f55ec045b40] concealing 1200 DC, 1200 AC, 1200 MV errors
          [h264 @ 0x7f55ec045b40] Missing reference picture
          [h264 @ 0x7f55ec045b40] decode_slice_header error
          [h264 @ 0x7f55ec045b40] concealing 1200 DC, 1200 AC, 1200 MV errors
          [flv @ 0x237a640] max_analyze_duration 4096 reached at 23000
          [flv @ 0x237a640] decoding for stream 1 failed
          [flv @ 0x237a640] Estimating duration from bitrate, this may be inaccurate

          [flv @ 0x23a0a40] Stream discovered after head already parsed
          [h264 @ 0x23a1a40] Missing reference picture
          [h264 @ 0x23a1a40] decode_slice_header error
          [h264 @ 0x23a1a40] concealing 1200 DC, 1200 AC, 1200 MV errors
          [flv @ 0x23a0a40] max_analyze_duration 4096 reached at 23000
          [flv @ 0x23a0a40] decoding for stream 1 failed
          [flv @ 0x23a0a40] Estimating duration from bitrate, this may be inaccurate

          [flv @ 0x2488960] Stream discovered after head already parsed
          [h264 @ 0x24898a0] reference picture missing during reorder
          [h264 @ 0x24898a0] reference picture missing during reorder
          [h264 @ 0x24898a0] Missing reference picture
          [h264 @ 0x24898a0] decode_slice_header error
          [h264 @ 0x24898a0] concealing 1200 DC, 1200 AC, 1200 MV errors
          [h264 @ 0x24898a0] Missing reference picture
          [h264 @ 0x24898a0] mmco: unref short failure
          [h264 @ 0x24898a0] mmco: unref short failure
          [flv @ 0x2488960] max_analyze_duration 4096 reached at 23000
          [flv @ 0x2488960] decoding for stream 1 failed

          А вот примерно так умирает rtmpdump

          DEBUG2: RTMP_ReadPacket: fd=4
          DEBUG2: 0000: 46 00 00 18 00 00 0b 08 F…
          DEBUG2: 0000: af 01 21 00 49 90 02 19 00 23 80 ..!.I....#.
          DEBUG2: RTMP_ReadPacket: fd=4
          DEBUG2: 0000: 46 00 00 17 00 00 0b 08 F…
          DEBUG2: 0000: af 01 21 00 49 90 02 19 00 23 80 ..!.I....#.
          DEBUG2: RTMP_ReadPacket: fd=4
          DEBUG2: 0000: 46 00 00 17 00 00 0b 08 F…
          DEBUG2: 0000: af 01 21 00 49 90 02 19 00 23 80 ..!.I....#.
          DEBUG2: RTMP_ReadPacket: fd=4
          DEBUG2: 0000: 47 00 06 f6 00 00 05 09 G…
          DEBUG2: 0000: 17 02 00 00 00…
          DEBUG: ignoring too small video packet: size: 5
          DEBUG2: RTMP_ReadPacket: fd=4
          DEBUG: RTMPSockBuf_Fill, recv returned -1. GetSockError(): 11 (Resource temporarily unavailable)
          ERROR: RTMP_ReadPacket, failed to read RTMP packet header
          • 0
            Это надо смотреть на конкретном примере. Из того, что я вижу, это отсутствие ключевого фрейма вначале трансляции (можно решить опцией wait_key on). Ошибка декодирования h264 говорит о том, что, вероятно, есть проблема с самим закодированным потоком.

            По rtmpdump я вообще не вижу ошибки. Это его стандартная ругань, когда у него экспайрится таймаут. Т.е. он просто не дождался данных.

            Но, повторюсь, поставьте 0.8 сначала.
            • 0
              Поток генерировался через wirecast / xsplit, проблема в том, что сервер отдает стрим не с кейфрейма
              • 0
                В 0.8 есть опция wait_key для решения этой проблемы.
                • 0
                  [x]
  • 0
    Артур, большое спасибо за модуль!

    Мы используем этот модуль в product окружении около полугода.
    По сравнению с wowza/erly ваше решение — небо и земля.
    Wowza при стриме потока больше 5 гигабит/с ухитрялась пожирать весь процессор на не слабом сервере (2 x E5-2620).
    После перехода на модуль nginx, эти проблемы решились и теперь спокойно держим 2 x 8 гигабит/с на одном E3-1240.

    ps. Один раз правда была проблема с утечкой памяти в модуле, но она быстро закрылась Артуром.
    • 0
      Спасибо!

      PS: я Роман, а не Артур
      • 0
        Да, Роман, вечно путаюсь с именами =)
    • 0
      А вы не пробовали rtmpd?
      • 0
        Нет.
    • 0
      А с erly video почему не срослось?
  • 0
    Роман, спасибо за модуль, как раз начал использовать его на шатком сервере для своих нужд, и работает все отлично.
    Но есть некоторые проблемы с вещанием потока с камеры. То есть я использую примерно такую команду:
    ffmpeg -f dshow -i video="Webcam":audio="Webcam Microphone" -c:v libx264 -c:a libmp3lame -ar 44100 -ac 1 -f  flv rtmp://server/live/stream
    

    Я могу при одних и тех же обстоятельствах наблюдать разные проблемы:
    Видео отправляется, но не проигрывается до того момента, пока не завершишь отправку потока на сервер. Сразу после этого отправленный в «буфер» поток воспроизводится. По наблюдениям так бывает, если добавить -an в параметры запуска ffmpeg.
    Также при этом поведении не ведется запись hls.
    Видео при трансляции с камеры вообще не воспроизводится во флеш-плеерах, иногда воспроизводится только звук.

    Одновременно с этим, если отправлять на сервер реалтайм-поток любого файла, то все работает как нужно, в том числе, если захватывать поток rtsp или http.

    Так как мне нужно было как-то вещать с камеры, то я делал захват через VLC, и скармливал rtsp-поток ffmpeg'у. В таком случае все работало как нужно, но безбожно весило компьютер.
    • 0
      Да, ffmpeg я качал здесь ffmpeg.zeranoe.com/builds/
      Собрано от вот так:

      ffmpeg version N-47062-g26c531c Copyright (c) 2000-2012 the FFmpeg developers built on Nov 25 2012 12:21:26 with gcc 4.7.2 (GCC) configuration: --enable-gpl --enable-version3 --disable-pthreads --enable-runtime-cpudetect --enable-avisynth --enable-bzlib --enable-frei0r --enable-libass --enable-libopencore-amrnb --enable-libopencore-amrwb --enable-libfreetype --enable-libgsm --enable-libmp3lame --enable-libnut --enable-libopenjpeg --enable-libopus --enable-librtmp --enable-libschroedinger --enable-libspeex --enable-libtheora --enable-libutvideo --enable-libvo-aacenc --enable-libvo-amrwbenc --enable-libvorbis --enable-libvpx --enable-libx264 --enable-libxavs --enable-libxvid --enable-zlib
    • 0
      Покрутите параметр -probesize чтобы ffmpeg не подвисал на чтении потока при отсутствии аудио или видео. Чем он меньше, тем меньше задержка перед началом передачи потока на сервер.

      С камеры можно захватывать самим ffmpeg'ом, я приводил пример. И вешать оно ничего не должно, если только вы не производите перекодировку видео.
      • 0
        Видимо я плохо сформулировал.
        Мне так и не удалось заставить ffmpeg стабильно передавать видео с камеры напрямую(через DirectShow). При любом раскладе ни один из флеш-плееров не показывают изображение, только звук.

        Но если передавать на сервер готовый файл(или любой другой входной поток — http/rtsp), при этом также транскодируя его на лету с параметром -re, то везде все и всегда воспроизводится прекрасно, hls сохраняется.

        А про «вешать» я имел ввиду, что та конфигурация, которую мне пришлось использовать (webcam -> VLC -> rtsp stream out -> ffmpeg -> out nginx rtmp) очень сильно подвешивает систему, но работает. Видимо VLC кодирует менее эффективно.
        • 0
          Попробуйте проигрывать поток не флешом, а ffplay или ffmpeg с параметром -loglelve verbose.
          • 0
            ffplay само собой показывает, но это как бы все равно лишает всю затею смысла.
            В итоге провозившись весть день ничего более предсказуемого, чем связка vlc(rtsp) + ffmpeg найти не удалось.
            • 0
              Хорошо, чот работает. Я иногда использую ffmpeg (или ffprobe) для проверки валидности потока. Он может сказать что не так.
  • 0
    Роман, вчера тестировал модуль для записи live-стрима (h264 + speex в flv). Пишет, нарезает… но полученные файлы не перевариаются потом при попытке перекодировать (ffmpeg: Could not find codec parameters (Video: h264) и игнорирует видео, cvlc — просто залипает). Воспроизводятся же эти файлы нормально.

    Исходный поток ffmpeg нормально переваривает.

    Подскажите, как побороть?
    • 0
      А где «битые файлы»? Где дампы заголовков в этих файлах? Я не Роман, но думаю он астралом тоже не умеет пользоваться.
    • 0
      А вы не могли бы собрать дебаг и прислать error.log?

      github.com/arut/nginx-rtmp-module/wiki/Debug-log
      • 0
        Положил лог и записанные файлы (с префиксом new-) туда же.
        • 0
          В общем, проблема понятна. Ваш поток начинается с intermediate фреймов. Вот именно такие фреймы и попадают первыми в записанный flv. Заголовок H264 и ключевой фрейм идут уже после 4х промежуточных фреймов.

          Эта проблема касается именно первого записанного файла, последующие записываются правильно.

          Строго говоря, в записанном flv есть все, чтобы нормально воспроизводиться и перекодироваться, но ffmpeg ожидает, что заголовок H264 будет в начале файла.

          В любом случае я сделаю так, чтобы этой проблемы не было при любом потоке. Спасибо.
          • 0
            Думал над таким вариантом, но, последующие файлы тоже не воспринимаются ffmpeg'ом почему-то…
            • 0
              с new-stream2-1355492018.flv разве есть проблемы? у меня с ним все ок
              • 0
                Да, его тоже не пережевывает. Может, ffmpeg'у чего не хватает?.. Пробовал на нескольких машинах.

                ffmpeg version 0.10.3
                built on Dec 14 2012 15:08:07 with gcc 4.5.4
                configuration: --prefix=/usr --libdir=/usr/lib64 --shlibdir=/usr/lib64 --mandir=/usr/share/man --enable-shared --cc=x86_64-pc-linux-gnu-gcc --cxx=x86_64-pc-linux-gnu-g++ --ar=x86_64-pc-linux-gnu-ar --optflags='-march=native -O2 -pipe' --extra-cflags='-march=native -O2 -pipe' --extra-cxxflags='-march=native -O2 -pipe' --disable-static --enable-gpl --enable-version3 --enable-postproc --enable-avfilter --disable-stripping --disable-debug --disable-doc --disable-vaapi --disable-vdpau --disable-ffplay --enable-runtime-cpudetect --enable-libmp3lame --enable-libvo-aacenc --enable-libvo-amrwbenc --enable-libtheora --enable-libvorbis --enable-libx264 --enable-libxvid --enable-libfaac --enable-nonfree --enable-libdc1394 --disable-indev=v4l --disable-indev=v4l2 --disable-indev=alsa --disable-indev=jack --disable-outdev=alsa --disable-outdev=sdl --enable-pthreads --enable-libopencore-amrwb --enable-libopencore-amrnb --enable-librtmp --enable-libschroedinger --enable-libspeex --enable-libvpx --enable-libopenjpeg --disable-amd3dnow --disable-amd3dnowext --disable-altivec --disable-avx --disable-mmx2 --disable-vis --disable-neon --cpu=host --enable-hardcoded-tables
                libavutil      51. 35.100 / 51. 35.100
                libavcodec     53. 61.100 / 53. 61.100
                libavformat    53. 32.100 / 53. 32.100
                libavdevice    53.  4.100 / 53.  4.100
                libavfilter     2. 61.100 /  2. 61.100
                libswscale      2.  1.100 /  2.  1.100
                libswresample   0.  6.100 /  0.  6.100
                libpostproc    52.  0.100 / 52.  0.100
                
                • 0
                  А что именно он говорит на этом файле? Укажите ему -loglevel verbose
                  • 0
                    Вот такое. Собрал 0.10.6, лог одинаковый.

                    Кстати, вчера я такого поведения не замечал. На этом файле ffmpeg подвисает просто, приходится выходить по ^C.

                    ffmpeg version 0.10.6 Copyright (c) 2000-2012 the FFmpeg developers
                      built on Dec 14 2012 17:57:46 with gcc 4.5.4
                      configuration: --prefix=/usr --libdir=/usr/lib64 --shlibdir=/usr/lib64 --mandir=/usr/share/man --enable-shared --cc=x86_64-pc-linux-gnu-gcc --cxx=x86_64-pc-linux-gnu-g++ --ar=x86_64-pc-linux-gnu-ar --optflags='-march=native -O2 -pipe' --extra-cflags='-march=native -O2 -pipe' --extra-cxxflags='-march=native -O2 -pipe' --disable-static --enable-gpl --enable-version3 --enable-postproc --enable-avfilter --disable-stripping --disable-debug --disable-doc --disable-vaapi --disable-vdpau --disable-ffplay --enable-runtime-cpudetect --enable-libmp3lame --enable-libvo-aacenc --enable-libvo-amrwbenc --enable-libtheora --enable-libvorbis --enable-libx264 --enable-libxvid --enable-libfaac --enable-nonfree --enable-libdc1394 --disable-indev=v4l --disable-indev=v4l2 --disable-indev=alsa --disable-indev=jack --disable-outdev=alsa --disable-outdev=sdl --enable-pthreads --enable-libopencore-amrwb --enable-libopencore-amrnb --enable-librtmp --enable-libschroedinger --enable-libspeex --enable-libvpx --enable-libopenjpeg --disa  libavutil      51. 35.100 / 51. 35.100
                      libavcodec     53. 61.100 / 53. 61.100
                      libavformat    53. 32.100 / 53. 32.100
                      libavdevice    53.  4.100 / 53.  4.100
                      libavfilter     2. 61.100 /  2. 61.100
                      libswscale      2.  1.100 /  2.  1.100
                      libswresample   0.  6.100 /  0.  6.100
                      libpostproc    52.  0.100 / 52.  0.100
                    [flv @ 0x626320] Format flv probed with size=2048 and score=100
                    [h264 @ 0x62c630] err{or,}_recognition separate: 1; 1
                    [h264 @ 0x62c630] err{or,}_recognition combined: 1; 10001
                    
                    • 0
                      Конкретно этот трабл скорее всего можно пофиксить малым значением -probesize.

                      В общем, что касается первых файлов в записи, то поддержку таких потоков я добавил, но это еще надо протестировать, я вам напишу, как будет полностью готово. А вот что касается последующих файлов, тут нам надо разобраться, у меня они отлично просматриваются, перекодируются итд.
                      • 0
                        Спасибо за помощь, попробую разобраться.
                        • 0
                          Попробуйте бранч «record-keyframes».
                          • 0
                            Спасибо, попробую и отпишусь. Наверное, уже на следующей неделе.
                            • 0
                              ну как, попробовали?
                              • 0
                                К сожалению, еще не дошли руки, пригрузили другим.
                                Может, сегодня получится.
                              • 0
                                Провел короткий тест, похоже, все отлично — файлы записаны, ффмпег не ругается, конвертирует.

                                Еще потестирую, но, похоже, мне именно это и нужно было. Огромное спасибо!
                                • 0
                                  Прекрасно. Тогда я смержу в мастер. Если что не так — пишите.
  • 0
    Очень интересное решение! Большое спасибо за проект и за этот пост :)
    Держите в курсе разработки, думаю она здесь многим будет интересна.
  • 0
    Роман, привет!

    А можно как-нибудь уменьшить задержку при вещании с веб-камеры? Или 3 секунды — это минимум?
    • 0
      Это настраивается на клиенте.
      У JWPlayer, например, такая настройка «bufferlength: 1».
  • 0
    А есть возможноть сделать VOD HLS как в официальном HLS модуле для nginx?
    • +1
      Насколько я знаю, Роман — автор того самого HLS модуля для nginx :)
      • 0
        Так и есть. Когда узнал все встало на свои места.
  • 0
    Пробовал включить отдельно HLS и получаю такое:

    adding module in /tmp/nginx-rtmp-module + ngx_rtmp_module was configured adding module in /tmp/nginx-rtmp-module/hls ./configure: error: no /tmp/nginx-rtmp-module/hls/config was found

    • 0
      Сейчас hls включен в основной модуль, ничего включать дополнительно не надо.
      Где-то в доках осталось описание старого поведения?
      • 0
        Понял, спасибо! Не осталось…
      • 0
        HLS не хочет работать, rtmp работает… Файлики в /tmp/hls создаются. Открываю на компьютере через VLC и идет только звук, на других устройствах не работает вообще.

        rtmp {
        server {
        listen 1935;
        ping 30s;
        notify_method get;
        application myapp {
        live on;
        exec_static /opt/ffmpeg/bin/ffmpeg -i udp://224.200.202.200:1234 -c copy -c:a libfaac -ar 48000 -ac 2 -f flv rtmp://address/myapp/MYSTREAM;

        # sample HLS
        hls on;
        hls_path /tmp/hls;
        hls_fragment 5s;
        }

        }

        • 0
          Убедитесь, что видео в h264.
          • 0
            Stream #0:0[0x6af]: Video: h264 (High) ([27][0][0][0] / 0x001B), yuv420p(tv, bt709), 1920x1080 [SAR 1:1 DAR 16:9], 25 fps, 50 tbr, 90k tbn, 50 tbc
            Stream #0:1[0x6b0](rus): Audio: mp2 ([4][0][0][0] / 0x0004), 48000 Hz, stereo, s16p, 192 kb/s
            • 0
              Довольно странно, что работает звук. Звук должен быть в AAC.
          • 0
            Сделал так. Все получилось вроде. Спасибо.
            exec_static /opt/ffmpeg/bin/ffmpeg -threads 6 -i udp://224.200.202.200:1234 -c copy -vcodec libx264 -c:a libfaac -ar 48000 -ac 2 -f flv rtmp://addressmyapp/MYSTREAM
      • 0
        Вот у меня так пишет
        configuring additional modules
        adding module in ~/build/nginx-rtmp-module
        ./configure: error: no ~/build/nginx-rtmp-module/config was found
        Как быть, файл там есть?!
        • 0
          что пишет по nginx -V
          а вообще советую почитать тут

Только зарегистрированные пользователи могут оставлять комментарии. Войдите, пожалуйста.