Python 是否可以使用FFmpeg来剪切;“随机”;从一个视频文件夹中删除部分并将其压缩成一个视频?
我意识到这听起来像是一个简单的问题,而且以前已经回答过了。然而,我似乎找不到一个脚本可以读取不同长度的视频文件夹,从每个视频中复制一个随机片段,并将其浓缩到单个视频中 例如: 我有一个文件夹,里面有150个视频,标签是Fashion-Setlist-01.mp4、Fashion-Setlist-02.mp4等。Python 是否可以使用FFmpeg来剪切;“随机”;从一个视频文件夹中删除部分并将其压缩成一个视频?,python,linux,bash,video,ffmpeg,Python,Linux,Bash,Video,Ffmpeg,我意识到这听起来像是一个简单的问题,而且以前已经回答过了。然而,我似乎找不到一个脚本可以读取不同长度的视频文件夹,从每个视频中复制一个随机片段,并将其浓缩到单个视频中 例如: 我有一个文件夹,里面有150个视频,标签是Fashion-Setlist-01.mp4、Fashion-Setlist-02.mp4等。 每个都超过1小时。我想从每个视频中随机抽取10秒的片段,然后随机将它们添加到一起,生成一个视频。这可能看起来很容易,只有几个视频,但计划是阅读潜在的100个视频。也可以从每个视频中提取多
每个都超过1小时。我想从每个视频中随机抽取10秒的片段,然后随机将它们添加到一起,生成一个视频。这可能看起来很容易,只有几个视频,但计划是阅读潜在的100个视频。也可以从每个视频中提取多个部分。如果视频需要更长,我想我们可以为更多的片段运行两次脚本。编辑:使用ffmpeg剪切视频需要在关键帧上完成。我对这段代码进行了大量编辑,首先找到关键帧,然后对其进行剪切。它对我有用 因此,要在
bash
中执行此操作,假设存在一些程序randtime.py
,该程序以“H:MM:SS”格式输出随机开始时间,以及一些其他程序在给定时间附近找到视频关键帧,下面是一个快速破解版本:
#! /usr/bin/env bash
CUTLIST=cut_list.txt
RANDTIME=~/Bin/randtime.py
KEYFRAMER=~/Bin/find_next_key_frame.py
count=0
echo "" > "$CUTLIST"
for file in *.mp4
do
count=$(( $count + 1 ));
outfile="cut_$count.mp4"
start_time=`python "$RANDTIME"`
# Find the next keyframe, at or after the random time
start_keyframe_time=`$KEYFRAMER "$file" "$start_time"`
if [ $? -eq 0 ]; then
echo "Nearest keyframe to \"$start_time\" is \"$start_keyframe_time\""
echo "ffmpeg -loglevel quiet -y -i \"$file\" -ss $start_keyframe_time -t 00:00:10 \"$outfile\""
ffmpeg -loglevel quiet -y -i "$file" -ss $start_keyframe_time -t 00:00:10 "$outfile"
if [ $? -ne 0 ]; then
echo "ffmpeg returned an error on [$file], aborting"
# exit 1
fi
echo "file '$outfile'" >> "$CUTLIST"
else
echo "ffprobe found no suitable key-frame near \"$start_time\""
fi
done
echo "Concatenating ... "
cat "$CUTLIST"
ffmpeg -f concat -i cut_list.txt -c copy all_cuts.mp4
if [ -f "$CUTLIST" ]; then
rm "$CUTLIST"
fi
以及随机时间,在python中:
#! /usr/bin/env python3
import random
#TODO: ensure we're at least 8 seconds before 1 hour
hrs = 0 # random.randint(0,1)
mns = random.randint(0,59)
scs = random.randint(0,59)
print("%d:%02d:%02d" % (hrs,mns,scs))
在python
中再次找到关键帧,或者正好在给定的时间之后
#! /usr/bin/env python3
import sys
import subprocess
import os
import os.path
FFPROBE='/usr/bin/ffprobe'
EXE=sys.argv[0].replace('\\','/').split('/')[-1]
if (not os.path.isfile(FFPROBE)):
sys.stderr.write("%s: The \"ffprobe\" part of FFMPEG seems to be missing\n" % (EXE))
sys.exit(1)
if (len(sys.argv) == 1 or (len(sys.argv)==2 and sys.argv[1] in ('--help', '-h', '-?', '/?'))):
sys.stderr.write("%s: Give video filename and time as arguments\n" % (EXE))
sys.stderr.write("%s: e.g.: video_file.mp4 0:25:14 \n" % (EXE))
sys.stderr.write("%s: Outputs the next keyframe at or after the given time\n" % (EXE))
sys.exit(1)
VIDEO_FILE = sys.argv[1]
FRAME_TIME = sys.argv[2].strip()
if (not os.path.isfile(VIDEO_FILE)):
sys.stderr.write("%s: The vdeo file \"%s\" seems to be missing\n" % (EXE, VIDEO_FILE))
sys.exit(1)
### Launch FFMPEG's ffprobe to identify the frames
command = "ffprobe -show_frames -pretty \"%s\"" % VIDEO_FILE
frame_list = subprocess.getoutput(command)
### The list of frames is a huge bunch of lines like:
### [FRAME]
### media_type=video
### key_frame=0
### best_effort_timestamp=153088
### best_effort_timestamp_time=0:00:09.966667
### pkt_duration_time=0:00:00.033333
### height=360
### ...
### [/FRAME]
### Parse the stats about each frame, keeping only the Video Keyframes
key_frames = []
for frame in frame_list.split("[FRAME]"):
# split the frame lines up into separate "x=y" pairs
frame_dict = {}
frame_vars = frame.replace('\r', '\n').replace("\n\n", '\n').split('\n')
for frame_pair in frame_vars:
if (frame_pair.find('=') != -1):
try:
var,value = frame_pair.split('=', 1)
frame_dict[var.strip()] = value.strip()
except:
sys.stderr.write("%s: Warning: Unable to parse [%s]\n" % (EXE, frame_pair))
# Do we want to keep this frame?
# we want video frames, that are key frames
if ("media_type", "key_frame" in frame_dict and frame_dict["media_type"] == "video" and frame_dict["key_frame"] == "1"):
key_frames.append(frame_dict)
### Throw away duplicates, ans sort (why are there duplicates?)
key_frame_list = set()
for frame_dict in key_frames:
#print(str(frame_dict))
if ("best_effort_timestamp_time" in frame_dict):
key_frame_list.add(frame_dict["best_effort_timestamp_time"])
key_frame_list = list(key_frame_list)
key_frame_list.sort()
sys.stderr.write("Keyframes found: %u, from %s -> %s\n" % (len(key_frame_list), key_frame_list[0], key_frame_list[-1]))
### Find the same, or next-larger keyframe
found = False
for frame_time in key_frame_list:
#sys.stderr.write("COMPARE %s > %s\n" % (frame_time , FRAME_TIME))
if (frame_time > FRAME_TIME):
print(frame_time)
found = True
break # THERE CAN BE ONLY ONE!
### Failed? Print something possibly useful
if (found == False):
sys.stderr.write("%s: Warning: No keyframe found\n" % (EXE))
print("0:00:00")
sys.exit(-1)
else:
sys.exit(0) # All'swell
是最合适的工具(它使用ffmpeg作为后端)。连接视频在moviepy中很简单:
import moviepy.editor
import os
import random
import fnmatch
directory = '/directory/to/videos/'
xdim = 854
ydim = 480
ext = "*mp4"
length = 10
outputs=[]
# compile list of videos
inputs = [os.path.join(directory,f) for f in os.listdir(directory) if os.path.isfile(os.path.join(directory, f)) and fnmatch.fnmatch(f, ext)]
for i in inputs:
# import to moviepy
clip = moviepy.editor.VideoFileClip(i).resize( (xdim, ydim) )
# select a random time point
start = round(random.uniform(0,clip.duration-length), 2)
# cut a subclip
out_clip = clip.subclip(start,start+length)
outputs.append(out_clip)
# combine clips from different videos
collage = moviepy.editor.concatenate_videoclips(outputs)
collage.write_videofile('out.mp4')
我认为这类问题通常可以通过从头编写管道来解决。我所说的管道(pipeline)不是指bash管道,而是指一系列脚本,每个脚本执行一个中间步骤,并将输出存储在一个临时目录中。序列中的后续脚本将处理它们找到的任何中间/临时文件。这有助于将其分解为可管理的步骤。例如,首先创建一个脚本,该脚本只执行一项任务,即从文件名参数中随机获取视频片段并将其存储在临时目录中。一个makefile可以协调这个过程。这不会开始剪切视频关键帧,我认为这会提供更好的输出。谢谢你的回复!不过我遇到了这个。“输入#0,concat,从'cut#list.txt':持续时间:N/A,比特率:N/A输出#0,mp4,到'all#cuts.mp4':输出文件#0不包含任何流””编辑:似乎只有4个文件中包含视频。剩下的大约是464字节。@EmmyStrand-看起来ffmpeg只能在关键帧(或类似的帧)上剪切。也许有一个选项可以告诉ffmpeg在给定时间后的下一个关键帧开始剪切,但如果有,我不知道是否有,所以我强行剪切。运行
find_next_key_frame.py
需要一段时间。这是次优。谢谢!!我认为它有效。调整代码使其每mp4随机剪切2次有多困难?或者,两次运行脚本而不是第一次运行concat会更容易吗?@EmmyStrand-如果在输入文件集中指定相同的文件两次,它将从每个文件中进行两次剪切。但这是随机的,所以它们可能重叠;)谢谢@hb_2;!我会试试看。编辑***这很好!!这正是我所需要的。但有一个问题,它从每个视频中提取的子剪辑是否超过1个?如果没有,那也没什么大不了的,我只是好奇。很乐意帮忙。上面的代码从每个视频中提取一个剪辑,但如果需要另一个,可以在for循环中复制这些行。或者,一个更具动态性的解决方案是将它放入一个while循环中,以您所寻找的最小总长度。