From 35624c2c3686a2ad40daffe073aa78507b0ef88e Mon Sep 17 00:00:00 2001 From: Ivo Creusen Date: Fri, 18 Sep 2015 09:46:51 +0200 Subject: [PATCH] Tool to convert RtcEventLog files to RtpDump format. This is a small utility that reads RtcEventLog files, and converts the RTP headers within it to RtpDump format. All other types of events are ignored. Three command-line flags are supported, --audio-only, --video-only and --data-only. When one of these flags is supplied, only RTP packets that match the requested type are converted. BUG=webrtc:4741 R=henrik.lundin@webrtc.org, kjellander@webrtc.org, stefan@webrtc.org, terelius@webrtc.org Review URL: https://codereview.webrtc.org/1297653002 . Cr-Commit-Position: refs/heads/master@{#9980} --- webrtc/test/rtp_file_writer.cc | 1 - webrtc/video/rtc_event_log2rtp_dump.cc | 207 +++++++++++++++++++++++++ webrtc/webrtc.gyp | 11 ++ 3 files changed, 218 insertions(+), 1 deletion(-) create mode 100644 webrtc/video/rtc_event_log2rtp_dump.cc diff --git a/webrtc/test/rtp_file_writer.cc b/webrtc/test/rtp_file_writer.cc index 793e51a55e..d9e0586468 100644 --- a/webrtc/test/rtp_file_writer.cc +++ b/webrtc/test/rtp_file_writer.cc @@ -40,7 +40,6 @@ class RtpDumpWriter : public RtpFileWriter { bool WritePacket(const RtpPacket* packet) override { uint16_t len = static_cast(packet->length + kPacketHeaderSize); - RTC_CHECK_GE(packet->original_length, packet->length); uint16_t plen = static_cast(packet->original_length); uint32_t offset = packet->time_ms; RTC_CHECK(WriteUint16(len)); diff --git a/webrtc/video/rtc_event_log2rtp_dump.cc b/webrtc/video/rtc_event_log2rtp_dump.cc new file mode 100644 index 0000000000..4f1d93bbe4 --- /dev/null +++ b/webrtc/video/rtc_event_log2rtp_dump.cc @@ -0,0 +1,207 @@ +/* + * Copyright (c) 2015 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 "gflags/gflags.h" +#include "webrtc/base/checks.h" +#include "webrtc/base/scoped_ptr.h" +#include "webrtc/modules/rtp_rtcp/source/byte_io.h" +#include "webrtc/test/rtp_file_writer.h" +#include "webrtc/video/rtc_event_log.h" + +// Files generated at build-time by the protobuf compiler. +#ifdef WEBRTC_ANDROID_PLATFORM_BUILD +#include "external/webrtc/webrtc/video/rtc_event_log.pb.h" +#else +#include "webrtc/video/rtc_event_log.pb.h" +#endif + +namespace { + +DEFINE_bool(noaudio, + false, + "Excludes audio packets from the converted RTPdump file."); +DEFINE_bool(novideo, + false, + "Excludes video packets from the converted RTPdump file."); +DEFINE_bool(nodata, + false, + "Excludes data packets from the converted RTPdump file."); +DEFINE_bool(nortp, + false, + "Excludes RTP packets from the converted RTPdump file."); +DEFINE_bool(nortcp, + false, + "Excludes RTCP packets from the converted RTPdump file."); +DEFINE_string(ssrc, + "", + "Store only packets with this SSRC (decimal or hex, the latter " + "starting with 0x)."); + +// Parses the input string for a valid SSRC. If a valid SSRC is found, it is +// written to the output variable |ssrc|, and true is returned. Otherwise, +// false is returned. +// The empty string must be validated as true, because it is the default value +// of the command-line flag. In this case, no value is written to the output +// variable. +bool ParseSsrc(std::string str, uint32_t* ssrc) { + // If the input string starts with 0x or 0X it indicates a hexadecimal number. + auto read_mode = std::dec; + if (str.size() > 2 && + (str.substr(0, 2) == "0x" || str.substr(0, 2) == "0X")) { + read_mode = std::hex; + str = str.substr(2); + } + std::stringstream ss(str); + ss >> read_mode >> *ssrc; + return str.empty() || (!ss.fail() && ss.eof()); +} + +} // namespace + +// This utility will convert a stored event log to the rtpdump format. +int main(int argc, char* argv[]) { + std::string program_name = argv[0]; + std::string usage = + "Tool for converting an RtcEventLog file to an RTP dump file.\n" + "Run " + + program_name + + " --helpshort for usage.\n" + "Example usage:\n" + + program_name + " input.rel output.rtp\n"; + google::SetUsageMessage(usage); + google::ParseCommandLineFlags(&argc, &argv, true); + + if (argc != 3) { + std::cout << google::ProgramUsage(); + return 0; + } + std::string input_file = argv[1]; + std::string output_file = argv[2]; + + uint32_t ssrc_filter = 0; + if (!FLAGS_ssrc.empty()) + RTC_CHECK(ParseSsrc(FLAGS_ssrc, &ssrc_filter)) + << "Flag verification has failed."; + + webrtc::rtclog::EventStream event_stream; + if (!webrtc::RtcEventLog::ParseRtcEventLog(input_file, &event_stream)) { + std::cerr << "Error while parsing input file: " << input_file << std::endl; + return -1; + } + + rtc::scoped_ptr rtp_writer( + webrtc::test::RtpFileWriter::Create( + webrtc::test::RtpFileWriter::FileFormat::kRtpDump, output_file)); + + if (!rtp_writer.get()) { + std::cerr << "Error while opening output file: " << output_file + << std::endl; + return -1; + } + + std::cout << "Found " << event_stream.stream_size() + << " events in the input file." << std::endl; + int rtp_counter = 0, rtcp_counter = 0; + bool header_only = false; + // TODO(ivoc): This can be refactored once the packet interpretation + // functions are finished. + for (int i = 0; i < event_stream.stream_size(); i++) { + const webrtc::rtclog::Event& event = event_stream.stream(i); + if (!FLAGS_nortp && event.has_type() && event.type() == event.RTP_EVENT) { + if (event.has_timestamp_us() && event.has_rtp_packet() && + event.rtp_packet().has_header() && + event.rtp_packet().header().size() >= 12 && + event.rtp_packet().has_packet_length() && + event.rtp_packet().has_type()) { + const webrtc::rtclog::RtpPacket& rtp_packet = event.rtp_packet(); + if (FLAGS_noaudio && rtp_packet.type() == webrtc::rtclog::AUDIO) + continue; + if (FLAGS_novideo && rtp_packet.type() == webrtc::rtclog::VIDEO) + continue; + if (FLAGS_nodata && rtp_packet.type() == webrtc::rtclog::DATA) + continue; + if (!FLAGS_ssrc.empty()) { + const uint32_t packet_ssrc = + webrtc::ByteReader::ReadBigEndian( + reinterpret_cast(rtp_packet.header().data() + + 8)); + if (packet_ssrc != ssrc_filter) + continue; + } + + webrtc::test::RtpPacket packet; + packet.length = rtp_packet.header().size(); + if (packet.length > packet.kMaxPacketBufferSize) { + std::cout << "Skipping packet with size " << packet.length + << ", the maximum supported size is " + << packet.kMaxPacketBufferSize << std::endl; + continue; + } + packet.original_length = rtp_packet.packet_length(); + if (packet.original_length > packet.length) + header_only = true; + packet.time_ms = event.timestamp_us() / 1000; + memcpy(packet.data, rtp_packet.header().data(), packet.length); + rtp_writer->WritePacket(&packet); + rtp_counter++; + } else { + std::cout << "Skipping malformed event." << std::endl; + } + } + if (!FLAGS_nortcp && event.has_type() && event.type() == event.RTCP_EVENT) { + if (event.has_timestamp_us() && event.has_rtcp_packet() && + event.rtcp_packet().has_type() && + event.rtcp_packet().has_packet_data() && + event.rtcp_packet().packet_data().size() > 0) { + const webrtc::rtclog::RtcpPacket& rtcp_packet = event.rtcp_packet(); + if (FLAGS_noaudio && rtcp_packet.type() == webrtc::rtclog::AUDIO) + continue; + if (FLAGS_novideo && rtcp_packet.type() == webrtc::rtclog::VIDEO) + continue; + if (FLAGS_nodata && rtcp_packet.type() == webrtc::rtclog::DATA) + continue; + if (!FLAGS_ssrc.empty()) { + const uint32_t packet_ssrc = + webrtc::ByteReader::ReadBigEndian( + reinterpret_cast( + rtcp_packet.packet_data().data() + 4)); + if (packet_ssrc != ssrc_filter) + continue; + } + + webrtc::test::RtpPacket packet; + packet.length = rtcp_packet.packet_data().size(); + if (packet.length > packet.kMaxPacketBufferSize) { + std::cout << "Skipping packet with size " << packet.length + << ", the maximum supported size is " + << packet.kMaxPacketBufferSize << std::endl; + continue; + } + // For RTCP packets the original_length should be set to 0 in the + // RTPdump format. + packet.original_length = 0; + packet.time_ms = event.timestamp_us() / 1000; + memcpy(packet.data, rtcp_packet.packet_data().data(), packet.length); + rtp_writer->WritePacket(&packet); + rtcp_counter++; + } else { + std::cout << "Skipping malformed event." << std::endl; + } + } + } + std::cout << "Wrote " << rtp_counter << (header_only ? " header-only" : "") + << " RTP packets and " << rtcp_counter << " RTCP packets to the " + << "output file." << std::endl; + return 0; +} diff --git a/webrtc/webrtc.gyp b/webrtc/webrtc.gyp index 12b14ee0ae..4f4f100483 100644 --- a/webrtc/webrtc.gyp +++ b/webrtc/webrtc.gyp @@ -29,6 +29,17 @@ }, 'includes': ['build/protoc.gypi'], }, + { + 'target_name': 'rtc_event_log2rtp_dump', + 'type': 'executable', + 'sources': ['video/rtc_event_log2rtp_dump.cc',], + 'dependencies': [ + '<(DEPTH)/third_party/gflags/gflags.gyp:gflags', + 'rtc_event_log', + 'rtc_event_log_proto', + 'test/test.gyp:rtp_test_utils' + ], + } ], }], ],