如何使用FFmpeg基于SMPTE时码同步多机位视频帧并输出匹配片段
如何使用FFmpeg基于SMPTE时码同步多机位视频帧并输出匹配片段
嘿,你已经搞定了最关键的第一步——成功提取出两个视频的SMPTE时码!接下来我们只需要把这些时码转换成FFmpeg能理解的时间偏移,算出两个视频的重合时间段,最后裁剪出完全匹配的片段就行。下面是完整的解决方案:
核心思路
- 将提取到的SMPTE时码(比如
00:00:53;30)转换为总秒数,方便计算时间差 - 获取每个视频的总时长,推算出各自的结束时码对应的秒数
- 找出两个视频时间线重合的区间(即较晚的起始时间到较早的结束时间)
- 用FFmpeg分别裁剪两个视频,只保留重合区间内的内容
完整bash脚本
我把所有步骤整合到了一个脚本里,你可以直接替换自己的文件名使用:
#!/bin/bash # 替换成你的两个视频文件路径 file1="A001C002_220101EB_CANON.MXF" file2="A001C002_220101US_CANON.MXF" # -------------------------- # 1. 提取SMPTE时码 # -------------------------- timecode1=$(ffmpeg -i "$file1" -hide_banner -f ffmetadata - 2>/dev/null | awk -F'=' '/timecode/{print $2}') timecode2=$(ffmpeg -i "$file2" -hide_banner -f ffmetadata - 2>/dev/null | awk -F'=' '/timecode/{print $2}') echo "视频1起始时码: $timecode1" echo "视频2起始时码: $timecode2" # -------------------------- # 2. 定义SMPTE时码转总秒数的函数 # 区分Drop-Frame(分号分隔)和Non-Drop-Frame(冒号分隔) # -------------------------- tc_to_seconds() { local tc="$1" local hours minutes seconds frames local frame_rate=29.97 # 根据你的视频帧率修改,佳能常用29.97或25 # 处理Drop-Frame时码(分号) if [[ "$tc" == *";"* ]]; then IFS=':;' read -r hours minutes seconds frames <<< "$tc" # Drop-Frame时码的转换公式:总帧数 = (hours*3600 + minutes*60 + seconds)*frame_rate + frames - 2*minutes + 2*(minutes//10) local total_frames=$(( (hours*3600 + minutes*60 + seconds)*frame_rate + frames - 2*minutes + 2*(minutes/10) )) echo "scale=6; $total_frames / $frame_rate" | bc else # Non-Drop-Frame时码(冒号) IFS=':' read -r hours minutes seconds frames <<< "$tc" echo "scale=6; ($hours*3600 + $minutes*60 + $seconds) + $frames/$frame_rate" | bc fi } # -------------------------- # 3. 获取每个视频的总时长(秒) # -------------------------- duration1=$(ffprobe -v error -show_entries format=duration -of default=noprint_wrappers=1:nokey=1 "$file1") duration2=$(ffprobe -v error -show_entries format=duration -of default=noprint_wrappers=1:nokey=1 "$file2") echo "视频1总时长: $duration1 秒" echo "视频2总时长: $duration2 秒" # -------------------------- # 4. 计算每个视频的起始/结束时间(秒) # -------------------------- start1=$(tc_to_seconds "$timecode1") start2=$(tc_to_seconds "$timecode2") end1=$(echo "scale=6; $start1 + $duration1" | bc) end2=$(echo "scale=6; $start2 + $duration2" | bc) # -------------------------- # 5. 计算重合时间区间 # -------------------------- # 重合起始点:取两个起始时间的最大值 overlap_start=$(echo "if($start1 > $start2) $start1 else $start2" | bc) # 重合结束点:取两个结束时间的最小值 overlap_end=$(echo "if($end1 < $end2) $end1 else $end2" | bc) # 检查是否有重合区间 if (( $(echo "$overlap_start >= $overlap_end" | bc -l) )); then echo "错误:两个视频没有时间重合的部分!" exit 1 fi echo "重合区间: $overlap_start 秒 到 $overlap_end 秒" # -------------------------- # 6. 计算每个视频需要裁剪的起始点和时长 # -------------------------- trim_start1=$(echo "scale=6; $overlap_start - $start1" | bc) trim_duration1=$(echo "scale=6; $overlap_end - $overlap_start" | bc) trim_start2=$(echo "scale=6; $overlap_start - $start2" | bc) trim_duration2=$(echo "scale=6; $overlap_end - $overlap_start" | bc) # -------------------------- # 7. 用FFmpeg裁剪视频 # -------------------------- echo "开始裁剪视频1..." ffmpeg -i "$file1" -ss "$trim_start1" -t "$trim_duration1" -c:v copy -c:a copy "synced_$file1" -y echo "开始裁剪视频2..." ffmpeg -i "$file2" -ss "$trim_start2" -t "$trim_duration2" -c:v copy -c:a copy "synced_$file2" -y echo "同步裁剪完成!输出文件为 synced_$file1 和 synced_$file2"
关键注意事项
- 帧率修改:脚本里默认帧率是29.97(佳能常用的Drop-Frame时码帧率),如果你的视频是25fps或其他帧率,记得修改
tc_to_seconds函数里的frame_rate变量。 - 无损裁剪:脚本里用了
-c:v copy -c:a copy参数,这是直接复制音视频流,不会重新编码,速度很快且无质量损失。如果你的视频需要重新编码(比如裁剪点不在关键帧上),可以去掉这两个参数,FFmpeg会自动重新编码。 - 时码格式:如果你的时码是Non-Drop-Frame(用冒号分隔帧,比如
00:00:53:30),脚本也能自动识别处理。
备注:内容来源于stack exchange,提问作者user9114146
相关产品推荐
相关产品推荐

