#include "protocol.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include extern char **environ; static bool write_exact(int fd, const void *data, size_t size) { const uint8_t *cursor = data; while (size) { ssize_t count = write(fd, cursor, size); if (count < 0 && errno == EINTR) continue; if (count <= 0) return false; cursor += (size_t)count; size -= (size_t)count; } return true; } static bool read_frame(int fd, uint8_t *pixels, size_t size, int timeout_ms) { size_t used = 0; while (used < size) { struct pollfd pollfd = {.fd = fd, .events = POLLIN}; int ready = poll(&pollfd, 1, timeout_ms); if (ready <= 0) return false; ssize_t count = read(fd, pixels + used, size - used); if (count < 0 && errno == EINTR) continue; if (count <= 0) return false; used += (size_t)count; } return true; } int main(int argc, char **argv) { if (argc < 2 || argc > 4) { fprintf(stderr, "usage: %s /path/to/obs-webkit-renderer [opaque | zoom | opaque-url URL]\n", argv[0]); return 2; } bool external_url = argc == 4 && strcmp(argv[2], "opaque-url") == 0; bool zoom = argc == 3 && strcmp(argv[2], "zoom") == 0; bool opaque = external_url || zoom || (argc == 3 && strcmp(argv[2], "opaque") == 0); int control[2]; int frames[2]; if (socketpair(AF_UNIX, SOCK_STREAM, 0, control) || pipe(frames)) { perror("IPC"); return 2; } const uint32_t width = external_url ? 1890 : 64; const uint32_t height = external_url ? 660 : 64; size_t shared_size = owb_shared_size(width, height); int shared_fd = memfd_create("renderer-smoke-frames", MFD_CLOEXEC); if (shared_fd < 0 || ftruncate(shared_fd, (off_t)shared_size) != 0) return 2; struct owb_shared_frames *shared = mmap(NULL, shared_size, PROT_READ | PROT_WRITE, MAP_SHARED, shared_fd, 0); if (shared == MAP_FAILED) return 2; memset(shared, 0, sizeof(*shared)); shared->magic = OWB_SHARED_MAGIC; shared->version = OWB_PROTOCOL_VERSION; shared->width = width; shared->height = height; shared->stride = width * 4; shared->slot_count = OWB_SHARED_FRAME_SLOTS; posix_spawn_file_actions_t actions; posix_spawn_file_actions_init(&actions); posix_spawn_file_actions_adddup2(&actions, control[1], STDIN_FILENO); posix_spawn_file_actions_adddup2(&actions, frames[1], STDOUT_FILENO); posix_spawn_file_actions_adddup2(&actions, shared_fd, OWB_SHARED_FRAME_FD); char *child_argv[] = {argv[1], NULL}; pid_t child = 0; int error = posix_spawn(&child, argv[1], &actions, NULL, child_argv, environ); posix_spawn_file_actions_destroy(&actions); close(control[1]); close(frames[1]); if (error) { fprintf(stderr, "posix_spawn: %s\n", strerror(error)); return 2; } const char internal_url[] = "data:text/html,
"; const char transparent_css[] = "html,body{margin:0}" "#box{position:fixed;left:16px;top:16px;width:16px;height:16px;" "background:rgb(241,23,17)}" "#box:after{content:'';position:fixed;left:32px;top:32px;width:16px;" "height:16px;background:rgba(255,0,0,.5)}"; const char opaque_css[] = "html,body{margin:0;background:rgb(7,19,31)!important}" "#box{position:fixed;left:16px;top:16px;width:32px;height:32px;" "background:rgb(241,23,17);transform:translateZ(0);filter:blur(.1px)}"; const char zoom_css[] = "html,body{margin:0;background:rgb(7,19,31)!important}" "#box{position:fixed;left:0;top:0;width:10px;height:10px;" "background:rgb(241,23,17)}"; const char *url = external_url ? argv[3] : internal_url; const char *css = external_url ? "" : zoom ? zoom_css : opaque ? opaque_css : transparent_css; const char *audio_sink = getenv("OWB_TEST_AUDIO_SINK"); struct owb_config_header config = { .magic = OWB_CONFIG_MAGIC, .version = OWB_PROTOCOL_VERSION, .width = width, .height = height, .fps = 10, .zoom_percent = zoom ? 200 : 100, .capture_audio = audio_sink && *audio_sink, .transparent = opaque ? 0 : 1, /* GPU rendering must still produce an invisible captured frame. */ .hardware_acceleration = 1, .url_length = (uint32_t)strlen(url), .css_length = (uint32_t)strlen(css), }; if (audio_sink) snprintf(config.audio_sink, sizeof(config.audio_sink), "%s", audio_sink); bool sent = write_exact(control[0], &config, sizeof(config)) && write_exact(control[0], url, strlen(url)) && write_exact(control[0], css, strlen(css)); uint8_t last_bgra[4] = {0}; uint8_t last_corner_bgra[4] = {0}; uint8_t last_half_bgra[4] = {0}; bool found_red = false; int max_attempts = external_url ? 120 : 60; for (int attempt = 0; sent && attempt < max_attempts; attempt++) { uint8_t slot; if (!read_frame(frames[0], &slot, sizeof(slot), 1000)) continue; if (slot >= OWB_SHARED_FRAME_SLOTS) continue; uint8_t *frame = owb_shared_slot(shared, slot); if (external_url) { bool nonuniform = false; uint32_t first; memcpy(&first, frame, sizeof(first)); for (uint32_t y = 0; y < height && !nonuniform; y += 8) { for (uint32_t x = 0; x < width; x += 8) { uint32_t sample; memcpy(&sample, frame + ((size_t)y * width + x) * 4, sizeof(sample)); if (sample != first) { nonuniform = true; break; } } } found_red = nonuniform; if ((attempt + 1) % 10 == 0) fprintf(stderr, "external page at %.1fs: %s\n", (double)(attempt + 1) / 10.0, nonuniform ? "content" : "uniform"); continue; } size_t center = zoom ? ((size_t)5 * width + 15) * 4 : opaque ? ((size_t)(height / 2) * width + width / 2) * 4 : ((size_t)24 * width + 24) * 4; memcpy(last_bgra, frame + center, sizeof(last_bgra)); size_t corner = zoom ? ((size_t)5 * width + 25) * 4 : 0; memcpy(last_corner_bgra, frame + corner, sizeof(last_corner_bgra)); if (!zoom && !opaque) { size_t half = ((size_t)40 * width + 40) * 4; memcpy(last_half_bgra, frame + half, sizeof(last_half_bgra)); } bool expected = zoom ? last_bgra[0] < 32 && last_bgra[1] < 40 && last_bgra[2] > 210 && last_bgra[3] == 255 && last_corner_bgra[0] > 20 && last_corner_bgra[1] < 32 && last_corner_bgra[2] < 32 && last_corner_bgra[3] == 255 : opaque ? last_bgra[0] < 32 && last_bgra[1] < 40 && last_bgra[2] > 210 && last_bgra[3] == 255 : last_bgra[0] < 32 && last_bgra[1] < 40 && last_bgra[2] > 225 && last_bgra[3] == 255 && last_half_bgra[0] < 8 && last_half_bgra[1] < 8 && last_half_bgra[2] > 248 && last_half_bgra[3] > 120 && last_half_bgra[3] < 136 && last_corner_bgra[0] == 0 && last_corner_bgra[1] == 0 && last_corner_bgra[2] == 0 && last_corner_bgra[3] == 0; if (expected) { found_red = true; break; } } shutdown(control[0], SHUT_RDWR); close(control[0]); close(frames[0]); munmap(shared, shared_size); close(shared_fd); kill(child, SIGTERM); int child_status = 0; while (waitpid(child, &child_status, 0) < 0 && errno == EINTR) { } if (WIFEXITED(child_status) && WEXITSTATUS(child_status) == 5) return 77; if (!found_red) { if (external_url) { fprintf(stderr, "renderer kept the external page uniformly colored: %s\n", url); return 1; } fprintf(stderr, "renderer did not produce the expected %s red BGRA frame " "(center BGRA=%u,%u,%u,%u; half BGRA=%u,%u,%u,%u; " "corner BGRA=%u,%u,%u,%u)\n", zoom ? "200%-zoomed" : opaque ? "opaque" : "transparent", last_bgra[0], last_bgra[1], last_bgra[2], last_bgra[3], last_half_bgra[0], last_half_bgra[1], last_half_bgra[2], last_half_bgra[3], last_corner_bgra[0], last_corner_bgra[1], last_corner_bgra[2], last_corner_bgra[3]); return 1; } return 0; }