I'm trying to build a GUI with Tkinter where a set of images is converted, via press of a button, to an .mp4 video.
When I run the following from the command line, all is well:
> "ffmpeg -r 5 -i ptimage%03d -crf 20 animation.mp4"
However, in Python, the following gives me an error that I think is related to passing the % in the argument:
commandString = "ffmpeg -r 5 -i ptimage%03d -crf 20 animation.mp4"
args = shlex.split(commandString)
p = subprocess.run(args)
The error I get is ptimage%03d: No such file or directory. I'm 99% sure I'm running the command from the right directory; when I run the same command replacing ptimage%03d with ptimage000.jpg, a specific image the list, I get a (really short) video successfully.
I've tried escaping the % with \%, but that doesn't help.
Any ideas?
You omitted the file extension. Use ptimage%03d.jpg, not ptimage%03d. With ptimage%03d ffmpeg is expecting files named ptimage000 ,ptimage001, etc.
ffmpeg -framerate 5 -i ptimage%03d.jpg -crf 20 animation.mp4
Unrelated notes: Some players (YouTube excluded) can't handle such a low frame, so consider adding the -r 10 output option. Same with the chroma subsampling: consider adding -vf format=yuv420p output option.
Related
I want to create an automated way to cut .mp3 files to 45 seconds.
So far I have been able to use ffmpeg to cut the audio to 45 seconds with this command:
ffmpeg -t 45 -i input.mp3 -acodec copy output.mp3
However this does not actually speed anything up, as if I have to do this with each file I might as well use audacity. I know that I should be able to use a .bat file to create a loop for this, however I don't know how to set up the loop. In python I would create a list of the file names in my directory with listdir:
fileNames = listdir(path),
and then create a for loop:
(something like
i = 1
for fileName in fileNames:
x = 2 * int(i)
ffmpeg -t 45 -i str(fileName)+'.mp3' -acodec copy str(x)+'.mp3'
that)
However I don't know how to create something like this in a .bat file. Some help with this, or a way to achieve this in python, would be much appreciated.
You can try using below script. Save the code into a *.bat file in the folder where you have your mp3 songs and execute it and it will process all your songs.
#ECHO OFF
setlocal enableextensions enabledelayedexpansion
set /a count = 1
for %%f in (*.mp3) do (
set "output=!count!.mp3"
ffmpeg -t 45 -i %%f -acodec copy !output!
set /a count+=1
)
endlocal
I have a folder with around 10 different mov files. I would like to add the filename as text on each of the videos using ffmpeg in a bat file. Could someone help me achieve this please?
EDIT:
I have tried using
#ECHO OFF&Setlocal EnableDelayedExpansion
Set INPUT=E:\\Users\\Oli\\Documents\\Projects\\v1.3.0\\downloads3
Set OUTPUT=E:\\Users\\Oli\\Documents\\Projects\\v1.3.0\\downloads3
for %%a in ("%INPUT%\*.*") DO (
set "filename=%%~na"
ffmpeg -i "%%a" -vf "drawtext=text=!fileName:.= !:x=105:y=120:fontfile=E:\\Users\\Oli\\Documents\\Projects\\v1.3.0\\downloads3\\impact.ttf:fontsize=25:fontcolor=white" -b:v 1M -r 60 -b:a 320k -ar 48000 -crf 17 "%%~na.mov"
)`
But it gives me the error:
Cannot find a valid font for the family Sans
[AVFilterGraph # 0000026eb75a9f40] Error initializing filter 'drawtext' with args 'text=FileName1'
Error reinitializing filters!
Failed to inject frame into filter network: No such file or directory
Error while processing the decoded data for stream #0:0
Let's get rid of the variable assignment and simply use variable expansion to set the name. Also, though it will still work, remove the secondary backslash because it is not needed and looks ugly, lastly, always wrap set variables for paths in double quotes. Give this a try.
#echo off
set "INPUT=E:\Users\Oli\Documents\Projects\v1.3.0\downloads3"
set "OUTPUT=E:\Users\Oli\Documents\Projects\v1.3.0\downloads3"
for %%a in ("%INPUT%\*.*") do (
ffmpeg -i "%%~a" -vf "drawtext=text=%%~na:x=105:y=120:fontfile=%~dp0impact.ttf:fontsize=25:fontcolor=white" -b:v 1M -r 60 -b:a 320k -ar 48000 -crf 17 "%%~na.mov"
)
C:\Windows\System32> ffmpeg -i D:\devaraj\KPIX_test.ts -vf "blackframe" -an -f n
ull - 2>&1|find "Parsed" > D:\devaraj\info.txt
this works fine , writes the file info.txt
subprocess.call('ffmpeg' ,'-i', 'D:\devaraj\KPIX_test.ts' ,'-vf', '"blackframe"', 'D:\devaraj\KPIX_textfinal.mp3', '- 2>&1>','|','find', '"Parsed"', '>' ,'D:\devaraj\info.txt', 'shell=True')
gives an error buffer size must be integer
were as
subprocess.call('ffmpeg -i D:\devaraj\KPIX_test.ts -vf "blackframe" -an -f n
ull - 2>&1|find "Parsed" > D:\devaraj\info.txt', shell=True)
gives an error
'find' is not recognized as an internal or external command,
operable program or batch file.
any help would be appreciated from d bottom of heart
you should use native python methods to get filtered ffmpeg output:
ffmpeg -i D:\devaraj\KPIX_test.ts -vf "blackframe" -an -f null - 2>&1|find "Parsed"
To do this, you'd normally require check_output but this particular example is known to provide the required info but exit with a non-zero return code (using run from Python 3.5 would work, though)
So I'll use Popen instead. It becomes (as list, without all redirections and filters), then read all output from process standard output:
p = subprocess.Popen(["ffmpeg","-i",r"D:\devaraj\KPIX_test.ts",
"-vf","blackframe","-an","-f","null"],stdout=subprocess.PIPE,stderr=subprocess.STDOUT)
output = p.stdout.read()
You don't need shell=True, and it merges error & output streams in the output variable.
Now output contains the output of ffmpeg command. Let's decode it (to get a string) and split the lines, check if the string is in the lines:
for line in output.decode().splitlines(): # python 2: output.splitlines()
if "Parsed" in line:
print(line.rstrip()) # or store it in a file, string, whatever
for a process outputting a lot more text, it would be better to iterate on p.stdout instead of reading the full contents (less memory hungry, allows real-time echo to the console)
I am trying to store an image that is the result of ffmpeg.
Using this command, I have frame.png as an external file output:
ffmpeg -flags2 +export_mvs -i video.avi -vf 'select=gte(n\,200),codecview=mv=pf+bf+bb' -vframes 1 frame.png
I want to be able to load the frame.png directly into python, maybe using openCV but without saving it in the computer.
I thought of something like this:
cmd = "ffmpeg -flags2 +export_mvs -i video.avi -vf 'select=gte(n\,200),codecview=mv=pf+bf+bb' -vframes 1 frame.png"
img = cv.imread(sp.Popen(cmd, shell=True, stdout = sp.PIPE, stderr = sp.PIPE).communicate()[0])
But I get an error:
TypeError: bad argument type for built-in operation
Any clue how to do this? The idea is, no frame.png should be generated as a file.
You can set the output file as /dev/stdout (you might need to specify the output format with -f)
Then you redirect your output to your python script like so
ffmpeg options /dev/stdout | python your_script.py
Then you can read this question to see how you can read an image from a file object. Just replace StringIO with sys.stdin
First, I'm not a developer. I'm trying to split a movie into 1 minute clips usinf ffmpeg-split.py python script. I made sure FFmpeg is installed it trying a simple command and it worked like magic:
ffmpeg -i soccer.mp4 -ss 00:00:00 -codec copy -t 10 soccer1.mp4
A new video file was created in the same folder.
I saved the FFmpeg-split.py in the same dir, updated python PATH and typed the following command:
python ffmpeg-split.py -f soccer.mp4 -s 10
what I got back was:
can't determine video length
I believe it just can't find the file. I switched video files and even deleted it and got the same message.
Any ideas?
first time I've seen that name!? Because I believe you were able to run ffmpeg from the command line and execute basic python stuff I recommend following my example as it should avoid any weird directory.connection.stuff in the given file (which i ignored). "Earlier that day": Let me ignore the .py script and share as follows:
Assuming you ran
ffmpeg -i soccer.mp4 ...stuff... soccer1.mp4
from a windows.command.line...
It would be better to write
ffmpeg -t 10 -i "Z:\\full\\input\\path.mp4" -c copy "Z:\\full\\output\\path.mp4"
This says, run ffmpeg, -t=input.duration.seconds, -i=input.file.next,
"fullinpath" in quotes cause spaces etc., -c=all.codecs, copy=atlantian.magic.trick,
"fulloutpath" also to be safe, nothing else!
"Piping" through python to windows works great for this:
import subprocess as subprocess
def pegRunner(cmd): #Takes a list of strings we'll pass to windows.
command = [x for x in cmd] # peg short for mpeg, shoulda used meg.gem.gepm.gipper.translyvania.otheroptions
result = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
output, err = result.communicate()
print result.wait()
return "pegRannered"
#########
# Find the duration from properties or something. If you need to do this
# often it's more complicated. Let's say you found 4mins33secs.
############
leng = 4*60+33 # time in seconds
last_dur = int(leng%60) #remaining time after the 4 one.min.vids
if last_dur == 0: num_vids = int(leng/60)
else: num_vids = int(leng/60)+1
for i in range(num_vids):
da_command = ['ffmpeg']
da_command.append('-ss')
da_command.append(str(i*60))
da_command.append('-t')
if i != num_vids: da_command.append('60')
else: da_command.append(str(last_dur))
da_command.append('-i')
da_command.append('Z:\\full\\input\\path.mp4') #this format!
da_command.append('-c')
da_command.append('copy')
#optionally to overwrite!!!! da_command.append('-y')
da_command.append('Z:\\full\\output\\path\\filename_'+str(i)+'.mp4')
print pegRunner(da_command)
print "Finished "+str(i)+" filez."
This should handle the 1.min pieces and provide a good starting place for ffmpeg from python.