Skip to content

Commit 6898ee8

Browse files
YPandasYuqi Huang
andauthored
Release v1.2.0 (#123)
* WebRTC ingestion with multi-viewer support (#115) * feat: enable ingest media for viewers with audio-only restriction - Remove master-only restriction for ingest media functionality - Add validation to prevent viewers from using ingest media with video - Implement storage session support for viewers (audio-only) - Update WebRTC activity to skip video track creation for storage viewers * Update WebRtcActivity with current version - Replace WebRtcActivity.java with current working version - remove the fallback solution of null clientID * adding verbose logs back and remove overrides that only call super * changing visibility of KEY_SEND_VIDEO variable * refactor: extract storage client methods and update master validation logic - Extract storage client configuration into separate methods in WebRtcActivity - Add helper methods for better code clarity - Remove mandatory video requirement for masters in non-storage scenarios - Update viewer validation message for multi-viewer participants * feat: Add H.264 codec validation for WebRTC storage sessions - Show AlertDialog when H.264 encoder missing for master with ingest media - Show AlertDialog when H.264 decoder missing for storage session viewers - Add codecValidationFailed flag to prevent WebRTC initialization after validation failure - Add null check for audioManager in onDestroy() to prevent crash on early exit * feat: Add codec logging and final parameter to WebRtcActivity - Restore codec logging in onCreate() to show all available encoders/decoders for debugging - Add final modifier --------- Co-authored-by: Yuqi Huang <yuuqih@amazon.com> * feat: Add error dialog for storage session join failures (#117) - Show AlertDialog when JoinStorageSession API fails - Close WebRTC activity when user dismisses error dialog Co-authored-by: Yuqi Huang <yuuqih@amazon.com> * endpoint and credential configuration through .env file (#116) * Add .env file support for AWS credentials with production warnings • Load AWS credentials from .env file via BuildConfig • Skip authentication flow when env credentials present • Disable logout functionality for env credential users • Add production warning message for env credential usage • Maintain fallback to Cognito authentication when no env file * Add custom endpoint configuration via .env file • Load CUSTOM_ENDPOINT from .env file via BuildConfig * format improvement * changing variable name(CUSTOM_ENDPOINT --> CONTROL_PLANE_URI) * Add CustomCredentialsProvider to use AWS credentials from .env file * refactor: centralize AWS credentials validation logic - Make hasEnvCredentials() public static in KinesisVideoWebRtcDemoApp - Remove duplicate hasEnvSetting() method from SimpleNavActivity - Replace inline credential validation in StartUpActivity --------- Co-authored-by: Yuqi Huang <yuuqih@amazon.com> * chore: upgrade AWS Android SDK from 2.75.0 to 2.81.1 (#118) Co-authored-by: Yuqi Huang <yuuqih@amazon.com> * feat: conditionally hide UI elements based on user settings (#119) - Hide data channel input when ingest media is enabled - Hide local video view when send video is disabled Co-authored-by: Yuqi Huang <yuuqih@amazon.com> * updated version from 1.1.0 to 1.2.0 (#120) Co-authored-by: Yuqi Huang <yuuqih@amazon.com> * update ci.yml to add develop branch to the ci target (#122) Co-authored-by: Yuqi Huang <yuuqih@amazon.com> * Updated Readme.md for WebRTC ingestion support (#121) Co-authored-by: Yuqi Huang <yuuqih@amazon.com> --------- Co-authored-by: Yuqi Huang <yuuqih@amazon.com>
1 parent af60b41 commit 6898ee8

9 files changed

Lines changed: 430 additions & 129 deletions

File tree

.github/workflows/ci.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,11 @@ on:
44
push:
55
branches:
66
- master
7+
- develop
78
pull_request:
89
branches:
910
- master
11+
- develop
1012
jobs:
1113
build:
1214
strategy:

README.md

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,33 @@ Once login is successful, you will entering the following channel information to
8989
* Start the web browser using the [Javascript SDK](https://github.com/awslabs/amazon-kinesis-video-streams-webrtc-sdk-js) and start it as `viewer`.
9090
* Verify media showing up from the Android device to the browser.
9191

92-
## 6. ICE Candidate Trickling
92+
## 6. WebRTC Ingestion support
93+
94+
The sample application demonstrates how to connect to the KVS WebRTC storage session as a master or viewer participant.
95+
96+
For more information about WebRTC ingestion, see https://docs.aws.amazon.com/kinesisvideostreams-webrtc-dg/latest/devguide/webrtc-ingestion.html.
97+
98+
### 6.1 Setting up signaling channel and stream
99+
100+
Follow the instructions below to:
101+
1. Create the signaling channel: https://docs.aws.amazon.com/kinesisvideostreams-webrtc-dg/latest/devguide/ingestion-create-channel.html
102+
2. Create the stream: https://docs.aws.amazon.com/kinesisvideostreams-webrtc-dg/latest/devguide/ingestion-create-stream.html
103+
3. Link the signaling channel and stream: https://docs.aws.amazon.com/kinesisvideostreams-webrtc-dg/latest/devguide/configure-ingestion.html
104+
4. Configure the IAM role with the correct permissions: https://docs.aws.amazon.com/kinesisvideostreams-webrtc-dg/latest/devguide/ingestion-grant-permission.html
105+
106+
### 6.2 Prerequisites
107+
108+
* Ensure H.264 and OPUS codec support is available on the device
109+
110+
Usage:
111+
1. Master with Ingest Media: Check the "Ingest Media" option and ensure both "Send Audio" and "Send Video" are selected. The master will stream audio and video to the KVS WebRTC storage session, who forwards it to all connected viewer participants and the configured Kinesis Video stream.
112+
113+
2. Viewer with Ingest Media: Check the "Ingest Media" option but only select "Send Audio" (video must be deselected). Multiple viewer participants (at most three) can connect to the same channel and their optional audio will be incorporated into the Kinesis Video stream, forwarded to the master participant, and forwarded to all other viewer participants.
114+
115+
> [!NOTE]
116+
> Android emulators may not have H.264 encoding support. A pop-up error will appear if the device you're using does not have H.264 encoding support for master participants or H.264 decoding support for viewer participants.
117+
118+
## 7. ICE Candidate Trickling
93119

94120
Candidate trickling is a technique through which a caller may incrementally provide candidates to the callee after the initial offer has been dispatched; the semantics of "Trickle ICE" are defined in [RFC8838].
95121

@@ -100,7 +126,7 @@ However, in the case that it needs to be disabled, locate the RTCConfiguration p
100126
PeerConnection.RTCConfiguration rtcConfig = new PeerConnection.RTCConfiguration();
101127
rtcConfig.continualGatheringPolicy = PeerConnection.ContinualGatheringPolicy.GATHER_CONTINUALLY;
102128
```
103-
## 7. Testing
129+
## 8. Testing
104130
This SDK has been tested with Java 11, 17 to build the Gradle dependencies and Java 8, 11, and, 17 in the compile options in build.gradle
105131

106132
```agsl

app/build.gradle

Lines changed: 34 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,40 @@ android {
1313
minSdk 29
1414
targetSdk 33
1515
versionCode 1
16-
versionName "1.1.0"
16+
versionName "1.2.0"
1717

1818
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
19+
20+
// Load .env file and set BuildConfig fields
21+
def envFile = rootProject.file('.env')
22+
def envFields = [:]
23+
if (envFile.exists()) {
24+
envFile.readLines().each {
25+
def (key, value) = it.tokenize('=')
26+
if (key && value) {
27+
envFields[key] = value
28+
buildConfigField "String", key, "\"${value}\""
29+
}
30+
}
31+
}
32+
33+
// Set default null values for missing fields
34+
if (!envFields.containsKey('CONTROL_PLANE_URI')) {
35+
buildConfigField "String", "CONTROL_PLANE_URI", "null"
36+
}
37+
if (!envFields.containsKey('AWS_ACCESS_KEY_ID')) {
38+
buildConfigField "String", "AWS_ACCESS_KEY_ID", "null"
39+
}
40+
if (!envFields.containsKey('AWS_SECRET_ACCESS_KEY')) {
41+
buildConfigField "String", "AWS_SECRET_ACCESS_KEY", "null"
42+
}
43+
if (!envFields.containsKey('AWS_SESSION_TOKEN')) {
44+
buildConfigField "String", "AWS_SESSION_TOKEN", "null"
45+
}
46+
}
47+
48+
buildFeatures {
49+
buildConfig true
1950
}
2051

2152
buildTypes {
@@ -34,7 +65,7 @@ android {
3465
dependencies {
3566
implementation fileTree(dir: 'libs', include: ['*.jar', '*.aar'])
3667

37-
def aws_version = '2.75.0'
68+
def aws_version = '2.81.1'
3869
implementation("com.amazonaws:aws-android-sdk-kinesisvideo:$aws_version@aar") { transitive = true }
3970
implementation("com.amazonaws:aws-android-sdk-kinesisvideo-signaling:$aws_version@aar") { transitive = true }
4071
implementation("com.amazonaws:aws-android-sdk-kinesisvideo-webrtcstorage:$aws_version@aar") { transitive = true }
@@ -91,4 +122,4 @@ tasks.register('checkForLibWebRTC') {
91122
}
92123
}
93124

94-
preBuild.dependsOn checkForLibWebRTC
125+
preBuild.dependsOn checkForLibWebRTC

app/src/main/java/com/amazonaws/kinesisvideo/demoapp/KinesisVideoWebRtcDemoApp.java

Lines changed: 72 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,10 @@
33
import android.app.Application;
44
import android.util.Log;
55

6+
import com.amazonaws.auth.AWSCredentials;
67
import com.amazonaws.auth.AWSCredentialsProvider;
8+
import com.amazonaws.auth.BasicAWSCredentials;
9+
import com.amazonaws.auth.BasicSessionCredentials;
710
import com.amazonaws.mobile.client.AWSMobileClient;
811
import com.amazonaws.mobile.config.AWSConfiguration;
912

@@ -14,30 +17,89 @@ public class KinesisVideoWebRtcDemoApp extends Application {
1417
private static final String TAG = KinesisVideoWebRtcDemoApp.class.getSimpleName();
1518

1619
public static AWSCredentialsProvider getCredentialsProvider() {
20+
// Check if custom credentials are available from .env
21+
if (hasEnvCredentials()) {
22+
return new CustomCredentialsProvider();
23+
}
1724
return AWSMobileClient.getInstance();
1825
}
1926

27+
public static boolean hasEnvCredentials() {
28+
try {
29+
String accessKeyId = BuildConfig.AWS_ACCESS_KEY_ID;
30+
String secretAccessKey = BuildConfig.AWS_SECRET_ACCESS_KEY;
31+
return accessKeyId != null && !accessKeyId.isEmpty() && !"null".equals(accessKeyId) &&
32+
secretAccessKey != null && !secretAccessKey.isEmpty() && !"null".equals(secretAccessKey);
33+
} catch (Exception e) {
34+
return false;
35+
}
36+
}
37+
38+
private static class CustomCredentialsProvider implements AWSCredentialsProvider {
39+
@Override
40+
public AWSCredentials getCredentials() {
41+
try {
42+
String accessKeyId = BuildConfig.AWS_ACCESS_KEY_ID;
43+
String secretAccessKey = BuildConfig.AWS_SECRET_ACCESS_KEY;
44+
String sessionToken = BuildConfig.AWS_SESSION_TOKEN;
45+
46+
if (!hasEnvCredentials()) {
47+
throw new RuntimeException("AWS credentials not available from .env");
48+
}
49+
50+
if (sessionToken != null && !sessionToken.isEmpty() && !"null".equals(sessionToken)) {
51+
return new BasicSessionCredentials(accessKeyId, secretAccessKey, sessionToken);
52+
} else {
53+
return new BasicAWSCredentials(accessKeyId, secretAccessKey);
54+
}
55+
} catch (Exception e) {
56+
throw new RuntimeException("Failed to get credentials from .env: " + e.getMessage(), e);
57+
}
58+
}
59+
60+
@Override
61+
public void refresh() {
62+
// No-op for static credentials
63+
}
64+
}
65+
2066
/**
2167
* Parse awsconfiguration.json and extract the region from it.
68+
* If using .env credentials, return a null region.
2269
*
2370
* @return The region in String form. {@code null} if not.
2471
* @throws IllegalStateException if awsconfiguration.json is not properly configured.
2572
*/
2673
public static String getRegion() {
27-
final AWSConfiguration configuration = AWSMobileClient.getInstance().getConfiguration();
28-
if (configuration == null) {
29-
throw new IllegalStateException("awsconfiguration.json has not been properly configured!");
74+
// If using .env credentials, return null (user will manually enter region)
75+
if (hasEnvCredentials()) {
76+
return null;
3077
}
78+
79+
try {
80+
final AWSConfiguration configuration = AWSMobileClient.getInstance().getConfiguration();
81+
if (configuration == null) {
82+
Log.w(TAG, "awsconfiguration.json not found, returning null region");
83+
return null; // Return null instead of throwing exception
84+
}
3185

32-
final JSONObject jsonObject = configuration.optJsonObject("CredentialsProvider");
86+
final JSONObject jsonObject = configuration.optJsonObject("CredentialsProvider");
87+
if (jsonObject == null) {
88+
Log.w(TAG, "CredentialsProvider not found in awsconfiguration.json");
89+
return null;
90+
}
3391

34-
String region = null;
35-
try {
36-
region = (String) ((JSONObject) (((JSONObject) jsonObject.get("CognitoIdentity")).get("Default"))).get("Region");
37-
} catch (final JSONException e) {
38-
Log.e(TAG, "Got exception when extracting region from cognito setting.", e);
92+
String region = null;
93+
try {
94+
region = (String) ((JSONObject) (((JSONObject) jsonObject.get("CognitoIdentity")).get("Default"))).get("Region");
95+
} catch (final JSONException e) {
96+
Log.e(TAG, "Got exception when extracting region from cognito setting.", e);
97+
}
98+
return region;
99+
} catch (Exception e) {
100+
Log.w(TAG, "Failed to get region from awsconfiguration.json: " + e.getMessage());
101+
return null; // Return null on any error
39102
}
40-
return region;
41103
}
42104

43105
}

app/src/main/java/com/amazonaws/kinesisvideo/demoapp/activity/SimpleNavActivity.java

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
import android.util.Log;
66
import android.view.MenuItem;
77

8+
import com.amazonaws.kinesisvideo.demoapp.BuildConfig;
9+
import com.amazonaws.kinesisvideo.demoapp.KinesisVideoWebRtcDemoApp;
810
import androidx.appcompat.app.ActionBarDrawerToggle;
911
import androidx.appcompat.app.AppCompatActivity;
1012
import androidx.appcompat.widget.Toolbar;
@@ -46,6 +48,11 @@ protected void onCreate(Bundle savedInstanceState) {
4648

4749
NavigationView navigationView = findViewById(R.id.nav_view);
4850
navigationView.setNavigationItemSelectedListener(this);
51+
52+
// Disable logout menu item if using credentials from .env file
53+
if (KinesisVideoWebRtcDemoApp.hasEnvCredentials()) {
54+
navigationView.getMenu().findItem(R.id.nav_logout).setEnabled(false);
55+
}
4956

5057
if (savedInstanceState != null) {
5158
streamFragment = getSupportFragmentManager().findFragmentByTag(StreamWebRtcConfigurationFragment.class.getName());
@@ -70,6 +77,11 @@ public boolean onNavigationItemSelected(MenuItem item) {
7077
int id = item.getItemId();
7178

7279
if (id == R.id.nav_logout) {
80+
if (KinesisVideoWebRtcDemoApp.hasEnvCredentials()) {
81+
Log.i(TAG, "Logout disabled when using credentials from .env file");
82+
return true;
83+
}
84+
7385
AWSMobileClient.getInstance().signOut();
7486
AWSMobileClient.getInstance().showSignIn(this,
7587
SignInUIOptions.builder()
@@ -111,4 +123,6 @@ public void startConfigFragment() {
111123
e.printStackTrace();
112124
}
113125
}
114-
}
126+
127+
128+
}

app/src/main/java/com/amazonaws/kinesisvideo/demoapp/activity/StartUpActivity.java

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88

99
import androidx.appcompat.app.AppCompatActivity;
1010

11+
import com.amazonaws.kinesisvideo.demoapp.BuildConfig;
12+
import com.amazonaws.kinesisvideo.demoapp.KinesisVideoWebRtcDemoApp;
1113
import com.amazonaws.kinesisvideo.demoapp.R;
1214
import com.amazonaws.kinesisvideo.demoapp.util.ActivityUtils;
1315
import com.amazonaws.mobile.client.AWSMobileClient;
@@ -31,7 +33,16 @@ protected void onCreate(Bundle savedInstanceState) {
3133
supportFinishAfterTransition();
3234

3335
AsyncTask.execute(() -> {
34-
if (auth.isSignedIn()) {
36+
// Check if custom credentials are available in .env
37+
boolean hasEnvSetting = KinesisVideoWebRtcDemoApp.hasEnvCredentials();
38+
39+
if (hasEnvSetting || auth.isSignedIn()) {
40+
Log.i(TAG, hasEnvSetting ? "Using credentials from environmental seetting" : "User already signed in");
41+
42+
if (hasEnvSetting) {
43+
showCredentialsWarning();
44+
}
45+
3546
ActivityUtils.startActivity(thisActivity, SimpleNavActivity.class);
3647
} else {
3748
auth.showSignIn(thisActivity,
@@ -79,4 +90,8 @@ public void onError(Exception e) {
7990
e.printStackTrace();
8091
}
8192
}
82-
}
93+
94+
private void showCredentialsWarning() {
95+
Log.w(TAG, "WARNING: Using environment settings - please follow standard AWS recommended practices for production (https://docs.aws.amazon.com/cognito/)");
96+
}
97+
}

0 commit comments

Comments
 (0)