fix: 修复 ffmpeg 合并视频 尺寸不对有黑边的bug

This commit is contained in:
imeepos 2026-02-09 15:49:50 +08:00
parent 05a4834ec5
commit e15a9d947b
1 changed files with 53 additions and 15 deletions

View File

@ -514,6 +514,26 @@ class VideoUtils:
total_videos = len(media_paths)
if total_videos == 0:
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:
output_path = FileUtils.file_path_extend(media_paths[0], "concat")
os.makedirs(os.path.dirname(output_path), exist_ok=True)
@ -523,17 +543,21 @@ class VideoUtils:
ffmpeg_cmd.input(input_path)
# 2. 统一所有视频的格式、分辨率和帧率
for i in range(total_videos):
filter_complex.extend(
[
# 先缩放到统一分辨率,然后设置帧率和格式
f"[{i}:v]scale={target_width}:{target_height}:force_original_aspect_ratio=decrease,"
f"pad={target_width}:{target_height}:(ow-iw)/2:(oh-ih)/2,"
f"setsar=1:1," # 新增强制设置SAR
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}]",
]
# 视频流处理
filter_complex.append(
f"[{i}:v]scale={target_width}:{target_height}:force_original_aspect_ratio=increase,"
f"crop={target_width}:{target_height},"
f"setsar=1:1,"
f"fps=30,format=yuv420p[v{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. 准备处理后的视频流和音频流的连接字符串
video_streams = "".join(f"[v{i}]" for i in range(total_videos))
@ -930,6 +954,20 @@ class VideoUtils:
total_videos = len(media_paths)
if total_videos == 0:
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:
output_path = FileUtils.file_path_extend(media_paths[0], "concat_video_only")
os.makedirs(os.path.dirname(output_path), exist_ok=True)
@ -940,10 +978,10 @@ class VideoUtils:
# 2. 统一所有视频的格式、分辨率和帧率(仅处理视频流)
for i in range(total_videos):
filter_complex.append(
# 先缩放到统一分辨率,然后设置帧率和格式
f"[{i}:v]scale={target_width}:{target_height}:force_original_aspect_ratio=decrease,"
f"pad={target_width}:{target_height}:(ow-iw)/2:(oh-ih)/2,"
f"setsar=1:1," # 强制设置SAR
# 放大到目标尺寸并裁剪,保持不变形
f"[{i}:v]scale={target_width}:{target_height}:force_original_aspect_ratio=increase,"
f"crop={target_width}:{target_height},"
f"setsar=1:1,"
f"fps=30,format=yuv420p[v{i}]"
)