|
|
@ -10,10 +10,12 @@ import android.util.Log; |
|
|
|
* This is independent from the channels count, as long as the read bytes include |
|
|
|
* This is independent from the channels count, as long as the read bytes include |
|
|
|
* all channels and the byte rate accounts for this as well. |
|
|
|
* all channels and the byte rate accounts for this as well. |
|
|
|
* If channels is 2, both values will be doubled and we behave the same. |
|
|
|
* If channels is 2, both values will be doubled and we behave the same. |
|
|
|
|
|
|
|
* |
|
|
|
|
|
|
|
* This class keeps track of gaps between frames. |
|
|
|
|
|
|
|
* This can be used, for example, to write zeros instead of nothing. |
|
|
|
*/ |
|
|
|
*/ |
|
|
|
class AudioTimestamp { |
|
|
|
class AudioTimestamp { |
|
|
|
|
|
|
|
|
|
|
|
@SuppressWarnings("WeakerAccess") |
|
|
|
|
|
|
|
static long bytesToUs(long bytes, int byteRate) { |
|
|
|
static long bytesToUs(long bytes, int byteRate) { |
|
|
|
return (1000000L * bytes) / byteRate; |
|
|
|
return (1000000L * bytes) / byteRate; |
|
|
|
} |
|
|
|
} |
|
|
@ -22,10 +24,15 @@ class AudioTimestamp { |
|
|
|
return (1000L * bytes) / byteRate; |
|
|
|
return (1000L * bytes) / byteRate; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private int mByteRate; |
|
|
|
private long mBaseTimeUs; |
|
|
|
private long mBaseTimeUs; |
|
|
|
private long mBytesSinceBaseTime; |
|
|
|
private long mBytesSinceBaseTime; |
|
|
|
private long mGapUs; |
|
|
|
private long mGapUs; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
AudioTimestamp(int byteRate) { |
|
|
|
|
|
|
|
mByteRate = byteRate; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
/** |
|
|
|
* This method accounts for the current time and proved to be the most reliable among |
|
|
|
* This method accounts for the current time and proved to be the most reliable among |
|
|
|
* the ones tested. |
|
|
|
* the ones tested. |
|
|
@ -37,8 +44,8 @@ class AudioTimestamp { |
|
|
|
* Returns timestamps in the {@link System#nanoTime()} reference. |
|
|
|
* Returns timestamps in the {@link System#nanoTime()} reference. |
|
|
|
*/ |
|
|
|
*/ |
|
|
|
@SuppressWarnings("SameParameterValue") |
|
|
|
@SuppressWarnings("SameParameterValue") |
|
|
|
long increaseUs(int readBytes, int byteRate) { |
|
|
|
long increaseUs(int readBytes) { |
|
|
|
long bufferDurationUs = bytesToUs((long) readBytes, byteRate); |
|
|
|
long bufferDurationUs = bytesToUs((long) readBytes, mByteRate); |
|
|
|
long bufferEndTimeUs = System.nanoTime() / 1000; // now
|
|
|
|
long bufferEndTimeUs = System.nanoTime() / 1000; // now
|
|
|
|
long bufferStartTimeUs = bufferEndTimeUs - bufferDurationUs; |
|
|
|
long bufferStartTimeUs = bufferEndTimeUs - bufferDurationUs; |
|
|
|
|
|
|
|
|
|
|
@ -48,7 +55,7 @@ class AudioTimestamp { |
|
|
|
// Recompute time assuming that we are respecting the sampling frequency.
|
|
|
|
// Recompute time assuming that we are respecting the sampling frequency.
|
|
|
|
// This puts the time at the end of last read buffer, which means, where we
|
|
|
|
// This puts the time at the end of last read buffer, which means, where we
|
|
|
|
// should be if we had no delay / missed buffers.
|
|
|
|
// should be if we had no delay / missed buffers.
|
|
|
|
long correctedTimeUs = mBaseTimeUs + bytesToUs(mBytesSinceBaseTime, byteRate); |
|
|
|
long correctedTimeUs = mBaseTimeUs + bytesToUs(mBytesSinceBaseTime, mByteRate); |
|
|
|
long correctionUs = bufferStartTimeUs - correctedTimeUs; |
|
|
|
long correctionUs = bufferStartTimeUs - correctedTimeUs; |
|
|
|
|
|
|
|
|
|
|
|
// However, if the correction is too big (> 2*bufferDurationUs), reset to this point.
|
|
|
|
// However, if the correction is too big (> 2*bufferDurationUs), reset to this point.
|
|
|
@ -65,14 +72,27 @@ class AudioTimestamp { |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// This is guaranteed to be > 1 (actually > 2, since 2 is the constant we
|
|
|
|
/** |
|
|
|
// use in the correction check).
|
|
|
|
* Returns the number of gaps (meaning, missing frames) assuming that each |
|
|
|
int getGapCount(int frameBytes, int byteRate) { |
|
|
|
* frame has frameBytes size. Possibly 0. |
|
|
|
|
|
|
|
* |
|
|
|
|
|
|
|
* @param frameBytes size of standard frame |
|
|
|
|
|
|
|
* @return number of gaps |
|
|
|
|
|
|
|
*/ |
|
|
|
|
|
|
|
int getGapCount(int frameBytes) { |
|
|
|
if (mGapUs == 0) return 0; |
|
|
|
if (mGapUs == 0) return 0; |
|
|
|
long durationUs = bytesToUs((long) frameBytes, byteRate); |
|
|
|
long durationUs = bytesToUs((long) frameBytes, mByteRate); |
|
|
|
return (int) (mGapUs / durationUs); |
|
|
|
return (int) (mGapUs / durationUs); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
|
|
* Returns the timestamp of the first missing frame. |
|
|
|
|
|
|
|
* Should be called only after {@link #getGapCount(int)} returns something |
|
|
|
|
|
|
|
* greater than zero. |
|
|
|
|
|
|
|
* |
|
|
|
|
|
|
|
* @param lastTimeUs the last real frame timestamp |
|
|
|
|
|
|
|
* @return the first missing frame timestamp |
|
|
|
|
|
|
|
*/ |
|
|
|
long getGapStartUs(long lastTimeUs) { |
|
|
|
long getGapStartUs(long lastTimeUs) { |
|
|
|
return lastTimeUs - mGapUs; |
|
|
|
return lastTimeUs - mGapUs; |
|
|
|
} |
|
|
|
} |
|
|
|