mirror of
https://github.com/mollyim/webrtc.git
synced 2025-05-14 14:20:45 +01:00

This new tool provides the similar functionality as the legacy tool, but is implemented using less legacy helpers. It also replaces RTPtimeshift and RTPchange. The most significant change versus the old RTPjitter tool is that the new tool takes the timing data in the form of integers in a text file (instead of the binary data file used by the old tool). This should make it easier to create custom timing files when needed. Bug: webrtc:2692 Change-Id: I5e46fe7abdd9ca8c04a04de87555204fca36e287 Reviewed-on: https://webrtc-review.googlesource.com/25700 Commit-Queue: Henrik Lundin <henrik.lundin@webrtc.org> Reviewed-by: Karl Wiberg <kwiberg@webrtc.org> Cr-Commit-Position: refs/heads/master@{#20868}
152 lines
4.8 KiB
C++
152 lines
4.8 KiB
C++
/*
|
|
* 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"
|
|
#include "rtc_base/flags.h"
|
|
#include "typedefs.h" // NOLINT(build/include)
|
|
|
|
namespace webrtc {
|
|
namespace test {
|
|
namespace {
|
|
|
|
DEFINE_bool(help, false, "Print help message");
|
|
|
|
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 (rtc::FlagList::SetFlagsFromCommandLine(&argc, argv, true) || FLAG_help ||
|
|
argc != 4) {
|
|
printf("%s", usage.c_str());
|
|
return FLAG_help ? 0 : 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);
|
|
}
|