-
Notifications
You must be signed in to change notification settings - Fork 116
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[android] Refine dropped frames reporting #2006
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -14,131 +14,38 @@ | |
|
||
#include "starboard/android/shared/video_frame_tracker.h" | ||
|
||
#include <cmath> | ||
#include <cstdint> | ||
#include <cstdlib> | ||
#include <vector> | ||
|
||
#include "starboard/common/log.h" | ||
#include "starboard/common/mutex.h" | ||
#include "starboard/time.h" | ||
|
||
namespace starboard { | ||
namespace android { | ||
namespace shared { | ||
namespace { | ||
|
||
const SbTime kMaxAllowedSkew = 5000; | ||
|
||
} // namespace | ||
|
||
SbTime VideoFrameTracker::seek_to_time() const { | ||
return seek_to_time_; | ||
} | ||
|
||
void VideoFrameTracker::OnInputBuffer(SbTime timestamp) { | ||
SB_DCHECK(thread_checker_.CalledOnValidThread()); | ||
void VideoFrameTracker::OnInputBufferEnqueued( | ||
const scoped_refptr<InputBuffer>& input_buffer) { | ||
SB_DCHECK(input_buffer); | ||
SB_DCHECK(media_time_provider_); | ||
|
||
if (frames_to_be_rendered_.empty()) { | ||
frames_to_be_rendered_.push_back(timestamp); | ||
if (input_buffer->timestamp() < seek_to_time_) { | ||
return; | ||
} | ||
|
||
if (frames_to_be_rendered_.size() > max_pending_frames_size_) { | ||
// OnFrameRendered() is only available after API level 23. Cap the size | ||
// of |frames_to_be_rendered_| in case OnFrameRendered() is not available. | ||
frames_to_be_rendered_.pop_front(); | ||
} | ||
bool is_playing = true; | ||
bool is_eos_played = true; | ||
bool is_underflow = true; | ||
double playback_rate = -1.0; | ||
SbTime media_time = media_time_provider_->GetCurrentMediaTime( | ||
&is_playing, &is_eos_played, &is_underflow, &playback_rate); | ||
|
||
// Sort by |timestamp|, because |timestamp| won't be monotonic if there are | ||
// B frames. | ||
for (auto it = frames_to_be_rendered_.end(); | ||
it != frames_to_be_rendered_.begin();) { | ||
it--; | ||
if (*it < timestamp) { | ||
frames_to_be_rendered_.emplace(++it, timestamp); | ||
return; | ||
} else if (*it == timestamp) { | ||
SB_LOG(WARNING) << "feed video AU with same time stamp " << timestamp; | ||
return; | ||
} | ||
if (input_buffer->timestamp() < media_time) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If a frame is queued right before the current media time, we treat it as not dropped. This is a quite strong assumption and may lead to under report of dropped frames. Have we tried on some devices to see whether this reflects the actual frame drops? |
||
dropped_frames_++; | ||
} | ||
|
||
frames_to_be_rendered_.emplace_front(timestamp); | ||
} | ||
|
||
void VideoFrameTracker::OnFrameRendered(int64_t frame_timestamp) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Consider checking with the partners on whether the behavior of the callback are reliable on certain platforms. |
||
ScopedLock lock(rendered_frames_mutex_); | ||
rendered_frames_on_decoder_thread_.push_back(frame_timestamp); | ||
} | ||
|
||
void VideoFrameTracker::Seek(SbTime seek_to_time) { | ||
SB_DCHECK(thread_checker_.CalledOnValidThread()); | ||
|
||
// Ensure that all dropped frames before seeking are captured. | ||
UpdateDroppedFrames(); | ||
|
||
frames_to_be_rendered_.clear(); | ||
seek_to_time_ = seek_to_time; | ||
} | ||
|
||
int VideoFrameTracker::UpdateAndGetDroppedFrames() { | ||
SB_DCHECK(thread_checker_.CalledOnValidThread()); | ||
UpdateDroppedFrames(); | ||
return dropped_frames_; | ||
} | ||
|
||
void VideoFrameTracker::UpdateDroppedFrames() { | ||
SB_DCHECK(thread_checker_.CalledOnValidThread()); | ||
|
||
{ | ||
ScopedLock lock(rendered_frames_mutex_); | ||
rendered_frames_on_tracker_thread_.swap(rendered_frames_on_decoder_thread_); | ||
} | ||
|
||
while (frames_to_be_rendered_.front() < seek_to_time_) { | ||
// It is possible that the initial frame rendered time is before the | ||
// seek to time, when the platform decides to render a frame earlier | ||
// than the seek to time during preroll. This shouldn't be an issue | ||
// after we align seek time to the next video key frame. | ||
frames_to_be_rendered_.pop_front(); | ||
} | ||
|
||
// Loop over all timestamps from OnFrameRendered and compare against ones from | ||
// OnInputBuffer. | ||
for (auto rendered_timestamp : rendered_frames_on_tracker_thread_) { | ||
auto to_render_timestamp = frames_to_be_rendered_.begin(); | ||
// Loop over all frames to render until we've caught up to the timestamp of | ||
// the last rendered frame. | ||
while (to_render_timestamp != frames_to_be_rendered_.end() && | ||
!(*to_render_timestamp - rendered_timestamp > kMaxAllowedSkew)) { | ||
if (std::abs(*to_render_timestamp - rendered_timestamp) <= | ||
kMaxAllowedSkew) { | ||
// This frame was rendered, remove it from frames_to_be_rendered_. | ||
to_render_timestamp = frames_to_be_rendered_.erase(to_render_timestamp); | ||
} else if (rendered_timestamp - *to_render_timestamp > kMaxAllowedSkew) { | ||
// The rendered frame is too far ahead. The to_render_timestamp frame | ||
// was dropped. | ||
SB_LOG(WARNING) << "Video frame dropped:" << *to_render_timestamp | ||
<< ", current frame timestamp:" << rendered_timestamp | ||
<< ", frames in the backlog:" | ||
<< frames_to_be_rendered_.size(); | ||
++dropped_frames_; | ||
to_render_timestamp = frames_to_be_rendered_.erase(to_render_timestamp); | ||
} else { | ||
// The rendered frame is too early to match the next frame to render. | ||
// This could happen if a frame is reported to be rendered twice or if | ||
// it is rendered more than kMaxAllowedSkew early. In the latter | ||
// scenario the frame will be reported dropped in the next iteration of | ||
// the outer loop. | ||
++to_render_timestamp; | ||
} | ||
} | ||
} | ||
|
||
rendered_frames_on_tracker_thread_.clear(); | ||
} | ||
|
||
} // namespace shared | ||
} // namespace android | ||
} // namespace starboard |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit:
OnInputBufferQueued()
, as the platform function is calledQueue*()
.