WebAssembly视频检测在社区创作平台的落地与实践 | 得物技术
一、背景&现状
创作者服务平台作为得物为社区创作者提供的PC端视频发布入口,地位非常重要。且随着功能的升级迭代,用户群体也越来越多。但我们偶尔会收到如下反馈:
- 视频损坏,无法播放
- 视频模糊
- 曝光度问题
- 黑屏,只有声音,没有画面
黑屏,无法播放
低清晰度
曝光异常
黑屏,只有声音
视频的损坏不仅影响用户体验,还可能导致忠诚用户的流失。用户在浏览时看到错误反馈或者无法播放的视频,容易产生挫败感。
第二,流量的上涨导致此类case越来越多,据统计,自2024年4月份开始,通过创作者平台发布的视频可分发视频量较之前上涨多倍。
为提升视频发布的质量和用户体验,视频发布前的检测能力需尽快落地。
二、业界的做法
在视频内容平台兴盛的今天,视频上传和检测方面已经有了一些有力措施,以确保用户上传的视频质量,最大程度地减少损坏视频对用户体验和平台形象的影响,比如服务端检测,创作者在上传完视频后,会立即触发服务端检测功能,经过等待后会反馈给用户信息,不强卡发布。
这一方案是可行的,但也存在些弊端:
- 需要视频上传完成才能拿到完整的文件流进行检测,通常在PC上传的视频文件很大,要等到传完作者才能知道自己的视频有问题,然后再上传,再检测
- 帖子发布时间拉长,增加创作者的等待,从心智上会影响创作者的体验
- 带宽成本
但这一方案有个优秀的点,如对涉黄、涉恐等元素的视频,能同时被检测到并且禁止分发。
三、得物音视频团队的方案
目前得物音视频团队在上传前的预检测这种场景下已有了一套较为完善的方案,那就是使用C+ffmpeg编写好检测代码后再通过Emscripten工具将其打包成WebAssembly的二进制文件使代码运行在web端。
目前这个方案的核心检测能力已应用在得物App发布工具场景使用,其能通过ffmpeg解析视频的元数据,获取其基本信息,如视频尺寸,码率等,能查找目标视频下的视频流,对音频和视频的AVPacket进行验证,检测文件是否损坏,时间轴是否存在异常等等。正常的流程如下:
经过此过程的检测,我们可以排除绝大多数文件格式存在问题的视频文件,下面列出一些常见的文件结构存在问题或者格式不合规的视频:
1.文件的moov不存在
2.视频帧的NAL结构异常
3.没有视频轨道
除了可以检测视频文件是否存在问题之外,我们还可以通过预检测获取大量的视频相关的信息:
1.视频的基本信息
- 宽高、帧率(是否是动态帧率)、码率
- 旋转角度
2.色域信息
- 是否是HDR、DP3色域
- 是8bit/10bit/12bit/16bit
3.视频编码附加信息
- 当前的视频是否是从其他平台上搬运而来的?
- 当前的视频是否使用其他的剪辑工具导出的?
- 我们可以通过识别视频中的metadata中的信息来分析当前的视频来自哪些平台的:
来自抖音
来自微信
来自快手
综上所述,我们在上传前预检测阶段,可以得到视频的很多信息 + 检测视频是否存在结构问题和格式问题。
四、我们需要解决的问题
虽然整体链路方案非常完善,但是SDK实际落地到web端运行还存在一些问题。
内存泄漏
我们先做个压力测试,在不刷新页面的情况,看下网页端内存如何变化:
1.准备32个50MB以内的小视频,1个800MB的大视频,内存采样为每3s采样一次
2.通过一个个上传,内存占用一直在上升,当传入800MB视频进行检测时,内存占用直接飙升至3G,未被正确释放
旧版SDK压力测试内存占用情况
此时控制台报错,内存溢出,页面卡死,用户必须刷新页面才可继续操作。
控制台内存溢出报错
ArrayBuffer异常占用
大视频无法检测
传入1.9GB的视频文件,控制台直接报错,无法申请1.9GB的内存。
大文件传入报错
检测速度慢
一步步通过裁剪,缩小视频大小,800MB时视频传入检测成功,耗时94630ms。
五、内存优化方案
针对以上问题,我们一个个来看:
内存溢出:先了解下现有代码中内存是如何被分配和销毁的,下面是部分核心代码。
IO核心代码
大致的流程如下:
通过流程图可见内存已经被正确的申请和释放了,但是实际表现确是申请的内存一直在被占用,所以可能不是代码逻辑问题,在翻阅了wasm官方设计文档时发现一个issue,其中提到了wasm内存设计方案存在几个问题(该issue还处于open状态):
wasm内存设计问题
所以正是由于第2、3点导致了wasm占用的内存只能被扩大,而无法通过释放被缩小,一旦过多的使用malloc内存占用达到wasm的最大内存限制后,申请必将失败,后续的链路也就无法继续了。那么排除其他因素,通过代码验证一下吧,我们只需要将extract_video_data函数修改一下,直接释放掉接收的内存指针指向的内存块:
修改代码片段
依旧还是之前的物料进行压力测试,可见内存整体增长情况态势与之前别无二致,所以基本可以确认,内存一直占用的问题是wasm底层Memory的设计导致的,所以malloc函数我们肯定是用不了了。
验证内存采集
大视频无法检测:通过分析"IO核心代码"一图可以看到,视频文件是转成ArrayBuffer后通过forEach把一个个的字节塞入了提前申请好的内存中来实现数据传递的,这种方案存在两个问题:
-
效率低,不考虑文件转ArrayBuffer的时间,光遍历动不动就上百兆量级的buffer需要的时间都是巨量的,经过测试800MB文件想要全部转化为Uint8ClampedArray,然后写入到wasm内存中耗时大约在14秒左右,这就是导致检测速度慢的一个原因。
-
ArrayBuffer的size是存在最大限制的,以chrome为例,这个限制是2GB,导致在js侧检测视频理论大小限制为了2GB,wasm侧最大可申请内存也存在限制,大约在1.6GB左右。在使用malloc申请到内存空间后又将其传入avio_alloc_context,avio_alloc_context内部将再次申请buffer_size大小的内存空间进行数据缓存, 从而导致实际检测视频最大为800MB。
所以ArrayBuffer这种方案是不能使用的,天然存在限制。那么到此问题原因都找到了,归根结底都出在文件的IO上,那么是否可以换一种思路,如果不需要任何的数据格式的转换,使wasm环境下的ffmpeg直接读取到文件这样肯定是最省时且高效的。那么ffmpeg能直接读取文件吗,答案是肯定的。
avformat_open_input可以传入文件路径并能将AVFormatContext自动分配并写入ps。意思是只要能拿到目标文件的文件路径,就能直接调用avformat_open_input读取文件到IOContext,省去了将数据手动塞入IOContext的逻辑,也就意味着能绕过多余的内存的申请和释放,且不再需要进行数据格式的转换,可是怎样才能拿到目标文件路径呢?
因为SDK的运行环境是在web端,web端想要访问本地文件只能通过file类型的input拿到文件对象,那传入blobUrl行不行呢,经过测试,传入blobUrl会报Permission denied 无权限的错误,因为wasm的文件系统有自己的特殊实现,其并不能与JavaScript 直接进行交互,读取文件需要通过nodefs.js,idbfs.js,workerfs.js,proxyfs.js这几个js分别构造的虚拟文件系统才行,其分别是:
- NODEFS,在node环境中使用的文件系统
- IDBFS,在web浏览器中可使用的文件系统,强依赖IndexedDB
- WORKERFS,工作在web浏览器,且只能运行在webWorker中的文件系统
- PROXYFS,允许通过代理的方式访问本地文件系统或远程文件系统,主要用于将文件操作传递到JavaScript中的其他实现。这种机制使得WebAssembly模块能够与JavaScript代码进行交互,进而访问不同的文件系统或数据源
由此看来想要提供给avformat_open_input目标文件的路径,我们还需将目标文件挂载到一个虚拟文件系统中。那么该如何选择呢?
因为我们的SDK是需要运行在web浏览器中,那么NODEEFS首先就被排除掉了,其次视频的读取检测属于计算密集型任务,是需要运行在webWorker中的,所以WORKERFS与我们的使用场景更加契合,他提供对webWorker中的file和Blob对象的只读访问,而无需将整个数据复制到内存中,非常适合对大型文件的读取,也满足了我们对于快速读取和内存占用少的要求,简直完美。
那么说干就干,webWorker + WORKERFS的方案需要对打包命令和代码进行改造。首先启用WORKERFS需要在wasm的打包命令中添加-l workerfs.js参数,并且导出运行时函数WORKERFS,完整命令如下:
emcc -O3 \ -I ${FFMPEG_DIST_DIR}/include \ -L ${FFMPEG_DIST_DIR}/lib -l avcodec -l avformat -l swresample -l avutil -l workerfs.js\ -I ${CJSON_SOURCE_DIR} \ -s EXPORTED_FUNCTIONS="['_get_video_info', '_extract_video_data']" \ -s WASM=1 \ -s ALLOW_MEMORY_GROWTH=1 \ -s EXPORTED_RUNTIME_METHODS=[\"wasmMemory\", \"FS\", \"WORKERFS\", \"ccall\"]" -fsanitize=address \ -o ${workspaceFolder}/sociality/main.js ${workspaceFolder}/sociality/main.c \ ${CJSON_SOURCE_DIR}/cJSON.c ${CJSON_SOURCE_DIR}/cJSON_Utils.c
如果导出成功就能在wasm模块中看到WORKERFS的实例:
导出成功后想要使用的话只需要在webworker中创建任意文件夹,将目标文件通过mount方法挂载到该文件夹上就行,直接上代码:
WORKERFS在Webworker中的使用
然后修改C语言侧extract_video_data方法:
文件就能正确地被读取和处理了。可见这个方案非常的简洁且省去了巨量的IO操作,效率提升了,但是内存占用问题还存不存在呢,再次跑个压力测试试一试:
对比老SDK内存占用情况
用同样的视频物料进行压力测试,得出的内存占用情况如图,可见优化后内存使用在压力测试后一直维持在900MB左右,且继续传入大视频文件不会继续上涨,判断为正常内存占用(绿色线条),检测速度也做了一个粗略的统计,与旧版SDK对比,性能方面,以800MB文件为例,检测时长分别为20s和95s,性能预计提升约78%;2GB视频文件检测时长为61s,对于更大的视频也能轻松应对。至此所有的问题都已解决。目前该功能已上线:得物创作者平台。
六、总结
通过WebAssembly技术的引入与整合,我们在视频损坏检测上迎来了新的机遇。在逐步解决技术挑战的过程中,完善了我们的视频上传流程,并提升了用户体验。展望未来,我们希望继续优化这些功能,确保用户能够在平台上无障碍地上传和分享他们的创作,进一步提升社区的活跃度和用户粘性。
文 / 佳庆&ALBERT
关注得物技术,每周更新技术干货
要是觉得文章对你有帮助的话,欢迎评论转发点赞~
未经得物技术许可严禁转载,否则依法追究法律责任。

低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
架构思考与实践:从通用到场景的转变
在当今复杂多变的商业环境中,企业架构的设计与优化成为了一个关键议题。本文通过一系列随笔,探讨了业务架构的价值、从通用架构到场景架构的转变、恰如其分的架构设计以及如何避免盲目低效等问题。通过对多个实际案例的分析,笔者揭示了架构设计不仅仅是技术问题,更是对企业现状和未来发展的深度理解与把握。本文适合希望深入了解业务架构及其实践意义的读者阅读。 价值理解:(业务)架构的价值 当我们需要了解一个人时,需要“察其言观其行”,不过也难免“误判”。企业的发展与管理,最重要的还是要了解企业自身,知道在做哪些事情,有哪些能力。如果企业都不知道自身的能力和发展情况,那么管理层就容易“纸上谈兵”、“朝令夕改”,或选择“无为而治”。 因为不了解一线情况,就会提出不合时宜、不够长期的个人主张,下面的人也会“晕头转向”,企业长期也可能“原地踏步”。 企业黑盒:盲目,低效 业务架构其实是为了“塑造一面镜子”,让企业看清自己。下面是本人之前梳理的一个整体逻辑图。从顶层的商业模式,到中间的业务能力、业务流程,再到具体的系统实现与资源消耗,是一个抽象层面的认知结构。 业务架构大图 说白了,我们需要一个能够承上启下的模型,...
- 下一篇
Databend x 沉浸式翻译 | 基于 Databend Cloud 构建高效低成本的业务数据分析体系
「沉浸式翻译」是一个非常流行的双语对照网页翻译扩展工具,用户可以用它来即时翻译外文网页、PDF 文档、ePub 电子书、字幕等。它不仅可以实现原文加译文实时双语对照显示,还支持 Google、OpenAI、DeepL、微软、Gemini、Claude 等数十家翻译平台服务的自定义设置,在网络上好评如潮。 随着用户量持续增长,其运营和产品团队希望在尊重用户隐私的前提下,通过业务数据为业务增长研究提供决策依据。 业务挑战 业务数据埋点指标是数据仓库中不可或缺的重要数据源之一,同时也是企业最宝贵的资产之一。通常情况下,业务数据分析包含两大数据源:业务数据分析日志和上游关系型数据库(如 MySQL)。基于这些数据,企业可以进行用户增长分析、业务数据研究,甚至通过业务数据分析精准排查用户问题。 业务数据分析的特点决定了要构建一套可扩展、灵活且低成本的分析架构并非易事,具体表现在以下几个方面: 高流量和大容量:业务数据的产生量非常大,对存储和分析能力要求高; 兼顾多种分析需求:既需支持 BI 报表的静态展示,也需满足灵活的 Adhoc 查询; 多样化****数据格式:业务数据通常包含结构化数据与半...
相关文章
文章评论
共有0条评论来说两句吧...
文章二维码
点击排行
推荐阅读
最新文章
- CentOS关闭SELinux安全模块
- SpringBoot2更换Tomcat为Jetty,小型站点的福音
- CentOS6,CentOS7官方镜像安装Oracle11G
- SpringBoot2编写第一个Controller,响应你的http请求并返回结果
- Red5直播服务器,属于Java语言的直播服务器
- CentOS7编译安装Cmake3.16.3,解决mysql等软件编译问题
- SpringBoot2整合MyBatis,连接MySql数据库做增删改查操作
- SpringBoot2整合Thymeleaf,官方推荐html解决方案
- CentOS8安装Docker,最新的服务器搭配容器使用
- CentOS7,CentOS8安装Elasticsearch6.8.6