基于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
相关产品推荐
相关产品推荐

