Страницы

Поиск по вопросам

вторник, 31 декабря 2019 г.

Python и FFmpeg: создать видео с аудио за один проход или иначе ускорить процесс

#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 и имя выходного файла.

Комментариев нет:

Отправить комментарий