Skip to content

Commit 82cd65e

Browse files
some minor polish (#9)
Implement VideoFrame, Local/RemoteVideoTrack, Video Publication classes and integrate them into SimpleRoom example
1 parent e568069 commit 82cd65e

15 files changed

+1283
-13
lines changed

CMakeLists.txt

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,7 @@ add_library(livekit
163163
include/livekit/ffi_handle.h
164164
include/livekit/ffi_client.h
165165
include/livekit/local_audio_track.h
166+
include/livekit/remote_audio_track.h
166167
include/livekit/participant.h
167168
include/livekit/local_participant.h
168169
include/livekit/livekit.h
@@ -171,11 +172,16 @@ add_library(livekit
171172
include/livekit/track_publication.h
172173
include/livekit/local_track_publication.h
173174
include/livekit/remote_track_publication.h
175+
include/livekit/video_frame.h
176+
include/livekit/video_source.h
177+
include/livekit/local_video_track.h
178+
include/livekit/remote_video_track.h
174179
src/audio_frame.cpp
175180
src/audio_source.cpp
176181
src/ffi_handle.cpp
177182
src/ffi_client.cpp
178183
src/local_audio_track.cpp
184+
src/remote_audio_track.cpp
179185
src/room.cpp
180186
src/room_proto_converter.cpp
181187
src/room_proto_converter.h
@@ -187,6 +193,12 @@ add_library(livekit
187193
src/track_publication.cpp
188194
src/local_track_publication.cpp
189195
src/remote_track_publication.cpp
196+
src/video_frame.cpp
197+
src/video_source.cpp
198+
src/local_video_track.cpp
199+
src/remote_video_track.cpp
200+
src/video_utils.cpp
201+
src/video_utils.h
190202
)
191203

192204
# Add generated proto objects to the wrapper

examples/simple_room/main.cpp

Lines changed: 104 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,9 @@
1313
// TODO(shijing), remove this livekit_ffi.h as it should be internal only.
1414
#include "livekit_ffi.h"
1515

16+
// Consider expose this video_utils.h to public ?
17+
#include "video_utils.h"
18+
1619
using namespace livekit;
1720

1821
namespace {
@@ -160,6 +163,58 @@ void runNoiseCaptureLoop(const std::shared_ptr<AudioSource> &source) {
160163
std::cout << "Error in clearQueue" << std::endl;
161164
}
162165
}
166+
167+
void runFakeVideoCaptureLoop(const std::shared_ptr<VideoSource> &source) {
168+
auto frame = LKVideoFrame::create(1280, 720, VideoBufferType::ARGB);
169+
double framerate = 1.0 / 30;
170+
while (g_running.load(std::memory_order_relaxed)) {
171+
static auto start = std::chrono::high_resolution_clock::now();
172+
float t = std::chrono::duration<float>(
173+
std::chrono::high_resolution_clock::now() - start)
174+
.count();
175+
// Cycle every 4 seconds: 0=red, 1=green, 2=blue, 3 black
176+
int stage = static_cast<int>(t) % 4;
177+
std::vector<int> rgb(4);
178+
switch (stage) {
179+
case 0: // red
180+
rgb[0] = 255;
181+
rgb[1] = 0;
182+
rgb[2] = 0;
183+
break;
184+
case 1: // green
185+
rgb[0] = 0;
186+
rgb[1] = 255;
187+
rgb[2] = 0;
188+
break;
189+
case 2: // blue
190+
rgb[0] = 0;
191+
rgb[1] = 0;
192+
rgb[2] = 255;
193+
break;
194+
case 4: // black
195+
rgb[0] = 0;
196+
rgb[1] = 0;
197+
rgb[2] = 0;
198+
}
199+
for (size_t i = 0; i < frame.dataSize(); i += 4) {
200+
frame.data()[i] = 255;
201+
frame.data()[i + 1] = rgb[0];
202+
frame.data()[i + 2] = rgb[1];
203+
frame.data()[i + 3] = rgb[2];
204+
}
205+
LKVideoFrame i420 = convertViaFfi(frame, VideoBufferType::I420, false);
206+
try {
207+
source->captureFrame(frame, 0, VideoRotation::VIDEO_ROTATION_0);
208+
} catch (const std::exception &e) {
209+
// If something goes wrong, log and break out
210+
std::cerr << "Error in captureFrame: " << e.what() << std::endl;
211+
break;
212+
}
213+
214+
std::this_thread::sleep_for(std::chrono::duration<double>(framerate));
215+
}
216+
}
217+
163218
} // namespace
164219

165220
int main(int argc, char *argv[]) {
@@ -210,35 +265,64 @@ int main(int argc, char *argv[]) {
210265
<< info.reliable_dc_buffered_amount_low_threshold << "\n"
211266
<< " Creation time (ms): " << info.creation_time << "\n";
212267

268+
// Setup Audio Source / Track
213269
auto audioSource = std::make_shared<AudioSource>(44100, 1, 10);
214270
auto audioTrack =
215271
LocalAudioTrack::createLocalAudioTrack("micTrack", audioSource);
216272

217-
TrackPublishOptions opts;
218-
opts.source = TrackSource::SOURCE_MICROPHONE;
219-
opts.dtx = false;
220-
opts.simulcast = false;
221-
273+
TrackPublishOptions audioOpts;
274+
audioOpts.source = TrackSource::SOURCE_MICROPHONE;
275+
audioOpts.dtx = false;
276+
audioOpts.simulcast = false;
277+
std::shared_ptr<LocalTrackPublication> audioPub;
222278
try {
223279
// publishTrack takes std::shared_ptr<Track>, LocalAudioTrack derives from
224280
// Track
225-
auto pub = room.local_participant()->publishTrack(audioTrack, opts);
281+
audioPub = room.local_participant()->publishTrack(audioTrack, audioOpts);
226282

227283
std::cout << "Published track:\n"
228-
<< " SID: " << pub->sid() << "\n"
229-
<< " Name: " << pub->name() << "\n"
230-
<< " Kind: " << static_cast<int>(pub->kind()) << "\n"
231-
<< " Source: " << static_cast<int>(pub->source()) << "\n"
232-
<< " Simulcasted: " << std::boolalpha << pub->simulcasted()
284+
<< " SID: " << audioPub->sid() << "\n"
285+
<< " Name: " << audioPub->name() << "\n"
286+
<< " Kind: " << static_cast<int>(audioPub->kind()) << "\n"
287+
<< " Source: " << static_cast<int>(audioPub->source()) << "\n"
288+
<< " Simulcasted: " << std::boolalpha << audioPub->simulcasted()
233289
<< "\n"
234-
<< " Muted: " << std::boolalpha << pub->muted() << "\n";
290+
<< " Muted: " << std::boolalpha << audioPub->muted() << "\n";
235291
} catch (const std::exception &e) {
236292
std::cerr << "Failed to publish track: " << e.what() << std::endl;
237293
}
238294

239295
// TODO, if we have pre-buffering feature, we might consider starting the
240296
// thread right after creating the source.
241297
std::thread audioThread(runNoiseCaptureLoop, audioSource);
298+
299+
// Setup Video Source / Track
300+
auto videoSource = std::make_shared<VideoSource>(1280, 720);
301+
std::shared_ptr<LocalVideoTrack> videoTrack =
302+
LocalVideoTrack::createLocalVideoTrack("cam", videoSource);
303+
TrackPublishOptions videoOpts;
304+
videoOpts.source = TrackSource::SOURCE_CAMERA;
305+
videoOpts.dtx = false;
306+
videoOpts.simulcast = true;
307+
std::shared_ptr<LocalTrackPublication> videoPub;
308+
try {
309+
// publishTrack takes std::shared_ptr<Track>, LocalAudioTrack derives from
310+
// Track
311+
videoPub = room.local_participant()->publishTrack(videoTrack, videoOpts);
312+
313+
std::cout << "Published track:\n"
314+
<< " SID: " << videoPub->sid() << "\n"
315+
<< " Name: " << videoPub->name() << "\n"
316+
<< " Kind: " << static_cast<int>(videoPub->kind()) << "\n"
317+
<< " Source: " << static_cast<int>(videoPub->source()) << "\n"
318+
<< " Simulcasted: " << std::boolalpha << videoPub->simulcasted()
319+
<< "\n"
320+
<< " Muted: " << std::boolalpha << videoPub->muted() << "\n";
321+
} catch (const std::exception &e) {
322+
std::cerr << "Failed to publish track: " << e.what() << std::endl;
323+
}
324+
std::thread videoThread(runFakeVideoCaptureLoop, videoSource);
325+
242326
// Keep the app alive until Ctrl-C so we continue receiving events,
243327
// similar to asyncio.run(main()) keeping the loop running.
244328
while (g_running.load()) {
@@ -249,6 +333,14 @@ int main(int argc, char *argv[]) {
249333
if (audioThread.joinable()) {
250334
audioThread.join();
251335
}
336+
// Clean up the audio track publishment
337+
room.local_participant()->unpublishTrack(audioPub->sid());
338+
339+
if (videoThread.joinable()) {
340+
videoThread.join();
341+
}
342+
// Clean up the video track publishment
343+
room.local_participant()->unpublishTrack(videoPub->sid());
252344

253345
FfiClient::instance().shutdown();
254346
std::cout << "Exiting.\n";

include/livekit/livekit.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,10 @@
1919
#include "local_audio_track.h"
2020
#include "local_participant.h"
2121
#include "local_track_publication.h"
22+
#include "local_video_track.h"
2223
#include "participant.h"
2324
#include "room.h"
2425
#include "room_delegate.h"
25-
#include "track_publication.h"
26+
#include "track_publication.h"
27+
#include "video_frame.h"
28+
#include "video_source.h"
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
/*
2+
* Copyright 2025 LiveKit
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an “AS IS” BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
#pragma once
18+
19+
#include "track.h"
20+
#include <memory>
21+
#include <string>
22+
23+
namespace livekit {
24+
25+
namespace proto {
26+
class OwnedTrack;
27+
}
28+
29+
class VideoSource;
30+
31+
// ============================================================
32+
// LocalAudioTrack
33+
// ============================================================
34+
class LocalVideoTrack : public Track {
35+
public:
36+
explicit LocalVideoTrack(FfiHandle handle, const proto::OwnedTrack &track);
37+
38+
static std::shared_ptr<LocalVideoTrack>
39+
createLocalVideoTrack(const std::string &name,
40+
const std::shared_ptr<VideoSource> &source);
41+
42+
// Mute/unmute
43+
void mute();
44+
void unmute();
45+
46+
std::string to_string() const;
47+
48+
private:
49+
// Optional: you may add private helpers if needed
50+
};
51+
52+
} // namespace livekit
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
/*
2+
* Copyright 2025 LiveKit
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an “AS IS” BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
#pragma once
18+
19+
#include "track.h"
20+
#include <memory>
21+
#include <string>
22+
23+
namespace livekit {
24+
25+
namespace proto {
26+
class OwnedTrack;
27+
}
28+
29+
class AudioSource;
30+
31+
// ============================================================
32+
// RemoteAudioTrack
33+
// ============================================================
34+
class RemoteAudioTrack : public Track {
35+
public:
36+
explicit RemoteAudioTrack(FfiHandle handle, const proto::OwnedTrack &track);
37+
38+
static std::shared_ptr<RemoteAudioTrack>
39+
createRemoteAudioTrack(const std::string &name,
40+
const std::shared_ptr<AudioSource> &source);
41+
42+
std::string to_string() const;
43+
44+
private:
45+
};
46+
47+
} // namespace livekit
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
/*
2+
* Copyright 2025 LiveKit
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an “AS IS” BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
#pragma once
18+
19+
#include "track.h"
20+
#include <memory>
21+
#include <string>
22+
23+
namespace livekit {
24+
25+
namespace proto {
26+
class OwnedTrack;
27+
}
28+
29+
class VideoSource;
30+
31+
// ============================================================
32+
// RemoteVideoTrack
33+
// ============================================================
34+
class RemoteVideoTrack : public Track {
35+
public:
36+
explicit RemoteVideoTrack(FfiHandle handle, const proto::OwnedTrack &track);
37+
38+
static std::shared_ptr<RemoteVideoTrack>
39+
createRemoteVideoTrack(const std::string &name,
40+
const std::shared_ptr<VideoSource> &source);
41+
42+
std::string to_string() const;
43+
44+
private:
45+
};
46+
47+
} // namespace livekit

0 commit comments

Comments
 (0)