From a656377feb6e13c9fe8c4fa0fd9e09250136ffa2 Mon Sep 17 00:00:00 2001 From: Jacob Cody Wimer Date: Tue, 22 Dec 2020 16:02:46 -0500 Subject: [PATCH] Added gpu support, error handling, and better output --- ffmpeg-files.py | 204 ++++++++++++++++++++++++++++++++++++------------ 1 file changed, 153 insertions(+), 51 deletions(-) diff --git a/ffmpeg-files.py b/ffmpeg-files.py index eabb97e..2abf616 100644 --- a/ffmpeg-files.py +++ b/ffmpeg-files.py @@ -1,51 +1,137 @@ import os import sys +import traceback +import subprocess def clearScreen(): os.system('cls' if os.name == 'nt' else 'clear') def convertFilesInImport(): 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 #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(): - if(os.name == "nt"): - return "ffmpeg\\windows\\ffmpeg.exe" + if(sys.platform == "win32"): + return "ffmpeg\\windows" elif(sys.platform == "darwin"): - return "ffmpeg/osx/ffmpeg" + return "ffmpeg/osx" else: + # Linux sys.platform returns 'linux' 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(): - if(os.name == "nt"): + if(sys.platform == "win32"): return "output\\" else: return "output/" def getInputPath(): - if(os.name == "nt"): + if(sys.platform == "win32"): return "input\\" else: return "input/" -def askForResolution(): - 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"): - clearScreen() - print("Tell me about your source video.") - print("1. 4k 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("4. 480p at a slow frame rate (24, 25, 30)") - print("5. 4k 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("8. 480p at a fast frame rate (48, 50, 60)") - inputresolution = input("Please enter a number from above: ") - return inputresolution +# def askForResolution(): +# 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"): +# clearScreen() +# print("Tell me about your source video.") +# print("1. 4k 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("4. 480p at a slow frame rate (24, 25, 30)") +# print("5. 4k 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("8. 480p at a fast frame rate (48, 50, 60)") +# inputresolution = input("Please enter a number from above: ") +# return inputresolution def askForAudioOutput(): audioOutput = "0" @@ -59,32 +145,42 @@ def askForAudioOutput(): audioOutput = input("Please enter a number from above: ") return audioOutput -def getBitrateSetting(): +def getRecommendedBitrateSetting(longest_side,frame_rate): # Bitrates recommended from YouTube: https://support.google.com/youtube/answer/1722171?hl=en - # 4k 24, 25,30 fps - if(resolution == "1"): - return "40000k" - # 1080p 24, 25,30 fps - elif(resolution == "2"): - return "8000k" - # 720p 24, 25,30 fps - elif(resolution == "3"): - return "5000k" - # 480p 24, 25,30 fps - elif(resolution == "4"): - return "2500k" - # 4k 48, 50, 60 fps - elif(resolution == "5"): - return "55000k" - # 1080p 48, 50, 60 fps - elif(resolution == "6"): - return "12000k" - # 720p 48, 50, 60 fps - elif(resolution == "7"): - return "7500k" - # 480p 48, 50, 60 fps - elif(resolution == "8"): - return "4000k" + if frame_rate <= 30: + if longest_side > 1920: + # 4k size + # 40,000k bitrate + return 40000000 + elif longest_side > 1280: + # 1080p size + # 8000k bitrate + return 8000000 + elif longest_side > 852: + # 720p size + # 5000k bitrate + return 5000000 + else: + # 480p size + # 2500k bitrate + return 2500000 + else: + if longest_side > 1920: + # 4k size + # 55,000k bitrate + return 55000000 + elif longest_side > 1280: + # 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(): if(audio == "1"): @@ -99,9 +195,15 @@ def getAudioSetting(): def returnErrorAndExit(message): input(message) - sys.exit() + sys.exit(1) #Run Program -resolution = askForResolution() -audio = askForAudioOutput() -convertFilesInImport() \ No newline at end of file +try: + # resolution = askForResolution() + audio = askForAudioOutput() + convertFilesInImport() + input("Done. Press enter to exit.") +except Exception: + print(traceback.print_exc()) + input("Press enter to exit.") + sys.exit(1) \ No newline at end of file