You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于FFmpeg实现TS文件间插入指定时长图片拼接及代码修复

解决FFmpeg生成对应时长图片TS片段的循环问题

看起来你现在踩了两个关键坑:循环逻辑错误导致时长重复复用,还有FFmpeg不支持你使用的帧格式时长。我帮你一步步梳理解决:

1. 先搞定循环逻辑问题

你之前的嵌套循环加break的写法完全搞反了时长和文件名的对应关系——外层循环遍历每个时长,内层循环只取第一个文件名就跳出,结果要么所有生成文件复用同一个文件名,要么(像你遇到的)错误地把第一个时长重复用在所有文件上。

正确的做法是用zip()把时长列表和文件名列表一一配对,这样每个时长对应一个文件名前缀:

import subprocess
import sys
import os

# 你的原始参数
new_ts3 = ['00:00:06:17', '00:00:00:16', '00:00:01:05', '00:00:00:31', '00:00:01:01']
filename = ['file1', 'file2', 'file3', 'file4', 'file5', 'file6']

# 遍历配对的时长和文件名前缀(注意filename[:-1]要和new_ts3长度一致)
for duration_str, file_prefix in zip(new_ts3, filename[:-1]):
    # 生成输出的图片TS文件名
    output_ts = f"{os.path.splitext(sys.argv[1])[0]}{file_prefix}_.ts"
    # 先留个坑:这里要处理时长格式,下面说
    # 构造FFmpeg命令
    cmd = [
        'ffmpeg', '-loop', '1', '-i', sys.argv[2],
        '-c:v', 'libx264', '-profile:v', 'high',
        '-t', duration_str,  # 这里先放原始格式,后面要改
        '-pix_fmt', 'yuvj420p',
        '-vf', 'scale=1280:720', '-f', 'mpegts',
        output_ts
    ]
    # 执行命令,同时捕获错误信息方便排查
    p1 = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
    stdout, stderr = p1.communicate()
    if p1.returncode != 0:
        print(f"生成{output_ts}失败: {stderr.decode('utf-8')}")

2. 修复FFmpeg时长格式不兼容问题

你用的时长格式是HH:MM:SS:FF(最后两位是帧数),但FFmpeg的-t参数默认只认HH:MM:SS.sss(毫秒)或者秒数。所以需要把帧格式转换为FFmpeg支持的格式,前提是你要知道视频的帧率(比如PAL是25fps,NTSC是30fps)。

加一个转换函数:

def frame_time_to_ffmpeg_format(time_str, fps=25):
    """把HH:MM:SS:FF格式转换为FFmpeg支持的秒数字符串"""
    h, m, s, frame = map(int, time_str.split(':'))
    total_seconds = h * 3600 + m * 60 + s + frame / fps
    # 返回保留两位小数的秒数,比如"6.68"
    return f"{total_seconds:.2f}"

然后把上面代码里的-t参数替换成转换后的时长:

# 替换这一行
duration = frame_time_to_ffmpeg_format(duration_str, fps=25)  # 根据你的实际帧率调整
cmd = [
    # ... 其他参数不变
    '-t', duration,
    # ... 其他参数不变
]

3. 最后一步:拼接所有TS文件

生成完所有图片片段后,你需要按video1 → img1 → video2 → img2 → ...的顺序拼接。最稳妥的方式是生成一个文件列表txt,再用FFmpeg的concat滤镜拼接:

# 生成拼接用的文件列表
with open('concat_list.txt', 'w', encoding='utf-8') as f:
    # 遍历原始TS文件和对应图片片段
    for ts_name, img_ts_name in zip(filename[:-1], 
                                   [f"{os.path.splitext(sys.argv[1])[0]}{m}_.ts" for m in filename[:-1]]):
        # 假设你的原始TS文件后缀是.ts,根据实际情况调整
        f.write(f"file '{ts_name}.ts'\n")
        f.write(f"file '{img_ts_name}'\n")
    # 如果filename最后一个元素是最后一个TS文件,记得加上它
    # f.write(f"file '{filename[-1]}.ts'\n")

# 执行拼接命令
concat_cmd = [
    'ffmpeg', '-f', 'concat', '-safe', '0',
    '-i', 'concat_list.txt', '-c', 'copy',
    'final_output.ts'
]
p_concat = subprocess.Popen(concat_cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
concat_out, concat_err = p_concat.communicate()
if p_concat.returncode != 0:
    print(f"拼接失败: {concat_err.decode('utf-8')}")

为什么你之前的代码会生成全是第一个时长的文件?

核心原因是嵌套循环里的break——每次外层循环拿到一个新的时长i,内层循环刚处理第一个文件名就跳出,结果相当于所有文件名都重复用了第一个时长。另外,之前的时长格式错误也可能导致FFmpeg无法解析,直接用了默认时长或者第一个有效时长,这也是你看到所有文件时长一致的可能原因。

内容的提问来源于stack exchange,提问作者srt243

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.14 09:09:37