Added gpu support, error handling, and better output

This commit is contained in:
2020-12-22 16:02:46 -05:00
parent 648306682f
commit a656377feb

View File

@@ -1,51 +1,137 @@
import os import os
import sys import sys
import traceback
import subprocess
def clearScreen(): def clearScreen():
os.system('cls' if os.name == 'nt' else 'clear') os.system('cls' if os.name == 'nt' else 'clear')
def convertFilesInImport(): def convertFilesInImport():
for filename in os.listdir('input'): for filename in os.listdir('input'):
print(f"{getInputPath()}{filename}")
frame_rate_string = getFrameRate(f"{getInputPath()}{filename}")
width = int(getWidth(f"{getInputPath()}{filename}"))
height = int(getHeight(f"{getInputPath()}{filename}"))
frame_rate = eval(frame_rate_string)
print(f"Frame rate is: {frame_rate}")
print(f"Width is: {width}")
print(f"Height is: {height}")
if width > height:
bitrate = getRecommendedBitrateSetting(width,frame_rate)
else:
bitrate = getRecommendedBitrateSetting(height,frame_rate)
print(f"Recommended bitrate: {bitrate}")
current_bitrate = int(getBitrate(f"{getInputPath()}{filename}"))
if current_bitrate < bitrate:
print(f"Current bitrate of {current_bitrate} is less than recommended. Using {current_bitrate}")
print("Converting now...")
#os.path.splitext(filename)[0] gets ride of file extension #os.path.splitext(filename)[0] gets ride of file extension
#yadiff is for deinterlace #yadiff is for deinterlace
ffmpegcmd = getFfmpegPath() + " -i " + getInputPath() + filename + " -vf yadif -b " + getBitrateSetting() + " -minrate " + getBitrateSetting() + " -maxrate " + getBitrateSetting() + " -bufsize " + getBitrateSetting() + " -vcodec libx264" + getAudioSetting() + " -y " + getOutputPath() + os.path.splitext(filename)[0] + ".mp4"
os.system(ffmpegcmd) #Windows and Linux have different nulls. Windows null and Linux /dev/null
FNULL = open(os.devnull, 'w')
ffmpegcmd_nvidia = getFfmpegExe() + " -i " + getInputPath() + filename + " -vf yadif -b " + str(bitrate) + " -minrate " + str(bitrate) + " -maxrate " + str(bitrate) + " -bufsize " + str(bitrate) + " -vcodec h264_nvenc " + getAudioSetting() + " -y " + getOutputPath() + os.path.splitext(filename)[0] + ".mp4"
# shell=True is not safe but needs used because I do not comma separate the commmand like:
# ["ffmpeg.exe", "-i", "etc"]
process = subprocess.Popen(ffmpegcmd_nvidia, stdout=FNULL, stderr=FNULL, shell=True)
process.communicate()[0]
if process.returncode != 0:
print("Using Nvidia gpu failed... Trying Intel.")
ffmpegcmd_intel = ffmpegcmd_nvidia.replace("h264_nvenc", "h264_qsv")
else:
continue
process = subprocess.Popen(ffmpegcmd_intel, stdout=FNULL, stderr=FNULL, shell=True)
process.communicate()[0]
if process.returncode != 0:
print("Using Intel gpu failed... Trying CPU.")
ffmpegcmd = ffmpegcmd_nvidia.replace("h264_qsv", "h264")
else:
continue
process = subprocess.Popen(ffmpegcmd, stdout=FNULL, stderr=FNULL, shell=True)
process.communicate()[0]
if process.returncode != 0:
raise Exception('There was an error running ffmpeg.')
def getFrameRate(file):
ffprobe_args = "-v 0 -of csv=p=0 -select_streams v:0 -show_entries stream=r_frame_rate"
ffprobe_exe = getFfprobeExe()
ffprobe_command = f"{ffprobe_exe} {ffprobe_args} {file}"
# popen then .read() allows setting stdout to a variable
return os.popen(ffprobe_command).read()
def getWidth(file):
ffprobe_args = "-v 0 -of csv=p=0 -select_streams v:0 -show_entries stream=width"
ffprobe_exe = getFfprobeExe()
ffprobe_command = f"{ffprobe_exe} {ffprobe_args} {file}"
# popen then .read() allows setting stdout to a variable
return os.popen(ffprobe_command).read()
def getHeight(file):
ffprobe_args = "-v 0 -of csv=p=0 -select_streams v:0 -show_entries stream=height"
ffprobe_exe = getFfprobeExe()
ffprobe_command = f"{ffprobe_exe} {ffprobe_args} {file}"
# popen then .read() allows setting stdout to a variable
return os.popen(ffprobe_command).read()
def getBitrate(file):
ffprobe_args = "-v 0 -of csv=p=0 -select_streams v:0 -show_entries stream=bit_rate"
ffprobe_exe = getFfprobeExe()
ffprobe_command = f"{ffprobe_exe} {ffprobe_args} {file}"
# popen then .read() allows setting stdout to a variable
return os.popen(ffprobe_command).read()
def getFfmpegPath(): def getFfmpegPath():
if(os.name == "nt"): if(sys.platform == "win32"):
return "ffmpeg\\windows\\ffmpeg.exe" return "ffmpeg\\windows"
elif(sys.platform == "darwin"): elif(sys.platform == "darwin"):
return "ffmpeg/osx/ffmpeg" return "ffmpeg/osx"
else: else:
# Linux sys.platform returns 'linux'
returnErrorAndExit("Your os is not supported. Try using Windows or OSX") returnErrorAndExit("Your os is not supported. Try using Windows or OSX")
def getFfmpegExe():
path = getFfmpegPath()
if(sys.platform == "win32"):
return f"{path}\\ffmpeg.exe"
else:
return f"{path}/ffmpeg"
def getFfprobeExe():
path = getFfmpegPath()
if(sys.platform == "win32"):
return f"{path}\\ffprobe.exe"
else:
return f"{path}/ffprobe"
def getOutputPath(): def getOutputPath():
if(os.name == "nt"): if(sys.platform == "win32"):
return "output\\" return "output\\"
else: else:
return "output/" return "output/"
def getInputPath(): def getInputPath():
if(os.name == "nt"): if(sys.platform == "win32"):
return "input\\" return "input\\"
else: else:
return "input/" return "input/"
def askForResolution(): # def askForResolution():
inputresolution = "0" # inputresolution = "0"
while(inputresolution != "1" and inputresolution != "2" and inputresolution != "3" and inputresolution != "4" and inputresolution != "5" and inputresolution != "6" and inputresolution != "7" and inputresolution != "8"): # while(inputresolution != "1" and inputresolution != "2" and inputresolution != "3" and inputresolution != "4" and inputresolution != "5" and inputresolution != "6" and inputresolution != "7" and inputresolution != "8"):
clearScreen() # clearScreen()
print("Tell me about your source video.") # print("Tell me about your source video.")
print("1. 4k at a slow frame rate (24, 25, 30)") # print("1. 4k at a slow frame rate (24, 25, 30)")
print("2. 1080p at a slow frame rate (24, 25, 30)") # print("2. 1080p at a slow frame rate (24, 25, 30)")
print("3. 720p at a slow frame rate (24, 25, 30)") # print("3. 720p at a slow frame rate (24, 25, 30)")
print("4. 480p at a slow frame rate (24, 25, 30)") # print("4. 480p at a slow frame rate (24, 25, 30)")
print("5. 4k at a fast frame rate (48, 50, 60)") # print("5. 4k at a fast frame rate (48, 50, 60)")
print("6. 1080p at a fast frame rate (48, 50, 60)") # print("6. 1080p at a fast frame rate (48, 50, 60)")
print("7. 720p at a fast frame rate (48, 50, 60)") # print("7. 720p at a fast frame rate (48, 50, 60)")
print("8. 480p at a fast frame rate (48, 50, 60)") # print("8. 480p at a fast frame rate (48, 50, 60)")
inputresolution = input("Please enter a number from above: ") # inputresolution = input("Please enter a number from above: ")
return inputresolution # return inputresolution
def askForAudioOutput(): def askForAudioOutput():
audioOutput = "0" audioOutput = "0"
@@ -59,32 +145,42 @@ def askForAudioOutput():
audioOutput = input("Please enter a number from above: ") audioOutput = input("Please enter a number from above: ")
return audioOutput return audioOutput
def getBitrateSetting(): def getRecommendedBitrateSetting(longest_side,frame_rate):
# Bitrates recommended from YouTube: https://support.google.com/youtube/answer/1722171?hl=en # Bitrates recommended from YouTube: https://support.google.com/youtube/answer/1722171?hl=en
# 4k 24, 25,30 fps if frame_rate <= 30:
if(resolution == "1"): if longest_side > 1920:
return "40000k" # 4k size
# 1080p 24, 25,30 fps # 40,000k bitrate
elif(resolution == "2"): return 40000000
return "8000k" elif longest_side > 1280:
# 720p 24, 25,30 fps # 1080p size
elif(resolution == "3"): # 8000k bitrate
return "5000k" return 8000000
# 480p 24, 25,30 fps elif longest_side > 852:
elif(resolution == "4"): # 720p size
return "2500k" # 5000k bitrate
# 4k 48, 50, 60 fps return 5000000
elif(resolution == "5"): else:
return "55000k" # 480p size
# 1080p 48, 50, 60 fps # 2500k bitrate
elif(resolution == "6"): return 2500000
return "12000k" else:
# 720p 48, 50, 60 fps if longest_side > 1920:
elif(resolution == "7"): # 4k size
return "7500k" # 55,000k bitrate
# 480p 48, 50, 60 fps return 55000000
elif(resolution == "8"): elif longest_side > 1280:
return "4000k" # 1080p size
# 12,000k bitrate
return 12000000
elif longest_side > 852:
# 720p size
# 7500k bitrate
return 7500000
else:
# 480p size
# 4000k bitrate
return 4000000
def getAudioSetting(): def getAudioSetting():
if(audio == "1"): if(audio == "1"):
@@ -99,9 +195,15 @@ def getAudioSetting():
def returnErrorAndExit(message): def returnErrorAndExit(message):
input(message) input(message)
sys.exit() sys.exit(1)
#Run Program #Run Program
resolution = askForResolution() try:
audio = askForAudioOutput() # resolution = askForResolution()
convertFilesInImport() audio = askForAudioOutput()
convertFilesInImport()
input("Done. Press enter to exit.")
except Exception:
print(traceback.print_exc())
input("Press enter to exit.")
sys.exit(1)