@@ -43,27 +43,40 @@ namespace {
4343std::atomic<bool > g_running{true };
4444
4545void printUsage (const char *prog) {
46- std::cerr << " Usage:\n "
47- << " " << prog << " <ws-url> <token>\n "
48- << " or:\n "
49- << " " << prog << " --url=<ws-url> --token=<token>\n "
50- << " " << prog << " --url <ws-url> --token <token>\n\n "
51- << " Env fallbacks:\n "
52- << " LIVEKIT_URL, LIVEKIT_TOKEN\n " ;
46+ std::cerr
47+ << " Usage:\n "
48+ << " " << prog
49+ << " <ws-url> <token> [--enable_e2ee] [--e2ee_key <key>]\n "
50+ << " or:\n "
51+ << " " << prog
52+ << " --url=<ws-url> --token=<token> [--enable_e2ee] [--e2ee_key=<key>]\n "
53+ << " " << prog
54+ << " --url <ws-url> --token <token> [--enable_e2ee] [--e2ee_key "
55+ " <key>]\n\n "
56+ << " E2EE:\n "
57+ << " --enable_e2ee Enable end-to-end encryption (E2EE)\n "
58+ << " --e2ee_key <key> Optional shared key (UTF-8). If omitted, "
59+ " E2EE is enabled\n "
60+ << " but no shared key is set (advanced "
61+ " usage).\n\n "
62+ << " Env fallbacks:\n "
63+ << " LIVEKIT_URL, LIVEKIT_TOKEN, LIVEKIT_E2EE_KEY\n " ;
5364}
5465
5566void handleSignal (int ) { g_running.store (false ); }
5667
57- bool parseArgs (int argc, char *argv[], std::string &url, std::string &token) {
58- // 1) --help
68+ bool parseArgs (int argc, char *argv[], std::string &url, std::string &token,
69+ bool &enable_e2ee, std::string &e2ee_key) {
70+ enable_e2ee = false ;
71+ // --help
5972 for (int i = 1 ; i < argc; ++i) {
6073 std::string a = argv[i];
6174 if (a == " -h" || a == " --help" ) {
6275 return false ;
6376 }
6477 }
6578
66- // 2) flags: --url= / --token= or split form
79+ // flags: --url= / --token= or split form
6780 auto get_flag_value = [&](const std::string &name, int &i) -> std::string {
6881 std::string arg = argv[i];
6982 const std::string eq = name + " =" ;
@@ -79,18 +92,24 @@ bool parseArgs(int argc, char *argv[], std::string &url, std::string &token) {
7992
8093 for (int i = 1 ; i < argc; ++i) {
8194 const std::string a = argv[i];
82- if (a.rfind (" --url" , 0 ) == 0 ) {
95+ if (a == " --enable_e2ee" ) {
96+ enable_e2ee = true ;
97+ } else if (a.rfind (" --url" , 0 ) == 0 ) {
8398 auto v = get_flag_value (" --url" , i);
8499 if (!v.empty ())
85100 url = v;
86101 } else if (a.rfind (" --token" , 0 ) == 0 ) {
87102 auto v = get_flag_value (" --token" , i);
88103 if (!v.empty ())
89104 token = v;
105+ } else if (a.rfind (" --e2ee_key" , 0 ) == 0 ) {
106+ auto v = get_flag_value (" --e2ee_key" , i);
107+ if (!v.empty ())
108+ e2ee_key = v;
90109 }
91110 }
92111
93- // 3) positional if still empty
112+ // positional if still empty
94113 if (url.empty () || token.empty ()) {
95114 std::vector<std::string> pos;
96115 for (int i = 1 ; i < argc; ++i) {
@@ -118,6 +137,11 @@ bool parseArgs(int argc, char *argv[], std::string &url, std::string &token) {
118137 if (e)
119138 token = e;
120139 }
140+ if (e2ee_key.empty ()) {
141+ const char *e = std::getenv (" LIVEKIT_E2EE_KEY" );
142+ if (e)
143+ e2ee_key = e;
144+ }
121145
122146 return !(url.empty () || token.empty ());
123147}
@@ -211,11 +235,17 @@ class SimpleRoomDelegate : public livekit::RoomDelegate {
211235 SDLMediaManager &media_;
212236};
213237
238+ static std::vector<std::uint8_t > toBytes (const std::string &s) {
239+ return std::vector<std::uint8_t >(s.begin (), s.end ());
240+ }
241+
214242} // namespace
215243
216244int main (int argc, char *argv[]) {
217245 std::string url, token;
218- if (!parseArgs (argc, argv, url, token)) {
246+ bool enable_e2ee = false ;
247+ std::string e2ee_key;
248+ if (!parseArgs (argc, argv, url, token, enable_e2ee, e2ee_key)) {
219249 printUsage (argv[0 ]);
220250 return 1 ;
221251 }
@@ -240,22 +270,41 @@ int main(int argc, char *argv[]) {
240270 // Handle Ctrl-C to exit the idle loop
241271 std::signal (SIGINT, handleSignal);
242272
243- livekit::Room room{} ;
273+ auto room = std::make_unique< livekit::Room>() ;
244274 SimpleRoomDelegate delegate (media);
245- room. setDelegate (&delegate);
275+ room-> setDelegate (&delegate);
246276
247277 RoomOptions options;
248278 options.auto_subscribe = true ;
249279 options.dynacast = false ;
250- bool res = room.Connect (url, token, options);
280+
281+ if (enable_e2ee) {
282+ livekit::E2EEOptions e2ee;
283+ e2ee.encryption_type = livekit::EncryptionType::GCM;
284+ // Optional shared key: if empty, we enable E2EE without setting a shared
285+ // key. (Advanced use: keys can be set/ratcheted later via
286+ // E2EEManager/KeyProvider.)
287+ if (!e2ee_key.empty ()) {
288+ e2ee.shared_key = toBytes (e2ee_key);
289+ }
290+ options.e2ee = e2ee;
291+ if (!e2ee_key.empty ()) {
292+ std::cout << " [E2EE] enabled : (shared key length=" << e2ee_key.size ()
293+ << " )\n " ;
294+ } else {
295+ std::cout << " [E2EE] enabled: (no shared key set)\n " ;
296+ }
297+ }
298+
299+ bool res = room->Connect (url, token, options);
251300 std::cout << " Connect result is " << std::boolalpha << res << std::endl;
252301 if (!res) {
253302 std::cerr << " Failed to connect to room\n " ;
254303 FfiClient::instance ().shutdown ();
255304 return 1 ;
256305 }
257306
258- auto info = room. room_info ();
307+ auto info = room-> room_info ();
259308 std::cout << " Connected to room:\n "
260309 << " SID: " << (info.sid ? *info.sid : " (none)" ) << " \n "
261310 << " Name: " << info.name << " \n "
@@ -286,7 +335,7 @@ int main(int argc, char *argv[]) {
286335 try {
287336 // publishTrack takes std::shared_ptr<Track>, LocalAudioTrack derives from
288337 // Track
289- audioPub = room. localParticipant ()->publishTrack (audioTrack, audioOpts);
338+ audioPub = room-> localParticipant ()->publishTrack (audioTrack, audioOpts);
290339
291340 std::cout << " Published track:\n "
292341 << " SID: " << audioPub->sid () << " \n "
@@ -314,7 +363,7 @@ int main(int argc, char *argv[]) {
314363 try {
315364 // publishTrack takes std::shared_ptr<Track>, LocalAudioTrack derives from
316365 // Track
317- videoPub = room. localParticipant ()->publishTrack (videoTrack, videoOpts);
366+ videoPub = room-> localParticipant ()->publishTrack (videoTrack, videoOpts);
318367
319368 std::cout << " Published track:\n "
320369 << " SID: " << videoPub->sid () << " \n "
@@ -341,12 +390,16 @@ int main(int argc, char *argv[]) {
341390 media.stopMic ();
342391
343392 // Clean up the audio track publishment
344- room. localParticipant ()->unpublishTrack (audioPub->sid ());
393+ room-> localParticipant ()->unpublishTrack (audioPub->sid ());
345394
346395 media.stopCamera ();
347396
348397 // Clean up the video track publishment
349- room.localParticipant ()->unpublishTrack (videoPub->sid ());
398+ room->localParticipant ()->unpublishTrack (videoPub->sid ());
399+
400+ // Must be cleaned up before FfiClient::instance().shutdown();
401+ room->setDelegate (nullptr );
402+ room.reset ();
350403
351404 FfiClient::instance ().shutdown ();
352405 std::cout << " Exiting.\n " ;
0 commit comments