H5平台支持

更新时间: 2025/06/11 16:45:39

RTC uni-app SDK 是基于 NERTC 原生 Android、iOS、Web SDK 的 uni-app Wrapper。本文介绍如何使用 uni-app SDK 开发 H5 平台应用。

关于其 H5 浏览器的兼容性,请参考 NERTC 原生 Web SDK 支持哪些浏览器

uni-app SDK H5 平台特点

uni-app SDK 在视图画布方面与移动端(原生安卓、iOS)相比有一些区别。

nertc-local-viewnertc-remote-view 是移动端使用的视图,而 web 端不支持,所以需要在 UI 界面上渲染并设置视图画布的 API 上增加平台区分。

UI 页面需要区分 H5

本端视频渲染画布

xml<view class="nertc-video-view">
    <!-- 本地摄像头的渲染窗口,移动端 APP 使用 nertc-local-view,H5 平台使用 div 元素即可 -->
    
    <!-- #ifdef APP-PLUS -->
    <nertc-local-view
        style="height: 303.84rpx; flex: 1;" 
        mediaType="video" 
        :viewID="userID">
    </nertc-local-view>
    <!-- #endif -->
    
    <!-- #ifdef H5 -->
    <div
        id="local_video"
        class="nertc-video-view"
    ></div>
    <!-- #endif -->
</view>

远端视频渲染画布

xml<view class="nertc-video-area">
    <!-- 远端摄像头的渲染窗口,移动端APP使用nertc-remote-view,H5平台使用div元素即可 -->

    <view class="nertc-video-view" v-for="(userID, index) in remoteUserIdVideoList" :key="userID">
        <!-- #ifdef APP-PLUS -->
        <nertc-remote-view
            style="height: 303.84rpx;flex: 1;"
            v-if="userID"
            mediaType="video"
            :userID="userID">
        </nertc-remote-view>
        <!-- #endif -->
        <!-- #ifdef H5 -->
        <div
            v-bind:id="getRemoteVideoViewId(userID)"
            class="nertc-video-view"
        ></div>
        <!-- #endif -->
        
    </view>
</view>

根据不同的平台设置视频画布

设置本端视频渲染画布

javascriptconsole.log('设置本端视频画布');
// #ifdef H5
// H5需要传入挂载节点view,APP则不需要
//keycenter为demo中的配置中心文件
this.engine.setupLocalVideoCanvas(Object.assign(keycenter.getLocalVideoCanvasConfig(), {view: document.getElementById('local_video')}));
// #endif
// #ifdef APP-PLUS
this.engine.setupLocalVideoCanvas(keycenter.getLocalVideoCanvasConfig());
// #endif

设置远端视频渲染画布

javascriptconsole.log('设置远端视频画布');
//keycenter为demo中的配置中心文件
const config = Object.assign(keycenter.getRemoteVideoCanvasConfig(), {userID: remoteUserID})
// #ifdef H5
// H5需要传入挂载节点view,APP则不需要
const id = this.getRemoteVideoViewId(remoteUserID)
const view = document.getElementById(id)
this.engine.setupRemoteVideoCanvas(Object.assign(config, {
    view
}))
// #endif
// #ifdef APP-PLUS
this.engine.setupRemoteVideoCanvas(config)
// #endif

示例代码

网易云信为您提供完整的创建界面实现基础音视频通话的示例代码作为参考,您可以直接拷贝用于运行测试。

xml 界面的完整示例代码
xml<?xml version="1.0" encoding="utf-8"?>
<view class="nertc-container">

    <view class="nertc-video-area">

        <!-- #ifdef H5 -->
		<button v-if="isResumeAudio" 
			class="nertc-title-text"
			style=" margin-top: 20rpx; background-color: yellow; color: blue;" 
			@click="resumeAudio"
		>
		    浏览器音频自动播放受限时,点击恢复音频播放后会消失
		</button>
		<!-- #endif -->

        <view class="nertc-video-view">
            <!-- 本地摄像头的渲染窗口 -->
            
            <!-- #ifdef APP-PLUS -->
            <nertc-local-view
                style="height: 303.84rpx;flex: 1;" 
                mediaType="video" 
                :viewID="userID">
            </nertc-local-view>
            <!-- #endif -->
            <!-- #ifdef H5 -->
            <div
                id="local_video"
                class="nertc-video-view"
            ></div>
            <!-- #endif -->
            
        </view>
    </view>

    <view class="nertc-video-area">
        <!-- 远端摄像头的渲染窗口 -->
        <view class="nertc-video-view" v-for="(userID, index) in remoteUserIdVideoList" :key="userID">
            <!-- #ifdef APP-PLUS -->
            <nertc-remote-view
                style="height: 303.84rpx;flex: 1;"
                v-if="userID"
                mediaType="video" 
                :userID="userID">
            </nertc-remote-view>
            <!-- #endif -->
            <!-- #ifdef H5 -->
            <div
                v-bind:id="getRemoteVideoViewId(userID)"
                class="nertc-video-view"
            ></div>
            <!-- #endif -->
            
        </view>
    </view>
</view>
实现音视频通话的完整示例代码
javascriptimport permision from "@/NERtcUniappSDK-JS/permission.js";
import NERTC from "@/NERtcUniappSDK-JS/lib/index";
import {pluginVersion} from "@/NERtcUniappSDK-JS/lib/index";
import NertcLocalView from "@/NERtcUniappSDK-JS/nertc-view/NertcLocalView";
import NertcRemoteView from "@/NERtcUniappSDK-JS/nertc-view/NertcRemoteView";

export default {
    components: {
        NertcRemoteView: NertcRemoteView,
        NertcLocalView: NertcLocalView
    },
    data() {
        engine: null,
        remoteUserIdVideoList: [],
        isResumeAudio: false
    },
    methods: {
        createEngine() {
            console.log('初始化NERTC引擎, SDK版本: ', pluginVersion);
            this.engine = NERTC.setupEngineWithContext({
                appKey: appkey, // your appkey
                logDir: logDir || '', // expected log directory
                logLevel: logLevel || 3
            });

            console.log('初始化引擎完成,开始设置本地视频画布');
            //keycenter为demo中的配置中心文件
			// #ifdef H5
            this.engine.setupLocalVideoCanvas(Object.assign(keycenter.getLocalVideoCanvasConfig(), {view: document.getElementById('local_video')}))
            // #endif
            // #ifdef APP-PLUS
            this.engine.setupLocalVideoCanvas(keycenter.getLocalVideoCanvasConfig())
            // #endif

            // #ifdef APP-PLUS
                //判断权限
                if (uni.getSystemInfoSync().platform === "android") {
                    permision.requestAndroidPermission(
                        "android.permission.RECORD_AUDIO"
                    );
                    permision.requestAndroidPermission(
                        "android.permission.CAMERA"
                    );
                }
            // #endif

            this.appendActionInfo('初始化引擎完成,启动视频')
            this.engine.enableLocalVideo({
                enable: true, //true表示设置启动摄像头,false表示关闭摄像头
                videoStreamType: 0 //0表示视频,1表示屏幕共享 //当前demo先使用数字
            })


            //注册NERTC的事件
            this.engine.addEventListener("onError", (code, message, extraInfo) => {
                let imessage = `onError通知:code = ${code}, message = ${message}, extraInfo = ${extraInfo}`
                this.engine.nertcPrint(imessage)
                console.log(imessage)
                if (code === 41030) {
                    imessage = `${extraInfo.userID} 音频播放收到浏览器限制需要用户手势触发`
                    //注意浏览器存在音频自动播放受限的问题,解决方法可以参考Demo中的实现
                    this.isResumeAudio = true //页面上会弹出提示框,使用者手动点击该按钮,执行重新播放即可
                    console.log(imessage)
                }
            });
            
            this.engine.addEventListener("onWaring", (code, message, extraInfo) => {
                const message = `onWaring通知:code = ${code}, message = ${message}, extraInfo = ${extraInfo}`
                console.log(message)
            });

            this.engine.addEventListener("onJoinChannel", (result, channelId, elapsed, userID, userStringID) => {
				let message = `onJoinChannel通知:自己加入房间状况,result = ${result}, channelId = ${channelId}, elapsed = ${elapsed}, userID = ${userID}, userStringID = ${userStringID}`
                console.log(message)
            });

            this.engine.addEventListener("onLeaveChannel", (result) => {
                const message = `onLeaveChannel通知:自己离开房间状况,result = ${result}`
                console.log(message)
            });

            this.engine.addEventListener("onUserJoined", (userID, customInfo, userStringID) => {
                const message = `onUserJoined通知:有人加入房间,userID = ${userID}, userStringID = ${userStringID}, customInfo = ${customInfo}`
                console.log(message)
            });

            this.engine.addEventListener("onUserLeave", (userID, reason, userStringID) => {
                const message = `onUserLeaved通知:有人离开房间,userID = ${userID}, userStringID = ${userStringID}, reason = ${reason}`
                console.log(message)
                //建议在当前页面上销毁远端的视频view组件
                const remoteUserID = userStringID
                let list = this.remoteUserIdVideoList.filter(val => val !== remoteUserID);
                this.remoteUserIdVideoList = list;
                list = this.remoteUserIDSubStreamVideoList.filter(val => val.viewID !== remoteUserID);
                this.remoteUserIDSubStreamVideoList = list;
            });

            this.engine.addEventListener("onUserAudioStart", ( userID, userStringID ) => {
                //sdk自动订阅对端音频,这里仅仅是通知信息,开发者不需要做什么逻辑
                const message = `onUserAudioStart通知:对方开启音频,userID = ${userID}, userStringID = ${userStringID}`
                console.log(message)
            });

            this.engine.addEventListener("onUserAudioStop", (userID, userStringID) => {
                const message = `onUserAudioStop通知:对方关闭音频,userID = ${userID}, userStringID = ${userStringID}`
                this.engine.nertcPrint(message)
                console.log(message)
            });
				
            this.engine.addEventListener("onUserVideoStart", (userID, maxProfile, userStringID) => {
                const message = `onUserVideoStart通知:对方开启视频,userID = ${userID}, userStringID = ${userStringID}, maxProfile = ${maxProfile}`
                this.engine.nertcPrint(message)
                console.log(message)
                //nertc-remote-view组件 viewID和userID的数据格式是String类型,onUserVideoStart事件通知的userID是number,这里做一下数据格式转换
                const remoteUserID = userStringID
                if (!this.remoteUserIdVideoList.includes(remoteUserID)) {
                    this.remoteUserIdVideoList.push(remoteUserID);
                    //保证当前远端试图组件渲染完成之后,再执行设置画布的接口
                    this.$nextTick(() => {
                        console.warn('此时远端视频 nertc-remote-view 渲染完成')
                        //需要开发者主动去做订阅对方视频的逻辑动作
                        this.subscribeRemoteVideo(userID, userStringID) 
                    })
                }
            });

            this.engine.addEventListener("onUserVideoStop", (userID, userStringID) => {
                const message = `onUserVideoStop通知:对方关闭视频,userID = ${userID}, userStringID = ${userStringID}`
                console.log(message)
                //建议在当前页面上销毁对应的视频view组件
                const remoteUserID = userStringID //`${userID}`
                //对端关闭视频,建议主动调用接口释放对端视频画布相关的资源
                this.engine.destroyRemoteVideoCanvas({
                    userID,
                    userStringID
                })
                const list = this.remoteUserIdVideoList.filter(val => val !== remoteUserID);
                this.remoteUserIdVideoList = list;
            });
        },

        destroyEngine() {
            console.log('销毁NERTC引擎')
            //清除注册的所有事件
            this.engine?.removeAllEventListener()
            //释放资源
            this.engine?.destroyEngine()
            this.engine = null
            this.remoteUserIdVideoList = []
        },

        joinChannel(){
            console.log('加入房间')
            this.engine.joinChannel({
                token: '',
                channelName: 'uniapp', //自定义房间名称
                myUid: 111 //该房间中个人userID标识,要求number类型(不要超出number范围),房间中唯一
            });
        },

        leaveChannel(){
            console.log('离开房间')
            this.engine.leaveChannel();
            this.remoteUserIdVideoList = []
        },

        subscribeRemoteVideo(remoteUserID, remoteUserStringID) {
            let messge = `开始拉流: remoteUserID=${remoteUserID}, remoteUserStringID=${remoteUserStringID}, 设置对端视频画布`
            console.log(messge)
            //keycenter为demo中的配置中心文件
            const config = Object.assign(keycenter.getRemoteVideoCanvasConfig(), {userID: remoteUserID, userStringID: remoteUserStringID})
            console.log('setupRemoteVideoCanvas config: ', JSON.stringify(config))
            
            // #ifdef H5
            // H5需要传入挂载节点
            const id = this.getRemoteVideoViewId(remoteUserID)
            const view = document.getElementById(id)
            this.engine.setupRemoteVideoCanvas(Object.assign(config, {
                view
            }))
            // #endif
            // #ifdef APP-PLUS
            this.engine.setupRemoteVideoCanvas(config)
            // #endif

            messge = `subscribeRemoteVideo: ${remoteUserID}, 订阅对端视频`
            console.log(messge)
            this.engine.subscribeRemoteVideo({
                userID: remoteUserID,
                userStringID: remoteUserStringID,
                streamType: 0, //0表示大流,1表示小流
                subscribe: true //true表示订阅,false表示取消订阅
            })
        },

        resumeAudio() {
            console.log('resumeAudio 恢复音频播放')
            if(this.engine){
                //调用sdk的接口重新播放,如果此时远端视频渲染也有影响,可以考虑使用this.engine.resume()替换
                this.engine.rePlayAudio()

                this.isResumeAudio = !this.isResumeAudio
            }
        }
    }
}
此文档是否对你有帮助?
有帮助
去反馈
  • uni-app SDK H5 平台特点
  • UI 页面需要区分 H5
  • 本端视频渲染画布
  • 远端视频渲染画布
  • 根据不同的平台设置视频画布
  • 设置本端视频渲染画布
  • 设置远端视频渲染画布
  • 示例代码