mirror of
https://github.com/Genymobile/scrcpy.git
synced 2025-08-02 22:29:25 +00:00
Merge d535bc980d
into c00a9ead5e
This commit is contained in:
commit
8be9ce0aae
27 changed files with 789 additions and 41 deletions
|
@ -38,7 +38,7 @@ Its features include:
|
||||||
|
|
||||||
## Requirements
|
## Requirements
|
||||||
|
|
||||||
The Android device requires at least API 21 (Android 5.0).
|
The Android device requires at least API 19 (Android 4.4).
|
||||||
|
|
||||||
Make sure you [enable adb debugging][enable-adb] on your device(s).
|
Make sure you [enable adb debugging][enable-adb] on your device(s).
|
||||||
|
|
||||||
|
|
|
@ -18,6 +18,7 @@
|
||||||
|
|
||||||
#define SC_SERVER_PATH_DEFAULT PREFIX "/share/scrcpy/" SC_SERVER_FILENAME
|
#define SC_SERVER_PATH_DEFAULT PREFIX "/share/scrcpy/" SC_SERVER_FILENAME
|
||||||
#define SC_DEVICE_SERVER_PATH "/data/local/tmp/scrcpy-server.jar"
|
#define SC_DEVICE_SERVER_PATH "/data/local/tmp/scrcpy-server.jar"
|
||||||
|
#define SC_DEVICE_ANDROID_DATA_PATH "/data/local/tmp"
|
||||||
|
|
||||||
static char *
|
static char *
|
||||||
get_server_path(void) {
|
get_server_path(void) {
|
||||||
|
@ -166,6 +167,9 @@ execute_server(struct sc_server *server,
|
||||||
cmd[count++] = "-s";
|
cmd[count++] = "-s";
|
||||||
cmd[count++] = serial;
|
cmd[count++] = serial;
|
||||||
cmd[count++] = "shell";
|
cmd[count++] = "shell";
|
||||||
|
cmd[count++] = "mkdir -p " SC_DEVICE_ANDROID_DATA_PATH "/dalvik-cache";
|
||||||
|
cmd[count++] = "&&";
|
||||||
|
cmd[count++] = "ANDROID_DATA=" SC_DEVICE_ANDROID_DATA_PATH;
|
||||||
cmd[count++] = "CLASSPATH=" SC_DEVICE_SERVER_PATH;
|
cmd[count++] = "CLASSPATH=" SC_DEVICE_SERVER_PATH;
|
||||||
cmd[count++] = "app_process";
|
cmd[count++] = "app_process";
|
||||||
|
|
||||||
|
|
|
@ -152,7 +152,6 @@ page at http://checkstyle.sourceforge.net/config.html -->
|
||||||
<property name="severity" value="info" />
|
<property name="severity" value="info" />
|
||||||
</module>
|
</module>
|
||||||
<module name="UpperEll" />
|
<module name="UpperEll" />
|
||||||
|
|
||||||
</module>
|
</module>
|
||||||
|
|
||||||
</module>
|
</module>
|
||||||
|
|
|
@ -16,3 +16,4 @@ org.gradle.jvmargs=-Xmx1536m
|
||||||
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
|
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
|
||||||
# org.gradle.parallel=true
|
# org.gradle.parallel=true
|
||||||
|
|
||||||
|
android.useAndroidX=true
|
||||||
|
|
1
libcore/build.gradle
Normal file
1
libcore/build.gradle
Normal file
|
@ -0,0 +1 @@
|
||||||
|
apply plugin: 'java-library'
|
30
libcore/src/main/java/libcore/io/ErrnoException.java
Normal file
30
libcore/src/main/java/libcore/io/ErrnoException.java
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2011 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package libcore.io;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A checked exception thrown when {@link Os} methods fail. This exception contains the native
|
||||||
|
* errno value, for comparison against the constants in {@link OsConstants}, should sophisticated
|
||||||
|
* callers need to adjust their behavior based on the exact failure.
|
||||||
|
*/
|
||||||
|
public final class ErrnoException extends Exception {
|
||||||
|
private final String functionName;
|
||||||
|
public final int errno;
|
||||||
|
|
||||||
|
public ErrnoException(String functionName, int errno) {
|
||||||
|
throw new AssertionError();
|
||||||
|
}
|
||||||
|
}
|
23
libcore/src/main/java/libcore/io/Libcore.java
Normal file
23
libcore/src/main/java/libcore/io/Libcore.java
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2011 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package libcore.io;
|
||||||
|
|
||||||
|
public final class Libcore {
|
||||||
|
private Libcore() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Os os = null;
|
||||||
|
}
|
24
libcore/src/main/java/libcore/io/Os.java
Normal file
24
libcore/src/main/java/libcore/io/Os.java
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2011 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package libcore.io;
|
||||||
|
|
||||||
|
import java.io.FileDescriptor;
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
|
||||||
|
public interface Os {
|
||||||
|
String strerror(int errno);
|
||||||
|
int write(FileDescriptor fd, ByteBuffer buffer) throws ErrnoException;
|
||||||
|
}
|
30
libcore/src/main/java/libcore/io/OsConstants.java
Normal file
30
libcore/src/main/java/libcore/io/OsConstants.java
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2011 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package libcore.io;
|
||||||
|
|
||||||
|
public final class OsConstants {
|
||||||
|
private OsConstants() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final int EINTR = placeholder();
|
||||||
|
|
||||||
|
public static String errnoName(int errno) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// A hack to avoid these constants being inlined by javac...
|
||||||
|
private static int placeholder() { return 0; }
|
||||||
|
}
|
24
os-compat/build.gradle
Normal file
24
os-compat/build.gradle
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
apply plugin: 'com.android.library'
|
||||||
|
|
||||||
|
android {
|
||||||
|
compileSdkVersion 31
|
||||||
|
defaultConfig {
|
||||||
|
minSdkVersion 19
|
||||||
|
targetSdkVersion 31
|
||||||
|
}
|
||||||
|
androidResources {
|
||||||
|
namespace = "androidx.system"
|
||||||
|
}
|
||||||
|
buildFeatures {
|
||||||
|
aidl = false
|
||||||
|
androidResources = false
|
||||||
|
buildConfig = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
compileOnly rootProject.fileTree("thirdparty/androidx/annotation/1.3.0/annotation-1.3.0.jar")
|
||||||
|
compileOnly project(':libcore')
|
||||||
|
}
|
||||||
|
|
||||||
|
apply from: "$project.rootDir/config/android-checkstyle.gradle"
|
91
os-compat/src/main/java/androidx/system/ErrnoException.java
Normal file
91
os-compat/src/main/java/androidx/system/ErrnoException.java
Normal file
|
@ -0,0 +1,91 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2011 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package androidx.system;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.net.SocketException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A checked exception thrown when {@link OsCompat} methods fail. This exception contains the native
|
||||||
|
* errno value, for comparison against the constants in {@link OsConstantsCompat}, should sophisticated
|
||||||
|
* callers need to adjust their behavior based on the exact failure.
|
||||||
|
*/
|
||||||
|
public final class ErrnoException extends Exception {
|
||||||
|
private final String functionName;
|
||||||
|
private final int errno;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs an instance with the given function name and errno value.
|
||||||
|
*/
|
||||||
|
public ErrnoException(String functionName, int errno) {
|
||||||
|
this.functionName = functionName;
|
||||||
|
this.errno = errno;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs an instance with the given function name, errno value, and cause.
|
||||||
|
*/
|
||||||
|
public ErrnoException(String functionName, int errno, Throwable cause) {
|
||||||
|
super(cause);
|
||||||
|
this.functionName = functionName;
|
||||||
|
this.errno = errno;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The errno value, for comparison with the {@code E} constants in {@link OsConstantsCompat}.
|
||||||
|
*/
|
||||||
|
public int getErrno() {
|
||||||
|
return errno;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts the stashed function name and errno value to a human-readable string.
|
||||||
|
* We do this here rather than in the constructor so that callers only pay for
|
||||||
|
* this if they need it.
|
||||||
|
*/
|
||||||
|
@Override public String getMessage() {
|
||||||
|
String errnoName = OsConstantsCompat.errnoName(errno);
|
||||||
|
if (errnoName == null) {
|
||||||
|
errnoName = "errno " + errno;
|
||||||
|
}
|
||||||
|
String description = OsCompat.strerror(errno);
|
||||||
|
return functionName + " failed: " + errnoName + " (" + description + ")";
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Throws an {@link IOException} with a message based on {@link #getMessage()} and with this
|
||||||
|
* instance as the cause.
|
||||||
|
*
|
||||||
|
* <p>This method always terminates by throwing the exception. Callers can write
|
||||||
|
* {@code throw e.rethrowAsIOException()} to make that clear to the compiler.
|
||||||
|
*/
|
||||||
|
public IOException rethrowAsIOException() throws IOException {
|
||||||
|
throw new IOException(getMessage(), this);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Throws a {@link SocketException} with a message based on {@link #getMessage()} and with this
|
||||||
|
* instance as the cause.
|
||||||
|
*
|
||||||
|
* <p>This method always terminates by throwing the exception. Callers can write
|
||||||
|
* {@code throw e.rethrowAsIOException()} to make that clear to the compiler.
|
||||||
|
*/
|
||||||
|
public SocketException rethrowAsSocketException() throws SocketException {
|
||||||
|
final SocketException newException = new SocketException(getMessage());
|
||||||
|
newException.initCause(this);
|
||||||
|
throw newException;
|
||||||
|
}
|
||||||
|
}
|
33
os-compat/src/main/java/androidx/system/Os.java
Normal file
33
os-compat/src/main/java/androidx/system/Os.java
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2011 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package androidx.system;
|
||||||
|
|
||||||
|
import java.io.FileDescriptor;
|
||||||
|
import java.io.InterruptedIOException;
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
|
||||||
|
interface Os {
|
||||||
|
String strerror(int errno);
|
||||||
|
|
||||||
|
int write(FileDescriptor fd, ByteBuffer buffer) throws ErrnoException, InterruptedIOException;
|
||||||
|
|
||||||
|
// https://android.googlesource.com/platform/libcore/+/lollipop-mr1-release/luni/src/main/java/libcore/io/Posix.java#253
|
||||||
|
static void maybeUpdateBufferPosition(ByteBuffer buffer, int originalPosition, int bytesReadOrWritten) {
|
||||||
|
if (bytesReadOrWritten > 0) {
|
||||||
|
buffer.position(bytesReadOrWritten + originalPosition);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
47
os-compat/src/main/java/androidx/system/OsApi21.java
Normal file
47
os-compat/src/main/java/androidx/system/OsApi21.java
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2011 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package androidx.system;
|
||||||
|
|
||||||
|
import androidx.annotation.RequiresApi;
|
||||||
|
|
||||||
|
import java.io.FileDescriptor;
|
||||||
|
import java.io.InterruptedIOException;
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
|
||||||
|
import static android.os.Build.VERSION.SDK_INT;
|
||||||
|
import static androidx.system.Os.maybeUpdateBufferPosition;
|
||||||
|
|
||||||
|
@RequiresApi(21)
|
||||||
|
final class OsApi21 implements Os {
|
||||||
|
@Override
|
||||||
|
public String strerror(int errno) {
|
||||||
|
return android.system.Os.strerror(errno);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int write(FileDescriptor fd, ByteBuffer buffer) throws ErrnoException, InterruptedIOException {
|
||||||
|
try {
|
||||||
|
final int position = buffer.position();
|
||||||
|
final int bytesWritten = android.system.Os.write(fd, buffer);
|
||||||
|
if (SDK_INT < 22) {
|
||||||
|
maybeUpdateBufferPosition(buffer, position, bytesWritten);
|
||||||
|
}
|
||||||
|
return bytesWritten;
|
||||||
|
} catch (android.system.ErrnoException e) {
|
||||||
|
throw new ErrnoException("write", e.errno);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
58
os-compat/src/main/java/androidx/system/OsCompat.java
Normal file
58
os-compat/src/main/java/androidx/system/OsCompat.java
Normal file
|
@ -0,0 +1,58 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2011 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package androidx.system;
|
||||||
|
|
||||||
|
import java.io.FileDescriptor;
|
||||||
|
import java.io.InterruptedIOException;
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
|
||||||
|
import static android.os.Build.VERSION.SDK_INT;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Access to low-level system functionality. Most of these are system calls. Most users will want
|
||||||
|
* to use higher-level APIs where available, but this class provides access to the underlying
|
||||||
|
* primitives used to implement the higher-level APIs.
|
||||||
|
*
|
||||||
|
* <p>The corresponding constants can be found in {@link OsConstantsCompat}.
|
||||||
|
*/
|
||||||
|
public final class OsCompat {
|
||||||
|
private OsCompat() {
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final Os IMPL;
|
||||||
|
|
||||||
|
static {
|
||||||
|
if (SDK_INT >= 21) {
|
||||||
|
IMPL = new OsApi21();
|
||||||
|
} else {
|
||||||
|
IMPL = new OsLibcore();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* See <a href="http://man7.org/linux/man-pages/man3/strerror.3.html">strerror(2)</a>.
|
||||||
|
*/
|
||||||
|
public static String strerror(int errno) {
|
||||||
|
return IMPL.strerror(errno);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* See <a href="http://man7.org/linux/man-pages/man2/write.2.html">write(2)</a>.
|
||||||
|
*/
|
||||||
|
public static int write(FileDescriptor fd, ByteBuffer buffer) throws ErrnoException, InterruptedIOException {
|
||||||
|
return IMPL.write(fd, buffer);
|
||||||
|
}
|
||||||
|
}
|
21
os-compat/src/main/java/androidx/system/OsConstants.java
Normal file
21
os-compat/src/main/java/androidx/system/OsConstants.java
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2011 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package androidx.system;
|
||||||
|
|
||||||
|
interface OsConstants {
|
||||||
|
|
||||||
|
String errnoName(int errno);
|
||||||
|
}
|
|
@ -0,0 +1,11 @@
|
||||||
|
package androidx.system;
|
||||||
|
|
||||||
|
import androidx.annotation.RequiresApi;
|
||||||
|
|
||||||
|
@RequiresApi(21)
|
||||||
|
final class OsConstantsApi21 implements OsConstants {
|
||||||
|
@Override
|
||||||
|
public String errnoName(int errno) {
|
||||||
|
return android.system.OsConstants.errnoName(errno);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,43 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2011 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package androidx.system;
|
||||||
|
|
||||||
|
import android.system.Os;
|
||||||
|
|
||||||
|
import static android.os.Build.VERSION.SDK_INT;
|
||||||
|
|
||||||
|
public final class OsConstantsCompat {
|
||||||
|
private OsConstantsCompat() {
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final OsConstants IMPL;
|
||||||
|
|
||||||
|
static {
|
||||||
|
if (SDK_INT >= 21) {
|
||||||
|
IMPL = new OsConstantsApi21();
|
||||||
|
} else {
|
||||||
|
IMPL = new OsConstantsLibcore();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the string name of an errno value.
|
||||||
|
* For example, "EACCES". See {@link Os#strerror} for human-readable errno descriptions.
|
||||||
|
*/
|
||||||
|
public static String errnoName(int errno) {
|
||||||
|
return IMPL.errnoName(errno);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,8 @@
|
||||||
|
package androidx.system;
|
||||||
|
|
||||||
|
final class OsConstantsLibcore implements OsConstants {
|
||||||
|
@Override
|
||||||
|
public String errnoName(int errno) {
|
||||||
|
return libcore.io.OsConstants.errnoName(errno);
|
||||||
|
}
|
||||||
|
}
|
102
os-compat/src/main/java/androidx/system/OsLibcore.java
Normal file
102
os-compat/src/main/java/androidx/system/OsLibcore.java
Normal file
|
@ -0,0 +1,102 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2011 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package androidx.system;
|
||||||
|
|
||||||
|
import libcore.io.Libcore;
|
||||||
|
import libcore.io.OsConstants;
|
||||||
|
|
||||||
|
import java.io.FileDescriptor;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InterruptedIOException;
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
|
||||||
|
import static androidx.system.Os.maybeUpdateBufferPosition;
|
||||||
|
|
||||||
|
final class OsLibcore implements Os {
|
||||||
|
@Override
|
||||||
|
public String strerror(int errno) {
|
||||||
|
return Libcore.os.strerror(errno);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int write(FileDescriptor fd, ByteBuffer buffer) throws ErrnoException, InterruptedIOException {
|
||||||
|
try {
|
||||||
|
return ioFailureRetry("write", fd, () -> {
|
||||||
|
final int position = buffer.position();
|
||||||
|
final int bytesWritten = Libcore.os.write(fd, buffer);
|
||||||
|
maybeUpdateBufferPosition(buffer, position, bytesWritten);
|
||||||
|
return bytesWritten;
|
||||||
|
});
|
||||||
|
} catch (libcore.io.ErrnoException e) {
|
||||||
|
throw new ErrnoException("write", e.errno);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// https://android.googlesource.com/platform/libcore/+/lollipop-release/luni/src/main/native/libcore_io_Posix.cpp#128
|
||||||
|
// https://android.googlesource.com/platform/libcore/+/kitkat-release/luni/src/main/java/libcore/io/IoBridge.java#186
|
||||||
|
private static int ioFailureRetry(String functionName, FileDescriptor fd, SysCall syscall)
|
||||||
|
throws libcore.io.ErrnoException, InterruptedIOException {
|
||||||
|
if (!fd.valid()) {
|
||||||
|
throw UndeclaredExceptions.raise(new IOException("File descriptor closed"));
|
||||||
|
}
|
||||||
|
int rc = -1;
|
||||||
|
do {
|
||||||
|
int syscallErrno = 0;
|
||||||
|
try {
|
||||||
|
rc = syscall.call();
|
||||||
|
} catch (libcore.io.ErrnoException e) {
|
||||||
|
syscallErrno = e.errno;
|
||||||
|
}
|
||||||
|
if (rc == -1 && !fd.valid()) {
|
||||||
|
throw new InterruptedIOException(functionName + " interrupted");
|
||||||
|
}
|
||||||
|
if (rc == -1 && syscallErrno != OsConstants.EINTR) {
|
||||||
|
throw new libcore.io.ErrnoException(functionName, syscallErrno);
|
||||||
|
}
|
||||||
|
} while (rc == -1);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
@FunctionalInterface
|
||||||
|
private interface SysCall {
|
||||||
|
|
||||||
|
Integer call() throws libcore.io.ErrnoException;
|
||||||
|
}
|
||||||
|
|
||||||
|
// https://dzone.com/articles/throwing-undeclared-checked
|
||||||
|
private static final class UndeclaredExceptions extends RuntimeException {
|
||||||
|
private static Throwable sThrowable = null;
|
||||||
|
|
||||||
|
public static synchronized RuntimeException raise(Throwable throwable) {
|
||||||
|
if (throwable instanceof ReflectiveOperationException || throwable instanceof RuntimeException) {
|
||||||
|
throw new IllegalArgumentException("Unsupported exception: " + throwable.getClass());
|
||||||
|
}
|
||||||
|
|
||||||
|
sThrowable = throwable;
|
||||||
|
try {
|
||||||
|
return UndeclaredExceptions.class.newInstance();
|
||||||
|
} catch (ReflectiveOperationException e) {
|
||||||
|
return new RuntimeException(e);
|
||||||
|
} finally {
|
||||||
|
sThrowable = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private UndeclaredExceptions() throws Throwable {
|
||||||
|
throw sThrowable;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -4,7 +4,7 @@ android {
|
||||||
compileSdkVersion 31
|
compileSdkVersion 31
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
applicationId "com.genymobile.scrcpy"
|
applicationId "com.genymobile.scrcpy"
|
||||||
minSdkVersion 21
|
minSdkVersion 19
|
||||||
targetSdkVersion 31
|
targetSdkVersion 31
|
||||||
versionCode 12400
|
versionCode 12400
|
||||||
versionName "1.24"
|
versionName "1.24"
|
||||||
|
@ -21,6 +21,8 @@ android {
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation fileTree(dir: 'libs', include: ['*.jar'])
|
implementation fileTree(dir: 'libs', include: ['*.jar'])
|
||||||
testImplementation 'junit:junit:4.13.1'
|
testImplementation 'junit:junit:4.13.1'
|
||||||
|
compileOnly rootProject.fileTree("thirdparty/androidx/annotation/1.3.0/annotation-1.3.0.jar")
|
||||||
|
implementation project(':os-compat')
|
||||||
}
|
}
|
||||||
|
|
||||||
apply from: "$project.rootDir/config/android-checkstyle.gradle"
|
apply from: "$project.rootDir/config/android-checkstyle.gradle"
|
||||||
|
|
|
@ -20,12 +20,14 @@ BUILD_TOOLS=${ANDROID_BUILD_TOOLS:-31.0.0}
|
||||||
BUILD_DIR="$(realpath ${BUILD_DIR:-build_manual})"
|
BUILD_DIR="$(realpath ${BUILD_DIR:-build_manual})"
|
||||||
CLASSES_DIR="$BUILD_DIR/classes"
|
CLASSES_DIR="$BUILD_DIR/classes"
|
||||||
SERVER_DIR=$(dirname "$0")
|
SERVER_DIR=$(dirname "$0")
|
||||||
|
ROOT_PROJECT_DIR=$(realpath $SERVER_DIR/..)
|
||||||
SERVER_BINARY=scrcpy-server
|
SERVER_BINARY=scrcpy-server
|
||||||
ANDROID_JAR="$ANDROID_HOME/platforms/android-$PLATFORM/android.jar"
|
ANDROID_JAR="$ANDROID_HOME/platforms/android-$PLATFORM/android.jar"
|
||||||
|
|
||||||
echo "Platform: android-$PLATFORM"
|
echo "Platform: android-$PLATFORM"
|
||||||
echo "Build-tools: $BUILD_TOOLS"
|
echo "Build-tools: $BUILD_TOOLS"
|
||||||
echo "Build dir: $BUILD_DIR"
|
echo "Build dir: $BUILD_DIR"
|
||||||
|
echo "Root project dir: $ROOT_PROJECT_DIR"
|
||||||
|
|
||||||
rm -rf "$CLASSES_DIR" "$BUILD_DIR/$SERVER_BINARY" classes.dex
|
rm -rf "$CLASSES_DIR" "$BUILD_DIR/$SERVER_BINARY" classes.dex
|
||||||
mkdir -p "$CLASSES_DIR/com/genymobile/scrcpy"
|
mkdir -p "$CLASSES_DIR/com/genymobile/scrcpy"
|
||||||
|
@ -48,8 +50,15 @@ cd "$SERVER_DIR/src/main/aidl"
|
||||||
|
|
||||||
echo "Compiling java sources..."
|
echo "Compiling java sources..."
|
||||||
cd ../java
|
cd ../java
|
||||||
javac -bootclasspath "$ANDROID_JAR" -cp "$CLASSES_DIR" -d "$CLASSES_DIR" \
|
classpath="$CLASSES_DIR"
|
||||||
|
classpath="$classpath:$ROOT_PROJECT_DIR/thirdparty/androidx/annotation/1.3.0/annotation-1.3.0.jar"
|
||||||
|
# https://stackoverflow.com/a/58768648/2444099
|
||||||
|
classpath="$classpath:$ANDROID_HOME/build-tools/$BUILD_TOOLS/core-lambda-stubs.jar"
|
||||||
|
javac -bootclasspath "$ANDROID_JAR" -cp "$classpath" -d "$CLASSES_DIR" \
|
||||||
|
-encoding UTF-8 \
|
||||||
-source 1.8 -target 1.8 \
|
-source 1.8 -target 1.8 \
|
||||||
|
$ROOT_PROJECT_DIR/os-compat/src/main/java/androidx/system/*.java \
|
||||||
|
$ROOT_PROJECT_DIR/libcore/src/main/java/libcore/io/*.java \
|
||||||
com/genymobile/scrcpy/*.java \
|
com/genymobile/scrcpy/*.java \
|
||||||
com/genymobile/scrcpy/wrappers/*.java
|
com/genymobile/scrcpy/wrappers/*.java
|
||||||
|
|
||||||
|
@ -61,6 +70,7 @@ then
|
||||||
# use dx
|
# use dx
|
||||||
"$ANDROID_HOME/build-tools/$BUILD_TOOLS/dx" --dex \
|
"$ANDROID_HOME/build-tools/$BUILD_TOOLS/dx" --dex \
|
||||||
--output "$BUILD_DIR/classes.dex" \
|
--output "$BUILD_DIR/classes.dex" \
|
||||||
|
androidx/system/*.class \
|
||||||
android/view/*.class \
|
android/view/*.class \
|
||||||
android/content/*.class \
|
android/content/*.class \
|
||||||
com/genymobile/scrcpy/*.class \
|
com/genymobile/scrcpy/*.class \
|
||||||
|
@ -74,6 +84,7 @@ else
|
||||||
# use d8
|
# use d8
|
||||||
"$ANDROID_HOME/build-tools/$BUILD_TOOLS/d8" --classpath "$ANDROID_JAR" \
|
"$ANDROID_HOME/build-tools/$BUILD_TOOLS/d8" --classpath "$ANDROID_JAR" \
|
||||||
--output "$BUILD_DIR/classes.zip" \
|
--output "$BUILD_DIR/classes.zip" \
|
||||||
|
androidx/system/*.class \
|
||||||
android/view/*.class \
|
android/view/*.class \
|
||||||
android/content/*.class \
|
android/content/*.class \
|
||||||
com/genymobile/scrcpy/*.class \
|
com/genymobile/scrcpy/*.class \
|
||||||
|
|
|
@ -1,8 +1,7 @@
|
||||||
package com.genymobile.scrcpy;
|
package com.genymobile.scrcpy;
|
||||||
|
|
||||||
import android.system.ErrnoException;
|
import androidx.system.ErrnoException;
|
||||||
import android.system.Os;
|
import androidx.system.OsCompat;
|
||||||
import android.system.OsConstants;
|
|
||||||
|
|
||||||
import java.io.FileDescriptor;
|
import java.io.FileDescriptor;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
@ -14,25 +13,14 @@ public final class IO {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void writeFully(FileDescriptor fd, ByteBuffer from) throws IOException {
|
public static void writeFully(FileDescriptor fd, ByteBuffer from) throws IOException {
|
||||||
// ByteBuffer position is not updated as expected by Os.write() on old Android versions, so
|
|
||||||
// count the remaining bytes manually.
|
|
||||||
// See <https://github.com/Genymobile/scrcpy/issues/291>.
|
|
||||||
int remaining = from.remaining();
|
|
||||||
while (remaining > 0) {
|
|
||||||
try {
|
try {
|
||||||
int w = Os.write(fd, from);
|
while (from.hasRemaining()) {
|
||||||
if (BuildConfig.DEBUG && w < 0) {
|
OsCompat.write(fd, from);
|
||||||
// w should not be negative, since an exception is thrown on error
|
|
||||||
throw new AssertionError("Os.write() returned a negative value (" + w + ")");
|
|
||||||
}
|
}
|
||||||
remaining -= w;
|
|
||||||
} catch (ErrnoException e) {
|
} catch (ErrnoException e) {
|
||||||
if (e.errno != OsConstants.EINTR) {
|
|
||||||
throw new IOException(e);
|
throw new IOException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void writeFully(FileDescriptor fd, byte[] buffer, int offset, int len) throws IOException {
|
public static void writeFully(FileDescriptor fd, byte[] buffer, int offset, int len) throws IOException {
|
||||||
writeFully(fd, ByteBuffer.wrap(buffer, offset, len));
|
writeFully(fd, ByteBuffer.wrap(buffer, offset, len));
|
||||||
|
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,87 @@
|
||||||
|
package com.genymobile.scrcpy;
|
||||||
|
|
||||||
|
import android.media.MediaCodecInfo;
|
||||||
|
import android.media.MediaCodecList;
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.annotation.RequiresApi;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import static android.media.MediaCodecList.REGULAR_CODECS;
|
||||||
|
import static android.os.Build.VERSION.SDK_INT;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Version of {@link MediaCodecList} that backports {@link #getCodecInfos()} to Kitkat.
|
||||||
|
*/
|
||||||
|
abstract class MediaCodecListCompat {
|
||||||
|
MediaCodecListCompat() {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the list of codecs that are suitable
|
||||||
|
* for regular (buffer-to-buffer) decoding or encoding.
|
||||||
|
*/
|
||||||
|
static MediaCodecListCompat regular() {
|
||||||
|
if (SDK_INT >= 21) {
|
||||||
|
return new Platform();
|
||||||
|
} else {
|
||||||
|
return new Backport();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the list of {@link MediaCodecInfo} objects for the list
|
||||||
|
* of media-codecs.
|
||||||
|
*/
|
||||||
|
@NonNull
|
||||||
|
abstract MediaCodecInfo[] getCodecInfos();
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
final MediaCodecInfo[] getEncoderInfosForType(@NonNull String mimeType) {
|
||||||
|
List<MediaCodecInfo> result = new ArrayList<>();
|
||||||
|
MediaCodecInfo[] codecInfos = MediaCodecListCompat.regular().getCodecInfos();
|
||||||
|
for (MediaCodecInfo codecInfo : codecInfos) {
|
||||||
|
if (codecInfo.isEncoder() && Arrays.asList(codecInfo.getSupportedTypes()).contains(mimeType)) {
|
||||||
|
result.add(codecInfo);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result.toArray(new MediaCodecInfo[result.size()]);
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("deprecation")
|
||||||
|
private static class Backport extends MediaCodecListCompat {
|
||||||
|
private final MediaCodecInfo[] mCodecInfos;
|
||||||
|
|
||||||
|
Backport() {
|
||||||
|
final int size = MediaCodecList.getCodecCount();
|
||||||
|
final MediaCodecInfo[] codecInfos = new MediaCodecInfo[size];
|
||||||
|
for (int i = 0; i < size; i++) {
|
||||||
|
codecInfos[i] = MediaCodecList.getCodecInfoAt(i);
|
||||||
|
}
|
||||||
|
this.mCodecInfos = codecInfos;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
@Override
|
||||||
|
public final MediaCodecInfo[] getCodecInfos() {
|
||||||
|
return Arrays.copyOf(mCodecInfos, mCodecInfos.length);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@RequiresApi(21)
|
||||||
|
private static class Platform extends MediaCodecListCompat {
|
||||||
|
private final MediaCodecList mDelegate;
|
||||||
|
|
||||||
|
Platform() {
|
||||||
|
this.mDelegate = new MediaCodecList(REGULAR_CODECS);
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
@Override
|
||||||
|
MediaCodecInfo[] getCodecInfos() {
|
||||||
|
return mDelegate.getCodecInfos();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -2,10 +2,10 @@ package com.genymobile.scrcpy;
|
||||||
|
|
||||||
import com.genymobile.scrcpy.wrappers.SurfaceControl;
|
import com.genymobile.scrcpy.wrappers.SurfaceControl;
|
||||||
|
|
||||||
|
import android.annotation.SuppressLint;
|
||||||
import android.graphics.Rect;
|
import android.graphics.Rect;
|
||||||
import android.media.MediaCodec;
|
import android.media.MediaCodec;
|
||||||
import android.media.MediaCodecInfo;
|
import android.media.MediaCodecInfo;
|
||||||
import android.media.MediaCodecList;
|
|
||||||
import android.media.MediaFormat;
|
import android.media.MediaFormat;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.os.IBinder;
|
import android.os.IBinder;
|
||||||
|
@ -14,8 +14,6 @@ import android.view.Surface;
|
||||||
import java.io.FileDescriptor;
|
import java.io.FileDescriptor;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.concurrent.atomic.AtomicBoolean;
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
|
|
||||||
|
@ -25,6 +23,9 @@ public class ScreenEncoder implements Device.RotationListener {
|
||||||
private static final int REPEAT_FRAME_DELAY_US = 100_000; // repeat after 100ms
|
private static final int REPEAT_FRAME_DELAY_US = 100_000; // repeat after 100ms
|
||||||
private static final String KEY_MAX_FPS_TO_ENCODER = "max-fps-to-encoder";
|
private static final String KEY_MAX_FPS_TO_ENCODER = "max-fps-to-encoder";
|
||||||
|
|
||||||
|
@SuppressLint("InlinedApi")
|
||||||
|
public static final String MIMETYPE_VIDEO_AVC = MediaFormat.MIMETYPE_VIDEO_AVC;
|
||||||
|
|
||||||
// Keep the values in descending order
|
// Keep the values in descending order
|
||||||
private static final int[] MAX_SIZE_FALLBACK = {2560, 1920, 1600, 1280, 1024, 800};
|
private static final int[] MAX_SIZE_FALLBACK = {2560, 1920, 1600, 1280, 1024, 800};
|
||||||
|
|
||||||
|
@ -144,7 +145,8 @@ public class ScreenEncoder implements Device.RotationListener {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean encode(MediaCodec codec, FileDescriptor fd) throws IOException {
|
private boolean encode(MediaCodec platformCodec, 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();
|
||||||
|
|
||||||
|
@ -201,28 +203,18 @@ public class ScreenEncoder implements Device.RotationListener {
|
||||||
IO.writeFully(fd, headerBuffer);
|
IO.writeFully(fd, headerBuffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static MediaCodecInfo[] listEncoders() {
|
|
||||||
List<MediaCodecInfo> result = new ArrayList<>();
|
|
||||||
MediaCodecList list = new MediaCodecList(MediaCodecList.REGULAR_CODECS);
|
|
||||||
for (MediaCodecInfo codecInfo : list.getCodecInfos()) {
|
|
||||||
if (codecInfo.isEncoder() && Arrays.asList(codecInfo.getSupportedTypes()).contains(MediaFormat.MIMETYPE_VIDEO_AVC)) {
|
|
||||||
result.add(codecInfo);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return result.toArray(new MediaCodecInfo[result.size()]);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static MediaCodec createCodec(String encoderName) throws IOException {
|
private static MediaCodec createCodec(String encoderName) throws IOException {
|
||||||
if (encoderName != null) {
|
if (encoderName != null) {
|
||||||
Ln.d("Creating encoder by name: '" + encoderName + "'");
|
Ln.d("Creating encoder by name: '" + encoderName + "'");
|
||||||
try {
|
try {
|
||||||
return MediaCodec.createByCodecName(encoderName);
|
return MediaCodec.createByCodecName(encoderName);
|
||||||
} catch (IllegalArgumentException e) {
|
} catch (IllegalArgumentException e) {
|
||||||
MediaCodecInfo[] encoders = listEncoders();
|
MediaCodecInfo[] encoders = MediaCodecListCompat.regular()
|
||||||
|
.getEncoderInfosForType(MIMETYPE_VIDEO_AVC);
|
||||||
throw new InvalidEncoderException(encoderName, encoders);
|
throw new InvalidEncoderException(encoderName, encoders);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
MediaCodec codec = MediaCodec.createEncoderByType(MediaFormat.MIMETYPE_VIDEO_AVC);
|
MediaCodec codec = MediaCodec.createEncoderByType(MIMETYPE_VIDEO_AVC);
|
||||||
Ln.d("Using encoder: '" + codec.getName() + "'");
|
Ln.d("Using encoder: '" + codec.getName() + "'");
|
||||||
return codec;
|
return codec;
|
||||||
}
|
}
|
||||||
|
@ -246,7 +238,7 @@ public class ScreenEncoder implements Device.RotationListener {
|
||||||
|
|
||||||
private static MediaFormat createFormat(int bitRate, int maxFps, List<CodecOption> codecOptions) {
|
private static MediaFormat createFormat(int bitRate, int maxFps, List<CodecOption> codecOptions) {
|
||||||
MediaFormat format = new MediaFormat();
|
MediaFormat format = new MediaFormat();
|
||||||
format.setString(MediaFormat.KEY_MIME, MediaFormat.MIMETYPE_VIDEO_AVC);
|
format.setString(MediaFormat.KEY_MIME, MIMETYPE_VIDEO_AVC);
|
||||||
format.setInteger(MediaFormat.KEY_BIT_RATE, bitRate);
|
format.setInteger(MediaFormat.KEY_BIT_RATE, bitRate);
|
||||||
// must be present to configure the encoder, but does not impact the actual frame rate, which is variable
|
// must be present to configure the encoder, but does not impact the actual frame rate, which is variable
|
||||||
format.setInteger(MediaFormat.KEY_FRAME_RATE, 60);
|
format.setInteger(MediaFormat.KEY_FRAME_RATE, 60);
|
||||||
|
|
|
@ -1 +1,3 @@
|
||||||
include ':server'
|
include ':server'
|
||||||
|
include ':os-compat'
|
||||||
|
include ':libcore'
|
||||||
|
|
BIN
thirdparty/androidx/annotation/1.3.0/annotation-1.3.0.jar
vendored
Normal file
BIN
thirdparty/androidx/annotation/1.3.0/annotation-1.3.0.jar
vendored
Normal file
Binary file not shown.
Loading…
Add table
Add a link
Reference in a new issue