From 6f4964f8ed9d96505f0776d81477805b488221e0 Mon Sep 17 00:00:00 2001 From: James Delaplane Date: Fri, 29 Oct 2021 09:03:20 -0700 Subject: [PATCH 01/38] Added 1-20% jitter to expiration value stored by AwsCredentials struct. This is part of a larger effort to help customers with large fleets distribute the timings of their attempts to reconnect to our servers. --- src/source/Common/IotCredentialProvider.c | 22 +++++++++++++++++----- src/source/Common/RequestInfo.c | 4 +++- 2 files changed, 20 insertions(+), 6 deletions(-) diff --git a/src/source/Common/IotCredentialProvider.c b/src/source/Common/IotCredentialProvider.c index a61218cae..e91eaee33 100644 --- a/src/source/Common/IotCredentialProvider.c +++ b/src/source/Common/IotCredentialProvider.c @@ -124,7 +124,7 @@ STATUS parseIotResponse(PIotCredentialProvider pIotCredentialProvider, PCallInfo jsmn_parser parser; jsmntok_t tokens[MAX_JSON_TOKEN_COUNT]; PCHAR accessKeyId = NULL, secretKey = NULL, sessionToken = NULL, expirationTimestamp = NULL, pResponseStr = NULL; - UINT64 expiration, currentTime; + UINT64 expiration, currentTime, jitter, randMultiplier; CHAR expirationTimestampStr[MAX_EXPIRATION_LEN + 1]; CHK(pIotCredentialProvider != NULL && pCallInfo != NULL, STATUS_NULL_ARG); @@ -176,10 +176,22 @@ STATUS parseIotResponse(PIotCredentialProvider pIotCredentialProvider, PCallInfo pIotCredentialProvider->pAwsCredentials = NULL; } - // Fix-up the expiration to be no more than max enforced token rotation to avoid extra token rotations - // as we are caching the returned value which is likely to be an hour but we are enforcing max - // rotation to be more frequent. - expiration = MIN(expiration, currentTime + MAX_ENFORCED_TOKEN_EXPIRATION_DURATION); + // Need to have large numbers since all items are in terms of HUNDREDS_NANOS_IN_A_SECOND. -1 to round down + randMultiplier = (ULONG_MAX / RAND_MAX) - 1; + + // add randomized jitter between 1-20% of expiration + srand(currentTime / ((UINT64) sessionToken[rand() % (sessionTokenLen - 1)] + (UINT64) sessionToken[rand() % (sessionTokenLen - 1)])); + expiration -= currentTime; + + // remainder after dividing by 20% + jitter = (rand() * randMultiplier) % (((expiration / 100) * 20) + (expiration % 100) * 20 / 100); + // if remainder is below 1%, add 1%. Adding instead of setting avoids a normal distribution having a large accumulation at exactly 1%. + if (jitter < expiration / 100) { + jitter += expiration / 100; + } + + expiration -= jitter; + expiration += currentTime; CHK_STATUS(createAwsCredentials(accessKeyId, accessKeyIdLen, secretKey, secretKeyLen, sessionToken, sessionTokenLen, expiration, &pIotCredentialProvider->pAwsCredentials)); diff --git a/src/source/Common/RequestInfo.c b/src/source/Common/RequestInfo.c index adda7066f..261d885b2 100644 --- a/src/source/Common/RequestInfo.c +++ b/src/source/Common/RequestInfo.c @@ -221,6 +221,8 @@ STATUS setRequestHeader(PRequestInfo pRequestInfo, PCHAR headerName, UINT32 head if (STATUS_FAILED(retStatus) && pRequestHeader != NULL) { MEMFREE(pRequestHeader); + } else { + DLOGD("Appending header to request: %s -> %s", headerName, headerValue); } return retStatus; @@ -340,4 +342,4 @@ STATUS releaseCallInfo(PCallInfo pCallInfo) CleanUp: return retStatus; -} \ No newline at end of file +} From e039399299318e3b58d9ca489cbc9d650a93bd36 Mon Sep 17 00:00:00 2001 From: James Delaplane Date: Mon, 9 May 2022 14:51:24 -0700 Subject: [PATCH 02/38] reduced clumped distribution between 1-2% --- src/source/Common/IotCredentialProvider.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/source/Common/IotCredentialProvider.c b/src/source/Common/IotCredentialProvider.c index e91eaee33..4cfdf4d10 100644 --- a/src/source/Common/IotCredentialProvider.c +++ b/src/source/Common/IotCredentialProvider.c @@ -183,12 +183,10 @@ STATUS parseIotResponse(PIotCredentialProvider pIotCredentialProvider, PCallInfo srand(currentTime / ((UINT64) sessionToken[rand() % (sessionTokenLen - 1)] + (UINT64) sessionToken[rand() % (sessionTokenLen - 1)])); expiration -= currentTime; - // remainder after dividing by 20% - jitter = (rand() * randMultiplier) % (((expiration / 100) * 20) + (expiration % 100) * 20 / 100); - // if remainder is below 1%, add 1%. Adding instead of setting avoids a normal distribution having a large accumulation at exactly 1%. - if (jitter < expiration / 100) { - jitter += expiration / 100; - } + // remainder after dividing by 19% + jitter = (rand() * randMultiplier) % (((expiration / 100) * 19) + (expiration % 100) * 19 / 100); + // add exaction 1% to change to range from 0-19 to 1-20; + jitter += expiration / 100; expiration -= jitter; expiration += currentTime; From 8a870286b517468f1da8492c8db1f026e4104de2 Mon Sep 17 00:00:00 2001 From: Michael Dietz Date: Wed, 7 Sep 2022 09:39:24 -0500 Subject: [PATCH 03/38] fix auth query template, used by signAwsRequestInfoQueryParam --- src/source/Common/AwsV4Signer.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/source/Common/AwsV4Signer.h b/src/source/Common/AwsV4Signer.h index 951c7f068..71208e2da 100644 --- a/src/source/Common/AwsV4Signer.h +++ b/src/source/Common/AwsV4Signer.h @@ -51,14 +51,14 @@ extern "C" { #define AUTH_HEADER_TEMPLATE "%s Credential=%.*s/%s, SignedHeaders=%.*s, Signature=%s" // Authentication query template -#define AUTH_QUERY_TEMPLATE "&X-Amz-Algorithm=%s&X-Amz-Credential=%s&X-Amz-Date=%s&X-Amz-Expires=%u&X-Amz-SignedHeaders=%.*s" +#define AUTH_QUERY_TEMPLATE "?X-Amz-Algorithm=%s&X-Amz-Credential=%s&X-Amz-Date=%s&X-Amz-Expires=%u&X-Amz-SignedHeaders=%.*s" // Token query param template #define SECURITY_TOKEN_PARAM_TEMPLATE "&X-Amz-Security-Token=%s" // Authentication query template #define AUTH_QUERY_TEMPLATE_WITH_TOKEN \ - "&X-Amz-Algorithm=%s&X-Amz-Credential=%s&X-Amz-Date=%s&X-Amz-Expires=%u&X-Amz-SignedHeaders=%.*s" SECURITY_TOKEN_PARAM_TEMPLATE + "?X-Amz-Algorithm=%s&X-Amz-Credential=%s&X-Amz-Date=%s&X-Amz-Expires=%u&X-Amz-SignedHeaders=%.*s" SECURITY_TOKEN_PARAM_TEMPLATE // Signature query param template #define SIGNATURE_PARAM_TEMPLATE "&X-Amz-Signature=%s" From 220bffdf8b108ead592776d2853ccef78953cc56 Mon Sep 17 00:00:00 2001 From: Michael Dietz Date: Fri, 16 Sep 2022 12:57:26 -0500 Subject: [PATCH 04/38] aws service configurable for presigned urls, eg s3 --- .../amazonaws/kinesis/video/common/Include.h | 6 ++++ src/source/Common/AwsV4Signer.c | 29 ++++++++++++++----- src/source/Common/AwsV4Signer.h | 2 ++ 3 files changed, 30 insertions(+), 7 deletions(-) diff --git a/src/include/com/amazonaws/kinesis/video/common/Include.h b/src/include/com/amazonaws/kinesis/video/common/Include.h index d5e548792..a38a8e3e8 100644 --- a/src/include/com/amazonaws/kinesis/video/common/Include.h +++ b/src/include/com/amazonaws/kinesis/video/common/Include.h @@ -98,6 +98,11 @@ extern "C" { */ #define MAX_REGION_NAME_LEN 128 +/** + * Maximum allowed service name length + */ +#define MAX_SERVICE_NAME_LEN 128 + /** * Maximum allowed user agent string length */ @@ -483,6 +488,7 @@ struct __RequestInfo { //!< NOTE: The body will follow the main struct UINT32 bodySize; //!< Size of the body in bytes CHAR url[MAX_URI_CHAR_LEN + 1]; //!< The URL for the request + CHAR service[MAX_SERVICE_NAME_LEN + 1]; //!< The AWS service for the request CHAR certPath[MAX_PATH_LEN + 1]; //!< CA Certificate path to use - optional CHAR sslCertPath[MAX_PATH_LEN + 1]; //!< SSL Certificate file path to use - optional CHAR sslPrivateKeyPath[MAX_PATH_LEN + 1]; //!< SSL Certificate private key file path to use - optional diff --git a/src/source/Common/AwsV4Signer.c b/src/source/Common/AwsV4Signer.c index 3522b3dae..d47a5267b 100644 --- a/src/source/Common/AwsV4Signer.c +++ b/src/source/Common/AwsV4Signer.c @@ -73,7 +73,7 @@ STATUS generateAwsSigV4Signature(PRequestInfo pRequestInfo, PCHAR dateTimeStr, B hmacSize = SIZEOF(hmac); CHK_STATUS(generateRequestHmac((PBYTE) pScratchBuf, curSize, (PBYTE) dateTimeStr, SIGNATURE_DATE_STRING_LEN * SIZEOF(CHAR), hmac, &hmacSize)); CHK_STATUS(generateRequestHmac(hmac, hmacSize, (PBYTE) pRequestInfo->region, (UINT32) STRLEN(pRequestInfo->region), hmac, &hmacSize)); - CHK_STATUS(generateRequestHmac(hmac, hmacSize, (PBYTE) KINESIS_VIDEO_SERVICE_NAME, (UINT32) STRLEN(KINESIS_VIDEO_SERVICE_NAME), hmac, &hmacSize)); + CHK_STATUS(generateRequestHmac(hmac, hmacSize, (PBYTE) pRequestInfo->service, (UINT32) STRNLEN(pRequestInfo->service, MAX_SERVICE_NAME_LEN), hmac, &hmacSize)); CHK_STATUS(generateRequestHmac(hmac, hmacSize, (PBYTE) AWS_SIG_V4_SIGNATURE_END, (UINT32) STRLEN(AWS_SIG_V4_SIGNATURE_END), hmac, &hmacSize)); CHK_STATUS(generateRequestHmac(hmac, hmacSize, (PBYTE) pSignedStr, signedStrLen * SIZEOF(CHAR), hmac, &hmacSize)); @@ -117,6 +117,11 @@ STATUS signAwsRequestInfo(PRequestInfo pRequestInfo) CHK(pRequestInfo != NULL && pRequestInfo->pAwsCredentials != NULL, STATUS_NULL_ARG); + // signAwsRequestInfo is a public api function, if service is not specified default to "kinesisvideo" so no breaking changes are introduced to the api + if (pRequestInfo->service[0] == L'\0') { + STRNCPY(pRequestInfo->service, KINESIS_VIDEO_SERVICE_NAME, MAX_SERVICE_NAME_LEN); + } + // Generate the time CHK_STATUS(generateSignatureDateTime(pRequestInfo->currentTime, dateTimeStr)); @@ -167,6 +172,11 @@ STATUS signAwsRequestInfoQueryParam(PRequestInfo pRequestInfo) CHK(pRequestInfo != NULL && pRequestInfo->pAwsCredentials != NULL, STATUS_NULL_ARG); + // signAwsRequestInfoQueryParam is a public api function, if service is not specified default to "kinesisvideo" so no breaking changes are introduced to the api + if (pRequestInfo->service[0] == L'\0') { + STRNCPY(pRequestInfo->service, KINESIS_VIDEO_SERVICE_NAME, MAX_SERVICE_NAME_LEN); + } + // Generate the time CHK_STATUS(generateSignatureDateTime(pRequestInfo->currentTime, dateTimeStr)); @@ -498,8 +508,13 @@ STATUS generateCanonicalRequestString(PRequestInfo pRequestInfo, PCHAR pRequestS len = SHA256_DIGEST_LENGTH * 2; CHK(curLen + len <= requestLen, STATUS_BUFFER_TOO_SMALL); if (pRequestInfo->body == NULL) { - // Streaming treats this portion as if the body were empty - CHK_STATUS(hexEncodedSha256((PBYTE) EMPTY_STRING, 0, pCurPtr)); + if (STRNCMP(pRequestInfo->service, KINESIS_VIDEO_SERVICE_NAME, MAX_SERVICE_NAME_LEN) == 0) { + // Streaming treats this portion as if the body were empty + CHK_STATUS(hexEncodedSha256((PBYTE) EMPTY_STRING, 0, pCurPtr)); + } else { + len = (UINT32) (ARRAY_SIZE(PREDEFINED_UNSIGNED_PAYLOAD) - 1); + MEMCPY(pCurPtr, PREDEFINED_UNSIGNED_PAYLOAD, SIZEOF(CHAR) * len); + } } else { // standard signing CHK_STATUS(hexEncodedSha256((PBYTE) pRequestInfo->body, pRequestInfo->bodySize, pCurPtr)); @@ -678,14 +693,14 @@ STATUS generateCredentialScope(PRequestInfo pRequestInfo, PCHAR dateTimeStr, PCH CHK(pRequestInfo != NULL && dateTimeStr != NULL && pScopeLen != NULL, STATUS_NULL_ARG); // Calculate the max string length with a null terminator at the end - scopeLen = SIGNATURE_DATE_TIME_STRING_LEN + 1 + MAX_REGION_NAME_LEN + 1 + (UINT32) STRLEN(KINESIS_VIDEO_SERVICE_NAME) + 1 + + scopeLen = SIGNATURE_DATE_TIME_STRING_LEN + 1 + MAX_REGION_NAME_LEN + 1 + (UINT32) STRNLEN(pRequestInfo->service, MAX_SERVICE_NAME_LEN) + 1 + (UINT32) STRLEN(AWS_SIG_V4_SIGNATURE_END) + 1; // Early exit on buffer calculation CHK(pScope != NULL, retStatus); scopeLen = (UINT32) SNPRINTF(pScope, *pScopeLen, CREDENTIAL_SCOPE_TEMPLATE, SIGNATURE_DATE_STRING_LEN, dateTimeStr, pRequestInfo->region, - KINESIS_VIDEO_SERVICE_NAME, AWS_SIG_V4_SIGNATURE_END); + pRequestInfo->service, AWS_SIG_V4_SIGNATURE_END); CHK(scopeLen > 0 && scopeLen <= *pScopeLen, STATUS_BUFFER_TOO_SMALL); CleanUp: @@ -707,7 +722,7 @@ STATUS generateEncodedCredentials(PRequestInfo pRequestInfo, PCHAR dateTimeStr, CHK(pRequestInfo != NULL && dateTimeStr != NULL && pCredsLen != NULL, STATUS_NULL_ARG); // Calculate the max string length with '/' and a null terminator at the end - credsLen = MAX_ACCESS_KEY_LEN + 1 + SIGNATURE_DATE_TIME_STRING_LEN + 1 + MAX_REGION_NAME_LEN + 1 + (UINT32) STRLEN(KINESIS_VIDEO_SERVICE_NAME) + + credsLen = MAX_ACCESS_KEY_LEN + 1 + SIGNATURE_DATE_TIME_STRING_LEN + 1 + MAX_REGION_NAME_LEN + 1 + (UINT32) STRNLEN(pRequestInfo->service, MAX_SERVICE_NAME_LEN) + 1 + (UINT32) STRLEN(AWS_SIG_V4_SIGNATURE_END) + 1; // Early exit on buffer calculation @@ -715,7 +730,7 @@ STATUS generateEncodedCredentials(PRequestInfo pRequestInfo, PCHAR dateTimeStr, credsLen = (UINT32) SNPRINTF(pCreds, *pCredsLen, URL_ENCODED_CREDENTIAL_TEMPLATE, pRequestInfo->pAwsCredentials->accessKeyIdLen, pRequestInfo->pAwsCredentials->accessKeyId, SIGNATURE_DATE_STRING_LEN, dateTimeStr, pRequestInfo->region, - KINESIS_VIDEO_SERVICE_NAME, AWS_SIG_V4_SIGNATURE_END); + pRequestInfo->service, AWS_SIG_V4_SIGNATURE_END); CHK(credsLen > 0 && credsLen <= *pCredsLen, STATUS_BUFFER_TOO_SMALL); CleanUp: diff --git a/src/source/Common/AwsV4Signer.h b/src/source/Common/AwsV4Signer.h index 71208e2da..ea9a7f622 100644 --- a/src/source/Common/AwsV4Signer.h +++ b/src/source/Common/AwsV4Signer.h @@ -82,6 +82,8 @@ extern "C" { #define KVS_MAX_HMAC_SIZE 64 +#define PREDEFINED_UNSIGNED_PAYLOAD "UNSIGNED-PAYLOAD" + //////////////////////////////////////////////////// // Function definitions //////////////////////////////////////////////////// From 75d53abcbac214e19d14afcb61dbf533056b560c Mon Sep 17 00:00:00 2001 From: James Delaplane Date: Mon, 18 Sep 2023 15:56:08 -0700 Subject: [PATCH 05/38] Update PIC to include modified threadpool teardown --- CMake/Dependencies/libkvspic-CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMake/Dependencies/libkvspic-CMakeLists.txt b/CMake/Dependencies/libkvspic-CMakeLists.txt index b72be4bcc..168e97ccd 100644 --- a/CMake/Dependencies/libkvspic-CMakeLists.txt +++ b/CMake/Dependencies/libkvspic-CMakeLists.txt @@ -7,7 +7,7 @@ include(ExternalProject) # clone repo only ExternalProject_Add(libkvspic-download GIT_REPOSITORY https://github.com/awslabs/amazon-kinesis-video-streams-pic.git - GIT_TAG d08be2e16303507d21b4cb376aecda98271687ad + GIT_TAG be6d170ced7ed4b1ad7a2fbdb7cf93f1ac21f5f9 SOURCE_DIR "${CMAKE_CURRENT_BINARY_DIR}/kvspic-src" BINARY_DIR "${CMAKE_CURRENT_BINARY_DIR}/kvspic-build" CMAKE_ARGS From d932a9dd6bfbb767d1a58083829746fe42a6da37 Mon Sep 17 00:00:00 2001 From: Niyati Maheshwari Date: Tue, 19 Sep 2023 13:28:24 -0700 Subject: [PATCH 06/38] use git tag commit from pic-develop --- CMake/Dependencies/libkvspic-CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMake/Dependencies/libkvspic-CMakeLists.txt b/CMake/Dependencies/libkvspic-CMakeLists.txt index 168e97ccd..40d21bf9c 100644 --- a/CMake/Dependencies/libkvspic-CMakeLists.txt +++ b/CMake/Dependencies/libkvspic-CMakeLists.txt @@ -7,7 +7,7 @@ include(ExternalProject) # clone repo only ExternalProject_Add(libkvspic-download GIT_REPOSITORY https://github.com/awslabs/amazon-kinesis-video-streams-pic.git - GIT_TAG be6d170ced7ed4b1ad7a2fbdb7cf93f1ac21f5f9 + GIT_TAG 98cc13510eda7da08eb91cb6904c8f980aa110af SOURCE_DIR "${CMAKE_CURRENT_BINARY_DIR}/kvspic-src" BINARY_DIR "${CMAKE_CURRENT_BINARY_DIR}/kvspic-build" CMAKE_ARGS From 02b2fa2374767ca4202b17366c7dfa6fa65179f0 Mon Sep 17 00:00:00 2001 From: James Delaplane Date: Mon, 18 Sep 2023 15:56:08 -0700 Subject: [PATCH 07/38] Update PIC to include modified threadpool teardown --- CMake/Dependencies/libkvspic-CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMake/Dependencies/libkvspic-CMakeLists.txt b/CMake/Dependencies/libkvspic-CMakeLists.txt index 6ab35a2da..168e97ccd 100644 --- a/CMake/Dependencies/libkvspic-CMakeLists.txt +++ b/CMake/Dependencies/libkvspic-CMakeLists.txt @@ -7,7 +7,7 @@ include(ExternalProject) # clone repo only ExternalProject_Add(libkvspic-download GIT_REPOSITORY https://github.com/awslabs/amazon-kinesis-video-streams-pic.git - GIT_TAG 57637ea593f4b43c509413a44d993ed08d7f2616 + GIT_TAG be6d170ced7ed4b1ad7a2fbdb7cf93f1ac21f5f9 SOURCE_DIR "${CMAKE_CURRENT_BINARY_DIR}/kvspic-src" BINARY_DIR "${CMAKE_CURRENT_BINARY_DIR}/kvspic-build" CMAKE_ARGS From 9583d7bc8e1709826f1ef130393c575d80b8457c Mon Sep 17 00:00:00 2001 From: Niyati Maheshwari Date: Tue, 19 Sep 2023 13:28:24 -0700 Subject: [PATCH 08/38] use git tag commit from pic-develop --- CMake/Dependencies/libkvspic-CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMake/Dependencies/libkvspic-CMakeLists.txt b/CMake/Dependencies/libkvspic-CMakeLists.txt index 168e97ccd..40d21bf9c 100644 --- a/CMake/Dependencies/libkvspic-CMakeLists.txt +++ b/CMake/Dependencies/libkvspic-CMakeLists.txt @@ -7,7 +7,7 @@ include(ExternalProject) # clone repo only ExternalProject_Add(libkvspic-download GIT_REPOSITORY https://github.com/awslabs/amazon-kinesis-video-streams-pic.git - GIT_TAG be6d170ced7ed4b1ad7a2fbdb7cf93f1ac21f5f9 + GIT_TAG 98cc13510eda7da08eb91cb6904c8f980aa110af SOURCE_DIR "${CMAKE_CURRENT_BINARY_DIR}/kvspic-src" BINARY_DIR "${CMAKE_CURRENT_BINARY_DIR}/kvspic-build" CMAKE_ARGS From 115b005c698efa3add3bc50039300f2362e1aade Mon Sep 17 00:00:00 2001 From: Dave Johansen Date: Thu, 14 Sep 2023 14:56:19 -0600 Subject: [PATCH 09/38] Set newly added timeouts to default value --- src/source/DeviceInfoProvider.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/source/DeviceInfoProvider.c b/src/source/DeviceInfoProvider.c index e9a54f590..8ebac6092 100644 --- a/src/source/DeviceInfoProvider.c +++ b/src/source/DeviceInfoProvider.c @@ -45,6 +45,8 @@ STATUS createDefaultDeviceInfo(PDeviceInfo* ppDeviceInfo) // use 0 for default values pDeviceInfo->clientInfo.stopStreamTimeout = 0; + pDeviceInfo->clientInfo.serviceCallConnectionTimeout = SERVICE_CALL_DEFAULT_CONNECTION_TIMEOUT; + pDeviceInfo->clientInfo.serviceCallCompletionTimeout = SERVICE_CALL_DEFAULT_TIMEOUT; pDeviceInfo->clientInfo.createClientTimeout = 0; pDeviceInfo->clientInfo.createStreamTimeout = 0; From a2541d9c79f4a19609f0e4d8d89c9d2b491f5087 Mon Sep 17 00:00:00 2001 From: James Delaplane Date: Tue, 10 Oct 2023 11:59:22 -0700 Subject: [PATCH 10/38] Expose CONSTRAINED_DEVICE option --- CMakeLists.txt | 5 +++++ README.md | 1 + 2 files changed, 6 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index d23b3cafa..cd6bc22cc 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -16,6 +16,7 @@ option(BUILD_COMMON_LWS "Whether or not to build ProducerC libwebsockets common option(BUILD_COMMON_CURL "Whether or not to build ProducerC curl common library" ON) option(BUILD_OPENSSL_PLATFORM "If buildng OpenSSL what is the target platform" OFF) option(LOCAL_OPENSSL_BUILD "Whether or not to use local OpenSSL build" OFF) +option(CONSTRAINED_DEVICE "Change pthread stack size" OFF) # Developer Flags option(BUILD_TEST "Build the testing tree." OFF) @@ -34,6 +35,10 @@ get_filename_component(ROOT "${CMAKE_CURRENT_SOURCE_DIR}" ABSOLUTE) add_definitions(-DVERSION_STRING=\"${PROJECT_VERSION}\") +if(CONSTRAINED_DEVICE) + add_definitions(-DCONSTRAINED_DEVICE) +endif() + if(NOT KINESIS_VIDEO_PRODUCER_C_SRC) if(DEFINED ENV{KINESIS_VIDEO_PRODUCER_C_SRC}) set(KINESIS_VIDEO_PRODUCER_C_SRC $ENV{KINESIS_VIDEO_PRODUCER_C_SRC}) diff --git a/README.md b/README.md index 9d6860a80..b45d97e29 100644 --- a/README.md +++ b/README.md @@ -58,6 +58,7 @@ You can pass the following options to `cmake ..`. * `-DUNDEFINED_BEHAVIOR_SANITIZER` Build with UndefinedBehaviorSanitizer * `-DALIGNED_MEMORY_MODEL` Build for aligned memory model only devices. Default is OFF. * `-DLOCAL_OPENSSL_BUILD` Whether or not to use local OpenSSL build. Default is OFF. +* `-DCONSTRAINED_DEVICE` -- Change thread stack size to 0.5Mb, needed for Alpine. DMEMORY_SANITIZER, DTHREAD_SANITIZER etc. flags works only with clang compiler From f934b6c5a36f0daa7a002fd843542902e3ed7471 Mon Sep 17 00:00:00 2001 From: James Delaplane Date: Wed, 11 Oct 2023 14:12:15 -0700 Subject: [PATCH 11/38] use develop tag --- CMake/Dependencies/libkvspic-CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMake/Dependencies/libkvspic-CMakeLists.txt b/CMake/Dependencies/libkvspic-CMakeLists.txt index 40d21bf9c..c8ebe188d 100644 --- a/CMake/Dependencies/libkvspic-CMakeLists.txt +++ b/CMake/Dependencies/libkvspic-CMakeLists.txt @@ -7,7 +7,7 @@ include(ExternalProject) # clone repo only ExternalProject_Add(libkvspic-download GIT_REPOSITORY https://github.com/awslabs/amazon-kinesis-video-streams-pic.git - GIT_TAG 98cc13510eda7da08eb91cb6904c8f980aa110af + GIT_TAG develop SOURCE_DIR "${CMAKE_CURRENT_BINARY_DIR}/kvspic-src" BINARY_DIR "${CMAKE_CURRENT_BINARY_DIR}/kvspic-build" CMAKE_ARGS From ff897a5bab6ecde3532569f1125ec16a7b60b006 Mon Sep 17 00:00:00 2001 From: Divya Sampath Kumar Date: Mon, 23 Oct 2023 15:44:27 -0400 Subject: [PATCH 12/38] Add logging section (#417) --- README.md | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/README.md b/README.md index b45d97e29..e49af0f3e 100644 --- a/README.md +++ b/README.md @@ -133,6 +133,31 @@ For audio only, run `./kvsAudioOnlyStreamingSample clientInfo.loggerLogLevel = LOG_LEVEL_DEBUG; +``` + +By default, our samples set the log level to `LOG_LEVEL_DEBUG`. + +The SDK also tracks entry and exit of functions which increases the verbosity of the logs. This will be useful when you want to track the transitions within the codebase. To do so, you need to set log level to `LOG_LEVEL_VERBOSE` and add the following to the cmake file: +`add_definitions(-DLOG_STREAMING)` +Note: This log level is extremely VERBOSE and could flood the files if using file based logging strategy. + ### Run unit tests Since these tests exercise networking you need to have AWS credentials specified, specifically you need to: From 4f1e7980d9fbc95bd9c91044c596deef16e0c426 Mon Sep 17 00:00:00 2001 From: Niyati Maheshwari Date: Wed, 29 Nov 2023 09:05:12 -0800 Subject: [PATCH 13/38] Introduce M1 build in the CI (#419) * m1 build * cmake update * run tests in m1 --- .github/workflows/ci.yml | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 3d2fd5547..eabeb418d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -85,6 +85,35 @@ jobs: cd build ./tst/producer_test + mac-os-m1-build-clang: + runs-on: macos-13-xlarge + env: + AWS_KVS_LOG_LEVEL: 2 + permissions: + id-token: write + contents: read + steps: + - name: Clone repository + uses: actions/checkout@v3 + - name: Build repository + run: | + brew install pkgconfig + brew unlink openssl # it seems the libcurl is trying to access this openssl despite explicitly setting it to our build + mkdir build && cd build + sh -c 'cmake .. -DBUILD_TEST=TRUE -DCOMPILER_WARNINGS=TRUE -DCMAKE_C_COMPILER=$(brew --prefix llvm@15)/bin/clang -DCMAKE_CXX_COMPILER=$(brew --prefix llvm@15)/bin/clang++;cmake .. -DBUILD_TEST=TRUE -DCOMPILER_WARNINGS=TRUE -DCMAKE_C_COMPILER=$(brew --prefix llvm@15)/bin/clang -DCMAKE_CXX_COMPILER=$(brew --prefix llvm@15)/bin/clang++' + make + - name: Configure AWS Credentials + uses: aws-actions/configure-aws-credentials@v1-node16 + with: + role-to-assume: ${{ secrets.AWS_ROLE_TO_ASSUME }} + role-session-name: ${{ secrets.AWS_ROLE_SESSION_NAME }} + aws-region: ${{ secrets.AWS_REGION }} + role-duration-seconds: 10800 + - name: Run tests + run: | + cd build + ./tst/producer_test + mac-os-build-gcc-local-openssl: runs-on: macos-11 permissions: From 52f562f9d4aaa05018f1f265df30fdc23f30d537 Mon Sep 17 00:00:00 2001 From: Dave Johansen Date: Thu, 10 Aug 2023 13:23:02 -0600 Subject: [PATCH 14/38] Add setting of fragment meta to the sample --- samples/KvsVideoOnlyRealtimeStreamingSample.c | 26 ++++++++++++++++--- 1 file changed, 22 insertions(+), 4 deletions(-) diff --git a/samples/KvsVideoOnlyRealtimeStreamingSample.c b/samples/KvsVideoOnlyRealtimeStreamingSample.c index a5e3d2efc..fe032b98e 100644 --- a/samples/KvsVideoOnlyRealtimeStreamingSample.c +++ b/samples/KvsVideoOnlyRealtimeStreamingSample.c @@ -68,10 +68,12 @@ INT32 main(INT32 argc, CHAR* argv[]) CHAR videoCodec[VIDEO_CODEC_NAME_MAX_LENGTH]; STRNCPY(videoCodec, VIDEO_CODEC_NAME_H264, STRLEN(VIDEO_CODEC_NAME_H264)); // h264 video by default VIDEO_CODEC_ID videoCodecID = VIDEO_CODEC_ID_H264; + int n, numMetadata = 0; + char key[200], value[200]; if (argc < 2) { DLOGE("Usage: AWS_ACCESS_KEY_ID=SAMPLEKEY AWS_SECRET_ACCESS_KEY=SAMPLESECRET %s " - "\n", + " [num_metadata]\n", argv[0]); CHK(FALSE, STATUS_INVALID_ARG); } @@ -82,7 +84,7 @@ INT32 main(INT32 argc, CHAR* argv[]) } MEMSET(frameFilePath, 0x00, MAX_PATH_LEN + 1); - if (argc < 5) { + if (argc < 5 || STRLEN(argv[4]) == 0) { STRCPY(frameFilePath, (PCHAR) "../samples/"); } else { STRNCPY(frameFilePath, argv[4], MAX_PATH_LEN); @@ -95,19 +97,23 @@ INT32 main(INT32 argc, CHAR* argv[]) region = (PCHAR) DEFAULT_AWS_REGION; } - if (argc >= 3) { + if (argc >= 3 && STRLEN(argv[2]) > 0) { if (!STRCMP(argv[2], VIDEO_CODEC_NAME_H265)) { STRNCPY(videoCodec, VIDEO_CODEC_NAME_H265, STRLEN(VIDEO_CODEC_NAME_H265)); videoCodecID = VIDEO_CODEC_ID_H265; } } - if (argc >= 4) { + if (argc >= 4 && STRLEN(argv[3]) > 0) { // Get the duration and convert to an integer CHK_STATUS(STRTOUI64(argv[3], NULL, 10, &streamingDuration)); streamingDuration *= HUNDREDS_OF_NANOS_IN_A_SECOND; } + if (argc >= 6 && STRLEN(argv[5]) > 0) { + numMetadata = STRTOUL(argv[5], NULL, 10); + } + streamStopTime = GETTIME() + streamingDuration; // default storage size is 128MB. Use setDeviceInfoStorageSize after create to change storage size. @@ -154,6 +160,18 @@ INT32 main(INT32 argc, CHAR* argv[]) CHK_STATUS(readFrameData(&frame, frameFilePath, videoCodec)); + if (frame.flags == FRAME_FLAG_KEY_FRAME) { + for (n=0; n Date: Tue, 5 Dec 2023 12:55:24 -0800 Subject: [PATCH 15/38] Formatting + style --- samples/KvsVideoOnlyRealtimeStreamingSample.c | 37 +++++++++---------- 1 file changed, 18 insertions(+), 19 deletions(-) diff --git a/samples/KvsVideoOnlyRealtimeStreamingSample.c b/samples/KvsVideoOnlyRealtimeStreamingSample.c index fe032b98e..b0fb9a698 100644 --- a/samples/KvsVideoOnlyRealtimeStreamingSample.c +++ b/samples/KvsVideoOnlyRealtimeStreamingSample.c @@ -11,6 +11,8 @@ #define VIDEO_CODEC_NAME_H264 "h264" #define VIDEO_CODEC_NAME_H265 "h265" #define VIDEO_CODEC_NAME_MAX_LENGTH 5 +#define METADATA_MAX_KEY_LENGTH 128 +#define METADATA_MAX_VALUE_LENGTH 256 #define NUMBER_OF_FRAME_FILES 403 @@ -57,10 +59,10 @@ INT32 main(INT32 argc, CHAR* argv[]) STREAM_HANDLE streamHandle = INVALID_STREAM_HANDLE_VALUE; STATUS retStatus = STATUS_SUCCESS; PCHAR accessKey = NULL, secretKey = NULL, sessionToken = NULL, streamName = NULL, region = NULL, cacertPath = NULL; - CHAR frameFilePath[MAX_PATH_LEN + 1]; + CHAR frameFilePath[MAX_PATH_LEN + 1], metadataKey[METADATA_MAX_KEY_LENGTH + 1], metadataValue[METADATA_MAX_VALUE_LENGTH + 1]; Frame frame; BYTE frameBuffer[200000]; // Assuming this is enough - UINT32 frameSize = SIZEOF(frameBuffer), frameIndex = 0, fileIndex = 0; + UINT32 frameSize = SIZEOF(frameBuffer), frameIndex = 0, fileIndex = 0, n = 0, numMetadata = 10; UINT64 streamStopTime, streamingDuration = DEFAULT_STREAM_DURATION; DOUBLE startUpLatency; BOOL firstFrame = TRUE; @@ -68,8 +70,6 @@ INT32 main(INT32 argc, CHAR* argv[]) CHAR videoCodec[VIDEO_CODEC_NAME_MAX_LENGTH]; STRNCPY(videoCodec, VIDEO_CODEC_NAME_H264, STRLEN(VIDEO_CODEC_NAME_H264)); // h264 video by default VIDEO_CODEC_ID videoCodecID = VIDEO_CODEC_ID_H264; - int n, numMetadata = 0; - char key[200], value[200]; if (argc < 2) { DLOGE("Usage: AWS_ACCESS_KEY_ID=SAMPLEKEY AWS_SECRET_ACCESS_KEY=SAMPLESECRET %s " @@ -83,13 +83,6 @@ INT32 main(INT32 argc, CHAR* argv[]) CHK(FALSE, STATUS_INVALID_ARG); } - MEMSET(frameFilePath, 0x00, MAX_PATH_LEN + 1); - if (argc < 5 || STRLEN(argv[4]) == 0) { - STRCPY(frameFilePath, (PCHAR) "../samples/"); - } else { - STRNCPY(frameFilePath, argv[4], MAX_PATH_LEN); - } - cacertPath = getenv(CACERT_PATH_ENV_VAR); sessionToken = getenv(SESSION_TOKEN_ENV_VAR); streamName = argv[1]; @@ -110,6 +103,13 @@ INT32 main(INT32 argc, CHAR* argv[]) streamingDuration *= HUNDREDS_OF_NANOS_IN_A_SECOND; } + MEMSET(frameFilePath, 0x00, MAX_PATH_LEN + 1); + if (argc < 5 || STRLEN(argv[4]) == 0) { + STRCPY(frameFilePath, (PCHAR) "../samples/"); + } else { + STRNCPY(frameFilePath, argv[4], MAX_PATH_LEN); + } + if (argc >= 6 && STRLEN(argv[5]) > 0) { numMetadata = STRTOUL(argv[5], NULL, 10); } @@ -160,15 +160,14 @@ INT32 main(INT32 argc, CHAR* argv[]) CHK_STATUS(readFrameData(&frame, frameFilePath, videoCodec)); + // Add the fragment metadata key-value pairs + // For limits, refer to https://docs.aws.amazon.com/kinesisvideostreams/latest/dg/limits.html#limits-streaming-metadata if (frame.flags == FRAME_FLAG_KEY_FRAME) { - for (n=0; n Date: Tue, 5 Dec 2023 13:35:37 -0800 Subject: [PATCH 16/38] More style --- samples/KvsVideoOnlyRealtimeStreamingSample.c | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/samples/KvsVideoOnlyRealtimeStreamingSample.c b/samples/KvsVideoOnlyRealtimeStreamingSample.c index b0fb9a698..6d0a9f0b4 100644 --- a/samples/KvsVideoOnlyRealtimeStreamingSample.c +++ b/samples/KvsVideoOnlyRealtimeStreamingSample.c @@ -13,6 +13,7 @@ #define VIDEO_CODEC_NAME_MAX_LENGTH 5 #define METADATA_MAX_KEY_LENGTH 128 #define METADATA_MAX_VALUE_LENGTH 256 +#define MAX_METADATA_PER_FRAGMENT 10 #define NUMBER_OF_FRAME_FILES 403 @@ -72,8 +73,8 @@ INT32 main(INT32 argc, CHAR* argv[]) VIDEO_CODEC_ID videoCodecID = VIDEO_CODEC_ID_H264; if (argc < 2) { - DLOGE("Usage: AWS_ACCESS_KEY_ID=SAMPLEKEY AWS_SECRET_ACCESS_KEY=SAMPLESECRET %s " - " [num_metadata]\n", + DLOGE("Usage: AWS_ACCESS_KEY_ID=SAMPLEKEY AWS_SECRET_ACCESS_KEY=SAMPLESECRET %s " + " [num_metadata = 10]\n", argv[0]); CHK(FALSE, STATUS_INVALID_ARG); } @@ -90,28 +91,30 @@ INT32 main(INT32 argc, CHAR* argv[]) region = (PCHAR) DEFAULT_AWS_REGION; } - if (argc >= 3 && STRLEN(argv[2]) > 0) { + if (argc >= 3 && !IS_EMPTY_STRING(argv[2])) { if (!STRCMP(argv[2], VIDEO_CODEC_NAME_H265)) { STRNCPY(videoCodec, VIDEO_CODEC_NAME_H265, STRLEN(VIDEO_CODEC_NAME_H265)); videoCodecID = VIDEO_CODEC_ID_H265; } } - if (argc >= 4 && STRLEN(argv[3]) > 0) { + if (argc >= 4 && !IS_EMPTY_STRING(argv[3])) { // Get the duration and convert to an integer CHK_STATUS(STRTOUI64(argv[3], NULL, 10, &streamingDuration)); streamingDuration *= HUNDREDS_OF_NANOS_IN_A_SECOND; } MEMSET(frameFilePath, 0x00, MAX_PATH_LEN + 1); - if (argc < 5 || STRLEN(argv[4]) == 0) { - STRCPY(frameFilePath, (PCHAR) "../samples/"); - } else { + if (argc >= 5 && !IS_EMPTY_STRING(argv[4])) { STRNCPY(frameFilePath, argv[4], MAX_PATH_LEN); + } else { + STRCPY(frameFilePath, (PCHAR) "../samples/"); } - if (argc >= 6 && STRLEN(argv[5]) > 0) { + if (argc >= 6 && !IS_EMPTY_STRING(argv[5])) { numMetadata = STRTOUL(argv[5], NULL, 10); + DLOGD("numMetadata: %d\n", numMetadata); + CHK(numMetadata <= MAX_METADATA_PER_FRAGMENT, STATUS_INVALID_ARG); } streamStopTime = GETTIME() + streamingDuration; From 59636246837a8641b53972d956f603ec438a55c1 Mon Sep 17 00:00:00 2001 From: Jeremy Gunawan Date: Tue, 5 Dec 2023 13:37:08 -0800 Subject: [PATCH 17/38] Small optimization --- samples/KvsVideoOnlyRealtimeStreamingSample.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/samples/KvsVideoOnlyRealtimeStreamingSample.c b/samples/KvsVideoOnlyRealtimeStreamingSample.c index 6d0a9f0b4..a7a02d335 100644 --- a/samples/KvsVideoOnlyRealtimeStreamingSample.c +++ b/samples/KvsVideoOnlyRealtimeStreamingSample.c @@ -113,7 +113,7 @@ INT32 main(INT32 argc, CHAR* argv[]) if (argc >= 6 && !IS_EMPTY_STRING(argv[5])) { numMetadata = STRTOUL(argv[5], NULL, 10); - DLOGD("numMetadata: %d\n", numMetadata); + DLOGE("numMetadata: %d\n", numMetadata); CHK(numMetadata <= MAX_METADATA_PER_FRAGMENT, STATUS_INVALID_ARG); } @@ -165,7 +165,7 @@ INT32 main(INT32 argc, CHAR* argv[]) // Add the fragment metadata key-value pairs // For limits, refer to https://docs.aws.amazon.com/kinesisvideostreams/latest/dg/limits.html#limits-streaming-metadata - if (frame.flags == FRAME_FLAG_KEY_FRAME) { + if (numMetadata > 0 && frame.flags == FRAME_FLAG_KEY_FRAME) { DLOGD("Adding metadata! frameIndex: %d", frame.index); for (n = 1; n <= numMetadata; n++) { SNPRINTF(metadataKey, METADATA_MAX_KEY_LENGTH, "TEST_KEY_%d", n); From 27d593b948def61d1f57270dea5d6971c05c0b31 Mon Sep 17 00:00:00 2001 From: Jeremy Gunawan Date: Tue, 5 Dec 2023 15:03:27 -0800 Subject: [PATCH 18/38] Update readme --- README.md | 11 +++++++++++ samples/KvsVideoOnlyRealtimeStreamingSample.c | 2 +- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index e49af0f3e..0d658c4ca 100644 --- a/README.md +++ b/README.md @@ -133,6 +133,17 @@ For audio only, run `./kvsAudioOnlyStreamingSample +``` + +`num-metadata` -- the number of sample fragment metadata key-value pairs that are added to each fragment. Min: 0, Max: 10. Default: 10. + ### Setting log levels diff --git a/samples/KvsVideoOnlyRealtimeStreamingSample.c b/samples/KvsVideoOnlyRealtimeStreamingSample.c index a7a02d335..2c8fe17ba 100644 --- a/samples/KvsVideoOnlyRealtimeStreamingSample.c +++ b/samples/KvsVideoOnlyRealtimeStreamingSample.c @@ -113,7 +113,7 @@ INT32 main(INT32 argc, CHAR* argv[]) if (argc >= 6 && !IS_EMPTY_STRING(argv[5])) { numMetadata = STRTOUL(argv[5], NULL, 10); - DLOGE("numMetadata: %d\n", numMetadata); + DLOGD("numMetadata: %d\n", numMetadata); CHK(numMetadata <= MAX_METADATA_PER_FRAGMENT, STATUS_INVALID_ARG); } From fd9e19f888a7347619964e975e154da0532f1f88 Mon Sep 17 00:00:00 2001 From: James Delaplane Date: Tue, 5 Dec 2023 15:13:23 -0800 Subject: [PATCH 19/38] Clang format --- src/source/Common/AwsV4Signer.c | 47 ++++++++++++++++++--------------- 1 file changed, 25 insertions(+), 22 deletions(-) diff --git a/src/source/Common/AwsV4Signer.c b/src/source/Common/AwsV4Signer.c index d41780a3a..1e4e44b76 100644 --- a/src/source/Common/AwsV4Signer.c +++ b/src/source/Common/AwsV4Signer.c @@ -73,7 +73,8 @@ STATUS generateAwsSigV4Signature(PRequestInfo pRequestInfo, PCHAR dateTimeStr, B hmacSize = SIZEOF(hmac); CHK_STATUS(generateRequestHmac((PBYTE) pScratchBuf, curSize, (PBYTE) dateTimeStr, SIGNATURE_DATE_STRING_LEN * SIZEOF(CHAR), hmac, &hmacSize)); CHK_STATUS(generateRequestHmac(hmac, hmacSize, (PBYTE) pRequestInfo->region, (UINT32) STRLEN(pRequestInfo->region), hmac, &hmacSize)); - CHK_STATUS(generateRequestHmac(hmac, hmacSize, (PBYTE) pRequestInfo->service, (UINT32) STRNLEN(pRequestInfo->service, MAX_SERVICE_NAME_LEN), hmac, &hmacSize)); + CHK_STATUS(generateRequestHmac(hmac, hmacSize, (PBYTE) pRequestInfo->service, (UINT32) STRNLEN(pRequestInfo->service, MAX_SERVICE_NAME_LEN), hmac, + &hmacSize)); CHK_STATUS(generateRequestHmac(hmac, hmacSize, (PBYTE) AWS_SIG_V4_SIGNATURE_END, (UINT32) STRLEN(AWS_SIG_V4_SIGNATURE_END), hmac, &hmacSize)); CHK_STATUS(generateRequestHmac(hmac, hmacSize, (PBYTE) pSignedStr, signedStrLen * SIZEOF(CHAR), hmac, &hmacSize)); @@ -117,7 +118,8 @@ STATUS signAwsRequestInfo(PRequestInfo pRequestInfo) CHK(pRequestInfo != NULL && pRequestInfo->pAwsCredentials != NULL, STATUS_NULL_ARG); - // signAwsRequestInfo is a public api function, if service is not specified default to "kinesisvideo" so no breaking changes are introduced to the api + // signAwsRequestInfo is a public api function, if service is not specified default to "kinesisvideo" so no breaking changes are introduced to the + // api if (pRequestInfo->service[0] == L'\0') { STRNCPY(pRequestInfo->service, KINESIS_VIDEO_SERVICE_NAME, MAX_SERVICE_NAME_LEN); } @@ -127,7 +129,7 @@ STATUS signAwsRequestInfo(PRequestInfo pRequestInfo) // Get the host header CHK_STATUS(getRequestHost(pRequestInfo->url, &pHostStart, &pHostEnd)); - len = (UINT32) (pHostEnd - pHostStart); + len = (UINT32)(pHostEnd - pHostStart); CHK_STATUS(setRequestHeader(pRequestInfo, AWS_SIG_V4_HEADER_HOST, 0, pHostStart, len)); CHK_STATUS(setRequestHeader(pRequestInfo, AWS_SIG_V4_HEADER_AMZ_DATE, 0, dateTimeStr, 0)); @@ -172,7 +174,8 @@ STATUS signAwsRequestInfoQueryParam(PRequestInfo pRequestInfo) CHK(pRequestInfo != NULL && pRequestInfo->pAwsCredentials != NULL, STATUS_NULL_ARG); - // signAwsRequestInfoQueryParam is a public api function, if service is not specified default to "kinesisvideo" so no breaking changes are introduced to the api + // signAwsRequestInfoQueryParam is a public api function, if service is not specified default to "kinesisvideo" so no breaking changes are + // introduced to the api if (pRequestInfo->service[0] == L'\0') { STRNCPY(pRequestInfo->service, KINESIS_VIDEO_SERVICE_NAME, MAX_SERVICE_NAME_LEN); } @@ -182,7 +185,7 @@ STATUS signAwsRequestInfoQueryParam(PRequestInfo pRequestInfo) // Need to add host header CHK_STATUS(getRequestHost(pRequestInfo->url, &pHostStart, &pHostEnd)); - len = (UINT32) (pHostEnd - pHostStart); + len = (UINT32)(pHostEnd - pHostStart); CHK_STATUS(setRequestHeader(pRequestInfo, AWS_SIG_V4_HEADER_HOST, 0, pHostStart, len)); // Encode the credentials scope @@ -202,7 +205,7 @@ STATUS signAwsRequestInfoQueryParam(PRequestInfo pRequestInfo) // Calculate the expiration in seconds expirationInSeconds = MIN(MAX_AWS_SIGV4_CREDENTIALS_EXPIRATION_IN_SECONDS, - (UINT32) ((pRequestInfo->pAwsCredentials->expiration - pRequestInfo->currentTime) / HUNDREDS_OF_NANOS_IN_A_SECOND)); + (UINT32)((pRequestInfo->pAwsCredentials->expiration - pRequestInfo->currentTime) / HUNDREDS_OF_NANOS_IN_A_SECOND)); expirationInSeconds = MAX(MIN_AWS_SIGV4_CREDENTIALS_EXPIRATION_IN_SECONDS, expirationInSeconds); // Add the params @@ -288,7 +291,7 @@ STATUS getCanonicalQueryParams(PCHAR pUrl, UINT32 urlLen, BOOL uriEncode, PCHAR* CHK_STATUS(singleListCreate(&pSingleList)); while (iterate) { - pQueryParamEnd = STRNCHR(pQueryParamStart, (UINT32) (pEndPtr - pQueryParamStart), '&'); + pQueryParamEnd = STRNCHR(pQueryParamStart, (UINT32)(pEndPtr - pQueryParamStart), '&'); if (pQueryParamEnd == NULL) { // break the loop iterate = FALSE; @@ -297,12 +300,12 @@ STATUS getCanonicalQueryParams(PCHAR pUrl, UINT32 urlLen, BOOL uriEncode, PCHAR* } // Process the resulting param name and value - CHK(NULL != (pParamValue = STRNCHR(pQueryParamStart, (UINT32) (pQueryParamEnd - pQueryParamStart), '=')), STATUS_INVALID_ARG); - nameLen = (UINT32) (pParamValue - pQueryParamStart); + CHK(NULL != (pParamValue = STRNCHR(pQueryParamStart, (UINT32)(pQueryParamEnd - pQueryParamStart), '=')), STATUS_INVALID_ARG); + nameLen = (UINT32)(pParamValue - pQueryParamStart); // Advance param start past '=' pParamValue++; - valueLen = (UINT32) (pQueryParamEnd - pParamValue); + valueLen = (UINT32)(pQueryParamEnd - pParamValue); // Max len is 3 times the size of the allocation to account for max bloat for encoding maxLen = MIN(MAX_URI_CHAR_LEN, nameLen + 1 + valueLen * 3 + 1); @@ -391,7 +394,7 @@ STATUS getCanonicalQueryParams(PCHAR pUrl, UINT32 urlLen, BOOL uriEncode, PCHAR* } *pCurPtr = '\0'; - queryLen = (UINT32) (pCurPtr - pQuery); + queryLen = (UINT32)(pCurPtr - pQuery); CleanUp: @@ -467,7 +470,7 @@ STATUS generateCanonicalRequestString(PRequestInfo pRequestInfo, PCHAR pRequestS // Get the canonical URI CHK_STATUS(getCanonicalUri(pRequestInfo->url, urlLen, &pUriStart, &pUriEnd, &defaultPath)); - len = defaultPath ? 1 : (UINT32) (pUriEnd - pUriStart); + len = defaultPath ? 1 : (UINT32)(pUriEnd - pUriStart); CHK(curLen + len + 1 <= requestLen, STATUS_BUFFER_TOO_SMALL); MEMCPY(pCurPtr, pUriStart, len * SIZEOF(CHAR)); @@ -482,7 +485,7 @@ STATUS generateCanonicalRequestString(PRequestInfo pRequestInfo, PCHAR pRequestS // The start of the query params is either end of the URI or ? so we skip one in that case pQueryStart = (pUriEnd == pQueryEnd) ? pUriEnd : pUriEnd + 1; - len = (UINT32) (pQueryEnd - pQueryStart); + len = (UINT32)(pQueryEnd - pQueryStart); CHK(curLen + len + 1 <= requestLen, STATUS_BUFFER_TOO_SMALL); MEMCPY(pCurPtr, pQueryStart, len * SIZEOF(CHAR)); pCurPtr += len; @@ -512,7 +515,7 @@ STATUS generateCanonicalRequestString(PRequestInfo pRequestInfo, PCHAR pRequestS // Streaming treats this portion as if the body were empty CHK_STATUS(hexEncodedSha256((PBYTE) EMPTY_STRING, 0, pCurPtr)); } else { - len = (UINT32) (ARRAY_SIZE(PREDEFINED_UNSIGNED_PAYLOAD) - 1); + len = (UINT32)(ARRAY_SIZE(PREDEFINED_UNSIGNED_PAYLOAD) - 1); MEMCPY(pCurPtr, PREDEFINED_UNSIGNED_PAYLOAD, SIZEOF(CHAR) * len); } } else { @@ -561,7 +564,7 @@ STATUS generateCanonicalHeaders(PRequestInfo pRequestInfo, PCHAR pCanonicalHeade // Process only if we have a canonical header name if (IS_CANONICAL_HEADER_NAME(pRequestHeader->pName)) { CHK_STATUS(TRIMSTRALL(pRequestHeader->pValue, pRequestHeader->valueLen, &pStart, &pEnd)); - valueLen = (UINT32) (pEnd - pStart); + valueLen = (UINT32)(pEnd - pStart); // Increase the overall length as we use the lower-case header, colon, trimmed lower-case value and new line overallLen += pRequestHeader->nameLen + 1 + valueLen + 1; @@ -673,7 +676,7 @@ STATUS generateSignatureDateTime(UINT64 currentTime, PCHAR pDateTimeStr) CHK(pDateTimeStr != NULL, STATUS_NULL_ARG); // Convert to time_t - timeT = (time_t) (currentTime / HUNDREDS_OF_NANOS_IN_A_SECOND); + timeT = (time_t)(currentTime / HUNDREDS_OF_NANOS_IN_A_SECOND); retSize = STRFTIME(pDateTimeStr, SIGNATURE_DATE_TIME_STRING_LEN, DATE_TIME_STRING_FORMAT, GMTIME(&timeT)); CHK(retSize > 0, STATUS_BUFFER_TOO_SMALL); pDateTimeStr[retSize] = '\0'; @@ -722,8 +725,8 @@ STATUS generateEncodedCredentials(PRequestInfo pRequestInfo, PCHAR dateTimeStr, CHK(pRequestInfo != NULL && dateTimeStr != NULL && pCredsLen != NULL, STATUS_NULL_ARG); // Calculate the max string length with '/' and a null terminator at the end - credsLen = MAX_ACCESS_KEY_LEN + 1 + SIGNATURE_DATE_TIME_STRING_LEN + 1 + MAX_REGION_NAME_LEN + 1 + (UINT32) STRNLEN(pRequestInfo->service, MAX_SERVICE_NAME_LEN) + - 1 + (UINT32) STRLEN(AWS_SIG_V4_SIGNATURE_END) + 1; + credsLen = MAX_ACCESS_KEY_LEN + 1 + SIGNATURE_DATE_TIME_STRING_LEN + 1 + MAX_REGION_NAME_LEN + 1 + + (UINT32) STRNLEN(pRequestInfo->service, MAX_SERVICE_NAME_LEN) + 1 + (UINT32) STRLEN(AWS_SIG_V4_SIGNATURE_END) + 1; // Early exit on buffer calculation CHK(pCreds != NULL, retStatus); @@ -831,7 +834,7 @@ STATUS getCanonicalUri(PCHAR pUrl, UINT32 len, PCHAR* ppStart, PCHAR* ppEnd, PBO } else if (*pCur == '/') { // This is the case of the path which we find pStart = pCur; - pEnd = STRNCHR(pCur, urlLen - (UINT32) (pCur - pUrl), '?'); + pEnd = STRNCHR(pCur, urlLen - (UINT32)(pCur - pUrl), '?'); iterate = FALSE; } @@ -875,7 +878,7 @@ STATUS uriEncodeString(PCHAR pSrc, UINT32 srcLen, PCHAR pDst, PUINT32 pDstLen) CHK(pSrc != NULL && pDstLen != NULL, STATUS_NULL_ARG); - while (((UINT32) (pCurPtr - pSrc) < strLen) && ((ch = *pCurPtr++) != '\0')) { + while (((UINT32)(pCurPtr - pSrc) < strLen) && ((ch = *pCurPtr++) != '\0')) { if ((ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z') || (ch >= '0' && ch <= '9') || ch == '_' || ch == '-' || ch == '~' || ch == '.') { encodedLen++; @@ -939,9 +942,9 @@ STATUS uriDecodeString(PCHAR pSrc, UINT32 srcLen, PCHAR pDst, PUINT32 pDstLen) CHK(pSrc != NULL && pDstLen != NULL, STATUS_NULL_ARG); - while (((UINT32) (pCurPtr - pSrc) < strLen) && ((ch = *pCurPtr) != '\0')) { + while (((UINT32)(pCurPtr - pSrc) < strLen) && ((ch = *pCurPtr) != '\0')) { if (ch == '%') { - CHK((UINT32) (pCurPtr - pSrc) + decLen <= strLen && *(pCurPtr + 1) != '\0' && *(pCurPtr + 2) != '\0', STATUS_INVALID_ARG); + CHK((UINT32)(pCurPtr - pSrc) + decLen <= strLen && *(pCurPtr + 1) != '\0' && *(pCurPtr + 2) != '\0', STATUS_INVALID_ARG); if (pDec != NULL) { size = remaining; CHK_STATUS(hexDecode(pCurPtr + 1, 2, (PBYTE) pDec, &size)); From eebe8a7b9d4caa955a9aee205237fea6e738a673 Mon Sep 17 00:00:00 2001 From: James Delaplane Date: Tue, 5 Dec 2023 15:42:22 -0800 Subject: [PATCH 20/38] Clang format, again --- src/source/Common/AwsV4Signer.c | 34 ++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/src/source/Common/AwsV4Signer.c b/src/source/Common/AwsV4Signer.c index 1e4e44b76..4c58739bf 100644 --- a/src/source/Common/AwsV4Signer.c +++ b/src/source/Common/AwsV4Signer.c @@ -129,7 +129,7 @@ STATUS signAwsRequestInfo(PRequestInfo pRequestInfo) // Get the host header CHK_STATUS(getRequestHost(pRequestInfo->url, &pHostStart, &pHostEnd)); - len = (UINT32)(pHostEnd - pHostStart); + len = (UINT32) (pHostEnd - pHostStart); CHK_STATUS(setRequestHeader(pRequestInfo, AWS_SIG_V4_HEADER_HOST, 0, pHostStart, len)); CHK_STATUS(setRequestHeader(pRequestInfo, AWS_SIG_V4_HEADER_AMZ_DATE, 0, dateTimeStr, 0)); @@ -185,7 +185,7 @@ STATUS signAwsRequestInfoQueryParam(PRequestInfo pRequestInfo) // Need to add host header CHK_STATUS(getRequestHost(pRequestInfo->url, &pHostStart, &pHostEnd)); - len = (UINT32)(pHostEnd - pHostStart); + len = (UINT32) (pHostEnd - pHostStart); CHK_STATUS(setRequestHeader(pRequestInfo, AWS_SIG_V4_HEADER_HOST, 0, pHostStart, len)); // Encode the credentials scope @@ -205,7 +205,7 @@ STATUS signAwsRequestInfoQueryParam(PRequestInfo pRequestInfo) // Calculate the expiration in seconds expirationInSeconds = MIN(MAX_AWS_SIGV4_CREDENTIALS_EXPIRATION_IN_SECONDS, - (UINT32)((pRequestInfo->pAwsCredentials->expiration - pRequestInfo->currentTime) / HUNDREDS_OF_NANOS_IN_A_SECOND)); + (UINT32) ((pRequestInfo->pAwsCredentials->expiration - pRequestInfo->currentTime) / HUNDREDS_OF_NANOS_IN_A_SECOND)); expirationInSeconds = MAX(MIN_AWS_SIGV4_CREDENTIALS_EXPIRATION_IN_SECONDS, expirationInSeconds); // Add the params @@ -291,7 +291,7 @@ STATUS getCanonicalQueryParams(PCHAR pUrl, UINT32 urlLen, BOOL uriEncode, PCHAR* CHK_STATUS(singleListCreate(&pSingleList)); while (iterate) { - pQueryParamEnd = STRNCHR(pQueryParamStart, (UINT32)(pEndPtr - pQueryParamStart), '&'); + pQueryParamEnd = STRNCHR(pQueryParamStart, (UINT32) (pEndPtr - pQueryParamStart), '&'); if (pQueryParamEnd == NULL) { // break the loop iterate = FALSE; @@ -300,12 +300,12 @@ STATUS getCanonicalQueryParams(PCHAR pUrl, UINT32 urlLen, BOOL uriEncode, PCHAR* } // Process the resulting param name and value - CHK(NULL != (pParamValue = STRNCHR(pQueryParamStart, (UINT32)(pQueryParamEnd - pQueryParamStart), '=')), STATUS_INVALID_ARG); - nameLen = (UINT32)(pParamValue - pQueryParamStart); + CHK(NULL != (pParamValue = STRNCHR(pQueryParamStart, (UINT32) (pQueryParamEnd - pQueryParamStart), '=')), STATUS_INVALID_ARG); + nameLen = (UINT32) (pParamValue - pQueryParamStart); // Advance param start past '=' pParamValue++; - valueLen = (UINT32)(pQueryParamEnd - pParamValue); + valueLen = (UINT32) (pQueryParamEnd - pParamValue); // Max len is 3 times the size of the allocation to account for max bloat for encoding maxLen = MIN(MAX_URI_CHAR_LEN, nameLen + 1 + valueLen * 3 + 1); @@ -394,7 +394,7 @@ STATUS getCanonicalQueryParams(PCHAR pUrl, UINT32 urlLen, BOOL uriEncode, PCHAR* } *pCurPtr = '\0'; - queryLen = (UINT32)(pCurPtr - pQuery); + queryLen = (UINT32) (pCurPtr - pQuery); CleanUp: @@ -470,7 +470,7 @@ STATUS generateCanonicalRequestString(PRequestInfo pRequestInfo, PCHAR pRequestS // Get the canonical URI CHK_STATUS(getCanonicalUri(pRequestInfo->url, urlLen, &pUriStart, &pUriEnd, &defaultPath)); - len = defaultPath ? 1 : (UINT32)(pUriEnd - pUriStart); + len = defaultPath ? 1 : (UINT32) (pUriEnd - pUriStart); CHK(curLen + len + 1 <= requestLen, STATUS_BUFFER_TOO_SMALL); MEMCPY(pCurPtr, pUriStart, len * SIZEOF(CHAR)); @@ -485,7 +485,7 @@ STATUS generateCanonicalRequestString(PRequestInfo pRequestInfo, PCHAR pRequestS // The start of the query params is either end of the URI or ? so we skip one in that case pQueryStart = (pUriEnd == pQueryEnd) ? pUriEnd : pUriEnd + 1; - len = (UINT32)(pQueryEnd - pQueryStart); + len = (UINT32) (pQueryEnd - pQueryStart); CHK(curLen + len + 1 <= requestLen, STATUS_BUFFER_TOO_SMALL); MEMCPY(pCurPtr, pQueryStart, len * SIZEOF(CHAR)); pCurPtr += len; @@ -515,7 +515,7 @@ STATUS generateCanonicalRequestString(PRequestInfo pRequestInfo, PCHAR pRequestS // Streaming treats this portion as if the body were empty CHK_STATUS(hexEncodedSha256((PBYTE) EMPTY_STRING, 0, pCurPtr)); } else { - len = (UINT32)(ARRAY_SIZE(PREDEFINED_UNSIGNED_PAYLOAD) - 1); + len = (UINT32) (ARRAY_SIZE(PREDEFINED_UNSIGNED_PAYLOAD) - 1); MEMCPY(pCurPtr, PREDEFINED_UNSIGNED_PAYLOAD, SIZEOF(CHAR) * len); } } else { @@ -564,7 +564,7 @@ STATUS generateCanonicalHeaders(PRequestInfo pRequestInfo, PCHAR pCanonicalHeade // Process only if we have a canonical header name if (IS_CANONICAL_HEADER_NAME(pRequestHeader->pName)) { CHK_STATUS(TRIMSTRALL(pRequestHeader->pValue, pRequestHeader->valueLen, &pStart, &pEnd)); - valueLen = (UINT32)(pEnd - pStart); + valueLen = (UINT32) (pEnd - pStart); // Increase the overall length as we use the lower-case header, colon, trimmed lower-case value and new line overallLen += pRequestHeader->nameLen + 1 + valueLen + 1; @@ -676,7 +676,7 @@ STATUS generateSignatureDateTime(UINT64 currentTime, PCHAR pDateTimeStr) CHK(pDateTimeStr != NULL, STATUS_NULL_ARG); // Convert to time_t - timeT = (time_t)(currentTime / HUNDREDS_OF_NANOS_IN_A_SECOND); + timeT = (time_t) (currentTime / HUNDREDS_OF_NANOS_IN_A_SECOND); retSize = STRFTIME(pDateTimeStr, SIGNATURE_DATE_TIME_STRING_LEN, DATE_TIME_STRING_FORMAT, GMTIME(&timeT)); CHK(retSize > 0, STATUS_BUFFER_TOO_SMALL); pDateTimeStr[retSize] = '\0'; @@ -834,7 +834,7 @@ STATUS getCanonicalUri(PCHAR pUrl, UINT32 len, PCHAR* ppStart, PCHAR* ppEnd, PBO } else if (*pCur == '/') { // This is the case of the path which we find pStart = pCur; - pEnd = STRNCHR(pCur, urlLen - (UINT32)(pCur - pUrl), '?'); + pEnd = STRNCHR(pCur, urlLen - (UINT32) (pCur - pUrl), '?'); iterate = FALSE; } @@ -878,7 +878,7 @@ STATUS uriEncodeString(PCHAR pSrc, UINT32 srcLen, PCHAR pDst, PUINT32 pDstLen) CHK(pSrc != NULL && pDstLen != NULL, STATUS_NULL_ARG); - while (((UINT32)(pCurPtr - pSrc) < strLen) && ((ch = *pCurPtr++) != '\0')) { + while (((UINT32) (pCurPtr - pSrc) < strLen) && ((ch = *pCurPtr++) != '\0')) { if ((ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z') || (ch >= '0' && ch <= '9') || ch == '_' || ch == '-' || ch == '~' || ch == '.') { encodedLen++; @@ -942,9 +942,9 @@ STATUS uriDecodeString(PCHAR pSrc, UINT32 srcLen, PCHAR pDst, PUINT32 pDstLen) CHK(pSrc != NULL && pDstLen != NULL, STATUS_NULL_ARG); - while (((UINT32)(pCurPtr - pSrc) < strLen) && ((ch = *pCurPtr) != '\0')) { + while (((UINT32) (pCurPtr - pSrc) < strLen) && ((ch = *pCurPtr) != '\0')) { if (ch == '%') { - CHK((UINT32)(pCurPtr - pSrc) + decLen <= strLen && *(pCurPtr + 1) != '\0' && *(pCurPtr + 2) != '\0', STATUS_INVALID_ARG); + CHK((UINT32) (pCurPtr - pSrc) + decLen <= strLen && *(pCurPtr + 1) != '\0' && *(pCurPtr + 2) != '\0', STATUS_INVALID_ARG); if (pDec != NULL) { size = remaining; CHK_STATUS(hexDecode(pCurPtr + 1, 2, (PBYTE) pDec, &size)); From 70a42f71a0a72f1468158d2c002e064def7549b8 Mon Sep 17 00:00:00 2001 From: Stefan Kieszkowski <85728496+stefankiesz@users.noreply.github.com> Date: Wed, 6 Dec 2023 10:12:38 -0800 Subject: [PATCH 21/38] Add gcc paths (#420) --- .github/workflows/ci.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index eabeb418d..5ce565edb 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -29,8 +29,8 @@ jobs: id-token: write contents: read env: - CC: gcc - CXX: g++ + CC: /usr/local/bin/gcc-13 + CXX: /usr/local/bin/g++-13 AWS_KVS_LOG_LEVEL: 2 steps: - name: Clone repository @@ -120,8 +120,8 @@ jobs: id-token: write contents: read env: - CC: gcc - CXX: g++ + CC: /usr/local/bin/gcc-13 + CXX: /usr/local/bin/g++-13 AWS_KVS_LOG_LEVEL: 2 LDFLAGS: -L/usr/local/opt/openssl@3/lib CPPFLAGS: -I/usr/local/opt/openssl@3/include From 94b9b0cf8f861b512b0ab07df03a3443b3887ac4 Mon Sep 17 00:00:00 2001 From: Stefan Kieszkowski <85728496+stefankiesz@users.noreply.github.com> Date: Wed, 6 Dec 2023 10:23:48 -0800 Subject: [PATCH 22/38] spotaws PR #226 (#423) * proper versioned shared libs * Fix version * Update to latest version * Exclude shared builds from windows --------- Co-authored-by: Tom spot Callaway Co-authored-by: Divya Sampath Kumar Co-authored-by: Hassan Sahibzada --- CMakeLists.txt | 36 +++++++++++++++++++++++++++++------- libcproducer.pc.cmake | 2 +- libkvsCommonCurl.pc.cmake | 2 +- libkvsCommonLws.pc.cmake | 2 +- 4 files changed, 32 insertions(+), 10 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index cd6bc22cc..8310dd98f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -3,6 +3,11 @@ set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/CMake;${CMAKE_MODULE_PATH}") include(Utilities) project(KinesisVideoProducerC VERSION 1.5.1 LANGUAGES C) +set(KINESIS_VIDEO_PRODUCER_C_MAJOR_VERSION 1) +set(KINESIS_VIDEO_PRODUCER_C_MINOR_VERSION 5) +set(KINESIS_VIDEO_PRODUCER_C_PATCH_VERSION 1) +set(KINESIS_VIDEO_PRODUCER_C_VERSION ${KINESIS_VIDEO_PRODUCER_C_MAJOR_VERSION}.${KINESIS_VIDEO_PRODUCER_C_MINOR_VERSION}.${KINESIS_VIDEO_PRODUCER_C_PATCH_VERSION}) + include(GNUInstallDirs) @@ -29,6 +34,11 @@ option(UNDEFINED_BEHAVIOR_SANITIZER "Build with UndefinedBehaviorSanitizer." OFF option(ALIGNED_MEMORY_MODEL "Aligned memory model ONLY." OFF) option(SET_SSL_CALLBACKS "Set SSL thread and lock callbacks." OFF) +set(TYPE_OF_LIB SHARED) +if (BUILD_STATIC) + set(TYPE_OF_LIB STATIC) +endif() + set(CMAKE_MACOSX_RPATH TRUE) get_filename_component(ROOT "${CMAKE_CURRENT_SOURCE_DIR}" ABSOLUTE) @@ -262,8 +272,15 @@ if(BUILD_COMMON_LWS) "${CMAKE_CURRENT_SOURCE_DIR}/libkvsCommonLws.pc.cmake" "${CMAKE_CURRENT_BINARY_DIR}/libkvsCommonLws.pc" @ONLY) - add_library(kvsCommonLws STATIC ${KVS_COMMON_SOURCE_FILES_BASE} ${KVS_COMMON_SOURCE_FILES_LWS}) + if (WIN32) + add_library(kvsCommonLws STATIC ${KVS_COMMON_SOURCE_FILES_BASE} ${KVS_COMMON_SOURCE_FILES_LWS}) + else() + add_library(kvsCommonLws ${TYPE_OF_LIB} ${KVS_COMMON_SOURCE_FILES_BASE} ${KVS_COMMON_SOURCE_FILES_LWS}) + endif() target_compile_definitions(kvsCommonLws PRIVATE KVS_BUILD_WITH_LWS ${CPRODUCER_COMMON_TLS_OPTION}) + if(NOT BUILD_STATIC) + set_target_properties(kvsCommonLws PROPERTIES VERSION ${KINESIS_VIDEO_PRODUCER_C_VERSION} SOVERSION ${KINESIS_VIDEO_PRODUCER_C_MAJOR_VERSION}) + endif() target_link_libraries(kvsCommonLws ${OPENSSL_CRYPTO_LIBRARY} ${OPENSSL_SSL_LIBRARY} @@ -295,8 +312,15 @@ if(BUILD_COMMON_CURL) endif() endif() - add_library(kvsCommonCurl STATIC ${KVS_COMMON_SOURCE_FILES_BASE} ${KVS_COMMON_SOURCE_FILES_CURL}) + if (WIN32) + add_library(kvsCommonCurl STATIC ${KVS_COMMON_SOURCE_FILES_BASE} ${KVS_COMMON_SOURCE_FILES_CURL}) + else() + add_library(kvsCommonCurl ${TYPE_OF_LIB} ${KVS_COMMON_SOURCE_FILES_BASE} ${KVS_COMMON_SOURCE_FILES_CURL}) + endif() target_compile_definitions(kvsCommonCurl PRIVATE KVS_BUILD_WITH_CURL ${CPRODUCER_COMMON_TLS_OPTION}) + if(NOT BUILD_STATIC) + set_target_properties(kvsCommonCurl PROPERTIES VERSION ${KINESIS_VIDEO_PRODUCER_C_VERSION} SOVERSION ${KINESIS_VIDEO_PRODUCER_C_MAJOR_VERSION}) + endif() target_link_libraries(kvsCommonCurl kvspicUtils ${CURL_LIBRARIES} @@ -315,16 +339,14 @@ if(BUILD_COMMON_CURL) "${CMAKE_CURRENT_SOURCE_DIR}/libcproducer.pc.cmake" "${CMAKE_CURRENT_BINARY_DIR}/libcproducer.pc" @ONLY) - set(TYPE_OF_LIB SHARED) - if (BUILD_STATIC) - set(TYPE_OF_LIB STATIC) - endif() - if (WIN32) add_library(cproducer STATIC ${PRODUCER_C_SOURCE_FILES}) else() add_library(cproducer ${TYPE_OF_LIB} ${PRODUCER_C_SOURCE_FILES}) endif() + if(NOT BUILD_STATIC) + set_target_properties(cproducer PROPERTIES VERSION ${KINESIS_VIDEO_PRODUCER_C_VERSION} SOVERSION ${KINESIS_VIDEO_PRODUCER_C_MAJOR_VERSION}) + endif() target_link_libraries(cproducer PUBLIC kvsCommonCurl kvspic) install( diff --git a/libcproducer.pc.cmake b/libcproducer.pc.cmake index 6391fd051..f4bfe45bb 100644 --- a/libcproducer.pc.cmake +++ b/libcproducer.pc.cmake @@ -5,6 +5,6 @@ libdir=${exec_prefix}/@CMAKE_INSTALL_LIBDIR@ Name: KVS-libcproducer Description: KVS C Producer library -Version: 0.0.0 +Version: @KINESIS_VIDEO_PRODUCER_C_VERSION@ Cflags: -I${includedir} Libs: -L${libdir} -lcproducer diff --git a/libkvsCommonCurl.pc.cmake b/libkvsCommonCurl.pc.cmake index 86325a40f..b2a3e47bd 100644 --- a/libkvsCommonCurl.pc.cmake +++ b/libkvsCommonCurl.pc.cmake @@ -5,6 +5,6 @@ libdir=${exec_prefix}/@CMAKE_INSTALL_LIBDIR@ Name: KVS-libkvsCommonCurl Description: KVS C Producer common curl library -Version: 0.0.0 +Version: @KINESIS_VIDEO_PRODUCER_C_VERSION@ Cflags: -I${includedir} Libs: -L${libdir} -lkvsCommonCurl diff --git a/libkvsCommonLws.pc.cmake b/libkvsCommonLws.pc.cmake index 975ae9c9e..04cb0f153 100644 --- a/libkvsCommonLws.pc.cmake +++ b/libkvsCommonLws.pc.cmake @@ -5,6 +5,6 @@ libdir=${exec_prefix}/@CMAKE_INSTALL_LIBDIR@ Name: KVS-libkvsCommonLws Description: KVS C Producer common libwebsockets library -Version: 0.0.0 +Version: @KINESIS_VIDEO_PRODUCER_C_VERSION@ Cflags: -I${includedir} Libs: -L${libdir} -lkvsCommonLws From ed2be37065ab1523bf7e5e15904ed49166c9c1fa Mon Sep 17 00:00:00 2001 From: Jeremy Gunawan Date: Wed, 6 Dec 2023 23:07:54 -0800 Subject: [PATCH 23/38] Revert "Pr 323" --- .../amazonaws/kinesis/video/common/Include.h | 6 ---- src/source/Common/AwsV4Signer.c | 34 +++++-------------- src/source/Common/AwsV4Signer.h | 6 ++-- 3 files changed, 10 insertions(+), 36 deletions(-) diff --git a/src/include/com/amazonaws/kinesis/video/common/Include.h b/src/include/com/amazonaws/kinesis/video/common/Include.h index f718a46eb..730b0b2bb 100644 --- a/src/include/com/amazonaws/kinesis/video/common/Include.h +++ b/src/include/com/amazonaws/kinesis/video/common/Include.h @@ -98,11 +98,6 @@ extern "C" { */ #define MAX_REGION_NAME_LEN 128 -/** - * Maximum allowed service name length - */ -#define MAX_SERVICE_NAME_LEN 128 - /** * Maximum allowed user agent string length */ @@ -488,7 +483,6 @@ struct __RequestInfo { //!< NOTE: The body will follow the main struct UINT32 bodySize; //!< Size of the body in bytes CHAR url[MAX_URI_CHAR_LEN + 1]; //!< The URL for the request - CHAR service[MAX_SERVICE_NAME_LEN + 1]; //!< The AWS service for the request CHAR certPath[MAX_PATH_LEN + 1]; //!< CA Certificate path to use - optional CHAR sslCertPath[MAX_PATH_LEN + 1]; //!< SSL Certificate file path to use - optional CHAR sslPrivateKeyPath[MAX_PATH_LEN + 1]; //!< SSL Certificate private key file path to use - optional diff --git a/src/source/Common/AwsV4Signer.c b/src/source/Common/AwsV4Signer.c index 4c58739bf..3eddaf44c 100644 --- a/src/source/Common/AwsV4Signer.c +++ b/src/source/Common/AwsV4Signer.c @@ -73,8 +73,7 @@ STATUS generateAwsSigV4Signature(PRequestInfo pRequestInfo, PCHAR dateTimeStr, B hmacSize = SIZEOF(hmac); CHK_STATUS(generateRequestHmac((PBYTE) pScratchBuf, curSize, (PBYTE) dateTimeStr, SIGNATURE_DATE_STRING_LEN * SIZEOF(CHAR), hmac, &hmacSize)); CHK_STATUS(generateRequestHmac(hmac, hmacSize, (PBYTE) pRequestInfo->region, (UINT32) STRLEN(pRequestInfo->region), hmac, &hmacSize)); - CHK_STATUS(generateRequestHmac(hmac, hmacSize, (PBYTE) pRequestInfo->service, (UINT32) STRNLEN(pRequestInfo->service, MAX_SERVICE_NAME_LEN), hmac, - &hmacSize)); + CHK_STATUS(generateRequestHmac(hmac, hmacSize, (PBYTE) KINESIS_VIDEO_SERVICE_NAME, (UINT32) STRLEN(KINESIS_VIDEO_SERVICE_NAME), hmac, &hmacSize)); CHK_STATUS(generateRequestHmac(hmac, hmacSize, (PBYTE) AWS_SIG_V4_SIGNATURE_END, (UINT32) STRLEN(AWS_SIG_V4_SIGNATURE_END), hmac, &hmacSize)); CHK_STATUS(generateRequestHmac(hmac, hmacSize, (PBYTE) pSignedStr, signedStrLen * SIZEOF(CHAR), hmac, &hmacSize)); @@ -118,12 +117,6 @@ STATUS signAwsRequestInfo(PRequestInfo pRequestInfo) CHK(pRequestInfo != NULL && pRequestInfo->pAwsCredentials != NULL, STATUS_NULL_ARG); - // signAwsRequestInfo is a public api function, if service is not specified default to "kinesisvideo" so no breaking changes are introduced to the - // api - if (pRequestInfo->service[0] == L'\0') { - STRNCPY(pRequestInfo->service, KINESIS_VIDEO_SERVICE_NAME, MAX_SERVICE_NAME_LEN); - } - // Generate the time CHK_STATUS(generateSignatureDateTime(pRequestInfo->currentTime, dateTimeStr)); @@ -174,12 +167,6 @@ STATUS signAwsRequestInfoQueryParam(PRequestInfo pRequestInfo) CHK(pRequestInfo != NULL && pRequestInfo->pAwsCredentials != NULL, STATUS_NULL_ARG); - // signAwsRequestInfoQueryParam is a public api function, if service is not specified default to "kinesisvideo" so no breaking changes are - // introduced to the api - if (pRequestInfo->service[0] == L'\0') { - STRNCPY(pRequestInfo->service, KINESIS_VIDEO_SERVICE_NAME, MAX_SERVICE_NAME_LEN); - } - // Generate the time CHK_STATUS(generateSignatureDateTime(pRequestInfo->currentTime, dateTimeStr)); @@ -511,13 +498,8 @@ STATUS generateCanonicalRequestString(PRequestInfo pRequestInfo, PCHAR pRequestS len = SHA256_DIGEST_LENGTH * 2; CHK(curLen + len <= requestLen, STATUS_BUFFER_TOO_SMALL); if (pRequestInfo->body == NULL) { - if (STRNCMP(pRequestInfo->service, KINESIS_VIDEO_SERVICE_NAME, MAX_SERVICE_NAME_LEN) == 0) { - // Streaming treats this portion as if the body were empty - CHK_STATUS(hexEncodedSha256((PBYTE) EMPTY_STRING, 0, pCurPtr)); - } else { - len = (UINT32) (ARRAY_SIZE(PREDEFINED_UNSIGNED_PAYLOAD) - 1); - MEMCPY(pCurPtr, PREDEFINED_UNSIGNED_PAYLOAD, SIZEOF(CHAR) * len); - } + // Streaming treats this portion as if the body were empty + CHK_STATUS(hexEncodedSha256((PBYTE) EMPTY_STRING, 0, pCurPtr)); } else { // standard signing CHK_STATUS(hexEncodedSha256((PBYTE) pRequestInfo->body, pRequestInfo->bodySize, pCurPtr)); @@ -696,14 +678,14 @@ STATUS generateCredentialScope(PRequestInfo pRequestInfo, PCHAR dateTimeStr, PCH CHK(pRequestInfo != NULL && dateTimeStr != NULL && pScopeLen != NULL, STATUS_NULL_ARG); // Calculate the max string length with a null terminator at the end - scopeLen = SIGNATURE_DATE_TIME_STRING_LEN + 1 + MAX_REGION_NAME_LEN + 1 + (UINT32) STRNLEN(pRequestInfo->service, MAX_SERVICE_NAME_LEN) + 1 + + scopeLen = SIGNATURE_DATE_TIME_STRING_LEN + 1 + MAX_REGION_NAME_LEN + 1 + (UINT32) STRLEN(KINESIS_VIDEO_SERVICE_NAME) + 1 + (UINT32) STRLEN(AWS_SIG_V4_SIGNATURE_END) + 1; // Early exit on buffer calculation CHK(pScope != NULL, retStatus); scopeLen = (UINT32) SNPRINTF(pScope, *pScopeLen, CREDENTIAL_SCOPE_TEMPLATE, SIGNATURE_DATE_STRING_LEN, dateTimeStr, pRequestInfo->region, - pRequestInfo->service, AWS_SIG_V4_SIGNATURE_END); + KINESIS_VIDEO_SERVICE_NAME, AWS_SIG_V4_SIGNATURE_END); CHK(scopeLen > 0 && scopeLen <= *pScopeLen, STATUS_BUFFER_TOO_SMALL); CleanUp: @@ -725,15 +707,15 @@ STATUS generateEncodedCredentials(PRequestInfo pRequestInfo, PCHAR dateTimeStr, CHK(pRequestInfo != NULL && dateTimeStr != NULL && pCredsLen != NULL, STATUS_NULL_ARG); // Calculate the max string length with '/' and a null terminator at the end - credsLen = MAX_ACCESS_KEY_LEN + 1 + SIGNATURE_DATE_TIME_STRING_LEN + 1 + MAX_REGION_NAME_LEN + 1 + - (UINT32) STRNLEN(pRequestInfo->service, MAX_SERVICE_NAME_LEN) + 1 + (UINT32) STRLEN(AWS_SIG_V4_SIGNATURE_END) + 1; + credsLen = MAX_ACCESS_KEY_LEN + 1 + SIGNATURE_DATE_TIME_STRING_LEN + 1 + MAX_REGION_NAME_LEN + 1 + (UINT32) STRLEN(KINESIS_VIDEO_SERVICE_NAME) + + 1 + (UINT32) STRLEN(AWS_SIG_V4_SIGNATURE_END) + 1; // Early exit on buffer calculation CHK(pCreds != NULL, retStatus); credsLen = (UINT32) SNPRINTF(pCreds, *pCredsLen, URL_ENCODED_CREDENTIAL_TEMPLATE, pRequestInfo->pAwsCredentials->accessKeyIdLen, pRequestInfo->pAwsCredentials->accessKeyId, SIGNATURE_DATE_STRING_LEN, dateTimeStr, pRequestInfo->region, - pRequestInfo->service, AWS_SIG_V4_SIGNATURE_END); + KINESIS_VIDEO_SERVICE_NAME, AWS_SIG_V4_SIGNATURE_END); CHK(credsLen > 0 && credsLen <= *pCredsLen, STATUS_BUFFER_TOO_SMALL); CleanUp: diff --git a/src/source/Common/AwsV4Signer.h b/src/source/Common/AwsV4Signer.h index ea9a7f622..951c7f068 100644 --- a/src/source/Common/AwsV4Signer.h +++ b/src/source/Common/AwsV4Signer.h @@ -51,14 +51,14 @@ extern "C" { #define AUTH_HEADER_TEMPLATE "%s Credential=%.*s/%s, SignedHeaders=%.*s, Signature=%s" // Authentication query template -#define AUTH_QUERY_TEMPLATE "?X-Amz-Algorithm=%s&X-Amz-Credential=%s&X-Amz-Date=%s&X-Amz-Expires=%u&X-Amz-SignedHeaders=%.*s" +#define AUTH_QUERY_TEMPLATE "&X-Amz-Algorithm=%s&X-Amz-Credential=%s&X-Amz-Date=%s&X-Amz-Expires=%u&X-Amz-SignedHeaders=%.*s" // Token query param template #define SECURITY_TOKEN_PARAM_TEMPLATE "&X-Amz-Security-Token=%s" // Authentication query template #define AUTH_QUERY_TEMPLATE_WITH_TOKEN \ - "?X-Amz-Algorithm=%s&X-Amz-Credential=%s&X-Amz-Date=%s&X-Amz-Expires=%u&X-Amz-SignedHeaders=%.*s" SECURITY_TOKEN_PARAM_TEMPLATE + "&X-Amz-Algorithm=%s&X-Amz-Credential=%s&X-Amz-Date=%s&X-Amz-Expires=%u&X-Amz-SignedHeaders=%.*s" SECURITY_TOKEN_PARAM_TEMPLATE // Signature query param template #define SIGNATURE_PARAM_TEMPLATE "&X-Amz-Signature=%s" @@ -82,8 +82,6 @@ extern "C" { #define KVS_MAX_HMAC_SIZE 64 -#define PREDEFINED_UNSIGNED_PAYLOAD "UNSIGNED-PAYLOAD" - //////////////////////////////////////////////////// // Function definitions //////////////////////////////////////////////////// From d94c0fd0686d841191a9254dea62d9282c5d2e6f Mon Sep 17 00:00:00 2001 From: jdelapla Date: Thu, 7 Dec 2023 07:28:51 -0800 Subject: [PATCH 24/38] Added Iot credential usage to producer c samples (#220) * Added Iot credential usage to producer c samples * Refactor samples to include IoT usage and update readme --------- Co-authored-by: delaplan Co-authored-by: Divya Sampath Kumar --- README.md | 9 ++ samples/KvsAudioOnlyStreamingSample.c | 50 +++++--- samples/KvsAudioVideoStreamingSample.c | 56 ++++++--- samples/KvsVideoOnlyOfflineStreamingSample.c | 39 ++++-- samples/KvsVideoOnlyRealtimeStreamingSample.c | 45 +++++-- samples/Samples.h | 28 +++++ scripts/generate-iot-credential.sh | 116 ++++++++++++++++++ 7 files changed, 293 insertions(+), 50 deletions(-) create mode 100644 samples/Samples.h create mode 100755 scripts/generate-iot-credential.sh diff --git a/README.md b/README.md index 0d658c4ca..ff2bd3464 100644 --- a/README.md +++ b/README.md @@ -133,6 +133,15 @@ For audio only, run `./kvsAudioOnlyStreamingSample +#include "Samples.h" #define DEFAULT_RETENTION_PERIOD 2 * HUNDREDS_OF_NANOS_IN_AN_HOUR #define DEFAULT_BUFFER_DURATION 120 * HUNDREDS_OF_NANOS_IN_A_SECOND @@ -15,8 +15,7 @@ #define NUMBER_OF_AUDIO_FRAME_FILES 582 -#define FILE_LOGGING_BUFFER_SIZE (100 * 1024) -#define MAX_NUMBER_OF_LOG_FILES 5 +// #define IOT_CORE_ENABLE_CREDENTIALS 1 typedef struct { PBYTE buffer; @@ -110,21 +109,32 @@ INT32 main(INT32 argc, CHAR* argv[]) STRNCPY(audioCodec, AUDIO_CODEC_NAME_AAC, STRLEN(AUDIO_CODEC_NAME_AAC)); // aac audio by default - if (argc >= 5) { - if (!STRCMP(argv[2], AUDIO_CODEC_NAME_ALAW)) { - STRNCPY(audioCodec, AUDIO_CODEC_NAME_ALAW, STRLEN(AUDIO_CODEC_NAME_ALAW)); - } - } +#ifdef IOT_CORE_ENABLE_CREDENTIALS + PCHAR pIotCoreCredentialEndpoint, pIotCoreCert, pIotCorePrivateKey, pIotCoreRoleAlias, pIotCoreThingName; + CHK_ERR((pIotCoreCredentialEndpoint = GETENV(IOT_CORE_CREDENTIAL_ENDPOINT)) != NULL, STATUS_INVALID_OPERATION, + "AWS_IOT_CORE_CREDENTIAL_ENDPOINT must be set"); + CHK_ERR((pIotCoreCert = GETENV(IOT_CORE_CERT)) != NULL, STATUS_INVALID_OPERATION, "AWS_IOT_CORE_CERT must be set"); + CHK_ERR((pIotCorePrivateKey = GETENV(IOT_CORE_PRIVATE_KEY)) != NULL, STATUS_INVALID_OPERATION, "AWS_IOT_CORE_PRIVATE_KEY must be set"); + CHK_ERR((pIotCoreRoleAlias = GETENV(IOT_CORE_ROLE_ALIAS)) != NULL, STATUS_INVALID_OPERATION, "AWS_IOT_CORE_ROLE_ALIAS must be set"); + CHK_ERR((pIotCoreThingName = GETENV(IOT_CORE_THING_NAME)) != NULL, STATUS_INVALID_OPERATION, "AWS_IOT_CORE_THING_NAME must be set"); +#else if (argc < 2) { printf("Usage: AWS_ACCESS_KEY_ID=SAMPLEKEY AWS_SECRET_ACCESS_KEY=SAMPLESECRET %s \n", argv[0]); CHK(FALSE, STATUS_INVALID_ARG); } - - if ((accessKey = getenv(ACCESS_KEY_ENV_VAR)) == NULL || (secretKey = getenv(SECRET_KEY_ENV_VAR)) == NULL) { + if ((accessKey = GETENV(ACCESS_KEY_ENV_VAR)) == NULL || (secretKey = GETENV(SECRET_KEY_ENV_VAR)) == NULL) { printf("Error missing credentials\n"); CHK(FALSE, STATUS_INVALID_ARG); } + sessionToken = GETENV(SESSION_TOKEN_ENV_VAR); +#endif + + if (argc >= 5) { + if (!STRCMP(argv[2], AUDIO_CODEC_NAME_ALAW)) { + STRNCPY(audioCodec, AUDIO_CODEC_NAME_ALAW, STRLEN(AUDIO_CODEC_NAME_ALAW)); + } + } MEMSET(data.sampleDir, 0x00, MAX_PATH_LEN + 1); if (argc < 4) { @@ -146,10 +156,16 @@ INT32 main(INT32 argc, CHAR* argv[]) } printf("Done loading audio frames.\n"); - cacertPath = getenv(CACERT_PATH_ENV_VAR); - sessionToken = getenv(SESSION_TOKEN_ENV_VAR); + cacertPath = GETENV(CACERT_PATH_ENV_VAR); + sessionToken = GETENV(SESSION_TOKEN_ENV_VAR); + +#ifdef IOT_CORE_ENABLE_CREDENTIALS + streamName = pIotCoreThingName; +#else streamName = argv[1]; - if ((region = getenv(DEFAULT_REGION_ENV_VAR)) == NULL) { +#endif + + if ((region = GETENV(DEFAULT_REGION_ENV_VAR)) == NULL) { region = (PCHAR) DEFAULT_AWS_REGION; } @@ -202,10 +218,16 @@ INT32 main(INT32 argc, CHAR* argv[]) data.startTime = GETTIME(); data.firstFrame = TRUE; + +#ifdef IOT_CORE_ENABLE_CREDENTIALS + CHK_STATUS(createDefaultCallbacksProviderWithIotCertificate(pIotCoreCredentialEndpoint, pIotCoreCert, pIotCorePrivateKey, cacertPath, + pIotCoreRoleAlias, pIotCoreThingName, region, NULL, NULL, &pClientCallbacks)); +#else CHK_STATUS(createDefaultCallbacksProviderWithAwsCredentials(accessKey, secretKey, sessionToken, MAX_UINT64, region, cacertPath, NULL, NULL, &pClientCallbacks)); +#endif - if (NULL != getenv(ENABLE_FILE_LOGGING)) { + if (NULL != GETENV(ENABLE_FILE_LOGGING)) { if ((retStatus = addFileLoggerPlatformCallbacksProvider(pClientCallbacks, FILE_LOGGING_BUFFER_SIZE, MAX_NUMBER_OF_LOG_FILES, (PCHAR) FILE_LOGGER_LOG_FILE_DIRECTORY_PATH, TRUE) != STATUS_SUCCESS)) { printf("File logging enable option failed with 0x%08x error code\n", retStatus); diff --git a/samples/KvsAudioVideoStreamingSample.c b/samples/KvsAudioVideoStreamingSample.c index 7f001aa37..84f299a5a 100644 --- a/samples/KvsAudioVideoStreamingSample.c +++ b/samples/KvsAudioVideoStreamingSample.c @@ -1,4 +1,4 @@ -#include +#include "Samples.h" #define DEFAULT_RETENTION_PERIOD 2 * HUNDREDS_OF_NANOS_IN_AN_HOUR #define DEFAULT_BUFFER_DURATION 120 * HUNDREDS_OF_NANOS_IN_A_SECOND @@ -22,8 +22,7 @@ #define NUMBER_OF_VIDEO_FRAME_FILES 403 #define NUMBER_OF_AUDIO_FRAME_FILES 582 -#define FILE_LOGGING_BUFFER_SIZE (100 * 1024) -#define MAX_NUMBER_OF_LOG_FILES 5 +// #define IOT_CORE_ENABLE_CREDENTIALS 1 UINT8 gEventsEnabled = 0; @@ -195,6 +194,28 @@ INT32 main(INT32 argc, CHAR* argv[]) STRNCPY(audioCodec, AUDIO_CODEC_NAME_AAC, STRLEN(AUDIO_CODEC_NAME_AAC)); // aac audio by default STRNCPY(videoCodec, VIDEO_CODEC_NAME_H264, STRLEN(VIDEO_CODEC_NAME_H264)); // h264 video by default +#ifdef IOT_CORE_ENABLE_CREDENTIALS + PCHAR pIotCoreCredentialEndpoint, pIotCoreCert, pIotCorePrivateKey, pIotCoreRoleAlias, pIotCoreThingName; + CHK_ERR((pIotCoreCredentialEndpoint = GETENV(IOT_CORE_CREDENTIAL_ENDPOINT)) != NULL, STATUS_INVALID_OPERATION, + "AWS_IOT_CORE_CREDENTIAL_ENDPOINT must be set"); + CHK_ERR((pIotCoreCert = GETENV(IOT_CORE_CERT)) != NULL, STATUS_INVALID_OPERATION, "AWS_IOT_CORE_CERT must be set"); + CHK_ERR((pIotCorePrivateKey = GETENV(IOT_CORE_PRIVATE_KEY)) != NULL, STATUS_INVALID_OPERATION, "AWS_IOT_CORE_PRIVATE_KEY must be set"); + CHK_ERR((pIotCoreRoleAlias = GETENV(IOT_CORE_ROLE_ALIAS)) != NULL, STATUS_INVALID_OPERATION, "AWS_IOT_CORE_ROLE_ALIAS must be set"); + CHK_ERR((pIotCoreThingName = GETENV(IOT_CORE_THING_NAME)) != NULL, STATUS_INVALID_OPERATION, "AWS_IOT_CORE_THING_NAME must be set"); +#else + if (argc < 2) { + printf("Usage: AWS_ACCESS_KEY_ID=SAMPLEKEY AWS_SECRET_ACCESS_KEY=SAMPLESECRET %s " + "[audio_codec] [video_codec] [events_enabled]\n", + argv[0]); + CHK(FALSE, STATUS_INVALID_ARG); + } + if ((accessKey = GETENV(ACCESS_KEY_ENV_VAR)) == NULL || (secretKey = GETENV(SECRET_KEY_ENV_VAR)) == NULL) { + printf("Error missing credentials\n"); + CHK(FALSE, STATUS_INVALID_ARG); + } + sessionToken = GETENV(SESSION_TOKEN_ENV_VAR); +#endif + if (argc == 7) { if (!STRCMP(argv[6], "1")) { gEventsEnabled = 1; @@ -209,17 +230,6 @@ INT32 main(INT32 argc, CHAR* argv[]) videoCodecID = VIDEO_CODEC_ID_H265; } } - if (argc < 2) { - printf("Usage: AWS_ACCESS_KEY_ID=SAMPLEKEY AWS_SECRET_ACCESS_KEY=SAMPLESECRET %s " - "[audio_codec] [video_codec] [events_enabled]\n", - argv[0]); - CHK(FALSE, STATUS_INVALID_ARG); - } - - if ((accessKey = getenv(ACCESS_KEY_ENV_VAR)) == NULL || (secretKey = getenv(SECRET_KEY_ENV_VAR)) == NULL) { - printf("Error missing credentials\n"); - CHK(FALSE, STATUS_INVALID_ARG); - } MEMSET(data.sampleDir, 0x00, MAX_PATH_LEN + 1); if (argc < 4) { @@ -251,10 +261,15 @@ INT32 main(INT32 argc, CHAR* argv[]) } printf("Done loading video frames.\n"); - cacertPath = getenv(CACERT_PATH_ENV_VAR); - sessionToken = getenv(SESSION_TOKEN_ENV_VAR); + cacertPath = GETENV(CACERT_PATH_ENV_VAR); + +#ifdef IOT_CORE_ENABLE_CREDENTIALS + streamName = pIotCoreThingName; +#else streamName = argv[1]; - if ((region = getenv(DEFAULT_REGION_ENV_VAR)) == NULL) { +#endif + + if ((region = GETENV(DEFAULT_REGION_ENV_VAR)) == NULL) { region = (PCHAR) DEFAULT_AWS_REGION; } @@ -309,10 +324,15 @@ INT32 main(INT32 argc, CHAR* argv[]) data.startTime = GETTIME(); data.firstFrame = TRUE; +#ifdef IOT_CORE_ENABLE_CREDENTIALS + CHK_STATUS(createDefaultCallbacksProviderWithIotCertificate(pIotCoreCredentialEndpoint, pIotCoreCert, pIotCorePrivateKey, cacertPath, + pIotCoreRoleAlias, pIotCoreThingName, region, NULL, NULL, &pClientCallbacks)); +#else CHK_STATUS(createDefaultCallbacksProviderWithAwsCredentials(accessKey, secretKey, sessionToken, MAX_UINT64, region, cacertPath, NULL, NULL, &pClientCallbacks)); +#endif - if (NULL != getenv(ENABLE_FILE_LOGGING)) { + if (NULL != GETENV(ENABLE_FILE_LOGGING)) { if ((retStatus = addFileLoggerPlatformCallbacksProvider(pClientCallbacks, FILE_LOGGING_BUFFER_SIZE, MAX_NUMBER_OF_LOG_FILES, (PCHAR) FILE_LOGGER_LOG_FILE_DIRECTORY_PATH, TRUE) != STATUS_SUCCESS)) { printf("File logging enable option failed with 0x%08x error code\n", retStatus); diff --git a/samples/KvsVideoOnlyOfflineStreamingSample.c b/samples/KvsVideoOnlyOfflineStreamingSample.c index 9424eeedc..b67463819 100644 --- a/samples/KvsVideoOnlyOfflineStreamingSample.c +++ b/samples/KvsVideoOnlyOfflineStreamingSample.c @@ -1,4 +1,4 @@ -#include +#include "Samples.h" #define DEFAULT_RETENTION_PERIOD 2 * HUNDREDS_OF_NANOS_IN_AN_HOUR #define DEFAULT_BUFFER_DURATION 120 * HUNDREDS_OF_NANOS_IN_A_SECOND @@ -14,8 +14,8 @@ #define NUMBER_OF_FRAME_FILES 403 -#define FILE_LOGGING_BUFFER_SIZE (100 * 1024) -#define MAX_NUMBER_OF_LOG_FILES 5 +// #define IOT_CORE_ENABLE_CREDENTIALS 1 + STATUS readFrameData(PFrame pFrame, PCHAR frameFilePath, PCHAR videoCodec) { STATUS retStatus = STATUS_SUCCESS; @@ -66,17 +66,27 @@ INT32 main(INT32 argc, CHAR* argv[]) STRNCPY(videoCodec, VIDEO_CODEC_NAME_H264, STRLEN(VIDEO_CODEC_NAME_H264)); // h264 video by default VIDEO_CODEC_ID videoCodecID = VIDEO_CODEC_ID_H264; +#ifdef IOT_CORE_ENABLE_CREDENTIALS + PCHAR pIotCoreCredentialEndpoint, pIotCoreCert, pIotCorePrivateKey, pIotCoreRoleAlias, pIotCoreThingName; + CHK_ERR((pIotCoreCredentialEndpoint = GETENV(IOT_CORE_CREDENTIAL_ENDPOINT)) != NULL, STATUS_INVALID_OPERATION, + "AWS_IOT_CORE_CREDENTIAL_ENDPOINT must be set"); + CHK_ERR((pIotCoreCert = GETENV(IOT_CORE_CERT)) != NULL, STATUS_INVALID_OPERATION, "AWS_IOT_CORE_CERT must be set"); + CHK_ERR((pIotCorePrivateKey = GETENV(IOT_CORE_PRIVATE_KEY)) != NULL, STATUS_INVALID_OPERATION, "AWS_IOT_CORE_PRIVATE_KEY must be set"); + CHK_ERR((pIotCoreRoleAlias = GETENV(IOT_CORE_ROLE_ALIAS)) != NULL, STATUS_INVALID_OPERATION, "AWS_IOT_CORE_ROLE_ALIAS must be set"); + CHK_ERR((pIotCoreThingName = GETENV(IOT_CORE_THING_NAME)) != NULL, STATUS_INVALID_OPERATION, "AWS_IOT_CORE_THING_NAME must be set"); +#else if (argc < 2) { DLOGE("Usage: AWS_ACCESS_KEY_ID=SAMPLEKEY AWS_SECRET_ACCESS_KEY=SAMPLESECRET %s " "\n", argv[0]); CHK(FALSE, STATUS_INVALID_ARG); } - - if ((accessKey = getenv(ACCESS_KEY_ENV_VAR)) == NULL || (secretKey = getenv(SECRET_KEY_ENV_VAR)) == NULL) { + if ((accessKey = GETENV(ACCESS_KEY_ENV_VAR)) == NULL || (secretKey = GETENV(SECRET_KEY_ENV_VAR)) == NULL) { DLOGE("Error missing credentials"); CHK(FALSE, STATUS_INVALID_ARG); } + sessionToken = GETENV(SESSION_TOKEN_ENV_VAR); +#endif MEMSET(frameFilePath, 0x00, MAX_PATH_LEN + 1); if (argc < 5) { @@ -85,10 +95,15 @@ INT32 main(INT32 argc, CHAR* argv[]) STRNCPY(frameFilePath, argv[4], MAX_PATH_LEN); } - cacertPath = getenv(CACERT_PATH_ENV_VAR); - sessionToken = getenv(SESSION_TOKEN_ENV_VAR); + cacertPath = GETENV(CACERT_PATH_ENV_VAR); + +#ifdef IOT_CORE_ENABLE_CREDENTIALS + streamName = pIotCoreThingName; +#else streamName = argv[1]; - if ((region = getenv(DEFAULT_REGION_ENV_VAR)) == NULL) { +#endif + + if ((region = GETENV(DEFAULT_REGION_ENV_VAR)) == NULL) { region = (PCHAR) DEFAULT_AWS_REGION; } @@ -119,10 +134,16 @@ INT32 main(INT32 argc, CHAR* argv[]) // adjust members of pStreamInfo here if needed startTime = GETTIME(); + +#ifdef IOT_CORE_ENABLE_CREDENTIALS + CHK_STATUS(createDefaultCallbacksProviderWithIotCertificate(pIotCoreCredentialEndpoint, pIotCoreCert, pIotCorePrivateKey, cacertPath, + pIotCoreRoleAlias, pIotCoreThingName, region, NULL, NULL, &pClientCallbacks)); +#else CHK_STATUS(createDefaultCallbacksProviderWithAwsCredentials(accessKey, secretKey, sessionToken, MAX_UINT64, region, cacertPath, NULL, NULL, &pClientCallbacks)); +#endif - if (NULL != getenv(ENABLE_FILE_LOGGING)) { + if (NULL != GETENV(ENABLE_FILE_LOGGING)) { if ((retStatus = addFileLoggerPlatformCallbacksProvider(pClientCallbacks, FILE_LOGGING_BUFFER_SIZE, MAX_NUMBER_OF_LOG_FILES, (PCHAR) FILE_LOGGER_LOG_FILE_DIRECTORY_PATH, TRUE) != STATUS_SUCCESS)) { printf("File logging enable option failed with 0x%08x error code\n", retStatus); diff --git a/samples/KvsVideoOnlyRealtimeStreamingSample.c b/samples/KvsVideoOnlyRealtimeStreamingSample.c index 2c8fe17ba..9c5b0a062 100644 --- a/samples/KvsVideoOnlyRealtimeStreamingSample.c +++ b/samples/KvsVideoOnlyRealtimeStreamingSample.c @@ -1,4 +1,4 @@ -#include +#include "Samples.h" #define DEFAULT_RETENTION_PERIOD 2 * HUNDREDS_OF_NANOS_IN_AN_HOUR #define DEFAULT_BUFFER_DURATION 120 * HUNDREDS_OF_NANOS_IN_A_SECOND @@ -17,8 +17,8 @@ #define NUMBER_OF_FRAME_FILES 403 -#define FILE_LOGGING_BUFFER_SIZE (100 * 1024) -#define MAX_NUMBER_OF_LOG_FILES 5 +// #define IOT_CORE_ENABLE_CREDENTIALS 1 + STATUS readFrameData(PFrame pFrame, PCHAR frameFilePath, PCHAR videoCodec) { STATUS retStatus = STATUS_SUCCESS; @@ -72,22 +72,43 @@ INT32 main(INT32 argc, CHAR* argv[]) STRNCPY(videoCodec, VIDEO_CODEC_NAME_H264, STRLEN(VIDEO_CODEC_NAME_H264)); // h264 video by default VIDEO_CODEC_ID videoCodecID = VIDEO_CODEC_ID_H264; +#ifdef IOT_CORE_ENABLE_CREDENTIALS + PCHAR pIotCoreCredentialEndpoint, pIotCoreCert, pIotCorePrivateKey, pIotCoreRoleAlias, pIotCoreThingName; + CHK_ERR((pIotCoreCredentialEndpoint = GETENV(IOT_CORE_CREDENTIAL_ENDPOINT)) != NULL, STATUS_INVALID_OPERATION, + "AWS_IOT_CORE_CREDENTIAL_ENDPOINT must be set"); + CHK_ERR((pIotCoreCert = GETENV(IOT_CORE_CERT)) != NULL, STATUS_INVALID_OPERATION, "AWS_IOT_CORE_CERT must be set"); + CHK_ERR((pIotCorePrivateKey = GETENV(IOT_CORE_PRIVATE_KEY)) != NULL, STATUS_INVALID_OPERATION, "AWS_IOT_CORE_PRIVATE_KEY must be set"); + CHK_ERR((pIotCoreRoleAlias = GETENV(IOT_CORE_ROLE_ALIAS)) != NULL, STATUS_INVALID_OPERATION, "AWS_IOT_CORE_ROLE_ALIAS must be set"); + CHK_ERR((pIotCoreRoleAlias = GETENV(IOT_CORE_ROLE_ALIAS)) != NULL, STATUS_INVALID_OPERATION, "AWS_IOT_CORE_ROLE_ALIAS must be set"); + CHK_ERR((pIotCoreThingName = GETENV(IOT_CORE_THING_NAME)) != NULL, STATUS_INVALID_OPERATION, "AWS_IOT_CORE_THING_NAME must be set"); +#else if (argc < 2) { DLOGE("Usage: AWS_ACCESS_KEY_ID=SAMPLEKEY AWS_SECRET_ACCESS_KEY=SAMPLESECRET %s " " [num_metadata = 10]\n", argv[0]); CHK(FALSE, STATUS_INVALID_ARG); } - - if ((accessKey = getenv(ACCESS_KEY_ENV_VAR)) == NULL || (secretKey = getenv(SECRET_KEY_ENV_VAR)) == NULL) { + if ((accessKey = GETENV(ACCESS_KEY_ENV_VAR)) == NULL || (secretKey = GETENV(SECRET_KEY_ENV_VAR)) == NULL) { DLOGE("Error missing credentials"); CHK(FALSE, STATUS_INVALID_ARG); } + sessionToken = GETENV(SESSION_TOKEN_ENV_VAR); +#endif - cacertPath = getenv(CACERT_PATH_ENV_VAR); - sessionToken = getenv(SESSION_TOKEN_ENV_VAR); + MEMSET(frameFilePath, 0x00, MAX_PATH_LEN + 1); + if (argc < 5) { + STRCPY(frameFilePath, (PCHAR) "../samples/"); + } else { + STRNCPY(frameFilePath, argv[4], MAX_PATH_LEN); + } + + cacertPath = GETENV(CACERT_PATH_ENV_VAR); +#ifdef IOT_CORE_ENABLE_CREDENTIALS + streamName = pIotCoreThingName; +#else streamName = argv[1]; - if ((region = getenv(DEFAULT_REGION_ENV_VAR)) == NULL) { +#endif + if ((region = GETENV(DEFAULT_REGION_ENV_VAR)) == NULL) { region = (PCHAR) DEFAULT_AWS_REGION; } @@ -131,10 +152,16 @@ INT32 main(INT32 argc, CHAR* argv[]) // adjust members of pStreamInfo here if needed startTime = GETTIME(); + +#ifdef IOT_CORE_ENABLE_CREDENTIALS + CHK_STATUS(createDefaultCallbacksProviderWithIotCertificate(pIotCoreCredentialEndpoint, pIotCoreCert, pIotCorePrivateKey, cacertPath, + pIotCoreRoleAlias, pIotCoreThingName, region, NULL, NULL, &pClientCallbacks)); +#else CHK_STATUS(createDefaultCallbacksProviderWithAwsCredentials(accessKey, secretKey, sessionToken, MAX_UINT64, region, cacertPath, NULL, NULL, &pClientCallbacks)); +#endif - if (NULL != getenv(ENABLE_FILE_LOGGING)) { + if (NULL != GETENV(ENABLE_FILE_LOGGING)) { if ((retStatus = addFileLoggerPlatformCallbacksProvider(pClientCallbacks, FILE_LOGGING_BUFFER_SIZE, MAX_NUMBER_OF_LOG_FILES, (PCHAR) FILE_LOGGER_LOG_FILE_DIRECTORY_PATH, TRUE) != STATUS_SUCCESS)) { printf("File logging enable option failed with 0x%08x error code\n", retStatus); diff --git a/samples/Samples.h b/samples/Samples.h new file mode 100644 index 000000000..0d1de4357 --- /dev/null +++ b/samples/Samples.h @@ -0,0 +1,28 @@ +/******************************************* +Shared include file for the samples +*******************************************/ +#ifndef __KINESIS_VIDEO_PRODUCER_SAMPLE_INCLUDE__ +#define __KINESIS_VIDEO_PRODUCER_SAMPLE_INCLUDE__ + +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +#define IOT_CORE_CREDENTIAL_ENDPOINT ((PCHAR) "AWS_IOT_CORE_CREDENTIAL_ENDPOINT") +#define IOT_CORE_CERT ((PCHAR) "AWS_IOT_CORE_CERT") +#define IOT_CORE_PRIVATE_KEY ((PCHAR) "AWS_IOT_CORE_PRIVATE_KEY") +#define IOT_CORE_ROLE_ALIAS ((PCHAR) "AWS_IOT_CORE_ROLE_ALIAS") +#define IOT_CORE_THING_NAME ((PCHAR) "AWS_IOT_CORE_THING_NAME") +#define IOT_CORE_CERTIFICATE_ID ((PCHAR) "AWS_IOT_CORE_CERTIFICATE_ID") + +#define FILE_LOGGING_BUFFER_SIZE (100 * 1024) +#define MAX_NUMBER_OF_LOG_FILES 5 + +#ifdef __cplusplus +} +#endif +#endif /* __KINESIS_VIDEO_PRODUCER_SAMPLE_INCLUDE__ */ \ No newline at end of file diff --git a/scripts/generate-iot-credential.sh b/scripts/generate-iot-credential.sh new file mode 100755 index 000000000..31b9ab0b0 --- /dev/null +++ b/scripts/generate-iot-credential.sh @@ -0,0 +1,116 @@ +#!/bin/bash +# You need to setup your aws cli first, because this script is based on aws cli. +# You can use this script to setup environment variables in the shell which samples run on. +# https://docs.aws.amazon.com/kinesisvideostreams/latest/dg/how-iot.html + +prefix=$1 +thingName="producerc_thing" +thingTypeName="producerc_thing_type" +iotPolicyName="producerc_policy" +kvsPolicyName="producerc_policy" +iotRoleName="producerc_role" +iotRoleAlias="producerc_role_alias" +iotCert="producerc_certificate.pem" +iotPublicKey="producerc_public.key" +iotPrivateKey="producerc_private.key" + +# Step 1: Create an IoT Thing Type and an IoT Thing +# The following example command creates a thing type $thingTypeName +aws --profile default iot create-thing-type --thing-type-name $thingTypeName > iot-thing-type.json +# And this example command creates the $thingName thing of the $thingTypeName thing type: +aws --profile default iot create-thing --thing-name $thingName --thing-type-name $thingTypeName > iot-thing.json + +# Step 2: Create an IAM Role to be Assumed by IoT +# You can use the following trust policy JSON for the iam-policy-document.json: +cat > iam-policy-document.json < iam-role.json + +# You can use the following IAM policy JSON for the iam-permission-document.json: +cat > iam-permission-document.json < iot-role-alias.json + +# You can use the following command to create the iot-policy-document.json document JSON: +cat > iot-policy-document.json << EOF +{ + "Version":"2012-10-17", + "Statement":[ + { + "Effect":"Allow", + "Action":[ + "iot:Connect" + ], + "Resource":"*" + }, + { + "Effect":"Allow", + "Action":[ + "iot:AssumeRoleWithCertificate" + ], + "Resource":"$(jq --raw-output '.roleAliasArn' iot-role-alias.json)" + } + ] +} +EOF + +# Now you can create the policy that will enable IoT to assume role with the certificate (once it is attached) using the role alias. +aws --profile default iot create-policy --policy-name $iotPolicyName --policy-document 'file://iot-policy-document.json' + +# Create the certificate to which you must attach the policy for IoT that you created above. +aws --profile default iot create-keys-and-certificate --set-as-active --certificate-pem-outfile $iotCert --public-key-outfile $iotPublicKey --private-key-outfile $iotPrivateKey > certificate +# Attach the policy for IoT (KvsCameraIoTPolicy created above) to this certificate. +aws --profile default iot attach-policy --policy-name $iotPolicyName --target $(jq --raw-output '.certificateArn' certificate) +# Attach your IoT thing (kvs_example_camera_stream) to the certificate you just created: +aws --profile default iot attach-thing-principal --thing-name $thingName --principal $(jq --raw-output '.certificateArn' certificate) +# In order to authorize requests through the IoT credentials provider, you need the IoT credentials endpoint which is unique to your AWS account ID. You can use the following command to get the IoT credentials endpoint. +aws --profile default iot describe-endpoint --endpoint-type iot:CredentialProvider --output text > iot-credential-provider.txt +# In addition to the X.509 cerficiate created above, you must also have a CA certificate to establish trust with the back-end service through TLS. You can get the CA certificate using the following command: +curl 'https://www.amazontrust.com/repository/SFSRootCAG2.pem' --output cacert.pem + +export AWS_IOT_CORE_CREDENTIAL_ENDPOINT=$(cat iot-credential-provider.txt) +export AWS_IOT_CORE_CERT=$(pwd)"/"$iotCert +export AWS_IOT_CORE_PRIVATE_KEY=$(pwd)"/"$iotPrivateKey +export AWS_IOT_CORE_ROLE_ALIAS=$iotRoleAlias +export AWS_IOT_CORE_THING_NAME=$thingName \ No newline at end of file From 530db0fd0e714b55ae3b49fa8021551928e8515c Mon Sep 17 00:00:00 2001 From: Divya Sampath Kumar Date: Wed, 20 Dec 2023 09:59:05 -0800 Subject: [PATCH 25/38] Update thing name length to be 128 as per AWS docs (#426) --- scripts/generate-iot-credential.sh | 1 - src/include/com/amazonaws/kinesis/video/common/Include.h | 4 ++-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/scripts/generate-iot-credential.sh b/scripts/generate-iot-credential.sh index 31b9ab0b0..29801cb01 100755 --- a/scripts/generate-iot-credential.sh +++ b/scripts/generate-iot-credential.sh @@ -3,7 +3,6 @@ # You can use this script to setup environment variables in the shell which samples run on. # https://docs.aws.amazon.com/kinesisvideostreams/latest/dg/how-iot.html -prefix=$1 thingName="producerc_thing" thingTypeName="producerc_thing_type" iotPolicyName="producerc_policy" diff --git a/src/include/com/amazonaws/kinesis/video/common/Include.h b/src/include/com/amazonaws/kinesis/video/common/Include.h index 730b0b2bb..045d43cd1 100644 --- a/src/include/com/amazonaws/kinesis/video/common/Include.h +++ b/src/include/com/amazonaws/kinesis/video/common/Include.h @@ -139,9 +139,9 @@ extern "C" { #define MAX_ROLE_ALIAS_LEN 128 /** - * Maximum allowed string length for IoT thing name + * Maximum allowed string length for IoT thing name: https://docs.aws.amazon.com/iot/latest/apireference/API_CreateThing.html */ -#define MAX_IOT_THING_NAME_LEN MAX_STREAM_NAME_LEN +#define MAX_IOT_THING_NAME_LEN 128 /** * Maximum allowed request header length From 2c046f227368e71e37e535b53aca79c185fef4da Mon Sep 17 00:00:00 2001 From: Divya Sampath Kumar Date: Wed, 17 Jan 2024 14:04:27 -0500 Subject: [PATCH 26/38] Release build for producer C (#428) * Release build for producer C * Fix assert failure --- CMake/Dependencies/libcurl-CMakeLists.txt | 1 + CMake/Dependencies/libjsmn-CMakeLists.txt | 14 -------------- CMake/Dependencies/libjsmn-add-cmakelists.patch | 17 ----------------- CMake/Dependencies/libmbedtls-CMakeLists.txt | 2 +- CMake/Dependencies/libwebsockets-CMakeLists.txt | 1 + CMakeLists.txt | 9 ++++++++- tst/ProducerTestFixture.cpp | 9 ++++++++- 7 files changed, 19 insertions(+), 34 deletions(-) delete mode 100644 CMake/Dependencies/libjsmn-CMakeLists.txt delete mode 100644 CMake/Dependencies/libjsmn-add-cmakelists.patch diff --git a/CMake/Dependencies/libcurl-CMakeLists.txt b/CMake/Dependencies/libcurl-CMakeLists.txt index 7d369197e..f0cb9f85e 100644 --- a/CMake/Dependencies/libcurl-CMakeLists.txt +++ b/CMake/Dependencies/libcurl-CMakeLists.txt @@ -17,6 +17,7 @@ endif() set(CMAKE_ARGS -DBUILD_SHARED_LIBS=${BUILD_SHARED_LIBS} + -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} -DCMAKE_INSTALL_PREFIX=${OPEN_SRC_INSTALL_PREFIX} -DBUILD_CURL_EXE=0 -DHTTP_ONLY=1) diff --git a/CMake/Dependencies/libjsmn-CMakeLists.txt b/CMake/Dependencies/libjsmn-CMakeLists.txt deleted file mode 100644 index b16ca0f5b..000000000 --- a/CMake/Dependencies/libjsmn-CMakeLists.txt +++ /dev/null @@ -1,14 +0,0 @@ -cmake_minimum_required(VERSION 3.6.3) - -project(libjsmn-download NONE) - -include(ExternalProject) -ExternalProject_Add(project_libjsmn - GIT_REPOSITORY https://github.com/zserge/jsmn.git - GIT_TAG v1.0.0 - PREFIX ${CMAKE_CURRENT_BINARY_DIR}/build - PATCH_COMMAND git apply --ignore-whitespace ${CMAKE_SOURCE_DIR}/../../CMake/Dependencies/libjsmn-add-cmakelists.patch - CMAKE_ARGS -DCMAKE_INSTALL_PREFIX=${OPEN_SRC_INSTALL_PREFIX} - BUILD_ALWAYS TRUE - TEST_COMMAND "" -) diff --git a/CMake/Dependencies/libjsmn-add-cmakelists.patch b/CMake/Dependencies/libjsmn-add-cmakelists.patch deleted file mode 100644 index 133c61d9d..000000000 --- a/CMake/Dependencies/libjsmn-add-cmakelists.patch +++ /dev/null @@ -1,17 +0,0 @@ -diff --git a/CMakeLists.txt b/CMakeLists.txt -new file mode 100644 -index 0000000..f4a1d44 ---- /dev/null -+++ b/CMakeLists.txt -@@ -0,0 +1,11 @@ -+cmake_minimum_required(VERSION 3.6.3) -+project(jsmn C) -+ -+ -+if("${CMAKE_C_COMPILER_ID}" MATCHES "GNU|Clang") -+ set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fPIC") -+endif() -+ -+add_library(jsmn STATIC jsmn.h jsmn.c) -+install(TARGETS jsmn DESTINATION lib) -+install(FILES jsmn.h DESTINATION include) diff --git a/CMake/Dependencies/libmbedtls-CMakeLists.txt b/CMake/Dependencies/libmbedtls-CMakeLists.txt index cd89da2b4..4a0d5f318 100644 --- a/CMake/Dependencies/libmbedtls-CMakeLists.txt +++ b/CMake/Dependencies/libmbedtls-CMakeLists.txt @@ -22,7 +22,7 @@ ExternalProject_Add( CMAKE_ARGS -DCMAKE_INSTALL_PREFIX=${OPEN_SRC_INSTALL_PREFIX} -DUSE_SHARED_MBEDTLS_LIBRARY=${BUILD_SHARED} - -DCMAKE_BUILD_TYPE=Release + -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} -DCMAKE_MACOSX_RPATH=${CMAKE_MACOSX_RPATH} BUILD_ALWAYS TRUE TEST_COMMAND "") diff --git a/CMake/Dependencies/libwebsockets-CMakeLists.txt b/CMake/Dependencies/libwebsockets-CMakeLists.txt index e00ee305d..3992337ef 100644 --- a/CMake/Dependencies/libwebsockets-CMakeLists.txt +++ b/CMake/Dependencies/libwebsockets-CMakeLists.txt @@ -25,6 +25,7 @@ ExternalProject_Add(project_libwebsockets CMAKE_ARGS -DCMAKE_INSTALL_PREFIX=${OPEN_SRC_INSTALL_PREFIX} -DLWS_WITH_HTTP2=1 + -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} -DLWS_HAVE_HMAC_CTX_new=1 -DLWS_HAVE_SSL_EXTRA_CHAIN_CERTS=1 -DLWS_HAVE_OPENSSL_ECDH_H=1 diff --git a/CMakeLists.txt b/CMakeLists.txt index 8310dd98f..10b5f12cc 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -59,6 +59,11 @@ endif() message(STATUS "Kinesis Video Producer path is ${KINESIS_VIDEO_PRODUCER_C_SRC}") +if(NOT CMAKE_BUILD_TYPE) + message(STATUS "Setting CMAKE_BUILD_TYPE to Release by default") + set(CMAKE_BUILD_TYPE "Release" CACHE STRING "Choose the type of build." FORCE) +endif() + if(BUILD_DEPENDENCIES) if (NOT OPEN_SRC_INSTALL_PREFIX) set(OPEN_SRC_INSTALL_PREFIX ${CMAKE_CURRENT_SOURCE_DIR}/open-source) @@ -88,7 +93,7 @@ if(BUILD_DEPENDENCIES) -DBUILD_OPENSSL_PLATFORM=${BUILD_OPENSSL_PLATFORM}) build_dependency(openssl ${BUILD_ARGS}) elseif(USE_MBEDTLS) - set(BUILD_ARGS -DBUILD_STATIC=${BUILD_STATIC}) + set(BUILD_ARGS -DBUILD_STATIC=${BUILD_STATIC} -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE}) build_dependency(mbedtls ${BUILD_ARGS}) else() message(FATAL_ERROR "No crypto library selected.") @@ -97,6 +102,7 @@ if(BUILD_DEPENDENCIES) if (BUILD_COMMON_LWS) set(BUILD_ARGS -DBUILD_STATIC=${BUILD_STATIC} + -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} -DOPENSSL_DIR=${OPEN_SRC_INSTALL_PREFIX} -DUSE_OPENSSL=${USE_OPENSSL} -DUSE_MBEDTLS=${USE_MBEDTLS}) @@ -105,6 +111,7 @@ if(BUILD_DEPENDENCIES) if (BUILD_COMMON_CURL) set(BUILD_ARGS -DBUILD_STATIC=${BUILD_STATIC} + -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} -DUSE_OPENSSL=${USE_OPENSSL} -DUSE_MBEDTLS=${USE_MBEDTLS}) build_dependency(curl ${BUILD_ARGS}) diff --git a/tst/ProducerTestFixture.cpp b/tst/ProducerTestFixture.cpp index 5ef4cdea2..b8aa3a782 100644 --- a/tst/ProducerTestFixture.cpp +++ b/tst/ProducerTestFixture.cpp @@ -233,9 +233,16 @@ ProducerClientTestBase::ProducerClientTestBase() : mDescribeFailCount(0), mDescribeRecoverCount(0) { + STATUS retStatus = STATUS_SUCCESS; auto logLevelStr = GETENV("AWS_KVS_LOG_LEVEL"); if (logLevelStr != NULL) { - assert(STRTOUI32(logLevelStr, NULL, 10, &this->loggerLogLevel) == STATUS_SUCCESS); + retStatus = STRTOUI32(logLevelStr, NULL, 10, &this->loggerLogLevel); + if (retStatus != STATUS_SUCCESS) { + // Throwing instead of asserting since this is being done in a constructor. ASSERT_EQ + // causes the function to exit immediately and constructor does not have a return type. + // The goal is to ensure if an env is set, it is set with a valid value and not empty + throw std::runtime_error("Failed to set log level from env"); + } SET_LOGGER_LOG_LEVEL(this->loggerLogLevel); } From 09785b1234979ba67449270c8656605b20590ca1 Mon Sep 17 00:00:00 2001 From: Hassan Sahibzada Date: Wed, 17 Jan 2024 22:19:50 -0500 Subject: [PATCH 27/38] Handle aws special regions (#430) --- .github/workflows/ci.yml | 2 - CMake/Dependencies/libopenssl-CMakeLists.txt | 8 ++- .../amazonaws/kinesis/video/common/Include.h | 16 ++++++ src/source/CurlApiCallbacks.c | 23 ++++++--- tst/ProducerCallbackProviderApiTest.cpp | 51 ++++++++++++++++++- 5 files changed, 90 insertions(+), 10 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5ce565edb..c3970541f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -58,8 +58,6 @@ jobs: runs-on: macos-11 env: AWS_KVS_LOG_LEVEL: 2 - LDFLAGS: -L/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/lib - CPATH: /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/ permissions: id-token: write contents: read diff --git a/CMake/Dependencies/libopenssl-CMakeLists.txt b/CMake/Dependencies/libopenssl-CMakeLists.txt index c97037a84..fbe3d41a2 100644 --- a/CMake/Dependencies/libopenssl-CMakeLists.txt +++ b/CMake/Dependencies/libopenssl-CMakeLists.txt @@ -1,6 +1,6 @@ cmake_minimum_required(VERSION 3.6.3) -project(libopenssl-download NONE) +project(libopenssl-download LANGUAGES C) if (WIN32) find_program(MAKE_EXE NAMES nmake) @@ -11,6 +11,12 @@ else() SET(OPENSSL_EXTRA ${OPENSSL_EXTRA} no-shared no-dso) endif() + if (DEFINED CMAKE_OSX_SYSROOT AND NOT CMAKE_OSX_SYSROOT STREQUAL "") + if ("${CMAKE_C_COMPILER_ID}" MATCHES "Clang") + SET(OPENSSL_EXTRA ${OPENSSL_EXTRA} -I${CMAKE_OSX_SYSROOT}/usr/include -L${CMAKE_OSX_SYSROOT}/usr/lib) + endif() + endif() + if (DEFINED BUILD_OPENSSL_PLATFORM AND NOT BUILD_OPENSSL_PLATFORM STREQUAL OFF) SET(CONFIGURE_COMMAND ${CMAKE_CURRENT_BINARY_DIR}/build/src/project_libopenssl/Configure ${OPENSSL_EXTRA} --prefix=${OPEN_SRC_INSTALL_PREFIX} --openssldir=${OPEN_SRC_INSTALL_PREFIX} ${BUILD_OPENSSL_PLATFORM} -Wno-nullability-completeness -Wno-expansion-to-defined) else() diff --git a/src/include/com/amazonaws/kinesis/video/common/Include.h b/src/include/com/amazonaws/kinesis/video/common/Include.h index 045d43cd1..cc8fae722 100644 --- a/src/include/com/amazonaws/kinesis/video/common/Include.h +++ b/src/include/com/amazonaws/kinesis/video/common/Include.h @@ -314,11 +314,27 @@ extern "C" { */ #define KINESIS_VIDEO_SERVICE_NAME "kinesisvideo" +#define AWS_KVS_FIPS_ENDPOINT_POSTFIX "-fips" + /** * Control plane postfix */ #define CONTROL_PLANE_URI_POSTFIX ".amazonaws.com" +#define CONTROL_PLANE_URI_POSTFIX_CN ".amazonaws.com.cn" + +#define CONTROL_PLANE_URI_POSTFIX_ISO ".c2s.ic.gov" + +#define CONTROL_PLANE_URI_POSTFIX_ISO_B ".sc2s.sgov.gov" + +#define AWS_ISO_B_REGION_PREFIX "us-isob-" + +#define AWS_ISO_REGION_PREFIX "us-iso-" + +#define AWS_GOV_CLOUD_REGION_PREFIX "us-gov-" + +#define AWS_CN_REGION_PREFIX "cn-" + /** * Default user agent name */ diff --git a/src/source/CurlApiCallbacks.c b/src/source/CurlApiCallbacks.c index 66b71a063..92a351e38 100644 --- a/src/source/CurlApiCallbacks.c +++ b/src/source/CurlApiCallbacks.c @@ -79,12 +79,23 @@ STATUS createCurlApiCallbacks(PCallbacksProvider pCallbacksProvider, PCHAR regio // Set the control plane URL if (controlPlaneUrl == NULL || controlPlaneUrl[0] == '\0') { - // Create a fully qualified URI - SNPRINTF(pCurlApiCallbacks->controlPlaneUrl, MAX_URI_CHAR_LEN, "%s%s.%s%s", CONTROL_PLANE_URI_PREFIX, KINESIS_VIDEO_SERVICE_NAME, - pCurlApiCallbacks->region, CONTROL_PLANE_URI_POSTFIX); - // If region is in CN, add CN region uri postfix - if (STRSTR(pCurlApiCallbacks->region, "cn-")) { - STRCAT(pCurlApiCallbacks->controlPlaneUrl, ".cn"); + if (0 == STRNCMP(pCurlApiCallbacks->region, AWS_ISO_B_REGION_PREFIX, STRLEN(AWS_ISO_B_REGION_PREFIX))) { + SNPRINTF(pCurlApiCallbacks->controlPlaneUrl, MAX_URI_CHAR_LEN, "%s%s%s.%s%s", CONTROL_PLANE_URI_PREFIX, KINESIS_VIDEO_SERVICE_NAME, + AWS_KVS_FIPS_ENDPOINT_POSTFIX, pCurlApiCallbacks->region, CONTROL_PLANE_URI_POSTFIX_ISO_B); + // Region is in "aws-iso" partition + } else if (0 == STRNCMP(pCurlApiCallbacks->region, AWS_ISO_REGION_PREFIX, STRLEN(AWS_ISO_REGION_PREFIX))) { + SNPRINTF(pCurlApiCallbacks->controlPlaneUrl, MAX_URI_CHAR_LEN, "%s%s%s.%s%s", CONTROL_PLANE_URI_PREFIX, KINESIS_VIDEO_SERVICE_NAME, + AWS_KVS_FIPS_ENDPOINT_POSTFIX, pCurlApiCallbacks->region, CONTROL_PLANE_URI_POSTFIX_ISO); + } else if (0 == STRNCMP(pCurlApiCallbacks->region, AWS_GOV_CLOUD_REGION_PREFIX, STRLEN(AWS_GOV_CLOUD_REGION_PREFIX))) { + SNPRINTF(pCurlApiCallbacks->controlPlaneUrl, MAX_URI_CHAR_LEN, "%s%s%s.%s%s", CONTROL_PLANE_URI_PREFIX, KINESIS_VIDEO_SERVICE_NAME, + AWS_KVS_FIPS_ENDPOINT_POSTFIX, pCurlApiCallbacks->region, CONTROL_PLANE_URI_POSTFIX); + } else if (0 == STRNCMP(pCurlApiCallbacks->region, AWS_CN_REGION_PREFIX, STRLEN(AWS_CN_REGION_PREFIX))) { + SNPRINTF(pCurlApiCallbacks->controlPlaneUrl, MAX_URI_CHAR_LEN, "%s%s.%s%s", CONTROL_PLANE_URI_PREFIX, KINESIS_VIDEO_SERVICE_NAME, + pCurlApiCallbacks->region, CONTROL_PLANE_URI_POSTFIX_CN); + } else { + // Create a fully qualified URI + SNPRINTF(pCurlApiCallbacks->controlPlaneUrl, MAX_URI_CHAR_LEN, "%s%s.%s%s", CONTROL_PLANE_URI_PREFIX, KINESIS_VIDEO_SERVICE_NAME, + pCurlApiCallbacks->region, CONTROL_PLANE_URI_POSTFIX); } } else { STRNCPY(pCurlApiCallbacks->controlPlaneUrl, controlPlaneUrl, MAX_URI_CHAR_LEN); diff --git a/tst/ProducerCallbackProviderApiTest.cpp b/tst/ProducerCallbackProviderApiTest.cpp index 844f8af42..aeca444aa 100644 --- a/tst/ProducerCallbackProviderApiTest.cpp +++ b/tst/ProducerCallbackProviderApiTest.cpp @@ -1,5 +1,5 @@ - #include "ProducerTestFixture.h" +#include namespace com { namespace amazonaws { namespace kinesis { namespace video { @@ -58,6 +58,55 @@ namespace com { namespace amazonaws { namespace kinesis { namespace video { } + TEST_F(ProducerCallbackProviderApiTest, TestCorrectControlPlaneUriForSpecifiedRegion) + { + PClientCallbacks pClientCallbacks = NULL; + // Map region to control plane url + std::map regionToControlPlaneUrlMap = { + {"us-east-1", "https://kinesisvideo.us-east-1.amazonaws.com"}, + {"us-west-2", "https://kinesisvideo.us-west-2.amazonaws.com"}, + {"ap-northeast-1", "https://kinesisvideo.ap-northeast-1.amazonaws.com"}, + {"ap-southeast-2", "https://kinesisvideo.ap-southeast-2.amazonaws.com"}, + {"eu-central-1", "https://kinesisvideo.eu-central-1.amazonaws.com"}, + {"eu-west-1", "https://kinesisvideo.eu-west-1.amazonaws.com"}, + {"ap-northeast-2", "https://kinesisvideo.ap-northeast-2.amazonaws.com"}, + {"ap-south-1", "https://kinesisvideo.ap-south-1.amazonaws.com"}, + {"ap-southeast-1", "https://kinesisvideo.ap-southeast-1.amazonaws.com"}, + {"ca-central-1", "https://kinesisvideo.ca-central-1.amazonaws.com"}, + {"eu-north-1", "https://kinesisvideo.eu-north-1.amazonaws.com"}, + {"eu-west-2", "https://kinesisvideo.eu-west-2.amazonaws.com"}, + {"sa-east-1", "https://kinesisvideo.sa-east-1.amazonaws.com"}, + {"us-east-2", "https://kinesisvideo.us-east-2.amazonaws.com"}, + {"ap-east-1", "https://kinesisvideo.ap-east-1.amazonaws.com"}, + {"af-south-1", "https://kinesisvideo.af-south-1.amazonaws.com"}, + {"us-iso-east-1", "https://kinesisvideo-fips.us-iso-east-1.c2s.ic.gov"}, + {"us-iso-west-1", "https://kinesisvideo-fips.us-iso-west-1.c2s.ic.gov"}, + {"us-isob-east-1", "https://kinesisvideo-fips.us-isob-east-1.sc2s.sgov.gov"}, + {"us-gov-west-1", "https://kinesisvideo-fips.us-gov-west-1.amazonaws.com"}, + {"us-gov-east-1", "https://kinesisvideo-fips.us-gov-east-1.amazonaws.com"}, + {"cn-north-1", "https://kinesisvideo.cn-north-1.amazonaws.com.cn"}, + {"cn-northwest-1", "https://kinesisvideo.cn-northwest-1.amazonaws.com.cn"}, + }; + + for (auto it : regionToControlPlaneUrlMap) { + EXPECT_EQ(STATUS_SUCCESS, + createDefaultCallbacksProvider(TEST_DEFAULT_CHAIN_COUNT, TEST_ACCESS_KEY, TEST_SECRET_KEY, TEST_SESSION_TOKEN, + TEST_STREAMING_TOKEN_DURATION, (PCHAR) it.first.c_str(), TEST_CONTROL_PLANE_URI, mCaCertPath, NULL, + TEST_USER_AGENT, API_CALL_CACHE_TYPE_NONE, TEST_CACHING_ENDPOINT_PERIOD, TRUE, + &pClientCallbacks)); + + PCallbacksProvider pCallbacksProvider = (PCallbacksProvider) pClientCallbacks; + PCurlApiCallbacks pCurlApiCallbacks = (PCurlApiCallbacks) pCallbacksProvider->pApiCallbacks[0].customData; + + EXPECT_EQ(0, STRNCMP(pCurlApiCallbacks->controlPlaneUrl, (PCHAR) it.second.c_str(), MAX_URI_CHAR_LEN)); + + EXPECT_EQ(STATUS_SUCCESS, freeCallbacksProvider(&pClientCallbacks)); + + EXPECT_EQ(STATUS_NULL_ARG, freeCallbacksProvider(NULL)); + } + + } + } // namespace video } // namespace kinesis } // namespace amazonaws From 13523a2f05a28164b0aa2523c517c2f93021b3f5 Mon Sep 17 00:00:00 2001 From: Hassan Sahibzada Date: Thu, 18 Jan 2024 12:59:53 -0500 Subject: [PATCH 28/38] run windows tests with debug logging (#431) * run windows tests with debug logging * zero out frame buffer to avoid invalid mkv --- .github/workflows/ci.yml | 2 +- src/source/CurlApiCallbacks.c | 9 ++++++--- tst/ProducerTestFixture.cpp | 2 +- 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c3970541f..76987b3c7 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -358,7 +358,7 @@ jobs: windows-msvc: runs-on: windows-2022 env: - AWS_KVS_LOG_LEVEL: 7 + AWS_KVS_LOG_LEVEL: 2 permissions: id-token: write contents: read diff --git a/src/source/CurlApiCallbacks.c b/src/source/CurlApiCallbacks.c index 92a351e38..073dc5b22 100644 --- a/src/source/CurlApiCallbacks.c +++ b/src/source/CurlApiCallbacks.c @@ -1328,7 +1328,7 @@ PVOID describeStreamCurlHandler(PVOID arg) UINT32 i, strLen, resultLen; INT32 tokenCount; UINT64 retention; - BOOL jsonInStreamDescription = FALSE, requestTerminating = FALSE; + BOOL jsonInStreamDescription = FALSE, requestTerminating = FALSE, responseReceived = FALSE; StreamDescription streamDescription; STREAM_HANDLE streamHandle = INVALID_STREAM_HANDLE_VALUE; SERVICE_CALL_RESULT callResult = SERVICE_CALL_RESULT_NOT_SET; @@ -1361,8 +1361,7 @@ PVOID describeStreamCurlHandler(PVOID arg) CHK(pCurlResponse->callInfo.callResult != SERVICE_CALL_RESULT_NOT_SET, STATUS_INVALID_OPERATION); pResponseStr = pCurlResponse->callInfo.responseData; resultLen = pCurlResponse->callInfo.responseDataLen; - - DLOGD("[%s] DescribeStream API response: %.*s", streamDescription.streamName, resultLen, pResponseStr); + responseReceived = TRUE; // skip json parsing if call result not ok CHK(pCurlResponse->callInfo.callResult == SERVICE_CALL_RESULT_OK && resultLen != 0 && pResponseStr != NULL, retStatus); @@ -1441,6 +1440,10 @@ PVOID describeStreamCurlHandler(PVOID arg) CleanUp: + if (responseReceived) { + DLOGD("[%s] DescribeStream API response: %.*s", streamDescription.streamName, resultLen, pResponseStr); + } + // Preserve the values as we need to free the request before the event notification if (pCurlRequest->pCurlResponse != NULL) { callResult = pCurlRequest->pCurlResponse->callInfo.callResult; diff --git a/tst/ProducerTestFixture.cpp b/tst/ProducerTestFixture.cpp index b8aa3a782..3309a98c8 100644 --- a/tst/ProducerTestFixture.cpp +++ b/tst/ProducerTestFixture.cpp @@ -339,7 +339,7 @@ ProducerClientTestBase::ProducerClientTestBase() : mFps = TEST_FPS; mKeyFrameInterval = TEST_FPS; mFrameSize = TEST_FRAME_SIZE; - mFrameBuffer = (PBYTE) MEMALLOC(mFrameSize); + mFrameBuffer = (PBYTE) MEMCALLOC(mFrameSize, SIZEOF(BYTE)); mFrame.duration = HUNDREDS_OF_NANOS_IN_A_SECOND / mFps; mFrame.frameData = mFrameBuffer; From 76a72e9a53291c107e57cc82550f027db2b9e76d Mon Sep 17 00:00:00 2001 From: Hassan Sahibzada Date: Mon, 5 Feb 2024 15:46:15 -0500 Subject: [PATCH 29/38] fix tsan related issues (#427) fix tsan related issues --- .github/workflows/ci.yml | 53 +++++++++-------- CMake/Dependencies/libcurl-CMakeLists.txt | 2 +- CMake/Dependencies/libgtest-CMakeLists.txt | 1 - CMake/Dependencies/libkvspic-CMakeLists.txt | 2 +- CMake/Dependencies/libmbedtls-CMakeLists.txt | 2 +- .../Dependencies/libwebsockets-CMakeLists.txt | 2 +- src/source/Common/AwsV4Signer.c | 2 +- src/source/CurlApiCallbacks.c | 33 +++++++---- src/source/Response.c | 10 ++-- src/source/Response.h | 2 +- tst/ProducerClientBasicTest.cpp | 59 ++++++++++--------- tst/ProducerTestFixture.cpp | 20 ++++--- tst/ProducerTestFixture.h | 4 +- 13 files changed, 106 insertions(+), 86 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 76987b3c7..a62b22f1d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -295,31 +295,34 @@ jobs: # ulimit -c unlimited -S # timeout --signal=SIGABRT 150m ./tst/producer_test --gtest_break_on_failure - # thread-sanitizer: - # runs-on: ubuntu-20.04 - # permissions: - # id-token: write - # contents: read - # env: - # CC: clang - # CXX: clang++ - # AWS_KVS_LOG_LEVEL: 2 - # steps: - # - name: Clone repository - # uses: actions/checkout@v3 - # - name: Configure AWS Credentials - # uses: aws-actions/configure-aws-credentials@v1-node16 - # with: - # role-to-assume: ${{ secrets.AWS_ROLE_TO_ASSUME }} - # role-session-name: ${{ secrets.AWS_ROLE_SESSION_NAME }} - # aws-region: ${{ secrets.AWS_REGION }} - # - name: Build repository - # run: | - # mkdir build && cd build - # cmake .. -DBUILD_TEST=TRUE -DTHREAD_SANITIZER=TRUE -DBUILD_COMMON_LWS=TRUE - # make - # ulimit -c unlimited -S - # timeout --signal=SIGABRT 150m ./tst/producer_test --gtest_break_on_failure + #thread-sanitizer: + # runs-on: ubuntu-20.04 + # permissions: + # id-token: write + # contents: read + # env: + # CC: clang + # CXX: clang++ + # AWS_KVS_LOG_LEVEL: 2 + # steps: + # - name: Clone repository + # uses: actions/checkout@v3 + # - name: Configure AWS Credentials + # uses: aws-actions/configure-aws-credentials@v1-node16 + # with: + # role-to-assume: ${{ secrets.AWS_ROLE_TO_ASSUME }} + # role-session-name: ${{ secrets.AWS_ROLE_SESSION_NAME }} + # aws-region: ${{ secrets.AWS_REGION }} + # - name: Build repository + # run: | + # mkdir build && cd build + # cmake .. -DBUILD_TEST=TRUE -DTHREAD_SANITIZER=TRUE -DBUILD_COMMON_LWS=TRUE + # make + # - name: Run tests + # run: | + # cd build + # ulimit -c unlimited -S + # timeout --signal=SIGABRT 150m ./tst/producer_test --gtest_break_on_failure ubuntu-gcc: runs-on: ubuntu-20.04 diff --git a/CMake/Dependencies/libcurl-CMakeLists.txt b/CMake/Dependencies/libcurl-CMakeLists.txt index f0cb9f85e..74e44b520 100644 --- a/CMake/Dependencies/libcurl-CMakeLists.txt +++ b/CMake/Dependencies/libcurl-CMakeLists.txt @@ -1,6 +1,6 @@ cmake_minimum_required(VERSION 3.6.3) -project(libcurl-download NONE) +project(libcurl-download LANGUAGES C) find_program(MAKE_EXE NAMES make) diff --git a/CMake/Dependencies/libgtest-CMakeLists.txt b/CMake/Dependencies/libgtest-CMakeLists.txt index 916087e76..14e4a09cb 100644 --- a/CMake/Dependencies/libgtest-CMakeLists.txt +++ b/CMake/Dependencies/libgtest-CMakeLists.txt @@ -3,7 +3,6 @@ cmake_minimum_required(VERSION 3.6.3) project(libgtest-download NONE) include(ExternalProject) - ExternalProject_Add(libgtest-download GIT_REPOSITORY https://github.com/google/googletest.git GIT_TAG release-1.12.1 diff --git a/CMake/Dependencies/libkvspic-CMakeLists.txt b/CMake/Dependencies/libkvspic-CMakeLists.txt index c8ebe188d..4e52d8e09 100644 --- a/CMake/Dependencies/libkvspic-CMakeLists.txt +++ b/CMake/Dependencies/libkvspic-CMakeLists.txt @@ -1,6 +1,6 @@ cmake_minimum_required(VERSION 3.6.3) -project(libkvspic-download NONE) +project(libkvspic-download LANGUAGES C) include(ExternalProject) diff --git a/CMake/Dependencies/libmbedtls-CMakeLists.txt b/CMake/Dependencies/libmbedtls-CMakeLists.txt index 4a0d5f318..edb782c68 100644 --- a/CMake/Dependencies/libmbedtls-CMakeLists.txt +++ b/CMake/Dependencies/libmbedtls-CMakeLists.txt @@ -1,6 +1,6 @@ cmake_minimum_required(VERSION 3.6.3) -project(libmbedtls-download NONE) +project(libmbedtls-download LANGUAGES C) include(ExternalProject) diff --git a/CMake/Dependencies/libwebsockets-CMakeLists.txt b/CMake/Dependencies/libwebsockets-CMakeLists.txt index 3992337ef..3ada49e4b 100644 --- a/CMake/Dependencies/libwebsockets-CMakeLists.txt +++ b/CMake/Dependencies/libwebsockets-CMakeLists.txt @@ -1,6 +1,6 @@ cmake_minimum_required(VERSION 3.6.3) -project(libwebsocket-download NONE) +project(libwebsocket-download LANGUAGES C) include(ExternalProject) diff --git a/src/source/Common/AwsV4Signer.c b/src/source/Common/AwsV4Signer.c index 3eddaf44c..19b5831ad 100644 --- a/src/source/Common/AwsV4Signer.c +++ b/src/source/Common/AwsV4Signer.c @@ -659,7 +659,7 @@ STATUS generateSignatureDateTime(UINT64 currentTime, PCHAR pDateTimeStr) // Convert to time_t timeT = (time_t) (currentTime / HUNDREDS_OF_NANOS_IN_A_SECOND); - retSize = STRFTIME(pDateTimeStr, SIGNATURE_DATE_TIME_STRING_LEN, DATE_TIME_STRING_FORMAT, GMTIME(&timeT)); + retSize = STRFTIME(pDateTimeStr, SIGNATURE_DATE_TIME_STRING_LEN, DATE_TIME_STRING_FORMAT, GMTIME_THREAD_SAFE(&timeT)); CHK(retSize > 0, STATUS_BUFFER_TOO_SMALL); pDateTimeStr[retSize] = '\0'; diff --git a/src/source/CurlApiCallbacks.c b/src/source/CurlApiCallbacks.c index 073dc5b22..c045e0e1e 100644 --- a/src/source/CurlApiCallbacks.c +++ b/src/source/CurlApiCallbacks.c @@ -1005,15 +1005,17 @@ STATUS createStreamCurl(UINT64 customData, PCHAR deviceName, PCHAR streamName, P CleanUp: + if (startLocked) { + // Release the lock to let the awaiting handler thread to continue + pCallbacksProvider->clientCallbacks.unlockMutexFn(pCallbacksProvider->clientCallbacks.customData, pCurlRequest->startLock); + } + if (STATUS_FAILED(retStatus)) { if (IS_VALID_TID_VALUE(threadId)) { THREAD_CANCEL(threadId); } freeCurlRequest(&pCurlRequest); - } else if (startLocked) { - // Release the lock to let the awaiting handler thread to continue - pCallbacksProvider->clientCallbacks.unlockMutexFn(pCallbacksProvider->clientCallbacks.customData, pCurlRequest->startLock); } if (shutdownLocked) { @@ -1237,15 +1239,17 @@ STATUS describeStreamCurl(UINT64 customData, PCHAR streamName, PServiceCallConte CleanUp: + if (startLocked) { + // Release the lock to let the awaiting handler thread to continue + pCallbacksProvider->clientCallbacks.unlockMutexFn(pCallbacksProvider->clientCallbacks.customData, pCurlRequest->startLock); + } + if (STATUS_FAILED(retStatus)) { if (IS_VALID_TID_VALUE(threadId)) { THREAD_CANCEL(threadId); } freeCurlRequest(&pCurlRequest); - } else if (startLocked) { - // Release the lock to let the awaiting handler thread to continue - pCallbacksProvider->clientCallbacks.unlockMutexFn(pCallbacksProvider->clientCallbacks.customData, pCurlRequest->startLock); } if (shutdownLocked) { @@ -1548,15 +1552,18 @@ STATUS getStreamingEndpointCurl(UINT64 customData, PCHAR streamName, PCHAR apiNa CleanUp: + if (startLocked) { + // Release the lock to let the awaiting handler thread to continue. + // This needs to be done before freeCurlRequest because there we will free the startLock mutex + pCallbacksProvider->clientCallbacks.unlockMutexFn(pCallbacksProvider->clientCallbacks.customData, pCurlRequest->startLock); + } + if (STATUS_FAILED(retStatus)) { if (IS_VALID_TID_VALUE(threadId)) { THREAD_CANCEL(threadId); } freeCurlRequest(&pCurlRequest); - } else if (startLocked) { - // Release the lock to let the awaiting handler thread to continue - pCallbacksProvider->clientCallbacks.unlockMutexFn(pCallbacksProvider->clientCallbacks.customData, pCurlRequest->startLock); } if (shutdownLocked) { @@ -1877,15 +1884,17 @@ STATUS tagResourceCurl(UINT64 customData, PCHAR streamArn, UINT32 tagCount, PTag CleanUp: + if (startLocked) { + // Release the lock to let the awaiting handler thread to continue + pCallbacksProvider->clientCallbacks.unlockMutexFn(pCallbacksProvider->clientCallbacks.customData, pCurlRequest->startLock); + } + if (STATUS_FAILED(retStatus)) { if (IS_VALID_TID_VALUE(threadId)) { THREAD_CANCEL(threadId); } freeCurlRequest(&pCurlRequest); - } else if (startLocked) { - // Release the lock to let the awaiting handler thread to continue - pCallbacksProvider->clientCallbacks.unlockMutexFn(pCallbacksProvider->clientCallbacks.customData, pCurlRequest->startLock); } if (shutdownLocked) { diff --git a/src/source/Response.c b/src/source/Response.c index 9b0f8397e..b96c05108 100644 --- a/src/source/Response.c +++ b/src/source/Response.c @@ -31,7 +31,7 @@ STATUS createCurlResponse(PCurlRequest pCurlRequest, PCurlResponse* ppCurlRespon // init putMedia related members pCurlResponse->endOfStream = FALSE; - pCurlResponse->paused = TRUE; + ATOMIC_STORE_BOOL(&pCurlResponse->paused, TRUE); pCurlResponse->debugDumpFile = FALSE; pCurlResponse->debugDumpFilePath[0] = '\0'; @@ -456,8 +456,8 @@ STATUS notifyDataAvailable(PCurlResponse pCurlResponse, UINT64 durationAvailable DLOGV("[%s] Note data received: duration(100ns): %" PRIu64 " bytes %" PRIu64 " for stream handle %" PRIu64, pCurlResponse->pCurlRequest->streamName, durationAvailable, sizeAvailable, pCurlResponse->pCurlRequest->uploadHandle); - if (pCurlResponse->paused && pCurlResponse->pCurl != NULL) { - pCurlResponse->paused = FALSE; + if (ATOMIC_LOAD_BOOL(&pCurlResponse->paused) && pCurlResponse->pCurl != NULL) { + ATOMIC_STORE_BOOL(&pCurlResponse->paused, FALSE); // frequent pause unpause causes curl segfault in offline scenario THREAD_SLEEP(10 * HUNDREDS_OF_NANOS_IN_A_MILLISECOND); // un-pause curl @@ -634,7 +634,7 @@ SIZE_T postReadCallback(PCHAR pBuffer, SIZE_T size, SIZE_T numItems, PVOID custo pCurlApiCallbacks = pCurlRequest->pCurlApiCallbacks; uploadHandle = pCurlResponse->pCurlRequest->uploadHandle; - if (pCurlResponse->paused) { + if (ATOMIC_LOAD_BOOL(&pCurlResponse->paused)) { bytesWritten = CURL_READFUNC_PAUSE; CHK(FALSE, retStatus); } @@ -721,7 +721,7 @@ SIZE_T postReadCallback(PCHAR pBuffer, SIZE_T size, SIZE_T numItems, PVOID custo } } } else if (bytesWritten == CURL_READFUNC_PAUSE) { - pCurlResponse->paused = TRUE; + ATOMIC_STORE_BOOL(&pCurlResponse->paused, TRUE); } // Since curl is about to terminate gracefully, set flag to prevent shutdown thread from timing it out. diff --git a/src/source/Response.h b/src/source/Response.h index 670b268e4..5f625f4a9 100644 --- a/src/source/Response.h +++ b/src/source/Response.h @@ -62,7 +62,7 @@ struct __CurlResponse { BOOL endOfStream; // Whether curl is paused - volatile BOOL paused; + volatile ATOMIC_BOOL paused; // Whether to dump streaming session into mkv file BOOL debugDumpFile; diff --git a/tst/ProducerClientBasicTest.cpp b/tst/ProducerClientBasicTest.cpp index 22a97a0e6..85f5f0681 100644 --- a/tst/ProducerClientBasicTest.cpp +++ b/tst/ProducerClientBasicTest.cpp @@ -10,8 +10,8 @@ class ProducerClientBasicTest : public ProducerClientTestBase { mStreamsCreated = CVAR_CREATE(); MEMSET(mClients, 0x00, SIZEOF(mClients)); MEMSET(mClientCallbacks, 0x00, SIZEOF(mClientCallbacks)); - mActiveStreamCount = 0; - mActiveClientCount = 0; + ATOMIC_STORE(&mActiveStreamCount, 0); + ATOMIC_STORE(&mActiveClientCount, 0); } VOID deinitialize() @@ -35,8 +35,8 @@ class ProducerClientBasicTest : public ProducerClientTestBase { CVAR mStreamsCreated; CLIENT_HANDLE mClients[TEST_STREAM_COUNT]; PClientCallbacks mClientCallbacks[TEST_STREAM_COUNT]; - volatile UINT32 mActiveStreamCount; - volatile UINT32 mActiveClientCount; + volatile SIZE_T mActiveStreamCount; + volatile SIZE_T mActiveClientCount; }; extern ProducerClientTestBase* gProducerClientTestBase; @@ -137,7 +137,8 @@ PVOID ProducerClientBasicTest::staticCreateProducerClientRoutine(PVOID arg) EXPECT_EQ(STATUS_SUCCESS, retStatus = createKinesisVideoStreamSync(pTest->mClients[index], &streamInfo, &pTest->mStreams[index])); - if (++pTest->mActiveStreamCount == TEST_STREAM_COUNT) { + ATOMIC_INCREMENT(&pTest->mActiveStreamCount); + if (ATOMIC_LOAD(&pTest->mActiveStreamCount) == TEST_STREAM_COUNT) { CVAR_SIGNAL(pTest->mStreamsCreated); } @@ -196,7 +197,8 @@ PVOID ProducerClientBasicTest::staticCreateProducerRoutine(PVOID arg) retStatus = createKinesisVideoStreamSync(pTest->mClientHandle, &streamInfo, &pTest->mStreams[index]); - if (++pTest->mActiveStreamCount == TEST_STREAM_COUNT) { + ATOMIC_INCREMENT(&pTest->mActiveStreamCount); + if (ATOMIC_LOAD(&pTest->mActiveStreamCount) == TEST_STREAM_COUNT) { CVAR_SIGNAL(pTest->mStreamsCreated); } @@ -223,10 +225,10 @@ PVOID ProducerClientBasicTest::staticProducerClientStartRoutine(PVOID arg) EXPECT_EQ(STATUS_SUCCESS, kinesisVideoStreamGetStreamInfo(streamHandle, &pStreamInfo)); // Set an indicator that the producer is not stopped - pTest->mProducerStopped = FALSE; + ATOMIC_STORE_BOOL(&pTest->mProducerStopped, FALSE); // Increment the active stream/producer count - pTest->mActiveClientCount++; + ATOMIC_INCREMENT(&pTest->mActiveClientCount); // Loop until cancelled frame.version = FRAME_CURRENT_VERSION; @@ -245,7 +247,7 @@ PVOID ProducerClientBasicTest::staticProducerClientStartRoutine(PVOID arg) EXPECT_EQ(STATUS_SUCCESS, kinesisVideoStreamFormatChanged(streamHandle, cpdSize, cpd, DEFAULT_VIDEO_TRACK_ID)); - while (!pTest->mStopProducer) { + while (!ATOMIC_LOAD_BOOL(&pTest->mStopProducer)) { // Produce frames timestamp = GETTIME(); @@ -319,8 +321,9 @@ PVOID ProducerClientBasicTest::staticProducerClientStartRoutine(PVOID arg) pTest->mStreams[streamIndex] = INVALID_STREAM_HANDLE_VALUE; // Indicate that the producer routine had stopped - if (--pTest->mActiveClientCount == 0) { - pTest->mProducerStopped = true; + ATOMIC_DECREMENT(&pTest->mActiveClientCount); + if (ATOMIC_LOAD(&pTest->mActiveClientCount) == 0) { + ATOMIC_STORE_BOOL(&pTest->mProducerStopped, TRUE); } return NULL; @@ -356,7 +359,7 @@ PVOID ProducerClientTestBase::basicProducerRoutine(STREAM_HANDLE streamHandle, S EXPECT_EQ(STATUS_SUCCESS, kinesisVideoStreamGetStreamInfo(streamHandle, &pStreamInfo)); // Set an indicator that the producer is not stopped - mProducerStopped = FALSE; + ATOMIC_STORE_BOOL(&mProducerStopped, FALSE); // Loop until cancelled frame.version = FRAME_CURRENT_VERSION; @@ -375,7 +378,7 @@ PVOID ProducerClientTestBase::basicProducerRoutine(STREAM_HANDLE streamHandle, S EXPECT_EQ(STATUS_SUCCESS, kinesisVideoStreamFormatChanged(streamHandle, cpdSize, cpd, DEFAULT_VIDEO_TRACK_ID)); - while (!mStopProducer) { + while (!ATOMIC_LOAD_BOOL(&mStopProducer)) { // Produce frames if (IS_OFFLINE_STREAMING_MODE(streamingType)) { timestamp += frame.duration; @@ -459,7 +462,7 @@ EXPECT_TRUE(kinesis_video_stream->putFrame(eofr)); EXPECT_EQ(STATUS_SUCCESS, stopKinesisVideoStreamSync(streamHandle)) << "Timed out awaiting for the stream stop notification"; // Indicate that the producer routine had stopped - mProducerStopped = true; + ATOMIC_STORE_BOOL(&mProducerStopped, TRUE); return NULL; } @@ -489,7 +492,7 @@ TEST_F(ProducerClientBasicTest, create_produce_stream) for (UINT32 iter = 0; iter < 10; iter++) { THREAD_SLEEP(10 * HUNDREDS_OF_NANOS_IN_A_SECOND); DLOGD("Stopping the streams"); - mStopProducer = TRUE; + ATOMIC_STORE_BOOL(&mStopProducer, TRUE); DLOGD("Waiting for the streams to finish and close..."); THREAD_SLEEP(10 * HUNDREDS_OF_NANOS_IN_A_SECOND); @@ -501,7 +504,7 @@ TEST_F(ProducerClientBasicTest, create_produce_stream) } DLOGD("Starting the streams again"); - mStopProducer = FALSE; + ATOMIC_STORE_BOOL(&mStopProducer, FALSE); // Create new streams for (UINT32 i = 0; i < TEST_STREAM_COUNT; i++) { @@ -521,7 +524,7 @@ TEST_F(ProducerClientBasicTest, create_produce_stream) THREAD_SLEEP(2*TEST_EXECUTION_DURATION); // Indicate the cancel for the threads - mStopProducer = TRUE; + ATOMIC_STORE_BOOL(&mStopProducer, TRUE); // Join the thread and wait to exit. // NOTE: This is not a right way of doing it as for the multiple stream scenario @@ -531,9 +534,9 @@ TEST_F(ProducerClientBasicTest, create_produce_stream) UINT32 index = 0; do { THREAD_SLEEP(100 * HUNDREDS_OF_NANOS_IN_A_MILLISECOND); - } while (index++ < 300 && !mProducerStopped); + } while (index++ < 300 && !ATOMIC_LOAD_BOOL(&mProducerStopped)); - EXPECT_TRUE(mProducerStopped) << "Producer thread failed to stop cleanly"; + EXPECT_TRUE(ATOMIC_LOAD_BOOL(&mProducerStopped)) << "Producer thread failed to stop cleanly"; // We will block for some time due to an incorrect implementation of the awaiting code // NOTE: The proper implementation should use synchronization primitives to await for the @@ -576,14 +579,14 @@ TEST_F(ProducerClientBasicTest, create_produce_stream_parallel) THREAD_SLEEP(2*TEST_EXECUTION_DURATION); // Indicate the cancel for the threads - mStopProducer = TRUE; + ATOMIC_STORE_BOOL(&mStopProducer, TRUE); UINT32 index = 0; do { THREAD_SLEEP(100 * HUNDREDS_OF_NANOS_IN_A_MILLISECOND); - } while (index++ < 300 && !mProducerStopped); + } while (index++ < 300 && !ATOMIC_LOAD_BOOL(&mProducerStopped)); - EXPECT_TRUE(mProducerStopped) << "Producer thread failed to stop cleanly"; + EXPECT_TRUE(ATOMIC_LOAD_BOOL(&mProducerStopped)) << "Producer thread failed to stop cleanly"; THREAD_SLEEP(10 * HUNDREDS_OF_NANOS_IN_A_MILLISECOND); @@ -622,14 +625,14 @@ TEST_F(ProducerClientBasicTest, create_produce_client_parallel) THREAD_SLEEP(2*TEST_EXECUTION_DURATION); // Indicate the cancel for the threads - mStopProducer = TRUE; + ATOMIC_STORE_BOOL(&mStopProducer, TRUE); UINT32 index = 0; do { THREAD_SLEEP(100 * HUNDREDS_OF_NANOS_IN_A_MILLISECOND); - } while (index++ < 300 && !mProducerStopped); + } while (index++ < 300 && !ATOMIC_LOAD_BOOL(&mProducerStopped)); - EXPECT_TRUE(mProducerStopped) << "Producer thread failed to stop cleanly"; + EXPECT_TRUE(ATOMIC_LOAD_BOOL(&mProducerStopped)) << "Producer thread failed to stop cleanly"; THREAD_SLEEP(10 * HUNDREDS_OF_NANOS_IN_A_MILLISECOND); @@ -662,7 +665,7 @@ TEST_F(ProducerClientBasicTest, cachingEndpointProvider_Returns_EndpointFromCach THREAD_SLEEP(TEST_STREAMING_TOKEN_DURATION * ITERATION_COUNT); // Indicate the cancel for the threads - mStopProducer = TRUE; + ATOMIC_STORE_BOOL(&mStopProducer, TRUE); // Join the thread and wait to exit. // NOTE: This is not a right way of doing it as for the multiple stream scenario @@ -672,9 +675,9 @@ TEST_F(ProducerClientBasicTest, cachingEndpointProvider_Returns_EndpointFromCach UINT32 index = 0; do { THREAD_SLEEP(100 * HUNDREDS_OF_NANOS_IN_A_MILLISECOND); - } while (index++ < 300 && !mProducerStopped); + } while (index++ < 300 && !ATOMIC_LOAD_BOOL(&mProducerStopped)); - EXPECT_TRUE(mProducerStopped) << "Producer thread failed to stop cleanly"; + EXPECT_TRUE(ATOMIC_LOAD_BOOL(&mProducerStopped)) << "Producer thread failed to stop cleanly"; // Expect the number of calls EXPECT_EQ(((ITERATION_COUNT + 1) * TEST_STREAM_COUNT), mPutStreamFnCount); diff --git a/tst/ProducerTestFixture.cpp b/tst/ProducerTestFixture.cpp index 3309a98c8..902ca1d65 100644 --- a/tst/ProducerTestFixture.cpp +++ b/tst/ProducerTestFixture.cpp @@ -181,9 +181,7 @@ ProducerClientTestBase::ProducerClientTestBase() : mAccessKeyIdSet(FALSE), mCaCertPath(NULL), mProducerThread(INVALID_TID_VALUE), - mProducerStopped(FALSE), mStartProducer(FALSE), - mStopProducer(FALSE), mAccessKey(NULL), mSecretKey(NULL), mSessionToken(NULL), @@ -245,6 +243,8 @@ ProducerClientTestBase::ProducerClientTestBase() : } SET_LOGGER_LOG_LEVEL(this->loggerLogLevel); } + ATOMIC_STORE_BOOL(&mStopProducer, FALSE); + ATOMIC_STORE_BOOL(&mProducerStopped, FALSE); // Store the function pointers gTotalProducerClientMemoryUsage = 0; @@ -542,7 +542,7 @@ STATUS ProducerClientTestBase::createTestStream(UINT32 index, STREAMING_TYPE str VOID ProducerClientTestBase::freeStreams(BOOL sync) { - mProducerStopped = TRUE; + ATOMIC_STORE_BOOL(&mProducerStopped, TRUE); for (UINT32 i = 0; i < TEST_STREAM_COUNT; i++) { DLOGD("Freeing stream index %u with handle value %" PRIu64 " %s", i, mStreams[i], sync ? "synchronously" : "asynchronously"); @@ -568,6 +568,7 @@ STATUS ProducerClientTestBase::curlEasyPerformHookFunc(PCurlResponse pCurlRespon // Get the test object ProducerClientTestBase* pTest = (ProducerClientTestBase*) pCurlResponse->pCurlRequest->pCurlApiCallbacks->hookCustomData; + MUTEX_LOCK(pTest->mTestCallbackLock); DLOGV("Curl perform hook for %s", pCurlResponse->pCurlRequest->requestInfo.url); @@ -630,6 +631,7 @@ STATUS ProducerClientTestBase::curlEasyPerformHookFunc(PCurlResponse pCurlRespon pTest->mPutMediaCallResult = SERVICE_CALL_RESULT_OK; } } + MUTEX_UNLOCK(pTest->mTestCallbackLock); return retStatus; } @@ -647,6 +649,7 @@ STATUS ProducerClientTestBase::curlWriteCallbackHookFunc(PCurlResponse pCurlResp // Get the test object ProducerClientTestBase* pTest = (ProducerClientTestBase*) pCurlResponse->pCurlRequest->pCurlApiCallbacks->hookCustomData; + MUTEX_LOCK(pTest->mTestCallbackLock); pTest->mWriteCallbackFnCount++; @@ -654,6 +657,7 @@ STATUS ProducerClientTestBase::curlWriteCallbackHookFunc(PCurlResponse pCurlResp *ppRetBuffer = pTest->mWriteBuffer; *pRetDataSize = pTest->mWriteDataSize; } + MUTEX_UNLOCK(pTest->mTestCallbackLock); return pTest->mWriteStatus; } @@ -678,6 +682,7 @@ STATUS ProducerClientTestBase::curlReadCallbackHookFunc(PCurlResponse pCurlRespo // Get the test object ProducerClientTestBase* pTest = (ProducerClientTestBase*) pCurlResponse->pCurlRequest->pCurlApiCallbacks->hookCustomData; + MUTEX_LOCK(pTest->mTestCallbackLock); pTest->mReadCallbackFnCount++; @@ -686,16 +691,17 @@ STATUS ProducerClientTestBase::curlReadCallbackHookFunc(PCurlResponse pCurlRespo } else { pTest->mReadStatus = status; } + MUTEX_UNLOCK(pTest->mTestCallbackLock); return pTest->mReadStatus; } STATUS ProducerClientTestBase::testFreeApiCallbackFunc(PUINT64 customData) { - ProducerClientTestBase* pTestBase = (ProducerClientTestBase*) *customData; - - pTestBase->mFreeApiCallbacksFnCount++; - + ProducerClientTestBase* pTest = (ProducerClientTestBase*) *customData; + MUTEX_LOCK(pTest->mTestCallbackLock); + pTest->mFreeApiCallbacksFnCount++; + MUTEX_UNLOCK(pTest->mTestCallbackLock); return STATUS_SUCCESS; } diff --git a/tst/ProducerTestFixture.h b/tst/ProducerTestFixture.h index 2754a7e29..ce6910d22 100644 --- a/tst/ProducerTestFixture.h +++ b/tst/ProducerTestFixture.h @@ -272,8 +272,8 @@ class ProducerClientTestBase : public ::testing::Test { STREAM_HANDLE mStreams[TEST_MAX_STREAM_COUNT]; volatile bool mStartProducer; - volatile bool mStopProducer; - volatile bool mProducerStopped; + volatile ATOMIC_BOOL mStopProducer; + volatile ATOMIC_BOOL mProducerStopped; // Test callbacks ApiCallbacks mApiCallbacks; From 70f2b300804ab2cdecb670f18267634c42863ff7 Mon Sep 17 00:00:00 2001 From: James Delaplane Date: Wed, 7 Feb 2024 13:50:51 -0800 Subject: [PATCH 30/38] pull new branch of pic --- CMake/Dependencies/libkvspic-CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMake/Dependencies/libkvspic-CMakeLists.txt b/CMake/Dependencies/libkvspic-CMakeLists.txt index 4e52d8e09..28ae0c0bb 100644 --- a/CMake/Dependencies/libkvspic-CMakeLists.txt +++ b/CMake/Dependencies/libkvspic-CMakeLists.txt @@ -7,7 +7,7 @@ include(ExternalProject) # clone repo only ExternalProject_Add(libkvspic-download GIT_REPOSITORY https://github.com/awslabs/amazon-kinesis-video-streams-pic.git - GIT_TAG develop + GIT_TAG thread_stack_size_api SOURCE_DIR "${CMAKE_CURRENT_BINARY_DIR}/kvspic-src" BINARY_DIR "${CMAKE_CURRENT_BINARY_DIR}/kvspic-build" CMAKE_ARGS From 235e5b179379977654ae49e2fafc14fcef852841 Mon Sep 17 00:00:00 2001 From: James Delaplane Date: Wed, 7 Feb 2024 15:23:36 -0800 Subject: [PATCH 31/38] CMake definitions for kvs stack size --- CMake/Dependencies/libkvspic-CMakeLists.txt | 1 + CMakeLists.txt | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/CMake/Dependencies/libkvspic-CMakeLists.txt b/CMake/Dependencies/libkvspic-CMakeLists.txt index 28ae0c0bb..f818313fa 100644 --- a/CMake/Dependencies/libkvspic-CMakeLists.txt +++ b/CMake/Dependencies/libkvspic-CMakeLists.txt @@ -12,6 +12,7 @@ ExternalProject_Add(libkvspic-download BINARY_DIR "${CMAKE_CURRENT_BINARY_DIR}/kvspic-build" CMAKE_ARGS -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} + -DKVS_DEFAULT_STACK_SIZE=${KVS_DEFAULT_STACK_SIZE} CONFIGURE_COMMAND "" BUILD_COMMAND "" INSTALL_COMMAND "" diff --git a/CMakeLists.txt b/CMakeLists.txt index 10b5f12cc..a05fb56a3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -49,6 +49,10 @@ if(CONSTRAINED_DEVICE) add_definitions(-DCONSTRAINED_DEVICE) endif() +if(KVS_DEFAULT_STACK_SIZE) + add_definitions(-DKVS_DEFAULT_STACK_SIZE=${KVS_DEFAULT_STACK_SIZE}) +endif() + if(NOT KINESIS_VIDEO_PRODUCER_C_SRC) if(DEFINED ENV{KINESIS_VIDEO_PRODUCER_C_SRC}) set(KINESIS_VIDEO_PRODUCER_C_SRC $ENV{KINESIS_VIDEO_PRODUCER_C_SRC}) From 982f45e66b9672c1ecd7a9f12dfa543edab9df4e Mon Sep 17 00:00:00 2001 From: James Delaplane Date: Wed, 14 Feb 2024 09:35:57 -0800 Subject: [PATCH 32/38] remove adding definition --- CMakeLists.txt | 4 ---- 1 file changed, 4 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index a05fb56a3..10b5f12cc 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -49,10 +49,6 @@ if(CONSTRAINED_DEVICE) add_definitions(-DCONSTRAINED_DEVICE) endif() -if(KVS_DEFAULT_STACK_SIZE) - add_definitions(-DKVS_DEFAULT_STACK_SIZE=${KVS_DEFAULT_STACK_SIZE}) -endif() - if(NOT KINESIS_VIDEO_PRODUCER_C_SRC) if(DEFINED ENV{KINESIS_VIDEO_PRODUCER_C_SRC}) set(KINESIS_VIDEO_PRODUCER_C_SRC $ENV{KINESIS_VIDEO_PRODUCER_C_SRC}) From 3e13e88eaa3c7283544adc0bf515685526d72396 Mon Sep 17 00:00:00 2001 From: James Delaplane Date: Fri, 16 Feb 2024 15:06:04 -0800 Subject: [PATCH 33/38] README update about constrained device flag --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index ff2bd3464..fbf04263c 100644 --- a/README.md +++ b/README.md @@ -59,6 +59,7 @@ You can pass the following options to `cmake ..`. * `-DALIGNED_MEMORY_MODEL` Build for aligned memory model only devices. Default is OFF. * `-DLOCAL_OPENSSL_BUILD` Whether or not to use local OpenSSL build. Default is OFF. * `-DCONSTRAINED_DEVICE` -- Change thread stack size to 0.5Mb, needed for Alpine. +* `-DKVS_DEFAULT_STACK_SIZE` -- Change thread stack size to given value, overrides CONSTRAINED_DEVICE flag DMEMORY_SANITIZER, DTHREAD_SANITIZER etc. flags works only with clang compiler From f67f56c872d7f56ce12108e8f7b3e53d90721624 Mon Sep 17 00:00:00 2001 From: Divya Sampath Kumar Date: Thu, 22 Feb 2024 11:14:48 -0800 Subject: [PATCH 34/38] Add PR description lint (#438) --- .github/PULL_REQUEST_TEMPLATE.md | 11 ++++++ .github/workflows/pr-desc-lint.yml | 55 ++++++++++++++++++++++++++++++ 2 files changed, 66 insertions(+) create mode 100644 .github/PULL_REQUEST_TEMPLATE.md create mode 100644 .github/workflows/pr-desc-lint.yml diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 000000000..8507ac840 --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,11 @@ +*Issue #, if available:* + +*What was changed?* + +*Why was it changed?* + +*How was it changed?* + +*What testing was done for the changes?* + +By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice. \ No newline at end of file diff --git a/.github/workflows/pr-desc-lint.yml b/.github/workflows/pr-desc-lint.yml new file mode 100644 index 000000000..e4fcc9216 --- /dev/null +++ b/.github/workflows/pr-desc-lint.yml @@ -0,0 +1,55 @@ +name: PR Description Check + +on: + pull_request: + branches: + - develop + - master + types: + - opened + - synchronize + - reopened + - edited + +jobs: + check-description: + runs-on: macos-latest + steps: + - name: Install GitHub CLI + run: | + brew install gh + + - name: Check PR Description + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + pr_description=$(gh pr view https://github.com/${GITHUB_REPOSITORY}/pull/${{ github.event.pull_request.number }} --json body -q ".body") + error_occurred=0 + # Define minimum character count for each section + MIN_CHARS=25 + + # Extract contents + what_changed=$(echo "$pr_description" | sed -n -e '/\*What was changed?\*/,/\*/p' | sed '$d' | sed '1d') + why_changed=$(echo "$pr_description" | sed -n -e '/\*Why was it changed?\*/,/\*/p' | sed '$d' | sed '1d') + how_changed=$(echo "$pr_description" | sed -n -e '/\*How was it changed?\*/,/\*/p' | sed '$d' | sed '1d') + testing_done=$(echo "$pr_description" | sed -n -e '/\*What testing was done for the changes?\*/,/\*/p' | sed '$d' | sed '1d') + + if [[ ${#what_changed} -lt $MIN_CHARS ]]; then + echo "PR description for what changed section is either missing or too short." + error_occurred=1 + fi + if [[ ${#why_changed} -lt $MIN_CHARS ]]; then + echo "PR description for why it changed section is either missing or too short." + error_occurred=1 + fi + if [[ ${#how_changed} -lt $MIN_CHARS ]]; then + echo "PR description for how was it changed section is either missing or too short." + error_occurred=1 + fi + if [[ ${#testing_done} -lt $MIN_CHARS ]]; then + echo "PR description for testing section are either missing or too short." + error_occurred=1 + fi + if [[ $error_occurred -eq 1 ]]; then + exit 1 + fi From ced3b7259d72c051d373a3f5168a2fcfc38c40db Mon Sep 17 00:00:00 2001 From: James Delaplane Date: Wed, 28 Feb 2024 11:26:57 -0800 Subject: [PATCH 35/38] update dependency branch --- CMake/Dependencies/libkvspic-CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMake/Dependencies/libkvspic-CMakeLists.txt b/CMake/Dependencies/libkvspic-CMakeLists.txt index f818313fa..51b80ba6d 100644 --- a/CMake/Dependencies/libkvspic-CMakeLists.txt +++ b/CMake/Dependencies/libkvspic-CMakeLists.txt @@ -7,7 +7,7 @@ include(ExternalProject) # clone repo only ExternalProject_Add(libkvspic-download GIT_REPOSITORY https://github.com/awslabs/amazon-kinesis-video-streams-pic.git - GIT_TAG thread_stack_size_api + GIT_TAG develop SOURCE_DIR "${CMAKE_CURRENT_BINARY_DIR}/kvspic-src" BINARY_DIR "${CMAKE_CURRENT_BINARY_DIR}/kvspic-build" CMAKE_ARGS From 7f9ff95ef313d414e77626f41a4690cb36ab8044 Mon Sep 17 00:00:00 2001 From: Divya Sampath Kumar Date: Thu, 29 Feb 2024 14:21:51 -0800 Subject: [PATCH 36/38] Mbedtls link (#442) * mbedtls link * PIC log * use crypto instead * Add CI * Add gcc to ci build name --- .github/workflows/ci.yml | 23 +++++++++++++++++++++++ CMakeLists.txt | 3 +-- 2 files changed, 24 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a62b22f1d..22a8bb264 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -176,6 +176,28 @@ jobs: cd build ./tst/producer_test + ubuntu-os-gcc-build-lws-mbedtls: + runs-on: ubuntu-20.04 + env: + AWS_KVS_LOG_LEVEL: 2 + CC: gcc + CXX: g++ + permissions: + id-token: write + contents: read + steps: + - name: Clone repository + uses: actions/checkout@v3 + - name: Install dependencies + run: | + sudo apt clean && sudo apt update + sudo sh -c 'echo 0 > /proc/sys/net/ipv6/conf/all/disable_ipv6' + - name: Build repository + run: | + mkdir build && cd build + cmake .. -DBUILD_COMMON_LWS=ON -DBUILD_COMMON_CURL=OFF -DUSE_OPENSSL=OFF -DUSE_MBEDTLS=ON + make + linux-gcc-code-coverage: runs-on: ubuntu-20.04 env: @@ -269,6 +291,7 @@ jobs: ulimit -c unlimited -S timeout --signal=SIGABRT 150m ./tst/producer_test --gtest_break_on_failure + # memory-sanitizer: # runs-on: ubuntu-20.04 # permissions: diff --git a/CMakeLists.txt b/CMakeLists.txt index 10b5f12cc..e984c8d27 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -289,8 +289,7 @@ if(BUILD_COMMON_LWS) set_target_properties(kvsCommonLws PROPERTIES VERSION ${KINESIS_VIDEO_PRODUCER_C_VERSION} SOVERSION ${KINESIS_VIDEO_PRODUCER_C_MAJOR_VERSION}) endif() target_link_libraries(kvsCommonLws - ${OPENSSL_CRYPTO_LIBRARY} - ${OPENSSL_SSL_LIBRARY} + ${PRODUCER_CRYPTO_LIBRARY} ${LIBWEBSOCKETS_LIBRARIES} kvspicUtils) From 03ed9455ae10e48a792c704e395a901ccf71a13a Mon Sep 17 00:00:00 2001 From: Divya Sampath Kumar Date: Thu, 25 Apr 2024 13:44:05 -0700 Subject: [PATCH 37/38] Version Check CI (#448) --- .github/workflows/ci.yml | 2 +- .github/workflows/version-check.yml | 42 +++++++++++++++++++++++++++++ 2 files changed, 43 insertions(+), 1 deletion(-) create mode 100644 .github/workflows/version-check.yml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 22a8bb264..bf53a496d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -393,7 +393,7 @@ jobs: uses: actions/checkout@v3 - name: Install dependencies run: | - choco install nasm strawberryperl + choco install nasm strawberryperl pkgconfiglite - name: Build repository run: | $env:Path += ';C:\Strawberry\perl\site\bin;C:\Strawberry\perl\bin;C:\Strawberry\c\bin;C:\Program Files\NASM;D:\a\amazon-kinesis-video-streams-producer-c\amazon-kinesis-video-streams-producer-c\open-source\lib;D:\a\amazon-kinesis-video-streams-producer-c\amazon-kinesis-video-streams-producer-c\open-source\bin' diff --git a/.github/workflows/version-check.yml b/.github/workflows/version-check.yml new file mode 100644 index 000000000..24ebef721 --- /dev/null +++ b/.github/workflows/version-check.yml @@ -0,0 +1,42 @@ +name: Check Version Mismatch between PR branch and master. + +on: + pull_request: + branches: + - master + +jobs: + check-version: + runs-on: ubuntu-latest + steps: + - name: Checkout PR branch + uses: actions/checkout@v4 + + - name: Get version from PR + id: pr_version + run: | + PR_VERSION=$(grep -Po 'KinesisVideoProducerC VERSION \K[0-9]+\.[0-9]+\.[0-9]+' CMakeLists.txt) + echo "PR_VERSION=$PR_VERSION" >> "$GITHUB_ENV" + echo "PR Version: $PR_VERSION" + + - name: Checkout master branch + uses: actions/checkout@v4 + with: + ref: master + + - name: Get version from master + id: master_version + run: | + MASTER_VERSION=$(grep -Po 'KinesisVideoProducerC VERSION \K[0-9]+\.[0-9]+\.[0-9]+' CMakeLists.txt) + echo "MASTER_VERSION=$MASTER_VERSION" >> "$GITHUB_ENV" + echo "Master version: $MASTER_VERSION" + + - name: Compare versions + run: | + echo "Comparing PR Version: $PR_VERSION with Master Version: $MASTER_VERSION" + if [ "$MASTER_VERSION" == "$PR_VERSION" ]; then + echo "Please update the version in CMakeLists.txt file (project(KinesisVideoProducerC VERSION LANGUAGES C). Any PR getting merged to master requires a version update" + exit 1 + else + echo "Version update detected." + fi \ No newline at end of file From eabf283a9f5eed8da9908bb3fc94055defdd3383 Mon Sep 17 00:00:00 2001 From: Divya Sampath Kumar Date: Tue, 7 May 2024 14:04:07 -0700 Subject: [PATCH 38/38] Increase UT coverage (#446) * Pre-ut-inc * Install lcov * Only source file changes * Auth, aws creds, utils, added cert file * iot cred test, aws v4 signer * Source the script, windows skip iot * Windows exclude iot, info provider * curl test, iot * Reqinfo, curl api mod, iot lws * Only windows * filter * Final list * Add lws to macos and ubuntu * Windows hang * Windows CI * CI, new completion, connection test * Enable mac too * printenv windows, more iot test * Quick test * Skip iot win * Signer tests more * Remove unused dump curl response function * Platform callbacks test * Readme update, cleanup * Windows fix * Readd dumpResponseCurlEasyInfo and comment out since it is unused --- .github/workflows/ci.yml | 49 +- .github/workflows/codecov.yml | 51 ++ .../Dependencies/libwebsockets-CMakeLists.txt | 2 +- README.md | 9 +- scripts/generate-iot-credential.sh | 29 +- scripts/get-iot-certs.sh | 28 + src/source/CallbacksProvider.c | 2 +- src/source/Common/AwsV4Signer.c | 4 +- src/source/Common/IotCredentialProvider.c | 2 +- src/source/Common/RequestInfo.c | 5 +- src/source/Response.c | 2 + src/source/Response.h | 12 +- src/source/StreamInfoProvider.c | 2 +- tst/AuthCallbackTest.cpp | 22 +- tst/AwsCredentialsTest.cpp | 3 + tst/AwsV4SignerTest.cpp | 117 ++++ tst/CMakeLists.txt | 8 + tst/CallbacksProviderPublicApiTest.cpp | 10 + tst/CurlApiTest.cpp | 39 ++ tst/InfoProviderApiTest.cpp | 651 +++++++++++------- tst/IoTCredentialTest.cpp | 311 +++++++++ tst/PlatformCallbackProviderApiTest.cpp | 60 +- tst/ProducerTestFixture.h | 8 + tst/RequestInfoTest.cpp | 85 +++ tst/UtilsApiTest.cpp | 42 ++ tst/cert.pem | 24 + tst/sample_file_creds.txt | 1 + 27 files changed, 1221 insertions(+), 357 deletions(-) create mode 100644 .github/workflows/codecov.yml create mode 100644 scripts/get-iot-certs.sh create mode 100644 tst/AwsV4SignerTest.cpp create mode 100644 tst/CurlApiTest.cpp create mode 100644 tst/IoTCredentialTest.cpp create mode 100644 tst/RequestInfoTest.cpp create mode 100644 tst/UtilsApiTest.cpp create mode 100644 tst/cert.pem create mode 100644 tst/sample_file_creds.txt diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index bf53a496d..21df5828c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -52,6 +52,7 @@ jobs: - name: Run tests run: | cd build + source ../scripts/get-iot-certs.sh ci ./tst/producer_test mac-os-build-clang: @@ -81,6 +82,7 @@ jobs: - name: Run tests run: | cd build + source ../scripts/get-iot-certs.sh ci ./tst/producer_test mac-os-m1-build-clang: @@ -110,6 +112,7 @@ jobs: - name: Run tests run: | cd build + source ../scripts/get-iot-certs.sh ci ./tst/producer_test mac-os-build-gcc-local-openssl: @@ -143,6 +146,7 @@ jobs: - name: Run tests run: | cd build + source ../scripts/get-iot-certs.sh ci ./tst/producer_test mac-os-build-clang-local-openssl: @@ -174,6 +178,7 @@ jobs: - name: Run tests run: | cd build + source ../scripts/get-iot-certs.sh ci ./tst/producer_test ubuntu-os-gcc-build-lws-mbedtls: @@ -198,39 +203,6 @@ jobs: cmake .. -DBUILD_COMMON_LWS=ON -DBUILD_COMMON_CURL=OFF -DUSE_OPENSSL=OFF -DUSE_MBEDTLS=ON make - linux-gcc-code-coverage: - runs-on: ubuntu-20.04 - env: - AWS_KVS_LOG_LEVEL: 2 - permissions: - id-token: write - contents: read - steps: - - name: Clone repository - uses: actions/checkout@v3 - - name: Build repository - run: | - mkdir build && cd build - cmake .. -DCODE_COVERAGE=TRUE -DBUILD_TEST=TRUE -DBUILD_COMMON_LWS=TRUE - make - - name: Configure AWS Credentials - uses: aws-actions/configure-aws-credentials@v1-node16 - with: - role-to-assume: ${{ secrets.AWS_ROLE_TO_ASSUME }} - role-session-name: ${{ secrets.AWS_ROLE_SESSION_NAME }} - aws-region: ${{ secrets.AWS_REGION }} - role-duration-seconds: 10800 - - name: Run tests - run: | - cd build - ulimit -c unlimited -S - timeout --signal=SIGABRT 150m ./tst/producer_test --gtest_break_on_failure - - name: Code coverage - run: | - cd build - for test_file in $(find cproducer.dir kvsCommonCurl.dir kvsCommonLws.dir -name '*.gcno'); do gcov $test_file; done - bash <(curl -s https://codecov.io/bash) - address-sanitizer: runs-on: ubuntu-20.04 permissions: @@ -240,6 +212,10 @@ jobs: CC: clang CXX: clang++ AWS_KVS_LOG_LEVEL: 2 + # The unit tests will have both LWS and CURL enabled, both linking to globalMemCalloc. Our PIC is + # has quite a few global definitions that get linked twice. TODO: see if we can list common dependency + # to remove this option + ASAN_OPTIONS: detect_odr_violation=0 steps: - name: Clone repository uses: actions/checkout@v3 @@ -259,6 +235,7 @@ jobs: run: | cd build ulimit -c unlimited -S + source ../scripts/get-iot-certs.sh ci timeout --signal=SIGABRT 150m ./tst/producer_test --gtest_break_on_failure undefined-behavior-sanitizer: @@ -289,6 +266,7 @@ jobs: run: | cd build ulimit -c unlimited -S + source ../scripts/get-iot-certs.sh ci timeout --signal=SIGABRT 150m ./tst/producer_test --gtest_break_on_failure @@ -366,7 +344,7 @@ jobs: - name: Build repository run: | mkdir build && cd build - cmake .. -DBUILD_TEST=TRUE + cmake .. -DBUILD_TEST=TRUE -DBUILD_COMMON_LWS=ON make - name: Configure AWS Credentials uses: aws-actions/configure-aws-credentials@v1-node16 @@ -379,6 +357,7 @@ jobs: run: | cd build ulimit -c unlimited -S + source ../scripts/get-iot-certs.sh ci timeout --signal=SIGABRT 150m ./tst/producer_test --gtest_break_on_failure windows-msvc: @@ -409,7 +388,7 @@ jobs: - name: Run tests run: | $env:Path += ';C:\Strawberry\perl\site\bin;C:\Strawberry\perl\bin;C:\Strawberry\c\bin;C:\Program Files\NASM;D:\a\amazon-kinesis-video-streams-producer-c\amazon-kinesis-video-streams-producer-c\open-source\lib;D:\a\amazon-kinesis-video-streams-producer-c\amazon-kinesis-video-streams-producer-c\open-source\bin' - & "D:\a\amazon-kinesis-video-streams-producer-c\amazon-kinesis-video-streams-producer-c\build\tst\producer_test.exe" --gtest_filter="-ProducerFunctionalityTest.pressure_on_buffer_duration_fail_new_connection_at_token_rotation" + & "D:\a\amazon-kinesis-video-streams-producer-c\amazon-kinesis-video-streams-producer-c\build\tst\producer_test.exe" --gtest_filter="-IoTCredentialTest.*:ProducerFunctionalityTest.pressure_on_buffer_duration_fail_new_connection_at_token_rotation" arm64-cross-compilation: runs-on: ubuntu-20.04 diff --git a/.github/workflows/codecov.yml b/.github/workflows/codecov.yml new file mode 100644 index 000000000..7a620c0ae --- /dev/null +++ b/.github/workflows/codecov.yml @@ -0,0 +1,51 @@ +name: Producer C SDK Codecov + +on: + push: + branches: + - develop + - master + pull_request: + branches: + - develop + - master + +jobs: + linux-gcc-code-coverage: + runs-on: ubuntu-20.04 + env: + AWS_KVS_LOG_LEVEL: 2 + permissions: + id-token: write + contents: read + steps: + - name: Clone repository + uses: actions/checkout@v3 + - name: Build repository + run: | + sudo apt install lcov + mkdir build && cd build + cmake .. -DCODE_COVERAGE=TRUE -DBUILD_TEST=TRUE -DBUILD_COMMON_LWS=TRUE + make + - name: Configure AWS Credentials + uses: aws-actions/configure-aws-credentials@v1-node16 + with: + role-to-assume: ${{ secrets.AWS_ROLE_TO_ASSUME }} + role-session-name: ${{ secrets.AWS_ROLE_SESSION_NAME }} + aws-region: ${{ secrets.AWS_REGION }} + role-duration-seconds: 10800 + - name: Run tests + run: | + cd build + ulimit -c unlimited -S + source ../scripts/get-iot-certs.sh ci + timeout --signal=SIGABRT 150m ./tst/producer_test --gtest_break_on_failure + - name: Generate and process coverage data with lcov + run: | + cd build + lcov --capture --directory CMakeFiles/cproducer.dir --directory CMakeFiles/kvsCommonCurl.dir --directory CMakeFiles/kvsCommonLws.dir --output-file coverage.info + lcov --list coverage.info + lcov --remove coverage.info '*jsmn*' -o coverage_filtered.info + lcov --list coverage_filtered.info + rm coverage.info + bash <(curl -s https://codecov.io/bash) -f ./coverage_filtered.info -t ${{ secrets.CODECOV_TOKEN }} -Z diff --git a/CMake/Dependencies/libwebsockets-CMakeLists.txt b/CMake/Dependencies/libwebsockets-CMakeLists.txt index 3ada49e4b..fbf9f36dc 100644 --- a/CMake/Dependencies/libwebsockets-CMakeLists.txt +++ b/CMake/Dependencies/libwebsockets-CMakeLists.txt @@ -20,7 +20,7 @@ endif() ExternalProject_Add(project_libwebsockets GIT_REPOSITORY https://github.com/warmcat/libwebsockets.git - GIT_TAG v3.2.3 + GIT_TAG v4.3.3 PREFIX ${CMAKE_CURRENT_BINARY_DIR}/build CMAKE_ARGS -DCMAKE_INSTALL_PREFIX=${OPEN_SRC_INSTALL_PREFIX} diff --git a/README.md b/README.md index fbf04263c..13e348dc0 100644 --- a/README.md +++ b/README.md @@ -138,10 +138,11 @@ This will stream the audio files from the `samples/aacSampleFrames` or `samples/ To run the samples with IoT credential provider: -1. Run the IoT thing generation script available under `scripts`: `source scripts/generate-iot-credential.sh`. For more information on IoT set up, visit [AWS KVS IoT Set up](https://docs.aws.amazon.com/kinesisvideostreams/latest/dg/how-iot.html) -2. Uncomment `#define IOT_CORE_ENABLE_CREDENTIALS 1` in the relevant sample -3. Build the changes: `make` -4. Run the sample using the instructions in previous section. +1. Run the IoT thing generation script available under `scripts`: `source scripts/generate-iot-credential.sh`. +2. Then run `source scripts/get-iot-certs.sh` to generate the certs and set the relevant envs. For more information on IoT set up, visit [AWS KVS IoT Set up](https://docs.aws.amazon.com/kinesisvideostreams/latest/dg/how-iot.html) +3. Uncomment `#define IOT_CORE_ENABLE_CREDENTIALS 1` in the relevant sample +4. Build the changes: `make` +5. Run the sample using the instructions in previous section. ### Fragment metadata diff --git a/scripts/generate-iot-credential.sh b/scripts/generate-iot-credential.sh index 29801cb01..0aaee193e 100755 --- a/scripts/generate-iot-credential.sh +++ b/scripts/generate-iot-credential.sh @@ -15,9 +15,9 @@ iotPrivateKey="producerc_private.key" # Step 1: Create an IoT Thing Type and an IoT Thing # The following example command creates a thing type $thingTypeName -aws --profile default iot create-thing-type --thing-type-name $thingTypeName > iot-thing-type.json +aws iot create-thing-type --thing-type-name $thingTypeName > iot-thing-type.json # And this example command creates the $thingName thing of the $thingTypeName thing type: -aws --profile default iot create-thing --thing-name $thingName --thing-type-name $thingTypeName > iot-thing.json +aws iot create-thing --thing-name $thingName --thing-type-name $thingTypeName > iot-thing.json # Step 2: Create an IAM Role to be Assumed by IoT # You can use the following trust policy JSON for the iam-policy-document.json: @@ -37,7 +37,7 @@ cat > iam-policy-document.json < iam-role.json +aws iam create-role --role-name $iotRoleName --assume-role-policy-document 'file://iam-policy-document.json' > iam-role.json # You can use the following IAM policy JSON for the iam-permission-document.json: cat > iam-permission-document.json < iam-permission-document.json < iot-role-alias.json +aws iot create-role-alias --role-alias $iotRoleAlias --role-arn $(jq --raw-output '.Role.Arn' iam-role.json) --credential-duration-seconds 3600 > iot-role-alias.json # You can use the following command to create the iot-policy-document.json document JSON: cat > iot-policy-document.json << EOF @@ -95,21 +95,4 @@ cat > iot-policy-document.json << EOF EOF # Now you can create the policy that will enable IoT to assume role with the certificate (once it is attached) using the role alias. -aws --profile default iot create-policy --policy-name $iotPolicyName --policy-document 'file://iot-policy-document.json' - -# Create the certificate to which you must attach the policy for IoT that you created above. -aws --profile default iot create-keys-and-certificate --set-as-active --certificate-pem-outfile $iotCert --public-key-outfile $iotPublicKey --private-key-outfile $iotPrivateKey > certificate -# Attach the policy for IoT (KvsCameraIoTPolicy created above) to this certificate. -aws --profile default iot attach-policy --policy-name $iotPolicyName --target $(jq --raw-output '.certificateArn' certificate) -# Attach your IoT thing (kvs_example_camera_stream) to the certificate you just created: -aws --profile default iot attach-thing-principal --thing-name $thingName --principal $(jq --raw-output '.certificateArn' certificate) -# In order to authorize requests through the IoT credentials provider, you need the IoT credentials endpoint which is unique to your AWS account ID. You can use the following command to get the IoT credentials endpoint. -aws --profile default iot describe-endpoint --endpoint-type iot:CredentialProvider --output text > iot-credential-provider.txt -# In addition to the X.509 cerficiate created above, you must also have a CA certificate to establish trust with the back-end service through TLS. You can get the CA certificate using the following command: -curl 'https://www.amazontrust.com/repository/SFSRootCAG2.pem' --output cacert.pem - -export AWS_IOT_CORE_CREDENTIAL_ENDPOINT=$(cat iot-credential-provider.txt) -export AWS_IOT_CORE_CERT=$(pwd)"/"$iotCert -export AWS_IOT_CORE_PRIVATE_KEY=$(pwd)"/"$iotPrivateKey -export AWS_IOT_CORE_ROLE_ALIAS=$iotRoleAlias -export AWS_IOT_CORE_THING_NAME=$thingName \ No newline at end of file +aws iot create-policy --policy-name $iotPolicyName --policy-document 'file://iot-policy-document.json' \ No newline at end of file diff --git a/scripts/get-iot-certs.sh b/scripts/get-iot-certs.sh new file mode 100644 index 000000000..0dbcbe7dd --- /dev/null +++ b/scripts/get-iot-certs.sh @@ -0,0 +1,28 @@ +#!/bin/bash + +prefix=$1 +thingName="${prefix}_thing" +thingTypeName="${prefix}_thing_type" +iotPolicyName="${prefix}_policy" +iotRoleName="${prefix}_role" +iotRoleAlias="${prefix}_role_alias" +iotCert="${prefix}_certificate.pem" +iotPublicKey="${prefix}_public.key" +iotPrivateKey="${prefix}_private.key" + +# Create the certificate to which you must attach the policy for IoT that you created above. +aws iot create-keys-and-certificate --set-as-active --certificate-pem-outfile $iotCert --public-key-outfile $iotPublicKey --private-key-outfile $iotPrivateKey > certificate +# Attach the policy for IoT (KvsCameraIoTPolicy created above) to this certificate. +aws iot attach-policy --policy-name $iotPolicyName --target $(jq --raw-output '.certificateArn' certificate) +# Attach your IoT thing (kvs_example_camera_stream) to the certificate you just created: +aws iot attach-thing-principal --thing-name $thingName --principal $(jq --raw-output '.certificateArn' certificate) +# In order to authorize requests through the IoT credentials provider, you need the IoT credentials endpoint which is unique to your AWS account ID. You can use the following command to get the IoT credentials endpoint. +aws iot describe-endpoint --endpoint-type iot:CredentialProvider --output text > iot-credential-provider.txt +# In addition to the X.509 cerficiate created above, you must also have a CA certificate to establish trust with the back-end service through TLS. You can get the CA certificate using the following command: +curl 'https://www.amazontrust.com/repository/SFSRootCAG2.pem' --output cacert.pem + +export AWS_IOT_CORE_CREDENTIAL_ENDPOINT=$(cat iot-credential-provider.txt) +export AWS_IOT_CORE_CERT=$(pwd)"/"$iotCert +export AWS_IOT_CORE_PRIVATE_KEY=$(pwd)"/"$iotPrivateKey +export AWS_IOT_CORE_ROLE_ALIAS=$iotRoleAlias +export AWS_IOT_CORE_THING_NAME=$thingName \ No newline at end of file diff --git a/src/source/CallbacksProvider.c b/src/source/CallbacksProvider.c index 28572c5d9..54ba90f59 100644 --- a/src/source/CallbacksProvider.c +++ b/src/source/CallbacksProvider.c @@ -230,7 +230,7 @@ STATUS createDefaultCallbacksProviderWithFileAuth(PCHAR credentialsFilePath, PCH } if (pAuthCallbacks != NULL) { - freeIotAuthCallbacks(&pAuthCallbacks); + freeFileAuthCallbacks(&pAuthCallbacks); } if (pStreamCallbacks != NULL) { diff --git a/src/source/Common/AwsV4Signer.c b/src/source/Common/AwsV4Signer.c index 19b5831ad..f0257d2fb 100644 --- a/src/source/Common/AwsV4Signer.c +++ b/src/source/Common/AwsV4Signer.c @@ -916,14 +916,14 @@ STATUS uriDecodeString(PCHAR pSrc, UINT32 srcLen, PCHAR pDst, PUINT32 pDstLen) PCHAR pCurPtr = pSrc, pDec = pDst; CHAR ch; + CHK(pSrc != NULL && pDstLen != NULL, STATUS_NULL_ARG); + // Set the source length to max if not specified strLen = (srcLen == 0) ? MAX_UINT32 : srcLen; // Set the remaining length remaining = (pDst == NULL) ? MAX_UINT32 : *pDstLen; - CHK(pSrc != NULL && pDstLen != NULL, STATUS_NULL_ARG); - while (((UINT32) (pCurPtr - pSrc) < strLen) && ((ch = *pCurPtr) != '\0')) { if (ch == '%') { CHK((UINT32) (pCurPtr - pSrc) + decLen <= strLen && *(pCurPtr + 1) != '\0' && *(pCurPtr + 2) != '\0', STATUS_INVALID_ARG); diff --git a/src/source/Common/IotCredentialProvider.c b/src/source/Common/IotCredentialProvider.c index ba23b185e..3deedd7c3 100644 --- a/src/source/Common/IotCredentialProvider.c +++ b/src/source/Common/IotCredentialProvider.c @@ -25,7 +25,7 @@ STATUS createIotCredentialProviderWithTime(PCHAR iotGetCredentialEndpoint, PCHAR pIotCredentialProvider->getCurrentTimeFn = (getCurrentTimeFn == NULL) ? commonDefaultGetCurrentTimeFunc : getCurrentTimeFn; pIotCredentialProvider->customData = customData; - CHK(STRNLEN(iotGetCredentialEndpoint, MAX_URI_CHAR_LEN + 1) <= MAX_URI_CHAR_LEN, MAX_URI_CHAR_LEN); + CHK(STRNLEN(iotGetCredentialEndpoint, MAX_URI_CHAR_LEN + 1) <= MAX_URI_CHAR_LEN, STATUS_INVALID_ARG); STRNCPY(pIotCredentialProvider->iotGetCredentialEndpoint, iotGetCredentialEndpoint, MAX_URI_CHAR_LEN); CHK(STRNLEN(roleAlias, MAX_ROLE_ALIAS_LEN + 1) <= MAX_ROLE_ALIAS_LEN, STATUS_MAX_ROLE_ALIAS_LEN_EXCEEDED); diff --git a/src/source/Common/RequestInfo.c b/src/source/Common/RequestInfo.c index 86e7d9365..2a9acc28e 100644 --- a/src/source/Common/RequestInfo.c +++ b/src/source/Common/RequestInfo.c @@ -245,7 +245,8 @@ STATUS removeRequestHeader(PRequestInfo pRequestInfo, PCHAR headerName) if (STRCMPI(pCurrentHeader->pName, headerName) == 0) { CHK_STATUS(singleListDeleteNode(pRequestInfo->pRequestHeaders, pCurNode)); - + // Free only if found + SAFE_MEMFREE(pCurrentHeader); // Early return CHK(FALSE, retStatus); } @@ -255,8 +256,6 @@ STATUS removeRequestHeader(PRequestInfo pRequestInfo, PCHAR headerName) CleanUp: - SAFE_MEMFREE(pCurrentHeader); - return retStatus; } diff --git a/src/source/Response.c b/src/source/Response.c index b96c05108..6d979664f 100644 --- a/src/source/Response.c +++ b/src/source/Response.c @@ -282,6 +282,7 @@ VOID terminateCurlSession(PCurlResponse pCurlResponse, UINT64 timeout) } } +/* UNCOMMENT if required to use for CURL debugging. Not used at the moment. VOID dumpResponseCurlEasyInfo(PCurlResponse pCurlResponse) { DOUBLE time; @@ -353,6 +354,7 @@ VOID dumpResponseCurlEasyInfo(PCurlResponse pCurlResponse) } } } + */ SERVICE_CALL_RESULT getServiceCallResultFromCurlStatus(CURLcode curlStatus) { diff --git a/src/source/Response.h b/src/source/Response.h index 5f625f4a9..60fe1b241 100644 --- a/src/source/Response.h +++ b/src/source/Response.h @@ -109,20 +109,18 @@ STATUS freeCurlResponse(PCurlResponse*); STATUS closeCurlHandles(PCurlResponse); /** - * Debug prints statistics of the request/response + * Terminates the ongoing curl session * * @param - PCurlResponse - IN - Response object + * @param - UINT64 - IN - Timeout value to use in 100ns * */ -VOID dumpResponseCurlEasyInfo(PCurlResponse); -/** - * Terminates the ongoing curl session - * - * @param - PCurlResponse - IN - Response object - * @param - UINT64 - IN - Timeout value to use in 100ns +/* @param - PCurlResponse - IN - Response object * + * VOID dumpResponseCurlEasyInfo(PCurlResponse); */ + VOID terminateCurlSession(PCurlResponse, UINT64); /** diff --git a/src/source/StreamInfoProvider.c b/src/source/StreamInfoProvider.c index 5bbdb0230..47e40c6f8 100644 --- a/src/source/StreamInfoProvider.c +++ b/src/source/StreamInfoProvider.c @@ -112,7 +112,7 @@ STATUS createVideoTrackInfo(VIDEO_CODEC_ID videoCodecId, PCHAR contentType, PTra break; default: STRCPY(pTrackInfo->codecId, MKV_H264_AVC_CODEC_ID); - STRCAT(contentType, MKV_H265_CONTENT_TYPE); + STRCAT(contentType, MKV_H264_CONTENT_TYPE); } STRCPY(pTrackInfo->trackName, DEFAULT_VIDEO_TRACK_NAME); pTrackInfo->trackType = MKV_TRACK_INFO_TYPE_VIDEO; diff --git a/tst/AuthCallbackTest.cpp b/tst/AuthCallbackTest.cpp index 270129bb1..0eb09723f 100644 --- a/tst/AuthCallbackTest.cpp +++ b/tst/AuthCallbackTest.cpp @@ -19,13 +19,16 @@ TEST_F(AuthCallbackTest, ioTExpirationParsing_Returns_Success) CHAR validFormatIotExpirationTimeStamp[] = "2019-01-31T23:00:59Z"; // expiration is current time + 1 hour UINT64 expirationTimestampInEpoch = 0; - convertTimestampToEpoch(validFormatIotExpirationTimeStamp, iotTimeInEpoch, &expirationTimestampInEpoch); + EXPECT_EQ(STATUS_NULL_ARG, convertTimestampToEpoch(NULL, iotTimeInEpoch, &expirationTimestampInEpoch)); + EXPECT_EQ(STATUS_NULL_ARG, convertTimestampToEpoch(NULL, iotTimeInEpoch, NULL)); + EXPECT_EQ(STATUS_NULL_ARG, convertTimestampToEpoch(validFormatIotExpirationTimeStamp, iotTimeInEpoch, NULL)); + EXPECT_EQ(STATUS_SUCCESS, convertTimestampToEpoch(validFormatIotExpirationTimeStamp, iotTimeInEpoch, &expirationTimestampInEpoch)); EXPECT_TRUE(iotTimeInEpoch == expirationTimestampInEpoch / HUNDREDS_OF_NANOS_IN_A_SECOND - 3600); iotTimeInEpoch = 1548975659; // iot expiration same as current time - convertTimestampToEpoch(validFormatIotExpirationTimeStamp, iotTimeInEpoch, &expirationTimestampInEpoch); + EXPECT_EQ(STATUS_SUCCESS,convertTimestampToEpoch(validFormatIotExpirationTimeStamp, iotTimeInEpoch, &expirationTimestampInEpoch)); EXPECT_TRUE(iotTimeInEpoch == expirationTimestampInEpoch / HUNDREDS_OF_NANOS_IN_A_SECOND); @@ -71,11 +74,7 @@ TEST_F(AuthCallbackTest, verify_fileAuthCallback_provider_works) PCHAR authFilePath = NULL; PAuthCallbacks pAuthCallbacks = NULL; - authFilePath = getenv(TEST_AUTH_FILE_PATH); - if (authFilePath == NULL) { - DLOGI("Auth file not provided, passing test"); - return; - } + authFilePath = SAMPLE_AUTH_FILE_PATH; STRNCPY(streamName, (PCHAR) TEST_STREAM_NAME, MAX_STREAM_NAME_LEN); streamName[MAX_STREAM_NAME_LEN] = '\0'; @@ -96,12 +95,6 @@ TEST_F(AuthCallbackTest, verify_fileAuthCallback_provider_works) authFilePath, &pAuthCallbacks)); - EXPECT_EQ(STATUS_SUCCESS, createKinesisVideoClientSync(pDeviceInfo, pClientCallbacks, &clientHandle)); - EXPECT_EQ(STATUS_SUCCESS, createKinesisVideoStreamSync(clientHandle, pStreamInfo, &streamHandle)); - - EXPECT_EQ(STATUS_SUCCESS, stopKinesisVideoStreamSync(streamHandle)); - EXPECT_EQ(STATUS_SUCCESS, freeKinesisVideoStream(&streamHandle)); - EXPECT_EQ(STATUS_SUCCESS, freeKinesisVideoClient(&clientHandle)); EXPECT_EQ(STATUS_SUCCESS, freeDeviceInfo(&pDeviceInfo)); EXPECT_EQ(STATUS_SUCCESS, freeStreamInfoProvider(&pStreamInfo)); EXPECT_EQ(STATUS_SUCCESS, freeCallbacksProvider(&pClientCallbacks)); @@ -136,6 +129,8 @@ TEST_F(AuthCallbackTest, credential_provider_auth_callbacks_test) NULL, &pClientCallbacks)); + EXPECT_EQ(STATUS_NULL_ARG, createStaticCredentialProvider(mAccessKey, 0, mSecretKey, 0, mSessionToken, 0, + MAX_UINT64, NULL)); // Create the credential provider based on static credentials which will be used with auth callbacks EXPECT_EQ(STATUS_SUCCESS, createStaticCredentialProvider(mAccessKey, 0, mSecretKey, 0, mSessionToken, 0, MAX_UINT64, &pAwsCredentialProvider)); @@ -159,6 +154,7 @@ TEST_F(AuthCallbackTest, credential_provider_auth_callbacks_test) EXPECT_EQ(STATUS_SUCCESS, freeDeviceInfo(&pDeviceInfo)); EXPECT_EQ(STATUS_SUCCESS, freeStreamInfoProvider(&pStreamInfo)); EXPECT_EQ(STATUS_SUCCESS, freeCallbacksProvider(&pClientCallbacks)); + EXPECT_EQ(STATUS_NULL_ARG, freeStaticCredentialProvider(NULL)); EXPECT_EQ(STATUS_SUCCESS, freeStaticCredentialProvider(&pAwsCredentialProvider)); } } // namespace video diff --git a/tst/AwsCredentialsTest.cpp b/tst/AwsCredentialsTest.cpp index 4d7f82434..108776ac2 100644 --- a/tst/AwsCredentialsTest.cpp +++ b/tst/AwsCredentialsTest.cpp @@ -237,6 +237,8 @@ TEST_F(AwsCredentialsTest, TestFileCredentialsWriteWithoutSession) { CHAR fileContent[10000]; UINT32 length = ARRAY_SIZE(fileContent); + EXPECT_EQ(STATUS_NULL_ARG, createFileCredentialProvider(TEST_FILE_CREDENTIALS_FILE_PATH, NULL)); + EXPECT_EQ(STATUS_INVALID_ARG, createFileCredentialProvider(EMPTY_STRING, &pAwsCredentialProvider)); // Store the credentials in a file under the current dir length = SNPRINTF(fileContent, length, "CREDENTIALS %s %s", mAccessKey, mSecretKey); ASSERT_GT(ARRAY_SIZE(fileContent), length); @@ -244,6 +246,7 @@ TEST_F(AwsCredentialsTest, TestFileCredentialsWriteWithoutSession) { // Create file creds provider from the file EXPECT_EQ(STATUS_SUCCESS, createFileCredentialProvider(TEST_FILE_CREDENTIALS_FILE_PATH, &pAwsCredentialProvider)); + EXPECT_EQ(STATUS_NULL_ARG, freeFileCredentialProvider(NULL)); EXPECT_EQ(STATUS_SUCCESS, freeFileCredentialProvider(&pAwsCredentialProvider)); } diff --git a/tst/AwsV4SignerTest.cpp b/tst/AwsV4SignerTest.cpp new file mode 100644 index 000000000..1969609e8 --- /dev/null +++ b/tst/AwsV4SignerTest.cpp @@ -0,0 +1,117 @@ +#include "ProducerTestFixture.h" +#include "src/source/Common/AwsV4Signer.h" + +namespace com { namespace amazonaws { namespace kinesis { namespace video { + +class AwsV4SignerTest : public ProducerClientTestBase { +}; + +TEST_F(AwsV4SignerTest, generateAwsSigV4SignatureApiInvalidTest) +{ + RequestInfo requestInfo; + PCHAR pSigningInfo; + UINT32 length; + EXPECT_EQ(STATUS_NULL_ARG, generateAwsSigV4Signature(NULL, NULL, TRUE, NULL, NULL)); + EXPECT_EQ(STATUS_NULL_ARG, generateAwsSigV4Signature(&requestInfo, NULL, TRUE, &pSigningInfo, NULL)); +} + +TEST_F(AwsV4SignerTest, signAwsRequestInfoQueryParamApiTest) +{ + RequestInfo requestInfo; + AwsCredentials awsCredentials; + // Initialize requestInfo and awsCredentials with default values + MEMSET(&requestInfo, 0, SIZEOF(requestInfo)); + MEMSET(&awsCredentials, 0, SIZEOF(awsCredentials)); + + // Set up default awsCredentials + awsCredentials.expiration = GETTIME() + 3600 * HUNDREDS_OF_NANOS_IN_A_SECOND; // 1 hour from now + + // Default setup for requestInfo + STRCPY(requestInfo.url, "https://kinesisvideo.us-west-2.amazonaws.com/describeStream?query=param"); + requestInfo.currentTime = GETTIME(); + requestInfo.pAwsCredentials = &awsCredentials; + EXPECT_EQ(STATUS_NULL_ARG, signAwsRequestInfoQueryParam(NULL)); + EXPECT_EQ(STATUS_SUCCESS, singleListCreate(&requestInfo.pRequestHeaders)); + EXPECT_EQ(STATUS_SUCCESS, signAwsRequestInfoQueryParam(&requestInfo)); + EXPECT_EQ(STATUS_SUCCESS, removeRequestHeaders(&requestInfo)); + EXPECT_EQ(STATUS_SUCCESS, singleListFree(requestInfo.pRequestHeaders)); +} + +TEST_F(AwsV4SignerTest, getCanonicalQueryParamsApiTest) +{ + PCHAR pQuery; + RequestInfo requestInfo; + EXPECT_EQ(STATUS_NULL_ARG, getCanonicalQueryParams(NULL, 0, TRUE, NULL, NULL)); + EXPECT_EQ(STATUS_NULL_ARG, getCanonicalQueryParams(requestInfo.url, STRLEN(requestInfo.url), TRUE, NULL, NULL)); + EXPECT_EQ(STATUS_NULL_ARG, getCanonicalQueryParams(requestInfo.url, STRLEN(requestInfo.url), TRUE, &pQuery, NULL)); +} + +TEST_F(AwsV4SignerTest, signAwsRequestInfoApiTest) +{ + RequestInfo requestInfo; + AwsCredentials awsCredentials; + UINT32 len; + // Initialize requestInfo and awsCredentials with default values + MEMSET(&requestInfo, 0, SIZEOF(requestInfo)); + MEMSET(&awsCredentials, 0, SIZEOF(awsCredentials)); + + // Set up default awsCredentials + awsCredentials.expiration = GETTIME() + 3600 * HUNDREDS_OF_NANOS_IN_A_SECOND; // 1 hour from now + // Default setup for requestInfo + STRCPY(requestInfo.url, "https://kinesisvideo.us-west-2.amazonaws.com/describeStream"); + requestInfo.currentTime = GETTIME(); + requestInfo.pAwsCredentials = &awsCredentials; + + EXPECT_EQ(STATUS_NULL_ARG, signAwsRequestInfo(NULL)); + EXPECT_EQ(STATUS_SUCCESS, singleListCreate(&requestInfo.pRequestHeaders)); + EXPECT_EQ(STATUS_SUCCESS, signAwsRequestInfo(&requestInfo)); + EXPECT_EQ(STATUS_SUCCESS, removeRequestHeaders(&requestInfo)); + EXPECT_EQ(STATUS_SUCCESS, singleListFree(requestInfo.pRequestHeaders)); +} + +TEST_F(AwsV4SignerTest, generateSignatureDateTimeApiTest) +{ + EXPECT_EQ(STATUS_NULL_ARG, generateSignatureDateTime(GETTIME(), NULL)); +} + +TEST_F(AwsV4SignerTest, uriDecodeStringApiTest) +{ + CHAR source[] = "Hello%20World%21"; + CHAR invalidSource[] = "Hello%2"; + CHAR destination[50]; + CHAR invalidSrcDest[50]; + CHAR smallDestination[5]; + UINT32 destLen = SIZEOF(destination); + UINT32 smallDestLen = SIZEOF(smallDestination); + UINT32 invalidSrcDestLen = SIZEOF(invalidSrcDest); + EXPECT_EQ(STATUS_SUCCESS, uriDecodeString(source, 0, destination, &destLen)); + EXPECT_STREQ("Hello World!", destination); + EXPECT_EQ(13U, destLen); // Includes null terminator + EXPECT_EQ(STATUS_NULL_ARG, uriDecodeString(NULL, 0, destination, &destLen)); + EXPECT_EQ(STATUS_NULL_ARG, uriDecodeString(source, STRLEN(source), destination, NULL)); + EXPECT_EQ(STATUS_BUFFER_TOO_SMALL, uriDecodeString(source, STRLEN(source), smallDestination, &smallDestLen)); + EXPECT_EQ(STATUS_INVALID_ARG, uriDecodeString(invalidSource, STRLEN(invalidSource), invalidSrcDest, &invalidSrcDestLen)); +} + +TEST_F(AwsV4SignerTest, uriEncodeStringApiTest) +{ + CHAR input[] = "$"; + CHAR outputInvalid[2] = {'\0'}; // Space required for 3 characters + '\0' . But reserved only for 2 + CHAR outputValid[4] = {'\0'}; + UINT32 outputInvalidLength = SIZEOF(outputInvalid); + UINT32 outputValidLength = SIZEOF(outputValid); + EXPECT_EQ(STATUS_NOT_ENOUGH_MEMORY, uriEncodeString(input, STRLEN(input), outputInvalid, &outputInvalidLength)); + EXPECT_EQ(STATUS_SUCCESS, uriEncodeString(input, STRLEN(input), outputValid, &outputValidLength)); +} +TEST_F(AwsV4SignerTest, getRequestVerbStringApiTest) +{ + EXPECT_STREQ(HTTP_REQUEST_VERB_PUT_STRING, getRequestVerbString(HTTP_REQUEST_VERB_PUT)); + EXPECT_STREQ(HTTP_REQUEST_VERB_GET_STRING, getRequestVerbString(HTTP_REQUEST_VERB_GET)); + EXPECT_STREQ(HTTP_REQUEST_VERB_POST_STRING, getRequestVerbString(HTTP_REQUEST_VERB_POST)); + EXPECT_EQ(NULL, getRequestVerbString((HTTP_REQUEST_VERB) 0xffff)); +} + +} // namespace video +} // namespace kinesis +} // namespace amazonaws +} // namespace com \ No newline at end of file diff --git a/tst/CMakeLists.txt b/tst/CMakeLists.txt index 50e3c738a..ace3dde03 100644 --- a/tst/CMakeLists.txt +++ b/tst/CMakeLists.txt @@ -19,6 +19,14 @@ endif() include_directories(${KINESIS_VIDEO_PRODUCER_C_SRC}) file(GLOB PRODUCER_TEST_SOURCE_FILES "*.cpp") +file(COPY "${CMAKE_CURRENT_SOURCE_DIR}/sample_file_creds.txt" DESTINATION .) add_executable(producer_test ${PRODUCER_TEST_SOURCE_FILES}) target_link_libraries(producer_test cproducer ${GTEST_LIBNAME}) + +if(BUILD_COMMON_LWS) + add_definitions(-DKVS_CA_CERT_PATH="${CMAKE_CURRENT_SOURCE_DIR}/cert.pem") + add_definitions(-DCMAKE_DETECTED_CACERT_PATH) + target_compile_definitions(producer_test PRIVATE KVS_BUILD_WITH_LWS) + target_link_libraries(producer_test kvsCommonLws ${GTEST_LIBNAME}) +endif() \ No newline at end of file diff --git a/tst/CallbacksProviderPublicApiTest.cpp b/tst/CallbacksProviderPublicApiTest.cpp index 7e214d24a..22c2dd8ba 100644 --- a/tst/CallbacksProviderPublicApiTest.cpp +++ b/tst/CallbacksProviderPublicApiTest.cpp @@ -639,6 +639,16 @@ TEST_F(CallbacksProviderPublicApiTest, createDefaultCallbacksProviderWithFileAut TEST_USER_AGENT, &pClientCallbacks)); EXPECT_EQ(NULL, pClientCallbacks); + + EXPECT_EQ(STATUS_SUCCESS, createDefaultCallbacksProviderWithFileAuth( + SAMPLE_AUTH_FILE_PATH, + (PCHAR) DEFAULT_AWS_REGION, + TEST_CA_CERT_PATH, + TEST_USER_AGENT_POSTFIX, + TEST_USER_AGENT, + &pClientCallbacks)); + + EXPECT_EQ(STATUS_SUCCESS, freeCallbacksProvider(&pClientCallbacks)); } TEST_F(CallbacksProviderPublicApiTest, createDefaultCallbacksProviderWithAuthCallbacks) diff --git a/tst/CurlApiTest.cpp b/tst/CurlApiTest.cpp new file mode 100644 index 000000000..3c06bd5a0 --- /dev/null +++ b/tst/CurlApiTest.cpp @@ -0,0 +1,39 @@ +#include "ProducerTestFixture.h" +#include "src/source/Response.h" + +namespace com { namespace amazonaws { namespace kinesis { namespace video { + +class CurlApiTest : public ProducerClientTestBase { +}; + + +TEST_F(CurlApiTest, createCurlRequestApiNullTest) +{ + CHAR url[MAX_URI_CHAR_LEN]; + CurlApiCallbacks curlApiCallbacks; + EXPECT_EQ(STATUS_NULL_ARG, createCurlRequest(HTTP_REQUEST_VERB_POST, NULL, NULL, INVALID_STREAM_HANDLE_VALUE, (PCHAR) DEFAULT_AWS_REGION, GETTIME(), + 3 * HUNDREDS_OF_NANOS_IN_A_SECOND, 5 * HUNDREDS_OF_NANOS_IN_A_SECOND, 5, NULL, NULL, + NULL, NULL)); + EXPECT_EQ(STATUS_NULL_ARG, createCurlRequest(HTTP_REQUEST_VERB_POST, url, NULL, INVALID_STREAM_HANDLE_VALUE, (PCHAR) DEFAULT_AWS_REGION, GETTIME(), + 3 * HUNDREDS_OF_NANOS_IN_A_SECOND, 5 * HUNDREDS_OF_NANOS_IN_A_SECOND, 5, NULL, NULL, + NULL, NULL)); + EXPECT_EQ(STATUS_NULL_ARG, createCurlRequest(HTTP_REQUEST_VERB_POST, url, NULL, INVALID_STREAM_HANDLE_VALUE, (PCHAR) DEFAULT_AWS_REGION, GETTIME(), + 3 * HUNDREDS_OF_NANOS_IN_A_SECOND, 5 * HUNDREDS_OF_NANOS_IN_A_SECOND, 5, NULL, NULL, + &curlApiCallbacks, NULL)); +} + +TEST_F(CurlApiTest, getServiceCallResultFromCurlStatusApiTest) +{ + EXPECT_EQ(SERVICE_CALL_RESULT_OK, getServiceCallResultFromCurlStatus(CURLE_OK)); + EXPECT_EQ(SERVICE_CALL_INVALID_ARG, getServiceCallResultFromCurlStatus(CURLE_UNSUPPORTED_PROTOCOL)); + EXPECT_EQ(SERVICE_CALL_NETWORK_CONNECTION_TIMEOUT, getServiceCallResultFromCurlStatus(CURLE_OPERATION_TIMEDOUT)); + EXPECT_EQ(SERVICE_CALL_NOT_AUTHORIZED, getServiceCallResultFromCurlStatus(CURLE_SSL_CERTPROBLEM)); + EXPECT_EQ(SERVICE_CALL_NOT_AUTHORIZED, getServiceCallResultFromCurlStatus(CURLE_SSL_CACERT)); + EXPECT_EQ(SERVICE_CALL_UNKNOWN, getServiceCallResultFromCurlStatus((CURLcode) 0xffff)); +} + + +} // namespace video +} // namespace kinesis +} // namespace amazonaws +} // namespace com; \ No newline at end of file diff --git a/tst/InfoProviderApiTest.cpp b/tst/InfoProviderApiTest.cpp index 262aa9049..3c0facca5 100644 --- a/tst/InfoProviderApiTest.cpp +++ b/tst/InfoProviderApiTest.cpp @@ -2,325 +2,452 @@ namespace com { namespace amazonaws { namespace kinesis { namespace video { - class InfoProviderApiTest : public ProducerClientTestBase { - }; +class InfoProviderApiTest : public ProducerClientTestBase { +}; - TEST_F(InfoProviderApiTest, CreateDefaultDeviceInfo_Returns_Success) - { - PDeviceInfo pDeviceInfo; - - EXPECT_EQ(STATUS_SUCCESS, createDefaultDeviceInfo(&pDeviceInfo)); - - EXPECT_EQ(STATUS_SUCCESS, setDeviceInfoStorageSize(pDeviceInfo, MAX_STORAGE_ALLOCATION_SIZE)); - - EXPECT_EQ(STATUS_INVALID_STORAGE_SIZE, setDeviceInfoStorageSize(pDeviceInfo, MAX_STORAGE_ALLOCATION_SIZE + 1)); - - EXPECT_TRUE(pDeviceInfo->clientInfo.loggerLogLevel == this->loggerLogLevel); - - // 500 Kbps with 2 sec duration - EXPECT_EQ(STATUS_SUCCESS, - setDeviceInfoStorageSizeBasedOnBitrateAndBufferDuration(pDeviceInfo, - 500 * 1000 * 8, - 2 * HUNDREDS_OF_NANOS_IN_A_SECOND)); - // 500 Kbps with 10 sec duration - EXPECT_EQ(STATUS_SUCCESS, - setDeviceInfoStorageSizeBasedOnBitrateAndBufferDuration(pDeviceInfo, - 500 * 1000 * 8, - 10 * HUNDREDS_OF_NANOS_IN_A_SECOND)); - // 1000 Kbps with 10 sec duration - EXPECT_EQ(STATUS_SUCCESS, - setDeviceInfoStorageSizeBasedOnBitrateAndBufferDuration(pDeviceInfo, - 1000 * 1000 * 8, - 10 * HUNDREDS_OF_NANOS_IN_A_SECOND)); - - EXPECT_EQ(STATUS_INVALID_ARG, - setDeviceInfoStorageSizeBasedOnBitrateAndBufferDuration(pDeviceInfo, - 0, - 0 * HUNDREDS_OF_NANOS_IN_A_SECOND)); - - EXPECT_EQ(STATUS_NULL_ARG, - setDeviceInfoStorageSizeBasedOnBitrateAndBufferDuration(NULL, - 0, - 0 * HUNDREDS_OF_NANOS_IN_A_SECOND)); - - EXPECT_EQ(STATUS_NULL_ARG, createDefaultDeviceInfo(NULL)); - - EXPECT_EQ(STATUS_NULL_ARG, setDeviceInfoStorageSize(NULL, MAX_STORAGE_ALLOCATION_SIZE)); - - EXPECT_EQ(STATUS_SUCCESS, freeDeviceInfo(&pDeviceInfo)); +TEST_F(InfoProviderApiTest, CreateDefaultDeviceInfo_Returns_Success) +{ + PDeviceInfo pDeviceInfo; + EXPECT_EQ(STATUS_SUCCESS, createDefaultDeviceInfo(&pDeviceInfo)); + EXPECT_EQ(STATUS_SUCCESS, setDeviceInfoStorageSize(pDeviceInfo, MAX_STORAGE_ALLOCATION_SIZE)); + EXPECT_EQ(STATUS_INVALID_STORAGE_SIZE, setDeviceInfoStorageSize(pDeviceInfo, MAX_STORAGE_ALLOCATION_SIZE + 1)); + EXPECT_TRUE(pDeviceInfo->clientInfo.loggerLogLevel == this->loggerLogLevel); + // 500 Kbps with 2 sec duration + EXPECT_EQ(STATUS_SUCCESS,setDeviceInfoStorageSizeBasedOnBitrateAndBufferDuration(pDeviceInfo, 500 * 1000 * 8, 2 * HUNDREDS_OF_NANOS_IN_A_SECOND)); + // 500 Kbps with 10 sec duration + EXPECT_EQ(STATUS_SUCCESS, setDeviceInfoStorageSizeBasedOnBitrateAndBufferDuration(pDeviceInfo, 500 * 1000 * 8, 10 * HUNDREDS_OF_NANOS_IN_A_SECOND)); + // 1000 Kbps with 10 sec duration + EXPECT_EQ(STATUS_SUCCESS, setDeviceInfoStorageSizeBasedOnBitrateAndBufferDuration(pDeviceInfo, 1000 * 1000 * 8, 10 * HUNDREDS_OF_NANOS_IN_A_SECOND)); + EXPECT_EQ(STATUS_INVALID_ARG, setDeviceInfoStorageSizeBasedOnBitrateAndBufferDuration(pDeviceInfo, 0, 0 * HUNDREDS_OF_NANOS_IN_A_SECOND)); + EXPECT_EQ(STATUS_NULL_ARG, setDeviceInfoStorageSizeBasedOnBitrateAndBufferDuration(NULL, 0, 0 * HUNDREDS_OF_NANOS_IN_A_SECOND)); + EXPECT_EQ(STATUS_NULL_ARG, createDefaultDeviceInfo(NULL)); + EXPECT_EQ(STATUS_NULL_ARG, setDeviceInfoStorageSize(NULL, MAX_STORAGE_ALLOCATION_SIZE)); + EXPECT_EQ(STATUS_SUCCESS, freeDeviceInfo(&pDeviceInfo)); + EXPECT_EQ(NULL, pDeviceInfo); + EXPECT_EQ(STATUS_SUCCESS, freeDeviceInfo(&pDeviceInfo)); + EXPECT_EQ(STATUS_NULL_ARG, freeDeviceInfo(NULL)); +} - EXPECT_EQ(NULL, pDeviceInfo); +TEST_F(InfoProviderApiTest, CreateOfflineVideoStreamInfoProvider_Returns_ValidVideoStreamInfo) +{ + PStreamInfo pStreamInfo; + EXPECT_EQ(STATUS_SUCCESS, createOfflineVideoStreamInfoProvider(TEST_STREAM_NAME, TEST_RETENTION_PERIOD, TEST_STREAM_BUFFER_DURATION, &pStreamInfo)); - EXPECT_EQ(STATUS_SUCCESS, freeDeviceInfo(&pDeviceInfo)); + EXPECT_EQ(FRAME_ORDER_MODE_PASS_THROUGH, pStreamInfo->streamCaps.frameOrderingMode); + EXPECT_EQ(MKV_TRACK_INFO_TYPE_VIDEO, pStreamInfo->streamCaps.trackInfoList->trackType); + EXPECT_EQ(STREAMING_TYPE_OFFLINE, pStreamInfo->streamCaps.streamingType); + EXPECT_NE(0, pStreamInfo->retention); + EXPECT_EQ(1, pStreamInfo->streamCaps.trackInfoCount); + EXPECT_EQ(TRUE, pStreamInfo->streamCaps.absoluteFragmentTimes); + EXPECT_EQ(STATUS_SUCCESS, freeStreamInfoProvider(&pStreamInfo)); + EXPECT_EQ(STATUS_NULL_ARG, createOfflineVideoStreamInfoProvider(NULL, TEST_RETENTION_PERIOD, TEST_STREAM_BUFFER_DURATION, &pStreamInfo)); + EXPECT_EQ(STATUS_SUCCESS, createOfflineVideoStreamInfoProvider(EMPTY_STRING, TEST_RETENTION_PERIOD, TEST_STREAM_BUFFER_DURATION, &pStreamInfo)); + EXPECT_EQ(STATUS_SUCCESS, freeStreamInfoProvider(&pStreamInfo)); - EXPECT_EQ(STATUS_NULL_ARG, freeDeviceInfo(NULL)); + EXPECT_EQ(STATUS_INVALID_ARG, createOfflineVideoStreamInfoProvider(TEST_STREAM_NAME, 0, TEST_STREAM_BUFFER_DURATION, &pStreamInfo)); - } + EXPECT_EQ(STATUS_INVALID_ARG, createOfflineVideoStreamInfoProvider(TEST_STREAM_NAME, TEST_RETENTION_PERIOD, 0, &pStreamInfo)); + EXPECT_EQ(STATUS_NULL_ARG, createOfflineVideoStreamInfoProvider(TEST_STREAM_NAME, TEST_RETENTION_PERIOD, TEST_STREAM_BUFFER_DURATION, NULL)); - TEST_F(InfoProviderApiTest, CreateOfflineVideoStreamInfoProvider_Returns_ValidVideoStreamInfo) - { - PStreamInfo pStreamInfo; + EXPECT_EQ(STATUS_SUCCESS, freeStreamInfoProvider(&pStreamInfo)); - EXPECT_EQ(STATUS_SUCCESS, - createOfflineVideoStreamInfoProvider(TEST_STREAM_NAME, - TEST_RETENTION_PERIOD, - TEST_STREAM_BUFFER_DURATION, - &pStreamInfo)); + EXPECT_EQ(NULL, pStreamInfo); + EXPECT_EQ(STATUS_SUCCESS, freeStreamInfoProvider(&pStreamInfo)); - EXPECT_EQ(FRAME_ORDER_MODE_PASS_THROUGH, pStreamInfo->streamCaps.frameOrderingMode); - EXPECT_EQ(MKV_TRACK_INFO_TYPE_VIDEO, pStreamInfo->streamCaps.trackInfoList->trackType); - EXPECT_EQ(STREAMING_TYPE_OFFLINE, pStreamInfo->streamCaps.streamingType); - EXPECT_NE(0, pStreamInfo->retention); - EXPECT_EQ(1, pStreamInfo->streamCaps.trackInfoCount); - EXPECT_EQ(TRUE, pStreamInfo->streamCaps.absoluteFragmentTimes); + EXPECT_EQ(STATUS_NULL_ARG, freeStreamInfoProvider(NULL)); +} - EXPECT_EQ(STATUS_SUCCESS, freeStreamInfoProvider(&pStreamInfo)); +TEST_F(InfoProviderApiTest, CreateRealTimeVideoStreamInfoProvider_Returns_ValidVideoStreamInfo) +{ + PStreamInfo pStreamInfo; - EXPECT_EQ(STATUS_NULL_ARG, - createOfflineVideoStreamInfoProvider(NULL, - TEST_RETENTION_PERIOD, - TEST_STREAM_BUFFER_DURATION, - &pStreamInfo)); + EXPECT_EQ(STATUS_SUCCESS, createRealtimeVideoStreamInfoProvider(TEST_STREAM_NAME, TEST_RETENTION_PERIOD, TEST_STREAM_BUFFER_DURATION, &pStreamInfo)); - EXPECT_EQ(STATUS_SUCCESS, - createOfflineVideoStreamInfoProvider(EMPTY_STRING, - TEST_RETENTION_PERIOD, - TEST_STREAM_BUFFER_DURATION, - &pStreamInfo)); + EXPECT_EQ(FRAME_ORDER_MODE_PASS_THROUGH, pStreamInfo->streamCaps.frameOrderingMode); + EXPECT_EQ(1, pStreamInfo->streamCaps.trackInfoCount); + EXPECT_EQ(STREAMING_TYPE_REALTIME, pStreamInfo->streamCaps.streamingType); + EXPECT_EQ(MKV_TRACK_INFO_TYPE_VIDEO, pStreamInfo->streamCaps.trackInfoList[0].trackType); - EXPECT_EQ(STATUS_SUCCESS, freeStreamInfoProvider(&pStreamInfo)); + EXPECT_EQ(STATUS_SUCCESS, freeStreamInfoProvider(&pStreamInfo)); + EXPECT_EQ(STATUS_NULL_ARG, createRealtimeVideoStreamInfoProvider(NULL, TEST_RETENTION_PERIOD,TEST_STREAM_BUFFER_DURATION, &pStreamInfo)); - EXPECT_EQ(STATUS_INVALID_ARG, - createOfflineVideoStreamInfoProvider(TEST_STREAM_NAME, - 0, - TEST_STREAM_BUFFER_DURATION, - &pStreamInfo)); + EXPECT_EQ(STATUS_SUCCESS, createRealtimeVideoStreamInfoProvider(EMPTY_STRING, TEST_RETENTION_PERIOD, TEST_STREAM_BUFFER_DURATION, &pStreamInfo)); - EXPECT_EQ(STATUS_INVALID_ARG, - createOfflineVideoStreamInfoProvider(TEST_STREAM_NAME, - TEST_RETENTION_PERIOD, - 0, - &pStreamInfo)); + EXPECT_EQ(STATUS_SUCCESS, freeStreamInfoProvider(&pStreamInfo)); + EXPECT_EQ(STATUS_SUCCESS, createRealtimeVideoStreamInfoProvider(TEST_STREAM_NAME, 0, TEST_STREAM_BUFFER_DURATION, &pStreamInfo)); - EXPECT_EQ(STATUS_NULL_ARG, - createOfflineVideoStreamInfoProvider(TEST_STREAM_NAME, - TEST_RETENTION_PERIOD, - TEST_STREAM_BUFFER_DURATION, - NULL)); + EXPECT_EQ(STATUS_SUCCESS, freeStreamInfoProvider(&pStreamInfo)); + EXPECT_EQ(STATUS_NULL_ARG, createRealtimeVideoStreamInfoProvider(TEST_STREAM_NAME, TEST_RETENTION_PERIOD, TEST_STREAM_BUFFER_DURATION, NULL)); - EXPECT_EQ(STATUS_SUCCESS, freeStreamInfoProvider(&pStreamInfo)); + EXPECT_EQ(STATUS_SUCCESS, freeStreamInfoProvider(&pStreamInfo)); - EXPECT_EQ(NULL, pStreamInfo); + EXPECT_EQ(NULL, pStreamInfo); - EXPECT_EQ(STATUS_SUCCESS, freeStreamInfoProvider(&pStreamInfo)); + EXPECT_EQ(STATUS_SUCCESS, freeStreamInfoProvider(&pStreamInfo)); - EXPECT_EQ(STATUS_NULL_ARG, freeStreamInfoProvider(NULL)); + EXPECT_EQ(STATUS_NULL_ARG, freeStreamInfoProvider(NULL)); - } +} - TEST_F(InfoProviderApiTest, CreateRealTimeVideoStreamInfoProvider_Returns_ValidVideoStreamInfo) - { - PStreamInfo pStreamInfo; +TEST_F(InfoProviderApiTest, CreateRealTimeAudioVideoStreamInfoProvider_Returns_ValidVideoStreamInfo) +{ + PStreamInfo pStreamInfo; - EXPECT_EQ(STATUS_SUCCESS, - createRealtimeVideoStreamInfoProvider(TEST_STREAM_NAME, - TEST_RETENTION_PERIOD, - TEST_STREAM_BUFFER_DURATION, - &pStreamInfo)); + EXPECT_EQ(STATUS_SUCCESS, createRealtimeAudioVideoStreamInfoProvider(TEST_STREAM_NAME, TEST_RETENTION_PERIOD, TEST_STREAM_BUFFER_DURATION, &pStreamInfo)); - EXPECT_EQ(FRAME_ORDER_MODE_PASS_THROUGH, pStreamInfo->streamCaps.frameOrderingMode); - EXPECT_EQ(1, pStreamInfo->streamCaps.trackInfoCount); - EXPECT_EQ(STREAMING_TYPE_REALTIME, pStreamInfo->streamCaps.streamingType); - EXPECT_EQ(MKV_TRACK_INFO_TYPE_VIDEO, pStreamInfo->streamCaps.trackInfoList[0].trackType); + EXPECT_EQ(FRAME_ORDERING_MODE_MULTI_TRACK_AV_COMPARE_PTS_ONE_MS_COMPENSATE_EOFR, pStreamInfo->streamCaps.frameOrderingMode); + EXPECT_EQ(2, pStreamInfo->streamCaps.trackInfoCount); + EXPECT_EQ(STREAMING_TYPE_REALTIME, pStreamInfo->streamCaps.streamingType); + EXPECT_EQ(MKV_TRACK_INFO_TYPE_VIDEO, pStreamInfo->streamCaps.trackInfoList[0].trackType); + EXPECT_EQ(MKV_TRACK_INFO_TYPE_AUDIO, pStreamInfo->streamCaps.trackInfoList[1].trackType); - EXPECT_EQ(STATUS_SUCCESS, freeStreamInfoProvider(&pStreamInfo)); + EXPECT_EQ(STATUS_SUCCESS, freeStreamInfoProvider(&pStreamInfo)); + EXPECT_EQ(STATUS_NULL_ARG, freeStreamInfoProvider(NULL)); + EXPECT_EQ(STATUS_NULL_ARG, createRealtimeAudioVideoStreamInfoProvider(NULL, TEST_RETENTION_PERIOD, TEST_STREAM_BUFFER_DURATION, &pStreamInfo)); - EXPECT_EQ(STATUS_NULL_ARG, - createRealtimeVideoStreamInfoProvider(NULL, - TEST_RETENTION_PERIOD, - TEST_STREAM_BUFFER_DURATION, - &pStreamInfo)); + EXPECT_EQ(STATUS_SUCCESS, createRealtimeAudioVideoStreamInfoProvider(EMPTY_STRING, TEST_RETENTION_PERIOD, TEST_STREAM_BUFFER_DURATION, &pStreamInfo)); - EXPECT_EQ(STATUS_SUCCESS, - createRealtimeVideoStreamInfoProvider(EMPTY_STRING, - TEST_RETENTION_PERIOD, - TEST_STREAM_BUFFER_DURATION, - &pStreamInfo)); + EXPECT_EQ(STATUS_SUCCESS, freeStreamInfoProvider(&pStreamInfo)); + EXPECT_EQ(STATUS_SUCCESS, createRealtimeAudioVideoStreamInfoProvider(TEST_STREAM_NAME, 0, TEST_STREAM_BUFFER_DURATION, &pStreamInfo)); - EXPECT_EQ(STATUS_SUCCESS, freeStreamInfoProvider(&pStreamInfo)); + EXPECT_EQ(STATUS_SUCCESS, freeStreamInfoProvider(&pStreamInfo)); - EXPECT_EQ(STATUS_SUCCESS, - createRealtimeVideoStreamInfoProvider(TEST_STREAM_NAME, - 0, - TEST_STREAM_BUFFER_DURATION, - &pStreamInfo)); + EXPECT_EQ(STATUS_NULL_ARG, createRealtimeAudioVideoStreamInfoProvider(TEST_STREAM_NAME, TEST_RETENTION_PERIOD, TEST_STREAM_BUFFER_DURATION, NULL)); - EXPECT_EQ(STATUS_SUCCESS, freeStreamInfoProvider(&pStreamInfo)); + EXPECT_EQ(STATUS_SUCCESS, freeStreamInfoProvider(&pStreamInfo)); + EXPECT_EQ(NULL, pStreamInfo); - EXPECT_EQ(STATUS_NULL_ARG, - createRealtimeVideoStreamInfoProvider(TEST_STREAM_NAME, - TEST_RETENTION_PERIOD, - TEST_STREAM_BUFFER_DURATION, - NULL)); + EXPECT_EQ(STATUS_SUCCESS, freeStreamInfoProvider(&pStreamInfo)); - EXPECT_EQ(STATUS_SUCCESS, freeStreamInfoProvider(&pStreamInfo)); + EXPECT_EQ(STATUS_NULL_ARG, freeStreamInfoProvider(NULL)); +} - EXPECT_EQ(NULL, pStreamInfo); +TEST_F(InfoProviderApiTest, setStreamInfoBasedOnStorageSizeApiTest) +{ + PStreamInfo pStreamInfo; - EXPECT_EQ(STATUS_SUCCESS, freeStreamInfoProvider(&pStreamInfo)); + EXPECT_EQ(STATUS_SUCCESS, createRealtimeVideoStreamInfoProvider(TEST_STREAM_NAME, TEST_RETENTION_PERIOD, TEST_STREAM_BUFFER_DURATION, &pStreamInfo)); - EXPECT_EQ(STATUS_NULL_ARG, freeStreamInfoProvider(NULL)); + EXPECT_EQ(STATUS_INVALID_ARG, setStreamInfoBasedOnStorageSize(0, 1000000, 1, pStreamInfo)); + EXPECT_EQ(STATUS_INVALID_ARG, setStreamInfoBasedOnStorageSize(2 * 1024 * 1024, 0, 1, pStreamInfo)); + EXPECT_EQ(STATUS_INVALID_ARG, setStreamInfoBasedOnStorageSize(2 * 1024 * 1024, 1000000, 0, pStreamInfo)); + EXPECT_EQ(STATUS_NULL_ARG, setStreamInfoBasedOnStorageSize(2 * 1024 * 1024, 1000000, 1, NULL)); - } + EXPECT_EQ(STATUS_SUCCESS, setStreamInfoBasedOnStorageSize(2 * 1024 * 1024, 1000000, 1, pStreamInfo)); + EXPECT_TRUE(pStreamInfo->streamCaps.bufferDuration != TEST_STREAM_BUFFER_DURATION); - TEST_F(InfoProviderApiTest, CreateRealTimeAudioVideoStreamInfoProvider_Returns_ValidVideoStreamInfo) - { - PStreamInfo pStreamInfo; + EXPECT_EQ(STATUS_SUCCESS, freeStreamInfoProvider(&pStreamInfo)); +} - EXPECT_EQ(STATUS_SUCCESS, - createRealtimeAudioVideoStreamInfoProvider(TEST_STREAM_NAME, - TEST_RETENTION_PERIOD, - TEST_STREAM_BUFFER_DURATION, - &pStreamInfo)); +TEST_F(InfoProviderApiTest, CreateOfflineAudioVideoStreamInfoProvider_Returns_ValidVideoStreamInfo) +{ + PStreamInfo pStreamInfo; - EXPECT_EQ(FRAME_ORDERING_MODE_MULTI_TRACK_AV_COMPARE_PTS_ONE_MS_COMPENSATE_EOFR, pStreamInfo->streamCaps.frameOrderingMode); - EXPECT_EQ(2, pStreamInfo->streamCaps.trackInfoCount); - EXPECT_EQ(STREAMING_TYPE_REALTIME, pStreamInfo->streamCaps.streamingType); - EXPECT_EQ(MKV_TRACK_INFO_TYPE_VIDEO, pStreamInfo->streamCaps.trackInfoList[0].trackType); - EXPECT_EQ(MKV_TRACK_INFO_TYPE_AUDIO, pStreamInfo->streamCaps.trackInfoList[1].trackType); + EXPECT_EQ(STATUS_SUCCESS, createOfflineAudioVideoStreamInfoProvider(TEST_STREAM_NAME, TEST_RETENTION_PERIOD,TEST_STREAM_BUFFER_DURATION, &pStreamInfo)); - EXPECT_EQ(STATUS_SUCCESS, freeStreamInfoProvider(&pStreamInfo)); - EXPECT_EQ(STATUS_NULL_ARG, freeStreamInfoProvider(NULL)); + EXPECT_EQ(FRAME_ORDERING_MODE_MULTI_TRACK_AV_COMPARE_PTS_ONE_MS_COMPENSATE_EOFR, pStreamInfo->streamCaps.frameOrderingMode); + EXPECT_EQ(2, pStreamInfo->streamCaps.trackInfoCount); + EXPECT_EQ(STREAMING_TYPE_OFFLINE, pStreamInfo->streamCaps.streamingType); + EXPECT_EQ(MKV_TRACK_INFO_TYPE_VIDEO, pStreamInfo->streamCaps.trackInfoList[0].trackType); + EXPECT_EQ(MKV_TRACK_INFO_TYPE_AUDIO, pStreamInfo->streamCaps.trackInfoList[1].trackType); - EXPECT_EQ(STATUS_NULL_ARG, - createRealtimeAudioVideoStreamInfoProvider(NULL, - TEST_RETENTION_PERIOD, - TEST_STREAM_BUFFER_DURATION, - &pStreamInfo)); + EXPECT_EQ(STATUS_SUCCESS, freeStreamInfoProvider(&pStreamInfo)); - EXPECT_EQ(STATUS_SUCCESS, - createRealtimeAudioVideoStreamInfoProvider(EMPTY_STRING, - TEST_RETENTION_PERIOD, - TEST_STREAM_BUFFER_DURATION, - &pStreamInfo)); + EXPECT_EQ(STATUS_NULL_ARG, createOfflineAudioVideoStreamInfoProvider(NULL, TEST_RETENTION_PERIOD, TEST_STREAM_BUFFER_DURATION, &pStreamInfo)); - EXPECT_EQ(STATUS_SUCCESS, freeStreamInfoProvider(&pStreamInfo)); + EXPECT_EQ(STATUS_SUCCESS, createOfflineAudioVideoStreamInfoProvider(EMPTY_STRING, TEST_RETENTION_PERIOD, TEST_STREAM_BUFFER_DURATION, &pStreamInfo)); + EXPECT_EQ(STATUS_SUCCESS, freeStreamInfoProvider(&pStreamInfo)); - EXPECT_EQ(STATUS_SUCCESS, - createRealtimeAudioVideoStreamInfoProvider(TEST_STREAM_NAME, - 0, - TEST_STREAM_BUFFER_DURATION, - &pStreamInfo)); + EXPECT_EQ(STATUS_INVALID_ARG, createOfflineAudioVideoStreamInfoProvider(TEST_STREAM_NAME, 0, TEST_STREAM_BUFFER_DURATION, &pStreamInfo)); - EXPECT_EQ(STATUS_SUCCESS, freeStreamInfoProvider(&pStreamInfo)); + EXPECT_EQ(STATUS_SUCCESS, freeStreamInfoProvider(&pStreamInfo)); - EXPECT_EQ(STATUS_NULL_ARG, - createRealtimeAudioVideoStreamInfoProvider(TEST_STREAM_NAME, - TEST_RETENTION_PERIOD, - TEST_STREAM_BUFFER_DURATION, - NULL)); + EXPECT_EQ(STATUS_NULL_ARG, createOfflineAudioVideoStreamInfoProvider(TEST_STREAM_NAME, TEST_RETENTION_PERIOD, TEST_STREAM_BUFFER_DURATION, NULL)); - EXPECT_EQ(STATUS_SUCCESS, freeStreamInfoProvider(&pStreamInfo)); + EXPECT_EQ(STATUS_SUCCESS, freeStreamInfoProvider(&pStreamInfo)); - EXPECT_EQ(NULL, pStreamInfo); + EXPECT_EQ(NULL, pStreamInfo); - EXPECT_EQ(STATUS_SUCCESS, freeStreamInfoProvider(&pStreamInfo)); - - EXPECT_EQ(STATUS_NULL_ARG, freeStreamInfoProvider(NULL)); + EXPECT_EQ(STATUS_SUCCESS, freeStreamInfoProvider(&pStreamInfo)); + EXPECT_EQ(STATUS_NULL_ARG, freeStreamInfoProvider(NULL)); +} +TEST_F(InfoProviderApiTest, CreateStream_Returns_Success) +{ + PStreamCallbacks pStreamCallbacks; + EXPECT_EQ(STATUS_SUCCESS, createStreamCallbacks(&pStreamCallbacks)); + EXPECT_EQ(STATUS_SUCCESS, freeStreamCallbacks(&pStreamCallbacks)); + EXPECT_EQ(NULL, pStreamCallbacks); + EXPECT_EQ(STATUS_SUCCESS, freeStreamCallbacks(&pStreamCallbacks)); + EXPECT_EQ(STATUS_NULL_ARG, createStreamCallbacks(NULL)); } - TEST_F(InfoProviderApiTest, setStreamInfoBasedOnStorageSizeApiTest) { - PStreamInfo pStreamInfo; - - EXPECT_EQ(STATUS_SUCCESS, - createRealtimeVideoStreamInfoProvider(TEST_STREAM_NAME, - TEST_RETENTION_PERIOD, - TEST_STREAM_BUFFER_DURATION, - &pStreamInfo)); - - EXPECT_EQ(STATUS_INVALID_ARG, setStreamInfoBasedOnStorageSize(0, 1000000, 1, pStreamInfo)); - EXPECT_EQ(STATUS_INVALID_ARG, setStreamInfoBasedOnStorageSize(2 * 1024 * 1024, 0, 1, pStreamInfo)); - EXPECT_EQ(STATUS_INVALID_ARG, setStreamInfoBasedOnStorageSize(2 * 1024 * 1024, 1000000, 0, pStreamInfo)); - EXPECT_EQ(STATUS_NULL_ARG, setStreamInfoBasedOnStorageSize(2 * 1024 * 1024, 1000000, 1, NULL)); - - EXPECT_EQ(STATUS_SUCCESS, setStreamInfoBasedOnStorageSize(2 * 1024 * 1024, 1000000, 1, pStreamInfo)); - EXPECT_TRUE(pStreamInfo->streamCaps.bufferDuration != TEST_STREAM_BUFFER_DURATION); - - EXPECT_EQ(STATUS_SUCCESS, freeStreamInfoProvider(&pStreamInfo)); - } - - TEST_F(InfoProviderApiTest, CreateOfflineAudioVideoStreamInfoProvider_Returns_ValidVideoStreamInfo) + TEST_F(InfoProviderApiTest, createRealtimeVideoStreamInfoProviderWithCodecsApiTest) { - PStreamInfo pStreamInfo; - - EXPECT_EQ(STATUS_SUCCESS, - createOfflineAudioVideoStreamInfoProvider(TEST_STREAM_NAME, - TEST_RETENTION_PERIOD, - TEST_STREAM_BUFFER_DURATION, - &pStreamInfo)); - - EXPECT_EQ(FRAME_ORDERING_MODE_MULTI_TRACK_AV_COMPARE_PTS_ONE_MS_COMPENSATE_EOFR, pStreamInfo->streamCaps.frameOrderingMode); - EXPECT_EQ(2, pStreamInfo->streamCaps.trackInfoCount); - EXPECT_EQ(STREAMING_TYPE_OFFLINE, pStreamInfo->streamCaps.streamingType); - EXPECT_EQ(MKV_TRACK_INFO_TYPE_VIDEO, pStreamInfo->streamCaps.trackInfoList[0].trackType); - EXPECT_EQ(MKV_TRACK_INFO_TYPE_AUDIO, pStreamInfo->streamCaps.trackInfoList[1].trackType); - - EXPECT_EQ(STATUS_SUCCESS, freeStreamInfoProvider(&pStreamInfo)); - - EXPECT_EQ(STATUS_NULL_ARG, - createOfflineAudioVideoStreamInfoProvider(NULL, - TEST_RETENTION_PERIOD, - TEST_STREAM_BUFFER_DURATION, - &pStreamInfo)); - - EXPECT_EQ(STATUS_SUCCESS, - createOfflineAudioVideoStreamInfoProvider(EMPTY_STRING, - TEST_RETENTION_PERIOD, - TEST_STREAM_BUFFER_DURATION, - &pStreamInfo)); - - EXPECT_EQ(STATUS_SUCCESS, freeStreamInfoProvider(&pStreamInfo)); - - EXPECT_EQ(STATUS_INVALID_ARG, - createOfflineAudioVideoStreamInfoProvider(TEST_STREAM_NAME, - 0, - TEST_STREAM_BUFFER_DURATION, - &pStreamInfo)); - - EXPECT_EQ(STATUS_SUCCESS, freeStreamInfoProvider(&pStreamInfo)); - - EXPECT_EQ(STATUS_NULL_ARG, - createOfflineAudioVideoStreamInfoProvider(TEST_STREAM_NAME, - TEST_RETENTION_PERIOD, - TEST_STREAM_BUFFER_DURATION, - NULL)); - - EXPECT_EQ(STATUS_SUCCESS, freeStreamInfoProvider(&pStreamInfo)); - - EXPECT_EQ(NULL, pStreamInfo); - - EXPECT_EQ(STATUS_SUCCESS, freeStreamInfoProvider(&pStreamInfo)); - - EXPECT_EQ(STATUS_NULL_ARG, freeStreamInfoProvider(NULL)); + PStreamInfo pStreamInfo; + EXPECT_EQ(STATUS_NULL_ARG, createRealtimeVideoStreamInfoProviderWithCodecs(TEST_STREAM_NAME, TEST_RETENTION_PERIOD,TEST_STREAM_BUFFER_DURATION, VIDEO_CODEC_ID_H264, NULL)); + EXPECT_EQ(STATUS_NULL_ARG, createRealtimeVideoStreamInfoProviderWithCodecs(NULL, TEST_RETENTION_PERIOD,TEST_STREAM_BUFFER_DURATION, VIDEO_CODEC_ID_H264, NULL)); + + // H264 + EXPECT_EQ(STATUS_SUCCESS, createRealtimeVideoStreamInfoProviderWithCodecs(TEST_STREAM_NAME, TEST_RETENTION_PERIOD,TEST_STREAM_BUFFER_DURATION, VIDEO_CODEC_ID_H264, &pStreamInfo)); + EXPECT_EQ(STREAMING_TYPE_REALTIME, pStreamInfo->streamCaps.streamingType); + EXPECT_STREQ(MKV_H264_CONTENT_TYPE, pStreamInfo->streamCaps.contentType); + EXPECT_EQ(STATUS_SUCCESS, freeStreamInfoProvider(&pStreamInfo)); + EXPECT_EQ(NULL, pStreamInfo); + + // H265 + EXPECT_EQ(STATUS_SUCCESS, createRealtimeVideoStreamInfoProviderWithCodecs(TEST_STREAM_NAME, TEST_RETENTION_PERIOD,TEST_STREAM_BUFFER_DURATION, VIDEO_CODEC_ID_H265, &pStreamInfo)); + EXPECT_EQ(STREAMING_TYPE_REALTIME, pStreamInfo->streamCaps.streamingType); + EXPECT_STREQ(MKV_H265_CONTENT_TYPE, pStreamInfo->streamCaps.contentType); + EXPECT_EQ(STATUS_SUCCESS, freeStreamInfoProvider(&pStreamInfo)); + EXPECT_EQ(NULL, pStreamInfo); + + // default + EXPECT_EQ(STATUS_SUCCESS, createRealtimeVideoStreamInfoProviderWithCodecs(TEST_STREAM_NAME, TEST_RETENTION_PERIOD,TEST_STREAM_BUFFER_DURATION, (VIDEO_CODEC_ID) 0xffff, &pStreamInfo)); + EXPECT_EQ(STREAMING_TYPE_REALTIME, pStreamInfo->streamCaps.streamingType); + EXPECT_STREQ(MKV_H264_CONTENT_TYPE, pStreamInfo->streamCaps.contentType); + EXPECT_EQ(STATUS_SUCCESS, freeStreamInfoProvider(&pStreamInfo)); + EXPECT_EQ(NULL, pStreamInfo); } - TEST_F(InfoProviderApiTest, CreateStream_Returns_Success) + TEST_F(InfoProviderApiTest, createOfflineVideoStreamInfoProviderWithCodecsApiTest) { - PStreamCallbacks pStreamCallbacks; - - EXPECT_EQ(STATUS_SUCCESS, createStreamCallbacks(&pStreamCallbacks)); + PStreamInfo pStreamInfo; + EXPECT_EQ(STATUS_NULL_ARG, createOfflineVideoStreamInfoProviderWithCodecs(TEST_STREAM_NAME, TEST_RETENTION_PERIOD,TEST_STREAM_BUFFER_DURATION, VIDEO_CODEC_ID_H264, NULL)); + EXPECT_EQ(STATUS_NULL_ARG, createOfflineVideoStreamInfoProviderWithCodecs(NULL, TEST_RETENTION_PERIOD,TEST_STREAM_BUFFER_DURATION, VIDEO_CODEC_ID_H264, NULL)); + + // H264 + EXPECT_EQ(STATUS_SUCCESS, createOfflineVideoStreamInfoProviderWithCodecs(TEST_STREAM_NAME, TEST_RETENTION_PERIOD,TEST_STREAM_BUFFER_DURATION, VIDEO_CODEC_ID_H264, &pStreamInfo)); + EXPECT_EQ(STREAMING_TYPE_OFFLINE, pStreamInfo->streamCaps.streamingType); + EXPECT_STREQ(MKV_H264_CONTENT_TYPE, pStreamInfo->streamCaps.contentType); + EXPECT_EQ(STATUS_SUCCESS, freeStreamInfoProvider(&pStreamInfo)); + EXPECT_EQ(NULL, pStreamInfo); + + // H265 + EXPECT_EQ(STATUS_SUCCESS, createOfflineVideoStreamInfoProviderWithCodecs(TEST_STREAM_NAME, TEST_RETENTION_PERIOD,TEST_STREAM_BUFFER_DURATION, VIDEO_CODEC_ID_H265, &pStreamInfo)); + EXPECT_EQ(STREAMING_TYPE_OFFLINE, pStreamInfo->streamCaps.streamingType); + EXPECT_STREQ(MKV_H265_CONTENT_TYPE, pStreamInfo->streamCaps.contentType); + EXPECT_EQ(STATUS_SUCCESS, freeStreamInfoProvider(&pStreamInfo)); + EXPECT_EQ(NULL, pStreamInfo); + + // default + EXPECT_EQ(STATUS_SUCCESS, createOfflineVideoStreamInfoProviderWithCodecs(TEST_STREAM_NAME, TEST_RETENTION_PERIOD,TEST_STREAM_BUFFER_DURATION, (VIDEO_CODEC_ID) 0xffff, &pStreamInfo)); + EXPECT_EQ(STREAMING_TYPE_OFFLINE, pStreamInfo->streamCaps.streamingType); + EXPECT_STREQ(MKV_H264_CONTENT_TYPE, pStreamInfo->streamCaps.contentType); + EXPECT_EQ(STATUS_SUCCESS, freeStreamInfoProvider(&pStreamInfo)); + EXPECT_EQ(NULL, pStreamInfo); +} + +TEST_F(InfoProviderApiTest, createRealtimeAudioVideoStreamInfoProviderWithCodecsApiTest) +{ + PStreamInfo pStreamInfo; + CHAR expectedContentType[MAX_CONTENT_TYPE_LEN + 1] = {"\0"}; + EXPECT_EQ(STATUS_NULL_ARG, createRealtimeAudioVideoStreamInfoProviderWithCodecs(TEST_STREAM_NAME, TEST_RETENTION_PERIOD,TEST_STREAM_BUFFER_DURATION, VIDEO_CODEC_ID_H264, AUDIO_CODEC_ID_AAC, NULL)); + EXPECT_EQ(STATUS_NULL_ARG, createRealtimeAudioVideoStreamInfoProviderWithCodecs(NULL, TEST_RETENTION_PERIOD,TEST_STREAM_BUFFER_DURATION, VIDEO_CODEC_ID_H264, AUDIO_CODEC_ID_AAC, NULL)); + + // H264, AAC + EXPECT_EQ(STATUS_SUCCESS, createRealtimeAudioVideoStreamInfoProviderWithCodecs(TEST_STREAM_NAME, TEST_RETENTION_PERIOD,TEST_STREAM_BUFFER_DURATION, VIDEO_CODEC_ID_H264, AUDIO_CODEC_ID_AAC, &pStreamInfo)); + EXPECT_EQ(STREAMING_TYPE_REALTIME, pStreamInfo->streamCaps.streamingType); + SNPRINTF(expectedContentType, SIZEOF(expectedContentType), "%s,%s", MKV_H264_CONTENT_TYPE, MKV_AAC_CONTENT_TYPE); + EXPECT_STREQ(expectedContentType, pStreamInfo->streamCaps.contentType); + EXPECT_EQ(STATUS_SUCCESS, freeStreamInfoProvider(&pStreamInfo)); + EXPECT_EQ(NULL, pStreamInfo); + MEMSET(expectedContentType, '\0', SIZEOF(expectedContentType)); + + // H264, PCM ALAW + EXPECT_EQ(STATUS_SUCCESS, createRealtimeAudioVideoStreamInfoProviderWithCodecs(TEST_STREAM_NAME, TEST_RETENTION_PERIOD,TEST_STREAM_BUFFER_DURATION, VIDEO_CODEC_ID_H264, AUDIO_CODEC_ID_PCM_ALAW, &pStreamInfo)); + EXPECT_EQ(STREAMING_TYPE_REALTIME, pStreamInfo->streamCaps.streamingType); + SNPRINTF(expectedContentType, SIZEOF(expectedContentType), "%s,%s", MKV_H264_CONTENT_TYPE, MKV_ALAW_CONTENT_TYPE); + EXPECT_STREQ(expectedContentType, pStreamInfo->streamCaps.contentType); + EXPECT_EQ(STATUS_SUCCESS, freeStreamInfoProvider(&pStreamInfo)); + EXPECT_EQ(NULL, pStreamInfo); + MEMSET(expectedContentType, '\0', SIZEOF(expectedContentType)); + + // H264, MULAW + EXPECT_EQ(STATUS_SUCCESS, createRealtimeAudioVideoStreamInfoProviderWithCodecs(TEST_STREAM_NAME, TEST_RETENTION_PERIOD,TEST_STREAM_BUFFER_DURATION, VIDEO_CODEC_ID_H264, AUDIO_CODEC_ID_PCM_MULAW, &pStreamInfo)); + EXPECT_EQ(STREAMING_TYPE_REALTIME, pStreamInfo->streamCaps.streamingType); + SNPRINTF(expectedContentType, SIZEOF(expectedContentType), "%s,%s", MKV_H264_CONTENT_TYPE, MKV_MULAW_CONTENT_TYPE); + EXPECT_STREQ(expectedContentType, pStreamInfo->streamCaps.contentType); + EXPECT_EQ(STATUS_SUCCESS, freeStreamInfoProvider(&pStreamInfo)); + EXPECT_EQ(NULL, pStreamInfo); + + + // H265, AAC + EXPECT_EQ(STATUS_SUCCESS, createRealtimeAudioVideoStreamInfoProviderWithCodecs(TEST_STREAM_NAME, TEST_RETENTION_PERIOD,TEST_STREAM_BUFFER_DURATION, VIDEO_CODEC_ID_H265, AUDIO_CODEC_ID_AAC, &pStreamInfo)); + EXPECT_EQ(STREAMING_TYPE_REALTIME, pStreamInfo->streamCaps.streamingType); + SNPRINTF(expectedContentType, SIZEOF(expectedContentType), "%s,%s", MKV_H265_CONTENT_TYPE, MKV_AAC_CONTENT_TYPE); + EXPECT_STREQ(expectedContentType, pStreamInfo->streamCaps.contentType); + EXPECT_EQ(STATUS_SUCCESS, freeStreamInfoProvider(&pStreamInfo)); + EXPECT_EQ(NULL, pStreamInfo); + MEMSET(expectedContentType, '\0', SIZEOF(expectedContentType)); + + // H265, PCM ALAW + EXPECT_EQ(STATUS_SUCCESS, createRealtimeAudioVideoStreamInfoProviderWithCodecs(TEST_STREAM_NAME, TEST_RETENTION_PERIOD,TEST_STREAM_BUFFER_DURATION, VIDEO_CODEC_ID_H265, AUDIO_CODEC_ID_PCM_ALAW, &pStreamInfo)); + EXPECT_EQ(STREAMING_TYPE_REALTIME, pStreamInfo->streamCaps.streamingType); + SNPRINTF(expectedContentType, SIZEOF(expectedContentType), "%s,%s", MKV_H265_CONTENT_TYPE, MKV_ALAW_CONTENT_TYPE); + EXPECT_STREQ(expectedContentType, pStreamInfo->streamCaps.contentType); + EXPECT_EQ(STATUS_SUCCESS, freeStreamInfoProvider(&pStreamInfo)); + EXPECT_EQ(NULL, pStreamInfo); + MEMSET(expectedContentType, '\0', SIZEOF(expectedContentType)); + + // H265, MULAW + EXPECT_EQ(STATUS_SUCCESS, createRealtimeAudioVideoStreamInfoProviderWithCodecs(TEST_STREAM_NAME, TEST_RETENTION_PERIOD,TEST_STREAM_BUFFER_DURATION, VIDEO_CODEC_ID_H265, AUDIO_CODEC_ID_PCM_MULAW, &pStreamInfo)); + EXPECT_EQ(STREAMING_TYPE_REALTIME, pStreamInfo->streamCaps.streamingType); + SNPRINTF(expectedContentType, SIZEOF(expectedContentType), "%s,%s", MKV_H265_CONTENT_TYPE, MKV_MULAW_CONTENT_TYPE); + EXPECT_STREQ(expectedContentType, pStreamInfo->streamCaps.contentType); + EXPECT_EQ(STATUS_SUCCESS, freeStreamInfoProvider(&pStreamInfo)); + EXPECT_EQ(NULL, pStreamInfo); + + // Default + EXPECT_EQ(STATUS_SUCCESS, createRealtimeAudioVideoStreamInfoProviderWithCodecs(TEST_STREAM_NAME, TEST_RETENTION_PERIOD,TEST_STREAM_BUFFER_DURATION, (VIDEO_CODEC_ID) 0xffff, (AUDIO_CODEC_ID) 0xffff, &pStreamInfo)); + EXPECT_EQ(STREAMING_TYPE_REALTIME, pStreamInfo->streamCaps.streamingType); + SNPRINTF(expectedContentType, SIZEOF(expectedContentType), "%s,%s", MKV_H264_CONTENT_TYPE, MKV_AAC_CONTENT_TYPE); + EXPECT_STREQ(expectedContentType, pStreamInfo->streamCaps.contentType); + EXPECT_EQ(STATUS_SUCCESS, freeStreamInfoProvider(&pStreamInfo)); + EXPECT_EQ(NULL, pStreamInfo); + +} + +TEST_F(InfoProviderApiTest, createOfflineAudioVideoStreamInfoProviderWithCodecsApiTest) +{ + PStreamInfo pStreamInfo; + CHAR expectedContentType[MAX_CONTENT_TYPE_LEN + 1] = {"\0"}; + EXPECT_EQ(STATUS_NULL_ARG, createOfflineAudioVideoStreamInfoProviderWithCodecs(TEST_STREAM_NAME, TEST_RETENTION_PERIOD,TEST_STREAM_BUFFER_DURATION, VIDEO_CODEC_ID_H264, AUDIO_CODEC_ID_AAC, NULL)); + EXPECT_EQ(STATUS_NULL_ARG, createOfflineAudioVideoStreamInfoProviderWithCodecs(NULL, TEST_RETENTION_PERIOD,TEST_STREAM_BUFFER_DURATION, VIDEO_CODEC_ID_H264, AUDIO_CODEC_ID_AAC, NULL)); + + // H264, AAC + EXPECT_EQ(STATUS_SUCCESS, createOfflineAudioVideoStreamInfoProviderWithCodecs(TEST_STREAM_NAME, TEST_RETENTION_PERIOD,TEST_STREAM_BUFFER_DURATION, VIDEO_CODEC_ID_H264, AUDIO_CODEC_ID_AAC, &pStreamInfo)); + EXPECT_EQ(STREAMING_TYPE_OFFLINE, pStreamInfo->streamCaps.streamingType); + SNPRINTF(expectedContentType, SIZEOF(expectedContentType), "%s,%s", MKV_H264_CONTENT_TYPE, MKV_AAC_CONTENT_TYPE); + EXPECT_STREQ(expectedContentType, pStreamInfo->streamCaps.contentType); + EXPECT_EQ(STATUS_SUCCESS, freeStreamInfoProvider(&pStreamInfo)); + EXPECT_EQ(NULL, pStreamInfo); + MEMSET(expectedContentType, '\0', SIZEOF(expectedContentType)); + + // H264, PCM ALAW + EXPECT_EQ(STATUS_SUCCESS, createOfflineAudioVideoStreamInfoProviderWithCodecs(TEST_STREAM_NAME, TEST_RETENTION_PERIOD,TEST_STREAM_BUFFER_DURATION, VIDEO_CODEC_ID_H264, AUDIO_CODEC_ID_PCM_ALAW, &pStreamInfo)); + EXPECT_EQ(STREAMING_TYPE_OFFLINE, pStreamInfo->streamCaps.streamingType); + SNPRINTF(expectedContentType, SIZEOF(expectedContentType), "%s,%s", MKV_H264_CONTENT_TYPE, MKV_ALAW_CONTENT_TYPE); + EXPECT_STREQ(expectedContentType, pStreamInfo->streamCaps.contentType); + EXPECT_EQ(STATUS_SUCCESS, freeStreamInfoProvider(&pStreamInfo)); + EXPECT_EQ(NULL, pStreamInfo); + MEMSET(expectedContentType, '\0', SIZEOF(expectedContentType)); + + // H264, MULAW + EXPECT_EQ(STATUS_SUCCESS, createOfflineAudioVideoStreamInfoProviderWithCodecs(TEST_STREAM_NAME, TEST_RETENTION_PERIOD,TEST_STREAM_BUFFER_DURATION, VIDEO_CODEC_ID_H264, AUDIO_CODEC_ID_PCM_MULAW, &pStreamInfo)); + EXPECT_EQ(STREAMING_TYPE_OFFLINE, pStreamInfo->streamCaps.streamingType); + SNPRINTF(expectedContentType, SIZEOF(expectedContentType), "%s,%s", MKV_H264_CONTENT_TYPE, MKV_MULAW_CONTENT_TYPE); + EXPECT_STREQ(expectedContentType, pStreamInfo->streamCaps.contentType); + EXPECT_EQ(STATUS_SUCCESS, freeStreamInfoProvider(&pStreamInfo)); + EXPECT_EQ(NULL, pStreamInfo); + + + // H265, AAC + EXPECT_EQ(STATUS_SUCCESS, createOfflineAudioVideoStreamInfoProviderWithCodecs(TEST_STREAM_NAME, TEST_RETENTION_PERIOD,TEST_STREAM_BUFFER_DURATION, VIDEO_CODEC_ID_H265, AUDIO_CODEC_ID_AAC, &pStreamInfo)); + EXPECT_EQ(STREAMING_TYPE_OFFLINE, pStreamInfo->streamCaps.streamingType); + SNPRINTF(expectedContentType, SIZEOF(expectedContentType), "%s,%s", MKV_H265_CONTENT_TYPE, MKV_AAC_CONTENT_TYPE); + EXPECT_STREQ(expectedContentType, pStreamInfo->streamCaps.contentType); + EXPECT_EQ(STATUS_SUCCESS, freeStreamInfoProvider(&pStreamInfo)); + EXPECT_EQ(NULL, pStreamInfo); + MEMSET(expectedContentType, '\0', SIZEOF(expectedContentType)); + + // H265, PCM ALAW + EXPECT_EQ(STATUS_SUCCESS, createOfflineAudioVideoStreamInfoProviderWithCodecs(TEST_STREAM_NAME, TEST_RETENTION_PERIOD,TEST_STREAM_BUFFER_DURATION, VIDEO_CODEC_ID_H265, AUDIO_CODEC_ID_PCM_ALAW, &pStreamInfo)); + EXPECT_EQ(STREAMING_TYPE_OFFLINE, pStreamInfo->streamCaps.streamingType); + SNPRINTF(expectedContentType, SIZEOF(expectedContentType), "%s,%s", MKV_H265_CONTENT_TYPE, MKV_ALAW_CONTENT_TYPE); + EXPECT_STREQ(expectedContentType, pStreamInfo->streamCaps.contentType); + EXPECT_EQ(STATUS_SUCCESS, freeStreamInfoProvider(&pStreamInfo)); + EXPECT_EQ(NULL, pStreamInfo); + MEMSET(expectedContentType, '\0', SIZEOF(expectedContentType)); + + // H265, MULAW + EXPECT_EQ(STATUS_SUCCESS, createOfflineAudioVideoStreamInfoProviderWithCodecs(TEST_STREAM_NAME, TEST_RETENTION_PERIOD,TEST_STREAM_BUFFER_DURATION, VIDEO_CODEC_ID_H265, AUDIO_CODEC_ID_PCM_MULAW, &pStreamInfo)); + EXPECT_EQ(STREAMING_TYPE_OFFLINE, pStreamInfo->streamCaps.streamingType); + SNPRINTF(expectedContentType, SIZEOF(expectedContentType), "%s,%s", MKV_H265_CONTENT_TYPE, MKV_MULAW_CONTENT_TYPE); + EXPECT_STREQ(expectedContentType, pStreamInfo->streamCaps.contentType); + EXPECT_EQ(STATUS_SUCCESS, freeStreamInfoProvider(&pStreamInfo)); + EXPECT_EQ(NULL, pStreamInfo); + + // Default + EXPECT_EQ(STATUS_SUCCESS, createOfflineAudioVideoStreamInfoProviderWithCodecs(TEST_STREAM_NAME, TEST_RETENTION_PERIOD,TEST_STREAM_BUFFER_DURATION, (VIDEO_CODEC_ID) 0xffff, (AUDIO_CODEC_ID) 0xffff, &pStreamInfo)); + EXPECT_EQ(STREAMING_TYPE_OFFLINE, pStreamInfo->streamCaps.streamingType); + SNPRINTF(expectedContentType, SIZEOF(expectedContentType), "%s,%s", MKV_H264_CONTENT_TYPE, MKV_AAC_CONTENT_TYPE); + EXPECT_STREQ(expectedContentType, pStreamInfo->streamCaps.contentType); + EXPECT_EQ(STATUS_SUCCESS, freeStreamInfoProvider(&pStreamInfo)); + EXPECT_EQ(NULL, pStreamInfo); +} + +TEST_F(InfoProviderApiTest, createRealtimeAudioStreamInfoProviderWithCodecsApiTest) +{ + PStreamInfo pStreamInfo; + EXPECT_EQ(STATUS_NULL_ARG, createRealtimeAudioStreamInfoProviderWithCodecs(TEST_STREAM_NAME, TEST_RETENTION_PERIOD,TEST_STREAM_BUFFER_DURATION, AUDIO_CODEC_ID_PCM_ALAW, NULL)); + EXPECT_EQ(STATUS_NULL_ARG, createRealtimeAudioStreamInfoProviderWithCodecs(NULL, TEST_RETENTION_PERIOD,TEST_STREAM_BUFFER_DURATION, AUDIO_CODEC_ID_PCM_ALAW, NULL)); + + // AAC + EXPECT_EQ(STATUS_SUCCESS, createRealtimeAudioStreamInfoProviderWithCodecs(TEST_STREAM_NAME, TEST_RETENTION_PERIOD,TEST_STREAM_BUFFER_DURATION, AUDIO_CODEC_ID_AAC, &pStreamInfo)); + EXPECT_EQ(STREAMING_TYPE_REALTIME, pStreamInfo->streamCaps.streamingType); + EXPECT_STREQ(MKV_AAC_CONTENT_TYPE, pStreamInfo->streamCaps.contentType); + EXPECT_EQ(STATUS_SUCCESS, freeStreamInfoProvider(&pStreamInfo)); + EXPECT_EQ(NULL, pStreamInfo); + + // PCM A LAW + EXPECT_EQ(STATUS_SUCCESS, createRealtimeAudioStreamInfoProviderWithCodecs(TEST_STREAM_NAME, TEST_RETENTION_PERIOD,TEST_STREAM_BUFFER_DURATION, AUDIO_CODEC_ID_PCM_ALAW, &pStreamInfo)); + EXPECT_EQ(STREAMING_TYPE_REALTIME, pStreamInfo->streamCaps.streamingType); + EXPECT_STREQ(MKV_ALAW_CONTENT_TYPE, pStreamInfo->streamCaps.contentType); + EXPECT_EQ(STATUS_SUCCESS, freeStreamInfoProvider(&pStreamInfo)); + EXPECT_EQ(NULL, pStreamInfo); + + // PCM MULAW + EXPECT_EQ(STATUS_SUCCESS, createRealtimeAudioStreamInfoProviderWithCodecs(TEST_STREAM_NAME, TEST_RETENTION_PERIOD,TEST_STREAM_BUFFER_DURATION, AUDIO_CODEC_ID_PCM_MULAW, &pStreamInfo)); + EXPECT_EQ(STREAMING_TYPE_REALTIME, pStreamInfo->streamCaps.streamingType); + EXPECT_STREQ(MKV_MULAW_CONTENT_TYPE, pStreamInfo->streamCaps.contentType); + EXPECT_EQ(STATUS_SUCCESS, freeStreamInfoProvider(&pStreamInfo)); + EXPECT_EQ(NULL, pStreamInfo); + + // default + EXPECT_EQ(STATUS_SUCCESS, createRealtimeAudioStreamInfoProviderWithCodecs(TEST_STREAM_NAME, TEST_RETENTION_PERIOD,TEST_STREAM_BUFFER_DURATION, (AUDIO_CODEC_ID) 0xffff, &pStreamInfo)); + EXPECT_EQ(STREAMING_TYPE_REALTIME, pStreamInfo->streamCaps.streamingType); + EXPECT_STREQ(MKV_AAC_CONTENT_TYPE, pStreamInfo->streamCaps.contentType); + EXPECT_EQ(STATUS_SUCCESS, freeStreamInfoProvider(&pStreamInfo)); + EXPECT_EQ(NULL, pStreamInfo); +} + +TEST_F(InfoProviderApiTest, createOfflineAudioStreamInfoProviderWithCodecsApiTest) +{ + PStreamInfo pStreamInfo; + EXPECT_EQ(STATUS_NULL_ARG, createOfflineAudioStreamInfoProviderWithCodecs(TEST_STREAM_NAME, TEST_RETENTION_PERIOD,TEST_STREAM_BUFFER_DURATION, AUDIO_CODEC_ID_PCM_ALAW, NULL)); + EXPECT_EQ(STATUS_NULL_ARG, createOfflineAudioStreamInfoProviderWithCodecs(NULL, TEST_RETENTION_PERIOD,TEST_STREAM_BUFFER_DURATION, AUDIO_CODEC_ID_PCM_ALAW, NULL)); + + // AAC + EXPECT_EQ(STATUS_SUCCESS, createOfflineAudioStreamInfoProviderWithCodecs(TEST_STREAM_NAME, TEST_RETENTION_PERIOD,TEST_STREAM_BUFFER_DURATION, AUDIO_CODEC_ID_AAC, &pStreamInfo)); + EXPECT_EQ(STREAMING_TYPE_OFFLINE, pStreamInfo->streamCaps.streamingType); + EXPECT_STREQ(MKV_AAC_CONTENT_TYPE, pStreamInfo->streamCaps.contentType); + EXPECT_EQ(STATUS_SUCCESS, freeStreamInfoProvider(&pStreamInfo)); + EXPECT_EQ(NULL, pStreamInfo); + + // PCM A LAW + EXPECT_EQ(STATUS_SUCCESS, createOfflineAudioStreamInfoProviderWithCodecs(TEST_STREAM_NAME, TEST_RETENTION_PERIOD,TEST_STREAM_BUFFER_DURATION, AUDIO_CODEC_ID_PCM_ALAW, &pStreamInfo)); + EXPECT_EQ(STREAMING_TYPE_OFFLINE, pStreamInfo->streamCaps.streamingType); + EXPECT_STREQ(MKV_ALAW_CONTENT_TYPE, pStreamInfo->streamCaps.contentType); + EXPECT_EQ(STATUS_SUCCESS, freeStreamInfoProvider(&pStreamInfo)); + EXPECT_EQ(NULL, pStreamInfo); + + // PCM MULAW + EXPECT_EQ(STATUS_SUCCESS, createOfflineAudioStreamInfoProviderWithCodecs(TEST_STREAM_NAME, TEST_RETENTION_PERIOD,TEST_STREAM_BUFFER_DURATION, AUDIO_CODEC_ID_PCM_MULAW, &pStreamInfo)); + EXPECT_EQ(STREAMING_TYPE_OFFLINE, pStreamInfo->streamCaps.streamingType); + EXPECT_STREQ(MKV_MULAW_CONTENT_TYPE, pStreamInfo->streamCaps.contentType); + EXPECT_EQ(STATUS_SUCCESS, freeStreamInfoProvider(&pStreamInfo)); + EXPECT_EQ(NULL, pStreamInfo); + + // default + EXPECT_EQ(STATUS_SUCCESS, createOfflineAudioStreamInfoProviderWithCodecs(TEST_STREAM_NAME, TEST_RETENTION_PERIOD,TEST_STREAM_BUFFER_DURATION, (AUDIO_CODEC_ID) 0xffff, &pStreamInfo)); + EXPECT_EQ(STREAMING_TYPE_OFFLINE, pStreamInfo->streamCaps.streamingType); + EXPECT_STREQ(MKV_AAC_CONTENT_TYPE, pStreamInfo->streamCaps.contentType); + EXPECT_EQ(STATUS_SUCCESS, freeStreamInfoProvider(&pStreamInfo)); + EXPECT_EQ(NULL, pStreamInfo); +} - EXPECT_EQ(STATUS_SUCCESS, freeStreamCallbacks(&pStreamCallbacks)); - - EXPECT_EQ(NULL, pStreamCallbacks); - - EXPECT_EQ(STATUS_SUCCESS, freeStreamCallbacks(&pStreamCallbacks)); - - EXPECT_EQ(STATUS_NULL_ARG, createStreamCallbacks(NULL)); - } } // namespace video } // namespace kinesis } // namespace amazonaws } // namespace com; - diff --git a/tst/IoTCredentialTest.cpp b/tst/IoTCredentialTest.cpp new file mode 100644 index 000000000..dc52608f0 --- /dev/null +++ b/tst/IoTCredentialTest.cpp @@ -0,0 +1,311 @@ +#include "ProducerTestFixture.h" +#include "src/source/Common/IotCredentialProvider.h" + +namespace com { namespace amazonaws { namespace kinesis { namespace video { + +class IoTCredentialTest : public ProducerClientTestBase { +}; + +TEST_F(IoTCredentialTest, createDefaultCallbacksProviderWithIotCertificateValidEndpoint) +{ + PClientCallbacks pClientCallbacks; + PCHAR iotCoreCredentialEndPoint = NULL; + PCHAR iotCoreCert = NULL; + PCHAR iotCorePrivateKey = NULL; + PCHAR iotCoreRoleAlias = NULL; + PCHAR iotThingName = NULL; + EXPECT_TRUE((iotCoreCredentialEndPoint = GETENV(AWS_IOT_CORE_CREDENTIAL_ENDPOINT_ENV_VAR)) != NULL); + EXPECT_TRUE((iotCoreCert = GETENV(AWS_IOT_CORE_CERT_ENV_VAR)) != NULL); + EXPECT_TRUE((iotCorePrivateKey = GETENV(AWS_IOT_CORE_PRIVATE_KEY_ENV_VAR)) != NULL); + EXPECT_TRUE((iotCoreRoleAlias = GETENV(AWS_IOT_CORE_ROLE_ALIAS_ENV_VAR)) != NULL); + EXPECT_TRUE((iotThingName = GETENV(AWS_IOT_CORE_THING_NAME_ENV_VAR)) != NULL); + + EXPECT_EQ(STATUS_SUCCESS, createDefaultCallbacksProviderWithIotCertificate( + iotCoreCredentialEndPoint, + iotCoreCert, + iotCorePrivateKey, + NULL, + iotCoreRoleAlias, + iotThingName, + (PCHAR) DEFAULT_AWS_REGION, + NULL, + NULL, + &pClientCallbacks)); + EXPECT_EQ(STATUS_SUCCESS, freeCallbacksProvider(&pClientCallbacks)); +} + +TEST_F(IoTCredentialTest, createDefaultCallbacksProviderWithIotCertificateAndTimeoutsValidEndpoint) +{ + PClientCallbacks pClientCallbacks; + PCHAR iotCoreCredentialEndPoint = NULL; + PCHAR iotCoreCert = NULL; + PCHAR iotCorePrivateKey = NULL; + PCHAR iotCoreRoleAlias = NULL; + PCHAR iotThingName = NULL; + EXPECT_TRUE((iotCoreCredentialEndPoint = GETENV(AWS_IOT_CORE_CREDENTIAL_ENDPOINT_ENV_VAR)) != NULL); + EXPECT_TRUE((iotCoreCert = GETENV(AWS_IOT_CORE_CERT_ENV_VAR)) != NULL); + EXPECT_TRUE((iotCorePrivateKey = GETENV(AWS_IOT_CORE_PRIVATE_KEY_ENV_VAR)) != NULL); + EXPECT_TRUE((iotCoreRoleAlias = GETENV(AWS_IOT_CORE_ROLE_ALIAS_ENV_VAR)) != NULL); + EXPECT_TRUE((iotThingName = GETENV(AWS_IOT_CORE_THING_NAME_ENV_VAR)) != NULL); + + EXPECT_EQ(STATUS_SUCCESS, createDefaultCallbacksProviderWithIotCertificateAndTimeouts( + iotCoreCredentialEndPoint, + iotCoreCert, + iotCorePrivateKey, + NULL, + iotCoreRoleAlias, + iotThingName, + (PCHAR) DEFAULT_AWS_REGION, + NULL, + NULL, + 5 * HUNDREDS_OF_NANOS_IN_A_SECOND, + 10 * HUNDREDS_OF_NANOS_IN_A_SECOND, + &pClientCallbacks)); + EXPECT_EQ(STATUS_SUCCESS, freeCallbacksProvider(&pClientCallbacks)); +} + +TEST_F(IoTCredentialTest, createCurlIotCredentialProviderApiTest) +{ + PAwsCredentialProvider pCredentialProvider = NULL; + PCHAR iotCoreCredentialEndPoint; + PCHAR iotCoreCert; + PCHAR iotCorePrivateKey; + PCHAR iotCoreRoleAlias; + PCHAR iotThingName; + + EXPECT_EQ(STATUS_NULL_ARG, createCurlIotCredentialProvider(NULL, + iotCoreCert, + iotCorePrivateKey, + NULL, + iotCoreRoleAlias, + iotThingName, + &pCredentialProvider)); + EXPECT_EQ(STATUS_NULL_ARG, createCurlIotCredentialProvider(NULL, + NULL, + iotCorePrivateKey, + NULL, + iotCoreRoleAlias, + iotThingName, + &pCredentialProvider)); + EXPECT_EQ(STATUS_NULL_ARG, createCurlIotCredentialProvider(NULL, + NULL, + NULL, + NULL, + iotCoreRoleAlias, + iotThingName, + &pCredentialProvider)); + EXPECT_EQ(STATUS_NULL_ARG, createCurlIotCredentialProvider(NULL, + NULL, + NULL, + NULL, + NULL, + iotThingName, + &pCredentialProvider)); + EXPECT_EQ(STATUS_NULL_ARG, createCurlIotCredentialProvider(NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + &pCredentialProvider)); + EXPECT_EQ(STATUS_NULL_ARG, createCurlIotCredentialProvider(NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL)); + EXPECT_EQ(STATUS_NULL_ARG, createCurlIotCredentialProvider(NULL, + iotCoreCert, + iotCorePrivateKey, + NULL, + iotCoreRoleAlias, + iotThingName, + &pCredentialProvider)); + +} + + +TEST_F(IoTCredentialTest, createCurlIotCredentialProviderWithTimeAndTimeoutApiTest) { + PAwsCredentialProvider pCredentialProvider = NULL; + PCHAR iotCoreCredentialEndPoint = NULL; + PCHAR iotCoreCert = NULL; + PCHAR iotCorePrivateKey = NULL; + PCHAR iotCoreRoleAlias = NULL; + PCHAR iotThingName = NULL; + + EXPECT_TRUE((iotCoreCredentialEndPoint = GETENV(AWS_IOT_CORE_CREDENTIAL_ENDPOINT_ENV_VAR)) != NULL); + EXPECT_TRUE((iotCoreCert = GETENV(AWS_IOT_CORE_CERT_ENV_VAR)) != NULL); + EXPECT_TRUE((iotCorePrivateKey = GETENV(AWS_IOT_CORE_PRIVATE_KEY_ENV_VAR)) != NULL); + EXPECT_TRUE((iotCoreRoleAlias = GETENV(AWS_IOT_CORE_ROLE_ALIAS_ENV_VAR)) != NULL); + EXPECT_TRUE((iotThingName = GETENV(AWS_IOT_CORE_THING_NAME_ENV_VAR)) != NULL); + + EXPECT_EQ(STATUS_SUCCESS, createCurlIotCredentialProviderWithTimeAndTimeout(iotCoreCredentialEndPoint, iotCoreCert, + iotCorePrivateKey, NULL, + iotCoreRoleAlias, + iotThingName, + 10 * HUNDREDS_OF_NANOS_IN_A_SECOND, + 5 * HUNDREDS_OF_NANOS_IN_A_SECOND, NULL, + 0, + &pCredentialProvider) + ); + EXPECT_EQ(IOT_REQUEST_CONNECTION_TIMEOUT, reinterpret_cast(pCredentialProvider)->connectionTimeout); + EXPECT_EQ(IOT_REQUEST_COMPLETION_TIMEOUT, reinterpret_cast(pCredentialProvider)->completionTimeout); + EXPECT_EQ(STATUS_SUCCESS, freeIotCredentialProvider(&pCredentialProvider)); +} + +#ifdef KVS_BUILD_WITH_LWS +TEST_F(IoTCredentialTest, createLwsIotCredentialProviderApiTest) { + PAwsCredentialProvider pCredentialProvider; + PCHAR iotCoreCredentialEndPoint; + PCHAR iotCoreCert; + PCHAR iotCorePrivateKey; + PCHAR iotCoreRoleAlias; + PCHAR iotThingName; + CHAR invalidCredentialEndpointLength[MAX_URI_CHAR_LEN + 2]; + CHAR invalidRoleAliasLength[MAX_ROLE_ALIAS_LEN + 2]; + CHAR invalidPrivateKeyPathLength[MAX_PATH_LEN + 2]; + CHAR invalidCaCertPathLength[MAX_PATH_LEN + 2]; + CHAR invalidCoreCertPathLength[MAX_PATH_LEN + 2]; + CHAR invalidThingNameLength[MAX_IOT_THING_NAME_LEN + 2]; + + EXPECT_EQ(STATUS_NULL_ARG, createLwsIotCredentialProvider(NULL, + iotCoreCert, + iotCorePrivateKey, + (PCHAR) DEFAULT_KVS_CACERT_PATH, + iotCoreRoleAlias, + iotThingName, + &pCredentialProvider)); + + EXPECT_EQ(STATUS_NULL_ARG, createLwsIotCredentialProvider(NULL, + NULL, + iotCorePrivateKey, + (PCHAR) DEFAULT_KVS_CACERT_PATH, + iotCoreRoleAlias, + iotThingName, + &pCredentialProvider)); + + EXPECT_EQ(STATUS_NULL_ARG, createLwsIotCredentialProvider(NULL, + NULL, + iotCorePrivateKey, + (PCHAR) DEFAULT_KVS_CACERT_PATH, + iotCoreRoleAlias, + iotThingName, + &pCredentialProvider)); + + EXPECT_EQ(STATUS_NULL_ARG, createLwsIotCredentialProvider(NULL, + NULL, + NULL, + (PCHAR) DEFAULT_KVS_CACERT_PATH, + iotCoreRoleAlias, + iotThingName, + &pCredentialProvider)); + + EXPECT_EQ(STATUS_NULL_ARG, createLwsIotCredentialProvider(NULL, + NULL, + NULL, + NULL, + iotCoreRoleAlias, + iotThingName, + &pCredentialProvider)); + + EXPECT_EQ(STATUS_NULL_ARG, createLwsIotCredentialProvider(NULL, + NULL, + NULL, + NULL, + NULL, + iotThingName, + &pCredentialProvider)); + + EXPECT_EQ(STATUS_NULL_ARG, createLwsIotCredentialProvider(NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + &pCredentialProvider)); + + EXPECT_EQ(STATUS_NULL_ARG, createLwsIotCredentialProvider(NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL)); + + EXPECT_TRUE((iotCoreCredentialEndPoint = GETENV(AWS_IOT_CORE_CREDENTIAL_ENDPOINT_ENV_VAR)) != NULL); + EXPECT_TRUE((iotCoreCert = GETENV(AWS_IOT_CORE_CERT_ENV_VAR)) != NULL); + EXPECT_TRUE((iotCorePrivateKey = GETENV(AWS_IOT_CORE_PRIVATE_KEY_ENV_VAR)) != NULL); + EXPECT_TRUE((iotCoreRoleAlias = GETENV(AWS_IOT_CORE_ROLE_ALIAS_ENV_VAR)) != NULL); + EXPECT_TRUE((iotThingName = GETENV(AWS_IOT_CORE_THING_NAME_ENV_VAR)) != NULL); + MEMSET(invalidCredentialEndpointLength, 'a', SIZEOF(invalidCredentialEndpointLength)); + MEMSET(invalidRoleAliasLength, 'b', SIZEOF(invalidRoleAliasLength)); + MEMSET(invalidPrivateKeyPathLength, 'c', SIZEOF(invalidPrivateKeyPathLength)); + MEMSET(invalidCaCertPathLength, 'd', SIZEOF(invalidCaCertPathLength)); + MEMSET(invalidCoreCertPathLength, 'e', SIZEOF(invalidCoreCertPathLength)); + MEMSET(invalidThingNameLength, 'f', SIZEOF(invalidThingNameLength)); + + EXPECT_EQ(STATUS_INVALID_ARG, createLwsIotCredentialProvider(invalidCredentialEndpointLength, + iotCoreCert, + iotCorePrivateKey, + (PCHAR) DEFAULT_KVS_CACERT_PATH, + iotCoreRoleAlias, + iotThingName, + &pCredentialProvider)); + EXPECT_EQ(STATUS_MAX_ROLE_ALIAS_LEN_EXCEEDED, createLwsIotCredentialProvider(iotCoreCredentialEndPoint, + iotCoreCert, + iotCorePrivateKey, + (PCHAR) DEFAULT_KVS_CACERT_PATH, + invalidRoleAliasLength, + iotThingName, + &pCredentialProvider)); + EXPECT_EQ(STATUS_PATH_TOO_LONG, createLwsIotCredentialProvider(iotCoreCredentialEndPoint, + iotCoreCert, + invalidPrivateKeyPathLength, + (PCHAR) DEFAULT_KVS_CACERT_PATH, + iotCoreRoleAlias, + iotThingName, + &pCredentialProvider)); + EXPECT_EQ(STATUS_PATH_TOO_LONG, createLwsIotCredentialProvider(iotCoreCredentialEndPoint, + iotCoreCert, + invalidPrivateKeyPathLength, + (PCHAR) DEFAULT_KVS_CACERT_PATH, + iotCoreRoleAlias, + iotThingName, + &pCredentialProvider)); + EXPECT_EQ(STATUS_PATH_TOO_LONG, createLwsIotCredentialProvider(iotCoreCredentialEndPoint, + iotCoreCert, + iotCorePrivateKey, + invalidCaCertPathLength, + iotCoreRoleAlias, + iotThingName, + &pCredentialProvider)); + EXPECT_EQ(STATUS_PATH_TOO_LONG, createLwsIotCredentialProvider(iotCoreCredentialEndPoint, + invalidCoreCertPathLength, + iotCorePrivateKey, + (PCHAR) DEFAULT_KVS_CACERT_PATH, + iotCoreRoleAlias, + iotThingName, + &pCredentialProvider)); + EXPECT_EQ(STATUS_MAX_IOT_THING_NAME_LENGTH, createLwsIotCredentialProvider(iotCoreCredentialEndPoint, + iotCoreCert, + iotCorePrivateKey, + (PCHAR) DEFAULT_KVS_CACERT_PATH, + iotCoreRoleAlias, + invalidThingNameLength, + &pCredentialProvider)); + EXPECT_EQ(STATUS_SUCCESS, createLwsIotCredentialProvider(iotCoreCredentialEndPoint, + iotCoreCert, + iotCorePrivateKey, + (PCHAR) DEFAULT_KVS_CACERT_PATH, + iotCoreRoleAlias, + iotThingName, + &pCredentialProvider)); + EXPECT_EQ(STATUS_SUCCESS, freeIotCredentialProvider(&pCredentialProvider)); +} +#endif + +} // namespace video +} // namespace kinesis +} // namespace amazonaws +} // namespace com \ No newline at end of file diff --git a/tst/PlatformCallbackProviderApiTest.cpp b/tst/PlatformCallbackProviderApiTest.cpp index 68c6dfa8d..519ad13b9 100644 --- a/tst/PlatformCallbackProviderApiTest.cpp +++ b/tst/PlatformCallbackProviderApiTest.cpp @@ -45,7 +45,7 @@ namespace com { namespace amazonaws { namespace kinesis { namespace video { TRUE, &pClientCallbacks)); - setPlatformCallbacks(pClientCallbacks, &platformCallbacks); + EXPECT_EQ(STATUS_SUCCESS, setPlatformCallbacks(pClientCallbacks, &platformCallbacks)); EXPECT_TRUE(broadcastConditionVariableAggregate != pClientCallbacks->broadcastConditionVariableFn); @@ -56,15 +56,67 @@ namespace com { namespace amazonaws { namespace kinesis { namespace video { EXPECT_TRUE(getCurrentTimeAggregate != pClientCallbacks->getCurrentTimeFn); EXPECT_TRUE(getRandomNumberAggregate != pClientCallbacks->getRandomNumberFn); EXPECT_TRUE(lockMutexAggregate != pClientCallbacks->lockMutexFn); + EXPECT_TRUE(tryLockMutexAggregate != pClientCallbacks->tryLockMutexFn); EXPECT_TRUE(signalConditionVariableAggregate != pClientCallbacks->signalConditionVariableFn); EXPECT_TRUE(unlockMutexAggregate != pClientCallbacks->unlockMutexFn); - EXPECT_TRUE(unlockMutexAggregate != pClientCallbacks->unlockMutexFn); + EXPECT_TRUE(broadcastConditionVariableAggregate != pClientCallbacks->broadcastConditionVariableFn); EXPECT_TRUE(waitConditionVariableAggregate != pClientCallbacks->waitConditionVariableFn); //non-null callback definition should return aggregated platform callback - platformCallbacks.getCurrentTimeFn = (UINT64 (*)(UINT64)) 2; - setPlatformCallbacks(pClientCallbacks, &platformCallbacks); + platformCallbacks.getCurrentTimeFn = [](UINT64 customParam) -> UINT64 { + return 1234567890ULL; // Return a dummy timestamp + }; + platformCallbacks.getRandomNumberFn = [](UINT64 seed) -> UINT32 { + return 1; + }; + platformCallbacks.createMutexFn = [](UINT64 customData, BOOL isRecursive) -> MUTEX { + return (MUTEX) 0xff; + }; + platformCallbacks.lockMutexFn = [](UINT64 customData, MUTEX mutex) { + return; + }; + platformCallbacks.unlockMutexFn = [] (UINT64 customData, MUTEX mutex) { + return; + }; + platformCallbacks.tryLockMutexFn = [] (UINT64 customData, MUTEX mutex) -> BOOL { + return TRUE; + }; + + platformCallbacks.freeMutexFn = [] (UINT64 customData, MUTEX mutex) { + MUTEX_FREE(mutex); + }; + + platformCallbacks.createConditionVariableFn = [] (UINT64 customData) -> CVAR { + return (CVAR) 1; + }; + platformCallbacks.signalConditionVariableFn = [] (UINT64 customData, CVAR a) -> STATUS { + return STATUS_SUCCESS; + }; + platformCallbacks.broadcastConditionVariableFn = [] (UINT64 customData, CVAR a) -> STATUS { + return STATUS_SUCCESS; + }; + platformCallbacks.waitConditionVariableFn = [] (UINT64 customData, CVAR cvar, MUTEX mutex, UINT64 timeout) -> STATUS { + return STATUS_SUCCESS; + }; + platformCallbacks.freeConditionVariableFn = [] (UINT64 customData, CVAR a) { + return; + }; + + EXPECT_EQ(STATUS_SUCCESS, setPlatformCallbacks(pClientCallbacks, &platformCallbacks)); + + EXPECT_TRUE(getCurrentTimeAggregate == pClientCallbacks->getCurrentTimeFn); + EXPECT_TRUE(getRandomNumberAggregate == pClientCallbacks->getRandomNumberFn); + EXPECT_TRUE(createMutexAggregate == pClientCallbacks->createMutexFn); + EXPECT_TRUE(lockMutexAggregate == pClientCallbacks->lockMutexFn); + EXPECT_TRUE(unlockMutexAggregate == pClientCallbacks->unlockMutexFn); + EXPECT_TRUE(tryLockMutexAggregate == pClientCallbacks->tryLockMutexFn); + EXPECT_TRUE(freeMutexAggregate == pClientCallbacks->freeMutexFn); + EXPECT_TRUE(createConditionVariableAggregate == pClientCallbacks->createConditionVariableFn); + EXPECT_TRUE(waitConditionVariableAggregate == pClientCallbacks->waitConditionVariableFn); + EXPECT_TRUE(signalConditionVariableAggregate == pClientCallbacks->signalConditionVariableFn); + EXPECT_TRUE(broadcastConditionVariableAggregate == pClientCallbacks->broadcastConditionVariableFn); + EXPECT_TRUE(freeConditionVariableAggregate == pClientCallbacks->freeConditionVariableFn); EXPECT_EQ(STATUS_SUCCESS, freeCallbacksProvider(&pClientCallbacks)); diff --git a/tst/ProducerTestFixture.h b/tst/ProducerTestFixture.h index ce6910d22..360c23cf9 100644 --- a/tst/ProducerTestFixture.h +++ b/tst/ProducerTestFixture.h @@ -4,6 +4,7 @@ #include "RotatingStaticAuthCallbacks.h" #define TEST_AUTH_FILE_PATH (PCHAR) "TEST_KVS_AUTH_FILE_PATH" +#define SAMPLE_AUTH_FILE_PATH (PCHAR) "./tst/sample_file_creds.txt" #define TEST_STREAM_NAME (PCHAR) "ScaryTestStream_0" #define TEST_DEVICE_INFO_NAME (PCHAR) "TestDeviceName" #define TEST_USER_AGENT (PCHAR) "Test User Agent" @@ -60,6 +61,13 @@ #define TEST_IOT_THING_NAME (PCHAR) "TestThingName" #define TEST_USER_AGENT_POSTFIX (PCHAR) "Postfix" +// IoT related envs +#define AWS_IOT_CORE_CREDENTIAL_ENDPOINT_ENV_VAR ((PCHAR) "AWS_IOT_CORE_CREDENTIAL_ENDPOINT") +#define AWS_IOT_CORE_CERT_ENV_VAR ((PCHAR) "AWS_IOT_CORE_CERT") +#define AWS_IOT_CORE_PRIVATE_KEY_ENV_VAR ((PCHAR) "AWS_IOT_CORE_PRIVATE_KEY") +#define AWS_IOT_CORE_ROLE_ALIAS_ENV_VAR ((PCHAR) "AWS_IOT_CORE_ROLE_ALIAS") +#define AWS_IOT_CORE_THING_NAME_ENV_VAR ((PCHAR) "AWS_IOT_CORE_THING_NAME") + #ifdef _WIN32 #define TEST_TEMP_DIR_PATH (PCHAR) "C:\\Windows\\Temp\\" #define TEST_TEMP_DIR_PATH_NO_ENDING_SEPARTOR (PCHAR) "C:\\Windows\\Temp" diff --git a/tst/RequestInfoTest.cpp b/tst/RequestInfoTest.cpp new file mode 100644 index 000000000..9f87f5cc4 --- /dev/null +++ b/tst/RequestInfoTest.cpp @@ -0,0 +1,85 @@ +#include "ProducerTestFixture.h" + +namespace com { namespace amazonaws { namespace kinesis { namespace video { + +class RequestInfoTest : public ProducerClientTestBase { +}; + +TEST_F(RequestInfoTest, createRequestInfoArgsTest) +{ + PRequestInfo pRequestInfo = NULL; + CHAR body[11] = "HelloThere"; + EXPECT_EQ(STATUS_NULL_ARG, createRequestInfo((PCHAR) "http://test.com", NULL, (PCHAR) DEFAULT_AWS_REGION, NULL, NULL, + NULL, SSL_CERTIFICATE_TYPE_PEM, (PCHAR) DEFAULT_USER_AGENT_NAME, + 3 * HUNDREDS_OF_NANOS_IN_A_SECOND, 5 * HUNDREDS_OF_NANOS_IN_A_SECOND, DEFAULT_LOW_SPEED_LIMIT, + DEFAULT_LOW_SPEED_TIME_LIMIT, NULL, NULL)); + EXPECT_EQ(STATUS_NULL_ARG, createRequestInfo(NULL, NULL, (PCHAR) DEFAULT_AWS_REGION, NULL, NULL, + NULL, SSL_CERTIFICATE_TYPE_PEM, (PCHAR) DEFAULT_USER_AGENT_NAME, + 3 * HUNDREDS_OF_NANOS_IN_A_SECOND, 5 * HUNDREDS_OF_NANOS_IN_A_SECOND, DEFAULT_LOW_SPEED_LIMIT, + DEFAULT_LOW_SPEED_TIME_LIMIT, NULL, NULL)); + EXPECT_EQ(STATUS_NULL_ARG, createRequestInfo(NULL, NULL, NULL, NULL, NULL, + NULL, SSL_CERTIFICATE_TYPE_PEM, (PCHAR) DEFAULT_USER_AGENT_NAME, + 3 * HUNDREDS_OF_NANOS_IN_A_SECOND, 5 * HUNDREDS_OF_NANOS_IN_A_SECOND, DEFAULT_LOW_SPEED_LIMIT, + DEFAULT_LOW_SPEED_TIME_LIMIT, NULL, &pRequestInfo)); + EXPECT_EQ(STATUS_SUCCESS, freeRequestInfo(&pRequestInfo)); // Test for idempotency + EXPECT_EQ(STATUS_SUCCESS, createRequestInfo((PCHAR) "http://test.com", NULL, (PCHAR) DEFAULT_AWS_REGION, NULL, NULL, + NULL, SSL_CERTIFICATE_TYPE_PEM, (PCHAR) DEFAULT_USER_AGENT_NAME, + 3 * HUNDREDS_OF_NANOS_IN_A_SECOND, 5 * HUNDREDS_OF_NANOS_IN_A_SECOND, DEFAULT_LOW_SPEED_LIMIT, + DEFAULT_LOW_SPEED_TIME_LIMIT, NULL, &pRequestInfo)); + EXPECT_EQ(STATUS_NULL_ARG, freeRequestInfo(NULL)); + EXPECT_EQ(STATUS_SUCCESS, freeRequestInfo(&pRequestInfo)); + EXPECT_EQ(STATUS_SUCCESS, createRequestInfo((PCHAR) "http://test.com", body, (PCHAR) DEFAULT_AWS_REGION, NULL, NULL, + NULL, SSL_CERTIFICATE_TYPE_PEM, (PCHAR) DEFAULT_USER_AGENT_NAME, + 3 * HUNDREDS_OF_NANOS_IN_A_SECOND, 5 * HUNDREDS_OF_NANOS_IN_A_SECOND, DEFAULT_LOW_SPEED_LIMIT, + DEFAULT_LOW_SPEED_TIME_LIMIT, NULL, &pRequestInfo)); + EXPECT_EQ(0, STRNCMP(body, pRequestInfo->body, STRLEN(body))); + EXPECT_EQ(STRLEN(body), pRequestInfo->bodySize); + EXPECT_EQ(STATUS_SUCCESS, freeRequestInfo(&pRequestInfo)); +} + +TEST_F(RequestInfoTest, removeRequestHeaderApiTest) +{ + RequestInfo requestInfo; + AwsCredentials awsCredentials; + CHAR headerName[5]; + PCHAR pHostStart, pHostEnd; + UINT32 len; + EXPECT_EQ(STATUS_NULL_ARG, removeRequestHeader(NULL, NULL)); + EXPECT_EQ(STATUS_NULL_ARG, removeRequestHeader(&requestInfo, NULL)); + EXPECT_EQ(STATUS_NULL_ARG, removeRequestHeader(NULL, headerName)); + EXPECT_EQ(STATUS_SUCCESS, singleListCreate(&requestInfo.pRequestHeaders)); + + STRCPY(requestInfo.url, "https://kinesisvideo.us-west-2.amazonaws.com/describeStream"); + requestInfo.currentTime = GETTIME(); + requestInfo.pAwsCredentials = &awsCredentials; + + // Get the host header + EXPECT_EQ(STATUS_SUCCESS, getRequestHost(requestInfo.url, &pHostStart, &pHostEnd)); + len = (UINT32) (pHostEnd - pHostStart); + EXPECT_EQ(STATUS_SUCCESS, setRequestHeader(&requestInfo, (PCHAR) "host", 0, pHostStart, len)); + EXPECT_EQ(STATUS_SUCCESS, removeRequestHeader(&requestInfo, (PCHAR) "test")); + EXPECT_EQ(STATUS_SUCCESS, removeRequestHeader(&requestInfo, (PCHAR) "host")); + EXPECT_EQ(STATUS_SUCCESS, singleListFree(requestInfo.pRequestHeaders)); +} + +TEST_F(RequestInfoTest, getServiceCallResultFromHttpStatusApiTest) +{ + EXPECT_EQ((SERVICE_CALL_RESULT) SERVICE_CALL_RESULT_OK, getServiceCallResultFromHttpStatus(SERVICE_CALL_RESULT_OK)); + EXPECT_EQ((SERVICE_CALL_RESULT) SERVICE_CALL_INVALID_ARG, getServiceCallResultFromHttpStatus(SERVICE_CALL_INVALID_ARG)); + EXPECT_EQ((SERVICE_CALL_RESULT) SERVICE_CALL_RESOURCE_NOT_FOUND, getServiceCallResultFromHttpStatus(SERVICE_CALL_RESOURCE_NOT_FOUND)); + EXPECT_EQ((SERVICE_CALL_RESULT) SERVICE_CALL_FORBIDDEN, getServiceCallResultFromHttpStatus(SERVICE_CALL_FORBIDDEN)); + EXPECT_EQ((SERVICE_CALL_RESULT) SERVICE_CALL_RESOURCE_DELETED, getServiceCallResultFromHttpStatus(SERVICE_CALL_RESOURCE_DELETED)); + EXPECT_EQ((SERVICE_CALL_RESULT) SERVICE_CALL_NOT_AUTHORIZED, getServiceCallResultFromHttpStatus(SERVICE_CALL_NOT_AUTHORIZED)); + EXPECT_EQ((SERVICE_CALL_RESULT) SERVICE_CALL_NOT_IMPLEMENTED, getServiceCallResultFromHttpStatus(SERVICE_CALL_NOT_IMPLEMENTED)); + EXPECT_EQ((SERVICE_CALL_RESULT) SERVICE_CALL_INTERNAL_ERROR, getServiceCallResultFromHttpStatus(SERVICE_CALL_INTERNAL_ERROR)); + EXPECT_EQ((SERVICE_CALL_RESULT) SERVICE_CALL_REQUEST_TIMEOUT, getServiceCallResultFromHttpStatus(SERVICE_CALL_REQUEST_TIMEOUT)); + EXPECT_EQ((SERVICE_CALL_RESULT) SERVICE_CALL_GATEWAY_TIMEOUT, getServiceCallResultFromHttpStatus(SERVICE_CALL_GATEWAY_TIMEOUT)); + EXPECT_EQ((SERVICE_CALL_RESULT) SERVICE_CALL_NETWORK_READ_TIMEOUT, getServiceCallResultFromHttpStatus(SERVICE_CALL_NETWORK_READ_TIMEOUT)); + EXPECT_EQ((SERVICE_CALL_RESULT) SERVICE_CALL_NETWORK_CONNECTION_TIMEOUT, getServiceCallResultFromHttpStatus(SERVICE_CALL_NETWORK_CONNECTION_TIMEOUT)); + EXPECT_EQ((SERVICE_CALL_RESULT) SERVICE_CALL_UNKNOWN, getServiceCallResultFromHttpStatus(MAX_UINT32)); +} + +} // namespace video +} // namespace kinesis +} // namespace amazonaws +} // namespace com \ No newline at end of file diff --git a/tst/UtilsApiTest.cpp b/tst/UtilsApiTest.cpp new file mode 100644 index 000000000..044ca0818 --- /dev/null +++ b/tst/UtilsApiTest.cpp @@ -0,0 +1,42 @@ +#include "ProducerTestFixture.h" +#include + +namespace com { namespace amazonaws { namespace kinesis { namespace video { + +class UtilsApiTest : public ProducerClientTestBase { +}; + + +TEST_F(UtilsApiTest, ioTExpirationParsingNullArgs) +{ + UINT64 iotTimeInEpoch = 1548972059; + CHAR validFormatIotExpirationTimeStamp[] = "2019-01-31T23:00:59Z"; // expiration is current time + 1 hour + UINT64 expirationTimestampInEpoch = 0; + + EXPECT_EQ(STATUS_NULL_ARG, convertTimestampToEpoch(NULL, iotTimeInEpoch, &expirationTimestampInEpoch)); + EXPECT_EQ(STATUS_NULL_ARG, convertTimestampToEpoch(NULL, iotTimeInEpoch, NULL)); + EXPECT_EQ(STATUS_NULL_ARG, convertTimestampToEpoch(validFormatIotExpirationTimeStamp, iotTimeInEpoch, NULL)); +} + +TEST_F(UtilsApiTest, ioTExpirationParsingValidArgs) +{ + UINT64 iotTimeInEpoch = 1548972059; + CHAR validFormatIotExpirationTimeStamp[] = "2019-01-31T23:00:59Z"; // expiration is current time + 1 hour + UINT64 expirationTimestampInEpoch = 0; + + EXPECT_EQ(STATUS_SUCCESS, convertTimestampToEpoch(validFormatIotExpirationTimeStamp, iotTimeInEpoch, &expirationTimestampInEpoch)); +} + +TEST_F(UtilsApiTest, getSslCertNameFromTypeApiTest) +{ + EXPECT_STREQ(SSL_CERTIFICATE_TYPE_PEM_STR, getSslCertNameFromType(SSL_CERTIFICATE_TYPE_PEM)); + EXPECT_STREQ(SSL_CERTIFICATE_TYPE_DER_STR, getSslCertNameFromType(SSL_CERTIFICATE_TYPE_DER)); + EXPECT_STREQ(SSL_CERTIFICATE_TYPE_ENG_STR, getSslCertNameFromType(SSL_CERTIFICATE_TYPE_ENG)); + EXPECT_STREQ(SSL_CERTIFICATE_TYPE_UNKNOWN_STR, getSslCertNameFromType((SSL_CERTIFICATE_TYPE) 0xffff)); +} + + +} // namespace video +} // namespace kinesis +} // namespace amazonaws +} // namespace com; \ No newline at end of file diff --git a/tst/cert.pem b/tst/cert.pem new file mode 100644 index 000000000..959eacf27 --- /dev/null +++ b/tst/cert.pem @@ -0,0 +1,24 @@ +-----BEGIN CERTIFICATE----- +MIID7zCCAtegAwIBAgIBADANBgkqhkiG9w0BAQsFADCBmDELMAkGA1UEBhMCVVMx +EDAOBgNVBAgTB0FyaXpvbmExEzARBgNVBAcTClNjb3R0c2RhbGUxJTAjBgNVBAoT +HFN0YXJmaWVsZCBUZWNobm9sb2dpZXMsIEluYy4xOzA5BgNVBAMTMlN0YXJmaWVs +ZCBTZXJ2aWNlcyBSb290IENlcnRpZmljYXRlIEF1dGhvcml0eSAtIEcyMB4XDTA5 +MDkwMTAwMDAwMFoXDTM3MTIzMTIzNTk1OVowgZgxCzAJBgNVBAYTAlVTMRAwDgYD +VQQIEwdBcml6b25hMRMwEQYDVQQHEwpTY290dHNkYWxlMSUwIwYDVQQKExxTdGFy +ZmllbGQgVGVjaG5vbG9naWVzLCBJbmMuMTswOQYDVQQDEzJTdGFyZmllbGQgU2Vy +dmljZXMgUm9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkgLSBHMjCCASIwDQYJKoZI +hvcNAQEBBQADggEPADCCAQoCggEBANUMOsQq+U7i9b4Zl1+OiFOxHz/Lz58gE20p +OsgPfTz3a3Y4Y9k2YKibXlwAgLIvWX/2h/klQ4bnaRtSmpDhcePYLQ1Ob/bISdm2 +8xpWriu2dBTrz/sm4xq6HZYuajtYlIlHVv8loJNwU4PahHQUw2eeBGg6345AWh1K +Ts9DkTvnVtYAcMtS7nt9rjrnvDH5RfbCYM8TWQIrgMw0R9+53pBlbQLPLJGmpufe +hRhJfGZOozptqbXuNC66DQO4M99H67FrjSXZm86B0UVGMpZwh94CDklDhbZsc7tk +6mFBrMnUVN+HL8cisibMn1lUaJ/8viovxFUcdUBgF4UCVTmLfwUCAwEAAaNCMEAw +DwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFJxfAN+q +AdcwKziIorhtSpzyEZGDMA0GCSqGSIb3DQEBCwUAA4IBAQBLNqaEd2ndOxmfZyMI +bw5hyf2E3F/YNoHN2BtBLZ9g3ccaaNnRbobhiCPPE95Dz+I0swSdHynVv/heyNXB +ve6SbzJ08pGCL72CQnqtKrcgfU28elUSwhXqvfdqlS5sdJ/PHLTyxQGjhdByPq1z +qwubdQxtRbeOlKyWN7Wg0I8VRw7j6IPdj/3vQQF3zCepYoUz8jcI73HPdwbeyBkd +iEDPfUYd/x7H4c7/I9vG+o1VTqkC50cRRj70/b17KSa7qWFiNyi2LSr2EIZkyXCn +0q23KXB56jzaYyWf/Wi3MOxw+3WKt21gZ7IeyLnp2KhvAotnDU0mV3HaIPzBSlCN +sSi6 +-----END CERTIFICATE----- \ No newline at end of file diff --git a/tst/sample_file_creds.txt b/tst/sample_file_creds.txt new file mode 100644 index 000000000..44f78a40d --- /dev/null +++ b/tst/sample_file_creds.txt @@ -0,0 +1 @@ +CREDENTIALS AAAAAAAAAAAAAAAA AAAAAAA \ No newline at end of file