共计 12147 个字符,预计需要花费 31 分钟才能阅读完成。
add-live-streaming-to-your-android-app-using-agora-featured1024×512 121 KB
视频互动直播是以后比拟热门的玩法,咱们常常见到有 PK 连麦、直播答题、一起 KTV、电商直播、互动大班课、视频相亲等。
本文将演示如何通过声网 Agora 视频 SDK 在 Android 端实现一个视频直播利用。注册声网账号后,开发者每个月可取得 10000 分钟的收费应用额度,可实现各类实时音视频场景。
话不多说,咱们开始入手实操。
一些前提条件
- Agora 开发者帐户(声网开发者注册指南)
- Android Studio
- Android 开发基础知识
一、通过开源 Demo,体验视频直播
可能有些人,还不理解咱们要实现的性能最初是怎么的。所以咱们在 GitHub 上提供一个开源的根底视频直播示例我的项目,在开始开发之前你能够通过该示例我的项目体验视频直播的体验成果。
Github:GitHub – Meherdeep/agora-android-live-streaming 1
588×1228 79.9 KB
在这里,我增加了两个直播流,同时能够让多个观众订阅它。
二、视频直播的技术原理
咱们在这里要实现的是视频直播,Agora 的视频直播能够实现互动成果,所以也常常叫互动直播。你能够了解为是多个用户通过退出同一个频道,实现的音视频的互通,而这个频道的数据,会通过声网的 Agora SD-RTN 实时网络来进行低延时传输的。
须要特地阐明的是,Agora 互动直播不同于视频通话。视频通话不辨别主播和观众,所有用户都能够发言并看见彼此;而互动直播的用户分为主播和观众,只有主播能够自在发言,且被其余用户看见。
下图展现在 App 中集成 Agora 互动直播的根本工作流程:
实现互动直播的步骤如下:
1. 设置角色:互动直播频道中,用户角色能够是主播或者观众。主播在频道内公布音视频流,观众仅可订阅音视频流。
2. 获取 Token:当 App 客户端退出频道时,你须要通过 Token 验证用户身份。App 客户端向 App 服务器发送申请,并获取 Token,而后在客户端退出频道时验证用户身份。
3. 退出频道:调用 joinChannel 创立并退出频道。应用同一频道名称的 App 客户端默认退出同一频道。
4. 在频道内公布和订阅音视频:退出频道后,角色为主播的 App 客户端能够公布音视频。对于角色为观众的客户端,如果想要公布音视频,能够调用 setClientRole 切换用户角色。
App 客户端退出频道须要以下信息:
- 频道名称:用于标识直播频道的字符串。
- App ID:Agora 随机生成的字符串,用于辨认你的 App,可从 Agora 控制台获取,(Agora 控制台链接:Dashboard
- 用户 ID:用户的惟一标识。你须要自行设置用户 ID,并确保它在频道内是惟一的。
- Token:在测试或生产环境中,你的 App 客户端会从你的服务器中获取 Token。为不便疾速测试,你也能够获取长期 Token。长期 Token 的有效期为 24 小时。
三、开发环境
声网 Agora SDK 的兼容性良好,对硬件设施和软件系统的要求不高,开发环境和测试环境满足以下条件即可:
• Android SDK API Level >= 16
• Android Studio 2.0 或以上版本
• 反对语音和视频性能的真机
• App 要求 Android 4.1 或以上设施
以下是本文的开发环境和测试环境:
开发环境
• Windows 10 家庭中文版
• Java Version SE 8
• Android Studio 3.2 Canary 4
测试环境
• Samsung Nexus (Android 4.4.2 API 19)
• Mi Note 3 (Android 7.1.1 API 25)
如果你此前还未接触过声网 Agora SDK,那么你还须要做以下筹备工作:
• 注册一个声网账号,进入后盾创立 AppID、获取 Token,具体办法可参考这篇教程;(这篇教程:404 – 知乎
• 下载声网官网最新的互动直播 SDK;(互动直播 SDK 链接:下载 – 全副产品 – 文档核心 – 声网 Agora
四、我的项目设置
1. 实现互动直播之前,参考如下步骤设置你的我的项目:
如需创立新我的项目,在 Android Studio 里,顺次抉择 Phone and Tablet > Empty Activity,创立 Android 我的项目。(创立 Android 我的项目链接:https://developer.android.com…)
创立我的项目后,Android Studio 会主动开始同步 gradle。请确保同步胜利再进行下一步操作。
2. 集成 SDK, 本文举荐应用 gradle 形式集成 Agora SDK:
a. 在 /Gradle Scripts/build.gradle(Project:) 文件中增加如下代码,以增加 jcenter 依赖:
buildscript {
repositories {
...
jcenter()}
...
}
allprojects {
repositories {
...
jcenter()}
}
b. 在 /Gradle Scripts/build.gradle(Module: .App) 文件中增加如下代码,将 Agora 视频 SDK 集成到你的 Android 我的项目中:
...
dependencies {
...
// x.y.z,请填写具体的 SDK 版本号,如:3.5.0。// 通过发版阐明获取最新版本号。implementation 'io.agora.rtc:full-sdk:x.y.z'
// 本例应用布局相干设置 constraintlayout
implementation 'androidx.constraintlayout:constraintlayout:2.0.4'
}
3. 权限设置
在 /App/Manifests/AndroidManifest.xml 文件中的 “ 前面增加如下网络和设施权限:
<uses-permission android:name="android.permission.INTERNET" />
<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.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.BLUETOOTH" />
4. 导入 Agora 相干的类
在 /app/src/main/java/com/agora/samtan/agorabroadcast/VideoActivity 文件中,退出如下代码:
package com.agora.samtan.agorabroadcast;
import io.agora.rtc.Constants;
import io.agora.rtc.IRtcEngineEventHandler;
import io.agora.rtc.RtcEngine;
import io.agora.rtc.video.VideoCanvas;
import io.agora.rtc.video.VideoEncoderConfiguration;
5. 设置 Agora 账号信息
在 /app/src/main/res/values/strings.xml 文件中,将你的 AppID 填写到 private_App_id 中:
<resources>
……
<string name="private_App_id"> 填写地位 </string>
……
</resources>
五、客户端实现
本节介绍如何应用 Agora 视频 SDK 在你的 App 里实现视频直播的几个小贴士:
1. 查看并获取必要权限
启动应用程序时,查看是否已在 App 中授予了实现视频直播所需的权限。在 onCreate 函数中调用如下代码:
@Override
protected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
int MY_PERMISSIONS_REQUEST_CAMERA = 0;
if (ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED || ContextCompat.checkSelfPermission(this, Manifest.permission.RECORD_AUDIO) != PackageManager.PERMISSION_GRANTED) {ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.CAMERA, Manifest.permission.RECORD_AUDIO}, MY_PERMISSIONS_REQUEST_CAMERA);
}
}
2. 实现互动直播逻辑
关上你的 App,创立 RtcEngine 实例,启用视频后退出频道。如果本地用户是主播,则将本地视频公布到用户界面下方的视图中。如果另一主播退出该频道,你的 App 会捕捉到这一退出事件,并将远端视频增加到用户界面右上角的视图中。
互动直播的 API 应用时序见下图:
image822×1048 106 KB
依照以下步骤实现该逻辑:
a) 初始化 RtcEngine
RtcEngine 类蕴含应用程序调用的次要办法,调用 RtcEngine 的接口最好在同一个线程进行,不倡议在不同的线程同时调用。
目前 Agora Native SDK 只反对一个 RtcEngine 实例,每个应用程序仅创立一个 RtcEngine 对象。RtcEngine 类的所有接口函数,如无非凡阐明,都是异步调用,对接口的调用倡议在同一个线程进行。所有返回值为 int 型的 API,如无非凡阐明,返回值 0 为调用胜利,返回值小于 0 为调用失败。
在 VideoActivity 文件中,通过 initializeAgoraEngine 用于初始化 RtcEngine 的办法:
private void initalizeAgoraEngine() {
try {mRtcEngine = RtcEngine.create(getBaseContext(), getString(R.string.private_App_id), mRtcEventHandler);
} catch (Exception e) {e.printStackTrace();
}
}
另外,有个重要的 IRtcEngineEventHandler 接口类用于 SDK 向应用程序发送回调事件告诉,应用程序通过继承该接口类的办法获取 SDK 的事件告诉。
接口类的所有办法都有缺省(空)实现,应用程序能够依据须要只继承关怀的事件。在回调办法中,应用程序不应该做耗时或者调用可能会引起阻塞的 API(如 SendMessage),否则可能影响 SDK 的运行。内容如下:
private final IRtcEngineEventHandler iRtcEngineEventHandler = new IRtcEngineEventHandler()
{
/**Reports a warning during SDK runtime.
* Warning code: https://docs.agora.io/en/Voice/API%20Reference/java/classio_1_1agora_1_1rtc_1_1_i_rtc_engine_event_handler_1_1_warn_code.html*/
@Override
public void onWarning(int warn)
{Log.w(TAG, String.format("onWarning code %d message %s", warn, RtcEngine.getErrorDescription(warn)));
}
/**Reports an error during SDK runtime.
* Error code: https://docs.agora.io/en/Voice/API%20Reference/java/classio_1_1agora_1_1rtc_1_1_i_rtc_engine_event_handler_1_1_error_code.html*/
@Override
public void onError(int err)
{Log.e(TAG, String.format("onError code %d message %s", err, RtcEngine.getErrorDescription(err)));
showAlert(String.format("onError code %d message %s", err, RtcEngine.getErrorDescription(err)));
}
/**Occurs when a user leaves the channel.
* @param stats With this callback, the Application retrieves the channel information,
* such as the call duration and statistics.*/
@Override
public void onLeaveChannel(RtcStats stats)
{super.onLeaveChannel(stats);
Log.i(TAG, String.format("local user %d leaveChannel!", myUid));
showLongToast(String.format("local user %d leaveChannel!", myUid));
}
/**Occurs when the local user joins a specified channel.
* The channel name assignment is based on channelName specified in the joinChannel method.
* If the uid is not specified when joinChannel is called, the server automatically assigns a uid.
* @param channel Channel name
* @param uid User ID
* @param elapsed Time elapsed (ms) from the user calling joinChannel until this callback is triggered*/
@Override
public void onJoinChannelSuccess(String channel, int uid, int elapsed)
{Log.i(TAG, String.format("onJoinChannelSuccess channel %s uid %d", channel, uid));
showLongToast(String.format("onJoinChannelSuccess channel %s uid %d", channel, uid));
myUid = uid;
joined = true;
handler.post(new Runnable()
{
@Override
public void run()
{join.setEnabled(true);
join.setText(getString(R.string.leave));
}
});
}
@Override
public void onRemoteAudioStats(io.agora.rtc.IRtcEngineEventHandler.RemoteAudioStats remoteAudioStats) {statisticsInfo.setRemoteAudioStats(remoteAudioStats);
updateRemoteStats();}
@Override
public void onLocalAudioStats(io.agora.rtc.IRtcEngineEventHandler.LocalAudioStats localAudioStats) {statisticsInfo.setLocalAudioStats(localAudioStats);
updateLocalStats();}
@Override
public void onRemoteVideoStats(io.agora.rtc.IRtcEngineEventHandler.RemoteVideoStats remoteVideoStats) {statisticsInfo.setRemoteVideoStats(remoteVideoStats);
updateRemoteStats();}
@Override
public void onLocalVideoStats(io.agora.rtc.IRtcEngineEventHandler.LocalVideoStats localVideoStats) {statisticsInfo.setLocalVideoStats(localVideoStats);
updateLocalStats();}
@Override
public void onRtcStats(io.agora.rtc.IRtcEngineEventHandler.RtcStats rtcStats) {statisticsInfo.setRtcStats(rtcStats);
}
};
所以,在咱们的 initialize 函数中,咱们将 mRtcEventHandler 作为参数之一传递给了 create 办法,这设置了一系列回调事件,每当用户退出频道或来到频道时就会触发这些事件。
private IRtcEngineEventHandler mRtcEventHandler = new IRtcEngineEventHandler() {
@Override
public void onUserJoined(final int uid, int elapsed) {super.onUserJoined(uid, elapsed);
runOnUiThread(new Runnable() {
@Override
public void run() {setupRemoteVideo(uid);
}
});
}
@Override
public void onUserOffline(int uid, int reason) {runOnUiThread(new Runnable() {
@Override
public void run() {onRemoteUserLeft();
}
});
}
};
b) 设置频道场景和角色
setChannelProfile() 是一个应用咱们 AgoraRtcEngine 对象援用的办法。Agora 提供了各种配置文件,能够通过该办法调用并集成到利用中。
setClientRole()办法,将用户的角色设置为主播或观众(默认)。这个办法应该在退出频道之前调用。退出频道后能够再次调用,切换客户端角色。
为了不便体验互动直播中主播角色和观众角色的成果,咱们将在咱们的 MainActivity 类中增加两个办法:
• 当用户从单选按钮中抉择一个选项时,将调用第一个办法。咱们将相应地设置一个变量。咱们将其设置为一个值,该值将确定用户是主播还是观众。
public void onRadioButtonClicked(View view) {boolean checked = ((RadioButton) view).isChecked();
switch (view.getId()) {
case R.id.host:
if (checked) {channelProfile = Constants.CLIENT_ROLE_BROADCASTER;}
break;
case R.id.audience:
if (checked) {channelProfile = Constants.CLIENT_ROLE_AUDIENCE;}
break;
}
}
• 而后咱们实现一个在用户提交详细信息时调用的函数。在这里,咱们将取得咱们须要的所有详细信息,并将它们发送到下一个 activity。
public void onSubmit(View view) {EditText channel = (EditText) findViewById(R.id.channel);
String channelName = channel.getText().toString();
Intent intent = new Intent(this, VideoActivity.class);
intent.putExtra(channelMessage, channelName);
intent.putExtra(profileMessage, channelProfile);
startActivity(intent);
}
c) 开始视频
setupVideoProfile() 函数用于定义视频须要渲染的形式。你能够对帧速率、比特率、方向、镜像模式和降级偏好等属性应用本人的自定义配置。
private void setupVideoProfile() {mRtcEngine.enableVideo();
mRtcEngine.setVideoEncoderConfiguration(new VideoEncoderConfiguration(VideoEncoderConfiguration.VD_640x480, VideoEncoderConfiguration.FRAME_RATE.FRAME_RATE_FPS_15,
VideoEncoderConfiguration.STANDARD_BITRATE,
VideoEncoderConfiguration.ORIENTATION_MODE.ORIENTATION_MODE_FIXED_PORTRAIT));
}
d) 设置本地视频
setupLocalVideo() 函数用于从咱们的 AgoraRtcEngine 中援用 setupLocalVideo 办法,咱们通过它为咱们的本地用户设置一个在直播流中应用的外表视图:
private void setupLocalVideo() {FrameLayout container = (FrameLayout) findViewById(R.id.local_video_view_container);
SurfaceView surfaceView = RtcEngine.CreateRendererView(getBaseContext());
surfaceView.setZOrderMediaOverlay(true);
container.addView(surfaceView);
mRtcEngine.setupLocalVideo(new VideoCanvas(surfaceView, VideoCanvas.RENDER_MODE_FIT, 0));
}
e) 退出频道
频道是人们在同一个视频通话中的公共空间。joinChannel()办法能够这样调用:
private void joinChannel() {mRtcEngine.joinChannel(token, channelName, "Optional Data", 0);
}
该办法须要四个参数能力胜利运行:
• Token:倡议对在生产环境中运行的所有 RTE APP 进行 Token 身份验证。更多对于声网 Agora 平台基于令牌的认证信息,请参见 https://docs.agora.io/cn/Vide…。
• 频道名称:须要一个字符串,让用户进入视频通话。
• 可选信息:这是一个可选字段,你能够通过它传递无关频道的其余信息。
• uid:每个退出频道的用户的惟一 ID。如果传入 0 或 null 值,Agora 会主动为每个用户调配一个 uid。
留神:此我的项目仅供参考和开发环境应用,不适用于生产环境。倡议对在生产环境中运行的所有 RTE APP 进行 Token 身份验证。
本例中初始化 App,调用外围办法来创立并退出 Agora 直播频道。在 VideoActivity 文件中,在 onCreate 函数后增加如下代码:
package com.agora.samtan.agorabroadcast;
import android.content.Intent;
import android.graphics.PorterDuff;
import android.os.Bundle;
import android.util.Log;
import android.view.SurfaceView;
import android.view.View; ;//;.;
import android.widget.FrameLayout;
import android.widget.ImageView;
import androidx.Appcompat.App.AppCompatActivity;
import io.agora.rtc.Constants;
import io.agora.rtc.IRtcEngineEventHandler;
import io.agora.rtc.RtcEngine;
import io.agora.rtc.video.VideoCanvas;
import io.agora.rtc.video.VideoEncoderConfiguration;
public class VideoActivity extends AppCompatActivity {
private RtcEngine mRtcEngine;
private String channelName;
private int channelProfile;
@Override
protected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);
setContentView(R.layout.activity_video);
Intent intent = getIntent();
channelName = intent.getStringExtra(MainActivity.channelMessage);
channelProfile = intent.getIntExtra(MainActivity.profileMessage, -1);
if (channelProfile == -1) {Log.e("TAG:", "No profile");
}
initAgoraEngineAndJoinChannel();}
}
咱们发表了一个名为 initAgoraEngineAndJoinChannel 的办法,它将调用直播过程中所需的所有其余办法。咱们还定义了事件处理程序,它将决定当近程用户退出或来到或静音时调用哪些办法。
private void initAgoraEngineAndJoinChannel() {initalizeAgoraEngine();
mRtcEngine.setChannelProfile(Constants.CHANNEL_PROFILE_LIVE_BROADCASTING);
mRtcEngine.setClientRole(channelProfile);
setupVideoProfile();
setupLocalVideo();
joinChannel();}
f) 当远端主播退出频道时增加远端界面
在 VideoActivity 文件中,initializeAndJoinChannel 函数后退出如下代码:
private void setupRemoteVideo(int uid) {FrameLayout container = (FrameLayout) findViewById(R.id.remote_video_view_container);
SurfaceView surfaceView = RtcEngine.CreateRendererView(getBaseContext());
container.addView(surfaceView);
mRtcEngine.setupRemoteVideo(new VideoCanvas(surfaceView, VideoCanvas.RENDER_MODE_FIT, uid));
}
g) 开释资源
最初,咱们增加 onDestroy 办法来开释咱们应用过的资源。相干代码如下:
@Override
protected void onDestroy() {super.onDestroy();
leaveChannel();
RtcEngine.destroy();
mRtcEngine = null;
}
至此,实现,运行看看成果。拿两部手机装置编译好的 App,退出同一个频道名,别离抉择主播角色和观众角色,如果 2 个手机都能看见同一个本人,阐明你胜利了。
如果你在开发过程中遇到问题,能够拜访论坛发问与声网工程师交换(链接:https://rtcdeveloper.agora.io/)1
也能够拜访后盾获取更进一步的技术