关于转码:AI-时代的视频云转码移动端化更快更好更低更广

编者按: AI技术的落地是慢慢地从服务器端、云端落地,逐渐到挪动端及边缘设施上。这些年随着AI技术的提高,轻量级算法模型开始在挪动端实时跑起来,并且挪动端算法也在一直进行迭代和欠缺,而对于实时直播场景,也有越来越多的AI算法落地。ZEGO即构科技提出了一套极轻量级AI算法模型,联合挪动端硬件个性,差异化优化前馈推理库,让算法模型、推理库、硬件成为一体,使得视频云转码挪动端化成为可能。LiveVideoStackCon 2022音视频技术大会上海站邀请到了即构科技的李凯老师,为咱们分享产品架构、挪动端视频转码、挪动端智能视频解决、四位一体网络模型设计以及具体实施Demo。 文/李凯 整顿/LiveVideoStack 这是我第二次在LiveVideoStackCon分享,第一次时自己肚子还没有这么大,疫情三年肚子长大了,但咱们的模型会变得越来越小、越来越快、成果越来越好,这就是“更快、更好、更低、更广”。 快 —— 速度越来越快,好 —— 成果越来越好,低 —— 消耗很低的码率,广 —— 尽可能笼罩更多的机型。已经在云端转码遇到大批量的工作,本次探讨在挪动端如何解决,分享内容更偏重在挪动端如何把工作疾速跑起来,把服务器端的事件放在手机端做,对 ToB 厂商的用户来说是十分开心的。 本次分享按以下方向逐个开展:一是 ZEGO 产品概貌;二是要在挪动端去做的理由,这是帮忙解决客户提出的需要问题;三是在挪动端能够做到哪种水平,能不能面对用户提出的刻薄需要,解决客户问题;四是如何解决行业内难题,这是任何一个做音视频厂商、做视频前后解决都手不释卷谋求的问题;五是简略试验后果分享。 1、产品架构 产品利用方面,会议端的小艺帮次要针对考试类的利用;直播类有游戏直播,KTV唱歌等;计划有社交娱乐、泛娱乐、以及这几年很火的在线教育。目前也会给小艺帮考试类的做利用,不限于在线教育的计划。咱们同时会提供根底技术服务,比方视频转码、画质加强、美颜特效、Avatar 元宇宙底层技术、背景宰割等。  1.1视频解决 本次分享的重点是视频解决。上图是咱们很直观的数据。 在 iPhoneXR 这样的中端机型上,超分辨率 640480 两倍率做 AI 超分到 1280960,21ms 能够搞定。同时也测试了安卓机型,大略能够笼罩 2500 款,1000 元以上机型根本可能笼罩。如果机型好一点,超分到 1080p 没有问题,但国家政策激励出海去东南亚、印度、非洲。这些国家市场十分大,但最大的现状为使用者大多为千元机以下,这就面临一个很大的难题,千元机难做超分。 去编码速度比超分速度快很多,超分人造的数据量很大,不能把图片放大,将图片原始分辨率不做改变的状况下放大。去编码效应的劣势在原始分辨率上做,处理速度比超分快很多,根本可笼罩千元机。而在印度、东南亚、非洲这些带宽很低的中央,这时做去编码效应比超分,更具理论价值。 最难的点是视频插帧,两帧插一帧,根底数据是在 IPhoneXR 上 960*540,32ms,这一部分仍然面临难笼罩千元机,也是咱们始终去谋求的点,后续能够演示插帧在挪动端上手机具体成果。 随后也会与大家分享如何实现低照度加强,大家也能够跟着这个思路复现低照度加强,速度能够笼罩到 2ms,很低端的机型例如 2015 年的机型小米2S,2ms 能够实现。情理很简略,就是 3D或 2D 查找表,十分快,成果在最初一部分演示。但低照度会有个问题,这也是业界难题,在极度暗的状况下呈现噪点放大。那咱们为什么走低照度加强? 给大家讲个小故事,咱们有一个东南亚客户之前提出需要,“须要 1280720 的分辨率,要求 30 帧率不能降落、灯全关掉、手机屏幕亮度调到最低,但对方能够清晰看到我的脸”。在与客户沟通时,问能不能把屏幕亮度调亮一点点,对方不乐意,他就须要这个成果,但咱们致力帮忙其解决问题他还是很开心的,尽管不能否定会给他带来噪点问题,但亮度调高一点点,噪点问题将被适度放大。如果应用长曝光 1280720,帧率很低,长曝光只能 10 帧,用户做很多动作想要 30 帧,也会不开心。 1.2其余AI技术产品 技术人员要常常与销售打交道,面对一线用户,技术人员就能理解是否要解决此问题,晓得需要在哪儿,做的技术有无价值。当然咱们也会做其余的利用。一是抠图,其中有隐衷反向打码,例如小米摄像头监控性能,老人在家监控但不释怀,就能够把老人反向打码,将老人虚化而背景不虚化,或只虚化老人身材的一部分,反向抠图,要害看你怎么去做去用。二是小指标检测,已经有用户给一个条件:小分辨率在 640*360,只有 10 像素的手机,考场监控场景下,看拿手机舞弊的小动作是否被检测出。这样的难题是理论中碰到过的很多根底利用。 ...

September 13, 2022 · 1 min · jiezi

commonjs-ES-module-babel转码-webpack转码

js模块发展历程-javaScript模块七日谈前端模块化开发那点历史 #588现代ES模块也需要各种转码工具才可以在浏览器里正常运行,下面是转码现代ES模块需要了解到的知识点 commonjs & ES module & babel转码 & webpack转码 CommonJS简述CommonJS 模块输出的是一个值的 拷贝,也就是说,一旦输出一个值,模块内部的变化就影响不到这个值了如果输出的是对象,改变其属性的话,外部引用的地方是会发生变化的如果直接改变输出的引用,那外界引用的地方是不会变化的(取缓存里面的结果)CommonJS 加载的是一个对象(即 module.exports 属性),该对象只有在脚本运行完才会生成commonjs 一个模块就是一个文件,require 命令第一次执行加载该脚本就会执行整个脚本,然后在内存中生成一个对象{ id: '...', // 模块名 exports: {}, // 真实的模块 loaded: true // 是否加载完毕}以后再次 require 该模块时,就会去缓存里取该对象的 exports 的属性无论 require 多少次,模块都只会运行一次,后续加载都是从缓存里面取module.exports 与 exports 的关系commonjs 规范仅仅定义了 exportsmodule.exports 是 nodejs 对 commonjs 规范的实现我们把这种实现称为 commonjs2https://github.com/webpack/webpack/issues/1114#issuecomment-105509929exports 只是在初始化对 module.exports 的引用初始化指向同一片内存空间模块导出的是 module.exports 如果对 module.exports 重新赋值,exports 上,挂的方法/属性将会失效require 引入的是 module.exports 导出的东西为避免混乱/错误,一般导出模块只建议用 module.exports 一般第三方包都用这种方式导出 modules.exports = exports = {}循环引用问题 (某个模块出现循环加载,就只输出已经执行的部分,还未执行的部分不会输出)// 代码如下// a.jsexports.A = '我是a模块';var b = require('./b.js');console.log('在 a.js 之中, 输出的 b模块==> ', b.B);exports.A = '我是后期修改过的a模块';console.log('a.js 执行完毕');// b.jsexports.B = '我是b模块';var a = require('./a.js');console.log('在 b.js 之中,输出a模块 ==>', a.A);exports.B = '我是修改后的b模块';console.log('b.js 执行完毕');// main.jsvar a = require('./a.js');var b = require('./b.js');console.log('在 main.js 之中,输出的 a模块=%j, b模块=%j', a.A, b.B);// 输出结果如下:➜ webpack-plugin git:(master) ✗ node src/babel/index 在 b.js 之中,输出a模块 ==> 我是a模块b.js 执行完毕在 a.js 之中, 输出的 b模块==> 我是修改后的b模块a.js 执行完毕在 main.js 之中,输出的 a模块="我是后期修改过的a模块", b模块="我是修改后的b模块"// 执行过程如下:执行 a.js 遇到 require b.js,暂停 a.js 执行,去执行 b.jsb.js 执行到第二行,遇到 require a.js ,从缓存中拿出刚刚 a.js 导出的模块,在 b.js 里面使用继续执行 b.js 后面的代码待 b.js 执行完毕后,控制权交还 a.js,继续执行拿到 b.js 导出的模块,在 a.js 继续使用 ... 直到结束 循环引用注意点:由于 commonjs 模块遇到循环加载时,返回的是当前已经执行的部分的值,而不是全部代码之后的值,两者可能会有差异,所以输入变量的时候必须非常小心,使用 var a = require('a') 而不是 var a = require('a').fooES6 Module基本使用export default A // 用户不需要知道导出模块的变量名import a from 'a.js'// 可以导出多个export var a = 1 // 这种方式可以直接导出一个表达式或var a = 1export {a} // 必须用花括号包起来import {a} from 'a.js'// as 关键字重命名模块export { a as A }// 导入导出合并export { default as Comps } from '../xxx'相当于import Comps from './xx'export { Comps }// 执行 loadsh 模块,但并不输出任何值import 'lodash';// 整体加载所有模块,访问时用 circle.xxx 访问import * as circle from './circle';简述: ES6 模块不是对象,而是通过export命令显式指定输出的代码,再通过import命令输入,它的接口只是一种静态定义,在代码静态解析阶段就会生成。// ES6模块import { stat, exists, readFile } from 'fs';上面代码的实质是从fs模块加载 3 个方法,其他方法不加载。这种加载称为“编译时加载”或者静态加载,即 ES6 可以在编译时就完成模块加载,效率要比 CommonJS 模块的加载方式高。当然,这也导致了没法引用 ES6 模块本身,因为它不是对象。由于 ES6 模块是编译时加载,使得静态分析成为可能import命令具有提升效果,会提升到整个模块的头部,首先执行import命令是编译阶段执行的,在代码运行之前。由于 import 是静态执行,所以不能使用表达式和变量(这类只有在运行时才能得到结果的语法结构)静态加载模块的好处:1. 不再需要UMD模块2. 浏览器API可以用模块格式提供,不必再做成全局变量,不再需要全局对象如:Math (可以像Python一样用模块导入)动态 import动态import() 是非常有用的。而静态型的 import 是初始化加载依赖项的最优选择,使用静态 import 更容易从代码静态分析工具和 tree shaking 中受益import(模块路径) 返回 promise,从 then 的结果里拿到加载的模块webpack 2.x 之后,有一个魔力注释的功能,会把加载的模块重命名为你注释里的文字ES6模块的浏览器加载传统方法加载js脚本script type="application/javascript"异步加载: async defer脚本异步加载,不会阻塞dom结构的解析async:加载完立即执行,渲染引擎中断,待之脚本执行完继续渲染defer:加载完会等待页面渲染完毕及页面其他脚本执行完毕才会执行多个 async 执行没有顺序保证,多个 defer 有顺序保证 es6 模块加载script type="module"浏览器对 type="module" 的处理和 defer 标志一致es6 模块的循环加载ES6 处理“循环加载”与 CommonJS 有本质的不同。ES6 模块是动态引用,如果使用import从一个模块加载变量(即import foo from 'foo'),那些变量不会被缓存,而是成为一个指向被加载模块的引用,需要开发者自己保证,真正取值的时候能够取到值。es6 模块会在使用使用时才去加载对应的模块如果是循环应用,可以将对应的输出改写成函数形式,利用函数的变量提升功能CommonJS 与 ES Module 的对比// 此处是对比CommonJS 模块时运行时加载 -- 值得拷贝ES6模块时 编译时 输出接口 -- 值得引用commonjs 模块只会加载一次,以后在 碰到 require 同样的东西就从缓存里面加载如果把原模块导出的东西改变,引入模块不会跟着改变,还是从缓存里面取原来的值ES6模块的运行机制与CommonJS不一样,它遇到模块加载命令import时,不会去执行模块,而是只生成一个动态的只读引用。等到真的需要用到时,再到模块里面去取值JS 引擎对脚本静态分析的时候,遇到模块加载命令import,就会生成一个只读引用。等到脚本真正执行时,再根据这个只读引用,到被加载的那个模块里面去取值。换句话说,ES6的输入有点像Unix系统的“符号连接”,原始值变了,import输入的值也会跟着变。ES6 模块是动态引用,并且不会缓存值,模块里面的变量绑定其所在的模块。commonjs: module.exports = {} exports 运行阶段才加载模块,可以使用逻辑语句 模块就是对象加载的就是该对象 加载的是整个模块即将所有的接口都加载进来 输出的是值得拷贝,原模块发生变化不会影响已经加载的 this 指向当前的模块es6 模块 export 可以输出多个 {} export default 解析阶段确定对外的接口,解析阶段输出接口,不可以使用逻辑语句 加载的模块不是对象 可以单独加载其中的几个模块 静态分析,动态引用输出的是值得引用,原模块变化会影响已加载的模块 this 指向 underfinedBabel 转换 ES6 的模块化语法Babel 对 ES6 模块转码就是转换成 CommonJS 规范模块输出语法转换Babel 对于模块输出的转换,就是把所有输出都赋值到 exports 对象的属性上,并加上 ESModule: true 的标识表示这个模块是由 ESModule 转换来的 CommonJS 输出对于解构赋值输入import {a} from './a.js'转义为var _c = require('./a.js')然后取 _c.a 对于 defaultimport a from './a'import {default as a} from './a'babel转义时的处理,引入了一个 函数function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : {'default': obj}}var _a = _interopRequireDefault(require("./a.js"));console.log(_a["default"]);// 意思就是如果不是 esmodule 就为其手动添加个 default 属性,取值时统一取 default有个疑问:babel 为什么 会把 export export.default 导出的模块转换为 exports.xxx 和 exports.default 呢?而不是 module.exports ???我没有找到解释,如果您知道,麻烦给我留言下webpack 对 es6 模块和commonjs 的处理webpack本身维护了一套模块系统,这套系统兼容所有历史进程下的前端规范写一个简单的webpack配置module.exports = { entry: "./index.js", output: { path: path.resolve(__dirname, "dist"), filename: "[name].[contenthash:8].js" }, mode: "development"};执行打包命令 webpack --config webpack.config.js --env=dev 输出 main.[hash].js// 打包后代码简化如下// 首先是一个 webpack 模块运行时代码(function(modules) { // webpackBootstrap // 缓存模块 var installedModules = {}; // 函数 __webpack_require__ 参数 模块 id,用于加载和缓存模块 function __webpack_require__(moduleId) { // Check if module is in cache if(installedModules[moduleId]) { return installedModules[moduleId].exports; } // Create a new module (and put it into the cache) var module = installedModules[moduleId] = { i: moduleId, l: false, exports: {} }; // Execute the module function modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); // Flag the module as loaded module.l = true; // Return the exports of the module return module.exports; } /*** 所有加载的模块都存在于 installedModules 内,其结构为: id: { id, loaded: Boolean // 是否加载过 exports // 模块的导出 } */ // 省略... 定义各种工具函数和变量 // Load entry module and return exports // 加载 entry 模块,并返回其导出,我们写的模块才会被真正执行 return __webpack_require__(__webpack_require__.s = "./index.js");})({ "./index.js": (function(module, __webpack_exports__, __webpack_require__) { // ... }, "./src/a.js": (function(module, __webpack_exports__, __webpack_require__) { // ... }, // ...})这个自调用的函数的参数 modules,就是包含所有待加载模块的一个对象{ [id: string]: Function}异步加载: import ==> webpack.requireEnsure ==> webpackJsonphttps://www.njleonzhang.com/2018/12/30/webpack-bundle-1.html其他常见问题1. babel 与 webpack 的关系webpack:将 ES6、CommonJS、amd、cmd 等模块化通过自己内部的机制统一成 webpack 的模块化。babel:转码 es6 语法,配合一些列 babel 工具链可以将新的 es2015+ 代码转换成所有浏览器支持的 es代码,babel 也可以将 es6 模块转换成 commonjs 模块2. es module 与 commonjs 为何可以混用因为 babel 会把 es module 转换成 commonjs 规范的代码babel 转码 es module 时如果遇到 export default 这种导出模块的书写方式后,会将其转换成 exports.default ,这时如果用 require 引入时,需要对其加上 .default 如 require('./a.js').a 这样才能获取 a 模块 export default 导出的 aimport 动态加载的模块也需要 .default 才能获取真实模块导出的值,如 import('./a.js').then(res => res.dafault)3. antd、element-ui 等ui框架的按需加载组件的实现需要 babel-plugin-component ...

August 18, 2019 · 4 min · jiezi

解密七牛云锐智转码技术打造低成本超高清的解决方案

互联网的迅速发展,提高了用户获取多样化信息的能力,根据《 2019 中国网络视听发展研究报告》显示:截至 2018 年底,我国网络视频用户规模达 7.25 亿,占总体网民的 87.5% 。视频已成为触达用户的主流媒介,网络视频行业的壮大已成为必然。如何在最小的带宽消耗下,最大幅度提升用户观看体验,成为了视频应用商首要考虑的问题。 七牛云深耕互联网行业 8 年,针对网络视频行业推出了锐智高清转码服务,融合感知编码、深度学习、图像处理、画质增强等技术,为点播、直播提供更智能的转码服务,也可以为存量视频做二次瘦身。 在体验方面,能够在低带宽条件下为用户提供高质量的视觉效果,画面更细腻、更美观。在成本方面,可以节省超过 40% 的视频码率,极大降低客户的传输带宽和视频存储成本。关键技术解密???? 智能场景分类不同场景可以适配更佳的算法提升压缩和画质效果。针对视频场景和复杂度进行两层分类,场景上包含:影视、教育、交通、体育、秀场等 10 多个类型;在部分场景上,会进一步做复杂度的分级。通过深度学习大量训练,目前CNN网络模型的准确度达到 95% 以上。 场景分类为自适应算法策略奠定基础,对于不同场景,适配的算法类别包括: 编码压缩算法和工具的组合前处理算法类别和参数ROI 感兴趣区域的算法客观质量评价工具 VMAF 的较准Per-Title 参数的选取锐智高清的智能自适应目标是在不同场景下匹配最优的算法,追求最佳的收益效果。以在线教育场景为例,屏幕录制为视频主要内容,针对屏幕内容采用特定算法可取得有效的针对性提升;而在秀场场景上,算法会更倾向于人脸和细节上的优化;再到体育场景,运动目标检测和去运动模糊是主要关注点。锐智高清智能自适应场景视频处理 Per-Title编码技术根据视频内容自适应码率以及自适应分辨率。在 Per-Title 技术出现之前,视频服务提供商通常会使用一个事先确定好的通用的编码配置来处理几乎所有的视频内容。一方面存在浪费,另一方面也不能始终给用户最佳体验。相比于传统的编码方案,Per-Title 编码技术更加注重不同类型视频之间的区分度,为每一个视频或视频片段找出满足清晰度和主观敏感度要求的最低码率点,以达到节省带宽的目的。除了根据内容自适应码率,Per-Title 技术还包括自适应分辨率。图片来源:Netflix 如图所示,对于同一视频源,采用三个分辨率,以及不同的码率编码。在各个分辨率下,编码质量随着码率的增加而单调上升。当码率达到一定阈值后,曲线开始变得平缓,这时需要通过提升分辨率才能进一步提升质量。 通过大量实战,锐智高清转码建立起的智能码控决策模型,对不同类型、相同类型不同片源、相同片源不同片段,应用完全不一样的编码参数,并且可以按帧级智能调优。锐智高清使用时可以让用户设置一个上限码率,码控模型将bits有效分配到最有价值的地方,在保障画质下尽可能输出最低码率。锐智高清秀场场景,画质相当节省码率66% AI视频前处理AI 视频前处理是提升画质的关键所在。由多个算法模块组成,每个模块负责某种画质增强的方向,包括:去编码脏效应、去噪、去毛刺、锐化、对比度增强、饱和度增强、去模糊等,各模块自适应调整强度,协同作用。 AI 视频前处理除了提升主观画质以外,其作用效果并非完全与视频编码解耦。比如在增强人眼易关注纹理的同时,弱化人眼不易察觉的信息,以期达到节省编码码率的目的。值得一提的是,所有的前处理 AI 算法,我们经过大量的调优打磨来平衡计算资源的消耗。锐智高清-AI前处理对画质大幅提升 ROI与码字分配ROI 感兴趣区域识别,以及更合理的码字分配,重点考虑了以下几个方面: 人脸人脸大概率是主观敏感区域。准确定位人脸,做针对性地画质调优以及编码参数调优。智能转码系统支持标准版和超低复杂度版本人脸检测,其中超低复杂度版本处理 1080P 视频在 2ms 每帧以内。 人眼聚焦区域、前景重点区域智能转码系统支持标准版和超低复杂度版人眼聚焦区域检测。标准版聚焦区域检测:采用眼动仪训练样本,有效定位人眼聚集区域。超低复杂度版聚焦区域检测:1080P 视频单核运算时间在 1ms 每帧以内,复杂度基本忽略不计。 各个块的主观敏感度X264 默认的自适应量化(AQ),仅仅依据方差大小作为评判依据,对方差大的块施以更大的量化因子。方差大小的鲁棒性不足,甚至都不能很好地判断平滑程度。如图示例的一维信号,左图的方差比右图更大,事实上左图是比较平滑的。图片来源:Fan Zhang, etc. Limitation and Challenges of Image Quality Measurement. SPIE 2010 以 RaceHorses 为例,第一行宏块,正好绿色草丛背景块的方差比较小,而涵盖了帽子、人脸、人眼的宏块方差比较大,导致主观敏感的人脸/人眼被施加了较大的 delta QP。智能转码系统区分了易被人眼关注的规则纹理,加以保护,在其他编码条件相同的条件下,取得明显改善。 ...

July 4, 2019 · 1 min · jiezi

视频flv转mp4最快的几种方法(就是不用格式工厂)

FFmpeg 是一个非常优秀的视频处理开源工具,假如你想把avi或者flv转成mpp4的时候,你就不必要用格式工厂很繁琐的方式去转换格式,因为比如flv和mp4他的编码(视频编码和音频编码)都是h.264编码其实并没有变,这是封装格式不一样而已,就是壳子不一样而已,格式工厂耗费资源和CPU较为严重,这里就不需要对视频进行重新编码,仅仅需要对文件重新加以MP4壳子即可,所以FFmpeg 就很方便。所以使用的代码为:ffmpeg -i input.flv -vcodec copy -acodec copy output.mp4或者干脆一点。ffmpeg -i input.flv output.mp4input就是你的文件名称,output就是输出名称,速度敲快~下载官网-FFmpeg,或者Github下载—FFmpeg选择对应的版本下载即可,下载完解压吧bin目录随便选择一个目录存放,里面这三个exe文件必须要有,然后把需要处理的视频放在目录下就ok了。更多的更详细的教程自行Google。简单粗暴的用mediacoder,很强大的一款开源软件,直接选择容器MP4用vlc播放器,直接拖拽入软件即可转mp4shotcut—免费开源的软件下载链接 提取码:IcOg

February 25, 2019 · 1 min · jiezi