From 56b5f77a2b389478a6254013c1d7f8dbca6b2432 Mon Sep 17 00:00:00 2001 From: "solenberg@webrtc.org" Date: Tue, 16 Apr 2013 10:31:56 +0000 Subject: [PATCH] Add support for multiple streams to RtpPlayer: - Tests video_rtp_play.cc, video_rtp_play_mt.cc, decode_from_storage.cc rewritten - rtp_player.cc/.h rewritten; added interfaces for externally setting up sinks - Support for reading .rtp files pulled out into rtp_file_reader namespace - Added support for reading .pcap (libpcap/wireshark/tcpdump) files, see pcap_file_reader BUG= TEST=trybots Review URL: https://webrtc-codereview.appspot.com/1201009 git-svn-id: http://webrtc.googlecode.com/svn/trunk@3856 4adac7df-926f-26a2-2b94-8c16560cd09d --- DEPS | 2 +- .../main/interface/video_coding_defines.h | 2 +- .../main/source/video_coding_test.gypi | 11 + .../main/test/decode_from_storage_test.cc | 211 ++--- .../main/test/generic_codec_test.cc | 1 + .../video_coding/main/test/media_opt_test.cc | 1 + .../video_coding/main/test/mt_rx_tx_test.cc | 1 + .../video_coding/main/test/normal_test.cc | 3 +- .../video_coding/main/test/normal_test.h | 1 + .../main/test/pcap_file_reader.cc | 463 +++++++++++ .../video_coding/main/test/pcap_file_reader.h | 26 + .../main/test/pcap_file_reader_unittest.cc | 99 +++ .../main/test/quality_modes_test.cc | 1 + .../video_coding/main/test/receiver_tests.h | 73 +- .../main/test/receiver_timing_tests.cc | 1 + .../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 | 786 +++++++++--------- .../video_coding/main/test/rtp_player.h | 168 ++-- .../video_coding/main/test/test_util.cc | 159 +++- .../video_coding/main/test/test_util.h | 80 +- .../main/test/vcm_payload_sink_factory.cc | 249 ++++++ .../main/test/vcm_payload_sink_factory.h | 66 ++ .../video_coding/main/test/video_rtp_play.cc | 276 ++---- .../main/test/video_rtp_play_mt.cc | 324 +++----- 26 files changed, 2079 insertions(+), 1171 deletions(-) create mode 100644 webrtc/modules/video_coding/main/test/pcap_file_reader.cc create mode 100644 webrtc/modules/video_coding/main/test/pcap_file_reader.h create mode 100644 webrtc/modules/video_coding/main/test/pcap_file_reader_unittest.cc create mode 100644 webrtc/modules/video_coding/main/test/rtp_file_reader.cc create mode 100644 webrtc/modules/video_coding/main/test/rtp_file_reader.h create mode 100644 webrtc/modules/video_coding/main/test/rtp_file_reader_unittest.cc create mode 100644 webrtc/modules/video_coding/main/test/vcm_payload_sink_factory.cc create mode 100644 webrtc/modules/video_coding/main/test/vcm_payload_sink_factory.h diff --git a/DEPS b/DEPS index 0f6971e801..6c77a16d1a 100644 --- a/DEPS +++ b/DEPS @@ -14,7 +14,7 @@ vars = { # External resources like video and audio files used for testing purposes. # Downloaded on demand when needed. - "webrtc_resources_revision": "15", + "webrtc_resources_revision": "16", } # NOTE: Prefer revision numbers to tags for svn deps. Use http rather than diff --git a/webrtc/modules/video_coding/main/interface/video_coding_defines.h b/webrtc/modules/video_coding/main/interface/video_coding_defines.h index 9edd00b05a..fcee34b585 100644 --- a/webrtc/modules/video_coding/main/interface/video_coding_defines.h +++ b/webrtc/modules/video_coding/main/interface/video_coding_defines.h @@ -38,7 +38,7 @@ namespace webrtc { #define VCM_RED_PAYLOAD_TYPE 96 #define VCM_ULPFEC_PAYLOAD_TYPE 97 -#define VCM_VP8_PAYLOAD_TYPE 120 +#define VCM_VP8_PAYLOAD_TYPE 100 #define VCM_I420_PAYLOAD_TYPE 124 enum VCMVideoProtection { 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 c00d56c701..032b3d147b 100644 --- a/webrtc/modules/video_coding/main/source/video_coding_test.gypi +++ b/webrtc/modules/video_coding/main/source/video_coding_test.gypi @@ -37,12 +37,15 @@ '../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', + '../test/vcm_payload_sink_factory.h', '../test/video_source.h', # sources @@ -54,12 +57,15 @@ '../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/receiver_timing_tests.cc', + '../test/rtp_file_reader.cc', '../test/rtp_player.cc', '../test/test_callbacks.cc', '../test/test_util.cc', '../test/tester_main.cc', + '../test/vcm_payload_sink_factory.cc', '../test/video_rtp_play_mt.cc', '../test/video_rtp_play.cc', '../test/video_source.cc', @@ -83,6 +89,7 @@ 'target_name': 'video_coding_unittests', 'type': 'executable', 'dependencies': [ + 'rtp_rtcp', 'video_codecs_test_framework', 'webrtc_video_coding', '<(webrtc_root)/test/test.gyp:test_support_main', @@ -102,6 +109,10 @@ 'video_coding_robustness_unittest.cc', 'video_coding_impl_unittest.cc', 'qm_select_unittest.cc', + '../test/pcap_file_reader.cc', + '../test/pcap_file_reader_unittest.cc', + '../test/rtp_file_reader.cc', + '../test/rtp_file_reader_unittest.cc', '../../codecs/test/packet_manipulator_unittest.cc', '../../codecs/test/stats_unittest.cc', '../../codecs/test/videoprocessor_unittest.cc', diff --git a/webrtc/modules/video_coding/main/test/decode_from_storage_test.cc b/webrtc/modules/video_coding/main/test/decode_from_storage_test.cc index 969a4a4d5d..3a32431b3f 100644 --- a/webrtc/modules/video_coding/main/test/decode_from_storage_test.cc +++ b/webrtc/modules/video_coding/main/test/decode_from_storage_test.cc @@ -8,167 +8,76 @@ * be found in the AUTHORS file in the root of the source tree. */ -#include "webrtc/modules/rtp_rtcp/interface/rtp_rtcp.h" -#include "webrtc/modules/video_coding/main/interface/video_coding.h" #include "webrtc/modules/video_coding/main/test/receiver_tests.h" -#include "webrtc/modules/video_coding/main/test/rtp_player.h" -#include "webrtc/system_wrappers/interface/clock.h" +#include "webrtc/modules/video_coding/main/test/vcm_payload_sink_factory.h" #include "webrtc/system_wrappers/interface/trace.h" +#include "webrtc/test/testsupport/fileutils.h" -using namespace webrtc; +namespace { -class FrameStorageCallback : public VCMFrameStorageCallback -{ -public: - FrameStorageCallback(VideoCodingModule* vcm) : _vcm(vcm) {} +const bool kConfigProtectionEnabled = false; +const webrtc::VCMVideoProtection kConfigProtectionMethod = + webrtc::kProtectionNack; +const float kConfigLossRate = 0.0f; +const bool kConfigReordering = false; +const uint32_t kConfigRttMs = 100; +const uint32_t kConfigRenderDelayMs = 0; +const uint32_t kConfigMinPlayoutDelayMs = 0; +const int64_t kConfigMaxRuntimeMs = -1; - int32_t StoreReceivedFrame(const EncodedVideoData& frameToStore) - { - _vcm->DecodeFromStorage(frameToStore); - return VCM_OK; +} // namespace + +int DecodeFromStorageTest(const CmdArgs& args) { + std::string trace_file = webrtc::test::OutputPath() + + "decodeFromStorageTestTrace.txt"; + webrtc::Trace::CreateTrace(); + webrtc::Trace::SetTraceFile(trace_file.c_str()); + webrtc::Trace::SetLevelFilter(webrtc::kTraceAll); + + webrtc::rtpplayer::PayloadTypes payload_types; + payload_types.push_back(webrtc::rtpplayer::PayloadCodecTuple( + VCM_VP8_PAYLOAD_TYPE, "VP8", webrtc::kVideoCodecVP8)); + + std::string output_file = args.outputFile; + if (output_file == "") { + output_file = webrtc::test::OutputPath() + "DecodeFromStorage.yuv"; + } + + webrtc::SimulatedClock clock(0); + webrtc::rtpplayer::VcmPayloadSinkFactory factory(output_file, &clock, + kConfigProtectionEnabled, kConfigProtectionMethod, kConfigRttMs, + kConfigRenderDelayMs, kConfigMinPlayoutDelayMs, true); + webrtc::scoped_ptr rtp_player( + webrtc::rtpplayer::Create(args.inputFile, &factory, &clock, payload_types, + kConfigLossRate, kConfigRttMs, kConfigReordering)); + if (rtp_player.get() == NULL) { + return -1; + } + + int ret = 0; + while ((ret = rtp_player->NextPacket(clock.TimeInMilliseconds())) == 0) { + ret = factory.DecodeAndProcessAll(false); + if (ret < 0 || (kConfigMaxRuntimeMs > -1 && + clock.TimeInMilliseconds() >= kConfigMaxRuntimeMs)) { + break; } + clock.AdvanceTimeMilliseconds(1); + } -private: - VideoCodingModule* _vcm; -}; + rtp_player->Print(); -int DecodeFromStorageTest(CmdArgs& args) -{ - // BEGIN Settings - bool protectionEnabled = false; - VCMVideoProtection protectionMethod = kProtectionNack; - uint32_t rttMS = 100; - float lossRate = 0.00f; - bool reordering = false; - uint32_t renderDelayMs = 0; - uint32_t minPlayoutDelayMs = 0; - const int64_t MAX_RUNTIME_MS = -1; - std::string rtpFilename = args.inputFile; - std::string outFilename = args.outputFile; - if (outFilename == "") - outFilename = test::OutputPath() + "DecodeFromStorage.yuv"; - - FrameReceiveCallback receiveCallback(outFilename.c_str()); - - // END Settings - - Trace::CreateTrace(); - Trace::SetTraceFile( - (test::OutputPath() + "decodeFromStorageTestTrace.txt").c_str()); - Trace::SetLevelFilter(webrtc::kTraceAll); - - - SimulatedClock clock(0); - NullEventFactory event_factory; - // TODO(hlundin): This test was not verified after changing to FakeTickTime. - VideoCodingModule* vcm = VideoCodingModule::Create(1, &clock, - &event_factory); - VideoCodingModule* vcmPlayback = VideoCodingModule::Create(2, &clock, - &event_factory); - FrameStorageCallback storageCallback(vcmPlayback); - RtpDataCallback dataCallback(vcm); - int32_t ret = vcm->InitializeReceiver(); - if (ret < 0) - { - return -1; - } - ret = vcmPlayback->InitializeReceiver(); - if (ret < 0) - { - return -1; - } - vcm->RegisterFrameStorageCallback(&storageCallback); - vcmPlayback->RegisterReceiveCallback(&receiveCallback); - RTPPlayer rtpStream(rtpFilename.c_str(), &dataCallback, &clock); - PayloadTypeList payloadTypes; - payloadTypes.push_front(new PayloadCodecTuple(VCM_VP8_PAYLOAD_TYPE, "VP8", - kVideoCodecVP8)); - - // Register receive codecs in VCM - for (PayloadTypeList::iterator it = payloadTypes.begin(); - it != payloadTypes.end(); ++it) { - PayloadCodecTuple* payloadType = *it; - if (payloadType != NULL) - { - VideoCodec codec; - memset(&codec, 0, sizeof(codec)); - strncpy(codec.plName, payloadType->name.c_str(), payloadType->name.length()); - codec.plName[payloadType->name.length()] = '\0'; - codec.plType = payloadType->payloadType; - codec.codecType = payloadType->codecType; - if (vcm->RegisterReceiveCodec(&codec, 1) < 0) - { - return -1; - } - if (vcmPlayback->RegisterReceiveCodec(&codec, 1) < 0) - { - return -1; - } - } - } - if (rtpStream.Initialize(&payloadTypes) < 0) - { - return -1; - } - bool nackEnabled = protectionEnabled && (protectionMethod == kProtectionNack || - protectionMethod == kProtectionDualDecoder); - rtpStream.SimulatePacketLoss(lossRate, nackEnabled, rttMS); - rtpStream.SetReordering(reordering); - vcm->SetChannelParameters(0, 0, rttMS); - vcm->SetVideoProtection(protectionMethod, protectionEnabled); - vcm->SetRenderDelay(renderDelayMs); - vcm->SetMinimumPlayoutDelay(minPlayoutDelayMs); - - ret = 0; - - // RTP stream main loop - while ((ret = rtpStream.NextPacket(clock.TimeInMilliseconds())) == 0) - { - if (clock.TimeInMilliseconds() % 5 == 0) - { - ret = vcm->Decode(); - if (ret < 0) - { - return -1; - } - } - if (vcm->TimeUntilNextProcess() <= 0) - { - vcm->Process(); - } - if (MAX_RUNTIME_MS > -1 && clock.TimeInMilliseconds() >= MAX_RUNTIME_MS) - { - break; - } - clock.AdvanceTimeMilliseconds(1); - } - - switch (ret) - { + switch (ret) { case 1: - printf("Success\n"); - break; + printf("Success\n"); + break; case -1: - printf("Failed\n"); - break; + printf("Failed\n"); + break; case 0: - printf("Timeout\n"); - break; - } + printf("Timeout\n"); + break; + } - rtpStream.Print(); - - // Tear down - while (!payloadTypes.empty()) - { - delete payloadTypes.front(); - payloadTypes.pop_front(); - } - VideoCodingModule::Destroy(vcm); - vcm = NULL; - VideoCodingModule::Destroy(vcmPlayback); - vcmPlayback = NULL; - Trace::ReturnTrace(); - - return 0; + webrtc::Trace::ReturnTrace(); + return 0; } diff --git a/webrtc/modules/video_coding/main/test/generic_codec_test.cc b/webrtc/modules/video_coding/main/test/generic_codec_test.cc index 88ae055649..c03d896faf 100644 --- a/webrtc/modules/video_coding/main/test/generic_codec_test.cc +++ b/webrtc/modules/video_coding/main/test/generic_codec_test.cc @@ -18,6 +18,7 @@ #include "webrtc/modules/video_coding/main/interface/video_coding.h" #include "webrtc/modules/video_coding/main/test/test_macros.h" #include "webrtc/system_wrappers/interface/clock.h" +#include "webrtc/test/testsupport/fileutils.h" using namespace webrtc; diff --git a/webrtc/modules/video_coding/main/test/media_opt_test.cc b/webrtc/modules/video_coding/main/test/media_opt_test.cc index 493791b12a..04da2e8cb8 100644 --- a/webrtc/modules/video_coding/main/test/media_opt_test.cc +++ b/webrtc/modules/video_coding/main/test/media_opt_test.cc @@ -21,6 +21,7 @@ #include "webrtc/modules/video_coding/main/interface/video_coding.h" #include "webrtc/modules/video_coding/main/test/test_macros.h" #include "webrtc/modules/video_coding/main/test/test_util.h" +#include "webrtc/test/testsupport/fileutils.h" #include "webrtc/test/testsupport/metrics/video_metrics.h" using namespace webrtc; diff --git a/webrtc/modules/video_coding/main/test/mt_rx_tx_test.cc b/webrtc/modules/video_coding/main/test/mt_rx_tx_test.cc index c7cd95f5ff..59b4454aa5 100644 --- a/webrtc/modules/video_coding/main/test/mt_rx_tx_test.cc +++ b/webrtc/modules/video_coding/main/test/mt_rx_tx_test.cc @@ -24,6 +24,7 @@ #include "webrtc/modules/video_coding/main/test/test_macros.h" #include "webrtc/modules/video_coding/main/test/test_util.h" #include "webrtc/system_wrappers/interface/thread_wrapper.h" +#include "webrtc/test/testsupport/fileutils.h" using namespace webrtc; diff --git a/webrtc/modules/video_coding/main/test/normal_test.cc b/webrtc/modules/video_coding/main/test/normal_test.cc index 9d5dedf808..04efa454cf 100644 --- a/webrtc/modules/video_coding/main/test/normal_test.cc +++ b/webrtc/modules/video_coding/main/test/normal_test.cc @@ -21,9 +21,10 @@ #include "webrtc/modules/video_coding/main/test/test_callbacks.h" #include "webrtc/modules/video_coding/main/test/test_macros.h" #include "webrtc/modules/video_coding/main/test/test_util.h" -#include "webrtc/test/testsupport/metrics/video_metrics.h" #include "webrtc/system_wrappers/interface/clock.h" #include "webrtc/system_wrappers/interface/trace.h" +#include "webrtc/test/testsupport/fileutils.h" +#include "webrtc/test/testsupport/metrics/video_metrics.h" using namespace webrtc; diff --git a/webrtc/modules/video_coding/main/test/normal_test.h b/webrtc/modules/video_coding/main/test/normal_test.h index 8fc0e1f352..9d2bcd29fe 100644 --- a/webrtc/modules/video_coding/main/test/normal_test.h +++ b/webrtc/modules/video_coding/main/test/normal_test.h @@ -14,6 +14,7 @@ #include "video_coding.h" #include "test_util.h" +#include #include class NormalTest; diff --git a/webrtc/modules/video_coding/main/test/pcap_file_reader.cc b/webrtc/modules/video_coding/main/test/pcap_file_reader.cc new file mode 100644 index 0000000000..bf46bd4be2 --- /dev/null +++ b/webrtc/modules/video_coding/main/test/pcap_file_reader.cc @@ -0,0 +1,463 @@ +/* + * 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/pcap_file_reader.h" + +#ifdef WIN32 +#include +#include +#else +#include +#endif +#include +#include +#include +#include +#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 { + +enum { + kResultFail = -1, + kResultSuccess = 0, + kResultSkip = 1, + + kPcapVersionMajor = 2, + kPcapVersionMinor = 4, + kLinktypeNull = 0, + kLinktypeEthernet = 1, + kBsdNullLoopback1 = 0x00000002, + kBsdNullLoopback2 = 0x02000000, + kEthernetIIHeaderMacSkip = 12, + kEthertypeIp = 0x0800, + kIpVersion4 = 4, + kMinIpHeaderLength = 20, + kFragmentOffsetClear = 0x0000, + kFragmentOffsetDoNotFragment = 0x4000, + kProtocolTcp = 0x06, + kProtocolUdp = 0x11, + kUdpHeaderLength = 8, + kMaxReadBufferSize = 4096 +}; + +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; \ + } \ + } while (0) + +// Read RTP packets from file in tcpdump/libpcap format, as documented at: +// http://wiki.wireshark.org/Development/LibpcapFileFormat +class PcapFileReaderImpl : public RtpPacketSourceInterface { + public: + PcapFileReaderImpl() + : file_(NULL), + swap_pcap_byte_order_(false), + swap_network_byte_order_(false), + read_buffer_(), + packets_by_ssrc_(), + packets_(), + next_packet_it_() { + int16_t test = 0x7f00; + if (test != htons(test)) { + swap_network_byte_order_ = true; + } + } + + virtual ~PcapFileReaderImpl() { + 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; + } + + if (ReadGlobalHeader() < 0) { + return kResultFail; + } + + int total_packet_count = 0; + uint32_t stream_start_ms = 0; + int32_t next_packet_pos = ftell(file_); + for (;;) { + ++total_packet_count; + TRY(fseek(file_, next_packet_pos, SEEK_SET)); + int result = ReadPacket(&next_packet_pos, stream_start_ms, + total_packet_count); + if (result == kResultFail) { + break; + } else if (result == kResultSuccess && packets_.size() == 1) { + assert(stream_start_ms == 0); + PacketIterator it = packets_.begin(); + stream_start_ms = it->time_offset_ms; + it->time_offset_ms = 0; + } + } + + if (feof(file_) == 0) { + printf("Failed reading file!\n"); + return kResultFail; + } + + printf("Total packets in file: %d\n", total_packet_count); + printf("Total RTP/RTCP packets: %d\n", static_cast(packets_.size())); + + for (SsrcMapIterator mit = packets_by_ssrc_.begin(); + mit != packets_by_ssrc_.end(); ++mit) { + uint32_t ssrc = mit->first; + const std::vector& packet_numbers = mit->second; + printf("SSRC: %08x, %d packets\n", ssrc, + static_cast(packet_numbers.size())); + } + + // TODO(solenberg): Better validation of identified SSRC streams. + // + // Since we're dealing with raw network data here, we will wrongly identify + // some packets as RTP. When these packets are consumed by RtpPlayer, they + // are unlikely to cause issues as they will ultimately be filtered out by + // the RtpRtcp module. However, we should really do better filtering here, + // which we can accomplish in a number of ways, e.g.: + // + // - Verify that the time stamps and sequence numbers for RTP packets are + // both increasing/decreasing. If they move in different directions, the + // SSRC is likely bogus and can be dropped. (Normally they should be inc- + // reasing but we must allow packet reordering). + // - If RTP sequence number is not changing, drop the stream. + // - Can also use srcip:port->dstip:port pairs, assuming few SSRC collisions + // for up/down streams. + + next_packet_it_ = packets_.begin(); + return kResultSuccess; + } + + virtual int NextPacket(uint8_t* data, uint32_t* length, uint32_t* time_ms) { + assert(data); + assert(length); + assert(time_ms); + + if (next_packet_it_ == packets_.end()) { + return -1; + } + 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)); + *length = next_packet_it_->payload_length; + *time_ms = next_packet_it_->time_offset_ms; + next_packet_it_++; + + return 0; + } + + private: + // A marker of an RTP packet within the file. + struct RtpPacketMarker { + uint32_t packet_number; + uint32_t time_offset_ms; + uint32_t source_ip; + uint32_t dest_ip; + uint16_t source_port; + uint16_t dest_port; + WebRtcRTPHeader rtp_header; + int32_t pos_in_file; + uint32_t payload_length; + }; + + typedef std::vector::iterator PacketIterator; + typedef std::map > SsrcMap; + typedef std::map >::iterator SsrcMapIterator; + + int ReadGlobalHeader() { + uint32_t magic; + TRY(Read(&magic, false)); + if (magic == kPcapBOMSwapOrder) { + swap_pcap_byte_order_ = true; + } else if (magic == kPcapBOMNoSwapOrder) { + swap_pcap_byte_order_ = false; + } else { + return kResultFail; + } + + uint16_t version_major; + uint16_t version_minor; + TRY(Read(&version_major, false)); + TRY(Read(&version_minor, false)); + if (version_major != kPcapVersionMajor || + version_minor != kPcapVersionMinor) { + return kResultFail; + } + + int32_t this_zone; // GMT to local correction. + 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)); + + // Accept only LINKTYPE_NULL and LINKTYPE_ETHERNET. + // See: http://www.tcpdump.org/linktypes.html + if (network != kLinktypeNull && network != kLinktypeEthernet) { + return kResultFail; + } + + return kResultSuccess; + } + + int ReadPacket(int32_t* next_packet_pos, uint32_t stream_start_ms, + uint32_t number) { + assert(next_packet_pos); + + uint32_t ts_sec; // Timestamp seconds. + 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)); + + *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)); + 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)); + + ModuleRTPUtility::RTPHeaderParser rtp_parser(read_buffer_, + marker.payload_length); + if (rtp_parser.RTCP()) { + packets_.push_back(marker); + } else { + if (!rtp_parser.Parse(marker.rtp_header, NULL)) { + DEBUG_LOG("Not recognized as RTP/RTCP"); + return kResultSkip; + } + + uint32_t ssrc = marker.rtp_header.header.ssrc; + packets_by_ssrc_[ssrc].push_back(marker.packet_number); + packets_.push_back(marker); + } + + return kResultSuccess; + } + + int ReadPacketHeader(RtpPacketMarker* marker) { + int32_t file_pos = ftell(file_); + + // Check for BSD null/loopback frame header. The header is just 4 bytes in + // native byte order, so we check for both versions as we don't care about + // 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)); + if (protocol == kBsdNullLoopback1 || protocol == kBsdNullLoopback2) { + int result = ReadXxpIpHeader(marker); + DEBUG_LOG("Recognized loopback frame"); + if (result != kResultSkip) { + return result; + } + } + + TRY(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)); + if (type == kEthertypeIp) { + int result = ReadXxpIpHeader(marker); + DEBUG_LOG("Recognized ethernet 2 frame"); + if (result != kResultSkip) { + return result; + } + } + + return kResultSkip; + } + + uint32_t CalcTimeDelta(uint32_t ts_sec, uint32_t ts_usec, uint32_t start_ms) { + // Round to nearest ms. + uint64_t t2_ms = ((static_cast(ts_sec) * 1000000) + ts_usec + + 500) / 1000; + uint64_t t1_ms = static_cast(start_ms); + if (t2_ms < t1_ms) { + return 0; + } else { + return t2_ms - t1_ms; + } + } + + int ReadXxpIpHeader(RtpPacketMarker* marker) { + assert(marker); + + uint16_t version; + uint16_t length; + uint16_t id; + 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)); + + if (((version >> 12) & 0x000f) != kIpVersion4) { + DEBUG_LOG("IP header is not IPv4"); + return kResultSkip; + } + + if (fragment != kFragmentOffsetClear && + fragment != kFragmentOffsetDoNotFragment) { + DEBUG_LOG("IP fragments cannot be handled"); + return kResultSkip; + } + + // Skip remaining fields of IP header. + uint16_t header_length = (version & 0x0f00) >> (8 - 2); + assert(header_length >= kMinIpHeaderLength); + TRY(Skip(header_length - kMinIpHeaderLength)); + + protocol = protocol & 0x00ff; + if (protocol == kProtocolTcp) { + DEBUG_LOG("TCP packets are not handled"); + return kResultSkip; + } 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)); + marker->payload_length = length - kUdpHeaderLength; + } else { + DEBUG_LOG("Unknown transport (expected UDP or TCP)"); + return kResultSkip; + } + + return kResultSuccess; + } + + int Read(uint32_t* out, bool expect_network_order) { + uint32_t tmp = 0; + if (fread(&tmp, 1, sizeof(uint32_t), file_) != sizeof(uint32_t)) { + return kResultFail; + } + if ((!expect_network_order && swap_pcap_byte_order_) || + (expect_network_order && swap_network_byte_order_)) { + tmp = ((tmp >> 24) & 0x000000ff) | (tmp << 24) | + ((tmp >> 8) & 0x0000ff00) | ((tmp << 8) & 0x00ff0000); + } + *out = tmp; + return kResultSuccess; + } + + int Read(uint16_t* out, bool expect_network_order) { + uint16_t tmp = 0; + if (fread(&tmp, 1, sizeof(uint16_t), file_) != sizeof(uint16_t)) { + return kResultFail; + } + if ((!expect_network_order && swap_pcap_byte_order_) || + (expect_network_order && swap_network_byte_order_)) { + tmp = ((tmp >> 8) & 0x00ff) | (tmp << 8); + } + *out = tmp; + return kResultSuccess; + } + + int Read(uint8_t* out, uint32_t count) { + if (fread(out, 1, count, file_) != count) { + return kResultFail; + } + return kResultSuccess; + } + + int Read(int32_t* out, bool expect_network_order) { + int32_t tmp = 0; + if (fread(&tmp, 1, sizeof(uint32_t), file_) != sizeof(uint32_t)) { + return kResultFail; + } + if ((!expect_network_order && swap_pcap_byte_order_) || + (expect_network_order && swap_network_byte_order_)) { + tmp = ((tmp >> 24) & 0x000000ff) | (tmp << 24) | + ((tmp >> 8) & 0x0000ff00) | ((tmp << 8) & 0x00ff0000); + } + *out = tmp; + return kResultSuccess; + } + + int Skip(uint32_t length) { + if (fseek(file_, length, SEEK_CUR) != 0) { + return kResultFail; + } + return kResultSuccess; + } + + FILE* file_; + bool swap_pcap_byte_order_; + 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); +}; + +RtpPacketSourceInterface* CreatePcapFileReader(const std::string& filename) { + scoped_ptr impl(new PcapFileReaderImpl()); + if (impl->Initialize(filename) != 0) { + return NULL; + } + return impl.release(); +} +} // namespace rtpplayer +} // namespace webrtc diff --git a/webrtc/modules/video_coding/main/test/pcap_file_reader.h b/webrtc/modules/video_coding/main/test/pcap_file_reader.h new file mode 100644 index 0000000000..6450e2d64a --- /dev/null +++ b/webrtc/modules/video_coding/main/test/pcap_file_reader.h @@ -0,0 +1,26 @@ +/* + * 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/pcap_file_reader_unittest.cc b/webrtc/modules/video_coding/main/test/pcap_file_reader_unittest.cc new file mode 100644 index 0000000000..640abc820d --- /dev/null +++ b/webrtc/modules/video_coding/main/test/pcap_file_reader_unittest.cc @@ -0,0 +1,99 @@ +/* + * 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 + +#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/testsupport/fileutils.h" + +namespace webrtc { +namespace rtpplayer { + +typedef std::map PacketsPerSsrc; + +class TestPcapFileReader : public ::testing::Test { + public: + void Init(const std::string& filename) { + std::string filepath = + test::ResourcePath("video_coding/" + filename, "pcap"); + rtp_packet_source_.reset(CreatePcapFileReader(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; + } + + 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; + + ModuleRTPUtility::RTPHeaderParser rtp_header_parser(data, length); + webrtc::WebRtcRTPHeader header; + if (!rtp_header_parser.RTCP() && rtp_header_parser.Parse(header, NULL)) { + pps[header.header.ssrc]++; + } + } + return pps; + } + + private: + scoped_ptr rtp_packet_source_; +}; + +TEST_F(TestPcapFileReader, TestEthernetIIFrame) { + Init("frame-ethernet-ii"); + EXPECT_EQ(368, CountRtpPackets()); +} + +TEST_F(TestPcapFileReader, TestLoopbackFrame) { + Init("frame-loopback"); + EXPECT_EQ(491, CountRtpPackets()); +} + +TEST_F(TestPcapFileReader, TestTwoSsrc) { + Init("ssrcs-2"); + PacketsPerSsrc pps = CountRtpPacketsPerSsrc(); + EXPECT_EQ(2UL, pps.size()); + EXPECT_EQ(370, pps[0x78d48f61]); + EXPECT_EQ(60, pps[0xae94130b]); +} + +TEST_F(TestPcapFileReader, TestThreeSsrc) { + Init("ssrcs-3"); + PacketsPerSsrc pps = CountRtpPacketsPerSsrc(); + EXPECT_EQ(3UL, pps.size()); + EXPECT_EQ(162, pps[0x938c5eaa]); + EXPECT_EQ(113, pps[0x59fe6ef0]); + EXPECT_EQ(61, pps[0xed2bd2ac]); +} + +} // namespace rtpplayer +} // namespace webrtc diff --git a/webrtc/modules/video_coding/main/test/quality_modes_test.cc b/webrtc/modules/video_coding/main/test/quality_modes_test.cc index d582b1c920..de7ec4bc5c 100644 --- a/webrtc/modules/video_coding/main/test/quality_modes_test.cc +++ b/webrtc/modules/video_coding/main/test/quality_modes_test.cc @@ -23,6 +23,7 @@ #include "webrtc/system_wrappers/interface/clock.h" #include "webrtc/system_wrappers/interface/data_log.h" #include "webrtc/system_wrappers/interface/data_log.h" +#include "webrtc/test/testsupport/fileutils.h" #include "webrtc/test/testsupport/metrics/video_metrics.h" using namespace webrtc; diff --git a/webrtc/modules/video_coding/main/test/receiver_tests.h b/webrtc/modules/video_coding/main/test/receiver_tests.h index 079fd6125e..15ec4245f8 100644 --- a/webrtc/modules/video_coding/main/test/receiver_tests.h +++ b/webrtc/modules/video_coding/main/test/receiver_tests.h @@ -16,73 +16,32 @@ #include "common_types.h" #include "rtp_rtcp.h" #include "typedefs.h" -#include "rtp_player.h" #include "test_util.h" #include #include -class RtpDataCallback : public webrtc::RtpData -{ -public: - RtpDataCallback(webrtc::VideoCodingModule* vcm) - : _vcm(vcm) {}; +class RtpDataCallback : public webrtc::RtpData { + public: + RtpDataCallback(webrtc::VideoCodingModule* vcm) : vcm_(vcm) {} + virtual ~RtpDataCallback() {} - virtual int32_t OnReceivedPayloadData(const uint8_t* payloadData, - const uint16_t payloadSize, - const webrtc::WebRtcRTPHeader* rtpHeader); -private: - webrtc::VideoCodingModule* _vcm; + virtual WebRtc_Word32 OnReceivedPayloadData( + const WebRtc_UWord8* payload_data, + const WebRtc_UWord16 payload_size, + const webrtc::WebRtcRTPHeader* rtp_header) { + return vcm_->IncomingPacket(payload_data, payload_size, *rtp_header); + } + + private: + webrtc::VideoCodingModule* vcm_; }; -class FrameReceiveCallback : public webrtc::VCMReceiveCallback -{ -public: - FrameReceiveCallback(std::string outFilename) : - _outFilename(outFilename), - _outFile(NULL), - _timingFile(NULL), - width_(0), - height_(0), - count_(0) {} - - virtual ~FrameReceiveCallback(); - - int32_t FrameToRender(webrtc::I420VideoFrame& videoFrame); - -private: - static void SplitFilename(std::string filename, std::string* basename, - std::string* ending); - static std::string AppendWidthHeightAndCount(std::string basename, - unsigned int width, - unsigned int height, - int count); - std::string _outFilename; - FILE* _outFile; - FILE* _timingFile; - unsigned int width_; - unsigned int height_; - int count_; -}; - -class SharedState -{ -public: - SharedState(webrtc::VideoCodingModule& vcm, RTPPlayer& rtpPlayer) : - _vcm(vcm), - _rtpPlayer(rtpPlayer) {} - webrtc::VideoCodingModule& _vcm; - RTPPlayer& _rtpPlayer; -}; - - -int RtpPlay(CmdArgs& args); -int RtpPlayMT(CmdArgs& args, - int releaseTest = 0, - webrtc::VideoCodecType releaseTestVideoType = webrtc::kVideoCodecVP8); +int RtpPlay(const CmdArgs& args); +int RtpPlayMT(const CmdArgs& args); int ReceiverTimingTests(CmdArgs& args); int JitterBufferTest(CmdArgs& args); -int DecodeFromStorageTest(CmdArgs& args); +int DecodeFromStorageTest(const CmdArgs& args); // Thread functions: bool ProcessingThread(void* obj); diff --git a/webrtc/modules/video_coding/main/test/receiver_timing_tests.cc b/webrtc/modules/video_coding/main/test/receiver_timing_tests.cc index f809d07eee..5e59ebd509 100644 --- a/webrtc/modules/video_coding/main/test/receiver_timing_tests.cc +++ b/webrtc/modules/video_coding/main/test/receiver_timing_tests.cc @@ -19,6 +19,7 @@ #include "webrtc/modules/video_coding/main/test/test_macros.h" #include "webrtc/modules/video_coding/main/test/test_util.h" #include "webrtc/system_wrappers/interface/trace.h" +#include "webrtc/test/testsupport/fileutils.h" using namespace webrtc; diff --git a/webrtc/modules/video_coding/main/test/rtp_file_reader.cc b/webrtc/modules/video_coding/main/test/rtp_file_reader.cc new file mode 100644 index 0000000000..f1921e9a46 --- /dev/null +++ b/webrtc/modules/video_coding/main/test/rtp_file_reader.cc @@ -0,0 +1,166 @@ +/* + * 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 new file mode 100644 index 0000000000..b763ee7513 --- /dev/null +++ b/webrtc/modules/video_coding/main/test/rtp_file_reader.h @@ -0,0 +1,26 @@ +/* + * 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 new file mode 100644 index 0000000000..7e0e607cae --- /dev/null +++ b/webrtc/modules/video_coding/main/test/rtp_file_reader_unittest.cc @@ -0,0 +1,54 @@ +/* + * 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 2487ef4a2d..57c1ca8997 100644 --- a/webrtc/modules/video_coding/main/test/rtp_player.cc +++ b/webrtc/modules/video_coding/main/test/rtp_player.cc @@ -10,435 +10,479 @@ #include "webrtc/modules/video_coding/main/test/rtp_player.h" -#include -#ifdef WIN32 -#include -#include -#else -#include -#endif +#include +#include -#include "testing/gtest/include/gtest/gtest.h" #include "webrtc/modules/rtp_rtcp/interface/rtp_rtcp.h" +#include "webrtc/modules/rtp_rtcp/source/rtp_utility.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" -using namespace webrtc; +#if 1 +# define DEBUG_LOG1(text, arg) +#else +# define DEBUG_LOG1(text, arg) (printf(text "\n", arg)) +#endif -RawRtpPacket::RawRtpPacket(uint8_t* rtp_data, uint16_t rtp_length) - : data(rtp_data), - length(rtp_length), - resend_time_ms(-1) { - data = new uint8_t[length]; - memcpy(data, rtp_data, length); -} +namespace webrtc { +namespace rtpplayer { -RawRtpPacket::~RawRtpPacket() { - delete [] data; -} +enum { + kMaxPacketBufferSize = 4096, + kDefaultTransmissionTimeOffsetExtensionId = 2 +}; -LostPackets::LostPackets() - : crit_sect_(CriticalSectionWrapper::CreateCriticalSection()), - loss_count_(0), - debug_file_(NULL), - packets_() { - debug_file_ = fopen("PacketLossDebug.txt", "w"); -} +class RawRtpPacket { + public: + RawRtpPacket(const uint8_t* data, uint32_t length, uint32_t ssrc, + uint16_t seq_num) + : data_(new uint8_t[length]), + length_(length), + resend_time_ms_(-1), + ssrc_(ssrc), + seq_num_(seq_num) { + assert(data); + memcpy(data_.get(), data, length_); + } -LostPackets::~LostPackets() { - if (debug_file_) { + const uint8_t* data() const { return data_.get(); } + uint32_t length() const { return length_; } + int64_t resend_time_ms() const { return resend_time_ms_; } + void set_resend_time_ms(int64_t timeMs) { resend_time_ms_ = timeMs; } + uint32_t ssrc() const { return ssrc_; } + uint16_t seq_num() const { return seq_num_; } + + private: + scoped_array data_; + uint32_t length_; + int64_t resend_time_ms_; + uint32_t ssrc_; + uint16_t seq_num_; + + DISALLOW_IMPLICIT_CONSTRUCTORS(RawRtpPacket); +}; + +class LostPackets { + public: + LostPackets(Clock* clock, uint32_t rtt_ms) + : crit_sect_(CriticalSectionWrapper::CreateCriticalSection()), + debug_file_(fopen("PacketLossDebug.txt", "w")), + loss_count_(0), + packets_(), + clock_(clock), + rtt_ms_(rtt_ms) { + assert(clock); + } + + ~LostPackets() { + if (debug_file_) { fclose(debug_file_); + debug_file_ = NULL; + } + while (!packets_.empty()) { + delete packets_.back(); + packets_.pop_back(); + } } - while (!packets_.empty()) { - delete packets_.front(); - packets_.pop_front(); - } - delete crit_sect_; -} -void LostPackets::AddPacket(RawRtpPacket* packet) { - CriticalSectionScoped cs(crit_sect_); - packets_.push_back(packet); - uint16_t seq_num = (packet->data[2] << 8) + packet->data[3]; - if (debug_file_ != NULL) { - fprintf(debug_file_, "%u Lost packet: %u\n", loss_count_, seq_num); + void AddPacket(RawRtpPacket* packet) { + assert(packet); + printf("Throw: %08x:%u\n", packet->ssrc(), packet->seq_num()); + CriticalSectionScoped cs(crit_sect_.get()); + if (debug_file_) { + fprintf(debug_file_, "%u Lost packet: %u\n", loss_count_, + packet->seq_num()); + } + packets_.push_back(packet); + loss_count_++; } - ++loss_count_; -} -void LostPackets::SetResendTime(uint16_t resend_seq_num, - int64_t resend_time_ms, - int64_t now_ms) { - CriticalSectionScoped cs(crit_sect_); - for (RtpPacketIterator it = packets_.begin(); it != packets_.end(); ++it) { - const uint16_t seq_num = ((*it)->data[2] << 8) + - (*it)->data[3]; - if (resend_seq_num == seq_num) { - if ((*it)->resend_time_ms + 10 < now_ms) { - if (debug_file_ != NULL) { - fprintf(debug_file_, "Resend %u at %u\n", seq_num, + void SetResendTime(uint32_t ssrc, int16_t resendSeqNum) { + int64_t resend_time_ms = clock_->TimeInMilliseconds() + rtt_ms_; + int64_t now_ms = clock_->TimeInMilliseconds(); + CriticalSectionScoped cs(crit_sect_.get()); + for (RtpPacketIterator it = packets_.begin(); it != packets_.end(); ++it) { + RawRtpPacket* packet = *it; + if (ssrc == packet->ssrc() && resendSeqNum == packet->seq_num() && + packet->resend_time_ms() + 10 < now_ms) { + if (debug_file_) { + fprintf(debug_file_, "Resend %u at %u\n", packet->seq_num(), MaskWord64ToUWord32(resend_time_ms)); } - (*it)->resend_time_ms = resend_time_ms; + packet->set_resend_time_ms(resend_time_ms); + return; } - return; } + // We may get here since the captured stream may itself be missing packets. } - assert(false); -} -RawRtpPacket* LostPackets::NextPacketToResend(int64_t timeNow) { - CriticalSectionScoped cs(crit_sect_); - for (RtpPacketIterator it = packets_.begin(); it != packets_.end(); ++it) { - if (timeNow >= (*it)->resend_time_ms && (*it)->resend_time_ms != -1) { + RawRtpPacket* NextPacketToResend(int64_t time_now) { + CriticalSectionScoped cs(crit_sect_.get()); + for (RtpPacketIterator it = packets_.begin(); it != packets_.end(); ++it) { RawRtpPacket* packet = *it; - it = packets_.erase(it); - return packet; + if (time_now >= packet->resend_time_ms() && + packet->resend_time_ms() != -1) { + packets_.erase(it); + return packet; + } } + return NULL; } - return NULL; -} -int LostPackets::NumberOfPacketsToResend() const { - CriticalSectionScoped cs(crit_sect_); - int count = 0; - for (ConstRtpPacketIterator it = packets_.begin(); it != packets_.end(); - ++it) { - if ((*it)->resend_time_ms >= 0) { + int NumberOfPacketsToResend() const { + CriticalSectionScoped cs(crit_sect_.get()); + int count = 0; + for (ConstRtpPacketIterator it = packets_.begin(); it != packets_.end(); + ++it) { + if ((*it)->resend_time_ms() >= 0) { count++; + } + } + return count; + } + + void LogPacketResent(RawRtpPacket* packet) { + int64_t now_ms = clock_->TimeInMilliseconds(); + CriticalSectionScoped cs(crit_sect_.get()); + if (debug_file_) { + fprintf(debug_file_, "Resent %u at %u\n", packet->seq_num(), + MaskWord64ToUWord32(now_ms)); } } - return count; -} -void LostPackets::SetPacketResent(uint16_t seq_num, int64_t now_ms) { - CriticalSectionScoped cs(crit_sect_); - if (debug_file_ != NULL) { - fprintf(debug_file_, "Resent %u at %u\n", seq_num, - MaskWord64ToUWord32(now_ms)); - } -} - -void LostPackets::Print() const { - CriticalSectionScoped cs(crit_sect_); - printf("Lost packets: %u\n", loss_count_); - printf("Packets waiting to be resent: %u\n", - NumberOfPacketsToResend()); - printf("Packets still lost: %u\n", - static_cast(packets_.size())); - printf("Sequence numbers:\n"); - for (ConstRtpPacketIterator it = packets_.begin(); it != packets_.end(); - ++it) { - uint16_t seq_num = ((*it)->data[2] << 8) + (*it)->data[3]; - printf("%u, ", seq_num); - } - printf("\n"); -} - -RTPPlayer::RTPPlayer(const char* filename, - RtpData* callback, - Clock* clock) -: -_clock(clock), -_rtpModule(NULL), -_nextRtpTime(0), -_dataCallback(callback), -_firstPacket(true), -_lossRate(0.0f), -_nackEnabled(false), -_resendPacketCount(0), -_noLossStartup(100), -_endOfFile(false), -_rttMs(0), -_firstPacketRtpTime(0), -_firstPacketTimeMs(0), -_reorderBuffer(NULL), -_reordering(false), -_nextPacket(), -_nextPacketLength(0), -_randVec(), -_randVecPos(0) -{ - _rtpFile = fopen(filename, "rb"); - memset(_nextPacket, 0, sizeof(_nextPacket)); -} - -RTPPlayer::~RTPPlayer() -{ - delete _rtpModule; - if (_rtpFile != NULL) - { - fclose(_rtpFile); + void Print() const { + CriticalSectionScoped cs(crit_sect_.get()); + printf("Lost packets: %u\n", loss_count_); + printf("Packets waiting to be resent: %d\n", NumberOfPacketsToResend()); + printf("Packets still lost: %zd\n", packets_.size()); + printf("Sequence numbers:\n"); + for (ConstRtpPacketIterator it = packets_.begin(); it != packets_.end(); + ++it) { + printf("%u, ", (*it)->seq_num()); + } + printf("\n"); + } + + private: + typedef std::vector RtpPacketList; + typedef RtpPacketList::iterator RtpPacketIterator; + typedef RtpPacketList::const_iterator ConstRtpPacketIterator; + + scoped_ptr crit_sect_; + FILE* debug_file_; + int loss_count_; + RtpPacketList packets_; + Clock* clock_; + uint32_t rtt_ms_; + + DISALLOW_IMPLICIT_CONSTRUCTORS(LostPackets); +}; + +class SsrcHandlers { + public: + SsrcHandlers(PayloadSinkFactoryInterface* payload_sink_factory, + const PayloadTypes& payload_types) + : payload_sink_factory_(payload_sink_factory), + payload_types_(payload_types), + handlers_() { + assert(payload_sink_factory); + } + + ~SsrcHandlers() { + while (!handlers_.empty()) { + delete handlers_.begin()->second; + handlers_.erase(handlers_.begin()); + } + } + + int RegisterSsrc(uint32_t ssrc, LostPackets* lost_packets) { + if (handlers_.count(ssrc) > 0) { + return 0; + } + DEBUG_LOG1("Registering handler for ssrc=%08x", ssrc); + + scoped_ptr handler( + new Handler(ssrc, payload_types_, lost_packets)); + handler->payload_sink_.reset(payload_sink_factory_->Create(handler.get())); + if (handler->payload_sink_.get() == NULL) { + return -1; } - if (_reorderBuffer != NULL) - { - delete _reorderBuffer; - _reorderBuffer = NULL; - } -} -int32_t RTPPlayer::Initialize(const PayloadTypeList* payloadList) -{ RtpRtcp::Configuration configuration; configuration.id = 1; configuration.audio = false; - configuration.incoming_data = _dataCallback; - _rtpModule = RtpRtcp::CreateRtpRtcp(configuration); + configuration.incoming_data = handler->payload_sink_.get(); + handler->rtp_module_.reset(RtpRtcp::CreateRtpRtcp(configuration)); + if (handler->rtp_module_.get() == NULL) { + return -1; + } - std::srand(321); - for (int i=0; i < RAND_VEC_LENGTH; i++) - { - _randVec[i] = rand(); + if (handler->rtp_module_->SetNACKStatus(kNackOff, + kMaxPacketAgeToNack) < 0) { + return -1; } - _randVecPos = 0; - int32_t ret = _rtpModule->SetNACKStatus(kNackOff, - kMaxPacketAgeToNack); - if (ret < 0) - { - return -1; - } - _rtpModule->SetRTCPStatus(kRtcpNonCompound); - _rtpModule->SetTMMBRStatus(true); + handler->rtp_module_->SetRTCPStatus(kRtcpNonCompound); + handler->rtp_module_->SetREMBStatus(true); + handler->rtp_module_->SetSSRCFilter(true, ssrc); + handler->rtp_module_->RegisterReceiveRtpHeaderExtension( + kRtpExtensionTransmissionTimeOffset, + kDefaultTransmissionTimeOffsetExtensionId); - if (ret < 0) - { + for (PayloadTypesIterator it = payload_types_.begin(); + it != payload_types_.end(); ++it) { + VideoCodec codec; + memset(&codec, 0, sizeof(codec)); + strncpy(codec.plName, it->name().c_str(), sizeof(codec.plName)-1); + codec.plType = it->payload_type(); + codec.codecType = it->codec_type(); + if (handler->rtp_module_->RegisterReceivePayload(codec) < 0) { return -1; + } } - // Register payload types - for (PayloadTypeList::const_iterator it = payloadList->begin(); - it != payloadList->end(); ++it) { - PayloadCodecTuple* payloadType = *it; - if (payloadType != NULL) - { - VideoCodec videoCodec; - strncpy(videoCodec.plName, payloadType->name.c_str(), 32); - videoCodec.plType = payloadType->payloadType; - if (_rtpModule->RegisterReceivePayload(videoCodec) < 0) - { - return -1; - } - } - } - if (ReadHeader() < 0) - { - return -1; - } - memset(_nextPacket, 0, sizeof(_nextPacket)); - _nextPacketLength = ReadPacket(_nextPacket, &_nextRtpTime); + + handlers_[ssrc] = handler.release(); return 0; -} + } -int32_t RTPPlayer::ReadHeader() -{ - char firstline[FIRSTLINELEN]; - if (_rtpFile == NULL) - { - return -1; + void Process() { + for (HandlerMapIt it = handlers_.begin(); it != handlers_.end(); ++it) { + it->second->rtp_module_->Process(); } - EXPECT_TRUE(fgets(firstline, FIRSTLINELEN, _rtpFile) != NULL); - if(strncmp(firstline,"#!rtpplay",9) == 0) { - if(strncmp(firstline,"#!rtpplay1.0",12) != 0){ - printf("ERROR: wrong rtpplay version, must be 1.0\n"); - return -1; - } + } + + void IncomingPacket(const uint8_t* data, uint32_t length) { + for (HandlerMapIt it = handlers_.begin(); it != handlers_.end(); ++it) { + it->second->rtp_module_->IncomingPacket(data, length); } - else if (strncmp(firstline,"#!RTPencode",11) == 0) { - if(strncmp(firstline,"#!RTPencode1.0",14) != 0){ - printf("ERROR: wrong RTPencode version, must be 1.0\n"); - return -1; - } + } + + private: + class Handler : public RtpStreamInterface { + public: + Handler(uint32_t ssrc, const PayloadTypes& payload_types, + LostPackets* lost_packets) + : rtp_module_(), + payload_sink_(), + ssrc_(ssrc), + payload_types_(payload_types), + lost_packets_(lost_packets) { + assert(lost_packets); } - else { - printf("ERROR: wrong file format of input file\n"); - return -1; + virtual ~Handler() {} + + virtual void ResendPackets(const uint16_t* sequence_numbers, + uint16_t length) { + assert(sequence_numbers); + for (uint16_t i = 0; i < length; i++) { + lost_packets_->SetResendTime(ssrc_, sequence_numbers[i]); + } } - uint32_t start_sec; - uint32_t start_usec; - uint32_t source; - uint16_t port; - uint16_t padding; - - EXPECT_GT(fread(&start_sec, 4, 1, _rtpFile), 0u); - start_sec=ntohl(start_sec); - EXPECT_GT(fread(&start_usec, 4, 1, _rtpFile), 0u); - start_usec=ntohl(start_usec); - EXPECT_GT(fread(&source, 4, 1, _rtpFile), 0u); - source=ntohl(source); - EXPECT_GT(fread(&port, 2, 1, _rtpFile), 0u); - port=ntohs(port); - EXPECT_GT(fread(&padding, 2, 1, _rtpFile), 0u); - padding=ntohs(padding); - return 0; -} - -uint32_t RTPPlayer::TimeUntilNextPacket() const -{ - int64_t timeLeft = (_nextRtpTime - _firstPacketRtpTime) - - (_clock->TimeInMilliseconds() - _firstPacketTimeMs); - if (timeLeft < 0) - { - return 0; + virtual uint32_t ssrc() const { return ssrc_; } + virtual const PayloadTypes& payload_types() const { + return payload_types_; } - return static_cast(timeLeft); -} -int32_t RTPPlayer::NextPacket(const int64_t timeNow) -{ - // Send any packets ready to be resent, - RawRtpPacket* resend_packet = _lostPackets.NextPacketToResend(timeNow); - while (resend_packet != NULL) { - const uint16_t seqNo = (resend_packet->data[2] << 8) + - resend_packet->data[3]; - printf("Resend: %u\n", seqNo); - int ret = SendPacket(resend_packet->data, resend_packet->length); - delete resend_packet; - _resendPacketCount++; + scoped_ptr rtp_module_; + scoped_ptr payload_sink_; + + private: + uint32_t ssrc_; + const PayloadTypes& payload_types_; + LostPackets* lost_packets_; + + DISALLOW_COPY_AND_ASSIGN(Handler); + }; + + typedef std::map HandlerMap; + typedef std::map::iterator HandlerMapIt; + + PayloadSinkFactoryInterface* payload_sink_factory_; + PayloadTypes payload_types_; + HandlerMap handlers_; + + DISALLOW_IMPLICIT_CONSTRUCTORS(SsrcHandlers); +}; + +class RtpPlayerImpl : public RtpPlayerInterface { + public: + RtpPlayerImpl(PayloadSinkFactoryInterface* payload_sink_factory, + const PayloadTypes& payload_types, Clock* clock, + scoped_ptr* packet_source, + float loss_rate, uint32_t rtt_ms, bool reordering) + : ssrc_handlers_(payload_sink_factory, payload_types), + clock_(clock), + packet_source_(NULL), + next_rtp_time_(0), + first_packet_(true), + first_packet_rtp_time_(0), + first_packet_time_ms_(0), + loss_rate_(loss_rate), + lost_packets_(clock, rtt_ms), + resend_packet_count_(0), + no_loss_startup_(100), + end_of_file_(false), + reordering_(false), + reorder_buffer_(), + next_packet_(), + next_packet_length_(0) { + assert(clock); + assert(packet_source); + assert(packet_source->get()); + packet_source_.swap(*packet_source); + srand(321); + } + + virtual ~RtpPlayerImpl() {} + + virtual int NextPacket(int64_t time_now) { + // Send any packets ready to be resent. + RawRtpPacket* packet; + while ((packet = lost_packets_.NextPacketToResend(time_now))) { + int ret = SendPacket(packet->data(), packet->length()); if (ret > 0) { - _lostPackets.SetPacketResent(seqNo, _clock->TimeInMilliseconds()); - } else if (ret < 0) { + printf("Resend: %08x:%u\n", packet->ssrc(), packet->seq_num()); + lost_packets_.LogPacketResent(packet); + resend_packet_count_++; + } + delete packet; + if (ret < 0) { return ret; } - resend_packet = _lostPackets.NextPacketToResend(timeNow); } - // Send any packets from rtp file - if (!_endOfFile && (TimeUntilNextPacket() == 0 || _firstPacket)) - { - _rtpModule->Process(); - if (_firstPacket) - { - _firstPacketRtpTime = static_cast(_nextRtpTime); - _firstPacketTimeMs = _clock->TimeInMilliseconds(); + // Send any packets from packet source. + if (!end_of_file_ && (TimeUntilNextPacket() == 0 || first_packet_)) { + ssrc_handlers_.Process(); + + if (first_packet_) { + next_packet_length_ = sizeof(next_packet_); + if (packet_source_->NextPacket(next_packet_, &next_packet_length_, + &next_rtp_time_) != 0) { + return 0; } - if (_reordering && _reorderBuffer == NULL) - { - _reorderBuffer = new RawRtpPacket(reinterpret_cast(_nextPacket), static_cast(_nextPacketLength)); - return 0; - } - int32_t ret = SendPacket(reinterpret_cast(_nextPacket), static_cast(_nextPacketLength)); - if (_reordering && _reorderBuffer != NULL) - { - RawRtpPacket* rtpPacket = _reorderBuffer; - _reorderBuffer = NULL; - SendPacket(rtpPacket->data, rtpPacket->length); - delete rtpPacket; - } - _firstPacket = false; - if (ret < 0) - { - return ret; - } - _nextPacketLength = ReadPacket(_nextPacket, &_nextRtpTime); - if (_nextPacketLength < 0) - { - _endOfFile = true; - return 0; - } - else if (_nextPacketLength == 0) - { - return 0; - } - } - if (_endOfFile && _lostPackets.NumberOfPacketsToResend() == 0) - { - return 1; - } - return 0; -} + first_packet_rtp_time_ = next_rtp_time_; + first_packet_time_ms_ = clock_->TimeInMilliseconds(); + first_packet_ = false; + } -int32_t RTPPlayer::SendPacket(uint8_t* rtpData, uint16_t rtpLen) -{ - if ((_randVec[(_randVecPos++) % RAND_VEC_LENGTH] + 1.0)/(RAND_MAX + 1.0) < _lossRate && - _noLossStartup < 0) - { - if (_nackEnabled) - { - const uint16_t seqNo = (rtpData[2] << 8) + rtpData[3]; - printf("Throw: %u\n", seqNo); - _lostPackets.AddPacket(new RawRtpPacket(rtpData, rtpLen)); - return 0; - } - } - else if (rtpLen > 0) - { - int32_t ret = _rtpModule->IncomingPacket(rtpData, rtpLen); - if (ret < 0) - { - return -1; - } - } - if (_noLossStartup >= 0) - { - _noLossStartup--; - } - return 1; -} - -int32_t RTPPlayer::ReadPacket(int16_t* rtpdata, uint32_t* offset) -{ - uint16_t length, plen; - - if (fread(&length,2,1,_rtpFile)==0) - return(-1); - length=ntohs(length); - - if (fread(&plen,2,1,_rtpFile)==0) - return(-1); - plen=ntohs(plen); - - if (fread(offset,4,1,_rtpFile)==0) - return(-1); - *offset=ntohl(*offset); - - // Use length here because a plen of 0 specifies rtcp - length = (uint16_t) (length - HDR_SIZE); - if (fread((unsigned short *) rtpdata,1,length,_rtpFile) != length) - return(-1); - -#ifdef JUNK_DATA - // destroy the RTP payload with random data - if (plen > 12) { // ensure that we have more than just a header - for ( int ix = 12; ix < plen; ix=ix+2 ) { - rtpdata[ix>>1] = (short) (rtpdata[ix>>1] + (short) rand()); - } - } -#endif - return plen; -} - -int32_t RTPPlayer::SimulatePacketLoss(float lossRate, bool enableNack, uint32_t rttMs) -{ - _nackEnabled = enableNack; - _lossRate = lossRate; - _rttMs = rttMs; - return 0; -} - -int32_t RTPPlayer::SetReordering(bool enabled) -{ - _reordering = enabled; - return 0; -} - -int32_t RTPPlayer::ResendPackets(const uint16_t* sequenceNumbers, uint16_t length) -{ - if (sequenceNumbers == NULL) - { + if (reordering_ && reorder_buffer_.get() == NULL) { + reorder_buffer_.reset(new RawRtpPacket(next_packet_, + next_packet_length_, 0, 0)); return 0; + } + int ret = SendPacket(next_packet_, next_packet_length_); + if (reorder_buffer_.get()) { + SendPacket(reorder_buffer_->data(), reorder_buffer_->length()); + reorder_buffer_.reset(NULL); + } + if (ret < 0) { + return ret; + } + + next_packet_length_ = sizeof(next_packet_); + if (packet_source_->NextPacket(next_packet_, &next_packet_length_, + &next_rtp_time_) != 0) { + end_of_file_ = true; + return 0; + } + else if (next_packet_length_ == 0) { + return 0; + } } - for (int i=0; i < length; i++) - { - _lostPackets.SetResendTime(sequenceNumbers[i], - _clock->TimeInMilliseconds() + _rttMs, - _clock->TimeInMilliseconds()); + + if (end_of_file_ && lost_packets_.NumberOfPacketsToResend() == 0) { + return 1; } return 0; -} + } -void RTPPlayer::Print() const -{ - printf("Resent packets: %u\n", _resendPacketCount); - _lostPackets.Print(); + virtual uint32_t TimeUntilNextPacket() const { + int64_t time_left = (next_rtp_time_ - first_packet_rtp_time_) - + (clock_->TimeInMilliseconds() - first_packet_time_ms_); + if (time_left < 0) { + return 0; + } + return static_cast(time_left); + } + + virtual void Print() const { + printf("Resent packets: %u\n", resend_packet_count_); + lost_packets_.Print(); + } + + private: + int SendPacket(const uint8_t* data, uint32_t length) { + assert(data); + assert(length > 0); + + ModuleRTPUtility::RTPHeaderParser rtp_header_parser(data, length); + if (!rtp_header_parser.RTCP()) { + WebRtcRTPHeader header; + if (!rtp_header_parser.Parse(header, NULL)) { + return -1; + } + uint32_t ssrc = header.header.ssrc; + if (ssrc_handlers_.RegisterSsrc(ssrc, &lost_packets_) < 0) { + DEBUG_LOG1("Unable to register ssrc: %d", ssrc); + return -1; + } + + if (no_loss_startup_ > 0) { + no_loss_startup_--; + } else if ((rand() + 1.0)/(RAND_MAX + 1.0) < loss_rate_) { + uint16_t seq_num = header.header.sequenceNumber; + lost_packets_.AddPacket(new RawRtpPacket(data, length, ssrc, seq_num)); + DEBUG_LOG1("Dropped packet: %d!", header.header.sequenceNumber); + return 0; + } + } + + ssrc_handlers_.IncomingPacket(data, length); + return 1; + } + + SsrcHandlers ssrc_handlers_; + Clock* clock_; + scoped_ptr packet_source_; + uint32_t next_rtp_time_; + bool first_packet_; + int64_t first_packet_rtp_time_; + int64_t first_packet_time_ms_; + float loss_rate_; + LostPackets lost_packets_; + uint32_t resend_packet_count_; + uint32_t no_loss_startup_; + bool end_of_file_; + bool reordering_; + scoped_ptr reorder_buffer_; + uint8_t next_packet_[kMaxPacketBufferSize]; + uint32_t next_packet_length_; + + DISALLOW_IMPLICIT_CONSTRUCTORS(RtpPlayerImpl); +}; + +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)); + if (packet_source.get() == NULL) { + packet_source.reset(CreatePcapFileReader(input_filename)); + if (packet_source.get() == NULL) { + return NULL; + } + } + + scoped_ptr impl(new RtpPlayerImpl(payload_sink_factory, + payload_types, clock, &packet_source, loss_rate, rtt_ms, reordering)); + return impl.release(); } +} // namespace rtpplayer +} // namespace webrtc diff --git a/webrtc/modules/video_coding/main/test/rtp_player.h b/webrtc/modules/video_coding/main/test/rtp_player.h index 4d615d8c31..24e62b63c5 100644 --- a/webrtc/modules/video_coding/main/test/rtp_player.h +++ b/webrtc/modules/video_coding/main/test/rtp_player.h @@ -11,109 +11,101 @@ #ifndef WEBRTC_MODULES_VIDEO_CODING_TEST_RTP_PLAYER_H_ #define WEBRTC_MODULES_VIDEO_CODING_TEST_RTP_PLAYER_H_ -#include "typedefs.h" -#include "rtp_rtcp.h" -#include "critical_section_wrapper.h" -#include "video_coding_defines.h" -#include "webrtc/system_wrappers/interface/clock.h" - -#include -#include #include +#include -#define HDR_SIZE 8 // rtpplay packet header size in bytes -#define FIRSTLINELEN 40 -#define RAND_VEC_LENGTH 4096 +#include "webrtc/modules/rtp_rtcp/interface/rtp_rtcp_defines.h" +#include "webrtc/modules/video_coding/main/interface/video_coding_defines.h" -struct PayloadCodecTuple; +namespace webrtc { +class Clock; -struct RawRtpPacket -{ -public: - RawRtpPacket(uint8_t* rtp_data, uint16_t rtp_length); - ~RawRtpPacket(); +namespace rtpplayer { - uint8_t* data; - uint16_t length; - int64_t resend_time_ms; -}; - -typedef std::list PayloadTypeList; -typedef std::list RtpPacketList; -typedef RtpPacketList::iterator RtpPacketIterator; -typedef RtpPacketList::const_iterator ConstRtpPacketIterator; - -class LostPackets { +class PayloadCodecTuple { public: - LostPackets(); - ~LostPackets(); + PayloadCodecTuple(uint8_t payload_type, const std::string& codec_name, + VideoCodecType codec_type) + : name_(codec_name), + payload_type_(payload_type), + codec_type_(codec_type) { + } - void AddPacket(RawRtpPacket* packet); - void SetResendTime(uint16_t sequenceNumber, - int64_t resendTime, - int64_t nowMs); - RawRtpPacket* NextPacketToResend(int64_t timeNow); - int NumberOfPacketsToResend() const; - void SetPacketResent(uint16_t seqNo, int64_t nowMs); - void Print() const; + const std::string& name() const { return name_; } + uint8_t payload_type() const { return payload_type_; } + VideoCodecType codec_type() const { return codec_type_; } private: - webrtc::CriticalSectionWrapper* crit_sect_; - int loss_count_; - FILE* debug_file_; - RtpPacketList packets_; + std::string name_; + uint8_t payload_type_; + VideoCodecType codec_type_; }; -struct PayloadCodecTuple -{ - PayloadCodecTuple(uint8_t plType, std::string codecName, webrtc::VideoCodecType type) : - name(codecName), payloadType(plType), codecType(type) {}; - const std::string name; - const uint8_t payloadType; - const webrtc::VideoCodecType codecType; +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; }; -class RTPPlayer : public webrtc::VCMPacketRequestCallback -{ -public: - RTPPlayer(const char* filename, - webrtc::RtpData* callback, - webrtc::Clock* clock); - virtual ~RTPPlayer(); +// Implemented by RtpPlayer and given to client as a means to retrieve +// information about a specific RTP stream. +class RtpStreamInterface { + public: + virtual ~RtpStreamInterface() {} - int32_t Initialize(const PayloadTypeList* payloadList); - int32_t NextPacket(const int64_t timeNow); - uint32_t TimeUntilNextPacket() const; - int32_t SimulatePacketLoss(float lossRate, bool enableNack = false, uint32_t rttMs = 0); - int32_t SetReordering(bool enabled); - int32_t ResendPackets(const uint16_t* sequenceNumbers, uint16_t length); - void Print() const; + // Ask for missing packets to be resent. + virtual void ResendPackets(const uint16_t* sequence_numbers, + uint16_t length) = 0; -private: - int32_t SendPacket(uint8_t* rtpData, uint16_t rtpLen); - int32_t ReadPacket(int16_t* rtpdata, uint32_t* offset); - int32_t ReadHeader(); - webrtc::Clock* _clock; - FILE* _rtpFile; - webrtc::RtpRtcp* _rtpModule; - uint32_t _nextRtpTime; - webrtc::RtpData* _dataCallback; - bool _firstPacket; - float _lossRate; - bool _nackEnabled; - LostPackets _lostPackets; - uint32_t _resendPacketCount; - int32_t _noLossStartup; - bool _endOfFile; - uint32_t _rttMs; - int64_t _firstPacketRtpTime; - int64_t _firstPacketTimeMs; - RawRtpPacket* _reorderBuffer; - bool _reordering; - int16_t _nextPacket[8000]; - int32_t _nextPacketLength; - int _randVec[RAND_VEC_LENGTH]; - int _randVecPos; + virtual uint32_t ssrc() const = 0; + virtual const PayloadTypes& payload_types() const = 0; }; +// Implemented by a sink. Wraps RtpData because its d-tor is protected. +class PayloadSinkInterface : public RtpData { + public: + virtual ~PayloadSinkInterface() {} +}; + +// Implemented to provide a sink for RTP data, such as hooking up a VCM to +// the incoming RTP stream. +class PayloadSinkFactoryInterface { + public: + virtual ~PayloadSinkFactoryInterface() {} + + // Return NULL if failed to create sink. 'stream' is guaranteed to be + // around for as long as the RtpData. The returned object is owned by + // the caller (RtpPlayer). + virtual PayloadSinkInterface* Create(RtpStreamInterface* stream) = 0; +}; + +// The client's view of an RtpPlayer. +class RtpPlayerInterface { + public: + virtual ~RtpPlayerInterface() {} + + virtual int NextPacket(int64_t timeNow) = 0; + virtual uint32_t TimeUntilNextPacket() const = 0; + virtual void Print() const = 0; +}; + +RtpPlayerInterface* Create(const std::string& inputFilename, + PayloadSinkFactoryInterface* payloadSinkFactory, Clock* clock, + const PayloadTypes& payload_types, float lossRate, uint32_t rttMs, + bool reordering); + +} // namespace rtpplayer +} // namespace webrtc + #endif // WEBRTC_MODULES_VIDEO_CODING_TEST_RTP_PLAYER_H_ diff --git a/webrtc/modules/video_coding/main/test/test_util.cc b/webrtc/modules/video_coding/main/test/test_util.cc index 1f8916839c..c16b98dffa 100644 --- a/webrtc/modules/video_coding/main/test/test_util.cc +++ b/webrtc/modules/video_coding/main/test/test_util.cc @@ -8,39 +8,150 @@ * be found in the AUTHORS file in the root of the source tree. */ -#include "test_util.h" -#include "test_macros.h" -#include "rtp_dump.h" -#include +#include "webrtc/modules/video_coding/main/test/test_util.h" -using namespace webrtc; +#include +#include +#include +#include + +#include "webrtc/common_video/libyuv/include/webrtc_libyuv.h" +#include "webrtc/modules/video_coding/main/source/internal_defines.h" +#include "webrtc/test/testsupport/fileutils.h" + +CmdArgs::CmdArgs() + : codecName("VP8"), + codecType(webrtc::kVideoCodecVP8), + width(352), + height(288), + bitRate(500), + frameRate(30), + packetLoss(0), + rtt(0), + protectionMode(0), + camaEnable(0), + inputFile(webrtc::test::ProjectRootPath() + "/resources/foreman_cif.yuv"), + outputFile(webrtc::test::OutputPath() + + "video_coding_test_output_352x288.yuv"), + fv_outputfile(webrtc::test::OutputPath() + "features.txt"), + testNum(0) { +} // Normal Distribution #define PI 3.14159265 -double -NormalDist(double mean, double stdDev) +double NormalDist(double mean, double stdDev) { // Creating a Normal distribution variable from two independent uniform // variables based on the Box-Muller transform - double uniform1 = (std::rand() + 1.0) / (RAND_MAX + 1.0); - double uniform2 = (std::rand() + 1.0) / (RAND_MAX + 1.0); + double uniform1 = (rand() + 1.0) / (RAND_MAX + 1.0); + double uniform2 = (rand() + 1.0) / (RAND_MAX + 1.0); return (mean + stdDev * sqrt(-2 * log(uniform1)) * cos(2 * PI * uniform2)); } -RTPVideoCodecTypes -ConvertCodecType(const char* plname) -{ - if (strncmp(plname,"VP8" , 3) == 0) - { - return kRTPVideoVP8; - } - else if (strncmp(plname,"I420" , 5) == 0) - { - return kRTPVideoI420; - } - else - { - return kRTPVideoNoVideo; // Default value - } +namespace { + +void SplitFilename(const std::string& filename, std::string* basename, + std::string* extension) { + assert(basename); + assert(extension); + + std::string::size_type idx; + idx = filename.rfind('.'); + + if(idx != std::string::npos) { + *basename = filename.substr(0, idx); + *extension = filename.substr(idx + 1); + } else { + *basename = filename; + *extension = ""; + } } +std::string AppendWidthHeightCount(const std::string& filename, int width, + int height, int count) { + std::string basename; + std::string extension; + SplitFilename(filename, &basename, &extension); + std::stringstream ss; + ss << basename << "_" << count << "." << width << "_" << height << "." << + extension; + return ss.str(); +} + +} // namespace + +FileOutputFrameReceiver::FileOutputFrameReceiver( + const std::string& base_out_filename, uint32_t ssrc) + : out_filename_(), + out_file_(NULL), + timing_file_(NULL), + width_(0), + height_(0), + count_(0) { + std::string basename; + std::string extension; + if (base_out_filename == "") { + basename = webrtc::test::OutputPath() + "rtp_decoded"; + extension = "yuv"; + } else { + SplitFilename(base_out_filename, &basename, &extension); + } + std::stringstream ss; + ss << basename << "_" << std::hex << std::setw(8) << std::setfill('0') << + ssrc << "." << extension; + out_filename_ = ss.str(); +} + +FileOutputFrameReceiver::~FileOutputFrameReceiver() { + if (timing_file_ != NULL) { + fclose(timing_file_); + } + if (out_file_ != NULL) { + fclose(out_file_); + } +} + +WebRtc_Word32 FileOutputFrameReceiver::FrameToRender( + webrtc::I420VideoFrame& video_frame) { + if (timing_file_ == NULL) { + std::string basename; + std::string extension; + SplitFilename(out_filename_, &basename, &extension); + timing_file_ = fopen((basename + "_renderTiming.txt").c_str(), "w"); + if (timing_file_ == NULL) { + return -1; + } + } + if (out_file_ == NULL || video_frame.width() != width_ || + video_frame.height() != height_) { + if (out_file_) { + fclose(out_file_); + } + printf("New size: %dx%d\n", video_frame.width(), video_frame.height()); + width_ = video_frame.width(); + height_ = video_frame.height(); + std::string filename_with_width_height = AppendWidthHeightCount( + out_filename_, width_, height_, count_); + ++count_; + out_file_ = fopen(filename_with_width_height.c_str(), "wb"); + if (out_file_ == NULL) { + return -1; + } + } + fprintf(timing_file_, "%u, %u\n", video_frame.timestamp(), + webrtc::MaskWord64ToUWord32(video_frame.render_time_ms())); + if (PrintI420VideoFrame(video_frame, out_file_) < 0) { + return -1; + } + return 0; +} + +webrtc::RTPVideoCodecTypes ConvertCodecType(const char* plname) { + if (strncmp(plname,"VP8" , 3) == 0) { + return webrtc::kRTPVideoVP8; + } else if (strncmp(plname,"I420" , 5) == 0) { + return webrtc::kRTPVideoI420; + } else { + return webrtc::kRTPVideoNoVideo; // Default value + } +} diff --git a/webrtc/modules/video_coding/main/test/test_util.h b/webrtc/modules/video_coding/main/test/test_util.h index 1e0537aea1..cfbd24bc41 100644 --- a/webrtc/modules/video_coding/main/test/test_util.h +++ b/webrtc/modules/video_coding/main/test/test_util.h @@ -15,56 +15,37 @@ * General declarations used through out VCM offline tests. */ -#include -#include -#include +#include #include "webrtc/modules/interface/module_common_types.h" #include "webrtc/modules/video_coding/main/interface/video_coding.h" #include "webrtc/system_wrappers/interface/event_wrapper.h" -#include "webrtc/test/testsupport/fileutils.h" +#include "webrtc/system_wrappers/interface/constructor_magic.h" enum { kMaxNackListSize = 250 }; enum { kMaxPacketAgeToNack = 450 }; // Class used for passing command line arguments to tests -class CmdArgs -{ +class CmdArgs { public: - CmdArgs() - : codecName("VP8"), - codecType(webrtc::kVideoCodecVP8), - width(352), - height(288), - bitRate(500), - frameRate(30), - packetLoss(0), - rtt(0), - protectionMode(0), - camaEnable(0), - inputFile(webrtc::test::ProjectRootPath() + - "/resources/foreman_cif.yuv"), - outputFile(webrtc::test::OutputPath() + - "video_coding_test_output_352x288.yuv"), - fv_outputfile(webrtc::test::OutputPath() + "features.txt"), - testNum(0) {} - std::string codecName; - webrtc::VideoCodecType codecType; - int width; - int height; - int bitRate; - int frameRate; - int packetLoss; - int rtt; - int protectionMode; - int camaEnable; - std::string inputFile; - std::string outputFile; - std::string fv_outputfile; - int testNum; + CmdArgs(); + + std::string codecName; + webrtc::VideoCodecType codecType; + int width; + int height; + int bitRate; + int frameRate; + int packetLoss; + int rtt; + int protectionMode; + int camaEnable; + std::string inputFile; + std::string outputFile; + std::string fv_outputfile; + int testNum; }; -// forward declaration int MTRxTxTest(CmdArgs& args); double NormalDist(double mean, double stdDev); @@ -100,8 +81,27 @@ class NullEventFactory : public webrtc::EventFactory { } }; +class FileOutputFrameReceiver : public webrtc::VCMReceiveCallback { + public: + FileOutputFrameReceiver(const std::string& base_out_filename, uint32_t ssrc); + virtual ~FileOutputFrameReceiver(); + + // VCMReceiveCallback + virtual WebRtc_Word32 FrameToRender(webrtc::I420VideoFrame& video_frame); + + private: + std::string out_filename_; + uint32_t ssrc_; + FILE* out_file_; + FILE* timing_file_; + int width_; + int height_; + int count_; + + DISALLOW_IMPLICIT_CONSTRUCTORS(FileOutputFrameReceiver); +}; + // Codec type conversion -webrtc::RTPVideoCodecTypes -ConvertCodecType(const char* plname); +webrtc::RTPVideoCodecTypes ConvertCodecType(const char* plname); #endif diff --git a/webrtc/modules/video_coding/main/test/vcm_payload_sink_factory.cc b/webrtc/modules/video_coding/main/test/vcm_payload_sink_factory.cc new file mode 100644 index 0000000000..0026767072 --- /dev/null +++ b/webrtc/modules/video_coding/main/test/vcm_payload_sink_factory.cc @@ -0,0 +1,249 @@ +/* + * 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/vcm_payload_sink_factory.h" + +#include +#include + +#include "webrtc/modules/rtp_rtcp/interface/rtp_rtcp.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" + +namespace webrtc { +namespace rtpplayer { + +class VcmPayloadSinkFactory::VcmPayloadSink + : public PayloadSinkInterface, + public VCMPacketRequestCallback, + public VCMFrameStorageCallback { + public: + VcmPayloadSink(VcmPayloadSinkFactory* factory, + RtpStreamInterface* stream, + scoped_ptr* vcm, + scoped_ptr* vcm_playback, + scoped_ptr* frame_receiver) + : factory_(factory), + stream_(stream), + vcm_(), + vcm_playback_(), + frame_receiver_() { + assert(factory); + assert(stream); + assert(vcm); + assert(vcm->get()); + assert(vcm_playback); + assert(frame_receiver); + assert(frame_receiver->get()); + vcm_.swap(*vcm); + vcm_playback_.swap(*vcm_playback); + frame_receiver_.swap(*frame_receiver); + vcm_->RegisterPacketRequestCallback(this); + if (vcm_playback_.get() == NULL) { + vcm_->RegisterReceiveCallback(frame_receiver_.get()); + } else { + vcm_->RegisterFrameStorageCallback(this); + vcm_playback_->RegisterReceiveCallback(frame_receiver_.get()); + } + } + + virtual ~VcmPayloadSink() { + factory_->Remove(this); + } + + // PayloadSinkInterface + virtual WebRtc_Word32 OnReceivedPayloadData(const WebRtc_UWord8* payload_data, + const WebRtc_UWord16 payload_size, + const WebRtcRTPHeader* rtp_header) { + return vcm_->IncomingPacket(payload_data, payload_size, *rtp_header); + } + + // VCMPacketRequestCallback + virtual WebRtc_Word32 ResendPackets(const WebRtc_UWord16* sequence_numbers, + WebRtc_UWord16 length) { + stream_->ResendPackets(sequence_numbers, length); + return 0; + } + + // VCMFrameStorageCallback + virtual WebRtc_Word32 StoreReceivedFrame( + const EncodedVideoData& frame_to_store) { + vcm_playback_->DecodeFromStorage(frame_to_store); + return VCM_OK; + } + + int DecodeAndProcess(bool should_decode, bool decode_dual_frame) { + if (should_decode) { + if (vcm_->Decode() < 0) { + return -1; + } + } + while (decode_dual_frame && vcm_->DecodeDualFrame(0) == 1) { + } + return Process() ? 0 : -1; + } + + bool Process() { + if (vcm_->TimeUntilNextProcess() <= 0) { + if (vcm_->Process() < 0) { + return false; + } + } + return true; + } + + bool Decode() { + vcm_->Decode(10000); + while (vcm_->DecodeDualFrame(0) == 1) { + } + return true; + } + + private: + VcmPayloadSinkFactory* factory_; + RtpStreamInterface* stream_; + scoped_ptr vcm_; + scoped_ptr vcm_playback_; + scoped_ptr frame_receiver_; + + DISALLOW_IMPLICIT_CONSTRUCTORS(VcmPayloadSink); +}; + +VcmPayloadSinkFactory::VcmPayloadSinkFactory( + const std::string& base_out_filename, Clock* clock, bool protection_enabled, + VCMVideoProtection protection_method, uint32_t rtt_ms, + uint32_t render_delay_ms, uint32_t min_playout_delay_ms, + bool use_frame_storage) + : base_out_filename_(base_out_filename), + clock_(clock), + protection_enabled_(protection_enabled), + protection_method_(protection_method), + rtt_ms_(rtt_ms), + render_delay_ms_(render_delay_ms), + min_playout_delay_ms_(min_playout_delay_ms), + use_frame_storage_(use_frame_storage), + null_event_factory_(new NullEventFactory()), + crit_sect_(CriticalSectionWrapper::CreateCriticalSection()), + sinks_(), + next_id_(1) { + assert(clock); + assert(crit_sect_.get()); +} + +VcmPayloadSinkFactory::~VcmPayloadSinkFactory() { + assert(sinks_.empty()); +} + +PayloadSinkInterface* VcmPayloadSinkFactory::Create( + RtpStreamInterface* stream) { + assert(stream); + CriticalSectionScoped cs(crit_sect_.get()); + + scoped_ptr vcm( + VideoCodingModule::Create(next_id_++, clock_, null_event_factory_.get())); + if (vcm.get() == NULL) { + return NULL; + } + if (vcm->InitializeReceiver() < 0) { + return NULL; + } + + scoped_ptr vcm_playback; + if (use_frame_storage_) { + vcm_playback.reset( + VideoCodingModule::Create(next_id_++, clock_, + null_event_factory_.get())); + if (vcm_playback.get() == NULL) { + return NULL; + } + if (vcm_playback->InitializeReceiver() < 0) { + return NULL; + } + } + + const PayloadTypes& plt = stream->payload_types(); + for (PayloadTypesIterator it = plt.begin(); it != plt.end(); + ++it) { + if (it->codec_type() != kVideoCodecULPFEC && + it->codec_type() != kVideoCodecRED) { + VideoCodec codec; + if (VideoCodingModule::Codec(it->codec_type(), &codec) < 0) { + return NULL; + } + codec.plType = it->payload_type(); + if (vcm->RegisterReceiveCodec(&codec, 1) < 0) { + return NULL; + } + if (use_frame_storage_) { + if (vcm_playback->RegisterReceiveCodec(&codec, 1) < 0) { + return NULL; + } + } + } + } + + vcm->SetChannelParameters(0, 0, rtt_ms_); + vcm->SetVideoProtection(protection_method_, protection_enabled_); + vcm->SetRenderDelay(render_delay_ms_); + vcm->SetMinimumPlayoutDelay(min_playout_delay_ms_); + vcm->SetNackSettings(kMaxNackListSize, kMaxPacketAgeToNack); + + scoped_ptr frame_receiver( + new FileOutputFrameReceiver(base_out_filename_, stream->ssrc())); + scoped_ptr sink( + new VcmPayloadSink(this, stream, &vcm, &vcm_playback, &frame_receiver)); + + sinks_.push_back(sink.get()); + return sink.release(); +} + +int VcmPayloadSinkFactory::DecodeAndProcessAll(bool decode_dual_frame) { + CriticalSectionScoped cs(crit_sect_.get()); + assert(clock_); + bool should_decode = (clock_->TimeInMilliseconds() % 5) == 0; + for (Sinks::iterator it = sinks_.begin(); it != sinks_.end(); ++it) { + if ((*it)->DecodeAndProcess(should_decode, decode_dual_frame) < 0) { + return -1; + } + } + return 0; +} + +bool VcmPayloadSinkFactory::ProcessAll() { + CriticalSectionScoped cs(crit_sect_.get()); + for (Sinks::iterator it = sinks_.begin(); it != sinks_.end(); ++it) { + if (!(*it)->Process()) { + return false; + } + } + return true; +} + +bool VcmPayloadSinkFactory::DecodeAll() { + CriticalSectionScoped cs(crit_sect_.get()); + for (Sinks::iterator it = sinks_.begin(); it != sinks_.end(); ++it) { + if (!(*it)->Decode()) { + return false; + } + } + return true; +} + +void VcmPayloadSinkFactory::Remove(VcmPayloadSink* sink) { + assert(sink); + CriticalSectionScoped cs(crit_sect_.get()); + Sinks::iterator it = std::find(sinks_.begin(), sinks_.end(), sink); + assert(it != sinks_.end()); + sinks_.erase(it); +} + +} // namespace rtpplayer +} // namespace webrtc diff --git a/webrtc/modules/video_coding/main/test/vcm_payload_sink_factory.h b/webrtc/modules/video_coding/main/test/vcm_payload_sink_factory.h new file mode 100644 index 0000000000..aa4dfa3da8 --- /dev/null +++ b/webrtc/modules/video_coding/main/test/vcm_payload_sink_factory.h @@ -0,0 +1,66 @@ +/* + * 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 +#include + +#include "webrtc/modules/video_coding/main/interface/video_coding_defines.h" +#include "webrtc/modules/video_coding/main/test/rtp_player.h" +#include "webrtc/system_wrappers/interface/constructor_magic.h" +#include "webrtc/system_wrappers/interface/scoped_ptr.h" + +class NullEventFactory; + +namespace webrtc { +class Clock; +class CriticalSectionWrapper; + +namespace rtpplayer { +class VcmPayloadSinkFactory : public PayloadSinkFactoryInterface { + public: + VcmPayloadSinkFactory(const std::string& base_out_filename, + Clock* clock, bool protection_enabled, + VCMVideoProtection protection_method, + uint32_t rtt_ms, uint32_t render_delay_ms, + uint32_t min_playout_delay_ms, + bool use_frame_storage); + virtual ~VcmPayloadSinkFactory(); + + // PayloadSinkFactoryInterface + virtual PayloadSinkInterface* Create(RtpStreamInterface* stream); + + int DecodeAndProcessAll(bool decode_dual_frame); + bool ProcessAll(); + bool DecodeAll(); + + private: + class VcmPayloadSink; + friend class VcmPayloadSink; + typedef std::vector Sinks; + + void Remove(VcmPayloadSink* sink); + + std::string base_out_filename_; + Clock* clock_; + bool protection_enabled_; + VCMVideoProtection protection_method_; + uint32_t rtt_ms_; + uint32_t render_delay_ms_; + uint32_t min_playout_delay_ms_; + bool use_frame_storage_; + scoped_ptr null_event_factory_; + scoped_ptr crit_sect_; + Sinks sinks_; + int next_id_; + + DISALLOW_IMPLICIT_CONSTRUCTORS(VcmPayloadSinkFactory); +}; +} // namespace rtpplayer +} // namespace webrtc diff --git a/webrtc/modules/video_coding/main/test/video_rtp_play.cc b/webrtc/modules/video_coding/main/test/video_rtp_play.cc index 020a5bcdec..6591a82366 100644 --- a/webrtc/modules/video_coding/main/test/video_rtp_play.cc +++ b/webrtc/modules/video_coding/main/test/video_rtp_play.cc @@ -8,239 +8,79 @@ * be found in the AUTHORS file in the root of the source tree. */ -#include -#include -#include - -#include "webrtc/common_video/libyuv/include/webrtc_libyuv.h" -#include "webrtc/modules/rtp_rtcp/interface/rtp_rtcp.h" -#include "webrtc/modules/video_coding/main/interface/video_coding.h" -#include "webrtc/modules/video_coding/main/source/internal_defines.h" #include "webrtc/modules/video_coding/main/test/receiver_tests.h" -#include "webrtc/modules/video_coding/main/test/test_macros.h" -#include "webrtc/modules/video_coding/main/test/rtp_player.h" -#include "webrtc/system_wrappers/interface/clock.h" +#include "webrtc/modules/video_coding/main/test/vcm_payload_sink_factory.h" #include "webrtc/system_wrappers/interface/trace.h" +#include "webrtc/test/testsupport/fileutils.h" -using namespace webrtc; +namespace { -int32_t -RtpDataCallback::OnReceivedPayloadData(const uint8_t* payloadData, - const uint16_t payloadSize, - const WebRtcRTPHeader* rtpHeader) -{ - return _vcm->IncomingPacket(payloadData, payloadSize, *rtpHeader); -} +const bool kConfigProtectionEnabled = true; +const webrtc::VCMVideoProtection kConfigProtectionMethod = + webrtc::kProtectionNack; +const float kConfigLossRate = 0.0f; +const bool kConfigReordering = false; +const uint32_t kConfigRttMs = 0; +const uint32_t kConfigRenderDelayMs = 0; +const uint32_t kConfigMinPlayoutDelayMs = 0; +const int64_t kConfigMaxRuntimeMs = -1; -FrameReceiveCallback::~FrameReceiveCallback() -{ - if (_timingFile != NULL) - { - fclose(_timingFile); - } - if (_outFile != NULL) - { - fclose(_outFile); - } -} +} // namespace -int32_t -FrameReceiveCallback::FrameToRender(I420VideoFrame& videoFrame) -{ - if (_timingFile == NULL) - { - _timingFile = fopen((test::OutputPath() + "renderTiming.txt").c_str(), - "w"); - if (_timingFile == NULL) - { - return -1; - } - } - if (_outFile == NULL || - videoFrame.width() != static_cast(width_) || - videoFrame.height() != static_cast(height_)) - { - if (_outFile) { - fclose(_outFile); - } - printf("New size: %ux%u\n", videoFrame.width(), videoFrame.height()); - width_ = videoFrame.width(); - height_ = videoFrame.height(); - std::string filename_with_width_height = AppendWidthHeightAndCount( - _outFilename, width_, height_, count_); - ++count_; - _outFile = fopen(filename_with_width_height.c_str(), "wb"); - if (_outFile == NULL) - { - return -1; - } - } - fprintf(_timingFile, "%u, %u\n", - videoFrame.timestamp(), - MaskWord64ToUWord32(videoFrame.render_time_ms())); - if (PrintI420VideoFrame(videoFrame, _outFile) < 0) { - return -1; - } - return 0; -} +int RtpPlay(const CmdArgs& args) { + std::string trace_file = webrtc::test::OutputPath() + "receiverTestTrace.txt"; + webrtc::Trace::CreateTrace(); + webrtc::Trace::SetTraceFile(trace_file.c_str()); + webrtc::Trace::SetLevelFilter(webrtc::kTraceAll); -void FrameReceiveCallback::SplitFilename(std::string filename, - std::string* basename, - std::string* ending) { - std::string::size_type idx; - idx = filename.rfind('.'); + webrtc::rtpplayer::PayloadTypes payload_types; + payload_types.push_back(webrtc::rtpplayer::PayloadCodecTuple( + VCM_ULPFEC_PAYLOAD_TYPE, "ULPFEC", webrtc::kVideoCodecULPFEC)); + payload_types.push_back(webrtc::rtpplayer::PayloadCodecTuple( + VCM_RED_PAYLOAD_TYPE, "RED", webrtc::kVideoCodecRED)); + payload_types.push_back(webrtc::rtpplayer::PayloadCodecTuple( + VCM_VP8_PAYLOAD_TYPE, "VP8", webrtc::kVideoCodecVP8)); - if(idx != std::string::npos) { - *ending = filename.substr(idx + 1); - *basename = filename.substr(0, idx); - } else { - *basename = filename; - *ending = ""; + std::string output_file = args.outputFile; + if (output_file == "") { + output_file = webrtc::test::OutputPath() + "RtpPlay_decoded.yuv"; } -} -std::string FrameReceiveCallback::AppendWidthHeightAndCount( - std::string filename, unsigned int width, unsigned int height, int count) { - std::string basename; - std::string ending; - SplitFilename(filename, &basename, &ending); - std::stringstream ss; - ss << basename << "_" << count << "." << width << "_" << height << "." << - ending; - return ss.str(); -} -int RtpPlay(CmdArgs& args) -{ - // BEGIN Settings - bool protectionEnabled = true; - VCMVideoProtection protectionMethod = kProtectionNack; - uint32_t rttMS = 0; - float lossRate = 0.0f; - bool reordering = false; - uint32_t renderDelayMs = 0; - uint32_t minPlayoutDelayMs = 0; - const int64_t MAX_RUNTIME_MS = -1; - std::string outFile = args.outputFile; - if (outFile == "") - outFile = test::OutputPath() + "RtpPlay_decoded.yuv"; - FrameReceiveCallback receiveCallback(outFile); - SimulatedClock clock(0); - NullEventFactory event_factory; - VideoCodingModule* vcm = VideoCodingModule::Create(1, &clock, - &event_factory); - RtpDataCallback dataCallback(vcm); - RTPPlayer rtpStream(args.inputFile.c_str(), &dataCallback, &clock); + webrtc::SimulatedClock clock(0); + webrtc::rtpplayer::VcmPayloadSinkFactory factory(output_file, &clock, + kConfigProtectionEnabled, kConfigProtectionMethod, kConfigRttMs, + kConfigRenderDelayMs, kConfigMinPlayoutDelayMs, false); + webrtc::scoped_ptr rtp_player( + webrtc::rtpplayer::Create(args.inputFile, &factory, &clock, payload_types, + kConfigLossRate, kConfigRttMs, kConfigReordering)); + if (rtp_player.get() == NULL) { + return -1; + } - - PayloadTypeList payloadTypes; - payloadTypes.push_front(new PayloadCodecTuple(VCM_VP8_PAYLOAD_TYPE, "VP8", - kVideoCodecVP8)); - payloadTypes.push_front(new PayloadCodecTuple(VCM_RED_PAYLOAD_TYPE, "RED", - kVideoCodecRED)); - payloadTypes.push_front(new PayloadCodecTuple(VCM_ULPFEC_PAYLOAD_TYPE, - "ULPFEC", kVideoCodecULPFEC)); - - Trace::CreateTrace(); - Trace::SetTraceFile((test::OutputPath() + "receiverTestTrace.txt").c_str()); - Trace::SetLevelFilter(webrtc::kTraceAll); - // END Settings - - // Set up - - int32_t ret = vcm->InitializeReceiver(); - if (ret < 0) - { - return -1; + int ret = 0; + while ((ret = rtp_player->NextPacket(clock.TimeInMilliseconds())) == 0) { + ret = factory.DecodeAndProcessAll(true); + if (ret < 0 || (kConfigMaxRuntimeMs > -1 && + clock.TimeInMilliseconds() >= kConfigMaxRuntimeMs)) { + break; } - vcm->RegisterReceiveCallback(&receiveCallback); - vcm->RegisterPacketRequestCallback(&rtpStream); + clock.AdvanceTimeMilliseconds(1); + } - // Register receive codecs in VCM - for (PayloadTypeList::iterator it = payloadTypes.begin(); - it != payloadTypes.end(); ++it) { - PayloadCodecTuple* payloadType = *it; - if (payloadType != NULL) - { - VideoCodec codec; - if (payloadType->codecType != kVideoCodecULPFEC && - payloadType->codecType != kVideoCodecRED) { - if (VideoCodingModule::Codec(payloadType->codecType, &codec) < 0) - { - return -1; - } - codec.plType = payloadType->payloadType; - if (vcm->RegisterReceiveCodec(&codec, 1) < 0) - { - return -1; - } - } - } - } + rtp_player->Print(); - if (rtpStream.Initialize(&payloadTypes) < 0) - { - return -1; - } - bool nackEnabled = protectionEnabled && - (protectionMethod == kProtectionNack || - protectionMethod == kProtectionDualDecoder); - rtpStream.SimulatePacketLoss(lossRate, nackEnabled, rttMS); - rtpStream.SetReordering(reordering); - vcm->SetChannelParameters(0, 0, rttMS); - vcm->SetVideoProtection(protectionMethod, protectionEnabled); - vcm->SetRenderDelay(renderDelayMs); - vcm->SetMinimumPlayoutDelay(minPlayoutDelayMs); - vcm->SetNackSettings(kMaxNackListSize, kMaxPacketAgeToNack); - - ret = 0; - - // RTP stream main loop - while ((ret = rtpStream.NextPacket(clock.TimeInMilliseconds())) == 0) - { - if (clock.TimeInMilliseconds() % 5 == 0) - { - ret = vcm->Decode(); - if (ret < 0) - { - return -1; - } - } - while (vcm->DecodeDualFrame(0) == 1) { - } - if (vcm->TimeUntilNextProcess() <= 0) - { - vcm->Process(); - } - if (MAX_RUNTIME_MS > -1 && clock.TimeInMilliseconds() >= - MAX_RUNTIME_MS) - { - break; - } - clock.AdvanceTimeMilliseconds(1); - } - - // Tear down - while (!payloadTypes.empty()) - { - delete payloadTypes.front(); - payloadTypes.pop_front(); - } - delete vcm; - vcm = NULL; - rtpStream.Print(); - Trace::ReturnTrace(); - - switch (ret) - { + switch (ret) { case 1: - printf("Success\n"); - return 0; + printf("Success\n"); + return 0; case -1: - printf("Failed\n"); - return -1; + printf("Failed\n"); + return -1; case 0: - printf("Timeout\n"); - return -1; - } - return 0; + printf("Timeout\n"); + return -1; + } + + webrtc::Trace::ReturnTrace(); + return 0; } diff --git a/webrtc/modules/video_coding/main/test/video_rtp_play_mt.cc b/webrtc/modules/video_coding/main/test/video_rtp_play_mt.cc index 1e43b92460..1ff05dd104 100644 --- a/webrtc/modules/video_coding/main/test/video_rtp_play_mt.cc +++ b/webrtc/modules/video_coding/main/test/video_rtp_play_mt.cc @@ -8,250 +8,136 @@ * be found in the AUTHORS file in the root of the source tree. */ -#include +#include -#include "webrtc/modules/rtp_rtcp/interface/rtp_rtcp.h" -#include "webrtc/modules/video_coding/main/interface/video_coding.h" #include "webrtc/modules/video_coding/main/test/receiver_tests.h" -#include "webrtc/modules/video_coding/main/test/rtp_player.h" -#include "webrtc/modules/video_coding/main/test/test_macros.h" -#include "webrtc/system_wrappers/interface/clock.h" +#include "webrtc/modules/video_coding/main/test/vcm_payload_sink_factory.h" #include "webrtc/system_wrappers/interface/event_wrapper.h" #include "webrtc/system_wrappers/interface/thread_wrapper.h" #include "webrtc/system_wrappers/interface/trace.h" +#include "webrtc/test/testsupport/fileutils.h" -using namespace webrtc; +using webrtc::rtpplayer::RtpPlayerInterface; +using webrtc::rtpplayer::VcmPayloadSinkFactory; -bool ProcessingThread(void* obj) -{ - SharedState* state = static_cast(obj); - if (state->_vcm.TimeUntilNextProcess() <= 0) - { - if (state->_vcm.Process() < 0) - { - return false; - } - } - return true; +namespace { + +const bool kConfigProtectionEnabled = true; +const webrtc::VCMVideoProtection kConfigProtectionMethod = + webrtc::kProtectionDualDecoder; +const float kConfigLossRate = 0.05f; +const uint32_t kConfigRttMs = 50; +const bool kConfigReordering = false; +const uint32_t kConfigRenderDelayMs = 0; +const uint32_t kConfigMinPlayoutDelayMs = 0; +const int64_t kConfigMaxRuntimeMs = 10000; + +} // namespace + +bool PlayerThread(void* obj) { + assert(obj); + RtpPlayerInterface* rtp_player = static_cast(obj); + + webrtc::scoped_ptr wait_event( + webrtc::EventWrapper::Create()); + if (wait_event.get() == NULL) { + return false; + } + + webrtc::Clock* clock = webrtc::Clock::GetRealTimeClock(); + if (rtp_player->NextPacket(clock->TimeInMilliseconds()) < 0) { + return false; + } + wait_event->Wait(rtp_player->TimeUntilNextPacket()); + + return true; } -bool RtpReaderThread(void* obj) -{ - SharedState* state = static_cast(obj); - EventWrapper& waitEvent = *EventWrapper::Create(); - // RTP stream main loop - Clock* clock = Clock::GetRealTimeClock(); - if (state->_rtpPlayer.NextPacket(clock->TimeInMilliseconds()) < 0) - { - return false; - } - waitEvent.Wait(state->_rtpPlayer.TimeUntilNextPacket()); - delete &waitEvent; - return true; +bool ProcessingThread(void* obj) { + assert(obj); + return static_cast(obj)->ProcessAll(); } -bool DecodeThread(void* obj) -{ - SharedState* state = static_cast(obj); - state->_vcm.Decode(10000); - while (state->_vcm.DecodeDualFrame(0) == 1) { - } - return true; +bool DecodeThread(void* obj) { + assert(obj); + return static_cast(obj)->DecodeAll(); } -int RtpPlayMT(CmdArgs& args, int releaseTestNo, webrtc::VideoCodecType releaseTestVideoType) -{ - // BEGIN Settings - bool protectionEnabled = true; - VCMVideoProtection protection = kProtectionDualDecoder; - uint8_t rttMS = 50; - float lossRate = 0.05f; - uint32_t renderDelayMs = 0; - uint32_t minPlayoutDelayMs = 0; - const int64_t MAX_RUNTIME_MS = 10000; - std::string outFilename = args.outputFile; - if (outFilename == "") - outFilename = test::OutputPath() + "RtpPlayMT_decoded.yuv"; +int RtpPlayMT(const CmdArgs& args) { + std::string trace_file = webrtc::test::OutputPath() + "receiverTestTrace.txt"; + webrtc::Trace::CreateTrace(); + webrtc::Trace::SetTraceFile(trace_file.c_str()); + webrtc::Trace::SetLevelFilter(webrtc::kTraceAll); - bool nackEnabled = (protectionEnabled && - (protection == kProtectionDualDecoder || - protection == kProtectionNack || - kProtectionNackFEC)); - VideoCodingModule* vcm = VideoCodingModule::Create(1); - RtpDataCallback dataCallback(vcm); - std::string rtpFilename; - rtpFilename = args.inputFile; - if (releaseTestNo > 0) - { - // Setup a release test - switch (releaseTestVideoType) - { - case webrtc::kVideoCodecVP8: - rtpFilename = args.inputFile; - outFilename = test::OutputPath() + "MTReceiveTest_VP8"; - break; - default: - return -1; - } - switch (releaseTestNo) - { - case 1: - // Normal execution - protectionEnabled = false; - nackEnabled = false; - rttMS = 0; - lossRate = 0.0f; - outFilename += "_Normal.yuv"; - break; - case 2: - // Packet loss - protectionEnabled = false; - nackEnabled = false; - rttMS = 0; - lossRate = 0.05f; - outFilename += "_0.05.yuv"; - break; - case 3: - // Packet loss and NACK - protection = kProtectionNack; - nackEnabled = true; - protectionEnabled = true; - rttMS = 100; - lossRate = 0.05f; - outFilename += "_0.05_NACK_100ms.yuv"; - break; - case 4: - // Packet loss and dual decoder - // Not implemented - return 0; - break; - default: - return -1; - } - printf("Watch %s to verify that the output is reasonable\n", outFilename.c_str()); - } - RTPPlayer rtpStream(rtpFilename.c_str(), &dataCallback, - Clock::GetRealTimeClock()); - PayloadTypeList payloadTypes; - payloadTypes.push_front(new PayloadCodecTuple(VCM_VP8_PAYLOAD_TYPE, "VP8", - kVideoCodecVP8)); - Trace::CreateTrace(); - Trace::SetTraceFile("receiverTestTrace.txt"); - Trace::SetLevelFilter(webrtc::kTraceAll); + webrtc::rtpplayer::PayloadTypes payload_types; + payload_types.push_back(webrtc::rtpplayer::PayloadCodecTuple( + VCM_VP8_PAYLOAD_TYPE, "VP8", webrtc::kVideoCodecVP8)); - // END Settings + std::string output_file = args.outputFile; + if (output_file == "") { + output_file = webrtc::test::OutputPath() + "RtpPlayMT_decoded.yuv"; + } - // Set up + webrtc::Clock* clock = webrtc::Clock::GetRealTimeClock(); + VcmPayloadSinkFactory factory(output_file, clock, kConfigProtectionEnabled, + kConfigProtectionMethod, kConfigRttMs, kConfigRenderDelayMs, + kConfigMinPlayoutDelayMs, false); + webrtc::scoped_ptr rtp_player(webrtc::rtpplayer::Create( + args.inputFile, &factory, clock, payload_types, kConfigLossRate, + kConfigRttMs, kConfigReordering)); + if (rtp_player.get() == NULL) { + return -1; + } - SharedState mtState(*vcm, rtpStream); - - if (rtpStream.Initialize(&payloadTypes) < 0) - { - return -1; - } - rtpStream.SimulatePacketLoss(lossRate, nackEnabled, rttMS); - - int32_t ret = vcm->InitializeReceiver(); - if (ret < 0) - { - return -1; + { + webrtc::scoped_ptr player_thread( + webrtc::ThreadWrapper::CreateThread(PlayerThread, rtp_player.get(), + webrtc::kNormalPriority, "PlayerThread")); + if (player_thread.get() == NULL) { + printf("Unable to start RTP reader thread\n"); + return -1; } - // Create and start all threads - ThreadWrapper* processingThread = ThreadWrapper::CreateThread( - ProcessingThread, &mtState, kNormalPriority, "ProcessingThread"); - ThreadWrapper* rtpReaderThread = ThreadWrapper::CreateThread( - RtpReaderThread, &mtState, kNormalPriority, "RtpReaderThread"); - ThreadWrapper* decodeThread = ThreadWrapper::CreateThread(DecodeThread, - &mtState, kNormalPriority, "DecodeThread"); - - // Register receive codecs in VCM - for (PayloadTypeList::iterator it = payloadTypes.begin(); - it != payloadTypes.end(); ++it) { - PayloadCodecTuple* payloadType = *it; - if (payloadType != NULL) - { - VideoCodec codec; - VideoCodingModule::Codec(payloadType->codecType, &codec); - codec.plType = payloadType->payloadType; - if (vcm->RegisterReceiveCodec(&codec, 1) < 0) - { - return -1; - } - } + webrtc::scoped_ptr processing_thread( + webrtc::ThreadWrapper::CreateThread(ProcessingThread, &factory, + webrtc::kNormalPriority, "ProcessingThread")); + if (processing_thread.get() == NULL) { + printf("Unable to start processing thread\n"); + return -1; } - if (processingThread != NULL) - { - unsigned int tid; - processingThread->Start(tid); - } - else - { - printf("Unable to start processing thread\n"); - return -1; - } - if (rtpReaderThread != NULL) - { - unsigned int tid; - rtpReaderThread->Start(tid); - } - else - { - printf("Unable to start RTP reader thread\n"); - return -1; - } - if (decodeThread != NULL) - { - unsigned int tid; - decodeThread->Start(tid); - } - else - { - printf("Unable to start decode thread\n"); - return -1; + webrtc::scoped_ptr decode_thread( + webrtc::ThreadWrapper::CreateThread(DecodeThread, &factory, + webrtc::kNormalPriority, "DecodeThread")); + if (decode_thread.get() == NULL) { + printf("Unable to start decode thread\n"); + return -1; } - FrameReceiveCallback receiveCallback(outFilename); - vcm->RegisterReceiveCallback(&receiveCallback); - vcm->RegisterPacketRequestCallback(&rtpStream); - - vcm->SetChannelParameters(0, 0, rttMS); - vcm->SetVideoProtection(protection, protectionEnabled); - vcm->SetRenderDelay(renderDelayMs); - vcm->SetMinimumPlayoutDelay(minPlayoutDelayMs); - vcm->SetNackSettings(kMaxNackListSize, kMaxPacketAgeToNack); - - EventWrapper& waitEvent = *EventWrapper::Create(); - - // Decode for 10 seconds and then tear down and exit. - waitEvent.Wait(MAX_RUNTIME_MS); - - // Tear down - while (!payloadTypes.empty()) - { - delete payloadTypes.front(); - payloadTypes.pop_front(); + webrtc::scoped_ptr wait_event( + webrtc::EventWrapper::Create()); + if (wait_event.get() == NULL) { + printf("Unable to create wait event\n"); + return -1; } - while (!processingThread->Stop()) - { - ; + + unsigned int dummy_thread_id; + player_thread->Start(dummy_thread_id); + processing_thread->Start(dummy_thread_id); + decode_thread->Start(dummy_thread_id); + + wait_event->Wait(kConfigMaxRuntimeMs); + + while (!player_thread->Stop()) { } - while (!rtpReaderThread->Stop()) - { - ; + while (!processing_thread->Stop()) { } - while (!decodeThread->Stop()) - { - ; + while (!decode_thread->Stop()) { } - VideoCodingModule::Destroy(vcm); - vcm = NULL; - delete &waitEvent; - delete processingThread; - delete decodeThread; - delete rtpReaderThread; - rtpStream.Print(); - Trace::ReturnTrace(); - return 0; + } + + rtp_player->Print(); + + webrtc::Trace::ReturnTrace(); + return 0; }