Skip to content
Toggle navigation
P
Projects
G
Groups
S
Snippets
Help
SDK
/
exoplayer
This project
Loading...
Sign in
Toggle navigation
Go to a project
Project
Repository
Issues
0
Merge Requests
0
Pipelines
Wiki
Snippets
Settings
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Commit
165562d8
authored
Nov 27, 2014
by
Oliver Woodman
Browse files
Options
_('Browse Files')
Download
Email Patches
Plain Diff
Add VSYNC aligning smooth frame release helper.
parent
2969bba6
Show whitespace changes
Inline
Side-by-side
Showing
1 changed file
with
180 additions
and
0 deletions
library/src/main/java/com/google/android/exoplayer/SmoothFrameReleaseTimeHelper.java
library/src/main/java/com/google/android/exoplayer/SmoothFrameReleaseTimeHelper.java
0 → 100644
View file @
165562d8
/*
* Copyright (C) 2014 The Android Open Source Project
*
* 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.
*/
package
com
.
google
.
android
.
exoplayer
;
import
com.google.android.exoplayer.MediaCodecVideoTrackRenderer.FrameReleaseTimeHelper
;
import
android.annotation.TargetApi
;
import
android.view.Choreographer
;
import
android.view.Choreographer.FrameCallback
;
/**
* Makes a best effort to adjust frame release timestamps for a smoother visual result.
*/
@TargetApi
(
16
)
public
class
SmoothFrameReleaseTimeHelper
implements
FrameReleaseTimeHelper
,
FrameCallback
{
private
static
final
long
CHOREOGRAPHER_SAMPLE_DELAY_MILLIS
=
500
;
private
static
final
long
MAX_ALLOWED_DRIFT_NS
=
20000000
;
private
static
final
long
VSYNC_OFFSET_PERCENTAGE
=
80
;
private
static
final
int
MIN_FRAMES_FOR_ADJUSTMENT
=
6
;
private
final
boolean
usePrimaryDisplayVsync
;
private
final
long
vsyncDurationNs
;
private
final
long
vsyncOffsetNs
;
private
Choreographer
choreographer
;
private
long
sampledVsyncTimeNs
;
private
long
lastUnadjustedFrameTimeUs
;
private
long
adjustedLastFrameTimeNs
;
private
long
pendingAdjustedFrameTimeNs
;
private
boolean
haveSync
;
private
long
syncReleaseTimeNs
;
private
long
syncFrameTimeNs
;
private
int
frameCount
;
/**
* @param primaryDisplayRefreshRate The refresh rate of the default display.
* @param usePrimaryDisplayVsync Whether to snap to the primary display vsync. May not be
* suitable when rendering to secondary displays.
*/
public
SmoothFrameReleaseTimeHelper
(
float
primaryDisplayRefreshRate
,
boolean
usePrimaryDisplayVsync
)
{
this
.
usePrimaryDisplayVsync
=
usePrimaryDisplayVsync
;
if
(
usePrimaryDisplayVsync
)
{
vsyncDurationNs
=
(
long
)
(
1000000000
d
/
primaryDisplayRefreshRate
);
vsyncOffsetNs
=
(
vsyncDurationNs
*
VSYNC_OFFSET_PERCENTAGE
)
/
100
;
}
else
{
vsyncDurationNs
=
-
1
;
vsyncOffsetNs
=
-
1
;
}
}
@Override
public
void
enable
()
{
haveSync
=
false
;
if
(
usePrimaryDisplayVsync
)
{
sampledVsyncTimeNs
=
0
;
choreographer
=
Choreographer
.
getInstance
();
choreographer
.
postFrameCallback
(
this
);
}
}
@Override
public
void
disable
()
{
if
(
usePrimaryDisplayVsync
)
{
choreographer
.
removeFrameCallback
(
this
);
choreographer
=
null
;
}
}
@Override
public
void
doFrame
(
long
vsyncTimeNs
)
{
sampledVsyncTimeNs
=
vsyncTimeNs
;
choreographer
.
postFrameCallbackDelayed
(
this
,
CHOREOGRAPHER_SAMPLE_DELAY_MILLIS
);
}
@Override
public
long
adjustReleaseTime
(
long
unadjustedFrameTimeUs
,
long
unadjustedReleaseTimeNs
)
{
long
unadjustedFrameTimeNs
=
unadjustedFrameTimeUs
*
1000
;
// Until we know better, the adjustment will be a no-op.
long
adjustedFrameTimeNs
=
unadjustedFrameTimeNs
;
long
adjustedReleaseTimeNs
=
unadjustedReleaseTimeNs
;
if
(
haveSync
)
{
// See if we've advanced to the next frame.
if
(
unadjustedFrameTimeUs
!=
lastUnadjustedFrameTimeUs
)
{
frameCount
++;
adjustedLastFrameTimeNs
=
pendingAdjustedFrameTimeNs
;
}
if
(
frameCount
>=
MIN_FRAMES_FOR_ADJUSTMENT
)
{
// We're synced and have waited the required number of frames to apply an adjustment.
// Calculate the average frame time across all the frames we've seen since the last sync.
// This will typically give us a framerate at a finer granularity than the frame times
// themselves (which often only have millisecond granularity).
long
averageFrameTimeNs
=
(
unadjustedFrameTimeNs
-
syncFrameTimeNs
)
/
frameCount
;
// Project the adjusted frame time forward using the average.
long
candidateAdjustedFrameTimeNs
=
adjustedLastFrameTimeNs
+
averageFrameTimeNs
;
if
(
isDriftTooLarge
(
candidateAdjustedFrameTimeNs
,
unadjustedReleaseTimeNs
))
{
haveSync
=
false
;
}
else
{
adjustedFrameTimeNs
=
candidateAdjustedFrameTimeNs
;
adjustedReleaseTimeNs
=
syncReleaseTimeNs
+
adjustedFrameTimeNs
-
syncFrameTimeNs
;
}
}
else
{
// We're synced but haven't waited the required number of frames to apply an adjustment.
// Check drift anyway.
if
(
isDriftTooLarge
(
unadjustedFrameTimeNs
,
unadjustedReleaseTimeNs
))
{
haveSync
=
false
;
}
}
}
// If we need to sync, do so now.
if
(!
haveSync
)
{
syncFrameTimeNs
=
unadjustedFrameTimeNs
;
syncReleaseTimeNs
=
unadjustedReleaseTimeNs
;
frameCount
=
0
;
haveSync
=
true
;
onSynced
();
}
lastUnadjustedFrameTimeUs
=
unadjustedFrameTimeUs
;
pendingAdjustedFrameTimeNs
=
adjustedFrameTimeNs
;
if
(
sampledVsyncTimeNs
==
0
)
{
return
adjustedReleaseTimeNs
;
}
// Find the timestamp of the closest vsync. This is the vsync that we're targeting.
long
snappedTimeNs
=
closestVsync
(
adjustedReleaseTimeNs
,
sampledVsyncTimeNs
,
vsyncDurationNs
);
// Apply an offset so that we release before the target vsync, but after the previous one.
return
snappedTimeNs
-
vsyncOffsetNs
;
}
protected
void
onSynced
()
{
// Do nothing.
}
private
boolean
isDriftTooLarge
(
long
frameTimeNs
,
long
releaseTimeNs
)
{
long
elapsedFrameTimeNs
=
frameTimeNs
-
syncFrameTimeNs
;
long
elapsedReleaseTimeNs
=
releaseTimeNs
-
syncReleaseTimeNs
;
return
Math
.
abs
(
elapsedReleaseTimeNs
-
elapsedFrameTimeNs
)
>
MAX_ALLOWED_DRIFT_NS
;
}
private
static
long
closestVsync
(
long
releaseTime
,
long
sampledVsyncTime
,
long
vsyncDuration
)
{
long
vsyncCount
=
(
releaseTime
-
sampledVsyncTime
)
/
vsyncDuration
;
long
snappedTimeNs
=
sampledVsyncTime
+
(
vsyncDuration
*
vsyncCount
);
long
snappedBeforeNs
;
long
snappedAfterNs
;
if
(
releaseTime
<=
snappedTimeNs
)
{
snappedBeforeNs
=
snappedTimeNs
-
vsyncDuration
;
snappedAfterNs
=
snappedTimeNs
;
}
else
{
snappedBeforeNs
=
snappedTimeNs
;
snappedAfterNs
=
snappedTimeNs
+
vsyncDuration
;
}
long
snappedAfterDiff
=
snappedAfterNs
-
releaseTime
;
long
snappedBeforeDiff
=
releaseTime
-
snappedBeforeNs
;
return
snappedAfterDiff
<
snappedBeforeDiff
?
snappedAfterNs
:
snappedBeforeNs
;
}
}
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment