音视频通话 2.0
Android

实现音视频通话

更新时间: 2023/08/22 10:45:32

网易云信音视频通话产品的基本功能包括高质量的实时音视频通话。当您成功初始化 SDK 之后,您可以简单体验本产品的基本业务流程。本文档为您展示音视频通话提供的基本业务流程。

前提条件

请确认您已完成以下操作:

示例代码

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

xml 界面的完整示例代码
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <TextView
        android:id="@+id/tv_channel_name"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerHorizontal="true"
        android:layout_margin="12dp"
        android:text="频道名称:xxx"
        android:textSize="16sp" />

    <FrameLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_below="@id/tv_channel_name">

        <com.netease.lava.nertc.sdk.video.NERtcVideoView
            android:id="@+id/render_remote_user"
            android:layout_width="match_parent"
            android:layout_height="match_parent" />


        <TextView
            android:id="@+id/tv_remote_uid"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_centerHorizontal="true"
            android:layout_margin="12dp"
            android:text="对端uid:xxx"
            android:textSize="16sp" />
    </FrameLayout>

    <FrameLayout
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_below="@id/tv_channel_name"
        android:layout_alignParentEnd="true"
        android:layout_margin="12dp">

        <com.netease.lava.nertc.sdk.video.NERtcVideoView
            android:id="@+id/render_local_user"
            android:layout_width="135dp"
            android:layout_height="240dp" />

        <TextView
            android:id="@+id/tv_local_uid"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_centerHorizontal="true"
            android:layout_margin="12dp"
            android:text="本地uid:xxx"
            android:textSize="16sp" />

    </FrameLayout>


    <LinearLayout
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"
        android:layout_centerHorizontal="true"
        android:orientation="horizontal">

        <Button
            android:id="@+id/btn_enable_video"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_margin="12dp"
            android:text="关闭视频"
            android:textColor="@color/black" />


        <Button
            android:id="@+id/btn_enable_audio"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_margin="12dp"
            android:text="关闭语音"
            android:textColor="@color/black" />

        <Button
            android:id="@+id/btn_leave_channel"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_margin="12dp"
            android:text="结束通话"
            android:textColor="@color/black" />

    </LinearLayout>

</RelativeLayout>
实现音视频通话的完整示例代码
NERtcVideoView localRenderView = findViewById(R.id.render_view_local);
//小画布置于大画布上面
localRenderView.setZOrderMediaOverlay(true);

NERtcVideoView remoteRenderView = findViewById(R.id.render_view_remote);

NERtcCallback neRtcCallback = new NERtcCallbackEx() {
    @Override
    public void onJoinChannel(int result, long channelId, long elapsed, long uid) {
        if (result == NERtcConstants.ErrorCode.OK) {
            // 加入房间成功
        } else {
            // 加入房间失败 ,退出页面
            finish();
        }
    }

    @Override
    public void onLeaveChannel(int result) {
    }

    @Override
    public void onUserJoined(long uid) {
    }

    @Override
    public void onUserLeave (long uid,int reason) {
    }

    @Override
    public void onUserAudioStart(long uid) {
    }

    @Override
    public void onUserAudioStop(long uid) {
    }

    @Override
    public void onUserVideoStart(long uid,int maxProfile) {
        //对方开启视频,按需设置画布及订阅视频
        NERtcEx.getInstance().setupRemoteVideoCanvas(remoteRenderView,uid);
        NERtcEx.getInstance().subscribeRemoteVideoStream(uid, NERtcRemoteVideoStreamType.kNERtcRemoteVideoStreamTypeHigh,true);
    }

    @Override
    public void onUserVideoStop(long uid) {
        //释放之前绑定的画布
        NERtcEx.getInstance().setupRemoteVideoCanvas(null,uid);
    }

    @Override
    public void onDisconnect(int reason) {
        // 与服务器断连 ,退出页面
        finish();
    }

    //*******其他一些回调******
};

//初始化SDK
try {
    NERtc.getInstance().init(getApplicationContext(), "your_app_key", neRtcCallback, null);
} catch (Exception e) {
    e.printStackTrace();
    showToast("SDK初始化失败");
    finish();
    return;
}

//设置本地视频参数
NERtcVideoConfig videoConfig = new NERtcVideoConfig();
videoConfig.width = 1080;
videoConfig.height = 720;
videoConfig.frameRate = NERtcEncodeConfig.NERtcVideoFrameRate.FRAME_RATE_FPS_30;
NERtcEx.getInstance().setLocalVideoConfig(videoConfig);

//加入房间前提前设置好视频开启状态
NERtcEx.getInstance().enableLocalVideo(true,kNERtcVideoStreamTypeMain);

//设置本地画布
NERtcEx.getInstance().setupLocalVideoCanvas(localRenderView);

//加入房间
long yourUid = 1234567;//同一个房间不允许相同uid
NERtcEx.getInstance().joinChannel("your_token", "your_channel_name", yourUidnull);

//结束通话
NERtcEx.getInstance().leaveChannel();

//释放SDK
NERtcEx.getInstance().release();

实现流程

实现音频通话的 API 调用时序如下图所示。

sequenceDiagram
    autonumber
    participant App
    participant NERtcSDK
    participant 云信服务端


    App->>NERtcSDK: initialize 初始化 NERtcEngine
    App->>NERtcSDK: joinChannel 加入房间
    NERtcSDK->>云信服务端: 请求加入房间
    NERtcSDK-->>App: onJoinChannel
    App->>NERtcSDK: leaveChannel 离开房间
    NERtcSDK->>云信服务端: 请求离开房间
    App->>NERtcSDK: release 销毁实例

实现视频通话的 API 调用时序如下图所示。

sequenceDiagram
    autonumber
    participant App
    participant NERtcSDK
    participant 云信服务端

    App->>NERtcSDK: initialize 初始化 NERtcEngine

    Note over App, NERtcSDK: 设置本地视图
    App->>NERtcSDK: setupLocalVideoCanvas
    App->>NERtcSDK: enableLocalVideo

    Note over App, NERtcSDK: 加入房间
    App->>NERtcSDK: joinChannel
    NERtcSDK->>云信服务端: 请求加入房间
    NERtcSDK-->>App: onJoinChannel

    Note over App, NERtcSDK: 设置远端视图
    Note right of NERtcSDK: 远端用户加入房间
    NERtcSDK-->>App: onUserJoined 远端用户加入房间的回调
    NERtcSDK-->>App: onUserVideoStart 远端用户发布视频流的回调
    App->>NERtcSDK: setupRemoteVideoCanvas 设置远端视频画布
    App->>NERtcSDK: subscribeRemoteVideoStream 订阅远端视频流
    云信服务端-->>App : onFirstVideoFrameDecoded 已接收到远端视频首帧并完成解码的回调

    Note over App, NERtcSDK: 离开房间
    App->>NERtcSDK: leaveChannel
    NERtcSDK->>云信服务端: 请求离开房间

    App->>NERtcSDK: release 销毁实例

实现音视频通话

步骤一 (可选)创建音视频通话界面

您可以参考此步骤根据业务场景创建相应的音视频通话界面,若您已实现相应界面,请忽略该步骤。

实现基础的音视频通话,建议您参考 xml 界面的示例代码在界面上添加以下控件。

  • 房间 ID
  • 用户昵称
  • 本端视频窗口
  • 远端视频窗口
  • 麦克风按钮
  • 摄像头按钮
  • 结束通话按钮

效果图如下图所示。

android效果图.png

步骤二 导入类

在您的工程中对应实现音视频通话的 Activity 文件里添加如下代码先导入以下重要类:

import com.netease.lava.nertc.sdk.NERtcCallbackEx;
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;

步骤三 初始化

默认情况下,请在导入后的 nertc-sdk-4.x.xx.jar 文件中先执行 init 方法完成初始化。

您需要将 App_Key 替换为您的应用对应的 App Key。

示例代码如下:

private void initializeSDK() {
      try {
          NERtcEx.getInstance().init(getApplicationContext(),Config.APP_KEY,callback,null);
      } catch (Exception e) {
          showToast("SDK初始化失败");
          finish();
          ...
          return;
      }
      ...
}

为了实现标准音视频通话业务,您还需要在初始化时注册相关必要回调,建议您请在初始化方法中传入原型为 NERtcCallbackEx 的以下回调,并增加相应必要的处理。

//NERtcCallbackEx 重要回调

//本端用户加入房间结果回调
@Override
public void onJoinChannel(int result, long channelId, long elapsed, long uid) {
    if (result == NERtcConstants.ErrorCode.OK) {
        // 加入房间成功
    } else {
        // 加入房间失败 ,退出页面
    }
}

//本端用户离开房间回调
@Override
public void onLeaveChannel(int result) {
}

//远端用户加入房间
@Override
public void onUserJoined(long uid) {
}

//远端用户离开房间
@Override
public void onUserLeave (long uid,int reason) {
}

//远端用户打开音频
@Override
public void onUserAudioStart(long uid) {
}

//远端用户关闭音频
@Override
public void onUserAudioStop(long uid) {
}

//远端用户打开视频,建议在此按需设置画布及订阅视频
@Override
public void onUserVideoStart(long uid,int maxProfile) {
}

//远端用户关闭视频,可释放之前绑定的画布
@Override
public void onUserVideoStop(long uid) {
}

//与服务器断连,退出页面
@Override
public void onDisconnect(int reason) {
}

步骤四 设置本地视图

初始化成功后,可以设置本地视图,来预览本地图像。您可以根据业务需要实现加入房间之前预览或加入房间后预览。

  • 若您想设置画布渲染参数,可以调用 setScalingType 方法设置渲染缩放模式或调用 setMirror 方法设置镜像模式。
  • 若您想调整摄像头的相关参数,请参考视频设备管理进行设置。
  • 在加入房间前,默认预览分辨率为 640*480,您可以通过 setLocalVideoConfig 接口的 width height 参数调整采集分辨率。
  • 实现加入房间前预览。

    1. 调用 setupLocalVideoCanvasstartVideoPreview(streamType) 方法,在加入房间前设置本地视图,预览本地图像。

      示例代码如下:

      //以开启本地视频主流预览为例
      NERtcVideoView localView = (NERtcVideoView)findViewById(R.id.render_view_local);
      NERtcEx.getInstance().setupLocalVideoCanvas(localView);
      NERtcEx.getInstance().startVideoPreview(kNERtcVideoStreamTypeMain);
      
      //设置本地视频画面的渲染模式:以保证原始视频尺寸比例为例(可选)
      NERtcEx.getInstance().setScalingType(NERtcConstants.VideoScalingType.SCALE_ASPECT_FIT);
      
      //设置本地视频画面的镜像模式:以开启镜像为例(可选)
      NERtcEx.getInstance().setMirror(true);
      
    2. 若要结束预览,或者准备加入房间时,调用 stopVideoPreview(streamType) 方法停止预览。

      stopVideoPreview(streamType)streamType 参数请与 startVideoPreview(streamType) 的保持一致,即同为主流或辅流的开启和停止预览。

  • 实现加入房间后预览。

    调用 setupLocalVideoCanvas 设置本地视图,再调用 enableLocalVideo(streamType) 方法进行视频的采集发送与预览。成功加入房间后,即可预览本地图像。

    示例代码如下:

    //设置本地预览画布
    NERtcVideoView localView = (NERtcVideoView)findViewById(R.id.render_view_local);
    NERtcEx.getInstance().setupLocalVideoCanvas(localView);
    
    //以开启本地视频主流采集并发送为例
    NERtcEx.getInstance().enableLocalVideo(true,kNERtcVideoStreamTypeMain);
    
    //设置本地视频画面的渲染模式:以保证原始视频尺寸比例为例(可选)
    NERtcEx.getInstance().setScalingType(NERtcConstants.VideoScalingType.SCALE_ASPECT_FIT);
    
    //设置本地视频画面的镜像模式:以开启镜像为例(可选)
    NERtcEx.getInstance().setMirror(true);
    

步骤五 加入房间

加入房间前,请确保已完成初始化相关事项。若您的业务中涉及呼叫邀请等机制,建议通过信令实现,总体实现流程请参见一对一会话操作流程,具体呼叫邀请机制的实现请参见邀请机制

调用 joinChannel 方法加入房间。

调用 joinChannel 之后,NERTC SDK 会通过 Android 的 AudioManager.setMode() 方法调整音频模式(audio mode),此后请勿修改 SDK 调整过的音频模式,否则会导致音频路由错误等问题。

示例代码如下:

NERtcEx.getInstance().joinChannel(token,channelName,uid,channelOptions);

参数说明

参数 说明
token 安全认证签名(NERTC Token)。
  • 调试模式下:可设置为 null。产品默认为安全模式,您可以在网易云信控制台将鉴权模式修改为调试模式,具体请参见Token 鉴权
    调试模式的安全性不高,请在产品正式上线前修改为安全模式。
  • 产品正式上线后:请设置为已获取的NERTC Token。安全模式下必须设置为获取到的 Token 。若未传入正确的 Token 将无法进入房间。

    推荐使用安全模式

channelName 房间名称,长度为 1 ~ 64 字节。目前支持以下 89 个字符:a-z, A-Z, 0-9, space, !#$%&()+-:;≤.,>? @[]^_{|}~"。
设置相同房间名称的用户会进入同一个通话房间。
您也可以在加入通道前,通过创建房间接口创建房间。加入房间时,若传入的 {channelName} 未事先创建,则云信服务器内部将为其自动创建一个名为 {channelName} 的通话房间。
uid 用户的唯一标识 id,为数字串,房间内每个用户的 uid 必须是唯一的。此 uid 为用户在您应用中的 ID,请在您的业务服务器上自行管理并维护。
channelOptions 加入房间时可以设置携带一些特定信息,包括高级权限密钥。默认值为 NULL,具体请参考 NERtcJoinChannelOptions
  • SDK 发起加入房间请求后,服务器会进行响应,您可以通过 NERtcCallbackonJoinChannel 回调监听加入房间的结果,同时该回调会抛出当前通话房间的 channelId 与加入房间总耗时(毫秒);其中 channelId 即音视频通话的 ID,建议您在业务层保存该数据,以便于后续问题排查。

  • 成功加入房间之后,您可以通过监听 onConnectionStateChanged 回调实时监控自己在本房间内的连接状态。

步骤六 设置远端视图并发起订阅

音视频通话过程中,除了要显示本地的视频画面,通常也要显示参与互动的其他连麦者/主播的远端视频画面。

设置远端视图并订阅的完整示例代码
//对方开启视频,按需设置画布及订阅视频
NERtcVideoView remoteRenderView = findViewById(R.id.render_view_remote);
NERtcEx.getInstance().setupRemoteVideoCanvas(remoteRenderView,uid);
NERtcEx.getInstance().subscribeRemoteVideoStream(uid, NERtcRemoteVideoStreamType.kNERtcRemoteVideoStreamTypeHigh,true);
  1. 监听远端用户进出房间。

    当远端用户加入房间时,本端会触发 onUserJoined 回调,并抛出对方的 uid。

    当本端加入房间后,也会通过此回调抛出通话房间内已有的其他用户。

  2. 设置远端视频画布。

    在监听到远端用户加入房间或发布视频流后,本端可以调用 setupRemoteVideoCanvas 方法设置远端用户视频画布,用于显示其视频画面。

    示例代码如下:

    NERtcVideoView remoteRenderView = findViewById(R.id.render_view_remote);
    NERtcEx.getInstance().setupRemoteVideoCanvas(remoteRenderView,uid);
    
  3. 监听远端视频流发布。

    当房间中的其他用户发布视频流时,本端会触发 onUserVideoStart 回调。

  4. 订阅远端视频流。

    在监听到远端用户发布视频流后,本端可以调用 subscribeRemoteVideoStream 方法对其发起视频流的订阅,来将对方的视频流渲染到视频画布上。

    示例代码如下:

    NERtcEx.getInstance().subscribeRemoteVideoStream(uid, streamType, subscribe);
    
  5. 监听远端用户离开房间或关闭视频功能。

步骤七 音频流

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

步骤八 退出通话房间

调用 leaveChannel 方法退出通话房间。

示例代码如下:

NERtcEx.getInstance().leaveChannel();

NERtcCallback 提供 onLeaveChannel 回调来监听当前用户退出房间的结果。

步骤九 销毁实例

当确定 App 短期内不再使用音视频通话实例时,可以调用 release 方法释放对应的对象资源。

示例代码如下:

// 销毁实例
NERtcEx.getInstance().release();
此文档是否对你有帮助?
有帮助
去反馈
  • 前提条件
  • 示例代码
  • 实现流程
  • 实现音视频通话
  • 步骤一 (可选)创建音视频通话界面
  • 步骤二 导入类
  • 步骤三 初始化
  • 步骤四 设置本地视图
  • 步骤五 加入房间
  • 步骤六 设置远端视图并发起订阅
  • 步骤七 音频流
  • 步骤八 退出通话房间
  • 步骤九 销毁实例