Простая программная генерация видео и картинок без стороннего API
В этой статье я покажу простой способ генерации видео программами на Python и C++ без использования стороннего API. Вам так же потребуется ffmpeg, без него вы не сможете конвертировать файлы в читаемые форматы!
Зачем это нужно?
Можно экспериментировать, например вы можете создать видео максимального качества и проверять как оно будет эффективно сжиматься тем или иным видео кодеком. Можете даже создать картинку с градиентом в 64-битном цвете и с дизерингом, мало ли какие ещё извращения можно придумать. Можно ещё делать видео с быстро движущимися объектами и сохранять его в 1000 кадров в секунду и потом тестировать всякие интерполяторы движения и моушн блюры.
Способ 1: на Python
С помощью скрипта на Python можно создать видео. Просто сохраните этот код в какой-нибудь "main.py"
Далее исполняете команду в консоли: python main.py | ffmpeg -y -f rawvideo -pixel_format gray -video_size 320x240 -framerate 25 -i pipe: out.mkvВ результате у вас получится двухсекундное видео с узором out.mkv.
Как это работает?
В командную строку в Windows и Linux можно выводить не только текст, но и бинарные данные файлов, а так же эти данные можно перенаправлять в другую программу, в данном случае это ffmpeg который принимает RAW кадры и конвертирует их в видео. И в коде и в команде вызова должны совпадать fps/framerate и video_size/w/h иначе всё разъедется. Нельзя просто взять и написать данные пикселей в консоль через print, нужно записывать их в stdout как в файл через os.write.
Если в коде изменить duration на 1, то создастся только один кадр с узором и его можно сохранить как картинку так:python main.py | ffmpeg -y -f rawvideo -pixel_format gray -video_size 320x240 -i pipe: out.png
Способ 2: на C++
Конечно Питон это медленно и я покажу как сделать это на C++, в этом языке стандартный поток вывода stdout тоже считается файлом и в него можно записывать бинарные данные.
Сборка и запуск:
Как сохранить в FFmpeg видео в полном качестве
Я специально не указывал выходной видео кодек для упрощения команд, но вы можете добавить в ffmpeg опции -vcodec libx264rgb -crf 0 для сохранения видео в lossless качестве. Если вы модернизируете программу и добавите в неё поддержку RGBA цвета, то помните что h264 не умеет сохранять прозрачность в кадрах и вам лучше использовать кодек FFV1.
Что ещё можно сделать
- Можно сгенерировать видео на любом языке программирования, если на нём можно переключить стандартный вывод в бинарный режим.
- По такой же логике можно и перенастроить поток ввода stdin в бинарный режим и передать в программу бинарные данные из ffmpeg, таким образом можно будет смастерить видео-фильтр. В общем надо сделать что-то типа того:ffmpeg | фильтр | ffmpeg. Вообще можно просто написать Frei0r фильтр на Си и использовать его в ffmpeg, но мой способ просто не требует никакого стороннего API.
- Поток можно перенаправлять и в файл и потом этот файл скармливать ffmpeg'у, но учтите что видео будет совсем без сжатия и несколько секунд видео 1280x720 будут весить гигабайты. Сделать это можно так:prog > video.datfmpeg -y -f rawvideo -pixel_format gray -video_size 320x240 -framerate 25 -i video.dat out.mkv
- Раз можно сгенерировать сырое видео, то можно и создать сырой PCM звук и конвертировать его в аудио форматы. Можно например генерировать мелодии и сохранять их в pcm_s16le поток. Опять же переключив stdin в pipe режим вы можете получать аудио поток извне, обрабатывать его своей программой и передавать далее, таким образом у вас получится аудио фильтр и не надо никакого VST/LADSPA API.
Заключение
Это очень простой способ создания видео (для программиста). Если что, в ffmpeg уже встроены некоторые генераторы тестовых видео. Сохраняются ли гигабайты сырых кадров в оперативной памяти при использовании такого способа передачи или же на диске - мне это неизвестно, возможно что у такого способа есть какие-то ограничения на размер передаваемых данных. Помните что в передаваемом потоке данных нет никаких меток синхронизации и если что-то где-то потеряется в пути, то видео всё станет кашей, так что не пытайтесь передавать такой поток через net cat (я не пробовал).