From 1d7d3d5c15c935924847cea353a168b9eb64ce33 Mon Sep 17 00:00:00 2001 From: voluntas Date: Mon, 26 Sep 2022 13:31:11 +0900 Subject: [PATCH 01/14] Update README.md --- README.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/README.md b/README.md index d60a608..595c510 100644 --- a/README.md +++ b/README.md @@ -95,6 +95,11 @@ Please read https://github.com/shiguredo/oss/blob/master/README.en.md before use - H.264 のハードウェアエンコードへの対応 - H.264 のハードウェアデコードへの対応 +## Sora Unity SDK for MS Hololens2 + +- support/hololens2 ブランチの修正や改善は有償でお受けしています +- 継続的な有償サポートは提供しておりません + ## 有償での優先実装 - Windows 版 NVIDIA VIDEO CODEC SDK による H.264 エンコーダ対応 From 77b3c48376dccffdca7ef51ae1f538a65f162efe Mon Sep 17 00:00:00 2001 From: miosakuma Date: Thu, 29 Sep 2022 11:08:51 +0900 Subject: [PATCH 02/14] [doc] Sora -> WebRTC SFU Sora --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 595c510..b8ec572 100644 --- a/README.md +++ b/README.md @@ -38,7 +38,7 @@ Please read https://github.com/shiguredo/oss/blob/master/README.en.md before use ## システム条件 -- Sora 2022.1.1 以降 +- WebRTC SFU Sora 2022.1.1 以降 ## 対応プラットフォーム From e1da67db081a1ef6969d60a48cbb87f33acd757f Mon Sep 17 00:00:00 2001 From: melpon Date: Mon, 3 Oct 2022 11:57:28 +0900 Subject: [PATCH 03/14] =?UTF-8?q?UnityCameraCapturer=20=E3=81=8C=E3=83=9E?= =?UTF-8?q?=E3=83=AB=E3=83=81=E3=82=B9=E3=83=AC=E3=83=83=E3=83=89=E4=B8=8B?= =?UTF-8?q?=E3=81=A7=E6=AD=A3=E5=B8=B8=E3=81=AB=E7=B5=82=E4=BA=86=E3=81=97?= =?UTF-8?q?=E3=81=AA=E3=81=84=E3=81=93=E3=81=A8=E3=81=8C=E3=81=82=E3=82=8B?= =?UTF-8?q?=E3=81=AE=E3=82=92=E4=BF=AE=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/sora.cpp | 3 +++ src/unity_camera_capturer.cpp | 10 ++++++++++ src/unity_camera_capturer.h | 4 ++++ 3 files changed, 17 insertions(+) diff --git a/src/sora.cpp b/src/sora.cpp index fc49a35..19641c0 100644 --- a/src/sora.cpp +++ b/src/sora.cpp @@ -57,6 +57,9 @@ Sora::~Sora() { static_cast(capturer_.get())->Stop(); } #endif + if (capturer_ != nullptr && capturer_type_ != 0) { + static_cast(capturer_.get())->Stop(); + } capturer_sink_ = nullptr; capturer_ = nullptr; unity_adm_ = nullptr; diff --git a/src/unity_camera_capturer.cpp b/src/unity_camera_capturer.cpp index 00965cc..411234b 100644 --- a/src/unity_camera_capturer.cpp +++ b/src/unity_camera_capturer.cpp @@ -18,6 +18,11 @@ rtc::scoped_refptr UnityCameraCapturer::Create( } void UnityCameraCapturer::OnRender() { + std::lock_guard guard(mutex_); + if (stopped_) { + return; + } + #if defined(SORA_UNITY_SDK_WINDOWS) || defined(SORA_UNITY_SDK_MACOS) || \ defined(SORA_UNITY_SDK_IOS) || defined(SORA_UNITY_SDK_ANDROID) || \ defined(SORA_UNITY_SDK_UBUNTU) @@ -39,6 +44,11 @@ void UnityCameraCapturer::OnFrame(const webrtc::VideoFrame& frame) { OnCapturedFrame(frame); } +void UnityCameraCapturer::Stop() { + std::lock_guard guard(mutex_); + stopped_ = true; +} + bool UnityCameraCapturer::Init(UnityContext* context, void* unity_camera_texture, int width, diff --git a/src/unity_camera_capturer.h b/src/unity_camera_capturer.h index 05c8bd4..96ede73 100644 --- a/src/unity_camera_capturer.h +++ b/src/unity_camera_capturer.h @@ -115,6 +115,8 @@ class UnityCameraCapturer : public sora::ScalableVideoTrackSource, #endif std::unique_ptr capturer_; + std::mutex mutex_; + bool stopped_ = false; public: static rtc::scoped_refptr Create( @@ -124,6 +126,8 @@ class UnityCameraCapturer : public sora::ScalableVideoTrackSource, void OnRender(); + void Stop(); + void OnFrame(const webrtc::VideoFrame& frame) override; private: From 67911897c4b3e1f0c242d6d7246e4e2e547cadf2 Mon Sep 17 00:00:00 2001 From: melpon Date: Mon, 3 Oct 2022 12:01:12 +0900 Subject: [PATCH 04/14] =?UTF-8?q?CRLF=20=E2=86=92=20LF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 10 +- .vscode/settings.json | 212 +-- Sora/Editor/SoraUnitySdkPostProcessor.cs | 88 +- run.py | 1584 +++++++++++----------- src/device_list.cpp | 342 ++--- src/device_list.h | 40 +- src/id_pointer.h | 54 +- src/sora.h | 348 ++--- src/unity_audio_device.h | 900 ++++++------ src/unity_camera_capturer.h | 284 ++-- src/unity_context.h | 108 +- src/unity_renderer.h | 108 +- 12 files changed, 2039 insertions(+), 2039 deletions(-) diff --git a/.gitignore b/.gitignore index 37c699f..6f0630d 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,5 @@ -/_source -/_build -/_install -*.swp -/Sora/Generated +/_source +/_build +/_install +*.swp +/Sora/Generated diff --git a/.vscode/settings.json b/.vscode/settings.json index 20ef565..7d38b0b 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,107 +1,107 @@ -{ - "[cpp]": { - "editor.formatOnSave": true, - }, - "files.associations": { - "*.cs": "csharp", - "memory": "cpp", - "utility": "cpp", - "xmemory": "cpp", - "thread": "cpp", - "algorithm": "cpp", - "any": "cpp", - "array": "cpp", - "atomic": "cpp", - "bitset": "cpp", - "cctype": "cpp", - "cfenv": "cpp", - "charconv": "cpp", - "chrono": "cpp", - "cinttypes": "cpp", - "clocale": "cpp", - "cmath": "cpp", - "codecvt": "cpp", - "compare": "cpp", - "complex": "cpp", - "concepts": "cpp", - "condition_variable": "cpp", - "csetjmp": "cpp", - "csignal": "cpp", - "cstdarg": "cpp", - "cstddef": "cpp", - "cstdint": "cpp", - "cstdio": "cpp", - "cstdlib": "cpp", - "cstring": "cpp", - "ctime": "cpp", - "cwchar": "cpp", - "cwctype": "cpp", - "deque": "cpp", - "exception": "cpp", - "execution": "cpp", - "resumable": "cpp", - "filesystem": "cpp", - "forward_list": "cpp", - "fstream": "cpp", - "functional": "cpp", - "future": "cpp", - "initializer_list": "cpp", - "iomanip": "cpp", - "ios": "cpp", - "iosfwd": "cpp", - "iostream": "cpp", - "istream": "cpp", - "iterator": "cpp", - "limits": "cpp", - "list": "cpp", - "locale": "cpp", - "map": "cpp", - "memory_resource": "cpp", - "mutex": "cpp", - "new": "cpp", - "numeric": "cpp", - "optional": "cpp", - "ostream": "cpp", - "queue": "cpp", - "random": "cpp", - "ratio": "cpp", - "regex": "cpp", - "scoped_allocator": "cpp", - "set": "cpp", - "shared_mutex": "cpp", - "sstream": "cpp", - "stack": "cpp", - "stdexcept": "cpp", - "streambuf": "cpp", - "string": "cpp", - "strstream": "cpp", - "system_error": "cpp", - "tuple": "cpp", - "type_traits": "cpp", - "typeindex": "cpp", - "typeinfo": "cpp", - "unordered_map": "cpp", - "unordered_set": "cpp", - "valarray": "cpp", - "variant": "cpp", - "vector": "cpp", - "xfacet": "cpp", - "xhash": "cpp", - "xiosbase": "cpp", - "xlocale": "cpp", - "xlocbuf": "cpp", - "xlocinfo": "cpp", - "xlocmes": "cpp", - "xlocmon": "cpp", - "xlocnum": "cpp", - "xloctime": "cpp", - "xstddef": "cpp", - "xstring": "cpp", - "xtr1common": "cpp", - "xtree": "cpp", - "xutility": "cpp", - "*.ipp": "cpp", - "hash_map": "cpp", - "hash_set": "cpp" - } +{ + "[cpp]": { + "editor.formatOnSave": true, + }, + "files.associations": { + "*.cs": "csharp", + "memory": "cpp", + "utility": "cpp", + "xmemory": "cpp", + "thread": "cpp", + "algorithm": "cpp", + "any": "cpp", + "array": "cpp", + "atomic": "cpp", + "bitset": "cpp", + "cctype": "cpp", + "cfenv": "cpp", + "charconv": "cpp", + "chrono": "cpp", + "cinttypes": "cpp", + "clocale": "cpp", + "cmath": "cpp", + "codecvt": "cpp", + "compare": "cpp", + "complex": "cpp", + "concepts": "cpp", + "condition_variable": "cpp", + "csetjmp": "cpp", + "csignal": "cpp", + "cstdarg": "cpp", + "cstddef": "cpp", + "cstdint": "cpp", + "cstdio": "cpp", + "cstdlib": "cpp", + "cstring": "cpp", + "ctime": "cpp", + "cwchar": "cpp", + "cwctype": "cpp", + "deque": "cpp", + "exception": "cpp", + "execution": "cpp", + "resumable": "cpp", + "filesystem": "cpp", + "forward_list": "cpp", + "fstream": "cpp", + "functional": "cpp", + "future": "cpp", + "initializer_list": "cpp", + "iomanip": "cpp", + "ios": "cpp", + "iosfwd": "cpp", + "iostream": "cpp", + "istream": "cpp", + "iterator": "cpp", + "limits": "cpp", + "list": "cpp", + "locale": "cpp", + "map": "cpp", + "memory_resource": "cpp", + "mutex": "cpp", + "new": "cpp", + "numeric": "cpp", + "optional": "cpp", + "ostream": "cpp", + "queue": "cpp", + "random": "cpp", + "ratio": "cpp", + "regex": "cpp", + "scoped_allocator": "cpp", + "set": "cpp", + "shared_mutex": "cpp", + "sstream": "cpp", + "stack": "cpp", + "stdexcept": "cpp", + "streambuf": "cpp", + "string": "cpp", + "strstream": "cpp", + "system_error": "cpp", + "tuple": "cpp", + "type_traits": "cpp", + "typeindex": "cpp", + "typeinfo": "cpp", + "unordered_map": "cpp", + "unordered_set": "cpp", + "valarray": "cpp", + "variant": "cpp", + "vector": "cpp", + "xfacet": "cpp", + "xhash": "cpp", + "xiosbase": "cpp", + "xlocale": "cpp", + "xlocbuf": "cpp", + "xlocinfo": "cpp", + "xlocmes": "cpp", + "xlocmon": "cpp", + "xlocnum": "cpp", + "xloctime": "cpp", + "xstddef": "cpp", + "xstring": "cpp", + "xtr1common": "cpp", + "xtree": "cpp", + "xutility": "cpp", + "*.ipp": "cpp", + "hash_map": "cpp", + "hash_set": "cpp" + } } \ No newline at end of file diff --git a/Sora/Editor/SoraUnitySdkPostProcessor.cs b/Sora/Editor/SoraUnitySdkPostProcessor.cs index 30af664..8d3aab2 100644 --- a/Sora/Editor/SoraUnitySdkPostProcessor.cs +++ b/Sora/Editor/SoraUnitySdkPostProcessor.cs @@ -1,44 +1,44 @@ -#if UNITY_IOS - -using UnityEngine; -using UnityEditor; -using UnityEditor.iOS.Xcode; -using UnityEditor.Callbacks; - -public class SoraUnitySdkPostProcessor -{ - [PostProcessBuildAttribute(500)] - public static void OnPostprocessBuild(BuildTarget buildTarget, string pathToBuiltProject) - { - if (buildTarget != BuildTarget.iOS) - { - return; - } - - var projPath = pathToBuiltProject + "/Unity-iPhone.xcodeproj/project.pbxproj"; - PBXProject proj = new PBXProject(); - proj.ReadFromFile(projPath); -#if UNITY_2019_3_OR_NEWER - string guid = proj.GetUnityFrameworkTargetGuid(); -#else - string guid = proj.TargetGuidByName("Unity-iPhone"); -#endif - - proj.AddBuildProperty(guid, "OTHER_LDFLAGS", "-ObjC"); - proj.SetBuildProperty(guid, "ENABLE_BITCODE", "NO"); - proj.AddFrameworkToProject(guid, "VideoToolbox.framework", false); - proj.AddFrameworkToProject(guid, "GLKit.framework", false); - proj.AddFrameworkToProject(guid, "Network.framework", false); - // libwebrtc.a には新しい libvpx が、libiPhone-lib.a には古い libvpx が入っていて、 - // デフォルトのリンク順序だと古い libvpx が使われてしまう。 - // それを回避するために libiPhone-lib.a を削除して新しく追加し直すことで - // リンク順序を変えてやる。 - string fileGuid = proj.FindFileGuidByProjectPath("Libraries/libiPhone-lib.a"); - proj.RemoveFileFromBuild(guid, fileGuid); - proj.AddFileToBuild(guid, fileGuid); - - proj.WriteToFile(projPath); - } -} - -#endif +#if UNITY_IOS + +using UnityEngine; +using UnityEditor; +using UnityEditor.iOS.Xcode; +using UnityEditor.Callbacks; + +public class SoraUnitySdkPostProcessor +{ + [PostProcessBuildAttribute(500)] + public static void OnPostprocessBuild(BuildTarget buildTarget, string pathToBuiltProject) + { + if (buildTarget != BuildTarget.iOS) + { + return; + } + + var projPath = pathToBuiltProject + "/Unity-iPhone.xcodeproj/project.pbxproj"; + PBXProject proj = new PBXProject(); + proj.ReadFromFile(projPath); +#if UNITY_2019_3_OR_NEWER + string guid = proj.GetUnityFrameworkTargetGuid(); +#else + string guid = proj.TargetGuidByName("Unity-iPhone"); +#endif + + proj.AddBuildProperty(guid, "OTHER_LDFLAGS", "-ObjC"); + proj.SetBuildProperty(guid, "ENABLE_BITCODE", "NO"); + proj.AddFrameworkToProject(guid, "VideoToolbox.framework", false); + proj.AddFrameworkToProject(guid, "GLKit.framework", false); + proj.AddFrameworkToProject(guid, "Network.framework", false); + // libwebrtc.a には新しい libvpx が、libiPhone-lib.a には古い libvpx が入っていて、 + // デフォルトのリンク順序だと古い libvpx が使われてしまう。 + // それを回避するために libiPhone-lib.a を削除して新しく追加し直すことで + // リンク順序を変えてやる。 + string fileGuid = proj.FindFileGuidByProjectPath("Libraries/libiPhone-lib.a"); + proj.RemoveFileFromBuild(guid, fileGuid); + proj.AddFileToBuild(guid, fileGuid); + + proj.WriteToFile(projPath); + } +} + +#endif diff --git a/run.py b/run.py index 898374e..1a6278e 100644 --- a/run.py +++ b/run.py @@ -1,792 +1,792 @@ -import subprocess -import logging -import os -import stat -import urllib.parse -import zipfile -import tarfile -import shutil -import platform -import multiprocessing -import argparse -from typing import Callable, NamedTuple, Optional, List, Union, Dict - - -logging.basicConfig(level=logging.DEBUG) - - -class ChangeDirectory(object): - def __init__(self, cwd): - self._cwd = cwd - - def __enter__(self): - self._old_cwd = os.getcwd() - logging.debug(f'pushd {self._old_cwd} --> {self._cwd}') - os.chdir(self._cwd) - - def __exit__(self, exctype, excvalue, trace): - logging.debug(f'popd {self._old_cwd} <-- {self._cwd}') - os.chdir(self._old_cwd) - return False - - -def cd(cwd): - return ChangeDirectory(cwd) - - -def cmd(args, **kwargs): - logging.debug(f'+{args} {kwargs}') - if 'check' not in kwargs: - kwargs['check'] = True - if 'resolve' in kwargs: - resolve = kwargs['resolve'] - del kwargs['resolve'] - else: - resolve = True - if resolve: - args = [shutil.which(args[0]), *args[1:]] - return subprocess.run(args, **kwargs) - - -# 標準出力をキャプチャするコマンド実行。シェルの `cmd ...` や $(cmd ...) と同じ -def cmdcap(args, **kwargs): - # 3.7 でしか使えない - # kwargs['capture_output'] = True - kwargs['stdout'] = subprocess.PIPE - kwargs['stderr'] = subprocess.PIPE - kwargs['encoding'] = 'utf-8' - return cmd(args, **kwargs).stdout.strip() - - -def rm_rf(path: str): - if not os.path.exists(path): - logging.debug(f'rm -rf {path} => path not found') - return - if os.path.isfile(path) or os.path.islink(path): - os.remove(path) - logging.debug(f'rm -rf {path} => file removed') - if os.path.isdir(path): - shutil.rmtree(path) - logging.debug(f'rm -rf {path} => directory removed') - - -def mkdir_p(path: str): - if os.path.exists(path): - logging.debug(f'mkdir -p {path} => already exists') - return - os.makedirs(path, exist_ok=True) - logging.debug(f'mkdir -p {path} => directory created') - - -if platform.system() == 'Windows': - PATH_SEPARATOR = ';' -else: - PATH_SEPARATOR = ':' - - -def add_path(path: str, is_after=False): - logging.debug(f'add_path: {path}') - if 'PATH' not in os.environ: - os.environ['PATH'] = path - return - - if is_after: - os.environ['PATH'] = os.environ['PATH'] + PATH_SEPARATOR + path - else: - os.environ['PATH'] = path + PATH_SEPARATOR + os.environ['PATH'] - - -def download(url: str, output_dir: Optional[str] = None, filename: Optional[str] = None) -> str: - if filename is None: - output_path = urllib.parse.urlparse(url).path.split('/')[-1] - else: - output_path = filename - - if output_dir is not None: - output_path = os.path.join(output_dir, output_path) - - if os.path.exists(output_path): - return output_path - - try: - if shutil.which('curl') is not None: - cmd(["curl", "-fLo", output_path, url]) - else: - cmd(["wget", "-cO", output_path, url]) - except Exception: - # ゴミを残さないようにする - if os.path.exists(output_path): - os.remove(output_path) - raise - - return output_path - - -def read_version_file(path: str) -> Dict[str, str]: - versions = {} - - lines = open(path).readlines() - for line in lines: - line = line.strip() - - # コメント行 - if line[:1] == '#': - continue - - # 空行 - if len(line) == 0: - continue - - [a, b] = map(lambda x: x.strip(), line.split('=', 2)) - versions[a] = b.strip('"') - - return versions - - -# dir 以下にある全てのファイルパスを、dir2 からの相対パスで返す -def enum_all_files(dir, dir2): - for root, _, files in os.walk(dir): - for file in files: - yield os.path.relpath(os.path.join(root, file), dir2) - - -def versioned(func): - def wrapper(version, version_file, *args, **kwargs): - if 'ignore_version' in kwargs: - if kwargs.get('ignore_version'): - rm_rf(version_file) - del kwargs['ignore_version'] - - if os.path.exists(version_file): - ver = open(version_file).read() - if ver.strip() == version.strip(): - return - - r = func(version=version, *args, **kwargs) - - with open(version_file, 'w') as f: - f.write(version) - - return r - - return wrapper - - -# アーカイブが単一のディレクトリに全て格納されているかどうかを調べる。 -# -# 単一のディレクトリに格納されている場合はそのディレクトリ名を返す。 -# そうでない場合は None を返す。 -def _is_single_dir(infos: List[Union[zipfile.ZipInfo, tarfile.TarInfo]], - get_name: Callable[[Union[zipfile.ZipInfo, tarfile.TarInfo]], str], - is_dir: Callable[[Union[zipfile.ZipInfo, tarfile.TarInfo]], bool]) -> Optional[str]: - # tarfile: ['path', 'path/to', 'path/to/file.txt'] - # zipfile: ['path/', 'path/to/', 'path/to/file.txt'] - # どちらも / 区切りだが、ディレクトリの場合、後ろに / が付くかどうかが違う - dirname = None - for info in infos: - name = get_name(info) - n = name.rstrip('/').find('/') - if n == -1: - # ルートディレクトリにファイルが存在している - if not is_dir(info): - return None - dir = name.rstrip('/') - else: - dir = name[0:n] - # ルートディレクトリに2個以上のディレクトリが存在している - if dirname is not None and dirname != dir: - return None - dirname = dir - - return dirname - - -def is_single_dir_tar(tar: tarfile.TarFile) -> Optional[str]: - return _is_single_dir(tar.getmembers(), lambda t: t.name, lambda t: t.isdir()) - - -def is_single_dir_zip(zip: zipfile.ZipFile) -> Optional[str]: - return _is_single_dir(zip.infolist(), lambda z: z.filename, lambda z: z.is_dir()) - - -# 解凍した上でファイル属性を付与する -def _extractzip(z: zipfile.ZipFile, path: str): - z.extractall(path) - if platform.system() == 'Windows': - return - for info in z.infolist(): - if info.is_dir(): - continue - filepath = os.path.join(path, info.filename) - mod = info.external_attr >> 16 - if (mod & 0o120000) == 0o120000: - # シンボリックリンク - with open(filepath, 'r') as f: - src = f.read() - os.remove(filepath) - with cd(os.path.dirname(filepath)): - if os.path.exists(src): - os.symlink(src, filepath) - if os.path.exists(filepath): - # 普通のファイル - os.chmod(filepath, mod & 0o777) - - -# zip または tar.gz ファイルを展開する。 -# -# 展開先のディレクトリは {output_dir}/{output_dirname} となり、 -# 展開先のディレクトリが既に存在していた場合は削除される。 -# -# もしアーカイブの内容が単一のディレクトリであった場合、 -# そのディレクトリは無いものとして展開される。 -# -# つまりアーカイブ libsora-1.23.tar.gz の内容が -# ['libsora-1.23', 'libsora-1.23/file1', 'libsora-1.23/file2'] -# であった場合、extract('libsora-1.23.tar.gz', 'out', 'libsora') のようにすると -# - out/libsora/file1 -# - out/libsora/file2 -# が出力される。 -# -# また、アーカイブ libsora-1.23.tar.gz の内容が -# ['libsora-1.23', 'libsora-1.23/file1', 'libsora-1.23/file2', 'LICENSE'] -# であった場合、extract('libsora-1.23.tar.gz', 'out', 'libsora') のようにすると -# - out/libsora/libsora-1.23/file1 -# - out/libsora/libsora-1.23/file2 -# - out/libsora/LICENSE -# が出力される。 -def extract(file: str, output_dir: str, output_dirname: str, filetype: Optional[str] = None): - path = os.path.join(output_dir, output_dirname) - logging.info(f"Extract {file} to {path}") - if filetype == 'gzip' or file.endswith('.tar.gz'): - rm_rf(path) - with tarfile.open(file) as t: - dir = is_single_dir_tar(t) - if dir is None: - os.makedirs(path, exist_ok=True) - t.extractall(path) - else: - logging.info(f"Directory {dir} is stripped") - path2 = os.path.join(output_dir, dir) - rm_rf(path2) - t.extractall(output_dir) - if path != path2: - logging.debug(f"mv {path2} {path}") - os.replace(path2, path) - elif filetype == 'zip' or file.endswith('.zip'): - rm_rf(path) - with zipfile.ZipFile(file) as z: - dir = is_single_dir_zip(z) - if dir is None: - os.makedirs(path, exist_ok=True) - # z.extractall(path) - _extractzip(z, path) - else: - logging.info(f"Directory {dir} is stripped") - path2 = os.path.join(output_dir, dir) - rm_rf(path2) - # z.extractall(output_dir) - _extractzip(z, output_dir) - if path != path2: - logging.debug(f"mv {path2} {path}") - os.replace(path2, path) - else: - raise Exception('file should end with .tar.gz or .zip') - - -def clone_and_checkout(url, version, dir, fetch, fetch_force): - if fetch_force: - rm_rf(dir) - - if not os.path.exists(os.path.join(dir, '.git')): - cmd(['git', 'clone', url, dir]) - fetch = True - - if fetch: - with cd(dir): - cmd(['git', 'fetch']) - cmd(['git', 'reset', '--hard']) - cmd(['git', 'clean', '-df']) - cmd(['git', 'checkout', '-f', version]) - - -def git_clone_shallow(url, hash, dir): - rm_rf(dir) - mkdir_p(dir) - with cd(dir): - cmd(['git', 'init']) - cmd(['git', 'remote', 'add', 'origin', url]) - cmd(['git', 'fetch', '--depth=1', 'origin', hash]) - cmd(['git', 'reset', '--hard', 'FETCH_HEAD']) - - -@versioned -def install_android_ndk(version, install_dir, source_dir): - archive = download( - f'https://dl.google.com/android/repository/android-ndk-{version}-linux.zip', - source_dir) - rm_rf(os.path.join(install_dir, 'android-ndk')) - extract(archive, output_dir=install_dir, output_dirname='android-ndk') - - -@versioned -def install_webrtc(version, source_dir, install_dir, platform: str): - win = platform.startswith("windows_") - filename = f'webrtc.{platform}.{"zip" if win else "tar.gz"}' - rm_rf(os.path.join(source_dir, filename)) - archive = download( - f'https://github.com/shiguredo-webrtc-build/webrtc-build/releases/download/{version}/{filename}', - output_dir=source_dir) - rm_rf(os.path.join(install_dir, 'webrtc')) - extract(archive, output_dir=install_dir, output_dirname='webrtc') - - -class WebrtcInfo(NamedTuple): - version_file: str - webrtc_include_dir: str - webrtc_library_dir: str - clang_dir: str - libcxx_dir: str - - -def get_webrtc_info(webrtcbuild: bool, source_dir: str, build_dir: str, install_dir: str) -> WebrtcInfo: - webrtc_source_dir = os.path.join(source_dir, 'webrtc') - webrtc_build_dir = os.path.join(build_dir, 'webrtc') - webrtc_install_dir = os.path.join(install_dir, 'webrtc') - - if webrtcbuild: - return WebrtcInfo( - version_file=os.path.join(source_dir, 'webrtc-build', 'VERSION'), - webrtc_include_dir=os.path.join(webrtc_source_dir, 'src'), - webrtc_library_dir=os.path.join(webrtc_build_dir, 'obj') - if platform.system() == 'Windows' else webrtc_build_dir, clang_dir=os.path.join( - webrtc_source_dir, 'src', 'third_party', 'llvm-build', 'Release+Asserts'), - libcxx_dir=os.path.join(webrtc_source_dir, 'src', 'buildtools', 'third_party', 'libc++', 'trunk'),) - else: - return WebrtcInfo( - version_file=os.path.join(webrtc_install_dir, 'VERSIONS'), - webrtc_include_dir=os.path.join(webrtc_install_dir, 'include'), - webrtc_library_dir=os.path.join(install_dir, 'webrtc', 'lib'), - clang_dir=os.path.join(install_dir, 'llvm', 'clang'), - libcxx_dir=os.path.join(install_dir, 'llvm', 'libcxx'), - ) - - -@versioned -def install_llvm(version, install_dir, - tools_url, tools_commit, - libcxx_url, libcxx_commit, - buildtools_url, buildtools_commit): - llvm_dir = os.path.join(install_dir, 'llvm') - rm_rf(llvm_dir) - mkdir_p(llvm_dir) - with cd(llvm_dir): - # tools の update.py を叩いて特定バージョンの clang バイナリを拾う - git_clone_shallow(tools_url, tools_commit, 'tools') - with cd('tools'): - cmd(['python3', - os.path.join('clang', 'scripts', 'update.py'), - '--output-dir', os.path.join(llvm_dir, 'clang')]) - - # 特定バージョンの libcxx を利用する - git_clone_shallow(libcxx_url, libcxx_commit, 'libcxx') - - # __config_site のために特定バージョンの buildtools を取得する - git_clone_shallow(buildtools_url, buildtools_commit, 'buildtools') - shutil.copyfile(os.path.join(llvm_dir, 'buildtools', 'third_party', 'libc++', '__config_site'), - os.path.join(llvm_dir, 'libcxx', 'include', '__config_site')) - - -@versioned -def install_boost(version, source_dir, install_dir, sora_version, platform: str): - win = platform.startswith("windows_") - filename = f'boost-{version}_sora-cpp-sdk-{sora_version}_{platform}.{"zip" if win else "tar.gz"}' - rm_rf(os.path.join(source_dir, filename)) - archive = download( - f'https://github.com/shiguredo/sora-cpp-sdk/releases/download/{sora_version}/{filename}', - output_dir=source_dir) - rm_rf(os.path.join(install_dir, 'boost')) - extract(archive, output_dir=install_dir, output_dirname='boost') - - -def cmake_path(path: str) -> str: - return path.replace('\\', '/') - - -@versioned -def install_cmake(version, source_dir, install_dir, platform: str, ext): - url = f'https://github.com/Kitware/CMake/releases/download/v{version}/cmake-{version}-{platform}.{ext}' - path = download(url, source_dir) - extract(path, install_dir, 'cmake') - # Android で自前の CMake を利用する場合、ninja へのパスが見つけられない問題があるので、同じディレクトリに symlink を貼る - # https://issuetracker.google.com/issues/206099937 - if platform.startswith('linux'): - with cd(os.path.join(install_dir, 'cmake', 'bin')): - cmd(['ln', '-s', '/usr/bin/ninja', 'ninja']) - - -@versioned -def install_sora(version, source_dir, install_dir, platform: str): - win = platform.startswith("windows_") - filename = f'sora-cpp-sdk-{version}_{platform}.{"zip" if win else "tar.gz"}' - rm_rf(os.path.join(source_dir, filename)) - archive = download( - f'https://github.com/shiguredo/sora-cpp-sdk/releases/download/{version}/{filename}', - output_dir=source_dir) - rm_rf(os.path.join(install_dir, 'sora')) - extract(archive, output_dir=install_dir, output_dirname='sora') - - -@versioned -def install_protobuf(version, source_dir, install_dir, platform: str): - # platform: - # - linux-aarch_64 - # - linux-ppcle_64 - # - linux-s390_64 - # - linux-x86_32 - # - linux-x86_64 - # - osx-aarch_64 - # - osx-universal_binary - # - osx-x86_64 - # - win32 - # - win64 - url = f'https://github.com/protocolbuffers/protobuf/releases/download/v{version}/protoc-{version}-{platform}.zip' - path = download(url, source_dir) - rm_rf(os.path.join(install_dir, 'protobuf')) - extract(path, install_dir, 'protobuf') - # なぜか実行属性が消えてるので入れてやる - for file in os.scandir(os.path.join(install_dir, 'protobuf', 'bin')): - if file.is_file: - os.chmod(file.path, file.stat().st_mode | stat.S_IXUSR) - - -@versioned -def install_protoc_gen_jsonif(version, source_dir, install_dir, platform: str): - # platform: - # - darwin-amd64 - # - darwin-arm64 - # - linux-amd64 - # - windows-amd64 - url = f'https://github.com/melpon/protoc-gen-jsonif/releases/download/{version}/protoc-gen-jsonif.tar.gz' - rm_rf(os.path.join(source_dir, 'protoc-gen-jsonif.tar.gz')) - path = download(url, source_dir) - jsonif_install_dir = os.path.join(install_dir, 'protoc-gen-jsonif') - rm_rf(jsonif_install_dir) - extract(path, install_dir, 'protoc-gen-jsonif') - # 自分の環境のバイナリを /bin に配置する - shutil.copytree( - os.path.join(jsonif_install_dir, *platform.split('-')), - os.path.join(jsonif_install_dir, 'bin')) - # なぜか実行属性が消えてるので入れてやる - for file in os.scandir(os.path.join(jsonif_install_dir, 'bin')): - if file.is_file: - os.chmod(file.path, file.stat().st_mode | stat.S_IXUSR) - - -BASE_DIR = os.path.abspath(os.path.dirname(__file__)) - - -def install_deps(platform: str, build_platform: str, source_dir, build_dir, install_dir, debug): - with cd(BASE_DIR): - version = read_version_file('VERSION') - - # Android NDK - if platform == 'android': - install_android_ndk_args = { - 'version': version['ANDROID_NDK_VERSION'], - 'version_file': os.path.join(install_dir, 'android-ndk.version'), - 'source_dir': source_dir, - 'install_dir': install_dir, - } - install_android_ndk(**install_android_ndk_args) - - # WebRTC - install_webrtc_args = { - 'version': version['WEBRTC_BUILD_VERSION'], - 'version_file': os.path.join(install_dir, 'webrtc.version'), - 'source_dir': source_dir, - 'install_dir': install_dir, - 'platform': platform, - } - - install_webrtc(**install_webrtc_args) - - webrtc_info = get_webrtc_info(False, source_dir, build_dir, install_dir) - webrtc_version = read_version_file(webrtc_info.version_file) - - # Windows は MSVC を使うので不要 - # macOS と iOS は Apple Clang を使うので不要 - # Android は libc++ のために必要 - if platform not in ('windows_x86_64', 'macos_x86_64', 'macos_arm64', 'ios'): - # LLVM - tools_url = webrtc_version['WEBRTC_SRC_TOOLS_URL'] - tools_commit = webrtc_version['WEBRTC_SRC_TOOLS_COMMIT'] - libcxx_url = webrtc_version['WEBRTC_SRC_BUILDTOOLS_THIRD_PARTY_LIBCXX_TRUNK_URL'] - libcxx_commit = webrtc_version['WEBRTC_SRC_BUILDTOOLS_THIRD_PARTY_LIBCXX_TRUNK_COMMIT'] - buildtools_url = webrtc_version['WEBRTC_SRC_BUILDTOOLS_URL'] - buildtools_commit = webrtc_version['WEBRTC_SRC_BUILDTOOLS_COMMIT'] - install_llvm_args = { - 'version': - f'{tools_url}.{tools_commit}.' - f'{libcxx_url}.{libcxx_commit}.' - f'{buildtools_url}.{buildtools_commit}', - 'version_file': os.path.join(install_dir, 'llvm.version'), - 'install_dir': install_dir, - 'tools_url': tools_url, - 'tools_commit': tools_commit, - 'libcxx_url': libcxx_url, - 'libcxx_commit': libcxx_commit, - 'buildtools_url': buildtools_url, - 'buildtools_commit': buildtools_commit, - } - install_llvm(**install_llvm_args) - - # Boost - install_boost_args = { - 'version': version['BOOST_VERSION'], - 'version_file': os.path.join(install_dir, 'boost.version'), - 'source_dir': source_dir, - 'install_dir': install_dir, - 'sora_version': version['SORA_CPP_SDK_VERSION'], - 'platform': platform, - } - install_boost(**install_boost_args) - - # CMake - install_cmake_args = { - 'version': version['CMAKE_VERSION'], - 'version_file': os.path.join(install_dir, 'cmake.version'), - 'source_dir': source_dir, - 'install_dir': install_dir, - 'platform': '', - 'ext': 'tar.gz' - } - if build_platform == 'windows_x86_64': - install_cmake_args['platform'] = 'windows-x86_64' - install_cmake_args['ext'] = 'zip' - elif build_platform in ('macos_x86_64', 'macos_arm64'): - install_cmake_args['platform'] = 'macos-universal' - elif build_platform == 'ubuntu-20.04_x86_64': - install_cmake_args['platform'] = 'linux-x86_64' - else: - raise Exception('Failed to install CMake') - install_cmake(**install_cmake_args) - - if build_platform in ('macos_x86_64', 'macos_arm64'): - add_path(os.path.join(install_dir, 'cmake', 'CMake.app', 'Contents', 'bin')) - else: - add_path(os.path.join(install_dir, 'cmake', 'bin')) - - # Sora C++ SDK - install_sora_args = { - 'version': version['SORA_CPP_SDK_VERSION'], - 'version_file': os.path.join(install_dir, 'sora.version'), - 'source_dir': source_dir, - 'install_dir': install_dir, - 'platform': platform, - } - install_sora(**install_sora_args) - - # protobuf - install_protobuf_args = { - 'version': version['PROTOBUF_VERSION'], - 'version_file': os.path.join(install_dir, 'protobuf.version'), - 'source_dir': source_dir, - 'install_dir': install_dir, - 'platform': '', - } - if build_platform == 'windows_x86_64': - install_protobuf_args['platform'] = 'win64' - elif build_platform in ('macos_x86_64', 'macos_arm64'): - install_protobuf_args['platform'] = 'osx-universal_binary' - elif build_platform == 'ubuntu-20.04_x86_64': - install_protobuf_args['platform'] = 'linux-x86_64' - else: - raise Exception('Failed to install Protobuf') - install_protobuf(**install_protobuf_args) - - # protoc-gen-jsonif - install_jsonif_args = { - 'version': version['PROTOC_GEN_JSONIF_VERSION'], - 'version_file': os.path.join(install_dir, 'protoc-gen-jsonif.version'), - 'source_dir': source_dir, - 'install_dir': install_dir, - 'platform': '', - } - if build_platform == 'windows_x86_64': - install_jsonif_args['platform'] = 'windows-amd64' - elif build_platform == 'macos_x86_64': - install_jsonif_args['platform'] = 'darwin-amd64' - elif build_platform == 'macos_arm64': - install_jsonif_args['platform'] = 'darwin-arm64' - elif build_platform == 'ubuntu-20.04_x86_64': - install_jsonif_args['platform'] = 'linux-amd64' - else: - raise Exception('Failed to install protoc-gen-jsonif') - install_protoc_gen_jsonif(**install_jsonif_args) - - -def get_build_platform(): - arch = platform.machine() - if arch in ('AMD64', 'x86_64'): - arch = 'x86_64' - elif arch in ('aarch64', 'arm64'): - arch = 'arm64' - else: - raise Exception(f'Arch {arch} not supported') - - os = platform.system() - if os == 'Windows': - if arch == 'x86_64': - return 'windows_x86_64' - else: - raise Exception('Unknown windows arch') - elif os == 'Darwin': - return f'macos_{arch}' - elif os == 'Linux': - release = read_version_file('/etc/os-release') - os = release['NAME'] - if os == 'Ubuntu': - osver = release['VERSION_ID'] - if osver == '20.04': - if arch == 'x86_64': - return 'ubuntu-20.04_x86_64' - else: - raise Exception('Unknown ubuntu arch') - else: - raise Exception('Unexpected Ubuntu version') - else: - raise Exception(f'OS {os} not supported') - else: - raise Exception(f'OS {os} not supported') - - -AVAILABLE_TARGETS = ['windows_x86_64', 'macos_arm64', 'ubuntu-20.04_x86_64', 'ios', 'android'] - -BUILD_PLATFORM = { - 'windows_x86_64': ['windows_x86_64'], - 'macos_x86_64': ['macos_x86_64', 'macos_arm64', 'ios'], - 'macos_arm64': ['macos_x86_64', 'macos_arm64', 'ios'], - 'ubuntu-20.04_x86_64': ['ubuntu-20.04_x86_64', 'android'], -} - -def main(): - parser = argparse.ArgumentParser() - parser.add_argument("--debug", action='store_true') - parser.add_argument("--relwithdebinfo", action='store_true') - parser.add_argument("target", choices=AVAILABLE_TARGETS) - - args = parser.parse_args() - - platform = args.target - build_platform = get_build_platform() - if build_platform not in BUILD_PLATFORM: - raise Exception(f'Build platform {build_platform} is not supported.') - if platform not in BUILD_PLATFORM[build_platform]: - raise Exception(f'Target {platform} is not supported on this build platform {build_platform}.') - - configuration_dir = 'debug' if args.debug else 'release' - source_dir = os.path.join(BASE_DIR, '_source', platform, configuration_dir) - build_dir = os.path.join(BASE_DIR, '_build', platform, configuration_dir) - install_dir = os.path.join(BASE_DIR, '_install', platform, configuration_dir) - mkdir_p(source_dir) - mkdir_p(build_dir) - mkdir_p(install_dir) - - install_deps(platform, build_platform, source_dir, build_dir, install_dir, args.debug) - - if args.debug: - configuration = 'Debug' - elif args.relwithdebinfo: - configuration = 'RelWithDebInfo' - else: - configuration = 'Release' - - unity_build_dir = os.path.join(build_dir, 'sora_unity_sdk') - mkdir_p(unity_build_dir) - with cd(unity_build_dir): - webrtc_info = get_webrtc_info(False, source_dir, build_dir, install_dir) - webrtc_version = read_version_file(webrtc_info.version_file) - webrtc_commit = webrtc_version['WEBRTC_COMMIT'] - with cd(BASE_DIR): - version = read_version_file('VERSION') - sora_unity_sdk_version = version['SORA_UNITY_SDK_VERSION'] - sora_unity_sdk_commit = cmdcap(['git', 'rev-parse', 'HEAD']) - android_native_api_level = version['ANDROID_NATIVE_API_LEVEL'] - - cmake_args = [] - cmake_args.append(f'-DCMAKE_BUILD_TYPE={configuration}') - cmake_args.append(f'-DSORA_UNITY_SDK_PACKAGE={platform}') - cmake_args.append(f'-DSORA_UNITY_SDK_VERSION={sora_unity_sdk_version}') - cmake_args.append(f'-DSORA_UNITY_SDK_COMMIT={sora_unity_sdk_commit}') - cmake_args.append(f"-DBOOST_ROOT={cmake_path(os.path.join(install_dir, 'boost'))}") - cmake_args.append('-DWEBRTC_LIBRARY_NAME=webrtc') - cmake_args.append(f"-DWEBRTC_INCLUDE_DIR={cmake_path(webrtc_info.webrtc_include_dir)}") - cmake_args.append(f"-DWEBRTC_LIBRARY_DIR={cmake_path(webrtc_info.webrtc_library_dir)}") - cmake_args.append(f"-DWEBRTC_COMMIT={webrtc_commit}") - cmake_args.append(f"-DSORA_DIR={cmake_path(os.path.join(install_dir, 'sora'))}") - 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'))}") - if platform == 'windows_x86_64': - pass - elif platform in ('macos_x86_64', 'macos_arm64'): - sysroot = cmdcap(['xcrun', '--sdk', 'macosx', '--show-sdk-path']) - arch = 'x86_64' if platform == 'macos_x86_64' else 'arm64' - target = 'x86_64-apple-darwin' if platform == 'macos_x86_64' else 'arm64-apple-darwin' - cmake_args.append(f'-DCMAKE_SYSTEM_PROCESSOR={arch}') - cmake_args.append(f'-DCMAKE_OSX_ARCHITECTURES={arch}') - cmake_args.append(f'-DCMAKE_C_COMPILER_TARGET={target}') - cmake_args.append(f'-DCMAKE_CXX_COMPILER_TARGET={target}') - cmake_args.append(f'-DCMAKE_OBJCXX_COMPILER_TARGET={target}') - cmake_args.append(f'-DCMAKE_SYSROOT={sysroot}') - elif platform == 'ios': - cmake_args += ['-G', 'Xcode'] - cmake_args.append('-DCMAKE_SYSTEM_NAME=iOS') - cmake_args.append('-DCMAKE_OSX_ARCHITECTURES="x86_64;arm64"') - cmake_args.append('-DCMAKE_OSX_DEPLOYMENT_TARGET=10.0') - cmake_args.append('-DCMAKE_XCODE_ATTRIBUTE_ONLY_ACTIVE_ARCH=NO') - elif platform == 'android': - toolchain_file = os.path.join(install_dir, 'android-ndk', 'build', 'cmake', 'android.toolchain.cmake') - cmake_args.append(f"-DCMAKE_TOOLCHAIN_FILE={toolchain_file}") - cmake_args.append(f"-DANDROID_PLATFORM=android-{android_native_api_level}") - cmake_args.append(f"-DANDROID_NATIVE_API_LEVEL={android_native_api_level}") - cmake_args.append('-DANDROID_ABI=arm64-v8a') - cmake_args.append('-DANDROID_STL=none') - cmake_args.append( - f"-DLIBCXX_INCLUDE_DIR={cmake_path(os.path.join(webrtc_info.libcxx_dir, 'include'))}") - cmake_args.append('-DANDROID_CPP_FEATURES=exceptions rtti') - # r23b には ANDROID_CPP_FEATURES=exceptions でも例外が設定されない問題がある - # https://github.com/android/ndk/issues/1618 - cmake_args.append('-DCMAKE_ANDROID_EXCEPTIONS=ON') - elif platform == 'ubuntu-20.04_x86_64': - cmake_args.append(f"-DCMAKE_C_COMPILER={os.path.join(webrtc_info.clang_dir, 'bin', 'clang')}") - cmake_args.append(f"-DCMAKE_CXX_COMPILER={os.path.join(webrtc_info.clang_dir, 'bin', 'clang++')}") - cmake_args.append( - f"-DLIBCXX_INCLUDE_DIR={cmake_path(os.path.join(webrtc_info.libcxx_dir, 'include'))}") - else: - raise Exception(f'Platform {platform} not supported.') - - cmd(['cmake', BASE_DIR, *cmake_args]) - - if platform == 'ios': - cmd(['cmake', '--build', '.', f'-j{multiprocessing.cpu_count()}', - '--config', configuration, - '--target', 'SoraUnitySdk', - '--', - '-arch', 'x86_64', - '-sdk', 'iphonesimulator']) - cmd(['cmake', '--build', '.', f'-j{multiprocessing.cpu_count()}', - '--config', configuration, - '--target', 'SoraUnitySdk', - '--', - '-arch', 'arm64', - '-sdk', 'iphoneos']) - cmd(['lipo', '-create', - '-output', os.path.join(unity_build_dir, 'libSoraUnitySdk.a'), - os.path.join(unity_build_dir, 'Release-iphonesimulator', 'libSoraUnitySdk.a'), - os.path.join(unity_build_dir, 'Release-iphoneos', 'libSoraUnitySdk.a')]) - else: - cmd(['cmake', '--build', '.', f'-j{multiprocessing.cpu_count()}', '--config', configuration]) - - -if __name__ == '__main__': - main() +import subprocess +import logging +import os +import stat +import urllib.parse +import zipfile +import tarfile +import shutil +import platform +import multiprocessing +import argparse +from typing import Callable, NamedTuple, Optional, List, Union, Dict + + +logging.basicConfig(level=logging.DEBUG) + + +class ChangeDirectory(object): + def __init__(self, cwd): + self._cwd = cwd + + def __enter__(self): + self._old_cwd = os.getcwd() + logging.debug(f'pushd {self._old_cwd} --> {self._cwd}') + os.chdir(self._cwd) + + def __exit__(self, exctype, excvalue, trace): + logging.debug(f'popd {self._old_cwd} <-- {self._cwd}') + os.chdir(self._old_cwd) + return False + + +def cd(cwd): + return ChangeDirectory(cwd) + + +def cmd(args, **kwargs): + logging.debug(f'+{args} {kwargs}') + if 'check' not in kwargs: + kwargs['check'] = True + if 'resolve' in kwargs: + resolve = kwargs['resolve'] + del kwargs['resolve'] + else: + resolve = True + if resolve: + args = [shutil.which(args[0]), *args[1:]] + return subprocess.run(args, **kwargs) + + +# 標準出力をキャプチャするコマンド実行。シェルの `cmd ...` や $(cmd ...) と同じ +def cmdcap(args, **kwargs): + # 3.7 でしか使えない + # kwargs['capture_output'] = True + kwargs['stdout'] = subprocess.PIPE + kwargs['stderr'] = subprocess.PIPE + kwargs['encoding'] = 'utf-8' + return cmd(args, **kwargs).stdout.strip() + + +def rm_rf(path: str): + if not os.path.exists(path): + logging.debug(f'rm -rf {path} => path not found') + return + if os.path.isfile(path) or os.path.islink(path): + os.remove(path) + logging.debug(f'rm -rf {path} => file removed') + if os.path.isdir(path): + shutil.rmtree(path) + logging.debug(f'rm -rf {path} => directory removed') + + +def mkdir_p(path: str): + if os.path.exists(path): + logging.debug(f'mkdir -p {path} => already exists') + return + os.makedirs(path, exist_ok=True) + logging.debug(f'mkdir -p {path} => directory created') + + +if platform.system() == 'Windows': + PATH_SEPARATOR = ';' +else: + PATH_SEPARATOR = ':' + + +def add_path(path: str, is_after=False): + logging.debug(f'add_path: {path}') + if 'PATH' not in os.environ: + os.environ['PATH'] = path + return + + if is_after: + os.environ['PATH'] = os.environ['PATH'] + PATH_SEPARATOR + path + else: + os.environ['PATH'] = path + PATH_SEPARATOR + os.environ['PATH'] + + +def download(url: str, output_dir: Optional[str] = None, filename: Optional[str] = None) -> str: + if filename is None: + output_path = urllib.parse.urlparse(url).path.split('/')[-1] + else: + output_path = filename + + if output_dir is not None: + output_path = os.path.join(output_dir, output_path) + + if os.path.exists(output_path): + return output_path + + try: + if shutil.which('curl') is not None: + cmd(["curl", "-fLo", output_path, url]) + else: + cmd(["wget", "-cO", output_path, url]) + except Exception: + # ゴミを残さないようにする + if os.path.exists(output_path): + os.remove(output_path) + raise + + return output_path + + +def read_version_file(path: str) -> Dict[str, str]: + versions = {} + + lines = open(path).readlines() + for line in lines: + line = line.strip() + + # コメント行 + if line[:1] == '#': + continue + + # 空行 + if len(line) == 0: + continue + + [a, b] = map(lambda x: x.strip(), line.split('=', 2)) + versions[a] = b.strip('"') + + return versions + + +# dir 以下にある全てのファイルパスを、dir2 からの相対パスで返す +def enum_all_files(dir, dir2): + for root, _, files in os.walk(dir): + for file in files: + yield os.path.relpath(os.path.join(root, file), dir2) + + +def versioned(func): + def wrapper(version, version_file, *args, **kwargs): + if 'ignore_version' in kwargs: + if kwargs.get('ignore_version'): + rm_rf(version_file) + del kwargs['ignore_version'] + + if os.path.exists(version_file): + ver = open(version_file).read() + if ver.strip() == version.strip(): + return + + r = func(version=version, *args, **kwargs) + + with open(version_file, 'w') as f: + f.write(version) + + return r + + return wrapper + + +# アーカイブが単一のディレクトリに全て格納されているかどうかを調べる。 +# +# 単一のディレクトリに格納されている場合はそのディレクトリ名を返す。 +# そうでない場合は None を返す。 +def _is_single_dir(infos: List[Union[zipfile.ZipInfo, tarfile.TarInfo]], + get_name: Callable[[Union[zipfile.ZipInfo, tarfile.TarInfo]], str], + is_dir: Callable[[Union[zipfile.ZipInfo, tarfile.TarInfo]], bool]) -> Optional[str]: + # tarfile: ['path', 'path/to', 'path/to/file.txt'] + # zipfile: ['path/', 'path/to/', 'path/to/file.txt'] + # どちらも / 区切りだが、ディレクトリの場合、後ろに / が付くかどうかが違う + dirname = None + for info in infos: + name = get_name(info) + n = name.rstrip('/').find('/') + if n == -1: + # ルートディレクトリにファイルが存在している + if not is_dir(info): + return None + dir = name.rstrip('/') + else: + dir = name[0:n] + # ルートディレクトリに2個以上のディレクトリが存在している + if dirname is not None and dirname != dir: + return None + dirname = dir + + return dirname + + +def is_single_dir_tar(tar: tarfile.TarFile) -> Optional[str]: + return _is_single_dir(tar.getmembers(), lambda t: t.name, lambda t: t.isdir()) + + +def is_single_dir_zip(zip: zipfile.ZipFile) -> Optional[str]: + return _is_single_dir(zip.infolist(), lambda z: z.filename, lambda z: z.is_dir()) + + +# 解凍した上でファイル属性を付与する +def _extractzip(z: zipfile.ZipFile, path: str): + z.extractall(path) + if platform.system() == 'Windows': + return + for info in z.infolist(): + if info.is_dir(): + continue + filepath = os.path.join(path, info.filename) + mod = info.external_attr >> 16 + if (mod & 0o120000) == 0o120000: + # シンボリックリンク + with open(filepath, 'r') as f: + src = f.read() + os.remove(filepath) + with cd(os.path.dirname(filepath)): + if os.path.exists(src): + os.symlink(src, filepath) + if os.path.exists(filepath): + # 普通のファイル + os.chmod(filepath, mod & 0o777) + + +# zip または tar.gz ファイルを展開する。 +# +# 展開先のディレクトリは {output_dir}/{output_dirname} となり、 +# 展開先のディレクトリが既に存在していた場合は削除される。 +# +# もしアーカイブの内容が単一のディレクトリであった場合、 +# そのディレクトリは無いものとして展開される。 +# +# つまりアーカイブ libsora-1.23.tar.gz の内容が +# ['libsora-1.23', 'libsora-1.23/file1', 'libsora-1.23/file2'] +# であった場合、extract('libsora-1.23.tar.gz', 'out', 'libsora') のようにすると +# - out/libsora/file1 +# - out/libsora/file2 +# が出力される。 +# +# また、アーカイブ libsora-1.23.tar.gz の内容が +# ['libsora-1.23', 'libsora-1.23/file1', 'libsora-1.23/file2', 'LICENSE'] +# であった場合、extract('libsora-1.23.tar.gz', 'out', 'libsora') のようにすると +# - out/libsora/libsora-1.23/file1 +# - out/libsora/libsora-1.23/file2 +# - out/libsora/LICENSE +# が出力される。 +def extract(file: str, output_dir: str, output_dirname: str, filetype: Optional[str] = None): + path = os.path.join(output_dir, output_dirname) + logging.info(f"Extract {file} to {path}") + if filetype == 'gzip' or file.endswith('.tar.gz'): + rm_rf(path) + with tarfile.open(file) as t: + dir = is_single_dir_tar(t) + if dir is None: + os.makedirs(path, exist_ok=True) + t.extractall(path) + else: + logging.info(f"Directory {dir} is stripped") + path2 = os.path.join(output_dir, dir) + rm_rf(path2) + t.extractall(output_dir) + if path != path2: + logging.debug(f"mv {path2} {path}") + os.replace(path2, path) + elif filetype == 'zip' or file.endswith('.zip'): + rm_rf(path) + with zipfile.ZipFile(file) as z: + dir = is_single_dir_zip(z) + if dir is None: + os.makedirs(path, exist_ok=True) + # z.extractall(path) + _extractzip(z, path) + else: + logging.info(f"Directory {dir} is stripped") + path2 = os.path.join(output_dir, dir) + rm_rf(path2) + # z.extractall(output_dir) + _extractzip(z, output_dir) + if path != path2: + logging.debug(f"mv {path2} {path}") + os.replace(path2, path) + else: + raise Exception('file should end with .tar.gz or .zip') + + +def clone_and_checkout(url, version, dir, fetch, fetch_force): + if fetch_force: + rm_rf(dir) + + if not os.path.exists(os.path.join(dir, '.git')): + cmd(['git', 'clone', url, dir]) + fetch = True + + if fetch: + with cd(dir): + cmd(['git', 'fetch']) + cmd(['git', 'reset', '--hard']) + cmd(['git', 'clean', '-df']) + cmd(['git', 'checkout', '-f', version]) + + +def git_clone_shallow(url, hash, dir): + rm_rf(dir) + mkdir_p(dir) + with cd(dir): + cmd(['git', 'init']) + cmd(['git', 'remote', 'add', 'origin', url]) + cmd(['git', 'fetch', '--depth=1', 'origin', hash]) + cmd(['git', 'reset', '--hard', 'FETCH_HEAD']) + + +@versioned +def install_android_ndk(version, install_dir, source_dir): + archive = download( + f'https://dl.google.com/android/repository/android-ndk-{version}-linux.zip', + source_dir) + rm_rf(os.path.join(install_dir, 'android-ndk')) + extract(archive, output_dir=install_dir, output_dirname='android-ndk') + + +@versioned +def install_webrtc(version, source_dir, install_dir, platform: str): + win = platform.startswith("windows_") + filename = f'webrtc.{platform}.{"zip" if win else "tar.gz"}' + rm_rf(os.path.join(source_dir, filename)) + archive = download( + f'https://github.com/shiguredo-webrtc-build/webrtc-build/releases/download/{version}/{filename}', + output_dir=source_dir) + rm_rf(os.path.join(install_dir, 'webrtc')) + extract(archive, output_dir=install_dir, output_dirname='webrtc') + + +class WebrtcInfo(NamedTuple): + version_file: str + webrtc_include_dir: str + webrtc_library_dir: str + clang_dir: str + libcxx_dir: str + + +def get_webrtc_info(webrtcbuild: bool, source_dir: str, build_dir: str, install_dir: str) -> WebrtcInfo: + webrtc_source_dir = os.path.join(source_dir, 'webrtc') + webrtc_build_dir = os.path.join(build_dir, 'webrtc') + webrtc_install_dir = os.path.join(install_dir, 'webrtc') + + if webrtcbuild: + return WebrtcInfo( + version_file=os.path.join(source_dir, 'webrtc-build', 'VERSION'), + webrtc_include_dir=os.path.join(webrtc_source_dir, 'src'), + webrtc_library_dir=os.path.join(webrtc_build_dir, 'obj') + if platform.system() == 'Windows' else webrtc_build_dir, clang_dir=os.path.join( + webrtc_source_dir, 'src', 'third_party', 'llvm-build', 'Release+Asserts'), + libcxx_dir=os.path.join(webrtc_source_dir, 'src', 'buildtools', 'third_party', 'libc++', 'trunk'),) + else: + return WebrtcInfo( + version_file=os.path.join(webrtc_install_dir, 'VERSIONS'), + webrtc_include_dir=os.path.join(webrtc_install_dir, 'include'), + webrtc_library_dir=os.path.join(install_dir, 'webrtc', 'lib'), + clang_dir=os.path.join(install_dir, 'llvm', 'clang'), + libcxx_dir=os.path.join(install_dir, 'llvm', 'libcxx'), + ) + + +@versioned +def install_llvm(version, install_dir, + tools_url, tools_commit, + libcxx_url, libcxx_commit, + buildtools_url, buildtools_commit): + llvm_dir = os.path.join(install_dir, 'llvm') + rm_rf(llvm_dir) + mkdir_p(llvm_dir) + with cd(llvm_dir): + # tools の update.py を叩いて特定バージョンの clang バイナリを拾う + git_clone_shallow(tools_url, tools_commit, 'tools') + with cd('tools'): + cmd(['python3', + os.path.join('clang', 'scripts', 'update.py'), + '--output-dir', os.path.join(llvm_dir, 'clang')]) + + # 特定バージョンの libcxx を利用する + git_clone_shallow(libcxx_url, libcxx_commit, 'libcxx') + + # __config_site のために特定バージョンの buildtools を取得する + git_clone_shallow(buildtools_url, buildtools_commit, 'buildtools') + shutil.copyfile(os.path.join(llvm_dir, 'buildtools', 'third_party', 'libc++', '__config_site'), + os.path.join(llvm_dir, 'libcxx', 'include', '__config_site')) + + +@versioned +def install_boost(version, source_dir, install_dir, sora_version, platform: str): + win = platform.startswith("windows_") + filename = f'boost-{version}_sora-cpp-sdk-{sora_version}_{platform}.{"zip" if win else "tar.gz"}' + rm_rf(os.path.join(source_dir, filename)) + archive = download( + f'https://github.com/shiguredo/sora-cpp-sdk/releases/download/{sora_version}/{filename}', + output_dir=source_dir) + rm_rf(os.path.join(install_dir, 'boost')) + extract(archive, output_dir=install_dir, output_dirname='boost') + + +def cmake_path(path: str) -> str: + return path.replace('\\', '/') + + +@versioned +def install_cmake(version, source_dir, install_dir, platform: str, ext): + url = f'https://github.com/Kitware/CMake/releases/download/v{version}/cmake-{version}-{platform}.{ext}' + path = download(url, source_dir) + extract(path, install_dir, 'cmake') + # Android で自前の CMake を利用する場合、ninja へのパスが見つけられない問題があるので、同じディレクトリに symlink を貼る + # https://issuetracker.google.com/issues/206099937 + if platform.startswith('linux'): + with cd(os.path.join(install_dir, 'cmake', 'bin')): + cmd(['ln', '-s', '/usr/bin/ninja', 'ninja']) + + +@versioned +def install_sora(version, source_dir, install_dir, platform: str): + win = platform.startswith("windows_") + filename = f'sora-cpp-sdk-{version}_{platform}.{"zip" if win else "tar.gz"}' + rm_rf(os.path.join(source_dir, filename)) + archive = download( + f'https://github.com/shiguredo/sora-cpp-sdk/releases/download/{version}/{filename}', + output_dir=source_dir) + rm_rf(os.path.join(install_dir, 'sora')) + extract(archive, output_dir=install_dir, output_dirname='sora') + + +@versioned +def install_protobuf(version, source_dir, install_dir, platform: str): + # platform: + # - linux-aarch_64 + # - linux-ppcle_64 + # - linux-s390_64 + # - linux-x86_32 + # - linux-x86_64 + # - osx-aarch_64 + # - osx-universal_binary + # - osx-x86_64 + # - win32 + # - win64 + url = f'https://github.com/protocolbuffers/protobuf/releases/download/v{version}/protoc-{version}-{platform}.zip' + path = download(url, source_dir) + rm_rf(os.path.join(install_dir, 'protobuf')) + extract(path, install_dir, 'protobuf') + # なぜか実行属性が消えてるので入れてやる + for file in os.scandir(os.path.join(install_dir, 'protobuf', 'bin')): + if file.is_file: + os.chmod(file.path, file.stat().st_mode | stat.S_IXUSR) + + +@versioned +def install_protoc_gen_jsonif(version, source_dir, install_dir, platform: str): + # platform: + # - darwin-amd64 + # - darwin-arm64 + # - linux-amd64 + # - windows-amd64 + url = f'https://github.com/melpon/protoc-gen-jsonif/releases/download/{version}/protoc-gen-jsonif.tar.gz' + rm_rf(os.path.join(source_dir, 'protoc-gen-jsonif.tar.gz')) + path = download(url, source_dir) + jsonif_install_dir = os.path.join(install_dir, 'protoc-gen-jsonif') + rm_rf(jsonif_install_dir) + extract(path, install_dir, 'protoc-gen-jsonif') + # 自分の環境のバイナリを /bin に配置する + shutil.copytree( + os.path.join(jsonif_install_dir, *platform.split('-')), + os.path.join(jsonif_install_dir, 'bin')) + # なぜか実行属性が消えてるので入れてやる + for file in os.scandir(os.path.join(jsonif_install_dir, 'bin')): + if file.is_file: + os.chmod(file.path, file.stat().st_mode | stat.S_IXUSR) + + +BASE_DIR = os.path.abspath(os.path.dirname(__file__)) + + +def install_deps(platform: str, build_platform: str, source_dir, build_dir, install_dir, debug): + with cd(BASE_DIR): + version = read_version_file('VERSION') + + # Android NDK + if platform == 'android': + install_android_ndk_args = { + 'version': version['ANDROID_NDK_VERSION'], + 'version_file': os.path.join(install_dir, 'android-ndk.version'), + 'source_dir': source_dir, + 'install_dir': install_dir, + } + install_android_ndk(**install_android_ndk_args) + + # WebRTC + install_webrtc_args = { + 'version': version['WEBRTC_BUILD_VERSION'], + 'version_file': os.path.join(install_dir, 'webrtc.version'), + 'source_dir': source_dir, + 'install_dir': install_dir, + 'platform': platform, + } + + install_webrtc(**install_webrtc_args) + + webrtc_info = get_webrtc_info(False, source_dir, build_dir, install_dir) + webrtc_version = read_version_file(webrtc_info.version_file) + + # Windows は MSVC を使うので不要 + # macOS と iOS は Apple Clang を使うので不要 + # Android は libc++ のために必要 + if platform not in ('windows_x86_64', 'macos_x86_64', 'macos_arm64', 'ios'): + # LLVM + tools_url = webrtc_version['WEBRTC_SRC_TOOLS_URL'] + tools_commit = webrtc_version['WEBRTC_SRC_TOOLS_COMMIT'] + libcxx_url = webrtc_version['WEBRTC_SRC_BUILDTOOLS_THIRD_PARTY_LIBCXX_TRUNK_URL'] + libcxx_commit = webrtc_version['WEBRTC_SRC_BUILDTOOLS_THIRD_PARTY_LIBCXX_TRUNK_COMMIT'] + buildtools_url = webrtc_version['WEBRTC_SRC_BUILDTOOLS_URL'] + buildtools_commit = webrtc_version['WEBRTC_SRC_BUILDTOOLS_COMMIT'] + install_llvm_args = { + 'version': + f'{tools_url}.{tools_commit}.' + f'{libcxx_url}.{libcxx_commit}.' + f'{buildtools_url}.{buildtools_commit}', + 'version_file': os.path.join(install_dir, 'llvm.version'), + 'install_dir': install_dir, + 'tools_url': tools_url, + 'tools_commit': tools_commit, + 'libcxx_url': libcxx_url, + 'libcxx_commit': libcxx_commit, + 'buildtools_url': buildtools_url, + 'buildtools_commit': buildtools_commit, + } + install_llvm(**install_llvm_args) + + # Boost + install_boost_args = { + 'version': version['BOOST_VERSION'], + 'version_file': os.path.join(install_dir, 'boost.version'), + 'source_dir': source_dir, + 'install_dir': install_dir, + 'sora_version': version['SORA_CPP_SDK_VERSION'], + 'platform': platform, + } + install_boost(**install_boost_args) + + # CMake + install_cmake_args = { + 'version': version['CMAKE_VERSION'], + 'version_file': os.path.join(install_dir, 'cmake.version'), + 'source_dir': source_dir, + 'install_dir': install_dir, + 'platform': '', + 'ext': 'tar.gz' + } + if build_platform == 'windows_x86_64': + install_cmake_args['platform'] = 'windows-x86_64' + install_cmake_args['ext'] = 'zip' + elif build_platform in ('macos_x86_64', 'macos_arm64'): + install_cmake_args['platform'] = 'macos-universal' + elif build_platform == 'ubuntu-20.04_x86_64': + install_cmake_args['platform'] = 'linux-x86_64' + else: + raise Exception('Failed to install CMake') + install_cmake(**install_cmake_args) + + if build_platform in ('macos_x86_64', 'macos_arm64'): + add_path(os.path.join(install_dir, 'cmake', 'CMake.app', 'Contents', 'bin')) + else: + add_path(os.path.join(install_dir, 'cmake', 'bin')) + + # Sora C++ SDK + install_sora_args = { + 'version': version['SORA_CPP_SDK_VERSION'], + 'version_file': os.path.join(install_dir, 'sora.version'), + 'source_dir': source_dir, + 'install_dir': install_dir, + 'platform': platform, + } + install_sora(**install_sora_args) + + # protobuf + install_protobuf_args = { + 'version': version['PROTOBUF_VERSION'], + 'version_file': os.path.join(install_dir, 'protobuf.version'), + 'source_dir': source_dir, + 'install_dir': install_dir, + 'platform': '', + } + if build_platform == 'windows_x86_64': + install_protobuf_args['platform'] = 'win64' + elif build_platform in ('macos_x86_64', 'macos_arm64'): + install_protobuf_args['platform'] = 'osx-universal_binary' + elif build_platform == 'ubuntu-20.04_x86_64': + install_protobuf_args['platform'] = 'linux-x86_64' + else: + raise Exception('Failed to install Protobuf') + install_protobuf(**install_protobuf_args) + + # protoc-gen-jsonif + install_jsonif_args = { + 'version': version['PROTOC_GEN_JSONIF_VERSION'], + 'version_file': os.path.join(install_dir, 'protoc-gen-jsonif.version'), + 'source_dir': source_dir, + 'install_dir': install_dir, + 'platform': '', + } + if build_platform == 'windows_x86_64': + install_jsonif_args['platform'] = 'windows-amd64' + elif build_platform == 'macos_x86_64': + install_jsonif_args['platform'] = 'darwin-amd64' + elif build_platform == 'macos_arm64': + install_jsonif_args['platform'] = 'darwin-arm64' + elif build_platform == 'ubuntu-20.04_x86_64': + install_jsonif_args['platform'] = 'linux-amd64' + else: + raise Exception('Failed to install protoc-gen-jsonif') + install_protoc_gen_jsonif(**install_jsonif_args) + + +def get_build_platform(): + arch = platform.machine() + if arch in ('AMD64', 'x86_64'): + arch = 'x86_64' + elif arch in ('aarch64', 'arm64'): + arch = 'arm64' + else: + raise Exception(f'Arch {arch} not supported') + + os = platform.system() + if os == 'Windows': + if arch == 'x86_64': + return 'windows_x86_64' + else: + raise Exception('Unknown windows arch') + elif os == 'Darwin': + return f'macos_{arch}' + elif os == 'Linux': + release = read_version_file('/etc/os-release') + os = release['NAME'] + if os == 'Ubuntu': + osver = release['VERSION_ID'] + if osver == '20.04': + if arch == 'x86_64': + return 'ubuntu-20.04_x86_64' + else: + raise Exception('Unknown ubuntu arch') + else: + raise Exception('Unexpected Ubuntu version') + else: + raise Exception(f'OS {os} not supported') + else: + raise Exception(f'OS {os} not supported') + + +AVAILABLE_TARGETS = ['windows_x86_64', 'macos_arm64', 'ubuntu-20.04_x86_64', 'ios', 'android'] + +BUILD_PLATFORM = { + 'windows_x86_64': ['windows_x86_64'], + 'macos_x86_64': ['macos_x86_64', 'macos_arm64', 'ios'], + 'macos_arm64': ['macos_x86_64', 'macos_arm64', 'ios'], + 'ubuntu-20.04_x86_64': ['ubuntu-20.04_x86_64', 'android'], +} + +def main(): + parser = argparse.ArgumentParser() + parser.add_argument("--debug", action='store_true') + parser.add_argument("--relwithdebinfo", action='store_true') + parser.add_argument("target", choices=AVAILABLE_TARGETS) + + args = parser.parse_args() + + platform = args.target + build_platform = get_build_platform() + if build_platform not in BUILD_PLATFORM: + raise Exception(f'Build platform {build_platform} is not supported.') + if platform not in BUILD_PLATFORM[build_platform]: + raise Exception(f'Target {platform} is not supported on this build platform {build_platform}.') + + configuration_dir = 'debug' if args.debug else 'release' + source_dir = os.path.join(BASE_DIR, '_source', platform, configuration_dir) + build_dir = os.path.join(BASE_DIR, '_build', platform, configuration_dir) + install_dir = os.path.join(BASE_DIR, '_install', platform, configuration_dir) + mkdir_p(source_dir) + mkdir_p(build_dir) + mkdir_p(install_dir) + + install_deps(platform, build_platform, source_dir, build_dir, install_dir, args.debug) + + if args.debug: + configuration = 'Debug' + elif args.relwithdebinfo: + configuration = 'RelWithDebInfo' + else: + configuration = 'Release' + + unity_build_dir = os.path.join(build_dir, 'sora_unity_sdk') + mkdir_p(unity_build_dir) + with cd(unity_build_dir): + webrtc_info = get_webrtc_info(False, source_dir, build_dir, install_dir) + webrtc_version = read_version_file(webrtc_info.version_file) + webrtc_commit = webrtc_version['WEBRTC_COMMIT'] + with cd(BASE_DIR): + version = read_version_file('VERSION') + sora_unity_sdk_version = version['SORA_UNITY_SDK_VERSION'] + sora_unity_sdk_commit = cmdcap(['git', 'rev-parse', 'HEAD']) + android_native_api_level = version['ANDROID_NATIVE_API_LEVEL'] + + cmake_args = [] + cmake_args.append(f'-DCMAKE_BUILD_TYPE={configuration}') + cmake_args.append(f'-DSORA_UNITY_SDK_PACKAGE={platform}') + cmake_args.append(f'-DSORA_UNITY_SDK_VERSION={sora_unity_sdk_version}') + cmake_args.append(f'-DSORA_UNITY_SDK_COMMIT={sora_unity_sdk_commit}') + cmake_args.append(f"-DBOOST_ROOT={cmake_path(os.path.join(install_dir, 'boost'))}") + cmake_args.append('-DWEBRTC_LIBRARY_NAME=webrtc') + cmake_args.append(f"-DWEBRTC_INCLUDE_DIR={cmake_path(webrtc_info.webrtc_include_dir)}") + cmake_args.append(f"-DWEBRTC_LIBRARY_DIR={cmake_path(webrtc_info.webrtc_library_dir)}") + cmake_args.append(f"-DWEBRTC_COMMIT={webrtc_commit}") + cmake_args.append(f"-DSORA_DIR={cmake_path(os.path.join(install_dir, 'sora'))}") + 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'))}") + if platform == 'windows_x86_64': + pass + elif platform in ('macos_x86_64', 'macos_arm64'): + sysroot = cmdcap(['xcrun', '--sdk', 'macosx', '--show-sdk-path']) + arch = 'x86_64' if platform == 'macos_x86_64' else 'arm64' + target = 'x86_64-apple-darwin' if platform == 'macos_x86_64' else 'arm64-apple-darwin' + cmake_args.append(f'-DCMAKE_SYSTEM_PROCESSOR={arch}') + cmake_args.append(f'-DCMAKE_OSX_ARCHITECTURES={arch}') + cmake_args.append(f'-DCMAKE_C_COMPILER_TARGET={target}') + cmake_args.append(f'-DCMAKE_CXX_COMPILER_TARGET={target}') + cmake_args.append(f'-DCMAKE_OBJCXX_COMPILER_TARGET={target}') + cmake_args.append(f'-DCMAKE_SYSROOT={sysroot}') + elif platform == 'ios': + cmake_args += ['-G', 'Xcode'] + cmake_args.append('-DCMAKE_SYSTEM_NAME=iOS') + cmake_args.append('-DCMAKE_OSX_ARCHITECTURES="x86_64;arm64"') + cmake_args.append('-DCMAKE_OSX_DEPLOYMENT_TARGET=10.0') + cmake_args.append('-DCMAKE_XCODE_ATTRIBUTE_ONLY_ACTIVE_ARCH=NO') + elif platform == 'android': + toolchain_file = os.path.join(install_dir, 'android-ndk', 'build', 'cmake', 'android.toolchain.cmake') + cmake_args.append(f"-DCMAKE_TOOLCHAIN_FILE={toolchain_file}") + cmake_args.append(f"-DANDROID_PLATFORM=android-{android_native_api_level}") + cmake_args.append(f"-DANDROID_NATIVE_API_LEVEL={android_native_api_level}") + cmake_args.append('-DANDROID_ABI=arm64-v8a') + cmake_args.append('-DANDROID_STL=none') + cmake_args.append( + f"-DLIBCXX_INCLUDE_DIR={cmake_path(os.path.join(webrtc_info.libcxx_dir, 'include'))}") + cmake_args.append('-DANDROID_CPP_FEATURES=exceptions rtti') + # r23b には ANDROID_CPP_FEATURES=exceptions でも例外が設定されない問題がある + # https://github.com/android/ndk/issues/1618 + cmake_args.append('-DCMAKE_ANDROID_EXCEPTIONS=ON') + elif platform == 'ubuntu-20.04_x86_64': + cmake_args.append(f"-DCMAKE_C_COMPILER={os.path.join(webrtc_info.clang_dir, 'bin', 'clang')}") + cmake_args.append(f"-DCMAKE_CXX_COMPILER={os.path.join(webrtc_info.clang_dir, 'bin', 'clang++')}") + cmake_args.append( + f"-DLIBCXX_INCLUDE_DIR={cmake_path(os.path.join(webrtc_info.libcxx_dir, 'include'))}") + else: + raise Exception(f'Platform {platform} not supported.') + + cmd(['cmake', BASE_DIR, *cmake_args]) + + if platform == 'ios': + cmd(['cmake', '--build', '.', f'-j{multiprocessing.cpu_count()}', + '--config', configuration, + '--target', 'SoraUnitySdk', + '--', + '-arch', 'x86_64', + '-sdk', 'iphonesimulator']) + cmd(['cmake', '--build', '.', f'-j{multiprocessing.cpu_count()}', + '--config', configuration, + '--target', 'SoraUnitySdk', + '--', + '-arch', 'arm64', + '-sdk', 'iphoneos']) + cmd(['lipo', '-create', + '-output', os.path.join(unity_build_dir, 'libSoraUnitySdk.a'), + os.path.join(unity_build_dir, 'Release-iphonesimulator', 'libSoraUnitySdk.a'), + os.path.join(unity_build_dir, 'Release-iphoneos', 'libSoraUnitySdk.a')]) + else: + cmd(['cmake', '--build', '.', f'-j{multiprocessing.cpu_count()}', '--config', configuration]) + + +if __name__ == '__main__': + main() diff --git a/src/device_list.cpp b/src/device_list.cpp index 0ce21d7..4bf7d49 100644 --- a/src/device_list.cpp +++ b/src/device_list.cpp @@ -1,171 +1,171 @@ -#include "device_list.h" - -// webrtc -#include "api/task_queue/default_task_queue_factory.h" -#include "modules/audio_device/include/audio_device.h" -#include "modules/audio_device/include/audio_device_factory.h" -#include "modules/video_capture/video_capture.h" -#include "modules/video_capture/video_capture_factory.h" -#include "rtc_base/logging.h" - -#ifdef SORA_UNITY_SDK_ANDROID -#include "sdk/android/native_api/audio_device_module/audio_device_android.h" -#include "sdk/android/native_api/jni/jvm.h" -#endif - -#if defined(SORA_UNITY_SDK_MACOS) || defined(SORA_UNITY_SDK_IOS) -#include -#elif defined(SORA_UNITY_SDK_ANDROID) -#include -#include "android_helper/android_context.h" -#endif - -namespace sora_unity_sdk { - -bool DeviceList::EnumVideoCapturer( - std::function f) { -#if defined(SORA_UNITY_SDK_MACOS) || defined(SORA_UNITY_SDK_IOS) - - return sora::MacCapturer::EnumVideoDevice(f); - -#elif defined(SORA_UNITY_SDK_ANDROID) - - JNIEnv* env = webrtc::AttachCurrentThreadIfNeeded(); - auto context = GetAndroidApplicationContext(env); - return sora::AndroidCapturer::EnumVideoDevice(env, context.obj(), f); - -#else - - std::unique_ptr info( - webrtc::VideoCaptureFactory::CreateDeviceInfo()); - if (!info) { - RTC_LOG(LS_WARNING) << "Failed to CreateDeviceInfo"; - return false; - } - - int num_devices = info->NumberOfDevices(); - for (int i = 0; i < num_devices; i++) { - char device_name[256]; - char unique_name[256]; - if (info->GetDeviceName(i, device_name, sizeof(device_name), unique_name, - sizeof(unique_name)) != 0) { - RTC_LOG(LS_WARNING) << "Failed to GetDeviceName: index=" << i; - continue; - } - - RTC_LOG(LS_INFO) << "EnumVideoCapturer: device_name=" << device_name - << " unique_name=" << unique_name; - f(device_name, unique_name); - } - return true; - -#endif -} - -bool DeviceList::EnumAudioRecording( - std::function f) { - auto task_queue_factory = webrtc::CreateDefaultTaskQueueFactory(); -#if defined(SORA_UNITY_SDK_ANDROID) || defined(SORA_UNITY_SDK_IOS) - // Android や iOS の場合常に1個しかなく、かつ adm->RecordingDeviceName() を呼ぶと fatal error が起きるので - // 適当な名前で1回だけコールバックする - f("0", "0"); - return true; -#else - -#if defined(SORA_UNITY_SDK_WINDOWS) - auto adm = - webrtc::CreateWindowsCoreAudioAudioDeviceModule(task_queue_factory.get()); -#else - auto adm = webrtc::AudioDeviceModule::Create( - webrtc::AudioDeviceModule::kPlatformDefaultAudio, - task_queue_factory.get()); -#endif - if (adm->Init() != 0) { - RTC_LOG(LS_WARNING) << "Failed to ADM Init"; - return false; - } - - int devices = adm->RecordingDevices(); - for (int i = 0; i < devices; i++) { - char name[webrtc::kAdmMaxDeviceNameSize]; - char guid[webrtc::kAdmMaxGuidSize]; - if (adm->SetRecordingDevice(i) != 0) { - RTC_LOG(LS_WARNING) << "Failed to SetRecordingDevice: index=" << i; - continue; - } - bool available = false; - if (adm->RecordingIsAvailable(&available) != 0) { - RTC_LOG(LS_WARNING) << "Failed to RecordingIsAvailable: index=" << i; - continue; - } - - if (!available) { - continue; - } - if (adm->RecordingDeviceName(i, name, guid) != 0) { - RTC_LOG(LS_WARNING) << "Failed to RecordingDeviceName: index=" << i; - continue; - } - - RTC_LOG(LS_INFO) << "EnumAudioRecording: device_name=" << name - << " unique_name=" << guid; - f(name, guid); - } - return true; -#endif -} - -bool DeviceList::EnumAudioPlayout( - std::function f) { - auto task_queue_factory = webrtc::CreateDefaultTaskQueueFactory(); -#if defined(SORA_UNITY_SDK_ANDROID) || defined(SORA_UNITY_SDK_IOS) - // Android や iOS の場合常に1個しかなく、かつ adm->PlayoutDeviceName() を呼ぶと fatal error が起きるので - // 適当な名前で1回だけコールバックする - f("0", "0"); - return true; -#else - -#if defined(SORA_UNITY_SDK_WINDOWS) - auto adm = - webrtc::CreateWindowsCoreAudioAudioDeviceModule(task_queue_factory.get()); -#else - auto adm = webrtc::AudioDeviceModule::Create( - webrtc::AudioDeviceModule::kPlatformDefaultAudio, - task_queue_factory.get()); -#endif - if (adm->Init() != 0) { - RTC_LOG(LS_WARNING) << "Failed to ADM Init"; - return false; - } - - int devices = adm->PlayoutDevices(); - for (int i = 0; i < devices; i++) { - char name[webrtc::kAdmMaxDeviceNameSize]; - char guid[webrtc::kAdmMaxGuidSize]; - if (adm->SetPlayoutDevice(i) != 0) { - RTC_LOG(LS_WARNING) << "Failed to SetPlayoutDevice: index=" << i; - continue; - } - bool available = false; - if (adm->PlayoutIsAvailable(&available) != 0) { - RTC_LOG(LS_WARNING) << "Failed to PlayoutIsAvailable: index=" << i; - continue; - } - - if (!available) { - continue; - } - if (adm->PlayoutDeviceName(i, name, guid) != 0) { - RTC_LOG(LS_WARNING) << "Failed to PlayoutDeviceName: index=" << i; - continue; - } - - RTC_LOG(LS_INFO) << "EnumAudioPlayout: device_name=" << name - << " unique_name=" << guid; - f(name, guid); - } - return true; -#endif -} - -} // namespace sora_unity_sdk +#include "device_list.h" + +// webrtc +#include "api/task_queue/default_task_queue_factory.h" +#include "modules/audio_device/include/audio_device.h" +#include "modules/audio_device/include/audio_device_factory.h" +#include "modules/video_capture/video_capture.h" +#include "modules/video_capture/video_capture_factory.h" +#include "rtc_base/logging.h" + +#ifdef SORA_UNITY_SDK_ANDROID +#include "sdk/android/native_api/audio_device_module/audio_device_android.h" +#include "sdk/android/native_api/jni/jvm.h" +#endif + +#if defined(SORA_UNITY_SDK_MACOS) || defined(SORA_UNITY_SDK_IOS) +#include +#elif defined(SORA_UNITY_SDK_ANDROID) +#include +#include "android_helper/android_context.h" +#endif + +namespace sora_unity_sdk { + +bool DeviceList::EnumVideoCapturer( + std::function f) { +#if defined(SORA_UNITY_SDK_MACOS) || defined(SORA_UNITY_SDK_IOS) + + return sora::MacCapturer::EnumVideoDevice(f); + +#elif defined(SORA_UNITY_SDK_ANDROID) + + JNIEnv* env = webrtc::AttachCurrentThreadIfNeeded(); + auto context = GetAndroidApplicationContext(env); + return sora::AndroidCapturer::EnumVideoDevice(env, context.obj(), f); + +#else + + std::unique_ptr info( + webrtc::VideoCaptureFactory::CreateDeviceInfo()); + if (!info) { + RTC_LOG(LS_WARNING) << "Failed to CreateDeviceInfo"; + return false; + } + + int num_devices = info->NumberOfDevices(); + for (int i = 0; i < num_devices; i++) { + char device_name[256]; + char unique_name[256]; + if (info->GetDeviceName(i, device_name, sizeof(device_name), unique_name, + sizeof(unique_name)) != 0) { + RTC_LOG(LS_WARNING) << "Failed to GetDeviceName: index=" << i; + continue; + } + + RTC_LOG(LS_INFO) << "EnumVideoCapturer: device_name=" << device_name + << " unique_name=" << unique_name; + f(device_name, unique_name); + } + return true; + +#endif +} + +bool DeviceList::EnumAudioRecording( + std::function f) { + auto task_queue_factory = webrtc::CreateDefaultTaskQueueFactory(); +#if defined(SORA_UNITY_SDK_ANDROID) || defined(SORA_UNITY_SDK_IOS) + // Android や iOS の場合常に1個しかなく、かつ adm->RecordingDeviceName() を呼ぶと fatal error が起きるので + // 適当な名前で1回だけコールバックする + f("0", "0"); + return true; +#else + +#if defined(SORA_UNITY_SDK_WINDOWS) + auto adm = + webrtc::CreateWindowsCoreAudioAudioDeviceModule(task_queue_factory.get()); +#else + auto adm = webrtc::AudioDeviceModule::Create( + webrtc::AudioDeviceModule::kPlatformDefaultAudio, + task_queue_factory.get()); +#endif + if (adm->Init() != 0) { + RTC_LOG(LS_WARNING) << "Failed to ADM Init"; + return false; + } + + int devices = adm->RecordingDevices(); + for (int i = 0; i < devices; i++) { + char name[webrtc::kAdmMaxDeviceNameSize]; + char guid[webrtc::kAdmMaxGuidSize]; + if (adm->SetRecordingDevice(i) != 0) { + RTC_LOG(LS_WARNING) << "Failed to SetRecordingDevice: index=" << i; + continue; + } + bool available = false; + if (adm->RecordingIsAvailable(&available) != 0) { + RTC_LOG(LS_WARNING) << "Failed to RecordingIsAvailable: index=" << i; + continue; + } + + if (!available) { + continue; + } + if (adm->RecordingDeviceName(i, name, guid) != 0) { + RTC_LOG(LS_WARNING) << "Failed to RecordingDeviceName: index=" << i; + continue; + } + + RTC_LOG(LS_INFO) << "EnumAudioRecording: device_name=" << name + << " unique_name=" << guid; + f(name, guid); + } + return true; +#endif +} + +bool DeviceList::EnumAudioPlayout( + std::function f) { + auto task_queue_factory = webrtc::CreateDefaultTaskQueueFactory(); +#if defined(SORA_UNITY_SDK_ANDROID) || defined(SORA_UNITY_SDK_IOS) + // Android や iOS の場合常に1個しかなく、かつ adm->PlayoutDeviceName() を呼ぶと fatal error が起きるので + // 適当な名前で1回だけコールバックする + f("0", "0"); + return true; +#else + +#if defined(SORA_UNITY_SDK_WINDOWS) + auto adm = + webrtc::CreateWindowsCoreAudioAudioDeviceModule(task_queue_factory.get()); +#else + auto adm = webrtc::AudioDeviceModule::Create( + webrtc::AudioDeviceModule::kPlatformDefaultAudio, + task_queue_factory.get()); +#endif + if (adm->Init() != 0) { + RTC_LOG(LS_WARNING) << "Failed to ADM Init"; + return false; + } + + int devices = adm->PlayoutDevices(); + for (int i = 0; i < devices; i++) { + char name[webrtc::kAdmMaxDeviceNameSize]; + char guid[webrtc::kAdmMaxGuidSize]; + if (adm->SetPlayoutDevice(i) != 0) { + RTC_LOG(LS_WARNING) << "Failed to SetPlayoutDevice: index=" << i; + continue; + } + bool available = false; + if (adm->PlayoutIsAvailable(&available) != 0) { + RTC_LOG(LS_WARNING) << "Failed to PlayoutIsAvailable: index=" << i; + continue; + } + + if (!available) { + continue; + } + if (adm->PlayoutDeviceName(i, name, guid) != 0) { + RTC_LOG(LS_WARNING) << "Failed to PlayoutDeviceName: index=" << i; + continue; + } + + RTC_LOG(LS_INFO) << "EnumAudioPlayout: device_name=" << name + << " unique_name=" << guid; + f(name, guid); + } + return true; +#endif +} + +} // namespace sora_unity_sdk diff --git a/src/device_list.h b/src/device_list.h index 5523c70..17ce372 100644 --- a/src/device_list.h +++ b/src/device_list.h @@ -1,20 +1,20 @@ -#ifndef SORA_UNITY_SDK_DEVICE_LIST_H_ -#define SORA_UNITY_SDK_DEVICE_LIST_H_ - -#include -#include - -namespace sora_unity_sdk { - -class DeviceList { - public: - static bool EnumVideoCapturer( - std::function f); - static bool EnumAudioRecording( - std::function f); - static bool EnumAudioPlayout(std::function f); -}; - -} // namespace sora_unity_sdk - -#endif +#ifndef SORA_UNITY_SDK_DEVICE_LIST_H_ +#define SORA_UNITY_SDK_DEVICE_LIST_H_ + +#include +#include + +namespace sora_unity_sdk { + +class DeviceList { + public: + static bool EnumVideoCapturer( + std::function f); + static bool EnumAudioRecording( + std::function f); + static bool EnumAudioPlayout(std::function f); +}; + +} // namespace sora_unity_sdk + +#endif diff --git a/src/id_pointer.h b/src/id_pointer.h index 2352a1d..a79f09f 100644 --- a/src/id_pointer.h +++ b/src/id_pointer.h @@ -1,27 +1,27 @@ -#ifndef SORA_UNITY_SDK_ID_POINTER_H_INCLUDED -#define SORA_UNITY_SDK_ID_POINTER_H_INCLUDED - -#include -#include - -#include "unity.h" - -namespace sora_unity_sdk { - -// TextureUpdateCallback のユーザデータが 32bit 整数しか扱えないので、 -// ID からポインタに変換する仕組みを用意する -class IdPointer { - std::mutex mutex_; - ptrid_t counter_ = 1; - std::map map_; - - public: - static IdPointer& Instance(); - ptrid_t Register(void* p); - void Unregister(ptrid_t id); - void* Lookup(ptrid_t id); -}; - -} // namespace sora_unity_sdk - -#endif +#ifndef SORA_UNITY_SDK_ID_POINTER_H_INCLUDED +#define SORA_UNITY_SDK_ID_POINTER_H_INCLUDED + +#include +#include + +#include "unity.h" + +namespace sora_unity_sdk { + +// TextureUpdateCallback のユーザデータが 32bit 整数しか扱えないので、 +// ID からポインタに変換する仕組みを用意する +class IdPointer { + std::mutex mutex_; + ptrid_t counter_ = 1; + std::map map_; + + public: + static IdPointer& Instance(); + ptrid_t Register(void* p); + void Unregister(ptrid_t id); + void* Lookup(ptrid_t id); +}; + +} // namespace sora_unity_sdk + +#endif diff --git a/src/sora.h b/src/sora.h index c56f695..fbfba84 100644 --- a/src/sora.h +++ b/src/sora.h @@ -1,174 +1,174 @@ -#ifndef SORA_UNITY_SDK_SORA_H_INCLUDED -#define SORA_UNITY_SDK_SORA_H_INCLUDED - -#include -#include -#include - -// Sora -#include - -// Boost -#include - -// WebRTC -#include -#include -#include -#include -#include - -#include "id_pointer.h" -#include "sora_conf.json.h" -#include "sora_conf_internal.json.h" -#include "unity.h" -#include "unity_audio_device.h" -#include "unity_camera_capturer.h" -#include "unity_context.h" -#include "unity_renderer.h" - -#ifdef SORA_UNITY_SDK_ANDROID -#include -#endif - -namespace sora_unity_sdk { - -class Sora : public std::enable_shared_from_this, - public sora::SoraSignalingObserver { - public: - Sora(UnityContext* context); - ~Sora(); - - void SetOnAddTrack(std::function on_add_track); - void SetOnRemoveTrack( - std::function on_remove_track); - void SetOnNotify(std::function on_notify); - void SetOnPush(std::function on_push); - void SetOnMessage(std::function on_message); - void SetOnDisconnect(std::function on_disconnect); - void SetOnDataChannel(std::function on_data_channel); - void SetOnCapturerFrame(std::function on_capturer_frame); - void DispatchEvents(); - - void Connect(const sora_conf::internal::ConnectConfig& cc); - void Disconnect(); - - static void UNITY_INTERFACE_API RenderCallbackStatic(int event_id); - int GetRenderCallbackEventID() const; - - void RenderCallback(); - - void ProcessAudio(const void* p, int offset, int samples); - void SetOnHandleAudio(std::function f); - - void GetStats(std::function on_get_stats); - - void SendMessage(const std::string& label, const std::string& data); - - private: - void* GetAndroidApplicationContext(void* env); - static sora_conf::ErrorCode ToErrorCode(sora::SoraSignalingErrorCode ec); - - // SoraSignalingObserver の実装 - void OnSetOffer(std::string offer) override; - void OnDisconnect(sora::SoraSignalingErrorCode ec, - std::string message) override; - void OnNotify(std::string text) override; - void OnPush(std::string text) override; - void OnMessage(std::string label, std::string data) override; - void OnTrack( - rtc::scoped_refptr transceiver) override; - void OnRemoveTrack( - rtc::scoped_refptr receiver) override; - void OnDataChannel(std::string label) override; - - private: - void DoConnect(const sora_conf::internal::ConnectConfig& config, - std::function on_disconnect); - - static rtc::scoped_refptr CreateADM( - webrtc::TaskQueueFactory* task_queue_factory, - bool dummy_audio, - bool unity_audio_input, - bool unity_audio_output, - std::function on_handle_audio, - std::string audio_recording_device, - std::string audio_playout_device, - rtc::Thread* worker_thread, - void* jni_env, - void* android_context); - static bool InitADM(rtc::scoped_refptr adm, - std::string audio_recording_device, - std::string audio_playout_device); - - static rtc::scoped_refptr - CreateVideoCapturer( - int capturer_type, - void* unity_camera_texture, - std::string video_capturer_device, - int video_width, - int video_height, - int video_fps, - std::function on_frame, - rtc::Thread* signaling_thread, - void* jni_env, - void* android_context); - - void PushEvent(std::function f); - - struct CapturerSink : rtc::VideoSinkInterface { - CapturerSink(rtc::scoped_refptr capturer, - std::function on_frame); - ~CapturerSink() override; - void OnFrame(const webrtc::VideoFrame& frame) override; - - private: - rtc::scoped_refptr capturer_; - std::function on_frame_; - }; - - private: - std::unique_ptr ioc_; - std::shared_ptr signaling_; - UnityContext* context_; - std::unique_ptr renderer_; - rtc::scoped_refptr audio_track_; - rtc::scoped_refptr video_track_; - std::function on_add_track_; - std::function on_remove_track_; - std::function on_notify_; - std::function on_push_; - std::function on_message_; - std::function on_disconnect_; - std::function on_data_channel_; - std::function on_handle_audio_; - std::function on_capturer_frame_; - - std::unique_ptr io_thread_; - std::unique_ptr network_thread_; - std::unique_ptr worker_thread_; - std::unique_ptr signaling_thread_; - rtc::scoped_refptr factory_; - rtc::scoped_refptr connection_context_; - - std::mutex event_mutex_; - std::deque> event_queue_; - - ptrid_t ptrid_; - - std::map connection_ids_; - - rtc::scoped_refptr capturer_; - int capturer_type_ = 0; - std::shared_ptr capturer_sink_; - - rtc::scoped_refptr unity_adm_; - webrtc::TaskQueueFactory* task_queue_factory_; -#if defined(SORA_UNITY_SDK_ANDROID) - webrtc::ScopedJavaGlobalRef android_context_; -#endif -}; - -} // namespace sora_unity_sdk - -#endif +#ifndef SORA_UNITY_SDK_SORA_H_INCLUDED +#define SORA_UNITY_SDK_SORA_H_INCLUDED + +#include +#include +#include + +// Sora +#include + +// Boost +#include + +// WebRTC +#include +#include +#include +#include +#include + +#include "id_pointer.h" +#include "sora_conf.json.h" +#include "sora_conf_internal.json.h" +#include "unity.h" +#include "unity_audio_device.h" +#include "unity_camera_capturer.h" +#include "unity_context.h" +#include "unity_renderer.h" + +#ifdef SORA_UNITY_SDK_ANDROID +#include +#endif + +namespace sora_unity_sdk { + +class Sora : public std::enable_shared_from_this, + public sora::SoraSignalingObserver { + public: + Sora(UnityContext* context); + ~Sora(); + + void SetOnAddTrack(std::function on_add_track); + void SetOnRemoveTrack( + std::function on_remove_track); + void SetOnNotify(std::function on_notify); + void SetOnPush(std::function on_push); + void SetOnMessage(std::function on_message); + void SetOnDisconnect(std::function on_disconnect); + void SetOnDataChannel(std::function on_data_channel); + void SetOnCapturerFrame(std::function on_capturer_frame); + void DispatchEvents(); + + void Connect(const sora_conf::internal::ConnectConfig& cc); + void Disconnect(); + + static void UNITY_INTERFACE_API RenderCallbackStatic(int event_id); + int GetRenderCallbackEventID() const; + + void RenderCallback(); + + void ProcessAudio(const void* p, int offset, int samples); + void SetOnHandleAudio(std::function f); + + void GetStats(std::function on_get_stats); + + void SendMessage(const std::string& label, const std::string& data); + + private: + void* GetAndroidApplicationContext(void* env); + static sora_conf::ErrorCode ToErrorCode(sora::SoraSignalingErrorCode ec); + + // SoraSignalingObserver の実装 + void OnSetOffer(std::string offer) override; + void OnDisconnect(sora::SoraSignalingErrorCode ec, + std::string message) override; + void OnNotify(std::string text) override; + void OnPush(std::string text) override; + void OnMessage(std::string label, std::string data) override; + void OnTrack( + rtc::scoped_refptr transceiver) override; + void OnRemoveTrack( + rtc::scoped_refptr receiver) override; + void OnDataChannel(std::string label) override; + + private: + void DoConnect(const sora_conf::internal::ConnectConfig& config, + std::function on_disconnect); + + static rtc::scoped_refptr CreateADM( + webrtc::TaskQueueFactory* task_queue_factory, + bool dummy_audio, + bool unity_audio_input, + bool unity_audio_output, + std::function on_handle_audio, + std::string audio_recording_device, + std::string audio_playout_device, + rtc::Thread* worker_thread, + void* jni_env, + void* android_context); + static bool InitADM(rtc::scoped_refptr adm, + std::string audio_recording_device, + std::string audio_playout_device); + + static rtc::scoped_refptr + CreateVideoCapturer( + int capturer_type, + void* unity_camera_texture, + std::string video_capturer_device, + int video_width, + int video_height, + int video_fps, + std::function on_frame, + rtc::Thread* signaling_thread, + void* jni_env, + void* android_context); + + void PushEvent(std::function f); + + struct CapturerSink : rtc::VideoSinkInterface { + CapturerSink(rtc::scoped_refptr capturer, + std::function on_frame); + ~CapturerSink() override; + void OnFrame(const webrtc::VideoFrame& frame) override; + + private: + rtc::scoped_refptr capturer_; + std::function on_frame_; + }; + + private: + std::unique_ptr ioc_; + std::shared_ptr signaling_; + UnityContext* context_; + std::unique_ptr renderer_; + rtc::scoped_refptr audio_track_; + rtc::scoped_refptr video_track_; + std::function on_add_track_; + std::function on_remove_track_; + std::function on_notify_; + std::function on_push_; + std::function on_message_; + std::function on_disconnect_; + std::function on_data_channel_; + std::function on_handle_audio_; + std::function on_capturer_frame_; + + std::unique_ptr io_thread_; + std::unique_ptr network_thread_; + std::unique_ptr worker_thread_; + std::unique_ptr signaling_thread_; + rtc::scoped_refptr factory_; + rtc::scoped_refptr connection_context_; + + std::mutex event_mutex_; + std::deque> event_queue_; + + ptrid_t ptrid_; + + std::map connection_ids_; + + rtc::scoped_refptr capturer_; + int capturer_type_ = 0; + std::shared_ptr capturer_sink_; + + rtc::scoped_refptr unity_adm_; + webrtc::TaskQueueFactory* task_queue_factory_; +#if defined(SORA_UNITY_SDK_ANDROID) + webrtc::ScopedJavaGlobalRef android_context_; +#endif +}; + +} // namespace sora_unity_sdk + +#endif diff --git a/src/unity_audio_device.h b/src/unity_audio_device.h index 434cc49..331886c 100644 --- a/src/unity_audio_device.h +++ b/src/unity_audio_device.h @@ -1,450 +1,450 @@ -#ifndef SORA_UNITY_SDK_UNITY_AUDIO_DEVICE_H_INCLUDED -#define SORA_UNITY_SDK_UNITY_AUDIO_DEVICE_H_INCLUDED - -#include -#include -#include -#include - -// webrtc -#include "modules/audio_device/audio_device_buffer.h" -#include "modules/audio_device/include/audio_device.h" -#include "rtc_base/ref_counted_object.h" -#include "rtc_base/thread.h" - -namespace sora_unity_sdk { - -class UnityAudioDevice : public webrtc::AudioDeviceModule { - public: - UnityAudioDevice( - rtc::scoped_refptr adm, - bool adm_recording, - bool adm_playout, - std::function - on_handle_audio, - webrtc::TaskQueueFactory* task_queue_factory) - : adm_(adm), - adm_recording_(adm_recording), - adm_playout_(adm_playout), - on_handle_audio_(on_handle_audio), - task_queue_factory_(task_queue_factory) {} - - ~UnityAudioDevice() override { - RTC_LOG(LS_INFO) << "~UnityAudioDevice"; - Terminate(); - } - - static rtc::scoped_refptr Create( - rtc::scoped_refptr adm, - bool adm_recording, - bool adm_playout, - std::function - on_handle_audio, - webrtc::TaskQueueFactory* task_queue_factory) { - return rtc::make_ref_counted( - adm, adm_recording, adm_playout, on_handle_audio, task_queue_factory); - } - - void ProcessAudioData(const float* data, int32_t size) { - if (!adm_recording_ && initialized_ && is_recording_) { - for (int i = 0; i < size; i++) { -#pragma warning(suppress : 4244) - converted_audio_data_.push_back(data[i] >= 0 ? data[i] * SHRT_MAX - : data[i] * -SHRT_MIN); - } - //opus supports up to 48khz sample rate, enforce 48khz here for quality - int chunk_size = 48000 * 2 / 100; - while (converted_audio_data_.size() > chunk_size) { - device_buffer_->SetRecordedBuffer(converted_audio_data_.data(), - chunk_size / 2); - device_buffer_->DeliverRecordedData(); - converted_audio_data_.erase(converted_audio_data_.begin(), - converted_audio_data_.begin() + chunk_size); - } - } - } - - //webrtc::AudioDeviceModule - // Retrieve the currently utilized audio layer - virtual int32_t ActiveAudioLayer(AudioLayer* audioLayer) const override { - //*audioLayer = AudioDeviceModule::kPlatformDefaultAudio; - adm_->ActiveAudioLayer(audioLayer); - return 0; - } - // Full-duplex transportation of PCM audio - virtual int32_t RegisterAudioCallback( - webrtc::AudioTransport* audioCallback) override { - RTC_LOG(LS_INFO) << "RegisterAudioCallback"; - if (!adm_recording_ || !adm_playout_) { - device_buffer_->RegisterAudioCallback(audioCallback); - } - return adm_->RegisterAudioCallback(audioCallback); - } - - // Main initialization and termination - virtual int32_t Init() override { - RTC_LOG(LS_INFO) << "Init"; - device_buffer_ = - std::make_unique(task_queue_factory_); - initialized_ = true; - return adm_->Init(); - } - virtual int32_t Terminate() override { - RTC_LOG(LS_INFO) << "Terminate"; - - DoStopPlayout(); - - initialized_ = false; - is_recording_ = false; - is_playing_ = false; - device_buffer_.reset(); - - auto result = adm_->Terminate(); - - RTC_LOG(LS_INFO) << "Terminate Completed"; - - return result; - } - virtual bool Initialized() const override { - return initialized_ && adm_->Initialized(); - } - - // Device enumeration - virtual int16_t PlayoutDevices() override { - RTC_LOG(LS_INFO) << "PlayoutDevices"; - return adm_playout_ ? adm_->PlayoutDevices() : 0; - } - virtual int16_t RecordingDevices() override { - return adm_recording_ ? adm_->RecordingDevices() : 0; - } - virtual int32_t PlayoutDeviceName( - uint16_t index, - char name[webrtc::kAdmMaxDeviceNameSize], - char guid[webrtc::kAdmMaxGuidSize]) override { - return adm_playout_ ? adm_->PlayoutDeviceName(index, name, guid) : 0; - } - virtual int32_t RecordingDeviceName( - uint16_t index, - char name[webrtc::kAdmMaxDeviceNameSize], - char guid[webrtc::kAdmMaxGuidSize]) override { - return adm_recording_ ? adm_->RecordingDeviceName(index, name, guid) : 0; - } - - // Device selection - virtual int32_t SetPlayoutDevice(uint16_t index) override { - RTC_LOG(LS_INFO) << "SetPlayoutDevice(" << index << ")"; - return adm_playout_ ? adm_->SetPlayoutDevice(index) : 0; - } - virtual int32_t SetPlayoutDevice(WindowsDeviceType device) override { - return adm_playout_ ? adm_->SetPlayoutDevice(device) : 0; - } - virtual int32_t SetRecordingDevice(uint16_t index) override { - RTC_LOG(LS_INFO) << "SetRecordingDevice(" << index << ")"; - return adm_recording_ ? adm_->SetRecordingDevice(index) : 0; - } - virtual int32_t SetRecordingDevice(WindowsDeviceType device) override { - return adm_recording_ ? adm_->SetRecordingDevice(device) : 0; - } - - void HandleAudioData() { - int channels = stereo_playout_ ? 2 : 1; - auto next_at = std::chrono::steady_clock::now(); - while (!handle_audio_thread_stopped_) { - // 10 ミリ秒ごとにオーディオデータを取得する - next_at += std::chrono::milliseconds(10); - std::this_thread::sleep_until(next_at); - - int chunk_size = 48000 / 100; - int samples = device_buffer_->RequestPlayoutData(chunk_size); - - //RTC_LOG(LS_INFO) << "handle audio data: chunk_size=" << chunk_size - // << " samples=" << samples; - - std::unique_ptr audio_buffer(new int16_t[samples * channels]); - device_buffer_->GetPlayoutData(audio_buffer.get()); - if (on_handle_audio_) { - on_handle_audio_(audio_buffer.get(), samples, channels); - } - } - } - - // Audio transport initialization - virtual int32_t PlayoutIsAvailable(bool* available) override { - RTC_LOG(LS_INFO) << "PlayoutIsAvailable"; - - if (adm_playout_) { - return adm_->PlayoutIsAvailable(available); - } else { - *available = true; - return 0; - } - } - virtual int32_t InitPlayout() override { - RTC_LOG(LS_INFO) << "InitPlayout"; - - if (adm_playout_) { - return adm_->InitPlayout(); - } else { - DoStopPlayout(); - - is_playing_ = true; - device_buffer_->SetPlayoutSampleRate(48000); - device_buffer_->SetPlayoutChannels(stereo_playout_ ? 2 : 1); - - return 0; - } - } - virtual bool PlayoutIsInitialized() const override { - auto result = - adm_playout_ ? adm_->PlayoutIsInitialized() : (bool)is_playing_; - RTC_LOG(LS_INFO) << "PlayoutIsInitialized: result=" << result; - return result; - } - virtual int32_t RecordingIsAvailable(bool* available) override { - if (adm_recording_) { - return adm_->RecordingIsAvailable(available); - } else { - *available = true; - return 0; - } - } - virtual int32_t InitRecording() override { - if (adm_recording_) { - return adm_->InitRecording(); - } else { - is_recording_ = true; - device_buffer_->SetRecordingSampleRate(48000); - device_buffer_->SetRecordingChannels(2); - return 0; - } - } - virtual bool RecordingIsInitialized() const override { - return adm_recording_ ? adm_->RecordingIsInitialized() - : (bool)is_recording_; - } - - // Audio transport control - virtual int32_t StartPlayout() override { - RTC_LOG(LS_INFO) << "StartPlayout"; - if (adm_playout_) { - return adm_->StartPlayout(); - } else { - is_playing_ = true; - handle_audio_thread_.reset(new std::thread([this]() { - RTC_LOG(LS_INFO) << "Sora Audio Playout Thread started"; - HandleAudioData(); - RTC_LOG(LS_INFO) << "Sora Audio Playout Thread finished"; - })); - - return 0; - } - } - void DoStopPlayout() { - if (handle_audio_thread_) { - RTC_LOG(LS_INFO) << "Terminating Audio Thread"; - handle_audio_thread_stopped_ = true; - handle_audio_thread_->join(); - handle_audio_thread_.reset(); - handle_audio_thread_stopped_ = false; - RTC_LOG(LS_INFO) << "Terminated Audio Thread"; - } - } - virtual int32_t StopPlayout() override { - RTC_LOG(LS_INFO) << "StopPlayout"; - if (adm_playout_) { - return adm_->StopPlayout(); - } else { - DoStopPlayout(); - is_playing_ = false; - return 0; - } - } - virtual bool Playing() const override { - return adm_playout_ ? adm_->Playing() : (bool)is_playing_; - } - virtual int32_t StartRecording() override { - return adm_recording_ ? adm_->StartRecording() : 0; - } - virtual int32_t StopRecording() override { - return adm_recording_ ? adm_->StopRecording() : 0; - } - virtual bool Recording() const override { - return adm_recording_ ? adm_->Recording() : (bool)is_recording_; - } - - // Audio mixer initialization - virtual int32_t InitSpeaker() override { - return adm_playout_ ? adm_->InitSpeaker() : 0; - } - virtual bool SpeakerIsInitialized() const override { - return adm_playout_ ? adm_->SpeakerIsInitialized() : false; - } - virtual int32_t InitMicrophone() override { - return adm_recording_ ? adm_->InitMicrophone() : 0; - } - virtual bool MicrophoneIsInitialized() const override { - return adm_recording_ ? adm_->MicrophoneIsInitialized() : false; - } - - // Speaker volume controls - virtual int32_t SpeakerVolumeIsAvailable(bool* available) override { - return adm_playout_ ? adm_->SpeakerVolumeIsAvailable(available) : 0; - } - virtual int32_t SetSpeakerVolume(uint32_t volume) override { - return adm_playout_ ? adm_->SetSpeakerVolume(volume) : 0; - } - virtual int32_t SpeakerVolume(uint32_t* volume) const override { - return adm_playout_ ? adm_->SpeakerVolume(volume) : 0; - } - virtual int32_t MaxSpeakerVolume(uint32_t* maxVolume) const override { - return adm_playout_ ? adm_->MaxSpeakerVolume(maxVolume) : 0; - } - virtual int32_t MinSpeakerVolume(uint32_t* minVolume) const override { - return adm_playout_ ? adm_->MinSpeakerVolume(minVolume) : 0; - } - - // Microphone volume controls - virtual int32_t MicrophoneVolumeIsAvailable(bool* available) override { - return adm_recording_ ? adm_->MicrophoneVolumeIsAvailable(available) : 0; - } - virtual int32_t SetMicrophoneVolume(uint32_t volume) override { - return adm_recording_ ? adm_->SetMicrophoneVolume(volume) : 0; - } - virtual int32_t MicrophoneVolume(uint32_t* volume) const override { - return adm_recording_ ? adm_->MicrophoneVolume(volume) : 0; - } - virtual int32_t MaxMicrophoneVolume(uint32_t* maxVolume) const override { - return adm_recording_ ? adm_->MaxMicrophoneVolume(maxVolume) : 0; - } - virtual int32_t MinMicrophoneVolume(uint32_t* minVolume) const override { - return adm_recording_ ? adm_->MinMicrophoneVolume(minVolume) : 0; - } - - // Speaker mute control - virtual int32_t SpeakerMuteIsAvailable(bool* available) override { - return adm_playout_ ? adm_->SpeakerMuteIsAvailable(available) : 0; - } - virtual int32_t SetSpeakerMute(bool enable) override { - return adm_playout_ ? adm_->SetSpeakerMute(enable) : 0; - } - virtual int32_t SpeakerMute(bool* enabled) const override { - return adm_playout_ ? adm_->SpeakerMute(enabled) : 0; - } - - // Microphone mute control - virtual int32_t MicrophoneMuteIsAvailable(bool* available) override { - return adm_recording_ ? adm_->MicrophoneMuteIsAvailable(available) : 0; - } - virtual int32_t SetMicrophoneMute(bool enable) override { - return adm_recording_ ? adm_->SetMicrophoneMute(enable) : 0; - } - virtual int32_t MicrophoneMute(bool* enabled) const override { - return adm_recording_ ? adm_->MicrophoneMute(enabled) : 0; - } - - // Stereo support - virtual int32_t StereoPlayoutIsAvailable(bool* available) const override { - if (adm_playout_) { - return adm_->StereoPlayoutIsAvailable(available); - } else { - // 今はステレオには対応しない - //*available = true; - *available = false; - return 0; - } - } - virtual int32_t SetStereoPlayout(bool enable) override { - if (adm_playout_) { - return adm_->SetStereoPlayout(enable); - } else { - stereo_playout_ = enable; - return 0; - }; - } - virtual int32_t StereoPlayout(bool* enabled) const override { - if (adm_playout_) { - return adm_->StereoPlayoutIsAvailable(enabled); - } else { - *enabled = stereo_playout_; - return 0; - } - } - virtual int32_t StereoRecordingIsAvailable(bool* available) const override { - if (adm_recording_) { - return adm_->StereoRecordingIsAvailable(available); - } else { - *available = true; - return 0; - } - } - virtual int32_t SetStereoRecording(bool enable) override { - return adm_recording_ ? adm_->SetStereoRecording(enable) : 0; - } - virtual int32_t StereoRecording(bool* enabled) const override { - if (adm_recording_) { - return adm_->StereoRecording(enabled); - } else { - *enabled = true; - return 0; - } - } - - // Playout delay - virtual int32_t PlayoutDelay(uint16_t* delayMS) const override { - return adm_playout_ ? adm_->PlayoutDelay(delayMS) : 0; - } - - // Only supported on Android. - virtual bool BuiltInAECIsAvailable() const override { - return false; - } - virtual bool BuiltInAGCIsAvailable() const override { - return false; - } - virtual bool BuiltInNSIsAvailable() const override { - return false; - } - - // Enables the built-in audio effects. Only supported on Android. - virtual int32_t EnableBuiltInAEC(bool enable) override { - return 0; - } - virtual int32_t EnableBuiltInAGC(bool enable) override { - return 0; - } - virtual int32_t EnableBuiltInNS(bool enable) override { - return 0; - } - -// Only supported on iOS. -#if defined(WEBRTC_IOS) - virtual int GetPlayoutAudioParameters( - webrtc::AudioParameters* params) const override { - return -1; - } - virtual int GetRecordAudioParameters( - webrtc::AudioParameters* params) const override { - return -1; - } -#endif // WEBRTC_IOS - - private: - rtc::scoped_refptr adm_; - bool adm_recording_; - bool adm_playout_; - webrtc::TaskQueueFactory* task_queue_factory_; - std::function - on_handle_audio_; - std::unique_ptr handle_audio_thread_; - std::atomic_bool handle_audio_thread_stopped_ = {false}; - std::unique_ptr device_buffer_; - std::atomic_bool initialized_ = {false}; - std::atomic_bool is_recording_ = {false}; - std::atomic_bool is_playing_ = {false}; - std::atomic_bool stereo_playout_ = {false}; - std::vector converted_audio_data_; -}; - -} // namespace sora_unity_sdk - -#endif +#ifndef SORA_UNITY_SDK_UNITY_AUDIO_DEVICE_H_INCLUDED +#define SORA_UNITY_SDK_UNITY_AUDIO_DEVICE_H_INCLUDED + +#include +#include +#include +#include + +// webrtc +#include "modules/audio_device/audio_device_buffer.h" +#include "modules/audio_device/include/audio_device.h" +#include "rtc_base/ref_counted_object.h" +#include "rtc_base/thread.h" + +namespace sora_unity_sdk { + +class UnityAudioDevice : public webrtc::AudioDeviceModule { + public: + UnityAudioDevice( + rtc::scoped_refptr adm, + bool adm_recording, + bool adm_playout, + std::function + on_handle_audio, + webrtc::TaskQueueFactory* task_queue_factory) + : adm_(adm), + adm_recording_(adm_recording), + adm_playout_(adm_playout), + on_handle_audio_(on_handle_audio), + task_queue_factory_(task_queue_factory) {} + + ~UnityAudioDevice() override { + RTC_LOG(LS_INFO) << "~UnityAudioDevice"; + Terminate(); + } + + static rtc::scoped_refptr Create( + rtc::scoped_refptr adm, + bool adm_recording, + bool adm_playout, + std::function + on_handle_audio, + webrtc::TaskQueueFactory* task_queue_factory) { + return rtc::make_ref_counted( + adm, adm_recording, adm_playout, on_handle_audio, task_queue_factory); + } + + void ProcessAudioData(const float* data, int32_t size) { + if (!adm_recording_ && initialized_ && is_recording_) { + for (int i = 0; i < size; i++) { +#pragma warning(suppress : 4244) + converted_audio_data_.push_back(data[i] >= 0 ? data[i] * SHRT_MAX + : data[i] * -SHRT_MIN); + } + //opus supports up to 48khz sample rate, enforce 48khz here for quality + int chunk_size = 48000 * 2 / 100; + while (converted_audio_data_.size() > chunk_size) { + device_buffer_->SetRecordedBuffer(converted_audio_data_.data(), + chunk_size / 2); + device_buffer_->DeliverRecordedData(); + converted_audio_data_.erase(converted_audio_data_.begin(), + converted_audio_data_.begin() + chunk_size); + } + } + } + + //webrtc::AudioDeviceModule + // Retrieve the currently utilized audio layer + virtual int32_t ActiveAudioLayer(AudioLayer* audioLayer) const override { + //*audioLayer = AudioDeviceModule::kPlatformDefaultAudio; + adm_->ActiveAudioLayer(audioLayer); + return 0; + } + // Full-duplex transportation of PCM audio + virtual int32_t RegisterAudioCallback( + webrtc::AudioTransport* audioCallback) override { + RTC_LOG(LS_INFO) << "RegisterAudioCallback"; + if (!adm_recording_ || !adm_playout_) { + device_buffer_->RegisterAudioCallback(audioCallback); + } + return adm_->RegisterAudioCallback(audioCallback); + } + + // Main initialization and termination + virtual int32_t Init() override { + RTC_LOG(LS_INFO) << "Init"; + device_buffer_ = + std::make_unique(task_queue_factory_); + initialized_ = true; + return adm_->Init(); + } + virtual int32_t Terminate() override { + RTC_LOG(LS_INFO) << "Terminate"; + + DoStopPlayout(); + + initialized_ = false; + is_recording_ = false; + is_playing_ = false; + device_buffer_.reset(); + + auto result = adm_->Terminate(); + + RTC_LOG(LS_INFO) << "Terminate Completed"; + + return result; + } + virtual bool Initialized() const override { + return initialized_ && adm_->Initialized(); + } + + // Device enumeration + virtual int16_t PlayoutDevices() override { + RTC_LOG(LS_INFO) << "PlayoutDevices"; + return adm_playout_ ? adm_->PlayoutDevices() : 0; + } + virtual int16_t RecordingDevices() override { + return adm_recording_ ? adm_->RecordingDevices() : 0; + } + virtual int32_t PlayoutDeviceName( + uint16_t index, + char name[webrtc::kAdmMaxDeviceNameSize], + char guid[webrtc::kAdmMaxGuidSize]) override { + return adm_playout_ ? adm_->PlayoutDeviceName(index, name, guid) : 0; + } + virtual int32_t RecordingDeviceName( + uint16_t index, + char name[webrtc::kAdmMaxDeviceNameSize], + char guid[webrtc::kAdmMaxGuidSize]) override { + return adm_recording_ ? adm_->RecordingDeviceName(index, name, guid) : 0; + } + + // Device selection + virtual int32_t SetPlayoutDevice(uint16_t index) override { + RTC_LOG(LS_INFO) << "SetPlayoutDevice(" << index << ")"; + return adm_playout_ ? adm_->SetPlayoutDevice(index) : 0; + } + virtual int32_t SetPlayoutDevice(WindowsDeviceType device) override { + return adm_playout_ ? adm_->SetPlayoutDevice(device) : 0; + } + virtual int32_t SetRecordingDevice(uint16_t index) override { + RTC_LOG(LS_INFO) << "SetRecordingDevice(" << index << ")"; + return adm_recording_ ? adm_->SetRecordingDevice(index) : 0; + } + virtual int32_t SetRecordingDevice(WindowsDeviceType device) override { + return adm_recording_ ? adm_->SetRecordingDevice(device) : 0; + } + + void HandleAudioData() { + int channels = stereo_playout_ ? 2 : 1; + auto next_at = std::chrono::steady_clock::now(); + while (!handle_audio_thread_stopped_) { + // 10 ミリ秒ごとにオーディオデータを取得する + next_at += std::chrono::milliseconds(10); + std::this_thread::sleep_until(next_at); + + int chunk_size = 48000 / 100; + int samples = device_buffer_->RequestPlayoutData(chunk_size); + + //RTC_LOG(LS_INFO) << "handle audio data: chunk_size=" << chunk_size + // << " samples=" << samples; + + std::unique_ptr audio_buffer(new int16_t[samples * channels]); + device_buffer_->GetPlayoutData(audio_buffer.get()); + if (on_handle_audio_) { + on_handle_audio_(audio_buffer.get(), samples, channels); + } + } + } + + // Audio transport initialization + virtual int32_t PlayoutIsAvailable(bool* available) override { + RTC_LOG(LS_INFO) << "PlayoutIsAvailable"; + + if (adm_playout_) { + return adm_->PlayoutIsAvailable(available); + } else { + *available = true; + return 0; + } + } + virtual int32_t InitPlayout() override { + RTC_LOG(LS_INFO) << "InitPlayout"; + + if (adm_playout_) { + return adm_->InitPlayout(); + } else { + DoStopPlayout(); + + is_playing_ = true; + device_buffer_->SetPlayoutSampleRate(48000); + device_buffer_->SetPlayoutChannels(stereo_playout_ ? 2 : 1); + + return 0; + } + } + virtual bool PlayoutIsInitialized() const override { + auto result = + adm_playout_ ? adm_->PlayoutIsInitialized() : (bool)is_playing_; + RTC_LOG(LS_INFO) << "PlayoutIsInitialized: result=" << result; + return result; + } + virtual int32_t RecordingIsAvailable(bool* available) override { + if (adm_recording_) { + return adm_->RecordingIsAvailable(available); + } else { + *available = true; + return 0; + } + } + virtual int32_t InitRecording() override { + if (adm_recording_) { + return adm_->InitRecording(); + } else { + is_recording_ = true; + device_buffer_->SetRecordingSampleRate(48000); + device_buffer_->SetRecordingChannels(2); + return 0; + } + } + virtual bool RecordingIsInitialized() const override { + return adm_recording_ ? adm_->RecordingIsInitialized() + : (bool)is_recording_; + } + + // Audio transport control + virtual int32_t StartPlayout() override { + RTC_LOG(LS_INFO) << "StartPlayout"; + if (adm_playout_) { + return adm_->StartPlayout(); + } else { + is_playing_ = true; + handle_audio_thread_.reset(new std::thread([this]() { + RTC_LOG(LS_INFO) << "Sora Audio Playout Thread started"; + HandleAudioData(); + RTC_LOG(LS_INFO) << "Sora Audio Playout Thread finished"; + })); + + return 0; + } + } + void DoStopPlayout() { + if (handle_audio_thread_) { + RTC_LOG(LS_INFO) << "Terminating Audio Thread"; + handle_audio_thread_stopped_ = true; + handle_audio_thread_->join(); + handle_audio_thread_.reset(); + handle_audio_thread_stopped_ = false; + RTC_LOG(LS_INFO) << "Terminated Audio Thread"; + } + } + virtual int32_t StopPlayout() override { + RTC_LOG(LS_INFO) << "StopPlayout"; + if (adm_playout_) { + return adm_->StopPlayout(); + } else { + DoStopPlayout(); + is_playing_ = false; + return 0; + } + } + virtual bool Playing() const override { + return adm_playout_ ? adm_->Playing() : (bool)is_playing_; + } + virtual int32_t StartRecording() override { + return adm_recording_ ? adm_->StartRecording() : 0; + } + virtual int32_t StopRecording() override { + return adm_recording_ ? adm_->StopRecording() : 0; + } + virtual bool Recording() const override { + return adm_recording_ ? adm_->Recording() : (bool)is_recording_; + } + + // Audio mixer initialization + virtual int32_t InitSpeaker() override { + return adm_playout_ ? adm_->InitSpeaker() : 0; + } + virtual bool SpeakerIsInitialized() const override { + return adm_playout_ ? adm_->SpeakerIsInitialized() : false; + } + virtual int32_t InitMicrophone() override { + return adm_recording_ ? adm_->InitMicrophone() : 0; + } + virtual bool MicrophoneIsInitialized() const override { + return adm_recording_ ? adm_->MicrophoneIsInitialized() : false; + } + + // Speaker volume controls + virtual int32_t SpeakerVolumeIsAvailable(bool* available) override { + return adm_playout_ ? adm_->SpeakerVolumeIsAvailable(available) : 0; + } + virtual int32_t SetSpeakerVolume(uint32_t volume) override { + return adm_playout_ ? adm_->SetSpeakerVolume(volume) : 0; + } + virtual int32_t SpeakerVolume(uint32_t* volume) const override { + return adm_playout_ ? adm_->SpeakerVolume(volume) : 0; + } + virtual int32_t MaxSpeakerVolume(uint32_t* maxVolume) const override { + return adm_playout_ ? adm_->MaxSpeakerVolume(maxVolume) : 0; + } + virtual int32_t MinSpeakerVolume(uint32_t* minVolume) const override { + return adm_playout_ ? adm_->MinSpeakerVolume(minVolume) : 0; + } + + // Microphone volume controls + virtual int32_t MicrophoneVolumeIsAvailable(bool* available) override { + return adm_recording_ ? adm_->MicrophoneVolumeIsAvailable(available) : 0; + } + virtual int32_t SetMicrophoneVolume(uint32_t volume) override { + return adm_recording_ ? adm_->SetMicrophoneVolume(volume) : 0; + } + virtual int32_t MicrophoneVolume(uint32_t* volume) const override { + return adm_recording_ ? adm_->MicrophoneVolume(volume) : 0; + } + virtual int32_t MaxMicrophoneVolume(uint32_t* maxVolume) const override { + return adm_recording_ ? adm_->MaxMicrophoneVolume(maxVolume) : 0; + } + virtual int32_t MinMicrophoneVolume(uint32_t* minVolume) const override { + return adm_recording_ ? adm_->MinMicrophoneVolume(minVolume) : 0; + } + + // Speaker mute control + virtual int32_t SpeakerMuteIsAvailable(bool* available) override { + return adm_playout_ ? adm_->SpeakerMuteIsAvailable(available) : 0; + } + virtual int32_t SetSpeakerMute(bool enable) override { + return adm_playout_ ? adm_->SetSpeakerMute(enable) : 0; + } + virtual int32_t SpeakerMute(bool* enabled) const override { + return adm_playout_ ? adm_->SpeakerMute(enabled) : 0; + } + + // Microphone mute control + virtual int32_t MicrophoneMuteIsAvailable(bool* available) override { + return adm_recording_ ? adm_->MicrophoneMuteIsAvailable(available) : 0; + } + virtual int32_t SetMicrophoneMute(bool enable) override { + return adm_recording_ ? adm_->SetMicrophoneMute(enable) : 0; + } + virtual int32_t MicrophoneMute(bool* enabled) const override { + return adm_recording_ ? adm_->MicrophoneMute(enabled) : 0; + } + + // Stereo support + virtual int32_t StereoPlayoutIsAvailable(bool* available) const override { + if (adm_playout_) { + return adm_->StereoPlayoutIsAvailable(available); + } else { + // 今はステレオには対応しない + //*available = true; + *available = false; + return 0; + } + } + virtual int32_t SetStereoPlayout(bool enable) override { + if (adm_playout_) { + return adm_->SetStereoPlayout(enable); + } else { + stereo_playout_ = enable; + return 0; + }; + } + virtual int32_t StereoPlayout(bool* enabled) const override { + if (adm_playout_) { + return adm_->StereoPlayoutIsAvailable(enabled); + } else { + *enabled = stereo_playout_; + return 0; + } + } + virtual int32_t StereoRecordingIsAvailable(bool* available) const override { + if (adm_recording_) { + return adm_->StereoRecordingIsAvailable(available); + } else { + *available = true; + return 0; + } + } + virtual int32_t SetStereoRecording(bool enable) override { + return adm_recording_ ? adm_->SetStereoRecording(enable) : 0; + } + virtual int32_t StereoRecording(bool* enabled) const override { + if (adm_recording_) { + return adm_->StereoRecording(enabled); + } else { + *enabled = true; + return 0; + } + } + + // Playout delay + virtual int32_t PlayoutDelay(uint16_t* delayMS) const override { + return adm_playout_ ? adm_->PlayoutDelay(delayMS) : 0; + } + + // Only supported on Android. + virtual bool BuiltInAECIsAvailable() const override { + return false; + } + virtual bool BuiltInAGCIsAvailable() const override { + return false; + } + virtual bool BuiltInNSIsAvailable() const override { + return false; + } + + // Enables the built-in audio effects. Only supported on Android. + virtual int32_t EnableBuiltInAEC(bool enable) override { + return 0; + } + virtual int32_t EnableBuiltInAGC(bool enable) override { + return 0; + } + virtual int32_t EnableBuiltInNS(bool enable) override { + return 0; + } + +// Only supported on iOS. +#if defined(WEBRTC_IOS) + virtual int GetPlayoutAudioParameters( + webrtc::AudioParameters* params) const override { + return -1; + } + virtual int GetRecordAudioParameters( + webrtc::AudioParameters* params) const override { + return -1; + } +#endif // WEBRTC_IOS + + private: + rtc::scoped_refptr adm_; + bool adm_recording_; + bool adm_playout_; + webrtc::TaskQueueFactory* task_queue_factory_; + std::function + on_handle_audio_; + std::unique_ptr handle_audio_thread_; + std::atomic_bool handle_audio_thread_stopped_ = {false}; + std::unique_ptr device_buffer_; + std::atomic_bool initialized_ = {false}; + std::atomic_bool is_recording_ = {false}; + std::atomic_bool is_playing_ = {false}; + std::atomic_bool stereo_playout_ = {false}; + std::vector converted_audio_data_; +}; + +} // namespace sora_unity_sdk + +#endif diff --git a/src/unity_camera_capturer.h b/src/unity_camera_capturer.h index 96ede73..3cb6d19 100644 --- a/src/unity_camera_capturer.h +++ b/src/unity_camera_capturer.h @@ -1,142 +1,142 @@ -#ifndef SORA_UNITY_SDK_UNITY_CAMERA_CAPTURER_H_INCLUDED -#define SORA_UNITY_SDK_UNITY_CAMERA_CAPTURER_H_INCLUDED - -// WebRTC -#include -#include -#include -#include -#include -#include -#include - -// sora -#include - -#include "unity_context.h" - -#ifdef SORA_UNITY_SDK_ANDROID -#include -#endif - -namespace sora_unity_sdk { - -struct UnityCameraCapturerConfig : sora::ScalableVideoTrackSourceConfig { - UnityContext* context; - void* unity_camera_texture; - int width; - int height; -}; - -class UnityCameraCapturer : public sora::ScalableVideoTrackSource, - public rtc::VideoSinkInterface { - webrtc::Clock* clock_ = webrtc::Clock::GetRealTimeClock(); - - struct Impl { - virtual ~Impl() {} - virtual bool Init(UnityContext* context, - void* camera_texture, - int width, - int height) = 0; - virtual rtc::scoped_refptr Capture() = 0; - }; - -#ifdef SORA_UNITY_SDK_WINDOWS - class D3D11Impl : public Impl { - UnityContext* context_; - void* camera_texture_; - void* frame_texture_; - int width_; - int height_; - - public: - bool Init(UnityContext* context, - void* camera_texture, - int width, - int height) override; - rtc::scoped_refptr Capture() override; - }; -#endif - -#if defined(SORA_UNITY_SDK_MACOS) || defined(SORA_UNITY_SDK_IOS) - class MetalImpl : public Impl { - UnityContext* context_; - void* camera_texture_; - void* frame_texture_; - int width_; - int height_; - - public: - bool Init(UnityContext* context, - void* camera_texture, - int width, - int height) override; - rtc::scoped_refptr Capture() override; - }; -#endif - -#ifdef SORA_UNITY_SDK_ANDROID - class VulkanImpl : public Impl { - UnityContext* context_; - void* camera_texture_; - VkImage image_ = VK_NULL_HANDLE; - VkDeviceMemory memory_ = VK_NULL_HANDLE; - VkCommandPool pool_ = VK_NULL_HANDLE; - int width_; - int height_; - - public: - ~VulkanImpl() override; - bool Init(UnityContext* context, - void* camera_texture, - int width, - int height) override; - rtc::scoped_refptr Capture() override; - }; -#endif - -#if defined(SORA_UNITY_SDK_ANDROID) || defined(SORA_UNITY_SDK_UBUNTU) - class OpenglImpl : public Impl { - UnityContext* context_; - void* camera_texture_; - int width_; - int height_; - unsigned int fbo_ = 0; - bool initialized_ = false; - - public: - ~OpenglImpl() override; - bool Init(UnityContext* context, - void* camera_texture, - int width, - int height) override; - rtc::scoped_refptr Capture() override; - }; -#endif - - std::unique_ptr capturer_; - std::mutex mutex_; - bool stopped_ = false; - - public: - static rtc::scoped_refptr Create( - const UnityCameraCapturerConfig& config); - - UnityCameraCapturer(const UnityCameraCapturerConfig& config); - - void OnRender(); - - void Stop(); - - void OnFrame(const webrtc::VideoFrame& frame) override; - - private: - bool Init(UnityContext* context, - void* unity_camera_texture, - int width, - int height); -}; - -} // namespace sora_unity_sdk - -#endif +#ifndef SORA_UNITY_SDK_UNITY_CAMERA_CAPTURER_H_INCLUDED +#define SORA_UNITY_SDK_UNITY_CAMERA_CAPTURER_H_INCLUDED + +// WebRTC +#include +#include +#include +#include +#include +#include +#include + +// sora +#include + +#include "unity_context.h" + +#ifdef SORA_UNITY_SDK_ANDROID +#include +#endif + +namespace sora_unity_sdk { + +struct UnityCameraCapturerConfig : sora::ScalableVideoTrackSourceConfig { + UnityContext* context; + void* unity_camera_texture; + int width; + int height; +}; + +class UnityCameraCapturer : public sora::ScalableVideoTrackSource, + public rtc::VideoSinkInterface { + webrtc::Clock* clock_ = webrtc::Clock::GetRealTimeClock(); + + struct Impl { + virtual ~Impl() {} + virtual bool Init(UnityContext* context, + void* camera_texture, + int width, + int height) = 0; + virtual rtc::scoped_refptr Capture() = 0; + }; + +#ifdef SORA_UNITY_SDK_WINDOWS + class D3D11Impl : public Impl { + UnityContext* context_; + void* camera_texture_; + void* frame_texture_; + int width_; + int height_; + + public: + bool Init(UnityContext* context, + void* camera_texture, + int width, + int height) override; + rtc::scoped_refptr Capture() override; + }; +#endif + +#if defined(SORA_UNITY_SDK_MACOS) || defined(SORA_UNITY_SDK_IOS) + class MetalImpl : public Impl { + UnityContext* context_; + void* camera_texture_; + void* frame_texture_; + int width_; + int height_; + + public: + bool Init(UnityContext* context, + void* camera_texture, + int width, + int height) override; + rtc::scoped_refptr Capture() override; + }; +#endif + +#ifdef SORA_UNITY_SDK_ANDROID + class VulkanImpl : public Impl { + UnityContext* context_; + void* camera_texture_; + VkImage image_ = VK_NULL_HANDLE; + VkDeviceMemory memory_ = VK_NULL_HANDLE; + VkCommandPool pool_ = VK_NULL_HANDLE; + int width_; + int height_; + + public: + ~VulkanImpl() override; + bool Init(UnityContext* context, + void* camera_texture, + int width, + int height) override; + rtc::scoped_refptr Capture() override; + }; +#endif + +#if defined(SORA_UNITY_SDK_ANDROID) || defined(SORA_UNITY_SDK_UBUNTU) + class OpenglImpl : public Impl { + UnityContext* context_; + void* camera_texture_; + int width_; + int height_; + unsigned int fbo_ = 0; + bool initialized_ = false; + + public: + ~OpenglImpl() override; + bool Init(UnityContext* context, + void* camera_texture, + int width, + int height) override; + rtc::scoped_refptr Capture() override; + }; +#endif + + std::unique_ptr capturer_; + std::mutex mutex_; + bool stopped_ = false; + + public: + static rtc::scoped_refptr Create( + const UnityCameraCapturerConfig& config); + + UnityCameraCapturer(const UnityCameraCapturerConfig& config); + + void OnRender(); + + void Stop(); + + void OnFrame(const webrtc::VideoFrame& frame) override; + + private: + bool Init(UnityContext* context, + void* unity_camera_texture, + int width, + int height); +}; + +} // namespace sora_unity_sdk + +#endif diff --git a/src/unity_context.h b/src/unity_context.h index 920d9e8..cbe5e63 100644 --- a/src/unity_context.h +++ b/src/unity_context.h @@ -1,54 +1,54 @@ -#ifndef SORA_UNITY_SDK_UNITY_CONTEXT_H_INCLUDED -#define SORA_UNITY_SDK_UNITY_CONTEXT_H_INCLUDED - -#include - -// webrtc -#include "rtc_base/log_sinks.h" - -#include "unity/IUnityGraphics.h" -#include "unity/IUnityInterface.h" - -#ifdef SORA_UNITY_SDK_WINDOWS -#include "unity/IUnityGraphicsD3D11.h" -#endif - -namespace sora_unity_sdk { - -class UnityContext { - std::mutex mutex_; - std::unique_ptr log_sink_; - IUnityInterfaces* ifs_ = nullptr; - IUnityGraphics* graphics_ = nullptr; - - private: - // Unity のプラグインイベント - static void UNITY_INTERFACE_API - OnGraphicsDeviceEventStatic(UnityGfxDeviceEventType eventType); - - void UNITY_INTERFACE_API - OnGraphicsDeviceEvent(UnityGfxDeviceEventType eventType); - - public: - static UnityContext& Instance(); - - bool IsInitialized(); - void Init(IUnityInterfaces* ifs); - void Shutdown(); - - IUnityInterfaces* GetInterfaces(); - -#ifdef SORA_UNITY_SDK_WINDOWS - private: - ID3D11Device* device_ = nullptr; - ID3D11DeviceContext* context_ = nullptr; - - public: - ID3D11Device* GetDevice(); - ID3D11DeviceContext* GetDeviceContext(); -#endif -}; - -} // namespace sora_unity_sdk - -#endif +#ifndef SORA_UNITY_SDK_UNITY_CONTEXT_H_INCLUDED +#define SORA_UNITY_SDK_UNITY_CONTEXT_H_INCLUDED + +#include + +// webrtc +#include "rtc_base/log_sinks.h" + +#include "unity/IUnityGraphics.h" +#include "unity/IUnityInterface.h" + +#ifdef SORA_UNITY_SDK_WINDOWS +#include "unity/IUnityGraphicsD3D11.h" +#endif + +namespace sora_unity_sdk { + +class UnityContext { + std::mutex mutex_; + std::unique_ptr log_sink_; + IUnityInterfaces* ifs_ = nullptr; + IUnityGraphics* graphics_ = nullptr; + + private: + // Unity のプラグインイベント + static void UNITY_INTERFACE_API + OnGraphicsDeviceEventStatic(UnityGfxDeviceEventType eventType); + + void UNITY_INTERFACE_API + OnGraphicsDeviceEvent(UnityGfxDeviceEventType eventType); + + public: + static UnityContext& Instance(); + + bool IsInitialized(); + void Init(IUnityInterfaces* ifs); + void Shutdown(); + + IUnityInterfaces* GetInterfaces(); + +#ifdef SORA_UNITY_SDK_WINDOWS + private: + ID3D11Device* device_ = nullptr; + ID3D11DeviceContext* context_ = nullptr; + + public: + ID3D11Device* GetDevice(); + ID3D11DeviceContext* GetDeviceContext(); +#endif +}; + +} // namespace sora_unity_sdk + +#endif diff --git a/src/unity_renderer.h b/src/unity_renderer.h index 48e0e1e..0349e43 100644 --- a/src/unity_renderer.h +++ b/src/unity_renderer.h @@ -1,54 +1,54 @@ -#ifndef SORA_UNITY_SDK_UNITY_RENDERER_H_INCLUDED -#define SORA_UNITY_SDK_UNITY_RENDERER_H_INCLUDED - -// webrtc -#include -#include -#include -#include -#include - -// sora -#include "id_pointer.h" -#include "unity/IUnityRenderingExtensions.h" - -namespace sora_unity_sdk { - -class UnityRenderer { - public: - class Sink : public rtc::VideoSinkInterface { - rtc::scoped_refptr track_; - ptrid_t ptrid_; - std::mutex mutex_; - rtc::scoped_refptr frame_buffer_; - uint8_t* temp_buf_ = nullptr; - - public: - Sink(webrtc::VideoTrackInterface* track); - ~Sink(); - ptrid_t GetSinkID() const; - - private: - rtc::scoped_refptr GetFrameBuffer(); - void SetFrameBuffer(rtc::scoped_refptr v); - - public: - void OnFrame(const webrtc::VideoFrame& frame) override; - static void UNITY_INTERFACE_API TextureUpdateCallback(int eventID, - void* data); - }; - - private: - typedef std::vector< - std::pair>> - VideoSinkVector; - VideoSinkVector sinks_; - - public: - ptrid_t AddTrack(webrtc::VideoTrackInterface* track); - ptrid_t RemoveTrack(webrtc::VideoTrackInterface* track); -}; - -} // namespace sora_unity_sdk - -#endif +#ifndef SORA_UNITY_SDK_UNITY_RENDERER_H_INCLUDED +#define SORA_UNITY_SDK_UNITY_RENDERER_H_INCLUDED + +// webrtc +#include +#include +#include +#include +#include + +// sora +#include "id_pointer.h" +#include "unity/IUnityRenderingExtensions.h" + +namespace sora_unity_sdk { + +class UnityRenderer { + public: + class Sink : public rtc::VideoSinkInterface { + rtc::scoped_refptr track_; + ptrid_t ptrid_; + std::mutex mutex_; + rtc::scoped_refptr frame_buffer_; + uint8_t* temp_buf_ = nullptr; + + public: + Sink(webrtc::VideoTrackInterface* track); + ~Sink(); + ptrid_t GetSinkID() const; + + private: + rtc::scoped_refptr GetFrameBuffer(); + void SetFrameBuffer(rtc::scoped_refptr v); + + public: + void OnFrame(const webrtc::VideoFrame& frame) override; + static void UNITY_INTERFACE_API TextureUpdateCallback(int eventID, + void* data); + }; + + private: + typedef std::vector< + std::pair>> + VideoSinkVector; + VideoSinkVector sinks_; + + public: + ptrid_t AddTrack(webrtc::VideoTrackInterface* track); + ptrid_t RemoveTrack(webrtc::VideoTrackInterface* track); +}; + +} // namespace sora_unity_sdk + +#endif From 5f056d56b93e1c39be18dddfe220c733f9ef4956 Mon Sep 17 00:00:00 2001 From: melpon Date: Mon, 3 Oct 2022 12:02:42 +0900 Subject: [PATCH 05/14] =?UTF-8?q?CHANGES=20=E6=9B=B4=E6=96=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGES.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGES.md b/CHANGES.md index fe0de5a..a8177f4 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -11,6 +11,9 @@ ## develop +- [FIX] UnityCameraCapturer がマルチスレッド下で正常に終了しないことがあるのを修正 + - @melpon + ## 2022.5.1 (2022-09-24) - [UPDATE] Sora C++ SDK を `2022.12.1` に上げる From eca287f2b4f8a2ac8b685bdeeb7871d6efa46dd9 Mon Sep 17 00:00:00 2001 From: miosakuma Date: Tue, 4 Oct 2022 16:40:30 +0900 Subject: [PATCH 06/14] =?UTF-8?q?Unity=20SDK=202022.5.2=20=E3=83=AA?= =?UTF-8?q?=E3=83=AA=E3=83=BC=E3=82=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGES.md | 2 ++ VERSION | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGES.md b/CHANGES.md index a8177f4..91653e1 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -11,6 +11,8 @@ ## develop +## 2022.5.2 (2022-10-04) + - [FIX] UnityCameraCapturer がマルチスレッド下で正常に終了しないことがあるのを修正 - @melpon diff --git a/VERSION b/VERSION index 6778077..0e825aa 100644 --- a/VERSION +++ b/VERSION @@ -1,4 +1,4 @@ -SORA_UNITY_SDK_VERSION=2022.5.1 +SORA_UNITY_SDK_VERSION=2022.5.2 SORA_CPP_SDK_VERSION=2022.12.1 WEBRTC_BUILD_VERSION=m105.5195.0.0 BOOST_VERSION=1.80.0 From 25a1d137a4d31c838ded1a8108653237ed72378d Mon Sep 17 00:00:00 2001 From: torikizikaeru <51085972+torikizi@users.noreply.github.com> Date: Sat, 22 Oct 2022 14:24:04 +0900 Subject: [PATCH 07/14] =?UTF-8?q?=20Sora=20C++=20SDK=20=E3=82=92=202022.14?= =?UTF-8?q?.0=20=E3=81=AB=E4=B8=8A=E3=81=92=E3=82=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION b/VERSION index 0e825aa..d5ddfa7 100644 --- a/VERSION +++ b/VERSION @@ -1,5 +1,5 @@ SORA_UNITY_SDK_VERSION=2022.5.2 -SORA_CPP_SDK_VERSION=2022.12.1 +SORA_CPP_SDK_VERSION=2022.14.0 WEBRTC_BUILD_VERSION=m105.5195.0.0 BOOST_VERSION=1.80.0 CMAKE_VERSION=3.23.1 From 337f6a751123d8439d9c9f0d3c0083c6a48cc2e7 Mon Sep 17 00:00:00 2001 From: voluntas Date: Mon, 24 Oct 2022 13:35:21 +0900 Subject: [PATCH 08/14] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index b8ec572..fd48643 100644 --- a/README.md +++ b/README.md @@ -97,7 +97,7 @@ Please read https://github.com/shiguredo/oss/blob/master/README.en.md before use ## Sora Unity SDK for MS Hololens2 -- support/hololens2 ブランチの修正や改善は有償でお受けしています +- [support/hololens2](https://github.com/shiguredo/sora-unity-sdk/tree/support/hololens2) ブランチの修正や改善は有償でお受けしています - 継続的な有償サポートは提供しておりません ## 有償での優先実装 From bc82254bb82b9d7c22fb00d56822385799b3ff13 Mon Sep 17 00:00:00 2001 From: voluntas Date: Mon, 31 Oct 2022 20:03:07 +0900 Subject: [PATCH 09/14] Update README.md --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index fd48643..1c2b939 100644 --- a/README.md +++ b/README.md @@ -112,6 +112,8 @@ Please read https://github.com/shiguredo/oss/blob/master/README.en.md before use - [株式会社KDDIテクノロジー](https://kddi-tech.com/) 様 - Android OpenGL ES 対応 - 企業名非公開 +- Microsoft HoloLens 2 対応 + - [NTTコノキュー](https://www.nttqonoq.com/) 様 ## 有償での優先実装が可能な機能一覧 @@ -159,3 +161,4 @@ See the License for the specific language governing permissions and limitations under the License. ``` + From 424dab66d3b62524e59e85d6465e4f3af6303414 Mon Sep 17 00:00:00 2001 From: melpon Date: Thu, 24 Nov 2022 23:55:45 +0900 Subject: [PATCH 10/14] =?UTF-8?q?=E3=82=BD=E3=83=95=E3=83=88=E3=82=A6?= =?UTF-8?q?=E3=82=A7=E3=82=A2=E3=83=9F=E3=83=A5=E3=83=BC=E3=83=88=E3=82=92?= =?UTF-8?q?=E5=AE=9F=E8=A3=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Sora/Sora.cs | 20 ++++++++++++++++++++ src/sora.cpp | 28 ++++++++++++++++++++++++++++ src/sora.h | 5 +++++ src/unity.cpp | 17 +++++++++++++++++ src/unity.h | 5 +++++ 5 files changed, 75 insertions(+) diff --git a/Sora/Sora.cs b/Sora/Sora.cs index 381b4ac..0617d81 100644 --- a/Sora/Sora.cs +++ b/Sora/Sora.cs @@ -652,6 +652,18 @@ public static bool IsH264Supported() return sora_is_h264_supported() != 0; } + public bool AudioEnabled + { + get { return sora_get_audio_enabled(p) != 0; } + set { sora_set_audio_enabled(p, value ? 1 : 0); } + } + + public bool VideoEnabled + { + get { return sora_get_video_enabled(p) != 0; } + set { sora_set_video_enabled(p, value ? 1 : 0); } + } + #if UNITY_IOS && !UNITY_EDITOR private const string DllName = "__Internal"; #else @@ -706,4 +718,12 @@ public static bool IsH264Supported() private static extern int sora_device_enum_audio_playout(DeviceEnumCallbackDelegate f, IntPtr userdata); [DllImport(DllName)] private static extern int sora_is_h264_supported(); + [DllImport(DllName)] + private static extern int sora_get_audio_enabled(IntPtr p); + [DllImport(DllName)] + private static extern void sora_set_audio_enabled(IntPtr p, int enabled); + [DllImport(DllName)] + private static extern int sora_get_video_enabled(IntPtr p); + [DllImport(DllName)] + private static extern void sora_set_video_enabled(IntPtr p, int enabled); } diff --git a/src/sora.cpp b/src/sora.cpp index 19641c0..0ae827e 100644 --- a/src/sora.cpp +++ b/src/sora.cpp @@ -804,4 +804,32 @@ void Sora::PushEvent(std::function f) { event_queue_.push_back(std::move(f)); } +bool Sora::GetAudioEnabled() const { + if (audio_track_ == nullptr) { + return false; + } + return audio_track_->enabled(); +} + +void Sora::SetAudioEnabled(bool enabled) { + if (audio_track_ == nullptr) { + return; + } + audio_track_->set_enabled(enabled); +} + +bool Sora::GetVideoEnabled() const { + if (video_track_ == nullptr) { + return false; + } + return video_track_->enabled(); +} + +void Sora::SetVideoEnabled(bool enabled) { + if (video_track_ == nullptr) { + return; + } + video_track_->set_enabled(enabled); +} + } // namespace sora_unity_sdk diff --git a/src/sora.h b/src/sora.h index fbfba84..acfd5b6 100644 --- a/src/sora.h +++ b/src/sora.h @@ -65,6 +65,11 @@ class Sora : public std::enable_shared_from_this, void SendMessage(const std::string& label, const std::string& data); + bool GetAudioEnabled() const; + void SetAudioEnabled(bool enabled); + bool GetVideoEnabled() const; + void SetVideoEnabled(bool enabled); + private: void* GetAndroidApplicationContext(void* env); static sora_conf::ErrorCode ToErrorCode(sora::SoraSignalingErrorCode ec); diff --git a/src/unity.cpp b/src/unity.cpp index 1112420..6ceed5c 100644 --- a/src/unity.cpp +++ b/src/unity.cpp @@ -212,6 +212,23 @@ unity_bool_t sora_is_h264_supported() { #endif } +unity_bool_t sora_get_audio_enabled(void* p) { + auto wsora = (SoraWrapper*)p; + return wsora->sora->GetAudioEnabled(); +} +void sora_set_audio_enabled(void* p, unity_bool_t enabled) { + auto wsora = (SoraWrapper*)p; + wsora->sora->SetAudioEnabled(enabled); +} +unity_bool_t sora_get_video_enabled(void* p) { + auto wsora = (SoraWrapper*)p; + return wsora->sora->GetVideoEnabled(); +} +void sora_set_video_enabled(void* p, unity_bool_t enabled) { + auto wsora = (SoraWrapper*)p; + wsora->sora->SetVideoEnabled(enabled); +} + // iOS の場合は static link で名前が被る可能性があるので、別の名前にしておく void UNITY_INTERFACE_EXPORT UNITY_INTERFACE_API #if defined(SORA_UNITY_SDK_IOS) diff --git a/src/unity.h b/src/unity.h index e1b5213..a851701 100644 --- a/src/unity.h +++ b/src/unity.h @@ -94,6 +94,11 @@ UNITY_INTERFACE_EXPORT unity_bool_t sora_device_enum_audio_playout(device_enum_cb_t f, void* userdata); UNITY_INTERFACE_EXPORT unity_bool_t sora_is_h264_supported(); +UNITY_INTERFACE_EXPORT unity_bool_t sora_get_audio_enabled(void* p); +UNITY_INTERFACE_EXPORT void sora_set_audio_enabled(void* p, unity_bool_t enabled); +UNITY_INTERFACE_EXPORT unity_bool_t sora_get_video_enabled(void* p); +UNITY_INTERFACE_EXPORT void sora_set_video_enabled(void* p, unity_bool_t enabled); + #ifdef __cplusplus } #endif From 8fe0b0e1c818786583b70cb8577ed6f48974b6c4 Mon Sep 17 00:00:00 2001 From: miosakuma Date: Tue, 6 Dec 2022 10:33:38 +0900 Subject: [PATCH 11/14] =?UTF-8?q?=E5=A4=89=E6=9B=B4=E5=B1=A5=E6=AD=B4?= =?UTF-8?q?=E3=81=AE=E6=9B=B4=E6=96=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGES.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGES.md b/CHANGES.md index 91653e1..64598d1 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -11,6 +11,9 @@ ## develop +- [ADD] 実行時に音声と映像のミュート、ミュート解除をする機能を追加 + - @melpon + ## 2022.5.2 (2022-10-04) - [FIX] UnityCameraCapturer がマルチスレッド下で正常に終了しないことがあるのを修正 From b93298afbde38c0a08be090fe450a380034ebe7b Mon Sep 17 00:00:00 2001 From: miosakuma Date: Tue, 6 Dec 2022 21:35:07 +0900 Subject: [PATCH 12/14] =?UTF-8?q?Sora=20C++=20SDK=20=E3=82=92=202022.12.1?= =?UTF-8?q?=20=E3=81=AB=E6=88=BB=E3=81=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION b/VERSION index d5ddfa7..0e825aa 100644 --- a/VERSION +++ b/VERSION @@ -1,5 +1,5 @@ SORA_UNITY_SDK_VERSION=2022.5.2 -SORA_CPP_SDK_VERSION=2022.14.0 +SORA_CPP_SDK_VERSION=2022.12.1 WEBRTC_BUILD_VERSION=m105.5195.0.0 BOOST_VERSION=1.80.0 CMAKE_VERSION=3.23.1 From 14b7d1fdf2bbe6df2b1141abb176fc3fb27bb668 Mon Sep 17 00:00:00 2001 From: miosakuma Date: Thu, 8 Dec 2022 10:37:09 +0900 Subject: [PATCH 13/14] =?UTF-8?q?README=20=E6=9B=B4=E6=96=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 1c2b939..b003740 100644 --- a/README.md +++ b/README.md @@ -38,7 +38,7 @@ Please read https://github.com/shiguredo/oss/blob/master/README.en.md before use ## システム条件 -- WebRTC SFU Sora 2022.1.1 以降 +- WebRTC SFU Sora 2022.1.3 以降 ## 対応プラットフォーム From dbc3445a45b89787419fe8c413d17a2cc0993d1c Mon Sep 17 00:00:00 2001 From: miosakuma Date: Thu, 8 Dec 2022 10:41:14 +0900 Subject: [PATCH 14/14] =?UTF-8?q?Unity=20SDK=202022.6.0=20=E3=83=AA?= =?UTF-8?q?=E3=83=AA=E3=83=BC=E3=82=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGES.md | 2 ++ VERSION | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGES.md b/CHANGES.md index 64598d1..67b9e6b 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -11,6 +11,8 @@ ## develop +## 2022.6.0 (2022-12-08) + - [ADD] 実行時に音声と映像のミュート、ミュート解除をする機能を追加 - @melpon diff --git a/VERSION b/VERSION index 0e825aa..33b8d97 100644 --- a/VERSION +++ b/VERSION @@ -1,4 +1,4 @@ -SORA_UNITY_SDK_VERSION=2022.5.2 +SORA_UNITY_SDK_VERSION=2022.6.0 SORA_CPP_SDK_VERSION=2022.12.1 WEBRTC_BUILD_VERSION=m105.5195.0.0 BOOST_VERSION=1.80.0