本文介绍如何使用声网音频互动直播 SDK 快速实现音频直播。
音频直播和语音通话的区别就在于,直播频道的用户有角色之分。你可以将角色设置为主播或者观众,其中主播可以收、发流,观众只能收流。
开始前,请确保你的开发环境满足以下条件:
本节介绍如何创建项目,将声网音频 SDK 集成进你的项目中,并添加相应的设备权限。
参考以下步骤创建一个 Android 项目。若已有 Android 项目,可以直接查看集成 SDK。
然后点击 Finish。根据屏幕提示,安装可能需要的插件。
选择如下任意一种方式将声网音频 SDK 集成到你的项目中。
方法一:使用 Maven Central 自动集成
/Gradle Scripts/build.gradle(Project: <projectname>)
文件中添加 Maven Central 支持:buildscript {
repositories {
...
mavenCentral()
}
...
}
allprojects {
repositories {
...
mavenCentral()
}
}
/Gradle Scripts/build.gradle(Module: <projectname>.app)
中添加如下依赖:...
dependencies {
...
// x.y.z 请填写具体版本号,如:3.5.0 或 3.7.0.2
// 可通过 SDK 发版说明取得最新版本号
implementation 'io.agora.rtc:voice-sdk:x.y.z'
}
方法二:手动复制 SDK 文件
文件或文件夹 | 项目路径 |
---|---|
agora-rtc-sdk.jar 文件 | /app/libs/ |
arm64-v8a 文件夹 | /app/src/main/jniLibs/ |
armeabi-v7a 文件夹 | /app/src/main/jniLibs/ |
include 文件夹 | /app/src/main/jniLibs/ |
x86 文件夹 | /app/src/main/jniLibs/ |
x86_64 文件夹 | /app/src/main/jniLibs/ |
根据场景需要,在 /app/src/main/AndroidManifest.xml 文件中添加如下行,获取相应的设备权限:
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="io.agora.tutorials1v1acall">
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.BLUETOOTH" />
<!-- 对于 Android 12.0 及以上设备,还需要添加如下权限: -->
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
...
</manifest>
在 /Gradle Scripts/proguard-rules.pro
文件中添加如下行,以防止声网 SDK 的代码被混淆:
-keep class io.agora.**{*;}
本节介绍如何实现音频直播。音频直播的 API 调用时序见下图:
根据场景需要,为你的项目创建音频直播的用户界面。若已有界面,可以直接查看导入类。
你可以参考 OpenLive-Voice-Only-Android 示例项目的 layout 文件中的代码。
<?xml version="1.0" encoding="UTF-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:keepScreenOn="true"
tools:context=".ui.LiveRoomActivity">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="@+id/room_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:layout_centerHorizontal="true"
android:layout_marginTop="6dp"
android:textColor="@color/dark_black"
android:textSize="16sp"
android:textStyle="bold" />
<io.agora.propeller.ui.AGLinearLayout
android:id="@+id/bottom_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:orientation="vertical">
<ImageView
android:id="@+id/bottom_action_end_call"
android:layout_width="54dp"
android:layout_height="54dp"
android:layout_gravity="center_horizontal"
android:onClick="onEndCallClicked"
android:scaleType="center"
android:src="@drawable/btn_endcall" />
<RelativeLayout
android:id="@+id/bottom_action_container"
android:layout_width="match_parent"
android:layout_height="54dp"
android:gravity="center_vertical"
android:orientation="horizontal">
<ImageView
android:id="@id/switch_broadcasting_id"
android:layout_width="54dp"
android:layout_height="match_parent"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:scaleType="center"
android:src="@drawable/btn_request_broadcast" />
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_centerInParent="true"
android:orientation="horizontal">
<ImageView
android:id="@id/switch_speaker_id"
android:layout_width="54dp"
android:layout_height="match_parent"
android:onClick="onSwitchSpeakerClicked"
android:scaleType="center"
android:src="@drawable/btn_speaker" />
<ImageView
android:id="@id/mute_local_speaker_id"
android:layout_width="54dp"
android:layout_height="match_parent"
android:onClick="onVoiceMuteClicked"
android:scaleType="center"
android:src="@drawable/btn_mute" />
</LinearLayout>
</RelativeLayout>
</io.agora.propeller.ui.AGLinearLayout>
<EditText
android:id="@+id/msg_list"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_above="@id/bottom_container"
android:layout_below="@id/room_name"
android:layout_marginBottom="8dp"
android:layout_marginTop="6dp"
android:enabled="true"
android:focusable="false"
android:gravity="start|top"
android:inputType="none"
android:scrollbars="vertical" />
</RelativeLayout>
</FrameLayout>
在项目的 Activity 文件中添加如下行:
import io.agora.rtc.IRtcEngineEventHandler;
import io.agora.rtc.RtcEngine;
调用 checkSelfPermission
方法,在开启 Activity 时检查并获取 Android 移动设备的麦克风使用权限。
private static final int PERMISSION_REQ_ID_RECORD_AUDIO = 22;
// App 运行时确认麦克风的使用权限。
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_voice_chat_view);
// 获取权限后,初始化 RtcEngine,并加入频道。
if (checkSelfPermission(Manifest.permission.RECORD_AUDIO, PERMISSION_REQ_ID_RECORD_AUDIO)) {
initAgoraEngineAndJoinChannel();
}
}
private void initAgoraEngineAndJoinChannel() {
initializeAgoraEngine();
joinChannel();
}
public boolean checkSelfPermission(String permission, int requestCode) {
Log.i(LOG_TAG, "checkSelfPermission " + permission + " " + requestCode);
if (ContextCompat.checkSelfPermission(this,
permission)
!= PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(this,
new String[]{permission},
requestCode);
return false;
}
return true;
}
在调用其他声网 API 前,需要创建并初始化 RtcEngine 对象。
将获取到的 App ID 添加到 string.xml
文件中的 agora_app_id
一栏。调用 create
方法,传入获取到的 App ID,即可初始化 RtcEngine。
你还根据场景需要,在初始化时注册想要监听的回调事件,如本地用户加入频道,及解码远端用户视频首帧等。注意不要在这些回调中进行 UI 操作。
private RtcEngine mRtcEngine;
private final IRtcEngineEventHandler mRtcEventHandler = new IRtcEngineEventHandler() {
@Override
// 注册 onJoinChannelSuccess 回调。
// 本地用户成功加入频道时,会触发该回调。
public void onJoinChannelSuccess(String channel, final int uid, int elapsed) {
runOnUiThread(new Runnable() {
@Override
public void run() {
Log.i("agora","Join channel success, uid: " + (uid & 0xFFFFFFFFL));
}
});
}
@Override
// 注册 onUserOffline 回调。
// 远端主播离开频道或掉线时,会触发该回调。
public void onUserOffline(final int uid, int reason) {
runOnUiThread(new Runnable() {
@Override
public void run() {
Log.i("agora","User offline, uid: " + (uid & 0xFFFFFFFFL));
onRemoteUserLeft();
}
});
}
};
...
// 初始化 RtcEngine 对象。
private void initializeEngine() {
try {
mRtcEngine = RtcEngine.create(getBaseContext(), getString(R.string.agora_app_id), mRtcEventHandler);
} catch (Exception e) {
Log.e(TAG, Log.getStackTraceString(e));
throw new RuntimeException("NEED TO check rtc sdk init fatal error\n" + Log.getStackTraceString(e));
}
}
初始化结束后,调用 setChannelProfile
方法,将频道场景设为直播。
一个 RtcEngine 只能使用一种频道场景。如果想切换为其他场景,需要先调用 destroy
方法释放当前的 RtcEngine 实例,然后使用 create 方法创建一个新实例,再调用 setChannelProfile
设置新的频道场景。
private void setChannelProfile() {
mRtcEngine.setChannelProfile(Constants.CHANNEL_PROFILE_LIVE_BROADCASTING);
}
直播频道有两种用户角色:主播和观众,其中默认的角色为观众。设置频道场景为直播后,你可以在 App 中参考如下步骤设置用户角色:
setClientRole
方法,然后使用用户选择的角色进行传参。注意,直播频道内的观众,只能听到主播的声音,不能交谈。加入频道后,如果你想切换用户角色,也可以调用 setClientRole
方法。
public void onClickJoin(View view) {
// 使用弹框让用户自行选择用户角色。
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setMessage(R.string.msg_choose_role);
builder.setNegativeButton(R.string.label_audience, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
MainActivity.this.forwardToLiveRoom(Constants.CLIENT_ROLE_AUDIENCE);
}
});
builder.setPositiveButton(R.string.label_broadcaster, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
MainActivity.this.forwardToLiveRoom(Constants.CLIENT_ROLE_BROADCASTER);
}
});
AlertDialog dialog = builder.create();
dialog.show();
}
// 获取用户设置的角色和频道名。
// 其中,频道名需要在加入频道时使用。
public void forwardToLiveRoom(int cRole) {
final EditText v_room = (EditText) findViewById(R.id.room_name);
String room = v_room.getText().toString();
Intent i = new Intent(MainActivity.this, LiveRoomActivity.class);
i.putExtra("CRole", cRole);
i.putExtra("CName", room);
startActivity(i);
}
// 传入用户设置的角色。
private int mRole;
mRole = getIntent().getIntExtra("CRole", 0);
private void setClientRole() {
mRtcEngine.setClientRole(mRole);
}
完成设置角色后,你就可以调用 joinChannel
方法加入频道。你需要在该方法中传入如下参数:
token
:传入用于鉴权的 Token,可设为如下一个值:
token
设为 ""。onJoinChannelSuccess
回调中报告。mute
方法实现。更多的参数设置注意事项请参考 joinChannel
接口中的参数描述。
private void joinChannel() {
// 使用 Token 加入频道。
private String mRoomName;
mRoomName = getIntent().getStringExtra("CName");
mRtcEngine.joinChannel(YOUR_TOKEN, mRoomName, "Extra Optional Data", 0);
}
根据场景需要,如结束直播、关闭 App 或 App 切换至后台时,调用 leaveChannel
离开当前直播频道。
@Override
protected void onDestroy() {
super.onDestroy();
if (!mCallEnd) {
leaveChannel();
}
RtcEngine.destroy();
}
private void leaveChannel() {
// 离开当前频道。
mRtcEngine.leaveChannel();
}
你可以在 OpenLive-Voice-Only-Android 示例项目中查看完整的源码和代码逻辑。
在 Android 设备中运行该项目。当成功开始音频直播时,观众可以听到主播的声音。
使用声网音频互动直播 SDK 开发过程中,你还可以参考如下文档: