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