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
diff --git a/webrtc/modules/audio_coding/neteq/tools/audio_loop.cc b/webrtc/modules/audio_coding/neteq/tools/audio_loop.cc
new file mode 100644
index 0000000..2d2a7e3
--- /dev/null
+++ b/webrtc/modules/audio_coding/neteq/tools/audio_loop.cc
@@ -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
diff --git a/webrtc/modules/audio_coding/neteq/tools/audio_loop.h b/webrtc/modules/audio_coding/neteq/tools/audio_loop.h
new file mode 100644
index 0000000..9647d82
--- /dev/null
+++ b/webrtc/modules/audio_coding/neteq/tools/audio_loop.h
@@ -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_
diff --git a/webrtc/modules/audio_coding/neteq/tools/input_audio_file.cc b/webrtc/modules/audio_coding/neteq/tools/input_audio_file.cc
new file mode 100644
index 0000000..8063173
--- /dev/null
+++ b/webrtc/modules/audio_coding/neteq/tools/input_audio_file.cc
@@ -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
diff --git a/webrtc/modules/audio_coding/neteq/tools/input_audio_file.h b/webrtc/modules/audio_coding/neteq/tools/input_audio_file.h
new file mode 100644
index 0000000..274f8ea
--- /dev/null
+++ b/webrtc/modules/audio_coding/neteq/tools/input_audio_file.h
@@ -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_
diff --git a/webrtc/modules/audio_coding/neteq/tools/neteq_performance_test.cc b/webrtc/modules/audio_coding/neteq/tools/neteq_performance_test.cc
new file mode 100644
index 0000000..433546f
--- /dev/null
+++ b/webrtc/modules/audio_coding/neteq/tools/neteq_performance_test.cc
@@ -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
diff --git a/webrtc/modules/audio_coding/neteq/tools/neteq_performance_test.h b/webrtc/modules/audio_coding/neteq/tools/neteq_performance_test.h
new file mode 100644
index 0000000..d094db0
--- /dev/null
+++ b/webrtc/modules/audio_coding/neteq/tools/neteq_performance_test.h
@@ -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_
diff --git a/webrtc/modules/audio_coding/neteq/tools/neteq_quality_test.cc b/webrtc/modules/audio_coding/neteq/tools/neteq_quality_test.cc
new file mode 100644
index 0000000..fc5d8ab
--- /dev/null
+++ b/webrtc/modules/audio_coding/neteq/tools/neteq_quality_test.cc
@@ -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
diff --git a/webrtc/modules/audio_coding/neteq/tools/neteq_quality_test.h b/webrtc/modules/audio_coding/neteq/tools/neteq_quality_test.h
new file mode 100644
index 0000000..87fc507
--- /dev/null
+++ b/webrtc/modules/audio_coding/neteq/tools/neteq_quality_test.h
@@ -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_
diff --git a/webrtc/modules/audio_coding/neteq/tools/neteq_rtpplay.cc b/webrtc/modules/audio_coding/neteq/tools/neteq_rtpplay.cc
new file mode 100644
index 0000000..3c5f6b0
--- /dev/null
+++ b/webrtc/modules/audio_coding/neteq/tools/neteq_rtpplay.cc
@@ -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;
+ }
+}
diff --git a/webrtc/modules/audio_coding/neteq/tools/packet.cc b/webrtc/modules/audio_coding/neteq/tools/packet.cc
new file mode 100644
index 0000000..d8fb713
--- /dev/null
+++ b/webrtc/modules/audio_coding/neteq/tools/packet.cc
@@ -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
diff --git a/webrtc/modules/audio_coding/neteq/tools/packet.h b/webrtc/modules/audio_coding/neteq/tools/packet.h
new file mode 100644
index 0000000..eb8ce28
--- /dev/null
+++ b/webrtc/modules/audio_coding/neteq/tools/packet.h
@@ -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_
diff --git a/webrtc/modules/audio_coding/neteq/tools/packet_source.h b/webrtc/modules/audio_coding/neteq/tools/packet_source.h
new file mode 100644
index 0000000..c539b8e
--- /dev/null
+++ b/webrtc/modules/audio_coding/neteq/tools/packet_source.h
@@ -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_
diff --git a/webrtc/modules/audio_coding/neteq/tools/packet_unittest.cc b/webrtc/modules/audio_coding/neteq/tools/packet_unittest.cc
new file mode 100644
index 0000000..df844ee
--- /dev/null
+++ b/webrtc/modules/audio_coding/neteq/tools/packet_unittest.cc
@@ -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
diff --git a/webrtc/modules/audio_coding/neteq/tools/rtp_analyze.cc b/webrtc/modules/audio_coding/neteq/tools/rtp_analyze.cc
new file mode 100644
index 0000000..773cc2c
--- /dev/null
+++ b/webrtc/modules/audio_coding/neteq/tools/rtp_analyze.cc
@@ -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;
+}
diff --git a/webrtc/modules/audio_coding/neteq/tools/rtp_file_source.cc b/webrtc/modules/audio_coding/neteq/tools/rtp_file_source.cc
new file mode 100644
index 0000000..8278635
--- /dev/null
+++ b/webrtc/modules/audio_coding/neteq/tools/rtp_file_source.cc
@@ -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
diff --git a/webrtc/modules/audio_coding/neteq/tools/rtp_file_source.h b/webrtc/modules/audio_coding/neteq/tools/rtp_file_source.h
new file mode 100644
index 0000000..527018e
--- /dev/null
+++ b/webrtc/modules/audio_coding/neteq/tools/rtp_file_source.h
@@ -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_
diff --git a/webrtc/modules/audio_coding/neteq/tools/rtp_generator.cc b/webrtc/modules/audio_coding/neteq/tools/rtp_generator.cc
new file mode 100644
index 0000000..17ac209
--- /dev/null
+++ b/webrtc/modules/audio_coding/neteq/tools/rtp_generator.cc
@@ -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
diff --git a/webrtc/modules/audio_coding/neteq/tools/rtp_generator.h b/webrtc/modules/audio_coding/neteq/tools/rtp_generator.h
new file mode 100644
index 0000000..d3824c8
--- /dev/null
+++ b/webrtc/modules/audio_coding/neteq/tools/rtp_generator.h
@@ -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_