随着观众、主播、开发者对RTC软件的要求越来越高,仅仅实现了一对一视频通话和多人聊天室的App纷纷失去了生存的土壤,越来越多的RTC App争先恐后加入了背景音乐、配音音效和美声变声功能,选择一个合适的第三方服务成了一个App 蒸蒸日上或者黯然失色的关键点。


声网Agora的SDK不但可以让App和网站实现高质量的音视频通话全互动直播,还能够通过背景音乐混动、美声变声实现丰富的声动互娱功能。我试着通过该SDK实现一个双人视频通话应用,来演示Agora声动互娱的简单使用。


快速集成

Agora的集成方式是傻瓜式的一键集成,只需要在project的build.gradle里进行如下配置:

dependencies {
    ……

    implementation 'io.agora.rtc:full-sdk:4.1.1'
}

然后在module的build.gradle里进行如下配置:

buildscript {
    repositories {
        mavenCentral()
    }
}

最后sync一下,声网Agora.io的SDK就集成到项目中来了。

SDK集成完毕后,为了保证SDK能正常运行,我们需要在AndroidManisfest.xml 文件中声明以下权限:

<!--允许程序连接网络-->
<uses-permission android:name="android.permission.INTERNET" />
<!--允许程序录制音频-->
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<!--允许程序使用照相设备-->
<uses-permission android:name="android.permission.CAMERA" />
<!--允许程序修改全局音频设置-->
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
<!--允许程序获取网络状态-->
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<!--允许对存储空间进行读写-->
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"
    tools:ignore="ScopedStorage" />
<!--允许程序连接到已配对的蓝牙设备-->
<uses-permission android:name="android.permission.BLUETOOTH" />
<!-- 对于 Android 12.0 及以上设备,还需要添加如下权限: -->
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT"  />

集成SDK并声明了权限后,就该考虑混淆的问题了,我们需要在project的proguard-rules.pro文件里添加以下代码:

# 避免声网Agora SDK 的代码被混淆
-keep class io.agora.**{*;}

申请AppID和免费Token

Agora申请AppID和免费Token的入口在“Agora官网首页>控制台>项目管理”路径下,点击“新建项目”,就可以给你分配AppID

基于Agora SDK实现Android端的声动互娱 (一)——前提条件 _android

申请AppID是不需要应用包名的,因为不同包名的app可以互相通信,比如电商软件的商家端和买家端,直播软件的主播端和观众端等等。点击“生成临时Token”,就会生成一个有效期24小时的免费Token

基于Agora SDK实现Android端的声动互娱 (一)——前提条件 _xml_02

基础功能(音视频聊天室)的快速开发

首先我们写一个布局文件:

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
    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"
    tools:context=".MainActivity">
    
    <!--  对方的预览画面铺满全屏  -->
    <FrameLayout
        android:id="@+id/remote_video_view_container"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="@android:color/white" />
    
    <!--  本人的预览画面放在右上角  -->
    <FrameLayout
        android:id="@+id/local_video_view_container"
        android:layout_width="160dp"
        android:layout_height="320dp"
        android:layout_alignParentEnd="true"
        android:layout_alignParentRight="true"
        android:layout_alignParentTop="true"
        android:layout_marginEnd="16dp"
        android:layout_marginRight="16dp"
        android:layout_marginTop="16dp"
        android:background="@android:color/darker_gray"
        tools:ignore="MissingConstraints" />

</androidx.constraintlayout.widget.ConstraintLayout>

然后把这一段代码,复制粘贴到你的MainActivity里:

private String appId = "刚才申请的appId";
private String token = "刚才申请的免费Token";
private String channelName = "HelloAgoraAdvanced";
private RtcEngine mRtcEngine;

private void setupRemoteVideo(int uid) {
    FrameLayout container = findViewById(R.id.remote_video_view_container);
    // 生成一个SurfaceView,并添加到布局文件中预览FrameLayout的子节点
    SurfaceView surfaceView = new SurfaceView (getBaseContext());
    surfaceView.setZOrderMediaOverlay(true);
    container.addView(surfaceView);
    // 显示对方的视图
    mRtcEngine.setupRemoteVideo(new VideoCanvas(surfaceView, VideoCanvas.RENDER_MODE_FIT, uid));
}

private final IRtcEngineEventHandler mRtcEventHandler = new IRtcEngineEventHandler() {
    @Override
    // 监听频道内的其他用户,获取他的uid
    public void onUserJoined(int uid, int elapsed) {
        runOnUiThread(new Runnable() {
            @Override
            public void run() {
                setupRemoteVideo(uid);
            }
        });
    }
};

因为这是一个简单的demo,不需要做登录功能,启动时根据手机的OAID生成uid,这是因为Android Q以后的版本,Android禁止获取IMEI,所以作者本人用中国信通院联合华为、小米、OPPO、VIVO等厂商共同推出的设备识别字段OAID代替。这与本文内容无关,不多赘述。以下是初始化并加入频道的代码:

// 初始化并加入频道
// 这个方法在onCreate()和权限申请的回调中调用
// 实际开发建议在Application中异步初始化,然后打开聊天室Activity的时候加入频道
private void initializeAndJoinChannel() {
    try {
        // 配置参数
        RtcEngineConfig config = new RtcEngineConfig();
        config.mContext = getBaseContext();
        config.mAppId = appId;
        config.mEventHandler = mRtcEventHandler;
        // 初始化核心类RtcEngine
        mRtcEngine = RtcEngine.create(config);
    } catch (Exception e) {
        throw new RuntimeException("Check the error.");
    }
    // 启用视频流(视频默认禁用)
    mRtcEngine.enableVideo();
    // 开启本地视频预览。
    mRtcEngine.startPreview();

    FrameLayout container = findViewById(R.id.local_video_view_container);
    // 创建一个 SurfaceView 对象,作为Framework的子节点
    SurfaceView surfaceView = new SurfaceView (getBaseContext());
    container.addView(surfaceView);
    // 渲染本地视频。
    // 你需要自行指定用户 ID,并确保其在频道内的唯一性。
    mRtcEngine.setupLocalVideo(new VideoCanvas(surfaceView, VideoCanvas.RENDER_MODE_FIT, myAgoraUid));

    ChannelMediaOptions options = new ChannelMediaOptions();
    // 视频通话场景下,设置频道场景为 BROADCASTING。
    options.channelProfile = Constants.CHANNEL_PROFILE_LIVE_BROADCASTING;
    // 将用户角色设置为 BROADCASTER。
    options.clientRoleType = Constants.CLIENT_ROLE_BROADCASTER;

    // 使用临时 Token 加入频道。
    mRtcEngine.joinChannel(token, channelName, myAgoraUid, options);
}

最后Override onDestory()方法的时候记得停止预览、退出频道,这样可以避免内存泄漏:

// Agora官方建议先退出频道,后停止本地预览
// 本人亲测两个方法调用先后顺序不影响GC结果
@Override
protected void onDestroy() {
    super.onDestroy();
    // 退出频道
    mRtcEngine.leaveChannel();
    // 停止本地预览
    mRtcEngine.stopPreview();
}

接下来我们用两台手机测试一下效果,视频里主画面(我的正脸)是对方视角,副画面(我的侧脸)是本人视角

【截图】

【结束】

我们已经完成了Agora的基础功能的简单使用,接下来要进行Agora的声动互娱功能的学习。我们接下来学习的内容是如何使用背景音乐混动(AudioMixing)