搭建FFmpeg处理音视频

基于Java+FFmpeg搭建一个处理音视频的处理中台。

主要的架构设计分为两块内容。

image.png

一个对外提供服务的音视频处理任务中台,承接来自业务方的所有音视频处理需求,核心逻辑包括

  • 将音视频处理任务落库
  • 将音视频任务发送至消息队列
  • 消费来自消息队列的消息,将音视频处理结果更新到数据库
  • 通过回调任务,将结果回调给各个业务方

一个承接所有音视频任务的处理集群,核心逻辑包括

  • 消费来自消息队列的消息
  • 通过FFmpeg进行处理
  • 将处理好的文件上传至OSS服务
  • 将处理结果发送至消息队列

音视频处理任务中台

中台的考虑很简单,能承受住业务方的所有流量即可。内部需要的角色也较为简单。

分布式JOB

用于处理音视频回调通知,所有提交过来的任务都必须携带回调地址,比如Http接口。

分布式MQ

作为中台与逻辑处理集群的通信方式,最开始为了简单快捷起见,选择了使用Redis(lpush + bpop)作为消息中间件来处理,当下来看是一个错误的决定。后来统一完成了Kafka。

为何不能是Redis作为消息中间件

1、生产方与消费方的处理速率根本不再一个量级,当消费方处理速率比生产方低时,消息会大量积压,进而形成了一个Big key
2、Redis有丢失的风险,所以生产方需要做好一个合适的二次投递机制
3、缺少MQ部分功能,比如可靠的重试机制

音视频任务处理集群

该服务主要基于Java + FFmpeg来实现,因为业务特性的问题,FFmpeg是极其吃性能,特别是CPU,所以机器的性能会更好。

此处设计之初的目标是能快速的复制,当业务量突然进来时能通过加机器来抗住,所以节点必须是无状态,其次为了保证节点的稳定性,我们也考虑了进程内任务的一些排队处理方案。

思路

简单的描述一下当前的一些程序设计以及考量,并非是绝对的正确,因为很多方案选择的时候是需要考虑时间成本、人力成本的,只能慢慢通过演进的方式找到最佳实践。

为何要放在后端

如果客户端可以做,一定要放在客户端做。因为后端如果来处理这种大规模的音视频内容,所需要的成本太高了。放在客户端做,不管是从体验还是成本都是更优的方案。(但是也要考虑一些性能不是很好地机子去处理,防止程序卡死)

有兴趣的可以在本地的机子上体验一下FFmpeg,简直就是CPU猛兽。

部署

毫无疑问,必须是Docker。将FFmpeg的打包到镜像中,节点的复制是镜像级别的复制。纯jar包的形式手工运维很痛苦,加机器还要处理FFmpeg,包括下载,解压,配置环境变量一系列操作,即便写成脚本也是运维同学的一个心智负担。

Java CV

最开始我们并未注意到JavaCV,所以先通过Java Process+FFmpeg+Shell做了第一版功能的实现。

后来注意到Java CV,通过依赖POM的形式就能把FFmpeg集成到Java程序中,毫无疑问比起通过Java Process来调用命令行工具更让我们心动。

可是JavaCV的学习曲线看起来很陡,目前也没有找到比较合适的学习资料,传送门,虽然能够实现我们所需要的相关功能,但是唯恐后续的成本太高甚至有可能切会Process的方式,所以我们当下仅作为调研方案参考。

并行处理

我们在考虑要不要使用线程池来并行消费MQ。

维护要并行处理?因为要更好的利用CPU,但是熟知FFmepg的同学一定知道,它是非常吃CPU性能的,并且FFmpeg本身就做了并行处理的多线程技术方案,那还需要并行处理吗?

在CPU密集型的任务中,其实是不建议开启多线程,因为本身CPU就很忙了,多线程下多了一些CPU的上下文切换,反而造成了性能的损耗。

答案是要,但是要拆分来看。把整个任务处理分成多个段来看,首先是FFmpeg处理,处理成功后要把本地文件上传至OSS服务器上,然后发出MQ,最后将本地文件删除。

拆分后可以很好的将任务分成CPU密集型+IO密集型两段,所以我们可以把后续的IO密集型线程池化处理。

Process

想来这是服务端开发这个方向上,Process应该算是比较冷门的API。

  • waitFor(long timeout, TimeUnit unit) 而不是 waitFor(),小心因为子进程阻塞把父进程耗死。
  • getInputStream + getErrorStream + close,两个流的内容都打印,遇到问题好排查
  • waitFor timeout 后调用 destroyForcibly,小心子进程丢失在父进程的管控下
  • exitValue,0表示成功,其他表示失败,比如1表示主动destroy, 6表示process 被hangs, 137表示子进程被其他进程kill

CosClient 踩坑

在使用CosClient不当时,还遇到了连接池泄露的问题。

程序设计

我们的程序设计改了两三次。

第一次设计忙于调研+交付,没有足够的时间来做设计,也没有进行合理的抽象与规划,导致第二个需求进来时几乎涉及大量复制,代码重复明显,所以进行初步设计。第二次设计又因为业务交付时间紧急只做了一部分的抽象。第三次忍无可忍在周末重新做了一遍设计。

核心逻辑主要通过责任链来组织核心代码。

前文提到把任务处理分成多个段来看,把任务分成了主要的CPU密集型+IO密集型的方式,通过责任链可以把任务拆分成更多的节点,让程序的扩展性更好。

image.png

幂等

为啥要做幂等?主要考虑MQ重试,重复投递等。

令牌桶

为啥要做令牌桶?考虑到当下我们申请的服务器的核数申请的比较高,单个任务并没有把我们的CPU核数给彻底跑满,或者接近满,所以设计了一个小的线程池来提高并发(SynchronousQueue)。

令牌桶当下我们有2个选择,原生JDK中的Semaphore,以及自定义基于Redis LPush + LPop的命令实现的令牌桶。

最后选择基于Redis LPush + LPop 的方案。为何?性能并不是我们的第一指标,Semaphore不支持动态的调整令牌个数,想要实现动态的调整令牌个数需要自定义实现。而基于Redis的方案成本很低,通过配置中心或者暴露出来的运维接口就可以较好的动态调整(令牌减少需要跟业务线程竞争令牌)。

推荐动态的去调整线程池大小,而不是令牌桶方案,这也是我们下个迭代会考虑的方案,并且对我们而言改动很小。

CPU线程池

前文已经说过了,绝大多数的任务并没有把我们的CPU的核数给彻底跑满,甚至还有不少富余,所以适当的增加并发是有必要的。

IO线程池

毋庸置疑,IO线程池干的就是大量IO的操作,适当的增加线程池的大小

FFmpeg的线程数

尝试通过限制FFmpeg线程数+CPU线程池的方式来合理的提供并发,后来发现并无优势,而FFmpeg内部的并行方案没有看到相关的源代码并不好判断,所以现在的方案是去掉了这些参数,由FFmpeg自行动态的调整。

filter_threads
filter_complex_threads

转码效率

在很多时候,需要面临处理一些特别大的视频,为了提高效率,我们需要通过一些参数来优化,但同时可能会损耗原视频的质量,评估的核心还是损耗后的质量是否在可接受范围呢。

根据码率、分辨率等参数来判断视频的质量,如何获取码率等信息?建议使用ffprobe工具,能够提取到音视频很多信息并以Json的格式返回。

具体参数:preset,参考:https://trac.ffmpeg.org/wiki/Encode/H.264

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,843评论 6 502
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,538评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 163,187评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,264评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,289评论 6 390
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,231评论 1 299
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,116评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,945评论 0 275
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,367评论 1 313
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,581评论 2 333
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,754评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,458评论 5 344
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,068评论 3 327
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,692评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,842评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,797评论 2 369
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,654评论 2 354

推荐阅读更多精彩内容