mirror of
https://github.com/Genymobile/scrcpy.git
synced 2025-08-02 22:29:25 +00:00
Extract MediaCodecCompat from ScreenEncoder
This commit is contained in:
parent
2460552bb8
commit
48cdbb23a9
2 changed files with 119 additions and 14 deletions
116
server/src/main/java/com/genymobile/scrcpy/MediaCodecCompat.java
Normal file
116
server/src/main/java/com/genymobile/scrcpy/MediaCodecCompat.java
Normal file
|
@ -0,0 +1,116 @@
|
||||||
|
package com.genymobile.scrcpy;
|
||||||
|
|
||||||
|
import android.media.MediaCodec;
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
import androidx.annotation.RequiresApi;
|
||||||
|
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
|
||||||
|
import static android.media.MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED;
|
||||||
|
import static android.os.Build.VERSION.SDK_INT;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Version of {@link MediaCodec} that backports {@link #getOutputBuffer} to Kitkat.
|
||||||
|
* The backported implementation isn't thread safe.
|
||||||
|
*/
|
||||||
|
abstract class MediaCodecCompat {
|
||||||
|
private final MediaCodec delegate;
|
||||||
|
|
||||||
|
MediaCodecCompat(@NonNull MediaCodec delegate) {
|
||||||
|
this.delegate = delegate;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
protected MediaCodec getDelegate() {
|
||||||
|
return delegate;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
static MediaCodecCompat wrap(@NonNull MediaCodec codec) {
|
||||||
|
if (SDK_INT >= 21) {
|
||||||
|
return new Platform(codec);
|
||||||
|
} else {
|
||||||
|
return new Backport(codec);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract int dequeueOutputBuffer(
|
||||||
|
@NonNull MediaCodec.BufferInfo info, long timeoutUs);
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
abstract ByteBuffer getOutputBuffer(int index);
|
||||||
|
|
||||||
|
abstract void releaseOutputBuffer(int index, boolean render);
|
||||||
|
|
||||||
|
@SuppressWarnings("deprecation")
|
||||||
|
private static class Backport extends MediaCodecCompat {
|
||||||
|
private ByteBuffer[] cachedOutputBuffers = null;
|
||||||
|
|
||||||
|
Backport(@NonNull MediaCodec delegate) {
|
||||||
|
super(delegate);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
int dequeueOutputBuffer(
|
||||||
|
@NonNull MediaCodec.BufferInfo info, long timeoutUs) {
|
||||||
|
final int res = getDelegate().dequeueOutputBuffer(info, timeoutUs);
|
||||||
|
if (res == INFO_OUTPUT_BUFFERS_CHANGED) {
|
||||||
|
cachedOutputBuffers = null;
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
ByteBuffer getOutputBuffer(int index) {
|
||||||
|
if (cachedOutputBuffers == null) {
|
||||||
|
cacheOutputBuffers();
|
||||||
|
}
|
||||||
|
if (cachedOutputBuffers == null) {
|
||||||
|
throw new IllegalStateException();
|
||||||
|
}
|
||||||
|
return cachedOutputBuffers[index];
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
void releaseOutputBuffer(int index, boolean render) {
|
||||||
|
cachedOutputBuffers = null;
|
||||||
|
getDelegate().releaseOutputBuffer(index, render);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void cacheOutputBuffers() {
|
||||||
|
ByteBuffer[] buffers = null;
|
||||||
|
try {
|
||||||
|
buffers = getDelegate().getOutputBuffers();
|
||||||
|
} catch (IllegalStateException e) {
|
||||||
|
// we don't get buffers in async mode
|
||||||
|
}
|
||||||
|
cachedOutputBuffers = buffers;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@RequiresApi(21)
|
||||||
|
private static class Platform extends MediaCodecCompat {
|
||||||
|
Platform(@NonNull MediaCodec delegate) {
|
||||||
|
super(delegate);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
int dequeueOutputBuffer(
|
||||||
|
@NonNull MediaCodec.BufferInfo info, long timeoutUs) {
|
||||||
|
return getDelegate().dequeueOutputBuffer(info, timeoutUs);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
ByteBuffer getOutputBuffer(int index) {
|
||||||
|
return getDelegate().getOutputBuffer(index);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
void releaseOutputBuffer(int index, boolean render) {
|
||||||
|
getDelegate().releaseOutputBuffer(index, render);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -144,11 +144,10 @@ public class ScreenEncoder implements Device.RotationListener {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("deprecation") // Android API 19 requires to call deprecated methods
|
private boolean encode(MediaCodec platformCodec, FileDescriptor fd) throws IOException {
|
||||||
private boolean encode(MediaCodec codec, FileDescriptor fd) throws IOException {
|
final MediaCodecCompat codec = MediaCodecCompat.wrap(platformCodec);
|
||||||
boolean eof = false;
|
boolean eof = false;
|
||||||
MediaCodec.BufferInfo bufferInfo = new MediaCodec.BufferInfo();
|
MediaCodec.BufferInfo bufferInfo = new MediaCodec.BufferInfo();
|
||||||
ByteBuffer[] cachedOutputBuffers = null;
|
|
||||||
|
|
||||||
while (!consumeRotationChange() && !eof) {
|
while (!consumeRotationChange() && !eof) {
|
||||||
int outputBufferId = codec.dequeueOutputBuffer(bufferInfo, -1);
|
int outputBufferId = codec.dequeueOutputBuffer(bufferInfo, -1);
|
||||||
|
@ -159,15 +158,7 @@ public class ScreenEncoder implements Device.RotationListener {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (outputBufferId >= 0) {
|
if (outputBufferId >= 0) {
|
||||||
ByteBuffer codecBuffer;
|
ByteBuffer codecBuffer = codec.getOutputBuffer(outputBufferId);
|
||||||
if (Build.VERSION.SDK_INT >= 21) {
|
|
||||||
codecBuffer = codec.getOutputBuffer(outputBufferId);
|
|
||||||
} else {
|
|
||||||
if (cachedOutputBuffers == null) {
|
|
||||||
cachedOutputBuffers = codec.getOutputBuffers();
|
|
||||||
}
|
|
||||||
codecBuffer = cachedOutputBuffers[outputBufferId];
|
|
||||||
}
|
|
||||||
|
|
||||||
if (sendFrameMeta) {
|
if (sendFrameMeta) {
|
||||||
writeFrameMeta(fd, bufferInfo, codecBuffer.remaining());
|
writeFrameMeta(fd, bufferInfo, codecBuffer.remaining());
|
||||||
|
@ -178,8 +169,6 @@ public class ScreenEncoder implements Device.RotationListener {
|
||||||
// If this is not a config packet, then it contains a frame
|
// If this is not a config packet, then it contains a frame
|
||||||
firstFrameSent = true;
|
firstFrameSent = true;
|
||||||
}
|
}
|
||||||
} else if (outputBufferId == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) {
|
|
||||||
cachedOutputBuffers = null;
|
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
if (outputBufferId >= 0) {
|
if (outputBufferId >= 0) {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue