티스토리 뷰
0. getWindow()
getWindow().addFlags(LayoutParams.FLAG_DISMISS_KEYGUARD | LayoutParams.FLAG_SHOW_WHEN_LOCKED
| LayoutParams.FLAG_TURN_SCREEN_ON);
- LayoutParams.FLAG_DISMISS_KEYGUARD // phone이 lock 상태면 보여주지 않음
- LayoutParams.FLAG_SHOW_WHEN_LOCKED // 잠금화면 위에 app 보여주기
- LayoutParams.FLAG_TURN_SCREEN_ON //꺼진 화면을 on
getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
| View.SYSTEM_UI_FLAG_FULLSCREEN | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY);
- View.SYSTEM_UI_FLAG_HIDE_NAVIGATION //네비바 숨김
- View.SYSTEM_UI_FLAG_FULLSCREEN // 풀스크린 모드
- View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY //
1. import android.databinding.DataBindingUtil;
- gradle app module에 dataBinding { enabled true} 추가
- xml 위아래에 <layout> 추가
android {
compileSdkVersion 30
buildToolsVersion "30.0.2"
dataBinding {
enabled = true
}....
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools">
- binding 세팅 후 build -> Rebuild Project를 꼭 해주어야 함 , 컴파일러가 자동으로 바인딩 클래스를 생성해주기 때문이다. 자동으로 생성된 Binding 클래스는 다음과 같은 규칙을 따른다.
1. Binding 클래스는 파일 이름을 파스칼 형식으로 바꾸고 그 뒤에 Binding을 접미사로 붙임
2. 컴포넌트 id는 카멜 표기법으로 변환
ex) activity_main (xml) -> ActivityMainBinding
ex) tv_sample (id) -> tvSample
2. 안드로이드 권한 정리
- 안드로이드 권한에는 "위험한 권한" & "일반권한" 이 존재
- "위험 권한"에는 반드시 권한을 요청해야하는데 이는 Runtime Permission이라는 권한 요청으로 구현 가능
- EasyPermission 라이브러리 구현
import pub.devrel.easypermissions.AfterPermissionGranted;
import pub.devrel.easypermissions.EasyPermissions;
* @AfterPermissionGranted()
- build.gradle dependencies에
implementation 'pub.devrel:easypermissions:1.1.3'
허가 추가
* Manifest.xml -> Manifest에 요청할 Permission 선언
<uses-feature android:name="android.hardware.camera" />
<uses-feature android:name="android.hardware.camera.autofocus" />
<uses-feature
android:glEsVersion="0x00020000"
android:required="true" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" />
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.BLUETOOTH" />
3. JAVA IO.socket
//gradle dependencies
implementation('io.socket:socket.io-client:1.0.0') {
exclude group: 'org.json', module: 'json'
}
import io.socket.client.IO;
import io.socket.client.Socket;
import static io.socket.client.Socket.EVENT_CONNECT;
import static io.socket.client.Socket.EVENT_DISCONNECT;
private Socket socker = IO.socket(url)
# 이벤트 전달 연결
1. Socket.on(String event, Listener listener)
- 첫번째 인자 -> 서버로부터 전달을 기다리는 이벤트명
- 두번째 인자 -> 해당이벤트로
2. Socket.emit(String event, Object args)
- 첫번재 인자 -> 서버로 전송할 이벤트 명
- 두번째 인자 -> 추가적으로 전송할 데이터 , 대부분의 서버-클라이언트 통신은 JSON을 이용한 통신이기 때문에 JSON Object로 데이터 생성 필요
* 중요한점 peer2peer로 연결될 때 똑같은 event 이름으로 설정 되어 있어야 한다.
socket= IO.socket("http://10.0.2.2:8080")
socket.on(EVENT_CONNECT, args->{
socket.emit("create or join", "foo");
}).on("ipaddr", args -> {
}).on("created", args -> {
}).on("full", args -> {
}).on("join", args -> {
}).on("log", args -> {
}) ....
4. PeerConnection
import org.webrtc.PeerConnection;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
|
JSONObject message = (JSONObject) args[0];
Log.d(TAG, "connectToSignallingServer: got message " + message);
if (message.getString("type").equals("offer")) {
Log.d(TAG, "connectToSignallingServer: received an offer " + isInitiator + " " + isStarted);
if (!isInitiator && !isStarted) {
maybeStart();
}
peerConnection.setRemoteDescription(new SimpleSdpObserver(), new SessionDescription(OFFER, message.getString("sdp")));
doAnswer();
} else if (message.getString("type").equals("answer") && isStarted) {
peerConnection.setRemoteDescription(new SimpleSdpObserver(), new SessionDescription(ANSWER, message.getString("sdp")));
} else if (message.getString("type").equals("candidate") && isStarted) {
Log.d(TAG, "connectToSignallingServer: receiving candidates");
IceCandidate candidate = new IceCandidate(message.getString("id"), message.getInt("label"), message.getString("candidate"));
peerConnection.addIceCandidate(candidate);
}
}
|
cs |
private PeerConnection peerConnection
(1) setRemoteDescription
- peerConnection.setRemoteDescription(new SimpleSdpObserver(), new SessionDescription(OFFER, message.getString("sdp")));
- peerConnection.setRemoteDescription(new SimpleSdpObserver(), new SessionDescription(ANSWER, message.getString("sdp")));
(2) setLocalDescription
- peerConnection.setLocalDescription(new SimpleSdpObserver(), sessionDescription);
(3) IceServer 구성하기
1
2
3
4
|
ArrayList<PeerConnection.IceServer> iceServers = new ArrayList<>();
iceServers.add(new PeerConnection.IceServer("stun:stun.l.google.com:19302"));
PeerConnection.RTCConfiguration rtcConfig = new PeerConnection.RTCConfiguration(iceServers);
|
cs |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
|
PeerConnection.Observer pcObserver = new PeerConnection.Observer() {
@Override
public void onSignalingChange(PeerConnection.SignalingState signalingState) {
Log.d(TAG, "onSignalingChange: ");
}
@Override
public void onIceConnectionChange(PeerConnection.IceConnectionState iceConnectionState) {
Log.d(TAG, "onIceConnectionChange: ");
}
@Override
public void onIceConnectionReceivingChange(boolean b) {
Log.d(TAG, "onIceConnectionReceivingChange: ");
}
@Override
public void onIceGatheringChange(PeerConnection.IceGatheringState iceGatheringState) {
Log.d(TAG, "onIceGatheringChange: ");
}
@Override
public void onIceCandidate(IceCandidate iceCandidate) {
Log.d(TAG, "onIceCandidate: ");
JSONObject message = new JSONObject();
try {
message.put("type", "candidate");
message.put("label", iceCandidate.sdpMLineIndex);
message.put("id", iceCandidate.sdpMid);
message.put("candidate", iceCandidate.sdp);
Log.d(TAG, "onIceCandidate: sending candidate " + message);
sendMessage(message);
} catch (JSONException e) {
e.printStackTrace();
}
}
@Override
public void onIceCandidatesRemoved(IceCandidate[] iceCandidates) {
Log.d(TAG, "onIceCandidatesRemoved: ");
}
@Override
public void onAddStream(MediaStream mediaStream) {
Log.d(TAG, "onAddStream: " + mediaStream.videoTracks.size());
VideoTrack remoteVideoTrack = mediaStream.videoTracks.get(0);
remoteVideoTrack.setEnabled(true);
remoteVideoTrack.addRenderer(new VideoRenderer(binding.surfaceView2));
}
@Override
public void onRemoveStream(MediaStream mediaStream) {
Log.d(TAG, "onRemoveStream: ");
}
@Override
public void onDataChannel(DataChannel dataChannel) {
Log.d(TAG, "onDataChannel: ");
}
@Override
public void onRenegotiationNeeded() {
Log.d(TAG, "onRenegotiationNeeded: ");
}
};
|
cs |
5. IceCandidate
import org.webrtc.IceCandidate;
IceCandidate candidate = new IceCandidate(message.getString("id"), message.getInt("label"), message.getString("candidate"));
6. EglBase
- Video renderers을 생성하는 것
import org.webrtc.EglBase;
private EglBase rootEglBase;
rootEglBase = EglBase.create();
#1 예시
binding.localVideoView.init(rootEglBase.getEglBaseContext(), null); // 기본
binding.remoteVideoView.init(rootEglBase.getEglBaseContext(), null); // 기본
binding.localVideoView.setZOrderMediaOverlay(true);
binding.localVideoView.setEnableHardwareScaler(true);
binding.remoteVideoView.setEnableHardwareScaler(true);
binding.remoteVideoLayout.setPosition(REMOTE_X, REMOTE_Y, REMOTE_WIDTH, REMOTE_HEIGHT);
binding.remoteVideoView.setScalingType(SCALE_ASPECT_FILL);
binding.remoteVideoView.setMirror(false);
if (iceConnected) {
binding.localVideoLayout.setPosition(
LOCAL_X_CONNECTED, LOCAL_Y_CONNECTED, LOCAL_WIDTH_CONNECTED, LOCAL_HEIGHT_CONNECTED);
binding.localVideoView.setScalingType(SCALE_ASPECT_FIT);
} else {
binding.localVideoLayout.setPosition(
LOCAL_X_CONNECTING, LOCAL_Y_CONNECTING, LOCAL_WIDTH_CONNECTING, LOCAL_HEIGHT_CONNECTING);
binding.localVideoView.setScalingType(SCALE_ASPECT_FILL);
}
binding.localVideoView.setMirror(true);
binding.localVideoView.requestLayout();
binding.remoteVideoView.requestLayout();
#2 예시
binding.surfaceView.init(rootEglBase.getEglBaseContext(), null);
binding.surfaceView.setEnableHardwareScaler(true);
binding.surfaceView.setMirror(true);
binding.surfaceView2.init(rootEglBase.getEglBaseContext(), null);
binding.surfaceView2.setEnableHardwareScaler(true);
binding.surfaceView2.setMirror(true);
7. 카메라 찾기
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
|
private VideoCapturer createCameraCapturer(CameraEnumerator enumerator) {
final String[] deviceNames = enumerator.getDeviceNames();
for (String deviceName : deviceNames) {
if (enumerator.isFrontFacing(deviceName)) {
VideoCapturer videoCapturer = enumerator.createCapturer(deviceName, null);
if (videoCapturer != null) {
return videoCapturer;
}
}
}
for (String deviceName : deviceNames) {
if (!enumerator.isFrontFacing(deviceName)) {
VideoCapturer videoCapturer = enumerator.createCapturer(deviceName, null);
if (videoCapturer != null) {
return videoCapturer;
}
}
}
return null;
}
|
cs |
<관련 Socket.io 출처 글>
1. 공식 socket.io
'Programming > Android' 카테고리의 다른 글
[JAVA/Android] Android Architecture Components - MVVM, MVP, Clean 아키텍쳐 비교 (0) | 2020.09.18 |
---|---|
[Android] 안드로이드 백그라운드 작업 (0) | 2020.09.03 |
[Android] 안드로이드 WebRTC 주의사항 (0) | 2020.08.25 |
[Android] 안드로이드 AppRTC 실행 및 오류 (0) | 2020.08.14 |
[Android] Kotlin 로그 출력 방법 (0) | 2020.08.11 |