New Packet and PacketSource classes for NetEq tests
These new classes are intended to replace the old NETEQTEST_RTPpacket classes. The code in rtp_analyze.cc has been updated to use the new classes; other test applications will follow. BUG=2692 R=andrew@webrtc.org, turaj@webrtc.org Review URL: https://webrtc-codereview.appspot.com/11769004 git-svn-id: http://webrtc.googlecode.com/svn/trunk@5901 4adac7df-926f-26a2-2b94-8c16560cd09d
This commit is contained in:
parent
1da6047132
commit
810acbc93e
@ -171,6 +171,7 @@
|
||||
'<(DEPTH)/testing/gmock.gyp:gmock',
|
||||
'<(DEPTH)/testing/gtest.gyp:gtest',
|
||||
'PCM16B', # Needed by neteq_performance_test.
|
||||
'rtp_rtcp',
|
||||
],
|
||||
'direct_dependent_settings': {
|
||||
'include_dirs': [
|
||||
@ -187,6 +188,11 @@
|
||||
'tools/input_audio_file.h',
|
||||
'tools/neteq_performance_test.cc',
|
||||
'tools/neteq_performance_test.h',
|
||||
'tools/packet.cc',
|
||||
'tools/packet.h',
|
||||
'tools/packet_source.h',
|
||||
'tools/rtp_file_source.cc',
|
||||
'tools/rtp_file_source.h',
|
||||
'tools/rtp_generator.cc',
|
||||
'tools/rtp_generator.h',
|
||||
'tools/neteq_quality_test.cc',
|
||||
|
@ -85,7 +85,7 @@
|
||||
'target_name': 'rtp_analyze',
|
||||
'type': 'executable',
|
||||
'dependencies': [
|
||||
'NetEq4TestTools',
|
||||
'neteq_unittest_tools',
|
||||
'<(DEPTH)/testing/gtest.gyp:gtest',
|
||||
'<(DEPTH)/third_party/gflags/gflags.gyp:gflags',
|
||||
],
|
||||
|
155
webrtc/modules/audio_coding/neteq4/tools/packet.cc
Normal file
155
webrtc/modules/audio_coding/neteq4/tools/packet.cc
Normal file
@ -0,0 +1,155 @@
|
||||
/*
|
||||
* Copyright (c) 2014 The WebRTC project authors. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#include "webrtc/modules/audio_coding/neteq4/tools/packet.h"
|
||||
#include "webrtc/modules/rtp_rtcp/interface/rtp_header_parser.h"
|
||||
|
||||
namespace webrtc {
|
||||
namespace test {
|
||||
|
||||
Packet::Packet(uint8_t* packet_memory,
|
||||
size_t allocated_bytes,
|
||||
double time_ms,
|
||||
const RtpHeaderParser& parser)
|
||||
: payload_memory_(packet_memory),
|
||||
payload_(NULL),
|
||||
packet_length_bytes_(allocated_bytes),
|
||||
payload_length_bytes_(0),
|
||||
virtual_packet_length_bytes_(allocated_bytes),
|
||||
virtual_payload_length_bytes_(0),
|
||||
time_ms_(time_ms) {
|
||||
valid_header_ = ParseHeader(parser);
|
||||
}
|
||||
|
||||
Packet::Packet(uint8_t* packet_memory,
|
||||
size_t allocated_bytes,
|
||||
size_t virtual_packet_length_bytes,
|
||||
double time_ms,
|
||||
const RtpHeaderParser& parser)
|
||||
: payload_memory_(packet_memory),
|
||||
payload_(NULL),
|
||||
packet_length_bytes_(allocated_bytes),
|
||||
payload_length_bytes_(0),
|
||||
virtual_packet_length_bytes_(virtual_packet_length_bytes),
|
||||
virtual_payload_length_bytes_(0),
|
||||
time_ms_(time_ms) {
|
||||
valid_header_ = ParseHeader(parser);
|
||||
}
|
||||
|
||||
Packet::Packet(uint8_t* packet_memory, size_t allocated_bytes, double time_ms)
|
||||
: payload_memory_(packet_memory),
|
||||
payload_(NULL),
|
||||
packet_length_bytes_(allocated_bytes),
|
||||
payload_length_bytes_(0),
|
||||
virtual_packet_length_bytes_(allocated_bytes),
|
||||
virtual_payload_length_bytes_(0),
|
||||
time_ms_(time_ms) {
|
||||
scoped_ptr<RtpHeaderParser> parser(RtpHeaderParser::Create());
|
||||
valid_header_ = ParseHeader(*parser);
|
||||
}
|
||||
|
||||
Packet::Packet(uint8_t* packet_memory,
|
||||
size_t allocated_bytes,
|
||||
size_t virtual_packet_length_bytes,
|
||||
double time_ms)
|
||||
: payload_memory_(packet_memory),
|
||||
payload_(NULL),
|
||||
packet_length_bytes_(allocated_bytes),
|
||||
payload_length_bytes_(0),
|
||||
virtual_packet_length_bytes_(virtual_packet_length_bytes),
|
||||
virtual_payload_length_bytes_(0),
|
||||
time_ms_(time_ms) {
|
||||
scoped_ptr<RtpHeaderParser> parser(RtpHeaderParser::Create());
|
||||
valid_header_ = ParseHeader(*parser);
|
||||
}
|
||||
|
||||
bool Packet::ExtractRedHeaders(std::list<RTPHeader*>* headers) const {
|
||||
//
|
||||
// 0 1 2 3
|
||||
// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// |1| block PT | timestamp offset | block length |
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// |1| ... |
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// |0| block PT |
|
||||
// +-+-+-+-+-+-+-+-+
|
||||
//
|
||||
|
||||
assert(payload_);
|
||||
const uint8_t* payload_ptr = payload_;
|
||||
const uint8_t* payload_end_ptr = payload_ptr + payload_length_bytes_;
|
||||
|
||||
// Find all RED headers with the extension bit set to 1. That is, all headers
|
||||
// but the last one.
|
||||
while ((payload_ptr < payload_end_ptr) && (*payload_ptr & 0x80)) {
|
||||
RTPHeader* header = new RTPHeader;
|
||||
CopyToHeader(header);
|
||||
header->payloadType = payload_ptr[0] & 0x7F;
|
||||
uint32_t offset = (payload_ptr[1] << 6) + ((payload_ptr[2] & 0xFC) >> 2);
|
||||
header->timestamp -= offset;
|
||||
headers->push_front(header);
|
||||
payload_ptr += 4;
|
||||
}
|
||||
// Last header.
|
||||
assert(payload_ptr < payload_end_ptr);
|
||||
if (payload_ptr >= payload_end_ptr) {
|
||||
return false; // Payload too short.
|
||||
}
|
||||
RTPHeader* header = new RTPHeader;
|
||||
CopyToHeader(header);
|
||||
header->payloadType = payload_ptr[0] & 0x7F;
|
||||
headers->push_front(header);
|
||||
return true;
|
||||
}
|
||||
|
||||
void Packet::DeleteRedHeaders(std::list<RTPHeader*>* headers) {
|
||||
while (!headers->empty()) {
|
||||
delete headers->front();
|
||||
headers->pop_front();
|
||||
}
|
||||
}
|
||||
|
||||
bool Packet::ParseHeader(const RtpHeaderParser& parser) {
|
||||
bool valid_header = parser.Parse(
|
||||
payload_memory_.get(), static_cast<int>(packet_length_bytes_), &header_);
|
||||
assert(valid_header);
|
||||
if (!valid_header) {
|
||||
return false;
|
||||
}
|
||||
assert(header_.headerLength <= packet_length_bytes_);
|
||||
payload_ = &payload_memory_[header_.headerLength];
|
||||
assert(packet_length_bytes_ >= header_.headerLength);
|
||||
payload_length_bytes_ = packet_length_bytes_ - header_.headerLength;
|
||||
assert(virtual_packet_length_bytes_ >= header_.headerLength);
|
||||
virtual_payload_length_bytes_ =
|
||||
virtual_packet_length_bytes_ - header_.headerLength;
|
||||
return true;
|
||||
}
|
||||
|
||||
void Packet::CopyToHeader(RTPHeader* destination) const {
|
||||
destination->markerBit = header_.markerBit;
|
||||
destination->payloadType = header_.payloadType;
|
||||
destination->sequenceNumber = header_.sequenceNumber;
|
||||
destination->timestamp = header_.timestamp;
|
||||
destination->ssrc = header_.ssrc;
|
||||
destination->numCSRCs = header_.numCSRCs;
|
||||
destination->paddingLength = header_.paddingLength;
|
||||
destination->headerLength = header_.headerLength;
|
||||
destination->payload_type_frequency = header_.payload_type_frequency;
|
||||
memcpy(&destination->arrOfCSRCs,
|
||||
&header_.arrOfCSRCs,
|
||||
sizeof(header_.arrOfCSRCs));
|
||||
memcpy(
|
||||
&destination->extension, &header_.extension, sizeof(header_.extension));
|
||||
}
|
||||
|
||||
} // namespace test
|
||||
} // namespace webrtc
|
117
webrtc/modules/audio_coding/neteq4/tools/packet.h
Normal file
117
webrtc/modules/audio_coding/neteq4/tools/packet.h
Normal file
@ -0,0 +1,117 @@
|
||||
/*
|
||||
* Copyright (c) 2014 The WebRTC project authors. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#ifndef WEBRTC_MODULES_AUDIO_CODING_NETEQ4_TOOLS_PACKET_H_
|
||||
#define WEBRTC_MODULES_AUDIO_CODING_NETEQ4_TOOLS_PACKET_H_
|
||||
|
||||
#include <list>
|
||||
|
||||
#include "webrtc/common_types.h"
|
||||
#include "webrtc/system_wrappers/interface/constructor_magic.h"
|
||||
#include "webrtc/system_wrappers/interface/scoped_ptr.h"
|
||||
#include "webrtc/typedefs.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
class RtpHeaderParser;
|
||||
|
||||
namespace test {
|
||||
|
||||
// Class for handling RTP packets in test applications.
|
||||
class Packet {
|
||||
public:
|
||||
// Creates a packet, with the packet payload (including header bytes) in
|
||||
// |packet_memory|. The length of |packet_memory| is |allocated_bytes|.
|
||||
// The new object assumes ownership of |packet_memory| and will delete it
|
||||
// when the Packet object is deleted. The |time_ms| is an extra time
|
||||
// associated with this packet, typically used to denote arrival time.
|
||||
// The first bytes in |packet_memory| will be parsed using |parser|.
|
||||
Packet(uint8_t* packet_memory,
|
||||
size_t allocated_bytes,
|
||||
double time_ms,
|
||||
const RtpHeaderParser& parser);
|
||||
|
||||
// Same as above, but with the extra argument |virtual_packet_length_bytes|.
|
||||
// This is typically used when reading RTP dump files that only contain the
|
||||
// RTP headers, and no payload (a.k.a RTP dummy files or RTP light). The
|
||||
// |virtual_packet_length_bytes| tells what size the packet had on wire,
|
||||
// including the now discarded payload, whereas |allocated_bytes| is the
|
||||
// length of the remaining payload (typically only the RTP header).
|
||||
Packet(uint8_t* packet_memory,
|
||||
size_t allocated_bytes,
|
||||
size_t virtual_packet_length_bytes,
|
||||
double time_ms,
|
||||
const RtpHeaderParser& parser);
|
||||
|
||||
// The following two constructors are the same as above, but without a
|
||||
// parser. Note that when the object is constructed using any of these
|
||||
// methods, the header will be parsed using a default RtpHeaderParser object.
|
||||
// In particular, RTP header extensions won't be parsed.
|
||||
Packet(uint8_t* packet_memory, size_t allocated_bytes, double time_ms);
|
||||
|
||||
Packet(uint8_t* packet_memory,
|
||||
size_t allocated_bytes,
|
||||
size_t virtual_packet_length_bytes,
|
||||
double time_ms);
|
||||
|
||||
virtual ~Packet() {}
|
||||
|
||||
// Parses the first bytes of the RTP payload, interpreting them as RED headers
|
||||
// according to RFC 2198. The headers will be inserted into |headers|. The
|
||||
// caller of the method assumes ownership of the objects in the list, and
|
||||
// must delete them properly.
|
||||
bool ExtractRedHeaders(std::list<RTPHeader*>* headers) const;
|
||||
|
||||
// Deletes all RTPHeader objects in |headers|, but does not delete |headers|
|
||||
// itself.
|
||||
static void DeleteRedHeaders(std::list<RTPHeader*>* headers);
|
||||
|
||||
const uint8_t* payload() const { return payload_; }
|
||||
|
||||
size_t packet_length_bytes() const { return packet_length_bytes_; }
|
||||
|
||||
size_t payload_length_bytes() const { return payload_length_bytes_; }
|
||||
|
||||
size_t virtual_packet_length_bytes() const {
|
||||
return virtual_packet_length_bytes_;
|
||||
}
|
||||
|
||||
size_t virtual_payload_length_bytes() const {
|
||||
return virtual_payload_length_bytes_;
|
||||
}
|
||||
|
||||
const RTPHeader& header() const { return header_; }
|
||||
|
||||
void set_time_ms(double time) { time_ms_ = time; }
|
||||
double time_ms() const { return time_ms_; }
|
||||
bool valid_header() const { return valid_header_; }
|
||||
|
||||
private:
|
||||
bool ParseHeader(const RtpHeaderParser& parser);
|
||||
void CopyToHeader(RTPHeader* destination) const;
|
||||
|
||||
RTPHeader header_;
|
||||
scoped_ptr<uint8_t[]> payload_memory_;
|
||||
const uint8_t* payload_; // First byte after header.
|
||||
const size_t packet_length_bytes_; // Total length of packet.
|
||||
size_t payload_length_bytes_; // Length of the payload, after RTP header.
|
||||
// Zero for dummy RTP packets.
|
||||
// Virtual lengths are used when parsing RTP header files (dummy RTP files).
|
||||
const size_t virtual_packet_length_bytes_;
|
||||
size_t virtual_payload_length_bytes_;
|
||||
double time_ms_; // Used to denote a packet's arrival time.
|
||||
bool valid_header_; // Set by the RtpHeaderParser.
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(Packet);
|
||||
};
|
||||
|
||||
} // namespace test
|
||||
} // namespace webrtc
|
||||
#endif // WEBRTC_MODULES_AUDIO_CODING_NETEQ4_TOOLS_PACKET_H_
|
36
webrtc/modules/audio_coding/neteq4/tools/packet_source.h
Normal file
36
webrtc/modules/audio_coding/neteq4/tools/packet_source.h
Normal file
@ -0,0 +1,36 @@
|
||||
/*
|
||||
* Copyright (c) 2014 The WebRTC project authors. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#ifndef WEBRTC_MODULES_AUDIO_CODING_NETEQ4_TOOLS_PACKET_SOURCE_H_
|
||||
#define WEBRTC_MODULES_AUDIO_CODING_NETEQ4_TOOLS_PACKET_SOURCE_H_
|
||||
|
||||
#include "webrtc/system_wrappers/interface/constructor_magic.h"
|
||||
|
||||
namespace webrtc {
|
||||
namespace test {
|
||||
|
||||
class Packet;
|
||||
|
||||
// Interface class for an object delivering RTP packets to test applications.
|
||||
class PacketSource {
|
||||
public:
|
||||
PacketSource() {}
|
||||
virtual ~PacketSource() {}
|
||||
|
||||
// Returns a pointer to the next packet.
|
||||
virtual Packet* NextPacket() = 0;
|
||||
|
||||
private:
|
||||
DISALLOW_COPY_AND_ASSIGN(PacketSource);
|
||||
};
|
||||
|
||||
} // namespace test
|
||||
} // namespace webrtc
|
||||
#endif // WEBRTC_MODULES_AUDIO_CODING_NETEQ4_TOOLS_PACKET_SOURCE_H_
|
202
webrtc/modules/audio_coding/neteq4/tools/packet_unittest.cc
Normal file
202
webrtc/modules/audio_coding/neteq4/tools/packet_unittest.cc
Normal file
@ -0,0 +1,202 @@
|
||||
/*
|
||||
* Copyright (c) 2014 The WebRTC project authors. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
// Unit tests for test Packet class.
|
||||
|
||||
#include "webrtc/modules/audio_coding/neteq4/tools/packet.h"
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
namespace webrtc {
|
||||
namespace test {
|
||||
|
||||
namespace {
|
||||
const int kHeaderLengthBytes = 12;
|
||||
|
||||
void MakeRtpHeader(int payload_type,
|
||||
int seq_number,
|
||||
uint32_t timestamp,
|
||||
uint32_t ssrc,
|
||||
uint8_t* rtp_data) {
|
||||
rtp_data[0] = 0x80;
|
||||
rtp_data[1] = payload_type & 0xFF;
|
||||
rtp_data[2] = (seq_number >> 8) & 0xFF;
|
||||
rtp_data[3] = (seq_number) & 0xFF;
|
||||
rtp_data[4] = (timestamp >> 24) & 0xFF;
|
||||
rtp_data[5] = (timestamp >> 16) & 0xFF;
|
||||
rtp_data[6] = (timestamp >> 8) & 0xFF;
|
||||
rtp_data[7] = timestamp & 0xFF;
|
||||
rtp_data[8] = (ssrc >> 24) & 0xFF;
|
||||
rtp_data[9] = (ssrc >> 16) & 0xFF;
|
||||
rtp_data[10] = (ssrc >> 8) & 0xFF;
|
||||
rtp_data[11] = ssrc & 0xFF;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
TEST(TestPacket, RegularPacket) {
|
||||
const size_t kPacketLengthBytes = 100;
|
||||
uint8_t* packet_memory = new uint8_t[kPacketLengthBytes];
|
||||
const uint8_t kPayloadType = 17;
|
||||
const uint16_t kSequenceNumber = 4711;
|
||||
const uint32_t kTimestamp = 47114711;
|
||||
const uint32_t kSsrc = 0x12345678;
|
||||
MakeRtpHeader(
|
||||
kPayloadType, kSequenceNumber, kTimestamp, kSsrc, packet_memory);
|
||||
const double kPacketTime = 1.0;
|
||||
// Hand over ownership of |packet_memory| to |packet|.
|
||||
Packet packet(packet_memory, kPacketLengthBytes, kPacketTime);
|
||||
ASSERT_TRUE(packet.valid_header());
|
||||
EXPECT_EQ(kPayloadType, packet.header().payloadType);
|
||||
EXPECT_EQ(kSequenceNumber, packet.header().sequenceNumber);
|
||||
EXPECT_EQ(kTimestamp, packet.header().timestamp);
|
||||
EXPECT_EQ(kSsrc, packet.header().ssrc);
|
||||
EXPECT_EQ(0, packet.header().numCSRCs);
|
||||
EXPECT_EQ(kPacketLengthBytes, packet.packet_length_bytes());
|
||||
EXPECT_EQ(kPacketLengthBytes - kHeaderLengthBytes,
|
||||
packet.payload_length_bytes());
|
||||
EXPECT_EQ(kPacketLengthBytes, packet.virtual_packet_length_bytes());
|
||||
EXPECT_EQ(kPacketLengthBytes - kHeaderLengthBytes,
|
||||
packet.virtual_payload_length_bytes());
|
||||
EXPECT_EQ(kPacketTime, packet.time_ms());
|
||||
}
|
||||
|
||||
TEST(TestPacket, DummyPacket) {
|
||||
const size_t kPacketLengthBytes = kHeaderLengthBytes; // Only RTP header.
|
||||
const size_t kVirtualPacketLengthBytes = 100;
|
||||
uint8_t* packet_memory = new uint8_t[kPacketLengthBytes];
|
||||
const uint8_t kPayloadType = 17;
|
||||
const uint16_t kSequenceNumber = 4711;
|
||||
const uint32_t kTimestamp = 47114711;
|
||||
const uint32_t kSsrc = 0x12345678;
|
||||
MakeRtpHeader(
|
||||
kPayloadType, kSequenceNumber, kTimestamp, kSsrc, packet_memory);
|
||||
const double kPacketTime = 1.0;
|
||||
// Hand over ownership of |packet_memory| to |packet|.
|
||||
Packet packet(packet_memory,
|
||||
kPacketLengthBytes,
|
||||
kVirtualPacketLengthBytes,
|
||||
kPacketTime);
|
||||
ASSERT_TRUE(packet.valid_header());
|
||||
EXPECT_EQ(kPayloadType, packet.header().payloadType);
|
||||
EXPECT_EQ(kSequenceNumber, packet.header().sequenceNumber);
|
||||
EXPECT_EQ(kTimestamp, packet.header().timestamp);
|
||||
EXPECT_EQ(kSsrc, packet.header().ssrc);
|
||||
EXPECT_EQ(0, packet.header().numCSRCs);
|
||||
EXPECT_EQ(kPacketLengthBytes, packet.packet_length_bytes());
|
||||
EXPECT_EQ(kPacketLengthBytes - kHeaderLengthBytes,
|
||||
packet.payload_length_bytes());
|
||||
EXPECT_EQ(kVirtualPacketLengthBytes, packet.virtual_packet_length_bytes());
|
||||
EXPECT_EQ(kVirtualPacketLengthBytes - kHeaderLengthBytes,
|
||||
packet.virtual_payload_length_bytes());
|
||||
EXPECT_EQ(kPacketTime, packet.time_ms());
|
||||
}
|
||||
|
||||
namespace {
|
||||
// Writes one RED block header starting at |rtp_data|, according to RFC 2198.
|
||||
// returns the number of bytes written (1 or 4).
|
||||
//
|
||||
// Format if |last_payoad| is false:
|
||||
// 0 1 2 3
|
||||
// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// |1| block PT | timestamp offset | block length |
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
//
|
||||
// Format if |last_payoad| is true:
|
||||
// 0 1 2 3 4 5 6 7
|
||||
// +-+-+-+-+-+-+-+-+
|
||||
// |0| Block PT |
|
||||
// +-+-+-+-+-+-+-+-+
|
||||
|
||||
int MakeRedHeader(int payload_type,
|
||||
uint32_t timestamp_offset,
|
||||
int block_length,
|
||||
bool last_payload,
|
||||
uint8_t* rtp_data) {
|
||||
rtp_data[0] = 0x80 | (payload_type & 0x7F); // Set the first bit to 1.
|
||||
if (last_payload) {
|
||||
rtp_data[0] &= 0x7F; // Reset the first but to 0 to indicate last block.
|
||||
return 1;
|
||||
}
|
||||
rtp_data[1] = timestamp_offset >> 6;
|
||||
rtp_data[2] = (timestamp_offset & 0x3F) << 2;
|
||||
rtp_data[2] |= block_length >> 8;
|
||||
rtp_data[3] = block_length & 0xFF;
|
||||
return 4;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
TEST(TestPacket, RED) {
|
||||
const size_t kPacketLengthBytes = 100;
|
||||
uint8_t* packet_memory = new uint8_t[kPacketLengthBytes];
|
||||
const uint8_t kRedPayloadType = 17;
|
||||
const uint16_t kSequenceNumber = 4711;
|
||||
const uint32_t kTimestamp = 47114711;
|
||||
const uint32_t kSsrc = 0x12345678;
|
||||
MakeRtpHeader(
|
||||
kRedPayloadType, kSequenceNumber, kTimestamp, kSsrc, packet_memory);
|
||||
// Create four RED headers.
|
||||
// Payload types are just the same as the block index the offset is 100 times
|
||||
// the block index.
|
||||
const int kRedBlocks = 4;
|
||||
uint8_t* payload_ptr =
|
||||
&packet_memory[kHeaderLengthBytes]; // First byte after header.
|
||||
for (int i = 0; i < kRedBlocks; ++i) {
|
||||
int payload_type = i;
|
||||
// Offset value is not used for the last block.
|
||||
uint32_t timestamp_offset = 100 * i;
|
||||
int block_length = 10 * i;
|
||||
bool last_block = (i == kRedBlocks - 1) ? true : false;
|
||||
payload_ptr += MakeRedHeader(
|
||||
payload_type, timestamp_offset, block_length, last_block, payload_ptr);
|
||||
}
|
||||
const double kPacketTime = 1.0;
|
||||
// Hand over ownership of |packet_memory| to |packet|.
|
||||
Packet packet(packet_memory, kPacketLengthBytes, kPacketTime);
|
||||
ASSERT_TRUE(packet.valid_header());
|
||||
EXPECT_EQ(kRedPayloadType, packet.header().payloadType);
|
||||
EXPECT_EQ(kSequenceNumber, packet.header().sequenceNumber);
|
||||
EXPECT_EQ(kTimestamp, packet.header().timestamp);
|
||||
EXPECT_EQ(kSsrc, packet.header().ssrc);
|
||||
EXPECT_EQ(0, packet.header().numCSRCs);
|
||||
EXPECT_EQ(kPacketLengthBytes, packet.packet_length_bytes());
|
||||
EXPECT_EQ(kPacketLengthBytes - kHeaderLengthBytes,
|
||||
packet.payload_length_bytes());
|
||||
EXPECT_EQ(kPacketLengthBytes, packet.virtual_packet_length_bytes());
|
||||
EXPECT_EQ(kPacketLengthBytes - kHeaderLengthBytes,
|
||||
packet.virtual_payload_length_bytes());
|
||||
EXPECT_EQ(kPacketTime, packet.time_ms());
|
||||
std::list<RTPHeader*> red_headers;
|
||||
EXPECT_TRUE(packet.ExtractRedHeaders(&red_headers));
|
||||
EXPECT_EQ(kRedBlocks, static_cast<int>(red_headers.size()));
|
||||
int block_index = 0;
|
||||
for (std::list<RTPHeader*>::reverse_iterator it = red_headers.rbegin();
|
||||
it != red_headers.rend();
|
||||
++it) {
|
||||
// Reading list from the back, since the extraction puts the main payload
|
||||
// (which is the last one on wire) first.
|
||||
RTPHeader* red_block = *it;
|
||||
EXPECT_EQ(block_index, red_block->payloadType);
|
||||
EXPECT_EQ(kSequenceNumber, red_block->sequenceNumber);
|
||||
if (block_index == kRedBlocks - 1) {
|
||||
// Last block has zero offset per definition.
|
||||
EXPECT_EQ(kTimestamp, red_block->timestamp);
|
||||
} else {
|
||||
EXPECT_EQ(kTimestamp - 100 * block_index, red_block->timestamp);
|
||||
}
|
||||
EXPECT_EQ(kSsrc, red_block->ssrc);
|
||||
EXPECT_EQ(0, red_block->numCSRCs);
|
||||
++block_index;
|
||||
}
|
||||
Packet::DeleteRedHeaders(&red_headers);
|
||||
}
|
||||
|
||||
} // namespace test
|
||||
} // namespace webrtc
|
@ -13,8 +13,9 @@
|
||||
#include <vector>
|
||||
|
||||
#include "google/gflags.h"
|
||||
#include "webrtc/modules/audio_coding/neteq4/test/NETEQTEST_DummyRTPpacket.h"
|
||||
#include "webrtc/modules/audio_coding/neteq4/test/NETEQTEST_RTPpacket.h"
|
||||
#include "webrtc/modules/audio_coding/neteq4/tools/packet.h"
|
||||
#include "webrtc/modules/audio_coding/neteq4/tools/rtp_file_source.h"
|
||||
#include "webrtc/system_wrappers/interface/scoped_ptr.h"
|
||||
|
||||
// Flag validator.
|
||||
static bool ValidatePayloadType(const char* flagname, int32_t value) {
|
||||
@ -23,11 +24,20 @@ static bool ValidatePayloadType(const char* flagname, int32_t value) {
|
||||
printf("Invalid value for --%s: %d\n", flagname, static_cast<int>(value));
|
||||
return false;
|
||||
}
|
||||
static bool ValidateExtensionId(const char* flagname, int32_t value) {
|
||||
if (value > 0 && value <= 255) // Value is ok.
|
||||
return true;
|
||||
printf("Invalid value for --%s: %d\n", flagname, static_cast<int>(value));
|
||||
return false;
|
||||
}
|
||||
|
||||
// Define command line flags.
|
||||
DEFINE_int32(red, 117, "RTP payload type for RED");
|
||||
static const bool pcmu_dummy =
|
||||
static const bool red_dummy =
|
||||
google::RegisterFlagValidator(&FLAGS_red, &ValidatePayloadType);
|
||||
DEFINE_int32(audio_level, 1, "Extension ID for audio level (RFC 6464)");
|
||||
static const bool audio_level_dummy =
|
||||
google::RegisterFlagValidator(&FLAGS_audio_level, &ValidateExtensionId);
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
std::string program_name = argv[0];
|
||||
@ -55,6 +65,16 @@ int main(int argc, char* argv[]) {
|
||||
return -1;
|
||||
}
|
||||
printf("Input file: %s\n", argv[1]);
|
||||
webrtc::scoped_ptr<webrtc::test::RtpFileSource> file_source(
|
||||
webrtc::test::RtpFileSource::Create(argv[1]));
|
||||
assert(file_source.get());
|
||||
// Set RTP extension ID.
|
||||
bool print_audio_level = false;
|
||||
if (!google::GetCommandLineFlagInfoOrDie("audio_level").is_default) {
|
||||
print_audio_level = true;
|
||||
file_source->RegisterRtpHeaderExtension(webrtc::kRtpExtensionAudioLevel,
|
||||
FLAGS_audio_level);
|
||||
}
|
||||
|
||||
FILE* out_file;
|
||||
if (argc == 3) {
|
||||
@ -69,37 +89,54 @@ int main(int argc, char* argv[]) {
|
||||
}
|
||||
|
||||
// Print file header.
|
||||
fprintf(out_file, "SeqNo TimeStamp SendTime Size PT M SSRC\n");
|
||||
fprintf(out_file, "SeqNo TimeStamp SendTime Size PT M SSRC");
|
||||
if (print_audio_level) {
|
||||
fprintf(out_file, " AuLvl (V)");
|
||||
}
|
||||
fprintf(out_file, "\n");
|
||||
|
||||
// Read file header.
|
||||
NETEQTEST_RTPpacket::skipFileHeader(in_file);
|
||||
NETEQTEST_RTPpacket packet;
|
||||
|
||||
while (packet.readFromFile(in_file) >= 0) {
|
||||
webrtc::scoped_ptr<webrtc::test::Packet> packet;
|
||||
while (!file_source->EndOfFile()) {
|
||||
packet.reset(file_source->NextPacket());
|
||||
if (!packet.get()) {
|
||||
// This is probably an RTCP packet. Move on to the next one.
|
||||
continue;
|
||||
}
|
||||
assert(packet.get());
|
||||
// Write packet data to file.
|
||||
fprintf(out_file,
|
||||
"%5u %10u %10u %5i %5i %2i %#08X\n",
|
||||
packet.sequenceNumber(),
|
||||
packet.timeStamp(),
|
||||
packet.time(),
|
||||
packet.dataLen(),
|
||||
packet.payloadType(),
|
||||
packet.markerBit(),
|
||||
packet.SSRC());
|
||||
if (packet.payloadType() == FLAGS_red) {
|
||||
webrtc::WebRtcRTPHeader red_header;
|
||||
int len;
|
||||
int red_index = 0;
|
||||
while ((len = packet.extractRED(red_index++, red_header)) >= 0) {
|
||||
"%5u %10u %10u %5i %5i %2i %#08X",
|
||||
packet->header().sequenceNumber,
|
||||
packet->header().timestamp,
|
||||
static_cast<unsigned int>(packet->time_ms()),
|
||||
static_cast<int>(packet->packet_length_bytes()),
|
||||
packet->header().payloadType,
|
||||
packet->header().markerBit,
|
||||
packet->header().ssrc);
|
||||
if (print_audio_level && packet->header().extension.hasAudioLevel) {
|
||||
// |audioLevel| consists of one bit for "V" and then 7 bits level.
|
||||
fprintf(out_file,
|
||||
"* %5u %10u %10u %5i %5i\n",
|
||||
red_header.header.sequenceNumber,
|
||||
red_header.header.timestamp,
|
||||
packet.time(),
|
||||
len,
|
||||
red_header.header.payloadType);
|
||||
" %5u (%1i)",
|
||||
packet->header().extension.audioLevel & 0x7F,
|
||||
(packet->header().extension.audioLevel & 0x80) == 0 ? 0 : 1);
|
||||
}
|
||||
fprintf(out_file, "\n");
|
||||
|
||||
if (packet->header().payloadType == FLAGS_red) {
|
||||
std::list<webrtc::RTPHeader*> red_headers;
|
||||
packet->ExtractRedHeaders(&red_headers);
|
||||
while (!red_headers.empty()) {
|
||||
webrtc::RTPHeader* red = red_headers.front();
|
||||
assert(red);
|
||||
fprintf(out_file,
|
||||
"* %5u %10u %10u %5i\n",
|
||||
red->sequenceNumber,
|
||||
red->timestamp,
|
||||
static_cast<unsigned int>(packet->time_ms()),
|
||||
red->payloadType);
|
||||
red_headers.pop_front();
|
||||
delete red;
|
||||
}
|
||||
assert(red_index > 1); // We must get at least one payload.
|
||||
}
|
||||
}
|
||||
|
||||
|
140
webrtc/modules/audio_coding/neteq4/tools/rtp_file_source.cc
Normal file
140
webrtc/modules/audio_coding/neteq4/tools/rtp_file_source.cc
Normal file
@ -0,0 +1,140 @@
|
||||
/*
|
||||
* Copyright (c) 2014 The WebRTC project authors. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#include "webrtc/modules/audio_coding/neteq4/tools/rtp_file_source.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
#ifdef WIN32
|
||||
#include <winsock2.h>
|
||||
#else
|
||||
#include <netinet/in.h>
|
||||
#endif
|
||||
|
||||
#include "webrtc/modules/audio_coding/neteq4/tools/packet.h"
|
||||
#include "webrtc/modules/rtp_rtcp/interface/rtp_header_parser.h"
|
||||
|
||||
namespace webrtc {
|
||||
namespace test {
|
||||
|
||||
RtpFileSource* RtpFileSource::Create(const std::string& file_name) {
|
||||
RtpFileSource* source = new RtpFileSource;
|
||||
assert(source);
|
||||
if (!source->OpenFile(file_name) || !source->SkipFileHeader()) {
|
||||
assert(false);
|
||||
delete source;
|
||||
return NULL;
|
||||
}
|
||||
return source;
|
||||
}
|
||||
|
||||
RtpFileSource::~RtpFileSource() {
|
||||
if (in_file_)
|
||||
fclose(in_file_);
|
||||
}
|
||||
|
||||
bool RtpFileSource::RegisterRtpHeaderExtension(RTPExtensionType type,
|
||||
uint8_t id) {
|
||||
assert(parser_.get());
|
||||
return parser_->RegisterRtpHeaderExtension(type, id);
|
||||
}
|
||||
|
||||
Packet* RtpFileSource::NextPacket() {
|
||||
uint16_t length;
|
||||
if (fread(&length, sizeof(uint16_t), 1, in_file_) == 0) {
|
||||
assert(false);
|
||||
return NULL;
|
||||
}
|
||||
length = ntohs(length);
|
||||
|
||||
uint16_t plen;
|
||||
if (fread(&plen, sizeof(uint16_t), 1, in_file_) == 0) {
|
||||
assert(false);
|
||||
return NULL;
|
||||
}
|
||||
plen = ntohs(plen);
|
||||
|
||||
uint32_t offset;
|
||||
if (fread(&offset, sizeof(uint32_t), 1, in_file_) == 0) {
|
||||
assert(false);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Use length here because a plen of 0 specifies RTCP.
|
||||
size_t packet_size_bytes = length - kPacketHeaderSize;
|
||||
if (packet_size_bytes <= 0) {
|
||||
// May be an RTCP packet.
|
||||
return NULL;
|
||||
}
|
||||
uint8_t* packet_memory = new uint8_t[packet_size_bytes];
|
||||
if (fread(packet_memory, 1, packet_size_bytes, in_file_) !=
|
||||
packet_size_bytes) {
|
||||
assert(false);
|
||||
delete[] packet_memory;
|
||||
return NULL;
|
||||
}
|
||||
Packet* packet = new Packet(
|
||||
packet_memory, packet_size_bytes, plen, ntohl(offset), *parser_.get());
|
||||
if (!packet->valid_header()) {
|
||||
assert(false);
|
||||
delete packet;
|
||||
return NULL;
|
||||
}
|
||||
return packet;
|
||||
}
|
||||
|
||||
bool RtpFileSource::EndOfFile() const {
|
||||
assert(in_file_);
|
||||
return ftell(in_file_) >= file_end_;
|
||||
}
|
||||
|
||||
RtpFileSource::RtpFileSource()
|
||||
: PacketSource(),
|
||||
in_file_(NULL),
|
||||
file_end_(-1),
|
||||
parser_(RtpHeaderParser::Create()) {}
|
||||
|
||||
bool RtpFileSource::OpenFile(const std::string& file_name) {
|
||||
in_file_ = fopen(file_name.c_str(), "rb");
|
||||
assert(in_file_);
|
||||
if (in_file_ == NULL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Find out how long the file is.
|
||||
fseek(in_file_, 0, SEEK_END);
|
||||
file_end_ = ftell(in_file_);
|
||||
rewind(in_file_);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool RtpFileSource::SkipFileHeader() {
|
||||
char firstline[kFirstLineLength];
|
||||
assert(in_file_);
|
||||
if (fgets(firstline, kFirstLineLength, in_file_) == NULL) {
|
||||
assert(false);
|
||||
return false;
|
||||
}
|
||||
// Check that the first line is ok.
|
||||
if ((strncmp(firstline, "#!rtpplay1.0", 12) != 0) &&
|
||||
(strncmp(firstline, "#!RTPencode1.0", 14) != 0)) {
|
||||
assert(false);
|
||||
return false;
|
||||
}
|
||||
// Skip the file header.
|
||||
if (fseek(in_file_, kRtpFileHeaderSize, SEEK_CUR) != 0) {
|
||||
assert(false);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace test
|
||||
} // namespace webrtc
|
66
webrtc/modules/audio_coding/neteq4/tools/rtp_file_source.h
Normal file
66
webrtc/modules/audio_coding/neteq4/tools/rtp_file_source.h
Normal file
@ -0,0 +1,66 @@
|
||||
/*
|
||||
* Copyright (c) 2014 The WebRTC project authors. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#ifndef WEBRTC_MODULES_AUDIO_CODING_NETEQ4_TOOLS_RTP_FILE_SOURCE_H_
|
||||
#define WEBRTC_MODULES_AUDIO_CODING_NETEQ4_TOOLS_RTP_FILE_SOURCE_H_
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string>
|
||||
|
||||
#include "webrtc/common_types.h"
|
||||
#include "webrtc/modules/audio_coding/neteq4/tools/packet_source.h"
|
||||
#include "webrtc/modules/rtp_rtcp/interface/rtp_rtcp_defines.h"
|
||||
#include "webrtc/system_wrappers/interface/constructor_magic.h"
|
||||
#include "webrtc/system_wrappers/interface/scoped_ptr.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
class RtpHeaderParser;
|
||||
|
||||
namespace test {
|
||||
|
||||
class RtpFileSource : public PacketSource {
|
||||
public:
|
||||
// Creates an RtpFileSource reading from |file_name|. If the file cannot be
|
||||
// opened, or has the wrong format, NULL will be returned.
|
||||
static RtpFileSource* Create(const std::string& file_name);
|
||||
|
||||
virtual ~RtpFileSource();
|
||||
|
||||
// Registers an RTP header extension and binds it to |id|.
|
||||
virtual bool RegisterRtpHeaderExtension(RTPExtensionType type, uint8_t id);
|
||||
|
||||
// Returns a pointer to the next packet.
|
||||
virtual Packet* NextPacket();
|
||||
|
||||
// Returns true if the end of file has been reached.
|
||||
virtual bool EndOfFile() const;
|
||||
|
||||
private:
|
||||
static const int kFirstLineLength = 40;
|
||||
static const int kRtpFileHeaderSize = 4 + 4 + 4 + 2 + 2;
|
||||
static const size_t kPacketHeaderSize = 8;
|
||||
|
||||
RtpFileSource();
|
||||
|
||||
bool OpenFile(const std::string& file_name);
|
||||
|
||||
bool SkipFileHeader();
|
||||
|
||||
FILE* in_file_;
|
||||
int64_t file_end_;
|
||||
scoped_ptr<RtpHeaderParser> parser_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(RtpFileSource);
|
||||
};
|
||||
|
||||
} // namespace test
|
||||
} // namespace webrtc
|
||||
#endif // WEBRTC_MODULES_AUDIO_CODING_NETEQ4_TOOLS_RTP_FILE_SOURCE_H_
|
@ -152,6 +152,7 @@
|
||||
'audio_coding/neteq4/mock/mock_external_decoder_pcm16b.h',
|
||||
'audio_coding/neteq4/mock/mock_packet_buffer.h',
|
||||
'audio_coding/neteq4/mock/mock_payload_splitter.h',
|
||||
'audio_coding/neteq4/tools/packet_unittest.cc',
|
||||
'audio_processing/aec/system_delay_unittest.cc',
|
||||
'audio_processing/aec/echo_cancellation_unittest.cc',
|
||||
'audio_processing/echo_cancellation_impl_unittest.cc',
|
||||
|
Loading…
x
Reference in New Issue
Block a user