Я пишу код на С++, где последовательность N различных кадров генерируется после выполнения некоторых операций, реализованных в ней. После того как каждый кадр завершен, я записываю его на диск как IMG_% d.png, и, наконец, я кодирую их в видео через ffmpeg с помощью кодека x264.
Сводный псевдокод основной части программы следующий:
std::vector<int> B(width*height*3);
for (i=0; i<N; i++)
{
// void generateframe(std::vector<int> &, int)
generateframe(B, i); // Returns different images for different i values.
sprintf(s, "IMG_%d.png", i+1);
WriteToDisk(B, s); // void WriteToDisk(std::vector<int>, char[])
}
Проблема этой реализации заключается в том, что количество желаемых кадров, N, обычно велико (N ~ 100000), а также разрешение изображений (1920x1080), что приводит к перегрузке диска, что создает циклы записи десятки GB после каждого исполнения.
Чтобы избежать этого, я пытаюсь найти документацию о разборе непосредственно каждого изображения, хранящегося в векторе B, кодеру, например x264 (без необходимости записи промежуточных файлов изображений на диск). Хотя некоторые интересные темы были найдены, ни одна из них не решила конкретно то, что я точно хочу, так как многие из них касаются выполнения кодировщика с существующими файлами изображений на диске, в то время как другие предоставляют решения для других языков программирования, таких как Python (здесь вы можете найти полностью удовлетворительное решение для этой платформы).
Псевдокод того, что я хотел бы получить, похож на это:
std::vector<int> B(width*height*3);
video_file=open_video("Generated_Video.mp4", ...[encoder options]...);
for (i=0; i<N; i++)
{
generateframe(B, i+1);
add_frame(video_file, B);
}
video_file.close();
В соответствии с тем, что я прочитал по смежным темам, API-интерфейс x264 С++ мог бы это сделать, но, как указано выше, я не нашел удовлетворительного ответа для моего конкретного вопроса. Я попытался изучить и использовать непосредственно исходный код ffmpeg, но и его низкая простота использования и проблемы компиляции заставили меня отказаться от этой возможности как простой непрофессиональный программист, я (я воспринимаю это так же, как и хобби, и, к несчастью, я не могу тратить что много времени изучает что-то столь требовательное).
Еще одно возможное решение, которое мне пришло в голову, - найти способ вызова двоичного файла ffmpeg в коде С++ и каким-то образом передать данные изображения каждой итерации (хранящейся в B) в кодировщик, позволяя добавить каждого кадра (то есть не "закрывать" видеофайл для записи) до последнего кадра, чтобы можно было добавить больше кадров до достижения N-го, где видеофайл будет "закрыт". Другими словами, вызовите ffmpeg.exe через программу С++, чтобы записать первый кадр в видео, но заставьте кодер "ждать" для большего количества кадров. Затем снова вызовите ffmpeg, чтобы добавить второй кадр и сделать кодер "ожидание" снова для большего количества кадров и так далее до достижения последнего кадра, где видео будет завершено. Однако я не знаю, как действовать, или если это действительно возможно.
Изменить 1:
Как было предложено в ответах, я документировал именованные каналы и пытался использовать их в своем коде. Прежде всего, следует отметить, что я работаю с Cygwin, поэтому мои именованные каналы создаются так, как они будут созданы под Linux. Модифицированный псевдокод, который я использовал (включая соответствующие системные библиотеки), является следующим:
FILE *fd;
mkfifo("myfifo", 0666);
for (i=0; i<N; i++)
{
fd=fopen("myfifo", "wb");
generateframe(B, i+1);
WriteToPipe(B, fd); // void WriteToPipe(std::vector<int>, FILE *&fd)
fflush(fd);
fd=fclose("myfifo");
}
unlink("myfifo");
WriteToPipe - это небольшая модификация предыдущей функции WriteToFile, где я убедился, что буфер записи для отправки данных изображения достаточно мал, чтобы соответствовать ограничениям буферизации.
Затем я компилирую и записываю следующую команду в терминале Cygwin:
./myprogram | ffmpeg -i pipe:myfifo -c:v libx264 -preset slow -crf 20 Video.mp4
Однако он остается застрявшим в цикле, когда я = 0 на линии "fopen" (то есть, первый вызов fopen). Если бы я не вызвал ffmpeg, это было бы естественно, поскольку сервер (моя программа) ожидал, что клиентская программа будет подключаться к "другой стороне" канала, но это не так. Похоже, что они не могут быть связаны через трубу каким-то образом, но я не смог найти дополнительную документацию, чтобы преодолеть эту проблему. Любое предложение?