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_parser.h" | |
12 | |
13 #include <string.h> | |
14 | |
15 #include <fstream> | |
16 | |
17 #include "webrtc/base/checks.h" | |
18 #include "webrtc/base/logging.h" | |
19 #include "webrtc/base/scoped_ptr.h" | |
20 #include "webrtc/call.h" | |
21 #include "webrtc/call/rtc_event_log.h" | |
22 #include "webrtc/modules/rtp_rtcp/include/rtp_rtcp_defines.h" | |
23 #include "webrtc/system_wrappers/include/file_wrapper.h" | |
24 | |
25 namespace webrtc { | |
26 | |
27 namespace { | |
28 MediaType GetRuntimeMediaType(rtclog::MediaType media_type) { | |
29 switch (media_type) { | |
30 case rtclog::MediaType::ANY: | |
31 return MediaType::ANY; | |
32 case rtclog::MediaType::AUDIO: | |
33 return MediaType::AUDIO; | |
34 case rtclog::MediaType::VIDEO: | |
35 return MediaType::VIDEO; | |
36 case rtclog::MediaType::DATA: | |
37 return MediaType::DATA; | |
38 } | |
39 RTC_NOTREACHED(); | |
40 return MediaType::ANY; | |
41 } | |
42 | |
43 RtcpMode GetRuntimeRtcpMode(rtclog::VideoReceiveConfig::RtcpMode rtcp_mode) { | |
44 switch (rtcp_mode) { | |
45 case rtclog::VideoReceiveConfig::RTCP_COMPOUND: | |
46 return RtcpMode::kCompound; | |
47 case rtclog::VideoReceiveConfig::RTCP_REDUCEDSIZE: | |
48 return RtcpMode::kReducedSize; | |
49 } | |
50 RTC_NOTREACHED(); | |
51 return RtcpMode::kOff; | |
52 } | |
53 | |
54 ParsedRtcEventLog::EventType GetRuntimeEventType( | |
55 rtclog::Event::EventType event_type) { | |
56 switch (event_type) { | |
57 case rtclog::Event::UNKNOWN_EVENT: | |
58 return ParsedRtcEventLog::EventType::UNKNOWN_EVENT; | |
59 case rtclog::Event::LOG_START: | |
60 return ParsedRtcEventLog::EventType::LOG_START; | |
61 case rtclog::Event::LOG_END: | |
62 return ParsedRtcEventLog::EventType::LOG_END; | |
63 case rtclog::Event::RTP_EVENT: | |
64 return ParsedRtcEventLog::EventType::RTP_EVENT; | |
65 case rtclog::Event::RTCP_EVENT: | |
66 return ParsedRtcEventLog::EventType::RTCP_EVENT; | |
67 case rtclog::Event::AUDIO_PLAYOUT_EVENT: | |
68 return ParsedRtcEventLog::EventType::AUDIO_PLAYOUT_EVENT; | |
69 case rtclog::Event::BWE_PACKET_LOSS_EVENT: | |
70 return ParsedRtcEventLog::EventType::BWE_PACKET_LOSS_EVENT; | |
71 case rtclog::Event::BWE_PACKET_DELAY_EVENT: | |
72 return ParsedRtcEventLog::EventType::BWE_PACKET_DELAY_EVENT; | |
73 case rtclog::Event::VIDEO_RECEIVER_CONFIG_EVENT: | |
74 return ParsedRtcEventLog::EventType::VIDEO_RECEIVER_CONFIG_EVENT; | |
75 case rtclog::Event::VIDEO_SENDER_CONFIG_EVENT: | |
76 return ParsedRtcEventLog::EventType::VIDEO_SENDER_CONFIG_EVENT; | |
77 case rtclog::Event::AUDIO_RECEIVER_CONFIG_EVENT: | |
78 return ParsedRtcEventLog::EventType::AUDIO_RECEIVER_CONFIG_EVENT; | |
79 case rtclog::Event::AUDIO_SENDER_CONFIG_EVENT: | |
80 return ParsedRtcEventLog::EventType::AUDIO_SENDER_CONFIG_EVENT; | |
81 } | |
82 RTC_NOTREACHED(); | |
83 return ParsedRtcEventLog::EventType::UNKNOWN_EVENT; | |
84 } | |
85 | |
86 bool ParseVarInt(std::FILE* file, uint64_t* varint, size_t* bytes_read) { | |
87 uint8_t byte; | |
88 *varint = 0; | |
89 for (*bytes_read = 0; *bytes_read < 10 && fread(&byte, 1, 1, file) == 1; | |
90 (*bytes_read)++) { | |
91 *varint |= static_cast<uint64_t>(byte & 0x7F) << (7 * *bytes_read); | |
ivoc
2016/04/01 08:27:59
Would be a good idea to add some comments here to
terelius
2016/04/19 17:01:45
Done.
| |
92 if ((byte & 0x80) == 0) { | |
93 return true; | |
94 } | |
95 } | |
96 return false; | |
ivoc
2016/04/01 08:27:59
How about returning bytes_read from this function,
terelius
2016/04/19 17:01:45
It is slightly more complicated because we also ne
| |
97 } | |
98 | |
99 } // namespace | |
100 | |
101 bool ParsedRtcEventLog::ParseFile(const std::string& filename) { | |
102 stream_.clear(); | |
103 const size_t kMaxEventSize = 1u << 16; | |
104 char tmp_buffer[kMaxEventSize]; | |
105 | |
106 std::FILE* file = fopen(filename.c_str(), "rb"); | |
107 if (!file) { | |
108 LOG(LS_WARNING) << "Could not open file for reading."; | |
109 return false; | |
110 } | |
111 | |
112 while (1) { | |
113 // Peek at the next message tag. | |
114 uint64_t tag, | |
115 expected_tag = (1 << 3) | 2; // (fieldnumber << 3) | wire_type | |
ivoc
2016/04/01 08:27:59
Some explanation for the bit juggling would be nic
terelius
2016/04/19 17:01:45
I've added a little, but it is hard to give any ju
| |
116 size_t bytes_read; | |
117 if (!ParseVarInt(file, &tag, &bytes_read) || tag != expected_tag) { | |
118 fclose(file); | |
119 if (bytes_read == 0) { | |
120 return true; // Reached end of file. | |
121 } | |
122 LOG(LS_WARNING) | |
123 << "Missing expected tag from beginning of protobuf event."; | |
124 return false; | |
125 } | |
126 | |
127 // Peek at the length field. | |
128 uint64_t message_length; | |
129 if (!ParseVarInt(file, &message_length, &bytes_read) || | |
130 message_length >= kMaxEventSize) { | |
131 LOG(LS_WARNING) << "Missing message length after protobuf field tag."; | |
132 fclose(file); | |
133 return false; | |
134 } | |
135 | |
136 if (fread(tmp_buffer, 1, message_length, file) != message_length) { | |
137 LOG(LS_WARNING) << "Failed to read protobuf message from file."; | |
138 fclose(file); | |
139 return false; | |
140 } | |
141 | |
142 rtclog::Event event; | |
143 if (!event.ParseFromArray(tmp_buffer, message_length)) { | |
144 LOG(LS_WARNING) << "Failed to parse protobuf message."; | |
145 fclose(file); | |
146 return false; | |
147 } | |
148 stream_.push_back(event); | |
149 } | |
150 } | |
151 | |
152 size_t ParsedRtcEventLog::GetNumberOfEvents() const { | |
153 return stream_.size(); | |
154 } | |
155 | |
156 int64_t ParsedRtcEventLog::GetTimestamp(size_t i) const { | |
157 RTC_CHECK_LT(i, GetNumberOfEvents()); | |
158 const rtclog::Event& event = stream_[i]; | |
159 RTC_CHECK(event.has_timestamp_us()); | |
160 return event.timestamp_us(); | |
161 } | |
162 | |
163 ParsedRtcEventLog::EventType ParsedRtcEventLog::GetEventType(size_t i) const { | |
164 RTC_CHECK_LT(i, GetNumberOfEvents()); | |
165 const rtclog::Event& event = stream_[i]; | |
166 RTC_CHECK(event.has_type()); | |
167 return GetRuntimeEventType(event.type()); | |
168 } | |
169 | |
170 // The header must have space for at least IP_PACKET_SIZE bytes. | |
171 void ParsedRtcEventLog::GetRtpHeader(size_t i, | |
172 PacketDirection* incoming, | |
173 MediaType* media_type, | |
174 uint8_t* header, | |
175 size_t* header_length, | |
176 size_t* total_length) const { | |
177 RTC_CHECK_LT(i, GetNumberOfEvents()); | |
178 const rtclog::Event& event = stream_[i]; | |
179 RTC_CHECK(event.has_type()); | |
180 RTC_CHECK_EQ(event.type(), rtclog::Event::RTP_EVENT); | |
181 RTC_CHECK(event.has_rtp_packet()); | |
182 const rtclog::RtpPacket& rtp_packet = event.rtp_packet(); | |
183 // Get direction of packet. | |
184 RTC_CHECK(rtp_packet.has_incoming()); | |
ivoc
2016/04/01 08:27:59
I guess these CHECKs can be inside of the ifs, rig
terelius
2016/04/19 17:01:45
They could, but since these fields are specified a
ivoc
2016/04/25 08:08:11
Acknowledged.
| |
185 if (incoming != nullptr) | |
186 *incoming = rtp_packet.incoming() ? kIncomingPacket : kOutgoingPacket; | |
187 // Get media type. | |
188 RTC_CHECK(rtp_packet.has_type()); | |
189 if (media_type != nullptr) { | |
190 *media_type = GetRuntimeMediaType(rtp_packet.type()); | |
191 } | |
192 // Get packet length. | |
193 RTC_CHECK(rtp_packet.has_packet_length()); | |
194 if (total_length != nullptr) | |
195 *total_length = rtp_packet.packet_length(); | |
196 // Get header length. | |
197 RTC_CHECK(rtp_packet.has_header()); | |
198 if (header_length != nullptr) | |
199 *header_length = rtp_packet.header().size(); | |
200 // Get header contents. | |
201 if (header != nullptr) { | |
202 RTC_CHECK_GE(rtp_packet.header().size(), 12u); | |
203 RTC_CHECK_LE(rtp_packet.header().size(), | |
204 static_cast<unsigned>(IP_PACKET_SIZE)); | |
205 memcpy(header, rtp_packet.header().data(), rtp_packet.header().size()); | |
206 } | |
207 } | |
208 | |
209 // The packet must have space for at least IP_PACKET_SIZE bytes. | |
210 void ParsedRtcEventLog::GetRtcpPacket(size_t i, | |
211 PacketDirection* incoming, | |
212 MediaType* media_type, | |
213 uint8_t* packet, | |
214 size_t* length) const { | |
215 RTC_CHECK_LT(i, GetNumberOfEvents()); | |
216 const rtclog::Event& event = stream_[i]; | |
217 RTC_CHECK(event.has_type()); | |
218 RTC_CHECK_EQ(event.type(), rtclog::Event::RTCP_EVENT); | |
219 RTC_CHECK(event.has_rtcp_packet()); | |
220 const rtclog::RtcpPacket& rtcp_packet = event.rtcp_packet(); | |
221 // Get direction of packet. | |
222 RTC_CHECK(rtcp_packet.has_incoming()); | |
223 if (incoming != nullptr) | |
224 *incoming = rtcp_packet.incoming() ? kIncomingPacket : kOutgoingPacket; | |
225 // Get media type. | |
226 RTC_CHECK(rtcp_packet.has_type()); | |
227 if (media_type != nullptr) { | |
228 *media_type = GetRuntimeMediaType(rtcp_packet.type()); | |
229 } | |
230 // Get packet length. | |
231 RTC_CHECK(rtcp_packet.has_packet_data()); | |
232 if (length != nullptr) | |
233 *length = rtcp_packet.packet_data().size(); | |
234 // Get packet contents. | |
235 if (packet != nullptr) { | |
236 RTC_CHECK_LE(rtcp_packet.packet_data().size(), | |
237 static_cast<unsigned>(IP_PACKET_SIZE)); | |
238 memcpy(packet, rtcp_packet.packet_data().data(), | |
239 rtcp_packet.packet_data().size()); | |
240 } | |
241 } | |
242 | |
243 void ParsedRtcEventLog::GetVideoReceiveConfig( | |
244 size_t i, | |
245 VideoReceiveStream::Config* config) const { | |
246 RTC_CHECK_LT(i, GetNumberOfEvents()); | |
247 const rtclog::Event& event = stream_[i]; | |
248 RTC_CHECK(config != nullptr); | |
249 RTC_CHECK(event.has_type()); | |
250 RTC_CHECK_EQ(event.type(), rtclog::Event::VIDEO_RECEIVER_CONFIG_EVENT); | |
251 RTC_CHECK(event.has_video_receiver_config()); | |
252 const rtclog::VideoReceiveConfig& receiver_config = | |
253 event.video_receiver_config(); | |
254 // Get SSRCs. | |
255 RTC_CHECK(receiver_config.has_remote_ssrc()); | |
256 config->rtp.remote_ssrc = receiver_config.remote_ssrc(); | |
257 RTC_CHECK(receiver_config.has_local_ssrc()); | |
258 config->rtp.local_ssrc = receiver_config.local_ssrc(); | |
259 // Get RTCP settings. | |
260 RTC_CHECK(receiver_config.has_rtcp_mode()); | |
261 config->rtp.rtcp_mode = GetRuntimeRtcpMode(receiver_config.rtcp_mode()); | |
262 RTC_CHECK(receiver_config.has_remb()); | |
263 config->rtp.remb = receiver_config.remb(); | |
264 // Get RTX map. | |
265 config->rtp.rtx.clear(); | |
266 for (int i = 0; i < receiver_config.rtx_map_size(); i++) { | |
267 const rtclog::RtxMap& map = receiver_config.rtx_map(i); | |
268 RTC_CHECK(map.has_payload_type()); | |
269 RTC_CHECK(map.has_config()); | |
270 RTC_CHECK(map.config().has_rtx_ssrc()); | |
271 RTC_CHECK(map.config().has_rtx_payload_type()); | |
272 webrtc::VideoReceiveStream::Config::Rtp::Rtx rtx_pair; | |
273 rtx_pair.ssrc = map.config().rtx_ssrc(); | |
274 rtx_pair.payload_type = map.config().rtx_payload_type(); | |
275 config->rtp.rtx.insert(std::make_pair(map.payload_type(), rtx_pair)); | |
276 } | |
277 // Get header extensions. | |
278 config->rtp.extensions.clear(); | |
279 for (int i = 0; i < receiver_config.header_extensions_size(); i++) { | |
280 RTC_CHECK(receiver_config.header_extensions(i).has_name()); | |
281 RTC_CHECK(receiver_config.header_extensions(i).has_id()); | |
282 const std::string& name = receiver_config.header_extensions(i).name(); | |
283 int id = receiver_config.header_extensions(i).id(); | |
284 config->rtp.extensions.push_back(RtpExtension(name, id)); | |
285 } | |
286 // Get decoders. | |
287 config->decoders.clear(); | |
288 for (int i = 0; i < receiver_config.decoders_size(); i++) { | |
289 RTC_CHECK(receiver_config.decoders(i).has_name()); | |
290 RTC_CHECK(receiver_config.decoders(i).has_payload_type()); | |
291 VideoReceiveStream::Decoder decoder; | |
292 decoder.payload_name = receiver_config.decoders(i).name(); | |
293 decoder.payload_type = receiver_config.decoders(i).payload_type(); | |
294 config->decoders.push_back(decoder); | |
295 } | |
296 } | |
297 | |
298 void ParsedRtcEventLog::GetVideoSendConfig( | |
299 size_t i, | |
300 VideoSendStream::Config* config) const { | |
301 RTC_CHECK_LT(i, GetNumberOfEvents()); | |
302 const rtclog::Event& event = stream_[i]; | |
303 RTC_CHECK(config != nullptr); | |
304 RTC_CHECK(event.has_type()); | |
305 RTC_CHECK_EQ(event.type(), rtclog::Event::VIDEO_SENDER_CONFIG_EVENT); | |
306 RTC_CHECK(event.has_video_sender_config()); | |
307 const rtclog::VideoSendConfig& sender_config = event.video_sender_config(); | |
308 // Get SSRCs. | |
309 config->rtp.ssrcs.clear(); | |
310 for (int i = 0; i < sender_config.ssrcs_size(); i++) { | |
311 config->rtp.ssrcs.push_back(sender_config.ssrcs(i)); | |
312 } | |
313 // Get header extensions. | |
314 config->rtp.extensions.clear(); | |
315 for (int i = 0; i < sender_config.header_extensions_size(); i++) { | |
316 RTC_CHECK(sender_config.header_extensions(i).has_name()); | |
317 RTC_CHECK(sender_config.header_extensions(i).has_id()); | |
318 const std::string& name = sender_config.header_extensions(i).name(); | |
319 int id = sender_config.header_extensions(i).id(); | |
320 config->rtp.extensions.push_back(RtpExtension(name, id)); | |
321 } | |
322 // Get RTX settings. | |
323 config->rtp.rtx.ssrcs.clear(); | |
324 for (int i = 0; i < sender_config.rtx_ssrcs_size(); i++) { | |
325 config->rtp.rtx.ssrcs.push_back(sender_config.rtx_ssrcs(i)); | |
326 } | |
327 if (sender_config.rtx_ssrcs_size() > 0) { | |
328 RTC_CHECK(sender_config.has_rtx_payload_type()); | |
329 config->rtp.rtx.payload_type = sender_config.rtx_payload_type(); | |
330 } else { | |
331 // Reset RTX payload type default value if no RTX SSRCs are used. | |
332 config->rtp.rtx.payload_type = -1; | |
333 } | |
334 // Get encoder. | |
335 RTC_CHECK(sender_config.has_encoder()); | |
336 RTC_CHECK(sender_config.encoder().has_name()); | |
337 RTC_CHECK(sender_config.encoder().has_payload_type()); | |
338 config->encoder_settings.payload_name = sender_config.encoder().name(); | |
339 config->encoder_settings.payload_type = | |
340 sender_config.encoder().payload_type(); | |
341 } | |
342 | |
343 void ParsedRtcEventLog::GetAudioPlayout(size_t i, uint32_t* ssrc) const { | |
344 RTC_CHECK_LT(i, GetNumberOfEvents()); | |
345 const rtclog::Event& event = stream_[i]; | |
346 RTC_CHECK(event.has_type()); | |
347 RTC_CHECK_EQ(event.type(), rtclog::Event::AUDIO_PLAYOUT_EVENT); | |
348 RTC_CHECK(event.has_audio_playout_event()); | |
349 const rtclog::AudioPlayoutEvent& loss_event = event.audio_playout_event(); | |
350 RTC_CHECK(loss_event.has_local_ssrc()); | |
351 if (ssrc != nullptr) | |
352 *ssrc = loss_event.local_ssrc(); | |
353 } | |
354 | |
355 void ParsedRtcEventLog::GetBwePacketLossEvent(size_t i, | |
356 int32_t* bitrate, | |
357 uint8_t* fraction_loss, | |
358 int32_t* total_packets) const { | |
359 RTC_CHECK_LT(i, GetNumberOfEvents()); | |
360 const rtclog::Event& event = stream_[i]; | |
361 RTC_CHECK(event.has_type()); | |
362 RTC_CHECK_EQ(event.type(), rtclog::Event::BWE_PACKET_LOSS_EVENT); | |
363 RTC_CHECK(event.has_bwe_packet_loss_event()); | |
364 const rtclog::BwePacketLossEvent& loss_event = event.bwe_packet_loss_event(); | |
365 RTC_CHECK(loss_event.has_bitrate()); | |
366 if (bitrate != nullptr) | |
367 *bitrate = loss_event.bitrate(); | |
368 RTC_CHECK(loss_event.has_fraction_loss()); | |
369 if (fraction_loss != nullptr) | |
370 *fraction_loss = loss_event.fraction_loss(); | |
371 RTC_CHECK(loss_event.has_total_packets()); | |
372 if (total_packets != nullptr) | |
373 *total_packets = loss_event.total_packets(); | |
374 } | |
375 | |
376 } // namespace webrtc | |
OLD | NEW |