blob: 5edf96fdc7097f1551ef1db7a7d0be13e12dc87f [file] [log] [blame]
// Copyright 2017 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/android/shared/video_render_algorithm.h"
#include <algorithm>
#include "starboard/android/shared/jni_utils.h"
#include "starboard/android/shared/media_common.h"
#include "starboard/common/log.h"
namespace starboard {
namespace android {
namespace shared {
namespace {
const SbTimeMonotonic kBufferTooLateThreshold = -32 * kSbTimeMillisecond;
const SbTimeMonotonic kBufferReadyThreshold = 50 * kSbTimeMillisecond;
jlong GetSystemNanoTime() {
timespec now;
clock_gettime(CLOCK_MONOTONIC, &now);
return now.tv_sec * 1000000000LL + now.tv_nsec;
}
} // namespace
VideoRenderAlgorithm::VideoRenderAlgorithm(VideoDecoder* video_decoder)
: video_decoder_(video_decoder) {
SB_DCHECK(video_decoder_);
video_decoder_->SetPlaybackRate(playback_rate_);
}
void VideoRenderAlgorithm::Render(
MediaTimeProvider* media_time_provider,
std::list<scoped_refptr<VideoFrame>>* frames,
VideoRendererSink::DrawFrameCB draw_frame_cb) {
SB_DCHECK(media_time_provider);
SB_DCHECK(frames);
SB_DCHECK(draw_frame_cb);
while (frames->size() > 0) {
if (frames->front()->is_end_of_stream()) {
frames->pop_front();
SB_DCHECK(frames->empty())
<< "Expected end of stream output buffer to be the last buffer.";
break;
}
bool is_audio_playing;
bool is_audio_eos_played;
bool is_underflow;
double playback_rate;
SbTime playback_time = media_time_provider->GetCurrentMediaTime(
&is_audio_playing, &is_audio_eos_played, &is_underflow, &playback_rate);
if (!is_audio_playing) {
break;
}
if (playback_rate != playback_rate_) {
playback_rate_ = playback_rate;
video_decoder_->SetPlaybackRate(playback_rate);
}
if (is_audio_eos_played) {
// If the audio stream has reached end of stream before the video stream,
// we should end the video stream immediately by only keeping one frame in
// the backlog.
bool popped = false;
while (frames->size() > 1) {
frames->pop_front();
popped = true;
}
if (popped) {
// Let the loop process the end of stream frame, if there is one.
continue;
} else {
break;
}
}
jlong early_us = frames->front()->timestamp() - playback_time;
auto system_time_ns = GetSystemNanoTime();
auto unadjusted_frame_release_time_ns =
system_time_ns + (early_us * kSbTimeNanosecondsPerMicrosecond);
auto adjusted_release_time_ns =
video_frame_release_time_helper_.AdjustReleaseTime(
frames->front()->timestamp(), unadjusted_frame_release_time_ns);
early_us = (adjusted_release_time_ns - system_time_ns) /
kSbTimeNanosecondsPerMicrosecond;
if (early_us < kBufferTooLateThreshold) {
frames->pop_front();
++dropped_frames_;
} else if (early_us < kBufferReadyThreshold) {
auto status = draw_frame_cb(frames->front(), adjusted_release_time_ns);
SB_DCHECK(status == VideoRendererSink::kReleased);
frames->pop_front();
} else {
break;
}
}
}
VideoRenderAlgorithm::VideoFrameReleaseTimeHelper::
VideoFrameReleaseTimeHelper() {
auto* env = JniEnvExt::Get();
j_video_frame_release_time_helper_ = env->NewObjectOrAbort(
"dev/cobalt/media/VideoFrameReleaseTimeHelper", "()V");
j_video_frame_release_time_helper_ =
env->ConvertLocalRefToGlobalRef(j_video_frame_release_time_helper_);
env->CallVoidMethod(j_video_frame_release_time_helper_, "enable", "()V");
}
VideoRenderAlgorithm::VideoFrameReleaseTimeHelper::
~VideoFrameReleaseTimeHelper() {
SB_DCHECK(j_video_frame_release_time_helper_);
auto* env = JniEnvExt::Get();
env->CallVoidMethod(j_video_frame_release_time_helper_, "disable", "()V");
env->DeleteGlobalRef(j_video_frame_release_time_helper_);
j_video_frame_release_time_helper_ = nullptr;
}
jlong VideoRenderAlgorithm::VideoFrameReleaseTimeHelper::AdjustReleaseTime(
jlong frame_presentation_time_us,
jlong unadjusted_release_time_ns) {
SB_DCHECK(j_video_frame_release_time_helper_);
auto* env = JniEnvExt::Get();
return env->CallLongMethodOrAbort(
j_video_frame_release_time_helper_, "adjustReleaseTime", "(JJ)J",
frame_presentation_time_us, unadjusted_release_time_ns);
}
} // namespace shared
} // namespace android
} // namespace starboard