音视频基础
帧率、码率、分辨率
-
体积(Volume):一个视频的容量(文件大小),单位是B(byte)。码率影响体积,与体积成正比。如果码率为变量,则帧率也会影响体积,帧率越高,每秒钟经过的画面越多,需要的码率也越高,体积也越大。
-
帧率(FPS):每秒钟要多少帧画面,就是在1秒钟时间里传输的图片的帧数。影响画面流畅度,与画面流畅度成正比。帧率越大,画面越流畅;帧率越小,画面越有跳动感。如我们看b站常见的
1080P 60帧
,其中60帧就是帧率,表示每秒播放60张图片。 -
码率(Bitrate):编码器每秒传输的数据大小(带宽),单位是kbps 即千位每秒,比如800kbps代表编码器每秒产生800kb(或100KB)的数据。如b站要求up主上传的视频码率最高为6000kbps(H264/AVC编码)。
-
分辨率(Resolution):单位英寸中所包含的横纵向像素点数(图像宽高); VGA:Video Graphics Array(视频图像分辨率)。影响图像大小,与图像大小成正比:分辨率越高,图像越大;分辨率越低,图像越小。在码率一定的情况下,分辨率与清晰度成反比关系:分辨率越高,图像越不清晰,分辨率越低,图像越清晰。如我们看b站常见的最大分辨率
1080P
,其中1080P就是分辨率,P表示(Progressive scanning,逐行扫描)。以我们常见的16:9的屏幕举例(注意,一定要强调屏幕的宽高比例),1080表示1920 * 1080
个像素(其实我们可以说,1080P每一行有接近1K的像素点),2K则是2560 * 1440
,4K则是3840 * 2160
。
好的画质是分辨率、帧率和码率三者之间的平衡:
码率不是越大越好,如果不做码率大小上的限制,那么分辨率越高,画质越细腻;帧率越高,视频也越流畅,但相应的码率也会很大,因为每秒钟需要用更多的数据来承载较高的清晰度和流畅度。
如果限定一个码率,比如800kbps,那么帧率越高,编码器就必须加大对单帧画面的压缩比,也就是通过降低画质来承载足够多的帧数。
视频格式
一个音视频文件实际上分为3层:基础数据,编码,封装。
编码
使用不同的编码格式
,对视频数据进行压缩;封装
使用不同的封装格式
,将视频数据封装成不同的文件。
-
封装格式:封装格式只是视频的一层皮,只是对 文件信息 和 压缩好的音视频数据,按照一定的规则进行编排,不限制视频数据的帧率、分辨率、码率等参数,因此封装格式不会影响视频的清晰度。如MP4(兼容性强)、FLV(在线播放nb)、HLS(长视频nb)、AVI、MKV(支持外接字幕)、PCM、ACC、MOV等。
-
编码格式:编码格式才是音视频流编码的内在组织形式,对原始的音视频数据进行压缩。如H.264、H.265等。
H.264和H.265视频压缩算法
H.264,同时也是MPEG-4的第十部分,被称作,是由联合视频组(JVT,Joint Video Team)提出的视频编解码器标准(视频压缩/编码算法)。这个标准通常被称之为H.264/AVC
(或者AVC/H.264
或者H.264/MPEG-4AVC
或MPEG-4/H.264 AVC
),其中AVC(Advanced Video Coding,高级视频编码)。H.264主要包含:宏块细分图像、帧内压缩(intra compress)减少空间冗余、帧间预测(inter prediction)减少时间冗余、转换(transform) 和 量化(quantization)进行残留数据压缩、去区块滤波器(deblocking filter)、熵编码(entropy coding) 等模块。
H.265,通常被称为 H.265/HEVC
,两者都是基于块的视频编码技术,H.265的编码架构大致上和H.264的架构相似,但编码的文件大小会比H.264小50%,相同画质下更加节省带宽/流量。也主要包含:宏块细分图像、帧内压缩(intra compress)减少空间冗余、帧间预测(inter prediction)减少时间冗余、转换(transform) 和 量化(quantization)进行残留数据压缩、去区块滤波器(deblocking filter)、熵编码(entropy coding) 等模块。但在HEVC编码架构中,整体被分为了三个基本单位,分別是:编码单位(coding unit,CU)
、预测单位(predict unit,PU)
和转换单位(transform unit,TU)
。
-
宏块划分:把图像划分成一个一个的小像素块(patch)。
-
帧内压缩(intra compress):类似JPEG这种有损压缩,减少空间冗余。
-
帧间预测(inter prediction):根据关键帧和运动变化的信息,预测出中间帧,减少时间冗余。把帧分为:I帧(关键帧)、P帧(根据前一个I或P帧预测出来的帧)、B帧(根据前后两个I或P帧预测出来的帧)。
I帧(关键帧)预测 P帧(下一帧):对于没有变化的宏块,P帧直接复制I帧。对于变化的宏块,编码的时候,记录变化的信息
,解码的时候,再预测回去。
B帧(双向预测帧):利用前后两帧来预测出的帧。
GPO的概念:I帧的间隔长度。录播视频GOP一般为帧率的4-5倍,直播视频GOP一般为帧率的1-2倍。如果太大,I帧间隔太远,会产生花屏问题,P帧和B帧预测不到位。
-
H264的编解码性能要求更低,更加普及;如H264由于算法优化,可以低于1Mbps的速度(码率)实现标清数字图像传送;
-
H265的压缩率更,因此带宽和容量的要求更低,但对编解码性能要求更高。如H265则可以实现利用1~2Mbps的传输速度(码率)传送720P(分辨率1280*720)普通高清音视频传送。
Moviepy
Moviepy 和 python-ffmpeg 和 用subprocess调用ffmpeg 有相同的效果,都可以实现:视频剪辑,视频拼接,插入标题、字幕水印,视频合成,自定义的高级的音视频特效等。此外,MoviePy 可以读写绝大多数常见的视频格式,甚至包括 GIF 格式!
MoviePy 使用软件 FFmpeg 读取和导出视频和音频文件,使用 ImageMagick 生产文字和 GIF 图。中间的处理过程赖于 Python 强大的数学处理库,高级特效和软件加强用到了许多的 Python 图像处理库。
- FFmpeg的优点是速度快,缺点则是命令复杂。
- Moviepy开发起来更加简便,缺点就是相当于ffmpeg速度更慢一些,且不支持stream video流媒体(如直播/摄像头)!
常见剪辑类
Clip
是所有剪辑类的基类,VideoClip
和AudioClip
继承自Clip,分别处理视频和音频数据。VideoClip
和AudioClip
又有很多派生类,用于处理 不同的视频和音频内容。所有的类都可以从Moviepy.editor
模块导入。Clip、VideoClip、AudioClip三个基类是非常少用的,我们接下来着重介绍他们的派生类。
其中最核心的是VideoClip类的对象 clips(视频片段)
,开发者可以对 clips 进行修改(剪切,调速度,调亮度…)或者和其他 clip 混合拼接到一起。vedio clip 可以由视频文件,图像,文本或者动画来创建实例。vedio clip 可以拥有一个音频轨道(audio clip) 和一个叠加层的 vedio clip(这是一个特殊的 VedioClip,这意味着,当一个视频和其他 VedioClip 混合的时候,这个叠加层 clip 是隐藏的)
VideoFlieClip
ImageFlieClip
ColorClip
TextClip
CompositeVideoClip
AudioFlieClip
CompositeAudioClip
常见操作
MoviePy - 中文文档 (一个专业的 python 音视频编辑库) 教程
音视频的读入与导出
读入视频,导出视频:write_videofile
# 导入需要的库
from moviepy.editor import *
# 从本地载入视频myHolidays.mp4,并截取00:00:50 - 00:00:60部分
clip = VideoFileClip("Mojito.mp4").subclip(50,60)
# 调低音频音量 (volume x 0.8)
clip = clip.volumex(0.8)
# 做一个txt clip. 自定义样式,颜色.
txt_clip = TextClip("Mojito by Jay",fontsize=70,color='white')
# 文本clip在屏幕正中显示持续10秒
txt_clip = txt_clip.set_pos('center').set_duration(10)
# 把 text clip 的内容覆盖 video clip
video = CompositeVideoClip([clip, txt_clip])
# 把最后生成的视频导出到文件内
video.write_videofile("Mojito_edited.mp4") # 默认编解码器codec="libx264"
video.write_videofile("Mojito_edited.mp4", codec="mpeg4") # MP4也可以指定编解码器为"mpeg4"
video.write_videofile("Mojito_edited.avi", codec="rawvideo")
video.write_videofile("Mojito_edited.webm") # 默认编解码器codec="libx264"
video.write_videofile("Mojito_edited.flv", codec="flv")
读入音频,导出音频:write_audiofile
# 导入需要的库
from moviepy.editor import *
# 从本地载入视频myHolidays.mp4,并截取00:00:50 - 00:00:60部分
clip = VideoFileClip("/data3/yzr/Mojito.mp4").subclip(3,10)
# 取clip的audio音频部分
audio = clip.audio
# 保存为MP3
audio.write_audiofile("Mojito.mp3")
# 保存为wav
audio.write_audiofile("Mojito.wav", codec="pcm_s32le")
# 保存为wmav1
audio.write_audiofile("Mojito.wam", codec="wam1")
导出GIF动图:write_gif
# 导入需要的库
from moviepy.editor import *
# 从本地载入视频myHolidays.mp4,并截取00:00:50 - 00:00:60部分
clip = VideoFileClip("/data3/yzr/Mojito.mp4").subclip(3,10)
# 把最后生成的视频导出到文件内
clip.write_gif("Mojito_edited.gif")
导出视频截图:save_frame
# 导入需要的库
from moviepy.editor import *
# 从本地载入视频myHolidays.mp4,并截取00:00:50 - 00:00:60部分
clip = VideoFileClip("/data3/yzr/Mojito.mp4").subclip(3,10)
# 保存clip起始帧
clip.save_frame("frame.png")
# 保存视频第10秒截图
clip.save_frame("frame.png", t=10)
假如我们希望每个2s保存一张截图,我们应该如何实现呢:
# 导入需要的库
from moviepy.editor import *
# 从本地载入视频myHolidays.mp4,并截取00:00:50 - 00:00:60部分
clip = VideoFileClip("/data3/yzr/Mojito.mp4").subclip(3,10)
image_dir = "frames"
if not os.path.exists(image_dir): os.mkdir(image_dir)
interval = 2
duration = clip.duration
times = list(range(0,int(duration),interval))
for t in times:
clip.save_frame(os.path.join(image_dir, f"{t}.png"), t=t)
截取音视频片段
使用subclip(t_start, t_end)
截取video clip的片段:
# 导入需要的库
from moviepy.editor import *
# 从本地载入视频myHolidays.mp4
clip = VideoFileClip("Mojito.mp4")
# 截取00:00:50 - 00:00:60部分
clip = clip.subclip(50,60)
# 默认视频编解码器codec="libx264",声音解码器audio_codec="acc"
clip.write_videofile("Mojito_edited.mp4", audio_codec="acc")
对于audio也是相同操作:
# 导入需要的库
from moviepy.editor import *
# 从本地载入视频myHolidays.mp4
clip = AudioFileClip("Mojito.mp3")
# 截取00:00:50 - 00:00:60部分
clip = clip.subclip(50,60)
clip.write_audiofile("Mojito_edited.mp4")
改变 clip 属性
非常常见的修改 clip 属性的方法有:clip.subclip
、clip.set_duration,
clip.set_audio
, clip.set_mask
, clip.set_start
等。
例如,使用resize
指定video clip的分辨率放缩比例,使用volumex
指定video clip的音量:
# 导入需要的库
from moviepy.editor import *
# 从本地载入视频myHolidays.mp4
clip = VideoFileClip("Mojito.mp4").subclip(3,7)
# 使用resize指定放缩比例0.5, 调低音频音量 (volume x 0.8)
clip = clip.resize(0.5).volumex(0.8)
# 默认视频编解码器codec="libx264",声音解码器audio_codec="acc"
clip.write_videofile("Mojito_edited.mp4", audio_codec="acc")
除了上述clip = clip.resize(0.5).volumex(0.8)
方式来修改clip属性,我们还可以借助fx
函数来统一修改音视频属性。
而且应用 clip.fx
方法还可以实现一些高级效果,通过fx
函数,分别使用vfx.resize
、vfx.speedx
、vfx.colorx
参数调整视频的分辨率、速度、亮度。loop
:让 clip 循环播放、timemirror
:让 clip 倒播。
这些方法位于特殊的模块 moviepy.video.fx
, moviepy.audio.fx
,比如 clip.fx(timemirror)
让视频倒播。
from moviepy.editor import *
clip = (VideoFileClip("myvideo.avi")
.fx( vfx.resize, width=460) # 尺寸变化,保持纵横比
.fx( vfx.speedx, 2) # 2倍速
.fx( vfx.colorx, 0.5)) # 画面调暗
为方便起见,当我们使用 moviepy.editor 的时候,比如我们使用 resize 的时候,我们经常会使用 clip.resize(0.5)
这样的简便的写法来代替 clip.fx( vfx.resize, 0.5)
的写法。
视频镜像翻转
使用fx()
函数传入vfx.mirror_x/y
对video clip进行水平或竖直翻转:
from moviepy.editor import VideoFileClip, clips_array, vfx
clip = clip1.fx(vfx.mirror_x)#x轴镜像
clip.write_videofile("my_video.mp4")
多视频拼接
视频拼接我们使用 concatenate_videoclips
函数,把视频一个接一个地拼接起来:
from moviepy.editor import VideoFileClip, concatenate_videoclips
clip1 = VideoFileClip("myvideo.mp4")
clip2 = VideoFileClip("myvideo2.mp4")
clip3 = VideoFileClip("myvideo3.mp4")
finalclip = concatenate_videoclips([clip1, clip2, clip3])
finalclip.write_videofile("my_concatenate.mp4")
会按照 clip1,clip2,clip3 的顺序将这三个 clip 播放。这些 clip 并不需要相同的时长或者大小,仅仅是首尾相连而已。我们还可以通过 transition=my_clip
这个参数来设置一下 clip 之间衔接的过渡动画。
多视频叠加
视频叠加我们使用 clip_array
函数,使用二维数组
的形式,将多个视频叠加在一块,margin(10)
指视频和视频之间的边框距离为10 pixel,比如一个大的画面同时播几个视频:
from moviepy.editor import VideoFileClip, clips_array, vfx
clip1 = VideoFileClip("myvideo.mp4").margin(10)
clip2 = clip1.fx(vfx.mirror_x)#x轴镜像
clip3 = clip1.fx(vfx.mirror_y)#y轴镜像
clip4 = clip1.resize(0.6)#尺寸等比缩放0.6
final_clip = clips_array([
[clip1, clip2],
[clip3, clip3]
])
final_clip.resize(width=480).write_videofile("my_stack.mp4")
复合叠加视频
CompositeVideoClip
这个类提供来更加灵活的方式来排版视频,但是它可要比 concatenate_videoclips
和 clips_array
函数要复杂的多了。
video = CompositeVideoClip([clip1, clip2, clip3])
现在,这 3 个 clip 是堆叠起来的,啥情况呢,就是,clip2 压在 clip1 上,clip3 压在最上面。大家可以想像成栈结构,从左往右,依次入栈这么堆叠在一块,对于一个普通的视频来说,这样堆叠实在是没办法看了。你想像一下,如果 clip3 的尺寸是最大的,比 clip1 和 clip2 都大,那我们只能看到 clip3,clip1 和 clip2 都被盖住了。 最终要合成的 clip 的尺寸是默认第一个 clip 的尺寸,一般第一个也就是做背景用了,但是我们有时候是需要我们的 clip 浮在一个比较大的合成体的上方的,那这个时候我们就需要声明合成 clip 的尺寸。
video = CompositeVideoClip([clip1, clip2, clip3], size=(720,480))
在堆叠视频中,每个 clip 会在通过 start()
函数声明的时间开始播放,我们可以像下面这样去设置。
video = CompositeVideoClip([
clip1, #在第0秒开始
clip2.set_start(5), #在第5秒开始
clip3.set_start(9) #第9秒开始
])
在下面的栗子里,可能 clip2 开始的时候,正好是 clip1 将要结束的时机,这样的情况,我们可以让 clip2 使用crossfadein(1)
在一秒内“渐入”的特效来显示。
video = CompositeVideoClip([
clip1,
clip2.set_start(5).crossfadein(1),
clip3.set_start(9).crossfadein(1.5)
])
假如clip2 和 clip3 都比 clip1 要小,那你可以通过设定位置决定 clip2 和 clip3 在画面中的位。下面的栗子就是通过set_pos((x,y))
指定坐标的形式(距离左上方的像素距离),把 clip2 和 clip3 在画面中指定位置。
video = CompositeVideoClip([
clip1,
clip2.set_start(5).crossfadein(1).set_pos((45,150)),
clip3.set_start(9).crossfadein(1.5).set_pos((90,100))
])
在 moviepy 中我们有很多的方法定位 clip 的位置:
clip2.set_pos((45,150)) #像素坐标
clip2.set_pos("center") #居中
clip2.set_pos(("center","top")) #水平方向居中,但是处置方向放置在顶部
clip2.set_pos(("left","center")) #水平方向放置在左边,垂直方向居中
clip2.set_pos((0.4,0.7), relative=True) #0.4倍宽,0.7倍高处
clip2.set_pos(lambda t: ('center', 50+t)) #水平居中,向下移动
实现在右下角叠加一个小的窗口,现实另一个视频
from moviepy.editor as mp
clip1 = VideoFileClip("myvideo.mp4")
clip2 = VideoFileClip("myvideo.mp4").resize(0.2)
video = CompositeVideoClip([
clip1set_pos(("center")),
clip2.set_pos(("right", "bottom"))
])
添加字幕或水印
Moviepy加字需要依赖ImageMagick
软件。
使用TextClip
创建文字类(可以用set_pos
和set_duration
设置字幕的位置和持续时间),使用CompositeVideoClip
将其与视频叠加在一起:
# 导入需要的库
from moviepy.editor import *
# 从本地载入视频myHolidays.mp4,并截取00:00:50 - 00:00:60部分
clip = VideoFileClip("Mojito.mp4").subclip(50,60)
# 做一个txt clip. 自定义样式,颜色.
txt_clip = TextClip("Mojito by Jay",fontsize=70,color='white',font=".苹方-简-常规体")
# 文本clip在屏幕正中显示持续10秒
txt_clip = txt_clip.set_pos((50,50)).set_duration(10)
# 把 text clip 的内容覆盖 video clip
video = CompositeVideoClip([clip, txt_clip])
# 把最后生成的视频导出到文件内
video.write_videofile("Mojito_edited.mp4") # 默认编解码器codec="libx264"
使用ImageClip
创建图片类(可以用set_pos
和set_duration
设置logo图片的位置和持续时间),使用CompositeVideoClip
将其与视频叠加在一起:
# 导入需要的库
from moviepy.editor import *
# 从本地载入视频myHolidays.mp4,并截取00:00:50 - 00:00:60部分
clip = VideoFileClip("Mojito.mp4").subclip(50,60)
logo = ImageClip("logo.png").resize(width=100,height=100).set_pos((50,50))..set_duration(10)
video = CompositeVideoClip([clip, logo])
# 把最后生成的视频导出到文件内
video.write_videofile("Mojito_edited.mp4") # 默认编解码器codec="libx264"
复合叠加音频
如果你有一些特殊的的定制合成音频的需求,应该使用 CompositeAudioClip
和 concatenate_audioclips
这俩类。
from moviepy.editor imports *
concat = concatenate_audioclips([clip1, clip2, clip3])
compo = CompositeAudioClip([
aclip1.volumex(1.2),
aclip2.set_start(5), # start at t=5s
aclip3.set_start(9)
])