快速入门

本文为您介绍如何使用 NERTC SDK 快速实现互动直播功能。

前提条件

在开始运行工程之前,请您准备如下材料:

集成SDK

Maven集成

建议使用此方式集成

集成前,请先前往SDK下载页面查看当前最新版本。这里以 x.x.x 版本为例,在项目对应模块的 build.gradle 中加入:

api 'com.netease.yunxin:nertc:x.x.x'

手动集成

首先,请先前往SDK下载页获取当前最新版本SDK,解压后将对应的文件拷贝至项目(以Sample Code为例)路径中。

文件/文件夹 项目路径
nertc-sdk-3.6.2.jar /app/libs/
arm64-v8a
arm64-v7a
x86
/app/src/main/jniLibs/

此外,您可能还会看到 part 文件夹,无特殊情况可以先忽略。

然后,在 app/build.gradle 文件中设置 libs 路径。

android {
    ...
    compileOptions {
        // SDK 依赖的JDK版本为Java 8
        sourceCompatibility JavaVersion.VERSION_1_8
        targetCompatibility JavaVersion.VERSION_1_8
    }
    ...

    dependencies {
        implementation fileTree(dir: "libs", include: ["*.jar"])
        ...
    }
}

最后,点击 File > Sync Project With Gradle Files 按钮,直到同步完成。

添加权限

打开 app/src/main/AndroidManifest.xml 文件,添加必要的设备权限。如:

 <uses-permission android:name="android.permission.INTERNET"/>
 <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
 <uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
 <uses-permission android:name="android.permission.CHANGE_WIFI_STATE"/>
 <uses-permission android:name="android.permission.WAKE_LOCK"/>
 <uses-permission android:name="android.permission.CAMERA"/>
 <uses-permission android:name="android.permission.RECORD_AUDIO"/>
 <uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS"/>
 <uses-permission android:name="android.permission.BLUETOOTH"/>
 <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
 <uses-permission android:name="android.permission.BROADCAST_STICKY"/>
 <uses-permission android:name="android.permission.READ_PHONE_STATE"/>

 <!-- AndroidQ及以上还需要添加该权限 -->
 <uses-permission android:name="android.permission.READ_PRIVILEGED_PHONE_STATE" />

 <uses-feature android:name="android.hardware.camera"/>
 <uses-feature android:name="android.hardware.camera.autofocus"/>

防止代码混淆

proguard-rules.pro 文件中,为 NERTC SDK 添加 -keep 类的配置,这样可以防止混淆 NERTC SDK 公共类名称。

-keep class com.netease.lava.** {*;}
-keep class com.netease.yunxin.** {*;}

实现互动直播

初始化

import com.netease.lava.nertc.sdk.NERtcCallback;
import com.netease.lava.nertc.sdk.NERtcConstants;
import com.netease.lava.nertc.sdk.NERtcEx;
import com.netease.lava.nertc.sdk.NERtcParameters;
import com.netease.lava.nertc.sdk.video.NERtcRemoteVideoStreamType;
import com.netease.lava.nertc.sdk.video.NERtcVideoView;
// 示例
private void initializeSDK() {
        try {
            NERtcEx.getInstance().init(getApplicationContext(),Config.APP_KEY,this,null);
        } catch (Exception e) {
            showToast("SDK初始化失败");
            finish();
            ...
            return;
        }
        ...
}

设置本地视图

启动视频流

在加入频道前或后自行调用该方法启动视频流。

/**
 * 开启/关闭本地视频采集以及发送
 * @param enable    true: 开启,false : 关闭
 * @return   返回 0 代表调用成功,否则为失败
 */
public abstract int enableLocalVideo(boolean enable);
NERtcEx.getInstance().enableLocalVideo(enable);

设置本地视频画布

启动视频流后,可以设置本地视频画布,用来显示本地采集的视频画面。

/**
 * 设置本地预览画布,同一个画布只能设置给一个用户
 * @param render    视频绘制画布
 * @return   返回 0 代表调用成功,否则为失败
 */
public abstract int setupLocalVideoCanvas(com.netease.lava.api.IVideoRender render);
 private void setupLocalVideo() {
    NERtcVideoView localView = (NERtcVideoView)findViewById(R.id.local_view);
    NERtcEx.getInstance().setupLocalVideoCanvas(localView);
}

设置直播模式

在互动直播的场景中,我们建议在加入频道前,设置频道模式为直播模式。当前默认为通信模式。

// 0 - COMMUNICATION,  1 - LIVE_BROADCASTING
NERtcEx.getInstance().setChannelProfile(1);

设置推流开关

在加入频道前,还需要打开推流开关,这样拉流的观众可以拉取到对应的音视频流。

对应的参数为 NERtcParameters 的 KEY_PUBLISH_SELF_STREAM 。

mRtcParameters.set(NERtcParameters.KEY_PUBLISH_SELF_STREAM, true);
NERtcEx.getInstance().setParameters(mRtcParameters); 

加入频道

加入频道前,请确保已完成初始化相关事项。若您的业务中涉及呼叫邀请等机制,可以使用信令

/**
 * 加入频道
 * @param token    频道token
 * @param channelName    频道名称
 * @param uid    用户的唯一标识Id,频道内每个用户的 UID 必须是唯一的。
 * @return   返回 0 代表调用成功,否则为失败
 */
public abstract int joinChannel(java.lang.String token,
                                java.lang.String channelName,
                                            long uid);
    private void joinChannel() {
        NERtcEx.getInstance().joinChannel(token,channelName,uid);
    }

初始化方法中传入的callback,其原型为 NERtcCallback。用户需要实现该接口来完成各种状态回调的处理。其中,onJoinChannel 回调为本端加入频道结果的回调。

/**
 * 加入频道结果回调
 * @param result   0 代表加入成功,其他为失败,参考 NERtcConstants.ErrorCode
 * @param channelId    频道id
 * @param elapsed    加入频道总耗时(毫秒)
 */
void onJoinChannel(int  result,
                   long channelId,
                   long elapsed);

推流任务

在成功加入房间( onJoinChannel )后,需要设置推流任务。典型的业务场景里是由主播进行设置。注意:此处的主播业务层面的角色,NERTC SDK体系内不区分主播与连麦者。

推流任务也可通过 服务端API 进行管理,请根据您的业务需求选择对应的方式。

NERTC中推流任务相关的类如下:

描述
NERtcLiveStreamTaskInfo 推流任务
NERtcLiveStreamLayout 视频布局
NERtcLiveStreamUserTranscoding 直播成员布局
NERtcLiveStreamImageInfo 背景图信息

增加推流任务

/**
 * 添加房间推流任务,成功添加后当前用户可以收到该直播流的状态通知。通话中有效。
 * @param taskInfo   直播任务信息
 * @param addLiveTaskCallback    操作结果回调,方法调用成功才有回调
 * @return  0 方法调用成功 , 其他失败。
 */
public abstract int addLiveStreamTask(NERtcLiveStreamTaskInfo taskInfo,
                                      AddLiveTaskCallback addLiveTaskCallback);
    // 初始化推流任务
    NERtcLiveStreamTaskInfo liveTask1 = new NERtcLiveStreamTaskInfo();
    //taskID 可选字母、数字,下划线,不超过64位
    liveTask1.taskId = String.valueOf(Math.abs(pushUlr.hashCode()));
    // 一个推流地址对应一个推流任务
    liveTask1.url = pushUlr;
    // 不进行直播录制,请注意与音视频服务端录制区分。
    liveTask1.serverRecordEnabled = false;
    // 设置推音视频流还是纯音频流
    liveTask1.liveMode = enableVideo? NERtcLiveStreamTaskInfo.NERtcLiveStreamMode.kNERtcLsModeVideo : NERtcLiveStreamTaskInfo.NERtcLiveStreamMode.kNERtcLsModeAudio;

    //设置整体布局
    NERtcLiveStreamLayout layout = new NERtcLiveStreamLayout();
    layout.userTranscodingList = new ArrayList<>();
    layout.width = 720;//整体布局宽度
    layout.height = 1280;//整体布局高度
    layout.backgroundColor = Color.parseColor("#3399ff"); // 整体背景色
    liveTask1.layout = layout;

    // 设置直播成员布局
    NERtcLiveStreamUserTranscoding user1 = new NERtcLiveStreamUserTranscoding();
    user1.uid = uid1; // 用户id
    user1.audioPush = true; // 推流是否发布user1 的音频
    user1.videoPush = enableVideo; // 推流是否发布user1的视频
    if (user1.videoPush) {// 如果发布视频,需要设置一下视频布局参数
        // user1 视频的缩放模式, 详情参考NERtcLiveStreamUserTranscoding 的API 文档
        user1.adaption = NERtcLiveStreamUserTranscoding.NERtcLiveStreamVideoScaleMode.kNERtcLsModeVideoScaleCropFill;
        user1.x = 10; // user1 的视频布局x偏移,相对整体布局的左上角
        user1.y = 10; // user1 的视频布局y偏移,相对整体布局的左上角
        user1.width = 180; // user1 的视频布局宽度
        user1.height = 320; //user1 的视频布局高度
    }

    layout.userTranscodingList.add(user1);

     ...
    // 设置第n位直播成员布局
    NERtcLiveStreamUserTranscoding usern = new NERtcLiveStreamUserTranscoding();
    usern.uid = uidn;
    usern.audioPush = true;
    usern.videoPush = enableVideo;
    if (usern.videoPush) {
            usern.adaption = NERtcLiveStreamUserTranscoding.NERtcLiveStreamVideoScaleMode.kNERtcLsModeVideoScaleCropFill;
            usern.x = user1.x + user1.width + 10;
            usern.y = user1.y + user1.height + 10;
            usern.width = 320;
            usern.height = 640;
        }
    layout.userTranscodingList.add(usern);

    // 调用 addLiveStreamTask 接口添加推流任务
    int ret = NERtcEx.getInstance().addLiveStreamTask(liveTask1, addLiveTaskCallback);
    if (ret != 0) {
            showToast("调用添加推流任务失败 , ret : " + ret);
        }

    // 添加推流任务的异步callback
    private AddLiveTaskCallback addLiveTaskCallback = new AddLiveTaskCallback(){
      void onAddLiveStreamTask(String taskId, int errCode){
         if (code == RtcCode.LiveCode.OK) {
                showToast("添加推流任务成功 : taskId " + taskId);
            } else {
                showToast("添加推流任务失败 : taskId " + taskId + " , code : " + code);
            }
         }
    };

更新推流任务

/**
 * 更新修改房间推流任务。通话中有效。
 * @param taskInfo   直播任务信息
 * @param updateLiveTaskCallback    操作结果回调,方法调用成功才有回调
 * @return  0 方法调用成功 , 其他失败。
 */
public abstract int updateLiveStreamTask(NERtcLiveStreamTaskInfo taskInfo,
                                         UpdateLiveTaskCallback updateLiveTaskCallback);
    // 初始化新推流任务。注意:其中的taskid保持不变。
    NERtcLiveStreamTaskInfo updateLiveTask1 = preLiveTask;
    ...
    // 更新推流任务
    int ret = NERtcEx.getInstance().updateLiveStreamTask(updateLiveTask1 , updateLiveTaskCallback);
    if (ret != 0) {
        showToast("调用更新推流任务失败 , ret : " + ret);
    }
    // 更新推流任务的异步callback
    private UpdateLiveTaskCallback updateLiveTaskCallback = new UpdateLiveTaskCallback() {
        @Override
        public void onUpdateLiveStreamTask(String taskId, int errCode) {
            if (code == RtcCode.LiveCode.OK) {
                showToast("更新推流任务成功 : taskId " + taskId);
             } else {
                showToast("更新推流任务失败 : taskId " + taskId + " , code : " + code);
             }
        }
    };

删除推流任务

当不再需要推流时,可以选择删掉之前配置的推流任务。

注意:推流任务不会随着成员的离开而自动结束,成员离开只是结束了自己的推流,只有在手动删除或者频道所有成员都离开时才会结束。

/**
 * 删除房间推流任务。通话中有效
 * @param taskInfo   直播任务信息
 * @param deleteLiveTaskCallback    操作结果回调,方法调用成功才有回调
 * @return  0 方法调用成功 , 其他失败。
 */
public abstract int removeLiveStreamTask(java.lang.String taskId,
                                         DeleteLiveTaskCallback deleteLiveTaskCallback);
    int ret = NERtcEx.getInstance().removeLiveStreamTask(preLiveTask.taskId,deleteLiveTaskCallback );
    if (ret != 0) {
        showToast("调用删除推流任务失败 : ret : " + ret);
    }
    // 删除推流任务的异步callback
    private DeleteLiveTaskCallback deleteLiveTaskCallback = new DeleteLiveTaskCallback() {
        @Override
        public void onDeleteLiveStreamTask(String taskId, int errCode) {
            if (code == RtcCode.LiveCode.OK) {
                showToast("删除推流任务成功 : taskId " + taskId);
             } else {
                showToast("删除推流任务失败 : taskId " + taskId + " , code : " + code);
             }
        }
    };

推流任务相关错误码

在增加、更新与删除推流任务时当发生错误时,callback回调出的错误码RtcCode.LiveCode列举如下:

错误码 含义
OK = 0 成功
TASK_REQUEST_INVALID = 1301 task请求无效,被后续操作覆盖
TASK_IS_INVALID = 1400 task参数格式错误
TASK_ROOM_EXITED = 1401 已经退出频道
TASK_NUM_LIMIT = 1402 推流任务超出上限
TASK_DUPLICATE_ID = 1403 推流ID重复
TASK_NOT_FOUND = 1404 taskId任务不存在,或频道不存在
TASK_REQUEST_ERR = 1417 请求失败
TASK_INTERNAL_SERVER_ERR = 1500 服务器内部错误
TASK_INVALID_LAYOUT = 1501 布局参数错误
TASK_USER_PIC_ERR = 1502 用户图片错误

推流状态回调

主播/连麦者参与互动直播的过程中,可以通过监听onLiveStreamState来接收与推流状态相关的信息:

    /**
     * 推流状态回调
     * @param taskId 推流task id
     * @param pushUrl 推流url
     * @param liveState  推流状态, {@link NERtcConstants.LiveStreamState}
     */
    public void onLiveStreamState(String taskId, String pushUrl, int liveState);;

其中,关于NERtcConstants.LiveStreamState

状态码 含义
NERtcConstants.LiveStreamState.STATE_PUSHING = 505 推流中
NERtcConstants.LiveStreamState.STATE_PUSH_FAIL = 506 互动直播推流失败
NERtcConstants.LiveStreamState.STATE_PUSH_STOPPED = 511 推流结束
NERtcConstants.LiveStreamState.STATE_IMAGE_ERROR = 511 背景图片设置出错

API参考

方法 功能描述
setParameters 设置参数大部分参数需要在NERtc.init(Context, String, NERtcCallback, NERtcOption)前设置才能生效
addLiveStreamTask 添加房间推流任务,成功添加后当前用户可以收到该直播流的状态通知
updateLiveStreamTask 更新修改房间推流任务
removeLiveStreamTask 删除房间推流任务
事件 事件描述
onLiveStreamState 直播推流状态回调

设置远端视图

视频通话过程中,除了要显示本地的视频画面,通常也要显示参与通话的其他用户的远端视频画面。

监听远端用户进出频道

NERtcCallback提供监听远端用户进出频道的回调。

// 监听远端用户进入频道
void onUserJoined(long uid);

// 监听远端用户离开频道
void onUserLeave(long uid, int reason);

设置远端视频画布

监听到远端用户加入频道后,可以设置远端视频画布,用来显示远端用户的视频画面。

/**
 * 设置远端用户视频画布
 * @param render    视频绘制画布
 * @return   返回 0 代表调用成功,否则为失败
 */
public abstract int setupRemoteVideoCanvas(com.netease.lava.api.IVideoRender render, long uid);
private void setupRemoteVideo(long uid) {
    NERtcVideoView remoteView = (NERtcVideoView)findViewById(R.id.remote_view);
    NERtcEx.getInstance().setupRemoteVideoCanvas(remoteView,uid);
  }

监听远端视频流发布

当频道中的其他用户有视频流发出/关闭时,分别会走入NERtcCallback中的以下回调:

// 监听远端用户视频流发布
void onUserVideoStart(long uid, int maxProfile);

// 监听远端用户关闭视频流
void onUserVideoStop(long uid);
参数 描述
uid 远端用户ID
maxProfile 用户发送视频的最大分辨率类型,如:
HD1080p:1080P
HD720P:超清
LOW:标清
STANDARD:高清

订阅远端视频流

在设置完远端视频画布后,且监听到远端用户有视频发布时,可以订阅远端用户的视频流。订阅成功后,即可显示远端的视频画面。

/**
 * 订阅 / 取消订阅指定远端用户的视频流
 * @param uid           用户ID
 * @param streamType    订阅的画质 NERtcRemoteVideoStreamType
 * @param subscribe     true: 订阅指定视频流(默认)false: 取消订阅指定视频流
 * @return   返回 0 代表调用成功,否则为失败
 */
public abstract int subscribeRemoteVideoStream(long uid,
                                               NERtcRemoteVideoStreamType streamType,
                                               boolean subscribe);
private void subscribeRemoteVideo(long uid, int profile, boolean subscribe) {
    NERtcEx.getInstance().subscribeRemoteVideoStream(uid, profile, subscribe);
  }

音频流

在 NERtcSDK 中,本地音频的采集发布和远端音频订阅播放是默认启动的,正常情况下无需开发者主动干预。

离开频道

当通话结束,需要离开频道,可以调用以下接口:

// 离开频道
public abstract int leaveChannel();
private void leaveChannel() {
    NERtcEx.getInstance().leaveChannel();
}

NERtcCallback中的 onLeaveChannel 回调为本端离开频道结果的回调。

/**
 * 离开频道回调
 * @param result  0代表退出成功,其他为失败,参见 NERtcConstants.ErrorCode
 */
void onLeaveChannel(int result);

销毁音视频实例

释放当前音视频实例,建议在 App 确定不再需要使用音视频 实例时,通过该接口释放实例的对象资源。

NERtcEx.getInstance().release();