diff --git a/client/include/interface/transport.h b/client/include/interface/transport.h index 38165aa6..0f691e75 100644 --- a/client/include/interface/transport.h +++ b/client/include/interface/transport.h @@ -236,31 +236,9 @@ LG_TransportControl; typedef uint64_t LG_TransportControlToken; -typedef struct LG_TransportOps +typedef struct LG_FrameOps { - const char * name; - void (*setup)(void); - bool (*create)(LG_Transport ** transport); - void (*destroy)(LG_Transport ** transport); - - LG_TransportStatus (*connect)(LG_Transport * transport, - LG_TransportSession * session); - void (*disconnect)(LG_Transport * transport); - bool (*sessionValid)(LG_Transport * transport); bool (*supportsDMA)(LG_Transport * transport); - /* Queried after connect. The returned operations and opaque value remain - * valid until disconnect; NULL indicates that this session has no input. */ - const LG_InputOps *(*getInputOps)(LG_Transport * transport, - void ** opaque); - /* Queried after connect. The returned operations and opaque value remain - * valid until disconnect; NULL indicates that this session has no audio. */ - const LG_AudioOps *(*getAudioOps)(LG_Transport * transport, - void ** opaque); - /* Queried after connect. The returned operations and opaque value remain - * valid until disconnect; NULL indicates that this session has no - * clipboard. */ - const LG_ClipboardOps *(*getClipboardOps)(LG_Transport * transport, - void ** opaque); bool (*attachRenderer)(LG_Transport * transport, const LG_RendererInterop * interop); void (*detachRenderer)(LG_Transport * transport); @@ -274,16 +252,62 @@ typedef struct LG_TransportOps const LG_TransportFrame * frame, LG_TransportFrameTiming * timing); void (*releaseFrame)(LG_Transport * transport, LG_TransportFrame * frame); /* Called by the frame consumer as it exits. A backend may release transient - * stream resources; nextFrame must reacquire them when the consumer restarts. */ + * stream resources; nextFrame must reacquire them when the consumer + * restarts. */ void (*stopFrame)(LG_Transport * transport); LG_TransportStatus (*nextPointer)(LG_Transport * transport, LG_TransportPointer * pointer); void (*releasePointer)(LG_Transport * transport, LG_TransportPointer * pointer); - /* Called by the pointer consumer as it exits. A backend may release transient - * stream resources; nextPointer must reacquire them when the consumer restarts. */ + /* Called by the pointer consumer as it exits. A backend may release + * transient stream resources; nextPointer must reacquire them when the + * consumer restarts. */ void (*stopPointer)(LG_Transport * transport); +} +LG_FrameOps; + +typedef enum LG_VideoType +{ + LG_VIDEO_TYPE_FRAME, +} +LG_VideoType; + +typedef struct LG_VideoOps +{ + const char * name; + LG_VideoType type; + const LG_FrameOps * frame; +} +LG_VideoOps; + +typedef struct LG_TransportOps +{ + const char * name; + void (*setup)(void); + bool (*create)(LG_Transport ** transport); + void (*destroy)(LG_Transport ** transport); + + LG_TransportStatus (*connect)(LG_Transport * transport, + LG_TransportSession * session); + void (*disconnect)(LG_Transport * transport); + bool (*sessionValid)(LG_Transport * transport); + /* Queried after create. The returned operations remain valid until the + * transport is destroyed. NULL indicates that this transport has no video. */ + const LG_VideoOps *(*getVideoOps)(LG_Transport * transport); + /* Queried after connect. The returned operations and opaque value remain + * valid until disconnect; NULL indicates that this session has no input. */ + const LG_InputOps *(*getInputOps)(LG_Transport * transport, + void ** opaque); + /* Queried after connect. The returned operations and opaque value remain + * valid until disconnect; NULL indicates that this session has no audio. */ + const LG_AudioOps *(*getAudioOps)(LG_Transport * transport, + void ** opaque); + /* Queried after connect. The returned operations and opaque value remain + * valid until disconnect; NULL indicates that this session has no + * clipboard. */ + const LG_ClipboardOps *(*getClipboardOps)(LG_Transport * transport, + void ** opaque); LG_TransportStatus (*sendControl)(LG_Transport * transport, const LG_TransportControl * control, LG_TransportControlToken * token); diff --git a/client/src/main.c b/client/src/main.c index 1dcbb95d..ce35570a 100644 --- a/client/src/main.c +++ b/client/src/main.c @@ -1044,8 +1044,8 @@ static int renderThread(void * unused) core_stopCursorThread(); core_stopFrameThread(); - if (g_state.transport.ops && g_state.transport.ops->detachRenderer) - g_state.transport.ops->detachRenderer(g_state.transport.handle); + if (g_state.videoOps && g_state.videoOps->frame->detachRenderer) + g_state.videoOps->frame->detachRenderer(g_state.transport.handle); RENDERER(deinitialize); g_state.lgr = NULL; @@ -1066,7 +1066,7 @@ int main_cursorThread(void * unused) while(app_getState() == APP_STATE_RUNNING && !g_state.stopVideo) { LG_TransportPointer pointer; - const LG_TransportStatus status = g_state.transport.ops->nextPointer( + const LG_TransportStatus status = g_state.videoOps->frame->nextPointer( g_state.transport.handle, &pointer); if (status != LG_TRANSPORT_OK) { @@ -1119,7 +1119,7 @@ int main_cursorThread(void * unused) case CURSOR_TYPE_MASKED_COLOR: cursorType = LG_CURSOR_MASKED_COLOR; break; default: DEBUG_ERROR("Invalid cursor type"); - g_state.transport.ops->releasePointer( + g_state.videoOps->frame->releasePointer( g_state.transport.handle, &pointer); continue; } @@ -1130,7 +1130,7 @@ int main_cursorThread(void * unused) pointer.pitch, pointer.shape)) { DEBUG_ERROR("Failed to update mouse shape"); - g_state.transport.ops->releasePointer( + g_state.videoOps->frame->releasePointer( g_state.transport.handle, &pointer); continue; } @@ -1192,11 +1192,12 @@ int main_cursorThread(void * unused) (g_params.mouseRedraw || contentChanged)))) cursorRepaintRequest(); - g_state.transport.ops->releasePointer(g_state.transport.handle, &pointer); + g_state.videoOps->frame->releasePointer( + g_state.transport.handle, &pointer); } - if (g_state.transport.ops->stopPointer) - g_state.transport.ops->stopPointer(g_state.transport.handle); + if (g_state.videoOps->frame->stopPointer) + g_state.videoOps->frame->stopPointer(g_state.transport.handle); return 0; } @@ -1213,15 +1214,15 @@ int main_frameThread(void * unused) lgWaitEvent(e_startup, TIMEOUT_INFINITE); if (app_getState() != APP_STATE_RUNNING) { - if (g_state.transport.ops->stopFrame) - g_state.transport.ops->stopFrame(g_state.transport.handle); + if (g_state.videoOps->frame->stopFrame) + g_state.videoOps->frame->stopFrame(g_state.transport.handle); return 0; } while(app_getState() == APP_STATE_RUNNING && !g_state.stopVideo) { LG_TransportFrame frame; - const LG_TransportStatus status = g_state.transport.ops->nextFrame( + const LG_TransportStatus status = g_state.videoOps->frame->nextFrame( g_state.transport.handle, g_state.useDMA, &frame); if (status != LG_TRANSPORT_OK) { @@ -1251,7 +1252,7 @@ int main_frameThread(void * unused) if (frame.serial == frameSerial && g_state.formatValid && !frame.scheduleOwner) { - g_state.transport.ops->releaseFrame(g_state.transport.handle, &frame); + g_state.videoOps->frame->releaseFrame(g_state.transport.handle, &frame); continue; } frameSerial = frame.serial; @@ -1261,7 +1262,7 @@ int main_frameThread(void * unused) if (!format) { DEBUG_ERROR("Transport returned a frame without format metadata"); - g_state.transport.ops->releaseFrame(g_state.transport.handle, &frame); + g_state.videoOps->frame->releaseFrame(g_state.transport.handle, &frame); app_setState(APP_STATE_SHUTDOWN); break; } @@ -1343,7 +1344,8 @@ int main_frameThread(void * unused) if (invalid) { DEBUG_ERROR("Unsupported frame type"); - g_state.transport.ops->releaseFrame(g_state.transport.handle, &frame); + g_state.videoOps->frame->releaseFrame( + g_state.transport.handle, &frame); app_setState(APP_STATE_SHUTDOWN); break; } @@ -1365,7 +1367,8 @@ int main_frameThread(void * unused) { LG_UNLOCK(g_state.lgrLock); DEBUG_ERROR("Renderer failed to configure format"); - g_state.transport.ops->releaseFrame(g_state.transport.handle, &frame); + g_state.videoOps->frame->releaseFrame( + g_state.transport.handle, &frame); app_setState(APP_STATE_SHUTDOWN); break; } @@ -1418,7 +1421,7 @@ int main_frameThread(void * unused) rendererOwnsFrame ? frame.releaseHandle : 0)) { frameTimingCancel(frameToken); - g_state.transport.ops->releaseFrame(g_state.transport.handle, &frame); + g_state.videoOps->frame->releaseFrame(g_state.transport.handle, &frame); DEBUG_ERROR("Renderer onFrame returned failure"); app_setState(APP_STATE_SHUTDOWN); break; @@ -1428,8 +1431,8 @@ int main_frameThread(void * unused) * is signalled below, so sample producer timing while this lease is still * unambiguously owned by the frame thread. */ LG_TransportFrameTiming timing = {}; - if (g_state.transport.ops->getFrameTiming) - g_state.transport.ops->getFrameTiming( + if (g_state.videoOps->frame->getFrameTiming) + g_state.videoOps->frame->getFrameTiming( g_state.transport.handle, &frame, &timing); const uint64_t queueStart = nanotime(); @@ -1474,7 +1477,7 @@ int main_frameThread(void * unused) frameTimingFinishFrame(frameToken, &timing); if (!rendererOwnsFrame) - g_state.transport.ops->releaseFrame(g_state.transport.handle, &frame); + g_state.videoOps->frame->releaseFrame(g_state.transport.handle, &frame); app_useSpiceDisplay(false); } @@ -1491,8 +1494,8 @@ int main_frameThread(void * unused) /* Renderer reset requests release for every asynchronous DMA snapshot. * Drain those requests before the transport unsubscribes or reconnects. */ - if (g_state.transport.ops->stopFrame) - g_state.transport.ops->stopFrame(g_state.transport.handle); + if (g_state.videoOps->frame->stopFrame) + g_state.videoOps->frame->stopFrame(g_state.transport.handle); return 0; } @@ -2126,6 +2129,16 @@ static int lg_run(void) } DEBUG_INFO("Using Transport: %s", g_state.transport.ops->name); + g_state.videoOps = + g_state.transport.ops->getVideoOps(g_state.transport.handle); + if (!g_state.videoOps || g_state.videoOps->type != LG_VIDEO_TYPE_FRAME || + !g_state.videoOps->frame) + { + DEBUG_ERROR("Transport does not provide a frame source"); + return -1; + } + DEBUG_INFO("Using Video: %s", g_state.videoOps->name); + // setup the spice startup condition if (!(e_spice = lgCreateEvent(false, 0))) { @@ -2207,7 +2220,8 @@ static int lg_run(void) return -1; } - g_state.useDMA = g_state.transport.ops->supportsDMA(g_state.transport.handle); + g_state.useDMA = + g_state.videoOps->frame->supportsDMA(g_state.transport.handle); // initialize the window dimensions at init for renderers g_state.windowW = g_params.w; @@ -2305,8 +2319,8 @@ static int lg_run(void) if (g_state.lgr->ops.getInterop && g_state.lgr->ops.getInterop(g_state.lgr, &interop)) interopPtr = &interop; - if (g_state.transport.ops->attachRenderer && - !g_state.transport.ops->attachRenderer( + if (g_state.videoOps->frame->attachRenderer && + !g_state.videoOps->frame->attachRenderer( g_state.transport.handle, interopPtr)) { DEBUG_ERROR("Failed to attach the renderer to the transport"); diff --git a/client/src/main.h b/client/src/main.h index 600b0f57..d1455b6c 100644 --- a/client/src/main.h +++ b/client/src/main.h @@ -124,6 +124,7 @@ struct AppState bool useDMA; LG_TransportInstance transport; + const LG_VideoOps * videoOps; LG_TransportFeatureFlags transportFeatures; LGThread * cursorThread; diff --git a/client/tests/lgmp_transport_test.c b/client/tests/lgmp_transport_test.c index 6c6e3f33..413187b1 100644 --- a/client/tests/lgmp_transport_test.c +++ b/client/tests/lgmp_transport_test.c @@ -82,6 +82,7 @@ int main(void) PLGMPMemory frameMemory = NULL; PLGMPMemory pointerMemory = NULL; LG_Transport * transport = NULL; + const LG_FrameOps * frameOps = NULL; debug_init(); @@ -155,6 +156,11 @@ int main(void) option_set_int("lgmp", "framePollInterval", 0); option_set_int("lgmp", "cursorPollInterval", 0); CHECK(LGT_LGMP.create(&transport)); + const LG_VideoOps * videoOps = LGT_LGMP.getVideoOps(transport); + CHECK(videoOps); + CHECK(videoOps->type == LG_VIDEO_TYPE_FRAME); + CHECK(videoOps->frame); + frameOps = videoOps->frame; CHECK(unlink(path) == 0); pathExists = false; @@ -167,8 +173,8 @@ int main(void) LG_TransportFrame frame; LG_TransportPointer pointer; - CHECK(LGT_LGMP.nextFrame(transport, false, &frame) == LG_TRANSPORT_TIMEOUT); - CHECK(LGT_LGMP.nextPointer(transport, &pointer) == LG_TRANSPORT_TIMEOUT); + CHECK(frameOps->nextFrame(transport, false, &frame) == LG_TRANSPORT_TIMEOUT); + CHECK(frameOps->nextPointer(transport, &pointer) == LG_TRANSPORT_TIMEOUT); CHECK(lgmpHostQueueHasSubs(frameQueue)); CHECK(lgmpHostQueueHasSubs(pointerQueue)); CHECK(lgmpHostQueueNewSubs(frameQueue) == 1); @@ -177,16 +183,16 @@ int main(void) CHECK(lgmpHostQueuePost(frameQueue, 0, frameMemory) == LGMP_OK); CHECK(lgmpHostQueuePost(pointerQueue, CURSOR_FLAG_POSITION, pointerMemory) == LGMP_OK); - CHECK(LGT_LGMP.nextFrame(transport, false, &frame) == LG_TRANSPORT_OK); + CHECK(frameOps->nextFrame(transport, false, &frame) == LG_TRANSPORT_OK); LG_TransportFrameTiming timing; - LGT_LGMP.getFrameTiming(transport, &frame, &timing); + frameOps->getFrameTiming(transport, &frame, &timing); CHECK(timing.captureTime == wireFrame->captureTime); CHECK(timing.postProcessTime == wireFrame->postProcessTime); CHECK(timing.copyTime == wireFrame->copyTime); CHECK(timing.readyTime == wireFrame->readyTime); - LGT_LGMP.releaseFrame(transport, &frame); - CHECK(LGT_LGMP.nextPointer(transport, &pointer) == LG_TRANSPORT_OK); - LGT_LGMP.releasePointer(transport, &pointer); + frameOps->releaseFrame(transport, &frame); + CHECK(frameOps->nextPointer(transport, &pointer) == LG_TRANSPORT_OK); + frameOps->releasePointer(transport, &pointer); /* * Stop with messages pending. Unsubscribing before the host timeout is the @@ -195,10 +201,10 @@ int main(void) CHECK(lgmpHostQueuePost(frameQueue, 0, frameMemory) == LGMP_OK); CHECK(lgmpHostQueuePost(pointerQueue, CURSOR_FLAG_POSITION, pointerMemory) == LGMP_OK); - CHECK(LGT_LGMP.stopFrame); - CHECK(LGT_LGMP.stopPointer); - LGT_LGMP.stopFrame(transport); - LGT_LGMP.stopPointer(transport); + CHECK(frameOps->stopFrame); + CHECK(frameOps->stopPointer); + frameOps->stopFrame(transport); + frameOps->stopPointer(transport); CHECK(!lgmpHostQueueHasSubs(frameQueue)); CHECK(!lgmpHostQueueHasSubs(pointerQueue)); @@ -206,8 +212,8 @@ int main(void) CHECK(lgmpHostQueuePending(frameQueue) == 0); CHECK(lgmpHostQueuePending(pointerQueue) == 0); - CHECK(LGT_LGMP.nextFrame(transport, false, &frame) == LG_TRANSPORT_TIMEOUT); - CHECK(LGT_LGMP.nextPointer(transport, &pointer) == LG_TRANSPORT_TIMEOUT); + CHECK(frameOps->nextFrame(transport, false, &frame) == LG_TRANSPORT_TIMEOUT); + CHECK(frameOps->nextPointer(transport, &pointer) == LG_TRANSPORT_TIMEOUT); CHECK(lgmpHostQueueNewSubs(frameQueue) == 1); CHECK(lgmpHostQueueNewSubs(pointerQueue) == 1); @@ -215,10 +221,10 @@ int main(void) CHECK(lgmpHostQueuePost(frameQueue, 0, frameMemory) == LGMP_OK); CHECK(lgmpHostQueuePost(pointerQueue, CURSOR_FLAG_POSITION, pointerMemory) == LGMP_OK); - CHECK(LGT_LGMP.nextFrame(transport, false, &frame) == LG_TRANSPORT_OK); - LGT_LGMP.releaseFrame(transport, &frame); - CHECK(LGT_LGMP.nextPointer(transport, &pointer) == LG_TRANSPORT_OK); - LGT_LGMP.releasePointer(transport, &pointer); + CHECK(frameOps->nextFrame(transport, false, &frame) == LG_TRANSPORT_OK); + frameOps->releaseFrame(transport, &frame); + CHECK(frameOps->nextPointer(transport, &pointer) == LG_TRANSPORT_OK); + frameOps->releasePointer(transport, &pointer); /* * Also recover when the host has already marked the cached handles bad. @@ -229,15 +235,15 @@ int main(void) CHECK(lgmpHostQueuePost(frameQueue, 0, frameMemory) == LGMP_OK); CHECK(lgmpHostQueuePost(pointerQueue, CURSOR_FLAG_POSITION, pointerMemory) == LGMP_OK); - CHECK(LGT_LGMP.nextFrame(transport, false, &frame) == LG_TRANSPORT_OK); + CHECK(frameOps->nextFrame(transport, false, &frame) == LG_TRANSPORT_OK); CHECK(waitForQueuesEmpty(host, frameQueue, pointerQueue)); CHECK(lgmpHostQueueHasSubs(frameQueue)); CHECK(lgmpHostQueueHasSubs(pointerQueue)); - LGT_LGMP.stopFrame(transport); - LGT_LGMP.stopPointer(transport); - CHECK(LGT_LGMP.nextFrame(transport, false, &frame) == LG_TRANSPORT_TIMEOUT); - CHECK(LGT_LGMP.nextPointer(transport, &pointer) == LG_TRANSPORT_TIMEOUT); + frameOps->stopFrame(transport); + frameOps->stopPointer(transport); + CHECK(frameOps->nextFrame(transport, false, &frame) == LG_TRANSPORT_TIMEOUT); + CHECK(frameOps->nextPointer(transport, &pointer) == LG_TRANSPORT_TIMEOUT); CHECK(lgmpHostQueueNewSubs(frameQueue) == 1); CHECK(lgmpHostQueueNewSubs(pointerQueue) == 1); @@ -245,10 +251,10 @@ int main(void) CHECK(lgmpHostQueuePost(frameQueue, 0, frameMemory) == LGMP_OK); CHECK(lgmpHostQueuePost(pointerQueue, CURSOR_FLAG_POSITION, pointerMemory) == LGMP_OK); - CHECK(LGT_LGMP.nextFrame(transport, false, &frame) == LG_TRANSPORT_OK); - LGT_LGMP.releaseFrame(transport, &frame); - CHECK(LGT_LGMP.nextPointer(transport, &pointer) == LG_TRANSPORT_OK); - LGT_LGMP.releasePointer(transport, &pointer); + CHECK(frameOps->nextFrame(transport, false, &frame) == LG_TRANSPORT_OK); + frameOps->releaseFrame(transport, &frame); + CHECK(frameOps->nextPointer(transport, &pointer) == LG_TRANSPORT_OK); + frameOps->releasePointer(transport, &pointer); result = 0; diff --git a/client/transports/LGMP/lgmp.c b/client/transports/LGMP/lgmp.c index eea5c612..92a5f23b 100644 --- a/client/transports/LGMP/lgmp.c +++ b/client/transports/LGMP/lgmp.c @@ -1360,17 +1360,9 @@ static const LG_InputOps * lgmp_getInputOps(LG_Transport * this, return lgmpInput_getOps(); } -const LG_TransportOps LGT_LGMP = +static const LG_FrameOps lgmpFrameOps = { - .name = "lgmp", - .setup = lgmp_setup, - .create = lgmp_create, - .destroy = lgmp_destroy, - .connect = lgmp_connect, - .disconnect = lgmp_disconnect, - .sessionValid = lgmp_sessionValid, .supportsDMA = lgmp_supportsDMA, - .getInputOps = lgmp_getInputOps, .attachRenderer = lgmp_attachRenderer, .detachRenderer = lgmp_detachRenderer, .nextFrame = lgmp_nextFrame, @@ -1380,6 +1372,31 @@ const LG_TransportOps LGT_LGMP = .nextPointer = lgmp_nextPointer, .releasePointer = lgmp_releasePointer, .stopPointer = lgmp_stopPointer, - .sendControl = lgmp_sendControl, - .controlStatus = lgmp_controlStatus, +}; + +static const LG_VideoOps lgmpVideoOps = +{ + .name = "LGMP", + .type = LG_VIDEO_TYPE_FRAME, + .frame = &lgmpFrameOps, +}; + +static const LG_VideoOps * lgmp_getVideoOps(LG_Transport * this) +{ + return &lgmpVideoOps; +} + +const LG_TransportOps LGT_LGMP = +{ + .name = "lgmp", + .setup = lgmp_setup, + .create = lgmp_create, + .destroy = lgmp_destroy, + .connect = lgmp_connect, + .disconnect = lgmp_disconnect, + .sessionValid = lgmp_sessionValid, + .getVideoOps = lgmp_getVideoOps, + .getInputOps = lgmp_getInputOps, + .sendControl = lgmp_sendControl, + .controlStatus = lgmp_controlStatus, }; diff --git a/client/transports/Test/test.c b/client/transports/Test/test.c index 4cf8a1e1..af3ff031 100644 --- a/client/transports/Test/test.c +++ b/client/transports/Test/test.c @@ -734,23 +734,40 @@ static const LG_InputOps * test_getInputOps(LG_Transport * this, return NULL; } -const LG_TransportOps LGT_Test = +static const LG_FrameOps testFrameOps = { - .name = "test", - .setup = test_setup, - .create = test_create, - .destroy = test_destroy, - .connect = test_connect, - .disconnect = test_disconnect, - .sessionValid = test_sessionValid, .supportsDMA = test_supportsDMA, - .getInputOps = test_getInputOps, .attachRenderer = test_attachRenderer, .detachRenderer = test_detachRenderer, .nextFrame = test_nextFrame, .releaseFrame = test_releaseFrame, .nextPointer = test_nextPointer, .releasePointer = test_releasePointer, - .sendControl = test_sendControl, - .controlStatus = test_controlStatus, +}; + +static const LG_VideoOps testVideoOps = +{ + .name = "test", + .type = LG_VIDEO_TYPE_FRAME, + .frame = &testFrameOps, +}; + +static const LG_VideoOps * test_getVideoOps(LG_Transport * this) +{ + return &testVideoOps; +} + +const LG_TransportOps LGT_Test = +{ + .name = "test", + .setup = test_setup, + .create = test_create, + .destroy = test_destroy, + .connect = test_connect, + .disconnect = test_disconnect, + .sessionValid = test_sessionValid, + .getVideoOps = test_getVideoOps, + .getInputOps = test_getInputOps, + .sendControl = test_sendControl, + .controlStatus = test_controlStatus, };