AudioTrackTest: test FastTrack timestamps
Bug: 26413951
Change-Id: Ib5b1f2a7eb180ea0f39c006565c33259729e5ec5
diff --git a/tests/tests/media/src/android/media/cts/AudioTrackTest.java b/tests/tests/media/src/android/media/cts/AudioTrackTest.java
index 26582dc..169434c 100644
--- a/tests/tests/media/src/android/media/cts/AudioTrackTest.java
+++ b/tests/tests/media/src/android/media/cts/AudioTrackTest.java
@@ -1907,56 +1907,86 @@
public void testGetTimestamp() throws Exception {
if (!hasAudioOutput()) {
- Log.w(TAG,"AUDIO_OUTPUT feature not found. This system might not have a valid "
+ Log.w(TAG, "AUDIO_OUTPUT feature not found. This system might not have a valid "
+ "audio output HAL");
return;
}
+ doTestTimestamp(
+ 22050 /* sampleRate */,
+ AudioFormat.CHANNEL_OUT_MONO ,
+ AudioFormat.ENCODING_PCM_16BIT,
+ AudioTrack.MODE_STREAM);
+ }
+
+ public void testFastTimestamp() throws Exception {
+ if (!hasAudioOutput()) {
+ Log.w(TAG, "AUDIO_OUTPUT feature not found. This system might not have a valid "
+ + "audio output HAL");
+ return;
+ }
+ doTestTimestamp(
+ AudioTrack.getNativeOutputSampleRate(AudioManager.STREAM_MUSIC),
+ AudioFormat.CHANNEL_OUT_MONO,
+ AudioFormat.ENCODING_PCM_16BIT,
+ AudioTrack.MODE_STREAM);
+ }
+
+ private void doTestTimestamp(
+ int sampleRate, int channelMask, int encoding, int transferMode) throws Exception {
// constants for test
final String TEST_NAME = "testGetTimestamp";
- final int TEST_SR = 22050;
- final int TEST_CONF = AudioFormat.CHANNEL_OUT_MONO;
- final int TEST_FORMAT = AudioFormat.ENCODING_PCM_16BIT;
- final int TEST_MODE = AudioTrack.MODE_STREAM;
- final int TEST_STREAM_TYPE = AudioManager.STREAM_MUSIC;
final int TEST_LOOP_CNT = 10;
+ final int TEST_BUFFER_MS = 100;
+ final int TEST_USAGE = AudioAttributes.USAGE_MEDIA;
// For jitter we allow 30 msec in frames. This is a large margin.
// Often this is just 0 or 1 frames, but that can depend on hardware.
- final int TEST_JITTER_FRAMES_ALLOWED = TEST_SR * 30 / 1000;
+ final int TEST_JITTER_FRAMES_ALLOWED = sampleRate * 30 / 1000;
// -------- initialization --------------
- final int bytesPerFrame =
- AudioFormat.getBytesPerSample(TEST_FORMAT)
- * AudioFormat.channelCountFromOutChannelMask(TEST_CONF);
- final int minBufferSizeInBytes =
- AudioTrack.getMinBufferSize(TEST_SR, TEST_CONF, TEST_FORMAT);
- final int bufferSizeInBytes = minBufferSizeInBytes * 3;
- byte[] data = new byte[bufferSizeInBytes];
- AudioTrack track = new AudioTrack(TEST_STREAM_TYPE, TEST_SR, TEST_CONF, TEST_FORMAT,
- minBufferSizeInBytes, TEST_MODE);
- // -------- test --------------
- assertTrue(TEST_NAME, track.getState() == AudioTrack.STATE_INITIALIZED);
+ final int frameSize =
+ AudioFormat.getBytesPerSample(encoding)
+ * AudioFormat.channelCountFromOutChannelMask(channelMask);
+ final int frameCount = sampleRate * TEST_BUFFER_MS / 1000;
+ // see whether we can use fast mode
+ final boolean fast =
+ sampleRate == AudioTrack.getNativeOutputSampleRate(AudioManager.STREAM_MUSIC);
+ AudioAttributes attributes = (fast ? new AudioAttributes.Builder()
+ .setFlags(AudioAttributes.FLAG_LOW_LATENCY) : new AudioAttributes.Builder())
+ .setUsage(TEST_USAGE)
+ .build();
+ AudioFormat format = new AudioFormat.Builder()
+ //.setChannelIndexMask((1 << AudioFormat.channelCountFromOutChannelMask(channelMask)) - 1)
+ .setChannelMask(channelMask)
+ .setEncoding(encoding)
+ .setSampleRate(sampleRate)
+ .build();
+ AudioTrack track = new AudioTrack.Builder()
+ .setAudioAttributes(attributes)
+ .setAudioFormat(format)
+ .setBufferSizeInBytes(AudioTrack.getMinBufferSize(
+ sampleRate, channelMask, encoding))
+ .setTransferMode(transferMode)
+ .build();
+ assertEquals(AudioTrack.STATE_INITIALIZED, track.getState());
+ track.play();
+
+ ByteBuffer data = ByteBuffer.allocate(frameCount * frameSize);
+ data.order(java.nio.ByteOrder.nativeOrder()).limit(frameCount * frameSize);
AudioTimestamp timestamp = new AudioTimestamp();
- boolean hasPlayed = false;
-
long framesWritten = 0, lastFramesPresented = 0, lastFramesPresentedAt = 0;
int cumulativeJitterCount = 0;
+ int differentials = 0;
float cumulativeJitter = 0;
float maxJitter = 0;
for (int i = 0; i < TEST_LOOP_CNT; i++) {
final long writeTime = System.nanoTime();
- for (int written = 0; written < data.length;) {
- int ret = track.write(data, written,
- Math.min(data.length - written, minBufferSizeInBytes));
- assertTrue(TEST_NAME, ret >= 0);
- written += ret;
- if (!hasPlayed) {
- track.play();
- hasPlayed = true;
- }
- }
- framesWritten += data.length / bytesPerFrame;
+ data.position(0);
+ assertEquals(data.limit(),
+ track.write(data, data.limit(), AudioTrack.WRITE_BLOCKING));
+ assertEquals(data.position(), data.limit());
+ framesWritten += data.limit() / frameSize;
// track.getTimestamp may return false if there are no physical HAL outputs.
// This may occur on TV devices without connecting an HDMI monitor.
@@ -1966,7 +1996,7 @@
// Nevertheless, we don't want to have unnecessary failures, so we ignore the
// first iteration if we don't get a timestamp.
final boolean result = track.getTimestamp(timestamp);
- assertTrue(TEST_NAME, result || i == 0);
+ assertTrue("timestamp could not be read", result || i == 0);
if (!result) {
continue;
}
@@ -1978,42 +2008,52 @@
// This is an "on-the-fly" read without pausing because pausing may cause the
// timestamp to become stale and affect our jitter measurements.
final int framesSeen = track.getPlaybackHeadPosition();
- assertTrue(TEST_NAME, framesWritten >= framesSeen);
- assertTrue(TEST_NAME, framesSeen >= framesPresented);
+ assertTrue("server frames ahead of client frames", framesWritten >= framesSeen);
+ assertTrue("presented frames ahead of server frames", framesSeen >= framesPresented);
- if (i > 1) { // need delta info from previous iteration (skipping first)
+ if (i > 0) { // need delta info from previous iteration (skipping first)
final long deltaFrames = framesPresented - lastFramesPresented;
final long deltaTime = framesPresentedAt - lastFramesPresentedAt;
final long NANOSECONDS_PER_SECOND = 1000000000;
- final long expectedFrames = deltaTime * TEST_SR / NANOSECONDS_PER_SECOND;
+ final long expectedFrames = deltaTime * sampleRate / NANOSECONDS_PER_SECOND;
final long jitterFrames = Math.abs(deltaFrames - expectedFrames);
- //Log.d(TAG, "framesWritten(" + framesWritten
- // + ") framesSeen(" + framesSeen
- // + ") framesPresented(" + framesPresented
- // + ") jitter(" + jitterFrames + ")");
+ Log.d(TAG, "framesWritten(" + framesWritten
+ + ") framesSeen(" + framesSeen
+ + ") framesPresented(" + framesPresented
+ + ") framesPresentedAt(" + framesPresentedAt
+ + ") lastframesPresented(" + lastFramesPresented
+ + ") lastFramesPresentedAt(" + lastFramesPresentedAt
+ + ") deltaFrames(" + deltaFrames
+ + ") deltaTime(" + deltaTime
+ + ") expectedFrames(" + expectedFrames
+ + ") writeTime(" + writeTime
+ + ") jitter(" + jitterFrames + ")");
+ assertTrue("timestamp time should be increasing", deltaTime >= 0);
+ assertTrue("timestamp frames should be increasing", deltaFrames >= 0);
- // We check that the timestamp position is reasonably accurate.
- assertTrue(TEST_NAME, deltaTime >= 0);
- assertTrue(TEST_NAME, deltaFrames >= 0);
- if (i > 2) {
- // The first two periods may have inherent jitter as the audio pipe
- // is filling up. We check jitter only after that.
- assertTrue(TEST_NAME, jitterFrames < TEST_JITTER_FRAMES_ALLOWED);
- cumulativeJitter += jitterFrames;
- cumulativeJitterCount++;
- if (jitterFrames > maxJitter) {
- maxJitter = jitterFrames;
+ // the first nonzero value may have a jump, wait for the second.
+ if (lastFramesPresented != 0) {
+ if (differentials++ > 1) {
+ // We check that the timestamp position is reasonably accurate.
+ assertTrue("jitterFrames(" + jitterFrames + ") < "
+ + TEST_JITTER_FRAMES_ALLOWED,
+ jitterFrames < TEST_JITTER_FRAMES_ALLOWED);
+ cumulativeJitter += jitterFrames;
+ cumulativeJitterCount++;
+ if (jitterFrames > maxJitter) {
+ maxJitter = jitterFrames;
+ }
}
+ final long NANOS_PER_MILLIS = 1000000;
+ final long closeTimeNs = TEST_BUFFER_MS * 2 * NANOS_PER_MILLIS;
+ // We check that the timestamp time is reasonably current.
+ assertTrue("framesPresentedAt(" + framesPresentedAt
+ + ") close to writeTime(" + writeTime
+ + ")", Math.abs(framesPresentedAt - writeTime) <= closeTimeNs);
+ assertTrue("timestamps must have causal time",
+ writeTime >= lastFramesPresentedAt);
}
-
- //Log.d(TAG, "lastFramesPresentedAt(" + lastFramesPresentedAt
- // + ") writeTime(" + writeTime
- // + ") framesPresentedAt(" + framesPresentedAt + ")");
-
- // We check that the timestamp time is reasonably current.
- assertTrue(TEST_NAME, framesPresentedAt >= writeTime);
- assertTrue(TEST_NAME, writeTime >= lastFramesPresentedAt);
}
lastFramesPresented = framesPresented;
lastFramesPresentedAt = framesPresentedAt;
@@ -2021,18 +2061,19 @@
// Full drain.
Thread.sleep(1000 /* millis */);
// check that we are really at the end of playback.
- assertTrue(TEST_NAME, track.getTimestamp(timestamp));
- assertEquals(framesWritten, timestamp.framePosition);
+ assertTrue("timestamp should be valid while draining", track.getTimestamp(timestamp));
+ if (!fast) { // fast tracks may not fully drain
+ assertEquals("timestamp should fully drain", framesWritten, timestamp.framePosition);
+ }
+ assertTrue("sufficient nonzero timestamps", differentials > 2);
- track.stop();
- Thread.sleep(WAIT_MSEC);
track.release();
// Log the average jitter
if (cumulativeJitterCount > 0) {
DeviceReportLog log = new DeviceReportLog();
final float averageJitterInFrames = cumulativeJitter / cumulativeJitterCount;
- final float averageJitterInMs = averageJitterInFrames * 1000 / TEST_SR;
- final float maxJitterInMs = maxJitter * 1000 / TEST_SR;
+ final float averageJitterInMs = averageJitterInFrames * 1000 / sampleRate;
+ final float maxJitterInMs = maxJitter * 1000 / sampleRate;
// ReportLog needs at least one Value and Summary.
log.addValue("Maximum Jitter", maxJitterInMs,
ResultType.LOWER_BETTER, ResultUnit.MS);