Improve comments

pull/506/head
Mattia Iavarone 6 years ago
parent 4e4041ab07
commit 3f876044a4
  1. 15
      cameraview/src/main/java/com/otaliastudios/cameraview/video/encoding/AudioMediaEncoder.java
  2. 36
      cameraview/src/main/java/com/otaliastudios/cameraview/video/encoding/AudioTimestamp.java

@ -103,7 +103,7 @@ public class AudioMediaEncoder extends MediaEncoder {
public AudioMediaEncoder(@NonNull Config config) { public AudioMediaEncoder(@NonNull Config config) {
super("AudioEncoder"); super("AudioEncoder");
mConfig = config.copy(); mConfig = config.copy();
mTimestamp = new AudioTimestamp(); mTimestamp = new AudioTimestamp(mConfig.byteRate());
// These two were in onPrepare() but it's better to do warm-up here // These two were in onPrepare() but it's better to do warm-up here
// since thread and looper creation is expensive. // since thread and looper creation is expensive.
mEncoder = new AudioEncodingHandler(); mEncoder = new AudioEncodingHandler();
@ -262,19 +262,28 @@ public class AudioMediaEncoder extends MediaEncoder {
* @param endOfStream end of stream? * @param endOfStream end of stream?
*/ */
private void increaseTime(int readBytes, boolean endOfStream) { private void increaseTime(int readBytes, boolean endOfStream) {
mLastTimeUs = mTimestamp.increaseUs(readBytes, mConfig.byteRate()); // Get the latest frame timestamp.
mLastTimeUs = mTimestamp.increaseUs(readBytes);
if (mFirstTimeUs == Long.MIN_VALUE) { if (mFirstTimeUs == Long.MIN_VALUE) {
mFirstTimeUs = mLastTimeUs; mFirstTimeUs = mLastTimeUs;
// Compute the first frame milliseconds as well. // Compute the first frame milliseconds as well.
notifyFirstFrameMillis(System.currentTimeMillis() notifyFirstFrameMillis(System.currentTimeMillis()
- AudioTimestamp.bytesToMillis(readBytes, mConfig.byteRate())); - AudioTimestamp.bytesToMillis(readBytes, mConfig.byteRate()));
} }
// See if we reached the max length value.
boolean didReachMaxLength = (mLastTimeUs - mFirstTimeUs) > getMaxLengthMillis() * 1000L; boolean didReachMaxLength = (mLastTimeUs - mFirstTimeUs) > getMaxLengthMillis() * 1000L;
if (didReachMaxLength && !endOfStream) { if (didReachMaxLength && !endOfStream) {
LOG.w("read thread - this frame reached the maxLength! deltaUs:", mLastTimeUs - mFirstTimeUs); LOG.w("read thread - this frame reached the maxLength! deltaUs:", mLastTimeUs - mFirstTimeUs);
notifyMaxLengthReached(); notifyMaxLengthReached();
} }
int gaps = mTimestamp.getGapCount(mConfig.frameSize(), mConfig.byteRate());
// Add zeroes if we have huge gaps. Even if timestamps are correct, if we have gaps between
// them, the encoder might shrink all timestamps to have a continuous audio. This results
// in a video that is fast-forwarded.
// Adding zeroes does not solve the gaps issue - audio will still be distorted. But at
// least we get a video that has the correct playback speed.
int gaps = mTimestamp.getGapCount(mConfig.frameSize());
if (gaps > 0) { if (gaps > 0) {
long gapStart = mTimestamp.getGapStartUs(mLastTimeUs); long gapStart = mTimestamp.getGapStartUs(mLastTimeUs);
long frameUs = AudioTimestamp.bytesToUs(mConfig.frameSize(), mConfig.byteRate()); long frameUs = AudioTimestamp.bytesToUs(mConfig.frameSize(), mConfig.byteRate());

@ -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;
} }

Loading…
Cancel
Save