mirror of
https://github.com/Genymobile/scrcpy
synced 2025-01-02 20:52:06 +00:00
Interrupt MediaCodec blocking call on reset
When the MediaCodec input is a Surface, no EOS (end-of-stream) will never occur automatically: it may only be triggered manually by MediaCodec.signalEndOfInputStream(). Use this signal to interrupt the blocking call to dequeueOutputBuffer() immediately on reset, without waiting for the next frame to be dequeued. PR #5432 <https://github.com/Genymobile/scrcpy/pull/5432>
This commit is contained in:
parent
69b836930a
commit
9958302e6f
@ -1,17 +1,29 @@
|
||||
package com.genymobile.scrcpy.video;
|
||||
|
||||
import android.media.MediaCodec;
|
||||
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
public class CaptureReset implements SurfaceCapture.CaptureListener {
|
||||
|
||||
private final AtomicBoolean reset = new AtomicBoolean();
|
||||
|
||||
// Current instance of MediaCodec to "interrupt" on reset
|
||||
private MediaCodec runningMediaCodec;
|
||||
|
||||
public boolean consumeReset() {
|
||||
return reset.getAndSet(false);
|
||||
}
|
||||
|
||||
public void reset() {
|
||||
public synchronized void reset() {
|
||||
reset.set(true);
|
||||
if (runningMediaCodec != null) {
|
||||
runningMediaCodec.signalEndOfInputStream();
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized void setRunningMediaCodec(MediaCodec runningMediaCodec) {
|
||||
this.runningMediaCodec = runningMediaCodec;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -94,7 +94,21 @@ public class SurfaceEncoder implements AsyncProcessor {
|
||||
|
||||
mediaCodec.start();
|
||||
|
||||
alive = encode(mediaCodec, streamer);
|
||||
// Set the MediaCodec instance to "interrupt" (by signaling an EOS) on reset
|
||||
reset.setRunningMediaCodec(mediaCodec);
|
||||
|
||||
if (stopped.get()) {
|
||||
alive = false;
|
||||
} else {
|
||||
boolean resetRequested = reset.consumeReset();
|
||||
if (!resetRequested) {
|
||||
// If a reset is requested during encode(), it will interrupt the encoding by an EOS
|
||||
encode(mediaCodec, streamer);
|
||||
}
|
||||
// The capture might have been closed internally (for example if the camera is disconnected)
|
||||
alive = !stopped.get() && !capture.isClosed();
|
||||
}
|
||||
|
||||
// do not call stop() on exception, it would trigger an IllegalStateException
|
||||
mediaCodec.stop();
|
||||
} catch (IllegalStateException | IllegalArgumentException e) {
|
||||
@ -105,6 +119,7 @@ public class SurfaceEncoder implements AsyncProcessor {
|
||||
Ln.i("Retrying...");
|
||||
alive = true;
|
||||
} finally {
|
||||
reset.setRunningMediaCodec(null);
|
||||
mediaCodec.reset();
|
||||
if (surface != null) {
|
||||
surface.release();
|
||||
@ -165,25 +180,16 @@ public class SurfaceEncoder implements AsyncProcessor {
|
||||
return 0;
|
||||
}
|
||||
|
||||
private boolean encode(MediaCodec codec, Streamer streamer) throws IOException {
|
||||
boolean eof = false;
|
||||
boolean alive = true;
|
||||
private void encode(MediaCodec codec, Streamer streamer) throws IOException {
|
||||
MediaCodec.BufferInfo bufferInfo = new MediaCodec.BufferInfo();
|
||||
|
||||
while (!reset.consumeReset() && !eof) {
|
||||
if (stopped.get()) {
|
||||
alive = false;
|
||||
break;
|
||||
}
|
||||
boolean eos;
|
||||
do {
|
||||
int outputBufferId = codec.dequeueOutputBuffer(bufferInfo, -1);
|
||||
try {
|
||||
if (reset.consumeReset()) {
|
||||
// must restart encoding with new size
|
||||
break;
|
||||
}
|
||||
|
||||
eof = (bufferInfo.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0;
|
||||
if (outputBufferId >= 0) {
|
||||
eos = (bufferInfo.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0;
|
||||
// On EOS, there might be data or not, depending on bufferInfo.size
|
||||
if (outputBufferId >= 0 && bufferInfo.size > 0) {
|
||||
ByteBuffer codecBuffer = codec.getOutputBuffer(outputBufferId);
|
||||
|
||||
boolean isConfig = (bufferInfo.flags & MediaCodec.BUFFER_FLAG_CODEC_CONFIG) != 0;
|
||||
@ -200,14 +206,7 @@ public class SurfaceEncoder implements AsyncProcessor {
|
||||
codec.releaseOutputBuffer(outputBufferId, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (capture.isClosed()) {
|
||||
// The capture might have been closed internally (for example if the camera is disconnected)
|
||||
alive = false;
|
||||
}
|
||||
|
||||
return !eof && alive;
|
||||
} while (!eos);
|
||||
}
|
||||
|
||||
private static MediaCodec createMediaCodec(Codec codec, String encoderName) throws IOException, ConfigurationException {
|
||||
@ -300,6 +299,7 @@ public class SurfaceEncoder implements AsyncProcessor {
|
||||
public void stop() {
|
||||
if (thread != null) {
|
||||
stopped.set(true);
|
||||
reset.reset();
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user