summaryrefslogtreecommitdiffstats
path: root/third_party/libwebrtc/modules/audio_coding/neteq/tools/rtp_jitter.cc
blob: cccaa9a3bbe852312f86ee79e65e78b2e91b06e8 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
/*
 *  Copyright (c) 2017 The WebRTC project authors. All Rights Reserved.
 *
 *  Use of this source code is governed by a BSD-style license
 *  that can be found in the LICENSE file in the root of the source
 *  tree. An additional intellectual property rights grant can be found
 *  in the file PATENTS.  All contributing project authors may
 *  be found in the AUTHORS file in the root of the source tree.
 */

#include <stdio.h>

#include <algorithm>
#include <fstream>
#include <iostream>
#include <vector>

#include "api/array_view.h"
#include "modules/rtp_rtcp/source/byte_io.h"
#include "rtc_base/buffer.h"

namespace webrtc {
namespace test {
namespace {

constexpr size_t kRtpDumpHeaderLength = 8;

// Returns the next packet or an empty buffer if end of file was encountered.
rtc::Buffer ReadNextPacket(FILE* file) {
  // Read the rtpdump header for the next packet.
  rtc::Buffer buffer;
  buffer.SetData(kRtpDumpHeaderLength, [&](rtc::ArrayView<uint8_t> x) {
    return fread(x.data(), 1, x.size(), file);
  });
  if (buffer.size() != kRtpDumpHeaderLength) {
    return rtc::Buffer();
  }

  // Get length field. This is the total length for this packet written to file,
  // including the kRtpDumpHeaderLength bytes already read.
  const uint16_t len = ByteReader<uint16_t>::ReadBigEndian(buffer.data());
  RTC_CHECK_GE(len, kRtpDumpHeaderLength);

  // Read remaining data from file directly into buffer.
  buffer.AppendData(len - kRtpDumpHeaderLength, [&](rtc::ArrayView<uint8_t> x) {
    return fread(x.data(), 1, x.size(), file);
  });
  if (buffer.size() != len) {
    buffer.Clear();
  }
  return buffer;
}

struct PacketAndTime {
  rtc::Buffer packet;
  int time;
};

void WritePacket(const PacketAndTime& packet, FILE* file) {
  // Write the first 4 bytes from the original packet.
  const auto* payload_ptr = packet.packet.data();
  RTC_CHECK_EQ(fwrite(payload_ptr, 4, 1, file), 1);
  payload_ptr += 4;

  // Convert the new time offset to network endian, and write to file.
  uint8_t time[sizeof(uint32_t)];
  ByteWriter<uint32_t, sizeof(uint32_t)>::WriteBigEndian(time, packet.time);
  RTC_CHECK_EQ(fwrite(time, sizeof(uint32_t), 1, file), 1);
  payload_ptr += 4;  // Skip the old time in the original payload.

  // Write the remaining part of the payload.
  RTC_DCHECK_EQ(payload_ptr - packet.packet.data(), kRtpDumpHeaderLength);
  RTC_CHECK_EQ(
      fwrite(payload_ptr, packet.packet.size() - kRtpDumpHeaderLength, 1, file),
      1);
}

int RunRtpJitter(int argc, char* argv[]) {
  const std::string program_name = argv[0];
  const std::string usage =
      "Tool for alternating the arrival times in an RTP dump file.\n"
      "Example usage:\n" +
      program_name + " input.rtp arrival_times_ms.txt output.rtp\n\n";
  if (argc != 4) {
    printf("%s", usage.c_str());
    return 1;
  }

  printf("Input RTP file: %s\n", argv[1]);
  FILE* in_file = fopen(argv[1], "rb");
  RTC_CHECK(in_file) << "Could not open file " << argv[1] << " for reading";
  printf("Timing file: %s\n", argv[2]);
  std::ifstream timing_file(argv[2]);
  printf("Output file: %s\n", argv[3]);
  FILE* out_file = fopen(argv[3], "wb");
  RTC_CHECK(out_file) << "Could not open file " << argv[2] << " for writing";

  // Copy the RTP file header to the output file.
  char header_string[30];
  RTC_CHECK(fgets(header_string, 30, in_file));
  fprintf(out_file, "%s", header_string);
  uint8_t file_header[16];
  RTC_CHECK_EQ(fread(file_header, sizeof(file_header), 1, in_file), 1);
  RTC_CHECK_EQ(fwrite(file_header, sizeof(file_header), 1, out_file), 1);

  // Read all time values from the timing file. Store in a vector.
  std::vector<int> new_arrival_times;
  int new_time;
  while (timing_file >> new_time) {
    new_arrival_times.push_back(new_time);
  }

  // Read all packets from the input RTP file, but no more than the number of
  // new time values. Store RTP packets together with new time values.
  auto time_it = new_arrival_times.begin();
  std::vector<PacketAndTime> packets;
  while (1) {
    auto packet = ReadNextPacket(in_file);
    if (packet.empty() || time_it == new_arrival_times.end()) {
      break;
    }
    packets.push_back({std::move(packet), *time_it});
    ++time_it;
  }

  // Sort on new time values.
  std::sort(packets.begin(), packets.end(),
            [](const PacketAndTime& a, const PacketAndTime& b) {
              return a.time < b.time;
            });

  // Write packets to output file.
  for (const auto& p : packets) {
    WritePacket(p, out_file);
  }

  fclose(in_file);
  fclose(out_file);
  return 0;
}

}  // namespace
}  // namespace test
}  // namespace webrtc

int main(int argc, char* argv[]) {
  return webrtc::test::RunRtpJitter(argc, argv);
}