diff --git a/encoder/src/main/java/com/pedro/encoder/BaseEncoder.java b/encoder/src/main/java/com/pedro/encoder/BaseEncoder.java
index 66d65f747e..c6cbefa456 100644
--- a/encoder/src/main/java/com/pedro/encoder/BaseEncoder.java
+++ b/encoder/src/main/java/com/pedro/encoder/BaseEncoder.java
@@ -38,19 +38,23 @@
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
+import java.util.concurrent.TimeUnit;
/**
* Created by pedro on 18/09/19.
*/
public abstract class BaseEncoder implements EncoderCallback {
+ private static final int MAX_RESET_ATTEMPTS = 3;
+ private static final long RESET_ATTEMPTS_WINDOW_MS = 30_000;
+
protected String TAG = "BaseEncoder";
protected final G711Codec g711Codec = new G711Codec();
private final MediaCodec.BufferInfo bufferInfo = new MediaCodec.BufferInfo();
private HandlerThread handlerThread;
- private ExecutorService executorService;
+ private volatile ExecutorService executorService;
protected BlockingQueue queue = new ArrayBlockingQueue<>(80);
- protected MediaCodec codec;
+ protected volatile MediaCodec codec;
protected volatile long presentTimeUs;
protected volatile boolean running = false;
protected boolean isBufferMode = true;
@@ -58,6 +62,8 @@ public abstract class BaseEncoder implements EncoderCallback {
private MediaCodec.Callback callback;
private volatile long oldTimeStamp = 0L;
protected boolean shouldReset = true;
+ private long lastResetTimeMs = 0;
+ private int resetAttempts = 0;
protected boolean prepared = false;
private Handler handler;
private CodecErrorCallback encoderErrorCallback;
@@ -82,6 +88,9 @@ public void restart() {
public void start(long startTs) {
if (!prepared) throw new IllegalStateException(TAG + " not prepared yet. You must call prepare method before start it");
presentTimeUs = startTs;
+ shouldReset = true;
+ lastResetTimeMs = 0;
+ resetAttempts = 0;
start(true);
initCodec();
}
@@ -111,7 +120,8 @@ private void initCodec() {
getDataFromEncoder();
} catch (IllegalStateException e) {
Log.i(TAG, "Encoding error", e);
- reloadCodec(e);
+ new Thread(() -> reloadCodec(e), TAG + " recovery").start();
+ return;
}
}
});
@@ -144,15 +154,24 @@ private void reloadCodec(IllegalStateException e) {
}
//Sometimes encoder crash, we will try recover it. Reset encoder a time if crash
CodecErrorCallback callback = encoderErrorCallback;
- if (callback != null) {
- shouldReset = callback.onEncodeError(typeError, e);
- }
- if (shouldReset) {
- Log.e(typeError.name(), "Encoder crashed, trying to recover it");
+ if (callback != null) shouldReset = callback.onEncodeError(typeError, e);
+ if (shouldReset && canReset()) {
+ Log.e(TAG, typeError.name() + ". Encoder crashed, trying to recover it", e);
reset();
+ } else {
+ Log.e(TAG, typeError.name() + ". Encoder crashed, stopping it", e);
+ stop();
}
}
+ private boolean canReset() {
+ long now = TimeUtils.getCurrentTimeMillis();
+ if (now - lastResetTimeMs > RESET_ATTEMPTS_WINDOW_MS) resetAttempts = 0;
+ lastResetTimeMs = now;
+ resetAttempts++;
+ return resetAttempts <= MAX_RESET_ATTEMPTS;
+ }
+
public void stop() {
stop(true);
}
@@ -165,9 +184,7 @@ public void stop(boolean resetTs) {
stopImp();
if (handlerThread != null) {
if (handlerThread.getLooper() != null) {
- if (handlerThread.getLooper().getThread() != null) {
- handlerThread.getLooper().getThread().interrupt();
- }
+ handlerThread.getLooper().getThread().interrupt();
handlerThread.getLooper().quit();
}
handlerThread.quit();
@@ -181,7 +198,15 @@ public void stop(boolean resetTs) {
handlerThread.getLooper().getThread().join(500);
} catch (Exception ignored) { }
}
- if (executorService != null) executorService.shutdownNow();
+ ExecutorService executor = executorService;
+ if (executor != null) {
+ executor.shutdownNow();
+ try {
+ executor.awaitTermination(500, TimeUnit.MILLISECONDS);
+ } catch (InterruptedException e) {
+ Thread.currentThread().interrupt();
+ }
+ }
queue.clear();
queue = new ArrayBlockingQueue<>(80);
try {
@@ -202,6 +227,8 @@ protected void getDataFromEncoder() throws IllegalStateException {
processG711();
return;
}
+ MediaCodec codec = this.codec;
+ if (codec == null) return;
if (isBufferMode) {
int inBufferIndex = codec.dequeueInputBuffer(0);
if (inBufferIndex >= 0) {
diff --git a/encoder/src/main/java/com/pedro/encoder/audio/AudioEncoder.java b/encoder/src/main/java/com/pedro/encoder/audio/AudioEncoder.java
index 6291fe0ee8..58eff9db35 100644
--- a/encoder/src/main/java/com/pedro/encoder/audio/AudioEncoder.java
+++ b/encoder/src/main/java/com/pedro/encoder/audio/AudioEncoder.java
@@ -126,7 +126,6 @@ public boolean prepareAudioEncoder() {
@Override
public void start(boolean resetTs) {
if (resetTs) tsBuffer = 0;
- shouldReset = resetTs;
Log.i(TAG, "started");
}
diff --git a/encoder/src/main/java/com/pedro/encoder/video/VideoEncoder.java b/encoder/src/main/java/com/pedro/encoder/video/VideoEncoder.java
index fa5ee97c93..d5d0843e49 100644
--- a/encoder/src/main/java/com/pedro/encoder/video/VideoEncoder.java
+++ b/encoder/src/main/java/com/pedro/encoder/video/VideoEncoder.java
@@ -41,6 +41,7 @@
import com.pedro.encoder.utils.yuv.YUVUtil;
import java.nio.ByteBuffer;
+import java.util.ArrayList;
import java.util.List;
/**
@@ -198,7 +199,6 @@ public boolean prepareVideoEncoder(int width, int height, int fps, int bitRate,
public void start(boolean resetTs) {
if (resetTs) firstTimestamp = 0;
forceKey = false;
- shouldReset = resetTs;
spsPpsSetted = false;
if (formatVideoEncoder != FormatVideoEncoder.SURFACE) {
YUVUtil.preAllocateBuffers(width * height * 3 / 2);
@@ -228,12 +228,13 @@ public boolean reset() {
}
private FormatVideoEncoder chooseColorDynamically(MediaCodecInfo mediaCodecInfo) {
- for (int color : mediaCodecInfo.getCapabilitiesForType(type.getMime()).colorFormats) {
- if (color == FormatVideoEncoder.YUV420PLANAR.getFormatCodec()) {
- return FormatVideoEncoder.YUV420PLANAR;
- } else if (color == FormatVideoEncoder.YUV420SEMIPLANAR.getFormatCodec()) {
- return FormatVideoEncoder.YUV420SEMIPLANAR;
- }
+ List colors = new ArrayList<>();
+ for (int color : mediaCodecInfo.getCapabilitiesForType(type.getMime()).colorFormats) colors.add(color);
+
+ if (colors.contains(FormatVideoEncoder.YUV420PLANAR.getFormatCodec())) {
+ return FormatVideoEncoder.YUV420PLANAR;
+ } else if (colors.contains(FormatVideoEncoder.YUV420SEMIPLANAR.getFormatCodec())) {
+ return FormatVideoEncoder.YUV420SEMIPLANAR;
}
return null;
}