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
a4f1e3ce
authored
Nov 17, 2015
by
Oliver Woodman
Browse files
Options
_('Browse Files')
Download
Email Patches
Plain Diff
Don't re-buffer when AudioTrack underruns occur.
parent
80d69992
Hide whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
96 additions
and
13 deletions
demo/src/main/java/com/google/android/exoplayer/demo/EventLogger.java
demo/src/main/java/com/google/android/exoplayer/demo/player/DemoPlayer.java
library/src/main/java/com/google/android/exoplayer/MediaCodecAudioTrackRenderer.java
library/src/main/java/com/google/android/exoplayer/audio/AudioTrack.java
playbacktests/src/main/java/com/google/android/exoplayer/playbacktests/util/LogcatLogger.java
demo/src/main/java/com/google/android/exoplayer/demo/EventLogger.java
View file @
a4f1e3ce
...
...
@@ -160,6 +160,12 @@ public class EventLogger implements DemoPlayer.Listener, DemoPlayer.InfoListener
}
@Override
public
void
onAudioTrackUnderrun
(
long
audioTrackBufferSizeMs
,
long
elapsedSinceLastFeedMs
)
{
printInternalError
(
"audioTrackUnderrun ["
+
audioTrackBufferSizeMs
+
", "
+
elapsedSinceLastFeedMs
+
"]"
,
null
);
}
@Override
public
void
onCryptoError
(
CryptoException
e
)
{
printInternalError
(
"cryptoError"
,
e
);
}
...
...
demo/src/main/java/com/google/android/exoplayer/demo/player/DemoPlayer.java
View file @
a4f1e3ce
...
...
@@ -105,6 +105,7 @@ public class DemoPlayer implements ExoPlayer.Listener, ChunkSampleSource.EventLi
void
onRendererInitializationError
(
Exception
e
);
void
onAudioTrackInitializationError
(
AudioTrack
.
InitializationException
e
);
void
onAudioTrackWriteError
(
AudioTrack
.
WriteException
e
);
void
onAudioTrackUnderrun
(
long
audioTrackBufferSizeMs
,
long
elapsedSinceLastFeedMs
);
void
onDecoderInitializationError
(
DecoderInitializationException
e
);
void
onCryptoError
(
CryptoException
e
);
void
onLoadError
(
int
sourceId
,
IOException
e
);
...
...
@@ -482,6 +483,13 @@ public class DemoPlayer implements ExoPlayer.Listener, ChunkSampleSource.EventLi
}
@Override
public
void
onAudioTrackUnderrun
(
long
audioTrackBufferSizeMs
,
long
elapsedSinceLastFeedMs
)
{
if
(
internalErrorListener
!=
null
)
{
internalErrorListener
.
onAudioTrackUnderrun
(
audioTrackBufferSizeMs
,
elapsedSinceLastFeedMs
);
}
}
@Override
public
void
onCryptoError
(
CryptoException
e
)
{
if
(
internalErrorListener
!=
null
)
{
internalErrorListener
.
onCryptoError
(
e
);
...
...
library/src/main/java/com/google/android/exoplayer/MediaCodecAudioTrackRenderer.java
View file @
a4f1e3ce
...
...
@@ -26,6 +26,7 @@ import android.media.AudioManager;
import
android.media.MediaCodec
;
import
android.media.audiofx.Virtualizer
;
import
android.os.Handler
;
import
android.os.SystemClock
;
import
java.nio.ByteBuffer
;
...
...
@@ -55,6 +56,14 @@ public class MediaCodecAudioTrackRenderer extends MediaCodecTrackRenderer implem
*/
void
onAudioTrackWriteError
(
AudioTrack
.
WriteException
e
);
/**
* Invoked when an {@link AudioTrack} underrun occurs.
*
* @param audioTrackBufferSizeMs The size of the {@link AudioTrack}'s buffer, in milliseconds.
* @param elapsedSinceLastFeedMs The time since the {@link AudioTrack} was last fed data.
*/
void
onAudioTrackUnderrun
(
long
audioTrackBufferSizeMs
,
long
elapsedSinceLastFeedMs
);
}
/**
...
...
@@ -77,6 +86,9 @@ public class MediaCodecAudioTrackRenderer extends MediaCodecTrackRenderer implem
private
long
currentPositionUs
;
private
boolean
allowPositionDiscontinuity
;
private
boolean
audioTrackHasData
;
private
long
lastFeedElapsedRealtimeMs
;
/**
* @param source The upstream source from which the renderer obtains samples.
*/
...
...
@@ -272,8 +284,7 @@ public class MediaCodecAudioTrackRenderer extends MediaCodecTrackRenderer implem
@Override
protected
boolean
isReady
()
{
return
audioTrack
.
hasPendingData
()
||
(
super
.
isReady
()
&&
getSourceState
()
==
SOURCE_STATE_READY_READ_MAY_FAIL
);
return
audioTrack
.
hasPendingData
()
||
super
.
isReady
();
}
@Override
...
...
@@ -321,8 +332,8 @@ public class MediaCodecAudioTrackRenderer extends MediaCodecTrackRenderer implem
return
true
;
}
// Initialize and start the audio track now.
if
(!
audioTrack
.
isInitialized
())
{
// Initialize the AudioTrack now.
try
{
if
(
audioSessionId
!=
AudioTrack
.
SESSION_ID_NOT_SET
)
{
audioTrack
.
initialize
(
audioSessionId
);
...
...
@@ -330,20 +341,29 @@ public class MediaCodecAudioTrackRenderer extends MediaCodecTrackRenderer implem
audioSessionId
=
audioTrack
.
initialize
();
onAudioSessionId
(
audioSessionId
);
}
audioTrackHasData
=
false
;
}
catch
(
AudioTrack
.
InitializationException
e
)
{
notifyAudioTrackInitializationError
(
e
);
throw
new
ExoPlaybackException
(
e
);
}
if
(
getState
()
==
TrackRenderer
.
STATE_STARTED
)
{
audioTrack
.
play
();
}
}
else
{
// Check for AudioTrack underrun.
boolean
audioTrackHadData
=
audioTrackHasData
;
audioTrackHasData
=
audioTrack
.
hasPendingData
();
if
(
audioTrackHadData
&&
!
audioTrackHasData
&&
getState
()
==
TrackRenderer
.
STATE_STARTED
)
{
long
elapsedSinceLastFeedMs
=
SystemClock
.
elapsedRealtime
()
-
lastFeedElapsedRealtimeMs
;
notifyAudioTrackUnderrun
(
audioTrack
.
getBufferSizeUs
()
/
1000
,
elapsedSinceLastFeedMs
);
}
}
int
handleBufferResult
;
try
{
handleBufferResult
=
audioTrack
.
handleBuffer
(
buffer
,
bufferInfo
.
offset
,
bufferInfo
.
size
,
bufferInfo
.
presentationTimeUs
);
lastFeedElapsedRealtimeMs
=
SystemClock
.
elapsedRealtime
();
}
catch
(
AudioTrack
.
WriteException
e
)
{
notifyAudioTrackWriteError
(
e
);
throw
new
ExoPlaybackException
(
e
);
...
...
@@ -405,4 +425,16 @@ public class MediaCodecAudioTrackRenderer extends MediaCodecTrackRenderer implem
}
}
private
void
notifyAudioTrackUnderrun
(
final
long
audioTrackBufferSizeMs
,
final
long
elapsedSinceLastFeedMs
)
{
if
(
eventHandler
!=
null
&&
eventListener
!=
null
)
{
eventHandler
.
post
(
new
Runnable
()
{
@Override
public
void
run
()
{
eventListener
.
onAudioTrackUnderrun
(
audioTrackBufferSizeMs
,
elapsedSinceLastFeedMs
);
}
});
}
}
}
library/src/main/java/com/google/android/exoplayer/audio/AudioTrack.java
View file @
a4f1e3ce
...
...
@@ -181,6 +181,7 @@ public final class AudioTrack {
private
int
frameSize
;
private
int
minBufferSize
;
private
int
bufferSize
;
private
long
bufferSizeUs
;
private
int
nextPlayheadOffsetIndex
;
private
int
playheadOffsetCount
;
...
...
@@ -433,9 +434,25 @@ public final class AudioTrack {
:
multipliedBufferSize
>
maxAppBufferSize
?
maxAppBufferSize
:
multipliedBufferSize
;
}
bufferSizeUs
=
framesToDurationUs
(
bytesToFrames
(
bufferSize
));
}
/** Starts/resumes playing audio if the audio track has been initialized. */
/**
* 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 #reconfigure} methods.
*
* @return The size of the buffer in microseconds.
*/
public
long
getBufferSizeUs
()
{
return
bufferSizeUs
;
}
/**
* Starts or resumes playing audio if the audio track has been initialized.
*/
public
void
play
()
{
if
(
isInitialized
())
{
resumeSystemTimeUs
=
System
.
nanoTime
()
/
1000
;
...
...
@@ -443,7 +460,9 @@ public final class AudioTrack {
}
}
/** Signals to the audio track that the next buffer is discontinuous with the previous buffer. */
/**
* Signals to the audio track that the next buffer is discontinuous with the previous buffer.
*/
public
void
handleDiscontinuity
()
{
// Force resynchronization after a skipped buffer.
if
(
startMediaTimeState
==
START_IN_SYNC
)
{
...
...
@@ -584,14 +603,18 @@ public final class AudioTrack {
return
audioTrack
.
write
(
buffer
,
size
,
android
.
media
.
AudioTrack
.
WRITE_NON_BLOCKING
);
}
/** Returns whether the audio track has more data pending that will be played back. */
/**
* Returns whether the audio track has more data pending that will be played back.
*/
public
boolean
hasPendingData
()
{
return
isInitialized
()
&&
(
bytesToFrames
(
submittedBytes
)
>
audioTrackUtil
.
getPlaybackHeadPosition
()
||
overrideHasPendingData
());
}
/** Sets the playback volume. */
/**
* Sets the playback volume.
*/
public
void
setVolume
(
float
volume
)
{
if
(
this
.
volume
!=
volume
)
{
this
.
volume
=
volume
;
...
...
@@ -619,7 +642,9 @@ public final class AudioTrack {
audioTrack
.
setStereoVolume
(
volume
,
volume
);
}
/** Pauses playback. */
/**
* Pauses playback.
*/
public
void
pause
()
{
if
(
isInitialized
())
{
resetSyncParams
();
...
...
@@ -662,13 +687,17 @@ public final class AudioTrack {
}
}
/** Releases all resources associated with this instance. */
/**
* Releases all resources associated with this instance.
*/
public
void
release
()
{
reset
();
releaseKeepSessionIdAudioTrack
();
}
/** Releases {@link #keepSessionIdAudioTrack} asynchronously, if it is non-{@code null}. */
/**
* Releases {@link #keepSessionIdAudioTrack} asynchronously, if it is non-{@code null}.
*/
private
void
releaseKeepSessionIdAudioTrack
()
{
if
(
keepSessionIdAudioTrack
==
null
)
{
return
;
...
...
@@ -685,7 +714,9 @@ public final class AudioTrack {
}.
start
();
}
/** Returns whether {@link #getCurrentPositionUs} can return the current playback position. */
/**
* Returns whether {@link #getCurrentPositionUs} can return the current playback position.
*/
private
boolean
hasCurrentPositionUs
()
{
return
isInitialized
()
&&
startMediaTimeState
!=
START_NOT_SET
;
}
...
...
@@ -757,7 +788,7 @@ public final class AudioTrack {
// Compute the audio track latency, excluding the latency due to the buffer (leaving
// latency due to the mixer and audio hardware driver).
latencyUs
=
(
Integer
)
getLatencyMethod
.
invoke
(
audioTrack
,
(
Object
[])
null
)
*
1000L
-
framesToDurationUs
(
bytesToFrames
(
bufferSize
))
;
-
bufferSizeUs
;
// Sanity check that the latency is non-negative.
latencyUs
=
Math
.
max
(
latencyUs
,
0
);
// Sanity check that the latency isn't too large.
...
...
playbacktests/src/main/java/com/google/android/exoplayer/playbacktests/util/LogcatLogger.java
View file @
a4f1e3ce
...
...
@@ -105,6 +105,12 @@ public final class LogcatLogger implements ExoPlayer.Listener,
}
@Override
public
void
onAudioTrackUnderrun
(
long
audioTrackBufferSizeMs
,
long
elapsedSinceLastFeedMs
)
{
Log
.
e
(
tag
,
"Audio track underrun ("
+
audioTrackBufferSizeMs
+
", "
+
elapsedSinceLastFeedMs
+
")"
);
}
@Override
public
void
onDroppedFrames
(
int
count
,
long
elapsed
)
{
Log
.
w
(
tag
,
"Dropped frames ("
+
count
+
")"
);
}
...
...
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