blob: b7981cf4d62d29f33a9b35aaad19405c4d0ae310 [file] [log] [blame]
// Copyright 2013 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "content/renderer/media/audio_decoder.h"
#include <stdint.h>
#include <variant>
#include <vector>
#include "base/compiler_specific.h"
#include "base/containers/span_writer.h"
#include "base/logging.h"
#include "base/memory/ptr_util.h"
#include "base/strings/string_util.h"
#include "base/strings/to_string.h"
#include "base/time/time.h"
#include "media/base/audio_bus.h"
#include "media/base/limits.h"
#include "media/base/media_switches.h"
#include "media/filters/audio_file_reader.h"
#include "media/filters/in_memory_url_protocol.h"
#include "media/filters/legacy_audio_file_reader.h"
#include "media/media_buildflags.h"
#include "third_party/blink/public/platform/web_audio_bus.h"
using media::AudioBus;
using media::AudioFileReader;
using media::InMemoryUrlProtocol;
using std::vector;
using blink::WebAudioBus;
namespace content {
namespace {
#if BUILDFLAG(ENABLE_FFMPEG)
// AudioFileReader and LegacyAudioFileReader do not share an interface. Since
// the legacy implementation is planned to be removed soon, this is the only
// usage of these classes, and the interface is small, we instead have a
// temporary reader class.
//
// TODO(crbug.com/440616500): remove this class once the new AudioFileReader
// has sufficiently baked in Stable.
class Reader {
public:
static std::unique_ptr<Reader> Create(base::span<const char> data) {
auto out = base::WrapUnique(new Reader(data));
return out->Open() ? std::move(out) : nullptr;
}
Reader(const Reader&) = delete;
Reader(Reader&&) = delete;
Reader& operator=(const Reader&) = delete;
Reader& operator=(Reader&&) = delete;
size_t Read(std::vector<std::unique_ptr<AudioBus>>* decoded_audio_packets) {
if (std::holds_alternative<media::AudioFileReader>(*reader_)) {
return std::get<media::AudioFileReader>(*reader_).Read(
decoded_audio_packets);
}
return base::checked_cast<size_t>(
std::get<media::LegacyAudioFileReader>(*reader_).Read(
decoded_audio_packets));
}
size_t estimated_frames() const {
if (std::holds_alternative<media::AudioFileReader>(*reader_)) {
const auto& r = std::get<media::AudioFileReader>(*reader_);
return r.HasKnownDuration() ? r.GetNumberOfFrames() : 0;
}
const auto& r = std::get<media::LegacyAudioFileReader>(*reader_);
return r.HasKnownDuration() ? r.GetNumberOfFrames() : 0;
}
size_t channels() const {
if (std::holds_alternative<media::AudioFileReader>(*reader_)) {
return std::get<media::AudioFileReader>(*reader_).channels();
}
return std::get<media::LegacyAudioFileReader>(*reader_).channels();
}
double sample_rate() const {
if (std::holds_alternative<media::AudioFileReader>(*reader_)) {
return std::get<media::AudioFileReader>(*reader_).sample_rate();
}
return std::get<media::LegacyAudioFileReader>(*reader_).sample_rate();
}
private:
explicit Reader(base::span<const char> data)
: url_protocol_(base::as_byte_span(data), false) {
if (base::FeatureList::IsEnabled(media::kAudioDecoderAudioFileReader)) {
reader_ = std::make_unique<ReaderImpl>(
std::in_place_type<media::AudioFileReader>, &url_protocol_);
} else {
reader_ = std::make_unique<ReaderImpl>(
std::in_place_type<media::LegacyAudioFileReader>, &url_protocol_);
}
}
bool Open() {
if (std::holds_alternative<media::AudioFileReader>(*reader_)) {
return std::get<media::AudioFileReader>(*reader_).Open();
}
return std::get<media::LegacyAudioFileReader>(*reader_).Open();
}
InMemoryUrlProtocol url_protocol_;
using ReaderImpl =
std::variant<media::AudioFileReader, media::LegacyAudioFileReader>;
std::unique_ptr<ReaderImpl> reader_;
};
#endif
} // namespace
// Decode in-memory audio file data.
bool DecodeAudioFileData(blink::WebAudioBus* destination_bus,
base::span<const char> data) {
DCHECK(destination_bus);
if (!destination_bus) {
return false;
}
#if BUILDFLAG(ENABLE_FFMPEG)
auto reader = Reader::Create(data);
if (!reader) {
return false;
}
const size_t number_of_channels = reader->channels();
const double file_sample_rate = reader->sample_rate();
// Apply sanity checks to make sure crazy values aren't coming out of
// FFmpeg.
if (!number_of_channels ||
number_of_channels > static_cast<size_t>(media::limits::kMaxChannels) ||
file_sample_rate < media::limits::kMinSampleRate ||
file_sample_rate > media::limits::kMaxSampleRate) {
return false;
}
std::vector<std::unique_ptr<AudioBus>> decoded_audio_packets;
const size_t number_of_frames = reader->Read(&decoded_audio_packets);
if (number_of_frames == 0) {
return false;
}
// Allocate and configure the output audio channel data and then
// copy the decoded data to the destination.
destination_bus->Initialize(number_of_channels, number_of_frames,
file_sample_rate);
std::vector<base::SpanWriter<float>> dest_channels;
dest_channels.reserve(number_of_channels);
for (size_t ch = 0; ch < number_of_channels; ++ch) {
dest_channels.emplace_back(UNSAFE_TODO(base::span(
destination_bus->ChannelData(ch), destination_bus->length())));
}
// Append all `decoded_audio_packets`, channel per channel.
for (const auto& packet : decoded_audio_packets) {
for (size_t ch = 0; ch < number_of_channels; ++ch) {
dest_channels[ch].Write(packet->channel_span(ch));
}
}
DVLOG(1) << "Decoded file data (unknown duration)-"
<< " data: " << base::ToString(data) << " data size: " << data.size()
<< ", decoded duration: " << (number_of_frames / file_sample_rate)
<< ", number of frames: " << number_of_frames
<< ", estimated frames (if available): "
<< reader->estimated_frames()
<< ", sample rate: " << file_sample_rate
<< ", number of channels: " << number_of_channels;
return number_of_frames > 0;
#else
return false;
#endif // BUILDFLAG(ENABLE_FFMPEG)
}
} // namespace content