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:
57
webrtc/modules/audio_coding/neteq/tools/audio_loop.cc
Normal file
57
webrtc/modules/audio_coding/neteq/tools/audio_loop.cc
Normal 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
|
59
webrtc/modules/audio_coding/neteq/tools/audio_loop.h
Normal file
59
webrtc/modules/audio_coding/neteq/tools/audio_loop.h
Normal 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_
|
51
webrtc/modules/audio_coding/neteq/tools/input_audio_file.cc
Normal file
51
webrtc/modules/audio_coding/neteq/tools/input_audio_file.cc
Normal 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
|
51
webrtc/modules/audio_coding/neteq/tools/input_audio_file.h
Normal file
51
webrtc/modules/audio_coding/neteq/tools/input_audio_file.h
Normal 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_
|
@ -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
|
@ -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_
|
115
webrtc/modules/audio_coding/neteq/tools/neteq_quality_test.cc
Normal file
115
webrtc/modules/audio_coding/neteq/tools/neteq_quality_test.cc
Normal 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
|
100
webrtc/modules/audio_coding/neteq/tools/neteq_quality_test.h
Normal file
100
webrtc/modules/audio_coding/neteq/tools/neteq_quality_test.h
Normal 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_
|
628
webrtc/modules/audio_coding/neteq/tools/neteq_rtpplay.cc
Normal file
628
webrtc/modules/audio_coding/neteq/tools/neteq_rtpplay.cc
Normal 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;
|
||||
}
|
||||
}
|
155
webrtc/modules/audio_coding/neteq/tools/packet.cc
Normal file
155
webrtc/modules/audio_coding/neteq/tools/packet.cc
Normal file
@ -0,0 +1,155 @@
|
||||
/*
|
||||
* Copyright (c) 2014 The WebRTC project authors. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#include "webrtc/modules/audio_coding/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
|
117
webrtc/modules/audio_coding/neteq/tools/packet.h
Normal file
117
webrtc/modules/audio_coding/neteq/tools/packet.h
Normal file
@ -0,0 +1,117 @@
|
||||
/*
|
||||
* Copyright (c) 2014 The WebRTC project authors. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#ifndef WEBRTC_MODULES_AUDIO_CODING_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_
|
36
webrtc/modules/audio_coding/neteq/tools/packet_source.h
Normal file
36
webrtc/modules/audio_coding/neteq/tools/packet_source.h
Normal file
@ -0,0 +1,36 @@
|
||||
/*
|
||||
* Copyright (c) 2014 The WebRTC project authors. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#ifndef WEBRTC_MODULES_AUDIO_CODING_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_
|
202
webrtc/modules/audio_coding/neteq/tools/packet_unittest.cc
Normal file
202
webrtc/modules/audio_coding/neteq/tools/packet_unittest.cc
Normal file
@ -0,0 +1,202 @@
|
||||
/*
|
||||
* Copyright (c) 2014 The WebRTC project authors. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
// Unit tests for test Packet class.
|
||||
|
||||
#include "webrtc/modules/audio_coding/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
|
147
webrtc/modules/audio_coding/neteq/tools/rtp_analyze.cc
Normal file
147
webrtc/modules/audio_coding/neteq/tools/rtp_analyze.cc
Normal 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;
|
||||
}
|
140
webrtc/modules/audio_coding/neteq/tools/rtp_file_source.cc
Normal file
140
webrtc/modules/audio_coding/neteq/tools/rtp_file_source.cc
Normal file
@ -0,0 +1,140 @@
|
||||
/*
|
||||
* Copyright (c) 2014 The WebRTC project authors. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#include "webrtc/modules/audio_coding/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
|
66
webrtc/modules/audio_coding/neteq/tools/rtp_file_source.h
Normal file
66
webrtc/modules/audio_coding/neteq/tools/rtp_file_source.h
Normal file
@ -0,0 +1,66 @@
|
||||
/*
|
||||
* Copyright (c) 2014 The WebRTC project authors. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#ifndef WEBRTC_MODULES_AUDIO_CODING_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_
|
48
webrtc/modules/audio_coding/neteq/tools/rtp_generator.cc
Normal file
48
webrtc/modules/audio_coding/neteq/tools/rtp_generator.cc
Normal 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
|
57
webrtc/modules/audio_coding/neteq/tools/rtp_generator.h
Normal file
57
webrtc/modules/audio_coding/neteq/tools/rtp_generator.h
Normal 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_
|
Reference in New Issue
Block a user