LINUX.ORG.RU

Почему av_write_frame в FFmpeg такой медленный???

 ,


0

2

Читаю данные с SD карты на одноплатнике и отправляю их по сети через ффмпеговские либы, дамп на передающей стороне выглядит так

Output #0, mjpeg, to 'udp://192.168.1.1:5555':
  Stream #0:0: Video: mjpeg (Baseline), yuvj422p(pc, bt470bg/unknown/unknown), 1920x1080, q=2-31, 10 fps, 90k tbn

решил тут померить почему выдает нагрузку на сеть всего 4мбит/c и оказывается операция av_write_frame занимает в среднем 300-350 мс!!!

В чем может быть причина???

К слову за те же 300 мс с карты удается считать по 4Мбайта

★★★

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

сомнительно, я пробовал напрямую с усб камеры через v4l2rtspserver пересылать - можно нагрузить сетевой интерфейс до 80мбит/c

а пишу с усб камеры на сд карту, а по запросу через мой мктт высылаю этот архив и на принимающей стороне жму его в х264 и записывают в mp4, так что тривиально не получится протестить где то еще…

железка nanopi neo core, нагрузка на цпу при этом около нулевая

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

ну отправить можно файл непосредственно ffmpeg в никуда по udp

что то вроде этого (не проверял корректность)

ffmpeg -re -i 1080P.mjpeg -vcodec copy -f mjpeg udp://192.168.1.1:1234
как пример https://www.programmersought.com/article/2920550131/

wolverin ★★★
() автор топика

решил тут померить почему выдает нагрузку на сеть всего 4мбит/c и оказывается операция av_write_frame занимает в среднем 300-350 мс!!!

av_write_frame в сеть не пишет, хочешь писать в сеть пиши av_packet{data, size}, расходимся пацаны

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

через мой мктт высылаю этот архив

может быть тут засада. mqtt конечно не лимитует размеры, но ориентирован не на рассылку архивов. То есть не самый быстрый и эффективный протокол для передачи больших объёмов, он вроде как по кускам квитирует.

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

И как это сделать для чужой либы

ffmpeg - свободная библиотека, она не более чужая чем твой код. С ней можно делать что угодно.

время выполнения вызова одной функции для 200 кбайт данных переданных в нее занимает 300мс?

Какая разница для профайлинга что там сколько занимает?

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

так я же написал выше, вот вроде рабочий пример

Почему av_write_frame в FFmpeg такой медленный??? (комментарий)

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

Отдебажить саму функцию av_write_frame. Собери дебажный билд ffmpeg и посмотри что конкретно тормозит. Потому что av_write_frame это комбайн, нужно смотреть глубже.

ox55ff ★★★★★
()

кстате...а сколько ожидаешь от 10 fps ?

насколько понял вы через ffmpeg шлёте rtsp, де-факто проигрываете его из архива(!!). И он вам честно делает требуемый фрейм-рейт.

цитата из топика :

Output #0, mjpeg, to 'udp://192.168.1.1:5555':
  Stream #0:0: Video: mjpeg (Baseline), yuvj422p(pc, bt470bg/unknown/unknown), 1920x1080, q=2-31, 10 fps, 90k tbn
MKuznetsov ★★★★★
()
Ответ на: комментарий от max_lapshin

да тоже так думаю, поэтому наверняка есть способ снаружи задать поменьше длину этих слипов, для H264 4.5 мбита хватает онлайн смотреть даже 1080р и 30фпс, а если писать его начну, почему бы всю полосу не использовать

с другой стороны провайдер мне и дает минимально 5 мбит/c канал, но привязываться к этому ограничению не хотелось бы иба забавный артефакт получаю при записи архива мжпег - он пишется на сд карту быстрее, чем в сеть передается ))

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

в 300 мс это 3-4 кадра в секунду, а не 10

300 это в среднем, там буферизация, jitter.. чтобы уложиться в 10fps ему приходится торрмозить

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

попробую конечно поднять эту цифру ради эксперимента, но вряд ли это связано, т.к. если каких буферов не хватает обычно ффмпег сообщает, а все эти временные базы - это всего лишь для обсчета временных меток у пакета.

возможно нужно что то в самом линуксе поковырять… по типу udp буфера

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

возможно нужно что то в самом линуксе поковырять… по типу udp буфера

не надо ничего там ковырять…

вы действительно гоните rtsp 10fps ? и на передачу часового видео уходит ровно час. «Надо что-то в консерватории править» :-)

то есть внедрять некие event queue,чтобы av_write_frame не висел бестолково на таймере, а за эти 300мс делалось что-то нужное партии.

при 10 fps нагрузка на сеть невелика, и никакими ухищрениями с ядром вы её не нагрузите. Это поток реального времени, там есть буферы, он таймируется, это не перелив архива по ftp/http. От скорости чтения SD не зависит почти никак.

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

как я уже писал - 3-4 кадра в секунду при записанных 10 фпс это 2-3 часа, а не час ) это конечно не ужас, т.к. часовые архивы не скачиваются, думаю в продакшене это минут 10 нужно от силы.

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

а так вообще 10 фпс при 200кб это должно быть 16 мбит/c, а не 4.5

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

а что вы делаете одновременно с этим ?

если на передачу 1 часа видео mjpeg rtsp в 10 fps уходит 2-3 часа и av_write_frame занимает «в среднем» до 300, значит где-то сбились с таймингов.

Это стопудово не проблема скорости чтения SD или отдачи UDPей.

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

одновременно с этим пишется архив на ту же флешку, может иногда просматриваться текущее видео, но уже пересжимаемое в х264

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

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

параметр re это для воспроизведения как мне кажется ситуации с ограничением трафика, я не «воспроизвожу» на принимающую сторону с частотой отправки

что от кода надо честно говоря не понимаю, fread c диска, поиск заголовка jpeg, присваивание AVPacket указателя на место в считанном буфере и размера, все работает, но av_write_frame медленно шлет данные

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

параметр re это для воспроизведения как мне кажется ситуации с ограничением трафика, я не «воспроизвожу» на принимающую сторону с частотой отправки

тогда зачем цитировать, если кажется

что от кода надо честно говоря не понимаю

ок

но av_write_frame медленно шлет данные

опять же вангование без кода, но av_write_frame не шлет данные, это высокоуровневая обертка которая делает не так уж и много над .write_packet протокола, который в твоем случае суть raw и там ничего cущественного не делается и тем более sleep, дальше уезжает в .url_write протокола

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

не мерял весь поток, я мерил только сам вызов av_write_frame

накуя вы вообще его меряли ? если часовое видео в 10 кадров отдаётся 2-3 часа, то это влёт проблема ваших алгоритмов и внутреннего устройства вашей кухни. Ведь не совсем Full-HD 50fps же..

да, когда всё дружно втормозило, av_write_frame (который кстати не обязан сразу отсылать UDP и моментально заканчиваться), накапливает кеширует и таймирует. Он единственный порядочный в вашем оркестре.

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

не вижу смысла дальше ванговать без кода

) хорошо, давайте вангование по коду

void ArchiveUpload(size_t cnt, time_t tStart, uint64_t firstPos, uint64_t lastPos, const string url_fmt, const string url)
{
    if (url_fmt.empty() || url.empty())
    {
        ArchiveLog("Error archive: -1", stderr);
        return;
    }

    if (!dp)
    {
        ArchiveLog("Error archive: -2", stderr);
        return;
    }

    const uint64_t offset = dp->first_lba * dp->sector_size - 1;
    if (lastPos - firstPos > dp->partition_size || firstPos < offset)
    {
        ArchiveLog("Error archive: -3", stderr);
        return;
    }

    if (pJpg->isJournalRange(firstPos, lastPos) < 0)
    {
        ArchiveLog("Error archive: -4", stderr);
        return;
    }

    void * pstURL = NULL;
    if (plURL) pstURL = plURL->addURL(url.c_str(), 0b11);
    if (!pstURL)
    {
        ArchiveLog("Error archive: -5", stderr);
        return;
    }

    statusArchive |= 0b100;

    FILE * pf = fopen(dp->device, "rb");
    if (!pf)
    {
        statusArchive ^= 0b100;
        ArchiveLog("Error archive: -6", stderr);
        return;
    }

    if (UploadOpen(url_fmt.compare("udp") ? url_fmt.c_str() : DEFAULT_FRAME_FORMAT, url.c_str(), DEFAULT_ARCHIVE_RATE) < 0)
    {
        fclose(pf);
        statusArchive ^= 0b100;
        ArchiveLog("Error archive: -7", stderr);
        return;
    }

    const size_t
        lbuf_r = 16 * 256 * 1024,
        lbuf_f = 2 * 256 * 1024;
    setvbuf(pf, NULL, _IOFBF, lbuf_r);

    uint64_t Pos = firstPos;
    fseeko(pf, Pos, SEEK_SET);

    size_t
        l, lr, lFrm,
        lfrag = 0;
    bool chkEnd = lastPos < firstPos;
    uint8_t
        lNote,
        * tmp,
        * head,
        * buf = (uint8_t *)malloc(lbuf_r * sizeof(uint8_t)),
        * frag = (uint8_t *)malloc(lbuf_f * sizeof(uint8_t));
    uint64_t
        pts = tStart * 1000;
    const uint64_t
        ptsStep = 1000 / DEFAULT_ARCHIVE_RATE;
int64_t start_time;;

    fprintf(stdout, "Upload archive containing points: %lu ... \n", (long unsigned int)cnt);
    while (abRun && (Pos <= lastPos || chkEnd))
    {
        if ((lr = fread(buf, 1, lbuf_r, pf)) == lbuf_r)
            Pos += lr;
        else
        {
            if (ferror(pf) || (!chkEnd && lr <= pJpg->szHeadMax))
            {
                ArchiveLog("Error archive: -8", stderr);
                goto END;
            }
            else if (chkEnd)
            {
                fseeko(pf, offset, SEEK_SET);
                Pos = offset;
                chkEnd = false;
            }
        }

        lFrm = l = 0;
        if (lr > pJpg->szHeadMax)
        {
            tmp = buf;
            while (abRun)
            {
                if (lfrag > 0)
                {
                    memcpy(frag + lfrag, buf, lbuf_f - lfrag);
                    tmp = frag;
                }

                if (pJpg->isJpgHead(tmp))
                {
                    head = tmp;
                    lNote = pJpg->initNote(&head);
                    if ((l += lFrm = pJpg->getlFrm(&head)) > lr)
                    {
                        lfrag = lFrm + lr - l;
                        break;
                    }

                    if (pJpg->isTimeInNote(lNote))
                    {
                        pts = pJpg->getTime(&head) * 1000;
                        if (pJpg->isPosInNote(lNote))
                        {
                            fprintf(stdout, "\nTimestamps not extracted %lu ... \n", (long unsigned int)--cnt);
                            if (abMQTT && plURL)
                            {
                                if (plURL->getStatusURL(pstURL) & 1)
                                {
                                    plURL->setStatusURL(pstURL, 0b10);
                                    MQTTSendMsgToTopicDef(("DWL_PING " + url).c_str());
                                }
                                else
                                {
                                    ArchiveLog(("DWL_TIMEOUT " + url).c_str(), stderr);
                                    goto END;
                                }
                            }
                        }
                    }
                    else
                        pts += ptsStep;

start_time = get_mcsec_relative();
                    if (UploadWrite(tmp, lFrm, pts) < 0)
                    {
                        ArchiveLog("Error archive: -9", stderr);
                        goto END;
                    }
fprintf(stderr, " - %lld\n", (get_mcsec_relative() - start_time)/1000);

                    if (lfrag > 0)
                    {
                        l -= lfrag;
                        lfrag = 0;
                    }

                    if (l + pJpg->szHeadMax < lr)
                        tmp = buf + l;
                    else
                        break;
                }
                else if (lfrag > 0)
                {
                    ArchiveLog("Error archive: -10", stderr);
                    goto END;
                }
                else
                    break;
            }
        }

        if (l != lr)
        {
            if (l < lr)
                lfrag = lr - l;

            memcpy(frag, buf + (lr - lfrag), lfrag);
        }
    }

END:
    ArchiveLog(("UPLOADED " + url).c_str(), stdout);

    free(buf);
    free(frag);

    fclose(pf);

    UploadClose();

    plURL->delURL(url.c_str());
}

int UploadWrite(uint8_t * pdata, int size, uint64_t ts)
{
    pPktUpl->data = pdata;
    pPktUpl->size = size;
    pPktUpl->pts = pPktUpl->dts = ts * pFmtCtxUpl->streams[0]->time_base.den / 1000;

    return av_write_frame(pFmtCtxUpl, pPktUpl);
}

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

виноват, прошу прощения

int UploadOpen(const char * url_fmt, const char * url, const uint8_t rate)
{
    int rt = 0;

    avformat_alloc_output_context2(&pFmtCtxUpl, NULL, url_fmt, url);
    if (!pFmtCtxUpl)
    {
        PrintErrorAV("\nError deduce upload format", (rt = AVERROR(ENOMEM)));
        return rt;
    }

    AVStream * pStm = avformat_new_stream(pFmtCtxUpl, NULL);
    if (!pStm)
    {
        PrintErrorAV("\nError allocate upload stream", (rt = AVERROR(ENOMEM)));
        return rt;
    }

    if ((rt = avcodec_parameters_from_context(pStm->codecpar, pCdcCtxInp)) < 0)
    {
        PrintErrorAV("Error copy upload context", rt);
        return rt;
    }
    pStm->time_base = DEFAULT_TIME_BASE;//(AVRational){ 1, 90000 };
    pStm->avg_frame_rate = (AVRational){ rate, 1 };

    av_dump_format(pFmtCtxUpl, 0, url, 1);

    if ((rt = avio_open(&pFmtCtxUpl->pb, url, AVIO_FLAG_WRITE)) < 0)
    {
        fprintf(stderr, "Error open '%s': %d %s\n", url, rt, av_err2str(rt));
        return rt;
    }

    if ((rt = avformat_write_header(pFmtCtxUpl, NULL)) < 0)     {
        PrintErrorAV("Error occurred when opening upload stream", rt);
        return rt;
    }

    if (!(pPktUpl = av_packet_alloc()))
    {
        PrintErrorAV("Error allocate upload AVPacket", (rt = AVERROR(ENOMEM)));
        return rt;
    }

    return rt;
}

wolverin ★★★
() автор топика