/* * 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 #include #include #include #include #include #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 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 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 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 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 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(), packet->size(), packet->PayloadType(), packet->Marker(), packet->Ssrc()); webrtc::AudioLevel audio_level; if (print_audio_level && packet->GetExtension(&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(&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((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(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(), red.payload_type); } } } fclose(out_file); return 0; }