Anticipate max length detection

pull/506/head
Mattia Iavarone 6 years ago
parent 38abe24228
commit 7fa0c595c3
  1. 18
      cameraview/src/main/java/com/otaliastudios/cameraview/video/encoding/AudioMediaEncoder.java
  2. 1
      cameraview/src/main/java/com/otaliastudios/cameraview/video/encoding/InputBuffer.java
  3. 35
      cameraview/src/main/java/com/otaliastudios/cameraview/video/encoding/MediaEncoder.java

@ -56,7 +56,7 @@ public class AudioMediaEncoder extends MediaEncoder {
// This value is the number of runnables that the encoder thread is allowed to be 'behind' // This value is the number of runnables that the encoder thread is allowed to be 'behind'
// the recorder thread. It's not safe to have it very large or we can end encoding A LOT AFTER // the recorder thread. It's not safe to have it very large or we can end encoding A LOT AFTER
// the actual recording. It's better to reduce this and skip recording at all. // the actual recording. It's better to reduce this and skip recording at all.
private static final int BUFFER_POOL_MAX_SIZE = 100; private static final int BUFFER_POOL_MAX_SIZE = 80;
private static long bytesToUs(int bytes) { private static long bytesToUs(int bytes) {
return (1000000L * bytes) / BYTE_RATE; return (1000000L * bytes) / BYTE_RATE;
@ -182,11 +182,11 @@ public class AudioMediaEncoder extends MediaEncoder {
// Sleeping before returning is a good way of balancing the two operations. // Sleeping before returning is a good way of balancing the two operations.
// However, if endOfStream, we CAN'T lose this frame! // However, if endOfStream, we CAN'T lose this frame!
if (endOfStream) { if (endOfStream) {
LOG.v("read thread - eos:", endOfStream, "- No buffer, retrying."); LOG.v("read thread - eos: true - No buffer, retrying.");
read(true); // try again read(true); // try again
} else { } else {
LOG.w("read thread - eos:", endOfStream, "- Skipping audio frame, encoding is too slow."); LOG.w("read thread - eos: false - Skipping audio frame, encoding is too slow.");
sleep(); sleep(); // sleep a bit
} }
} else { } else {
mCurrentBuffer.clear(); mCurrentBuffer.clear();
@ -290,8 +290,9 @@ public class AudioMediaEncoder extends MediaEncoder {
@SuppressLint("HandlerLeak") @SuppressLint("HandlerLeak")
class AudioEncodingHandler extends Handler { class AudioEncodingHandler extends Handler {
InputBufferPool mInputBufferPool = new InputBufferPool(); private InputBufferPool mInputBufferPool = new InputBufferPool();
LinkedBlockingQueue<InputBuffer> mPendingOps = new LinkedBlockingQueue<>(); private LinkedBlockingQueue<InputBuffer> mPendingOps = new LinkedBlockingQueue<>();
private long mFirstFrameTimestamp = Long.MIN_VALUE;
AudioEncodingHandler() { AudioEncodingHandler() {
super(WorkerHandler.get("AudioEncodingHandler").getLooper()); super(WorkerHandler.get("AudioEncodingHandler").getLooper());
@ -310,6 +311,10 @@ public class AudioMediaEncoder extends MediaEncoder {
super.handleMessage(msg); super.handleMessage(msg);
boolean endOfStream = msg.what == 1; boolean endOfStream = msg.what == 1;
long timestamp = (((long) msg.arg1) << 32) | (((long) msg.arg2) & 0xffffffffL); long timestamp = (((long) msg.arg1) << 32) | (((long) msg.arg2) & 0xffffffffL);
if (mFirstFrameTimestamp == Long.MIN_VALUE) {
mFirstFrameTimestamp = timestamp;
}
LOG.i("encoding thread - got buffer. timestamp:", timestamp, "eos:", endOfStream); LOG.i("encoding thread - got buffer. timestamp:", timestamp, "eos:", endOfStream);
ByteBuffer buffer = (ByteBuffer) msg.obj; ByteBuffer buffer = (ByteBuffer) msg.obj;
int readBytes = buffer.remaining(); int readBytes = buffer.remaining();
@ -319,6 +324,7 @@ public class AudioMediaEncoder extends MediaEncoder {
inputBuffer.timestamp = timestamp; inputBuffer.timestamp = timestamp;
inputBuffer.length = readBytes; inputBuffer.length = readBytes;
inputBuffer.isEndOfStream = endOfStream; inputBuffer.isEndOfStream = endOfStream;
inputBuffer.didReachMaxLength = (timestamp - mFirstFrameTimestamp) > getMaxLengthMillis() * 1000;
mPendingOps.add(inputBuffer); mPendingOps.add(inputBuffer);
LOG.i("encoding thread - performing", mPendingOps.size(), "pending operations."); LOG.i("encoding thread - performing", mPendingOps.size(), "pending operations.");

@ -13,4 +13,5 @@ class InputBuffer {
int length; int length;
long timestamp; long timestamp;
boolean isEndOfStream; boolean isEndOfStream;
boolean didReachMaxLength;
} }

@ -55,6 +55,11 @@ import java.nio.ByteBuffer;
* *
* For VIDEO encoders, things are much easier because we skip the whole input part. * For VIDEO encoders, things are much easier because we skip the whole input part.
* See description in {@link VideoMediaEncoder}. * See description in {@link VideoMediaEncoder}.
*
* For max length constraint, it will be checked automatically during {@link #drainOutput(boolean)},
* or subclasses can provide an hint to this encoder using {@link InputBuffer#didReachMaxLength}.
* In this second case, we can request a stop at reading time, so we avoid useless readings
* in certain setups (where drain is called a lot after reading).
*/ */
// https://github.com/saki4510t/AudioVideoRecordingSample/blob/master/app/src/main/java/com/serenegiant/encoder/MediaEncoder.java // https://github.com/saki4510t/AudioVideoRecordingSample/blob/master/app/src/main/java/com/serenegiant/encoder/MediaEncoder.java
@RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2) @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2)
@ -100,9 +105,12 @@ abstract class MediaEncoder {
private OutputBufferPool mOutputBufferPool; private OutputBufferPool mOutputBufferPool;
private MediaCodec.BufferInfo mBufferInfo; private MediaCodec.BufferInfo mBufferInfo;
private MediaCodecBuffers mBuffers; private MediaCodecBuffers mBuffers;
private long mMaxLengthMillis; private long mMaxLengthMillis;
private boolean mMaxLengthReached; private boolean mMaxLengthReached;
private long mStartPresentationTimeUs = Long.MIN_VALUE;
private long mLastPresentationTimeUs = 0;
/** /**
* Needs a readable name for the thread and for logging. * Needs a readable name for the thread and for logging.
* @param name a name * @param name a name
@ -334,6 +342,9 @@ abstract class MediaEncoder {
mMediaCodec.queueInputBuffer(buffer.index, 0, buffer.length, mMediaCodec.queueInputBuffer(buffer.index, 0, buffer.length,
buffer.timestamp, 0); buffer.timestamp, 0);
} }
if (buffer.didReachMaxLength) {
onMaxLengthReached();
}
} }
/** /**
@ -421,10 +432,7 @@ abstract class MediaEncoder {
LOG.w(mName, "DRAINING - Reached maxLength! mLastPresentationTimeUs:", mLastPresentationTimeUs, LOG.w(mName, "DRAINING - Reached maxLength! mLastPresentationTimeUs:", mLastPresentationTimeUs,
"mStartPresentationTimeUs:", mStartPresentationTimeUs, "mStartPresentationTimeUs:", mStartPresentationTimeUs,
"mMaxLengthUs:", mMaxLengthMillis * 1000); "mMaxLengthUs:", mMaxLengthMillis * 1000);
mMaxLengthReached = true; onMaxLengthReached();
LOG.w(mName, "DRAINING - Requesting a stop.");
setState(STATE_LIMIT_REACHED);
mController.requestStop(mTrackIndex);
break; break;
} }
@ -438,8 +446,21 @@ abstract class MediaEncoder {
} }
} }
private long mStartPresentationTimeUs = Long.MIN_VALUE;
private long mLastPresentationTimeUs = 0;
abstract int getEncodedBitRate(); abstract int getEncodedBitRate();
private void onMaxLengthReached() {
if (mMaxLengthReached) return;
mMaxLengthReached = true;
if (mState >= STATE_LIMIT_REACHED) {
LOG.w(mName, "onMaxLengthReached: Reached in wrong state. Aborting.", mState);
} else {
LOG.w(mName, "onMaxLengthReached: Requesting a stop.");
setState(STATE_LIMIT_REACHED);
mController.requestStop(mTrackIndex);
}
}
protected long getMaxLengthMillis() {
return mMaxLengthMillis;
}
} }

Loading…
Cancel
Save