From 59fc89f4e341792b242a913e371c6e838b5b6ff4 Mon Sep 17 00:00:00 2001 From: Miuzarte <982809597@qq.com> Date: Mon, 13 Jul 2026 13:21:49 +0800 Subject: [PATCH 1/3] upstream: scrcpy v4.1 --- CHANGELOG.md | 5 ++++ app/build.gradle.kts | 20 +++++++-------- .../scrcpyforandroid/NativeCoreFacade.kt | 2 ++ ...xBDecoder.kt => MediaCodecVideoDecoder.kt} | 10 +++++--- .../nativecore/VideoDecoderController.kt | 16 ++++++------ .../pages/ScrcpyAllOptionsScreen.kt | 10 ++++++++ .../scrcpyforandroid/scrcpy/ClientOptions.kt | 7 ++++++ .../scrcpyforandroid/scrcpy/Scrcpy.kt | 25 ++++++++++++++++--- .../scrcpyforandroid/scrcpy/ServerParams.kt | 4 +++ .../scrcpyforandroid/scrcpy/Shared.kt | 2 ++ .../scrcpyforandroid/storage/ScrcpyOptions.kt | 14 +++++++++++ app/src/main/res/values-zh/strings.xml | 1 + app/src/main/res/values/strings.xml | 1 + 13 files changed, 93 insertions(+), 24 deletions(-) rename app/src/main/java/io/github/miuzarte/scrcpyforandroid/nativecore/{AnnexBDecoder.kt => MediaCodecVideoDecoder.kt} (96%) diff --git a/CHANGELOG.md b/CHANGELOG.md index c9b7c4fb..12f4eaec 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,10 @@ # Change Log +## 0.5.0 + +- 依赖: scrcpy 升级至 v4.1 +- 新增: vp8/vp9 解码 + ## 0.4.5 - 重构: 改了好多不知道怎么写,总之修复了一些已知问题 diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 880bbe21..f346153b 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -59,8 +59,8 @@ android { applicationId = "io.github.miuzarte.scrcpyforandroid" minSdk = 26 targetSdk = 37 - versionCode = 35 - versionName = "0.4.5_pre1" + versionCode = 36 + versionName = "0.5.0" externalNativeBuild { cmake { @@ -168,9 +168,9 @@ dependencies { } val scrcpyServerAssetDir = "${project.projectDir}/src/main/assets/bin" -val scrcpyServerAssetFile = "$scrcpyServerAssetDir/scrcpy-server-v4.0" -val scrcpyServerDownloadUrl = "https://github.com/Genymobile/scrcpy/releases/download/v4.0/scrcpy-server-v4.0" -val scrcpyServerSha256 = "84924bd564a1eb6089c872c7521f968058977f91f5ff02514a8c74aff3210f3a" +val scrcpyServerAssetFile = "$scrcpyServerAssetDir/scrcpy-server-v4.1" +val scrcpyServerDownloadUrl = "https://github.com/Genymobile/scrcpy/releases/download/v4.1/scrcpy-server-v4.1" +val scrcpyServerSha256 = "deacb991ed2509715160ffdc7907e47b4160eb30d1566217e9047fd5b8850cae" val downloadScrcpyServer by tasks.registering { description = "Download scrcpy-server binary from GitHub releases if absent or SHA256 mismatch" @@ -203,7 +203,7 @@ val downloadScrcpyServer by tasks.registering { val needsDownload = !file.exists() || computeSha256(file) != expectedSha if (needsDownload) { - logger.lifecycle("Downloading scrcpy-server-v4.0 from GitHub releases...") + logger.lifecycle("Downloading scrcpy-server-v4.1 from GitHub releases...") try { URI(url).toURL().openStream().use { input -> file.outputStream().use { output -> @@ -212,7 +212,7 @@ val downloadScrcpyServer by tasks.registering { } } catch (e: Exception) { throw GradleException( - "Failed to download scrcpy-server-v4.0 from GitHub releases.\n" + + "Failed to download scrcpy-server-v4.1 from GitHub releases.\n" + " URL: $url\n" + " You may download it manually and place it at: ${file.absolutePath}\n" + " If you are behind a proxy, check your Gradle proxy settings\n" + @@ -223,14 +223,14 @@ val downloadScrcpyServer by tasks.registering { val actualSha = computeSha256(file) require(actualSha == expectedSha) { - "SHA256 mismatch for scrcpy-server-v4.0!\n" + + "SHA256 mismatch for scrcpy-server-v4.1!\n" + " Expected: $expectedSha\n" + " Got: $actualSha\n" + " Delete ${file.absolutePath} to retry download." } - logger.lifecycle("scrcpy-server-v4.0 downloaded and verified.") + logger.lifecycle("scrcpy-server-v4.1 downloaded and verified.") } else { - logger.lifecycle("scrcpy-server-v4.0 exists with correct SHA256, skip download.") + logger.lifecycle("scrcpy-server-v4.1 exists with correct SHA256, skip download.") } } } diff --git a/app/src/main/java/io/github/miuzarte/scrcpyforandroid/NativeCoreFacade.kt b/app/src/main/java/io/github/miuzarte/scrcpyforandroid/NativeCoreFacade.kt index 594a4695..abd5657e 100644 --- a/app/src/main/java/io/github/miuzarte/scrcpyforandroid/NativeCoreFacade.kt +++ b/app/src/main/java/io/github/miuzarte/scrcpyforandroid/NativeCoreFacade.kt @@ -327,6 +327,8 @@ object NativeCoreFacade { Codec.H264 -> "video/avc" Codec.H265 -> "video/hevc" Codec.AV1 -> "video/av01" + Codec.VP8 -> "video/x-vnd.on2.vp8" + Codec.VP9 -> "video/x-vnd.on2.vp9" else -> "video/avc" } diff --git a/app/src/main/java/io/github/miuzarte/scrcpyforandroid/nativecore/AnnexBDecoder.kt b/app/src/main/java/io/github/miuzarte/scrcpyforandroid/nativecore/MediaCodecVideoDecoder.kt similarity index 96% rename from app/src/main/java/io/github/miuzarte/scrcpyforandroid/nativecore/AnnexBDecoder.kt rename to app/src/main/java/io/github/miuzarte/scrcpyforandroid/nativecore/MediaCodecVideoDecoder.kt index 8f0a3ce7..edf85c52 100644 --- a/app/src/main/java/io/github/miuzarte/scrcpyforandroid/nativecore/AnnexBDecoder.kt +++ b/app/src/main/java/io/github/miuzarte/scrcpyforandroid/nativecore/MediaCodecVideoDecoder.kt @@ -6,19 +6,21 @@ import android.util.Log import android.view.Surface /** - * AnnexBDecoder + * MediaCodecVideoDecoder * * Purpose: - * - Wraps Android MediaCodec for Annex-B framed codecs (H.264/H.265/AV1). + * - Wraps Android MediaCodec for all video codecs (H.264/H.265/AV1/VP8/VP9). * - Handles critical startup packets (config/keyframes) and provides callbacks * for output size changes and FPS updates. + * - Codecs without Annex-B config packets (VP8/VP9) work natively: no CSD is + * required for MediaCodec initialization. * * Threading / safety: * - Public methods are synchronized to allow calls from multiple threads * (packet producer vs. teardown). Internally, MediaCodec callbacks and * buffer queues are used on the calling thread. */ -class AnnexBDecoder( +class MediaCodecVideoDecoder( width: Int, height: Int, outputSurface: Surface, @@ -232,7 +234,7 @@ class AnnexBDecoder( } companion object { - private const val TAG = "AnnexBDecoder" + private const val TAG = "MediaCodecVideoDecoder" private const val MIME_AVC = "video/avc" private const val INPUT_TIMEOUT_US = 10_000L private const val CRITICAL_INPUT_TIMEOUT_US = 50_000L diff --git a/app/src/main/java/io/github/miuzarte/scrcpyforandroid/nativecore/VideoDecoderController.kt b/app/src/main/java/io/github/miuzarte/scrcpyforandroid/nativecore/VideoDecoderController.kt index ea026ff5..b5612a93 100644 --- a/app/src/main/java/io/github/miuzarte/scrcpyforandroid/nativecore/VideoDecoderController.kt +++ b/app/src/main/java/io/github/miuzarte/scrcpyforandroid/nativecore/VideoDecoderController.kt @@ -13,7 +13,7 @@ import java.util.ArrayDeque import java.util.concurrent.CopyOnWriteArraySet /** - * Owns the video [AnnexBDecoder] lifecycle and all state surrounding it: + * Owns the video [MediaCodecVideoDecoder] lifecycle and all state surrounding it: * * - Creating / rebuilding / releasing the decoder bound to the persistent renderer surface. * - Caching and replaying bootstrap packets (config + keyframe + following frames) so a @@ -27,7 +27,7 @@ import java.util.concurrent.CopyOnWriteArraySet * - All public methods are safe to call from any thread; mutations are guarded by * [controllerLock] for synchronous methods and by the facade's session mutex for * suspend methods. - * - The decoder itself is single-threaded (AnnexBDecoder is @Synchronized). + * - The decoder itself is single-threaded (MediaCodecVideoDecoder is @Synchronized). * * This class does **not** decide session restart policy; it reports errors and lets * [NativeCoreFacade] decide whether to restart the scrcpy session. @@ -38,7 +38,7 @@ internal class VideoDecoderController( private val mainHandler = Handler(Looper.getMainLooper()) private val controllerLock = Any() - private var decoder: AnnexBDecoder? = null + private var decoder: MediaCodecVideoDecoder? = null @Volatile private var currentSessionInfo: Scrcpy.Session.SessionInfo? = null @@ -239,7 +239,7 @@ internal class VideoDecoderController( } /** - * Core method: construct a new [AnnexBDecoder] and replay bootstrap packets into it. + * Core method: construct a new [MediaCodecVideoDecoder] and replay bootstrap packets into it. * * Caller must have already released the old decoder (if any) and, when needed, recreated * the persistent surface. Must be called while holding [controllerLock]. @@ -259,6 +259,8 @@ internal class VideoDecoderController( Codec.H264 -> "video/avc" Codec.H265 -> "video/hevc" Codec.AV1 -> "video/av01" + Codec.VP8 -> "video/x-vnd.on2.vp8" + Codec.VP9 -> "video/x-vnd.on2.vp9" else -> "video/avc" } Log.i( @@ -269,7 +271,7 @@ internal class VideoDecoderController( "persistent=true", ) val newDecoder = try { - AnnexBDecoder( + MediaCodecVideoDecoder( width = session.width, height = session.height, outputSurface = surface, @@ -277,7 +279,7 @@ internal class VideoDecoderController( onOutputSizeChanged = { width, height -> val current = currentSessionInfo if (current == null || (current.width == width && current.height == height)) { - return@AnnexBDecoder + return@MediaCodecVideoDecoder } Log.i( TAG, @@ -340,7 +342,7 @@ internal class VideoDecoderController( // ---------- Bootstrap packet cache ---------- - private fun replayBootstrapPackets(decoder: AnnexBDecoder) { + private fun replayBootstrapPackets(decoder: MediaCodecVideoDecoder) { val snapshot = synchronized(bootstrapLock) { bootstrapPackets.toList() } if (snapshot.isEmpty()) { return diff --git a/app/src/main/java/io/github/miuzarte/scrcpyforandroid/pages/ScrcpyAllOptionsScreen.kt b/app/src/main/java/io/github/miuzarte/scrcpyforandroid/pages/ScrcpyAllOptionsScreen.kt index 4a24f044..c2eb0f37 100644 --- a/app/src/main/java/io/github/miuzarte/scrcpyforandroid/pages/ScrcpyAllOptionsScreen.kt +++ b/app/src/main/java/io/github/miuzarte/scrcpyforandroid/pages/ScrcpyAllOptionsScreen.kt @@ -1779,6 +1779,16 @@ internal fun ScrcpyAllOptionsPage( ) }, ) + SwitchPreference( + title = stringResource(R.string.scrcpyopt_ignore_video_encoder_constraints), + summary = "--ignore-video-encoder-constraints", + checked = soBundle.ignoreVideoEncoderConstraints, + onCheckedChange = { + soBundle = soBundle.copy( + ignoreVideoEncoderConstraints = it, + ) + }, + ) } } diff --git a/app/src/main/java/io/github/miuzarte/scrcpyforandroid/scrcpy/ClientOptions.kt b/app/src/main/java/io/github/miuzarte/scrcpyforandroid/scrcpy/ClientOptions.kt index e26627c5..52a920e5 100644 --- a/app/src/main/java/io/github/miuzarte/scrcpyforandroid/scrcpy/ClientOptions.kt +++ b/app/src/main/java/io/github/miuzarte/scrcpyforandroid/scrcpy/ClientOptions.kt @@ -212,6 +212,12 @@ data class ClientOptions( // --flex-display var flexDisplay: Boolean = false, // to server + + // --ignore-video-encoder-constraints + var ignoreVideoEncoderConstraints: Boolean = false, // to server + + // --no-terminal-title + // var updateTerminalTitle: Boolean = true, ) { enum class KeyInjectMode(val string: String) { MIXED("mixed"), @@ -633,6 +639,7 @@ data class ClientOptions( vdSystemDecorations = vdSystemDecorations, keepActive = keepActive, flexDisplay = flexDisplay, + ignoreVideoEncoderConstraints = ignoreVideoEncoderConstraints, list = list, ) } diff --git a/app/src/main/java/io/github/miuzarte/scrcpyforandroid/scrcpy/Scrcpy.kt b/app/src/main/java/io/github/miuzarte/scrcpyforandroid/scrcpy/Scrcpy.kt index 9d9ae774..aca9c143 100644 --- a/app/src/main/java/io/github/miuzarte/scrcpyforandroid/scrcpy/Scrcpy.kt +++ b/app/src/main/java/io/github/miuzarte/scrcpyforandroid/scrcpy/Scrcpy.kt @@ -116,9 +116,9 @@ class Scrcpy( companion object { private const val TAG = "Scrcpy" - const val DEFAULT_SERVER_ASSET = "bin/scrcpy-server-v4.0" - const val DEFAULT_SERVER_ASSET_NAME = "scrcpy-server-v4.0" - const val DEFAULT_SERVER_VERSION = "4.0" + const val DEFAULT_SERVER_ASSET = "bin/scrcpy-server-v4.1" + const val DEFAULT_SERVER_ASSET_NAME = "scrcpy-server-v4.1" + const val DEFAULT_SERVER_VERSION = "4.1" const val DEFAULT_REMOTE_PATH = "/data/local/tmp/scrcpy-server.jar" // Regex patterns for parsing server output @@ -389,6 +389,10 @@ class Scrcpy( session.setClipboard(text, paste) } + suspend fun scanFile(path: String) = withContext(Dispatchers.IO) { + session.scanFile(path) + } + suspend fun injectTouch( action: Int, pointerId: Long, @@ -1317,6 +1321,10 @@ class Scrcpy( withControlWriter("setClipboard") { setClipboard(text, paste) } } + suspend fun scanFile(path: String) = mutex.withLock { + withControlWriter("scanFile") { scanFile(path) } + } + suspend fun injectTouch( action: Int, pointerId: Long, @@ -1775,6 +1783,16 @@ class Scrcpy( output.flush() } + @Synchronized + fun scanFile(path: String) { + val bytes = path.toByteArray(Charsets.UTF_8) + require(bytes.size <= 255) { "scan file path is too long (max 255 bytes)" } + output.writeByte(TYPE_SCAN_FILE) + output.writeInt(bytes.size) + output.write(bytes) + output.flush() + } + private fun writePosition(x: Int, y: Int, screenWidth: Int, screenHeight: Int) { output.writeInt(x) output.writeInt(y) @@ -1836,6 +1854,7 @@ class Scrcpy( private const val TYPE_CAMERA_ZOOM_IN = 19 private const val TYPE_CAMERA_ZOOM_OUT = 20 private const val TYPE_RESIZE_DISPLAY = 21 + private const val TYPE_SCAN_FILE = 22 private const val SEQUENCE_INVALID = 0L diff --git a/app/src/main/java/io/github/miuzarte/scrcpyforandroid/scrcpy/ServerParams.kt b/app/src/main/java/io/github/miuzarte/scrcpyforandroid/scrcpy/ServerParams.kt index 93154d1a..46f94e43 100644 --- a/app/src/main/java/io/github/miuzarte/scrcpyforandroid/scrcpy/ServerParams.kt +++ b/app/src/main/java/io/github/miuzarte/scrcpyforandroid/scrcpy/ServerParams.kt @@ -86,6 +86,7 @@ data class ServerParams( val vdSystemDecorations: Boolean, val keepActive: Boolean, val flexDisplay: Boolean, + val ignoreVideoEncoderConstraints: Boolean, val list: ListOptions, ) { @@ -268,6 +269,9 @@ data class ServerParams( if (flexDisplay) { cmd.add("flex_display=true") } + if (ignoreVideoEncoderConstraints) { + cmd.add("ignore_video_encoder_constraints=true") + } if (displayImePolicy != DisplayImePolicy.UNDEFINED) { cmd.add("display_ime_policy=${displayImePolicy.string}") } diff --git a/app/src/main/java/io/github/miuzarte/scrcpyforandroid/scrcpy/Shared.kt b/app/src/main/java/io/github/miuzarte/scrcpyforandroid/scrcpy/Shared.kt index 72d6190c..063310e5 100644 --- a/app/src/main/java/io/github/miuzarte/scrcpyforandroid/scrcpy/Shared.kt +++ b/app/src/main/java/io/github/miuzarte/scrcpyforandroid/scrcpy/Shared.kt @@ -89,6 +89,8 @@ class Shared { H264("h264", "H.264", Type.VIDEO, 0x68323634, false), // default, ignore when passing H265("h265", "H.265", Type.VIDEO, 0x68323635, false), AV1("av1", "AV1", Type.VIDEO, 0x00617631, false), + VP8("vp8", "VP8", Type.VIDEO, 0x00767038, false), + VP9("vp9", "VP9", Type.VIDEO, 0x00767039, false), OPUS("opus", "Opus", Type.AUDIO, 0x6f707573, false), // default, ignore when passing AAC("aac", "AAC", Type.AUDIO, 0x00616163, false), diff --git a/app/src/main/java/io/github/miuzarte/scrcpyforandroid/storage/ScrcpyOptions.kt b/app/src/main/java/io/github/miuzarte/scrcpyforandroid/storage/ScrcpyOptions.kt index 8c8d8d18..316f0e5b 100644 --- a/app/src/main/java/io/github/miuzarte/scrcpyforandroid/storage/ScrcpyOptions.kt +++ b/app/src/main/java/io/github/miuzarte/scrcpyforandroid/storage/ScrcpyOptions.kt @@ -283,6 +283,10 @@ class ScrcpyOptions(context: Context): Settings(context, "ScrcpyOptions") { booleanPreferencesKey("flex_display"), false, ) + val IGNORE_VIDEO_ENCODER_CONSTRAINTS = Pair( + booleanPreferencesKey("ignore_video_encoder_constraints"), + false, + ) fun defaultBundle() = Bundle( crop = CROP.defaultValue, @@ -351,6 +355,7 @@ class ScrcpyOptions(context: Context): Settings(context, "ScrcpyOptions") { cameraTorch = CAMERA_TORCH.defaultValue, keepActive = KEEP_ACTIVE.defaultValue, flexDisplay = FLEX_DISPLAY.defaultValue, + ignoreVideoEncoderConstraints = IGNORE_VIDEO_ENCODER_CONSTRAINTS.defaultValue, ) } @@ -422,6 +427,7 @@ class ScrcpyOptions(context: Context): Settings(context, "ScrcpyOptions") { val cameraTorch: Boolean, val keepActive: Boolean, val flexDisplay: Boolean, + val ignoreVideoEncoderConstraints: Boolean, ): Parcelable { } @@ -491,6 +497,7 @@ class ScrcpyOptions(context: Context): Settings(context, "ScrcpyOptions") { bundleField(CAMERA_TORCH) { it.cameraTorch }, bundleField(KEEP_ACTIVE) { it.keepActive }, bundleField(FLEX_DISPLAY) { it.flexDisplay }, + bundleField(IGNORE_VIDEO_ENCODER_CONSTRAINTS) { it.ignoreVideoEncoderConstraints }, ) val bundleState: StateFlow = createBundleState(::bundleFromPreferences) @@ -562,6 +569,7 @@ class ScrcpyOptions(context: Context): Settings(context, "ScrcpyOptions") { cameraTorch = preferences.read(CAMERA_TORCH), keepActive = preferences.read(KEEP_ACTIVE), flexDisplay = preferences.read(FLEX_DISPLAY), + ignoreVideoEncoderConstraints = preferences.read(IGNORE_VIDEO_ENCODER_CONSTRAINTS), ) suspend fun loadBundle() = loadBundle(::bundleFromPreferences) @@ -645,6 +653,7 @@ class ScrcpyOptions(context: Context): Settings(context, "ScrcpyOptions") { cameraTorch = bundle.cameraTorch, keepActive = bundle.keepActive, flexDisplay = bundle.flexDisplay, + ignoreVideoEncoderConstraints = bundle.ignoreVideoEncoderConstraints, ) } @@ -716,6 +725,7 @@ internal fun encodeBundleToJson(bundle: ScrcpyOptions.Bundle): JSONObject = .put("cameraTorch", bundle.cameraTorch) .put("keepActive", bundle.keepActive) .put("flexDisplay", bundle.flexDisplay) + .put("ignoreVideoEncoderConstraints", bundle.ignoreVideoEncoderConstraints) internal fun decodeBundleFromJson(bundleJson: JSONObject?): ScrcpyOptions.Bundle { val json = bundleJson ?: return ScrcpyOptions.defaultBundle() @@ -984,6 +994,10 @@ internal fun decodeBundleFromJson(bundleJson: JSONObject?): ScrcpyOptions.Bundle "flexDisplay", ScrcpyOptions.FLEX_DISPLAY.defaultValue, ), + ignoreVideoEncoderConstraints = json.optBooleanOrDefault( + "ignoreVideoEncoderConstraints", + ScrcpyOptions.IGNORE_VIDEO_ENCODER_CONSTRAINTS.defaultValue, + ), ) } diff --git a/app/src/main/res/values-zh/strings.xml b/app/src/main/res/values-zh/strings.xml index 3287fc42..2221031a 100644 --- a/app/src/main/res/values-zh/strings.xml +++ b/app/src/main/res/values-zh/strings.xml @@ -536,6 +536,7 @@ 禁用结束后清理 默认情况下,scrcpy 会从设备中移除服务器二进制文件,并在退出时恢复设备状态(显示触摸、保持唤醒和电源模式)\n此选项将禁用此清理操作 自动调整尺寸以匹配界面 + 忽略视频编码器约束 scrcpy 启动后打开应用 本机 日志等级 diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index be742654..c55d524d 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -537,6 +537,7 @@ Disable cleanup on exit By default, scrcpy removes the server binary from the device and restores device state on exit (show touches, stay awake, and power mode).\nThis option disables this cleanup operation Automatically resize to match the screen + Ignore video encoder constraints Open app after scrcpy starts Native Log level From 692c4d5b53bc7b1304e458ff6195b3afa017c6d0 Mon Sep 17 00:00:00 2001 From: Miuzarte <982809597@qq.com> Date: Mon, 13 Jul 2026 13:38:35 +0800 Subject: [PATCH 2/3] merge mime mapping to class Codec --- .../scrcpyforandroid/NativeCoreFacade.kt | 11 +--------- .../nativecore/VideoDecoderController.kt | 9 +------- .../scrcpyforandroid/scrcpy/Shared.kt | 21 ++++++++++--------- 3 files changed, 13 insertions(+), 28 deletions(-) diff --git a/app/src/main/java/io/github/miuzarte/scrcpyforandroid/NativeCoreFacade.kt b/app/src/main/java/io/github/miuzarte/scrcpyforandroid/NativeCoreFacade.kt index abd5657e..e901ef7d 100644 --- a/app/src/main/java/io/github/miuzarte/scrcpyforandroid/NativeCoreFacade.kt +++ b/app/src/main/java/io/github/miuzarte/scrcpyforandroid/NativeCoreFacade.kt @@ -210,7 +210,7 @@ object NativeCoreFacade { val info = scrcpy.currentSessionState.value ?: return@withLock if (info.width <= 0 || info.height <= 0) return@withLock - val mime = mimeForCodec(info.codec) + val mime = info.codec?.mime ?: "video/avc" if (!DecoderCapabilities.isSizeSupported(mime, info.width, info.height)) { Log.w( TAG, @@ -323,14 +323,5 @@ object NativeCoreFacade { runCatching { scrcpy.start(options) } } - private fun mimeForCodec(codec: Codec?): String = when (codec) { - Codec.H264 -> "video/avc" - Codec.H265 -> "video/hevc" - Codec.AV1 -> "video/av01" - Codec.VP8 -> "video/x-vnd.on2.vp8" - Codec.VP9 -> "video/x-vnd.on2.vp9" - else -> "video/avc" - } - private const val TAG = "NativeCoreFacade" } diff --git a/app/src/main/java/io/github/miuzarte/scrcpyforandroid/nativecore/VideoDecoderController.kt b/app/src/main/java/io/github/miuzarte/scrcpyforandroid/nativecore/VideoDecoderController.kt index b5612a93..460f7049 100644 --- a/app/src/main/java/io/github/miuzarte/scrcpyforandroid/nativecore/VideoDecoderController.kt +++ b/app/src/main/java/io/github/miuzarte/scrcpyforandroid/nativecore/VideoDecoderController.kt @@ -255,14 +255,7 @@ internal class VideoDecoderController( } val surface = renderer.getDecoderSurface() - val mime = when (session.codec) { - Codec.H264 -> "video/avc" - Codec.H265 -> "video/hevc" - Codec.AV1 -> "video/av01" - Codec.VP8 -> "video/x-vnd.on2.vp8" - Codec.VP9 -> "video/x-vnd.on2.vp9" - else -> "video/avc" - } + val mime = session.codec?.mime ?: "video/avc" Log.i( TAG, "createOrReplaceDecoder(): codec=${session.codec?.string ?: "null"}, " + diff --git a/app/src/main/java/io/github/miuzarte/scrcpyforandroid/scrcpy/Shared.kt b/app/src/main/java/io/github/miuzarte/scrcpyforandroid/scrcpy/Shared.kt index 063310e5..45d54a29 100644 --- a/app/src/main/java/io/github/miuzarte/scrcpyforandroid/scrcpy/Shared.kt +++ b/app/src/main/java/io/github/miuzarte/scrcpyforandroid/scrcpy/Shared.kt @@ -82,20 +82,21 @@ class Shared { enum class Codec( val string: String, val displayName: String, + val mime: String, val type: Type, val id: Int, val isLossless: Boolean, ) { - H264("h264", "H.264", Type.VIDEO, 0x68323634, false), // default, ignore when passing - H265("h265", "H.265", Type.VIDEO, 0x68323635, false), - AV1("av1", "AV1", Type.VIDEO, 0x00617631, false), - VP8("vp8", "VP8", Type.VIDEO, 0x00767038, false), - VP9("vp9", "VP9", Type.VIDEO, 0x00767039, false), - - OPUS("opus", "Opus", Type.AUDIO, 0x6f707573, false), // default, ignore when passing - AAC("aac", "AAC", Type.AUDIO, 0x00616163, false), - FLAC("flac", "FLAC", Type.AUDIO, 0x666c6163, true), - RAW("raw", "RAW", Type.AUDIO, 0x00726177, true); // wav raw + H264("h264", "H.264", "video/avc", Type.VIDEO, 0x68323634, false), // default, ignore when passing + H265("h265", "H.265", "video/hevc", Type.VIDEO, 0x68323635, false), + AV1("av1", "AV1", "video/av01", Type.VIDEO, 0x00617631, false), + VP8("vp8", "VP8", "video/x-vnd.on2.vp8", Type.VIDEO, 0x00767038, false), + VP9("vp9", "VP9", "video/x-vnd.on2.vp9", Type.VIDEO, 0x00767039, false), + + OPUS("opus", "Opus", "audio/opus", Type.AUDIO, 0x6f707573, false), // default, ignore when passing + AAC("aac", "AAC", "audio/mp4a-latm", Type.AUDIO, 0x00616163, false), + FLAC("flac", "FLAC", "audio/flac", Type.AUDIO, 0x666c6163, true), + RAW("raw", "RAW", "audio/raw", Type.AUDIO, 0x00726177, true); // wav raw enum class Type { VIDEO, AUDIO } From fe8b5d65bf5538572d158baebe7b87908e746ee6 Mon Sep 17 00:00:00 2001 From: Miuzarte <982809597@qq.com> Date: Mon, 13 Jul 2026 13:51:28 +0800 Subject: [PATCH 3/3] fix CONTROL_MSG_SCAN_FILE_PATH_MAX_LENGTH --- .../java/io/github/miuzarte/scrcpyforandroid/scrcpy/Scrcpy.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/io/github/miuzarte/scrcpyforandroid/scrcpy/Scrcpy.kt b/app/src/main/java/io/github/miuzarte/scrcpyforandroid/scrcpy/Scrcpy.kt index aca9c143..a965544c 100644 --- a/app/src/main/java/io/github/miuzarte/scrcpyforandroid/scrcpy/Scrcpy.kt +++ b/app/src/main/java/io/github/miuzarte/scrcpyforandroid/scrcpy/Scrcpy.kt @@ -1786,7 +1786,7 @@ class Scrcpy( @Synchronized fun scanFile(path: String) { val bytes = path.toByteArray(Charsets.UTF_8) - require(bytes.size <= 255) { "scan file path is too long (max 255 bytes)" } + require(bytes.size <= 256) { "scan file path is too long (max 256 bytes)" } output.writeByte(TYPE_SCAN_FILE) output.writeInt(bytes.size) output.write(bytes)