Skip to content

Commit 4bf975c

Browse files
authored
Linux support (#2)
* wip * wip: file capture and record * add sdp manipulator * update demo * linux demo basically works * kt commit * add ci * fix ci
1 parent f7445e9 commit 4bf975c

File tree

38 files changed

+1058
-99
lines changed

38 files changed

+1058
-99
lines changed

.github/workflows/ci.yaml

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -19,20 +19,22 @@ jobs:
1919
fail-fast: false
2020
matrix:
2121
include:
22-
- cmd: "./gradlew :kmp-webrtc:testDebugUnitTest"
23-
os: macos-latest
24-
- cmd: "./gradlew :example:androidApp:assembleDebug"
25-
os: macos-latest
26-
- cmd: "./scripts/build_ios_demo.sh"
27-
os: macos-latest
22+
- os: macos-latest
23+
cmd: "./gradlew :kmp-webrtc:testDebugUnitTest"
24+
- os: macos-latest
25+
cmd: "./gradlew :example:androidApp:assembleDebug"
26+
- os: macos-latest
2827
dep: "brew update && brew install cocoapods xcodegen"
29-
- cmd: "./scripts/build_mac_demo.sh"
30-
os: macos-latest
28+
cmd: "./scripts/build_ios_demo.sh"
29+
- os: macos-latest
3130
dep: "brew update && brew install cocoapods xcodegen"
32-
- cmd: ".\\scripts\\setup_windows.bat \"C:\\Program Files\\Microsoft Visual Studio\\2022\\Enterprise\\VC\\Tools\\MSVC\\14.43.34808\\bin\\Hostx64\\x64\\lib.exe\" && cd example\\winApp && msbuild winApp.vcxproj /t:Build /p:Configuration=Release /p:Platform=x64"
33-
os: windows-latest
34-
- cmd: "./gradlew :example:webApp:jsBrowserDistribution"
35-
os: macos-latest
31+
cmd: "./scripts/build_mac_demo.sh"
32+
- os: windows-latest
33+
cmd: ".\\scripts\\setup_windows.bat \"C:\\Program Files\\Microsoft Visual Studio\\2022\\Enterprise\\VC\\Tools\\MSVC\\14.43.34808\\bin\\Hostx64\\x64\\lib.exe\" && cd example\\winApp && msbuild winApp.vcxproj /t:Build /p:Configuration=Release /p:Platform=x64"
34+
- os: macos-latest
35+
cmd: "./gradlew :example:webApp:jsBrowserDistribution"
36+
- os: ubuntu-latest
37+
cmd: "./scripts/build_linux_demo.sh"
3638
runs-on: ${{ matrix.os }}
3739
permissions:
3840
pull-requests: write

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,9 @@ captures
99
.cxx
1010
local.properties
1111

12+
*.mp4
13+
*.mkv
14+
1215
xcuserdata
1316
**.podspec
1417
**.xcodeproj

README.md

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ KMP wrapper for WebRTC.
1313
| `macOS` | 🚀 |
1414
| `Windows X64` | 🚀 |
1515
| `JS` (Chrome) | 🚀 |
16-
| `Linux X64` | 🔮 |
16+
| `Linux X64` | 🚀 |
1717

1818
## Dependency
1919

@@ -89,6 +89,13 @@ Open the project (the repo root dir) in Android studio, and run the example.andr
8989
# open example\winApp\winApp.sln in Visual Studio 2022, and run it.
9090
```
9191

92+
### Linux
93+
94+
```bash
95+
./scripts/build_linux_demo.sh
96+
./example/linuxApp/build/loopback <path to video file>
97+
```
98+
9299
### JS
93100

94101
```bash
@@ -115,6 +122,14 @@ File structure for Windows:
115122
- kmp-webrtc
116123
```
117124

125+
File structure for Linux:
126+
127+
```
128+
- webrtc_android
129+
- src
130+
- kmp-webrtc
131+
```
132+
118133
### Android
119134

120135
CPP code need to be built on Linux.
@@ -148,6 +163,14 @@ In `x64 Native Tools Command Prompt for VS 2022`:
148163
.\sdk\build_windows_libs.bat ..\..\kmp-webrtc
149164
```
150165

166+
### Linux
167+
168+
```bash
169+
pushd ../webrtc_android/src/ && \
170+
./sdk/build_linux_libs.sh ../../kmp-webrtc/libs --skip-build-ffmpeg && \
171+
popd
172+
```
173+
151174
### Upload libs zip
152175

153176
```bash

example/common/cpp/libKmpWebrtc.cpp

Lines changed: 34 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -20,16 +20,16 @@
2020
#error "Unknown target"
2121
#endif
2222

23-
#define KFunc(NAME) g_lib->kotlin.root.com.piasy.kmp.webrtc.##NAME
24-
#define KType(NAME) kmp_webrtc_kref_com_piasy_kmp_webrtc_##NAME
23+
#define KFunc(NAME) g_lib->kotlin.root.com.piasy.kmp.webrtc.NAME
24+
#define KType(NAME) KT_SYMBOL(kmp_webrtc_kref_com_piasy_kmp_webrtc_##NAME)
2525

2626
static KT_SYMBOL(kmp_webrtc_ExportedSymbols)* g_lib = nullptr;
2727

2828
int InitializeWebRTC(const char* field_trials, int debug_log) {
2929
if (!g_lib) {
3030
g_lib = KT_SYMBOL(kmp_webrtc_symbols)();
3131
}
32-
return KFunc(initializeWebRTC)(kmp_webrtc_kref_kotlin_Any(), field_trials, debug_log);
32+
return KFunc(initializeWebRTC)(KT_SYMBOL(kmp_webrtc_kref_kotlin_Any)(), field_trials, debug_log);
3333
}
3434

3535
struct PCClientFactoryConfig* DefaultPCClientFactoryConfig() {
@@ -39,6 +39,11 @@ struct PCClientFactoryConfig* DefaultPCClientFactoryConfig() {
3939
config->video_capture_height = 720;
4040
config->video_capture_fps = 30;
4141
config->private_config.hwnd = nullptr;
42+
config->private_config.disable_encryption = 0;
43+
config->private_config.dummy_audio_device = 0;
44+
config->private_config.transit_video = 0;
45+
config->private_config.capture_file_path = "";
46+
config->private_config.capture_dump_path = "";
4247
return config;
4348
}
4449

@@ -52,14 +57,18 @@ struct PcClientFactoryHolder {
5257
};
5358

5459
void* CreatePCClientFactory(struct PCClientFactoryConfig* config, PCClientFactoryErrorHandler handler, void* opaque) {
55-
KType(PeerConnectionClientFactory_PrivateConfig) private_config = KFunc(PeerConnectionClientFactory.PrivateConfig.PrivateConfig)();
60+
KType(PeerConnectionClientFactory_PrivateConfig) pconfig = KFunc(PeerConnectionClientFactory.PrivateConfig.PrivateConfig)();
5661
KType(PeerConnectionClientFactory_Config) k_config = KFunc(PeerConnectionClientFactory.Config.Config)(
5762
(int) config->video_capture_impl, config->video_capture_width, config->video_capture_height,
58-
config->video_capture_fps, 0, private_config);
59-
KType(WinPrivateConfig) win_private_config = KFunc(WinPrivateConfig.WinPrivateConfig)(config->private_config.hwnd, config->private_config.disable_encryption);
60-
KType(PeerConnectionClientFactory_Config) k_config_with_pri = KFunc(utils.createPcClientFactoryConfig)(k_config, win_private_config);
63+
config->video_capture_fps, 0, pconfig);
64+
#if defined(WEBRTC_WIN)
65+
KType(WinPrivateConfig) private_config = KFunc(WinPrivateConfig.WinPrivateConfig)(config->private_config.hwnd, config->private_config.disable_encryption);
66+
#else
67+
KType(LinuxPrivateConfig) private_config = KFunc(LinuxPrivateConfig.LinuxPrivateConfig)(config->private_config.hwnd, config->private_config.disable_encryption, config->private_config.dummy_audio_device, config->private_config.transit_video, config->private_config.capture_file_path, config->private_config.capture_dump_path);
68+
#endif
69+
KType(PeerConnectionClientFactory_Config) k_config_with_pri = KFunc(utils.createPcClientFactoryConfig)(k_config, private_config);
6170

62-
kmp_webrtc_kref_kotlin_Function2 error_handler = KFunc(utils.createErrorHandler)(handler, opaque);
71+
KT_SYMBOL(kmp_webrtc_kref_kotlin_Function2) error_handler = KFunc(utils.createErrorHandler)((void*) handler, opaque);
6372

6473
PcClientFactoryHolder* holder = new PcClientFactoryHolder();
6574
holder->factory = KFunc(createPeerConnectionClientFactory)(k_config_with_pri, error_handler);
@@ -69,7 +78,7 @@ void* CreatePCClientFactory(struct PCClientFactoryConfig* config, PCClientFactor
6978
void DestroyPCClientFactory(void** pc_client_factory) {
7079
PcClientFactoryHolder* holder = reinterpret_cast<PcClientFactoryHolder*>(*pc_client_factory);
7180
KFunc(PeerConnectionClientFactory.destroyPeerConnectionFactory)(holder->factory);
72-
delete (*pc_client_factory);
81+
delete (holder);
7382
*pc_client_factory = nullptr;
7483
}
7584

@@ -106,13 +115,13 @@ void* CreatePeerConnectionClient(void* pc_client_factory, const char* peer_uid,
106115
void ClosePeerConnectionClient(void** pc_client) {
107116
PcClientHolder* holder = reinterpret_cast<PcClientHolder*>(*pc_client);
108117
KFunc(PeerConnectionClient.close)(holder->client);
109-
delete (*pc_client);
118+
delete (holder);
110119
*pc_client = nullptr;
111120
}
112121

113122
void CreatePeerConnection(void* pc_client) {
114123
PcClientHolder* holder = reinterpret_cast<PcClientHolder*>(pc_client);
115-
kmp_webrtc_kref_kotlin_collections_List ice_servers = KFunc(utils.emptyIceServers)();
124+
KT_SYMBOL(kmp_webrtc_kref_kotlin_collections_List) ice_servers = KFunc(utils.emptyIceServers)();
116125
KFunc(PeerConnectionClient.createPeerConnection)(holder->client, ice_servers);
117126
}
118127

@@ -138,6 +147,16 @@ void GetStats(void* pc_client) {
138147
KFunc(PeerConnectionClient.getStats)(holder->client);
139148
}
140149

150+
int StartRecorder(void* pc_client, int dir, const char* path) {
151+
PcClientHolder* holder = reinterpret_cast<PcClientHolder*>(pc_client);
152+
return KFunc(PeerConnectionClient.startRecorder)(holder->client, dir, path);
153+
}
154+
155+
int StopRecorder(void* pc_client, int dir) {
156+
PcClientHolder* holder = reinterpret_cast<PcClientHolder*>(pc_client);
157+
return KFunc(PeerConnectionClient.stopRecorder)(holder->client, dir);
158+
}
159+
141160
#if defined(WEBRTC_WIN)
142161
void AddRemoteTrackRenderer(void* pc_client, void* renderer) {
143162
PcClientHolder* holder = reinterpret_cast<PcClientHolder*>(pc_client);
@@ -148,3 +167,7 @@ void AddRemoteTrackRenderer(void* pc_client, void* renderer) {
148167
void LogInfo(const char* log) {
149168
KFunc(utils.logInfo)(log);
150169
}
170+
171+
const char* PreferSdp(const char* sdp, int codec) {
172+
return KFunc(utils.preferCodec)(sdp, codec);
173+
}

example/common/cpp/libKmpWebrtc.h

Lines changed: 25 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -16,20 +16,29 @@
1616
extern "C" {
1717
#endif
1818

19-
enum KmpWebRTCCaptureImpl {
20-
kKmpWebRTCCaptureSystemCamera = 1,
21-
kKmpWebRTCCaptureScreen = 2,
22-
kKmpWebRTCCaptureFile = 3,
23-
kKmpWebRTCCaptureApp = 4,
24-
};
25-
2619
enum KmpWebRTCDir {
2720
kKmpWebRTCDirSendRecv = 0,
2821
kKmpWebRTCDirSendOnly = 1,
2922
kKmpWebRTCDirRecvOnly = 2,
3023
kKmpWebRTCDirInactive = 3,
3124
};
3225

26+
enum KmpWebRTCVideoCodec {
27+
kKmpWebRTCVideoCodecVP8 = 1,
28+
kKmpWebRTCVideoCodecVP9 = 2,
29+
kKmpWebRTCVideoCodecH264Baseline = 3,
30+
kKmpWebRTCVideoCodecH264HighProfile = 4,
31+
kKmpWebRTCVideoCodecH265 = 5,
32+
kKmpWebRTCVideoCodecAV1 = 6,
33+
};
34+
35+
enum KmpWebRTCCaptureImpl {
36+
kKmpWebRTCCaptureSystemCamera = 1,
37+
kKmpWebRTCCaptureScreen = 2,
38+
kKmpWebRTCCaptureFile = 3,
39+
kKmpWebRTCCaptureApp = 4,
40+
};
41+
3342
enum KmpWebRTCSdpType {
3443
kKmpWebRTCSdpOffer = 1,
3544
kKmpWebRTCSdpPrAnswer = 2,
@@ -50,6 +59,10 @@ enum KmpWebRTCError {
5059
struct PCClientFactoryPrivateConfig {
5160
void* hwnd;
5261
int disable_encryption;
62+
int dummy_audio_device;
63+
int transit_video;
64+
const char* capture_file_path;
65+
const char* capture_dump_path;
5366
};
5467

5568
struct PCClientFactoryConfig {
@@ -84,12 +97,17 @@ KMP_WEBRTC_API void SetRemoteDescription(void* pc_client, KmpWebRTCSdpType type,
8497
KMP_WEBRTC_API void AddIceCandidate(void* pc_client, const char* sdp_mid, int m_line_index, const char* sdp);
8598
KMP_WEBRTC_API void GetStats(void* pc_client);
8699

100+
KMP_WEBRTC_API int StartRecorder(void* pc_client, int dir, const char* path);
101+
KMP_WEBRTC_API int StopRecorder(void* pc_client, int dir);
102+
87103
#if defined(WEBRTC_WIN)
88104
KMP_WEBRTC_API void AddRemoteTrackRenderer(void* pc_client, void* renderer);
89105
#endif
90106

91107
KMP_WEBRTC_API void LogInfo(const char* log);
92108

109+
KMP_WEBRTC_API const char* PreferSdp(const char* sdp, int codec);
110+
93111
#if __cplusplus
94112
}
95113
#endif

example/linuxApp/CMakeLists.txt

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
cmake_minimum_required(VERSION 3.10)
2+
project(linuxApp)
3+
4+
set(CMAKE_CXX_STANDARD 11)
5+
set(CMAKE_CXX_STANDARD_REQUIRED True)
6+
7+
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/../../libs/linux/x64)
8+
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/../../libs/windows_linux/include)
9+
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/../common/cpp)
10+
11+
add_definitions(-DWEBRTC_LINUX=1)
12+
13+
file(GLOB deps
14+
${CMAKE_CURRENT_SOURCE_DIR}/../../libs/linux/x64/libkmp_webrtc.so
15+
${CMAKE_CURRENT_SOURCE_DIR}/../../libs/linux/x64/liblinux_pc_client.so
16+
)
17+
18+
set(BUILD_QT_DEMO FALSE)
19+
if (BUILD_QT_DEMO)
20+
find_package(Qt5 REQUIRED COMPONENTS Widgets)
21+
22+
include_directories(${Qt5Widgets_INCLUDE_DIRS})
23+
24+
set(CMAKE_AUTOMOC ON)
25+
set(CMAKE_AUTORCC ON)
26+
set(CMAKE_AUTOUIC ON)
27+
28+
add_executable(${PROJECT_NAME}
29+
main.cpp
30+
main_window.cpp
31+
main_window.h
32+
loopback.cpp
33+
${CMAKE_CURRENT_SOURCE_DIR}/../common/cpp/libKmpWebrtc.cpp
34+
)
35+
36+
target_link_libraries(${PROJECT_NAME}
37+
${Qt5Widgets_LIBRARIES}
38+
${deps}
39+
)
40+
endif()
41+
42+
43+
add_executable(loopback
44+
console_app.cpp
45+
loopback.cpp
46+
${CMAKE_CURRENT_SOURCE_DIR}/../common/cpp/libKmpWebrtc.cpp
47+
)
48+
49+
target_link_libraries(loopback
50+
${deps}
51+
)
52+
53+
set(BUILD_FFMPEG_TEST FALSE)
54+
if (BUILD_FFMPEG_TEST)
55+
set(FFMPEG_INCLUDE_DIR /home/linker/src/FFmpeg/build/include)
56+
set(FFMPEG_LIB_DIR /home/linker/src/FFmpeg/build/lib)
57+
include_directories(${FFMPEG_INCLUDE_DIR})
58+
59+
add_executable(ffmpeg_test
60+
ffmpeg_test.cpp
61+
)
62+
63+
target_link_libraries(ffmpeg_test
64+
${FFMPEG_LIB_DIR}/libavcodec.so
65+
${FFMPEG_LIB_DIR}/libavformat.so
66+
${FFMPEG_LIB_DIR}/libavutil.so
67+
)
68+
endif ()

example/linuxApp/console_app.cpp

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
#include "loopback.h"
2+
3+
#include <atomic>
4+
#include <signal.h>
5+
#include <stdio.h>
6+
#include <unistd.h>
7+
8+
std::atomic<bool> running(true);
9+
10+
void signal_handler(int sig) {
11+
running = false;
12+
}
13+
14+
int main(int argc, char *argv[]) {
15+
if (argc != 2) {
16+
printf("Usage: loopback <input video file path>\n");
17+
return -1;
18+
}
19+
20+
signal(SIGINT, signal_handler);
21+
startLoopback(argv[1]);
22+
while (running) {
23+
sleep(100);
24+
}
25+
stopLoopback();
26+
return 0;
27+
}

0 commit comments

Comments
 (0)