diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 6bdaa999..8507ac84 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -1,6 +1,11 @@ *Issue #, if available:* -*Description of changes:* +*What was changed?* +*Why was it changed?* -By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice. +*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 0bd55607..514f0913 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,5 +1,4 @@ name: Producer CPP SDK CI - on: push: branches: @@ -12,9 +11,11 @@ on: jobs: mac-os-build-clang: - runs-on: macos-11 + runs-on: macos-12 env: AWS_KVS_LOG_LEVEL: 2 + CC: /usr/bin/clang + CXX: /usr/bin/clang++ permissions: id-token: write contents: read @@ -23,12 +24,12 @@ jobs: uses: actions/checkout@v3 - name: Install dependencies run: | - brew install pkg-config openssl cmake gstreamer log4cplus + brew install pkg-config cmake gstreamer brew unlink openssl - name: Build repository run: | mkdir build && cd build - sh -c 'cmake .. -DBUILD_TEST=TRUE -DCOMPILER_WARNINGS=TRUE;cmake .. -DBUILD_TEST=TRUE -DCOMPILER_WARNINGS=TRUE -DCMAKE_INSTALL_PREFIX=.' + cmake .. -DBUILD_TEST=TRUE -DCOMPILER_WARNINGS=TRUE -DCMAKE_INSTALL_PREFIX=. make make install - name: Configure AWS Credentials @@ -38,6 +39,37 @@ jobs: role-session-name: ${{ secrets.AWS_ROLE_SESSION_NAME }} aws-region: ${{ secrets.AWS_REGION }} role-duration-seconds: 10800 + - name: Run tests + run: | + cd build + ./tst/producerTest + + 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: Install dependencies + run: | + brew install pkg-config openssl cmake gstreamer + brew unlink openssl + - name: Build repository + run: | + mkdir build && cd build + sh -c 'cmake .. -DBUILD_TEST=TRUE -DCOMPILER_WARNINGS=TRUE -DCMAKE_INSTALL_PREFIX=. -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_INSTALL_PREFIX=. -DCMAKE_C_COMPILER=$(brew --prefix llvm@15)/bin/clang -DCMAKE_CXX_COMPILER=$(brew --prefix llvm@15)/bin/clang++' + make install + - 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 @@ -49,8 +81,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 @@ -189,7 +221,7 @@ jobs: timeout --signal=SIGABRT 60m ./tst/producerTest # memory-sanitizer: - # runs-on: ubuntu-20.04 + # runs-on: ubuntu-20.04 # permissions: # id-token: write # contents: read @@ -244,8 +276,6 @@ jobs: runs-on: ubuntu-20.04 env: AWS_KVS_LOG_LEVEL: 2 - CC: gcc - CXX: g++ permissions: id-token: write contents: read @@ -286,16 +316,21 @@ jobs: steps: - name: Clone repository uses: actions/checkout@v3 + - name: Move cloned repo # to shorten path length for Windows OS + run: | + git config --system core.longpaths true + mkdir C:\amazon-kinesis-video-streams-producer-sdk-cpp + Move-Item -Path "D:\a\amazon-kinesis-video-streams-producer-sdk-cpp\amazon-kinesis-video-streams-producer-sdk-cpp\*" -Destination "C:\amazon-kinesis-video-streams-producer-sdk-cpp" - name: Install dependencies run: | choco install nasm strawberryperl choco install gstreamer --version=1.16.2 - choco install gstreamer-devel --version=1.16.2 + choco install gstreamer-devel --version=1.16.2 - 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-sdk-cpp\amazon-kinesis-video-streams-producer-sdk-cpp\open-source\local\lib;D:\a\amazon-kinesis-video-streams-producer-sdk-cpp\amazon-kinesis-video-streams-producer-sdk-cpp\open-source\local\bin' - git config --system core.longpaths true - .github/build_windows.bat + $env:Path += ";C:\Strawberry\perl\site\bin;C:\Strawberry\perl\bin;C:\Strawberry\c\bin;C:\Program Files\NASM;C:\amazon-kinesis-video-streams-producer-sdk-cpp\open-source\local\lib;C:\amazon-kinesis-video-streams-producer-sdk-cpp\open-source\local\bin" + cd C:\amazon-kinesis-video-streams-producer-sdk-cpp + .github\build_windows.bat - name: Configure AWS Credentials uses: aws-actions/configure-aws-credentials@v1-node16 with: @@ -305,8 +340,8 @@ jobs: role-duration-seconds: 10800 - 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-sdk-cpp\amazon-kinesis-video-streams-producer-sdk-cpp\open-source\local\lib;D:\a\amazon-kinesis-video-streams-producer-sdk-cpp\amazon-kinesis-video-streams-producer-sdk-cpp\open-source\local\bin' - & "D:\a\amazon-kinesis-video-streams-producer-sdk-cpp\amazon-kinesis-video-streams-producer-sdk-cpp\build\tst\producerTest.exe" + $env:Path += ';C:\Strawberry\perl\site\bin;C:\Strawberry\perl\bin;C:\Strawberry\c\bin;C:\Program Files\NASM;C:\amazon-kinesis-video-streams-producer-sdk-cpp\open-source\local\lib;C:\amazon-kinesis-video-streams-producer-sdk-cpp\open-source\local\bin' + & "C:\amazon-kinesis-video-streams-producer-sdk-cpp\build\tst\producerTest.exe" arm64-cross-compilation: runs-on: ubuntu-20.04 diff --git a/.github/workflows/pr-desc-lint.yml b/.github/workflows/pr-desc-lint.yml new file mode 100644 index 00000000..e4fcc921 --- /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/.gitignore b/.gitignore index b4dc61e3..43ff4423 100644 --- a/.gitignore +++ b/.gitignore @@ -6,16 +6,9 @@ build cmake-build-debug/ cmake-build-release/ doc/ -open-source/libgtest -open-source/libjsmn -open-source/libopenssl -open-source/libsrtp -open-source/libusrsctp -open-source/libwebsockets -open-source/local -open-source/libautoconf +open-source/ outputs tags dependency +.vs -.vs \ No newline at end of file diff --git a/CMake/Dependencies/libautoconf-CMakeLists.txt b/CMake/Dependencies/libautoconf-CMakeLists.txt deleted file mode 100644 index a4544aff..00000000 --- a/CMake/Dependencies/libautoconf-CMakeLists.txt +++ /dev/null @@ -1,16 +0,0 @@ -cmake_minimum_required(VERSION 3.6.3) - -project(libautoconf-download NONE) - -find_program(MAKE_EXE NAMES make) - -include(ExternalProject) -ExternalProject_Add(project_libautoconf - URL http://ftp.gnu.org/gnu/autoconf/autoconf-2.69.tar.gz - PREFIX ${CMAKE_CURRENT_BINARY_DIR}/build - CONFIGURE_COMMAND ${CMAKE_CURRENT_BINARY_DIR}/build/src/project_libautoconf/configure --prefix=${OPEN_SRC_INSTALL_PREFIX} - BUILD_COMMAND ${MAKE_EXE} -j 4 - BUILD_IN_SOURCE TRUE - INSTALL_COMMAND ${MAKE_EXE} install - TEST_COMMAND "" -) diff --git a/CMake/Dependencies/libautomake-CMakeLists.txt b/CMake/Dependencies/libautomake-CMakeLists.txt deleted file mode 100644 index 8544a16f..00000000 --- a/CMake/Dependencies/libautomake-CMakeLists.txt +++ /dev/null @@ -1,16 +0,0 @@ -cmake_minimum_required(VERSION 3.6.3) - -project(libautomake-download NONE) - -find_program(MAKE_EXE NAMES make) - -include(ExternalProject) -ExternalProject_Add(project_libautomake - URL http://ftp.gnu.org/gnu/automake/automake-1.16.1.tar.gz - PREFIX ${CMAKE_CURRENT_BINARY_DIR}/build - CONFIGURE_COMMAND ${CMAKE_CURRENT_BINARY_DIR}/build/src/project_libautomake/configure --prefix=${OPEN_SRC_INSTALL_PREFIX} - BUILD_COMMAND ${MAKE_EXE} -j 4 - BUILD_IN_SOURCE TRUE - INSTALL_COMMAND ${MAKE_EXE} install - TEST_COMMAND "" -) diff --git a/CMake/Dependencies/libgtest-CMakeLists.txt b/CMake/Dependencies/libgtest-CMakeLists.txt index db88fda4..1e55b44f 100644 --- a/CMake/Dependencies/libgtest-CMakeLists.txt +++ b/CMake/Dependencies/libgtest-CMakeLists.txt @@ -1,6 +1,6 @@ cmake_minimum_required(VERSION 3.6.3) -project(libgtest-download NONE) +project(libgtest-download LANGUAGES CXX) include(ExternalProject) diff --git a/CMake/Dependencies/libjsmn-CMakeLists.txt b/CMake/Dependencies/libjsmn-CMakeLists.txt deleted file mode 100644 index b16ca0f5..00000000 --- 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 133c61d9..00000000 --- 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/libkvscproducer-CMakeLists.txt b/CMake/Dependencies/libkvscproducer-CMakeLists.txt index 35532f94..9c643086 100644 --- a/CMake/Dependencies/libkvscproducer-CMakeLists.txt +++ b/CMake/Dependencies/libkvscproducer-CMakeLists.txt @@ -1,15 +1,16 @@ cmake_minimum_required(VERSION 3.6.3) -project(libkvscproducer-download NONE) +project(libkvscproducer-download LANGUAGES CXX) include(ExternalProject) # clone repo only ExternalProject_Add(libkvscproducer-download GIT_REPOSITORY https://github.com/awslabs/amazon-kinesis-video-streams-producer-c.git - GIT_TAG 178109a5dbfc5288ba5cf7fab1dc1afd5e2e182b + GIT_TAG v1.5.2 SOURCE_DIR "${CMAKE_CURRENT_BINARY_DIR}/kvscproducer-src" BINARY_DIR "${CMAKE_CURRENT_BINARY_DIR}/kvscproducer-build" + CMAKE_ARGS -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} CONFIGURE_COMMAND "" BUILD_COMMAND "" INSTALL_COMMAND "" diff --git a/CMake/Dependencies/liblog4cplus-CMakeLists.txt b/CMake/Dependencies/liblog4cplus-CMakeLists.txt index 54fc8a88..13d5c5bb 100644 --- a/CMake/Dependencies/liblog4cplus-CMakeLists.txt +++ b/CMake/Dependencies/liblog4cplus-CMakeLists.txt @@ -1,7 +1,7 @@ cmake_minimum_required(VERSION 3.6.3) find_program(MAKE_EXE NAMES make) -project(log4cplus-download NONE) +project(log4cplus-download LANGUAGES CXX) set(BUILD_SHARED_LIBS 1) if (BUILD_STATIC) @@ -19,26 +19,27 @@ if (DEFINED CMAKE_OSX_SYSROOT AND NOT CMAKE_OSX_SYSROOT STREQUAL "") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -isysroot${CMAKE_OSX_SYSROOT}") endif() +if (CMAKE_CXX_COMPILER_ID STREQUAL "AppleClang") + SET(CONFIGURE_COMMAND ${CONFIGURE_STATIC}) +endif() include(ExternalProject) if (WIN32) ExternalProject_Add(project_log4cplus GIT_REPOSITORY https://github.com/log4cplus/log4cplus - GIT_TAG REL_2_0_1 + GIT_TAG REL_2_0_7 PREFIX ${CMAKE_CURRENT_BINARY_DIR}/build TEST_COMMAND "" - CMAKE_ARGS -DCMAKE_INSTALL_PREFIX=${OPEN_SRC_INSTALL_PREFIX} -DLOG4CPLUS_BUILD_TESTING=0 -DLOG4CPLUS_BUILD_LOGGINGSERVER=0 -DUNICODE=0 -DBUILD_SHARED_LIBS=${BUILD_SHARED_LIBS} + CMAKE_ARGS -DCMAKE_INSTALL_PREFIX=${OPEN_SRC_INSTALL_PREFIX} -DLOG4CPLUS_ENABLE_THREAD_POOL=OFF -DLOG4CPLUS_BUILD_TESTING=0 -DLOG4CPLUS_BUILD_LOGGINGSERVER=0 -DUNICODE=0 -DBUILD_SHARED_LIBS=${BUILD_SHARED_LIBS} -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} ) else() ExternalProject_Add(project_log4cplus - GIT_REPOSITORY https://github.com/log4cplus/log4cplus - GIT_TAG REL_2_0_1 - PREFIX ${CMAKE_CURRENT_BINARY_DIR}/build - CONFIGURE_COMMAND ${CONFIGURE_COMMAND} - BUILD_COMMAND ${MAKE_EXE} - BUILD_IN_SOURCE TRUE - INSTALL_COMMAND ${MAKE_EXE} install - TEST_COMMAND "" + GIT_REPOSITORY https://github.com/log4cplus/log4cplus + GIT_TAG REL_2_0_7 # Fixes the CATCH_REQUIRE related build errors encountered in MacOS M1 + PREFIX ${CMAKE_CURRENT_BINARY_DIR}/build + CMAKE_ARGS -DCMAKE_INSTALL_PREFIX=${OPEN_SRC_INSTALL_PREFIX} -DBUILD_SHARED_LIBS=${BUILD_SHARED_LIBS} -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} + BUILD_ALWAYS TRUE + TEST_COMMAND "" ) endif() diff --git a/CMake/Dependencies/libopenssl-CMakeLists.txt b/CMake/Dependencies/libopenssl-CMakeLists.txt deleted file mode 100644 index bf4edca2..00000000 --- a/CMake/Dependencies/libopenssl-CMakeLists.txt +++ /dev/null @@ -1,31 +0,0 @@ -cmake_minimum_required(VERSION 3.6.3) - -project(libopenssl-download NONE) - -if (WIN32) - find_program(MAKE_EXE NAMES nmake) - SET(CONFIGURE_COMMAND perl ${CMAKE_CURRENT_BINARY_DIR}/build/src/project_libopenssl/Configure VC-WIN64A no-asm --prefix=${OPEN_SRC_INSTALL_PREFIX} --openssldir=${OPEN_SRC_INSTALL_PREFIX}) -else() - find_program(MAKE_EXE NAMES make) - if (BUILD_STATIC) - SET(OPENSSL_EXTRA ${OPENSSL_EXTRA} no-shared no-dso) - 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}) - else() - SET(CONFIGURE_COMMAND ${CMAKE_CURRENT_BINARY_DIR}/build/src/project_libopenssl/config ${OPENSSL_EXTRA} --prefix=${OPEN_SRC_INSTALL_PREFIX} --openssldir=${OPEN_SRC_INSTALL_PREFIX}) - endif() -endif() - -include(ExternalProject) -ExternalProject_Add(project_libopenssl - GIT_REPOSITORY https://github.com/openssl/openssl.git - GIT_TAG OpenSSL_1_1_1t - PREFIX ${CMAKE_CURRENT_BINARY_DIR}/build - CONFIGURE_COMMAND ${CONFIGURE_COMMAND} - BUILD_COMMAND ${MAKE_EXE} - BUILD_IN_SOURCE TRUE - INSTALL_COMMAND ${MAKE_EXE} install_sw - TEST_COMMAND "" -) diff --git a/CMake/FindJsmn.cmake b/CMake/FindJsmn.cmake deleted file mode 100644 index fe627eee..00000000 --- a/CMake/FindJsmn.cmake +++ /dev/null @@ -1,9 +0,0 @@ -find_path(JSMN_INCLUDE_DIRS jsmn.h) - -find_library(JSMN_LIBRARY jsmn) - -include(FindPackageHandleStandardArgs) -find_package_handle_standard_args(JSMN DEFAULT_MSG - JSMN_LIBRARY JSMN_INCLUDE_DIRS) - -mark_as_advanced(JSMN_LIBRARY JSMN_INCLUDE_DIRS) diff --git a/CMake/FindLog4cplus.cmake b/CMake/FindLog4cplus.cmake index c329e28d..6238f9ce 100644 --- a/CMake/FindLog4cplus.cmake +++ b/CMake/FindLog4cplus.cmake @@ -36,6 +36,7 @@ find_path(LOG4CPLUS_INCLUDE_DIR find_library(LOG4CPLUS_LIBRARY NAMES log4cplus + log4cplusS PATHS /usr/local /usr diff --git a/CMake/Utilities.cmake b/CMake/Utilities.cmake index 560ebc3a..f38fbd60 100644 --- a/CMake/Utilities.cmake +++ b/CMake/Utilities.cmake @@ -33,12 +33,6 @@ endfunction() function(build_dependency lib_name) set(supported_libs gtest - jsmn - openssl - curl - mbedtls - autoconf - automake log4cplus) list(FIND supported_libs ${lib_name} index) if(${index} EQUAL -1) @@ -48,24 +42,12 @@ function(build_dependency lib_name) set(target_found NOTFOUND) - if (${lib_name} STREQUAL "autoconf" OR ${lib_name} STREQUAL "automake") - find_program( - target_found - NAMES ${lib_name} - PATHS ${OPEN_SRC_INSTALL_PREFIX}/bin - NO_DEFAULT_PATH) - else() - set(lib_file_name ${lib_name}) - if (${lib_name} STREQUAL "openssl") - set(lib_file_name ssl) - endif() + set(lib_file_name ${lib_name}) - find_library( - target_found + find_library(target_found NAMES ${lib_file_name} PATHS ${OPEN_SRC_INSTALL_PREFIX}/lib NO_DEFAULT_PATH) - endif() if(target_found) message(STATUS "${lib_name} already built") diff --git a/CMakeLists.txt b/CMakeLists.txt index ec001a7b..f5f8272b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -3,20 +3,24 @@ set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/CMake;${CMAKE_MODULE_PATH}") include(Utilities) project(KinesisVideoProducerCpp) -project(KinesisVideoProducerCpp VERSION 3.4.1) +project(KinesisVideoProducerCpp VERSION 3.4.2) set(CMAKE_CXX_STANDARD 11) include(GNUInstallDirs) +set(KINESIS_VIDEO_PRODUCER_CPP_MAJOR_VERSION 3) +set(KINESIS_VIDEO_PRODUCER_CPP_MINOR_VERSION 4) +set(KINESIS_VIDEO_PRODUCER_CPP_PATCH_VERSION 2) +set(KINESIS_VIDEO_PRODUCER_CPP_VERSION ${KINESIS_VIDEO_PRODUCER_CPP_MAJOR_VERSION}.${KINESIS_VIDEO_PRODUCER_CPP_MINOR_VERSION}.${KINESIS_VIDEO_PRODUCER_CPP_PATCH_VERSION}) + # User Flags option(BUILD_GSTREAMER_PLUGIN "Build kvssink GStreamer plugin" OFF) option(BUILD_JNI "Build C++ wrapper for JNI to expose the functionality to Java/Android" OFF) option(BUILD_STATIC "Build with static linkage" OFF) option(ADD_MUCLIBC "Add -muclibc c flag" OFF) option(BUILD_DEPENDENCIES "Whether or not to build depending libraries from source" ON) -option(BUILD_OPENSSL_PLATFORM "If buildng OpenSSL what is the target platform" OFF) option(BUILD_LOG4CPLUS_HOST "Specify host-name for log4cplus for cross-compilation" OFF) - +option(CONSTRAINED_DEVICE "Set thread stack size to 0.5MB" OFF) # Developer Flags option(BUILD_TEST "Build the testing tree" OFF) @@ -27,7 +31,7 @@ option(MEMORY_SANITIZER "Build with MemorySanitizer" OFF) option(THREAD_SANITIZER "Build with ThreadSanitizer" OFF) option(UNDEFINED_BEHAVIOR_SANITIZER "Build with UndefinedBehaviorSanitizer" OFF) -add_definitions(-DCPP_VERSION_STRING=\"${PROJECT_VERSION}\") +add_definitions(-DCPP_VERSION_STRING=\"${KINESIS_VIDEO_PRODUCER_CPP_VERSION}\") set(CMAKE_MACOSX_RPATH TRUE) get_filename_component(ROOT "${CMAKE_CURRENT_SOURCE_DIR}" ABSOLUTE) @@ -56,6 +60,10 @@ endif() add_definitions(-DKVS_CA_CERT_PATH="${CMAKE_CURRENT_SOURCE_DIR}/certs/cert.pem") add_definitions(-DCMAKE_DETECTED_CACERT_PATH) +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 EXISTS ${KINESIS_VIDEO_OPEN_SOURCE_SRC}) @@ -73,11 +81,6 @@ if(BUILD_DEPENDENCIES) message(STATUS "Begin building dependencies.") - if (NOT WIN32) - build_dependency(autoconf) - build_dependency(automake) - endif() - if(BUILD_LOG4CPLUS_HOST) set(BUILD_ARGS -DBUILD_LOG4CPLUS_HOST=${BUILD_LOG4CPLUS_HOST}) build_dependency(log4cplus ${BUILD_ARGS} -DBUILD_STATIC=${BUILD_STATIC}) @@ -88,46 +91,42 @@ if(BUILD_DEPENDENCIES) message(STATUS "Finished building dependencies.") endif() -set(BUILD_COMMON_LWS - FALSE - CACHE BOOL "Build ProducerC without LWS Support" FORCE) -set(BUILD_COMMON_CURL - TRUE - CACHE BOOL "Build ProducerC with CURL Support" FORCE) -set(DEPENDENCY_DOWNLOAD_PATH ${CMAKE_CURRENT_SOURCE_DIR}/dependency) -if(NOT EXISTS ${DEPENDENCY_DOWNLOAD_PATH}) - file(MAKE_DIRECTORY ${DEPENDENCY_DOWNLOAD_PATH}) -endif() -fetch_repo(kvscproducer) -add_subdirectory(${DEPENDENCY_DOWNLOAD_PATH}/libkvscproducer/kvscproducer-src EXCLUDE_FROM_ALL) - ############# find dependent libraries ############ find_package(Threads) find_package(PkgConfig REQUIRED) -if (OPEN_SRC_INSTALL_PREFIX) - find_package(CURL REQUIRED PATHS ${OPEN_SRC_INSTALL_PREFIX}) +pkg_check_modules(KVSCPRODUCER libcproducer) +if(KVSCPRODUCER_FOUND) + set(OPEN_SRC_INCLUDE_DIRS ${OPEN_SRC_INCLUDE_DIRS} ${KVSCPRODUCER_INCLUDE_DIRS}) + link_directories(${KVSCPRODUCER_LIBRARY_DIRS}) else() - find_package(CURL REQUIRED) + set(DEPENDENCY_DOWNLOAD_PATH ${CMAKE_CURRENT_SOURCE_DIR}/dependency) + if(NOT EXISTS ${DEPENDENCY_DOWNLOAD_PATH}) + file(MAKE_DIRECTORY ${DEPENDENCY_DOWNLOAD_PATH}) + endif() + fetch_repo(kvscproducer) + add_subdirectory(${DEPENDENCY_DOWNLOAD_PATH}/libkvscproducer/kvscproducer-src EXCLUDE_FROM_ALL) endif() -set(OPEN_SRC_INCLUDE_DIRS ${OPEN_SRC_INCLUDE_DIRS} ${CURL_INCLUDE_DIRS}) -link_directories(${CURL_LIBRARY_DIRS}) - if (WIN32) find_package(Log4cplus NAMES log4cplus REQUIRED PATHS ${OPEN_SRC_INSTALL_PREFIX}/lib) - SET(Log4cplus "log4cplus::log4cplus") + set(LOG4CPLUS_LIBRARIES "log4cplus::log4cplus") else() find_package(Log4cplus REQUIRED) - include_directories(${LOG4CPLUS_INCLUDE_DIR}) - set(Log4cplus ${LOG4CPLUS_LIBRARIES}) endif() +message("LOG4CPLUS_INCLUDE_DIR is ${LOG4CPLUS_INCLUDE_DIR}") +message("LOG4CPLUS_LIBRARIES is ${LOG4CPLUS_LIBRARIES}") + if (WIN32) - set(PKG_CONFIG_EXECUTABLE "C:\\gstreamer\\1.0\\x86_64\\bin\\pkg-config.exe") + if(EXISTS "C:\\gstreamer\\1.0\\x86_64\\bin\\pkg-config.exe") + set(PKG_CONFIG_EXECUTABLE "C:\\gstreamer\\1.0\\x86_64\\bin\\pkg-config.exe") + else() + set(PKG_CONFIG_EXECUTABLE "D:\\gstreamer\\1.0\\x86_64\\bin\\pkg-config.exe") + endif() endif() ############# Enable Sanitizers ############ @@ -178,17 +177,34 @@ include_directories(${KINESIS_VIDEO_PRODUCER_CPP_SRC}/src/credential-providers) include_directories(${KINESIS_VIDEO_PRODUCER_CPP_SRC}/src/common) include_directories(${KINESIS_VIDEO_PRODUCER_CPP_SRC}/src/JNI/include) +include_directories(${LOG4CPLUS_INCLUDE_DIR}) + install( DIRECTORY ${KINESIS_VIDEO_PRODUCER_CPP_SRC}/src DESTINATION .) +link_directories(${OPEN_SRC_INSTALL_PREFIX}/lib) + add_library(KinesisVideoProducer ${LINKAGE} ${PRODUCER_CPP_SOURCE_FILES}) +if(NOT BUILD_STATIC) + set_target_properties(KinesisVideoProducer PROPERTIES VERSION ${KINESIS_VIDEO_PRODUCER_CPP_VERSION} SOVERSION ${KINESIS_VIDEO_PRODUCER_CPP_MAJOR_VERSION}) +endif() target_link_libraries( KinesisVideoProducer - PUBLIC kvsCommonCurl - cproducer - ${Log4cplus} - ${LIBCURL_LIBRARIES}) + PUBLIC cproducer + ${LOG4CPLUS_LIBRARIES}) + +install( + TARGETS KinesisVideoProducer + ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}" + LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}" + RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}") + +install( + TARGETS cproducer + ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}" + LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}" + RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}") install( TARGETS KinesisVideoProducer @@ -199,9 +215,21 @@ install( if(BUILD_JNI) find_package(JNI REQUIRED) include_directories(${JNI_INCLUDE_DIRS}) - + + install( + DIRECTORY ${KINESIS_VIDEO_PRODUCER_CPP_SRC}/src/JNI/include + DESTINATION .) add_library(KinesisVideoProducerJNI SHARED ${JNI_HEADERS} ${JNI_SOURCE_FILES}) + if(NOT BUILD_STATIC) + set_target_properties(KinesisVideoProducerJNI PROPERTIES VERSION ${KINESIS_VIDEO_PRODUCER_CPP_VERSION} SOVERSION ${KINESIS_VIDEO_PRODUCER_CPP_MAJOR_VERSION}) + endif() target_link_libraries(KinesisVideoProducerJNI kvspic) + + install( + TARGETS KinesisVideoProducerJNI + ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}" + LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}" + RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}") endif() @@ -209,28 +237,36 @@ if(BUILD_GSTREAMER_PLUGIN) pkg_check_modules(GST_APP REQUIRED gstreamer-app-1.0) include_directories(${GST_APP_INCLUDE_DIRS}) link_directories(${GST_APP_LIBRARY_DIRS}) + include_directories(${OPEN_SRC_INSTALL_PREFIX}/include) if(BUILD_STATIC) add_library(gstkvssink STATIC ${GST_PLUGIN_SOURCE_FILES}) else() add_library(gstkvssink MODULE ${GST_PLUGIN_SOURCE_FILES}) endif() - target_link_libraries(gstkvssink PRIVATE ${GST_APP_LIBRARIES} KinesisVideoProducer) + target_link_libraries(gstkvssink PRIVATE ${LOG4CPLUS_LIBRARIES} ${GST_APP_LIBRARIES} KinesisVideoProducer) add_executable(kvssink_gstreamer_sample samples/kvssink_gstreamer_sample.cpp) - target_link_libraries(kvssink_gstreamer_sample ${GST_APP_LIBRARIES} KinesisVideoProducer) + target_link_libraries(kvssink_gstreamer_sample ${LOG4CPLUS_LIBRARIES} ${GST_APP_LIBRARIES} kvspic) add_executable(kvs_gstreamer_sample samples/kvs_gstreamer_sample.cpp) - target_link_libraries(kvs_gstreamer_sample ${GST_APP_LIBRARIES} KinesisVideoProducer kvspic) + target_link_libraries(kvs_gstreamer_sample ${LOG4CPLUS_LIBRARIES} ${GST_APP_LIBRARIES} KinesisVideoProducer kvspic) add_executable(kvs_gstreamer_multistream_sample samples/kvs_gstreamer_multistream_sample.cpp) - target_link_libraries(kvs_gstreamer_multistream_sample ${GST_APP_LIBRARIES} KinesisVideoProducer) + target_link_libraries(kvs_gstreamer_multistream_sample ${LOG4CPLUS_LIBRARIES} ${GST_APP_LIBRARIES} KinesisVideoProducer) add_executable(kvs_gstreamer_audio_video_sample samples/kvs_gstreamer_audio_video_sample.cpp) - target_link_libraries(kvs_gstreamer_audio_video_sample ${GST_APP_LIBRARIES} KinesisVideoProducer) + target_link_libraries(kvs_gstreamer_audio_video_sample ${LOG4CPLUS_LIBRARIES} ${GST_APP_LIBRARIES} KinesisVideoProducer) add_executable(kvs_gstreamer_file_uploader_sample samples/kvs_gstreamer_file_uploader_sample.cpp) - target_link_libraries(kvs_gstreamer_file_uploader_sample ${GST_APP_LIBRARIES}) + target_link_libraries(kvs_gstreamer_file_uploader_sample ${LOG4CPLUS_LIBRARIES} ${GST_APP_LIBRARIES}) + + install( + TARGETS gstkvssink + ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}" + LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}" + RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}") + endif() if(BUILD_TEST) diff --git a/README.md b/README.md index 473caa5e..f2768e2a 100644 --- a/README.md +++ b/README.md @@ -30,7 +30,9 @@ Amazon Kinesis Video Streams Producer SDK for C/C++ makes it easy to build an on ### Download To download run the following command: -`git clone https://github.com/awslabs/amazon-kinesis-video-streams-producer-sdk-cpp.git` +```bash +git clone https://github.com/awslabs/amazon-kinesis-video-streams-producer-sdk-cpp.git +``` Note: You will also need to install `pkg-config`, `CMake`, `m4` and a build enviroment. If you are building the GStreamer plugin you will also need GStreamer and GStreamer (Development Libraries). @@ -40,7 +42,7 @@ Refer to the [FAQ](#FAQ) for platform specific instructions. Prepare a build directory in the newly checked out repository: -``` +```bash mkdir -p amazon-kinesis-video-streams-producer-sdk-cpp/build cd amazon-kinesis-video-streams-producer-sdk-cpp/build ``` @@ -54,12 +56,12 @@ By default we download all the libraries from GitHub and build them locally, so If you do wish to link to existing libraries you can do `cmake .. -DBUILD_DEPENDENCIES=OFF` Libraries needed to build producer are: Curl, Openssl and Log4cplus. If you want to build the gstreamer plugin you will need to have gstreamer in your system. On Mac OS you can get the libraries using homebrew -``` -$ brew install pkg-config openssl cmake gstreamer gst-plugins-base gst-plugins-good gst-plugins-bad gst-plugins-ugly log4cplus gst-libav +```bash +brew install pkg-config openssl cmake gstreamer gst-plugins-base gst-plugins-good gst-plugins-bad gst-plugins-ugly log4cplus gst-libav ``` On Ubuntu and Raspberry Pi OS you can get the libraries by running -``` -$ sudo apt-get install libssl-dev libcurl4-openssl-dev liblog4cplus-dev libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev gstreamer1.0-plugins-base-apps gstreamer1.0-plugins-bad gstreamer1.0-plugins-good gstreamer1.0-plugins-ugly gstreamer1.0-tools +```bash +sudo apt-get install libssl-dev libcurl4-openssl-dev liblog4cplus-dev libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev gstreamer1.0-plugins-base-apps gstreamer1.0-plugins-bad gstreamer1.0-plugins-good gstreamer1.0-plugins-ugly gstreamer1.0-tools ``` ### Setup desired log level: Set up the desired log level. The log levels currently available with `log4cplus` are: @@ -74,6 +76,10 @@ To set a log level, update the log level value [here](https://github.com/awslabs Note: The default log level is `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 TRACE 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. + #### Cross-Compilation If you wish to cross-compile `CC` and `CXX` are respected when building the library and all its dependencies. See our [ci.yml](https://github.com/awslabs/amazon-kinesis-video-streams-producer-sdk-cpp/blob/develop/.github/workflows/ci.yml) for an example of this. Every commit is cross compiled to ensure that it continues to work. Please note that GStreamer is not cross-compiled as a part of the cross-compilation of the KVS-SDK, customers will have to cross-compile it separately. @@ -94,12 +100,13 @@ 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. * `-DBUILD_LOG4CPLUS_HOST` Specify host-name for log4cplus for cross-compilation. Default is OFF. +* `-DCONSTRAINED_DEVICE` Set the thread stack size to 0.5MB, needed for Alpine builds #### To Include JNI JNI examples are NOT built by default. If you wish to build JNI you MUST add `-DBUILD_JNI=TRUE` when running `cmake`: -``` +```bash cmake -DBUILD_JNI=TRUE ``` @@ -107,14 +114,14 @@ cmake -DBUILD_JNI=TRUE The GStreamer plugin and samples are NOT built by default. If you wish to build them you MUST add `-DBUILD_GSTREAMER_PLUGIN=TRUE` when running cmake: -``` +```bash cmake -DBUILD_GSTREAMER_PLUGIN=TRUE .. ``` ### Compiling After running cmake, in the same build directory run `make`: -``` +```bash make ``` @@ -132,14 +139,14 @@ The GStreamer plugin is located in your `build` directory. To load this plugin set the following environment variables. This should be run from the root of the repo, NOT the `build` directory. -``` +```bash export GST_PLUGIN_PATH=`pwd`/build export LD_LIBRARY_PATH=`pwd`/open-source/local/lib ``` The equivalent for Windows is -``` +```bat set GST_PLUGIN_PATH=%CD%\build set PATH=%PATH%;%CD%\open-source\local\bin;%CD%\open-source\local\lib ``` @@ -196,7 +203,7 @@ both -- for both The events will start on the 2nd key frame, and will reoccur every 200 key frames. If you would to change this frequence you can edit the sample. #### To run from a file -in the kvs_gstreamer_audio_video_sample.cpp if you would like to upload from a file, include the option flag -f +In the kvs_gstreamer_audio_video_sample.cpp if you would like to upload from a file, include the option flag -f ## Running in offline mode By default, the samples run in near realtime mode. To set offline mode, set streamInfo.streamCaps.streamingType to `STREAMING_TYPE_OFFLINE`, where, `streamInfo` is of type `StreamInfo`, `streamCaps` is of type `StreamCaps` and `streamingType` is of type `STREAMING_TYPE`. @@ -205,10 +212,11 @@ By default, the samples run in near realtime mode. To set offline mode, set stre * The sample docker scripts for RTSP plugin, raspberry pi and linux can be found in the [Kinesis demos repository](https://github.com/aws-samples/amazon-kinesis-video-streams-demos/tree/master/producer-cpp). ## DEBUG -* When building the JNI, if you run into a cmake error `Could NOT find JNI (missing: JAVA_INCLUDE_PATH JAVA_INCLUDE_PATH2 JAVA_AWT_INCLUDE_PATH)`, make sure your environment variables are set correctly: +* When building the JNI, if you run into a cmake error `Could NOT find JNI (missing: JAVA_INCLUDE_PATH JAVA_INCLUDE_PATH2 JAVA_AWT_INCLUDE_PATH)`, make sure Java is installed and your environment variables are set correctly: `export JAVA_INCLUDE_PATH2=/Library/Java/JavaVirtualMachines//Contents/Home/include` or `export JAVA_INCLUDE_PATH2=$JAVA_HOME/include` for Mac OS. `export JAVA_INCLUDE_PATH2='/usr/java//include'` for Linux. * If you are successfully streaming but run into issue with playback. You can do `export KVS_DEBUG_DUMP_DATA_FILE_DIR=/path/to/directory` before streaming. Producer will then dump MKV files into that path. The file is exactly what KVS will receive. You can use [MKVToolNIX](https://mkvtoolnix.download/index.html) to check that everything looks correct. You can also try to play the MKV file in compatible players. +* If you are running into issues building libcurl on M1 Mac, you can try `brew unlink openssl`. * If you would like to visualize the GStreamer pipeline being constructed in a GStreamer application, include the following after the elements have been linked: `GST_DEBUG_BIN_TO_DOT_FILE(, GST_DEBUG_GRAPH_SHOW_ALL, );` For example, if the application created a pipeline object `GstPipeline* pipeline = gst_pipeline_new("test-pipeline")`, and you would like to see the visualized pipeline with filename pipeline, add: diff --git a/docs/linux.md b/docs/linux.md index 0d148fa1..9be834cd 100644 --- a/docs/linux.md +++ b/docs/linux.md @@ -9,10 +9,18 @@ Define AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY environment variables with the AWS access key id and secret key: ``` -$ export AWS_ACCESS_KEY_ID=YourAccessKeyId -$ export AWS_SECRET_ACCESS_KEY=YourSecretAccessKey +export AWS_ACCESS_KEY_ID=YourAccessKeyId +export AWS_SECRET_ACCESS_KEY=YourSecretAccessKey ``` -optionally, set `AWS_SESSION_TOKEN` if integrating with temporary token and `AWS_DEFAULT_REGION` for the region other than `us-west-2` +optionally, set `AWS_SESSION_TOKEN` if integrating with temporary token. + +#### Setting region + +If using kvssink, the region can be set in 2 ways: +1. Set `AWS_DEFAULT_REGION` to the desired region, or, +2. Set the `aws-region` property. + +If `aws-region` and `AWS_DEFAULT_REGION` are set, the `aws-region` property would be used instead of the env. ###### Discovering audio and video devices available in your system. Run the `gst-device-monitor-1.0` command to identify available media devices in your system. An example output as follows: @@ -47,24 +55,35 @@ Device found: ###### Running the `gst-launch-1.0` command to start streaming from a RTSP camera source. ``` -$ gst-launch-1.0 -v rtspsrc location=rtsp://YourCameraRtspUrl short-header=TRUE ! rtph264depay ! h264parse ! kvssink stream-name=YourStreamName storage-size=128 +gst-launch-1.0 -v rtspsrc location=rtsp://YourCameraRtspUrl short-header=TRUE ! rtph264depay ! h264parse ! kvssink stream-name=YourStreamName storage-size=128 ``` **Note:** If you are using **IoT credentials** then you can pass them as parameters to the gst-launch-1.0 command ``` -$ gst-launch-1.0 -v rtspsrc location="rtsp://YourCameraRtspUrl" short-header=TRUE ! rtph264depay ! h264parse ! kvssink stream-name="iot-stream" iot-certificate="iot-certificate,endpoint=endpoint,cert-path=/path/to/certificate,key-path=/path/to/private/key,ca-path=/path/to/ca-cert,role-aliases=role-aliases" +gst-launch-1.0 -v rtspsrc location="rtsp://YourCameraRtspUrl" short-header=TRUE ! rtph264depay ! h264parse ! kvssink stream-name="iot-stream" iot-certificate="iot-certificate,endpoint=endpoint,cert-path=/path/to/certificate,key-path=/path/to/private/key,ca-path=/path/to/ca-cert,role-aliases=role-aliases" ``` You can find the RTSP URL from your IP camera manual or manufacturers product page. For more information on how to set up IoT/role policies and role-aliases, please refer to [iot-based-credential-provider](auth.md#iot-based-credential-provider) and https://docs.aws.amazon.com/kinesisvideostreams/latest/dg/how-iot.html. ###### Running the `gst-launch-1.0` command to start streaming from USB camera source in **Ubuntu**. ``` -$ gst-launch-1.0 -v v4l2src device=/dev/video0 ! videoconvert ! video/x-raw,format=I420,width=640,height=480,framerate=30/1 ! x264enc bframes=0 key-int-max=45 bitrate=500 tune=zerolatency ! video/x-h264,stream-format=avc,alignment=au ! kvssink stream-name=YourStreamName storage-size=128 access-key="YourAccessKey" secret-key="YourSecretKey" +gst-launch-1.0 -v v4l2src device=/dev/video0 ! videoconvert ! video/x-raw,format=I420,width=640,height=480,framerate=30/1 ! x264enc bframes=0 key-int-max=45 bitrate=500 tune=zerolatency ! video/x-h264,stream-format=avc,alignment=au ! kvssink stream-name=YourStreamName storage-size=128 access-key="YourAccessKey" secret-key="YourSecretKey" ``` ###### Running the `gst-launch-1.0` command to start streaming from USB camera source which has h264 encoded stream already: ``` -$ gst-launch-1.0 -v v4l2src device=/dev/video0 ! h264parse ! video/x-h264,stream-format=avc,alignment=au ! kvssink stream-name=YourStreamName storage-size=128 access-key="YourAccessKey" secret-key="YourSecretKey" +gst-launch-1.0 -v v4l2src device=/dev/video0 ! h264parse ! video/x-h264,stream-format=avc,alignment=au ! kvssink stream-name=YourStreamName storage-size=128 access-key="YourAccessKey" secret-key="YourSecretKey" +``` + +###### Running the `gst-launch-1.0` command to upload video from HLS .m3u8 playlist. + +``` +gst-launch-1.0 -v souphttpsrc location="https://.../playlist.m3u8" ! hlsdemux ! tsdemux ! video/x-h264,format=avc,alignment=au ! h264parse ! kvssink stream-name="YourStreamName aws-region="YourRegion" ``` +###### Running the `gst-launch-1.0` command to upload audio and video from HLS .m3u8 playlist. + +``` +gst-launch-1.0 -v souphttpsrc location="https://.../playlist.m3u8" ! hlsdemux name=mux ! tsdemux ! video/x-h264,format=avc,alignment=au ! h264parse ! kvssink stream-name="YourStreamName" aws-region="YourRegion" streaming-type=offline name=sink mux. ! queue ! aacparse ! sink. +``` ###### Running the `gst-launch-1.0` command to start streaming both audio and video: @@ -102,10 +121,9 @@ gst-launch-1.0 -v v4l2src device=/dev/video0 ! h264parse ! video/x-h264,stream-f ##### Running the `gst-launch-1.0` command with Iot-certificate and different stream-names than the thing-name -**Note:** Supply a the matching iot-thing-name (that the certificate points to) and we can stream to multiple stream-names (without the stream-name needing to be the same as the thing-name) using the same certificate credentials. iot-thing-name and stream-name can be completely different as long as there is a policy that allows the thing to write to the kinesis stream +**Note:** Supply the matching iot-thing-name (that the certificate points to) and we can stream to multiple stream-names (without the stream-name needing to be the same as the thing-name) using the same certificate credentials. iot-thing-name and stream-name can be completely different as long as there is a policy that allows the thing to write to the kinesis stream ``` -$ gst-launch-1.0 -v rtspsrc location="rtsp://YourCameraRtspUrl" short-header=TRUE ! rtph264depay ! video/x-h264, format=avc,alignment=au ! - h264parse ! kvssink name=aname storage-size=512 iot-certificate="iot-certificate,endpoint=xxxxx.credentials.iot.ap-southeast-2.amazonaws.com,cert-path=/greengrass/v2/thingCert.crt,key-path=/greengrass/v2/privKey.key,ca-path=/greengrass/v2/rootCA.pem,role-aliases=KvsCameraIoTRoleAlias,iot-thing-name=myThingName123" aws-region="ap-southeast-2" log-config="/etc/mtdata/kvssink-log.config" stream-name=myThingName123-video1 +gst-launch-1.0 -v rtspsrc location="rtsp://YourCameraRtspUrl" short-header=TRUE ! rtph264depay ! video/x-h264, format=avc,alignment=au ! h264parse ! kvssink name=aname storage-size=512 iot-certificate="iot-certificate,endpoint=xxxxx.credentials.iot.ap-southeast-2.amazonaws.com,cert-path=/greengrass/v2/thingCert.crt,key-path=/greengrass/v2/privKey.key,ca-path=/greengrass/v2/rootCA.pem,role-aliases=KvsCameraIoTRoleAlias,iot-thing-name=myThingName123" aws-region="ap-southeast-2" log-config="/etc/mtdata/kvssink-log.config" stream-name=myThingName123-video1 ``` ##### Running the GStreamer webcam sample application @@ -181,8 +199,8 @@ For additional examples on using Kinesis Video Streams Java SDK and Kinesis Vid **Note:** Please set the credentials before running the unit tests: ``` -$ export AWS_ACCESS_KEY_ID=YourAccessKeyId -$ export AWS_SECRET_ACCESS_KEY=YourSecretAccessKey +export AWS_ACCESS_KEY_ID=YourAccessKeyId +export AWS_SECRET_ACCESS_KEY=YourSecretAccessKey optionally, set AWS_SESSION_TOKEN if integrating with temporary token and AWS_DEFAULT_REGION for the region other than us-west-2 ``` @@ -193,8 +211,8 @@ The executable for **unit tests** will be built as `./tst/producer_test` inside **Note:** Please set the credentials before running the unit tests: ``` -$ export AWS_ACCESS_KEY_ID=YourAccessKeyId -$ export AWS_SECRET_ACCESS_KEY=YourSecretAccessKey +export AWS_ACCESS_KEY_ID=YourAccessKeyId +export AWS_SECRET_ACCESS_KEY=YourSecretAccessKey optionally, set AWS_SESSION_TOKEN if integrating with temporary token and AWS_DEFAULT_REGION for the region other than us-west-2 ``` diff --git a/docs/macos.md b/docs/macos.md index 0bcd3c5c..cb8bb5b7 100644 --- a/docs/macos.md +++ b/docs/macos.md @@ -3,10 +3,17 @@ Define AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY environment variables with the AWS access key id and secret key: ``` -$ export AWS_ACCESS_KEY_ID=YourAccessKeyId -$ export AWS_SECRET_ACCESS_KEY=YourSecretAccessKey +export AWS_ACCESS_KEY_ID=YourAccessKeyId +export AWS_SECRET_ACCESS_KEY=YourSecretAccessKey ``` -optionally, set `AWS_SESSION_TOKEN` if integrating with temporary token and `AWS_DEFAULT_REGION` for the region other than `us-west-2` + +#### Setting region + +If using kvssink, the region can be set in 2 ways: +1. Set `AWS_DEFAULT_REGION` to the desired region, or, +2. Set the `aws-region` property. + +If `aws-region` and `AWS_DEFAULT_REGION` are set, the `aws-region` property would be used instead of the env. ###### Discovering available devices. Run the `gst-device-monitor-1.0` command to identify available media devices in your system. An example output as follows: @@ -25,20 +32,20 @@ Device found: ###### Running the `gst-launch-1.0` command to start streaming from RTSP camera source. ``` -$ gst-launch-1.0 rtspsrc location=rtsp://YourCameraRtspUrl short-header=TRUE ! rtph264depay ! h264parse ! kvssink stream-name=YourStreamName storage-size=128 access-key="YourAccessKey" secret-key="YourSecretKey" +gst-launch-1.0 rtspsrc location=rtsp://YourCameraRtspUrl short-header=TRUE ! rtph264depay ! h264parse ! kvssink stream-name=YourStreamName storage-size=128 access-key="YourAccessKey" secret-key="YourSecretKey" ``` **Note:** If you are using **IoT credentials** then you can pass them as parameters to the gst-launch-1.0 command ``` -$ gst-launch-1.0 rtspsrc location=rtsp://YourCameraRtspUrl short-header=TRUE ! rtph264depay ! h264parse ! kvssink stream-name="iot-stream" iot-certificate="iot-certificate,endpoint=endpoint,cert-path=/path/to/certificate,key-path=/path/to/private/key,ca-path=/path/to/ca-cert,role-aliases=role-aliases" +gst-launch-1.0 rtspsrc location=rtsp://YourCameraRtspUrl short-header=TRUE ! rtph264depay ! h264parse ! kvssink stream-name="iot-stream" iot-certificate="iot-certificate,endpoint=endpoint,cert-path=/path/to/certificate,key-path=/path/to/private/key,ca-path=/path/to/ca-cert,role-aliases=role-aliases" ``` You can find the RTSP URL from your IP camera manual or manufacturers product page. ###### Running the `gst-launch-1.0` command to start streaming from camera source in **Mac-OS**. ``` -$ gst-launch-1.0 autovideosrc ! videoconvert ! video/x-raw,format=I420,width=640,height=480,framerate=30/1 ! vtenc_h264_hw allow-frame-reordering=FALSE realtime=TRUE max-keyframe-interval=45 bitrate=500 ! h264parse ! video/x-h264,stream-format=avc,alignment=au,profile=baseline ! kvssink stream-name=YourStreamName storage-size=128 access-key="YourAccessKey" secret-key="YourSecretKey" +gst-launch-1.0 autovideosrc ! videoconvert ! video/x-raw,format=I420,width=640,height=480,framerate=30/1 ! vtenc_h264_hw allow-frame-reordering=FALSE realtime=TRUE max-keyframe-interval=45 bitrate=500 ! h264parse ! video/x-h264,stream-format=avc,alignment=au,profile=baseline ! kvssink stream-name=YourStreamName storage-size=128 access-key="YourAccessKey" secret-key="YourSecretKey" ``` ###### Running the `gst-launch-1.0` command to start streaming both audio and raw video in **Mac-OS**. @@ -63,8 +70,7 @@ gst-launch-1.0 -v avfvideosrc device-index=1 ! videoconvert ! vtenc_h264_hw allo **Note:** Supply a the matching iot-thing-name (that the certificate points to) and we can stream to multiple stream-names (without the stream-name needing to be the same as the thing-name) using the same certificate credentials. iot-thing-name and stream-name can be completely different as long as there is a policy that allows the thing to write to the kinesis stream ``` -$ gst-launch-1.0 -v rtspsrc location="rtsp://YourCameraRtspUrl" short-header=TRUE ! rtph264depay ! video/x-h264, format=avc,alignment=au ! - h264parse ! kvssink name=aname storage-size=512 iot-certificate="iot-certificate,endpoint=xxxxx.credentials.iot.ap-southeast-2.amazonaws.com,cert-path=/greengrass/v2/thingCert.crt,key-path=/greengrass/v2/privKey.key,ca-path=/greengrass/v2/rootCA.pem,role-aliases=KvsCameraIoTRoleAlias,iot-thing-name=myThingName123" aws-region="ap-southeast-2" log-config="/etc/mtdata/kvssink-log.config" stream-name=myThingName123-video1 +gst-launch-1.0 -v rtspsrc location="rtsp://YourCameraRtspUrl" short-header=TRUE ! rtph264depay ! video/x-h264, format=avc,alignment=au ! h264parse ! kvssink name=aname storage-size=512 iot-certificate="iot-certificate,endpoint=xxxxx.credentials.iot.ap-southeast-2.amazonaws.com,cert-path=/greengrass/v2/thingCert.crt,key-path=/greengrass/v2/privKey.key,ca-path=/greengrass/v2/rootCA.pem,role-aliases=KvsCameraIoTRoleAlias,iot-thing-name=myThingName123" aws-region="ap-southeast-2" log-config="/etc/mtdata/kvssink-log.config" stream-name=myThingName123-video1 ``` ##### Running the GStreamer webcam sample application @@ -120,6 +126,18 @@ gst-launch-1.0 -v filesrc location="YourAudioVideo.mp4" ! qtdemux name=demux ! gst-launch-1.0 -v filesrc location="YourAudioVideo.ts" ! tsdemux name=demux ! queue ! h264parse ! video/x-h264,stream-format=avc,alignment=au ! kvssink name=sink stream-name="audio-video-file" access-key="YourAccessKeyId" secret-key="YourSecretAccessKey" streaming-type=offline demux. ! queue ! aacparse ! sink. ``` +###### Running the `gst-launch-1.0` command to upload video from HLS .m3u8 playlist. + +``` +gst-launch-1.0 -v souphttpsrc location="https://.../playlist.m3u8" ! hlsdemux ! tsdemux ! video/x-h264,format=avc,alignment=au ! h264parse ! kvssink stream-name="YourStreamName" +``` + +###### Running the `gst-launch-1.0` command to upload audio and video from HLS .m3u8 playlist. + +``` +gst-launch-1.0 -v souphttpsrc location="https://.../playlist.m3u8" ! hlsdemux name=mux ! tsdemux ! video/x-h264,format=avc,alignment=au ! h264parse ! kvssink stream-name="YourStreamName" streaming-type=offline name=sink mux. ! queue ! aacparse ! sink. +``` + ##### Running the GStreamer sample application to upload a *audio and video* file `kvs_gstreamer_audio_video_sample` supports uploading a video that is either MKV, MPEGTS, or MP4. The sample application expects the video is encoded in H264 and audio is encoded in AAC format. Note: If your media uses a different format, then you can revise the pipeline elements in the sample application to suit your media format. @@ -127,7 +145,7 @@ gst-launch-1.0 -v filesrc location="YourAudioVideo.ts" ! tsdemux name=demux ! q Change your current working directory to `build`. Launch the sample application with a stream name and a path to the file and it will start streaming. ``` -AWS_ACCESS_KEY_ID=YourAccessKeyId AWS_SECRET_ACCESS_KEY=YourSecretAccessKey ./kvs_gstreamer_audio_video_sample +AWS_ACCESS_KEY_ID=YourAccessKeyId AWS_SECRET_ACCESS_KEY=YourSecretAccessKey AWS_DEFAULT_REGION=YourRegion ./kvs_gstreamer_audio_video_sample ``` ##### Running the GStreamer sample application to stream audio and video from live source @@ -159,8 +177,8 @@ For additional examples on using Kinesis Video Streams Java SDK and Kinesis Vid **Note:** Please set the credentials before running the unit tests: ``` -$ export AWS_ACCESS_KEY_ID=YourAccessKeyId -$ export AWS_SECRET_ACCESS_KEY=YourSecretAccessKey +export AWS_ACCESS_KEY_ID=YourAccessKeyId +export AWS_SECRET_ACCESS_KEY=YourSecretAccessKey optionally, set AWS_SESSION_TOKEN if integrating with temporary token and AWS_DEFAULT_REGION for the region other than us-west-2 ``` @@ -171,8 +189,8 @@ The executable for **unit tests** will be built as `./tst/producer_test` inside **Note:** Please set the credentials before running the unit tests: ``` -$ export AWS_ACCESS_KEY_ID=YourAccessKeyId -$ export AWS_SECRET_ACCESS_KEY=YourSecretAccessKey +export AWS_ACCESS_KEY_ID=YourAccessKeyId +export AWS_SECRET_ACCESS_KEY=YourSecretAccessKey optionally, set AWS_SESSION_TOKEN if integrating with temporary token and AWS_DEFAULT_REGION for the region other than us-west-2 ``` diff --git a/docs/raspberry-pi.md b/docs/raspberry-pi.md index a062a016..4767bfaf 100644 --- a/docs/raspberry-pi.md +++ b/docs/raspberry-pi.md @@ -28,14 +28,21 @@ Ensure you run `cmake` with `BUILD_GSTREAMER_PLUGIN=ON` option from build direct Define AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY environment variables with the AWS access key id and secret key: ``` -$ export AWS_ACCESS_KEY_ID=YourAccessKeyId -$ export AWS_SECRET_ACCESS_KEY=YourSecretAccessKey +export AWS_ACCESS_KEY_ID=YourAccessKeyId +export AWS_SECRET_ACCESS_KEY=YourSecretAccessKey ``` -optionally, set `AWS_SESSION_TOKEN` if integrating with temporary token and `AWS_DEFAULT_REGION` for the region other than `us-west-2` + +#### Setting region + +If using kvssink, the region can be set in 2 ways: +1. Set `AWS_DEFAULT_REGION` to the desired region, or, +2. Set the `aws-region` property. + +If `aws-region` and `AWS_DEFAULT_REGION` are set, the `aws-region` property would be used instead of the env. ##### Set GST_PLUGIN_PATH in environment variables ``` -$ export GST_PLUGIN_PATH=>SDK_FOLDER_PATH/build +export GST_PLUGIN_PATH=>SDK_FOLDER_PATH/build ``` ###### Discovering audio and video devices available in your system. @@ -71,28 +78,28 @@ Device found: ###### Running the `gst-launch-1.0` command to start streaming from a RTSP camera source. ``` -$ gst-launch-1.0 -v rtspsrc location=rtsp://YourCameraRtspUrl short-header=TRUE ! rtph264depay ! h264parse ! kvssink stream-name=YourStreamName storage-size=128 +gst-launch-1.0 -v rtspsrc location=rtsp://YourCameraRtspUrl short-header=TRUE ! rtph264depay ! h264parse ! kvssink stream-name=YourStreamName storage-size=128 ``` **Note:** If you are using **IoT credentials** then you can pass them as parameters to the gst-launch-1.0 command ``` -$ gst-launch-1.0 -v rtspsrc location="rtsp://YourCameraRtspUrl" short-header=TRUE ! rtph264depay ! h264parse ! kvssink stream-name="iot-stream" iot-certificate="iot-certificate,endpoint=endpoint,cert-path=/path/to/certificate,key-path=/path/to/private/key,ca-path=/path/to/ca-cert,role-aliases=role-aliases" +gst-launch-1.0 -v rtspsrc location="rtsp://YourCameraRtspUrl" short-header=TRUE ! rtph264depay ! h264parse ! kvssink stream-name="iot-stream" iot-certificate="iot-certificate,endpoint=endpoint,cert-path=/path/to/certificate,key-path=/path/to/private/key,ca-path=/path/to/ca-cert,role-aliases=role-aliases" ``` You can find the RTSP URL from your IP camera manual or manufacturers product page. ###### Running the `gst-launch-1.0` command to start streaming from USB camera source which has h264 encoded stream already: ``` -$ gst-launch-1.0 -v v4l2src device=/dev/video0 ! h264parse ! video/x-h264,stream-format=avc,alignment=au ! kvssink stream-name=YourStreamName storage-size=128 access-key="YourAccessKey" secret-key="YourSecretKey" +gst-launch-1.0 -v v4l2src device=/dev/video0 ! h264parse ! video/x-h264,stream-format=avc,alignment=au ! kvssink stream-name=YourStreamName storage-size=128 access-key="YourAccessKey" secret-key="YourSecretKey" ``` ###### Running the `gst-launch-1.0` command to start streaming from camera source: ``` -$ gst-launch-1.0 -v v4l2src do-timestamp=TRUE device=/dev/video0 ! videoconvert ! video/x-raw,format=I420,width=640,height=480,framerate=30/1 ! omxh264enc periodicty-idr=45 inline-header=FALSE ! h264parse ! video/x-h264,stream-format=avc,alignment=au ! kvssink stream-name=YourStreamName access-key="YourAccessKey" secret-key="YourSecretKey" +gst-launch-1.0 -v v4l2src do-timestamp=TRUE device=/dev/video0 ! videoconvert ! video/x-raw,format=I420,width=640,height=480,framerate=30/1 ! omxh264enc periodicty-idr=45 inline-header=FALSE ! h264parse ! video/x-h264,stream-format=avc,alignment=au ! kvssink stream-name=YourStreamName access-key="YourAccessKey" secret-key="YourSecretKey" ``` or use a different encoder ``` -$ gst-launch-1.0 -v v4l2src device=/dev/video0 ! videoconvert ! video/x-raw,format=I420,width=640,height=480,framerate=30/1 ! x264enc bframes=0 key-int-max=45 bitrate=500 tune=zerolatency ! video/x-h264,stream-format=avc,alignment=au ! kvssink stream-name=YourStreamName storage-size=128 access-key="YourAccessKey" secret-key="YourSecretKey" +gst-launch-1.0 -v v4l2src device=/dev/video0 ! videoconvert ! video/x-raw,format=I420,width=640,height=480,framerate=30/1 ! x264enc bframes=0 key-int-max=45 bitrate=500 tune=zerolatency ! video/x-h264,stream-format=avc,alignment=au ! kvssink stream-name=YourStreamName storage-size=128 access-key="YourAccessKey" secret-key="YourSecretKey" ``` **Note:** If you are using **Raspberry PI with Bullseye** you have to use another encoder as well as `libcamerasrc` instead of `v4l2src device=/dev/video0` @@ -182,8 +189,7 @@ gst-launch-1.0 -v filesrc location="YourAudioVideo.ts" ! tsdemux name=demux ! q **Note:** Supply a the matching iot-thing-name (that the certificate points to) and we can stream to multiple stream-names (without the stream-name needing to be the same as the thing-name) using the same certificate credentials. iot-thing-name and stream-name can be completely different as long as there is a policy that allows the thing to write to the kinesis stream ``` -$ gst-launch-1.0 -v rtspsrc location="rtsp://YourCameraRtspUrl" short-header=TRUE ! rtph264depay ! video/x-h264, format=avc,alignment=au ! - h264parse ! kvssink name=aname storage-size=512 iot-certificate="iot-certificate,endpoint=xxxxx.credentials.iot.ap-southeast-2.amazonaws.com,cert-path=/greengrass/v2/thingCert.crt,key-path=/greengrass/v2/privKey.key,ca-path=/greengrass/v2/rootCA.pem,role-aliases=KvsCameraIoTRoleAlias,iot-thing-name=myThingName123" aws-region="ap-southeast-2" log-config="/etc/mtdata/kvssink-log.config" stream-name=myThingName123-video1 +gst-launch-1.0 -v rtspsrc location="rtsp://YourCameraRtspUrl" short-header=TRUE ! rtph264depay ! video/x-h264, format=avc,alignment=au ! h264parse ! kvssink name=aname storage-size=512 iot-certificate="iot-certificate,endpoint=xxxxx.credentials.iot.ap-southeast-2.amazonaws.com,cert-path=/greengrass/v2/thingCert.crt,key-path=/greengrass/v2/privKey.key,ca-path=/greengrass/v2/rootCA.pem,role-aliases=KvsCameraIoTRoleAlias,iot-thing-name=myThingName123" aws-region="ap-southeast-2" log-config="/etc/mtdata/kvssink-log.config" stream-name=myThingName123-video1 ``` ##### Running the GStreamer sample application to upload a *audio and video* file @@ -225,8 +231,8 @@ For additional examples on using Kinesis Video Streams Java SDK and Kinesis Vid **Note:** Please set the credentials before running the unit tests: ``` -$ export AWS_ACCESS_KEY_ID=YourAccessKeyId -$ export AWS_SECRET_ACCESS_KEY=YourSecretAccessKey +export AWS_ACCESS_KEY_ID=YourAccessKeyId +export AWS_SECRET_ACCESS_KEY=YourSecretAccessKey optionally, set AWS_SESSION_TOKEN if integrating with temporary token and AWS_DEFAULT_REGION for the region other than us-west-2 ``` @@ -237,8 +243,8 @@ The executable for **unit tests** will be built as `./tst/producer_test` inside **Note:** Please set the credentials before running the unit tests: ``` -$ export AWS_ACCESS_KEY_ID=YourAccessKeyId -$ export AWS_SECRET_ACCESS_KEY=YourSecretAccessKey +export AWS_ACCESS_KEY_ID=YourAccessKeyId +export AWS_SECRET_ACCESS_KEY=YourSecretAccessKey optionally, set AWS_SESSION_TOKEN if integrating with temporary token and AWS_DEFAULT_REGION for the region other than us-west-2 ``` @@ -298,11 +304,11 @@ The projects depend on the following open source components. Running `CMake` wil ##### Raspberry PI failure to load the camera device. To check this is the case run `ls /dev/video*` - it should be file not found. The remedy is to run the following: ``` -$ls /dev/video* +ls /dev/video* {not found} ``` ``` -$vcgencmd get_camera +vcgencmd get_camera ``` Example output: ``` @@ -311,20 +317,20 @@ supported=1 detected=1 if the driver does not detect the camera then * Check for the camera setup and whether it's connected properly -* Run firmware update `$ sudo rpi-update` and restart +* Run firmware update `sudo rpi-update` and restart ``` -$sudo modprobe bcm2835-v4l2 +sudo modprobe bcm2835-v4l2 ``` ``` -$ls /dev/video* +ls /dev/video* {lists the device} ``` ##### Raspberry PI timestamp/range assertion at runtime. Update the Raspberry PI firmware. ``` -$ sudo rpi-update -$ sudo reboot +sudo rpi-update +sudo reboot ``` * Raspberry PI GStreamer assertion on gst_value_set_fraction_range_full: assertion 'gst_util_fraction_compare (numerator_start, denominator_start, numerator_end, denominator_end) < 0' failed. The uv4l service running in the background. Kill the service and restart the sample app. diff --git a/docs/windows.md b/docs/windows.md index 8c710930..7f703ed9 100644 --- a/docs/windows.md +++ b/docs/windows.md @@ -53,6 +53,7 @@ Device found: wasapi.device.description = "Speakers\ \(Conexant\ ISST\ Audio\)" ``` + Start sample application to send video stream to KVS using gstreamer plugin by executing the following command: 1. Before running the demo applications, set the environment by following the instructions below. @@ -117,6 +118,12 @@ gst-launch-1.0 -v filesrc location="YourAudioVideo.ts" ! tsdemux name=demux ! q set AWS_SECRET_ACCESS_KEY=YourSecretAccessKey ``` + * If using kvssink, the region can be set in 2 ways: + 1. Set `AWS_DEFAULT_REGION` to the desired region, or, + 2. Set the `aws-region` property. + + If `aws-region` and `AWS_DEFAULT_REGION` are set, the `aws-region` property would be used instead of the env. + * Run the demo * **Example**: * Run the sample demo application for sending **webcam video** by executing ` kvs_gstreamer_sample.exe my-test-stream ` or diff --git a/samples/kvssink_gstreamer_sample.cpp b/samples/kvssink_gstreamer_sample.cpp index 195254b6..ade28782 100644 --- a/samples/kvssink_gstreamer_sample.cpp +++ b/samples/kvssink_gstreamer_sample.cpp @@ -2,7 +2,6 @@ #include #include #include -#include "KinesisVideoProducer.h" #include #include #include @@ -58,12 +57,10 @@ typedef struct _CustomData { first_pts(GST_CLOCK_TIME_NONE), use_absolute_fragment_times(true), max_runtime(0) { - producer_start_time = chrono::duration_cast(systemCurrentTime().time_since_epoch()).count(); + producer_start_time = chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(); } GMainLoop *main_loop; - unique_ptr kinesis_video_producer; - shared_ptr kinesis_video_stream; bool stream_started; bool h264_stream_supported; char *stream_name; diff --git a/src/DefaultCallbackProvider.cpp b/src/DefaultCallbackProvider.cpp index a58d6c35..66a371d1 100644 --- a/src/DefaultCallbackProvider.cpp +++ b/src/DefaultCallbackProvider.cpp @@ -422,7 +422,7 @@ DefaultCallbackProvider::DefaultCallbackProvider( + region_ + CONTROL_PLANE_URI_POSTFIX; // If region is in CN, add CN region uri postfix - if(region_.rfind("cn-", 0) == 0) { + if (region_.rfind("cn-", 0) == 0) { control_plane_uri_ += ".cn"; } } diff --git a/src/KinesisVideoStream.cpp b/src/KinesisVideoStream.cpp index fc030629..21c6f36b 100644 --- a/src/KinesisVideoStream.cpp +++ b/src/KinesisVideoStream.cpp @@ -19,7 +19,7 @@ KinesisVideoStream::KinesisVideoStream(const KinesisVideoProducer& kinesis_video } } -bool KinesisVideoStream::putFrame(KinesisVideoFrame& frame) const { +STATUS KinesisVideoStream::putFrame(KinesisVideoFrame& frame) const { if (debug_dump_frame_info_) { LOG_DEBUG("[" << this->stream_name_ << "] pts: " << frame.presentationTs << ", dts: " << frame.decodingTs << ", duration: " << frame.duration << ", size: " << frame.size << ", trackId: " << frame.trackId << ", isKey: " << CHECK_FRAME_FLAG_KEY_FRAME(frame.flags)); @@ -29,7 +29,7 @@ bool KinesisVideoStream::putFrame(KinesisVideoFrame& frame) const { STATUS status = putKinesisVideoFrame(stream_handle_, &frame); if (STATUS_FAILED(status)) { LOG_ERROR("Put frame for " << this->stream_name_ << " failed with 0x" << std::hex << status); - return false; + return status; } // Print metrics on every key-frame @@ -60,9 +60,7 @@ bool KinesisVideoStream::putFrame(KinesisVideoFrame& frame) const { LOG_ERROR("Failed to get metrics. Error: " << err.what()); } } - - // Even if metrics fail, we do not want to return false for putFrame. We just log the error - return true; + return status; } bool KinesisVideoStream::start(const std::string& hexEncodedCodecPrivateData, uint64_t trackId) { diff --git a/src/KinesisVideoStream.h b/src/KinesisVideoStream.h index 294b109d..6f6590db 100644 --- a/src/KinesisVideoStream.h +++ b/src/KinesisVideoStream.h @@ -60,7 +60,7 @@ class KinesisVideoStream { * @param frame The frame to be packaged and streamed. * @return true if the encoder accepted the frame and false otherwise. */ - bool putFrame(KinesisVideoFrame& frame) const; + STATUS putFrame(KinesisVideoFrame& frame) const; /** * Gets the stream metrics. diff --git a/src/gstreamer/gstkvssink.cpp b/src/gstreamer/gstkvssink.cpp index 83b9a4b1..bc079028 100644 --- a/src/gstreamer/gstkvssink.cpp +++ b/src/gstreamer/gstkvssink.cpp @@ -105,7 +105,7 @@ GST_DEBUG_CATEGORY_STATIC (gst_kvs_sink_debug); #define DEFAULT_ACCESS_KEY "access_key" #define DEFAULT_SECRET_KEY "secret_key" #define DEFAULT_SESSION_TOKEN "session_token" -#define DEFAULT_REGION "us-west-2" +#define DEFAULT_REGION "" #define DEFAULT_ROTATION_PERIOD_SECONDS 3600 #define DEFAULT_LOG_FILE_PATH "../kvs_log_configuration" #define DEFAULT_STORAGE_SIZE_MB 128 @@ -318,12 +318,16 @@ void kinesis_video_producer_init(GstKvsSink *kvssink) session_token_str = string(kvssink->session_token); } - if (nullptr == (default_region = getenv(DEFAULT_REGION_ENV_VAR))) { - region_str = string(kvssink->aws_region); + if (IS_EMPTY_STRING(kvssink->aws_region)) { + default_region = GETENV(DEFAULT_REGION_ENV_VAR); + if (IS_NULL_OR_EMPTY_STRING(default_region)) { + LOG_AND_THROW("No region set. Either set non-empty value with env " << DEFAULT_REGION_ENV_VAR << " or set kvssink property aws-region"); + } else { + region_str = string(default_region); + } } else { - region_str = string(default_region); // Use env var if both property and env var are available. + region_str = string(kvssink->aws_region); } - unique_ptr credential_provider; if (kvssink->iot_certificate) { @@ -1213,14 +1217,17 @@ void create_kinesis_video_frame(Frame *frame, const nanoseconds &pts, const nano frame->trackId = static_cast(track_id); } -bool put_frame(shared_ptr data, void *frame_data, size_t len, const nanoseconds &pts, +STATUS +put_frame(std::shared_ptr data, void *frame_data, size_t len, const nanoseconds &pts, const nanoseconds &dts, FRAME_FLAGS flags, uint64_t track_id, uint32_t index) { + STATUS put_frame_status = STATUS_SUCCESS; Frame frame; + create_kinesis_video_frame(&frame, pts, dts, flags, frame_data, len, track_id, index); - bool ret = data->kinesis_video_stream->putFrame(frame); - if (data->get_metrics && ret) { - if (CHECK_FRAME_FLAG_KEY_FRAME(flags) || data->on_first_frame) { + put_frame_status = data->kinesis_video_stream->putFrame(frame); + if (data->get_metrics && STATUS_SUCCEEDED(put_frame_status)) { + if (CHECK_FRAME_FLAG_KEY_FRAME(flags) || data->on_first_frame){ KvsSinkMetric *kvs_sink_metric = new KvsSinkMetric(); kvs_sink_metric->stream_metrics = data->kinesis_video_stream->getMetrics(); kvs_sink_metric->client_metrics = data->kinesis_video_producer->getMetrics(); @@ -1231,7 +1238,7 @@ bool put_frame(shared_ptr data, void *frame_data, size_t len, delete kvs_sink_metric; } } - return ret; + return put_frame_status; } static GstFlowReturn @@ -1249,6 +1256,7 @@ gst_kvs_sink_handle_buffer (GstCollectPads * pads, uint64_t track_id; FRAME_FLAGS kinesis_video_flags = FRAME_FLAG_NONE; GstMapInfo info; + STATUS put_frame_status = STATUS_SUCCESS; info.data = NULL; // eos reached @@ -1309,7 +1317,8 @@ gst_kvs_sink_handle_buffer (GstCollectPads * pads, if (!data->use_original_pts) { buf->dts = 0; // if offline mode, i.e. streaming a file, the dts from gstreamer is undefined. buf->pts += data->pts_base; - } else { + } + else { buf->pts = buf->dts; } } else if (!GST_BUFFER_DTS_IS_VALID(buf)) { @@ -1345,24 +1354,24 @@ gst_kvs_sink_handle_buffer (GstCollectPads * pads, if (data->first_pts == GST_CLOCK_TIME_NONE) { data->first_pts = buf->pts; } - if (data->producer_start_time == GST_CLOCK_TIME_NONE) { data->producer_start_time = (uint64_t) chrono::duration_cast( systemCurrentTime().time_since_epoch()).count(); } - if (!data->use_original_pts) { buf->pts += data->producer_start_time - data->first_pts; - } else { + } + else { buf->pts = buf->dts; } } - put_frame(kvssink->data, info.data, info.size, - std::chrono::nanoseconds(buf->pts), - std::chrono::nanoseconds(buf->dts), kinesis_video_flags, track_id, data->frame_count); + put_frame_status = put_frame(data, info.data, info.size, + std::chrono::nanoseconds(buf->pts), + std::chrono::nanoseconds(buf->dts), kinesis_video_flags, track_id, data->frame_count); data->frame_count++; - } else { + } + else { LOG_WARN("GStreamer buffer is invalid for " << kvssink->stream_name); } @@ -1374,6 +1383,11 @@ gst_kvs_sink_handle_buffer (GstCollectPads * pads, if (buf != NULL) { gst_buffer_unref (buf); } + + if (STATUS_FAILED(put_frame_status)) { + GST_ELEMENT_WARNING (kvssink, RESOURCE, WRITE, (NULL), + ("put frame error occurred. Status: 0x%08x", put_frame_status)); + } return ret; } @@ -1573,12 +1587,35 @@ init_track_data(GstKvsSink *kvssink) { static GstStateChangeReturn gst_kvs_sink_change_state(GstElement *element, GstStateChange transition) { + /* + The below state transition cases are separated into two switch blocks: + one for upward (NULL->READY->PAUSED->PLAYING) transitions and one for + downward (PLAYING->PAUSED->READY->NULL) transitions. It is typically* necessary to + transition an element's parent class state after any of the element's upward + transitions but before any downward transitions. As per GStreamer documentation, + "this is necessary in order to safely handle concurrent access by multiple threads." + + https://gstreamer.freedesktop.org/documentation/plugin-development/basics/states. + html?gi-language=c#:~:text=Note%20that%20upwards,destroying%20allocated%20resources. + + * NOTE: The gst_collect_pads_stop call should be called before calling the parent + element state change function in the PAUSED_TO_READY state change to ensure + no pad is blocked and the element can finish streaming. + + https://gstreamer.freedesktop.org/documentation/base/gstcollectpads.html?gi- + language=c#:~:text=The%20gst_collect_pads_stop%20call%20should%20be%20called% + 20before%20calling%20the%20parent%20element%20state%20change%20function%20in% + 20the%20PAUSED_TO_READY%20state%20change%20to%20ensure%20no%20pad%20is%20bloc + ked%20and%20the%20element%20can%20finish%20streaming. + */ + GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS; GstKvsSink *kvssink = GST_KVS_SINK (element); auto data = kvssink->data; string err_msg = ""; ostringstream oss; + // Upward transitions switch (transition) { case GST_STATE_CHANGE_NULL_TO_READY: if (kvssink->log_config_path != NULL) { @@ -1610,10 +1647,25 @@ gst_kvs_sink_change_state(GstElement *element, GstStateChange transition) { case GST_STATE_CHANGE_READY_TO_PAUSED: gst_collect_pads_start (kvssink->collect); break; + + // (Downward Transition) gst_collect_pads_stop must be called prior to parent class PAUSED->READY transition. case GST_STATE_CHANGE_PAUSED_TO_READY: LOG_INFO("Stopping kvssink for " << kvssink->stream_name); - gst_collect_pads_stop (kvssink->collect); + gst_collect_pads_stop(kvssink->collect); + break; + default: + break; + } + + // Parent class transition + ret = GST_ELEMENT_CLASS (parent_class)->change_state(element, transition); + if (ret == GST_STATE_CHANGE_FAILURE) { + goto CleanUp; + } + // Downward transitions + switch (transition) { + case GST_STATE_CHANGE_PAUSED_TO_READY: // Need this check in case an EOS was received in the buffer handler and // stream was already stopped. Although stopSync() is an idempotent call, // we want to avoid an extra call @@ -1632,8 +1684,6 @@ gst_kvs_sink_change_state(GstElement *element, GstStateChange transition) { break; } - ret = GST_ELEMENT_CLASS (parent_class)->change_state(element, transition); - CleanUp: if (ret != GST_STATE_CHANGE_SUCCESS) { diff --git a/tst/ProducerApiTest.cpp b/tst/ProducerApiTest.cpp index 46cb9226..2246a243 100644 --- a/tst/ProducerApiTest.cpp +++ b/tst/ProducerApiTest.cpp @@ -114,11 +114,11 @@ PVOID ProducerTestBase::basicProducerRoutine(KinesisVideoStream* kinesis_video_s // Simulate EoFr first if (frame.index % 50 == 0 && frame.index != 0) { Frame eofr = EOFR_FRAME_INITIALIZER; - EXPECT_TRUE(kinesis_video_stream->putFrame(eofr)); + EXPECT_EQ(STATUS_SUCCESS, kinesis_video_stream->putFrame(eofr)); } #endif - EXPECT_TRUE(kinesis_video_stream->putFrame(frame)); + EXPECT_EQ(STATUS_SUCCESS, kinesis_video_stream->putFrame(frame)); // Sleep a while for non-offline modes if (streaming_type != STREAMING_TYPE_OFFLINE) { @@ -308,7 +308,7 @@ TEST_F(ProducerApiTest, create_produce_start_stop_stream) << ", Dts: " << frame.decodingTs << ", Pts: " << frame.presentationTs); - EXPECT_TRUE(kinesis_video_stream->putFrame(frame)); + EXPECT_EQ(STATUS_SUCCESS, kinesis_video_stream->putFrame(frame)); THREAD_SLEEP(frame_duration_); } @@ -374,7 +374,7 @@ TEST_F(ProducerApiTest, create_produce_start_stop_stream_endpoint_cached) << ", Dts: " << frame.decodingTs << ", Pts: " << frame.presentationTs); - EXPECT_TRUE(kinesis_video_stream->putFrame(frame)); + EXPECT_EQ(STATUS_SUCCESS, kinesis_video_stream->putFrame(frame)); THREAD_SLEEP(frame_duration_); } @@ -440,7 +440,7 @@ TEST_F(ProducerApiTest, create_produce_start_stop_stream_all_cached) << ", Dts: " << frame.decodingTs << ", Pts: " << frame.presentationTs); - EXPECT_TRUE(kinesis_video_stream->putFrame(frame)); + EXPECT_EQ(STATUS_SUCCESS, kinesis_video_stream->putFrame(frame)); THREAD_SLEEP(frame_duration_); } @@ -506,7 +506,7 @@ TEST_F(ProducerApiTest, create_produce_start_stop_reset_stream_endpoint_cached) << ", Dts: " << frame.decodingTs << ", Pts: " << frame.presentationTs); - EXPECT_TRUE(kinesis_video_stream->putFrame(frame)); + EXPECT_EQ(STATUS_SUCCESS, kinesis_video_stream->putFrame(frame)); THREAD_SLEEP(frame_duration_); } @@ -574,7 +574,7 @@ TEST_F(ProducerApiTest, create_produce_start_stop_reset_stream_all_cached) << ", Dts: " << frame.decodingTs << ", Pts: " << frame.presentationTs); - EXPECT_TRUE(kinesis_video_stream->putFrame(frame)); + EXPECT_EQ(STATUS_SUCCESS, kinesis_video_stream->putFrame(frame)); THREAD_SLEEP(frame_duration_); } diff --git a/tst/ProducerFunctionalityTest.cpp b/tst/ProducerFunctionalityTest.cpp index e62b8827..43c42be8 100644 --- a/tst/ProducerFunctionalityTest.cpp +++ b/tst/ProducerFunctionalityTest.cpp @@ -68,7 +68,7 @@ TEST_F(ProducerFunctionalityTest, offline_upload_limited_buffer_duration) { << ", Dts: " << frame.decodingTs << ", Pts: " << frame.presentationTs); - EXPECT_TRUE(kinesis_video_stream->putFrame(frame)); + EXPECT_EQ(STATUS_SUCCESS, kinesis_video_stream->putFrame(frame)); timestamp += frame_duration_; } @@ -127,7 +127,7 @@ TEST_F(ProducerFunctionalityTest, offline_upload_limited_storage) { << ", Dts: " << frame.decodingTs << ", Pts: " << frame.presentationTs); - EXPECT_TRUE(kinesis_video_stream->putFrame(frame)); + EXPECT_EQ(STATUS_SUCCESS, kinesis_video_stream->putFrame(frame)); timestamp += frame_duration_; } @@ -192,7 +192,7 @@ TEST_F(ProducerFunctionalityTest, intermittent_file_upload) { << ", Dts: " << frame.decodingTs << ", Pts: " << frame.presentationTs); - EXPECT_TRUE(kinesis_video_stream->putFrame(frame)); + EXPECT_EQ(STATUS_SUCCESS, kinesis_video_stream->putFrame(frame)); timestamp += frame_duration_; // pause at the last frame of each clip @@ -257,7 +257,7 @@ TEST_F(ProducerFunctionalityTest, high_fragment_rate_file_upload) { << ", Dts: " << frame.decodingTs << ", Pts: " << frame.presentationTs); - EXPECT_TRUE(kinesis_video_stream->putFrame(frame)); + EXPECT_EQ(STATUS_SUCCESS, kinesis_video_stream->putFrame(frame)); timestamp += frame_duration_; } @@ -316,7 +316,7 @@ TEST_F(ProducerFunctionalityTest, offline_mode_token_rotation_block_on_space) { << ", Dts: " << frame.decodingTs << ", Pts: " << frame.presentationTs); - EXPECT_TRUE(kinesis_video_stream->putFrame(frame)); + EXPECT_EQ(STATUS_SUCCESS, kinesis_video_stream->putFrame(frame)); timestamp += frame_duration_; } @@ -376,7 +376,7 @@ TEST_F(ProducerFunctionalityTest, realtime_intermittent_no_latency_pressure_eofr // Pause on the 5th if (i == 5 * key_frame_interval_) { - EXPECT_TRUE(kinesis_video_stream->putFrame(eofr)); + EXPECT_EQ(STATUS_SUCCESS, kinesis_video_stream->putFrame(eofr)); // Make sure we hit the connection idle timeout THREAD_SLEEP(60 * HUNDREDS_OF_NANOS_IN_A_SECOND); @@ -398,7 +398,7 @@ TEST_F(ProducerFunctionalityTest, realtime_intermittent_no_latency_pressure_eofr << ", Dts: " << frame.decodingTs << ", Pts: " << frame.presentationTs); - EXPECT_TRUE(kinesis_video_stream->putFrame(frame)); + EXPECT_EQ(STATUS_SUCCESS, kinesis_video_stream->putFrame(frame)); timestamp += frame_duration_; THREAD_SLEEP(30 * HUNDREDS_OF_NANOS_IN_A_MILLISECOND); @@ -483,7 +483,7 @@ TEST_F(ProducerFunctionalityTest, DISABLED_realtime_intermittent_no_latency_pres << ", Dts: " << frame.decodingTs << ", Pts: " << frame.presentationTs); - EXPECT_TRUE(kinesis_video_stream->putFrame(frame)); + EXPECT_EQ(STATUS_SUCCESS, kinesis_video_stream->putFrame(frame)); timestamp += frame_duration_; THREAD_SLEEP(30 * HUNDREDS_OF_NANOS_IN_A_MILLISECOND); @@ -579,7 +579,7 @@ TEST_F(ProducerFunctionalityTest, realtime_intermittent_latency_pressure) { << ", Dts: " << frame.decodingTs << ", Pts: " << frame.presentationTs); - EXPECT_TRUE(kinesis_video_stream->putFrame(frame)); + EXPECT_EQ(STATUS_SUCCESS, kinesis_video_stream->putFrame(frame)); THREAD_SLEEP(frame_duration_); } @@ -664,7 +664,7 @@ TEST_F(ProducerFunctionalityTest, realtime_auto_intermittent_latency_pressure) { << ", Dts: " << frame.decodingTs << ", Pts: " << frame.presentationTs); - EXPECT_TRUE(kinesis_video_stream->putFrame(frame)); + EXPECT_EQ(STATUS_SUCCESS, kinesis_video_stream->putFrame(frame)); THREAD_SLEEP(frame_duration_); }