From a4e72af37a51345de11a44d71de1f87d3c551de0 Mon Sep 17 00:00:00 2001 From: KirisameMarisa Date: Sun, 18 Feb 2024 15:19:15 +0900 Subject: [PATCH 01/21] add web rtc --- Application/BocchiTracker.WPF.sln | 96 ++++++++++++++- Application/Models/Config/Config.csproj | 1 + .../Models/Config/Configs/ProjectConfig.cs | 9 ++ .../Models/Config/Configs/UserConfig.cs | 13 +++ Application/Models/Config/GameCaptureType.cs | 16 +++ .../GameCaptureRTC/CaptureFrameStorage.cs | 44 +++++++ .../GameCaptureRTC/GameCaptureRTC.csproj | 26 +++++ Application/Models/GameCaptureRTC/Module.cs | 16 +++ .../Protocol/ICaptureProtocol.cs | 17 +++ .../GameCaptureRTC/Protocol/OBSCapture.cs | 35 ++++++ .../Models/GameCaptureRTC/Protocol/WebRTC.cs | 110 ++++++++++++++++++ .../GameCaptureRTC/RecordingController.cs | 60 ++++++++++ .../ImageProcessorAsync.csproj | 18 --- .../Models/IssueAssetCollector/AssetData.cs | 1 - .../Handlers/Movie/MovieHandler.cs | 21 ++++ .../Handlers/Movie/OBSStudioHandler.cs | 22 ++++ .../Handlers/Movie/WebRTCHandler.cs | 91 +++++++++++++++ .../Screenshot/RemoteScreenshotHandler.cs | 1 - .../ImageProcessor.cs | 5 +- .../IssueAssetCollector.csproj | 7 +- .../Models/ModelEvent/GameCaptureEvent.cs | 36 ++++++ .../BocchiTracker - Backup.WebRTCTest.csproj | 19 +++ .../BocchiTracker.WebRTCTest.csproj | 27 +++++ .../BocchiTracker.WebRTCTest.csproj.user | 4 + .../BocchiTracker.WebRTCTest.sln | 31 +++++ .../Tests/BocchiTracker.WebRTCTest/Program.cs | 50 ++++++++ .../BocchiTracker.Client.csproj | 1 - 27 files changed, 748 insertions(+), 29 deletions(-) create mode 100644 Application/Models/Config/GameCaptureType.cs create mode 100644 Application/Models/GameCaptureRTC/CaptureFrameStorage.cs create mode 100644 Application/Models/GameCaptureRTC/GameCaptureRTC.csproj create mode 100644 Application/Models/GameCaptureRTC/Module.cs create mode 100644 Application/Models/GameCaptureRTC/Protocol/ICaptureProtocol.cs create mode 100644 Application/Models/GameCaptureRTC/Protocol/OBSCapture.cs create mode 100644 Application/Models/GameCaptureRTC/Protocol/WebRTC.cs create mode 100644 Application/Models/GameCaptureRTC/RecordingController.cs delete mode 100644 Application/Models/ImageProcessorAsync/ImageProcessorAsync.csproj create mode 100644 Application/Models/IssueAssetCollector/Handlers/Movie/MovieHandler.cs create mode 100644 Application/Models/IssueAssetCollector/Handlers/Movie/OBSStudioHandler.cs create mode 100644 Application/Models/IssueAssetCollector/Handlers/Movie/WebRTCHandler.cs rename Application/Models/{ImageProcessorAsync => IssueAssetCollector}/ImageProcessor.cs (94%) create mode 100644 Application/Models/ModelEvent/GameCaptureEvent.cs create mode 100644 Application/Tests/BocchiTracker.WebRTCTest/BocchiTracker - Backup.WebRTCTest.csproj create mode 100644 Application/Tests/BocchiTracker.WebRTCTest/BocchiTracker.WebRTCTest.csproj create mode 100644 Application/Tests/BocchiTracker.WebRTCTest/BocchiTracker.WebRTCTest.csproj.user create mode 100644 Application/Tests/BocchiTracker.WebRTCTest/BocchiTracker.WebRTCTest.sln create mode 100644 Application/Tests/BocchiTracker.WebRTCTest/Program.cs diff --git a/Application/BocchiTracker.WPF.sln b/Application/BocchiTracker.WPF.sln index 90f27e2..a7b5ef3 100644 --- a/Application/BocchiTracker.WPF.sln +++ b/Application/BocchiTracker.WPF.sln @@ -28,6 +28,9 @@ EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ProcessLinkQuery", "Models\ProcessLinkQuery\ProcessLinkQuery.csproj", "{98D7A38A-C3D8-4047-9172-674317FE78CF}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BocchiTracker.Client", "WPF\BocchiTracker.Client\BocchiTracker.Client.csproj", "{949ED95B-18C1-43B3-984D-9144D967C57B}" + ProjectSection(ProjectDependencies) = postProject + {ADEDB772-6FFB-4198-9107-1AD791899679} = {ADEDB772-6FFB-4198-9107-1AD791899679} + EndProjectSection EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "WPF", "WPF", "{12AAC0C3-970D-43F6-BD47-0B662B7157F6}" EndProject @@ -43,82 +46,162 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ServiceClientData", "Models EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BocchiTracker.Client.Share", "WPF\BocchiTracker.Client.Share\BocchiTracker.Client.Share.csproj", "{D214D1D6-7DBA-4F53-B070-9672BA3F7CBA}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ImageProcessorAsync", "Models\ImageProcessorAsync\ImageProcessorAsync.csproj", "{ADB878D3-01EC-4FFD-94D9-4348E1689605}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "GameCaptureRTC", "Models\GameCaptureRTC\GameCaptureRTC.csproj", "{ADEDB772-6FFB-4198-9107-1AD791899679}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BocchiTracker.WebRTCTest", "Tests\BocchiTracker.WebRTCTest\BocchiTracker.WebRTCTest.csproj", "{B3675F7D-DF3F-4F0E-A9E0-9575381BC964}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU + Debug|x64 = Debug|x64 Release|Any CPU = Release|Any CPU + Release|x64 = Release|x64 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {54957DCE-658B-4057-82DF-1AE2679DFCEB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {54957DCE-658B-4057-82DF-1AE2679DFCEB}.Debug|Any CPU.Build.0 = Debug|Any CPU + {54957DCE-658B-4057-82DF-1AE2679DFCEB}.Debug|x64.ActiveCfg = Debug|Any CPU + {54957DCE-658B-4057-82DF-1AE2679DFCEB}.Debug|x64.Build.0 = Debug|Any CPU {54957DCE-658B-4057-82DF-1AE2679DFCEB}.Release|Any CPU.ActiveCfg = Release|Any CPU {54957DCE-658B-4057-82DF-1AE2679DFCEB}.Release|Any CPU.Build.0 = Release|Any CPU + {54957DCE-658B-4057-82DF-1AE2679DFCEB}.Release|x64.ActiveCfg = Release|Any CPU + {54957DCE-658B-4057-82DF-1AE2679DFCEB}.Release|x64.Build.0 = Release|Any CPU {1F43C496-63EF-41D8-ADA0-39862677302E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {1F43C496-63EF-41D8-ADA0-39862677302E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {1F43C496-63EF-41D8-ADA0-39862677302E}.Debug|x64.ActiveCfg = Debug|Any CPU + {1F43C496-63EF-41D8-ADA0-39862677302E}.Debug|x64.Build.0 = Debug|Any CPU {1F43C496-63EF-41D8-ADA0-39862677302E}.Release|Any CPU.ActiveCfg = Release|Any CPU {1F43C496-63EF-41D8-ADA0-39862677302E}.Release|Any CPU.Build.0 = Release|Any CPU + {1F43C496-63EF-41D8-ADA0-39862677302E}.Release|x64.ActiveCfg = Release|Any CPU + {1F43C496-63EF-41D8-ADA0-39862677302E}.Release|x64.Build.0 = Release|Any CPU {ECE3A759-0580-47DC-8FD9-17F3A987DA67}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {ECE3A759-0580-47DC-8FD9-17F3A987DA67}.Debug|Any CPU.Build.0 = Debug|Any CPU + {ECE3A759-0580-47DC-8FD9-17F3A987DA67}.Debug|x64.ActiveCfg = Debug|Any CPU + {ECE3A759-0580-47DC-8FD9-17F3A987DA67}.Debug|x64.Build.0 = Debug|Any CPU {ECE3A759-0580-47DC-8FD9-17F3A987DA67}.Release|Any CPU.ActiveCfg = Release|Any CPU {ECE3A759-0580-47DC-8FD9-17F3A987DA67}.Release|Any CPU.Build.0 = Release|Any CPU + {ECE3A759-0580-47DC-8FD9-17F3A987DA67}.Release|x64.ActiveCfg = Release|Any CPU + {ECE3A759-0580-47DC-8FD9-17F3A987DA67}.Release|x64.Build.0 = Release|Any CPU {6E5DBE53-A122-4CF3-8881-3223C2918F2A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {6E5DBE53-A122-4CF3-8881-3223C2918F2A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6E5DBE53-A122-4CF3-8881-3223C2918F2A}.Debug|x64.ActiveCfg = Debug|Any CPU + {6E5DBE53-A122-4CF3-8881-3223C2918F2A}.Debug|x64.Build.0 = Debug|Any CPU {6E5DBE53-A122-4CF3-8881-3223C2918F2A}.Release|Any CPU.ActiveCfg = Release|Any CPU {6E5DBE53-A122-4CF3-8881-3223C2918F2A}.Release|Any CPU.Build.0 = Release|Any CPU + {6E5DBE53-A122-4CF3-8881-3223C2918F2A}.Release|x64.ActiveCfg = Release|Any CPU + {6E5DBE53-A122-4CF3-8881-3223C2918F2A}.Release|x64.Build.0 = Release|Any CPU {7F470DCB-EC9D-44A5-9E66-FAD44223951E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {7F470DCB-EC9D-44A5-9E66-FAD44223951E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7F470DCB-EC9D-44A5-9E66-FAD44223951E}.Debug|x64.ActiveCfg = Debug|Any CPU + {7F470DCB-EC9D-44A5-9E66-FAD44223951E}.Debug|x64.Build.0 = Debug|Any CPU {7F470DCB-EC9D-44A5-9E66-FAD44223951E}.Release|Any CPU.ActiveCfg = Release|Any CPU {7F470DCB-EC9D-44A5-9E66-FAD44223951E}.Release|Any CPU.Build.0 = Release|Any CPU + {7F470DCB-EC9D-44A5-9E66-FAD44223951E}.Release|x64.ActiveCfg = Release|Any CPU + {7F470DCB-EC9D-44A5-9E66-FAD44223951E}.Release|x64.Build.0 = Release|Any CPU {CA925BF6-4D4A-450E-98FC-F54B06662E54}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {CA925BF6-4D4A-450E-98FC-F54B06662E54}.Debug|Any CPU.Build.0 = Debug|Any CPU + {CA925BF6-4D4A-450E-98FC-F54B06662E54}.Debug|x64.ActiveCfg = Debug|Any CPU + {CA925BF6-4D4A-450E-98FC-F54B06662E54}.Debug|x64.Build.0 = Debug|Any CPU {CA925BF6-4D4A-450E-98FC-F54B06662E54}.Release|Any CPU.ActiveCfg = Release|Any CPU {CA925BF6-4D4A-450E-98FC-F54B06662E54}.Release|Any CPU.Build.0 = Release|Any CPU + {CA925BF6-4D4A-450E-98FC-F54B06662E54}.Release|x64.ActiveCfg = Release|Any CPU + {CA925BF6-4D4A-450E-98FC-F54B06662E54}.Release|x64.Build.0 = Release|Any CPU {FE19C801-2DB0-4166-9516-9F0C40A206AF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {FE19C801-2DB0-4166-9516-9F0C40A206AF}.Debug|Any CPU.Build.0 = Debug|Any CPU + {FE19C801-2DB0-4166-9516-9F0C40A206AF}.Debug|x64.ActiveCfg = Debug|Any CPU + {FE19C801-2DB0-4166-9516-9F0C40A206AF}.Debug|x64.Build.0 = Debug|Any CPU {FE19C801-2DB0-4166-9516-9F0C40A206AF}.Release|Any CPU.ActiveCfg = Release|Any CPU {FE19C801-2DB0-4166-9516-9F0C40A206AF}.Release|Any CPU.Build.0 = Release|Any CPU + {FE19C801-2DB0-4166-9516-9F0C40A206AF}.Release|x64.ActiveCfg = Release|Any CPU + {FE19C801-2DB0-4166-9516-9F0C40A206AF}.Release|x64.Build.0 = Release|Any CPU {B9CF3E8D-4599-40C6-9750-73783203AD2B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {B9CF3E8D-4599-40C6-9750-73783203AD2B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B9CF3E8D-4599-40C6-9750-73783203AD2B}.Debug|x64.ActiveCfg = Debug|Any CPU + {B9CF3E8D-4599-40C6-9750-73783203AD2B}.Debug|x64.Build.0 = Debug|Any CPU {B9CF3E8D-4599-40C6-9750-73783203AD2B}.Release|Any CPU.ActiveCfg = Release|Any CPU {B9CF3E8D-4599-40C6-9750-73783203AD2B}.Release|Any CPU.Build.0 = Release|Any CPU + {B9CF3E8D-4599-40C6-9750-73783203AD2B}.Release|x64.ActiveCfg = Release|Any CPU + {B9CF3E8D-4599-40C6-9750-73783203AD2B}.Release|x64.Build.0 = Release|Any CPU {1C806CC4-1BC0-4AD9-90A5-B7E9BBB462DD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {1C806CC4-1BC0-4AD9-90A5-B7E9BBB462DD}.Debug|Any CPU.Build.0 = Debug|Any CPU + {1C806CC4-1BC0-4AD9-90A5-B7E9BBB462DD}.Debug|x64.ActiveCfg = Debug|Any CPU + {1C806CC4-1BC0-4AD9-90A5-B7E9BBB462DD}.Debug|x64.Build.0 = Debug|Any CPU {1C806CC4-1BC0-4AD9-90A5-B7E9BBB462DD}.Release|Any CPU.ActiveCfg = Release|Any CPU {1C806CC4-1BC0-4AD9-90A5-B7E9BBB462DD}.Release|Any CPU.Build.0 = Release|Any CPU + {1C806CC4-1BC0-4AD9-90A5-B7E9BBB462DD}.Release|x64.ActiveCfg = Release|Any CPU + {1C806CC4-1BC0-4AD9-90A5-B7E9BBB462DD}.Release|x64.Build.0 = Release|Any CPU {98D7A38A-C3D8-4047-9172-674317FE78CF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {98D7A38A-C3D8-4047-9172-674317FE78CF}.Debug|Any CPU.Build.0 = Debug|Any CPU + {98D7A38A-C3D8-4047-9172-674317FE78CF}.Debug|x64.ActiveCfg = Debug|Any CPU + {98D7A38A-C3D8-4047-9172-674317FE78CF}.Debug|x64.Build.0 = Debug|Any CPU {98D7A38A-C3D8-4047-9172-674317FE78CF}.Release|Any CPU.ActiveCfg = Release|Any CPU {98D7A38A-C3D8-4047-9172-674317FE78CF}.Release|Any CPU.Build.0 = Release|Any CPU + {98D7A38A-C3D8-4047-9172-674317FE78CF}.Release|x64.ActiveCfg = Release|Any CPU + {98D7A38A-C3D8-4047-9172-674317FE78CF}.Release|x64.Build.0 = Release|Any CPU {949ED95B-18C1-43B3-984D-9144D967C57B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {949ED95B-18C1-43B3-984D-9144D967C57B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {949ED95B-18C1-43B3-984D-9144D967C57B}.Debug|x64.ActiveCfg = Debug|Any CPU + {949ED95B-18C1-43B3-984D-9144D967C57B}.Debug|x64.Build.0 = Debug|Any CPU {949ED95B-18C1-43B3-984D-9144D967C57B}.Release|Any CPU.ActiveCfg = Release|Any CPU {949ED95B-18C1-43B3-984D-9144D967C57B}.Release|Any CPU.Build.0 = Release|Any CPU + {949ED95B-18C1-43B3-984D-9144D967C57B}.Release|x64.ActiveCfg = Release|Any CPU + {949ED95B-18C1-43B3-984D-9144D967C57B}.Release|x64.Build.0 = Release|Any CPU {30DFA2A5-1CCB-4AF2-B49C-78D3774260E2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {30DFA2A5-1CCB-4AF2-B49C-78D3774260E2}.Debug|Any CPU.Build.0 = Debug|Any CPU + {30DFA2A5-1CCB-4AF2-B49C-78D3774260E2}.Debug|x64.ActiveCfg = Debug|Any CPU + {30DFA2A5-1CCB-4AF2-B49C-78D3774260E2}.Debug|x64.Build.0 = Debug|Any CPU {30DFA2A5-1CCB-4AF2-B49C-78D3774260E2}.Release|Any CPU.ActiveCfg = Release|Any CPU {30DFA2A5-1CCB-4AF2-B49C-78D3774260E2}.Release|Any CPU.Build.0 = Release|Any CPU + {30DFA2A5-1CCB-4AF2-B49C-78D3774260E2}.Release|x64.ActiveCfg = Release|Any CPU + {30DFA2A5-1CCB-4AF2-B49C-78D3774260E2}.Release|x64.Build.0 = Release|Any CPU {ABF54AFD-B968-4600-A0E1-5364021E78D4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {ABF54AFD-B968-4600-A0E1-5364021E78D4}.Debug|Any CPU.Build.0 = Debug|Any CPU + {ABF54AFD-B968-4600-A0E1-5364021E78D4}.Debug|x64.ActiveCfg = Debug|Any CPU + {ABF54AFD-B968-4600-A0E1-5364021E78D4}.Debug|x64.Build.0 = Debug|Any CPU {ABF54AFD-B968-4600-A0E1-5364021E78D4}.Release|Any CPU.ActiveCfg = Release|Any CPU {ABF54AFD-B968-4600-A0E1-5364021E78D4}.Release|Any CPU.Build.0 = Release|Any CPU + {ABF54AFD-B968-4600-A0E1-5364021E78D4}.Release|x64.ActiveCfg = Release|Any CPU + {ABF54AFD-B968-4600-A0E1-5364021E78D4}.Release|x64.Build.0 = Release|Any CPU {E703C033-82F7-4294-B0CE-ACF9703FE0B6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {E703C033-82F7-4294-B0CE-ACF9703FE0B6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E703C033-82F7-4294-B0CE-ACF9703FE0B6}.Debug|x64.ActiveCfg = Debug|Any CPU + {E703C033-82F7-4294-B0CE-ACF9703FE0B6}.Debug|x64.Build.0 = Debug|Any CPU {E703C033-82F7-4294-B0CE-ACF9703FE0B6}.Release|Any CPU.ActiveCfg = Release|Any CPU {E703C033-82F7-4294-B0CE-ACF9703FE0B6}.Release|Any CPU.Build.0 = Release|Any CPU + {E703C033-82F7-4294-B0CE-ACF9703FE0B6}.Release|x64.ActiveCfg = Release|Any CPU + {E703C033-82F7-4294-B0CE-ACF9703FE0B6}.Release|x64.Build.0 = Release|Any CPU {84D6A2DD-327D-4CF4-AC94-8DF9A910E7A9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {84D6A2DD-327D-4CF4-AC94-8DF9A910E7A9}.Debug|Any CPU.Build.0 = Debug|Any CPU + {84D6A2DD-327D-4CF4-AC94-8DF9A910E7A9}.Debug|x64.ActiveCfg = Debug|Any CPU + {84D6A2DD-327D-4CF4-AC94-8DF9A910E7A9}.Debug|x64.Build.0 = Debug|Any CPU {84D6A2DD-327D-4CF4-AC94-8DF9A910E7A9}.Release|Any CPU.ActiveCfg = Release|Any CPU {84D6A2DD-327D-4CF4-AC94-8DF9A910E7A9}.Release|Any CPU.Build.0 = Release|Any CPU + {84D6A2DD-327D-4CF4-AC94-8DF9A910E7A9}.Release|x64.ActiveCfg = Release|Any CPU + {84D6A2DD-327D-4CF4-AC94-8DF9A910E7A9}.Release|x64.Build.0 = Release|Any CPU {D214D1D6-7DBA-4F53-B070-9672BA3F7CBA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {D214D1D6-7DBA-4F53-B070-9672BA3F7CBA}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D214D1D6-7DBA-4F53-B070-9672BA3F7CBA}.Debug|x64.ActiveCfg = Debug|Any CPU + {D214D1D6-7DBA-4F53-B070-9672BA3F7CBA}.Debug|x64.Build.0 = Debug|Any CPU {D214D1D6-7DBA-4F53-B070-9672BA3F7CBA}.Release|Any CPU.ActiveCfg = Release|Any CPU {D214D1D6-7DBA-4F53-B070-9672BA3F7CBA}.Release|Any CPU.Build.0 = Release|Any CPU - {ADB878D3-01EC-4FFD-94D9-4348E1689605}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {ADB878D3-01EC-4FFD-94D9-4348E1689605}.Debug|Any CPU.Build.0 = Debug|Any CPU - {ADB878D3-01EC-4FFD-94D9-4348E1689605}.Release|Any CPU.ActiveCfg = Release|Any CPU - {ADB878D3-01EC-4FFD-94D9-4348E1689605}.Release|Any CPU.Build.0 = Release|Any CPU + {D214D1D6-7DBA-4F53-B070-9672BA3F7CBA}.Release|x64.ActiveCfg = Release|Any CPU + {D214D1D6-7DBA-4F53-B070-9672BA3F7CBA}.Release|x64.Build.0 = Release|Any CPU + {ADEDB772-6FFB-4198-9107-1AD791899679}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {ADEDB772-6FFB-4198-9107-1AD791899679}.Debug|Any CPU.Build.0 = Debug|Any CPU + {ADEDB772-6FFB-4198-9107-1AD791899679}.Debug|x64.ActiveCfg = Debug|Any CPU + {ADEDB772-6FFB-4198-9107-1AD791899679}.Debug|x64.Build.0 = Debug|Any CPU + {ADEDB772-6FFB-4198-9107-1AD791899679}.Release|Any CPU.ActiveCfg = Release|Any CPU + {ADEDB772-6FFB-4198-9107-1AD791899679}.Release|Any CPU.Build.0 = Release|Any CPU + {ADEDB772-6FFB-4198-9107-1AD791899679}.Release|x64.ActiveCfg = Release|Any CPU + {ADEDB772-6FFB-4198-9107-1AD791899679}.Release|x64.Build.0 = Release|Any CPU + {B3675F7D-DF3F-4F0E-A9E0-9575381BC964}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B3675F7D-DF3F-4F0E-A9E0-9575381BC964}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B3675F7D-DF3F-4F0E-A9E0-9575381BC964}.Debug|x64.ActiveCfg = Debug|x64 + {B3675F7D-DF3F-4F0E-A9E0-9575381BC964}.Debug|x64.Build.0 = Debug|x64 + {B3675F7D-DF3F-4F0E-A9E0-9575381BC964}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B3675F7D-DF3F-4F0E-A9E0-9575381BC964}.Release|Any CPU.Build.0 = Release|Any CPU + {B3675F7D-DF3F-4F0E-A9E0-9575381BC964}.Release|x64.ActiveCfg = Release|x64 + {B3675F7D-DF3F-4F0E-A9E0-9575381BC964}.Release|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -141,7 +224,8 @@ Global {E703C033-82F7-4294-B0CE-ACF9703FE0B6} = {677ECDC0-9125-4E30-8B8C-E0CC5F98DFF5} {84D6A2DD-327D-4CF4-AC94-8DF9A910E7A9} = {677ECDC0-9125-4E30-8B8C-E0CC5F98DFF5} {D214D1D6-7DBA-4F53-B070-9672BA3F7CBA} = {12AAC0C3-970D-43F6-BD47-0B662B7157F6} - {ADB878D3-01EC-4FFD-94D9-4348E1689605} = {677ECDC0-9125-4E30-8B8C-E0CC5F98DFF5} + {ADEDB772-6FFB-4198-9107-1AD791899679} = {677ECDC0-9125-4E30-8B8C-E0CC5F98DFF5} + {B3675F7D-DF3F-4F0E-A9E0-9575381BC964} = {E876F453-952B-4D58-AA0E-1D95DFF58FB5} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {F04B3FFB-34D2-4BBE-85BA-F28DEE1BCAB6} diff --git a/Application/Models/Config/Config.csproj b/Application/Models/Config/Config.csproj index f26cea2..407cd23 100644 --- a/Application/Models/Config/Config.csproj +++ b/Application/Models/Config/Config.csproj @@ -4,6 +4,7 @@ + diff --git a/Application/Models/Config/Configs/ProjectConfig.cs b/Application/Models/Config/Configs/ProjectConfig.cs index 969351b..e1ec303 100644 --- a/Application/Models/Config/Configs/ProjectConfig.cs +++ b/Application/Models/Config/Configs/ProjectConfig.cs @@ -7,6 +7,13 @@ namespace BocchiTracker.Config.Configs { + public class CaptureSetting + { + public string? FFmpegPath { get; set; } + + public SIPSorceryMedia.Abstractions.VideoCodecsEnum VideoCodecs { get; set; } = SIPSorceryMedia.Abstractions.VideoCodecsEnum.VP8; + } + public class MonitoredDirectoryConfig { public string? Directory { get; set; } @@ -61,6 +68,8 @@ public class ProjectConfig public string FileSaveDirectory { get; set; } = Path.Combine(Environment.CurrentDirectory, "FileSave"); + public CaptureSetting CaptureSetting { get; set; } = new CaptureSetting(); + public List MonitoredDirectoryConfigs { get; set; } = new List(); public string? CacheDirectory { get; set; } diff --git a/Application/Models/Config/Configs/UserConfig.cs b/Application/Models/Config/Configs/UserConfig.cs index c068401..f6c613c 100644 --- a/Application/Models/Config/Configs/UserConfig.cs +++ b/Application/Models/Config/Configs/UserConfig.cs @@ -7,8 +7,21 @@ namespace BocchiTracker.Config.Configs { + public class UserCaptureSetting + { + public GameCaptureType GameCaptureType { get; set; } = GameCaptureType.NotUse; + + public bool IncludeAudio = false; + + public int RecordingFrameRate { get; set; } = 30; + + public int RecordingMintes { get; set; } = 3; + } + public class UserConfig { + public UserCaptureSetting UserCaptureSetting { get; set; } = new UserCaptureSetting(); + public string? ProjectConfigFilename { get; set; } public bool IsOpenWebBrowser { get; set; } diff --git a/Application/Models/Config/GameCaptureType.cs b/Application/Models/Config/GameCaptureType.cs new file mode 100644 index 0000000..b47598c --- /dev/null +++ b/Application/Models/Config/GameCaptureType.cs @@ -0,0 +1,16 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace BocchiTracker.Config +{ + public enum GameCaptureType + { + OBSStudio, + WebRTC, + + NotUse, + } +} diff --git a/Application/Models/GameCaptureRTC/CaptureFrameStorage.cs b/Application/Models/GameCaptureRTC/CaptureFrameStorage.cs new file mode 100644 index 0000000..fb0dbdb --- /dev/null +++ b/Application/Models/GameCaptureRTC/CaptureFrameStorage.cs @@ -0,0 +1,44 @@ + +using System; +using System.Runtime.InteropServices; + +namespace BocchiTracker.GameCaptureRTC +{ + public class CaptureFrameStorage + { + private int _frameRate = 30; + private int _recordingMaxFrames = 0; + public ModelEvent.CaptureStreamParameter CaptureStreamParameter { get; set; } = new ModelEvent.CaptureStreamParameter(); + + public CaptureFrameStorage(int inFrameRate, int inRecordingMintes) + { + _frameRate = inFrameRate; + _recordingMaxFrames = (inRecordingMintes * 60) * _frameRate; + } + + public void AddFrame(int inWidth, int inHeight, int inStride, nint inData) + { + if (CaptureStreamParameter.Frames.Count > _recordingMaxFrames) + CaptureStreamParameter.Frames.RemoveAt(0); + + // メモリ領域のコピーを作成して渡す + byte[] dataCopy = new byte[inHeight * inStride]; + unsafe + { + byte* src = (byte*)inData; + for (int i = 0; i < dataCopy.Length; i++) + { + dataCopy[i] = *(src + i); + } + } + + CaptureStreamParameter.Frames.Add(new ModelEvent.CaptureStreamParameter.Frame + { + Data = dataCopy, + Width = inWidth, + Height = inHeight, + Stride = inStride, + }); + } + } +} diff --git a/Application/Models/GameCaptureRTC/GameCaptureRTC.csproj b/Application/Models/GameCaptureRTC/GameCaptureRTC.csproj new file mode 100644 index 0000000..da6bc2b --- /dev/null +++ b/Application/Models/GameCaptureRTC/GameCaptureRTC.csproj @@ -0,0 +1,26 @@ + + + + + + True + net7.0-windows7.0 + + + + + + + + + + + + + + + + + + + diff --git a/Application/Models/GameCaptureRTC/Module.cs b/Application/Models/GameCaptureRTC/Module.cs new file mode 100644 index 0000000..b23c08a --- /dev/null +++ b/Application/Models/GameCaptureRTC/Module.cs @@ -0,0 +1,16 @@ +using Prism.Ioc; +using Prism.Modularity; + +namespace BocchiTracker.GameCaptureRTC +{ + public class Module : IModule + { + public void OnInitialized(IContainerProvider containerProvider) {} + + public void RegisterTypes(IContainerRegistry containerRegistry) + { + containerRegistry.RegisterSingleton(typeof(CaptureFrameStorage)); + containerRegistry.RegisterSingleton(typeof(RecordingController)); + } + } +} \ No newline at end of file diff --git a/Application/Models/GameCaptureRTC/Protocol/ICaptureProtocol.cs b/Application/Models/GameCaptureRTC/Protocol/ICaptureProtocol.cs new file mode 100644 index 0000000..48731fe --- /dev/null +++ b/Application/Models/GameCaptureRTC/Protocol/ICaptureProtocol.cs @@ -0,0 +1,17 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace BocchiTracker.GameCaptureRTC.Protocol +{ + public interface ICaptureProtocol + { + void Start(); + + void Stop(); + + bool IsConnect(); + } +} diff --git a/Application/Models/GameCaptureRTC/Protocol/OBSCapture.cs b/Application/Models/GameCaptureRTC/Protocol/OBSCapture.cs new file mode 100644 index 0000000..448d8e8 --- /dev/null +++ b/Application/Models/GameCaptureRTC/Protocol/OBSCapture.cs @@ -0,0 +1,35 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using OBSWebsocketDotNet; + +namespace BocchiTracker.GameCaptureRTC.Protocol +{ + public class OBSCapture : ICaptureProtocol + { + private OBSWebsocket _obsWebSocket; + + public OBSCapture(int inPort, string inCaptureSource) + { + _obsWebSocket = new OBSWebsocket(); + var scenes = _obsWebSocket.ListScenes(); + } + + public bool IsConnect() + { + throw new NotImplementedException(); + } + + public void Start() + { + throw new NotImplementedException(); + } + + public void Stop() + { + throw new NotImplementedException(); + } + } +} diff --git a/Application/Models/GameCaptureRTC/Protocol/WebRTC.cs b/Application/Models/GameCaptureRTC/Protocol/WebRTC.cs new file mode 100644 index 0000000..4936341 --- /dev/null +++ b/Application/Models/GameCaptureRTC/Protocol/WebRTC.cs @@ -0,0 +1,110 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net; +using System.Threading.Tasks; +using WebSocketSharp.Server; +using Prism.Events; +using BocchiTracker.ModelEvent; +using SIPSorcery.Net; +using SIPSorceryMedia.Abstractions; +using SIPSorceryMedia.FFmpeg; +using BocchiTracker.Config.Configs; + +namespace BocchiTracker.GameCaptureRTC.Protocol +{ + public class WebRTC : WebSocketBehavior, ICaptureProtocol, IDisposable + { + private readonly IEventAggregator _eventAggregator; + private WebSocketServer _web_socket; + private CaptureFrameStorage _captureFrameStorage; + private static bool _isConnecting; + + public WebRTC(IEventAggregator inEventAggregator, int inPort, bool inSecure, CaptureSetting inCaptureSetting, UserCaptureSetting inUserCaptureSetting) + { + _eventAggregator = inEventAggregator; + _captureFrameStorage = new CaptureFrameStorage(inUserCaptureSetting.RecordingFrameRate, inUserCaptureSetting.RecordingMintes); + _web_socket = new WebSocketServer(IPAddress.Any, inPort, inSecure); + _web_socket.AddWebSocketService("/", (peer) => peer.CreatePeerConnection = () => CreatePeerConnection(inCaptureSetting, inUserCaptureSetting, _captureFrameStorage)); + _web_socket.Start(); + } + + public void Dispose() + { + _web_socket.Stop(); + } + + public bool IsConnect() + { + return _isConnecting; + } + + public void Start() + { + + } + + public void Stop() + { + _eventAggregator + .GetEvent() + .Publish(new GameCaptureFinishEventParameter { CaptureStreamParameter = _captureFrameStorage.CaptureStreamParameter }); + _captureFrameStorage.CaptureStreamParameter.Frames.Clear(); + } + + private static Task CreatePeerConnection(CaptureSetting inCaptureSetting, UserCaptureSetting inUserCaptureSetting, CaptureFrameStorage inFrameStorage) + { + FFmpegInit.Initialise(FfmpegLogLevelEnum.AV_LOG_VERBOSE, inCaptureSetting.FFmpegPath); + var videoEP = new FFmpegVideoEndPoint(); + videoEP.RestrictFormats(format => format.Codec == inCaptureSetting.VideoCodecs); + + videoEP.OnVideoSinkDecodedSampleFaster += (RawImage rawImage) => + { + if (rawImage.PixelFormat == SIPSorceryMedia.Abstractions.VideoPixelFormatsEnum.Rgb) + { + inFrameStorage.AddFrame((int)rawImage.Width, (int)rawImage.Height, (int)rawImage.Stride, rawImage.Sample, rawImage.PixelFormat.ToString()); + } + }; + + RTCConfiguration config = new RTCConfiguration + { + //iceServers = new List { new RTCIceServer { urls = STUN_URL } } + X_UseRtpFeedbackProfile = true + }; + var pc = new RTCPeerConnection(config); + + if (inUserCaptureSetting.IncludeAudio) + { + MediaStreamTrack audioTrack = new MediaStreamTrack(SDPMediaTypesEnum.audio, false, + new List { new SDPAudioVideoMediaFormat(SDPWellKnownMediaFormatsEnum.PCMU) }, MediaStreamStatusEnum.RecvOnly); + pc.addTrack(audioTrack); + } + MediaStreamTrack videoTrack = new MediaStreamTrack(videoEP.GetVideoSinkFormats(), MediaStreamStatusEnum.RecvOnly); + pc.addTrack(videoTrack); + + pc.OnVideoFrameReceived += videoEP.GotVideoFrame; + pc.OnVideoFormatsNegotiated += (formats) => videoEP.SetVideoSinkFormat(formats.First()); + pc.onconnectionstatechange += async (state) => + { + Console.WriteLine($"Peer connection state change to {state}."); + switch (state) + { + case RTCPeerConnectionState.failed: pc.Close("ice disconnection"); break; + case RTCPeerConnectionState.closed: await videoEP.CloseVideo(); break; + default: break; + } + }; + pc.OnSendReport += (media, sr) => Console.WriteLine($"RTCP Send for {media}\n{sr.GetDebugSummary()}"); + pc.oniceconnectionstatechange += (state) => + { + Console.WriteLine($"ICE connection state change to {state}."); + switch (state) + { + case RTCIceConnectionState.connected: _isConnecting = true; break; + default: _isConnecting = false; break; + } + }; + return Task.FromResult(pc); + } + } +} diff --git a/Application/Models/GameCaptureRTC/RecordingController.cs b/Application/Models/GameCaptureRTC/RecordingController.cs new file mode 100644 index 0000000..f586a1a --- /dev/null +++ b/Application/Models/GameCaptureRTC/RecordingController.cs @@ -0,0 +1,60 @@ +using BocchiTracker.GameCaptureRTC.Protocol; +using BocchiTracker.ModelEvent; +using Prism.Events; + +namespace BocchiTracker.GameCaptureRTC +{ + public class RecordingController : ICaptureProtocol + { + private readonly IEventAggregator _eventAggregator; + + private ICaptureProtocol? _captureProtocol; + + public RecordingController(IEventAggregator inEventAggregator) + { + _eventAggregator = inEventAggregator; + _eventAggregator + .GetEvent() + .Subscribe(OnConfigReload); + } + + public bool IsConnect() + { + if (_captureProtocol == null) + return false; + return _captureProtocol.IsConnect(); + } + + public void Start() + { + _captureProtocol?.Start(); + } + + public void Stop() + { + _captureProtocol?.Stop(); + } + + private void OnConfigReload(ConfigReloadEventParameter inParam) + { + if (inParam.UserConfig == null || inParam.ProjectConfig == null) + return; + + switch (inParam.UserConfig.UserCaptureSetting.GameCaptureType) + { + case Config.GameCaptureType.OBSStudio: + { + _captureProtocol = new Protocol.OBSCapture(inParam.ProjectConfig.Port, ""); + } + break; + case Config.GameCaptureType.WebRTC: + { + _captureProtocol = new Protocol.WebRTC(_eventAggregator, inParam.ProjectConfig.Port, false, inParam.ProjectConfig.CaptureSetting, inParam.UserConfig.UserCaptureSetting); + } + break; + default: + break; + } + } + } +} diff --git a/Application/Models/ImageProcessorAsync/ImageProcessorAsync.csproj b/Application/Models/ImageProcessorAsync/ImageProcessorAsync.csproj deleted file mode 100644 index c612cd5..0000000 --- a/Application/Models/ImageProcessorAsync/ImageProcessorAsync.csproj +++ /dev/null @@ -1,18 +0,0 @@ - - - - net7.0 - enable - enable - - - - - - - - - - - - diff --git a/Application/Models/IssueAssetCollector/AssetData.cs b/Application/Models/IssueAssetCollector/AssetData.cs index 047d683..df9b7cb 100644 --- a/Application/Models/IssueAssetCollector/AssetData.cs +++ b/Application/Models/IssueAssetCollector/AssetData.cs @@ -7,7 +7,6 @@ using SixLabors.ImageSharp.Formats; using System.Threading.Tasks; using System.ComponentModel; -using BocchiTracker.ImageProcessorAsync; using System.Reflection; using System; using static System.Net.Mime.MediaTypeNames; diff --git a/Application/Models/IssueAssetCollector/Handlers/Movie/MovieHandler.cs b/Application/Models/IssueAssetCollector/Handlers/Movie/MovieHandler.cs new file mode 100644 index 0000000..3c1a221 --- /dev/null +++ b/Application/Models/IssueAssetCollector/Handlers/Movie/MovieHandler.cs @@ -0,0 +1,21 @@ +using BocchiTracker.ApplicationInfoCollector; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace BocchiTracker.IssueAssetCollector.Handlers.Movie +{ + public class MovieHandler : IHandle + { + public IFilenameGenerator _filenameGenerator { private set; get; } + + public MovieHandler(IFilenameGenerator inFilenameGenerator) + { + _filenameGenerator = inFilenameGenerator; + } + + public virtual void Handle(AppStatusBundle inAppStatusBundle, int inPID, string inOutput) { } + } +} diff --git a/Application/Models/IssueAssetCollector/Handlers/Movie/OBSStudioHandler.cs b/Application/Models/IssueAssetCollector/Handlers/Movie/OBSStudioHandler.cs new file mode 100644 index 0000000..61f8a80 --- /dev/null +++ b/Application/Models/IssueAssetCollector/Handlers/Movie/OBSStudioHandler.cs @@ -0,0 +1,22 @@ +using BocchiTracker.ApplicationInfoCollector; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace BocchiTracker.IssueAssetCollector.Handlers.Movie +{ + public class OBSStudioHandler : MovieHandler + { + public OBSStudioHandler(IFilenameGenerator inFilenameGenerator) : base(inFilenameGenerator) + { + + } + + public override void Handle(AppStatusBundle inAppStatusBundle, int inPID, string inOutput) + { + + } + } +} diff --git a/Application/Models/IssueAssetCollector/Handlers/Movie/WebRTCHandler.cs b/Application/Models/IssueAssetCollector/Handlers/Movie/WebRTCHandler.cs new file mode 100644 index 0000000..59cd554 --- /dev/null +++ b/Application/Models/IssueAssetCollector/Handlers/Movie/WebRTCHandler.cs @@ -0,0 +1,91 @@ +using BocchiTracker.ApplicationInfoCollector; +using BocchiTracker.ModelEvent; +using Prism.Events; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Reflection; +using System.Text; +using System.Threading.Tasks; +using System.Windows.Media.Media3D; + +namespace BocchiTracker.IssueAssetCollector.Handlers.Movie +{ + public class GameCaptureFrameConvertMovieProcess + { + public string Output { get; set; } = string.Empty; + + private IEventAggregator _eventAggregator; + + public GameCaptureFrameConvertMovieProcess(IEventAggregator inEventAggregator) + { + _eventAggregator = inEventAggregator; + _eventAggregator + .GetEvent() + .Subscribe(Handle, ThreadOption.PublisherThread); + } + + public void Handle(GameCaptureFinishEventParameter inParam) + { + var c = inParam.CaptureStreamParameter; + if (c == null) return; + + int idx = 0; + unsafe + { + string output = Path.Combine(Output, "temp"); + new DirectoryInfo(output).Create(); + + foreach (var frame in c.Frames.ToList()) + { + if (frame.Data == null) continue; + + Span dataSpan = new Span(frame.Data); + using (var image = new Image(frame.Width, frame.Height)) + { + for (int y = 0; y < frame.Height; y++) + { + for (int x = 0; x < frame.Width; x++) + { + var index = y * frame.Stride + x * 3; + var color = new Rgb24(dataSpan[index + 2], dataSpan[index + 1], dataSpan[index]); + image[x, y] = color; + } + } + image.Save(Path.Combine(output, $"{idx++}.png")); + } + } + } + + //!< 連番テクスチャーをここで動画化する + //!< TODO + + //!< ここで連番テクスチャを削除する + //!< TODO + } + } + + public class WebRTCHandler : MovieHandler + { + private IEventAggregator _eventAggregator; + private GameCaptureFrameConvertMovieProcess _convert_movie_process; + + public WebRTCHandler(IEventAggregator inEventAggregator, IFilenameGenerator inFilenameGenerator) : base(inFilenameGenerator) + { + _eventAggregator = inEventAggregator; + _convert_movie_process = new GameCaptureFrameConvertMovieProcess(inEventAggregator); + } + + public override void Handle(AppStatusBundle inAppStatusBundle, int inPID, string inOutput) + { + this._convert_movie_process.Output = Path.Combine(inOutput, _filenameGenerator.Generate(inAppStatusBundle) + ".mp4"); + + _eventAggregator + .GetEvent() + .Publish(new ScreenshotRequestEventParameter(inAppStatusBundle.AppBasicInfo.ClientID)); + } + } +} diff --git a/Application/Models/IssueAssetCollector/Handlers/Screenshot/RemoteScreenshotHandler.cs b/Application/Models/IssueAssetCollector/Handlers/Screenshot/RemoteScreenshotHandler.cs index 8758fc5..b0bd372 100644 --- a/Application/Models/IssueAssetCollector/Handlers/Screenshot/RemoteScreenshotHandler.cs +++ b/Application/Models/IssueAssetCollector/Handlers/Screenshot/RemoteScreenshotHandler.cs @@ -1,5 +1,4 @@ using BocchiTracker.ApplicationInfoCollector; -using BocchiTracker.ImageProcessorAsync; using BocchiTracker.ModelEvent; using BocchiTracker.ProcessLinkQuery.Queries; using Prism.Events; diff --git a/Application/Models/ImageProcessorAsync/ImageProcessor.cs b/Application/Models/IssueAssetCollector/ImageProcessor.cs similarity index 94% rename from Application/Models/ImageProcessorAsync/ImageProcessor.cs rename to Application/Models/IssueAssetCollector/ImageProcessor.cs index e94d702..5493d7f 100644 --- a/Application/Models/ImageProcessorAsync/ImageProcessor.cs +++ b/Application/Models/IssueAssetCollector/ImageProcessor.cs @@ -4,8 +4,11 @@ using SixLabors.ImageSharp; using System.IO; using System.Reflection; +using System.Collections.Generic; +using SixLabors.ImageSharp.PixelFormats; +using System.Threading; -namespace BocchiTracker.ImageProcessorAsync +namespace BocchiTracker.IssueAssetCollector { public class ImageProcessor { diff --git a/Application/Models/IssueAssetCollector/IssueAssetCollector.csproj b/Application/Models/IssueAssetCollector/IssueAssetCollector.csproj index 1c30d86..b413a49 100644 --- a/Application/Models/IssueAssetCollector/IssueAssetCollector.csproj +++ b/Application/Models/IssueAssetCollector/IssueAssetCollector.csproj @@ -2,6 +2,10 @@ + + True + + @@ -12,13 +16,14 @@ + + - diff --git a/Application/Models/ModelEvent/GameCaptureEvent.cs b/Application/Models/ModelEvent/GameCaptureEvent.cs new file mode 100644 index 0000000..638e4e8 --- /dev/null +++ b/Application/Models/ModelEvent/GameCaptureEvent.cs @@ -0,0 +1,36 @@ +using Prism.Events; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace BocchiTracker.ModelEvent +{ + public class OBSStudioParameter + { + public string MoviePath { get; set; } = string.Empty; + } + + public class CaptureStreamParameter + { + public class Frame + { + public int Width { get; set; } + public int Height { get; set; } + public int Stride { get; set; } + public byte[]? Data { get; set; } + } + public List Frames { get; set; } = new List(); + } + + public class GameCaptureFinishEventParameter + { + public CaptureStreamParameter? CaptureStreamParameter { get; set; } + public OBSStudioParameter? OBSStudioParameter { get; set; } + } + + public class GameCaptureStartEvent : PubSubEvent { } + + public class GameCaptureFinishEvent : PubSubEvent { } +} diff --git a/Application/Tests/BocchiTracker.WebRTCTest/BocchiTracker - Backup.WebRTCTest.csproj b/Application/Tests/BocchiTracker.WebRTCTest/BocchiTracker - Backup.WebRTCTest.csproj new file mode 100644 index 0000000..f6d0f85 --- /dev/null +++ b/Application/Tests/BocchiTracker.WebRTCTest/BocchiTracker - Backup.WebRTCTest.csproj @@ -0,0 +1,19 @@ + + + + true + WinExe + disable + net7.0-windows + False + YutoArita + ..\Artifact + + + + + + + + + diff --git a/Application/Tests/BocchiTracker.WebRTCTest/BocchiTracker.WebRTCTest.csproj b/Application/Tests/BocchiTracker.WebRTCTest/BocchiTracker.WebRTCTest.csproj new file mode 100644 index 0000000..b37329e --- /dev/null +++ b/Application/Tests/BocchiTracker.WebRTCTest/BocchiTracker.WebRTCTest.csproj @@ -0,0 +1,27 @@ + + + + true + Exe + disable + net7.0-windows7.0 + False + YutoArita + ..\Artifact + true + AnyCPU;x64 + 7.0 + + + + + + + + + + + + + + diff --git a/Application/Tests/BocchiTracker.WebRTCTest/BocchiTracker.WebRTCTest.csproj.user b/Application/Tests/BocchiTracker.WebRTCTest/BocchiTracker.WebRTCTest.csproj.user new file mode 100644 index 0000000..88a5509 --- /dev/null +++ b/Application/Tests/BocchiTracker.WebRTCTest/BocchiTracker.WebRTCTest.csproj.user @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/Application/Tests/BocchiTracker.WebRTCTest/BocchiTracker.WebRTCTest.sln b/Application/Tests/BocchiTracker.WebRTCTest/BocchiTracker.WebRTCTest.sln new file mode 100644 index 0000000..d941244 --- /dev/null +++ b/Application/Tests/BocchiTracker.WebRTCTest/BocchiTracker.WebRTCTest.sln @@ -0,0 +1,31 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.6.33815.320 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BocchiTracker.WebRTCTest", "BocchiTracker.WebRTCTest.csproj", "{BB1C689F-71B7-4F47-BB65-326816806C84}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Debug|x64 = Debug|x64 + Release|Any CPU = Release|Any CPU + Release|x64 = Release|x64 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {BB1C689F-71B7-4F47-BB65-326816806C84}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {BB1C689F-71B7-4F47-BB65-326816806C84}.Debug|Any CPU.Build.0 = Debug|Any CPU + {BB1C689F-71B7-4F47-BB65-326816806C84}.Debug|x64.ActiveCfg = Debug|x64 + {BB1C689F-71B7-4F47-BB65-326816806C84}.Debug|x64.Build.0 = Debug|x64 + {BB1C689F-71B7-4F47-BB65-326816806C84}.Release|Any CPU.ActiveCfg = Release|Any CPU + {BB1C689F-71B7-4F47-BB65-326816806C84}.Release|Any CPU.Build.0 = Release|Any CPU + {BB1C689F-71B7-4F47-BB65-326816806C84}.Release|x64.ActiveCfg = Release|x64 + {BB1C689F-71B7-4F47-BB65-326816806C84}.Release|x64.Build.0 = Release|x64 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {577FD981-9EE6-49A7-A5BD-274536A0FBAC} + EndGlobalSection +EndGlobal diff --git a/Application/Tests/BocchiTracker.WebRTCTest/Program.cs b/Application/Tests/BocchiTracker.WebRTCTest/Program.cs new file mode 100644 index 0000000..c67c8a1 --- /dev/null +++ b/Application/Tests/BocchiTracker.WebRTCTest/Program.cs @@ -0,0 +1,50 @@ +using BocchiTracker.GameCaptureRTC; +using BocchiTracker.IssueAssetCollector.Handlers.Movie; +using BocchiTracker.ModelEvent; +using Prism.Events; +using System; +using System.Threading; + +namespace BocchiTracker.WebRTCTest +{ + class Program + { + static void Main() + { + var eventAggregator = new EventAggregator(); + var recordingController = new RecordingController(eventAggregator); + var movieSaveProcess = new GameCaptureFrameConvertMovieProcess(eventAggregator); + + eventAggregator.GetEvent().Publish(new ConfigReloadEventParameter + ( + new Config.Configs.ProjectConfig { + Port = 8888, + CaptureSetting = new Config.Configs.CaptureSetting + { + FFmpegPath = @"C:\Users\maris\AppData\Local\Microsoft\WinGet\Packages\Gyan.FFmpeg.Shared_Microsoft.Winget.Source_8wekyb3d8bbwe\ffmpeg-6.1.1-full_build-shared\bin", + VideoCodecs = SIPSorceryMedia.Abstractions.VideoCodecsEnum.VP8 + } + }, + new Config.Configs.UserConfig { + UserCaptureSetting = new Config.Configs.UserCaptureSetting + { + RecordingMintes = 1, + GameCaptureType = Config.GameCaptureType.WebRTC + } + } + )); + + Console.WriteLine("サーバー接続中..."); + while (!recordingController.IsConnect()) + Thread.Sleep(10); + + Console.WriteLine("キャプチャーを開始しました。"); + { + recordingController.Start(); + Thread.Sleep(5000); + recordingController.Stop(); + } + Console.WriteLine("キャプチャーを停止しました。"); + } + } +} \ No newline at end of file diff --git a/Application/WPF/BocchiTracker.Client/BocchiTracker.Client.csproj b/Application/WPF/BocchiTracker.Client/BocchiTracker.Client.csproj index 35faf96..2b7312e 100644 --- a/Application/WPF/BocchiTracker.Client/BocchiTracker.Client.csproj +++ b/Application/WPF/BocchiTracker.Client/BocchiTracker.Client.csproj @@ -19,7 +19,6 @@ - From 570074a1d8a346b5be2c18b3c64f95e4476bdea9 Mon Sep 17 00:00:00 2001 From: KirisameMarisa Date: Sun, 18 Feb 2024 22:54:18 +0900 Subject: [PATCH 02/21] add unity sample scene --- ExternalTools/websocket-sharp | 1 + ...{SampleScene.unity => CreatePackage.unity} | 83 ++- ...ne.unity.meta => CreatePackage.unity.meta} | 0 .../Unity/project/Assets/Scenes/Sample.meta | 8 + .../Scenes/Sample/PlayerCameraController.cs | 83 +++ .../Sample/PlayerCameraController.cs.meta | 11 + .../Assets/Scenes/Sample/Sample3D.unity | 621 ++++++++++++++++++ .../Assets/Scenes/Sample/Sample3D.unity.meta | 7 + .../SceneTemplateSettings.json | 121 ++++ 9 files changed, 931 insertions(+), 4 deletions(-) create mode 160000 ExternalTools/websocket-sharp rename Plugins/Unity/project/Assets/Scenes/{SampleScene.unity => CreatePackage.unity} (72%) rename Plugins/Unity/project/Assets/Scenes/{SampleScene.unity.meta => CreatePackage.unity.meta} (100%) create mode 100644 Plugins/Unity/project/Assets/Scenes/Sample.meta create mode 100644 Plugins/Unity/project/Assets/Scenes/Sample/PlayerCameraController.cs create mode 100644 Plugins/Unity/project/Assets/Scenes/Sample/PlayerCameraController.cs.meta create mode 100644 Plugins/Unity/project/Assets/Scenes/Sample/Sample3D.unity create mode 100644 Plugins/Unity/project/Assets/Scenes/Sample/Sample3D.unity.meta create mode 100644 Plugins/Unity/project/ProjectSettings/SceneTemplateSettings.json diff --git a/ExternalTools/websocket-sharp b/ExternalTools/websocket-sharp new file mode 160000 index 0000000..3c30164 --- /dev/null +++ b/ExternalTools/websocket-sharp @@ -0,0 +1 @@ +Subproject commit 3c30164a81b3dba9be9e7f3cdede9ee382b793cd diff --git a/Plugins/Unity/project/Assets/Scenes/SampleScene.unity b/Plugins/Unity/project/Assets/Scenes/CreatePackage.unity similarity index 72% rename from Plugins/Unity/project/Assets/Scenes/SampleScene.unity rename to Plugins/Unity/project/Assets/Scenes/CreatePackage.unity index 9421266..5aa46ff 100644 --- a/Plugins/Unity/project/Assets/Scenes/SampleScene.unity +++ b/Plugins/Unity/project/Assets/Scenes/CreatePackage.unity @@ -104,7 +104,7 @@ NavMeshSettings: serializedVersion: 2 m_ObjectHideFlags: 0 m_BuildSettings: - serializedVersion: 2 + serializedVersion: 3 agentTypeID: 0 agentRadius: 0.5 agentHeight: 2 @@ -117,12 +117,72 @@ NavMeshSettings: cellSize: 0.16666667 manualTileSize: 0 tileSize: 256 - accuratePlacement: 0 + buildHeightMesh: 0 maxJobWorkers: 0 preserveTilesOutsideBounds: 0 debug: m_Flags: 0 m_NavMeshData: {fileID: 0} +--- !u!1 &141257305 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 141257306} + - component: {fileID: 141257308} + - component: {fileID: 141257307} + m_Layer: 0 + m_Name: BocchiTrackerSystem + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &141257306 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 141257305} + serializedVersion: 2 + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 0} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!114 &141257307 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 141257305} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 185bdc4ad8bc5cd48b48c8ad85703eeb, type: 3} + m_Name: + m_EditorClassIdentifier: + serverAddress: 127.0.0.1 + serverPort: 8888 + screenshotCamera: {fileID: 519420031} +--- !u!114 &141257308 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 141257305} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 76bbb54e572cbe347aa280de5de39ae7, type: 3} + m_Name: + m_EditorClassIdentifier: --- !u!1 &519420028 GameObject: m_ObjectHideFlags: 0 @@ -163,9 +223,17 @@ Camera: m_projectionMatrixMode: 1 m_GateFitMode: 2 m_FOVAxisMode: 0 + m_Iso: 200 + m_ShutterSpeed: 0.005 + m_Aperture: 16 + m_FocusDistance: 10 + m_FocalLength: 50 + m_BladeCount: 5 + m_Curvature: {x: 2, y: 11} + m_BarrelClipping: 0.25 + m_Anamorphism: 0 m_SensorSize: {x: 36, y: 24} m_LensShift: {x: 0, y: 0} - m_FocalLength: 50 m_NormalizedViewPortRect: serializedVersion: 2 x: 0 @@ -199,10 +267,17 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 519420028} + serializedVersion: 2 m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: 0, y: 0, z: -10} m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 0} - m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!1660057539 &9223372036854775807 +SceneRoots: + m_ObjectHideFlags: 0 + m_Roots: + - {fileID: 519420032} + - {fileID: 141257306} diff --git a/Plugins/Unity/project/Assets/Scenes/SampleScene.unity.meta b/Plugins/Unity/project/Assets/Scenes/CreatePackage.unity.meta similarity index 100% rename from Plugins/Unity/project/Assets/Scenes/SampleScene.unity.meta rename to Plugins/Unity/project/Assets/Scenes/CreatePackage.unity.meta diff --git a/Plugins/Unity/project/Assets/Scenes/Sample.meta b/Plugins/Unity/project/Assets/Scenes/Sample.meta new file mode 100644 index 0000000..5dde45e --- /dev/null +++ b/Plugins/Unity/project/Assets/Scenes/Sample.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: eac5aaa441bd74a41969ea29ffea74dd +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Plugins/Unity/project/Assets/Scenes/Sample/PlayerCameraController.cs b/Plugins/Unity/project/Assets/Scenes/Sample/PlayerCameraController.cs new file mode 100644 index 0000000..f3c7efd --- /dev/null +++ b/Plugins/Unity/project/Assets/Scenes/Sample/PlayerCameraController.cs @@ -0,0 +1,83 @@ +using System.Collections; +using System.Collections.Generic; +using UnityEngine; +using UnityEngine.SocialPlatforms; + +public class CameraController : MonoBehaviour +{ + // WASDFO㍶Ëړ + // QEF㏸E~ + // hbOF_̈ړ + + //J̈ړ + [SerializeField, Range(0.1f, 20.0f)] + private float _positionStep = 5.0f; + + //}EXx + [SerializeField, Range(30.0f, 300.0f)] + private float _mouseSensitive = 90.0f; + + //Jtransform + private Transform _camTransform; + + //}EX̎n_ + private Vector3 _startMousePos; + + //J]̎n_ + private Vector3 _presentCamRotation; + private Vector3 _presentCamPos; + + + void Start() + { + _camTransform = this.gameObject.transform; + } + + void Update() + { + CameraRotationMouseControl(); //J̉] }EX + CameraPositionKeyControl(); //J̃[Jړ L[ + } + + + //J̉] }EX + private void CameraRotationMouseControl() + { + /* }EXNbNꂽƂ */ + if (Input.GetMouseButtonDown(0)) + { + _startMousePos = Input.mousePosition; + _presentCamRotation.x = _camTransform.transform.eulerAngles.x; + _presentCamRotation.y = _camTransform.transform.eulerAngles.y; + } + + /* }EXNbNĂ */ + if (Input.GetMouseButton(0)) + { + //(ړJnW - }EX̌ݍW) / 𑜓x ŐK + float x = (_startMousePos.x - Input.mousePosition.x) / Screen.width; + float y = (_startMousePos.y - Input.mousePosition.y) / Screen.height; + + //]Jnpx { }EX̕ω * }EXx + float eulerX = _presentCamRotation.x + y * _mouseSensitive; + float eulerY = _presentCamRotation.y + x * _mouseSensitive; + + _camTransform.rotation = Quaternion.Euler(eulerX, eulerY, 0); + } + } + + //J̃[Jړ L[ + private void CameraPositionKeyControl() + { + Vector3 campos = _camTransform.position; + + if (Input.GetKey(KeyCode.D)) { campos += _camTransform.right * Time.deltaTime * _positionStep; } + if (Input.GetKey(KeyCode.A)) { campos -= _camTransform.right * Time.deltaTime * _positionStep; } + if (Input.GetKey(KeyCode.E)) { campos += _camTransform.up * Time.deltaTime * _positionStep; } + if (Input.GetKey(KeyCode.Q)) { campos -= _camTransform.up * Time.deltaTime * _positionStep; } + if (Input.GetKey(KeyCode.W)) { campos += _camTransform.forward * Time.deltaTime * _positionStep; } + if (Input.GetKey(KeyCode.S)) { campos -= _camTransform.forward * Time.deltaTime * _positionStep; } + + _camTransform.position = campos; + } +} \ No newline at end of file diff --git a/Plugins/Unity/project/Assets/Scenes/Sample/PlayerCameraController.cs.meta b/Plugins/Unity/project/Assets/Scenes/Sample/PlayerCameraController.cs.meta new file mode 100644 index 0000000..3b964ad --- /dev/null +++ b/Plugins/Unity/project/Assets/Scenes/Sample/PlayerCameraController.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 7d23cafb66791614ba960893c4c71932 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Plugins/Unity/project/Assets/Scenes/Sample/Sample3D.unity b/Plugins/Unity/project/Assets/Scenes/Sample/Sample3D.unity new file mode 100644 index 0000000..9010181 --- /dev/null +++ b/Plugins/Unity/project/Assets/Scenes/Sample/Sample3D.unity @@ -0,0 +1,621 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!29 &1 +OcclusionCullingSettings: + m_ObjectHideFlags: 0 + serializedVersion: 2 + m_OcclusionBakeSettings: + smallestOccluder: 5 + smallestHole: 0.25 + backfaceThreshold: 100 + m_SceneGUID: 00000000000000000000000000000000 + m_OcclusionCullingData: {fileID: 0} +--- !u!104 &2 +RenderSettings: + m_ObjectHideFlags: 0 + serializedVersion: 9 + m_Fog: 0 + m_FogColor: {r: 0.5, g: 0.5, b: 0.5, a: 1} + m_FogMode: 3 + m_FogDensity: 0.01 + m_LinearFogStart: 0 + m_LinearFogEnd: 300 + m_AmbientSkyColor: {r: 0.212, g: 0.227, b: 0.259, a: 1} + m_AmbientEquatorColor: {r: 0.114, g: 0.125, b: 0.133, a: 1} + m_AmbientGroundColor: {r: 0.047, g: 0.043, b: 0.035, a: 1} + m_AmbientIntensity: 1 + m_AmbientMode: 0 + m_SubtractiveShadowColor: {r: 0.42, g: 0.478, b: 0.627, a: 1} + m_SkyboxMaterial: {fileID: 10304, guid: 0000000000000000f000000000000000, type: 0} + m_HaloStrength: 0.5 + m_FlareStrength: 1 + m_FlareFadeSpeed: 3 + m_HaloTexture: {fileID: 0} + m_SpotCookie: {fileID: 10001, guid: 0000000000000000e000000000000000, type: 0} + m_DefaultReflectionMode: 0 + m_DefaultReflectionResolution: 128 + m_ReflectionBounces: 1 + m_ReflectionIntensity: 1 + m_CustomReflection: {fileID: 0} + m_Sun: {fileID: 705507994} + m_IndirectSpecularColor: {r: 0.18028352, g: 0.22571376, b: 0.30692244, a: 1} + m_UseRadianceAmbientProbe: 0 +--- !u!157 &3 +LightmapSettings: + m_ObjectHideFlags: 0 + serializedVersion: 12 + m_GIWorkflowMode: 1 + m_GISettings: + serializedVersion: 2 + m_BounceScale: 1 + m_IndirectOutputScale: 1 + m_AlbedoBoost: 1 + m_EnvironmentLightingMode: 0 + m_EnableBakedLightmaps: 1 + m_EnableRealtimeLightmaps: 0 + m_LightmapEditorSettings: + serializedVersion: 12 + m_Resolution: 2 + m_BakeResolution: 40 + m_AtlasSize: 1024 + m_AO: 0 + m_AOMaxDistance: 1 + m_CompAOExponent: 1 + m_CompAOExponentDirect: 0 + m_ExtractAmbientOcclusion: 0 + m_Padding: 2 + m_LightmapParameters: {fileID: 0} + m_LightmapsBakeMode: 1 + m_TextureCompression: 1 + m_FinalGather: 0 + m_FinalGatherFiltering: 1 + m_FinalGatherRayCount: 256 + m_ReflectionCompression: 2 + m_MixedBakeMode: 2 + m_BakeBackend: 1 + m_PVRSampling: 1 + m_PVRDirectSampleCount: 32 + m_PVRSampleCount: 500 + m_PVRBounces: 2 + m_PVREnvironmentSampleCount: 500 + m_PVREnvironmentReferencePointCount: 2048 + m_PVRFilteringMode: 2 + m_PVRDenoiserTypeDirect: 0 + m_PVRDenoiserTypeIndirect: 0 + m_PVRDenoiserTypeAO: 0 + m_PVRFilterTypeDirect: 0 + m_PVRFilterTypeIndirect: 0 + m_PVRFilterTypeAO: 0 + m_PVREnvironmentMIS: 0 + m_PVRCulling: 1 + m_PVRFilteringGaussRadiusDirect: 1 + m_PVRFilteringGaussRadiusIndirect: 5 + m_PVRFilteringGaussRadiusAO: 2 + m_PVRFilteringAtrousPositionSigmaDirect: 0.5 + m_PVRFilteringAtrousPositionSigmaIndirect: 2 + m_PVRFilteringAtrousPositionSigmaAO: 1 + m_ExportTrainingData: 0 + m_TrainingDataDestination: TrainingData + m_LightProbeSampleCountMultiplier: 4 + m_LightingDataAsset: {fileID: 112000000, guid: 6d15c3e866e22d249983ac3debb11460, type: 2} + m_LightingSettings: {fileID: 0} +--- !u!196 &4 +NavMeshSettings: + serializedVersion: 2 + m_ObjectHideFlags: 0 + m_BuildSettings: + serializedVersion: 3 + agentTypeID: 0 + agentRadius: 0.5 + agentHeight: 2 + agentSlope: 45 + agentClimb: 0.4 + ledgeDropHeight: 0 + maxJumpAcrossDistance: 0 + minRegionArea: 2 + manualCellSize: 0 + cellSize: 0.16666667 + manualTileSize: 0 + tileSize: 256 + buildHeightMesh: 0 + maxJobWorkers: 0 + preserveTilesOutsideBounds: 0 + debug: + m_Flags: 0 + m_NavMeshData: {fileID: 0} +--- !u!1 &705507993 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 705507995} + - component: {fileID: 705507994} + m_Layer: 0 + m_Name: Directional Light + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!108 &705507994 +Light: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 705507993} + m_Enabled: 1 + serializedVersion: 10 + m_Type: 1 + m_Shape: 0 + m_Color: {r: 1, g: 0.95686275, b: 0.8392157, a: 1} + m_Intensity: 1 + m_Range: 10 + m_SpotAngle: 30 + m_InnerSpotAngle: 21.80208 + m_CookieSize: 10 + m_Shadows: + m_Type: 2 + m_Resolution: -1 + m_CustomResolution: -1 + m_Strength: 1 + m_Bias: 0.05 + m_NormalBias: 0.4 + m_NearPlane: 0.2 + m_CullingMatrixOverride: + e00: 1 + e01: 0 + e02: 0 + e03: 0 + e10: 0 + e11: 1 + e12: 0 + e13: 0 + e20: 0 + e21: 0 + e22: 1 + e23: 0 + e30: 0 + e31: 0 + e32: 0 + e33: 1 + m_UseCullingMatrixOverride: 0 + m_Cookie: {fileID: 0} + m_DrawHalo: 0 + m_Flare: {fileID: 0} + m_RenderMode: 0 + m_CullingMask: + serializedVersion: 2 + m_Bits: 4294967295 + m_RenderingLayerMask: 1 + m_Lightmapping: 1 + m_LightShadowCasterMode: 0 + m_AreaSize: {x: 1, y: 1} + m_BounceIntensity: 1 + m_ColorTemperature: 6570 + m_UseColorTemperature: 0 + m_BoundingSphereOverride: {x: 0, y: 0, z: 0, w: 0} + m_UseBoundingSphereOverride: 0 + m_UseViewFrustumForShadowCasterCull: 1 + m_ShadowRadius: 0 + m_ShadowAngle: 0 +--- !u!4 &705507995 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 705507993} + serializedVersion: 2 + m_LocalRotation: {x: 0.40821788, y: -0.23456968, z: 0.10938163, w: 0.8754261} + m_LocalPosition: {x: 0, y: 3, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 0} + m_LocalEulerAnglesHint: {x: 50, y: -30, z: 0} +--- !u!1 &963194225 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 963194228} + - component: {fileID: 963194227} + - component: {fileID: 963194226} + - component: {fileID: 963194229} + m_Layer: 0 + m_Name: Main Camera + m_TagString: MainCamera + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!81 &963194226 +AudioListener: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 963194225} + m_Enabled: 1 +--- !u!20 &963194227 +Camera: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 963194225} + m_Enabled: 1 + serializedVersion: 2 + m_ClearFlags: 1 + m_BackGroundColor: {r: 0.19215687, g: 0.3019608, b: 0.4745098, a: 0} + m_projectionMatrixMode: 1 + m_GateFitMode: 2 + m_FOVAxisMode: 0 + m_Iso: 200 + m_ShutterSpeed: 0.005 + m_Aperture: 16 + m_FocusDistance: 10 + m_FocalLength: 50 + m_BladeCount: 5 + m_Curvature: {x: 2, y: 11} + m_BarrelClipping: 0.25 + m_Anamorphism: 0 + m_SensorSize: {x: 36, y: 24} + m_LensShift: {x: 0, y: 0} + m_NormalizedViewPortRect: + serializedVersion: 2 + x: 0 + y: 0 + width: 1 + height: 1 + near clip plane: 0.3 + far clip plane: 1000 + field of view: 60 + orthographic: 0 + orthographic size: 5 + m_Depth: -1 + m_CullingMask: + serializedVersion: 2 + m_Bits: 4294967295 + m_RenderingPath: -1 + m_TargetTexture: {fileID: 0} + m_TargetDisplay: 0 + m_TargetEye: 3 + m_HDR: 1 + m_AllowMSAA: 1 + m_AllowDynamicResolution: 0 + m_ForceIntoRT: 0 + m_OcclusionCulling: 1 + m_StereoConvergence: 10 + m_StereoSeparation: 0.022 +--- !u!4 &963194228 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 963194225} + serializedVersion: 2 + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 2.27, y: 1, z: -6.73} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 0} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!114 &963194229 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 963194225} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 7d23cafb66791614ba960893c4c71932, type: 3} + m_Name: + m_EditorClassIdentifier: + _positionStep: 5 + _mouseSensitive: 90 +--- !u!1 &1060528352 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1060528355} + - component: {fileID: 1060528354} + - component: {fileID: 1060528353} + m_Layer: 0 + m_Name: BocchiTrackerSystem + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!114 &1060528353 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1060528352} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 185bdc4ad8bc5cd48b48c8ad85703eeb, type: 3} + m_Name: + m_EditorClassIdentifier: + serverAddress: 127.0.0.1 + serverPort: 8888 + captureCamera: {fileID: 963194227} +--- !u!114 &1060528354 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1060528352} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 76bbb54e572cbe347aa280de5de39ae7, type: 3} + m_Name: + m_EditorClassIdentifier: +--- !u!4 &1060528355 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1060528352} + serializedVersion: 2 + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 0} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!1 &1494285074 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1494285079} + - component: {fileID: 1494285078} + - component: {fileID: 1494285077} + - component: {fileID: 1494285076} + - component: {fileID: 1494285080} + m_Layer: 0 + m_Name: Player + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!65 &1494285076 +BoxCollider: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1494285074} + m_Material: {fileID: 0} + m_IncludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_ExcludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_LayerOverridePriority: 0 + m_IsTrigger: 0 + m_ProvidesContacts: 0 + m_Enabled: 1 + serializedVersion: 3 + m_Size: {x: 1, y: 1, z: 1} + m_Center: {x: 0, y: 0, z: 0} +--- !u!23 &1494285077 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1494285074} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2100000, guid: aec180434ccd5f640b7f0fe17e84545d, type: 2} + m_StaticBatchInfo: + firstSubMesh: 0 + subMeshCount: 0 + m_StaticBatchRoot: {fileID: 0} + m_ProbeAnchor: {fileID: 0} + m_LightProbeVolumeOverride: {fileID: 0} + m_ScaleInLightmap: 1 + m_ReceiveGI: 1 + m_PreserveUVs: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 1 + m_SelectedEditorRenderState: 3 + m_MinimumChartSize: 4 + m_AutoUVMaxDistance: 0.5 + m_AutoUVMaxAngle: 89 + m_LightmapParameters: {fileID: 0} + m_SortingLayerID: 0 + m_SortingLayer: 0 + m_SortingOrder: 0 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!33 &1494285078 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1494285074} + m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0} +--- !u!4 &1494285079 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1494285074} + serializedVersion: 2 + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 0} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!114 &1494285080 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1494285074} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 0533936b3531aba4e8ed4b9ee28c1e28, type: 3} + m_Name: + m_EditorClassIdentifier: + distanceThreshold: 100 + stage: +--- !u!1 &1638225779 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1638225783} + - component: {fileID: 1638225782} + - component: {fileID: 1638225781} + - component: {fileID: 1638225780} + m_Layer: 0 + m_Name: Ground + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!64 &1638225780 +MeshCollider: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1638225779} + m_Material: {fileID: 0} + m_IncludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_ExcludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_LayerOverridePriority: 0 + m_IsTrigger: 0 + m_ProvidesContacts: 0 + m_Enabled: 1 + serializedVersion: 5 + m_Convex: 0 + m_CookingOptions: 30 + m_Mesh: {fileID: 10209, guid: 0000000000000000e000000000000000, type: 0} +--- !u!23 &1638225781 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1638225779} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 10303, guid: 0000000000000000f000000000000000, type: 0} + m_StaticBatchInfo: + firstSubMesh: 0 + subMeshCount: 0 + m_StaticBatchRoot: {fileID: 0} + m_ProbeAnchor: {fileID: 0} + m_LightProbeVolumeOverride: {fileID: 0} + m_ScaleInLightmap: 1 + m_ReceiveGI: 1 + m_PreserveUVs: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 1 + m_SelectedEditorRenderState: 3 + m_MinimumChartSize: 4 + m_AutoUVMaxDistance: 0.5 + m_AutoUVMaxAngle: 89 + m_LightmapParameters: {fileID: 0} + m_SortingLayerID: 0 + m_SortingLayer: 0 + m_SortingOrder: 0 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!33 &1638225782 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1638225779} + m_Mesh: {fileID: 10209, guid: 0000000000000000e000000000000000, type: 0} +--- !u!4 &1638225783 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1638225779} + serializedVersion: 2 + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 10, y: 1, z: 10} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 0} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!1660057539 &9223372036854775807 +SceneRoots: + m_ObjectHideFlags: 0 + m_Roots: + - {fileID: 963194228} + - {fileID: 705507995} + - {fileID: 1638225783} + - {fileID: 1494285079} + - {fileID: 1060528355} diff --git a/Plugins/Unity/project/Assets/Scenes/Sample/Sample3D.unity.meta b/Plugins/Unity/project/Assets/Scenes/Sample/Sample3D.unity.meta new file mode 100644 index 0000000..636035c --- /dev/null +++ b/Plugins/Unity/project/Assets/Scenes/Sample/Sample3D.unity.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 341a59395c668a441a811125658e0304 +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Plugins/Unity/project/ProjectSettings/SceneTemplateSettings.json b/Plugins/Unity/project/ProjectSettings/SceneTemplateSettings.json new file mode 100644 index 0000000..5e97f83 --- /dev/null +++ b/Plugins/Unity/project/ProjectSettings/SceneTemplateSettings.json @@ -0,0 +1,121 @@ +{ + "templatePinStates": [], + "dependencyTypeInfos": [ + { + "userAdded": false, + "type": "UnityEngine.AnimationClip", + "defaultInstantiationMode": 0 + }, + { + "userAdded": false, + "type": "UnityEditor.Animations.AnimatorController", + "defaultInstantiationMode": 0 + }, + { + "userAdded": false, + "type": "UnityEngine.AnimatorOverrideController", + "defaultInstantiationMode": 0 + }, + { + "userAdded": false, + "type": "UnityEditor.Audio.AudioMixerController", + "defaultInstantiationMode": 0 + }, + { + "userAdded": false, + "type": "UnityEngine.ComputeShader", + "defaultInstantiationMode": 1 + }, + { + "userAdded": false, + "type": "UnityEngine.Cubemap", + "defaultInstantiationMode": 0 + }, + { + "userAdded": false, + "type": "UnityEngine.GameObject", + "defaultInstantiationMode": 0 + }, + { + "userAdded": false, + "type": "UnityEditor.LightingDataAsset", + "defaultInstantiationMode": 0 + }, + { + "userAdded": false, + "type": "UnityEngine.LightingSettings", + "defaultInstantiationMode": 0 + }, + { + "userAdded": false, + "type": "UnityEngine.Material", + "defaultInstantiationMode": 0 + }, + { + "userAdded": false, + "type": "UnityEditor.MonoScript", + "defaultInstantiationMode": 1 + }, + { + "userAdded": false, + "type": "UnityEngine.PhysicMaterial", + "defaultInstantiationMode": 0 + }, + { + "userAdded": false, + "type": "UnityEngine.PhysicsMaterial2D", + "defaultInstantiationMode": 0 + }, + { + "userAdded": false, + "type": "UnityEngine.Rendering.PostProcessing.PostProcessProfile", + "defaultInstantiationMode": 0 + }, + { + "userAdded": false, + "type": "UnityEngine.Rendering.PostProcessing.PostProcessResources", + "defaultInstantiationMode": 0 + }, + { + "userAdded": false, + "type": "UnityEngine.Rendering.VolumeProfile", + "defaultInstantiationMode": 0 + }, + { + "userAdded": false, + "type": "UnityEditor.SceneAsset", + "defaultInstantiationMode": 1 + }, + { + "userAdded": false, + "type": "UnityEngine.Shader", + "defaultInstantiationMode": 1 + }, + { + "userAdded": false, + "type": "UnityEngine.ShaderVariantCollection", + "defaultInstantiationMode": 1 + }, + { + "userAdded": false, + "type": "UnityEngine.Texture", + "defaultInstantiationMode": 0 + }, + { + "userAdded": false, + "type": "UnityEngine.Texture2D", + "defaultInstantiationMode": 0 + }, + { + "userAdded": false, + "type": "UnityEngine.Timeline.TimelineAsset", + "defaultInstantiationMode": 0 + } + ], + "defaultDependencyTypeInfo": { + "userAdded": false, + "type": "", + "defaultInstantiationMode": 1 + }, + "newSceneOverride": 0 +} \ No newline at end of file From 5ebbf61fa0558a041951f1be555ab317e2391273 Mon Sep 17 00:00:00 2001 From: KirisameMarisa Date: Sun, 18 Feb 2024 22:55:11 +0900 Subject: [PATCH 03/21] add to build script. * websocket-sharp --- .../BuildScripts/build_websocket_csharp.py | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 ExternalTools/BuildScripts/build_websocket_csharp.py diff --git a/ExternalTools/BuildScripts/build_websocket_csharp.py b/ExternalTools/BuildScripts/build_websocket_csharp.py new file mode 100644 index 0000000..7fc9828 --- /dev/null +++ b/ExternalTools/BuildScripts/build_websocket_csharp.py @@ -0,0 +1,17 @@ +from pathlib import Path +import subprocess +import shutil +import config + +def Build(): + print("Build C# websocket-sharp") + flatbuffers_csproj_path = config.cWebSocketSharpPath / "websocket-sharp" / "websocket-sharp.csproj" + subprocess.call(["dotnet", "build", flatbuffers_csproj_path.resolve(), "-c", "Release"], cwd=config.cFlatBuffersPath) + + print("Copying flatbuffers to Unity directory...") + artifact = config.cWebSocketSharpPath / "websocket-sharp" / "bin" / "Release" / "netstandard2.0" + unity_external_packages_path = config.cUnityPath / "project" / "Assets" / "BocchiTracker" / "ExternalPackages" / "websocket-sharp" + shutil.copytree(artifact, unity_external_packages_path.resolve(), dirs_exist_ok=True) + +if __name__ == '__main__': + Build() \ No newline at end of file From e1a67b1a8237522bad46097b50f41daa7559d176 Mon Sep 17 00:00:00 2001 From: KirisameMarisa Date: Sun, 18 Feb 2024 22:58:07 +0900 Subject: [PATCH 04/21] add websocket-sharp meta --- .../ExternalPackages/websocket-sharp.meta | 8 +++++ .../websocket-sharp.deps.json.meta | 7 ++++ .../websocket-sharp/websocket-sharp.dll.meta | 33 +++++++++++++++++++ 3 files changed, 48 insertions(+) create mode 100644 Plugins/Unity/project/Assets/BocchiTracker/ExternalPackages/websocket-sharp.meta create mode 100644 Plugins/Unity/project/Assets/BocchiTracker/ExternalPackages/websocket-sharp/websocket-sharp.deps.json.meta create mode 100644 Plugins/Unity/project/Assets/BocchiTracker/ExternalPackages/websocket-sharp/websocket-sharp.dll.meta diff --git a/Plugins/Unity/project/Assets/BocchiTracker/ExternalPackages/websocket-sharp.meta b/Plugins/Unity/project/Assets/BocchiTracker/ExternalPackages/websocket-sharp.meta new file mode 100644 index 0000000..5ffcd20 --- /dev/null +++ b/Plugins/Unity/project/Assets/BocchiTracker/ExternalPackages/websocket-sharp.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: f88a14eb7abc99447bfc038ea4c37b2b +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Plugins/Unity/project/Assets/BocchiTracker/ExternalPackages/websocket-sharp/websocket-sharp.deps.json.meta b/Plugins/Unity/project/Assets/BocchiTracker/ExternalPackages/websocket-sharp/websocket-sharp.deps.json.meta new file mode 100644 index 0000000..a22b4ae --- /dev/null +++ b/Plugins/Unity/project/Assets/BocchiTracker/ExternalPackages/websocket-sharp/websocket-sharp.deps.json.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 4a908c5d082b59342b1ea4921eb55905 +TextScriptImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Plugins/Unity/project/Assets/BocchiTracker/ExternalPackages/websocket-sharp/websocket-sharp.dll.meta b/Plugins/Unity/project/Assets/BocchiTracker/ExternalPackages/websocket-sharp/websocket-sharp.dll.meta new file mode 100644 index 0000000..41b5f38 --- /dev/null +++ b/Plugins/Unity/project/Assets/BocchiTracker/ExternalPackages/websocket-sharp/websocket-sharp.dll.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 721ec68a33968c14d93482986ff8ca64 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 1 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + Windows Store Apps: WindowsStoreApps + second: + enabled: 0 + settings: + CPU: AnyCPU + userData: + assetBundleName: + assetBundleVariant: From 4e3eb0ac71ea1042318812ef94a90b6a09fed8c2 Mon Sep 17 00:00:00 2001 From: KirisameMarisa Date: Sun, 18 Feb 2024 22:59:44 +0900 Subject: [PATCH 05/21] add bocchi tracker video capture module --- .../Runtime/BocchiTrackerVideoCapture.cs | 211 ++++++++++++++++++ .../Runtime/BocchiTrackerVideoCapture.cs.meta | 11 + 2 files changed, 222 insertions(+) create mode 100644 Plugins/Unity/project/Assets/BocchiTracker/Runtime/BocchiTrackerVideoCapture.cs create mode 100644 Plugins/Unity/project/Assets/BocchiTracker/Runtime/BocchiTrackerVideoCapture.cs.meta diff --git a/Plugins/Unity/project/Assets/BocchiTracker/Runtime/BocchiTrackerVideoCapture.cs b/Plugins/Unity/project/Assets/BocchiTracker/Runtime/BocchiTrackerVideoCapture.cs new file mode 100644 index 0000000..72a8d73 --- /dev/null +++ b/Plugins/Unity/project/Assets/BocchiTracker/Runtime/BocchiTrackerVideoCapture.cs @@ -0,0 +1,211 @@ +using System.Collections; +using System.Collections.Generic; +using UnityEngine; +using Unity.WebRTC; +using WebSocketSharp; +using Unity.Plastic.Newtonsoft.Json; + +public class BocchiTrackerVideoCapture : MonoBehaviour +{ + [System.Serializable] + public class RTCMessage + { + public string type; + public string candidate; + public string sdp; + public string sdpMid; + public int sdpMLineIndex; + } + + private WebSocket webSocket; + + private RenderTexture cameraTexture; + + private RTCPeerConnection rtcPeerConnection; + + private Queue messageQueue = new Queue(); + + private bool isProcessing = false; + + public string ServerAddr { get; set; } + + public Camera CaptureCamera { get; set; } + + private void Start() + { + CreatePeerConnection(); + StartCoroutine(ConnectServerTask()); + } + + private void Update() + { + if (!isProcessing && messageQueue.Count > 0) + { + isProcessing = true; + StartCoroutine(ProcessMessages()); + } + } + + private void LateUpdate() + { + StartCoroutine(OnCapture()); + } + + void OnDestroy() + { + if (rtcPeerConnection != null) + { + foreach (var transceiver in rtcPeerConnection.GetTransceivers()) + { + rtcPeerConnection.RemoveTrack(transceiver.Sender); + } + + // Close datachannel befor offer + rtcPeerConnection.Close(); + rtcPeerConnection = null; + } + + if (webSocket != null) + { + webSocket.Close(); + webSocket = null; + } + } + + private IEnumerator ProcessMessages() + { + while (messageQueue.Count > 0) + { + string message = messageQueue.Dequeue(); + yield return MessageProcesser(message); + } + isProcessing = false; + } + + private void CreatePeerConnection() + { + Debug.LogWarning("create new peerConnection start"); + + RTCConfiguration config = default; + rtcPeerConnection = new RTCPeerConnection(ref config); + rtcPeerConnection.OnIceConnectionChange = (state) => { Debug.LogWarning($"OnIceConnectionChange {state}"); }; + rtcPeerConnection.OnIceGatheringStateChange = (state) => { Debug.LogWarning($"OnIceGatheringStateChange {state}"); }; + rtcPeerConnection.OnConnectionStateChange = (state) => { Debug.LogWarning($"OnConnectionStateChange {state}"); }; + rtcPeerConnection.OnIceCandidate = (ev) => + { + Debug.LogWarning(ev.Candidate); + if (ev.Candidate != null) + { + var data = JsonConvert.SerializeObject(new Dictionary + { + { "candidate", ev.Candidate } + }); + webSocket.Send(data); + } + }; + + cameraTexture = new RenderTexture(Screen.width, Screen.height, 32, RenderTextureFormat.BGRA32); + var track = new VideoStreamTrack(cameraTexture); + rtcPeerConnection.AddTrack(track); + { + var codecs = RTCRtpSender.GetCapabilities(TrackKind.Video).codecs; + foreach (var transceiver in rtcPeerConnection.GetTransceivers()) + { + transceiver.SetCodecPreferences(codecs); + } + } + StartCoroutine(WebRTC.Update()); + } + + private IEnumerator ConnectServerTask() + { + yield return -1; + + Debug.LogWarning("create call back start"); + Debug.LogWarning("connect to signaling server start"); + + webSocket = new WebSocket(ServerAddr); + + webSocket.OnMessage += (sender, e) => + { + if (e.Data == null) + { + Debug.LogWarning("OnMessage Data null"); + return; + } + messageQueue.Enqueue(e.Data); + }; + + webSocket.OnError += (sender, e) => + { + Debug.LogWarning(@$"Error! {e.Message}"); + }; + + // waiting for messages + webSocket.Connect(); + yield break; + } + + private IEnumerator MessageProcesser(string inMessage) + { + RTCMessage desc = JsonUtility.FromJson(inMessage); + switch(desc.type) + { + case "ice": + { + RTCIceCandidate candidate = new RTCIceCandidate( + new RTCIceCandidateInit + { + candidate = desc.candidate, + sdpMid = desc.sdpMid, + sdpMLineIndex = desc.sdpMLineIndex + } + ); + rtcPeerConnection.AddIceCandidate(candidate); + } + break; + default: + { + RTCSessionDescription session = new RTCSessionDescription { type = RTCSdpType.Offer, sdp = desc.sdp }; + var op1 = rtcPeerConnection.SetRemoteDescription(ref session); + yield return new WaitUntil(() => op1.IsDone); + + var op2 = rtcPeerConnection.CreateAnswer(); + yield return new WaitUntil(() => op2.IsDone); + + var ans_session = op2.Desc; + var op3 = rtcPeerConnection.SetLocalDescription(ref ans_session); + yield return new WaitUntil(() => op3.IsDone); + + var data = JsonConvert.SerializeObject(new Dictionary + { + {"type", ans_session.type.ToString() }, + {"sdp", ans_session.sdp}, + }); + webSocket.Send(data); + } + break; + } + if (desc.type == "ice") + { + + } + else + { + + } + yield break; + } + + private IEnumerator OnCapture() + { + yield return new WaitForEndOfFrame(); + + RenderTexture cam_render_texture = CaptureCamera.targetTexture; + CaptureCamera.targetTexture = cameraTexture; + CaptureCamera.Render(); + CaptureCamera.targetTexture = cam_render_texture; + + yield break; + } +} \ No newline at end of file diff --git a/Plugins/Unity/project/Assets/BocchiTracker/Runtime/BocchiTrackerVideoCapture.cs.meta b/Plugins/Unity/project/Assets/BocchiTracker/Runtime/BocchiTrackerVideoCapture.cs.meta new file mode 100644 index 0000000..d5e0765 --- /dev/null +++ b/Plugins/Unity/project/Assets/BocchiTracker/Runtime/BocchiTrackerVideoCapture.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: fefad8c27918bd648a239d180eec6e40 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: From b474a861eb4168f44f2b7d1df6aa013f7f66b313 Mon Sep 17 00:00:00 2001 From: KirisameMarisa Date: Sun, 18 Feb 2024 23:00:45 +0900 Subject: [PATCH 06/21] forgot commit --- Application/Models/GameCaptureRTC/Protocol/WebRTC.cs | 2 +- ExternalTools/BuildScripts/config.py | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/Application/Models/GameCaptureRTC/Protocol/WebRTC.cs b/Application/Models/GameCaptureRTC/Protocol/WebRTC.cs index 4936341..0a0dd02 100644 --- a/Application/Models/GameCaptureRTC/Protocol/WebRTC.cs +++ b/Application/Models/GameCaptureRTC/Protocol/WebRTC.cs @@ -62,7 +62,7 @@ private static Task CreatePeerConnection(CaptureSetting inCap { if (rawImage.PixelFormat == SIPSorceryMedia.Abstractions.VideoPixelFormatsEnum.Rgb) { - inFrameStorage.AddFrame((int)rawImage.Width, (int)rawImage.Height, (int)rawImage.Stride, rawImage.Sample, rawImage.PixelFormat.ToString()); + inFrameStorage.AddFrame((int)rawImage.Width, (int)rawImage.Height, (int)rawImage.Stride, rawImage.Sample); } }; diff --git a/ExternalTools/BuildScripts/config.py b/ExternalTools/BuildScripts/config.py index a91cbd9..1863d98 100644 --- a/ExternalTools/BuildScripts/config.py +++ b/ExternalTools/BuildScripts/config.py @@ -1,6 +1,7 @@ from pathlib import Path cFlatBuffersPath = Path("ExternalTools") / "flatbuffers" +cWebSocketSharpPath = Path("ExternalTools") / "websocket-sharp" cPluginsPath = "Plugins/" cUnrealEnginePath = Path(cPluginsPath) / "UnrealEngine" cUnityPath = Path(cPluginsPath) / "Unity" From 69a2542018a10b72cf2add03edac180302044fc1 Mon Sep 17 00:00:00 2001 From: KirisameMarisa Date: Sun, 18 Feb 2024 23:02:11 +0900 Subject: [PATCH 07/21] update git config * add submodule websocket-sharp * websocket-sharp Add to ignore list --- .gitignore | 2 ++ .gitmodules | 3 +++ 2 files changed, 5 insertions(+) diff --git a/.gitignore b/.gitignore index 409abca..acb39a2 100644 --- a/.gitignore +++ b/.gitignore @@ -17,6 +17,8 @@ Plugins/UnrealEngine/BocchiTracker/Intermediate Plugins/UnrealEngine/ThirdParty/flatbuffers/include Plugins/UnrealEngine/ThirdParty/flatbuffers/lib Google.FlatBuffers.* +websocket-sharp.* + ### Unity ### # This .gitignore file should be placed at the root of your Unity project directory diff --git a/.gitmodules b/.gitmodules index 0c2d5c6..8ba8bb8 100644 --- a/.gitmodules +++ b/.gitmodules @@ -4,3 +4,6 @@ [submodule "ExternalTools/godot-cpp"] path = ExternalTools/godot-cpp url = https://github.com/godotengine/godot-cpp +[submodule "ExternalTools/websocket-sharp"] + path = ExternalTools/websocket-sharp + url = https://github.com/sta/websocket-sharp From bc420427b74c71032f538a9bdfe8264c0d70460b Mon Sep 17 00:00:00 2001 From: KirisameMarisa Date: Sun, 18 Feb 2024 23:02:42 +0900 Subject: [PATCH 08/21] add unity sample --- Plugins/Unity/project/Packages/manifest.json | 1 + .../Unity/project/Packages/packages-lock.json | 18 ++++++++++++++++++ .../ProjectSettings/EditorBuildSettings.asset | 2 +- 3 files changed, 20 insertions(+), 1 deletion(-) diff --git a/Plugins/Unity/project/Packages/manifest.json b/Plugins/Unity/project/Packages/manifest.json index 530e2e5..5619c57 100644 --- a/Plugins/Unity/project/Packages/manifest.json +++ b/Plugins/Unity/project/Packages/manifest.json @@ -9,6 +9,7 @@ "com.unity.timeline": "1.7.5", "com.unity.ugui": "1.0.0", "com.unity.visualscripting": "1.8.0", + "com.unity.webrtc": "3.0.0-pre.7", "com.unity.modules.ai": "1.0.0", "com.unity.modules.androidjni": "1.0.0", "com.unity.modules.animation": "1.0.0", diff --git a/Plugins/Unity/project/Packages/packages-lock.json b/Plugins/Unity/project/Packages/packages-lock.json index 8316e65..771ba80 100644 --- a/Plugins/Unity/project/Packages/packages-lock.json +++ b/Plugins/Unity/project/Packages/packages-lock.json @@ -120,6 +120,13 @@ }, "url": "https://packages.unity.com" }, + "com.unity.editorcoroutines": { + "version": "1.0.0", + "depth": 1, + "source": "registry", + "dependencies": {}, + "url": "https://packages.unity.com" + }, "com.unity.ext.nunit": { "version": "1.0.6", "depth": 1, @@ -218,6 +225,17 @@ }, "url": "https://packages.unity.com" }, + "com.unity.webrtc": { + "version": "3.0.0-pre.7", + "depth": 0, + "source": "registry", + "dependencies": { + "com.unity.modules.jsonserialize": "1.0.0", + "com.unity.editorcoroutines": "1.0.0", + "com.unity.modules.audio": "1.0.0" + }, + "url": "https://packages.unity.com" + }, "com.unity.modules.ai": { "version": "1.0.0", "depth": 0, diff --git a/Plugins/Unity/project/ProjectSettings/EditorBuildSettings.asset b/Plugins/Unity/project/ProjectSettings/EditorBuildSettings.asset index 82ab0f5..c377bf6 100644 --- a/Plugins/Unity/project/ProjectSettings/EditorBuildSettings.asset +++ b/Plugins/Unity/project/ProjectSettings/EditorBuildSettings.asset @@ -6,6 +6,6 @@ EditorBuildSettings: serializedVersion: 2 m_Scenes: - enabled: 1 - path: Assets/Scenes/SampleScene.unity + path: Assets/Scenes/CreatePackage.unity guid: 2cda990e2423bbf4892e6590ba056729 m_configObjects: {} From c346505414a10fe74da9bca61afa551e890d24cb Mon Sep 17 00:00:00 2001 From: KirisameMarisa Date: Sun, 18 Feb 2024 23:05:28 +0900 Subject: [PATCH 09/21] BocchiTrackerSystem create component video capture module. --- .../Runtime/BocchiTrackerPlugin.asmdef | 4 ++- .../Runtime/BocchiTrackerSetting.cs | 5 ++-- .../Runtime/BocchiTrackerSystem .cs | 28 +++++++------------ 3 files changed, 15 insertions(+), 22 deletions(-) diff --git a/Plugins/Unity/project/Assets/BocchiTracker/Runtime/BocchiTrackerPlugin.asmdef b/Plugins/Unity/project/Assets/BocchiTracker/Runtime/BocchiTrackerPlugin.asmdef index 853d603..698fe71 100644 --- a/Plugins/Unity/project/Assets/BocchiTracker/Runtime/BocchiTrackerPlugin.asmdef +++ b/Plugins/Unity/project/Assets/BocchiTracker/Runtime/BocchiTrackerPlugin.asmdef @@ -1,7 +1,9 @@ { "name": "BocchiTrackerPlugin", "rootNamespace": "", - "references": [], + "references": [ + "Unity.WebRTC" + ], "includePlatforms": [], "excludePlatforms": [], "allowUnsafeCode": false, diff --git a/Plugins/Unity/project/Assets/BocchiTracker/Runtime/BocchiTrackerSetting.cs b/Plugins/Unity/project/Assets/BocchiTracker/Runtime/BocchiTrackerSetting.cs index deed37e..7a0b1b9 100644 --- a/Plugins/Unity/project/Assets/BocchiTracker/Runtime/BocchiTrackerSetting.cs +++ b/Plugins/Unity/project/Assets/BocchiTracker/Runtime/BocchiTrackerSetting.cs @@ -17,8 +17,7 @@ public class BocchiTrackerSetting : MonoBehaviour public int ServerPort => serverPort; [SerializeField] - private Camera screenshotCamera = null; - public Camera ScreenshotCamera => screenshotCamera; - + private Camera captureCamera = null; + public Camera CaptureCamera => captureCamera; } } diff --git a/Plugins/Unity/project/Assets/BocchiTracker/Runtime/BocchiTrackerSystem .cs b/Plugins/Unity/project/Assets/BocchiTracker/Runtime/BocchiTrackerSystem .cs index 8104b4c..151a919 100644 --- a/Plugins/Unity/project/Assets/BocchiTracker/Runtime/BocchiTrackerSystem .cs +++ b/Plugins/Unity/project/Assets/BocchiTracker/Runtime/BocchiTrackerSystem .cs @@ -23,12 +23,6 @@ public class BocchiTrackerSystem : MonoBehaviour private bool isSentAppBasicInfo; private Queue pendingProcessRequest = new Queue(); - public BocchiTrackerSystem() - { - if (tcpSocket == null) - tcpSocket = new BocchiTrackerTcpSocket(); - } - private void Awake() { if (logHook == null) @@ -38,18 +32,16 @@ private void Awake() private void Start() { setting = GetComponent(); - if(tcpSocket == null) - tcpSocket = gameObject.AddComponent(); + tcpSocket = gameObject.AddComponent(); + var video_capture = gameObject.AddComponent(); + video_capture.ServerAddr = $"ws://{setting.ServerAddress}:{setting.ServerPort}"; + video_capture.CaptureCamera = setting.CaptureCamera; + if (!IsConnect()) isSentAppBasicInfo = false; tcpSocket.ReciveCallback = this.OnReceiveData; } - private void OnDestroy() - { - tcpSocket.DisConnect(); - } - private void Update() { if (IsConnect()) @@ -171,14 +163,14 @@ private IEnumerator ProcessSendScreenshot() // Capture the screenshot RenderTexture rt = new RenderTexture(screenWidth, screenHeight, 32); - RenderTexture prev_rt = setting.ScreenshotCamera.targetTexture; - Quaternion prev_rot = setting.ScreenshotCamera.transform.rotation; + RenderTexture prev_rt = setting.CaptureCamera.targetTexture; + Quaternion prev_rot = setting.CaptureCamera.transform.rotation; Texture2D screenShot = new Texture2D(screenWidth, screenHeight, TextureFormat.RGBA32, false); // Configure camera for screenshot - setting.ScreenshotCamera.targetTexture = rt; - setting.ScreenshotCamera.Render(); - setting.ScreenshotCamera.targetTexture = prev_rt; + setting.CaptureCamera.targetTexture = rt; + setting.CaptureCamera.Render(); + setting.CaptureCamera.targetTexture = prev_rt; // Read and apply the screenshot data RenderTexture.active = rt; From 77f8487f698c2b376eac95b69be6d0e4409b96df Mon Sep 17 00:00:00 2001 From: KirisameMarisa Date: Wed, 21 Feb 2024 10:02:06 +0900 Subject: [PATCH 10/21] supported by PixelStreaming of UE5 --- .../Models/GameCaptureRTC/Protocol/WebRTC.cs | 98 +++++++++++++++++++ 1 file changed, 98 insertions(+) diff --git a/Application/Models/GameCaptureRTC/Protocol/WebRTC.cs b/Application/Models/GameCaptureRTC/Protocol/WebRTC.cs index 0a0dd02..1efdbda 100644 --- a/Application/Models/GameCaptureRTC/Protocol/WebRTC.cs +++ b/Application/Models/GameCaptureRTC/Protocol/WebRTC.cs @@ -10,9 +10,105 @@ using SIPSorceryMedia.Abstractions; using SIPSorceryMedia.FFmpeg; using BocchiTracker.Config.Configs; +using WebSocketSharp; +using Newtonsoft; +using Newtonsoft.Json.Linq; namespace BocchiTracker.GameCaptureRTC.Protocol { + public class WebRTCWebSocketPeer : WebSocketBehavior + { + private RTCPeerConnection _pc = default!; + + public Func> CreatePeerConnection = default!; + + public RTCPeerConnection RTCPeerConnection => _pc; + + public RTCOfferOptions OfferOptions { get; set; } = default!; + + public RTCAnswerOptions AnswerOptions { get; set; } = default!; + + protected override async void OnMessage(MessageEventArgs e) + { + RTCSessionDescriptionInit init2; + if (RTCIceCandidateInit.TryParse(e.Data, out var init)) + { + Console.WriteLine("Got remote ICE candidate."); + _pc.addIceCandidate(init); + } + else if (RTCSessionDescriptionInit.TryParse(e.Data, out init2)) + { + Console.WriteLine($"Got remote SDP, type {init2.type}."); + SetDescriptionResultEnum setDescriptionResultEnum = _pc.setRemoteDescription(init2); + if (setDescriptionResultEnum != 0) + { + Console.WriteLine($"Failed to set remote description, {setDescriptionResultEnum}."); + _pc.Close("failed to set remote description"); + base.Close(); + } + else if (_pc.signalingState == RTCSignalingState.have_remote_offer) + { + RTCSessionDescriptionInit answerSdp = _pc.createAnswer(AnswerOptions); + await _pc.setLocalDescription(answerSdp).ConfigureAwait(continueOnCapturedContext: false); + Console.WriteLine($"Sending SDP answer to client {Context.UserEndPoint}."); + Context.WebSocket.Send(AddPlayerIdJson(answerSdp.toJSON())); + } + } + else + { + Console.WriteLine("websocket-server could not parse JSON message. " + e.Data); + } + } + + protected override async void OnOpen() + { + base.OnOpen(); + Console.WriteLine($"Web socket client connection from {Context.UserEndPoint}."); + _pc = await CreatePeerConnection().ConfigureAwait(continueOnCapturedContext: false); + + var rtc_config = _pc.getConfiguration(); + var data = Newtonsoft.Json.JsonConvert.SerializeObject( + new Dictionary + { + { "type", "config" }, + { "peerConnectionOptions", new Dictionary { + { "bundlePolicy", rtc_config.bundlePolicy.ToString() }, + { "certificates", rtc_config.certificates }, + { "iceCandidatePoolSize", rtc_config.iceCandidatePoolSize }, + { "iceServers", rtc_config.iceServers }, + { "iceTransportPolicy", rtc_config.iceTransportPolicy.ToString() }, + { "rtcpMuxPolicy", rtc_config.rtcpMuxPolicy.ToString() }, + } + }, + } + ); + Context.WebSocket.Send(data); + + _pc.onicecandidate += delegate (RTCIceCandidate iceCandidate) + { + if (_pc.signalingState == RTCSignalingState.have_remote_offer || _pc.signalingState == RTCSignalingState.stable) + { + Context.WebSocket.Send(AddPlayerIdJson(iceCandidate.toJSON())); + } + }; + + if (Context.QueryString["role"] != "offer") + { + RTCSessionDescriptionInit offerSdp = _pc.createOffer(OfferOptions); + await _pc.setLocalDescription(offerSdp).ConfigureAwait(continueOnCapturedContext: false); + Console.WriteLine($"Sending SDP offer to client {Context.UserEndPoint}."); + Context.WebSocket.Send(AddPlayerIdJson(offerSdp.toJSON())); + } + } + + private string AddPlayerIdJson(string inJson) + { + string custom_json = inJson; + custom_json = custom_json.Insert(inJson.LastIndexOf('}'), ", \"playerid\": \"BocchiTrackerPlayer\""); + return custom_json; + } + } + public class WebRTC : WebSocketBehavior, ICaptureProtocol, IDisposable { private readonly IEventAggregator _eventAggregator; @@ -25,6 +121,8 @@ public WebRTC(IEventAggregator inEventAggregator, int inPort, bool inSecure, Cap _eventAggregator = inEventAggregator; _captureFrameStorage = new CaptureFrameStorage(inUserCaptureSetting.RecordingFrameRate, inUserCaptureSetting.RecordingMintes); _web_socket = new WebSocketServer(IPAddress.Any, inPort, inSecure); + _web_socket.Log.Level = WebSocketSharp.LogLevel.Trace; + _web_socket.AllowForwardedRequest = true; _web_socket.AddWebSocketService("/", (peer) => peer.CreatePeerConnection = () => CreatePeerConnection(inCaptureSetting, inUserCaptureSetting, _captureFrameStorage)); _web_socket.Start(); } From eaec25452ddffab7f87e934aba8d7c131b1445e4 Mon Sep 17 00:00:00 2001 From: KirisameMarisa Date: Wed, 21 Feb 2024 10:09:11 +0900 Subject: [PATCH 11/21] RTC config Parameter can be received from the BocchiTracker --- .../Runtime/BocchiTrackerVideoCapture.cs | 30 +++++++++++-------- 1 file changed, 18 insertions(+), 12 deletions(-) diff --git a/Plugins/Unity/project/Assets/BocchiTracker/Runtime/BocchiTrackerVideoCapture.cs b/Plugins/Unity/project/Assets/BocchiTracker/Runtime/BocchiTrackerVideoCapture.cs index 72a8d73..dfe5469 100644 --- a/Plugins/Unity/project/Assets/BocchiTracker/Runtime/BocchiTrackerVideoCapture.cs +++ b/Plugins/Unity/project/Assets/BocchiTracker/Runtime/BocchiTrackerVideoCapture.cs @@ -7,6 +7,9 @@ public class BocchiTrackerVideoCapture : MonoBehaviour { + public string ServerAddr { get; set; } + public Camera CaptureCamera { get; set; } + [System.Serializable] public class RTCMessage { @@ -18,22 +21,16 @@ public class RTCMessage } private WebSocket webSocket; - private RenderTexture cameraTexture; - private RTCPeerConnection rtcPeerConnection; - private Queue messageQueue = new Queue(); - private bool isProcessing = false; - - public string ServerAddr { get; set; } - - public Camera CaptureCamera { get; set; } + private bool isRecivedConfigParam = false; + private RTCConfiguration rtcConfig = new RTCConfiguration(); private void Start() { - CreatePeerConnection(); + StartCoroutine(CreatePeerConnection()); StartCoroutine(ConnectServerTask()); } @@ -82,12 +79,13 @@ private IEnumerator ProcessMessages() isProcessing = false; } - private void CreatePeerConnection() + private IEnumerator CreatePeerConnection() { Debug.LogWarning("create new peerConnection start"); - RTCConfiguration config = default; - rtcPeerConnection = new RTCPeerConnection(ref config); + yield return new WaitUntil(() => isRecivedConfigParam); + + rtcPeerConnection = new RTCPeerConnection(ref rtcConfig); rtcPeerConnection.OnIceConnectionChange = (state) => { Debug.LogWarning($"OnIceConnectionChange {state}"); }; rtcPeerConnection.OnIceGatheringStateChange = (state) => { Debug.LogWarning($"OnIceGatheringStateChange {state}"); }; rtcPeerConnection.OnConnectionStateChange = (state) => { Debug.LogWarning($"OnConnectionStateChange {state}"); }; @@ -115,6 +113,8 @@ private void CreatePeerConnection() } } StartCoroutine(WebRTC.Update()); + + yield break; } private IEnumerator ConnectServerTask() @@ -164,6 +164,12 @@ private IEnumerator MessageProcesser(string inMessage) rtcPeerConnection.AddIceCandidate(candidate); } break; + case "config": + { + //!< TODO... + isRecivedConfigParam = true; + } + break; default: { RTCSessionDescription session = new RTCSessionDescription { type = RTCSdpType.Offer, sdp = desc.sdp }; From 7462f52000a94b03746cd66c39d4ca4993fb94ee Mon Sep 17 00:00:00 2001 From: KirisameMarisa Date: Sun, 25 Feb 2024 12:29:14 +0900 Subject: [PATCH 12/21] unity web rtc stabilized * web socket added reconnection process * RTCMessage added the playerid filed. (because it matches the UnrealEngine structure type. --- .../Runtime/BocchiTrackerSystem .cs | 2 - .../Runtime/BocchiTrackerVideoCapture.cs | 188 ++++++++++++------ 2 files changed, 132 insertions(+), 58 deletions(-) diff --git a/Plugins/Unity/project/Assets/BocchiTracker/Runtime/BocchiTrackerSystem .cs b/Plugins/Unity/project/Assets/BocchiTracker/Runtime/BocchiTrackerSystem .cs index 151a919..e50e25e 100644 --- a/Plugins/Unity/project/Assets/BocchiTracker/Runtime/BocchiTrackerSystem .cs +++ b/Plugins/Unity/project/Assets/BocchiTracker/Runtime/BocchiTrackerSystem .cs @@ -34,8 +34,6 @@ private void Start() setting = GetComponent(); tcpSocket = gameObject.AddComponent(); var video_capture = gameObject.AddComponent(); - video_capture.ServerAddr = $"ws://{setting.ServerAddress}:{setting.ServerPort}"; - video_capture.CaptureCamera = setting.CaptureCamera; if (!IsConnect()) isSentAppBasicInfo = false; diff --git a/Plugins/Unity/project/Assets/BocchiTracker/Runtime/BocchiTrackerVideoCapture.cs b/Plugins/Unity/project/Assets/BocchiTracker/Runtime/BocchiTrackerVideoCapture.cs index dfe5469..5c00e66 100644 --- a/Plugins/Unity/project/Assets/BocchiTracker/Runtime/BocchiTrackerVideoCapture.cs +++ b/Plugins/Unity/project/Assets/BocchiTracker/Runtime/BocchiTrackerVideoCapture.cs @@ -4,12 +4,26 @@ using Unity.WebRTC; using WebSocketSharp; using Unity.Plastic.Newtonsoft.Json; +using System; +using System.Threading.Tasks; +using BocchiTracker; +using UnityEngine.Windows.WebCam; public class BocchiTrackerVideoCapture : MonoBehaviour { - public string ServerAddr { get; set; } - public Camera CaptureCamera { get; set; } + /// + /// Address of the WebSocket server + /// + private string serverAddr { get; set; } + /// + /// Camera to capture + /// + private Camera captureCamera { get; set; } + + /// + /// Represents the state of connection to the server. + /// [System.Serializable] public class RTCMessage { @@ -18,24 +32,61 @@ public class RTCMessage public string sdp; public string sdpMid; public int sdpMLineIndex; + public string playerid; + } + + public enum ConnectionState + { + ConnectStart, + Connecting, + Connected, + Close, } + private ConnectionState connectionState = ConnectionState.ConnectStart; + + /// + /// Time until connection timeout + /// + private float connectingTimeout = 0f; + /// + /// WebSocket connection + /// private WebSocket webSocket; + + /// + /// Camera texture + /// private RenderTexture cameraTexture; + + /// + /// WebRTC peer connection + /// private RTCPeerConnection rtcPeerConnection; + + /// + /// Message queue + /// private Queue messageQueue = new Queue(); + + /// + /// Flag indicating whether messages are being processed + /// private bool isProcessing = false; - private bool isRecivedConfigParam = false; - private RTCConfiguration rtcConfig = new RTCConfiguration(); private void Start() { - StartCoroutine(CreatePeerConnection()); - StartCoroutine(ConnectServerTask()); + var setting = GetComponent(); + serverAddr = $"ws://{setting.ServerAddress}:{setting.ServerPort}"; + captureCamera = setting.CaptureCamera; + + CreatePeerConnection(); } private void Update() { + StartCoroutine(ConnectServerTask()); + if (!isProcessing && messageQueue.Count > 0) { isProcessing = true; @@ -79,19 +130,15 @@ private IEnumerator ProcessMessages() isProcessing = false; } - private IEnumerator CreatePeerConnection() + private void CreatePeerConnection() { - Debug.LogWarning("create new peerConnection start"); - - yield return new WaitUntil(() => isRecivedConfigParam); - - rtcPeerConnection = new RTCPeerConnection(ref rtcConfig); - rtcPeerConnection.OnIceConnectionChange = (state) => { Debug.LogWarning($"OnIceConnectionChange {state}"); }; - rtcPeerConnection.OnIceGatheringStateChange = (state) => { Debug.LogWarning($"OnIceGatheringStateChange {state}"); }; - rtcPeerConnection.OnConnectionStateChange = (state) => { Debug.LogWarning($"OnConnectionStateChange {state}"); }; + RTCConfiguration config = default; + rtcPeerConnection = new RTCPeerConnection(ref config); + rtcPeerConnection.OnIceConnectionChange = (state) => { Debug.Log($"OnIceConnectionChange {state}"); }; + rtcPeerConnection.OnIceGatheringStateChange = (state) => { Debug.Log($"OnIceGatheringStateChange {state}"); }; + rtcPeerConnection.OnConnectionStateChange = (state) => { Debug.Log($"OnConnectionStateChange {state}"); }; rtcPeerConnection.OnIceCandidate = (ev) => { - Debug.LogWarning(ev.Candidate); if (ev.Candidate != null) { var data = JsonConvert.SerializeObject(new Dictionary @@ -113,43 +160,77 @@ private IEnumerator CreatePeerConnection() } } StartCoroutine(WebRTC.Update()); - - yield break; } private IEnumerator ConnectServerTask() { yield return -1; - - Debug.LogWarning("create call back start"); - Debug.LogWarning("connect to signaling server start"); - - webSocket = new WebSocket(ServerAddr); - - webSocket.OnMessage += (sender, e) => + switch (connectionState) { - if (e.Data == null) - { - Debug.LogWarning("OnMessage Data null"); - return; - } - messageQueue.Enqueue(e.Data); - }; + case ConnectionState.ConnectStart: + { + Debug.Assert(webSocket == null); + webSocket = new WebSocket(serverAddr); + webSocket.WaitTime = TimeSpan.FromSeconds(10); + webSocket.OnError += (sender, e) => + { + Debug.LogError(@$"Error! {e.Message}"); + }; + webSocket.OnOpen += (sender, e) => + { + connectionState = ConnectionState.Connected; + connectingTimeout = 0f; + Debug.Log(@$"OnOpen: {connectionState}"); + }; + webSocket.OnClose += (sender, e) => + { + connectionState = ConnectionState.Close; + Debug.Log(@$"OnClose: {connectionState}"); + }; + webSocket.OnMessage += (sender, e) => + { + if (e.Data == null) + return; + messageQueue.Enqueue(e.Data); + }; + connectionState = ConnectionState.Connecting; + webSocket.ConnectAsync(); + } + break; - webSocket.OnError += (sender, e) => - { - Debug.LogWarning(@$"Error! {e.Message}"); - }; + case ConnectionState.Connecting: + { + connectingTimeout += Time.deltaTime; + if (connectingTimeout >= 30f) + { + connectingTimeout = 0f; + connectionState = ConnectionState.Close; + } + } + break; - // waiting for messages - webSocket.Connect(); + case ConnectionState.Connected: + break; + + case ConnectionState.Close: + { + if(webSocket != null) + { + webSocket.Close(); + webSocket = null; + } + connectionState = ConnectionState.ConnectStart; + } + break; + } yield break; } private IEnumerator MessageProcesser(string inMessage) { RTCMessage desc = JsonUtility.FromJson(inMessage); - switch(desc.type) + Debug.Log($"Processing Msg {desc.type}"); + switch (desc.type) { case "ice": { @@ -165,24 +246,27 @@ private IEnumerator MessageProcesser(string inMessage) } break; case "config": - { - //!< TODO... - isRecivedConfigParam = true; + { + } break; default: { + Debug.Log($"SetRemoteDescription..."); RTCSessionDescription session = new RTCSessionDescription { type = RTCSdpType.Offer, sdp = desc.sdp }; var op1 = rtcPeerConnection.SetRemoteDescription(ref session); yield return new WaitUntil(() => op1.IsDone); + Debug.Log($"CreateAnswer..."); var op2 = rtcPeerConnection.CreateAnswer(); yield return new WaitUntil(() => op2.IsDone); + Debug.Log($"SetLocalDescription..."); var ans_session = op2.Desc; var op3 = rtcPeerConnection.SetLocalDescription(ref ans_session); yield return new WaitUntil(() => op3.IsDone); - + + Debug.Log($"Send Aswer BocchiTracker..."); var data = JsonConvert.SerializeObject(new Dictionary { {"type", ans_session.type.ToString() }, @@ -191,14 +275,6 @@ private IEnumerator MessageProcesser(string inMessage) webSocket.Send(data); } break; - } - if (desc.type == "ice") - { - - } - else - { - } yield break; } @@ -206,11 +282,11 @@ private IEnumerator MessageProcesser(string inMessage) private IEnumerator OnCapture() { yield return new WaitForEndOfFrame(); - - RenderTexture cam_render_texture = CaptureCamera.targetTexture; - CaptureCamera.targetTexture = cameraTexture; - CaptureCamera.Render(); - CaptureCamera.targetTexture = cam_render_texture; + + RenderTexture cam_render_texture = captureCamera.targetTexture; + captureCamera.targetTexture = cameraTexture; + captureCamera.Render(); + captureCamera.targetTexture = cam_render_texture; yield break; } From 9037b55cf8d3bfbbbd8bdef83b3a1d04e3c23c70 Mon Sep 17 00:00:00 2001 From: KirisameMarisa Date: Fri, 1 Mar 2024 09:55:33 +0900 Subject: [PATCH 13/21] temporarily, web rtc port changed --- .../Runtime/BocchiTrackerSetting.cs | 4 +++ .../Runtime/BocchiTrackerVideoCapture.cs | 2 +- .../Assets/Scenes/Sample/Sample3D.unity | 33 ++++++++++--------- 3 files changed, 22 insertions(+), 17 deletions(-) diff --git a/Plugins/Unity/project/Assets/BocchiTracker/Runtime/BocchiTrackerSetting.cs b/Plugins/Unity/project/Assets/BocchiTracker/Runtime/BocchiTrackerSetting.cs index 7a0b1b9..07bec7f 100644 --- a/Plugins/Unity/project/Assets/BocchiTracker/Runtime/BocchiTrackerSetting.cs +++ b/Plugins/Unity/project/Assets/BocchiTracker/Runtime/BocchiTrackerSetting.cs @@ -16,6 +16,10 @@ public class BocchiTrackerSetting : MonoBehaviour private int serverPort = 8888; public int ServerPort => serverPort; + [SerializeField] + private int websocketPort = serverPort + 1; + public int WebSocketPort => websocketPort; + [SerializeField] private Camera captureCamera = null; public Camera CaptureCamera => captureCamera; diff --git a/Plugins/Unity/project/Assets/BocchiTracker/Runtime/BocchiTrackerVideoCapture.cs b/Plugins/Unity/project/Assets/BocchiTracker/Runtime/BocchiTrackerVideoCapture.cs index 5c00e66..77fbe27 100644 --- a/Plugins/Unity/project/Assets/BocchiTracker/Runtime/BocchiTrackerVideoCapture.cs +++ b/Plugins/Unity/project/Assets/BocchiTracker/Runtime/BocchiTrackerVideoCapture.cs @@ -77,7 +77,7 @@ public enum ConnectionState private void Start() { var setting = GetComponent(); - serverAddr = $"ws://{setting.ServerAddress}:{setting.ServerPort}"; + serverAddr = $"ws://{setting.ServerAddress}:{setting.WebSocketPort}"; captureCamera = setting.CaptureCamera; CreatePeerConnection(); diff --git a/Plugins/Unity/project/Assets/Scenes/Sample/Sample3D.unity b/Plugins/Unity/project/Assets/Scenes/Sample/Sample3D.unity index 9010181..481238e 100644 --- a/Plugins/Unity/project/Assets/Scenes/Sample/Sample3D.unity +++ b/Plugins/Unity/project/Assets/Scenes/Sample/Sample3D.unity @@ -334,7 +334,7 @@ GameObject: m_Component: - component: {fileID: 1060528355} - component: {fileID: 1060528354} - - component: {fileID: 1060528353} + - component: {fileID: 1060528356} m_Layer: 0 m_Name: BocchiTrackerSystem m_TagString: Untagged @@ -342,21 +342,6 @@ GameObject: m_NavMeshLayer: 0 m_StaticEditorFlags: 0 m_IsActive: 1 ---- !u!114 &1060528353 -MonoBehaviour: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 1060528352} - m_Enabled: 1 - m_EditorHideFlags: 0 - m_Script: {fileID: 11500000, guid: 185bdc4ad8bc5cd48b48c8ad85703eeb, type: 3} - m_Name: - m_EditorClassIdentifier: - serverAddress: 127.0.0.1 - serverPort: 8888 - captureCamera: {fileID: 963194227} --- !u!114 &1060528354 MonoBehaviour: m_ObjectHideFlags: 0 @@ -384,6 +369,22 @@ Transform: m_Children: [] m_Father: {fileID: 0} m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!114 &1060528356 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1060528352} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 185bdc4ad8bc5cd48b48c8ad85703eeb, type: 3} + m_Name: + m_EditorClassIdentifier: + serverAddress: 127.0.0.1 + serverPort: 8888 + websocketPort: 8822 + captureCamera: {fileID: 963194227} --- !u!1 &1494285074 GameObject: m_ObjectHideFlags: 0 From ec4426078af843321691c3dc0e3872cdf83c6aae Mon Sep 17 00:00:00 2001 From: KirisameMarisa Date: Fri, 1 Mar 2024 10:24:41 +0900 Subject: [PATCH 14/21] * Move web rtc movie capture processing to CaptureFrameStorage. ** When the movie frame received, write directly to the movie file ** Delete old movie files when the maximum number of movie frames is reached ** IssueAssetCollector is simpler, it only receives movie file path --- Application/BocchiTracker.WPF.sln | 13 ++ .../Models/Config/Configs/ProjectConfig.cs | 6 +- .../GameCaptureRTC/CaptureFrameStorage.cs | 157 +++++++++++++++--- .../GameCaptureRTC/GameCaptureRTC.csproj | 14 +- Application/Models/GameCaptureRTC/Module.cs | 2 +- .../Protocol/ICaptureProtocol.cs | 5 +- .../GameCaptureRTC/Protocol/OBSCapture.cs | 12 +- .../Models/GameCaptureRTC/Protocol/WebRTC.cs | 56 ++++--- .../GameCaptureRTC/RecordingController.cs | 36 ++-- .../Handlers/CreateActionHandler.cs | 13 +- .../Handlers/Movie/MovieHandler.cs | 40 ++++- .../Handlers/Movie/OBSStudioHandler.cs | 22 --- .../Handlers/Movie/WebRTCHandler.cs | 91 ---------- .../IssueAssetCollector.csproj | 4 + .../Models/ModelEvent/GameCaptureEvent.cs | 29 +--- 15 files changed, 277 insertions(+), 223 deletions(-) delete mode 100644 Application/Models/IssueAssetCollector/Handlers/Movie/OBSStudioHandler.cs delete mode 100644 Application/Models/IssueAssetCollector/Handlers/Movie/WebRTCHandler.cs diff --git a/Application/BocchiTracker.WPF.sln b/Application/BocchiTracker.WPF.sln index a7b5ef3..72b7fe7 100644 --- a/Application/BocchiTracker.WPF.sln +++ b/Application/BocchiTracker.WPF.sln @@ -50,6 +50,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "GameCaptureRTC", "Models\Ga EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BocchiTracker.WebRTCTest", "Tests\BocchiTracker.WebRTCTest\BocchiTracker.WebRTCTest.csproj", "{B3675F7D-DF3F-4F0E-A9E0-9575381BC964}" EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "ExternalTools", "ExternalTools", "{75B25179-1B35-461D-80AA-80EF52E7E101}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "websocket-sharp", "..\ExternalTools\websocket-sharp\websocket-sharp\websocket-sharp.csproj", "{0D0785D7-4A07-4FA0-919F-FD3AB6AED2F4}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -202,6 +206,14 @@ Global {B3675F7D-DF3F-4F0E-A9E0-9575381BC964}.Release|Any CPU.Build.0 = Release|Any CPU {B3675F7D-DF3F-4F0E-A9E0-9575381BC964}.Release|x64.ActiveCfg = Release|x64 {B3675F7D-DF3F-4F0E-A9E0-9575381BC964}.Release|x64.Build.0 = Release|x64 + {0D0785D7-4A07-4FA0-919F-FD3AB6AED2F4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {0D0785D7-4A07-4FA0-919F-FD3AB6AED2F4}.Debug|Any CPU.Build.0 = Debug|Any CPU + {0D0785D7-4A07-4FA0-919F-FD3AB6AED2F4}.Debug|x64.ActiveCfg = Debug|Any CPU + {0D0785D7-4A07-4FA0-919F-FD3AB6AED2F4}.Debug|x64.Build.0 = Debug|Any CPU + {0D0785D7-4A07-4FA0-919F-FD3AB6AED2F4}.Release|Any CPU.ActiveCfg = Release|Any CPU + {0D0785D7-4A07-4FA0-919F-FD3AB6AED2F4}.Release|Any CPU.Build.0 = Release|Any CPU + {0D0785D7-4A07-4FA0-919F-FD3AB6AED2F4}.Release|x64.ActiveCfg = Release|Any CPU + {0D0785D7-4A07-4FA0-919F-FD3AB6AED2F4}.Release|x64.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -226,6 +238,7 @@ Global {D214D1D6-7DBA-4F53-B070-9672BA3F7CBA} = {12AAC0C3-970D-43F6-BD47-0B662B7157F6} {ADEDB772-6FFB-4198-9107-1AD791899679} = {677ECDC0-9125-4E30-8B8C-E0CC5F98DFF5} {B3675F7D-DF3F-4F0E-A9E0-9575381BC964} = {E876F453-952B-4D58-AA0E-1D95DFF58FB5} + {0D0785D7-4A07-4FA0-919F-FD3AB6AED2F4} = {75B25179-1B35-461D-80AA-80EF52E7E101} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {F04B3FFB-34D2-4BBE-85BA-F28DEE1BCAB6} diff --git a/Application/Models/Config/Configs/ProjectConfig.cs b/Application/Models/Config/Configs/ProjectConfig.cs index e1ec303..ed80542 100644 --- a/Application/Models/Config/Configs/ProjectConfig.cs +++ b/Application/Models/Config/Configs/ProjectConfig.cs @@ -9,7 +9,7 @@ namespace BocchiTracker.Config.Configs { public class CaptureSetting { - public string? FFmpegPath { get; set; } + public string? FFmpegPath { get; set; } = string.Empty; public SIPSorceryMedia.Abstractions.VideoCodecsEnum VideoCodecs { get; set; } = SIPSorceryMedia.Abstractions.VideoCodecsEnum.VP8; } @@ -56,7 +56,9 @@ public class ExternalToolsPath public class ProjectConfig { - public int Port { get; set; } = 8888; + public int Port { get; set; } = 8888; + + public int WebSocketPort { get; set; } = 8822; public List TicketTypes { get; set; } = new List { "Bug", "Task", "Question" }; diff --git a/Application/Models/GameCaptureRTC/CaptureFrameStorage.cs b/Application/Models/GameCaptureRTC/CaptureFrameStorage.cs index fb0dbdb..8e67ab3 100644 --- a/Application/Models/GameCaptureRTC/CaptureFrameStorage.cs +++ b/Application/Models/GameCaptureRTC/CaptureFrameStorage.cs @@ -1,44 +1,157 @@  +using BocchiTracker.ModelEvent; +using FFMpegCore; +using OpenCvSharp; +using Prism.Events; +using SixLabors.ImageSharp; +using SixLabors.ImageSharp.PixelFormats; using System; +using System.IO; +using System.Linq; using System.Runtime.InteropServices; +using System.Text.RegularExpressions; +using System.Threading; +using System.Threading.Tasks; namespace BocchiTracker.GameCaptureRTC { - public class CaptureFrameStorage + public class CaptureFrameStorage : IDisposable { - private int _frameRate = 30; - private int _recordingMaxFrames = 0; - public ModelEvent.CaptureStreamParameter CaptureStreamParameter { get; set; } = new ModelEvent.CaptureStreamParameter(); + private object _mutext = new object(); - public CaptureFrameStorage(int inFrameRate, int inRecordingMintes) + private int _maxRecordingFrameCount = 0; + private int _maxSplitFrameCount = 0; + private int _curFrameCount = 0; + private int _curSpliteFrameCount = 0; + + private string _tempCancatMovieDirectory = Path.Combine(Path.GetTempPath(), "BocchiTracker", "temp", "concat_movies"); + private string _tempMovieDirectory = Path.Combine(Path.GetTempPath(), "BocchiTracker", "temp", "movies"); + private string _tempPicsDirectory = Path.Combine(Path.GetTempPath(), "BocchiTracker", "temp", "pics"); + + private int _movieID = 0; + private int _adjustedwidth = 640; + private int _adjustedHeight = 480; + + private VideoWriter _videoWriter = default!; + + public CaptureFrameStorage(string inFFmpegPath, int inMaxRecordingFrameCount, int inMaxSplitFrameCount) { - _frameRate = inFrameRate; - _recordingMaxFrames = (inRecordingMintes * 60) * _frameRate; + GlobalFFOptions.Configure(options => options.BinaryFolder = inFFmpegPath); + + _maxSplitFrameCount = inMaxSplitFrameCount; + _maxRecordingFrameCount = inMaxRecordingFrameCount; + + if (!Directory.Exists(_tempMovieDirectory)) + Directory.CreateDirectory(_tempMovieDirectory); + if (!Directory.Exists(_tempCancatMovieDirectory)) + Directory.CreateDirectory(_tempCancatMovieDirectory); + if (!Directory.Exists(_tempPicsDirectory)) + Directory.CreateDirectory(_tempPicsDirectory); + + Cleanup(); } public void AddFrame(int inWidth, int inHeight, int inStride, nint inData) { - if (CaptureStreamParameter.Frames.Count > _recordingMaxFrames) - CaptureStreamParameter.Frames.RemoveAt(0); - - // メモリ領域のコピーを作成して渡す - byte[] dataCopy = new byte[inHeight * inStride]; - unsafe + lock(_mutext) { - byte* src = (byte*)inData; - for (int i = 0; i < dataCopy.Length; i++) + if(_adjustedwidth > inWidth || _adjustedwidth == 0) + _adjustedwidth = inWidth % 2 == 0 ? inWidth : inWidth - 1; + if (_adjustedHeight > inHeight || _adjustedHeight == 0) + _adjustedHeight = inHeight % 2 == 0 ? inHeight : inHeight - 1; + + if (_curSpliteFrameCount == 0) + { + System.Console.WriteLine("start video capture"); + if (_videoWriter == null || _videoWriter.IsDisposed) + _videoWriter = new VideoWriter(); + _videoWriter.Open(Path.Combine(_tempMovieDirectory, $"movie.{_movieID}.mp4"), FourCC.MPG4, 30, new OpenCvSharp.Size(inWidth, inHeight)); + ++_movieID; + } + + unsafe + { + byte[] dataCopy = new byte[inHeight * inStride]; + unsafe + { + byte* src = (byte*)inData; + for (int i = 0; i < dataCopy.Length; i++) + { + dataCopy[i] = *(src + i); + } + } + + using (var mat = new Mat(inHeight, inWidth, MatType.CV_8UC3, dataCopy, inStride)) + { + _videoWriter.Write(mat); + + _curFrameCount++; + _curSpliteFrameCount++; + } + + } + + if (_curSpliteFrameCount > _maxSplitFrameCount) { - dataCopy[i] = *(src + i); + System.Console.WriteLine("_videoWriter wrote maximum frame, so next video..."); + _curSpliteFrameCount = 0; + _videoWriter.Dispose(); } + + if (_curFrameCount > _maxRecordingFrameCount) + { + var bochi_files = Directory.GetFiles(_tempMovieDirectory, "*.mp4") + .OrderBy(file => int.Parse(Regex.Match(file, @"(?<=movie\.)(\d+)(?=\.mp4)").Value)) + .ToList(); + if(bochi_files.Any()) + { + System.Console.WriteLine("the maximum recording time has been exceeded, so remove old video file"); + File.Delete(bochi_files[0]); + } + _curFrameCount -= _maxSplitFrameCount; + } + } + + } + + public string ConcatMovie() + { + lock (_mutext) + { + Dispose(); + _curFrameCount = 0; + _curSpliteFrameCount = 0; + + var output = Path.Combine(_tempCancatMovieDirectory, "bocchi_movie.mp4"); + var bochi_files = Directory.GetFiles(_tempMovieDirectory, "*.mp4"); + if (!bochi_files.Any()) + return string.Empty; + + var command = FFMpegArguments + .FromDemuxConcatInput(bochi_files) + .OutputToFile(output, overwrite: true, op => op.Resize(_adjustedwidth, _adjustedHeight)); + bool ret = command.ProcessSynchronously(); + if (ret) + Cleanup(); + return output; } + } - CaptureStreamParameter.Frames.Add(new ModelEvent.CaptureStreamParameter.Frame + public void Cleanup() + { + var bochi_files = Directory.GetFiles(_tempMovieDirectory, "*.mp4"); + foreach (var file in bochi_files) { - Data = dataCopy, - Width = inWidth, - Height = inHeight, - Stride = inStride, - }); + File.Delete(file); + } + } + + public void Dispose() + { + if (_videoWriter != null && !_videoWriter.IsDisposed) + { + _videoWriter.Dispose(); + } } } } diff --git a/Application/Models/GameCaptureRTC/GameCaptureRTC.csproj b/Application/Models/GameCaptureRTC/GameCaptureRTC.csproj index da6bc2b..0876314 100644 --- a/Application/Models/GameCaptureRTC/GameCaptureRTC.csproj +++ b/Application/Models/GameCaptureRTC/GameCaptureRTC.csproj @@ -5,20 +5,32 @@ True net7.0-windows7.0 + x64 + + + + + + + + + + - + + diff --git a/Application/Models/GameCaptureRTC/Module.cs b/Application/Models/GameCaptureRTC/Module.cs index b23c08a..6775285 100644 --- a/Application/Models/GameCaptureRTC/Module.cs +++ b/Application/Models/GameCaptureRTC/Module.cs @@ -3,7 +3,7 @@ namespace BocchiTracker.GameCaptureRTC { - public class Module : IModule + public class GameCaptureRTCModule : IModule { public void OnInitialized(IContainerProvider containerProvider) {} diff --git a/Application/Models/GameCaptureRTC/Protocol/ICaptureProtocol.cs b/Application/Models/GameCaptureRTC/Protocol/ICaptureProtocol.cs index 48731fe..9057267 100644 --- a/Application/Models/GameCaptureRTC/Protocol/ICaptureProtocol.cs +++ b/Application/Models/GameCaptureRTC/Protocol/ICaptureProtocol.cs @@ -1,4 +1,5 @@ -using System; +using BocchiTracker.Config.Configs; +using System; using System.Collections.Generic; using System.Linq; using System.Text; @@ -8,7 +9,7 @@ namespace BocchiTracker.GameCaptureRTC.Protocol { public interface ICaptureProtocol { - void Start(); + void Start(int inPort, ProjectConfig inProjectConfig, UserConfig inUserConfig); void Stop(); diff --git a/Application/Models/GameCaptureRTC/Protocol/OBSCapture.cs b/Application/Models/GameCaptureRTC/Protocol/OBSCapture.cs index 448d8e8..22347e3 100644 --- a/Application/Models/GameCaptureRTC/Protocol/OBSCapture.cs +++ b/Application/Models/GameCaptureRTC/Protocol/OBSCapture.cs @@ -3,26 +3,26 @@ using System.Linq; using System.Text; using System.Threading.Tasks; +using BocchiTracker.Config.Configs; using OBSWebsocketDotNet; namespace BocchiTracker.GameCaptureRTC.Protocol { public class OBSCapture : ICaptureProtocol { - private OBSWebsocket _obsWebSocket; + private OBSWebsocket _obsWebSocket = default!; - public OBSCapture(int inPort, string inCaptureSource) + public OBSCapture() { - _obsWebSocket = new OBSWebsocket(); - var scenes = _obsWebSocket.ListScenes(); + } public bool IsConnect() { throw new NotImplementedException(); } - - public void Start() + + public void Start(int inPort, ProjectConfig inProjectConfig, UserConfig inUserConfig) { throw new NotImplementedException(); } diff --git a/Application/Models/GameCaptureRTC/Protocol/WebRTC.cs b/Application/Models/GameCaptureRTC/Protocol/WebRTC.cs index 1efdbda..d648adc 100644 --- a/Application/Models/GameCaptureRTC/Protocol/WebRTC.cs +++ b/Application/Models/GameCaptureRTC/Protocol/WebRTC.cs @@ -13,6 +13,10 @@ using WebSocketSharp; using Newtonsoft; using Newtonsoft.Json.Linq; +using System.Net.Sockets; +using System.Threading; +using System.Reflection.Metadata; +using System.IO; namespace BocchiTracker.GameCaptureRTC.Protocol { @@ -54,10 +58,6 @@ protected override async void OnMessage(MessageEventArgs e) Context.WebSocket.Send(AddPlayerIdJson(answerSdp.toJSON())); } } - else - { - Console.WriteLine("websocket-server could not parse JSON message. " + e.Data); - } } protected override async void OnOpen() @@ -91,14 +91,11 @@ protected override async void OnOpen() Context.WebSocket.Send(AddPlayerIdJson(iceCandidate.toJSON())); } }; - - if (Context.QueryString["role"] != "offer") - { - RTCSessionDescriptionInit offerSdp = _pc.createOffer(OfferOptions); - await _pc.setLocalDescription(offerSdp).ConfigureAwait(continueOnCapturedContext: false); - Console.WriteLine($"Sending SDP offer to client {Context.UserEndPoint}."); - Context.WebSocket.Send(AddPlayerIdJson(offerSdp.toJSON())); - } + + RTCSessionDescriptionInit offerSdp = _pc.createOffer(OfferOptions); + await _pc.setLocalDescription(offerSdp).ConfigureAwait(continueOnCapturedContext: false); + Console.WriteLine($"Sending SDP offer to client {Context.UserEndPoint}."); + Context.WebSocket.Send(AddPlayerIdJson(offerSdp.toJSON())); } private string AddPlayerIdJson(string inJson) @@ -112,24 +109,21 @@ private string AddPlayerIdJson(string inJson) public class WebRTC : WebSocketBehavior, ICaptureProtocol, IDisposable { private readonly IEventAggregator _eventAggregator; - private WebSocketServer _web_socket; - private CaptureFrameStorage _captureFrameStorage; + private WebSocketServer _web_socket = default!; + private CaptureFrameStorage _captureFrameStorage = default!; private static bool _isConnecting; + private SubscriptionToken _subscriptionToken; - public WebRTC(IEventAggregator inEventAggregator, int inPort, bool inSecure, CaptureSetting inCaptureSetting, UserCaptureSetting inUserCaptureSetting) + public WebRTC(IEventAggregator inEventAggregator) { _eventAggregator = inEventAggregator; - _captureFrameStorage = new CaptureFrameStorage(inUserCaptureSetting.RecordingFrameRate, inUserCaptureSetting.RecordingMintes); - _web_socket = new WebSocketServer(IPAddress.Any, inPort, inSecure); - _web_socket.Log.Level = WebSocketSharp.LogLevel.Trace; - _web_socket.AllowForwardedRequest = true; - _web_socket.AddWebSocketService("/", (peer) => peer.CreatePeerConnection = () => CreatePeerConnection(inCaptureSetting, inUserCaptureSetting, _captureFrameStorage)); - _web_socket.Start(); + _subscriptionToken = _eventAggregator.GetEvent().Subscribe(Stop, ThreadOption.BackgroundThread); } public void Dispose() { _web_socket.Stop(); + _eventAggregator.GetEvent().Unsubscribe(_subscriptionToken); } public bool IsConnect() @@ -137,17 +131,27 @@ public bool IsConnect() return _isConnecting; } - public void Start() + public void Start(int inPort, ProjectConfig inProjectConfig, UserConfig inUserConfig) { + string? ffmpeg = inProjectConfig.CaptureSetting.FFmpegPath; + if (ffmpeg == null || !Path.Exists(ffmpeg)) + throw new Exception("FFmpeg path is not set."); + int maxFrameCount = (60 * inUserConfig.UserCaptureSetting.RecordingFrameRate) * inUserConfig.UserCaptureSetting.RecordingMintes; + _captureFrameStorage = new CaptureFrameStorage(ffmpeg, maxFrameCount, maxFrameCount / 10); + _web_socket = new WebSocketServer(IPAddress.Any, inPort, false); + _web_socket.Log.Level = WebSocketSharp.LogLevel.Trace; + _web_socket.AllowForwardedRequest = true; + _web_socket.AddWebSocketService("/", (peer) => peer.CreatePeerConnection = () => CreatePeerConnection(inProjectConfig.CaptureSetting, inUserConfig.UserCaptureSetting, _captureFrameStorage)); + _web_socket.Start(); } public void Stop() { - _eventAggregator - .GetEvent() - .Publish(new GameCaptureFinishEventParameter { CaptureStreamParameter = _captureFrameStorage.CaptureStreamParameter }); - _captureFrameStorage.CaptureStreamParameter.Frames.Clear(); + string movie = _captureFrameStorage.ConcatMovie(); + if(movie.IsNullOrEmpty()) + return; + _eventAggregator.GetEvent().Publish(movie); } private static Task CreatePeerConnection(CaptureSetting inCaptureSetting, UserCaptureSetting inUserCaptureSetting, CaptureFrameStorage inFrameStorage) diff --git a/Application/Models/GameCaptureRTC/RecordingController.cs b/Application/Models/GameCaptureRTC/RecordingController.cs index f586a1a..efe8058 100644 --- a/Application/Models/GameCaptureRTC/RecordingController.cs +++ b/Application/Models/GameCaptureRTC/RecordingController.cs @@ -1,6 +1,8 @@ -using BocchiTracker.GameCaptureRTC.Protocol; +using BocchiTracker.Config.Configs; +using BocchiTracker.GameCaptureRTC.Protocol; using BocchiTracker.ModelEvent; using Prism.Events; +using System.Threading.Tasks; namespace BocchiTracker.GameCaptureRTC { @@ -13,9 +15,6 @@ public class RecordingController : ICaptureProtocol public RecordingController(IEventAggregator inEventAggregator) { _eventAggregator = inEventAggregator; - _eventAggregator - .GetEvent() - .Subscribe(OnConfigReload); } public bool IsConnect() @@ -25,36 +24,31 @@ public bool IsConnect() return _captureProtocol.IsConnect(); } - public void Start() + public void Start(int inPort, ProjectConfig inProjectConfig, UserConfig inUserConfig) { - _captureProtocol?.Start(); - } - - public void Stop() - { - _captureProtocol?.Stop(); - } - - private void OnConfigReload(ConfigReloadEventParameter inParam) - { - if (inParam.UserConfig == null || inParam.ProjectConfig == null) - return; - - switch (inParam.UserConfig.UserCaptureSetting.GameCaptureType) + switch (inUserConfig.UserCaptureSetting.GameCaptureType) { case Config.GameCaptureType.OBSStudio: { - _captureProtocol = new Protocol.OBSCapture(inParam.ProjectConfig.Port, ""); + _captureProtocol = new Protocol.OBSCapture(); } break; case Config.GameCaptureType.WebRTC: { - _captureProtocol = new Protocol.WebRTC(_eventAggregator, inParam.ProjectConfig.Port, false, inParam.ProjectConfig.CaptureSetting, inParam.UserConfig.UserCaptureSetting); + _captureProtocol = new Protocol.WebRTC(_eventAggregator); } break; default: break; } + + if(_captureProtocol != null) + _captureProtocol.Start(inPort, inProjectConfig, inUserConfig); + } + + public void Stop() + { + _captureProtocol?.Stop(); } } } diff --git a/Application/Models/IssueAssetCollector/Handlers/CreateActionHandler.cs b/Application/Models/IssueAssetCollector/Handlers/CreateActionHandler.cs index bda6986..36113aa 100644 --- a/Application/Models/IssueAssetCollector/Handlers/CreateActionHandler.cs +++ b/Application/Models/IssueAssetCollector/Handlers/CreateActionHandler.cs @@ -2,6 +2,7 @@ using BocchiTracker.Config.Configs; using BocchiTracker.IssueAssetCollector.Handlers.Coredump; using BocchiTracker.IssueAssetCollector.Handlers.Log; +using BocchiTracker.IssueAssetCollector.Handlers.Movie; using BocchiTracker.IssueAssetCollector.Handlers.Screenshot; using BocchiTracker.ModelEvent; using BocchiTracker.ProcessLinkQuery.Queries; @@ -26,13 +27,15 @@ public class CreateActionHandler : ICreateActionHandler private readonly IEventAggregator _eventAggregator; private readonly IFilenameGeneratorFactory _filenameGeneratorFactory; private readonly ProjectConfig _projectConfig; + private readonly UserConfig _userConfig; private readonly AppStatusBundles _appStatusBundles; - public CreateActionHandler(IEventAggregator inEventAggregator, IFilenameGeneratorFactory inFilenameGenFac, AppStatusBundles inAppStatusBundles, ProjectConfig inConfig) + public CreateActionHandler(IEventAggregator inEventAggregator, IFilenameGeneratorFactory inFilenameGenFac, AppStatusBundles inAppStatusBundles, ProjectConfig inProjectConfig, UserConfig inUserConfig) { _eventAggregator = inEventAggregator; _filenameGeneratorFactory = inFilenameGenFac; - _projectConfig = inConfig; + _projectConfig = inProjectConfig; + _userConfig = inUserConfig; _appStatusBundles = inAppStatusBundles; } @@ -71,6 +74,12 @@ public IHandle Create(Type inType) _cacheHandles.Add(inType, handler); } + if(inType == typeof(MovieHandler)) + { + var handler = new MovieHandler(_eventAggregator, _filenameGeneratorFactory.GetFilenameGenerator(typeof(TimestampedFilenameGenerator)), _projectConfig.CaptureSetting.FFmpegPath); + _cacheHandles.Add(inType, handler); + } + return _cacheHandles[inType]; } } diff --git a/Application/Models/IssueAssetCollector/Handlers/Movie/MovieHandler.cs b/Application/Models/IssueAssetCollector/Handlers/Movie/MovieHandler.cs index 3c1a221..0a56cd6 100644 --- a/Application/Models/IssueAssetCollector/Handlers/Movie/MovieHandler.cs +++ b/Application/Models/IssueAssetCollector/Handlers/Movie/MovieHandler.cs @@ -1,21 +1,57 @@ using BocchiTracker.ApplicationInfoCollector; +using BocchiTracker.ModelEvent; +using FFMpegCore; +using Prism.Events; using System; using System.Collections.Generic; +using System.Diagnostics; +using System.IO; using System.Linq; using System.Text; using System.Threading.Tasks; namespace BocchiTracker.IssueAssetCollector.Handlers.Movie { + public class GameCaptureFrameConvertMovieProcess + { + + public string Output { get; set; } = string.Empty; + private IEventAggregator _eventAggregator; + + public GameCaptureFrameConvertMovieProcess(IEventAggregator inEventAggregator, string inFFmpegPath) + { + _eventAggregator = inEventAggregator; + _eventAggregator + .GetEvent() + .Subscribe(OnGameCaptureFinishEvent, ThreadOption.BackgroundThread); + + GlobalFFOptions.Configure(options => options.BinaryFolder = inFFmpegPath); + } + + public void OnGameCaptureFinishEvent(string inMoviePath) + { + File.Copy(inMoviePath, Output, true); + } + } + public class MovieHandler : IHandle { public IFilenameGenerator _filenameGenerator { private set; get; } + private IEventAggregator _eventAggregator; + private GameCaptureFrameConvertMovieProcess _convert_movie_process; - public MovieHandler(IFilenameGenerator inFilenameGenerator) + public MovieHandler(IEventAggregator inEventAggregator, IFilenameGenerator inFilenameGenerator, string? inFFmpegPath) { + Debug.Assert(inFFmpegPath != null, "should need ffmpegpath"); _filenameGenerator = inFilenameGenerator; + _eventAggregator = inEventAggregator; + _convert_movie_process = new GameCaptureFrameConvertMovieProcess(inEventAggregator, inFFmpegPath); } - public virtual void Handle(AppStatusBundle inAppStatusBundle, int inPID, string inOutput) { } + public void Handle(AppStatusBundle inAppStatusBundle, int inPID, string inOutput) + { + this._convert_movie_process.Output = Path.Combine(inOutput, _filenameGenerator.Generate(inAppStatusBundle) + ".mp4"); + _eventAggregator.GetEvent().Publish(); + } } } diff --git a/Application/Models/IssueAssetCollector/Handlers/Movie/OBSStudioHandler.cs b/Application/Models/IssueAssetCollector/Handlers/Movie/OBSStudioHandler.cs deleted file mode 100644 index 61f8a80..0000000 --- a/Application/Models/IssueAssetCollector/Handlers/Movie/OBSStudioHandler.cs +++ /dev/null @@ -1,22 +0,0 @@ -using BocchiTracker.ApplicationInfoCollector; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace BocchiTracker.IssueAssetCollector.Handlers.Movie -{ - public class OBSStudioHandler : MovieHandler - { - public OBSStudioHandler(IFilenameGenerator inFilenameGenerator) : base(inFilenameGenerator) - { - - } - - public override void Handle(AppStatusBundle inAppStatusBundle, int inPID, string inOutput) - { - - } - } -} diff --git a/Application/Models/IssueAssetCollector/Handlers/Movie/WebRTCHandler.cs b/Application/Models/IssueAssetCollector/Handlers/Movie/WebRTCHandler.cs deleted file mode 100644 index 59cd554..0000000 --- a/Application/Models/IssueAssetCollector/Handlers/Movie/WebRTCHandler.cs +++ /dev/null @@ -1,91 +0,0 @@ -using BocchiTracker.ApplicationInfoCollector; -using BocchiTracker.ModelEvent; -using Prism.Events; -using SixLabors.ImageSharp.PixelFormats; -using SixLabors.ImageSharp; -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Reflection; -using System.Text; -using System.Threading.Tasks; -using System.Windows.Media.Media3D; - -namespace BocchiTracker.IssueAssetCollector.Handlers.Movie -{ - public class GameCaptureFrameConvertMovieProcess - { - public string Output { get; set; } = string.Empty; - - private IEventAggregator _eventAggregator; - - public GameCaptureFrameConvertMovieProcess(IEventAggregator inEventAggregator) - { - _eventAggregator = inEventAggregator; - _eventAggregator - .GetEvent() - .Subscribe(Handle, ThreadOption.PublisherThread); - } - - public void Handle(GameCaptureFinishEventParameter inParam) - { - var c = inParam.CaptureStreamParameter; - if (c == null) return; - - int idx = 0; - unsafe - { - string output = Path.Combine(Output, "temp"); - new DirectoryInfo(output).Create(); - - foreach (var frame in c.Frames.ToList()) - { - if (frame.Data == null) continue; - - Span dataSpan = new Span(frame.Data); - using (var image = new Image(frame.Width, frame.Height)) - { - for (int y = 0; y < frame.Height; y++) - { - for (int x = 0; x < frame.Width; x++) - { - var index = y * frame.Stride + x * 3; - var color = new Rgb24(dataSpan[index + 2], dataSpan[index + 1], dataSpan[index]); - image[x, y] = color; - } - } - image.Save(Path.Combine(output, $"{idx++}.png")); - } - } - } - - //!< 連番テクスチャーをここで動画化する - //!< TODO - - //!< ここで連番テクスチャを削除する - //!< TODO - } - } - - public class WebRTCHandler : MovieHandler - { - private IEventAggregator _eventAggregator; - private GameCaptureFrameConvertMovieProcess _convert_movie_process; - - public WebRTCHandler(IEventAggregator inEventAggregator, IFilenameGenerator inFilenameGenerator) : base(inFilenameGenerator) - { - _eventAggregator = inEventAggregator; - _convert_movie_process = new GameCaptureFrameConvertMovieProcess(inEventAggregator); - } - - public override void Handle(AppStatusBundle inAppStatusBundle, int inPID, string inOutput) - { - this._convert_movie_process.Output = Path.Combine(inOutput, _filenameGenerator.Generate(inAppStatusBundle) + ".mp4"); - - _eventAggregator - .GetEvent() - .Publish(new ScreenshotRequestEventParameter(inAppStatusBundle.AppBasicInfo.ClientID)); - } - } -} diff --git a/Application/Models/IssueAssetCollector/IssueAssetCollector.csproj b/Application/Models/IssueAssetCollector/IssueAssetCollector.csproj index b413a49..52d1709 100644 --- a/Application/Models/IssueAssetCollector/IssueAssetCollector.csproj +++ b/Application/Models/IssueAssetCollector/IssueAssetCollector.csproj @@ -17,6 +17,10 @@ + + + + diff --git a/Application/Models/ModelEvent/GameCaptureEvent.cs b/Application/Models/ModelEvent/GameCaptureEvent.cs index 638e4e8..791990e 100644 --- a/Application/Models/ModelEvent/GameCaptureEvent.cs +++ b/Application/Models/ModelEvent/GameCaptureEvent.cs @@ -1,36 +1,15 @@ using Prism.Events; using System; using System.Collections.Generic; +using System.IO; using System.Linq; +using System.Runtime.Serialization.Formatters.Binary; using System.Text; using System.Threading.Tasks; namespace BocchiTracker.ModelEvent { - public class OBSStudioParameter - { - public string MoviePath { get; set; } = string.Empty; - } + public class GameCaptureStopRequest : PubSubEvent { } - public class CaptureStreamParameter - { - public class Frame - { - public int Width { get; set; } - public int Height { get; set; } - public int Stride { get; set; } - public byte[]? Data { get; set; } - } - public List Frames { get; set; } = new List(); - } - - public class GameCaptureFinishEventParameter - { - public CaptureStreamParameter? CaptureStreamParameter { get; set; } - public OBSStudioParameter? OBSStudioParameter { get; set; } - } - - public class GameCaptureStartEvent : PubSubEvent { } - - public class GameCaptureFinishEvent : PubSubEvent { } + public class GameCaptureFinishEvent : PubSubEvent { } } From ee998a98bd1d6396d30639ae352720a8099e7027 Mon Sep 17 00:00:00 2001 From: KirisameMarisa Date: Fri, 1 Mar 2024 10:29:13 +0900 Subject: [PATCH 15/21] * add take movie button --- .../WPF/BocchiTracker.Client/App.xaml.cs | 11 +++++++++++ .../BocchiTracker.Client.csproj | 1 + .../ReportParts/UtilityViewModel.cs | 19 +++++++++++++++++++ .../Views/ReportParts/UtilityView.xaml | 12 ++++++++++++ 4 files changed, 43 insertions(+) diff --git a/Application/WPF/BocchiTracker.Client/App.xaml.cs b/Application/WPF/BocchiTracker.Client/App.xaml.cs index dd70e9b..e28264d 100644 --- a/Application/WPF/BocchiTracker.Client/App.xaml.cs +++ b/Application/WPF/BocchiTracker.Client/App.xaml.cs @@ -36,6 +36,7 @@ using BocchiTracker.Config; using BocchiTracker.CrossServiceUploader; using BocchiTracker.ModelEvent; +using BocchiTracker.GameCaptureRTC; namespace BocchiTracker.Client { @@ -56,6 +57,9 @@ protected override void OnExit(ExitEventArgs e) var connection = Container.Resolve(); connection.Stop(); + var recording = Container.Resolve(); + recording.Stop(); + base.OnExit(e); } @@ -128,6 +132,7 @@ protected override void OnInitialized() var cacheProvider = Container.Resolve(); var connection = Container.Resolve(); + var recording = Container.Resolve(); var dataRepository = Container.Resolve(); var issueInfoBundle = Container.Resolve(); var serviceClientFactory = Container.Resolve(); @@ -136,6 +141,7 @@ protected override void OnInitialized() authConfigRepositoryFactory.Initialize(Path.Combine("Configs", nameof(AuthConfig) + "s")); _ = connection.StartAsync(projectConfig.Port); + recording.Start(projectConfig.WebSocketPort, projectConfig, userConfig); cacheProvider.SetCacheDirectory(string.IsNullOrEmpty(projectConfig.CacheDirectory) ? Path.GetTempPath() : projectConfig.CacheDirectory); @@ -180,6 +186,11 @@ protected override void ConfigureModuleCatalog(IModuleCatalog moduleCatalog) moduleCatalog.AddModule(); moduleCatalog.AddModule(); moduleCatalog.AddModule(); + moduleCatalog.AddModule( + dependsOn: new string[] + { + typeof(ConfigModule).Name + }); moduleCatalog.AddModule( dependsOn: new string[] { diff --git a/Application/WPF/BocchiTracker.Client/BocchiTracker.Client.csproj b/Application/WPF/BocchiTracker.Client/BocchiTracker.Client.csproj index 2b7312e..11fbd02 100644 --- a/Application/WPF/BocchiTracker.Client/BocchiTracker.Client.csproj +++ b/Application/WPF/BocchiTracker.Client/BocchiTracker.Client.csproj @@ -19,6 +19,7 @@ + diff --git a/Application/WPF/BocchiTracker.Client/ViewModels/ReportParts/UtilityViewModel.cs b/Application/WPF/BocchiTracker.Client/ViewModels/ReportParts/UtilityViewModel.cs index ca95b61..397b127 100644 --- a/Application/WPF/BocchiTracker.Client/ViewModels/ReportParts/UtilityViewModel.cs +++ b/Application/WPF/BocchiTracker.Client/ViewModels/ReportParts/UtilityViewModel.cs @@ -30,6 +30,7 @@ using BocchiTracker.Client.Share.Commands; using BocchiTracker.ModelEvent; using BocchiTracker.IssueAssetCollector.Handlers.Log; +using BocchiTracker.IssueAssetCollector.Handlers.Movie; namespace BocchiTracker.Client.ViewModels.ReportParts { @@ -39,6 +40,7 @@ public class UtilityViewModel : BindableBase public ICommand TakeScreenshotCommand { get; private set; } public ICommand CaptureCoredumpCommand { get; private set; } + public ICommand TakeMovieCommand { get; private set; } public ICommand PostIssueCommand { get; private set; } [Required(ErrorMessage = "Required")] @@ -66,6 +68,7 @@ public UtilityViewModel( { TakeScreenshotCommand = new DelegateCommand(OnTakeScreenshot); CaptureCoredumpCommand = new DelegateCommand(OnCaptureCoredump); + TakeMovieCommand = new AsyncCommand(OnTakeMovie); PostIssueCommand = new AsyncCommand(OnPostIssue); PostServices = new ReactiveCollection(); @@ -189,6 +192,22 @@ public void OnTakeScreenshot() handler.Handle(_appStatusBundles.TrackerApplication, 0, _projectConfig.FileSaveDirectory); } + public async Task OnTakeMovie() + { + if (_projectConfig == null) + return; + + _eventAggregator.GetEvent().Publish(new ProgressEventParameter { Message = "Take movie" }); + { + await Task.Run(() => + { + var handler = _createActionHandler.Create(typeof(MovieHandler)); + handler.Handle(_appStatusBundles.TrackerApplication, 0, _projectConfig.FileSaveDirectory); + }); + } + _eventAggregator.GetEvent().Publish(); + } + public void OnConnectedCreateHandle(AppStatusBundle inAppStatusBundle) { { diff --git a/Application/WPF/BocchiTracker.Client/Views/ReportParts/UtilityView.xaml b/Application/WPF/BocchiTracker.Client/Views/ReportParts/UtilityView.xaml index e0919bd..899f39f 100644 --- a/Application/WPF/BocchiTracker.Client/Views/ReportParts/UtilityView.xaml +++ b/Application/WPF/BocchiTracker.Client/Views/ReportParts/UtilityView.xaml @@ -36,6 +36,18 @@ +