├── utils ├── .npmrc └── package.json ├── .gitignore ├── scripts ├── common.sh ├── run.sh └── format.sh ├── include ├── MediaStreamTrackFactory.hpp └── Broadcaster.hpp ├── deps └── libwebrtc │ ├── test │ ├── testsupport │ │ ├── mac_file_utils.h │ │ ├── ios_file_utils.h │ │ ├── mac_file_utils.mm │ │ ├── ios_file_utils.mm │ │ ├── file_utils_override.h │ │ ├── ivf_video_frame_generator.h │ │ ├── file_utils.h │ │ ├── file_utils_override.cc │ │ ├── ivf_video_frame_generator.cc │ │ └── file_utils.cc │ ├── frame_utils.h │ ├── test_video_capturer.h │ ├── frame_utils.cc │ ├── test_video_capturer.cc │ ├── frame_generator.h │ ├── frame_generator_capturer.h │ ├── frame_generator_capturer.cc │ └── frame_generator.cc │ ├── rtc_base │ ├── task_queue_for_test.cc │ └── task_queue_for_test.h │ ├── CMakeLists.txt │ ├── pc │ └── test │ │ ├── fake_periodic_video_track_source.h │ │ ├── frame_generator_capturer_video_track_source.h │ │ ├── fake_periodic_video_source.h │ │ ├── fake_audio_capture_module.h │ │ └── fake_audio_capture_module.cc │ ├── media │ └── base │ │ ├── fake_frame_source.h │ │ └── fake_frame_source.cc │ └── api │ └── test │ ├── create_frame_generator.cc │ └── create_frame_generator.h ├── .clang-format ├── CMakeLists.txt ├── README.md ├── src ├── main.cpp ├── MediaStreamTrackFactory.cpp └── Broadcaster.cpp └── .clang-tidy /utils/.npmrc: -------------------------------------------------------------------------------- 1 | package-lock=false 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | /utils/node_modules/ 3 | -------------------------------------------------------------------------------- /utils/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "utils", 3 | "version": "0.0.1", 4 | "private": true, 5 | "os": [ 6 | "!win32" 7 | ], 8 | "dependencies": { 9 | "clang-tools-prebuilt": "^0.1.4", 10 | "clang-format": "^1.2.4" 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /scripts/common.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -e 4 | 5 | # OS name. 6 | OS="$(uname -s)" 7 | 8 | # Number of cores. 9 | NUM_CORES= 10 | 11 | case "${OS}" in 12 | Linux*) NUM_CORES=$(nproc);; 13 | Darwin*) NUM_CORES=$(sysctl -n hw.ncpu);; 14 | *) NUM_CORES=1;; 15 | esac 16 | -------------------------------------------------------------------------------- /include/MediaStreamTrackFactory.hpp: -------------------------------------------------------------------------------- 1 | #ifndef MSC_TEST_MEDIA_STREAM_TRACK_FACTORY_HPP 2 | #define MSC_TEST_MEDIA_STREAM_TRACK_FACTORY_HPP 3 | 4 | #include "api/media_stream_interface.h" 5 | 6 | rtc::scoped_refptr createAudioTrack(const std::string& label); 7 | 8 | rtc::scoped_refptr createVideoTrack(const std::string& label); 9 | 10 | rtc::scoped_refptr createSquaresVideoTrack(const std::string& label); 11 | 12 | #endif 13 | -------------------------------------------------------------------------------- /scripts/run.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -e 4 | 5 | PROJECT_PWD=${PWD} 6 | 7 | current_dir_name=${PROJECT_PWD##*/} 8 | if [ "${current_dir_name}" != "mediasoup-broadcaster-demo" ] && [ "${current_dir_name}" != "v3-mediasoup-broadcaster-demo" ] ; then 9 | echo ">>> [ERROR] $(basename $0) must be called from root directory" >&2 10 | exit 1 11 | fi 12 | 13 | if [ "$1" == "build" ]; then 14 | echo ">>> building..." 15 | cmake --build build 16 | elif [ "$1" == "rebuild" ]; then 17 | echo ">>> rebuilding..." 18 | rm -rf build/ 19 | cmake . -Bbuild 20 | cmake --build build 21 | fi 22 | 23 | # Run. 24 | ./build/broadcaster $@ 25 | -------------------------------------------------------------------------------- /deps/libwebrtc/test/testsupport/mac_file_utils.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2018 The WebRTC project authors. All Rights Reserved. 3 | * 4 | * Use of this source code is governed by a BSD-style license 5 | * that can be found in the LICENSE file in the root of the source 6 | * tree. An additional intellectual property rights grant can be found 7 | * in the file PATENTS. All contributing project authors may 8 | * be found in the AUTHORS file in the root of the source tree. 9 | */ 10 | 11 | #ifndef TEST_TESTSUPPORT_MAC_FILE_UTILS_H_ 12 | #define TEST_TESTSUPPORT_MAC_FILE_UTILS_H_ 13 | 14 | #include 15 | 16 | namespace webrtc { 17 | namespace test { 18 | 19 | void GetNSExecutablePath(std::string* path); 20 | 21 | } // namespace test 22 | } // namespace webrtc 23 | 24 | #endif // TEST_TESTSUPPORT_MAC_FILE_UTILS_H_ 25 | -------------------------------------------------------------------------------- /deps/libwebrtc/rtc_base/task_queue_for_test.cc: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 The WebRTC Project Authors. All rights reserved. 3 | * 4 | * Use of this source code is governed by a BSD-style license 5 | * that can be found in the LICENSE file in the root of the source 6 | * tree. An additional intellectual property rights grant can be found 7 | * in the file PATENTS. All contributing project authors may 8 | * be found in the AUTHORS file in the root of the source tree. 9 | */ 10 | 11 | #include "rtc_base/task_queue_for_test.h" 12 | 13 | #include "api/task_queue/default_task_queue_factory.h" 14 | 15 | namespace webrtc { 16 | 17 | TaskQueueForTest::TaskQueueForTest(absl::string_view name, Priority priority) 18 | : TaskQueue( 19 | CreateDefaultTaskQueueFactory()->CreateTaskQueue(name, priority)) {} 20 | 21 | } // namespace webrtc 22 | -------------------------------------------------------------------------------- /scripts/format.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -e 4 | 5 | PROJECT_PWD=${PWD} 6 | 7 | # Import utils // OS, NUM_CORES 8 | . scripts/common.sh 9 | 10 | if [ "${OS}" != "Darwin" ] && [ "${OS}" != "Linux" ] ; then 11 | echo "Only available for MacOS and Linux" 12 | exit 1; 13 | fi 14 | 15 | current_dir_name=${PROJECT_PWD##*/} 16 | if [ "${current_dir_name}" != "mediasoup-broadcaster-demo" ] && [ "${current_dir_name}" != "v3-mediasoup-broadcaster-demo" ] ; then 17 | echo ">>> [ERROR] $(basename $0) must be called from root directory" >&2 18 | exit 1 19 | fi 20 | 21 | # Run clang-format -i on 'include' and 'src' folders. 22 | for dir in "include src"; do 23 | find ${dir} \ 24 | \( -name '*.cpp' \ 25 | -o -name '*.hpp' \) \ 26 | -exec 'utils/node_modules/.bin/clang-format' -i '{}' \; 27 | popd &>/dev/null 28 | done 29 | -------------------------------------------------------------------------------- /deps/libwebrtc/test/testsupport/ios_file_utils.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2018 The WebRTC project authors. All Rights Reserved. 3 | * 4 | * Use of this source code is governed by a BSD-style license 5 | * that can be found in the LICENSE file in the root of the source 6 | * tree. An additional intellectual property rights grant can be found 7 | * in the file PATENTS. All contributing project authors may 8 | * be found in the AUTHORS file in the root of the source tree. 9 | */ 10 | 11 | #ifndef TEST_TESTSUPPORT_IOS_FILE_UTILS_H_ 12 | #define TEST_TESTSUPPORT_IOS_FILE_UTILS_H_ 13 | 14 | #include 15 | 16 | namespace webrtc { 17 | namespace test { 18 | 19 | std::string IOSOutputPath(); 20 | std::string IOSRootPath(); 21 | std::string IOSResourcePath(std::string name, std::string extension); 22 | 23 | } // namespace test 24 | } // namespace webrtc 25 | 26 | #endif // TEST_TESTSUPPORT_IOS_FILE_UTILS_H_ 27 | -------------------------------------------------------------------------------- /deps/libwebrtc/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | set(SOURCE_FILES 2 | api/test/create_frame_generator.cc 3 | media/base/fake_frame_source.cc 4 | pc/test/fake_audio_capture_module.cc 5 | rtc_base/task_queue_for_test.cc 6 | test/frame_generator.cc 7 | test/frame_generator_capturer.cc 8 | test/frame_utils.cc 9 | test/test_video_capturer.cc 10 | test/testsupport/ivf_video_frame_generator.cc 11 | test/testsupport/file_utils.cc 12 | test/testsupport/file_utils_override.cc 13 | ) 14 | 15 | if(APPLE) 16 | set(SOURCE_FILES ${SOURCE_FILES} 17 | test/testsupport/ios_file_utils.mm 18 | test/testsupport/mac_file_utils.mm 19 | ) 20 | endif(APPLE) 21 | 22 | # Create target. 23 | add_library(webrtc_broadcaster STATIC ${SOURCE_FILES}) 24 | 25 | # Private (implementation) header files. 26 | target_include_directories(webrtc_broadcaster PRIVATE ${PROJECT_SOURCE_DIR}/deps/libwebrtc) 27 | 28 | # Public (interface) headers from dependencies. 29 | target_include_directories(webrtc_broadcaster PUBLIC 30 | "${LIBWEBRTC_INCLUDE_PATH}" 31 | "${LIBWEBRTC_INCLUDE_PATH}/third_party/abseil-cpp" 32 | ) 33 | 34 | # Compile definitions for libwebrtc. 35 | target_compile_definitions(webrtc_broadcaster PUBLIC 36 | $<$>:WEBRTC_POSIX> 37 | $<$:WEBRTC_WIN> 38 | $<$:WEBRTC_MAC> 39 | ) 40 | 41 | -------------------------------------------------------------------------------- /deps/libwebrtc/test/testsupport/mac_file_utils.mm: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2018 The WebRTC project authors. All Rights Reserved. 3 | * 4 | * Use of this source code is governed by a BSD-style license 5 | * that can be found in the LICENSE file in the root of the source 6 | * tree. An additional intellectual property rights grant can be found 7 | * in the file PATENTS. All contributing project authors may 8 | * be found in the AUTHORS file in the root of the source tree. 9 | */ 10 | 11 | #import 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | #include "rtc_base/checks.h" 18 | 19 | namespace webrtc { 20 | namespace test { 21 | 22 | void GetNSExecutablePath(std::string* path) { 23 | RTC_DCHECK(path); 24 | // Executable path can have relative references ("..") depending on 25 | // how the app was launched. 26 | uint32_t executable_length = 0; 27 | _NSGetExecutablePath(NULL, &executable_length); 28 | RTC_DCHECK_GT(executable_length, 1u); 29 | char executable_path[PATH_MAX + 1]; 30 | int rv = _NSGetExecutablePath(executable_path, &executable_length); 31 | RTC_DCHECK_EQ(rv, 0); 32 | 33 | char full_path[PATH_MAX]; 34 | if (realpath(executable_path, full_path) == nullptr) { 35 | *path = ""; 36 | return; 37 | } 38 | 39 | *path = full_path; 40 | } 41 | 42 | } // namespace test 43 | } // namespace webrtc 44 | -------------------------------------------------------------------------------- /deps/libwebrtc/pc/test/fake_periodic_video_track_source.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 The WebRTC project authors. All Rights Reserved. 3 | * 4 | * Use of this source code is governed by a BSD-style license 5 | * that can be found in the LICENSE file in the root of the source 6 | * tree. An additional intellectual property rights grant can be found 7 | * in the file PATENTS. All contributing project authors may 8 | * be found in the AUTHORS file in the root of the source tree. 9 | */ 10 | 11 | #ifndef PC_TEST_FAKE_PERIODIC_VIDEO_TRACK_SOURCE_H_ 12 | #define PC_TEST_FAKE_PERIODIC_VIDEO_TRACK_SOURCE_H_ 13 | 14 | #include "pc/test/fake_periodic_video_source.h" 15 | #include "pc/video_track_source.h" 16 | 17 | namespace webrtc { 18 | 19 | // A VideoTrackSource generating frames with configured size and frame interval. 20 | class FakePeriodicVideoTrackSource : public VideoTrackSource { 21 | public: 22 | explicit FakePeriodicVideoTrackSource(bool remote) 23 | : FakePeriodicVideoTrackSource(FakePeriodicVideoSource::Config(), 24 | remote) {} 25 | 26 | FakePeriodicVideoTrackSource(FakePeriodicVideoSource::Config config, 27 | bool remote) 28 | : VideoTrackSource(remote), source_(config) {} 29 | 30 | ~FakePeriodicVideoTrackSource() = default; 31 | 32 | const FakePeriodicVideoSource& fake_periodic_source() const { 33 | return source_; 34 | } 35 | 36 | protected: 37 | rtc::VideoSourceInterface* source() override { return &source_; } 38 | 39 | private: 40 | FakePeriodicVideoSource source_; 41 | }; 42 | 43 | } // namespace webrtc 44 | 45 | #endif // PC_TEST_FAKE_PERIODIC_VIDEO_TRACK_SOURCE_H_ 46 | -------------------------------------------------------------------------------- /deps/libwebrtc/media/base/fake_frame_source.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2018 The WebRTC project authors. All Rights Reserved. 3 | * 4 | * Use of this source code is governed by a BSD-style license 5 | * that can be found in the LICENSE file in the root of the source 6 | * tree. An additional intellectual property rights grant can be found 7 | * in the file PATENTS. All contributing project authors may 8 | * be found in the AUTHORS file in the root of the source tree. 9 | */ 10 | 11 | #ifndef MEDIA_BASE_FAKE_FRAME_SOURCE_H_ 12 | #define MEDIA_BASE_FAKE_FRAME_SOURCE_H_ 13 | 14 | #include "api/video/video_frame.h" 15 | #include "rtc_base/time_utils.h" 16 | 17 | namespace cricket { 18 | 19 | class FakeFrameSource { 20 | public: 21 | FakeFrameSource(int width, 22 | int height, 23 | int interval_us, 24 | int64_t timestamp_offset_us); 25 | FakeFrameSource(int width, int height, int interval_us); 26 | 27 | webrtc::VideoRotation GetRotation() const; 28 | void SetRotation(webrtc::VideoRotation rotation); 29 | 30 | webrtc::VideoFrame GetFrame(); 31 | webrtc::VideoFrame GetFrameRotationApplied(); 32 | 33 | // Override configuration. 34 | webrtc::VideoFrame GetFrame(int width, 35 | int height, 36 | webrtc::VideoRotation rotation, 37 | int interval_us); 38 | 39 | private: 40 | const int width_; 41 | const int height_; 42 | const int interval_us_; 43 | 44 | webrtc::VideoRotation rotation_ = webrtc::kVideoRotation_0; 45 | int64_t next_timestamp_us_; 46 | }; 47 | 48 | } // namespace cricket 49 | 50 | #endif // MEDIA_BASE_FAKE_FRAME_SOURCE_H_ 51 | -------------------------------------------------------------------------------- /deps/libwebrtc/test/frame_utils.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved. 3 | * 4 | * Use of this source code is governed by a BSD-style license 5 | * that can be found in the LICENSE file in the root of the source 6 | * tree. An additional intellectual property rights grant can be found 7 | * in the file PATENTS. All contributing project authors may 8 | * be found in the AUTHORS file in the root of the source tree. 9 | */ 10 | #ifndef TEST_FRAME_UTILS_H_ 11 | #define TEST_FRAME_UTILS_H_ 12 | 13 | #include 14 | 15 | #include "api/scoped_refptr.h" 16 | 17 | namespace webrtc { 18 | class I420Buffer; 19 | class VideoFrame; 20 | class VideoFrameBuffer; 21 | namespace test { 22 | 23 | bool EqualPlane(const uint8_t* data1, 24 | const uint8_t* data2, 25 | int stride1, 26 | int stride2, 27 | int width, 28 | int height); 29 | 30 | static inline bool EqualPlane(const uint8_t* data1, 31 | const uint8_t* data2, 32 | int stride, 33 | int width, 34 | int height) { 35 | return EqualPlane(data1, data2, stride, stride, width, height); 36 | } 37 | 38 | bool FramesEqual(const webrtc::VideoFrame& f1, const webrtc::VideoFrame& f2); 39 | 40 | bool FrameBufsEqual(const rtc::scoped_refptr& f1, 41 | const rtc::scoped_refptr& f2); 42 | 43 | rtc::scoped_refptr ReadI420Buffer(int width, int height, FILE*); 44 | 45 | } // namespace test 46 | } // namespace webrtc 47 | 48 | #endif // TEST_FRAME_UTILS_H_ 49 | -------------------------------------------------------------------------------- /deps/libwebrtc/test/test_video_capturer.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved. 3 | * 4 | * Use of this source code is governed by a BSD-style license 5 | * that can be found in the LICENSE file in the root of the source 6 | * tree. An additional intellectual property rights grant can be found 7 | * in the file PATENTS. All contributing project authors may 8 | * be found in the AUTHORS file in the root of the source tree. 9 | */ 10 | #ifndef TEST_TEST_VIDEO_CAPTURER_H_ 11 | #define TEST_TEST_VIDEO_CAPTURER_H_ 12 | 13 | #include 14 | 15 | #include 16 | 17 | #include "api/video/video_frame.h" 18 | #include "api/video/video_source_interface.h" 19 | #include "media/base/video_adapter.h" 20 | #include "media/base/video_broadcaster.h" 21 | #include "rtc_base/synchronization/mutex.h" 22 | 23 | namespace webrtc { 24 | namespace test { 25 | 26 | class TestVideoCapturer : public rtc::VideoSourceInterface { 27 | public: 28 | class FramePreprocessor { 29 | public: 30 | virtual ~FramePreprocessor() = default; 31 | 32 | virtual VideoFrame Preprocess(const VideoFrame& frame) = 0; 33 | }; 34 | 35 | ~TestVideoCapturer() override; 36 | 37 | void AddOrUpdateSink(rtc::VideoSinkInterface* sink, 38 | const rtc::VideoSinkWants& wants) override; 39 | void RemoveSink(rtc::VideoSinkInterface* sink) override; 40 | void SetFramePreprocessor(std::unique_ptr preprocessor) { 41 | MutexLock lock(&lock_); 42 | preprocessor_ = std::move(preprocessor); 43 | } 44 | 45 | protected: 46 | void OnFrame(const VideoFrame& frame); 47 | rtc::VideoSinkWants GetSinkWants(); 48 | 49 | private: 50 | void UpdateVideoAdapter(); 51 | VideoFrame MaybePreprocess(const VideoFrame& frame); 52 | 53 | Mutex lock_; 54 | std::unique_ptr preprocessor_ RTC_GUARDED_BY(lock_); 55 | rtc::VideoBroadcaster broadcaster_; 56 | cricket::VideoAdapter video_adapter_; 57 | }; 58 | } // namespace test 59 | } // namespace webrtc 60 | 61 | #endif // TEST_TEST_VIDEO_CAPTURER_H_ 62 | -------------------------------------------------------------------------------- /deps/libwebrtc/test/testsupport/ios_file_utils.mm: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 The WebRTC Project Authors. All rights reserved. 3 | * 4 | * Use of this source code is governed by a BSD-style license 5 | * that can be found in the LICENSE file in the root of the source 6 | * tree. An additional intellectual property rights grant can be found 7 | * in the file PATENTS. All contributing project authors may 8 | * be found in the AUTHORS file in the root of the source tree. 9 | */ 10 | 11 | #if defined(WEBRTC_IOS) 12 | 13 | #import 14 | #include 15 | 16 | #import "sdk/objc/helpers/NSString+StdString.h" 17 | 18 | #include "rtc_base/checks.h" 19 | 20 | namespace webrtc { 21 | namespace test { 22 | 23 | // For iOS, resource files are added to the application bundle in the root 24 | // and not in separate folders as is the case for other platforms. This method 25 | // therefore removes any prepended folders and uses only the actual file name. 26 | std::string IOSResourcePath(std::string name, std::string extension) { 27 | @autoreleasepool { 28 | NSString* path = [NSString stringForStdString:name]; 29 | NSString* fileName = path.lastPathComponent; 30 | NSString* fileType = [NSString stringForStdString:extension]; 31 | // Get full pathname for the resource identified by the name and extension. 32 | NSString* pathString = [[NSBundle mainBundle] pathForResource:fileName 33 | ofType:fileType]; 34 | return [NSString stdStringForString:pathString]; 35 | } 36 | } 37 | 38 | std::string IOSRootPath() { 39 | @autoreleasepool { 40 | NSBundle* mainBundle = [NSBundle mainBundle]; 41 | return [NSString stdStringForString:mainBundle.bundlePath] + "/"; 42 | } 43 | } 44 | 45 | // For iOS, we don't have access to the output directory. Return the path to the 46 | // temporary directory instead. This is mostly used by tests that need to write 47 | // output files to disk. 48 | std::string IOSOutputPath() { 49 | @autoreleasepool { 50 | NSString* tempDir = NSTemporaryDirectory(); 51 | if (tempDir == nil) 52 | tempDir = @"/tmp"; 53 | return [NSString stdStringForString:tempDir]; 54 | } 55 | } 56 | 57 | } // namespace test 58 | } // namespace webrtc 59 | 60 | #endif // defined(WEBRTC_IOS) 61 | -------------------------------------------------------------------------------- /deps/libwebrtc/test/testsupport/file_utils_override.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2018 The WebRTC project authors. All Rights Reserved. 3 | * 4 | * Use of this source code is governed by a BSD-style license 5 | * that can be found in the LICENSE file in the root of the source 6 | * tree. An additional intellectual property rights grant can be found 7 | * in the file PATENTS. All contributing project authors may 8 | * be found in the AUTHORS file in the root of the source tree. 9 | */ 10 | 11 | #include 12 | 13 | #ifndef TEST_TESTSUPPORT_FILE_UTILS_OVERRIDE_H_ 14 | #define TEST_TESTSUPPORT_FILE_UTILS_OVERRIDE_H_ 15 | 16 | namespace webrtc { 17 | namespace test { 18 | namespace internal { 19 | 20 | // Returns the absolute path to the output directory where log files and other 21 | // test artifacts should be put. The output directory is generally a directory 22 | // named "out" at the project root. This root is assumed to be two levels above 23 | // where the test binary is located; this is because tests execute in a dir 24 | // out/Whatever relative to the project root. This convention is also followed 25 | // in Chromium. 26 | // 27 | // The exception is Android where we use /sdcard/ instead. 28 | // 29 | // If symbolic links occur in the path they will be resolved and the actual 30 | // directory will be returned. 31 | // 32 | // Returns the path WITH a trailing path delimiter. If the project root is not 33 | // found, the current working directory ("./") is returned as a fallback. 34 | std::string OutputPath(); 35 | 36 | // Gets the current working directory for the executing program. 37 | // Returns "./" if for some reason it is not possible to find the working 38 | // directory. 39 | std::string WorkingDir(); 40 | 41 | // Returns a full path to a resource file in the resources_dir dir. 42 | // 43 | // Arguments: 44 | // name - Name of the resource file. If a plain filename (no directory path) 45 | // is supplied, the file is assumed to be located in resources/ 46 | // If a directory path is prepended to the filename, a subdirectory 47 | // hierarchy reflecting that path is assumed to be present. 48 | // extension - File extension, without the dot, i.e. "bmp" or "yuv". 49 | std::string ResourcePath(const std::string& name, const std::string& extension); 50 | 51 | } // namespace internal 52 | } // namespace test 53 | } // namespace webrtc 54 | 55 | #endif // TEST_TESTSUPPORT_FILE_UTILS_OVERRIDE_H_ 56 | -------------------------------------------------------------------------------- /.clang-format: -------------------------------------------------------------------------------- 1 | Language: Cpp 2 | AccessModifierOffset: -2 3 | AlignAfterOpenBracket: AlwaysBreak 4 | AlignConsecutiveAssignments: true 5 | AlignConsecutiveDeclarations: false 6 | AlignEscapedNewlinesLeft: false 7 | AlignOperands: true 8 | AlignTrailingComments: true 9 | AllowAllParametersOfDeclarationOnNextLine: true 10 | AllowShortBlocksOnASingleLine: false 11 | AllowShortCaseLabelsOnASingleLine: false 12 | AllowShortFunctionsOnASingleLine: false 13 | AllowShortIfStatementsOnASingleLine: false 14 | AllowShortLoopsOnASingleLine: false 15 | AlwaysBreakAfterReturnType: None 16 | AlwaysBreakBeforeMultilineStrings: true 17 | AlwaysBreakTemplateDeclarations: true 18 | BinPackArguments: false 19 | BinPackParameters: false 20 | BreakBeforeBraces: Custom 21 | BraceWrapping: 22 | AfterClass: true 23 | AfterControlStatement: true 24 | AfterEnum: true 25 | AfterFunction: true 26 | AfterNamespace: true 27 | AfterStruct: true 28 | AfterUnion: true 29 | BeforeCatch: true 30 | BeforeElse: true 31 | IndentBraces: false 32 | BreakBeforeBinaryOperators: None 33 | BreakBeforeInheritanceComma: false 34 | BreakBeforeTernaryOperators: true 35 | BreakConstructorInitializersBeforeComma: false 36 | BreakStringLiterals: false 37 | ColumnLimit: 100 38 | CommentPragmas: 'NOLINT' 39 | ConstructorInitializerAllOnOneLineOrOnePerLine: false 40 | ConstructorInitializerIndentWidth: 2 41 | ContinuationIndentWidth: 2 42 | Cpp11BracedListStyle: false 43 | DerivePointerAlignment: false 44 | DisableFormat: false 45 | ExperimentalAutoDetectBinPacking: true 46 | FixNamespaceComments: true 47 | IncludeCategories: 48 | - Regex: '^"(json|sdptransform)(.|/)' 49 | Priority: 2 50 | - Regex: '^"(api|media_base|rtc_base)(.|/)' 51 | Priority: 3 52 | - Regex: '"*"' 53 | Priority: 1 54 | - Regex: '<*>' 55 | Priority: 4 56 | IncludeIsMainRegex: '$' 57 | IndentCaseLabels: true 58 | IndentWidth: 2 59 | IndentWrappedFunctionNames: false 60 | KeepEmptyLinesAtTheStartOfBlocks: false 61 | MaxEmptyLinesToKeep: 1 62 | NamespaceIndentation: Inner 63 | PenaltyBreakBeforeFirstCallParameter: 5 64 | PenaltyBreakComment: 100 65 | PenaltyBreakFirstLessLess: 200 66 | PenaltyBreakString: 20 67 | PenaltyExcessCharacter: 10 68 | PenaltyReturnTypeOnItsOwnLine: 1000 69 | PointerAlignment: Left 70 | ReflowComments: true 71 | SortIncludes: true 72 | SpaceAfterCStyleCast: false 73 | SpaceAfterTemplateKeyword: false 74 | SpaceBeforeAssignmentOperators: true 75 | SpaceBeforeParens: ControlStatements 76 | SpaceInEmptyParentheses: false 77 | SpacesBeforeTrailingComments: 1 78 | SpacesInAngles: false 79 | SpacesInCStyleCastParentheses: false 80 | SpacesInContainerLiterals: true 81 | SpacesInParentheses: false 82 | SpacesInSquareBrackets: false 83 | Standard: Cpp11 84 | TabWidth: 2 85 | UseTab: ForIndentation 86 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.8) 2 | 3 | project(broadcaster LANGUAGES CXX) 4 | 5 | # Set version number. 6 | set(broadcaster_VERSION_MAJOR 0) 7 | set(broadcaster_VERSION_MINOR 1) 8 | 9 | # C++ standard requirements. 10 | set(CMAKE_CXX_STANDARD 14) 11 | set(CMAKE_CXX_STANDARD_REQUIRED ON) 12 | 13 | message("\n=========== mediasoup-broadcaster-demo Build Configuration ===========\n") 14 | message(STATUS "LIBWEBRTC_INCLUDE_PATH : " ${LIBWEBRTC_INCLUDE_PATH}) 15 | message(STATUS "LIBWEBRTC_BINARY_PATH : " ${LIBWEBRTC_BINARY_PATH}) 16 | message(STATUS "OPENSSL_INCLUDE_DIR : " ${OPENSSL_INCLUDE_DIR}) 17 | message(STATUS "CMAKE_USE_OPENSSL : " ${CMAKE_USE_OPENSSL}) 18 | message("") 19 | 20 | # Add some compile flags to our source files. 21 | set_source_files_properties(${SOURCE_FILES} 22 | PROPERTIES COMPILE_FLAGS -Wall -Wextra -Wpedantic) 23 | 24 | # Create target. 25 | add_executable(${PROJECT_NAME} ${SOURCES}) 26 | 27 | # Extra libs required in order to use Apple media devices. 28 | if(APPLE) 29 | find_library(APPLICATION_SERVICES ApplicationServices) 30 | find_library(AUDIO_TOOLBOX AudioToolbox) 31 | find_library(AV_FOUNDATION AVFoundation) 32 | find_library(CORE_AUDIO CoreAudio) 33 | find_library(CORE_FOUNDATION Foundation) 34 | find_library(CORE_MEDIA CoreMedia) 35 | find_library(CORE_VIDEO CoreVideo) 36 | find_library(CORE_SERVICES CoreServices) 37 | 38 | target_link_libraries(${PROJECT_NAME} PUBLIC 39 | ${APPLICATION_SERVICES} 40 | ${AUDIO_TOOLBOX} 41 | ${AV_FOUNDATION} 42 | ${CORE_AUDIO} 43 | ${CORE_FOUNDATION} 44 | ${CORE_MEDIA} 45 | ${CORE_VIDEO} 46 | ${CORE_SERVICES} 47 | ) 48 | endif(APPLE) 49 | 50 | include(FetchContent) 51 | 52 | message(STATUS "\nFetching mediasoupclient...\n") 53 | FetchContent_Declare( 54 | mediasoupclient 55 | GIT_REPOSITORY https://github.com/versatica/libmediasoupclient.git 56 | GIT_TAG v3 57 | ) 58 | FetchContent_MakeAvailable(mediasoupclient) 59 | 60 | message(STATUS "Fetching cpr...\n") 61 | FetchContent_Declare( 62 | cpr 63 | GIT_REPOSITORY https://github.com/whoshuu/cpr 64 | GIT_TAG 1.4.0 65 | ) 66 | FetchContent_MakeAvailable(cpr) 67 | 68 | target_sources(${PROJECT_NAME} PRIVATE 69 | src/Broadcaster.cpp 70 | src/main.cpp 71 | src/MediaStreamTrackFactory.cpp 72 | ) 73 | 74 | # Private (implementation) header files. 75 | target_include_directories(${PROJECT_NAME} PRIVATE ${PROJECT_SOURCE_DIR}/include) 76 | 77 | # Source Dependencies. 78 | add_subdirectory(deps/libwebrtc "${CMAKE_CURRENT_BINARY_DIR}/libwebrtc") 79 | 80 | # Public (interface) headers from dependencies. 81 | target_include_directories(${PROJECT_NAME} PUBLIC 82 | ${mediasoupclient_SOURCE_DIR}/include 83 | ${cpr_SOURCE_DIR}/include 84 | "${PROJECT_SOURCE_DIR}/deps/libwebrtc" 85 | ) 86 | 87 | # Public (interface) dependencies. 88 | target_link_libraries(${PROJECT_NAME} PUBLIC 89 | ${LIBWEBRTC_BINARY_PATH}/libwebrtc${CMAKE_STATIC_LIBRARY_SUFFIX} 90 | cpr 91 | mediasoupclient 92 | webrtc_broadcaster 93 | ) 94 | 95 | -------------------------------------------------------------------------------- /deps/libwebrtc/api/test/create_frame_generator.cc: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2019 The WebRTC project authors. All Rights Reserved. 3 | * 4 | * Use of this source code is governed by a BSD-style license 5 | * that can be found in the LICENSE file in the root of the source 6 | * tree. An additional intellectual property rights grant can be found 7 | * in the file PATENTS. All contributing project authors may 8 | * be found in the AUTHORS file in the root of the source tree. 9 | */ 10 | 11 | #include "api/test/create_frame_generator.h" 12 | 13 | #include 14 | #include 15 | 16 | #include "rtc_base/checks.h" 17 | #include "test/frame_generator.h" 18 | #include "test/testsupport/ivf_video_frame_generator.h" 19 | 20 | namespace webrtc { 21 | namespace test { 22 | 23 | std::unique_ptr CreateSquareFrameGenerator( 24 | int width, 25 | int height, 26 | absl::optional type, 27 | absl::optional num_squares) { 28 | return std::make_unique( 29 | width, height, type.value_or(FrameGeneratorInterface::OutputType::kI420), 30 | num_squares.value_or(10)); 31 | } 32 | 33 | std::unique_ptr CreateFromYuvFileFrameGenerator( 34 | std::vector filenames, 35 | size_t width, 36 | size_t height, 37 | int frame_repeat_count) { 38 | RTC_DCHECK(!filenames.empty()); 39 | std::vector files; 40 | for (const std::string& filename : filenames) { 41 | FILE* file = fopen(filename.c_str(), "rb"); 42 | RTC_DCHECK(file != nullptr) << "Failed to open: '" << filename << "'\n"; 43 | files.push_back(file); 44 | } 45 | 46 | return std::make_unique(files, width, height, 47 | frame_repeat_count); 48 | } 49 | 50 | std::unique_ptr CreateFromIvfFileFrameGenerator( 51 | std::string filename) { 52 | return std::make_unique(std::move(filename)); 53 | } 54 | 55 | std::unique_ptr 56 | CreateScrollingInputFromYuvFilesFrameGenerator( 57 | Clock* clock, 58 | std::vector filenames, 59 | size_t source_width, 60 | size_t source_height, 61 | size_t target_width, 62 | size_t target_height, 63 | int64_t scroll_time_ms, 64 | int64_t pause_time_ms) { 65 | RTC_DCHECK(!filenames.empty()); 66 | std::vector files; 67 | for (const std::string& filename : filenames) { 68 | FILE* file = fopen(filename.c_str(), "rb"); 69 | RTC_DCHECK(file != nullptr); 70 | files.push_back(file); 71 | } 72 | 73 | return std::make_unique( 74 | clock, files, source_width, source_height, target_width, target_height, 75 | scroll_time_ms, pause_time_ms); 76 | } 77 | 78 | std::unique_ptr 79 | CreateSlideFrameGenerator(int width, int height, int frame_repeat_count) { 80 | return std::make_unique(width, height, frame_repeat_count); 81 | } 82 | 83 | } // namespace test 84 | } // namespace webrtc 85 | -------------------------------------------------------------------------------- /deps/libwebrtc/api/test/create_frame_generator.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2019 The WebRTC project authors. All Rights Reserved. 3 | * 4 | * Use of this source code is governed by a BSD-style license 5 | * that can be found in the LICENSE file in the root of the source 6 | * tree. An additional intellectual property rights grant can be found 7 | * in the file PATENTS. All contributing project authors may 8 | * be found in the AUTHORS file in the root of the source tree. 9 | */ 10 | 11 | #ifndef API_TEST_CREATE_FRAME_GENERATOR_H_ 12 | #define API_TEST_CREATE_FRAME_GENERATOR_H_ 13 | 14 | #include 15 | #include 16 | #include 17 | 18 | #include "absl/types/optional.h" 19 | #include "api/test/frame_generator_interface.h" 20 | #include "system_wrappers/include/clock.h" 21 | 22 | namespace webrtc { 23 | namespace test { 24 | 25 | // Creates a frame generator that produces frames with small squares that 26 | // move randomly towards the lower right corner. 27 | // `type` has the default value FrameGeneratorInterface::OutputType::I420. 28 | // `num_squares` has the default value 10. 29 | std::unique_ptr CreateSquareFrameGenerator( 30 | int width, 31 | int height, 32 | absl::optional type, 33 | absl::optional num_squares); 34 | 35 | // Creates a frame generator that repeatedly plays a set of yuv files. 36 | // The frame_repeat_count determines how many times each frame is shown, 37 | // with 1 = show each frame once, etc. 38 | std::unique_ptr CreateFromYuvFileFrameGenerator( 39 | std::vector filenames, 40 | size_t width, 41 | size_t height, 42 | int frame_repeat_count); 43 | 44 | // Creates a frame generator that repeatedly plays an ivf file. 45 | std::unique_ptr CreateFromIvfFileFrameGenerator( 46 | std::string filename); 47 | 48 | // Creates a frame generator which takes a set of yuv files (wrapping a 49 | // frame generator created by CreateFromYuvFile() above), but outputs frames 50 | // that have been cropped to specified resolution: source_width/source_height 51 | // is the size of the source images, target_width/target_height is the size of 52 | // the cropped output. For each source image read, the cropped viewport will 53 | // be scrolled top to bottom/left to right for scroll_tim_ms milliseconds. 54 | // After that the image will stay in place for pause_time_ms milliseconds, 55 | // and then this will be repeated with the next file from the input set. 56 | std::unique_ptr 57 | CreateScrollingInputFromYuvFilesFrameGenerator( 58 | Clock* clock, 59 | std::vector filenames, 60 | size_t source_width, 61 | size_t source_height, 62 | size_t target_width, 63 | size_t target_height, 64 | int64_t scroll_time_ms, 65 | int64_t pause_time_ms); 66 | 67 | // Creates a frame generator that produces randomly generated slides. It fills 68 | // the frames with randomly sized and colored squares. 69 | // `frame_repeat_count` determines how many times each slide is shown. 70 | std::unique_ptr 71 | CreateSlideFrameGenerator(int width, int height, int frame_repeat_count); 72 | 73 | } // namespace test 74 | } // namespace webrtc 75 | 76 | #endif // API_TEST_CREATE_FRAME_GENERATOR_H_ 77 | -------------------------------------------------------------------------------- /deps/libwebrtc/rtc_base/task_queue_for_test.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 The WebRTC Project Authors. All rights reserved. 3 | * 4 | * Use of this source code is governed by a BSD-style license 5 | * that can be found in the LICENSE file in the root of the source 6 | * tree. An additional intellectual property rights grant can be found 7 | * in the file PATENTS. All contributing project authors may 8 | * be found in the AUTHORS file in the root of the source tree. 9 | */ 10 | 11 | #ifndef RTC_BASE_TASK_QUEUE_FOR_TEST_H_ 12 | #define RTC_BASE_TASK_QUEUE_FOR_TEST_H_ 13 | 14 | #include 15 | 16 | #include "absl/strings/string_view.h" 17 | #include "api/task_queue/task_queue_base.h" 18 | #include "rtc_base/checks.h" 19 | #include "rtc_base/event.h" 20 | #include "rtc_base/location.h" 21 | #include "rtc_base/task_queue.h" 22 | #include "rtc_base/task_utils/to_queued_task.h" 23 | #include "rtc_base/thread_annotations.h" 24 | 25 | namespace webrtc { 26 | 27 | template 28 | void SendTask(rtc::Location loc, TaskQueueBase* task_queue, Closure&& task) { 29 | RTC_CHECK(!task_queue->IsCurrent()) 30 | << "Called SendTask to a queue from the same queue at " << loc.ToString(); 31 | rtc::Event event; 32 | task_queue->PostTask( 33 | ToQueuedTask(std::forward(task), [&event] { event.Set(); })); 34 | RTC_CHECK(event.Wait(/*give_up_after_ms=*/rtc::Event::kForever, 35 | /*warn_after_ms=*/10'000)) 36 | << "Waited too long at " << loc.ToString(); 37 | } 38 | 39 | class RTC_LOCKABLE TaskQueueForTest : public rtc::TaskQueue { 40 | public: 41 | using rtc::TaskQueue::TaskQueue; 42 | explicit TaskQueueForTest(absl::string_view name = "TestQueue", 43 | Priority priority = Priority::NORMAL); 44 | TaskQueueForTest(const TaskQueueForTest&) = delete; 45 | TaskQueueForTest& operator=(const TaskQueueForTest&) = delete; 46 | ~TaskQueueForTest() = default; 47 | 48 | // A convenience, test-only method that blocks the current thread while 49 | // a task executes on the task queue. 50 | // This variant is specifically for posting custom QueuedTask derived 51 | // implementations that tests do not want to pass ownership of over to the 52 | // task queue (i.e. the Run() method always returns `false`.). 53 | template 54 | void SendTask(Closure* task) { 55 | RTC_CHECK(!IsCurrent()); 56 | rtc::Event event; 57 | PostTask(ToQueuedTask( 58 | [&task] { RTC_CHECK_EQ(false, static_cast(task)->Run()); }, 59 | [&event] { event.Set(); })); 60 | event.Wait(rtc::Event::kForever); 61 | } 62 | 63 | // A convenience, test-only method that blocks the current thread while 64 | // a task executes on the task queue. 65 | template 66 | void SendTask(Closure&& task, rtc::Location loc) { 67 | ::webrtc::SendTask(loc, Get(), std::forward(task)); 68 | } 69 | 70 | // Wait for the completion of all tasks posted prior to the 71 | // WaitForPreviouslyPostedTasks() call. 72 | void WaitForPreviouslyPostedTasks() { 73 | // Post an empty task on the queue and wait for it to finish, to ensure 74 | // that all already posted tasks on the queue get executed. 75 | SendTask([]() {}, RTC_FROM_HERE); 76 | } 77 | }; 78 | 79 | } // namespace webrtc 80 | 81 | #endif // RTC_BASE_TASK_QUEUE_FOR_TEST_H_ 82 | -------------------------------------------------------------------------------- /deps/libwebrtc/media/base/fake_frame_source.cc: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2018 The WebRTC project authors. All Rights Reserved. 3 | * 4 | * Use of this source code is governed by a BSD-style license 5 | * that can be found in the LICENSE file in the root of the source 6 | * tree. An additional intellectual property rights grant can be found 7 | * in the file PATENTS. All contributing project authors may 8 | * be found in the AUTHORS file in the root of the source tree. 9 | */ 10 | 11 | #include "media/base/fake_frame_source.h" 12 | 13 | #include "api/scoped_refptr.h" 14 | #include "api/video/i420_buffer.h" 15 | #include "api/video/video_frame_buffer.h" 16 | #include "rtc_base/checks.h" 17 | #include "rtc_base/time_utils.h" 18 | 19 | namespace cricket { 20 | 21 | FakeFrameSource::FakeFrameSource(int width, 22 | int height, 23 | int interval_us, 24 | int64_t timestamp_offset_us) 25 | : width_(width), 26 | height_(height), 27 | interval_us_(interval_us), 28 | next_timestamp_us_(timestamp_offset_us) { 29 | RTC_CHECK_GT(width_, 0); 30 | RTC_CHECK_GT(height_, 0); 31 | RTC_CHECK_GT(interval_us_, 0); 32 | RTC_CHECK_GE(next_timestamp_us_, 0); 33 | } 34 | 35 | FakeFrameSource::FakeFrameSource(int width, int height, int interval_us) 36 | : FakeFrameSource(width, height, interval_us, rtc::TimeMicros()) {} 37 | 38 | webrtc::VideoRotation FakeFrameSource::GetRotation() const { 39 | return rotation_; 40 | } 41 | 42 | void FakeFrameSource::SetRotation(webrtc::VideoRotation rotation) { 43 | rotation_ = rotation; 44 | } 45 | 46 | webrtc::VideoFrame FakeFrameSource::GetFrameRotationApplied() { 47 | switch (rotation_) { 48 | case webrtc::kVideoRotation_0: 49 | case webrtc::kVideoRotation_180: 50 | return GetFrame(width_, height_, webrtc::kVideoRotation_0, interval_us_); 51 | case webrtc::kVideoRotation_90: 52 | case webrtc::kVideoRotation_270: 53 | return GetFrame(height_, width_, webrtc::kVideoRotation_0, interval_us_); 54 | } 55 | RTC_NOTREACHED() << "Invalid rotation value: " << static_cast(rotation_); 56 | // Without this return, the Windows Visual Studio compiler complains 57 | // "not all control paths return a value". 58 | return GetFrame(); 59 | } 60 | 61 | webrtc::VideoFrame FakeFrameSource::GetFrame() { 62 | return GetFrame(width_, height_, rotation_, interval_us_); 63 | } 64 | 65 | webrtc::VideoFrame FakeFrameSource::GetFrame(int width, 66 | int height, 67 | webrtc::VideoRotation rotation, 68 | int interval_us) { 69 | RTC_CHECK_GT(width, 0); 70 | RTC_CHECK_GT(height, 0); 71 | RTC_CHECK_GT(interval_us, 0); 72 | 73 | rtc::scoped_refptr buffer( 74 | webrtc::I420Buffer::Create(width, height)); 75 | 76 | buffer->InitializeData(); 77 | webrtc::VideoFrame frame = webrtc::VideoFrame::Builder() 78 | .set_video_frame_buffer(buffer) 79 | .set_rotation(rotation) 80 | .set_timestamp_us(next_timestamp_us_) 81 | .build(); 82 | 83 | next_timestamp_us_ += interval_us; 84 | return frame; 85 | } 86 | 87 | } // namespace cricket 88 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # mediasoup broadcaster demo (libmediasoupclient v3) 2 | 3 | [libmediasoupclient][libmediasoupclient] based application that produces artificial sound and video to the specified room in [mediasoup-demo] application. The video consists of some colored rectangles moving towards the lower-right corner of the image. Credit for the artificial media creation goes to the WEBRTC team ([LICENSE](https://webrtc.googlesource.com/src/+/refs/heads/master/LICENSE)). 4 | 5 | 6 | ## Resources 7 | 8 | * mediasoup website and documentation: [mediasoup.org](https://mediasoup.org) 9 | * mediasoup support forum: [mediasoup.discourse.group](https://mediasoup.discourse.group) 10 | 11 | 12 | ## Usage 13 | 14 | Once installed (see **Installation** below): 15 | 16 | ```bash 17 | SERVER_URL=https://my.mediasoup-demo.org:4443 ROOM_ID=broadcaster build/broadcaster 18 | ``` 19 | 20 | Environment variables: 21 | 22 | * `SERVER_URL`: The URL of the mediasoup-demo HTTP API server (required). 23 | * `ROOM_ID`: Room id (required). 24 | * `USE_SIMULCAST`: If "false" no simulcast will be used (defaults to "true"). 25 | * `ENABLE_AUDIO`: If "false" no audio Producer is created (defaults to "true"). 26 | * `WEBRTC_DEBUG`: Enable libwebrtc logging. Can be "info", "warn" or "error" (optional). 27 | * `VERIFY_SSL`: Verifies server side SSL certificate (defaults to "true") (optional). 28 | 29 | ## Dependencies 30 | 31 | * [libmediasoupclient][libmediasoupclient] (already included in the repository) 32 | * [cpr][cpr] (already included in the repository) 33 | * OpenSSL (must be installed in the system including its headers) 34 | 35 | 36 | ## Installation 37 | 38 | ```bash 39 | git clone https://github.com/versatica/mediasoup-broadcaster-demo.git 40 | 41 | cmake . -Bbuild \ 42 | -DLIBWEBRTC_INCLUDE_PATH:PATH=${PATH_TO_LIBWEBRTC_SOURCES} \ 43 | -DLIBWEBRTC_BINARY_PATH:PATH=${PATH_TO_LIBWEBRTC_BINARY} \ 44 | -DOPENSSL_INCLUDE_DIR:PATH=${PATH_TO_OPENSSL_HEADERS} \ 45 | -DCMAKE_USE_OPENSSL=ON 46 | 47 | make -C build 48 | ``` 49 | 50 | #### Linkage Considerations (1) 51 | 52 | ``` 53 | [ 65%] Linking C shared library ../../../../lib/libcurl.dylib ld: cannot link directly with dylib/framework, your binary is not an allowed client of /usr/lib/libcrypto.dylib for architecture x86_64 clang: error: linker command failed with exit code 1 (use -v to see invocation) 54 | make[2]: *** [lib/libcurl.dylib] Error 1 make[1]: *** [cpr/opt/curl/lib/CMakeFiles/libcurl.dir/all] Error 2 55 | make: *** [all] Error 2 56 | ``` 57 | 58 | The following error may happen if the linker is not able to find the openssl crypto library. In order to avoid this error, specify the crypto library path along with the openssl root directory using the `OPENSSL_CRYPTO_LIBRARY` flag. Ie: 59 | 60 | ``` 61 | -DOPENSSL_ROOT_DIR=/usr/local/Cellar/openssl@1.1/1.1.1h \ 62 | -DOPENSSL_CRYPTO_LIBRARY=/usr/local/Cellar/openssl@1.1/1.1.1h/lib/libcrypto.1.1.dylib 63 | ``` 64 | 65 | 66 | 67 | ## License 68 | 69 | Some files contain specific license agreements, written in the beginning of the respective files. 70 | 71 | *NOTE 1:* `PATH_TO_OPENSSL_HEADERS` is `/usr/local/opt/openssl/include` if you install OpenSSL using Homebrew in OSX. 72 | 73 | [mediasoup-demo]: https://github.com/versatica/mediasoup-demo 74 | [libmediasoupclient]: https://github.com/versatica/libmediasoupclient 75 | [cpr]: https://github.com/whoshuu/cpr 76 | -------------------------------------------------------------------------------- /deps/libwebrtc/pc/test/frame_generator_capturer_video_track_source.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 The WebRTC project authors. All Rights Reserved. 3 | * 4 | * Use of this source code is governed by a BSD-style license 5 | * that can be found in the LICENSE file in the root of the source 6 | * tree. An additional intellectual property rights grant can be found 7 | * in the file PATENTS. All contributing project authors may 8 | * be found in the AUTHORS file in the root of the source tree. 9 | */ 10 | 11 | #ifndef PC_TEST_FRAME_GENERATOR_CAPTURER_VIDEO_TRACK_SOURCE_H_ 12 | #define PC_TEST_FRAME_GENERATOR_CAPTURER_VIDEO_TRACK_SOURCE_H_ 13 | 14 | #include 15 | #include 16 | 17 | #include "api/task_queue/default_task_queue_factory.h" 18 | #include "api/task_queue/task_queue_factory.h" 19 | #include "api/test/create_frame_generator.h" 20 | #include "pc/video_track_source.h" 21 | #include "test/frame_generator_capturer.h" 22 | 23 | namespace webrtc { 24 | 25 | // Implements a VideoTrackSourceInterface to be used for creating VideoTracks. 26 | // The video source is generated using a FrameGeneratorCapturer, specifically 27 | // a SquareGenerator that generates frames with randomly sized and colored 28 | // squares. 29 | class FrameGeneratorCapturerVideoTrackSource : public VideoTrackSource { 30 | public: 31 | static const int kDefaultFramesPerSecond = 30; 32 | static const int kDefaultWidth = 640; 33 | static const int kDefaultHeight = 480; 34 | static const int kNumSquaresGenerated = 50; 35 | 36 | struct Config { 37 | int frames_per_second = kDefaultFramesPerSecond; 38 | int width = kDefaultWidth; 39 | int height = kDefaultHeight; 40 | int num_squares_generated = 50; 41 | }; 42 | 43 | FrameGeneratorCapturerVideoTrackSource(Config config, 44 | Clock* clock, 45 | bool is_screencast) 46 | : VideoTrackSource(false /* remote */), 47 | task_queue_factory_(CreateDefaultTaskQueueFactory()), 48 | is_screencast_(is_screencast) { 49 | video_capturer_ = std::make_unique( 50 | clock, 51 | test::CreateSquareFrameGenerator(config.width, config.height, 52 | absl::nullopt, 53 | config.num_squares_generated), 54 | config.frames_per_second, *task_queue_factory_); 55 | video_capturer_->Init(); 56 | } 57 | 58 | FrameGeneratorCapturerVideoTrackSource( 59 | std::unique_ptr video_capturer, 60 | bool is_screencast) 61 | : VideoTrackSource(false /* remote */), 62 | video_capturer_(std::move(video_capturer)), 63 | is_screencast_(is_screencast) {} 64 | 65 | ~FrameGeneratorCapturerVideoTrackSource() = default; 66 | 67 | void Start() { SetState(kLive); } 68 | 69 | void Stop() { SetState(kMuted); } 70 | 71 | bool is_screencast() const override { return is_screencast_; } 72 | 73 | protected: 74 | rtc::VideoSourceInterface* source() override { 75 | return video_capturer_.get(); 76 | } 77 | 78 | private: 79 | const std::unique_ptr task_queue_factory_; 80 | std::unique_ptr video_capturer_; 81 | const bool is_screencast_; 82 | }; 83 | 84 | } // namespace webrtc 85 | 86 | #endif // PC_TEST_FRAME_GENERATOR_CAPTURER_VIDEO_TRACK_SOURCE_H_ 87 | -------------------------------------------------------------------------------- /deps/libwebrtc/test/frame_utils.cc: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved. 3 | * 4 | * Use of this source code is governed by a BSD-style license 5 | * that can be found in the LICENSE file in the root of the source 6 | * tree. An additional intellectual property rights grant can be found 7 | * in the file PATENTS. All contributing project authors may 8 | * be found in the AUTHORS file in the root of the source tree. 9 | */ 10 | 11 | #include "test/frame_utils.h" 12 | 13 | #include 14 | #include 15 | 16 | #include "api/video/i420_buffer.h" 17 | #include "api/video/video_frame.h" 18 | 19 | namespace webrtc { 20 | namespace test { 21 | 22 | bool EqualPlane(const uint8_t* data1, 23 | const uint8_t* data2, 24 | int stride1, 25 | int stride2, 26 | int width, 27 | int height) { 28 | for (int y = 0; y < height; ++y) { 29 | if (memcmp(data1, data2, width) != 0) 30 | return false; 31 | data1 += stride1; 32 | data2 += stride2; 33 | } 34 | return true; 35 | } 36 | 37 | bool FramesEqual(const webrtc::VideoFrame& f1, const webrtc::VideoFrame& f2) { 38 | if (f1.timestamp() != f2.timestamp() || 39 | f1.ntp_time_ms() != f2.ntp_time_ms() || 40 | f1.render_time_ms() != f2.render_time_ms()) { 41 | return false; 42 | } 43 | return FrameBufsEqual(f1.video_frame_buffer(), f2.video_frame_buffer()); 44 | } 45 | 46 | bool FrameBufsEqual(const rtc::scoped_refptr& f1, 47 | const rtc::scoped_refptr& f2) { 48 | if (f1 == f2) { 49 | return true; 50 | } 51 | // Exlude nullptr (except if both are nullptr, as above) 52 | if (!f1 || !f2) { 53 | return false; 54 | } 55 | 56 | if (f1->width() != f2->width() || f1->height() != f2->height() || 57 | f1->type() != f2->type()) { 58 | return false; 59 | } 60 | 61 | rtc::scoped_refptr f1_i420 = f1->ToI420(); 62 | rtc::scoped_refptr f2_i420 = f2->ToI420(); 63 | return EqualPlane(f1_i420->DataY(), f2_i420->DataY(), f1_i420->StrideY(), 64 | f2_i420->StrideY(), f1_i420->width(), f1_i420->height()) && 65 | EqualPlane(f1_i420->DataU(), f2_i420->DataU(), f1_i420->StrideU(), 66 | f2_i420->StrideU(), f1_i420->ChromaWidth(), 67 | f1_i420->ChromaHeight()) && 68 | EqualPlane(f1_i420->DataV(), f2_i420->DataV(), f1_i420->StrideV(), 69 | f2_i420->StrideV(), f1_i420->ChromaWidth(), 70 | f1_i420->ChromaHeight()); 71 | } 72 | 73 | rtc::scoped_refptr ReadI420Buffer(int width, int height, FILE* f) { 74 | int half_width = (width + 1) / 2; 75 | rtc::scoped_refptr buffer( 76 | // Explicit stride, no padding between rows. 77 | I420Buffer::Create(width, height, width, half_width, half_width)); 78 | size_t size_y = static_cast(width) * height; 79 | size_t size_uv = static_cast(half_width) * ((height + 1) / 2); 80 | 81 | if (fread(buffer->MutableDataY(), 1, size_y, f) < size_y) 82 | return nullptr; 83 | if (fread(buffer->MutableDataU(), 1, size_uv, f) < size_uv) 84 | return nullptr; 85 | if (fread(buffer->MutableDataV(), 1, size_uv, f) < size_uv) 86 | return nullptr; 87 | return buffer; 88 | } 89 | 90 | } // namespace test 91 | } // namespace webrtc 92 | -------------------------------------------------------------------------------- /deps/libwebrtc/test/test_video_capturer.cc: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017 The WebRTC project authors. All Rights Reserved. 3 | * 4 | * Use of this source code is governed by a BSD-style license 5 | * that can be found in the LICENSE file in the root of the source 6 | * tree. An additional intellectual property rights grant can be found 7 | * in the file PATENTS. All contributing project authors may 8 | * be found in the AUTHORS file in the root of the source tree. 9 | */ 10 | 11 | #include "test/test_video_capturer.h" 12 | 13 | #include 14 | 15 | #include "api/scoped_refptr.h" 16 | #include "api/video/i420_buffer.h" 17 | #include "api/video/video_frame_buffer.h" 18 | #include "api/video/video_rotation.h" 19 | 20 | namespace webrtc { 21 | namespace test { 22 | TestVideoCapturer::~TestVideoCapturer() = default; 23 | 24 | void TestVideoCapturer::OnFrame(const VideoFrame& original_frame) { 25 | int cropped_width = 0; 26 | int cropped_height = 0; 27 | int out_width = 0; 28 | int out_height = 0; 29 | 30 | VideoFrame frame = MaybePreprocess(original_frame); 31 | 32 | if (!video_adapter_.AdaptFrameResolution( 33 | frame.width(), frame.height(), frame.timestamp_us() * 1000, 34 | &cropped_width, &cropped_height, &out_width, &out_height)) { 35 | // Drop frame in order to respect frame rate constraint. 36 | return; 37 | } 38 | 39 | if (out_height != frame.height() || out_width != frame.width()) { 40 | // Video adapter has requested a down-scale. Allocate a new buffer and 41 | // return scaled version. 42 | // For simplicity, only scale here without cropping. 43 | rtc::scoped_refptr scaled_buffer = 44 | I420Buffer::Create(out_width, out_height); 45 | scaled_buffer->ScaleFrom(*frame.video_frame_buffer()->ToI420()); 46 | VideoFrame::Builder new_frame_builder = 47 | VideoFrame::Builder() 48 | .set_video_frame_buffer(scaled_buffer) 49 | .set_rotation(kVideoRotation_0) 50 | .set_timestamp_us(frame.timestamp_us()) 51 | .set_id(frame.id()); 52 | if (frame.has_update_rect()) { 53 | VideoFrame::UpdateRect new_rect = frame.update_rect().ScaleWithFrame( 54 | frame.width(), frame.height(), 0, 0, frame.width(), frame.height(), 55 | out_width, out_height); 56 | new_frame_builder.set_update_rect(new_rect); 57 | } 58 | broadcaster_.OnFrame(new_frame_builder.build()); 59 | 60 | } else { 61 | // No adaptations needed, just return the frame as is. 62 | broadcaster_.OnFrame(frame); 63 | } 64 | } 65 | 66 | rtc::VideoSinkWants TestVideoCapturer::GetSinkWants() { 67 | return broadcaster_.wants(); 68 | } 69 | 70 | void TestVideoCapturer::AddOrUpdateSink( 71 | rtc::VideoSinkInterface* sink, 72 | const rtc::VideoSinkWants& wants) { 73 | broadcaster_.AddOrUpdateSink(sink, wants); 74 | UpdateVideoAdapter(); 75 | } 76 | 77 | void TestVideoCapturer::RemoveSink(rtc::VideoSinkInterface* sink) { 78 | broadcaster_.RemoveSink(sink); 79 | UpdateVideoAdapter(); 80 | } 81 | 82 | void TestVideoCapturer::UpdateVideoAdapter() { 83 | video_adapter_.OnSinkWants(broadcaster_.wants()); 84 | } 85 | 86 | VideoFrame TestVideoCapturer::MaybePreprocess(const VideoFrame& frame) { 87 | MutexLock lock(&lock_); 88 | if (preprocessor_ != nullptr) { 89 | return preprocessor_->Preprocess(frame); 90 | } else { 91 | return frame; 92 | } 93 | } 94 | 95 | } // namespace test 96 | } // namespace webrtc 97 | -------------------------------------------------------------------------------- /deps/libwebrtc/test/testsupport/ivf_video_frame_generator.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2019 The WebRTC project authors. All Rights Reserved. 3 | * 4 | * Use of this source code is governed by a BSD-style license 5 | * that can be found in the LICENSE file in the root of the source 6 | * tree. An additional intellectual property rights grant can be found 7 | * in the file PATENTS. All contributing project authors may 8 | * be found in the AUTHORS file in the root of the source tree. 9 | */ 10 | 11 | #ifndef TEST_TESTSUPPORT_IVF_VIDEO_FRAME_GENERATOR_H_ 12 | #define TEST_TESTSUPPORT_IVF_VIDEO_FRAME_GENERATOR_H_ 13 | 14 | #include 15 | #include 16 | 17 | #include "absl/types/optional.h" 18 | #include "api/sequence_checker.h" 19 | #include "api/test/frame_generator_interface.h" 20 | #include "api/video/video_codec_type.h" 21 | #include "api/video/video_frame.h" 22 | #include "api/video_codecs/video_decoder.h" 23 | #include "modules/video_coding/utility/ivf_file_reader.h" 24 | #include "rtc_base/event.h" 25 | #include "rtc_base/synchronization/mutex.h" 26 | 27 | namespace webrtc { 28 | namespace test { 29 | 30 | // All methods except constructor must be used from the same thread. 31 | class IvfVideoFrameGenerator : public FrameGeneratorInterface { 32 | public: 33 | explicit IvfVideoFrameGenerator(const std::string& file_name); 34 | ~IvfVideoFrameGenerator() override; 35 | 36 | VideoFrameData NextFrame() override; 37 | void ChangeResolution(size_t width, size_t height) override; 38 | 39 | private: 40 | class DecodedCallback : public DecodedImageCallback { 41 | public: 42 | explicit DecodedCallback(IvfVideoFrameGenerator* reader) 43 | : reader_(reader) {} 44 | 45 | int32_t Decoded(VideoFrame& decoded_image) override; 46 | int32_t Decoded(VideoFrame& decoded_image, int64_t decode_time_ms) override; 47 | void Decoded(VideoFrame& decoded_image, 48 | absl::optional decode_time_ms, 49 | absl::optional qp) override; 50 | 51 | private: 52 | IvfVideoFrameGenerator* const reader_; 53 | }; 54 | 55 | void OnFrameDecoded(const VideoFrame& decoded_frame); 56 | static std::unique_ptr CreateVideoDecoder( 57 | VideoCodecType codec_type); 58 | 59 | DecodedCallback callback_; 60 | std::unique_ptr file_reader_; 61 | std::unique_ptr video_decoder_; 62 | 63 | size_t width_; 64 | size_t height_; 65 | 66 | // This lock is used to ensure that all API method will be called 67 | // sequentially. It is required because we need to ensure that generator 68 | // won't be destroyed while it is reading the next frame on another thread, 69 | // because it will cause SIGSEGV when decoder callback will be invoked. 70 | // 71 | // FrameGenerator is injected into PeerConnection via some scoped_ref object 72 | // and it can happen that the last pointer will be destroyed on the different 73 | // thread comparing to the one from which frames were read. 74 | Mutex lock_; 75 | // This lock is used to sync between sending and receiving frame from decoder. 76 | // We can't reuse `lock_` because then generator can be destroyed between 77 | // frame was sent to decoder and decoder callback was invoked. 78 | Mutex frame_decode_lock_; 79 | 80 | rtc::Event next_frame_decoded_; 81 | absl::optional next_frame_ RTC_GUARDED_BY(frame_decode_lock_); 82 | }; 83 | 84 | } // namespace test 85 | } // namespace webrtc 86 | 87 | #endif // TEST_TESTSUPPORT_IVF_VIDEO_FRAME_GENERATOR_H_ 88 | -------------------------------------------------------------------------------- /src/main.cpp: -------------------------------------------------------------------------------- 1 | #include "Broadcaster.hpp" 2 | #include "mediasoupclient.hpp" 3 | #include 4 | #include // sigsuspend() 5 | #include 6 | #include 7 | #include 8 | 9 | using json = nlohmann::json; 10 | 11 | void signalHandler(int signum) 12 | { 13 | std::cout << "[INFO] interrupt signal (" << signum << ") received" << std::endl; 14 | 15 | std::cout << "[INFO] leaving!" << std::endl; 16 | 17 | std::exit(signum); 18 | } 19 | 20 | int main(int /*argc*/, char* /*argv*/[]) 21 | { 22 | // Register signal SIGINT and signal handler. 23 | signal(SIGINT, signalHandler); 24 | 25 | // Retrieve configuration from environment variables. 26 | const char* envServerUrl = std::getenv("SERVER_URL"); 27 | const char* envRoomId = std::getenv("ROOM_ID"); 28 | const char* envEnableAudio = std::getenv("ENABLE_AUDIO"); 29 | const char* envUseSimulcast = std::getenv("USE_SIMULCAST"); 30 | const char* envWebrtcDebug = std::getenv("WEBRTC_DEBUG"); 31 | const char* envVerifySsl = std::getenv("VERIFY_SSL"); 32 | 33 | if (envServerUrl == nullptr) 34 | { 35 | std::cerr << "[ERROR] missing 'SERVER_URL' environment variable" << std::endl; 36 | 37 | return 1; 38 | } 39 | 40 | if (envRoomId == nullptr) 41 | { 42 | std::cerr << "[ERROR] missing 'ROOM_ID' environment variable" << std::endl; 43 | 44 | return 1; 45 | } 46 | 47 | std::string baseUrl = envServerUrl; 48 | baseUrl.append("/rooms/").append(envRoomId); 49 | 50 | bool enableAudio = true; 51 | 52 | if (envEnableAudio && std::string(envEnableAudio) == "false") 53 | enableAudio = false; 54 | 55 | bool useSimulcast = true; 56 | 57 | if (envUseSimulcast && std::string(envUseSimulcast) == "false") 58 | useSimulcast = false; 59 | 60 | bool verifySsl = true; 61 | if (envVerifySsl && std::string(envVerifySsl) == "false") 62 | verifySsl = false; 63 | 64 | // Set RTC logging severity. 65 | if (envWebrtcDebug) 66 | { 67 | if (std::string(envWebrtcDebug) == "info") 68 | rtc::LogMessage::LogToDebug(rtc::LoggingSeverity::LS_INFO); 69 | else if (std::string(envWebrtcDebug) == "warn") 70 | rtc::LogMessage::LogToDebug(rtc::LoggingSeverity::LS_WARNING); 71 | else if (std::string(envWebrtcDebug) == "error") 72 | rtc::LogMessage::LogToDebug(rtc::LoggingSeverity::LS_ERROR); 73 | } 74 | 75 | auto logLevel = mediasoupclient::Logger::LogLevel::LOG_DEBUG; 76 | mediasoupclient::Logger::SetLogLevel(logLevel); 77 | mediasoupclient::Logger::SetDefaultHandler(); 78 | 79 | // Initilize mediasoupclient. 80 | mediasoupclient::Initialize(); 81 | 82 | std::cout << "[INFO] welcome to mediasoup broadcaster app!\n" << std::endl; 83 | 84 | std::cout << "[INFO] verifying that room '" << envRoomId << "' exists..." << std::endl; 85 | auto r = cpr::GetAsync(cpr::Url{ baseUrl }, cpr::VerifySsl{ verifySsl }).get(); 86 | 87 | if (r.status_code != 200) 88 | { 89 | std::cerr << "[ERROR] unable to retrieve room info" 90 | << " [status code:" << r.status_code << ", body:\"" << r.text << "\"]" << std::endl; 91 | 92 | return 1; 93 | } 94 | else 95 | { 96 | std::cout << "[INFO] found room" << envRoomId << std::endl; 97 | } 98 | 99 | auto response = nlohmann::json::parse(r.text); 100 | 101 | Broadcaster broadcaster; 102 | 103 | broadcaster.Start(baseUrl, enableAudio, useSimulcast, response, verifySsl); 104 | 105 | std::cout << "[INFO] press Ctrl+C or Cmd+C to leave..." << std::endl; 106 | 107 | while (true) 108 | { 109 | std::cin.get(); 110 | } 111 | 112 | return 0; 113 | } 114 | -------------------------------------------------------------------------------- /deps/libwebrtc/pc/test/fake_periodic_video_source.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 The WebRTC project authors. All Rights Reserved. 3 | * 4 | * Use of this source code is governed by a BSD-style license 5 | * that can be found in the LICENSE file in the root of the source 6 | * tree. An additional intellectual property rights grant can be found 7 | * in the file PATENTS. All contributing project authors may 8 | * be found in the AUTHORS file in the root of the source tree. 9 | */ 10 | 11 | #ifndef PC_TEST_FAKE_PERIODIC_VIDEO_SOURCE_H_ 12 | #define PC_TEST_FAKE_PERIODIC_VIDEO_SOURCE_H_ 13 | 14 | #include 15 | 16 | #include "api/video/video_source_interface.h" 17 | #include "media/base/fake_frame_source.h" 18 | #include "media/base/video_broadcaster.h" 19 | #include "rtc_base/synchronization/mutex.h" 20 | #include "rtc_base/task_queue_for_test.h" 21 | #include "rtc_base/task_utils/repeating_task.h" 22 | 23 | namespace webrtc { 24 | 25 | class FakePeriodicVideoSource final 26 | : public rtc::VideoSourceInterface { 27 | public: 28 | static constexpr int kDefaultFrameIntervalMs = 33; 29 | static constexpr int kDefaultWidth = 640; 30 | static constexpr int kDefaultHeight = 480; 31 | 32 | struct Config { 33 | int width = kDefaultWidth; 34 | int height = kDefaultHeight; 35 | int frame_interval_ms = kDefaultFrameIntervalMs; 36 | VideoRotation rotation = kVideoRotation_0; 37 | int64_t timestamp_offset_ms = 0; 38 | }; 39 | 40 | FakePeriodicVideoSource() : FakePeriodicVideoSource(Config()) {} 41 | explicit FakePeriodicVideoSource(Config config) 42 | : frame_source_( 43 | config.width, 44 | config.height, 45 | config.frame_interval_ms * rtc::kNumMicrosecsPerMillisec, 46 | config.timestamp_offset_ms * rtc::kNumMicrosecsPerMillisec), 47 | task_queue_(std::make_unique( 48 | "FakePeriodicVideoTrackSource")) { 49 | thread_checker_.Detach(); 50 | frame_source_.SetRotation(config.rotation); 51 | 52 | TimeDelta frame_interval = TimeDelta::Millis(config.frame_interval_ms); 53 | RepeatingTaskHandle::Start(task_queue_->Get(), [this, frame_interval] { 54 | if (broadcaster_.wants().rotation_applied) { 55 | broadcaster_.OnFrame(frame_source_.GetFrameRotationApplied()); 56 | } else { 57 | broadcaster_.OnFrame(frame_source_.GetFrame()); 58 | } 59 | return frame_interval; 60 | }); 61 | } 62 | 63 | rtc::VideoSinkWants wants() const { 64 | MutexLock lock(&mutex_); 65 | return wants_; 66 | } 67 | 68 | void RemoveSink(rtc::VideoSinkInterface* sink) override { 69 | RTC_DCHECK(thread_checker_.IsCurrent()); 70 | broadcaster_.RemoveSink(sink); 71 | } 72 | 73 | void AddOrUpdateSink(rtc::VideoSinkInterface* sink, 74 | const rtc::VideoSinkWants& wants) override { 75 | RTC_DCHECK(thread_checker_.IsCurrent()); 76 | { 77 | MutexLock lock(&mutex_); 78 | wants_ = wants; 79 | } 80 | broadcaster_.AddOrUpdateSink(sink, wants); 81 | } 82 | 83 | void Stop() { 84 | RTC_DCHECK(task_queue_); 85 | task_queue_.reset(); 86 | } 87 | 88 | private: 89 | SequenceChecker thread_checker_; 90 | 91 | rtc::VideoBroadcaster broadcaster_; 92 | cricket::FakeFrameSource frame_source_; 93 | mutable Mutex mutex_; 94 | rtc::VideoSinkWants wants_ RTC_GUARDED_BY(&mutex_); 95 | 96 | std::unique_ptr task_queue_; 97 | }; 98 | 99 | } // namespace webrtc 100 | 101 | #endif // PC_TEST_FAKE_PERIODIC_VIDEO_SOURCE_H_ 102 | -------------------------------------------------------------------------------- /src/MediaStreamTrackFactory.cpp: -------------------------------------------------------------------------------- 1 | #define MSC_CLASS "MediaStreamTrackFactory" 2 | 3 | #include 4 | 5 | #include "MediaSoupClientErrors.hpp" 6 | #include "MediaStreamTrackFactory.hpp" 7 | #include "pc/test/fake_audio_capture_module.h" 8 | #include "pc/test/fake_periodic_video_track_source.h" 9 | #include "pc/test/frame_generator_capturer_video_track_source.h" 10 | #include "system_wrappers/include/clock.h" 11 | #include "api/audio_codecs/builtin_audio_decoder_factory.h" 12 | #include "api/audio_codecs/builtin_audio_encoder_factory.h" 13 | #include "api/create_peerconnection_factory.h" 14 | #include "api/video_codecs/builtin_video_decoder_factory.h" 15 | #include "api/video_codecs/builtin_video_encoder_factory.h" 16 | 17 | using namespace mediasoupclient; 18 | 19 | static rtc::scoped_refptr factory; 20 | 21 | /* MediaStreamTrack holds reference to the threads of the PeerConnectionFactory. 22 | * Use plain pointers in order to avoid threads being destructed before tracks. 23 | */ 24 | static rtc::Thread* networkThread; 25 | static rtc::Thread* signalingThread; 26 | static rtc::Thread* workerThread; 27 | 28 | static void createFactory() 29 | { 30 | networkThread = rtc::Thread::Create().release(); 31 | signalingThread = rtc::Thread::Create().release(); 32 | workerThread = rtc::Thread::Create().release(); 33 | 34 | networkThread->SetName("network_thread", nullptr); 35 | signalingThread->SetName("signaling_thread", nullptr); 36 | workerThread->SetName("worker_thread", nullptr); 37 | 38 | if (!networkThread->Start() || !signalingThread->Start() || !workerThread->Start()) 39 | { 40 | MSC_THROW_INVALID_STATE_ERROR("thread start errored"); 41 | } 42 | 43 | webrtc::PeerConnectionInterface::RTCConfiguration config; 44 | 45 | auto fakeAudioCaptureModule = FakeAudioCaptureModule::Create(); 46 | if (!fakeAudioCaptureModule) 47 | { 48 | MSC_THROW_INVALID_STATE_ERROR("audio capture module creation errored"); 49 | } 50 | 51 | factory = webrtc::CreatePeerConnectionFactory( 52 | networkThread, 53 | workerThread, 54 | signalingThread, 55 | fakeAudioCaptureModule, 56 | webrtc::CreateBuiltinAudioEncoderFactory(), 57 | webrtc::CreateBuiltinAudioDecoderFactory(), 58 | webrtc::CreateBuiltinVideoEncoderFactory(), 59 | webrtc::CreateBuiltinVideoDecoderFactory(), 60 | nullptr /*audio_mixer*/, 61 | nullptr /*audio_processing*/); 62 | 63 | if (!factory) 64 | { 65 | MSC_THROW_ERROR("error ocurred creating peerconnection factory"); 66 | } 67 | } 68 | 69 | // Audio track creation. 70 | rtc::scoped_refptr createAudioTrack(const std::string& label) 71 | { 72 | if (!factory) 73 | createFactory(); 74 | 75 | cricket::AudioOptions options; 76 | options.highpass_filter = false; 77 | 78 | rtc::scoped_refptr source = factory->CreateAudioSource(options); 79 | 80 | return factory->CreateAudioTrack(label, source); 81 | } 82 | 83 | // Video track creation. 84 | rtc::scoped_refptr createVideoTrack(const std::string& /*label*/) 85 | { 86 | if (!factory) 87 | createFactory(); 88 | 89 | auto* videoTrackSource = 90 | new rtc::RefCountedObject(false /* remote */); 91 | 92 | return factory->CreateVideoTrack(rtc::CreateRandomUuid(), videoTrackSource); 93 | } 94 | 95 | rtc::scoped_refptr createSquaresVideoTrack(const std::string& /*label*/) 96 | { 97 | if (!factory) 98 | createFactory(); 99 | 100 | std::cout << "[INFO] getting frame generator" << std::endl; 101 | auto* videoTrackSource = new rtc::RefCountedObject( 102 | webrtc::FrameGeneratorCapturerVideoTrackSource::Config(), webrtc::Clock::GetRealTimeClock(), false); 103 | videoTrackSource->Start(); 104 | 105 | std::cout << "[INFO] creating video track" << std::endl; 106 | return factory->CreateVideoTrack(rtc::CreateRandomUuid(), videoTrackSource); 107 | } 108 | -------------------------------------------------------------------------------- /include/Broadcaster.hpp: -------------------------------------------------------------------------------- 1 | #ifndef BROADCASTER_H 2 | #define BROADCASTER_H 3 | 4 | #include "mediasoupclient.hpp" 5 | #include "json.hpp" 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | class Broadcaster : public 13 | mediasoupclient::SendTransport::Listener, 14 | mediasoupclient::RecvTransport::Listener, 15 | mediasoupclient::Producer::Listener, 16 | mediasoupclient::DataProducer::Listener, 17 | mediasoupclient::DataConsumer::Listener 18 | { 19 | public: 20 | struct TimerKiller 21 | { 22 | // returns false if killed: 23 | template 24 | bool WaitFor(std::chrono::duration const& time) const 25 | { 26 | std::unique_lock lock(m); 27 | return !cv.wait_for(lock, time, [&] { return terminate; }); 28 | } 29 | void Kill() 30 | { 31 | std::unique_lock lock(m); 32 | terminate = true; // Should be modified inside mutex lock. 33 | cv.notify_all(); // It is safe, and *sometimes* optimal, to do this outside the lock. 34 | } 35 | 36 | private: 37 | mutable std::condition_variable cv; 38 | mutable std::mutex m; 39 | bool terminate = false; 40 | }; 41 | 42 | /* Virtual methods inherited from SendTransport::Listener. */ 43 | public: 44 | std::future OnConnect( 45 | mediasoupclient::Transport* transport, const nlohmann::json& dtlsParameters) override; 46 | void OnConnectionStateChange( 47 | mediasoupclient::Transport* transport, const std::string& connectionState) override; 48 | std::future OnProduce( 49 | mediasoupclient::SendTransport* /*transport*/, 50 | const std::string& kind, 51 | nlohmann::json rtpParameters, 52 | const nlohmann::json& appData) override; 53 | 54 | std::future OnProduceData( 55 | mediasoupclient::SendTransport* transport, 56 | const nlohmann::json& sctpStreamParameters, 57 | const std::string& label, 58 | const std::string& protocol, 59 | const nlohmann::json& appData) override; 60 | 61 | /* Virtual methods inherited from Producer::Listener. */ 62 | public: 63 | void OnTransportClose(mediasoupclient::Producer* producer) override; 64 | 65 | /* Virtual methods inherited from DataConsumer::Listener */ 66 | public: 67 | void OnMessage(mediasoupclient::DataConsumer* dataConsumer, const webrtc::DataBuffer& buffer) override; 68 | void OnConnecting(mediasoupclient::DataConsumer* dataConsumer) override 69 | { 70 | } 71 | void OnClosing(mediasoupclient::DataConsumer* dataConsumer) override 72 | { 73 | } 74 | void OnClose(mediasoupclient::DataConsumer* dataConsumer) override 75 | { 76 | } 77 | void OnOpen(mediasoupclient::DataConsumer* dataConsumer) override 78 | { 79 | } 80 | void OnTransportClose(mediasoupclient::DataConsumer* dataConsumer) override 81 | { 82 | } 83 | 84 | /* Virtual methods inherited from DataProducer::Listener */ 85 | public: 86 | void OnOpen(mediasoupclient::DataProducer* dataProducer) override; 87 | void OnClose(mediasoupclient::DataProducer* dataProducer) override; 88 | void OnBufferedAmountChange(mediasoupclient::DataProducer* dataProducer, uint64_t size) override; 89 | void OnTransportClose(mediasoupclient::DataProducer* dataProducer) override; 90 | 91 | public: 92 | void Start( 93 | const std::string& baseUrl, 94 | bool enableAudio, 95 | bool useSimulcast, 96 | const nlohmann::json& routerRtpCapabilities, 97 | bool verifySsl = true); 98 | void Stop(); 99 | 100 | ~Broadcaster(); 101 | 102 | private: 103 | mediasoupclient::Device device; 104 | mediasoupclient::SendTransport* sendTransport{ nullptr }; 105 | mediasoupclient::RecvTransport* recvTransport{ nullptr }; 106 | mediasoupclient::DataProducer* dataProducer{ nullptr }; 107 | mediasoupclient::DataConsumer* dataConsumer{ nullptr }; 108 | 109 | std::string id = std::to_string(rtc::CreateRandomId()); 110 | std::string baseUrl; 111 | std::thread sendDataThread; 112 | 113 | struct TimerKiller timerKiller; 114 | bool verifySsl = true; 115 | 116 | std::future OnConnectSendTransport(const nlohmann::json& dtlsParameters); 117 | std::future OnConnectRecvTransport(const nlohmann::json& dtlsParameters); 118 | 119 | void CreateSendTransport(bool enableAudio, bool useSimulcast); 120 | void CreateRecvTransport(); 121 | void CreateDataConsumer(); 122 | }; 123 | 124 | #endif // STOKER_HPP 125 | -------------------------------------------------------------------------------- /deps/libwebrtc/test/testsupport/file_utils.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. 3 | * 4 | * Use of this source code is governed by a BSD-style license 5 | * that can be found in the LICENSE file in the root of the source 6 | * tree. An additional intellectual property rights grant can be found 7 | * in the file PATENTS. All contributing project authors may 8 | * be found in the AUTHORS file in the root of the source tree. 9 | */ 10 | 11 | #include 12 | 13 | #ifndef TEST_TESTSUPPORT_FILE_UTILS_H_ 14 | #define TEST_TESTSUPPORT_FILE_UTILS_H_ 15 | 16 | #include 17 | #include 18 | 19 | #include "absl/types/optional.h" 20 | 21 | namespace webrtc { 22 | namespace test { 23 | 24 | // This is the "directory" returned if the ProjectPath() function fails 25 | // to find the project root. 26 | extern const char* kCannotFindProjectRootDir; 27 | 28 | // Slash or backslash, depending on platform. NUL-terminated string. 29 | extern const char* kPathDelimiter; 30 | 31 | // Returns the absolute path to the output directory where log files and other 32 | // test artifacts should be put. The output directory is generally a directory 33 | // named "out" at the project root. This root is assumed to be two levels above 34 | // where the test binary is located; this is because tests execute in a dir 35 | // out/Whatever relative to the project root. This convention is also followed 36 | // in Chromium. 37 | // 38 | // The exception is Android where we use /sdcard/ instead. 39 | // 40 | // If symbolic links occur in the path they will be resolved and the actual 41 | // directory will be returned. 42 | // 43 | // Returns the path WITH a trailing path delimiter. If the project root is not 44 | // found, the current working directory ("./") is returned as a fallback. 45 | std::string OutputPath(); 46 | 47 | // Generates an empty file with a unique name in the specified directory and 48 | // returns the file name and path. 49 | // TODO(titovartem) rename to TempFile and next method to TempFilename 50 | std::string TempFilename(const std::string& dir, const std::string& prefix); 51 | 52 | // Generates a unique file name that can be used for file creation. Doesn't 53 | // create any files. 54 | std::string GenerateTempFilename(const std::string& dir, 55 | const std::string& prefix); 56 | 57 | // Returns a path to a resource file in [project-root]/resources/ dir. 58 | // Returns an absolute path 59 | // 60 | // Arguments: 61 | // name - Name of the resource file. If a plain filename (no directory path) 62 | // is supplied, the file is assumed to be located in resources/ 63 | // If a directory path is prepended to the filename, a subdirectory 64 | // hierarchy reflecting that path is assumed to be present. 65 | // extension - File extension, without the dot, i.e. "bmp" or "yuv". 66 | std::string ResourcePath(const std::string& name, const std::string& extension); 67 | 68 | // Joins directory name and file name, separated by the path delimiter. 69 | std::string JoinFilename(const std::string& dir, const std::string& name); 70 | 71 | // Gets the current working directory for the executing program. 72 | // Returns "./" if for some reason it is not possible to find the working 73 | // directory. 74 | std::string WorkingDir(); 75 | 76 | // Reads the content of a directory and, in case of success, returns a vector 77 | // of strings with one element for each found file or directory. Each element is 78 | // a path created by prepending `dir` to the file/directory name. "." and ".." 79 | // are never added in the returned vector. 80 | absl::optional> ReadDirectory(std::string path); 81 | 82 | // Creates a directory if it not already exists. 83 | // Returns true if successful. Will print an error message to stderr and return 84 | // false if a file with the same name already exists. 85 | bool CreateDir(const std::string& directory_name); 86 | 87 | // Removes a directory, which must already be empty. 88 | bool RemoveDir(const std::string& directory_name); 89 | 90 | // Removes a file. 91 | bool RemoveFile(const std::string& file_name); 92 | 93 | // Checks if a file exists. 94 | bool FileExists(const std::string& file_name); 95 | 96 | // Checks if a directory exists. 97 | bool DirExists(const std::string& directory_name); 98 | 99 | // Strips the rightmost path segment from a path. 100 | std::string DirName(const std::string& path); 101 | 102 | // File size of the supplied file in bytes. Will return 0 if the file is 103 | // empty or if the file does not exist/is readable. 104 | size_t GetFileSize(const std::string& filename); 105 | 106 | } // namespace test 107 | } // namespace webrtc 108 | 109 | #endif // TEST_TESTSUPPORT_FILE_UTILS_H_ 110 | -------------------------------------------------------------------------------- /deps/libwebrtc/test/testsupport/file_utils_override.cc: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2018 The WebRTC project authors. All Rights Reserved. 3 | * 4 | * Use of this source code is governed by a BSD-style license 5 | * that can be found in the LICENSE file in the root of the source 6 | * tree. An additional intellectual property rights grant can be found 7 | * in the file PATENTS. All contributing project authors may 8 | * be found in the AUTHORS file in the root of the source tree. 9 | */ 10 | 11 | #include "test/testsupport/file_utils_override.h" 12 | 13 | #include 14 | #include 15 | 16 | #if defined(WEBRTC_WIN) 17 | #include 18 | #include 19 | #include 20 | 21 | #include 22 | #include 23 | #include 24 | 25 | #include "Shlwapi.h" 26 | #include "WinDef.h" 27 | #include "rtc_base/win32.h" 28 | 29 | #define GET_CURRENT_DIR _getcwd 30 | #else 31 | #include 32 | 33 | #define GET_CURRENT_DIR getcwd 34 | #endif 35 | 36 | #if defined(WEBRTC_IOS) 37 | #include "test/testsupport/ios_file_utils.h" 38 | #endif 39 | 40 | #if defined(WEBRTC_MAC) 41 | #include "test/testsupport/mac_file_utils.h" 42 | #endif 43 | 44 | #include "absl/types/optional.h" 45 | #include "rtc_base/arraysize.h" 46 | #include "rtc_base/checks.h" 47 | #include "rtc_base/string_utils.h" 48 | 49 | namespace webrtc { 50 | namespace test { 51 | 52 | std::string DirName(const std::string& path); 53 | bool CreateDir(const std::string& directory_name); 54 | 55 | namespace internal { 56 | 57 | namespace { 58 | #if defined(WEBRTC_WIN) 59 | const char* kPathDelimiter = "\\"; 60 | #elif !defined(WEBRTC_IOS) 61 | const char* kPathDelimiter = "/"; 62 | #endif 63 | 64 | #if defined(WEBRTC_ANDROID) 65 | // This is a special case in Chrome infrastructure. See 66 | // base/test/test_support_android.cc. 67 | const char* kAndroidChromiumTestsRoot = "/sdcard/chromium_tests_root/"; 68 | #endif 69 | 70 | #if !defined(WEBRTC_IOS) 71 | const char* kResourcesDirName = "resources"; 72 | #endif 73 | 74 | } // namespace 75 | 76 | // Finds the WebRTC src dir. 77 | // The returned path always ends with a path separator. 78 | absl::optional ProjectRootPath() { 79 | #if defined(WEBRTC_ANDROID) 80 | return kAndroidChromiumTestsRoot; 81 | #elif defined WEBRTC_IOS 82 | return IOSRootPath(); 83 | #elif defined(WEBRTC_MAC) 84 | std::string path; 85 | GetNSExecutablePath(&path); 86 | std::string exe_dir = DirName(path); 87 | // On Mac, tests execute in out/Whatever, so src is two levels up except if 88 | // the test is bundled (which our tests are not), in which case it's 5 levels. 89 | return DirName(DirName(exe_dir)) + kPathDelimiter; 90 | #elif defined(WEBRTC_POSIX) 91 | char buf[PATH_MAX]; 92 | ssize_t count = ::readlink("/proc/self/exe", buf, arraysize(buf)); 93 | if (count <= 0) { 94 | RTC_NOTREACHED() << "Unable to resolve /proc/self/exe."; 95 | return absl::nullopt; 96 | } 97 | // On POSIX, tests execute in out/Whatever, so src is two levels up. 98 | std::string exe_dir = DirName(std::string(buf, count)); 99 | return DirName(DirName(exe_dir)) + kPathDelimiter; 100 | #elif defined(WEBRTC_WIN) 101 | wchar_t buf[MAX_PATH]; 102 | buf[0] = 0; 103 | if (GetModuleFileNameW(NULL, buf, MAX_PATH) == 0) 104 | return absl::nullopt; 105 | 106 | std::string exe_path = rtc::ToUtf8(std::wstring(buf)); 107 | std::string exe_dir = DirName(exe_path); 108 | return DirName(DirName(exe_dir)) + kPathDelimiter; 109 | #endif 110 | } 111 | 112 | std::string OutputPath() { 113 | #if defined(WEBRTC_IOS) 114 | return IOSOutputPath(); 115 | #elif defined(WEBRTC_ANDROID) 116 | return kAndroidChromiumTestsRoot; 117 | #else 118 | absl::optional path_opt = ProjectRootPath(); 119 | RTC_DCHECK(path_opt); 120 | std::string path = *path_opt + "out"; 121 | if (!CreateDir(path)) { 122 | return "./"; 123 | } 124 | return path + kPathDelimiter; 125 | #endif 126 | } 127 | 128 | std::string WorkingDir() { 129 | #if defined(WEBRTC_ANDROID) 130 | return kAndroidChromiumTestsRoot; 131 | #else 132 | char path_buffer[FILENAME_MAX]; 133 | if (!GET_CURRENT_DIR(path_buffer, sizeof(path_buffer))) { 134 | fprintf(stderr, "Cannot get current directory!\n"); 135 | return "./"; 136 | } else { 137 | return std::string(path_buffer); 138 | } 139 | #endif 140 | } 141 | 142 | std::string ResourcePath(const std::string& name, 143 | const std::string& extension) { 144 | #if defined(WEBRTC_IOS) 145 | return IOSResourcePath(name, extension); 146 | #else 147 | absl::optional path_opt = ProjectRootPath(); 148 | RTC_DCHECK(path_opt); 149 | std::string resources_path = *path_opt + kResourcesDirName + kPathDelimiter; 150 | return resources_path + name + "." + extension; 151 | #endif 152 | } 153 | 154 | } // namespace internal 155 | } // namespace test 156 | } // namespace webrtc 157 | -------------------------------------------------------------------------------- /deps/libwebrtc/test/frame_generator.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved. 3 | * 4 | * Use of this source code is governed by a BSD-style license 5 | * that can be found in the LICENSE file in the root of the source 6 | * tree. An additional intellectual property rights grant can be found 7 | * in the file PATENTS. All contributing project authors may 8 | * be found in the AUTHORS file in the root of the source tree. 9 | */ 10 | #ifndef TEST_FRAME_GENERATOR_H_ 11 | #define TEST_FRAME_GENERATOR_H_ 12 | 13 | #include 14 | #include 15 | #include 16 | 17 | #include "api/scoped_refptr.h" 18 | #include "api/test/frame_generator_interface.h" 19 | #include "api/video/i420_buffer.h" 20 | #include "api/video/video_frame.h" 21 | #include "api/video/video_frame_buffer.h" 22 | #include "api/video/video_source_interface.h" 23 | #include "rtc_base/random.h" 24 | #include "rtc_base/synchronization/mutex.h" 25 | #include "system_wrappers/include/clock.h" 26 | 27 | namespace webrtc { 28 | namespace test { 29 | 30 | // SquareGenerator is a FrameGenerator that draws a given amount of randomly 31 | // sized and colored squares. Between each new generated frame, the squares 32 | // are moved slightly towards the lower right corner. 33 | class SquareGenerator : public FrameGeneratorInterface { 34 | public: 35 | SquareGenerator(int width, int height, OutputType type, int num_squares); 36 | 37 | void ChangeResolution(size_t width, size_t height) override; 38 | VideoFrameData NextFrame() override; 39 | 40 | private: 41 | rtc::scoped_refptr CreateI420Buffer(int width, int height); 42 | 43 | class Square { 44 | public: 45 | Square(int width, int height, int seed); 46 | 47 | void Draw(const rtc::scoped_refptr& frame_buffer); 48 | 49 | private: 50 | Random random_generator_; 51 | int x_; 52 | int y_; 53 | const int length_; 54 | const uint8_t yuv_y_; 55 | const uint8_t yuv_u_; 56 | const uint8_t yuv_v_; 57 | const uint8_t yuv_a_; 58 | }; 59 | 60 | Mutex mutex_; 61 | const OutputType type_; 62 | int width_ RTC_GUARDED_BY(&mutex_); 63 | int height_ RTC_GUARDED_BY(&mutex_); 64 | std::vector> squares_ RTC_GUARDED_BY(&mutex_); 65 | }; 66 | 67 | class YuvFileGenerator : public FrameGeneratorInterface { 68 | public: 69 | YuvFileGenerator(std::vector files, 70 | size_t width, 71 | size_t height, 72 | int frame_repeat_count); 73 | 74 | ~YuvFileGenerator(); 75 | 76 | VideoFrameData NextFrame() override; 77 | void ChangeResolution(size_t width, size_t height) override { 78 | RTC_NOTREACHED(); 79 | } 80 | 81 | private: 82 | // Returns true if the new frame was loaded. 83 | // False only in case of a single file with a single frame in it. 84 | bool ReadNextFrame(); 85 | 86 | size_t file_index_; 87 | size_t frame_index_; 88 | const std::vector files_; 89 | const size_t width_; 90 | const size_t height_; 91 | const size_t frame_size_; 92 | const std::unique_ptr frame_buffer_; 93 | const int frame_display_count_; 94 | int current_display_count_; 95 | rtc::scoped_refptr last_read_buffer_; 96 | }; 97 | 98 | // SlideGenerator works similarly to YuvFileGenerator but it fills the frames 99 | // with randomly sized and colored squares instead of reading their content 100 | // from files. 101 | class SlideGenerator : public FrameGeneratorInterface { 102 | public: 103 | SlideGenerator(int width, int height, int frame_repeat_count); 104 | 105 | VideoFrameData NextFrame() override; 106 | void ChangeResolution(size_t width, size_t height) override { 107 | RTC_NOTREACHED(); 108 | } 109 | 110 | private: 111 | // Generates some randomly sized and colored squares scattered 112 | // over the frame. 113 | void GenerateNewFrame(); 114 | 115 | const int width_; 116 | const int height_; 117 | const int frame_display_count_; 118 | int current_display_count_; 119 | Random random_generator_; 120 | rtc::scoped_refptr buffer_; 121 | }; 122 | 123 | class ScrollingImageFrameGenerator : public FrameGeneratorInterface { 124 | public: 125 | ScrollingImageFrameGenerator(Clock* clock, 126 | const std::vector& files, 127 | size_t source_width, 128 | size_t source_height, 129 | size_t target_width, 130 | size_t target_height, 131 | int64_t scroll_time_ms, 132 | int64_t pause_time_ms); 133 | ~ScrollingImageFrameGenerator() override = default; 134 | 135 | VideoFrameData NextFrame() override; 136 | void ChangeResolution(size_t width, size_t height) override { 137 | RTC_NOTREACHED(); 138 | } 139 | 140 | private: 141 | void UpdateSourceFrame(size_t frame_num); 142 | void CropSourceToScrolledImage(double scroll_factor); 143 | 144 | Clock* const clock_; 145 | const int64_t start_time_; 146 | const int64_t scroll_time_; 147 | const int64_t pause_time_; 148 | const size_t num_frames_; 149 | const int target_width_; 150 | const int target_height_; 151 | 152 | size_t current_frame_num_; 153 | bool prev_frame_not_scrolled_; 154 | VideoFrameData current_source_frame_; 155 | VideoFrameData current_frame_; 156 | YuvFileGenerator file_generator_; 157 | }; 158 | 159 | } // namespace test 160 | } // namespace webrtc 161 | 162 | #endif // TEST_FRAME_GENERATOR_H_ 163 | -------------------------------------------------------------------------------- /deps/libwebrtc/test/testsupport/ivf_video_frame_generator.cc: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2019 The WebRTC project authors. All Rights Reserved. 3 | * 4 | * Use of this source code is governed by a BSD-style license 5 | * that can be found in the LICENSE file in the root of the source 6 | * tree. An additional intellectual property rights grant can be found 7 | * in the file PATENTS. All contributing project authors may 8 | * be found in the AUTHORS file in the root of the source tree. 9 | */ 10 | 11 | #include "test/testsupport/ivf_video_frame_generator.h" 12 | 13 | #include 14 | 15 | #include "api/video/encoded_image.h" 16 | #include "api/video/i420_buffer.h" 17 | #include "api/video_codecs/video_codec.h" 18 | #include "media/base/media_constants.h" 19 | #include "modules/video_coding/codecs/h264/include/h264.h" 20 | #include "modules/video_coding/codecs/vp8/include/vp8.h" 21 | #include "modules/video_coding/codecs/vp9/include/vp9.h" 22 | #include "modules/video_coding/include/video_error_codes.h" 23 | #include "rtc_base/checks.h" 24 | #include "rtc_base/system/file_wrapper.h" 25 | 26 | namespace webrtc { 27 | namespace test { 28 | namespace { 29 | 30 | constexpr int kMaxNextFrameWaitTemeoutMs = 1000; 31 | 32 | } // namespace 33 | 34 | IvfVideoFrameGenerator::IvfVideoFrameGenerator(const std::string& file_name) 35 | : callback_(this), 36 | file_reader_(IvfFileReader::Create(FileWrapper::OpenReadOnly(file_name))), 37 | video_decoder_(CreateVideoDecoder(file_reader_->GetVideoCodecType())), 38 | width_(file_reader_->GetFrameWidth()), 39 | height_(file_reader_->GetFrameHeight()) { 40 | RTC_CHECK(video_decoder_) << "No decoder found for file's video codec type"; 41 | VideoCodec codec_settings; 42 | codec_settings.codecType = file_reader_->GetVideoCodecType(); 43 | codec_settings.width = file_reader_->GetFrameWidth(); 44 | codec_settings.height = file_reader_->GetFrameHeight(); 45 | // Set buffer pool size to max value to ensure that if users of generator, 46 | // ex. test frameworks, will retain frames for quite a long time, decoder 47 | // won't crash with buffers pool overflow error. 48 | codec_settings.buffer_pool_size = std::numeric_limits::max(); 49 | RTC_CHECK_EQ(video_decoder_->RegisterDecodeCompleteCallback(&callback_), 50 | WEBRTC_VIDEO_CODEC_OK); 51 | RTC_CHECK_EQ( 52 | video_decoder_->InitDecode(&codec_settings, /*number_of_cores=*/1), 53 | WEBRTC_VIDEO_CODEC_OK); 54 | } 55 | IvfVideoFrameGenerator::~IvfVideoFrameGenerator() { 56 | MutexLock lock(&lock_); 57 | if (!file_reader_) { 58 | return; 59 | } 60 | file_reader_->Close(); 61 | file_reader_.reset(); 62 | // Reset decoder to prevent it from async access to `this`. 63 | video_decoder_.reset(); 64 | { 65 | MutexLock frame_lock(&frame_decode_lock_); 66 | next_frame_ = absl::nullopt; 67 | // Set event in case another thread is waiting on it. 68 | next_frame_decoded_.Set(); 69 | } 70 | } 71 | 72 | FrameGeneratorInterface::VideoFrameData IvfVideoFrameGenerator::NextFrame() { 73 | MutexLock lock(&lock_); 74 | next_frame_decoded_.Reset(); 75 | RTC_CHECK(file_reader_); 76 | if (!file_reader_->HasMoreFrames()) { 77 | file_reader_->Reset(); 78 | } 79 | absl::optional image = file_reader_->NextFrame(); 80 | RTC_CHECK(image); 81 | // Last parameter is undocumented and there is no usage of it found. 82 | RTC_CHECK_EQ(WEBRTC_VIDEO_CODEC_OK, 83 | video_decoder_->Decode(*image, /*missing_frames=*/false, 84 | /*render_time_ms=*/0)); 85 | bool decoded = next_frame_decoded_.Wait(kMaxNextFrameWaitTemeoutMs); 86 | RTC_CHECK(decoded) << "Failed to decode next frame in " 87 | << kMaxNextFrameWaitTemeoutMs << "ms. Can't continue"; 88 | 89 | MutexLock frame_lock(&frame_decode_lock_); 90 | rtc::scoped_refptr buffer = 91 | next_frame_->video_frame_buffer(); 92 | if (width_ != static_cast(buffer->width()) || 93 | height_ != static_cast(buffer->height())) { 94 | // Video adapter has requested a down-scale. Allocate a new buffer and 95 | // return scaled version. 96 | rtc::scoped_refptr scaled_buffer = 97 | I420Buffer::Create(width_, height_); 98 | scaled_buffer->ScaleFrom(*buffer->ToI420()); 99 | buffer = scaled_buffer; 100 | } 101 | return VideoFrameData(buffer, next_frame_->update_rect()); 102 | } 103 | 104 | void IvfVideoFrameGenerator::ChangeResolution(size_t width, size_t height) { 105 | MutexLock lock(&lock_); 106 | width_ = width; 107 | height_ = height; 108 | } 109 | 110 | int32_t IvfVideoFrameGenerator::DecodedCallback::Decoded( 111 | VideoFrame& decoded_image) { 112 | Decoded(decoded_image, 0, 0); 113 | return WEBRTC_VIDEO_CODEC_OK; 114 | } 115 | int32_t IvfVideoFrameGenerator::DecodedCallback::Decoded( 116 | VideoFrame& decoded_image, 117 | int64_t decode_time_ms) { 118 | Decoded(decoded_image, decode_time_ms, 0); 119 | return WEBRTC_VIDEO_CODEC_OK; 120 | } 121 | void IvfVideoFrameGenerator::DecodedCallback::Decoded( 122 | VideoFrame& decoded_image, 123 | absl::optional decode_time_ms, 124 | absl::optional qp) { 125 | reader_->OnFrameDecoded(decoded_image); 126 | } 127 | 128 | void IvfVideoFrameGenerator::OnFrameDecoded(const VideoFrame& decoded_frame) { 129 | MutexLock lock(&frame_decode_lock_); 130 | next_frame_ = decoded_frame; 131 | next_frame_decoded_.Set(); 132 | } 133 | 134 | std::unique_ptr IvfVideoFrameGenerator::CreateVideoDecoder( 135 | VideoCodecType codec_type) { 136 | if (codec_type == VideoCodecType::kVideoCodecVP8) { 137 | return VP8Decoder::Create(); 138 | } 139 | if (codec_type == VideoCodecType::kVideoCodecVP9) { 140 | return VP9Decoder::Create(); 141 | } 142 | if (codec_type == VideoCodecType::kVideoCodecH264) { 143 | return H264Decoder::Create(); 144 | } 145 | return nullptr; 146 | } 147 | 148 | } // namespace test 149 | } // namespace webrtc 150 | -------------------------------------------------------------------------------- /deps/libwebrtc/test/frame_generator_capturer.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved. 3 | * 4 | * Use of this source code is governed by a BSD-style license 5 | * that can be found in the LICENSE file in the root of the source 6 | * tree. An additional intellectual property rights grant can be found 7 | * in the file PATENTS. All contributing project authors may 8 | * be found in the AUTHORS file in the root of the source tree. 9 | */ 10 | #ifndef TEST_FRAME_GENERATOR_CAPTURER_H_ 11 | #define TEST_FRAME_GENERATOR_CAPTURER_H_ 12 | 13 | #include 14 | #include 15 | 16 | #include "api/task_queue/task_queue_factory.h" 17 | #include "api/test/frame_generator_interface.h" 18 | #include "api/video/video_frame.h" 19 | #include "rtc_base/synchronization/mutex.h" 20 | #include "rtc_base/task_queue.h" 21 | #include "rtc_base/task_utils/repeating_task.h" 22 | #include "system_wrappers/include/clock.h" 23 | #include "test/test_video_capturer.h" 24 | 25 | namespace webrtc { 26 | 27 | namespace test { 28 | namespace frame_gen_cap_impl { 29 | template 30 | class AutoOpt : public absl::optional { 31 | public: 32 | using absl::optional::optional; 33 | T* operator->() { 34 | if (!absl::optional::has_value()) 35 | this->emplace(T()); 36 | return absl::optional::operator->(); 37 | } 38 | }; 39 | } // namespace frame_gen_cap_impl 40 | struct FrameGeneratorCapturerConfig { 41 | struct SquaresVideo { 42 | int framerate = 30; 43 | FrameGeneratorInterface::OutputType pixel_format = 44 | FrameGeneratorInterface::OutputType::kI420; 45 | int width = 320; 46 | int height = 180; 47 | int num_squares = 10; 48 | }; 49 | 50 | struct SquareSlides { 51 | int framerate = 30; 52 | TimeDelta change_interval = TimeDelta::Seconds(10); 53 | int width = 1600; 54 | int height = 1200; 55 | }; 56 | 57 | struct VideoFile { 58 | int framerate = 30; 59 | std::string name; 60 | // Must be set to width and height of the source video file. 61 | int width = 0; 62 | int height = 0; 63 | }; 64 | 65 | struct ImageSlides { 66 | int framerate = 30; 67 | TimeDelta change_interval = TimeDelta::Seconds(10); 68 | struct Crop { 69 | TimeDelta scroll_duration = TimeDelta::Seconds(0); 70 | absl::optional width; 71 | absl::optional height; 72 | } crop; 73 | int width = 1850; 74 | int height = 1110; 75 | std::vector paths = { 76 | "web_screenshot_1850_1110", 77 | "presentation_1850_1110", 78 | "photo_1850_1110", 79 | "difficult_photo_1850_1110", 80 | }; 81 | }; 82 | 83 | frame_gen_cap_impl::AutoOpt squares_video; 84 | frame_gen_cap_impl::AutoOpt squares_slides; 85 | frame_gen_cap_impl::AutoOpt video_file; 86 | frame_gen_cap_impl::AutoOpt image_slides; 87 | }; 88 | 89 | class FrameGeneratorCapturer : public TestVideoCapturer { 90 | public: 91 | class SinkWantsObserver { 92 | public: 93 | // OnSinkWantsChanged is called when FrameGeneratorCapturer::AddOrUpdateSink 94 | // is called. 95 | virtual void OnSinkWantsChanged(rtc::VideoSinkInterface* sink, 96 | const rtc::VideoSinkWants& wants) = 0; 97 | 98 | protected: 99 | virtual ~SinkWantsObserver() {} 100 | }; 101 | 102 | FrameGeneratorCapturer( 103 | Clock* clock, 104 | std::unique_ptr frame_generator, 105 | int target_fps, 106 | TaskQueueFactory& task_queue_factory); 107 | virtual ~FrameGeneratorCapturer(); 108 | 109 | static std::unique_ptr Create( 110 | Clock* clock, 111 | TaskQueueFactory& task_queue_factory, 112 | FrameGeneratorCapturerConfig::SquaresVideo config); 113 | static std::unique_ptr Create( 114 | Clock* clock, 115 | TaskQueueFactory& task_queue_factory, 116 | FrameGeneratorCapturerConfig::SquareSlides config); 117 | static std::unique_ptr Create( 118 | Clock* clock, 119 | TaskQueueFactory& task_queue_factory, 120 | FrameGeneratorCapturerConfig::VideoFile config); 121 | static std::unique_ptr Create( 122 | Clock* clock, 123 | TaskQueueFactory& task_queue_factory, 124 | FrameGeneratorCapturerConfig::ImageSlides config); 125 | static std::unique_ptr Create( 126 | Clock* clock, 127 | TaskQueueFactory& task_queue_factory, 128 | const FrameGeneratorCapturerConfig& config); 129 | 130 | void Start(); 131 | void Stop(); 132 | void ChangeResolution(size_t width, size_t height); 133 | void ChangeFramerate(int target_framerate); 134 | 135 | void SetSinkWantsObserver(SinkWantsObserver* observer); 136 | 137 | void AddOrUpdateSink(rtc::VideoSinkInterface* sink, 138 | const rtc::VideoSinkWants& wants) override; 139 | void RemoveSink(rtc::VideoSinkInterface* sink) override; 140 | 141 | void ForceFrame(); 142 | void SetFakeRotation(VideoRotation rotation); 143 | void SetFakeColorSpace(absl::optional color_space); 144 | 145 | int64_t first_frame_capture_time() const { return first_frame_capture_time_; } 146 | 147 | bool Init(); 148 | 149 | private: 150 | void InsertFrame(); 151 | static bool Run(void* obj); 152 | int GetCurrentConfiguredFramerate(); 153 | void UpdateFps(int max_fps) RTC_EXCLUSIVE_LOCKS_REQUIRED(&lock_); 154 | 155 | Clock* const clock_; 156 | RepeatingTaskHandle frame_task_; 157 | bool sending_; 158 | SinkWantsObserver* sink_wants_observer_ RTC_GUARDED_BY(&lock_); 159 | 160 | Mutex lock_; 161 | std::unique_ptr frame_generator_; 162 | 163 | int source_fps_ RTC_GUARDED_BY(&lock_); 164 | int target_capture_fps_ RTC_GUARDED_BY(&lock_); 165 | absl::optional wanted_fps_ RTC_GUARDED_BY(&lock_); 166 | VideoRotation fake_rotation_ = kVideoRotation_0; 167 | absl::optional fake_color_space_ RTC_GUARDED_BY(&lock_); 168 | 169 | int64_t first_frame_capture_time_; 170 | // Must be the last field, so it will be deconstructed first as tasks 171 | // in the TaskQueue access other fields of the instance of this class. 172 | rtc::TaskQueue task_queue_; 173 | }; 174 | } // namespace test 175 | } // namespace webrtc 176 | 177 | #endif // TEST_FRAME_GENERATOR_CAPTURER_H_ 178 | -------------------------------------------------------------------------------- /deps/libwebrtc/test/testsupport/file_utils.cc: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. 3 | * 4 | * Use of this source code is governed by a BSD-style license 5 | * that can be found in the LICENSE file in the root of the source 6 | * tree. An additional intellectual property rights grant can be found 7 | * in the file PATENTS. All contributing project authors may 8 | * be found in the AUTHORS file in the root of the source tree. 9 | */ 10 | 11 | #include "test/testsupport/file_utils.h" 12 | 13 | 14 | #if defined(WEBRTC_POSIX) 15 | #include 16 | #endif 17 | 18 | #if defined(WEBRTC_WIN) 19 | #include 20 | #include 21 | #include 22 | 23 | #include 24 | #include 25 | #include 26 | 27 | #include "Shlwapi.h" 28 | #include "WinDef.h" 29 | #include "rtc_base/win32.h" 30 | 31 | #define GET_CURRENT_DIR _getcwd 32 | #else 33 | #include 34 | 35 | #define GET_CURRENT_DIR getcwd 36 | #endif 37 | 38 | #include // To check for directory existence. 39 | #ifndef S_ISDIR // Not defined in stat.h on Windows. 40 | #define S_ISDIR(mode) (((mode)&S_IFMT) == S_IFDIR) 41 | #endif 42 | 43 | #include 44 | #include 45 | 46 | #include 47 | #include 48 | #include 49 | 50 | #if defined(WEBRTC_IOS) 51 | #include "test/testsupport/ios_file_utils.h" 52 | #elif defined(WEBRTC_MAC) 53 | #include "test/testsupport/mac_file_utils.h" 54 | #endif 55 | 56 | #include "rtc_base/checks.h" 57 | #include "rtc_base/string_utils.h" 58 | #include "test/testsupport/file_utils_override.h" 59 | 60 | namespace webrtc { 61 | namespace test { 62 | 63 | #if defined(WEBRTC_WIN) 64 | const char* kPathDelimiter = "\\"; 65 | #else 66 | const char* kPathDelimiter = "/"; 67 | #endif 68 | 69 | std::string DirName(const std::string& path) { 70 | if (path.empty()) 71 | return ""; 72 | if (path == kPathDelimiter) 73 | return path; 74 | 75 | std::string result = path; 76 | if (result.back() == *kPathDelimiter) 77 | result.pop_back(); // Remove trailing separator. 78 | 79 | return result.substr(0, result.find_last_of(kPathDelimiter)); 80 | } 81 | 82 | bool FileExists(const std::string& file_name) { 83 | struct stat file_info = {0}; 84 | return stat(file_name.c_str(), &file_info) == 0; 85 | } 86 | 87 | bool DirExists(const std::string& directory_name) { 88 | struct stat directory_info = {0}; 89 | return stat(directory_name.c_str(), &directory_info) == 0 && 90 | S_ISDIR(directory_info.st_mode); 91 | } 92 | 93 | std::string OutputPath() { 94 | return webrtc::test::internal::OutputPath(); 95 | } 96 | 97 | std::string WorkingDir() { 98 | return webrtc::test::internal::WorkingDir(); 99 | } 100 | 101 | // Generate a temporary filename in a safe way. 102 | // Largely copied from talk/base/{unixfilesystem,win32filesystem}.cc. 103 | std::string TempFilename(const std::string& dir, const std::string& prefix) { 104 | #ifdef WIN32 105 | wchar_t filename[MAX_PATH]; 106 | if (::GetTempFileNameW(rtc::ToUtf16(dir).c_str(), 107 | rtc::ToUtf16(prefix).c_str(), 0, filename) != 0) 108 | return rtc::ToUtf8(filename); 109 | RTC_NOTREACHED(); 110 | return ""; 111 | #else 112 | int len = dir.size() + prefix.size() + 2 + 6; 113 | std::unique_ptr tempname(new char[len]); 114 | 115 | snprintf(tempname.get(), len, "%s/%sXXXXXX", dir.c_str(), prefix.c_str()); 116 | int fd = ::mkstemp(tempname.get()); 117 | if (fd == -1) { 118 | RTC_NOTREACHED(); 119 | return ""; 120 | } else { 121 | ::close(fd); 122 | } 123 | std::string ret(tempname.get()); 124 | return ret; 125 | #endif 126 | } 127 | 128 | std::string GenerateTempFilename(const std::string& dir, 129 | const std::string& prefix) { 130 | std::string filename = TempFilename(dir, prefix); 131 | RemoveFile(filename); 132 | return filename; 133 | } 134 | 135 | absl::optional> ReadDirectory(std::string path) { 136 | if (path.length() == 0) 137 | return absl::optional>(); 138 | 139 | #if defined(WEBRTC_WIN) 140 | // Append separator character if needed. 141 | if (path.back() != '\\') 142 | path += '\\'; 143 | 144 | // Init. 145 | WIN32_FIND_DATAW data; 146 | HANDLE handle = ::FindFirstFileW(rtc::ToUtf16(path + '*').c_str(), &data); 147 | if (handle == INVALID_HANDLE_VALUE) 148 | return absl::optional>(); 149 | 150 | // Populate output. 151 | std::vector found_entries; 152 | do { 153 | const std::string name = rtc::ToUtf8(data.cFileName); 154 | if (name != "." && name != "..") 155 | found_entries.emplace_back(path + name); 156 | } while (::FindNextFileW(handle, &data) == TRUE); 157 | 158 | // Release resources. 159 | if (handle != INVALID_HANDLE_VALUE) 160 | ::FindClose(handle); 161 | #else 162 | // Append separator character if needed. 163 | if (path.back() != '/') 164 | path += '/'; 165 | 166 | // Init. 167 | DIR* dir = ::opendir(path.c_str()); 168 | if (dir == nullptr) 169 | return absl::optional>(); 170 | 171 | // Populate output. 172 | std::vector found_entries; 173 | while (dirent* dirent = readdir(dir)) { 174 | const std::string& name = dirent->d_name; 175 | if (name != "." && name != "..") 176 | found_entries.emplace_back(path + name); 177 | } 178 | 179 | // Release resources. 180 | closedir(dir); 181 | #endif 182 | 183 | return absl::optional>(std::move(found_entries)); 184 | } 185 | 186 | bool CreateDir(const std::string& directory_name) { 187 | struct stat path_info = {0}; 188 | // Check if the path exists already: 189 | if (stat(directory_name.c_str(), &path_info) == 0) { 190 | if (!S_ISDIR(path_info.st_mode)) { 191 | fprintf(stderr, 192 | "Path %s exists but is not a directory! Remove this " 193 | "file and re-run to create the directory.\n", 194 | directory_name.c_str()); 195 | return false; 196 | } 197 | } else { 198 | #ifdef WIN32 199 | return _mkdir(directory_name.c_str()) == 0; 200 | #else 201 | return mkdir(directory_name.c_str(), S_IRWXU | S_IRWXG | S_IRWXO) == 0; 202 | #endif 203 | } 204 | return true; 205 | } 206 | 207 | bool RemoveDir(const std::string& directory_name) { 208 | #ifdef WIN32 209 | return RemoveDirectoryA(directory_name.c_str()) != FALSE; 210 | #else 211 | return rmdir(directory_name.c_str()) == 0; 212 | #endif 213 | } 214 | 215 | bool RemoveFile(const std::string& file_name) { 216 | #ifdef WIN32 217 | return DeleteFileA(file_name.c_str()) != FALSE; 218 | #else 219 | return unlink(file_name.c_str()) == 0; 220 | #endif 221 | } 222 | 223 | std::string ResourcePath(const std::string& name, 224 | const std::string& extension) { 225 | return webrtc::test::internal::ResourcePath(name, extension); 226 | } 227 | 228 | std::string JoinFilename(const std::string& dir, const std::string& name) { 229 | RTC_CHECK(!dir.empty()) << "Special cases not implemented."; 230 | return dir + kPathDelimiter + name; 231 | } 232 | 233 | size_t GetFileSize(const std::string& filename) { 234 | FILE* f = fopen(filename.c_str(), "rb"); 235 | size_t size = 0; 236 | if (f != NULL) { 237 | if (fseek(f, 0, SEEK_END) == 0) { 238 | size = ftell(f); 239 | } 240 | fclose(f); 241 | } 242 | return size; 243 | } 244 | 245 | } // namespace test 246 | } // namespace webrtc 247 | -------------------------------------------------------------------------------- /deps/libwebrtc/pc/test/fake_audio_capture_module.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012 The WebRTC project authors. All Rights Reserved. 3 | * 4 | * Use of this source code is governed by a BSD-style license 5 | * that can be found in the LICENSE file in the root of the source 6 | * tree. An additional intellectual property rights grant can be found 7 | * in the file PATENTS. All contributing project authors may 8 | * be found in the AUTHORS file in the root of the source tree. 9 | */ 10 | 11 | // This class implements an AudioCaptureModule that can be used to detect if 12 | // audio is being received properly if it is fed by another AudioCaptureModule 13 | // in some arbitrary audio pipeline where they are connected. It does not play 14 | // out or record any audio so it does not need access to any hardware and can 15 | // therefore be used in the gtest testing framework. 16 | 17 | // Note P postfix of a function indicates that it should only be called by the 18 | // processing thread. 19 | 20 | #ifndef PC_TEST_FAKE_AUDIO_CAPTURE_MODULE_H_ 21 | #define PC_TEST_FAKE_AUDIO_CAPTURE_MODULE_H_ 22 | 23 | #include 24 | #include 25 | 26 | #include 27 | 28 | #include "api/scoped_refptr.h" 29 | #include "api/sequence_checker.h" 30 | #include "modules/audio_device/include/audio_device.h" 31 | #include "modules/audio_device/include/audio_device_defines.h" 32 | #include "rtc_base/message_handler.h" 33 | #include "rtc_base/synchronization/mutex.h" 34 | #include "rtc_base/thread.h" 35 | #include "rtc_base/thread_annotations.h" 36 | #include "rtc_base/thread_message.h" 37 | 38 | namespace rtc { 39 | class Thread; 40 | } // namespace rtc 41 | 42 | class FakeAudioCaptureModule : public webrtc::AudioDeviceModule, 43 | public rtc::MessageHandlerAutoCleanup { 44 | public: 45 | typedef uint16_t Sample; 46 | 47 | // The value for the following constants have been derived by running VoE 48 | // using a real ADM. The constants correspond to 10ms of mono audio at 44kHz. 49 | static const size_t kNumberSamples = 440; 50 | static const size_t kNumberBytesPerSample = sizeof(Sample); 51 | 52 | // Creates a FakeAudioCaptureModule or returns NULL on failure. 53 | static rtc::scoped_refptr Create(); 54 | 55 | // Returns the number of frames that have been successfully pulled by the 56 | // instance. Note that correctly detecting success can only be done if the 57 | // pulled frame was generated/pushed from a FakeAudioCaptureModule. 58 | int frames_received() const RTC_LOCKS_EXCLUDED(mutex_); 59 | 60 | int32_t ActiveAudioLayer(AudioLayer* audio_layer) const override; 61 | 62 | // Note: Calling this method from a callback may result in deadlock. 63 | int32_t RegisterAudioCallback(webrtc::AudioTransport* audio_callback) override 64 | RTC_LOCKS_EXCLUDED(mutex_); 65 | 66 | int32_t Init() override; 67 | int32_t Terminate() override; 68 | bool Initialized() const override; 69 | 70 | int16_t PlayoutDevices() override; 71 | int16_t RecordingDevices() override; 72 | int32_t PlayoutDeviceName(uint16_t index, 73 | char name[webrtc::kAdmMaxDeviceNameSize], 74 | char guid[webrtc::kAdmMaxGuidSize]) override; 75 | int32_t RecordingDeviceName(uint16_t index, 76 | char name[webrtc::kAdmMaxDeviceNameSize], 77 | char guid[webrtc::kAdmMaxGuidSize]) override; 78 | 79 | int32_t SetPlayoutDevice(uint16_t index) override; 80 | int32_t SetPlayoutDevice(WindowsDeviceType device) override; 81 | int32_t SetRecordingDevice(uint16_t index) override; 82 | int32_t SetRecordingDevice(WindowsDeviceType device) override; 83 | 84 | int32_t PlayoutIsAvailable(bool* available) override; 85 | int32_t InitPlayout() override; 86 | bool PlayoutIsInitialized() const override; 87 | int32_t RecordingIsAvailable(bool* available) override; 88 | int32_t InitRecording() override; 89 | bool RecordingIsInitialized() const override; 90 | 91 | int32_t StartPlayout() RTC_LOCKS_EXCLUDED(mutex_) override; 92 | int32_t StopPlayout() RTC_LOCKS_EXCLUDED(mutex_) override; 93 | bool Playing() const RTC_LOCKS_EXCLUDED(mutex_) override; 94 | int32_t StartRecording() RTC_LOCKS_EXCLUDED(mutex_) override; 95 | int32_t StopRecording() RTC_LOCKS_EXCLUDED(mutex_) override; 96 | bool Recording() const RTC_LOCKS_EXCLUDED(mutex_) override; 97 | 98 | int32_t InitSpeaker() override; 99 | bool SpeakerIsInitialized() const override; 100 | int32_t InitMicrophone() override; 101 | bool MicrophoneIsInitialized() const override; 102 | 103 | int32_t SpeakerVolumeIsAvailable(bool* available) override; 104 | int32_t SetSpeakerVolume(uint32_t volume) override; 105 | int32_t SpeakerVolume(uint32_t* volume) const override; 106 | int32_t MaxSpeakerVolume(uint32_t* max_volume) const override; 107 | int32_t MinSpeakerVolume(uint32_t* min_volume) const override; 108 | 109 | int32_t MicrophoneVolumeIsAvailable(bool* available) override; 110 | int32_t SetMicrophoneVolume(uint32_t volume) 111 | RTC_LOCKS_EXCLUDED(mutex_) override; 112 | int32_t MicrophoneVolume(uint32_t* volume) const 113 | RTC_LOCKS_EXCLUDED(mutex_) override; 114 | int32_t MaxMicrophoneVolume(uint32_t* max_volume) const override; 115 | 116 | int32_t MinMicrophoneVolume(uint32_t* min_volume) const override; 117 | 118 | int32_t SpeakerMuteIsAvailable(bool* available) override; 119 | int32_t SetSpeakerMute(bool enable) override; 120 | int32_t SpeakerMute(bool* enabled) const override; 121 | 122 | int32_t MicrophoneMuteIsAvailable(bool* available) override; 123 | int32_t SetMicrophoneMute(bool enable) override; 124 | int32_t MicrophoneMute(bool* enabled) const override; 125 | 126 | int32_t StereoPlayoutIsAvailable(bool* available) const override; 127 | int32_t SetStereoPlayout(bool enable) override; 128 | int32_t StereoPlayout(bool* enabled) const override; 129 | int32_t StereoRecordingIsAvailable(bool* available) const override; 130 | int32_t SetStereoRecording(bool enable) override; 131 | int32_t StereoRecording(bool* enabled) const override; 132 | 133 | int32_t PlayoutDelay(uint16_t* delay_ms) const override; 134 | 135 | bool BuiltInAECIsAvailable() const override { return false; } 136 | int32_t EnableBuiltInAEC(bool enable) override { return -1; } 137 | bool BuiltInAGCIsAvailable() const override { return false; } 138 | int32_t EnableBuiltInAGC(bool enable) override { return -1; } 139 | bool BuiltInNSIsAvailable() const override { return false; } 140 | int32_t EnableBuiltInNS(bool enable) override { return -1; } 141 | 142 | int32_t GetPlayoutUnderrunCount() const override { return -1; } 143 | #if defined(WEBRTC_IOS) 144 | int GetPlayoutAudioParameters( 145 | webrtc::AudioParameters* params) const override { 146 | return -1; 147 | } 148 | int GetRecordAudioParameters(webrtc::AudioParameters* params) const override { 149 | return -1; 150 | } 151 | #endif // WEBRTC_IOS 152 | 153 | // End of functions inherited from webrtc::AudioDeviceModule. 154 | 155 | // The following function is inherited from rtc::MessageHandler. 156 | void OnMessage(rtc::Message* msg) override; 157 | 158 | protected: 159 | // The constructor is protected because the class needs to be created as a 160 | // reference counted object (for memory managment reasons). It could be 161 | // exposed in which case the burden of proper instantiation would be put on 162 | // the creator of a FakeAudioCaptureModule instance. To create an instance of 163 | // this class use the Create(..) API. 164 | FakeAudioCaptureModule(); 165 | // The destructor is protected because it is reference counted and should not 166 | // be deleted directly. 167 | virtual ~FakeAudioCaptureModule(); 168 | 169 | private: 170 | // Initializes the state of the FakeAudioCaptureModule. This API is called on 171 | // creation by the Create() API. 172 | bool Initialize(); 173 | // SetBuffer() sets all samples in send_buffer_ to `value`. 174 | void SetSendBuffer(int value); 175 | // Resets rec_buffer_. I.e., sets all rec_buffer_ samples to 0. 176 | void ResetRecBuffer(); 177 | // Returns true if rec_buffer_ contains one or more sample greater than or 178 | // equal to `value`. 179 | bool CheckRecBuffer(int value); 180 | 181 | // Returns true/false depending on if recording or playback has been 182 | // enabled/started. 183 | bool ShouldStartProcessing() RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_); 184 | 185 | // Starts or stops the pushing and pulling of audio frames. 186 | void UpdateProcessing(bool start) RTC_LOCKS_EXCLUDED(mutex_); 187 | 188 | // Starts the periodic calling of ProcessFrame() in a thread safe way. 189 | void StartProcessP(); 190 | // Periodcally called function that ensures that frames are pulled and pushed 191 | // periodically if enabled/started. 192 | void ProcessFrameP() RTC_LOCKS_EXCLUDED(mutex_); 193 | // Pulls frames from the registered webrtc::AudioTransport. 194 | void ReceiveFrameP() RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_); 195 | // Pushes frames to the registered webrtc::AudioTransport. 196 | void SendFrameP() RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_); 197 | 198 | // Callback for playout and recording. 199 | webrtc::AudioTransport* audio_callback_ RTC_GUARDED_BY(mutex_); 200 | 201 | bool recording_ RTC_GUARDED_BY( 202 | mutex_); // True when audio is being pushed from the instance. 203 | bool playing_ RTC_GUARDED_BY( 204 | mutex_); // True when audio is being pulled by the instance. 205 | 206 | bool play_is_initialized_; // True when the instance is ready to pull audio. 207 | bool rec_is_initialized_; // True when the instance is ready to push audio. 208 | 209 | // Input to and output from RecordedDataIsAvailable(..) makes it possible to 210 | // modify the current mic level. The implementation does not care about the 211 | // mic level so it just feeds back what it receives. 212 | uint32_t current_mic_level_ RTC_GUARDED_BY(mutex_); 213 | 214 | // next_frame_time_ is updated in a non-drifting manner to indicate the next 215 | // wall clock time the next frame should be generated and received. started_ 216 | // ensures that next_frame_time_ can be initialized properly on first call. 217 | bool started_ RTC_GUARDED_BY(mutex_); 218 | int64_t next_frame_time_ RTC_GUARDED_BY(process_thread_checker_); 219 | 220 | std::unique_ptr process_thread_; 221 | 222 | // Buffer for storing samples received from the webrtc::AudioTransport. 223 | char rec_buffer_[kNumberSamples * kNumberBytesPerSample]; 224 | // Buffer for samples to send to the webrtc::AudioTransport. 225 | char send_buffer_[kNumberSamples * kNumberBytesPerSample]; 226 | 227 | // Counter of frames received that have samples of high enough amplitude to 228 | // indicate that the frames are not faked somewhere in the audio pipeline 229 | // (e.g. by a jitter buffer). 230 | int frames_received_; 231 | 232 | // Protects variables that are accessed from process_thread_ and 233 | // the main thread. 234 | mutable webrtc::Mutex mutex_; 235 | webrtc::SequenceChecker process_thread_checker_; 236 | }; 237 | 238 | #endif // PC_TEST_FAKE_AUDIO_CAPTURE_MODULE_H_ 239 | -------------------------------------------------------------------------------- /deps/libwebrtc/test/frame_generator_capturer.cc: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved. 3 | * 4 | * Use of this source code is governed by a BSD-style license 5 | * that can be found in the LICENSE file in the root of the source 6 | * tree. An additional intellectual property rights grant can be found 7 | * in the file PATENTS. All contributing project authors may 8 | * be found in the AUTHORS file in the root of the source tree. 9 | */ 10 | 11 | #include "test/frame_generator_capturer.h" 12 | 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | 20 | #include "absl/strings/match.h" 21 | #include "api/test/create_frame_generator.h" 22 | #include "rtc_base/checks.h" 23 | #include "rtc_base/logging.h" 24 | #include "rtc_base/task_queue.h" 25 | #include "rtc_base/time_utils.h" 26 | #include "system_wrappers/include/clock.h" 27 | #include "test/testsupport/file_utils.h" 28 | 29 | namespace webrtc { 30 | namespace test { 31 | namespace { 32 | std::string TransformFilePath(std::string path) { 33 | static const std::string resource_prefix = "res://"; 34 | int ext_pos = path.rfind('.'); 35 | if (ext_pos < 0) { 36 | return test::ResourcePath(path, "yuv"); 37 | } else if (absl::StartsWith(path, resource_prefix)) { 38 | std::string name = path.substr(resource_prefix.length(), ext_pos); 39 | std::string ext = path.substr(ext_pos, path.size()); 40 | return test::ResourcePath(name, ext); 41 | } 42 | return path; 43 | } 44 | } // namespace 45 | 46 | FrameGeneratorCapturer::FrameGeneratorCapturer( 47 | Clock* clock, 48 | std::unique_ptr frame_generator, 49 | int target_fps, 50 | TaskQueueFactory& task_queue_factory) 51 | : clock_(clock), 52 | sending_(true), 53 | sink_wants_observer_(nullptr), 54 | frame_generator_(std::move(frame_generator)), 55 | source_fps_(target_fps), 56 | target_capture_fps_(target_fps), 57 | first_frame_capture_time_(-1), 58 | task_queue_(task_queue_factory.CreateTaskQueue( 59 | "FrameGenCapQ", 60 | TaskQueueFactory::Priority::HIGH)) { 61 | RTC_DCHECK(frame_generator_); 62 | RTC_DCHECK_GT(target_fps, 0); 63 | } 64 | 65 | FrameGeneratorCapturer::~FrameGeneratorCapturer() { 66 | Stop(); 67 | } 68 | 69 | std::unique_ptr FrameGeneratorCapturer::Create( 70 | Clock* clock, 71 | TaskQueueFactory& task_queue_factory, 72 | FrameGeneratorCapturerConfig::SquaresVideo config) { 73 | return std::make_unique( 74 | clock, 75 | CreateSquareFrameGenerator(config.width, config.height, 76 | config.pixel_format, config.num_squares), 77 | config.framerate, task_queue_factory); 78 | } 79 | std::unique_ptr FrameGeneratorCapturer::Create( 80 | Clock* clock, 81 | TaskQueueFactory& task_queue_factory, 82 | FrameGeneratorCapturerConfig::SquareSlides config) { 83 | return std::make_unique( 84 | clock, 85 | CreateSlideFrameGenerator( 86 | config.width, config.height, 87 | /*frame_repeat_count*/ config.change_interval.seconds() * 88 | config.framerate), 89 | config.framerate, task_queue_factory); 90 | } 91 | std::unique_ptr FrameGeneratorCapturer::Create( 92 | Clock* clock, 93 | TaskQueueFactory& task_queue_factory, 94 | FrameGeneratorCapturerConfig::VideoFile config) { 95 | RTC_CHECK(config.width && config.height); 96 | return std::make_unique( 97 | clock, 98 | CreateFromYuvFileFrameGenerator({TransformFilePath(config.name)}, 99 | config.width, config.height, 100 | /*frame_repeat_count*/ 1), 101 | config.framerate, task_queue_factory); 102 | } 103 | 104 | std::unique_ptr FrameGeneratorCapturer::Create( 105 | Clock* clock, 106 | TaskQueueFactory& task_queue_factory, 107 | FrameGeneratorCapturerConfig::ImageSlides config) { 108 | std::unique_ptr slides_generator; 109 | std::vector paths = config.paths; 110 | for (std::string& path : paths) 111 | path = TransformFilePath(path); 112 | 113 | if (config.crop.width || config.crop.height) { 114 | TimeDelta pause_duration = 115 | config.change_interval - config.crop.scroll_duration; 116 | RTC_CHECK_GE(pause_duration, TimeDelta::Zero()); 117 | int crop_width = config.crop.width.value_or(config.width); 118 | int crop_height = config.crop.height.value_or(config.height); 119 | RTC_CHECK_LE(crop_width, config.width); 120 | RTC_CHECK_LE(crop_height, config.height); 121 | slides_generator = CreateScrollingInputFromYuvFilesFrameGenerator( 122 | clock, paths, config.width, config.height, crop_width, crop_height, 123 | config.crop.scroll_duration.ms(), pause_duration.ms()); 124 | } else { 125 | slides_generator = CreateFromYuvFileFrameGenerator( 126 | paths, config.width, config.height, 127 | /*frame_repeat_count*/ config.change_interval.seconds() * 128 | config.framerate); 129 | } 130 | return std::make_unique( 131 | clock, std::move(slides_generator), config.framerate, task_queue_factory); 132 | } 133 | 134 | std::unique_ptr FrameGeneratorCapturer::Create( 135 | Clock* clock, 136 | TaskQueueFactory& task_queue_factory, 137 | const FrameGeneratorCapturerConfig& config) { 138 | if (config.video_file) { 139 | return Create(clock, task_queue_factory, *config.video_file); 140 | } else if (config.image_slides) { 141 | return Create(clock, task_queue_factory, *config.image_slides); 142 | } else if (config.squares_slides) { 143 | return Create(clock, task_queue_factory, *config.squares_slides); 144 | } else { 145 | return Create(clock, task_queue_factory, 146 | config.squares_video.value_or( 147 | FrameGeneratorCapturerConfig::SquaresVideo())); 148 | } 149 | } 150 | 151 | void FrameGeneratorCapturer::SetFakeRotation(VideoRotation rotation) { 152 | MutexLock lock(&lock_); 153 | fake_rotation_ = rotation; 154 | } 155 | 156 | void FrameGeneratorCapturer::SetFakeColorSpace( 157 | absl::optional color_space) { 158 | MutexLock lock(&lock_); 159 | fake_color_space_ = color_space; 160 | } 161 | 162 | bool FrameGeneratorCapturer::Init() { 163 | // This check is added because frame_generator_ might be file based and should 164 | // not crash because a file moved. 165 | if (frame_generator_.get() == nullptr) 166 | return false; 167 | 168 | frame_task_ = RepeatingTaskHandle::DelayedStart( 169 | task_queue_.Get(), 170 | TimeDelta::Seconds(1) / GetCurrentConfiguredFramerate(), [this] { 171 | InsertFrame(); 172 | return TimeDelta::Seconds(1) / GetCurrentConfiguredFramerate(); 173 | }); 174 | return true; 175 | } 176 | 177 | void FrameGeneratorCapturer::InsertFrame() { 178 | MutexLock lock(&lock_); 179 | if (sending_) { 180 | FrameGeneratorInterface::VideoFrameData frame_data = 181 | frame_generator_->NextFrame(); 182 | // TODO(srte): Use more advanced frame rate control to allow arbritrary 183 | // fractions. 184 | int decimation = 185 | std::round(static_cast(source_fps_) / target_capture_fps_); 186 | for (int i = 1; i < decimation; ++i) 187 | frame_data = frame_generator_->NextFrame(); 188 | 189 | VideoFrame frame = VideoFrame::Builder() 190 | .set_video_frame_buffer(frame_data.buffer) 191 | .set_rotation(fake_rotation_) 192 | .set_timestamp_us(clock_->TimeInMicroseconds()) 193 | .set_ntp_time_ms(clock_->CurrentNtpInMilliseconds()) 194 | .set_update_rect(frame_data.update_rect) 195 | .set_color_space(fake_color_space_) 196 | .build(); 197 | if (first_frame_capture_time_ == -1) { 198 | first_frame_capture_time_ = frame.ntp_time_ms(); 199 | } 200 | 201 | TestVideoCapturer::OnFrame(frame); 202 | } 203 | } 204 | 205 | void FrameGeneratorCapturer::Start() { 206 | { 207 | MutexLock lock(&lock_); 208 | sending_ = true; 209 | } 210 | if (!frame_task_.Running()) { 211 | frame_task_ = RepeatingTaskHandle::Start(task_queue_.Get(), [this] { 212 | InsertFrame(); 213 | return TimeDelta::Seconds(1) / GetCurrentConfiguredFramerate(); 214 | }); 215 | } 216 | } 217 | 218 | void FrameGeneratorCapturer::Stop() { 219 | MutexLock lock(&lock_); 220 | sending_ = false; 221 | } 222 | 223 | void FrameGeneratorCapturer::ChangeResolution(size_t width, size_t height) { 224 | MutexLock lock(&lock_); 225 | frame_generator_->ChangeResolution(width, height); 226 | } 227 | 228 | void FrameGeneratorCapturer::ChangeFramerate(int target_framerate) { 229 | MutexLock lock(&lock_); 230 | RTC_CHECK(target_capture_fps_ > 0); 231 | if (target_framerate > source_fps_) 232 | RTC_LOG(LS_WARNING) << "Target framerate clamped from " << target_framerate 233 | << " to " << source_fps_; 234 | if (source_fps_ % target_capture_fps_ != 0) { 235 | int decimation = 236 | std::round(static_cast(source_fps_) / target_capture_fps_); 237 | int effective_rate = target_capture_fps_ / decimation; 238 | RTC_LOG(LS_WARNING) << "Target framerate, " << target_framerate 239 | << ", is an uneven fraction of the source rate, " 240 | << source_fps_ 241 | << ". The framerate will be :" << effective_rate; 242 | } 243 | target_capture_fps_ = std::min(source_fps_, target_framerate); 244 | } 245 | 246 | void FrameGeneratorCapturer::SetSinkWantsObserver(SinkWantsObserver* observer) { 247 | MutexLock lock(&lock_); 248 | RTC_DCHECK(!sink_wants_observer_); 249 | sink_wants_observer_ = observer; 250 | } 251 | 252 | void FrameGeneratorCapturer::AddOrUpdateSink( 253 | rtc::VideoSinkInterface* sink, 254 | const rtc::VideoSinkWants& wants) { 255 | TestVideoCapturer::AddOrUpdateSink(sink, wants); 256 | MutexLock lock(&lock_); 257 | if (sink_wants_observer_) { 258 | // Tests need to observe unmodified sink wants. 259 | sink_wants_observer_->OnSinkWantsChanged(sink, wants); 260 | } 261 | UpdateFps(GetSinkWants().max_framerate_fps); 262 | } 263 | 264 | void FrameGeneratorCapturer::RemoveSink( 265 | rtc::VideoSinkInterface* sink) { 266 | TestVideoCapturer::RemoveSink(sink); 267 | 268 | MutexLock lock(&lock_); 269 | UpdateFps(GetSinkWants().max_framerate_fps); 270 | } 271 | 272 | void FrameGeneratorCapturer::UpdateFps(int max_fps) { 273 | if (max_fps < target_capture_fps_) { 274 | wanted_fps_.emplace(max_fps); 275 | } else { 276 | wanted_fps_.reset(); 277 | } 278 | } 279 | 280 | void FrameGeneratorCapturer::ForceFrame() { 281 | // One-time non-repeating task, 282 | task_queue_.PostTask([this] { InsertFrame(); }); 283 | } 284 | 285 | int FrameGeneratorCapturer::GetCurrentConfiguredFramerate() { 286 | MutexLock lock(&lock_); 287 | if (wanted_fps_ && *wanted_fps_ < target_capture_fps_) 288 | return *wanted_fps_; 289 | return target_capture_fps_; 290 | } 291 | 292 | } // namespace test 293 | } // namespace webrtc 294 | -------------------------------------------------------------------------------- /deps/libwebrtc/test/frame_generator.cc: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved. 3 | * 4 | * Use of this source code is governed by a BSD-style license 5 | * that can be found in the LICENSE file in the root of the source 6 | * tree. An additional intellectual property rights grant can be found 7 | * in the file PATENTS. All contributing project authors may 8 | * be found in the AUTHORS file in the root of the source tree. 9 | */ 10 | #include "test/frame_generator.h" 11 | 12 | #include 13 | 14 | #include 15 | #include 16 | #include 17 | 18 | #include "api/video/i010_buffer.h" 19 | #include "api/video/nv12_buffer.h" 20 | #include "api/video/video_rotation.h" 21 | #include "common_video/include/video_frame_buffer.h" 22 | #include "common_video/libyuv/include/webrtc_libyuv.h" 23 | #include "rtc_base/checks.h" 24 | #include "test/frame_utils.h" 25 | 26 | namespace webrtc { 27 | namespace test { 28 | 29 | SquareGenerator::SquareGenerator(int width, 30 | int height, 31 | OutputType type, 32 | int num_squares) 33 | : type_(type) { 34 | ChangeResolution(width, height); 35 | for (int i = 0; i < num_squares; ++i) { 36 | squares_.emplace_back(new Square(width, height, i + 1)); 37 | } 38 | } 39 | 40 | void SquareGenerator::ChangeResolution(size_t width, size_t height) { 41 | MutexLock lock(&mutex_); 42 | width_ = static_cast(width); 43 | height_ = static_cast(height); 44 | RTC_CHECK(width_ > 0); 45 | RTC_CHECK(height_ > 0); 46 | } 47 | 48 | rtc::scoped_refptr SquareGenerator::CreateI420Buffer(int width, 49 | int height) { 50 | rtc::scoped_refptr buffer(I420Buffer::Create(width, height)); 51 | memset(buffer->MutableDataY(), 127, height * buffer->StrideY()); 52 | memset(buffer->MutableDataU(), 127, 53 | buffer->ChromaHeight() * buffer->StrideU()); 54 | memset(buffer->MutableDataV(), 127, 55 | buffer->ChromaHeight() * buffer->StrideV()); 56 | return buffer; 57 | } 58 | 59 | FrameGeneratorInterface::VideoFrameData SquareGenerator::NextFrame() { 60 | MutexLock lock(&mutex_); 61 | 62 | rtc::scoped_refptr buffer = nullptr; 63 | switch (type_) { 64 | case OutputType::kI420: 65 | case OutputType::kI010: 66 | case OutputType::kNV12: { 67 | buffer = CreateI420Buffer(width_, height_); 68 | break; 69 | } 70 | case OutputType::kI420A: { 71 | rtc::scoped_refptr yuv_buffer = 72 | CreateI420Buffer(width_, height_); 73 | rtc::scoped_refptr axx_buffer = 74 | CreateI420Buffer(width_, height_); 75 | buffer = WrapI420ABuffer(yuv_buffer->width(), yuv_buffer->height(), 76 | yuv_buffer->DataY(), yuv_buffer->StrideY(), 77 | yuv_buffer->DataU(), yuv_buffer->StrideU(), 78 | yuv_buffer->DataV(), yuv_buffer->StrideV(), 79 | axx_buffer->DataY(), axx_buffer->StrideY(), 80 | // To keep references alive. 81 | [yuv_buffer, axx_buffer] {}); 82 | break; 83 | } 84 | default: 85 | RTC_NOTREACHED() << "The given output format is not supported."; 86 | } 87 | 88 | for (const auto& square : squares_) 89 | square->Draw(buffer); 90 | 91 | if (type_ == OutputType::kI010) { 92 | buffer = I010Buffer::Copy(*buffer->ToI420()); 93 | } else if (type_ == OutputType::kNV12) { 94 | buffer = NV12Buffer::Copy(*buffer->ToI420()); 95 | } 96 | 97 | return VideoFrameData(buffer, absl::nullopt); 98 | } 99 | 100 | SquareGenerator::Square::Square(int width, int height, int seed) 101 | : random_generator_(seed), 102 | x_(random_generator_.Rand(0, width)), 103 | y_(random_generator_.Rand(0, height)), 104 | length_(random_generator_.Rand(1, width > 4 ? width / 4 : 1)), 105 | yuv_y_(random_generator_.Rand(0, 255)), 106 | yuv_u_(random_generator_.Rand(0, 255)), 107 | yuv_v_(random_generator_.Rand(0, 255)), 108 | yuv_a_(random_generator_.Rand(0, 255)) {} 109 | 110 | void SquareGenerator::Square::Draw( 111 | const rtc::scoped_refptr& frame_buffer) { 112 | RTC_DCHECK(frame_buffer->type() == VideoFrameBuffer::Type::kI420 || 113 | frame_buffer->type() == VideoFrameBuffer::Type::kI420A); 114 | rtc::scoped_refptr buffer = frame_buffer->ToI420(); 115 | int length_cap = std::min(buffer->height(), buffer->width()) / 4; 116 | int length = std::min(length_, length_cap); 117 | x_ = (x_ + random_generator_.Rand(0, 4)) % (buffer->width() - length); 118 | y_ = (y_ + random_generator_.Rand(0, 4)) % (buffer->height() - length); 119 | for (int y = y_; y < y_ + length; ++y) { 120 | uint8_t* pos_y = 121 | (const_cast(buffer->DataY()) + x_ + y * buffer->StrideY()); 122 | memset(pos_y, yuv_y_, length); 123 | } 124 | 125 | for (int y = y_; y < y_ + length; y = y + 2) { 126 | uint8_t* pos_u = (const_cast(buffer->DataU()) + x_ / 2 + 127 | y / 2 * buffer->StrideU()); 128 | memset(pos_u, yuv_u_, length / 2); 129 | uint8_t* pos_v = (const_cast(buffer->DataV()) + x_ / 2 + 130 | y / 2 * buffer->StrideV()); 131 | memset(pos_v, yuv_v_, length / 2); 132 | } 133 | 134 | if (frame_buffer->type() == VideoFrameBuffer::Type::kI420) 135 | return; 136 | 137 | // Optionally draw on alpha plane if given. 138 | const webrtc::I420ABufferInterface* yuva_buffer = frame_buffer->GetI420A(); 139 | for (int y = y_; y < y_ + length; ++y) { 140 | uint8_t* pos_y = (const_cast(yuva_buffer->DataA()) + x_ + 141 | y * yuva_buffer->StrideA()); 142 | memset(pos_y, yuv_a_, length); 143 | } 144 | } 145 | 146 | YuvFileGenerator::YuvFileGenerator(std::vector files, 147 | size_t width, 148 | size_t height, 149 | int frame_repeat_count) 150 | : file_index_(0), 151 | frame_index_(std::numeric_limits::max()), 152 | files_(files), 153 | width_(width), 154 | height_(height), 155 | frame_size_(CalcBufferSize(VideoType::kI420, 156 | static_cast(width_), 157 | static_cast(height_))), 158 | frame_buffer_(new uint8_t[frame_size_]), 159 | frame_display_count_(frame_repeat_count), 160 | current_display_count_(0) { 161 | RTC_DCHECK_GT(width, 0); 162 | RTC_DCHECK_GT(height, 0); 163 | RTC_DCHECK_GT(frame_repeat_count, 0); 164 | } 165 | 166 | YuvFileGenerator::~YuvFileGenerator() { 167 | for (FILE* file : files_) 168 | fclose(file); 169 | } 170 | 171 | FrameGeneratorInterface::VideoFrameData YuvFileGenerator::NextFrame() { 172 | // Empty update by default. 173 | VideoFrame::UpdateRect update_rect{0, 0, 0, 0}; 174 | if (current_display_count_ == 0) { 175 | const bool got_new_frame = ReadNextFrame(); 176 | // Full update on a new frame from file. 177 | if (got_new_frame) { 178 | update_rect = VideoFrame::UpdateRect{0, 0, static_cast(width_), 179 | static_cast(height_)}; 180 | } 181 | } 182 | if (++current_display_count_ >= frame_display_count_) 183 | current_display_count_ = 0; 184 | 185 | return VideoFrameData(last_read_buffer_, update_rect); 186 | } 187 | 188 | bool YuvFileGenerator::ReadNextFrame() { 189 | size_t prev_frame_index = frame_index_; 190 | size_t prev_file_index = file_index_; 191 | last_read_buffer_ = test::ReadI420Buffer( 192 | static_cast(width_), static_cast(height_), files_[file_index_]); 193 | ++frame_index_; 194 | if (!last_read_buffer_) { 195 | // No more frames to read in this file, rewind and move to next file. 196 | rewind(files_[file_index_]); 197 | 198 | frame_index_ = 0; 199 | file_index_ = (file_index_ + 1) % files_.size(); 200 | last_read_buffer_ = 201 | test::ReadI420Buffer(static_cast(width_), 202 | static_cast(height_), files_[file_index_]); 203 | RTC_CHECK(last_read_buffer_); 204 | } 205 | return frame_index_ != prev_frame_index || file_index_ != prev_file_index; 206 | } 207 | 208 | SlideGenerator::SlideGenerator(int width, int height, int frame_repeat_count) 209 | : width_(width), 210 | height_(height), 211 | frame_display_count_(frame_repeat_count), 212 | current_display_count_(0), 213 | random_generator_(1234) { 214 | RTC_DCHECK_GT(width, 0); 215 | RTC_DCHECK_GT(height, 0); 216 | RTC_DCHECK_GT(frame_repeat_count, 0); 217 | } 218 | 219 | FrameGeneratorInterface::VideoFrameData SlideGenerator::NextFrame() { 220 | if (current_display_count_ == 0) 221 | GenerateNewFrame(); 222 | if (++current_display_count_ >= frame_display_count_) 223 | current_display_count_ = 0; 224 | 225 | return VideoFrameData(buffer_, absl::nullopt); 226 | } 227 | 228 | void SlideGenerator::GenerateNewFrame() { 229 | // The squares should have a varying order of magnitude in order 230 | // to simulate variation in the slides' complexity. 231 | const int kSquareNum = 1 << (4 + (random_generator_.Rand(0, 3) * 2)); 232 | 233 | buffer_ = I420Buffer::Create(width_, height_); 234 | memset(buffer_->MutableDataY(), 127, height_ * buffer_->StrideY()); 235 | memset(buffer_->MutableDataU(), 127, 236 | buffer_->ChromaHeight() * buffer_->StrideU()); 237 | memset(buffer_->MutableDataV(), 127, 238 | buffer_->ChromaHeight() * buffer_->StrideV()); 239 | 240 | for (int i = 0; i < kSquareNum; ++i) { 241 | int length = random_generator_.Rand(1, width_ > 4 ? width_ / 4 : 1); 242 | // Limit the length of later squares so that they don't overwrite the 243 | // previous ones too much. 244 | length = (length * (kSquareNum - i)) / kSquareNum; 245 | 246 | int x = random_generator_.Rand(0, width_ - length); 247 | int y = random_generator_.Rand(0, height_ - length); 248 | uint8_t yuv_y = random_generator_.Rand(0, 255); 249 | uint8_t yuv_u = random_generator_.Rand(0, 255); 250 | uint8_t yuv_v = random_generator_.Rand(0, 255); 251 | 252 | for (int yy = y; yy < y + length; ++yy) { 253 | uint8_t* pos_y = (buffer_->MutableDataY() + x + yy * buffer_->StrideY()); 254 | memset(pos_y, yuv_y, length); 255 | } 256 | for (int yy = y; yy < y + length; yy += 2) { 257 | uint8_t* pos_u = 258 | (buffer_->MutableDataU() + x / 2 + yy / 2 * buffer_->StrideU()); 259 | memset(pos_u, yuv_u, length / 2); 260 | uint8_t* pos_v = 261 | (buffer_->MutableDataV() + x / 2 + yy / 2 * buffer_->StrideV()); 262 | memset(pos_v, yuv_v, length / 2); 263 | } 264 | } 265 | } 266 | 267 | ScrollingImageFrameGenerator::ScrollingImageFrameGenerator( 268 | Clock* clock, 269 | const std::vector& files, 270 | size_t source_width, 271 | size_t source_height, 272 | size_t target_width, 273 | size_t target_height, 274 | int64_t scroll_time_ms, 275 | int64_t pause_time_ms) 276 | : clock_(clock), 277 | start_time_(clock->TimeInMilliseconds()), 278 | scroll_time_(scroll_time_ms), 279 | pause_time_(pause_time_ms), 280 | num_frames_(files.size()), 281 | target_width_(static_cast(target_width)), 282 | target_height_(static_cast(target_height)), 283 | current_frame_num_(num_frames_ - 1), 284 | prev_frame_not_scrolled_(false), 285 | current_source_frame_(nullptr, absl::nullopt), 286 | current_frame_(nullptr, absl::nullopt), 287 | file_generator_(files, source_width, source_height, 1) { 288 | RTC_DCHECK(clock_ != nullptr); 289 | RTC_DCHECK_GT(num_frames_, 0); 290 | RTC_DCHECK_GE(source_height, target_height); 291 | RTC_DCHECK_GE(source_width, target_width); 292 | RTC_DCHECK_GE(scroll_time_ms, 0); 293 | RTC_DCHECK_GE(pause_time_ms, 0); 294 | RTC_DCHECK_GT(scroll_time_ms + pause_time_ms, 0); 295 | } 296 | 297 | FrameGeneratorInterface::VideoFrameData 298 | ScrollingImageFrameGenerator::NextFrame() { 299 | const int64_t kFrameDisplayTime = scroll_time_ + pause_time_; 300 | const int64_t now = clock_->TimeInMilliseconds(); 301 | int64_t ms_since_start = now - start_time_; 302 | 303 | size_t frame_num = (ms_since_start / kFrameDisplayTime) % num_frames_; 304 | UpdateSourceFrame(frame_num); 305 | 306 | bool cur_frame_not_scrolled; 307 | 308 | double scroll_factor; 309 | int64_t time_into_frame = ms_since_start % kFrameDisplayTime; 310 | if (time_into_frame < scroll_time_) { 311 | scroll_factor = static_cast(time_into_frame) / scroll_time_; 312 | cur_frame_not_scrolled = false; 313 | } else { 314 | scroll_factor = 1.0; 315 | cur_frame_not_scrolled = true; 316 | } 317 | CropSourceToScrolledImage(scroll_factor); 318 | 319 | bool same_scroll_position = 320 | prev_frame_not_scrolled_ && cur_frame_not_scrolled; 321 | if (!same_scroll_position) { 322 | // If scrolling is not finished yet, force full frame update. 323 | current_frame_.update_rect = 324 | VideoFrame::UpdateRect{0, 0, target_width_, target_height_}; 325 | } 326 | prev_frame_not_scrolled_ = cur_frame_not_scrolled; 327 | 328 | return current_frame_; 329 | } 330 | 331 | void ScrollingImageFrameGenerator::UpdateSourceFrame(size_t frame_num) { 332 | VideoFrame::UpdateRect acc_update{0, 0, 0, 0}; 333 | while (current_frame_num_ != frame_num) { 334 | current_source_frame_ = file_generator_.NextFrame(); 335 | if (current_source_frame_.update_rect) { 336 | acc_update.Union(*current_source_frame_.update_rect); 337 | } 338 | current_frame_num_ = (current_frame_num_ + 1) % num_frames_; 339 | } 340 | current_source_frame_.update_rect = acc_update; 341 | } 342 | 343 | void ScrollingImageFrameGenerator::CropSourceToScrolledImage( 344 | double scroll_factor) { 345 | int scroll_margin_x = current_source_frame_.buffer->width() - target_width_; 346 | int pixels_scrolled_x = 347 | static_cast(scroll_margin_x * scroll_factor + 0.5); 348 | int scroll_margin_y = current_source_frame_.buffer->height() - target_height_; 349 | int pixels_scrolled_y = 350 | static_cast(scroll_margin_y * scroll_factor + 0.5); 351 | 352 | rtc::scoped_refptr i420_buffer = 353 | current_source_frame_.buffer->ToI420(); 354 | int offset_y = 355 | (i420_buffer->StrideY() * pixels_scrolled_y) + pixels_scrolled_x; 356 | int offset_u = (i420_buffer->StrideU() * (pixels_scrolled_y / 2)) + 357 | (pixels_scrolled_x / 2); 358 | int offset_v = (i420_buffer->StrideV() * (pixels_scrolled_y / 2)) + 359 | (pixels_scrolled_x / 2); 360 | 361 | VideoFrame::UpdateRect update_rect = 362 | current_source_frame_.update_rect->IsEmpty() 363 | ? VideoFrame::UpdateRect{0, 0, 0, 0} 364 | : VideoFrame::UpdateRect{0, 0, target_width_, target_height_}; 365 | current_frame_ = VideoFrameData( 366 | WrapI420Buffer(target_width_, target_height_, 367 | &i420_buffer->DataY()[offset_y], i420_buffer->StrideY(), 368 | &i420_buffer->DataU()[offset_u], i420_buffer->StrideU(), 369 | &i420_buffer->DataV()[offset_v], i420_buffer->StrideV(), 370 | // To keep reference alive. 371 | [i420_buffer] {}), 372 | update_rect); 373 | } 374 | 375 | } // namespace test 376 | } // namespace webrtc 377 | -------------------------------------------------------------------------------- /deps/libwebrtc/pc/test/fake_audio_capture_module.cc: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012 The WebRTC project authors. All Rights Reserved. 3 | * 4 | * Use of this source code is governed by a BSD-style license 5 | * that can be found in the LICENSE file in the root of the source 6 | * tree. An additional intellectual property rights grant can be found 7 | * in the file PATENTS. All contributing project authors may 8 | * be found in the AUTHORS file in the root of the source tree. 9 | */ 10 | 11 | #include "pc/test/fake_audio_capture_module.h" 12 | 13 | #include 14 | 15 | #include "rtc_base/checks.h" 16 | #include "rtc_base/location.h" 17 | #include "rtc_base/ref_counted_object.h" 18 | #include "rtc_base/thread.h" 19 | #include "rtc_base/time_utils.h" 20 | 21 | // Audio sample value that is high enough that it doesn't occur naturally when 22 | // frames are being faked. E.g. NetEq will not generate this large sample value 23 | // unless it has received an audio frame containing a sample of this value. 24 | // Even simpler buffers would likely just contain audio sample values of 0. 25 | static const int kHighSampleValue = 10000; 26 | 27 | // Constants here are derived by running VoE using a real ADM. 28 | // The constants correspond to 10ms of mono audio at 44kHz. 29 | static const int kTimePerFrameMs = 10; 30 | static const uint8_t kNumberOfChannels = 1; 31 | static const int kSamplesPerSecond = 44000; 32 | static const int kTotalDelayMs = 0; 33 | static const int kClockDriftMs = 0; 34 | static const uint32_t kMaxVolume = 14392; 35 | 36 | enum { 37 | MSG_START_PROCESS, 38 | MSG_RUN_PROCESS, 39 | }; 40 | 41 | FakeAudioCaptureModule::FakeAudioCaptureModule() 42 | : audio_callback_(nullptr), 43 | recording_(false), 44 | playing_(false), 45 | play_is_initialized_(false), 46 | rec_is_initialized_(false), 47 | current_mic_level_(kMaxVolume), 48 | started_(false), 49 | next_frame_time_(0), 50 | frames_received_(0) { 51 | process_thread_checker_.Detach(); 52 | } 53 | 54 | FakeAudioCaptureModule::~FakeAudioCaptureModule() { 55 | if (process_thread_) { 56 | process_thread_->Stop(); 57 | } 58 | } 59 | 60 | rtc::scoped_refptr FakeAudioCaptureModule::Create() { 61 | auto capture_module = rtc::make_ref_counted(); 62 | if (!capture_module->Initialize()) { 63 | return nullptr; 64 | } 65 | return capture_module; 66 | } 67 | 68 | int FakeAudioCaptureModule::frames_received() const { 69 | webrtc::MutexLock lock(&mutex_); 70 | return frames_received_; 71 | } 72 | 73 | int32_t FakeAudioCaptureModule::ActiveAudioLayer( 74 | AudioLayer* /*audio_layer*/) const { 75 | RTC_NOTREACHED(); 76 | return 0; 77 | } 78 | 79 | int32_t FakeAudioCaptureModule::RegisterAudioCallback( 80 | webrtc::AudioTransport* audio_callback) { 81 | webrtc::MutexLock lock(&mutex_); 82 | audio_callback_ = audio_callback; 83 | return 0; 84 | } 85 | 86 | int32_t FakeAudioCaptureModule::Init() { 87 | // Initialize is called by the factory method. Safe to ignore this Init call. 88 | return 0; 89 | } 90 | 91 | int32_t FakeAudioCaptureModule::Terminate() { 92 | // Clean up in the destructor. No action here, just success. 93 | return 0; 94 | } 95 | 96 | bool FakeAudioCaptureModule::Initialized() const { 97 | RTC_NOTREACHED(); 98 | return 0; 99 | } 100 | 101 | int16_t FakeAudioCaptureModule::PlayoutDevices() { 102 | RTC_NOTREACHED(); 103 | return 0; 104 | } 105 | 106 | int16_t FakeAudioCaptureModule::RecordingDevices() { 107 | RTC_NOTREACHED(); 108 | return 0; 109 | } 110 | 111 | int32_t FakeAudioCaptureModule::PlayoutDeviceName( 112 | uint16_t /*index*/, 113 | char /*name*/[webrtc::kAdmMaxDeviceNameSize], 114 | char /*guid*/[webrtc::kAdmMaxGuidSize]) { 115 | RTC_NOTREACHED(); 116 | return 0; 117 | } 118 | 119 | int32_t FakeAudioCaptureModule::RecordingDeviceName( 120 | uint16_t /*index*/, 121 | char /*name*/[webrtc::kAdmMaxDeviceNameSize], 122 | char /*guid*/[webrtc::kAdmMaxGuidSize]) { 123 | RTC_NOTREACHED(); 124 | return 0; 125 | } 126 | 127 | int32_t FakeAudioCaptureModule::SetPlayoutDevice(uint16_t /*index*/) { 128 | // No playout device, just playing from file. Return success. 129 | return 0; 130 | } 131 | 132 | int32_t FakeAudioCaptureModule::SetPlayoutDevice(WindowsDeviceType /*device*/) { 133 | if (play_is_initialized_) { 134 | return -1; 135 | } 136 | return 0; 137 | } 138 | 139 | int32_t FakeAudioCaptureModule::SetRecordingDevice(uint16_t /*index*/) { 140 | // No recording device, just dropping audio. Return success. 141 | return 0; 142 | } 143 | 144 | int32_t FakeAudioCaptureModule::SetRecordingDevice( 145 | WindowsDeviceType /*device*/) { 146 | if (rec_is_initialized_) { 147 | return -1; 148 | } 149 | return 0; 150 | } 151 | 152 | int32_t FakeAudioCaptureModule::PlayoutIsAvailable(bool* /*available*/) { 153 | RTC_NOTREACHED(); 154 | return 0; 155 | } 156 | 157 | int32_t FakeAudioCaptureModule::InitPlayout() { 158 | play_is_initialized_ = true; 159 | return 0; 160 | } 161 | 162 | bool FakeAudioCaptureModule::PlayoutIsInitialized() const { 163 | return play_is_initialized_; 164 | } 165 | 166 | int32_t FakeAudioCaptureModule::RecordingIsAvailable(bool* /*available*/) { 167 | RTC_NOTREACHED(); 168 | return 0; 169 | } 170 | 171 | int32_t FakeAudioCaptureModule::InitRecording() { 172 | rec_is_initialized_ = true; 173 | return 0; 174 | } 175 | 176 | bool FakeAudioCaptureModule::RecordingIsInitialized() const { 177 | return rec_is_initialized_; 178 | } 179 | 180 | int32_t FakeAudioCaptureModule::StartPlayout() { 181 | if (!play_is_initialized_) { 182 | return -1; 183 | } 184 | { 185 | webrtc::MutexLock lock(&mutex_); 186 | playing_ = true; 187 | } 188 | bool start = true; 189 | UpdateProcessing(start); 190 | return 0; 191 | } 192 | 193 | int32_t FakeAudioCaptureModule::StopPlayout() { 194 | bool start = false; 195 | { 196 | webrtc::MutexLock lock(&mutex_); 197 | playing_ = false; 198 | start = ShouldStartProcessing(); 199 | } 200 | UpdateProcessing(start); 201 | return 0; 202 | } 203 | 204 | bool FakeAudioCaptureModule::Playing() const { 205 | webrtc::MutexLock lock(&mutex_); 206 | return playing_; 207 | } 208 | 209 | int32_t FakeAudioCaptureModule::StartRecording() { 210 | if (!rec_is_initialized_) { 211 | return -1; 212 | } 213 | { 214 | webrtc::MutexLock lock(&mutex_); 215 | recording_ = true; 216 | } 217 | bool start = true; 218 | UpdateProcessing(start); 219 | return 0; 220 | } 221 | 222 | int32_t FakeAudioCaptureModule::StopRecording() { 223 | bool start = false; 224 | { 225 | webrtc::MutexLock lock(&mutex_); 226 | recording_ = false; 227 | start = ShouldStartProcessing(); 228 | } 229 | UpdateProcessing(start); 230 | return 0; 231 | } 232 | 233 | bool FakeAudioCaptureModule::Recording() const { 234 | webrtc::MutexLock lock(&mutex_); 235 | return recording_; 236 | } 237 | 238 | int32_t FakeAudioCaptureModule::InitSpeaker() { 239 | // No speaker, just playing from file. Return success. 240 | return 0; 241 | } 242 | 243 | bool FakeAudioCaptureModule::SpeakerIsInitialized() const { 244 | RTC_NOTREACHED(); 245 | return 0; 246 | } 247 | 248 | int32_t FakeAudioCaptureModule::InitMicrophone() { 249 | // No microphone, just playing from file. Return success. 250 | return 0; 251 | } 252 | 253 | bool FakeAudioCaptureModule::MicrophoneIsInitialized() const { 254 | RTC_NOTREACHED(); 255 | return 0; 256 | } 257 | 258 | int32_t FakeAudioCaptureModule::SpeakerVolumeIsAvailable(bool* /*available*/) { 259 | RTC_NOTREACHED(); 260 | return 0; 261 | } 262 | 263 | int32_t FakeAudioCaptureModule::SetSpeakerVolume(uint32_t /*volume*/) { 264 | RTC_NOTREACHED(); 265 | return 0; 266 | } 267 | 268 | int32_t FakeAudioCaptureModule::SpeakerVolume(uint32_t* /*volume*/) const { 269 | RTC_NOTREACHED(); 270 | return 0; 271 | } 272 | 273 | int32_t FakeAudioCaptureModule::MaxSpeakerVolume( 274 | uint32_t* /*max_volume*/) const { 275 | RTC_NOTREACHED(); 276 | return 0; 277 | } 278 | 279 | int32_t FakeAudioCaptureModule::MinSpeakerVolume( 280 | uint32_t* /*min_volume*/) const { 281 | RTC_NOTREACHED(); 282 | return 0; 283 | } 284 | 285 | int32_t FakeAudioCaptureModule::MicrophoneVolumeIsAvailable( 286 | bool* /*available*/) { 287 | RTC_NOTREACHED(); 288 | return 0; 289 | } 290 | 291 | int32_t FakeAudioCaptureModule::SetMicrophoneVolume(uint32_t volume) { 292 | webrtc::MutexLock lock(&mutex_); 293 | current_mic_level_ = volume; 294 | return 0; 295 | } 296 | 297 | int32_t FakeAudioCaptureModule::MicrophoneVolume(uint32_t* volume) const { 298 | webrtc::MutexLock lock(&mutex_); 299 | *volume = current_mic_level_; 300 | return 0; 301 | } 302 | 303 | int32_t FakeAudioCaptureModule::MaxMicrophoneVolume( 304 | uint32_t* max_volume) const { 305 | *max_volume = kMaxVolume; 306 | return 0; 307 | } 308 | 309 | int32_t FakeAudioCaptureModule::MinMicrophoneVolume( 310 | uint32_t* /*min_volume*/) const { 311 | RTC_NOTREACHED(); 312 | return 0; 313 | } 314 | 315 | int32_t FakeAudioCaptureModule::SpeakerMuteIsAvailable(bool* /*available*/) { 316 | RTC_NOTREACHED(); 317 | return 0; 318 | } 319 | 320 | int32_t FakeAudioCaptureModule::SetSpeakerMute(bool /*enable*/) { 321 | RTC_NOTREACHED(); 322 | return 0; 323 | } 324 | 325 | int32_t FakeAudioCaptureModule::SpeakerMute(bool* /*enabled*/) const { 326 | RTC_NOTREACHED(); 327 | return 0; 328 | } 329 | 330 | int32_t FakeAudioCaptureModule::MicrophoneMuteIsAvailable(bool* /*available*/) { 331 | RTC_NOTREACHED(); 332 | return 0; 333 | } 334 | 335 | int32_t FakeAudioCaptureModule::SetMicrophoneMute(bool /*enable*/) { 336 | RTC_NOTREACHED(); 337 | return 0; 338 | } 339 | 340 | int32_t FakeAudioCaptureModule::MicrophoneMute(bool* /*enabled*/) const { 341 | RTC_NOTREACHED(); 342 | return 0; 343 | } 344 | 345 | int32_t FakeAudioCaptureModule::StereoPlayoutIsAvailable( 346 | bool* available) const { 347 | // No recording device, just dropping audio. Stereo can be dropped just 348 | // as easily as mono. 349 | *available = true; 350 | return 0; 351 | } 352 | 353 | int32_t FakeAudioCaptureModule::SetStereoPlayout(bool /*enable*/) { 354 | // No recording device, just dropping audio. Stereo can be dropped just 355 | // as easily as mono. 356 | return 0; 357 | } 358 | 359 | int32_t FakeAudioCaptureModule::StereoPlayout(bool* /*enabled*/) const { 360 | RTC_NOTREACHED(); 361 | return 0; 362 | } 363 | 364 | int32_t FakeAudioCaptureModule::StereoRecordingIsAvailable( 365 | bool* available) const { 366 | // Keep thing simple. No stereo recording. 367 | *available = false; 368 | return 0; 369 | } 370 | 371 | int32_t FakeAudioCaptureModule::SetStereoRecording(bool enable) { 372 | if (!enable) { 373 | return 0; 374 | } 375 | return -1; 376 | } 377 | 378 | int32_t FakeAudioCaptureModule::StereoRecording(bool* /*enabled*/) const { 379 | RTC_NOTREACHED(); 380 | return 0; 381 | } 382 | 383 | int32_t FakeAudioCaptureModule::PlayoutDelay(uint16_t* delay_ms) const { 384 | // No delay since audio frames are dropped. 385 | *delay_ms = 0; 386 | return 0; 387 | } 388 | 389 | void FakeAudioCaptureModule::OnMessage(rtc::Message* msg) { 390 | switch (msg->message_id) { 391 | case MSG_START_PROCESS: 392 | StartProcessP(); 393 | break; 394 | case MSG_RUN_PROCESS: 395 | ProcessFrameP(); 396 | break; 397 | default: 398 | // All existing messages should be caught. Getting here should never 399 | // happen. 400 | RTC_NOTREACHED(); 401 | } 402 | } 403 | 404 | bool FakeAudioCaptureModule::Initialize() { 405 | // Set the send buffer samples high enough that it would not occur on the 406 | // remote side unless a packet containing a sample of that magnitude has been 407 | // sent to it. Note that the audio processing pipeline will likely distort the 408 | // original signal. 409 | SetSendBuffer(kHighSampleValue); 410 | return true; 411 | } 412 | 413 | void FakeAudioCaptureModule::SetSendBuffer(int value) { 414 | Sample* buffer_ptr = reinterpret_cast(send_buffer_); 415 | const size_t buffer_size_in_samples = 416 | sizeof(send_buffer_) / kNumberBytesPerSample; 417 | for (size_t i = 0; i < buffer_size_in_samples; ++i) { 418 | buffer_ptr[i] = value; 419 | } 420 | } 421 | 422 | void FakeAudioCaptureModule::ResetRecBuffer() { 423 | memset(rec_buffer_, 0, sizeof(rec_buffer_)); 424 | } 425 | 426 | bool FakeAudioCaptureModule::CheckRecBuffer(int value) { 427 | const Sample* buffer_ptr = reinterpret_cast(rec_buffer_); 428 | const size_t buffer_size_in_samples = 429 | sizeof(rec_buffer_) / kNumberBytesPerSample; 430 | for (size_t i = 0; i < buffer_size_in_samples; ++i) { 431 | if (buffer_ptr[i] >= value) 432 | return true; 433 | } 434 | return false; 435 | } 436 | 437 | bool FakeAudioCaptureModule::ShouldStartProcessing() { 438 | return recording_ || playing_; 439 | } 440 | 441 | void FakeAudioCaptureModule::UpdateProcessing(bool start) { 442 | if (start) { 443 | if (!process_thread_) { 444 | process_thread_ = rtc::Thread::Create(); 445 | process_thread_->Start(); 446 | } 447 | process_thread_->Post(RTC_FROM_HERE, this, MSG_START_PROCESS); 448 | } else { 449 | if (process_thread_) { 450 | process_thread_->Stop(); 451 | process_thread_.reset(nullptr); 452 | process_thread_checker_.Detach(); 453 | } 454 | webrtc::MutexLock lock(&mutex_); 455 | started_ = false; 456 | } 457 | } 458 | 459 | void FakeAudioCaptureModule::StartProcessP() { 460 | RTC_DCHECK_RUN_ON(&process_thread_checker_); 461 | { 462 | webrtc::MutexLock lock(&mutex_); 463 | if (started_) { 464 | // Already started. 465 | return; 466 | } 467 | } 468 | ProcessFrameP(); 469 | } 470 | 471 | void FakeAudioCaptureModule::ProcessFrameP() { 472 | RTC_DCHECK_RUN_ON(&process_thread_checker_); 473 | { 474 | webrtc::MutexLock lock(&mutex_); 475 | if (!started_) { 476 | next_frame_time_ = rtc::TimeMillis(); 477 | started_ = true; 478 | } 479 | 480 | // Receive and send frames every kTimePerFrameMs. 481 | if (playing_) { 482 | ReceiveFrameP(); 483 | } 484 | if (recording_) { 485 | SendFrameP(); 486 | } 487 | } 488 | 489 | next_frame_time_ += kTimePerFrameMs; 490 | const int64_t current_time = rtc::TimeMillis(); 491 | const int64_t wait_time = 492 | (next_frame_time_ > current_time) ? next_frame_time_ - current_time : 0; 493 | process_thread_->PostDelayed(RTC_FROM_HERE, wait_time, this, MSG_RUN_PROCESS); 494 | } 495 | 496 | void FakeAudioCaptureModule::ReceiveFrameP() { 497 | RTC_DCHECK_RUN_ON(&process_thread_checker_); 498 | if (!audio_callback_) { 499 | return; 500 | } 501 | ResetRecBuffer(); 502 | size_t nSamplesOut = 0; 503 | int64_t elapsed_time_ms = 0; 504 | int64_t ntp_time_ms = 0; 505 | if (audio_callback_->NeedMorePlayData(kNumberSamples, kNumberBytesPerSample, 506 | kNumberOfChannels, kSamplesPerSecond, 507 | rec_buffer_, nSamplesOut, 508 | &elapsed_time_ms, &ntp_time_ms) != 0) { 509 | RTC_NOTREACHED(); 510 | } 511 | RTC_CHECK(nSamplesOut == kNumberSamples); 512 | 513 | // The SetBuffer() function ensures that after decoding, the audio buffer 514 | // should contain samples of similar magnitude (there is likely to be some 515 | // distortion due to the audio pipeline). If one sample is detected to 516 | // have the same or greater magnitude somewhere in the frame, an actual frame 517 | // has been received from the remote side (i.e. faked frames are not being 518 | // pulled). 519 | if (CheckRecBuffer(kHighSampleValue)) { 520 | ++frames_received_; 521 | } 522 | } 523 | 524 | void FakeAudioCaptureModule::SendFrameP() { 525 | RTC_DCHECK_RUN_ON(&process_thread_checker_); 526 | if (!audio_callback_) { 527 | return; 528 | } 529 | bool key_pressed = false; 530 | uint32_t current_mic_level = current_mic_level_; 531 | if (audio_callback_->RecordedDataIsAvailable( 532 | send_buffer_, kNumberSamples, kNumberBytesPerSample, 533 | kNumberOfChannels, kSamplesPerSecond, kTotalDelayMs, kClockDriftMs, 534 | current_mic_level, key_pressed, current_mic_level) != 0) { 535 | RTC_NOTREACHED(); 536 | } 537 | current_mic_level_ = current_mic_level; 538 | } 539 | -------------------------------------------------------------------------------- /.clang-tidy: -------------------------------------------------------------------------------- 1 | --- 2 | Checks: '*,-boost-use-to-string,-cert-*,-clang-analyzer-osx.*,-clang-analyzer-optin.osx.*,-cppcoreguidelines-pro-bounds-array-to-pointer-decay,-cppcoreguidelines-pro-bounds-constant-array-index,-cppcoreguidelines-pro-bounds-pointer-arithmetic,-cppcoreguidelines-pro-type-vararg,-cppcoreguidelines-pro-type-const-cast,-cppcoreguidelines-pro-type-reinterpret-cast,-cppcoreguidelines-pro-type-static-cast-downcast,-cppcoreguidelines-pro-type-union-access,-cppcoreguidelines-special-member-functions,-google-readability-*,-llvm-include-order,-readability-else-after-return,-readability-implicit-bool-cast,-modernize-pass-by-value,-cppcoreguidelines-no-malloc,-modernize-make-unique,-google-default-arguments,-hicpp-uppercase-literal-suffix,-readability-uppercase-literal-suffix,-hicpp-braces-around-statements,-hicpp-vararg,-hicpp-no-array-decay,-cppcoreguidelines-avoid-c-arrays,-hicpp-avoid-c-arrays,-modernize-avoid-c-arrays,-fuchsia-default-arguments-calls,-readability-implicit-bool-conversion,-modernize-use-trailing-return-type,-cppcoreguidelines-avoid-magic-numbers,-cppcoreguidelines-init-variables,-readability-magic-numbers,-cppcoreguidelines-avoid-goto,-hicpp-avoid-goto,-cppcoreguidelines-owning-memory,-hicpp-special-member-functions,-google-runtime-references,-fuchsia-default-arguments-declarations,-cppcoreguidelines-non-private-member-variables-in-classes,-misc-non-private-member-variables-in-classes,-hicpp-signed-bitwise,-fuchsia-default-arguments-calls' 3 | 4 | HeaderFilterRegex: '' 5 | AnalyzeTemporaryDtors: false 6 | User: mediasoup 7 | CheckOptions: 8 | - key: cppcoreguidelines-pro-bounds-constant-array-index.GslHeader 9 | value: '' 10 | - key: cppcoreguidelines-pro-bounds-constant-array-index.IncludeStyle 11 | value: '0' 12 | - key: misc-assert-side-effect.AssertMacros 13 | value: assert 14 | - key: misc-assert-side-effect.CheckFunctionCalls 15 | value: '0' 16 | - key: misc-throw-by-value-catch-by-reference.CheckThrowTemporaries 17 | value: '1' 18 | - key: modernize-loop-convert.MaxCopySize 19 | value: '16' 20 | - key: modernize-loop-convert.MinConfidence 21 | value: reasonable 22 | - key: modernize-loop-convert.NamingStyle 23 | value: CamelCase 24 | - key: modernize-replace-auto-ptr.IncludeStyle 25 | value: llvm 26 | - key: modernize-use-nullptr.NullMacros 27 | value: 'NULL' 28 | - key: modernize-use-default-member-init.UseAssignment 29 | value: '0' 30 | - key: readability-braces-around-statements.ShortStatementLines 31 | value: '3' 32 | - key: readability-function-size.BranchThreshold 33 | value: '4294967295' 34 | - key: readability-function-size.LineThreshold 35 | value: '4294967295' 36 | - key: readability-function-size.StatementThreshold 37 | value: '800' 38 | - key: readability-identifier-naming.AbstractClassCase 39 | value: CamelCase 40 | - key: readability-identifier-naming.AbstractClassPrefix 41 | value: '' 42 | - key: readability-identifier-naming.AbstractClassSuffix 43 | value: '' 44 | - key: readability-identifier-naming.ClassCase 45 | value: CamelCase 46 | - key: readability-identifier-naming.ClassConstantCase 47 | value: camelBack 48 | - key: readability-identifier-naming.ClassConstantPrefix 49 | value: '' 50 | - key: readability-identifier-naming.ClassConstantSuffix 51 | value: '' 52 | - key: readability-identifier-naming.ClassMemberCase 53 | value: camelBack 54 | - key: readability-identifier-naming.ClassMemberPrefix 55 | value: '' 56 | - key: readability-identifier-naming.ClassMemberSuffix 57 | value: '' 58 | - key: readability-identifier-naming.ClassMethodCase 59 | value: CamelCase 60 | - key: readability-identifier-naming.ClassMethodPrefix 61 | value: '' 62 | - key: readability-identifier-naming.ClassMethodSuffix 63 | value: '' 64 | - key: readability-identifier-naming.ClassPrefix 65 | value: '' 66 | - key: readability-identifier-naming.ClassSuffix 67 | value: '' 68 | - key: readability-identifier-naming.ConstantCase 69 | value: UPPER_CASE 70 | - key: readability-identifier-naming.ConstantMemberCase 71 | value: camelBack 72 | - key: readability-identifier-naming.ConstantMemberPrefix 73 | value: '' 74 | - key: readability-identifier-naming.ConstantMemberSuffix 75 | value: '' 76 | - key: readability-identifier-naming.ConstantParameterCase 77 | value: camelBack 78 | - key: readability-identifier-naming.ConstantParameterPrefix 79 | value: '' 80 | - key: readability-identifier-naming.ConstantParameterSuffix 81 | value: '' 82 | - key: readability-identifier-naming.ConstantPrefix 83 | value: '' 84 | - key: readability-identifier-naming.ConstantSuffix 85 | value: '' 86 | - key: readability-identifier-naming.ConstexprFunctionCase 87 | value: camelBack 88 | - key: readability-identifier-naming.ConstexprFunctionPrefix 89 | value: '' 90 | - key: readability-identifier-naming.ConstexprFunctionSuffix 91 | value: '' 92 | - key: readability-identifier-naming.ConstexprMethodCase 93 | value: camelBack 94 | - key: readability-identifier-naming.ConstexprMethodPrefix 95 | value: '' 96 | - key: readability-identifier-naming.ConstexprMethodSuffix 97 | value: '' 98 | - key: readability-identifier-naming.ConstexprVariableCase 99 | value: CamelCase 100 | - key: readability-identifier-naming.ConstexprVariablePrefix 101 | value: '' 102 | - key: readability-identifier-naming.ConstexprVariableSuffix 103 | value: '' 104 | - key: readability-identifier-naming.EnumCase 105 | value: aNy_Case 106 | - key: readability-identifier-naming.EnumConstantCase 107 | value: aNy_Case 108 | - key: readability-identifier-naming.EnumConstantPrefix 109 | value: '' 110 | - key: readability-identifier-naming.EnumConstantSuffix 111 | value: '' 112 | - key: readability-identifier-naming.EnumPrefix 113 | value: '' 114 | - key: readability-identifier-naming.EnumSuffix 115 | value: '' 116 | - key: readability-identifier-naming.FunctionCase 117 | value: camelBack 118 | - key: readability-identifier-naming.FunctionPrefix 119 | value: '' 120 | - key: readability-identifier-naming.FunctionSuffix 121 | value: '' 122 | - key: readability-identifier-naming.GlobalConstantCase 123 | value: CamelCase 124 | - key: readability-identifier-naming.GlobalConstantPrefix 125 | value: '' 126 | - key: readability-identifier-naming.GlobalConstantSuffix 127 | value: '' 128 | - key: readability-identifier-naming.GlobalFunctionCase 129 | value: camelBack 130 | - key: readability-identifier-naming.GlobalFunctionPrefix 131 | value: '' 132 | - key: readability-identifier-naming.GlobalFunctionSuffix 133 | value: '' 134 | - key: readability-identifier-naming.GlobalVariableCase 135 | value: CamelCase 136 | - key: readability-identifier-naming.GlobalVariablePrefix 137 | value: '' 138 | - key: readability-identifier-naming.GlobalVariableSuffix 139 | value: '' 140 | - key: readability-identifier-naming.IgnoreFailedSplit 141 | value: '0' 142 | - key: readability-identifier-naming.InlineNamespaceCase 143 | value: aNy_Case 144 | - key: readability-identifier-naming.InlineNamespacePrefix 145 | value: '' 146 | - key: readability-identifier-naming.InlineNamespaceSuffix 147 | value: '' 148 | - key: readability-identifier-naming.LocalConstantCase 149 | value: camelBack 150 | - key: readability-identifier-naming.LocalConstantPrefix 151 | value: '' 152 | - key: readability-identifier-naming.LocalConstantSuffix 153 | value: '' 154 | - key: readability-identifier-naming.LocalVariableCase 155 | value: camelBack 156 | - key: readability-identifier-naming.LocalVariablePrefix 157 | value: '' 158 | - key: readability-identifier-naming.LocalVariableSuffix 159 | value: '' 160 | - key: readability-identifier-naming.MemberCase 161 | value: camelBack 162 | - key: readability-identifier-naming.MemberPrefix 163 | value: '' 164 | - key: readability-identifier-naming.MemberSuffix 165 | value: '' 166 | - key: readability-identifier-naming.MethodCase 167 | value: CamelCase 168 | - key: readability-identifier-naming.MethodPrefix 169 | value: '' 170 | - key: readability-identifier-naming.MethodSuffix 171 | value: '' 172 | - key: readability-identifier-naming.NamespaceCase 173 | value: aNy_Case 174 | - key: readability-identifier-naming.NamespacePrefix 175 | value: '' 176 | - key: readability-identifier-naming.NamespaceSuffix 177 | value: '' 178 | - key: readability-identifier-naming.ParameterCase 179 | value: camelBack 180 | - key: readability-identifier-naming.ParameterPackCase 181 | value: camelBack 182 | - key: readability-identifier-naming.ParameterPackPrefix 183 | value: '' 184 | - key: readability-identifier-naming.ParameterPackSuffix 185 | value: '' 186 | - key: readability-identifier-naming.ParameterPrefix 187 | value: '' 188 | - key: readability-identifier-naming.ParameterSuffix 189 | value: '' 190 | - key: readability-identifier-naming.PrivateMemberCase 191 | value: camelBack 192 | - key: readability-identifier-naming.PrivateMemberPrefix 193 | value: '' 194 | - key: readability-identifier-naming.PrivateMemberSuffix 195 | value: '' 196 | - key: readability-identifier-naming.PrivateMethodCase 197 | value: CamelCase 198 | - key: readability-identifier-naming.PrivateMethodPrefix 199 | value: '' 200 | - key: readability-identifier-naming.PrivateMethodSuffix 201 | value: '' 202 | - key: readability-identifier-naming.ProtectedMemberCase 203 | value: camelBack 204 | - key: readability-identifier-naming.ProtectedMemberPrefix 205 | value: '' 206 | - key: readability-identifier-naming.ProtectedMemberSuffix 207 | value: '' 208 | - key: readability-identifier-naming.ProtectedMethodCase 209 | value: CamelCase 210 | - key: readability-identifier-naming.ProtectedMethodPrefix 211 | value: '' 212 | - key: readability-identifier-naming.ProtectedMethodSuffix 213 | value: '' 214 | - key: readability-identifier-naming.PublicMemberCase 215 | value: camelBack 216 | - key: readability-identifier-naming.PublicMemberPrefix 217 | value: '' 218 | - key: readability-identifier-naming.PublicMemberSuffix 219 | value: '' 220 | - key: readability-identifier-naming.PublicMethodCase 221 | value: CamelCase 222 | - key: readability-identifier-naming.PublicMethodPrefix 223 | value: '' 224 | - key: readability-identifier-naming.PublicMethodSuffix 225 | value: '' 226 | - key: readability-identifier-naming.StaticConstantCase 227 | value: CamelCase 228 | - key: readability-identifier-naming.StaticConstantPrefix 229 | value: '' 230 | - key: readability-identifier-naming.StaticConstantSuffix 231 | value: '' 232 | - key: readability-identifier-naming.StaticVariableCase 233 | value: camelBack 234 | - key: readability-identifier-naming.StaticVariablePrefix 235 | value: '' 236 | - key: readability-identifier-naming.StaticVariableSuffix 237 | value: '' 238 | - key: readability-identifier-naming.StructCase 239 | value: CamelCase 240 | - key: readability-identifier-naming.StructPrefix 241 | value: '' 242 | - key: readability-identifier-naming.StructSuffix 243 | value: '' 244 | - key: readability-identifier-naming.TemplateParameterCase 245 | value: CamelCase 246 | - key: readability-identifier-naming.TemplateParameterPrefix 247 | value: '' 248 | - key: readability-identifier-naming.TemplateParameterSuffix 249 | value: '' 250 | - key: readability-identifier-naming.TemplateTemplateParameterCase 251 | value: CamelCase 252 | - key: readability-identifier-naming.TemplateTemplateParameterPrefix 253 | value: '' 254 | - key: readability-identifier-naming.TemplateTemplateParameterSuffix 255 | value: '' 256 | - key: readability-identifier-naming.TypeTemplateParameterCase 257 | value: CamelCase 258 | - key: readability-identifier-naming.TypeTemplateParameterPrefix 259 | value: '' 260 | - key: readability-identifier-naming.TypeTemplateParameterSuffix 261 | value: '' 262 | - key: readability-identifier-naming.TypedefCase 263 | value: CamelCase 264 | - key: readability-identifier-naming.TypedefPrefix 265 | value: '' 266 | - key: readability-identifier-naming.TypedefSuffix 267 | value: '' 268 | - key: readability-identifier-naming.UnionCase 269 | value: CamelCase 270 | - key: readability-identifier-naming.UnionPrefix 271 | value: '' 272 | - key: readability-identifier-naming.UnionSuffix 273 | value: '' 274 | - key: readability-identifier-naming.ValueTemplateParameterCase 275 | value: camelBack 276 | - key: readability-identifier-naming.ValueTemplateParameterPrefix 277 | value: '' 278 | - key: readability-identifier-naming.ValueTemplateParameterSuffix 279 | value: '' 280 | - key: readability-identifier-naming.VariableCase 281 | value: camelBack 282 | - key: readability-identifier-naming.VariablePrefix 283 | value: '' 284 | - key: readability-identifier-naming.VariableSuffix 285 | value: '' 286 | - key: readability-identifier-naming.VirtualMethodCase 287 | value: CamelCase 288 | - key: readability-identifier-naming.VirtualMethodPrefix 289 | value: '' 290 | - key: readability-identifier-naming.VirtualMethodSuffix 291 | value: '' 292 | - key: readability-simplify-boolean-expr.ChainedConditionalAssignment 293 | value: '1' 294 | - key: readability-simplify-boolean-expr.ChainedConditionalReturn 295 | value: '1' 296 | ... 297 | 298 | -------------------------------------------------------------------------------- /src/Broadcaster.cpp: -------------------------------------------------------------------------------- 1 | #include "Broadcaster.hpp" 2 | #include "MediaStreamTrackFactory.hpp" 3 | #include "mediasoupclient.hpp" 4 | #include "json.hpp" 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | using json = nlohmann::json; 15 | 16 | Broadcaster::~Broadcaster() 17 | { 18 | this->Stop(); 19 | } 20 | 21 | void Broadcaster::OnTransportClose(mediasoupclient::Producer* /*producer*/) 22 | { 23 | std::cout << "[INFO] Broadcaster::OnTransportClose()" << std::endl; 24 | } 25 | 26 | void Broadcaster::OnTransportClose(mediasoupclient::DataProducer* /*dataProducer*/) 27 | { 28 | std::cout << "[INFO] Broadcaster::OnTransportClose()" << std::endl; 29 | } 30 | 31 | /* Transport::Listener::OnConnect 32 | * 33 | * Fired for the first Transport::Consume() or Transport::Produce(). 34 | * Update the already created remote transport with the local DTLS parameters. 35 | */ 36 | std::future Broadcaster::OnConnect(mediasoupclient::Transport* transport, const json& dtlsParameters) 37 | { 38 | std::cout << "[INFO] Broadcaster::OnConnect()" << std::endl; 39 | // std::cout << "[INFO] dtlsParameters: " << dtlsParameters.dump(4) << std::endl; 40 | 41 | if (transport->GetId() == this->sendTransport->GetId()) 42 | { 43 | return this->OnConnectSendTransport(dtlsParameters); 44 | } 45 | else if (transport->GetId() == this->recvTransport->GetId()) 46 | { 47 | return this->OnConnectRecvTransport(dtlsParameters); 48 | } 49 | else 50 | { 51 | std::promise promise; 52 | 53 | promise.set_exception(std::make_exception_ptr("Unknown transport requested to connect")); 54 | 55 | return promise.get_future(); 56 | } 57 | } 58 | 59 | std::future Broadcaster::OnConnectSendTransport(const json& dtlsParameters) 60 | { 61 | std::promise promise; 62 | 63 | /* clang-format off */ 64 | json body = 65 | { 66 | { "dtlsParameters", dtlsParameters } 67 | }; 68 | /* clang-format on */ 69 | 70 | auto r = cpr::PostAsync( 71 | cpr::Url{ this->baseUrl + "/broadcasters/" + this->id + "/transports/" + 72 | this->sendTransport->GetId() + "/connect" }, 73 | cpr::Body{ body.dump() }, 74 | cpr::Header{ { "Content-Type", "application/json" } }, 75 | cpr::VerifySsl{ verifySsl }) 76 | .get(); 77 | 78 | if (r.status_code == 200) 79 | { 80 | promise.set_value(); 81 | } 82 | else 83 | { 84 | std::cerr << "[ERROR] unable to connect transport" 85 | << " [status code:" << r.status_code << ", body:\"" << r.text << "\"]" << std::endl; 86 | 87 | promise.set_exception(std::make_exception_ptr(r.text)); 88 | } 89 | 90 | return promise.get_future(); 91 | } 92 | 93 | std::future Broadcaster::OnConnectRecvTransport(const json& dtlsParameters) 94 | { 95 | std::promise promise; 96 | 97 | /* clang-format off */ 98 | json body = 99 | { 100 | { "dtlsParameters", dtlsParameters } 101 | }; 102 | /* clang-format on */ 103 | 104 | auto r = cpr::PostAsync( 105 | cpr::Url{ this->baseUrl + "/broadcasters/" + this->id + "/transports/" + 106 | this->recvTransport->GetId() + "/connect" }, 107 | cpr::Body{ body.dump() }, 108 | cpr::Header{ { "Content-Type", "application/json" } }, 109 | cpr::VerifySsl{ verifySsl }) 110 | .get(); 111 | 112 | if (r.status_code == 200) 113 | { 114 | promise.set_value(); 115 | } 116 | else 117 | { 118 | std::cerr << "[ERROR] unable to connect transport" 119 | << " [status code:" << r.status_code << ", body:\"" << r.text << "\"]" << std::endl; 120 | 121 | promise.set_exception(std::make_exception_ptr(r.text)); 122 | } 123 | 124 | return promise.get_future(); 125 | } 126 | 127 | /* 128 | * Transport::Listener::OnConnectionStateChange. 129 | */ 130 | void Broadcaster::OnConnectionStateChange( 131 | mediasoupclient::Transport* /*transport*/, const std::string& connectionState) 132 | { 133 | std::cout << "[INFO] Broadcaster::OnConnectionStateChange() [connectionState:" << connectionState 134 | << "]" << std::endl; 135 | 136 | if (connectionState == "failed") 137 | { 138 | Stop(); 139 | std::exit(0); 140 | } 141 | } 142 | 143 | /* Producer::Listener::OnProduce 144 | * 145 | * Fired when a producer needs to be created in mediasoup. 146 | * Retrieve the remote producer ID and feed the caller with it. 147 | */ 148 | std::future Broadcaster::OnProduce( 149 | mediasoupclient::SendTransport* /*transport*/, 150 | const std::string& kind, 151 | json rtpParameters, 152 | const json& /*appData*/) 153 | { 154 | std::cout << "[INFO] Broadcaster::OnProduce()" << std::endl; 155 | // std::cout << "[INFO] rtpParameters: " << rtpParameters.dump(4) << std::endl; 156 | 157 | std::promise promise; 158 | 159 | /* clang-format off */ 160 | json body = 161 | { 162 | { "kind", kind }, 163 | { "rtpParameters", rtpParameters } 164 | }; 165 | /* clang-format on */ 166 | 167 | auto r = cpr::PostAsync( 168 | cpr::Url{ this->baseUrl + "/broadcasters/" + this->id + "/transports/" + 169 | this->sendTransport->GetId() + "/producers" }, 170 | cpr::Body{ body.dump() }, 171 | cpr::Header{ { "Content-Type", "application/json" } }, 172 | cpr::VerifySsl{ verifySsl }) 173 | .get(); 174 | 175 | if (r.status_code == 200) 176 | { 177 | auto response = json::parse(r.text); 178 | 179 | auto it = response.find("id"); 180 | if (it == response.end() || !it->is_string()) 181 | { 182 | promise.set_exception(std::make_exception_ptr("'id' missing in response")); 183 | } 184 | 185 | promise.set_value((*it).get()); 186 | } 187 | else 188 | { 189 | std::cerr << "[ERROR] unable to create producer" 190 | << " [status code:" << r.status_code << ", body:\"" << r.text << "\"]" << std::endl; 191 | 192 | promise.set_exception(std::make_exception_ptr(r.text)); 193 | } 194 | 195 | return promise.get_future(); 196 | } 197 | 198 | /* Producer::Listener::OnProduceData 199 | * 200 | * Fired when a data producer needs to be created in mediasoup. 201 | * Retrieve the remote producer ID and feed the caller with it. 202 | */ 203 | std::future Broadcaster::OnProduceData( 204 | mediasoupclient::SendTransport* /*transport*/, 205 | const json& sctpStreamParameters, 206 | const std::string& label, 207 | const std::string& protocol, 208 | const json& /*appData*/) 209 | { 210 | std::cout << "[INFO] Broadcaster::OnProduceData()" << std::endl; 211 | // std::cout << "[INFO] rtpParameters: " << rtpParameters.dump(4) << std::endl; 212 | 213 | std::promise promise; 214 | 215 | /* clang-format off */ 216 | json body = 217 | { 218 | { "label" , label }, 219 | { "protocol" , protocol }, 220 | { "sctpStreamParameters" , sctpStreamParameters } 221 | // { "appData" , "someAppData" } 222 | }; 223 | /* clang-format on */ 224 | 225 | auto r = cpr::PostAsync( 226 | cpr::Url{ this->baseUrl + "/broadcasters/" + this->id + "/transports/" + 227 | this->sendTransport->GetId() + "/produce/data" }, 228 | cpr::Body{ body.dump() }, 229 | cpr::Header{ { "Content-Type", "application/json" } }, 230 | cpr::VerifySsl{ verifySsl }) 231 | .get(); 232 | 233 | if (r.status_code == 200) 234 | { 235 | auto response = json::parse(r.text); 236 | 237 | auto it = response.find("id"); 238 | if (it == response.end() || !it->is_string()) 239 | { 240 | promise.set_exception(std::make_exception_ptr("'id' missing in response")); 241 | } 242 | else 243 | { 244 | auto dataProducerId = (*it).get(); 245 | promise.set_value(dataProducerId); 246 | } 247 | } 248 | else 249 | { 250 | std::cerr << "[ERROR] unable to create data producer" 251 | << " [status code:" << r.status_code << ", body:\"" << r.text << "\"]" << std::endl; 252 | 253 | promise.set_exception(std::make_exception_ptr(r.text)); 254 | } 255 | 256 | return promise.get_future(); 257 | } 258 | 259 | void Broadcaster::Start( 260 | const std::string& baseUrl, 261 | bool enableAudio, 262 | bool useSimulcast, 263 | const json& routerRtpCapabilities, 264 | bool verifySsl) 265 | { 266 | std::cout << "[INFO] Broadcaster::Start()" << std::endl; 267 | 268 | this->baseUrl = baseUrl; 269 | this->verifySsl = verifySsl; 270 | 271 | // Load the device. 272 | this->device.Load(routerRtpCapabilities); 273 | 274 | std::cout << "[INFO] creating Broadcaster..." << std::endl; 275 | 276 | /* clang-format off */ 277 | json body = 278 | { 279 | { "id", this->id }, 280 | { "displayName", "broadcaster" }, 281 | { "device", 282 | { 283 | { "name", "libmediasoupclient" }, 284 | { "version", mediasoupclient::Version() } 285 | } 286 | }, 287 | { "rtpCapabilities", this->device.GetRtpCapabilities() } 288 | }; 289 | /* clang-format on */ 290 | 291 | auto r = cpr::PostAsync( 292 | cpr::Url{ this->baseUrl + "/broadcasters" }, 293 | cpr::Body{ body.dump() }, 294 | cpr::Header{ { "Content-Type", "application/json" } }, 295 | cpr::VerifySsl{ verifySsl }) 296 | .get(); 297 | 298 | if (r.status_code != 200) 299 | { 300 | std::cerr << "[ERROR] unable to create Broadcaster" 301 | << " [status code:" << r.status_code << ", body:\"" << r.text << "\"]" << std::endl; 302 | 303 | return; 304 | } 305 | 306 | this->CreateSendTransport(enableAudio, useSimulcast); 307 | this->CreateRecvTransport(); 308 | } 309 | 310 | void Broadcaster::CreateDataConsumer() 311 | { 312 | const std::string& dataProducerId = this->dataProducer->GetId(); 313 | 314 | /* clang-format off */ 315 | json body = 316 | { 317 | { "dataProducerId", dataProducerId } 318 | }; 319 | /* clang-format on */ 320 | // create server data consumer 321 | auto r = cpr::PostAsync( 322 | cpr::Url{ this->baseUrl + "/broadcasters/" + this->id + "/transports/" + 323 | this->recvTransport->GetId() + "/consume/data" }, 324 | cpr::Body{ body.dump() }, 325 | cpr::Header{ { "Content-Type", "application/json" } }, 326 | cpr::VerifySsl{ verifySsl }) 327 | .get(); 328 | if (r.status_code != 200) 329 | { 330 | std::cerr << "[ERROR] server unable to consume mediasoup recv WebRtcTransport" 331 | << " [status code:" << r.status_code << ", body:\"" << r.text << "\"]" << std::endl; 332 | return; 333 | } 334 | 335 | auto response = json::parse(r.text); 336 | if (response.find("id") == response.end()) 337 | { 338 | std::cerr << "[ERROR] 'id' missing in response" << std::endl; 339 | return; 340 | } 341 | auto dataConsumerId = response["id"].get(); 342 | 343 | if (response.find("streamId") == response.end()) 344 | { 345 | std::cerr << "[ERROR] 'streamId' missing in response" << std::endl; 346 | return; 347 | } 348 | auto streamId = response["streamId"].get(); 349 | 350 | // Create client consumer. 351 | this->dataConsumer = this->recvTransport->ConsumeData( 352 | this, dataConsumerId, dataProducerId, streamId, "chat", "", nlohmann::json()); 353 | } 354 | 355 | void Broadcaster::CreateSendTransport(bool enableAudio, bool useSimulcast) 356 | { 357 | std::cout << "[INFO] creating mediasoup send WebRtcTransport..." << std::endl; 358 | 359 | json sctpCapabilities = this->device.GetSctpCapabilities(); 360 | /* clang-format off */ 361 | json body = 362 | { 363 | { "type", "webrtc" }, 364 | { "rtcpMux", true }, 365 | { "sctpCapabilities", sctpCapabilities } 366 | }; 367 | /* clang-format on */ 368 | 369 | auto r = cpr::PostAsync( 370 | cpr::Url{ this->baseUrl + "/broadcasters/" + this->id + "/transports" }, 371 | cpr::Body{ body.dump() }, 372 | cpr::Header{ { "Content-Type", "application/json" } }, 373 | cpr::VerifySsl{ verifySsl }) 374 | .get(); 375 | 376 | if (r.status_code != 200) 377 | { 378 | std::cerr << "[ERROR] unable to create send mediasoup WebRtcTransport" 379 | << " [status code:" << r.status_code << ", body:\"" << r.text << "\"]" << std::endl; 380 | 381 | return; 382 | } 383 | 384 | auto response = json::parse(r.text); 385 | 386 | if (response.find("id") == response.end()) 387 | { 388 | std::cerr << "[ERROR] 'id' missing in response" << std::endl; 389 | 390 | return; 391 | } 392 | else if (response.find("iceParameters") == response.end()) 393 | { 394 | std::cerr << "[ERROR] 'iceParametersd' missing in response" << std::endl; 395 | 396 | return; 397 | } 398 | else if (response.find("iceCandidates") == response.end()) 399 | { 400 | std::cerr << "[ERROR] 'iceCandidates' missing in response" << std::endl; 401 | 402 | return; 403 | } 404 | else if (response.find("dtlsParameters") == response.end()) 405 | { 406 | std::cerr << "[ERROR] 'dtlsParameters' missing in response" << std::endl; 407 | 408 | return; 409 | } 410 | else if (response.find("sctpParameters") == response.end()) 411 | { 412 | std::cerr << "[ERROR] 'sctpParameters' missing in response" << std::endl; 413 | 414 | return; 415 | } 416 | 417 | std::cout << "[INFO] creating SendTransport..." << std::endl; 418 | 419 | auto sendTransportId = response["id"].get(); 420 | 421 | this->sendTransport = this->device.CreateSendTransport( 422 | this, 423 | sendTransportId, 424 | response["iceParameters"], 425 | response["iceCandidates"], 426 | response["dtlsParameters"], 427 | response["sctpParameters"]); 428 | 429 | ///////////////////////// Create Audio Producer ////////////////////////// 430 | 431 | if (enableAudio && this->device.CanProduce("audio")) 432 | { 433 | auto audioTrack = createAudioTrack(std::to_string(rtc::CreateRandomId())); 434 | 435 | /* clang-format off */ 436 | json codecOptions = { 437 | { "opusStereo", true }, 438 | { "opusDtx", true } 439 | }; 440 | /* clang-format on */ 441 | 442 | this->sendTransport->Produce(this, audioTrack, nullptr, &codecOptions, nullptr); 443 | } 444 | else 445 | { 446 | std::cerr << "[WARN] cannot produce audio" << std::endl; 447 | } 448 | 449 | ///////////////////////// Create Video Producer ////////////////////////// 450 | 451 | if (this->device.CanProduce("video")) 452 | { 453 | auto videoTrack = createSquaresVideoTrack(std::to_string(rtc::CreateRandomId())); 454 | 455 | if (useSimulcast) 456 | { 457 | std::vector encodings; 458 | encodings.emplace_back(webrtc::RtpEncodingParameters()); 459 | encodings.emplace_back(webrtc::RtpEncodingParameters()); 460 | encodings.emplace_back(webrtc::RtpEncodingParameters()); 461 | 462 | this->sendTransport->Produce(this, videoTrack, &encodings, nullptr, nullptr); 463 | } 464 | else 465 | { 466 | this->sendTransport->Produce(this, videoTrack, nullptr, nullptr, nullptr); 467 | } 468 | } 469 | else 470 | { 471 | std::cerr << "[WARN] cannot produce video" << std::endl; 472 | 473 | return; 474 | } 475 | 476 | ///////////////////////// Create Data Producer ////////////////////////// 477 | 478 | this->dataProducer = sendTransport->ProduceData(this); 479 | 480 | uint32_t intervalSeconds = 10; 481 | std::thread([this, intervalSeconds]() { 482 | bool run = true; 483 | while (run) 484 | { 485 | std::chrono::system_clock::time_point p = std::chrono::system_clock::now(); 486 | std::time_t t = std::chrono::system_clock::to_time_t(p); 487 | std::string s = std::ctime(&t); 488 | auto dataBuffer = webrtc::DataBuffer(s); 489 | std::cout << "[INFO] sending chat data: " << s << std::endl; 490 | this->dataProducer->Send(dataBuffer); 491 | run = timerKiller.WaitFor(std::chrono::seconds(intervalSeconds)); 492 | } 493 | }) 494 | .detach(); 495 | } 496 | 497 | void Broadcaster::CreateRecvTransport() 498 | { 499 | std::cout << "[INFO] creating mediasoup recv WebRtcTransport..." << std::endl; 500 | 501 | json sctpCapabilities = this->device.GetSctpCapabilities(); 502 | /* clang-format off */ 503 | json body = 504 | { 505 | { "type", "webrtc" }, 506 | { "rtcpMux", true }, 507 | { "sctpCapabilities", sctpCapabilities } 508 | }; 509 | /* clang-format on */ 510 | 511 | // create server transport 512 | auto r = cpr::PostAsync( 513 | cpr::Url{ this->baseUrl + "/broadcasters/" + this->id + "/transports" }, 514 | cpr::Body{ body.dump() }, 515 | cpr::Header{ { "Content-Type", "application/json" } }, 516 | cpr::VerifySsl{ verifySsl }) 517 | .get(); 518 | 519 | if (r.status_code != 200) 520 | { 521 | std::cerr << "[ERROR] unable to create mediasoup recv WebRtcTransport" 522 | << " [status code:" << r.status_code << ", body:\"" << r.text << "\"]" << std::endl; 523 | 524 | return; 525 | } 526 | 527 | auto response = json::parse(r.text); 528 | 529 | if (response.find("id") == response.end()) 530 | { 531 | std::cerr << "[ERROR] 'id' missing in response" << std::endl; 532 | 533 | return; 534 | } 535 | else if (response.find("iceParameters") == response.end()) 536 | { 537 | std::cerr << "[ERROR] 'iceParameters' missing in response" << std::endl; 538 | 539 | return; 540 | } 541 | else if (response.find("iceCandidates") == response.end()) 542 | { 543 | std::cerr << "[ERROR] 'iceCandidates' missing in response" << std::endl; 544 | 545 | return; 546 | } 547 | else if (response.find("dtlsParameters") == response.end()) 548 | { 549 | std::cerr << "[ERROR] 'dtlsParameters' missing in response" << std::endl; 550 | 551 | return; 552 | } 553 | else if (response.find("sctpParameters") == response.end()) 554 | { 555 | std::cerr << "[ERROR] 'sctpParameters' missing in response" << std::endl; 556 | 557 | return; 558 | } 559 | 560 | auto recvTransportId = response["id"].get(); 561 | 562 | std::cout << "[INFO] creating RecvTransport..." << std::endl; 563 | 564 | auto sctpParameters = response["sctpParameters"]; 565 | 566 | this->recvTransport = this->device.CreateRecvTransport( 567 | this, 568 | recvTransportId, 569 | response["iceParameters"], 570 | response["iceCandidates"], 571 | response["dtlsParameters"], 572 | sctpParameters); 573 | 574 | this->CreateDataConsumer(); 575 | } 576 | 577 | void Broadcaster::OnMessage(mediasoupclient::DataConsumer* dataConsumer, const webrtc::DataBuffer& buffer) 578 | { 579 | std::cout << "[INFO] Broadcaster::OnMessage()" << std::endl; 580 | if (dataConsumer->GetLabel() == "chat") 581 | { 582 | std::string s = std::string(buffer.data.data(), buffer.data.size()); 583 | std::cout << "[INFO] received chat data: " + s << std::endl; 584 | } 585 | } 586 | 587 | void Broadcaster::Stop() 588 | { 589 | std::cout << "[INFO] Broadcaster::Stop()" << std::endl; 590 | 591 | this->timerKiller.Kill(); 592 | 593 | if (this->recvTransport) 594 | { 595 | recvTransport->Close(); 596 | } 597 | 598 | if (this->sendTransport) 599 | { 600 | sendTransport->Close(); 601 | } 602 | 603 | cpr::DeleteAsync( 604 | cpr::Url{ this->baseUrl + "/broadcasters/" + this->id }, cpr::VerifySsl{ verifySsl }) 605 | .get(); 606 | } 607 | 608 | void Broadcaster::OnOpen(mediasoupclient::DataProducer* /*dataProducer*/) 609 | { 610 | std::cout << "[INFO] Broadcaster::OnOpen()" << std::endl; 611 | } 612 | void Broadcaster::OnClose(mediasoupclient::DataProducer* /*dataProducer*/) 613 | { 614 | std::cout << "[INFO] Broadcaster::OnClose()" << std::endl; 615 | } 616 | void Broadcaster::OnBufferedAmountChange(mediasoupclient::DataProducer* /*dataProducer*/, uint64_t /*size*/) 617 | { 618 | std::cout << "[INFO] Broadcaster::OnBufferedAmountChange()" << std::endl; 619 | } 620 | --------------------------------------------------------------------------------