xref: /aosp_15_r20/external/webrtc/logging/rtc_event_log/rtc_event_log2rtp_dump.cc (revision d9f758449e529ab9291ac668be2861e7a55c2422)
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 <stdint.h>
12 #include <string.h>
13 
14 #include <iostream>
15 #include <memory>
16 #include <string>
17 #include <utility>
18 #include <vector>
19 
20 #include "absl/flags/flag.h"
21 #include "absl/flags/parse.h"
22 #include "absl/flags/usage.h"
23 #include "absl/memory/memory.h"
24 #include "absl/strings/string_view.h"
25 #include "absl/types/optional.h"
26 #include "api/array_view.h"
27 #include "api/rtc_event_log/rtc_event_log.h"
28 #include "api/rtp_headers.h"
29 #include "logging/rtc_event_log/rtc_event_log_parser.h"
30 #include "logging/rtc_event_log/rtc_event_processor.h"
31 #include "modules/rtp_rtcp/include/rtp_header_extension_map.h"
32 #include "modules/rtp_rtcp/source/rtp_header_extensions.h"
33 #include "modules/rtp_rtcp/source/rtp_packet.h"
34 #include "rtc_base/checks.h"
35 #include "test/rtp_file_reader.h"
36 #include "test/rtp_file_writer.h"
37 
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 
69 namespace {
70 
71 using MediaType = webrtc::ParsedRtcEventLog::MediaType;
72 
73 // Parses the input string for a valid SSRC. If a valid SSRC is found, it is
74 // written to the output variable `ssrc`, and true is returned. Otherwise,
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.
ParseSsrc(absl::string_view str)79 absl::optional<uint32_t> ParseSsrc(absl::string_view str) {
80   // If the input string starts with 0x or 0X it indicates a hexadecimal number.
81   uint32_t ssrc;
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   }
88   std::stringstream ss(std::string{str});
89   ss >> read_mode >> ssrc;
90   if (str.empty() || (!ss.fail() && ss.eof()))
91     return ssrc;
92   return absl::nullopt;
93 }
94 
ShouldSkipStream(MediaType media_type,uint32_t ssrc,absl::optional<uint32_t> ssrc_filter)95 bool ShouldSkipStream(MediaType media_type,
96                       uint32_t ssrc,
97                       absl::optional<uint32_t> ssrc_filter) {
98   if (!absl::GetFlag(FLAGS_audio) && media_type == MediaType::AUDIO)
99     return true;
100   if (!absl::GetFlag(FLAGS_video) && media_type == MediaType::VIDEO)
101     return true;
102   if (!absl::GetFlag(FLAGS_data) && media_type == MediaType::DATA)
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.
ConvertRtpPacket(const webrtc::LoggedRtpPacketIncoming & incoming,const webrtc::RtpHeaderExtensionMap & default_extension_map,webrtc::test::RtpPacket * packet)112 void ConvertRtpPacket(
113     const webrtc::LoggedRtpPacketIncoming& incoming,
114     const webrtc::RtpHeaderExtensionMap& default_extension_map,
115     webrtc::test::RtpPacket* packet) {
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)
139     reconstructed_packet.SetExtension<webrtc::AudioLevel>(
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;
163 }
164 
165 }  // namespace
166 
167 // This utility will convert a stored event log to the rtpdump format.
main(int argc,char * argv[])168 int main(int argc, char* argv[]) {
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");
174   std::vector<char*> args = absl::ParseCommandLine(argc, argv);
175   if (args.size() != 3) {
176     std::cout << absl::ProgramUsageMessage();
177     return 1;
178   }
179 
180   std::string input_file = args[1];
181   std::string output_file = args[2];
182 
183   absl::optional<uint32_t> ssrc_filter;
184   if (!absl::GetFlag(FLAGS_ssrc).empty()) {
185     ssrc_filter = ParseSsrc(absl::GetFlag(FLAGS_ssrc));
186     RTC_CHECK(ssrc_filter.has_value()) << "Failed to read SSRC filter flag.";
187   }
188 
189   webrtc::ParsedRtcEventLog parsed_stream;
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;
194     return -1;
195   }
196 
197   std::unique_ptr<webrtc::test::RtpFileWriter> rtp_writer(
198       webrtc::test::RtpFileWriter::Create(
199           webrtc::test::RtpFileWriter::FileFormat::kRtpDump, output_file));
200 
201   if (!rtp_writer) {
202     std::cerr << "Error while opening output file: " << output_file
203               << std::endl;
204     return -1;
205   }
206 
207   int rtp_counter = 0, rtcp_counter = 0;
208   bool header_only = false;
209 
210   webrtc::RtpHeaderExtensionMap default_extension_map =
211       webrtc::ParsedRtcEventLog::GetDefaultHeaderExtensionMap();
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);
216 
217     rtp_writer->WritePacket(&packet);
218     rtp_counter++;
219   };
220 
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();
231 
232     rtp_writer->WritePacket(&packet);
233     rtcp_counter++;
234   };
235 
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;
242     event_processor.AddEvents(stream.incoming_packets, handle_rtp);
243   }
244   // Note that `packet_ssrc` is the sender SSRC. An RTCP message may contain
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.
248   event_processor.AddEvents(parsed_stream.incoming_rtcp_packets(), handle_rtcp);
249 
250   event_processor.ProcessEventsInOrder();
251 
252   std::cout << "Wrote " << rtp_counter << (header_only ? " header-only" : "")
253             << " RTP packets and " << rtcp_counter
254             << " RTCP packets to the "
255                "output file."
256             << std::endl;
257   return 0;
258 }
259