blob: 105e23fc6a9d668836397f01ffb2ebe24800cd8f [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/filter/audio_renderer_internal.h"
#include <algorithm>
#include "starboard/memory.h"
namespace starboard {
namespace shared {
namespace starboard {
namespace player {
namespace filter {
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(scoped_ptr<AudioDecoder> decoder,
const SbMediaAudioHeader& audio_header)
: channels_(audio_header.number_of_channels),
bytes_per_frame_(
(decoder->GetSampleType() == kSbMediaAudioSampleTypeInt16 ? 2 : 4) *
channels_),
paused_(true),
seeking_(false),
seeking_to_pts_(0),
frame_buffer_(kMaxCachedFrames * bytes_per_frame_),
frames_in_buffer_(0),
offset_in_frames_(0),
frames_consumed_(0),
end_of_stream_reached_(false),
decoder_(decoder.Pass()),
audio_sink_(kSbAudioSinkInvalid) {
SB_DCHECK(decoder_ != NULL);
frame_buffers_[0] = &frame_buffer_[0];
}
AudioRenderer::~AudioRenderer() {
if (audio_sink_ != kSbAudioSinkInvalid) {
SbAudioSinkDestroy(audio_sink_);
}
}
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<uint8_t> 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() / bytes_per_frame_);
if (seeking_ && frame_buffer_.size() > kPrerollFrames * bytes_per_frame_) {
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, decoder_->GetSampleType(),
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 uint8_t* 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 * bytes_per_frame_],
source_buffer,
(kMaxCachedFrames - offset_to_append) * bytes_per_frame_);
source_buffer += (kMaxCachedFrames - offset_to_append) * bytes_per_frame_;
frames_to_append -= kMaxCachedFrames - offset_to_append;
frames_in_buffer_ += kMaxCachedFrames - offset_to_append;
offset_to_append = 0;
}
SbMemoryCopy(&frame_buffer_[offset_to_append * bytes_per_frame_],
source_buffer, frames_to_append * bytes_per_frame_);
frames_in_buffer_ += frames_to_append;
}
} // namespace filter
} // namespace player
} // namespace starboard
} // namespace shared
} // namespace starboard