mirror of
https://github.com/moonlight-stream/moonlight-common-c.git
synced 2026-04-03 06:16:04 +00:00
Introduce optional pull-based API for video data
This commit is contained in:
@@ -173,6 +173,18 @@ int LiStartConnection(PSERVER_INFORMATION serverInfo, PSTREAM_CONFIGURATION stre
|
||||
void* audioContext, int arFlags) {
|
||||
int err;
|
||||
|
||||
if ((drCallbacks->capabilities & CAPABILITY_PULL_RENDERER) && drCallbacks->submitDecodeUnit) {
|
||||
Limelog("CAPABILITY_PULL_RENDERER cannot be set with a submitDecodeUnit callback\n");
|
||||
err = -1;
|
||||
goto Cleanup;
|
||||
}
|
||||
|
||||
if ((drCallbacks->capabilities & CAPABILITY_PULL_RENDERER) && (drCallbacks->capabilities & CAPABILITY_DIRECT_SUBMIT)) {
|
||||
Limelog("CAPABILITY_PULL_RENDERER and CAPABILITY_DIRECT_SUBMIT cannot be set together\n");
|
||||
err = -1;
|
||||
goto Cleanup;
|
||||
}
|
||||
|
||||
// Replace missing callbacks with placeholders
|
||||
fixupMissingCallbacks(&drCallbacks, &arCallbacks, &clCallbacks);
|
||||
memcpy(&VideoCallbacks, drCallbacks, sizeof(VideoCallbacks));
|
||||
|
||||
@@ -94,8 +94,8 @@ void requestDecoderRefresh(void);
|
||||
|
||||
void initializeVideoStream(void);
|
||||
void destroyVideoStream(void);
|
||||
void notifyKeyFrameReceived(void);
|
||||
int startVideoStream(void* rendererContext, int drFlags);
|
||||
void submitFrame(PQUEUED_DECODE_UNIT qdu);
|
||||
void stopVideoStream(void);
|
||||
|
||||
int initializeAudioStream(void);
|
||||
|
||||
@@ -241,6 +241,12 @@ typedef struct _DECODE_UNIT {
|
||||
// buffer size rather than just assuming it will always be 240.
|
||||
#define CAPABILITY_SUPPORTS_ARBITRARY_AUDIO_DURATION 0x10
|
||||
|
||||
// This flag opts the renderer into a pull-based model rather than the default push-based
|
||||
// callback model. The renderer must invoke the new functions (LiWaitForNextVideoFrame(),
|
||||
// LiCompleteVideoFrame(), and similar) to receive A/V data. Setting this capability while
|
||||
// also providing a sample callback is not allowed.
|
||||
#define CAPABILITY_PULL_RENDERER 0x20
|
||||
|
||||
// If set in the video renderer capabilities field, this macro specifies that the renderer
|
||||
// supports slicing to increase decoding performance. The parameter specifies the desired
|
||||
// number of slices per frame. This capability is only valid on video renderers.
|
||||
@@ -629,6 +635,18 @@ void LiStringifyPortFlags(unsigned int portFlags, const char* separator, char* o
|
||||
#define ML_TEST_RESULT_INCONCLUSIVE 0xFFFFFFFF
|
||||
unsigned int LiTestClientConnectivity(const char* testServer, unsigned short referencePort, unsigned int testPortFlags);
|
||||
|
||||
// This family of functions can be used for pull-based video renderers that opt to manage a decoding/rendering
|
||||
// thread themselves. After successfully calling the WaitFor/Poll variants that dequeue the video frame, you
|
||||
// must call LiCompleteVideoFrame() to notify that processing is completed. The same DR_* status values
|
||||
// from drSubmitDecodeUnit() must be passed to LiCompleteVideoFrame() as the drStatus argument.
|
||||
//
|
||||
// In order to safely use these functions, you must set CAPABILITY_PULL_RENDERER on the video decoder.
|
||||
typedef void* VIDEO_FRAME_HANDLE;
|
||||
bool LiWaitForNextVideoFrame(VIDEO_FRAME_HANDLE* frameHandle, PDECODE_UNIT* decodeUnit);
|
||||
bool LiPollNextVideoFrame(VIDEO_FRAME_HANDLE* frameHandle, PDECODE_UNIT* decodeUnit);
|
||||
bool LiPeekNextVideoFrame(PDECODE_UNIT* decodeUnit);
|
||||
void LiCompleteVideoFrame(VIDEO_FRAME_HANDLE handle, int drStatus);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -7,9 +7,6 @@ typedef struct _QUEUED_DECODE_UNIT {
|
||||
LINKED_BLOCKING_QUEUE_ENTRY entry;
|
||||
} QUEUED_DECODE_UNIT, *PQUEUED_DECODE_UNIT;
|
||||
|
||||
void completeQueuedDecodeUnit(PQUEUED_DECODE_UNIT qdu, int drStatus);
|
||||
bool getNextQueuedDecodeUnit(PQUEUED_DECODE_UNIT* qdu);
|
||||
|
||||
#pragma pack(push, 1)
|
||||
|
||||
#define FLAG_CONTAINS_PIC_DATA 0x1
|
||||
|
||||
@@ -117,7 +117,7 @@ static void freeDecodeUnitList(PLINKED_BLOCKING_QUEUE_ENTRY entry) {
|
||||
nextEntry = entry->flink;
|
||||
|
||||
// Complete this with a failure status
|
||||
completeQueuedDecodeUnit((PQUEUED_DECODE_UNIT)entry->data, DR_CLEANUP);
|
||||
LiCompleteVideoFrame(entry->data, DR_CLEANUP);
|
||||
|
||||
entry = nextEntry;
|
||||
}
|
||||
@@ -181,16 +181,53 @@ static bool getSpecialSeq(PBUFFER_DESC current, PBUFFER_DESC candidate) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Get the first decode unit available
|
||||
bool getNextQueuedDecodeUnit(PQUEUED_DECODE_UNIT* qdu) {
|
||||
int err = LbqWaitForQueueElement(&decodeUnitQueue, (void**)qdu);
|
||||
return (err == LBQ_SUCCESS);
|
||||
bool LiWaitForNextVideoFrame(VIDEO_FRAME_HANDLE* frameHandle, PDECODE_UNIT* decodeUnit) {
|
||||
PQUEUED_DECODE_UNIT qdu;
|
||||
|
||||
int err = LbqWaitForQueueElement(&decodeUnitQueue, (void**)&qdu);
|
||||
if (err != LBQ_SUCCESS) {
|
||||
return false;
|
||||
}
|
||||
|
||||
*frameHandle = qdu;
|
||||
*decodeUnit = &qdu->decodeUnit;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool LiPollNextVideoFrame(VIDEO_FRAME_HANDLE* frameHandle, PDECODE_UNIT* decodeUnit) {
|
||||
PQUEUED_DECODE_UNIT qdu;
|
||||
|
||||
int err = LbqPollQueueElement(&decodeUnitQueue, (void**)&qdu);
|
||||
if (err != LBQ_SUCCESS) {
|
||||
return false;
|
||||
}
|
||||
|
||||
*frameHandle = qdu;
|
||||
*decodeUnit = &qdu->decodeUnit;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool LiPeekNextVideoFrame(PDECODE_UNIT* decodeUnit) {
|
||||
PQUEUED_DECODE_UNIT qdu;
|
||||
|
||||
int err = LbqPeekQueueElement(&decodeUnitQueue, (void**)&qdu);
|
||||
if (err != LBQ_SUCCESS) {
|
||||
return false;
|
||||
}
|
||||
|
||||
*decodeUnit = &qdu->decodeUnit;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Cleanup a decode unit by freeing the buffer chain and the holder
|
||||
void completeQueuedDecodeUnit(PQUEUED_DECODE_UNIT qdu, int drStatus) {
|
||||
void LiCompleteVideoFrame(VIDEO_FRAME_HANDLE handle, int drStatus) {
|
||||
PQUEUED_DECODE_UNIT qdu = handle;
|
||||
PLENTRY_INTERNAL lastEntry;
|
||||
|
||||
if (qdu->decodeUnit.frameType == FRAME_TYPE_IDR) {
|
||||
notifyKeyFrameReceived();
|
||||
}
|
||||
|
||||
if (drStatus == DR_NEED_IDR) {
|
||||
Limelog("Requesting IDR frame on behalf of DR\n");
|
||||
requestDecoderRefresh();
|
||||
@@ -353,7 +390,7 @@ static void reassembleFrame(int frameNumber) {
|
||||
}
|
||||
else {
|
||||
// Submit the frame to the decoder
|
||||
submitFrame(qdu);
|
||||
LiCompleteVideoFrame(qdu, VideoCallbacks.submitDecodeUnit(&qdu->decodeUnit));
|
||||
}
|
||||
|
||||
// Notify the control connection
|
||||
|
||||
@@ -154,24 +154,22 @@ static void ReceiveThreadProc(void* context) {
|
||||
}
|
||||
}
|
||||
|
||||
void submitFrame(PQUEUED_DECODE_UNIT qdu) {
|
||||
// Pass the frame to the decoder
|
||||
int ret = VideoCallbacks.submitDecodeUnit(&qdu->decodeUnit);
|
||||
completeQueuedDecodeUnit(qdu, ret);
|
||||
|
||||
void notifyKeyFrameReceived(void) {
|
||||
// Remember that we got a full frame successfully
|
||||
receivedFullFrame = true;
|
||||
}
|
||||
|
||||
// Decoder thread proc
|
||||
static void DecoderThreadProc(void* context) {
|
||||
PQUEUED_DECODE_UNIT qdu;
|
||||
while (!PltIsThreadInterrupted(&decoderThread)) {
|
||||
if (!getNextQueuedDecodeUnit(&qdu)) {
|
||||
VIDEO_FRAME_HANDLE frameHandle;
|
||||
PDECODE_UNIT decodeUnit;
|
||||
|
||||
if (!LiWaitForNextVideoFrame(&frameHandle, &decodeUnit)) {
|
||||
return;
|
||||
}
|
||||
|
||||
submitFrame(qdu);
|
||||
LiCompleteVideoFrame(frameHandle, VideoCallbacks.submitDecodeUnit(decodeUnit));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user