介绍
在本教程中,您将构建一个能够从输入视频中提取音频、转录提取的音频、根据转录生成字幕文件,然后将字幕添加到输入视频副本的Python应用程序。
为了构建这个应用程序,您将使用FFmpeg从输入视频中提取音频。您将使用OpenAI的Whisper生成提取的音频的转录,然后使用这个转录生成字幕文件。此外,您将使用FFmpeg将生成的字幕文件添加到输入视频的副本中。
FFmpeg是一个处理多媒体数据的强大且开源的软件套件,包括音频和视频处理任务。它提供了一个命令行工具,允许用户使用各种格式和编解码器转换、编辑和操作多媒体文件。
OpenAI的Whisper是一个自动语音识别(ASR)系统,旨在将口语转换为书面文本。通过大量的多语言和多任务监督数据进行训练,它在准确转录各种音频内容方面表现出色。
通过本教程,您将拥有一个能够为视频添加字幕的应用程序:
先决条件
为了按照本教程操作,读者需要准备以下工具:
步骤 1 — 创建项目根目录
在本部分,你将创建项目目录,下载输入视频,创建并激活虚拟环境,并安装所需的 Python 包。
打开终端窗口并导航到适合项目的位置。运行以下命令创建项目目录:
进入项目目录:
下载 这个编辑过的视频 并将其存储在项目的根目录下,命名为 input.mp4
。该视频展示了一个名叫 Rushawn 的孩子演唱 Jermaine Edward 的 “Beautiful Day”。你将在本教程中使用的编辑过的视频来自以下 YouTube 视频:
创建一个新的虚拟环境并命名为 env
:
激活虚拟环境:
现在,使用以下命令安装构建此应用程序所需的软件包:
通过上述命令,你安装了以下库:
-
faster-whisper
: 是 OpenAI Whisper 模型的重新设计版本,利用了 CTranslate2,这是一个用于 Transformer 模型的高性能推理引擎。该实现在消耗更少内存的同时,速度比 openai/whisper 快四倍,精度相当。 -
ffmpeg-python
:是一个Python库,它提供了对FFmpeg工具的封装,使用户可以轻松地在Python脚本中与FFmpeg功能进行交互。通过Pythonic接口,它可以执行视频和音频处理任务,如编辑、转换和操作。
运行以下命令,将在虚拟环境中使用pip
安装的软件包保存到名为requirements.txt
的文件中:
requirements.txt
文件应该类似于以下内容:
av==10.0.0
certifi==2023.7.22
charset-normalizer==3.3.2
coloredlogs==15.0.1
ctranslate2==3.20.0
faster-whisper==0.9.0
ffmpeg-python==0.2.0
filelock==3.13.1
flatbuffers==23.5.26
fsspec==2023.10.0
future==0.18.3
huggingface-hub==0.17.3
humanfriendly==10.0
idna==3.4
mpmath==1.3.0
numpy==1.26.1
onnxruntime==1.16.1
packaging==23.2
protobuf==4.25.0
PyYAML==6.0.1
requests==2.31.0
sympy==1.12
tokenizers==0.14.1
tqdm==4.66.1
typing_extensions==4.8.0
urllib3==2.0.7
在这一部分,您创建了项目目录,下载了将在本教程中使用的输入视频,设置了虚拟环境,激活了它,并安装了必要的Python包。在下一部分中,您将为输入视频生成转录。
第二步 — 生成视频转录
在这个部分,你将创建Python脚本,应用程序将在其中运行。在这个脚本内部,你将使用ffmpeg-python
库从前一节下载的输入视频中提取音轨,并将其保存为WAV文件。接下来,你将使用faster-whisper
库为提取的音频生成转录。
在你的项目根目录下,创建一个名为main.py
的文件,并将以下代码添加到其中:
在这里,代码首先通过导入各种库和模块开始,包括time
、math
、从ffmpeg-python
导入ffmpeg
,以及从faster_whisper
导入名为WhisperModel
的自定义模块。这些库将用于视频和音频处理、转录和字幕生成。
接下来,代码设置输入视频文件名,并将其存储在名为input_video
的常量中,然后将视频文件名(不带.mp4
扩展名)存储在名为input_video_name
的常量中。在这里设置输入文件名将允许你处理多个输入视频,而不会覆盖为它们生成的字幕和输出视频文件。
将以下代码添加到你的main.py
的底部:
上述代码定义了一个名为extract_audio()
的函数,负责从输入视频中提取音频。
首先,它将要提取的音频的名称设置为通过将audio-
附加到输入视频的基本名称并添加.wav
扩展名形成的名称,并将此名称存储在一个名为extracted_audio
的常量中。
接下来,代码调用ffmpeg
库的ffmpeg.input()
方法打开输入视频并创建一个名为stream
的输入流对象。
然后,代码调用ffmpeg.output()
方法创建一个带有输入流和定义的提取音频文件名的输出流对象。
在设置输出流之后,代码调用ffmpeg.run()
方法,将输出流作为参数传递以启动音频提取过程并将提取的音频文件保存在项目的根目录中。此外,还包括一个布尔参数overwrite_output=True
,以替换任何已存在的输出文件为新生成的文件,如果这样的文件已经存在的话。
最后,代码返回提取的音频文件的名称。
在extract_audio()
函数下方添加以下代码:
在这里,代码定义了一个名为run()
的函数然后调用它。此函数调用所有生成并向视频添加字幕所需的函数。
在函数内部,代码调用extract_audio()
函数从视频中提取音频,然后将返回的音频文件名存储在名为extracted_audio
的变量中。
返回终端并执行以下命令以运行main.py
脚本:
执行上述命令后,FFmpeg的输出将显示在终端上,并且名为audio-input.wav
的文件,其中包含从输入视频中提取的音频,将存储在项目的根目录中。
返回到您的main.py
文件,并在extract_audio()
和run()
函数之间添加以下代码:
上述代码定义了一个名为transcribe
的函数,负责转录从输入视频中提取的音频文件。
首先,该代码创建了一个WhisperModel
对象的实例,并将模型类型设置为small
。OpenAI的Whisper有以下模型类型:tiny,base,small,medium和large。tiny模型是最小且速度最快的,large模型是最大且最慢但最准确的。
接下来,代码使用提取的音频调用model.transcribe()
方法,将分段函数和音频信息检索为变量info
和segments
。分段函数是一个Python生成器,因此只有在代码迭代它时转录才会开始。通过在list
或for
循环中收集段,可以完全运行转录。
然后,代码将检测到的音频语言存储在名为info
的常量中,并将其打印到控制台。
在打印检测到的语言之后,代码会将转录段收集到一个list
中以运行转录,并将收集到的段存储在一个名为segments
的变量中。然后,代码循环遍历转录段列表,并将每个段的开始时间、结束时间和文本打印到控制台。
最后,代码返回在音频中检测到的语言和转录段。
在run()
函数内添加以下代码:
添加的代码调用了提取的音频作为参数的转录函数,并将返回的值存储在名为language
和segments
的常量中。
返回终端并执行以下命令以运行main.py
脚本:
首次运行此脚本时,代码将首先下载并缓存Whisper Small模型,后续运行将会快得多。
执行上述命令后,您应该在控制台中看到以下输出:
…
Transcription language en
[0.00s -> 4.00s] This morning I wake up and I look in the mirror
[4.00s -> 8.00s] Every part of my body was in the place many people lie
[8.00s -> 11.00s] I don't wanna act too high and mighty
[11.00s -> 15.00s] Cause tomorrow I may fall down on my face
[15.00s -> 17.00s] Lord I thank You for sunshine
[17.00s -> 19.00s] Thank You for rain
[19.00s -> 20.00s] Thank You for joy
[20.00s -> 22.00s] Thank You for pain
[22.00s -> 25.00s] It's a beautiful day
[25.00s -> 28.00s] It's a beautiful day
上面的输出显示音频中检测到的语言为英语(en
)。此外,它显示了每个转录段的开始和结束时间(以秒为单位)以及文本。
警告:尽管OpenAI的Whisper语音识别非常准确,但并非100%准确,它可能会受到限制和偶发错误的影响,特别是在具有挑战性的语言或音频场景中。因此,请务必始终手动检查转录。
在这一部分,您创建了一个用于应用程序的Python脚本。在脚本内部,使用了ffmpeg-python
来从下载的视频中提取音频并将其保存为WAV文件。然后,使用了faster-whisper
库来为提取的音频生成转录。在接下来的部分,您将根据转录生成字幕文件,然后将字幕添加到视频中。
第三步 —— 生成并添加字幕到视频
在这一部分,首先,您将了解字幕文件是什么以及它的结构。接下来,您将使用前一部分生成的转录片段来创建字幕文件。创建字幕文件后,您将使用ffmpeg-python
库将字幕文件添加到输入视频的副本中。
理解字幕:结构和类型
A subtitle file is a text file that contains timed text information corresponding to spoken or written content in a video or film. It typically includes information about when each subtitle should appear and disappear on the screen. There are many subtitle formats, however, in this tutorial, we will focus on the widely used format named SubRip (SRT).
A subtitle file is organized into a series of subtitle entries, each typically following a specific format. The common structure of a subtitle entry includes:
-
字幕索引:在文件中表示字幕顺序的顺序号。
-
时间码:开始和结束时间标记,指定字幕文本应显示的时间。时间码通常格式化为
HH:MM:SS,sss
(小时,分钟,秒,毫秒)。 -
字幕文本:字幕条目的实际文本,代表口头或书面内容。此文本在指定的时间间隔内显示在屏幕上。
例如,SRT文件中的字幕条目可能如下所示:
1
00:00:10,500 --> 00:00:15,000
This is an example subtitle.
在此示例中,索引为1
,时间码指示字幕应从10.5
秒显示到15
秒,字幕文本为This is an example subtitle.
字幕可分为两种主要类型:
-
软字幕:也称为闭路字幕,作为单独的文件(如SRT)外部存储,并且可以独立于视频添加或移除。它们提供了观众的灵活性,允许切换、语言切换和设置的自定义。然而,它们的有效性取决于视频播放器的支持,并非所有播放器都普遍支持软字幕。
-
硬字幕:在编辑或编码过程中永久嵌入视频帧中,成为视频的固定部分。虽然它们确保在不支持外部字幕文件的播放器上始终可见,但修改或关闭它们需要重新编码整个视频,限制了用户的控制权。
创建字幕文件
返回到您的main.py
文件,并在transcribe()
和run()
函数之间添加以下代码:
这里,代码定义了一个名为format_time()
的函数,该函数负责将给定的转录片段的起始时间和结束时间(以秒为单位)转换为适用于字幕的时间格式,显示小时、分钟、秒和毫秒(HH:MM:SS,sss
)。
代码首先从给定的时间(以秒为单位)计算出小时、分钟、秒和毫秒,然后相应地对它们进行格式化,然后返回格式化后的时间。
在format_time()
和run()
函数之间添加以下代码:
添加的代码定义了一个名为generate_subtitle_file()
的函数,该函数接受提取的音频中检测到的语言和转录片段作为参数。此函数负责根据语言和转录片段生成一个SRT字幕文件。
首先,代码将字幕文件的名称设置为通过将检测到的语言附加到输入视频的基本名称并添加“.srt”扩展名形成的名称,并将此名称存储在名为subtitle_file
的常量中。此外,代码定义了一个名为text
的变量,用于存储字幕条目。
然后,代码遍历转录片段,使用format_time()
函数对起始和结束时间进行格式化,使用这些格式化的值以及段索引和文本创建一个字幕条目,然后在每个字幕条目之间添加一个空行。
最后,该代码在您的项目根目录中创建一个以前设置的名称命名的字幕文件,将字幕条目添加到文件中,并返回字幕文件名。
将以下代码添加到您的run()
函数的末尾:
添加的代码调用generate_subtitle_file()
函数,参数为检测到的语言和转录片段,并将返回的字幕文件名存储在一个名为subtitle_file
的常量中。
返回终端并执行以下命令以运行main.py
脚本:
上述命令运行后,将在您的项目根目录中保存一个名为sub-input.en.srt
的字幕文件。
打开sub-input.en.srt
字幕文件,您应该看到类似以下内容:
1
00:00:0,000 --> 00:00:4,000
This morning I wake up and I look in the mirror
2
00:00:4,000 --> 00:00:8,000
Every part of my body was in the place many people lie
3
00:00:8,000 --> 00:00:11,000
I don't wanna act too high and mighty
4
00:00:11,000 --> 00:00:15,000
Cause tomorrow I may fall down on my face
5
00:00:15,000 --> 00:00:17,000
Lord I thank You for sunshine
6
00:00:17,000 --> 00:00:19,000
Thank You for rain
7
00:00:19,000 --> 00:00:20,000
Thank You for joy
8
00:00:20,000 --> 00:00:22,000
Thank You for pain
9
00:00:22,000 --> 00:00:25,000
It's a beautiful day
10
00:00:25,000 --> 00:00:28,000
It's a beautiful day
向视频添加字幕
将以下代码添加到generate_subtitle_file()
和run()
函数之间:
这里,该代码定义了一个名为add_subtitle_to_video()
的函数,其参数为一个布尔值,用于确定是添加软字幕还是硬字幕,字幕文件名以及在转录中检测到的语言。该函数负责将软字幕或硬字幕添加到输入视频的副本中。
首先,代碼使用 ffmpeg.input()
方法,將輸入視頻和字幕文件創建為輸入流對象,並分別將它們存儲在常量 video_input_stream
和 subtitle_input_stream
中。
在創建輸入流之後,代碼將輸出視頻文件的名稱設置為將 output-
附加到輸入視頻的基本名稱並添加“.mp4”擴展名,並將此名稱存儲在一個名為 output_video
的常量中。此外,將字幕軌道的名稱設置為字幕文件名,不包括 .srt
擴展名,並將此名稱存儲在一個名為 subtitle_track_title
的常量中。
接下來,代碼檢查布爾值 soft_subtitle
是否設置為 True
,這表示應該添加一個軟字幕。
如果是這種情況,代碼調用 ffmpeg.output()
方法,使用輸入流、輸出視頻文件名以及以下選項來創建輸出流對象:
-
"c": "copy"
: 它指定視頻編解碼器和其他視頻參數應直接從輸入複製到輸出,而無需重新編碼。 -
"c:s": "mov_text"
:它指定字幕编解码器和参数也应该从输入复制到输出,而不重新编码。mov_text
是在MP4/MOV文件中常用的字幕编解码器。 -
"metadata:s:s:0": f"language={subtitle_language}"
:它为字幕流设置语言元数据。语言设置为存储在subtitle_language
中的值。 -
"metadata:s:s:0": f"title={subtitle_track_title}"
:它为字幕流设置标题元数据。标题设置为存储在subtitle_track_title
中的值。
最后,代码调用ffmpeg.run()
方法,将输出流作为参数传递,将软字幕添加到视频并将输出视频文件保存在项目的根目录中。
在add_subtitle_to_video()
函数的底部添加以下代码:
如果布尔值soft_subtitle
设置为False
,则突出显示的代码将运行,表示应添加硬字幕。
如果是这种情况,首先,代码调用ffmpeg.output()
方法创建一个输出流对象,其中包括输入视频流、输出视频文件名和vf=f"subtitles={subtitle_file}"
参数。这里的vf
代表“视频过滤器”,它用于对视频流应用过滤器。在这种情况下,正在应用的过滤器是添加字幕。
最后,代码调用ffmpeg.run()
方法,将输出流作为参数传递,将硬字幕添加到视频中,并将输出视频文件保存在项目的根目录中。
将以下突出显示的代码添加到run()
函数中:
突出显示的代码调用add_subtitle_to_video()
,将soft_subtitle
参数设置为True
,传递字幕文件名和字幕语言,以将软字幕添加到输入视频的副本中。
返回终端并执行以下命令来运行main.py
脚本:
运行上述命令后,一个名为output-input.mp4
的输出视频文件将保存在项目的根目录中。
使用您喜欢的视频播放器打开视频,为视频选择字幕,并注意在选择之前字幕不会显示:
返回到main.py
文件,导航至run()
函数,并在add_subtitle_to_video()
函数调用中将soft_subtitle
参数设置为False
:
在这里,将soft_subtitle
参数设置为False
以向视频添加硬字幕。
返回到您的终端并执行以下命令以运行main.py
脚本:
运行上述命令后,位于您项目根目录中的output-input.mp4
视频文件将被覆盖。
使用您喜欢的视频播放器打开视频,尝试为视频选择字幕,并注意到尚未提供一个字幕,但是一个字幕正在显示:
在本节中,您了解了SRT字幕文件的结构,并利用了上一节的转录片段来创建一个。随后,使用了ffmpeg-python
库将生成的字幕文件添加到视频中。
结论
在本教程中,您使用了ffmpeg-python
和faster-whisper
Python库来构建一个能够从输入视频中提取音频、转录提取的音频、基于转录生成字幕文件,并将字幕添加到输入视频副本的应用程序。