微信公众平台H5支付

值得注意的是openid值的获取, 直接贴代码这里输入代码`

    /**
     * 微信统一下单
     * 
     * @param fenpay
     * @param orderStr
     * @return
     */
    private String placOrder(int fenpay, String orderStr, String subject) {
        Object openid = WebUtils.getSessionAttribute("openid");
        if (openid == null) {
            throw new ServiceException("没有获得微信授权,不能使用微信支付");
        }
        String url = "https://api.mch.weixin.qq.com/pay/unifiedorder";
        SortedMap<String, String> sm = new TreeMap<String, String>();
        // 添加三个固定的参数
        sm.put("appid", WeiXinUtil.APPID);
        sm.put("mch_id", WeiXinUtil.MCH_ID);
        sm.put("notify_url", WeiXinUtil.NOTIFY_URL);
        sm.put("trade_type", WeiXinUtil.TRADE_TYPE);
        sm.put("nonce_str", RandomStringGenerator.getRandomStringByLength(8));
        sm.put("body", subject);
        sm.put("out_trade_no", orderStr);
        sm.put("total_fee", String.valueOf(fenpay));
        sm.put("spbill_create_ip", "127.0.0.1");
        // 现在写死的
        // sm.put("openid", "oMEZLt3zK-GrAnkHRsHxKOaDONpM");
        //
        String openidStr = openid.toString();
        sm.put("openid", openidStr);
        getLogger().debug("opendid:" + openid);
        String sign = WeiXinUtil.createSign(sm);
        getLogger().debug("sign:" + sign);
        String xml = XmlUtil.createXml(sm, sign);
        getLogger().debug("post:" + xml);
        String result = HttpClientUtil.postXML(url, xml);
        getLogger().debug("result:" + result);
        Map<String, String> xmlmap = XmlUtil.parseXml(result);
        // 返回预支付标识
        String prepay_id = xmlmap.get("prepay_id");
        // 凡是拿不到prepay_id都统统归结为下单失败
        if (StringUtils.isEmpty(prepay_id)) {
            throw new ServiceException("微信下单失败");
        }
        return "prepay_id=" + prepay_id;
    }

我是通过授权跳转的方式获取openid,即在点击“去支付”按钮的时候跳转到微信平台进行自动授权然后跳转会指定页面,也可在系统登录的时候就进行授权跳转。贴代码注意加粗代码即可。还是推荐看文档 授权跳转 页面跳转授权

<a href="https://open.weixin.qq.com/connect/oauth2/authorize?appid=${appId}&redirect_uri=${payUrl2}&response_type=code&state=${user.uid}&scope=snsapi_base" class="paymentbtn">

redirect_uri为你要授权之后跳转回来的url

    /**
     * 
     * 活动报名
     * 
     * @return
     */
    @SuppressWarnings("unchecked")
    public String payApply() {
        Map<String, Object> user = (Map<String, Object>) WebUtils.getSessionAttribute("user");
**        // 获得微信支付返回来的state,用来存储登录用户的uid这个时候重新从接口中
        String state = WebUtils.getParameter("state");**
        getLogger().info("获取授权之后的state" + state);
        if (user == null && StringUtils.isNotEmpty(state)) {
            Map<String, Object> map = new HashMap<String, Object>();
            map.put("uid", state);
            String userStr = HttpClientUtil.doGet("http://120.24.240.104/bikeapi/?m=user&a=get_user", map);
            user = (Map<String, Object>) JSON.parse(userStr);
            ActionContext.getContext().getSession().put("user", user);
        }

        if (getActivityId() == null) {
            return "activityerror";
        }
        Activity av = activityService.findById(getActivityId());
        setActivity(av);
**        // 获得用户的openid,支付的时候需要用到
        String code = WebUtils.getParameter("code");**
        // String code = "021ed6c6c2f9b4689e166d7d0ea9caaD";
        if (StringUtils.isEmpty(code)) {
            // 不能获得支付授权
            setMsg("获得微信支付授权失败,不能使用微信支付");
        } else {
**            // ------------一下为获取openid的代码
            String url = "https://api.weixin.qq.com/sns/oauth2/access_token";
            Map<String, Object> map = new HashMap<String, Object>();
            map.put("appid", WeiXinUtil.APPID);
            map.put("secret", WeiXinUtil.APPSECRET);
            map.put("code", code);
            map.put("grant_type", "authorization_code");
            try {
                // 授权字符串
                String authStr = HttpClientUtil.doPost(url, map);
                JSONObject json = new JSONObject(authStr);
                String openid = json.getString("openid");
                // 把他set到session中,这应该是最简单的了吧
                WebUtils.getSession().setAttribute("openid", openid);**
            } catch (Exception e) {
                e.printStackTrace();
                setMsg("获得微信支付授权失败,不能使用微信支付");
            }
        }
        // 没有登录不能报名
        if (user != null) {
            // 查询当前用户的余额,如果查不到那么就是0元咯
            BigDecimal balance = userService.findBalanceByUid(Long.valueOf(user.get("uid").toString()));
            this.setUserBalance(balance);
            return "payapply";
        } else {
            fromUrl = "/index/index!login.action?from_url=/activity/activity!apply.action?id="
                    + String.valueOf(getActivityId());
            return "login";
        }
    }

好的,完成了统一下单之后你应该获取到一个package格式为prepay_id=123456789这样子的订单信息,那么接下来就可以在页面发起支付请求了。 刚开始的时候我按照文档的发起H5支付,结果怎么调试都不成功,相信很多朋友也是在这个地方遇到问题 https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=7_7 这个应该是官方文档没更新的原因,新版的微信中已经支持这种发起支付调用,而应该采用JSSDK发起支付请求。直接看到支付的api 输入链接说明 输入图片说明 遇到不懂的地方或者缺少的参数请往上看文档。 主要具体步骤有 输入图片说明

最后贴一下我的发起支付的页面代码,我只能帮你们到这里了,剩下的就是调试了。

<%@ page contentType="text/html;charset=UTF-8" %>
<%@ include file="/common/header.jsp" %>
<title>微信支付</title>
<link rel="stylesheet" type="text/css" href="css/activity.css">
<script type="text/javascript" charset="UTF-8" src="http://res.wx.qq.com/open/js/jweixin-1.0.0.js"></script>
<script type="text/javascript">

function pay(){
    var appId = $('#appId').val();
    var timeStamp = $('#timeStamp').val();
    var nonceStr = $('#nonceStr').val();
    var pk = $('#package').val();
    var signType = $('#signType').val();
    var paySign = $('#paySign').val();
    //config
    var timeStamp2 = $('#timeStamp2').val();
    var nonceStr2 = $('#nonceStr2').val();
    var signature = $('#signature').val();
    //
    var activityId = $('#activityId').val();

    wx.config({
        debug: false, // 开启调试模式,调用的所有api的返回值会在客户端alert出来,若要查看传入的参数,可以在pc端打开,参数信息会通过log打出,仅在pc端时才会打印。
        appId: appId, // 必填,公众号的唯一标识
        timestamp:timeStamp2 , // 必填,生成签名的时间戳
        nonceStr: nonceStr2, // 必填,生成签名的随机串
        signature: signature,// 必填,签名,见附录1
        jsApiList: ['chooseWXPay'] // 必填,需要使用的JS接口列表,所有JS接口列表见附录2
    });
    wx.ready(function(){
        // config信息验证后会执行ready方法,所有接口调用都必须在config接口获得结果之后,config是一个客户端的异步操作,所以如果需要在页面加载时就调用相关接口,则须把相关接口放在ready函数中调用来确保正确执行。对于用户触发时才调用的接口,则可以直接调用,不需要放在ready函数中。
        wx.chooseWXPay({
            timestamp: timeStamp, // 支付签名时间戳,注意微信jssdk中的所有使用timestamp字段均为小写。但最新版的支付后台生成签名使用的timeStamp字段名需大写其中的S字符
            nonceStr: nonceStr, // 支付签名随机串,不长于 32 位
            package: pk, // 统一支付接口返回的prepay_id参数值,提交格式如:prepay_id=***)
            signType: signType, // 签名方式,默认为'SHA1',使用新版支付需传入'MD5'
            paySign: paySign, // 支付签名
            complete: function (res) {
                // 支付成功后的回调函数
                location.href=basePath+'/activityorder/activity-order!list.action?activityId='+activityId;
            }
        });
    });
}
</script>
</head>
<body onload="pay()">
    <input id="activityId" type="hidden" value="${activityId}" />

    <input id="appId" type="hidden" value="${payparam.appId}" />
    <input id="timeStamp" type="hidden" value="${payparam.timeStamp}" />
    <input id="nonceStr" type="hidden" value="${payparam.nonceStr}" />
    <input id="package" type="hidden" value="${payparam.package1}" />
    <input id="signType" type="hidden" value="${payparam.signType}" />
    <input id="paySign" type="hidden" value="${payparam.paySign}" />

    <input id="timeStamp2" type="hidden" value="${apijsmap.timestamp}" />
    <input id="nonceStr2" type="hidden" value="${apijsmap.noncestr}" />
    <input id="signature" type="hidden" value="${apijsmap.signature}" />
</body>
</html>

最后记得在公众平台的公众权限配置,你发起支付的页面一定要是在公众平台设置了授权的页面,调用jsjdk的页面也要授权了的。至于具体要哪些授权,地方太多了慢慢找吧。

Visual Studio 2017 ASP.NET Core开发

Visual Studio 2017 ASP.NET Core开发,Visual Studio 2017 已经内置ASP.NET Core 开发工具.

在选择.NET Core 功能安装以后就可以进行ASP.NET Core开发。

新的ASP.NET Core项目为csproj ,打开之前的xproj项目,会提示单向升级,确认以后,会自动帮你升级至csproj。

 

新建项目

VS 2017新建ASP.NET Core 项目:

 

确定以后

可选择ASP.NET Core 1.0 和ASP.NET Core 1.1 ,以及启用Docker支持。

以下是ASP.NET Core 1.1 启用Docker支持 项目结构。

项目就可以运行在Docker 上,如果想在Docker调试等须在本地安装Docker。

ASP.NET Core 1.1  增加了一些新的特性。比如: WebSockets 支持。

安装 Microsoft.AspNetCore.WebSockets 包,然后在Startup 类Configure 方法中添加:

app.UseWebSockets();

具体可以看官方文档:

https://docs.microsoft.com/en-us/aspnet/core/aspnetcore-1.1#choosing-between-versions-10-and-11-of-aspnet-core

.NET Core csproj 支持

在项目的csproj文件中,你可以注意到项目的引用极大简化。

右键编辑csproj 文件:

复制代码
<Project Sdk="Microsoft.NET.Sdk.Web">

  <PropertyGroup>
    <TargetFramework>netcoreapp1.1</TargetFramework>
  </PropertyGroup>

  <PropertyGroup>
    <PackageTargetFallback>$(PackageTargetFallback);portable-net45+win8+wp8+wpa81;</PackageTargetFallback>
    <DockerComposeProjectPath>..\docker-compose.dcproj</DockerComposeProjectPath>
  </PropertyGroup>
  <ItemGroup>
    <PackageReference Include="Microsoft.ApplicationInsights.AspNetCore" Version="2.0.0" />
    <PackageReference Include="Microsoft.AspNetCore" Version="1.1.1" />
    <PackageReference Include="Microsoft.AspNetCore.Mvc" Version="1.1.2" />
    <PackageReference Include="Microsoft.AspNetCore.StaticFiles" Version="1.1.1" />
    <PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="1.1.1" />
    <PackageReference Include="Microsoft.VisualStudio.Web.BrowserLink" Version="1.1.0" />
  </ItemGroup>
  <ItemGroup>
    <DotNetCliToolReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Tools" Version="1.0.0" />
  </ItemGroup>

</Project>
复制代码

 

PackageReference 为NuGet 包

DotNetCliToolReference 为增强 dotnet 命令行工具

 

发布应用程序

在项目上右键选择 发布 ,接着选择文件夹

点击发布如下:

目标位置后面的设置中可以进行具体的一些设置。

 

推荐Visual Studio 2017 扩展

开发ASP.NET Core ,下面两个扩展推荐安装:

 编辑的csproj文件推荐NuGet 安装包: Project File Tools

https://marketplace.visualstudio.com/items?itemName=ms-madsk.ProjectFileTools

ASP.NET Core Tag Helpers 智能提示:Razor Language Services

https://marketplace.visualstudio.com/items?itemName=ms-madsk.RazorLanguageServices

可以根据上面地址下载下来安装,也可以在 工具->扩展和更新 中搜索安装:

 

以下再推荐两款VS 2017 扩展:

Web Essentials Web开发利器:

https://marketplace.visualstudio.com/items?itemName=MadsKristensen.WebExtensionPack2017

Productivity Power Tools 2017 效率开发:

https://marketplace.visualstudio.com/items?itemName=VisualStudioProductTeam.ProductivityPowerPack2017

 

由于VS2017 刚出正式版,问题还是存在一些。

具体可以去 https://www.visualstudio.com/en-us/news/releasenotes/vs2017-relnotes#a-idknownissues-aknown-issues 查看。

你如果遇到问题,可以点击右上方发送反馈报告问题。

 

参考文档:https://blogs.msdn.microsoft.com/webdev/2017/03/07/announcing-visual-studio-2017/

 

如果你觉得本文对你有帮助,请点击“推荐”,谢谢。

ASP.NET Core实践交流群: 133144964

.NET Core 跨平台交流群: 550897034
博客示例代码GitHub:https://github.com/linezero/Blog
分类: ASP.NET Core

如何打造类似数据虫巢官网系列教程之二:爬虫是怎么炼成的

文·blogchong

本文接上一篇《如何打造类似数据虫巢官网系列教程之一:介绍已经准备工作》,不清楚前面剧情的童鞋可以先看看。

这篇文章重点在于解决“数据虫巢官网”的底层数据问题,即那些分析数据的原始数据的来源。

结论很明显,当然是爬过来的,所以这篇我们将重点讲讲如何进行数据爬取,并且以虫巢官网的底层数据爬取代码为例子进行讲解。

当然,其中会一些常规的防爬机制破解以及应对的话题,整体来说这篇会偏重互联网公开数据集的爬取,即爬虫。

此外,整个数据虫巢官网的站点源代码,目前已经整理到github上咯,先放上github的链接:github.com/blogchong/mite8-com。

这是一个完整的项目,这部分代码包括以下部分:

1 整个数据虫巢数据处理后端框架。
2 前端每个页面JSP代码部分,以及涉及数据可视化渲染部分。
3 几个重点数据源的爬虫逻辑,以及定期更新爬虫数据的入口逻辑。
4 数据处理中涉及到的NLP部分,有几个侧重点,包括重构加工的分词工具,以及简单的情感分析,并且提供了分词的一个工具接口。
PS:如果感兴趣,clone之前别忘了给个star,哈哈。

爬虫框架 – Webcollector

回到主题,说到爬虫,由于我之前对于Python并不是很熟悉,而Java则是我的拿手好戏,并且目前市面上封装的爬虫工具很多,所以,我的考虑就是Java语言封装的Webcollector。

简单说一下这个框架,大伙儿感兴趣的可以去开源中国搜一下他的主页,对于Java不熟悉的朋友,其实也无所谓的,使用其他的Python框架一样是可以的,那这部分关于框架这块的就可以略过啦。

Webcollector支持各种自定义的遍历策略,这种在于路径规则不明确的时候使用是很有用的,比如我当时在爬取各大主流招聘网站的JD数据时,就通过这种模式做的,但如果是目的明确的,其实就是按照自己的业务逻辑去固定路径一次性获取数据了。

Webcollector对于Cookie、请求头之类的信息,提供了设置接口,可以很方便的伪装成浏览器,以及登录状态去爬取数据。

Webcollector集成了传统的JDBC持久化策略,可以很方便的将爬取的数据进行MySQL落地,以及MongDB落地等。

使用上也很方面,集成在Maven中,并且更新还是蛮及时的,所以需要集成到自己的Java代码中,只需要引入Jar包即可开整。

除此之外,Webcollector内部封装了selenium,对于动态加载的JS数据来说,也可以很轻松的拿到相关的数据。

其实上面基本都是它的一些特性,对于新手来说都太模糊,这个框架最好的地方在于作者提供了大量的博客实例,来解释各种特性,以及各种简单的爬虫实例可供参考,简直就是初学者的福音。

具体不再多说,想了解更多的,可以搜索然后进入进行学习。

爬取数据

不同的网站对于数据的展现以及输出方式可能都有点不同,静态的网页数据是最好获取的,比如我之前爬取一些偏传统的招聘网站的数据,直接通过入口就可以拿到数据,基本不设防。

代码例子:

CrawlDatum crawlDatum = new CrawlDatum(listUrl).putMetaData(“method”, “POST”);
HttpRequest request = new HttpRequest(crawlDatum.getUrl());
request.setMethod(crawlDatum.getMetaData(“method”));
HttpResponse httpResponse = request.getResponse();
Page page = new Page(crawlDatum, httpResponse);

我们拿到了HttpResponse对象,并且封装成Page对象,通过Page对象提供的Html解析方法,进行数据拆解。

其实Page底层的实现依然是Jsoup,一种很常规的Html结构解析包,我们来看一下具体的使用:

page.select(“div[class=review-content clearfix]”).text()

这是一个很常见的解析过程语法,在page对象中查找class名为“review-content clearfix ”的div,并且调用text方法,将内容转换为String。

静态页面,基本上会上门两招就够了,访问页面数据,然后解析数据,将非结构化的数据转换为结构化数据,当然具体怎么入库,在Java里方式就很多了。

除了静态页面之外,还有其他形式的数据获取。

比如现在很流行的一种做法,那就是前后端进行分离,即后端数据由额外的请求进行获取,再通过前端进行异步渲染。

其实这种做法也是有理由的,因为后端数据的请求跟前端其他部分渲染效率是不同,所以一般做成异步请求,这样在整个页面在后端效率不高时不会造成整个页面等待,提升用户效率。

这个时候,你单纯的看页面源码已经不行啦,你需要会使用浏览器的元素审查,把这些异步请求的链接给逮出来。

我在做雾霾影响分析报告时,基础原始数据是京东的口罩购买数据,并且是评论数据,其评论就是异步加载获取的。

通过F12做元素审查,找到评论数据的真正调用接口,一般异步操作都是放到JS里,并且接口在命名上有一定的提示,如上图就是京东商品的评论数据接口。

大概链接长这样子:

https://sclub.jd.com/comment/productPageComments.action?productId=2582352&score=3&sortType=3&page=0&pageSize=10&isShadowSku=0&callback=fetchJSON_comment98vv47364

里头有控制翻页的参数,我们控制部分参数就可以愉快的获取到数据啦,我们再把callback参数去掉,就是实打实的JSON数据了,连清洗数据的活都省了。

除此之外,还有一个需要注意的点就是,控制访问频度,不管你是单机爬着玩也好,或者是工作大范围爬用代理池也好,频度是一个很基础的防爬机制。

具体的虫巢涉及的代码呢,我就不一一列出来了,这里列一下开源出来的代码,涉及到爬虫的部分,做个备注,有兴趣的可以去我github上clone下来,然后按下面的路径去分析分析逻辑,克隆完了记得给个star哟。

mite8-com开源项目涉及到爬虫的部分:

1 京东雾霾相关的爬虫逻辑:package com.mite8.Insight.jd_wumai;
2 电影《长城》相关的爬虫逻辑:package com.mite8.Insight.movie_great_wall;
3 政务舆情相关的爬虫逻辑:package com.mite8.jx.gz.dn.service; //service下对应的几个子目录,下面的utils,入口是OptXXX类。

防爬的一些机制,以及对应的破解之道

在这里再说一些玩爬虫时,会遇到的一些常见的防爬手段,以及对应的破解之道。

由于俺不是专业的爬虫,所以这部分这么完善的东西显然不是出自我之手,是我团队里爬虫大神在内部技术分享时总结的,俺只是个搬运工。

第一种,伪装成合法的浏览器

在一般情况下,我们会对请求头进行伪装,最重点的key就是user-agent,这部分信息就是浏览器的内核信息。

由于很多公司,甚至是大楼都是用同一个对外IP,所以单纯的使用频度进行防爬封锁,这种情况很容易造成误杀,这也是目标网站不愿意看到的情况。

但是这种情况下,一般不同的电脑其浏览器是不同的,包括内核版本等等,防爬时会分析这个user-agent是不是一样的,或者说非法的字符。

因为很多爬虫框架,或者进程方位URL时会有默认的标志,通过分析这个频度可以明显知道是不是机器在访问页面。

所以,我们通常会获取一批正常的user-agent做随机封装,去获取数据,这种措施会导致上面说的那种防爬机制时效。

第二种,IP频度封锁

在一个IP过于频繁的访问页面时,网站根据一定的判定策略,会判断这个IP是非法的机器,进行IP封锁,导致这个IP无法访问目标页面。

这个时候,我们可以控制访问频度避免被封,但很多时候我们爬取的量很大,控制频度很难完成任务,那么我们就需要使用代理池来做了。

通过代理池的IP,进行IP伪装,这样就破解了频度的控制。

通常代理池分免费与收费,一般免费的代理池都是被人用烂了的,里头的IP都是在各大主流网站的黑名单里。

最后,至于说每个网站的频度是什么样子的,以及控制力度(禁封几分钟,或者是一天等等),就需要自己多测试尝试了。

第三种,用户验证机制

用户验证,这是个很常见的东西,很多页面只有用户登录之后可以访问。

一般通常的做法都是cookie验证,所以,关键是我们如何获取这个cookie。

一次性爬取比较容易,直接把cookie帖进去,做访问即可,但是遇到自动化的时候,我们就需要研究用户登录的过程了,使用POST做表单提交,获取cookie,后面的流程就通啦。

第四种,验证码

很多操作是需要验证码才能下一步操作的,这个时候除了破解验证码无法可破。

不错对于简单的验证码,或者说自己技术犀利的话,写个图像识别的东东,做图像识别,识别验证码也行,但是,目前验证码设计的都很变态,详情参考12306,所以这个方法打折的厉害。

还有一种手段,购买付费的打码平台服务,直接完破之,就是费钱而已。

第五种,动态页面

所谓动态页面,即很多时候数据是通过js动态加载出来的,或者JS加密的,这个时候,直接访问是拿不到数据。

也有破解之道,使用JS引擎做JS解析,目前不管是Python的还是Java的,有不少这种引擎可以供调用。

最后一种方法,使用浏览器内核去访问这个链接,就跟真正的浏览器访问页面没有什么差别啦,Java中经典的selenium就是其中一种。

据闻,技术高端点的公司还有更变态的,通过机器学习来学习真实用户的访问轨迹,通过算法来判断这种访问轨迹是否是机器造成的,然后再做判断是否做禁封。

好吧,玩高深的爬虫,其实就一部防爬与反爬的斗争史,其乐无穷。

下一篇,接着话题,我们讲讲述云平台搭建,服务器部署,环境配置相关的东西:《如何打造类似数据虫巢官网系列教程之三:服务器》。

最后,再贴一遍,数据虫巢官网(www.mite8.com)的开源代码地址(可以随意fork、star 哈哈):github.com/blogchong/mite8-com。


相关阅读:

《如何打造类似数据虫巢官网系列教程之一:介绍已经准备工作》


广告Time:

要不要一起探讨大数据的相关的话题,是不是想要跨界大数据,进一步了解、讨论mite-com的开源代码,欢迎加入“数据虫巢读者私密群”,=>>戳此进入

查看原文

「视频直播技术详解」系列之七:直播云 SDK 性能测试模型

作者:七牛云
链接:https://zhuanlan.zhihu.com/p/22902315
来源:知乎
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

关于直播的技术文章不少,成体系的不多。我们将用七篇文章,更系统化地介绍当下大热的视频直播各环节的关键技术,帮助视频直播创业者们更全面、深入地了解视频直播技术,更好地技术选型。

本篇是《视频直播技术详解》系列的最后一篇直播云 SDK 性能测试模型,SDK 的性能对最终 App 的影响非常大。SDK 版本迭代快速,每次发布前都要进行系统的测试,测试要有比较一致的行为,要有性能模型作为理论基础,对 SDK 的性能做量化评估。本文就是来探讨影响 SDK 性能的指标并建立相应的性能模型的。

本系列文章大纲如下:

(一)开篇

(二)处理

(三)编码和封装

(四)推流和传输

(五)延迟优化

(六)现代播放器原理

(七)SDK 性能测试模型

影响视频质量和大小的重要参数

在进行测试之前我们需要明确几个对视频的质量和大小影响最大的参数:帧率、码率和分辨率。

1)如何制定帧率

一帧就是一副静止的画面,连续的帧就形成动画,如电视图象等。我们通常说帧数,简单地说,就是在 1 秒钟时间里传输的图片的数,也可以理解为图形处理器每秒钟能够刷新几次,通常用 fps(Frames Per Second)表示。每一帧都是静止的图象,快速连续地显示帧便形成了运动的假象。高的帧率可以得到更流畅、更逼真的动画。每秒钟帧数 (fps) 愈多,所显示的动作就会愈流畅。

2)如何制定码率

我们首先看视频编码的目的,它是为了在有限的带宽中传输尽可能清晰的视频,我们以每秒 25 帧的图像举例,25 帧图像中定义了 GOP 组,目前主要是有 I,B,P 帧三种帧格式,I 帧是关键帧,你可以想象它就是一幅 JPEG 压缩图像,而 B,P 帧是依靠 I 帧存在的,如果丢失了 I 帧,B,P 帧是看不到图像的,B,P 帧描述的不是实际的图像像素内容,而是每个相关像素的变化量,他们相对于 I 帧信息量会很小。GOP 组是指一个关键帧I帧所在的组的长度,每个 GOP 组只有 1 个 I 帧。

我们再来看,一组画面的码流大小跟什么有关?当视频编码的压缩方式都一样,清晰度要求都一样的时候,GOP 组的长度格式决定了码流的大小,例如:每秒 25 帧画面,GOP 组长度为 5,那么帧格式为 IBPBP,那么 1 秒钟有 5 个 I 帧,10 个 B 帧,10 个 P 帧,如果 GOP 组长度为 15,帧格式就是 IBBPBBPBBPBBPBB,那么 1 秒钟内会有 2 个 I 帧和 16 个 B 帧和 7 个 P 帧,那么 5 个 I 帧比 2 个 I 帧占用的数据信息量大,所以 GOP 组的长度格式也决定了码流的大小。

3)如何指定分辨率

分辨率概念视频分辨率是指视频成像产品所成图像的大小或尺寸。常见的视像分辨率有 640×480,1088×720,1920×1088。在成像的两组数字中,前者为图片长度,后者为图片的宽度,两者相乘得出的是图片的像素。

影响 SDK 性能的指标

有了上述的前置知识,我们可以开始准备测试 SDK 的性能了,我们首先分析一下都有哪些指标可以反映 SDK 的性能,分成 Android 和 iOS 两个平台:

Android

  • GC :可以通过 GC 日志记录,Mirror GC 和 Full GC 的频次和时间,Full GC 会造成比较明显的卡顿,需要评估
  • UI Loop 就是 VSync Loop :反映 SDK 对 App 流畅度的影响,理论上 60 fps 是最流畅的值。
  • Memory :反映 SDK 占用内存的大小
  • CPU Usage :反映 SDK 占用计算资源的大小

iOS

  • UI Loop :反映 SDK 对 App 流畅度的影响,理论上 60 fps 是最流畅的值。
  • Memory :反映 SDK 占用内存的大小
  • CPU Usage :反映 SDK 占用计算资源的大小

除了上面的一些系统级别的指标外,下面是直播 SDK 中特有的一些指标,这些指标可以反映出 SDK 的核心竞争力和一些主要的差异,涉及到视频的清晰度和流畅度,也是可以量化的。

1)影响视频清晰度的指标

  • 帧率
  • 码率
  • 分辨率
  • 量化参数(压缩比)

2)影响视频流畅度的指标

  • 码率
  • 帧率

3)其他重要指标

直播是流量和性能的消耗大户,有一些指标,直接影响了用户的感受,也是我们需要重点关注的:

  • 耗电量
  • 发热(不好量化,大部分情况发热和耗电量正比,可以使用耗电量暂时替代)

测试计划

测试过程需要先固化一些测试条件,然后根据不同的测试条件得出测试结果,这里选择了两个现在最常见的条件,是我们通过回访大量的客户得出的一些统计数字,可以反映大部分直播应用所处的场景。主要从分辨率、视频处理、码率和网络环境几个维度进行限制。
最后分为几个两种测试指标:客观和主观指标,前者反映了 SDK 对系统的消耗程度,但虽说是客观指标并不是说对用户没有影响、只是说得出的结果用户感受不明显。主观指标则会直接影响最终用户体验,但在传统的测试中反而容易被忽略,因为不好量化,这里拍砖引玉的提出一些量化的方式,希望引起读者的思考。

测试条件 A

  • 分辨率 480p
  • 无水印,无美颜
  • 码率 1 M
  • 网络保证在 0.5 M ~ 2 M

这个条件,反映了大部分低速网络情况下的使用场景,也反映了 SDK 基本的性能情况,可以作为 SDK 基本推流和拉流情况下的基准测试,不引入太多的测试依赖。

测试条件 B

  • 分辨率 720p
  • 无水印,有美颜
  • 码率 1 M
  • 网络保证在 0.5 M ~ 2 M

这个条件,反映了大部分客户的使用场景,具有较高的分辨率和美颜视频处理,可以作为 SDK 竞品分析的重要依据,测试结果非常接近真实场景。

1)客观指标测试计划
客观影响 App 稳定性和性能的指标:

  • Memory
  • 测试 10 分钟,内存曲线
  • 测试 1 小时,TP99,TP95,TP90,需要归档
  • 测试 1 小时,内存增量,考察是否有内存泄露,需要归档
  • 参考值:上次结果
  • CPU
  • 测试 10 分钟,CPU Usage 曲线
  • 测试 10 分钟,TP99,TP95,TP90,需要归档
  • 参考值:上次结果
  • 码率
  • 测试 10 分钟,TP99,TP95,TP90,重点说明,这里的码率控制需要分开来看,如果网络抖动造成码率降低,这样的点不计入进来,只测试 SDK 码率控制,需要归档
  • 参考值:1 M(大小都是偏差)
  • 耗电量
  • 测试一小时,记录进程总耗电量、屏幕显示耗电量、CPU 耗电量,需要归档
  • 参考值:上次结果

2)主观指标测试计划
主观影响 App 使用者的指标:

  • UI Loop App 本身可以达到的最大帧率,不同于视频帧率,统计他的原因是我们的 SDK 可能会影响整个 App 的流畅度,需要跟踪
  • 测试 10 分钟,UI Loop 曲线
  • 测试 10 分钟,UI Loop TP99,TP95,TP90,需要归档反复比较
  • 参考值:60 fps
  • Android GC
  • 测试 1 小时,记录 Mirror GC 和 Full GC 的频次,记录 GC 时长的 TP99,TP95,TP90,需要归档反复比较
  • 参考值:上次结果
  • 帧率(fps)
  • 测试 10 分钟,TP99,TP95,TP90,需要归档反复比较
  • 参考值:30 fps
  • PSNR 比较视频清晰度的指标
  • 测试 10 分钟,需要归档反复比较,这个指标可以使用固定视频作为输入。
  • 参考值:上次结果

3)结果显示

  • 表格显示具体指标
  • 曲线显示原始数据和时间轴的数据
  • 热图显示和参考值的偏差
  • 热图显示距离上次归档值是改善了还是恶化了

通过这种反复迭代的自动化的、系统化的测试,我们以职人之心近乎偏执地反复打磨着 SDK 的性能,只为给最终用户带来最好的直播体验,帮助我们的客户通过次时代的媒体最大化自己的商业价值,我们希望在您披荆斩棘的路上我们始终相伴。

七牛云机构号将实时分享云计算领域的技术洞见以及行业讯息,为你的创新加速。
欢迎提问&求关注 ( ´ ▽ ` )ノ
以上。


「视频直播技术详解」系列之六:现代播放器原理

作者:七牛云
链接:https://zhuanlan.zhihu.com/p/22693248
来源:知乎
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

关于直播的技术文章不少,成体系的不多。我们将用七篇文章,更系统化地介绍当下大热的视频直播各环节的关键技术,帮助视频直播创业者们更全面、深入地了解视频直播技术,更好地技术选型。
本系列文章大纲如下:

(一)采集

(二)处理

(三)编码和封装

(四)推流和传输

(五)延迟优化

(六)现代播放器原理

(七)SDK 性能测试模型
在上一篇延迟优化中,我们分享了不少简单实用的调优技巧。本篇是《视频直播技术详解》系列之六:现代播放器原理。
近年来,多平台适配需求的增长导致了流媒体自适应码率播放的兴起,这迫使 Web 和移动开发者们必须重新思考视频技术的相关逻辑。首先,巨头们分分发布了 HLS、HDS 和 Smooth Streaming 等协议,把所有相关细节都隐藏在它们专供的 SDK 中。开发者们没法自由的修改播放器中的多媒体引擎等逻辑:你没法修改自适应码率的规则和缓存大小,甚至是你切片的长度。这些播放器可能用起来简单,但是你没有太多去定制它的选择,即便是糟糕的功能也只能忍受。

但是随着不同应用场景的增加,可定制化功能的需求越来越强。仅仅是直播和点播之间,就存在不同的 buffer 管理、ABR 策略和缓存策略等方面的差别。这些需求催生了一系列更为底层关于多媒体操作 API 的诞生:Flash 上面的 Netstream,HTML5 上的 Media Source Extensions,以及 Android 上的 Media Codec,同时业界又出现了一个基于 HTTP 的标准流格式 MPEG-DASH。这些更高级的能力为开发者提供了更好的灵活性,让他们可以构建适合自己业务需求的播放器和多媒体引擎。

今天我们来分享一下如何构建一个现代播放器,以及构建这样一个播放器需要哪些关键组件。通常来说,一个典型的播放器可以分解成三部分:UI、 多媒体引擎和解码器,如图 1 所示:
图 1. 现代播放器架构
用户界面(UI):这是播放器最上层的部分。它通过三部分不同的功能特性定义了终端用户的观看体验:皮肤(播放器的外观设计)、UI(所有可自定义的特性如播放列表和社交分享等)以及业务逻辑部分(特定的业务逻辑特性如广告、设备兼容性逻辑以及认证管理等)。

多媒体引擎:这里处理所有播放控制相关的逻辑,如描述文件的解析,视频片段的拉取,以及自适应码率规则的设定和切换等等,我们将在下文中详细讲解这部分内容。由于这些引擎一般和平台绑定的比较紧,因此可能需要使用多种不同的引擎才能覆盖所有平台。

解码器和 DRM 管理器:播放器最底层的部分是解码器和 DRM 管理器,这层的功能直接调用操作系统暴露出来的 API。解码器的主要功能在于解码并渲染视频内容,而 DRM 管理器则通过解密过程来控制是否有权播放。

接下来我们将使用例子来介绍各层所扮演的不同角色。

一、用户界面(UI)

UI 层是播放器的最上层,它控制了你用户所能看到和交互的东西,同时也可以使用你自己的品牌来将其定制,为你的用户提供独特的用户体验。这一层最接近于我们说的前端开发部分。在 UI 内部,我们也包含了业务逻辑组件,这些组件构成了你播放体验的独特性,虽然终端用户没法直接和这部分功能进行交互。

UI 部分主要包含三大组件:

1. 皮肤

皮肤是对播放器视觉相关部分的统称:进度控制条、按钮和动画图标等等,如图 2 所示。和大部分设计类的组件一样,这部分组件也是使用 CSS 来实现的,设计师或者开发者可以很方便的拿来集成(即便你使用的是 JW Player 和 Bitdash 这种整套解决方案)。
图 2. 播放器皮肤
2. UI 逻辑

UI 逻辑部分定义了播放过程中和用户交互方面所有可见的交互:播放列表、缩略图、播放频道的选择以及社交媒体分享等。基于你预期达到的播放体验,还可以往这部分中加入很多其它的功能特性,其中有很多以插件的形式存在了,或许可以从中找到一些灵感:Plugins · videojs/video.js Wiki · GitHub 逻辑部分包含的功能较多,我们不一一详细介绍,直接以 Eurosport 播放器的 UI 来作为例子直观感受一下这些功能。
图 3. Eurosport 播放器的用户界面

从图 3 可以看出,除了传统的 UI 元素之外,还有一个非常有趣的特性,在用户观看 DVR 流媒体的时候,直播以小视窗的形式展示,观众可以通过这个小窗口随时回到直播中。由于布局或者 UI 和多媒体引擎完全独立,这些特性在 HTML5 中使用 dash.js 只需要几行代码就能实现。对于 UI 部分来说,最好的实现方式是让各种特性都以插件/模块的形式添加到 UI 核心模块中。

3. 业务逻辑

除了上面两部分「可见」的功能特性之外,还有一个不可见的部分,这部分构成了你业务的独特性:认证和支付、频道和播放列表的获取,以及广告等。这里也包含一些技术相关的东西,比如用于 A/B 测试模块,以及和设备相关的配置,这些配置用于在多种不同类型的设备之间选择多个不同的媒体引擎。

为了揭开底层隐藏的复杂性,我们在这里更详细的讲解一下这些模块:

设备检测与配置逻辑:这是最重要的特性之一,因为它将播放和渲染剥离开来了。例如,基于你浏览器的不同版本,播放器可能会自动为你选择一个基于 HTML5 MSE 的多媒体引擎 hls.js,或者为你选择一个基于 flash 的播放引擎 FlasHls 来播放 HLS 视频流。这部分的最大特点在于,无论你使用什么样的底层引擎,在上层都可以使用相同的 JavaScript 或者 CSS 来定制你的 UI 或者业务逻辑。

能够检测用户设备的能力允许你按需配置终端用户的体验:如果是在移动设备而非 4K 屏幕设备上播放,你可能需要从一个较低的码率开始。

A/B 测试逻辑:A/B 测试是为了能够在生产环节中灰度部分用户。例如,你可能会给部分 Chrome 用户提供一个新的按钮或者新的多媒体引擎,并且还能保证它所有的工作都正常如期进行。

广告(可选):在客户端处理广告是最复杂的业务逻辑之一。如 videojs-contrib-ads 这个插件模块的流程图给出一样,插入广告的流程中包含多个步骤。对于 HTTP 视频流来说,你或多或少会用到一些已有的格式如 VAST、VPAID 或者 Google IMA,它们能够帮你从广告服务器中拉取视频广告(通常是过时的非自适应格式),放在视频的前期、中期和后期进行播放,且不可跳过。

总结:

针对你的定制化需求,你可能选择使用包含所有经典功能的 JW Player 来播放(它也允许你定制部分功能),或者基于 Videojs 这样的开源播放器来定制你自己的功能特性。甚至为了在浏览器和原生播放器之间统一用户体验,你也可以考虑使用 React Native 来进行 UI 或者皮肤的开发,使用 Haxe 来进行业务逻辑的开发,这些优秀的库都可以在多种不同类型的设备之间共用同一套代码库。

图 4. 业务逻辑流程图

二、多媒体引擎

近年来,多媒体引擎更是以一种全新独立的组件出现在播放器架构中。在 MP4 时代,平台处理了所有播放相关的逻辑,而只将一部分多媒体处理相关的特性(仅仅是播放、暂停、拖拽和全屏模式等功能)开放给开发者。

然而,新的基于 HTTP 的流媒体格式需要一种全新的组件来处理和控制新的复杂性:解析声明文件、下载视频片段、自适应码率监控以及决策指定等等甚至更多。起初,ABR 的复杂性被平台或者设备提供商处理了。然而,随着主播控制和定制播放器需求的递增,一些新的播放器中慢慢也开放了一些更为底层的 API(如 Web 上的 Media Source Extensons,Flash 上的 Netstream 以及 Android 平台的 Media Codec),并迅速吸引来了很多基于这些底层 API 的强大而健壮的多媒体引擎。
图 5. Google 提供的多媒体处理引擎 Shakaplayer 的数据流程图

接下来我们将详细讲解现代多媒体处理引擎中各组件的细节:

1. 声明文件解释和解析器

在基于 HTTP 的视频流中,一切都是以一个描述文件开始。该声明文件包含了媒体服务器所需理解的元信息:有多少种不同类型的视频质量、语言以及字母等,它们分别是什么。解析器从 XML 文件(对于 HLS 来说则是一种特殊的 m3u8 文件)中取得描述信息,然后从这些信息中取得正确的视频信息。当然,媒体服务器的类型很多,并不是所有都正确的实现了规范,因此解析器可能需要处理一些额外的实现错误。

一旦提取了视频信息,解析器则会从中解析出数据,用于构建流式的视觉图像,同时知道如何获取不同的视频片段。在某些多媒体引擎中,这些视觉图像先以一副抽象多媒体图的形式出现,然后在屏幕上绘制出不同 HTTP 视频流格式的差异特征。

在直播流场景中,解析器也必须周期性的重新获取声明文件,以便获得最新的视频片段信息。

2. 下载器(下载声明文件、多媒体片段以及密钥)

下载器是一个包装了处理 HTTP 请求原生 API 的模块。它不仅用于下载多媒体文件,在必要的时候也可以用于下载声明文件和 DRM 密钥。下载器在处理网络错误和重试方面扮演着非常重要的角色,同时能够收集当前可用带宽的数据。

注意:下载多媒体文件可能使用 HTTP 协议,也可能使用别的协议,如点对点实时通信场景中的 WebRTC 协议。

3. 流播放引擎

流播放引擎是和解码器 API 交互的中央模块,它将不同的多媒体片段导入编码器,同时处理多码率切换和播放时的差异性(如声明文件和视频切片的差异,以及卡顿时的自动跳帧)。

4. 资源质量参数预估器(带宽、CPU 和帧率等)

预估器从各种不同的维度获取数据(块大小,每片段下载时间,以及跳帧数),并将其汇聚起来用于估算用户可用的带宽和 CPU 计算能力。这是输出用于 ABR (Adaptive Bitrate, 自适应码率)切换控制器做判断。

5. ABR 切换控制器

ABR 切换器可能是多媒体引擎中最为关键的部分——通常也是大家最为忽视的部分。该控制器读取预估器输出的数据(带宽和跳帧数),使用自定义算法根据这些数据做出判断,告诉流播放引擎是否需要切换视频或者音频质量。该领域有很多研究性的工作,其中最大的难点在于在再缓冲风险和切换频率(太频繁的切换可能导致糟糕的用户体验)之间找到平衡。

6. DRM 管理器(可选组件)

今天所有的付费视频服务都基于 DRM 管理,而 DRM 则很大程度上依赖于平台或者设备,我们将在后续讲解播放器的时候看到。多媒体引擎中的 DRM 管理器是更底层解码器中内容解密 API 的包装。只要有可能,它会尽量通过抽象的方式来屏蔽浏览器或者操作系统实现细节的差异性。该组件通常和流处理引擎紧密连接在一起,因为它经常和解码器层交互。

7. 格式转换复用器(可选组件)

后文中我们将看到,每个平台在封包和编码方面都有它的局限性(Flash 读的是 FLV 容器封装的 H.264/AAC 文件,MSE 读的是 ISOBMFF 容器封装的 H.264/AAC 文件)。这就导致了有些视频片段在解码之前需要进行格式转换。例如,有了 MPEG2-TS 到 ISOBMFF 的格式转换复用器之后,hls.js 就能使用 MSE 格式的内容来播放 HLS 视频流。多媒体引擎层面的格式转换复用器曾经遭受质疑;然而,随着现代 JavaScript 或者 Flash 解释权性能的提升,它带来的性能损耗几乎可以忽略不计,对用户体验也不会造成多大的影响。

总结

多媒体引擎中也有非常多的不同组件和特性,从字幕到截图到广告插入等等。接下来我们也会单独写一篇文章来对比多种不同引擎的差异,通过一些测试和市场数据来为引擎的选择给出一些实质性的指导。值得注意的是,要构建一个兼容各平台的播放器,提供多个可自由替换的多媒体引擎是非常重要的,因为底层解码器是和用户平台相关的,接下来我们将重点讲解这方面的内容。

三、解码器和 DRM 管理器

出于解码性能(解码器)和安全考虑(DRM),解码器和 DRM 管理器与操作系统平台密切绑定。
图 6. 解码器、渲染器和 DRM 工作流程图
1. 解码器

解码器处理最底层播放相关的逻辑。它将不同封装格式的视频进行解包,并将其内容解码,然后将解码后的视频帧交给操作系统进行渲染,最终让终端用户看到。

由于视频压缩算法变得越来越复杂,解码过程是一个需要密集计算的过程,并且为了保证解码性能和流畅的播放体验,解码过程需要强依赖于操作系统和硬件。现在的大部分解码都依赖于 GPU 加速解码的帮助(这也是为什么免费而更强大的 VP9 解码器没有赢得 H.264 市场地位的原因之一)。如果没有 GPU 的加速,解码一个 1080P 的视频就会占去 70% 左右的 CPU 计算量,并且丢帧率还可能很严重。

在解码和渲染视频帧的基础之上,管理器也提供了一个原生的 buffer,多媒体引擎可以直接与该 buffer 进行交互,实时了解它的大小并在必要的时候刷新它。

我们前面提到,每个平台都有它自己的渲染引擎和相应的 API:Flash 平台有 Netstream,Android 平台有 Media Codec API,而 Web 上则有标准的 Media Sources Extensions。MSE 越来越吸引眼球,将来可能会成为继浏览器之后其它平台上的事实标准。

2. DRM 管理器
图 7. DRM 管理器
今天,在传输工作室生产的付费内容的时候,DRM 是必要的。这些内容必须防止被盗,因此 DRM 的代码和工作过程都向终端用户和开发者屏蔽了。解密过的内容不会离开解码层,因此也不会被拦截。

为了标准化 DRM 以及为各平台的实现提供一定的互通性,几个 Web 巨头一起创建了通用加密标准Common Encryption (CENC) 和通用的多媒体加密扩展Encrypted Media Extensions,以便为多个 DRM 提供商(例如,EME 可用于 Edge 平台上的 Playready 和 Chrome 平台上的 Widewine)构建一套通用的 API,这些 API 能够从 DRM 授权模块读取视频内容加密密钥用于解密。

CENC 声明了一套标准的加密和密钥映射方法,它可用于在多个 DRM 系统上解密相同的内容,只需要提供相同的密钥即可。

在浏览器内部,基于视频内容的元信息,EME 可以通过识别它使用了哪个 DRM 系统加密,并调用相应的解密模块(Content Decryption Module, CDM)解密 CENC 加密过的内容。解密模块 CDM 则会去处理内容授权相关的工作,获得密钥并解密视频内容。

CENC 没有规定授权的发放、授权的格式、授权的存储、以及使用规则和权限的映射关系等细节,这些细节的处理都由 DRM 提供商负责。

四、总结

今天我们深入了解了一下视频播放器三个层面的不同内容,这个现代播放器结构最优秀之处在于其交互部分完全和多媒体引擎逻辑部分分离,让主播可以无缝而自由灵活的定制终端用户体验,同时在多种不同终端设备上使用不同的多媒体引擎还能保证顺利播放多种不同格式的视频内容。

在 Web 平台,得益于多媒体引擎如 dash.js、Shaka Player 和 hls.js 这些趋于成熟库的帮助, MSE 和 EME 正在成为播放的新标准,同时也越来越多有影响力的厂家使用这些播放引擎。近年来,注意力也开始伸向机顶盒和互联网电视,我们也看到越来越多这样的新设备使用 MSE 来作为其底层多媒体处理引擎。我们也将持续投入更多的力量去支持这些标准。
本文由七牛云布道师何李石翻译自How Modern Video Players Work

七牛云机构号将实时分享云计算领域的技术洞见以及行业讯息,为你的创新加速。
欢迎提问&求关注 ( ´ ▽ ` )ノ
以上。

「视频直播技术详解」系列之五:延迟优化

作者:七牛云
链接:https://zhuanlan.zhihu.com/p/22663282
来源:知乎
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

关于直播的技术文章不少,成体系的不多。我们将用七篇文章,更系统化地介绍当下大热的视频直播各环节的关键技术,帮助视频直播创业者们更全面、深入地了解视频直播技术,更好地技术选型。
本系列文章大纲如下:

(一)采集

(二)处理

(三)编码和封装

(四)推流和传输

(五)延迟优化

(六)现代播放器原理

(七)SDK 性能测试模型

在上一篇推流和传输中,关于「直播的第一公里」的关键因素我们展开了详细的介绍。本篇是《解密视频直播技术》系列之五:延迟优化。

我们在很多线上和线下场合分享了如何优化直播体验,详细讲解了各部分造成低延迟和卡顿的原因和相应的优化原理。实际上,音视频的直播系统是一个复杂的工程系统,要做到非常低延迟的直播,需要复杂的系统工程优化和对各组件非常熟悉的掌握。这里面我们再分享几个简单而常用的调优技巧。

编码优化

1. 确保 Codec 开启了最低延迟的设置。Codec 一般都会有低延迟优化的开关,对于 H.264 来说其效果尤其明显。很多人可能不知道 H.264 的解码器正常情况下会在显示之前缓存一定的视频帧,对于 QCIF 分辨率大小的视频(176 × 144)一般会缓存 16 帧,对于 720P 的视频则缓存 5 帧。对于第一帧的读取来说,这是一个很大的延迟。如果你的视频不是使用 H.264 来编码压缩的,确保没有使用到 B 帧,它对延迟也会有较大的影响,因为视频中 B 帧的解码依赖于前后的视频帧,会增加延迟。

2. 编码器一般都会有码控造成的延迟,一般也叫做初始化延迟或者视频缓存检验器 VBV 的缓存大小,把它当成编码器和解码器比特流之间的缓存,在不影响视频质量的情况下可以将其设置得尽可能小也可以降低延迟。

3. 如果是仅仅优化首开延迟,可以在视频帧间插入较多的关键帧,这样客户端收到视频流之后可以尽快解码。但如果需要优化传输过程中的累计延迟,尽可能少使用关键帧也就是 I 帧(GOP 变大),在保证同等视频质量的情况下,I 帧越多,码率越大,传输所需的网络带宽越多,也就意味着累计延迟可能越大。这个优化效果可能在秒级延迟的系统中不是很明显,但是在 100 ms 甚至更低延迟的系统中就会非常明显。同时,尽量使用 AAC-LC Codec 来编码音频,HE-AAC 或者 HE-AAC V2 虽然编码效率高,但是编码所需时间更长,而产生更大体积的音频造成的传输延迟对于视频流的传输来说影响更小。

4. 不要使用视频 MJPEG 的视频压缩格式,至少使用不带 B 帧的 MPEG4 视频压缩格式(Simple profile),甚至最好使用 H.264 baseline profile(X264 还有一个「-tune zerolatency」的优化开关)。这样一个简单的优化可以降低延迟,因为它能够以更低的码率编码全帧率视频。

5. 如果使用了 FFmpeg,降低「-probesize 」和「 -analyze duration」参数的值,这两个值用于视频帧信息监测和用于监测的时长,这两个值越大对编码延迟的影响越大,在直播场景下对于视频流来说 analyzeduration 参数甚至没有必要设定。

6. 固定码率编码 CBR 可以一定程度上消除网络抖动影响,如果能够使用可变码率编码 VBR 可以节省一些不必要的网络带宽,降低一定的延迟。因此建议尽量使用 VBR 进行编码。

传输协议优化

1. 在服务端节点和节点之间尽量使用 RTMP 而非基于 HTTP 的 HLS 协议进行传输,这样可以降低整体的传输延迟。这个主要针对终端用户使用 HLS 进行播放的情况。

2. 如果终端用户使用 RTMP 来播放,尽量在靠近推流端的收流节点进行转码,这样传输的视频流比原始视频流更小。

3. 如果有必要,可以使用定制的 UDP 协议来替换 TCP 协议,省去弱网环节下的丢包重传可以降低延迟。它的主要缺点在于,基于 UDP 协议进行定制的协议的视频流的传输和分发不够通用,CDN 厂商支持的是标准的传输协议。另一个缺点在于可能出现丢包导致的花屏或者模糊(缺少关键帧的解码参考),这就要求协议定制方在 UDP 基础之上做好丢包控制。

传输网络优化

1. 我们曾经介绍过实时流传输网络,它是一种新型的节点自组织的网状传输网络,既适合国内多运营商网络条件下的传输优化,也适合众多海外直播的需求。

2. 在服务端节点中缓存当前 GOP,配合播放器端优化视频首开时间。

3. 服务端实时记录每个视频流流向每个环节时的秒级帧率和码率,实时监控码率和帧率的波动。

4. 客户端(推流和播放)通过查询服务端准实时获取当前最优节点(5 秒一次),准实时下线当前故障节点和线路。

推流、播放优化

1. 考察发送端系统自带的网络 buffer 大小,系统可能在发送数据之前缓存数据,这个参数的调优也需要找到一个平衡点。

2. 播放端缓存控制对于视频的首开延迟也有较大影响,如果仅优化首开延迟,可以在 0 缓存情况下在数据到达的时候立即解码。但如果在弱网环境下为了消除网络抖动造成的影响,设置一定的缓存也有必要,因此需要在直播的稳定性和首开延迟优化上找到平衡,调整优化缓冲区大小这个值。

3. 播放端动态 buffer 策略,这是上面播放端缓存控制的改进版本。如果只是做 0 缓存和固定大小的缓存之间进行选择找到平衡,最终还是会选择一个固定大小的缓存,这对亿级的移动互联网终端用户来说并不公平,他们不同的网络状况决定了这个固定大小的缓存并不完全合适。因此,我们可以考虑一种「动态 buffer 策略」,在播放器开启的时候采用非常小甚至 0 缓存的策略,通过对下载首片视频的耗时来决定下一个时间片的缓存大小,同时在播放过程中实时监测当前网络,实时调整播放过程中缓存的大小。这样即可做到极低的首开时间,又可能够尽量消除网络抖动造成的影响。

4. 动态码率播放策略。除了动态调整 buffer 大小的策略之外,也可以利用实时监测的网络信息来动态调整播放过程中的码率,在网络带宽不足的情况下降低码率进行播放,减少延迟。

以上,是我们在低延迟优化方面的部分技巧。实际上我们优化低延迟的时候并不是只关注「低延迟」,而是在保证其它条件不影响用户体验的情况下尽量做到低延迟,因此它的内容涉及到更多广泛的话题。而视频直播的优化也包含方方面面,这里只分享了其中经过我们实践的部分。随着实践的积累,我们接下来会在线上和线下分享更多关于视频直播甚至点播的优化技巧。

「视频直播技术详解」系列之四:推流和传输

作者:七牛云
链接:https://zhuanlan.zhihu.com/p/22567635
来源:知乎
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

关于直播的技术文章不少,成体系的不多。我们将用七篇文章,更系统化地介绍当下大热的视频直播各环节的关键技术,帮助视频直播创业者们更全面、深入地了解视频直播技术,更好地技术选型。

在上一期中,我们介绍了讲解编码和封装。 本篇是《解密视频直播技术》系列之四:推流和传输。推流是直播的第一公里,直播的推流对这个直播链路影响非常大,如果推流的网络不稳定,无论我们如何做优化,观众的体验都会很糟糕。所以也是我们排查问题的第一步,如何系统地解决这类问题需要我们对相关理论有基础的认识。

本系列文章大纲如下:

(一)采集

(二)处理

(三)编码和封装

(四)推流和传输

(五)延迟优化
(六)现代播放器原理
(七)SDK 性能测试模型

推送协议

下面就先介绍一下都有哪些推送协议,他们在直播领域的现状和优缺点。

  • RTMP
  • WebRTC
  • 基于 UDP 的私有协议

1. RTMP

RTMP 是 Real Time Messaging Protocol(实时消息传输协议)的首字母缩写。该协议基于 TCP,是一个协议族,包括 RTMP 基本协议及 RTMPT/RTMPS/RTMPE 等多种变种。RTMP 是一种设计用来进行实时数据通信的网络协议,主要用来在 Flash/AIR 平台和支持 RTMP 协议的流媒体/交互服务器之间进行音视频和数据通信。支持该协议的软件包括 Adobe Media Server/Ultrant Media Server/red5 等。

RTMP 是目前主流的流媒体传输协议,广泛用于直播领域,可以说市面上绝大多数的直播产品都采用了这个协议。

优点

  • CDN 支持良好,主流的 CDN 厂商都支持
  • 协议简单,在各平台上实现容易

缺点

  • 基于 TCP ,传输成本高,在弱网环境丢包率高的情况下问题显著
  • 不支持浏览器推送
  • Adobe 私有协议,Adobe 已经不再更新

2. WebRTC

WebRTC,名称源自网页即时通信(英语:Web Real-Time Communication)的缩写,是一个支持网页浏览器进行实时语音对话或视频对话的 API。它于 2011 年 6 月 1 日开源并在 Google、Mozilla、Opera 支持下被纳入万维网联盟的 W3C 推荐标准。

目前主要应用于视频会议和连麦中,协议分层如下:
优点

  • W3C 标准,主流浏览器支持程度高
  • Google 在背后支撑,并在各平台有参考实现
  • 底层基于 SRTP 和 UDP,弱网情况优化空间大
  • 可以实现点对点通信,通信双方延时低

缺点

  • ICE,STUN,TURN 传统 CDN 没有类似的服务提供

3. 基于 UDP 的私有协议

有些直播应用会使用 UDP 做为底层协议开发自己的私有协议,因为 UDP 在弱网环境下的优势通过一些定制化的调优可以达到比较好的弱网优化效果,但同样因为是私有协议也势必有现实问题:

优点

  • 更多空间进行定制化优化

缺点

  • 开发成本高
  • CDN 不友好,需要自建 CDN 或者和 CDN 达成协议
  • 独立作战,无法和社区一起演进

传输网络

我们推送出去的流媒体需要传输到观众,整个链路就是传输网络,类比货运物流就是从出发地到目的地见的所有路程了,如果道路的容量不够,会引发堵车也就是网络拥塞,这时我们会改变路程也就是所谓的智能调度,但是传输网络会站在全局的角度进行调度,所以会比原子世界的调度有更好的效果,可以想象有一个上帝在天空中俯视出发地和目的地间的所有的路况信息,而且还是实时的,然后给出你一条明路,何等的神奇,但这些我们在 LiveNet 中都已经实现了。

这里先回顾一下传统的内容分发网络。

1. 为什么要有内容分发网络,内容分发网络的由来

互联网起源于美国军方的一个内部网络,Tim Berners-Lee 是互联网发明者之一,他很早就预见到在不久的将来网络拥塞将成为互联网发展的最大障碍,于是他提出了一个学术难题,要发明一种全新的、从根本上解决问题的方法来实现互联网内容的无拥塞分发,这项学术难题最终催生出一种革新性的互联网服务——CDN 。当时 Berners-Lee 博士隔壁是 Tom Leighton 教授的办公室,一位麻省理工学院应用数学教授,他被 Berners-Lee 的挑战激起了兴趣。Letghton 最终解决了这个难题并开始自己的商业计划,成立了 Akamai 公司,成为世界上第一家 CDN 公司。

2. 传统 CDN 的架构
上图是一个典型的 CDN 系统的三级部署示意图,节点是 CDN 系统中的最基本部署单元,分为三级部署,中心节点、区域节点和边缘节点,最上面一级是中心节点,中间一级是区域节点,边缘节点地理位置分散,为用户提供就近的内容访问服务。
下面介绍一下 CDN 节点的分类,主要分成两大类,骨干节点和 POP 节点,骨干节点又分为中心节点和区域节点。

  • 骨干节点:中心节点,区域节点
  • POP节点:边缘节点

逻辑上来讲,骨干节点主要负责内容分发和边缘节点未命中时进行回源,POP 节点主要负责提供给用户就近的内容访问服务。但如果 CDN 网络规模较大,边缘节点直接向中心节点回源会给中间层的核心设备造成的压力过大,在物理上引入区域节点,负责一个地理区域的管理,保存部分热点数据。

3. 直播传输网络有别于传统 CDN 的痛点

随着 Live 时代的到来,直播成为当前 CDN 厂商的又一个主要的战场,那么 Live 时代 CDN 需要支持什么样的服务呢?

  • 流媒体协议的支持,包括 RTMP,HLS ,HTTP-FLV 等。
  • 首屏秒开,从用户点击到播放控制在秒级以内
  • 1~3 延迟控制,从推流端到播放端,延迟控制在 1~3 秒之间
  • 全球全网智能路由,可以利用整个 CDN 网络内的所有节点为某一单一用户服务,不受地域限制。随着全球一体化进程不断推进,跨区域、跨国家、跨洲的直播正变为常态,很可能主播在欧美,而用户在亚洲。
  • 天级别的节点按需增加,中国公司出海已成大势,CDN 需要更多的海外节点,如今比拼的更多的是海外节点可以快速部署,从提出节点增加需求到节点入网提供服务,需要达到一天之内,对 CDN 运维和规划提出非常高的要求。原有的月级别规划和入网满足不了先进的要求。

4. 传统 CDN 的链路路由

CDN 基于树状网络拓扑结构,每一层都有 GSLB (Global Server Load Balancing) 用于同一层内的多个 CDN 节点负载均衡,这样有什么好处呢?

前面提到的众多 CDN 的应用场景中,网页加速、视频加速、文件传输加速,都是同时依赖 GSLB 和 Cache 系统的,Cache 系统是整个 CDN 系统中的成本所在,设计树形结构可以最大化的节省 Cache 系统的资本投入。因为只有中心节点需要保持机会所有的 Cache 副本,向下逐级减少,到了边缘节点只需要少量的热点 Cache 就可以命中大部分 CDN 访问请求,这样极大的降低了 CDN 网络的成本,也符合当时 CDN 用户的需求,可谓双赢。

但是到了 Live 时代,直播业务是流式业务,很少涉及到 Cache 系统,基本都是播完就可以释放掉存储资源,即使因为政策原因有存储的需求也都是冷存储,对于存储的投入相对非常低廉,而且不要求存储在所有节点中,只要保证数据可回溯,可用即可。

我们看看树状网络拓扑,用户的链路选择数量是有限的,如下图,用户在某一个区域内可选择的链路数是:2 * 5 = 10
用户在某一区域内,则 GSLB (通常在边缘节点这一层是 Smart DNS)会把用户路由到该区域内的某个边缘节点,上一层又会路由到某个区域节点(这里的 GSLB 通常是内部的负载均衡器),最后又回溯到中心节点,中心节点会链接源站。

这里的假设是:

  • 用户能访问的最快节点一定是该区域内的边缘节点,如果该区域没有边缘节点则最快的一定是逻辑相邻的区域内的边缘节点。
  • 边缘节点能访问的最快节点一定是该区域内的区域节点,一定不会是其他区域的节点。
  • 区域节点到中心节点一定是最快的,这个链路的速度和带宽都是最优的。

但实际真的如此么?引入了如此多的假设真的正确么?
实际上就算理论上我们可以证明以上假设有效,但是节点规划和区域配置大都依赖于人的设计和规划,我们知道人多是不靠谱的,而且就算当时区域规划正确,谁能保证这些静态的网络规划不会因为铺设了一条光纤或者因为某些 IDC 压力过大而发生了改变呢?所以我们可以跳出树状网络拓扑结构的桎梏,探索新的适合直播加速的网络拓扑结构。

为了摆脱有限的链路路由线路限制,激活整理网络的能力,我们可以把上述的节点变成网状网络拓扑结构:

我们看到一旦我们把网络结构改成了网状结构,则用户的可选择链路变为:无向图的指定两点间的所有路径,学过图论的同学都知道,数量惊人。

系统可以通过智能路由选择任何一个最快的链路而不用依赖于系统部署时过时的人工规划,无论是某些链路间增加了光纤或者某个 IDC 压力过大都可以实时的反映到整理网络中,帮助用户实时推倒出最优链路。这时我们可以去掉前面的一些假设,通过机器而不是人类来时实时规划网络的链路路由,这种实时大规模的计算任务天生就不是人类的强项,我们应该交给更适合的物种。

5. CDN 的扩容

前面提到中国公司的出海已成大势,CDN 海外节点的需求越来越大,遇到这种情况需要 CDN 厂商在新的区域部署新的骨干网和边缘节点,需要做详细的网络规划。时代发生变化,原来 CDN 用户都是企业级用户,本身业务线的迭代周期较长,有较长时间的规划,留给 CDN 厂商的时间也比较多。而互联网公司讲究的是速度,双周迭代已成常态,这里面涉及到成本和响应速度的矛盾,如果提前部署节点可以更好的为这些互联网公司服务,但是有较高的成本压力,反之则无法响应这些快速发展的互联网公司。

理想情况是,用户提出需求,CDN 厂商内部评估,当天给出反馈,当天部署,客户当天就可以测试新区域的新节点。怎么解决?

答案是基于网状拓扑结构的对等网络,在网状拓扑结构中每个节点都是 Peer ,逻辑上每个节点提供的服务对等,不需要按区域设计复杂的网络拓扑结构,节点上线后不需要复杂的开局过程,直接上线注册节点信息,就可以对用户提供服务了,结合虚拟化技术前后时间理论上可以控制在一天之内。

6. 回归本质:LiveNet

我们知道最早的互联网就是网状拓扑结构,后来才慢慢加入了骨干网来解决各种各样的问题,我们是时候该回归本质,拥抱下一代 Live 分发网络:LiveNet 。总结前面的讨论,我们发现 Live 时代我们需要的内容分发网络是:

  • 对 Cache 的要求没有以前那么高
  • 对实时性的要求非常高
  • 对节点运维的要求高,要更智能,尽量减少人工干预
  • 对扩容这种运维事件响应度要求非常高

要做到如上几点,我们需要:

  • 去中心化,网状拓扑
  • 全球全网调度
  • 节点无状态,节点对等
  • 智能运维

以上这些就是 LiveNet 设计时候的斟酌,让运维更自动化,系统运行高度自治,依赖机器计算而不是人工判断,下面分别介绍一下。

1)去中心,网状拓扑
网状拓扑结构是设计的根本和基础,只有看清了我们对 Cache 需求的降低,网状拓扑结构才更有优势。

2)全球全网调度
基于全球一张网,不在受限于区域网络调度,将调度的范围从区域网络扩展到全球,全网内的节点都可以响应用户的请求,参与链路路由,不再先由人工假设选定一部分节点进行路由,去掉人工干预,让整个系统更智能。

3)节点无状态,节点对等
LiveNet 节点无状态和节点对等都方便了运维,去掉了区域概念后的全球一张网让整个拓扑结构变的异常复杂,如果各个节点间有先后依赖关系,势必让运维成为噩梦,需要专有的服务编排系统,同时也给扩容带来困难,需要运维人员设计复杂的扩容方案,需要预演多次才敢在复杂的网络拓扑中扩容。当时如果节点本身对等且无状态,则运维和扩容都变的容易很多。

但整个系统在运行过程中还是会一些状态和数据需要保持,比如某些 Live 内容需要落地回放的需求,这些通过久经考验的七牛云存储来存储。

4)智能运维
智能运维建立在以上的「网状拓扑结构的对等网络」的基础上会变的容易的多。可以方便的下线有问题的节点而不影响整个 LiveNet 网络,可以方便快速的上线新节点,提升系统容量。通过节点的数据分析可以更好的了解整个网络的整体状态。

下面列举部分 LiveNet 采用的智能运维方案,让内容分发网络再次升级,以符合 Live 时代的要求。

  • 监控节点健康状况,实时下线有问题的节点
  • Failover 机制,保证服务一直可用
  • 快速扩容

7.LiveNet VS P2P

最后我们和 P2P 网络做一个对比:
我们发现 P2P 方案,节点的可控性和链路的稳定性上还有很大提升空间,比较适合在实时性要求不高的场景使用、适合长尾需求,在 Live 的场景下面多是对实时性要求比较高的重度用户,无法忍受频繁的 FailOver 和节点质量参差不齐带来的网络抖动,但是如果是文件分发就比较适合用这种混合方案,可以有效降低 CDN 厂商成本,利用共享经济提高资源利用率。

这篇介绍了推送和传输网络部分,我们已经把流媒体送到了观众的终端中,下一步就是把它展现在屏幕上了,想了解这部分内容请继续关注我们的下一篇内容。

七牛云机构号将实时分享云计算领域的技术洞见以及行业讯息,为你的创新加速。
欢迎提问&求关注 ( ´ ▽ ` )ノ
以上。

视频直播技术详解」系列之三:编码和封装

作者:七牛云
链接:https://zhuanlan.zhihu.com/p/22544282
来源:知乎
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

关于直播的技术文章不少,成体系的不多。我们将用七篇文章,更系统化地介绍当下大热的视频直播各环节的关键技术,帮助视频直播创业者们更全面、深入地了解视频直播技术,更好地技术选型。

视频编码是视频直播技术系列文章的第三篇,是本系列一个非常重要的部分,是移动开发必修的基础课程,本篇文章从理论到实践一网打尽主流编码器。

如果把整个流媒体比喻成一个物流系统,那么编解码就是其中配货和装货的过程,这个过程非常重要,它的速度和压缩比对物流系统的意义非常大,影响物流系统的整体速度和成本。同样,对流媒体传输来说,编码也非常重要,它的编码性能、编码速度和编码压缩比会直接影响整个流媒体传输的用户体验和传输成本。

本系列文章大纲如下,想复习之前文章的直接点击直达链接:

(一)采集

(二)处理

(三)编码和封装

(四)推流和传输

(五)延迟优化
(六)现代播放器原理
(七)SDK 性能测试模型

视频编码的意义

  • 原始视频数据存储空间大,一个 1080P 的 7 s 视频需要 817 MB
  • 原始视频数据传输占用带宽大,10 Mbps 的带宽传输上述 7 s 视频需要 11 分钟

而经过 H.264 编码压缩之后,视频大小只有 708 k ,10 Mbps 的带宽仅仅需要 500 ms ,可以满足实时传输的需求,所以从视频采集传感器采集来的原始视频势必要经过视频编码。

基本原理

那为什么巨大的原始视频可以编码成很小的视频呢?这其中的技术是什么呢?
核心思想就是去除冗余信息:

  • 空间冗余:图像相邻像素之间有较强的相关性
  • 时间冗余:视频序列的相邻图像之间内容相似
  • 编码冗余:不同像素值出现的概率不同
  • 视觉冗余:人的视觉系统对某些细节不敏感
  • 知识冗余:规律性的结构可由先验知识和背景知识得到

视频本质上讲是一系列图片连续快速的播放,最简单的压缩方式就是对每一帧图片进行压缩,例如比较古老的 MJPEG 编码就是这种编码方式,这种编码方式只有帧内编码,利用空间上的取样预测来编码。形象的比喻就是把每帧都作为一张图片,采用 JPEG 的编码格式对图片进行压缩,这种编码只考虑了一张图片内的冗余信息压缩,如图 1,绿色的部分就是当前待编码的区域,灰色就是尚未编码的区域,绿色区域可以根据已经编码的部分进行预测(绿色的左边,下边,左下等)。
图1
但是帧和帧之间因为时间的相关性,后续开发出了一些比较高级的编码器可以采用帧间编码,简单点说就是通过搜索算法选定了帧上的某些区域,然后通过计算当前帧和前后参考帧的向量差进行编码的一种形式,通过下面两个图 2 连续帧我们可以看到,滑雪的同学是向前位移的,但实际上是雪景在向后位移,P 帧通过参考帧(I 或其他 P 帧)就可以进行编码了,编码之后的大小非常小,压缩比非常高。
图 2
可能有同学对这两张图片怎么来的感兴趣,这里用了 FFmpeg 的两行命令来实现,具体 FFmpeg 的更多内容请看后续章节:

  • 第一行生成带有移动矢量的视频
  • 第二行把每一帧都输出成图片
ffmpeg  -flags2 +export_mvs -i tutu.mp4 -vf codecview=mv=pf+bf+bb tutudebug2.mp4

ffmpeg -i tutudebug2.mp4 'tutunormal-%03d.bmp'

除了空间冗余和时间冗余的压缩,主要还有编码压缩和视觉压缩,下面是一个编码器主要的流程图:
图 3
图 4
图 3、图 4 两个流程,图 3 是帧内编码,图 4 是帧间编码,从图上看到的主要区别就是第一步不相同,其实这两个流程也是结合在一起的,我们通常说的 I 帧和 P 帧就是分别采用了帧内编码和帧间编码。

编码器的选择

前面梳理了一下编码器的原理和基本流程,编码器经历了数十年的发展,已经从开始的只支持帧内编码演进到现如今的 H.265 和 VP9 为代表的新一代编码器,就目前一些常见的编码器进行分析,带大家探索一下编码器的世界。

1) H.264

简介

H.264/AVC 项目意图创建一种视频标准。与旧标准相比,它能够在更低带宽下提供优质视频(换言之,只有 MPEG-2,H.263 或 MPEG-4 第 2 部分的一半带宽或更少),也不增加太多设计复杂度使得无法实现或实现成本过高。另一目的是提供足够的灵活性以在各种应用、网络及系统中使用,包括高、低带宽,高、低视频分辨率,广播,DVD 存储,RTP/IP 网络,以及 ITU-T 多媒体电话系统。

H.264/AVC 包含了一系列新的特征,使得它比起以前的编解码器不但能够更有效的进行编码,还能在各种网络环境下的应用中使用。这样的技术基础让 H.264 成为包括 YouTube 在内的在线视频公司采用它作为主要的编解码器,但是使用它并不是一件很轻松的事情,理论上讲使用 H.264 需要交纳不菲的专利费用。

专利许可

和 MPEG-2 第一部分、第二部分,MPEG-4第二部分一样,使用 H.264/AVC 的产品制造商和服务提供商需要向他们的产品所使用的专利的持有者支付专利许可费用。这些专利许可的主要来源是一家称为 MPEG-LA LLC 的私有组织,该组织和 MPEG 标准化组织没有任何关系,但是该组织也管理著 MPEG-2 第一部分系统、第二部分视频、MPEG-4 第二部分视频和其它一些技术的专利许可。

其他的专利许可则需要向另一家称为 VIA Licensing 的私有组织申请,这家公司另外也管理偏向音频压缩的标准如 MPEG-2 AAC 及 MPEG-4 Audio 的专利许可。

H.264 的开源实现

  • openh264
  • x264

openh264 是思科实现的开源 H.264 编码,虽然 H.264 需要交纳不菲的专利费用,但是专利费有一个年度上限,思科把 OpenH264 实现的年度专利费交满后,OpenH264 事实上就可以免费自由的使用了。

x264 x264是一个采用GPL授权的视频编码自由软件。x264 的主要功能在于进行 H.264/MPEG-4 AVC 的视频编码,而不是作为解码器(decoder)之用。

除去费用问题比较来看:

  • openh264 CPU 的占用相对 x264低很多
  • openh264 只支持 baseline profile,x264 支持更多 profile

2) HEVC/H.265

简介

高效率视频编码(High Efficiency Video Coding,简称HEVC)是一种视频压缩标准,被视为是 ITU-T H.264/MPEG-4 AVC 标准的继任者。2004 年开始由 ISO/IEC Moving Picture Experts Group(MPEG)和 ITU-T Video Coding Experts Group(VCEG)作为 ISO/IEC 23008-2 MPEG-H Part 2 或称作 ITU-T H.265 开始制定。第一版的 HEVC/H.265 视频压缩标准在 2013 年 4 月 13 日被接受为国际电信联盟(ITU-T)的正式标准。HEVC 被认为不仅提升视频质量,同时也能达到 H.264/MPEG-4 AVC 两倍之压缩率(等同于同样画面质量下比特率减少了 50%),可支持 4K 分辨率甚至到超高清电视(UHDTV),最高分辨率可达到 8192×4320(8K分辨率)。

H.265 的开源实现

  • libde265
  • x265

libde265 HEVC 由 struktur 公司以开源许可证 GNU LesserGeneral Public License (LGPL) 提供,观众可以较慢的网速下欣赏到最高品质的影像。跟以前基于H.264标准的解码器相比,libde265 HEVC 解码器可以将您的全高清内容带给多达两倍的受众,或者,减少 50% 流媒体播放所需要的带宽。高清或者 4K/8K 超高清流媒体播放,低延迟/低带宽视频会议,以及完整的移动设备覆盖。具有「拥塞感知」视频编码的稳定性,十分适合应用在 3/4G 和 LTE 网络。

专利许可

HEVC Advance 要求所有包括苹果、YouTube、Netflix、Facebook、亚马逊等使用 H.265 技术的内容制造商上缴内容收入的 0.5%作为技术使用费,而整个流媒体市场每年达到约 1000 亿美元的规模,且不断增长中,征收 0.5%绝对是一笔庞大的费用。而且他们还没有放过设备制造商,其中电视厂商需要支付每台 1.5 美元、移动设备厂商每台 0.8 美元的专利费。他们甚至没有放过蓝光设备播放器、游戏机、录像机这样的厂商,这些厂商必须支付每台 1.1 美元的费用。最无法令人接受的是,HEVC Advance 的专利使用权追溯到了厂商的「」”,意思是之前已经发售的产品依然要追缴费用。

x265 是由 MulticoreWare 开发,并开源。采用 GPL 协议,但是资助这个项目的几个公司组成了联盟可以在非 GPL 协议下使用这个软件。

3) VP8

简介

VP8 是一个开放的视频压缩格式,最早由 On2 Technologies 开发,随后由 Google 发布。同时 Google 也发布了 VP8 编码的实做库:libvpx,以 BSD 授权条款的方式发行,随后也附加了专利使用权。而在经过一些争论之后,最终 VP8 的授权确认为一个开放源代码授权。

目前支持 VP8 的网页浏览器有 Opera、Firefox 和 Chrome。

专利许可

2013 年三月,Google 与 MPEG LA 及 11 个专利持有者达成协议,让Google 获取 VP8 以及其之前的 VPx 等编码所可能侵犯的专利授权,同时 Google 也可以无偿再次授权相关专利给 VP8 的用户,此协议同时适用于下一代 VPx 编码。至此 MPEG LA 放弃成立 VP8 专利集中授权联盟,VP8 的用户将可确定无偿使用此编码而无须担心可能的专利侵权授权金的问题。

VP8 的开源实现

  • libvpx

libvpx 是 VP8 的唯一开源实现,由 On2 Technologies 开发,Google 收购后将其开放源码,License 非常宽松可以自由使用。

4) VP9

简介

VP9 的开发从 2011 年第三季开始,目标是在同画质下,比 VP8 编码减少 50%的文件大小,另一个目标则是要在编码效率上超越 HEVC 编码。

2012 年 12 月 13 日,Chromium 浏览器加入了 VP9 编码的支持。Chrome 浏览器则是在 2013 年 2 月 21 日开始支持 VP9 编码的视频播放。

Google 宣布会在 2013 年 6 月 17 日完成 VP9 编码的制定工作,届时Chrome 浏览器将会把 VP9 编码默认引导。2014 年 3 月 18 日,Mozilla 在 Firefox 浏览器中加入了 VP9 的支持。

2015 年 4 月 3 日,谷歌发布了 libvpx1.4.0 增加了对 10 位和 12 位的比特深度支持、4:2:2 和 4:4:4 色度抽样,并 VP9 多核心编/解码。

专利许可

VP9 是一个开放格式、无权利金的视频编码格式。

VP9 的开源实现

  • libvpx

libvpx 是 VP9 的唯一开源实现,由 Google 开发维护,里面有部分代码是 VP8 和 VP9 公用的,其余分别是 VP8 和 VP9 的编解码实现。

VP9 和 H.264 和 HEVC 比较

CodecHEVCx264vp9HEVC-42.2%32.6%x26475.8%18.5%vp948.3%-14.6%CodecHEVC vs. VP9(in %)VP9 vs. x264 (in %)Total Average61239399引用 Comparative Assessment of H.265/MPEG-HEVC, VP9, and
H.264/MPEG-AVC Encoders for Low-Delay Video Applications 这篇比较新的论文对,低延迟视频进行编码的测试结果。

HEVC 和 H.264 在不同分辨率下的比较

跟 H.264/MPEG-4 相比,HEVC 的平均比特率减低值为:

分辨率480P720P1080P4K UHDHEVC52%56%62%64%可见码率下降了 60% 以上。

  • HEVC (H.265) 对 VP9 和 H.264 在码率节省上有较大的优势,在相同 PSNR 下分别节省了 48.3% 和 75.8%。
  • H.264 在编码时间上有巨大优势,对比 VP9 和 HEVC(H.265) ,HEVC 是 VP9 的6倍,VP9 是 H.264 的将近 40 倍

5) FFmpeg

谈到视频编码相关内容就不得不提一个伟大的软件包 — FFmpeg。

FFmpeg 是一个自由软件,可以运行音频和视频多种格式的录影、转换、流功能,包含了 libavcodec ——这是一个用于多个项目中音频和视频的解码器库,以及 libavformat ——一个音频与视频格式转换库。

FFmpeg 这个单词中的 FF 指的是 Fast Forward。有些新手写信给 FFmpeg 的项目负责人,询问 FF 是不是代表 Fast Free 或者 Fast Fourier 等意思,FFmpeg 的项目负责人回信说:「Just for the record, the original meaning of FF in FFmpeg is Fast Forward…」

这个项目最初是由 Fabrice Bellard 发起的,而现在是由 Michael Niedermayer 在进行维护。许多FFmpeg的开发者同时也是 MPlayer 项目的成员,FFmpeg 在 MPlayer 项目中是被设计为服务器版本进行开发。

FFmpeg 下载地址是 : FFmpeg Download

  • 可以浏览器输入下载,目前支持 Linux ,Mac OS,Windows 三个主流的平台,也可以自己编译到 Android 或者 iOS 平台。
  • 如果是 Mac OS ,可以通过 brew 安装 brew install ffmpeg –with-libvpx –with-libvorbis –with-ffplay

我们可以用 FFmpeg 来做哪些有用有好玩的事情呢?通过一系列小实验来带大家领略 FFmpeg 的神奇和强大。

FFmpeg 录屏

通过一个小例子看一下怎么在 Mac OS 下面使用 FFmpeg 进行录屏:

输入:

ffmpeg -f avfoundation -list_devices true -i ""

输出:

[AVFoundation input device @ 0x7fbec0c10940] AVFoundation video devices:
[AVFoundation input device @ 0x7fbec0c10940] [0] FaceTime HD Camera
[AVFoundation input device @ 0x7fbec0c10940] [1] Capture screen 0
[AVFoundation input device @ 0x7fbec0c10940] [2] Capture screen 1
[AVFoundation input device @ 0x7fbec0c10940] AVFoundation audio devices:
[AVFoundation input device @ 0x7fbec0c10940] [0] Built-in Microphone

给出了当前设备支持的所有输入设备的列表和编号,我本地有两块显示器,所以 1 和 2 都是我屏幕,可以选择一块进行录屏。

查看当前的 H.264 编解码器:

输入:

ffmpeg -codecs | grep 264

输出:

 DEV.LS h264                 H.264 / AVC / MPEG-4 AVC / MPEG-4 part 10 (decoders: h264 h264_vda ) (encoders: libx264 libx264rgb )

查看当前的 VP8 编解码器:

输入:

ffmpeg -codecs | grep vp8

输出:

  DEV.L. vp8                  On2 VP8 (decoders: vp8 libvpx ) (encoders: libvpx )

可以选择用 vp8 或者 h264 做编码器

ffmpeg -r 30 -f avfoundation -i 1 -vcodec vp8 -quality realtime screen2.webm
# -quality realtime 用来优化编码器,如果不加在我的 Air 上帧率只能达到 2

or

ffmpeg -r 30 -f avfoundation -i 1 -vcodec h264 screen.mp4

然后用 ffplay 播放就可以了

ffplay screen.mp4

or

ffplay screen2.webp

FFmpeg 视频转换成 gif

有一个特别有用的需求,在网上发现了一个特别有趣的视频想把它转换成一个动态表情,作为一个 IT 从业者,我第一个想到的不是下载一个转码器,也不是去找一个在线转换网站,直接利用手边的工具 FFmpeg,瞬间就完成了转码:

ffmpeg -ss 10 -t 10  -i tutu.mp4  -s 80x60  tutu.gif
## -ss 指从 10s 开始转码,-t 指转换 10s 的视频 -s

FFmpeg 录制屏幕并直播

可以继续扩展例子1,直播当前屏幕的内容,向大家介绍一下怎么通过几行命令搭建一个测试用的直播服务:

Step 1:首先安装 docker:
访问 Docker Download ,按操作系统下载安装。

Step 2:下载 nginx-rtmp 镜像:

docker pull chakkritte/docker-nginx-rtmp

Step 3:创建 nginx html 路径,启动 docker-nginx-rtmp

mkdir ~/rtmp

docker run -d -p 80:80 -p 1935:1935 -v ~/rtmp:/usr/local/nginx/html chakkritte/docker-nginx-rtmp

Step 4:推送屏幕录制到 nignx-rtmp

ffmpeg -y -loglevel warning -f avfoundation -i 2 -r 30 -s 480x320 -threads 2 -vcodec libx264  -f flv rtmp://127.0.0.1/live/test

Step 5:用 ffplay 播放

ffplay rtmp://127.0.0.1/live/test

总结一下,FFmpeg 是个优秀的工具,可以通过它完成很多日常的工作和实验,但是距离提供真正可用的流媒体服务、直播服务还有非常多的工作要做,这方面可以参考七牛云发布的 七牛直播云服务

封装

介绍完了视频编码后,再来介绍一些封装。沿用前面的比喻,封装可以理解为采用哪种货车去运输,也就是媒体的容器。

所谓容器,就是把编码器生成的多媒体内容(视频,音频,字幕,章节信息等)混合封装在一起的标准。容器使得不同多媒体内容同步播放变得很简单,而容器的另一个作用就是为多媒体内容提供索引,也就是说如果没有容器存在的话一部影片你只能从一开始看到最后,不能拖动进度条(当然这种情况下有的播放器会话比较长的时间临时创建索引),而且如果你不自己去手动另外载入音频就没有声音,下面介绍几种常见的封装格式和优缺点:

  1. AVI 格式(后缀为 .AVI): 它的英文全称为 Audio Video Interleaved ,即音频视频交错格式。它于 1992 年被 Microsoft 公司推出。
    这种视频格式的优点是图像质量好。由于无损AVI可以保存 alpha 通道,经常被我们使用。缺点太多,体积过于庞大,而且更加糟糕的是压缩标准不统一,最普遍的现象就是高版本 Windows 媒体播放器播放不了采用早期编码编辑的AVI格式视频,而低版本 Windows 媒体播放器又播放不了采用最新编码编辑的AVI格式视频,所以我们在进行一些AVI格式的视频播放时常会出现由于视频编码问题而造成的视频不能播放或即使能够播放,但存在不能调节播放进度和播放时只有声音没有图像等一些莫名其妙的问题。
  2. DV-AVI 格式(后缀为 .AVI): DV的英文全称是 Digital Video Format ,是由索尼、松下、JVC 等多家厂商联合提出的一种家用数字视频格式。
    数字摄像机就是使用这种格式记录视频数据的。它可以通过电脑的 IEEE 1394 端口传输视频数据到电脑,也可以将电脑中编辑好的的视频数据回录到数码摄像机中。这种视频格式的文件扩展名也是 avi。电视台采用录像带记录模拟信号,通过 EDIUS 由IEEE 1394端口采集卡从录像带中采集出来的视频就是这种格式。
  3. QuickTime File Format 格式(后缀为 .MOV): 美国Apple公司开发的一种视频格式,默认的播放器是苹果的QuickTime。
    具有较高的压缩比率和较完美的视频清晰度等特点,并可以保存alpha通道。
  4. MPEG 格式(文件后缀可以是 .MPG .MPEG .MPE .DAT .VOB .ASF .3GP .MP4等) : 它的英文全称为 Moving Picture Experts Group,即运动图像专家组格式,该专家组建于1988年,专门负责为 CD 建立视频和音频标准,而成员都是为视频、音频及系统领域的技术专家。
    MPEG 文件格式是运动图像压缩算法的国际标准。MPEG 格式目前有三个压缩标准,分别是 MPEG-1、MPEG-2、和MPEG-4 。MPEG-1、MPEG-2 目前已经使用较少,着重介绍 MPEG-4,其制定于1998年,MPEG-4 是为了播放流式媒体的高质量视频而专门设计的,以求使用最少的数据获得最佳的图像质量。目前 MPEG-4 最有吸引力的地方在于它能够保存接近于DVD画质的小体积视频文件。
  5. WMV 格式(后缀为.WMV .ASF): 它的英文全称为Windows Media Video,也是微软推出的一种采用独立编码方式并且可以直接在网上实时观看视频节目的文件压缩格式。
    WMV格式的主要优点包括:本地或网络回放,丰富的流间关系以及扩展性等。WMV 格式需要在网站上播放,需要安装 Windows Media Player( 简称 WMP ),很不方便,现在已经几乎没有网站采用了。
  6. Real Video 格式(后缀为 .RM .RMVB): Real Networks 公司所制定的音频视频压缩规范称为Real Media。
    用户可以使用 RealPlayer 根据不同的网络传输速率制定出不同的压缩比率,从而实现在低速率的网络上进行影像数据实时传送和播放。RMVB 格式:这是一种由RM视频格式升级延伸出的新视频格式,当然性能上有很大的提升。RMVB 视频也是有着较明显的优势,一部大小为700MB左右的 DVD 影片,如果将其转录成同样品质的 RMVB 格式,其个头最多也就 400MB 左右。大家可能注意到了,以前在网络上下载电影和视频的时候,经常接触到 RMVB 格式,但是随着时代的发展这种格式被越来越多的更优秀的格式替代,著名的人人影视字幕组在2013年已经宣布不再压制 RMVB 格式视频。
  7. Flash Video 格式(后缀为 .FLV):由 Adobe Flash 延伸出来的的一种流行网络视频封装格式。随着视频网站的丰富,这个格式已经非常普及。
  8. Matroska 格式(后缀为 .MKV):是一种新的多媒体封装格式,这个封装格式可把多种不同编码的视频及16条或以上不同格式的音频和语言不同的字幕封装到一个 Matroska Media 档内。它也是其中一种开放源代码的多媒体封装格式。Matroska 同时还可以提供非常好的交互功能,而且比 MPEG 的方便、强大。
  9. MPEG2-TS 格式 (后缀为 .ts)(Transport Stream“传输流”;又称MTS、TS)是一种传输和存储包含音效、视频与通信协议各种数据的标准格式,用于数字电视广播系统,如DVB、ATSC、IPTV等等。
    MPEG2-TS 定义于 MPEG-2 第一部分,系统(即原来之ISO/IEC标准13818-1或ITU-T Rec. H.222.0)。
    Media Player Classic、VLC 多媒体播放器等软件可以直接播放MPEG-TS文件。

目前,我们在流媒体传输,尤其是直播中主要采用的就是 FLV 和 MPEG2-TS 格式,分别用于 RTMP/HTTP-FLV 和 HLS 协议。

下一期我们将系统讲解视频直播的推流和传输,敬请期待~

「视频直播技术详解」系列之二:处理

作者:七牛云
链接:https://zhuanlan.zhihu.com/p/22527424
来源:知乎
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

七牛云于 6 月底发布了一个针对视频直播的实时流网络 LiveNet 和完整的直播云解决方案,很多开发者对这个网络和解决方案的细节和使用场景非常感兴趣。

我们将用七篇文章,更系统化地介绍当下大热的视频直播各环节的关键技术,帮助视频直播创业者们更全面、深入地了解视频直播技术,更好地技术选型。

本系列文章大纲如下,想复习之前文章的直接点击直达链接:
(一)采集

(二)处理

(三)编码和封装

(四)推流和传输

(五)延迟优化
(六)现代播放器原理
(七)SDK 性能测试模型

在上期采集中,我们介绍了视频采集针对音频采集和图像采集以及它们分别对应两种完全不同的输入源和数据格式。 本篇是《解密视频直播技术》系列之二:处理。我们将讲解常见视频处理功能如美颜、视频水印、滤镜、连麦等。

视频或者音频完成采集之后得到原始数据,为了增强一些现场效果或者加上一些额外的效果,我们一般会在将其编码压缩前进行处理,比如打上时间戳或者公司 Logo 的水印,祛斑美颜和声音混淆等处理。在主播和观众连麦场景中,主播需要和某个或者多个观众进行对话,并将对话结果实时分享给其他所有观众,连麦的处理也有部分工作在推流端完成。

开放式设计

如上图所示,处理环节中分为音频和视频处理,音频处理中具体包含混音、降噪和声音特效等处理,视频处理中包含美颜、水印、以及各种自定义滤镜等处理。对于七牛这样的直播云服务来说,为了满足所有客户的需求,除了要提供这些「标准」处理功能之外,我们还需要将该模块设计成可自由接入自定义处理功能的方式。

iOS SDK 地址:github.com/pili-enginee
Android SDK 地址:GitHub – pili-engineering/PLDroidMediaStreaming

常见视频处理功能

1.美颜

都说「80% 的主播没有美颜根本没法看」,美颜是直播产品中最常见的功能之一。最近准备在香港上市的美图公司的主打产品就是美颜相机和美拍,有媒体戏称其会冲击化妆品行业,其实就是美颜的效果的功劳,让美女主播们不化妆也可以自信的直播,而美颜相机的用户则可以拍出「更好的自己」。

美颜的主要原理是通过「磨皮+美白」来达到整体美颜的效果。磨皮的技术术语是「去噪」,也即对图像中的噪点进行去除或者模糊化处理,常见的去噪算法有均值模糊、高斯模糊和中值滤波等。当然, 由于脸部的每个部位不尽相同,脸上的雀斑可能呈现出眼睛黑点的样子,对整张图像进行「去噪」处理的时候不需要将眼睛也去掉,因此这个环节中也涉及到人脸和皮肤检测技术。

在我们的直播系统中提供的 iOS 和 Android 推流 SDK 中内置了美颜功能,你可以根据自己的需要选择开关美颜功能,并且能够自由调节包括美颜,美白,红润等在内的参数。其中 iOS 端 SDK PLCameraStreamingKit 的参数设置如下:
1)按照默认参数开启或关闭美颜:

-(void)setBeautifyModeOn:(BOOL)beautifyModeOn;

2)设置美颜程度,范围为 0 ~ 1:

-(void)setBeautify:(CGFloat)beautify;

3)设置美白程度,范围为 0 ~ 1

-(void)setWhiten:(CGFloat)whiten;

4)设置红润程度,范围为 0 ~ 1

-(void)setRedden:(CGFloat)redden;

2.视频水印

水印是图片和视频内容中常见的功能之一,它可用于简单是版权保护,或者进行广告设置。处于监管的需求,国家相关部门也规定视频直播过程中必须打上水印,同时直播的视频必须录制存储下来保存一定的时间,并在录制的视频上打上水印。

视频水印包括播放器水印和视频内嵌水印两种方式可供选择,对于播放器水印来说,如果没有有效的防盗措施,对于没有播放鉴权的推流,客户端拿到直播流之后可以在任何一个不带水印的播放器里面播放,因此也就失去了视频保护的能力。综合考虑云端录制对于水印的需求,我们一般会选择「视频内嵌水印」的方式打水印。

在我们的直播系统中提供的 iOS 和 Android 推流 SDK 中也内置了水印功能,你可以根据自己的需要添加水印或移除水印,并且能够自由设置水印的大小和位置。其中 iOS 端 SDK PLCameraStreamingKit 的参数设置如下:

1)添加水印

-(void)setWaterMarkWithImage:(UIImage *)wateMarkImage position:(CGPoint)position;

该方法将为直播流添加一个水印,水印的大小由 wateMarkImage 的大小决定,位置由 position 决定,需要注意的是这些值都是以采集数据的像素点为单位的。例如我们使用AVCaptureSessionPreset1280x720 进行采集,同时 wateMarkImage.size 为 (100, 100) 对应的origin 为 (200, 300),那么水印的位置将在大小为 1280×720 的采集画幅中位于 (200, 300) 的位置,大小为 (100, 100)。

2)移除水印

-(void)clearWaterMark;

3.滤镜

除了上面提到的美颜和水印之外,视频中还有很多其它的处理效果也在这个环节完成。七牛直播云提供的 SDK 在开放性设计基础之上,通过数据源回调接口,可以支持各种自定义滤镜的接入。

为了实现丰富的滤镜效果,在 iOS 端可以考虑使用 GPUImage 这个库,这是一个开源的基于GPU的图片或视频的处理框架,内置了多达120多种常见的滤镜效果。有了它,添加实时的滤镜只需要简单地添加几行代码,还可以基于这个库自己写算法实现更丰富端效果。GPUImage 地址:GitHub – BradLarson/GPUImage: An open source iOS framework for GPU-based image and video processing
除了 iOS 端之外,Android 也有 GPUImage 这个库的移植:GitHub – CyberAgent/android-gpuimage: Android filters based on OpenGL (idea from GPUImage for iOS)同时,Google 官方也开源了一个伟大的库,覆盖了 Android 上面很多多媒体和图形图像相关的处理:GitHub – google/grafika: Grafika test app

4.连麦
连麦是互动直播中常见的需求,其流程如上图所示。主播和部分观众之间可以进行实时互动,然后将互动结果实时播放给其他观众观看。

基于以上业务需求,我们很容易想到基于单向直播原理,在主播端和连麦观众端进行双向推流和双向播流的方式互动,然后在服务端将两路推流合成一路推送给其他观众。但 RTMP 带来的延迟决定了这种方式无法做到用户可接受的互动直播。

实际上,互动直播的主要技术难点在于:
1)低延迟互动:保证主播和互动观众之间能够实时互动,两者之间就像电话沟通,因此必须保证两者能在秒级以内听到对方的声音,看到对方的视频;

2)音画同步:互动直播中对音画同步的需求和单向直播中类似,只不过互动直播中的延迟要求更高,必须保证在音视频秒级传输情况下的秒级同步。

3)音视频实时合成:其他观众需要实时观看到对话结果,因此需要在客户端或者服务端将画面和声音实时合成,然后以低成本高品质的方式传输观众端。

在视频和电话会议领域,目前比较成熟的方案是使用思科或者 WebEx 的方案,但这些商用的方案一不开源,二比较封闭,三成本比较高。对于互动人数比较少的互动直播,目前市场上比较成熟的方案是使用基于 WebRTC 的实时通讯方案。
上图是一个基于 WebRTC 协议实现多方实时通讯的示意图,本地用户(主播)和远程用户(连麦观众)之间的连接通过 RTCPeerConnection API 管理,这个 API 包装了底层流管理和信令控制相关的细节。基于该方案可以轻松实现多人(14 人以下)的多方实时通信,如下图所示:
当然,在通信人数少的情况下,其复杂度相对简单,如 2 人情况下。但人数增多至 4 人之后,其可选的网络结构就增多了,如上图所示,可以每个点之间形成自组织网络的方式通信,也可以以 1 人为中心形成星型通信网络,还可以让大家都通过一个集中式的服务端进行通信。
作为一个高性能、可伸缩的直播基础服务提供商,七牛直播云经过评估选择了以主播为中心形成星形通信网络,支持主播和多个观众之间的互动质量。同时,为了保证合成后的音视频实时传输到其他观众端,这里采用经过改造的 UDP 协议传输:

  1. 通过 UDP 降低传输延迟。
  2. 在 UDP 之上进行传输控制,保证用户互动体验 QoS。

在下一篇连载中,我们将详细介绍编码和封装。

敬请期待!

七牛云机构号将实时分享云计算领域的技术洞见以及行业讯息,为你的创新加速。
欢迎提问&求关注 ( ´ ▽ ` )ノ
以上。

「视频直播技术详解」系列之一:采集

作者:七牛云
链接:https://zhuanlan.zhihu.com/p/22502905
来源:知乎
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

关于直播的技术文章不少,成体系的不多。我们将用七篇文章,更系统化地介绍当下大热的视频直播各环节的关键技术,帮助视频直播创业者们更全面、深入地了解视频直播技术,更好地技术选型。

本系列文章大纲如下:

(一)采集

(二)处理

(三)编码和封装

(四)推流和传输

(五)延迟优化
(六)现代播放器原理
(七)SDK 性能测试模型
本篇将重点聊聊:采集

采集是整个视频推流过程中的第一个环节,它从系统的采集设备中获取原始视频数据,将其输出到下一个环节。视频的采集涉及两方面数据的采集:音频采集和图像采集,它们分别对应两种完全不同的输入源和数据格式。

采集内容

1.音频采集

音频数据既能与图像结合组合成视频数据,也能以纯音频的方式采集播放,后者在很多成熟的应用场景如在线电台和语音电台等起着非常重要的作用。音频的采集过程主要通过设备将环境中的模拟信号采集成 PCM 编码的原始数据,然后编码压缩成 MP3 等格式的数据分发出去。常见的音频压缩格式有:MP3,AAC,HE-AAC,Opus,FLAC,Vorbis (Ogg),Speex 和 AMR等。

音频采集和编码主要面临的挑战在于:延时敏感、卡顿敏感、噪声消除(Denoise)、回声消除(AEC)、静音检测(VAD)和各种混音算法等。

在音频采集阶段,参考的主要技术参数有 :

  • 采样率(samplerate):采样就是把模拟信号数字化的过程,采样频率越高,记录这一段音频信号所用的数据量就越大,同时音频质量也就越高。
  • 位宽:每一个采样点都需要用一个数值来表示大小,这个数值的数据类型大小可以是:4bit、8bit、16bit、32bit 等等,位数越多,表示得就越精细,声音质量自然就越好,而数据量也会成倍增大。我们在音频采样过程中常用的位宽是 8bit 或者 16bit。
  • 声道数(channels):由于音频的采集和播放是可以叠加的,因此,可以同时从多个音频源采集声音,并分别输出到不同的扬声器,故声道数一般表示声音录制时的音源数量或回放时相应的扬声器数量。声道数为 1 和 2 分别称为单声道和双声道,是比较常见的声道参数。
  • 音频帧(frame):音频跟视频很不一样,视频每一帧就是一张图像,而从上面的正玄波可以看出,音频数据是流式的,本身没有明确的一帧帧的概念,在实际的应用中,为了音频算法处理/传输的方便,一般约定俗成取 2.5ms~60ms 为单位的数据量为一帧音频。这个时间被称之为“采样时间”,其长度没有特别的标准,它是根据编解码器和具体应用的需求来决定的。

根据以上定义,我们可以计算一下一帧音频帧的大小。假设某音频信号是采样率为 8kHz、双通道、位宽为 16bit,20ms 一帧,则一帧音频数据的大小为:

size = 8000 x 2 x 16bit x 0.02s = 5120 bit = 640 byte

2.图像采集图像采集的图片结果组合成一组连续播放的动画,即构成视频中可肉眼观看的内容。图像的采集过程主要由摄像头等设备拍摄成 YUV 编码的原始数据,然后经过编码压缩成 H.264 等格式的数据分发出去。常见的视频封装格式有:MP4、3GP、AVI、MKV、WMV、MPG、VOB、FLV、SWF、MOV、RMVB 和 WebM 等。图像由于其直观感受最强并且体积也比较大,构成了一个视频内容的主要部分。图像采集和编码面临的主要挑战在于:设备兼容性差、延时敏感、卡顿敏感以及各种对图像的处理操作如美颜和水印等。

在图像采集阶段,参考的主要技术参数有:

  • 图像传输格式:通用影像传输格式(Common Intermediate Format)是视讯会议(video conference)中常使用的影像传输格式。
  • 图像格式:通常采用 YUV 格式存储原始数据信息,其中包含用 8 位表示的黑白图像灰度值,以及可由 RGB 三种色彩组合成的彩色图像。
  • 传输通道:正常情况下视频的拍摄只需 1 路通道,随着 VR 和 AR 技术的日渐成熟,为了拍摄一个完整的 360° 视频,可能需要通过不同角度拍摄,然后经过多通道传输后合成。
  • 分辨率:随着设备屏幕尺寸的日益增多,视频采集过程中原始视频分辨率起着越来越重要的作用,后续处理环节中使用的所有视频分辨率的定义都以原始视频分辨率为基础。视频采集卡能支持的最大点阵反映了其分辨率的性能。
  • 采样频率:采样频率反映了采集卡处理图像的速度和能力。在进行高度图像采集时,需要注意采集卡的采样频率是否满足要求。采样率越高,图像质量越高,同时保存这些图像信息的数据量也越大。

以上,构成了一个视频采集的主要技术参数,以及视频中音频和图像编码的常用格式。而对于直播 App 开发者来说,了解这些细节虽然更有帮助,但实际开发过程中可能很少能够关注采集环节中技术参数的控制,而是直接在 SDK 中将采集后的数据传递给下一个「处理」和「编码」环节。

采集源

1.摄像头采集

对于视频内容的采集,目前摄像头采集是社交直播中最常见的采集方式,比如主播使用手机的前置和后置摄像头拍摄。在现场直播场景中,也有专业的摄影、摄像设备用来采集。安防监控场景中也有专业的摄像头进行监控采集。

目前七牛提供的 SDK 对以上两类摄像头的采集都支持,对于手机,iOS 和 Android 分别支持前置后置摄像头的采集,只是 iOS 由于设备种类和系统版本不多,因此采集模块兼容性较好;而 Android 需要适配的硬件设备和系统则非常多,目前支持 Android 4.0.3 及以上的摄像头采集。对于专业摄像机或者摄像头,七牛云提供了兼容适合嵌入式系统的 C 语言采集模块的实现,欢迎参考使用:GitHub – pili-engineering/ipcam_sdk

2.屏幕录制

屏幕录制采集的方式在游戏直播场景中非常常见,目前我们在 Android SDK 中实现了屏幕录制的功能。而 iOS 则由于系统本身没有开放屏幕录制的权限而没法直接操作,但对于 iOS 9 以上的版本,是有个取巧的办法,可以通过模拟一个 AirPlay 镜像连接到(当前 App)自身,这样就可以在软件上捕获到屏幕上的任何操作,达到录制屏幕的效果。

在教育直播或者会场演讲场合,我们经常看见需要录制电脑桌面上 PPT 的场景,针对这种场景,目前市面上比较方便的方案是使用开源的桌面推流工具 OBS 来进行屏幕录制和推流:Open Broadcaster Software

3.从视频文件推流

除了从硬件设备采集视频进行推流之外,我们也可能需要将一个视频或者音频文件以直播流的形式实时传输给观众,比如在线电台或者电视节目,它们的输入可能直接来自于一些已经录制剪辑好的视频内容。

开放式设计

以上从采集内容和采集源两个维度分别介绍了视频采集相关的知识,但对于采集源来说,市场上可见的采集源远远不止这三种,即便是摄像头也有很多分类。对于一个完整的覆盖推流、传输和播放三个环节的直播云服务来说,支持尽可能多的采集源和播放终端是一项既无法规避也很难完成的工作。

为了支持市场上所有采集源的接入,我们在 SDK 中采用了开放式的设计,只要采集源实现方遵循相应的接口,即可支持任意的采集源。
图中我们把采集的内容分为图像和音频,其中图像的采集源包含摄像头、屏幕录制或者本地的视频文件,甚至是其它需要重新定义和实现的采集源。而音频的采集源包含麦克风、系统声音或者本地音频文件,当然也可以为它定义别的输入源。

这样设计最大的好处在于,可以以轻量的设计方式支持丰富的采集源,而采集源的具体实现也可以交给使用者。

在下一篇连载中,我们将详细介绍下直播中的处理环节,解答如何满足市场上主播的各种需求如美颜、水印、连麦互动等。