[go: nahoru, domu]

blob: a4f05600ef2312bdb72263f13c75ef87b46c59e9 [file] [log] [blame]
// Copyright 2015 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chromecast/media/api/cast_audio_decoder.h"
#include <algorithm>
#include <cstdint>
#include <limits>
#include <utility>
#include <vector>
#include "base/containers/queue.h"
#include "base/functional/bind.h"
#include "base/functional/callback_helpers.h"
#include "base/location.h"
#include "base/logging.h"
#include "base/task/single_thread_task_runner.h"
#include "chromecast/media/api/decoder_buffer_base.h"
#include "chromecast/media/cma/base/decoder_buffer_adapter.h"
#include "chromecast/media/cma/base/decoder_config_adapter.h"
#include "chromecast/media/cma/decoder/external_audio_decoder_wrapper.h"
#include "chromecast/media/common/base/decoder_config_logging.h"
#include "media/base/audio_buffer.h"
#include "media/base/audio_bus.h"
#include "media/base/cdm_context.h"
#include "media/base/channel_layout.h"
#include "media/base/decoder_buffer.h"
#include "media/base/media_util.h"
#include "media/base/sample_format.h"
#include "media/base/status.h"
#include "media/filters/ffmpeg_audio_decoder.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
namespace chromecast {
namespace media {
namespace {
// This class wraps the underlying data of a DecoderBufferBase.
// This class does not take the ownership of the data. The DecoderBufferBase
// is still responsible for deleting the data. This class holds a reference
// to the DecoderBufferBase so that it lives longer than this DecoderBuffer.
class DecoderBuffer : public ::media::DecoderBuffer {
public:
DecoderBuffer(scoped_refptr<DecoderBufferBase> buffer)
: ::media::DecoderBuffer(
std::unique_ptr<uint8_t[]>(const_cast<uint8_t*>(buffer->data())),
buffer->data_size()),
buffer_(std::move(buffer)) {
set_timestamp(::base::Microseconds(buffer_->timestamp()));
}
private:
~DecoderBuffer() override {
// Releases the data to prevent it from being deleted.
DCHECK_EQ(data_.get(), buffer_->data());
data_.release();
}
scoped_refptr<DecoderBufferBase> buffer_;
};
class CastAudioDecoderImpl : public CastAudioDecoder {
public:
CastAudioDecoderImpl(scoped_refptr<base::SingleThreadTaskRunner> task_runner,
const media::AudioConfig& config,
OutputFormat output_format)
: task_runner_(std::move(task_runner)),
output_format_(output_format),
weak_factory_(this) {
weak_this_ = weak_factory_.GetWeakPtr();
DCHECK(task_runner_);
input_config_ = config;
input_config_.encryption_scheme = EncryptionScheme::kUnencrypted;
output_config_ = input_config_;
output_config_.codec = kCodecPCM;
output_config_.sample_format =
(output_format_ == kOutputSigned16 ? kSampleFormatS16
: kSampleFormatPlanarF32);
decoder_ = std::make_unique<::media::FFmpegAudioDecoder>(task_runner_,
&media_log_);
decoder_->Initialize(
media::DecoderConfigAdapter::ToMediaAudioDecoderConfig(input_config_),
nullptr,
base::BindRepeating(&CastAudioDecoderImpl::OnInitialized, weak_this_),
base::BindRepeating(&CastAudioDecoderImpl::OnDecoderOutput, weak_this_),
base::NullCallback());
// Unfortunately there is no result from decoder_->Initialize() until later
// (the pipeline status callback is posted to the task runner).
}
CastAudioDecoderImpl(const CastAudioDecoderImpl&) = delete;
CastAudioDecoderImpl& operator=(const CastAudioDecoderImpl&) = delete;
// CastAudioDecoder implementation:
const AudioConfig& GetOutputConfig() const override { return output_config_; }
void Decode(scoped_refptr<media::DecoderBufferBase> data,
DecodeCallback decode_callback) override {
DCHECK(decode_callback);
DCHECK(task_runner_->RunsTasksInCurrentSequence());
if (data->decrypt_context() != nullptr || error_) {
if (data->decrypt_context() != nullptr) {
LOG(ERROR) << "Audio decoder doesn't support encrypted stream";
}
// Post the task to ensure that |decode_callback| is not called from
// within a call to Decode().
task_runner_->PostTask(
FROM_HERE, base::BindOnce(&CastAudioDecoderImpl::CallDecodeCallback,
weak_this_, std::move(decode_callback),
kDecodeError, std::move(data)));
} else if (!initialized_ || decode_pending_) {
decode_queue_.push(
std::make_pair(std::move(data), std::move(decode_callback)));
} else {
DecodeNow(std::move(data), std::move(decode_callback));
}
}
private:
typedef std::pair<scoped_refptr<media::DecoderBufferBase>, DecodeCallback>
DecodeBufferCallbackPair;
void CallDecodeCallback(DecodeCallback decode_callback,
Status status,
scoped_refptr<media::DecoderBufferBase> data) {
std::move(decode_callback).Run(status, output_config_, std::move(data));
}
void DecodeNow(scoped_refptr<media::DecoderBufferBase> data,
DecodeCallback decode_callback) {
if (data->end_of_stream()) {
// Post the task to ensure that |decode_callback| is not called from
// within a call to Decode().
task_runner_->PostTask(
FROM_HERE, base::BindOnce(&CastAudioDecoderImpl::CallDecodeCallback,
weak_this_, std::move(decode_callback),
kDecodeOk, std::move(data)));
return;
}
// FFmpegAudioDecoder requires a timestamp to be set.
base::TimeDelta timestamp = base::Microseconds(data->timestamp());
if (timestamp == ::media::kNoTimestamp)
data->set_timestamp(base::TimeDelta());
decode_pending_ = true;
pending_decode_callback_ = std::move(decode_callback);
decoder_->Decode(base::WrapRefCounted(new DecoderBuffer(std::move(data))),
base::BindRepeating(&CastAudioDecoderImpl::OnDecodeStatus,
weak_this_, timestamp));
}
void OnInitialized(::media::DecoderStatus status) {
DCHECK(!initialized_);
initialized_ = true;
if (status.is_ok()) {
if (!decode_queue_.empty()) {
auto& d = decode_queue_.front();
DecodeNow(std::move(d.first), std::move(d.second));
decode_queue_.pop();
}
return;
}
error_ = true;
LOG(ERROR) << "Failed to initialize audio decoder";
LOG(INFO) << "Config:";
LOG(INFO) << "\tCodec: " << input_config_.codec;
LOG(INFO) << "\tSample format: " << input_config_.sample_format;
LOG(INFO) << "\tChannels: " << input_config_.channel_number;
LOG(INFO) << "\tSample rate: " << input_config_.samples_per_second;
while (!decode_queue_.empty()) {
auto& d = decode_queue_.front();
std::move(d.second).Run(kDecodeError, output_config_, std::move(d.first));
decode_queue_.pop();
}
}
void OnDecodeStatus(base::TimeDelta buffer_timestamp,
::media::DecoderStatus status) {
DCHECK(pending_decode_callback_);
Status result_status = kDecodeOk;
scoped_refptr<media::DecoderBufferBase> decoded;
if (status.is_ok() && !decoded_chunks_.empty()) {
decoded = ConvertDecoded();
} else {
if (!status.is_ok())
result_status = kDecodeError;
decoded = base::MakeRefCounted<media::DecoderBufferAdapter>(
output_config_.id, base::MakeRefCounted<::media::DecoderBuffer>(0));
}
decoded_chunks_.clear();
decoded->set_timestamp(buffer_timestamp);
base::WeakPtr<CastAudioDecoderImpl> self = weak_factory_.GetWeakPtr();
std::move(pending_decode_callback_)
.Run(result_status, output_config_, std::move(decoded));
if (!self)
return; // Return immediately if the decode callback deleted this.
// Do not reset decode_pending_ to false until after the callback has
// finished running because the callback may call Decode().
decode_pending_ = false;
if (decode_queue_.empty())
return;
auto& d = decode_queue_.front();
// Calling DecodeNow() here does not result in a loop, because
// OnDecodeStatus() is always called asynchronously (guaranteed by the
// AudioDecoder interface).
DecodeNow(std::move(d.first), std::move(d.second));
decode_queue_.pop();
}
void OnDecoderOutput(scoped_refptr<::media::AudioBuffer> decoded) {
if (decoded->sample_rate() != output_config_.samples_per_second) {
LOG(WARNING) << "sample_rate changed to " << decoded->sample_rate()
<< " from " << output_config_.samples_per_second;
output_config_.samples_per_second = decoded->sample_rate();
}
ChannelLayout decoded_channel_layout =
DecoderConfigAdapter::ToChannelLayout(decoded->channel_layout());
if (decoded->channel_count() != output_config_.channel_number ||
decoded_channel_layout != output_config_.channel_layout) {
LOG(WARNING) << "channel_count changed to " << decoded->channel_count()
<< " from " << output_config_.channel_number
<< ", channel_layout changed to "
<< static_cast<int>(decoded_channel_layout) << " from "
<< static_cast<int>(output_config_.channel_layout);
output_config_.channel_number = decoded->channel_count();
output_config_.channel_layout =
DecoderConfigAdapter::ToChannelLayout(decoded->channel_layout());
decoded_bus_.reset();
}
decoded_chunks_.push_back(std::move(decoded));
}
scoped_refptr<media::DecoderBufferBase> ConvertDecoded() {
DCHECK(!decoded_chunks_.empty());
int num_frames = 0;
for (auto& chunk : decoded_chunks_)
num_frames += chunk->frame_count();
// Copy decoded data into an AudioBus for conversion.
if (!decoded_bus_ || decoded_bus_->frames() < num_frames) {
decoded_bus_ = ::media::AudioBus::Create(output_config_.channel_number,
num_frames * 2);
}
int bus_frame_offset = 0;
for (auto& chunk : decoded_chunks_) {
chunk->ReadFrames(chunk->frame_count(), 0, bus_frame_offset,
decoded_bus_.get());
bus_frame_offset += chunk->frame_count();
}
return FinishConversion(decoded_bus_.get(), bus_frame_offset);
}
scoped_refptr<media::DecoderBufferBase> FinishConversion(
::media::AudioBus* bus,
int num_frames) {
int size =
num_frames * bus->channels() * OutputFormatSizeInBytes(output_format_);
auto result = base::MakeRefCounted<::media::DecoderBuffer>(size);
if (output_format_ == kOutputSigned16) {
bus->ToInterleaved<::media::SignedInt16SampleTypeTraits>(
num_frames, reinterpret_cast<int16_t*>(result->writable_data()));
} else if (output_format_ == kOutputPlanarFloat) {
// Data in an AudioBus is already in planar float format; just copy each
// channel into the result buffer in order.
float* ptr = reinterpret_cast<float*>(result->writable_data());
for (int c = 0; c < bus->channels(); ++c) {
std::copy_n(bus->channel(c), num_frames, ptr);
ptr += num_frames;
}
} else {
NOTREACHED();
}
result->set_duration(
base::Microseconds(num_frames * base::Time::kMicrosecondsPerSecond /
output_config_.samples_per_second));
return base::MakeRefCounted<media::DecoderBufferAdapter>(output_config_.id,
result);
}
::media::NullMediaLog media_log_;
const scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
OutputFormat output_format_;
bool initialized_ = false;
bool error_ = false;
media::AudioConfig input_config_;
media::AudioConfig output_config_;
std::unique_ptr<::media::AudioDecoder> decoder_;
base::queue<DecodeBufferCallbackPair> decode_queue_;
bool decode_pending_ = false;
DecodeCallback pending_decode_callback_;
std::vector<scoped_refptr<::media::AudioBuffer>> decoded_chunks_;
std::unique_ptr<::media::AudioBus> decoded_bus_;
base::WeakPtr<CastAudioDecoderImpl> weak_this_;
base::WeakPtrFactory<CastAudioDecoderImpl> weak_factory_;
};
} // namespace
// static
std::unique_ptr<CastAudioDecoder> CastAudioDecoder::Create(
scoped_refptr<base::SingleThreadTaskRunner> task_runner,
const media::AudioConfig& config,
OutputFormat output_format) {
if (ExternalAudioDecoderWrapper::IsSupportedConfig(config)) {
auto external_decoder = std::make_unique<ExternalAudioDecoderWrapper>(
std::move(task_runner), config, output_format);
if (!external_decoder->initialized()) {
return nullptr;
}
return external_decoder;
}
return std::make_unique<CastAudioDecoderImpl>(std::move(task_runner), config,
output_format);
}
// static
int CastAudioDecoder::OutputFormatSizeInBytes(
CastAudioDecoder::OutputFormat format) {
switch (format) {
case CastAudioDecoder::OutputFormat::kOutputSigned16:
return 2;
case CastAudioDecoder::OutputFormat::kOutputPlanarFloat:
return 4;
}
NOTREACHED();
return 1;
}
} // namespace media
} // namespace chromecast