blob: 7c57c692a48ac25af2a86611ba2f435065962cb7 [file] [log] [blame]
// Copyright 2018 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "cobalt/media_capture/encoders/flac_audio_encoder.h"
#include <memory>
#include <string>
#include "base/bind.h"
#include "base/bind_helpers.h"
#include "base/strings/string_util.h"
namespace {
// See https://tools.ietf.org/html/rfc2586 for MIME type
const char kFlacMimeType[] = "audio/flac";
// This is a guesstimated value. This number should be > 1.
const double kEstimatedFlacCompressionRatio = 1.30;
} // namespace
namespace cobalt {
namespace media_capture {
namespace encoders {
bool FlacAudioEncoder::IsFlacMIMEType(const base::StringPiece& mime_type) {
base::StringPiece mime_type_container = mime_type;
size_t pos = mime_type.find_first_of(';');
if (pos != base::StringPiece::npos) {
mime_type_container = base::StringPiece(mime_type.begin(), pos);
}
const base::StringPiece flac_mime_type(kFlacMimeType);
auto match_iterator =
std::search(mime_type_container.begin(), mime_type_container.end(),
flac_mime_type.begin(), flac_mime_type.end(),
base::CaseInsensitiveCompareASCII<char>());
return match_iterator == mime_type_container.begin();
}
FlacAudioEncoder::FlacAudioEncoder() : thread_("FlacEncodingThd") {
thread_.Start();
}
FlacAudioEncoder::~FlacAudioEncoder() {
thread_.task_runner()->PostBlockingTask(
FROM_HERE,
base::Bind(&FlacAudioEncoder::DestroyEncoder, base::Unretained(this)));
}
void FlacAudioEncoder::Encode(const AudioBus& audio_bus,
base::TimeTicks reference_time) {
std::unique_ptr<AudioBus> audio_bus_copy(
new AudioBus(audio_bus.channels(), audio_bus.frames(),
audio_bus.sample_type(), audio_bus.storage_type()));
audio_bus_copy->Assign(audio_bus);
// base::Unretained usage is safe here, since we're posting to a thread that
// we own.
thread_.task_runner()->PostTask(
FROM_HERE, base::Bind(&FlacAudioEncoder::DoEncode, base::Unretained(this),
base::Passed(&audio_bus_copy), reference_time));
}
void FlacAudioEncoder::Finish(base::TimeTicks reference_time) {
thread_.task_runner()->PostBlockingTask(
FROM_HERE, base::Bind(&FlacAudioEncoder::DoFinish, base::Unretained(this),
reference_time));
}
std::string FlacAudioEncoder::GetMimeType() const {
return flac_encoder_->GetMimeType();
}
int64 FlacAudioEncoder::GetEstimatedOutputBitsPerSecond(
const media_stream::AudioParameters& params) const {
DCHECK_EQ(params.bits_per_sample(), 16);
DCHECK_EQ(params.channel_count(), 1);
return static_cast<int64>(params.GetBitsPerSecond() /
kEstimatedFlacCompressionRatio);
}
void FlacAudioEncoder::OnSetFormat(
const media_stream::AudioParameters& params) {
// base::Unretained usage is safe here, since we're posting to a thread that
// we own.
thread_.task_runner()->PostTask(FROM_HERE,
base::Bind(&FlacAudioEncoder::CreateEncoder,
base::Unretained(this), params));
}
void FlacAudioEncoder::CreateEncoder(
const media_stream::AudioParameters& params) {
flac_encoder_.reset(new speech::AudioEncoderFlac(params.sample_rate()));
}
void FlacAudioEncoder::DestroyEncoder() { flac_encoder_.reset(); }
void FlacAudioEncoder::DoEncode(std::unique_ptr<AudioBus> audio_bus,
base::TimeTicks reference_time) {
DCHECK(flac_encoder_);
flac_encoder_->Encode(audio_bus.get());
auto data = flac_encoder_->GetAndClearAvailableEncodedData();
if (data.empty()) {
return;
}
PushDataToAllListeners(reinterpret_cast<uint8*>(&data[0]), data.size(),
reference_time);
}
void FlacAudioEncoder::DoFinish(base::TimeTicks reference_time) {
if (!flac_encoder_) {
return;
}
flac_encoder_->Finish();
auto data = flac_encoder_->GetAndClearAvailableEncodedData();
if (data.empty()) {
PushDataToAllListeners(nullptr, 0, reference_time);
return;
}
PushDataToAllListeners(reinterpret_cast<uint8*>(&data[0]), data.size(),
reference_time);
}
} // namespace encoders
} // namespace media_capture
} // namespace cobalt