You need to enable JavaScript to run this app.
导航

Android

最近更新时间2024.03.13 15:27:32

首次发布时间2022.04.28 16:43:35

本文介绍如何集成火山引擎 RTC SDK,并实现实时音视频通话。根据如下步骤操作,即可从 0 开始构建一个简单的音视频通话应用。
你也可以参考示例项目,了解更完整的项目实现。

前提条件

在开始集成 RTC SDK 前,请确保满足以下要求:

  • Android Studio Arctic Fox | 2020.3.1 或以上版本(本文使用 Android Studio Giraffe | 2022.3.1 版本)

  • Android 4.4 或以上版本的 Android 真机或模拟器

    说明

    推荐使用真机进行调试,连接指南参看在硬件设备上运行应用

  • Android 设备和开发电脑可以正常访问互联网

  • 火山引擎控制台开通实时音视频服务,你需要从控制台获取 AppID 和临时 Token 用于项目跑通

创建和配置项目

新建项目

  1. 打开 Android Studio。在 Welcome to Android Studio 窗口中,单击 New Project

  2. 在项目模板页选择 Empty Views Activity,然后单击 Next

  3. 在项目配置页,设置项目名称、软件包名称、存储路径等信息,开发语言选择 Java,最低 API 级别选择 19 或以上,build 配置语言选择 Groovy,完成后单击 Finish。经过一段时间的处理后,Android Studio 主窗口会出现,此时你已经构建好了 Android 工程,可以开始编码了。

配置权限

SDK 已在内部声明所需权限,无需手动添加。对于敏感权限,你需要在 Activity 中动态申请,本文动态申请权限章节将提供示例代码。

说明

  • 如果你的应用以 Android 11(API 级别 30)或更高版本为目标平台,且希望在应用退到后台时仍能进行麦克风采集,则需在 AndroidManifest.xml 文件中声明前台服务类型 android:foregroundServiceType:microphone。此外,如果你的应用以 Android 14(API 级别 34)或更高版本为目标平台,则必须针对前台服务将要执行的工作类型请求 FOREGROUND_SERVICE_MICROPHONE 权限。摄像头权限同理。详细说明和示例代码参看前台服务权限适配方法
  • 如果你的应用以 Android 12(API 级别 31)或更高版本为目标平台,且需使用蓝牙,则应在 AndroidManifest.xml 文件中额外声明 BLUETOOTH_CONNECT 权限,并在 Activity 中动态申请。示例代码参看应用的 targetSDKVersion >= 31 时如何配置蓝牙权限?
集成 SDK

通过 Maven 集成

  1. 在 Android Studio 左上角将工程视图切换为 Project 模式,在项目根目录的 settings.gradle 文件配置 Maven 仓库地址。

    dependencyResolutionManagement {
        repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
        repositories {
            ...
            maven {  url 'https://artifact.bytedance.com/repository/Volcengine/' }
            }
    }
    

    说明

    如果你的 Android Gradle Plugin 版本低于 v7.1.0,则应在项目根目录的 build.gradle 文件配置 Maven 仓库地址。

    allprojects {
        repositories {
            ...
            maven { url 'https://artifact.bytedance.com/repository/Volcengine/' }
        }
    }
    
  2. 在 App 的 build.gradle 文件中添加 RTC SDK 依赖。

    说明

    你需要将 '3.x.y.z' 替换为具体的版本号,最新版本号请参看下载 SDK

    dependencies {
        ...
        implementation 'com.volcengine:VolcEngineRTC:3.x.y.z' // 填写需要接入的 RTC SDK 版本号
    }
    
  3. 在项目根目录的 gradle.properties 文件中添加 android.enableJetifier=true,解决兼容性问题。

  4. 设置完成后,单击 Sync now 完成同步。

手动集成

说明

本章节介绍全量集成 RTC SDK 的方法。如需减小 App 体积,请参看按需集成插件

  1. 下载并解压火山引擎 RTC SDK文件。

  2. 在 Android Studio 左上角将工程视图切换为 Project 模式,将解压后的 VolcEngineRTC-lite.aareffectAAR-release_V4.4.3Lite.aar 放在 app/libs 目录下,解压的四个架构文件夹放置在 app/jniLibs 目录下。

    alt

  3. 在 App 的 build.gradle 文件中添加 RTC SDK 依赖和 .so 文件依赖。

    android {
        ...
        sourceSets {
            main {
                // 指定 .so 文件所在目录
                jniLibs.srcDirs = ['jniLibs']
            }
        }
    }
    ...
    dependencies {
        ...
        // 集成主库
        implementation files('libs/VolcEngineRTC-lite.aar')
        // 集成特效库,用于实现美颜和特效相关功能
        implementation files('libs/effectAAR-release_V4.4.3Lite.aar')
    }
    
  4. 在项目根目录的 gradle.properties 文件中添加 android.enableJetifier=true,解决兼容性问题。

  5. 设置完成后,单击 Sync now 完成同步。

创建用户界面

根据场景需要,为你的项目创建音视频通话的用户界面。为实现基本的音视频通话,建议用户界面中至少包含本地视频窗口、远端视频窗口、加入房间按钮、退出房间按钮。
将以下示例代码在 src/main/res/layout/activity_main.xml 中进行替换,即可快速创建用户界面。

<?xml version="1.0" encoding="utf-8"?>
<androidx.appcompat.widget.LinearLayoutCompat xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context=".examples.QuickStartActivity"
    android:layout_marginHorizontal="15dp">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="300dp"
        android:orientation="horizontal">

        <FrameLayout
            android:id="@+id/local_view_container"
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_weight="1"
            app:layout_constraintTop_toTopOf="parent"
            />

        <FrameLayout
            android:id="@+id/remote_view_container"
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_weight="1"
            app:layout_constraintTop_toTopOf="parent"
            />
    </LinearLayout>
    
    <androidx.appcompat.widget.AppCompatButton
        android:id="@+id/btn_join_room"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="加入房间"/>

    <androidx.appcompat.widget.AppCompatButton
        android:id="@+id/btn_leave_room"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="退出房间"/>
    
</androidx.appcompat.widget.LinearLayoutCompat>
实现音视频通话

本章节将先向你提供 API 调用时序图和完整的实现代码,再对具体的实现步骤展开介绍。

时序图

下图为使用火山引擎 RTC SDK 实现基础音视频通话的 API 调用时序图。

alt

完整示例代码

复制以下示例代码,替换 MainActivity.java 文件中 package com.example.<projectname>; 后的全部内容,此处的 com.example.<projectname> 即为你新建项目时指定的软件包名称。连接并选择你的 Android 设备,单击 Android Studio 窗口右上角的运行按钮(Run 'app'),即可快速实现音视频通话。

说明

你需要将代码中的 roomId、userId、APP_ID、token 替换为你在控制台上生成临时 Token 时所使用的房间 ID 和用户 ID,以及获取到的 AppID 和临时 Token。

// package com.example.<projectname>; 你的软件包名称

import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.widget.AppCompatButton;
import androidx.core.content.ContextCompat;

import android.Manifest;
import android.content.pm.PackageManager;
import android.os.Build;
import android.os.Bundle;
import android.view.TextureView;
import android.widget.FrameLayout;

import com.ss.bytertc.engine.RTCRoom;
import com.ss.bytertc.engine.RTCRoomConfig;
import com.ss.bytertc.engine.RTCVideo;
import com.ss.bytertc.engine.UserInfo;
import com.ss.bytertc.engine.VideoCanvas;
import com.ss.bytertc.engine.data.RemoteStreamKey;
import com.ss.bytertc.engine.data.StreamIndex;
import com.ss.bytertc.engine.handler.IRTCRoomEventHandler;
import com.ss.bytertc.engine.handler.IRTCVideoEventHandler;
import com.ss.bytertc.engine.type.ChannelProfile;
import com.ss.bytertc.engine.type.MediaStreamType;

public class MainActivity extends AppCompatActivity {

    private static final String APP_ID = ""; // 填写 appId
    private static final String roomId = ""; // 填写房间号
    private static final String userId = ""; //填写 userId
    private static final String token = ""; // 填写临时 token

    private FrameLayout localViewContainer;
    private FrameLayout remoteViewContainer;

    private RTCVideo rtcVideo;
    private RTCRoom rtcRoom;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        requestPermission();

        localViewContainer = findViewById(R.id.local_view_container);
        remoteViewContainer = findViewById(R.id.remote_view_container);

        ((AppCompatButton) findViewById(R.id.btn_join_room)).setOnClickListener(v -> {
            joinRoom(roomId);
        });

        ((AppCompatButton) findViewById(R.id.btn_leave_room)).setOnClickListener(v -> {
            leaveRoom();
        });

        // 创建引擎
        rtcVideo = RTCVideo.createRTCVideo(this, APP_ID, videoEventHandler, null, null);
        // 设置本端渲染视图
        setLocalRenderView();
        // 开启音视频采集
        rtcVideo.startVideoCapture();
        rtcVideo.startAudioCapture();
    }

    public void requestPermission() {
        String[] PERMISSIONS_STORAGE = {
                Manifest.permission.RECORD_AUDIO,
                Manifest.permission.CAMERA};

        if (Build.VERSION.SDK_INT  >= Build.VERSION_CODES.M) {
            if (ContextCompat.checkSelfPermission(this, "android.permission.CAMERA") != PackageManager.PERMISSION_GRANTED
                    || ContextCompat.checkSelfPermission(this, "android.permission.RECORD_AUDIO") != PackageManager.PERMISSION_GRANTED) {
                requestPermissions(PERMISSIONS_STORAGE, 22);
            }
        }
    }

    /**
     * 设置本地渲染视图,支持TextureView和SurfaceView
     */
    private void setLocalRenderView() {
        TextureView textureView = new TextureView(this);
        localViewContainer.removeAllViews();
        localViewContainer.addView(textureView);

        VideoCanvas videoCanvas = new VideoCanvas();
        videoCanvas.renderView = textureView;
        videoCanvas.renderMode = VideoCanvas.RENDER_MODE_HIDDEN;
        // 设置本地视频渲染视图
        rtcVideo.setLocalVideoCanvas(StreamIndex.STREAM_INDEX_MAIN, videoCanvas);
    }

    private void setRemoteRenderView(String uid) {
        TextureView remoteTextureView = new TextureView(this);
        remoteViewContainer.removeAllViews();
        remoteViewContainer.addView(remoteTextureView);
        VideoCanvas videoCanvas = new VideoCanvas();
        videoCanvas.renderView = remoteTextureView;
        videoCanvas.renderMode = VideoCanvas.RENDER_MODE_HIDDEN;

        RemoteStreamKey remoteStreamKey = new RemoteStreamKey(roomId, uid, StreamIndex.STREAM_INDEX_MAIN);
        // 设置远端视频渲染视图
        rtcVideo.setRemoteVideoCanvas(remoteStreamKey, videoCanvas);
    }

    /**
     * 引擎回调信息
     */
    IRTCVideoEventHandler videoEventHandler = new IRTCVideoEventHandler() {
    };

    IRTCRoomEventHandler rtcRoomEventHandler = new IRTCRoomEventHandler() {

        @Override
        public void onUserPublishStream(String uid, MediaStreamType type) {
            super.onUserPublishStream(uid, type);
            runOnUiThread(() -> setRemoteRenderView(uid));
        }

    };

    /**
     * 加入房间
     *  @param roomId
     */
    private void joinRoom(String roomId) {
        rtcRoom = rtcVideo.createRTCRoom(roomId);
        rtcRoom.setRTCRoomEventHandler(rtcRoomEventHandler);
        // 用户信息
        UserInfo userInfo = new UserInfo(userId, "");
        // 设置房间配置
        boolean isAutoPublish = true;
        boolean isAutoSubscribeAudio = true;
        boolean isAutoSubscribeVideo = true;
        RTCRoomConfig roomConfig = new RTCRoomConfig(ChannelProfile.CHANNEL_PROFILE_CHAT_ROOM, isAutoPublish, isAutoSubscribeAudio, isAutoSubscribeVideo);
        // 加入房间
        rtcRoom.joinRoom(token, userInfo, roomConfig);
    }

    /**
     * 离开房间
     */
    private void leaveRoom() {
        if (rtcRoom != null) {
            rtcRoom.leaveRoom();
            rtcRoom.destroy();
            rtcRoom = null;
        }
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        if (rtcVideo != null) {
            rtcVideo.stopVideoCapture();
            rtcVideo.stopAudioCapture();
        }
        RTCVideo.destroyRTCVideo();
    }
}

实现步骤详解

引入类

引入 SDK 需要的类。

import com.ss.bytertc.engine.RTCRoom;
import com.ss.bytertc.engine.RTCRoomConfig;
import com.ss.bytertc.engine.RTCVideo;
import com.ss.bytertc.engine.UserInfo;
import com.ss.bytertc.engine.VideoCanvas;
import com.ss.bytertc.engine.data.RemoteStreamKey;
import com.ss.bytertc.engine.data.StreamIndex;
import com.ss.bytertc.engine.handler.IRTCRoomEventHandler;
import com.ss.bytertc.engine.handler.IRTCVideoEventHandler;
import com.ss.bytertc.engine.type.ChannelProfile;
import com.ss.bytertc.engine.type.MediaStreamType;

动态申请权限

启动应用程序时,检查是否已在 App 中授予了所需的权限。

public void requestPermission() {
    String[] PERMISSIONS_STORAGE = {
            Manifest.permission.RECORD_AUDIO,
            Manifest.permission.CAMERA};

    if (Build.VERSION.SDK_INT  >= Build.VERSION_CODES.M) {
        if (ContextCompat.checkSelfPermission(this, "android.permission.CAMERA") != PackageManager.PERMISSION_GRANTED
                || ContextCompat.checkSelfPermission(this, "android.permission.RECORD_AUDIO") != PackageManager.PERMISSION_GRANTED) {
            requestPermissions(PERMISSIONS_STORAGE, 22);
        }
    }
}

初始化引擎

调用 createRTCVideo 初始化 RTCVideo 引擎,所有 RTC 相关的 API 调用都要在创建引擎之后。

// 创建引擎
rtcVideo = RTCVideo.createRTCVideo(this, APP_ID, videoEventHandler, null, null);

采集音视频

创建引擎后,调用 startVideoCapture 开启视频采集,调用 startAudioCapture 开启音频采集。

// 开启音视频采集
rtcVideo.startVideoCapture();
rtcVideo.startAudioCapture();

设置本地渲染视图

调用 setLocalVideoCanvas 设置本端渲染窗口,支持 TextureView 和 SurfaceView。

/**
 * 设置本地渲染视图,支持TextureView和SurfaceView
 */
private void setLocalRenderView() {
    TextureView textureView = new TextureView(this);
    localViewContainer.removeAllViews();
    localViewContainer.addView(textureView);

    VideoCanvas videoCanvas = new VideoCanvas();
    videoCanvas.renderView = textureView;
    videoCanvas.renderMode = VideoCanvas.RENDER_MODE_HIDDEN;
    // 设置本地视频渲染视图
    rtcVideo.setLocalVideoCanvas(StreamIndex.STREAM_INDEX_MAIN, videoCanvas);
}

创建并加入房间

按以下步骤创建并加入房间:

  1. 调用 createRTCRoom 创建 RTC 房间,所有和房间相关的 API 都在 RTCRoom 类。

  2. 调用 setRTCRoomEventHandler 设置房间回调监听。

  3. 调用 joinRoom 加入房间。

/**
 * 加入房间
 *  @param roomId
 */
private void joinRoom(String roomId) {
    rtcRoom = rtcVideo.createRTCRoom(roomId);
    rtcRoom.setRTCRoomEventHandler(rtcRoomEventHandler);
    // 用户信息
    UserInfo userInfo = new UserInfo(userId, "");
    // 设置房间配置
    boolean isAutoPublish = true;
    boolean isAutoSubscribeAudio = true;
    boolean isAutoSubscribeVideo = true;
    RTCRoomConfig roomConfig = new RTCRoomConfig(ChannelProfile.CHANNEL_PROFILE_CHAT_ROOM, isAutoPublish, isAutoSubscribeAudio, isAutoSubscribeVideo);
    // 加入房间
    rtcRoom.joinRoom(token, userInfo, roomConfig);
}

设置远端渲染视频

当远端用户加入频道并发布视频流时,在 onUserPublishStream 回调中调用 setRemoteVideoCanvas 设置并渲染远端视图。

IRTCRoomEventHandler rtcRoomEventHandler = new IRTCRoomEventHandler() {
    @Override
    public void onUserPublishStream(String uid, MediaStreamType type) {
        super.onUserPublishStream(uid, type);
        runOnUiThread(() -> setRemoteRenderView(uid));
    }

};

private void setRemoteRenderView(String uid) {
    TextureView remoteTextureView = new TextureView(this);
    remoteViewContainer.removeAllViews();
    remoteViewContainer.addView(remoteTextureView);
    VideoCanvas videoCanvas = new VideoCanvas();
    videoCanvas.renderView = remoteTextureView;
    videoCanvas.renderMode = VideoCanvas.RENDER_MODE_HIDDEN;

    RemoteStreamKey remoteStreamKey = new RemoteStreamKey(roomId, uid, StreamIndex.STREAM_INDEX_MAIN);
    // 设置远端视频渲染视图
    rtcVideo.setRemoteVideoCanvas(remoteStreamKey, videoCanvas);
}

离开房间

调用 leaveRoom 离开房间, 并调用 destroy 销毁房间实例。

/**
 * 离开房间
 */
private void leaveRoom() {
    if (rtcRoom != null) {
        rtcRoom.leaveRoom();
        rtcRoom.destroy();
        rtcRoom = null;
    }
}

销毁引擎

退出 app 时,调用 destroyRTCVideo 销毁 RTCVideo 引擎。

RTCVideo.destroyRTCVideo();

编译与运行

  1. 开启 Android 设备的开发者选项,打开 USB 调试,通过 USB 连接线将 Android 设备接入电脑,并在 Android 设备选项中勾选你的 Android 设备。详情参看在硬件设备上运行应用

  2. 单击 Android Studio 窗口右上角的运行按钮(Run 'app')开始编译。

  3. 你的 Android 设备会自动打开 Demo 应用,在弹窗中选择开启摄像头和麦克风权限。

  4. (可选)在第二台设备上使用相同的 AppID 和 RoomID,更换 UserID 并生成新的临时 Token,即可加入同一个房间体验双端通话。

常见问题