├── .gitattributes
├── NOTICE
├── lint-baseline.xml
├── app
├── libs
│ └── instructions.txt
├── src
│ ├── main
│ │ ├── res
│ │ │ ├── mipmap-hdpi
│ │ │ │ ├── ic_launcher.png
│ │ │ │ ├── ic_launcher_round.png
│ │ │ │ ├── kinesisvideo_logo.png
│ │ │ │ └── ic_launcher_foreground.png
│ │ │ ├── mipmap-mdpi
│ │ │ │ ├── ic_launcher.png
│ │ │ │ ├── ic_launcher_round.png
│ │ │ │ └── ic_launcher_foreground.png
│ │ │ ├── mipmap-xhdpi
│ │ │ │ ├── ic_launcher.png
│ │ │ │ ├── ic_launcher_round.png
│ │ │ │ └── ic_launcher_foreground.png
│ │ │ ├── values
│ │ │ │ ├── ic_launcher_background.xml
│ │ │ │ ├── dimens.xml
│ │ │ │ ├── colors.xml
│ │ │ │ ├── strings.xml
│ │ │ │ ├── themes.xml
│ │ │ │ └── styles.xml
│ │ │ ├── mipmap-xxhdpi
│ │ │ │ ├── ic_launcher.png
│ │ │ │ ├── ic_launcher_round.png
│ │ │ │ └── ic_launcher_foreground.png
│ │ │ ├── mipmap-xxxhdpi
│ │ │ │ ├── ic_launcher.png
│ │ │ │ ├── ic_launcher_round.png
│ │ │ │ └── ic_launcher_foreground.png
│ │ │ ├── xml
│ │ │ │ ├── backup_descriptor.xml
│ │ │ │ ├── backup_rules.xml
│ │ │ │ └── data_extraction_rules.xml
│ │ │ ├── mipmap-anydpi-v26
│ │ │ │ ├── ic_launcher.xml
│ │ │ │ └── ic_launcher_round.xml
│ │ │ ├── drawable
│ │ │ │ ├── side_nav_bar.xml
│ │ │ │ ├── button_default.xml
│ │ │ │ ├── button_pressed.xml
│ │ │ │ ├── button_selector.xml
│ │ │ │ └── ic_menu_share.xml
│ │ │ ├── values-w820dp
│ │ │ │ └── dimens.xml
│ │ │ ├── menu
│ │ │ │ └── activity_simple_nav_drawer.xml
│ │ │ ├── raw
│ │ │ │ └── awsconfiguration.json
│ │ │ ├── layout
│ │ │ │ ├── content_simple_nav.xml
│ │ │ │ ├── activity_simple_nav.xml
│ │ │ │ ├── app_bar_simple_nav.xml
│ │ │ │ ├── nav_header_simple_nav.xml
│ │ │ │ ├── activity_webrtc_main.xml
│ │ │ │ └── fragment_stream_webrtc_configuration.xml
│ │ │ └── values-night
│ │ │ │ └── themes.xml
│ │ ├── java
│ │ │ └── com
│ │ │ │ └── amazonaws
│ │ │ │ └── kinesisvideo
│ │ │ │ ├── signaling
│ │ │ │ ├── Signaling.java
│ │ │ │ ├── SignalingListener.java
│ │ │ │ ├── okhttp
│ │ │ │ │ ├── SignalingServiceWebSocketClient.java
│ │ │ │ │ └── WebSocketClient.java
│ │ │ │ └── model
│ │ │ │ │ ├── Message.java
│ │ │ │ │ └── Event.java
│ │ │ │ ├── utils
│ │ │ │ ├── Constants.java
│ │ │ │ ├── AwsV4SignerConstants.java
│ │ │ │ └── AwsV4Signer.java
│ │ │ │ ├── webrtc
│ │ │ │ ├── KinesisVideoSdpObserver.java
│ │ │ │ └── KinesisVideoPeerConnection.java
│ │ │ │ └── demoapp
│ │ │ │ ├── util
│ │ │ │ └── ActivityUtils.java
│ │ │ │ ├── activity
│ │ │ │ ├── StartUpActivity.java
│ │ │ │ └── SimpleNavActivity.java
│ │ │ │ ├── KinesisVideoWebRtcDemoApp.java
│ │ │ │ └── fragment
│ │ │ │ └── StreamWebRtcConfigurationFragment.java
│ │ └── AndroidManifest.xml
│ ├── test
│ │ └── java
│ │ │ └── com
│ │ │ └── amazonaws
│ │ │ └── kinesisvideo
│ │ │ ├── demoapp
│ │ │ └── ExampleUnitTest.java
│ │ │ ├── signaling
│ │ │ └── model
│ │ │ │ └── MessageTest.java
│ │ │ └── utils
│ │ │ └── AwsV4SignerTest.java
│ └── androidTest
│ │ └── java
│ │ └── com
│ │ └── amazonaws
│ │ └── kinesisvideo
│ │ └── demoapp
│ │ └── ExampleInstrumentedTest.java
├── proguard-rules.pro
└── build.gradle
├── RELEASE_NOTES.md
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── CODE_OF_CONDUCT.md
├── .gitignore
├── settings.gradle
├── .github
└── workflows
│ └── ci.yml
├── gradle.properties
├── THIRD-PARTY-LICENSES.txt
├── gradlew.bat
├── CONTRIBUTING.md
├── gradlew
├── README.md
└── LICENSE
/.gitattributes:
--------------------------------------------------------------------------------
1 | *.aar binary
2 | *.jar binary
3 | gradlew* binary
--------------------------------------------------------------------------------
/NOTICE:
--------------------------------------------------------------------------------
1 | Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
2 |
--------------------------------------------------------------------------------
/lint-baseline.xml:
--------------------------------------------------------------------------------
1 |
2 |
38 | * Connect as Master URL - GetSignalingChannelEndpoint (master role) + Query Parameters: Channel ARN as X-Amz-ChannelARN 39 | * Additional info 40 | *
41 | * Connect as Viewer URL - GetSignalingChannelEndpoint (viewer role) + Query Parameters: Channel ARN as X-Amz-ChannelARN & Client Id as X-Amz-ClientId 42 | * Additional info 43 | *
44 | * Viewer URL example: {@literal wss://v-a1b2c3d4.kinesisvideo.us-west-2.amazonaws.com?X-Amz-ChannelARN=arn:aws:kinesisvideo:us-west-2:123456789012:channel/demo-channel/1234567890123&X-Amz-ClientId=d7d1c6e2-9cb0-4d61-bea9-ecb3d3816557} 45 | *
46 | * Note: The Signaling Channel Endpoints are different depending on the role (master/viewer) specified in GetSignalingChannelEndpoint API call.
47 | * Additional info
48 | * @param accessKey AWS Access Key Id.
49 | * @param secretKey AWS Secret Key.
50 | * @param sessionToken AWS Session Token, if applicable. Otherwise, can be {@code null} or an empty String ({@code ""}).
51 | * @param wssUri Same as URL to sign, excluding query parameters.
52 | * @param region AWS region. Example: us-west-2.
53 | * @param dateMilli Date at which this request to be signed. Milliseconds since epoch.
54 | * @return Presigned WebSocket URL you can use to connect to Kinesis Video Signaling.
55 | * @see Kinesis Video Streams WebRTC Websocket APIs
56 | */
57 | public static URI sign(final URI uri, final String accessKey, final String secretKey,
58 | final String sessionToken, final URI wssUri, final String region,
59 | final long dateMilli) {
60 | // Step 1. Create canonical request.
61 | final String amzDate = getTimeStamp(dateMilli);
62 | final String datestamp = getDateStamp(dateMilli);
63 | final Map
95 | * The query parameters that are included in this map are:
96 | *
173 | * The format of the full scope is as follows:
174 | * {@code
192 | * An example canonical request looks like the following:
193 | *
203 | * The format of the canonical request are as follows:
204 | *
251 | * Here is an explanation of each line.
252 | *
260 | * An example of a string to sign looks like the following:
261 | *
308 | * The formula is as follows:
309 | *
340 | * For example if the date and time was "08/01/2016 15:32:41.982-700"
341 | * then it must first be converted to UTC (Coordinated Universal Time)
342 | * and then submitted as "20160801T223241Z".
343 | *
344 | * @param dateMilli The milliseconds since epoch at which this request is to be signed at.
345 | * Must be the same as the one passed to {@link #getDateStamp(long)} to avoid
346 | * signing issues at midnight UTC.
347 | * @return The date string, formatted to the ISO 8601 standard.
348 | */
349 | static String getTimeStamp(final long dateMilli) {
350 | return DateUtils.format(AwsV4SignerConstants.TIME_PATTERN, new Date(dateMilli));
351 | }
352 |
353 | /**
354 | * Returns the date in yyyyMMdd format.
355 | *
356 | * For example if the date and time was "08/01/2016 15:32:41.982-700"
357 | * then it must first be converted to UTC (Coordinated Universal Time)
358 | * and "20160801" will be returned.
359 | *
360 | * @param dateMilli The milliseconds since epoch at which this request is to be signed at.
361 | * Must be the same as the one passed to {@link #getTimeStamp(long)} to avoid
362 | * signing issues at midnight UTC.
363 | * @return The date string, without the current time.
364 | */
365 | static String getDateStamp(final long dateMilli) {
366 | return DateUtils.format(AwsV4SignerConstants.DATE_PATTERN, new Date(dateMilli));
367 | }
368 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/amazonaws/kinesisvideo/demoapp/fragment/StreamWebRtcConfigurationFragment.java:
--------------------------------------------------------------------------------
1 | package com.amazonaws.kinesisvideo.demoapp.fragment;
2 |
3 | import android.Manifest;
4 | import com.amazonaws.kinesisvideo.demoapp.BuildConfig;
5 | import android.content.Intent;
6 | import android.content.pm.PackageManager;
7 | import android.os.AsyncTask;
8 | import android.os.Bundle;
9 | import android.util.Log;
10 | import android.util.SparseBooleanArray;
11 | import android.view.LayoutInflater;
12 | import android.view.View;
13 | import android.view.ViewGroup;
14 | import android.widget.ArrayAdapter;
15 | import android.widget.Button;
16 | import android.widget.CheckBox;
17 | import android.widget.CheckedTextView;
18 | import android.widget.EditText;
19 | import android.widget.ListView;
20 | import android.widget.Spinner;
21 |
22 | import androidx.annotation.NonNull;
23 | import androidx.annotation.Nullable;
24 | import androidx.appcompat.app.AlertDialog;
25 | import androidx.core.app.ActivityCompat;
26 | import androidx.core.content.ContextCompat;
27 | import androidx.fragment.app.Fragment;
28 | import android.content.Context;
29 |
30 | import com.amazonaws.kinesisvideo.demoapp.KinesisVideoWebRtcDemoApp;
31 | import com.amazonaws.kinesisvideo.demoapp.R;
32 | import com.amazonaws.kinesisvideo.demoapp.activity.SimpleNavActivity;
33 | import com.amazonaws.kinesisvideo.demoapp.activity.WebRtcActivity;
34 | import com.amazonaws.regions.Region;
35 | import com.amazonaws.services.kinesisvideo.AWSKinesisVideoClient;
36 | import com.amazonaws.services.kinesisvideo.model.ChannelRole;
37 | import com.amazonaws.services.kinesisvideo.model.CreateSignalingChannelRequest;
38 | import com.amazonaws.services.kinesisvideo.model.CreateSignalingChannelResult;
39 | import com.amazonaws.services.kinesisvideo.model.DescribeMediaStorageConfigurationRequest;
40 | import com.amazonaws.services.kinesisvideo.model.DescribeMediaStorageConfigurationResult;
41 | import com.amazonaws.services.kinesisvideo.model.DescribeSignalingChannelRequest;
42 | import com.amazonaws.services.kinesisvideo.model.DescribeSignalingChannelResult;
43 | import com.amazonaws.services.kinesisvideo.model.GetSignalingChannelEndpointRequest;
44 | import com.amazonaws.services.kinesisvideo.model.GetSignalingChannelEndpointResult;
45 | import com.amazonaws.services.kinesisvideo.model.ResourceEndpointListItem;
46 | import com.amazonaws.services.kinesisvideo.model.ResourceNotFoundException;
47 | import com.amazonaws.services.kinesisvideo.model.SingleMasterChannelEndpointConfiguration;
48 | import com.amazonaws.services.kinesisvideosignaling.AWSKinesisVideoSignalingClient;
49 | import com.amazonaws.services.kinesisvideosignaling.model.GetIceServerConfigRequest;
50 | import com.amazonaws.services.kinesisvideosignaling.model.GetIceServerConfigResult;
51 | import com.amazonaws.services.kinesisvideosignaling.model.IceServer;
52 |
53 |
54 | import java.lang.ref.WeakReference;
55 | import java.util.ArrayList;
56 | import java.util.Arrays;
57 | import java.util.List;
58 |
59 | public class StreamWebRtcConfigurationFragment extends Fragment {
60 | private static final String TAG = StreamWebRtcConfigurationFragment.class.getSimpleName();
61 |
62 | private static final String KEY_CHANNEL_NAME = "channelName";
63 | public static final String KEY_CLIENT_ID = "clientId";
64 | public static final String KEY_REGION = "region";
65 | public static final String KEY_CHANNEL_ARN = "channelArn";
66 | public static final String KEY_STREAM_ARN = "streamArn";
67 | public static final String KEY_WSS_ENDPOINT = "wssEndpoint";
68 | public static final String KEY_WEBRTC_ENDPOINT = "webrtcEndpoint";
69 | public static final String KEY_IS_MASTER = "isMaster";
70 | public static final String KEY_ICE_SERVER_USER_NAME = "iceServerUserName";
71 | public static final String KEY_ICE_SERVER_PASSWORD = "iceServerPassword";
72 | public static final String KEY_ICE_SERVER_TTL = "iceServerTTL";
73 | public static final String KEY_ICE_SERVER_URI = "iceServerUri";
74 | public static final String KEY_CAMERA_FRONT_FACING = "cameraFrontFacing";
75 |
76 | public static final String KEY_SEND_VIDEO = "sendVideo";
77 | public static final String KEY_SEND_AUDIO = "sendAudio";
78 |
79 | private static final String[] WEBRTC_OPTIONS = {
80 | "Send Video",
81 | "Send Audio",
82 | };
83 |
84 | private static final String[] KEY_OF_OPTIONS = {
85 | KEY_SEND_VIDEO,
86 | KEY_SEND_AUDIO,
87 | };
88 |
89 |
90 | private EditText mChannelName;
91 | private EditText mClientId;
92 | private EditText mRegion;
93 | private Spinner mCameras;
94 | private CheckBox mIngestMedia;
95 | private final List
380 | * The task returns {@code null} upon success, otherwise, it returns an error message.
381 | */
382 | static class UpdateSignalingChannelInfoTask extends AsyncTask
97 | *
105 | *
106 | * @param uri URL to sign.
107 | * @param accessKey AWS Access Key Id.
108 | * @param sessionToken AWS Session Token. Can be null or an empty string if non-temporary credentials are used.
109 | * @param region AWS region. Example: us-west-2.
110 | * @param amzDate The result of {@link #getTimeStamp(long)}.
111 | * @param datestamp The result of {@link #getDateStamp(long)}.
112 | * @return Map of the query parameters to be included.
113 | * @see Authenticating Requests: Using Query Parameters (AWS Signature Version 4)
114 | */
115 | static Map
194 | * GET
195 | * /
196 | * X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-ChannelARN=arn%3Aaws%3Akinesisvideo%3Aus-west-2%3A123456789012%3Achannel%2Fdemo-channel%2F1234567890123&X-Amz-ClientId=d7d1c6e2-9cb0-4d61-bea9-ecb3d3816557&X-Amz-Credential=AKIAIOSFODNN7EXAMPLE%2F20230718%2Fus-west-2%2Fkinesisvideo%2Faws4_request&X-Amz-Date=20230718T191301Z&X-Amz-Expires=299&X-Amz-SignedHeaders=host
197 | * host:v-1a2b3c4d.kinesisvideo.us-west-2.amazonaws.com
198 | *
199 | * host
200 | * e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
201 | *
202 | *
205 | *
212 | *
213 | * @param uri The URL to sign. For example, {@code wss://v-a1b2c3d4.kinesisvideo.us-west-2.amazonaws.com?
214 | * X-Amz-ChannelARN=arn:aws:kinesisvideo:us-west-2:123456789012:channel/demo-channel/1234567890123&X-Amz-ClientId=d7d1c6e2-9cb0-4d61-bea9-ecb3d3816557}.
215 | * @param canonicalQuerystring The Canonical Query String to use in the Canonical Request. Sorted list of query
216 | * parameters (and their values), excluding X-Amz-Signature, already URL-encoded.
217 | * @return The fully-constructed canonical request.
218 | * @see URL encoding
219 | * @see Canonical request specification
220 | */
221 | static String getCanonicalRequest(final URI uri, final String canonicalQuerystring) {
222 | final String payloadHash = sha256().hashString(EMPTY, UTF_8).toString();
223 | final String canonicalUri = getCanonicalUri(uri);
224 | final String canonicalHeaders = "host:" + uri.getHost() + AwsV4SignerConstants.NEW_LINE_DELIMITER;
225 |
226 | return new StringJoiner(AwsV4SignerConstants.NEW_LINE_DELIMITER)
227 | .add(AwsV4SignerConstants.METHOD)
228 | .add(canonicalUri)
229 | .add(canonicalQuerystring)
230 | .add(canonicalHeaders)
231 | .add(AwsV4SignerConstants.SIGNED_HEADERS)
232 | .add(payloadHash)
233 | .toString();
234 | }
235 |
236 | static String getCanonicalUri(final URI uri) {
237 | return Optional.of(uri.getPath())
238 | .filter(s -> !isEmpty(s))
239 | .orElse("/");
240 | }
241 |
242 | /**
243 | * Returns the following string. Each line (except the last one) is followed by a newline character.
244 | *
245 | * Algorithm
246 | * RequestDateTime
247 | * CredentialScope
248 | * HashedCanonicalRequest
249 | *
250 | *
253 | *
259 | *
262 | * AWS4-HMAC-SHA256
263 | * 20150830T123600Z
264 | * AKIDEXAMPLE/20150830/us-west-2/kinesisvideo/aws4_request
265 | * 816cd5b414d056048ba4f7c5386d6e0533120fb1fcfa93762cf0fc39e2cf19e0
266 | *
267 | *
268 | * @param amzDate The result of {@link #getTimeStamp(long)}.
269 | * @param credentialScope the result of {@link #createCredentialScope(String, String)}.
270 | * @param canonicalRequest the result of {@link #getCanonicalRequest(URI, String)}.
271 | * @return The string to sign.
272 | * @see String to sign
273 | */
274 | static String signString(final String amzDate, final String credentialScope, final String canonicalRequest) {
275 | return new StringJoiner(AwsV4SignerConstants.NEW_LINE_DELIMITER)
276 | .add(AwsV4SignerConstants.ALGORITHM_AWS4_HMAC_SHA_256)
277 | .add(amzDate)
278 | .add(credentialScope)
279 | .add(sha256().hashString(canonicalRequest, UTF_8).toString())
280 | .toString();
281 | }
282 |
283 | static String urlEncode(final String str) {
284 | try {
285 | return URLEncoder.encode(str, UTF_8.name());
286 | } catch (final Exception e) {
287 | throw new IllegalArgumentException(e.getMessage(), e);
288 | }
289 | }
290 |
291 | // https://docs.aws.amazon.com/general/latest/gr/signature-v4-examples.html#signature-v4-examples-java
292 | static byte[] hmacSha256(final String data, final byte[] key) {
293 | final String algorithm = "HmacSHA256";
294 | final Mac mac;
295 | try {
296 | mac = Mac.getInstance(algorithm);
297 | mac.init(new SecretKeySpec(key, algorithm));
298 | return mac.doFinal(data.getBytes(StandardCharsets.UTF_8));
299 | } catch (final Exception e) {
300 | throw new IllegalArgumentException(e.getMessage(), e);
301 | }
302 | }
303 |
304 | /**
305 | * Calculate and return the signature key. Note: the returned signature must be converted from
306 | * binary to hexadecimal representation (lowercase letters).
307 | *
310 | * kDate = hash("AWS4" + Key, Date)
311 | * kRegion = hash(kDate, Region)
312 | * kService = hash(kRegion, ServiceName)
313 | * kSigning = hash(kService, "aws4_request")
314 | * ksignature = hash(kSigning, string-to-sign)
315 | *
316 | *
317 | * @param key AWS secret access key.
318 | * @param dateStamp Date used in the credential scope. Format: yyyyMMdd.
319 | * @param regionName AWS region. Example: us-west-2.
320 | * @param serviceName The name of the service. Should be {@code kinesisvideo}.
321 | * @return {@code ksignature}, as specified above.
322 | * @see Calculate signature
323 | */
324 | static byte[] getSignatureKey(
325 | final String key,
326 | final String dateStamp,
327 | final String regionName,
328 | final String serviceName) {
329 | final byte[] kSecret = ("AWS4" + key).getBytes(StandardCharsets.UTF_8);
330 | final byte[] kDate = hmacSha256(dateStamp, kSecret);
331 | final byte[] kRegion = hmacSha256(regionName, kDate);
332 | final byte[] kService = hmacSha256(serviceName, kRegion);
333 | return hmacSha256(AwsV4SignerConstants.AWS4_REQUEST_TYPE, kService);
334 | }
335 |
336 | /**
337 | * Returns the date and time, formatted to follow the ISO 8601 standard,
338 | * which is the "yyyyMMddTHHmmssZ" format.
339 | * > urisList = new ArrayList<>();
276 | for (final IceServer iceServer : mIceServerList) {
277 | userNames.add(iceServer.getUsername());
278 | passwords.add(iceServer.getPassword());
279 | ttls.add(iceServer.getTtl());
280 | urisList.add(iceServer.getUris());
281 | }
282 | extras.putStringArrayList(KEY_ICE_SERVER_USER_NAME, userNames);
283 | extras.putStringArrayList(KEY_ICE_SERVER_PASSWORD, passwords);
284 | extras.putIntegerArrayList(KEY_ICE_SERVER_TTL, ttls);
285 | extras.putSerializable(KEY_ICE_SERVER_URI, urisList);
286 | } else {
287 | extras.putStringArrayList(KEY_ICE_SERVER_USER_NAME, null);
288 | extras.putStringArrayList(KEY_ICE_SERVER_PASSWORD, null);
289 | extras.putIntegerArrayList(KEY_ICE_SERVER_TTL, null);
290 | extras.putSerializable(KEY_ICE_SERVER_URI, null);
291 | }
292 |
293 | for (ResourceEndpointListItem endpoint : mEndpointList) {
294 | if (endpoint.getProtocol().equals("WSS")) {
295 | extras.putString(KEY_WSS_ENDPOINT, endpoint.getResourceEndpoint());
296 | } else if (endpoint.getProtocol().equals("WEBRTC")) {
297 | extras.putString(KEY_WEBRTC_ENDPOINT, endpoint.getResourceEndpoint());
298 | }
299 | }
300 |
301 | final SparseBooleanArray checked = mOptions.getCheckedItemPositions();
302 | for (int i = 0; i < mOptions.getCount(); i++) {
303 | extras.putBoolean(KEY_OF_OPTIONS[i], checked.get(i));
304 | }
305 |
306 | extras.putBoolean(KEY_CAMERA_FRONT_FACING, mCameras.getSelectedItem().equals("Front Camera"));
307 |
308 | return extras;
309 | }
310 |
311 | private AWSKinesisVideoClient getAwsKinesisVideoClient(final String region) {
312 | final AWSKinesisVideoClient awsKinesisVideoClient = new AWSKinesisVideoClient(
313 | KinesisVideoWebRtcDemoApp.getCredentialsProvider().getCredentials());
314 | awsKinesisVideoClient.setRegion(Region.getRegion(region));
315 | awsKinesisVideoClient.setSignerRegionOverride(region);
316 | awsKinesisVideoClient.setServiceNameIntern("kinesisvideo");
317 | try {
318 | String customEndpoint = BuildConfig.CONTROL_PLANE_URI;
319 | if (customEndpoint != null && !customEndpoint.isEmpty() && !"null".equals(customEndpoint)) {
320 | awsKinesisVideoClient.setEndpoint(customEndpoint);
321 | }
322 | } catch (Exception e) {
323 | // CONTROL_PLANE_URI not defined in .env
324 | }
325 |
326 | return awsKinesisVideoClient;
327 | }
328 |
329 | private AWSKinesisVideoSignalingClient getAwsKinesisVideoSignalingClient(final String region, final String endpoint) {
330 | final AWSKinesisVideoSignalingClient client = new AWSKinesisVideoSignalingClient(
331 | KinesisVideoWebRtcDemoApp.getCredentialsProvider().getCredentials());
332 | client.setRegion(Region.getRegion(region));
333 | client.setSignerRegionOverride(region);
334 | client.setServiceNameIntern("kinesisvideo");
335 | client.setEndpoint(endpoint);
336 | return client;
337 | }
338 |
339 | /**
340 | * Fetches info needed to connect to the Amazon Kinesis Video Streams Signaling channel.
341 | *
342 | * @param region The region the Signaling channel is located in.
343 | * @param channelName The name of the Amazon Kinesis Video Streams Signaling channel.
344 | * @param role The signaling channel role (master or viewer).
345 | * @return {@code true} on success. {@code false} if unsuccessful.
346 | */
347 | private boolean updateSignalingChannelInfo(final String region, final String channelName, final ChannelRole role) {
348 | mEndpointList.clear();
349 | mIceServerList.clear();
350 | mChannelArn = null;
351 | final UpdateSignalingChannelInfoTask task = new UpdateSignalingChannelInfoTask(this);
352 |
353 | String errorMessage = null;
354 | try {
355 | errorMessage = task.execute(region, channelName, role).get();
356 | } catch (Exception e) {
357 | Log.e(TAG, "Failed to wait for response of UpdateSignalingChannelInfoTask", e);
358 | }
359 |
360 | if (errorMessage != null) {
361 | Log.e(TAG, "updateSignalingChannelInfo() encountered an error: " + errorMessage);
362 | // Show error to user for debugging
363 | final String finalErrorMessage = errorMessage;
364 | if (getActivity() != null) {
365 | getActivity().runOnUiThread(() -> {
366 | new AlertDialog.Builder(getContext())
367 | .setTitle("Connection Error")
368 | .setMessage(finalErrorMessage)
369 | .setPositiveButton("OK", null)
370 | .show();
371 | });
372 | }
373 | }
374 | return errorMessage == null;
375 | }
376 |
377 | /**
378 | * Makes backend calls to KVS in order to obtain info needed to start the WebRTC session.
379 | *