Skip to content

Commit aea6218

Browse files
committed
Tune hardware streaming path
1 parent c340112 commit aea6218

6 files changed

Lines changed: 375 additions & 37 deletions

File tree

cli/XCWH264Encoder.m

Lines changed: 54 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -311,11 +311,9 @@ static CGSize XCWScaledDimensionsForSourceSize(int32_t width, int32_t height, XC
311311

312312
int32_t maximumDimension = realtimeStreamMode
313313
? XCWRealtimeMaximumEncodedDimension()
314-
: XCWMaximumEncodedDimension;
314+
: XCWMaximumSoftwareEncodedDimension;
315315
if (mode == XCWVideoEncoderModeH264Software && lowLatencyMode) {
316316
maximumDimension = MIN(maximumDimension, XCWMaximumLowLatencySoftwareEncodedDimension);
317-
} else if (mode == XCWVideoEncoderModeH264Software && !realtimeStreamMode) {
318-
maximumDimension = XCWMaximumSoftwareEncodedDimension;
319317
}
320318
int32_t longestEdge = MAX(width, height);
321319
if (longestEdge <= maximumDimension) {
@@ -333,7 +331,7 @@ static int32_t XCWAverageBitRateForDimensions(int32_t width, int32_t height, XCW
333331
if (realtimeStreamMode && !lowLatencyMode) {
334332
bitsPerPixelBudget = XCWRealtimeBitsPerPixelBudgetValue();
335333
minimumAverageBitRate = XCWRealtimeMinimumAverageBitRate();
336-
} else if (mode == XCWVideoEncoderModeH264Software) {
334+
} else if (mode == XCWVideoEncoderModeH264Software || !realtimeStreamMode) {
337335
bitsPerPixelBudget = lowLatencyMode
338336
? XCWLowLatencySoftwareBitsPerPixelBudget
339337
: XCWSoftwareBitsPerPixelBudget;
@@ -411,6 +409,28 @@ static BOOL XCWCompressionSessionUsesHardwareEncoder(VTCompressionSessionRef ses
411409
return isHardware;
412410
}
413411

412+
static NSString *XCWCompressionSessionEncoderID(VTCompressionSessionRef session) {
413+
if (session == NULL) {
414+
return nil;
415+
}
416+
417+
CFTypeRef value = NULL;
418+
OSStatus status = VTSessionCopyProperty(session,
419+
kVTCompressionPropertyKey_EncoderID,
420+
kCFAllocatorDefault,
421+
&value);
422+
if (status != noErr || value == NULL) {
423+
return nil;
424+
}
425+
426+
NSString *encoderID = nil;
427+
if (CFGetTypeID(value) == CFStringGetTypeID()) {
428+
encoderID = [(__bridge NSString *)value copy];
429+
}
430+
CFRelease(value);
431+
return encoderID;
432+
}
433+
414434
static BOOL XCWPixelFormatSupportsSoftwareScaling(OSType pixelFormat) {
415435
switch (pixelFormat) {
416436
case kCVPixelFormatType_32ARGB:
@@ -474,6 +494,7 @@ @implementation XCWH264Encoder {
474494
uint64_t _lastRealtimeHardwareSubmissionUs;
475495
NSUInteger _realtimeHardwarePacedFrameCount;
476496
NSUInteger _realtimeHardwareHealthyFrameCount;
497+
NSString *_selectedEncoderID;
477498
NSInteger _lastSessionStatus;
478499
NSInteger _lastPrepareStatus;
479500
NSInteger _lastScaleStatus;
@@ -497,7 +518,7 @@ - (instancetype)initWithOutputHandler:(XCWH264EncoderOutputHandler)outputHandler
497518
_realtimeStreamMode = XCWRealtimeStreamModeFromEnvironment() || _lowLatencyMode;
498519
_codecType = XCWVideoCodecTypeForMode(_encoderMode);
499520
_softwareFrameIntervalUs = [self initialSoftwareFrameIntervalUsLocked];
500-
_realtimeHardwareFrameIntervalUs = XCWRealtimeFrameIntervalUs();
521+
_realtimeHardwareFrameIntervalUs = [self initialHardwareFrameIntervalUsLocked];
501522
return self;
502523
}
503524

@@ -547,7 +568,7 @@ - (void)reconfigureForStreamQualityChange {
547568
self->_softwareFrameIntervalUs = [self initialSoftwareFrameIntervalUsLocked];
548569
self->_softwarePacedFrameCount = 0;
549570
self->_softwareHealthyFrameCount = 0;
550-
self->_realtimeHardwareFrameIntervalUs = XCWRealtimeFrameIntervalUs();
571+
self->_realtimeHardwareFrameIntervalUs = [self initialHardwareFrameIntervalUsLocked];
551572
self->_realtimeHardwarePacedFrameCount = 0;
552573
self->_realtimeHardwareHealthyFrameCount = 0;
553574
});
@@ -584,6 +605,7 @@ - (NSDictionary *)statsRepresentation {
584605
@"lowLatencyMode": @(self->_lowLatencyMode),
585606
@"realtimeStreamMode": @(self->_realtimeStreamMode),
586607
@"encoderId": XCWVideoEncoderIDForMode(self->_encoderMode) ?: @"automatic",
608+
@"selectedEncoderId": self->_selectedEncoderID ?: NSNull.null,
587609
@"hardwareAccelerated": @(self->_hardwareAccelerated),
588610
@"lastSessionStatus": @(self->_lastSessionStatus),
589611
@"lastPrepareStatus": @(self->_lastPrepareStatus),
@@ -642,6 +664,18 @@ - (NSUInteger)softwareHealthyFrameWindowLocked {
642664
return _lowLatencyMode ? XCWLowLatencySoftwareHealthyFrameWindow : XCWSoftwareHealthyFrameWindow;
643665
}
644666

667+
- (uint64_t)minimumHardwareFrameIntervalUsLocked {
668+
return _realtimeStreamMode ? XCWRealtimeFrameIntervalUs() : XCWSoftwareMinimumFrameIntervalUs;
669+
}
670+
671+
- (uint64_t)initialHardwareFrameIntervalUsLocked {
672+
return _realtimeStreamMode ? XCWRealtimeFrameIntervalUs() : XCWSoftwareInitialFrameIntervalUs;
673+
}
674+
675+
- (uint64_t)maximumHardwareFrameIntervalUsLocked {
676+
return _realtimeStreamMode ? XCWRealtimeMaximumFrameIntervalUs() : XCWSoftwareMaximumFrameIntervalUs;
677+
}
678+
645679
- (int32_t)expectedFrameRateLocked {
646680
if (_encoderMode == XCWVideoEncoderModeH264Software) {
647681
if (_lowLatencyMode) {
@@ -660,7 +694,7 @@ - (BOOL)shouldPaceRealtimeHardwareFrameAtTimeUs:(uint64_t)nowUs {
660694
return NO;
661695
}
662696
if (_realtimeHardwareFrameIntervalUs == 0) {
663-
_realtimeHardwareFrameIntervalUs = XCWRealtimeFrameIntervalUs();
697+
_realtimeHardwareFrameIntervalUs = [self initialHardwareFrameIntervalUsLocked];
664698
}
665699
if (_lastRealtimeHardwareSubmissionUs == 0) {
666700
return NO;
@@ -735,11 +769,11 @@ - (void)adaptRealtimeHardwarePacingForLatencyUs:(uint64_t)latencyUs {
735769
return;
736770
}
737771
if (_realtimeHardwareFrameIntervalUs == 0) {
738-
_realtimeHardwareFrameIntervalUs = XCWRealtimeFrameIntervalUs();
772+
_realtimeHardwareFrameIntervalUs = [self initialHardwareFrameIntervalUsLocked];
739773
}
740774

741-
uint64_t minimumIntervalUs = XCWRealtimeFrameIntervalUs();
742-
uint64_t maximumIntervalUs = XCWRealtimeMaximumFrameIntervalUs();
775+
uint64_t minimumIntervalUs = [self minimumHardwareFrameIntervalUsLocked];
776+
uint64_t maximumIntervalUs = [self maximumHardwareFrameIntervalUsLocked];
743777
if (latencyUs > _realtimeHardwareFrameIntervalUs) {
744778
uint64_t nextIntervalUs = _realtimeHardwareFrameIntervalUs + XCWRealtimeHardwareFrameIntervalStepUs;
745779
uint64_t latencyBoundIntervalUs = latencyUs + XCWRealtimeHardwareFrameIntervalStepUs;
@@ -855,7 +889,7 @@ - (BOOL)encodePixelBufferLocked:(CVPixelBufferRef)pixelBuffer {
855889
_lastRealtimeHardwareSubmissionUs = nowUs;
856890
}
857891
_maxInFlightFrameCount = MAX(_maxInFlightFrameCount, _inFlightFrameCount);
858-
if (_encoderMode == XCWVideoEncoderModeH264Software) {
892+
if (_encoderMode == XCWVideoEncoderModeH264Software || !_realtimeStreamMode) {
859893
VTCompressionSessionCompleteFrames(_compressionSession, presentationTime);
860894
}
861895
return YES;
@@ -873,7 +907,7 @@ - (BOOL)ensureCompressionSessionWithWidth:(int32_t)width height:(int32_t)height
873907
if (encoderID.length > 0) {
874908
encoderSpecification[(__bridge NSString *)kVTVideoEncoderSpecification_EncoderID] = encoderID;
875909
}
876-
if (_encoderMode != XCWVideoEncoderModeH264Software) {
910+
if (_encoderMode != XCWVideoEncoderModeH264Software && _realtimeStreamMode) {
877911
if (@available(macOS 11.3, *)) {
878912
encoderSpecification[(__bridge NSString *)kVTVideoEncoderSpecification_EnableLowLatencyRateControl] = @YES;
879913
}
@@ -919,18 +953,14 @@ - (BOOL)ensureCompressionSessionWithWidth:(int32_t)width height:(int32_t)height
919953
if (@available(macOS 10.14, *)) {
920954
VTSessionSetProperty(session, kVTCompressionPropertyKey_AllowOpenGOP, kCFBooleanFalse);
921955
}
922-
if (_encoderMode == XCWVideoEncoderModeH264Software || _realtimeStreamMode) {
923-
if (@available(macOS 12.0, *)) {
924-
VTSessionSetProperty(session,
925-
kVTCompressionPropertyKey_ProfileLevel,
926-
kVTProfileLevel_H264_ConstrainedBaseline_AutoLevel);
927-
} else {
928-
VTSessionSetProperty(session, kVTCompressionPropertyKey_ProfileLevel, kVTProfileLevel_H264_Baseline_AutoLevel);
929-
}
930-
VTSessionSetProperty(session, kVTCompressionPropertyKey_H264EntropyMode, kVTH264EntropyMode_CAVLC);
956+
if (@available(macOS 12.0, *)) {
957+
VTSessionSetProperty(session,
958+
kVTCompressionPropertyKey_ProfileLevel,
959+
kVTProfileLevel_H264_ConstrainedBaseline_AutoLevel);
931960
} else {
932-
VTSessionSetProperty(session, kVTCompressionPropertyKey_ProfileLevel, kVTProfileLevel_H264_High_AutoLevel);
961+
VTSessionSetProperty(session, kVTCompressionPropertyKey_ProfileLevel, kVTProfileLevel_H264_Baseline_AutoLevel);
933962
}
963+
VTSessionSetProperty(session, kVTCompressionPropertyKey_H264EntropyMode, kVTH264EntropyMode_CAVLC);
934964
VTSessionSetProperty(session, kVTCompressionPropertyKey_ExpectedFrameRate, (__bridge CFTypeRef)@(expectedFrameRate));
935965
BOOL shortKeyframeInterval = _lowLatencyMode || _realtimeStreamMode;
936966
VTSessionSetProperty(session, kVTCompressionPropertyKey_MaxKeyFrameInterval, (__bridge CFTypeRef)@(shortKeyframeInterval ? MAX(1, expectedFrameRate / 2) : expectedFrameRate * 2));
@@ -964,6 +994,7 @@ - (BOOL)ensureCompressionSessionWithWidth:(int32_t)width height:(int32_t)height
964994
return NO;
965995
}
966996
_hardwareAccelerated = XCWCompressionSessionUsesHardwareEncoder(session);
997+
_selectedEncoderID = XCWCompressionSessionEncoderID(session);
967998

968999
return YES;
9691000
}
@@ -984,6 +1015,7 @@ - (void)invalidateCompressionSessionLocked {
9841015
_lastSoftwareSubmissionUs = 0;
9851016
_lastRealtimeHardwareSubmissionUs = 0;
9861017
_hardwareAccelerated = NO;
1018+
_selectedEncoderID = nil;
9871019
[self invalidateScalingResourcesLocked];
9881020
}
9891021

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@
6767
"test:integration:js-api:verbose": "SIMDECK_INTEGRATION_SHOW_SIMULATOR=1 node scripts/integration/js-api.mjs",
6868
"test:studio-provider": "node --test scripts/studio-provider-bridge.test.mjs",
6969
"test:stress": "node scripts/stress/simdeck.mjs",
70+
"bench:encoder:build": "scripts/bench/build-encoder-benchmark.sh",
7071
"ci": "npm run lint && npm run build:all && npm run test && npm run package:vscode-extension",
7172
"dev": "npm run build:cli && node scripts/dev.mjs",
7273
"preview:swiftui": "node scripts/experimental/swiftui-preview.mjs",
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
#!/bin/sh
2+
set -eu
3+
4+
ROOT_DIR="$(CDPATH= cd -- "$(dirname -- "$0")/../.." && pwd)"
5+
mkdir -p "$ROOT_DIR/build/bench"
6+
7+
clang \
8+
-fobjc-arc \
9+
-fmodules \
10+
-I"$ROOT_DIR/cli" \
11+
"$ROOT_DIR/scripts/bench/encoder-benchmark.m" \
12+
"$ROOT_DIR/cli/XCWH264Encoder.m" \
13+
-framework Foundation \
14+
-framework CoreVideo \
15+
-framework CoreMedia \
16+
-framework VideoToolbox \
17+
-framework QuartzCore \
18+
-framework Accelerate \
19+
-o "$ROOT_DIR/build/bench/encoder-benchmark"
20+
21+
printf '%s\n' "$ROOT_DIR/build/bench/encoder-benchmark"

0 commit comments

Comments
 (0)