Rename neteq4 folder to neteq

Keep the old neteq4/audio_decoder_unittests.isolate while waiting for
a hard-coded reference to change.

This CL effectively reverts r6257 "Rename neteq4 folder to neteq".

BUG=2996
TBR=tina.legrand@webrtc.org

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

git-svn-id: http://webrtc.googlecode.com/svn/trunk@6367 4adac7df-926f-26a2-2b94-8c16560cd09d
This commit is contained in:
henrik.lundin@webrtc.org
2014-06-09 08:10:28 +00:00
parent 31f967c611
commit 9c55f0f957
158 changed files with 547 additions and 511 deletions

View File

@ -0,0 +1,57 @@
/*
* 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/audio_coding/neteq/tools/audio_loop.h"
#include <assert.h>
#include <stdio.h>
#include <string.h>
namespace webrtc {
namespace test {
bool AudioLoop::Init(const std::string file_name,
size_t max_loop_length_samples,
size_t block_length_samples) {
FILE* fp = fopen(file_name.c_str(), "rb");
if (!fp) return false;
audio_array_.reset(new int16_t[max_loop_length_samples +
block_length_samples]);
size_t samples_read = fread(audio_array_.get(), sizeof(int16_t),
max_loop_length_samples, fp);
fclose(fp);
// Block length must be shorter than the loop length.
if (block_length_samples > samples_read) return false;
// Add an extra block length of samples to the end of the array, starting
// over again from the beginning of the array. This is done to simplify
// the reading process when reading over the end of the loop.
memcpy(&audio_array_[samples_read], audio_array_.get(),
block_length_samples * sizeof(int16_t));
loop_length_samples_ = samples_read;
block_length_samples_ = block_length_samples;
return true;
}
const int16_t* AudioLoop::GetNextBlock() {
// Check that the AudioLoop is initialized.
if (block_length_samples_ == 0) return NULL;
const int16_t* output_ptr = &audio_array_[next_index_];
next_index_ = (next_index_ + block_length_samples_) % loop_length_samples_;
return output_ptr;
}
} // namespace test
} // namespace webrtc

View File

@ -0,0 +1,59 @@
/*
* 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_AUDIO_CODING_NETEQ_TOOLS_AUDIO_LOOP_H_
#define WEBRTC_MODULES_AUDIO_CODING_NETEQ_TOOLS_AUDIO_LOOP_H_
#include <string>
#include "webrtc/base/constructormagic.h"
#include "webrtc/system_wrappers/interface/scoped_ptr.h"
#include "webrtc/typedefs.h"
namespace webrtc {
namespace test {
// Class serving as an infinite source of audio, realized by looping an audio
// clip.
class AudioLoop {
public:
AudioLoop()
: next_index_(0),
loop_length_samples_(0),
block_length_samples_(0) {
}
virtual ~AudioLoop() {}
// Initializes the AudioLoop by reading from |file_name|. The loop will be no
// longer than |max_loop_length_samples|, if the length of the file is
// greater. Otherwise, the loop length is the same as the file length.
// The audio will be delivered in blocks of |block_length_samples|.
// Returns false if the initialization failed, otherwise true.
bool Init(const std::string file_name, size_t max_loop_length_samples,
size_t block_length_samples);
// Returns a pointer to the next block of audio. The number given as
// |block_length_samples| to the Init() function determines how many samples
// that can be safely read from the pointer.
const int16_t* GetNextBlock();
private:
size_t next_index_;
size_t loop_length_samples_;
size_t block_length_samples_;
scoped_ptr<int16_t[]> audio_array_;
DISALLOW_COPY_AND_ASSIGN(AudioLoop);
};
} // namespace test
} // namespace webrtc
#endif // WEBRTC_MODULES_AUDIO_CODING_NETEQ_TOOLS_AUDIO_LOOP_H_

View File

@ -0,0 +1,51 @@
/*
* 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/audio_coding/neteq/tools/input_audio_file.h"
namespace webrtc {
namespace test {
InputAudioFile::InputAudioFile(const std::string file_name) {
fp_ = fopen(file_name.c_str(), "rb");
}
InputAudioFile::~InputAudioFile() { fclose(fp_); }
bool InputAudioFile::Read(size_t samples, int16_t* destination) {
if (!fp_) {
return false;
}
size_t samples_read = fread(destination, sizeof(int16_t), samples, fp_);
if (samples_read < samples) {
// Rewind and read the missing samples.
rewind(fp_);
size_t missing_samples = samples - samples_read;
if (fread(destination, sizeof(int16_t), missing_samples, fp_) <
missing_samples) {
// Could not read enough even after rewinding the file.
return false;
}
}
return true;
}
void InputAudioFile::DuplicateInterleaved(const int16_t* source, size_t samples,
size_t channels,
int16_t* destination) {
for (size_t i = 0; i < samples; ++i) {
for (size_t j = 0; j < channels; ++j) {
destination[i * channels + j] = source[i];
}
}
}
} // namespace test
} // namespace webrtc

View File

@ -0,0 +1,51 @@
/*
* 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_AUDIO_CODING_NETEQ_TOOLS_INPUT_AUDIO_FILE_H_
#define WEBRTC_MODULES_AUDIO_CODING_NETEQ_TOOLS_INPUT_AUDIO_FILE_H_
#include <stdio.h>
#include <string>
#include "webrtc/base/constructormagic.h"
#include "webrtc/typedefs.h"
namespace webrtc {
namespace test {
// Class for handling a looping input audio file.
class InputAudioFile {
public:
explicit InputAudioFile(const std::string file_name);
virtual ~InputAudioFile();
// Reads |samples| elements from source file to |destination|. Returns true
// if the read was successful, otherwise false. If the file end is reached,
// the file is rewound and reading continues from the beginning.
// The output |destination| must have the capacity to hold |samples| elements.
bool Read(size_t samples, int16_t* destination);
// Creates a multi-channel signal from a mono signal. Each sample is repeated
// |channels| times to create an interleaved multi-channel signal where all
// channels are identical. The output |destination| must have the capacity to
// hold samples * channels elements.
static void DuplicateInterleaved(const int16_t* source, size_t samples,
size_t channels, int16_t* destination);
private:
FILE* fp_;
DISALLOW_COPY_AND_ASSIGN(InputAudioFile);
};
} // namespace test
} // namespace webrtc
#endif // WEBRTC_MODULES_AUDIO_CODING_NETEQ_TOOLS_INPUT_AUDIO_FILE_H_

View File

@ -0,0 +1,132 @@
/*
* 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/neteq/tools/neteq_performance_test.h"
#include "webrtc/modules/audio_coding/codecs/pcm16b/include/pcm16b.h"
#include "webrtc/modules/audio_coding/neteq/interface/neteq.h"
#include "webrtc/modules/audio_coding/neteq/tools/audio_loop.h"
#include "webrtc/modules/audio_coding/neteq/tools/rtp_generator.h"
#include "webrtc/system_wrappers/interface/clock.h"
#include "webrtc/test/testsupport/fileutils.h"
#include "webrtc/typedefs.h"
using webrtc::NetEq;
using webrtc::test::AudioLoop;
using webrtc::test::RtpGenerator;
using webrtc::WebRtcRTPHeader;
namespace webrtc {
namespace test {
int64_t NetEqPerformanceTest::Run(int runtime_ms,
int lossrate,
double drift_factor) {
const std::string kInputFileName =
webrtc::test::ResourcePath("audio_coding/testfile32kHz", "pcm");
const int kSampRateHz = 32000;
const webrtc::NetEqDecoder kDecoderType = webrtc::kDecoderPCM16Bswb32kHz;
const int kPayloadType = 95;
// Initialize NetEq instance.
NetEq::Config config;
config.sample_rate_hz = kSampRateHz;
NetEq* neteq = NetEq::Create(config);
// Register decoder in |neteq|.
if (neteq->RegisterPayloadType(kDecoderType, kPayloadType) != 0)
return -1;
// Set up AudioLoop object.
AudioLoop audio_loop;
const size_t kMaxLoopLengthSamples = kSampRateHz * 10; // 10 second loop.
const size_t kInputBlockSizeSamples = 60 * kSampRateHz / 1000; // 60 ms.
if (!audio_loop.Init(kInputFileName, kMaxLoopLengthSamples,
kInputBlockSizeSamples))
return -1;
int32_t time_now_ms = 0;
// Get first input packet.
WebRtcRTPHeader rtp_header;
RtpGenerator rtp_gen(kSampRateHz / 1000);
// Start with positive drift first half of simulation.
rtp_gen.set_drift_factor(drift_factor);
bool drift_flipped = false;
int32_t packet_input_time_ms =
rtp_gen.GetRtpHeader(kPayloadType, kInputBlockSizeSamples, &rtp_header);
const int16_t* input_samples = audio_loop.GetNextBlock();
if (!input_samples) exit(1);
uint8_t input_payload[kInputBlockSizeSamples * sizeof(int16_t)];
int payload_len = WebRtcPcm16b_Encode(const_cast<int16_t*>(input_samples),
kInputBlockSizeSamples,
input_payload);
assert(payload_len == kInputBlockSizeSamples * sizeof(int16_t));
// Main loop.
webrtc::Clock* clock = webrtc::Clock::GetRealTimeClock();
int64_t start_time_ms = clock->TimeInMilliseconds();
while (time_now_ms < runtime_ms) {
while (packet_input_time_ms <= time_now_ms) {
// Drop every N packets, where N = FLAGS_lossrate.
bool lost = false;
if (lossrate > 0) {
lost = ((rtp_header.header.sequenceNumber - 1) % lossrate) == 0;
}
if (!lost) {
// Insert packet.
int error = neteq->InsertPacket(
rtp_header, input_payload, payload_len,
packet_input_time_ms * kSampRateHz / 1000);
if (error != NetEq::kOK)
return -1;
}
// Get next packet.
packet_input_time_ms = rtp_gen.GetRtpHeader(kPayloadType,
kInputBlockSizeSamples,
&rtp_header);
input_samples = audio_loop.GetNextBlock();
if (!input_samples) return -1;
payload_len = WebRtcPcm16b_Encode(const_cast<int16_t*>(input_samples),
kInputBlockSizeSamples,
input_payload);
assert(payload_len == kInputBlockSizeSamples * sizeof(int16_t));
}
// Get output audio, but don't do anything with it.
static const int kMaxChannels = 1;
static const int kMaxSamplesPerMs = 48000 / 1000;
static const int kOutputBlockSizeMs = 10;
static const int kOutDataLen = kOutputBlockSizeMs * kMaxSamplesPerMs *
kMaxChannels;
int16_t out_data[kOutDataLen];
int num_channels;
int samples_per_channel;
int error = neteq->GetAudio(kOutDataLen, out_data, &samples_per_channel,
&num_channels, NULL);
if (error != NetEq::kOK)
return -1;
assert(samples_per_channel == kSampRateHz * 10 / 1000);
time_now_ms += kOutputBlockSizeMs;
if (time_now_ms >= runtime_ms / 2 && !drift_flipped) {
// Apply negative drift second half of simulation.
rtp_gen.set_drift_factor(-drift_factor);
drift_flipped = true;
}
}
int64_t end_time_ms = clock->TimeInMilliseconds();
delete neteq;
return end_time_ms - start_time_ms;
}
} // namespace test
} // namespace webrtc

View File

@ -0,0 +1,32 @@
/*
* 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_NETEQ_TOOLS_NETEQ_PERFORMANCE_TEST_H_
#define WEBRTC_MODULES_AUDIO_CODING_NETEQ_TOOLS_NETEQ_PERFORMANCE_TEST_H_
#include "webrtc/typedefs.h"
namespace webrtc {
namespace test {
class NetEqPerformanceTest {
public:
// Runs a performance test with parameters as follows:
// |runtime_ms|: the simulation time, i.e., the duration of the audio data.
// |lossrate|: drop one out of |lossrate| packets, e.g., one out of 10.
// |drift_factor|: clock drift in [0, 1].
// Returns the runtime in ms.
static int64_t Run(int runtime_ms, int lossrate, double drift_factor);
};
} // namespace test
} // namespace webrtc
#endif // WEBRTC_MODULES_AUDIO_CODING_NETEQ_TOOLS_NETEQ_PERFORMANCE_TEST_H_

View File

@ -0,0 +1,115 @@
/*
* 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 <stdio.h>
#include "webrtc/modules/audio_coding/neteq/tools/neteq_quality_test.h"
namespace webrtc {
namespace test {
const uint8_t kPayloadType = 95;
const int kOutputSizeMs = 10;
NetEqQualityTest::NetEqQualityTest(int block_duration_ms,
int in_sampling_khz,
int out_sampling_khz,
enum NetEqDecoder decoder_type,
int channels,
double drift_factor,
std::string in_filename,
std::string out_filename)
: decoded_time_ms_(0),
decodable_time_ms_(0),
drift_factor_(drift_factor),
block_duration_ms_(block_duration_ms),
in_sampling_khz_(in_sampling_khz),
out_sampling_khz_(out_sampling_khz),
decoder_type_(decoder_type),
channels_(channels),
in_filename_(in_filename),
out_filename_(out_filename),
in_size_samples_(in_sampling_khz_ * block_duration_ms_),
out_size_samples_(out_sampling_khz_ * kOutputSizeMs),
payload_size_bytes_(0),
max_payload_bytes_(0),
in_file_(new InputAudioFile(in_filename_)),
out_file_(NULL),
rtp_generator_(new RtpGenerator(in_sampling_khz_, 0, 0,
decodable_time_ms_)) {
NetEq::Config config;
config.sample_rate_hz = out_sampling_khz_ * 1000;
neteq_.reset(NetEq::Create(config));
max_payload_bytes_ = in_size_samples_ * channels_ * sizeof(int16_t);
in_data_.reset(new int16_t[in_size_samples_ * channels_]);
payload_.reset(new uint8_t[max_payload_bytes_]);
out_data_.reset(new int16_t[out_size_samples_ * channels_]);
}
void NetEqQualityTest::SetUp() {
out_file_ = fopen(out_filename_.c_str(), "wb");
ASSERT_TRUE(out_file_ != NULL);
ASSERT_EQ(0, neteq_->RegisterPayloadType(decoder_type_, kPayloadType));
rtp_generator_->set_drift_factor(drift_factor_);
}
void NetEqQualityTest::TearDown() {
fclose(out_file_);
}
int NetEqQualityTest::Transmit() {
int packet_input_time_ms =
rtp_generator_->GetRtpHeader(kPayloadType, in_size_samples_,
&rtp_header_);
if (!PacketLost(packet_input_time_ms) && payload_size_bytes_ > 0) {
int ret = neteq_->InsertPacket(rtp_header_, &payload_[0],
payload_size_bytes_,
packet_input_time_ms * in_sampling_khz_);
if (ret != NetEq::kOK)
return -1;
}
return packet_input_time_ms;
}
int NetEqQualityTest::DecodeBlock() {
int channels;
int samples;
int ret = neteq_->GetAudio(out_size_samples_ * channels_, &out_data_[0],
&samples, &channels, NULL);
if (ret != NetEq::kOK) {
return -1;
} else {
assert(channels == channels_);
assert(samples == kOutputSizeMs * out_sampling_khz_);
fwrite(&out_data_[0], sizeof(int16_t), samples * channels, out_file_);
return samples;
}
}
void NetEqQualityTest::Simulate(int end_time_ms) {
int audio_size_samples;
while (decoded_time_ms_ < end_time_ms) {
while (decodable_time_ms_ - kOutputSizeMs < decoded_time_ms_) {
ASSERT_TRUE(in_file_->Read(in_size_samples_ * channels_, &in_data_[0]));
payload_size_bytes_ = EncodeBlock(&in_data_[0],
in_size_samples_, &payload_[0],
max_payload_bytes_);
decodable_time_ms_ = Transmit() + block_duration_ms_;
}
audio_size_samples = DecodeBlock();
if (audio_size_samples > 0) {
decoded_time_ms_ += audio_size_samples / out_sampling_khz_;
}
}
}
} // namespace test
} // namespace webrtc

View File

@ -0,0 +1,100 @@
/*
* 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_NETEQ_TOOLS_NETEQ_QUALITY_TEST_H_
#define WEBRTC_MODULES_AUDIO_CODING_NETEQ_TOOLS_NETEQ_QUALITY_TEST_H_
#include <string>
#include "testing/gtest/include/gtest/gtest.h"
#include "webrtc/modules/audio_coding/neteq/interface/neteq.h"
#include "webrtc/modules/audio_coding/neteq/tools/input_audio_file.h"
#include "webrtc/modules/audio_coding/neteq/tools/rtp_generator.h"
#include "webrtc/system_wrappers/interface/scoped_ptr.h"
#include "webrtc/typedefs.h"
namespace webrtc {
namespace test {
class NetEqQualityTest : public ::testing::Test {
protected:
NetEqQualityTest(int block_duration_ms,
int in_sampling_khz,
int out_sampling_khz,
enum NetEqDecoder decoder_type,
int channels,
double drift_factor,
std::string in_filename,
std::string out_filename);
virtual void SetUp() OVERRIDE;
virtual void TearDown() OVERRIDE;
// EncodeBlock(...) does the following:
// 1. encodes a block of audio, saved in |in_data| and has a length of
// |block_size_samples| (samples per channel),
// 2. save the bit stream to |payload| of |max_bytes| bytes in size,
// 3. returns the length of the payload (in bytes),
virtual int EncodeBlock(int16_t* in_data, int block_size_samples,
uint8_t* payload, int max_bytes) = 0;
// PacketLoss(...) determines weather a packet sent at an indicated time gets
// lost or not.
virtual bool PacketLost(int packet_input_time_ms) { return false; }
// DecodeBlock() decodes a block of audio using the payload stored in
// |payload_| with the length of |payload_size_bytes_| (bytes). The decoded
// audio is to be stored in |out_data_|.
int DecodeBlock();
// Transmit() uses |rtp_generator_| to generate a packet and passes it to
// |neteq_|.
int Transmit();
// Simulate(...) runs encoding / transmitting / decoding up to |end_time_ms|
// (miliseconds), the resulted audio is stored in the file with the name of
// |out_filename_|.
void Simulate(int end_time_ms);
private:
int decoded_time_ms_;
int decodable_time_ms_;
double drift_factor_;
const int block_duration_ms_;
const int in_sampling_khz_;
const int out_sampling_khz_;
const enum NetEqDecoder decoder_type_;
const int channels_;
const std::string in_filename_;
const std::string out_filename_;
// Number of samples per channel in a frame.
const int in_size_samples_;
// Expected output number of samples per channel in a frame.
const int out_size_samples_;
int payload_size_bytes_;
int max_payload_bytes_;
scoped_ptr<InputAudioFile> in_file_;
FILE* out_file_;
scoped_ptr<RtpGenerator> rtp_generator_;
scoped_ptr<NetEq> neteq_;
scoped_ptr<int16_t[]> in_data_;
scoped_ptr<uint8_t[]> payload_;
scoped_ptr<int16_t[]> out_data_;
WebRtcRTPHeader rtp_header_;
};
} // namespace test
} // namespace webrtc
#endif // WEBRTC_MODULES_AUDIO_CODING_NETEQ_TOOLS_NETEQ_QUALITY_TEST_H_

View File

@ -0,0 +1,628 @@
/*
* 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.
*/
// TODO(hlundin): The functionality in this file should be moved into one or
// several classes.
#include <assert.h>
#include <stdio.h>
#include <algorithm>
#include <iostream>
#include <string>
#include "google/gflags.h"
#include "webrtc/modules/audio_coding/codecs/pcm16b/include/pcm16b.h"
#include "webrtc/modules/audio_coding/neteq/interface/neteq.h"
#include "webrtc/modules/audio_coding/neteq/test/NETEQTEST_RTPpacket.h"
#include "webrtc/modules/audio_coding/neteq/test/NETEQTEST_DummyRTPpacket.h"
#include "webrtc/modules/audio_coding/neteq/tools/input_audio_file.h"
#include "webrtc/modules/interface/module_common_types.h"
#include "webrtc/system_wrappers/interface/scoped_ptr.h"
#include "webrtc/system_wrappers/interface/trace.h"
#include "webrtc/test/testsupport/fileutils.h"
#include "webrtc/typedefs.h"
using webrtc::NetEq;
using webrtc::WebRtcRTPHeader;
// Flag validators.
static bool ValidatePayloadType(const char* flagname, int32_t value) {
if (value >= 0 && value <= 127) // 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(pcmu, 0, "RTP payload type for PCM-u");
static const bool pcmu_dummy =
google::RegisterFlagValidator(&FLAGS_pcmu, &ValidatePayloadType);
DEFINE_int32(pcma, 8, "RTP payload type for PCM-a");
static const bool pcma_dummy =
google::RegisterFlagValidator(&FLAGS_pcma, &ValidatePayloadType);
DEFINE_int32(ilbc, 102, "RTP payload type for iLBC");
static const bool ilbc_dummy =
google::RegisterFlagValidator(&FLAGS_ilbc, &ValidatePayloadType);
DEFINE_int32(isac, 103, "RTP payload type for iSAC");
static const bool isac_dummy =
google::RegisterFlagValidator(&FLAGS_isac, &ValidatePayloadType);
DEFINE_int32(isac_swb, 104, "RTP payload type for iSAC-swb (32 kHz)");
static const bool isac_swb_dummy =
google::RegisterFlagValidator(&FLAGS_isac_swb, &ValidatePayloadType);
DEFINE_int32(pcm16b, 93, "RTP payload type for PCM16b-nb (8 kHz)");
static const bool pcm16b_dummy =
google::RegisterFlagValidator(&FLAGS_pcm16b, &ValidatePayloadType);
DEFINE_int32(pcm16b_wb, 94, "RTP payload type for PCM16b-wb (16 kHz)");
static const bool pcm16b_wb_dummy =
google::RegisterFlagValidator(&FLAGS_pcm16b_wb, &ValidatePayloadType);
DEFINE_int32(pcm16b_swb32, 95, "RTP payload type for PCM16b-swb32 (32 kHz)");
static const bool pcm16b_swb32_dummy =
google::RegisterFlagValidator(&FLAGS_pcm16b_swb32, &ValidatePayloadType);
DEFINE_int32(pcm16b_swb48, 96, "RTP payload type for PCM16b-swb48 (48 kHz)");
static const bool pcm16b_swb48_dummy =
google::RegisterFlagValidator(&FLAGS_pcm16b_swb48, &ValidatePayloadType);
DEFINE_int32(g722, 9, "RTP payload type for G.722");
static const bool g722_dummy =
google::RegisterFlagValidator(&FLAGS_g722, &ValidatePayloadType);
DEFINE_int32(avt, 106, "RTP payload type for AVT/DTMF");
static const bool avt_dummy =
google::RegisterFlagValidator(&FLAGS_avt, &ValidatePayloadType);
DEFINE_int32(red, 117, "RTP payload type for redundant audio (RED)");
static const bool red_dummy =
google::RegisterFlagValidator(&FLAGS_red, &ValidatePayloadType);
DEFINE_int32(cn_nb, 13, "RTP payload type for comfort noise (8 kHz)");
static const bool cn_nb_dummy =
google::RegisterFlagValidator(&FLAGS_cn_nb, &ValidatePayloadType);
DEFINE_int32(cn_wb, 98, "RTP payload type for comfort noise (16 kHz)");
static const bool cn_wb_dummy =
google::RegisterFlagValidator(&FLAGS_cn_wb, &ValidatePayloadType);
DEFINE_int32(cn_swb32, 99, "RTP payload type for comfort noise (32 kHz)");
static const bool cn_swb32_dummy =
google::RegisterFlagValidator(&FLAGS_cn_swb32, &ValidatePayloadType);
DEFINE_int32(cn_swb48, 100, "RTP payload type for comfort noise (48 kHz)");
static const bool cn_swb48_dummy =
google::RegisterFlagValidator(&FLAGS_cn_swb48, &ValidatePayloadType);
DEFINE_bool(codec_map, false, "Prints the mapping between RTP payload type and "
"codec");
DEFINE_bool(dummy_rtp, false, "The input file contains ""dummy"" RTP data, "
"i.e., only headers");
DEFINE_string(replacement_audio_file, "",
"A PCM file that will be used to populate ""dummy"" RTP packets");
// Declaring helper functions (defined further down in this file).
std::string CodecName(webrtc::NetEqDecoder codec);
void RegisterPayloadTypes(NetEq* neteq);
void PrintCodecMapping();
size_t ReplacePayload(webrtc::test::InputAudioFile* replacement_audio_file,
webrtc::scoped_ptr<int16_t[]>* replacement_audio,
webrtc::scoped_ptr<uint8_t[]>* payload,
size_t* payload_mem_size_bytes,
size_t* frame_size_samples,
WebRtcRTPHeader* rtp_header,
NETEQTEST_RTPpacket* next_rtp);
int CodecSampleRate(uint8_t payload_type);
int CodecTimestampRate(uint8_t payload_type);
bool IsComfortNosie(uint8_t payload_type);
int main(int argc, char* argv[]) {
static const int kMaxChannels = 5;
static const int kMaxSamplesPerMs = 48000 / 1000;
static const int kOutputBlockSizeMs = 10;
std::string program_name = argv[0];
std::string usage = "Tool for decoding an RTP dump file using NetEq.\n"
"Run " + program_name + " --helpshort for usage.\n"
"Example usage:\n" + program_name +
" input.rtp output.pcm\n";
google::SetUsageMessage(usage);
google::ParseCommandLineFlags(&argc, &argv, true);
if (FLAGS_codec_map) {
PrintCodecMapping();
}
if (argc != 3) {
if (FLAGS_codec_map) {
// We have already printed the codec map. Just end the program.
return 0;
}
// Print usage information.
std::cout << google::ProgramUsage();
return 0;
}
FILE* in_file = fopen(argv[1], "rb");
if (!in_file) {
std::cerr << "Cannot open input file " << argv[1] << std::endl;
exit(1);
}
std::cout << "Input file: " << argv[1] << std::endl;
FILE* out_file = fopen(argv[2], "wb");
if (!in_file) {
std::cerr << "Cannot open output file " << argv[2] << std::endl;
exit(1);
}
std::cout << "Output file: " << argv[2] << std::endl;
// Check if a replacement audio file was provided, and if so, open it.
bool replace_payload = false;
webrtc::scoped_ptr<webrtc::test::InputAudioFile> replacement_audio_file;
if (!FLAGS_replacement_audio_file.empty()) {
replacement_audio_file.reset(
new webrtc::test::InputAudioFile(FLAGS_replacement_audio_file));
replace_payload = true;
}
// Read RTP file header.
if (NETEQTEST_RTPpacket::skipFileHeader(in_file) != 0) {
std::cerr << "Wrong format in RTP file" << std::endl;
exit(1);
}
// Enable tracing.
webrtc::Trace::CreateTrace();
webrtc::Trace::SetTraceFile((webrtc::test::OutputPath() +
"neteq_trace.txt").c_str());
webrtc::Trace::set_level_filter(webrtc::kTraceAll);
// Initialize NetEq instance.
int sample_rate_hz = 16000;
NetEq::Config config;
config.sample_rate_hz = sample_rate_hz;
NetEq* neteq = NetEq::Create(config);
RegisterPayloadTypes(neteq);
// Read first packet.
NETEQTEST_RTPpacket* rtp;
NETEQTEST_RTPpacket* next_rtp = NULL;
if (!FLAGS_dummy_rtp) {
rtp = new NETEQTEST_RTPpacket();
if (replace_payload) {
next_rtp = new NETEQTEST_RTPpacket();
}
} else {
rtp = new NETEQTEST_DummyRTPpacket();
if (replace_payload) {
next_rtp = new NETEQTEST_DummyRTPpacket();
}
}
rtp->readFromFile(in_file);
if (rtp->dataLen() < 0) {
std::cout << "Warning: RTP file is empty" << std::endl;
}
// Set up variables for audio replacement if needed.
size_t input_frame_size_timestamps = 0;
webrtc::scoped_ptr<int16_t[]> replacement_audio;
webrtc::scoped_ptr<uint8_t[]> payload;
size_t payload_mem_size_bytes = 0;
if (replace_payload) {
// Initially assume that the frame size is 30 ms at the initial sample rate.
// This value will be replaced with the correct one as soon as two
// consecutive packets are found.
input_frame_size_timestamps = 30 * sample_rate_hz / 1000;
replacement_audio.reset(new int16_t[input_frame_size_timestamps]);
payload_mem_size_bytes = 2 * input_frame_size_timestamps;
payload.reset(new uint8_t[payload_mem_size_bytes]);
assert(next_rtp);
next_rtp->readFromFile(in_file);
}
// This is the main simulation loop.
int time_now_ms = rtp->time(); // Start immediately with the first packet.
int next_input_time_ms = rtp->time();
int next_output_time_ms = time_now_ms;
if (time_now_ms % kOutputBlockSizeMs != 0) {
// Make sure that next_output_time_ms is rounded up to the next multiple
// of kOutputBlockSizeMs. (Legacy bit-exactness.)
next_output_time_ms +=
kOutputBlockSizeMs - time_now_ms % kOutputBlockSizeMs;
}
while (rtp->dataLen() >= 0) {
// Check if it is time to insert packet.
while (time_now_ms >= next_input_time_ms && rtp->dataLen() >= 0) {
if (rtp->dataLen() > 0) {
// Parse RTP header.
WebRtcRTPHeader rtp_header;
rtp->parseHeader(&rtp_header);
uint8_t* payload_ptr = rtp->payload();
size_t payload_len = rtp->payloadLen();
if (replace_payload) {
payload_len = ReplacePayload(replacement_audio_file.get(),
&replacement_audio,
&payload,
&payload_mem_size_bytes,
&input_frame_size_timestamps,
&rtp_header,
next_rtp);
payload_ptr = payload.get();
}
int error = neteq->InsertPacket(rtp_header, payload_ptr,
static_cast<int>(payload_len),
rtp->time() * sample_rate_hz / 1000);
if (error != NetEq::kOK) {
std::cerr << "InsertPacket returned error code " <<
neteq->LastError() << std::endl;
}
}
// Get next packet from file.
rtp->readFromFile(in_file);
if (replace_payload) {
// At this point |rtp| contains the packet *after* |next_rtp|.
// Swap RTP packet objects between |rtp| and |next_rtp|.
NETEQTEST_RTPpacket* temp_rtp = rtp;
rtp = next_rtp;
next_rtp = temp_rtp;
}
next_input_time_ms = rtp->time();
}
// Check if it is time to get output audio.
if (time_now_ms >= next_output_time_ms) {
static const int kOutDataLen = kOutputBlockSizeMs * kMaxSamplesPerMs *
kMaxChannels;
int16_t out_data[kOutDataLen];
int num_channels;
int samples_per_channel;
int error = neteq->GetAudio(kOutDataLen, out_data, &samples_per_channel,
&num_channels, NULL);
if (error != NetEq::kOK) {
std::cerr << "GetAudio returned error code " <<
neteq->LastError() << std::endl;
} else {
// Calculate sample rate from output size.
sample_rate_hz = 1000 * samples_per_channel / kOutputBlockSizeMs;
}
// Write to file.
// TODO(hlundin): Make writing to file optional.
size_t write_len = samples_per_channel * num_channels;
if (fwrite(out_data, sizeof(out_data[0]), write_len, out_file) !=
write_len) {
std::cerr << "Error while writing to file" << std::endl;
webrtc::Trace::ReturnTrace();
exit(1);
}
next_output_time_ms += kOutputBlockSizeMs;
}
// Advance time to next event.
time_now_ms = std::min(next_input_time_ms, next_output_time_ms);
}
std::cout << "Simulation done" << std::endl;
fclose(in_file);
fclose(out_file);
delete rtp;
delete next_rtp;
delete neteq;
webrtc::Trace::ReturnTrace();
return 0;
}
// Help functions.
// Maps a codec type to a printable name string.
std::string CodecName(webrtc::NetEqDecoder codec) {
switch (codec) {
case webrtc::kDecoderPCMu:
return "PCM-u";
case webrtc::kDecoderPCMa:
return "PCM-a";
case webrtc::kDecoderILBC:
return "iLBC";
case webrtc::kDecoderISAC:
return "iSAC";
case webrtc::kDecoderISACswb:
return "iSAC-swb (32 kHz)";
case webrtc::kDecoderPCM16B:
return "PCM16b-nb (8 kHz)";
case webrtc::kDecoderPCM16Bwb:
return "PCM16b-wb (16 kHz)";
case webrtc::kDecoderPCM16Bswb32kHz:
return "PCM16b-swb32 (32 kHz)";
case webrtc::kDecoderPCM16Bswb48kHz:
return "PCM16b-swb48 (48 kHz)";
case webrtc::kDecoderG722:
return "G.722";
case webrtc::kDecoderRED:
return "redundant audio (RED)";
case webrtc::kDecoderAVT:
return "AVT/DTMF";
case webrtc::kDecoderCNGnb:
return "comfort noise (8 kHz)";
case webrtc::kDecoderCNGwb:
return "comfort noise (16 kHz)";
case webrtc::kDecoderCNGswb32kHz:
return "comfort noise (32 kHz)";
case webrtc::kDecoderCNGswb48kHz:
return "comfort noise (48 kHz)";
default:
assert(false);
return "undefined";
}
}
// Registers all decoders in |neteq|.
void RegisterPayloadTypes(NetEq* neteq) {
assert(neteq);
int error;
error = neteq->RegisterPayloadType(webrtc::kDecoderPCMu, FLAGS_pcmu);
if (error) {
std::cerr << "Cannot register payload type " << FLAGS_pcmu <<
" as " << CodecName(webrtc::kDecoderPCMu).c_str() << std::endl;
exit(1);
}
error = neteq->RegisterPayloadType(webrtc::kDecoderPCMa, FLAGS_pcma);
if (error) {
std::cerr << "Cannot register payload type " << FLAGS_pcma <<
" as " << CodecName(webrtc::kDecoderPCMa).c_str() << std::endl;
exit(1);
}
error = neteq->RegisterPayloadType(webrtc::kDecoderILBC, FLAGS_ilbc);
if (error) {
std::cerr << "Cannot register payload type " << FLAGS_ilbc <<
" as " << CodecName(webrtc::kDecoderILBC).c_str() << std::endl;
exit(1);
}
error = neteq->RegisterPayloadType(webrtc::kDecoderISAC, FLAGS_isac);
if (error) {
std::cerr << "Cannot register payload type " << FLAGS_isac <<
" as " << CodecName(webrtc::kDecoderISAC).c_str() << std::endl;
exit(1);
}
error = neteq->RegisterPayloadType(webrtc::kDecoderISACswb, FLAGS_isac_swb);
if (error) {
std::cerr << "Cannot register payload type " << FLAGS_isac_swb <<
" as " << CodecName(webrtc::kDecoderISACswb).c_str() << std::endl;
exit(1);
}
error = neteq->RegisterPayloadType(webrtc::kDecoderPCM16B, FLAGS_pcm16b);
if (error) {
std::cerr << "Cannot register payload type " << FLAGS_pcm16b <<
" as " << CodecName(webrtc::kDecoderPCM16B).c_str() << std::endl;
exit(1);
}
error = neteq->RegisterPayloadType(webrtc::kDecoderPCM16Bwb,
FLAGS_pcm16b_wb);
if (error) {
std::cerr << "Cannot register payload type " << FLAGS_pcm16b_wb <<
" as " << CodecName(webrtc::kDecoderPCM16Bwb).c_str() << std::endl;
exit(1);
}
error = neteq->RegisterPayloadType(webrtc::kDecoderPCM16Bswb32kHz,
FLAGS_pcm16b_swb32);
if (error) {
std::cerr << "Cannot register payload type " << FLAGS_pcm16b_swb32 <<
" as " << CodecName(webrtc::kDecoderPCM16Bswb32kHz).c_str() <<
std::endl;
exit(1);
}
error = neteq->RegisterPayloadType(webrtc::kDecoderPCM16Bswb48kHz,
FLAGS_pcm16b_swb48);
if (error) {
std::cerr << "Cannot register payload type " << FLAGS_pcm16b_swb48 <<
" as " << CodecName(webrtc::kDecoderPCM16Bswb48kHz).c_str() <<
std::endl;
exit(1);
}
error = neteq->RegisterPayloadType(webrtc::kDecoderG722, FLAGS_g722);
if (error) {
std::cerr << "Cannot register payload type " << FLAGS_g722 <<
" as " << CodecName(webrtc::kDecoderG722).c_str() << std::endl;
exit(1);
}
error = neteq->RegisterPayloadType(webrtc::kDecoderAVT, FLAGS_avt);
if (error) {
std::cerr << "Cannot register payload type " << FLAGS_avt <<
" as " << CodecName(webrtc::kDecoderAVT).c_str() << std::endl;
exit(1);
}
error = neteq->RegisterPayloadType(webrtc::kDecoderRED, FLAGS_red);
if (error) {
std::cerr << "Cannot register payload type " << FLAGS_red <<
" as " << CodecName(webrtc::kDecoderRED).c_str() << std::endl;
exit(1);
}
error = neteq->RegisterPayloadType(webrtc::kDecoderCNGnb, FLAGS_cn_nb);
if (error) {
std::cerr << "Cannot register payload type " << FLAGS_cn_nb <<
" as " << CodecName(webrtc::kDecoderCNGnb).c_str() << std::endl;
exit(1);
}
error = neteq->RegisterPayloadType(webrtc::kDecoderCNGwb, FLAGS_cn_wb);
if (error) {
std::cerr << "Cannot register payload type " << FLAGS_cn_wb <<
" as " << CodecName(webrtc::kDecoderCNGwb).c_str() << std::endl;
exit(1);
}
error = neteq->RegisterPayloadType(webrtc::kDecoderCNGswb32kHz,
FLAGS_cn_swb32);
if (error) {
std::cerr << "Cannot register payload type " << FLAGS_cn_swb32 <<
" as " << CodecName(webrtc::kDecoderCNGswb32kHz).c_str() << std::endl;
exit(1);
}
error = neteq->RegisterPayloadType(webrtc::kDecoderCNGswb48kHz,
FLAGS_cn_swb48);
if (error) {
std::cerr << "Cannot register payload type " << FLAGS_cn_swb48 <<
" as " << CodecName(webrtc::kDecoderCNGswb48kHz).c_str() << std::endl;
exit(1);
}
}
void PrintCodecMapping() {
std::cout << CodecName(webrtc::kDecoderPCMu).c_str() << ": " << FLAGS_pcmu <<
std::endl;
std::cout << CodecName(webrtc::kDecoderPCMa).c_str() << ": " << FLAGS_pcma <<
std::endl;
std::cout << CodecName(webrtc::kDecoderILBC).c_str() << ": " << FLAGS_ilbc <<
std::endl;
std::cout << CodecName(webrtc::kDecoderISAC).c_str() << ": " << FLAGS_isac <<
std::endl;
std::cout << CodecName(webrtc::kDecoderISACswb).c_str() << ": " <<
FLAGS_isac_swb << std::endl;
std::cout << CodecName(webrtc::kDecoderPCM16B).c_str() << ": " <<
FLAGS_pcm16b << std::endl;
std::cout << CodecName(webrtc::kDecoderPCM16Bwb).c_str() << ": " <<
FLAGS_pcm16b_wb << std::endl;
std::cout << CodecName(webrtc::kDecoderPCM16Bswb32kHz).c_str() << ": " <<
FLAGS_pcm16b_swb32 << std::endl;
std::cout << CodecName(webrtc::kDecoderPCM16Bswb48kHz).c_str() << ": " <<
FLAGS_pcm16b_swb48 << std::endl;
std::cout << CodecName(webrtc::kDecoderG722).c_str() << ": " << FLAGS_g722 <<
std::endl;
std::cout << CodecName(webrtc::kDecoderAVT).c_str() << ": " << FLAGS_avt <<
std::endl;
std::cout << CodecName(webrtc::kDecoderRED).c_str() << ": " << FLAGS_red <<
std::endl;
std::cout << CodecName(webrtc::kDecoderCNGnb).c_str() << ": " <<
FLAGS_cn_nb << std::endl;
std::cout << CodecName(webrtc::kDecoderCNGwb).c_str() << ": " <<
FLAGS_cn_wb << std::endl;
std::cout << CodecName(webrtc::kDecoderCNGswb32kHz).c_str() << ": " <<
FLAGS_cn_swb32 << std::endl;
std::cout << CodecName(webrtc::kDecoderCNGswb48kHz).c_str() << ": " <<
FLAGS_cn_swb48 << std::endl;
}
size_t ReplacePayload(webrtc::test::InputAudioFile* replacement_audio_file,
webrtc::scoped_ptr<int16_t[]>* replacement_audio,
webrtc::scoped_ptr<uint8_t[]>* payload,
size_t* payload_mem_size_bytes,
size_t* frame_size_samples,
WebRtcRTPHeader* rtp_header,
NETEQTEST_RTPpacket* next_rtp) {
size_t payload_len = 0;
// Check for CNG.
if (IsComfortNosie(rtp_header->header.payloadType)) {
// If CNG, simply insert a zero-energy one-byte payload.
if (*payload_mem_size_bytes < 1) {
(*payload).reset(new uint8_t[1]);
*payload_mem_size_bytes = 1;
}
(*payload)[0] = 127; // Max attenuation of CNG.
payload_len = 1;
} else {
if (next_rtp->payloadLen() > 0) {
// Check if payload length has changed.
if (next_rtp->sequenceNumber() == rtp_header->header.sequenceNumber + 1) {
if (*frame_size_samples !=
next_rtp->timeStamp() - rtp_header->header.timestamp) {
*frame_size_samples =
next_rtp->timeStamp() - rtp_header->header.timestamp;
(*replacement_audio).reset(
new int16_t[*frame_size_samples]);
*payload_mem_size_bytes = 2 * *frame_size_samples;
(*payload).reset(new uint8_t[*payload_mem_size_bytes]);
}
}
}
// Get new speech.
assert((*replacement_audio).get());
if (CodecTimestampRate(rtp_header->header.payloadType) !=
CodecSampleRate(rtp_header->header.payloadType) ||
rtp_header->header.payloadType == FLAGS_red ||
rtp_header->header.payloadType == FLAGS_avt) {
// Some codecs have different sample and timestamp rates. And neither
// RED nor DTMF is supported for replacement.
std::cerr << "Codec not supported for audio replacement." <<
std::endl;
webrtc::Trace::ReturnTrace();
exit(1);
}
assert(*frame_size_samples > 0);
if (!replacement_audio_file->Read(*frame_size_samples,
(*replacement_audio).get())) {
std::cerr << "Could no read replacement audio file." << std::endl;
webrtc::Trace::ReturnTrace();
exit(1);
}
// Encode it as PCM16.
assert((*payload).get());
payload_len = WebRtcPcm16b_Encode((*replacement_audio).get(),
static_cast<int16_t>(*frame_size_samples),
(*payload).get());
assert(payload_len == 2 * *frame_size_samples);
// Change payload type to PCM16.
switch (CodecSampleRate(rtp_header->header.payloadType)) {
case 8000:
rtp_header->header.payloadType = FLAGS_pcm16b;
break;
case 16000:
rtp_header->header.payloadType = FLAGS_pcm16b_wb;
break;
case 32000:
rtp_header->header.payloadType = FLAGS_pcm16b_swb32;
break;
case 48000:
rtp_header->header.payloadType = FLAGS_pcm16b_swb48;
break;
default:
std::cerr << "Payload type " <<
static_cast<int>(rtp_header->header.payloadType) <<
" not supported or unknown." << std::endl;
webrtc::Trace::ReturnTrace();
exit(1);
assert(false);
}
}
return payload_len;
}
int CodecSampleRate(uint8_t payload_type) {
if (payload_type == FLAGS_pcmu ||
payload_type == FLAGS_pcma ||
payload_type == FLAGS_ilbc ||
payload_type == FLAGS_pcm16b ||
payload_type == FLAGS_cn_nb) {
return 8000;
} else if (payload_type == FLAGS_isac ||
payload_type == FLAGS_pcm16b_wb ||
payload_type == FLAGS_g722 ||
payload_type == FLAGS_cn_wb) {
return 16000;
} else if (payload_type == FLAGS_isac_swb ||
payload_type == FLAGS_pcm16b_swb32 ||
payload_type == FLAGS_cn_swb32) {
return 32000;
} else if (payload_type == FLAGS_pcm16b_swb48 ||
payload_type == FLAGS_cn_swb48) {
return 48000;
} else if (payload_type == FLAGS_avt ||
payload_type == FLAGS_red) {
return 0;
} else {
return -1;
}
}
int CodecTimestampRate(uint8_t payload_type) {
if (payload_type == FLAGS_g722) {
return 8000;
} else {
return CodecSampleRate(payload_type);
}
}
bool IsComfortNosie(uint8_t payload_type) {
if (payload_type == FLAGS_cn_nb ||
payload_type == FLAGS_cn_wb ||
payload_type == FLAGS_cn_swb32 ||
payload_type == FLAGS_cn_swb48) {
return true;
} else {
return false;
}
}

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/neteq/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_NETEQ_TOOLS_PACKET_H_
#define WEBRTC_MODULES_AUDIO_CODING_NETEQ_TOOLS_PACKET_H_
#include <list>
#include "webrtc/base/constructormagic.h"
#include "webrtc/common_types.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_NETEQ_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_NETEQ_TOOLS_PACKET_SOURCE_H_
#define WEBRTC_MODULES_AUDIO_CODING_NETEQ_TOOLS_PACKET_SOURCE_H_
#include "webrtc/base/constructormagic.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_NETEQ_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/neteq/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

@ -0,0 +1,147 @@
/*
* Copyright (c) 2012 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 <assert.h>
#include <stdio.h>
#include <vector>
#include "google/gflags.h"
#include "webrtc/modules/audio_coding/neteq/tools/packet.h"
#include "webrtc/modules/audio_coding/neteq/tools/rtp_file_source.h"
#include "webrtc/system_wrappers/interface/scoped_ptr.h"
// Flag validator.
static bool ValidatePayloadType(const char* flagname, int32_t value) {
if (value >= 0 && value <= 127) // Value is ok.
return true;
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 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];
std::string usage =
"Tool for parsing an RTP dump file to text output.\n"
"Run " +
program_name +
" --helpshort for usage.\n"
"Example usage:\n" +
program_name + " input.rtp output.txt\n\n" +
"Output is sent to stdout if no output file is given." +
"Note that this tool can read files with our without payloads.";
google::SetUsageMessage(usage);
google::ParseCommandLineFlags(&argc, &argv, true);
if (argc != 2 && argc != 3) {
// Print usage information.
printf("%s", google::ProgramUsage());
return 0;
}
FILE* in_file = fopen(argv[1], "rb");
if (!in_file) {
printf("Cannot open input file %s\n", argv[1]);
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) {
out_file = fopen(argv[2], "wt");
if (!out_file) {
printf("Cannot open output file %s\n", argv[2]);
return -1;
}
printf("Output file: %s\n\n", argv[2]);
} else {
out_file = stdout;
}
// Print file header.
fprintf(out_file, "SeqNo TimeStamp SendTime Size PT M SSRC");
if (print_audio_level) {
fprintf(out_file, " AuLvl (V)");
}
fprintf(out_file, "\n");
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",
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\n",
red->sequenceNumber,
red->timestamp,
static_cast<unsigned int>(packet->time_ms()),
red->payloadType);
red_headers.pop_front();
delete red;
}
}
}
fclose(in_file);
fclose(out_file);
return 0;
}

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/neteq/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/neteq/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_NETEQ_TOOLS_RTP_FILE_SOURCE_H_
#define WEBRTC_MODULES_AUDIO_CODING_NETEQ_TOOLS_RTP_FILE_SOURCE_H_
#include <stdio.h>
#include <string>
#include "webrtc/base/constructormagic.h"
#include "webrtc/common_types.h"
#include "webrtc/modules/audio_coding/neteq/tools/packet_source.h"
#include "webrtc/modules/rtp_rtcp/interface/rtp_rtcp_defines.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_NETEQ_TOOLS_RTP_FILE_SOURCE_H_

View File

@ -0,0 +1,48 @@
/*
* 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 <assert.h>
#include "webrtc/modules/audio_coding/neteq/tools/rtp_generator.h"
namespace webrtc {
namespace test {
uint32_t RtpGenerator::GetRtpHeader(uint8_t payload_type,
size_t payload_length_samples,
WebRtcRTPHeader* rtp_header) {
assert(rtp_header);
if (!rtp_header) {
return 0;
}
rtp_header->header.sequenceNumber = seq_number_++;
rtp_header->header.timestamp = timestamp_;
timestamp_ += static_cast<uint32_t>(payload_length_samples);
rtp_header->header.payloadType = payload_type;
rtp_header->header.markerBit = false;
rtp_header->header.ssrc = ssrc_;
rtp_header->header.numCSRCs = 0;
rtp_header->frameType = kAudioFrameSpeech;
uint32_t this_send_time = next_send_time_ms_;
assert(samples_per_ms_ > 0);
next_send_time_ms_ += ((1.0 + drift_factor_) * payload_length_samples) /
samples_per_ms_;
return this_send_time;
}
void RtpGenerator::set_drift_factor(double factor) {
if (factor > -1.0) {
drift_factor_ = factor;
}
}
} // namespace test
} // namespace webrtc

View File

@ -0,0 +1,57 @@
/*
* Copyright (c) 2012 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_NETEQ_TOOLS_RTP_GENERATOR_H_
#define WEBRTC_MODULES_AUDIO_CODING_NETEQ_TOOLS_RTP_GENERATOR_H_
#include "webrtc/base/constructormagic.h"
#include "webrtc/modules/interface/module_common_types.h"
#include "webrtc/typedefs.h"
namespace webrtc {
namespace test {
// Class for generating RTP headers.
class RtpGenerator {
public:
RtpGenerator(int samples_per_ms,
uint16_t start_seq_number = 0,
uint32_t start_timestamp = 0,
uint32_t start_send_time_ms = 0,
uint32_t ssrc = 0x12345678)
: seq_number_(start_seq_number),
timestamp_(start_timestamp),
next_send_time_ms_(start_send_time_ms),
ssrc_(ssrc),
samples_per_ms_(samples_per_ms),
drift_factor_(0.0) {
}
// Writes the next RTP header to |rtp_header|, which will be of type
// |payload_type|. Returns the send time for this packet (in ms). The value of
// |payload_length_samples| determines the send time for the next packet.
uint32_t GetRtpHeader(uint8_t payload_type, size_t payload_length_samples,
WebRtcRTPHeader* rtp_header);
void set_drift_factor(double factor);
private:
uint16_t seq_number_;
uint32_t timestamp_;
uint32_t next_send_time_ms_;
const uint32_t ssrc_;
const int samples_per_ms_;
double drift_factor_;
DISALLOW_COPY_AND_ASSIGN(RtpGenerator);
};
} // namespace test
} // namespace webrtc
#endif // WEBRTC_MODULES_AUDIO_CODING_NETEQ_TOOLS_RTP_GENERATOR_H_