88#include < stdlib.h>
99
1010static const int32_t XCWMaximumEncodedDimension = 1920 ;
11+ static const int32_t XCWMaximumRealtimeHardwareEncodedDimension = 1600 ;
1112static const int32_t XCWMaximumSoftwareEncodedDimension = 1600 ;
1213static const int32_t XCWMaximumLowLatencySoftwareEncodedDimension = 1170 ;
1314static const int32_t XCWTargetRealTimeFrameRate = 60 ;
1415static const int32_t XCWTargetSoftwareFrameRate = 60 ;
1516static const int32_t XCWTargetLowLatencySoftwareFrameRate = 15 ;
1617static const NSUInteger XCWMaximumInFlightFrames = 2 ;
1718static const int32_t XCWMinimumAverageBitRate = 18000000 ;
19+ static const int32_t XCWMinimumRealtimeAverageBitRate = 5000000 ;
1820static const int32_t XCWMinimumSoftwareAverageBitRate = 3000000 ;
1921static const int32_t XCWMinimumLowLatencySoftwareAverageBitRate = 2000000 ;
2022static const int64_t XCWBitsPerPixelBudget = 10 ;
23+ static const int64_t XCWRealtimeBitsPerPixelBudget = 5 ;
2124static const int64_t XCWSoftwareBitsPerPixelBudget = 6 ;
2225static const int64_t XCWLowLatencySoftwareBitsPerPixelBudget = 3 ;
2326static const uint64_t XCWSoftwareMinimumFrameIntervalUs = 16667 ;
@@ -57,6 +60,15 @@ static BOOL XCWLowLatencyModeFromEnvironment(void) {
5760 [value isEqualToString: @" on" ];
5861}
5962
63+ static BOOL XCWRealtimeStreamModeFromEnvironment (void ) {
64+ const char *rawValue = getenv (" SIMDECK_REALTIME_STREAM" );
65+ NSString *value = rawValue != NULL ? [[[NSString alloc ] initWithUTF8String: rawValue] lowercaseString ] : @" " ;
66+ return [value isEqualToString: @" 1" ] ||
67+ [value isEqualToString: @" true" ] ||
68+ [value isEqualToString: @" yes" ] ||
69+ [value isEqualToString: @" on" ];
70+ }
71+
6072static CMVideoCodecType XCWVideoCodecTypeForMode (XCWVideoEncoderMode mode) {
6173 switch (mode) {
6274 case XCWVideoEncoderModeH264Hardware:
@@ -206,13 +218,15 @@ static int32_t XCWRoundToEvenDimension(double value) {
206218 return rounded;
207219}
208220
209- static CGSize XCWScaledDimensionsForSourceSize (int32_t width, int32_t height, XCWVideoEncoderMode mode, BOOL lowLatencyMode) {
221+ static CGSize XCWScaledDimensionsForSourceSize (int32_t width, int32_t height, XCWVideoEncoderMode mode, BOOL lowLatencyMode, BOOL realtimeStreamMode ) {
210222 if (width <= 0 || height <= 0 ) {
211223 return CGSizeZero;
212224 }
213225
214226 int32_t maximumDimension = XCWMaximumEncodedDimension;
215- if (mode == XCWVideoEncoderModeH264Software) {
227+ if (mode == XCWVideoEncoderModeH264Hardware && realtimeStreamMode) {
228+ maximumDimension = XCWMaximumRealtimeHardwareEncodedDimension;
229+ } else if (mode == XCWVideoEncoderModeH264Software) {
216230 maximumDimension = lowLatencyMode
217231 ? XCWMaximumLowLatencySoftwareEncodedDimension
218232 : XCWMaximumSoftwareEncodedDimension;
@@ -227,10 +241,13 @@ static CGSize XCWScaledDimensionsForSourceSize(int32_t width, int32_t height, XC
227241 XCWRoundToEvenDimension (height * scale));
228242}
229243
230- static int32_t XCWAverageBitRateForDimensions (int32_t width, int32_t height, XCWVideoEncoderMode mode, BOOL lowLatencyMode) {
244+ static int32_t XCWAverageBitRateForDimensions (int32_t width, int32_t height, XCWVideoEncoderMode mode, BOOL lowLatencyMode, BOOL realtimeStreamMode ) {
231245 int64_t bitsPerPixelBudget = XCWBitsPerPixelBudget;
232246 int64_t minimumAverageBitRate = XCWMinimumAverageBitRate;
233- if (mode == XCWVideoEncoderModeH264Software) {
247+ if (mode == XCWVideoEncoderModeH264Hardware && realtimeStreamMode) {
248+ bitsPerPixelBudget = XCWRealtimeBitsPerPixelBudget;
249+ minimumAverageBitRate = XCWMinimumRealtimeAverageBitRate;
250+ } else if (mode == XCWVideoEncoderModeH264Software) {
234251 bitsPerPixelBudget = lowLatencyMode
235252 ? XCWLowLatencySoftwareBitsPerPixelBudget
236253 : XCWSoftwareBitsPerPixelBudget;
@@ -352,6 +369,7 @@ @implementation XCWH264Encoder {
352369 OSType _scaledPixelFormat;
353370 XCWVideoEncoderMode _encoderMode;
354371 BOOL _lowLatencyMode;
372+ BOOL _realtimeStreamMode;
355373 CMVideoCodecType _codecType;
356374 BOOL _hardwareAccelerated;
357375 NSUInteger _inputFrameCount;
@@ -386,6 +404,7 @@ - (instancetype)initWithOutputHandler:(XCWH264EncoderOutputHandler)outputHandler
386404 _needsKeyFrame = YES ;
387405 _encoderMode = XCWVideoEncoderModeFromEnvironment ();
388406 _lowLatencyMode = (_encoderMode == XCWVideoEncoderModeH264Software) && XCWLowLatencyModeFromEnvironment ();
407+ _realtimeStreamMode = XCWRealtimeStreamModeFromEnvironment () || _lowLatencyMode;
389408 _codecType = XCWVideoCodecTypeForMode (_encoderMode);
390409 _softwareFrameIntervalUs = [self initialSoftwareFrameIntervalUsLocked ];
391410 return self;
@@ -456,6 +475,7 @@ - (NSDictionary *)statsRepresentation {
456475 @" transportCodec" : XCWCodecName (self->_codecType ),
457476 @" encoderMode" : XCWVideoEncoderModeName (self->_encoderMode ),
458477 @" lowLatencyMode" : @(self->_lowLatencyMode ),
478+ @" realtimeStreamMode" : @(self->_realtimeStreamMode ),
459479 @" encoderId" : XCWVideoEncoderIDForMode (self->_encoderMode ) ?: @" automatic" ,
460480 @" hardwareAccelerated" : @(self->_hardwareAccelerated ),
461481 @" lastSessionStatus" : @(self->_lastSessionStatus ),
@@ -483,7 +503,7 @@ - (void)invalidate {
483503}
484504
485505- (NSUInteger )maximumInFlightFrameCountLocked {
486- return (_encoderMode == XCWVideoEncoderModeH264Software && _lowLatencyMode) ? 1 : XCWMaximumInFlightFrames;
506+ return (_realtimeStreamMode || ( _encoderMode == XCWVideoEncoderModeH264Software && _lowLatencyMode) ) ? 1 : XCWMaximumInFlightFrames;
487507}
488508
489509- (uint64_t )minimumSoftwareFrameIntervalUsLocked {
@@ -600,7 +620,7 @@ - (BOOL)encodePixelBufferLocked:(CVPixelBufferRef)pixelBuffer {
600620 return NO ;
601621 }
602622
603- CGSize targetSize = XCWScaledDimensionsForSourceSize (sourceWidth, sourceHeight, _encoderMode, _lowLatencyMode);
623+ CGSize targetSize = XCWScaledDimensionsForSourceSize (sourceWidth, sourceHeight, _encoderMode, _lowLatencyMode, _realtimeStreamMode );
604624 int32_t targetWidth = (int32_t )targetSize.width ;
605625 int32_t targetHeight = (int32_t )targetSize.height ;
606626 if (targetWidth <= 0 || targetHeight <= 0 ) {
@@ -708,7 +728,7 @@ - (BOOL)ensureCompressionSessionWithWidth:(int32_t)width height:(int32_t)height
708728 _needsKeyFrame = YES ;
709729
710730 int expectedFrameRate = [self expectedFrameRateLocked ];
711- int averageBitRate = XCWAverageBitRateForDimensions (width, height, _encoderMode, _lowLatencyMode);
731+ int averageBitRate = XCWAverageBitRateForDimensions (width, height, _encoderMode, _lowLatencyMode, _realtimeStreamMode );
712732
713733 VTSessionSetProperty (session, kVTCompressionPropertyKey_RealTime , kCFBooleanTrue );
714734 if (@available (macOS 10.14 , *)) {
@@ -733,8 +753,9 @@ - (BOOL)ensureCompressionSessionWithWidth:(int32_t)width height:(int32_t)height
733753 VTSessionSetProperty (session, kVTCompressionPropertyKey_ProfileLevel , kVTProfileLevel_H264_High_AutoLevel );
734754 }
735755 VTSessionSetProperty (session, kVTCompressionPropertyKey_ExpectedFrameRate , (__bridge CFTypeRef)@(expectedFrameRate));
736- VTSessionSetProperty (session, kVTCompressionPropertyKey_MaxKeyFrameInterval , (__bridge CFTypeRef)@(_lowLatencyMode ? expectedFrameRate : expectedFrameRate * 2 ));
737- VTSessionSetProperty (session, kVTCompressionPropertyKey_MaxKeyFrameIntervalDuration , (__bridge CFTypeRef)@(_lowLatencyMode ? 1.0 : 2.0 ));
756+ BOOL shortKeyframeInterval = _lowLatencyMode || _realtimeStreamMode;
757+ VTSessionSetProperty (session, kVTCompressionPropertyKey_MaxKeyFrameInterval , (__bridge CFTypeRef)@(shortKeyframeInterval ? expectedFrameRate : expectedFrameRate * 2 ));
758+ VTSessionSetProperty (session, kVTCompressionPropertyKey_MaxKeyFrameIntervalDuration , (__bridge CFTypeRef)@(shortKeyframeInterval ? 1.0 : 2.0 ));
738759 VTSessionSetProperty (session, kVTCompressionPropertyKey_AverageBitRate , (__bridge CFTypeRef)@(averageBitRate));
739760 if (@available (macOS 11.0 , *)) {
740761 VTSessionSetProperty (session,
0 commit comments