音视频进阶教程-SEI直播补充增强信息实现

1 SEI功能简介

在音视频流媒体应用中,除了可以流媒体通道推拉音视频内容外,还可以使用流 SEI(Supplemental Enhancement Information,媒体补充增强信息)通过流媒体通道将文本信息与音视频内容打包在一起,从主播端(推流端)推出,并从观众端(拉流端)接收,以此实现文本数据与音视频内容的精准同步的目的。

一般可用于视频画面的精准布局、远端歌词同步、直播答题等应用场景。

SEI 的相关概念及原理请参考 如何理解和使用 SEI(媒体补充增强信息)

适合对消息发送有较高频率和实时性要求,且消息丢失不会影响业务逻辑时,推荐使用 SEI(Supplemental Enhancement Information,媒体补充增强信息)。

2 SEI示例源码下载

请参考 下载示例源码 获取源码。

相关源码请查看 “/ZegoExpressExample/Examples/Others/SupplementalEnhancementInformation” 目录下的文件。

3 实现SEI之前的前提条件

在实现 SEI 功能之前,请确保:

4 SEI接口使用步骤

发送与接收 SEI 信息功能需要推流和拉流端配对使用才能展示效果,即需要在推流端发送 SEI 信息,拉流端接收 SEI 信息。以下内容将介绍各端如何使用发送与接收 SEI 信息功能。

微信图片_20221019143102.png

主播推流发送 SEI 消息调用流程如下:

  1. 调用 createEngine 接口创建 engine 对象。

  2. 调用 loginRoom 接口登录房间。

  3. 调用 startPublishingStream 接口推流。

  4. 在推流成功后,调用 sendSEI 接口发送 SEI 信息。

观众拉流接收 SEI 消息调用流程如下:

  1. 调用 createEngine 接口创建 engine 对象。

  2. 创建 IZegoEventHandler 对象,并重写接收 SEI 信息的 onPlayerRecvSEI 方法,调用 setEventHandler 接口传入创建的 IZegoEventHandler 监听 onPlayerRecvSEI 的回调。

  3. 调用 loginRoom 接口登录房间。

  4. 调用 startPlayingStream 接口拉流。

  5. 在拉流成功后,接收到推流端发送的 SEI 信息之后触发 onPlayerRecvSEI 回调。

拉流时,如果开发者通过调用 `mutePlayStreamVideo` 或 `muteAllPlayStreamVideo` 接口,设置了只拉音频流时,将无法接收 SEI 信息。

4.1 (可选)设置 SEI 类型

设置 SEI 类型

由于 SDK 默认使用 ZEGO 自行定义的 SEI(nalu type = 6, payload type = 243)类型打包,且此类型是 SEI 标准未规定的类型,因此跟视频编码器或者视频文件中的 SEI 不存在冲突。但当开发者需要使用第三方解码器解码时(如 FFmpeg),会导致解不出正确的 SEI,此时需要在推流前调用 setSEIConfig 接口更换 SDK 发送 SEI 的类型,使用 UserUnregister 的 SEI(nalu type = 6, payload type = 5)类型打包。

仅当开发者使用第三方解码器解码 SEI 时需要执行该步骤。

  • 接口原型

    /**
     * 设置媒体增强补充信息(SEI)类型
     *
     * 必须在推流之前设置。
     *
     * @param config SEI 配置属性。默认使用 ZEGO 定义的 SEI 类型。
     */
    public void setSEIConfig(ZegoSEIConfig config);
    
  • 调用示例

    ZegoSEIConfig seiConfig = new ZegoSEIConfig();
    // 采用 H.264 的 SEI (nalu type = 6,payload type = 5) 类型打包,因为视频编码器自身会产生 payload type 为 5 的 SEI,或者使用视频文件推流时,视频文件中也可能存在这样的 SEI,所以使用此类型时,用户需要把 uuid + content 当作 buffer 塞给 SEI 发送接口;此时为了区别视频编码器自身产生的 SEI, App 在发送此类型 SEI 时,可以填写业务特定的 uuid(uuid长度为16字节),接收方使用 SDK 解析 payload type 为 5 的 SEI 时,会根据设置的过滤字符串过滤出 uuid 相符的 SEI 抛给业务,如果没有设置过滤字符串,SDK 会把所有收到的 SEI 都抛给开发者。
    

seiConfig.type = ZegoSEIType.USER_UNREGISTER;

engine.setSEIConfig(seiConfig);

// 通过 advancedConfig 设置 uuid 过滤字段,设置之后 SDK 只会抛出前 12 个字节为开发者所设置 uuid 的 SEI
ZegoEngineConfig engineConfig = new ZegoEngineConfig();
// 其他用户通过 [onPlayerRecvSEI] 收到的 SEI 信息前 12 个字节一定是 zegozegozego,其他会被过滤
engineConfig.advancedConfig.put("unregister_sei_filter", "zegozegozego");
ZegoExpressEngine.setEngineConfig(engineConfig);

// 开始推流
engine.startPublishingStream("STREAM_ID");
```

4.2 直播推流方

发送 SEI 信息的接口需要在推流成功之后调用,接口原型如下:

  • 接口原型

    /**
     * 发送媒体增强补充信息
     *
     * 此接口可在开发者推流传输音视频流数据同时,发送流媒体增强补充信息来同步一些其他附加信息。
     * 一般如同步音乐歌词或视频画面精准布局等场景,可选择使用发送 SEI。
     * 当推流方发送 SEI 后,拉流方可通过监听 [onPlayerRecvSEI] 的回调获取 SEI 内容。
     * 由于 SEI 信息跟随视频帧或音频帧,由于网络问题有可能丢帧,因此 SEI 信息也有可能丢,为解决这种情况,应该在限制频率内多发几次。
     * 限制频率:1秒钟不要超过30次。
     * SEI 数据长度限制为 4096 字节。
     * @param data SEI 内容
     */
    public void sendSEI(byte[] data)
    
  • 调用示例

    /** 定义 SDK 引擎对象 */
    ZegoExpressEngine engine;
    
    ZegoEngineProfile profile = new ZegoEngineProfile();
    /** 请通过官网注册获取,格式为 123456789L */
    profile.appID = appID;
    /** 请通过官网注册获取,格式为:@"0123456789012345678901234567890123456789012345678901234567890123"(共64个字符)*/
    profile.appSign = appSign; 
    /** 通用场景接入 */
    profile.scenario = ZegoScenario.GENERAL;
    /** 设置app的application 对象 */
    profile.application = getApplication();
    /** 创建引擎 */
    engine = ZegoExpressEngine.createEngine(profile, null);
    // 登录房间
    engine.loginRoom("roomid", new ZegoUser("userid_1"));
    // 推流
    engine.startPublishingStream("streamid");
    // 开发者的其他业务逻辑
    ...;
    // 在业务场景需要的时机发送 SEI 信息
    engine.sendSEI("12345".getBytes());
    

4.3 直播拉流方

接收 SEI 信息的回调接口需要在拉流成功之后触发,接口原型如下:

  • 接口原型

    /**
     * 收到远端流的 SEI 内容
     *
     * 拉流成功后,当远端流调用 sendSEI 后,本端会收到此回调。
     * 若只拉纯音频流,将收不到推流端发送的 SEI 信息。
     * @param streamID 拉流的流 ID
     * @param data SEI 内容
     */
    public void onPlayerRecvSEI(String streamID, byte[] data){
    
    }
    
  • 调用示例

    /** 定义 SDK 引擎对象 */
    ZegoExpressEngine engine;
    
    ZegoEngineProfile profile = new ZegoEngineProfile();
    /** 请通过官网注册获取,格式为 123456789L */
    profile.appID = appID;
    /** 请通过官网注册获取,格式为:@"0123456789012345678901234567890123456789012345678901234567890123"(共64个字符)*/
    profile.appSign = appSign; 
    /** 通用场景接入 */
    profile.scenario = ZegoScenario.GENERAL;
    /** 设置app的application 对象 */
    profile.application = getApplication();
    /** 创建引擎 */
    engine = ZegoExpressEngine.createEngine(profile, null);
    
    // 创建 IZegoEventHandler 对象, 并重写 onPlayerRecvSEI 方法
    IZegoEventHandler handler = new IZegoEventHandler(){
        // 监听其他回调
        ...;
    
        // 监听接收 SEI 信息的回调, 当发送端调用 sendSEI 发送信息时会触发此回调
        public void onPlayerRecvSEI(String streamID, byte[] data) {
            // 在这里实现业务场景相关的逻辑, 例如展现相关的UI等
            ...;
        }
    
    }
    // 添加监听的回调对象
    engine.setEventHandler(handler);
    // 登录房间
    engine.loginRoom("roomid", new ZegoUser("userid_2"));
    // 拉流, canvas 为 ZegoCanvas 类型的索引 UI 渲染控件的对象
    engine.startPlayingStream("streamid", canvas);
    // 开发者的其他业务逻辑
    ...;
    
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,530评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 86,403评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,120评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,770评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,758评论 5 367
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,649评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,021评论 3 398
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,675评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,931评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,659评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,751评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,410评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,004评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,969评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,203评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,042评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,493评论 2 343

推荐阅读更多精彩内容