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
8da6c35b
authored
Apr 25, 2022
by
samrobinson
Committed by
Ian Baker
Apr 26, 2022
Browse files
Options
_('Browse Files')
Download
Email Patches
Plain Diff
Keep AudioTrack on flush as default
PiperOrigin-RevId: 444264961
parent
0760520b
Hide whitespace changes
Inline
Side-by-side
Showing
6 changed files
with
69 additions
and
122 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 @
8da6c35b
...
...
@@ -453,18 +453,6 @@ 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 sink, releasing any resources that it currently holds. */
void
reset
();
}
library/core/src/main/java/com/google/android/exoplayer2/audio/DecoderAudioRenderer.java
View file @
8da6c35b
...
...
@@ -125,7 +125,6 @@ public abstract class DecoderAudioRenderer<
private
int
encoderDelay
;
private
int
encoderPadding
;
private
boolean
experimentalKeepAudioTrackOnSeek
;
private
boolean
firstStreamSampleRead
;
@Nullable
private
T
decoder
;
...
...
@@ -205,19 +204,6 @@ public abstract class DecoderAudioRenderer<
audioSinkNeedsConfigure
=
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
()
{
...
...
@@ -551,12 +537,7 @@ public abstract class DecoderAudioRenderer<
@Override
protected
void
onPositionReset
(
long
positionUs
,
boolean
joining
)
throws
ExoPlaybackException
{
if
(
experimentalKeepAudioTrackOnSeek
)
{
audioSink
.
experimentalFlushWithoutAudioTrackRelease
();
}
else
{
audioSink
.
flush
();
}
audioSink
.
flush
();
currentPositionUs
=
positionUs
;
allowFirstBufferPositionDiscontinuity
=
true
;
allowPositionDiscontinuity
=
true
;
...
...
library/core/src/main/java/com/google/android/exoplayer2/audio/DefaultAudioSink.java
View file @
8da6c35b
...
...
@@ -898,7 +898,7 @@ public final class DefaultAudioSink implements AudioSink {
// We're waiting for playout on the current audio track to finish.
return
false
;
}
flush
();
flush
AndReleaseAudioTrack
();
}
else
{
// The current audio track can be reused for the new configuration.
configuration
=
pendingConfiguration
;
...
...
@@ -1025,7 +1025,7 @@ public final class DefaultAudioSink implements AudioSink {
if
(
audioTrackPositionTracker
.
isStalled
(
getWrittenFrames
()))
{
Log
.
w
(
TAG
,
"Resetting stalled audio track"
);
flush
();
flush
AndReleaseAudioTrack
();
return
true
;
}
...
...
@@ -1315,7 +1315,8 @@ public final class DefaultAudioSink implements AudioSink {
// The audio attributes are ignored in tunneling mode, so no need to reset.
return
;
}
flush
();
// audioAttributes change requires the audioTrack to be recreated.
flushAndReleaseAudioTrack
();
}
@Override
...
...
@@ -1328,7 +1329,8 @@ public final class DefaultAudioSink implements AudioSink {
if
(
this
.
audioSessionId
!=
audioSessionId
)
{
this
.
audioSessionId
=
audioSessionId
;
externalAudioSessionIdProvided
=
audioSessionId
!=
C
.
AUDIO_SESSION_ID_UNSET
;
flush
();
// audioSessionId change requires the audioTrack to be recreated.
flushAndReleaseAudioTrack
();
}
}
...
...
@@ -1356,7 +1358,7 @@ public final class DefaultAudioSink implements AudioSink {
Assertions
.
checkState
(
externalAudioSessionIdProvided
);
if
(!
tunneling
)
{
tunneling
=
true
;
flush
();
flush
AndReleaseAudioTrack
();
}
}
...
...
@@ -1364,7 +1366,7 @@ public final class DefaultAudioSink implements AudioSink {
public
void
disableTunneling
()
{
if
(
tunneling
)
{
tunneling
=
false
;
flush
();
flush
AndReleaseAudioTrack
();
}
}
...
...
@@ -1396,70 +1398,26 @@ public final class DefaultAudioSink implements AudioSink {
@Override
public
void
flush
()
{
if
(
isAudioTrackInitialized
())
{
resetSinkStateForFlush
();
if
(
audioTrackPositionTracker
.
isPlaying
())
{
audioTrack
.
pause
();
}
if
(
isOffloadedPlayback
(
audioTrack
))
{
checkNotNull
(
offloadStreamEventCallbackV29
).
unregister
(
audioTrack
);
}
// AudioTrack.release can take some time, so we call it on a background thread.
final
AudioTrack
toRelease
=
audioTrack
;
audioTrack
=
null
;
if
(
Util
.
SDK_INT
<
21
&&
!
externalAudioSessionIdProvided
)
{
// Prior to API level 21, audio sessions are not kept alive once there are no components
// associated with them. If we generated the session ID internally, the only component
// associated with the session is the audio track that's being released, and therefore
// the session will not be kept alive. As a result, we need to generate a new session when
// we next create an audio track.
audioSessionId
=
C
.
AUDIO_SESSION_ID_UNSET
;
}
if
(
pendingConfiguration
!=
null
)
{
configuration
=
pendingConfiguration
;
pendingConfiguration
=
null
;
}
audioTrackPositionTracker
.
reset
();
releasingConditionVariable
.
close
();
new
Thread
(
"ExoPlayer:AudioTrackReleaseThread"
)
{
@Override
public
void
run
()
{
try
{
toRelease
.
flush
();
toRelease
.
release
();
}
finally
{
releasingConditionVariable
.
open
();
}
}
}.
start
();
if
(!
isAudioTrackInitialized
())
{
return
;
}
writeExceptionPendingExceptionHolder
.
clear
();
initializationExceptionPendingExceptionHolder
.
clear
();
}
@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)
// Prior to SDK 25, AudioTrack flush does not work as intended, so it must be released and
// reinitialized. (Internal reference: b/143500232)
if
(
Util
.
SDK_INT
<
25
)
{
flush
();
flush
AndReleaseAudioTrack
();
return
;
}
writeExceptionPendingExceptionHolder
.
clear
();
initializationExceptionPendingExceptionHolder
.
clear
();
if
(!
isAudioTrackInitialized
())
{
return
;
}
resetSinkStateForFlush
();
if
(
audioTrackPositionTracker
.
isPlaying
())
{
audioTrack
.
pause
();
}
audioTrack
.
flush
();
audioTrack
.
flush
();
audioTrackPositionTracker
.
reset
();
audioTrackPositionTracker
.
setAudioTrack
(
audioTrack
,
...
...
@@ -1467,13 +1425,12 @@ public final class DefaultAudioSink implements AudioSink {
configuration
.
outputEncoding
,
configuration
.
outputPcmFrameSize
,
configuration
.
bufferSize
);
startMediaTimeUsNeedsInit
=
true
;
}
@Override
public
void
reset
()
{
flush
();
flush
AndReleaseAudioTrack
();
for
(
AudioProcessor
audioProcessor
:
toIntPcmAvailableAudioProcessors
)
{
audioProcessor
.
reset
();
}
...
...
@@ -1486,6 +1443,51 @@ public final class DefaultAudioSink implements AudioSink {
// Internal methods.
private
void
flushAndReleaseAudioTrack
()
{
if
(!
isAudioTrackInitialized
())
{
return
;
}
writeExceptionPendingExceptionHolder
.
clear
();
initializationExceptionPendingExceptionHolder
.
clear
();
resetSinkStateForFlush
();
if
(
audioTrackPositionTracker
.
isPlaying
())
{
audioTrack
.
pause
();
}
if
(
isOffloadedPlayback
(
audioTrack
))
{
checkNotNull
(
offloadStreamEventCallbackV29
).
unregister
(
audioTrack
);
}
// AudioTrack.release can take some time, so we call it on a background thread.
final
AudioTrack
toRelease
=
audioTrack
;
audioTrack
=
null
;
if
(
Util
.
SDK_INT
<
21
&&
!
externalAudioSessionIdProvided
)
{
// Prior to API level 21, audio sessions are not kept alive once there are no components
// associated with them. If we generated the session ID internally, the only component
// associated with the session is the audio track that's being released, and therefore
// the session will not be kept alive. As a result, we need to generate a new session when
// we next create an audio track.
audioSessionId
=
C
.
AUDIO_SESSION_ID_UNSET
;
}
if
(
pendingConfiguration
!=
null
)
{
configuration
=
pendingConfiguration
;
pendingConfiguration
=
null
;
}
audioTrackPositionTracker
.
reset
();
releasingConditionVariable
.
close
();
new
Thread
(
"ExoPlayer:AudioTrackReleaseThread"
)
{
@Override
public
void
run
()
{
try
{
toRelease
.
flush
();
toRelease
.
release
();
}
finally
{
releasingConditionVariable
.
open
();
}
}
}.
start
();
}
private
void
resetSinkStateForFlush
()
{
submittedPcmBytes
=
0
;
submittedEncodedFrames
=
0
;
...
...
library/core/src/main/java/com/google/android/exoplayer2/audio/ForwardingAudioSink.java
View file @
8da6c35b
...
...
@@ -160,11 +160,6 @@ 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 @
8da6c35b
...
...
@@ -105,8 +105,6 @@ public class MediaCodecAudioRenderer extends MediaCodecRenderer implements Media
private
boolean
allowPositionDiscontinuity
;
private
boolean
audioSinkNeedsConfigure
;
private
boolean
experimentalKeepAudioTrackOnSeek
;
@Nullable
private
WakeupListener
wakeupListener
;
/**
...
...
@@ -262,19 +260,6 @@ 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
protected
@Capabilities
int
supportsFormat
(
MediaCodecSelector
mediaCodecSelector
,
Format
format
)
throws
DecoderQueryException
{
...
...
@@ -527,11 +512,7 @@ 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
();
}
audioSink
.
flush
();
currentPositionUs
=
positionUs
;
allowFirstBufferPositionDiscontinuity
=
true
;
...
...
library/core/src/test/java/com/google/android/exoplayer2/audio/DefaultAudioSinkTest.java
View file @
8da6c35b
...
...
@@ -265,15 +265,15 @@ public final class DefaultAudioSinkTest {
}
@Test
public
void
handle
sBufferAfterExperimentalFlush
()
throws
Exception
{
// This is demonstrating that no Exceptions are thrown as a result of handling a buffer after a
n
//
experimental
flush.
public
void
handle
Buffer_afterFlush_doesntThrow
()
throws
Exception
{
// This is demonstrating that no Exceptions are thrown as a result of handling a buffer after a
// flush.
configureDefaultAudioSink
(
CHANNEL_COUNT_STEREO
);
defaultAudioSink
.
handleBuffer
(
createDefaultSilenceBuffer
(),
/* presentationTimeUs= */
0
,
/* encodedAccessUnitCount= */
1
);
// After the
experimental
flush we can successfully queue more input.
defaultAudioSink
.
experimentalFlushWithoutAudioTrackRelease
();
// After the flush we can successfully queue more input.
defaultAudioSink
.
flush
();
defaultAudioSink
.
handleBuffer
(
createDefaultSilenceBuffer
(),
/* presentationTimeUs= */
5_000
,
...
...
@@ -281,13 +281,13 @@ public final class DefaultAudioSinkTest {
}
@Test
public
void
getCurrentPosition_
returnsUnset_afterExperimentalFlush
()
throws
Exception
{
public
void
getCurrentPosition_
afterFlush_returnsUnset
()
throws
Exception
{
configureDefaultAudioSink
(
CHANNEL_COUNT_STEREO
);
defaultAudioSink
.
handleBuffer
(
createDefaultSilenceBuffer
(),
/* presentationTimeUs= */
5
*
C
.
MICROS_PER_SECOND
,
/* encodedAccessUnitCount= */
1
);
defaultAudioSink
.
experimentalFlushWithoutAudioTrackRelease
();
defaultAudioSink
.
flush
();
assertThat
(
defaultAudioSink
.
getCurrentPositionUs
(
/* sourceEnded= */
false
))
.
isEqualTo
(
CURRENT_POSITION_NOT_SET
);
}
...
...
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