From 0ae9ec6b80deff9df724555927fb6595a23054f1 Mon Sep 17 00:00:00 2001 From: Hassan Sahibzada Date: Tue, 7 Nov 2023 13:33:57 -0800 Subject: [PATCH] testing release with media storage support (#1783) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Log ice candidate protocol appropriately (#1230) * Log ice candidate protocol appropriately * Add develop to travis CI * Address comments * Realign branches during transition (#1257) * create data channel sample (#1203) * create data channel sample * moved variables to Samples.h, encapsulated with ENABLE_DATA_CHANNEL directive * removed unused variables, moved variable declarations to the top of a block * create data channel sample * moved variables to Samples.h, encapsulated with ENABLE_DATA_CHANNEL directive * Cancel the thread once mediaThreadStarted flag is set to false (#1227) * Initial stale PR template (#1226) * Update stale issue template * Testing every minute * Fix stale issue PR template (#1234) No code changed. Just a template change. So merging * Add bug and question label * Switch stale PR action to daily cadence * [FIX] When protocol in DCEP header of data channel open command is not empty, there is a check preventing the data channel to be opened. Now protocol length is correctly handled to avoid check failing. (#1228) Co-authored-by: Niyati Maheshwari Co-authored-by: Divya Sampath Kumar Co-authored-by: ela34 <8700736+ela34@users.noreply.github.com> * set protocol value following the spec (#1259) Co-authored-by: liyufang * filter out invalid candidate in sdp (#1260) Co-authored-by: liyufang * Added targets for cmake clean (#1253) * added targets for cmake clean * added new line, updated readme * added missing new line * updated readme to include folder details * updated readme to include folder details * viewer sample: wrap datachannel callback with `ENABLE_DATA_CHANNEL` (#1261) * create data channel sample (#1203) * create data channel sample * moved variables to Samples.h, encapsulated with ENABLE_DATA_CHANNEL directive * removed unused variables, moved variable declarations to the top of a block * create data channel sample * moved variables to Samples.h, encapsulated with ENABLE_DATA_CHANNEL directive * Cancel the thread once mediaThreadStarted flag is set to false (#1227) * Initial stale PR template (#1226) * Update stale issue template * Testing every minute * Fix stale issue PR template (#1234) No code changed. Just a template change. So merging * Add bug and question label * Switch stale PR action to daily cadence * [FIX] When protocol in DCEP header of data channel open command is not empty, there is a check preventing the data channel to be opened. Now protocol length is correctly handled to avoid check failing. (#1228) * viewer sample: wrap datachannel callback with `ENABLE_DATA_CHANNEL` Signed-off-by: zhiqinli@amazon.com Co-authored-by: Niyati Maheshwari Co-authored-by: Divya Sampath Kumar Co-authored-by: ela34 <8700736+ela34@users.noreply.github.com> * libwebsockets: add patch to fix pipe fd leak issue (#1264) Signed-off-by: Alex.Li * Fix scripts/pare_status.py for non-status #defines (#1268) (#1274) Co-authored-by: Kevin Allen * fix a few bugs * Revert "fix a few bugs" This reverts commit 093902432fb24f42ade2857c23eca950e0b41a2f. * fix some bugs (#1277) * Fix bugs (#1279) * fix edge case try to unlock a mutex which is not locked in teh acse the sample config was NULL (#1286) * cmake: allow user to specify OPEN_SRC_INSTALL_PREFIX (#1293) Signed-off-by: Alex.Li * Fix inconsistent log priority (#1309) Patch reduces priority of a few info level log messages to verbose level to be consistent with other functions in file and rest of code base. * Modify protocol loggin design in develop (#1311) * typo: typo fix (#1317) Signed-off-by: Alex.Li * Signaling state machine rework (#1323) * replaced recursive calls to stepSignalingStateMachine with loops * removed recursive calls to stepSignalingStateMachine * replaced stepSignalingstatemachine with signalingStateMachineIterator * removed stepUntil, continueOnReady; removed status from iterator signature; set signalingclient version; added a lock in refreshIceConfiguration * changed the declaration for i from int to UINT32 * added signaling version; removed unnecessary comment * removed redeclaration; changed value in an existing macro * Fix issue with API call failures being treated as success (#1328) * return proper error, do not reset call result value * for non retriable failures, set the terminal exit status for state in… (#1320) * for non retriable failures, set the terminal exit status for state in state machine * address comments * adjust tests set retry max to 1 * Update LwsApiCalls.c trigger travis ci * Incorporating PIC state machine level retry changes into webrtc signaling state machine (#1326) * Incorporating PIC state machine level retry changes into webrtc signaling state machine * Signaling state machine rework (#1323) * replaced recursive calls to stepSignalingStateMachine with loops * removed recursive calls to stepSignalingStateMachine * replaced stepSignalingstatemachine with signalingStateMachineIterator * removed stepUntil, continueOnReady; removed status from iterator signature; set signalingclient version; added a lock in refreshIceConfiguration * changed the declaration for i from int to UINT32 * added signaling version; removed unnecessary comment * removed redeclaration; changed value in an existing macro * Fix issue with API call failures being treated as success (#1328) * return proper error, do not reset call result value * for non retriable failures, set the terminal exit status for state in… (#1320) * for non retriable failures, set the terminal exit status for state in state machine * address comments * adjust tests set retry max to 1 * Update LwsApiCalls.c trigger travis ci * Add retry strategy to client info to avoiud changing create signaling channel API signature * Incorporating PIC state machine level retry changes into webrtc signaling state machine * Add retry strategy to client info to avoiud changing create signaling channel API signature * Adding more debug logs in the code * fix merge conflicts * PR feedback Co-authored-by: Niyati Maheshwari Co-authored-by: Hassan Sahibzada * Revert " Incorporating PIC state machine level retry changes into webrtc signaling state machine (#1326)" (#1339) This reverts commit 06bffcecd7f60571e29e51eb15fa8e4549638213. * Incorporating PIC state machine level retry changes into webrtc signaling state machine (#1341) * Incorporating PIC state machine level retry changes into webrtc signaling state machine * Add a n optional check for free retry strategy * Remove unused goto label * add missing sleep in get token state machine execute API * update log line * Update Producer hash * Adding a high level retry strategy while creating signaling client * Add Retry count retrieval in hook (#1335) * Add Retry count retrieval in hook * Fix compile issue on travis * Pull in latest changes in retry structures * Rebasing off develop with successful travis run for retry * Log git hash (#1345) * Log git hash * Update CMakeLists.txt white space change to trigger travis Co-authored-by: Hassan Sahibzada * Fix retry count to indicate count only after a retry has occured (#1348) Fix retry count to indicate count only after a retry has occurred * add support for automatic clock skew detection and correction (#1344) * add support for automatic clock skew detection and correction * auto fix clock skew and add tests * enable test to check clock recovery * remove stuff left over from debugging, reset logging default to WARN * remove unused variable * get rid of verbose logging in unit tests causes static build to fail due to excessive log output * simplify code get rid of extra call to length because the lws_hdr_copy returns the same value * handle null stream track in case of recvonly (#1346) * handle null stream track in case of recvonly * a test with null stream track for revconly * Fix a comment * whitespace change * State machine preparation is decoupled from the constructor (#1343) * Merge master into develop (#1352) * Missing the public API switch in the other samples (#1356) * Fix Fetch error retry cases (#1359) * Fix Fetch error retry cases * update comment * added implementation and a test to use remote sdp to construct local sdp (#1238) * added implementation and a test to use remote sdp to construct local sdp * used same strings in offer and test * updated tests * Update stale action workflow (#1381) * Modify issue triaging management * test on branch * Update ga for stale issue to use aws-actions instead of the one publicly available * Travis to ga (#1347) * Test github actions * Clang format * Change platform * Add macos gtest * Ubuntu latest * Address sanitizer * Add ubsan, tsan and old gcc build * Fix macos build * Allow mac and ubsan to fail * Fixing clang format * Testing allow on failure * Run github actions only on PR and merge * Test mbedtls * Rebase on develop and fix clang failure * Test if travis builds without ga * Restore travis * Test mac build and add sample check * Fix sample check and revert mac to allow fail * Travis CI to GHA migration phase 1 * Set clang for mbedtls and test msan * Split files * Comment out failing tests * Add codecov * Travis CI to GHA migration phase 1 * new Chrome v98 produces extra sdp attributes, up the limit so we do n… (#1391) * new Chrome v98 produces extra sdp attributes, up the limit so we do not reject * Fix test and change count to unit16 to avoid overflow issues * fix issue reported by codeql Co-authored-by: Divya Sampath Kumar * GitHub actions setup (#1390) * add additional tests * fix syntax error * fix syntax error * fix syntax error * fix syntax error * fix syntax error * static build and msan without docker * fix msan * use dependencies with msan build * use gcc 9 for mac * use gcc 4.2 for mac gcc build * use gcc 4.9 for mac gcc build * check gcc version; use macos 10.15 * use clang-7 for ubsan * arm cross compilation * missed cloning the repository * use gcc * remove compiler from env * fix install deps * use clang7, macos10.15, ubuntu18.04 * fix windows build * fix test instruction * fix static build * fix static build * fix static build * fix static build * setup docker for static build * use alpine for the static build * use alpine for the static build * install dependencies * install dependencies * fix errors in static build * install dependencies * enable ipv6 * use gcc for static build * fix ubsan * fix ubsan * collate all working github actions builds * fix errors * have static,msan,windows,doxygen in travis * comment failing builds;fix ci rules * fix travis comments, os, remove exports * update log level, mac build-name * add CC * update gcc * update gcc * check cc and cxx for mac gcc * Revert "check cc and cxx for mac gcc" This reverts commit 2db1e0e4b7a9291a389cbae0f3793edd7f88c7ae. * remove travis statement * Fix swapped SSL_free() and SSL_CTX_free() (#1401) * Hangup value included in LWS retry strategy (#1403) * GitHub actions setup static build (#1405) * fix static build * remove static build from travis * fix warnings in mac builds; setup iam role for sample-check * Migrate Doxygen to GitHub Actions (#1416) * add doxygen workflow to ga * install graphviz * install ttf-freefont * specify doxyfile * use enc key * use enc key * use marketplace action to deploy * use marketplace action to deploy * use marketplace action to deploy * check current folder * check current folder * check current folder * check current folder * check current folder * check current folder * check current folder * fix deployment * fix deployment * fix deployment * fix deployment * specify doxyfile path * fix syntax * fix paths in doxyfile * fix forked branch ci * add develop branch * Fix ResourceEndpoint length check (#1411) ResourceEndpoint length check mistakenly uses ChannelName max length. * update cross-compilation builds ga (#1429) * Addressing error handling concerns for an edge case (#1443) * Addressing error handling concerns for an edge case * Clang format * Removed useless status_success check, changed test to look for correct error value * sample: fix possible timerQueue deadlock when freeSampleStreamingSession (#1448) Signed-off-by: zhiqinli@amazon.com * metrics: allow viewer sample to get ice candidate pair stats (#1406) Signed-off-by: Alex.Li * account for hexidecimal numbers (#1465) * Fix default data channel ordering mode (#1476) * Invoke gst_object_unref on allocated elements (#1477) * Fix typo (#1478) * Fix typo * Add unit test * find gcc version for static build * revert gtest version update and change alpine version * fix memory leak issue and abort trap in test * fix video delay 4s (#1525) * fix mips compile error (#1532) * fix mips compile error * fix mips compile error * fix mips compile error * Fix file caching (#1575) * Fix file caching * Update test to count entries, remove unnecessary memcpy * UT uses MEMFREE * Fix deadlock getIceCandidatePairStatsCallback (#1561) * sample: fix possible timerQueue deadlock when freeSampleStreamingSession * minor change (formatting) * wrtc stream ingestion support * clean up * default to disable data channel for wrtc ingestion * Fix missing SDP m-line problem (#1458) * use fake transceiver for missing m-line * use hashtable to track previously seen transceivers * fix failing tests with updates to seenTransceivers hashtable * use hashtables for unknown codec rtpmap and payloadtype * fix firstCodec value in case of only a single codec in m-line * rename variables * clang build * fix mac-gcc build * add test * add test for missingAudioTransceiver * add comments * add comments * remove unused variable to fix mac buils * wrtc stream ingestion support (#1605) * update readme (#1608) * update readme * update description * remove gst based storage sample (#1619) * Fix GHA CI build issues (#1631) * separate builds and tests * fix clang format issues * update libgtest version to 1.12.1 * fix deprecation in libgtest * update test suite call * run tests from build * remove build from tst instruction * fix san options * fix windows build * fix path in windows build * update bat file for windows * build openssl on windows * add openssl path for windows build * specify path to openssl root folder * specify path to openssl root folder * specify path to openssl root folder * upgrade cmake version * fix windows build * update checkoutv2 to v3 * fix checkout and install pthreads * remove windows build temporarily * move to v1-node16 for aws credentials * add sudo clean * add sudo to both commanda * use event triggers * Fix ice candidate issue with GStreamer sample (#1629) * fix gst sample * add branch to ci * fix clang * fix os versions in ci * remove unused vars to fix mac-clang build * remove unused vars to fix mac-gcc build * separate builds and tests * separate builds and tests * change test instruction for ci * change test instruction for ci * fix failing builds * add branch * Fix CI for forked branches (#1634) * use only target * use only target * remove branch name * blank space change to trigger CI * fix WSAPoll failed when set POLLPRI flag (#1636) Co-authored-by: weishao * Update README.md (#1637) Co-authored-by: Hassan Sahibzada * add paths for fedora (#1638) Co-authored-by: Hassan Sahibzada * Add clarifying comment for fallthrough (#1639) * Bkneff patch 2 (#1640) * Update kvsWebRTCClientMasterGstreamerSample.c Provide an example gstreamer pipeline for Raspberry Pi hardware encoding. Tested on Raspberry Pi 3 * Clang --------- Co-authored-by: bkneff <44409173+bkneff@users.noreply.github.com> * thread cancel memory leak, recreate signaling client & lws_context whenever a significant error has occurred, verbose and debug logging for ice & turn (#1641) * recreate signaling client & lws_context whenever a significant error has occurred * clang-format * recreate signaling client & lws_context whenever a significant error has occurred * Clang * adding local and remote null checks * More verbose and debug logging for ice & turn * Thread cancel on the media sender thread leads to memory leaks from writeFrame() not freeing all its heap usage * Clang again --------- Co-authored-by: James Delaplane * wrong port/address on debug log (#1642) Co-authored-by: Alex Zhukov * Update openssl version (#1643) * Update openssl version and fix unused param typo * Pull in producer C openssl update * fix(project): fix issue--1614 (#1644) Co-authored-by: niedb * update versions for actions (#1647) * Seggregaated Audio and Video frame handling, updated Master sample to handle audio and video both (#1646) Co-authored-by: Vikram(VS) * Mark Datachannel specific code explicitly (#1666) Co-authored-by: Dmitry Ilyin * Fix turn allocation taken long time when credential is incorrect (#1667) * fix turn allocation use long time sometimes * update some code style * fix compile error * Update IceAgent.c * fix clang format * fix prflx update interrupted connectivity check * fix clang format --------- Co-authored-by: Gordon * Update RtcpPacket.h (#1680) * Update RtcpPacket.h * Clang format * Changed sample frames to have 1 second GOP size, and removed overly verbose log (#1686) * Jitter buffer overflows (#1677) * Modified jitterbuffer to include checks for timestamp overflow * All tests passing, some new tests still need to be added * More tests and accompanying fixes * Resolve coverity finding of multiplication causing type overflow * Change function order * Clang format * Mac compile errors fixed * Commenting out long-running test, adding timestamp overflow test * Fix port printf in log (#1682) * Fix port printf in log * Fix the clang-format Fix the clang-format use ./scripts/clang-format.sh -d src/ * fix turn permission delay 2s (#1675) * fix turn permission delay 2s * Fix the clang-format Fix the clang-format use ./scripts/clang-format.sh -d src/ * Instruction to build on MacOS M1 (#1689) * Fix build dependencies off and add version requirements in readme (#1691) * Unlock mutex in case of error (#1696) * Unlock in case of error * fix typos (#1697) Co-authored-by: Ben Mesander * IoT CertificateId as Stream Name. (#1721) * IoT CertificateId as Stream Name. * Fix the clang-format use ./scripts/clang-format.sh -d src/ --------- Co-authored-by: Lina Chen * If local ICE is invalid, return from sending packet. (#1718) * If local ICE is invalid, return from sending packet. * Fix the clang-format use ./scripts/clang-format.sh -d src/ --------- Co-authored-by: Lina Chen * Adds RTSP Source to GStreamer Sample (#1740) * Adds rtsp source support, modifies source-type determination * All use cases tested and debugged. * Clang formatting. * Clang formatting. * Updates credentials configuration to fix sample-check error. * Sample-check fix attempt #2 * Sample-check fix attempt 3 * Sample-check fix attempt 4 * Sample-check fix attempt * "" * " " * " " * " " * Update README.md * Updates sample run instructions structure, adds RTSP source instructions * Update README.md * Renames sample file to be same as executable, updates CMakeLists to reflect this. * Update README.md Co-authored-by: Niyati Maheshwari * Update README.md * Updates usage statement to new sample executable name --------- Co-authored-by: Niyati Maheshwari * replace stun with sctp (#1743) * Modify the PR template (#1745) * modify the PR template * Use bullets * Update raspberry pi hardware encode to current gstreamer (#1746) * Update raspberry pi hardware encode to current gstreamer * - Provide build instructions for 32-bit raspbian on 64-bit hardware --------- Co-authored-by: Ben Mesander * Set a default user agent if not provided (#1739) * Connection timeout sleep (#1744) * Remove thread_detach and looping sleep, add thread_join * Added support for socketpair() and added it to connectionlistener * Clang formatting * Fix test that previously expected TID to be invalid * remove unused variables * Fixing incorrect typing on comparison * clang formatting * strlen -> STRLEN macro * Off by 1 error in poll rfds * clang formatting * Do Not Send SCTP Packets when Data Channel is Disabled (#1749) * no sctp packets when data channel is disabled * fix clang format * Fix comment typo regarding default source in Gst sample (#1750) * Parse IP address if the hostname conforms to Public ipv4 DNS format (#1759) * Parse IP address if the hostname conforms to Public ipv4 DNS format * Unit test for ipIpAddr function * Include and use threadpool for signaling channel messages (#1761) * Include and use threadpool for signaling channel messages * Fix clang and linux gcc compile errors * Fix pointer cast * Using SignalingClientInfo to pass information about Threadpool * edit README.md * Not necessary to change struct version * Update struct version again, add fall-through * clang formatting * Clang formatting, again * Fix the Windows build in the CI (#1764) * modify libwebsockets to work on Windows * set LWS_WITH_STATIC 1 * resume windows ci * add rest of the options * fix syntax in ci.yml * remove other options * set cmd vard * missing space in bat * add missing = in build_windows.bat * introduce cmake arguments * find_package openssl for non-windows builds * static + shared for rest of the builds * set static + shared true for shared builds always * add back ssl and crypto paths * rename vars in libwebsockets' cmake, rearrange, pthread fix * static=1 always * remove ssl and crypto paths * with_threadpool=1 * correction * correction 2 * correction 3 * correction 4 * correction 5 static * install gstreamer on windows * test mbedtls on windows * use if(WIN32) with set vars * 1671 * remove openssl arg from mbedtls build * fix mbedtls windows later * Introducing profiling of code sections and APIs (#1755) * Adds RTSP Source to GStreamer Sample (#1740) * Adds rtsp source support, modifies source-type determination * All use cases tested and debugged. * Clang formatting. * Clang formatting. * Updates credentials configuration to fix sample-check error. * Sample-check fix attempt #2 * Sample-check fix attempt 3 * Sample-check fix attempt 4 * Sample-check fix attempt * "" * " " * " " * " " * Update README.md * Updates sample run instructions structure, adds RTSP source instructions * Update README.md * Renames sample file to be same as executable, updates CMakeLists to reflect this. * Update README.md Co-authored-by: Niyati Maheshwari * Update README.md * Updates usage statement to new sample executable name --------- Co-authored-by: Niyati Maheshwari * Connection timeout sleep (#1744) * Remove thread_detach and looping sleep, add thread_join * Added support for socketpair() and added it to connectionlistener * Clang formatting * Fix test that previously expected TID to be invalid * remove unused variables * Fixing incorrect typing on comparison * clang formatting * strlen -> STRLEN macro * Off by 1 error in poll rfds * clang formatting * Fix comment typo regarding default source in Gst sample (#1750) * Initial commit * New structures to track signaling latency in diagnostics * Make peer connection stats accessible in application * Fix ice agent profling to store in variables, new APIs for getting ice agent metrics * Profile create peer connection * Macro fixes * More macro fix --------- Co-authored-by: Stefan Kieszkowski <85728496+stefankiesz@users.noreply.github.com> Co-authored-by: Niyati Maheshwari Co-authored-by: jdelapla * Fix Windows tests (#1769) * enable verbose logs * fix syntax * without gtest filter * env path * cmd.exe /c * give permissions to file * move to C: instead of D: * don't move anything * add paths to pthread, ssl, crypto, websockets dlls * cd missing * uncomment other builds * comment msan * filter out tests * Refactor issue templates (#1770) * Update commit * fix issues * update ci to run, always memset SignalingFileCacheEntry to zero to avoid cache corruption for uninitialized members, add arn validation and signaling channel name parsing out of arn, add and update tests * add media storage enabled flag to cache, fix state machine issues with newly introduced states * clang format * remove unused, adjust print * mark unused param as unused * support temp creds in test * if needed allocate channel name inside the create validate channel info to properly free, fix cache to actually update items * clang format * adjust test timeout * free channel info in test * need mutex around initScptSession because it could be called from two different threads, alerted by address sanitizer. free every instance of channel info * false to FALSE * use global init make sctpIsEnabled an atomic bool since it is used per peer connection and can be modified/read across different threads this is necessary to ensure we will not attempt to send any sctp packets when data channel has not been requested in the offer or the client application has not asked for an offer to be constructed with a data channel * lock around global init/de-init calls, may move this inside the public APIs themselves so they are thread safe * fail test if could not init signaling * remove merge left over items * resolve merge issues * remove extra sctp init, add aws sdk to test project * fix data race * remove unused vars * testing adding aws sdks * working tests for join storage session * wrap up tests changes * adjust transitions * fix compile Werror * fix * jss metric 0 if no storage * build on OSX Clang without needing to set CPATH * build seamlessly Clang * get libcurl in CI for aws sdk builds * more ci tweaks * skip aws sdk related tests when building with gcc4.4 * clang format from update 17.0.2 version of clang-format * signal jss wait cvar during termination sequence to avoid data race when free siganling client is called and callback routines are still running we may attempt to broadcast this cvar when it was already deleted * do not build aws sdk or related tests in ci, fix free order to free lock first and then cvar for jssWaitLock * install libcurl so aws sdk cpp can be installed * fix issues * clang fix and set useMediaStorage variable in tests * copy use media storage field from client supplied channel info * fix some tests * differentiate between offer received and offer sent times, fixes tsan issue no longer racing betwen same value in callback, was previously hidden due to entire if block inside messageReceiveFn null check and test sets that to null so that code path was not exercised in test * fix clang formatting and compile issue * print error message when we fail to update media storage configuration * introduce lock to resolve thread data race * free mutex * adjust behavior for join session 1 hr termination. include 44a93a1d35c318ba80b5b582adfe6f0c03cd0ccb mbedtls ci build failure fix for ubuntu 22.04 * clang format and add libcurl to ci build for aws sdk * connect call will call new join session so need to increment counts. address some cr comments * fix clang format * cleanup, revert some timeout values * cleanup, revert some timeout defaults * add correlationId to answer in sample, other cleanup * when stepping out of join session connect if signaling is still connected move to get ice config so it will be refreshed before next join session call * update state transition counts based on new transitions * address remaining PR comments * cleanup, update readme, enable feature with extra command line arg in sample * clang formatting * add media server flag to gst master sample as well and update readme * remove unused timeout * update readme * move code coverage to its own file so auto comment in pr can work * tag must match exactly linux-gcc-codecov * add missing jobs tag --------- Signed-off-by: Alex.Li Signed-off-by: zhiqinli@amazon.com Co-authored-by: Divya Sampath Kumar Co-authored-by: jdelapla Co-authored-by: Niyati Maheshwari Co-authored-by: ela34 <8700736+ela34@users.noreply.github.com> Co-authored-by: northteam Co-authored-by: liyufang Co-authored-by: Alex.D.Scofield Co-authored-by: Kevin Allen Co-authored-by: Chris Hiszpanski Co-authored-by: Anurag Katey Co-authored-by: Chris Hiszpanski Co-authored-by: Gordon Co-authored-by: Thomas Ruf Co-authored-by: Jeremy Gunawan Co-authored-by: weishao Co-authored-by: Jeremy Gunawan Co-authored-by: bkneff <44409173+bkneff@users.noreply.github.com> Co-authored-by: Alex Zhukov Co-authored-by: helloworld098765 <108649814+helloworld098765@users.noreply.github.com> Co-authored-by: niedb Co-authored-by: Vikram Shitole Co-authored-by: Vikram(VS) Co-authored-by: Dmitry Ilyin Co-authored-by: Ben Mesander Co-authored-by: Ben Mesander Co-authored-by: chenln1124 <112364884+chenln1124@users.noreply.github.com> Co-authored-by: Lina Chen Co-authored-by: Stefan Kieszkowski <85728496+stefankiesz@users.noreply.github.com> --- .github/build_windows_openssl.bat | 2 +- .github/workflows/ci.yml | 80 +- .github/workflows/codecov.yml | 26 +- .gitignore | 1 + CMake/Dependencies/libawscpp-CMakeLists.txt | 14 + CMake/Dependencies/libmbedtls-CMakeLists.txt | 2 + CMake/Dependencies/libopenssl-CMakeLists.txt | 15 +- CMake/Dependencies/libsrtp-CMakeLists.txt | 37 +- CMake/Utilities.cmake | 3 + CMakeLists.txt | 32 +- README.md | 14 +- samples/CMakeLists.txt | 1 + samples/Common.c | 69 +- samples/Samples.h | 3 + samples/kvsWebRTCClientMaster.c | 7 + samples/kvsWebRTCClientViewer.c | 4 + samples/kvsWebrtcClientMasterGstSample.c | 7 + .../kinesis/video/webrtcclient/Include.h | 28 +- .../kinesis/video/webrtcclient/Stats.h | 3 + src/source/Ice/IceAgent.c | 28 +- src/source/Ice/IceAgentStateMachine.c | 27 +- src/source/Ice/NatBehaviorDiscovery.c | 2 +- src/source/Ice/Network.c | 9 +- src/source/Ice/Network.h | 7 +- src/source/Ice/SocketConnection.c | 48 +- src/source/Ice/TurnConnection.c | 10 +- src/source/PeerConnection/PeerConnection.c | 29 +- src/source/PeerConnection/PeerConnection.h | 2 +- src/source/PeerConnection/Rtcp.c | 2 +- src/source/PeerConnection/Rtcp.h | 4 +- .../PeerConnection/SessionDescription.c | 45 +- .../PeerConnection/SessionDescription.h | 1 + src/source/Rtp/RtpPacket.h | 2 +- src/source/Signaling/ChannelInfo.c | 189 +++- src/source/Signaling/ChannelInfo.h | 13 + src/source/Signaling/Client.c | 15 +- src/source/Signaling/FileCache.c | 24 +- src/source/Signaling/FileCache.h | 3 + src/source/Signaling/LwsApiCalls.c | 253 ++++- src/source/Signaling/LwsApiCalls.h | 21 +- src/source/Signaling/Signaling.c | 188 +++- src/source/Signaling/Signaling.h | 61 +- src/source/Signaling/StateMachine.c | 405 +++++++- src/source/Signaling/StateMachine.h | 38 +- tst/CMakeLists.txt | 21 +- tst/IngestionFunctionalityTests.cpp | 921 ++++++++++++++++++ tst/SignalingApiFunctionalityTest.cpp | 136 +-- tst/SignalingApiFunctionalityTest.h | 67 ++ tst/SignalingApiTest.cpp | 161 ++- tst/TurnConnectionFunctionalityTest.cpp | 4 +- tst/WebRTCClientTestFixture.h | 2 + tst/main.cpp | 16 +- 52 files changed, 2789 insertions(+), 313 deletions(-) create mode 100644 CMake/Dependencies/libawscpp-CMakeLists.txt create mode 100644 tst/IngestionFunctionalityTests.cpp create mode 100644 tst/SignalingApiFunctionalityTest.h diff --git a/.github/build_windows_openssl.bat b/.github/build_windows_openssl.bat index c168aa96b4..3fb65e786d 100644 --- a/.github/build_windows_openssl.bat +++ b/.github/build_windows_openssl.bat @@ -2,5 +2,5 @@ call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Buil mkdir build cd build cmd.exe /c cmake -G "NMake Makefiles" .. -cmake -G "NMake Makefiles" -DBUILD_TEST=TRUE -DEXT_PTHREAD_INCLUDE_DIR="C:/tools/pthreads-w32-2-9-1-release/Pre-built.2/include/" -DEXT_PTHREAD_LIBRARIES="C:/tools/pthreads-w32-2-9-1-release/Pre-built.2/lib/x64/libpthreadGC2.a" .. +cmake -G "NMake Makefiles" -DBUILD_TEST=TRUE -DENABLE_AWS_SDK_IN_TESTS=OFF -DEXT_PTHREAD_INCLUDE_DIR="C:/tools/pthreads-w32-2-9-1-release/Pre-built.2/include/" -DEXT_PTHREAD_LIBRARIES="C:/tools/pthreads-w32-2-9-1-release/Pre-built.2/lib/x64/libpthreadGC2.a" .. nmake \ No newline at end of file diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 158c07fe91..280659f4c7 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -28,8 +28,6 @@ jobs: CC: /usr/bin/clang CXX: /usr/bin/clang++ 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 @@ -80,8 +78,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 @@ -102,33 +98,6 @@ jobs: run: | cd build ./tst/webrtc_client_test - linux-gcc-code-coverage: - runs-on: ubuntu-20.04 - env: - AWS_KVS_LOG_LEVEL: 2 - permissions: - id-token: write - contents: read - steps: - - name: Clone repository - uses: actions/checkout@v3 - - name: Configure AWS Credentials - uses: aws-actions/configure-aws-credentials@v2 - with: - role-to-assume: ${{ secrets.AWS_ROLE_TO_ASSUME }} - aws-region: ${{ secrets.AWS_REGION }} - - name: Build repository - run: | - sudo sh -c 'echo 0 > /proc/sys/net/ipv6/conf/all/disable_ipv6' - mkdir build && cd build - cmake .. -DCODE_COVERAGE=TRUE -DBUILD_TEST=TRUE - make - ulimit -c unlimited -S - timeout --signal=SIGABRT 60m ./tst/webrtc_client_test - - name: Code coverage - run: | - for test_file in $(find CMakeFiles/kvsWebrtcClient.dir CMakeFiles/kvsWebrtcSignalingClient.dir -name '*.gcno'); do gcov $test_file; done - bash <(curl -s https://codecov.io/bash) address-sanitizer: runs-on: ubuntu-20.04 env: @@ -152,6 +121,7 @@ jobs: run: | sudo apt clean && sudo apt update sudo apt-get -y install clang + sudo apt-get -y install libcurl4-openssl-dev - name: Build repository run: | # TODO: Remove the following line. This is only a workaround for enabling IPv6, https://github.com/travis-ci/travis-ci/issues/8891. @@ -186,6 +156,7 @@ jobs: run: | sudo apt clean && sudo apt update sudo apt-get -y install clang + sudo apt-get -y install libcurl4-openssl-dev - name: Build repository run: | # TODO: Remove the following line. This is only a workaround for enabling IPv6, https://github.com/travis-ci/travis-ci/issues/8891. @@ -241,6 +212,7 @@ jobs: run: | sudo apt clean && sudo apt update sudo apt-get -y install clang + sudo apt-get -y install libcurl4-openssl-dev - name: Build repository run: | sudo sh -c 'echo 0 > /proc/sys/net/ipv6/conf/all/disable_ipv6' @@ -278,10 +250,12 @@ jobs: sudo apt-get -q update sudo apt-get -y install gcc-4.4 sudo apt-get -y install gdb + sudo apt-get -y install libcurl4-openssl-dev - name: Build repository run: | mkdir build && cd build - cmake .. -DBUILD_TEST=TRUE + # As per REAMDE here: https://github.com/aws/aws-sdk-cpp minimum supported GCC is 4.9 so we do not run those tests here + cmake .. -DBUILD_TEST=TRUE -DENABLE_AWS_SDK_IN_TESTS=OFF make ulimit -c unlimited -S - name: Run tests @@ -306,7 +280,7 @@ jobs: run: | apk update apk upgrade - apk add alpine-sdk cmake clang linux-headers perl bash openssl-dev + apk add alpine-sdk cmake clang linux-headers perl bash openssl-dev zlib-dev curl-dev - name: Build Repository run: | mkdir build && cd build @@ -337,6 +311,41 @@ jobs: sudo apt-get -q update sudo apt-get -y install gcc-4.4 sudo apt-get -y install gdb + sudo apt-get -y install libcurl4-openssl-dev + - name: Build repository + run: | + mkdir build && cd build + cmake .. -DBUILD_TEST=TRUE -DUSE_OPENSSL=OFF -DUSE_MBEDTLS=ON + make + ulimit -c unlimited -S + - name: Run tests + run: | + cd build + timeout --signal=SIGABRT 60m ./tst/webrtc_client_test + mbedtls-ubuntu-gcc-11: + runs-on: ubuntu-latest + env: + AWS_KVS_LOG_LEVEL: 2 + permissions: + id-token: write + contents: read + steps: + - name: Clone repository + uses: actions/checkout@v3 + - name: Configure AWS Credentials + uses: aws-actions/configure-aws-credentials@v2 + with: + role-to-assume: ${{ secrets.AWS_ROLE_TO_ASSUME }} + aws-region: ${{ secrets.AWS_REGION }} + - name: Install deps + run: | + sudo apt clean && sudo apt update + sudo sh -c 'echo 0 > /proc/sys/net/ipv6/conf/all/disable_ipv6' + sudo add-apt-repository -y ppa:ubuntu-toolchain-r/test + sudo add-apt-repository 'deb http://archive.ubuntu.com/ubuntu/ jammy main' + sudo add-apt-repository 'deb http://archive.ubuntu.com/ubuntu/ jammy universe' + sudo apt-get -q update + sudo apt-get -y install libcurl4-openssl-dev - name: Build repository run: | mkdir build && cd build @@ -368,6 +377,7 @@ jobs: run: | sudo apt clean && sudo apt update sudo apt-get -y install clang + sudo apt-get -y install libcurl4-openssl-dev - name: Build repository run: | sudo sh -c 'echo 0 > /proc/sys/net/ipv6/conf/all/disable_ipv6' @@ -418,6 +428,10 @@ jobs: with: role-to-assume: ${{ secrets.AWS_ROLE_TO_ASSUME }} aws-region: ${{ secrets.AWS_REGION }} + - name: Install dependencies + run: | + sudo apt clean && sudo apt update + sudo apt-get -y install libcurl4-openssl-dev - name: Build repository run: | # TODO: Remove the following line. This is only a workaround for enabling IPv6, https://github.com/travis-ci/travis-ci/issues/8891. diff --git a/.github/workflows/codecov.yml b/.github/workflows/codecov.yml index 368292b147..50432e72be 100644 --- a/.github/workflows/codecov.yml +++ b/.github/workflows/codecov.yml @@ -10,28 +10,36 @@ on: - master jobs: linux-gcc-codecov: - runs-on: ubuntu-latest + runs-on: ubuntu-20.04 + env: + AWS_KVS_LOG_LEVEL: 2 permissions: id-token: write contents: read steps: - - name: Fetch + - name: Clone repository uses: actions/checkout@v3 - with: - fetch-depth: 2 - name: Configure AWS Credentials uses: aws-actions/configure-aws-credentials@v2 with: role-to-assume: ${{ secrets.AWS_ROLE_TO_ASSUME }} aws-region: ${{ secrets.AWS_REGION }} - - name: Run code coverage + - name: Install dependencies run: | - mkdir build - cd build + sudo apt clean && sudo apt update + sudo apt-get -y install libcurl4-openssl-dev + - name: Build repository + run: | + sudo sh -c 'echo 0 > /proc/sys/net/ipv6/conf/all/disable_ipv6' + mkdir build && cd build cmake .. -DCODE_COVERAGE=TRUE -DBUILD_TEST=TRUE make - export AWS_KVS_LOG_LEVEL=3 ulimit -c unlimited -S + - name: Run tests + run: | + cd build timeout --signal=SIGABRT 60m ./tst/webrtc_client_test + - name: Code coverage + run: | for test_file in $(find CMakeFiles/kvsWebrtcClient.dir CMakeFiles/kvsWebrtcSignalingClient.dir -name '*.gcno'); do gcov $test_file; done - bash <(curl -s https://codecov.io/bash) + bash <(curl -s https://codecov.io/bash) \ No newline at end of file diff --git a/.gitignore b/.gitignore index 6cd9353247..359199ab79 100644 --- a/.gitignore +++ b/.gitignore @@ -10,4 +10,5 @@ open-source/ outputs tags CMakeLists.txt.user +*.vscode *.swp diff --git a/CMake/Dependencies/libawscpp-CMakeLists.txt b/CMake/Dependencies/libawscpp-CMakeLists.txt new file mode 100644 index 0000000000..062e4110a8 --- /dev/null +++ b/CMake/Dependencies/libawscpp-CMakeLists.txt @@ -0,0 +1,14 @@ +cmake_minimum_required(VERSION 3.6.3) +project(libawscpp-download NONE) +include(ExternalProject) + +ExternalProject_Add(libawscpp-download + GIT_REPOSITORY https://github.com/aws/aws-sdk-cpp.git + GIT_TAG 1.11.157 + LIST_SEPARATOR "|" + CMAKE_ARGS -DBUILD_SHARED_LIBS=OFF + -DBUILD_ONLY=kinesisvideo|kinesis-video-webrtc-storage + -DCMAKE_INSTALL_PREFIX=${OPEN_SRC_INSTALL_PREFIX} + BUILD_ALWAYS TRUE + TEST_COMMAND "" +) \ No newline at end of file diff --git a/CMake/Dependencies/libmbedtls-CMakeLists.txt b/CMake/Dependencies/libmbedtls-CMakeLists.txt index 66ae44d0e0..aad9516994 100644 --- a/CMake/Dependencies/libmbedtls-CMakeLists.txt +++ b/CMake/Dependencies/libmbedtls-CMakeLists.txt @@ -16,6 +16,8 @@ else() set(CMAKE_MACOSX_RPATH 0) endif() +message(STATUS "C flags here are ${CMAKE_C_FLAGS}") + ExternalProject_Add( project_libmbedtls GIT_REPOSITORY https://github.com/ARMmbed/mbedtls.git diff --git a/CMake/Dependencies/libopenssl-CMakeLists.txt b/CMake/Dependencies/libopenssl-CMakeLists.txt index 1180b72f9c..a905a4bdac 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) @@ -16,13 +16,26 @@ 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} no-async --prefix=${OPEN_SRC_INSTALL_PREFIX} --openssldir=${OPEN_SRC_INSTALL_PREFIX} ${BUILD_OPENSSL_PLATFORM} -Wno-nullability-completeness -Wno-expansion-to-defined) 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} -Wno-nullability-completeness -Wno-expansion-to-defined) endif() + endif() + +message(STATUS "PREFIX is ${CMAKE_CURRENT_BINARY_DIR}/build") +message(STATUS "CONFIGURE_COMMAND is ${CONFIGURE_COMMAND}") +message(STATUS "BUILD_COMMAND is ${MAKE_EXE}") +message(STATUS "INSTALL_COMMAND is ${MAKE_EXE} install_sw") + include(ExternalProject) ExternalProject_Add(project_libopenssl GIT_REPOSITORY https://github.com/openssl/openssl.git diff --git a/CMake/Dependencies/libsrtp-CMakeLists.txt b/CMake/Dependencies/libsrtp-CMakeLists.txt index 6ba8ab0ed3..5cb4d304f8 100644 --- a/CMake/Dependencies/libsrtp-CMakeLists.txt +++ b/CMake/Dependencies/libsrtp-CMakeLists.txt @@ -1,18 +1,31 @@ cmake_minimum_required(VERSION 3.6.3) -project(libsrtp-download NONE) +project(libsrtp-download LANGUAGES C) SET(CONFIGURE_COMMAND "") # There is known bug in libsrtp where cross compiling using configure on ARM fails. Do not # enable this option if cross compilng on ARM # Check https://github.com/cisco/libsrtp/pull/496 + + if(BUILD_LIBSRTP_DESTINATION_PLATFORM STREQUAL BUILD_LIBSRTP_HOST_PLATFORM) + if(UNIX OR APPLE) - if(USE_OPENSSL) - SET(CONFIGURE_COMMAND ${CMAKE_CURRENT_BINARY_DIR}/build/src/project_libsrtp/configure "CFLAGS=${CMAKE_C_FLAGS}" --prefix=${OPEN_SRC_INSTALL_PREFIX} --enable-openssl --with-openssl-dir=${OPENSSL_DIR}) + + SET(CONFIGURE_COMMAND ${CMAKE_CURRENT_BINARY_DIR}/build/src/project_libsrtp/configure CC=${CMAKE_C_COMPILER}) + + if (DEFINED CMAKE_OSX_SYSROOT AND NOT CMAKE_OSX_SYSROOT STREQUAL "") + if ("${CMAKE_C_COMPILER_ID}" MATCHES "Clang") + SET(KVS_CFLAGS "-I${CMAKE_OSX_SYSROOT}/usr/include -Wno-error=implicit-function-declaration") + SET(CONFIGURE_COMMAND ${CONFIGURE_COMMAND} CFLAGS=${KVS_CFLAGS} LDFLAGS=-L${CMAKE_OSX_SYSROOT}/usr/lib) + endif() + endif() + + if (USE_OPENSSL) + SET(CONFIGURE_COMMAND ${CONFIGURE_COMMAND} --prefix=${OPEN_SRC_INSTALL_PREFIX} --enable-openssl --with-openssl-dir=${OPENSSL_DIR}) else() - SET(CONFIGURE_COMMAND ${CMAKE_CURRENT_BINARY_DIR}/build/src/project_libsrtp/configure "CFLAGS=${CMAKE_C_FLAGS}" --prefix=${OPEN_SRC_INSTALL_PREFIX}) + SET(CONFIGURE_COMMAND ${CONFIGURE_COMMAND} --prefix=${OPEN_SRC_INSTALL_PREFIX}) endif() if (DEFINED BUILD_LIBSRTP_DESTINATION_PLATFORM AND NOT BUILD_LIBSRTP_DESTINATION_PLATFORM STREQUAL OFF) @@ -23,9 +36,6 @@ if(BUILD_LIBSRTP_DESTINATION_PLATFORM STREQUAL BUILD_LIBSRTP_HOST_PLATFORM) set(CONFIGURE_COMMAND ${CONFIGURE_COMMAND} --build=${BUILD_LIBSRTP_HOST_PLATFORM}) endif() - if (DEFINED CMAKE_OSX_SYSROOT AND NOT CMAKE_OSX_SYSROOT STREQUAL "") - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -isysroot${CMAKE_OSX_SYSROOT}") - endif() endif() endif() @@ -41,18 +51,19 @@ else() set(LIBSRTP_ENABLE_OPENSSL OFF) endif() +message(STATUS "CONFIGURE_COMMAND is ${CONFIGURE_COMMAND}") + include(ExternalProject) ExternalProject_Add(project_libsrtp GIT_REPOSITORY https://github.com/cisco/libsrtp.git GIT_TAG bd0f27ec0e299ad101a396dde3f7c90d48efc8fc PREFIX ${CMAKE_CURRENT_BINARY_DIR}/build - CMAKE_ARGS -D CMAKE_BUILD_TYPE:STRING=${CMAKE_BUILD_TYPE} - -D CMAKE_INSTALL_PREFIX:STRING=${OPEN_SRC_INSTALL_PREFIX} - -D ENABLE_OPENSSL=${LIBSRTP_ENABLE_OPENSSL} - -D BUILD_SHARED_LIBS=${LIBSRTP_SHARED_LIBS} - -D OPENSSL_ROOT_DIR=${OPEN_SRC_INSTALL_PREFIX} - -DCMAKE_C_FLAGS=${CMAKE_C_FLAGS} + CMAKE_ARGS -DCMAKE_BUILD_TYPE:STRING=${CMAKE_BUILD_TYPE} + -DCMAKE_INSTALL_PREFIX:STRING=${OPEN_SRC_INSTALL_PREFIX} + -DENABLE_OPENSSL=${LIBSRTP_ENABLE_OPENSSL} + -DBUILD_SHARED_LIBS=${LIBSRTP_SHARED_LIBS} + -DOPENSSL_ROOT_DIR=${OPEN_SRC_INSTALL_PREFIX} CONFIGURE_COMMAND ${CONFIGURE_COMMAND} TEST_COMMAND "" ) diff --git a/CMake/Utilities.cmake b/CMake/Utilities.cmake index 35c9e82446..23869d3d68 100644 --- a/CMake/Utilities.cmake +++ b/CMake/Utilities.cmake @@ -3,6 +3,7 @@ function(build_dependency lib_name) set(supported_libs gperftools gtest + awscpp benchmark jsmn openssl @@ -26,6 +27,8 @@ function(build_dependency lib_name) set(lib_file_name srtp2) elseif(${lib_name} STREQUAL "gperftools") set(lib_file_name profiler) + elseif(${lib_name} STREQUAL "awscpp") + set(lib_file_name aws-cpp-sdk-core) endif() set(library_found NOTFOUND) find_library( diff --git a/CMakeLists.txt b/CMakeLists.txt index 7bcfb871c8..96aa15ce33 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -7,6 +7,7 @@ include(CheckFunctionExists) # The version MUST be updated before every release project(KinesisVideoWebRTCClient VERSION 1.8.1 LANGUAGES C) + # User Flags option(ADD_MUCLIBC "Add -muclibc c flag" OFF) option(BUILD_DEPENDENCIES "Whether or not to build depending libraries from source" ON) @@ -19,6 +20,7 @@ option(BUILD_LIBSRTP_DESTINATION_PLATFORM "If buildng LibSRTP what is the destin option(BUILD_SAMPLE "Build available samples" ON) option(ENABLE_DATA_CHANNEL "Enable support for data channel" ON) option(INSTRUMENTED_ALLOCATORS "Enable memory instrumentation" OFF) +option(ENABLE_AWS_SDK_IN_TESTS "Enable support for compiling AWS SDKs for tests" ON) # Developer Flags option(BUILD_TEST "Build the testing tree." OFF) @@ -36,6 +38,7 @@ if(WIN32) set(EXT_PTHREAD_LIBRARIES "" CACHE FILEPATH "Path to PThread libraries") endif() + execute_process( COMMAND git rev-parse HEAD WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} @@ -103,10 +106,18 @@ if(USE_OPENSSL) elseif(USE_MBEDTLS) add_definitions(-DKVS_USE_MBEDTLS) # FIXME: there's probably a better way to inject MBEDTLS_USER_CONFIG_FILE flag without mutating the global CMAKE_C_FLAGS and CMAKE_CXX_FLAGS - set(CMAKE_C_FLAGS "-I${CMAKE_CURRENT_SOURCE_DIR}/configs -DMBEDTLS_USER_CONFIG_FILE=\"\" ${CMAKE_C_FLAGS}") - set(CMAKE_CXX_FLAGS "-I${CMAKE_CURRENT_SOURCE_DIR}/configs -DMBEDTLS_USER_CONFIG_FILE=\"\" ${CMAKE_CXX_FLAGS}") + if("${CMAKE_C_COMPILER_ID}" MATCHES "Clang") + message(STATUS "Detected clang") + set(CMAKE_C_FLAGS "-I${CMAKE_CURRENT_SOURCE_DIR}/configs -DMBEDTLS_USER_CONFIG_FILE=\"\" ${CMAKE_C_FLAGS}") + set(CMAKE_CXX_FLAGS "-I${CMAKE_CURRENT_SOURCE_DIR}/configs -DMBEDTLS_USER_CONFIG_FILE=\"\" ${CMAKE_CXX_FLAGS}") + else() + message(STATUS "Detected gcc") + set(CMAKE_C_FLAGS "-Wno-error=stringop-overflow -I${CMAKE_CURRENT_SOURCE_DIR}/configs -DMBEDTLS_USER_CONFIG_FILE=\"\" ${CMAKE_C_FLAGS}") + set(CMAKE_CXX_FLAGS "-Wno-error=stringop-overflow -I${CMAKE_CURRENT_SOURCE_DIR}/configs -DMBEDTLS_USER_CONFIG_FILE=\"\" ${CMAKE_CXX_FLAGS}") + endif() endif() + if(BUILD_DEPENDENCIES) if(NOT EXISTS ${OPEN_SRC_INSTALL_PREFIX}) file(MAKE_DIRECTORY ${OPEN_SRC_INSTALL_PREFIX}) @@ -178,8 +189,14 @@ if(BUILD_DEPENDENCIES) -DCMAKE_C_FLAGS=${CMAKE_C_FLAGS}) build_dependency(usrsctp ${BUILD_ARGS}) + if(BUILD_TEST) build_dependency(gtest) + + if(ENABLE_AWS_SDK_IN_TESTS) + build_dependency(awscpp) + endif() + endif() if(BUILD_BENCHMARK) @@ -245,14 +262,15 @@ set(OPEN_SRC_INCLUDE_DIRS ${OPEN_SRC_INCLUDE_DIRS} ${LIBSRTP_INCLUDE_DIRS} link_directories(${LIBSRTP_LIBRARY_DIRS}) link_directories(${LIBWEBSOCKETS_LIBRARY_DIRS}) link_directories(${OPEN_SRC_INSTALL_PREFIX}/lib) -link_directories(${OPEN_SRC_INSTALL_PREFIX}/lib64) +if(EXISTS ${OPEN_SRC_INSTALL_PREFIX}/lib64) + link_directories(${OPEN_SRC_INSTALL_PREFIX}/lib64) +endif() ############# find dependent libraries end ############ if("${CMAKE_C_COMPILER_ID}" MATCHES "GNU|Clang") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fPIC") - if(CODE_COVERAGE) set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -O0 -g -fprofile-arcs -ftest-coverage") set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} --coverage") @@ -295,6 +313,10 @@ if (INSTRUMENTED_ALLOCATORS) add_definitions(-DINSTRUMENTED_ALLOCATORS) endif() +if(ENABLE_AWS_SDK_IN_TESTS) + add_definitions(-DENABLE_AWS_SDK_IN_TESTS) +endif() + file( GLOB WEBRTC_CLIENT_SOURCE_FILES @@ -397,6 +419,8 @@ if (BUILD_SAMPLE) endif() if(BUILD_TEST) + # adding ZLIB because aws sdk static link seems to be broken when zlib is needed + find_package(ZLIB REQUIRED) add_subdirectory(tst) endif() diff --git a/README.md b/README.md index dfcfedd4cf..ab08aee972 100644 --- a/README.md +++ b/README.md @@ -41,6 +41,8 @@ Please refer to the release notes in [Releases](https://github.com/awslabs/amazo * Signaling Client Included - KVS Provides STUN/TURN and Signaling Backend - Connect with [Android](https://github.com/awslabs/amazon-kinesis-video-streams-webrtc-sdk-android)/[iOS](https://github.com/awslabs/amazon-kinesis-video-streams-webrtc-sdk-ios)/[Web](https://github.com/awslabs/amazon-kinesis-video-streams-webrtc-sdk-js) using pre-made samples +* Storage for WebRTC [NEW] + * Ingest media into a Kinesis Video Stream. * Portable - Tested on Linux/MacOS - Tested on x64, ARMv5 @@ -216,21 +218,29 @@ This application sends sample H264/Opus frames (path: `/samples/h264SampleFrames ./samples/kvsWebrtcClientMaster ``` +To use the **Storage for WebRTC** feature, run the same command as above but with an additional command line arg to enable the feature. + +``` +./samples/kvsWebrtcClientMaster 1 +``` + #### Sample: kvsWebrtcClientMasterGstSample This application can send media from a GStreamer pipeline using test H264/Opus frames, device `autovideosrc` and `autoaudiosrc` input, or a received RTSP stream. It also will playback incoming audio via an `autoaudiosink`. To run: ``` ./samples/kvsWebrtcClientMasterGstSample ``` -Pass the desired media and source type when running the sample. The mediaType can be `audio-video` or `video-only`. The source type can be `testsrc`, `devicesrc`, or `rtspsrc`. Specify the RTSP URI if using `rtspsrc`: +Pass the desired media and source type when running the sample. The mediaType can be `audio-video` or `video-only`. To use the **Storage For WebRTC** feature, use `audio-video-storage` as the mediaType. The source type can be `testsrc`, `devicesrc`, or `rtspsrc`. Specify the RTSP URI if using `rtspsrc`: ``` ./samples/kvsWebrtcClientMasterGstSample rtspsrc rtsp:// ``` + #### Sample: kvsWebrtcClientViewer This application accepts sample H264/Opus frames and prints them out. To run: ``` -./samples/kvsWebrtcClientMaster +./samples/kvsWebrtcClientViewer ``` + ### Viewing Master Samples After running one of the master samples, when the command line application prints "Signaling client connection to socket established", indicating that your signaling channel is created and the connected master is streaming media to it, you can view the stream. To do so, check the media playback viewer on the KVS Signaling Channels console or open the [WebRTC SDK Test Page](https://awslabs.github.io/amazon-kinesis-video-streams-webrtc-sdk-js/examples/index.html). diff --git a/samples/CMakeLists.txt b/samples/CMakeLists.txt index 513182b907..aaecb4dd1f 100644 --- a/samples/CMakeLists.txt +++ b/samples/CMakeLists.txt @@ -78,3 +78,4 @@ endif() install(TARGETS kvsWebrtcClientMaster kvsWebrtcClientViewer discoverNatBehavior RUNTIME DESTINATION bin ) + diff --git a/samples/Common.c b/samples/Common.c index 97898a13da..63365e5d49 100644 --- a/samples/Common.c +++ b/samples/Common.c @@ -28,7 +28,8 @@ STATUS signalingCallFailed(STATUS status) { return (STATUS_SIGNALING_GET_TOKEN_CALL_FAILED == status || STATUS_SIGNALING_DESCRIBE_CALL_FAILED == status || STATUS_SIGNALING_CREATE_CALL_FAILED == status || STATUS_SIGNALING_GET_ENDPOINT_CALL_FAILED == status || - STATUS_SIGNALING_GET_ICE_CONFIG_CALL_FAILED == status || STATUS_SIGNALING_CONNECT_CALL_FAILED == status); + STATUS_SIGNALING_GET_ICE_CONFIG_CALL_FAILED == status || STATUS_SIGNALING_CONNECT_CALL_FAILED == status || + STATUS_SIGNALING_DESCRIBE_MEDIA_CALL_FAILED == status); } VOID onDataChannelMessage(UINT64 customData, PRtcDataChannel pDataChannel, BOOL isBinary, PBYTE pMessage, UINT32 pMessageLen) @@ -79,6 +80,7 @@ VOID onConnectionStateChange(UINT64 customData, RTC_PEER_CONNECTION_STATE newSta case RTC_PEER_CONNECTION_STATE_CLOSED: // explicit fallthrough case RTC_PEER_CONNECTION_STATE_DISCONNECTED: + DLOGD("p2p connection disconnected"); ATOMIC_STORE_BOOL(&pSampleStreamingSession->terminateFlag, TRUE); CVAR_BROADCAST(pSampleConfiguration->cvar); // explicit fallthrough @@ -220,7 +222,7 @@ STATUS handleOffer(PSampleConfiguration pSampleConfiguration, PSampleStreamingSe MEMSET(&offerSessionDescriptionInit, 0x00, SIZEOF(RtcSessionDescriptionInit)); MEMSET(&pSampleStreamingSession->answerSessionDescriptionInit, 0x00, SIZEOF(RtcSessionDescriptionInit)); - + DLOGD("**offer:%s", pSignalingMessage->payload); CHK_STATUS(deserializeSessionDescriptionInit(pSignalingMessage->payload, pSignalingMessage->payloadLen, &offerSessionDescriptionInit)); CHK_STATUS(setRemoteDescription(pSampleStreamingSession->pPeerConnection, &offerSessionDescriptionInit)); canTrickle = canTrickleIceCandidates(pSampleStreamingSession->pPeerConnection); @@ -273,7 +275,8 @@ STATUS sendSignalingMessage(PSampleStreamingSession pSampleStreamingSession, PSi CHK_STATUS(signalingClientSendMessageSync(pSampleConfiguration->signalingClientHandle, pMessage)); if (pMessage->messageType == SIGNALING_MESSAGE_TYPE_ANSWER) { CHK_STATUS(signalingClientGetMetrics(pSampleConfiguration->signalingClientHandle, &pSampleConfiguration->signalingClientMetrics)); - DLOGP("[Signaling offer to answer] %" PRIu64 " ms", pSampleConfiguration->signalingClientMetrics.signalingClientStats.offerToAnswerTime); + DLOGP("[Signaling offer received to answer sent time] %" PRIu64 " ms", + pSampleConfiguration->signalingClientMetrics.signalingClientStats.offerToAnswerTime); } CleanUp: @@ -298,8 +301,9 @@ STATUS respondWithAnswer(PSampleStreamingSession pSampleStreamingSession) message.messageType = SIGNALING_MESSAGE_TYPE_ANSWER; STRNCPY(message.peerClientId, pSampleStreamingSession->peerId, MAX_SIGNALING_CLIENT_ID_LEN); message.payloadLen = (UINT32) STRLEN(message.payload); - message.correlationId[0] = '\0'; - + // SNPRINTF appends null terminator, so we do not manually add it + SNPRINTF(message.correlationId, MAX_CORRELATION_ID_LEN, "%llu_%llu", GETTIME(), ATOMIC_INCREMENT(&pSampleStreamingSession->correlationIdPostFix)); + DLOGD("Responding With Answer With correlationId: %s", message.correlationId); CHK_STATUS(sendSignalingMessage(pSampleStreamingSession, &message)); CleanUp: @@ -634,6 +638,12 @@ VOID sampleAudioFrameHandler(UINT64 customData, PFrame pFrame) DLOGV("Audio Frame received. TrackId: %" PRIu64 ", Size: %u, Flags %u", pFrame->trackId, pFrame->size, pFrame->flags); } +VOID sampleFrameHandler(UINT64 customData, PFrame pFrame) +{ + UNUSED_PARAM(customData); + DLOGV("Video Frame received. TrackId: %" PRIu64 ", Size: %u, Flags %u", pFrame->trackId, pFrame->size, pFrame->flags); +} + VOID sampleBandwidthEstimationHandler(UINT64 customData, DOUBLE maximumBitrate) { UNUSED_PARAM(customData); @@ -704,7 +714,7 @@ STATUS lookForSslCert(PSampleConfiguration* ppSampleConfiguration) PSampleConfiguration pSampleConfiguration = *ppSampleConfiguration; MEMSET(certName, 0x0, ARRAY_SIZE(certName)); - pSampleConfiguration->pCaCertPath = getenv(CACERT_PATH_ENV_VAR); + pSampleConfiguration->pCaCertPath = GETENV(CACERT_PATH_ENV_VAR); // if ca cert path is not set from the environment, try to use the one that cmake detected if (pSampleConfiguration->pCaCertPath == NULL) { @@ -747,22 +757,22 @@ STATUS createSampleConfiguration(PCHAR channelName, SIGNALING_CHANNEL_ROLE_TYPE CHK(NULL != (pSampleConfiguration = (PSampleConfiguration) MEMCALLOC(1, SIZEOF(SampleConfiguration))), STATUS_NOT_ENOUGH_MEMORY); #ifdef IOT_CORE_ENABLE_CREDENTIALS - PCHAR pIotCoreCredentialEndPoint, pIotCoreCert, pIotCorePrivateKey, pIotCoreRoleAlias, pIotCoreCertificateId; - CHK_ERR((pIotCoreCredentialEndPoint = getenv(IOT_CORE_CREDENTIAL_ENDPOINT)) != NULL, STATUS_INVALID_OPERATION, + 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((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"); #else - CHK_ERR((pAccessKey = getenv(ACCESS_KEY_ENV_VAR)) != NULL, STATUS_INVALID_OPERATION, "AWS_ACCESS_KEY_ID must be set"); - CHK_ERR((pSecretKey = getenv(SECRET_KEY_ENV_VAR)) != NULL, STATUS_INVALID_OPERATION, "AWS_SECRET_ACCESS_KEY must be set"); + CHK_ERR((pAccessKey = GETENV(ACCESS_KEY_ENV_VAR)) != NULL, STATUS_INVALID_OPERATION, "AWS_ACCESS_KEY_ID must be set"); + CHK_ERR((pSecretKey = GETENV(SECRET_KEY_ENV_VAR)) != NULL, STATUS_INVALID_OPERATION, "AWS_SECRET_ACCESS_KEY must be set"); #endif - pSessionToken = getenv(SESSION_TOKEN_ENV_VAR); + pSessionToken = GETENV(SESSION_TOKEN_ENV_VAR); // If the env is set, we generate normal log files apart from filtered profile log files // If not set, we generate only the filtered profile log files - if (NULL != getenv(ENABLE_FILE_LOGGING)) { + if (NULL != GETENV(ENABLE_FILE_LOGGING)) { retStatus = createFileLoggerWithLevelFiltering(FILE_LOGGING_BUFFER_SIZE, MAX_NUMBER_OF_LOG_FILES, (PCHAR) FILE_LOGGER_LOG_FILE_DIRECTORY_PATH, TRUE, TRUE, TRUE, LOG_LEVEL_PROFILE, NULL); @@ -782,7 +792,7 @@ STATUS createSampleConfiguration(PCHAR channelName, SIGNALING_CHANNEL_ROLE_TYPE } } - if ((pSampleConfiguration->channelInfo.pRegion = getenv(DEFAULT_REGION_ENV_VAR)) == NULL) { + if ((pSampleConfiguration->channelInfo.pRegion = GETENV(DEFAULT_REGION_ENV_VAR)) == NULL) { pSampleConfiguration->channelInfo.pRegion = DEFAULT_AWS_REGION; } @@ -892,6 +902,11 @@ STATUS initSignaling(PSampleConfiguration pSampleConfiguration, PCHAR clientId) // Enable the processing of the messages CHK_STATUS(signalingClientFetchSync(pSampleConfiguration->signalingClientHandle)); + +#ifdef ENABLE_DATA_CHANNEL + pSampleConfiguration->onDataChannel = onDataChannel; +#endif + CHK_STATUS(signalingClientConnectSync(pSampleConfiguration->signalingClientHandle)); signalingClientGetMetrics(pSampleConfiguration->signalingClientHandle, &signalingClientMetrics); @@ -899,10 +914,14 @@ STATUS initSignaling(PSampleConfiguration pSampleConfiguration, PCHAR clientId) // Logging this here since the logs in signaling library do not get routed to file DLOGP("[Signaling Get token] %" PRIu64 " ms", signalingClientMetrics.signalingClientStats.getTokenCallTime); DLOGP("[Signaling Describe] %" PRIu64 " ms", signalingClientMetrics.signalingClientStats.describeCallTime); + DLOGP("[Signaling Describe Media] %" PRIu64 " ms", signalingClientMetrics.signalingClientStats.describeMediaCallTime); DLOGP("[Signaling Create Channel] %" PRIu64 " ms", signalingClientMetrics.signalingClientStats.createCallTime); DLOGP("[Signaling Get endpoint] %" PRIu64 " ms", signalingClientMetrics.signalingClientStats.getEndpointCallTime); DLOGP("[Signaling Get ICE config] %" PRIu64 " ms", signalingClientMetrics.signalingClientStats.getIceConfigCallTime); DLOGP("[Signaling Connect] %" PRIu64 " ms", signalingClientMetrics.signalingClientStats.connectCallTime); + if (signalingClientMetrics.signalingClientStats.joinSessionCallTime != 0) { + DLOGP("[Signaling Join Session] %" PRIu64 " ms", signalingClientMetrics.signalingClientStats.joinSessionCallTime); + } DLOGP("[Signaling create client] %" PRIu64 " ms", signalingClientMetrics.signalingClientStats.createClientTime); DLOGP("[Signaling fetch client] %" PRIu64 " ms", signalingClientMetrics.signalingClientStats.fetchClientTime); DLOGP("[Signaling connect client] %" PRIu64 " ms", signalingClientMetrics.signalingClientStats.connectClientTime); @@ -1102,6 +1121,7 @@ STATUS freeSampleConfiguration(PSampleConfiguration* ppSampleConfiguration) pSampleConfiguration = *ppSampleConfiguration; CHK(pSampleConfiguration != NULL, retStatus); + if (IS_VALID_TIMER_QUEUE_HANDLE(pSampleConfiguration->timerQueueHandle)) { if (pSampleConfiguration->iceCandidatePairStatsTimerId != MAX_UINT32) { retStatus = timerQueueCancelTimer(pSampleConfiguration->timerQueueHandle, pSampleConfiguration->iceCandidatePairStatsTimerId, @@ -1218,7 +1238,7 @@ STATUS sessionCleanupWait(PSampleConfiguration pSampleConfiguration) STATUS retStatus = STATUS_SUCCESS; PSampleStreamingSession pSampleStreamingSession = NULL; UINT32 i, clientIdHash; - BOOL sampleConfigurationObjLockLocked = FALSE, streamingSessionListReadLockLocked = FALSE, peerConnectionFound = FALSE; + BOOL sampleConfigurationObjLockLocked = FALSE, streamingSessionListReadLockLocked = FALSE, peerConnectionFound = FALSE, sessionFreed = FALSE; SIGNALING_CLIENT_STATE signalingClientState; CHK(pSampleConfiguration != NULL, STATUS_NULL_ARG); @@ -1252,9 +1272,21 @@ STATUS sessionCleanupWait(PSampleConfiguration pSampleConfiguration) streamingSessionListReadLockLocked = FALSE; CHK_STATUS(freeSampleStreamingSession(&pSampleStreamingSession)); + sessionFreed = TRUE; } } + if (sessionFreed && pSampleConfiguration->channelInfo.useMediaStorage && !ATOMIC_LOAD_BOOL(&pSampleConfiguration->recreateSignalingClient)) { + // In the WebRTC Media Storage Ingestion Case the backend will terminate the session after + // 1 hour. The SDK needs to make a new JoinSession Call in order to receive a new + // offer from the backend. We will create a new sample streaming session upon receipt of the + // offer. The signalingClientConnectSync call will result in a JoinSession API call being made. + CHK_STATUS(signalingClientDisconnectSync(pSampleConfiguration->signalingClientHandle)); + CHK_STATUS(signalingClientFetchSync(pSampleConfiguration->signalingClientHandle)); + CHK_STATUS(signalingClientConnectSync(pSampleConfiguration->signalingClientHandle)); + sessionFreed = FALSE; + } + // Check if we need to re-create the signaling client on-the-fly if (ATOMIC_LOAD_BOOL(&pSampleConfiguration->recreateSignalingClient)) { retStatus = signalingClientFetchSync(pSampleConfiguration->signalingClientHandle); @@ -1428,7 +1460,8 @@ STATUS signalingMessageReceived(UINT64 customData, PReceivedSignalingMessage pRe startStats = pSampleConfiguration->iceCandidatePairStatsTimerId == MAX_UINT32; CHK_STATUS(signalingClientGetMetrics(pSampleConfiguration->signalingClientHandle, &pSampleConfiguration->signalingClientMetrics)); - DLOGP("[Signaling offer to answer] %" PRIu64 " ms", pSampleConfiguration->signalingClientMetrics.signalingClientStats.offerToAnswerTime); + DLOGP("[Signaling offer sent to answer received time] %" PRIu64 " ms", + pSampleConfiguration->signalingClientMetrics.signalingClientStats.offerToAnswerTime); break; case SIGNALING_MESSAGE_TYPE_ICE_CANDIDATE: diff --git a/samples/Samples.h b/samples/Samples.h index dedd53d65e..41c34897af 100644 --- a/samples/Samples.h +++ b/samples/Samples.h @@ -127,6 +127,7 @@ typedef struct { UINT32 iceUriCount; SignalingClientCallbacks signalingClientCallbacks; SignalingClientInfo clientInfo; + RtcStats rtcIceCandidatePairMetrics; MUTEX signalingSendMessageLock; @@ -152,6 +153,7 @@ struct __SampleStreamingSession { volatile ATOMIC_BOOL peerIdReceived; volatile ATOMIC_BOOL firstFrame; volatile SIZE_T frameIndex; + volatile SIZE_T correlationIdPostFix; PRtcPeerConnection pPeerConnection; PRtcRtpTransceiver pVideoRtcRtpTransceiver; PRtcRtpTransceiver pAudioRtcRtpTransceiver; @@ -199,6 +201,7 @@ STATUS respondWithAnswer(PSampleStreamingSession); STATUS resetSampleConfigurationState(PSampleConfiguration); VOID sampleVideoFrameHandler(UINT64, PFrame); VOID sampleAudioFrameHandler(UINT64, PFrame); +VOID sampleFrameHandler(UINT64, PFrame); VOID sampleBandwidthEstimationHandler(UINT64, DOUBLE); VOID sampleSenderBandwidthEstimationHandler(UINT64, UINT32, UINT32, UINT32, UINT32, UINT64); VOID onDataChannel(UINT64, PRtcDataChannel); diff --git a/samples/kvsWebRTCClientMaster.c b/samples/kvsWebRTCClientMaster.c index 1913401f18..f4a4529685 100644 --- a/samples/kvsWebRTCClientMaster.c +++ b/samples/kvsWebRTCClientMaster.c @@ -30,7 +30,14 @@ INT32 main(INT32 argc, CHAR* argv[]) pSampleConfiguration->audioSource = sendAudioPackets; pSampleConfiguration->videoSource = sendVideoPackets; pSampleConfiguration->receiveAudioVideoSource = sampleReceiveAudioVideoFrame; + + if (argc > 2 && STRNCMP(argv[2], "1", 2) == 0) { + pSampleConfiguration->channelInfo.useMediaStorage = TRUE; + } + +#ifdef ENABLE_DATA_CHANNEL pSampleConfiguration->onDataChannel = onDataChannel; +#endif pSampleConfiguration->mediaType = SAMPLE_STREAMING_AUDIO_VIDEO; DLOGI("[KVS Master] Finished setting handlers"); diff --git a/samples/kvsWebRTCClientViewer.c b/samples/kvsWebRTCClientViewer.c index 7ed9a3c4ba..000aa61261 100644 --- a/samples/kvsWebRTCClientViewer.c +++ b/samples/kvsWebRTCClientViewer.c @@ -62,6 +62,10 @@ INT32 main(INT32 argc, CHAR* argv[]) CHK_STATUS(initKvsWebRtc()); DLOGI("[KVS Viewer] KVS WebRTC initialization completed successfully"); +#ifdef ENABLE_DATA_CHANNEL + pSampleConfiguration->onDataChannel = onDataChannel; +#endif + SPRINTF(clientId, "%s_%u", SAMPLE_VIEWER_CLIENT_ID, RAND() % MAX_UINT32); CHK_STATUS(initSignaling(pSampleConfiguration, clientId)); DLOGI("[KVS Viewer] Signaling client connection established"); diff --git a/samples/kvsWebrtcClientMasterGstSample.c b/samples/kvsWebrtcClientMasterGstSample.c index 1fac4cd78a..0ccf87b5bd 100644 --- a/samples/kvsWebrtcClientMasterGstSample.c +++ b/samples/kvsWebrtcClientMasterGstSample.c @@ -406,7 +406,10 @@ INT32 main(INT32 argc, CHAR* argv[]) pSampleConfiguration->videoSource = sendGstreamerAudioVideo; pSampleConfiguration->mediaType = SAMPLE_STREAMING_VIDEO_ONLY; pSampleConfiguration->receiveAudioVideoSource = receiveGstreamerAudioVideo; + +#ifdef ENABLE_DATA_CHANNEL pSampleConfiguration->onDataChannel = onDataChannel; +#endif pSampleConfiguration->customData = (UINT64) pSampleConfiguration; pSampleConfiguration->srcType = DEVICE_SOURCE; // Default to device source (autovideosrc and autoaudiosrc) /* Initialize GStreamer */ @@ -417,6 +420,10 @@ INT32 main(INT32 argc, CHAR* argv[]) if (STRCMP(argv[2], "video-only") == 0) { pSampleConfiguration->mediaType = SAMPLE_STREAMING_VIDEO_ONLY; DLOGI("[KVS Gstreamer Master] Streaming video only"); + } else if (STRCMP(argv[2], "audio-video-storage") == 0) { + pSampleConfiguration->mediaType = SAMPLE_STREAMING_AUDIO_VIDEO; + pSampleConfiguration->channelInfo.useMediaStorage = TRUE; + DLOGI("[KVS Gstreamer Master] Streaming audio and video"); } else if (STRCMP(argv[2], "audio-video") == 0) { pSampleConfiguration->mediaType = SAMPLE_STREAMING_AUDIO_VIDEO; DLOGI("[KVS Gstreamer Master] Streaming audio and video"); diff --git a/src/include/com/amazonaws/kinesis/video/webrtcclient/Include.h b/src/include/com/amazonaws/kinesis/video/webrtcclient/Include.h index 0df3022f5d..dce4517605 100644 --- a/src/include/com/amazonaws/kinesis/video/webrtcclient/Include.h +++ b/src/include/com/amazonaws/kinesis/video/webrtcclient/Include.h @@ -332,6 +332,14 @@ extern "C" { #define STATUS_SIGNALING_INVALID_METRICS_VERSION STATUS_SIGNALING_BASE + 0x00000032 #define STATUS_SIGNALING_INVALID_CLIENT_INFO_CACHE_FILE_PATH_LEN STATUS_SIGNALING_BASE + 0x00000033 #define STATUS_SIGNALING_LWS_CALL_FAILED STATUS_SIGNALING_BASE + 0x00000034 +#define STATUS_SIGNALING_INVALID_STREAM_ARN_LENGTH STATUS_SIGNALING_BASE + 0x00000035 +#define STATUS_SIGNALING_MISMATCH_MEDIA_STORAGE_CONFIG STATUS_SIGNALING_BASE + 0x00000036 +#define STATUS_SIGNALING_UPDATE_MEDIA_STORAGE_CONFIG STATUS_SIGNALING_BASE + 0x00000037 +#define STATUS_SIGNALING_MEDIA_STORAGE_DISABLED STATUS_SIGNALING_BASE + 0x00000038 +#define STATUS_SIGNALING_INVALID_CHANNEL_ARN STATUS_SIGNALING_BASE + 0x00000039 +#define STATUS_SIGNALING_JOIN_SESSION_CALL_FAILED STATUS_SIGNALING_BASE + 0x0000004a +#define STATUS_SIGNALING_JOIN_SESSION_CONNECTED_FAILED STATUS_SIGNALING_BASE + 0x0000004b +#define STATUS_SIGNALING_DESCRIBE_MEDIA_CALL_FAILED STATUS_SIGNALING_BASE + 0x0000004c /*!@} */ @@ -652,6 +660,8 @@ extern "C" { */ #define SIGNALING_DEFAULT_MESSAGE_TTL_VALUE (60 * HUNDREDS_OF_NANOS_IN_A_SECOND) +#define SIGNALING_JOIN_STORAGE_SESSION_WAIT_TIMEOUT (6 * HUNDREDS_OF_NANOS_IN_A_SECOND) + /** * Default jitter buffer tolerated latency, frame will be dropped if it is out of window */ @@ -855,6 +865,10 @@ typedef enum { SIGNALING_CLIENT_STATE_DELETE, //!< This state transition happens when the application calls signalingClientDeleteSync API. SIGNALING_CLIENT_STATE_DELETED, //!< This state transition happens after the channel gets deleted as a result of a signalingClientDeleteSync API. //!< This is a terminal state. + SIGNALING_CLIENT_STATE_DESCRIBE_MEDIA, + SIGNALING_CLIENT_STATE_JOIN_SESSION, + SIGNALING_CLIENT_STATE_JOIN_SESSION_WAITING, + SIGNALING_CLIENT_STATE_JOIN_SESSION_CONNECTED, SIGNALING_CLIENT_STATE_MAX_VALUE, //!< This state indicates maximum number of signaling client states } SIGNALING_CLIENT_STATE, *PSIGNALING_CLIENT_STATE; @@ -910,6 +924,7 @@ typedef enum { //!< information will be cached into file //!< which will allow the cache to persist next time the signaling client is created. } SIGNALING_API_CALL_CACHE_TYPE; + /*!@} */ //////////////////////////////////////////////////// @@ -1263,8 +1278,10 @@ typedef struct { PCHAR pChannelName; //!< Name of the signaling channel name. Maximum length is defined by MAX_CHANNEL_NAME_LEN + 1 - PCHAR pChannelArn; //!< Channel Amazon Resource Name (ARN). This is an optional parameter - //!< Maximum length is defined by MAX_ARN_LEN+1 + PCHAR pChannelArn; //!< Channel Amazon Resource Name (ARN). This is an optional parameter + //!< Maximum length is defined by MAX_ARN_LEN+1 + PCHAR pStorageStreamArn; //!< Storage Stream Amazon Resource Name (ARN). This is an optional parameter + //!< Maximum length is defined by MAX_ARN_LEN+1 PCHAR pRegion; //!< AWS Region in which the channel is to be opened. Can be empty for default //!< Maximum length is defined by MAX_REGION_NAME_LEN+1 @@ -1313,6 +1330,8 @@ typedef struct { //!< is done reactively when needed which will simplify the processing //!< and will help with issues on a small footprint platforms + BOOL useMediaStorage; //!< use the feature of media storage. + } ChannelInfo, *PChannelInfo; /** @@ -1331,6 +1350,11 @@ typedef struct { CHAR userName[MAX_ICE_CONFIG_USER_NAME_LEN + 1]; //!< Username for the server CHAR password[MAX_ICE_CONFIG_CREDENTIAL_LEN + 1]; //!< Password for the server } IceConfigInfo, *PIceConfigInfo; + +typedef struct { + BOOL storageStatus; //!< Indicate the association between channelArn and storageStreamArn + CHAR storageStreamArn[MAX_ARN_LEN + 1]; //!< The arn of kvs stream, optional if you already associate signaling channel with stream +} MediaStorageConfig, *PMediaStorageConfig; /*!@} */ /*! \addtogroup Callbacks diff --git a/src/include/com/amazonaws/kinesis/video/webrtcclient/Stats.h b/src/include/com/amazonaws/kinesis/video/webrtcclient/Stats.h index 39ac7cc438..56b1f6711a 100644 --- a/src/include/com/amazonaws/kinesis/video/webrtcclient/Stats.h +++ b/src/include/com/amazonaws/kinesis/video/webrtcclient/Stats.h @@ -595,15 +595,18 @@ typedef struct { UINT32 apiCallRetryCount; //!< Number of retries due to API call failures in the state machine UINT64 getTokenCallTime; //!< Time (ms) taken to get credentials for signaling UINT64 describeCallTime; //!< Time (ms) taken to execute describeChannel call + UINT64 describeMediaCallTime; //!< Time (ms) taken to execute describeChannel call UINT64 createCallTime; //!< Time (ms) taken to execute createChannel call UINT64 getEndpointCallTime; //!< Time (ms) taken to execute getEndpoint call UINT64 getIceConfigCallTime; //!< Time (ms) taken to execute getIceServerConfig call UINT64 connectCallTime; //!< Time (ms) taken to execute connectChannel call + UINT64 joinSessionCallTime; //!< Time (ms) taken to execute joinSession call UINT64 createClientTime; //!< Total time (ms) taken to create signaling client which includes getting credentials UINT64 fetchClientTime; //!< Total time (ms) taken to fetch signaling client which includes describe, create, get endpoint and get ICE server config UINT64 connectClientTime; //!< Total time (ms) taken to connect the signaling client which includes connecting to the signaling channel UINT64 offerToAnswerTime; + UINT64 joinSessionToOfferRecvTime; //!< Total time (ms) taken from joinSession call until offer is received } SignalingClientStats, *PSignalingClientStats; typedef struct { diff --git a/src/source/Ice/IceAgent.c b/src/source/Ice/IceAgent.c index 2eaa49a2b1..7e3ce47c5e 100644 --- a/src/source/Ice/IceAgent.c +++ b/src/source/Ice/IceAgent.c @@ -1354,7 +1354,8 @@ STATUS iceAgentStateTransitionTimerCallback(UINT32 timerId, UINT64 currentTime, CHK(pIceAgent != NULL, STATUS_NULL_ARG); - // Do not acquire lock because stepIceAgentStateMachine acquires lock. + // Do not acquire lock because from/execute methods + // in state machine acquire lock // Drive the state machine CHK_STATUS(stepIceAgentStateMachine(pIceAgent)); @@ -2144,12 +2145,13 @@ STATUS iceAgentReadyStateSetup(PIceAgent pIceAgent) if (pIceCandidatePair->nominated && pIceCandidatePair->state == ICE_CANDIDATE_PAIR_STATE_SUCCEEDED) { pNominatedAndValidCandidatePair = pIceCandidatePair; + pNominatedAndValidCandidatePair->rtcIceCandidatePairDiagnostics.nominated = pIceCandidatePair->nominated; break; } } CHK(pNominatedAndValidCandidatePair != NULL, STATUS_ICE_NO_NOMINATED_VALID_CANDIDATE_PAIR_AVAILABLE); - + // change the data sending ice candidate pair as the nomination ice candidate pair. pIceAgent->pDataSendingIceCandidatePair = pNominatedAndValidCandidatePair; CHK_STATUS(getIpAddrStr(&pIceAgent->pDataSendingIceCandidatePair->local->ipAddress, ipAddrStr, ARRAY_SIZE(ipAddrStr))); DLOGP("Selected pair %s_%s, local candidate type: %s. remote candidate type: %s. Round trip time %u ms. Local candidate priority: %u, ice " @@ -2402,7 +2404,7 @@ STATUS iceCandidateSerialize(PIceCandidate pIceCandidate, PCHAR pOutputData, PUI if (pOutputData == NULL) { *pOutputLength = ((UINT32) amountWritten) + 1; // +1 for null terminator } else { - // amountWritten doesnt account for null char + // amountWritten doesn't account for null char CHK(amountWritten < (INT32) *pOutputLength, STATUS_BUFFER_TOO_SMALL); } @@ -2466,8 +2468,9 @@ STATUS handleStunPacket(PIceAgent pIceAgent, PBYTE pBuffer, UINT32 bufferLen, PS if (!pIceCandidatePair->nominated) { CHK_STATUS(getStunAttribute(pStunPacket, STUN_ATTRIBUTE_TYPE_USE_CANDIDATE, &pStunAttr)); if (pStunAttr != NULL) { - DLOGD("received candidate with USE_CANDIDATE flag, local candidate type %s.", - iceAgentGetCandidateTypeStr(pIceCandidatePair->local->iceCandidateType)); + DLOGD("received candidate with USE_CANDIDATE flag, local candidate type %s(%s:%s).", + iceAgentGetCandidateTypeStr(pIceCandidatePair->local->iceCandidateType), pIceCandidatePair->local->id, + pIceCandidatePair->remote->id); pIceCandidatePair->nominated = TRUE; } } @@ -2482,6 +2485,8 @@ STATUS handleStunPacket(PIceAgent pIceAgent, PBYTE pBuffer, UINT32 bufferLen, PS pIceAgent->pDataSendingIceCandidatePair->rtcIceCandidatePairDiagnostics.requestsReceived += connectivityCheckRequestsReceived; pIceAgent->pDataSendingIceCandidatePair->rtcIceCandidatePairDiagnostics.responsesSent += connectivityCheckResponsesSent; pIceAgent->pDataSendingIceCandidatePair->rtcIceCandidatePairDiagnostics.nominated = pIceCandidatePair->nominated; + } else { + DLOGD("going to change the data sending ice candidate pair."); } break; @@ -2606,6 +2611,19 @@ STATUS handleStunPacket(PIceAgent pIceAgent, PBYTE pBuffer, UINT32 bufferLen, PS CHK(hexStr != NULL, STATUS_NOT_ENOUGH_MEMORY); CHK_STATUS(hexEncode(pBuffer, bufferLen, hexStr, &hexStrLen)); DLOGW("Error STUN packet. Packet type: 0x%02x. Packet content: \n\t%s", stunPacketType, hexStr); + if (stunPacketType == STUN_PACKET_TYPE_BINDING_RESPONSE_ERROR) { + CHK_STATUS( + findIceCandidatePairWithLocalSocketConnectionAndRemoteAddr(pIceAgent, pSocketConnection, pSrcAddr, TRUE, &pIceCandidatePair)); + if (pIceCandidatePair == NULL) { + CHK_STATUS(getIpAddrStr(pSrcAddr, ipAddrStr, ARRAY_SIZE(ipAddrStr))); + CHK_STATUS(getIpAddrStr(&pSocketConnection->hostIpAddr, ipAddrStr2, ARRAY_SIZE(ipAddrStr2))); + CHK_WARN( + FALSE, retStatus, + "ERROR cannot find candidate pair with local candidate %s and remote candidate %s. Dropping STUN binding error response", + ipAddrStr2, ipAddrStr); + } + DLOGW("Error binding response! %s %s", pIceCandidatePair->local->id, pIceCandidatePair->remote->id); + } SAFE_MEMFREE(hexStr); } break; diff --git a/src/source/Ice/IceAgentStateMachine.c b/src/source/Ice/IceAgentStateMachine.c index 54b48a7980..9cef8d59d8 100644 --- a/src/source/Ice/IceAgentStateMachine.c +++ b/src/source/Ice/IceAgentStateMachine.c @@ -33,6 +33,7 @@ STATUS stepIceAgentStateMachine(PIceAgent pIceAgent) { ENTERS(); STATUS retStatus = STATUS_SUCCESS; + STATUS iceAgentStatus = STATUS_SUCCESS; UINT64 oldState; CHK(pIceAgent != NULL, STATUS_NULL_ARG); @@ -41,8 +42,12 @@ STATUS stepIceAgentStateMachine(PIceAgent pIceAgent) CHK_STATUS(stepStateMachine(pIceAgent->pStateMachine)); + MUTEX_LOCK(pIceAgent->lock); + iceAgentStatus = pIceAgent->iceAgentStatus; + MUTEX_UNLOCK(pIceAgent->lock); + // if any failure happened and state machine is not in failed state, stepStateMachine again into failed state. - if (pIceAgent->iceAgentState != ICE_AGENT_STATE_FAILED && STATUS_FAILED(pIceAgent->iceAgentStatus)) { + if (pIceAgent->iceAgentState != ICE_AGENT_STATE_FAILED && STATUS_FAILED(iceAgentStatus)) { CHK_STATUS(stepStateMachine(pIceAgent->pStateMachine)); } @@ -103,6 +108,7 @@ STATUS iceAgentStateMachineCheckDisconnection(PIceAgent pIceAgent, PUINT64 pNext currentTime = GETTIME(); if (!pIceAgent->detectedDisconnection && IS_VALID_TIMESTAMP(pIceAgent->lastDataReceivedTime) && pIceAgent->lastDataReceivedTime + KVS_ICE_ENTER_STATE_DISCONNECTION_GRACE_PERIOD <= currentTime) { + DLOGD("detect disconnection"); *pNextState = ICE_AGENT_STATE_DISCONNECTED; } else if (pIceAgent->detectedDisconnection) { if (IS_VALID_TIMESTAMP(pIceAgent->lastDataReceivedTime) && @@ -280,7 +286,9 @@ STATUS executeCheckConnectionIceAgentState(UINT64 customData, UINT64 time) CleanUp: if (STATUS_FAILED(retStatus)) { + MUTEX_LOCK(pIceAgent->lock); pIceAgent->iceAgentStatus = retStatus; + MUTEX_UNLOCK(pIceAgent->lock); // fix up retStatus so we can successfully transition to failed state. retStatus = STATUS_SUCCESS; @@ -353,7 +361,9 @@ STATUS executeConnectedIceAgentState(UINT64 customData, UINT64 time) CleanUp: if (STATUS_FAILED(retStatus)) { + MUTEX_LOCK(pIceAgent->lock); pIceAgent->iceAgentStatus = retStatus; + MUTEX_UNLOCK(pIceAgent->lock); // fix up retStatus so we can successfully transition to failed state. retStatus = STATUS_SUCCESS; @@ -452,7 +462,9 @@ STATUS executeNominatingIceAgentState(UINT64 customData, UINT64 time) CleanUp: if (STATUS_FAILED(retStatus)) { + MUTEX_LOCK(pIceAgent->lock); pIceAgent->iceAgentStatus = retStatus; + MUTEX_UNLOCK(pIceAgent->lock); // fix up retStatus so we can successfully transition to failed state. retStatus = STATUS_SUCCESS; @@ -538,7 +550,9 @@ STATUS executeReadyIceAgentState(UINT64 customData, UINT64 time) CleanUp: if (STATUS_FAILED(retStatus)) { + MUTEX_LOCK(pIceAgent->lock); pIceAgent->iceAgentStatus = retStatus; + MUTEX_UNLOCK(pIceAgent->lock); // fix up retStatus so we can successfully transition to failed state. retStatus = STATUS_SUCCESS; @@ -621,7 +635,9 @@ STATUS executeDisconnectedIceAgentState(UINT64 customData, UINT64 time) CleanUp: if (STATUS_FAILED(retStatus)) { + MUTEX_LOCK(pIceAgent->lock); pIceAgent->iceAgentStatus = retStatus; + MUTEX_UNLOCK(pIceAgent->lock); // fix up retStatus so we can successfully transition to failed state. retStatus = STATUS_SUCCESS; @@ -653,6 +669,7 @@ STATUS executeFailedIceAgentState(UINT64 customData, UINT64 time) ENTERS(); UNUSED_PARAM(time); STATUS retStatus = STATUS_SUCCESS; + STATUS iceAgentStatus = STATUS_SUCCESS; PIceAgent pIceAgent = (PIceAgent) customData; const PCHAR errMsgPrefix = (PCHAR) "IceAgent fatal error:"; @@ -661,13 +678,17 @@ STATUS executeFailedIceAgentState(UINT64 customData, UINT64 time) pIceAgent->iceAgentState = ICE_AGENT_STATE_FAILED; + MUTEX_LOCK(pIceAgent->lock); + iceAgentStatus = pIceAgent->iceAgentStatus; + MUTEX_UNLOCK(pIceAgent->lock); + // log some debug info about the failure once. - switch (pIceAgent->iceAgentStatus) { + switch (iceAgentStatus) { case STATUS_ICE_NO_AVAILABLE_ICE_CANDIDATE_PAIR: DLOGE("%s No ice candidate pairs available to make connection.", errMsgPrefix); break; default: - DLOGE("IceAgent failed with 0x%08x", pIceAgent->iceAgentStatus); + DLOGE("IceAgent failed with 0x%08x", iceAgentStatus); break; } diff --git a/src/source/Ice/NatBehaviorDiscovery.c b/src/source/Ice/NatBehaviorDiscovery.c index 1e350a894f..a0b70e4721 100644 --- a/src/source/Ice/NatBehaviorDiscovery.c +++ b/src/source/Ice/NatBehaviorDiscovery.c @@ -23,7 +23,7 @@ STATUS natTestIncomingDataHandler(UINT64 customData, PSocketConnection pSocketCo CleanUp: - return STATUS_SUCCESS; + return retStatus; } /* diff --git a/src/source/Ice/Network.c b/src/source/Ice/Network.c index 8656f15dfc..318e8e1a73 100644 --- a/src/source/Ice/Network.c +++ b/src/source/Ice/Network.c @@ -174,16 +174,17 @@ STATUS createSocket(KVS_IP_FAMILY_TYPE familyType, KVS_SOCKET_PROTOCOL protocol, sockType = protocol == KVS_SOCKET_PROTOCOL_UDP ? SOCK_DGRAM : SOCK_STREAM; sockfd = socket(familyType == KVS_IP_FAMILY_TYPE_IPV4 ? AF_INET : AF_INET6, sockType, 0); + if (sockfd == -1) { DLOGW("socket() failed to create socket with errno %s", getErrorString(getErrorCode())); CHK(FALSE, STATUS_CREATE_UDP_SOCKET_FAILED); } - +#ifdef NO_SIGNAL_SOCK_OPT optionValue = 1; - if (setsockopt(sockfd, SOL_SOCKET, NO_SIGNAL, &optionValue, SIZEOF(optionValue)) < 0) { - DLOGD("setsockopt() NO_SIGNAL failed with errno %s", getErrorString(getErrorCode())); + if (setsockopt(sockfd, SOL_SOCKET, NO_SIGNAL_SOCK_OPT, &optionValue, SIZEOF(optionValue)) < 0) { + DLOGD("setsockopt() NO_SIGNAL_SOCK_OPT failed with errno %s", getErrorString(getErrorCode())); } - +#endif /* NO_SIGNAL_SOCK_OPT */ if (sendBufSize > 0 && setsockopt(sockfd, SOL_SOCKET, SO_SNDBUF, &sendBufSize, SIZEOF(sendBufSize)) < 0) { DLOGW("setsockopt() SO_SNDBUF failed with errno %s", getErrorString(getErrorCode())); CHK(FALSE, STATUS_SOCKET_SET_SEND_BUFFER_SIZE_FAILED); diff --git a/src/source/Ice/Network.h b/src/source/Ice/Network.h index 93d1f79678..80fb745160 100644 --- a/src/source/Ice/Network.h +++ b/src/source/Ice/Network.h @@ -26,12 +26,13 @@ extern "C" { #define IPV6_TEMPLATE "%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x" #if defined(__MACH__) -#define NO_SIGNAL SO_NOSIGPIPE +#define NO_SIGNAL_SOCK_OPT SO_NOSIGPIPE +#define NO_SIGNAL_SEND 0 #else -#define NO_SIGNAL MSG_NOSIGNAL +#define NO_SIGNAL_SEND MSG_NOSIGNAL #endif -// Some systems such as Windows doesn't have this value +// Some systems, such as Windows, do not have this value #ifndef EAI_SYSTEM #define EAI_SYSTEM -11 #endif diff --git a/src/source/Ice/SocketConnection.c b/src/source/Ice/SocketConnection.c index 7d3fb02719..f464c9c876 100644 --- a/src/source/Ice/SocketConnection.c +++ b/src/source/Ice/SocketConnection.c @@ -11,6 +11,7 @@ STATUS createSocketConnection(KVS_IP_FAMILY_TYPE familyType, KVS_SOCKET_PROTOCOL ENTERS(); STATUS retStatus = STATUS_SUCCESS; PSocketConnection pSocketConnection = NULL; + CHAR ipAddr[KVS_IP_ADDRESS_STRING_BUFFER_LEN]; CHK(ppSocketConnection != NULL, STATUS_NULL_ARG); CHK(protocol == KVS_SOCKET_PROTOCOL_UDP || pPeerIpAddr != NULL, STATUS_INVALID_ARG); @@ -33,6 +34,7 @@ STATUS createSocketConnection(KVS_IP_FAMILY_TYPE familyType, KVS_SOCKET_PROTOCOL pSocketConnection->peerIpAddr = *pPeerIpAddr; CHK_STATUS(socketConnect(pPeerIpAddr, pSocketConnection->localSocket)); } + ATOMIC_STORE_BOOL(&pSocketConnection->connectionClosed, FALSE); ATOMIC_STORE_BOOL(&pSocketConnection->receiveData, FALSE); ATOMIC_STORE_BOOL(&pSocketConnection->inUse, FALSE); @@ -43,6 +45,17 @@ STATUS createSocketConnection(KVS_IP_FAMILY_TYPE familyType, KVS_SOCKET_PROTOCOL CHK_LOG_ERR(retStatus); + if (pBindAddr) { + getIpAddrStr(pBindAddr, ipAddr, ARRAY_SIZE(ipAddr)); + DLOGD("create socket with ip: %s:%u. family:%d", ipAddr, (UINT16) getInt16(pBindAddr->port), pBindAddr->family); + } else { + DLOGD("create socket without the bind address(%d:%d)", familyType, protocol); + } + if (protocol == KVS_SOCKET_PROTOCOL_TCP) { + getIpAddrStr(pPeerIpAddr, ipAddr, ARRAY_SIZE(ipAddr)); + DLOGD("tcp socket connected with ip: %s:%u. family:%d", ipAddr, (UINT16) getInt16(pPeerIpAddr->port), pPeerIpAddr->family); + } + if (STATUS_FAILED(retStatus) && pSocketConnection != NULL) { freeSocketConnection(&pSocketConnection); pSocketConnection = NULL; @@ -62,6 +75,7 @@ STATUS freeSocketConnection(PSocketConnection* ppSocketConnection) STATUS retStatus = STATUS_SUCCESS; PSocketConnection pSocketConnection = NULL; UINT64 shutdownTimeout; + CHAR ipAddr[KVS_IP_ADDRESS_STRING_BUFFER_LEN]; CHK(ppSocketConnection != NULL, STATUS_NULL_ARG); pSocketConnection = *ppSocketConnection; @@ -91,6 +105,14 @@ STATUS freeSocketConnection(PSocketConnection* ppSocketConnection) freeTlsSession(&pSocketConnection->pTlsSession); } + getIpAddrStr(&pSocketConnection->hostIpAddr, ipAddr, ARRAY_SIZE(ipAddr)); + DLOGD("close socket with ip: %s:%u. family:%d", ipAddr, (UINT16) getInt16(pSocketConnection->hostIpAddr.port), + pSocketConnection->hostIpAddr.family); + + getIpAddrStr(&pSocketConnection->peerIpAddr, ipAddr, ARRAY_SIZE(ipAddr)); + DLOGD("close socket connected with ip: %s:%u. family:%d", ipAddr, (UINT16) getInt16(pSocketConnection->peerIpAddr.port), + pSocketConnection->peerIpAddr.family); + if (STATUS_FAILED(retStatus = closeSocket(pSocketConnection->localSocket))) { DLOGW("Failed to close the local socket with 0x%08x", retStatus); } @@ -285,6 +307,8 @@ BOOL socketConnectionIsConnected(PSocketConnection pSocketConnection) socklen_t addrLen; struct sockaddr_in ipv4PeerAddr; struct sockaddr_in6 ipv6PeerAddr; + CHAR hostIpAddr[KVS_IP_ADDRESS_STRING_BUFFER_LEN]; + CHAR peerIpAddr[KVS_IP_ADDRESS_STRING_BUFFER_LEN]; CHECK(pSocketConnection != NULL); @@ -312,11 +336,17 @@ BOOL socketConnectionIsConnected(PSocketConnection pSocketConnection) retVal = connect(pSocketConnection->localSocket, peerSockAddr, addrLen); MUTEX_UNLOCK(pSocketConnection->lock); + getIpAddrStr(&pSocketConnection->hostIpAddr, hostIpAddr, ARRAY_SIZE(hostIpAddr)); + getIpAddrStr(&pSocketConnection->peerIpAddr, peerIpAddr, ARRAY_SIZE(peerIpAddr)); + DLOGD("connect ip: %s:%u. family:%d with ip: %s:%u. family:%d", hostIpAddr, (UINT16) getInt16(pSocketConnection->hostIpAddr.port), + pSocketConnection->hostIpAddr.family, peerIpAddr, (UINT16) getInt16(pSocketConnection->peerIpAddr.port), + pSocketConnection->peerIpAddr.family); + if (retVal == 0 || getErrorCode() == EISCONN) { return TRUE; } - DLOGW("socket connection check failed with errno %s", getErrorString(getErrorCode())); + DLOGW("socket connection check failed with errno %s(%d)", getErrorString(getErrorCode()), getErrorCode()); return FALSE; } @@ -357,7 +387,7 @@ STATUS socketSendDataWithRetry(PSocketConnection pSocketConnection, PBYTE buf, U } while (socketWriteAttempt < MAX_SOCKET_WRITE_RETRY && bytesWritten < bufLen) { - result = sendto(pSocketConnection->localSocket, buf + bytesWritten, bufLen - bytesWritten, NO_SIGNAL, destAddr, addrLen); + result = sendto(pSocketConnection->localSocket, buf + bytesWritten, bufLen - bytesWritten, NO_SIGNAL_SEND, destAddr, addrLen); if (result < 0) { errorNum = getErrorCode(); if (errorNum == EAGAIN || errorNum == EWOULDBLOCK) { @@ -378,7 +408,19 @@ STATUS socketSendDataWithRetry(PSocketConnection pSocketConnection, PBYTE buf, U /* nothing need to be done, just retry */ } else { /* fatal error from send() */ - DLOGE("sendto() failed with errno %s", getErrorString(errorNum)); + DLOGE("sendto() failed with errno %s(%d)", getErrorString(errorNum), errorNum); + CHAR ipAddr[KVS_IP_ADDRESS_STRING_BUFFER_LEN]; + + if (pDestIp != NULL) { + getIpAddrStr(pDestIp, ipAddr, ARRAY_SIZE(ipAddr)); + DLOGD("Dest Ip: %s:%u. family:%d", ipAddr, (UINT16) getInt16(pDestIp->port), pDestIp->family); + } else { + DLOGD("TCP dest IP"); + } + + getIpAddrStr(&pSocketConnection->hostIpAddr, ipAddr, ARRAY_SIZE(ipAddr)); + DLOGD("hostIpAddr Ip: %s:%u. family:%d", ipAddr, (UINT16) getInt16(pSocketConnection->hostIpAddr.port), + pSocketConnection->hostIpAddr.family); break; } diff --git a/src/source/Ice/TurnConnection.c b/src/source/Ice/TurnConnection.c index 460211700e..50d6ef65fc 100644 --- a/src/source/Ice/TurnConnection.c +++ b/src/source/Ice/TurnConnection.c @@ -416,15 +416,23 @@ STATUS turnConnectionHandleStunError(PTurnConnection pTurnConnection, PBYTE pBuf /* Remove peer for any other error */ DLOGW("Received STUN error response. Error type: 0x%02x, Error Code: %u. attribute len %u, Error detail: %s.", stunPacketType, pStunAttributeErrorCode->errorCode, pStunAttributeErrorCode->attribute.length, pStunAttributeErrorCode->errorPhrase); - + BOOL found = FALSE; /* Find TurnPeer using transaction Id, then mark it as failed */ for (i = 0; iterate && i < pTurnConnection->turnPeerCount; ++i) { pTurnPeer = &pTurnConnection->turnPeerList[i]; if (transactionIdStoreHasId(pTurnPeer->pTransactionIdStore, pBuffer + STUN_PACKET_TRANSACTION_ID_OFFSET)) { + CHAR ipAddr[KVS_IP_ADDRESS_STRING_BUFFER_LEN]; pTurnPeer->connectionState = TURN_PEER_CONN_STATE_FAILED; iterate = FALSE; + found = TRUE; + getIpAddrStr(&pTurnPeer->address, ipAddr, ARRAY_SIZE(ipAddr)); + DLOGD("remove turn peer with ip: %s:%u. family:%d", ipAddr, (UINT16) getInt16(pTurnPeer->address.port), + pTurnPeer->address.family); } } + if (found == FALSE) { + DLOGD("can not find any corresponding turn peer"); + } break; } diff --git a/src/source/PeerConnection/PeerConnection.c b/src/source/PeerConnection/PeerConnection.c index 5da590b125..3d10070a3c 100644 --- a/src/source/PeerConnection/PeerConnection.c +++ b/src/source/PeerConnection/PeerConnection.c @@ -93,8 +93,8 @@ STATUS allocateSctp(PKvsPeerConnection pKvsPeerConnection) CHK(FALSE, retStatus); } CHK(pKvsDataChannel != NULL, STATUS_INTERNAL_ERROR); - sctpSessionWriteDcep(pKvsPeerConnection->pSctpSession, currentDataChannelId, pKvsDataChannel->dataChannel.name, - STRLEN(pKvsDataChannel->dataChannel.name), &pKvsDataChannel->rtcDataChannelInit); + CHK_STATUS(sctpSessionWriteDcep(pKvsPeerConnection->pSctpSession, currentDataChannelId, pKvsDataChannel->dataChannel.name, + STRLEN(pKvsDataChannel->dataChannel.name), &pKvsDataChannel->rtcDataChannelInit)); pKvsDataChannel->rtcDataChannelDiagnostics.state = RTC_DATA_CHANNEL_STATE_OPEN; if (STATUS_FAILED(hashTableUpsert(pKvsPeerConnection->pDataChannels, currentDataChannelId, (UINT64) pKvsDataChannel))) { DLOGW("Failed to update entry in hash table with recent changes to data channel"); @@ -133,7 +133,7 @@ VOID onInboundPacket(UINT64 customData, PBYTE buff, UINT32 buffLen) dtlsSessionProcessPacket(pKvsPeerConnection->pDtlsSession, buff, &signedBuffLen); #ifdef ENABLE_DATA_CHANNEL - if (signedBuffLen > 0) { + if (ATOMIC_LOAD_BOOL(&pKvsPeerConnection->sctpIsEnabled) && signedBuffLen > 0) { CHK_STATUS(putSctpPacket(pKvsPeerConnection->pSctpSession, buff, signedBuffLen)); } #endif @@ -145,8 +145,14 @@ VOID onInboundPacket(UINT64 customData, PBYTE buff, UINT32 buffLen) } #ifdef ENABLE_DATA_CHANNEL - if (pKvsPeerConnection->pSctpSession == NULL) { - CHK_STATUS(allocateSctp(pKvsPeerConnection)); + if (ATOMIC_LOAD_BOOL(&pKvsPeerConnection->sctpIsEnabled)) { + if (pKvsPeerConnection->pSctpSession == NULL) { + CHK_STATUS(allocateSctp(pKvsPeerConnection)); + } + + if (signedBuffLen > 0) { + CHK_STATUS(putSctpPacket(pKvsPeerConnection->pSctpSession, buff, signedBuffLen)); + } } #endif changePeerConnectionState(pKvsPeerConnection, RTC_PEER_CONNECTION_STATE_CONNECTED); @@ -425,6 +431,7 @@ VOID onIceConnectionStateChange(UINT64 customData, UINT64 connectionState) break; case ICE_AGENT_STATE_DISCONNECTED: + DLOGD("ice agent disconnected"); newConnectionState = RTC_PEER_CONNECTION_STATE_DISCONNECTED; break; @@ -732,7 +739,7 @@ STATUS createPeerConnection(PRtcConfiguration pConfiguration, PRtcPeerConnection pKvsPeerConnection->MTU = pConfiguration->kvsRtcConfiguration.maximumTransmissionUnit == 0 ? DEFAULT_MTU_SIZE : pConfiguration->kvsRtcConfiguration.maximumTransmissionUnit; - pKvsPeerConnection->sctpIsEnabled = FALSE; + ATOMIC_STORE_BOOL(&pKvsPeerConnection->sctpIsEnabled, FALSE); iceAgentCallbacks.customData = (UINT64) pKvsPeerConnection; iceAgentCallbacks.inboundPacketFn = onInboundPacket; @@ -1085,7 +1092,9 @@ STATUS setRemoteDescription(PRtcPeerConnection pPeerConnection, PRtcSessionDescr for (i = 0; i < pSessionDescription->mediaCount; i++) { #ifdef ENABLE_DATA_CHANNEL if (STRNCMP(pSessionDescription->mediaDescriptions[i].mediaName, "application", SIZEOF("application") - 1) == 0) { - pKvsPeerConnection->sctpIsEnabled = TRUE; + if (!pKvsPeerConnection->isOffer && !ATOMIC_LOAD_BOOL(&pKvsPeerConnection->sctpIsEnabled)) { + ATOMIC_STORE_BOOL(&pKvsPeerConnection->sctpIsEnabled, TRUE); + } } #endif @@ -1168,8 +1177,9 @@ STATUS createOffer(PRtcPeerConnection pPeerConnection, PRtcSessionDescriptionIni pKvsPeerConnection->isOffer = TRUE; #ifdef ENABLE_DATA_CHANNEL - pKvsPeerConnection->sctpIsEnabled = TRUE; + ATOMIC_STORE_BOOL(&pKvsPeerConnection->sctpIsEnabled, TRUE); #endif + CHK_STATUS(setPayloadTypesForOffer(pKvsPeerConnection->pCodecTable)); CHK_STATUS(populateSessionDescription(pKvsPeerConnection, &(pKvsPeerConnection->remoteSessionDescription), pSessionDescription)); @@ -1201,6 +1211,7 @@ STATUS createAnswer(PRtcPeerConnection pPeerConnection, PRtcSessionDescriptionIn CHK(pKvsPeerConnection->remoteSessionDescription.sessionName[0] != '\0', STATUS_PEERCONNECTION_CREATE_ANSWER_WITHOUT_REMOTE_DESCRIPTION); pSessionDescriptionInit->type = SDP_TYPE_ANSWER; + pKvsPeerConnection->isOffer = FALSE; CHK_STATUS(peerConnectionGetCurrentLocalDescription(pPeerConnection, pSessionDescriptionInit)); @@ -1340,7 +1351,7 @@ STATUS addIceCandidate(PRtcPeerConnection pPeerConnection, PCHAR pIceCandidate) PKvsPeerConnection pKvsPeerConnection = (PKvsPeerConnection) pPeerConnection; CHK(pKvsPeerConnection != NULL && pIceCandidate != NULL, STATUS_NULL_ARG); - + DLOGD("%s", pIceCandidate); iceAgentAddRemoteCandidate(pKvsPeerConnection->pIceAgent, pIceCandidate); CleanUp: diff --git a/src/source/PeerConnection/PeerConnection.h b/src/source/PeerConnection/PeerConnection.h index 67cfe713d8..d473809da5 100644 --- a/src/source/PeerConnection/PeerConnection.h +++ b/src/source/PeerConnection/PeerConnection.h @@ -88,7 +88,7 @@ typedef struct { PDoubleList pFakeTransceivers; PDoubleList pAnswerTransceivers; - BOOL sctpIsEnabled; + volatile ATOMIC_BOOL sctpIsEnabled; CHAR localIceUfrag[LOCAL_ICE_UFRAG_LEN + 1]; CHAR localIcePwd[LOCAL_ICE_PWD_LEN + 1]; diff --git a/src/source/PeerConnection/Rtcp.c b/src/source/PeerConnection/Rtcp.c index 0aa4a0166e..bbf6e73927 100644 --- a/src/source/PeerConnection/Rtcp.c +++ b/src/source/PeerConnection/Rtcp.c @@ -340,7 +340,7 @@ STATUS onRtcpPacket(PKvsPeerConnection pKvsPeerConnection, PBYTE pBuff, UINT32 b UINT32 currentOffset = 0; CHK(pKvsPeerConnection != NULL && pBuff != NULL, STATUS_NULL_ARG); - + DLOGD("**rtcp**"); while (currentOffset < buffLen) { CHK_STATUS(setRtcpPacketFromBytes(pBuff + currentOffset, buffLen - currentOffset, &rtcpPacket)); diff --git a/src/source/PeerConnection/Rtcp.h b/src/source/PeerConnection/Rtcp.h index 513d29f0b8..229cebbe3f 100644 --- a/src/source/PeerConnection/Rtcp.h +++ b/src/source/PeerConnection/Rtcp.h @@ -31,7 +31,7 @@ typedef enum { #define TWCC_FB_PACKETCHUNK_SIZE 2 #define IS_TWCC_RUNLEN(packetChunk) ((((packetChunk) >> 15u) & 1u) == 0) #define TWCC_RUNLEN_STATUS_SYMBOL(packetChunk) (((packetChunk) >> 13u) & 3u) -#define TWCC_RUNLEN_GET(packetChunk) ((packetChunk) &0x1fffu) +#define TWCC_RUNLEN_GET(packetChunk) ((packetChunk) & 0x1fffu) #define TWCC_IS_NOTRECEIVED(statusSymbol) ((statusSymbol) == TWCC_STATUS_SYMBOL_NOTRECEIVED) #define TWCC_ISRECEIVED(statusSymbol) ((statusSymbol) == TWCC_STATUS_SYMBOL_SMALLDELTA || (statusSymbol) == TWCC_STATUS_SYMBOL_LARGEDELTA) #define TWCC_RUNLEN_ISRECEIVED(packetChunk) TWCC_ISRECEIVED(TWCC_RUNLEN_STATUS_SYMBOL(packetChunk)) @@ -39,7 +39,7 @@ typedef enum { #define TWCC_STATUSVECTOR_SSIZE(packetChunk) (TWCC_STATUSVECTOR_IS_2BIT(packetChunk) ? 2u : 1u) #define TWCC_STATUSVECTOR_SMASK(packetChunk) (TWCC_STATUSVECTOR_IS_2BIT(packetChunk) ? 2u : 1u) #define TWCC_STATUSVECTOR_STATUS(packetChunk, i) \ - (((packetChunk) >> (14u - (i) *TWCC_STATUSVECTOR_SSIZE(packetChunk))) & TWCC_STATUSVECTOR_SMASK(packetChunk)) + (((packetChunk) >> (14u - (i) * TWCC_STATUSVECTOR_SSIZE(packetChunk))) & TWCC_STATUSVECTOR_SMASK(packetChunk)) #define TWCC_STATUSVECTOR_COUNT(packetChunk) (TWCC_STATUSVECTOR_IS_2BIT(packetChunk) ? 7 : 14) #define TWCC_PACKET_STATUS_COUNT(payload) (getUnalignedInt16BigEndian((payload) + 10)) diff --git a/src/source/PeerConnection/SessionDescription.c b/src/source/PeerConnection/SessionDescription.c index a3fedc2cc4..e14f2150fe 100644 --- a/src/source/PeerConnection/SessionDescription.c +++ b/src/source/PeerConnection/SessionDescription.c @@ -399,6 +399,9 @@ STATUS populateSingleMediaSection(PKvsPeerConnection pKvsPeerConnection, PKvsRtp PRtcMediaStreamTrack pRtcMediaStreamTrack = &(pKvsRtpTransceiver->sender.track); PSdpMediaDescription pSdpMediaDescriptionRemote; PCHAR currentFmtp = NULL, rtpMapValue = NULL; + CHAR remoteSdpAttributeValue[MAX_SDP_ATTRIBUTE_VALUE_LENGTH]; + + MEMSET(remoteSdpAttributeValue, 0, MAX_SDP_ATTRIBUTE_VALUE_LENGTH); if (pRtcMediaStreamTrack->codec == RTC_CODEC_UNKNOWN && pUnknownCodecPayloadTypesTable != NULL) { CHK_STATUS(hashTableGet(pUnknownCodecPayloadTypesTable, unknownCodecHashTableKey, &payloadType)); @@ -516,7 +519,22 @@ STATUS populateSingleMediaSection(PKvsPeerConnection pKvsPeerConnection, PKvsRtp attributeCount++; STRCPY(pSdpMediaDescription->sdpAttributes[attributeCount].attributeName, "mid"); - SPRINTF(pSdpMediaDescription->sdpAttributes[attributeCount].attributeValue, "%d", mediaSectionId); + // check all session attribute lines to see if a line with mid is present. If it is present, copy its content and break + for (i = 0; i < pRemoteSessionDescription->mediaDescriptions[mediaSectionId].mediaAttributesCount; i++) { + if (STRCMP(pRemoteSessionDescription->mediaDescriptions[mediaSectionId].sdpAttributes[i].attributeName, MID_KEY) == 0) { + STRCPY(remoteSdpAttributeValue, pRemoteSessionDescription->mediaDescriptions[mediaSectionId].sdpAttributes[i].attributeValue); + break; + } + } + + // check if we already have a value for the "mid" session attribute from remote description. If we have it, we use it. + // If we don't have it, we loop over, create and add them + if (STRLEN(remoteSdpAttributeValue) > 0) { + CHK(STRLEN(remoteSdpAttributeValue) < MAX_SDP_ATTRIBUTE_VALUE_LENGTH, STATUS_BUFFER_TOO_SMALL); + SPRINTF(pSdpMediaDescription->sdpAttributes[attributeCount].attributeValue, "%s", remoteSdpAttributeValue); + } else { + SPRINTF(pSdpMediaDescription->sdpAttributes[attributeCount].attributeValue, "%d", mediaSectionId); + } attributeCount++; if (pKvsPeerConnection->isOffer) { @@ -563,10 +581,13 @@ STATUS populateSingleMediaSection(PKvsPeerConnection pKvsPeerConnection, PKvsRtp STRCPY(pSdpMediaDescription->sdpAttributes[attributeCount].attributeName, "rtcp-mux"); attributeCount++; - STRCPY(pSdpMediaDescription->sdpAttributes[attributeCount].attributeName, "rtcp-rsize"); - attributeCount++; + if (mediaSectionId != 0) { + STRCPY(pSdpMediaDescription->sdpAttributes[attributeCount].attributeName, "rtcp-rsize"); + attributeCount++; + } if (pRtcMediaStreamTrack->codec == RTC_CODEC_H264_PROFILE_42E01F_LEVEL_ASYMMETRY_ALLOWED_PACKETIZATION_MODE) { + // TODO: Need additional condition for a signaling channel with an ENABLED media storage configuration if (pKvsPeerConnection->isOffer) { currentFmtp = DEFAULT_H264_FMTP; } @@ -574,6 +595,11 @@ STATUS populateSingleMediaSection(PKvsPeerConnection pKvsPeerConnection, PKvsRtp SPRINTF(pSdpMediaDescription->sdpAttributes[attributeCount].attributeValue, "%" PRId64 " H264/90000", payloadType); attributeCount++; + STRCPY(pSdpMediaDescription->sdpAttributes[attributeCount].attributeName, "rtcp-fb"); + SPRINTF(pSdpMediaDescription->sdpAttributes[attributeCount].attributeValue, "%" PRId64 " nack", payloadType); + SPRINTF(pSdpMediaDescription->sdpAttributes[attributeCount].attributeValue, "%" PRId64 " nack pli", payloadType); + attributeCount++; + // TODO: If level asymmetry is allowed, consider sending back DEFAULT_H264_FMTP instead of the received fmtp value. if (currentFmtp != NULL) { STRCPY(pSdpMediaDescription->sdpAttributes[attributeCount].attributeName, "fmtp"); @@ -594,6 +620,7 @@ STATUS populateSingleMediaSection(PKvsPeerConnection pKvsPeerConnection, PKvsRtp if (pKvsPeerConnection->isOffer) { currentFmtp = DEFAULT_OPUS_FMTP; } + STRCPY(pSdpMediaDescription->sdpAttributes[attributeCount].attributeName, "rtpmap"); SPRINTF(pSdpMediaDescription->sdpAttributes[attributeCount].attributeValue, "%" PRId64 " opus/48000/2", payloadType); attributeCount++; @@ -633,8 +660,14 @@ STATUS populateSingleMediaSection(PKvsPeerConnection pKvsPeerConnection, PKvsRtp attributeCount++; } - STRCPY(pSdpMediaDescription->sdpAttributes[attributeCount].attributeName, "rtcp-fb"); - SPRINTF(pSdpMediaDescription->sdpAttributes[attributeCount].attributeValue, "%" PRId64 " nack", payloadType); + STRCPY(pSdpMediaDescription->sdpAttributes[attributeCount].attributeName, "ssrc"); + SPRINTF(pSdpMediaDescription->sdpAttributes[attributeCount].attributeValue, "%u cname:%s", pKvsRtpTransceiver->sender.ssrc, + pKvsPeerConnection->localCNAME); + attributeCount++; + + STRCPY(pSdpMediaDescription->sdpAttributes[attributeCount].attributeName, "ssrc"); + SPRINTF(pSdpMediaDescription->sdpAttributes[attributeCount].attributeValue, "%u msid:%s %s", pKvsRtpTransceiver->sender.ssrc, + pRtcMediaStreamTrack->streamId, pRtcMediaStreamTrack->trackId); attributeCount++; STRCPY(pSdpMediaDescription->sdpAttributes[attributeCount].attributeName, "rtcp-fb"); @@ -821,7 +854,7 @@ STATUS populateSessionDescriptionMedia(PKvsPeerConnection pKvsPeerConnection, PS } } - if (pKvsPeerConnection->sctpIsEnabled) { + if (ATOMIC_LOAD_BOOL(&pKvsPeerConnection->sctpIsEnabled)) { CHK(pLocalSessionDescription->mediaCount < MAX_SDP_SESSION_MEDIA_COUNT, STATUS_SESSION_DESCRIPTION_MAX_MEDIA_COUNT); CHK_STATUS(populateSessionDescriptionDataChannel(pKvsPeerConnection, &(pLocalSessionDescription->mediaDescriptions[pLocalSessionDescription->mediaCount]), diff --git a/src/source/PeerConnection/SessionDescription.h b/src/source/PeerConnection/SessionDescription.h index 9be9bca2de..15a6b45d11 100644 --- a/src/source/PeerConnection/SessionDescription.h +++ b/src/source/PeerConnection/SessionDescription.h @@ -27,6 +27,7 @@ extern "C" { #define CANDIDATE_KEY "candidate" #define SSRC_KEY "ssrc" #define BUNDLE_KEY "BUNDLE" +#define MID_KEY "mid" #define H264_VALUE "H264/90000" #define OPUS_VALUE "opus/48000" diff --git a/src/source/Rtp/RtpPacket.h b/src/source/Rtp/RtpPacket.h index 68025c7e4a..c8dc874d22 100644 --- a/src/source/Rtp/RtpPacket.h +++ b/src/source/Rtp/RtpPacket.h @@ -46,7 +46,7 @@ extern "C" { */ // https://tools.ietf.org/html/draft-holmer-rmcat-transport-wide-cc-extensions-01 #define TWCC_EXT_PROFILE 0xBEDE -#define TWCC_PAYLOAD(extId, sequenceNum) htonl((((extId) &0xfu) << 28u) | (1u << 24u) | ((UINT32) (sequenceNum) << 8u)) +#define TWCC_PAYLOAD(extId, sequenceNum) htonl((((extId) & 0xfu) << 28u) | (1u << 24u) | ((UINT32) (sequenceNum) << 8u)) #define TWCC_SEQNUM(extPayload) ((UINT16) getUnalignedInt16BigEndian(extPayload + 1)) typedef STATUS (*DepayRtpPayloadFunc)(PBYTE, UINT32, PBYTE, PUINT32, PBOOL); diff --git a/src/source/Signaling/ChannelInfo.c b/src/source/Signaling/ChannelInfo.c index 6f8dcaf560..3aba6d57cb 100644 --- a/src/source/Signaling/ChannelInfo.c +++ b/src/source/Signaling/ChannelInfo.c @@ -1,15 +1,33 @@ #define LOG_CLASS "ChannelInfo" #include "../Include_i.h" +#define ARN_DELIMETER_CHAR ':' +#define ARN_CHANNEL_NAME_CODE_SEP '/' +#define ARN_BEGIN "arn:aws" +#define SIGNALING_CHANNEL_ARN_SERVICE_NAME "kinesisvideo" +#define SIGNALING_CHANNEL_ARN_RESOURCE_TYPE "channel/" +#define AWS_ACCOUNT_ID_LENGTH 12 +#define AWS_KVS_ARN_CODE_LENGTH 13 +#define SIGNALING_CHANNEL_ARN_MIN_LENGTH 59 + +// Example: arn:aws:kinesisvideo:region:account-id:channel/channel-name/code +// Min Length of ":account-id:channel/channel-name/code" +// = len(":") + len(account-id) + len(":") + len("channel") + len("/") + len(channel-name) + len("/") + len(code) +// channel name must be at least 1 char +// = 1 + 12 + 1 + 7 + 1 + 1 + 1 + 13 = 37 +#define CHANNEL_ARN_MIN_DIST_FROM_REGION_END_TO_END_OF_ARN 37 + STATUS createValidateChannelInfo(PChannelInfo pOrigChannelInfo, PChannelInfo* ppChannelInfo) { ENTERS(); STATUS retStatus = STATUS_SUCCESS; + UINT16 channelNameStartPos = 0, channelNameLen = 0; - UINT32 allocSize, nameLen = 0, arnLen = 0, regionLen = 0, cplLen = 0, certLen = 0, postfixLen = 0, agentLen = 0, userAgentLen = 0, kmsLen = 0, - tagsSize; + UINT32 allocSize, channelArnLen = 0, storageStreamArnLen = 0, regionLen = 0, cpUrlLen = 0, certPathLen = 0, userAgentPostfixLen = 0, + customUserAgentLen = 0, userAgentLen = 0, kmsLen = 0, tagsSize; PCHAR pCurPtr, pRegionPtr, pUserAgentPostfixPtr; CHAR agentString[MAX_CUSTOM_USER_AGENT_NAME_POSTFIX_LEN + 1]; + PChannelInfo pChannelInfo = NULL; CHK(pOrigChannelInfo != NULL && ppChannelInfo != NULL, STATUS_NULL_ARG); @@ -18,12 +36,22 @@ STATUS createValidateChannelInfo(PChannelInfo pOrigChannelInfo, PChannelInfo* pp // Get and validate the lengths for all strings and store lengths excluding null terminator if (pOrigChannelInfo->pChannelName != NULL) { - CHK((nameLen = (UINT32) STRNLEN(pOrigChannelInfo->pChannelName, MAX_CHANNEL_NAME_LEN + 1)) <= MAX_CHANNEL_NAME_LEN, + CHK((channelNameLen = (UINT32) STRNLEN(pOrigChannelInfo->pChannelName, MAX_CHANNEL_NAME_LEN + 1)) <= MAX_CHANNEL_NAME_LEN, STATUS_SIGNALING_INVALID_CHANNEL_NAME_LENGTH); } if (pOrigChannelInfo->pChannelArn != NULL) { - CHK((arnLen = (UINT32) STRNLEN(pOrigChannelInfo->pChannelArn, MAX_ARN_LEN + 1)) <= MAX_ARN_LEN, STATUS_SIGNALING_INVALID_CHANNEL_ARN_LENGTH); + CHK((channelArnLen = (UINT32) STRNLEN(pOrigChannelInfo->pChannelArn, MAX_ARN_LEN + 1)) <= MAX_ARN_LEN, + STATUS_SIGNALING_INVALID_CHANNEL_ARN_LENGTH); + + if (pOrigChannelInfo->pChannelName == NULL) { + CHK_STATUS(validateKvsSignalingChannelArnAndExtractChannelName(pOrigChannelInfo, &channelNameStartPos, &channelNameLen)); + } + } + + if (pOrigChannelInfo->pStorageStreamArn != NULL) { + CHK((storageStreamArnLen = (UINT32) STRNLEN(pOrigChannelInfo->pStorageStreamArn, MAX_ARN_LEN + 1)) <= MAX_ARN_LEN, + STATUS_SIGNALING_INVALID_CHANNEL_ARN_LENGTH); } // Fix-up the region @@ -37,37 +65,37 @@ STATUS createValidateChannelInfo(PChannelInfo pOrigChannelInfo, PChannelInfo* pp } if (pOrigChannelInfo->pControlPlaneUrl != NULL) { - CHK((cplLen = (UINT32) STRNLEN(pOrigChannelInfo->pControlPlaneUrl, MAX_URI_CHAR_LEN + 1)) <= MAX_URI_CHAR_LEN, + CHK((cpUrlLen = (UINT32) STRNLEN(pOrigChannelInfo->pControlPlaneUrl, MAX_URI_CHAR_LEN + 1)) <= MAX_URI_CHAR_LEN, STATUS_SIGNALING_INVALID_CPL_LENGTH); } else { - cplLen = MAX_CONTROL_PLANE_URI_CHAR_LEN; + cpUrlLen = MAX_CONTROL_PLANE_URI_CHAR_LEN; } if (pOrigChannelInfo->pCertPath != NULL) { - CHK((certLen = (UINT32) STRNLEN(pOrigChannelInfo->pCertPath, MAX_PATH_LEN + 1)) <= MAX_PATH_LEN, + CHK((certPathLen = (UINT32) STRNLEN(pOrigChannelInfo->pCertPath, MAX_PATH_LEN + 1)) <= MAX_PATH_LEN, STATUS_SIGNALING_INVALID_CERTIFICATE_PATH_LENGTH); } userAgentLen = MAX_USER_AGENT_LEN; if (pOrigChannelInfo->pUserAgentPostfix != NULL && STRCMP(pOrigChannelInfo->pUserAgentPostfix, EMPTY_STRING) != 0) { - CHK((postfixLen = (UINT32) STRNLEN(pOrigChannelInfo->pUserAgentPostfix, MAX_CUSTOM_USER_AGENT_NAME_POSTFIX_LEN + 1)) <= + CHK((userAgentPostfixLen = (UINT32) STRNLEN(pOrigChannelInfo->pUserAgentPostfix, MAX_CUSTOM_USER_AGENT_NAME_POSTFIX_LEN + 1)) <= MAX_CUSTOM_USER_AGENT_NAME_POSTFIX_LEN, STATUS_SIGNALING_INVALID_AGENT_POSTFIX_LENGTH); pUserAgentPostfixPtr = pOrigChannelInfo->pUserAgentPostfix; } else { // Account for the "/" in the agent string. // The default user agent postfix is:AWS-WEBRTC-KVS-AGENT/ - postfixLen = STRLEN(SIGNALING_USER_AGENT_POSTFIX_NAME) + STRLEN(SIGNALING_USER_AGENT_POSTFIX_VERSION) + 1; - CHK(postfixLen <= MAX_CUSTOM_USER_AGENT_NAME_POSTFIX_LEN, STATUS_SIGNALING_INVALID_AGENT_POSTFIX_LENGTH); + userAgentPostfixLen = STRLEN(SIGNALING_USER_AGENT_POSTFIX_NAME) + STRLEN(SIGNALING_USER_AGENT_POSTFIX_VERSION) + 1; + CHK(userAgentPostfixLen <= MAX_CUSTOM_USER_AGENT_NAME_POSTFIX_LEN, STATUS_SIGNALING_INVALID_AGENT_POSTFIX_LENGTH); SNPRINTF(agentString, - postfixLen + 1, // account for null terminator + userAgentPostfixLen + 1, // account for null terminator (PCHAR) "%s/%s", SIGNALING_USER_AGENT_POSTFIX_NAME, SIGNALING_USER_AGENT_POSTFIX_VERSION); pUserAgentPostfixPtr = agentString; } if (pOrigChannelInfo->pCustomUserAgent != NULL) { - CHK((agentLen = (UINT32) STRNLEN(pOrigChannelInfo->pCustomUserAgent, MAX_CUSTOM_USER_AGENT_LEN + 1)) <= MAX_CUSTOM_USER_AGENT_LEN, + CHK((customUserAgentLen = (UINT32) STRNLEN(pOrigChannelInfo->pCustomUserAgent, MAX_CUSTOM_USER_AGENT_LEN + 1)) <= MAX_CUSTOM_USER_AGENT_LEN, STATUS_SIGNALING_INVALID_AGENT_LENGTH); } @@ -89,10 +117,12 @@ STATUS createValidateChannelInfo(PChannelInfo pOrigChannelInfo, PChannelInfo* pp CHK_STATUS(packageTags(pOrigChannelInfo->tagCount, pOrigChannelInfo->pTags, 0, NULL, &tagsSize)); // Allocate enough storage to hold the data with aligned strings size and set the pointers and NULL terminators - allocSize = SIZEOF(ChannelInfo) + ALIGN_UP_TO_MACHINE_WORD(1 + nameLen) + ALIGN_UP_TO_MACHINE_WORD(1 + arnLen) + - ALIGN_UP_TO_MACHINE_WORD(1 + regionLen) + ALIGN_UP_TO_MACHINE_WORD(1 + cplLen) + ALIGN_UP_TO_MACHINE_WORD(1 + certLen) + - ALIGN_UP_TO_MACHINE_WORD(1 + postfixLen) + ALIGN_UP_TO_MACHINE_WORD(1 + agentLen) + ALIGN_UP_TO_MACHINE_WORD(1 + userAgentLen) + - ALIGN_UP_TO_MACHINE_WORD(1 + kmsLen) + tagsSize; + // concatenate the data space with channel info. + allocSize = SIZEOF(ChannelInfo) + ALIGN_UP_TO_MACHINE_WORD(1 + channelNameLen) + ALIGN_UP_TO_MACHINE_WORD(1 + channelArnLen) + + ALIGN_UP_TO_MACHINE_WORD(1 + storageStreamArnLen) + ALIGN_UP_TO_MACHINE_WORD(1 + regionLen) + ALIGN_UP_TO_MACHINE_WORD(1 + cpUrlLen) + + ALIGN_UP_TO_MACHINE_WORD(1 + certPathLen) + ALIGN_UP_TO_MACHINE_WORD(1 + userAgentPostfixLen) + + ALIGN_UP_TO_MACHINE_WORD(1 + customUserAgentLen) + ALIGN_UP_TO_MACHINE_WORD(1 + userAgentLen) + ALIGN_UP_TO_MACHINE_WORD(1 + kmsLen) + + tagsSize; CHK(NULL != (pChannelInfo = (PChannelInfo) MEMCALLOC(1, allocSize)), STATUS_NOT_ENOUGH_MEMORY); pChannelInfo->version = CHANNEL_INFO_CURRENT_VERSION; @@ -103,6 +133,7 @@ STATUS createValidateChannelInfo(PChannelInfo pOrigChannelInfo, PChannelInfo* pp pChannelInfo->reconnect = pOrigChannelInfo->reconnect; pChannelInfo->messageTtl = pOrigChannelInfo->messageTtl; pChannelInfo->tagCount = pOrigChannelInfo->tagCount; + pChannelInfo->useMediaStorage = pOrigChannelInfo->useMediaStorage; // V1 handling if (pOrigChannelInfo->version > 0) { @@ -116,16 +147,26 @@ STATUS createValidateChannelInfo(PChannelInfo pOrigChannelInfo, PChannelInfo* pp // Set the pointers to the end and copy the data. // NOTE: the structure is calloc-ed so the strings will be NULL terminated - if (nameLen != 0) { - STRCPY(pCurPtr, pOrigChannelInfo->pChannelName); + if (channelNameLen != 0) { + if (pOrigChannelInfo->pChannelName != NULL) { + STRCPY(pCurPtr, pOrigChannelInfo->pChannelName); + } else { + STRNCPY(pCurPtr, pOrigChannelInfo->pChannelArn + channelNameStartPos, channelNameLen); + } pChannelInfo->pChannelName = pCurPtr; - pCurPtr += ALIGN_UP_TO_MACHINE_WORD(nameLen + 1); // For the NULL terminator + pCurPtr += ALIGN_UP_TO_MACHINE_WORD(channelNameLen + 1); // For the NULL terminator } - if (arnLen != 0) { + if (channelArnLen != 0) { STRCPY(pCurPtr, pOrigChannelInfo->pChannelArn); pChannelInfo->pChannelArn = pCurPtr; - pCurPtr += ALIGN_UP_TO_MACHINE_WORD(arnLen + 1); + pCurPtr += ALIGN_UP_TO_MACHINE_WORD(channelArnLen + 1); + } + + if (storageStreamArnLen != 0) { + STRCPY(pCurPtr, pOrigChannelInfo->pStorageStreamArn); + pChannelInfo->pStorageStreamArn = pCurPtr; + pCurPtr += ALIGN_UP_TO_MACHINE_WORD(storageStreamArnLen + 1); } STRCPY(pCurPtr, pRegionPtr); @@ -145,24 +186,24 @@ STATUS createValidateChannelInfo(PChannelInfo pOrigChannelInfo, PChannelInfo* pp } pChannelInfo->pControlPlaneUrl = pCurPtr; - pCurPtr += ALIGN_UP_TO_MACHINE_WORD(cplLen + 1); + pCurPtr += ALIGN_UP_TO_MACHINE_WORD(cpUrlLen + 1); - if (certLen != 0) { + if (certPathLen != 0) { STRCPY(pCurPtr, pOrigChannelInfo->pCertPath); pChannelInfo->pCertPath = pCurPtr; - pCurPtr += ALIGN_UP_TO_MACHINE_WORD(certLen + 1); + pCurPtr += ALIGN_UP_TO_MACHINE_WORD(certPathLen + 1); } - if (postfixLen != 0) { + if (userAgentPostfixLen != 0) { STRCPY(pCurPtr, pUserAgentPostfixPtr); pChannelInfo->pUserAgentPostfix = pCurPtr; - pCurPtr += ALIGN_UP_TO_MACHINE_WORD(postfixLen + 1); + pCurPtr += ALIGN_UP_TO_MACHINE_WORD(userAgentPostfixLen + 1); } - if (agentLen != 0) { + if (customUserAgentLen != 0) { STRCPY(pCurPtr, pOrigChannelInfo->pCustomUserAgent); pChannelInfo->pCustomUserAgent = pCurPtr; - pCurPtr += ALIGN_UP_TO_MACHINE_WORD(agentLen + 1); + pCurPtr += ALIGN_UP_TO_MACHINE_WORD(customUserAgentLen + 1); } getUserAgentString(pUserAgentPostfixPtr, pOrigChannelInfo->pCustomUserAgent, MAX_USER_AGENT_LEN, pCurPtr); @@ -308,3 +349,95 @@ PCHAR getStringFromChannelRoleType(SIGNALING_CHANNEL_ROLE_TYPE type) return typeStr; } + +// https://docs.aws.amazon.com/kinesisvideostreams-webrtc-dg/latest/devguide/kvswebrtc-how-iam.html#kinesis-using-iam-arn-format +// Example: arn:aws:kinesisvideo:region:account-id:channel/channel-name/code +STATUS validateKvsSignalingChannelArnAndExtractChannelName(PChannelInfo pChannelInfo, PUINT16 pStart, PUINT16 pNumChars) +{ + UINT16 arnLength, currPosIndex = 0, channelNameLength = 0, channelNameStart = 0; + PCHAR partitionEnd, regionEnd, channelNameEnd; + PCHAR currPos; + UINT8 accountIdStart = 1, accountIdEnd = AWS_ACCOUNT_ID_LENGTH; + UINT8 timeCodeStart = 0, timeCodeEnd = AWS_KVS_ARN_CODE_LENGTH - 1; + + if (pChannelInfo != NULL && pChannelInfo->pChannelArn != NULL) { + arnLength = STRNLEN(pChannelInfo->pChannelArn, MAX_ARN_LEN); + + if (arnLength >= SIGNALING_CHANNEL_ARN_MIN_LENGTH) { + currPos = pChannelInfo->pChannelArn; + + if (STRNCMP(currPos, ARN_BEGIN, STRLEN(ARN_BEGIN)) == 0) { + currPosIndex += STRLEN(ARN_BEGIN); + partitionEnd = STRNCHR(currPos + currPosIndex, arnLength - currPosIndex, ARN_DELIMETER_CHAR); + + if (partitionEnd != NULL && (partitionEnd - currPos) < arnLength) { + currPosIndex = partitionEnd - currPos + 1; + + if (currPosIndex < arnLength && + STRNCMP(currPos + currPosIndex, SIGNALING_CHANNEL_ARN_SERVICE_NAME, STRLEN(SIGNALING_CHANNEL_ARN_SERVICE_NAME)) == 0) { + currPosIndex += STRLEN(SIGNALING_CHANNEL_ARN_SERVICE_NAME); + + if (currPosIndex < arnLength && *(currPos + currPosIndex) == ARN_DELIMETER_CHAR) { + currPosIndex++; + + regionEnd = STRNCHR(currPos + currPosIndex, arnLength - currPosIndex, ARN_DELIMETER_CHAR); + + if (regionEnd != NULL) { + if (currPosIndex < arnLength && (regionEnd - currPos) < arnLength) { + currPosIndex = regionEnd - currPos; + + if (currPosIndex + CHANNEL_ARN_MIN_DIST_FROM_REGION_END_TO_END_OF_ARN <= arnLength) { + while (accountIdStart <= accountIdEnd && + (*(currPos + currPosIndex + accountIdStart) >= '0' && + *(currPos + currPosIndex + accountIdStart) <= '9')) { + accountIdStart++; + } + + if (accountIdStart == accountIdEnd + 1 && *(currPos + currPosIndex + accountIdStart) == ARN_DELIMETER_CHAR) { + currPosIndex += (accountIdStart + 1); + + if (STRNCMP(currPos + currPosIndex, SIGNALING_CHANNEL_ARN_RESOURCE_TYPE, + STRLEN(SIGNALING_CHANNEL_ARN_RESOURCE_TYPE)) == 0) { + // Channel Name Begins Here, ends when we hit ARN_CHANNEL_NAME_CODE_SEP + currPosIndex += STRLEN(SIGNALING_CHANNEL_ARN_RESOURCE_TYPE); + channelNameEnd = STRNCHR(currPos + currPosIndex, arnLength - currPosIndex - AWS_KVS_ARN_CODE_LENGTH, + ARN_CHANNEL_NAME_CODE_SEP); + + if (channelNameEnd != NULL) { + channelNameLength = channelNameEnd - (currPos + currPosIndex); + if (channelNameLength > 0) { + channelNameStart = currPosIndex; + currPosIndex += (channelNameLength + 1); + + if (currPosIndex + AWS_KVS_ARN_CODE_LENGTH == arnLength) { + // 13 digit time code + + while ((timeCodeStart <= timeCodeEnd) && + *(currPos + currPosIndex + timeCodeStart) >= '0' && + *(currPos + currPosIndex + timeCodeStart) <= '9') { + timeCodeStart++; + } + + // Verify that we have 13 digits and that we are not at the end of the arn + if (currPosIndex + timeCodeStart == arnLength) { + *pStart = channelNameStart; + *pNumChars = channelNameLength; + return STATUS_SUCCESS; + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + + return STATUS_SIGNALING_INVALID_CHANNEL_ARN; +} diff --git a/src/source/Signaling/ChannelInfo.h b/src/source/Signaling/ChannelInfo.h index 22b3aec222..604b23386e 100644 --- a/src/source/Signaling/ChannelInfo.h +++ b/src/source/Signaling/ChannelInfo.h @@ -105,6 +105,19 @@ SIGNALING_CHANNEL_ROLE_TYPE getChannelRoleTypeFromString(PCHAR, UINT32); */ PCHAR getStringFromChannelRoleType(SIGNALING_CHANNEL_ROLE_TYPE); +/** + * Determines whether or not the channel arn is valid + * If VALID it extracts the channel name + * And Sets the pChannelName field in PChannelInfo + * + * @param - PChannelInfo - IN - channel info object + * @param - PUINT16 - OUT - start index of the arn (if valid) where the channel name is + * @param - PUINT16 - OUT - number of characters for the arn (if valid) + * + *@return - success if arn was valid otherwise failure + */ +STATUS validateKvsSignalingChannelArnAndExtractChannelName(PChannelInfo, PUINT16, PUINT16); + #ifdef __cplusplus } #endif diff --git a/src/source/Signaling/Client.c b/src/source/Signaling/Client.c index 425edcabf0..6c9272a2cf 100644 --- a/src/source/Signaling/Client.c +++ b/src/source/Signaling/Client.c @@ -327,6 +327,8 @@ STATUS signalingClientGetCurrentState(SIGNALING_CLIENT_HANDLE signalingClientHan CHK_STATUS(getStateMachineCurrentState(pSignalingClient->pStateMachine, &pStateMachineState)); state = getSignalingStateFromStateMachineState(pStateMachineState->state); + DLOGV("Current state: 0x%016" PRIx64, pStateMachineState->state); + CleanUp: if (pState != NULL) { @@ -393,7 +395,18 @@ STATUS signalingClientGetStateString(SIGNALING_CLIENT_STATE state, PCHAR* ppStat case SIGNALING_CLIENT_STATE_DELETED: *ppStateStr = SIGNALING_CLIENT_STATE_DELETED_STR; break; - + case SIGNALING_CLIENT_STATE_DESCRIBE_MEDIA: + *ppStateStr = SIGNALING_CLIENT_STATE_DESCRIBE_MEDIA_STR; + break; + case SIGNALING_CLIENT_STATE_JOIN_SESSION: + *ppStateStr = SIGNALING_CLIENT_STATE_JOIN_SESSION_STR; + break; + case SIGNALING_CLIENT_STATE_JOIN_SESSION_WAITING: + *ppStateStr = SIGNALING_CLIENT_STATE_JOIN_SESSION_WAITING_STR; + break; + case SIGNALING_CLIENT_STATE_JOIN_SESSION_CONNECTED: + *ppStateStr = SIGNALING_CLIENT_STATE_JOIN_SESSION_CONNECTED_STR; + break; case SIGNALING_CLIENT_STATE_MAX_VALUE: case SIGNALING_CLIENT_STATE_UNKNOWN: // Explicit fall-through diff --git a/src/source/Signaling/FileCache.c b/src/source/Signaling/FileCache.c index 08b43dee56..3f69fb9296 100644 --- a/src/source/Signaling/FileCache.c +++ b/src/source/Signaling/FileCache.c @@ -41,7 +41,7 @@ STATUS deserializeSignalingCacheEntries(PCHAR cachedFileContent, UINT64 fileSize while (remainingSize > MAX_SIGNALING_CACHE_ENTRY_TIMESTAMP_STR_LEN) { nextLine = STRCHR(pCurrent, '\n'); while ((nextToken = STRCHR(pCurrent, ',')) != NULL && nextToken < nextLine) { - switch (tokenCount % 7) { + switch (tokenCount % 10) { case 0: STRNCPY(pSignalingFileCacheEntryList[entryCount].channelName, pCurrent, nextToken - pCurrent); break; @@ -67,6 +67,15 @@ STATUS deserializeSignalingCacheEntries(PCHAR cachedFileContent, UINT64 fileSize case 5: STRNCPY(pSignalingFileCacheEntryList[entryCount].wssEndpoint, pCurrent, nextToken - pCurrent); break; + case 6: + STRNCPY(pSignalingFileCacheEntryList[entryCount].storageEnabled, pCurrent, nextToken - pCurrent); + break; + case 7: + STRNCPY(pSignalingFileCacheEntryList[entryCount].storageStreamArn, pCurrent, nextToken - pCurrent); + break; + case 8: + STRNCPY(pSignalingFileCacheEntryList[entryCount].webrtcEndpoint, pCurrent, nextToken - pCurrent); + break; default: break; } @@ -146,7 +155,7 @@ STATUS signalingCacheLoadFromFile(PCHAR channelName, PCHAR region, SIGNALING_CHA /* Assume channel name and region has been validated */ if (STRCMP(entries[i].channelName, channelName) == 0 && STRCMP(entries[i].region, region) == 0 && entries[i].role == role) { cacheFound = TRUE; - *pSignalingFileCacheEntry = entries[i]; + MEMCPY(pSignalingFileCacheEntry, &entries[i], SIZEOF(entries[i])); } } } @@ -209,23 +218,24 @@ STATUS signalingCacheSaveToFile(PSignalingFileCacheEntry pSignalingFileCacheEntr } /* at this point i is at most entryCount */ - if (entryCount >= MAX_SIGNALING_CACHE_ENTRY_COUNT) { - DLOGW("Overwrote 32nd entry to store signaling cache because max entry count of %u reached", MAX_SIGNALING_CACHE_ENTRY_COUNT); + if (newEntry && entryCount >= MAX_SIGNALING_CACHE_ENTRY_COUNT) { i = MAX_SIGNALING_CACHE_ENTRY_COUNT - 1; newEntry = FALSE; } - entries[i] = *pSignalingFileCacheEntry; if (newEntry) { entryCount++; } + entries[i] = *pSignalingFileCacheEntry; + for (i = 0; i < entryCount; ++i) { serializedCacheEntryLen = - SNPRINTF(serializedCacheEntry, ARRAY_SIZE(serializedCacheEntry), "%s,%s,%s,%s,%s,%s,%.10" PRIu64 "\n", entries[i].channelName, + SNPRINTF(serializedCacheEntry, ARRAY_SIZE(serializedCacheEntry), "%s,%s,%s,%s,%s,%s,%s,%s,%s,%.10" PRIu64 "\n", entries[i].channelName, entries[i].role == SIGNALING_CHANNEL_ROLE_TYPE_MASTER ? SIGNALING_FILE_CACHE_ROLE_TYPE_MASTER_STR : SIGNALING_FILE_CACHE_ROLE_TYPE_VIEWER_STR, - entries[i].region, entries[i].channelArn, entries[i].httpsEndpoint, entries[i].wssEndpoint, entries[i].creationTsEpochSeconds); + entries[i].region, entries[i].channelArn, entries[i].httpsEndpoint, entries[i].wssEndpoint, entries[i].storageEnabled, + entries[i].storageStreamArn, entries[i].webrtcEndpoint, entries[i].creationTsEpochSeconds); CHK_STATUS(writeFile(cacheFilePath, FALSE, i == 0 ? FALSE : TRUE, (PBYTE) serializedCacheEntry, serializedCacheEntryLen)); } diff --git a/src/source/Signaling/FileCache.h b/src/source/Signaling/FileCache.h index 5cada931aa..fa2f666bf8 100644 --- a/src/source/Signaling/FileCache.h +++ b/src/source/Signaling/FileCache.h @@ -25,11 +25,14 @@ extern "C" { typedef struct { SIGNALING_CHANNEL_ROLE_TYPE role; UINT64 creationTsEpochSeconds; + CHAR storageEnabled[2]; CHAR channelName[MAX_CHANNEL_NAME_LEN + 1]; CHAR channelArn[MAX_ARN_LEN + 1]; CHAR region[MAX_REGION_NAME_LEN + 1]; CHAR httpsEndpoint[MAX_SIGNALING_ENDPOINT_URI_LEN + 1]; CHAR wssEndpoint[MAX_SIGNALING_ENDPOINT_URI_LEN + 1]; + CHAR storageStreamArn[MAX_ARN_LEN + 1]; + CHAR webrtcEndpoint[MAX_SIGNALING_ENDPOINT_URI_LEN + 1]; } SignalingFileCacheEntry, *PSignalingFileCacheEntry; STATUS deserializeSignalingCacheEntries(PCHAR, UINT64, PSignalingFileCacheEntry, PUINT32, PCHAR); diff --git a/src/source/Signaling/LwsApiCalls.c b/src/source/Signaling/LwsApiCalls.c index 18367e884d..72c5fc1e32 100644 --- a/src/source/Signaling/LwsApiCalls.c +++ b/src/source/Signaling/LwsApiCalls.c @@ -3,6 +3,7 @@ */ #define LOG_CLASS "LwsApiCalls" #include "../Include_i.h" +#define WEBRTC_SCHEME_NAME "webrtc" static BOOL gInterruptedFlagBySignalHandler; VOID lwsSignalHandler(INT32 signal) @@ -310,6 +311,7 @@ INT32 lwsWssCallbackRoutine(struct lws* wsi, enum lws_callback_reasons reason, P case LWS_CALLBACK_CLIENT_WRITEABLE: break; default: + DLOGI("WSS callback with reason %d", reason); CHK(FALSE, retStatus); } @@ -614,11 +616,11 @@ STATUS lwsCompleteSync(PLwsCallInfo pCallInfo) // and a wss is in progress. This is the case when we have a current websocket listener // and need to perform an https call due to ICE server config refresh for example. // If we have an ongoing wss operations, we can't call lws_client_connect_via_info API - // due to threading model of LWS. WHat we need to do is to wake up the potentially blocked + // due to threading model of LWS. What we need to do is to wake up the potentially blocked // ongoing wss handler for it to release the service lock which it holds while calling lws_service() // API so we can grab the lock in order to perform the lws_client_connect_via_info API call. // The need to wake up the wss handler (if any) to compete for the lock is the reason for this - // loop. In order to avoid pegging of the CPU while the contention for the lock happes, + // loop. In order to avoid pegging of the CPU while the contention for the lock happens, // we are setting an atomic and releasing it to trigger a timed wait when the lws_service call // awakes to make sure we are not going to starve this thread. @@ -798,9 +800,7 @@ STATUS describeChannelLws(PSignalingClient pSignalingClient, UINT64 time) tokenCount = jsmn_parse(&parser, pResponseStr, resultLen, tokens, SIZEOF(tokens) / SIZEOF(jsmntok_t)); CHK(tokenCount > 1, STATUS_INVALID_API_CALL_RETURN_JSON); CHK(tokens[0].type == JSMN_OBJECT, STATUS_INVALID_API_CALL_RETURN_JSON); - MEMSET(&pSignalingClient->channelDescription, 0x00, SIZEOF(SignalingChannelDescription)); - // Loop through the tokens and extract the stream description for (i = 1; i < tokenCount; i++) { if (!jsonInChannelDescription) { @@ -1006,8 +1006,13 @@ STATUS getChannelEndpointLws(PSignalingClient pSignalingClient, UINT64 time) STRCAT(url, GET_SIGNALING_CHANNEL_ENDPOINT_API_POSTFIX); // Prepare the json params for the call - SNPRINTF(paramsJson, ARRAY_SIZE(paramsJson), GET_CHANNEL_ENDPOINT_PARAM_JSON_TEMPLATE, pSignalingClient->channelDescription.channelArn, - SIGNALING_CHANNEL_PROTOCOL, getStringFromChannelRoleType(pSignalingClient->pChannelInfo->channelRoleType)); + if (pSignalingClient->mediaStorageConfig.storageStatus == FALSE) { + SNPRINTF(paramsJson, ARRAY_SIZE(paramsJson), GET_CHANNEL_ENDPOINT_PARAM_JSON_TEMPLATE, pSignalingClient->channelDescription.channelArn, + SIGNALING_CHANNEL_PROTOCOL, getStringFromChannelRoleType(pSignalingClient->pChannelInfo->channelRoleType)); + } else { + SNPRINTF(paramsJson, ARRAY_SIZE(paramsJson), GET_CHANNEL_ENDPOINT_PARAM_JSON_TEMPLATE, pSignalingClient->channelDescription.channelArn, + SIGNALING_CHANNEL_PROTOCOL_W_MEDIA_STORAGE, getStringFromChannelRoleType(pSignalingClient->pChannelInfo->channelRoleType)); + } // Create the request info with the body CHK_STATUS(createRequestInfo(url, paramsJson, pSignalingClient->pChannelInfo->pRegion, pSignalingClient->pChannelInfo->pCertPath, NULL, NULL, @@ -1044,6 +1049,7 @@ STATUS getChannelEndpointLws(PSignalingClient pSignalingClient, UINT64 time) pSignalingClient->channelEndpointWss[0] = '\0'; pSignalingClient->channelEndpointHttps[0] = '\0'; + pSignalingClient->channelEndpointWebrtc[0] = '\0'; // Loop through the tokens and extract the stream description for (i = 1; i < tokenCount; i++) { @@ -1065,6 +1071,9 @@ STATUS getChannelEndpointLws(PSignalingClient pSignalingClient, UINT64 time) } else if (0 == STRNCMPI(pProtocol, HTTPS_SCHEME_NAME, protocolLen)) { STRNCPY(pSignalingClient->channelEndpointHttps, pEndpoint, MIN(endpointLen, MAX_SIGNALING_ENDPOINT_URI_LEN)); pSignalingClient->channelEndpointHttps[MAX_SIGNALING_ENDPOINT_URI_LEN] = '\0'; + } else if (0 == STRNCMPI(pProtocol, WEBRTC_SCHEME_NAME, protocolLen)) { + STRNCPY(pSignalingClient->channelEndpointWebrtc, pEndpoint, MIN(endpointLen, MAX_SIGNALING_ENDPOINT_URI_LEN)); + pSignalingClient->channelEndpointWebrtc[MAX_SIGNALING_ENDPOINT_URI_LEN] = '\0'; } } @@ -1100,6 +1109,9 @@ STATUS getChannelEndpointLws(PSignalingClient pSignalingClient, UINT64 time) } else if (0 == STRNCMPI(pProtocol, HTTPS_SCHEME_NAME, protocolLen)) { STRNCPY(pSignalingClient->channelEndpointHttps, pEndpoint, MIN(endpointLen, MAX_SIGNALING_ENDPOINT_URI_LEN)); pSignalingClient->channelEndpointHttps[MAX_SIGNALING_ENDPOINT_URI_LEN] = '\0'; + } else if (0 == STRNCMPI(pProtocol, WEBRTC_SCHEME_NAME, protocolLen)) { + STRNCPY(pSignalingClient->channelEndpointWebrtc, pEndpoint, MIN(endpointLen, MAX_SIGNALING_ENDPOINT_URI_LEN)); + pSignalingClient->channelEndpointWebrtc[MAX_SIGNALING_ENDPOINT_URI_LEN] = '\0'; } } @@ -1468,6 +1480,186 @@ STATUS connectSignalingChannelLws(PSignalingClient pSignalingClient, UINT64 time return retStatus; } +STATUS joinStorageSessionLws(PSignalingClient pSignalingClient, UINT64 time) +{ + ENTERS(); + STATUS retStatus = STATUS_SUCCESS; + UNUSED_PARAM(time); + + PRequestInfo pRequestInfo = NULL; + CHAR url[MAX_URI_CHAR_LEN + 1]; + CHAR paramsJson[MAX_JSON_PARAMETER_STRING_LEN]; + PLwsCallInfo pLwsCallInfo = NULL; + PCHAR pResponseStr; + UINT32 resultLen; + + CHK(pSignalingClient != NULL, STATUS_NULL_ARG); + CHK(pSignalingClient->channelEndpointWebrtc[0] != '\0', STATUS_INTERNAL_ERROR); + + // Create the API url + STRCPY(url, pSignalingClient->channelEndpointWebrtc); + STRCAT(url, JOIN_STORAGE_SESSION_API_POSTFIX); + + // Prepare the json params for the call + if (pSignalingClient->pChannelInfo->channelRoleType == SIGNALING_CHANNEL_ROLE_TYPE_VIEWER) { + SNPRINTF(paramsJson, ARRAY_SIZE(paramsJson), SIGNALING_JOIN_STORAGE_SESSION_VIEWER_PARAM_JSON_TEMPLATE, + pSignalingClient->channelDescription.channelArn, pSignalingClient->clientInfo.signalingClientInfo.clientId); + } else { + SNPRINTF(paramsJson, ARRAY_SIZE(paramsJson), SIGNALING_JOIN_STORAGE_SESSION_MASTER_PARAM_JSON_TEMPLATE, + pSignalingClient->channelDescription.channelArn); + } + + // Create the request info with the body + CHK_STATUS(createRequestInfo(url, paramsJson, pSignalingClient->pChannelInfo->pRegion, pSignalingClient->pChannelInfo->pCertPath, NULL, NULL, + SSL_CERTIFICATE_TYPE_NOT_SPECIFIED, pSignalingClient->pChannelInfo->pUserAgent, + SIGNALING_SERVICE_API_CALL_CONNECTION_TIMEOUT, SIGNALING_SERVICE_API_CALL_COMPLETION_TIMEOUT, + DEFAULT_LOW_SPEED_LIMIT, DEFAULT_LOW_SPEED_TIME_LIMIT, pSignalingClient->pAwsCredentials, &pRequestInfo)); + + // createRequestInfo does not have access to the getCurrentTime callback, this hook is used for tests. + if (pSignalingClient->signalingClientCallbacks.getCurrentTimeFn != NULL) { + pRequestInfo->currentTime = + pSignalingClient->signalingClientCallbacks.getCurrentTimeFn(pSignalingClient->signalingClientCallbacks.customData); + } + + checkAndCorrectForClockSkew(pSignalingClient, pRequestInfo); + + CHK_STATUS(createLwsCallInfo(pSignalingClient, pRequestInfo, PROTOCOL_INDEX_HTTPS, + &pLwsCallInfo)); //!< TBD. Accroding to the design document, the prefix of url will be webrtc:// + + if (pSignalingClient->mediaStorageConfig.storageStatus) { + pSignalingClient->diagnostics.joinSessionToOfferRecvTime = GETTIME(); + } + // Make a blocking call + CHK_STATUS(lwsCompleteSync(pLwsCallInfo)); + + // Set the service call result + ATOMIC_STORE(&pSignalingClient->result, (SIZE_T) pLwsCallInfo->callInfo.callResult); + pResponseStr = pLwsCallInfo->callInfo.responseData; + resultLen = pLwsCallInfo->callInfo.responseDataLen; + + // Early return if we have a non-success result + CHK((SERVICE_CALL_RESULT) ATOMIC_LOAD(&pSignalingClient->result) == SERVICE_CALL_RESULT_OK, STATUS_SIGNALING_LWS_CALL_FAILED); + + // Perform some validation on the channel description + CHK(pSignalingClient->channelDescription.channelStatus != SIGNALING_CHANNEL_STATUS_DELETING, STATUS_SIGNALING_CHANNEL_BEING_DELETED); + +CleanUp: + + if (STATUS_FAILED(retStatus)) { + DLOGE("Call Failed with Status: 0x%08x", retStatus); + } + + freeLwsCallInfo(&pLwsCallInfo); + + LEAVES(); + return retStatus; +} + +STATUS describeMediaStorageConfLws(PSignalingClient pSignalingClient, UINT64 time) +{ + ENTERS(); + STATUS retStatus = STATUS_SUCCESS; + UNUSED_PARAM(time); + + PRequestInfo pRequestInfo = NULL; + CHAR url[MAX_URI_CHAR_LEN + 1]; + CHAR paramsJson[MAX_JSON_PARAMETER_STRING_LEN]; + PLwsCallInfo pLwsCallInfo = NULL; + PCHAR pResponseStr; + jsmn_parser parser; + jsmntok_t tokens[MAX_JSON_TOKEN_COUNT]; + UINT32 i, strLen, resultLen; + UINT32 tokenCount; + BOOL jsonInMediaStorageConfig = FALSE; + + CHK(pSignalingClient != NULL, STATUS_NULL_ARG); + + // Create the API url + STRCPY(url, pSignalingClient->pChannelInfo->pControlPlaneUrl); + STRCAT(url, DESCRIBE_MEDIA_STORAGE_CONF_API_POSTFIX); + + // Prepare the json params for the call + CHK(pSignalingClient->channelDescription.channelArn[0] != '\0', STATUS_NULL_ARG); + SNPRINTF(paramsJson, ARRAY_SIZE(paramsJson), DESCRIBE_MEDIA_STORAGE_CONF_PARAM_JSON_TEMPLATE, pSignalingClient->channelDescription.channelArn); + // Create the request info with the body + CHK_STATUS(createRequestInfo(url, paramsJson, pSignalingClient->pChannelInfo->pRegion, pSignalingClient->pChannelInfo->pCertPath, NULL, NULL, + SSL_CERTIFICATE_TYPE_NOT_SPECIFIED, pSignalingClient->pChannelInfo->pUserAgent, + SIGNALING_SERVICE_API_CALL_CONNECTION_TIMEOUT, SIGNALING_SERVICE_API_CALL_COMPLETION_TIMEOUT, + DEFAULT_LOW_SPEED_LIMIT, DEFAULT_LOW_SPEED_TIME_LIMIT, pSignalingClient->pAwsCredentials, &pRequestInfo)); + + // createRequestInfo does not have access to the getCurrentTime callback, this hook is used for tests. + if (pSignalingClient->signalingClientCallbacks.getCurrentTimeFn != NULL) { + pRequestInfo->currentTime = + pSignalingClient->signalingClientCallbacks.getCurrentTimeFn(pSignalingClient->signalingClientCallbacks.customData); + } + + checkAndCorrectForClockSkew(pSignalingClient, pRequestInfo); + + CHK_STATUS(createLwsCallInfo(pSignalingClient, pRequestInfo, PROTOCOL_INDEX_HTTPS, &pLwsCallInfo)); + + // Make a blocking call + CHK_STATUS(lwsCompleteSync(pLwsCallInfo)); + + // Set the service call result + ATOMIC_STORE(&pSignalingClient->result, (SIZE_T) pLwsCallInfo->callInfo.callResult); + pResponseStr = pLwsCallInfo->callInfo.responseData; + resultLen = pLwsCallInfo->callInfo.responseDataLen; + + // Early return if we have a non-success result + CHK((SERVICE_CALL_RESULT) ATOMIC_LOAD(&pSignalingClient->result) == SERVICE_CALL_RESULT_OK && resultLen != 0 && pResponseStr != NULL, + STATUS_SIGNALING_LWS_CALL_FAILED); + + // Parse the response + jsmn_init(&parser); + tokenCount = jsmn_parse(&parser, pResponseStr, resultLen, tokens, SIZEOF(tokens) / SIZEOF(jsmntok_t)); + CHK(tokenCount > 1, STATUS_INVALID_API_CALL_RETURN_JSON); + CHK(tokens[0].type == JSMN_OBJECT, STATUS_INVALID_API_CALL_RETURN_JSON); + + // Loop through the tokens and extract the stream description + for (i = 1; i < tokenCount; i++) { + if (!jsonInMediaStorageConfig) { + if (compareJsonString(pResponseStr, &tokens[i], JSMN_STRING, (PCHAR) "MediaStorageConfiguration")) { + jsonInMediaStorageConfig = TRUE; + i++; + } + } else { + if (compareJsonString(pResponseStr, &tokens[i], JSMN_STRING, (PCHAR) "Status")) { + strLen = (UINT32) (tokens[i + 1].end - tokens[i + 1].start); + CHK(strLen <= MAX_ARN_LEN, STATUS_INVALID_API_CALL_RETURN_JSON); + if (STRNCMP("ENABLED", pResponseStr + tokens[i + 1].start, strLen) == 0) { + pSignalingClient->mediaStorageConfig.storageStatus = TRUE; + } else { + pSignalingClient->mediaStorageConfig.storageStatus = FALSE; + } + i++; + } else if (compareJsonString(pResponseStr, &tokens[i], JSMN_STRING, (PCHAR) "StreamARN")) { + // StorageStream may be null. + if (tokens[i + 1].type != JSMN_PRIMITIVE) { + strLen = (UINT32) (tokens[i + 1].end - tokens[i + 1].start); + CHK(strLen <= MAX_ARN_LEN, STATUS_INVALID_API_CALL_RETURN_JSON); + STRNCPY(pSignalingClient->mediaStorageConfig.storageStreamArn, pResponseStr + tokens[i + 1].start, strLen); + pSignalingClient->mediaStorageConfig.storageStreamArn[MAX_ARN_LEN] = '\0'; + } + i++; + } + } + } + + // Perform some validation on the channel description + CHK(pSignalingClient->channelDescription.channelStatus != SIGNALING_CHANNEL_STATUS_DELETING, STATUS_SIGNALING_CHANNEL_BEING_DELETED); + +CleanUp: + + if (STATUS_FAILED(retStatus)) { + DLOGE("Call Failed with Status: 0x%08x", retStatus); + } + + freeLwsCallInfo(&pLwsCallInfo); + + LEAVES(); + return retStatus; +} + PVOID lwsListenerHandler(PVOID args) { ENTERS(); @@ -1545,7 +1737,8 @@ PVOID reconnectHandler(PVOID args) // Attempt to reconnect by driving the state machine to connected state CHK_STATUS(signalingStateMachineIterator(pSignalingClient, SIGNALING_GET_CURRENT_TIME(pSignalingClient) + SIGNALING_CONNECT_STATE_TIMEOUT, - SIGNALING_STATE_CONNECTED)); + pSignalingClient->mediaStorageConfig.storageStatus ? SIGNALING_STATE_JOIN_SESSION_CONNECTED + : SIGNALING_STATE_CONNECTED)); CleanUp: @@ -1586,7 +1779,7 @@ STATUS sendLwsMessage(PSignalingClient pSignalingClient, SIGNALING_MESSAGE_TYPE UINT64 curTime; // Ensure we are in a connected state - CHK_STATUS(acceptSignalingStateMachineState(pSignalingClient, SIGNALING_STATE_CONNECTED)); + CHK_STATUS(acceptSignalingStateMachineState(pSignalingClient, SIGNALING_STATE_CONNECTED | SIGNALING_STATE_JOIN_SESSION_CONNECTED)); CHK(pSignalingClient != NULL && pSignalingClient->pOngoingCallInfo != NULL, STATUS_NULL_ARG); @@ -1604,7 +1797,8 @@ STATUS sendLwsMessage(PSignalingClient pSignalingClient, SIGNALING_MESSAGE_TYPE default: CHK(FALSE, STATUS_INVALID_ARG); } - + DLOGD("%s", pMessageType); + DLOGD("%s", pMessage); // Calculate the lengths if not specified if (messageLen == 0) { size = (UINT32) STRLEN(pMessage); @@ -1968,7 +2162,8 @@ STATUS receiveLwsMessage(PSignalingClient pSignalingClient, PCHAR pMessage, UINT // Iterate the state machinery CHK_STATUS(signalingStateMachineIterator(pSignalingClient, SIGNALING_GET_CURRENT_TIME(pSignalingClient) + SIGNALING_CONNECT_STATE_TIMEOUT, - SIGNALING_STATE_CONNECTED)); + pSignalingClient->mediaStorageConfig.storageStatus ? SIGNALING_STATE_JOIN_SESSION_CONNECTED + : SIGNALING_STATE_CONNECTED)); CHK(FALSE, retStatus); break; @@ -1982,15 +2177,18 @@ STATUS receiveLwsMessage(PSignalingClient pSignalingClient, PCHAR pMessage, UINT // Iterate the state machinery CHK_STATUS(signalingStateMachineIterator(pSignalingClient, SIGNALING_GET_CURRENT_TIME(pSignalingClient) + SIGNALING_CONNECT_STATE_TIMEOUT, - SIGNALING_STATE_CONNECTED)); + pSignalingClient->mediaStorageConfig.storageStatus ? SIGNALING_STATE_JOIN_SESSION_CONNECTED + : SIGNALING_STATE_CONNECTED)); CHK(FALSE, retStatus); break; case SIGNALING_MESSAGE_TYPE_OFFER: - CHK(pSignalingMessageWrapper->receivedSignalingMessage.signalingMessage.peerClientId[0] != '\0', - STATUS_SIGNALING_NO_PEER_CLIENT_ID_IN_MESSAGE); - // Explicit fall-through !!! + if (!pSignalingClient->mediaStorageConfig.storageStatus) { + CHK(pSignalingMessageWrapper->receivedSignalingMessage.signalingMessage.peerClientId[0] != '\0', + STATUS_SIGNALING_NO_PEER_CLIENT_ID_IN_MESSAGE); + } + // Explicit fall-through !!! case SIGNALING_MESSAGE_TYPE_ANSWER: case SIGNALING_MESSAGE_TYPE_ICE_CANDIDATE: CHK(pSignalingMessageWrapper->receivedSignalingMessage.signalingMessage.payloadLen > 0 && @@ -2054,6 +2252,7 @@ STATUS terminateConnectionWithStatus(PSignalingClient pSignalingClient, SERVICE_ CVAR_BROADCAST(pSignalingClient->connectedCvar); CVAR_BROADCAST(pSignalingClient->receiveCvar); CVAR_BROADCAST(pSignalingClient->sendCvar); + CVAR_BROADCAST(pSignalingClient->jssWaitCvar); ATOMIC_STORE(&pSignalingClient->messageResult, (SIZE_T) SERVICE_CALL_UNKNOWN); ATOMIC_STORE(&pSignalingClient->result, (SIZE_T) callResult); @@ -2171,14 +2370,28 @@ PVOID receiveLwsMessageWrapper(PVOID args) // Updating the diagnostics info before calling the client callback ATOMIC_INCREMENT(&pSignalingClient->diagnostics.numberOfMessagesReceived); + if (messageType == SIGNALING_MESSAGE_TYPE_OFFER) { + MUTEX_LOCK(pSignalingClient->offerSendReceiveTimeLock); + pSignalingClient->offerReceivedTime = GETTIME(); + MUTEX_UNLOCK(pSignalingClient->offerSendReceiveTimeLock); + + if (pSignalingClient->mediaStorageConfig.storageStatus) { + MUTEX_LOCK(pSignalingClient->jssWaitLock); + ATOMIC_STORE_BOOL(&pSignalingClient->offerReceived, TRUE); + DLOGI("Offer Received from JoinStorageSession Call."); + pSignalingClient->diagnostics.joinSessionToOfferRecvTime = + pSignalingClient->offerReceivedTime - pSignalingClient->diagnostics.joinSessionToOfferRecvTime; + CVAR_BROADCAST(pSignalingClient->jssWaitCvar); + MUTEX_UNLOCK(pSignalingClient->jssWaitLock); + } + } else if (messageType == SIGNALING_MESSAGE_TYPE_ANSWER) { + MUTEX_LOCK(pSignalingClient->offerSendReceiveTimeLock); + PROFILE_WITH_START_TIME_OBJ(pSignalingClient->offerSentTime, pSignalingClient->diagnostics.offerToAnswerTime, + "Offer Sent to Answer Received time"); + MUTEX_UNLOCK(pSignalingClient->offerSendReceiveTimeLock); + } // Calling client receive message callback if specified if (pSignalingClient->signalingClientCallbacks.messageReceivedFn != NULL) { - if (messageType == SIGNALING_MESSAGE_TYPE_OFFER) { - pSignalingClient->offerTime = GETTIME(); - } - if (messageType == SIGNALING_MESSAGE_TYPE_ANSWER) { - PROFILE_WITH_START_TIME_OBJ(pSignalingClient->offerTime, pSignalingClient->diagnostics.offerToAnswerTime, "Offer to answer time"); - } CHK_STATUS(pSignalingClient->signalingClientCallbacks.messageReceivedFn(pSignalingClient->signalingClientCallbacks.customData, &pSignalingMessageWrapper->receivedSignalingMessage)); } diff --git a/src/source/Signaling/LwsApiCalls.h b/src/source/Signaling/LwsApiCalls.h index 83d76c032b..45c4ed9e22 100644 --- a/src/source/Signaling/LwsApiCalls.h +++ b/src/source/Signaling/LwsApiCalls.h @@ -31,12 +31,17 @@ extern "C" { #define GET_SIGNALING_CHANNEL_ENDPOINT_API_POSTFIX "/getSignalingChannelEndpoint" #define DELETE_SIGNALING_CHANNEL_API_POSTFIX "/deleteSignalingChannel" #define GET_ICE_CONFIG_API_POSTFIX "/v1/get-ice-server-config" +#define JOIN_STORAGE_SESSION_API_POSTFIX "/joinStorageSession" +#define DESCRIBE_MEDIA_STORAGE_CONF_API_POSTFIX "/describeMediaStorageConfiguration" +#define UPDATE_MEDIA_STORAGE_CONF_API_POSTFIX "/updateMediaStorageConfiguration" // Signaling protocol name -#define SIGNALING_CHANNEL_PROTOCOL "\"WSS\", \"HTTPS\"" +#define SIGNALING_CHANNEL_PROTOCOL "\"WSS\", \"HTTPS\"" +#define SIGNALING_CHANNEL_PROTOCOL_W_MEDIA_STORAGE "\"WSS\", \"HTTPS\", \"WEBRTC\"" // Parameterized string for Describe Channel API -#define DESCRIBE_CHANNEL_PARAM_JSON_TEMPLATE "{\n\t\"ChannelName\": \"%s\"\n}" +#define DESCRIBE_CHANNEL_PARAM_JSON_TEMPLATE "{\n\t\"ChannelName\": \"%s\"\n}" +#define DESCRIBE_MEDIA_STORAGE_CONF_PARAM_JSON_TEMPLATE "{\n\t\"ChannelARN\": \"%s\"\n}" // Parameterized string for Delete Channel API #define DELETE_CHANNEL_PARAM_JSON_TEMPLATE \ @@ -72,6 +77,16 @@ extern "C" { "\n\t\"Service\": \"TURN\"" \ "\n}" +#define SIGNALING_JOIN_STORAGE_SESSION_MASTER_PARAM_JSON_TEMPLATE "{\n\t\"channelArn\": \"%s\"\n}" +#define SIGNALING_JOIN_STORAGE_SESSION_VIEWER_PARAM_JSON_TEMPLATE \ + "{\n\t\"channelArn\": \"%s\"," \ + "\n\t\"clientId\": \"%s\"\n}" +#define SIGNALING_UPDATE_STORAGE_CONFIG_PARAM_JSON_TEMPLATE \ + "{\n\t\"StreamARN\": \"%s\"," \ + "\n\t\"ChannelARN\": \"%s\"," \ + "\n\t\"StorageStatus\": \"%s\"" \ + "\n}" + // Parameter names for Signaling connect URL #define SIGNALING_ROLE_PARAM_NAME "X-Amz-Role" #define SIGNALING_CHANNEL_NAME_PARAM_NAME "X-Amz-ChannelName" @@ -247,6 +262,8 @@ STATUS createChannelLws(PSignalingClient, UINT64); STATUS getChannelEndpointLws(PSignalingClient, UINT64); STATUS getIceConfigLws(PSignalingClient, UINT64); STATUS connectSignalingChannelLws(PSignalingClient, UINT64); +STATUS joinStorageSessionLws(PSignalingClient, UINT64); +STATUS describeMediaStorageConfLws(PSignalingClient, UINT64); STATUS deleteChannelLws(PSignalingClient, UINT64); STATUS createLwsCallInfo(PSignalingClient, PRequestInfo, UINT32, PLwsCallInfo*); diff --git a/src/source/Signaling/Signaling.c b/src/source/Signaling/Signaling.c index c5761bf2b8..e0406e8bff 100644 --- a/src/source/Signaling/Signaling.c +++ b/src/source/Signaling/Signaling.c @@ -24,7 +24,7 @@ STATUS createSignalingSync(PSignalingClientInfoInternal pClientInfo, PChannelInf CHK(pClientInfo != NULL && pChannelInfo != NULL && pCallbacks != NULL && pCredentialProvider != NULL && ppSignalingClient != NULL, STATUS_NULL_ARG); CHK(pChannelInfo->version <= CHANNEL_INFO_CURRENT_VERSION, STATUS_SIGNALING_INVALID_CHANNEL_INFO_VERSION); - CHK(NULL != (pFileCacheEntry = (PSignalingFileCacheEntry) MEMALLOC(SIZEOF(SignalingFileCacheEntry))), STATUS_NOT_ENOUGH_MEMORY); + CHK(NULL != (pFileCacheEntry = (PSignalingFileCacheEntry) MEMCALLOC(1, SIZEOF(SignalingFileCacheEntry))), STATUS_NOT_ENOUGH_MEMORY); // Allocate enough storage CHK(NULL != (pSignalingClient = (PSignalingClient) MEMCALLOC(1, SIZEOF(SignalingClient))), STATUS_NOT_ENOUGH_MEMORY); @@ -41,7 +41,6 @@ STATUS createSignalingSync(PSignalingClientInfoInternal pClientInfo, PChannelInf CHK_STATUS(threadpoolCreate(&pSignalingClient->pThreadpool, pClientInfo->signalingClientInfo.signalingMessagesMinimumThreads, pClientInfo->signalingClientInfo.signalingMessagesMaximumThreads)); #endif - pSignalingClient->version = SIGNALING_CLIENT_CURRENT_VERSION; // Set invalid call times pSignalingClient->describeTime = INVALID_TIMESTAMP_VALUE; @@ -50,18 +49,29 @@ STATUS createSignalingSync(PSignalingClientInfoInternal pClientInfo, PChannelInf pSignalingClient->getIceConfigTime = INVALID_TIMESTAMP_VALUE; pSignalingClient->deleteTime = INVALID_TIMESTAMP_VALUE; pSignalingClient->connectTime = INVALID_TIMESTAMP_VALUE; + pSignalingClient->describeMediaTime = INVALID_TIMESTAMP_VALUE; + pSignalingClient->joinSessionTime = INVALID_TIMESTAMP_VALUE; + pSignalingClient->offerReceivedTime = INVALID_TIMESTAMP_VALUE; + pSignalingClient->offerSentTime = INVALID_TIMESTAMP_VALUE; if (pSignalingClient->pChannelInfo->cachingPolicy == SIGNALING_API_CALL_CACHE_TYPE_FILE) { - // Signaling channel name can be NULL in case of pre-created channels in which case we use ARN as the name - if (STATUS_FAILED(signalingCacheLoadFromFile(pChannelInfo->pChannelName != NULL ? pChannelInfo->pChannelName : pChannelInfo->pChannelArn, - pChannelInfo->pRegion, pChannelInfo->channelRoleType, pFileCacheEntry, &cacheFound, + if (STATUS_FAILED(signalingCacheLoadFromFile(pSignalingClient->pChannelInfo->pChannelName, pSignalingClient->pChannelInfo->pRegion, + pSignalingClient->pChannelInfo->channelRoleType, pFileCacheEntry, &cacheFound, pSignalingClient->clientInfo.cacheFilePath))) { DLOGW("Failed to load signaling cache from file"); } else if (cacheFound) { + STRCPY(pSignalingClient->channelDescription.channelName, pFileCacheEntry->channelName); STRCPY(pSignalingClient->channelDescription.channelArn, pFileCacheEntry->channelArn); + STRCPY(pSignalingClient->mediaStorageConfig.storageStreamArn, pFileCacheEntry->storageStreamArn); + // If client channel info has explicitly set use media storage to false, we need to set this to false even if the cache says the signaling + // channel has media enabled + pSignalingClient->mediaStorageConfig.storageStatus = + (pSignalingClient->pChannelInfo->useMediaStorage && (STRNCMP(pFileCacheEntry->storageEnabled, "1", 1) == 0)) ? TRUE : FALSE; STRCPY(pSignalingClient->channelEndpointHttps, pFileCacheEntry->httpsEndpoint); STRCPY(pSignalingClient->channelEndpointWss, pFileCacheEntry->wssEndpoint); + STRCPY(pSignalingClient->channelEndpointWebrtc, pFileCacheEntry->webrtcEndpoint); pSignalingClient->describeTime = pFileCacheEntry->creationTsEpochSeconds * HUNDREDS_OF_NANOS_IN_A_SECOND; + pSignalingClient->describeMediaTime = pFileCacheEntry->creationTsEpochSeconds * HUNDREDS_OF_NANOS_IN_A_SECOND; pSignalingClient->getEndpointTime = pFileCacheEntry->creationTsEpochSeconds * HUNDREDS_OF_NANOS_IN_A_SECOND; } } @@ -131,6 +141,12 @@ STATUS createSignalingSync(PSignalingClientInfoInternal pClientInfo, PChannelInf CHK(IS_VALID_CVAR_VALUE(pSignalingClient->receiveCvar), STATUS_INVALID_OPERATION); pSignalingClient->receiveLock = MUTEX_CREATE(FALSE); CHK(IS_VALID_MUTEX_VALUE(pSignalingClient->receiveLock), STATUS_INVALID_OPERATION); + pSignalingClient->jssWaitCvar = CVAR_CREATE(); + CHK(IS_VALID_CVAR_VALUE(pSignalingClient->jssWaitCvar), STATUS_INVALID_OPERATION); + pSignalingClient->jssWaitLock = MUTEX_CREATE(FALSE); + CHK(IS_VALID_MUTEX_VALUE(pSignalingClient->jssWaitLock), STATUS_INVALID_OPERATION); + pSignalingClient->offerSendReceiveTimeLock = MUTEX_CREATE(FALSE); + CHK(IS_VALID_MUTEX_VALUE(pSignalingClient->offerSendReceiveTimeLock), STATUS_INVALID_OPERATION); pSignalingClient->stateLock = MUTEX_CREATE(TRUE); CHK(IS_VALID_MUTEX_VALUE(pSignalingClient->stateLock), STATUS_INVALID_OPERATION); @@ -253,6 +269,14 @@ STATUS freeSignaling(PSignalingClient* ppSignalingClient) CVAR_FREE(pSignalingClient->receiveCvar); } + if (IS_VALID_MUTEX_VALUE(pSignalingClient->jssWaitLock)) { + MUTEX_FREE(pSignalingClient->jssWaitLock); + } + + if (IS_VALID_CVAR_VALUE(pSignalingClient->jssWaitCvar)) { + CVAR_FREE(pSignalingClient->jssWaitCvar); + } + if (IS_VALID_MUTEX_VALUE(pSignalingClient->stateLock)) { MUTEX_FREE(pSignalingClient->stateLock); } @@ -273,6 +297,10 @@ STATUS freeSignaling(PSignalingClient* ppSignalingClient) MUTEX_FREE(pSignalingClient->diagnosticsLock); } + if (IS_VALID_MUTEX_VALUE(pSignalingClient->offerSendReceiveTimeLock)) { + MUTEX_FREE(pSignalingClient->offerSendReceiveTimeLock); + } + uninitializeThreadTracker(&pSignalingClient->reconnecterTracker); uninitializeThreadTracker(&pSignalingClient->listenerTracker); @@ -387,12 +415,15 @@ STATUS signalingSendMessageSync(PSignalingClient pSignalingClient, PSignalingMes // Perform the call CHK_STATUS(sendLwsMessage(pSignalingClient, pSignalingMessage->messageType, pSignalingMessage->peerClientId, pSignalingMessage->payload, pSignalingMessage->payloadLen, pSignalingMessage->correlationId, 0)); - if (pSignalingMessage->messageType == SIGNALING_MESSAGE_TYPE_ANSWER) { - PROFILE_WITH_START_TIME_OBJ(pSignalingClient->offerTime, pSignalingClient->diagnostics.offerToAnswerTime, "Offer to answer time"); - } + + MUTEX_LOCK(pSignalingClient->offerSendReceiveTimeLock); if (pSignalingMessage->messageType == SIGNALING_MESSAGE_TYPE_OFFER) { - pSignalingClient->offerTime = GETTIME(); + pSignalingClient->offerSentTime = GETTIME(); + } else if (pSignalingMessage->messageType == SIGNALING_MESSAGE_TYPE_ANSWER) { + PROFILE_WITH_START_TIME_OBJ(pSignalingClient->offerReceivedTime, pSignalingClient->diagnostics.offerToAnswerTime, + "Offer Received to Answer Sent time"); } + MUTEX_UNLOCK(pSignalingClient->offerSendReceiveTimeLock); // Update the internal diagnostics only after successfully sending ATOMIC_INCREMENT(&pSignalingClient->diagnostics.numberOfMessagesSent); @@ -496,17 +527,21 @@ STATUS signalingConnectSync(PSignalingClient pSignalingClient) CHK(pSignalingClient != NULL, STATUS_NULL_ARG); // Validate the state - CHK_STATUS(acceptSignalingStateMachineState( - pSignalingClient, SIGNALING_STATE_READY | SIGNALING_STATE_CONNECT | SIGNALING_STATE_DISCONNECTED | SIGNALING_STATE_CONNECTED)); + CHK_STATUS(acceptSignalingStateMachineState(pSignalingClient, + SIGNALING_STATE_READY | SIGNALING_STATE_CONNECT | SIGNALING_STATE_DISCONNECTED | + SIGNALING_STATE_CONNECTED | SIGNALING_STATE_JOIN_SESSION | SIGNALING_STATE_JOIN_SESSION_WAITING | + SIGNALING_STATE_JOIN_SESSION_CONNECTED)); // Check if we are already connected - CHK(!ATOMIC_LOAD_BOOL(&pSignalingClient->connected), retStatus); + CHK(!ATOMIC_LOAD_BOOL(&pSignalingClient->connected) || pSignalingClient->mediaStorageConfig.storageStatus, retStatus); // Store the signaling state in case we error/timeout so we can re-set it on exit CHK_STATUS(getStateMachineCurrentState(pSignalingClient->pStateMachine, &pState)); + // If media storage is enabled we keep going until join session connected, otherwise stop at connected. CHK_STATUS(signalingStateMachineIterator(pSignalingClient, SIGNALING_GET_CURRENT_TIME(pSignalingClient) + SIGNALING_CONNECT_STATE_TIMEOUT, - SIGNALING_STATE_CONNECTED)); + pSignalingClient->mediaStorageConfig.storageStatus ? SIGNALING_STATE_JOIN_SESSION_CONNECTED + : SIGNALING_STATE_CONNECTED)); CleanUp: @@ -694,8 +729,10 @@ STATUS refreshIceConfiguration(PSignalingClient pSignalingClient) CHK(pSignalingClient->iceConfigCount == 0 || curTime > pSignalingClient->iceConfigExpiration, retStatus); // ICE config can be retrieved in specific states only - CHK_STATUS(acceptSignalingStateMachineState( - pSignalingClient, SIGNALING_STATE_READY | SIGNALING_STATE_CONNECT | SIGNALING_STATE_CONNECTED | SIGNALING_STATE_DISCONNECTED)); + CHK_STATUS(acceptSignalingStateMachineState(pSignalingClient, + SIGNALING_STATE_READY | SIGNALING_STATE_CONNECT | SIGNALING_STATE_CONNECTED | + SIGNALING_STATE_JOIN_SESSION | SIGNALING_STATE_JOIN_SESSION_WAITING | + SIGNALING_STATE_JOIN_SESSION_CONNECTED | SIGNALING_STATE_DISCONNECTED)); MUTEX_LOCK(pSignalingClient->stateLock); locked = TRUE; @@ -706,8 +743,9 @@ STATUS refreshIceConfiguration(PSignalingClient pSignalingClient) ATOMIC_STORE(&pSignalingClient->result, (SIZE_T) SERVICE_CALL_RESULT_SIGNALING_RECONNECT_ICE); ATOMIC_STORE(&pSignalingClient->refreshIceConfig, TRUE); DLOGI("Retrieving ICE config through getIceServerConfig call again"); - // Iterate the state machinery in steady states only - ready or connected - if (pStateMachineState->state == SIGNALING_STATE_READY || pStateMachineState->state == SIGNALING_STATE_CONNECTED) { + // Iterate the state machinery in steady states only - ready / connected / join session connected + if (pStateMachineState->state == SIGNALING_STATE_READY || pStateMachineState->state == SIGNALING_STATE_CONNECTED || + pStateMachineState->state == SIGNALING_STATE_JOIN_SESSION_CONNECTED) { CHK_STATUS(signalingStateMachineIterator(pSignalingClient, curTime + SIGNALING_REFRESH_ICE_CONFIG_STATE_TIMEOUT, pStateMachineState->state)); } @@ -1052,11 +1090,12 @@ STATUS getChannelEndpoint(PSignalingClient pSignalingClient, UINT64 time) case SIGNALING_API_CALL_CACHE_TYPE_DESCRIBE_GETENDPOINT: /* explicit fall-through */ case SIGNALING_API_CALL_CACHE_TYPE_FILE: + DLOGD("time: %llu, endpoint time: %llu, Caching Period: %llu", time, pSignalingClient->getEndpointTime, + pSignalingClient->pChannelInfo->cachingPeriod); if (IS_VALID_TIMESTAMP(pSignalingClient->getEndpointTime) && time <= pSignalingClient->getEndpointTime + pSignalingClient->pChannelInfo->cachingPeriod) { apiCall = FALSE; } - break; } @@ -1082,8 +1121,11 @@ STATUS getChannelEndpoint(PSignalingClient pSignalingClient, UINT64 time) : pSignalingClient->pChannelInfo->pChannelArn); STRCPY(signalingFileCacheEntry.region, pSignalingClient->pChannelInfo->pRegion); STRCPY(signalingFileCacheEntry.channelArn, pSignalingClient->channelDescription.channelArn); + STRCPY(signalingFileCacheEntry.storageEnabled, pSignalingClient->mediaStorageConfig.storageStatus ? "1" : "0"); + STRCPY(signalingFileCacheEntry.storageStreamArn, pSignalingClient->mediaStorageConfig.storageStreamArn); STRCPY(signalingFileCacheEntry.httpsEndpoint, pSignalingClient->channelEndpointHttps); STRCPY(signalingFileCacheEntry.wssEndpoint, pSignalingClient->channelEndpointWss); + STRCPY(signalingFileCacheEntry.webrtcEndpoint, pSignalingClient->channelEndpointWebrtc); if (STATUS_FAILED(signalingCacheSaveToFile(&signalingFileCacheEntry, pSignalingClient->clientInfo.cacheFilePath))) { DLOGW("Failed to save signaling cache to file"); } @@ -1236,6 +1278,113 @@ STATUS connectSignalingChannel(PSignalingClient pSignalingClient, UINT64 time) return retStatus; } +STATUS joinStorageSession(PSignalingClient pSignalingClient, UINT64 time) +{ + ENTERS(); + STATUS retStatus = STATUS_SUCCESS; + + CHK(pSignalingClient != NULL, STATUS_NULL_ARG); + + THREAD_SLEEP_UNTIL(time); + + CHK(pSignalingClient->mediaStorageConfig.storageStatus == TRUE, STATUS_SIGNALING_MEDIA_STORAGE_DISABLED); + // Check for the stale credentials + CHECK_SIGNALING_CREDENTIALS_EXPIRATION(pSignalingClient); + + ATOMIC_STORE(&pSignalingClient->result, (SIZE_T) SERVICE_CALL_RESULT_NOT_SET); + + // We are not caching connect calls + if (pSignalingClient->clientInfo.joinSessionPreHookFn != NULL) { + retStatus = pSignalingClient->clientInfo.joinSessionPreHookFn(pSignalingClient->clientInfo.hookCustomData); + } + + if (STATUS_SUCCEEDED(retStatus)) { + if (ATOMIC_LOAD_BOOL(&pSignalingClient->connected)) { + retStatus = joinStorageSessionLws(pSignalingClient, time); + + // Store the time of the call on success + if (STATUS_SUCCEEDED(retStatus)) { + pSignalingClient->joinSessionTime = time; + } + // Calculate the latency whether the call succeeded or not + SIGNALING_API_LATENCY_CALCULATION(pSignalingClient, time, TRUE); + } else { + ATOMIC_STORE(&pSignalingClient->result, (SIZE_T) SERVICE_CALL_INVALID_ARG); //!< TBD + } + } + + if (pSignalingClient->clientInfo.joinSessionPostHookFn != NULL) { + retStatus = pSignalingClient->clientInfo.joinSessionPostHookFn(pSignalingClient->clientInfo.hookCustomData); + } + +CleanUp: + + LEAVES(); + return retStatus; +} + +STATUS describeMediaStorageConf(PSignalingClient pSignalingClient, UINT64 time) +{ + ENTERS(); + STATUS retStatus = STATUS_SUCCESS; + BOOL apiCall = TRUE; + + CHK(pSignalingClient != NULL, STATUS_NULL_ARG); + + THREAD_SLEEP_UNTIL(time); + // Check for the stale credentials + CHECK_SIGNALING_CREDENTIALS_EXPIRATION(pSignalingClient); + + ATOMIC_STORE(&pSignalingClient->result, (SIZE_T) SERVICE_CALL_RESULT_NOT_SET); + + switch (pSignalingClient->pChannelInfo->cachingPolicy) { + case SIGNALING_API_CALL_CACHE_TYPE_NONE: + break; + + case SIGNALING_API_CALL_CACHE_TYPE_DESCRIBE_GETENDPOINT: + /* explicit fall-through */ + case SIGNALING_API_CALL_CACHE_TYPE_FILE: + if (IS_VALID_TIMESTAMP(pSignalingClient->describeMediaTime) && + time <= pSignalingClient->describeMediaTime + pSignalingClient->pChannelInfo->cachingPeriod) { + apiCall = FALSE; + } + + break; + } + + // Call API + if (STATUS_SUCCEEDED(retStatus)) { + if (apiCall) { + // Call pre hook func + if (pSignalingClient->clientInfo.describeMediaStorageConfPreHookFn != NULL) { + retStatus = pSignalingClient->clientInfo.describeMediaStorageConfPreHookFn(pSignalingClient->clientInfo.hookCustomData); + } + + if (STATUS_SUCCEEDED(retStatus)) { + retStatus = describeMediaStorageConfLws(pSignalingClient, time); + // Store the last call time on success + if (STATUS_SUCCEEDED(retStatus)) { + pSignalingClient->describeMediaTime = time; + } + // Calculate the latency whether the call succeeded or not + SIGNALING_API_LATENCY_CALCULATION(pSignalingClient, time, TRUE); + } + + // Call post hook func + if (pSignalingClient->clientInfo.describeMediaStorageConfPostHookFn != NULL) { + retStatus = pSignalingClient->clientInfo.describeMediaStorageConfPostHookFn(pSignalingClient->clientInfo.hookCustomData); + } + } else { + ATOMIC_STORE(&pSignalingClient->result, (SIZE_T) SERVICE_CALL_RESULT_OK); + } + } + +CleanUp: + + LEAVES(); + return retStatus; +} + UINT64 signalingGetCurrentTime(UINT64 customData) { UNUSED_PARAM(customData); @@ -1267,6 +1416,7 @@ STATUS signalingGetMetrics(PSignalingClient pSignalingClient, PSignalingClientMe case 1: pSignalingClientMetrics->signalingClientStats.getTokenCallTime = pSignalingClient->diagnostics.getTokenCallTime; pSignalingClientMetrics->signalingClientStats.describeCallTime = pSignalingClient->diagnostics.describeCallTime; + pSignalingClientMetrics->signalingClientStats.describeMediaCallTime = pSignalingClient->diagnostics.describeMediaCallTime; pSignalingClientMetrics->signalingClientStats.createCallTime = pSignalingClient->diagnostics.createCallTime; pSignalingClientMetrics->signalingClientStats.getEndpointCallTime = pSignalingClient->diagnostics.getEndpointCallTime; pSignalingClientMetrics->signalingClientStats.getIceConfigCallTime = pSignalingClient->diagnostics.getIceConfigCallTime; @@ -1274,7 +1424,9 @@ STATUS signalingGetMetrics(PSignalingClient pSignalingClient, PSignalingClientMe pSignalingClientMetrics->signalingClientStats.createClientTime = pSignalingClient->diagnostics.createClientTime; pSignalingClientMetrics->signalingClientStats.fetchClientTime = pSignalingClient->diagnostics.fetchClientTime; pSignalingClientMetrics->signalingClientStats.connectClientTime = pSignalingClient->diagnostics.connectClientTime; + pSignalingClientMetrics->signalingClientStats.joinSessionCallTime = pSignalingClient->diagnostics.joinSessionCallTime; pSignalingClientMetrics->signalingClientStats.offerToAnswerTime = pSignalingClient->diagnostics.offerToAnswerTime; + pSignalingClientMetrics->signalingClientStats.joinSessionToOfferRecvTime = pSignalingClient->diagnostics.joinSessionToOfferRecvTime; case 0: // Fill in the data structures according to the version of the requested structure pSignalingClientMetrics->signalingClientStats.signalingClientUptime = curTime - pSignalingClient->diagnostics.createTime; diff --git a/src/source/Signaling/Signaling.h b/src/source/Signaling/Signaling.h index 0355be4bac..6f0aee0209 100644 --- a/src/source/Signaling/Signaling.h +++ b/src/source/Signaling/Signaling.h @@ -24,19 +24,23 @@ extern "C" { #define SIGNALING_CLIENT_SHUTDOWN_TIMEOUT ((2 + SIGNALING_SERVICE_API_CALL_TIMEOUT_IN_SECONDS) * HUNDREDS_OF_NANOS_IN_A_SECOND) // Signaling client state literal definitions -#define SIGNALING_CLIENT_STATE_UNKNOWN_STR "Unknown" -#define SIGNALING_CLIENT_STATE_NEW_STR "New" -#define SIGNALING_CLIENT_STATE_GET_CREDENTIALS_STR "Get Security Credentials" -#define SIGNALING_CLIENT_STATE_DESCRIBE_STR "Describe Channel" -#define SIGNALING_CLIENT_STATE_CREATE_STR "Create Channel" -#define SIGNALING_CLIENT_STATE_GET_ENDPOINT_STR "Get Channel Endpoint" -#define SIGNALING_CLIENT_STATE_GET_ICE_CONFIG_STR "Get ICE Server Configuration" -#define SIGNALING_CLIENT_STATE_READY_STR "Ready" -#define SIGNALING_CLIENT_STATE_CONNECTING_STR "Connecting" -#define SIGNALING_CLIENT_STATE_CONNECTED_STR "Connected" -#define SIGNALING_CLIENT_STATE_DISCONNECTED_STR "Disconnected" -#define SIGNALING_CLIENT_STATE_DELETE_STR "Delete" -#define SIGNALING_CLIENT_STATE_DELETED_STR "Deleted" +#define SIGNALING_CLIENT_STATE_UNKNOWN_STR "Unknown" +#define SIGNALING_CLIENT_STATE_NEW_STR "New" +#define SIGNALING_CLIENT_STATE_GET_CREDENTIALS_STR "Get Security Credentials" +#define SIGNALING_CLIENT_STATE_DESCRIBE_STR "Describe Channel" +#define SIGNALING_CLIENT_STATE_CREATE_STR "Create Channel" +#define SIGNALING_CLIENT_STATE_GET_ENDPOINT_STR "Get Channel Endpoint" +#define SIGNALING_CLIENT_STATE_GET_ICE_CONFIG_STR "Get ICE Server Configuration" +#define SIGNALING_CLIENT_STATE_READY_STR "Ready" +#define SIGNALING_CLIENT_STATE_CONNECTING_STR "Connecting" +#define SIGNALING_CLIENT_STATE_CONNECTED_STR "Connected" +#define SIGNALING_CLIENT_STATE_DISCONNECTED_STR "Disconnected" +#define SIGNALING_CLIENT_STATE_DELETE_STR "Delete" +#define SIGNALING_CLIENT_STATE_DELETED_STR "Deleted" +#define SIGNALING_CLIENT_STATE_DESCRIBE_MEDIA_STR "Describe Media Storage" +#define SIGNALING_CLIENT_STATE_JOIN_SESSION_STR "Join Session" +#define SIGNALING_CLIENT_STATE_JOIN_SESSION_WAITING_STR "Join Session Waiting" +#define SIGNALING_CLIENT_STATE_JOIN_SESSION_CONNECTED_STR "Join Session Connected" // Error refreshing ICE server configuration string #define SIGNALING_ICE_CONFIG_REFRESH_ERROR_MSG "Failed refreshing ICE server configuration with status code 0x%08x." @@ -148,6 +152,10 @@ typedef struct { SignalingApiCallHookFunc getIceConfigPostHookFn; SignalingApiCallHookFunc connectPreHookFn; SignalingApiCallHookFunc connectPostHookFn; + SignalingApiCallHookFunc joinSessionPreHookFn; + SignalingApiCallHookFunc joinSessionPostHookFn; + SignalingApiCallHookFunc describeMediaStorageConfPreHookFn; + SignalingApiCallHookFunc describeMediaStorageConfPostHookFn; SignalingApiCallHookFunc deletePreHookFn; SignalingApiCallHookFunc deletePostHookFn; @@ -182,6 +190,7 @@ typedef struct { UINT64 dpApiLatency; UINT64 getTokenCallTime; UINT64 describeCallTime; + UINT64 describeMediaCallTime; UINT64 createCallTime; UINT64 getEndpointCallTime; UINT64 getIceConfigCallTime; @@ -190,6 +199,8 @@ typedef struct { UINT64 fetchClientTime; UINT64 connectClientTime; UINT64 offerToAnswerTime; + UINT64 joinSessionCallTime; + UINT64 joinSessionToOfferRecvTime; PHashTable pEndpointToClockSkewHashMap; UINT32 stateMachineRetryCount; } SignalingDiagnostics, PSignalingDiagnostics; @@ -232,6 +243,8 @@ typedef struct { // Indicates that there is another thread attempting to grab the service lock volatile ATOMIC_BOOL serviceLockContention; + volatile ATOMIC_BOOL offerReceived; + // Stored Client info SignalingClientInfoInternal clientInfo; @@ -247,12 +260,18 @@ typedef struct { // Returned signaling channel description SignalingChannelDescription channelDescription; + // Returned media storage session + MediaStorageConfig mediaStorageConfig; + // Signaling endpoint CHAR channelEndpointWss[MAX_SIGNALING_ENDPOINT_URI_LEN + 1]; // Signaling endpoint CHAR channelEndpointHttps[MAX_SIGNALING_ENDPOINT_URI_LEN + 1]; + // Media storage endpoint + CHAR channelEndpointWebrtc[MAX_SIGNALING_ENDPOINT_URI_LEN + 1]; + // Number of Ice Server objects UINT32 iceConfigCount; @@ -338,11 +357,23 @@ typedef struct { UINT64 getIceConfigTime; UINT64 deleteTime; UINT64 connectTime; + UINT64 describeMediaTime; #ifdef KVS_USE_SIGNALING_CHANNEL_THREADPOOL PThreadpool pThreadpool; #endif - UINT64 offerTime; + UINT64 offerReceivedTime; + UINT64 offerSentTime; + + MUTEX offerSendReceiveTimeLock; + UINT64 joinSessionTime; + + // mutex for join session wait condition variable + MUTEX jssWaitLock; + + // Conditional variable for join storage session wait state + CVAR jssWaitCvar; + } SignalingClient, *PSignalingClient; // Public handle to and from object converters @@ -383,6 +414,8 @@ STATUS createChannel(PSignalingClient, UINT64); STATUS getChannelEndpoint(PSignalingClient, UINT64); STATUS getIceConfig(PSignalingClient, UINT64); STATUS connectSignalingChannel(PSignalingClient, UINT64); +STATUS joinStorageSession(PSignalingClient, UINT64); +STATUS describeMediaStorageConf(PSignalingClient, UINT64); STATUS deleteChannel(PSignalingClient, UINT64); STATUS signalingGetMetrics(PSignalingClient, PSignalingClientMetrics); diff --git a/src/source/Signaling/StateMachine.c b/src/source/Signaling/StateMachine.c index 44462f2af7..3c4d0d8dbf 100644 --- a/src/source/Signaling/StateMachine.c +++ b/src/source/Signaling/StateMachine.c @@ -11,44 +11,63 @@ StateMachineState SIGNALING_STATE_MACHINE_STATES[] = { {SIGNALING_STATE_NEW, SIGNALING_STATE_NONE | SIGNALING_STATE_NEW, fromNewSignalingState, executeNewSignalingState, defaultSignalingStateTransitionHook, INFINITE_RETRY_COUNT_SENTINEL, STATUS_SIGNALING_INVALID_READY_STATE}, {SIGNALING_STATE_GET_TOKEN, - SIGNALING_STATE_NEW | SIGNALING_STATE_DESCRIBE | SIGNALING_STATE_CREATE | SIGNALING_STATE_GET_ENDPOINT | SIGNALING_STATE_GET_ICE_CONFIG | - SIGNALING_STATE_READY | SIGNALING_STATE_CONNECT | SIGNALING_STATE_CONNECTED | SIGNALING_STATE_DELETE | SIGNALING_STATE_GET_TOKEN, + SIGNALING_STATE_NEW | SIGNALING_STATE_DESCRIBE | SIGNALING_STATE_DESCRIBE_MEDIA | SIGNALING_STATE_CREATE | SIGNALING_STATE_GET_ENDPOINT | + SIGNALING_STATE_GET_ICE_CONFIG | SIGNALING_STATE_READY | SIGNALING_STATE_CONNECT | SIGNALING_STATE_CONNECTED | SIGNALING_STATE_JOIN_SESSION | + SIGNALING_STATE_JOIN_SESSION_WAITING | SIGNALING_STATE_JOIN_SESSION_CONNECTED | SIGNALING_STATE_DELETE | SIGNALING_STATE_GET_TOKEN, fromGetTokenSignalingState, executeGetTokenSignalingState, defaultSignalingStateTransitionHook, SIGNALING_STATES_DEFAULT_RETRY_COUNT, STATUS_SIGNALING_GET_TOKEN_CALL_FAILED}, {SIGNALING_STATE_DESCRIBE, SIGNALING_STATE_GET_TOKEN | SIGNALING_STATE_CREATE | SIGNALING_STATE_GET_ENDPOINT | SIGNALING_STATE_GET_ICE_CONFIG | SIGNALING_STATE_CONNECT | - SIGNALING_STATE_CONNECTED | SIGNALING_STATE_DELETE | SIGNALING_STATE_DESCRIBE | SIGNALING_STATE_READY | SIGNALING_STATE_DISCONNECTED, + SIGNALING_STATE_CONNECTED | SIGNALING_STATE_JOIN_SESSION | SIGNALING_STATE_JOIN_SESSION_CONNECTED | SIGNALING_STATE_DELETE | + SIGNALING_STATE_DESCRIBE | SIGNALING_STATE_READY | SIGNALING_STATE_DISCONNECTED, fromDescribeSignalingState, executeDescribeSignalingState, defaultSignalingStateTransitionHook, SIGNALING_STATES_DEFAULT_RETRY_COUNT, STATUS_SIGNALING_DESCRIBE_CALL_FAILED}, - {SIGNALING_STATE_CREATE, SIGNALING_STATE_DESCRIBE | SIGNALING_STATE_CREATE, fromCreateSignalingState, executeCreateSignalingState, - defaultSignalingStateTransitionHook, SIGNALING_STATES_DEFAULT_RETRY_COUNT, STATUS_SIGNALING_CREATE_CALL_FAILED}, + {SIGNALING_STATE_DESCRIBE_MEDIA, SIGNALING_STATE_DESCRIBE | SIGNALING_STATE_DESCRIBE_MEDIA, fromDescribeMediaStorageConfState, + executeDescribeMediaStorageConfState, defaultSignalingStateTransitionHook, SIGNALING_STATES_DEFAULT_RETRY_COUNT, + STATUS_SIGNALING_DESCRIBE_MEDIA_CALL_FAILED}, + {SIGNALING_STATE_CREATE, SIGNALING_STATE_DESCRIBE | SIGNALING_STATE_DESCRIBE_MEDIA | SIGNALING_STATE_CREATE, fromCreateSignalingState, + executeCreateSignalingState, defaultSignalingStateTransitionHook, SIGNALING_STATES_DEFAULT_RETRY_COUNT, STATUS_SIGNALING_CREATE_CALL_FAILED}, {SIGNALING_STATE_GET_ENDPOINT, - SIGNALING_STATE_DESCRIBE | SIGNALING_STATE_CREATE | SIGNALING_STATE_GET_TOKEN | SIGNALING_STATE_READY | SIGNALING_STATE_CONNECT | - SIGNALING_STATE_CONNECTED | SIGNALING_STATE_GET_ENDPOINT, + SIGNALING_STATE_DESCRIBE | SIGNALING_STATE_DESCRIBE_MEDIA | SIGNALING_STATE_CREATE | SIGNALING_STATE_GET_TOKEN | SIGNALING_STATE_READY | + SIGNALING_STATE_CONNECT | SIGNALING_STATE_CONNECTED | SIGNALING_STATE_JOIN_SESSION | SIGNALING_STATE_JOIN_SESSION_CONNECTED | + SIGNALING_STATE_GET_ENDPOINT, fromGetEndpointSignalingState, executeGetEndpointSignalingState, defaultSignalingStateTransitionHook, SIGNALING_STATES_DEFAULT_RETRY_COUNT, STATUS_SIGNALING_GET_ENDPOINT_CALL_FAILED}, {SIGNALING_STATE_GET_ICE_CONFIG, - SIGNALING_STATE_DESCRIBE | SIGNALING_STATE_CONNECT | SIGNALING_STATE_CONNECTED | SIGNALING_STATE_GET_ENDPOINT | SIGNALING_STATE_READY | - SIGNALING_STATE_GET_ICE_CONFIG, + SIGNALING_STATE_DESCRIBE | SIGNALING_STATE_DESCRIBE_MEDIA | SIGNALING_STATE_CONNECT | SIGNALING_STATE_CONNECTED | SIGNALING_STATE_JOIN_SESSION | + SIGNALING_STATE_JOIN_SESSION_CONNECTED | SIGNALING_STATE_GET_ENDPOINT | SIGNALING_STATE_READY | SIGNALING_STATE_GET_ICE_CONFIG, fromGetIceConfigSignalingState, executeGetIceConfigSignalingState, defaultSignalingStateTransitionHook, SIGNALING_STATES_DEFAULT_RETRY_COUNT, STATUS_SIGNALING_GET_ICE_CONFIG_CALL_FAILED}, {SIGNALING_STATE_READY, SIGNALING_STATE_GET_ICE_CONFIG | SIGNALING_STATE_DISCONNECTED | SIGNALING_STATE_READY, fromReadySignalingState, executeReadySignalingState, defaultSignalingStateTransitionHook, INFINITE_RETRY_COUNT_SENTINEL, STATUS_SIGNALING_READY_CALLBACK_FAILED}, - {SIGNALING_STATE_CONNECT, SIGNALING_STATE_READY | SIGNALING_STATE_DISCONNECTED | SIGNALING_STATE_CONNECTED | SIGNALING_STATE_CONNECT, + {SIGNALING_STATE_CONNECT, + SIGNALING_STATE_READY | SIGNALING_STATE_DISCONNECTED | SIGNALING_STATE_CONNECTED | SIGNALING_STATE_JOIN_SESSION | SIGNALING_STATE_CONNECT, fromConnectSignalingState, executeConnectSignalingState, defaultSignalingStateTransitionHook, INFINITE_RETRY_COUNT_SENTINEL, STATUS_SIGNALING_CONNECT_CALL_FAILED}, - {SIGNALING_STATE_CONNECTED, SIGNALING_STATE_CONNECT | SIGNALING_STATE_CONNECTED, fromConnectedSignalingState, executeConnectedSignalingState, - defaultSignalingStateTransitionHook, INFINITE_RETRY_COUNT_SENTINEL, STATUS_SIGNALING_CONNECTED_CALLBACK_FAILED}, - {SIGNALING_STATE_DISCONNECTED, SIGNALING_STATE_CONNECT | SIGNALING_STATE_CONNECTED, fromDisconnectedSignalingState, - executeDisconnectedSignalingState, defaultSignalingStateTransitionHook, SIGNALING_STATES_DEFAULT_RETRY_COUNT, + {SIGNALING_STATE_CONNECTED, SIGNALING_STATE_CONNECT | SIGNALING_STATE_CONNECTED | SIGNALING_STATE_JOIN_SESSION, fromConnectedSignalingState, + executeConnectedSignalingState, defaultSignalingStateTransitionHook, INFINITE_RETRY_COUNT_SENTINEL, STATUS_SIGNALING_CONNECTED_CALLBACK_FAILED}, + {SIGNALING_STATE_DISCONNECTED, + SIGNALING_STATE_CONNECT | SIGNALING_STATE_CONNECTED | SIGNALING_STATE_JOIN_SESSION | SIGNALING_STATE_JOIN_SESSION_WAITING | + SIGNALING_STATE_JOIN_SESSION_CONNECTED, + fromDisconnectedSignalingState, executeDisconnectedSignalingState, defaultSignalingStateTransitionHook, SIGNALING_STATES_DEFAULT_RETRY_COUNT, STATUS_SIGNALING_DISCONNECTED_CALLBACK_FAILED}, {SIGNALING_STATE_DELETE, - SIGNALING_STATE_GET_TOKEN | SIGNALING_STATE_DESCRIBE | SIGNALING_STATE_CREATE | SIGNALING_STATE_GET_ENDPOINT | SIGNALING_STATE_GET_ICE_CONFIG | - SIGNALING_STATE_READY | SIGNALING_STATE_CONNECT | SIGNALING_STATE_CONNECTED | SIGNALING_STATE_DISCONNECTED | SIGNALING_STATE_DELETE, + SIGNALING_STATE_GET_TOKEN | SIGNALING_STATE_DESCRIBE | SIGNALING_STATE_DESCRIBE_MEDIA | SIGNALING_STATE_CREATE | SIGNALING_STATE_GET_ENDPOINT | + SIGNALING_STATE_GET_ICE_CONFIG | SIGNALING_STATE_READY | SIGNALING_STATE_CONNECT | SIGNALING_STATE_CONNECTED | SIGNALING_STATE_JOIN_SESSION | + SIGNALING_STATE_DISCONNECTED | SIGNALING_STATE_DELETE, fromDeleteSignalingState, executeDeleteSignalingState, defaultSignalingStateTransitionHook, SIGNALING_STATES_DEFAULT_RETRY_COUNT, STATUS_SIGNALING_DELETE_CALL_FAILED}, {SIGNALING_STATE_DELETED, SIGNALING_STATE_DELETE | SIGNALING_STATE_DELETED, fromDeletedSignalingState, executeDeletedSignalingState, defaultSignalingStateTransitionHook, INFINITE_RETRY_COUNT_SENTINEL, STATUS_SIGNALING_DELETE_CALL_FAILED}, + {SIGNALING_STATE_JOIN_SESSION, SIGNALING_STATE_CONNECTED | SIGNALING_STATE_JOIN_SESSION_WAITING | SIGNALING_STATE_JOIN_SESSION_CONNECTED, + fromJoinStorageSessionState, executeJoinStorageSessionState, defaultSignalingStateTransitionHook, INFINITE_RETRY_COUNT_SENTINEL, + STATUS_SIGNALING_JOIN_SESSION_CALL_FAILED}, + {SIGNALING_STATE_JOIN_SESSION_WAITING, SIGNALING_STATE_JOIN_SESSION, fromJoinStorageSessionWaitingState, executeJoinStorageSessionWaitingState, + defaultSignalingStateTransitionHook, INFINITE_RETRY_COUNT_SENTINEL, STATUS_SIGNALING_JOIN_SESSION_CONNECTED_FAILED}, + {SIGNALING_STATE_JOIN_SESSION_CONNECTED, SIGNALING_STATE_JOIN_SESSION_WAITING | SIGNALING_STATE_JOIN_SESSION_CONNECTED, + fromJoinStorageSessionConnectedState, executeJoinStorageSessionConnectedState, defaultSignalingStateTransitionHook, + INFINITE_RETRY_COUNT_SENTINEL, STATUS_SIGNALING_JOIN_SESSION_CONNECTED_FAILED} + }; UINT32 SIGNALING_STATE_MACHINE_STATE_COUNT = ARRAY_SIZE(SIGNALING_STATE_MACHINE_STATES); @@ -135,7 +154,14 @@ STATUS signalingStateMachineIterator(PSignalingClient pSignalingClient, UINT64 e retStatus = stepStateMachine(pSignalingClient->pStateMachine); + if (STATUS_FAILED(retStatus)) { + DLOGD("Exited step state machine with status: 0x%08x", retStatus); + } + CHK_STATUS(getStateMachineCurrentState(pSignalingClient->pStateMachine, &pState)); + + DLOGV("State Machine - Current state: 0x%016" PRIx64, pState->state); + CHK(!(pState->state == finalState), STATUS_SUCCESS); } @@ -192,6 +218,18 @@ SIGNALING_CLIENT_STATE getSignalingStateFromStateMachineState(UINT64 state) case SIGNALING_STATE_DELETED: clientState = SIGNALING_CLIENT_STATE_DELETED; break; + case SIGNALING_STATE_DESCRIBE_MEDIA: + clientState = SIGNALING_CLIENT_STATE_DESCRIBE_MEDIA; + break; + case SIGNALING_STATE_JOIN_SESSION: + clientState = SIGNALING_CLIENT_STATE_JOIN_SESSION; + break; + case SIGNALING_STATE_JOIN_SESSION_WAITING: + clientState = SIGNALING_CLIENT_STATE_JOIN_SESSION_WAITING; + break; + case SIGNALING_STATE_JOIN_SESSION_CONNECTED: + clientState = SIGNALING_CLIENT_STATE_JOIN_SESSION_CONNECTED; + break; default: clientState = SIGNALING_CLIENT_STATE_UNKNOWN; } @@ -286,8 +324,7 @@ STATUS fromGetTokenSignalingState(UINT64 customData, PUINT64 pState) // Store the ARN in the stream description object first STRNCPY(pSignalingClient->channelDescription.channelArn, pSignalingClient->pChannelInfo->pChannelArn, MAX_ARN_LEN); pSignalingClient->channelDescription.channelArn[MAX_ARN_LEN] = '\0'; - - // Move to get endpoint state + // Move to get endpoint state if the media storage is not enabled. state = SIGNALING_STATE_GET_ENDPOINT; } else { state = SIGNALING_STATE_DESCRIBE; @@ -361,7 +398,11 @@ STATUS fromDescribeSignalingState(UINT64 customData, PUINT64 pState) if (ATOMIC_LOAD_BOOL(&pSignalingClient->deleting)) { state = SIGNALING_STATE_DELETE; } else { - state = SIGNALING_STATE_GET_ENDPOINT; + if (pSignalingClient->pChannelInfo->useMediaStorage) { + state = SIGNALING_STATE_DESCRIBE_MEDIA; + } else { + state = SIGNALING_STATE_GET_ENDPOINT; + } } break; @@ -414,6 +455,74 @@ STATUS executeDescribeSignalingState(UINT64 customData, UINT64 time) return retStatus; } +STATUS fromDescribeMediaStorageConfState(UINT64 customData, PUINT64 pState) +{ + ENTERS(); + STATUS retStatus = STATUS_SUCCESS; + PSignalingClient pSignalingClient = SIGNALING_CLIENT_FROM_CUSTOM_DATA(customData); + UINT64 state = SIGNALING_STATE_DESCRIBE_MEDIA; + SIZE_T result; + + CHK(pSignalingClient != NULL && pState != NULL, STATUS_NULL_ARG); + + result = ATOMIC_LOAD(&pSignalingClient->result); + switch (result) { + case SERVICE_CALL_RESULT_OK: + // If we are trying to delete the channel then move to delete state + if (ATOMIC_LOAD_BOOL(&pSignalingClient->deleting)) { + state = SIGNALING_STATE_DELETE; + } else { + state = SIGNALING_STATE_GET_ENDPOINT; + } + break; + + case SERVICE_CALL_RESOURCE_NOT_FOUND: + state = SIGNALING_STATE_CREATE; + break; + + case SERVICE_CALL_FORBIDDEN: + case SERVICE_CALL_NOT_AUTHORIZED: + state = SIGNALING_STATE_GET_TOKEN; + break; + + default: + break; + } + + *pState = state; + +CleanUp: + + LEAVES(); + return retStatus; +} + +STATUS executeDescribeMediaStorageConfState(UINT64 customData, UINT64 time) +{ + ENTERS(); + STATUS retStatus = STATUS_SUCCESS; + PSignalingClient pSignalingClient = SIGNALING_CLIENT_FROM_CUSTOM_DATA(customData); + UINT64 startTimeInMacro = 0; + + CHK(pSignalingClient != NULL, STATUS_NULL_ARG); + ATOMIC_STORE(&pSignalingClient->result, (SIZE_T) SERVICE_CALL_RESULT_NOT_SET); + + // Notify of the state change + if (pSignalingClient->signalingClientCallbacks.stateChangeFn != NULL) { + CHK_STATUS(pSignalingClient->signalingClientCallbacks.stateChangeFn(pSignalingClient->signalingClientCallbacks.customData, + SIGNALING_CLIENT_STATE_DESCRIBE_MEDIA)); + } + + // Call the aggregate function + PROFILE_CALL_WITH_T_OBJ(retStatus = describeMediaStorageConf(pSignalingClient, time), pSignalingClient->diagnostics.describeMediaCallTime, + "Describe Media Storage call"); + +CleanUp: + + LEAVES(); + return retStatus; +} + STATUS fromCreateSignalingState(UINT64 customData, PUINT64 pState) { ENTERS(); @@ -552,6 +661,14 @@ STATUS fromGetIceConfigSignalingState(UINT64 customData, PUINT64 pState) case SERVICE_CALL_NOT_AUTHORIZED: state = SIGNALING_STATE_GET_TOKEN; break; + case SERVICE_CALL_RESOURCE_NOT_FOUND: + // This can happen if we read from the cache and the channel either doesn't exist + // Or was re-created so now has a new channel arn. We need to invalidate the cache. + pSignalingClient->describeTime = INVALID_TIMESTAMP_VALUE; + pSignalingClient->describeMediaTime = INVALID_TIMESTAMP_VALUE; + pSignalingClient->getEndpointTime = INVALID_TIMESTAMP_VALUE; + state = SIGNALING_STATE_DESCRIBE; + break; default: break; @@ -762,6 +879,8 @@ STATUS fromConnectedSignalingState(UINT64 customData, PUINT64 pState) case SERVICE_CALL_RESULT_OK: if (!ATOMIC_LOAD_BOOL(&pSignalingClient->connected)) { state = SIGNALING_STATE_DISCONNECTED; + } else if (pSignalingClient->mediaStorageConfig.storageStatus) { + state = SIGNALING_STATE_JOIN_SESSION; } break; @@ -829,6 +948,254 @@ STATUS executeConnectedSignalingState(UINT64 customData, UINT64 time) return retStatus; } +STATUS fromJoinStorageSessionState(UINT64 customData, PUINT64 pState) +{ + ENTERS(); + STATUS retStatus = STATUS_SUCCESS; + PSignalingClient pSignalingClient = SIGNALING_CLIENT_FROM_CUSTOM_DATA(customData); + UINT64 state = SIGNALING_STATE_CONNECT; + SIZE_T result; + + CHK(pSignalingClient != NULL && pState != NULL, STATUS_NULL_ARG); + + result = ATOMIC_LOAD(&pSignalingClient->result); + + switch (result) { + case SERVICE_CALL_RESULT_OK: + if (!ATOMIC_LOAD_BOOL(&pSignalingClient->connected)) { + state = SIGNALING_STATE_DISCONNECTED; + } else { + state = SIGNALING_STATE_JOIN_SESSION_WAITING; + } + break; + + case SERVICE_CALL_RESOURCE_NOT_FOUND: + state = SIGNALING_STATE_DESCRIBE; + break; + + case SERVICE_CALL_FORBIDDEN: + case SERVICE_CALL_NOT_AUTHORIZED: + state = SIGNALING_STATE_GET_TOKEN; + break; + + case SERVICE_CALL_INTERNAL_ERROR: + state = SIGNALING_STATE_GET_ENDPOINT; + break; + + state = SIGNALING_STATE_GET_ENDPOINT; + break; + + case SERVICE_CALL_RESULT_SIGNALING_RECONNECT_ICE: + state = SIGNALING_STATE_GET_ICE_CONFIG; + break; + + case SERVICE_CALL_BAD_REQUEST: + case SERVICE_CALL_NETWORK_CONNECTION_TIMEOUT: + case SERVICE_CALL_NETWORK_READ_TIMEOUT: + case SERVICE_CALL_REQUEST_TIMEOUT: + case SERVICE_CALL_GATEWAY_TIMEOUT: + // Attempt to get a new endpoint + state = SIGNALING_STATE_GET_ENDPOINT; + break; + + default: + DLOGW("unknown response code(%d).", result); + state = SIGNALING_STATE_GET_TOKEN; + break; + } + + // Overwrite the state if we are force refreshing + state = ATOMIC_EXCHANGE_BOOL(&pSignalingClient->refreshIceConfig, FALSE) ? SIGNALING_STATE_GET_ICE_CONFIG : state; + + *pState = state; + +CleanUp: + + LEAVES(); + return retStatus; +} + +STATUS executeJoinStorageSessionState(UINT64 customData, UINT64 time) +{ + ENTERS(); + STATUS retStatus = STATUS_SUCCESS; + PSignalingClient pSignalingClient = SIGNALING_CLIENT_FROM_CUSTOM_DATA(customData); + UINT64 startTimeInMacro = 0; + + CHK(pSignalingClient != NULL, STATUS_NULL_ARG); + + // Notify of the state change + if (pSignalingClient->signalingClientCallbacks.stateChangeFn != NULL) { + CHK_STATUS(pSignalingClient->signalingClientCallbacks.stateChangeFn(pSignalingClient->signalingClientCallbacks.customData, + SIGNALING_CLIENT_STATE_JOIN_SESSION)); + } + + // In case we are re-trying we need to reset this to false + ATOMIC_STORE_BOOL(&pSignalingClient->offerReceived, FALSE); + PROFILE_CALL_WITH_T_OBJ(retStatus = joinStorageSession(pSignalingClient, time), pSignalingClient->diagnostics.joinSessionCallTime, + "Join Session call"); + +CleanUp: + + LEAVES(); + return retStatus; +} + +STATUS fromJoinStorageSessionWaitingState(UINT64 customData, PUINT64 pState) +{ + ENTERS(); + STATUS retStatus = STATUS_SUCCESS; + PSignalingClient pSignalingClient = SIGNALING_CLIENT_FROM_CUSTOM_DATA(customData); + UINT64 state = SIGNALING_STATE_JOIN_SESSION; + SIZE_T result; + + CHK(pSignalingClient != NULL && pState != NULL, STATUS_NULL_ARG); + + result = ATOMIC_LOAD(&pSignalingClient->result); + + switch (result) { + case SERVICE_CALL_RESULT_OK: + if (!ATOMIC_LOAD_BOOL(&pSignalingClient->connected)) { + state = SIGNALING_STATE_DISCONNECTED; + } else { + state = SIGNALING_STATE_JOIN_SESSION_CONNECTED; + } + break; + case SERVICE_CALL_RESULT_NOT_SET: + // We timed out and did not get an offer in time + // so if we are still connected we need to retry join session + if (!ATOMIC_LOAD_BOOL(&pSignalingClient->connected)) { + state = SIGNALING_STATE_DISCONNECTED; + } else { + state = SIGNALING_STATE_JOIN_SESSION; + } + break; + + default: + DLOGW("unknown response code(%d).", result); + state = SIGNALING_STATE_GET_TOKEN; + break; + } + + *pState = state; + +CleanUp: + + LEAVES(); + return retStatus; +} + +STATUS executeJoinStorageSessionWaitingState(UINT64 customData, UINT64 time) +{ + ENTERS(); + UNUSED_PARAM(time); + STATUS retStatus = STATUS_SUCCESS; + BOOL locked = FALSE; + PSignalingClient pSignalingClient = SIGNALING_CLIENT_FROM_CUSTOM_DATA(customData); + + CHK(pSignalingClient != NULL, STATUS_NULL_ARG); + + // Notify of the state change + if (pSignalingClient->signalingClientCallbacks.stateChangeFn != NULL) { + CHK_STATUS(pSignalingClient->signalingClientCallbacks.stateChangeFn(pSignalingClient->signalingClientCallbacks.customData, + SIGNALING_CLIENT_STATE_JOIN_SESSION_WAITING)); + } + + MUTEX_LOCK(pSignalingClient->jssWaitLock); + locked = TRUE; + while (!ATOMIC_LOAD(&pSignalingClient->offerReceived)) { + DLOGI("Waiting for offer from JoinStorageSession Call."); + CHK_STATUS(CVAR_WAIT(pSignalingClient->jssWaitCvar, pSignalingClient->jssWaitLock, SIGNALING_JOIN_STORAGE_SESSION_WAIT_TIMEOUT)); + } + MUTEX_UNLOCK(pSignalingClient->jssWaitLock); + locked = FALSE; + +CleanUp: + + if (retStatus == STATUS_OPERATION_TIMED_OUT) { + ATOMIC_STORE(&pSignalingClient->result, (SIZE_T) SERVICE_CALL_RESULT_NOT_SET); + } else if (STATUS_SUCCEEDED(retStatus)) { + ATOMIC_STORE(&pSignalingClient->result, (SIZE_T) SERVICE_CALL_RESULT_OK); + } else { + ATOMIC_STORE(&pSignalingClient->result, (SIZE_T) SERVICE_CALL_UNKNOWN); + } + + if (locked) { + MUTEX_UNLOCK(pSignalingClient->jssWaitLock); + } + + LEAVES(); + return retStatus; +} + +STATUS fromJoinStorageSessionConnectedState(UINT64 customData, PUINT64 pState) +{ + ENTERS(); + STATUS retStatus = STATUS_SUCCESS; + PSignalingClient pSignalingClient = SIGNALING_CLIENT_FROM_CUSTOM_DATA(customData); + UINT64 state = SIGNALING_STATE_CONNECTED; + SIZE_T result; + + CHK(pSignalingClient != NULL && pState != NULL, STATUS_NULL_ARG); + + result = ATOMIC_LOAD(&pSignalingClient->result); + switch (result) { + case SERVICE_CALL_RESULT_OK: + if (!ATOMIC_LOAD_BOOL(&pSignalingClient->connected)) { + state = SIGNALING_STATE_DISCONNECTED; + } else if (pSignalingClient->mediaStorageConfig.storageStatus) { + // Before calling JoinSession again after stepping out of the + // storage streaming state, we need to update ice config + state = SIGNALING_STATE_GET_ICE_CONFIG; + } + + break; + + case SERVICE_CALL_RESULT_SIGNALING_GO_AWAY: + state = SIGNALING_STATE_DESCRIBE; + break; + + case SERVICE_CALL_RESULT_SIGNALING_RECONNECT_ICE: + state = SIGNALING_STATE_GET_ICE_CONFIG; + break; + + default: + state = SIGNALING_STATE_GET_TOKEN; + break; + } + + // Overwrite the state if we are force refreshing + state = ATOMIC_EXCHANGE_BOOL(&pSignalingClient->refreshIceConfig, FALSE) ? SIGNALING_STATE_GET_ICE_CONFIG : state; + + *pState = state; + +CleanUp: + + LEAVES(); + return retStatus; +} + +STATUS executeJoinStorageSessionConnectedState(UINT64 customData, UINT64 time) +{ + ENTERS(); + UNUSED_PARAM(time); + STATUS retStatus = STATUS_SUCCESS; + PSignalingClient pSignalingClient = SIGNALING_CLIENT_FROM_CUSTOM_DATA(customData); + + CHK(pSignalingClient != NULL, STATUS_NULL_ARG); + + // Notify of the state change + if (pSignalingClient->signalingClientCallbacks.stateChangeFn != NULL) { + CHK_STATUS(pSignalingClient->signalingClientCallbacks.stateChangeFn(pSignalingClient->signalingClientCallbacks.customData, + SIGNALING_CLIENT_STATE_JOIN_SESSION_CONNECTED)); + } + +CleanUp: + + LEAVES(); + return retStatus; +} + STATUS fromDisconnectedSignalingState(UINT64 customData, PUINT64 pState) { ENTERS(); diff --git a/src/source/Signaling/StateMachine.h b/src/source/Signaling/StateMachine.h index 545596cd89..5dda7bcf30 100644 --- a/src/source/Signaling/StateMachine.h +++ b/src/source/Signaling/StateMachine.h @@ -13,19 +13,23 @@ extern "C" { /** * Signaling states definitions */ -#define SIGNALING_STATE_NONE ((UINT64) 0) -#define SIGNALING_STATE_NEW ((UINT64) (1 << 0)) -#define SIGNALING_STATE_GET_TOKEN ((UINT64) (1 << 1)) -#define SIGNALING_STATE_DESCRIBE ((UINT64) (1 << 2)) -#define SIGNALING_STATE_CREATE ((UINT64) (1 << 3)) -#define SIGNALING_STATE_GET_ENDPOINT ((UINT64) (1 << 4)) -#define SIGNALING_STATE_GET_ICE_CONFIG ((UINT64) (1 << 5)) -#define SIGNALING_STATE_READY ((UINT64) (1 << 6)) -#define SIGNALING_STATE_CONNECT ((UINT64) (1 << 7)) -#define SIGNALING_STATE_CONNECTED ((UINT64) (1 << 8)) -#define SIGNALING_STATE_DISCONNECTED ((UINT64) (1 << 9)) -#define SIGNALING_STATE_DELETE ((UINT64) (1 << 10)) -#define SIGNALING_STATE_DELETED ((UINT64) (1 << 11)) +#define SIGNALING_STATE_NONE ((UINT64) 0) +#define SIGNALING_STATE_NEW ((UINT64) (1 << 0)) +#define SIGNALING_STATE_GET_TOKEN ((UINT64) (1 << 1)) +#define SIGNALING_STATE_DESCRIBE ((UINT64) (1 << 2)) +#define SIGNALING_STATE_CREATE ((UINT64) (1 << 3)) +#define SIGNALING_STATE_GET_ENDPOINT ((UINT64) (1 << 4)) +#define SIGNALING_STATE_GET_ICE_CONFIG ((UINT64) (1 << 5)) +#define SIGNALING_STATE_READY ((UINT64) (1 << 6)) +#define SIGNALING_STATE_CONNECT ((UINT64) (1 << 7)) +#define SIGNALING_STATE_CONNECTED ((UINT64) (1 << 8)) +#define SIGNALING_STATE_DISCONNECTED ((UINT64) (1 << 9)) +#define SIGNALING_STATE_DELETE ((UINT64) (1 << 10)) +#define SIGNALING_STATE_DELETED ((UINT64) (1 << 11)) +#define SIGNALING_STATE_DESCRIBE_MEDIA ((UINT64) (1 << 12)) +#define SIGNALING_STATE_JOIN_SESSION ((UINT64) (1 << 13)) +#define SIGNALING_STATE_JOIN_SESSION_WAITING ((UINT64) (1 << 14)) +#define SIGNALING_STATE_JOIN_SESSION_CONNECTED ((UINT64) (1 << 15)) // Indicates infinite retries #define INFINITE_RETRY_COUNT_SENTINEL 0 @@ -45,6 +49,8 @@ STATUS fromGetTokenSignalingState(UINT64, PUINT64); STATUS executeGetTokenSignalingState(UINT64, UINT64); STATUS fromDescribeSignalingState(UINT64, PUINT64); STATUS executeDescribeSignalingState(UINT64, UINT64); +STATUS fromDescribeMediaStorageConfState(UINT64, PUINT64); +STATUS executeDescribeMediaStorageConfState(UINT64, UINT64); STATUS fromCreateSignalingState(UINT64, PUINT64); STATUS executeCreateSignalingState(UINT64, UINT64); STATUS fromGetEndpointSignalingState(UINT64, PUINT64); @@ -55,6 +61,12 @@ STATUS fromReadySignalingState(UINT64, PUINT64); STATUS executeReadySignalingState(UINT64, UINT64); STATUS fromConnectSignalingState(UINT64, PUINT64); STATUS executeConnectSignalingState(UINT64, UINT64); +STATUS fromJoinStorageSessionState(UINT64, PUINT64); +STATUS executeJoinStorageSessionState(UINT64, UINT64); +STATUS fromJoinStorageSessionWaitingState(UINT64, PUINT64); +STATUS executeJoinStorageSessionWaitingState(UINT64, UINT64); +STATUS fromJoinStorageSessionConnectedState(UINT64, PUINT64); +STATUS executeJoinStorageSessionConnectedState(UINT64, UINT64); STATUS fromConnectedSignalingState(UINT64, PUINT64); STATUS executeConnectedSignalingState(UINT64, UINT64); STATUS fromDisconnectedSignalingState(UINT64, PUINT64); diff --git a/tst/CMakeLists.txt b/tst/CMakeLists.txt index e31cd7658f..fd75b33534 100644 --- a/tst/CMakeLists.txt +++ b/tst/CMakeLists.txt @@ -1,9 +1,9 @@ cmake_minimum_required(VERSION 3.6.3) +set(CMAKE_VERBOSE_MAKEFILE ON) # Enabling the instrumented allocators to track memory add_definitions(-DINSTRUMENTED_ALLOCATORS) - project (WebRTCClientTest) set(CMAKE_CXX_STANDARD 11) @@ -11,8 +11,16 @@ set(KINESIS_VIDEO_WebRTCClient_SRC "${CMAKE_CURRENT_SOURCE_DIR}/..") if (OPEN_SRC_INSTALL_PREFIX) find_package(GTest REQUIRED PATHS ${OPEN_SRC_INSTALL_PREFIX}) + if(ENABLE_AWS_SDK_IN_TESTS) + set(CMAKE_PREFIX_PATH ${OPEN_SRC_INSTALL_PREFIX}) + find_package(AWSSDK REQUIRED PATHS ${OPEN_SRC_INSTALL_PREFIX} COMPONENTS kinesisvideo kinesis-video-webrtc-storage) + endif() else() find_package(GTest REQUIRED) + if(ENABLE_AWS_SDK_IN_TESTS) + find_package(AWSSDK REQUIRED COMPONENTS kinesisvideo kinesis-video-webrtc-storage) + endif() + endif() SET(GTEST_LIBNAME GTest::gtest) @@ -22,12 +30,19 @@ endif() include_directories(${KINESIS_VIDEO_WebRTCClient_SRC}) +set(AWS_SDK_TEST_LIBS "") +if(ENABLE_AWS_SDK_IN_TESTS) + include_directories(${AWS_SDK_INCLUDE_DIR}) + set(AWS_SDK_TEST_LIBS ${AWSSDK_LINK_LIBRARIES}) +endif() + file(GLOB WEBRTC_CLIENT_TEST_SOURCE_FILES "*.cpp" ) -add_executable(webrtc_client_test ${WEBRTC_CLIENT_TEST_SOURCE_FILES}) +add_executable(webrtc_client_test ${WEBRTC_CLIENT_TEST_SOURCE_FILES} SignalingApiFunctionalityTest.h) target_link_libraries(webrtc_client_test kvsWebrtcClient kvsWebrtcSignalingClient ${OPENSSL_CRYPTO_LIBRARY} kvspicUtils - ${GTEST_LIBNAME}) + ${GTEST_LIBNAME} + ${AWS_SDK_TEST_LIBS}) diff --git a/tst/IngestionFunctionalityTests.cpp b/tst/IngestionFunctionalityTests.cpp new file mode 100644 index 0000000000..5b62fc5fc9 --- /dev/null +++ b/tst/IngestionFunctionalityTests.cpp @@ -0,0 +1,921 @@ +//////////////////////////////////////////////////////////////////// +// Join Session Functionality Tests [AWS SDK Deps Required] +//////////////////////////////////////////////////////////////////// +#ifdef ENABLE_AWS_SDK_IN_TESTS + +#include "SignalingApiFunctionalityTest.h" +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +namespace com { +namespace amazonaws { +namespace kinesis { +namespace video { +namespace webrtcclient { + +class IngestionFunctionalityTest : public SignalingApiFunctionalityTest { + public: + IngestionFunctionalityTest() + { + joinSessionCount = 0; + joinSessionFail = MAX_UINT32; + joinSessionRecover = 0; + joinSessionResult = STATUS_SUCCESS; + } + + STATUS joinSessionResult; + UINT32 joinSessionFail; + UINT32 joinSessionRecover; + UINT32 joinSessionCount; + + typedef struct { + std::string streamName; + std::string streamArn; + std::string channelName; + std::string channelArn; + BOOL enabledStatus; + BOOL isValid; + } MediaConfigurationInfo; + + MediaConfigurationInfo createStreamAndChannelAndLink(); + VOID UnlinkAndDeleteStreamAndChannel(MediaConfigurationInfo); + + private: + Aws::KinesisVideo::KinesisVideoClient mKvsClient; + Aws::KinesisVideoWebRTCStorage::KinesisVideoWebRTCStorageClient mKvsWebRTCStorageClient; +}; + +STATUS joinSessionPreHook(UINT64 hookCustomData) +{ + STATUS retStatus = STATUS_SUCCESS; + IngestionFunctionalityTest* pTest = (IngestionFunctionalityTest*) hookCustomData; + CHECK(pTest != NULL); + + if (pTest->joinSessionCount >= pTest->joinSessionFail && pTest->joinSessionCount < pTest->joinSessionRecover) { + retStatus = pTest->joinSessionResult; + } + + pTest->joinSessionCount++; + DLOGD("Signaling client join session pre hook returning 0x%08x", retStatus); + return retStatus; +} + +unsigned char random_char() { + std::random_device rd; + std::mt19937 gen(rd()); + std::uniform_int_distribution<> dis(0, 255); + return static_cast(dis(gen)); +} + +std::string generate_hex(const unsigned int len) { + std::stringstream ss; + for(unsigned int i = 0; i < len; i++) { + auto rc = random_char(); + std::stringstream hexstream; + hexstream << std::hex << int(rc); + auto hex = hexstream.str(); + ss << (hex.length() < 2 ? '0' + hex : hex); + } + return ss.str(); +} + + +// Create a Stream and a Channel and link +IngestionFunctionalityTest::MediaConfigurationInfo IngestionFunctionalityTest::createStreamAndChannelAndLink() { + IngestionFunctionalityTest::MediaConfigurationInfo mediaConfigurationInfo; + + Aws::KinesisVideo::Model::CreateStreamOutcome createStreamOutcome; + Aws::KinesisVideo::Model::CreateStreamRequest createStreamRequest; + + std::string nameSuffix = generate_hex(16); + std::string streamName = "WrtcIngestionTestStream_" + nameSuffix; + std::string channelName = "WrtcIngestionTestChannel_" + nameSuffix; + createStreamRequest.WithStreamName(streamName); + createStreamRequest.WithDataRetentionInHours(24); + + createStreamOutcome = mKvsClient.CreateStream(createStreamRequest); + + if (createStreamOutcome.IsSuccess()) { + Aws::KinesisVideo::Model::CreateSignalingChannelOutcome createSignalingChannelOutcome; + Aws::KinesisVideo::Model::CreateSignalingChannelRequest createSignalingChannelRequest; + + createSignalingChannelRequest.WithChannelName(channelName); + + createSignalingChannelOutcome = mKvsClient.CreateSignalingChannel(createSignalingChannelRequest); + + if (createSignalingChannelOutcome.IsSuccess()) { + // UpdateMediaStorageConfiguration needs the ChannelARN and StreamARN. We will call the following + // APIs to get the needed ARNs + // 1. DescribeSignalingChannel + // 2. DescribeStream + + // long sleep to make sure the stream and channel are ready for use + THREAD_SLEEP(10 * HUNDREDS_OF_NANOS_IN_A_SECOND); + + + Aws::KinesisVideo::Model::DescribeSignalingChannelOutcome describeSignalingChannelOutcome; + Aws::KinesisVideo::Model::DescribeSignalingChannelRequest describeSignalingChannelRequest; + + describeSignalingChannelRequest.WithChannelName(channelName); + + describeSignalingChannelOutcome = mKvsClient.DescribeSignalingChannel(describeSignalingChannelRequest); + + + if (describeSignalingChannelOutcome.IsSuccess()) { + const Aws::String channelARN = describeSignalingChannelOutcome.GetResult().GetChannelInfo().GetChannelARN(); + + Aws::KinesisVideo::Model::DescribeStreamOutcome describeStreamOutcome; + Aws::KinesisVideo::Model::DescribeStreamRequest describeStreamRequest; + + describeStreamRequest.WithStreamName(streamName); + describeStreamOutcome = mKvsClient.DescribeStream(describeStreamRequest); + + if (describeStreamOutcome.IsSuccess()) { + const Aws::String streamARN = describeStreamOutcome.GetResult().GetStreamInfo().GetStreamARN(); + + Aws::KinesisVideo::Model::UpdateMediaStorageConfigurationOutcome updateMediaStorageConfigurationOutcome; + Aws::KinesisVideo::Model::UpdateMediaStorageConfigurationRequest updateMediaStorageConfigurationRequest; + + Aws::KinesisVideo::Model::MediaStorageConfiguration mediaStorageConfiguration; + mediaStorageConfiguration.WithStreamARN(streamARN); + mediaStorageConfiguration.WithStatus(Aws::KinesisVideo::Model::MediaStorageConfigurationStatus::ENABLED); + + updateMediaStorageConfigurationRequest.WithChannelARN(channelARN); + updateMediaStorageConfigurationRequest.WithMediaStorageConfiguration(mediaStorageConfiguration); + + std::string cARN(channelARN.c_str(), channelARN.size()); + std::string sARN(streamARN.c_str(), streamARN.size()); + + + DLOGD("ChannelArn: %s, StreamArn: %s", (PCHAR)cARN.c_str(), (PCHAR)sARN.c_str()); + + updateMediaStorageConfigurationOutcome = mKvsClient.UpdateMediaStorageConfiguration(updateMediaStorageConfigurationRequest); + + if (updateMediaStorageConfigurationOutcome.IsSuccess()) { + mediaConfigurationInfo.channelName = channelName; + mediaConfigurationInfo.channelArn = channelARN; + mediaConfigurationInfo.streamName = streamName; + mediaConfigurationInfo.streamArn = streamARN; + mediaConfigurationInfo.enabledStatus = TRUE; + mediaConfigurationInfo.isValid = TRUE; + return mediaConfigurationInfo; + } else { + DLOGE("Update Media Storage Configuration FAILED. %s", updateMediaStorageConfigurationOutcome.GetError().GetMessage().c_str()); + } + } + } + } else { + DLOGE("Creating Signaling Channel FAILED"); + } + } else { + DLOGE("Creating Stream FAILED"); + } + + + mediaConfigurationInfo.isValid = FALSE; + + return mediaConfigurationInfo; +} + + +VOID IngestionFunctionalityTest::UnlinkAndDeleteStreamAndChannel(IngestionFunctionalityTest::MediaConfigurationInfo mediaConfigurationInfo) { + Aws::KinesisVideo::Model::UpdateMediaStorageConfigurationOutcome updateMediaStorageConfigurationOutcome; + Aws::KinesisVideo::Model::UpdateMediaStorageConfigurationRequest updateMediaStorageConfigurationRequest; + + Aws::KinesisVideo::Model::MediaStorageConfiguration mediaStorageConfiguration; + mediaStorageConfiguration.WithStreamARN(mediaConfigurationInfo.streamArn); + mediaStorageConfiguration.WithStatus(Aws::KinesisVideo::Model::MediaStorageConfigurationStatus::DISABLED); + + updateMediaStorageConfigurationRequest.WithChannelARN(mediaConfigurationInfo.channelArn); + updateMediaStorageConfigurationRequest.WithMediaStorageConfiguration(mediaStorageConfiguration); + + updateMediaStorageConfigurationOutcome = mKvsClient.UpdateMediaStorageConfiguration(updateMediaStorageConfigurationRequest); + + if (!updateMediaStorageConfigurationOutcome.IsSuccess()) { + DLOGE("Update Media Storage Configuration FAILED! %s", updateMediaStorageConfigurationOutcome.GetError().GetMessage().c_str()); + } + + Aws::KinesisVideo::Model::DeleteSignalingChannelOutcome deleteSignalingChannelOutcome; + Aws::KinesisVideo::Model::DeleteSignalingChannelRequest deleteSignalingChannelRequest; + + deleteSignalingChannelRequest.WithChannelARN(mediaConfigurationInfo.channelArn); + + deleteSignalingChannelOutcome = mKvsClient.DeleteSignalingChannel(deleteSignalingChannelRequest); + + if (!deleteSignalingChannelOutcome.IsSuccess()) { + DLOGE("Delete Signaling Channel (%s) FAILED! %s", (PCHAR)mediaConfigurationInfo.channelArn.c_str(), deleteSignalingChannelOutcome.GetError().GetMessage().c_str()); + } + + Aws::KinesisVideo::Model::DeleteStreamOutcome deleteStreamOutcome; + Aws::KinesisVideo::Model::DeleteStreamRequest deleteStreamRequest; + + deleteStreamRequest.WithStreamARN(mediaConfigurationInfo.streamArn); + + deleteStreamOutcome = mKvsClient.DeleteStream(deleteStreamRequest); + + if (!deleteStreamOutcome.IsSuccess()) { + DLOGE("Delete Stream (%s) FAILED! %s", (PCHAR)mediaConfigurationInfo.streamArn.c_str(), deleteStreamOutcome.GetError().GetMessage().c_str()); + } + +} + + +TEST_F(IngestionFunctionalityTest, basicCreateConnectFreeNoJoinSession) +{ + ASSERT_EQ(TRUE, mAccessKeyIdSet); + + ChannelInfo channelInfo; + SignalingClientCallbacks signalingClientCallbacks; + SignalingClientInfoInternal clientInfoInternal; + SIGNALING_CLIENT_HANDLE signalingHandle = INVALID_SIGNALING_CLIENT_HANDLE_VALUE; + PSignalingClient pSignalingClient; + + signalingClientCallbacks.version = SIGNALING_CLIENT_CALLBACKS_CURRENT_VERSION; + signalingClientCallbacks.customData = (UINT64) this; + signalingClientCallbacks.messageReceivedFn = NULL; + signalingClientCallbacks.errorReportFn = signalingClientError; + signalingClientCallbacks.stateChangeFn = signalingClientStateChanged; + signalingClientCallbacks.getCurrentTimeFn = NULL; + + MEMSET(&clientInfoInternal, 0x00, SIZEOF(SignalingClientInfoInternal)); + + clientInfoInternal.signalingClientInfo.version = SIGNALING_CLIENT_INFO_CURRENT_VERSION; + clientInfoInternal.signalingClientInfo.loggingLevel = mLogLevel; + STRCPY(clientInfoInternal.signalingClientInfo.clientId, TEST_SIGNALING_MASTER_CLIENT_ID); + clientInfoInternal.hookCustomData = (UINT64) this; + clientInfoInternal.connectPreHookFn = connectPreHook; + clientInfoInternal.describePreHookFn = describePreHook; + clientInfoInternal.getEndpointPreHookFn = getEndpointPreHook; + clientInfoInternal.describeMediaStorageConfPreHookFn = describeMediaPreHook; + clientInfoInternal.joinSessionPreHookFn = joinSessionPreHook; + setupSignalingStateMachineRetryStrategyCallbacks(&clientInfoInternal); + + MEMSET(&channelInfo, 0x00, SIZEOF(ChannelInfo)); + channelInfo.version = CHANNEL_INFO_CURRENT_VERSION; + channelInfo.pChannelName = mChannelName; + channelInfo.pKmsKeyId = NULL; + channelInfo.tagCount = 0; + channelInfo.pTags = NULL; + channelInfo.channelType = SIGNALING_CHANNEL_TYPE_SINGLE_MASTER; + channelInfo.channelRoleType = SIGNALING_CHANNEL_ROLE_TYPE_MASTER; + channelInfo.cachingPolicy = SIGNALING_API_CALL_CACHE_TYPE_NONE; + channelInfo.retry = TRUE; + channelInfo.reconnect = TRUE; + channelInfo.pCertPath = mCaCertPath; + channelInfo.messageTtl = TEST_SIGNALING_MESSAGE_TTL; + + channelInfo.useMediaStorage = TRUE; + + EXPECT_EQ(STATUS_SUCCESS, + createSignalingSync(&clientInfoInternal, &channelInfo, &signalingClientCallbacks, (PAwsCredentialProvider) mTestCredentialProvider, + &pSignalingClient)); + + signalingHandle = TO_SIGNALING_CLIENT_HANDLE(pSignalingClient); + EXPECT_TRUE(IS_VALID_SIGNALING_CLIENT_HANDLE(signalingHandle)); + + EXPECT_EQ(STATUS_SUCCESS,signalingClientFetchSync(signalingHandle)); + pActiveClient = pSignalingClient; + + // Connect twice - the second time will be no-op + EXPECT_EQ(STATUS_SUCCESS, signalingClientConnectSync(signalingHandle)); + + EXPECT_EQ(STATUS_SUCCESS, signalingClientConnectSync(signalingHandle)); + + // Validate the hook counts + EXPECT_EQ(2, describeCount); + EXPECT_EQ(1, describeMediaCount); + EXPECT_EQ(1, getEndpointCount); + EXPECT_EQ(1, connectCount); + + // This channel does not have an associated stream + EXPECT_EQ(0, joinSessionCount); + + deleteChannelLws(FROM_SIGNALING_CLIENT_HANDLE(signalingHandle), 0); + + EXPECT_EQ(STATUS_SUCCESS, freeSignalingClient(&signalingHandle)); +} + + +/* 1. Create a Stream + * 2. Create a Signaling channel + * 3. Link the Stream with the Signaling Channel + * 4. Step To Connect (which should invoke join session and if offer received to join session connected state + * 5. Un link and delete stream and signaling channel +*/ +TEST_F(IngestionFunctionalityTest, basicCreateConnectJoinSession) +{ + ASSERT_EQ(TRUE, mAccessKeyIdSet); + + SignalingClientMetrics signalingClientMetrics; + signalingClientMetrics.version = SIGNALING_CLIENT_METRICS_CURRENT_VERSION; + + MediaConfigurationInfo mediaConfigurationInfo = createStreamAndChannelAndLink(); + ASSERT_EQ(TRUE, mediaConfigurationInfo.isValid); + ASSERT_EQ(TRUE, mediaConfigurationInfo.enabledStatus); + + + ChannelInfo channelInfo; + SignalingClientCallbacks signalingClientCallbacks; + SignalingClientInfoInternal clientInfoInternal; + SIGNALING_CLIENT_HANDLE signalingHandle = INVALID_SIGNALING_CLIENT_HANDLE_VALUE; + PSignalingClient pSignalingClient; + + signalingClientCallbacks.version = SIGNALING_CLIENT_CALLBACKS_CURRENT_VERSION; + signalingClientCallbacks.customData = (UINT64) this; + signalingClientCallbacks.messageReceivedFn = NULL; + signalingClientCallbacks.errorReportFn = signalingClientError; + signalingClientCallbacks.stateChangeFn = signalingClientStateChanged; + signalingClientCallbacks.getCurrentTimeFn = NULL; + + MEMSET(&clientInfoInternal, 0x00, SIZEOF(SignalingClientInfoInternal)); + + clientInfoInternal.signalingClientInfo.version = SIGNALING_CLIENT_INFO_CURRENT_VERSION; + clientInfoInternal.signalingClientInfo.loggingLevel = mLogLevel; + STRCPY(clientInfoInternal.signalingClientInfo.clientId, TEST_SIGNALING_MASTER_CLIENT_ID); + clientInfoInternal.hookCustomData = (UINT64) this; + clientInfoInternal.connectPreHookFn = connectPreHook; + clientInfoInternal.describePreHookFn = describePreHook; + clientInfoInternal.getEndpointPreHookFn = getEndpointPreHook; + clientInfoInternal.describeMediaStorageConfPreHookFn = describeMediaPreHook; + clientInfoInternal.getIceConfigPreHookFn = getIceConfigPreHook; + + clientInfoInternal.joinSessionPreHookFn = joinSessionPreHook; + setupSignalingStateMachineRetryStrategyCallbacks(&clientInfoInternal); + + MEMSET(&channelInfo, 0x00, SIZEOF(ChannelInfo)); + channelInfo.version = CHANNEL_INFO_CURRENT_VERSION; + channelInfo.pChannelName = (PCHAR)mediaConfigurationInfo.channelName.c_str(); + channelInfo.pKmsKeyId = NULL; + channelInfo.tagCount = 0; + channelInfo.pTags = NULL; + channelInfo.channelType = SIGNALING_CHANNEL_TYPE_SINGLE_MASTER; + channelInfo.channelRoleType = SIGNALING_CHANNEL_ROLE_TYPE_MASTER; + channelInfo.cachingPolicy = SIGNALING_API_CALL_CACHE_TYPE_NONE; + channelInfo.retry = TRUE; + channelInfo.reconnect = TRUE; + channelInfo.pCertPath = mCaCertPath; + channelInfo.messageTtl = TEST_SIGNALING_MESSAGE_TTL; + channelInfo.useMediaStorage = TRUE; + + EXPECT_EQ(STATUS_SUCCESS, + createSignalingSync(&clientInfoInternal, &channelInfo, &signalingClientCallbacks, (PAwsCredentialProvider) mTestCredentialProvider, + &pSignalingClient)); + + signalingHandle = TO_SIGNALING_CLIENT_HANDLE(pSignalingClient); + EXPECT_TRUE(IS_VALID_SIGNALING_CLIENT_HANDLE(signalingHandle)); + + EXPECT_EQ(STATUS_SUCCESS,signalingClientFetchSync(signalingHandle)); + pActiveClient = pSignalingClient; + + EXPECT_EQ(STATUS_SUCCESS, signalingClientConnectSync(signalingHandle)); + + // Validate the hook counts + EXPECT_EQ(1, describeCount); + EXPECT_EQ(1, describeMediaCount); + EXPECT_EQ(1, getEndpointCount); + EXPECT_EQ(1, getIceConfigCount); + + EXPECT_EQ(1, connectCount); + + // This channel has ENABLED status so we should be calling join session + EXPECT_EQ(1, joinSessionCount); + + UnlinkAndDeleteStreamAndChannel(mediaConfigurationInfo); + + EXPECT_EQ(STATUS_SUCCESS, freeSignalingClient(&signalingHandle)); +} + + +TEST_F(IngestionFunctionalityTest, iceReconnectEmulationWithJoinSession) +{ + ASSERT_EQ(TRUE, mAccessKeyIdSet); + + MediaConfigurationInfo mediaConfigurationInfo = createStreamAndChannelAndLink(); + ASSERT_EQ(TRUE, mediaConfigurationInfo.isValid); + ASSERT_EQ(TRUE, mediaConfigurationInfo.enabledStatus); + + + ChannelInfo channelInfo; + SignalingClientCallbacks signalingClientCallbacks; + PSignalingClient pSignalingClient; + SIGNALING_CLIENT_HANDLE signalingHandle = INVALID_SIGNALING_CLIENT_HANDLE_VALUE; + SignalingClientInfoInternal clientInfoInternal; + + signalingClientCallbacks.version = SIGNALING_CLIENT_CALLBACKS_CURRENT_VERSION; + signalingClientCallbacks.customData = (UINT64) this; + signalingClientCallbacks.messageReceivedFn = NULL; + signalingClientCallbacks.errorReportFn = signalingClientError; + signalingClientCallbacks.stateChangeFn = signalingClientStateChanged; + signalingClientCallbacks.getCurrentTimeFn = NULL; + + + MEMSET(&clientInfoInternal, 0x00, SIZEOF(SignalingClientInfoInternal)); + + clientInfoInternal.signalingClientInfo.version = SIGNALING_CLIENT_INFO_CURRENT_VERSION; + clientInfoInternal.signalingClientInfo.loggingLevel = LOG_LEVEL_VERBOSE; + clientInfoInternal.hookCustomData = (UINT64) this; + clientInfoInternal.connectPreHookFn = connectPreHook; + clientInfoInternal.describePreHookFn = describePreHook; + clientInfoInternal.getEndpointPreHookFn = getEndpointPreHook; + clientInfoInternal.getIceConfigPreHookFn = getIceConfigPreHook; + clientInfoInternal.describeMediaStorageConfPreHookFn = describeMediaPreHook; + clientInfoInternal.joinSessionPreHookFn = joinSessionPreHook; + STRCPY(clientInfoInternal.signalingClientInfo.clientId, TEST_SIGNALING_MASTER_CLIENT_ID); + setupSignalingStateMachineRetryStrategyCallbacks(&clientInfoInternal); + + MEMSET(&channelInfo, 0x00, SIZEOF(ChannelInfo)); + channelInfo.version = CHANNEL_INFO_CURRENT_VERSION; + channelInfo.pChannelName = (PCHAR)mediaConfigurationInfo.channelName.c_str(); + channelInfo.pKmsKeyId = NULL; + channelInfo.tagCount = 0; + channelInfo.pTags = NULL; + channelInfo.channelType = SIGNALING_CHANNEL_TYPE_SINGLE_MASTER; + channelInfo.channelRoleType = SIGNALING_CHANNEL_ROLE_TYPE_MASTER; + channelInfo.cachingPolicy = SIGNALING_API_CALL_CACHE_TYPE_NONE; + channelInfo.retry = TRUE; + channelInfo.reconnect = TRUE; + channelInfo.pCertPath = mCaCertPath; + channelInfo.messageTtl = TEST_SIGNALING_MESSAGE_TTL; + channelInfo.useMediaStorage = TRUE; + + EXPECT_EQ(STATUS_SUCCESS, + createSignalingSync(&clientInfoInternal, &channelInfo, &signalingClientCallbacks, (PAwsCredentialProvider) mTestCredentialProvider, + &pSignalingClient)); + + signalingHandle = TO_SIGNALING_CLIENT_HANDLE(pSignalingClient); + EXPECT_TRUE(IS_VALID_SIGNALING_CLIENT_HANDLE(signalingHandle)); + + pSignalingClient = FROM_SIGNALING_CLIENT_HANDLE(signalingHandle); + pActiveClient = pSignalingClient; + + EXPECT_EQ(STATUS_SUCCESS,signalingClientFetchSync(signalingHandle)); + // Connect to the signaling client + EXPECT_EQ(STATUS_SUCCESS, signalingClientConnectSync(signalingHandle)); + + // Validate the hook counts + EXPECT_EQ(1, describeCount); + EXPECT_EQ(1, describeMediaCount); + EXPECT_EQ(1, getEndpointCount); + EXPECT_EQ(1, getIceConfigCount); + EXPECT_EQ(1, connectCount); + + // This channel has ENABLED status so we should be calling join session + EXPECT_EQ(1, joinSessionCount); + + DLOGV("Before RECONNECT_ICE_SERVER injection"); + + // Inject a reconnect ice server message + CHAR message[] = "{\n" + " \"senderClientId\": \"TestSender\",\n" + " \"messageType\": \"RECONNECT_ICE_SERVER\",\n" + " \"messagePayload\": \"MessagePayload\",\n" + " \"statusResponse\": {\n" + " \"correlationId\": \"CorrelationID\",\n" + " \"errorType\": \"Reconnect ice server\",\n" + " \"statusCode\": \"200\",\n" + " \"description\": \"Test attempt to reconnect ice server\"\n" + " }\n" + "}"; + EXPECT_EQ(STATUS_SUCCESS, receiveLwsMessage(pSignalingClient, message, ARRAY_SIZE(message))); + + DLOGV("After RECONNECT_ICE_SERVER injection"); + + // Check that we are connected and can send a message + SignalingMessage signalingMessage; + signalingMessage.version = SIGNALING_MESSAGE_CURRENT_VERSION; + signalingMessage.messageType = SIGNALING_MESSAGE_TYPE_OFFER; + STRCPY(signalingMessage.peerClientId, TEST_SIGNALING_MASTER_CLIENT_ID); + MEMSET(signalingMessage.payload, 'A', 100); + signalingMessage.payload[100] = '\0'; + signalingMessage.payloadLen = 0; + signalingMessage.correlationId[0] = '\0'; + + + EXPECT_EQ(STATUS_SUCCESS, signalingClientSendMessageSync(signalingHandle, &signalingMessage)); + + // Validate the hook counts, caching is disabled in this test, but reconnect should move to + // Get Ice Server config + EXPECT_EQ(1, describeCount); + EXPECT_EQ(1, describeMediaCount); + EXPECT_EQ(1, getEndpointCount); + EXPECT_EQ(2, getIceConfigCount); + EXPECT_EQ(2, connectCount); + + // This channel has ENABLED status so we should be calling join session + EXPECT_EQ(2, joinSessionCount); + + UnlinkAndDeleteStreamAndChannel(mediaConfigurationInfo); + + EXPECT_EQ(STATUS_SUCCESS, freeSignalingClient(&signalingHandle)); +} + + +TEST_F(IngestionFunctionalityTest, iceServerConfigRefreshNotConnectedJoinSessionWithBadAuth) +{ + ASSERT_EQ(TRUE, mAccessKeyIdSet); + + MediaConfigurationInfo mediaConfigurationInfo = createStreamAndChannelAndLink(); + ASSERT_EQ(TRUE, mediaConfigurationInfo.isValid); + ASSERT_EQ(TRUE, mediaConfigurationInfo.enabledStatus); + + ChannelInfo channelInfo; + SignalingClientCallbacks signalingClientCallbacks; + SignalingClientInfoInternal clientInfoInternal; + PSignalingClient pSignalingClient; + SIGNALING_CLIENT_HANDLE signalingHandle; + UINT32 iceCount; + PIceConfigInfo pIceConfigInfo; + + signalingClientCallbacks.version = SIGNALING_CLIENT_CALLBACKS_CURRENT_VERSION; + signalingClientCallbacks.customData = (UINT64) this; + signalingClientCallbacks.messageReceivedFn = NULL; + signalingClientCallbacks.errorReportFn = signalingClientError; + signalingClientCallbacks.stateChangeFn = signalingClientStateChanged; + signalingClientCallbacks.getCurrentTimeFn = NULL; + + MEMSET(&clientInfoInternal, 0x00, SIZEOF(SignalingClientInfoInternal)); + + clientInfoInternal.signalingClientInfo.version = SIGNALING_CLIENT_INFO_CURRENT_VERSION; + clientInfoInternal.signalingClientInfo.loggingLevel = mLogLevel; + STRCPY(clientInfoInternal.signalingClientInfo.clientId, TEST_SIGNALING_MASTER_CLIENT_ID); + setupSignalingStateMachineRetryStrategyCallbacks(&clientInfoInternal); + + MEMSET(&channelInfo, 0x00, SIZEOF(ChannelInfo)); + channelInfo.version = CHANNEL_INFO_CURRENT_VERSION; + channelInfo.pChannelName = (PCHAR)mediaConfigurationInfo.channelName.c_str(); + channelInfo.pKmsKeyId = NULL; + channelInfo.tagCount = 0; + channelInfo.pTags = NULL; + channelInfo.channelType = SIGNALING_CHANNEL_TYPE_SINGLE_MASTER; + channelInfo.channelRoleType = SIGNALING_CHANNEL_ROLE_TYPE_MASTER; + channelInfo.cachingPolicy = SIGNALING_API_CALL_CACHE_TYPE_NONE; + channelInfo.retry = TRUE; + channelInfo.reconnect = TRUE; + channelInfo.pCertPath = mCaCertPath; + channelInfo.messageTtl = TEST_SIGNALING_MESSAGE_TTL; + channelInfo.useMediaStorage = TRUE; + + EXPECT_EQ(STATUS_SUCCESS, createSignalingSync(&clientInfoInternal, &channelInfo, &signalingClientCallbacks, (PAwsCredentialProvider) mTestCredentialProvider, &pSignalingClient)); + signalingHandle = TO_SIGNALING_CLIENT_HANDLE(pSignalingClient); + EXPECT_TRUE(IS_VALID_SIGNALING_CLIENT_HANDLE(signalingHandle)); + EXPECT_EQ(STATUS_SUCCESS,signalingClientFetchSync(signalingHandle)); + + pActiveClient = pSignalingClient; + + // Check the states first, we did not connect yet + EXPECT_EQ(1, signalingStatesCounts[SIGNALING_CLIENT_STATE_NEW]); + EXPECT_EQ(1, signalingStatesCounts[SIGNALING_CLIENT_STATE_GET_CREDENTIALS]); + EXPECT_EQ(1, signalingStatesCounts[SIGNALING_CLIENT_STATE_DESCRIBE]); + EXPECT_EQ(1, signalingStatesCounts[SIGNALING_CLIENT_STATE_DESCRIBE_MEDIA]); + EXPECT_EQ(0, signalingStatesCounts[SIGNALING_CLIENT_STATE_CREATE]); + EXPECT_EQ(1, signalingStatesCounts[SIGNALING_CLIENT_STATE_GET_ENDPOINT]); + EXPECT_EQ(1, signalingStatesCounts[SIGNALING_CLIENT_STATE_GET_ICE_CONFIG]); + EXPECT_EQ(1, signalingStatesCounts[SIGNALING_CLIENT_STATE_READY]); + EXPECT_EQ(0, signalingStatesCounts[SIGNALING_CLIENT_STATE_CONNECTING]); + EXPECT_EQ(0, signalingStatesCounts[SIGNALING_CLIENT_STATE_CONNECTED]); + EXPECT_EQ(0, signalingStatesCounts[SIGNALING_CLIENT_STATE_JOIN_SESSION]); + EXPECT_EQ(0, signalingStatesCounts[SIGNALING_CLIENT_STATE_JOIN_SESSION_WAITING]); + EXPECT_EQ(0, signalingStatesCounts[SIGNALING_CLIENT_STATE_JOIN_SESSION_CONNECTED]); + EXPECT_EQ(0, signalingStatesCounts[SIGNALING_CLIENT_STATE_DISCONNECTED]); + + // Set bad auth info + BYTE firstByte = pSignalingClient->pAwsCredentials->secretKey[0]; + if(firstByte == 'A') { + pSignalingClient->pAwsCredentials->secretKey[0] = 'B'; + } else { + pSignalingClient->pAwsCredentials->secretKey[0] = 'A'; + } + + // Trigger the ICE refresh on the next call + pSignalingClient->iceConfigCount = 0; + EXPECT_NE(STATUS_SUCCESS, signalingClientGetIceConfigInfoCount(signalingHandle, &iceCount)); + EXPECT_NE(STATUS_SUCCESS, signalingClientGetIceConfigInfo(signalingHandle, 0, &pIceConfigInfo)); + + // This time the states will circle through connecting/connected again + EXPECT_EQ(1, signalingStatesCounts[SIGNALING_CLIENT_STATE_NEW]); + EXPECT_LT(1, signalingStatesCounts[SIGNALING_CLIENT_STATE_GET_CREDENTIALS]); + EXPECT_LT(1, signalingStatesCounts[SIGNALING_CLIENT_STATE_DESCRIBE]); + EXPECT_EQ(1, signalingStatesCounts[SIGNALING_CLIENT_STATE_DESCRIBE_MEDIA]); + EXPECT_EQ(0, signalingStatesCounts[SIGNALING_CLIENT_STATE_CREATE]); + EXPECT_EQ(1, signalingStatesCounts[SIGNALING_CLIENT_STATE_GET_ENDPOINT]); + EXPECT_LT(2, signalingStatesCounts[SIGNALING_CLIENT_STATE_GET_ICE_CONFIG]); + EXPECT_EQ(1, signalingStatesCounts[SIGNALING_CLIENT_STATE_READY]); + EXPECT_EQ(0, signalingStatesCounts[SIGNALING_CLIENT_STATE_CONNECTING]); + EXPECT_EQ(0, signalingStatesCounts[SIGNALING_CLIENT_STATE_CONNECTED]); + EXPECT_EQ(0, signalingStatesCounts[SIGNALING_CLIENT_STATE_JOIN_SESSION]); + EXPECT_EQ(0, signalingStatesCounts[SIGNALING_CLIENT_STATE_JOIN_SESSION_WAITING]); + EXPECT_EQ(0, signalingStatesCounts[SIGNALING_CLIENT_STATE_JOIN_SESSION_CONNECTED]); + EXPECT_EQ(0, signalingStatesCounts[SIGNALING_CLIENT_STATE_DISCONNECTED]); + + // Reset the auth and ensure we succeed this time + pSignalingClient->pAwsCredentials->secretKey[0] = firstByte; + EXPECT_EQ(STATUS_SUCCESS, signalingClientGetIceConfigInfoCount(signalingHandle, &iceCount)); + EXPECT_EQ(STATUS_SUCCESS, signalingClientGetIceConfigInfo(signalingHandle, 0, &pIceConfigInfo)); + + // Connect to the signaling client + EXPECT_EQ(STATUS_SUCCESS, signalingClientConnectSync(signalingHandle)); + EXPECT_EQ(1, signalingStatesCounts[SIGNALING_CLIENT_STATE_NEW]); + EXPECT_LT(1, signalingStatesCounts[SIGNALING_CLIENT_STATE_GET_CREDENTIALS]); + EXPECT_LT(1, signalingStatesCounts[SIGNALING_CLIENT_STATE_DESCRIBE]); + EXPECT_EQ(1, signalingStatesCounts[SIGNALING_CLIENT_STATE_DESCRIBE_MEDIA]); + + EXPECT_EQ(0, signalingStatesCounts[SIGNALING_CLIENT_STATE_CREATE]); + EXPECT_EQ(1, signalingStatesCounts[SIGNALING_CLIENT_STATE_GET_ENDPOINT]); + EXPECT_LT(2, signalingStatesCounts[SIGNALING_CLIENT_STATE_GET_ICE_CONFIG]); + EXPECT_EQ(2, signalingStatesCounts[SIGNALING_CLIENT_STATE_READY]); + EXPECT_EQ(1, signalingStatesCounts[SIGNALING_CLIENT_STATE_CONNECTING]); + EXPECT_EQ(1, signalingStatesCounts[SIGNALING_CLIENT_STATE_CONNECTED]); + EXPECT_EQ(1, signalingStatesCounts[SIGNALING_CLIENT_STATE_JOIN_SESSION]); + EXPECT_EQ(1, signalingStatesCounts[SIGNALING_CLIENT_STATE_JOIN_SESSION_WAITING]); + EXPECT_EQ(1, signalingStatesCounts[SIGNALING_CLIENT_STATE_JOIN_SESSION_CONNECTED]); + EXPECT_EQ(0, signalingStatesCounts[SIGNALING_CLIENT_STATE_DISCONNECTED]); + + // Check that we are connected and can send a message + SignalingMessage signalingMessage; + signalingMessage.version = SIGNALING_MESSAGE_CURRENT_VERSION; + signalingMessage.messageType = SIGNALING_MESSAGE_TYPE_OFFER; + STRCPY(signalingMessage.peerClientId, TEST_SIGNALING_MASTER_CLIENT_ID); + MEMSET(signalingMessage.payload, 'A', 100); + signalingMessage.payload[100] = '\0'; + signalingMessage.payloadLen = 0; + signalingMessage.correlationId[0] = '\0'; + + EXPECT_EQ(STATUS_SUCCESS, signalingClientSendMessageSync(signalingHandle, &signalingMessage)); + + UnlinkAndDeleteStreamAndChannel(mediaConfigurationInfo); + + EXPECT_EQ(STATUS_SUCCESS, freeSignalingClient(&signalingHandle)); +} + + +TEST_F(IngestionFunctionalityTest, iceServerConfigRefreshConnectedJoinSessionWithBadAuth) +{ + ASSERT_EQ(TRUE, mAccessKeyIdSet); + + MediaConfigurationInfo mediaConfigurationInfo = createStreamAndChannelAndLink(); + ASSERT_EQ(TRUE, mediaConfigurationInfo.isValid); + ASSERT_EQ(TRUE, mediaConfigurationInfo.enabledStatus); + + ChannelInfo channelInfo; + SignalingClientCallbacks signalingClientCallbacks; + SignalingClientInfoInternal clientInfoInternal; + PSignalingClient pSignalingClient; + SIGNALING_CLIENT_HANDLE signalingHandle; + UINT32 iceCount; + PIceConfigInfo pIceConfigInfo; + + signalingClientCallbacks.version = SIGNALING_CLIENT_CALLBACKS_CURRENT_VERSION; + signalingClientCallbacks.customData = (UINT64) this; + signalingClientCallbacks.messageReceivedFn = NULL; + signalingClientCallbacks.errorReportFn = signalingClientError; + signalingClientCallbacks.stateChangeFn = signalingClientStateChanged; + signalingClientCallbacks.getCurrentTimeFn = NULL; + + + MEMSET(&clientInfoInternal, 0x00, SIZEOF(SignalingClientInfoInternal)); + + clientInfoInternal.signalingClientInfo.version = SIGNALING_CLIENT_INFO_CURRENT_VERSION; + clientInfoInternal.signalingClientInfo.loggingLevel = mLogLevel; + STRCPY(clientInfoInternal.signalingClientInfo.clientId, TEST_SIGNALING_MASTER_CLIENT_ID); + setupSignalingStateMachineRetryStrategyCallbacks(&clientInfoInternal); + + MEMSET(&channelInfo, 0x00, SIZEOF(ChannelInfo)); + channelInfo.version = CHANNEL_INFO_CURRENT_VERSION; + channelInfo.pChannelName = (PCHAR)mediaConfigurationInfo.channelName.c_str(); + channelInfo.pKmsKeyId = NULL; + channelInfo.tagCount = 0; + channelInfo.pTags = NULL; + channelInfo.channelType = SIGNALING_CHANNEL_TYPE_SINGLE_MASTER; + channelInfo.channelRoleType = SIGNALING_CHANNEL_ROLE_TYPE_MASTER; + channelInfo.cachingPolicy = SIGNALING_API_CALL_CACHE_TYPE_NONE; + channelInfo.retry = TRUE; + channelInfo.reconnect = TRUE; + channelInfo.pCertPath = mCaCertPath; + channelInfo.messageTtl = TEST_SIGNALING_MESSAGE_TTL; + channelInfo.useMediaStorage = TRUE; + + EXPECT_EQ(STATUS_SUCCESS, createSignalingSync(&clientInfoInternal, &channelInfo, &signalingClientCallbacks, (PAwsCredentialProvider) mTestCredentialProvider, &pSignalingClient)); + signalingHandle = TO_SIGNALING_CLIENT_HANDLE(pSignalingClient); + EXPECT_TRUE(IS_VALID_SIGNALING_CLIENT_HANDLE(signalingHandle)); + EXPECT_EQ(STATUS_SUCCESS,signalingClientFetchSync(signalingHandle)); + + // Connect to the channel, will call join session + EXPECT_EQ(STATUS_SUCCESS, signalingClientConnectSync(signalingHandle)); + + pActiveClient = pSignalingClient; + + // Check the states first + EXPECT_EQ(1, signalingStatesCounts[SIGNALING_CLIENT_STATE_NEW]); + EXPECT_EQ(1, signalingStatesCounts[SIGNALING_CLIENT_STATE_GET_CREDENTIALS]); + EXPECT_EQ(1, signalingStatesCounts[SIGNALING_CLIENT_STATE_DESCRIBE]); + EXPECT_EQ(1, signalingStatesCounts[SIGNALING_CLIENT_STATE_DESCRIBE_MEDIA]); + + // We should not be calling create because it's pre-created at the start of the test + EXPECT_EQ(0, signalingStatesCounts[SIGNALING_CLIENT_STATE_CREATE]); + EXPECT_EQ(1, signalingStatesCounts[SIGNALING_CLIENT_STATE_GET_ENDPOINT]); + EXPECT_EQ(1, signalingStatesCounts[SIGNALING_CLIENT_STATE_GET_ICE_CONFIG]); + EXPECT_EQ(1, signalingStatesCounts[SIGNALING_CLIENT_STATE_READY]); + EXPECT_EQ(1, signalingStatesCounts[SIGNALING_CLIENT_STATE_CONNECTING]); + EXPECT_EQ(1, signalingStatesCounts[SIGNALING_CLIENT_STATE_CONNECTED]); + EXPECT_EQ(1, signalingStatesCounts[SIGNALING_CLIENT_STATE_JOIN_SESSION]); + EXPECT_EQ(1, signalingStatesCounts[SIGNALING_CLIENT_STATE_JOIN_SESSION_WAITING]); + EXPECT_EQ(1, signalingStatesCounts[SIGNALING_CLIENT_STATE_JOIN_SESSION_CONNECTED]); + EXPECT_EQ(0, signalingStatesCounts[SIGNALING_CLIENT_STATE_DISCONNECTED]); + + // Set bad auth info + BYTE firstByte = pSignalingClient->pAwsCredentials->secretKey[0]; + if(firstByte == 'A') { + pSignalingClient->pAwsCredentials->secretKey[0] = 'B'; + } else { + pSignalingClient->pAwsCredentials->secretKey[0] = 'A'; + } + + // Trigger the ICE refresh on the next call + pSignalingClient->iceConfigCount = 0; + EXPECT_NE(STATUS_SUCCESS, signalingClientGetIceConfigInfoCount(signalingHandle, &iceCount)); + EXPECT_NE(STATUS_SUCCESS, signalingClientGetIceConfigInfo(signalingHandle, 0, &pIceConfigInfo)); + + // This time the states will circle through connecting/connected again + EXPECT_EQ(1, signalingStatesCounts[SIGNALING_CLIENT_STATE_NEW]); + EXPECT_LT(1, signalingStatesCounts[SIGNALING_CLIENT_STATE_GET_CREDENTIALS]); + EXPECT_LT(1, signalingStatesCounts[SIGNALING_CLIENT_STATE_DESCRIBE]); + EXPECT_EQ(1, signalingStatesCounts[SIGNALING_CLIENT_STATE_DESCRIBE_MEDIA]); + EXPECT_EQ(0, signalingStatesCounts[SIGNALING_CLIENT_STATE_CREATE]); + EXPECT_EQ(1, signalingStatesCounts[SIGNALING_CLIENT_STATE_GET_ENDPOINT]); + EXPECT_LT(2, signalingStatesCounts[SIGNALING_CLIENT_STATE_GET_ICE_CONFIG]); + EXPECT_EQ(1, signalingStatesCounts[SIGNALING_CLIENT_STATE_READY]); + EXPECT_EQ(1, signalingStatesCounts[SIGNALING_CLIENT_STATE_CONNECTING]); + EXPECT_EQ(1, signalingStatesCounts[SIGNALING_CLIENT_STATE_CONNECTED]); + EXPECT_EQ(1, signalingStatesCounts[SIGNALING_CLIENT_STATE_JOIN_SESSION]); + EXPECT_EQ(1, signalingStatesCounts[SIGNALING_CLIENT_STATE_JOIN_SESSION_WAITING]); + EXPECT_EQ(1, signalingStatesCounts[SIGNALING_CLIENT_STATE_JOIN_SESSION_CONNECTED]); + EXPECT_EQ(0, signalingStatesCounts[SIGNALING_CLIENT_STATE_DISCONNECTED]); + + // Reset the auth and ensure we succeed this time + pSignalingClient->pAwsCredentials->secretKey[0] = firstByte; + EXPECT_EQ(STATUS_SUCCESS, signalingClientGetIceConfigInfoCount(signalingHandle, &iceCount)); + EXPECT_EQ(STATUS_SUCCESS, signalingClientGetIceConfigInfo(signalingHandle, 0, &pIceConfigInfo)); + + // Connect to the signaling client (already connected -- but we will call join session again) + EXPECT_EQ(STATUS_SUCCESS, signalingClientConnectSync(signalingHandle)); + EXPECT_EQ(1, signalingStatesCounts[SIGNALING_CLIENT_STATE_NEW]); + EXPECT_LT(1, signalingStatesCounts[SIGNALING_CLIENT_STATE_GET_CREDENTIALS]); + EXPECT_LT(1, signalingStatesCounts[SIGNALING_CLIENT_STATE_DESCRIBE]); + EXPECT_EQ(1, signalingStatesCounts[SIGNALING_CLIENT_STATE_DESCRIBE_MEDIA]); + EXPECT_EQ(0, signalingStatesCounts[SIGNALING_CLIENT_STATE_CREATE]); + EXPECT_EQ(1, signalingStatesCounts[SIGNALING_CLIENT_STATE_GET_ENDPOINT]); + EXPECT_LT(2, signalingStatesCounts[SIGNALING_CLIENT_STATE_GET_ICE_CONFIG]); + EXPECT_EQ(3, signalingStatesCounts[SIGNALING_CLIENT_STATE_READY]); + EXPECT_EQ(3, signalingStatesCounts[SIGNALING_CLIENT_STATE_CONNECTING]); + EXPECT_EQ(3, signalingStatesCounts[SIGNALING_CLIENT_STATE_CONNECTED]); + EXPECT_EQ(3, signalingStatesCounts[SIGNALING_CLIENT_STATE_JOIN_SESSION]); + EXPECT_EQ(3, signalingStatesCounts[SIGNALING_CLIENT_STATE_JOIN_SESSION_WAITING]); + EXPECT_EQ(3, signalingStatesCounts[SIGNALING_CLIENT_STATE_JOIN_SESSION_CONNECTED]); + EXPECT_EQ(0, signalingStatesCounts[SIGNALING_CLIENT_STATE_DISCONNECTED]); + + // Check that we are connected and can send a message + SignalingMessage signalingMessage; + signalingMessage.version = SIGNALING_MESSAGE_CURRENT_VERSION; + signalingMessage.messageType = SIGNALING_MESSAGE_TYPE_OFFER; + STRCPY(signalingMessage.peerClientId, TEST_SIGNALING_MASTER_CLIENT_ID); + MEMSET(signalingMessage.payload, 'A', 100); + signalingMessage.payload[100] = '\0'; + signalingMessage.payloadLen = 0; + signalingMessage.correlationId[0] = '\0'; + + EXPECT_EQ(STATUS_SUCCESS, signalingClientSendMessageSync(signalingHandle, &signalingMessage)); + + UnlinkAndDeleteStreamAndChannel(mediaConfigurationInfo); + + EXPECT_EQ(STATUS_SUCCESS, freeSignalingClient(&signalingHandle)); +} + + +TEST_F(IngestionFunctionalityTest, fileCachingTestWithDescribeMedia) +{ + ASSERT_EQ(TRUE, mAccessKeyIdSet); + + ChannelInfo channelInfo; + SignalingClientCallbacks signalingClientCallbacks; + SignalingClientInfoInternal clientInfoInternal; + PSignalingClient pSignalingClient; + SIGNALING_CLIENT_HANDLE signalingHandle; + CHAR signalingChannelName[64]; + const UINT32 totalChannelCount = MAX_SIGNALING_CACHE_ENTRY_COUNT + 1; + UINT32 i, describeCountNoCache, describeMediaCountNoCache, getEndpointCountNoCache; + CHAR channelArn[MAX_ARN_LEN + 1]; + + signalingClientCallbacks.version = SIGNALING_CLIENT_CALLBACKS_CURRENT_VERSION; + signalingClientCallbacks.customData = (UINT64) this; + signalingClientCallbacks.messageReceivedFn = NULL; + signalingClientCallbacks.errorReportFn = signalingClientError; + signalingClientCallbacks.stateChangeFn = signalingClientStateChanged; + signalingClientCallbacks.getCurrentTimeFn = NULL; + + MEMSET(&clientInfoInternal, 0x00, SIZEOF(SignalingClientInfoInternal)); + + clientInfoInternal.signalingClientInfo.version = SIGNALING_CLIENT_INFO_CURRENT_VERSION; + clientInfoInternal.signalingClientInfo.loggingLevel = mLogLevel; + STRCPY(clientInfoInternal.signalingClientInfo.clientId, TEST_SIGNALING_MASTER_CLIENT_ID); + clientInfoInternal.hookCustomData = (UINT64) this; + clientInfoInternal.connectPreHookFn = connectPreHook; + clientInfoInternal.describePreHookFn = describePreHook; + clientInfoInternal.describeMediaStorageConfPreHookFn = describeMediaPreHook; + clientInfoInternal.getEndpointPreHookFn = getEndpointPreHook; + setupSignalingStateMachineRetryStrategyCallbacks(&clientInfoInternal); + + MEMSET(&channelInfo, 0x00, SIZEOF(ChannelInfo)); + channelInfo.version = CHANNEL_INFO_CURRENT_VERSION; + channelInfo.pKmsKeyId = NULL; + channelInfo.tagCount = 0; + channelInfo.pTags = NULL; + channelInfo.channelType = SIGNALING_CHANNEL_TYPE_SINGLE_MASTER; + channelInfo.channelRoleType = SIGNALING_CHANNEL_ROLE_TYPE_MASTER; + channelInfo.retry = TRUE; + channelInfo.reconnect = TRUE; + channelInfo.pCertPath = mCaCertPath; + channelInfo.messageTtl = TEST_SIGNALING_MESSAGE_TTL; + channelInfo.cachingPolicy = SIGNALING_API_CALL_CACHE_TYPE_FILE; + channelInfo.pRegion = TEST_DEFAULT_REGION; + channelInfo.useMediaStorage = TRUE; + + + FREMOVE(DEFAULT_CACHE_FILE_PATH); + + for (i = 0; i < totalChannelCount; ++i) { + SPRINTF(signalingChannelName, "%s%u", TEST_SIGNALING_CHANNEL_NAME, i); + channelInfo.pChannelName = signalingChannelName; + EXPECT_EQ(STATUS_SUCCESS, + createSignalingSync(&clientInfoInternal, &channelInfo, &signalingClientCallbacks, (PAwsCredentialProvider) mTestCredentialProvider, + &pSignalingClient)); + signalingHandle = TO_SIGNALING_CLIENT_HANDLE(pSignalingClient); + EXPECT_EQ(STATUS_SUCCESS,signalingClientFetchSync(signalingHandle)); + EXPECT_TRUE(IS_VALID_SIGNALING_CLIENT_HANDLE(signalingHandle)); + EXPECT_EQ(STATUS_SUCCESS, freeSignalingClient(&signalingHandle)); + } + + describeCountNoCache = describeCount; + describeMediaCountNoCache = describeMediaCount; + getEndpointCountNoCache = getEndpointCount; + + for (i = 0; i < totalChannelCount; ++i) { + SPRINTF(signalingChannelName, "%s%u", TEST_SIGNALING_CHANNEL_NAME, i); + channelInfo.pChannelName = signalingChannelName; + channelInfo.pChannelArn = NULL; + EXPECT_EQ(STATUS_SUCCESS, + createSignalingSync(&clientInfoInternal, &channelInfo, &signalingClientCallbacks, (PAwsCredentialProvider) mTestCredentialProvider, + &pSignalingClient)) + << "Failed on channel name: " << channelInfo.pChannelName; + + signalingHandle = TO_SIGNALING_CLIENT_HANDLE(pSignalingClient); + EXPECT_TRUE(IS_VALID_SIGNALING_CLIENT_HANDLE(signalingHandle)); + EXPECT_EQ(STATUS_SUCCESS,signalingClientFetchSync(signalingHandle)); + + // Store the channel ARN to be used later + STRCPY(channelArn, pSignalingClient->channelDescription.channelArn); + + EXPECT_EQ(STATUS_SUCCESS, freeSignalingClient(&signalingHandle)); + + // Repeat the same with the ARN only + channelInfo.pChannelName = NULL; + channelInfo.pChannelArn = channelArn; + + EXPECT_EQ(STATUS_SUCCESS, + createSignalingSync(&clientInfoInternal, &channelInfo, &signalingClientCallbacks, (PAwsCredentialProvider) mTestCredentialProvider, + &pSignalingClient)); + + signalingHandle = TO_SIGNALING_CLIENT_HANDLE(pSignalingClient); + EXPECT_TRUE(IS_VALID_SIGNALING_CLIENT_HANDLE(signalingHandle)); + EXPECT_EQ(STATUS_SUCCESS, signalingClientFetchSync(signalingHandle)); + EXPECT_EQ(STATUS_SUCCESS, freeSignalingClient(&signalingHandle)); + } + + DLOGD("describeCount: %d, describeCountNoCache: %d", describeCount, describeCountNoCache); + DLOGD("describeMediaCount: %d, describeMediaCountNoCache: %d", describeMediaCount, describeMediaCountNoCache); + DLOGD("getEndpointCount: %d, getEndpointCountNoCache: %d", getEndpointCount, getEndpointCountNoCache); + + /* describeCount and getEndpointCount should only increase by 2 because they are cached for all channels except one and we iterate twice*/ + EXPECT_TRUE(describeCount > describeCountNoCache && (describeCount - describeCountNoCache) == 2); + EXPECT_TRUE(describeMediaCount > describeMediaCountNoCache && (describeMediaCount - describeMediaCountNoCache) == 2); + EXPECT_TRUE(getEndpointCount > getEndpointCountNoCache && (getEndpointCount - getEndpointCountNoCache) == 2); +} + + + +} // namespace webrtcclient +} // namespace video +} // namespace kinesis +} // namespace amazonaws +} // namespace com + +#endif diff --git a/tst/SignalingApiFunctionalityTest.cpp b/tst/SignalingApiFunctionalityTest.cpp index 3197bf15aa..377b9b3cf5 100644 --- a/tst/SignalingApiFunctionalityTest.cpp +++ b/tst/SignalingApiFunctionalityTest.cpp @@ -1,4 +1,4 @@ -#include "WebRTCClientTestFixture.h" +#include "SignalingApiFunctionalityTest.h" namespace com { namespace amazonaws { @@ -6,60 +6,41 @@ namespace kinesis { namespace video { namespace webrtcclient { -class SignalingApiFunctionalityTest : public WebRtcClientTestBase { - public: - SignalingApiFunctionalityTest() : pActiveClient(NULL) - { - MEMSET(signalingStatesCounts, 0x00, SIZEOF(signalingStatesCounts)); - - getIceConfigCount = 0; - getIceConfigFail = MAX_UINT32; - getIceConfigRecover = 0; - getIceConfigResult = STATUS_SUCCESS; - - connectCount = 0; - connectFail = MAX_UINT32; - connectRecover = 0; - connectResult = STATUS_SUCCESS; - - describeCount = 0; - describeFail = MAX_UINT32; - describeRecover = 0; - describeResult = STATUS_SUCCESS; - - getEndpointCount = 0; - getEndpointFail = MAX_UINT32; - getEndpointRecover = 0; - getEndpointResult = STATUS_SUCCESS; - - errStatus = STATUS_SUCCESS; - errMsg[0] = '\0'; - }; - - PSignalingClient pActiveClient; - UINT32 getIceConfigFail; - UINT32 getIceConfigRecover; - UINT32 getIceConfigCount; - STATUS getIceConfigResult; - UINT32 signalingStatesCounts[SIGNALING_CLIENT_STATE_MAX_VALUE]; - STATUS errStatus; - CHAR errMsg[1024]; - - STATUS connectResult; - UINT32 connectFail; - UINT32 connectRecover; - UINT32 connectCount; - - STATUS describeResult; - UINT32 describeFail; - UINT32 describeRecover; - UINT32 describeCount; - - STATUS getEndpointResult; - UINT32 getEndpointFail; - UINT32 getEndpointRecover; - UINT32 getEndpointCount; -}; + + + +SignalingApiFunctionalityTest::SignalingApiFunctionalityTest() : pActiveClient(NULL) +{ + MEMSET(signalingStatesCounts, 0x00, SIZEOF(signalingStatesCounts)); + + getIceConfigCount = 0; + getIceConfigFail = MAX_UINT32; + getIceConfigRecover = 0; + getIceConfigResult = STATUS_SUCCESS; + + connectCount = 0; + connectFail = MAX_UINT32; + connectRecover = 0; + connectResult = STATUS_SUCCESS; + + describeCount = 0; + describeFail = MAX_UINT32; + describeRecover = 0; + describeResult = STATUS_SUCCESS; + + describeMediaCount = 0; + describeMediaFail = MAX_UINT32; + describeMediaRecover = 0; + describeMediaResult = STATUS_SUCCESS; + + getEndpointCount = 0; + getEndpointFail = MAX_UINT32; + getEndpointRecover = 0; + getEndpointResult = STATUS_SUCCESS; + + errStatus = STATUS_SUCCESS; + errMsg[0] = '\0'; +} STATUS masterMessageReceived(UINT64 customData, PReceivedSignalingMessage pReceivedSignalingMessage) { @@ -184,6 +165,21 @@ STATUS describePreHook(UINT64 hookCustomData) return retStatus; }; +STATUS describeMediaPreHook(UINT64 hookCustomData) +{ + STATUS retStatus = STATUS_SUCCESS; + SignalingApiFunctionalityTest* pTest = (SignalingApiFunctionalityTest*) hookCustomData; + CHECK(pTest != NULL); + + if (pTest->describeMediaCount >= pTest->describeFail && pTest->describeMediaCount < pTest->describeMediaRecover) { + retStatus = pTest->describeResult; + } + + pTest->describeMediaCount++; + DLOGD("Signaling client describe hook returning 0x%08x", retStatus); + return retStatus; +}; + STATUS getEndpointPreHook(UINT64 hookCustomData) { STATUS retStatus = STATUS_SUCCESS; @@ -3237,7 +3233,7 @@ TEST_F(SignalingApiFunctionalityTest, fileCachingTest) SIGNALING_CLIENT_HANDLE signalingHandle; CHAR signalingChannelName[64]; const UINT32 totalChannelCount = MAX_SIGNALING_CACHE_ENTRY_COUNT + 1; - UINT32 i, describeCountNoCache, getEndpointCountNoCache; + UINT32 i, describeCountNoCache, describeMediaCountNoCache, getEndpointCountNoCache; CHAR channelArn[MAX_ARN_LEN + 1]; signalingClientCallbacks.version = SIGNALING_CLIENT_CALLBACKS_CURRENT_VERSION; @@ -3255,6 +3251,7 @@ TEST_F(SignalingApiFunctionalityTest, fileCachingTest) clientInfoInternal.hookCustomData = (UINT64) this; clientInfoInternal.connectPreHookFn = connectPreHook; clientInfoInternal.describePreHookFn = describePreHook; + clientInfoInternal.describeMediaStorageConfPreHookFn = describeMediaPreHook; clientInfoInternal.getEndpointPreHookFn = getEndpointPreHook; setupSignalingStateMachineRetryStrategyCallbacks(&clientInfoInternal); @@ -3272,6 +3269,7 @@ TEST_F(SignalingApiFunctionalityTest, fileCachingTest) channelInfo.cachingPolicy = SIGNALING_API_CALL_CACHE_TYPE_FILE; channelInfo.pRegion = TEST_DEFAULT_REGION; + FREMOVE(DEFAULT_CACHE_FILE_PATH); for (i = 0; i < totalChannelCount; ++i) { @@ -3287,6 +3285,7 @@ TEST_F(SignalingApiFunctionalityTest, fileCachingTest) } describeCountNoCache = describeCount; + describeMediaCountNoCache = describeMediaCount; getEndpointCountNoCache = getEndpointCount; for (i = 0; i < totalChannelCount; ++i) { @@ -3317,18 +3316,22 @@ TEST_F(SignalingApiFunctionalityTest, fileCachingTest) signalingHandle = TO_SIGNALING_CLIENT_HANDLE(pSignalingClient); EXPECT_TRUE(IS_VALID_SIGNALING_CLIENT_HANDLE(signalingHandle)); - EXPECT_EQ(STATUS_SUCCESS,signalingClientFetchSync(signalingHandle)); + EXPECT_EQ(STATUS_SUCCESS, signalingClientFetchSync(signalingHandle)); EXPECT_EQ(STATUS_SUCCESS, freeSignalingClient(&signalingHandle)); } DLOGD("describeCount: %d, describeCountNoCache: %d", describeCount, describeCountNoCache); + DLOGD("describeMediaCount: %d, describeMediaCountNoCache: %d", describeMediaCount, describeMediaCountNoCache); DLOGD("getEndpointCount: %d, getEndpointCountNoCache: %d", getEndpointCount, getEndpointCountNoCache); /* describeCount and getEndpointCount should only increase by 2 because they are cached for all channels except one and we iterate twice*/ EXPECT_TRUE(describeCount > describeCountNoCache && (describeCount - describeCountNoCache) == 2); - EXPECT_TRUE(getEndpointCount > getEndpointCountNoCache && (getEndpointCount - 2*getEndpointCountNoCache) == 2); + EXPECT_TRUE(describeMediaCount == 0); + EXPECT_TRUE(getEndpointCount > getEndpointCountNoCache && (getEndpointCount - getEndpointCountNoCache) == 2); } + + TEST_F(SignalingApiFunctionalityTest, fileCachingUpdateCache) { FREMOVE(DEFAULT_CACHE_FILE_PATH); @@ -3336,6 +3339,13 @@ TEST_F(SignalingApiFunctionalityTest, fileCachingUpdateCache) SignalingFileCacheEntry testEntry; SignalingFileCacheEntry testEntry2; + // It is very important to MEMSET these to 0. + // Otherwise SignalingFileCacheEntry will have uninitialized values for + // the items that were not initialized and this results in garbage values + // being written to the cache file + MEMSET(&testEntry, 0x00, SIZEOF(testEntry)); + MEMSET(&testEntry2, 0x00, SIZEOF(testEntry2)); + testEntry.role = SIGNALING_CHANNEL_ROLE_TYPE_VIEWER; STRCPY(testEntry.wssEndpoint, "testWssEnpoint"); STRCPY(testEntry.httpsEndpoint, "testHttpsEnpoint"); @@ -3365,6 +3375,8 @@ TEST_F(SignalingApiFunctionalityTest, fileCachingUpdateMultiChannelCache) srand(GETTIME()); SignalingFileCacheEntry testEntry; + MEMSET(&testEntry, 0x00, SIZEOF(testEntry)); + BOOL cacheFound = FALSE; int additionalEntries = rand()%15 + 2; char testWssEndpoint[MAX_SIGNALING_ENDPOINT_URI_LEN + 1] = {0}; @@ -3379,6 +3391,9 @@ TEST_F(SignalingApiFunctionalityTest, fileCachingUpdateMultiChannelCache) UINT64 fileSize; UINT32 entryCount; SignalingFileCacheEntry entries[MAX_SIGNALING_CACHE_ENTRY_COUNT]; + MEMSET(entries, 0x00, SIZEOF(entries)); + + const int TEST_CHANNEL_COUNT = 5; for(i = 0; i < MAX_SIGNALING_CACHE_ENTRY_COUNT + additionalEntries; i++) { @@ -3426,7 +3441,6 @@ TEST_F(SignalingApiFunctionalityTest, fileCachingUpdateMultiChannelCache) EXPECT_LT(entryCount, TEST_CHANNEL_COUNT+1); MEMFREE(fileBuffer); - FREMOVE(DEFAULT_CACHE_FILE_PATH); } @@ -3436,6 +3450,8 @@ TEST_F(SignalingApiFunctionalityTest, fileCachingUpdateFullMultiChannelCache) srand(GETTIME()); SignalingFileCacheEntry testEntry; + MEMSET(&testEntry, 0x00, SIZEOF(testEntry)); + BOOL cacheFound = FALSE; int additionalEntries = rand()%15 + 2; char testWssEndpoint[MAX_SIGNALING_ENDPOINT_URI_LEN + 1] = {0}; @@ -3483,6 +3499,7 @@ TEST_F(SignalingApiFunctionalityTest, fileCachingUpdateFullMultiChannelCache) FREMOVE(DEFAULT_CACHE_FILE_PATH); } + TEST_F(SignalingApiFunctionalityTest, receivingIceConfigOffer) { ASSERT_EQ(TRUE, mAccessKeyIdSet); @@ -4265,7 +4282,6 @@ TEST_F(SignalingApiFunctionalityTest, receivingIceConfigOffer_FastClockSkew_Veri EXPECT_EQ(STATUS_SUCCESS, freeSignalingClient(&signalingHandle)); } - } // namespace webrtcclient } // namespace video } // namespace kinesis diff --git a/tst/SignalingApiFunctionalityTest.h b/tst/SignalingApiFunctionalityTest.h new file mode 100644 index 0000000000..6fe3dc7937 --- /dev/null +++ b/tst/SignalingApiFunctionalityTest.h @@ -0,0 +1,67 @@ + +#ifndef AMAZON_KINESIS_VIDEO_STREAMS_WEBRTC_SDK_C_SIGNALINGAPIFUNCTIONALITYTEST_H +#define AMAZON_KINESIS_VIDEO_STREAMS_WEBRTC_SDK_C_SIGNALINGAPIFUNCTIONALITYTEST_H + +#include "WebRTCClientTestFixture.h" + +namespace com { +namespace amazonaws { +namespace kinesis { +namespace video { +namespace webrtcclient { + +class SignalingApiFunctionalityTest : public WebRtcClientTestBase { + public: + SignalingApiFunctionalityTest(); + + PSignalingClient pActiveClient; + UINT32 getIceConfigFail; + UINT32 getIceConfigRecover; + UINT32 getIceConfigCount; + STATUS getIceConfigResult; + UINT32 signalingStatesCounts[SIGNALING_CLIENT_STATE_MAX_VALUE]; + STATUS errStatus; + CHAR errMsg[1024]; + + STATUS connectResult; + UINT32 connectFail; + UINT32 connectRecover; + UINT32 connectCount; + + STATUS describeResult; + UINT32 describeFail; + UINT32 describeRecover; + UINT32 describeCount; + + STATUS describeMediaResult; + UINT32 describeMediaFail; + UINT32 describeMediaRecover; + UINT32 describeMediaCount; + + STATUS getEndpointResult; + UINT32 getEndpointFail; + UINT32 getEndpointRecover; + UINT32 getEndpointCount; +}; + +STATUS masterMessageReceived(UINT64, PReceivedSignalingMessage); +STATUS signalingClientStateChanged(UINT64, SIGNALING_CLIENT_STATE); +STATUS signalingClientError(UINT64, STATUS, PCHAR, UINT32); +STATUS viewerMessageReceived(UINT64, PReceivedSignalingMessage); +STATUS getIceConfigPreHook(UINT64); +UINT64 getCurrentTimeFastClock(UINT64); +UINT64 getCurrentTimeSlowClock(UINT64); +STATUS connectPreHook(UINT64); +STATUS describePreHook(UINT64); +STATUS describeMediaPreHook(UINT64); +STATUS getEndpointPreHook(UINT64); +VOID setupSignalingStateMachineRetryStrategyCallbacks(PSignalingClientInfoInternal); +VOID setupSignalingStateMachineRetryStrategyCallbacks(PSignalingClientInfo); + +} // namespace webrtcclient +} // namespace video +} // namespace kinesis +} // namespace amazonaws +} // namespace com + +#endif // AMAZON_KINESIS_VIDEO_STREAMS_WEBRTC_SDK_C_SIGNALINGAPIFUNCTIONALITYTEST_H diff --git a/tst/SignalingApiTest.cpp b/tst/SignalingApiTest.cpp index 29652b8544..ec937f999a 100644 --- a/tst/SignalingApiTest.cpp +++ b/tst/SignalingApiTest.cpp @@ -17,11 +17,14 @@ TEST_F(SignalingApiTest, createValidateChannelInfo) UINT32 postfixLen = STRLEN(SIGNALING_USER_AGENT_POSTFIX_NAME) + STRLEN(SIGNALING_USER_AGENT_POSTFIX_VERSION) + 1; SNPRINTF(agentString, postfixLen + 1, (PCHAR) "%s/%s", SIGNALING_USER_AGENT_POSTFIX_NAME, SIGNALING_USER_AGENT_POSTFIX_VERSION); STRCPY(mChannelArn, TEST_CHANNEL_ARN); + STRCPY(mStreamArn, TEST_STREAM_ARN); STRCPY(mKmsKeyId, TEST_KMS_KEY_ID_ARN); mChannelInfo.pChannelArn = mChannelArn; + mChannelInfo.pStorageStreamArn = mStreamArn; mChannelInfo.pKmsKeyId = mKmsKeyId; EXPECT_EQ(STATUS_SUCCESS, createValidateChannelInfo(&mChannelInfo, &rChannelInfo)); EXPECT_EQ(0, STRCMP(rChannelInfo->pChannelArn, TEST_CHANNEL_ARN)); + EXPECT_EQ(0, STRCMP(rChannelInfo->pStorageStreamArn, TEST_STREAM_ARN)); EXPECT_EQ(0, STRCMP(rChannelInfo->pKmsKeyId, TEST_KMS_KEY_ID_ARN)); EXPECT_EQ(rChannelInfo->version, CHANNEL_INFO_CURRENT_VERSION); EXPECT_EQ(rChannelInfo->tagCount, 3); @@ -40,6 +43,160 @@ TEST_F(SignalingApiTest, createValidateChannelInfo) freeChannelInfo(&rChannelInfo); } +TEST_F(SignalingApiTest, testChannelArnsValid) +{ + PChannelInfo pChannelInfo; + ChannelInfo channelInfo; + + MEMSET(&channelInfo, 0x00, SIZEOF(ChannelInfo)); + + PCHAR arn1 = (PCHAR)"arn:aws:kinesisvideo:us-west-2:123456789012:channel/a/0123456789012"; + PCHAR arn2 = (PCHAR)"arn:aws:kinesisvideo:us-west-2:123456789012:channel/ab/0123456789012"; + PCHAR arn3 = (PCHAR)"arn:aws-cn:kinesisvideo:us-west-2:123456789012:channel/channel_name/0123456789012"; + PCHAR arn4 = (PCHAR)"arn:aws-xyz:kinesisvideo:us-west-2:123456789012:channel/channel_name/0123456789012"; + PCHAR arn5 = (PCHAR)"arn:aws:kinesisvideo:us-east-2:123456789012:channel/channel_name/0123456789012"; + PCHAR arn6 = (PCHAR)"arn:aws:kinesisvideo:us-east-1:123456789012:channel/channel_name/0123456789012"; + PCHAR arn7 = (PCHAR)"arn:aws:kinesisvideo:us-west-2:123456789012:channel/channel_name/5738283847173"; + PCHAR arn8 = (PCHAR)"arn:aws:kinesisvideo:us-west-2:123456789012:channel/channel_name/1223445566666"; + + channelInfo.pChannelArn = arn1; + EXPECT_EQ(createValidateChannelInfo(&channelInfo, &pChannelInfo), STATUS_SUCCESS); + freeChannelInfo(&pChannelInfo); + + channelInfo.pChannelArn = arn2; + EXPECT_EQ(createValidateChannelInfo(&channelInfo, &pChannelInfo), STATUS_SUCCESS); + freeChannelInfo(&pChannelInfo); + + channelInfo.pChannelArn = arn3; + EXPECT_EQ(createValidateChannelInfo(&channelInfo, &pChannelInfo), STATUS_SUCCESS); + freeChannelInfo(&pChannelInfo); + + channelInfo.pChannelArn = arn4; + EXPECT_EQ(createValidateChannelInfo(&channelInfo, &pChannelInfo), STATUS_SUCCESS); + freeChannelInfo(&pChannelInfo); + + channelInfo.pChannelArn = arn5; + EXPECT_EQ(createValidateChannelInfo(&channelInfo, &pChannelInfo), STATUS_SUCCESS); + freeChannelInfo(&pChannelInfo); + + channelInfo.pChannelArn = arn6; + EXPECT_EQ(createValidateChannelInfo(&channelInfo, &pChannelInfo), STATUS_SUCCESS); + freeChannelInfo(&pChannelInfo); + + channelInfo.pChannelArn = arn7; + EXPECT_EQ(createValidateChannelInfo(&channelInfo, &pChannelInfo), STATUS_SUCCESS); + freeChannelInfo(&pChannelInfo); + + channelInfo.pChannelArn = arn8; + EXPECT_EQ(createValidateChannelInfo(&channelInfo, &pChannelInfo), STATUS_SUCCESS); + freeChannelInfo(&pChannelInfo); +} + + +TEST_F(SignalingApiTest, testChannelArnsInValid) +{ + PChannelInfo pChannelInfo; + ChannelInfo channelInfo; + MEMSET(&channelInfo, 0x00, SIZEOF(ChannelInfo)); + + PCHAR arn1 = (PCHAR)"arn:aws:kinesaisvideo:us-west-2:123456789012:channel/a/0123456789012"; + PCHAR arn2 = (PCHAR)"arn:aws:kinesisvideo:us-west-2:123456789012:chanel/ab/0123456789012"; + PCHAR arn3 = (PCHAR)"arn:aw:kinesisvideo:us-west-2:123456789012:channel/channel_name/0123456789012"; + PCHAR arn4 = (PCHAR)"arn:aws-xyz:kinesisvideo:us-west-2:12345679012:channel/channel_name/0123456789012"; + PCHAR arn5 = (PCHAR)"arn:aws:kinesisvideo:us-east-2:123456789012:channel/channel_name/012345679012"; + PCHAR arn6 = (PCHAR)"arn:aws:kinesisvideo:us-east-1:123456789012:channel//0123456789012"; + PCHAR arn7 = (PCHAR)"arn:aws:kinesisvideo:us-west-2:123456789012:channel/5738283847173"; + PCHAR arn8 = (PCHAR)"arn:aws:kinesisvideo:us-west-2:123456789012:channel1223445566666"; + PCHAR arn9 = (PCHAR)"arn:aws:kinesisvideo:us-west-2:123456789012channel/a/0123456789012"; + PCHAR arn10 = (PCHAR)"arn:aws:kinesisvideo:us-west-2:123456789012:channnnnnnnnel/ab/0123456789012"; + PCHAR arn11 = (PCHAR)"arn:aws:kinesisvideo:123456789012:channel/channel_name/0123456789012"; + PCHAR arn12 = (PCHAR)"arn:aws-xyz:kinesisvideo:::channel/channel_name/0123456789012"; + PCHAR arn13 = (PCHAR)"arn:aws:012345679012"; + PCHAR arn14 = (PCHAR)"this:is:a:test:arn:which:is:not:real"; + PCHAR arn15 = (PCHAR)"arn:aws:kinesisvideo:us-west-2:123456789012:/:/:///5738283847173"; + PCHAR arn16 = (PCHAR)"arn:aws:kinesisvideo:us-west-2:cool_channel_Name"; + PCHAR arn17 = (PCHAR)"arn:aws:kinesisvideo::123456789012:channel/a/0123456789012"; + PCHAR arn18 = (PCHAR)"arn:aws:kinesisvideo:us-west-2:123456789012:channel/a/01234567890123"; + PCHAR arn19 = (PCHAR)"ar:aws:kinesisvideo:us-west-2:123456789012:channel/a/0123456789012"; + PCHAR arn20 = (PCHAR)"arn:aws:kinesisvideo::us-west-2:123456789012:channel/a/01234567890123"; + PCHAR arn21 = (PCHAR)"arn:aws::kinesisvideo:us-west-2:123456789012:channel/a/01234567890123"; + PCHAR arn22 = (PCHAR)"arn::aws::kinesisvideo:us-west-2:123456789012:channel/a/01234567890123"; + PCHAR arn23 = (PCHAR)"arn:aws::kinesisvideo:us-west-2::123456789012:channel/a/01234567890123"; + PCHAR arn24 = (PCHAR)"arn:aws:kinesisvideo:us-west-2:123456789012:channel/a/b/0123456789012"; + + channelInfo.pChannelArn = arn1; + EXPECT_EQ(STATUS_SIGNALING_INVALID_CHANNEL_ARN, createValidateChannelInfo(&channelInfo, &pChannelInfo)); + + channelInfo.pChannelArn = arn2; + EXPECT_EQ(STATUS_SIGNALING_INVALID_CHANNEL_ARN, createValidateChannelInfo(&channelInfo, &pChannelInfo)); + + channelInfo.pChannelArn = arn3; + EXPECT_EQ(STATUS_SIGNALING_INVALID_CHANNEL_ARN, createValidateChannelInfo(&channelInfo, &pChannelInfo)); + + channelInfo.pChannelArn = arn4; + EXPECT_EQ(STATUS_SIGNALING_INVALID_CHANNEL_ARN, createValidateChannelInfo(&channelInfo, &pChannelInfo)); + + channelInfo.pChannelArn = arn5; + EXPECT_EQ(STATUS_SIGNALING_INVALID_CHANNEL_ARN, createValidateChannelInfo(&channelInfo, &pChannelInfo)); + + channelInfo.pChannelArn = arn6; + EXPECT_EQ(STATUS_SIGNALING_INVALID_CHANNEL_ARN, createValidateChannelInfo(&channelInfo, &pChannelInfo)); + + channelInfo.pChannelArn = arn7; + EXPECT_EQ(STATUS_SIGNALING_INVALID_CHANNEL_ARN, createValidateChannelInfo(&channelInfo, &pChannelInfo)); + + channelInfo.pChannelArn = arn8; + EXPECT_EQ(STATUS_SIGNALING_INVALID_CHANNEL_ARN, createValidateChannelInfo(&channelInfo, &pChannelInfo)); + + channelInfo.pChannelArn = arn9; + EXPECT_EQ(STATUS_SIGNALING_INVALID_CHANNEL_ARN, createValidateChannelInfo(&channelInfo, &pChannelInfo)); + + channelInfo.pChannelArn = arn10; + EXPECT_EQ(STATUS_SIGNALING_INVALID_CHANNEL_ARN, createValidateChannelInfo(&channelInfo, &pChannelInfo)); + + channelInfo.pChannelArn = arn11; + EXPECT_EQ(STATUS_SIGNALING_INVALID_CHANNEL_ARN, createValidateChannelInfo(&channelInfo, &pChannelInfo)); + + channelInfo.pChannelArn = arn12; + EXPECT_EQ(STATUS_SIGNALING_INVALID_CHANNEL_ARN, createValidateChannelInfo(&channelInfo, &pChannelInfo)); + + channelInfo.pChannelArn = arn13; + EXPECT_EQ(STATUS_SIGNALING_INVALID_CHANNEL_ARN, createValidateChannelInfo(&channelInfo, &pChannelInfo)); + + channelInfo.pChannelArn = arn14; + EXPECT_EQ(STATUS_SIGNALING_INVALID_CHANNEL_ARN, createValidateChannelInfo(&channelInfo, &pChannelInfo)); + + channelInfo.pChannelArn = arn15; + EXPECT_EQ(STATUS_SIGNALING_INVALID_CHANNEL_ARN, createValidateChannelInfo(&channelInfo, &pChannelInfo)); + + channelInfo.pChannelArn = arn16; + EXPECT_EQ(STATUS_SIGNALING_INVALID_CHANNEL_ARN, createValidateChannelInfo(&channelInfo, &pChannelInfo)); + + channelInfo.pChannelArn = arn17; + EXPECT_EQ(STATUS_SIGNALING_INVALID_CHANNEL_ARN, createValidateChannelInfo(&channelInfo, &pChannelInfo)); + + channelInfo.pChannelArn = arn18; + EXPECT_EQ(STATUS_SIGNALING_INVALID_CHANNEL_ARN, createValidateChannelInfo(&channelInfo, &pChannelInfo)); + + channelInfo.pChannelArn = arn19; + EXPECT_EQ(STATUS_SIGNALING_INVALID_CHANNEL_ARN, createValidateChannelInfo(&channelInfo, &pChannelInfo)); + + channelInfo.pChannelArn = arn20; + EXPECT_EQ(STATUS_SIGNALING_INVALID_CHANNEL_ARN, createValidateChannelInfo(&channelInfo, &pChannelInfo)); + + channelInfo.pChannelArn = arn21; + EXPECT_EQ(STATUS_SIGNALING_INVALID_CHANNEL_ARN, createValidateChannelInfo(&channelInfo, &pChannelInfo)); + + channelInfo.pChannelArn = arn22; + EXPECT_EQ(STATUS_SIGNALING_INVALID_CHANNEL_ARN, createValidateChannelInfo(&channelInfo, &pChannelInfo)); + + channelInfo.pChannelArn = arn23; + EXPECT_EQ(STATUS_SIGNALING_INVALID_CHANNEL_ARN, createValidateChannelInfo(&channelInfo, &pChannelInfo)); + + channelInfo.pChannelArn = arn24; + EXPECT_EQ(STATUS_SIGNALING_INVALID_CHANNEL_ARN, createValidateChannelInfo(&channelInfo, &pChannelInfo)); +} + TEST_F(SignalingApiTest, signalingSendMessageSync) { STATUS expectedStatus; @@ -91,7 +248,6 @@ TEST_F(SignalingApiTest, signalingSendMessageSyncFileCredsProvider) UINT32 length = ARRAY_SIZE(fileContent); CHAR futureTime[] = "2200-06-05T09:39:36Z"; - ASSERT_EQ(TRUE, mAccessKeyIdSet); if (mSessionToken == NULL) { @@ -104,9 +260,8 @@ TEST_F(SignalingApiTest, signalingSendMessageSyncFileCredsProvider) length = SNPRINTF(fileContent, length, "CREDENTIALS %s %s %s %s", mAccessKey, futureTime, mSecretKey, mSessionToken); ASSERT_GT(ARRAY_SIZE(fileContent), length); } - - ASSERT_EQ(STATUS_SUCCESS, writeFile(TEST_FILE_CREDENTIALS_FILE_PATH, FALSE, FALSE, (PBYTE) fileContent, length)); + ASSERT_EQ(STATUS_SUCCESS, writeFile(TEST_FILE_CREDENTIALS_FILE_PATH, FALSE, FALSE, (PBYTE) fileContent, length)); // Create file creds provider from the file EXPECT_EQ(STATUS_SUCCESS, createFileCredentialProvider(TEST_FILE_CREDENTIALS_FILE_PATH, &pAwsCredentialProvider)); diff --git a/tst/TurnConnectionFunctionalityTest.cpp b/tst/TurnConnectionFunctionalityTest.cpp index 9f02977a60..c2de24c876 100644 --- a/tst/TurnConnectionFunctionalityTest.cpp +++ b/tst/TurnConnectionFunctionalityTest.cpp @@ -26,7 +26,9 @@ class TurnConnectionFunctionalityTest : public WebRtcClientTestBase { PKvsIpAddress pTurnSocketAddr = NULL; PSocketConnection pTurnSocket = NULL; - initializeSignalingClient(); + // If this failed we will not be in the Connected state, need to bail out + ASSERT_EQ(STATUS_SUCCESS, initializeSignalingClient()); + EXPECT_EQ(STATUS_SUCCESS, signalingClientGetIceConfigInfoCount(mSignalingClientHandle, &iceConfigCount)); for (uriCount = 0, i = 0; i < iceConfigCount; i++) { diff --git a/tst/WebRTCClientTestFixture.h b/tst/WebRTCClientTestFixture.h index f4abf4532e..cd1a9d93f0 100644 --- a/tst/WebRTCClientTestFixture.h +++ b/tst/WebRTCClientTestFixture.h @@ -16,6 +16,7 @@ #define TEST_SIGNALING_CHANNEL_NAME (PCHAR) "ScaryTestChannel_" #define TEST_KMS_KEY_ID_ARN (PCHAR) "arn:aws:kms:us-west-2:123456789012:key/0000-0000-0000-0000-0000" #define TEST_CHANNEL_ARN (PCHAR) "arn:aws:kinesisvideo:us-west-2:123456789012:channel/ScaryTestChannel" +#define TEST_STREAM_ARN (PCHAR) "arn:aws:kinesisvideo:us-west-2:123456789012:stream/ScaryTestStream" #define SIGNAING_TEST_CORRELATION_ID (PCHAR) "Test_correlation_id" #define TEST_SIGNALING_MESSAGE_TTL (120 * HUNDREDS_OF_NANOS_IN_A_SECOND) #define TEST_VIDEO_FRAME_SIZE (120 * 1024) @@ -297,6 +298,7 @@ class WebRtcClientTestBase : public ::testing::Test { BOOL mAccessKeyIdSet; CHAR mChannelName[MAX_CHANNEL_NAME_LEN + 1]; CHAR mChannelArn[MAX_ARN_LEN + 1]; + CHAR mStreamArn[MAX_ARN_LEN + 1]; CHAR mKmsKeyId[MAX_ARN_LEN + 1]; PJitterBuffer mJitterBuffer; diff --git a/tst/main.cpp b/tst/main.cpp index f12f3989fe..43121344c9 100644 --- a/tst/main.cpp +++ b/tst/main.cpp @@ -7,6 +7,10 @@ #include #include +#ifdef ENABLE_AWS_SDK_IN_TESTS +#include +#endif + // The number of retries allowed. 0 means no retry, all tests will run exactly run once. #define MAX_TRIALS 10 @@ -46,10 +50,16 @@ int main(int argc, char** argv) { int trial = 0, rc; bool breakOnFailure; - +#ifdef ENABLE_AWS_SDK_IN_TESTS + Aws::SDKOptions options; +#endif ::testing::InitGoogleTest(&argc, argv); breakOnFailure = ::testing::GTEST_FLAG(break_on_failure); +#ifdef ENABLE_AWS_SDK_IN_TESTS + Aws::InitAPI(options); +#endif + Retrier* retrier = new Retrier(); // Adds a listener to the end. googletest takes the ownership. ::testing::TestEventListeners& listeners = ::testing::UnitTest::GetInstance()->listeners(); @@ -73,5 +83,9 @@ int main(int argc, char** argv) ::testing::GTEST_FLAG(filter) = retrier->testFilter(); } while (rc != 0 && trial++ < MAX_TRIALS); +#ifdef ENABLE_AWS_SDK_IN_TESTS + Aws::ShutdownAPI(options); +#endif + return rc; }