|
|
@ -14,6 +14,9 @@ import com.otaliastudios.cameraview.CameraLogger; |
|
|
|
import com.otaliastudios.cameraview.internal.utils.WorkerHandler; |
|
|
|
import com.otaliastudios.cameraview.internal.utils.WorkerHandler; |
|
|
|
|
|
|
|
|
|
|
|
import java.nio.ByteBuffer; |
|
|
|
import java.nio.ByteBuffer; |
|
|
|
|
|
|
|
import java.util.HashMap; |
|
|
|
|
|
|
|
import java.util.Map; |
|
|
|
|
|
|
|
import java.util.concurrent.atomic.AtomicInteger; |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
/** |
|
|
|
* Base class for single-track encoders, coordinated by a {@link MediaEncoderEngine}. |
|
|
|
* Base class for single-track encoders, coordinated by a {@link MediaEncoderEngine}. |
|
|
@ -117,6 +120,7 @@ public abstract class MediaEncoder { |
|
|
|
private OutputBufferPool mOutputBufferPool; |
|
|
|
private OutputBufferPool mOutputBufferPool; |
|
|
|
private MediaCodec.BufferInfo mBufferInfo; |
|
|
|
private MediaCodec.BufferInfo mBufferInfo; |
|
|
|
private MediaCodecBuffers mBuffers; |
|
|
|
private MediaCodecBuffers mBuffers; |
|
|
|
|
|
|
|
private final Map<String, AtomicInteger> mPendingEvents = new HashMap<>(); |
|
|
|
|
|
|
|
|
|
|
|
private long mMaxLengthMillis; |
|
|
|
private long mMaxLengthMillis; |
|
|
|
private boolean mMaxLengthReached; |
|
|
|
private boolean mMaxLengthReached; |
|
|
@ -223,13 +227,18 @@ public abstract class MediaEncoder { |
|
|
|
* @param event what happened |
|
|
|
* @param event what happened |
|
|
|
* @param data object |
|
|
|
* @param data object |
|
|
|
*/ |
|
|
|
*/ |
|
|
|
|
|
|
|
@SuppressWarnings("ConstantConditions") |
|
|
|
final void notify(final @NonNull String event, final @Nullable Object data) { |
|
|
|
final void notify(final @NonNull String event, final @Nullable Object data) { |
|
|
|
LOG.v(mName, "Notify was called. Posting."); |
|
|
|
if (!mPendingEvents.containsKey(event)) mPendingEvents.put(event, new AtomicInteger(0)); |
|
|
|
|
|
|
|
final AtomicInteger pendingEvents = mPendingEvents.get(event); |
|
|
|
|
|
|
|
pendingEvents.incrementAndGet(); |
|
|
|
|
|
|
|
LOG.v(mName, "Notify was called. Posting. pendingEvents:", pendingEvents.intValue()); |
|
|
|
mWorker.post(new Runnable() { |
|
|
|
mWorker.post(new Runnable() { |
|
|
|
@Override |
|
|
|
@Override |
|
|
|
public void run() { |
|
|
|
public void run() { |
|
|
|
LOG.v(mName, "Notify was called. Executing."); |
|
|
|
LOG.v(mName, "Notify was called. Executing. pendingEvents:", pendingEvents.intValue()); |
|
|
|
onEvent(event, data); |
|
|
|
onEvent(event, data); |
|
|
|
|
|
|
|
pendingEvents.decrementAndGet(); |
|
|
|
} |
|
|
|
} |
|
|
|
}); |
|
|
|
}); |
|
|
|
} |
|
|
|
} |
|
|
@ -357,7 +366,9 @@ public abstract class MediaEncoder { |
|
|
|
*/ |
|
|
|
*/ |
|
|
|
@SuppressWarnings("WeakerAccess") |
|
|
|
@SuppressWarnings("WeakerAccess") |
|
|
|
protected void encodeInputBuffer(InputBuffer buffer) { |
|
|
|
protected void encodeInputBuffer(InputBuffer buffer) { |
|
|
|
LOG.v(mName, "ENCODING - Buffer:", buffer.index, "Bytes:", buffer.length, "Presentation:", buffer.timestamp); |
|
|
|
LOG.v(mName, "ENCODING - Buffer:", buffer.index, |
|
|
|
|
|
|
|
"Bytes:", buffer.length, |
|
|
|
|
|
|
|
"Presentation:", buffer.timestamp); |
|
|
|
if (buffer.isEndOfStream) { // send EOS
|
|
|
|
if (buffer.isEndOfStream) { // send EOS
|
|
|
|
mMediaCodec.queueInputBuffer(buffer.index, 0, 0, |
|
|
|
mMediaCodec.queueInputBuffer(buffer.index, 0, 0, |
|
|
|
buffer.timestamp, MediaCodec.BUFFER_FLAG_END_OF_STREAM); |
|
|
|
buffer.timestamp, MediaCodec.BUFFER_FLAG_END_OF_STREAM); |
|
|
@ -380,7 +391,7 @@ public abstract class MediaEncoder { |
|
|
|
@SuppressLint("LogNotTimber") |
|
|
|
@SuppressLint("LogNotTimber") |
|
|
|
@SuppressWarnings("WeakerAccess") |
|
|
|
@SuppressWarnings("WeakerAccess") |
|
|
|
protected void drainOutput(boolean drainAll) { |
|
|
|
protected void drainOutput(boolean drainAll) { |
|
|
|
LOG.v(mName, "DRAINING - EOS:", drainAll); |
|
|
|
LOG.i(mName, "DRAINING - EOS:", drainAll); |
|
|
|
if (mMediaCodec == null) { |
|
|
|
if (mMediaCodec == null) { |
|
|
|
LOG.e("drain() was called before prepare() or after releasing."); |
|
|
|
LOG.e("drain() was called before prepare() or after releasing."); |
|
|
|
return; |
|
|
|
return; |
|
|
@ -437,7 +448,7 @@ public abstract class MediaEncoder { |
|
|
|
mBufferInfo.presentationTimeUs = (mStartTimeMillis * 1000) + mLastTimeUs - mFirstTimeUs; |
|
|
|
mBufferInfo.presentationTimeUs = (mStartTimeMillis * 1000) + mLastTimeUs - mFirstTimeUs; |
|
|
|
|
|
|
|
|
|
|
|
// Write.
|
|
|
|
// Write.
|
|
|
|
LOG.v(mName, "DRAINING - About to write(). Adjusted presentation:", mBufferInfo.presentationTimeUs); |
|
|
|
LOG.i(mName, "DRAINING - About to write(). Adjusted presentation:", mBufferInfo.presentationTimeUs); |
|
|
|
OutputBuffer buffer = mOutputBufferPool.get(); |
|
|
|
OutputBuffer buffer = mOutputBufferPool.get(); |
|
|
|
//noinspection ConstantConditions
|
|
|
|
//noinspection ConstantConditions
|
|
|
|
buffer.info = mBufferInfo; |
|
|
|
buffer.info = mBufferInfo; |
|
|
@ -455,6 +466,7 @@ public abstract class MediaEncoder { |
|
|
|
&& mLastTimeUs - mFirstTimeUs > mMaxLengthMillis * 1000) { |
|
|
|
&& mLastTimeUs - mFirstTimeUs > mMaxLengthMillis * 1000) { |
|
|
|
LOG.w(mName, "DRAINING - Reached maxLength! mLastTimeUs:", mLastTimeUs, |
|
|
|
LOG.w(mName, "DRAINING - Reached maxLength! mLastTimeUs:", mLastTimeUs, |
|
|
|
"mStartTimeUs:", mFirstTimeUs, |
|
|
|
"mStartTimeUs:", mFirstTimeUs, |
|
|
|
|
|
|
|
"mDeltaUs:", mLastTimeUs - mFirstTimeUs, |
|
|
|
"mMaxLengthUs:", mMaxLengthMillis * 1000); |
|
|
|
"mMaxLengthUs:", mMaxLengthMillis * 1000); |
|
|
|
onMaxLengthReached(); |
|
|
|
onMaxLengthReached(); |
|
|
|
break; |
|
|
|
break; |
|
|
@ -494,6 +506,11 @@ public abstract class MediaEncoder { |
|
|
|
onMaxLengthReached(); |
|
|
|
onMaxLengthReached(); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@SuppressWarnings("WeakerAccess") |
|
|
|
|
|
|
|
protected boolean hasReachedMaxLength() { |
|
|
|
|
|
|
|
return mMaxLengthReached; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
/** |
|
|
|
* Called by us (during {@link #drainOutput(boolean)}) or by subclasses |
|
|
|
* Called by us (during {@link #drainOutput(boolean)}) or by subclasses |
|
|
|
* (through {@link #notifyMaxLengthReached()}) to notify that we reached the |
|
|
|
* (through {@link #notifyMaxLengthReached()}) to notify that we reached the |
|
|
@ -523,4 +540,17 @@ public abstract class MediaEncoder { |
|
|
|
protected final void notifyFirstFrameMillis(long firstFrameMillis) { |
|
|
|
protected final void notifyFirstFrameMillis(long firstFrameMillis) { |
|
|
|
mStartTimeMillis = firstFrameMillis; |
|
|
|
mStartTimeMillis = firstFrameMillis; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
|
|
* Returns the number of events (see {@link #onEvent(String, Object)}) that were scheduled |
|
|
|
|
|
|
|
* but still not passed to that function. Could be used to drop some of them if this |
|
|
|
|
|
|
|
* number is too high. |
|
|
|
|
|
|
|
* |
|
|
|
|
|
|
|
* @param event the event type |
|
|
|
|
|
|
|
* @return the pending events number |
|
|
|
|
|
|
|
*/ |
|
|
|
|
|
|
|
@SuppressWarnings({"SameParameterValue", "ConstantConditions", "WeakerAccess"}) |
|
|
|
|
|
|
|
protected final int getPendingEvents(@NonNull String event) { |
|
|
|
|
|
|
|
return mPendingEvents.get(event).intValue(); |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|