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
72f4b964
authored
Feb 19, 2020
by
tonihei
Committed by
Oliver Woodman
Feb 25, 2020
Browse files
Options
_('Browse Files')
Download
Email Patches
Plain Diff
Add parameter to Renderer.enable to allow rendering of first sample.
PiperOrigin-RevId: 295985916
parent
c95ed7d1
Hide whitespace changes
Inline
Side-by-side
Showing
13 changed files
with
491 additions
and
108 deletions
library/core/src/main/java/com/google/android/exoplayer2/BaseRenderer.java
library/core/src/main/java/com/google/android/exoplayer2/ExoPlayerImplInternal.java
library/core/src/main/java/com/google/android/exoplayer2/NoSampleRenderer.java
library/core/src/main/java/com/google/android/exoplayer2/Renderer.java
library/core/src/main/java/com/google/android/exoplayer2/audio/MediaCodecAudioRenderer.java
library/core/src/main/java/com/google/android/exoplayer2/audio/SimpleDecoderAudioRenderer.java
library/core/src/main/java/com/google/android/exoplayer2/mediacodec/MediaCodecRenderer.java
library/core/src/main/java/com/google/android/exoplayer2/video/MediaCodecVideoRenderer.java
library/core/src/main/java/com/google/android/exoplayer2/video/SimpleDecoderVideoRenderer.java
library/core/src/test/java/com/google/android/exoplayer2/ExoPlayerTest.java
library/core/src/test/java/com/google/android/exoplayer2/analytics/AnalyticsCollectorTest.java
library/core/src/test/java/com/google/android/exoplayer2/audio/SimpleDecoderAudioRendererTest.java
library/core/src/test/java/com/google/android/exoplayer2/video/SimpleDecoderVideoRendererTest.java
library/core/src/main/java/com/google/android/exoplayer2/BaseRenderer.java
View file @
72f4b964
...
@@ -82,13 +82,19 @@ public abstract class BaseRenderer implements Renderer, RendererCapabilities {
...
@@ -82,13 +82,19 @@ public abstract class BaseRenderer implements Renderer, RendererCapabilities {
}
}
@Override
@Override
public
final
void
enable
(
RendererConfiguration
configuration
,
Format
[]
formats
,
public
final
void
enable
(
SampleStream
stream
,
long
positionUs
,
boolean
joining
,
long
offsetUs
)
RendererConfiguration
configuration
,
Format
[]
formats
,
SampleStream
stream
,
long
positionUs
,
boolean
joining
,
boolean
mayRenderStartOfStream
,
long
offsetUs
)
throws
ExoPlaybackException
{
throws
ExoPlaybackException
{
Assertions
.
checkState
(
state
==
STATE_DISABLED
);
Assertions
.
checkState
(
state
==
STATE_DISABLED
);
this
.
configuration
=
configuration
;
this
.
configuration
=
configuration
;
state
=
STATE_ENABLED
;
state
=
STATE_ENABLED
;
onEnabled
(
joining
);
onEnabled
(
joining
,
mayRenderStartOfStream
);
replaceStream
(
formats
,
stream
,
offsetUs
);
replaceStream
(
formats
,
stream
,
offsetUs
);
onPositionReset
(
positionUs
,
joining
);
onPositionReset
(
positionUs
,
joining
);
}
}
...
@@ -193,27 +199,30 @@ public abstract class BaseRenderer implements Renderer, RendererCapabilities {
...
@@ -193,27 +199,30 @@ public abstract class BaseRenderer implements Renderer, RendererCapabilities {
/**
/**
* Called when the renderer is enabled.
* Called when the renderer is enabled.
*
<p>
*
* The default implementation is a no-op.
*
<p>
The default implementation is a no-op.
*
*
* @param joining Whether this renderer is being enabled to join an ongoing playback.
* @param joining Whether this renderer is being enabled to join an ongoing playback.
* @param mayRenderStartOfStream Whether this renderer is allowed to render the start of the
* stream even if the state is not {@link #STATE_STARTED} yet.
* @throws ExoPlaybackException If an error occurs.
* @throws ExoPlaybackException If an error occurs.
*/
*/
protected
void
onEnabled
(
boolean
joining
)
throws
ExoPlaybackException
{
protected
void
onEnabled
(
boolean
joining
,
boolean
mayRenderStartOfStream
)
throws
ExoPlaybackException
{
// Do nothing.
// Do nothing.
}
}
/**
/**
* Called when the renderer's stream has changed. This occurs when the renderer is enabled after
* Called when the renderer's stream has changed. This occurs when the renderer is enabled after
* {@link #onEnabled(boolean
)} has been called, and also when the stream has been replaced whilst
* {@link #onEnabled(boolean
, boolean)} has been called, and also when the stream has been
* the renderer is enabled or started.
*
replaced whilst
the renderer is enabled or started.
*
<p>
*
* The default implementation is a no-op.
*
<p>
The default implementation is a no-op.
*
*
* @param formats The enabled formats.
* @param formats The enabled formats.
* @param offsetUs The offset that will be added to the timestamps of buffers read via
* @param offsetUs The offset that will be added to the timestamps of buffers read via
{@link
*
{@link #readSource(FormatHolder, DecoderInputBuffer, boolean)} so that decoder input
*
#readSource(FormatHolder, DecoderInputBuffer, boolean)} so that decoder input buffers have
*
buffers have
monotonically increasing timestamps.
* monotonically increasing timestamps.
* @throws ExoPlaybackException If an error occurs.
* @throws ExoPlaybackException If an error occurs.
*/
*/
protected
void
onStreamChanged
(
Format
[]
formats
,
long
offsetUs
)
throws
ExoPlaybackException
{
protected
void
onStreamChanged
(
Format
[]
formats
,
long
offsetUs
)
throws
ExoPlaybackException
{
...
...
library/core/src/main/java/com/google/android/exoplayer2/ExoPlayerImplInternal.java
View file @
72f4b964
...
@@ -2002,6 +2002,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
...
@@ -2002,6 +2002,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
playingPeriodHolder
.
sampleStreams
[
rendererIndex
],
playingPeriodHolder
.
sampleStreams
[
rendererIndex
],
rendererPositionUs
,
rendererPositionUs
,
joining
,
joining
,
/* mayRenderStartOfStream= */
true
,
playingPeriodHolder
.
getRendererOffset
());
playingPeriodHolder
.
getRendererOffset
());
mediaClock
.
onRendererEnabled
(
renderer
);
mediaClock
.
onRendererEnabled
(
renderer
);
// Start the renderer if playing.
// Start the renderer if playing.
...
...
library/core/src/main/java/com/google/android/exoplayer2/NoSampleRenderer.java
View file @
72f4b964
...
@@ -60,24 +60,15 @@ public abstract class NoSampleRenderer implements Renderer, RendererCapabilities
...
@@ -60,24 +60,15 @@ public abstract class NoSampleRenderer implements Renderer, RendererCapabilities
return
state
;
return
state
;
}
}
/**
* Replaces the {@link SampleStream} that will be associated with this renderer.
* <p>
* This method may be called when the renderer is in the following states:
* {@link #STATE_DISABLED}.
*
* @param configuration The renderer configuration.
* @param formats The enabled formats. Should be empty.
* @param stream The {@link SampleStream} from which the renderer should consume.
* @param positionUs The player's current position.
* @param joining Whether this renderer is being enabled to join an ongoing playback.
* @param offsetUs The offset that should be subtracted from {@code positionUs}
* to get the playback position with respect to the media.
* @throws ExoPlaybackException If an error occurs.
*/
@Override
@Override
public
final
void
enable
(
RendererConfiguration
configuration
,
Format
[]
formats
,
public
final
void
enable
(
SampleStream
stream
,
long
positionUs
,
boolean
joining
,
long
offsetUs
)
RendererConfiguration
configuration
,
Format
[]
formats
,
SampleStream
stream
,
long
positionUs
,
boolean
joining
,
boolean
mayRenderStartOfStream
,
long
offsetUs
)
throws
ExoPlaybackException
{
throws
ExoPlaybackException
{
Assertions
.
checkState
(
state
==
STATE_DISABLED
);
Assertions
.
checkState
(
state
==
STATE_DISABLED
);
this
.
configuration
=
configuration
;
this
.
configuration
=
configuration
;
...
@@ -94,18 +85,6 @@ public abstract class NoSampleRenderer implements Renderer, RendererCapabilities
...
@@ -94,18 +85,6 @@ public abstract class NoSampleRenderer implements Renderer, RendererCapabilities
onStarted
();
onStarted
();
}
}
/**
* Replaces the {@link SampleStream} that will be associated with this renderer.
* <p>
* This method may be called when the renderer is in the following states:
* {@link #STATE_ENABLED}, {@link #STATE_STARTED}.
*
* @param formats The enabled formats. Should be empty.
* @param stream The {@link SampleStream} to be associated with this renderer.
* @param offsetUs The offset that should be subtracted from {@code positionUs} in
* {@link #render(long, long)} to get the playback position with respect to the media.
* @throws ExoPlaybackException If an error occurs.
*/
@Override
@Override
public
final
void
replaceStream
(
Format
[]
formats
,
SampleStream
stream
,
long
offsetUs
)
public
final
void
replaceStream
(
Format
[]
formats
,
SampleStream
stream
,
long
offsetUs
)
throws
ExoPlaybackException
{
throws
ExoPlaybackException
{
...
...
library/core/src/main/java/com/google/android/exoplayer2/Renderer.java
View file @
72f4b964
...
@@ -222,21 +222,30 @@ public interface Renderer extends PlayerMessage.Target {
...
@@ -222,21 +222,30 @@ public interface Renderer extends PlayerMessage.Target {
/**
/**
* Enables the renderer to consume from the specified {@link SampleStream}.
* Enables the renderer to consume from the specified {@link SampleStream}.
*
<p>
*
*
This method may be called when the renderer is in the following states:
*
<p>This method may be called when the renderer is in the following states: {@link
*
{@link
#STATE_DISABLED}.
* #STATE_DISABLED}.
*
*
* @param configuration The renderer configuration.
* @param configuration The renderer configuration.
* @param formats The enabled formats.
* @param formats The enabled formats.
* @param stream The {@link SampleStream} from which the renderer should consume.
* @param stream The {@link SampleStream} from which the renderer should consume.
* @param positionUs The player's current position.
* @param positionUs The player's current position.
* @param joining Whether this renderer is being enabled to join an ongoing playback.
* @param joining Whether this renderer is being enabled to join an ongoing playback.
* @param offsetUs The offset to be added to timestamps of buffers read from {@code stream}
* @param mayRenderStartOfStream Whether this renderer is allowed to render the start of the
* before they are rendered.
* stream even if the state is not {@link #STATE_STARTED} yet.
* @param offsetUs The offset to be added to timestamps of buffers read from {@code stream} before
* they are rendered.
* @throws ExoPlaybackException If an error occurs.
* @throws ExoPlaybackException If an error occurs.
*/
*/
void
enable
(
RendererConfiguration
configuration
,
Format
[]
formats
,
SampleStream
stream
,
void
enable
(
long
positionUs
,
boolean
joining
,
long
offsetUs
)
throws
ExoPlaybackException
;
RendererConfiguration
configuration
,
Format
[]
formats
,
SampleStream
stream
,
long
positionUs
,
boolean
joining
,
boolean
mayRenderStartOfStream
,
long
offsetUs
)
throws
ExoPlaybackException
;
/**
/**
* Starts the renderer, meaning that calls to {@link #render(long, long)} will cause media to be
* Starts the renderer, meaning that calls to {@link #render(long, long)} will cause media to be
...
@@ -341,21 +350,32 @@ public interface Renderer extends PlayerMessage.Target {
...
@@ -341,21 +350,32 @@ public interface Renderer extends PlayerMessage.Target {
/**
/**
* Incrementally renders the {@link SampleStream}.
* Incrementally renders the {@link SampleStream}.
* <p>
*
* If the renderer is in the {@link #STATE_ENABLED} state then each call to this method will do
* <p>If the renderer is in the {@link #STATE_ENABLED} state then each call to this method will do
* work toward being ready to render the {@link SampleStream} when the renderer is started. It may
* work toward being ready to render the {@link SampleStream} when the renderer is started. If the
* also render the very start of the media, for example the first frame of a video stream. If the
* renderer is in the {@link #STATE_STARTED} state then calls to this method will render the
* renderer is in the {@link #STATE_STARTED} state then calls to this method will render the
* {@link SampleStream} in sync with the specified media positions.
* {@link SampleStream} in sync with the specified media positions.
* <p>
*
* This method should return quickly, and should not block if the renderer is unable to make
* <p>The renderer may also render the very start of the media at the current position (e.g. the
* first frame of a video stream) while still in the {@link #STATE_ENABLED} state. It's not
* allowed to do that in the following two cases:
*
* <ol>
* <li>The initial start of the media after calling {@link #enable(RendererConfiguration,
* Format[], SampleStream, long, boolean, boolean, long)} with {@code
* mayRenderStartOfStream} set to {@code false}.
* <li>The start of a new stream after calling {@link #replaceStream(Format[], SampleStream,
* long)}.
* </ol>
*
* <p>This method should return quickly, and should not block if the renderer is unable to make
* useful progress.
* useful progress.
* <p>
* This method may be called when the renderer is in the following states:
* {@link #STATE_ENABLED}, {@link #STATE_STARTED}.
*
*
* @param positionUs The current media time in microseconds, measured at the start of the
* <p>This method may be called when the renderer is in the following states: {@link
* current iteration of the rendering loop.
* #STATE_ENABLED}, {@link #STATE_STARTED}.
*
* @param positionUs The current media time in microseconds, measured at the start of the current
* iteration of the rendering loop.
* @param elapsedRealtimeUs {@link android.os.SystemClock#elapsedRealtime()} in microseconds,
* @param elapsedRealtimeUs {@link android.os.SystemClock#elapsedRealtime()} in microseconds,
* measured at the start of the current iteration of the rendering loop.
* measured at the start of the current iteration of the rendering loop.
* @throws ExoPlaybackException If an error occurs.
* @throws ExoPlaybackException If an error occurs.
...
...
library/core/src/main/java/com/google/android/exoplayer2/audio/MediaCodecAudioRenderer.java
View file @
72f4b964
...
@@ -491,8 +491,9 @@ public class MediaCodecAudioRenderer extends MediaCodecRenderer implements Media
...
@@ -491,8 +491,9 @@ public class MediaCodecAudioRenderer extends MediaCodecRenderer implements Media
}
}
@Override
@Override
protected
void
onEnabled
(
boolean
joining
)
throws
ExoPlaybackException
{
protected
void
onEnabled
(
boolean
joining
,
boolean
mayRenderStartOfStream
)
super
.
onEnabled
(
joining
);
throws
ExoPlaybackException
{
super
.
onEnabled
(
joining
,
mayRenderStartOfStream
);
eventDispatcher
.
enabled
(
decoderCounters
);
eventDispatcher
.
enabled
(
decoderCounters
);
int
tunnelingAudioSessionId
=
getConfiguration
().
tunnelingAudioSessionId
;
int
tunnelingAudioSessionId
=
getConfiguration
().
tunnelingAudioSessionId
;
if
(
tunnelingAudioSessionId
!=
C
.
AUDIO_SESSION_ID_UNSET
)
{
if
(
tunnelingAudioSessionId
!=
C
.
AUDIO_SESSION_ID_UNSET
)
{
...
...
library/core/src/main/java/com/google/android/exoplayer2/audio/SimpleDecoderAudioRenderer.java
View file @
72f4b964
...
@@ -495,7 +495,8 @@ public abstract class SimpleDecoderAudioRenderer extends BaseRenderer implements
...
@@ -495,7 +495,8 @@ public abstract class SimpleDecoderAudioRenderer extends BaseRenderer implements
}
}
@Override
@Override
protected
void
onEnabled
(
boolean
joining
)
throws
ExoPlaybackException
{
protected
void
onEnabled
(
boolean
joining
,
boolean
mayRenderStartOfStream
)
throws
ExoPlaybackException
{
decoderCounters
=
new
DecoderCounters
();
decoderCounters
=
new
DecoderCounters
();
eventDispatcher
.
enabled
(
decoderCounters
);
eventDispatcher
.
enabled
(
decoderCounters
);
int
tunnelingAudioSessionId
=
getConfiguration
().
tunnelingAudioSessionId
;
int
tunnelingAudioSessionId
=
getConfiguration
().
tunnelingAudioSessionId
;
...
...
library/core/src/main/java/com/google/android/exoplayer2/mediacodec/MediaCodecRenderer.java
View file @
72f4b964
...
@@ -679,7 +679,8 @@ public abstract class MediaCodecRenderer extends BaseRenderer {
...
@@ -679,7 +679,8 @@ public abstract class MediaCodecRenderer extends BaseRenderer {
}
}
@Override
@Override
protected
void
onEnabled
(
boolean
joining
)
throws
ExoPlaybackException
{
protected
void
onEnabled
(
boolean
joining
,
boolean
mayRenderStartOfStream
)
throws
ExoPlaybackException
{
decoderCounters
=
new
DecoderCounters
();
decoderCounters
=
new
DecoderCounters
();
}
}
...
...
library/core/src/main/java/com/google/android/exoplayer2/video/MediaCodecVideoRenderer.java
View file @
72f4b964
...
@@ -129,7 +129,9 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer {
...
@@ -129,7 +129,9 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer {
private
Surface
surface
;
private
Surface
surface
;
private
Surface
dummySurface
;
private
Surface
dummySurface
;
@VideoScalingMode
private
int
scalingMode
;
@VideoScalingMode
private
int
scalingMode
;
private
boolean
renderedFirstFrame
;
private
boolean
renderedFirstFrameAfterReset
;
private
boolean
mayRenderFirstFrameAfterEnableIfNotStarted
;
private
boolean
renderedFirstFrameAfterEnable
;
private
long
initialPositionUs
;
private
long
initialPositionUs
;
private
long
joiningDeadlineMs
;
private
long
joiningDeadlineMs
;
private
long
droppedFrameAccumulationStartTimeMs
;
private
long
droppedFrameAccumulationStartTimeMs
;
...
@@ -360,8 +362,9 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer {
...
@@ -360,8 +362,9 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer {
}
}
@Override
@Override
protected
void
onEnabled
(
boolean
joining
)
throws
ExoPlaybackException
{
protected
void
onEnabled
(
boolean
joining
,
boolean
mayRenderStartOfStream
)
super
.
onEnabled
(
joining
);
throws
ExoPlaybackException
{
super
.
onEnabled
(
joining
,
mayRenderStartOfStream
);
int
oldTunnelingAudioSessionId
=
tunnelingAudioSessionId
;
int
oldTunnelingAudioSessionId
=
tunnelingAudioSessionId
;
tunnelingAudioSessionId
=
getConfiguration
().
tunnelingAudioSessionId
;
tunnelingAudioSessionId
=
getConfiguration
().
tunnelingAudioSessionId
;
tunneling
=
tunnelingAudioSessionId
!=
C
.
AUDIO_SESSION_ID_UNSET
;
tunneling
=
tunnelingAudioSessionId
!=
C
.
AUDIO_SESSION_ID_UNSET
;
...
@@ -370,6 +373,8 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer {
...
@@ -370,6 +373,8 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer {
}
}
eventDispatcher
.
enabled
(
decoderCounters
);
eventDispatcher
.
enabled
(
decoderCounters
);
frameReleaseTimeHelper
.
enable
();
frameReleaseTimeHelper
.
enable
();
mayRenderFirstFrameAfterEnableIfNotStarted
=
mayRenderStartOfStream
;
renderedFirstFrameAfterEnable
=
false
;
}
}
@Override
@Override
...
@@ -387,8 +392,11 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer {
...
@@ -387,8 +392,11 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer {
@Override
@Override
public
boolean
isReady
()
{
public
boolean
isReady
()
{
if
(
super
.
isReady
()
&&
(
renderedFirstFrame
||
(
dummySurface
!=
null
&&
surface
==
dummySurface
)
if
(
super
.
isReady
()
||
getCodec
()
==
null
||
tunneling
))
{
&&
(
renderedFirstFrameAfterReset
||
(
dummySurface
!=
null
&&
surface
==
dummySurface
)
||
getCodec
()
==
null
||
tunneling
))
{
// Ready. If we were joining then we've now joined, so clear the joining deadline.
// Ready. If we were joining then we've now joined, so clear the joining deadline.
joiningDeadlineMs
=
C
.
TIME_UNSET
;
joiningDeadlineMs
=
C
.
TIME_UNSET
;
return
true
;
return
true
;
...
@@ -729,11 +737,15 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer {
...
@@ -729,11 +737,15 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer {
long
elapsedRealtimeNowUs
=
SystemClock
.
elapsedRealtime
()
*
1000
;
long
elapsedRealtimeNowUs
=
SystemClock
.
elapsedRealtime
()
*
1000
;
long
elapsedSinceLastRenderUs
=
elapsedRealtimeNowUs
-
lastRenderTimeUs
;
long
elapsedSinceLastRenderUs
=
elapsedRealtimeNowUs
-
lastRenderTimeUs
;
boolean
isStarted
=
getState
()
==
STATE_STARTED
;
boolean
isStarted
=
getState
()
==
STATE_STARTED
;
boolean
shouldRenderFirstFrame
=
!
renderedFirstFrameAfterEnable
?
(
isStarted
||
mayRenderFirstFrameAfterEnableIfNotStarted
)
:
!
renderedFirstFrameAfterReset
;
// Don't force output until we joined and the position reached the current stream.
// Don't force output until we joined and the position reached the current stream.
boolean
forceRenderOutputBuffer
=
boolean
forceRenderOutputBuffer
=
joiningDeadlineMs
==
C
.
TIME_UNSET
joiningDeadlineMs
==
C
.
TIME_UNSET
&&
positionUs
>=
outputStreamOffsetUs
&&
positionUs
>=
outputStreamOffsetUs
&&
(
!
rendered
FirstFrame
&&
(
shouldRender
FirstFrame
||
(
isStarted
&&
shouldForceRenderOutputBuffer
(
earlyUs
,
elapsedSinceLastRenderUs
)));
||
(
isStarted
&&
shouldForceRenderOutputBuffer
(
earlyUs
,
elapsedSinceLastRenderUs
)));
if
(
forceRenderOutputBuffer
)
{
if
(
forceRenderOutputBuffer
)
{
long
releaseTimeNs
=
System
.
nanoTime
();
long
releaseTimeNs
=
System
.
nanoTime
();
...
@@ -1056,7 +1068,7 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer {
...
@@ -1056,7 +1068,7 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer {
}
}
private
void
clearRenderedFirstFrame
()
{
private
void
clearRenderedFirstFrame
()
{
renderedFirstFrame
=
false
;
renderedFirstFrame
AfterReset
=
false
;
// The first frame notification is triggered by renderOutputBuffer or renderOutputBufferV21 for
// The first frame notification is triggered by renderOutputBuffer or renderOutputBufferV21 for
// non-tunneled playback, onQueueInputBuffer for tunneled playback prior to API level 23, and
// non-tunneled playback, onQueueInputBuffer for tunneled playback prior to API level 23, and
// OnFrameRenderedListenerV23.onFrameRenderedListener for tunneled playback on API level 23 and
// OnFrameRenderedListenerV23.onFrameRenderedListener for tunneled playback on API level 23 and
...
@@ -1071,14 +1083,15 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer {
...
@@ -1071,14 +1083,15 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer {
}
}
/* package */
void
maybeNotifyRenderedFirstFrame
()
{
/* package */
void
maybeNotifyRenderedFirstFrame
()
{
if
(!
renderedFirstFrame
)
{
renderedFirstFrameAfterEnable
=
true
;
renderedFirstFrame
=
true
;
if
(!
renderedFirstFrameAfterReset
)
{
renderedFirstFrameAfterReset
=
true
;
eventDispatcher
.
renderedFirstFrame
(
surface
);
eventDispatcher
.
renderedFirstFrame
(
surface
);
}
}
}
}
private
void
maybeRenotifyRenderedFirstFrame
()
{
private
void
maybeRenotifyRenderedFirstFrame
()
{
if
(
renderedFirstFrame
)
{
if
(
renderedFirstFrame
AfterReset
)
{
eventDispatcher
.
renderedFirstFrame
(
surface
);
eventDispatcher
.
renderedFirstFrame
(
surface
);
}
}
}
}
...
...
library/core/src/main/java/com/google/android/exoplayer2/video/SimpleDecoderVideoRenderer.java
View file @
72f4b964
...
@@ -92,7 +92,9 @@ public abstract class SimpleDecoderVideoRenderer extends BaseRenderer {
...
@@ -92,7 +92,9 @@ public abstract class SimpleDecoderVideoRenderer extends BaseRenderer {
@ReinitializationState
private
int
decoderReinitializationState
;
@ReinitializationState
private
int
decoderReinitializationState
;
private
boolean
decoderReceivedBuffers
;
private
boolean
decoderReceivedBuffers
;
private
boolean
renderedFirstFrame
;
private
boolean
renderedFirstFrameAfterReset
;
private
boolean
mayRenderFirstFrameAfterEnableIfNotStarted
;
private
boolean
renderedFirstFrameAfterEnable
;
private
long
initialPositionUs
;
private
long
initialPositionUs
;
private
long
joiningDeadlineMs
;
private
long
joiningDeadlineMs
;
private
boolean
waitingForKeys
;
private
boolean
waitingForKeys
;
...
@@ -195,7 +197,7 @@ public abstract class SimpleDecoderVideoRenderer extends BaseRenderer {
...
@@ -195,7 +197,7 @@ public abstract class SimpleDecoderVideoRenderer extends BaseRenderer {
}
}
if
(
inputFormat
!=
null
if
(
inputFormat
!=
null
&&
(
isSourceReady
()
||
outputBuffer
!=
null
)
&&
(
isSourceReady
()
||
outputBuffer
!=
null
)
&&
(
renderedFirstFrame
||
!
hasOutput
()))
{
&&
(
renderedFirstFrame
AfterReset
||
!
hasOutput
()))
{
// Ready. If we were joining then we've now joined, so clear the joining deadline.
// Ready. If we were joining then we've now joined, so clear the joining deadline.
joiningDeadlineMs
=
C
.
TIME_UNSET
;
joiningDeadlineMs
=
C
.
TIME_UNSET
;
return
true
;
return
true
;
...
@@ -215,9 +217,12 @@ public abstract class SimpleDecoderVideoRenderer extends BaseRenderer {
...
@@ -215,9 +217,12 @@ public abstract class SimpleDecoderVideoRenderer extends BaseRenderer {
// Protected methods.
// Protected methods.
@Override
@Override
protected
void
onEnabled
(
boolean
joining
)
throws
ExoPlaybackException
{
protected
void
onEnabled
(
boolean
joining
,
boolean
mayRenderStartOfStream
)
throws
ExoPlaybackException
{
decoderCounters
=
new
DecoderCounters
();
decoderCounters
=
new
DecoderCounters
();
eventDispatcher
.
enabled
(
decoderCounters
);
eventDispatcher
.
enabled
(
decoderCounters
);
mayRenderFirstFrameAfterEnableIfNotStarted
=
mayRenderStartOfStream
;
renderedFirstFrameAfterEnable
=
false
;
}
}
@Override
@Override
...
@@ -267,6 +272,9 @@ public abstract class SimpleDecoderVideoRenderer extends BaseRenderer {
...
@@ -267,6 +272,9 @@ public abstract class SimpleDecoderVideoRenderer extends BaseRenderer {
@Override
@Override
protected
void
onStreamChanged
(
Format
[]
formats
,
long
offsetUs
)
throws
ExoPlaybackException
{
protected
void
onStreamChanged
(
Format
[]
formats
,
long
offsetUs
)
throws
ExoPlaybackException
{
// TODO: This shouldn't just update the output stream offset as long as there are still buffers
// of the previous stream in the decoder. It should also make sure to render the first frame of
// the next stream if the playback position reached the new stream and the renderer is started.
outputStreamOffsetUs
=
offsetUs
;
outputStreamOffsetUs
=
offsetUs
;
super
.
onStreamChanged
(
formats
,
offsetUs
);
super
.
onStreamChanged
(
formats
,
offsetUs
);
}
}
...
@@ -787,10 +795,15 @@ public abstract class SimpleDecoderVideoRenderer extends BaseRenderer {
...
@@ -787,10 +795,15 @@ public abstract class SimpleDecoderVideoRenderer extends BaseRenderer {
}
}
long
elapsedRealtimeNowUs
=
SystemClock
.
elapsedRealtime
()
*
1000
;
long
elapsedRealtimeNowUs
=
SystemClock
.
elapsedRealtime
()
*
1000
;
long
elapsedSinceLastRenderUs
=
elapsedRealtimeNowUs
-
lastRenderTimeUs
;
boolean
isStarted
=
getState
()
==
STATE_STARTED
;
boolean
isStarted
=
getState
()
==
STATE_STARTED
;
if
(!
renderedFirstFrame
boolean
shouldRenderFirstFrame
=
||
(
isStarted
!
renderedFirstFrameAfterEnable
&&
shouldForceRenderOutputBuffer
(
earlyUs
,
elapsedRealtimeNowUs
-
lastRenderTimeUs
)))
{
?
(
isStarted
||
mayRenderFirstFrameAfterEnableIfNotStarted
)
:
!
renderedFirstFrameAfterReset
;
// TODO: We shouldn't force render while we are joining an ongoing playback.
if
(
shouldRenderFirstFrame
||
(
isStarted
&&
shouldForceRenderOutputBuffer
(
earlyUs
,
elapsedSinceLastRenderUs
)))
{
renderOutputBuffer
(
outputBuffer
,
presentationTimeUs
,
outputFormat
);
renderOutputBuffer
(
outputBuffer
,
presentationTimeUs
,
outputFormat
);
return
true
;
return
true
;
}
}
...
@@ -799,6 +812,7 @@ public abstract class SimpleDecoderVideoRenderer extends BaseRenderer {
...
@@ -799,6 +812,7 @@ public abstract class SimpleDecoderVideoRenderer extends BaseRenderer {
return
false
;
return
false
;
}
}
// TODO: Treat dropped buffers as skipped while we are joining an ongoing playback.
if
(
shouldDropBuffersToKeyframe
(
earlyUs
,
elapsedRealtimeUs
)
if
(
shouldDropBuffersToKeyframe
(
earlyUs
,
elapsedRealtimeUs
)
&&
maybeDropBuffersToKeyframe
(
positionUs
))
{
&&
maybeDropBuffersToKeyframe
(
positionUs
))
{
return
false
;
return
false
;
...
@@ -862,18 +876,19 @@ public abstract class SimpleDecoderVideoRenderer extends BaseRenderer {
...
@@ -862,18 +876,19 @@ public abstract class SimpleDecoderVideoRenderer extends BaseRenderer {
}
}
private
void
clearRenderedFirstFrame
()
{
private
void
clearRenderedFirstFrame
()
{
renderedFirstFrame
=
false
;
renderedFirstFrame
AfterReset
=
false
;
}
}
private
void
maybeNotifyRenderedFirstFrame
()
{
private
void
maybeNotifyRenderedFirstFrame
()
{
if
(!
renderedFirstFrame
)
{
renderedFirstFrameAfterEnable
=
true
;
renderedFirstFrame
=
true
;
if
(!
renderedFirstFrameAfterReset
)
{
renderedFirstFrameAfterReset
=
true
;
eventDispatcher
.
renderedFirstFrame
(
surface
);
eventDispatcher
.
renderedFirstFrame
(
surface
);
}
}
}
}
private
void
maybeRenotifyRenderedFirstFrame
()
{
private
void
maybeRenotifyRenderedFirstFrame
()
{
if
(
renderedFirstFrame
)
{
if
(
renderedFirstFrame
AfterReset
)
{
eventDispatcher
.
renderedFirstFrame
(
surface
);
eventDispatcher
.
renderedFirstFrame
(
surface
);
}
}
}
}
...
...
library/core/src/test/java/com/google/android/exoplayer2/ExoPlayerTest.java
View file @
72f4b964
...
@@ -5606,7 +5606,8 @@ public final class ExoPlayerTest {
...
@@ -5606,7 +5606,8 @@ public final class ExoPlayerTest {
FakeRenderer
audioRenderer
=
FakeRenderer
audioRenderer
=
new
FakeRenderer
(
Builder
.
AUDIO_FORMAT
)
{
new
FakeRenderer
(
Builder
.
AUDIO_FORMAT
)
{
@Override
@Override
protected
void
onEnabled
(
boolean
joining
)
throws
ExoPlaybackException
{
protected
void
onEnabled
(
boolean
joining
,
boolean
mayRenderStartOfStream
)
throws
ExoPlaybackException
{
// Fail when enabling the renderer. This will happen during the period transition.
// Fail when enabling the renderer. This will happen during the period transition.
throw
createRendererException
(
new
IllegalStateException
(),
Builder
.
AUDIO_FORMAT
);
throw
createRendererException
(
new
IllegalStateException
(),
Builder
.
AUDIO_FORMAT
);
}
}
...
@@ -5672,7 +5673,8 @@ public final class ExoPlayerTest {
...
@@ -5672,7 +5673,8 @@ public final class ExoPlayerTest {
FakeRenderer
audioRenderer
=
FakeRenderer
audioRenderer
=
new
FakeRenderer
(
Builder
.
AUDIO_FORMAT
)
{
new
FakeRenderer
(
Builder
.
AUDIO_FORMAT
)
{
@Override
@Override
protected
void
onEnabled
(
boolean
joining
)
throws
ExoPlaybackException
{
protected
void
onEnabled
(
boolean
joining
,
boolean
mayRenderStartOfStream
)
throws
ExoPlaybackException
{
// Fail when enabling the renderer. This will happen during the playlist update.
// Fail when enabling the renderer. This will happen during the playlist update.
throw
createRendererException
(
new
IllegalStateException
(),
Builder
.
AUDIO_FORMAT
);
throw
createRendererException
(
new
IllegalStateException
(),
Builder
.
AUDIO_FORMAT
);
}
}
...
...
library/core/src/test/java/com/google/android/exoplayer2/analytics/AnalyticsCollectorTest.java
View file @
72f4b964
...
@@ -241,8 +241,8 @@ public final class AnalyticsCollectorTest {
...
@@ -241,8 +241,8 @@ public final class AnalyticsCollectorTest {
period0
/* audio */
,
period0
/* video */
,
period1
/* audio */
,
period1
/* video */
);
period0
/* audio */
,
period0
/* video */
,
period1
/* audio */
,
period1
/* video */
);
assertThat
(
listener
.
getEvents
(
EVENT_AUDIO_SESSION_ID
)).
containsExactly
(
period0
);
assertThat
(
listener
.
getEvents
(
EVENT_AUDIO_SESSION_ID
)).
containsExactly
(
period0
);
assertThat
(
listener
.
getEvents
(
EVENT_DROPPED_VIDEO_FRAMES
)).
containsExactly
(
period1
);
assertThat
(
listener
.
getEvents
(
EVENT_DROPPED_VIDEO_FRAMES
)).
containsExactly
(
period1
);
assertThat
(
listener
.
getEvents
(
EVENT_VIDEO_SIZE_CHANGED
)).
containsExactly
(
period0
);
assertThat
(
listener
.
getEvents
(
EVENT_VIDEO_SIZE_CHANGED
)).
containsExactly
(
period0
,
period1
);
assertThat
(
listener
.
getEvents
(
EVENT_RENDERED_FIRST_FRAME
)).
containsExactly
(
period0
);
assertThat
(
listener
.
getEvents
(
EVENT_RENDERED_FIRST_FRAME
)).
containsExactly
(
period0
,
period1
);
assertThat
(
listener
.
getEvents
(
EVENT_VIDEO_FRAME_PROCESSING_OFFSET
)).
containsExactly
(
period1
);
assertThat
(
listener
.
getEvents
(
EVENT_VIDEO_FRAME_PROCESSING_OFFSET
)).
containsExactly
(
period1
);
listener
.
assertNoMoreEvents
();
listener
.
assertNoMoreEvents
();
}
}
...
@@ -444,8 +444,10 @@ public final class AnalyticsCollectorTest {
...
@@ -444,8 +444,10 @@ public final class AnalyticsCollectorTest {
assertThat
(
listener
.
getEvents
(
EVENT_AUDIO_SESSION_ID
)).
containsExactly
(
period1Seq2
);
assertThat
(
listener
.
getEvents
(
EVENT_AUDIO_SESSION_ID
)).
containsExactly
(
period1Seq2
);
assertThat
(
listener
.
getEvents
(
EVENT_DROPPED_VIDEO_FRAMES
))
assertThat
(
listener
.
getEvents
(
EVENT_DROPPED_VIDEO_FRAMES
))
.
containsExactly
(
period0
,
period1Seq2
,
period1Seq2
);
.
containsExactly
(
period0
,
period1Seq2
,
period1Seq2
);
assertThat
(
listener
.
getEvents
(
EVENT_VIDEO_SIZE_CHANGED
)).
containsExactly
(
period0
,
period0
);
assertThat
(
listener
.
getEvents
(
EVENT_VIDEO_SIZE_CHANGED
))
assertThat
(
listener
.
getEvents
(
EVENT_RENDERED_FIRST_FRAME
)).
containsExactly
(
period0
,
period0
);
.
containsExactly
(
period0
,
period1Seq1
,
period0
,
period1Seq2
);
assertThat
(
listener
.
getEvents
(
EVENT_RENDERED_FIRST_FRAME
))
.
containsExactly
(
period0
,
period1Seq1
,
period0
,
period1Seq2
);
assertThat
(
listener
.
getEvents
(
EVENT_VIDEO_FRAME_PROCESSING_OFFSET
))
assertThat
(
listener
.
getEvents
(
EVENT_VIDEO_FRAME_PROCESSING_OFFSET
))
.
containsExactly
(
period0
,
period1Seq2
,
period1Seq2
);
.
containsExactly
(
period0
,
period1Seq2
,
period1Seq2
);
listener
.
assertNoMoreEvents
();
listener
.
assertNoMoreEvents
();
...
@@ -672,9 +674,9 @@ public final class AnalyticsCollectorTest {
...
@@ -672,9 +674,9 @@ public final class AnalyticsCollectorTest {
assertThat
(
listener
.
getEvents
(
EVENT_DECODER_DISABLED
)).
containsExactly
(
window0Period1Seq0
);
assertThat
(
listener
.
getEvents
(
EVENT_DECODER_DISABLED
)).
containsExactly
(
window0Period1Seq0
);
assertThat
(
listener
.
getEvents
(
EVENT_DROPPED_VIDEO_FRAMES
)).
containsExactly
(
window0Period1Seq0
);
assertThat
(
listener
.
getEvents
(
EVENT_DROPPED_VIDEO_FRAMES
)).
containsExactly
(
window0Period1Seq0
);
assertThat
(
listener
.
getEvents
(
EVENT_VIDEO_SIZE_CHANGED
))
assertThat
(
listener
.
getEvents
(
EVENT_VIDEO_SIZE_CHANGED
))
.
containsExactly
(
window0Period1Seq0
,
period1Seq0
);
.
containsExactly
(
window0Period1Seq0
,
window1Period0Seq1
,
period1Seq0
);
assertThat
(
listener
.
getEvents
(
EVENT_RENDERED_FIRST_FRAME
))
assertThat
(
listener
.
getEvents
(
EVENT_RENDERED_FIRST_FRAME
))
.
containsExactly
(
window0Period1Seq0
,
period1Seq0
);
.
containsExactly
(
window0Period1Seq0
,
window1Period0Seq1
,
period1Seq0
);
assertThat
(
listener
.
getEvents
(
EVENT_VIDEO_FRAME_PROCESSING_OFFSET
))
assertThat
(
listener
.
getEvents
(
EVENT_VIDEO_FRAME_PROCESSING_OFFSET
))
.
containsExactly
(
window0Period1Seq0
);
.
containsExactly
(
window0Period1Seq0
);
listener
.
assertNoMoreEvents
();
listener
.
assertNoMoreEvents
();
...
@@ -964,8 +966,22 @@ public final class AnalyticsCollectorTest {
...
@@ -964,8 +966,22 @@ public final class AnalyticsCollectorTest {
contentAfterPostroll
);
contentAfterPostroll
);
assertThat
(
listener
.
getEvents
(
EVENT_DROPPED_VIDEO_FRAMES
))
assertThat
(
listener
.
getEvents
(
EVENT_DROPPED_VIDEO_FRAMES
))
.
containsExactly
(
contentAfterPreroll
,
contentAfterMidroll
,
contentAfterPostroll
);
.
containsExactly
(
contentAfterPreroll
,
contentAfterMidroll
,
contentAfterPostroll
);
assertThat
(
listener
.
getEvents
(
EVENT_VIDEO_SIZE_CHANGED
)).
containsExactly
(
prerollAd
);
assertThat
(
listener
.
getEvents
(
EVENT_VIDEO_SIZE_CHANGED
))
assertThat
(
listener
.
getEvents
(
EVENT_RENDERED_FIRST_FRAME
)).
containsExactly
(
prerollAd
);
.
containsExactly
(
prerollAd
,
contentAfterPreroll
,
midrollAd
,
contentAfterMidroll
,
postrollAd
,
contentAfterPostroll
);
assertThat
(
listener
.
getEvents
(
EVENT_RENDERED_FIRST_FRAME
))
.
containsExactly
(
prerollAd
,
contentAfterPreroll
,
midrollAd
,
contentAfterMidroll
,
postrollAd
,
contentAfterPostroll
);
assertThat
(
listener
.
getEvents
(
EVENT_VIDEO_FRAME_PROCESSING_OFFSET
))
assertThat
(
listener
.
getEvents
(
EVENT_VIDEO_FRAME_PROCESSING_OFFSET
))
.
containsExactly
(
contentAfterPreroll
,
contentAfterMidroll
,
contentAfterPostroll
);
.
containsExactly
(
contentAfterPreroll
,
contentAfterMidroll
,
contentAfterPostroll
);
listener
.
assertNoMoreEvents
();
listener
.
assertNoMoreEvents
();
...
@@ -1082,9 +1098,9 @@ public final class AnalyticsCollectorTest {
...
@@ -1082,9 +1098,9 @@ public final class AnalyticsCollectorTest {
assertThat
(
listener
.
getEvents
(
EVENT_DECODER_DISABLED
)).
containsExactly
(
contentBeforeMidroll
);
assertThat
(
listener
.
getEvents
(
EVENT_DECODER_DISABLED
)).
containsExactly
(
contentBeforeMidroll
);
assertThat
(
listener
.
getEvents
(
EVENT_DROPPED_VIDEO_FRAMES
)).
containsExactly
(
contentAfterMidroll
);
assertThat
(
listener
.
getEvents
(
EVENT_DROPPED_VIDEO_FRAMES
)).
containsExactly
(
contentAfterMidroll
);
assertThat
(
listener
.
getEvents
(
EVENT_VIDEO_SIZE_CHANGED
))
assertThat
(
listener
.
getEvents
(
EVENT_VIDEO_SIZE_CHANGED
))
.
containsExactly
(
contentBeforeMidroll
,
midrollAd
);
.
containsExactly
(
contentBeforeMidroll
,
midrollAd
,
contentAfterMidroll
);
assertThat
(
listener
.
getEvents
(
EVENT_RENDERED_FIRST_FRAME
))
assertThat
(
listener
.
getEvents
(
EVENT_RENDERED_FIRST_FRAME
))
.
containsExactly
(
contentBeforeMidroll
,
midrollAd
);
.
containsExactly
(
contentBeforeMidroll
,
midrollAd
,
contentAfterMidroll
);
assertThat
(
listener
.
getEvents
(
EVENT_VIDEO_FRAME_PROCESSING_OFFSET
))
assertThat
(
listener
.
getEvents
(
EVENT_VIDEO_FRAME_PROCESSING_OFFSET
))
.
containsExactly
(
contentAfterMidroll
);
.
containsExactly
(
contentAfterMidroll
);
listener
.
assertNoMoreEvents
();
listener
.
assertNoMoreEvents
();
...
@@ -1194,7 +1210,10 @@ public final class AnalyticsCollectorTest {
...
@@ -1194,7 +1210,10 @@ public final class AnalyticsCollectorTest {
private
final
VideoRendererEventListener
.
EventDispatcher
eventDispatcher
;
private
final
VideoRendererEventListener
.
EventDispatcher
eventDispatcher
;
private
final
DecoderCounters
decoderCounters
;
private
final
DecoderCounters
decoderCounters
;
private
Format
format
;
private
Format
format
;
private
boolean
renderedFirstFrame
;
private
long
streamOffsetUs
;
private
boolean
renderedFirstFrameAfterReset
;
private
boolean
mayRenderFirstFrameAfterStreamChangeIfNotStarted
;
private
boolean
renderedFirstFrameAfterStreamChange
;
public
FakeVideoRenderer
(
Handler
handler
,
VideoRendererEventListener
eventListener
)
{
public
FakeVideoRenderer
(
Handler
handler
,
VideoRendererEventListener
eventListener
)
{
super
(
ExoPlayerTestRunner
.
Builder
.
VIDEO_FORMAT
);
super
(
ExoPlayerTestRunner
.
Builder
.
VIDEO_FORMAT
);
...
@@ -1203,10 +1222,23 @@ public final class AnalyticsCollectorTest {
...
@@ -1203,10 +1222,23 @@ public final class AnalyticsCollectorTest {
}
}
@Override
@Override
protected
void
onEnabled
(
boolean
joining
)
throws
ExoPlaybackException
{
protected
void
onEnabled
(
boolean
joining
,
boolean
mayRenderStartOfStream
)
super
.
onEnabled
(
joining
);
throws
ExoPlaybackException
{
super
.
onEnabled
(
joining
,
mayRenderStartOfStream
);
eventDispatcher
.
enabled
(
decoderCounters
);
eventDispatcher
.
enabled
(
decoderCounters
);
renderedFirstFrame
=
false
;
mayRenderFirstFrameAfterStreamChangeIfNotStarted
=
mayRenderStartOfStream
;
renderedFirstFrameAfterStreamChange
=
false
;
}
@Override
protected
void
onStreamChanged
(
Format
[]
formats
,
long
offsetUs
)
throws
ExoPlaybackException
{
super
.
onStreamChanged
(
formats
,
offsetUs
);
streamOffsetUs
=
offsetUs
;
if
(
renderedFirstFrameAfterReset
)
{
renderedFirstFrameAfterReset
=
false
;
renderedFirstFrameAfterStreamChange
=
false
;
mayRenderFirstFrameAfterStreamChangeIfNotStarted
=
false
;
}
}
}
@Override
@Override
...
@@ -1226,7 +1258,7 @@ public final class AnalyticsCollectorTest {
...
@@ -1226,7 +1258,7 @@ public final class AnalyticsCollectorTest {
@Override
@Override
protected
void
onPositionReset
(
long
positionUs
,
boolean
joining
)
throws
ExoPlaybackException
{
protected
void
onPositionReset
(
long
positionUs
,
boolean
joining
)
throws
ExoPlaybackException
{
super
.
onPositionReset
(
positionUs
,
joining
);
super
.
onPositionReset
(
positionUs
,
joining
);
renderedFirstFrame
=
false
;
renderedFirstFrame
AfterReset
=
false
;
}
}
@Override
@Override
...
@@ -1242,11 +1274,18 @@ public final class AnalyticsCollectorTest {
...
@@ -1242,11 +1274,18 @@ public final class AnalyticsCollectorTest {
@Override
@Override
protected
boolean
shouldProcessBuffer
(
long
bufferTimeUs
,
long
playbackPositionUs
)
{
protected
boolean
shouldProcessBuffer
(
long
bufferTimeUs
,
long
playbackPositionUs
)
{
boolean
shouldProcess
=
super
.
shouldProcessBuffer
(
bufferTimeUs
,
playbackPositionUs
);
boolean
shouldProcess
=
super
.
shouldProcessBuffer
(
bufferTimeUs
,
playbackPositionUs
);
if
(
shouldProcess
&&
!
renderedFirstFrame
)
{
boolean
shouldRenderFirstFrame
=
!
renderedFirstFrameAfterStreamChange
?
(
getState
()
==
Renderer
.
STATE_STARTED
||
mayRenderFirstFrameAfterStreamChangeIfNotStarted
)
:
!
renderedFirstFrameAfterReset
;
shouldProcess
|=
shouldRenderFirstFrame
&&
playbackPositionUs
>=
streamOffsetUs
;
if
(
shouldProcess
&&
!
renderedFirstFrameAfterReset
)
{
eventDispatcher
.
videoSizeChanged
(
eventDispatcher
.
videoSizeChanged
(
format
.
width
,
format
.
height
,
format
.
rotationDegrees
,
format
.
pixelWidthHeightRatio
);
format
.
width
,
format
.
height
,
format
.
rotationDegrees
,
format
.
pixelWidthHeightRatio
);
eventDispatcher
.
renderedFirstFrame
(
/* surface= */
null
);
eventDispatcher
.
renderedFirstFrame
(
/* surface= */
null
);
renderedFirstFrame
=
true
;
renderedFirstFrameAfterReset
=
true
;
renderedFirstFrameAfterStreamChange
=
true
;
}
}
return
shouldProcess
;
return
shouldProcess
;
}
}
...
@@ -1265,8 +1304,9 @@ public final class AnalyticsCollectorTest {
...
@@ -1265,8 +1304,9 @@ public final class AnalyticsCollectorTest {
}
}
@Override
@Override
protected
void
onEnabled
(
boolean
joining
)
throws
ExoPlaybackException
{
protected
void
onEnabled
(
boolean
joining
,
boolean
mayRenderStartOfStream
)
super
.
onEnabled
(
joining
);
throws
ExoPlaybackException
{
super
.
onEnabled
(
joining
,
mayRenderStartOfStream
);
eventDispatcher
.
enabled
(
decoderCounters
);
eventDispatcher
.
enabled
(
decoderCounters
);
notifiedAudioSessionId
=
false
;
notifiedAudioSessionId
=
false
;
}
}
...
...
library/core/src/test/java/com/google/android/exoplayer2/audio/SimpleDecoderAudioRendererTest.java
View file @
72f4b964
...
@@ -97,9 +97,10 @@ public class SimpleDecoderAudioRendererTest {
...
@@ -97,9 +97,10 @@ public class SimpleDecoderAudioRendererTest {
RendererConfiguration
.
DEFAULT
,
RendererConfiguration
.
DEFAULT
,
new
Format
[]
{
FORMAT
},
new
Format
[]
{
FORMAT
},
new
FakeSampleStream
(
FORMAT
,
/* eventDispatcher= */
null
,
/* shouldOutputSample= */
false
),
new
FakeSampleStream
(
FORMAT
,
/* eventDispatcher= */
null
,
/* shouldOutputSample= */
false
),
0
,
/* positionUs= */
0
,
false
,
/* joining= */
false
,
0
);
/* mayRenderStartOfStream= */
true
,
/* offsetUs= */
0
);
audioRenderer
.
setCurrentStreamFinal
();
audioRenderer
.
setCurrentStreamFinal
();
when
(
mockAudioSink
.
isEnded
()).
thenReturn
(
true
);
when
(
mockAudioSink
.
isEnded
()).
thenReturn
(
true
);
while
(!
audioRenderer
.
isEnded
())
{
while
(!
audioRenderer
.
isEnded
())
{
...
...
library/core/src/test/java/com/google/android/exoplayer2/video/SimpleDecoderVideoRendererTest.java
0 → 100644
View file @
72f4b964
/*
* Copyright (C) 2020 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
.
exoplayer2
.
video
;
import
static
org
.
mockito
.
ArgumentMatchers
.
any
;
import
static
org
.
mockito
.
Mockito
.
never
;
import
static
org
.
mockito
.
Mockito
.
times
;
import
static
org
.
mockito
.
Mockito
.
verify
;
import
android.graphics.SurfaceTexture
;
import
android.os.Handler
;
import
android.os.SystemClock
;
import
android.view.Surface
;
import
androidx.annotation.Nullable
;
import
androidx.test.ext.junit.runners.AndroidJUnit4
;
import
com.google.android.exoplayer2.C
;
import
com.google.android.exoplayer2.Format
;
import
com.google.android.exoplayer2.RendererCapabilities
;
import
com.google.android.exoplayer2.RendererConfiguration
;
import
com.google.android.exoplayer2.decoder.SimpleDecoder
;
import
com.google.android.exoplayer2.drm.ExoMediaCrypto
;
import
com.google.android.exoplayer2.testutil.FakeSampleStream
;
import
com.google.android.exoplayer2.testutil.FakeSampleStream.FakeSampleStreamItem
;
import
com.google.android.exoplayer2.util.MimeTypes
;
import
org.junit.Before
;
import
org.junit.Ignore
;
import
org.junit.Rule
;
import
org.junit.Test
;
import
org.junit.runner.RunWith
;
import
org.mockito.Mock
;
import
org.mockito.junit.MockitoJUnit
;
import
org.mockito.junit.MockitoRule
;
/** Unit test for {@link SimpleDecoderVideoRenderer}. */
@RunWith
(
AndroidJUnit4
.
class
)
public
final
class
SimpleDecoderVideoRendererTest
{
@Rule
public
final
MockitoRule
mockito
=
MockitoJUnit
.
rule
();
private
static
final
Format
BASIC_MP4_1080
=
Format
.
createVideoSampleFormat
(
/* id= */
null
,
/* sampleMimeType= */
MimeTypes
.
VIDEO_MP4
,
/* codecs= */
null
,
/* bitrate= */
Format
.
NO_VALUE
,
/* maxInputSize= */
Format
.
NO_VALUE
,
/* width= */
1920
,
/* height= */
1080
,
/* frameRate= */
Format
.
NO_VALUE
,
/* initializationData= */
null
,
/* rotationDegrees= */
0
,
/* pixelWidthHeightRatio= */
1
f
,
/* drmInitData= */
null
);
private
SimpleDecoderVideoRenderer
renderer
;
@Mock
private
VideoRendererEventListener
eventListener
;
@Before
public
void
setUp
()
{
renderer
=
new
SimpleDecoderVideoRenderer
(
/* allowedJoiningTimeMs= */
0
,
new
Handler
(),
eventListener
,
/* maxDroppedFramesToNotify= */
-
1
)
{
@C
.
VideoOutputMode
private
int
outputMode
;
@Override
@Capabilities
public
int
supportsFormat
(
Format
format
)
{
return
RendererCapabilities
.
create
(
FORMAT_HANDLED
);
}
@Override
protected
void
setDecoderOutputMode
(
@C
.
VideoOutputMode
int
outputMode
)
{
this
.
outputMode
=
outputMode
;
}
@Override
protected
void
renderOutputBufferToSurface
(
VideoDecoderOutputBuffer
outputBuffer
,
Surface
surface
)
{
// Do nothing.
}
@Override
protected
SimpleDecoder
<
VideoDecoderInputBuffer
,
?
extends
VideoDecoderOutputBuffer
,
?
extends
VideoDecoderException
>
createDecoder
(
Format
format
,
@Nullable
ExoMediaCrypto
mediaCrypto
)
{
return
new
SimpleDecoder
<
VideoDecoderInputBuffer
,
VideoDecoderOutputBuffer
,
VideoDecoderException
>(
new
VideoDecoderInputBuffer
[
10
],
new
VideoDecoderOutputBuffer
[
10
])
{
@Override
protected
VideoDecoderInputBuffer
createInputBuffer
()
{
return
new
VideoDecoderInputBuffer
();
}
@Override
protected
VideoDecoderOutputBuffer
createOutputBuffer
()
{
return
new
VideoDecoderOutputBuffer
(
this
::
releaseOutputBuffer
);
}
@Override
protected
VideoDecoderException
createUnexpectedDecodeException
(
Throwable
error
)
{
return
new
VideoDecoderException
(
"error"
,
error
);
}
@Nullable
@Override
protected
VideoDecoderException
decode
(
VideoDecoderInputBuffer
inputBuffer
,
VideoDecoderOutputBuffer
outputBuffer
,
boolean
reset
)
{
outputBuffer
.
init
(
inputBuffer
.
timeUs
,
outputMode
,
/* supplementalData= */
null
);
return
null
;
}
@Override
public
String
getName
()
{
return
"TestDecoder"
;
}
};
}
};
renderer
.
setOutputSurface
(
new
Surface
(
new
SurfaceTexture
(
/* texName= */
0
)));
}
@Test
public
void
enable_withMayRenderStartOfStream_rendersFirstFrameBeforeStart
()
throws
Exception
{
FakeSampleStream
fakeSampleStream
=
new
FakeSampleStream
(
/* format= */
BASIC_MP4_1080
,
/* eventDispatcher= */
null
,
/* firstSampleTimeUs= */
0
,
/* timeUsIncrement= */
50
,
new
FakeSampleStreamItem
(
new
byte
[]
{
0
},
C
.
BUFFER_FLAG_KEY_FRAME
));
renderer
.
enable
(
RendererConfiguration
.
DEFAULT
,
new
Format
[]
{
BASIC_MP4_1080
},
fakeSampleStream
,
/* positionUs= */
0
,
/* joining= */
false
,
/* mayRenderStartOfStream= */
true
,
/* offsetUs */
0
);
for
(
int
i
=
0
;
i
<
10
;
i
++)
{
renderer
.
render
(
/* positionUs= */
0
,
SystemClock
.
elapsedRealtime
()
*
1000
);
}
verify
(
eventListener
).
onRenderedFirstFrame
(
any
());
}
@Test
public
void
enable_withoutMayRenderStartOfStream_doesNotRenderFirstFrameBeforeStart
()
throws
Exception
{
FakeSampleStream
fakeSampleStream
=
new
FakeSampleStream
(
/* format= */
BASIC_MP4_1080
,
/* eventDispatcher= */
null
,
/* firstSampleTimeUs= */
0
,
/* timeUsIncrement= */
50
,
new
FakeSampleStreamItem
(
new
byte
[]
{
0
},
C
.
BUFFER_FLAG_KEY_FRAME
));
renderer
.
enable
(
RendererConfiguration
.
DEFAULT
,
new
Format
[]
{
BASIC_MP4_1080
},
fakeSampleStream
,
/* positionUs= */
0
,
/* joining= */
false
,
/* mayRenderStartOfStream= */
false
,
/* offsetUs */
0
);
for
(
int
i
=
0
;
i
<
10
;
i
++)
{
renderer
.
render
(
/* positionUs= */
0
,
SystemClock
.
elapsedRealtime
()
*
1000
);
}
verify
(
eventListener
,
never
()).
onRenderedFirstFrame
(
any
());
}
@Test
public
void
enable_withoutMayRenderStartOfStream_rendersFirstFrameAfterStart
()
throws
Exception
{
FakeSampleStream
fakeSampleStream
=
new
FakeSampleStream
(
/* format= */
BASIC_MP4_1080
,
/* eventDispatcher= */
null
,
/* firstSampleTimeUs= */
0
,
/* timeUsIncrement= */
50
,
new
FakeSampleStreamItem
(
new
byte
[]
{
0
},
C
.
BUFFER_FLAG_KEY_FRAME
));
renderer
.
enable
(
RendererConfiguration
.
DEFAULT
,
new
Format
[]
{
BASIC_MP4_1080
},
fakeSampleStream
,
/* positionUs= */
0
,
/* joining= */
false
,
/* mayRenderStartOfStream= */
false
,
/* offsetUs */
0
);
renderer
.
start
();
for
(
int
i
=
0
;
i
<
10
;
i
++)
{
renderer
.
render
(
/* positionUs= */
0
,
SystemClock
.
elapsedRealtime
()
*
1000
);
}
verify
(
eventListener
).
onRenderedFirstFrame
(
any
());
}
// TODO: First frame of replaced stream are not yet reported.
@Ignore
@Test
public
void
replaceStream_whenStarted_rendersFirstFrameOfNewStream
()
throws
Exception
{
FakeSampleStream
fakeSampleStream1
=
new
FakeSampleStream
(
/* format= */
BASIC_MP4_1080
,
/* eventDispatcher= */
null
,
/* firstSampleTimeUs= */
0
,
/* timeUsIncrement= */
50
,
new
FakeSampleStreamItem
(
new
byte
[]
{
0
},
C
.
BUFFER_FLAG_KEY_FRAME
),
FakeSampleStreamItem
.
END_OF_STREAM_ITEM
);
FakeSampleStream
fakeSampleStream2
=
new
FakeSampleStream
(
/* format= */
BASIC_MP4_1080
,
/* eventDispatcher= */
null
,
/* firstSampleTimeUs= */
0
,
/* timeUsIncrement= */
50
,
new
FakeSampleStreamItem
(
new
byte
[]
{
0
},
C
.
BUFFER_FLAG_KEY_FRAME
),
FakeSampleStreamItem
.
END_OF_STREAM_ITEM
);
renderer
.
enable
(
RendererConfiguration
.
DEFAULT
,
new
Format
[]
{
BASIC_MP4_1080
},
fakeSampleStream1
,
/* positionUs= */
0
,
/* joining= */
false
,
/* mayRenderStartOfStream= */
true
,
/* offsetUs */
0
);
renderer
.
start
();
boolean
replacedStream
=
false
;
for
(
int
i
=
0
;
i
<
200
;
i
+=
10
)
{
renderer
.
render
(
/* positionUs= */
i
*
10
,
SystemClock
.
elapsedRealtime
()
*
1000
);
if
(!
replacedStream
&&
renderer
.
hasReadStreamToEnd
())
{
renderer
.
replaceStream
(
new
Format
[]
{
BASIC_MP4_1080
},
fakeSampleStream2
,
/* offsetUs= */
100
);
replacedStream
=
true
;
}
}
verify
(
eventListener
,
times
(
2
)).
onRenderedFirstFrame
(
any
());
}
@Test
public
void
replaceStream_whenNotStarted_doesNotRenderFirstFrameOfNewStream
()
throws
Exception
{
FakeSampleStream
fakeSampleStream1
=
new
FakeSampleStream
(
/* format= */
BASIC_MP4_1080
,
/* eventDispatcher= */
null
,
/* firstSampleTimeUs= */
0
,
/* timeUsIncrement= */
50
,
new
FakeSampleStreamItem
(
new
byte
[]
{
0
},
C
.
BUFFER_FLAG_KEY_FRAME
),
FakeSampleStreamItem
.
END_OF_STREAM_ITEM
);
FakeSampleStream
fakeSampleStream2
=
new
FakeSampleStream
(
/* format= */
BASIC_MP4_1080
,
/* eventDispatcher= */
null
,
/* firstSampleTimeUs= */
0
,
/* timeUsIncrement= */
50
,
new
FakeSampleStreamItem
(
new
byte
[]
{
0
},
C
.
BUFFER_FLAG_KEY_FRAME
),
FakeSampleStreamItem
.
END_OF_STREAM_ITEM
);
renderer
.
enable
(
RendererConfiguration
.
DEFAULT
,
new
Format
[]
{
BASIC_MP4_1080
},
fakeSampleStream1
,
/* positionUs= */
0
,
/* joining= */
false
,
/* mayRenderStartOfStream= */
true
,
/* offsetUs */
0
);
boolean
replacedStream
=
false
;
for
(
int
i
=
0
;
i
<
200
;
i
+=
10
)
{
renderer
.
render
(
/* positionUs= */
i
*
10
,
SystemClock
.
elapsedRealtime
()
*
1000
);
if
(!
replacedStream
&&
renderer
.
hasReadStreamToEnd
())
{
renderer
.
replaceStream
(
new
Format
[]
{
BASIC_MP4_1080
},
fakeSampleStream2
,
/* offsetUs= */
100
);
replacedStream
=
true
;
}
}
verify
(
eventListener
).
onRenderedFirstFrame
(
any
());
}
}
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