Make an AudioEncoder subclass for Opus
BUG=3926 R=henrik.lundin@webrtc.org, kjellander@webrtc.org Review URL: https://webrtc-codereview.appspot.com/23239004 git-svn-id: http://webrtc.googlecode.com/svn/trunk@7552 4adac7df-926f-26a2-2b94-8c16560cd09d
This commit is contained in:
@@ -608,6 +608,8 @@ config("opus_config") {
|
|||||||
|
|
||||||
source_set("webrtc_opus") {
|
source_set("webrtc_opus") {
|
||||||
sources = [
|
sources = [
|
||||||
|
"codecs/opus/audio_encoder_opus.cc",
|
||||||
|
"codecs/opus/interface/audio_encoder_opus.h",
|
||||||
"codecs/opus/interface/opus_interface.h",
|
"codecs/opus/interface/opus_interface.h",
|
||||||
"codecs/opus/opus_inst.h",
|
"codecs/opus/opus_inst.h",
|
||||||
"codecs/opus/opus_interface.c",
|
"codecs/opus/opus_interface.c",
|
||||||
|
|||||||
@@ -33,13 +33,13 @@ class AudioEncoder {
|
|||||||
// output.
|
// output.
|
||||||
bool Encode(uint32_t timestamp,
|
bool Encode(uint32_t timestamp,
|
||||||
const int16_t* audio,
|
const int16_t* audio,
|
||||||
size_t num_samples,
|
size_t num_samples_per_channel,
|
||||||
size_t max_encoded_bytes,
|
size_t max_encoded_bytes,
|
||||||
uint8_t* encoded,
|
uint8_t* encoded,
|
||||||
size_t* encoded_bytes,
|
size_t* encoded_bytes,
|
||||||
uint32_t* encoded_timestamp) {
|
uint32_t* encoded_timestamp) {
|
||||||
CHECK_EQ(num_samples,
|
CHECK_EQ(num_samples_per_channel,
|
||||||
static_cast<size_t>(sample_rate_hz() / 100 * num_channels()));
|
static_cast<size_t>(sample_rate_hz() / 100));
|
||||||
bool ret = Encode(timestamp,
|
bool ret = Encode(timestamp,
|
||||||
audio,
|
audio,
|
||||||
max_encoded_bytes,
|
max_encoded_bytes,
|
||||||
|
|||||||
104
webrtc/modules/audio_coding/codecs/opus/audio_encoder_opus.cc
Normal file
104
webrtc/modules/audio_coding/codecs/opus/audio_encoder_opus.cc
Normal file
@@ -0,0 +1,104 @@
|
|||||||
|
/*
|
||||||
|
* 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/codecs/opus/interface/audio_encoder_opus.h"
|
||||||
|
|
||||||
|
#include "webrtc/modules/audio_coding/codecs/opus/interface/opus_interface.h"
|
||||||
|
|
||||||
|
namespace webrtc {
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
// We always encode at 48 kHz.
|
||||||
|
const int kSampleRateHz = 48000;
|
||||||
|
|
||||||
|
int DivExact(int a, int b) {
|
||||||
|
CHECK_EQ(a % b, 0);
|
||||||
|
return a / b;
|
||||||
|
}
|
||||||
|
|
||||||
|
int16_t ClampInt16(size_t x) {
|
||||||
|
return static_cast<int16_t>(
|
||||||
|
std::min(x, static_cast<size_t>(std::numeric_limits<int16_t>::max())));
|
||||||
|
}
|
||||||
|
|
||||||
|
int16_t CastInt16(size_t x) {
|
||||||
|
DCHECK_LE(x, static_cast<size_t>(std::numeric_limits<int16_t>::max()));
|
||||||
|
return static_cast<int16_t>(x);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
AudioEncoderOpus::Config::Config() : frame_size_ms(20), num_channels(1) {}
|
||||||
|
|
||||||
|
bool AudioEncoderOpus::Config::IsOk() const {
|
||||||
|
if (frame_size_ms <= 0 || frame_size_ms % 10 != 0)
|
||||||
|
return false;
|
||||||
|
if (num_channels <= 0)
|
||||||
|
return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
AudioEncoderOpus::AudioEncoderOpus(const Config& config)
|
||||||
|
: num_10ms_frames_per_packet_(DivExact(config.frame_size_ms, 10)),
|
||||||
|
num_channels_(config.num_channels),
|
||||||
|
samples_per_10ms_frame_(DivExact(kSampleRateHz, 100) * num_channels_) {
|
||||||
|
CHECK(config.IsOk());
|
||||||
|
input_buffer_.reserve(num_10ms_frames_per_packet_ * samples_per_10ms_frame_);
|
||||||
|
CHECK_EQ(0, WebRtcOpus_EncoderCreate(&inst_, num_channels_));
|
||||||
|
}
|
||||||
|
|
||||||
|
AudioEncoderOpus::~AudioEncoderOpus() {
|
||||||
|
CHECK_EQ(0, WebRtcOpus_EncoderFree(inst_));
|
||||||
|
}
|
||||||
|
|
||||||
|
int AudioEncoderOpus::sample_rate_hz() const {
|
||||||
|
return kSampleRateHz;
|
||||||
|
}
|
||||||
|
|
||||||
|
int AudioEncoderOpus::num_channels() const {
|
||||||
|
return num_channels_;
|
||||||
|
}
|
||||||
|
|
||||||
|
int AudioEncoderOpus::num_10ms_frames_per_packet() const {
|
||||||
|
return num_10ms_frames_per_packet_;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AudioEncoderOpus::Encode(uint32_t timestamp,
|
||||||
|
const int16_t* audio,
|
||||||
|
size_t max_encoded_bytes,
|
||||||
|
uint8_t* encoded,
|
||||||
|
size_t* encoded_bytes,
|
||||||
|
uint32_t* encoded_timestamp) {
|
||||||
|
if (input_buffer_.empty())
|
||||||
|
first_timestamp_in_buffer_ = timestamp;
|
||||||
|
input_buffer_.insert(input_buffer_.end(), audio,
|
||||||
|
audio + samples_per_10ms_frame_);
|
||||||
|
if (input_buffer_.size() < (static_cast<size_t>(num_10ms_frames_per_packet_) *
|
||||||
|
samples_per_10ms_frame_)) {
|
||||||
|
*encoded_bytes = 0;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
CHECK_EQ(input_buffer_.size(),
|
||||||
|
static_cast<size_t>(num_10ms_frames_per_packet_) *
|
||||||
|
samples_per_10ms_frame_);
|
||||||
|
int16_t r = WebRtcOpus_Encode(
|
||||||
|
inst_, &input_buffer_[0],
|
||||||
|
DivExact(CastInt16(input_buffer_.size()), num_channels_),
|
||||||
|
ClampInt16(max_encoded_bytes), encoded);
|
||||||
|
input_buffer_.clear();
|
||||||
|
if (r < 0)
|
||||||
|
return false;
|
||||||
|
*encoded_bytes = r;
|
||||||
|
*encoded_timestamp = first_timestamp_in_buffer_;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace webrtc
|
||||||
@@ -0,0 +1,55 @@
|
|||||||
|
/*
|
||||||
|
* 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_CODECS_OPUS_INTERFACE_AUDIO_ENCODER_OPUS_H_
|
||||||
|
#define WEBRTC_MODULES_AUDIO_CODING_CODECS_OPUS_INTERFACE_AUDIO_ENCODER_OPUS_H_
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "webrtc/modules/audio_coding/codecs/opus/interface/opus_interface.h"
|
||||||
|
#include "webrtc/modules/audio_coding/codecs/audio_encoder.h"
|
||||||
|
|
||||||
|
namespace webrtc {
|
||||||
|
|
||||||
|
class AudioEncoderOpus : public AudioEncoder {
|
||||||
|
public:
|
||||||
|
struct Config {
|
||||||
|
Config();
|
||||||
|
bool IsOk() const;
|
||||||
|
int frame_size_ms;
|
||||||
|
int num_channels;
|
||||||
|
};
|
||||||
|
|
||||||
|
explicit AudioEncoderOpus(const Config& config);
|
||||||
|
virtual ~AudioEncoderOpus() OVERRIDE;
|
||||||
|
|
||||||
|
virtual int sample_rate_hz() const OVERRIDE;
|
||||||
|
virtual int num_channels() const OVERRIDE;
|
||||||
|
virtual int num_10ms_frames_per_packet() const OVERRIDE;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual bool Encode(uint32_t timestamp,
|
||||||
|
const int16_t* audio,
|
||||||
|
size_t max_encoded_bytes,
|
||||||
|
uint8_t* encoded,
|
||||||
|
size_t* encoded_bytes,
|
||||||
|
uint32_t* encoded_timestamp) OVERRIDE;
|
||||||
|
|
||||||
|
private:
|
||||||
|
const int num_10ms_frames_per_packet_;
|
||||||
|
const int num_channels_;
|
||||||
|
const int samples_per_10ms_frame_;
|
||||||
|
std::vector<int16_t> input_buffer_;
|
||||||
|
OpusEncInst* inst_;
|
||||||
|
uint32_t first_timestamp_in_buffer_;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace webrtc
|
||||||
|
#endif // WEBRTC_MODULES_AUDIO_CODING_CODECS_OPUS_INTERFACE_AUDIO_ENCODER_OPUS_H_
|
||||||
@@ -27,6 +27,8 @@
|
|||||||
'<(webrtc_root)',
|
'<(webrtc_root)',
|
||||||
],
|
],
|
||||||
'sources': [
|
'sources': [
|
||||||
|
'audio_encoder_opus.cc',
|
||||||
|
'interface/audio_encoder_opus.h',
|
||||||
'interface/opus_interface.h',
|
'interface/opus_interface.h',
|
||||||
'opus_inst.h',
|
'opus_inst.h',
|
||||||
'opus_interface.c',
|
'opus_interface.c',
|
||||||
|
|||||||
@@ -26,7 +26,7 @@
|
|||||||
#include "webrtc/modules/audio_coding/codecs/ilbc/interface/ilbc.h"
|
#include "webrtc/modules/audio_coding/codecs/ilbc/interface/ilbc.h"
|
||||||
#include "webrtc/modules/audio_coding/codecs/isac/fix/interface/isacfix.h"
|
#include "webrtc/modules/audio_coding/codecs/isac/fix/interface/isacfix.h"
|
||||||
#include "webrtc/modules/audio_coding/codecs/isac/main/interface/isac.h"
|
#include "webrtc/modules/audio_coding/codecs/isac/main/interface/isac.h"
|
||||||
#include "webrtc/modules/audio_coding/codecs/opus/interface/opus_interface.h"
|
#include "webrtc/modules/audio_coding/codecs/opus/interface/audio_encoder_opus.h"
|
||||||
#include "webrtc/modules/audio_coding/codecs/pcm16b/include/pcm16b.h"
|
#include "webrtc/modules/audio_coding/codecs/pcm16b/include/pcm16b.h"
|
||||||
#include "webrtc/modules/audio_coding/neteq/tools/resample_input_audio_file.h"
|
#include "webrtc/modules/audio_coding/neteq/tools/resample_input_audio_file.h"
|
||||||
#include "webrtc/system_wrappers/interface/data_log.h"
|
#include "webrtc/system_wrappers/interface/data_log.h"
|
||||||
@@ -140,17 +140,20 @@ class AudioDecoderTest : public ::testing::Test {
|
|||||||
size_t input_len_samples,
|
size_t input_len_samples,
|
||||||
uint8_t* output) {
|
uint8_t* output) {
|
||||||
size_t enc_len_bytes = 0;
|
size_t enc_len_bytes = 0;
|
||||||
|
scoped_ptr<int16_t[]> interleaved_input(
|
||||||
|
new int16_t[channels_ * input_len_samples]);
|
||||||
for (int i = 0; i < audio_encoder_->num_10ms_frames_per_packet(); ++i) {
|
for (int i = 0; i < audio_encoder_->num_10ms_frames_per_packet(); ++i) {
|
||||||
EXPECT_EQ(0u, enc_len_bytes);
|
EXPECT_EQ(0u, enc_len_bytes);
|
||||||
EXPECT_TRUE(audio_encoder_->Encode(0,
|
|
||||||
input,
|
// Duplicate the mono input signal to however many channels the test
|
||||||
audio_encoder_->sample_rate_hz() / 100,
|
// wants.
|
||||||
data_length_ * 2,
|
test::InputAudioFile::DuplicateInterleaved(
|
||||||
output,
|
input, input_len_samples, channels_, interleaved_input.get());
|
||||||
&enc_len_bytes,
|
|
||||||
&output_timestamp_));
|
EXPECT_TRUE(audio_encoder_->Encode(
|
||||||
|
0, interleaved_input.get(), audio_encoder_->sample_rate_hz() / 100,
|
||||||
|
data_length_ * 2, output, &enc_len_bytes, &output_timestamp_));
|
||||||
}
|
}
|
||||||
EXPECT_EQ(input_len_samples, enc_len_bytes);
|
|
||||||
return static_cast<int>(enc_len_bytes);
|
return static_cast<int>(enc_len_bytes);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -636,56 +639,22 @@ class AudioDecoderOpusTest : public AudioDecoderTest {
|
|||||||
frame_size_ = 480;
|
frame_size_ = 480;
|
||||||
data_length_ = 10 * frame_size_;
|
data_length_ = 10 * frame_size_;
|
||||||
decoder_ = new AudioDecoderOpus(kDecoderOpus);
|
decoder_ = new AudioDecoderOpus(kDecoderOpus);
|
||||||
assert(decoder_);
|
AudioEncoderOpus::Config config;
|
||||||
WebRtcOpus_EncoderCreate(&encoder_, 1);
|
config.frame_size_ms = static_cast<int>(frame_size_) / 48;
|
||||||
|
audio_encoder_.reset(new AudioEncoderOpus(config));
|
||||||
}
|
}
|
||||||
|
|
||||||
~AudioDecoderOpusTest() {
|
|
||||||
WebRtcOpus_EncoderFree(encoder_);
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual void InitEncoder() {}
|
|
||||||
|
|
||||||
virtual int EncodeFrame(const int16_t* input, size_t input_len_samples,
|
|
||||||
uint8_t* output) OVERRIDE {
|
|
||||||
int enc_len_bytes = WebRtcOpus_Encode(encoder_, const_cast<int16_t*>(input),
|
|
||||||
static_cast<int16_t>(input_len_samples),
|
|
||||||
static_cast<int16_t>(data_length_), output);
|
|
||||||
EXPECT_GT(enc_len_bytes, 0);
|
|
||||||
return enc_len_bytes;
|
|
||||||
}
|
|
||||||
|
|
||||||
OpusEncInst* encoder_;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class AudioDecoderOpusStereoTest : public AudioDecoderOpusTest {
|
class AudioDecoderOpusStereoTest : public AudioDecoderOpusTest {
|
||||||
protected:
|
protected:
|
||||||
AudioDecoderOpusStereoTest() : AudioDecoderOpusTest() {
|
AudioDecoderOpusStereoTest() : AudioDecoderOpusTest() {
|
||||||
channels_ = 2;
|
channels_ = 2;
|
||||||
WebRtcOpus_EncoderFree(encoder_);
|
|
||||||
delete decoder_;
|
delete decoder_;
|
||||||
decoder_ = new AudioDecoderOpus(kDecoderOpus_2ch);
|
decoder_ = new AudioDecoderOpus(kDecoderOpus_2ch);
|
||||||
assert(decoder_);
|
AudioEncoderOpus::Config config;
|
||||||
WebRtcOpus_EncoderCreate(&encoder_, 2);
|
config.frame_size_ms = static_cast<int>(frame_size_) / 48;
|
||||||
}
|
config.num_channels = 2;
|
||||||
|
audio_encoder_.reset(new AudioEncoderOpus(config));
|
||||||
virtual int EncodeFrame(const int16_t* input, size_t input_len_samples,
|
|
||||||
uint8_t* output) OVERRIDE {
|
|
||||||
// Create stereo by duplicating each sample in |input|.
|
|
||||||
const int input_stereo_samples = static_cast<int>(input_len_samples) * 2;
|
|
||||||
scoped_ptr<int16_t[]> input_stereo(new int16_t[input_stereo_samples]);
|
|
||||||
test::InputAudioFile::DuplicateInterleaved(
|
|
||||||
input, input_len_samples, 2, input_stereo.get());
|
|
||||||
|
|
||||||
// Note that the input length is given as samples per channel.
|
|
||||||
int enc_len_bytes =
|
|
||||||
WebRtcOpus_Encode(encoder_,
|
|
||||||
input_stereo.get(),
|
|
||||||
static_cast<int16_t>(input_len_samples),
|
|
||||||
static_cast<int16_t>(data_length_),
|
|
||||||
output);
|
|
||||||
EXPECT_GT(enc_len_bytes, 0);
|
|
||||||
return enc_len_bytes;
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user