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
eabc486b
authored
Aug 14, 2020
by
samrobinson
Committed by
kim-vde
Aug 17, 2020
Browse files
Options
_('Browse Files')
Download
Email Patches
Plain Diff
Keep AudioTrack on seek - experimental.
PiperOrigin-RevId: 326622573
parent
c2ac33af
Show whitespace changes
Inline
Side-by-side
Showing
6 changed files
with
151 additions
and
25 deletions
library/core/src/main/java/com/google/android/exoplayer2/audio/AudioSink.java
library/core/src/main/java/com/google/android/exoplayer2/audio/DecoderAudioRenderer.java
library/core/src/main/java/com/google/android/exoplayer2/audio/DefaultAudioSink.java
library/core/src/main/java/com/google/android/exoplayer2/audio/ForwardingAudioSink.java
library/core/src/main/java/com/google/android/exoplayer2/audio/MediaCodecAudioRenderer.java
library/core/src/test/java/com/google/android/exoplayer2/audio/DefaultAudioSinkTest.java
library/core/src/main/java/com/google/android/exoplayer2/audio/AudioSink.java
View file @
eabc486b
...
...
@@ -362,6 +362,18 @@ public interface AudioSink {
*/
void
flush
();
/**
* Flushes the sink, after which it is ready to receive buffers from a new playback position.
*
* <p>Does not release the {@link AudioTrack} held by the sink.
*
* <p>This method is experimental, and will be renamed or removed in a future release.
*
* <p>Only for experimental use as part of {@link
* MediaCodecAudioRenderer#experimentalSetEnableKeepAudioTrackOnSeek(boolean)}.
*/
void
experimentalFlushWithoutAudioTrackRelease
();
/** Resets the renderer, releasing any resources that it currently holds. */
void
reset
();
}
library/core/src/main/java/com/google/android/exoplayer2/audio/DecoderAudioRenderer.java
View file @
eabc486b
...
...
@@ -112,6 +112,8 @@ public abstract class DecoderAudioRenderer<
private
int
encoderDelay
;
private
int
encoderPadding
;
private
boolean
experimentalKeepAudioTrackOnSeek
;
@Nullable
private
T
decoder
;
@Nullable
private
DecoderInputBuffer
inputBuffer
;
...
...
@@ -185,6 +187,19 @@ public abstract class DecoderAudioRenderer<
audioTrackNeedsConfigure
=
true
;
}
/**
* Sets whether to enable the experimental feature that keeps and flushes the {@link
* android.media.AudioTrack} when a seek occurs, as opposed to releasing and reinitialising. Off
* by default.
*
* <p>This method is experimental, and will be renamed or removed in a future release.
*
* @param enableKeepAudioTrackOnSeek Whether to keep the {@link android.media.AudioTrack} on seek.
*/
public
void
experimentalSetEnableKeepAudioTrackOnSeek
(
boolean
enableKeepAudioTrackOnSeek
)
{
this
.
experimentalKeepAudioTrackOnSeek
=
enableKeepAudioTrackOnSeek
;
}
@Override
@Nullable
public
MediaClock
getMediaClock
()
{
...
...
@@ -507,7 +522,12 @@ public abstract class DecoderAudioRenderer<
@Override
protected
void
onPositionReset
(
long
positionUs
,
boolean
joining
)
throws
ExoPlaybackException
{
if
(
experimentalKeepAudioTrackOnSeek
)
{
audioSink
.
experimentalFlushWithoutAudioTrackRelease
();
}
else
{
audioSink
.
flush
();
}
currentPositionUs
=
positionUs
;
allowFirstBufferPositionDiscontinuity
=
true
;
allowPositionDiscontinuity
=
true
;
...
...
library/core/src/main/java/com/google/android/exoplayer2/audio/DefaultAudioSink.java
View file @
eabc486b
...
...
@@ -279,7 +279,9 @@ public final class DefaultAudioSink implements AudioSink {
@MonotonicNonNull
private
StreamEventCallbackV29
offloadStreamEventCallbackV29
;
@Nullable
private
Listener
listener
;
/** Used to keep the audio session active on pre-V21 builds (see {@link #initialize(long)}). */
/**
* Used to keep the audio session active on pre-V21 builds (see {@link #initializeAudioTrack()}).
*/
@Nullable
private
AudioTrack
keepSessionIdAudioTrack
;
@Nullable
private
Configuration
pendingConfiguration
;
...
...
@@ -300,6 +302,7 @@ public final class DefaultAudioSink implements AudioSink {
private
long
writtenEncodedFrames
;
private
int
framesPerEncodedSample
;
private
boolean
startMediaTimeUsNeedsSync
;
private
boolean
startMediaTimeUsNeedsInit
;
private
long
startMediaTimeUs
;
private
float
volume
;
...
...
@@ -470,7 +473,7 @@ public final class DefaultAudioSink implements AudioSink {
@Override
public
long
getCurrentPositionUs
(
boolean
sourceEnded
)
{
if
(!
is
Initialized
()
)
{
if
(!
is
AudioTrackInitialized
()
||
startMediaTimeUsNeedsInit
)
{
return
CURRENT_POSITION_NOT_SET
;
}
long
positionUs
=
audioTrackPositionTracker
.
getCurrentPositionUs
(
sourceEnded
);
...
...
@@ -581,7 +584,7 @@ public final class DefaultAudioSink implements AudioSink {
specifiedBufferSize
,
canApplyPlaybackParameters
,
availableAudioProcessors
);
if
(
isInitialized
())
{
if
(
is
AudioTrack
Initialized
())
{
this
.
pendingConfiguration
=
pendingConfiguration
;
}
else
{
configuration
=
pendingConfiguration
;
...
...
@@ -612,7 +615,7 @@ public final class DefaultAudioSink implements AudioSink {
}
}
private
void
initialize
(
long
presentationTimeUs
)
throws
InitializationException
{
private
void
initialize
AudioTrack
(
)
throws
InitializationException
{
// If we're asynchronously releasing a previous audio track then we block until it has been
// released. This guarantees that we cannot end up in a state where we have multiple audio
// track instances. Without this guarantee it would be possible, in extreme cases, to exhaust
...
...
@@ -647,17 +650,9 @@ public final class DefaultAudioSink implements AudioSink {
}
}
startMediaTimeUs
=
max
(
0
,
presentationTimeUs
);
startMediaTimeUsNeedsSync
=
false
;
if
(
enableAudioTrackPlaybackParams
&&
Util
.
SDK_INT
>=
23
)
{
setAudioTrackPlaybackSpeedV23
(
audioTrackPlaybackSpeed
);
}
applyAudioProcessorPlaybackSpeedAndSkipSilence
(
presentationTimeUs
);
audioTrackPositionTracker
.
setAudioTrack
(
audioTrack
,
configuration
.
outputMode
==
OUTPUT_MODE_PASSTHROUGH
,
/* isPassthrough= */
configuration
.
outputMode
==
OUTPUT_MODE_PASSTHROUGH
,
configuration
.
outputEncoding
,
configuration
.
outputPcmFrameSize
,
configuration
.
bufferSize
);
...
...
@@ -667,12 +662,14 @@ public final class DefaultAudioSink implements AudioSink {
audioTrack
.
attachAuxEffect
(
auxEffectInfo
.
effectId
);
audioTrack
.
setAuxEffectSendLevel
(
auxEffectInfo
.
sendLevel
);
}
startMediaTimeUsNeedsInit
=
true
;
}
@Override
public
void
play
()
{
playing
=
true
;
if
(
isInitialized
())
{
if
(
is
AudioTrack
Initialized
())
{
audioTrackPositionTracker
.
start
();
audioTrack
.
play
();
}
...
...
@@ -716,8 +713,20 @@ public final class DefaultAudioSink implements AudioSink {
applyAudioProcessorPlaybackSpeedAndSkipSilence
(
presentationTimeUs
);
}
if
(!
isInitialized
())
{
initialize
(
presentationTimeUs
);
if
(!
isAudioTrackInitialized
())
{
initializeAudioTrack
();
}
if
(
startMediaTimeUsNeedsInit
)
{
startMediaTimeUs
=
max
(
0
,
presentationTimeUs
);
startMediaTimeUsNeedsSync
=
false
;
startMediaTimeUsNeedsInit
=
false
;
if
(
enableAudioTrackPlaybackParams
&&
Util
.
SDK_INT
>=
23
)
{
setAudioTrackPlaybackSpeedV23
(
audioTrackPlaybackSpeed
);
}
applyAudioProcessorPlaybackSpeedAndSkipSilence
(
presentationTimeUs
);
if
(
playing
)
{
play
();
}
...
...
@@ -945,7 +954,7 @@ public final class DefaultAudioSink implements AudioSink {
@Override
public
void
playToEndOfStream
()
throws
WriteException
{
if
(!
handledEndOfStream
&&
isInitialized
()
&&
drainToEndOfStream
())
{
if
(!
handledEndOfStream
&&
is
AudioTrack
Initialized
()
&&
drainToEndOfStream
())
{
playPendingData
();
handledEndOfStream
=
true
;
}
...
...
@@ -987,12 +996,13 @@ public final class DefaultAudioSink implements AudioSink {
@Override
public
boolean
isEnded
()
{
return
!
isInitialized
()
||
(
handledEndOfStream
&&
!
hasPendingData
());
return
!
is
AudioTrack
Initialized
()
||
(
handledEndOfStream
&&
!
hasPendingData
());
}
@Override
public
boolean
hasPendingData
()
{
return
isInitialized
()
&&
audioTrackPositionTracker
.
hasPendingData
(
getWrittenFrames
());
return
isAudioTrackInitialized
()
&&
audioTrackPositionTracker
.
hasPendingData
(
getWrittenFrames
());
}
@Override
...
...
@@ -1090,7 +1100,7 @@ public final class DefaultAudioSink implements AudioSink {
}
private
void
setVolumeInternal
()
{
if
(!
isInitialized
())
{
if
(!
is
AudioTrack
Initialized
())
{
// Do nothing.
}
else
if
(
Util
.
SDK_INT
>=
21
)
{
setVolumeInternalV21
(
audioTrack
,
volume
);
...
...
@@ -1102,14 +1112,14 @@ public final class DefaultAudioSink implements AudioSink {
@Override
public
void
pause
()
{
playing
=
false
;
if
(
isInitialized
()
&&
audioTrackPositionTracker
.
pause
())
{
if
(
is
AudioTrack
Initialized
()
&&
audioTrackPositionTracker
.
pause
())
{
audioTrack
.
pause
();
}
}
@Override
public
void
flush
()
{
if
(
isInitialized
())
{
if
(
is
AudioTrack
Initialized
())
{
resetSinkStateForFlush
();
if
(
audioTrackPositionTracker
.
isPlaying
())
{
...
...
@@ -1142,6 +1152,36 @@ public final class DefaultAudioSink implements AudioSink {
}
@Override
public
void
experimentalFlushWithoutAudioTrackRelease
()
{
// Prior to SDK 25, AudioTrack flush does not work as intended, and therefore it must be
// released and reinitialized. (Internal reference: b/143500232)
if
(
Util
.
SDK_INT
<
25
)
{
flush
();
return
;
}
if
(!
isAudioTrackInitialized
())
{
return
;
}
resetSinkStateForFlush
();
if
(
audioTrackPositionTracker
.
isPlaying
())
{
audioTrack
.
pause
();
}
audioTrack
.
flush
();
audioTrackPositionTracker
.
reset
();
audioTrackPositionTracker
.
setAudioTrack
(
audioTrack
,
/* isPassthrough= */
configuration
.
outputMode
==
OUTPUT_MODE_PASSTHROUGH
,
configuration
.
outputEncoding
,
configuration
.
outputPcmFrameSize
,
configuration
.
bufferSize
);
startMediaTimeUsNeedsInit
=
true
;
}
@Override
public
void
reset
()
{
flush
();
releaseKeepSessionIdAudioTrack
();
...
...
@@ -1204,7 +1244,7 @@ public final class DefaultAudioSink implements AudioSink {
@RequiresApi
(
23
)
private
void
setAudioTrackPlaybackSpeedV23
(
float
audioTrackPlaybackSpeed
)
{
if
(
isInitialized
())
{
if
(
is
AudioTrack
Initialized
())
{
PlaybackParams
playbackParams
=
new
PlaybackParams
()
.
allowDefaults
()
...
...
@@ -1233,7 +1273,7 @@ public final class DefaultAudioSink implements AudioSink {
skipSilence
,
/* mediaTimeUs= */
C
.
TIME_UNSET
,
/* audioTrackPositionUs= */
C
.
TIME_UNSET
);
if
(
isInitialized
())
{
if
(
is
AudioTrack
Initialized
())
{
// Drain the audio processors so we can determine the frame position at which the new
// parameters apply.
this
.
afterDrainParameters
=
mediaPositionParameters
;
...
...
@@ -1313,7 +1353,7 @@ public final class DefaultAudioSink implements AudioSink {
+
configuration
.
framesToDurationUs
(
audioProcessorChain
.
getSkippedOutputFrameCount
());
}
private
boolean
isInitialized
()
{
private
boolean
is
AudioTrack
Initialized
()
{
return
audioTrack
!=
null
;
}
...
...
library/core/src/main/java/com/google/android/exoplayer2/audio/ForwardingAudioSink.java
View file @
eabc486b
...
...
@@ -148,6 +148,11 @@ public class ForwardingAudioSink implements AudioSink {
}
@Override
public
void
experimentalFlushWithoutAudioTrackRelease
()
{
sink
.
experimentalFlushWithoutAudioTrackRelease
();
}
@Override
public
void
reset
()
{
sink
.
reset
();
}
...
...
library/core/src/main/java/com/google/android/exoplayer2/audio/MediaCodecAudioRenderer.java
View file @
eabc486b
...
...
@@ -97,6 +97,8 @@ public class MediaCodecAudioRenderer extends MediaCodecRenderer implements Media
private
boolean
allowFirstBufferPositionDiscontinuity
;
private
boolean
allowPositionDiscontinuity
;
private
boolean
experimentalKeepAudioTrackOnSeek
;
@Nullable
private
WakeupListener
wakeupListener
;
/**
...
...
@@ -205,6 +207,19 @@ public class MediaCodecAudioRenderer extends MediaCodecRenderer implements Media
return
TAG
;
}
/**
* Sets whether to enable the experimental feature that keeps and flushes the {@link
* android.media.AudioTrack} when a seek occurs, as opposed to releasing and reinitialising. Off
* by default.
*
* <p>This method is experimental, and will be renamed or removed in a future release.
*
* @param enableKeepAudioTrackOnSeek Whether to keep the {@link android.media.AudioTrack} on seek.
*/
public
void
experimentalSetEnableKeepAudioTrackOnSeek
(
boolean
enableKeepAudioTrackOnSeek
)
{
this
.
experimentalKeepAudioTrackOnSeek
=
enableKeepAudioTrackOnSeek
;
}
@Override
@Capabilities
protected
int
supportsFormat
(
MediaCodecSelector
mediaCodecSelector
,
Format
format
)
...
...
@@ -465,7 +480,12 @@ public class MediaCodecAudioRenderer extends MediaCodecRenderer implements Media
@Override
protected
void
onPositionReset
(
long
positionUs
,
boolean
joining
)
throws
ExoPlaybackException
{
super
.
onPositionReset
(
positionUs
,
joining
);
if
(
experimentalKeepAudioTrackOnSeek
)
{
audioSink
.
experimentalFlushWithoutAudioTrackRelease
();
}
else
{
audioSink
.
flush
();
}
currentPositionUs
=
positionUs
;
allowFirstBufferPositionDiscontinuity
=
true
;
allowPositionDiscontinuity
=
true
;
...
...
library/core/src/test/java/com/google/android/exoplayer2/audio/DefaultAudioSinkTest.java
View file @
eabc486b
...
...
@@ -15,6 +15,7 @@
*/
package
com
.
google
.
android
.
exoplayer2
.
audio
;
import
static
com
.
google
.
android
.
exoplayer2
.
audio
.
AudioSink
.
CURRENT_POSITION_NOT_SET
;
import
static
com
.
google
.
android
.
exoplayer2
.
audio
.
AudioSink
.
SINK_FORMAT_SUPPORTED_DIRECTLY
;
import
static
com
.
google
.
android
.
exoplayer2
.
audio
.
AudioSink
.
SINK_FORMAT_SUPPORTED_WITH_TRANSCODING
;
import
static
com
.
google
.
common
.
truth
.
Truth
.
assertThat
;
...
...
@@ -275,6 +276,34 @@ public final class DefaultAudioSinkTest {
assertThat
(
defaultAudioSink
.
supportsFormat
(
aacLcFormat
)).
isFalse
();
}
@Test
public
void
handlesBufferAfterExperimentalFlush
()
throws
Exception
{
// This is demonstrating that no Exceptions are thrown as a result of handling a buffer after an
// experimental flush.
configureDefaultAudioSink
(
CHANNEL_COUNT_STEREO
);
defaultAudioSink
.
handleBuffer
(
createDefaultSilenceBuffer
(),
/* presentationTimeUs= */
0
,
/* encodedAccessUnitCount= */
1
);
// After the experimental flush we can successfully queue more input.
defaultAudioSink
.
experimentalFlushWithoutAudioTrackRelease
();
defaultAudioSink
.
handleBuffer
(
createDefaultSilenceBuffer
(),
/* presentationTimeUs= */
5_000
,
/* encodedAccessUnitCount= */
1
);
}
@Test
public
void
getCurrentPosition_returnsUnset_afterExperimentalFlush
()
throws
Exception
{
configureDefaultAudioSink
(
CHANNEL_COUNT_STEREO
);
defaultAudioSink
.
handleBuffer
(
createDefaultSilenceBuffer
(),
/* presentationTimeUs= */
5
*
C
.
MICROS_PER_SECOND
,
/* encodedAccessUnitCount= */
1
);
defaultAudioSink
.
experimentalFlushWithoutAudioTrackRelease
();
assertThat
(
defaultAudioSink
.
getCurrentPositionUs
(
/* sourceEnded= */
false
))
.
isEqualTo
(
CURRENT_POSITION_NOT_SET
);
}
private
void
configureDefaultAudioSink
(
int
channelCount
)
throws
AudioSink
.
ConfigurationException
{
configureDefaultAudioSink
(
channelCount
,
/* trimStartFrames= */
0
,
/* trimEndFrames= */
0
);
}
...
...
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