blob: 26df8f00a84d526667b81520753da64fc71bcc96 [file] [log] [blame]
/*
* Copyright (c) 2012 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 <algorithm>
#include <cstdint>
#include <cstdio>
#include <memory>
#include <string>
#include <vector>
#include "absl/flags/flag.h"
#include "absl/flags/parse.h"
#include "api/rtp_headers.h"
#include "modules/audio_coding/neteq/tools/rtp_file_source.h"
#include "modules/rtp_rtcp/include/rtp_rtcp_defines.h"
#include "modules/rtp_rtcp/source/rtp_header_extensions.h"
#include "modules/rtp_rtcp/source/rtp_packet_received.h"
#include "rtc_base/checks.h"
ABSL_FLAG(int, red, 117, "RTP payload type for RED");
ABSL_FLAG(int,
audio_level,
-1,
"Extension ID for audio level (RFC 6464); "
"-1 not to print audio level");
ABSL_FLAG(int,
abs_send_time,
-1,
"Extension ID for absolute sender time; "
"-1 not to print absolute send time");
namespace {
struct RedHeader {
uint32_t rtp_timestamp;
int payload_type;
};
std::vector<RedHeader> ExtractRedHeaders(const webrtc::RtpPacket& packet) {
//
// 0 1 2 3
// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// |1| block PT | timestamp offset | block length |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// |1| ... |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// |0| block PT |
// +-+-+-+-+-+-+-+-+
//
const uint8_t* payload_ptr = packet.payload().data();
const uint8_t* payload_end_ptr =
packet.payload().data() + packet.payload().size();
// Find all RED headers with the extension bit set to 1. That is, all headers
// but the last one.
std::vector<RedHeader> red_headers;
while ((payload_ptr < payload_end_ptr) && (*payload_ptr & 0x80)) {
RedHeader header;
header.payload_type = payload_ptr[0] & 0x7F;
uint32_t offset = (payload_ptr[1] << 6) + ((payload_ptr[2] & 0xFC) >> 2);
header.rtp_timestamp = packet.Timestamp() - offset;
red_headers.push_back(header);
payload_ptr += 4;
}
// Last header.
RTC_DCHECK_LT(payload_ptr, payload_end_ptr);
if (payload_ptr >= payload_end_ptr) {
return {}; // Payload too short.
}
RedHeader header;
header.payload_type = payload_ptr[0] & 0x7F;
header.rtp_timestamp = packet.Timestamp();
red_headers.push_back(header);
std::reverse(red_headers.begin(), red_headers.end());
return red_headers;
}
} // namespace
int main(int argc, char* argv[]) {
std::vector<char*> args = absl::ParseCommandLine(argc, argv);
std::string usage =
"Tool for parsing an RTP dump file to text output.\n"
"Example usage:\n"
"./rtp_analyze input.rtp output.txt\n\n"
"Output is sent to stdout if no output file is given. "
"Note that this tool can read files with or without payloads.\n";
if (args.size() != 2 && args.size() != 3) {
printf("%s", usage.c_str());
return 1;
}
RTC_CHECK(absl::GetFlag(FLAGS_red) >= 0 &&
absl::GetFlag(FLAGS_red) <= 127); // Payload type
RTC_CHECK(absl::GetFlag(FLAGS_audio_level) == -1 || // Default
(absl::GetFlag(FLAGS_audio_level) > 0 &&
absl::GetFlag(FLAGS_audio_level) <= 255)); // Extension ID
RTC_CHECK(absl::GetFlag(FLAGS_abs_send_time) == -1 || // Default
(absl::GetFlag(FLAGS_abs_send_time) > 0 &&
absl::GetFlag(FLAGS_abs_send_time) <= 255)); // Extension ID
printf("Input file: %s\n", args[1]);
std::unique_ptr<webrtc::test::RtpFileSource> file_source(
webrtc::test::RtpFileSource::Create(args[1]));
RTC_DCHECK(file_source.get());
// Set RTP extension IDs.
bool print_audio_level = false;
if (absl::GetFlag(FLAGS_audio_level) != -1) {
print_audio_level = true;
file_source->RegisterRtpHeaderExtension(webrtc::kRtpExtensionAudioLevel,
absl::GetFlag(FLAGS_audio_level));
}
bool print_abs_send_time = false;
if (absl::GetFlag(FLAGS_abs_send_time) != -1) {
print_abs_send_time = true;
file_source->RegisterRtpHeaderExtension(
webrtc::kRtpExtensionAbsoluteSendTime,
absl::GetFlag(FLAGS_abs_send_time));
}
FILE* out_file;
if (args.size() == 3) {
out_file = fopen(args[2], "wt");
if (!out_file) {
printf("Cannot open output file %s\n", args[2]);
return -1;
}
printf("Output file: %s\n\n", args[2]);
} else {
out_file = stdout;
}
// Print file header.
fprintf(out_file, "SeqNo TimeStamp SendTime Size PT M SSRC");
if (print_audio_level) {
fprintf(out_file, " AuLvl (V)");
}
if (print_abs_send_time) {
fprintf(out_file, " AbsSendTime");
}
fprintf(out_file, "\n");
uint32_t max_abs_send_time = 0;
int cycles = -1;
std::unique_ptr<webrtc::RtpPacketReceived> packet;
while (true) {
packet = file_source->NextPacket();
if (!packet) {
// End of file reached.
break;
}
// Write packet data to file. Use virtual_packet_length_bytes so that the
// correct packet sizes are printed also for RTP header-only dumps.
fprintf(out_file, "%5u %10u %10i %5zu %5i %2i %#08X",
packet->SequenceNumber(), packet->Timestamp(),
packet->arrival_time().ms<int>(), packet->size(),
packet->PayloadType(), packet->Marker(), packet->Ssrc());
webrtc::AudioLevel audio_level;
if (print_audio_level &&
packet->GetExtension<webrtc::AudioLevelExtension>(&audio_level)) {
fprintf(out_file, " %5d (%1i)", audio_level.level(),
audio_level.voice_activity());
}
uint32_t abs_sent_time;
if (print_abs_send_time &&
packet->GetExtension<webrtc::AbsoluteSendTime>(&abs_sent_time)) {
if (cycles == -1) {
// Initialize.
max_abs_send_time = abs_sent_time;
cycles = 0;
}
// Abs sender time is 24 bit 6.18 fixed point. Shift by 8 to normalize to
// 32 bits (unsigned). Calculate the difference between this packet's
// send time and the maximum observed. Cast to signed 32-bit to get the
// desired wrap-around behavior.
if (static_cast<int32_t>((abs_sent_time << 8) -
(max_abs_send_time << 8)) >= 0) {
// The difference is non-negative, meaning that this packet is newer
// than the previously observed maximum absolute send time.
if (abs_sent_time < max_abs_send_time) {
// Wrap detected.
cycles++;
}
max_abs_send_time = abs_sent_time;
}
// Abs sender time is 24 bit 6.18 fixed point. Divide by 2^18 to convert
// to floating point representation.
double send_time_seconds =
static_cast<double>(abs_sent_time) / 262144 + 64.0 * cycles;
fprintf(out_file, " %11f", send_time_seconds);
}
fprintf(out_file, "\n");
if (packet->PayloadType() == absl::GetFlag(FLAGS_red)) {
for (const RedHeader& red : ExtractRedHeaders(*packet)) {
fprintf(out_file, "* %5u %10u %10i %5i\n", packet->SequenceNumber(),
red.rtp_timestamp, packet->arrival_time().ms<int>(),
red.payload_type);
}
}
}
fclose(out_file);
return 0;
}