| 1403 | } |
| 1404 | |
| 1405 | bool RuntimeManager::ReadRemoteFrame(const std::string& vm_id, |
| 1406 | RemoteVideoFrame* frame, |
| 1407 | bool need_full_frame, |
| 1408 | std::chrono::milliseconds wait_timeout) { |
| 1409 | auto session = FindSession(vm_id); |
| 1410 | if (!session || !frame) return false; |
| 1411 | std::unique_lock<std::mutex> lock(session->frame_mutex); |
| 1412 | |
| 1413 | // Block the consumer until either the producer queues new slices or the |
| 1414 | // timeout expires. need_full_frame short-circuits the wait because we can |
| 1415 | // synthesize a full-frame slice from the shared framebuffer regardless of |
| 1416 | // whether new partials have arrived. |
| 1417 | if (wait_timeout.count() > 0 && |
| 1418 | !need_full_frame && |
| 1419 | session->remote_frame.slices.empty() && |
| 1420 | session->running.load()) { |
| 1421 | session->remote_frame_cv.wait_for(lock, wait_timeout, [&] { |
| 1422 | return !session->remote_frame.slices.empty() || |
| 1423 | session->remote_frame_force_full || |
| 1424 | !session->running.load(); |
| 1425 | }); |
| 1426 | } |
| 1427 | |
| 1428 | if ((need_full_frame || session->remote_frame_force_full) && |
| 1429 | session->framebuffer && |
| 1430 | session->framebuffer->IsValid() && |
| 1431 | session->framebuffer->width() > 0 && |
| 1432 | session->framebuffer->height() > 0) { |
| 1433 | const PixelFormat target_format = session->remote_video_format; |
| 1434 | RemoteVideoSlice full = MakeSliceFromBgra( |
| 1435 | session->framebuffer->data(), |
| 1436 | static_cast<int>(session->framebuffer->stride()), |
| 1437 | 0, 0, |
| 1438 | session->framebuffer->width(), |
| 1439 | session->framebuffer->height(), |
| 1440 | target_format); |
| 1441 | if (!full.data.empty()) { |
| 1442 | session->remote_frame.slices.clear(); |
| 1443 | session->remote_frame.slices.push_back(std::move(full)); |
| 1444 | session->remote_frame.width = session->framebuffer->width(); |
| 1445 | session->remote_frame.height = session->framebuffer->height(); |
| 1446 | session->remote_frame.format = target_format; |
| 1447 | session->remote_frame_force_full = false; |
| 1448 | } |
| 1449 | } |
| 1450 | |
| 1451 | if (session->remote_frame.slices.empty()) return false; |
| 1452 | |
| 1453 | frame->width = session->remote_frame.width; |
| 1454 | frame->height = session->remote_frame.height; |
| 1455 | frame->format = session->remote_frame.format; |
| 1456 | frame->seq = session->remote_frame.seq; |
| 1457 | frame->slices = std::move(session->remote_frame.slices); |
| 1458 | session->remote_frame.slices.clear(); |
| 1459 | return true; |
| 1460 | } |
| 1461 | |
| 1462 | bool RuntimeManager::AttachConsole(const std::string& vm_id, ipc::UnixSocketConnection* client, std::string* error) { |
no test coverage detected