【实战分享】手把手教你直播解决方案

2020-06-04 17:50:27 浏览数 (1)

导语

风口上在飞的,虽然一直都是猪,但是如果仔细看去,便会发现永远不会是同一头。

要说2019最火的猪是人工智能的话,那么2020就一定是直播带货了。斗鱼、虎牙等培养起了大家观看直播的习惯,薇娅、李佳琪将直播带货拉进了大家的眼帘,而“老罗”罗永浩的入场则是个人认为真正的将直播带货这个事儿给完全的行业化了。老罗之后,各家各业都将直播带货拉入了P0的产品需求,本文就将介绍如何基于腾讯云服务来实现一套直播的解决方案。

基础介绍

本文涉及的腾讯云服务主要包含云直播和云点播两个产品,将会从主播端推流,用户侧播放,视频记录回放这三个角度来主要介绍实现过程。在介绍具体方案之前,我们先来看看推流、拉流、直播和点播分别是什么?

  • 推流:主播将本地视频源和音频源推送到腾讯视频云服务器,在有些场景中也被称为“RTMP 发布”。
  • 直播:直播的视频源是实时生成的,有人推流的前提,直播才有意义,同时一旦主播停播,直播 URL 也就失效了。而且由于是实时直播,所以播放器在播直播视频的时候是没有进度条的。
  • 点播:点播其实对应到的是视频源是云端的一个文件,文件只要没有被提供方删除就随时可以播放(类似腾讯视频), 而且由于整个视频都在服务器上,所以播放的时候是有进度条的。点播是视频回放的实现基础

下面我们将分别解决这个问题:1. 直播推拉流;2.视频录制回放;3. 账号资源隔离。

直播推拉流

为了实现直播,需要两个必要元素,推流地址和播放地址,我们先来看推流地址的生成。

推流

云直播平台已为我们提供一个数字开头的系统推流域名,当然基本上在正式项目使用中,我们也可以添加自有已备案域名进行推流。在有了推流域名的基础上,我们便可以计算出推流地址,推流地址有一套拼接算法,一个符合腾讯云标准的推流 URL,由下面四个部分组成:

直播推流地址URL组成算法直播推流地址URL组成算法

我们来实际计算一个推流地址,域名假设为 12345.livepush.myzijiebao.com,AppName我们采用默认的live,StreamName假设为stream_test_01,txSecret是用来防盗链(可以在每个域名下面配置)假设为1qaz2wsx,txTime是表示当前推流有效期,可以用来告知腾讯云该 URL 过期时间的,假设为2020-05-28 23:59:59。那么我们可以得到的推流地址为:

rtmp://99995.livepush.myzijiebao.com/live/stream_test_01?txSecret=691bef580972ac54691b1e5ee40460ec&txTime=5ECFDFFF

上面这条流就是在2020-05-28 23:59:59之前可以用来往云端推流的游戏啊url了,而一旦推流成功,在控制台的流管理就可以看到实时的在线流。注意:其中txSecret 字段是用来安全防盗链的,它的作用是防止攻击者伪造您的后台生成推流 URL 或者非法盗取您的播放地址进行谋利。

拉流

关于播放域名,我们则需要添加自有已备案域名进行直播播放。然后只需要填写与推流地址相同的 StreamName, 就可以播放对应的流。拉流地址也是有着和推流地址类似的组成算法,这儿就不再赘述。

对于上述拼装算法逻辑的Java实现demo如下:

代码语言:javascript复制
private static String byteArrayToHexString(byte[] data) {
    char[] out = new char[data.length << 1];
    for (int i = 0, j = 0; i < data.length; i  ) {
        out[j  ] = DIGITS_LOWER[(0xF0 & data[i]) >>> 4];
        out[j  ] = DIGITS_LOWER[0x0F & data[i]];
    }
    return new String(out);
}
    
private static String getSafeUrl(String key, String streamName, long txTime) {
    String input = new StringBuilder().
            append(key).
            append(streamName).
            append(Long.toHexString(txTime).toUpperCase()).toString();
    String txSecret = null;
    try {
        MessageDigest messageDigest = MessageDigest.getInstance("MD5");
        txSecret = byteArrayToHexString(messageDigest.digest(input.getBytes("UTF-8")));
    } catch (NoSuchAlgorithmException e) {
        e.printStackTrace();
    } catch (UnsupportedEncodingException e) {
        e.printStackTrace();
    }
    return txSecret == null ? "" :
            new StringBuilder().
                    append("txSecret=").
                    append(txSecret).
                    append("&").
                    append("txTime=").
                    append(Long.toHexString(txTime).toUpperCase()).
                    toString();
}

public List<String> CreateLiveUrls(String streamName, long txTime) {
    String pushExt = getSafeUrl(cloudVideoConfig.getPushKey(), streamName, txTime);
    String pullExt = getSafeUrl(cloudVideoConfig.getPullKey(), streamName, txTime);

    String appid = "live";
    String pushUrl = String.format("rtmp://%s/"   appid   "/%s?%s", cloudVideoConfig.getPushDomain(), streamName, pushExt);
    String pullUrl = String.format("http://%s/"   appid   "/%s.flv?%s", cloudVideoConfig.getPullDomain(), streamName, pullExt);

    List<String> list = new ArrayList<>();
    list.add(pushUrl);
    list.add(pullUrl);

    return list;
}

其中,对于appid,我们采用live,没做任何其他变更,因为在整个云直播体系中,一条直播流是通过流名称来做唯一识别,与应用无关。当我们把数据流推流成功,就可以从流管理测试观看到,当然也可以通过VLC中网络流来进行观看测试。

录播回放

在介绍完毕推拉流之后,我们下面将介绍如何实现视频回放观看的功能。

首先来看下我们的需求是什么,录像回放,很明确的就是在当前主播直播完成之后,需要将直播内容完整的回放给用户。腾讯云的云直播服务通过打通云点播服务,将直播原始流经过转音视频封装(不修改音频、视频数据以及对应的时间戳等信息)得到的文件存储到云点播平台的服务,来实现直播视频的存储。

直播录制

云直播平台支持API或者控制台两种方式来创建录制直播视频。从功能角度来看,分为4种录制方式:

使用场景

说明

按推流域名、流名称多级别录制

您可以在推流域名、流名称级别配置是否需要录制。

按指定时间段录制

您可以通过调用 API 控制录制的开始与结束时间,在您指定的时间内进行录制。

精彩视频录制

在推流过程中碰到精彩画面,您可以通过调用 API 实时产生录制。

纯音频录制

若推流为纯音频,您可以配置 AAC 纯音频录制。

希望详细了解不同场景该使用哪种方式的话,可以官网了解更多。在我们当前描述的场景,主要是为了实现直播流的全程回放功能,因此我们采用的是第一种,对推流下所有直播流开启录制。为了实现该功能,我们需要在云直播-功能模板-录制配置来进行模板配置,截图如下:

录制模板配置截图录制模板配置截图

可以看到在上面的录制到子应用的地方,就是指定云点播平台的应用。然后再去域名管理,绑定上面配置的录制模版到我们的推流域名地址,如下所示:

绑定录制模板到推流域名绑定录制模板到推流域名

当绑定完毕之后,我们就形成了推流->录制模板->点播端这样的录播数据流向了。

点播

上面提到,我们的直播录制数据都是存储于云点播服务内的,下面我们继续介绍云点播平台,在进入点播后,默认如下图所示。其中,所有从云直播落盘的视频记录均展现在媒资管理中。

云点播页面截图云点播页面截图

云点播除了提供基础的视频存放之外,还拥有着视频编辑等一系列重要的增值功能。

视频拼接

在进行完上面的配置之后,我们就可以实现一个基本的直播-播放-回看的功能线了。不过细心的同学可能会发现两个问题:

1. 录制模板配置里面的最大时长只有120分钟,但是现在一场直播的时长超过120分钟的概率很大,超过之后怎么办?

2. 直播过程中,主播经常会断开视频流(主动断开或网络差被动断开),这样的断开是否会影响视频回放?

也就是说,在一个完整的直播过程中,我们的录制文件可能是会分成很多份存储在云端的,而如果需要正常回放,就需要以正确的顺序重组这些视频文件成一个完整的文件。为了解决这个视频的拼接问题,我们需要借用服务端视频编辑API的能力,EditMedia接口可以对视频进行编辑(剪辑、拼接等),生成一个新的点播视频。编辑的功能包括:

  1. 对点播中的一个文件进行剪辑,生成一个新的视频;
  2. 对点播中的多个文件进行拼接,生成一个新的视频;
  3. 对点播中的多个文件进行剪辑,然后再拼接,生成一个新的视频;
  4. 对点播中的一个流,直接生成一个新的视频;
  5. 对点播中的一个流进行剪辑,生成一个新的视频;
  6. 对点播中的多个流进行拼接,生成一个新的视频;
  7. 对点播中的多个流进行剪辑,然后拼接,生成一个新的视频。

我们这儿主要是采用第二个功能:对点播中的多个文件进行拼接,生成一个新的视频。关于视频编辑接口的具体的参数和使用方法可以去官网上看,这儿提供一下java中的调用方法:

代码语言:javascript复制
private String editMedia(List<String> fileIds) {
    EditMediaResponse resp;
    try {
        //参数生成
        int len = fileIds.size();
        EditMediaFileInfo[] files = new EditMediaFileInfo[len];
        for (int i = 0; i < len; i  ) {
            EditMediaFileInfo f = new EditMediaFileInfo();
            f.setFileId(fileIds.get(i));
            files[i] = f;
        }

        EditMediaRequest req = new EditMediaRequest();
        req.setFileInfos(files);
        //req.setSessionId();//TODO
        req.setInputType(VOD_INPUT_TYPE_FILE);
        req.setDefinition(VOD_DEFINITION_HIGH_DEFINITION);
        req.setTasksPriority(VOD_PRIORITY_MEDIUM);
        req.setSubAppId(cloudVideoConfig.getVodSubAppId());

        //发起拼接点播记录任务
        Credential cred = new Credential(cloudVideoConfig.getSecretId(), cloudVideoConfig.getSecretKey());
        ClientProfile clientProfile = new ClientProfile();
        clientProfile.setSignMethod(ClientProfile.SIGN_TC3_256);
        VodClient client = new VodClient(cred, cloudVideoConfig.getRegion(), clientProfile);

        resp = client.EditMedia(req);
    } catch (Exception e) {
        e.printStackTrace();
        log.info(e.getMessage());
        throw new UserBizException(BizErrorCode.VOD_GENERATOR_RECORD_ERROR);
    }

    return resp.getTaskId();
}

视频剪辑完成后的文件,也和前面云直播中常产生的文件一样落在云点播,当然每个文件的fileid是不同的,对于文件的类型,我们可以通过视频来源来区分,录制or视频处理。

事件处理

为什么要提到事件处理呢,在上面我们说到了如何推流,拉流,然后如何通过视频剪辑获取回放的内容。但是将这整个过程衔接推动起来的,是一个个事件。所以下面我们再来介绍下直播和点播中的事件处理过程。

直播事件处理

开始/暂停推流

直播/暂停推流事件是当主播开启/停止推流时,直播服务内部有事件发生,消息将会经由事件消息通知服务统一回调给观众。是否要将消息推送给观众是一个业务需求,但是我们通过关注该事件,可以及时获取流的开启状态,这是很有必要的。

为了实现直播事件的接收,我们需要去云直播-功能模板-回调配置,进行如下类似的配置操作:

云直播回调配置云直播回调配置

当完成配置回调模板之后,需要将推流域名与回调模板绑定起来才可以生效,如下所示:

绑定回调模板绑定回调模板

在做事件接收时,需要注意的几个点是几个校验:

1. 事件时间校验,需要保证过来的事件的时间,不能与当前服务器时间差别太多,这可以通过字段t和当前时间比较来判别;

2.事件类型校验,需要保证过来的事件的类型,是需要的类型,事件枚举如下:

代码语言:javascript复制
直播推流事件,event_type = 1
直播断流事件,event_type = 0
录制文件生成事件,event_type = 100
截图文件生成事件,event_type = 200

3.事件签名校验,签名算法可以参考文档:sign = MD5(key t),腾讯云把加密 key 和 t 进行字符串拼接后通过 MD5 计算得出 sign 值,并将其放在通知消息里,您的后台服务器在收到通知消息后可以根据同样的算法确认 sign 是否正确,进而确认消息是否确实来自腾讯云后台。

当这些校验都过了之后,就可以处理事件的内容了。

文件生成事件

前面在介绍录播时,我们需要做视频拼接的操作,但是何时去执行视频编辑的操作呢,以及每个片段视频文件的地址(或者云端文件fileid)是多少呢,这就涉及到了云直播平台的文件生成事件通知了。

文件生成事件的处理方式与上面提到的相差不多,就不再赘述。这儿需要额外提一下的是,由于事件是异步的,所以我们在接受事件,以及处理文件列表时,需要特别注意先后顺序,不能将文件拼接顺序弄反了。这样就会导致整个录播的视频出问题。

点播事件处理

在上文我们提交了视频编辑的请求后,编辑文件的生成,也是基于事件通知的。云点播平台提供了视频编辑完成事件来供我们使用。当 App 配置了事件通知,并且在编辑视频完成后,App 后台即可通过“普通回调”或“可靠回调”的方式获取该事件通知。事件通知内容为EditMediaTask 结构。

资源隔离

上面的内容基本上已经说完了整体的直播流程了。这儿需要再介绍一下云直播和云点播平台的资源隔离体系。因为在项目使用时,云点播所有的资源全部存于此,一个很大的问题就是资源隔离的问题,比如我们一般会使用一个腾讯云账号,但是却同时需要承载不同的项目,我们不可能为了每一个项目去弄一套腾讯云账号,所以不同项目应用在不同环境下的数据隔离很有必要。

云直播资源隔离

我们先来看一下云直播的资源隔离体系。从实际使用角度来说,假设我们有proj1,proj2两个项目,同时又存在环境dev,qa和online,对此我们一般分别申请域名如下:

----proj1推流

proj1-dev.push.tencent.com

proj1-qa.push.tencent.com

proj1.push.tencent.com

----proj1拉流

proj1-dev.pull.tencent.com

proj1-qa.pull.tencent.com

proj1.pull.tencent.com

----proj2推流

proj2-dev.push.tencent.com

proj2-qa.push.tencent.com

proj2.push.tencent.com

----proj2拉流

proj2-dev.pull.tencent.com

proj2-qa.pull.tencent.com

proj2.pull.tencent.com

首先,我们需要十分明确的一点是,一个腾讯云账号下的云直播的流资源是通过streamId流名称来区分的,和域名没有任何关系。这也就是域名没有隔离和映射的关系,当我们有多个推流域名时,无论我们通过哪个域名来推流,只要streamId是同样的,那就是同一个流。同样的,当一个云账号下有多个拉流域名时,只要获得了流名称,任何一个拉流域名都可以播放该流数据。基于这样的一个前提,我们必须要保证流id的唯一,且最好基于不同环境下,对于streamId可以有一个前缀/后缀标识。

当然,至于有需要帮推流、拉流一一绑定起来,从而实现完全隔离的话,控制台上是没有,不过大家可以去咨询下相关人员,或者开工单咨询,说不定会有小惊喜哦。

云点播资源隔离

为了使开发者能够在云点播中实现资源隔离,云点播提供了子应用特性。这儿所说的资源包括云点播中的媒体文件及其属性、由媒体文件衍生的其它文件、各类配置、CDN 域名、使用点播服务产生的统计信息等。

子应用是云点播的一个内部概念,是一种资源划分的方式,一个子应用的外在表现类似于一个独立的云点播账号。在创建子应用前后,我们对比下点播资源的归属形式如下图所示:

云点播子应用云点播子应用

通过利用子应用,我们可以实现:多部门/多业务隔离,权限控制以及区分正式、测试环境等。子应用体系中有三类身份:管理员、主应用和子应用,更过关于子应用的介绍在官网看到。

当我们的账号开启了子应用体系之后,我们的控制台的边栏色系将会变成白色(应该是为了区别显示吧),同时可以切换不同的应用(管理员、主应用、子应用A、子应用B等):

云点播子应用体系控制台云点播子应用体系控制台

那么,在我们直播方案中,如何应用到这儿的点播子应用体系呢?这就要回到我们上文的云直播的录制模板配置界面了,在录制模板界面,当我们开启了云点播的子应用之后,我们的直播录制模板的【录制至子应用】就可以选择到对应的点播应用,从而实现了从直播到点播的资源隔离。

直播录制模板配置到对应点播子应用直播录制模板配置到对应点播子应用

总结

通过上面,我们基于腾讯云的能力对于如何推流直播,拉流播放,事件处理和项目环境资源隔离等四个大的方面进行了详细介绍。其实一个标准的直播项目基于这些已经可以实现了,当然,我们的项目肯定有远不止于此的业务需求,譬如带货,直播间、计划管理等等,这就需要各位研发兄弟和产品兄弟们,斗智斗勇了啊,祝好!

0 人点赞