OLD | NEW |
| (Empty) |
1 /* | |
2 * Copyright (c) 2016 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_parser.h" | |
12 | |
13 #include <string.h> | |
14 | |
15 #include <fstream> | |
16 #include <istream> | |
17 #include <utility> | |
18 | |
19 #include "webrtc/base/checks.h" | |
20 #include "webrtc/base/logging.h" | |
21 #include "webrtc/call.h" | |
22 #include "webrtc/call/rtc_event_log.h" | |
23 #include "webrtc/modules/rtp_rtcp/include/rtp_rtcp_defines.h" | |
24 #include "webrtc/system_wrappers/include/file_wrapper.h" | |
25 | |
26 namespace webrtc { | |
27 | |
28 namespace { | |
29 MediaType GetRuntimeMediaType(rtclog::MediaType media_type) { | |
30 switch (media_type) { | |
31 case rtclog::MediaType::ANY: | |
32 return MediaType::ANY; | |
33 case rtclog::MediaType::AUDIO: | |
34 return MediaType::AUDIO; | |
35 case rtclog::MediaType::VIDEO: | |
36 return MediaType::VIDEO; | |
37 case rtclog::MediaType::DATA: | |
38 return MediaType::DATA; | |
39 } | |
40 RTC_NOTREACHED(); | |
41 return MediaType::ANY; | |
42 } | |
43 | |
44 RtcpMode GetRuntimeRtcpMode(rtclog::VideoReceiveConfig::RtcpMode rtcp_mode) { | |
45 switch (rtcp_mode) { | |
46 case rtclog::VideoReceiveConfig::RTCP_COMPOUND: | |
47 return RtcpMode::kCompound; | |
48 case rtclog::VideoReceiveConfig::RTCP_REDUCEDSIZE: | |
49 return RtcpMode::kReducedSize; | |
50 } | |
51 RTC_NOTREACHED(); | |
52 return RtcpMode::kOff; | |
53 } | |
54 | |
55 ParsedRtcEventLog::EventType GetRuntimeEventType( | |
56 rtclog::Event::EventType event_type) { | |
57 switch (event_type) { | |
58 case rtclog::Event::UNKNOWN_EVENT: | |
59 return ParsedRtcEventLog::EventType::UNKNOWN_EVENT; | |
60 case rtclog::Event::LOG_START: | |
61 return ParsedRtcEventLog::EventType::LOG_START; | |
62 case rtclog::Event::LOG_END: | |
63 return ParsedRtcEventLog::EventType::LOG_END; | |
64 case rtclog::Event::RTP_EVENT: | |
65 return ParsedRtcEventLog::EventType::RTP_EVENT; | |
66 case rtclog::Event::RTCP_EVENT: | |
67 return ParsedRtcEventLog::EventType::RTCP_EVENT; | |
68 case rtclog::Event::AUDIO_PLAYOUT_EVENT: | |
69 return ParsedRtcEventLog::EventType::AUDIO_PLAYOUT_EVENT; | |
70 case rtclog::Event::BWE_PACKET_LOSS_EVENT: | |
71 return ParsedRtcEventLog::EventType::BWE_PACKET_LOSS_EVENT; | |
72 case rtclog::Event::BWE_PACKET_DELAY_EVENT: | |
73 return ParsedRtcEventLog::EventType::BWE_PACKET_DELAY_EVENT; | |
74 case rtclog::Event::VIDEO_RECEIVER_CONFIG_EVENT: | |
75 return ParsedRtcEventLog::EventType::VIDEO_RECEIVER_CONFIG_EVENT; | |
76 case rtclog::Event::VIDEO_SENDER_CONFIG_EVENT: | |
77 return ParsedRtcEventLog::EventType::VIDEO_SENDER_CONFIG_EVENT; | |
78 case rtclog::Event::AUDIO_RECEIVER_CONFIG_EVENT: | |
79 return ParsedRtcEventLog::EventType::AUDIO_RECEIVER_CONFIG_EVENT; | |
80 case rtclog::Event::AUDIO_SENDER_CONFIG_EVENT: | |
81 return ParsedRtcEventLog::EventType::AUDIO_SENDER_CONFIG_EVENT; | |
82 } | |
83 RTC_NOTREACHED(); | |
84 return ParsedRtcEventLog::EventType::UNKNOWN_EVENT; | |
85 } | |
86 | |
87 std::pair<uint64_t, bool> ParseVarInt(std::istream& stream) { | |
88 uint64_t varint = 0; | |
89 for (size_t bytes_read = 0; bytes_read < 10; ++bytes_read) { | |
90 // The most significant bit of each byte is 0 if it is the last byte in | |
91 // the varint and 1 otherwise. Thus, we take the 7 least significant bits | |
92 // of each byte and shift them 7 bits for each byte read previously to get | |
93 // the (unsigned) integer. | |
94 int byte = stream.get(); | |
95 if (stream.eof()) { | |
96 return std::make_pair(varint, false); | |
97 } | |
98 RTC_DCHECK(0 <= byte && byte <= 255); | |
99 varint |= static_cast<uint64_t>(byte & 0x7F) << (7 * bytes_read); | |
100 if ((byte & 0x80) == 0) { | |
101 return std::make_pair(varint, true); | |
102 } | |
103 } | |
104 return std::make_pair(varint, false); | |
105 } | |
106 | |
107 } // namespace | |
108 | |
109 bool ParsedRtcEventLog::ParseFile(const std::string& filename) { | |
110 std::ifstream file(filename, std::ios_base::in | std::ios_base::binary); | |
111 if (!file.good() || !file.is_open()) { | |
112 LOG(LS_WARNING) << "Could not open file for reading."; | |
113 return false; | |
114 } | |
115 | |
116 return ParseStream(file); | |
117 } | |
118 | |
119 bool ParsedRtcEventLog::ParseString(const std::string& s) { | |
120 std::istringstream stream(s, std::ios_base::in | std::ios_base::binary); | |
121 return ParseStream(stream); | |
122 } | |
123 | |
124 bool ParsedRtcEventLog::ParseStream(std::istream& stream) { | |
125 events_.clear(); | |
126 const size_t kMaxEventSize = (1u << 16) - 1; | |
127 std::vector<char> tmp_buffer(kMaxEventSize); | |
128 uint64_t tag; | |
129 uint64_t message_length; | |
130 bool success; | |
131 | |
132 RTC_DCHECK(stream.good()); | |
133 | |
134 while (1) { | |
135 // Check whether we have reached end of file. | |
136 stream.peek(); | |
137 if (stream.eof()) { | |
138 return true; | |
139 } | |
140 | |
141 // Read the next message tag. The tag number is defined as | |
142 // (fieldnumber << 3) | wire_type. In our case, the field number is | |
143 // supposed to be 1 and the wire type for an length-delimited field is 2. | |
144 const uint64_t kExpectedTag = (1 << 3) | 2; | |
145 std::tie(tag, success) = ParseVarInt(stream); | |
146 if (!success) { | |
147 LOG(LS_WARNING) << "Missing field tag from beginning of protobuf event."; | |
148 return false; | |
149 } else if (tag != kExpectedTag) { | |
150 LOG(LS_WARNING) << "Unexpected field tag at beginning of protobuf event."; | |
151 return false; | |
152 } | |
153 | |
154 // Read the length field. | |
155 std::tie(message_length, success) = ParseVarInt(stream); | |
156 if (!success) { | |
157 LOG(LS_WARNING) << "Missing message length after protobuf field tag."; | |
158 return false; | |
159 } else if (message_length > kMaxEventSize) { | |
160 LOG(LS_WARNING) << "Protobuf message length is too large."; | |
161 return false; | |
162 } | |
163 | |
164 // Read the next protobuf event to a temporary char buffer. | |
165 stream.read(tmp_buffer.data(), message_length); | |
166 if (stream.gcount() != static_cast<int>(message_length)) { | |
167 LOG(LS_WARNING) << "Failed to read protobuf message from file."; | |
168 return false; | |
169 } | |
170 | |
171 // Parse the protobuf event from the buffer. | |
172 rtclog::Event event; | |
173 if (!event.ParseFromArray(tmp_buffer.data(), message_length)) { | |
174 LOG(LS_WARNING) << "Failed to parse protobuf message."; | |
175 return false; | |
176 } | |
177 events_.push_back(event); | |
178 } | |
179 } | |
180 | |
181 size_t ParsedRtcEventLog::GetNumberOfEvents() const { | |
182 return events_.size(); | |
183 } | |
184 | |
185 int64_t ParsedRtcEventLog::GetTimestamp(size_t index) const { | |
186 RTC_CHECK_LT(index, GetNumberOfEvents()); | |
187 const rtclog::Event& event = events_[index]; | |
188 RTC_CHECK(event.has_timestamp_us()); | |
189 return event.timestamp_us(); | |
190 } | |
191 | |
192 ParsedRtcEventLog::EventType ParsedRtcEventLog::GetEventType( | |
193 size_t index) const { | |
194 RTC_CHECK_LT(index, GetNumberOfEvents()); | |
195 const rtclog::Event& event = events_[index]; | |
196 RTC_CHECK(event.has_type()); | |
197 return GetRuntimeEventType(event.type()); | |
198 } | |
199 | |
200 // The header must have space for at least IP_PACKET_SIZE bytes. | |
201 void ParsedRtcEventLog::GetRtpHeader(size_t index, | |
202 PacketDirection* incoming, | |
203 MediaType* media_type, | |
204 uint8_t* header, | |
205 size_t* header_length, | |
206 size_t* total_length) const { | |
207 RTC_CHECK_LT(index, GetNumberOfEvents()); | |
208 const rtclog::Event& event = events_[index]; | |
209 RTC_CHECK(event.has_type()); | |
210 RTC_CHECK_EQ(event.type(), rtclog::Event::RTP_EVENT); | |
211 RTC_CHECK(event.has_rtp_packet()); | |
212 const rtclog::RtpPacket& rtp_packet = event.rtp_packet(); | |
213 // Get direction of packet. | |
214 RTC_CHECK(rtp_packet.has_incoming()); | |
215 if (incoming != nullptr) { | |
216 *incoming = rtp_packet.incoming() ? kIncomingPacket : kOutgoingPacket; | |
217 } | |
218 // Get media type. | |
219 RTC_CHECK(rtp_packet.has_type()); | |
220 if (media_type != nullptr) { | |
221 *media_type = GetRuntimeMediaType(rtp_packet.type()); | |
222 } | |
223 // Get packet length. | |
224 RTC_CHECK(rtp_packet.has_packet_length()); | |
225 if (total_length != nullptr) { | |
226 *total_length = rtp_packet.packet_length(); | |
227 } | |
228 // Get header length. | |
229 RTC_CHECK(rtp_packet.has_header()); | |
230 if (header_length != nullptr) { | |
231 *header_length = rtp_packet.header().size(); | |
232 } | |
233 // Get header contents. | |
234 if (header != nullptr) { | |
235 const size_t kMinRtpHeaderSize = 12; | |
236 RTC_CHECK_GE(rtp_packet.header().size(), kMinRtpHeaderSize); | |
237 RTC_CHECK_LE(rtp_packet.header().size(), | |
238 static_cast<size_t>(IP_PACKET_SIZE)); | |
239 memcpy(header, rtp_packet.header().data(), rtp_packet.header().size()); | |
240 } | |
241 } | |
242 | |
243 // The packet must have space for at least IP_PACKET_SIZE bytes. | |
244 void ParsedRtcEventLog::GetRtcpPacket(size_t index, | |
245 PacketDirection* incoming, | |
246 MediaType* media_type, | |
247 uint8_t* packet, | |
248 size_t* length) const { | |
249 RTC_CHECK_LT(index, GetNumberOfEvents()); | |
250 const rtclog::Event& event = events_[index]; | |
251 RTC_CHECK(event.has_type()); | |
252 RTC_CHECK_EQ(event.type(), rtclog::Event::RTCP_EVENT); | |
253 RTC_CHECK(event.has_rtcp_packet()); | |
254 const rtclog::RtcpPacket& rtcp_packet = event.rtcp_packet(); | |
255 // Get direction of packet. | |
256 RTC_CHECK(rtcp_packet.has_incoming()); | |
257 if (incoming != nullptr) { | |
258 *incoming = rtcp_packet.incoming() ? kIncomingPacket : kOutgoingPacket; | |
259 } | |
260 // Get media type. | |
261 RTC_CHECK(rtcp_packet.has_type()); | |
262 if (media_type != nullptr) { | |
263 *media_type = GetRuntimeMediaType(rtcp_packet.type()); | |
264 } | |
265 // Get packet length. | |
266 RTC_CHECK(rtcp_packet.has_packet_data()); | |
267 if (length != nullptr) { | |
268 *length = rtcp_packet.packet_data().size(); | |
269 } | |
270 // Get packet contents. | |
271 if (packet != nullptr) { | |
272 RTC_CHECK_LE(rtcp_packet.packet_data().size(), | |
273 static_cast<unsigned>(IP_PACKET_SIZE)); | |
274 memcpy(packet, rtcp_packet.packet_data().data(), | |
275 rtcp_packet.packet_data().size()); | |
276 } | |
277 } | |
278 | |
279 void ParsedRtcEventLog::GetVideoReceiveConfig( | |
280 size_t index, | |
281 VideoReceiveStream::Config* config) const { | |
282 RTC_CHECK_LT(index, GetNumberOfEvents()); | |
283 const rtclog::Event& event = events_[index]; | |
284 RTC_CHECK(config != nullptr); | |
285 RTC_CHECK(event.has_type()); | |
286 RTC_CHECK_EQ(event.type(), rtclog::Event::VIDEO_RECEIVER_CONFIG_EVENT); | |
287 RTC_CHECK(event.has_video_receiver_config()); | |
288 const rtclog::VideoReceiveConfig& receiver_config = | |
289 event.video_receiver_config(); | |
290 // Get SSRCs. | |
291 RTC_CHECK(receiver_config.has_remote_ssrc()); | |
292 config->rtp.remote_ssrc = receiver_config.remote_ssrc(); | |
293 RTC_CHECK(receiver_config.has_local_ssrc()); | |
294 config->rtp.local_ssrc = receiver_config.local_ssrc(); | |
295 // Get RTCP settings. | |
296 RTC_CHECK(receiver_config.has_rtcp_mode()); | |
297 config->rtp.rtcp_mode = GetRuntimeRtcpMode(receiver_config.rtcp_mode()); | |
298 RTC_CHECK(receiver_config.has_remb()); | |
299 config->rtp.remb = receiver_config.remb(); | |
300 // Get RTX map. | |
301 config->rtp.rtx.clear(); | |
302 for (int i = 0; i < receiver_config.rtx_map_size(); i++) { | |
303 const rtclog::RtxMap& map = receiver_config.rtx_map(i); | |
304 RTC_CHECK(map.has_payload_type()); | |
305 RTC_CHECK(map.has_config()); | |
306 RTC_CHECK(map.config().has_rtx_ssrc()); | |
307 RTC_CHECK(map.config().has_rtx_payload_type()); | |
308 webrtc::VideoReceiveStream::Config::Rtp::Rtx rtx_pair; | |
309 rtx_pair.ssrc = map.config().rtx_ssrc(); | |
310 rtx_pair.payload_type = map.config().rtx_payload_type(); | |
311 config->rtp.rtx.insert(std::make_pair(map.payload_type(), rtx_pair)); | |
312 } | |
313 // Get header extensions. | |
314 config->rtp.extensions.clear(); | |
315 for (int i = 0; i < receiver_config.header_extensions_size(); i++) { | |
316 RTC_CHECK(receiver_config.header_extensions(i).has_name()); | |
317 RTC_CHECK(receiver_config.header_extensions(i).has_id()); | |
318 const std::string& name = receiver_config.header_extensions(i).name(); | |
319 int id = receiver_config.header_extensions(i).id(); | |
320 config->rtp.extensions.push_back(RtpExtension(name, id)); | |
321 } | |
322 // Get decoders. | |
323 config->decoders.clear(); | |
324 for (int i = 0; i < receiver_config.decoders_size(); i++) { | |
325 RTC_CHECK(receiver_config.decoders(i).has_name()); | |
326 RTC_CHECK(receiver_config.decoders(i).has_payload_type()); | |
327 VideoReceiveStream::Decoder decoder; | |
328 decoder.payload_name = receiver_config.decoders(i).name(); | |
329 decoder.payload_type = receiver_config.decoders(i).payload_type(); | |
330 config->decoders.push_back(decoder); | |
331 } | |
332 } | |
333 | |
334 void ParsedRtcEventLog::GetVideoSendConfig( | |
335 size_t index, | |
336 VideoSendStream::Config* config) const { | |
337 RTC_CHECK_LT(index, GetNumberOfEvents()); | |
338 const rtclog::Event& event = events_[index]; | |
339 RTC_CHECK(config != nullptr); | |
340 RTC_CHECK(event.has_type()); | |
341 RTC_CHECK_EQ(event.type(), rtclog::Event::VIDEO_SENDER_CONFIG_EVENT); | |
342 RTC_CHECK(event.has_video_sender_config()); | |
343 const rtclog::VideoSendConfig& sender_config = event.video_sender_config(); | |
344 // Get SSRCs. | |
345 config->rtp.ssrcs.clear(); | |
346 for (int i = 0; i < sender_config.ssrcs_size(); i++) { | |
347 config->rtp.ssrcs.push_back(sender_config.ssrcs(i)); | |
348 } | |
349 // Get header extensions. | |
350 config->rtp.extensions.clear(); | |
351 for (int i = 0; i < sender_config.header_extensions_size(); i++) { | |
352 RTC_CHECK(sender_config.header_extensions(i).has_name()); | |
353 RTC_CHECK(sender_config.header_extensions(i).has_id()); | |
354 const std::string& name = sender_config.header_extensions(i).name(); | |
355 int id = sender_config.header_extensions(i).id(); | |
356 config->rtp.extensions.push_back(RtpExtension(name, id)); | |
357 } | |
358 // Get RTX settings. | |
359 config->rtp.rtx.ssrcs.clear(); | |
360 for (int i = 0; i < sender_config.rtx_ssrcs_size(); i++) { | |
361 config->rtp.rtx.ssrcs.push_back(sender_config.rtx_ssrcs(i)); | |
362 } | |
363 if (sender_config.rtx_ssrcs_size() > 0) { | |
364 RTC_CHECK(sender_config.has_rtx_payload_type()); | |
365 config->rtp.rtx.payload_type = sender_config.rtx_payload_type(); | |
366 } else { | |
367 // Reset RTX payload type default value if no RTX SSRCs are used. | |
368 config->rtp.rtx.payload_type = -1; | |
369 } | |
370 // Get encoder. | |
371 RTC_CHECK(sender_config.has_encoder()); | |
372 RTC_CHECK(sender_config.encoder().has_name()); | |
373 RTC_CHECK(sender_config.encoder().has_payload_type()); | |
374 config->encoder_settings.payload_name = sender_config.encoder().name(); | |
375 config->encoder_settings.payload_type = | |
376 sender_config.encoder().payload_type(); | |
377 } | |
378 | |
379 void ParsedRtcEventLog::GetAudioPlayout(size_t index, uint32_t* ssrc) const { | |
380 RTC_CHECK_LT(index, GetNumberOfEvents()); | |
381 const rtclog::Event& event = events_[index]; | |
382 RTC_CHECK(event.has_type()); | |
383 RTC_CHECK_EQ(event.type(), rtclog::Event::AUDIO_PLAYOUT_EVENT); | |
384 RTC_CHECK(event.has_audio_playout_event()); | |
385 const rtclog::AudioPlayoutEvent& loss_event = event.audio_playout_event(); | |
386 RTC_CHECK(loss_event.has_local_ssrc()); | |
387 if (ssrc != nullptr) { | |
388 *ssrc = loss_event.local_ssrc(); | |
389 } | |
390 } | |
391 | |
392 void ParsedRtcEventLog::GetBwePacketLossEvent(size_t index, | |
393 int32_t* bitrate, | |
394 uint8_t* fraction_loss, | |
395 int32_t* total_packets) const { | |
396 RTC_CHECK_LT(index, GetNumberOfEvents()); | |
397 const rtclog::Event& event = events_[index]; | |
398 RTC_CHECK(event.has_type()); | |
399 RTC_CHECK_EQ(event.type(), rtclog::Event::BWE_PACKET_LOSS_EVENT); | |
400 RTC_CHECK(event.has_bwe_packet_loss_event()); | |
401 const rtclog::BwePacketLossEvent& loss_event = event.bwe_packet_loss_event(); | |
402 RTC_CHECK(loss_event.has_bitrate()); | |
403 if (bitrate != nullptr) { | |
404 *bitrate = loss_event.bitrate(); | |
405 } | |
406 RTC_CHECK(loss_event.has_fraction_loss()); | |
407 if (fraction_loss != nullptr) { | |
408 *fraction_loss = loss_event.fraction_loss(); | |
409 } | |
410 RTC_CHECK(loss_event.has_total_packets()); | |
411 if (total_packets != nullptr) { | |
412 *total_packets = loss_event.total_packets(); | |
413 } | |
414 } | |
415 | |
416 } // namespace webrtc | |
OLD | NEW |