Skip to content

Commit 6f7ff20

Browse files
committed
wip: file capture and record
1 parent 92a0149 commit 6f7ff20

File tree

15 files changed

+291
-20
lines changed

15 files changed

+291
-20
lines changed

.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

example/common/cpp/libKmpWebrtc.cpp

Lines changed: 25 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() {
@@ -52,14 +52,18 @@ struct PcClientFactoryHolder {
5252
};
5353

5454
void* CreatePCClientFactory(struct PCClientFactoryConfig* config, PCClientFactoryErrorHandler handler, void* opaque) {
55-
KType(PeerConnectionClientFactory_PrivateConfig) private_config = KFunc(PeerConnectionClientFactory.PrivateConfig.PrivateConfig)();
55+
KType(PeerConnectionClientFactory_PrivateConfig) pconfig = KFunc(PeerConnectionClientFactory.PrivateConfig.PrivateConfig)();
5656
KType(PeerConnectionClientFactory_Config) k_config = KFunc(PeerConnectionClientFactory.Config.Config)(
5757
(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);
58+
config->video_capture_fps, 0, pconfig);
59+
#if defined(WEBRTC_WIN)
60+
KType(WinPrivateConfig) private_config = KFunc(WinPrivateConfig.WinPrivateConfig)(config->private_config.hwnd, config->private_config.disable_encryption);
61+
#else
62+
KType(LinuxPrivateConfig) private_config = KFunc(LinuxPrivateConfig.LinuxPrivateConfig)(config->private_config.hwnd, config->private_config.disable_encryption, config->private_config.capture_file_path);
63+
#endif
64+
KType(PeerConnectionClientFactory_Config) k_config_with_pri = KFunc(utils.createPcClientFactoryConfig)(k_config, private_config);
6165

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

6468
PcClientFactoryHolder* holder = new PcClientFactoryHolder();
6569
holder->factory = KFunc(createPeerConnectionClientFactory)(k_config_with_pri, error_handler);
@@ -69,7 +73,7 @@ void* CreatePCClientFactory(struct PCClientFactoryConfig* config, PCClientFactor
6973
void DestroyPCClientFactory(void** pc_client_factory) {
7074
PcClientFactoryHolder* holder = reinterpret_cast<PcClientFactoryHolder*>(*pc_client_factory);
7175
KFunc(PeerConnectionClientFactory.destroyPeerConnectionFactory)(holder->factory);
72-
delete (*pc_client_factory);
76+
delete (holder);
7377
*pc_client_factory = nullptr;
7478
}
7579

@@ -106,13 +110,13 @@ void* CreatePeerConnectionClient(void* pc_client_factory, const char* peer_uid,
106110
void ClosePeerConnectionClient(void** pc_client) {
107111
PcClientHolder* holder = reinterpret_cast<PcClientHolder*>(*pc_client);
108112
KFunc(PeerConnectionClient.close)(holder->client);
109-
delete (*pc_client);
113+
delete (holder);
110114
*pc_client = nullptr;
111115
}
112116

113117
void CreatePeerConnection(void* pc_client) {
114118
PcClientHolder* holder = reinterpret_cast<PcClientHolder*>(pc_client);
115-
kmp_webrtc_kref_kotlin_collections_List ice_servers = KFunc(utils.emptyIceServers)();
119+
KT_SYMBOL(kmp_webrtc_kref_kotlin_collections_List) ice_servers = KFunc(utils.emptyIceServers)();
116120
KFunc(PeerConnectionClient.createPeerConnection)(holder->client, ice_servers);
117121
}
118122

@@ -138,6 +142,16 @@ void GetStats(void* pc_client) {
138142
KFunc(PeerConnectionClient.getStats)(holder->client);
139143
}
140144

145+
int StartRecorder(void* pc_client, int dir, const char* path) {
146+
PcClientHolder* holder = reinterpret_cast<PcClientHolder*>(pc_client);
147+
return KFunc(PeerConnectionClient.startRecorder)(holder->client, dir, path);
148+
}
149+
150+
int StopRecorder(void* pc_client, int dir) {
151+
PcClientHolder* holder = reinterpret_cast<PcClientHolder*>(pc_client);
152+
return KFunc(PeerConnectionClient.stopRecorder)(holder->client, dir);
153+
}
154+
141155
#if defined(WEBRTC_WIN)
142156
void AddRemoteTrackRenderer(void* pc_client, void* renderer) {
143157
PcClientHolder* holder = reinterpret_cast<PcClientHolder*>(pc_client);

example/common/cpp/libKmpWebrtc.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ enum KmpWebRTCError {
5050
struct PCClientFactoryPrivateConfig {
5151
void* hwnd;
5252
int disable_encryption;
53+
const char* capture_file_path;
5354
};
5455

5556
struct PCClientFactoryConfig {
@@ -84,6 +85,9 @@ KMP_WEBRTC_API void SetRemoteDescription(void* pc_client, KmpWebRTCSdpType type,
8485
KMP_WEBRTC_API void AddIceCandidate(void* pc_client, const char* sdp_mid, int m_line_index, const char* sdp);
8586
KMP_WEBRTC_API void GetStats(void* pc_client);
8687

88+
KMP_WEBRTC_API int StartRecorder(void* pc_client, int dir, const char* path);
89+
KMP_WEBRTC_API int StopRecorder(void* pc_client, int dir);
90+
8791
#if defined(WEBRTC_WIN)
8892
KMP_WEBRTC_API void AddRemoteTrackRenderer(void* pc_client, void* renderer);
8993
#endif

example/linuxApp/CMakeLists.txt

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
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+
find_package(Qt5 REQUIRED COMPONENTS Widgets)
8+
9+
include_directories(${Qt5Widgets_INCLUDE_DIRS})
10+
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/../../libs/linux/x64)
11+
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/../../libs/windows_linux/include)
12+
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/../common/cpp)
13+
14+
set(CMAKE_AUTOMOC ON)
15+
set(CMAKE_AUTORCC ON)
16+
set(CMAKE_AUTOUIC ON)
17+
18+
add_definitions(-DWEBRTC_LINUX=1)
19+
20+
file(GLOB deps
21+
${CMAKE_CURRENT_SOURCE_DIR}/../../libs/linux/x64/libkmp_webrtc.so
22+
${CMAKE_CURRENT_SOURCE_DIR}/../../libs/linux/x64/liblinux_pc_client.so
23+
)
24+
25+
add_executable(${PROJECT_NAME}
26+
main.cpp
27+
main_window.cpp
28+
main_window.h
29+
loopback.cpp
30+
${CMAKE_CURRENT_SOURCE_DIR}/../common/cpp/libKmpWebrtc.cpp
31+
)
32+
33+
target_link_libraries(${PROJECT_NAME}
34+
${Qt5Widgets_LIBRARIES}
35+
${deps}
36+
)
37+
38+
add_executable(loopback
39+
console_app.cpp
40+
loopback.cpp
41+
${CMAKE_CURRENT_SOURCE_DIR}/../common/cpp/libKmpWebrtc.cpp
42+
)
43+
44+
target_link_libraries(loopback
45+
${deps}
46+
)

example/linuxApp/console_app.cpp

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
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+
loopback(argv[1]);
22+
while (running) {
23+
sleep(100);
24+
}
25+
return 0;
26+
}

example/linuxApp/loopback.cpp

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
#include "loopback.h"
2+
3+
#include <iostream>
4+
#include <string>
5+
#include <sstream>
6+
#include <cwchar>
7+
#include <cstring>
8+
#include <chrono>
9+
#include <inttypes.h>
10+
11+
#include "libKmpWebrtc.h"
12+
13+
void* gPcClientFactory = nullptr;
14+
void* gPcClient = nullptr;
15+
PCClientCallback* gPcClientCallback = nullptr;
16+
17+
static void pcClientFactoryErrorHandler(void*, int error, const char* message) {
18+
std::ostringstream oss;
19+
oss << "pcClientFactoryError code: " << error << ", message: " << message;
20+
LogInfo(oss.str().c_str());
21+
}
22+
23+
static const char* pcClientOnPreferCodecs(void*, const char* peer_uid, const char* sdp) {
24+
return sdp;
25+
}
26+
27+
static void pcClientOnPeerConnectionStatsReady(void*, const char* peer_uid, const char* stats) {
28+
std::ostringstream oss;
29+
oss << "pcClientOnPeerConnectionStatsReady " << peer_uid << " " << stats;
30+
LogInfo(oss.str().c_str());
31+
}
32+
33+
static void pcClientOnIceDisconnected(void*, const char* peer_uid) {
34+
}
35+
36+
static void pcClientOnError(void*, const char* peer_uid, int code) {
37+
std::ostringstream oss;
38+
oss << "pcClientError code: " << code;
39+
LogInfo(oss.str().c_str());
40+
}
41+
42+
static void pcClientOnLocalDescription(void*, const char* peer_uid, int type, const char* sdp);
43+
static void pcClientOnIceCandidate(void*, const char* peer_uid, const char* sdp_mid, int m_line_index, const char* sdp);
44+
static void pcClientOnIceConnected(void*, const char* peer_uid);
45+
46+
void loopback(const char* path) {
47+
// 1. initialize
48+
InitializeWebRTC("", true);
49+
50+
// 2. create gPcClientFactory
51+
PCClientFactoryConfig* config = DefaultPCClientFactoryConfig();
52+
config->video_capture_impl = kKmpWebRTCCaptureFile;
53+
config->private_config.disable_encryption = 1;
54+
config->private_config.capture_file_path = path;
55+
gPcClientFactory = CreatePCClientFactory(config, pcClientFactoryErrorHandler, nullptr);
56+
PCClientFactoryConfigDestroy(&config);
57+
58+
// 3. create local tracks
59+
CreateLocalTracks(gPcClientFactory);
60+
61+
// 4. add local preview & start camera capture
62+
StartVideoCapture(gPcClientFactory);
63+
64+
// 5. create PcClient
65+
gPcClientCallback = new PCClientCallback();
66+
gPcClientCallback->on_prefer_codecs = pcClientOnPreferCodecs;
67+
gPcClientCallback->on_local_description = pcClientOnLocalDescription;
68+
gPcClientCallback->on_ice_candidate = pcClientOnIceCandidate;
69+
gPcClientCallback->on_stats_ready = pcClientOnPeerConnectionStatsReady;
70+
gPcClientCallback->on_ice_connected = pcClientOnIceConnected;
71+
gPcClientCallback->on_ice_disconnected = pcClientOnIceDisconnected;
72+
gPcClientCallback->on_error = pcClientOnError;
73+
gPcClient = CreatePeerConnectionClient(gPcClientFactory, "test", kKmpWebRTCDirSendRecv, 1, 800, 30, gPcClientCallback, nullptr);
74+
75+
// 6. create pc
76+
CreatePeerConnection(gPcClient);
77+
78+
// 7. create offer
79+
CreateOffer(gPcClient);
80+
}
81+
82+
static void pcClientOnLocalDescription(void*, const char* peer_uid, int type, const char* sdp) {
83+
std::ostringstream oss;
84+
oss << "pcClientOnLocalDescription " << peer_uid << " " << sdp;
85+
LogInfo(oss.str().c_str());
86+
// 8. send offer to remote, get answer from remote, and set answer
87+
SetRemoteDescription(gPcClient, kKmpWebRTCSdpAnswer, sdp);
88+
}
89+
90+
static void pcClientOnIceCandidate(void*, const char* peer_uid, const char* sdp_mid, int m_line_index, const char* sdp) {
91+
std::ostringstream oss;
92+
oss << "pcClientOnIceCandidate " << peer_uid << " " << sdp;
93+
LogInfo(oss.str().c_str());
94+
// 9. send ice candidate to remote, get ice candidate from remote, add ice candidate
95+
AddIceCandidate(gPcClient, sdp_mid, m_line_index, sdp);
96+
}
97+
98+
static void pcClientOnIceConnected(void*, const char* peer_uid) {
99+
std::ostringstream oss;
100+
oss << "pcClientOnIceConnected " << peer_uid;
101+
LogInfo(oss.str().c_str());
102+
103+
// 10. on ice connected, add renderer for remote stream
104+
StartRecorder(gPcClient, kKmpWebRTCDirRecvOnly, "recv.mkv");
105+
}

example/linuxApp/loopback.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
#ifndef LOOPBACK_H
2+
#define LOOPBACK_H
3+
4+
void loopback(const char* path);
5+
6+
#endif // LOOPBACK_H

example/linuxApp/main.cpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
#include <QApplication>
2+
#include "main_window.h"
3+
4+
int main(int argc, char *argv[]) {
5+
QApplication app(argc, argv);
6+
MainWindow mainWindow;
7+
mainWindow.show();
8+
return app.exec();
9+
}

example/linuxApp/main_window.cpp

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
#include "main_window.h"
2+
3+
#include "loopback.h"
4+
5+
MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) {
6+
button = new QPushButton("click me", this);
7+
setCentralWidget(button);
8+
9+
connect(button, &QPushButton::clicked, this, &MainWindow::onButtonClicked);
10+
11+
setGeometry(100, 100, 400, 300);
12+
}
13+
14+
MainWindow::~MainWindow() {
15+
}
16+
17+
void MainWindow::onButtonClicked() {
18+
loopback("");
19+
}

example/linuxApp/main_window.h

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
#ifndef MAINWINDOW_H
2+
#define MAINWINDOW_H
3+
4+
#include <QMainWindow>
5+
#include <QPushButton>
6+
#include <QMessageBox>
7+
8+
class MainWindow : public QMainWindow {
9+
Q_OBJECT
10+
11+
public:
12+
MainWindow(QWidget *parent = nullptr);
13+
~MainWindow();
14+
15+
private slots:
16+
void onButtonClicked();
17+
18+
private:
19+
QPushButton *button;
20+
};
21+
22+
#endif // MAINWINDOW_H

0 commit comments

Comments
 (0)