OLD | NEW |
| (Empty) |
1 /* | |
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 | |
11 #include "webrtc/call/rtc_event_log.h" | |
12 | |
13 #include <limits> | |
14 #include <vector> | |
15 | |
16 #include "webrtc/base/checks.h" | |
17 #include "webrtc/base/constructormagic.h" | |
18 #include "webrtc/base/event.h" | |
19 #include "webrtc/base/swap_queue.h" | |
20 #include "webrtc/base/thread_checker.h" | |
21 #include "webrtc/call.h" | |
22 #include "webrtc/call/rtc_event_log_helper_thread.h" | |
23 #include "webrtc/modules/rtp_rtcp/include/rtp_rtcp_defines.h" | |
24 #include "webrtc/modules/rtp_rtcp/source/byte_io.h" | |
25 #include "webrtc/modules/rtp_rtcp/source/rtcp_utility.h" | |
26 #include "webrtc/system_wrappers/include/clock.h" | |
27 #include "webrtc/system_wrappers/include/file_wrapper.h" | |
28 #include "webrtc/system_wrappers/include/logging.h" | |
29 | |
30 #ifdef ENABLE_RTC_EVENT_LOG | |
31 // Files generated at build-time by the protobuf compiler. | |
32 #ifdef WEBRTC_ANDROID_PLATFORM_BUILD | |
33 #include "external/webrtc/webrtc/call/rtc_event_log.pb.h" | |
34 #else | |
35 #include "webrtc/call/rtc_event_log.pb.h" | |
36 #endif | |
37 #endif | |
38 | |
39 namespace webrtc { | |
40 | |
41 #ifdef ENABLE_RTC_EVENT_LOG | |
42 | |
43 class RtcEventLogImpl final : public RtcEventLog { | |
44 public: | |
45 explicit RtcEventLogImpl(const Clock* clock); | |
46 ~RtcEventLogImpl() override; | |
47 | |
48 bool StartLogging(const std::string& file_name, | |
49 int64_t max_size_bytes) override; | |
50 bool StartLogging(rtc::PlatformFile platform_file, | |
51 int64_t max_size_bytes) override; | |
52 void StopLogging() override; | |
53 void LogVideoReceiveStreamConfig( | |
54 const VideoReceiveStream::Config& config) override; | |
55 void LogVideoSendStreamConfig(const VideoSendStream::Config& config) override; | |
56 void LogRtpHeader(PacketDirection direction, | |
57 MediaType media_type, | |
58 const uint8_t* header, | |
59 size_t packet_length) override; | |
60 void LogRtcpPacket(PacketDirection direction, | |
61 MediaType media_type, | |
62 const uint8_t* packet, | |
63 size_t length) override; | |
64 void LogAudioPlayout(uint32_t ssrc) override; | |
65 void LogBwePacketLossEvent(int32_t bitrate, | |
66 uint8_t fraction_loss, | |
67 int32_t total_packets) override; | |
68 | |
69 private: | |
70 void StoreEvent(std::unique_ptr<rtclog::Event>* event); | |
71 | |
72 // Message queue for passing control messages to the logging thread. | |
73 SwapQueue<RtcEventLogHelperThread::ControlMessage> message_queue_; | |
74 | |
75 // Message queue for passing events to the logging thread. | |
76 SwapQueue<std::unique_ptr<rtclog::Event> > event_queue_; | |
77 | |
78 const Clock* const clock_; | |
79 | |
80 RtcEventLogHelperThread helper_thread_; | |
81 rtc::ThreadChecker thread_checker_; | |
82 | |
83 RTC_DISALLOW_IMPLICIT_CONSTRUCTORS(RtcEventLogImpl); | |
84 }; | |
85 | |
86 namespace { | |
87 // The functions in this namespace convert enums from the runtime format | |
88 // that the rest of the WebRtc project can use, to the corresponding | |
89 // serialized enum which is defined by the protobuf. | |
90 | |
91 rtclog::VideoReceiveConfig_RtcpMode ConvertRtcpMode(RtcpMode rtcp_mode) { | |
92 switch (rtcp_mode) { | |
93 case RtcpMode::kCompound: | |
94 return rtclog::VideoReceiveConfig::RTCP_COMPOUND; | |
95 case RtcpMode::kReducedSize: | |
96 return rtclog::VideoReceiveConfig::RTCP_REDUCEDSIZE; | |
97 case RtcpMode::kOff: | |
98 RTC_NOTREACHED(); | |
99 return rtclog::VideoReceiveConfig::RTCP_COMPOUND; | |
100 } | |
101 RTC_NOTREACHED(); | |
102 return rtclog::VideoReceiveConfig::RTCP_COMPOUND; | |
103 } | |
104 | |
105 rtclog::MediaType ConvertMediaType(MediaType media_type) { | |
106 switch (media_type) { | |
107 case MediaType::ANY: | |
108 return rtclog::MediaType::ANY; | |
109 case MediaType::AUDIO: | |
110 return rtclog::MediaType::AUDIO; | |
111 case MediaType::VIDEO: | |
112 return rtclog::MediaType::VIDEO; | |
113 case MediaType::DATA: | |
114 return rtclog::MediaType::DATA; | |
115 } | |
116 RTC_NOTREACHED(); | |
117 return rtclog::ANY; | |
118 } | |
119 | |
120 // The RTP and RTCP buffers reserve space for twice the expected number of | |
121 // sent packets because they also contain received packets. | |
122 static const int kEventsPerSecond = 1000; | |
123 static const int kControlMessagesPerSecond = 10; | |
124 } // namespace | |
125 | |
126 // RtcEventLogImpl member functions. | |
127 RtcEventLogImpl::RtcEventLogImpl(const Clock* clock) | |
128 // Allocate buffers for roughly one second of history. | |
129 : message_queue_(kControlMessagesPerSecond), | |
130 event_queue_(kEventsPerSecond), | |
131 clock_(clock), | |
132 helper_thread_(&message_queue_, | |
133 &event_queue_, | |
134 clock), | |
135 thread_checker_() { | |
136 thread_checker_.DetachFromThread(); | |
137 } | |
138 | |
139 RtcEventLogImpl::~RtcEventLogImpl() { | |
140 // The RtcEventLogHelperThread destructor closes the file | |
141 // and waits for the thread to terminate. | |
142 } | |
143 | |
144 bool RtcEventLogImpl::StartLogging(const std::string& file_name, | |
145 int64_t max_size_bytes) { | |
146 RTC_DCHECK(thread_checker_.CalledOnValidThread()); | |
147 RtcEventLogHelperThread::ControlMessage message; | |
148 message.message_type = RtcEventLogHelperThread::ControlMessage::START_FILE; | |
149 message.max_size_bytes = max_size_bytes <= 0 | |
150 ? std::numeric_limits<int64_t>::max() | |
151 : max_size_bytes; | |
152 message.start_time = clock_->TimeInMicroseconds(); | |
153 message.stop_time = std::numeric_limits<int64_t>::max(); | |
154 message.file.reset(FileWrapper::Create()); | |
155 if (!message.file->OpenFile(file_name.c_str(), false)) { | |
156 LOG(LS_ERROR) << "Can't open file. WebRTC event log not started."; | |
157 return false; | |
158 } | |
159 if (!message_queue_.Insert(&message)) { | |
160 LOG(LS_ERROR) << "Message queue full. Can't start logging."; | |
161 return false; | |
162 } | |
163 helper_thread_.SignalNewEvent(); | |
164 LOG(LS_INFO) << "Starting WebRTC event log."; | |
165 return true; | |
166 } | |
167 | |
168 bool RtcEventLogImpl::StartLogging(rtc::PlatformFile platform_file, | |
169 int64_t max_size_bytes) { | |
170 RTC_DCHECK(thread_checker_.CalledOnValidThread()); | |
171 RtcEventLogHelperThread::ControlMessage message; | |
172 message.message_type = RtcEventLogHelperThread::ControlMessage::START_FILE; | |
173 message.max_size_bytes = max_size_bytes <= 0 | |
174 ? std::numeric_limits<int64_t>::max() | |
175 : max_size_bytes; | |
176 message.start_time = clock_->TimeInMicroseconds(); | |
177 message.stop_time = std::numeric_limits<int64_t>::max(); | |
178 message.file.reset(FileWrapper::Create()); | |
179 FILE* file_handle = rtc::FdopenPlatformFileForWriting(platform_file); | |
180 if (!file_handle) { | |
181 LOG(LS_ERROR) << "Can't open file. WebRTC event log not started."; | |
182 // Even though we failed to open a FILE*, the platform_file is still open | |
183 // and needs to be closed. | |
184 if (!rtc::ClosePlatformFile(platform_file)) { | |
185 LOG(LS_ERROR) << "Can't close file."; | |
186 } | |
187 return false; | |
188 } | |
189 if (!message.file->OpenFromFileHandle(file_handle)) { | |
190 LOG(LS_ERROR) << "Can't open file. WebRTC event log not started."; | |
191 return false; | |
192 } | |
193 if (!message_queue_.Insert(&message)) { | |
194 LOG(LS_ERROR) << "Message queue full. Can't start logging."; | |
195 return false; | |
196 } | |
197 helper_thread_.SignalNewEvent(); | |
198 LOG(LS_INFO) << "Starting WebRTC event log."; | |
199 return true; | |
200 } | |
201 | |
202 void RtcEventLogImpl::StopLogging() { | |
203 RTC_DCHECK(thread_checker_.CalledOnValidThread()); | |
204 RtcEventLogHelperThread::ControlMessage message; | |
205 message.message_type = RtcEventLogHelperThread::ControlMessage::STOP_FILE; | |
206 message.stop_time = clock_->TimeInMicroseconds(); | |
207 while (!message_queue_.Insert(&message)) { | |
208 // TODO(terelius): We would like to have a blocking Insert function in the | |
209 // SwapQueue, but for the time being we will just clear any previous | |
210 // messages. | |
211 // Since StopLogging waits for the thread, it is essential that we don't | |
212 // clear any STOP_FILE messages. To ensure that there is only one call at a | |
213 // time, we require that all calls to StopLogging are made on the same | |
214 // thread. | |
215 LOG(LS_ERROR) << "Message queue full. Clearing queue to stop logging."; | |
216 message_queue_.Clear(); | |
217 } | |
218 LOG(LS_INFO) << "Stopping WebRTC event log."; | |
219 helper_thread_.WaitForFileFinished(); | |
220 } | |
221 | |
222 void RtcEventLogImpl::LogVideoReceiveStreamConfig( | |
223 const VideoReceiveStream::Config& config) { | |
224 std::unique_ptr<rtclog::Event> event(new rtclog::Event()); | |
225 event->set_timestamp_us(clock_->TimeInMicroseconds()); | |
226 event->set_type(rtclog::Event::VIDEO_RECEIVER_CONFIG_EVENT); | |
227 | |
228 rtclog::VideoReceiveConfig* receiver_config = | |
229 event->mutable_video_receiver_config(); | |
230 receiver_config->set_remote_ssrc(config.rtp.remote_ssrc); | |
231 receiver_config->set_local_ssrc(config.rtp.local_ssrc); | |
232 | |
233 receiver_config->set_rtcp_mode(ConvertRtcpMode(config.rtp.rtcp_mode)); | |
234 receiver_config->set_remb(config.rtp.remb); | |
235 | |
236 for (const auto& kv : config.rtp.rtx) { | |
237 rtclog::RtxMap* rtx = receiver_config->add_rtx_map(); | |
238 rtx->set_payload_type(kv.first); | |
239 rtx->mutable_config()->set_rtx_ssrc(kv.second.ssrc); | |
240 rtx->mutable_config()->set_rtx_payload_type(kv.second.payload_type); | |
241 } | |
242 | |
243 for (const auto& e : config.rtp.extensions) { | |
244 rtclog::RtpHeaderExtension* extension = | |
245 receiver_config->add_header_extensions(); | |
246 extension->set_name(e.uri); | |
247 extension->set_id(e.id); | |
248 } | |
249 | |
250 for (const auto& d : config.decoders) { | |
251 rtclog::DecoderConfig* decoder = receiver_config->add_decoders(); | |
252 decoder->set_name(d.payload_name); | |
253 decoder->set_payload_type(d.payload_type); | |
254 } | |
255 StoreEvent(&event); | |
256 } | |
257 | |
258 void RtcEventLogImpl::LogVideoSendStreamConfig( | |
259 const VideoSendStream::Config& config) { | |
260 std::unique_ptr<rtclog::Event> event(new rtclog::Event()); | |
261 event->set_timestamp_us(clock_->TimeInMicroseconds()); | |
262 event->set_type(rtclog::Event::VIDEO_SENDER_CONFIG_EVENT); | |
263 | |
264 rtclog::VideoSendConfig* sender_config = event->mutable_video_sender_config(); | |
265 | |
266 for (const auto& ssrc : config.rtp.ssrcs) { | |
267 sender_config->add_ssrcs(ssrc); | |
268 } | |
269 | |
270 for (const auto& e : config.rtp.extensions) { | |
271 rtclog::RtpHeaderExtension* extension = | |
272 sender_config->add_header_extensions(); | |
273 extension->set_name(e.uri); | |
274 extension->set_id(e.id); | |
275 } | |
276 | |
277 for (const auto& rtx_ssrc : config.rtp.rtx.ssrcs) { | |
278 sender_config->add_rtx_ssrcs(rtx_ssrc); | |
279 } | |
280 sender_config->set_rtx_payload_type(config.rtp.rtx.payload_type); | |
281 | |
282 rtclog::EncoderConfig* encoder = sender_config->mutable_encoder(); | |
283 encoder->set_name(config.encoder_settings.payload_name); | |
284 encoder->set_payload_type(config.encoder_settings.payload_type); | |
285 StoreEvent(&event); | |
286 } | |
287 | |
288 void RtcEventLogImpl::LogRtpHeader(PacketDirection direction, | |
289 MediaType media_type, | |
290 const uint8_t* header, | |
291 size_t packet_length) { | |
292 // Read header length (in bytes) from packet data. | |
293 if (packet_length < 12u) { | |
294 return; // Don't read outside the packet. | |
295 } | |
296 const bool x = (header[0] & 0x10) != 0; | |
297 const uint8_t cc = header[0] & 0x0f; | |
298 size_t header_length = 12u + cc * 4u; | |
299 | |
300 if (x) { | |
301 if (packet_length < 12u + cc * 4u + 4u) { | |
302 return; // Don't read outside the packet. | |
303 } | |
304 size_t x_len = ByteReader<uint16_t>::ReadBigEndian(header + 14 + cc * 4); | |
305 header_length += (x_len + 1) * 4; | |
306 } | |
307 | |
308 std::unique_ptr<rtclog::Event> rtp_event(new rtclog::Event()); | |
309 rtp_event->set_timestamp_us(clock_->TimeInMicroseconds()); | |
310 rtp_event->set_type(rtclog::Event::RTP_EVENT); | |
311 rtp_event->mutable_rtp_packet()->set_incoming(direction == kIncomingPacket); | |
312 rtp_event->mutable_rtp_packet()->set_type(ConvertMediaType(media_type)); | |
313 rtp_event->mutable_rtp_packet()->set_packet_length(packet_length); | |
314 rtp_event->mutable_rtp_packet()->set_header(header, header_length); | |
315 StoreEvent(&rtp_event); | |
316 } | |
317 | |
318 void RtcEventLogImpl::LogRtcpPacket(PacketDirection direction, | |
319 MediaType media_type, | |
320 const uint8_t* packet, | |
321 size_t length) { | |
322 std::unique_ptr<rtclog::Event> rtcp_event(new rtclog::Event()); | |
323 rtcp_event->set_timestamp_us(clock_->TimeInMicroseconds()); | |
324 rtcp_event->set_type(rtclog::Event::RTCP_EVENT); | |
325 rtcp_event->mutable_rtcp_packet()->set_incoming(direction == kIncomingPacket); | |
326 rtcp_event->mutable_rtcp_packet()->set_type(ConvertMediaType(media_type)); | |
327 | |
328 RTCPUtility::RtcpCommonHeader header; | |
329 const uint8_t* block_begin = packet; | |
330 const uint8_t* packet_end = packet + length; | |
331 RTC_DCHECK(length <= IP_PACKET_SIZE); | |
332 uint8_t buffer[IP_PACKET_SIZE]; | |
333 uint32_t buffer_length = 0; | |
334 while (block_begin < packet_end) { | |
335 if (!RtcpParseCommonHeader(block_begin, packet_end - block_begin, | |
336 &header)) { | |
337 break; // Incorrect message header. | |
338 } | |
339 uint32_t block_size = header.BlockSize(); | |
340 switch (header.packet_type) { | |
341 case RTCPUtility::PT_SR: | |
342 FALLTHROUGH(); | |
343 case RTCPUtility::PT_RR: | |
344 FALLTHROUGH(); | |
345 case RTCPUtility::PT_BYE: | |
346 FALLTHROUGH(); | |
347 case RTCPUtility::PT_IJ: | |
348 FALLTHROUGH(); | |
349 case RTCPUtility::PT_RTPFB: | |
350 FALLTHROUGH(); | |
351 case RTCPUtility::PT_PSFB: | |
352 FALLTHROUGH(); | |
353 case RTCPUtility::PT_XR: | |
354 // We log sender reports, receiver reports, bye messages | |
355 // inter-arrival jitter, third-party loss reports, payload-specific | |
356 // feedback and extended reports. | |
357 memcpy(buffer + buffer_length, block_begin, block_size); | |
358 buffer_length += block_size; | |
359 break; | |
360 case RTCPUtility::PT_SDES: | |
361 FALLTHROUGH(); | |
362 case RTCPUtility::PT_APP: | |
363 FALLTHROUGH(); | |
364 default: | |
365 // We don't log sender descriptions, application defined messages | |
366 // or message blocks of unknown type. | |
367 break; | |
368 } | |
369 | |
370 block_begin += block_size; | |
371 } | |
372 rtcp_event->mutable_rtcp_packet()->set_packet_data(buffer, buffer_length); | |
373 StoreEvent(&rtcp_event); | |
374 } | |
375 | |
376 void RtcEventLogImpl::LogAudioPlayout(uint32_t ssrc) { | |
377 std::unique_ptr<rtclog::Event> event(new rtclog::Event()); | |
378 event->set_timestamp_us(clock_->TimeInMicroseconds()); | |
379 event->set_type(rtclog::Event::AUDIO_PLAYOUT_EVENT); | |
380 auto playout_event = event->mutable_audio_playout_event(); | |
381 playout_event->set_local_ssrc(ssrc); | |
382 StoreEvent(&event); | |
383 } | |
384 | |
385 void RtcEventLogImpl::LogBwePacketLossEvent(int32_t bitrate, | |
386 uint8_t fraction_loss, | |
387 int32_t total_packets) { | |
388 std::unique_ptr<rtclog::Event> event(new rtclog::Event()); | |
389 event->set_timestamp_us(clock_->TimeInMicroseconds()); | |
390 event->set_type(rtclog::Event::BWE_PACKET_LOSS_EVENT); | |
391 auto bwe_event = event->mutable_bwe_packet_loss_event(); | |
392 bwe_event->set_bitrate(bitrate); | |
393 bwe_event->set_fraction_loss(fraction_loss); | |
394 bwe_event->set_total_packets(total_packets); | |
395 StoreEvent(&event); | |
396 } | |
397 | |
398 void RtcEventLogImpl::StoreEvent(std::unique_ptr<rtclog::Event>* event) { | |
399 if (!event_queue_.Insert(event)) { | |
400 LOG(LS_ERROR) << "WebRTC event log queue full. Dropping event."; | |
401 } | |
402 helper_thread_.SignalNewEvent(); | |
403 } | |
404 | |
405 bool RtcEventLog::ParseRtcEventLog(const std::string& file_name, | |
406 rtclog::EventStream* result) { | |
407 char tmp_buffer[1024]; | |
408 int bytes_read = 0; | |
409 std::unique_ptr<FileWrapper> dump_file(FileWrapper::Create()); | |
410 if (!dump_file->OpenFile(file_name.c_str(), true)) { | |
411 return false; | |
412 } | |
413 std::string dump_buffer; | |
414 while ((bytes_read = dump_file->Read(tmp_buffer, sizeof(tmp_buffer))) > 0) { | |
415 dump_buffer.append(tmp_buffer, bytes_read); | |
416 } | |
417 dump_file->CloseFile(); | |
418 return result->ParseFromString(dump_buffer); | |
419 } | |
420 | |
421 #endif // ENABLE_RTC_EVENT_LOG | |
422 | |
423 bool RtcEventLogNullImpl::StartLogging(rtc::PlatformFile platform_file, | |
424 int64_t max_size_bytes) { | |
425 // The platform_file is open and needs to be closed. | |
426 if (!rtc::ClosePlatformFile(platform_file)) { | |
427 LOG(LS_ERROR) << "Can't close file."; | |
428 } | |
429 return false; | |
430 } | |
431 | |
432 // RtcEventLog member functions. | |
433 std::unique_ptr<RtcEventLog> RtcEventLog::Create(const Clock* clock) { | |
434 #ifdef ENABLE_RTC_EVENT_LOG | |
435 return std::unique_ptr<RtcEventLog>(new RtcEventLogImpl(clock)); | |
436 #else | |
437 return std::unique_ptr<RtcEventLog>(new RtcEventLogNullImpl()); | |
438 #endif // ENABLE_RTC_EVENT_LOG | |
439 } | |
440 | |
441 std::unique_ptr<RtcEventLog> RtcEventLog::CreateNull() { | |
442 return std::unique_ptr<RtcEventLog>(new RtcEventLogNullImpl()); | |
443 } | |
444 | |
445 } // namespace webrtc | |
OLD | NEW |