收到一个需求, 移动硬盘里面的视频素材需要剪辑, 但是格式是sony A9录制的4K视频, 对剪辑硬件要求很高, 所以要压缩码率以便实现流畅剪辑的体验. 虽然有proxy这种方式但是常用的人都知道有各种小毛病. 那么这里的需求是preview剪辑完之后再把源视频文件及直接覆盖回去( premiere直接一键relocate )就可以渲染高清视频了.同时也能节约硬盘空间.
那么, 一般常规做法是整理素材,分类,然后使用Adobe Media Encoder批量压缩. 虽然 Adobe Encoder 有 media pool 这种东西但是无法批量转换整个文件夹并且还保持目录结构不变. 然后我有另外网上搜了一圈试用了好几个软件, 要么收费, 要么就是无法保持文件夹结构. 后来一想, 似乎可以自己写一个简单的程序来辅助处理.
C:\python.exe bathconvert.py
参数需要编辑 bathconvert.py 在源代码末尾的主程序入口设定.
import os
import shutil
import subprocess
import datetime as datetime
from colorama import Fore
import json
class MaoFolderConvert:
__support_formats = ['.mp4'] # need all be lowered string, default .mp4
__resume = False
__startfile = ''
__endfile = ''
counter = 0
total_files = 0
pars = []
__cachefile = r"C:\mao_cache.json" # 交换临时文件, 不需要更改, 用于记录当前编码位置, 用于"断点续传"
def __init__(self, formats, pars, resume):
self.__support_formats = formats
self.pars = pars
self.__resume = resume
# 设置当前正在处理,未完成的文件
def setindex(self, index):
with open(self.__cachefile, 'w') as f:
f.write(json.dumps(index, ensure_ascii=False))
f.close()
# 获取当前正在处理,未完成的文件
def getindex(self):
if not os.path.exists(self.__cachefile):
with open(self.__cachefile, 'w') as f:
f.write(json.dumps({'src': '', 'des': ''}, ensure_ascii=False))
f.close()
return ''
with open(self.__cachefile, 'r') as f:
res = json.load(f)
f.close()
return res
def convert(self, src, des):
ffmpegCmd = 'ffmpeg -i "{}" -vcodec libx264 -b:v {} -vf scale={}:-2 -threads 4 "{}" -y '.format(src,
self.pars[0],
self.pars[1],
des)
self.setindex({'src': src, 'des': des})
returncode = subprocess.call(ffmpegCmd)
print(Fore.LIGHTGREEN_EX + '[ ' + datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S") + ' ] Done! => ' + des)
self.counter = self.counter + 1
self.setindex({'src': '', 'des': ''})
def start(self, path, out):
lastfile = self.getindex()
if lastfile is not '' and lastfile['src'] is not '': # 首先处理未完成任务
self.convert(lastfile['src'], lastfile['des'])
for files in os.listdir(path):
name = os.path.join(path, files)
back_name = os.path.join(out, files)
if os.path.isfile(name):
basedir = (os.path.abspath(os.path.dirname(back_name)))
if not os.path.isdir(basedir):
os.makedirs(basedir)
if os.path.splitext(name)[-1].lower() in self.__support_formats:
if not os.path.exists(back_name) or not self.__resume:
self.convert(name, back_name) # only convert what we need
self.total_files = self.total_files + 1
else:
if not os.path.exists(back_name) or not self.__resume:
shutil.copy(name, back_name) # copy other files directly
self.total_files = self.total_files + 1
else:
if not os.path.isdir(back_name):
os.makedirs(back_name)
self.start(name, back_name)
if __name__ == '__main__':
A = r"J:\@CLIENT_RAW\xx" # 源文件夹
B = r"J:\temp\xx" # 目标文件夹指定(自动创建,如果存在则强制覆盖)
autodown = False # 转换完之后是否自动关机,默认不关机
resume = True # 是否断点继续压缩(不从头覆盖), 默认是断点续写模式
pars = [
'3000k', # 要压缩为的目标视频码率
'1280' # 目标视频宽度, 高度自适应
]
starttime = datetime.datetime.now()
print("===== Mao Convert Start =====")
mao = MaoFolderConvert(['.mp4'], pars, resume)
mao.start(A, B)
duration = (datetime.datetime.now() - starttime).seconds
m, s = divmod(duration, 60)
h, m = divmod(m, 60)
duration = "%d:%02d:%02d" % (h, m, s)
print("===== Done! Convert Report =====")
print(
"== Converted video: {}, total files: {}\r\n== Total time: {} ".format(mao.counter,
mao.total_files,
duration))
if autodown:
os.system('shutdown -s -t 1')