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