1 /*
2  *  Copyright (c) 2017 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 <stdio.h>
12 
13 #include <algorithm>
14 #include <fstream>
15 #include <iostream>
16 #include <vector>
17 
18 #include "api/array_view.h"
19 #include "modules/rtp_rtcp/source/byte_io.h"
20 #include "rtc_base/buffer.h"
21 
22 namespace webrtc {
23 namespace test {
24 namespace {
25 
26 constexpr size_t kRtpDumpHeaderLength = 8;
27 
28 // Returns the next packet or an empty buffer if end of file was encountered.
ReadNextPacket(FILE * file)29 rtc::Buffer ReadNextPacket(FILE* file) {
30   // Read the rtpdump header for the next packet.
31   rtc::Buffer buffer;
32   buffer.SetData(kRtpDumpHeaderLength, [&](rtc::ArrayView<uint8_t> x) {
33     return fread(x.data(), 1, x.size(), file);
34   });
35   if (buffer.size() != kRtpDumpHeaderLength) {
36     return rtc::Buffer();
37   }
38 
39   // Get length field. This is the total length for this packet written to file,
40   // including the kRtpDumpHeaderLength bytes already read.
41   const uint16_t len = ByteReader<uint16_t>::ReadBigEndian(buffer.data());
42   RTC_CHECK_GE(len, kRtpDumpHeaderLength);
43 
44   // Read remaining data from file directly into buffer.
45   buffer.AppendData(len - kRtpDumpHeaderLength, [&](rtc::ArrayView<uint8_t> x) {
46     return fread(x.data(), 1, x.size(), file);
47   });
48   if (buffer.size() != len) {
49     buffer.Clear();
50   }
51   return buffer;
52 }
53 
54 struct PacketAndTime {
55   rtc::Buffer packet;
56   int time;
57 };
58 
WritePacket(const PacketAndTime & packet,FILE * file)59 void WritePacket(const PacketAndTime& packet, FILE* file) {
60   // Write the first 4 bytes from the original packet.
61   const auto* payload_ptr = packet.packet.data();
62   RTC_CHECK_EQ(fwrite(payload_ptr, 4, 1, file), 1);
63   payload_ptr += 4;
64 
65   // Convert the new time offset to network endian, and write to file.
66   uint8_t time[sizeof(uint32_t)];
67   ByteWriter<uint32_t, sizeof(uint32_t)>::WriteBigEndian(time, packet.time);
68   RTC_CHECK_EQ(fwrite(time, sizeof(uint32_t), 1, file), 1);
69   payload_ptr += 4;  // Skip the old time in the original payload.
70 
71   // Write the remaining part of the payload.
72   RTC_DCHECK_EQ(payload_ptr - packet.packet.data(), kRtpDumpHeaderLength);
73   RTC_CHECK_EQ(
74       fwrite(payload_ptr, packet.packet.size() - kRtpDumpHeaderLength, 1, file),
75       1);
76 }
77 
RunRtpJitter(int argc,char * argv[])78 int RunRtpJitter(int argc, char* argv[]) {
79   const std::string program_name = argv[0];
80   const std::string usage =
81       "Tool for alternating the arrival times in an RTP dump file.\n"
82       "Example usage:\n" +
83       program_name + " input.rtp arrival_times_ms.txt output.rtp\n\n";
84   if (argc != 4) {
85     printf("%s", usage.c_str());
86     return 1;
87   }
88 
89   printf("Input RTP file: %s\n", argv[1]);
90   FILE* in_file = fopen(argv[1], "rb");
91   RTC_CHECK(in_file) << "Could not open file " << argv[1] << " for reading";
92   printf("Timing file: %s\n", argv[2]);
93   std::ifstream timing_file(argv[2]);
94   printf("Output file: %s\n", argv[3]);
95   FILE* out_file = fopen(argv[3], "wb");
96   RTC_CHECK(out_file) << "Could not open file " << argv[2] << " for writing";
97 
98   // Copy the RTP file header to the output file.
99   char header_string[30];
100   RTC_CHECK(fgets(header_string, 30, in_file));
101   fprintf(out_file, "%s", header_string);
102   uint8_t file_header[16];
103   RTC_CHECK_EQ(fread(file_header, sizeof(file_header), 1, in_file), 1);
104   RTC_CHECK_EQ(fwrite(file_header, sizeof(file_header), 1, out_file), 1);
105 
106   // Read all time values from the timing file. Store in a vector.
107   std::vector<int> new_arrival_times;
108   int new_time;
109   while (timing_file >> new_time) {
110     new_arrival_times.push_back(new_time);
111   }
112 
113   // Read all packets from the input RTP file, but no more than the number of
114   // new time values. Store RTP packets together with new time values.
115   auto time_it = new_arrival_times.begin();
116   std::vector<PacketAndTime> packets;
117   while (1) {
118     auto packet = ReadNextPacket(in_file);
119     if (packet.empty() || time_it == new_arrival_times.end()) {
120       break;
121     }
122     packets.push_back({std::move(packet), *time_it});
123     ++time_it;
124   }
125 
126   // Sort on new time values.
127   std::sort(packets.begin(), packets.end(),
128             [](const PacketAndTime& a, const PacketAndTime& b) {
129               return a.time < b.time;
130             });
131 
132   // Write packets to output file.
133   for (const auto& p : packets) {
134     WritePacket(p, out_file);
135   }
136 
137   fclose(in_file);
138   fclose(out_file);
139   return 0;
140 }
141 
142 }  // namespace
143 }  // namespace test
144 }  // namespace webrtc
145 
main(int argc,char * argv[])146 int main(int argc, char* argv[]) {
147   return webrtc::test::RunRtpJitter(argc, argv);
148 }
149