FFmpeg与ffmpeg的介绍

FFmpeg or ffmpeg

image.png

FFmpeg tools

FFmpeg提供的所有工具:
image.png

FFmpeg libraries

image.png

ffmpeg

image.png

安装ffmpeg

https://github.com/BtbN/FFmpeg-Builds/releases
可以去这个github网站里下载构建版本

ffmpeg基础语法

在powershell中使用
image.png构建好的ffmpeg目录下的bin目录下按shift加鼠标右键然后按s快捷键即可在目录下打开powershell
前面是输入选项和路径,后面是输出选项和路径。
image.png

将图片序列转成视频

将 文件夹中符合img_seq.%04d的图片整合成24帧的视频序列,用h264编码:.\ffmpeg.exe -framerate 24 -i ._image_sequences\img_seq\img_seq.%04d.png -c:v h264 output.mp4
-c:v: h264 意思是使用h264编码视频流

ffmpeg与输出以及h264相关的option介绍

Constant Rate Factor (crf)输出质量

image.png

Preset 编码速度预设

image.png

Tune(不经常使用)

image.png

Profile(-profile:v) and Level(-level) (为了兼容旧设备才需要看这个)

image.png

更改输出的缩放比

image.png

更改像素格式

可以通过ffmpeg -pix_fmts查看所有可用像素格式的列表
image.png

添加音频

图片序列转化成视频的同时添加音频文件到视频文件中

image.png

设置输出的音频的选项

image.png

当音频长度比视频长度要长时,如何舍弃掉多余的视频

image.png

当音频长度比视频长度短时

image.png

Codecs and Containers 编码解码器与容器

codec(编码解码器)

image.png

Container(容器,指文件格式)

image.png

file encoding(文件编码)

image.png

Converting Containers 转换文件格式

image.png

Transcoding转码

image.png

Converting and transcoding 转码

image.png

Generating an Image Sequence生成图像序列

图像序列转成视频:image.png
视频转成图像序列(PNG):image.png png格式文件大小更大,生成质量更高
视频转成图像序列(JPG):image.png jpg格式文件大小很小,默认生成质量低,但是可以通过 -qscale:v 4 来提高质量, 数值为(1-31)数值越小质量越高。

控制是否覆盖

image.png
image.png
image.png

通过python调用ffmpeg

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
import subprocess


FFMPEG_PATH = "D:/ffmpeg-master-latest-win64-gpl-shared/bin/ffmpeg.exe"


def encode_image_sequence(image_seq_path, output_path, framerate=24, crf=21, preset="ultrafast", audio_path=None):


ffmpeg_cmd = FFMPEG_PATH
ffmpeg_cmd += ' -y'
ffmpeg_cmd += ' -framerate {0}'.format(framerate)
ffmpeg_cmd += ' -i {0}'.format(image_seq_path)
if audio_path:
ffmpeg_cmd += ' -i {0}'.format(audio_path)

ffmpeg_cmd += ' -c:v libx264 -crf {0} -preset {1}'.format(crf,preset)
if audio_path:
ffmpeg_cmd += ' -c:a aac -filter_complex "[1:0] apad" -shortest'

ffmpeg_cmd += ' {0}'.format(output_path)

print(ffmpeg_cmd)
subprocess.call(ffmpeg_cmd)



if __name__ == "__main__":


img_seq_path = "D:/ffmpeg-master-latest-win64-gpl-shared/bin/_image_sequences/tears_of_steel_100_frames/overrun.%04d.png"
audio_path = "D:/ffmpeg-master-latest-win64-gpl-shared/bin/_audio/overrun.wav"
output_path = "D:/ffmpeg-master-latest-win64-gpl-shared/bin/overrun.mp4"


encode_image_sequence(img_seq_path,output_path,audio_path=audio_path)

使用Pyside2制作带界面的视频转换工具

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
import os
import subprocess
import sys
from PySide2 import QtGui
from PySide2 import QtWidgets


class TranscodeWindow(QtWidgets.QWidget):


FFMPEG_PATH = "D:/ffmpeg-master-latest-win64-gpl-shared/bin/ffmpeg.exe"


QUALITY_OPTIONS = [
["very high", "18"], # combobox item label, crf value
["high", "20"],
["medium", "23"],
["low", "26"]
]
QUALITY_DEFAULT = "medium"


PRESETS = [
["very slow", "veryslow"], # combobox item label, preset value
["slower", "slower"],
["slow", "slow"],
["medium", "medium"],
["fast", "fast"],
["faster", "faster"],
["very fast", "veryfast"],
["ultra fast", "ultrafast"]
]
PRESET_DEFAULT = "medium"



def __init__(self):
super(TranscodeWindow, self).__init__(parent=None)


self.setWindowTitle("FFmpeg Transcoder")
self.setMinimumSize(400, 300)


self.create_widgets()
self.create_layout()
self.create_connections()


def create_widgets(self):
self.input_path_le = QtWidgets.QLineEdit()
self.input_path_btn = QtWidgets.QPushButton("...")
self.input_path_btn.setFixedWidth(30)


self.output_path_le = QtWidgets.QLineEdit()
self.output_path_btn = QtWidgets.QPushButton("...")
self.output_path_btn.setFixedWidth(30)


self.video_codec_combo = QtWidgets.QComboBox()
self.video_codec_combo.addItem("h264", "libx264")


self.quality_combo = QtWidgets.QComboBox()
for quality_option in self.QUALITY_OPTIONS:
self.quality_combo.addItem(quality_option[0], quality_option[1])
self.quality_combo.setCurrentText(self.QUALITY_DEFAULT)


self.preset_combo = QtWidgets.QComboBox()
for preset in self.PRESETS:
self.preset_combo.addItem(preset[0], preset[1])
self.preset_combo.setCurrentText(self.PRESET_DEFAULT)


self.audio_codec_combo = QtWidgets.QComboBox()
self.audio_codec_combo.addItem("aac", "aac")


self.transcode_btn = QtWidgets.QPushButton("Transcode")
self.cancel_btn = QtWidgets.QPushButton("Cancel")


def create_layout(self):


input_grp = QtWidgets.QGroupBox("Input Path")
input_grp_layout = QtWidgets.QHBoxLayout()
input_grp_layout.addWidget(self.input_path_le)
input_grp_layout.addWidget(self.input_path_btn)
input_grp.setLayout(input_grp_layout)


output_grp = QtWidgets.QGroupBox("Output Path")
output_grp_layout = QtWidgets.QHBoxLayout()
output_grp_layout.addWidget(self.output_path_le)
output_grp_layout.addWidget(self.output_path_btn)
output_grp.setLayout(output_grp_layout)


video_options_grp = QtWidgets.QGroupBox("Video Options")
video_options_grp_layout = QtWidgets.QFormLayout()
video_options_grp.setLayout(video_options_grp_layout)


video_codec_layout = QtWidgets.QHBoxLayout()
video_codec_layout.addWidget(self.video_codec_combo)
video_codec_layout.addStretch()
video_options_grp_layout.addRow("Video Codec:", video_codec_layout)


quality_layout = QtWidgets.QHBoxLayout()
quality_layout.addWidget(self.quality_combo)
quality_layout.ad
quality_layout.addStretch()
video_options_grp_layout.addRow("Quality:", quality_layout)


preset_layout = QtWidgets.QHBoxLayout()
preset_layout.addWidget(self.preset_combo)
preset_layout.addStretch()
video_options_grp_layout.addRow("Preset:", preset_layout)


audio_options_grp = QtWidgets.QGroupBox("Audio Options")
audio_options_grp_layout = QtWidgets.QFormLayout()
audio_options_grp.setLayout(audio_options_grp_layout)


audio_codec_layout = QtWidgets.QHBoxLayout()
audio_codec_layout.addWidget(self.audio_codec_combo)
audio_codec_layout.addStretch()
audio_options_grp_layout.addRow("Audio Codec:", audio_codec_layout)

options_layout = QtWidgets.QHBoxLayout()
options_layout.addWidget(video_options_grp)
options_layout.addWidget(audio_options_grp)


button_layout = QtWidgets.QHBoxLayout()
button_layout.addStretch()
button_layout.addWidget(self.transcode_btn)
button_layout.addWidget(self.cancel_btn)


main_layout = QtWidgets.QVBoxLayout(self)
main_layout.addWidget(input_grp)
main_layout.addWidget(output_grp)
main_layout.addLayout(options_layout)
main_layout.addStretch()
main_layout.addLayout(button_layout)


def create_connections(self):
self.input_path_btn.clicked.connect(self.set_input_path)
self.output_path_btn.clicked.connect(self.set_output_path)


self.transcode_btn.clicked.connect(self.transcode)
self.cancel_btn.clicked.connect(self.close)


def set_input_path(self):
filters = ""
selected_filter = ""


input_path, selected_filter = QtWidgets.QFileDialog.getOpenFileName(self, "Select an Input File", "", filters, selected_filter)
if input_path:
self.input_path_le.setText(input_path)


def set_output_path(self):
filters = "*.mp4"
selected_filter = "*.mp4"


output_path, selected_filter = QtWidgets.QFileDialog.getSaveFileName(self, "Save File As", "", filters, selected_filter)
if output_path:
self.output_path_le.setText(output_path)


def transcode(self):

input_path = self.input_path_le.text()
if not input_path:
QtWidgets.QMessageBox.critical(self, "Transcode Error", "Input path not set")
return
if not os.path.exists(input_path):
QtWidgets.QMessageBox.critical(self, "Transcode Error", "Input path does not exist")
return


output_path = self.output_path_le.text()
if not output_path:
QtWidgets.QMessageBox.critical(self, "Transcode Error", "Output path not set")
return


video_codec = self.video_codec_combo.currentData()
crf = self.quality_combo.currentData()
preset = self.preset_combo.currentData()


audio_codec = self.audio_codec_combo.currentData()


args = [self.FFMPEG_PATH] # executable path
args.extend(["-hide_banner", "-y"]) # global options
args.extend(["-i", input_path]) # input path
args.extend(["-c:v", video_codec, "-crf", crf, "-preset", preset]) # video output options
args.extend(["-c:a", audio_codec]) # audio output options
args.append(output_path) # output path


subprocess.call(args)


QtWidgets.QMessageBox.information(self, "Transcode Complete", "File transcode operation complete.")



if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv)


window = TranscodeWindow()
window.show()


app.exec_()

修剪视频

舍弃前面的视频内容

image.png

舍弃最后或中间的视频内容

image.png
image.png

输出指定时间的视频

image.png

警告

image.png

提取视频的第一帧的图片

获得视频的第一帧的图片:image.png

提取视频的中间处的一帧图片

其中ffprobe.exe是FFmpeg提供的用来查看视频信息的exe文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
import subprocess


FFMPEG_PATH = "D:/ffmpeg/ffmpeg-4.2.1/bin/ffmpeg.exe"
FFPROBE_PATH = "D:/ffmpeg/ffmpeg-4.2.1/bin/ffprobe.exe"



def extract_middle_image(source_path, output_path):


ffprobe_cmd = FFPROBE_PATH
ffprobe_cmd += ' -v error -show_entries format=duration -of default=noprint_wrappers=1:nokey=1 '
ffprobe_cmd += source_path


duration = float(subprocess.check_output(ffprobe_cmd))


ffmpeg_cmd = FFMPEG_PATH
ffmpeg_cmd += ' -y -i {0} -ss {1} -frames:v 1 {2}'.format(source_path, duration/2.0, output_path)


print(ffmpeg_cmd)
subprocess.call(ffmpeg_cmd)



def encode_image_sequence(image_seq_path, output_path, framerate=24, crf=21, preset="ultrafast", audio_path=None):


ffmpeg_cmd = FFMPEG_PATH
ffmpeg_cmd += ' -y '
ffmpeg_cmd += ' -framerate {0}'.format(framerate)
ffmpeg_cmd += ' -i {0}'.format(image_seq_path)
if audio_path:
ffmpeg_cmd += ' -i {0}'.format(audio_path)


ffmpeg_cmd += ' -c:v libx264 -crf {0} -preset {1}'.format(crf, preset)
if audio_path:
ffmpeg_cmd += ' -c:a aac -filter_complex "[1:0] apad" -shortest'


ffmpeg_cmd += ' {0}'.format(output_path)


print(ffmpeg_cmd)
subprocess.call(ffmpeg_cmd)



if __name__ == "__main__":


source_path = "D:/ffmpeg/ffmpeg-4.2.1/bin/bbb_shot_060.mp4"
output_path = "D:/ffmpeg/ffmpeg-4.2.1/bin/middle_frame.png"


extract_middle_image(source_path, output_path)

通过过滤器添加水印

image.png
举例:
image.png
将图片放到视频的右下角
image.png这里的[0][1]可以不要,然后(W-w-20):(H-h-20)的意思是水印的位置,水印的位置左边如图所示:W为视频的宽度,w为图片的宽度
image.png

通过过滤器调整添加的水印的大小的透明度

image.png
过滤器为蓝色的字体, [1]scale 是指将前面的输入中的第二个进行缩放。 iw为原图片的像素大小。 iw也可以换成数字代指像素,h=-1的意思是让ffmpeg根据前面的宽度w的大小自动调整高度h的大小。然后后面的[image_scaled]的意思是通过过滤器修改后的图片的名字(因为后面要用到这个更改过缩放的图片,因此先定义一个暂时的名字),[image_scaled]lut=a=val*0.4[image_final];的意思是在原来的基础上将alpha通道的大小调整为原来的0.4倍,然后再给个修改后的名字[image_final],这个名字是自定义的。然后[0][image_final]overlay=(W-w-20):(H-h-20)为添加水印。

刻录时间码

image.png