summaryrefslogtreecommitdiffstats
path: root/media/base
diff options
context:
space:
mode:
Diffstat (limited to 'media/base')
-rw-r--r--media/base/audio_converter.cc3
-rw-r--r--media/base/audio_hardware_config.cc79
-rw-r--r--media/base/audio_hardware_config.h33
-rw-r--r--media/base/audio_hardware_config_unittest.cc56
-rw-r--r--media/base/channel_layout.cc29
-rw-r--r--media/base/channel_layout.h7
-rw-r--r--media/base/channel_mixer.cc70
-rw-r--r--media/base/channel_mixer.h7
-rw-r--r--media/base/channel_mixer_unittest.cc5
9 files changed, 221 insertions, 68 deletions
diff --git a/media/base/audio_converter.cc b/media/base/audio_converter.cc
index 1b66b03..5fda460 100644
--- a/media/base/audio_converter.cc
+++ b/media/base/audio_converter.cc
@@ -30,8 +30,7 @@ AudioConverter::AudioConverter(const AudioParameters& input_params,
<< " to " << output_params.channel_layout() << "; from "
<< input_params.channels() << " channels to "
<< output_params.channels() << " channels.";
- channel_mixer_.reset(new ChannelMixer(
- input_params.channel_layout(), output_params.channel_layout()));
+ channel_mixer_.reset(new ChannelMixer(input_params, output_params));
// Pare off data as early as we can for efficiency.
downmix_early_ = input_params.channels() > output_params.channels();
diff --git a/media/base/audio_hardware_config.cc b/media/base/audio_hardware_config.cc
index eaacc69..d72fce7 100644
--- a/media/base/audio_hardware_config.cc
+++ b/media/base/audio_hardware_config.cc
@@ -4,50 +4,77 @@
#include "media/base/audio_hardware_config.h"
+using base::AutoLock;
+using media::AudioParameters;
+
namespace media {
AudioHardwareConfig::AudioHardwareConfig(
- int output_buffer_size, int output_sample_rate,
- int input_sample_rate, ChannelLayout input_channel_layout)
- : output_buffer_size_(output_buffer_size),
- output_sample_rate_(output_sample_rate),
- input_sample_rate_(input_sample_rate),
- input_channel_layout_(input_channel_layout) {
+ const AudioParameters& input_params,
+ const AudioParameters& output_params)
+ : input_params_(input_params),
+ output_params_(output_params) {
}
AudioHardwareConfig::~AudioHardwareConfig() {}
-int AudioHardwareConfig::GetOutputBufferSize() {
- base::AutoLock auto_lock(config_lock_);
- return output_buffer_size_;
+int AudioHardwareConfig::GetOutputBufferSize() const {
+ AutoLock auto_lock(config_lock_);
+ return output_params_.frames_per_buffer();
+}
+
+int AudioHardwareConfig::GetOutputSampleRate() const {
+ AutoLock auto_lock(config_lock_);
+ return output_params_.sample_rate();
+}
+
+ChannelLayout AudioHardwareConfig::GetOutputChannelLayout() const {
+ AutoLock auto_lock(config_lock_);
+ return output_params_.channel_layout();
+}
+
+int AudioHardwareConfig::GetOutputChannels() const {
+ AutoLock auto_lock(config_lock_);
+ return output_params_.channels();
+}
+
+int AudioHardwareConfig::GetInputSampleRate() const {
+ AutoLock auto_lock(config_lock_);
+ return input_params_.sample_rate();
+}
+
+ChannelLayout AudioHardwareConfig::GetInputChannelLayout() const {
+ AutoLock auto_lock(config_lock_);
+ return input_params_.channel_layout();
}
-int AudioHardwareConfig::GetOutputSampleRate() {
- base::AutoLock auto_lock(config_lock_);
- return output_sample_rate_;
+int AudioHardwareConfig::GetInputChannels() const {
+ AutoLock auto_lock(config_lock_);
+ return input_params_.channels();
}
-int AudioHardwareConfig::GetInputSampleRate() {
- base::AutoLock auto_lock(config_lock_);
- return input_sample_rate_;
+media::AudioParameters
+AudioHardwareConfig::GetInputConfig() const {
+ AutoLock auto_lock(config_lock_);
+ return input_params_;
}
-ChannelLayout AudioHardwareConfig::GetInputChannelLayout() {
- base::AutoLock auto_lock(config_lock_);
- return input_channel_layout_;
+media::AudioParameters
+AudioHardwareConfig::GetOutputConfig() const {
+ AutoLock auto_lock(config_lock_);
+ return output_params_;
}
void AudioHardwareConfig::UpdateInputConfig(
- int sample_rate, media::ChannelLayout channel_layout) {
- base::AutoLock auto_lock(config_lock_);
- input_sample_rate_ = sample_rate;
- input_channel_layout_ = channel_layout;
+ const AudioParameters& input_params) {
+ AutoLock auto_lock(config_lock_);
+ input_params_ = input_params;
}
-void AudioHardwareConfig::UpdateOutputConfig(int buffer_size, int sample_rate) {
- base::AutoLock auto_lock(config_lock_);
- output_buffer_size_ = buffer_size;
- output_sample_rate_ = sample_rate;
+void AudioHardwareConfig::UpdateOutputConfig(
+ const AudioParameters& output_params) {
+ AutoLock auto_lock(config_lock_);
+ output_params_ = output_params;
}
} // namespace media
diff --git a/media/base/audio_hardware_config.h b/media/base/audio_hardware_config.h
index e61d9ba..d1621b98 100644
--- a/media/base/audio_hardware_config.h
+++ b/media/base/audio_hardware_config.h
@@ -7,6 +7,7 @@
#include "base/compiler_specific.h"
#include "base/synchronization/lock.h"
+#include "media/audio/audio_parameters.h"
#include "media/base/channel_layout.h"
#include "media/base/media_export.h"
@@ -15,32 +16,36 @@ namespace media {
// Provides thread safe access to the audio hardware configuration.
class MEDIA_EXPORT AudioHardwareConfig {
public:
- AudioHardwareConfig(int output_buffer_size, int output_sample_rate,
- int input_sample_rate,
- ChannelLayout input_channel_layout);
+ AudioHardwareConfig(const media::AudioParameters& input_params,
+ const media::AudioParameters& output_params);
virtual ~AudioHardwareConfig();
// Accessors for the currently cached hardware configuration. Safe to call
// from any thread.
- int GetOutputBufferSize();
- int GetOutputSampleRate();
- int GetInputSampleRate();
- ChannelLayout GetInputChannelLayout();
+ int GetOutputBufferSize() const;
+ int GetOutputSampleRate() const;
+ ChannelLayout GetOutputChannelLayout() const;
+ int GetOutputChannels() const;
+
+ int GetInputSampleRate() const;
+ ChannelLayout GetInputChannelLayout() const;
+ int GetInputChannels() const;
+
+ media::AudioParameters GetInputConfig() const;
+ media::AudioParameters GetOutputConfig() const;
// Allows callers to update the cached values for either input or output. The
// values are paired under the assumption that these values will only be set
// after an input or output device change respectively. Safe to call from
// any thread.
- void UpdateInputConfig(int sample_rate, media::ChannelLayout channel_layout);
- void UpdateOutputConfig(int buffer_size, int sample_rate);
+ void UpdateInputConfig(const media::AudioParameters& input_params);
+ void UpdateOutputConfig(const media::AudioParameters& output_params);
private:
// Cached values; access is protected by |config_lock_|.
- base::Lock config_lock_;
- int output_buffer_size_;
- int output_sample_rate_;
- int input_sample_rate_;
- ChannelLayout input_channel_layout_;
+ mutable base::Lock config_lock_;
+ media::AudioParameters input_params_;
+ media::AudioParameters output_params_;
DISALLOW_COPY_AND_ASSIGN(AudioHardwareConfig);
};
diff --git a/media/base/audio_hardware_config_unittest.cc b/media/base/audio_hardware_config_unittest.cc
index afa2e0d..4a742bf 100644
--- a/media/base/audio_hardware_config_unittest.cc
+++ b/media/base/audio_hardware_config_unittest.cc
@@ -3,19 +3,33 @@
// found in the LICENSE file.
#include "media/base/audio_hardware_config.h"
+#include "media/audio/audio_parameters.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace media {
static const int kOutputBufferSize = 2048;
static const int kOutputSampleRate = 48000;
+static const ChannelLayout kOutputChannelLayout = CHANNEL_LAYOUT_STEREO;
static const int kInputSampleRate = 44100;
static const ChannelLayout kInputChannelLayout = CHANNEL_LAYOUT_STEREO;
TEST(AudioHardwareConfig, Getters) {
- AudioHardwareConfig fake_config(
- kOutputBufferSize, kOutputSampleRate, kInputSampleRate,
- kInputChannelLayout);
+ AudioParameters input_params(
+ AudioParameters::AUDIO_PCM_LOW_LATENCY,
+ kInputChannelLayout,
+ kInputSampleRate,
+ 16,
+ kOutputBufferSize);
+
+ AudioParameters output_params(
+ AudioParameters::AUDIO_PCM_LOW_LATENCY,
+ kOutputChannelLayout,
+ kOutputSampleRate,
+ 16,
+ kOutputBufferSize);
+
+ AudioHardwareConfig fake_config(input_params, output_params);
EXPECT_EQ(kOutputBufferSize, fake_config.GetOutputBufferSize());
EXPECT_EQ(kOutputSampleRate, fake_config.GetOutputSampleRate());
@@ -24,16 +38,35 @@ TEST(AudioHardwareConfig, Getters) {
}
TEST(AudioHardwareConfig, Setters) {
- AudioHardwareConfig fake_config(
- kOutputBufferSize, kOutputSampleRate, kInputSampleRate,
- kInputChannelLayout);
+ AudioParameters input_params(
+ AudioParameters::AUDIO_PCM_LOW_LATENCY,
+ kInputChannelLayout,
+ kInputSampleRate,
+ 16,
+ kOutputBufferSize);
+
+ AudioParameters output_params(
+ AudioParameters::AUDIO_PCM_LOW_LATENCY,
+ kOutputChannelLayout,
+ kOutputSampleRate,
+ 16,
+ kOutputBufferSize);
+
+ AudioHardwareConfig fake_config(input_params, output_params);
// Verify output parameters.
const int kNewOutputBufferSize = kOutputBufferSize * 2;
const int kNewOutputSampleRate = kOutputSampleRate * 2;
EXPECT_NE(kNewOutputBufferSize, fake_config.GetOutputBufferSize());
EXPECT_NE(kNewOutputSampleRate, fake_config.GetOutputSampleRate());
- fake_config.UpdateOutputConfig(kNewOutputBufferSize, kNewOutputSampleRate);
+
+ AudioParameters new_output_params(
+ AudioParameters::AUDIO_PCM_LOW_LATENCY,
+ kOutputChannelLayout,
+ kNewOutputSampleRate,
+ 16,
+ kNewOutputBufferSize);
+ fake_config.UpdateOutputConfig(new_output_params);
EXPECT_EQ(kNewOutputBufferSize, fake_config.GetOutputBufferSize());
EXPECT_EQ(kNewOutputSampleRate, fake_config.GetOutputSampleRate());
@@ -42,7 +75,14 @@ TEST(AudioHardwareConfig, Setters) {
const ChannelLayout kNewInputChannelLayout = CHANNEL_LAYOUT_MONO;
EXPECT_NE(kNewInputSampleRate, fake_config.GetInputSampleRate());
EXPECT_NE(kNewInputChannelLayout, fake_config.GetInputChannelLayout());
- fake_config.UpdateInputConfig(kNewInputSampleRate, kNewInputChannelLayout);
+
+ AudioParameters new_input_params(
+ AudioParameters::AUDIO_PCM_LOW_LATENCY,
+ kNewInputChannelLayout,
+ kNewInputSampleRate,
+ 16,
+ kOutputBufferSize);
+ fake_config.UpdateInputConfig(new_input_params);
EXPECT_EQ(kNewInputSampleRate, fake_config.GetInputSampleRate());
EXPECT_EQ(kNewInputChannelLayout, fake_config.GetInputChannelLayout());
}
diff --git a/media/base/channel_layout.cc b/media/base/channel_layout.cc
index e622f91..927cd77 100644
--- a/media/base/channel_layout.cc
+++ b/media/base/channel_layout.cc
@@ -39,6 +39,7 @@ static const int kLayoutToChannels[] = {
7, // CHANNEL_LAYOUT_7_0_FRONT
8, // CHANNEL_LAYOUT_7_1_WIDE_BACK
8, // CHANNEL_LAYOUT_OCTAGONAL
+ 0, // CHANNEL_LAYOUT_DISCRETE
};
// The channel orderings for each layout as specified by FFmpeg. Each value
@@ -141,6 +142,9 @@ static const int kChannelOrderings[CHANNEL_LAYOUT_MAX][CHANNELS_MAX] = {
// CHANNEL_LAYOUT_OCTAGONAL
{ 0 , 1 , 2 , -1 , 5 , 6 , -1 , -1 , 7 , 3 , 4 },
+ // CHANNEL_LAYOUT_DISCRETE
+ { -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 },
+
// FL | FR | FC | LFE | BL | BR | FLofC | FRofC | BC | SL | SR
};
@@ -149,6 +153,31 @@ int ChannelLayoutToChannelCount(ChannelLayout layout) {
return kLayoutToChannels[layout];
}
+// Converts a channel count into a channel layout.
+ChannelLayout GuessChannelLayout(int channels) {
+ switch (channels) {
+ case 1:
+ return CHANNEL_LAYOUT_MONO;
+ case 2:
+ return CHANNEL_LAYOUT_STEREO;
+ case 3:
+ return CHANNEL_LAYOUT_SURROUND;
+ case 4:
+ return CHANNEL_LAYOUT_QUAD;
+ case 5:
+ return CHANNEL_LAYOUT_5_0;
+ case 6:
+ return CHANNEL_LAYOUT_5_1;
+ case 7:
+ return CHANNEL_LAYOUT_6_1;
+ case 8:
+ return CHANNEL_LAYOUT_7_1;
+ default:
+ DVLOG(1) << "Unsupported channel count: " << channels;
+ }
+ return CHANNEL_LAYOUT_UNSUPPORTED;
+}
+
int ChannelOrder(ChannelLayout layout, Channels channel) {
DCHECK_LT(static_cast<size_t>(layout), arraysize(kChannelOrderings));
DCHECK_LT(static_cast<size_t>(channel), arraysize(kChannelOrderings[0]));
diff --git a/media/base/channel_layout.h b/media/base/channel_layout.h
index 8153ca6..4c96ca5 100644
--- a/media/base/channel_layout.h
+++ b/media/base/channel_layout.h
@@ -96,6 +96,9 @@ enum ChannelLayout {
// Front L, Front R, Front C, Side L, Side R, Rear C, Back L, Back R.
CHANNEL_LAYOUT_OCTAGONAL = 28,
+ // Channels are not explicitly mapped to speakers.
+ CHANNEL_LAYOUT_DISCRETE = 29,
+
// Total number of layouts.
CHANNEL_LAYOUT_MAX // Must always be last!
};
@@ -123,6 +126,10 @@ MEDIA_EXPORT int ChannelOrder(ChannelLayout layout, Channels channel);
// Returns the number of channels in a given ChannelLayout.
MEDIA_EXPORT int ChannelLayoutToChannelCount(ChannelLayout layout);
+// Given the number of channels, return the best layout,
+// or return CHANNEL_LAYOUT_UNSUPPORTED if there is no good match.
+MEDIA_EXPORT ChannelLayout GuessChannelLayout(int channels);
+
} // namespace media
#endif // MEDIA_BASE_CHANNEL_LAYOUT_H_
diff --git a/media/base/channel_mixer.cc b/media/base/channel_mixer.cc
index fa4cbb6..420ecda 100644
--- a/media/base/channel_mixer.cc
+++ b/media/base/channel_mixer.cc
@@ -11,6 +11,7 @@
#include <cmath>
#include "base/logging.h"
+#include "media/audio/audio_parameters.h"
#include "media/base/audio_bus.h"
#include "media/base/vector_math.h"
@@ -20,14 +21,11 @@ namespace media {
// value for stereo -> mono and mono -> stereo mixes.
static const float kEqualPowerScale = static_cast<float>(M_SQRT1_2);
-static int ValidateLayout(ChannelLayout layout) {
+static void ValidateLayout(ChannelLayout layout) {
CHECK_NE(layout, CHANNEL_LAYOUT_NONE);
CHECK_NE(layout, CHANNEL_LAYOUT_MAX);
-
- // TODO(dalecurtis, crogers): We will eventually handle unsupported layouts by
- // simply copying the input channels to the output channels, similar to if the
- // user requests identical input and output layouts today.
CHECK_NE(layout, CHANNEL_LAYOUT_UNSUPPORTED);
+ CHECK_NE(layout, CHANNEL_LAYOUT_DISCRETE);
// Verify there's at least one channel. Should always be true here by virtue
// of not being one of the invalid layouts, but lets double check to be sure.
@@ -52,24 +50,60 @@ static int ValidateLayout(ChannelLayout layout) {
DCHECK_EQ(layout, CHANNEL_LAYOUT_MONO);
}
- return channel_count;
+ return;
+}
+
+ChannelMixer::ChannelMixer(ChannelLayout input_layout,
+ ChannelLayout output_layout) {
+ Initialize(input_layout,
+ ChannelLayoutToChannelCount(input_layout),
+ output_layout,
+ ChannelLayoutToChannelCount(output_layout));
}
-ChannelMixer::ChannelMixer(ChannelLayout input, ChannelLayout output)
- : input_layout_(input),
- output_layout_(output),
- remapping_(false) {
+ChannelMixer::ChannelMixer(
+ const AudioParameters& input, const AudioParameters& output) {
+ Initialize(input.channel_layout(),
+ input.channels(),
+ output.channel_layout(),
+ output.channels());
+}
+
+void ChannelMixer::Initialize(
+ ChannelLayout input_layout, int input_channels,
+ ChannelLayout output_layout, int output_channels) {
+ input_layout_ = input_layout;
+ output_layout_ = output_layout;
+ remapping_ = false;
+
// Stereo down mix should never be the output layout.
CHECK_NE(output_layout_, CHANNEL_LAYOUT_STEREO_DOWNMIX);
- int input_channels = ValidateLayout(input_layout_);
- int output_channels = ValidateLayout(output_layout_);
+ if (input_layout_ != CHANNEL_LAYOUT_DISCRETE)
+ ValidateLayout(input_layout_);
+ if (output_layout_ != CHANNEL_LAYOUT_DISCRETE)
+ ValidateLayout(output_layout_);
// Size out the initial matrix.
matrix_.reserve(output_channels);
for (int output_ch = 0; output_ch < output_channels; ++output_ch)
matrix_.push_back(std::vector<float>(input_channels, 0));
+ // First check for discrete case.
+ if (input_layout_ == CHANNEL_LAYOUT_DISCRETE ||
+ output_layout_ == CHANNEL_LAYOUT_DISCRETE) {
+ // If the number of input channels is more than output channels, then
+ // copy as many as we can then drop the remaining input channels.
+ // If the number of input channels is less than output channels, then
+ // copy them all, then zero out the remaining output channels.
+ int passthrough_channels = std::min(input_channels, output_channels);
+ for (int i = 0; i < passthrough_channels; ++i)
+ matrix_[i][i] = 1;
+
+ remapping_ = true;
+ return;
+ }
+
// Route matching channels and figure out which ones aren't accounted for.
for (Channels ch = LEFT; ch < CHANNELS_MAX;
ch = static_cast<Channels>(ch + 1)) {
@@ -102,7 +136,8 @@ ChannelMixer::ChannelMixer(ChannelLayout input, ChannelLayout output)
// When down mixing to mono from stereo, we need to be careful of full scale
// stereo mixes. Scaling by 1 / sqrt(2) here will likely lead to clipping
// so we use 1 / 2 instead.
- float scale = (output == CHANNEL_LAYOUT_MONO && input_channels == 2) ?
+ float scale =
+ (output_layout_ == CHANNEL_LAYOUT_MONO && input_channels == 2) ?
0.5 : kEqualPowerScale;
Mix(LEFT, CENTER, scale);
Mix(RIGHT, CENTER, scale);
@@ -111,7 +146,8 @@ ChannelMixer::ChannelMixer(ChannelLayout input, ChannelLayout output)
// Mix center into front LR.
if (IsUnaccounted(CENTER)) {
// When up mixing from mono, just do a copy to front LR.
- float scale = (input == CHANNEL_LAYOUT_MONO) ? 1 : kEqualPowerScale;
+ float scale =
+ (input_layout_ == CHANNEL_LAYOUT_MONO) ? 1 : kEqualPowerScale;
MixWithoutAccounting(CENTER, LEFT, scale);
Mix(CENTER, RIGHT, scale);
}
@@ -128,7 +164,7 @@ ChannelMixer::ChannelMixer(ChannelLayout input, ChannelLayout output)
// Mix back LR into back center.
Mix(BACK_LEFT, BACK_CENTER, kEqualPowerScale);
Mix(BACK_RIGHT, BACK_CENTER, kEqualPowerScale);
- } else if (output > CHANNEL_LAYOUT_MONO) {
+ } else if (output_layout_ > CHANNEL_LAYOUT_MONO) {
// Mix back LR into front LR.
Mix(BACK_LEFT, LEFT, kEqualPowerScale);
Mix(BACK_RIGHT, RIGHT, kEqualPowerScale);
@@ -151,7 +187,7 @@ ChannelMixer::ChannelMixer(ChannelLayout input, ChannelLayout output)
// Mix side LR into back center.
Mix(SIDE_LEFT, BACK_CENTER, kEqualPowerScale);
Mix(SIDE_RIGHT, BACK_CENTER, kEqualPowerScale);
- } else if (output > CHANNEL_LAYOUT_MONO) {
+ } else if (output_layout_ > CHANNEL_LAYOUT_MONO) {
// Mix side LR into front LR.
Mix(SIDE_LEFT, LEFT, kEqualPowerScale);
Mix(SIDE_RIGHT, RIGHT, kEqualPowerScale);
@@ -172,7 +208,7 @@ ChannelMixer::ChannelMixer(ChannelLayout input, ChannelLayout output)
// Mix back center into side LR.
MixWithoutAccounting(BACK_CENTER, SIDE_LEFT, kEqualPowerScale);
Mix(BACK_CENTER, SIDE_RIGHT, kEqualPowerScale);
- } else if (output > CHANNEL_LAYOUT_MONO) {
+ } else if (output_layout_ > CHANNEL_LAYOUT_MONO) {
// Mix back center into front LR.
// TODO(dalecurtis): Not sure about these values?
MixWithoutAccounting(BACK_CENTER, LEFT, kEqualPowerScale);
diff --git a/media/base/channel_mixer.h b/media/base/channel_mixer.h
index 0fdcc18..c88669d 100644
--- a/media/base/channel_mixer.h
+++ b/media/base/channel_mixer.h
@@ -14,6 +14,7 @@
namespace media {
class AudioBus;
+class AudioParameters;
// ChannelMixer is for converting audio between channel layouts. The conversion
// matrix is built upon construction and used during each Transform() call. The
@@ -23,9 +24,13 @@ class AudioBus;
// input channels as defined in the matrix.
class MEDIA_EXPORT ChannelMixer {
public:
- ChannelMixer(ChannelLayout input, ChannelLayout output);
+ ChannelMixer(ChannelLayout input_layout, ChannelLayout output_layout);
+ ChannelMixer(const AudioParameters& input, const AudioParameters& output);
~ChannelMixer();
+ void Initialize(ChannelLayout input_layout, int input_channels,
+ ChannelLayout output_layout, int output_channels);
+
// Transforms all channels from |input| into |output| channels.
void Transform(const AudioBus* input, AudioBus* output);
diff --git a/media/base/channel_mixer_unittest.cc b/media/base/channel_mixer_unittest.cc
index a71f86b..3e44409 100644
--- a/media/base/channel_mixer_unittest.cc
+++ b/media/base/channel_mixer_unittest.cc
@@ -25,6 +25,11 @@ TEST(ChannelMixerTest, ConstructAllPossibleLayouts) {
for (ChannelLayout output_layout = CHANNEL_LAYOUT_MONO;
output_layout < CHANNEL_LAYOUT_STEREO_DOWNMIX;
output_layout = static_cast<ChannelLayout>(output_layout + 1)) {
+ // DISCRETE can't be tested here based on the current approach.
+ if (input_layout == CHANNEL_LAYOUT_DISCRETE ||
+ output_layout == CHANNEL_LAYOUT_DISCRETE)
+ continue;
+
SCOPED_TRACE(base::StringPrintf(
"Input Layout: %d, Output Layout: %d", input_layout, output_layout));
ChannelMixer mixer(input_layout, output_layout);