fix: 修复 ffmpeg 合并视频 尺寸不对有黑边的bug
This commit is contained in:
parent
05a4834ec5
commit
e15a9d947b
|
|
@ -514,6 +514,26 @@ class VideoUtils:
|
||||||
total_videos = len(media_paths)
|
total_videos = len(media_paths)
|
||||||
if total_videos == 0:
|
if total_videos == 0:
|
||||||
raise ValueError("没有可以合并的视频源")
|
raise ValueError("没有可以合并的视频源")
|
||||||
|
|
||||||
|
# 自动检测所有视频的宽高和音频流,找出最大值
|
||||||
|
max_width = 0
|
||||||
|
max_height = 0
|
||||||
|
has_audio = []
|
||||||
|
for media_path in media_paths:
|
||||||
|
width, height = await VideoUtils.ffprobe_video_size_async(media_path)
|
||||||
|
max_width = max(max_width, width)
|
||||||
|
max_height = max(max_height, height)
|
||||||
|
# 检查是否有音频流
|
||||||
|
metadata = VideoUtils.ffprobe_media_metadata(media_path)
|
||||||
|
audio_exists = any(stream.codec_type == "audio" for stream in metadata.streams)
|
||||||
|
has_audio.append(audio_exists)
|
||||||
|
logger.info(f"{media_path} - 音频流: {'存在' if audio_exists else '不存在'}")
|
||||||
|
|
||||||
|
# 使用检测到的最大宽高作为目标尺寸
|
||||||
|
target_width = max_width
|
||||||
|
target_height = max_height
|
||||||
|
logger.info(f"检测到最大分辨率: {target_width}x{target_height}")
|
||||||
|
|
||||||
if not output_path:
|
if not output_path:
|
||||||
output_path = FileUtils.file_path_extend(media_paths[0], "concat")
|
output_path = FileUtils.file_path_extend(media_paths[0], "concat")
|
||||||
os.makedirs(os.path.dirname(output_path), exist_ok=True)
|
os.makedirs(os.path.dirname(output_path), exist_ok=True)
|
||||||
|
|
@ -523,18 +543,22 @@ class VideoUtils:
|
||||||
ffmpeg_cmd.input(input_path)
|
ffmpeg_cmd.input(input_path)
|
||||||
# 2. 统一所有视频的格式、分辨率和帧率
|
# 2. 统一所有视频的格式、分辨率和帧率
|
||||||
for i in range(total_videos):
|
for i in range(total_videos):
|
||||||
filter_complex.extend(
|
# 视频流处理
|
||||||
[
|
filter_complex.append(
|
||||||
# 先缩放到统一分辨率,然后设置帧率和格式
|
f"[{i}:v]scale={target_width}:{target_height}:force_original_aspect_ratio=increase,"
|
||||||
f"[{i}:v]scale={target_width}:{target_height}:force_original_aspect_ratio=decrease,"
|
f"crop={target_width}:{target_height},"
|
||||||
f"pad={target_width}:{target_height}:(ow-iw)/2:(oh-ih)/2,"
|
f"setsar=1:1,"
|
||||||
f"setsar=1:1," # 新增强制设置SAR
|
f"fps=30,format=yuv420p[v{i}]"
|
||||||
f"fps=30,format=yuv420p[v{i}]",
|
|
||||||
# 修改音频过滤器,确保输出为AAC兼容格式
|
|
||||||
# f"[{i}:a]aformat=sample_fmts=fltp:sample_rates=44100:channel_layouts=stereo[a{i}]",
|
|
||||||
f"[{i}:a]aformat=sample_fmts=s16:sample_rates=44100:channel_layouts=stereo[a{i}]",
|
|
||||||
]
|
|
||||||
)
|
)
|
||||||
|
# 音频流处理:如果有音频则使用原音频,否则生成静默音频
|
||||||
|
if has_audio[i]:
|
||||||
|
filter_complex.append(
|
||||||
|
f"[{i}:a]aformat=sample_fmts=s16:sample_rates=44100:channel_layouts=stereo[a{i}]"
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
filter_complex.append(
|
||||||
|
f"anullsrc=channel_layout=stereo:sample_rate=44100[a{i}]"
|
||||||
|
)
|
||||||
# 3. 准备处理后的视频流和音频流的连接字符串
|
# 3. 准备处理后的视频流和音频流的连接字符串
|
||||||
video_streams = "".join(f"[v{i}]" for i in range(total_videos))
|
video_streams = "".join(f"[v{i}]" for i in range(total_videos))
|
||||||
audio_streams = "".join(f"[a{i}]" for i in range(total_videos))
|
audio_streams = "".join(f"[a{i}]" for i in range(total_videos))
|
||||||
|
|
@ -930,6 +954,20 @@ class VideoUtils:
|
||||||
total_videos = len(media_paths)
|
total_videos = len(media_paths)
|
||||||
if total_videos == 0:
|
if total_videos == 0:
|
||||||
raise ValueError("没有可以合并的视频源")
|
raise ValueError("没有可以合并的视频源")
|
||||||
|
|
||||||
|
# 自动检测所有视频的宽高,找出最大值
|
||||||
|
max_width = 0
|
||||||
|
max_height = 0
|
||||||
|
for media_path in media_paths:
|
||||||
|
width, height = await VideoUtils.ffprobe_video_size_async(media_path)
|
||||||
|
max_width = max(max_width, width)
|
||||||
|
max_height = max(max_height, height)
|
||||||
|
|
||||||
|
# 使用检测到的最大宽高作为目标尺寸
|
||||||
|
target_width = max_width
|
||||||
|
target_height = max_height
|
||||||
|
logger.info(f"检测到最大分辨率: {target_width}x{target_height}")
|
||||||
|
|
||||||
if not output_path:
|
if not output_path:
|
||||||
output_path = FileUtils.file_path_extend(media_paths[0], "concat_video_only")
|
output_path = FileUtils.file_path_extend(media_paths[0], "concat_video_only")
|
||||||
os.makedirs(os.path.dirname(output_path), exist_ok=True)
|
os.makedirs(os.path.dirname(output_path), exist_ok=True)
|
||||||
|
|
@ -940,10 +978,10 @@ class VideoUtils:
|
||||||
# 2. 统一所有视频的格式、分辨率和帧率(仅处理视频流)
|
# 2. 统一所有视频的格式、分辨率和帧率(仅处理视频流)
|
||||||
for i in range(total_videos):
|
for i in range(total_videos):
|
||||||
filter_complex.append(
|
filter_complex.append(
|
||||||
# 先缩放到统一分辨率,然后设置帧率和格式
|
# 放大到目标尺寸并裁剪,保持不变形
|
||||||
f"[{i}:v]scale={target_width}:{target_height}:force_original_aspect_ratio=decrease,"
|
f"[{i}:v]scale={target_width}:{target_height}:force_original_aspect_ratio=increase,"
|
||||||
f"pad={target_width}:{target_height}:(ow-iw)/2:(oh-ih)/2,"
|
f"crop={target_width}:{target_height},"
|
||||||
f"setsar=1:1," # 强制设置SAR
|
f"setsar=1:1,"
|
||||||
f"fps=30,format=yuv420p[v{i}]"
|
f"fps=30,format=yuv420p[v{i}]"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue