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'
// 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.
private static final int BUFFER_POOL_MAX_SIZE = 100;
private static final int BUFFER_POOL_MAX_SIZE = 80;
private static long bytesToUs(int bytes) {
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.
// However, if endOfStream, we CAN'T lose this frame!
if (endOfStream) {
LOG.v("read thread - eos:", endOfStream, "- No buffer, retrying.");
LOG.v("read thread - eos: true - No buffer, retrying.");
read(true); // try again
} else {
LOG.w("read thread - eos:", endOfStream, "- Skipping audio frame, encoding is too slow.");
sleep();
LOG.w("read thread - eos: false - Skipping audio frame, encoding is too slow.");
sleep(); // sleep a bit
}
} else {
mCurrentBuffer.clear();
@ -290,8 +290,9 @@ public class AudioMediaEncoder extends MediaEncoder {
@SuppressLint("HandlerLeak")
class AudioEncodingHandler extends Handler {
InputBufferPool mInputBufferPool = new InputBufferPool();
LinkedBlockingQueue<InputBuffer> mPendingOps = new LinkedBlockingQueue<>();
private InputBufferPool mInputBufferPool = new InputBufferPool();
private LinkedBlockingQueue<InputBuffer> mPendingOps = new LinkedBlockingQueue<>();
private long mFirstFrameTimestamp = Long.MIN_VALUE;
AudioEncodingHandler() {
super(WorkerHandler.get("AudioEncodingHandler").getLooper());
@ -310,6 +311,10 @@ public class AudioMediaEncoder extends MediaEncoder {
super.handleMessage(msg);
boolean endOfStream = msg.what == 1;
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);
ByteBuffer buffer = (ByteBuffer) msg.obj;
int readBytes = buffer.remaining();
@ -319,6 +324,7 @@ public class AudioMediaEncoder extends MediaEncoder {
inputBuffer.timestamp = timestamp;
inputBuffer.length = readBytes;
inputBuffer.isEndOfStream = endOfStream;
inputBuffer.didReachMaxLength = (timestamp - mFirstFrameTimestamp) > getMaxLengthMillis() * 1000;
mPendingOps.add(inputBuffer);
LOG.i("encoding thread - performing", mPendingOps.size(), "pending operations.");

@ -13,4 +13,5 @@ class InputBuffer {
int length;
long timestamp;
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.
* 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
@RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2)
@ -100,9 +105,12 @@ abstract class MediaEncoder {
private OutputBufferPool mOutputBufferPool;
private MediaCodec.BufferInfo mBufferInfo;
private MediaCodecBuffers mBuffers;
private long mMaxLengthMillis;
private boolean mMaxLengthReached;
private long mStartPresentationTimeUs = Long.MIN_VALUE;
private long mLastPresentationTimeUs = 0;
/**
* Needs a readable name for the thread and for logging.
* @param name a name
@ -334,6 +342,9 @@ abstract class MediaEncoder {
mMediaCodec.queueInputBuffer(buffer.index, 0, buffer.length,
buffer.timestamp, 0);
}
if (buffer.didReachMaxLength) {
onMaxLengthReached();
}
}
/**
@ -421,10 +432,7 @@ abstract class MediaEncoder {
LOG.w(mName, "DRAINING - Reached maxLength! mLastPresentationTimeUs:", mLastPresentationTimeUs,
"mStartPresentationTimeUs:", mStartPresentationTimeUs,
"mMaxLengthUs:", mMaxLengthMillis * 1000);
mMaxLengthReached = true;
LOG.w(mName, "DRAINING - Requesting a stop.");
setState(STATE_LIMIT_REACHED);
mController.requestStop(mTrackIndex);
onMaxLengthReached();
break;
}
@ -438,8 +446,21 @@ abstract class MediaEncoder {
}
}
private long mStartPresentationTimeUs = Long.MIN_VALUE;
private long mLastPresentationTimeUs = 0;
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