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