#python #windows #ffmpeg #видео
пытаюсь подружить Python и FFmpeg под Windows.
Необходимо из набора изображений и звуковых дорожек (хранятся в памяти программы
как numpy array) сделать одно длинное видео.
Смог сделать это в 2 прохода:
сначала создаю видео файл без аудио дорожки,
потом прибавляю к нему аудио, но хотелось бы оптимальнее и быстрее:
ffmpeg -y -f rawvideo -vcodec rawvideo -s 1920x1080 -pix_fmt bgr24 -r 5.00 ^
-i pipe:0 -an -vcodec libx264 -preset medium -pix_fmt yuv420p video.avi
- создаю видео
ffmpeg -y -f s16le -acodec pcm_s16le -ar 44100 -ac 1 ^
-i pipe:0 -i video.avi -c:v h264 -c:a ac3 videoANDaudio.avi
- добавляю аудио.
Можно ли сделать это в 1 проход? Т.е. как то надо передавать видео и аудио одновременно,
в 2 потока. Думал о NamedPipe, но не нашел в интернете информацию о том, как его создать
в Windows.
PS. Предложите технологию, лучше FFmpeg, для решения задачи, если такая имеется.
Ответы
Ответ 1
Про обработку видео в Python
PS. Предложите технологию, лучше FFMpeg для решения задачи, если такая
имеется.
PyAV
Есть такой замечаетельный проект: PyAV.
Это питонячие биндинги к libav.
Для работы с видео в Python я использую его.
АPI PyAV сильно не совпадает с аргументами ffmpeg и ванильного libav,
но при этом оно кажется весьма понятным и логичным в контексте Python.
PyAV: Пример
https://gist.github.com/w495/7d843bd5d42fc35e15486ec60a87d9bf
import av
from av.video.frame import VideoFrame
from av.video.stream import VideoStream
# В этом списке будем хранить кадры в виде numpy-векторов.
array_list = []
# Откроем контейнер на чтение
input_container = av.open('input.mp4')
# Применим «инверсное мультиплексирование» =)
# Получим пакеты из потока.
input_packets = input_container.demux()
# Получии все кадры видео и положим их в `array_list`.
for packet in input_packets:
if isinstance(packet.stream, VideoStream):
# Получим все кадры пакета
frames = packet.decode()
for raw_frame in frames:
# Переформатируем кадры, к нужному размеру и виду.
# Это лучше делать средствами pyav (libav)
# потому что быстрее.
frame = raw_frame.reformat(32, 32, 'rgb24')
# Превратить каждый кадр в numpy-вектор (dtype=int).
array = frame.to_nd_array()
# Положим в список numpy-векторов.
array_list += [array]
# Откроем контейнер на запись.
output_container = av.open('out.mp4', mode='w', format='mp4')
# Добавим к контейнеру поток c кодеком h264.
output_stream = output_container.add_stream('h264', rate=25)
# В этом списке будем хранить пакеты выходного потока.
output_packets = []
# Пройдем по списку векторов и упакуем их в пакеты выходного протока.
for array in array_list:
# Построим видео-кадр по вектору.
frame = VideoFrame.from_ndarray(array, format='rgb24')
# Запакуем полученный кадр.
packet = output_stream.encode(frame)
# Положим в список пакетов.
output_packets += [packet]
# Применим «прямое мультиплексирование» =)
# Для каждого пакета вызовем мультиплексор.
for packet in output_packets:
if packet:
output_container.mux(packet)
output_container.close()
Еще примеры:
encode_frames.py — создает видео из последовательности переданных изображений; для
работы с изображениями использует OpenCV. На самом деле, тут можно обойтись и без OpenCV.
gen_rgb_rotate.py — создает видео, в котором цвет кадра меняется в последовательности
цветов радуги.
encode.py — записывает кадры исходного видео, до тех пор пока, их количество видео-кадров
не превысит 100.
Сам я активно использую PyAV в этом проекте: Video Shot Detector.
Возможно, в его коде будет что-то полезное для Вас.
https://github.com/w495/python-video-shot-detector
PyAV: Установка
Есть маленькая проблема в том, что PyAV достаточно тяжело собрать. Тем более под
Windows.
Для сборки из исходников требуются конкретные версии зависимостей (ffmpeg, h264 и
пр.) Но есть уже готовые сборки для питонячьего пакетного менеджера conda.
Я не пробовал, но кажется достаточно просто поставить conda на Windows:
Выбираете нужный вам инсталлятор тут: Miniconda. Я предполагаю что это будет Python
2.7 64-bit (exe installer)
Дальше запускаете и следуете его инструкциям.
Далее как описано в Windows Miniconda Install в командной строке Windows conda list .
После этого вам потребуется поставить нужные пакеты.
conda install numpy
conda install -c danielballan pyav
# или conda install -c soft-matter pyav
Я не уверен, что это все заведется под Windows — я не пробовал.
Если нет, то на официальном сайте есть инcтрукция как собрать самостоятельно:
PyAV Installation On Windows;
+ еще есть вот такая заметка PyAV for Windows.
Авторы библиотеки весьма отзывчивы, и им можно смело задавать вопросы и писать о
проблемах тут: PyAV Issues.
Альтернативы
Из альтернатив, еще наталкивался на
Avpy — просто биндинг к ffmpeg и libav;
pyffmpeg — тоже биндинг к ffmpeg;
ffmpeg-cffi-py — еще один биндинг, работает только на Windows;
pyVideoInput — используют свой обработчик видео без ffmpeg, выглядит он слишком заморочено;
Python GStreamer — используют свой обработчик видео, пока не осилил, но при беглом
осмотре, выглядит удобным;
Python OpenCV — обработка видео через OpenCV изначально похожа на забивание гвоздей
микроскопом;
MoviePy — библиотека для не линейного монтажа видео, но кодировать ей тоже можно.
Отчасти тоже забивание гвоздей микроскопом.
Про Python GStreamer:
Using GStreamer with Python;
Getting started with GStreamer with Python;
Python GStreamer Tutorial.
Качество альтернатив
Avpy у меня почему-то так и не завелся.
pyffmpeg тоже сходу не собирается, а перед началом установки приходится править код.
ffmpeg-cffi-py не смог найти путей до нужных библиотек.
Остальные пока не пробовал.
Ответ 2
Про FFMpeg
Кратко
Можно ли сделать это в 1 проход
Можно. Для этого нужно ffmpeg в одной команде передать и звук и видео.
Это легко делается, если просто исходной команде передать еще один входной поток
ffmpeg
-f rawvideo -codec:v rawvideo
-s 1920x1080
-pix_fmt bgr24
-r 5.00
-i pipe:0 # video
-f s16le -codec:a pcm_s16le
-ar 44100
-ac 1
-i pipe:0 # audio
-codec:v libx264
-preset medium
-pix_fmt yuv420p
-codec:a ac3
Но проблема в том, что вы посылаете ему изображения и семплы через stdin (pipe:0).
Самым простым решением было бы:
Записать изображения из Вашей программы в один файл.
Семплы записать в другой файл.
причем эти действия можно совершать параллельно (multiprocessing);
Оба файла скормить как -i filename.
Предполагаю, что это будет работать быстрее.
но хотелось бы оптимальнее и быстрее.
Для FFmpeg ключ -threads со значением 0 позволяет работаеть многопоточно, и распределять
вычисления по всем процессорам.
Много букв
У ffmpeg есть некоторая особенность — можно передавать много одноименных аргументов.
Причем порядок этих аргументов имеет значение. Во многих случаях эта особенность не
очень заментна. Но в какой-то момент сильно выстреливает.
Порядок аргументов подчиняется следуюшей логике.
Параметры входных потоков
сначала общие:
формат, смещение по времени и пр, что-то типа:
-ss '00:05:00' — кодировать с пятой минуты.
потом раздельные:
видео, аудио, субтитры:
-f rawvideo -codec:v rawvideo ...;
-f s16le -codec:a pcm_s16le ...;
Параметры выходных потоков
сначала раздельные:
видео, аудио, субтитры:
-codec:v 'libx264' -profile:v 'main' -b:v '1000k' -filter:v "yadif=1:-1:0,scale=0:576";
-strict 'experimental' -codec:a 'aac' -b:a '196k' -ac '6'.
потом общие:
смещение по времени, формат контейнера и пр, что-то типа:
* -ss '00:05:01' -to '00:05:30'
* -movflags '+faststart' -f 'mp4' -y file_name.mp4
При соблюдении этой логики можно будет в одной команде собирать,
видео более чем из одного потока.
И даже больше чем в один поток: Creating Multiple Outputs.
У меня под рукой есть несколько примеров (из Bulk Video Converter). Кажется, что
запись видео с экрана и аудио с микрофона очень похожа на вашу ситуацию.
/usr/bin/ffmpeg \
-threads '0' \
-f x11grab \
-s wxga \
-i ':0.0' \
-f alsa \
-i hw:0 \
-codec:v 'libx264' \
-profile:v 'main' \
-b:v '1000k' \
-filter:v "yadif=1:-1:0,scale=0:576" \
-codec:a 'libmp3lame' \
-b:a '196k' \
-f 'mp4' -y 'video_pal_sd.mp4' \
На *nix это работает. Предполагаю, что на Windows будет работать с точностью до имен
кодеров. Я не до конца понимаю, вашу задачу, потому тут сейчас распишу, что означает
каждый из аргументов.
Настройки входных потоков:
Входное видео:
-f x11grab — формат аудио, в моем случае — название драйвера;
-s wxga — размер входного видео-кадра, тут мы говорим ffmpeg как воспринимать, то
что мы его передаем — в моем случае: размер моего монитора —
-i ':0.0' — тут, номер дисплея, но возомжен любой иной источник;
Входное аудио:
-f 'alsa — формат аудио, в моем случае — название драйвера аудио-карты;
-i 'hw:0' — тут, аудио-карты (входной поток — т.е. микрофон), но возможен любой иной
источник;
Настройки результирующих потоков:
Результирующее видео:
-codec:v 'libx264' — определяем кодек, которым будем кодировать видео (h264).
-profile:v 'main' — профиль кодирования для h264.
-b:v '1000k' — битрейт для видео-потока.
-filter:v "yadif=1:-1:0,scale=0:576" — фильтры для видео-потока:
yadif=1:-1:0 — убирает чересстрочность;
scale=0:576 — приводит выходной видео-поток к ножному размеру — этот размер никак
не соотносится с размером входного потока.
Результирующее аудио:
-codec:a 'libmp3lame' — определяем кодек, которым будем кодировать аудио (mp3).
-b:a '196k' — битрейт для аудио-потока.
Настройки результирующего файла (контейнера):
* -f 'mp4' — формат, можно не указывать, и тогда ffmpeg попробует «догадаться» сам;
* -y 'video_pal_sd.mp4' — флаг перезаписи -y и имя выходного файла.