blob: 8ca4625875d0cdbd74f77600e3876ae35a4417d6 [file] [log] [blame]
// Copyright 2018 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/opus/opus_audio_decoder.h"
#include "starboard/log.h"
#include "starboard/memory.h"
#include "starboard/shared/starboard/media/media_util.h"
#include "starboard/string.h"
namespace starboard {
namespace shared {
namespace opus {
namespace {
const int kMaxOpusFramesPerAU = 9600;
} // namespace
OpusAudioDecoder::OpusAudioDecoder(const SbMediaAudioHeader& audio_header)
: audio_header_(audio_header) {
#if SB_HAS_QUIRK(SUPPORT_INT16_AUDIO_SAMPLES)
working_buffer_.resize(kMaxOpusFramesPerAU *
audio_header_.number_of_channels * sizeof(opus_int16));
#else // SB_HAS_QUIRK(SUPPORT_INT16_AUDIO_SAMPLES)
working_buffer_.resize(kMaxOpusFramesPerAU *
audio_header_.number_of_channels * sizeof(float));
#endif // SB_HAS_QUIRK(SUPPORT_INT16_AUDIO_SAMPLES)
int error;
decoder_ = opus_decoder_create(audio_header_.samples_per_second,
audio_header_.number_of_channels, &error);
if (error != OPUS_OK) {
SB_LOG(ERROR) << "Failed to create decoder with error: "
<< opus_strerror(error);
decoder_ = NULL;
return;
}
SB_DCHECK(decoder_ != NULL);
}
OpusAudioDecoder::~OpusAudioDecoder() {
if (decoder_) {
opus_decoder_destroy(decoder_);
}
}
void OpusAudioDecoder::Initialize(const OutputCB& output_cb,
const ErrorCB& error_cb) {
SB_DCHECK(BelongsToCurrentThread());
SB_DCHECK(output_cb);
SB_DCHECK(!output_cb_);
SB_DCHECK(error_cb);
SB_DCHECK(!error_cb_);
output_cb_ = output_cb;
error_cb_ = error_cb;
}
void OpusAudioDecoder::Decode(const scoped_refptr<InputBuffer>& input_buffer,
const ConsumedCB& consumed_cb) {
SB_DCHECK(BelongsToCurrentThread());
SB_DCHECK(input_buffer);
SB_DCHECK(output_cb_);
Schedule(consumed_cb);
if (stream_ended_) {
SB_LOG(ERROR) << "Decode() is called after WriteEndOfStream() is called.";
return;
}
#if SB_HAS_QUIRK(SUPPORT_INT16_AUDIO_SAMPLES)
const char kDecodeFunctionName[] = "opus_decode";
int decoded_frames = opus_decode(
decoder_, static_cast<const unsigned char*>(input_buffer->data()),
input_buffer->size(),
reinterpret_cast<opus_int16*>(working_buffer_.data()),
kMaxOpusFramesPerAU, 0);
#else // SB_HAS_QUIRK(SUPPORT_INT16_AUDIO_SAMPLES)
const char kDecodeFunctionName[] = "opus_decode_float";
int decoded_frames = opus_decode_float(
decoder_, static_cast<const unsigned char*>(input_buffer->data()),
input_buffer->size(), reinterpret_cast<float*>(working_buffer_.data()),
kMaxOpusFramesPerAU, 0);
#endif // SB_HAS_QUIRK(SUPPORT_INT16_AUDIO_SAMPLES)
if (decoded_frames <= 0) {
// TODO: Consider fill it with silence.
SB_LOG(ERROR) << kDecodeFunctionName
<< "() failed with error code: " << decoded_frames;
error_cb_(kSbPlayerErrorDecode,
FormatString("%s() failed with error code: %d",
kDecodeFunctionName, decoded_frames));
return;
}
scoped_refptr<DecodedAudio> decoded_audio = new DecodedAudio(
audio_header_.number_of_channels, GetSampleType(), GetStorageType(),
input_buffer->timestamp(),
audio_header_.number_of_channels * decoded_frames *
starboard::media::GetBytesPerSample(GetSampleType()));
SbMemoryCopy(decoded_audio->buffer(), working_buffer_.data(),
decoded_audio->size());
decoded_audios_.push(decoded_audio);
Schedule(output_cb_);
}
void OpusAudioDecoder::WriteEndOfStream() {
SB_DCHECK(BelongsToCurrentThread());
SB_DCHECK(output_cb_);
// Opus has no dependent frames so we needn't flush the decoder. Set the
// flag to ensure that Decode() is not called when the stream is ended.
stream_ended_ = true;
// Put EOS into the queue.
decoded_audios_.push(new DecodedAudio);
Schedule(output_cb_);
}
scoped_refptr<OpusAudioDecoder::DecodedAudio> OpusAudioDecoder::Read() {
SB_DCHECK(BelongsToCurrentThread());
SB_DCHECK(output_cb_);
SB_DCHECK(!decoded_audios_.empty());
scoped_refptr<DecodedAudio> result;
if (!decoded_audios_.empty()) {
result = decoded_audios_.front();
decoded_audios_.pop();
}
return result;
}
void OpusAudioDecoder::Reset() {
SB_DCHECK(BelongsToCurrentThread());
stream_ended_ = false;
while (!decoded_audios_.empty()) {
decoded_audios_.pop();
}
}
bool OpusAudioDecoder::is_valid() const {
return decoder_ != NULL;
}
SbMediaAudioSampleType OpusAudioDecoder::GetSampleType() const {
SB_DCHECK(BelongsToCurrentThread());
#if SB_HAS_QUIRK(SUPPORT_INT16_AUDIO_SAMPLES)
return kSbMediaAudioSampleTypeInt16;
#else // SB_HAS_QUIRK(SUPPORT_INT16_AUDIO_SAMPLES)
return kSbMediaAudioSampleTypeFloat32;
#endif // SB_HAS_QUIRK(SUPPORT_INT16_AUDIO_SAMPLES)
}
SbMediaAudioFrameStorageType OpusAudioDecoder::GetStorageType() const {
SB_DCHECK(BelongsToCurrentThread());
return kSbMediaAudioFrameStorageTypeInterleaved;
}
int OpusAudioDecoder::GetSamplesPerSecond() const {
return audio_header_.samples_per_second;
}
} // namespace opus
} // namespace shared
} // namespace starboard