From 7849fd993efcdd2fbab533d297dfd5d56bd8494d Mon Sep 17 00:00:00 2001 From: qiyinxi <52456734+qiyinxi@users.noreply.github.com> Date: Tue, 10 Nov 2026 14:38:51 +0100 Subject: [PATCH] =?UTF-8?q?MuMu=20=E5=A4=96=E5=A3=B3=E5=9C=A8=E7=AC=AC?= =?UTF-8?q?=E4=B8=80=E5=B8=A7=E5=8F=91=E5=B8=83=E5=89=8D=E7=85=A7=E6=97=A7?= =?UTF-8?q?=E4=BA=A4=E5=87=BA=E5=B0=BA=E5=AF=B8=E5=92=8C=E9=BB=91=E5=9B=BE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit #8 让截图在第一帧发布前返回未就绪,外壳也跟着给 MaaCore 返回 -2。 MaaCore 连上时马上要尺寸,失败就放弃这条路;截图失败会重新测速。 外壳改用内部标志照旧拷贝,和它替换掉的 DLL 一样;公开接口不受影响。 README 补上 Linux、macOS 打开时先经控制台开流、第一帧前要重试。 --- README.md | 4 +++- shim/mumu_shim.c | 12 +++++++----- src/core.c | 4 ++-- src/mx_internal.h | 7 +++++++ tests/test_core.c | 7 +++++++ tests/test_shim.c | 11 +++++++++++ 6 files changed, 37 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 04fdbe8..563512c 100644 --- a/README.md +++ b/README.md @@ -46,13 +46,15 @@ if (modavd_open(&p, &h) == MODAVD_OK) { modavd_capture(h, 0, MODAVD_FMT_BGR, 0, NULL, 0, &info); /* 先问尺寸 */ void* buf = malloc((size_t)info.stride * info.height); int rc = modavd_capture(h, 0, MODAVD_FMT_BGR, 0, buf, (size_t)info.stride * info.height, &info); - /* rc: MODAVD_OK;MODAVD_E_NOT_READY 还没有画面;MODAVD_E_RESIZED 要重新 open */ + /* rc: MODAVD_OK;MODAVD_E_NOT_READY 还没有画面(刚开流、第一帧没到时也是这样,稍后再调); + * MODAVD_E_RESIZED 要重新 open */ free(buf); modavd_close(h); } ``` - 所有函数返回 `MODAVD_OK`(0)或负的 `MODAVD_E_*`。 +- Linux、macOS 上 `modavd_open` 默认先经控制台开流(`screenrecord webrtc start`),所以控制台要连得上、鉴权要能过;模拟器退出后旧的共享内存可能还在,不开流会拿到上次的旧画面。确定映射属于当前实例而且已经在开流的,可以带 `MODAVD_OPEN_NO_START` 跳过控制台。 - 结构体开头是 `size`,以后加字段不破坏旧的调用方。 - `modavd_api_version()` 每加一个函数就加一,函数从不删。`modavd_capabilities()` 说明这个实例能做哪些事。 - `modavd_frame_info` 里有帧号和时间戳:调用方可以据此跳过重复帧,也能分清「画面没变」和「模拟器卡住」。 diff --git a/shim/mumu_shim.c b/shim/mumu_shim.c index 419e9a8..30d0cbf 100644 --- a/shim/mumu_shim.c +++ b/shim/mumu_shim.c @@ -203,11 +203,13 @@ EXPORT int nemu_capture_display(int handle, unsigned int display_id, int buffer_ memset(&info, 0, sizeof(info)); info.size = sizeof(info); size_t size = (buffer_size > 0 && pixels) ? (size_t)buffer_size : 0; - /* mx_copy_frame, not modavd_capture: when a new frame arrived during every - * copy, hand the last copy over as the DLL this replaces did, instead of - * failing (MaaCore would drop to its screencap speed test again). */ - int rc = mx_copy_frame(&h->shm, MODAVD_FMT_RGBA, MODAVD_CAPTURE_BOTTOM_UP, h->simd, size ? pixels : NULL, size, - &info); + /* mx_copy_frame, not modavd_capture: as the DLL this replaces did, report + * the size and a black frame before the first frame is published, and hand + * the last copy over when a new frame arrived during every copy, instead + * of failing (MaaCore would give up on this path at connect, or drop to its + * screencap speed test again). */ + int rc = mx_copy_frame(&h->shm, MODAVD_FMT_RGBA, MODAVD_CAPTURE_BOTTOM_UP | MX_CAPTURE_UNPUBLISHED, h->simd, + size ? pixels : NULL, size, &info); if (rc == MX_TORN) { rc = MODAVD_OK; } diff --git a/src/core.c b/src/core.c index 5fd297c..84c9d5e 100644 --- a/src/core.c +++ b/src/core.c @@ -311,7 +311,7 @@ int mx_copy_frame(const mx_shm* shm, uint32_t format, uint32_t flags, int simd, info->timestamp_us = vi->ts_us; /* Dimensions are set before the first frame is published. The counter * can wrap to zero, and a published static frame need not advance. */ - if (w == 0 || h == 0 || info->timestamp_us == 0) { + if (w == 0 || h == 0 || (info->timestamp_us == 0 && !(flags & MX_CAPTURE_UNPUBLISHED))) { return MODAVD_E_NOT_READY; } if (MX_HEADER_SIZE + (uint64_t)w * h * 4 > shm->size) { @@ -356,7 +356,7 @@ int modavd_capture(modavd_handle h, int display, uint32_t format, uint32_t flags if (display != 0) { return MODAVD_E_UNSUPPORTED; } - int rc = mx_copy_frame(&h->shm, format, flags, h->simd, buf, buf_size, info); + int rc = mx_copy_frame(&h->shm, format, flags & ~MX_CAPTURE_UNPUBLISHED, h->simd, buf, buf_size, info); if (rc == MX_TORN) { mx_log(MODAVD_LOG_WARN, "port=%d: a new frame arrived during each of %d copies", h->console_port, MX_MAX_COPY_ATTEMPTS); diff --git a/src/mx_internal.h b/src/mx_internal.h index 8e48383..60618f2 100644 --- a/src/mx_internal.h +++ b/src/mx_internal.h @@ -38,6 +38,13 @@ typedef struct mx_video_info { * DLL it replaces did. */ #define MX_TORN 1 +/* mx_copy_frame() flag, internal only (modavd_capture() clears it): copy the + * frame even if the emulator has not published one yet (timestamp 0). The + * region then holds the size and a black frame. The MuMu-shaped shim uses + * it, as the DLL it replaces did: a failed capture makes MaaCore give up on + * this path or rerun its screencap speed test. */ +#define MX_CAPTURE_UNPUBLISHED (1u << 31) + /* ---- log.c ---- */ void mx_log(int level, const char* fmt, ...) #if defined(__GNUC__) diff --git a/tests/test_core.c b/tests/test_core.c index dbe5075..4149bc9 100644 --- a/tests/test_core.c +++ b/tests/test_core.c @@ -232,6 +232,13 @@ static void test_capture_before_first_frame(void) CHECK_EQ(buf[i], 0xA5); } } + /* The shim's internal flag is not part of the public API. */ + CHECK_EQ(modavd_capture(h, 0, MODAVD_FMT_BGRA, MX_CAPTURE_UNPUBLISHED, buf, sizeof(buf), &info), + MODAVD_E_NOT_READY); + /* The shim's own call copies the unpublished frame. */ + CHECK_EQ(mx_copy_frame(&h->shm, MODAVD_FMT_BGRA, MX_CAPTURE_UNPUBLISHED, 0, buf, sizeof(buf), &info), + MODAVD_OK); + CHECK_EQ(info.timestamp_us, 0); /* A counter may wrap to zero. A published, unchanged black frame is valid. */ vi->ts_us = 123456789ull; diff --git a/tests/test_shim.c b/tests/test_shim.c index f66e6ae..2d325fb 100644 --- a/tests/test_shim.c +++ b/tests/test_shim.c @@ -96,6 +96,17 @@ int main(int argc, char** argv) CHECK(nemu_capture_display(handle, 0, (int)size, &cw, &ch, buf) == -2); fake_frame_set_size(&f, w, h); + /* Started but not published yet: the size and the frame as they are, like + * the DLL this replaces; MaaCore's connect asks for the size right away. */ + mx_video_info* vi = (mx_video_info*)f.view; + uint64_t ts = vi->ts_us; + vi->ts_us = 0; + cw = ch = 0; + CHECK(nemu_capture_display(handle, 0, 0, &cw, &ch, NULL) == 0); + CHECK(cw == (int)w && ch == (int)h); + CHECK(nemu_capture_display(handle, 0, (int)size, &cw, &ch, buf) == 0); + vi->ts_us = ts; + nemu_disconnect(handle); CHECK(nemu_capture_display(handle, 0, 0, &cw, &ch, NULL) == -1); free(buf);