首先聲明本文是Agora SDK入門的小白文章前端
其中最重要的要數 App ID 了java
之前看一個Android項目先看
AndroidManifest.xml
,我更喜歡先把文件夾內的結構樹打印出來
打印文件夾內的結構樹可詳見:雜篇-從整理文件發起的雜談[-File-]android
|---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
複製代碼
settings.gradle
和build.gradle
(最外層)若是你想導入AS中查看,能夠看一下
com.android.tools.build:gradle
的版本修改一下git
---->[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
}
複製代碼
模塊下的build.gradle
---->[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'
}
複製代碼
AndroidManifest.xml
,獲得入口Activity可見示例的入口是
VideoChatViewActivity
,並看一下權限編程
<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"/>
複製代碼
項目的配置如圖,將依賴包以及.so文件放在對應位置後端
爲了方便些,將res文件夾的資源拷貝一下瀏覽器
一共也就200多行,還包括一大坨權限申請的代碼,這裏權限申請的代碼單獨拎出來,就當複習一下。bash
---->[成員變量]----------------------------------
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;
}
}
}
複製代碼
/**
* 初始化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
}
複製代碼
IRtcEngineEventHandler
IRtcEngineEventHandler是一個抽象類,定義了很是多的抽象方法還有一些靜態內部類服務器
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);
}
}
複製代碼
/**
* 是否屏蔽視頻
* @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();
}
複製代碼
我的感觸,不想看,可略過,嘻嘻...
)記得第一次接觸網絡是在高二的時候(2011年),那時候手機仍是鍵盤式的,貌似每個月有100M流量
印象很深,當時用手機上網查了"GPRS流量"是什麼意思? 看得我一頭霧水, 一直沒弄明白。
那時好像超過1M收一元錢,因此流量對我來講很寶貴。經過不斷測試(用網+短信查詢):網絡
1.它和網絡有關
2.只要上網,GPRS流量就會減小
3.文字消耗的流量不多,圖片消耗的流量較多
4.下載多大的文件就會消耗多大的GPRS流量
5.進制1M=1024KB ; 1KB = 1024B
複製代碼
那時主要的溝通方式就是短信,一個月500條免費的短信都不夠發
當時以爲用手機能夠打電話,發短信是一件多麼神奇和美好的事
也曾幻想過用手機視頻通話,但感受就像在癡人說夢,然而技術的發展每每使人驚歎。
高考以後(2012年)的暑假,在一個好友家裏建了一個QQ號,起名"張風杰特烈"(後改名"張風捷特烈")
今後以後個人手機基本和打電話,發短信無緣了。能夠說是打開了新世界的大門吧。
以後手機換了一部又一部,屏幕愈來愈大,流量也從100M變成了200M,以後 500M, 2G, 流量無限量
有了本身的筆記本電腦,本身捯飭連上了路由器,自此WIFI成了相依爲命的夥伴,流量也不是我
一年前仍是個連流量都不知道是什麼的人,卻能在短期內融入這個網絡時代,也許就是年輕人的優點吧
複製代碼
很快,QQ就支持視頻通話了,那高不可攀的夢如夢般降臨,而我就這麼幸運的站在夢中
因爲個人專業須要使用不少軟件,因此電腦玩的還算比較6的。但看到很早就接觸電腦的人用起來卻很是生澀
我曾對一我的說過:我抓住了時代的尾巴,正一點點先前攀爬,而身處時代中的人已中止不前
說到網絡就不得不說遊戲,這也是網絡的雙刃之處,不少人沉迷其中沒法自拔
能夠說做爲一個94年的小夥子,我接觸網絡算很是晚的,所以什麼魔獸世界,星際爭霸都與我無緣
英雄聯盟,王者榮耀什麼的,看到小人跑我頭都暈,一點興趣都沒有
大二吧,比較喜歡玩QQ飛車,玩的挺6的。後來被幾個班裏的"後起之秀"虐了,也逐漸沒什麼興趣了
從小我就喜歡《遊戲王》,因此卡牌對我來講是頗有誘惑力的,特別是集卡和策略
因此玩過很多策略卡牌類的手遊,[聖火英雄傳],[召喚師對決],[六界],[神之刃]等
我通常都是V3,一段時間後,基本都是V9如下無敵手,而後成區霸,最後遊戲倒閉, 關門大吉。
經歷幾回後,感受也看淡了,都是些數據而已,也不必去較真,也就不玩遊戲了
複製代碼
做爲一名使用者,我能夠貪婪的享受着網絡中的一切便利,但我愈來愈感到,只是這樣是不夠的
網絡對我來講僅是一個黑盒,它爲我提供服務,我卻對它一無所知,這讓我感到困惑和恐懼
並不只爲此
,我決定踏上編程之路,想要更深刻一點去看待這個時代,而不止於使用者
當個人網站連同之時,整個互聯網中有了一個屬於我節點。也許是我真正踏入網絡時代大門的那一刻
從那時,世界網絡中[二進制流]的輸入與輸出便成了我對網絡時代的認知,眼中的一切彷佛都有所不一樣
打開一個網址,瀏覽器和服務器經過Http協議用請求與響應傳輸數據,數據在流動中加工,反饋,展示
經過前端、後端、移動端的涉獵,基本明白了是怎麼回事。這並不是神蹟,而是大概有三個要點:
接口(協議)、邏輯(代碼)、數據(bit流)
複製代碼