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
57ee90a9
authored
Nov 17, 2015
by
Oliver Woodman
Browse files
Options
_('Browse Files')
Download
Email Patches
Plain Diff
Clean up AudioTrack.
parent
a4f1e3ce
Hide whitespace changes
Inline
Side-by-side
Showing
3 changed files
with
126 additions
and
91 deletions
extensions/opus/src/main/java/com/google/android/exoplayer/ext/opus/LibopusAudioTrackRenderer.java
library/src/main/java/com/google/android/exoplayer/MediaCodecAudioTrackRenderer.java
library/src/main/java/com/google/android/exoplayer/audio/AudioTrack.java
extensions/opus/src/main/java/com/google/android/exoplayer/ext/opus/LibopusAudioTrackRenderer.java
View file @
57ee90a9
...
...
@@ -385,7 +385,7 @@ public final class LibopusAudioTrackRenderer extends SampleSourceTrackRenderer
int
result
=
readSource
(
positionUs
,
formatHolder
,
null
,
false
);
if
(
result
==
SampleSource
.
FORMAT_READ
)
{
format
=
formatHolder
.
format
;
audioTrack
.
re
configure
(
format
.
getFrameworkMediaFormatV16
(),
false
);
audioTrack
.
configure
(
format
.
getFrameworkMediaFormatV16
(),
false
);
return
true
;
}
return
false
;
...
...
library/src/main/java/com/google/android/exoplayer/MediaCodecAudioTrackRenderer.java
View file @
57ee90a9
...
...
@@ -246,7 +246,7 @@ public class MediaCodecAudioTrackRenderer extends MediaCodecTrackRenderer implem
@Override
protected
void
onOutputFormatChanged
(
android
.
media
.
MediaFormat
outputFormat
)
{
boolean
passthrough
=
passthroughMediaFormat
!=
null
;
audioTrack
.
re
configure
(
passthrough
?
passthroughMediaFormat
:
outputFormat
,
passthrough
);
audioTrack
.
configure
(
passthrough
?
passthroughMediaFormat
:
outputFormat
,
passthrough
);
}
/**
...
...
library/src/main/java/com/google/android/exoplayer/audio/AudioTrack.java
View file @
57ee90a9
...
...
@@ -36,19 +36,24 @@ import java.nio.ByteBuffer;
/**
* Plays audio data. The implementation delegates to an {@link android.media.AudioTrack} and handles
* playback position smoothing, non-blocking writes and reconfiguration.
*
* <p>If {@link #isInitialized} returns {@code false}, the instance can be {@link #initialize}d.
* After initialization, start playback by calling {@link #play}.
*
* <p>Call {@link #handleBuffer} to write data for playback.
*
* <p>Call {@link #handleDiscontinuity} when a buffer is skipped.
*
* <p>Call {@link #reconfigure} when the output format changes.
*
* <p>Call {@link #reset} to free resources. It is safe to re-{@link #initialize} the instance.
*
* <p>Call {@link #release} when the instance will no longer be used.
* <p>
* Before starting playback, specify the input audio format by calling one of the {@link #configure}
* methods and {@link #initialize} the instance, optionally specifying an audio session.
* <p>
* Call {@link #handleBuffer(ByteBuffer, int, int, long)} to write data to play back, and
* {@link #handleDiscontinuity()} when a buffer is skipped. Call {@link #play()} to start playing
* back written data.
* <p>
* Call {@link #configure} again whenever the input format changes. If {@link #isInitialized()}
* returns false after calling {@link #configure}, it is necessary to re-{@link #initialize} the
* instance before writing more data.
* <p>
* The underlying framework audio track is created by {@link #initialize} and released
* asynchronously by {@link #reset} (and {@link #configure}, unless the format is unchanged).
* Reinitialization blocks until releasing the old audio track completes. It is safe to
* re-{@link #initialize} the instance after calling {@link #reset()}, without reconfiguration.
* <p>
* Call {@link #release()} when the instance will no longer be used.
*/
@TargetApi
(
16
)
public
final
class
AudioTrack
{
...
...
@@ -58,7 +63,9 @@ public final class AudioTrack {
*/
public
static
final
class
InitializationException
extends
Exception
{
/** The state as reported by {@link android.media.AudioTrack#getState()}. */
/**
* The state as reported by {@link android.media.AudioTrack#getState()}.
*/
public
final
int
audioTrackState
;
public
InitializationException
(
...
...
@@ -75,7 +82,9 @@ public final class AudioTrack {
*/
public
static
final
class
WriteException
extends
Exception
{
/** The value returned from {@link android.media.AudioTrack#write(byte[], int, int)}. */
/**
* The value returned from {@link android.media.AudioTrack#write(byte[], int, int)}.
*/
public
final
int
errorCode
;
public
WriteException
(
int
errorCode
)
{
...
...
@@ -97,20 +106,32 @@ public final class AudioTrack {
}
/** Returned in the result of {@link #handleBuffer} if the buffer was discontinuous. */
/**
* Returned in the result of {@link #handleBuffer} if the buffer was discontinuous.
*/
public
static
final
int
RESULT_POSITION_DISCONTINUITY
=
1
;
/** Returned in the result of {@link #handleBuffer} if the buffer can be released. */
/**
* Returned in the result of {@link #handleBuffer} if the buffer can be released.
*/
public
static
final
int
RESULT_BUFFER_CONSUMED
=
2
;
/** Represents an unset {@link android.media.AudioTrack} session identifier. */
/**
* Represents an unset {@link android.media.AudioTrack} session identifier.
*/
public
static
final
int
SESSION_ID_NOT_SET
=
0
;
/** Returned by {@link #getCurrentPositionUs} when the position is not set. */
/**
* Returned by {@link #getCurrentPositionUs} when the position is not set.
*/
public
static
final
long
CURRENT_POSITION_NOT_SET
=
Long
.
MIN_VALUE
;
/** A minimum length for the {@link android.media.AudioTrack} buffer, in microseconds. */
/**
* A minimum length for the {@link android.media.AudioTrack} buffer, in microseconds.
*/
private
static
final
long
MIN_BUFFER_DURATION_US
=
250000
;
/** A maximum length for the {@link android.media.AudioTrack} buffer, in microseconds. */
/**
* A maximum length for the {@link android.media.AudioTrack} buffer, in microseconds.
*/
private
static
final
long
MAX_BUFFER_DURATION_US
=
750000
;
/**
* A multiplication factor to apply to the minimum buffer size requested by the underlying
...
...
@@ -171,7 +192,9 @@ public final class AudioTrack {
private
final
long
[]
playheadOffsets
;
private
final
AudioTrackUtil
audioTrackUtil
;
/** Used to keep the audio session active on pre-V21 builds (see {@link #initialize()}). */
/**
* Used to keep the audio session active on pre-V21 builds (see {@link #initialize()}).
*/
private
android
.
media
.
AudioTrack
keepSessionIdAudioTrack
;
private
android
.
media
.
AudioTrack
audioTrack
;
...
...
@@ -307,85 +330,25 @@ public final class AudioTrack {
}
/**
* Initializes the audio track for writing new buffers using {@link #handleBuffer}.
*
* @return The audio track session identifier.
*/
public
int
initialize
()
throws
InitializationException
{
return
initialize
(
SESSION_ID_NOT_SET
);
}
/**
* Initializes the audio track for writing new buffers using {@link #handleBuffer}.
*
* @param sessionId Audio track session identifier to re-use, or {@link #SESSION_ID_NOT_SET} to
* create a new one.
* @return The new (or re-used) session identifier.
*/
public
int
initialize
(
int
sessionId
)
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
// the shared memory that's available for audio track buffers. This would in turn cause the
// initialization of the audio track to fail.
releasingConditionVariable
.
block
();
if
(
sessionId
==
SESSION_ID_NOT_SET
)
{
audioTrack
=
new
android
.
media
.
AudioTrack
(
streamType
,
sampleRate
,
channelConfig
,
encoding
,
bufferSize
,
android
.
media
.
AudioTrack
.
MODE_STREAM
);
}
else
{
// Re-attach to the same audio session.
audioTrack
=
new
android
.
media
.
AudioTrack
(
streamType
,
sampleRate
,
channelConfig
,
encoding
,
bufferSize
,
android
.
media
.
AudioTrack
.
MODE_STREAM
,
sessionId
);
}
checkAudioTrackInitialized
();
sessionId
=
audioTrack
.
getAudioSessionId
();
if
(
enablePreV21AudioSessionWorkaround
)
{
if
(
Util
.
SDK_INT
<
21
)
{
// The workaround creates an audio track with a two byte buffer on the same session, and
// does not release it until this object is released, which keeps the session active.
if
(
keepSessionIdAudioTrack
!=
null
&&
sessionId
!=
keepSessionIdAudioTrack
.
getAudioSessionId
())
{
releaseKeepSessionIdAudioTrack
();
}
if
(
keepSessionIdAudioTrack
==
null
)
{
int
sampleRate
=
4000
;
// Equal to private android.media.AudioTrack.MIN_SAMPLE_RATE.
int
channelConfig
=
AudioFormat
.
CHANNEL_OUT_MONO
;
int
encoding
=
AudioFormat
.
ENCODING_PCM_16BIT
;
int
bufferSize
=
2
;
// Use a two byte buffer, as it is not actually used for playback.
keepSessionIdAudioTrack
=
new
android
.
media
.
AudioTrack
(
streamType
,
sampleRate
,
channelConfig
,
encoding
,
bufferSize
,
android
.
media
.
AudioTrack
.
MODE_STATIC
,
sessionId
);
}
}
}
audioTrackUtil
.
reconfigure
(
audioTrack
,
needsPassthroughWorkarounds
());
setAudioTrackVolume
();
return
sessionId
;
}
/**
* Reconfigures the audio track to play back media in {@code format}, inferring a buffer size from
* the format.
* Configures (or reconfigures) the audio track to play back media in {@code format}, inferring a
* buffer size from the format.
*
* @param format Specifies the channel count and sample rate to play back.
* @param passthrough Whether to play back using a passthrough encoding.
*/
public
void
re
configure
(
MediaFormat
format
,
boolean
passthrough
)
{
re
configure
(
format
,
passthrough
,
0
);
public
void
configure
(
MediaFormat
format
,
boolean
passthrough
)
{
configure
(
format
,
passthrough
,
0
);
}
/**
*
Reconfigures
the audio track to play back media in {@code format}.
*
Configures (or reconfigures)
the audio track to play back media in {@code format}.
*
* @param format Specifies the channel count and sample rate to play back.
* @param passthrough Whether to playback using a passthrough encoding.
* @param specifiedBufferSize A specific size for the playback buffer in bytes, or 0 to use a
* size inferred from the format.
*/
public
void
re
configure
(
MediaFormat
format
,
boolean
passthrough
,
int
specifiedBufferSize
)
{
public
void
configure
(
MediaFormat
format
,
boolean
passthrough
,
int
specifiedBufferSize
)
{
int
channelCount
=
format
.
getInteger
(
MediaFormat
.
KEY_CHANNEL_COUNT
);
int
channelConfig
;
switch
(
channelCount
)
{
...
...
@@ -395,9 +358,21 @@ public final class AudioTrack {
case
2
:
channelConfig
=
AudioFormat
.
CHANNEL_OUT_STEREO
;
break
;
case
3
:
channelConfig
=
AudioFormat
.
CHANNEL_OUT_STEREO
|
AudioFormat
.
CHANNEL_OUT_FRONT_CENTER
;
break
;
case
4
:
channelConfig
=
AudioFormat
.
CHANNEL_OUT_QUAD
;
break
;
case
5
:
channelConfig
=
AudioFormat
.
CHANNEL_OUT_QUAD
|
AudioFormat
.
CHANNEL_OUT_FRONT_CENTER
;
break
;
case
6
:
channelConfig
=
AudioFormat
.
CHANNEL_OUT_5POINT1
;
break
;
case
7
:
channelConfig
=
AudioFormat
.
CHANNEL_OUT_5POINT1
|
AudioFormat
.
CHANNEL_OUT_BACK_CENTER
;
break
;
case
8
:
channelConfig
=
C
.
CHANNEL_OUT_7POINT1_SURROUND
;
break
;
...
...
@@ -438,11 +413,71 @@ public final class AudioTrack {
}
/**
* Initializes the audio track for writing new buffers using {@link #handleBuffer}.
*
* @return The audio track session identifier.
*/
public
int
initialize
()
throws
InitializationException
{
return
initialize
(
SESSION_ID_NOT_SET
);
}
/**
* Initializes the audio track for writing new buffers using {@link #handleBuffer}.
*
* @param sessionId Audio track session identifier to re-use, or {@link #SESSION_ID_NOT_SET} to
* create a new one.
* @return The new (or re-used) session identifier.
*/
public
int
initialize
(
int
sessionId
)
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
// the shared memory that's available for audio track buffers. This would in turn cause the
// initialization of the audio track to fail.
releasingConditionVariable
.
block
();
if
(
sessionId
==
SESSION_ID_NOT_SET
)
{
audioTrack
=
new
android
.
media
.
AudioTrack
(
streamType
,
sampleRate
,
channelConfig
,
encoding
,
bufferSize
,
android
.
media
.
AudioTrack
.
MODE_STREAM
);
}
else
{
// Re-attach to the same audio session.
audioTrack
=
new
android
.
media
.
AudioTrack
(
streamType
,
sampleRate
,
channelConfig
,
encoding
,
bufferSize
,
android
.
media
.
AudioTrack
.
MODE_STREAM
,
sessionId
);
}
checkAudioTrackInitialized
();
sessionId
=
audioTrack
.
getAudioSessionId
();
if
(
enablePreV21AudioSessionWorkaround
)
{
if
(
Util
.
SDK_INT
<
21
)
{
// The workaround creates an audio track with a two byte buffer on the same session, and
// does not release it until this object is released, which keeps the session active.
if
(
keepSessionIdAudioTrack
!=
null
&&
sessionId
!=
keepSessionIdAudioTrack
.
getAudioSessionId
())
{
releaseKeepSessionIdAudioTrack
();
}
if
(
keepSessionIdAudioTrack
==
null
)
{
int
sampleRate
=
4000
;
// Equal to private android.media.AudioTrack.MIN_SAMPLE_RATE.
int
channelConfig
=
AudioFormat
.
CHANNEL_OUT_MONO
;
int
encoding
=
AudioFormat
.
ENCODING_PCM_16BIT
;
int
bufferSize
=
2
;
// Use a two byte buffer, as it is not actually used for playback.
keepSessionIdAudioTrack
=
new
android
.
media
.
AudioTrack
(
streamType
,
sampleRate
,
channelConfig
,
encoding
,
bufferSize
,
android
.
media
.
AudioTrack
.
MODE_STATIC
,
sessionId
);
}
}
}
audioTrackUtil
.
reconfigure
(
audioTrack
,
needsPassthroughWorkarounds
());
setAudioTrackVolume
();
return
sessionId
;
}
/**
* Returns the size of this {@link AudioTrack}'s buffer in microseconds, given its current
* configuration.
* <p>
* The duration returned from this method may change as a result of calling one of the
* {@link #
re
configure} methods.
* {@link #configure} methods.
*
* @return The size of the buffer in microseconds.
*/
...
...
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