rtc_event_log2rtp_dump.cc (9296B)
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 <cstdint> 12 #include <cstring> 13 #include <iostream> 14 #include <memory> 15 #include <optional> 16 #include <string> 17 #include <vector> 18 19 #include "absl/flags/flag.h" 20 #include "absl/flags/parse.h" 21 #include "absl/flags/usage.h" 22 #include "absl/strings/string_view.h" 23 #include "api/array_view.h" 24 #include "api/rtp_headers.h" 25 #include "logging/rtc_event_log/events/logged_rtp_rtcp.h" 26 #include "logging/rtc_event_log/rtc_event_log_parser.h" 27 #include "logging/rtc_event_log/rtc_event_processor.h" 28 #include "modules/rtp_rtcp/include/rtp_header_extension_map.h" 29 #include "modules/rtp_rtcp/source/rtp_header_extensions.h" 30 #include "modules/rtp_rtcp/source/rtp_packet.h" 31 #include "rtc_base/checks.h" 32 #include "rtc_base/string_to_number.h" 33 #include "test/rtp_file_reader.h" 34 #include "test/rtp_file_writer.h" 35 36 ABSL_FLAG( 37 bool, 38 audio, 39 true, 40 "Use --noaudio to exclude audio packets from the converted RTPdump file."); 41 ABSL_FLAG( 42 bool, 43 video, 44 true, 45 "Use --novideo to exclude video packets from the converted RTPdump file."); 46 ABSL_FLAG( 47 bool, 48 data, 49 true, 50 "Use --nodata to exclude data packets from the converted RTPdump file."); 51 ABSL_FLAG( 52 bool, 53 rtp, 54 true, 55 "Use --nortp to exclude RTP packets from the converted RTPdump file."); 56 ABSL_FLAG( 57 bool, 58 rtcp, 59 true, 60 "Use --nortcp to exclude RTCP packets from the converted RTPdump file."); 61 ABSL_FLAG(std::string, 62 ssrc, 63 "", 64 "Store only packets with this SSRC (decimal or hex, the latter " 65 "starting with 0x)."); 66 67 namespace { 68 69 using MediaType = webrtc::ParsedRtcEventLog::MediaType; 70 71 // Parses the input string for a valid SSRC. If a valid SSRC is found, it is 72 // written to the output variable `ssrc`, and true is returned. Otherwise, 73 // false is returned. 74 // The empty string must be validated as true, because it is the default value 75 // of the command-line flag. In this case, no value is written to the output 76 // variable. 77 std::optional<uint32_t> ParseSsrc(absl::string_view str) { 78 // Set `base` to 0 to allow detection of the "0x" prefix in case hex is used. 79 return webrtc::StringToNumber<uint32_t>(str, 0); 80 } 81 82 bool ShouldSkipStream(MediaType media_type, 83 uint32_t ssrc, 84 std::optional<uint32_t> ssrc_filter) { 85 if (!absl::GetFlag(FLAGS_audio) && media_type == MediaType::AUDIO) 86 return true; 87 if (!absl::GetFlag(FLAGS_video) && media_type == MediaType::VIDEO) 88 return true; 89 if (!absl::GetFlag(FLAGS_data) && media_type == MediaType::DATA) 90 return true; 91 if (ssrc_filter.has_value() && ssrc != *ssrc_filter) 92 return true; 93 return false; 94 } 95 96 // Convert a LoggedRtpPacketIncoming to a test::RtpPacket. Header extension IDs 97 // are allocated according to the provided extension map. This might not match 98 // the extension map used in the actual call. 99 void ConvertRtpPacket( 100 const webrtc::LoggedRtpPacketIncoming& incoming, 101 const webrtc::RtpHeaderExtensionMap& default_extension_map, 102 webrtc::test::RtpPacket* packet) { 103 webrtc::RtpPacket reconstructed_packet(&default_extension_map); 104 105 reconstructed_packet.SetMarker(incoming.rtp.header.markerBit); 106 reconstructed_packet.SetPayloadType(incoming.rtp.header.payloadType); 107 reconstructed_packet.SetSequenceNumber(incoming.rtp.header.sequenceNumber); 108 reconstructed_packet.SetTimestamp(incoming.rtp.header.timestamp); 109 reconstructed_packet.SetSsrc(incoming.rtp.header.ssrc); 110 if (incoming.rtp.header.numCSRCs > 0) { 111 reconstructed_packet.SetCsrcs(webrtc::ArrayView<const uint32_t>( 112 incoming.rtp.header.arrOfCSRCs, incoming.rtp.header.numCSRCs)); 113 } 114 115 // Set extensions. 116 if (incoming.rtp.header.extension.hasTransmissionTimeOffset) 117 reconstructed_packet.SetExtension<webrtc::TransmissionOffset>( 118 incoming.rtp.header.extension.transmissionTimeOffset); 119 if (incoming.rtp.header.extension.hasAbsoluteSendTime) 120 reconstructed_packet.SetExtension<webrtc::AbsoluteSendTime>( 121 incoming.rtp.header.extension.absoluteSendTime); 122 if (incoming.rtp.header.extension.hasTransportSequenceNumber) 123 reconstructed_packet.SetExtension<webrtc::TransportSequenceNumber>( 124 incoming.rtp.header.extension.transportSequenceNumber); 125 if (incoming.rtp.header.extension.audio_level()) 126 reconstructed_packet.SetExtension<webrtc::AudioLevelExtension>( 127 *incoming.rtp.header.extension.audio_level()); 128 if (incoming.rtp.header.extension.hasVideoRotation) 129 reconstructed_packet.SetExtension<webrtc::VideoOrientation>( 130 incoming.rtp.header.extension.videoRotation); 131 if (incoming.rtp.header.extension.hasVideoContentType) 132 reconstructed_packet.SetExtension<webrtc::VideoContentTypeExtension>( 133 incoming.rtp.header.extension.videoContentType); 134 if (incoming.rtp.header.extension.has_video_timing) 135 reconstructed_packet.SetExtension<webrtc::VideoTimingExtension>( 136 incoming.rtp.header.extension.video_timing); 137 138 RTC_DCHECK_EQ(reconstructed_packet.size(), incoming.rtp.header_length); 139 RTC_DCHECK_EQ(reconstructed_packet.headers_size(), 140 incoming.rtp.header_length); 141 memcpy(packet->data, reconstructed_packet.data(), 142 reconstructed_packet.headers_size()); 143 packet->length = reconstructed_packet.headers_size(); 144 packet->original_length = incoming.rtp.total_length; 145 packet->time_ms = incoming.log_time_ms(); 146 // Set padding bit. 147 if (incoming.rtp.header.paddingLength > 0) 148 packet->data[0] = packet->data[0] | 0x20; 149 } 150 151 } // namespace 152 153 // This utility will convert a stored event log to the rtpdump format. 154 int main(int argc, char* argv[]) { 155 absl::SetProgramUsageMessage( 156 "Tool for converting an RtcEventLog file to an " 157 "RTP dump file.\n" 158 "Example usage:\n" 159 "./rtc_event_log2rtp_dump input.rel output.rtp\n"); 160 std::vector<char*> args = absl::ParseCommandLine(argc, argv); 161 if (args.size() != 3) { 162 std::cout << absl::ProgramUsageMessage(); 163 return 1; 164 } 165 166 std::string input_file = args[1]; 167 std::string output_file = args[2]; 168 169 std::optional<uint32_t> ssrc_filter; 170 if (!absl::GetFlag(FLAGS_ssrc).empty()) { 171 ssrc_filter = ParseSsrc(absl::GetFlag(FLAGS_ssrc)); 172 RTC_CHECK(ssrc_filter.has_value()) << "Failed to read SSRC filter flag."; 173 } 174 175 webrtc::ParsedRtcEventLog parsed_stream; 176 auto status = parsed_stream.ParseFile(input_file); 177 if (!status.ok()) { 178 std::cerr << "Failed to parse event log " << input_file << ": " 179 << status.message() << std::endl; 180 return -1; 181 } 182 183 std::unique_ptr<webrtc::test::RtpFileWriter> rtp_writer( 184 webrtc::test::RtpFileWriter::Create( 185 webrtc::test::RtpFileWriter::FileFormat::kRtpDump, output_file)); 186 187 if (!rtp_writer) { 188 std::cerr << "Error while opening output file: " << output_file 189 << std::endl; 190 return -1; 191 } 192 193 int rtp_counter = 0, rtcp_counter = 0; 194 bool header_only = false; 195 196 webrtc::RtpHeaderExtensionMap default_extension_map = 197 webrtc::ParsedRtcEventLog::GetDefaultHeaderExtensionMap(); 198 auto handle_rtp = [&default_extension_map, &rtp_writer, &rtp_counter]( 199 const webrtc::LoggedRtpPacketIncoming& incoming) { 200 webrtc::test::RtpPacket packet; 201 ConvertRtpPacket(incoming, default_extension_map, &packet); 202 203 rtp_writer->WritePacket(&packet); 204 rtp_counter++; 205 }; 206 207 auto handle_rtcp = [&rtp_writer, &rtcp_counter]( 208 const webrtc::LoggedRtcpPacketIncoming& incoming) { 209 webrtc::test::RtpPacket packet; 210 memcpy(packet.data, incoming.rtcp.raw_data.data(), 211 incoming.rtcp.raw_data.size()); 212 packet.length = incoming.rtcp.raw_data.size(); 213 // For RTCP packets the original_length should be set to 0 in the 214 // RTPdump format. 215 packet.original_length = 0; 216 packet.time_ms = incoming.log_time_ms(); 217 218 rtp_writer->WritePacket(&packet); 219 rtcp_counter++; 220 }; 221 222 webrtc::RtcEventProcessor event_processor; 223 for (const auto& stream : parsed_stream.incoming_rtp_packets_by_ssrc()) { 224 MediaType media_type = 225 parsed_stream.GetMediaType(stream.ssrc, webrtc::kIncomingPacket); 226 if (ShouldSkipStream(media_type, stream.ssrc, ssrc_filter)) 227 continue; 228 event_processor.AddEvents(stream.incoming_packets, handle_rtp); 229 } 230 // Note that `packet_ssrc` is the sender SSRC. An RTCP message may contain 231 // report blocks for many streams, thus several SSRCs and they don't 232 // necessarily have to be of the same media type. We therefore don't 233 // support filtering of RTCP based on SSRC and media type. 234 event_processor.AddEvents(parsed_stream.incoming_rtcp_packets(), handle_rtcp); 235 236 event_processor.ProcessEventsInOrder(); 237 238 std::cout << "Wrote " << rtp_counter << (header_only ? " header-only" : "") 239 << " RTP packets and " << rtcp_counter 240 << " RTCP packets to the " 241 "output file." 242 << std::endl; 243 return 0; 244 }