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/ci.yml b/.github/workflows/ci.yml index 3d2fd5547..858c88f59 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 @@ -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 @@ -85,14 +83,78 @@ 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 + 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-m1-build-gcc: + runs-on: macos-13-xlarge + env: + CC: /opt/homebrew/bin/gcc-13 + CXX: /opt/homebrew/bin/g++-13 + AWS_KVS_LOG_LEVEL: 2 + permissions: + id-token: write + contents: read + steps: + - name: Setup XCode version 15.2 + uses: maxim-lobanov/setup-xcode@v1 + with: + xcode-version: '15.2' + - 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 + cmake .. -DBUILD_TEST=TRUE -DCOMPILER_WARNINGS=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 + ./tst/producer_test + mac-os-build-gcc-local-openssl: runs-on: macos-11 permissions: 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 @@ -268,31 +330,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 @@ -331,7 +396,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/.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 diff --git a/CMake/Dependencies/libcurl-CMakeLists.txt b/CMake/Dependencies/libcurl-CMakeLists.txt index 7d369197e..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) @@ -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/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/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/libkvspic-CMakeLists.txt b/CMake/Dependencies/libkvspic-CMakeLists.txt index 6ab35a2da..87a0fb728 100644 --- a/CMake/Dependencies/libkvspic-CMakeLists.txt +++ b/CMake/Dependencies/libkvspic-CMakeLists.txt @@ -1,13 +1,13 @@ cmake_minimum_required(VERSION 3.6.3) -project(libkvspic-download NONE) +project(libkvspic-download LANGUAGES C) 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 v1.1.0 SOURCE_DIR "${CMAKE_CURRENT_BINARY_DIR}/kvspic-src" BINARY_DIR "${CMAKE_CURRENT_BINARY_DIR}/kvspic-build" CMAKE_ARGS diff --git a/CMake/Dependencies/libmbedtls-CMakeLists.txt b/CMake/Dependencies/libmbedtls-CMakeLists.txt index cd89da2b4..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) @@ -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/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/CMake/Dependencies/libwebsockets-CMakeLists.txt b/CMake/Dependencies/libwebsockets-CMakeLists.txt index e00ee305d..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) @@ -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 d23b3cafa..3131b8479 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,7 +1,12 @@ cmake_minimum_required(VERSION 3.6.3) set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/CMake;${CMAKE_MODULE_PATH}") include(Utilities) -project(KinesisVideoProducerC VERSION 1.5.1 LANGUAGES C) +project(KinesisVideoProducerC VERSION 1.5.2 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 2) +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) @@ -16,6 +21,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) @@ -28,12 +34,21 @@ 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) 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}) @@ -44,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) @@ -73,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.") @@ -82,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}) @@ -90,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}) @@ -257,8 +279,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} @@ -290,8 +319,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} @@ -310,16 +346,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/README.md b/README.md index 9d6860a80..ff2bd3464 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 @@ -132,6 +133,51 @@ 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 + + +### Setup logging: +Set up the desired log level. The log levels and corresponding values currently available are: +1. `LOG_LEVEL_VERBOSE` ---- 1 +2. `LOG_LEVEL_DEBUG` ---- 2 +3. `LOG_LEVEL_INFO` ---- 3 +4. `LOG_LEVEL_WARN` ---- 4 +5. `LOG_LEVEL_ERROR` ---- 5 +6. `LOG_LEVEL_FATAL` ---- 6 +7. `LOG_LEVEL_SILENT` ---- 7 +8. `LOG_LEVEL_PROFILE` ---- 8 + +To set a log level, you can set it using the deviceInfo structure. +``` +pDeviceInfo->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: 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 diff --git a/samples/KvsAudioOnlyStreamingSample.c b/samples/KvsAudioOnlyStreamingSample.c index 8a992ffad..e99de022c 100644 --- a/samples/KvsAudioOnlyStreamingSample.c +++ b/samples/KvsAudioOnlyStreamingSample.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 @@ -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 a5e3d2efc..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 @@ -11,11 +11,14 @@ #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 MAX_METADATA_PER_FRAGMENT 10 #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; @@ -57,10 +60,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; @@ -69,17 +72,28 @@ 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 " - "\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); } - - 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) { @@ -88,26 +102,42 @@ 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; } - if (argc >= 3) { + 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) { + 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 && !IS_EMPTY_STRING(argv[4])) { + STRNCPY(frameFilePath, argv[4], MAX_PATH_LEN); + } else { + STRCPY(frameFilePath, (PCHAR) "../samples/"); + } + + 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; // default storage size is 128MB. Use setDeviceInfoStorageSize after create to change storage size. @@ -122,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); @@ -154,6 +190,17 @@ 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 (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); + SNPRINTF(metadataValue, METADATA_MAX_VALUE_LENGTH, "TEST_VALUE_%d", frame.index + n); + CHK_STATUS(putKinesisVideoFragmentMetadata(streamHandle, metadataKey, metadataValue, TRUE)); + } + } + CHK_STATUS(putKinesisVideoFrame(streamHandle, &frame)); if (firstFrame) { startUpLatency = (DOUBLE) (GETTIME() - startTime) / (DOUBLE) HUNDREDS_OF_NANOS_IN_A_MILLISECOND; 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..29801cb01 --- /dev/null +++ b/scripts/generate-iot-credential.sh @@ -0,0 +1,115 @@ +#!/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 + +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 diff --git a/src/include/com/amazonaws/kinesis/video/common/Include.h b/src/include/com/amazonaws/kinesis/video/common/Include.h index 730b0b2bb..cc8fae722 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 @@ -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/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/Common/IotCredentialProvider.c b/src/source/Common/IotCredentialProvider.c index 4e0604628..ba23b185e 100644 --- a/src/source/Common/IotCredentialProvider.c +++ b/src/source/Common/IotCredentialProvider.c @@ -139,7 +139,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); @@ -191,10 +191,20 @@ 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 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; 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 a3890eda8..86e7d9365 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 +} diff --git a/src/source/CurlApiCallbacks.c b/src/source/CurlApiCallbacks.c index 66b71a063..c045e0e1e 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); @@ -994,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) { @@ -1226,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) { @@ -1317,7 +1332,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; @@ -1350,8 +1365,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); @@ -1430,6 +1444,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; @@ -1534,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) { @@ -1863,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/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; 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/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 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 5ef4cdea2..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), @@ -233,11 +231,20 @@ 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); } + ATOMIC_STORE_BOOL(&mStopProducer, FALSE); + ATOMIC_STORE_BOOL(&mProducerStopped, FALSE); // Store the function pointers gTotalProducerClientMemoryUsage = 0; @@ -332,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; @@ -535,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"); @@ -561,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); @@ -623,6 +631,7 @@ STATUS ProducerClientTestBase::curlEasyPerformHookFunc(PCurlResponse pCurlRespon pTest->mPutMediaCallResult = SERVICE_CALL_RESULT_OK; } } + MUTEX_UNLOCK(pTest->mTestCallbackLock); return retStatus; } @@ -640,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++; @@ -647,6 +657,7 @@ STATUS ProducerClientTestBase::curlWriteCallbackHookFunc(PCurlResponse pCurlResp *ppRetBuffer = pTest->mWriteBuffer; *pRetDataSize = pTest->mWriteDataSize; } + MUTEX_UNLOCK(pTest->mTestCallbackLock); return pTest->mWriteStatus; } @@ -671,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++; @@ -679,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;