背景

在实际业务中,视频处理,大都时候都挺费时的。例如:以下表格是,某中算法,在不同分辨率、帧率下的,cpu计算耗时体系(不包括:上传、下载的耗时):

分辨率 帧数 处理时长 / 视频时长
720p 30帧 2
720p 60帧 4
1080p 30帧 3
1080p 60帧 6
2k 30帧 3.5
2k 60帧 8
4k 30帧 7
4k 60帧 20

另外,一般情况下,视频越长,所占用的cpu or gpu,也就越久。如此有以下弊端:
1、对于用户而言,耗时久,体验非常不好,有可能直接放弃了。
2、很容易造成资源负载不均,例如:有些机器比较倒霉,每次分配的都是:长视频(5min以上),有些机器比较luck,分配的大都都是:短视频(30s以内)
3、超过1h的视频,单台物理机,基本上不太可能能够处理完毕,姑且不谈cpu,内存都有可能爆满。

视频处理

这里面的视频处理,可以是:libx264转码、libx265转码、超分、ai绘画、去除水印、人像增强、去模糊、去躁点等等。

思路梳理

资源扩容

例如:某一个算法,目前部署在:8核cpu上,可逐步调大cpu 核数 + 内存大小,当然了,资源也是有限的,扩大到一定核数后,并不能100%能解决问题,另外,成本也是一个大问题。

硬件升级

假设不差钱,可以考虑上:更强型号的cpu or 更强型号的gpu,例如:大型机 or GPU:RTX
4090,可惜,有时候,就算是上了最强的CPU,在面对:长视频 + 高帧率 + 高分辨率的情况下,也有力不从心的时候。

算法优化

深入算法处理的底层原理,分析处理过程,降低算法复杂度:

1、能够并行计算的,尽量并行计算,充分压榨cpu or gpu资源。让cpu利用率常态超过50%以上,而不是偶尔能达到,其实是挺有挑战的。

2、目前很多算法 or 模型,在计算视频时,是将视频解封装到帧,然后,逐帧计算处理,是否可以再次优化呢?

并行计算

除了单进程内部开启并行计算外,其实也可以考虑:单机多进程并行计算,集群多进行并行计算,具体而言:将:长视频、高帧率、高分辨率的视频切成多个小视频,然后,集群调度,并行计算各个小视频,最后将它们的结果合并起来,从而得到原先视频的计算结果,

其整体过程如下:
1、切片:分解(Divide)将一个大视频切成多个小视频。
2、并行:集群并行调度:处理这些片段。
3、合并:将这些片段的结果合并起来,以获得整体的结果。

注意:注意:能够进行以上计算的前提条件是:片段与片段之间,无相关性,或者,可以通过某种手段,将各个片段降维到无相关性。

为啥能加速?

能够加速的原因 重要程度 说明 备注
切 -> 并 -> 合,强依赖于:并行调度 top 0 单个视频的所有切片并行计算,子任务之间并发执行,尽量不要排队等待。。并发度 = 并发执行的任务数 / 总的任务数
GPU池子 与cpu池子的合理配比 top 0 1、如果cpu池子中核数不够多,例如:<= 10核,那么大概率起不到加速效果。
2、如果gpu池子的卡数不够多 or 调度太慢,例如:一个算法只能调度到一张卡,那么,也起不到加速效果
切片策略:合理设置各个阶段算法参数,确保处理后的结果正常 top 0 切片加速策略
开启切片的条件:视频时长 top 1 通用策略:如果视频时长 <= 1min,触发加速有可能会适得其反,费资源,又更慢(毕竟:切、合是有成本的)。
当然了,不同算法,这个阈值,有可能会不一样,需灵活对待。
不同分辨率 + 不同格式的视频等,是否有这一规律,目前待定,并未找到规律。

解决方案

如上所述,将重点探讨:切片加速的相关方案。大体过程,如下图所示:


备注:
1、上述过程要求:片与片之间,没有关联性,然后,并行处理,来提高处理速度 + 降低对硬件的需求。
2、在实际业务中,片与片之间,并非100%无关联,片段之间存在上下文的关联性。其解决思路之一:分析下关联性有什么特点,然后,将这个关联性降维到无关联性。例如:某一算法的加速流程如下图所示:

注意:以上整体流程,由:切 -> 并 -> 合,变成:切 -> 切 -> 合 -> 并 -> 合。

3、一般音频处理比较快,因此,切并合,只针对视频,不针对音频。因此,直接并行触发:音频转码。
4、如果需要在切片中处理:色偏 + 格式等问题,则需要开启切片转码,此时,建议:并行化,类似分片转码。
5、”处理”,需并行处理,这是能够提速的关键。

模块抽象

模块 功能定位 是否连db 是否上传 or 下载 并发度
总控集群 切片策略、算法编排、任务重试、任务取消、优先级调度等 高,单个pod基本上要达到300QPS以上(8核cpu,经验值)
计算集群 原子化cpu、gpu计算,负责:算 低,单个pod一般处理:2个以内的视频 or 5个以内的图片(经验值)

总控进程

功能 重要性 说明 备注
切片策略 top 0 描述一个视频该怎么切片,按帧切?还是按视频时长切?
算法编排 top 1 切片、音频提取、视频合并、音视合并等各个阶段编排在一起,整体流程
任务重试 top 0 理论上,该任务可以重试,重新触发计算,不影响结果
check bill top 1 任务卡死、节点卡死时,如何触发继续流转下去?如何通知业务方?

task、event、segment、trace

名词 解释 关系 说明 备注
task 任务 一次请求,对应一笔任务 一笔切片请求,对应一笔任务 可使用UUID生成唯一的:task_id
event 事件 task 与event 是一对多的关系 一个dag节点,对应一个事件 event_id = task_id + “_” + node_name
segment event 与 segment 是一对多的关系 一个视频片,对应一个event segment_id = event_id + “_” + order
trace 跟踪 trace 与 event、segment 是一对多的关系 一次cpu、gpu计算,对应一次trace 可使用UUID生成唯一的:trace_id

音频剥离

依赖算法:libfdk_aac or aac,将音频从原始视频中剥离开来,例如:ffmpeg命令如下:

1
ffmpeg -i source.mp4 -vn -y -acodec libfdk_aac -copyts output.aac -loglevel error

为啥要音频提取并且重新转码?
1、一般情况下,音频比较小,计算量比较小,可单独并行处理,因此,将音频流提取出来,能够:减少视频处理过程的IO + 减少:cpu、gpu计算量。
2、用户输入的视频,格式千变万化,很多视频无法直接通过:-acodec
copy。若直接将音频提取出来会发生:奇奇怪怪的错误。因此,统一将音频流转码,统一音频格式。另外,需:-copyts的保留原始音频的PTS信息。
3、有些视频天然没有音频,因此,在处理前,需判断原始视频是否有音频,若有,再触发:音频提取流程,否则:直接触发视频切片。

视频侦测:meta信息提取

1、显示“流”的全局属性:

- 显示的是流级信息,每个流(视频、音频、字幕等)只有一条记录。
- 提供整体的技术参数,例如编解码器、分辨率、码率、帧率、采样率等。
- 适用于快速查看媒体文件的基本属性和整体结构。
1
ffprobe -v quiet -print_format json -show_format -show_streams -loglevel error {path}

2、显示“帧”的全局信息

- 显示的是帧级信息,每一帧(视频帧、音频帧)都有详细记录。
- 包含帧的时间戳(如 pts_time)、帧类型(I、P、B 帧)、时长、关键帧标识等详细数据。
- 适用于需要分析视频细节(例如精确剪辑、帧检测等)时使用,因为它输出的数据量通常非常大。
1
ffprobe -select_streams v -show_frames -print_format json {path}

1、以上两条命令中的:{path}可以是本地路径,也可以是url,如果视频必须要下载到本地,则使用本地路径,否则:更建议使用url。
2、以上两条命令是对输入视频进行画像,以便决策:它是否能够进行切片加速?以及相关的切片策略?

指标 说明 备注
码率 码率太低 or 码率太高,需要设置不同的码率策略 可行
帧率 可行
分辨率 480p、540、720、1080、2k、4k
负数帧
部分帧的pts都是一模一样的
色域 当前视频的色域格式
编码格式 h264、h265、h266、avi等

视频切片

功能 说明 是否可行 业界是否有
按照gop关键帧切 输入视频时长,快速分割出一坨的视频文件 可行
按帧索引切 将原始视频的帧解封装,然后,使用libx264 or libx265重新压缩 可行 暂无
视频解码成图片 将原始视频里的帧解码成图片,然后,直接基于图片并行处理 暂不可行,图片太多,并发度太大,网络带宽扛不住 暂无

当然了,视频如何切,需要考虑:底层算法,
1、有些算法,需要预留一些buffer帧进行预热,才能确保切片后的视频画质,能够不输于:不切片时的结果视频画质。
这种buffer帧策略,分为:前向重叠帧策略、后置重叠帧策略、双向重叠帧策略、无重叠帧。
2、有些算法,例如:消除某段视频里的某个物体,片与片之间存在上下文的相关性,此时,切片时,要将该物体所对应的帧序列,单独切出来,然后,合并到各个片上去,将所有片降维到:无关联性。
3、切片时,最好不要保留原始视频的PTS,也就是:切片时,将所有片的PTS都重置成0。因为:多个步骤的编解码,无法100%保留原始视频的PTS值。
4、切片时,重叠的一些帧,在后续步骤中,需去除掉。

另外:1、按照视频时长切片,参考的ffmepg命令:

1
ffmpeg -i input.mp4 -c copy -map 0 -segment_time 10 -f segment output_%03d.mp4

假设需要实现:重叠帧 or 预热帧的功能,则

2、使用cv2:pip install opencv-python ffmpeg-python 按照帧索引切:参考代码如下:

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
def slice_and_combine_video(input_video_path, output_video_path, frame_start, frame_end):
video_capture = cv2.VideoCapture(input_video_path)
fps = int(video_capture.get(cv2.CAP_PROP_FPS))
width = int(video_capture.get(cv2.CAP_PROP_FRAME_WIDTH))
height = int(video_capture.get(cv2.CAP_PROP_FRAME_HEIGHT))
fourcc = cv2.VideoWriter_fourcc(*"H264")
video_writer = cv2.VideoWriter(output_video_path, fourcc, fps, (width, height))
success, frame = video_capture.read()
count = 0
while success:
if count >= frame_start and count <= frame_end:
video_writer.write(frame)

success, frame = video_capture.read()
count += 1
# 释放资源
video_capture.release()
video_writer.release()


if __name__ == "__main__":
input_video_path = ""
output_video_path = ""
frame_start = 10
frame_end = 50
slice_and_combine_video(input_video_path, output_video_path, frame_start, frame_end)

并行处理

算法 片与片之间 是否支持切片 说明 备注
libx264 不相关 支持
libx265 不相关 支持
去水印 不相关 支持
去字幕 不相关 支持
去某个物体 有点相关 可降维到不相关,支持
文生图 强相关 调研突破中
文生视频 强相关 调研突破中
图生视频 弱相关 支持

无论是哪种算法,都需要对输入视频进行解封装 + 重新编解码,因此,如果切片时有预留一些帧做buffer等操作,一般都在:并行处理中,去除这些buffer帧。
因此,底层算法,需要支持:remove_frames:[start_frames,end_frames]、[start_gop,end_gop]

视频合并

算法并行处理完毕后,能够得出一坨的算法处理后的视频片段,收集到这些片段后,将这些片段合并,即能得到一个完整的、处理后的视频。例如:参考ffmpeg命令:

1
ffmpeg -f concat -safe 0 -i {0} -i {1} -c copy -output_ts_offset {2} -movflags faststart {3} -loglevel error

注意:输入的各个片段的PTS,已重置过 + 算法重新编解码过,因此,需要在这边做:视频PTS对齐操作。否则,极容易导致:音画不同步的问题。

音视合并

最后一步,将音频流 + 视频流合并,使用如下:ffmepg命令,即可正常合并。

1
ffmpeg -i {0} -i {1} -copyts -c:v copy -c:a aac -map_metadata 0 -strict experimental -movflags faststart {2} -loglevel error

备注:
1、-c:v copy 本身不会主动增加零帧,但如果首帧 PTS 不为 0,FFmpeg 可能会调整时间戳或插入一个“零帧”以保证 PTS 归 0,尤其是在
MP4 等格式中。使用 -copyt -map_metadata 0s 选项可以保留原始时间戳,防止 PTS 归 0 造成的影响。
2、-map_metadata 0,请确保:{0}为视频,另外,也是为了将视频流的部分meta信息输出到结果视频中。

算法编排

如本文中切片加速
图片所示:整个过程,可以硬编代码写死,也可以使用dag引擎来编排,详细内容,可参考章节:任务编排
,这里不再赘述。

资源调度

包括:多云调度、cpu集群与gpu集群协调、自动扩缩容等等,这些将在章节:资源调度,重点描述,这里不再赘述。

cb体系

如本文中切片加速 包括的图片所示,由于中间各个步骤,包括了大量的:cpu or
gpu计算服务,比较容易导致:算法奔溃,卡死,导致总控进程长时间未收到回调,因此,需要总控进程触发check 任务状态,假设超过一定的时间阈值,重试
or 设置整个任务为失败。

监控指标

压缩比

压缩比 = 输出视频文件大小 / 输入视频的文件大小

成本

小文件存储成本

类别 说明 存储规格 生命周期 备注
原视频 存放在桶1 推荐:3天内,标准存储,超出:自动降档 按照业务场景定,例如:3天内,自动归档
结果视频 存放在桶2 推荐:21天内,标准存储,超出:自动清理 按照业务场景定,例如:21天,标准存储,之后自动清理
中间片段 存放在桶3 单副本即可 按照业务场景定,建议1天后,自动清理

备注:
1、强烈建议:原始视频 + 结果视频 + 中间片段,存放在3个桶里,或者:至少3个文件夹里,单独根据业务场景设置生命周期规则,例如:小文件存储提供的特性,自动清理
2、上述:推荐的存储规格,只是一种建议。真实落地时,得根据实际项目的运行情况来。

性能与成本均衡

经过实测,切片能够加速,能够加速:处理时长/视频时长 ≈ 0.5左右,但是,切片成本 / 不切片成本 = 1.1,视不同算法而定。
因此,是否所有视频都开启切片?答案:否定的,得根据实际情况来定,例如:超过1min的视频,才开启切片加速。

另外,cpu集群与gpu集群,可以考虑混部,

存储集群 + 计算集群,可以考虑:单元化部署,当然了,还是遵守:存算一体原则。

问题

图片处理是否需要切片并行?

图片处理,一般比较快,如果遇到慢的问题,可通过:扩容资源的方式来处理,暂时无法进行:切片并行的策略。

当然了,图片分块后,块与块之间,都有相关性,暂时无法做到:并行化。

色偏

切片与并行算法处理,均需要考虑:色偏问题,重点是:做好色域映射。

音轨、视轨无法对齐

视频PTS对齐 + 音频PTS对齐