-
Notifications
You must be signed in to change notification settings - Fork 336
Expand file tree
/
Copy pathLimelight.h
More file actions
966 lines (815 loc) · 47.4 KB
/
Copy pathLimelight.h
File metadata and controls
966 lines (815 loc) · 47.4 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
//
// This header exposes the public streaming API for client usage
//
#pragma once
#include <stdint.h>
#include <stdbool.h>
#ifdef __cplusplus
extern "C" {
#endif
// Enable this definition during debugging to enable assertions
//#define LC_DEBUG
// Values for the 'streamingRemotely' field below
#define STREAM_CFG_LOCAL 0
#define STREAM_CFG_REMOTE 1
#define STREAM_CFG_AUTO 2
// Values for the 'colorSpace' field below.
// Rec. 2020 is not supported with H.264 video streams on GFE hosts.
#define COLORSPACE_REC_601 0
#define COLORSPACE_REC_709 1
#define COLORSPACE_REC_2020 2
// Values for the 'colorRange' field below
#define COLOR_RANGE_LIMITED 0
#define COLOR_RANGE_FULL 1
// Values for 'encryptionFlags' field below
#define ENCFLG_NONE 0x00000000
#define ENCFLG_AUDIO 0x00000001
#define ENCFLG_VIDEO 0x00000002
#define ENCFLG_ALL 0xFFFFFFFF
// This function returns a string that you SHOULD append to the /launch and /resume
// query parameter string. This is used to enable certain extended functionality
// with Sunshine hosts. The returned string is owned by moonlight-common-c and
// should not be freed by the caller.
const char* LiGetLaunchUrlQueryParameters(void);
typedef struct _STREAM_CONFIGURATION {
// Dimensions in pixels of the desired video stream
int width;
int height;
// FPS of the desired video stream
int fps;
// Bitrate of the desired video stream (audio adds another ~1 Mbps). This
// includes error correction data, so the actual encoder bitrate will be
// about 20% lower when using the standard 20% FEC configuration.
int bitrate;
// Max video packet size in bytes (use 1024 if unsure). If STREAM_CFG_AUTO
// determines the stream is remote (see below), it will cap this value at
// 1024 to avoid MTU-related issues like packet loss and fragmentation.
int packetSize;
// Determines whether to enable remote (over the Internet)
// streaming optimizations. If unsure, set to STREAM_CFG_AUTO.
// STREAM_CFG_AUTO uses a heuristic (whether the target address is
// in the RFC 1918 address blocks) to decide whether the stream
// is remote or not.
int streamingRemotely;
// Specifies the channel configuration of the audio stream.
// See AUDIO_CONFIGURATION constants and MAKE_AUDIO_CONFIGURATION() below.
int audioConfiguration;
// Specifies the mask of supported video formats.
// See VIDEO_FORMAT constants below.
int supportedVideoFormats;
// If specified, the client's display refresh rate x 100. For example,
// 59.94 Hz would be specified as 5994. This is used by recent versions
// of GFE for enhanced frame pacing.
int clientRefreshRateX100;
// If specified, sets the encoder colorspace to the provided COLORSPACE_*
// option (listed above). If not set, the encoder will default to Rec 601.
int colorSpace;
// If specified, sets the encoder color range to the provided COLOR_RANGE_*
// option (listed above). If not set, the encoder will default to Limited.
int colorRange;
// Specifies the data streams where encryption may be enabled if supported
// by the host PC. Ideally, you would pass ENCFLG_ALL to encrypt everything
// that we support encrypting. However, lower performance hardware may not
// be able to support encrypting heavy stuff like video or audio data, so
// that encryption may be disabled here. Remote input encryption is always
// enabled.
int encryptionFlags;
// AES encryption data for the remote input stream. This must be
// the same as what was passed as rikey and rikeyid
// in /launch and /resume requests.
char remoteInputAesKey[16];
char remoteInputAesIv[16];
} STREAM_CONFIGURATION, *PSTREAM_CONFIGURATION;
// Use this function to zero the stream configuration when allocated on the stack or heap
void LiInitializeStreamConfiguration(PSTREAM_CONFIGURATION streamConfig);
// These identify codec configuration data in the buffer lists
// of frames identified as IDR frames for H.264 and HEVC formats.
// For other codecs, all data is marked as BUFFER_TYPE_PICDATA.
#define BUFFER_TYPE_PICDATA 0x00
#define BUFFER_TYPE_SPS 0x01
#define BUFFER_TYPE_PPS 0x02
#define BUFFER_TYPE_VPS 0x03
typedef struct _LENTRY {
// Pointer to the next entry or NULL if this is the last entry
struct _LENTRY* next;
// Pointer to data (never NULL)
char* data;
// Size of data in bytes (never <= 0)
int length;
// Buffer type (listed above, only set for H.264 and HEVC formats)
int bufferType;
} LENTRY, *PLENTRY;
// This is a standard frame which references the IDR frame and
// previous P-frames.
#define FRAME_TYPE_PFRAME 0x00
// This is a key frame.
//
// For H.264 and HEVC, this means the frame contains SPS, PPS, and VPS (HEVC only) NALUs
// as the first buffers in the list. The I-frame data follows immediately
// after the codec configuration NALUs.
//
// For other codecs, any configuration data is not split into separate buffers.
#define FRAME_TYPE_IDR 0x01
// A decode unit describes a buffer chain of video data from multiple packets
typedef struct _DECODE_UNIT {
// Frame number
int frameNumber;
// Frame type
int frameType;
// Optional host processing latency of the frame, in 1/10 ms units.
// Zero when the host doesn't provide the latency data
// or frame processing latency is not applicable to the current frame
// (happens when the frame is repeated).
uint16_t frameHostProcessingLatency;
// Receive time of first buffer. This value uses an implementation-defined epoch,
// but the same epoch as enqueueTimeMs and LiGetMillis().
uint64_t receiveTimeMs;
// Time the frame was fully assembled and queued for the video decoder to process.
// This is also approximately the same time as the final packet was received, so
// enqueueTimeMs - receiveTimeMs is the time taken to receive the frame. At the
// time the decode unit is passed to submitDecodeUnit(), the total queue delay
// can be calculated by LiGetMillis() - enqueueTimeMs.
uint64_t enqueueTimeMs;
// Presentation time in milliseconds with the epoch at the first captured frame.
// This can be used to aid frame pacing or to drop old frames that were queued too
// long prior to display.
unsigned int presentationTimeMs;
// Length of the entire buffer chain in bytes
int fullLength;
// Head of the buffer chain (never NULL)
PLENTRY bufferList;
// Determines if this frame is SDR or HDR
//
// Note: This is not currently parsed from the actual bitstream, so if your
// client has access to a bitstream parser, prefer that over this field.
bool hdrActive;
// Provides the colorspace of this frame (see COLORSPACE_* defines above)
//
// Note: This is not currently parsed from the actual bitstream, so if your
// client has access to a bitstream parser, prefer that over this field.
uint8_t colorspace;
} DECODE_UNIT, *PDECODE_UNIT;
// Specifies that the audio stream should be encoded in stereo (default)
#define AUDIO_CONFIGURATION_STEREO MAKE_AUDIO_CONFIGURATION(2, 0x3)
// Specifies that the audio stream should be in 5.1 surround sound if the PC is able
#define AUDIO_CONFIGURATION_51_SURROUND MAKE_AUDIO_CONFIGURATION(6, 0x3F)
// Specifies that the audio stream should be in 7.1 surround sound if the PC is able
#define AUDIO_CONFIGURATION_71_SURROUND MAKE_AUDIO_CONFIGURATION(8, 0x63F)
// Specifies an audio configuration by channel count and channel mask
// See https://docs.microsoft.com/en-us/windows-hardware/drivers/audio/channel-mask for channelMask values
// NOTE: Not all combinations are supported by GFE and/or this library.
#define MAKE_AUDIO_CONFIGURATION(channelCount, channelMask) \
(((channelMask) << 16) | (channelCount << 8) | 0xCA)
// Helper macros for retreiving channel count and channel mask from the audio configuration
#define CHANNEL_COUNT_FROM_AUDIO_CONFIGURATION(x) (((x) >> 8) & 0xFF)
#define CHANNEL_MASK_FROM_AUDIO_CONFIGURATION(x) (((x) >> 16) & 0xFFFF)
// Helper macro to retreive the surroundAudioInfo parameter value that must be passed in
// the /launch and /resume HTTPS requests when starting the session.
#define SURROUNDAUDIOINFO_FROM_AUDIO_CONFIGURATION(x) \
(CHANNEL_MASK_FROM_AUDIO_CONFIGURATION(x) << 16 | CHANNEL_COUNT_FROM_AUDIO_CONFIGURATION(x))
// The maximum number of channels supported
#define AUDIO_CONFIGURATION_MAX_CHANNEL_COUNT 8
// Passed in StreamConfiguration.supportedVideoFormats to specify supported codecs
// and to DecoderRendererSetup() to specify selected codec.
#define VIDEO_FORMAT_H264 0x0001 // H.264 High Profile
#define VIDEO_FORMAT_H264_HIGH8_444 0x0004 // H.264 High 4:4:4 8-bit Profile
#define VIDEO_FORMAT_H265 0x0100 // HEVC Main Profile
#define VIDEO_FORMAT_H265_MAIN10 0x0200 // HEVC Main10 Profile
#define VIDEO_FORMAT_H265_REXT8_444 0x0400 // HEVC RExt 4:4:4 8-bit Profile
#define VIDEO_FORMAT_H265_REXT10_444 0x0800 // HEVC RExt 4:4:4 10-bit Profile
#define VIDEO_FORMAT_AV1_MAIN8 0x1000 // AV1 Main 8-bit profile
#define VIDEO_FORMAT_AV1_MAIN10 0x2000 // AV1 Main 10-bit profile
#define VIDEO_FORMAT_AV1_HIGH8_444 0x4000 // AV1 High 4:4:4 8-bit profile
#define VIDEO_FORMAT_AV1_HIGH10_444 0x8000 // AV1 High 4:4:4 10-bit profile
// Masks for clients to use to match video codecs without profile-specific details.
#define VIDEO_FORMAT_MASK_H264 0x000F
#define VIDEO_FORMAT_MASK_H265 0x0F00
#define VIDEO_FORMAT_MASK_AV1 0xF000
#define VIDEO_FORMAT_MASK_10BIT 0xAA00
#define VIDEO_FORMAT_MASK_YUV444 0xCC04
// If set in the renderer capabilities field, this flag will cause audio/video data to
// be submitted directly from the receive thread. This should only be specified if the
// renderer is non-blocking. This flag is valid on both audio and video renderers.
#define CAPABILITY_DIRECT_SUBMIT 0x1
// If set in the video renderer capabilities field, this flag specifies that the renderer
// supports reference frame invalidation for AVC/H.264 streams. This flag is only valid on video renderers.
// If using this feature, the bitstream may not be patched (changing num_ref_frames or max_dec_frame_buffering)
// to avoid video corruption on packet loss.
#define CAPABILITY_REFERENCE_FRAME_INVALIDATION_AVC 0x2
// If set in the video renderer capabilities field, this flag specifies that the renderer
// supports reference frame invalidation for HEVC/H.265 streams. This flag is only valid on video renderers.
#define CAPABILITY_REFERENCE_FRAME_INVALIDATION_HEVC 0x4
// If set in the audio renderer capabilities field, this flag will cause the RTSP negotiation
// to never request the "high quality" audio preset. If unset, high quality audio will be
// used with video streams above 15 Mbps.
#define CAPABILITY_SLOW_OPUS_DECODER 0x8
// If set in the audio renderer capabilities field, this indicates that audio packets
// may contain more or less than 5 ms of audio. This requires that audio renderers read the
// samplesPerFrame field in OPUS_MULTISTREAM_CONFIGURATION to calculate the correct decoded
// 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 flag specifies that the renderer
// supports reference frame invalidation for AV1 streams. This flag is only valid on video renderers.
#define CAPABILITY_REFERENCE_FRAME_INVALIDATION_AV1 0x40
// 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.
#define CAPABILITY_SLICES_PER_FRAME(x) (((unsigned char)(x)) << 24)
// If set in the video renderer capabilities field, this flag specifies that the renderer
// needs to opt-in to intra refresh, some clients (like the Xbox client with NVEnc) needs it
#define CAPABILITY_INTRA_REFRESH 0x80
// This callback is invoked to provide details about the video stream and allow configuration of the decoder.
// Returns 0 on success, non-zero on failure.
typedef int(*DecoderRendererSetup)(int videoFormat, int width, int height, int redrawRate, void* context, int drFlags);
// This callback notifies the decoder that the stream is starting. No frames can be submitted before this callback returns.
typedef void(*DecoderRendererStart)(void);
// This callback notifies the decoder that the stream is stopping. Frames may still be submitted but they may be safely discarded.
typedef void(*DecoderRendererStop)(void);
// This callback performs the teardown of the video decoder. No more frames will be submitted when this callback is invoked.
typedef void(*DecoderRendererCleanup)(void);
// This callback provides Annex B formatted elementary stream data to the
// decoder. If the decoder is unable to process the submitted data for some reason,
// it must return DR_NEED_IDR to generate a keyframe.
#define DR_OK 0
#define DR_NEED_IDR -1
typedef int(*DecoderRendererSubmitDecodeUnit)(PDECODE_UNIT decodeUnit);
typedef struct _DECODER_RENDERER_CALLBACKS {
DecoderRendererSetup setup;
DecoderRendererStart start;
DecoderRendererStop stop;
DecoderRendererCleanup cleanup;
DecoderRendererSubmitDecodeUnit submitDecodeUnit;
int capabilities;
} DECODER_RENDERER_CALLBACKS, *PDECODER_RENDERER_CALLBACKS;
// Use this function to zero the video callbacks when allocated on the stack or heap
void LiInitializeVideoCallbacks(PDECODER_RENDERER_CALLBACKS drCallbacks);
// This structure provides the Opus multistream decoder parameters required to successfully
// decode the audio stream being sent from the computer. See opus_multistream_decoder_init docs
// for details about these fields.
//
// The supplied mapping array is indexed according to the following output channel order:
// 0 - Front Left
// 1 - Front Right
// 2 - Center
// 3 - LFE
// 4 - Back Left
// 5 - Back Right
// 6 - Side Left
// 7 - Side Right
//
// If the mapping order does not match the channel order of the audio renderer, you may swap
// the values in the mismatched indices until the mapping array matches the desired channel order.
typedef struct _OPUS_MULTISTREAM_CONFIGURATION {
int sampleRate;
int channelCount;
int streams;
int coupledStreams;
int samplesPerFrame;
unsigned char mapping[AUDIO_CONFIGURATION_MAX_CHANNEL_COUNT];
} OPUS_MULTISTREAM_CONFIGURATION, *POPUS_MULTISTREAM_CONFIGURATION;
// This callback initializes the audio renderer. The audio configuration parameter
// provides the negotiated audio configuration. This may differ from the one
// specified in the stream configuration. Returns 0 on success, non-zero on failure.
typedef int(*AudioRendererInit)(int audioConfiguration, const POPUS_MULTISTREAM_CONFIGURATION opusConfig, void* context, int arFlags);
// This callback notifies the decoder that the stream is starting. No audio can be submitted before this callback returns.
typedef void(*AudioRendererStart)(void);
// This callback notifies the decoder that the stream is stopping. Audio samples may still be submitted but they may be safely discarded.
typedef void(*AudioRendererStop)(void);
// This callback performs the final teardown of the audio decoder. No additional audio will be submitted when this callback is invoked.
typedef void(*AudioRendererCleanup)(void);
// This callback provides Opus audio data to be decoded and played. sampleLength is in bytes.
typedef void(*AudioRendererDecodeAndPlaySample)(char* sampleData, int sampleLength);
typedef struct _AUDIO_RENDERER_CALLBACKS {
AudioRendererInit init;
AudioRendererStart start;
AudioRendererStop stop;
AudioRendererCleanup cleanup;
AudioRendererDecodeAndPlaySample decodeAndPlaySample;
int capabilities;
} AUDIO_RENDERER_CALLBACKS, *PAUDIO_RENDERER_CALLBACKS;
// Use this function to zero the audio callbacks when allocated on the stack or heap
void LiInitializeAudioCallbacks(PAUDIO_RENDERER_CALLBACKS arCallbacks);
// Subject to change in future releases
// Use LiGetStageName() for stable stage names
#define STAGE_NONE 0
#define STAGE_PLATFORM_INIT 1
#define STAGE_NAME_RESOLUTION 2
#define STAGE_AUDIO_STREAM_INIT 3
#define STAGE_RTSP_HANDSHAKE 4
#define STAGE_CONTROL_STREAM_INIT 5
#define STAGE_VIDEO_STREAM_INIT 6
#define STAGE_INPUT_STREAM_INIT 7
#define STAGE_CONTROL_STREAM_START 8
#define STAGE_VIDEO_STREAM_START 9
#define STAGE_AUDIO_STREAM_START 10
#define STAGE_INPUT_STREAM_START 11
#define STAGE_MAX 12
// This callback is invoked to indicate that a stage of initialization is about to begin
typedef void(*ConnListenerStageStarting)(int stage);
// This callback is invoked to indicate that a stage of initialization has completed
typedef void(*ConnListenerStageComplete)(int stage);
// This callback is invoked to indicate that a stage of initialization has failed.
// ConnListenerConnectionTerminated() will not be invoked because the connection was
// not yet fully established. LiInterruptConnection() and LiStopConnection() may
// result in this callback being invoked, but it is not guaranteed.
typedef void(*ConnListenerStageFailed)(int stage, int errorCode);
// This callback is invoked after the connection is successfully established
typedef void(*ConnListenerConnectionStarted)(void);
// This callback is invoked when a connection is terminated after establishment.
// The errorCode will be 0 if the termination was reported to be intentional
// from the server (for example, the user closed the game). If errorCode is
// non-zero, it means the termination was probably unexpected (loss of network,
// crash, or similar conditions). This will not be invoked as a result of a call
// to LiStopConnection() or LiInterruptConnection().
typedef void(*ConnListenerConnectionTerminated)(int errorCode);
// This error code is passed to ConnListenerConnectionTerminated() when the stream
// is being gracefully terminated by the host. It usually means the app on the host
// PC has exited.
#define ML_ERROR_GRACEFUL_TERMINATION 0
// This error is passed to ConnListenerConnectionTerminated() if no video data
// was ever received for this connection after waiting several seconds. It likely
// indicates a problem with traffic on UDP 47998 due to missing or incorrect
// firewall or port forwarding rules.
#define ML_ERROR_NO_VIDEO_TRAFFIC -100
// This error is passed to ConnListenerConnectionTerminated() if a fully formed
// frame could not be received after waiting several seconds. It likely indicates
// an extremely unstable connection or a bitrate that is far too high.
#define ML_ERROR_NO_VIDEO_FRAME -101
// This error is passed to ConnListenerConnectionTerminated() if the stream ends
// very soon after starting due to a graceful termination from the host. Usually
// this seems to happen if DRM protected content is on-screen (pre-GFE 3.22), or
// another issue that prevents the encoder from being able to capture video successfully.
#define ML_ERROR_UNEXPECTED_EARLY_TERMINATION -102
// This error is passed to ConnListenerConnectionTerminated() if the stream ends
// due to a protected content error from the host. This value is supported on GFE 3.22+.
#define ML_ERROR_PROTECTED_CONTENT -103
// This error is passed to ConnListenerConnectionTerminated() if the stream ends
// due a frame conversion error. This is most commonly due to an incompatible
// desktop resolution and streaming resolution with HDR enabled. This value is
// supported on GFE 3.22+.
#define ML_ERROR_FRAME_CONVERSION -104
// This callback is invoked to log debug message
typedef void(*ConnListenerLogMessage)(const char* format, ...);
// This callback is invoked to rumble a gamepad. The rumble effect values
// set in this callback are expected to persist until a future call sets a
// different haptic effect or turns off the motors by passing 0 for both
// motors. It is possible to receive rumble events for gamepads that aren't
// physically present, so your callback should handle this possibility.
typedef void(*ConnListenerRumble)(unsigned short controllerNumber, unsigned short lowFreqMotor, unsigned short highFreqMotor);
// This callback is used to notify the client of a connection status change.
// Consider displaying an overlay for the user to notify them why their stream
// is not performing as expected.
#define CONN_STATUS_OKAY 0
#define CONN_STATUS_POOR 1
typedef void(*ConnListenerConnectionStatusUpdate)(int connectionStatus);
// This callback is invoked to notify the client of a change in HDR mode on
// the host. The client will probably want to update the local display mode
// to match the state of HDR on the host. This callback may be invoked even
// if the stream is not using an HDR-capable codec.
typedef void(*ConnListenerSetHdrMode)(bool hdrEnabled);
// This callback is invoked to rumble a gamepad's triggers. For more details,
// see the comment above on ConnListenerRumble().
typedef void(*ConnListenerRumbleTriggers)(uint16_t controllerNumber, uint16_t leftTriggerMotor, uint16_t rightTriggerMotor);
// This callback is invoked to notify the client that the host would like motion
// sensor reports for the specified gamepad (see LiSendControllerMotionEvent())
// at the specified reporting rate (or as close as possible).
//
// If reportRateHz is 0, the host is asking for motion event reporting to stop.
typedef void(*ConnListenerSetMotionEventState)(uint16_t controllerNumber, uint8_t motionType, uint16_t reportRateHz);
// This callback is invoked to set a controller's RGB LED (if present).
typedef void(*ConnListenerSetControllerLED)(uint16_t controllerNumber, uint8_t r, uint8_t g, uint8_t b);
typedef struct _CONNECTION_LISTENER_CALLBACKS {
ConnListenerStageStarting stageStarting;
ConnListenerStageComplete stageComplete;
ConnListenerStageFailed stageFailed;
ConnListenerConnectionStarted connectionStarted;
ConnListenerConnectionTerminated connectionTerminated;
ConnListenerLogMessage logMessage;
ConnListenerRumble rumble;
ConnListenerConnectionStatusUpdate connectionStatusUpdate;
ConnListenerSetHdrMode setHdrMode;
ConnListenerRumbleTriggers rumbleTriggers;
ConnListenerSetMotionEventState setMotionEventState;
ConnListenerSetControllerLED setControllerLED;
} CONNECTION_LISTENER_CALLBACKS, *PCONNECTION_LISTENER_CALLBACKS;
// Use this function to zero the connection callbacks when allocated on the stack or heap
void LiInitializeConnectionCallbacks(PCONNECTION_LISTENER_CALLBACKS clCallbacks);
// ServerCodecModeSupport values
#define SCM_H264 0x00000001
#define SCM_HEVC 0x00000100
#define SCM_HEVC_MAIN10 0x00000200
#define SCM_AV1_MAIN8 0x00010000 // Sunshine extension
#define SCM_AV1_MAIN10 0x00020000 // Sunshine extension
#define SCM_H264_HIGH8_444 0x00040000 // Sunshine extension
#define SCM_HEVC_REXT8_444 0x00080000 // Sunshine extension
#define SCM_HEVC_REXT10_444 0x00100000 // Sunshine extension
#define SCM_AV1_HIGH8_444 0x00200000 // Sunshine extension
#define SCM_AV1_HIGH10_444 0x00400000 // Sunshine extension
// SCM masks to identify various codec capabilities
#define SCM_MASK_H264 (SCM_H264 | SCM_H264_HIGH8_444)
#define SCM_MASK_HEVC (SCM_HEVC | SCM_HEVC_MAIN10 | SCM_HEVC_REXT8_444 | SCM_HEVC_REXT10_444)
#define SCM_MASK_AV1 (SCM_AV1_MAIN8 | SCM_AV1_MAIN10 | SCM_AV1_HIGH8_444 | SCM_AV1_HIGH10_444)
#define SCM_MASK_10BIT (SCM_HEVC_MAIN10 | SCM_HEVC_REXT10_444 | SCM_AV1_MAIN10 | SCM_AV1_HIGH10_444)
#define SCM_MASK_YUV444 (SCM_H264_HIGH8_444 | SCM_HEVC_REXT8_444 | SCM_HEVC_REXT10_444 | SCM_AV1_HIGH8_444 | SCM_AV1_HIGH10_444)
typedef struct _SERVER_INFORMATION {
// Server host name or IP address in text form
const char* address;
// Text inside 'appversion' tag in /serverinfo
const char* serverInfoAppVersion;
// Text inside 'GfeVersion' tag in /serverinfo (if present)
const char* serverInfoGfeVersion;
// Text inside 'sessionUrl0' tag in /resume and /launch (if present)
const char* rtspSessionUrl;
// Specifies the 'ServerCodecModeSupport' from the /serverinfo response.
int serverCodecModeSupport;
} SERVER_INFORMATION, *PSERVER_INFORMATION;
// Use this function to zero the server information when allocated on the stack or heap
void LiInitializeServerInformation(PSERVER_INFORMATION serverInfo);
// This function begins streaming.
//
// Callbacks are all optional. Pass NULL for individual callbacks within each struct or pass NULL for the entire struct
// to use the defaults for all callbacks.
//
// This function is not thread-safe.
//
int LiStartConnection(PSERVER_INFORMATION serverInfo, PSTREAM_CONFIGURATION streamConfig, PCONNECTION_LISTENER_CALLBACKS clCallbacks,
PDECODER_RENDERER_CALLBACKS drCallbacks, PAUDIO_RENDERER_CALLBACKS arCallbacks, void* renderContext, int drFlags,
void* audioContext, int arFlags);
// This function stops streaming. This function is not thread-safe.
void LiStopConnection(void);
// This function interrupts a pending LiStartConnection() call. This interruption happens asynchronously
// so it is not safe to start another connection before the first LiStartConnection() call returns.
void LiInterruptConnection(void);
// Use to get a user-visible string to display initialization progress
// from the integer passed to the ConnListenerStageXXX callbacks
const char* LiGetStageName(int stage);
// This function returns an estimate of the current RTT to the host PC obtained via ENet
// protocol statistics. This function will fail if the current GFE version does not use
// ENet for the control stream (very old versions), or if the ENet peer is not connected.
// This function may only be called between LiStartConnection() and LiStopConnection().
bool LiGetEstimatedRttInfo(uint32_t* estimatedRtt, uint32_t* estimatedRttVariance);
// This function queues a relative mouse move event to be sent to the remote server.
int LiSendMouseMoveEvent(short deltaX, short deltaY);
// This function queues a mouse position update event to be sent to the remote server.
// This functionality is only reliably supported on GFE 3.20 or later. Earlier versions
// may not position the mouse correctly.
//
// Absolute mouse motion doesn't work in many games, so this mode should not be the default
// for mice when streaming. It may be desirable as the default touchscreen behavior when
// LiSendTouchEvent() is not supported and the touchscreen is not the primary input method.
// In the latter case, a touchscreen-as-trackpad mode using LiSendMouseMoveEvent() is likely
// to be better for gaming use cases.
//
// The x and y values are transformed to host coordinates as if they are from a plane which
// is referenceWidth by referenceHeight in size. This allows you to provide coordinates that
// are relative to an arbitrary plane, such as a window, screen, or scaled video view.
//
// For example, if you wanted to directly pass window coordinates as x and y, you would set
// referenceWidth and referenceHeight to your window width and height.
int LiSendMousePositionEvent(short x, short y, short referenceWidth, short referenceHeight);
// This function queues a mouse position update event to be sent to the remote server, so
// all of the limitations of LiSendMousePositionEvent() mentioned above apply here too!
//
// This function behaves like a combination of LiSendMouseMoveEvent() and LiSendMousePositionEvent()
// in that it sends a relative motion event, however it sends this data as an absolute position
// based on the computed position of a virtual client cursor which is "moved" any time that
// LiSendMousePositionEvent() or LiSendMouseMoveAsMousePositionEvent() is called. As a result
// of this internal virtual cursor state, callers must ensure LiSendMousePositionEvent() and
// LiSendMouseMoveAsMousePositionEvent() are not called concurrently!
//
// The big advantage of this function is that it allows callers to avoid mouse acceleration that
// would otherwise affect motion when using LiSendMouseMoveEvent(). The downside is that it has the
// same game compatibility issues as LiSendMousePositionEvent().
//
// This function can be useful when mouse capture is the only feasible way to receive mouse input,
// like on Android or iOS, and the OS cannot provide raw unaccelerated mouse motion when capturing.
// Using this function avoids double-acceleration in cases when the client motion is also accelerated.
int LiSendMouseMoveAsMousePositionEvent(short deltaX, short deltaY, short referenceWidth, short referenceHeight);
// Error return value to indicate that the requested functionality is not supported by the host
#define LI_ERR_UNSUPPORTED -5501
// This function allows multi-touch input to be sent directly to Sunshine hosts. The x and y values
// are normalized device coordinates stretching top-left corner (0.0, 0.0) to bottom-right corner
// (1.0, 1.0) of the video area.
//
// Pointer ID is an opaque ID that must uniquely identify each active touch on screen. It must
// remain constant through any down/up/move/cancel events involved in a single touch interaction.
//
// Rotation is in degrees from vertical in Y dimension (parallel to screen, 0..360). If rotation is
// unknown, pass LI_ROT_UNKNOWN.
//
// Pressure is a 0.0 to 1.0 range value from min to max pressure. Sending a down/move event with
// a pressure of 0.0 indicates the actual pressure is unknown.
//
// For hover events, the pressure value is treated as a 1.0 to 0.0 range of distance from the touch
// surface where 1.0 is the farthest measurable distance and 0.0 is actually touching the display
// (which is invalid for a hover event). Reporting distance 0.0 for a hover event indicates the
// actual distance is unknown.
//
// Contact area is modelled as an ellipse with major and minor axis values in normalized device
// coordinates. If contact area is unknown, report 0.0 for both contact area axis parameters.
// For circular contact areas or if a minor axis value is not available, pass the same value
// for major and minor axes. For APIs or devices, that don't report contact area as an ellipse,
// approximations can be used such as: https://docs.kernel.org/input/multi-touch-protocol.html#event-computation
//
// For hover events, the "contact area" is the size of the hovering finger/tool. If unavailable,
// pass 0.0 for both contact area parameters.
//
// Touches can be cancelled using LI_TOUCH_EVENT_CANCEL or LI_TOUCH_EVENT_CANCEL_ALL. When using
// LI_TOUCH_EVENT_CANCEL, only the pointerId parameter is valid. All other parameters are ignored.
// To cancel all active touches (on focus loss, for example), use LI_TOUCH_EVENT_CANCEL_ALL.
//
// If unsupported by the host, this will return LI_ERR_UNSUPPORTED and the caller should consider
// falling back to other functions to send this input (such as LiSendMousePositionEvent()).
//
// To determine if LiSendTouchEvent() is supported without calling it, call LiGetHostFeatureFlags()
// and check for the LI_FF_PEN_TOUCH_EVENTS flag.
#define LI_TOUCH_EVENT_HOVER 0x00
#define LI_TOUCH_EVENT_DOWN 0x01
#define LI_TOUCH_EVENT_UP 0x02
#define LI_TOUCH_EVENT_MOVE 0x03
#define LI_TOUCH_EVENT_CANCEL 0x04
#define LI_TOUCH_EVENT_BUTTON_ONLY 0x05
#define LI_TOUCH_EVENT_HOVER_LEAVE 0x06
#define LI_TOUCH_EVENT_CANCEL_ALL 0x07
#define LI_ROT_UNKNOWN 0xFFFF
int LiSendTouchEvent(uint8_t eventType, uint32_t pointerId, float x, float y, float pressureOrDistance,
float contactAreaMajor, float contactAreaMinor, uint16_t rotation);
// This function is similar to LiSendTouchEvent() but allows additional parameters relevant for pen
// input, including tilt and buttons. Tilt is in degrees from vertical in Z dimension (perpendicular
// to screen, 0..90). See LiSendTouchEvent() for detailed documentation on other parameters.
//
// x, y, pressure, rotation, contact area, and tilt are ignored for LI_TOUCH_EVENT_BUTTON_ONLY events.
// If one of those changes, send LI_TOUCH_EVENT_MOVE or LI_TOUCH_EVENT_HOVER instead.
//
// To determine if LiSendPenEvent() is supported without calling it, call LiGetHostFeatureFlags()
// and check for the LI_FF_PEN_TOUCH_EVENTS flag.
#define LI_TOOL_TYPE_UNKNOWN 0x00
#define LI_TOOL_TYPE_PEN 0x01
#define LI_TOOL_TYPE_ERASER 0x02
#define LI_PEN_BUTTON_PRIMARY 0x01
#define LI_PEN_BUTTON_SECONDARY 0x02
#define LI_PEN_BUTTON_TERTIARY 0x04
#define LI_TILT_UNKNOWN 0xFF
int LiSendPenEvent(uint8_t eventType, uint8_t toolType, uint8_t penButtons,
float x, float y, float pressureOrDistance,
float contactAreaMajor, float contactAreaMinor,
uint16_t rotation, uint8_t tilt);
// This function queues a mouse button event to be sent to the remote server.
#define BUTTON_ACTION_PRESS 0x07
#define BUTTON_ACTION_RELEASE 0x08
#define BUTTON_LEFT 0x01
#define BUTTON_MIDDLE 0x02
#define BUTTON_RIGHT 0x03
#define BUTTON_X1 0x04
#define BUTTON_X2 0x05
int LiSendMouseButtonEvent(char action, int button);
// This function queues a keyboard event to be sent to the remote server.
// Key codes are Win32 Virtual Key (VK) codes and interpreted as keys on
// a US English layout.
#define KEY_ACTION_DOWN 0x03
#define KEY_ACTION_UP 0x04
#define MODIFIER_SHIFT 0x01
#define MODIFIER_CTRL 0x02
#define MODIFIER_ALT 0x04
#define MODIFIER_META 0x08
int LiSendKeyboardEvent(short keyCode, char keyAction, char modifiers);
// Similar to LiSendKeyboardEvent() but allows the client to inform the host that
// the keycode was not mapped to a standard US English scancode and should be
// interpreted as-is. This is a Sunshine protocol extension.
#define SS_KBE_FLAG_NON_NORMALIZED 0x01
int LiSendKeyboardEvent2(short keyCode, char keyAction, char modifiers, char flags);
// This function queues an UTF-8 encoded text to be sent to the remote server.
int LiSendUtf8TextEvent(const char *text, unsigned int length);
// Button flags
#define A_FLAG 0x1000
#define B_FLAG 0x2000
#define X_FLAG 0x4000
#define Y_FLAG 0x8000
#define UP_FLAG 0x0001
#define DOWN_FLAG 0x0002
#define LEFT_FLAG 0x0004
#define RIGHT_FLAG 0x0008
#define LB_FLAG 0x0100
#define RB_FLAG 0x0200
#define PLAY_FLAG 0x0010
#define BACK_FLAG 0x0020
#define LS_CLK_FLAG 0x0040
#define RS_CLK_FLAG 0x0080
#define SPECIAL_FLAG 0x0400
// Extended buttons (Sunshine only)
#define PADDLE1_FLAG 0x010000
#define PADDLE2_FLAG 0x020000
#define PADDLE3_FLAG 0x040000
#define PADDLE4_FLAG 0x080000
#define TOUCHPAD_FLAG 0x100000 // Touchpad buttons on Sony controllers
#define MISC_FLAG 0x200000 // Share/Mic/Capture/Mute buttons on various controllers
// This function queues a controller event to be sent to the remote server. It will
// be seen by the computer as the first controller.
int LiSendControllerEvent(int buttonFlags, unsigned char leftTrigger, unsigned char rightTrigger,
short leftStickX, short leftStickY, short rightStickX, short rightStickY);
// This function queues a controller event to be sent to the remote server. The controllerNumber
// parameter is a zero-based index of which controller this event corresponds to. The largest legal
// controller number is 3 for GFE hosts and 15 for Sunshine hosts. On generation 3 servers (GFE 2.1.x),
// these will be sent as controller 0 regardless of the controllerNumber parameter.
//
// The activeGamepadMask parameter is a bitfield with bits set for each controller present.
// On GFE, activeGamepadMask is limited to a maximum of 4 bits (0xF).
// On Sunshine, it is limited to 16 bits (0xFFFF).
//
// To indicate arrival of a gamepad, you may send an empty event with the controller number
// set to the new controller and the bit of the new controller set in the active gamepad mask.
// However, you should prefer LiSendControllerArrivalEvent() instead of this function for
// that purpose, because it allows the host to make a better choice of emulated controller.
//
// To indicate removal of a gamepad, send an empty event with the controller number set to the
// removed controller and the bit of the removed controller cleared in the active gamepad mask.
int LiSendMultiControllerEvent(short controllerNumber, short activeGamepadMask,
int buttonFlags, unsigned char leftTrigger, unsigned char rightTrigger,
short leftStickX, short leftStickY, short rightStickX, short rightStickY);
// This function provides a method of informing the host of the available buttons and capabilities
// on a new controller. This is the recommended approach for indicating the arrival of a new controller.
//
// This can allow the host to make better decisions about what type of controller to emulate and what
// capabilities to advertise to the OS on the virtual controller.
//
// If controller arrival events are unsupported by the host, this will fall back to indicating
// arrival via LiSendMultiControllerEvent().
#define LI_CTYPE_UNKNOWN 0x00
#define LI_CTYPE_XBOX 0x01
#define LI_CTYPE_PS 0x02
#define LI_CTYPE_NINTENDO 0x03
#define LI_CCAP_ANALOG_TRIGGERS 0x01 // Reports values between 0x00 and 0xFF for trigger axes
#define LI_CCAP_RUMBLE 0x02 // Can rumble in response to ConnListenerRumble() callback
#define LI_CCAP_TRIGGER_RUMBLE 0x04 // Can rumble triggers in response to ConnListenerRumbleTriggers() callback
#define LI_CCAP_TOUCHPAD 0x08 // Reports touchpad events via LiSendControllerTouchEvent()
#define LI_CCAP_ACCEL 0x10 // Can report accelerometer events via LiSendControllerMotionEvent()
#define LI_CCAP_GYRO 0x20 // Can report gyroscope events via LiSendControllerMotionEvent()
#define LI_CCAP_BATTERY_STATE 0x40 // Reports battery state via LiSendControllerBatteryEvent()
#define LI_CCAP_RGB_LED 0x80 // Can set RGB LED state via ConnListenerSetControllerLED()
int LiSendControllerArrivalEvent(uint8_t controllerNumber, uint16_t activeGamepadMask, uint8_t type,
uint32_t supportedButtonFlags, uint16_t capabilities);
// This function is similar to LiSendTouchEvent(), but the touch events are associated with a
// touchpad device present on a game controller instead of a touchscreen.
//
// If unsupported by the host, this will return LI_ERR_UNSUPPORTED and the caller should consider
// using this touch input to simulate trackpad input.
//
// To determine if LiSendControllerTouchEvent() is supported without calling it, call LiGetHostFeatureFlags()
// and check for the LI_FF_CONTROLLER_TOUCH_EVENTS flag.
int LiSendControllerTouchEvent(uint8_t controllerNumber, uint8_t eventType, uint32_t pointerId, float x, float y, float pressure);
// This function allows clients to send controller-associated motion events to a supported host.
//
// For power and performance reasons, motion sensors should not be enabled unless the host has
// explicitly asked for motion event reports via ConnListenerSetMotionEventState().
//
// LI_MOTION_TYPE_ACCEL should report data in m/s^2 (inclusive of gravitational acceleration).
// LI_MOTION_TYPE_GYRO should report data in deg/s.
//
// The x/y/z axis assignments follow SDL's convention documented here:
// https://github.com/libsdl-org/SDL/blob/96720f335002bef62115e39327940df454d78f6c/include/SDL3/SDL_sensor.h#L80-L124
#define LI_MOTION_TYPE_ACCEL 0x01
#define LI_MOTION_TYPE_GYRO 0x02
int LiSendControllerMotionEvent(uint8_t controllerNumber, uint8_t motionType, float x, float y, float z);
// This function allows clients to send controller battery state to a supported host. If the
// host can adjust battery state on the emulated controller, it can use this information to
// make the virtual controller match the physical controller on the client.
#define LI_BATTERY_STATE_UNKNOWN 0x00
#define LI_BATTERY_STATE_NOT_PRESENT 0x01
#define LI_BATTERY_STATE_DISCHARGING 0x02
#define LI_BATTERY_STATE_CHARGING 0x03
#define LI_BATTERY_STATE_NOT_CHARGING 0x04 // Connected to power but not charging
#define LI_BATTERY_STATE_FULL 0x05
#define LI_BATTERY_PERCENTAGE_UNKNOWN 0xFF
int LiSendControllerBatteryEvent(uint8_t controllerNumber, uint8_t batteryState, uint8_t batteryPercentage);
// This function queues a vertical scroll event to the remote server.
// The number of "clicks" is multiplied by WHEEL_DELTA (120) before
// being sent to the PC.
int LiSendScrollEvent(signed char scrollClicks);
// This function queues a vertical scroll event to the remote server.
// Unlike LiSendScrollEvent(), this function can send wheel events
// smaller than 120 units for devices that support "high resolution"
// scrolling (Apple Trackpads, Microsoft Precision Touchpads, etc.).
int LiSendHighResScrollEvent(short scrollAmount);
// These functions send horizontal scroll events to the host which are
// analogous to LiSendScrollEvent() and LiSendHighResScrollEvent().
// This is a Sunshine protocol extension.
int LiSendHScrollEvent(signed char scrollClicks);
int LiSendHighResHScrollEvent(short scrollAmount);
// This function returns a time in milliseconds with an implementation-defined epoch.
uint64_t LiGetMillis(void);
// This is a simplistic STUN function that can assist clients in getting the WAN address
// for machines they find using mDNS over IPv4. This can be used to pre-populate the external
// address for streaming after GFE stopped sending it a while back. wanAddr is returned in
// network byte order.
int LiFindExternalAddressIP4(const char* stunServer, unsigned short stunPort, unsigned int* wanAddr);
// Returns the number of queued video frames ready for delivery. Only relevant
// if CAPABILITY_DIRECT_SUBMIT is not set for the video renderer.
int LiGetPendingVideoFrames(void);
// Returns the number of queued audio frames ready for delivery. Only relevant
// if CAPABILITY_DIRECT_SUBMIT is not set for the audio renderer. For most uses,
// LiGetPendingAudioDuration() is probably a better option than this function.
int LiGetPendingAudioFrames(void);
// Similar to LiGetPendingAudioFrames() except it returns the pending audio in
// milliseconds rather than frames, which allows callers to be agnostic of the
// negotiated audio frame duration.
int LiGetPendingAudioDuration(void);
// Port index flags for use with LiGetPortFromPortFlagIndex() and LiGetProtocolFromPortFlagIndex()
#define ML_PORT_INDEX_TCP_47984 0
#define ML_PORT_INDEX_TCP_47989 1
#define ML_PORT_INDEX_TCP_48010 2
#define ML_PORT_INDEX_UDP_47998 8
#define ML_PORT_INDEX_UDP_47999 9
#define ML_PORT_INDEX_UDP_48000 10
#define ML_PORT_INDEX_UDP_48010 11
// Port flags for use with LiTestClientConnectivity()
#define ML_PORT_FLAG_ALL 0xFFFFFFFF
#define ML_PORT_FLAG_TCP_47984 0x0001
#define ML_PORT_FLAG_TCP_47989 0x0002
#define ML_PORT_FLAG_TCP_48010 0x0004
#define ML_PORT_FLAG_UDP_47998 0x0100
#define ML_PORT_FLAG_UDP_47999 0x0200
#define ML_PORT_FLAG_UDP_48000 0x0400
#define ML_PORT_FLAG_UDP_48010 0x0800
// Returns the port flags that correspond to ports involved in a failing connection stage, or
// connection termination error.
//
// These may be used to specifically test the ports that could have caused the connection failure.
// If no ports are likely involved with a given failure, this function returns 0.
unsigned int LiGetPortFlagsFromStage(int stage);
unsigned int LiGetPortFlagsFromTerminationErrorCode(int errorCode);
// Returns the IPPROTO_* value for the specified port index
int LiGetProtocolFromPortFlagIndex(int portFlagIndex);
// Returns the port number for the specified port index
unsigned short LiGetPortFromPortFlagIndex(int portFlagIndex);
// Populates the output buffer with a stringified list of the port flags set in the input argument.
// The second and subsequent entries will be prepended by 'separator' (if provided).
// If the output buffer is too small, the output will be truncated to fit the provided buffer.
void LiStringifyPortFlags(unsigned int portFlags, const char* separator, char* outputBuffer, int outputBufferLength);
// This function may be used to test if the local network is blocking Moonlight's ports. It requires
// a test server running on an Internet-reachable host. To perform a test, pass in the DNS hostname
// of the test server, a reference TCP port to ensure the test host is reachable at all (something
// very unlikely to blocked, like 80 or 443), and a set of ML_PORT_FLAG_* values corresponding to
// the ports you'd like to test. On return, it returns ML_TEST_RESULT_INCONCLUSIVE on catastrophic error,
// or the set of port flags that failed to validate. If all ports validate successfully, it returns 0.
//
// It's encouraged to not use the port flags explicitly (because GameStream ports may change in the future),
// but to instead use ML_PORT_FLAG_ALL or LiGetPortFlagsFromStage() on connection failure.
//
// The test server is available at https://github.com/cgutman/gfe-loopback
#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 LiWakeWaitForVideoFrame(void);
void LiCompleteVideoFrame(VIDEO_FRAME_HANDLE handle, int drStatus);
// This function returns the last reported HDR mode from the host PC.
// See ConnListenerSetHdrMode() for more details.
bool LiGetCurrentHostDisplayHdrMode(void);
typedef struct _SS_HDR_METADATA {
// RGB order
struct {
uint16_t x; // Normalized to 50,000
uint16_t y; // Normalized to 50,000
} displayPrimaries[3];
struct {
uint16_t x; // Normalized to 50,000
uint16_t y; // Normalized to 50,000
} whitePoint;
uint16_t maxDisplayLuminance; // Nits
uint16_t minDisplayLuminance; // 1/10000th of a nit
// These are content-specific values which may not be available for all hosts.
uint16_t maxContentLightLevel; // Nits
uint16_t maxFrameAverageLightLevel; // Nits
// These are display-specific values which may not be available for all hosts.
uint16_t maxFullFrameLuminance; // Nits
} SS_HDR_METADATA, *PSS_HDR_METADATA;
// This function populates the provided mastering metadata struct with the HDR metadata
// from the host PC's monitor and content (if available). It is only valid to call this
// function when HDR mode is active on the host. This is a Sunshine protocol extension.
bool LiGetHdrMetadata(PSS_HDR_METADATA metadata);
// This function requests an IDR frame from the host. Typically this is done using DR_NEED_IDR, but clients
// processing frames asynchronously may need to reset their decoder state even after returning DR_OK for
// the prior frame. Rather than wait for a new frame and return DR_NEED_IDR for that one, they can just
// call this API instead. Note that this function does not guarantee that the *next* frame will be an IDR
// frame, just that an IDR frame will arrive soon.
void LiRequestIdrFrame(void);
// This function returns any extended feature flags supported by the host.
#define LI_FF_PEN_TOUCH_EVENTS 0x01 // LiSendTouchEvent()/LiSendPenEvent() supported
#define LI_FF_CONTROLLER_TOUCH_EVENTS 0x02 // LiSendControllerTouchEvent() supported
uint32_t LiGetHostFeatureFlags(void);
#ifdef __cplusplus
}
#endif