|
@@ -37,45 +37,14 @@ class FFmpeg():
|
|
|
return 0
|
|
|
|
|
|
|
|
|
- """
|
|
|
- 获取视频文件的时长(秒)
|
|
|
- """
|
|
|
- @classmethod
|
|
|
- def get_videos_duration(cls, video_file):
|
|
|
- result = subprocess.run(
|
|
|
- ["ffprobe", "-v", "error", "-show_entries", "format=duration",
|
|
|
- "-of", "default=noprint_wrappers=1:nokey=1", video_file],
|
|
|
- capture_output=True, text=True)
|
|
|
- return float(result.stdout)
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
- """
|
|
|
- 视频裁剪
|
|
|
- """
|
|
|
- @classmethod
|
|
|
- def video_tailor(cls, video_url):
|
|
|
- output_video_path = ''
|
|
|
- try:
|
|
|
- # 获取视频的原始宽高信息
|
|
|
- width, height = cls.get_w_h_size(video_url)
|
|
|
- # 计算裁剪后的高度
|
|
|
- new_height = int(height * 0.8)
|
|
|
- # 构建 FFmpeg 命令,裁剪视频高度为原始高度的70%,并将宽度缩放为320x480
|
|
|
- ffmpeg_cmd = [
|
|
|
- "ffmpeg",
|
|
|
- "-i", video_url,
|
|
|
- "-vf", f"crop={width}:{new_height},scale=320:480",
|
|
|
- "-c:v", "libx264",
|
|
|
- "-c:a", "aac",
|
|
|
- "-y",
|
|
|
- output_video_path
|
|
|
- ]
|
|
|
- # 执行 FFmpeg 命令
|
|
|
- subprocess.run(ffmpeg_cmd, check=True)
|
|
|
- return output_video_path
|
|
|
- except Exception as e:
|
|
|
- return None
|
|
|
+ # """
|
|
|
+ # 获取视频文件的时长(秒)
|
|
|
+ # """
|
|
|
+ # @classmethod
|
|
|
+ # def get_videos_duration(cls, video_file):
|
|
|
+ # result = cls.asyncio_run_subprocess(["ffprobe", "-v", "error", "-show_entries", "format=duration",
|
|
|
+ # "-of", "default=noprint_wrappers=1:nokey=1", video_file], timeout=10)
|
|
|
+ # return float(result)
|
|
|
|
|
|
"""
|
|
|
获取视频宽高
|
|
@@ -84,10 +53,8 @@ class FFmpeg():
|
|
|
def get_w_h_size(cls, new_video_path):
|
|
|
try:
|
|
|
# 获取视频的原始宽高信息
|
|
|
- ffprobe_cmd = f"ffprobe -v error -select_streams v:0 -show_entries stream=width,height -of csv=p=0 {new_video_path}"
|
|
|
- ffprobe_process = subprocess.Popen(ffprobe_cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True)
|
|
|
- output, _ = ffprobe_process.communicate()
|
|
|
- output_decoded = output.decode().strip()
|
|
|
+ ffprobe_cmd = cls.asyncio_run_subprocess(["ffprobe", "-v" ,"error" ,"-select_streams" ,"v:0" ,"-show_entries", "stream=width,height" ,"-of" ,"csv=p=0" ,new_video_path],timeout=10)
|
|
|
+ output_decoded = ffprobe_cmd.strip()
|
|
|
split_output = [value for value in output_decoded.split(',') if value.strip()]
|
|
|
height, width = map(int, split_output)
|
|
|
return width, height
|
|
@@ -102,16 +69,16 @@ class FFmpeg():
|
|
|
def video_crop(cls, video_path, file_path):
|
|
|
crop_url = file_path + 'crop.mp4'
|
|
|
# 获取视频的原始宽高信息
|
|
|
- ffprobe_cmd = f"ffprobe -v error -select_streams v:0 -show_entries stream=width,height -of csv=p=0 {new_video_path}"
|
|
|
- ffprobe_process = subprocess.Popen(ffprobe_cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True)
|
|
|
- output, _ = ffprobe_process.communicate()
|
|
|
- width, height = map(int, output.decode().strip().split(','))
|
|
|
-
|
|
|
+ ffprobe_cmd = cls.asyncio_run_subprocess(
|
|
|
+ ["ffprobe", "-v", "error", "-select_streams", "v:0", "-show_entries", "stream=width,height", "-of",
|
|
|
+ "csv=p=0", video_path], timeout=10)
|
|
|
+ width, height = map(int, ffprobe_cmd.strip().split(','))
|
|
|
# 计算裁剪后的高度
|
|
|
new_height = int(height * 0.8)
|
|
|
|
|
|
# 构建 FFmpeg 命令,裁剪视频高度为原始高度的80%
|
|
|
- ffmpeg_cmd = [
|
|
|
+ cls.asyncio_run_subprocess(
|
|
|
+ [
|
|
|
"ffmpeg",
|
|
|
"-i", video_path,
|
|
|
"-vf", f"crop={width}:{new_height}",
|
|
@@ -119,8 +86,7 @@ class FFmpeg():
|
|
|
"-c:a", "aac",
|
|
|
"-y",
|
|
|
crop_url
|
|
|
- ]
|
|
|
- subprocess.run(ffmpeg_cmd)
|
|
|
+ ],timeout=240)
|
|
|
return crop_url
|
|
|
|
|
|
"""
|
|
@@ -132,11 +98,11 @@ class FFmpeg():
|
|
|
# 获取视频时长
|
|
|
total_duration = cls.get_video_duration(video_path)
|
|
|
if total_duration == 0:
|
|
|
- return new_video_path
|
|
|
+ return gg_duration_url
|
|
|
duration = int(total_duration) - int(gg_duration_total)
|
|
|
if int(total_duration) < int(gg_duration_total):
|
|
|
- return new_video_path
|
|
|
- ffmpeg_cmd = [
|
|
|
+ return gg_duration_url
|
|
|
+ cls.asyncio_run_subprocess([
|
|
|
"ffmpeg",
|
|
|
"-i", video_path,
|
|
|
"-c:v", "libx264",
|
|
@@ -144,8 +110,7 @@ class FFmpeg():
|
|
|
"-t", str(duration),
|
|
|
"-y",
|
|
|
gg_duration_url
|
|
|
- ]
|
|
|
- subprocess.run(ffmpeg_cmd)
|
|
|
+ ], timeout= 360)
|
|
|
return gg_duration_url
|
|
|
|
|
|
"""
|
|
@@ -163,8 +128,8 @@ class FFmpeg():
|
|
|
return jpg_url
|
|
|
time_offset = total_duration - 1 # 提取倒数第一秒的帧
|
|
|
# 获取视频最后一秒,生成.jpg
|
|
|
- subprocess.run(
|
|
|
- ['ffmpeg', '-ss', str(time_offset), '-i', video_path, '-t', str(total_duration), '-vf', 'fps=1,scale=360:640', "-y", jpg_url])
|
|
|
+ cls.asyncio_run_subprocess(
|
|
|
+ ['ffmpeg', '-ss', str(time_offset), '-i', video_path, '-t', str(total_duration), '-vf', 'fps=1,scale=360:640', "-y", jpg_url], timeout=120)
|
|
|
return jpg_url
|
|
|
|
|
|
"""
|
|
@@ -173,15 +138,13 @@ class FFmpeg():
|
|
|
@classmethod
|
|
|
def get_video_mp3(cls, video_file, video_path_url, pw_random_id):
|
|
|
pw_mp3_path = video_path_url + str(pw_random_id) +'pw_video.mp3'
|
|
|
- command = [
|
|
|
+ cls.asyncio_run_subprocess([
|
|
|
'ffmpeg',
|
|
|
'-i', video_file,
|
|
|
'-q:a', '0',
|
|
|
'-map', 'a',
|
|
|
- # '-codec:a', 'libmp3lame', # 指定 MP3 编码器
|
|
|
pw_mp3_path
|
|
|
- ]
|
|
|
- subprocess.run(command)
|
|
|
+ ], timeout=120)
|
|
|
time.sleep(1)
|
|
|
return pw_mp3_path
|
|
|
|
|
@@ -189,26 +152,21 @@ class FFmpeg():
|
|
|
@classmethod
|
|
|
def update_video_h_w(cls, video_path, file_path):
|
|
|
video_h_w_path = file_path +'video_h_w_video.mp4'
|
|
|
-
|
|
|
- ffmpeg_cmd = f"ffmpeg -i {video_path} -vf 'scale=640:ih*640/iw,pad=iw:iw*16/9:(ow-iw)/2:(oh-ih)/2' {video_h_w_path}"
|
|
|
- subprocess.run(ffmpeg_cmd, shell=True)
|
|
|
+ cls.asyncio_run_subprocess(["ffmpeg" ,"-i" ,video_path ,"-vf" ,"scale=640:ih*640/iw,pad=iw:iw*16/9:(ow-iw)/2:(oh-ih)/2" ,video_h_w_path],timeout=420)
|
|
|
return video_h_w_path
|
|
|
|
|
|
"""视频转为640像素"""
|
|
|
@classmethod
|
|
|
def video_640(cls, video_path, file_path):
|
|
|
video_url = file_path + 'pixelvideo.mp4'
|
|
|
- ffmpeg_cmd = f"ffmpeg -i {video_path} -vf 'scale=360:640' {video_url}"
|
|
|
- subprocess.run(ffmpeg_cmd, shell=True)
|
|
|
+ cls.asyncio_run_subprocess(["ffmpeg" ,"-i" ,video_path ,"-vf" ,"scale=360:640" ,video_url],timeout=420)
|
|
|
return video_url
|
|
|
|
|
|
"""视频拼接到一起"""
|
|
|
@classmethod
|
|
|
def h_b_video(cls, video_path, pw_path, file_path):
|
|
|
video_url = file_path + 'hbvideo.mp4'
|
|
|
-
|
|
|
- ffmpeg_cmd = f"ffmpeg -i {video_path} -i {pw_path} -filter_complex '[0:v]scale=360:640[v1]; [1:v]scale=360:640[v2]; [v1][0:a][v2][1:a]concat=n=2:v=1:a=1[outv][outa]' -map '[outv]' -map '[outa]' {video_url}"
|
|
|
- subprocess.run(ffmpeg_cmd, shell=True)
|
|
|
+ cls.asyncio_run_subprocess(["ffmpeg","-i", video_path, "-i", pw_path, "-filter_complex" ,"[0:v]scale=360:640[v1]; [1:v]scale=360:640[v2]; [v1][0:a][v2][1:a]concat=n=2:v=1:a=1[outv][outa]" ,"-map" ,"[outv]" ,"-map" ,"[outa]" ,video_url],timeout=500)
|
|
|
return video_url
|
|
|
|
|
|
"""横屏视频顶部增加字幕"""
|
|
@@ -229,19 +187,18 @@ class FFmpeg():
|
|
|
f.write(f"1\n{start_time} --> {end_time}\n{new_text}\n\n")
|
|
|
subtitle_cmd = f"subtitles={single_video_srt}:force_style='Fontsize=12,Fontname=wqy-zenhei,Outline=2,PrimaryColour=&H00FFFF,SecondaryColour=&H000000,Bold=1,MarginV=225'"
|
|
|
draw = f"{subtitle_cmd}"
|
|
|
- ffmpeg_cmd = [
|
|
|
+ cls.asyncio_run_subprocess([
|
|
|
"ffmpeg",
|
|
|
"-f", "concat",
|
|
|
"-safe", "0",
|
|
|
"-i", single_video_txt,
|
|
|
"-c:v", "libx264",
|
|
|
"-c:a", "aac",
|
|
|
- # '-vf', f"scale=640x360",
|
|
|
"-vf", draw,
|
|
|
"-y",
|
|
|
single_video
|
|
|
- ]
|
|
|
- subprocess.run(ffmpeg_cmd)
|
|
|
+ ],timeout=500)
|
|
|
+ # subprocess.run(ffmpeg_cmd)
|
|
|
return single_video
|
|
|
|
|
|
"""获取mp3时长"""
|
|
@@ -283,7 +240,7 @@ class FFmpeg():
|
|
|
bg_position_offset = (int(360) - 360//8) / 1.75
|
|
|
background_cmd = f"drawbox=y=(ih-{int(360)}/2-{bg_position_offset}):color=yellow@1.0:width=iw:height={int(360)}/4:t=fill"
|
|
|
|
|
|
- ffmpeg_cmd = [
|
|
|
+ cls.asyncio_run_subprocess([
|
|
|
'ffmpeg',
|
|
|
'-loop', '1',
|
|
|
'-i', jpg_path, # 输入的图片文件
|
|
@@ -296,46 +253,11 @@ class FFmpeg():
|
|
|
'-shortest', # 确保输出视频的长度与音频一致
|
|
|
'-vf', f"{background_cmd},{subtitle_cmd}", # 视频过滤器,设置分辨率和其他过滤器
|
|
|
pw_url_path # 输出的视频文件路径
|
|
|
- ]
|
|
|
- subprocess.run(ffmpeg_cmd)
|
|
|
+ ], timeout=500)
|
|
|
if os.path.exists(pw_srt_path):
|
|
|
os.remove(pw_srt_path)
|
|
|
return pw_url_path
|
|
|
|
|
|
- """
|
|
|
- 设置统一格式拼接视频
|
|
|
- """
|
|
|
- @classmethod
|
|
|
- def concatenate_videos(cls, video_list, video_path):
|
|
|
- concatenate_videos_url = video_path + 'concatenate_videos.mp4'
|
|
|
- # 拼接视频
|
|
|
- VIDEO_COUNTER = 0
|
|
|
- FF_INPUT = ""
|
|
|
- FF_SCALE = ""
|
|
|
- FF_FILTER = ""
|
|
|
- ffmpeg_cmd = ["ffmpeg"]
|
|
|
- for videos in video_list:
|
|
|
- # 添加输入文件
|
|
|
- FF_INPUT += f" -i {videos}"
|
|
|
- # 为每个视频文件统一长宽,并设置SAR(采样宽高比)
|
|
|
- FF_SCALE += f"[{VIDEO_COUNTER}:v]scale=360x640,setsar=1[v{VIDEO_COUNTER}];"
|
|
|
- # 为每个视频文件创建一个输入流,并添加到-filter_complex参数中
|
|
|
- FF_FILTER += f"[v{VIDEO_COUNTER}][{VIDEO_COUNTER}:a]"
|
|
|
- # 增加视频计数器
|
|
|
- VIDEO_COUNTER += 1
|
|
|
- ffmpeg_cmd.extend(FF_INPUT.split())
|
|
|
- ffmpeg_cmd.extend(["-filter_complex", f"{FF_SCALE}{FF_FILTER}concat=n={VIDEO_COUNTER}:v=1:a=1[v][a]",
|
|
|
- "-map", "[v]", "-map", "[a]", "-y", concatenate_videos_url])
|
|
|
- try:
|
|
|
- print(ffmpeg_cmd)
|
|
|
- # result = subprocess.run(ffmpeg_cmd)
|
|
|
- # if result.returncode != 0:
|
|
|
- # return None
|
|
|
- # else:
|
|
|
- # return concatenate_videos_url
|
|
|
- except Exception as e:
|
|
|
- logger.error(f"[+] 视频合并失败,失败信息{e}")
|
|
|
- return None
|
|
|
|
|
|
"""
|
|
|
单个视频拼接
|
|
@@ -362,7 +284,7 @@ class FFmpeg():
|
|
|
# 多线程数
|
|
|
num_threads = 5
|
|
|
# 构建 FFmpeg 命令,生成视频
|
|
|
- ffmpeg_cmd_oss = [
|
|
|
+ cls.asyncio_run_subprocess([
|
|
|
"ffmpeg",
|
|
|
"-f", "concat",
|
|
|
"-safe", "0",
|
|
@@ -375,8 +297,7 @@ class FFmpeg():
|
|
|
"-vf", subtitle_cmd,
|
|
|
"-y",
|
|
|
single_video_url
|
|
|
- ]
|
|
|
- subprocess.run(ffmpeg_cmd_oss)
|
|
|
+ ], timeout=400)
|
|
|
if os.path.exists(single_video_srt):
|
|
|
os.remove(single_video_srt)
|
|
|
return single_video_url
|
|
@@ -403,9 +324,6 @@ class FFmpeg():
|
|
|
|
|
|
|
|
|
if __name__ == '__main__':
|
|
|
- new_video_path = '/Users/tzld/Desktop/video_rewriting/path/output1.mp4'
|
|
|
- video_path_url = '/Users/tzld/Desktop/video_rewriting/path/'
|
|
|
- zm = '温馨提示:下方按钮可分享到群'
|
|
|
- FFmpeg.single_video(new_video_path, video_path_url, zm)
|
|
|
+ FFmpeg.get_w_h_size("/Users/z/Downloads/c2ce4ba6-fdb3-4e1b-bdba-dc05cb292abfvideo.mp4")
|
|
|
|
|
|
|