ffmpeg可以烧录时间码吗?

23

我需要在视频中加入时间码,想知道ffmpeg是否有这种功能?


所以我假设你想将“当前视频时间”烧录到视频中?或者你想使用libav *在视频中添加自己的时间码文本? - rogerdpack
7个回答

36

FFMPEG的drawtext过滤器适用于我,你可以这样指定起始时间码和其格式:

-vf drawtext="fontsize=15:fontfile=/Library/Fonts/DroidSansMono.ttf:\
timecode='00\:00\:00\:00':rate=25:text='TCR\:':fontsize=72:fontcolor='white':\
boxcolor=0x000000AA:box=1:x=860-text_w/2:y=960"

你需要在时间码格式中指定hh:mm:ss[:;,]ff的形式。请注意,必须转义时间码格式字符串中的冒号,并指定时间码速率(这里是25fps)。您还可以指定其他文本-此处为“TCR:”

您可以使用ffprobe和一些shell fu来获取帧率:

frame_rate=$(ffprobe -i "movie.mov" -show_streams 2>&1|grep fps|sed "s/.*, \([0-9.]*\) fps,.*/\1/")

因此,您可以轻松地将所有内容插入批处理脚本中进行处理,例如:

for i in *.mov
frame_rate=$(ffprobe -i "$i" -show_streams 2>&1|grep fps|sed "s/.*, \([0-9.]*\) fps,.*/\1/")
clipname=${(basename "$i")/\.*/}
ffmpeg -i "$i" -vcodec whatever -acodec whatever \
-vf drawtext="fontsize=15:fontfile=/Library/Fonts/DroidSansMono.ttf:\
timecode='00\:00\:00\:00':rate=$frame_rate:text='$clipname' TCR:':\
fontsize=72:fontcolor='white':boxcolor=0x000000AA:\
box=1:x=860-text_w/2:y=960" "${i/.mov/_tc.mov}"
done

这将在1920x1080的帧的底部中心以半透明框的形式添加剪辑名称和滚动时间编码。

编辑 由于我现在使用Windows Powershell环境,因此我使用以下内容:

ls -R -File -filter *.M*|%{
ffmpeg -n -i $_.fullname -vf drawtext="fontsize=72:x=12:y=12:`
timecode='00\:00\:00\:00':rate=25:fontcolor='white':`
boxcolor=0x000000AA:box=1" `
("c:\path\to\destination\{0}" -F ($_.name -replace 'M[OPT][V4S]', 'mp4'))}

这个命令会根据含有 .MOV、.MP4 和 .MTS 文件的文件夹生成 mp4 文件(使用 -filter 命令查找文件名中包含 *.M* 的文件,如果你要处理 .AVI 文件则需要更改)。它的设置比较简单,只使用 libx264 作为输出编解码器,并且不指定字体等其他参数。在这种情况下,时间码会被烧录在帧的左上角。


1
好主意,不幸的是它不能处理像 12.34 fps 这样的分数。 - Lekensteyn
实际上它似乎只适用于帧率标准,如25,必须转码,即使这样,对于某些原因,时间对我来说也是错误的。 - ntg
批处理脚本中有一个错别字,会强制使用25fps,现在已经修复了。看看效果如何。 - stib
1
另外,@ntg,你需要进行转码。如果不进行转码,就无法更改视频光栅的内容。 - stib
我不确定如何使用ffprobe实现这一点,但如果您将“rate”参数指定为有理数,则可以使用它,因此如果您说“24000/1001”,那对我来说是有效的。 - iluvcapra

21
drawtext过滤器是在@stib的答案中提到的插入时间的关键。然而,使用timecode选项不能匹配实际时间。如果你将r(timecode_rate)参数设置错误,则时间不会与播放时间匹配。
还有其他选项,例如text ='%{prt}'选项允许您以微秒精度显示经过的时间。命令:
ffmpeg -i video.mp4 -vf "drawtext=text='%{prt}'" output.mp4

为了获得一个时钟,我不得不使用已弃用的strftime选项。这个选项有一个未记录在文档中的basetime选项,可以用来设置微秒级别的起始时间。以下是一个示例,我在2013年12月1日中午12点设置了起始时间($(...)部分是由shell执行的扩展),并仅显示时间(请参见strftime手册以获取可能的格式):

ffmpeg -i video.mp4 -vf "drawtext=expansion=strftime: \
    basetime=$(date +%s -d'2013-12-01 12:00:00')000000: \
    text='%H\\:%S\\:%S'" output.mp4

\\:用于转义:,否则会被解释为选项分隔符。

另一个例子:一个命令用于在黑盒子内插入日期+时间,距离左上角一些像素,并在边缘添加"一些填充"(实际上是两个空格和换行符):

newline=$'\r'
ffmpeg -i video.mp4 -vf "drawtext=x=8:y=8:box=1:fontcolor=white:boxcolor=black: \
    expansion=strftime:basetime=$(date +%s -d'2013-12-01 12:00:00')000000: \
    text='$newline %Y-%m-%d %H\\:%M\\:%S $newline'" output.mp4

获取当前时间戳的微秒示例:

newline=$'\r'
ffmpeg -i video.mp4 -vf "drawtext=expansion=strftime: \
    basetime=$(date +%s -d'2013-12-01 12:00:00')000000: \
    text='$newline %H\\:%M\\:%S $newline':fontcolor=white:box=1:boxcolor=black, \
    drawtext=text='$newline %{pts} $newline': \
    y=2*th/3:box=1:fontcolor=white:boxcolor=black:" output.mp4

这个方法利用了文本实际上是三行长的事实,并且两个文本都有一个换行符(回车,^M)前缀和后缀。(没有这个换行符,空格将被剥离)

其他提示:

  • -vf-filter:v 是等效的。
  • 你不能多次指定滤镜,例如 -vf drawtext=text=1 -vf drawtext=text=2 只会绘制第二个文本。可以像我上面展示的那样用逗号来组合滤镜。

“localtime”选项在这里有帮助吗?http://ffmpeg.org/pipermail/ffmpeg-user/2014-July/022355.html - rogerdpack
本地时间并不能帮助解决问题,因为它基于本地时间而不是固定时刻。我尝试的命令是:ffplay -i video.mp4 -vf drawtext='expansion=normal:text=%{localtime\\:%a %b %d %Y}' - Lekensteyn
这个可以工作的newline=$'\r' ffmpeg -i video.mp4 -vf "drawtext=expansion=strftime:
basetime=$(date +%s -d'2013-12-01 12:00:00')000000:
text='$newline %H\:%M\:%S $newline':fontcolor=white:box=1:boxcolor=black,
drawtext=text='$newline %{pts} $newline':
y=2*th/3:box=1:fontcolor=white:boxcolor=black:" output.mp4
- Rick2047

8

简短回答,不行。

详细回答是,可以通过使用另一个库来创建带有渲染时间代码的帧,并用透明填充剩余部分,然后使用FFmpeg将这些帧叠加在现有视频上。我头脑中没有具体方法,但我相信如果你有创意,就能想出解决方法。

编辑:我一直在解决这个问题,因为这对我来说是一个有趣的问题/项目。我已经写了一个Perl脚本,它可以为任何一个FFmpeg可以读取元数据的视频文件生成嵌入时间代码的.srt文件。它使用Video::FFmpeg库读取持续时间,并将字幕文件保存为${video}.srt。如果您在~/.mplayer/config中插入以下行,则可以自动在Mplayer中呈现:

# select subtitle files automatically in the current directory, all files
# matching the basename of the current playing file
sub-fuzziness=1

我正在努力研究如何在视频上定位和叠加渲染的字幕,并在相同格式下重新编码。如果有更多信息,我会更新此帖子。


这是一个有趣的方法。有没有可能会做到这个的库呢?如果没有,我可以去找一下。 - spinon
有一件事我应该补充,因为你显然在这里做了一些很棒的工作。那就是我最终想要做的是创建一个带有时间码的新视频。所以不仅仅是在播放器中播放它们,而是创建一个新的视频,以便可以在任何播放器中播放。由于你已经付出了这么多努力,我将为这个问题添加一个赏金。 - spinon
2
显然,现在可以使用视频过滤器实现。请参见https://gist.github.com/reidransom/2630650 - Neil Slater

2

这是我的解决方案,我相信它是正确的,因为它既避免了手动设置速率,又允许您格式化输出。

ffmpeg -i test.mp4 -vf \
    "drawtext=fontfile=arialbd.ttf:text='%{pts\:gmtime\:0\:%H\\\:%M\\\:%S}'" test.avi

这将生成一个HH:MM:SS格式的时间戳;您可以使用strftime更改它为任何您想要的格式。 看起来似乎会使用gmtime插入时间戳,但实际上并不是这样。它实际上将视频当前时间(以秒为单位)馈送给gmtime,产生一个日期为1970年1月1日,时间为午夜后多少秒的日期和时间。因此,您只需丢弃日期部分并使用时间部分即可。 请注意pts函数中的三重转义冒号,如果像上面那样输入,则必须这样做。此外,您可以看到我复制了Arial Bold字体文件并将其放入了工作目录中,以简化操作。

1
在最近的版本中,您可以使用drawtext过滤器(其中示例中的“t”是帧的时间戳)来插入文本。它也适用于当前系统时间的srtftime。

文档中存在一个错误,没有 t 选项。它被命名为 basetime(http://git.videolan.org/?p=ffmpeg.git;a=blob;f=libavfilter/vf_drawtext.c;h=dde3b0a3202d45b82326f1f523673d52eff6236b;hb=HEAD#l190)。无论如何,我无法使其正常工作。 - Lekensteyn
是的,显然它已被弃用并删除。还有一些可能有用的东西:您可以使用任何“eval”支持的内容,并且对于strftime的内容,可以使用“frame_num”和“localtime”。 - rogerdpack
请查看我对这个问题的回答,昨天我发现了一些其他获取时间的方法。t似乎是一个文档错误,应该是basetime(仅适用于expansion=strftime)。 - Lekensteyn
实际上t在FFMPEG 4.1中,您可以使用ffmpeg -i input.mp4 -vf drawtext="fontsize=60:fontcolor=yellow:text='%{e\:t*1000}':x=(w-text_w):y=(h-text_h)" output.mp4 - bk138

1
我找到的最简单的解决方案是,在文件被捕获时显示时钟,而不是它的持续时间,并且它基于这篇文章,谢谢!
D:\Temp\2>ffmpeg.exe -i test.avi -vf "drawtext=fontfile=C\\:/Windows/Fonts/arial.ttf:timecode='00\:20\:10\:00':rate=25:text='TCR\:':fontsize=46:fontcolor=white:x=500:y=50: box=1: boxcolor=0x00000000@1"  -f mp4 textts.mp4

时间码非常简单 - 输入起始时间,然后计数就会正常运行!以下示例适用于Windows。


这个有效,太棒了,顺便问一下,在这里TCR的意思是什么? - http8086

-1

FFMPEG可以完成大部分的工作,但它不会全部打包好。使用FFMPEG,你可以让它按顺序解码所有帧并给出“演示时间戳”(某些格式可能还有其他与时间相关的元数据,但你想要开始时找到PTS)。然后,你需要自己将文本绘制到已解码的帧上。我使用Qt来做类似的事情,通过在带有帧数据的QImage上使用QPainter,但你可能会找到其他API更容易绘制图像。然后,使用FFMPEG API制作一个压缩视频,其中包含你新绘制的帧。如果你还想添加音频,这将会更加复杂。我自己的工作并不关心音频,所以我没有烦恼地学习API的音频方面。基本上,在从文件中获取包的读取循环中,其中一些将是音频。你需要将它们保存并将它们写入输出文件。

我只使用过C API,而不是C#,所以我不知道是否有任何特殊的问题需要担心。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接