Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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` 里有帧号和时间戳:调用方可以据此跳过重复帧,也能分清「画面没变」和「模拟器卡住」。
Expand Down
12 changes: 7 additions & 5 deletions shim/mumu_shim.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down
4 changes: 2 additions & 2 deletions src/core.c
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down Expand Up @@ -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);
Expand Down
7 changes: 7 additions & 0 deletions src/mx_internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -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__)
Expand Down
7 changes: 7 additions & 0 deletions tests/test_core.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
11 changes: 11 additions & 0 deletions tests/test_shim.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
Loading