blob: 27d84e1e0c46d6472294460463a87745ddde8725 [file] [log] [blame]
// Copyright 2017 The Cobalt Authors. 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 "starboard/shared/win32/audio_decoder.h"
#include "starboard/atomic.h"
#include "starboard/audio_sink.h"
#include "starboard/common/log.h"
#include "starboard/memory.h"
#include "starboard/shared/win32/media_common.h"
namespace starboard {
namespace shared {
namespace win32 {
class AudioDecoder::CallbackScheduler : private JobOwner {
public:
CallbackScheduler() : callback_signaled_(false) {}
void SetCallbackOnce(ConsumedCB cb) {
SB_DCHECK(cb);
::starboard::ScopedLock lock(mutex_);
if (!cb_) {
cb_ = cb;
}
}
void ScheduleCallbackIfNecessary() {
::starboard::ScopedLock lock(mutex_);
if (!cb_ || callback_signaled_) {
return;
}
callback_signaled_ = true;
JobOwner::Schedule(cb_);
}
void OnCallbackSignaled() {
::starboard::ScopedLock lock(mutex_);
callback_signaled_ = false;
}
ConsumedCB cb_;
::starboard::Mutex mutex_;
bool callback_signaled_;
};
AudioDecoder::AudioDecoder(const AudioStreamInfo& audio_stream_info,
SbDrmSystem drm_system)
: audio_stream_info_(audio_stream_info),
drm_system_(drm_system),
sample_type_((audio_stream_info.codec == kSbMediaAudioCodecAc3 ||
audio_stream_info.codec == kSbMediaAudioCodecEac3)
?
#if SB_HAS_QUIRK(SUPPORT_INT16_AUDIO_SAMPLES)
kSbMediaAudioSampleTypeInt16
#else
kSbMediaAudioSampleTypeInt16Deprecated
#endif // SB_HAS_QUIRK(SUPPORT_INT16_AUDIO_SAMPLES)
: kSbMediaAudioSampleTypeFloat32),
stream_ended_(false) {
SB_DCHECK(audio_stream_info.codec == kSbMediaAudioCodecAac ||
audio_stream_info.codec == kSbMediaAudioCodecAc3 ||
audio_stream_info.codec == kSbMediaAudioCodecEac3);
}
AudioDecoder::~AudioDecoder() {
SB_DCHECK(thread_checker_.CalledOnValidThread());
decoder_thread_.reset(nullptr);
decoder_impl_.reset(nullptr);
callback_scheduler_.reset(nullptr);
}
void AudioDecoder::Initialize(const OutputCB& output_cb,
const ErrorCB& error_cb) {
SB_DCHECK(thread_checker_.CalledOnValidThread());
SB_DCHECK(output_cb);
SB_DCHECK(!output_cb_);
output_cb_ = output_cb;
decoder_impl_ = AbstractWin32AudioDecoder::Create(
kSbMediaAudioFrameStorageTypeInterleaved, sample_type_,
audio_stream_info_, drm_system_);
decoder_thread_.reset(new AudioDecoderThread(decoder_impl_.get(), this));
callback_scheduler_.reset(new CallbackScheduler());
}
void AudioDecoder::Decode(const InputBuffers& input_buffers,
const ConsumedCB& consumed_cb) {
SB_DCHECK(thread_checker_.CalledOnValidThread());
SB_DCHECK(input_buffers.size() == 1);
SB_DCHECK(input_buffers[0]);
callback_scheduler_->SetCallbackOnce(consumed_cb);
callback_scheduler_->OnCallbackSignaled();
const bool can_take_more_data = decoder_thread_->QueueInput(input_buffers[0]);
if (can_take_more_data) {
callback_scheduler_->ScheduleCallbackIfNecessary();
}
if (stream_ended_) {
SB_LOG(ERROR) << "Decode() is called after WriteEndOfStream() is called.";
return;
}
}
void AudioDecoder::WriteEndOfStream() {
SB_DCHECK(thread_checker_.CalledOnValidThread());
::starboard::ScopedLock lock(mutex_);
stream_ended_ = true;
decoder_thread_->QueueEndOfStream();
}
scoped_refptr<AudioDecoder::DecodedAudio> AudioDecoder::Read(
int* samples_per_second) {
SB_DCHECK(thread_checker_.CalledOnValidThread());
DecodedAudioPtr data = decoded_data_.PopFront();
SB_DCHECK(data);
*samples_per_second = decoder_impl_->GetSamplesPerSecond();
return data;
}
void AudioDecoder::Reset() {
SB_DCHECK(thread_checker_.CalledOnValidThread());
decoder_thread_.reset(nullptr);
decoder_impl_->Reset();
decoded_data_.Clear();
stream_ended_ = false;
callback_scheduler_.reset(new CallbackScheduler());
CancelPendingJobs();
decoder_thread_.reset(new AudioDecoderThread(decoder_impl_.get(), this));
}
void AudioDecoder::OnAudioDecoded(DecodedAudioPtr data) {
decoded_data_.PushBack(data);
if (output_cb_) {
Schedule(output_cb_);
}
callback_scheduler_->ScheduleCallbackIfNecessary();
}
} // namespace win32
} // namespace shared
} // namespace starboard