blob: 6a159123fd00f8112055973d1b81596433cb5133 [file] [log] [blame]
Bjorn Terelius36411852015-07-30 12:45:18 +02001/*
2 * Copyright (c) 2015 The WebRTC project authors. All Rights Reserved.
3 *
4 * Use of this source code is governed by a BSD-style license
5 * that can be found in the LICENSE file in the root of the source
6 * tree. An additional intellectual property rights grant can be found
7 * in the file PATENTS. All contributing project authors may
8 * be found in the AUTHORS file in the root of the source tree.
9 */
10
Peter Boström5c389d32015-09-25 13:58:30 +020011#include "webrtc/call/rtc_event_log.h"
Bjorn Terelius36411852015-07-30 12:45:18 +020012
13#include <deque>
14
15#include "webrtc/base/checks.h"
16#include "webrtc/base/criticalsection.h"
17#include "webrtc/base/thread_annotations.h"
18#include "webrtc/call.h"
terelius2f9fd5d2015-09-04 03:39:42 -070019#include "webrtc/modules/rtp_rtcp/source/byte_io.h"
Bjorn Terelius36411852015-07-30 12:45:18 +020020#include "webrtc/system_wrappers/interface/clock.h"
21#include "webrtc/system_wrappers/interface/file_wrapper.h"
22
23#ifdef ENABLE_RTC_EVENT_LOG
24// Files generated at build-time by the protobuf compiler.
25#ifdef WEBRTC_ANDROID_PLATFORM_BUILD
Peter Boström5c389d32015-09-25 13:58:30 +020026#include "external/webrtc/webrtc/call/rtc_event_log.pb.h"
Bjorn Terelius36411852015-07-30 12:45:18 +020027#else
Peter Boström5c389d32015-09-25 13:58:30 +020028#include "webrtc/call/rtc_event_log.pb.h"
Bjorn Terelius36411852015-07-30 12:45:18 +020029#endif
30#endif
31
32namespace webrtc {
33
34#ifndef ENABLE_RTC_EVENT_LOG
35
36// No-op implementation if flag is not set.
37class RtcEventLogImpl final : public RtcEventLog {
38 public:
39 void StartLogging(const std::string& file_name, int duration_ms) override {}
ivoc112a3d82015-10-16 02:22:18 -070040 bool StartLogging(rtc::PlatformFile log_file) override { return false; }
Bjorn Terelius36411852015-07-30 12:45:18 +020041 void StopLogging(void) override {}
42 void LogVideoReceiveStreamConfig(
43 const VideoReceiveStream::Config& config) override {}
44 void LogVideoSendStreamConfig(
45 const VideoSendStream::Config& config) override {}
46 void LogRtpHeader(bool incoming,
47 MediaType media_type,
48 const uint8_t* header,
terelius2f9fd5d2015-09-04 03:39:42 -070049 size_t packet_length) override {}
Bjorn Terelius36411852015-07-30 12:45:18 +020050 void LogRtcpPacket(bool incoming,
51 MediaType media_type,
52 const uint8_t* packet,
53 size_t length) override {}
Ivo Creusenae856f22015-09-17 16:30:16 +020054 void LogAudioPlayout(uint32_t ssrc) override {}
Bjorn Terelius36411852015-07-30 12:45:18 +020055};
56
57#else // ENABLE_RTC_EVENT_LOG is defined
58
59class RtcEventLogImpl final : public RtcEventLog {
60 public:
Bjorn Terelius36411852015-07-30 12:45:18 +020061 void StartLogging(const std::string& file_name, int duration_ms) override;
ivoc112a3d82015-10-16 02:22:18 -070062 bool StartLogging(rtc::PlatformFile log_file) override;
Bjorn Terelius36411852015-07-30 12:45:18 +020063 void StopLogging() override;
64 void LogVideoReceiveStreamConfig(
65 const VideoReceiveStream::Config& config) override;
66 void LogVideoSendStreamConfig(const VideoSendStream::Config& config) override;
67 void LogRtpHeader(bool incoming,
68 MediaType media_type,
69 const uint8_t* header,
terelius2f9fd5d2015-09-04 03:39:42 -070070 size_t packet_length) override;
Bjorn Terelius36411852015-07-30 12:45:18 +020071 void LogRtcpPacket(bool incoming,
72 MediaType media_type,
73 const uint8_t* packet,
74 size_t length) override;
Ivo Creusenae856f22015-09-17 16:30:16 +020075 void LogAudioPlayout(uint32_t ssrc) override;
Bjorn Terelius36411852015-07-30 12:45:18 +020076
77 private:
ivoc112a3d82015-10-16 02:22:18 -070078 // Starts logging. This function assumes the file_ has been opened succesfully
79 // and that the start_time_us_ and _duration_us_ have been set.
80 void StartLoggingLocked() EXCLUSIVE_LOCKS_REQUIRED(crit_);
Bjorn Terelius36411852015-07-30 12:45:18 +020081 // Stops logging and clears the stored data and buffers.
82 void StopLoggingLocked() EXCLUSIVE_LOCKS_REQUIRED(crit_);
83 // Adds a new event to the logfile if logging is active, or adds it to the
84 // list of recent log events otherwise.
85 void HandleEvent(rtclog::Event* event) EXCLUSIVE_LOCKS_REQUIRED(crit_);
86 // Writes the event to the file. Note that this will destroy the state of the
87 // input argument.
88 void StoreToFile(rtclog::Event* event) EXCLUSIVE_LOCKS_REQUIRED(crit_);
89 // Adds the event to the list of recent events, and removes any events that
90 // are too old and no longer fall in the time window.
91 void AddRecentEvent(const rtclog::Event& event)
92 EXCLUSIVE_LOCKS_REQUIRED(crit_);
93
94 // Amount of time in microseconds to record log events, before starting the
95 // actual log.
96 const int recent_log_duration_us = 10000000;
97
98 rtc::CriticalSection crit_;
ivoc112a3d82015-10-16 02:22:18 -070099 rtc::scoped_ptr<FileWrapper> file_ GUARDED_BY(crit_) =
100 rtc::scoped_ptr<FileWrapper>(FileWrapper::Create());
101 rtc::PlatformFile platform_file_ GUARDED_BY(crit_) =
102 rtc::kInvalidPlatformFileValue;
Bjorn Terelius36411852015-07-30 12:45:18 +0200103 rtclog::EventStream stream_ GUARDED_BY(crit_);
104 std::deque<rtclog::Event> recent_log_events_ GUARDED_BY(crit_);
ivoc112a3d82015-10-16 02:22:18 -0700105 bool currently_logging_ GUARDED_BY(crit_) = false;
106 int64_t start_time_us_ GUARDED_BY(crit_) = 0;
107 int64_t duration_us_ GUARDED_BY(crit_) = 0;
108 const Clock* const clock_ = Clock::GetRealTimeClock();
Bjorn Terelius36411852015-07-30 12:45:18 +0200109};
110
111namespace {
112// The functions in this namespace convert enums from the runtime format
113// that the rest of the WebRtc project can use, to the corresponding
114// serialized enum which is defined by the protobuf.
115
116// Do not add default return values to the conversion functions in this
117// unnamed namespace. The intention is to make the compiler warn if anyone
118// adds unhandled new events/modes/etc.
119
pbosda903ea2015-10-02 02:36:56 -0700120rtclog::VideoReceiveConfig_RtcpMode ConvertRtcpMode(RtcpMode rtcp_mode) {
Bjorn Terelius36411852015-07-30 12:45:18 +0200121 switch (rtcp_mode) {
pbosda903ea2015-10-02 02:36:56 -0700122 case RtcpMode::kCompound:
Bjorn Terelius36411852015-07-30 12:45:18 +0200123 return rtclog::VideoReceiveConfig::RTCP_COMPOUND;
pbosda903ea2015-10-02 02:36:56 -0700124 case RtcpMode::kReducedSize:
Bjorn Terelius36411852015-07-30 12:45:18 +0200125 return rtclog::VideoReceiveConfig::RTCP_REDUCEDSIZE;
pbosda903ea2015-10-02 02:36:56 -0700126 case RtcpMode::kOff:
127 RTC_NOTREACHED();
128 return rtclog::VideoReceiveConfig::RTCP_COMPOUND;
Bjorn Terelius36411852015-07-30 12:45:18 +0200129 }
130 RTC_NOTREACHED();
131 return rtclog::VideoReceiveConfig::RTCP_COMPOUND;
132}
133
134rtclog::MediaType ConvertMediaType(MediaType media_type) {
135 switch (media_type) {
136 case MediaType::ANY:
137 return rtclog::MediaType::ANY;
138 case MediaType::AUDIO:
139 return rtclog::MediaType::AUDIO;
140 case MediaType::VIDEO:
141 return rtclog::MediaType::VIDEO;
142 case MediaType::DATA:
143 return rtclog::MediaType::DATA;
144 }
145 RTC_NOTREACHED();
146 return rtclog::ANY;
147}
148
149} // namespace
150
151// RtcEventLogImpl member functions.
Bjorn Terelius36411852015-07-30 12:45:18 +0200152
153void RtcEventLogImpl::StartLogging(const std::string& file_name,
154 int duration_ms) {
155 rtc::CritScope lock(&crit_);
156 if (currently_logging_) {
157 StopLoggingLocked();
158 }
159 if (file_->OpenFile(file_name.c_str(), false) != 0) {
160 return;
161 }
Bjorn Terelius36411852015-07-30 12:45:18 +0200162 start_time_us_ = clock_->TimeInMicroseconds();
163 duration_us_ = static_cast<int64_t>(duration_ms) * 1000;
ivoc112a3d82015-10-16 02:22:18 -0700164 StartLoggingLocked();
165}
166
167bool RtcEventLogImpl::StartLogging(rtc::PlatformFile log_file) {
168 rtc::CritScope lock(&crit_);
169
170 if (currently_logging_) {
171 StopLoggingLocked();
172 }
173 RTC_DCHECK(platform_file_ == rtc::kInvalidPlatformFileValue);
174
175 FILE* file_stream = rtc::FdopenPlatformFileForWriting(log_file);
176 if (!file_stream) {
177 rtc::ClosePlatformFile(log_file);
178 return false;
179 }
180
181 if (file_->OpenFromFileHandle(file_stream, true, false) != 0) {
182 rtc::ClosePlatformFile(log_file);
183 return false;
184 }
185 platform_file_ = log_file;
186 // Set the start time and duration to keep logging for 10 minutes.
187 start_time_us_ = clock_->TimeInMicroseconds();
188 duration_us_ = 10 * 60 * 1000000;
189 StartLoggingLocked();
190 return true;
191}
192
193void RtcEventLogImpl::StartLoggingLocked() {
194 currently_logging_ = true;
Bjorn Terelius36411852015-07-30 12:45:18 +0200195 // Write all the recent events to the log file, ignoring any old events.
196 for (auto& event : recent_log_events_) {
197 if (event.timestamp_us() >= start_time_us_ - recent_log_duration_us) {
198 StoreToFile(&event);
199 }
200 }
201 recent_log_events_.clear();
202 // Write a LOG_START event to the file.
203 rtclog::Event start_event;
204 start_event.set_timestamp_us(start_time_us_);
Ivo Creusen301aaed2015-10-08 18:07:41 +0200205 start_event.set_type(rtclog::Event::LOG_START);
Bjorn Terelius36411852015-07-30 12:45:18 +0200206 StoreToFile(&start_event);
207}
208
209void RtcEventLogImpl::StopLogging() {
210 rtc::CritScope lock(&crit_);
211 StopLoggingLocked();
212}
213
214void RtcEventLogImpl::LogVideoReceiveStreamConfig(
215 const VideoReceiveStream::Config& config) {
216 rtc::CritScope lock(&crit_);
217
218 rtclog::Event event;
219 const int64_t timestamp = clock_->TimeInMicroseconds();
220 event.set_timestamp_us(timestamp);
221 event.set_type(rtclog::Event::VIDEO_RECEIVER_CONFIG_EVENT);
222
223 rtclog::VideoReceiveConfig* receiver_config =
224 event.mutable_video_receiver_config();
225 receiver_config->set_remote_ssrc(config.rtp.remote_ssrc);
226 receiver_config->set_local_ssrc(config.rtp.local_ssrc);
227
228 receiver_config->set_rtcp_mode(ConvertRtcpMode(config.rtp.rtcp_mode));
229
230 receiver_config->set_receiver_reference_time_report(
231 config.rtp.rtcp_xr.receiver_reference_time_report);
232 receiver_config->set_remb(config.rtp.remb);
233
234 for (const auto& kv : config.rtp.rtx) {
235 rtclog::RtxMap* rtx = receiver_config->add_rtx_map();
236 rtx->set_payload_type(kv.first);
237 rtx->mutable_config()->set_rtx_ssrc(kv.second.ssrc);
238 rtx->mutable_config()->set_rtx_payload_type(kv.second.payload_type);
239 }
240
241 for (const auto& e : config.rtp.extensions) {
242 rtclog::RtpHeaderExtension* extension =
243 receiver_config->add_header_extensions();
244 extension->set_name(e.name);
245 extension->set_id(e.id);
246 }
247
248 for (const auto& d : config.decoders) {
249 rtclog::DecoderConfig* decoder = receiver_config->add_decoders();
250 decoder->set_name(d.payload_name);
251 decoder->set_payload_type(d.payload_type);
252 }
253 // TODO(terelius): We should use a separate event queue for config events.
254 // The current approach of storing the configuration together with the
255 // RTP events causes the configuration information to be removed 10s
256 // after the ReceiveStream is created.
257 HandleEvent(&event);
258}
259
260void RtcEventLogImpl::LogVideoSendStreamConfig(
261 const VideoSendStream::Config& config) {
262 rtc::CritScope lock(&crit_);
263
264 rtclog::Event event;
265 const int64_t timestamp = clock_->TimeInMicroseconds();
266 event.set_timestamp_us(timestamp);
267 event.set_type(rtclog::Event::VIDEO_SENDER_CONFIG_EVENT);
268
269 rtclog::VideoSendConfig* sender_config = event.mutable_video_sender_config();
270
271 for (const auto& ssrc : config.rtp.ssrcs) {
272 sender_config->add_ssrcs(ssrc);
273 }
274
275 for (const auto& e : config.rtp.extensions) {
276 rtclog::RtpHeaderExtension* extension =
277 sender_config->add_header_extensions();
278 extension->set_name(e.name);
279 extension->set_id(e.id);
280 }
281
282 for (const auto& rtx_ssrc : config.rtp.rtx.ssrcs) {
283 sender_config->add_rtx_ssrcs(rtx_ssrc);
284 }
285 sender_config->set_rtx_payload_type(config.rtp.rtx.payload_type);
286
287 sender_config->set_c_name(config.rtp.c_name);
288
289 rtclog::EncoderConfig* encoder = sender_config->mutable_encoder();
290 encoder->set_name(config.encoder_settings.payload_name);
291 encoder->set_payload_type(config.encoder_settings.payload_type);
292
293 // TODO(terelius): We should use a separate event queue for config events.
294 // The current approach of storing the configuration together with the
295 // RTP events causes the configuration information to be removed 10s
296 // after the ReceiveStream is created.
297 HandleEvent(&event);
298}
299
Bjorn Terelius36411852015-07-30 12:45:18 +0200300void RtcEventLogImpl::LogRtpHeader(bool incoming,
301 MediaType media_type,
302 const uint8_t* header,
terelius2f9fd5d2015-09-04 03:39:42 -0700303 size_t packet_length) {
304 // Read header length (in bytes) from packet data.
305 if (packet_length < 12u) {
306 return; // Don't read outside the packet.
307 }
308 const bool x = (header[0] & 0x10) != 0;
309 const uint8_t cc = header[0] & 0x0f;
310 size_t header_length = 12u + cc * 4u;
311
312 if (x) {
313 if (packet_length < 12u + cc * 4u + 4u) {
314 return; // Don't read outside the packet.
315 }
316 size_t x_len = ByteReader<uint16_t>::ReadBigEndian(header + 14 + cc * 4);
317 header_length += (x_len + 1) * 4;
318 }
319
Bjorn Terelius36411852015-07-30 12:45:18 +0200320 rtc::CritScope lock(&crit_);
321 rtclog::Event rtp_event;
322 const int64_t timestamp = clock_->TimeInMicroseconds();
323 rtp_event.set_timestamp_us(timestamp);
324 rtp_event.set_type(rtclog::Event::RTP_EVENT);
325 rtp_event.mutable_rtp_packet()->set_incoming(incoming);
326 rtp_event.mutable_rtp_packet()->set_type(ConvertMediaType(media_type));
terelius2f9fd5d2015-09-04 03:39:42 -0700327 rtp_event.mutable_rtp_packet()->set_packet_length(packet_length);
Bjorn Terelius36411852015-07-30 12:45:18 +0200328 rtp_event.mutable_rtp_packet()->set_header(header, header_length);
329 HandleEvent(&rtp_event);
330}
331
332void RtcEventLogImpl::LogRtcpPacket(bool incoming,
333 MediaType media_type,
334 const uint8_t* packet,
335 size_t length) {
336 rtc::CritScope lock(&crit_);
337 rtclog::Event rtcp_event;
338 const int64_t timestamp = clock_->TimeInMicroseconds();
339 rtcp_event.set_timestamp_us(timestamp);
340 rtcp_event.set_type(rtclog::Event::RTCP_EVENT);
341 rtcp_event.mutable_rtcp_packet()->set_incoming(incoming);
342 rtcp_event.mutable_rtcp_packet()->set_type(ConvertMediaType(media_type));
343 rtcp_event.mutable_rtcp_packet()->set_packet_data(packet, length);
344 HandleEvent(&rtcp_event);
345}
346
Ivo Creusenae856f22015-09-17 16:30:16 +0200347void RtcEventLogImpl::LogAudioPlayout(uint32_t ssrc) {
Bjorn Terelius36411852015-07-30 12:45:18 +0200348 rtc::CritScope lock(&crit_);
349 rtclog::Event event;
350 const int64_t timestamp = clock_->TimeInMicroseconds();
351 event.set_timestamp_us(timestamp);
Ivo Creusen301aaed2015-10-08 18:07:41 +0200352 event.set_type(rtclog::Event::AUDIO_PLAYOUT_EVENT);
353 auto playout_event = event.mutable_audio_playout_event();
354 playout_event->set_local_ssrc(ssrc);
Bjorn Terelius36411852015-07-30 12:45:18 +0200355 HandleEvent(&event);
356}
357
358void RtcEventLogImpl::StopLoggingLocked() {
359 if (currently_logging_) {
360 currently_logging_ = false;
Ivo Creusen301aaed2015-10-08 18:07:41 +0200361 // Create a LogEnd event
Bjorn Terelius36411852015-07-30 12:45:18 +0200362 rtclog::Event event;
363 int64_t timestamp = clock_->TimeInMicroseconds();
364 event.set_timestamp_us(timestamp);
Ivo Creusen301aaed2015-10-08 18:07:41 +0200365 event.set_type(rtclog::Event::LOG_END);
Bjorn Terelius36411852015-07-30 12:45:18 +0200366 // Store the event and close the file
henrikg91d6ede2015-09-17 00:24:34 -0700367 RTC_DCHECK(file_->Open());
Bjorn Terelius36411852015-07-30 12:45:18 +0200368 StoreToFile(&event);
369 file_->CloseFile();
ivoc112a3d82015-10-16 02:22:18 -0700370 if (platform_file_ != rtc::kInvalidPlatformFileValue) {
371 rtc::ClosePlatformFile(platform_file_);
372 platform_file_ = rtc::kInvalidPlatformFileValue;
373 }
Bjorn Terelius36411852015-07-30 12:45:18 +0200374 }
henrikg91d6ede2015-09-17 00:24:34 -0700375 RTC_DCHECK(!file_->Open());
Bjorn Terelius36411852015-07-30 12:45:18 +0200376 stream_.Clear();
377}
378
379void RtcEventLogImpl::HandleEvent(rtclog::Event* event) {
380 if (currently_logging_) {
381 if (clock_->TimeInMicroseconds() < start_time_us_ + duration_us_) {
382 StoreToFile(event);
383 return;
384 }
385 StopLoggingLocked();
386 }
387 AddRecentEvent(*event);
388}
389
390void RtcEventLogImpl::StoreToFile(rtclog::Event* event) {
391 // Reuse the same object at every log event.
392 if (stream_.stream_size() < 1) {
393 stream_.add_stream();
394 }
henrikg91d6ede2015-09-17 00:24:34 -0700395 RTC_DCHECK_EQ(stream_.stream_size(), 1);
Bjorn Terelius36411852015-07-30 12:45:18 +0200396 stream_.mutable_stream(0)->Swap(event);
397 // TODO(terelius): Doesn't this create a new EventStream per event?
398 // Is this guaranteed to work e.g. in future versions of protobuf?
399 std::string dump_buffer;
400 stream_.SerializeToString(&dump_buffer);
401 file_->Write(dump_buffer.data(), dump_buffer.size());
402}
403
404void RtcEventLogImpl::AddRecentEvent(const rtclog::Event& event) {
405 recent_log_events_.push_back(event);
406 while (recent_log_events_.front().timestamp_us() <
407 event.timestamp_us() - recent_log_duration_us) {
408 recent_log_events_.pop_front();
409 }
410}
411
412bool RtcEventLog::ParseRtcEventLog(const std::string& file_name,
413 rtclog::EventStream* result) {
414 char tmp_buffer[1024];
415 int bytes_read = 0;
416 rtc::scoped_ptr<FileWrapper> dump_file(FileWrapper::Create());
417 if (dump_file->OpenFile(file_name.c_str(), true) != 0) {
418 return false;
419 }
420 std::string dump_buffer;
421 while ((bytes_read = dump_file->Read(tmp_buffer, sizeof(tmp_buffer))) > 0) {
422 dump_buffer.append(tmp_buffer, bytes_read);
423 }
424 dump_file->CloseFile();
425 return result->ParseFromString(dump_buffer);
426}
427
428#endif // ENABLE_RTC_EVENT_LOG
429
430// RtcEventLog member functions.
431rtc::scoped_ptr<RtcEventLog> RtcEventLog::Create() {
432 return rtc::scoped_ptr<RtcEventLog>(new RtcEventLogImpl());
433}
434} // namespace webrtc