Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 39 additions & 12 deletions encoder/src/main/java/com/pedro/encoder/BaseEncoder.java
Original file line number Diff line number Diff line change
Expand Up @@ -38,26 +38,32 @@
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<Frame> queue = new ArrayBlockingQueue<>(80);
protected MediaCodec codec;
protected volatile MediaCodec codec;
protected volatile long presentTimeUs;
protected volatile boolean running = false;
protected boolean isBufferMode = true;
protected CodecUtil.CodecType codecType = CodecUtil.CodecType.FIRST_COMPATIBLE_FOUND;
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;
Expand All @@ -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();
}
Expand Down Expand Up @@ -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;
}
}
});
Expand Down Expand Up @@ -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);
}
Expand All @@ -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();
Expand All @@ -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 {
Expand All @@ -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) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,6 @@ public boolean prepareAudioEncoder() {
@Override
public void start(boolean resetTs) {
if (resetTs) tsBuffer = 0;
shouldReset = resetTs;
Log.i(TAG, "started");
}

Expand Down
15 changes: 8 additions & 7 deletions encoder/src/main/java/com/pedro/encoder/video/VideoEncoder.java
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
import com.pedro.encoder.utils.yuv.YUVUtil;

import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.List;

/**
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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<Integer> 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;
}
Expand Down
Loading