RTP video playback tool using Call APIs.

Plays back rtpdump files from Wireshark in realtime as well as save the
resulting raw video to file. Unlike the RTP playback tool it doesn't
support faster-than-realtime playback/rendering, but it instead utilizes
the same path as production code and also contains support for playing
back FEC.

BUG=
R=stefan@webrtc.org

Review URL: https://webrtc-codereview.appspot.com/16969004

git-svn-id: http://webrtc.googlecode.com/svn/trunk@6838 4adac7df-926f-26a2-2b94-8c16560cd09d
This commit is contained in:
pbos@webrtc.org
2014-08-06 16:26:56 +00:00
parent 1ccff349ee
commit 4b5625e5ac
19 changed files with 646 additions and 467 deletions

View File

@ -21,6 +21,7 @@
'<(webrtc_root)/test/metrics.gyp:metrics',
'<(webrtc_root)/common_video/common_video.gyp:common_video',
'<(webrtc_root)/system_wrappers/source/system_wrappers.gyp:field_trial_default',
'<(webrtc_root)/test/webrtc_test_common.gyp:webrtc_test_common',
],
'sources': [
# headers
@ -30,11 +31,9 @@
'../test/media_opt_test.h',
'../test/mt_test_common.h',
'../test/normal_test.h',
'../test/pcap_file_reader.h',
'../test/quality_modes_test.h',
'../test/receiver_tests.h',
'../test/release_test.h',
'../test/rtp_file_reader.h',
'../test/rtp_player.h',
'../test/test_callbacks.h',
'../test/test_util.h',
@ -48,9 +47,7 @@
'../test/mt_test_common.cc',
'../test/mt_rx_tx_test.cc',
'../test/normal_test.cc',
'../test/pcap_file_reader.cc',
'../test/quality_modes_test.cc',
'../test/rtp_file_reader.cc',
'../test/rtp_player.cc',
'../test/test_callbacks.cc',
'../test/test_util.cc',

View File

@ -1,464 +0,0 @@
/*
* Copyright (c) 2013 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree. An additional intellectual property rights grant can be found
* in the file PATENTS. All contributing project authors may
* be found in the AUTHORS file in the root of the source tree.
*/
#include "webrtc/modules/video_coding/main/test/pcap_file_reader.h"
#ifdef WIN32
#include <windows.h>
#include <Winsock2.h>
#else
#include <arpa/inet.h>
#endif
#include <assert.h>
#include <stdio.h>
#include <map>
#include <string>
#include <vector>
#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 (;;) {
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<int>(packets_.size()));
for (SsrcMapIterator mit = packets_by_ssrc_.begin();
mit != packets_by_ssrc_.end(); ++mit) {
uint32_t ssrc = mit->first;
const std::vector<uint32_t>& packet_numbers = mit->second;
uint8_t pt = packets_[packet_numbers[0]].rtp_header.payloadType;
printf("SSRC: %08x, %d packets, pt=%d\n", ssrc,
static_cast<int>(packet_numbers.size()), pt);
}
// 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; // One-based index (like in WireShark)
uint32_t time_offset_ms;
uint32_t source_ip;
uint32_t dest_ip;
uint16_t source_port;
uint16_t dest_port;
RTPHeader rtp_header;
int32_t pos_in_file; // Byte offset of payload from start of file.
uint32_t payload_length;
};
typedef std::vector<RtpPacketMarker>::iterator PacketIterator;
typedef std::map<uint32_t, std::vector<uint32_t> > SsrcMap;
typedef std::map<uint32_t, std::vector<uint32_t> >::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));
RtpUtility::RtpHeaderParser rtp_parser(read_buffer_, marker.payload_length);
if (rtp_parser.RTCP()) {
rtp_parser.ParseRtcp(&marker.rtp_header);
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.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<uint64_t>(ts_sec) * 1000000) + ts_usec +
500) / 1000;
uint64_t t1_ms = static_cast<uint64_t>(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<RtpPacketMarker> packets_;
PacketIterator next_packet_it_;
DISALLOW_COPY_AND_ASSIGN(PcapFileReaderImpl);
};
RtpPacketSourceInterface* CreatePcapFileReader(const std::string& filename) {
scoped_ptr<PcapFileReaderImpl> impl(new PcapFileReaderImpl());
if (impl->Initialize(filename) != 0) {
return NULL;
}
return impl.release();
}
} // namespace rtpplayer
} // namespace webrtc

View File

@ -1,26 +0,0 @@
/*
* Copyright (c) 2013 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree. An additional intellectual property rights grant can be found
* in the file PATENTS. All contributing project authors may
* be found in the AUTHORS file in the root of the source tree.
*/
#ifndef WEBRTC_MODULES_VIDEO_CODING_TEST_PCAP_FILE_READER_H_
#define WEBRTC_MODULES_VIDEO_CODING_TEST_PCAP_FILE_READER_H_
#include <string>
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_

View File

@ -1,99 +0,0 @@
/*
* Copyright (c) 2013 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree. An additional intellectual property rights grant can be found
* in the file PATENTS. All contributing project authors may
* be found in the AUTHORS file in the root of the source tree.
*/
#include <map>
#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<uint32_t, int> 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;
RtpUtility::RtpHeaderParser rtp_header_parser(data, length);
webrtc::RTPHeader header;
if (!rtp_header_parser.RTCP() && rtp_header_parser.Parse(header, NULL)) {
pps[header.ssrc]++;
}
}
return pps;
}
private:
scoped_ptr<RtpPacketSourceInterface> 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

View File

@ -1,166 +0,0 @@
/*
* Copyright (c) 2013 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree. An additional intellectual property rights grant can be found
* in the file PATENTS. All contributing project authors may
* be found in the AUTHORS file in the root of the source tree.
*/
#include "webrtc/modules/video_coding/main/test/rtp_file_reader.h"
#ifdef WIN32
#include <windows.h>
#include <Winsock2.h>
#else
#include <arpa/inet.h>
#endif
#include <assert.h>
#include <stdio.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,
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<RtpFileReaderImpl> impl(new RtpFileReaderImpl());
if (impl->Initialize(filename) != 0) {
return NULL;
}
return impl.release();
}
} // namespace rtpplayer
} // namespace webrtc

View File

@ -1,26 +0,0 @@
/*
* Copyright (c) 2013 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree. An additional intellectual property rights grant can be found
* in the file PATENTS. All contributing project authors may
* be found in the AUTHORS file in the root of the source tree.
*/
#ifndef WEBRTC_MODULES_VIDEO_CODING_MAIN_TEST_RTP_FILE_READER_H_
#define WEBRTC_MODULES_VIDEO_CODING_MAIN_TEST_RTP_FILE_READER_H_
#include <string>
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_

View File

@ -1,54 +0,0 @@
/*
* Copyright (c) 2013 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree. An additional intellectual property rights grant can be found
* in the file PATENTS. All contributing project authors may
* be found in the AUTHORS file in the root of the source tree.
*/
#include "testing/gtest/include/gtest/gtest.h"
#include "webrtc/modules/rtp_rtcp/source/rtp_utility.h"
#include "webrtc/modules/video_coding/main/test/rtp_file_reader.h"
#include "webrtc/modules/video_coding/main/test/rtp_player.h"
#include "webrtc/system_wrappers/interface/scoped_ptr.h"
#include "webrtc/test/testsupport/fileutils.h"
namespace webrtc {
namespace rtpplayer {
class TestRtpFileReader : public ::testing::Test {
public:
void Init(const std::string& filename) {
std::string filepath =
test::ResourcePath("video_coding/" + filename, "rtp");
rtp_packet_source_.reset(CreateRtpFileReader(filepath));
ASSERT_TRUE(rtp_packet_source_.get() != NULL);
}
int CountRtpPackets() {
const uint32_t kBufferSize = 4096;
uint8_t data[kBufferSize];
uint32_t length = kBufferSize;
uint32_t dummy_time_ms = 0;
int c = 0;
while (rtp_packet_source_->NextPacket(data, &length, &dummy_time_ms) == 0) {
EXPECT_GE(kBufferSize, length);
length = kBufferSize;
c++;
}
return c;
}
private:
scoped_ptr<RtpPacketSourceInterface> rtp_packet_source_;
};
TEST_F(TestRtpFileReader, Test60Packets) {
Init("pltype103");
EXPECT_EQ(60, CountRtpPackets());
}
} // namespace rtpplayer
} // namespace webrtc

View File

@ -19,12 +19,11 @@
#include "webrtc/modules/rtp_rtcp/interface/rtp_receiver.h"
#include "webrtc/modules/rtp_rtcp/interface/rtp_rtcp.h"
#include "webrtc/modules/video_coding/main/source/internal_defines.h"
#include "webrtc/modules/video_coding/main/test/pcap_file_reader.h"
#include "webrtc/modules/video_coding/main/test/rtp_file_reader.h"
#include "webrtc/modules/video_coding/main/test/test_util.h"
#include "webrtc/system_wrappers/interface/clock.h"
#include "webrtc/system_wrappers/interface/critical_section_wrapper.h"
#include "webrtc/system_wrappers/interface/scoped_ptr.h"
#include "webrtc/test/rtp_file_reader.h"
#if 1
# define DEBUG_LOG1(text, arg)
@ -323,7 +322,7 @@ class RtpPlayerImpl : public RtpPlayerInterface {
public:
RtpPlayerImpl(PayloadSinkFactoryInterface* payload_sink_factory,
const PayloadTypes& payload_types, Clock* clock,
scoped_ptr<RtpPacketSourceInterface>* packet_source,
scoped_ptr<test::RtpFileReader>* packet_source,
float loss_rate, uint32_t rtt_ms, bool reordering)
: ssrc_handlers_(payload_sink_factory, payload_types),
clock_(clock),
@ -337,9 +336,7 @@ class RtpPlayerImpl : public RtpPlayerInterface {
no_loss_startup_(100),
end_of_file_(false),
reordering_(false),
reorder_buffer_(),
next_packet_(),
next_packet_length_(0) {
reorder_buffer_() {
assert(clock);
assert(packet_source);
assert(packet_source->get());
@ -368,22 +365,23 @@ class RtpPlayerImpl : public RtpPlayerInterface {
// Send any packets from packet source.
if (!end_of_file_ && (TimeUntilNextPacket() == 0 || first_packet_)) {
if (first_packet_) {
next_packet_length_ = sizeof(next_packet_);
if (packet_source_->NextPacket(next_packet_, &next_packet_length_,
&next_rtp_time_) != 0) {
if (!packet_source_->NextPacket(&next_packet_))
return 0;
}
first_packet_rtp_time_ = next_rtp_time_;
first_packet_rtp_time_ = next_packet_.time_ms;
first_packet_time_ms_ = clock_->TimeInMilliseconds();
first_packet_ = false;
}
if (reordering_ && reorder_buffer_.get() == NULL) {
reorder_buffer_.reset(new RawRtpPacket(next_packet_,
next_packet_length_, 0, 0));
reorder_buffer_.reset(
new RawRtpPacket(next_packet_.data,
static_cast<uint32_t>(next_packet_.length),
0,
0));
return 0;
}
int ret = SendPacket(next_packet_, next_packet_length_);
int ret = SendPacket(next_packet_.data,
static_cast<uint32_t>(next_packet_.length));
if (reorder_buffer_.get()) {
SendPacket(reorder_buffer_->data(), reorder_buffer_->length());
reorder_buffer_.reset(NULL);
@ -392,13 +390,11 @@ class RtpPlayerImpl : public RtpPlayerInterface {
return ret;
}
next_packet_length_ = sizeof(next_packet_);
if (packet_source_->NextPacket(next_packet_, &next_packet_length_,
&next_rtp_time_) != 0) {
if (!packet_source_->NextPacket(&next_packet_)) {
end_of_file_ = true;
return 0;
}
else if (next_packet_length_ == 0) {
else if (next_packet_.length == 0) {
return 0;
}
}
@ -456,7 +452,8 @@ class RtpPlayerImpl : public RtpPlayerInterface {
SsrcHandlers ssrc_handlers_;
Clock* clock_;
scoped_ptr<RtpPacketSourceInterface> packet_source_;
scoped_ptr<test::RtpFileReader> packet_source_;
test::RtpFileReader::Packet next_packet_;
uint32_t next_rtp_time_;
bool first_packet_;
int64_t first_packet_rtp_time_;
@ -468,8 +465,6 @@ class RtpPlayerImpl : public RtpPlayerInterface {
bool end_of_file_;
bool reordering_;
scoped_ptr<RawRtpPacket> reorder_buffer_;
uint8_t next_packet_[kMaxPacketBufferSize];
uint32_t next_packet_length_;
DISALLOW_IMPLICIT_CONSTRUCTORS(RtpPlayerImpl);
};
@ -478,10 +473,11 @@ RtpPlayerInterface* Create(const std::string& input_filename,
PayloadSinkFactoryInterface* payload_sink_factory, Clock* clock,
const PayloadTypes& payload_types, float loss_rate, uint32_t rtt_ms,
bool reordering) {
scoped_ptr<RtpPacketSourceInterface> packet_source(
CreateRtpFileReader(input_filename));
scoped_ptr<test::RtpFileReader> packet_source(test::RtpFileReader::Create(
test::RtpFileReader::kRtpDump, input_filename));
if (packet_source.get() == NULL) {
packet_source.reset(CreatePcapFileReader(input_filename));
packet_source.reset(test::RtpFileReader::Create(test::RtpFileReader::kPcap,
input_filename));
if (packet_source.get() == NULL) {
return NULL;
}

View File

@ -44,20 +44,6 @@ class PayloadCodecTuple {
typedef std::vector<PayloadCodecTuple> PayloadTypes;
typedef std::vector<PayloadCodecTuple>::const_iterator PayloadTypesIterator;
// Implemented by something that can provide RTP packets, for instance a file
// format parser such as the rtp_file_reader or the pcap_file_reader.
class RtpPacketSourceInterface {
public:
virtual ~RtpPacketSourceInterface() {}
// Read next RTP packet into buffer pointed to by rtp_data. On call, 'length'
// field must be filled in with the size of the buffer. The actual size of
// the packet is available in 'length' upon returning. Time in milliseconds
// from start of stream is returned in 'time_ms'.
virtual int NextPacket(uint8_t* rtp_data, uint32_t* length,
uint32_t* time_ms) = 0;
};
// Implemented by RtpPlayer and given to client as a means to retrieve
// information about a specific RTP stream.
class RtpStreamInterface {