背景

在实际业务中,视频处理,大都时候都挺费时的。例如:以下表格是,某中算法,在不同分辨率、帧率下的,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绘画、水印去除、人像增强、去模糊、去躁点等等。

为啥只谈视频切片?

切片并行加速,是否支持以下2个场景?

1、文生视频,可否将:文本切成多段,然后,每段并发分别生成视频,最终合成一个大视频?
2、图片切片加速处理,可否将图片按块切片,然后,并发处理每一块,最终合成一个大图片?

根据实际项目上看,暂时不行,尚需深入调研分析。
1、文生视频中的:文本是有上下文相关性的,暂时无法做到:将文本降维,变成:上下文无关性。
2、图片处理,目前性能不慢,暂时没有切片加速的需求,另外,图片按块来切,貌似也是有上下文相关性。

思路梳理

资源扩容

例如:某一个算法,目前部署在:8核cpu上,假设不差钱,或者:太紧急,就继续往上调整:16核 -> 32核 -> 64核 -> 128核等等
当然了,资源也是有限的,有时候,并非每次都能通过扩容来解决。

最强硬件技术

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

并行计算策略

遇到长视频、高帧率、高分辨率的视频,处理慢时,可以考虑将视频切成多个小视频,然后,并行地处理:小视频,最后将它们的结果合并起来,从而得到原先视频的计算结果,典型的并行算法计算的策略。
1、切片:分解(Divide)将一个大视频切成多个小视频。
2、并行:并行处理这些片段。
3、合并:将这些片段的结果合并起来,以获得整体的结果

解决方案

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

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

其整体流程就会由:切 -> 并 -> 合,变成:切 -> 切 -> 合 -> 并 -> 合。
3、提取音频时,直接做音频转码,也属于:旁路的并行流程。
4、切片 + 处理,需并行处理,是能够提速的关键。

模块抽象

模块 功能定位 是否连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 output.aac -loglevel error

为啥要音频提取并且重新转码?
1、一般算法处理,都是针对视频流,很少有针对音频流的,而处理慢,主要是处理视频慢,非处理音频慢,因此,将音频流提取出来,能够:减少视频处理过程的IO +
减少:cpu、gpu计算量。
2、用户输入的视频,格式千变万化,有些视频无法直接通过:-acodec copy,直接将音频提取出来,会奇奇怪怪的错误。因此,统一将音频流转码,统一音频格式。
3、有些视频天然没有音频,因此,在处理前,需判断原始视频是否有音频,若有,再触发:音频提取流程,否则:直接触发视频切片。

视频切片

功能 说明 备注
按视频时长切 按GOP关键帧快速切
按帧索引切 将原始视频的帧解封装,然后,使用libx264 or libx265重新压缩 暂无

当然了,视频如何切,需要考虑:底层算法,
1、有些算法,需要预留一些buffer帧进行预热,才能确保切片后的视频画质,能够不输于:不切片时的结果视频画质。
这种buffer帧策略,分为:前向重叠帧策略、后置重叠帧策略、双向重叠帧策略。
2、有些算法,例如:消除某段视频里的某个物体,片与片之间存在上下文的相关性,此时,切片时,要将该物体所对应的帧序列,单独切出来,然后,合并到各个片上去,将所有片降维到:无关联性。
3、切片时,最好不要保留:PTS(后续基本用不上),也就是:切片时,将所有片的PTS都重置成0。
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]

视频合并

算法并行处理完毕后,能够得出一坨的算法处理后的视频片段,收集到这些片段后,将这些片段合并,即能得到一个完整的、处理后的视频。例如:参考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} -c:v copy -c:a copy -strict experimental -movflags faststart {2} -loglevel error

算法编排

如本文中切片加速
图片所示:整个过程,可以硬编代码写死,也可以使用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对齐