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;
|
package com.genymobile.scrcpy.video;
|
||||||
|
|
||||||
|
import android.media.MediaCodec;
|
||||||
|
|
||||||
import java.util.concurrent.atomic.AtomicBoolean;
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
|
|
||||||
public class CaptureReset implements SurfaceCapture.CaptureListener {
|
public class CaptureReset implements SurfaceCapture.CaptureListener {
|
||||||
|
|
||||||
private final AtomicBoolean reset = new AtomicBoolean();
|
private final AtomicBoolean reset = new AtomicBoolean();
|
||||||
|
|
||||||
|
// Current instance of MediaCodec to "interrupt" on reset
|
||||||
|
private MediaCodec runningMediaCodec;
|
||||||
|
|
||||||
public boolean consumeReset() {
|
public boolean consumeReset() {
|
||||||
return reset.getAndSet(false);
|
return reset.getAndSet(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void reset() {
|
public synchronized void reset() {
|
||||||
reset.set(true);
|
reset.set(true);
|
||||||
|
if (runningMediaCodec != null) {
|
||||||
|
runningMediaCodec.signalEndOfInputStream();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public synchronized void setRunningMediaCodec(MediaCodec runningMediaCodec) {
|
||||||
|
this.runningMediaCodec = runningMediaCodec;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -94,7 +94,21 @@ public class SurfaceEncoder implements AsyncProcessor {
|
|||||||
|
|
||||||
mediaCodec.start();
|
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
|
// do not call stop() on exception, it would trigger an IllegalStateException
|
||||||
mediaCodec.stop();
|
mediaCodec.stop();
|
||||||
} catch (IllegalStateException | IllegalArgumentException e) {
|
} catch (IllegalStateException | IllegalArgumentException e) {
|
||||||
@ -105,6 +119,7 @@ public class SurfaceEncoder implements AsyncProcessor {
|
|||||||
Ln.i("Retrying...");
|
Ln.i("Retrying...");
|
||||||
alive = true;
|
alive = true;
|
||||||
} finally {
|
} finally {
|
||||||
|
reset.setRunningMediaCodec(null);
|
||||||
mediaCodec.reset();
|
mediaCodec.reset();
|
||||||
if (surface != null) {
|
if (surface != null) {
|
||||||
surface.release();
|
surface.release();
|
||||||
@ -165,25 +180,16 @@ public class SurfaceEncoder implements AsyncProcessor {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean encode(MediaCodec codec, Streamer streamer) throws IOException {
|
private void encode(MediaCodec codec, Streamer streamer) throws IOException {
|
||||||
boolean eof = false;
|
|
||||||
boolean alive = true;
|
|
||||||
MediaCodec.BufferInfo bufferInfo = new MediaCodec.BufferInfo();
|
MediaCodec.BufferInfo bufferInfo = new MediaCodec.BufferInfo();
|
||||||
|
|
||||||
while (!reset.consumeReset() && !eof) {
|
boolean eos;
|
||||||
if (stopped.get()) {
|
do {
|
||||||
alive = false;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
int outputBufferId = codec.dequeueOutputBuffer(bufferInfo, -1);
|
int outputBufferId = codec.dequeueOutputBuffer(bufferInfo, -1);
|
||||||
try {
|
try {
|
||||||
if (reset.consumeReset()) {
|
eos = (bufferInfo.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0;
|
||||||
// must restart encoding with new size
|
// On EOS, there might be data or not, depending on bufferInfo.size
|
||||||
break;
|
if (outputBufferId >= 0 && bufferInfo.size > 0) {
|
||||||
}
|
|
||||||
|
|
||||||
eof = (bufferInfo.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0;
|
|
||||||
if (outputBufferId >= 0) {
|
|
||||||
ByteBuffer codecBuffer = codec.getOutputBuffer(outputBufferId);
|
ByteBuffer codecBuffer = codec.getOutputBuffer(outputBufferId);
|
||||||
|
|
||||||
boolean isConfig = (bufferInfo.flags & MediaCodec.BUFFER_FLAG_CODEC_CONFIG) != 0;
|
boolean isConfig = (bufferInfo.flags & MediaCodec.BUFFER_FLAG_CODEC_CONFIG) != 0;
|
||||||
@ -200,14 +206,7 @@ public class SurfaceEncoder implements AsyncProcessor {
|
|||||||
codec.releaseOutputBuffer(outputBufferId, false);
|
codec.releaseOutputBuffer(outputBufferId, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
} while (!eos);
|
||||||
|
|
||||||
if (capture.isClosed()) {
|
|
||||||
// The capture might have been closed internally (for example if the camera is disconnected)
|
|
||||||
alive = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return !eof && alive;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static MediaCodec createMediaCodec(Codec codec, String encoderName) throws IOException, ConfigurationException {
|
private static MediaCodec createMediaCodec(Codec codec, String encoderName) throws IOException, ConfigurationException {
|
||||||
@ -300,6 +299,7 @@ public class SurfaceEncoder implements AsyncProcessor {
|
|||||||
public void stop() {
|
public void stop() {
|
||||||
if (thread != null) {
|
if (thread != null) {
|
||||||
stopped.set(true);
|
stopped.set(true);
|
||||||
|
reset.reset();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user