Декодирование MJPEG происходит в 3 раза медленнее при открытии устройства ввода V4L2 [закрыто]C++

Программы на C++. Форум разработчиков
Ответить
Anonymous
 Декодирование MJPEG происходит в 3 раза медленнее при открытии устройства ввода V4L2 [закрыто]

Сообщение Anonymous »

Я пытаюсь декодировать видеопоток MJPEG, поступающий с веб-камеры, но сталкиваюсь с некоторыми блокировщиками производительности при использовании C API FFmpeg в своем приложении. Я воссоздал проблему на примере видеодекодера, где я просто открываю устройство ввода V4L2, считываю пакеты и отправляю их в декодер. Что странно, если я попытаюсь получить входные пакеты с устройства V4L2, а не из файла, вызов декодера avcodec_send_packet будет почти в 3 раза медленнее. После дальнейших поисков я сузил вопрос до того, открываю ли я вообще устройство V4L2.
Давайте посмотрим на минимальный пример, демонстрирующий такое поведение:

Код: Выделить всё

extern "C"
{
#include 
#include 
#include 
#include 
}

#define INBUF_SIZE 4096

static void decode(AVCodecContext *dec_ctx, AVFrame *frame, AVPacket *pkt)
{
if (avcodec_send_packet(dec_ctx, pkt) < 0)
exit(1);

int ret = 0;
while (ret >= 0) {
ret = avcodec_receive_frame(dec_ctx, frame);
if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)
return;
else if (ret < 0)
exit(1);

// Here we'd save off the decoded frame, but that's not necessary for the example.
}
}

int main(int argc, char **argv)
{
const char *filename;
const AVCodec *codec;
AVCodecParserContext *parser;
AVCodecContext *c= NULL;
FILE *f;
AVFrame *frame;
uint8_t inbuf[INBUF_SIZE + AV_INPUT_BUFFER_PADDING_SIZE];
uint8_t *data;
size_t   data_size;
int ret;
int eof;
AVPacket *pkt;

filename = argv[1];

pkt = av_packet_alloc();
if (!pkt)
exit(1);

/* set end of buffer to 0 (this ensures that no overreading happens for damaged MPEG streams) */
memset(inbuf + INBUF_SIZE, 0, AV_INPUT_BUFFER_PADDING_SIZE);

// Use MJPEG instead of the example's MPEG1
//codec = avcodec_find_decoder(AV_CODEC_ID_MPEG1VIDEO);
codec = avcodec_find_decoder(AV_CODEC_ID_MJPEG);
if (!codec) {
fprintf(stderr, "Codec not found\n");
exit(1);
}

parser = av_parser_init(codec->id);
if (!parser) {
fprintf(stderr, "parser not found\n");
exit(1);
}

c = avcodec_alloc_context3(codec);
if (!c) {
fprintf(stderr, "Could not allocate video codec context\n");
exit(1);
}

if (avcodec_open2(c, codec, NULL) < 0) {
fprintf(stderr, "Could not open codec\n");
exit(1);
}

c->pix_fmt = AV_PIX_FMT_YUVJ422P;

f = fopen(filename, "rb");
if (!f) {
fprintf(stderr, "Could not open %s\n", filename);
exit(1);
}

frame = av_frame_alloc();
if (!frame) {
fprintf(stderr, "Could not allocate video frame\n");
exit(1);
}

avdevice_register_all();
auto* inputFormat = av_find_input_format("v4l2");
AVDictionary* options = nullptr;
av_dict_set(&options, "input_format", "mjpeg", 0);
av_dict_set(&options, "video_size", "1920x1080", 0);

AVFormatContext* fmtCtx = nullptr;

// Commenting this line out results in fast encoding!
// Notice how fmtCtx is not even used anywhere, we still read packets from the file
avformat_open_input(&fmtCtx, "/dev/video0", inputFormat, &options);

// Just parse packets from a file and send them to the decoder.
do {
data_size = fread(inbuf, 1, INBUF_SIZE, f);
if (ferror(f))
break;
eof = !data_size;

data = inbuf;
while (data_size > 0 || eof) {
ret = av_parser_parse2(parser, c, &pkt->data, &pkt->size,
data, data_size, AV_NOPTS_VALUE, AV_NOPTS_VALUE, 0);
if (ret < 0) {
fprintf(stderr, "Error while parsing\n");
exit(1);
}
data      += ret;
data_size -= ret;

if (pkt->size)
decode(c, frame, pkt);
else if (eof)
break;
}
} while (!eof);

return 0;
}
Вот гистограмма времени ЦП, затраченного на вызов функции avcodec_send_packet с открытием устройства и без него, закомментировав вызов avformat_open_input выше.
Не открывая устройство V4L2:
Изображение

При открытии устройства V4L2:
Изображение

Интересно, что значительное количество вызовов функций находится в интервале времени ~25 мс! Но большинство из них ~78 мс... почему?
Так что же здесь происходит? Почему открытие устройства ухудшает мою производительность декодирования?
Кроме того, если я попытаюсь запустить, казалось бы, эквивалентный конвейер через сам инструмент ffmpeg, я не столкнусь с этой проблемой. Запуск этой команды:

Код: Выделить всё

ffmpeg -f v4l2 -input_format mjpeg -video_size 1920x1080 -r 30 -c:v mjpeg -i /dev/video0 -c:v copy out.mjpeg
Генерирует выходной файл с заявленной скоростью чуть более 1,0x, то есть. 30 кадров в секунду. Отлично, почему C API не дает мне таких же результатов? Следует отметить одну вещь: я получаю периодические ошибки от декодера MJPEG (примерно каждую секунду), но не уверен, являются ли они проблемой или нет:

Код: Выделить всё

[mjpeg @ 0x5590d6b7b0] Application provided invalid, non monotonically increasing dts to muxer in stream 0: 27 >= 27
[mjpeg @ 0x5590d6b7b0] Application provided invalid, non monotonically increasing dts to muxer in stream 0: 30 >= 30
...
Я использую Raspberry Pi CM4 с FFmpeg 6.1.1


Подробнее здесь: https://stackoverflow.com/questions/791 ... put-device
Ответить

Быстрый ответ

Изменение регистра текста: 
Смайлики
:) :( :oops: :roll: :wink: :muza: :clever: :sorry: :angel: :read: *x)
Ещё смайлики…
   
К этому ответу прикреплено по крайней мере одно вложение.

Если вы не хотите добавлять вложения, оставьте поля пустыми.

Максимально разрешённый размер вложения: 15 МБ.

Вернуться в «C++»