우선 메니페스트에 권한들 먼저 넣어줄게요.
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
그리고 라이브러리 추가를 해주겠습니다.
implementation 'gun0912.ted:tedpermission:2.2.3'
implementation files('libs/agora-rtc-sdk.jar')
implementation files('libs/commons-codec-1.15.jar')
implementation 'io.agora.rtm:rtm-sdk:1.4.9'
권한 처리를 위한 테드퍼미션과 아고라 관련된 라이브러리 및 나중에 라이브토큰 발급을위한 라이브러리입니다.
해당 jar파일을 libs 폴더에 넣어주시면 됩니다.
그 다음에 Application 클래스를 하나 생성해줄게요.
public class AgoraApplication extends Application {
private RtcEngine mRtcEngine;
private EngineConfig mGlobalConfig = new EngineConfig();
private AgoraEventHandler mHandler = new AgoraEventHandler();
private StatsManager mStatsManager = new StatsManager();
private static AgoraApplication sInstance;
private ChatManager mChatManager;
public static AgoraApplication the() {
return sInstance;
}
@Override
public void onCreate() {
super.onCreate();
try {
mRtcEngine = RtcEngine.create(getApplicationContext(), "앱 아이디", mHandler);
} catch (Exception e) {
e.printStackTrace();
}
initConfig();
sInstance = this;
mChatManager = new ChatManager(this);
mChatManager.init();
}
public ChatManager getChatManager() {
return mChatManager;
}
private void initConfig() {
SharedPreferences pref = PrefManager.getPreferences(getApplicationContext());
mGlobalConfig.setVideoDimenIndex(pref.getInt(
Constants.PREF_RESOLUTION_IDX, Constants.DEFAULT_PROFILE_IDX));
boolean showStats = pref.getBoolean(Constants.PREF_ENABLE_STATS, false);
mGlobalConfig.setIfShowVideoStats(showStats);
mStatsManager.enableStats(showStats);
mGlobalConfig.setMirrorLocalIndex(pref.getInt(Constants.PREF_MIRROR_LOCAL, 0));
mGlobalConfig.setMirrorRemoteIndex(pref.getInt(Constants.PREF_MIRROR_REMOTE, 0));
mGlobalConfig.setMirrorEncodeIndex(pref.getInt(Constants.PREF_MIRROR_ENCODE, 0));
}
public EngineConfig engineConfig() {
return mGlobalConfig;
}
public RtcEngine rtcEngine() {
return mRtcEngine;
}
public StatsManager statsManager() {
return mStatsManager;
}
public void registerEventHandler(EventHandler handler) {
mHandler.addHandler(handler);
}
public void removeEventHandler(EventHandler handler) {
mHandler.removeHandler(handler);
}
@Override
public void onTerminate() {
super.onTerminate();
RtcEngine.destroy();
}
}
Application 클래스를 사용 하실 때는 메니페스트에
android:name=".AgoraApplication"
한줄 추가해주세요.
그다음에는 본격 라이브 소스를 구현하기에 앞서 기본 클래스들을 셋팅해주겠습니다.
public class Constants {
public static final String PREF_NAME = "io.agora.openlive";
public static final int DEFAULT_PROFILE_IDX = 2;
public static final String PREF_RESOLUTION_IDX = "pref_profile_index";
public static final String PREF_ENABLE_STATS = "pref_enable_stats";
public static final String PREF_MIRROR_LOCAL = "pref_mirror_local";
public static final String PREF_MIRROR_REMOTE = "pref_mirror_remote";
public static final String PREF_MIRROR_ENCODE = "pref_mirror_encode";
public static final String KEY_CLIENT_ROLE = "key_client_role";
public static int[] VIDEO_MIRROR_MODES = new int[]{
io.agora.rtc.Constants.VIDEO_MIRROR_MODE_AUTO,
io.agora.rtc.Constants.VIDEO_MIRROR_MODE_ENABLED,
io.agora.rtc.Constants.VIDEO_MIRROR_MODE_DISABLED,
};
public static VideoEncoderConfiguration.VideoDimensions[] VIDEO_DIMENSIONS = new VideoEncoderConfiguration.VideoDimensions[]{
VideoEncoderConfiguration.VD_320x240,
VideoEncoderConfiguration.VD_480x360,
VideoEncoderConfiguration.VD_640x360,
VideoEncoderConfiguration.VD_640x480,
new VideoEncoderConfiguration.VideoDimensions(960, 540),
VideoEncoderConfiguration.VD_1280x720
};
}
----------------------------------------------------------------------------------------------------------------------
public class LocalStatsData extends StatsData {
private static final String FORMAT = "Local(%d)\n\n" +
"%dx%d %dfps\n" +
"LastMile delay: %d ms\n" +
"Video tx/rx (kbps): %d/%d\n" +
"Audio tx/rx (kbps): %d/%d\n" +
"CPU: app/total %.1f%%/%.1f%%\n" +
"Quality tx/rx: %s/%s\n" +
"Loss tx/rx: %d%%/%d%%";
private int lastMileDelay;
private int videoSend;
private int videoRecv;
private int audioSend;
private int audioRecv;
private double cpuApp;
private double cpuTotal;
private int sendLoss;
private int recvLoss;
@Override
public String toString() {
return String.format(Locale.getDefault(), FORMAT,
getUid(),
getWidth(), getHeight(), getFramerate(),
getLastMileDelay(),
getVideoSendBitrate(), getVideoRecvBitrate(),
getAudioSendBitrate(), getAudioRecvBitrate(),
getCpuApp(), getCpuTotal(),
getSendQuality(), getRecvQuality(),
getSendLoss(), getRecvLoss());
}
public int getLastMileDelay() {
return lastMileDelay;
}
public void setLastMileDelay(int lastMileDelay) {
this.lastMileDelay = lastMileDelay;
}
public int getVideoSendBitrate() {
return videoSend;
}
public void setVideoSendBitrate(int videoSend) {
this.videoSend = videoSend;
}
public int getVideoRecvBitrate() {
return videoRecv;
}
public void setVideoRecvBitrate(int videoRecv) {
this.videoRecv = videoRecv;
}
public int getAudioSendBitrate() {
return audioSend;
}
public void setAudioSendBitrate(int audioSend) {
this.audioSend = audioSend;
}
public int getAudioRecvBitrate() {
return audioRecv;
}
public void setAudioRecvBitrate(int audioRecv) {
this.audioRecv = audioRecv;
}
public double getCpuApp() {
return cpuApp;
}
public void setCpuApp(double cpuApp) {
this.cpuApp = cpuApp;
}
public double getCpuTotal() {
return cpuTotal;
}
public void setCpuTotal(double cpuTotal) {
this.cpuTotal = cpuTotal;
}
public int getSendLoss() {
return sendLoss;
}
public void setSendLoss(int sendLoss) {
this.sendLoss = sendLoss;
}
public int getRecvLoss() {
return recvLoss;
}
public void setRecvLoss(int recvLoss) {
this.recvLoss = recvLoss;
}
}
----------------------------------------------------------------------------------------------------------------------
public class RemoteStatsData extends StatsData {
private static final String FORMAT = "Remote(%d)\n\n" +
"%dx%d %dfps\n" +
"Quality tx/rx: %s/%s\n" +
"Video delay: %d ms\n" +
"Audio net delay/jitter: %dms/%dms\n" +
"Audio loss/quality: %d%%/%s";
private int videoDelay;
private int audioNetDelay;
private int audioNetJitter;
private int audioLoss;
private String audioQuality;
@Override
public String toString() {
return String.format(Locale.getDefault(), FORMAT,
getUid(),
getWidth(), getHeight(), getFramerate(),
getSendQuality(), getRecvQuality(),
getVideoDelay(),
getAudioNetDelay(), getAudioNetJitter(),
getAudioLoss(), getAudioQuality());
}
public static String getFORMAT() {
return FORMAT;
}
public int getVideoDelay() {
return videoDelay;
}
public void setVideoDelay(int videoDelay) {
this.videoDelay = videoDelay;
}
public int getAudioNetDelay() {
return audioNetDelay;
}
public void setAudioNetDelay(int audioNetDelay) {
this.audioNetDelay = audioNetDelay;
}
public int getAudioNetJitter() {
return audioNetJitter;
}
public void setAudioNetJitter(int audioNetJitter) {
this.audioNetJitter = audioNetJitter;
}
public int getAudioLoss() {
return audioLoss;
}
public void setAudioLoss(int audioLoss) {
this.audioLoss = audioLoss;
}
public String getAudioQuality() {
return audioQuality;
}
public void setAudioQuality(String audioQuality) {
this.audioQuality = audioQuality;
}
}
----------------------------------------------------------------------------------------------------------------------
public class StatsData {
private long uid;
private int width;
private int height;
private int framerate;
private String recvQuality;
private String sendQuality;
public long getUid() {
return uid;
}
public void setUid(long uid) {
this.uid = uid;
}
public int getWidth() {
return width;
}
public void setWidth(int width) {
this.width = width;
}
public int getHeight() {
return height;
}
public void setHeight(int height) {
this.height = height;
}
public int getFramerate() {
return framerate;
}
public void setFramerate(int framerate) {
this.framerate = framerate;
}
public String getRecvQuality() {
return recvQuality;
}
public void setRecvQuality(String recvQuality) {
this.recvQuality = recvQuality;
}
public String getSendQuality() {
return sendQuality;
}
public void setSendQuality(String sendQuality) {
this.sendQuality = sendQuality;
}
}
----------------------------------------------------------------------------------------------------------------------
public class StatsManager {
private List<Integer> mUidList = new ArrayList<>();
private Map<Integer, StatsData> mDataMap = new HashMap<>();
private boolean mEnable = false;
public void addUserStats(int uid, boolean ifLocal) {
if (mUidList.contains(uid) && mDataMap.containsKey(uid)) {
return;
}
StatsData data = ifLocal
? new LocalStatsData()
: new RemoteStatsData();
// in case 32-bit unsigned integer uid is received
data.setUid(uid & 0xFFFFFFFFL);
if (ifLocal) mUidList.add(0, uid);
else mUidList.add(uid);
mDataMap.put(uid, data);
}
public void removeUserStats(int uid) {
if (mUidList.contains(uid) && mDataMap.containsKey(uid)) {
mUidList.remove((Integer) uid);
mDataMap.remove(uid);
}
}
public StatsData getStatsData(int uid) {
if (mUidList.contains(uid) && mDataMap.containsKey(uid)) {
return mDataMap.get(uid);
} else {
return null;
}
}
public String qualityToString(int quality) {
switch (quality) {
case io.agora.rtc.Constants.QUALITY_EXCELLENT:
return "Exc";
case io.agora.rtc.Constants.QUALITY_GOOD:
return "Good";
case io.agora.rtc.Constants.QUALITY_POOR:
return "Poor";
case io.agora.rtc.Constants.QUALITY_BAD:
return "Bad";
case io.agora.rtc.Constants.QUALITY_VBAD:
return "VBad";
case Constants.QUALITY_DOWN:
return "Down";
default:
return "Unk";
}
}
public void enableStats(boolean enabled) {
mEnable = enabled;
}
public boolean isEnabled() {
return mEnable;
}
public void clearAllData() {
mUidList.clear();
mDataMap.clear();
}
}
----------------------------------------------------------------------------------------------------------------------
public class WindowUtil {
public static void hideWindowStatusBar(Window window) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
window.getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
| View.SYSTEM_UI_FLAG_LAYOUT_STABLE);
window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
window.setStatusBarColor(Color.TRANSPARENT);
}
else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
window.addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
}
}
public static int getSystemStatusBarHeight(Context context) {
int id = context.getResources().getIdentifier(
"status_bar_height", "dimen", "android");
return id > 0 ? context.getResources().getDimensionPixelSize(id) : id;
}
}
이렇게 기본 클래스들 셋팅을해주세요 (아고라 문서에서 공식제공해주는 소스입니다.)
다음은 영상처리에 있는 이벤트를 처리해줄 핸들러를 작성해줄게요.
public class AgoraEventHandler extends IRtcEngineEventHandler {
private ArrayList<EventHandler> mHandler = new ArrayList<>();
public void addHandler(EventHandler handler) {
mHandler.add(handler);
}
public void removeHandler(EventHandler handler) {
mHandler.remove(handler);
}
@Override
public void onJoinChannelSuccess(String channel, int uid, int elapsed) {
for (EventHandler handler : mHandler) {
handler.onJoinChannelSuccess(channel, uid, elapsed);
}
}
@Override
public void onLeaveChannel(RtcStats stats) {
for (EventHandler handler : mHandler) {
handler.onLeaveChannel(stats);
}
}
@Override
public void onFirstRemoteVideoDecoded(int uid, int width, int height, int elapsed) {
for (EventHandler handler : mHandler) {
handler.onFirstRemoteVideoDecoded(uid, width, height, elapsed);
}
}
@Override
public void onUserJoined(int uid, int elapsed) {
for (EventHandler handler : mHandler) {
handler.onUserJoined(uid, elapsed);
}
}
@Override
public void onUserOffline(int uid, int reason) {
for (EventHandler handler : mHandler) {
handler.onUserOffline(uid, reason);
}
}
@Override
public void onLocalVideoStats(IRtcEngineEventHandler.LocalVideoStats stats) {
for (EventHandler handler : mHandler) {
handler.onLocalVideoStats(stats);
}
}
@Override
public void onRtcStats(IRtcEngineEventHandler.RtcStats stats) {
for (EventHandler handler : mHandler) {
handler.onRtcStats(stats);
}
}
@Override
public void onNetworkQuality(int uid, int txQuality, int rxQuality) {
for (EventHandler handler : mHandler) {
handler.onNetworkQuality(uid, txQuality, rxQuality);
}
}
@Override
public void onRemoteVideoStats(IRtcEngineEventHandler.RemoteVideoStats stats) {
for (EventHandler handler : mHandler) {
handler.onRemoteVideoStats(stats);
}
}
@Override
public void onRemoteAudioStats(IRtcEngineEventHandler.RemoteAudioStats stats) {
for (EventHandler handler : mHandler) {
handler.onRemoteAudioStats(stats);
}
}
@Override
public void onLastmileQuality(int quality) {
for (EventHandler handler : mHandler) {
handler.onLastmileQuality(quality);
}
}
@Override
public void onLastmileProbeResult(IRtcEngineEventHandler.LastmileProbeResult result) {
for (EventHandler handler : mHandler) {
handler.onLastmileProbeResult(result);
}
}
}
----------------------------------------------------------------------------------------------------------------------
public class EngineConfig {
// private static final int DEFAULT_UID = 0;
// private int mUid = DEFAULT_UID;
private String nickName;
private String mChannelName;
private boolean mShowVideoStats;
private int mDimenIndex = Constants.DEFAULT_PROFILE_IDX;
private int mMirrorLocalIndex;
private int mMirrorRemoteIndex;
private int mMirrorEncodeIndex;
public int getVideoDimenIndex() {
return mDimenIndex;
}
public void setVideoDimenIndex(int index) {
mDimenIndex = index;
}
public String getChannelName() {
return mChannelName;
}
public void setChannelName(String mChannel) {
this.mChannelName = mChannel;
}
public void setNicklName(String nickName) {
this.nickName = nickName;
}
public String getNicklName() {
return nickName;
}
public boolean ifShowVideoStats() {
return mShowVideoStats;
}
public void setIfShowVideoStats(boolean show) {
mShowVideoStats = show;
}
public int getMirrorLocalIndex() {
return mMirrorLocalIndex;
}
public void setMirrorLocalIndex(int index) {
mMirrorLocalIndex = index;
}
public int getMirrorRemoteIndex() {
return mMirrorRemoteIndex;
}
public void setMirrorRemoteIndex(int index) {
mMirrorRemoteIndex = index;
}
public int getMirrorEncodeIndex() {
return mMirrorEncodeIndex;
}
public void setMirrorEncodeIndex(int index) {
mMirrorEncodeIndex = index;
}
}
----------------------------------------------------------------------------------------------------------------------
public interface EventHandler {
void onFirstRemoteVideoDecoded(int uid, int width, int height, int elapsed);
void onLeaveChannel(IRtcEngineEventHandler.RtcStats stats);
void onJoinChannelSuccess(String channel, int uid, int elapsed);
void onUserOffline(int uid, int reason);
void onUserJoined(int uid, int elapsed);
void onLastmileQuality(int quality);
void onLastmileProbeResult(IRtcEngineEventHandler.LastmileProbeResult result);
void onLocalVideoStats(IRtcEngineEventHandler.LocalVideoStats stats);
void onRtcStats(IRtcEngineEventHandler.RtcStats stats);
void onNetworkQuality(int uid, int txQuality, int rxQuality);
void onRemoteVideoStats(IRtcEngineEventHandler.RemoteVideoStats stats);
void onRemoteAudioStats(IRtcEngineEventHandler.RemoteAudioStats stats);
}
----------------------------------------------------------------------------------------------------------------------
라이브를 구현하기 앞서 기본 소스셋팅을 끝냈습니다.
다음 게시글에서는 본격적으로 라이브 작업을 해보도록 하겠습니다.
ㄱ ㅏ ㄱ 푸시
ㅗ o ㅗ 푸시 :D
'Android' 카테고리의 다른 글
아고라 플랫폼을 이용한 안드로이드 라이브스트리밍(RTSP) - 4 (0) | 2022.05.12 |
---|---|
아고라 플랫폼을 이용한 안드로이드 라이브스트리밍(RTSP) - 3 (0) | 2022.05.12 |
아고라 플랫폼을 이용한 안드로이드 라이브스트리밍(RTSP) - 1 (0) | 2022.05.12 |
파이어베이스 휴대폰인증 로봇체크(reCaptcha) 생략하기! (0) | 2022.02.15 |
<코틀린 Kotlin> 안드로이드 파이어베이스 휴대폰인증 (0) | 2022.02.15 |
댓글