前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Agora SDK 在Android中的使用(在线视频通话)

Agora SDK 在Android中的使用(在线视频通话)

作者头像
张风捷特烈
发布2020-04-30 15:34:00
1.5K0
发布2020-04-30 15:34:00
举报
文章被收录于专栏:Android知识点总结

首先声明本文是Agora SDK入门的小白文章


一.集成
1.注册账号创建项目

其中最重要的要数 App ID 了


2.下载Agora SDK

二、学会看示例代码(可跳过)
1.整体了解项目结构(1v1的视频通信示例)

以前看一个Android项目先看AndroidManifest.xml,我更喜欢先把文件夹内的结构树打印出来 打印文件夹内的结构树可详见:杂篇-从整理文件发起的杂谈[-File-]

代码语言:javascript
复制
|---app
    |---.gitignore
    |---build.gradle
    |---libs
        |---PLACEHOLDER
    |---proguard-rules.pro
    |---src
        |---main
            |---AndroidManifest.xml
            |---java
                |---io
                    |---agora
                        |---tutorials1v1vcall
                            |---VideoChatViewActivity.java
            |---jniLibs
                |---arm64-v8a
                    |---PLACEHOLDER
                |---armeabi-v7a
                    |---PLACEHOLDER
                |---x86
                    |---PLACEHOLDER
            |---res
                |---drawable-xxxhdpi
                    |---btn_end_call.png
                    |---btn_mute.png
                    |---btn_switch_camera.png
                    |---btn_video.png
                    |---btn_voice.png
                    |---ic_launcher.png
                |---layout
                    |---activity_video_chat_view.xml
                |---values
                    |---colors.xml
                    |---dimens.xml
                    |---strings.xml
                    |---styles.xml
|---build.gradle
|---gradle
    |---wrapper
        |---gradle-wrapper.jar
        |---gradle-wrapper.properties
|---gradle.properties
|---gradlew
|---gradlew.bat
|---images
    |---ActivityViewChat.png
|---LICENSE.md
|---README.md
|---README.zh.md
|---settings.gradle

2.查看最项目的settings.gradlebuild.gradle(最外层)

如果你想导入AS中查看,可以看一下com.android.tools.build:gradle的版本修改一下

代码语言:javascript
复制
---->[settings.gradle]----------------看一下项目包含的模块------------
include ':app'

---->[build.gradle]----------------看一下项目的一些信息------------
buildscript {
    repositories {
        jcenter()
        google()
    }
    dependencies {
        classpath 'com.android.tools.build:gradle:3.1.4'
    }
}
allprojects {
    repositories {
        jcenter()
        google()
    }
}
task clean(type: Delete) {
    delete rootProject.buildDir
}

3.查看模块下的build.gradle
代码语言:javascript
复制
---->[app/build.gradle]----------------看一下项目的具体信息------------
apply plugin: 'com.android.application'
android {
    compileSdkVersion 26
    defaultConfig {
        applicationId "io.agora.tutorials1v1vcall"
        minSdkVersion 16
        targetSdkVersion 26
        versionCode 1
        versionName "1.0"
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
    sourceSets {//这里是jniLibs的目录
        main {
            jniLibs.srcDirs = ['../../../libs']
        }
    }
}

dependencies {//这里是依赖
    implementation fileTree(dir: '../../../libs', include: ['*.jar']) // DO NOT CHANGE, CI may needs it when packaging
    implementation 'com.android.support:appcompat-v7:26.1.0'
}

4.查看AndroidManifest.xml,得到入口Activity

可见示例的入口是VideoChatViewActivity,并看一下权限

代码语言:javascript
复制
<activity
    android:name=".VideoChatViewActivity"
    android:screenOrientation="sensorPortrait"
    android:theme="@style/FullScreenVideoTheme">
    <intent-filter>
        <action android:name="android.intent.action.MAIN" />
        <category android:name="android.intent.category.LAUNCHER" />
    </intent-filter>
</activity>

|--- 权限 ------------
<!--网络权限-->
<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_NETWORK_STATE"/>
<!--相机权限-->
<uses-permission android:name="android.permission.CAMERA"/>
<!--蓝牙权限-->
<uses-permission android:name="android.permission.BLUETOOTH"/>
<!--SD卡写权限-->
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>

三、创建IChat项目

1、配置项目

项目的配置如图,将依赖包以及.so文件放在对应位置

为了方便些,将res文件夹的资源拷贝一下


2、配置APP ID

3.视频通话Activity的分析

一共也就200多行,还包括一大坨权限申请的代码,这里权限申请的代码单独拎出来,就当复习一下。

3.1:权限申请(非要点,可忽略)
代码语言:javascript
复制
---->[成员变量]----------------------------------
private static final int PERMISSION_REQ_ID = 22;
//WRITE_EXTERNAL_STORAGE 权限只是为了保存日志到SD卡
private static final String[] REQUESTED_PERMISSIONS = {
        Manifest.permission.RECORD_AUDIO,//录音权限
        Manifest.permission.CAMERA,//相机权限
        Manifest.permission.WRITE_EXTERNAL_STORAGE//SD卡写权限
};

if (checkSelfPermission(REQUESTED_PERMISSIONS[0], PERMISSION_REQ_ID) &&
        checkSelfPermission(REQUESTED_PERMISSIONS[1], PERMISSION_REQ_ID) &&
        checkSelfPermission(REQUESTED_PERMISSIONS[2], PERMISSION_REQ_ID)) {
    //执行到此处说明已有权限成功     
    initAgoraEngineAndJoinChannel();
}

/**
 * 检查权限的方法
 *
 * @param permission  权限
 * @param requestCode 请求码
 * @return 是否拥有权限
 */
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, REQUESTED_PERMISSIONS, requestCode);
        return false;
    }
    return true;
}

@Override
public void onRequestPermissionsResult(
        int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
    Log.i(LOG_TAG, "onRequestPermissionsResult " + grantResults[0] + " " + requestCode);
    switch (requestCode) {
        case PERMISSION_REQ_ID: {//请求码
            if (grantResults[0] != PackageManager.PERMISSION_GRANTED ||
                    grantResults[1] != PackageManager.PERMISSION_GRANTED ||
                    grantResults[2] != PackageManager.PERMISSION_GRANTED) {
                //三个权限有任意的未被允许,弹吐司,退出
                showLongToast("Need permissions " +
                        Manifest.permission.RECORD_AUDIO + "/" +
                        Manifest.permission.CAMERA + "/" +
                        Manifest.permission.WRITE_EXTERNAL_STORAGE);
                finish();
                break;
            }
            //执行到此处说明用户已允许权限
            initAgoraEngineAndJoinChannel();
            break;
        }
    }
}

4.初始化Agora引擎和连接频道
代码语言:javascript
复制
/**
 * 初始化Agora引擎和连接频道
 */
private void initAgoraEngineAndJoinChannel() {
    initializeAgoraEngine();//初始化Agora引擎
    setupVideoProfile();//设置视频信息
    setupLocalVideo();//设置本地的视频窗
    joinChannel();//连接频道
}

/**
 * 初始化Agora引擎
 */
private void initializeAgoraEngine() {
    try {
        mRtcEngine = RtcEngine.create(//实例化Rtc引擎
            getBaseContext(),//传入Context 
            getString(R.string.agora_app_id), //传入APP ID
            mRtcEventHandler);//RTC事件处理器
    } catch (Exception e) {//发生异常时捕获异常
        Log.e(LOG_TAG, Log.getStackTraceString(e));
        throw new RuntimeException("NEED TO check rtc sdk init fatal error\n" + Log.getStackTraceString(e));
    }
}


/**
 * 设置视频信息
 */
private void setupVideoProfile() {
    mRtcEngine.enableVideo();//启用视屏
    mRtcEngine.setVideoEncoderConfiguration(//视频解码配置
            new VideoEncoderConfiguration(//实例化对象
                    VideoEncoderConfiguration.VD_120x120,//尺寸
                    VideoEncoderConfiguration.FRAME_RATE.FRAME_RATE_FPS_15,//帧率
                    VideoEncoderConfiguration.STANDARD_BITRATE,//比特率
                    VideoEncoderConfiguration.ORIENTATION_MODE.ORIENTATION_MODE_FIXED_PORTRAIT));//旋转模式
}
|---关于VideoEncoderConfiguration对象---->[VideoEncoderConfiguration构造方法]------------------
 public VideoEncoderConfiguration(
 VideoEncoderConfiguration.VideoDimensions dimensions, //尺寸
 VideoEncoderConfiguration.FRAME_RATE frameRate,//帧率 
 int bitrate, //比特率
 VideoEncoderConfiguration.ORIENTATION_MODE orientationMode)//旋转模式

/**
 * 设置本地视频窗
 */
private void setupLocalVideo() {
    FrameLayout container = findViewById(R.id.local_video_view_container);//FrameLayout视图
    SurfaceView surfaceView = RtcEngine.CreateRendererView(getBaseContext());//创建SurfaceView
    surfaceView.setZOrderMediaOverlay(true);
    container.addView(surfaceView);
    mRtcEngine.setupLocalVideo(new VideoCanvas(surfaceView, VideoCanvas.RENDER_MODE_FIT, 0));
}

 /**
  * 连接到频道
  */
 private void joinChannel() {
     mRtcEngine.joinChannel(null, "demoChannel1", "Extra Optional Data", 0);
     // 如果你不指定 uid(第四参), 我们会为你生成一个 uid
 }
复制代码

5.RTC事件处理器:IRtcEngineEventHandler

IRtcEngineEventHandler是一个抽象类,定义了非常多的抽象方法还有一些静态内部类

代码语言:javascript
复制
private final IRtcEngineEventHandler mRtcEventHandler = new IRtcEngineEventHandler() {
    @Override//已完成远端视频首帧解码回调。
    public void onFirstRemoteVideoDecoded(final int uid, int width, int height, int elapsed) {
        runOnUiThread(() -> setupRemoteVideo(uid));
    }
    @Override//远端用户(通信模式)/主播(直播模式)离开当前频道回调。
    public void onUserOffline(int uid, int reason) {
        runOnUiThread(() -> onRemoteUserLeft());
    }
    @Override//远端用户静音回调。
    public void onUserMuteVideo(final int uid, final boolean muted) {
        runOnUiThread(() -> onRemoteUserVideoMuted(uid, muted));
    }
};


/**
 * 根据uid设置远端视频
 * @param uid 唯一标识符
 */
private void setupRemoteVideo(int uid) {
    FrameLayout container = findViewById(R.id.remote_video_view_container);
    if (container.getChildCount() >= 1) {
        return;
    }
    SurfaceView surfaceView = RtcEngine.CreateRendererView(getBaseContext());
    container.addView(surfaceView);
    mRtcEngine.setupRemoteVideo(new VideoCanvas(surfaceView, VideoCanvas.RENDER_MODE_FIT, u
    surfaceView.setTag(uid); // 用uid为surfaceView打标签
    View tipMsg = findViewById(R.id.quick_tips_when_use_agora_sdk); // 隐藏文字UI
    tipMsg.setVisibility(View.GONE);
}

/**
 * 远端用户挂断
 */
private void onRemoteUserLeft() {
    FrameLayout container = findViewById(R.id.remote_video_view_container);
    container.removeAllViews();
    View tipMsg = findViewById(R.id.quick_tips_when_use_agora_sdk); // 显示文字UI
    tipMsg.setVisibility(View.VISIBLE);
}

/**
 * 远端用户静音
 * @param uid 标识符
 * @param muted 是否静音
 */
private void onRemoteUserVideoMuted(int uid, boolean muted) {
    FrameLayout container = findViewById(R.id.remote_video_view_container);
    SurfaceView surfaceView = (SurfaceView) container.getChildAt(0);
    Object tag = surfaceView.getTag();
    if (tag != null && (Integer) tag == uid) {
        surfaceView.setVisibility(muted ? View.GONE : View.VISIBLE);
    }
}
复制代码

6.几个点击事件
代码语言:javascript
复制
/**
 * 是否屏蔽视频
 * @param view
 */
public void onLocalVideoMuteClicked(View view) {
    ImageView iv = (ImageView) view;
    if (iv.isSelected()) {
        iv.setSelected(false);
        iv.clearColorFilter();
    } else {
        iv.setSelected(true);
        iv.setColorFilter(getResources().getColor(R.color.colorPrimary), PorterDuff.Mode.MULTIPLY);
    }
    mRtcEngine.muteLocalVideoStream(iv.isSelected());//核心的一句API,
    FrameLayout container = findViewById(R.id.local_video_view_container);
    SurfaceView surfaceView = (SurfaceView) container.getChildAt(0);
    surfaceView.setZOrderMediaOverlay(!iv.isSelected());
    surfaceView.setVisibility(iv.isSelected() ? View.GONE : View.VISIBLE);
}

/**
 * 是否静音
 * @param view
 */
public void onLocalAudioMuteClicked(View view) {
    ImageView iv = (ImageView) view;
    if (iv.isSelected()) {
        iv.setSelected(false);
        iv.clearColorFilter();
    } else {
        iv.setSelected(true);
        iv.setColorFilter(getResources().getColor(R.color.colorPrimary), PorterDuff.Mode.MULTIPLY);
    }
    mRtcEngine.muteLocalAudioStream(iv.isSelected());//核心的一句API,
}

/**
 * 切换摄像头
 * @param view
 */
public void onSwitchCameraClicked(View view) {
    mRtcEngine.switchCamera();
}

/**
 * 关闭
 * @param view
 */
public void onEncCallClicked(View view) {
    finish();
}


 @Override
 protected void onDestroy() {
     super.onDestroy();
     leaveChannel();//离开频道
     RtcEngine.destroy();//引擎销毁
     mRtcEngine = null;//引擎置空
 }
 
 /**
 * 离开频道
 */
private void leaveChannel() {
    mRtcEngine.leaveChannel();
}
复制代码

[番外]:我与网络(个人感触,不想看,可略过,嘻嘻...)
1.我与网络的初遇

记得第一次接触网络是在高二的时候(2011年),那时候手机还是键盘式的,貌似每月有100M流量 印象很深,当时用手机上网查了"GPRS流量"是什么意思? 看得我一头雾水, 一直没弄明白。 那时好像超过1M收一元钱,所以流量对我来说很宝贵。通过不断测试(用网+短信查询):

代码语言:javascript
复制
1.它和网络有关
2.只要上网,GPRS流量就会减少
3.文字消耗的流量很少,图片消耗的流量较多
4.下载多大的文件就会消耗多大的GPRS流量
5.进制1M=1024KB ; 1KB = 1024B

2.时代的变迁

那时主要的沟通方式就是短信,一个月500条免费的短信都不够发 当时觉得用手机可以打电话,发短信是一件多么神奇和美好的事 也曾幻想过用手机视频通话,但感觉就像在痴人说梦,然而技术的发展往往令人惊叹。

代码语言:javascript
复制
高考之后(2012年)的暑假,在一个好友家里建了一个QQ号,起名"张风杰特烈"(后更名"张风捷特烈")
从此之后我的手机基本和打电话,发短信无缘了。可以说是打开了新世界的大门吧。
之后手机换了一部又一部,屏幕越来越大,流量也从100M变成了200M,之后 500M, 2G, 流量无限量
有了自己的笔记本电脑,自己捯饬连上了路由器,自此WIFI成了相依为命的伙伴,流量也不是我  
一年前还是个连流量都不知道是什么的人,却能在短时间内融入这个网络时代,也许就是年轻人的优势吧

很快,QQ就支持视频通话了,那遥不可及的梦如梦般降临,而我就这么幸运的站在梦中 由于我的专业需要使用很多软件,所以电脑玩的还算比较6的。但看到很早就接触电脑的人用起来却非常生涩 我曾对一个人说过:我抓住了时代的尾巴,正一点点先前攀爬,而身处时代中的人已停止不前


3.我与游戏

说到网络就不得不说游戏,这也是网络的双刃之处,很多人沉迷其中无法自拔 可以说作为一个94年的小伙子,我接触网络算非常晚的,因此什么魔兽世界,星际争霸都与我无缘

代码语言:javascript
复制
英雄联盟,王者荣耀什么的,看到小人跑我头都晕,一点兴趣都没有
大二吧,比较喜欢玩QQ飞车,玩的挺6的。后来被几个班里的"后起之秀"虐了,也逐渐没什么兴趣了  
从小我就喜欢《游戏王》,所以卡牌对我来说是很有诱惑力的,特别是集卡和策略   
所以玩过不少策略卡牌类的手游,[圣火英雄传],[召唤师对决],[六界],[神之刃]等  
我一般都是V3,一段时间后,基本都是V9以下无敌手,然后成区霸,最后游戏倒闭, 关门大吉。 
经历几次后,感觉也看淡了,都是些数据而已,也没必要去较真,也就不玩游戏了

4.真正踏入网络时代

作为一名使用者,我可以贪婪的享受着网络中的一切便利,但我越来越感到,只是这样是不够的 网络对我来说仅是一个黑盒,它为我提供服务,我却对它一无所知,这让我感到困惑和恐惧 并不仅为此,我决定踏上编程之路,想要更深入一点去看待这个时代,而不止于使用者

代码语言:javascript
复制
当我的网站连同之时,整个互联网中有了一个属于我节点。也许是我真正踏入网络时代大门的那一刻
从那时,世界网络中[二进制流]的输入与输出便成了我对网络时代的认知,眼中的一切似乎都有所不同  
打开一个网址,浏览器和服务器通过Http协议用请求与响应传输数据,数据在流动中加工,反馈,展现
通过前端、后端、移动端的涉猎,基本明白了是怎么回事。这并非神迹,而是大概有三个要点:
接口(协议)、逻辑(代码)、数据(bit流)
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2019年04月26日,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一.集成
    • 1.注册账号创建项目
      • 2.下载Agora SDK
      • 二、学会看示例代码(可跳过)
        • 1.整体了解项目结构(1v1的视频通信示例)
          • 2.查看最项目的settings.gradle和build.gradle(最外层)
            • 3.查看模块下的build.gradle
              • 4.查看AndroidManifest.xml,得到入口Activity
              • 三、创建IChat项目
                • 1、配置项目
                  • 2、配置APP ID
                    • 3.视频通话Activity的分析
                      • 3.1:权限申请(非要点,可忽略)
                    • 4.初始化Agora引擎和连接频道
                      • 5.RTC事件处理器:IRtcEngineEventHandler
                        • 6.几个点击事件
                        • [番外]:我与网络(个人感触,不想看,可略过,嘻嘻...)
                          • 1.我与网络的初遇
                            • 2.时代的变迁
                              • 3.我与游戏
                                • 4.真正踏入网络时代
                                相关产品与服务
                                短信
                                腾讯云短信(Short Message Service,SMS)可为广大企业级用户提供稳定可靠,安全合规的短信触达服务。用户可快速接入,调用 API / SDK 或者通过控制台即可发送,支持发送验证码、通知类短信和营销短信。国内验证短信秒级触达,99%到达率;国际/港澳台短信覆盖全球200+国家/地区,全球多服务站点,稳定可靠。
                                领券
                                问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档