3030#include < string>
3131#include < thread>
3232#include < unordered_set>
33+ #include < vector>
3334
3435#include " livekit/livekit.h"
3536
@@ -57,14 +58,49 @@ formatUserTimestamp(const std::optional<VideoFrameMetadata> &metadata) {
5758
5859void printUsage (const char *program) {
5960 std::cerr << " Usage:\n "
60- << " " << program << " <ws-url> <token>\n "
61+ << " " << program << " <ws-url> <token> [--ignore-user-timestamp] \n "
6162 << " or:\n "
62- << " LIVEKIT_URL=... LIVEKIT_TOKEN=... " << program << " \n " ;
63+ << " LIVEKIT_URL=... LIVEKIT_TOKEN=... " << program
64+ << " [--ignore-user-timestamp]\n " ;
65+ }
66+
67+ bool parseArgs (int argc, char *argv[], std::string &url, std::string &token,
68+ bool &read_user_timestamp) {
69+ read_user_timestamp = true ;
70+ std::vector<std::string> positional;
71+
72+ for (int i = 1 ; i < argc; ++i) {
73+ const std::string arg = argv[i];
74+ if (arg == " -h" || arg == " --help" ) {
75+ return false ;
76+ }
77+ if (arg == " --ignore-user-timestamp" ) {
78+ read_user_timestamp = false ;
79+ continue ;
80+ }
81+ if (arg == " --read-user-timestamp" ) {
82+ read_user_timestamp = true ;
83+ continue ;
84+ }
85+
86+ positional.push_back (arg);
87+ }
88+
89+ url = getenvOrEmpty (" LIVEKIT_URL" );
90+ token = getenvOrEmpty (" LIVEKIT_TOKEN" );
91+
92+ if (positional.size () >= 2 ) {
93+ url = positional[0 ];
94+ token = positional[1 ];
95+ }
96+
97+ return !(url.empty () || token.empty ());
6398}
6499
65100class UserTimestampedVideoConsumerDelegate : public RoomDelegate {
66101public:
67- explicit UserTimestampedVideoConsumerDelegate (Room &room) : room_(room) {}
102+ UserTimestampedVideoConsumerDelegate (Room &room, bool read_user_timestamp)
103+ : room_(room), read_user_timestamp_(read_user_timestamp) {}
68104
69105 void registerExistingParticipants () {
70106 for (const auto &participant : room_.remoteParticipants ()) {
@@ -114,39 +150,50 @@ class UserTimestampedVideoConsumerDelegate : public RoomDelegate {
114150 VideoStream::Options stream_options;
115151 stream_options.format = VideoBufferType::RGBA;
116152
117- room_.setOnVideoFrameEventCallback (
118- identity, TrackSource::SOURCE_CAMERA,
119- [identity](const VideoFrameEvent &event) {
120- std::cout << " [consumer] from=" << identity
121- << " size=" << event.frame .width () << " x"
122- << event.frame .height ()
123- << " capture_ts_us=" << event.timestamp_us
124- << " user_ts_us=" << formatUserTimestamp (event.metadata )
125- << " rotation=" << static_cast <int >(event.rotation ) << " \n " ;
126- },
127- stream_options);
153+ if (read_user_timestamp_) {
154+ room_.setOnVideoFrameEventCallback (
155+ identity, TrackSource::SOURCE_CAMERA,
156+ [identity](const VideoFrameEvent &event) {
157+ std::cout << " [consumer] from=" << identity
158+ << " size=" << event.frame .width () << " x"
159+ << event.frame .height ()
160+ << " capture_ts_us=" << event.timestamp_us
161+ << " user_ts_us=" << formatUserTimestamp (event.metadata )
162+ << " rotation=" << static_cast <int >(event.rotation )
163+ << " \n " ;
164+ },
165+ stream_options);
166+ } else {
167+ room_.setOnVideoFrameCallback (
168+ identity, TrackSource::SOURCE_CAMERA,
169+ [identity](const VideoFrame &frame, std::int64_t timestamp_us) {
170+ std::cout << " [consumer] from=" << identity
171+ << " size=" << frame.width () << " x" << frame.height ()
172+ << " capture_ts_us=" << timestamp_us
173+ << " user_ts_us=ignored\n " ;
174+ },
175+ stream_options);
176+ }
128177
129178 std::cout << " [consumer] listening for camera frames from " << identity
130- << " \n " ;
179+ << " with user timestamp "
180+ << (read_user_timestamp_ ? " enabled" : " ignored" ) << " \n " ;
131181 }
132182
133183 Room &room_;
184+ bool read_user_timestamp_;
134185 std::mutex mutex_;
135186 std::unordered_set<std::string> registered_identities_;
136187};
137188
138189} // namespace
139190
140191int main (int argc, char *argv[]) {
141- std::string url = getenvOrEmpty (" LIVEKIT_URL" );
142- std::string token = getenvOrEmpty (" LIVEKIT_TOKEN" );
143-
144- if (argc >= 3 ) {
145- url = argv[1 ];
146- token = argv[2 ];
147- }
192+ std::string url;
193+ std::string token;
194+ bool read_user_timestamp = true ;
148195
149- if (url. empty () || token. empty ( )) {
196+ if (! parseArgs (argc, argv, url, token, read_user_timestamp )) {
150197 printUsage (argv[0 ]);
151198 return 1 ;
152199 }
@@ -165,7 +212,7 @@ int main(int argc, char *argv[]) {
165212 options.auto_subscribe = true ;
166213 options.dynacast = false ;
167214
168- UserTimestampedVideoConsumerDelegate delegate (room);
215+ UserTimestampedVideoConsumerDelegate delegate (room, read_user_timestamp );
169216 room.setDelegate (&delegate);
170217
171218 std::cout << " [consumer] connecting to " << url << " \n " ;
@@ -175,7 +222,8 @@ int main(int argc, char *argv[]) {
175222 } else {
176223 std::cout << " [consumer] connected as "
177224 << room.localParticipant ()->identity () << " to room '"
178- << room.room_info ().name << " '\n " ;
225+ << room.room_info ().name << " ' with user timestamp "
226+ << (read_user_timestamp ? " enabled" : " ignored" ) << " \n " ;
179227
180228 delegate.registerExistingParticipants ();
181229
0 commit comments