写点什么

使用 Agora 为 Android APP 添加视频直播

作者:声网
  • 2022 年 5 月 13 日
  • 本文字数:8697 字

    阅读完需:约 29 分钟


add-live-streaming-to-your-android-app-using-agora-featured1024×512 121 KB


视频互动直播是当前比较热门的玩法,我们经常见到有 PK 连麦、直播答题、一起 KTV、电商直播、互动大班课、视频相亲等。


本文将演示如何通过声网 Agora 视频 SDK 在 Android 端实现一个视频直播应用。注册声网账号后,开发者每个月可获得 10000 分钟的免费使用额度,可实现各类实时音视频场景。


话不多说,我们开始动手实操。

一些前提条件

一、 通过开源 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](https://docs.agora.io/cn/All/downloads?platform=All Platforms#SDK Downloads))

四、 项目设置

1. 实现互动直播之前,参考如下步骤设置你的项目:

如需创建新项目,在 Android Studio 里,依次选择 Phone and Tablet > Empty Activity,创建 Android 项目。(创建 Android 项目链接:https://developer.android.com/studio/projects/create-project)创建项目后,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'//本例使用布局相关设置constraintlayoutimplementation  '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) 初始化 RtcEngineRtcEngine 类包含应用程序调用的主要方法,调用 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/Video/token?platform=All%20Platforms。• 频道名称:需要一个字符串,让用户进入视频通话。• 可选信息:这是一个可选字段,你可以通过它传递有关频道的其他信息。• 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也可以访问后台获取更进一步的技术

用户头像

声网

关注

还未添加个人签名 2021.02.05 加入

声网(NASDAQ:API)成立于2014年。开发者可通过声网API,在应用内构建多种实时音视频互动场景。使用声网服务的包括小米、陌陌、斗鱼、哔哩哔哩、新东方、小红书、HTC VIVE 、Yalla等遍布全球的巨头、独角兽企业。

评论

发布
暂无评论
使用 Agora 为Android APP添加视频直播_android_声网_InfoQ写作社区