├── settings.gradle
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── step3web
├── css
│ └── main.css
├── package.json
├── index.html
├── .gitignore
├── index.js
└── js
│ └── main.js
├── step4web
├── css
│ └── main.css
├── package.json
├── index.html
├── .gitignore
├── index.js
└── js
│ └── main.js
├── step1camera
├── .gitignore
├── src
│ ├── main
│ │ ├── res
│ │ │ ├── values
│ │ │ │ ├── strings.xml
│ │ │ │ ├── colors.xml
│ │ │ │ └── styles.xml
│ │ │ ├── mipmap-hdpi
│ │ │ │ ├── ic_launcher.png
│ │ │ │ └── ic_launcher_round.png
│ │ │ ├── mipmap-mdpi
│ │ │ │ ├── ic_launcher.png
│ │ │ │ └── ic_launcher_round.png
│ │ │ ├── mipmap-xhdpi
│ │ │ │ ├── ic_launcher.png
│ │ │ │ └── ic_launcher_round.png
│ │ │ ├── mipmap-xxhdpi
│ │ │ │ ├── ic_launcher.png
│ │ │ │ └── ic_launcher_round.png
│ │ │ ├── mipmap-xxxhdpi
│ │ │ │ ├── ic_launcher.png
│ │ │ │ └── ic_launcher_round.png
│ │ │ ├── mipmap-anydpi-v26
│ │ │ │ ├── ic_launcher.xml
│ │ │ │ └── ic_launcher_round.xml
│ │ │ ├── layout
│ │ │ │ └── activity_main.xml
│ │ │ ├── drawable-v24
│ │ │ │ └── ic_launcher_foreground.xml
│ │ │ └── drawable
│ │ │ │ └── ic_launcher_background.xml
│ │ ├── AndroidManifest.xml
│ │ └── java
│ │ │ └── cc
│ │ │ └── rome753
│ │ │ └── wat
│ │ │ └── MainActivity.java
│ ├── test
│ │ └── java
│ │ │ └── cc
│ │ │ └── rome753
│ │ │ └── wat
│ │ │ └── ExampleUnitTest.java
│ └── androidTest
│ │ └── java
│ │ └── cc
│ │ └── rome753
│ │ └── wat
│ │ └── ExampleInstrumentedTest.java
├── proguard-rules.pro
└── build.gradle
├── step2loopback
├── .gitignore
├── src
│ ├── main
│ │ ├── res
│ │ │ ├── values
│ │ │ │ ├── strings.xml
│ │ │ │ ├── colors.xml
│ │ │ │ └── styles.xml
│ │ │ ├── mipmap-hdpi
│ │ │ │ ├── ic_launcher.png
│ │ │ │ └── ic_launcher_round.png
│ │ │ ├── mipmap-mdpi
│ │ │ │ ├── ic_launcher.png
│ │ │ │ └── ic_launcher_round.png
│ │ │ ├── mipmap-xhdpi
│ │ │ │ ├── ic_launcher.png
│ │ │ │ └── ic_launcher_round.png
│ │ │ ├── mipmap-xxhdpi
│ │ │ │ ├── ic_launcher.png
│ │ │ │ └── ic_launcher_round.png
│ │ │ ├── mipmap-xxxhdpi
│ │ │ │ ├── ic_launcher.png
│ │ │ │ └── ic_launcher_round.png
│ │ │ ├── mipmap-anydpi-v26
│ │ │ │ ├── ic_launcher.xml
│ │ │ │ └── ic_launcher_round.xml
│ │ │ ├── layout
│ │ │ │ └── activity_main.xml
│ │ │ ├── drawable-v24
│ │ │ │ └── ic_launcher_foreground.xml
│ │ │ └── drawable
│ │ │ │ └── ic_launcher_background.xml
│ │ ├── java
│ │ │ └── cc
│ │ │ │ └── rome753
│ │ │ │ └── wat
│ │ │ │ ├── SdpAdapter.java
│ │ │ │ ├── PeerConnectionAdapter.java
│ │ │ │ └── MainActivity.java
│ │ └── AndroidManifest.xml
│ ├── test
│ │ └── java
│ │ │ └── cc
│ │ │ └── rome753
│ │ │ └── wat
│ │ │ └── ExampleUnitTest.java
│ └── androidTest
│ │ └── java
│ │ └── cc
│ │ └── rome753
│ │ └── wat
│ │ └── ExampleInstrumentedTest.java
├── proguard-rules.pro
└── build.gradle
├── step5opengl
├── .gitignore
├── src
│ ├── main
│ │ ├── res
│ │ │ ├── values
│ │ │ │ ├── strings.xml
│ │ │ │ ├── colors.xml
│ │ │ │ └── styles.xml
│ │ │ ├── mipmap-hdpi
│ │ │ │ ├── ic_launcher.png
│ │ │ │ └── ic_launcher_round.png
│ │ │ ├── mipmap-mdpi
│ │ │ │ ├── ic_launcher.png
│ │ │ │ └── ic_launcher_round.png
│ │ │ ├── mipmap-xhdpi
│ │ │ │ ├── ic_launcher.png
│ │ │ │ └── ic_launcher_round.png
│ │ │ ├── mipmap-xxhdpi
│ │ │ │ ├── ic_launcher.png
│ │ │ │ └── ic_launcher_round.png
│ │ │ ├── mipmap-xxxhdpi
│ │ │ │ ├── ic_launcher.png
│ │ │ │ └── ic_launcher_round.png
│ │ │ ├── mipmap-anydpi-v26
│ │ │ │ ├── ic_launcher.xml
│ │ │ │ └── ic_launcher_round.xml
│ │ │ ├── layout
│ │ │ │ └── activity_main.xml
│ │ │ ├── drawable-v24
│ │ │ │ └── ic_launcher_foreground.xml
│ │ │ └── drawable
│ │ │ │ └── ic_launcher_background.xml
│ │ ├── java
│ │ │ └── cc
│ │ │ │ └── rome753
│ │ │ │ └── wat
│ │ │ │ ├── App.kt
│ │ │ │ ├── YUVRender.kt
│ │ │ │ ├── egl
│ │ │ │ ├── EglThread.java
│ │ │ │ └── EglCore.java
│ │ │ │ ├── ImageBytes.java
│ │ │ │ ├── EglProcessor.java
│ │ │ │ ├── MainActivity.java
│ │ │ │ ├── ShaderUtils.java
│ │ │ │ └── YUVShader.kt
│ │ ├── assets
│ │ │ ├── shader_yuv_v.glsl
│ │ │ └── shader_yuv_f.glsl
│ │ └── AndroidManifest.xml
│ ├── test
│ │ └── java
│ │ │ └── cc
│ │ │ └── rome753
│ │ │ └── wat
│ │ │ └── ExampleUnitTest.java
│ └── androidTest
│ │ └── java
│ │ └── cc
│ │ └── rome753
│ │ └── wat
│ │ └── ExampleInstrumentedTest.java
├── proguard-rules.pro
└── build.gradle
├── step3signaling
├── .gitignore
├── src
│ ├── main
│ │ ├── res
│ │ │ ├── values
│ │ │ │ ├── strings.xml
│ │ │ │ ├── colors.xml
│ │ │ │ └── styles.xml
│ │ │ ├── mipmap-hdpi
│ │ │ │ ├── ic_launcher.png
│ │ │ │ └── ic_launcher_round.png
│ │ │ ├── mipmap-mdpi
│ │ │ │ ├── ic_launcher.png
│ │ │ │ └── ic_launcher_round.png
│ │ │ ├── mipmap-xhdpi
│ │ │ │ ├── ic_launcher.png
│ │ │ │ └── ic_launcher_round.png
│ │ │ ├── mipmap-xxhdpi
│ │ │ │ ├── ic_launcher.png
│ │ │ │ └── ic_launcher_round.png
│ │ │ ├── mipmap-xxxhdpi
│ │ │ │ ├── ic_launcher.png
│ │ │ │ └── ic_launcher_round.png
│ │ │ ├── mipmap-anydpi-v26
│ │ │ │ ├── ic_launcher.xml
│ │ │ │ └── ic_launcher_round.xml
│ │ │ ├── layout
│ │ │ │ └── activity_main.xml
│ │ │ ├── drawable-v24
│ │ │ │ └── ic_launcher_foreground.xml
│ │ │ └── drawable
│ │ │ │ └── ic_launcher_background.xml
│ │ ├── java
│ │ │ └── cc
│ │ │ │ └── rome753
│ │ │ │ └── wat
│ │ │ │ ├── SdpAdapter.java
│ │ │ │ ├── PeerConnectionAdapter.java
│ │ │ │ ├── SignalingClient.java
│ │ │ │ └── MainActivity.java
│ │ └── AndroidManifest.xml
│ ├── test
│ │ └── java
│ │ │ └── cc
│ │ │ └── rome753
│ │ │ └── wat
│ │ │ └── ExampleUnitTest.java
│ └── androidTest
│ │ └── java
│ │ └── cc
│ │ └── rome753
│ │ └── wat
│ │ └── ExampleInstrumentedTest.java
├── proguard-rules.pro
└── build.gradle
├── step4multipeers
├── .gitignore
├── src
│ ├── main
│ │ ├── res
│ │ │ ├── values
│ │ │ │ ├── strings.xml
│ │ │ │ ├── colors.xml
│ │ │ │ └── styles.xml
│ │ │ ├── mipmap-hdpi
│ │ │ │ ├── ic_launcher.png
│ │ │ │ └── ic_launcher_round.png
│ │ │ ├── mipmap-mdpi
│ │ │ │ ├── ic_launcher.png
│ │ │ │ └── ic_launcher_round.png
│ │ │ ├── mipmap-xhdpi
│ │ │ │ ├── ic_launcher.png
│ │ │ │ └── ic_launcher_round.png
│ │ │ ├── mipmap-xxhdpi
│ │ │ │ ├── ic_launcher.png
│ │ │ │ └── ic_launcher_round.png
│ │ │ ├── mipmap-xxxhdpi
│ │ │ │ ├── ic_launcher.png
│ │ │ │ └── ic_launcher_round.png
│ │ │ ├── mipmap-anydpi-v26
│ │ │ │ ├── ic_launcher.xml
│ │ │ │ └── ic_launcher_round.xml
│ │ │ ├── drawable-v24
│ │ │ │ └── ic_launcher_foreground.xml
│ │ │ ├── layout
│ │ │ │ └── activity_main.xml
│ │ │ └── drawable
│ │ │ │ └── ic_launcher_background.xml
│ │ ├── java
│ │ │ └── cc
│ │ │ │ └── rome753
│ │ │ │ └── wat
│ │ │ │ ├── SdpAdapter.java
│ │ │ │ ├── PeerConnectionAdapter.java
│ │ │ │ ├── SignalingClient.java
│ │ │ │ └── MainActivity.java
│ │ └── AndroidManifest.xml
│ ├── test
│ │ └── java
│ │ │ └── cc
│ │ │ └── rome753
│ │ │ └── wat
│ │ │ └── ExampleUnitTest.java
│ └── androidTest
│ │ └── java
│ │ └── cc
│ │ └── rome753
│ │ └── wat
│ │ └── ExampleInstrumentedTest.java
├── proguard-rules.pro
└── build.gradle
├── .idea
├── vcs.xml
├── compiler.xml
├── misc.xml
└── jarRepositories.xml
├── README.md
├── .gitignore
├── gradle.properties
├── gradlew.bat
└── gradlew
/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':step1camera', ':step2loopback', 'step3signaling', 'step4multipeers', 'step5opengl'
2 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rome753/webrtc-android-tutorial/HEAD/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/step3web/css/main.css:
--------------------------------------------------------------------------------
1 | body {
2 | font-family: sans-serif;
3 | }
4 |
5 | video {
6 | max-width: 100%;
7 | width: 320px;
8 | }
9 |
--------------------------------------------------------------------------------
/step4web/css/main.css:
--------------------------------------------------------------------------------
1 | body {
2 | font-family: sans-serif;
3 | }
4 |
5 | video {
6 | max-width: 100%;
7 | width: 320px;
8 | }
9 |
--------------------------------------------------------------------------------
/step1camera/.gitignore:
--------------------------------------------------------------------------------
1 | *.iml
2 | .gradle
3 | /local.properties
4 | /.idea
5 | .DS_Store
6 | /build
7 | /captures
8 | .externalNativeBuild
9 |
--------------------------------------------------------------------------------
/step1camera/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | webrtc-android-tutorial
3 |
4 |
--------------------------------------------------------------------------------
/step2loopback/.gitignore:
--------------------------------------------------------------------------------
1 | *.iml
2 | .gradle
3 | /local.properties
4 | /.idea
5 | .DS_Store
6 | /build
7 | /captures
8 | .externalNativeBuild
9 |
--------------------------------------------------------------------------------
/step2loopback/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | webrtc-android-tutorial
3 |
4 |
--------------------------------------------------------------------------------
/step5opengl/.gitignore:
--------------------------------------------------------------------------------
1 | *.iml
2 | .gradle
3 | /local.properties
4 | /.idea
5 | .DS_Store
6 | /build
7 | /captures
8 | .externalNativeBuild
9 |
--------------------------------------------------------------------------------
/step5opengl/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | webrtc-android-tutorial
3 |
4 |
--------------------------------------------------------------------------------
/step3signaling/.gitignore:
--------------------------------------------------------------------------------
1 | *.iml
2 | .gradle
3 | /local.properties
4 | /.idea
5 | .DS_Store
6 | /build
7 | /captures
8 | .externalNativeBuild
9 |
--------------------------------------------------------------------------------
/step3signaling/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | webrtc-android-tutorial
3 |
4 |
--------------------------------------------------------------------------------
/step4multipeers/.gitignore:
--------------------------------------------------------------------------------
1 | *.iml
2 | .gradle
3 | /local.properties
4 | /.idea
5 | .DS_Store
6 | /build
7 | /captures
8 | .externalNativeBuild
9 |
--------------------------------------------------------------------------------
/step4multipeers/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | webrtc-android-tutorial
3 |
4 |
--------------------------------------------------------------------------------
/step1camera/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rome753/webrtc-android-tutorial/HEAD/step1camera/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/step1camera/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rome753/webrtc-android-tutorial/HEAD/step1camera/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/step1camera/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rome753/webrtc-android-tutorial/HEAD/step1camera/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/step5opengl/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rome753/webrtc-android-tutorial/HEAD/step5opengl/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/step5opengl/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rome753/webrtc-android-tutorial/HEAD/step5opengl/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/step5opengl/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rome753/webrtc-android-tutorial/HEAD/step5opengl/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/step1camera/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rome753/webrtc-android-tutorial/HEAD/step1camera/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/step1camera/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rome753/webrtc-android-tutorial/HEAD/step1camera/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/step2loopback/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rome753/webrtc-android-tutorial/HEAD/step2loopback/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/step2loopback/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rome753/webrtc-android-tutorial/HEAD/step2loopback/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/step2loopback/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rome753/webrtc-android-tutorial/HEAD/step2loopback/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/step3signaling/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rome753/webrtc-android-tutorial/HEAD/step3signaling/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/step3signaling/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rome753/webrtc-android-tutorial/HEAD/step3signaling/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/step5opengl/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rome753/webrtc-android-tutorial/HEAD/step5opengl/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/step5opengl/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rome753/webrtc-android-tutorial/HEAD/step5opengl/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/step1camera/src/main/res/mipmap-hdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rome753/webrtc-android-tutorial/HEAD/step1camera/src/main/res/mipmap-hdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/step1camera/src/main/res/mipmap-mdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rome753/webrtc-android-tutorial/HEAD/step1camera/src/main/res/mipmap-mdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/step2loopback/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rome753/webrtc-android-tutorial/HEAD/step2loopback/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/step2loopback/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rome753/webrtc-android-tutorial/HEAD/step2loopback/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/step3signaling/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rome753/webrtc-android-tutorial/HEAD/step3signaling/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/step3signaling/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rome753/webrtc-android-tutorial/HEAD/step3signaling/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/step3signaling/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rome753/webrtc-android-tutorial/HEAD/step3signaling/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/step4multipeers/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rome753/webrtc-android-tutorial/HEAD/step4multipeers/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/step4multipeers/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rome753/webrtc-android-tutorial/HEAD/step4multipeers/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/step4multipeers/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rome753/webrtc-android-tutorial/HEAD/step4multipeers/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/step4multipeers/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rome753/webrtc-android-tutorial/HEAD/step4multipeers/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/step5opengl/src/main/res/mipmap-hdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rome753/webrtc-android-tutorial/HEAD/step5opengl/src/main/res/mipmap-hdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/step5opengl/src/main/res/mipmap-mdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rome753/webrtc-android-tutorial/HEAD/step5opengl/src/main/res/mipmap-mdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/step1camera/src/main/res/mipmap-xhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rome753/webrtc-android-tutorial/HEAD/step1camera/src/main/res/mipmap-xhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/step1camera/src/main/res/mipmap-xxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rome753/webrtc-android-tutorial/HEAD/step1camera/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/step2loopback/src/main/res/mipmap-hdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rome753/webrtc-android-tutorial/HEAD/step2loopback/src/main/res/mipmap-hdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/step2loopback/src/main/res/mipmap-mdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rome753/webrtc-android-tutorial/HEAD/step2loopback/src/main/res/mipmap-mdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/step4multipeers/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rome753/webrtc-android-tutorial/HEAD/step4multipeers/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/step5opengl/src/main/res/mipmap-xhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rome753/webrtc-android-tutorial/HEAD/step5opengl/src/main/res/mipmap-xhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/step5opengl/src/main/res/mipmap-xxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rome753/webrtc-android-tutorial/HEAD/step5opengl/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/step1camera/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rome753/webrtc-android-tutorial/HEAD/step1camera/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/step2loopback/src/main/res/mipmap-xhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rome753/webrtc-android-tutorial/HEAD/step2loopback/src/main/res/mipmap-xhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/step2loopback/src/main/res/mipmap-xxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rome753/webrtc-android-tutorial/HEAD/step2loopback/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/step2loopback/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rome753/webrtc-android-tutorial/HEAD/step2loopback/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/step3signaling/src/main/res/mipmap-hdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rome753/webrtc-android-tutorial/HEAD/step3signaling/src/main/res/mipmap-hdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/step3signaling/src/main/res/mipmap-mdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rome753/webrtc-android-tutorial/HEAD/step3signaling/src/main/res/mipmap-mdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/step3signaling/src/main/res/mipmap-xhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rome753/webrtc-android-tutorial/HEAD/step3signaling/src/main/res/mipmap-xhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/step3signaling/src/main/res/mipmap-xxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rome753/webrtc-android-tutorial/HEAD/step3signaling/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/step4multipeers/src/main/res/mipmap-hdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rome753/webrtc-android-tutorial/HEAD/step4multipeers/src/main/res/mipmap-hdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/step4multipeers/src/main/res/mipmap-mdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rome753/webrtc-android-tutorial/HEAD/step4multipeers/src/main/res/mipmap-mdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/step4multipeers/src/main/res/mipmap-xhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rome753/webrtc-android-tutorial/HEAD/step4multipeers/src/main/res/mipmap-xhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/step5opengl/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rome753/webrtc-android-tutorial/HEAD/step5opengl/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/step3signaling/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rome753/webrtc-android-tutorial/HEAD/step3signaling/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/step4multipeers/src/main/res/mipmap-xxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rome753/webrtc-android-tutorial/HEAD/step4multipeers/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/step4multipeers/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rome753/webrtc-android-tutorial/HEAD/step4multipeers/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/.idea/vcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.idea/compiler.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/step3web/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "webrtc-codelab",
3 | "version": "0.0.1",
4 | "description": "WebRTC codelab",
5 | "dependencies": {
6 | "node-static": "^0.7.10",
7 | "socket.io": "^2.0.4"
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/step4web/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "webrtc-codelab",
3 | "version": "0.0.1",
4 | "description": "WebRTC codelab",
5 | "dependencies": {
6 | "node-static": "^0.7.10",
7 | "socket.io": "^2.0.4"
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/step1camera/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #3F51B5
4 | #303F9F
5 | #FF4081
6 |
7 |
--------------------------------------------------------------------------------
/step2loopback/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #3F51B5
4 | #303F9F
5 | #FF4081
6 |
7 |
--------------------------------------------------------------------------------
/step3signaling/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #3F51B5
4 | #303F9F
5 | #FF4081
6 |
7 |
--------------------------------------------------------------------------------
/step4multipeers/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #3F51B5
4 | #303F9F
5 | #FF4081
6 |
7 |
--------------------------------------------------------------------------------
/step5opengl/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #3F51B5
4 | #303F9F
5 | #FF4081
6 |
7 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Sat Oct 12 20:19:15 CST 2019
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.5-all.zip
7 |
--------------------------------------------------------------------------------
/step1camera/src/main/res/mipmap-anydpi-v26/ic_launcher.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/step2loopback/src/main/res/mipmap-anydpi-v26/ic_launcher.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/step5opengl/src/main/java/cc/rome753/wat/App.kt:
--------------------------------------------------------------------------------
1 | package cc.rome753.wat
2 |
3 | import android.app.Application
4 |
5 | class App : Application() {
6 |
7 | override fun onCreate() {
8 | super.onCreate()
9 | app = this
10 | }
11 |
12 | companion object {
13 | var app : App? = null
14 | }
15 | }
--------------------------------------------------------------------------------
/step5opengl/src/main/res/mipmap-anydpi-v26/ic_launcher.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/step1camera/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/step3signaling/src/main/res/mipmap-anydpi-v26/ic_launcher.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/step4multipeers/src/main/res/mipmap-anydpi-v26/ic_launcher.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/step5opengl/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/step2loopback/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/step3signaling/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/step4multipeers/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # webrtc-android-tutorial
2 | The simplest tutorial for android WebRTC, based on [Google official tutorial(Javascript)](https://codelabs.developers.google.com/codelabs/webrtc-web/#0).
3 |
4 | A packaging of the webrtc library.
5 | https://mvnrepository.com/artifact/io.github.100mslive/webrtc
6 |
7 | [中文文档](https://www.jianshu.com/p/eb5fd116e6c8)
8 |
--------------------------------------------------------------------------------
/step1camera/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/step2loopback/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/step5opengl/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/step3signaling/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/step4multipeers/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/.idea/misc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/step1camera/src/test/java/cc/rome753/wat/ExampleUnitTest.java:
--------------------------------------------------------------------------------
1 | package cc.rome753.wat;
2 |
3 | import org.junit.Test;
4 |
5 | import static org.junit.Assert.*;
6 |
7 | /**
8 | * Example local unit test, which will execute on the development machine (host).
9 | *
10 | * @see Testing documentation
11 | */
12 | public class ExampleUnitTest {
13 | @Test
14 | public void addition_isCorrect() throws Exception {
15 | assertEquals(4, 2 + 2);
16 | }
17 | }
--------------------------------------------------------------------------------
/step2loopback/src/test/java/cc/rome753/wat/ExampleUnitTest.java:
--------------------------------------------------------------------------------
1 | package cc.rome753.wat;
2 |
3 | import org.junit.Test;
4 |
5 | import static org.junit.Assert.*;
6 |
7 | /**
8 | * Example local unit test, which will execute on the development machine (host).
9 | *
10 | * @see Testing documentation
11 | */
12 | public class ExampleUnitTest {
13 | @Test
14 | public void addition_isCorrect() throws Exception {
15 | assertEquals(4, 2 + 2);
16 | }
17 | }
--------------------------------------------------------------------------------
/step3signaling/src/test/java/cc/rome753/wat/ExampleUnitTest.java:
--------------------------------------------------------------------------------
1 | package cc.rome753.wat;
2 |
3 | import org.junit.Test;
4 |
5 | import static org.junit.Assert.*;
6 |
7 | /**
8 | * Example local unit test, which will execute on the development machine (host).
9 | *
10 | * @see Testing documentation
11 | */
12 | public class ExampleUnitTest {
13 | @Test
14 | public void addition_isCorrect() throws Exception {
15 | assertEquals(4, 2 + 2);
16 | }
17 | }
--------------------------------------------------------------------------------
/step4multipeers/src/test/java/cc/rome753/wat/ExampleUnitTest.java:
--------------------------------------------------------------------------------
1 | package cc.rome753.wat;
2 |
3 | import org.junit.Test;
4 |
5 | import static org.junit.Assert.*;
6 |
7 | /**
8 | * Example local unit test, which will execute on the development machine (host).
9 | *
10 | * @see Testing documentation
11 | */
12 | public class ExampleUnitTest {
13 | @Test
14 | public void addition_isCorrect() throws Exception {
15 | assertEquals(4, 2 + 2);
16 | }
17 | }
--------------------------------------------------------------------------------
/step5opengl/src/test/java/cc/rome753/wat/ExampleUnitTest.java:
--------------------------------------------------------------------------------
1 | package cc.rome753.wat;
2 |
3 | import org.junit.Test;
4 |
5 | import static org.junit.Assert.*;
6 |
7 | /**
8 | * Example local unit test, which will execute on the development machine (host).
9 | *
10 | * @see Testing documentation
11 | */
12 | public class ExampleUnitTest {
13 | @Test
14 | public void addition_isCorrect() throws Exception {
15 | assertEquals(4, 2 + 2);
16 | }
17 | }
--------------------------------------------------------------------------------
/step5opengl/src/main/assets/shader_yuv_v.glsl:
--------------------------------------------------------------------------------
1 | #version 300 es
2 | layout (location = 0) in vec3 vPosition;
3 | layout (location = 1) in vec3 vColor;
4 | layout (location = 2) in vec2 vTexCoord;
5 | out vec3 aColor;
6 | out vec2 aTexCoord;
7 |
8 | uniform mat4 transform;
9 |
10 | void main() {
11 | gl_Position = vec4(vPosition, 1.0f);
12 | aColor = vColor;
13 | aTexCoord = (transform * vec4(vTexCoord, 1.0f, 1.0f)).xy;
14 |
15 | // aTexCoord.y = 1.0f - aTexCoord.y;
16 | // aTexCoord = vTexCoord.xy;
17 | }
--------------------------------------------------------------------------------
/step3web/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Realtime communication with WebRTC
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 | Realtime communication with WebRTC
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/step4web/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Realtime communication with WebRTC
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 | Realtime communication with WebRTC
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/step1camera/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # If your project uses WebView with JS, uncomment the following
9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | # public *;
13 | #}
14 |
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 |
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
22 |
--------------------------------------------------------------------------------
/step5opengl/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # If your project uses WebView with JS, uncomment the following
9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | # public *;
13 | #}
14 |
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 |
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
22 |
--------------------------------------------------------------------------------
/step2loopback/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # If your project uses WebView with JS, uncomment the following
9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | # public *;
13 | #}
14 |
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 |
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
22 |
--------------------------------------------------------------------------------
/step3signaling/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # If your project uses WebView with JS, uncomment the following
9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | # public *;
13 | #}
14 |
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 |
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
22 |
--------------------------------------------------------------------------------
/step4multipeers/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # If your project uses WebView with JS, uncomment the following
9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | # public *;
13 | #}
14 |
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 |
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
22 |
--------------------------------------------------------------------------------
/step1camera/src/androidTest/java/cc/rome753/wat/ExampleInstrumentedTest.java:
--------------------------------------------------------------------------------
1 | package cc.rome753.wat;
2 |
3 | import android.content.Context;
4 | import android.support.test.InstrumentationRegistry;
5 | import android.support.test.runner.AndroidJUnit4;
6 |
7 | import org.junit.Test;
8 | import org.junit.runner.RunWith;
9 |
10 | import static org.junit.Assert.*;
11 |
12 | /**
13 | * Instrumented test, which will execute on an Android device.
14 | *
15 | * @see Testing documentation
16 | */
17 | @RunWith(AndroidJUnit4.class)
18 | public class ExampleInstrumentedTest {
19 | @Test
20 | public void useAppContext() throws Exception {
21 | // Context of the app under test.
22 | Context appContext = InstrumentationRegistry.getTargetContext();
23 |
24 | assertEquals("cc.rome753.wat", appContext.getPackageName());
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/step2loopback/src/androidTest/java/cc/rome753/wat/ExampleInstrumentedTest.java:
--------------------------------------------------------------------------------
1 | package cc.rome753.wat;
2 |
3 | import android.content.Context;
4 | import android.support.test.InstrumentationRegistry;
5 | import android.support.test.runner.AndroidJUnit4;
6 |
7 | import org.junit.Test;
8 | import org.junit.runner.RunWith;
9 |
10 | import static org.junit.Assert.*;
11 |
12 | /**
13 | * Instrumented test, which will execute on an Android device.
14 | *
15 | * @see Testing documentation
16 | */
17 | @RunWith(AndroidJUnit4.class)
18 | public class ExampleInstrumentedTest {
19 | @Test
20 | public void useAppContext() throws Exception {
21 | // Context of the app under test.
22 | Context appContext = InstrumentationRegistry.getTargetContext();
23 |
24 | assertEquals("cc.rome753.wat", appContext.getPackageName());
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/step5opengl/src/androidTest/java/cc/rome753/wat/ExampleInstrumentedTest.java:
--------------------------------------------------------------------------------
1 | package cc.rome753.wat;
2 |
3 | import android.content.Context;
4 | import android.support.test.InstrumentationRegistry;
5 | import android.support.test.runner.AndroidJUnit4;
6 |
7 | import org.junit.Test;
8 | import org.junit.runner.RunWith;
9 |
10 | import static org.junit.Assert.*;
11 |
12 | /**
13 | * Instrumented test, which will execute on an Android device.
14 | *
15 | * @see Testing documentation
16 | */
17 | @RunWith(AndroidJUnit4.class)
18 | public class ExampleInstrumentedTest {
19 | @Test
20 | public void useAppContext() throws Exception {
21 | // Context of the app under test.
22 | Context appContext = InstrumentationRegistry.getTargetContext();
23 |
24 | assertEquals("cc.rome753.wat", appContext.getPackageName());
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/step3signaling/src/androidTest/java/cc/rome753/wat/ExampleInstrumentedTest.java:
--------------------------------------------------------------------------------
1 | package cc.rome753.wat;
2 |
3 | import android.content.Context;
4 | import android.support.test.InstrumentationRegistry;
5 | import android.support.test.runner.AndroidJUnit4;
6 |
7 | import org.junit.Test;
8 | import org.junit.runner.RunWith;
9 |
10 | import static org.junit.Assert.*;
11 |
12 | /**
13 | * Instrumented test, which will execute on an Android device.
14 | *
15 | * @see Testing documentation
16 | */
17 | @RunWith(AndroidJUnit4.class)
18 | public class ExampleInstrumentedTest {
19 | @Test
20 | public void useAppContext() throws Exception {
21 | // Context of the app under test.
22 | Context appContext = InstrumentationRegistry.getTargetContext();
23 |
24 | assertEquals("cc.rome753.wat", appContext.getPackageName());
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/step4multipeers/src/androidTest/java/cc/rome753/wat/ExampleInstrumentedTest.java:
--------------------------------------------------------------------------------
1 | package cc.rome753.wat;
2 |
3 | import android.content.Context;
4 | import android.support.test.InstrumentationRegistry;
5 | import android.support.test.runner.AndroidJUnit4;
6 |
7 | import org.junit.Test;
8 | import org.junit.runner.RunWith;
9 |
10 | import static org.junit.Assert.*;
11 |
12 | /**
13 | * Instrumented test, which will execute on an Android device.
14 | *
15 | * @see Testing documentation
16 | */
17 | @RunWith(AndroidJUnit4.class)
18 | public class ExampleInstrumentedTest {
19 | @Test
20 | public void useAppContext() throws Exception {
21 | // Context of the app under test.
22 | Context appContext = InstrumentationRegistry.getTargetContext();
23 |
24 | assertEquals("cc.rome753.wat", appContext.getPackageName());
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/step1camera/src/main/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/step5opengl/src/main/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/step3web/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 |
6 | # Runtime data
7 | pids
8 | *.pid
9 | *.seed
10 | *.pid.lock
11 |
12 | # Directory for instrumented libs generated by jscoverage/JSCover
13 | lib-cov
14 |
15 | # Coverage directory used by tools like istanbul
16 | coverage
17 |
18 | # nyc test coverage
19 | .nyc_output
20 |
21 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
22 | .grunt
23 |
24 | # node-waf configuration
25 | .lock-wscript
26 |
27 | # Compiled binary addons (http://nodejs.org/api/addons.html)
28 | build/Release
29 |
30 | # Dependency directories
31 | node_modules
32 | */node_modules
33 | jspm_packages
34 | package-lock.json
35 | .DS_Store
36 |
37 | # Optional npm cache directory
38 | .npm
39 |
40 | # Optional eslint cache
41 | .eslintcache
42 |
43 | # Optional REPL history
44 | .node_repl_history
45 |
46 | # Output of 'npm pack'
47 | *.tgz
48 |
49 | # Yarn Integrity file
50 | .yarn-integrity
51 |
52 | cert.pem
53 | key.pem
54 |
--------------------------------------------------------------------------------
/step4web/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 |
6 | # Runtime data
7 | pids
8 | *.pid
9 | *.seed
10 | *.pid.lock
11 |
12 | # Directory for instrumented libs generated by jscoverage/JSCover
13 | lib-cov
14 |
15 | # Coverage directory used by tools like istanbul
16 | coverage
17 |
18 | # nyc test coverage
19 | .nyc_output
20 |
21 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
22 | .grunt
23 |
24 | # node-waf configuration
25 | .lock-wscript
26 |
27 | # Compiled binary addons (http://nodejs.org/api/addons.html)
28 | build/Release
29 |
30 | # Dependency directories
31 | node_modules
32 | */node_modules
33 | jspm_packages
34 | package-lock.json
35 | .DS_Store
36 |
37 | # Optional npm cache directory
38 | .npm
39 |
40 | # Optional eslint cache
41 | .eslintcache
42 |
43 | # Optional REPL history
44 | .node_repl_history
45 |
46 | # Output of 'npm pack'
47 | *.tgz
48 |
49 | # Yarn Integrity file
50 | .yarn-integrity
51 |
52 | cert.pem
53 | key.pem
54 |
--------------------------------------------------------------------------------
/step2loopback/src/main/java/cc/rome753/wat/SdpAdapter.java:
--------------------------------------------------------------------------------
1 | package cc.rome753.wat;
2 |
3 | import android.util.Log;
4 |
5 | import org.webrtc.SdpObserver;
6 | import org.webrtc.SessionDescription;
7 |
8 | /**
9 | * Created by chao on 2019/1/29.
10 | */
11 |
12 | public class SdpAdapter implements SdpObserver {
13 |
14 |
15 | private String tag;
16 |
17 | public SdpAdapter(String tag) {
18 | this.tag = "chao " + tag;
19 | }
20 |
21 | private void log(String s) {
22 | Log.d(tag, s);
23 | }
24 |
25 | @Override
26 | public void onCreateSuccess(SessionDescription sessionDescription) {
27 | log("onCreateSuccess " + sessionDescription);
28 | }
29 |
30 | @Override
31 | public void onSetSuccess() {
32 | log("onSetSuccess ");
33 | }
34 |
35 | @Override
36 | public void onCreateFailure(String s) {
37 | log("onCreateFailure " + s);
38 | }
39 |
40 | @Override
41 | public void onSetFailure(String s) {
42 | log("onSetFailure " + s);
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/step3signaling/src/main/java/cc/rome753/wat/SdpAdapter.java:
--------------------------------------------------------------------------------
1 | package cc.rome753.wat;
2 |
3 | import android.util.Log;
4 |
5 | import org.webrtc.SdpObserver;
6 | import org.webrtc.SessionDescription;
7 |
8 | /**
9 | * Created by chao on 2019/1/29.
10 | */
11 |
12 | public class SdpAdapter implements SdpObserver {
13 |
14 |
15 | private String tag;
16 |
17 | public SdpAdapter(String tag) {
18 | this.tag = "chao " + tag;
19 | }
20 |
21 | private void log(String s) {
22 | Log.d(tag, s);
23 | }
24 |
25 | @Override
26 | public void onCreateSuccess(SessionDescription sessionDescription) {
27 | log("onCreateSuccess " + sessionDescription);
28 | }
29 |
30 | @Override
31 | public void onSetSuccess() {
32 | log("onSetSuccess ");
33 | }
34 |
35 | @Override
36 | public void onCreateFailure(String s) {
37 | log("onCreateFailure " + s);
38 | }
39 |
40 | @Override
41 | public void onSetFailure(String s) {
42 | log("onSetFailure " + s);
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/step4multipeers/src/main/java/cc/rome753/wat/SdpAdapter.java:
--------------------------------------------------------------------------------
1 | package cc.rome753.wat;
2 |
3 | import android.util.Log;
4 |
5 | import org.webrtc.SdpObserver;
6 | import org.webrtc.SessionDescription;
7 |
8 | /**
9 | * Created by chao on 2019/1/29.
10 | */
11 |
12 | public class SdpAdapter implements SdpObserver {
13 |
14 |
15 | private String tag;
16 |
17 | public SdpAdapter(String tag) {
18 | this.tag = "chao " + tag;
19 | }
20 |
21 | private void log(String s) {
22 | Log.d(tag, s);
23 | }
24 |
25 | @Override
26 | public void onCreateSuccess(SessionDescription sessionDescription) {
27 | log("onCreateSuccess " + sessionDescription);
28 | }
29 |
30 | @Override
31 | public void onSetSuccess() {
32 | log("onSetSuccess ");
33 | }
34 |
35 | @Override
36 | public void onCreateFailure(String s) {
37 | log("onCreateFailure " + s);
38 | }
39 |
40 | @Override
41 | public void onSetFailure(String s) {
42 | log("onSetFailure " + s);
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/step5opengl/src/main/java/cc/rome753/wat/YUVRender.kt:
--------------------------------------------------------------------------------
1 | package cc.rome753.wat
2 |
3 | import android.media.Image
4 | import android.opengl.GLES20
5 | import android.opengl.GLSurfaceView
6 | import javax.microedition.khronos.egl.EGLConfig
7 | import javax.microedition.khronos.opengles.GL10
8 |
9 | class YUVRender: GLSurfaceView.Renderer {
10 |
11 | var yuvShader: YUVShader? = null
12 | // var yuvShader = DiffShader()
13 | var imageBytes: ImageBytes? = null
14 |
15 | override fun onSurfaceCreated(gl: GL10?, config: EGLConfig?) {
16 | if (yuvShader == null) {
17 | yuvShader = YUVShader()
18 | yuvShader!!.init()
19 | GLES20.glClearColor(0.5f, 0.5f, 0.5f, 0.5f)
20 | }
21 | }
22 |
23 | override fun onSurfaceChanged(gl: GL10?, width: Int, height: Int) {
24 | GLES20.glViewport(0, 0, width, height)
25 | }
26 |
27 | override fun onDrawFrame(gl: GL10?) {
28 | imageBytes?.let {
29 | GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT)
30 | yuvShader!!.draw(it)
31 | }
32 | }
33 |
34 | }
--------------------------------------------------------------------------------
/step5opengl/src/main/java/cc/rome753/wat/egl/EglThread.java:
--------------------------------------------------------------------------------
1 | package cc.rome753.wat.egl;
2 |
3 | import android.os.Handler;
4 | import android.os.HandlerThread;
5 |
6 | public class EglThread {
7 | Handler mHandler;
8 | EglCore mEglCore;
9 |
10 | public EglThread(String name) {
11 | HandlerThread ht = new HandlerThread(name);
12 | ht.start();
13 |
14 | mHandler = new Handler(ht.getLooper());
15 | }
16 |
17 | public void initEglCore(int w, int h) {
18 | if (mEglCore == null) {
19 | mHandler.post(() -> {
20 | mEglCore = new EglCore();
21 | mEglCore.makeCurrent(w, h);
22 | });
23 | }
24 | }
25 |
26 | public void post(Runnable r) {
27 | if (mHandler != null) {
28 | mHandler.post(r);
29 | }
30 | }
31 |
32 |
33 | public void destroy() {
34 | if (mHandler != null) {
35 | mHandler.post(() -> {
36 | mEglCore.release();
37 | mHandler.getLooper().quitSafely();
38 | });
39 | }
40 | }
41 |
42 |
43 | }
44 |
--------------------------------------------------------------------------------
/step1camera/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
8 |
9 |
10 |
11 |
12 |
19 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Built application files
2 | *.apk
3 | *.ap_
4 |
5 | # Files for the ART/Dalvik VM
6 | *.dex
7 |
8 | # Java class files
9 | *.class
10 |
11 | # Generated files
12 | bin/
13 | gen/
14 | out/
15 |
16 | # Gradle files
17 | .gradle/
18 | build/
19 |
20 | # Local configuration file (sdk path, etc)
21 | local.properties
22 |
23 | # Proguard folder generated by Eclipse
24 | proguard/
25 |
26 | # Log Files
27 | *.log
28 |
29 | # Android Studio Navigation editor temp files
30 | .navigation/
31 |
32 | # Android Studio captures folder
33 | captures/
34 |
35 | # IntelliJ
36 | *.iml
37 | .idea/
38 |
39 | # Keystore files
40 | # Uncomment the following line if you do not want to check your keystore files in.
41 | #*.jks
42 |
43 | # External native build folder generated in Android Studio 2.2 and later
44 | .externalNativeBuild
45 |
46 | # Google Services (e.g. APIs or Firebase)
47 | google-services.json
48 |
49 | # Freeline
50 | freeline.py
51 | freeline/
52 | freeline_project_description.json
53 |
54 | # fastlane
55 | fastlane/report.xml
56 | fastlane/Preview.html
57 | fastlane/screenshots
58 | fastlane/test_output
59 | fastlane/readme.md
60 |
--------------------------------------------------------------------------------
/step5opengl/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
8 |
9 |
10 |
11 |
12 |
20 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
--------------------------------------------------------------------------------
/step5opengl/src/main/assets/shader_yuv_f.glsl:
--------------------------------------------------------------------------------
1 | #version 300 es
2 |
3 | precision mediump float;
4 | in vec3 aColor;
5 | in vec2 aTexCoord;
6 | out vec4 fragColor;
7 |
8 | uniform sampler2D tex0;
9 | uniform sampler2D tex1;
10 | uniform sampler2D tex2;
11 |
12 | void main() {
13 |
14 | float y = texture(tex0, aTexCoord).r;
15 | float u = texture(tex1, aTexCoord).r - 0.5;
16 | float v = texture(tex1, aTexCoord).a - 0.5;
17 |
18 | // float y = texture(tex0, aTexCoord).r;
19 | // float u = texture(tex2, aTexCoord).r - 0.5;
20 | // float v = texture(tex1, aTexCoord).r - 0.5;
21 |
22 | vec3 yuv = vec3(y, v, u);
23 |
24 | // 下面两种视觉上差异不大
25 |
26 | // // BT.601, which is the standard for SDTV is provided as a reference
27 | // vec3 rgb = mat3( 1, 1, 1,
28 | // 0, -.39465, 2.03211,
29 | // 1.13983, -.58060, 0) * yuv;
30 |
31 | // Using BT.709 which is the standard for HDTV
32 | vec3 rgb = mat3( 1, 1, 1,
33 | 0, -.21482, 2.12798,
34 | 1.28033, -.38059, 0) * yuv;
35 |
36 |
37 | // fragColor = vec4(rgb, 1.0);
38 | fragColor = 1.0f - vec4(rgb, 1.0);
39 |
40 | }
--------------------------------------------------------------------------------
/step1camera/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.application'
2 |
3 | android {
4 | compileSdk 34
5 | defaultConfig {
6 | applicationId "cc.rome753.wat"
7 | minSdkVersion 21
8 | targetSdkVersion 34
9 | versionCode 1
10 | versionName "1.0"
11 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
12 | }
13 | buildTypes {
14 | release {
15 | minifyEnabled false
16 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
17 | }
18 | }
19 | compileOptions {
20 | sourceCompatibility JavaVersion.VERSION_11
21 | targetCompatibility JavaVersion.VERSION_11
22 | }
23 | }
24 |
25 | dependencies {
26 | implementation fileTree(dir: 'libs', include: ['*.jar'])
27 | implementation 'androidx.appcompat:appcompat:1.1.0'
28 | implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
29 | testImplementation 'junit:junit:4.12'
30 | androidTestImplementation 'androidx.test:runner:1.2.0'
31 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
32 |
33 | implementation 'io.github.100mslive:webrtc:m94-4606'
34 | }
35 |
--------------------------------------------------------------------------------
/step2loopback/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.application'
2 |
3 | android {
4 | compileSdk 34
5 | defaultConfig {
6 | applicationId "cc.rome753.wat"
7 | minSdkVersion 21
8 | targetSdkVersion 34
9 | versionCode 1
10 | versionName "1.0"
11 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
12 | }
13 | buildTypes {
14 | release {
15 | minifyEnabled false
16 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
17 | }
18 | }
19 | compileOptions {
20 | sourceCompatibility JavaVersion.VERSION_11
21 | targetCompatibility JavaVersion.VERSION_11
22 | }
23 | }
24 |
25 | dependencies {
26 | implementation fileTree(dir: 'libs', include: ['*.jar'])
27 | implementation 'androidx.appcompat:appcompat:1.1.0'
28 | implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
29 | testImplementation 'junit:junit:4.12'
30 | androidTestImplementation 'androidx.test:runner:1.2.0'
31 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
32 |
33 | implementation 'io.github.100mslive:webrtc:m94-4606'
34 | }
35 |
--------------------------------------------------------------------------------
/step2loopback/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
21 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
--------------------------------------------------------------------------------
/step3signaling/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
21 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
--------------------------------------------------------------------------------
/step4multipeers/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
21 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 | # IDE (e.g. Android Studio) users:
3 | # Gradle settings configured through the IDE *will override*
4 | # any settings specified in this file.
5 | # For more details on how to configure your build environment visit
6 | # http://www.gradle.org/docs/current/userguide/build_environment.html
7 | # Specifies the JVM arguments used for the daemon process.
8 | # The setting is particularly useful for tweaking memory settings.
9 | org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
10 | # When configured, Gradle will run in incubating parallel mode.
11 | # This option should only be used with decoupled projects. More details, visit
12 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
13 | # org.gradle.parallel=true
14 | # AndroidX package structure to make it clearer which packages are bundled with the
15 | # Android operating system, and which are packaged with your app"s APK
16 | # https://developer.android.com/topic/libraries/support-library/androidx-rn
17 | android.useAndroidX=true
18 | # Enables namespacing of each library's R class so that its R class includes only the
19 | # resources declared in the library itself and none from the library's dependencies,
20 | # thereby reducing the size of the R class for that library
21 | android.nonTransitiveRClass=true
--------------------------------------------------------------------------------
/step5opengl/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.application'
2 | apply plugin: 'org.jetbrains.kotlin.android'
3 |
4 | android {
5 | compileSdk 34
6 | defaultConfig {
7 | applicationId "cc.rome753.wat"
8 | minSdkVersion 21
9 | targetSdkVersion 34
10 | versionCode 1
11 | versionName "1.0"
12 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
13 | }
14 | buildTypes {
15 | release {
16 | minifyEnabled false
17 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
18 | }
19 | }
20 | compileOptions {
21 | sourceCompatibility JavaVersion.VERSION_1_8
22 | targetCompatibility JavaVersion.VERSION_1_8
23 | }
24 | }
25 |
26 | dependencies {
27 | implementation fileTree(dir: 'libs', include: ['*.jar'])
28 | implementation 'androidx.appcompat:appcompat:1.1.0'
29 | implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
30 | implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
31 | testImplementation 'junit:junit:4.12'
32 | androidTestImplementation 'androidx.test:runner:1.2.0'
33 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
34 |
35 | implementation 'io.github.100mslive:webrtc:m94-4606'
36 | }
37 |
--------------------------------------------------------------------------------
/step3signaling/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.application'
2 |
3 | android {
4 | compileSdk 34
5 | defaultConfig {
6 | applicationId "cc.rome753.wat"
7 | minSdkVersion 21
8 | targetSdkVersion 34
9 | versionCode 1
10 | versionName "1.0"
11 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
12 | }
13 | buildTypes {
14 | release {
15 | minifyEnabled false
16 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
17 | }
18 | }
19 | compileOptions {
20 | sourceCompatibility JavaVersion.VERSION_1_8
21 | targetCompatibility JavaVersion.VERSION_1_8
22 | }
23 | }
24 |
25 | dependencies {
26 | implementation fileTree(dir: 'libs', include: ['*.jar'])
27 | implementation 'androidx.appcompat:appcompat:1.1.0'
28 | implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
29 | testImplementation 'junit:junit:4.12'
30 | androidTestImplementation 'androidx.test:runner:1.2.0'
31 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
32 |
33 | implementation 'io.github.100mslive:webrtc:m94-4606'
34 | implementation('io.socket:socket.io-client:0.8.3') {
35 | // excluding org.json which is provided by Android
36 | exclude group: 'org.json', module: 'json'
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/step4multipeers/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.application'
2 |
3 | android {
4 | compileSdk 34
5 | defaultConfig {
6 | applicationId "cc.rome753.wat"
7 | minSdkVersion 21
8 | targetSdkVersion 34
9 | versionCode 1
10 | versionName "1.0"
11 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
12 | }
13 | buildTypes {
14 | release {
15 | minifyEnabled false
16 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
17 | }
18 | }
19 | compileOptions {
20 | sourceCompatibility JavaVersion.VERSION_1_8
21 | targetCompatibility JavaVersion.VERSION_1_8
22 | }
23 | }
24 |
25 | dependencies {
26 | implementation fileTree(dir: 'libs', include: ['*.jar'])
27 | implementation 'androidx.appcompat:appcompat:1.1.0'
28 | implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
29 | testImplementation 'junit:junit:4.12'
30 | androidTestImplementation 'androidx.test:runner:1.2.0'
31 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
32 |
33 | implementation 'io.github.100mslive:webrtc:m94-4606'
34 | implementation('io.socket:socket.io-client:0.8.3') {
35 | // excluding org.json which is provided by Android
36 | exclude group: 'org.json', module: 'json'
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/step2loopback/src/main/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
17 |
18 |
26 |
27 |
33 |
34 |
40 |
41 |
42 |
--------------------------------------------------------------------------------
/step3signaling/src/main/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
17 |
18 |
26 |
27 |
33 |
34 |
40 |
41 |
42 |
--------------------------------------------------------------------------------
/step1camera/src/main/res/drawable-v24/ic_launcher_foreground.xml:
--------------------------------------------------------------------------------
1 |
7 |
12 |
13 |
19 |
22 |
25 |
26 |
27 |
28 |
34 |
35 |
--------------------------------------------------------------------------------
/step2loopback/src/main/res/drawable-v24/ic_launcher_foreground.xml:
--------------------------------------------------------------------------------
1 |
7 |
12 |
13 |
19 |
22 |
25 |
26 |
27 |
28 |
34 |
35 |
--------------------------------------------------------------------------------
/step3signaling/src/main/res/drawable-v24/ic_launcher_foreground.xml:
--------------------------------------------------------------------------------
1 |
7 |
12 |
13 |
19 |
22 |
25 |
26 |
27 |
28 |
34 |
35 |
--------------------------------------------------------------------------------
/step5opengl/src/main/res/drawable-v24/ic_launcher_foreground.xml:
--------------------------------------------------------------------------------
1 |
7 |
12 |
13 |
19 |
22 |
25 |
26 |
27 |
28 |
34 |
35 |
--------------------------------------------------------------------------------
/step4multipeers/src/main/res/drawable-v24/ic_launcher_foreground.xml:
--------------------------------------------------------------------------------
1 |
7 |
12 |
13 |
19 |
22 |
25 |
26 |
27 |
28 |
34 |
35 |
--------------------------------------------------------------------------------
/.idea/jarRepositories.xml:
--------------------------------------------------------------------------------
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 |
--------------------------------------------------------------------------------
/step2loopback/src/main/java/cc/rome753/wat/PeerConnectionAdapter.java:
--------------------------------------------------------------------------------
1 | package cc.rome753.wat;
2 |
3 | import android.util.Log;
4 |
5 | import org.webrtc.DataChannel;
6 | import org.webrtc.IceCandidate;
7 | import org.webrtc.MediaStream;
8 | import org.webrtc.PeerConnection;
9 | import org.webrtc.RtpReceiver;
10 |
11 | /**
12 | * Created by chao on 2019/1/28.
13 | */
14 |
15 | public class PeerConnectionAdapter implements PeerConnection.Observer {
16 |
17 | private String tag;
18 |
19 | public PeerConnectionAdapter(String tag) {
20 | this.tag = "chao " + tag;
21 | }
22 |
23 | private void log(String s) {
24 | Log.d(tag, s);
25 | }
26 |
27 | @Override
28 | public void onSignalingChange(PeerConnection.SignalingState signalingState) {
29 | log("onSignalingChange " + signalingState);
30 | }
31 |
32 | @Override
33 | public void onIceConnectionChange(PeerConnection.IceConnectionState iceConnectionState) {
34 | log("onIceConnectionChange " + iceConnectionState);
35 | }
36 |
37 | @Override
38 | public void onIceConnectionReceivingChange(boolean b) {
39 | log("onIceConnectionReceivingChange " + b);
40 | }
41 |
42 | @Override
43 | public void onIceGatheringChange(PeerConnection.IceGatheringState iceGatheringState) {
44 | log("onIceGatheringChange " + iceGatheringState);
45 | }
46 |
47 | @Override
48 | public void onIceCandidate(IceCandidate iceCandidate) {
49 | log("onIceCandidate " + iceCandidate);
50 | }
51 |
52 | @Override
53 | public void onIceCandidatesRemoved(IceCandidate[] iceCandidates) {
54 | log("onIceCandidatesRemoved " + iceCandidates);
55 | }
56 |
57 | @Override
58 | public void onAddStream(MediaStream mediaStream) {
59 | log("onAddStream " + mediaStream);
60 | }
61 |
62 | @Override
63 | public void onRemoveStream(MediaStream mediaStream) {
64 | log("onRemoveStream " + mediaStream);
65 | }
66 |
67 | @Override
68 | public void onDataChannel(DataChannel dataChannel) {
69 | log("onDataChannel " + dataChannel);
70 | }
71 |
72 | @Override
73 | public void onRenegotiationNeeded() {
74 | log("onRenegotiationNeeded ");
75 | }
76 |
77 | @Override
78 | public void onAddTrack(RtpReceiver rtpReceiver, MediaStream[] mediaStreams) {
79 | log("onAddTrack " + mediaStreams);
80 | }
81 | }
82 |
--------------------------------------------------------------------------------
/step3signaling/src/main/java/cc/rome753/wat/PeerConnectionAdapter.java:
--------------------------------------------------------------------------------
1 | package cc.rome753.wat;
2 |
3 | import android.util.Log;
4 |
5 | import org.webrtc.DataChannel;
6 | import org.webrtc.IceCandidate;
7 | import org.webrtc.MediaStream;
8 | import org.webrtc.PeerConnection;
9 | import org.webrtc.RtpReceiver;
10 |
11 | /**
12 | * Created by chao on 2019/1/28.
13 | */
14 |
15 | public class PeerConnectionAdapter implements PeerConnection.Observer {
16 |
17 | private String tag;
18 |
19 | public PeerConnectionAdapter(String tag) {
20 | this.tag = "chao " + tag;
21 | }
22 |
23 | private void log(String s) {
24 | Log.d(tag, s);
25 | }
26 |
27 | @Override
28 | public void onSignalingChange(PeerConnection.SignalingState signalingState) {
29 | log("onSignalingChange " + signalingState);
30 | }
31 |
32 | @Override
33 | public void onIceConnectionChange(PeerConnection.IceConnectionState iceConnectionState) {
34 | log("onIceConnectionChange " + iceConnectionState);
35 | }
36 |
37 | @Override
38 | public void onIceConnectionReceivingChange(boolean b) {
39 | log("onIceConnectionReceivingChange " + b);
40 | }
41 |
42 | @Override
43 | public void onIceGatheringChange(PeerConnection.IceGatheringState iceGatheringState) {
44 | log("onIceGatheringChange " + iceGatheringState);
45 | }
46 |
47 | @Override
48 | public void onIceCandidate(IceCandidate iceCandidate) {
49 | log("onIceCandidate " + iceCandidate);
50 | }
51 |
52 | @Override
53 | public void onIceCandidatesRemoved(IceCandidate[] iceCandidates) {
54 | log("onIceCandidatesRemoved " + iceCandidates);
55 | }
56 |
57 | @Override
58 | public void onAddStream(MediaStream mediaStream) {
59 | log("onAddStream " + mediaStream);
60 | }
61 |
62 | @Override
63 | public void onRemoveStream(MediaStream mediaStream) {
64 | log("onRemoveStream " + mediaStream);
65 | }
66 |
67 | @Override
68 | public void onDataChannel(DataChannel dataChannel) {
69 | log("onDataChannel " + dataChannel);
70 | }
71 |
72 | @Override
73 | public void onRenegotiationNeeded() {
74 | log("onRenegotiationNeeded ");
75 | }
76 |
77 | @Override
78 | public void onAddTrack(RtpReceiver rtpReceiver, MediaStream[] mediaStreams) {
79 | log("onAddTrack " + mediaStreams);
80 | }
81 | }
82 |
--------------------------------------------------------------------------------
/step3web/index.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var os = require('os');
4 | var nodeStatic = require('node-static');
5 | var https = require('https');
6 | var socketIO = require('socket.io');
7 | var fs = require('fs');
8 | var options = {
9 | key: fs.readFileSync('key.pem'),
10 | cert: fs.readFileSync('cert.pem')
11 | };
12 |
13 | var fileServer = new(nodeStatic.Server)();
14 | var app = https.createServer(options, function(req, res) {
15 | fileServer.serve(req, res);
16 | }).listen(8080);
17 |
18 | var io = socketIO.listen(app);
19 | io.sockets.on('connection', function(socket) {
20 |
21 | // convenience function to log server messages on the client
22 | function log() {
23 | var array = ['Message from server:'];
24 | array.push.apply(array, arguments);
25 | socket.emit('log', array);
26 |
27 | console.log('chao', array);
28 | }
29 |
30 | socket.on('message', function(message) {
31 | log('Client said: ', message);
32 | // for a real app, would be room-only (not broadcast)
33 | socket.broadcast.emit('message', message);
34 | });
35 |
36 | socket.on('create or join', function(room) {
37 | log('Received request to create or join room ' + room);
38 |
39 | var clientsInRoom = io.sockets.adapter.rooms[room];
40 | var numClients = clientsInRoom ? Object.keys(clientsInRoom.sockets).length : 0;
41 | log('Room ' + room + ' now has ' + numClients + ' client(s)');
42 |
43 | if (numClients === 0) {
44 | socket.join(room);
45 | log('Client ID ' + socket.id + ' created room ' + room);
46 | socket.emit('created', room, socket.id);
47 |
48 | } else if (numClients === 1) {
49 | log('Client ID ' + socket.id + ' joined room ' + room);
50 | io.sockets.in(room).emit('join', room);
51 | socket.join(room);
52 | socket.emit('joined', room, socket.id);
53 | io.sockets.in(room).emit('ready');
54 | } else { // max two clients
55 | socket.emit('full', room);
56 | }
57 | });
58 |
59 | socket.on('ipaddr', function() {
60 | var ifaces = os.networkInterfaces();
61 | for (var dev in ifaces) {
62 | ifaces[dev].forEach(function(details) {
63 | if (details.family === 'IPv4' && details.address !== '127.0.0.1') {
64 | socket.emit('ipaddr', details.address);
65 | }
66 | });
67 | }
68 | });
69 |
70 | socket.on('bye', function(){
71 | console.log('received bye');
72 | });
73 |
74 | });
75 |
--------------------------------------------------------------------------------
/step4multipeers/src/main/java/cc/rome753/wat/PeerConnectionAdapter.java:
--------------------------------------------------------------------------------
1 | package cc.rome753.wat;
2 |
3 | import android.util.Log;
4 |
5 | import org.webrtc.DataChannel;
6 | import org.webrtc.IceCandidate;
7 | import org.webrtc.MediaStream;
8 | import org.webrtc.PeerConnection;
9 | import org.webrtc.RtpReceiver;
10 |
11 | /**
12 | * Created by chao on 2019/1/28.
13 | */
14 |
15 | public class PeerConnectionAdapter implements PeerConnection.Observer {
16 |
17 | private String tag;
18 |
19 | public PeerConnectionAdapter(String tag) {
20 | this.tag = "chao " + tag;
21 | }
22 |
23 | private void log(String s) {
24 | Log.d(tag, s);
25 | }
26 |
27 | @Override
28 | public void onSignalingChange(PeerConnection.SignalingState signalingState) {
29 | log("onSignalingChange " + signalingState);
30 | }
31 |
32 | @Override
33 | public void onIceConnectionChange(PeerConnection.IceConnectionState iceConnectionState) {
34 | log("onIceConnectionChange " + iceConnectionState);
35 | }
36 |
37 | @Override
38 | public void onIceConnectionReceivingChange(boolean b) {
39 | log("onIceConnectionReceivingChange " + b);
40 | }
41 |
42 | @Override
43 | public void onIceGatheringChange(PeerConnection.IceGatheringState iceGatheringState) {
44 | log("onIceGatheringChange " + iceGatheringState);
45 | }
46 |
47 | @Override
48 | public void onIceCandidate(IceCandidate iceCandidate) {
49 | log("onIceCandidate " + iceCandidate);
50 | }
51 |
52 | @Override
53 | public void onIceCandidatesRemoved(IceCandidate[] iceCandidates) {
54 | log("onIceCandidatesRemoved " + iceCandidates);
55 | }
56 |
57 | @Override
58 | public void onAddStream(MediaStream mediaStream) {
59 | log("onAddStream " + mediaStream);
60 | }
61 |
62 | @Override
63 | public void onRemoveStream(MediaStream mediaStream) {
64 | log("onRemoveStream " + mediaStream);
65 | }
66 |
67 | @Override
68 | public void onDataChannel(DataChannel dataChannel) {
69 | log("onDataChannel " + dataChannel);
70 | }
71 |
72 | @Override
73 | public void onRenegotiationNeeded() {
74 | log("onRenegotiationNeeded ");
75 | }
76 |
77 | @Override
78 | public void onAddTrack(RtpReceiver rtpReceiver, MediaStream[] mediaStreams) {
79 | log("onAddTrack " + mediaStreams);
80 | }
81 | }
82 |
--------------------------------------------------------------------------------
/step4web/index.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var os = require('os');
4 | var nodeStatic = require('node-static');
5 | var https = require('https');
6 | var socketIO = require('socket.io');
7 | var fs = require('fs');
8 | var options = {
9 | key: fs.readFileSync('key.pem'),
10 | cert: fs.readFileSync('cert.pem')
11 | };
12 |
13 | var fileServer = new(nodeStatic.Server)();
14 | var app = https.createServer(options, function(req, res) {
15 | fileServer.serve(req, res);
16 | }).listen(8080);
17 |
18 | var io = socketIO.listen(app);
19 | io.sockets.on('connection', function(socket) {
20 |
21 | // convenience function to log server messages on the client
22 | function log() {
23 | var array = ['Message from server:'];
24 | array.push.apply(array, arguments);
25 | socket.emit('log', array);
26 |
27 | console.log('chao', array);
28 | }
29 |
30 | socket.on('message', function(message) {
31 | // for a real app, would be room-only (not broadcast)
32 | // socket.broadcast.emit('message', message);
33 |
34 | var to = message['to'];
35 | log('from:' + socket.id + " to:" + to, message);
36 | io.sockets.sockets[to].emit('message', message);
37 | });
38 |
39 | socket.on('create or join', function(room) {
40 | log('Received request to create or join room ' + room);
41 |
42 | var clientsInRoom = io.sockets.adapter.rooms[room];
43 | var numClients = clientsInRoom ? Object.keys(clientsInRoom.sockets).length : 0;
44 | log('Room ' + room + ' now has ' + numClients + ' client(s)');
45 |
46 | if (numClients === 0) {
47 | socket.join(room);
48 | log('Client ID ' + socket.id + ' created room ' + room);
49 | socket.emit('created', room, socket.id);
50 |
51 | } else {
52 | log('Client ID ' + socket.id + ' joined room ' + room);
53 | io.sockets.in(room).emit('join', room, socket.id);
54 | socket.join(room);
55 | socket.emit('joined', room, socket.id);
56 | io.sockets.in(room).emit('ready');
57 | }
58 | });
59 |
60 | socket.on('ipaddr', function() {
61 | var ifaces = os.networkInterfaces();
62 | for (var dev in ifaces) {
63 | ifaces[dev].forEach(function(details) {
64 | if (details.family === 'IPv4' && details.address !== '127.0.0.1') {
65 | socket.emit('ipaddr', details.address);
66 | }
67 | });
68 | }
69 | });
70 |
71 | socket.on('bye', function(){
72 | console.log('received bye');
73 | });
74 |
75 | });
76 |
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @if "%DEBUG%" == "" @echo off
2 | @rem ##########################################################################
3 | @rem
4 | @rem Gradle startup script for Windows
5 | @rem
6 | @rem ##########################################################################
7 |
8 | @rem Set local scope for the variables with windows NT shell
9 | if "%OS%"=="Windows_NT" setlocal
10 |
11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
12 | set DEFAULT_JVM_OPTS=
13 |
14 | set DIRNAME=%~dp0
15 | if "%DIRNAME%" == "" set DIRNAME=.
16 | set APP_BASE_NAME=%~n0
17 | set APP_HOME=%DIRNAME%
18 |
19 | @rem Find java.exe
20 | if defined JAVA_HOME goto findJavaFromJavaHome
21 |
22 | set JAVA_EXE=java.exe
23 | %JAVA_EXE% -version >NUL 2>&1
24 | if "%ERRORLEVEL%" == "0" goto init
25 |
26 | echo.
27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
28 | echo.
29 | echo Please set the JAVA_HOME variable in your environment to match the
30 | echo location of your Java installation.
31 |
32 | goto fail
33 |
34 | :findJavaFromJavaHome
35 | set JAVA_HOME=%JAVA_HOME:"=%
36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
37 |
38 | if exist "%JAVA_EXE%" goto init
39 |
40 | echo.
41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
42 | echo.
43 | echo Please set the JAVA_HOME variable in your environment to match the
44 | echo location of your Java installation.
45 |
46 | goto fail
47 |
48 | :init
49 | @rem Get command-line arguments, handling Windowz variants
50 |
51 | if not "%OS%" == "Windows_NT" goto win9xME_args
52 | if "%@eval[2+2]" == "4" goto 4NT_args
53 |
54 | :win9xME_args
55 | @rem Slurp the command line arguments.
56 | set CMD_LINE_ARGS=
57 | set _SKIP=2
58 |
59 | :win9xME_args_slurp
60 | if "x%~1" == "x" goto execute
61 |
62 | set CMD_LINE_ARGS=%*
63 | goto execute
64 |
65 | :4NT_args
66 | @rem Get arguments from the 4NT Shell from JP Software
67 | set CMD_LINE_ARGS=%$
68 |
69 | :execute
70 | @rem Setup the command line
71 |
72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
73 |
74 | @rem Execute Gradle
75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
76 |
77 | :end
78 | @rem End local scope for the variables with windows NT shell
79 | if "%ERRORLEVEL%"=="0" goto mainEnd
80 |
81 | :fail
82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
83 | rem the _cmd.exe /c_ return code!
84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
85 | exit /b 1
86 |
87 | :mainEnd
88 | if "%OS%"=="Windows_NT" endlocal
89 |
90 | :omega
91 |
--------------------------------------------------------------------------------
/step4multipeers/src/main/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
17 |
18 |
26 |
27 |
35 |
36 |
44 |
45 |
51 |
52 |
58 |
59 |
60 |
--------------------------------------------------------------------------------
/step5opengl/src/main/java/cc/rome753/wat/ImageBytes.java:
--------------------------------------------------------------------------------
1 | package cc.rome753.wat;
2 |
3 | import android.media.Image;
4 |
5 | import org.webrtc.VideoFrame;
6 |
7 | import java.nio.ByteBuffer;
8 |
9 | public class ImageBytes {
10 | public byte[] bytes;
11 | public int width;
12 | public int height;
13 |
14 | public ByteBuffer bufY;
15 | public ByteBuffer bufU;
16 | public ByteBuffer bufV;
17 |
18 | public ImageBytes(byte[] bytes, int width, int height) {
19 | this.bytes = bytes;
20 | this.width = width;
21 | this.height = height;
22 |
23 | int r0 = width * height;
24 | int u0 = r0 / 4;
25 | int v0 = u0;
26 |
27 | bufY = ByteBuffer.allocate(r0).put(bytes, 0, r0);
28 | // camera1的nv21格式,需要把uv全部放到bufU中,否则画面只有一半正常
29 | // 实际上bufU只使用了一半,这跟相机画面扫描的方向有关
30 | bufU = ByteBuffer.allocate(u0 + v0).put(bytes, r0, u0 + v0);
31 | bufV = ByteBuffer.allocate(v0).put(bytes, r0 + u0, v0);
32 |
33 | bufY.position(0);
34 | bufU.position(0);
35 | bufV.position(0);
36 | }
37 |
38 | public ImageBytes(Image image) {
39 | final Image.Plane[] planes = image.getPlanes();
40 |
41 | Image.Plane p0 = planes[0];
42 | Image.Plane p1 = planes[1];
43 | Image.Plane p2 = planes[2];
44 |
45 | ByteBuffer b0 = p0.getBuffer();
46 | ByteBuffer b1 = p1.getBuffer();
47 | ByteBuffer b2 = p2.getBuffer();
48 |
49 | int r0 = b0.remaining();
50 |
51 | int w0 = p0.getRowStride();
52 | int h0 = r0 / w0;
53 | if(r0 % w0 != 0) h0++;
54 |
55 | this.width = w0;
56 | this.height = h0;
57 | this.bufY = b0;
58 | this.bufU = b1;
59 | this.bufV = b2;
60 | }
61 |
62 | public static ImageBytes create(VideoFrame image) {
63 | VideoFrame.I420Buffer buf = image.getBuffer().toI420();
64 |
65 | ByteBuffer b0 = buf.getDataY();
66 | ByteBuffer b1 = buf.getDataU();
67 | ByteBuffer b2 = buf.getDataV();
68 |
69 | int r0 = b0.remaining();
70 | int r1 = b1.remaining();
71 | int r2 = b2.remaining();
72 |
73 | int w0 = buf.getStrideY();
74 | int h0 = r0 / w0;
75 | if(r0 % w0 > 0) h0++;
76 | int w1 = buf.getStrideU();
77 | int h1 = r1 / w1;
78 | if(r1 % w1 > 1) h1++;
79 | int w2 = buf.getStrideV();
80 | int h2 = r2 / w2;
81 | if(r2 % w2 > 2) h2++;
82 |
83 | int y = w0 * h0;
84 | int u = w1 * h1;
85 | int v = w2 * h2;
86 |
87 | byte[] bytes = new byte[y + u + v];
88 |
89 | b0.get(bytes, 0, r0);
90 | b1.get(bytes, y, r1); // u
91 | b2.get(bytes, y + u, r2); // v
92 |
93 | return new ImageBytes(bytes, w0, h0);
94 | }
95 |
96 | }
97 |
--------------------------------------------------------------------------------
/step5opengl/src/main/java/cc/rome753/wat/EglProcessor.java:
--------------------------------------------------------------------------------
1 | package cc.rome753.wat;
2 |
3 | import android.opengl.GLES20;
4 |
5 | import org.webrtc.JavaI420Buffer;
6 | import org.webrtc.VideoFrame;
7 | import org.webrtc.VideoProcessor;
8 | import org.webrtc.VideoSink;
9 |
10 | import java.nio.ByteBuffer;
11 | import java.nio.IntBuffer;
12 |
13 | import cc.rome753.wat.egl.EglThread;
14 |
15 | public class EglProcessor implements VideoProcessor {
16 |
17 | EglThread eglThread = new EglThread("my-egl");
18 | YUVRender yuvRender = new YUVRender();
19 | VideoSink videoSink;
20 |
21 | ByteBuffer dataY;
22 | ByteBuffer dataU;
23 | ByteBuffer dataV;
24 |
25 | @Override
26 | public void setSink(VideoSink videoSink) {
27 | this.videoSink = videoSink;
28 | }
29 |
30 | @Override
31 | public void onCapturerStarted(boolean b) {
32 | }
33 |
34 | @Override
35 | public void onCapturerStopped() {
36 |
37 | }
38 |
39 | @Override
40 | public void onFrameCaptured(VideoFrame videoFrame) {
41 | int w = videoFrame.getBuffer().getWidth();
42 | int h = videoFrame.getBuffer().getHeight();
43 |
44 | // ImageBytes imageBytes = ImageBytes.create(videoFrame);
45 | VideoFrame.I420Buffer ori = videoFrame.getBuffer().toI420();
46 |
47 | eglThread.initEglCore(w, h);
48 | eglThread.post(() -> {
49 | yuvRender.onSurfaceCreated(null, null);
50 | yuvRender.onSurfaceChanged(null, w, h);
51 | GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
52 | yuvRender.getYuvShader().draw(w, h, ori.getDataY(), ori.getDataU(), ori.getDataU());
53 |
54 | IntBuffer buffer = IntBuffer.allocate(w * h);
55 | GLES20.glReadPixels(0, 0, w, h, GLES20.GL_RGBA, GLES20.GL_UNSIGNED_BYTE, buffer);
56 | byte[] yuv = YUVTools.rgb2Yuv420(buffer.array(), w, h);
57 | buffer.rewind();
58 |
59 | if (dataY == null) {
60 | dataY = ByteBuffer.allocateDirect(w * h);
61 | }
62 | dataY.put(yuv, 0, w * h);
63 |
64 | if (dataU == null) {
65 | dataU = ByteBuffer.allocateDirect(w * h / 4);
66 | }
67 | dataU.put(yuv, w * h, w * h / 4);
68 |
69 | if (dataV == null) {
70 | dataV = ByteBuffer.allocateDirect(w * h / 4);
71 | }
72 | dataV.put(yuv, w * h + w * h / 4, w * h / 4);
73 |
74 | dataY.position(0);
75 | dataU.position(0);
76 | dataV.position(0);
77 |
78 | JavaI420Buffer nb = JavaI420Buffer.wrap(w, h, dataY, ori.getStrideY(), dataU, ori.getStrideU(), dataV, ori.getStrideV(), null);
79 | VideoFrame newFrame = new VideoFrame(nb, videoFrame.getRotation(), videoFrame.getTimestampNs());
80 | videoSink.onFrame(newFrame);
81 | });
82 |
83 | // videoSink.onFrame(videoFrame);
84 | }
85 |
86 | }
87 |
--------------------------------------------------------------------------------
/step1camera/src/main/java/cc/rome753/wat/MainActivity.java:
--------------------------------------------------------------------------------
1 | package cc.rome753.wat;
2 |
3 | import android.os.Bundle;
4 | import androidx.appcompat.app.AppCompatActivity;
5 |
6 | import org.webrtc.AudioSource;
7 | import org.webrtc.AudioTrack;
8 | import org.webrtc.Camera1Enumerator;
9 | import org.webrtc.EglBase;
10 | import org.webrtc.MediaConstraints;
11 | import org.webrtc.PeerConnectionFactory;
12 | import org.webrtc.SurfaceTextureHelper;
13 | import org.webrtc.SurfaceViewRenderer;
14 | import org.webrtc.VideoCapturer;
15 | import org.webrtc.VideoSource;
16 | import org.webrtc.VideoTrack;
17 |
18 | public class MainActivity extends AppCompatActivity {
19 |
20 | @Override
21 | protected void onCreate(Bundle savedInstanceState) {
22 | super.onCreate(savedInstanceState);
23 | setContentView(R.layout.activity_main);
24 |
25 | // create PeerConnectionFactory
26 | PeerConnectionFactory.InitializationOptions initializationOptions =
27 | PeerConnectionFactory.InitializationOptions.builder(this).createInitializationOptions();
28 | PeerConnectionFactory.initialize(initializationOptions);
29 | PeerConnectionFactory peerConnectionFactory = PeerConnectionFactory.builder().createPeerConnectionFactory();
30 |
31 | // create AudioSource
32 | AudioSource audioSource = peerConnectionFactory.createAudioSource(new MediaConstraints());
33 | AudioTrack audioTrack = peerConnectionFactory.createAudioTrack("101", audioSource);
34 |
35 | EglBase.Context eglBaseContext = EglBase.create().getEglBaseContext();
36 |
37 | SurfaceTextureHelper surfaceTextureHelper = SurfaceTextureHelper.create("CaptureThread", eglBaseContext);
38 | // create VideoCapturer
39 | VideoCapturer videoCapturer = createCameraCapturer();
40 | VideoSource videoSource = peerConnectionFactory.createVideoSource(videoCapturer.isScreencast());
41 | videoCapturer.initialize(surfaceTextureHelper, getApplicationContext(), videoSource.getCapturerObserver());
42 | videoCapturer.startCapture(480, 640, 30);
43 |
44 | SurfaceViewRenderer localView = findViewById(R.id.localView);
45 | localView.setMirror(true);
46 | localView.init(eglBaseContext, null);
47 |
48 | // create VideoTrack
49 | VideoTrack videoTrack = peerConnectionFactory.createVideoTrack("100", videoSource);
50 | // display in localView
51 | videoTrack.addSink(localView);
52 | }
53 |
54 | private VideoCapturer createCameraCapturer() {
55 | Camera1Enumerator enumerator = new Camera1Enumerator(false);
56 | final String[] deviceNames = enumerator.getDeviceNames();
57 |
58 | // First, try to find front facing camera
59 | for (String deviceName : deviceNames) {
60 | if (enumerator.isFrontFacing(deviceName)) {
61 | VideoCapturer videoCapturer = enumerator.createCapturer(deviceName, null);
62 |
63 | if (videoCapturer != null) {
64 | return videoCapturer;
65 | }
66 | }
67 | }
68 |
69 | // Front facing camera not found, try something else
70 | for (String deviceName : deviceNames) {
71 | if (!enumerator.isFrontFacing(deviceName)) {
72 | VideoCapturer videoCapturer = enumerator.createCapturer(deviceName, null);
73 |
74 | if (videoCapturer != null) {
75 | return videoCapturer;
76 | }
77 | }
78 | }
79 |
80 | return null;
81 | }
82 |
83 | }
84 |
--------------------------------------------------------------------------------
/step5opengl/src/main/java/cc/rome753/wat/egl/EglCore.java:
--------------------------------------------------------------------------------
1 | package cc.rome753.wat.egl;
2 |
3 | import android.opengl.EGL14;
4 | import android.opengl.EGLConfig;
5 | import android.opengl.EGLContext;
6 | import android.opengl.EGLDisplay;
7 | import android.opengl.EGLSurface;
8 | import android.util.Log;
9 |
10 |
11 | public class EglCore {
12 |
13 | EGLDisplay mEglDisplay = EGL14.EGL_NO_DISPLAY;
14 | EGLContext mEglContext = EGL14.EGL_NO_CONTEXT;
15 | EGLConfig mEglConfig = null;
16 |
17 | EGLSurface mEglSurface = null;
18 |
19 | public EglCore() {
20 | mEglDisplay = EGL14.eglGetDisplay(EGL14.EGL_DEFAULT_DISPLAY);
21 | if (mEglDisplay == EGL14.EGL_NO_DISPLAY) {
22 | throw new RuntimeException("unable to get EGL14 display");
23 | }
24 | int[] version = new int[2];
25 | if (!EGL14.eglInitialize(mEglDisplay, version, 0, version, 0)) {
26 | mEglDisplay = null;
27 | throw new RuntimeException("unable to initialize EGL14");
28 | }
29 |
30 | String vendor = EGL14.eglQueryString(mEglDisplay, EGL14.EGL_VENDOR);
31 | Log.d("chao", "egl vendor: " + vendor); // 打印此版本EGL的实现厂商
32 |
33 | String versionStr = EGL14.eglQueryString(mEglDisplay, EGL14.EGL_VERSION);
34 | Log.d("chao", "egl version: " + versionStr);// 打印EGL版本号
35 |
36 | String extension = EGL14.eglQueryString(mEglDisplay, EGL14.EGL_EXTENSIONS);
37 | Log.d("chao", "egl extension: " + extension); //打印支持的EGL扩展
38 |
39 |
40 | int[] attributes = new int[] {
41 | EGL14.EGL_RED_SIZE, 8, //指定RGB中的R大小(bits)
42 | EGL14.EGL_GREEN_SIZE, 8, //指定G大小
43 | EGL14.EGL_BLUE_SIZE, 8, //指定B大小
44 | EGL14.EGL_ALPHA_SIZE, 8, //指定Alpha大小,以上四项实际上指定了像素格式
45 | EGL14.EGL_DEPTH_SIZE, 16, //指定深度缓存(Z Buffer)大小
46 | EGL14.EGL_RENDERABLE_TYPE, 4, //指定渲染api类别,这里或者是硬编码的4,或者是EGL14.EGL_OPENGL_ES2_BIT
47 | EGL14.EGL_NONE }; //总是以EGL10.EGL_NONE结尾
48 |
49 | EGLConfig[] configs = new EGLConfig[1];
50 | int[] numConfigs = new int[1];
51 | if (!EGL14.eglChooseConfig(mEglDisplay, attributes, 0, configs, 0, configs.length, numConfigs, 0)) { //获取所有
52 | Log.w("chao", "unable to find RGB8888 suitable EGLConfig");
53 | }
54 | mEglConfig = configs[0];
55 |
56 | int[] attrs = { EGL14.EGL_CONTEXT_CLIENT_VERSION, 2, EGL14.EGL_NONE };
57 | mEglContext = EGL14.eglCreateContext(mEglDisplay, mEglConfig, EGL14.EGL_NO_CONTEXT, attrs, 0);
58 | }
59 |
60 | EGLSurface createPbufferSurface(int w, int h) {
61 | int [] attrs = {
62 | EGL14.EGL_WIDTH, w,
63 | EGL14.EGL_HEIGHT, h,
64 | EGL14.EGL_NONE
65 | };
66 | EGLSurface eglSurface = EGL14.eglCreatePbufferSurface(mEglDisplay, mEglConfig, attrs, 0);
67 | if (eglSurface == null) {
68 | throw new RuntimeException("eglCreatePbufferSurface failed!");
69 | }
70 | return eglSurface;
71 | }
72 |
73 | public void makeCurrent(int w, int h) {
74 | mEglSurface = createPbufferSurface(w, h);
75 | if (!EGL14.eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mEglContext)) {
76 | Log.e("chao", "eglMakeCurrent failed");
77 | }
78 | }
79 |
80 | public void release() {
81 | EGL14.eglMakeCurrent(mEglDisplay, EGL14.EGL_NO_SURFACE, EGL14.EGL_NO_SURFACE, EGL14.EGL_NO_CONTEXT);
82 | EGL14.eglDestroySurface(mEglDisplay, mEglSurface);
83 | EGL14.eglDestroyContext(mEglDisplay, mEglContext);
84 | EGL14.eglReleaseThread();
85 | EGL14.eglTerminate(mEglDisplay);
86 | }
87 | }
88 |
--------------------------------------------------------------------------------
/step5opengl/src/main/java/cc/rome753/wat/MainActivity.java:
--------------------------------------------------------------------------------
1 | package cc.rome753.wat;
2 |
3 | import android.graphics.Bitmap;
4 | import android.opengl.GLES20;
5 | import android.os.Bundle;
6 | import android.widget.ImageView;
7 |
8 | import androidx.appcompat.app.AppCompatActivity;
9 |
10 | import org.webrtc.AudioSource;
11 | import org.webrtc.AudioTrack;
12 | import org.webrtc.Camera1Enumerator;
13 | import org.webrtc.EglBase;
14 | import org.webrtc.JavaI420Buffer;
15 | import org.webrtc.MediaConstraints;
16 | import org.webrtc.PeerConnectionFactory;
17 | import org.webrtc.SurfaceTextureHelper;
18 | import org.webrtc.SurfaceViewRenderer;
19 | import org.webrtc.VideoCapturer;
20 | import org.webrtc.VideoFrame;
21 | import org.webrtc.VideoProcessor;
22 | import org.webrtc.VideoSink;
23 | import org.webrtc.VideoSource;
24 | import org.webrtc.VideoTrack;
25 |
26 | import java.nio.ByteBuffer;
27 | import java.nio.IntBuffer;
28 | import java.util.Arrays;
29 |
30 | import cc.rome753.wat.egl.EglThread;
31 |
32 | public class MainActivity extends AppCompatActivity {
33 |
34 | @Override
35 | protected void onCreate(Bundle savedInstanceState) {
36 | super.onCreate(savedInstanceState);
37 | setContentView(R.layout.activity_main);
38 |
39 | // create PeerConnectionFactory
40 | PeerConnectionFactory.InitializationOptions initializationOptions =
41 | PeerConnectionFactory.InitializationOptions.builder(this).createInitializationOptions();
42 | PeerConnectionFactory.initialize(initializationOptions);
43 | PeerConnectionFactory peerConnectionFactory = PeerConnectionFactory.builder().createPeerConnectionFactory();
44 |
45 | // create AudioSource
46 | AudioSource audioSource = peerConnectionFactory.createAudioSource(new MediaConstraints());
47 | AudioTrack audioTrack = peerConnectionFactory.createAudioTrack("101", audioSource);
48 |
49 | EglBase.Context eglBaseContext = EglBase.create().getEglBaseContext();
50 |
51 | SurfaceTextureHelper surfaceTextureHelper = SurfaceTextureHelper.create("CaptureThread", eglBaseContext);
52 | // create VideoCapturer
53 | VideoCapturer videoCapturer = createCameraCapturer();
54 | VideoSource videoSource = peerConnectionFactory.createVideoSource(videoCapturer.isScreencast());
55 | videoSource.setVideoProcessor(new EglProcessor());
56 | videoCapturer.initialize(surfaceTextureHelper, getApplicationContext(), videoSource.getCapturerObserver());
57 | videoCapturer.startCapture(480, 640, 30);
58 |
59 | SurfaceViewRenderer localView = findViewById(R.id.localView);
60 | localView.setMirror(true);
61 | localView.init(eglBaseContext, null);
62 |
63 | // create VideoTrack
64 | VideoTrack videoTrack = peerConnectionFactory.createVideoTrack("100", videoSource);
65 | // display in localView
66 | videoTrack.addSink(localView);
67 | }
68 |
69 | private VideoCapturer createCameraCapturer() {
70 | Camera1Enumerator enumerator = new Camera1Enumerator(false);
71 | final String[] deviceNames = enumerator.getDeviceNames();
72 |
73 | // First, try to find front facing camera
74 | for (String deviceName : deviceNames) {
75 | if (enumerator.isFrontFacing(deviceName)) {
76 | VideoCapturer videoCapturer = enumerator.createCapturer(deviceName, null);
77 |
78 | if (videoCapturer != null) {
79 | return videoCapturer;
80 | }
81 | }
82 | }
83 |
84 | // Front facing camera not found, try something else
85 | for (String deviceName : deviceNames) {
86 | if (!enumerator.isFrontFacing(deviceName)) {
87 | VideoCapturer videoCapturer = enumerator.createCapturer(deviceName, null);
88 |
89 | if (videoCapturer != null) {
90 | return videoCapturer;
91 | }
92 | }
93 | }
94 |
95 | return null;
96 | }
97 |
98 | }
99 |
--------------------------------------------------------------------------------
/step5opengl/src/main/java/cc/rome753/wat/ShaderUtils.java:
--------------------------------------------------------------------------------
1 | package cc.rome753.wat;
2 |
3 | import static android.opengl.GLES30.GL_COMPILE_STATUS;
4 | import static android.opengl.GLES30.GL_FRAGMENT_SHADER;
5 | import static android.opengl.GLES30.GL_LINK_STATUS;
6 | import static android.opengl.GLES30.GL_VERTEX_SHADER;
7 | import static android.opengl.GLES30.glAttachShader;
8 | import static android.opengl.GLES30.glCompileShader;
9 | import static android.opengl.GLES30.glCreateProgram;
10 | import static android.opengl.GLES30.glCreateShader;
11 | import static android.opengl.GLES30.glDeleteProgram;
12 | import static android.opengl.GLES30.glDeleteShader;
13 | import static android.opengl.GLES30.glGetProgramInfoLog;
14 | import static android.opengl.GLES30.glGetProgramiv;
15 | import static android.opengl.GLES30.glGetShaderInfoLog;
16 | import static android.opengl.GLES30.glGetShaderiv;
17 | import static android.opengl.GLES30.glLinkProgram;
18 | import static android.opengl.GLES30.glShaderSource;
19 |
20 | import android.graphics.Bitmap;
21 | import android.graphics.BitmapFactory;
22 | import android.util.Log;
23 |
24 | import java.io.InputStream;
25 | import java.nio.charset.StandardCharsets;
26 |
27 | public class ShaderUtils {
28 |
29 | public static int loadProgram() {
30 | int vShader = ShaderUtils.loadShader(GL_VERTEX_SHADER, loadAssets("shader_base_v.glsl"));
31 | int fShader = ShaderUtils.loadShader(GL_FRAGMENT_SHADER, loadAssets("shader_base_f.glsl"));
32 | return linkProgram(vShader, fShader);
33 | }
34 |
35 | public static int loadProgramYUV() {
36 | int vShader = ShaderUtils.loadShader(GL_VERTEX_SHADER, loadAssets("shader_yuv_v.glsl"));
37 | int fShader = ShaderUtils.loadShader(GL_FRAGMENT_SHADER, loadAssets("shader_yuv_f.glsl"));
38 | return linkProgram(vShader, fShader);
39 | }
40 |
41 | public static int loadProgramDiff() {
42 | int vShader = ShaderUtils.loadShader(GL_VERTEX_SHADER, loadAssets("shader_diff_v.glsl"));
43 | int fShader = ShaderUtils.loadShader(GL_FRAGMENT_SHADER, loadAssets("shader_diff_f.glsl"));
44 | return linkProgram(vShader, fShader);
45 | }
46 |
47 | public static int loadProgram(String vs, String fs) {
48 | int vShader = ShaderUtils.loadShader(GL_VERTEX_SHADER, vs);
49 | int fShader = ShaderUtils.loadShader(GL_FRAGMENT_SHADER, fs);
50 | return linkProgram(vShader, fShader);
51 | }
52 |
53 | private static int loadShader(int type, String shaderSrc) {
54 | int shader = glCreateShader(type);
55 | if (shader == 0) {
56 | Log.e("chao", "compile shader == 0");
57 | return 0;
58 | }
59 | glShaderSource(shader, shaderSrc);
60 | glCompileShader(shader);
61 | int[] compileStatus = new int[1];
62 | glGetShaderiv(shader, GL_COMPILE_STATUS, compileStatus, 0);
63 | if (compileStatus[0] == 0) {
64 | String log = glGetShaderInfoLog(shader);
65 | Log.e("chao", "glGetShaderiv fail " + log);
66 | glDeleteShader(shader);
67 | return 0;
68 | }
69 | return shader;
70 | }
71 |
72 | private static int linkProgram(int vShader, int fShader) {
73 | int program = glCreateProgram();
74 | if (program == 0) {
75 | Log.e("chao", "program == 0");
76 | return 0;
77 | }
78 |
79 | glAttachShader(program, vShader);
80 | glAttachShader(program, fShader);
81 |
82 | glLinkProgram(program);
83 | int[] linkStatus = new int[1];
84 | glGetProgramiv(program, GL_LINK_STATUS, linkStatus, 0);
85 | if (linkStatus[0] == 0) {
86 | String log = glGetProgramInfoLog(program);
87 | Log.e("chao", "linkProgram fail " + log);
88 | glDeleteProgram(program);
89 | return 0;
90 | }
91 | return program;
92 | }
93 |
94 |
95 | public static String loadAssets(String name) {
96 | String s = null;
97 | try {
98 | InputStream is = App.Companion.getApp().getAssets().open(name);
99 | int size = is.available();
100 | byte[] buffer = new byte[size];
101 | is.read(buffer);
102 | is.close();
103 | s = new String(buffer, StandardCharsets.UTF_8);
104 | } catch (Exception e) {
105 | e.printStackTrace();
106 | }
107 | return s;
108 | }
109 |
110 | public static Bitmap loadImageAssets(String name) {
111 | try {
112 | InputStream is = App.Companion.getApp().getAssets().open(name);
113 | return BitmapFactory.decodeStream(is);
114 | } catch (Exception e) {
115 | e.printStackTrace();
116 | }
117 | return null;
118 | }
119 | }
120 |
--------------------------------------------------------------------------------
/step5opengl/src/main/java/cc/rome753/wat/YUVShader.kt:
--------------------------------------------------------------------------------
1 | package cc.rome753.wat
2 |
3 | import android.media.Image
4 | import android.opengl.GLES20.*
5 | import android.opengl.GLES30
6 | import android.opengl.GLES30.glBindVertexArray
7 | import android.opengl.GLES30.glGenVertexArrays
8 | import android.opengl.Matrix
9 | import java.nio.ByteBuffer
10 | import java.nio.ByteOrder
11 | import java.nio.FloatBuffer
12 | import java.nio.IntBuffer
13 |
14 | class YUVShader {
15 |
16 | var vertices = floatArrayOf( // ---- 位置 ---- ---- 颜色 ---- - 纹理坐标 -
17 | -1f, -1f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, // 左下
18 | 1f, -1f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, // 右下
19 | -1f, 1f, 0.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f, // 左上
20 | 1f, 1f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f // 右上
21 | )
22 |
23 | val indices = intArrayOf( // 注意索引从0开始!
24 | 0, 1, 2, // 第一个三角形
25 | 1, 2, 3 // 第二个三角形
26 | )
27 |
28 | var program = 0
29 | var vertexBuffer: FloatBuffer? = null
30 | var intBuffer: IntBuffer? = null
31 | var vao: IntArray = IntArray(1)
32 |
33 | var tex: IntArray = IntArray(3) // yuv
34 |
35 | var transform = FloatArray(16)
36 |
37 | fun init() {
38 | program = ShaderUtils.loadProgramYUV()
39 | //分配内存空间,每个浮点型占4字节空间
40 | vertexBuffer = ByteBuffer.allocateDirect(vertices.size * 4)
41 | .order(ByteOrder.nativeOrder())
42 | .asFloatBuffer()
43 | //传入指定的坐标数据
44 | vertexBuffer!!.put(vertices)
45 | vertexBuffer!!.position(0)
46 | vao = IntArray(1)
47 | glGenVertexArrays(1, vao, 0)
48 | glBindVertexArray(vao[0])
49 | val vbo = IntArray(1)
50 | glGenBuffers(1, vbo, 0)
51 | glBindBuffer(GL_ARRAY_BUFFER, vbo[0])
52 | glBufferData(GL_ARRAY_BUFFER, vertices.size * 4, vertexBuffer, GL_STATIC_DRAW)
53 |
54 | intBuffer = IntBuffer.allocate(indices.size * 4)
55 | intBuffer!!.put(indices)
56 | intBuffer!!.position(0)
57 | val ebo = IntArray(1)
58 | glGenBuffers(1, ebo, 0)
59 | glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo[0])
60 | glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices.size * 4, intBuffer, GL_STATIC_DRAW)
61 |
62 | glUseProgram(program)
63 | glGenTextures(3, tex, 0)
64 | for (i in 0..2) {
65 | glActiveTexture(GL_TEXTURE0 + i)
66 | glBindTexture(GL_TEXTURE_2D, tex[i])
67 | // 为当前绑定的纹理对象设置环绕、过滤方式
68 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT)
69 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT)
70 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR)
71 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR)
72 | // val bitmap: Bitmap = ShaderUtils.loadImageAssets("face.png")
73 | // GLUtils.texImage2D(GL_TEXTURE_2D, 0, bitmap, 0)
74 | // glGenerateMipmap(GL_TEXTURE_2D)
75 |
76 | val loc0 = glGetUniformLocation(program, "tex$i")
77 | glUniform1i(loc0, i)
78 | }
79 |
80 | // Load the vertex data
81 | glVertexAttribPointer(0, 3, GL_FLOAT, false, 8 * 4, 0)
82 | glEnableVertexAttribArray(0)
83 | glVertexAttribPointer(1, 3, GL_FLOAT, false, 8 * 4, 3 * 4)
84 | glEnableVertexAttribArray(1)
85 | glVertexAttribPointer(2, 2, GL_FLOAT, false, 8 * 4, 6 * 4)
86 | glEnableVertexAttribArray(2)
87 |
88 | glBindBuffer(GL_ARRAY_BUFFER, 0)
89 | glBindVertexArray(0)
90 |
91 | Matrix.setIdentityM(transform, 0)
92 |
93 | }
94 |
95 | fun draw(ib: ImageBytes) {
96 | draw(ib.width, ib.height, ib.bufY, ib.bufU, ib.bufV)
97 | }
98 |
99 | fun draw(w0: Int, h0: Int, bufY: ByteBuffer, bufU: ByteBuffer, bufV: ByteBuffer) {
100 | val w1 = w0 / 2
101 | val h1 = h0 / 2
102 | glUseProgram(program)
103 |
104 | glActiveTexture(tex[0])
105 | glBindTexture(GL_TEXTURE_2D, tex[0])
106 | glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, w0, h0, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, bufY)
107 |
108 | glActiveTexture(tex[1])
109 | glBindTexture(GL_TEXTURE_2D, tex[1])
110 | glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE_ALPHA, w1, h1, 0, GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE, bufU)
111 | // glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, w1, h1, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, ib.bufU)
112 |
113 | glActiveTexture(tex[2])
114 | glBindTexture(GL_TEXTURE_2D, tex[2])
115 | glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE_ALPHA, w1, h1, 0, GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE, bufV)
116 | // glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, w1, h1, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, ib.bufV)
117 |
118 | val loc = glGetUniformLocation(program, "transform")
119 | glUniformMatrix4fv(loc, 1, false, transform, 0)
120 |
121 | glBindVertexArray(vao[0])
122 | glDrawElements(GL_TRIANGLES, vertices.size, GL_UNSIGNED_INT, 0)
123 | }
124 |
125 | }
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | ##############################################################################
4 | ##
5 | ## Gradle start up script for UN*X
6 | ##
7 | ##############################################################################
8 |
9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
10 | DEFAULT_JVM_OPTS=""
11 |
12 | APP_NAME="Gradle"
13 | APP_BASE_NAME=`basename "$0"`
14 |
15 | # Use the maximum available, or set MAX_FD != -1 to use that value.
16 | MAX_FD="maximum"
17 |
18 | warn ( ) {
19 | echo "$*"
20 | }
21 |
22 | die ( ) {
23 | echo
24 | echo "$*"
25 | echo
26 | exit 1
27 | }
28 |
29 | # OS specific support (must be 'true' or 'false').
30 | cygwin=false
31 | msys=false
32 | darwin=false
33 | case "`uname`" in
34 | CYGWIN* )
35 | cygwin=true
36 | ;;
37 | Darwin* )
38 | darwin=true
39 | ;;
40 | MINGW* )
41 | msys=true
42 | ;;
43 | esac
44 |
45 | # Attempt to set APP_HOME
46 | # Resolve links: $0 may be a link
47 | PRG="$0"
48 | # Need this for relative symlinks.
49 | while [ -h "$PRG" ] ; do
50 | ls=`ls -ld "$PRG"`
51 | link=`expr "$ls" : '.*-> \(.*\)$'`
52 | if expr "$link" : '/.*' > /dev/null; then
53 | PRG="$link"
54 | else
55 | PRG=`dirname "$PRG"`"/$link"
56 | fi
57 | done
58 | SAVED="`pwd`"
59 | cd "`dirname \"$PRG\"`/" >/dev/null
60 | APP_HOME="`pwd -P`"
61 | cd "$SAVED" >/dev/null
62 |
63 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
64 |
65 | # Determine the Java command to use to start the JVM.
66 | if [ -n "$JAVA_HOME" ] ; then
67 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
68 | # IBM's JDK on AIX uses strange locations for the executables
69 | JAVACMD="$JAVA_HOME/jre/sh/java"
70 | else
71 | JAVACMD="$JAVA_HOME/bin/java"
72 | fi
73 | if [ ! -x "$JAVACMD" ] ; then
74 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
75 |
76 | Please set the JAVA_HOME variable in your environment to match the
77 | location of your Java installation."
78 | fi
79 | else
80 | JAVACMD="java"
81 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
82 |
83 | Please set the JAVA_HOME variable in your environment to match the
84 | location of your Java installation."
85 | fi
86 |
87 | # Increase the maximum file descriptors if we can.
88 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
89 | MAX_FD_LIMIT=`ulimit -H -n`
90 | if [ $? -eq 0 ] ; then
91 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
92 | MAX_FD="$MAX_FD_LIMIT"
93 | fi
94 | ulimit -n $MAX_FD
95 | if [ $? -ne 0 ] ; then
96 | warn "Could not set maximum file descriptor limit: $MAX_FD"
97 | fi
98 | else
99 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
100 | fi
101 | fi
102 |
103 | # For Darwin, add options to specify how the application appears in the dock
104 | if $darwin; then
105 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
106 | fi
107 |
108 | # For Cygwin, switch paths to Windows format before running java
109 | if $cygwin ; then
110 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
111 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
112 | JAVACMD=`cygpath --unix "$JAVACMD"`
113 |
114 | # We build the pattern for arguments to be converted via cygpath
115 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
116 | SEP=""
117 | for dir in $ROOTDIRSRAW ; do
118 | ROOTDIRS="$ROOTDIRS$SEP$dir"
119 | SEP="|"
120 | done
121 | OURCYGPATTERN="(^($ROOTDIRS))"
122 | # Add a user-defined pattern to the cygpath arguments
123 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
124 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
125 | fi
126 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
127 | i=0
128 | for arg in "$@" ; do
129 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
130 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
131 |
132 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
133 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
134 | else
135 | eval `echo args$i`="\"$arg\""
136 | fi
137 | i=$((i+1))
138 | done
139 | case $i in
140 | (0) set -- ;;
141 | (1) set -- "$args0" ;;
142 | (2) set -- "$args0" "$args1" ;;
143 | (3) set -- "$args0" "$args1" "$args2" ;;
144 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
145 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
146 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
147 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
148 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
149 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
150 | esac
151 | fi
152 |
153 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
154 | function splitJvmOpts() {
155 | JVM_OPTS=("$@")
156 | }
157 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
158 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
159 |
160 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
161 |
--------------------------------------------------------------------------------
/step3signaling/src/main/java/cc/rome753/wat/SignalingClient.java:
--------------------------------------------------------------------------------
1 | package cc.rome753.wat;
2 |
3 | import android.telecom.Call;
4 | import android.util.Log;
5 |
6 | import org.json.JSONException;
7 | import org.json.JSONObject;
8 | import org.webrtc.IceCandidate;
9 | import org.webrtc.SessionDescription;
10 |
11 | import java.net.URISyntaxException;
12 | import java.security.KeyManagementException;
13 | import java.security.NoSuchAlgorithmException;
14 | import java.security.cert.CertificateException;
15 | import java.security.cert.X509Certificate;
16 | import java.util.Arrays;
17 |
18 | import javax.net.ssl.SSLContext;
19 | import javax.net.ssl.TrustManager;
20 | import javax.net.ssl.X509TrustManager;
21 |
22 | import io.socket.client.IO;
23 | import io.socket.client.Socket;
24 |
25 | /**
26 | * Created by chao on 2019/1/30.
27 | */
28 |
29 | public class SignalingClient {
30 |
31 | private static SignalingClient instance;
32 | private SignalingClient(){
33 | init();
34 | }
35 | public static SignalingClient get() {
36 | if(instance == null) {
37 | synchronized (SignalingClient.class) {
38 | if(instance == null) {
39 | instance = new SignalingClient();
40 | }
41 | }
42 | }
43 | return instance;
44 | }
45 |
46 | private Socket socket;
47 | private String room = "OldPlace";
48 | private Callback callback;
49 |
50 | private final TrustManager[] trustAll = new TrustManager[]{
51 | new X509TrustManager() {
52 | @Override
53 | public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
54 |
55 | }
56 |
57 | @Override
58 | public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
59 |
60 | }
61 |
62 | @Override
63 | public X509Certificate[] getAcceptedIssuers() {
64 | return new X509Certificate[0];
65 | }
66 | }
67 | };
68 |
69 | public void setCallback(Callback callback) {
70 | this.callback = callback;
71 | }
72 |
73 | private void init() {
74 | try {
75 | SSLContext sslContext = SSLContext.getInstance("TLS");
76 | sslContext.init(null, trustAll, null);
77 | IO.setDefaultHostnameVerifier((hostname, session) -> true);
78 | IO.setDefaultSSLContext(sslContext);
79 |
80 | socket = IO.socket("https://192.168.1.97:8080");
81 | socket.connect();
82 |
83 | socket.emit("create or join", room);
84 |
85 | socket.on("created", args -> {
86 | Log.e("chao", "room created");
87 | callback.onCreateRoom();
88 | });
89 | socket.on("full", args -> {
90 | Log.e("chao", "room full");
91 | });
92 | socket.on("join", args -> {
93 | Log.e("chao", "peer joined");
94 | callback.onPeerJoined();
95 | });
96 | socket.on("joined", args -> {
97 | Log.e("chao", "self joined");
98 | callback.onSelfJoined();
99 | });
100 | socket.on("log", args -> {
101 | Log.e("chao", "log call " + Arrays.toString(args));
102 | });
103 | socket.on("bye", args -> {
104 | Log.e("chao", "bye " + args[0]);
105 | callback.onPeerLeave((String) args[0]);
106 | });
107 | socket.on("message", args -> {
108 | Log.e("chao", "message " + Arrays.toString(args));
109 | Object arg = args[0];
110 | if(arg instanceof String) {
111 |
112 | } else if(arg instanceof JSONObject) {
113 | JSONObject data = (JSONObject) arg;
114 | String type = data.optString("type");
115 | if("offer".equals(type)) {
116 | callback.onOfferReceived(data);
117 | } else if("answer".equals(type)) {
118 | callback.onAnswerReceived(data);
119 | } else if("candidate".equals(type)) {
120 | callback.onIceCandidateReceived(data);
121 | }
122 | }
123 | });
124 |
125 | } catch (NoSuchAlgorithmException e) {
126 | e.printStackTrace();
127 | } catch (KeyManagementException e) {
128 | e.printStackTrace();
129 | } catch (URISyntaxException e) {
130 | e.printStackTrace();
131 | }
132 | }
133 |
134 | public void sendIceCandidate(IceCandidate iceCandidate) {
135 | JSONObject jo = new JSONObject();
136 | try {
137 | jo.put("type", "candidate");
138 | jo.put("label", iceCandidate.sdpMLineIndex);
139 | jo.put("id", iceCandidate.sdpMid);
140 | jo.put("candidate", iceCandidate.sdp);
141 |
142 | socket.emit("message", jo);
143 | } catch (JSONException e) {
144 | e.printStackTrace();
145 | }
146 | }
147 |
148 | public void sendSessionDescription(SessionDescription sdp) {
149 | JSONObject jo = new JSONObject();
150 | try {
151 | jo.put("type", sdp.type.canonicalForm());
152 | jo.put("sdp", sdp.description);
153 |
154 | socket.emit("message", jo);
155 | } catch (JSONException e) {
156 | e.printStackTrace();
157 | }
158 | }
159 |
160 | public interface Callback {
161 | void onCreateRoom();
162 | void onPeerJoined();
163 | void onSelfJoined();
164 | void onPeerLeave(String msg);
165 |
166 | void onOfferReceived(JSONObject data);
167 | void onAnswerReceived(JSONObject data);
168 | void onIceCandidateReceived(JSONObject data);
169 | }
170 |
171 | }
172 |
--------------------------------------------------------------------------------
/step1camera/src/main/res/drawable/ic_launcher_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
10 |
15 |
20 |
25 |
30 |
35 |
40 |
45 |
50 |
55 |
60 |
65 |
70 |
75 |
80 |
85 |
90 |
95 |
100 |
105 |
110 |
115 |
120 |
125 |
130 |
135 |
140 |
145 |
150 |
155 |
160 |
165 |
170 |
171 |
--------------------------------------------------------------------------------
/step5opengl/src/main/res/drawable/ic_launcher_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
10 |
15 |
20 |
25 |
30 |
35 |
40 |
45 |
50 |
55 |
60 |
65 |
70 |
75 |
80 |
85 |
90 |
95 |
100 |
105 |
110 |
115 |
120 |
125 |
130 |
135 |
140 |
145 |
150 |
155 |
160 |
165 |
170 |
171 |
--------------------------------------------------------------------------------
/step2loopback/src/main/res/drawable/ic_launcher_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
10 |
15 |
20 |
25 |
30 |
35 |
40 |
45 |
50 |
55 |
60 |
65 |
70 |
75 |
80 |
85 |
90 |
95 |
100 |
105 |
110 |
115 |
120 |
125 |
130 |
135 |
140 |
145 |
150 |
155 |
160 |
165 |
170 |
171 |
--------------------------------------------------------------------------------
/step3signaling/src/main/res/drawable/ic_launcher_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
10 |
15 |
20 |
25 |
30 |
35 |
40 |
45 |
50 |
55 |
60 |
65 |
70 |
75 |
80 |
85 |
90 |
95 |
100 |
105 |
110 |
115 |
120 |
125 |
130 |
135 |
140 |
145 |
150 |
155 |
160 |
165 |
170 |
171 |
--------------------------------------------------------------------------------
/step4multipeers/src/main/res/drawable/ic_launcher_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
10 |
15 |
20 |
25 |
30 |
35 |
40 |
45 |
50 |
55 |
60 |
65 |
70 |
75 |
80 |
85 |
90 |
95 |
100 |
105 |
110 |
115 |
120 |
125 |
130 |
135 |
140 |
145 |
150 |
155 |
160 |
165 |
170 |
171 |
--------------------------------------------------------------------------------
/step4multipeers/src/main/java/cc/rome753/wat/SignalingClient.java:
--------------------------------------------------------------------------------
1 | package cc.rome753.wat;
2 |
3 | import android.telecom.Call;
4 | import android.util.Log;
5 |
6 | import org.json.JSONException;
7 | import org.json.JSONObject;
8 | import org.webrtc.IceCandidate;
9 | import org.webrtc.SessionDescription;
10 |
11 | import java.net.URISyntaxException;
12 | import java.security.KeyManagementException;
13 | import java.security.NoSuchAlgorithmException;
14 | import java.security.cert.CertificateException;
15 | import java.security.cert.X509Certificate;
16 | import java.util.Arrays;
17 |
18 | import javax.net.ssl.SSLContext;
19 | import javax.net.ssl.TrustManager;
20 | import javax.net.ssl.X509TrustManager;
21 |
22 | import io.socket.client.IO;
23 | import io.socket.client.Socket;
24 |
25 | /**
26 | * Created by chao on 2019/1/30.
27 | */
28 |
29 | public class SignalingClient {
30 |
31 | private static SignalingClient instance;
32 | private SignalingClient(){}
33 | public static SignalingClient get() {
34 | if(instance == null) {
35 | synchronized (SignalingClient.class) {
36 | if(instance == null) {
37 | instance = new SignalingClient();
38 | }
39 | }
40 | }
41 | return instance;
42 | }
43 |
44 | private Socket socket;
45 | private String room = "OldPlace";
46 | private Callback callback;
47 |
48 | private final TrustManager[] trustAll = new TrustManager[]{
49 | new X509TrustManager() {
50 | @Override
51 | public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
52 |
53 | }
54 |
55 | @Override
56 | public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
57 |
58 | }
59 |
60 | @Override
61 | public X509Certificate[] getAcceptedIssuers() {
62 | return new X509Certificate[0];
63 | }
64 | }
65 | };
66 |
67 | public void init(Callback callback) {
68 | this.callback = callback;
69 | try {
70 | SSLContext sslContext = SSLContext.getInstance("TLS");
71 | sslContext.init(null, trustAll, null);
72 | IO.setDefaultHostnameVerifier((hostname, session) -> true);
73 | IO.setDefaultSSLContext(sslContext);
74 |
75 | socket = IO.socket("https://192.168.1.97:8080");
76 | socket.connect();
77 |
78 | socket.emit("create or join", room);
79 |
80 | socket.on("created", args -> {
81 | Log.e("chao", "room created:" + socket.id());
82 | callback.onCreateRoom();
83 | });
84 | socket.on("full", args -> {
85 | Log.e("chao", "room full");
86 | });
87 | socket.on("join", args -> {
88 | Log.e("chao", "peer joined " + Arrays.toString(args));
89 | callback.onPeerJoined(String.valueOf(args[1]));
90 | });
91 | socket.on("joined", args -> {
92 | Log.e("chao", "self joined:" + socket.id());
93 | callback.onSelfJoined();
94 | });
95 | socket.on("log", args -> {
96 | Log.e("chao", "log call " + Arrays.toString(args));
97 | });
98 | socket.on("bye", args -> {
99 | Log.e("chao", "bye " + args[0]);
100 | callback.onPeerLeave((String) args[0]);
101 | });
102 | socket.on("message", args -> {
103 | Log.e("chao", "message " + Arrays.toString(args));
104 | Object arg = args[0];
105 | if(arg instanceof String) {
106 |
107 | } else if(arg instanceof JSONObject) {
108 | JSONObject data = (JSONObject) arg;
109 | String type = data.optString("type");
110 | if("offer".equals(type)) {
111 | callback.onOfferReceived(data);
112 | } else if("answer".equals(type)) {
113 | callback.onAnswerReceived(data);
114 | } else if("candidate".equals(type)) {
115 | callback.onIceCandidateReceived(data);
116 | }
117 | }
118 | });
119 | } catch (NoSuchAlgorithmException e) {
120 | e.printStackTrace();
121 | } catch (KeyManagementException e) {
122 | e.printStackTrace();
123 | } catch (URISyntaxException e) {
124 | e.printStackTrace();
125 | }
126 | }
127 |
128 | public void destroy() {
129 | socket.emit("bye", socket.id());
130 | socket.disconnect();
131 | socket.close();
132 | instance = null;
133 | }
134 |
135 | public void sendIceCandidate(IceCandidate iceCandidate, String to) {
136 | JSONObject jo = new JSONObject();
137 | try {
138 | jo.put("type", "candidate");
139 | jo.put("label", iceCandidate.sdpMLineIndex);
140 | jo.put("id", iceCandidate.sdpMid);
141 | jo.put("candidate", iceCandidate.sdp);
142 | jo.put("from", socket.id());
143 | jo.put("to", to);
144 |
145 | socket.emit("message", jo);
146 | } catch (JSONException e) {
147 | e.printStackTrace();
148 | }
149 | }
150 |
151 | public void sendSessionDescription(SessionDescription sdp, String to) {
152 | JSONObject jo = new JSONObject();
153 | try {
154 | jo.put("type", sdp.type.canonicalForm());
155 | jo.put("sdp", sdp.description);
156 | jo.put("from", socket.id());
157 | jo.put("to", to);
158 | socket.emit("message", jo);
159 | } catch (JSONException e) {
160 | e.printStackTrace();
161 | }
162 | }
163 |
164 | public interface Callback {
165 | void onCreateRoom();
166 | void onPeerJoined(String socketId);
167 | void onSelfJoined();
168 | void onPeerLeave(String msg);
169 |
170 | void onOfferReceived(JSONObject data);
171 | void onAnswerReceived(JSONObject data);
172 | void onIceCandidateReceived(JSONObject data);
173 | }
174 |
175 | }
176 |
--------------------------------------------------------------------------------
/step3web/js/main.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var isChannelReady = false;
4 | var isInitiator = false;
5 | var isStarted = false;
6 | var localStream;
7 | var pc;
8 | var remoteStream;
9 | var turnReady;
10 |
11 | var pcConfig = {
12 | 'iceServers': [{
13 | 'urls': 'stun:stun.l.google.com:19302'
14 | }]
15 | };
16 |
17 | // Set up audio and video regardless of what devices are present.
18 | var sdpConstraints = {
19 | offerToReceiveAudio: true,
20 | offerToReceiveVideo: true
21 | };
22 |
23 | /////////////////////////////////////////////
24 |
25 | var room = 'OldPlace';
26 | // Could prompt for room name:
27 | // room = prompt('Enter room name:');
28 |
29 | var socket = io.connect();
30 |
31 | if (room !== '') {
32 | socket.emit('create or join', room);
33 | console.log('Attempted to create or join room', room);
34 | }
35 |
36 | socket.on('created', function(room) {
37 | console.log('Created room ' + room);
38 | isInitiator = true;
39 | });
40 |
41 | socket.on('full', function(room) {
42 | console.log('Room ' + room + ' is full');
43 | });
44 |
45 | socket.on('join', function (room){
46 | console.log('Another peer made a request to join room ' + room);
47 | console.log('This peer is the initiator of room ' + room + '!');
48 | isChannelReady = true;
49 | });
50 |
51 | socket.on('joined', function(room) {
52 | console.log('joined: ' + room);
53 | isChannelReady = true;
54 | });
55 |
56 | socket.on('log', function(array) {
57 | console.log.apply(console, array);
58 | });
59 |
60 | ////////////////////////////////////////////////
61 |
62 | function sendMessage(message) {
63 | console.log('Client sending message: ', message);
64 | socket.emit('message', message);
65 | }
66 |
67 | // This client receives a message
68 | socket.on('message', function(message) {
69 | console.log('Client received message:', message);
70 | if (message === 'got user media') {
71 | maybeStart();
72 | } else if (message.type === 'offer') {
73 | if (!isInitiator && !isStarted) {
74 | maybeStart();
75 | }
76 | pc.setRemoteDescription(new RTCSessionDescription(message));
77 | doAnswer();
78 | } else if (message.type === 'answer' && isStarted) {
79 | pc.setRemoteDescription(new RTCSessionDescription(message));
80 | } else if (message.type === 'candidate' && isStarted) {
81 | var candidate = new RTCIceCandidate({
82 | sdpMLineIndex: message.label,
83 | candidate: message.candidate
84 | });
85 | pc.addIceCandidate(candidate);
86 | } else if (message === 'bye' && isStarted) {
87 | handleRemoteHangup();
88 | }
89 | });
90 |
91 | ////////////////////////////////////////////////////
92 |
93 | var localVideo = document.querySelector('#localVideo');
94 | var remoteVideo = document.querySelector('#remoteVideo');
95 |
96 | navigator.mediaDevices.getUserMedia({
97 | audio: false,
98 | video: true
99 | })
100 | .then(gotStream)
101 | .catch(function(e) {
102 | alert('getUserMedia() err: ' + e.name + ' ' + e.message);
103 | });
104 |
105 | function gotStream(stream) {
106 | console.log('Adding local stream.');
107 | localStream = stream;
108 | localVideo.srcObject = stream;
109 | sendMessage('got user media');
110 | if (isInitiator) {
111 | maybeStart();
112 | }
113 | }
114 |
115 | var constraints = {
116 | video: true
117 | };
118 |
119 | console.log('Getting user media with constraints', constraints);
120 |
121 | if (location.hostname !== 'localhost') {
122 | requestTurn(
123 | 'https://computeengineondemand.appspot.com/turn?username=41784574&key=4080218913'
124 | );
125 | }
126 |
127 | function maybeStart() {
128 | console.log('>>>>>>> maybeStart() ', isStarted, localStream, isChannelReady);
129 | if (!isStarted && typeof localStream !== 'undefined' && isChannelReady) {
130 | console.log('>>>>>> creating peer connection');
131 | createPeerConnection();
132 | pc.addStream(localStream);
133 | isStarted = true;
134 | console.log('isInitiator', isInitiator);
135 | if (isInitiator) {
136 | doCall();
137 | }
138 | }
139 | }
140 |
141 | window.onbeforeunload = function() {
142 | sendMessage('bye');
143 | };
144 |
145 | /////////////////////////////////////////////////////////
146 |
147 | function createPeerConnection() {
148 | try {
149 | pc = new RTCPeerConnection(null);
150 | pc.onicecandidate = handleIceCandidate;
151 | pc.onaddstream = handleRemoteStreamAdded;
152 | pc.onremovestream = handleRemoteStreamRemoved;
153 | console.log('Created RTCPeerConnnection');
154 | } catch (e) {
155 | console.log('Failed to create PeerConnection, exception: ' + e.message);
156 | alert('Cannot create RTCPeerConnection object.');
157 | return;
158 | }
159 | }
160 |
161 | function handleIceCandidate(event) {
162 | console.log('icecandidate event: ', event);
163 | if (event.candidate) {
164 | sendMessage({
165 | type: 'candidate',
166 | label: event.candidate.sdpMLineIndex,
167 | id: event.candidate.sdpMid,
168 | candidate: event.candidate.candidate
169 | });
170 | } else {
171 | console.log('End of candidates.');
172 | }
173 | }
174 |
175 | function handleCreateOfferError(event) {
176 | console.log('createOffer() error: ', event);
177 | }
178 |
179 | function doCall() {
180 | console.log('Sending offer to peer');
181 | pc.createOffer(setLocalAndSendMessage, handleCreateOfferError);
182 | }
183 |
184 | function doAnswer() {
185 | console.log('Sending answer to peer.');
186 | pc.createAnswer().then(
187 | setLocalAndSendMessage,
188 | onCreateSessionDescriptionError
189 | );
190 | }
191 |
192 | function setLocalAndSendMessage(sessionDescription) {
193 | pc.setLocalDescription(sessionDescription);
194 | console.log('setLocalAndSendMessage sending message', sessionDescription);
195 | sendMessage(sessionDescription);
196 | }
197 |
198 | function onCreateSessionDescriptionError(error) {
199 | trace('Failed to create session description: ' + error.toString());
200 | }
201 |
202 | function requestTurn(turnURL) {
203 | var turnExists = false;
204 | for (var i in pcConfig.iceServers) {
205 | if (pcConfig.iceServers[i].urls.substr(0, 5) === 'turn:') {
206 | turnExists = true;
207 | turnReady = true;
208 | break;
209 | }
210 | }
211 | if (!turnExists) {
212 | console.log('Getting TURN server from ', turnURL);
213 | // No TURN server. Get one from computeengineondemand.appspot.com:
214 | var xhr = new XMLHttpRequest();
215 | xhr.onreadystatechange = function() {
216 | if (xhr.readyState === 4 && xhr.status === 200) {
217 | var turnServer = JSON.parse(xhr.responseText);
218 | console.log('Got TURN server: ', turnServer);
219 | pcConfig.iceServers.push({
220 | 'urls': 'turn:' + turnServer.username + '@' + turnServer.turn,
221 | 'credential': turnServer.password
222 | });
223 | turnReady = true;
224 | }
225 | };
226 | xhr.open('GET', turnURL, true);
227 | xhr.send();
228 | }
229 | }
230 |
231 | function handleRemoteStreamAdded(event) {
232 | console.log('Remote stream added.');
233 | remoteStream = event.stream;
234 | remoteVideo.srcObject = remoteStream;
235 | }
236 |
237 | function handleRemoteStreamRemoved(event) {
238 | console.log('Remote stream removed. Event: ', event);
239 | }
240 |
241 | function hangup() {
242 | console.log('Hanging up.');
243 | stop();
244 | sendMessage('bye');
245 | }
246 |
247 | function handleRemoteHangup() {
248 | console.log('Session terminated.');
249 | stop();
250 | isInitiator = false;
251 | }
252 |
253 | function stop() {
254 | isStarted = false;
255 | pc.close();
256 | pc = null;
257 | }
258 |
--------------------------------------------------------------------------------
/step4web/js/main.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var isChannelReady = false;
4 | var isInitiator = false;
5 | var isStarted = false;
6 | var localStream;
7 | var pc;
8 | var remoteStream;
9 | var turnReady;
10 |
11 | var pcConfig = {
12 | 'iceServers': [{
13 | 'urls': 'stun:stun.l.google.com:19302'
14 | }]
15 | };
16 |
17 | // Set up audio and video regardless of what devices are present.
18 | var sdpConstraints = {
19 | offerToReceiveAudio: true,
20 | offerToReceiveVideo: true
21 | };
22 |
23 | /////////////////////////////////////////////
24 |
25 | var room = 'OldPlace';
26 | // Could prompt for room name:
27 | // room = prompt('Enter room name:');
28 |
29 | var socket = io.connect();
30 |
31 | if (room !== '') {
32 | socket.emit('create or join', room);
33 | console.log('Attempted to create or join room', room);
34 | }
35 |
36 | socket.on('created', function(room) {
37 | console.log('Created room ' + room);
38 | isInitiator = true;
39 | });
40 |
41 | socket.on('full', function(room) {
42 | console.log('Room ' + room + ' is full');
43 | });
44 |
45 | socket.on('join', function (room){
46 | console.log('Another peer made a request to join room ' + room);
47 | console.log('This peer is the initiator of room ' + room + '!');
48 | isChannelReady = true;
49 | });
50 |
51 | socket.on('joined', function(room) {
52 | console.log('joined: ' + room);
53 | isChannelReady = true;
54 | });
55 |
56 | socket.on('log', function(array) {
57 | console.log.apply(console, array);
58 | });
59 |
60 | ////////////////////////////////////////////////
61 |
62 | function sendMessage(message) {
63 | console.log('Client sending message: ', message);
64 | socket.emit('message', message);
65 | }
66 |
67 | // This client receives a message
68 | socket.on('message', function(message) {
69 | console.log('Client received message:', message);
70 | if (message === 'got user media') {
71 | maybeStart();
72 | } else if (message.type === 'offer') {
73 | if (!isInitiator && !isStarted) {
74 | maybeStart();
75 | }
76 | pc.setRemoteDescription(new RTCSessionDescription(message));
77 | doAnswer();
78 | } else if (message.type === 'answer' && isStarted) {
79 | pc.setRemoteDescription(new RTCSessionDescription(message));
80 | } else if (message.type === 'candidate' && isStarted) {
81 | var candidate = new RTCIceCandidate({
82 | sdpMLineIndex: message.label,
83 | candidate: message.candidate
84 | });
85 | pc.addIceCandidate(candidate);
86 | } else if (message === 'bye' && isStarted) {
87 | handleRemoteHangup();
88 | }
89 | });
90 |
91 | ////////////////////////////////////////////////////
92 |
93 | var localVideo = document.querySelector('#localVideo');
94 | var remoteVideo = document.querySelector('#remoteVideo');
95 |
96 | navigator.mediaDevices.getUserMedia({
97 | audio: false,
98 | video: true
99 | })
100 | .then(gotStream)
101 | .catch(function(e) {
102 | alert('getUserMedia() err: ' + e.name + ' ' + e.message);
103 | });
104 |
105 | function gotStream(stream) {
106 | console.log('Adding local stream.');
107 | localStream = stream;
108 | localVideo.srcObject = stream;
109 | sendMessage('got user media');
110 | if (isInitiator) {
111 | maybeStart();
112 | }
113 | }
114 |
115 | var constraints = {
116 | video: true
117 | };
118 |
119 | console.log('Getting user media with constraints', constraints);
120 |
121 | if (location.hostname !== 'localhost') {
122 | requestTurn(
123 | 'https://computeengineondemand.appspot.com/turn?username=41784574&key=4080218913'
124 | );
125 | }
126 |
127 | function maybeStart() {
128 | console.log('>>>>>>> maybeStart() ', isStarted, localStream, isChannelReady);
129 | if (!isStarted && typeof localStream !== 'undefined' && isChannelReady) {
130 | console.log('>>>>>> creating peer connection');
131 | createPeerConnection();
132 | pc.addStream(localStream);
133 | isStarted = true;
134 | console.log('isInitiator', isInitiator);
135 | if (isInitiator) {
136 | doCall();
137 | }
138 | }
139 | }
140 |
141 | window.onbeforeunload = function() {
142 | sendMessage('bye');
143 | };
144 |
145 | /////////////////////////////////////////////////////////
146 |
147 | function createPeerConnection() {
148 | try {
149 | pc = new RTCPeerConnection(null);
150 | pc.onicecandidate = handleIceCandidate;
151 | pc.onaddstream = handleRemoteStreamAdded;
152 | pc.onremovestream = handleRemoteStreamRemoved;
153 | console.log('Created RTCPeerConnnection');
154 | } catch (e) {
155 | console.log('Failed to create PeerConnection, exception: ' + e.message);
156 | alert('Cannot create RTCPeerConnection object.');
157 | return;
158 | }
159 | }
160 |
161 | function handleIceCandidate(event) {
162 | console.log('icecandidate event: ', event);
163 | if (event.candidate) {
164 | sendMessage({
165 | type: 'candidate',
166 | label: event.candidate.sdpMLineIndex,
167 | id: event.candidate.sdpMid,
168 | candidate: event.candidate.candidate
169 | });
170 | } else {
171 | console.log('End of candidates.');
172 | }
173 | }
174 |
175 | function handleCreateOfferError(event) {
176 | console.log('createOffer() error: ', event);
177 | }
178 |
179 | function doCall() {
180 | console.log('Sending offer to peer');
181 | pc.createOffer(setLocalAndSendMessage, handleCreateOfferError);
182 | }
183 |
184 | function doAnswer() {
185 | console.log('Sending answer to peer.');
186 | pc.createAnswer().then(
187 | setLocalAndSendMessage,
188 | onCreateSessionDescriptionError
189 | );
190 | }
191 |
192 | function setLocalAndSendMessage(sessionDescription) {
193 | pc.setLocalDescription(sessionDescription);
194 | console.log('setLocalAndSendMessage sending message', sessionDescription);
195 | sendMessage(sessionDescription);
196 | }
197 |
198 | function onCreateSessionDescriptionError(error) {
199 | trace('Failed to create session description: ' + error.toString());
200 | }
201 |
202 | function requestTurn(turnURL) {
203 | var turnExists = false;
204 | for (var i in pcConfig.iceServers) {
205 | if (pcConfig.iceServers[i].urls.substr(0, 5) === 'turn:') {
206 | turnExists = true;
207 | turnReady = true;
208 | break;
209 | }
210 | }
211 | if (!turnExists) {
212 | console.log('Getting TURN server from ', turnURL);
213 | // No TURN server. Get one from computeengineondemand.appspot.com:
214 | var xhr = new XMLHttpRequest();
215 | xhr.onreadystatechange = function() {
216 | if (xhr.readyState === 4 && xhr.status === 200) {
217 | var turnServer = JSON.parse(xhr.responseText);
218 | console.log('Got TURN server: ', turnServer);
219 | pcConfig.iceServers.push({
220 | 'urls': 'turn:' + turnServer.username + '@' + turnServer.turn,
221 | 'credential': turnServer.password
222 | });
223 | turnReady = true;
224 | }
225 | };
226 | xhr.open('GET', turnURL, true);
227 | xhr.send();
228 | }
229 | }
230 |
231 | function handleRemoteStreamAdded(event) {
232 | console.log('Remote stream added.');
233 | remoteStream = event.stream;
234 | remoteVideo.srcObject = remoteStream;
235 | }
236 |
237 | function handleRemoteStreamRemoved(event) {
238 | console.log('Remote stream removed. Event: ', event);
239 | }
240 |
241 | function hangup() {
242 | console.log('Hanging up.');
243 | stop();
244 | sendMessage('bye');
245 | }
246 |
247 | function handleRemoteHangup() {
248 | console.log('Session terminated.');
249 | stop();
250 | isInitiator = false;
251 | }
252 |
253 | function stop() {
254 | isStarted = false;
255 | pc.close();
256 | pc = null;
257 | }
258 |
--------------------------------------------------------------------------------
/step3signaling/src/main/java/cc/rome753/wat/MainActivity.java:
--------------------------------------------------------------------------------
1 | package cc.rome753.wat;
2 |
3 | import android.os.Bundle;
4 | import androidx.appcompat.app.AppCompatActivity;
5 |
6 | import org.json.JSONObject;
7 | import org.webrtc.AudioSource;
8 | import org.webrtc.AudioTrack;
9 | import org.webrtc.Camera1Enumerator;
10 | import org.webrtc.DefaultVideoDecoderFactory;
11 | import org.webrtc.DefaultVideoEncoderFactory;
12 | import org.webrtc.EglBase;
13 | import org.webrtc.IceCandidate;
14 | import org.webrtc.MediaConstraints;
15 | import org.webrtc.MediaStream;
16 | import org.webrtc.PeerConnection;
17 | import org.webrtc.PeerConnectionFactory;
18 | import org.webrtc.SessionDescription;
19 | import org.webrtc.SurfaceTextureHelper;
20 | import org.webrtc.SurfaceViewRenderer;
21 | import org.webrtc.VideoCapturer;
22 | import org.webrtc.VideoSource;
23 | import org.webrtc.VideoTrack;
24 |
25 | import java.util.ArrayList;
26 | import java.util.List;
27 |
28 | public class MainActivity extends AppCompatActivity implements SignalingClient.Callback {
29 |
30 | PeerConnectionFactory peerConnectionFactory;
31 | PeerConnection peerConnection;
32 | SurfaceViewRenderer localView;
33 | SurfaceViewRenderer remoteView;
34 | MediaStream mediaStream;
35 |
36 | @Override
37 | protected void onCreate(Bundle savedInstanceState) {
38 | super.onCreate(savedInstanceState);
39 | setContentView(R.layout.activity_main);
40 | EglBase.Context eglBaseContext = EglBase.create().getEglBaseContext();
41 |
42 | // create PeerConnectionFactory
43 | PeerConnectionFactory.initialize(PeerConnectionFactory.InitializationOptions
44 | .builder(this)
45 | .createInitializationOptions());
46 | PeerConnectionFactory.Options options = new PeerConnectionFactory.Options();
47 | DefaultVideoEncoderFactory defaultVideoEncoderFactory =
48 | new DefaultVideoEncoderFactory(eglBaseContext, true, true);
49 | DefaultVideoDecoderFactory defaultVideoDecoderFactory =
50 | new DefaultVideoDecoderFactory(eglBaseContext);
51 | peerConnectionFactory = PeerConnectionFactory.builder()
52 | .setOptions(options)
53 | .setVideoEncoderFactory(defaultVideoEncoderFactory)
54 | .setVideoDecoderFactory(defaultVideoDecoderFactory)
55 | .createPeerConnectionFactory();
56 |
57 | SurfaceTextureHelper surfaceTextureHelper = SurfaceTextureHelper.create("CaptureThread", eglBaseContext);
58 | // create VideoCapturer
59 | VideoCapturer videoCapturer = createCameraCapturer(true);
60 | VideoSource videoSource = peerConnectionFactory.createVideoSource(videoCapturer.isScreencast());
61 | videoCapturer.initialize(surfaceTextureHelper, getApplicationContext(), videoSource.getCapturerObserver());
62 | videoCapturer.startCapture(480, 640, 30);
63 |
64 | localView = findViewById(R.id.localView);
65 | localView.setMirror(true);
66 | localView.init(eglBaseContext, null);
67 |
68 | // create VideoTrack
69 | VideoTrack videoTrack = peerConnectionFactory.createVideoTrack("100", videoSource);
70 | // // display in localView
71 | videoTrack.addSink(localView);
72 |
73 |
74 | remoteView = findViewById(R.id.remoteView);
75 | remoteView.setMirror(false);
76 | remoteView.init(eglBaseContext, null);
77 |
78 |
79 | AudioSource audioSource = peerConnectionFactory.createAudioSource(new MediaConstraints());
80 | AudioTrack audioTrack = peerConnectionFactory.createAudioTrack("101", audioSource);
81 |
82 | mediaStream = peerConnectionFactory.createLocalMediaStream("mediaStream");
83 | mediaStream.addTrack(videoTrack);
84 | mediaStream.addTrack(audioTrack);
85 |
86 | SignalingClient.get().setCallback(this);
87 | call();
88 | }
89 |
90 |
91 | private void call() {
92 | List iceServers = new ArrayList<>();
93 | iceServers.add(PeerConnection.IceServer.builder("stun:stun.l.google.com:19302").createIceServer());
94 | peerConnection = peerConnectionFactory.createPeerConnection(iceServers, new PeerConnectionAdapter("localconnection") {
95 | @Override
96 | public void onIceCandidate(IceCandidate iceCandidate) {
97 | super.onIceCandidate(iceCandidate);
98 | SignalingClient.get().sendIceCandidate(iceCandidate);
99 | }
100 |
101 | @Override
102 | public void onAddStream(MediaStream mediaStream) {
103 | super.onAddStream(mediaStream);
104 | VideoTrack remoteVideoTrack = mediaStream.videoTracks.get(0);
105 | runOnUiThread(() -> {
106 | remoteVideoTrack.addSink(remoteView);
107 | });
108 | }
109 | });
110 |
111 | peerConnection.addStream(mediaStream);
112 | }
113 |
114 | private VideoCapturer createCameraCapturer(boolean isFront) {
115 | Camera1Enumerator enumerator = new Camera1Enumerator(false);
116 | final String[] deviceNames = enumerator.getDeviceNames();
117 |
118 | // First, try to find front facing camera
119 | for (String deviceName : deviceNames) {
120 | if (isFront ? enumerator.isFrontFacing(deviceName) : enumerator.isBackFacing(deviceName)) {
121 | VideoCapturer videoCapturer = enumerator.createCapturer(deviceName, null);
122 |
123 | if (videoCapturer != null) {
124 | return videoCapturer;
125 | }
126 | }
127 | }
128 |
129 | return null;
130 | }
131 |
132 | @Override
133 | public void onCreateRoom() {
134 |
135 | }
136 |
137 | @Override
138 | public void onPeerJoined() {
139 |
140 | }
141 |
142 | @Override
143 | public void onSelfJoined() {
144 | peerConnection.createOffer(new SdpAdapter("local offer sdp") {
145 | @Override
146 | public void onCreateSuccess(SessionDescription sessionDescription) {
147 | super.onCreateSuccess(sessionDescription);
148 | peerConnection.setLocalDescription(new SdpAdapter("local set local"), sessionDescription);
149 | SignalingClient.get().sendSessionDescription(sessionDescription);
150 | }
151 | }, new MediaConstraints());
152 | }
153 |
154 | @Override
155 | public void onPeerLeave(String msg) {
156 |
157 | }
158 |
159 | @Override
160 | public void onOfferReceived(JSONObject data) {
161 | runOnUiThread(() -> {
162 | peerConnection.setRemoteDescription(new SdpAdapter("localSetRemote"),
163 | new SessionDescription(SessionDescription.Type.OFFER, data.optString("sdp")));
164 | peerConnection.createAnswer(new SdpAdapter("localAnswerSdp") {
165 | @Override
166 | public void onCreateSuccess(SessionDescription sdp) {
167 | super.onCreateSuccess(sdp);
168 | peerConnection.setLocalDescription(new SdpAdapter("localSetLocal"), sdp);
169 | SignalingClient.get().sendSessionDescription(sdp);
170 | }
171 | }, new MediaConstraints());
172 |
173 | });
174 | }
175 |
176 | @Override
177 | public void onAnswerReceived(JSONObject data) {
178 | peerConnection.setRemoteDescription(new SdpAdapter("localSetRemote"),
179 | new SessionDescription(SessionDescription.Type.ANSWER, data.optString("sdp")));
180 | }
181 |
182 | @Override
183 | public void onIceCandidateReceived(JSONObject data) {
184 | peerConnection.addIceCandidate(new IceCandidate(
185 | data.optString("id"),
186 | data.optInt("label"),
187 | data.optString("candidate")
188 | ));
189 | }
190 | }
191 |
--------------------------------------------------------------------------------
/step2loopback/src/main/java/cc/rome753/wat/MainActivity.java:
--------------------------------------------------------------------------------
1 | package cc.rome753.wat;
2 |
3 | import android.os.Bundle;
4 |
5 | import androidx.appcompat.app.AppCompatActivity;
6 |
7 | import org.webrtc.Camera1Enumerator;
8 | import org.webrtc.DefaultVideoDecoderFactory;
9 | import org.webrtc.DefaultVideoEncoderFactory;
10 | import org.webrtc.EglBase;
11 | import org.webrtc.IceCandidate;
12 | import org.webrtc.MediaConstraints;
13 | import org.webrtc.MediaStream;
14 | import org.webrtc.PeerConnection;
15 | import org.webrtc.PeerConnectionFactory;
16 | import org.webrtc.SessionDescription;
17 | import org.webrtc.SurfaceTextureHelper;
18 | import org.webrtc.SurfaceViewRenderer;
19 | import org.webrtc.VideoCapturer;
20 | import org.webrtc.VideoSource;
21 | import org.webrtc.VideoTrack;
22 |
23 | import java.util.ArrayList;
24 | import java.util.List;
25 |
26 | public class MainActivity extends AppCompatActivity {
27 |
28 | PeerConnectionFactory peerConnectionFactory;
29 | PeerConnection peerConnectionLocal;
30 | PeerConnection peerConnectionRemote;
31 | SurfaceViewRenderer localView;
32 | SurfaceViewRenderer remoteView;
33 | MediaStream mediaStreamLocal;
34 | MediaStream mediaStreamRemote;
35 |
36 | @Override
37 | protected void onCreate(Bundle savedInstanceState) {
38 | super.onCreate(savedInstanceState);
39 | setContentView(R.layout.activity_main);
40 | EglBase.Context eglBaseContext = EglBase.create().getEglBaseContext();
41 |
42 | // create PeerConnectionFactory
43 | PeerConnectionFactory.initialize(PeerConnectionFactory.InitializationOptions
44 | .builder(this)
45 | .createInitializationOptions());
46 | PeerConnectionFactory.Options options = new PeerConnectionFactory.Options();
47 | DefaultVideoEncoderFactory defaultVideoEncoderFactory =
48 | new DefaultVideoEncoderFactory(eglBaseContext, true, true);
49 | DefaultVideoDecoderFactory defaultVideoDecoderFactory =
50 | new DefaultVideoDecoderFactory(eglBaseContext);
51 | peerConnectionFactory = PeerConnectionFactory.builder()
52 | .setOptions(options)
53 | .setVideoEncoderFactory(defaultVideoEncoderFactory)
54 | .setVideoDecoderFactory(defaultVideoDecoderFactory)
55 | .createPeerConnectionFactory();
56 |
57 | SurfaceTextureHelper surfaceTextureHelper = SurfaceTextureHelper.create("CaptureThread", eglBaseContext);
58 | // create VideoCapturer
59 | VideoCapturer videoCapturer = createCameraCapturer(true);
60 | VideoSource videoSource = peerConnectionFactory.createVideoSource(videoCapturer.isScreencast());
61 | videoCapturer.initialize(surfaceTextureHelper, getApplicationContext(), videoSource.getCapturerObserver());
62 | videoCapturer.startCapture(480, 640, 30);
63 |
64 | localView = findViewById(R.id.localView);
65 | localView.setMirror(true);
66 | localView.init(eglBaseContext, null);
67 |
68 | // create VideoTrack
69 | VideoTrack videoTrack = peerConnectionFactory.createVideoTrack("100", videoSource);
70 | // // display in localView
71 | // videoTrack.addSink(localView);
72 |
73 |
74 |
75 |
76 | SurfaceTextureHelper remoteSurfaceTextureHelper = SurfaceTextureHelper.create("RemoteCaptureThread", eglBaseContext);
77 | // create VideoCapturer
78 | VideoCapturer remoteVideoCapturer = createCameraCapturer(false);
79 | VideoSource remoteVideoSource = peerConnectionFactory.createVideoSource(remoteVideoCapturer.isScreencast());
80 | remoteVideoCapturer.initialize(remoteSurfaceTextureHelper, getApplicationContext(), remoteVideoSource.getCapturerObserver());
81 | remoteVideoCapturer.startCapture(480, 640, 30);
82 |
83 | remoteView = findViewById(R.id.remoteView);
84 | remoteView.setMirror(false);
85 | remoteView.init(eglBaseContext, null);
86 |
87 | // create VideoTrack
88 | VideoTrack remoteVideoTrack = peerConnectionFactory.createVideoTrack("102", remoteVideoSource);
89 | // // display in remoteView
90 | // remoteVideoTrack.addSink(remoteView);
91 |
92 |
93 |
94 | mediaStreamLocal = peerConnectionFactory.createLocalMediaStream("mediaStreamLocal");
95 | mediaStreamLocal.addTrack(videoTrack);
96 |
97 | mediaStreamRemote = peerConnectionFactory.createLocalMediaStream("mediaStreamRemote");
98 | mediaStreamRemote.addTrack(remoteVideoTrack);
99 |
100 | call(mediaStreamLocal, mediaStreamRemote);
101 | }
102 |
103 |
104 | private void call(MediaStream localMediaStream, MediaStream remoteMediaStream) {
105 | List iceServers = new ArrayList<>();
106 | peerConnectionLocal = peerConnectionFactory.createPeerConnection(iceServers, new PeerConnectionAdapter("localconnection") {
107 | @Override
108 | public void onIceCandidate(IceCandidate iceCandidate) {
109 | super.onIceCandidate(iceCandidate);
110 | peerConnectionRemote.addIceCandidate(iceCandidate);
111 | }
112 |
113 | @Override
114 | public void onAddStream(MediaStream mediaStream) {
115 | super.onAddStream(mediaStream);
116 | VideoTrack remoteVideoTrack = mediaStream.videoTracks.get(0);
117 | runOnUiThread(() -> {
118 | remoteVideoTrack.addSink(localView);
119 | });
120 | }
121 | });
122 |
123 | peerConnectionRemote = peerConnectionFactory.createPeerConnection(iceServers, new PeerConnectionAdapter("remoteconnection") {
124 | @Override
125 | public void onIceCandidate(IceCandidate iceCandidate) {
126 | super.onIceCandidate(iceCandidate);
127 | peerConnectionLocal.addIceCandidate(iceCandidate);
128 | }
129 |
130 | @Override
131 | public void onAddStream(MediaStream mediaStream) {
132 | super.onAddStream(mediaStream);
133 | VideoTrack localVideoTrack = mediaStream.videoTracks.get(0);
134 | runOnUiThread(() -> {
135 | localVideoTrack.addSink(remoteView);
136 | });
137 | }
138 | });
139 |
140 | peerConnectionLocal.addStream(localMediaStream);
141 | peerConnectionLocal.createOffer(new SdpAdapter("local offer sdp") {
142 | @Override
143 | public void onCreateSuccess(SessionDescription sessionDescription) {
144 | super.onCreateSuccess(sessionDescription);
145 | // todo crashed here
146 | peerConnectionLocal.setLocalDescription(new SdpAdapter("local set local"), sessionDescription);
147 | peerConnectionRemote.addStream(remoteMediaStream);
148 | peerConnectionRemote.setRemoteDescription(new SdpAdapter("remote set remote"), sessionDescription);
149 | peerConnectionRemote.createAnswer(new SdpAdapter("remote answer sdp") {
150 | @Override
151 | public void onCreateSuccess(SessionDescription sdp) {
152 | super.onCreateSuccess(sdp);
153 | peerConnectionRemote.setLocalDescription(new SdpAdapter("remote set local"), sdp);
154 | peerConnectionLocal.setRemoteDescription(new SdpAdapter("local set remote"), sdp);
155 | }
156 | }, new MediaConstraints());
157 | }
158 | }, new MediaConstraints());
159 | }
160 |
161 | private VideoCapturer createCameraCapturer(boolean isFront) {
162 | Camera1Enumerator enumerator = new Camera1Enumerator(false);
163 | final String[] deviceNames = enumerator.getDeviceNames();
164 |
165 | // First, try to find front facing camera
166 | for (String deviceName : deviceNames) {
167 | if (isFront ? enumerator.isFrontFacing(deviceName) : enumerator.isBackFacing(deviceName)) {
168 | VideoCapturer videoCapturer = enumerator.createCapturer(deviceName, null);
169 |
170 | if (videoCapturer != null) {
171 | return videoCapturer;
172 | }
173 | }
174 | }
175 |
176 | return null;
177 | }
178 |
179 | }
180 |
--------------------------------------------------------------------------------
/step4multipeers/src/main/java/cc/rome753/wat/MainActivity.java:
--------------------------------------------------------------------------------
1 | package cc.rome753.wat;
2 |
3 | import android.os.Bundle;
4 | import androidx.appcompat.app.AppCompatActivity;
5 |
6 | import org.json.JSONObject;
7 | import org.webrtc.Camera1Enumerator;
8 | import org.webrtc.DefaultVideoDecoderFactory;
9 | import org.webrtc.DefaultVideoEncoderFactory;
10 | import org.webrtc.EglBase;
11 | import org.webrtc.IceCandidate;
12 | import org.webrtc.MediaConstraints;
13 | import org.webrtc.MediaStream;
14 | import org.webrtc.PeerConnection;
15 | import org.webrtc.PeerConnectionFactory;
16 | import org.webrtc.SdpObserver;
17 | import org.webrtc.SessionDescription;
18 | import org.webrtc.SurfaceTextureHelper;
19 | import org.webrtc.SurfaceViewRenderer;
20 | import org.webrtc.VideoCapturer;
21 | import org.webrtc.VideoSource;
22 | import org.webrtc.VideoTrack;
23 |
24 | import java.util.ArrayList;
25 | import java.util.HashMap;
26 | import java.util.List;
27 |
28 | public class MainActivity extends AppCompatActivity implements SignalingClient.Callback {
29 |
30 | EglBase.Context eglBaseContext;
31 | PeerConnectionFactory peerConnectionFactory;
32 | SurfaceViewRenderer localView;
33 | MediaStream mediaStream;
34 | List iceServers;
35 |
36 | HashMap peerConnectionMap;
37 | SurfaceViewRenderer[] remoteViews;
38 | int remoteViewsIndex = 0;
39 |
40 | @Override
41 | protected void onCreate(Bundle savedInstanceState) {
42 | super.onCreate(savedInstanceState);
43 | setContentView(R.layout.activity_main);
44 |
45 | peerConnectionMap = new HashMap<>();
46 | iceServers = new ArrayList<>();
47 | iceServers.add(PeerConnection.IceServer.builder("stun:stun.l.google.com:19302").createIceServer());
48 |
49 | eglBaseContext = EglBase.create().getEglBaseContext();
50 |
51 | // create PeerConnectionFactory
52 | PeerConnectionFactory.initialize(PeerConnectionFactory.InitializationOptions
53 | .builder(this)
54 | .createInitializationOptions());
55 | PeerConnectionFactory.Options options = new PeerConnectionFactory.Options();
56 | DefaultVideoEncoderFactory defaultVideoEncoderFactory =
57 | new DefaultVideoEncoderFactory(eglBaseContext, true, true);
58 | DefaultVideoDecoderFactory defaultVideoDecoderFactory =
59 | new DefaultVideoDecoderFactory(eglBaseContext);
60 | peerConnectionFactory = PeerConnectionFactory.builder()
61 | .setOptions(options)
62 | .setVideoEncoderFactory(defaultVideoEncoderFactory)
63 | .setVideoDecoderFactory(defaultVideoDecoderFactory)
64 | .createPeerConnectionFactory();
65 |
66 | SurfaceTextureHelper surfaceTextureHelper = SurfaceTextureHelper.create("CaptureThread", eglBaseContext);
67 | // create VideoCapturer
68 | VideoCapturer videoCapturer = createCameraCapturer(true);
69 | VideoSource videoSource = peerConnectionFactory.createVideoSource(videoCapturer.isScreencast());
70 | videoCapturer.initialize(surfaceTextureHelper, getApplicationContext(), videoSource.getCapturerObserver());
71 | videoCapturer.startCapture(480, 640, 30);
72 |
73 | localView = findViewById(R.id.localView);
74 | localView.setMirror(true);
75 | localView.init(eglBaseContext, null);
76 |
77 | // create VideoTrack
78 | VideoTrack videoTrack = peerConnectionFactory.createVideoTrack("100", videoSource);
79 | // // display in localView
80 | videoTrack.addSink(localView);
81 |
82 |
83 |
84 | remoteViews = new SurfaceViewRenderer[]{
85 | findViewById(R.id.remoteView),
86 | findViewById(R.id.remoteView2),
87 | findViewById(R.id.remoteView3),
88 | };
89 | for(SurfaceViewRenderer remoteView : remoteViews) {
90 | remoteView.setMirror(false);
91 | remoteView.init(eglBaseContext, null);
92 | }
93 |
94 |
95 | mediaStream = peerConnectionFactory.createLocalMediaStream("mediaStream");
96 | mediaStream.addTrack(videoTrack);
97 |
98 | SignalingClient.get().init(this);
99 | }
100 |
101 |
102 | private synchronized PeerConnection getOrCreatePeerConnection(String socketId) {
103 | PeerConnection peerConnection = peerConnectionMap.get(socketId);
104 | if(peerConnection != null) {
105 | return peerConnection;
106 | }
107 | peerConnection = peerConnectionFactory.createPeerConnection(iceServers, new PeerConnectionAdapter("PC:" + socketId) {
108 | @Override
109 | public void onIceCandidate(IceCandidate iceCandidate) {
110 | super.onIceCandidate(iceCandidate);
111 | SignalingClient.get().sendIceCandidate(iceCandidate, socketId);
112 | }
113 |
114 | @Override
115 | public void onAddStream(MediaStream mediaStream) {
116 | super.onAddStream(mediaStream);
117 | VideoTrack remoteVideoTrack = mediaStream.videoTracks.get(0);
118 | runOnUiThread(() -> {
119 | remoteVideoTrack.addSink(remoteViews[remoteViewsIndex++]);
120 | });
121 | }
122 | });
123 | peerConnection.addStream(mediaStream);
124 | peerConnectionMap.put(socketId, peerConnection);
125 | return peerConnection;
126 | }
127 |
128 | @Override
129 | public void onCreateRoom() {
130 |
131 | }
132 |
133 | @Override
134 | public void onPeerJoined(String socketId) {
135 | PeerConnection peerConnection = getOrCreatePeerConnection(socketId);
136 | peerConnection.createOffer(new SdpAdapter("createOfferSdp:" + socketId) {
137 | @Override
138 | public void onCreateSuccess(SessionDescription sessionDescription) {
139 | super.onCreateSuccess(sessionDescription);
140 | peerConnection.setLocalDescription(new SdpAdapter("setLocalSdp:" + socketId), sessionDescription);
141 | SignalingClient.get().sendSessionDescription(sessionDescription, socketId);
142 | }
143 | }, new MediaConstraints());
144 | }
145 |
146 | @Override
147 | public void onSelfJoined() {
148 |
149 | }
150 |
151 | @Override
152 | public void onPeerLeave(String msg) {
153 |
154 | }
155 |
156 | @Override
157 | public void onOfferReceived(JSONObject data) {
158 | runOnUiThread(() -> {
159 | final String socketId = data.optString("from");
160 | PeerConnection peerConnection = getOrCreatePeerConnection(socketId);
161 | peerConnection.setRemoteDescription(new SdpAdapter("setRemoteSdp:" + socketId),
162 | new SessionDescription(SessionDescription.Type.OFFER, data.optString("sdp")));
163 | peerConnection.createAnswer(new SdpAdapter("localAnswerSdp") {
164 | @Override
165 | public void onCreateSuccess(SessionDescription sdp) {
166 | super.onCreateSuccess(sdp);
167 | peerConnectionMap.get(socketId).setLocalDescription(new SdpAdapter("setLocalSdp:" + socketId), sdp);
168 | SignalingClient.get().sendSessionDescription(sdp, socketId);
169 | }
170 | }, new MediaConstraints());
171 |
172 | });
173 | }
174 |
175 | @Override
176 | public void onAnswerReceived(JSONObject data) {
177 | String socketId = data.optString("from");
178 | PeerConnection peerConnection = getOrCreatePeerConnection(socketId);
179 | peerConnection.setRemoteDescription(new SdpAdapter("setRemoteSdp:" + socketId),
180 | new SessionDescription(SessionDescription.Type.ANSWER, data.optString("sdp")));
181 | }
182 |
183 | @Override
184 | public void onIceCandidateReceived(JSONObject data) {
185 | String socketId = data.optString("from");
186 | PeerConnection peerConnection = getOrCreatePeerConnection(socketId);
187 | peerConnection.addIceCandidate(new IceCandidate(
188 | data.optString("id"),
189 | data.optInt("label"),
190 | data.optString("candidate")
191 | ));
192 | }
193 |
194 | @Override
195 | protected void onDestroy() {
196 | super.onDestroy();
197 | SignalingClient.get().destroy();
198 | }
199 |
200 | private VideoCapturer createCameraCapturer(boolean isFront) {
201 | Camera1Enumerator enumerator = new Camera1Enumerator(false);
202 | final String[] deviceNames = enumerator.getDeviceNames();
203 |
204 | // First, try to find front facing camera
205 | for (String deviceName : deviceNames) {
206 | if (isFront ? enumerator.isFrontFacing(deviceName) : enumerator.isBackFacing(deviceName)) {
207 | VideoCapturer videoCapturer = enumerator.createCapturer(deviceName, null);
208 |
209 | if (videoCapturer != null) {
210 | return videoCapturer;
211 | }
212 | }
213 | }
214 |
215 | return null;
216 | }
217 | }
218 |
--------------------------------------------------------------------------------