LINUX.ORG.RU

ffmpeg дропает кадры с камеры, если битрейд увеличивается (too full or near too full)

 , , , ,


1

2

Вкратце: стримминг камеры(+звука) на rtmp сервисы через ffmpeg

Проблема в следующем: Кадры со входа дропаются, если битрейд передачи потока превышает ~400kbps.

По умолчанию, если ffmpeg получает видеокадр «слишком рано» (в то время как предыдущий кадр еще не закончен), он отбрасывает этот кадр, чтобы он мог поддерживать ввод в реальном времени

Вот что в доках:
https://trac.ffmpeg.org/wiki/DirectShow

By default FFmpeg captures frames from the input, and then does whatever you told it to do, for instance, re-encoding them and saving them to an output file. By default if it receives a video frame «too early» (while the previous frame isn't finished yet), it will discard that frame, so that it can keep up the the real time input.

Ну ок, тут вроде понятно, если кадры с камеры идут очень быстро, а я их не успеваю обрабатывать, то их нужно отбрасывать, для достижения реалтайма. Но я не вижу узких мест. Кодировщик настроен на минималку, FPS на входе и на выходе одинаков. Процессор нагружен на 2-3%. Сеть ДОМРУ ну где то 50Mbps. Сервер тоже тянет.

В целом все хорошо работает. Но сбился с толку уже с этим rtbufsize, который постоянно «too full or near too full», если битрейд превышает 400kbps

rtbufsize - буфер памяти для входа для realtime устройств. Я его установил в 10M(мегабайт). Играть с ним нет вообще никакого смысла, если установишь хоть в гигабайт, то все равно он со временем переполнится, а latency будет огромный

Камеру установил на минималку (640x480@20), выходной поток такой же (640x480@20), битрейд максимальный - 800k.

ffmpeg -f dshow -video_size 640x480 -framerate 20 -rtbufsize 10M -i "video=dev_realtime" -vcodec libx264 -preset veryfast -b 800k -maxrate 800k -bufsize 1600K -r 20 -f flv "rtmp://x.y"


Ещё заметил:
* если на выход установить файл (out.flv), то все четко идет.
* если поднять локально rtmp сервер (ffmpeg -y -f flv -listen 1 -i «rtmp://127.0.0.1:1935/app/live») и передавать на него, то тоже все четко идет
* udp://microsoft.com:666 - хорошо идет, буфер не переполняется

Удаленный сервер менял (от оборудования до провайдера), на сервере замерял битрейд - приходит четко 800kpbs. То есть успевает ffmpeg в сеть передавать?

Не пойму в чем затык! Вроде и на сервер успевает передавать своевременно, но и в тоже время на входе дропаются кадры. Ничего не пойму

Все гугления сводятся к бестолковым «увеличь rtbufsize»

UDP
Кажется баг в rtmp под windows. Помогают правки в реестре (DefaultReceiveWindow, DefaultSendWindow)
https://trac.ffmpeg.org/ticket/1604

★★★★

Может попробовать поменять настройки tcp по умолчанию, типа net.ipv4.tcp_wmem .

Или попробуйте запустить два ffmpeg, один на кодирование, а другой только на передачу:

ffmpeg ... -f flv - | ffmpeg -f flv -i - -c copy -f flv "rtmp://x.y"

mky ★★★★★ ()
Ответ на: комментарий от mky

Нашел 8 летнюю тему https://trac.ffmpeg.org/ticket/1604
Поменял в реестре и буфер перестал переполнятся.
Изменил, перезагрузил, сейчас

DefaultReceiveWindow=65536
DefaultSendWindow=65536

что можно в сырцах ffmpeg изменить, чтобы не лезть в реестр?

gobot ★★★★ ()

Изменил 1 строчку в исходниках, пересобрал и баг исчез. Странно, что его разработчики до сих пор не исправили, хотя их 8 лет назад носом тыкали

gobot ★★★★ ()
Ответ на: комментарий от anonymous

Дык они сами знают. См. ссылку выше на баг 8 летней давности. Странные разработчики

gobot ★★★★ ()
Ответ на: комментарий от gobot

Не совсем понятно что ты поменял в исходниках, если у тебя проблема в буфере udp, который лимитирует ОС и через реестр ты его увеличивал. Скорее всего ты так же теряешь кадры, но не видишь ошибки.

Stack77 ()
Ответ на: комментарий от Stack77

SO_RCVBUF заменил на значение, наверное? Можно, должно сработать. Только это не баг программы, а ограничения ОС на которое она опирается. Если буфер заполнен - остальные udp пакеты отбрасываются. Тогда лучше через реестр, а может даже ffmpeg опциями распологает для подобного, man смотреть нужно. Но багом это сложно назвать.

Stack77 ()
Последнее исправление: Stack77 (всего исправлений: 2)
Ответ на: комментарий от peregrine

Да и под Linux по умолчанию не сильно большой, можно тоже проблем поиметь. По хорошему можно и в параметры ffmpeg вынести, странно если такого нет. Кто там ман по ffmpeg хорошо знает?

Виндузятники должны страдать

Само собой :)

Stack77 ()
Ответ на: комментарий от Stack77

ты его увеличивал

Нельзя однозначно это утверждать. Возможно это из за переполнения буфера.

Владимир

anonymous ()
Ответ на: комментарий от Stack77

Не заменил, там вообще опции буферов не выставлялись. Да, сделал setsockopt(fd, SOL_SOCKET, SO_SNDBUF...). По умолчанию буфер был 8092, а пакеты шли и по 30000

Но багом это сложно назвать

Ну да, это не баг, это какая то тупость. Они даже в tcp.c не проверяли скольно байтов отправилось, о чем можно вообще говорить. Это я говорю, человек, далекий от с\с++. ffmpeg конечно вещь, но не ожидал от них такой лабуды. Такое ощущение что специально навредили виндузятникам )))

gobot ★★★★ ()
Последнее исправление: gobot (всего исправлений: 1)
Ответ на: комментарий от targitaj

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

gobot ★★★★ ()
Ответ на: комментарий от gobot

Они даже в tcp.c не проверяли скольно байтов отправилось, о чем можно вообще говорить.

Скачал сорцы FFmpeg 4.3.1, посмотрел. Проверяется там число отправленных байт. Обрабатывается ситуация блокирующих и неблокирующих сокетов. Обрабатывается ситуация с прерванными системными вызовами. То есть всё, что нужно. Честно говоря, даже больше. Я бы не стал заморачиваться с неблокирующими сокетами. Это уже какая-то защита от тараканов в голове программистов, которые этим API будут пользоваться.

Ну да, это не баг, это какая то тупость. <…> Это я говорю, человек, далекий от с\с++.

Рекомендую тебе встать поближе к C/C++ перед тем, как делать подобные заявления. Иначе ты просто выставляешь себя в дурном свете.

i-rinat ★★★★★ ()
Ответ на: комментарий от i-rinat

Проверяется там число отправленных байт

Где там проверяется? ret < 0?

static int tcp_write(URLContext *h, const uint8_t *buf, int size)
{
    TCPContext *s = h->priv_data;
    int ret;

    if (!(h->flags & AVIO_FLAG_NONBLOCK)) {
        ret = ff_network_wait_fd_timeout(s->fd, 1, h->rw_timeout, &h->interrupt_callback);
        if (ret)
            return ret;
    }
    ret = send(s->fd, buf, size, MSG_NOSIGNAL);
    return ret < 0 ? ff_neterrno() : ret;
}


https://github.com/FFmpeg/FFmpeg/blob/a7f9b3b954ece129402ac7ef8f96e0fa1c01d8b...

gobot ★★★★ ()
Ответ на: комментарий от i-rinat

Рекомендую тебе встать поближе к C/C++ перед тем, как делать подобные заявления

Какое я делал заявление? Баг есть, о нем писали ещё 8 лет назад. Зачем отправлять данные, превышающие системный буфер?

gobot ★★★★ ()
Ответ на: комментарий от gobot

Где там проверяется? ret < 0?

ret передаётся дальше. Саму функцию вызывает ffurl_write(), опосредовано через retry_transfer_wrapper(), в которую передётся требование отослать все size байт из size. Поэтому если единичный вызов send() не рапортует об отсылке всего, send() будет вызваться для остатков буфера.

i-rinat ★★★★★ ()
Ответ на: комментарий от gobot

Какое я делал заявление?

Вот это:

Ну да, это не баг, это какая то тупость.

Баг есть, о нем писали ещё 8 лет назад. Зачем отправлять данные, превышающие системный буфер?

Какой ещё баг? Стартовый размер окна TCP никак не связан с UDP. Там ты просто не сможешь послать датаграмму, если она больше некого лимита. Сегментация данных и сортировка порядка датаграмм — обязанность приложения. В TCP размеры окон это всего лишь деталь реализации. Пользователям API вообще побоку, какой там размер окон. Они посылают и принимают поток байт. Можно хоть терабайтный буфер в send() передать, и ОС обязана эту ситуацию обрабатывать. Сама побить на пакеты, пронумеровать, контрольные суммы посчитать, весь фарш.

Если ОС не в состоянии масштабировать TCP окно, это не баг в прикладном софте, это баг в коде TCP в ядре.

i-rinat ★★★★★ ()
Ответ на: комментарий от gobot

Дык дело не в UDP.

И? Я вообще-то и про TCP тоже сказал.

rtmp по tcp работает

Там у них для p2p была какая-то вариация, которая через UDP работала, если это так уж важно.

i-rinat ★★★★★ ()
Ответ на: комментарий от i-rinat

Ок. Но все же ffmpeg глупо работает, если кадры дропаются? Я как пользователь что должен был сделать, чтобы этого избежать, не пересобирая ffmpeg? Правки реестра?

gobot ★★★★ ()
Ответ на: комментарий от gobot

Я как пользователь что должен был сделать

Например, купить услуги квалифицированных специалистов. Когда дело касается тюнинга производительности, в том числе и сети, всегда очень много тонких моментов, которые каждому знать невозможно.

Но все же ffmpeg глупо работает, если кадры дропаются?

FFmpeg это набор инструментов. Он не будет думать за тебя.

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

i-rinat ★★★★★ ()
Последнее исправление: i-rinat (всего исправлений: 1)
Ответ на: комментарий от i-rinat

Да каких специалистов?

setsockopt (fd, SOL_SOCKET, SO_SNDBUF...)

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

gobot ★★★★ ()
Ответ на: комментарий от gobot

Да каких специалистов?

Нормальных таких специалистов, которые в состоянии понять, что размер буфера можно через опции ffmpeg передать, и пересборки не требуется.

Какой тюнинг

Подбор подходящих параметров, и не только параметров сетевого кода. Например, выбор опций x264 это тоже тюнинг. Или если ты вдруг решишь поставить ultraslow, и окажется, что в таком режиме кадры теряются, то снова ffmpeg виноват будет? Как я уже упоминал, шланги и резервуары.

i-rinat ★★★★★ ()
Ответ на: комментарий от i-rinat

но их можно решить, поставив чуть больший размер

О, класс! Я кажется нашел этот параметр, -send_buffer_size нужно ставить после формата (после -f). Он как раз и отвечает за то, что я делал в коде )

gobot ★★★★ ()
Ответ на: комментарий от i-rinat

Или если ты вдруг решишь поставить ultraslow, и окажется, что в таком режиме кадры теряются

А что за ultraslow? Такого пресета нет. Да, кстати, с "-tune zerolatency" почему то кадры дропаются тоже, реже намного, но все равно.

-vcodec libx264  \
-b 1000k -minrate 500k -maxrate 1000k   -bufsize 500k \
-r 30 \
-video_size 640x480 \
-x264-params scenecut=0  \
-pix_fmt yuv420p \
-preset medium \
-tune zerolatency \

и битрейт почему то с zerolatency сразу падает до 350kbps, хотя -minrate 500k. Что ему нужно ещё? Без zerolatency идеально работает, ни один кадр не пропадает

gobot ★★★★ ()

попытки из тцп перетюнить-переделать в удп ничем хорошим не закончатся

anonymous ()

Кажется баг в rtmp под windows. Помогают правки в реестре (DefaultReceiveWindow, DefaultSendWindow)

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

byko3y ★★★★ ()
Ответ на: комментарий от gobot

А что за ultraslow? Такого пресета нет.

Ну всё, вот ты меня и подловил. Такого пресета действительно нет. Я не сверился со списком пресетов и ошибся.

i-rinat ★★★★★ ()
Ответ на: комментарий от Stack77

Облажались мы с тобой. Не в буфере дело было.

Владимир

anonymous ()
Ограничение на отправку комментариев: только для зарегистрированных пользователей