@@ -54,14 +54,42 @@ - (void)frameWasEncoded : (OSStatus)status flags : (VTEncodeInfoFlags)infoFlags
54
54
// The ratio between kVTCompressionPropertyKey_DataRateLimits and
55
55
// kVTCompressionPropertyKey_AverageBitRate. The data rate limit is set higher
56
56
// than the average bit rate to avoid undershooting the target.
57
- const float kLimitToAverageBitRateFactor = 1 . 5f ;
57
+ const float kLimitToAverageBitRateFactor = 10 . 0f ;
58
58
// These thresholds deviate from the default h264 QP thresholds, as they
59
59
// have been found to work better on devices that support VideoToolbox
60
60
const int kLowH264QpThreshold = 28 ;
61
61
const int kHighH264QpThreshold = 39 ;
62
+ const int kBitsPerByte = 8 ;
62
63
63
64
const OSType kNV12PixelFormat = kCVPixelFormatType_420YpCbCr8BiPlanarFullRange ;
64
65
66
+ typedef NS_ENUM (NSInteger , RTCVideoEncodeMode) {
67
+ Variable = 0 ,
68
+ Constant = 1 ,
69
+ };
70
+
71
+ NSArray *CreateRateLimitArray (uint32_t computedBitrateBps, RTCVideoEncodeMode mode) {
72
+ switch (mode) {
73
+ case Variable: {
74
+ // 5 seconds should be an okay interval for VBR to enforce the long-term
75
+ // limit.
76
+ float avgInterval = 5.0 ;
77
+ uint32_t avgBytesPerSecond = computedBitrateBps / kBitsPerByte * avgInterval;
78
+ // And the peak bitrate is measured per-second in a way similar to CBR.
79
+ float peakInterval = 1.0 ;
80
+ uint32_t peakBytesPerSecond =
81
+ computedBitrateBps * kLimitToAverageBitRateFactor / kBitsPerByte ;
82
+ return @[ @(peakBytesPerSecond), @(peakInterval), @(avgBytesPerSecond), @(avgInterval) ];
83
+ }
84
+ case Constant: {
85
+ // CBR should be enforces with granularity of a second.
86
+ float targetInterval = 1.0 ;
87
+ int32_t targetBitrate = computedBitrateBps / kBitsPerByte ;
88
+ return @[ @(targetBitrate), @(targetInterval) ];
89
+ }
90
+ }
91
+ }
92
+
65
93
// Struct that we pass to the encoder per frame to encode. We receive it again
66
94
// in the encoder callback.
67
95
struct RTCFrameEncodeParams {
@@ -177,9 +205,9 @@ CFStringRef ExtractProfile(const webrtc::H264ProfileLevelId &profile_level_id, b
177
205
switch (profile_level_id.profile ) {
178
206
case webrtc::H264Profile::kProfileConstrainedBaseline :
179
207
case webrtc::H264Profile::kProfileBaseline :
180
- if (screenSharing) {
181
- return kVTProfileLevel_H264_Baseline_AutoLevel ;
182
- }
208
+ if (screenSharing) {
209
+ return kVTProfileLevel_H264_Baseline_AutoLevel ;
210
+ }
183
211
switch (profile_level_id.level ) {
184
212
case webrtc::H264Level::kLevel3 :
185
213
return kVTProfileLevel_H264_Baseline_3_0 ;
@@ -315,8 +343,8 @@ NSUInteger GetMaxSampleRate(const webrtc::H264ProfileLevelId &profile_level_id)
315
343
316
344
@implementation RTC_OBJC_TYPE (RTCVideoEncoderH264) {
317
345
RTC_OBJC_TYPE (RTCVideoCodecInfo) * _codecInfo;
318
- std::unique_ptr<webrtc::BitrateAdjuster> _bitrateAdjuster;
319
346
uint32_t _targetBitrateBps;
347
+ uint32_t _targetFrameRate;
320
348
uint32_t _encoderBitrateBps;
321
349
uint32_t _encoderFrameRate;
322
350
uint32_t _maxAllowedFrameRate;
@@ -327,10 +355,16 @@ @implementation RTC_OBJC_TYPE (RTCVideoEncoderH264) {
327
355
int32_t _height;
328
356
VTCompressionSessionRef _compressionSession;
329
357
CVPixelBufferPoolRef _pixelBufferPool;
330
- RTCVideoCodecMode _mode;
358
+ RTCVideoCodecMode _codecMode;
359
+ unsigned int _maxQP;
360
+ unsigned int _minBitrate;
361
+ unsigned int _maxBitrate;
362
+ RTCVideoEncodeMode _encodeMode;
331
363
332
364
webrtc::H264BitstreamParser _h264BitstreamParser;
333
365
std::vector<uint8_t > _frameScaleBuffer;
366
+
367
+ CMTime _previousPresentationTimeStamp;
334
368
}
335
369
336
370
// .5 is set as a mininum to prevent overcompensating for large temporary
@@ -343,12 +377,14 @@ @implementation RTC_OBJC_TYPE (RTCVideoEncoderH264) {
343
377
- (instancetype )initWithCodecInfo : (RTC_OBJC_TYPE(RTCVideoCodecInfo) *)codecInfo {
344
378
if (self = [super init ]) {
345
379
_codecInfo = codecInfo;
346
- _bitrateAdjuster.reset (new webrtc::BitrateAdjuster (.5 , .95 ));
347
380
_packetizationMode = RTCH264PacketizationModeNonInterleaved;
348
381
_profile_level_id =
349
382
webrtc::ParseSdpForH264ProfileLevelId ([codecInfo nativeSdpVideoFormat ].parameters );
383
+ _previousPresentationTimeStamp = kCMTimeZero ;
350
384
RTC_DCHECK (_profile_level_id);
351
- RTC_LOG (LS_INFO) << " Using profile " << CFStringToString (ExtractProfile (*_profile_level_id, _mode == RTCVideoCodecModeScreensharing));
385
+ RTC_LOG (LS_INFO) << " Using profile "
386
+ << CFStringToString (ExtractProfile (
387
+ *_profile_level_id, _codecMode == RTCVideoCodecModeScreensharing));
352
388
RTC_CHECK ([codecInfo.name isEqualToString: kRTCVideoCodecH264Name ]);
353
389
}
354
390
return self;
@@ -365,17 +401,28 @@ - (NSInteger)startEncodeWithSettings:(RTC_OBJC_TYPE(RTCVideoEncoderSettings) *)s
365
401
366
402
_width = settings.width ;
367
403
_height = settings.height ;
368
- _mode = settings.mode ;
404
+ _codecMode = settings.mode ;
405
+ _maxQP = settings.qpMax ;
406
+
407
+ _encodeMode = Variable; // Always variable mode for now
408
+ _minBitrate = settings.minBitrate * 1000 ; // minBitrate is in kbps.
409
+ _maxBitrate = settings.maxBitrate * 1000 ; // maxBitrate is in kbps.
369
410
370
411
uint32_t aligned_width = (((_width + 15 ) >> 4 ) << 4 );
371
412
uint32_t aligned_height = (((_height + 15 ) >> 4 ) << 4 );
372
413
_maxAllowedFrameRate = static_cast <uint32_t >(GetMaxSampleRate (*_profile_level_id) /
373
414
(aligned_width * aligned_height));
374
415
375
416
// We can only set average bitrate on the HW encoder.
376
- _targetBitrateBps = settings.startBitrate * 1000 ; // startBitrate is in kbps.
377
- _bitrateAdjuster->SetTargetBitrateBps (_targetBitrateBps);
378
- _encoderFrameRate = MIN (settings.maxFramerate , _maxAllowedFrameRate);
417
+ if (_encodeMode == Constant) {
418
+ _targetBitrateBps = _maxBitrate;
419
+ } else {
420
+ _targetBitrateBps = settings.startBitrate * 1000 ; // startBitrate is in kbps.
421
+ }
422
+
423
+ _targetFrameRate = MIN (settings.maxFramerate , _maxAllowedFrameRate);
424
+ _encoderBitrateBps = 0 ;
425
+ _encoderFrameRate = 0 ;
379
426
if (settings.maxFramerate > _maxAllowedFrameRate && _maxAllowedFrameRate > 0 ) {
380
427
RTC_LOG (LS_WARNING) << " Initial encoder frame rate setting " << settings.maxFramerate
381
428
<< " is larger than the "
@@ -396,8 +443,15 @@ - (NSInteger)encode:(RTC_OBJC_TYPE(RTCVideoFrame) *)frame
396
443
if (!_callback || !_compressionSession) {
397
444
return WEBRTC_VIDEO_CODEC_UNINITIALIZED;
398
445
}
399
- BOOL isKeyframeRequired = NO ;
400
446
447
+ CMTime presentationTimeStamp = CMTimeMake (frame.timeStampNs / rtc::kNumNanosecsPerMillisec , 1000 );
448
+ if (CMTimeCompare (presentationTimeStamp, _previousPresentationTimeStamp) == 0 ) {
449
+ // Same PTS
450
+ return WEBRTC_VIDEO_CODEC_NO_OUTPUT;
451
+ }
452
+ _previousPresentationTimeStamp = presentationTimeStamp;
453
+
454
+ BOOL isKeyframeRequired = NO ;
401
455
// Get a pixel buffer from the pool and copy frame data over.
402
456
if ([self resetCompressionSessionIfNeededWithFrame: frame]) {
403
457
isKeyframeRequired = YES ;
@@ -424,8 +478,8 @@ - (NSInteger)encode:(RTC_OBJC_TYPE(RTCVideoFrame) *)frame
424
478
int dstWidth = CVPixelBufferGetWidth (pixelBuffer);
425
479
int dstHeight = CVPixelBufferGetHeight (pixelBuffer);
426
480
if ([rtcPixelBuffer requiresScalingToWidth: dstWidth height: dstHeight]) {
427
- int size =
428
- [rtcPixelBuffer bufferSizeForCroppingAndScalingToWidth: dstWidth height: dstHeight];
481
+ int size = [rtcPixelBuffer bufferSizeForCroppingAndScalingToWidth: dstWidth
482
+ height: dstHeight];
429
483
_frameScaleBuffer.resize (size);
430
484
} else {
431
485
_frameScaleBuffer.clear ();
@@ -462,7 +516,6 @@ - (NSInteger)encode:(RTC_OBJC_TYPE(RTCVideoFrame) *)frame
462
516
}
463
517
}
464
518
465
- CMTime presentationTimeStamp = CMTimeMake (frame.timeStampNs / rtc::kNumNanosecsPerMillisec , 1000 );
466
519
CFDictionaryRef frameProperties = nullptr ;
467
520
if (isKeyframeRequired) {
468
521
CFTypeRef keys[] = {kVTEncodeFrameOptionKey_ForceKeyFrame };
@@ -480,8 +533,8 @@ - (NSInteger)encode:(RTC_OBJC_TYPE(RTCVideoFrame) *)frame
480
533
frame.rotation ));
481
534
encodeParams->codecSpecificInfo .packetizationMode = _packetizationMode;
482
535
483
- // Update the bitrate if needed.
484
- [self setBitrateBps: _bitrateAdjuster-> GetAdjustedBitrateBps () frameRate: _encoderFrameRate ];
536
+ // Update encoder bitrate or frameRate if needed.
537
+ [self updateEncoderBitrateAndFrameRate ];
485
538
486
539
OSStatus status = VTCompressionSessionEncodeFrame (_compressionSession,
487
540
pixelBuffer,
@@ -522,14 +575,19 @@ - (void)setCallback:(RTCVideoEncoderCallback)callback {
522
575
}
523
576
524
577
- (int )setBitrate : (uint32_t )bitrateKbit framerate : (uint32_t )framerate {
525
- _targetBitrateBps = 1000 * bitrateKbit;
526
- _bitrateAdjuster->SetTargetBitrateBps (_targetBitrateBps);
578
+ // set target bitrate bps
579
+ _targetBitrateBps = bitrateKbit * 1000 ;
580
+
581
+ RTC_LOG (LS_INFO) << " setBitrateKBit: " << bitrateKbit << " targetBps: " << _targetBitrateBps
582
+ << " frameRate: " << framerate;
583
+
527
584
if (framerate > _maxAllowedFrameRate && _maxAllowedFrameRate > 0 ) {
528
585
RTC_LOG (LS_WARNING) << " Encoder frame rate setting " << framerate << " is larger than the "
529
586
<< " maximal allowed frame rate " << _maxAllowedFrameRate << " ." ;
530
587
}
531
- framerate = MIN (framerate, _maxAllowedFrameRate);
532
- [self setBitrateBps: _bitrateAdjuster->GetAdjustedBitrateBps () frameRate: framerate];
588
+
589
+ _targetFrameRate = MIN (framerate, _maxAllowedFrameRate);
590
+
533
591
return WEBRTC_VIDEO_CODEC_OK;
534
592
}
535
593
@@ -621,14 +679,19 @@ - (int)resetCompressionSessionWithPixelFormat:(OSType)framePixelFormat {
621
679
(NSString *)kCVPixelBufferPixelFormatTypeKey : @(framePixelFormat),
622
680
};
623
681
624
- NSDictionary *encoder_specs;
682
+ NSMutableDictionary *encoder_specs;
625
683
#if defined(WEBRTC_MAC) && !defined(WEBRTC_IOS)
626
684
// Currently hw accl is supported above 360p on mac, below 360p
627
685
// the compression session will be created with hw accl disabled.
628
- encoder_specs = @{
686
+ encoder_specs = [ @{
629
687
(NSString *)kVTVideoEncoderSpecification_EnableHardwareAcceleratedVideoEncoder : @(YES ),
630
- };
631
-
688
+ } mutableCopy];
689
+ // Enable low-latency video encoding
690
+ if (@available (iOS 14.5 , macOS 11.3 , *)) {
691
+ [encoder_specs addEntriesFromDictionary: @{
692
+ (NSString *)kVTVideoEncoderSpecification_EnableLowLatencyRateControl : @(YES ),
693
+ }];
694
+ }
632
695
#endif
633
696
OSStatus status = VTCompressionSessionCreate (
634
697
nullptr , // use default allocator
@@ -669,11 +732,30 @@ - (int)resetCompressionSessionWithPixelFormat:(OSType)framePixelFormat {
669
732
- (void )configureCompressionSession {
670
733
RTC_DCHECK (_compressionSession);
671
734
SetVTSessionProperty (_compressionSession, kVTCompressionPropertyKey_RealTime , true );
672
- SetVTSessionProperty (_compressionSession,
673
- kVTCompressionPropertyKey_ProfileLevel ,
674
- ExtractProfile (*_profile_level_id, _mode == RTCVideoCodecModeScreensharing));
735
+ // Sacrifice encoding speed over quality when necessary
736
+ if (@available (iOS 14.0 , macOS 11.0 , *)) {
737
+ SetVTSessionProperty (
738
+ _compressionSession, kVTCompressionPropertyKey_PrioritizeEncodingSpeedOverQuality , true );
739
+ }
740
+ // Set maximum QP for screen sharing mode, range must be within 1 to 51
741
+ // https://developer.apple.com/documentation/videotoolbox/kvtcompressionpropertykey_maxallowedframeqp
742
+ if (@available (iOS 15.0 , macOS 12.0 , *)) {
743
+ // Only enable for screen sharing and let VideoToolbox do the optimizing as much as possible.
744
+ if (_codecMode == RTCVideoCodecModeScreensharing) {
745
+ RTC_LOG (LS_INFO) << " Configuring VideoToolbox to use maxQP: " << kHighH264QpThreshold
746
+ << " mode: " << _codecMode;
747
+ SetVTSessionProperty (
748
+ _compressionSession, kVTCompressionPropertyKey_MaxAllowedFrameQP , kHighH264QpThreshold );
749
+ }
750
+ }
751
+ SetVTSessionProperty (
752
+ _compressionSession,
753
+ kVTCompressionPropertyKey_ProfileLevel ,
754
+ ExtractProfile (*_profile_level_id, _codecMode == RTCVideoCodecModeScreensharing));
675
755
SetVTSessionProperty (_compressionSession, kVTCompressionPropertyKey_AllowFrameReordering , false );
676
- [self setEncoderBitrateBps: _targetBitrateBps frameRate: _encoderFrameRate];
756
+
757
+ // [self updateEncoderBitrateAndFrameRate];
758
+
677
759
// TODO(tkchin): Look at entropy mode and colorspace matrices.
678
760
// TODO(tkchin): Investigate to see if there's any way to make this work.
679
761
// May need it to interop with Android. Currently this call just fails.
@@ -701,49 +783,59 @@ - (NSString *)implementationName {
701
783
return @" VideoToolbox" ;
702
784
}
703
785
704
- - (void )setBitrateBps : (uint32_t )bitrateBps frameRate : (uint32_t )frameRate {
705
- if (_encoderBitrateBps != bitrateBps || _encoderFrameRate != frameRate) {
706
- [self setEncoderBitrateBps: bitrateBps frameRate: frameRate];
786
+ - (void )updateEncoderBitrateAndFrameRate {
787
+ // If no compression session simply return
788
+ if (!_compressionSession) {
789
+ return ;
707
790
}
708
- }
791
+ // Initial status
792
+ OSStatus status = noErr;
709
793
710
- - (void )setEncoderBitrateBps : (uint32_t )bitrateBps frameRate : (uint32_t )frameRate {
711
- if (_compressionSession) {
712
- SetVTSessionProperty (_compressionSession, kVTCompressionPropertyKey_AverageBitRate , bitrateBps);
794
+ uint32_t computedBitrateBps = _targetBitrateBps;
713
795
714
- // With zero `_maxAllowedFrameRate`, we fall back to automatic frame rate detection.
715
- if (_maxAllowedFrameRate > 0 ) {
716
- SetVTSessionProperty (
717
- _compressionSession, kVTCompressionPropertyKey_ExpectedFrameRate , frameRate);
718
- }
796
+ // With zero `_maxAllowedFrameRate`, we fall back to automatic frame rate detection.
797
+ uint32_t computedFrameRate = _maxAllowedFrameRate > 0 ? _targetFrameRate : 0 ;
719
798
720
- // TODO(tkchin): Add a helper method to set array value.
721
- int64_t dataLimitBytesPerSecondValue =
722
- static_cast <int64_t >(bitrateBps * kLimitToAverageBitRateFactor / 8 );
723
- CFNumberRef bytesPerSecond =
724
- CFNumberCreate (kCFAllocatorDefault , kCFNumberSInt64Type , &dataLimitBytesPerSecondValue);
725
- int64_t oneSecondValue = 1 ;
726
- CFNumberRef oneSecond =
727
- CFNumberCreate (kCFAllocatorDefault , kCFNumberSInt64Type , &oneSecondValue);
728
- const void *nums[2 ] = {bytesPerSecond, oneSecond};
729
- CFArrayRef dataRateLimits = CFArrayCreate (nullptr , nums, 2 , &kCFTypeArrayCallBacks );
730
- OSStatus status = VTSessionSetProperty (
731
- _compressionSession, kVTCompressionPropertyKey_DataRateLimits , dataRateLimits);
732
- if (bytesPerSecond) {
733
- CFRelease (bytesPerSecond);
734
- }
735
- if (oneSecond) {
736
- CFRelease (oneSecond);
799
+ // Set frame rate
800
+ if (computedFrameRate != _encoderFrameRate) {
801
+ status = VTSessionSetProperty (_compressionSession,
802
+ kVTCompressionPropertyKey_ExpectedFrameRate ,
803
+ (__bridge CFTypeRef) @(computedFrameRate));
804
+ // Ensure the bitrate was set successfully
805
+ if (status != noErr) {
806
+ RTC_LOG (LS_ERROR) << " Failed to set frame rate: " << computedFrameRate
807
+ << " error: " << status;
808
+ } else {
809
+ RTC_LOG (LS_INFO) << " Did update encoder frame rate: " << computedFrameRate;
737
810
}
738
- if (dataRateLimits) {
739
- CFRelease (dataRateLimits);
811
+ _encoderFrameRate = computedFrameRate;
812
+ }
813
+
814
+ // Set bitrate
815
+ if (computedBitrateBps != _encoderBitrateBps) {
816
+ status = VTSessionSetProperty (_compressionSession,
817
+ kVTCompressionPropertyKey_AverageBitRate ,
818
+ (__bridge CFTypeRef) @(computedBitrateBps));
819
+
820
+ // Ensure the bitrate was set successfully
821
+ if (status != noErr) {
822
+ RTC_LOG (LS_ERROR) << " Failed to update encoder bitrate: " << computedBitrateBps
823
+ << " error: " << status;
824
+ } else {
825
+ RTC_LOG (LS_INFO) << " Did update encoder bitrate: " << computedBitrateBps;
740
826
}
827
+
828
+ status = VTSessionSetProperty (
829
+ _compressionSession,
830
+ kVTCompressionPropertyKey_DataRateLimits ,
831
+ (__bridge CFArrayRef)CreateRateLimitArray (computedBitrateBps, _encodeMode));
741
832
if (status != noErr) {
742
- RTC_LOG (LS_ERROR) << " Failed to set data rate limit with code: " << status;
833
+ RTC_LOG (LS_ERROR) << " Failed to update encoder data rate limits" ;
834
+ } else {
835
+ RTC_LOG (LS_INFO) << " Did update encoder data rate limits" ;
743
836
}
744
837
745
- _encoderBitrateBps = bitrateBps;
746
- _encoderFrameRate = frameRate;
838
+ _encoderBitrateBps = computedBitrateBps;
747
839
}
748
840
}
749
841
@@ -799,8 +891,9 @@ - (void)frameWasEncoded:(OSStatus)status
799
891
frame.captureTimeMs = renderTimeMs;
800
892
frame.timeStamp = timestamp;
801
893
frame.rotation = rotation;
802
- frame.contentType = (_mode == RTCVideoCodecModeScreensharing) ? RTCVideoContentTypeScreenshare :
803
- RTCVideoContentTypeUnspecified;
894
+ frame.contentType = (_codecMode == RTCVideoCodecModeScreensharing) ?
895
+ RTCVideoContentTypeScreenshare :
896
+ RTCVideoContentTypeUnspecified;
804
897
frame.flags = webrtc::VideoSendTiming::kInvalid ;
805
898
806
899
_h264BitstreamParser.ParseBitstream (*buffer);
@@ -811,7 +904,6 @@ - (void)frameWasEncoded:(OSStatus)status
811
904
RTC_LOG (LS_ERROR) << " Encode callback failed" ;
812
905
return ;
813
906
}
814
- _bitrateAdjuster->Update (frame.buffer .length );
815
907
}
816
908
817
909
- (nullable RTC_OBJC_TYPE (RTCVideoEncoderQpThresholds) *)scalingSettings {
0 commit comments