From 4b5625e5acc4022fd2b9e01f7746497e569103ea Mon Sep 17 00:00:00 2001 From: "pbos@webrtc.org" Date: Wed, 6 Aug 2014 16:26:56 +0000 Subject: [PATCH] RTP video playback tool using Call APIs. Plays back rtpdump files from Wireshark in realtime as well as save the resulting raw video to file. Unlike the RTP playback tool it doesn't support faster-than-realtime playback/rendering, but it instead utilizes the same path as production code and also contains support for playing back FEC. BUG= R=stefan@webrtc.org Review URL: https://webrtc-codereview.appspot.com/16969004 git-svn-id: http://webrtc.googlecode.com/svn/trunk@6838 4adac7df-926f-26a2-2b94-8c16560cd09d --- webrtc/modules/modules.gyp | 4 - .../remote_bitrate_estimator.gypi | 8 +- .../remote_bitrate_estimator/tools/bwe_rtp.cc | 10 +- .../remote_bitrate_estimator/tools/bwe_rtp.h | 6 +- .../tools/bwe_rtp_play.cc | 26 +- .../tools/rtp_to_text.cc | 36 +-- .../main/source/video_coding_test.gypi | 5 +- .../video_coding/main/test/pcap_file_reader.h | 26 -- .../video_coding/main/test/rtp_file_reader.cc | 166 ---------- .../video_coding/main/test/rtp_file_reader.h | 26 -- .../main/test/rtp_file_reader_unittest.cc | 54 ---- .../video_coding/main/test/rtp_player.cc | 44 ++- .../video_coding/main/test/rtp_player.h | 14 - .../rtp_file_reader.cc} | 291 +++++++++++++----- webrtc/test/rtp_file_reader.h | 42 +++ .../rtp_file_reader_unittest.cc} | 60 ++-- webrtc/test/webrtc_test_common.gyp | 3 + webrtc/video/replay.cc | 266 ++++++++++++++++ webrtc/webrtc_tests.gypi | 26 ++ 19 files changed, 646 insertions(+), 467 deletions(-) delete mode 100644 webrtc/modules/video_coding/main/test/pcap_file_reader.h delete mode 100644 webrtc/modules/video_coding/main/test/rtp_file_reader.cc delete mode 100644 webrtc/modules/video_coding/main/test/rtp_file_reader.h delete mode 100644 webrtc/modules/video_coding/main/test/rtp_file_reader_unittest.cc rename webrtc/{modules/video_coding/main/test/pcap_file_reader.cc => test/rtp_file_reader.cc} (64%) create mode 100644 webrtc/test/rtp_file_reader.h rename webrtc/{modules/video_coding/main/test/pcap_file_reader_unittest.cc => test/rtp_file_reader_unittest.cc} (63%) create mode 100644 webrtc/video/replay.cc diff --git a/webrtc/modules/modules.gyp b/webrtc/modules/modules.gyp index 10a1860021..b8aa55b18b 100644 --- a/webrtc/modules/modules.gyp +++ b/webrtc/modules/modules.gyp @@ -242,10 +242,6 @@ 'video_coding/main/source/qm_select_unittest.cc', 'video_coding/main/source/test/stream_generator.cc', 'video_coding/main/source/test/stream_generator.h', - 'video_coding/main/test/pcap_file_reader.cc', - 'video_coding/main/test/pcap_file_reader_unittest.cc', - 'video_coding/main/test/rtp_file_reader.cc', - 'video_coding/main/test/rtp_file_reader_unittest.cc', 'video_processing/main/test/unit_test/brightness_detection_test.cc', 'video_processing/main/test/unit_test/color_enhancement_test.cc', 'video_processing/main/test/unit_test/content_metrics_test.cc', diff --git a/webrtc/modules/remote_bitrate_estimator/remote_bitrate_estimator.gypi b/webrtc/modules/remote_bitrate_estimator/remote_bitrate_estimator.gypi index c2f1b3da47..f290a34c53 100644 --- a/webrtc/modules/remote_bitrate_estimator/remote_bitrate_estimator.gypi +++ b/webrtc/modules/remote_bitrate_estimator/remote_bitrate_estimator.gypi @@ -56,8 +56,8 @@ }, 'sources': [ 'tools/rtp_to_text.cc', - '<(webrtc_root)/modules/video_coding/main/test/rtp_file_reader.cc', - '<(webrtc_root)/modules/video_coding/main/test/rtp_file_reader.h', + '<(webrtc_root)/test/rtp_file_reader.cc', + '<(webrtc_root)/test/rtp_file_reader.h', ], # source }, { @@ -79,8 +79,8 @@ }, 'sources': [ 'tools/bwe_rtp_play.cc', - '<(webrtc_root)/modules/video_coding/main/test/rtp_file_reader.cc', - '<(webrtc_root)/modules/video_coding/main/test/rtp_file_reader.h', + '<(webrtc_root)/test/rtp_file_reader.cc', + '<(webrtc_root)/test/rtp_file_reader.h', ], # source }, ], # targets diff --git a/webrtc/modules/remote_bitrate_estimator/tools/bwe_rtp.cc b/webrtc/modules/remote_bitrate_estimator/tools/bwe_rtp.cc index 40fa6df8ff..e71c75ce39 100644 --- a/webrtc/modules/remote_bitrate_estimator/tools/bwe_rtp.cc +++ b/webrtc/modules/remote_bitrate_estimator/tools/bwe_rtp.cc @@ -16,10 +16,7 @@ #include "webrtc/modules/remote_bitrate_estimator/include/remote_bitrate_estimator.h" #include "webrtc/modules/rtp_rtcp/interface/rtp_header_parser.h" #include "webrtc/modules/rtp_rtcp/interface/rtp_payload_registry.h" -#include "webrtc/modules/video_coding/main/test/rtp_file_reader.h" -#include "webrtc/modules/video_coding/main/test/rtp_player.h" - -using webrtc::rtpplayer::RtpPacketSourceInterface; +#include "webrtc/test/rtp_file_reader.h" const int kMinBitrateBps = 30000; @@ -27,11 +24,12 @@ bool ParseArgsAndSetupEstimator(int argc, char** argv, webrtc::Clock* clock, webrtc::RemoteBitrateObserver* observer, - RtpPacketSourceInterface** rtp_reader, + webrtc::test::RtpFileReader** rtp_reader, webrtc::RtpHeaderParser** parser, webrtc::RemoteBitrateEstimator** estimator, std::string* estimator_used) { - *rtp_reader = webrtc::rtpplayer::CreateRtpFileReader(argv[3]); + *rtp_reader = webrtc::test::RtpFileReader::Create( + webrtc::test::RtpFileReader::kRtpDump, argv[3]); if (!*rtp_reader) { fprintf(stderr, "Cannot open input file %s\n", argv[3]); return false; diff --git a/webrtc/modules/remote_bitrate_estimator/tools/bwe_rtp.h b/webrtc/modules/remote_bitrate_estimator/tools/bwe_rtp.h index 714457d566..2d12a8083f 100644 --- a/webrtc/modules/remote_bitrate_estimator/tools/bwe_rtp.h +++ b/webrtc/modules/remote_bitrate_estimator/tools/bwe_rtp.h @@ -18,8 +18,8 @@ class Clock; class RemoteBitrateEstimator; class RemoteBitrateObserver; class RtpHeaderParser; -namespace rtpplayer { -class RtpPacketSourceInterface; +namespace test { +class RtpFileReader; } } @@ -28,7 +28,7 @@ bool ParseArgsAndSetupEstimator( char** argv, webrtc::Clock* clock, webrtc::RemoteBitrateObserver* observer, - webrtc::rtpplayer::RtpPacketSourceInterface** rtp_reader, + webrtc::test::RtpFileReader** rtp_reader, webrtc::RtpHeaderParser** parser, webrtc::RemoteBitrateEstimator** estimator, std::string* estimator_used); diff --git a/webrtc/modules/remote_bitrate_estimator/tools/bwe_rtp_play.cc b/webrtc/modules/remote_bitrate_estimator/tools/bwe_rtp_play.cc index 9ea3f08eab..eb224f2e4a 100644 --- a/webrtc/modules/remote_bitrate_estimator/tools/bwe_rtp_play.cc +++ b/webrtc/modules/remote_bitrate_estimator/tools/bwe_rtp_play.cc @@ -14,11 +14,8 @@ #include "webrtc/modules/remote_bitrate_estimator/tools/bwe_rtp.h" #include "webrtc/modules/rtp_rtcp/interface/rtp_header_parser.h" #include "webrtc/modules/rtp_rtcp/interface/rtp_payload_registry.h" -#include "webrtc/modules/video_coding/main/test/rtp_file_reader.h" -#include "webrtc/modules/video_coding/main/test/rtp_player.h" #include "webrtc/system_wrappers/interface/scoped_ptr.h" - -using webrtc::rtpplayer::RtpPacketSourceInterface; +#include "webrtc/test/rtp_file_reader.h" class Observer : public webrtc::RemoteBitrateObserver { public: @@ -49,7 +46,7 @@ int main(int argc, char** argv) { " is the id associated with the extension.\n"); return -1; } - RtpPacketSourceInterface* reader; + webrtc::test::RtpFileReader* reader; webrtc::RemoteBitrateEstimator* estimator; webrtc::RtpHeaderParser* parser; std::string estimator_used; @@ -59,7 +56,7 @@ int main(int argc, char** argv) { &parser, &estimator, &estimator_used)) { return -1; } - webrtc::scoped_ptr rtp_reader(reader); + webrtc::scoped_ptr rtp_reader(reader); webrtc::scoped_ptr rtp_parser(parser); webrtc::scoped_ptr rbe(estimator); @@ -68,30 +65,25 @@ int main(int argc, char** argv) { int64_t next_process_time_ms = 0; int64_t next_rtp_time_ms = 0; int64_t first_rtp_time_ms = -1; - const uint32_t kMaxPacketSize = 1500; - uint8_t packet_buffer[kMaxPacketSize]; - uint8_t* packet = packet_buffer; int non_zero_abs_send_time = 0; int non_zero_ts_offsets = 0; while (true) { - uint32_t next_rtp_time; if (next_rtp_time_ms <= clock.TimeInMilliseconds()) { - uint32_t packet_length = kMaxPacketSize; - if (rtp_reader->NextPacket(packet, &packet_length, - &next_rtp_time) == -1) { + webrtc::test::RtpFileReader::Packet packet; + if (!rtp_reader->NextPacket(&packet)) { break; } if (first_rtp_time_ms == -1) - first_rtp_time_ms = next_rtp_time; - next_rtp_time_ms = next_rtp_time - first_rtp_time_ms; + first_rtp_time_ms = packet.time_ms; + packet.time_ms = packet.time_ms - first_rtp_time_ms; webrtc::RTPHeader header; - parser->Parse(packet, packet_length, &header); + parser->Parse(packet.data, packet.length, &header); if (header.extension.absoluteSendTime != 0) ++non_zero_abs_send_time; if (header.extension.transmissionTimeOffset != 0) ++non_zero_ts_offsets; rbe->IncomingPacket(clock.TimeInMilliseconds(), - packet_length - header.headerLength, + static_cast(packet.length - header.headerLength), header); ++packet_counter; } diff --git a/webrtc/modules/remote_bitrate_estimator/tools/rtp_to_text.cc b/webrtc/modules/remote_bitrate_estimator/tools/rtp_to_text.cc index af4a4d4ee7..a85bca4f64 100644 --- a/webrtc/modules/remote_bitrate_estimator/tools/rtp_to_text.cc +++ b/webrtc/modules/remote_bitrate_estimator/tools/rtp_to_text.cc @@ -14,11 +14,8 @@ #include "webrtc/modules/remote_bitrate_estimator/tools/bwe_rtp.h" #include "webrtc/modules/rtp_rtcp/interface/rtp_header_parser.h" #include "webrtc/modules/rtp_rtcp/interface/rtp_payload_registry.h" -#include "webrtc/modules/video_coding/main/test/rtp_file_reader.h" -#include "webrtc/modules/video_coding/main/test/rtp_player.h" #include "webrtc/system_wrappers/interface/scoped_ptr.h" - -using webrtc::rtpplayer::RtpPacketSourceInterface; +#include "webrtc/test/rtp_file_reader.h" int main(int argc, char** argv) { if (argc < 4) { @@ -32,43 +29,44 @@ int main(int argc, char** argv) { " output.\n"); return -1; } - RtpPacketSourceInterface* reader; + webrtc::test::RtpFileReader* reader; webrtc::RtpHeaderParser* parser; if (!ParseArgsAndSetupEstimator(argc, argv, NULL, NULL, &reader, &parser, NULL, NULL)) { return -1; } bool arrival_time_only = (argc >= 5 && strncmp(argv[4], "-t", 2) == 0); - webrtc::scoped_ptr rtp_reader(reader); + webrtc::scoped_ptr rtp_reader(reader); webrtc::scoped_ptr rtp_parser(parser); fprintf(stdout, "seqnum timestamp ts_offset abs_sendtime recvtime " "markerbit ssrc size\n"); int packet_counter = 0; - static const uint32_t kMaxPacketSize = 1500; - uint8_t packet_buffer[kMaxPacketSize]; - uint8_t* packet = packet_buffer; - uint32_t packet_length = kMaxPacketSize; - uint32_t time_ms = 0; int non_zero_abs_send_time = 0; int non_zero_ts_offsets = 0; - while (rtp_reader->NextPacket(packet, &packet_length, &time_ms) == 0) { + webrtc::test::RtpFileReader::Packet packet; + while (rtp_reader->NextPacket(&packet)) { webrtc::RTPHeader header; - parser->Parse(packet, packet_length, &header); + parser->Parse(packet.data, packet.length, &header); if (header.extension.absoluteSendTime != 0) ++non_zero_abs_send_time; if (header.extension.transmissionTimeOffset != 0) ++non_zero_ts_offsets; if (arrival_time_only) { std::stringstream ss; - ss << static_cast(time_ms) * 1000000; + ss << static_cast(packet.time_ms) * 1000000; fprintf(stdout, "%s\n", ss.str().c_str()); } else { - fprintf(stdout, "%u %u %d %u %u %d %u %u\n", header.sequenceNumber, - header.timestamp, header.extension.transmissionTimeOffset, - header.extension.absoluteSendTime, time_ms, header.markerBit, - header.ssrc, packet_length); + fprintf(stdout, + "%u %u %d %u %u %d %u %d\n", + header.sequenceNumber, + header.timestamp, + header.extension.transmissionTimeOffset, + header.extension.absoluteSendTime, + packet.time_ms, + header.markerBit, + header.ssrc, + static_cast(packet.length)); } - packet_length = kMaxPacketSize; ++packet_counter; } fprintf(stderr, "Parsed %d packets\n", packet_counter); diff --git a/webrtc/modules/video_coding/main/source/video_coding_test.gypi b/webrtc/modules/video_coding/main/source/video_coding_test.gypi index b0fe510cf9..576524d13e 100644 --- a/webrtc/modules/video_coding/main/source/video_coding_test.gypi +++ b/webrtc/modules/video_coding/main/source/video_coding_test.gypi @@ -21,6 +21,7 @@ '<(webrtc_root)/test/metrics.gyp:metrics', '<(webrtc_root)/common_video/common_video.gyp:common_video', '<(webrtc_root)/system_wrappers/source/system_wrappers.gyp:field_trial_default', + '<(webrtc_root)/test/webrtc_test_common.gyp:webrtc_test_common', ], 'sources': [ # headers @@ -30,11 +31,9 @@ '../test/media_opt_test.h', '../test/mt_test_common.h', '../test/normal_test.h', - '../test/pcap_file_reader.h', '../test/quality_modes_test.h', '../test/receiver_tests.h', '../test/release_test.h', - '../test/rtp_file_reader.h', '../test/rtp_player.h', '../test/test_callbacks.h', '../test/test_util.h', @@ -48,9 +47,7 @@ '../test/mt_test_common.cc', '../test/mt_rx_tx_test.cc', '../test/normal_test.cc', - '../test/pcap_file_reader.cc', '../test/quality_modes_test.cc', - '../test/rtp_file_reader.cc', '../test/rtp_player.cc', '../test/test_callbacks.cc', '../test/test_util.cc', diff --git a/webrtc/modules/video_coding/main/test/pcap_file_reader.h b/webrtc/modules/video_coding/main/test/pcap_file_reader.h deleted file mode 100644 index 6450e2d64a..0000000000 --- a/webrtc/modules/video_coding/main/test/pcap_file_reader.h +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright (c) 2013 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. - */ - -#ifndef WEBRTC_MODULES_VIDEO_CODING_TEST_PCAP_FILE_READER_H_ -#define WEBRTC_MODULES_VIDEO_CODING_TEST_PCAP_FILE_READER_H_ - -#include - -namespace webrtc { -namespace rtpplayer { - -class RtpPacketSourceInterface; - -RtpPacketSourceInterface* CreatePcapFileReader(const std::string& filename); - -} // namespace rtpplayer -} // namespace webrtc - -#endif // WEBRTC_MODULES_VIDEO_CODING_TEST_PCAP_FILE_READER_H_ diff --git a/webrtc/modules/video_coding/main/test/rtp_file_reader.cc b/webrtc/modules/video_coding/main/test/rtp_file_reader.cc deleted file mode 100644 index 447eb9bea9..0000000000 --- a/webrtc/modules/video_coding/main/test/rtp_file_reader.cc +++ /dev/null @@ -1,166 +0,0 @@ -/* - * Copyright (c) 2013 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 "webrtc/modules/video_coding/main/test/rtp_file_reader.h" - -#ifdef WIN32 -#include -#include -#else -#include -#endif -#include -#include - -#include "webrtc/modules/video_coding/main/test/rtp_player.h" -#include "webrtc/system_wrappers/interface/scoped_ptr.h" - -namespace webrtc { -namespace rtpplayer { - -enum { - kResultFail = -1, - kResultSuccess = 0, - - kFirstLineLength = 40, // More than needed to read the ID line. - kPacketHeaderSize = 8 // Rtpplay packet header size in bytes. -}; - -#if 1 -# define DEBUG_LOG(text) -# define DEBUG_LOG1(text, arg) -#else -# define DEBUG_LOG(text) (printf(text "\n")) -# define DEBUG_LOG1(text, arg) (printf(text "\n", arg)) -#endif - -#define TRY(expr) \ - do { \ - if ((expr) < 0) { \ - DEBUG_LOG1("FAIL at " __FILE__ ":%d", __LINE__); \ - return kResultFail; \ - } \ - } while (0) - -// Read RTP packets from file in rtpdump format, as documented at: -// http://www.cs.columbia.edu/irt/software/rtptools/ -class RtpFileReaderImpl : public RtpPacketSourceInterface { - public: - RtpFileReaderImpl() : file_(NULL) {} - virtual ~RtpFileReaderImpl() { - if (file_ != NULL) { - fclose(file_); - file_ = NULL; - } - } - - int Initialize(const std::string& filename) { - file_ = fopen(filename.c_str(), "rb"); - if (file_ == NULL) { - printf("ERROR: Can't open file: %s\n", filename.c_str()); - return kResultFail; - } - - char firstline[kFirstLineLength + 1] = {0}; - if (fgets(firstline, kFirstLineLength, file_) == NULL) { - DEBUG_LOG("ERROR: Can't read from file\n"); - return kResultFail; - } - if (strncmp(firstline, "#!rtpplay", 9) == 0) { - if (strncmp(firstline, "#!rtpplay1.0", 12) != 0) { - DEBUG_LOG("ERROR: wrong rtpplay version, must be 1.0\n"); - return kResultFail; - } - } else if (strncmp(firstline, "#!RTPencode", 11) == 0) { - if (strncmp(firstline, "#!RTPencode1.0", 14) != 0) { - DEBUG_LOG("ERROR: wrong RTPencode version, must be 1.0\n"); - return kResultFail; - } - } else { - DEBUG_LOG("ERROR: wrong file format of input file\n"); - return kResultFail; - } - - uint32_t start_sec; - uint32_t start_usec; - uint32_t source; - uint16_t port; - uint16_t padding; - TRY(Read(&start_sec)); - TRY(Read(&start_usec)); - TRY(Read(&source)); - TRY(Read(&port)); - TRY(Read(&padding)); - - return kResultSuccess; - } - - virtual int NextPacket(uint8_t* rtp_data, uint32_t* length, - uint32_t* time_ms) { - assert(rtp_data); - assert(length); - assert(time_ms); - - uint16_t len; - uint16_t plen; - uint32_t offset; - TRY(Read(&len)); - TRY(Read(&plen)); - TRY(Read(&offset)); - - // Use 'len' here because a 'plen' of 0 specifies rtcp. - len -= kPacketHeaderSize; - if (*length < len) { - return kResultFail; - } - if (fread(rtp_data, 1, len, file_) != len) { - return kResultFail; - } - - *length = len; - *time_ms = offset; - return kResultSuccess; - } - - private: - int Read(uint32_t* out) { - assert(out); - uint32_t tmp = 0; - if (fread(&tmp, 1, sizeof(uint32_t), file_) != sizeof(uint32_t)) { - return kResultFail; - } - *out = ntohl(tmp); - return kResultSuccess; - } - - int Read(uint16_t* out) { - assert(out); - uint16_t tmp = 0; - if (fread(&tmp, 1, sizeof(uint16_t), file_) != sizeof(uint16_t)) { - return kResultFail; - } - *out = ntohs(tmp); - return kResultSuccess; - } - - FILE* file_; - - DISALLOW_COPY_AND_ASSIGN(RtpFileReaderImpl); -}; - -RtpPacketSourceInterface* CreateRtpFileReader(const std::string& filename) { - scoped_ptr impl(new RtpFileReaderImpl()); - if (impl->Initialize(filename) != 0) { - return NULL; - } - return impl.release(); -} -} // namespace rtpplayer -} // namespace webrtc diff --git a/webrtc/modules/video_coding/main/test/rtp_file_reader.h b/webrtc/modules/video_coding/main/test/rtp_file_reader.h deleted file mode 100644 index b763ee7513..0000000000 --- a/webrtc/modules/video_coding/main/test/rtp_file_reader.h +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright (c) 2013 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. - */ - -#ifndef WEBRTC_MODULES_VIDEO_CODING_MAIN_TEST_RTP_FILE_READER_H_ -#define WEBRTC_MODULES_VIDEO_CODING_MAIN_TEST_RTP_FILE_READER_H_ - -#include - -namespace webrtc { -namespace rtpplayer { - -class RtpPacketSourceInterface; - -RtpPacketSourceInterface* CreateRtpFileReader(const std::string& filename); - -} // namespace rtpplayer -} // namespace webrtc - -#endif // WEBRTC_MODULES_VIDEO_CODING_MAIN_TEST_RTP_FILE_READER_H_ diff --git a/webrtc/modules/video_coding/main/test/rtp_file_reader_unittest.cc b/webrtc/modules/video_coding/main/test/rtp_file_reader_unittest.cc deleted file mode 100644 index 7e0e607cae..0000000000 --- a/webrtc/modules/video_coding/main/test/rtp_file_reader_unittest.cc +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright (c) 2013 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 "testing/gtest/include/gtest/gtest.h" -#include "webrtc/modules/rtp_rtcp/source/rtp_utility.h" -#include "webrtc/modules/video_coding/main/test/rtp_file_reader.h" -#include "webrtc/modules/video_coding/main/test/rtp_player.h" -#include "webrtc/system_wrappers/interface/scoped_ptr.h" -#include "webrtc/test/testsupport/fileutils.h" - -namespace webrtc { -namespace rtpplayer { - -class TestRtpFileReader : public ::testing::Test { - public: - void Init(const std::string& filename) { - std::string filepath = - test::ResourcePath("video_coding/" + filename, "rtp"); - rtp_packet_source_.reset(CreateRtpFileReader(filepath)); - ASSERT_TRUE(rtp_packet_source_.get() != NULL); - } - - int CountRtpPackets() { - const uint32_t kBufferSize = 4096; - uint8_t data[kBufferSize]; - uint32_t length = kBufferSize; - uint32_t dummy_time_ms = 0; - int c = 0; - while (rtp_packet_source_->NextPacket(data, &length, &dummy_time_ms) == 0) { - EXPECT_GE(kBufferSize, length); - length = kBufferSize; - c++; - } - return c; - } - - private: - scoped_ptr rtp_packet_source_; -}; - -TEST_F(TestRtpFileReader, Test60Packets) { - Init("pltype103"); - EXPECT_EQ(60, CountRtpPackets()); -} - -} // namespace rtpplayer -} // namespace webrtc diff --git a/webrtc/modules/video_coding/main/test/rtp_player.cc b/webrtc/modules/video_coding/main/test/rtp_player.cc index f02aebba5e..8c8c56ebe3 100644 --- a/webrtc/modules/video_coding/main/test/rtp_player.cc +++ b/webrtc/modules/video_coding/main/test/rtp_player.cc @@ -19,12 +19,11 @@ #include "webrtc/modules/rtp_rtcp/interface/rtp_receiver.h" #include "webrtc/modules/rtp_rtcp/interface/rtp_rtcp.h" #include "webrtc/modules/video_coding/main/source/internal_defines.h" -#include "webrtc/modules/video_coding/main/test/pcap_file_reader.h" -#include "webrtc/modules/video_coding/main/test/rtp_file_reader.h" #include "webrtc/modules/video_coding/main/test/test_util.h" #include "webrtc/system_wrappers/interface/clock.h" #include "webrtc/system_wrappers/interface/critical_section_wrapper.h" #include "webrtc/system_wrappers/interface/scoped_ptr.h" +#include "webrtc/test/rtp_file_reader.h" #if 1 # define DEBUG_LOG1(text, arg) @@ -323,7 +322,7 @@ class RtpPlayerImpl : public RtpPlayerInterface { public: RtpPlayerImpl(PayloadSinkFactoryInterface* payload_sink_factory, const PayloadTypes& payload_types, Clock* clock, - scoped_ptr* packet_source, + scoped_ptr* packet_source, float loss_rate, uint32_t rtt_ms, bool reordering) : ssrc_handlers_(payload_sink_factory, payload_types), clock_(clock), @@ -337,9 +336,7 @@ class RtpPlayerImpl : public RtpPlayerInterface { no_loss_startup_(100), end_of_file_(false), reordering_(false), - reorder_buffer_(), - next_packet_(), - next_packet_length_(0) { + reorder_buffer_() { assert(clock); assert(packet_source); assert(packet_source->get()); @@ -368,22 +365,23 @@ class RtpPlayerImpl : public RtpPlayerInterface { // Send any packets from packet source. if (!end_of_file_ && (TimeUntilNextPacket() == 0 || first_packet_)) { if (first_packet_) { - next_packet_length_ = sizeof(next_packet_); - if (packet_source_->NextPacket(next_packet_, &next_packet_length_, - &next_rtp_time_) != 0) { + if (!packet_source_->NextPacket(&next_packet_)) return 0; - } - first_packet_rtp_time_ = next_rtp_time_; + first_packet_rtp_time_ = next_packet_.time_ms; first_packet_time_ms_ = clock_->TimeInMilliseconds(); first_packet_ = false; } if (reordering_ && reorder_buffer_.get() == NULL) { - reorder_buffer_.reset(new RawRtpPacket(next_packet_, - next_packet_length_, 0, 0)); + reorder_buffer_.reset( + new RawRtpPacket(next_packet_.data, + static_cast(next_packet_.length), + 0, + 0)); return 0; } - int ret = SendPacket(next_packet_, next_packet_length_); + int ret = SendPacket(next_packet_.data, + static_cast(next_packet_.length)); if (reorder_buffer_.get()) { SendPacket(reorder_buffer_->data(), reorder_buffer_->length()); reorder_buffer_.reset(NULL); @@ -392,13 +390,11 @@ class RtpPlayerImpl : public RtpPlayerInterface { return ret; } - next_packet_length_ = sizeof(next_packet_); - if (packet_source_->NextPacket(next_packet_, &next_packet_length_, - &next_rtp_time_) != 0) { + if (!packet_source_->NextPacket(&next_packet_)) { end_of_file_ = true; return 0; } - else if (next_packet_length_ == 0) { + else if (next_packet_.length == 0) { return 0; } } @@ -456,7 +452,8 @@ class RtpPlayerImpl : public RtpPlayerInterface { SsrcHandlers ssrc_handlers_; Clock* clock_; - scoped_ptr packet_source_; + scoped_ptr packet_source_; + test::RtpFileReader::Packet next_packet_; uint32_t next_rtp_time_; bool first_packet_; int64_t first_packet_rtp_time_; @@ -468,8 +465,6 @@ class RtpPlayerImpl : public RtpPlayerInterface { bool end_of_file_; bool reordering_; scoped_ptr reorder_buffer_; - uint8_t next_packet_[kMaxPacketBufferSize]; - uint32_t next_packet_length_; DISALLOW_IMPLICIT_CONSTRUCTORS(RtpPlayerImpl); }; @@ -478,10 +473,11 @@ RtpPlayerInterface* Create(const std::string& input_filename, PayloadSinkFactoryInterface* payload_sink_factory, Clock* clock, const PayloadTypes& payload_types, float loss_rate, uint32_t rtt_ms, bool reordering) { - scoped_ptr packet_source( - CreateRtpFileReader(input_filename)); + scoped_ptr packet_source(test::RtpFileReader::Create( + test::RtpFileReader::kRtpDump, input_filename)); if (packet_source.get() == NULL) { - packet_source.reset(CreatePcapFileReader(input_filename)); + packet_source.reset(test::RtpFileReader::Create(test::RtpFileReader::kPcap, + input_filename)); if (packet_source.get() == NULL) { return NULL; } diff --git a/webrtc/modules/video_coding/main/test/rtp_player.h b/webrtc/modules/video_coding/main/test/rtp_player.h index 24e62b63c5..1703618a24 100644 --- a/webrtc/modules/video_coding/main/test/rtp_player.h +++ b/webrtc/modules/video_coding/main/test/rtp_player.h @@ -44,20 +44,6 @@ class PayloadCodecTuple { typedef std::vector PayloadTypes; typedef std::vector::const_iterator PayloadTypesIterator; -// Implemented by something that can provide RTP packets, for instance a file -// format parser such as the rtp_file_reader or the pcap_file_reader. -class RtpPacketSourceInterface { - public: - virtual ~RtpPacketSourceInterface() {} - - // Read next RTP packet into buffer pointed to by rtp_data. On call, 'length' - // field must be filled in with the size of the buffer. The actual size of - // the packet is available in 'length' upon returning. Time in milliseconds - // from start of stream is returned in 'time_ms'. - virtual int NextPacket(uint8_t* rtp_data, uint32_t* length, - uint32_t* time_ms) = 0; -}; - // Implemented by RtpPlayer and given to client as a means to retrieve // information about a specific RTP stream. class RtpStreamInterface { diff --git a/webrtc/modules/video_coding/main/test/pcap_file_reader.cc b/webrtc/test/rtp_file_reader.cc similarity index 64% rename from webrtc/modules/video_coding/main/test/pcap_file_reader.cc rename to webrtc/test/rtp_file_reader.cc index 68c856652d..be8dc2bc8f 100644 --- a/webrtc/modules/video_coding/main/test/pcap_file_reader.cc +++ b/webrtc/test/rtp_file_reader.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved. + * Copyright (c) 2014 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 @@ -8,15 +8,8 @@ * be found in the AUTHORS file in the root of the source tree. */ -#include "webrtc/modules/video_coding/main/test/pcap_file_reader.h" +#include "webrtc/test/rtp_file_reader.h" -#ifdef WIN32 -#include -#include -#else -#include -#endif -#include #include #include @@ -24,11 +17,142 @@ #include #include "webrtc/modules/rtp_rtcp/source/rtp_utility.h" -#include "webrtc/modules/video_coding/main/test/rtp_player.h" #include "webrtc/system_wrappers/interface/scoped_ptr.h" namespace webrtc { -namespace rtpplayer { +namespace test { + +static const size_t kFirstLineLength = 40; +static uint16_t kPacketHeaderSize = 8; + +#if 1 +# define DEBUG_LOG(text) +# define DEBUG_LOG1(text, arg) +#else +# define DEBUG_LOG(text) (printf(text "\n")) +# define DEBUG_LOG1(text, arg) (printf(text "\n", arg)) +#endif + +#define TRY(expr) \ + do { \ + if (!(expr)) { \ + DEBUG_LOG1("FAIL at " __FILE__ ":%d", __LINE__); \ + return false; \ + } \ + } while (0) + +class RtpFileReaderImpl : public RtpFileReader { + public: + virtual bool Init(const std::string& filename) = 0; +}; + +// Read RTP packets from file in rtpdump format, as documented at: +// http://www.cs.columbia.edu/irt/software/rtptools/ +class RtpDumpReader : public RtpFileReaderImpl { + public: + RtpDumpReader() : file_(NULL) {} + virtual ~RtpDumpReader() { + if (file_ != NULL) { + fclose(file_); + file_ = NULL; + } + } + + bool Init(const std::string& filename) { + file_ = fopen(filename.c_str(), "rb"); + if (file_ == NULL) { + printf("ERROR: Can't open file: %s\n", filename.c_str()); + return false; + } + + char firstline[kFirstLineLength + 1] = {0}; + if (fgets(firstline, kFirstLineLength, file_) == NULL) { + DEBUG_LOG("ERROR: Can't read from file\n"); + return false; + } + if (strncmp(firstline, "#!rtpplay", 9) == 0) { + if (strncmp(firstline, "#!rtpplay1.0", 12) != 0) { + DEBUG_LOG("ERROR: wrong rtpplay version, must be 1.0\n"); + return false; + } + } else if (strncmp(firstline, "#!RTPencode", 11) == 0) { + if (strncmp(firstline, "#!RTPencode1.0", 14) != 0) { + DEBUG_LOG("ERROR: wrong RTPencode version, must be 1.0\n"); + return false; + } + } else { + DEBUG_LOG("ERROR: wrong file format of input file\n"); + return false; + } + + uint32_t start_sec; + uint32_t start_usec; + uint32_t source; + uint16_t port; + uint16_t padding; + TRY(Read(&start_sec)); + TRY(Read(&start_usec)); + TRY(Read(&source)); + TRY(Read(&port)); + TRY(Read(&padding)); + + return true; + } + + virtual bool NextPacket(Packet* packet) OVERRIDE { + uint8_t* rtp_data = packet->data; + packet->length = Packet::kMaxPacketBufferSize; + + uint16_t len; + uint16_t plen; + uint32_t offset; + TRY(Read(&len)); + TRY(Read(&plen)); + TRY(Read(&offset)); + + // Use 'len' here because a 'plen' of 0 specifies rtcp. + len -= kPacketHeaderSize; + if (packet->length < len) { + return false; + } + if (fread(rtp_data, 1, len, file_) != len) { + return false; + } + + packet->length = len; + packet->time_ms = offset; + return true; + } + + private: + bool Read(uint32_t* out) { + *out = 0; + for (size_t i = 0; i < 4; ++i) { + *out <<= 8; + uint8_t tmp; + if (fread(&tmp, 1, sizeof(uint8_t), file_) != sizeof(uint8_t)) + return false; + *out |= tmp; + } + return true; + } + + bool Read(uint16_t* out) { + *out = 0; + for (size_t i = 0; i < 2; ++i) { + *out <<= 8; + uint8_t tmp; + if (fread(&tmp, 1, sizeof(uint8_t), file_) != sizeof(uint8_t)) + return false; + *out |= tmp; + } + return true; + } + + FILE* file_; + + DISALLOW_COPY_AND_ASSIGN(RtpDumpReader); +}; enum { kResultFail = -1, @@ -56,50 +180,46 @@ enum { const uint32_t kPcapBOMSwapOrder = 0xd4c3b2a1UL; const uint32_t kPcapBOMNoSwapOrder = 0xa1b2c3d4UL; -#if 1 -# define DEBUG_LOG(text) -# define DEBUG_LOG1(text, arg) -#else -# define DEBUG_LOG(text) (printf(text "\n")) -# define DEBUG_LOG1(text, arg) (printf(text "\n", arg)) -#endif - -#define TRY(expr) \ - do { \ - int r = (expr); \ - if (r == kResultFail) { \ - DEBUG_LOG1("FAIL at " __FILE__ ":%d", __LINE__); \ - return kResultFail; \ - } else if (r == kResultSkip) { \ - return kResultSkip; \ - } \ +#define TRY_PCAP(expr) \ + do { \ + int r = (expr); \ + if (r == kResultFail) { \ + DEBUG_LOG1("FAIL at " __FILE__ ":%d", __LINE__); \ + return kResultFail; \ + } else if (r == kResultSkip) { \ + return kResultSkip; \ + } \ } while (0) // Read RTP packets from file in tcpdump/libpcap format, as documented at: // http://wiki.wireshark.org/Development/LibpcapFileFormat -class PcapFileReaderImpl : public RtpPacketSourceInterface { +class PcapReader : public RtpFileReaderImpl { public: - PcapFileReaderImpl() + PcapReader() : file_(NULL), swap_pcap_byte_order_(false), +#ifdef WEBRTC_ARCH_BIG_ENDIAN swap_network_byte_order_(false), +#else + swap_network_byte_order_(true), +#endif read_buffer_(), packets_by_ssrc_(), packets_(), next_packet_it_() { - int16_t test = 0x7f00; - if (test != htons(test)) { - swap_network_byte_order_ = true; - } } - virtual ~PcapFileReaderImpl() { + virtual ~PcapReader() { if (file_ != NULL) { fclose(file_); file_ = NULL; } } + bool Init(const std::string& filename) OVERRIDE { + return Initialize(filename) == kResultSuccess; + } + int Initialize(const std::string& filename) { file_ = fopen(filename.c_str(), "rb"); if (file_ == NULL) { @@ -115,7 +235,7 @@ class PcapFileReaderImpl : public RtpPacketSourceInterface { uint32_t stream_start_ms = 0; int32_t next_packet_pos = ftell(file_); for (;;) { - TRY(fseek(file_, next_packet_pos, SEEK_SET)); + TRY_PCAP(fseek(file_, next_packet_pos, SEEK_SET)); int result = ReadPacket(&next_packet_pos, stream_start_ms, ++total_packet_count); if (result == kResultFail) { @@ -165,7 +285,15 @@ class PcapFileReaderImpl : public RtpPacketSourceInterface { return kResultSuccess; } - virtual int NextPacket(uint8_t* data, uint32_t* length, uint32_t* time_ms) { + virtual bool NextPacket(Packet* packet) OVERRIDE { + uint32_t length = Packet::kMaxPacketBufferSize; + if (NextPcap(packet->data, &length, &packet->time_ms) != kResultSuccess) + return false; + packet->length = static_cast(length); + return true; + } + + virtual int NextPcap(uint8_t* data, uint32_t* length, uint32_t* time_ms) { assert(data); assert(length); assert(time_ms); @@ -176,8 +304,8 @@ class PcapFileReaderImpl : public RtpPacketSourceInterface { if (*length < next_packet_it_->payload_length) { return -1; } - TRY(fseek(file_, next_packet_it_->pos_in_file, SEEK_SET)); - TRY(Read(data, next_packet_it_->payload_length)); + TRY_PCAP(fseek(file_, next_packet_it_->pos_in_file, SEEK_SET)); + TRY_PCAP(Read(data, next_packet_it_->payload_length)); *length = next_packet_it_->payload_length; *time_ms = next_packet_it_->time_offset_ms; next_packet_it_++; @@ -205,7 +333,7 @@ class PcapFileReaderImpl : public RtpPacketSourceInterface { int ReadGlobalHeader() { uint32_t magic; - TRY(Read(&magic, false)); + TRY_PCAP(Read(&magic, false)); if (magic == kPcapBOMSwapOrder) { swap_pcap_byte_order_ = true; } else if (magic == kPcapBOMNoSwapOrder) { @@ -216,8 +344,8 @@ class PcapFileReaderImpl : public RtpPacketSourceInterface { uint16_t version_major; uint16_t version_minor; - TRY(Read(&version_major, false)); - TRY(Read(&version_minor, false)); + TRY_PCAP(Read(&version_major, false)); + TRY_PCAP(Read(&version_minor, false)); if (version_major != kPcapVersionMajor || version_minor != kPcapVersionMinor) { return kResultFail; @@ -227,10 +355,10 @@ class PcapFileReaderImpl : public RtpPacketSourceInterface { uint32_t sigfigs; // Accuracy of timestamps. uint32_t snaplen; // Max length of captured packets, in octets. uint32_t network; // Data link type. - TRY(Read(&this_zone, false)); - TRY(Read(&sigfigs, false)); - TRY(Read(&snaplen, false)); - TRY(Read(&network, false)); + TRY_PCAP(Read(&this_zone, false)); + TRY_PCAP(Read(&sigfigs, false)); + TRY_PCAP(Read(&snaplen, false)); + TRY_PCAP(Read(&network, false)); // Accept only LINKTYPE_NULL and LINKTYPE_ETHERNET. // See: http://www.tcpdump.org/linktypes.html @@ -249,24 +377,24 @@ class PcapFileReaderImpl : public RtpPacketSourceInterface { uint32_t ts_usec; // Timestamp microseconds. uint32_t incl_len; // Number of octets of packet saved in file. uint32_t orig_len; // Actual length of packet. - TRY(Read(&ts_sec, false)); - TRY(Read(&ts_usec, false)); - TRY(Read(&incl_len, false)); - TRY(Read(&orig_len, false)); + TRY_PCAP(Read(&ts_sec, false)); + TRY_PCAP(Read(&ts_usec, false)); + TRY_PCAP(Read(&incl_len, false)); + TRY_PCAP(Read(&orig_len, false)); *next_packet_pos = ftell(file_) + incl_len; RtpPacketMarker marker = {0}; marker.packet_number = number; marker.time_offset_ms = CalcTimeDelta(ts_sec, ts_usec, stream_start_ms); - TRY(ReadPacketHeader(&marker)); + TRY_PCAP(ReadPacketHeader(&marker)); marker.pos_in_file = ftell(file_); if (marker.payload_length > sizeof(read_buffer_)) { printf("Packet too large!\n"); return kResultFail; } - TRY(Read(read_buffer_, marker.payload_length)); + TRY_PCAP(Read(read_buffer_, marker.payload_length)); RtpUtility::RtpHeaderParser rtp_parser(read_buffer_, marker.payload_length); if (rtp_parser.RTCP()) { @@ -294,7 +422,7 @@ class PcapFileReaderImpl : public RtpPacketSourceInterface { // the header as such and will likely fail reading the IP header if this is // something else than null/loopback. uint32_t protocol; - TRY(Read(&protocol, true)); + TRY_PCAP(Read(&protocol, true)); if (protocol == kBsdNullLoopback1 || protocol == kBsdNullLoopback2) { int result = ReadXxpIpHeader(marker); DEBUG_LOG("Recognized loopback frame"); @@ -303,12 +431,12 @@ class PcapFileReaderImpl : public RtpPacketSourceInterface { } } - TRY(fseek(file_, file_pos, SEEK_SET)); + TRY_PCAP(fseek(file_, file_pos, SEEK_SET)); // Check for Ethernet II, IP frame header. uint16_t type; - TRY(Skip(kEthernetIIHeaderMacSkip)); // Source+destination MAC. - TRY(Read(&type, true)); + TRY_PCAP(Skip(kEthernetIIHeaderMacSkip)); // Source+destination MAC. + TRY_PCAP(Read(&type, true)); if (type == kEthertypeIp) { int result = ReadXxpIpHeader(marker); DEBUG_LOG("Recognized ethernet 2 frame"); @@ -341,14 +469,14 @@ class PcapFileReaderImpl : public RtpPacketSourceInterface { uint16_t fragment; uint16_t protocol; uint16_t checksum; - TRY(Read(&version, true)); - TRY(Read(&length, true)); - TRY(Read(&id, true)); - TRY(Read(&fragment, true)); - TRY(Read(&protocol, true)); - TRY(Read(&checksum, true)); - TRY(Read(&marker->source_ip, true)); - TRY(Read(&marker->dest_ip, true)); + TRY_PCAP(Read(&version, true)); + TRY_PCAP(Read(&length, true)); + TRY_PCAP(Read(&id, true)); + TRY_PCAP(Read(&fragment, true)); + TRY_PCAP(Read(&protocol, true)); + TRY_PCAP(Read(&checksum, true)); + TRY_PCAP(Read(&marker->source_ip, true)); + TRY_PCAP(Read(&marker->dest_ip, true)); if (((version >> 12) & 0x000f) != kIpVersion4) { DEBUG_LOG("IP header is not IPv4"); @@ -364,7 +492,7 @@ class PcapFileReaderImpl : public RtpPacketSourceInterface { // Skip remaining fields of IP header. uint16_t header_length = (version & 0x0f00) >> (8 - 2); assert(header_length >= kMinIpHeaderLength); - TRY(Skip(header_length - kMinIpHeaderLength)); + TRY_PCAP(Skip(header_length - kMinIpHeaderLength)); protocol = protocol & 0x00ff; if (protocol == kProtocolTcp) { @@ -373,10 +501,10 @@ class PcapFileReaderImpl : public RtpPacketSourceInterface { } else if (protocol == kProtocolUdp) { uint16_t length; uint16_t checksum; - TRY(Read(&marker->source_port, true)); - TRY(Read(&marker->dest_port, true)); - TRY(Read(&length, true)); - TRY(Read(&checksum, true)); + TRY_PCAP(Read(&marker->source_port, true)); + TRY_PCAP(Read(&marker->dest_port, true)); + TRY_PCAP(Read(&length, true)); + TRY_PCAP(Read(&checksum, true)); marker->payload_length = length - kUdpHeaderLength; } else { DEBUG_LOG("Unknown transport (expected UDP or TCP)"); @@ -443,22 +571,33 @@ class PcapFileReaderImpl : public RtpPacketSourceInterface { FILE* file_; bool swap_pcap_byte_order_; - bool swap_network_byte_order_; + const bool swap_network_byte_order_; uint8_t read_buffer_[kMaxReadBufferSize]; SsrcMap packets_by_ssrc_; std::vector packets_; PacketIterator next_packet_it_; - DISALLOW_COPY_AND_ASSIGN(PcapFileReaderImpl); + DISALLOW_COPY_AND_ASSIGN(PcapReader); }; -RtpPacketSourceInterface* CreatePcapFileReader(const std::string& filename) { - scoped_ptr impl(new PcapFileReaderImpl()); - if (impl->Initialize(filename) != 0) { +RtpFileReader* RtpFileReader::Create(FileFormat format, + const std::string& filename) { + RtpFileReaderImpl* reader = NULL; + switch (format) { + case kPcap: + reader = new PcapReader(); + break; + case kRtpDump: + reader = new RtpDumpReader(); + break; + } + if (!reader->Init(filename)) { + delete reader; return NULL; } - return impl.release(); + return reader; } -} // namespace rtpplayer + +} // namespace test } // namespace webrtc diff --git a/webrtc/test/rtp_file_reader.h b/webrtc/test/rtp_file_reader.h new file mode 100644 index 0000000000..379bf2d7a5 --- /dev/null +++ b/webrtc/test/rtp_file_reader.h @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2014 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. + */ +#ifndef WEBRTC_TEST_RTP_FILE_READER_H_ +#define WEBRTC_TEST_RTP_FILE_READER_H_ + +#include + +#include "webrtc/common_types.h" + +namespace webrtc { +namespace test { +class RtpFileReader { + public: + enum FileFormat { + kPcap, + kRtpDump, + }; + + struct Packet { + static const size_t kMaxPacketBufferSize = 1500; + uint8_t data[kMaxPacketBufferSize]; + size_t length; + + uint32_t time_ms; + }; + + virtual ~RtpFileReader() {} + static RtpFileReader* Create(FileFormat format, + const std::string& filename); + + virtual bool NextPacket(Packet* packet) = 0; +}; +} // namespace test +} // namespace webrtc +#endif // WEBRTC_TEST_RTP_FILE_READER_H_ diff --git a/webrtc/modules/video_coding/main/test/pcap_file_reader_unittest.cc b/webrtc/test/rtp_file_reader_unittest.cc similarity index 63% rename from webrtc/modules/video_coding/main/test/pcap_file_reader_unittest.cc rename to webrtc/test/rtp_file_reader_unittest.cc index c6f1d511c1..b5fa260c7d 100644 --- a/webrtc/modules/video_coding/main/test/pcap_file_reader_unittest.cc +++ b/webrtc/test/rtp_file_reader_unittest.cc @@ -12,13 +12,38 @@ #include "testing/gtest/include/gtest/gtest.h" #include "webrtc/modules/rtp_rtcp/source/rtp_utility.h" -#include "webrtc/modules/video_coding/main/test/pcap_file_reader.h" -#include "webrtc/modules/video_coding/main/test/rtp_player.h" #include "webrtc/system_wrappers/interface/scoped_ptr.h" +#include "webrtc/test/rtp_file_reader.h" #include "webrtc/test/testsupport/fileutils.h" namespace webrtc { -namespace rtpplayer { + +class TestRtpFileReader : public ::testing::Test { + public: + void Init(const std::string& filename) { + std::string filepath = + test::ResourcePath("video_coding/" + filename, "rtp"); + rtp_packet_source_.reset( + test::RtpFileReader::Create(test::RtpFileReader::kRtpDump, filepath)); + ASSERT_TRUE(rtp_packet_source_.get() != NULL); + } + + int CountRtpPackets() { + test::RtpFileReader::Packet packet; + int c = 0; + while (rtp_packet_source_->NextPacket(&packet)) + c++; + return c; + } + + private: + scoped_ptr rtp_packet_source_; +}; + +TEST_F(TestRtpFileReader, Test60Packets) { + Init("pltype103"); + EXPECT_EQ(60, CountRtpPackets()); +} typedef std::map PacketsPerSsrc; @@ -27,35 +52,24 @@ class TestPcapFileReader : public ::testing::Test { void Init(const std::string& filename) { std::string filepath = test::ResourcePath("video_coding/" + filename, "pcap"); - rtp_packet_source_.reset(CreatePcapFileReader(filepath)); + rtp_packet_source_.reset( + test::RtpFileReader::Create(test::RtpFileReader::kPcap, filepath)); ASSERT_TRUE(rtp_packet_source_.get() != NULL); } int CountRtpPackets() { - const uint32_t kBufferSize = 4096; - uint8_t data[kBufferSize]; - uint32_t length = kBufferSize; - uint32_t dummy_time_ms = 0; int c = 0; - while (rtp_packet_source_->NextPacket(data, &length, &dummy_time_ms) == 0) { - EXPECT_GE(kBufferSize, length); - length = kBufferSize; + test::RtpFileReader::Packet packet; + while (rtp_packet_source_->NextPacket(&packet)) c++; - } return c; } PacketsPerSsrc CountRtpPacketsPerSsrc() { - const uint32_t kBufferSize = 4096; - uint8_t data[kBufferSize]; - uint32_t length = kBufferSize; - uint32_t dummy_time_ms = 0; PacketsPerSsrc pps; - while (rtp_packet_source_->NextPacket(data, &length, &dummy_time_ms) == 0) { - EXPECT_GE(kBufferSize, length); - length = kBufferSize; - - RtpUtility::RtpHeaderParser rtp_header_parser(data, length); + test::RtpFileReader::Packet packet; + while (rtp_packet_source_->NextPacket(&packet)) { + RtpUtility::RtpHeaderParser rtp_header_parser(packet.data, packet.length); webrtc::RTPHeader header; if (!rtp_header_parser.RTCP() && rtp_header_parser.Parse(header, NULL)) { pps[header.ssrc]++; @@ -65,7 +79,7 @@ class TestPcapFileReader : public ::testing::Test { } private: - scoped_ptr rtp_packet_source_; + scoped_ptr rtp_packet_source_; }; TEST_F(TestPcapFileReader, TestEthernetIIFrame) { @@ -94,6 +108,4 @@ TEST_F(TestPcapFileReader, TestThreeSsrc) { EXPECT_EQ(113, pps[0x59fe6ef0]); EXPECT_EQ(61, pps[0xed2bd2ac]); } - -} // namespace rtpplayer } // namespace webrtc diff --git a/webrtc/test/webrtc_test_common.gyp b/webrtc/test/webrtc_test_common.gyp index 8c8774fba2..be6b303f87 100644 --- a/webrtc/test/webrtc_test_common.gyp +++ b/webrtc/test/webrtc_test_common.gyp @@ -35,6 +35,8 @@ 'mock_transport.h', 'null_transport.cc', 'null_transport.h', + 'rtp_file_reader.cc', + 'rtp_file_reader.h', 'rtp_rtcp_observer.h', 'run_loop.cc', 'run_loop.h', @@ -184,6 +186,7 @@ ], 'sources': [ 'fake_network_pipe_unittest.cc', + 'rtp_file_reader_unittest.cc', ], }, ], #targets diff --git a/webrtc/video/replay.cc b/webrtc/video/replay.cc new file mode 100644 index 0000000000..75390259a5 --- /dev/null +++ b/webrtc/video/replay.cc @@ -0,0 +1,266 @@ +/* + * Copyright (c) 2014 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 "testing/gtest/include/gtest/gtest.h" + +#include "webrtc/call.h" +#include "webrtc/common_video/libyuv/include/webrtc_libyuv.h" +#include "webrtc/modules/rtp_rtcp/interface/rtp_header_parser.h" +#include "webrtc/system_wrappers/interface/clock.h" +#include "webrtc/system_wrappers/interface/scoped_ptr.h" +#include "webrtc/system_wrappers/interface/sleep.h" +#include "webrtc/test/encoder_settings.h" +#include "webrtc/test/null_transport.h" +#include "webrtc/test/rtp_file_reader.h" +#include "webrtc/test/run_loop.h" +#include "webrtc/test/run_test.h" +#include "webrtc/test/video_capturer.h" +#include "webrtc/test/video_renderer.h" +#include "webrtc/typedefs.h" + +namespace webrtc { +namespace flags { + +// TODO(pbos): Multiple receivers. + +// Flag for payload type. +static bool ValidatePayloadType(const char* flagname, int32_t payload_type) { + return payload_type > 0 && payload_type <= 127; +} +DEFINE_int32(payload_type, 0, "Payload type."); +static int PayloadType() { return static_cast(FLAGS_payload_type); } +static const bool payload_dummy = + google::RegisterFlagValidator(&FLAGS_payload_type, &ValidatePayloadType); + +// Flag for SSRC. +static bool ValidateSsrc(const char* flagname, uint64_t ssrc) { + return ssrc > 0 && ssrc <= 0xFFFFFFFFu; +} + +DEFINE_uint64(ssrc, 0, "Incoming SSRC."); +static uint32_t Ssrc() { return static_cast(FLAGS_ssrc); } +static const bool ssrc_dummy = + google::RegisterFlagValidator(&FLAGS_ssrc, &ValidateSsrc); + +static bool ValidateOptionalPayloadType(const char* flagname, + int32_t payload_type) { + return payload_type == -1 || ValidatePayloadType(flagname, payload_type); +} + +// Flag for RED payload type. +DEFINE_int32(red_payload_type, -1, "RED payload type."); +static int RedPayloadType() { + return static_cast(FLAGS_red_payload_type); +} +static const bool red_dummy = + google::RegisterFlagValidator(&FLAGS_red_payload_type, + &ValidateOptionalPayloadType); + +// Flag for ULPFEC payload type. +DEFINE_int32(fec_payload_type, -1, "ULPFEC payload type."); +static int FecPayloadType() { + return static_cast(FLAGS_fec_payload_type); +} +static const bool fec_dummy = + google::RegisterFlagValidator(&FLAGS_fec_payload_type, + &ValidateOptionalPayloadType); + +// Flag for abs-send-time id. +static bool ValidateRtpHeaderExtensionId(const char* flagname, + int32_t extension_id) { + return extension_id >= -1 || extension_id < 15; +} +DEFINE_int32(abs_send_time_id, -1, "RTP extension ID for abs-send-time."); +static int AbsSendTimeId() { return static_cast(FLAGS_abs_send_time_id); } +static const bool abs_send_time_dummy = + google::RegisterFlagValidator(&FLAGS_abs_send_time_id, + &ValidateRtpHeaderExtensionId); + +// Flag for transmission-offset id. +DEFINE_int32(transmission_offset_id, + -1, + "RTP extension ID for transmission-offset."); +static int TransmissionOffsetId() { + return static_cast(FLAGS_transmission_offset_id); +} +static const bool timestamp_offset_dummy = + google::RegisterFlagValidator(&FLAGS_transmission_offset_id, + &ValidateRtpHeaderExtensionId); + +// Flag for rtpdump input file. +DEFINE_string(input_file, "", "rtpdump input file."); +static std::string InputFile() { + return static_cast(FLAGS_input_file); +} + +// Flag for raw output files. +DEFINE_string(out_base, "", "Basename (excluding .yuv) for raw output."); +static std::string OutBase() { + return static_cast(FLAGS_out_base); +} + +// Flag for video codec. +DEFINE_string(codec, "VP8", "Video codec."); +static std::string Codec() { return static_cast(FLAGS_codec); } + +} // namespace flags + +static const uint32_t kReceiverLocalSsrc = 0x123456; + +class FileRenderPassthrough : public VideoRenderer { + public: + FileRenderPassthrough(const std::string& basename, VideoRenderer* renderer) + : basename_(basename), + renderer_(renderer), + file_(NULL), + count_(0), + last_width_(0), + last_height_(0) {} + + ~FileRenderPassthrough() { + if (file_ != NULL) + fclose(file_); + } + + private: + virtual void RenderFrame(const I420VideoFrame& video_frame, + int time_to_render_ms) OVERRIDE { + if (renderer_ != NULL) + renderer_->RenderFrame(video_frame, time_to_render_ms); + if (basename_ == "") + return; + if (last_width_ != video_frame.width() || + last_height_ != video_frame.height()) { + if (file_ != NULL) + fclose(file_); + std::stringstream filename; + filename << basename_; + if (++count_ > 1) + filename << '-' << count_; + filename << '_' << video_frame.width() << 'x' << video_frame.height() + << ".yuv"; + file_ = fopen(filename.str().c_str(), "wb"); + if (file_ == NULL) { + fprintf(stderr, + "Couldn't open file for writing: %s\n", + filename.str().c_str()); + } + } + last_width_ = video_frame.width(); + last_height_ = video_frame.height(); + if (file_ == NULL) + return; + PrintI420VideoFrame(video_frame, file_); + } + + const std::string basename_; + VideoRenderer* const renderer_; + FILE* file_; + size_t count_; + int last_width_; + int last_height_; +}; + +void RtpReplay() { + scoped_ptr playback_video(test::VideoRenderer::Create( + "Playback Video", 640, 480)); + FileRenderPassthrough file_passthrough(flags::OutBase(), + playback_video.get()); + + // TODO(pbos): Might be good to have a transport that prints keyframe requests + // etc. + test::NullTransport transport; + Call::Config call_config(&transport); + scoped_ptr call(Call::Create(call_config)); + + VideoReceiveStream::Config receive_config; + receive_config.rtp.remote_ssrc = flags::Ssrc(); + receive_config.rtp.local_ssrc = kReceiverLocalSsrc; + receive_config.rtp.fec.ulpfec_payload_type = flags::FecPayloadType(); + receive_config.rtp.fec.red_payload_type = flags::RedPayloadType(); + receive_config.rtp.nack.rtp_history_ms = 1000; + if (flags::TransmissionOffsetId() != -1) { + receive_config.rtp.extensions.push_back( + RtpExtension(RtpExtension::kTOffset, flags::TransmissionOffsetId())); + } + if (flags::AbsSendTimeId() != -1) { + receive_config.rtp.extensions.push_back( + RtpExtension(RtpExtension::kAbsSendTime, flags::AbsSendTimeId())); + } + receive_config.renderer = &file_passthrough; + + VideoSendStream::Config::EncoderSettings encoder_settings; + encoder_settings.payload_name = flags::Codec(); + encoder_settings.payload_type = flags::PayloadType(); + VideoCodec codec = test::CreateDecoderVideoCodec(encoder_settings); + receive_config.codecs.push_back(codec); + + VideoReceiveStream* receive_stream = + call->CreateVideoReceiveStream(receive_config); + + scoped_ptr rtp_reader(test::RtpFileReader::Create( + test::RtpFileReader::kRtpDump, flags::InputFile())); + if (rtp_reader.get() == NULL) { + rtp_reader.reset(test::RtpFileReader::Create(test::RtpFileReader::kPcap, + flags::InputFile())); + if (rtp_reader.get() == NULL) { + fprintf(stderr, + "Couldn't open input file as either a rtpdump or .pcap. Note " + "that .pcapng is not supported.\n"); + return; + } + } + receive_stream->Start(); + + uint32_t last_time_ms = 0; + int num_packets = 0; + while (true) { + test::RtpFileReader::Packet packet; + if (!rtp_reader->NextPacket(&packet)) + break; + ++num_packets; + switch (call->Receiver()->DeliverPacket(packet.data, packet.length)) { + case PacketReceiver::DELIVERY_OK: + break; + case PacketReceiver::DELIVERY_UNKNOWN_SSRC: { + RTPHeader header; + scoped_ptr parser(RtpHeaderParser::Create()); + parser->Parse(packet.data, packet.length, &header); + fprintf(stderr, "Unknown SSRC: %u!\n", header.ssrc); + break; + } + case PacketReceiver::DELIVERY_PACKET_ERROR: + fprintf(stderr, "Packet error, corrupt packets or incorrect setup?\n"); + break; + } + if (last_time_ms != 0 && last_time_ms != packet.time_ms) { + SleepMs(packet.time_ms - last_time_ms); + } + last_time_ms = packet.time_ms; + } + fprintf(stderr, "num_packets: %d\n", num_packets); + + call->DestroyVideoReceiveStream(receive_stream); +} +} // namespace webrtc + +int main(int argc, char* argv[]) { + ::testing::InitGoogleTest(&argc, argv); + google::ParseCommandLineFlags(&argc, &argv, true); + + webrtc::test::RunTest(webrtc::RtpReplay); + return 0; +} diff --git a/webrtc/webrtc_tests.gypi b/webrtc/webrtc_tests.gypi index 78b199f09d..ace6684386 100644 --- a/webrtc/webrtc_tests.gypi +++ b/webrtc/webrtc_tests.gypi @@ -13,6 +13,7 @@ 'dependencies': [ 'video_engine_tests', 'video_loopback', + 'video_replay', 'webrtc_perf_tests', ], }, @@ -41,6 +42,31 @@ 'webrtc', ], }, + { + 'target_name': 'video_replay', + 'type': 'executable', + 'sources': [ + 'test/mac/run_test.mm', + 'test/run_test.cc', + 'test/run_test.h', + 'video/replay.cc', + ], + 'conditions': [ + ['OS=="mac"', { + 'sources!': [ + 'test/run_test.cc', + ], + }], + ], + 'dependencies': [ + '<(DEPTH)/testing/gtest.gyp:gtest', + '<(DEPTH)/third_party/gflags/gflags.gyp:gflags', + 'system_wrappers/source/system_wrappers.gyp:field_trial_default', + 'test/webrtc_test_common.gyp:webrtc_test_common', + 'test/webrtc_test_common.gyp:webrtc_test_renderer', + 'webrtc', + ], + }, { 'target_name': 'video_engine_tests', 'type': '<(gtest_target_type)',