View Full Version : Help piping video+audio to ffmpeg in python script
groucho86
25th July 2025, 18:24
Hello,
I know vspipe is preferred (and the "output" class function near-deprecated), but hoping to find a way, without the need of temp audio files, to write video+audio via ffmpeg in a python script.
Video-only is very easy:
cmd_l = ['ffmpeg, '-f', 'yuv4mpegpipe',
'-i', '-',
'-c:v', 'prores', '-profile:v', '3','-an',
'-y']
pipe = subprocess.Popen(cmd_l,stdin=subprocess.PIPE, shell=False)
clip.output(fileobj=pipe.stdin, y4m=True)
pipe.communicate()
Since audio node doesn't have output... any suggestions? I'm on Mac/Linux so can absolutely use multiple named pipes.
_Al_
26th July 2025, 16:57
You can always run subprocess using ffmpeg a using -i your_video.mp4 as a source to encode audio first, then using your lines to encode video and then to mux them.
Than makes audio from your source directly in the script, as long as clip is not trimmed of course, to have same video and audio lengths.
But I'd rather separate actual vapoursynth utilities from pynthon utilities, something like shown here (https://forum.videohelp.com/threads/404931-x264-[error]-x264_encoder_encode-failed#post2650039) for example, to separate vapoursynth action from other action in separate, dedicated module - vapoursynth script.
Myrsloik
26th July 2025, 19:10
Hello,
I know vspipe is preferred (and the "output" class function near-deprecated), but hoping to find a way, without the need of temp audio files, to write video+audio via ffmpeg in a python script.
Video-only is very easy:
cmd_l = ['ffmpeg, '-f', 'yuv4mpegpipe',
'-i', '-',
'-c:v', 'prores', '-profile:v', '3','-an',
'-y']
pipe = subprocess.Popen(cmd_l,stdin=subprocess.PIPE, shell=False)
clip.output(fileobj=pipe.stdin, y4m=True)
pipe.communicate()
Since audio node doesn't have output... any suggestions? I'm on Mac/Linux so can absolutely use multiple named pipes.
First spawn a vspipe instance that outputs the audio as to a named pipe. Then do the rest as you do now and specify the named pipe as the audio source.
Or something like that. Never tested it myself. I'll probably add avi container output to vspipe in the not too distant future.
_Al_
2nd August 2025, 02:47
wanted to actually test those named pipes using portable python 3.12 and portable vapoursynth R72.
Dumped portable python into a directory, dumped portable vapoursynth into same directory but running test.py:
import sys
print(sys.version)
import vapoursynth as vs
gives me:
3.12.10 (tags/v3.12.10:0cc8128, Apr 8 2025, 12:21:36) [MSC v.1943 64 bit (AMD64)]
Traceback (most recent call last):
File "G:\test\test.py", line 3, in <module>
import vapoursynth as vs
ModuleNotFoundError: No module named 'vapoursynth'
It used to work before like that, what do i need to do to "install" vaporsynth for that directory?
Selur
2nd August 2025, 05:43
@_Al_: Here's what I usually do:
create an empty Vapoursynth folder.
download portable Python (3.12), i.e.: https://www.python.org/ftp/python/3.12.9/python-3.12.9-embed-amd64.zip
download portable Vapoursynth, i.e.: https://github.com/vapoursynth/vapoursynth/releases/download/R72/VapourSynth64-Portable-R72.zip
extract Python and then Vapoursynth into the Vapoursynth folder
change the content of the python312._pth to
Scripts
Lib\site-packages
python312.zip
.
# Uncomment to run site.main() automatically
#import site
Install pip&Vapoursynth
download the pip installer https://bootstrap.pypa.io/get-pip.py and move it into your Vapoursynth folder
open a terminal inside the Vapoursynth-folder and call python get-pip.py
integrate Vapoursynth to the environment by calling:
python -m pip install wheel/vapoursynth-72-cp312-abi3-win_amd64.whl
Hope that helps.
Cu Selur
_Al_
2nd August 2025, 06:19
that works!, thanks
_Al_
3rd August 2025, 01:02
testing named pipes, small steps, just working with audio,
my_vapoursynth_script.py:
from vapoursynth import core
audio = core.std.BlankAudio()
audio.set_output(1)
server.bat:
@echo off
VSPipe "my_vapoursynth_script.py" --container wav --outputindex 1 "\\.\pipe\my_audio_pipe"
pause
client.bat:
@echo off
ffmpeg -i "\\.\pipe\my_audio_pipe" -c:a libopus -b:a 96k -y "output.opus"
pause
It works, running server.bat (that creates pipe) and then client.bat(encodes audio).
But for using python AND windows, there perhaps is some woo-doo must be involved, simply, running python (below) does nothing. Syntax is perhaps correct, but pipe is not created, it just runs thru code, prints command lines but does nothing:
from vapoursynth import core
audio = core.std.BlankAudio()
audio.set_output(1)
if not __name__ == '__vapoursynth__': #this block is not run when vspipe is involved, vspipe sets __name__ to '__vapoursynth__'
import sys
import subprocess
import shlex
from pathlib import Path
ffmpeg = r'G:\tools\ffmpeg.exe'
ffmpeg = Path(ffmpeg).as_posix() # so path is printed correctly for windows
scripts_directory = Path(__file__).parent
vspipe = scripts_directory / 'VSPipe.exe'
vspipe = vspipe.as_posix()
pipe_name = Path(r'\\.\pipe\my_audio_pipe').as_posix()
output = scripts_directory / 'output.opus'
output = output.as_posix()
audio_cmd = f'"{vspipe}" "{__file__}" --container wav --outputindex 1 "{pipe_name}" '
print(audio_cmd)
audio_pipe = subprocess.Popen(shlex.split(audio_cmd), stdout=subprocess.PIPE, stderr=subprocess.PIPE)
audio_pipe.communicate()
encode_cmd = f'"{ffmpeg}" -c:a libopus -b:a 96k -y -i "{pipe_name}" -y "{output}"'
print(encode_cmd)
encode_pipe = subprocess.Popen(shlex.split(encode_cmd), stdout=subprocess.PIPE, stderr=subprocess.PIPE)
encode_pipe.communicate()
found this: https://stackoverflow.com/questions/48542644/python-and-windows-named-pipes, hopefully something like that does not have to be involved on Windows.
Myrsloik
3rd August 2025, 17:11
@_Al_: Here's what I usually do:
...
Hope that helps.
Cu Selur
The portable install script does all this automatically. I strongly recommend it.
Selur
3rd August 2025, 18:03
Good to know, never looked at it since I usually don't use PowerShell and my steps worked. :)
Cu Selur
_Al_
3rd August 2025, 20:31
That"auto portable install" works, thanks!
Install-Portable-VapourSynth-R72.ps1 from vapoursynth release page: https://github.com/vapoursynth/vapoursynth/releases , running that power shell script (right click, Run with PowerShell) it does all automatically:
Installing...
Determining latest Python 3.13.x version...
Python version 3.13.5 will be used for installation
Downloading Python...
Downloading VapourSynth...
Downloading Pip...
Extracting Python...
Installing Pip...
Collecting pip
Using cached pip-25.2-py3-none-any.whl.metadata (4.7 kB)
Using cached pip-25.2-py3-none-any.whl (1.8 MB)
Installing collected packages: pip
Successfully installed pip-25.2
Extracting VapourSynth...
Installing VapourSynth...
Processing g:\my_programs\auto install test\vapoursynth-portable\wheel\vapoursynth-72-cp312-abi3-win_amd64.whl
Installing collected packages: vapoursynth
Successfully installed vapoursynth-72
Installation complete
Press Enter to continue...:
It creates automatically vapoursynth-portable directory in directory where Install-Portable-VapourSynth-R72.ps1 sits in,
all seams to work, thanks. It does not delete a temp directory vs-temp-dl (with downloaded python, vs and pip) so that could be deleted manually if those are not needed.
But can anyone give some light how to make named pipes work using a python script using windows?
A simple example would be enough.
_Al_
3rd August 2025, 22:12
Succes, finally, it was syntax problem in paths and also, two python scripts need to be launched as it seams, not sure, so having:
simple_vapoursynth_script.py:
from vapoursynth import core
audio = core.std.BlankAudio()
audio.set_output(1)
then server.py:
import subprocess
from pathlib import Path
scripts_directory = Path(__file__).parent
vspipe = scripts_directory / 'VSPipe.exe'
vspipe = vspipe.as_posix()
vapoursynth_script = scripts_directory / 'simple_vapoursynth_script.py'
vapoursynth_script = vapoursynth_script.as_posix()
pipe_name = Path(r'\\.\pipe\my_audio_pipe')
audio_cmd = [f'{vspipe}',
f'{vapoursynth_script}',
'--container', 'wav',
'--outputindex', '1',
f'{pipe_name}']
print(audio_cmd)
audio_pipe = subprocess.Popen(audio_cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
out, err = audio_pipe.communicate()
print(out.decode())
print(err.decode())
then client.py:
import subprocess
from pathlib import Path
ffmpeg =r'G:\tools\ffmpeg.exe'
ffmpeg = Path(ffmpeg).as_posix() # so path is printed correctly for windows
pipe_name = Path(r'\\.\pipe\my_audio_pipe')
output = Path(__file__).parent / '__output.opus'
encode_cmd = [f'{ffmpeg}',
'-i', f'{pipe_name}',
'-c:a', 'libopus',
'-b:a', '96k',
'-y',
f'{output}']
print(encode_cmd)
video_pipe = subprocess.Popen(encode_cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
out, err = video_pipe.communicate()
print(err.decode())
then running:
python server.py
python client.py
would encode file, :-), so this could be modified to different needs ...
_Al_
3rd August 2025, 23:08
this works too, using shlex, problem was correct syntax for pipe_name
simple_vapoursynth_script.py:
from vapoursynth import core
audio = core.std.BlankAudio()
audio.set_output(1)
server.py:
import subprocess
from pathlib import Path
import shlex
scripts_directory = Path(__file__).parent
vspipe = scripts_directory / 'VSPipe.exe'
vspipe = vspipe.as_posix()
vapoursynth_script = scripts_directory / 'simple_vapoursynth_script.py'
vapoursynth_script = vapoursynth_script.as_posix()
pipe_name = r'\\\\.\\pipe\\my_audio_pipe'
audio_cmd = f'"{vspipe}" "{vapoursynth_script}" --container wav --outputindex 1 "{pipe_name}"'
audio_pipe = subprocess.Popen(shlex.split(audio_cmd), stdout=subprocess.PIPE, stderr=subprocess.PIPE)
out, err = audio_pipe.communicate()
print(err.decode())
client.py:
import subprocess
from pathlib import Path
import shlex
ffmpeg = r'D:\My_programs\ffmpeg.exe'
ffmpeg = Path(ffmpeg).as_posix() # so path is printed correctly for windows
pipe_name = r'\\\\.\\pipe\\my_audio_pipe'
output = Path(__file__).parent / '__output.opus'
encode_cmd = f'"{ffmpeg}" -i "{pipe_name}" -c:a libopus -b:a 96k -y "{output}"'
video_pipe = subprocess.Popen(shlex.split(encode_cmd), stdout=subprocess.PIPE, stderr=subprocess.PIPE)
out, err = video_pipe.communicate()
print(err.decode())
_Al_
4th August 2025, 04:56
So finally op solution, creating named pipe for audio (audio is not physicaly created), then creating video (using output attribute for a vapoursynth clip) and muxing video+audio at the same time.
This was tested on Windows, not sure if it works on linux, there might be differences.
All is in one script, that serves as vapoursynth script because actual encoding is in a block that vspipe does not get into.
Not sure if this is a good idea, perhaps better is to separate vapoursynth action and other utility functions in separate scripts.
Note that namedpipe is created in a separate thread, otherwise code would just lock, does not matter what. So to put it all in one script that thread thing worked.
Also printing into a console could be out of order, overlapping. But my console prints stderr in red so it could be more clear what is a code print and what is stderr print.
This is a crude solution, there are ways how to print seeing actual progress etc.
import vapoursynth as vs
from vapoursynth import core
video = core.std.BlankClip(format=vs.YUV420P8)
audio = core.std.BlankAudio()
video.set_output(0)
audio.set_output(1)
def start_named_pipe(cmd):
print('creating named pipe:')
print(cmd)
process = subprocess.Popen(shlex.split(cmd), stdout=subprocess.PIPE, stderr=subprocess.PIPE)
_, err = process.communicate()
if err:
print(err.decode())
if not __name__ == '__vapoursynth__':
'''
encoding a video from vapoursynth script where ffmpeg loads named pipe audio,
so audio is actually NOT physically saved on disk first
'''
import subprocess
from pathlib import Path
import shlex
import threading
import time
# creating audio named pipe
scripts_directory = Path(__file__).parent
vspipe = scripts_directory / 'VSPipe.exe'
vspipe = vspipe.as_posix()
pipe_name = r'\\\\.\\pipe\\my_audio_pipe'
audio_cmd = f'"{vspipe}" "{__file__}" --container wav --outputindex 1 "{pipe_name}"'
# starting named pipe in a thread, otherwise process would lock after launching named pipe
my_thread = threading.Thread(target=start_named_pipe, args=(audio_cmd,))
my_thread.start()
time.sleep(2) # wait for named pipe to be created!
# encoding video and audio
ffmpeg = r'G:\tools\ffmpeg.exe'
ffmpeg = Path(ffmpeg).as_posix()
output = Path(__file__).parent / 'output.mkv'
cmd_l = (f'"{ffmpeg}" -f yuv4mpegpipe -i "-" -i "{pipe_name}" -map 0:v:0 -map 1:a:0 '
'-c:v libx264 -crf 18 '
'-c:a libopus -b:a 96k '
'-y '
f'"{output}"')
print(cmd_l)
process = subprocess.Popen(shlex.split(cmd_l), stdin=subprocess.PIPE)
video.output(fileobj=process.stdin, y4m=True)
_, err = process.communicate()
if err:
print(err.decode())
EDIT: noticed I left "{vapoursynth_script}" in audio_cmd command line(vspipe cmd line). But there should be "{__file__}" instead, so it takes audionode from this script.
So I corrected it.
vBulletin® v3.8.11, Copyright ©2000-2025, vBulletin Solutions Inc.