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:
henrik.lundin@webrtc.org 2014-04-14 18:42:23 +00:00
parent 1da6047132
commit 810acbc93e
10 changed files with 790 additions and 30 deletions

View File

@ -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',

View File

@ -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',
],

View 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

View 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_

View 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_

View 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

View File

@ -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 (%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 %5i\n",
red_header.header.sequenceNumber,
red_header.header.timestamp,
packet.time(),
len,
red_header.header.payloadType);
"* %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.
}
}

View 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

View 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_

View File

@ -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',