blob: dfdf8d6bdf74afbba74c26896fca3665293a9bce [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/video_renderer_internal.h"
#include <algorithm>
#include <functional>
namespace starboard {
namespace shared {
namespace starboard {
namespace player {
namespace filter {
using std::placeholders::_1;
using std::placeholders::_2;
VideoRenderer::VideoRenderer(scoped_ptr<VideoDecoder> decoder,
MediaTimeProvider* media_time_provider,
scoped_ptr<VideoRenderAlgorithm> algorithm,
scoped_refptr<VideoRendererSink> sink)
: media_time_provider_(media_time_provider),
algorithm_(algorithm.Pass()),
sink_(sink),
seeking_(false),
seeking_to_time_(0),
end_of_stream_written_(false),
need_more_input_(true),
decoder_(decoder.Pass()),
first_input_written_(false) {
SB_DCHECK(decoder_ != NULL);
SB_DCHECK(algorithm_ != NULL);
SB_DCHECK(sink_ != NULL);
}
VideoRenderer::~VideoRenderer() {
sink_ = NULL;
// Be sure to release anything created by the decoder_ before releasing the
// decoder_ itself.
if (first_input_written_) {
decoder_->Reset();
}
frames_.clear();
decoder_.reset();
}
void VideoRenderer::Initialize(const ErrorCB& error_cb) {
decoder_->Initialize(std::bind(&VideoRenderer::OnDecoderStatus, this, _1, _2),
error_cb);
if (sink_) {
sink_->SetRenderCB(std::bind(&VideoRenderer::Render, this, _1));
}
}
void VideoRenderer::SetBounds(int z_index,
int x,
int y,
int width,
int height) {
sink_->SetBounds(z_index, x, y, width, height);
}
void VideoRenderer::WriteSample(
const scoped_refptr<InputBuffer>& input_buffer) {
SB_DCHECK(thread_checker_.CalledOnValidThread());
SB_DCHECK(input_buffer);
if (end_of_stream_written_) {
SB_LOG(ERROR) << "Appending video sample at " << input_buffer->timestamp()
<< " after EOS reached.";
return;
}
if (!first_input_written_) {
first_input_written_ = true;
absolute_time_of_first_input_ = SbTimeGetMonotonicNow();
}
{
ScopedLock lock(mutex_);
need_more_input_ = false;
}
decoder_->WriteInputBuffer(input_buffer);
}
void VideoRenderer::WriteEndOfStream() {
SB_DCHECK(thread_checker_.CalledOnValidThread());
SB_LOG_IF(WARNING, end_of_stream_written_)
<< "Try to write EOS after EOS is reached";
if (end_of_stream_written_) {
return;
}
end_of_stream_written_ = true;
first_input_written_ = true;
decoder_->WriteEndOfStream();
}
void VideoRenderer::Seek(SbTime seek_to_time) {
SB_DCHECK(thread_checker_.CalledOnValidThread());
SB_DCHECK(seek_to_time >= 0);
if (first_input_written_) {
decoder_->Reset();
first_input_written_ = false;
}
ScopedLock lock(mutex_);
seeking_to_time_ = std::max<SbTime>(seek_to_time, 0);
seeking_ = true;
end_of_stream_written_ = false;
need_more_input_ = true;
frames_.clear();
}
bool VideoRenderer::IsEndOfStreamPlayed() const {
SB_DCHECK(thread_checker_.CalledOnValidThread());
ScopedLock lock(mutex_);
return end_of_stream_written_ && frames_.size() <= 1;
}
bool VideoRenderer::CanAcceptMoreData() const {
SB_DCHECK(thread_checker_.CalledOnValidThread());
ScopedLock lock(mutex_);
return frames_.size() < decoder_->GetMaxNumberOfCachedFrames() &&
!end_of_stream_written_ && need_more_input_;
}
bool VideoRenderer::UpdateAndRetrieveIsSeekingInProgress() {
SB_DCHECK(thread_checker_.CalledOnValidThread());
ScopedLock lock(mutex_);
if (seeking_ && !frames_.empty()) {
auto elapsed = SbTimeGetMonotonicNow() - absolute_time_of_first_input_;
seeking_ = elapsed < decoder_->GetPrerollTimeout();
}
return seeking_;
}
void VideoRenderer::OnDecoderStatus(VideoDecoder::Status status,
const scoped_refptr<VideoFrame>& frame) {
ScopedLock lock(mutex_);
if (status == VideoDecoder::kReleaseAllFrames) {
frames_.clear();
return;
}
if (frame) {
SB_DCHECK(first_input_written_);
bool frame_too_early = false;
if (seeking_) {
if (frame->is_end_of_stream()) {
seeking_ = false;
} else if (frame->timestamp() < seeking_to_time_) {
frame_too_early = true;
}
}
if (!frame_too_early) {
frames_.push_back(frame);
}
if (seeking_ && frames_.size() >= decoder_->GetPrerollFrameCount()) {
seeking_ = false;
}
}
need_more_input_ = (status == VideoDecoder::kNeedMoreInput);
}
void VideoRenderer::Render(VideoRendererSink::DrawFrameCB draw_frame_cb) {
ScopedLock lock(mutex_);
algorithm_->Render(media_time_provider_, &frames_, draw_frame_cb);
}
SbDecodeTarget VideoRenderer::GetCurrentDecodeTarget() {
// FilterBasedPlayerWorkerHandler::Stop() ensures that this function won't be
// called right before VideoRenderer dtor is called and |decoder_| is set to
// NULL inside the dtor.
SB_DCHECK(decoder_);
return decoder_->GetCurrentDecodeTarget();
}
} // namespace filter
} // namespace player
} // namespace starboard
} // namespace shared
} // namespace starboard