diff --git a/.vscode/c_cpp_properties.json b/.vscode/c_cpp_properties.json index 0e840d1..3af780e 100644 --- a/.vscode/c_cpp_properties.json +++ b/.vscode/c_cpp_properties.json @@ -9,15 +9,17 @@ "${workspaceFolder}/_source/libdatachannel/deps/plog/include", "${workspaceFolder}/_source/opus/include", + "${workspaceFolder}/_build/ubuntu-20.04_x86_64/release/sorac", + "${workspaceFolder}/_build/ubuntu-20.04_x86_64/release/sorac/proto/sorac", "${workspaceFolder}/_install/ubuntu-20.04_x86_64/release/mbedtls/include", "${workspaceFolder}/_install/ubuntu-20.04_x86_64/release/openh264/include", - "${workspaceFolder}/_build/ubuntu-20.04_x86_64/release/sorac/proto/sorac", "${workspaceFolder}/_install/ubuntu-20.04_x86_64/release/libjpeg-turbo/include", "${workspaceFolder}/_install/ubuntu-20.04_x86_64/release/libyuv/include", + "${workspaceFolder}/_build/macos_arm64/release/sorac", + "${workspaceFolder}/_build/macos_arm64/release/sorac/proto/sorac", "${workspaceFolder}/_install/macos_arm64/release/mbedtls/include", "${workspaceFolder}/_install/macos_arm64/release/openh264/include", - "${workspaceFolder}/_build/macos_arm64/release/sorac/proto/sorac", "${workspaceFolder}/_install/macos_arm64/release/libjpeg-turbo/include", "${workspaceFolder}/_install/macos_arm64/release/libyuv/include" ], diff --git a/CMakeLists.txt b/CMakeLists.txt index bb83458..416e3aa 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -62,6 +62,9 @@ endfunction() add_library(sorac STATIC) +configure_file(src/version.gen.h.template ${CMAKE_CURRENT_BINARY_DIR}/version.gen.h) +target_include_directories(sorac PRIVATE ${CMAKE_CURRENT_BINARY_DIR}) + file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/proto") file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/proto/sorac") if (WIN32) @@ -92,8 +95,10 @@ target_sources(sorac src/open_h264_video_encoder.cpp src/opus_audio_encoder.cpp src/signaling.cpp - src/types.cpp src/sorac.cpp + src/types.cpp + src/util.cpp + src/version.cpp PUBLIC FILE_SET HEADERS BASE_DIRS @@ -146,9 +151,12 @@ target_link_libraries(sorac ) if (SORAC_TARGET STREQUAL "macos_arm64") + enable_language(OBJCXX) + target_link_options(sumomo PRIVATE -ObjC) target_sources(sorac PRIVATE src/vt_h26x_video_encoder.cpp + src/mac_version.mm PUBLIC FILE_SET HEADERS BASE_DIRS diff --git a/include/sorac/version.hpp b/include/sorac/version.hpp new file mode 100644 index 0000000..9dafb1e --- /dev/null +++ b/include/sorac/version.hpp @@ -0,0 +1,16 @@ +#ifndef SORAC_VERSION_HPP_ +#define SORAC_VERSION_HPP_ + +#include + +namespace sorac { + +class Version { + public: + static std::string GetClientName(); + static std::string GetEnvironment(); +}; + +} // namespace sorac + +#endif diff --git a/run.py b/run.py index 8cce521..d5ecf7c 100644 --- a/run.py +++ b/run.py @@ -625,6 +625,64 @@ def install_deps(target_platform: str, build_platform: str, source_dir, shared_s install_libyuv(**install_libyuv_args) +class LibVersion(object): + sora_c_sdk: str + sora_c_sdk_commit: str + libdatachannel: str + opus: str + mbedtls: str + nlohmann_json: str + libjuice: str + libsrtp: str + plog: str + usrsctp: str + + def to_cmake(self): + return [f'-DSORA_C_SDK_VERSION={self.sora_c_sdk}', + f'-DSORA_C_SDK_COMMIT={self.sora_c_sdk_commit}', + f'-DLIBDATACHANNEL_VERSION={self.libdatachannel}', + f'-DOPUS_VERSION={self.opus}', + f'-DMBEDTLS_VERSION={self.mbedtls}', + f'-DNLOHMANN_JSON_VERSION={self.nlohmann_json}', + f'-DLIBJUICE_VERSION={self.libjuice}', + f'-DLIBSRTP_VERSION={self.libsrtp}', + f'-DPLOG_VERSION={self.plog}', + f'-DUSRSCTP_VERSION={self.usrsctp}'] + + @staticmethod + def create(version, base_dir, libdatachannel_dir): + libv = LibVersion() + with cd(base_dir): + libv.sora_c_sdk_commit = cmdcap(['git', 'rev-parse', 'HEAD']) + with cd(libdatachannel_dir): + # 以下のような出力が得られるので、ここから必要な部分を取り出す + # bc889afb4c5bf1c0d8ee29ef35eaaf4c8bef8a5d deps/json (bc889afb) + # 5f753cad49059cea4eb492eb5c11a3bbb4dd6324 deps/libjuice (v1.3.3) + # a566a9cfcd619e8327784aa7cff4a1276dc1e895 deps/libsrtp (a566a9c) + # e21baecd4753f14da64ede979c5a19302618b752 deps/plog (e21baec) + # 5ca29ac7d8055802c7657191325c06386640ac24 deps/usrsctp (5ca29ac) + r = cmdcap(['git', 'submodule', 'status']) + lines = r.split('\n') + for line in lines: + name, commit = line.strip().split(' ')[1:3] + commit = commit.strip('()') + if '/json' in name: + libv.nlohmann_json = commit + elif '/libjuice' in name: + libv.libjuice = commit + elif '/libsrtp' in name: + libv.libsrtp = commit + elif '/plog' in name: + libv.plog = commit + elif '/usrsctp' in name: + libv.usrsctp = commit + libv.sora_c_sdk = version['SORA_C_SDK_VERSION'] + libv.libdatachannel = version['LIBDATACHANNEL_VERSION'] + libv.opus = version['OPUS_VERSION'] + libv.mbedtls = version['MBEDTLS_VERSION'] + return libv + + AVAILABLE_TARGETS = ['windows_x86_64', 'macos_x86_64', 'macos_arm64', 'ubuntu-20.04_x86_64', 'ubuntu-22.04_x86_64', 'ios', 'android'] @@ -685,16 +743,12 @@ def main(): cmake_args = [] cmake_args.append(f'-DCMAKE_BUILD_TYPE={configuration}') cmake_args.append(f"-DCMAKE_INSTALL_PREFIX={cmake_path(os.path.join(install_dir, 'sorac'))}") + cmake_args.append(f"-DSORAC_TARGET={target_platform}") + libver = LibVersion.create(read_version_file(os.path.join(BASE_DIR, 'VERSION')), + BASE_DIR, os.path.join(shared_source_dir, 'libdatachannel')) + cmake_args += libver.to_cmake() cmake_args.append(f"-DPROTOBUF_DIR={cmake_path(os.path.join(install_dir, 'protobuf'))}") cmake_args.append(f"-DPROTOC_GEN_JSONIF_DIR={cmake_path(os.path.join(install_dir, 'protoc-gen-jsonif'))}") - with cd(BASE_DIR): - version = read_version_file('VERSION') - sora_c_sdk_version = version['SORA_C_SDK_VERSION'] - sora_c_sdk_commit = cmdcap(['git', 'rev-parse', 'HEAD']) - # android_native_api_level = version['ANDROID_NATIVE_API_LEVEL'] - cmake_args.append(f"-DSORAC_VERSION={sora_c_sdk_version}") - cmake_args.append(f"-DSORAC_COMMIT={sora_c_sdk_commit}") - cmake_args.append(f"-DSORAC_TARGET={target_platform}") if target_platform in ('macos_x86_64', 'macos_arm64'): sysroot = cmdcap(['xcrun', '--sdk', 'macosx', '--show-sdk-path']) target = 'x86_64-apple-darwin' if target_platform in ('macos_x86_64',) else 'aarch64-apple-darwin' diff --git a/src/mac_version.hpp b/src/mac_version.hpp new file mode 100644 index 0000000..45c9e2e --- /dev/null +++ b/src/mac_version.hpp @@ -0,0 +1,16 @@ +#ifndef SORAC_MAC_VERSION_HPP_ +#define SORAC_MAC_VERSION_HPP_ + +#include + +namespace sorac { + +class MacVersion { + public: + static std::string GetOSName(); + static std::string GetOSVersion(); +}; + +} // namespace sorac + +#endif diff --git a/src/mac_version.mm b/src/mac_version.mm new file mode 100644 index 0000000..af03296 --- /dev/null +++ b/src/mac_version.mm @@ -0,0 +1,95 @@ +#include "mac_version.h" + +#import +#include + +// TARGET_OS_* から OS 名を調べる。 +// アーキテクチャもマクロから分かるけど、それは実行時に uname を使って調べるので不要 + +// 以下の情報は /Library/Developer/CommandLineTools/SDKs/MacOSX10.15.sdk/usr/include/TargetConditionals.h からのコピペ + +/**************************************************************************************************** + + TARGET_CPU_* + These conditionals specify which microprocessor instruction set is being + generated. At most one of these is true, the rest are false. + + TARGET_CPU_PPC - Compiler is generating PowerPC instructions for 32-bit mode + TARGET_CPU_PPC64 - Compiler is generating PowerPC instructions for 64-bit mode + TARGET_CPU_68K - Compiler is generating 680x0 instructions + TARGET_CPU_X86 - Compiler is generating x86 instructions for 32-bit mode + TARGET_CPU_X86_64 - Compiler is generating x86 instructions for 64-bit mode + TARGET_CPU_ARM - Compiler is generating ARM instructions for 32-bit mode + TARGET_CPU_ARM64 - Compiler is generating ARM instructions for 64-bit mode + TARGET_CPU_MIPS - Compiler is generating MIPS instructions + TARGET_CPU_SPARC - Compiler is generating Sparc instructions + TARGET_CPU_ALPHA - Compiler is generating Dec Alpha instructions + + + TARGET_OS_* + These conditionals specify in which Operating System the generated code will + run. Indention is used to show which conditionals are evolutionary subclasses. + + The MAC/WIN32/UNIX conditionals are mutually exclusive. + The IOS/TV/WATCH conditionals are mutually exclusive. + + + TARGET_OS_WIN32 - Generated code will run under 32-bit Windows + TARGET_OS_UNIX - Generated code will run under some Unix (not OSX) + TARGET_OS_MAC - Generated code will run under Mac OS X variant + TARGET_OS_OSX - Generated code will run under OS X devices + TARGET_OS_IPHONE - Generated code for firmware, devices, or simulator + TARGET_OS_IOS - Generated code will run under iOS + TARGET_OS_TV - Generated code will run under Apple TV OS + TARGET_OS_WATCH - Generated code will run under Apple Watch OS + TARGET_OS_BRIDGE - Generated code will run under Bridge devices + TARGET_OS_MACCATALYST - Generated code will run under macOS + TARGET_OS_SIMULATOR - Generated code will run under a simulator + + TARGET_OS_EMBEDDED - DEPRECATED: Use TARGET_OS_IPHONE and/or TARGET_OS_SIMULATOR instead + TARGET_IPHONE_SIMULATOR - DEPRECATED: Same as TARGET_OS_SIMULATOR + TARGET_OS_NANO - DEPRECATED: Same as TARGET_OS_WATCH + + +----------------------------------------------------------------+ + | TARGET_OS_MAC | + | +---+ +-----------------------------------------------------+ | + | | | | TARGET_OS_IPHONE | | + | |OSX| | +-----+ +----+ +-------+ +--------+ +-------------+ | | + | | | | | IOS | | TV | | WATCH | | BRIDGE | | MACCATALYST | | | + | | | | +-----+ +----+ +-------+ +--------+ +-------------+ | | + | +---+ +-----------------------------------------------------+ | + +----------------------------------------------------------------+ + + TARGET_RT_* + These conditionals specify in which runtime the generated code will + run. This is needed when the OS and CPU support more than one runtime + (e.g. Mac OS X supports CFM and mach-o). + + TARGET_RT_LITTLE_ENDIAN - Generated code uses little endian format for integers + TARGET_RT_BIG_ENDIAN - Generated code uses big endian format for integers + TARGET_RT_64_BIT - Generated code uses 64-bit pointers + TARGET_RT_MAC_CFM - TARGET_OS_MAC is true and CFM68K or PowerPC CFM (TVectors) are used + TARGET_RT_MAC_MACHO - TARGET_OS_MAC is true and Mach-O/dlyd runtime is used + + +****************************************************************************************************/ + +namespace sorac { + +std::string MacVersion::GetOSName() { +#if TARGET_OS_MAC + return "macOS"; +#elif TARGET_OS_IPHONE + return "iPhone"; +#else + return "Unknown OS"; +#endif +} + +std::string MacVersion::GetOSVersion() { + // "Version 10.8.2 (Build 12C60)" みたいな文字列を取得できる + NSString* str = NSProcessInfo.processInfo.operatingSystemVersionString; + return [str UTF8String]; +} + +} diff --git a/src/signaling.cpp b/src/signaling.cpp index 40d7eae..79424d5 100644 --- a/src/signaling.cpp +++ b/src/signaling.cpp @@ -14,11 +14,14 @@ #include "sorac/current_time.hpp" #include "sorac/open_h264_video_encoder.hpp" #include "sorac/opus_audio_encoder.hpp" +#include "sorac/version.hpp" #if defined(__APPLE__) #include "sorac/vt_h26x_video_encoder.hpp" #endif +#include "util.hpp" + // https://github.com/paullouisageneau/libdatachannel/issues/990 namespace rtc { using ::operator<<; @@ -48,49 +51,6 @@ struct Client { std::map> dcs; }; -static std::string generate_random_string(int length, std::string pattern) { - if (pattern.size() == 0) { - return ""; - } - - std::random_device random; - // % を計算する時にマイナス値があると危険なので unsigned 型であることを保証する - typedef std::make_unsigned::type - unsigned_type; - std::string r; - for (int i = 0; i < length; i++) { - r += pattern[(unsigned_type)random() % pattern.size()]; - } - return r; -} - -static std::string generate_random_string(int length) { - return generate_random_string( - length, "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"); -} - -static std::vector split_with(const std::string& str, - const std::string& token) { - int sp = 0; - std::vector lines; - while (true) { - auto ep = str.find(token, sp); - if (ep == std::string::npos) { - if (str.size() - sp > 0) { - lines.push_back(str.substr(sp)); - } - break; - } - lines.push_back(str.substr(sp, ep - sp)); - sp = ep + token.size(); - } - return lines; -} - -static bool starts_with(const std::string& str, const std::string& s) { - return str.substr(0, s.size()) == s; -} - class SignalingImpl : public Signaling { public: SignalingImpl(const soracp::SignalingConfig& config) : config_(config) {} @@ -486,6 +446,8 @@ class SignalingImpl : public Signaling { {"type", "connect"}, {"role", sc.role}, {"channel_id", sc.channel_id}, + {"sora_client", Version::GetClientName()}, + {"environment", Version::GetEnvironment()}, }; auto set_if = [](nlohmann::json& js, const std::string& key, auto value, bool cond) { diff --git a/src/util.cpp b/src/util.cpp new file mode 100644 index 0000000..c0f7b12 --- /dev/null +++ b/src/util.cpp @@ -0,0 +1,59 @@ +#include "util.hpp" + +#include + +namespace sorac { + +std::string generate_random_string(int length) { + return generate_random_string( + length, "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"); +} + +std::string generate_random_string(int length, std::string pattern) { + if (pattern.size() == 0) { + return ""; + } + + std::random_device random; + // % を計算する時にマイナス値があると危険なので unsigned 型であることを保証する + typedef std::make_unsigned::type + unsigned_type; + std::string r; + for (int i = 0; i < length; i++) { + r += pattern[(unsigned_type)random() % pattern.size()]; + } + return r; +} + +std::vector split_with(const std::string& str, + const std::string& token) { + int sp = 0; + std::vector lines; + while (true) { + auto ep = str.find(token, sp); + if (ep == std::string::npos) { + if (str.size() - sp > 0) { + lines.push_back(str.substr(sp)); + } + break; + } + lines.push_back(str.substr(sp, ep - sp)); + sp = ep + token.size(); + } + return lines; +} + +bool starts_with(const std::string& str, const std::string& s) { + return str.substr(0, s.size()) == s; +} + +std::string trim(const std::string& str, const std::string& trim_chars) { + auto sp = str.find_first_not_of(trim_chars); + if (sp == std::string::npos) { + return ""; + } + auto ep = str.find_last_not_of(trim_chars); + return str.substr(sp, ep - sp + 1); +} + +} // namespace sorac \ No newline at end of file diff --git a/src/util.hpp b/src/util.hpp new file mode 100644 index 0000000..a0ca7ab --- /dev/null +++ b/src/util.hpp @@ -0,0 +1,18 @@ +#ifndef SORAC_UTIL_HPP_ +#define SORAC_UTIL_HPP_ + +#include +#include + +namespace sorac { + +std::string generate_random_string(int length); +std::string generate_random_string(int length, std::string pattern); +std::vector split_with(const std::string& str, + const std::string& token); +bool starts_with(const std::string& str, const std::string& s); +std::string trim(const std::string& str, const std::string& trim_chars); + +} // namespace sorac + +#endif diff --git a/src/version.cpp b/src/version.cpp new file mode 100644 index 0000000..6000a18 --- /dev/null +++ b/src/version.cpp @@ -0,0 +1,147 @@ +#include "sorac/version.hpp" + +#include +#include +#include + +#include "util.hpp" +#include "version.gen.h" + +#if defined(__APPLE__) || defined(__linux__) +#include +#endif + +#if defined(__APPLE__) +#include "mac_version.hpp" +#endif + +namespace sorac { + +std::string Version::GetClientName() { + return "Sora C SDK " SORA_C_SDK_VERSION " (" SORA_C_SDK_COMMIT ")"; +} + +std::string Version::GetEnvironment() { + std::string os = "Unknown OS"; + std::string osver = "Unknown Version"; + std::string arch = "Unknown Arch"; + +#if defined(WIN32) + os = "Windows"; + + SYSTEM_INFO sysInfo; + GetSystemInfo(&sysInfo); + switch (sysInfo.wProcessorArchitecture) { + // x64 (AMD or Intel) + case PROCESSOR_ARCHITECTURE_AMD64: + arch = "x64"; + break; + // ARM + case PROCESSOR_ARCHITECTURE_ARM: + arch = "arm"; + break; + // ARM64 + case PROCESSOR_ARCHITECTURE_ARM64: + arch = "arm64"; + break; + // Intel Itanium-based + case PROCESSOR_ARCHITECTURE_IA64: + arch = "IA64"; + break; + // x86 + case PROCESSOR_ARCHITECTURE_INTEL: + arch = "x86"; + break; + case PROCESSOR_ARCHITECTURE_UNKNOWN: + default: + arch = "unknown"; + break; + } + + HMODULE module = GetModuleHandleW(L"ntdll.dll"); + if (module != nullptr) { + typedef int(WINAPI * RtlGetVersionFunc)(LPOSVERSIONINFOW); + + auto rtl_get_version = + (RtlGetVersionFunc)GetProcAddress(module, "RtlGetVersion"); + if (rtl_get_version != nullptr) { + OSVERSIONINFOW versionInfo = {sizeof(OSVERSIONINFOW)}; + auto status = rtl_get_version(&versionInfo); + if (status == 0) { + osver = std::to_string(versionInfo.dwMajorVersion) + "." + + std::to_string(versionInfo.dwMinorVersion) + " Build " + + std::to_string(versionInfo.dwBuildNumber); + } + } + } + +#endif + +#if defined(__APPLE__) || defined(__linux__) + struct utsname u; + int r = uname(&u); + if (r == 0) { + arch = u.machine; + } +#endif + +#if defined(__APPLE__) + os = MacVersion::GetOSName(); + osver = MacVersion::GetOSVersion(); +#else + // /etc/os-release ファイルを読んで PRETTY_NAME を利用する + + // /etc/os-release は以下のような内容になっているので、これを適当にパースする + /* + $ docker run -it --rm ubuntu cat /etc/os-release + NAME="Ubuntu" + VERSION="18.04.3 LTS (Bionic Beaver)" + ID=ubuntu + ID_LIKE=debian + PRETTY_NAME="Ubuntu 18.04.3 LTS" + VERSION_ID="18.04" + HOME_URL="https://www.ubuntu.com/" + SUPPORT_URL="https://help.ubuntu.com/" + BUG_REPORT_URL="https://bugs.launchpad.net/ubuntu/" + PRIVACY_POLICY_URL="https://www.ubuntu.com/legal/terms-and-policies/privacy-policy" + VERSION_CODENAME=bionic + UBUNTU_CODENAME=bionic + */ + // 行ごとに分けたデータを取得 + std::vector lines; + { + std::stringstream ss; + std::ifstream fin("/etc/os-release"); + ss << fin.rdbuf(); + std::string content = ss.str(); + lines = split_with(ss.str(), "\n"); + } + const std::string PRETTY_NAME = "PRETTY_NAME="; + for (auto& line : lines) { + // 先頭が PRETTY_NAME= の行を探す + if (line.find(PRETTY_NAME) != 0) { + continue; + } + // PRETTY_NAME= 以降のデータを取り出す + os = line.substr(PRETTY_NAME.size()); + // 左右の " を除ける + os = trim(os, "\""); + // os にバージョン情報も含まれてしまったので、osver には空文字を設定しておく + osver = ""; + break; + } +#endif + + std::string env; + env += os; + if (!osver.empty()) { + env += " " + osver; + } + if (!arch.empty()) { + env += " [" + arch + "]"; + } + env += std::string(" / ") + SORAC_ALL_VERSION; + return env; +} + +} // namespace sorac \ No newline at end of file diff --git a/src/version.gen.h.template b/src/version.gen.h.template new file mode 100644 index 0000000..2ab5063 --- /dev/null +++ b/src/version.gen.h.template @@ -0,0 +1,26 @@ +#ifndef VERSION_GEN_H_ +#define VERSION_GEN_H_ + +#define SORA_C_SDK_VERSION "@SORA_C_SDK_VERSION@" +#define SORA_C_SDK_COMMIT "@SORA_C_SDK_COMMIT@" +#define LIBDATACHANNEL_VERSION "@LIBDATACHANNEL_VERSION@" +#define OPUS_VERSION "@OPUS_VERSION@" +#define MBEDTLS_VERSION "@MBEDTLS_VERSION@" +#define NLOHMANN_JSON_VERSION "@NLOHMANN_JSON_VERSION@" +#define LIBJUICE_VERSION "@LIBJUICE_VERSION@" +#define LIBSRTP_VERSION "@LIBSRTP_VERSION@" +#define PLOG_VERSION "@PLOG_VERSION@" +#define USRSCTP_VERSION "@USRSCTP_VERSION@" + +#define SORAC_ALL_VERSION \ + "sora-c-sdk " SORA_C_SDK_VERSION " / " \ + "libdatachannel " LIBDATACHANNEL_VERSION " / " \ + "opus " OPUS_VERSION " / " \ + "mbedtls " MBEDTLS_VERSION " / " \ + "nlohmann-json " NLOHMANN_JSON_VERSION " / " \ + "libjuice " LIBJUICE_VERSION " / " \ + "libsrtp " LIBSRTP_VERSION " / " \ + "plog " PLOG_VERSION " / " \ + "usrsctp " USRSCTP_VERSION + +#endif