blob: 35b6bf02585248474a3d95056e45ea8845d27869 [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>
namespace starboard {
namespace shared {
namespace starboard {
namespace player {
namespace filter {
VideoRenderer::VideoRenderer(scoped_ptr<VideoDecoder> decoder)
: seeking_(false),
seeking_to_pts_(0),
end_of_stream_written_(false),
need_more_input_(true),
decoder_(decoder.Pass()) {
SB_DCHECK(decoder_ != NULL);
decoder_->SetHost(this);
}
void VideoRenderer::WriteSample(const InputBuffer& input_buffer) {
SB_DCHECK(thread_checker_.CalledOnValidThread());
if (end_of_stream_written_) {
SB_LOG(ERROR) << "Appending video sample at " << input_buffer.pts()
<< " after EOS reached.";
return;
}
{
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;
decoder_->WriteEndOfStream();
}
void VideoRenderer::Seek(SbMediaTime seek_to_pts) {
SB_DCHECK(thread_checker_.CalledOnValidThread());
SB_DCHECK(seek_to_pts >= 0);
decoder_->Reset();
ScopedLock lock(mutex_);
seeking_to_pts_ = std::max<SbMediaTime>(seek_to_pts, 0);
seeking_ = true;
end_of_stream_written_ = false;
if (!frames_.empty()) {
seeking_frame_ = frames_.front();
}
frames_.clear();
}
scoped_refptr<VideoFrame> VideoRenderer::GetCurrentFrame(
SbMediaTime media_time) {
SB_DCHECK(thread_checker_.CalledOnValidThread());
if (frames_.empty()) {
return seeking_frame_;
}
// Remove any frames with timestamps earlier than |media_time|, but always
// keep at least one of the frames.
while (frames_.size() > 1 && frames_.front()->pts() < media_time) {
frames_.pop_front();
}
return frames_.front();
}
bool VideoRenderer::IsEndOfStreamPlayed() const {
SB_DCHECK(thread_checker_.CalledOnValidThread());
return end_of_stream_written_ && frames_.size() <= 1;
}
bool VideoRenderer::CanAcceptMoreData() const {
SB_DCHECK(thread_checker_.CalledOnValidThread());
ScopedLock lock(mutex_);
return frames_.size() < kMaxCachedFrames && !end_of_stream_written_ &&
need_more_input_;
}
bool VideoRenderer::IsSeekingInProgress() const {
SB_DCHECK(thread_checker_.CalledOnValidThread());
return seeking_;
}
void VideoRenderer::OnDecoderStatusUpdate(
VideoDecoder::Status status,
const scoped_refptr<VideoFrame>& frame) {
ScopedLock lock(mutex_);
if (frame) {
bool frame_too_early = false;
if (seeking_) {
if (frame->IsEndOfStream()) {
seeking_ = false;
} else if (frame->pts() < seeking_to_pts_) {
frame_too_early = true;
}
}
if (!frame_too_early) {
frames_.push_back(frame);
}
if (seeking_ && frames_.size() >= kPrerollFrames) {
seeking_ = false;
}
}
need_more_input_ = (status == VideoDecoder::kNeedMoreInput);
}
} // namespace filter
} // namespace player
} // namespace starboard
} // namespace shared
} // namespace starboard