blob: c761c092a814aa8c8f53b33d63935fb948f8acdd [file] [log] [blame]
// Copyright 2016 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 "starboard/shared/starboard/player/audio_renderer_internal.h"
#include <algorithm>
#include "starboard/memory.h"
namespace starboard {
namespace shared {
namespace starboard {
namespace player {
namespace {
// TODO: This should be retrieved from the decoder.
// TODO: Make it not dependent on the frame size of AAC and HE-AAC.
const int kMaxFramesPerAccessUnit = 1024 * 2;
} // namespace
AudioRenderer::AudioRenderer(AudioDecoder* decoder,
const SbMediaAudioHeader& audio_header)
: channels_(audio_header.number_of_channels),
paused_(true),
seeking_(false),
seeking_to_pts_(0),
frame_buffer_(kMaxCachedFrames * audio_header.number_of_channels),
frames_in_buffer_(0),
offset_in_frames_(0),
frames_consumed_(0),
end_of_stream_reached_(false),
decoder_(decoder),
audio_sink_(kSbAudioSinkInvalid) {
SB_DCHECK(decoder_ != NULL);
frame_buffers_[0] = &frame_buffer_[0];
}
AudioRenderer::~AudioRenderer() {
if (audio_sink_ != kSbAudioSinkInvalid) {
SbAudioSinkDestroy(audio_sink_);
}
delete decoder_;
}
void AudioRenderer::WriteSample(const InputBuffer& input_buffer) {
if (end_of_stream_reached_) {
SB_LOG(ERROR) << "Appending audio sample at " << input_buffer.pts()
<< " after EOS reached.";
return;
}
SbMediaTime input_pts = input_buffer.pts();
std::vector<float> decoded_audio;
decoder_->Decode(input_buffer, &decoded_audio);
if (decoded_audio.empty()) {
SB_DLOG(ERROR) << "decoded_audio contains no frames.";
return;
}
{
ScopedLock lock(mutex_);
if (seeking_) {
if (input_pts < seeking_to_pts_) {
return;
}
}
AppendFrames(&decoded_audio[0], decoded_audio.size() / channels_);
if (seeking_ && frame_buffer_.size() > kPrerollFrames * channels_) {
seeking_ = false;
}
}
// Create the audio sink if it is the first incoming AU after seeking.
if (audio_sink_ == kSbAudioSinkInvalid) {
int sample_rate = decoder_->GetSamplesPerSecond();
// TODO: Implement resampler.
SB_DCHECK(sample_rate ==
SbAudioSinkGetNearestSupportedSampleFrequency(sample_rate));
// TODO: Handle sink creation failure.
audio_sink_ = SbAudioSinkCreate(
channels_, sample_rate, kSbMediaAudioSampleTypeFloat32,
kSbMediaAudioFrameStorageTypeInterleaved,
reinterpret_cast<SbAudioSinkFrameBuffers>(frame_buffers_),
kMaxCachedFrames, &AudioRenderer::UpdateSourceStatusFunc,
&AudioRenderer::ConsumeFramesFunc, this);
}
}
void AudioRenderer::WriteEndOfStream() {
SB_LOG_IF(WARNING, end_of_stream_reached_)
<< "Try to write EOS after EOS is reached";
if (end_of_stream_reached_) {
return;
}
end_of_stream_reached_ = true;
decoder_->WriteEndOfStream();
ScopedLock lock(mutex_);
// If we are seeking, we consider the seek is finished if end of stream is
// reached as there won't be any audio data in future.
if (seeking_) {
seeking_ = false;
}
}
void AudioRenderer::Play() {
ScopedLock lock(mutex_);
paused_ = false;
}
void AudioRenderer::Pause() {
ScopedLock lock(mutex_);
paused_ = true;
}
void AudioRenderer::Seek(SbMediaTime seek_to_pts) {
SB_DCHECK(seek_to_pts >= 0);
SbAudioSinkDestroy(audio_sink_);
audio_sink_ = kSbAudioSinkInvalid;
seeking_to_pts_ = std::max<SbMediaTime>(seek_to_pts, 0);
seeking_ = true;
frames_in_buffer_ = 0;
offset_in_frames_ = 0;
frames_consumed_ = 0;
end_of_stream_reached_ = false;
decoder_->Reset();
return;
}
bool AudioRenderer::IsEndOfStreamPlayed() const {
return end_of_stream_reached_ && frames_in_buffer_ == 0;
}
bool AudioRenderer::CanAcceptMoreData() const {
ScopedLock lock(mutex_);
return frames_in_buffer_ <= kMaxCachedFrames - kMaxFramesPerAccessUnit &&
!end_of_stream_reached_;
}
bool AudioRenderer::IsSeekingInProgress() const {
return seeking_;
}
SbMediaTime AudioRenderer::GetCurrentTime() {
if (seeking_) {
return seeking_to_pts_;
}
return seeking_to_pts_ +
frames_consumed_ * kSbMediaTimeSecond /
decoder_->GetSamplesPerSecond();
}
// static
void AudioRenderer::UpdateSourceStatusFunc(int* frames_in_buffer,
int* offset_in_frames,
bool* is_playing,
bool* is_eos_reached,
void* context) {
AudioRenderer* audio_renderer = reinterpret_cast<AudioRenderer*>(context);
SB_DCHECK(audio_renderer);
SB_DCHECK(frames_in_buffer);
SB_DCHECK(offset_in_frames);
SB_DCHECK(is_playing);
SB_DCHECK(is_eos_reached);
audio_renderer->UpdateSourceStatus(frames_in_buffer, offset_in_frames,
is_playing, is_eos_reached);
}
// static
void AudioRenderer::ConsumeFramesFunc(int frames_consumed, void* context) {
AudioRenderer* audio_renderer = reinterpret_cast<AudioRenderer*>(context);
SB_DCHECK(audio_renderer);
audio_renderer->ConsumeFrames(frames_consumed);
}
void AudioRenderer::UpdateSourceStatus(int* frames_in_buffer,
int* offset_in_frames,
bool* is_playing,
bool* is_eos_reached) {
ScopedLock lock(mutex_);
*is_eos_reached = end_of_stream_reached_;
if (paused_ || seeking_) {
*is_playing = false;
*frames_in_buffer = *offset_in_frames = 0;
return;
}
*is_playing = true;
*frames_in_buffer = frames_in_buffer_;
*offset_in_frames = offset_in_frames_;
}
void AudioRenderer::ConsumeFrames(int frames_consumed) {
ScopedLock lock(mutex_);
SB_DCHECK(frames_consumed <= frames_in_buffer_);
offset_in_frames_ += frames_consumed;
offset_in_frames_ %= kMaxCachedFrames;
frames_in_buffer_ -= frames_consumed;
frames_consumed_ += frames_consumed;
}
void AudioRenderer::AppendFrames(const float* source_buffer,
int frames_to_append) {
SB_DCHECK(frames_in_buffer_ + frames_to_append <= kMaxCachedFrames);
int offset_to_append =
(offset_in_frames_ + frames_in_buffer_) % kMaxCachedFrames;
if (frames_to_append > kMaxCachedFrames - offset_to_append) {
SbMemoryCopy(
&frame_buffer_[offset_to_append * channels_], source_buffer,
(kMaxCachedFrames - offset_to_append) * sizeof(float) * channels_);
source_buffer += (kMaxCachedFrames - offset_to_append) * channels_;
frames_to_append -= kMaxCachedFrames - offset_to_append;
frames_in_buffer_ += kMaxCachedFrames - offset_to_append;
offset_to_append = 0;
}
SbMemoryCopy(&frame_buffer_[offset_to_append * channels_], source_buffer,
frames_to_append * sizeof(float) * channels_);
frames_in_buffer_ += frames_to_append;
}
} // namespace player
} // namespace starboard
} // namespace shared
} // namespace starboard