Commit 379cd04a authored by Ken MacKay's avatar Ken MacKay Committed by Commit Bot

[Chromecast] Add abstract resampler API and implementation

This allows internal code to resample without directly depending on
Chromium media code.

Bug: internal b/148569388
Change-Id: I08ecf20daf1be67727a18b7ce6829d28854ccffd
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2170424
Commit-Queue: Kenneth MacKay <kmackay@chromium.org>
Reviewed-by: default avatarYuchen Liu <yucliu@chromium.org>
Cr-Commit-Position: refs/heads/master@{#763519}
parent e62abe20
......@@ -7,6 +7,7 @@ import("//chromecast/chromecast.gni")
cast_source_set("api") {
sources = [
"cast_audio_decoder.h",
"cast_audio_resampler.h",
"cma_backend.h",
"cma_backend_factory.h",
"decoder_buffer_base.h",
......
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CHROMECAST_MEDIA_API_CAST_AUDIO_RESAMPLER_H_
#define CHROMECAST_MEDIA_API_CAST_AUDIO_RESAMPLER_H_
#include <memory>
#include <vector>
namespace chromecast {
namespace media {
// Audio resampler interface.
class CastAudioResampler {
public:
// Creates a CastAudioResampler instance.
static std::unique_ptr<CastAudioResampler> Create(int channel_count,
int input_sample_rate,
int output_sample_rate);
virtual ~CastAudioResampler() = default;
// Resamples |input|, which is assumed to be in planar float format, appending
// the resampled audio in planar float format into |output_channels|, which is
// an array of |channel_count| vectors (one per channel). Note that some
// audio from |input| may be stored internally and will not be output until
// the next call to Resample() or Flush().
virtual void Resample(const float* input,
int num_frames,
std::vector<float>* output_channels) = 0;
// Resamples any internally-buffered input audio, filling with silence as
// necessary. The resampled audio is appended in planar float format into
// |output_channels|, which is an array of |channel_count| vectors (one per
// channel).
virtual void Flush(std::vector<float>* output_channels) = 0;
// Returns the number of input frames that are buffered internally (ie, have
// not yet been resampled into output).
virtual int BufferedInputFrames() const = 0;
};
} // namespace media
} // namespace chromecast
#endif // CHROMECAST_MEDIA_API_CAST_AUDIO_RESAMPLER_H_
......@@ -67,6 +67,7 @@ cast_source_set("audio") {
"cast_audio_mixer.h",
"cast_audio_output_stream.cc",
"cast_audio_output_stream.h",
"cast_audio_resampler_impl.cc",
"cma_audio_output_stream.cc",
"cma_audio_output_stream.h",
]
......@@ -76,6 +77,7 @@ cast_source_set("audio") {
"//base",
"//chromecast/base",
"//chromecast/common/mojom",
"//chromecast/media/api",
"//chromecast/media/audio/capture_service:receiver",
"//chromecast/media/audio/mixer_service:common",
"//chromecast/media/audio/mixer_service:output_stream_connection",
......
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <algorithm>
#include <cmath>
#include <memory>
#include <vector>
#include "base/bind.h"
#include "base/logging.h"
#include "chromecast/media/api/cast_audio_resampler.h"
#include "media/base/audio_bus.h"
#include "media/base/multi_channel_resampler.h"
namespace chromecast {
namespace media {
namespace {
constexpr int kRequestFrames = 128;
class CastAudioResamplerImpl : public CastAudioResampler {
public:
CastAudioResamplerImpl(int channel_count,
int input_sample_rate,
int output_sample_rate)
: channel_count_(channel_count),
resampler_(channel_count,
static_cast<double>(input_sample_rate) / output_sample_rate,
kRequestFrames,
base::BindRepeating(&CastAudioResamplerImpl::ReadCallback,
base::Unretained(this))),
input_buffer_(::media::AudioBus::Create(channel_count, kRequestFrames)),
output_buffer_(
::media::AudioBus::Create(channel_count, kRequestFrames * 2)) {}
~CastAudioResamplerImpl() override = default;
CastAudioResamplerImpl(const CastAudioResamplerImpl&) = delete;
CastAudioResamplerImpl& operator=(const CastAudioResamplerImpl&) = delete;
private:
void ResampleOneChunk(std::vector<float>* output_channels) {
int output_frames = resampler_.ChunkSize();
if (output_frames > output_buffer_->frames()) {
output_buffer_ =
::media::AudioBus::Create(channel_count_, output_frames * 2);
}
resampler_.Resample(output_frames, output_buffer_.get());
for (int c = 0; c < channel_count_; ++c) {
const float* input_channel = output_buffer_->channel(c);
output_channels[c].insert(output_channels[c].end(), input_channel,
input_channel + output_frames);
}
}
void ReadCallback(int frame_delay, ::media::AudioBus* audio_bus) {
DCHECK_LE(buffered_frames_, audio_bus->frames());
input_buffer_->CopyPartialFramesTo(0, buffered_frames_, 0, audio_bus);
int frames_left = audio_bus->frames() - buffered_frames_;
int dest_offset = buffered_frames_;
buffered_frames_ = 0;
CopyCurrentInputTo(frames_left, audio_bus, dest_offset);
}
void CopyCurrentInputTo(int frames_to_copy,
::media::AudioBus* dest,
int dest_offset) {
DCHECK(current_input_);
DCHECK_LE(dest_offset + frames_to_copy, dest->frames());
DCHECK_LE(current_frame_offset_ + frames_to_copy, current_num_frames_);
for (int c = 0; c < channel_count_; ++c) {
const float* input_channel = current_input_ + c * current_num_frames_;
std::copy_n(input_channel + current_frame_offset_, frames_to_copy,
dest->channel(c) + dest_offset);
}
current_frame_offset_ += frames_to_copy;
}
// CastAudioResampler implementation:
void Resample(const float* input,
int num_frames,
std::vector<float>* output_channels) override {
current_input_ = input;
current_num_frames_ = num_frames;
current_frame_offset_ = 0;
while (buffered_frames_ + current_num_frames_ - current_frame_offset_ >=
kRequestFrames) {
ResampleOneChunk(output_channels);
}
int frames_left = current_num_frames_ - current_frame_offset_;
CopyCurrentInputTo(frames_left, input_buffer_.get(), buffered_frames_);
buffered_frames_ += frames_left;
current_input_ = nullptr;
current_num_frames_ = 0;
current_frame_offset_ = 0;
}
void Flush(std::vector<float>* output_channels) override {
// TODO(kmackay) May need some additional flushing to get out data stored in
// the SincResamplers.
if (buffered_frames_ == 0) {
return;
}
input_buffer_->ZeroFramesPartial(buffered_frames_,
kRequestFrames - buffered_frames_);
buffered_frames_ = kRequestFrames;
while (buffered_frames_) {
ResampleOneChunk(output_channels);
}
resampler_.Flush();
}
int BufferedInputFrames() const override {
return buffered_frames_ + std::round(resampler_.BufferedFrames());
}
const int channel_count_;
::media::MultiChannelResampler resampler_;
std::unique_ptr<::media::AudioBus> input_buffer_;
int buffered_frames_ = 0;
const float* current_input_ = nullptr;
int current_num_frames_ = 0;
int current_frame_offset_ = 0;
std::unique_ptr<::media::AudioBus> output_buffer_;
};
} // namespace
// static
std::unique_ptr<CastAudioResampler> CastAudioResampler::Create(
int channel_count,
int input_sample_rate,
int output_sample_rate) {
return std::make_unique<CastAudioResamplerImpl>(
channel_count, input_sample_rate, output_sample_rate);
}
} // namespace media
} // namespace chromecast
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment