Changeset 278272 in webkit


Ignore:
Timestamp:
May 31, 2021 12:28:32 AM (14 months ago)
Author:
youenn@apple.com
Message:

Use tighter bitrate allocation rules for WebRTC H264 software encoder
https://bugs.webkit.org/show_bug.cgi?id=226319
<rdar://73150695>

Reviewed by Eric Carlson.

Software H264 encoder is sometimes overshooting target bitrate in which case WebRTC backend will start dropping frames.
The encoder might then think it is on target and will not try to increase compression.
This makes it possible to be locked in a very low frame rate but high quality image situation.
It is often better to preserve frame rate and lower quality, the application could always lower frame rate if desired.

To do so, we detect whether the encoder is using software code path or not.
If so, we compute the actual frame rate and compare it with the expected frame rate.
If the actual frame rate is twice smaller or even below, we enter in a low frame rate mode.
Otherwise, we are in a regular frame rate mode where we apply the normal bitrate rules.
In the low frame rate mode, we divide the target bitrate by 3 so as to quickly recover frame rate.

This works well in situations where motion increases from time to time.
It is still not perfect for instance in case the video is muted and gets unmuted or when the scene is completely still and suddenly large motion happens.
In those cases, frame rate is recovered after a minute or so according my testing.

  • Source/webrtc/sdk/objc/components/video_codec/RTCVideoEncoderH264.mm:

(-[RTCVideoEncoderH264 initWithCodecInfo:]):
(-[RTCVideoEncoderH264 encode:codecSpecificInfo:frameTypes:]):
(-[RTCVideoEncoderH264 resetCompressionSessionWithPixelFormat:]):
(-[RTCVideoEncoderH264 setEncoderBitrateBps:frameRate:]):
(-[RTCVideoEncoderH264 updateBitRateAccordingActualFrameRate]):

Location:
trunk/Source/ThirdParty/libwebrtc
Files:
2 edited

Legend:

Unmodified
Added
Removed
  • trunk/Source/ThirdParty/libwebrtc/ChangeLog

    r277785 r278272  
     12021-05-31  Youenn Fablet  <youenn@apple.com>
     2
     3        Use tighter bitrate allocation rules for WebRTC H264 software encoder
     4        https://bugs.webkit.org/show_bug.cgi?id=226319
     5        <rdar://73150695>
     6
     7        Reviewed by Eric Carlson.
     8
     9        Software H264 encoder is sometimes overshooting target bitrate in which case WebRTC backend will start dropping frames.
     10        The encoder might then think it is on target and will not try to increase compression.
     11        This makes it possible to be locked in a very low frame rate but high quality image situation.
     12        It is often better to preserve frame rate and lower quality, the application could always lower frame rate if desired.
     13
     14        To do so, we detect whether the encoder is using software code path or not.
     15        If so, we compute the actual frame rate and compare it with the expected frame rate.
     16        If the actual frame rate is twice smaller or even below, we enter in a low frame rate mode.
     17        Otherwise, we are in a regular frame rate mode where we apply the normal bitrate rules.
     18        In the low frame rate mode, we divide the target bitrate by 3 so as to quickly recover frame rate.
     19
     20        This works well in situations where motion increases from time to time.
     21        It is still not perfect for instance in case the video is muted and gets unmuted or when the scene is completely still and suddenly large motion happens.
     22        In those cases, frame rate is recovered after a minute or so according my testing.
     23
     24        * Source/webrtc/sdk/objc/components/video_codec/RTCVideoEncoderH264.mm:
     25        (-[RTCVideoEncoderH264 initWithCodecInfo:]):
     26        (-[RTCVideoEncoderH264 encode:codecSpecificInfo:frameTypes:]):
     27        (-[RTCVideoEncoderH264 resetCompressionSessionWithPixelFormat:]):
     28        (-[RTCVideoEncoderH264 setEncoderBitrateBps:frameRate:]):
     29        (-[RTCVideoEncoderH264 updateBitRateAccordingActualFrameRate]):
     30
    1312021-05-20  Youenn Fablet  <youenn@apple.com>
    232
  • trunk/Source/ThirdParty/libwebrtc/Source/webrtc/sdk/objc/components/video_codec/RTCVideoEncoderH264.mm

    r277785 r278272  
    3636#include "rtc_base/buffer.h"
    3737#include "rtc_base/logging.h"
     38#include "rtc_base/system/arch.h"
    3839#include "rtc_base/time_utils.h"
    3940#include "sdk/objc/components/video_codec/nalu_rewriter.h"
     
    6162               rotation:(RTCVideoRotation)rotation
    6263     isKeyFrameRequired:(bool)isKeyFrameRequired;
    63 
     64- (void)updateBitRateAccordingActualFrameRate;
    6465@end
    6566
     
    7677
    7778const OSType kNV12PixelFormat = kCVPixelFormatType_420YpCbCr8BiPlanarFullRange;
     79
     80const int kBelowFrameRateThreshold = 2;
     81const int kBelowFrameRateBitRateDecrease = 3;
    7882
    7983// Struct that we pass to the encoder per frame to encode. We receive it again
     
    350354  bool _isKeyFrameRequired;
    351355  bool _isH264LowLatencyEncoderEnabled;
     356  bool _isUsingSoftwareEncoder;
     357  bool _isBelowExpectedFrameRate;
     358  uint32_t _frameCount;
     359  int64_t _lastFrameRateEstimationTime;
    352360}
    353361
     
    378386  _isKeyFrameRequired = false;
    379387  _isH264LowLatencyEncoderEnabled = true;
    380 
     388  _isUsingSoftwareEncoder = false;
     389  _isBelowExpectedFrameRate = false;
     390  _frameCount = 0;
     391  _lastFrameRateEstimationTime = 0;
    381392  return self;
    382393}
     
    442453  if (_disableEncoding) {
    443454    return WEBRTC_VIDEO_CODEC_ERROR;
     455  }
     456  if (_isUsingSoftwareEncoder) {
     457    [self updateBitRateAccordingActualFrameRate];
    444458  }
    445459
     
    752766                               &_vcpCompressionSession);
    753767  }
     768#elif defined(WEBRTC_MAC) && !defined(WEBRTC_IOS) && !(ENABLE_VCP_FOR_H264_BASELINE) && defined(WEBRTC_ARCH_X86_FAMILY)
     769  CFBooleanRef hwaccl_enabled = nullptr;
     770  if (status == noErr) {
     771    auto result = VTSessionCopyProperty(_vtCompressionSession, kVTCompressionPropertyKey_UsingHardwareAcceleratedVideoEncoder, nullptr, &hwaccl_enabled);
     772    _isUsingSoftwareEncoder = result == noErr ? !CFBooleanGetValue(hwaccl_enabled) : _width <= 480 && _height <= 480;
     773  }
    754774#else
    755775  // Provided encoder should be good enough.
     
    840860- (void)setEncoderBitrateBps:(uint32_t)bitrateBps frameRate:(uint32_t)frameRate {
    841861  if ([self hasCompressionSession]) {
    842     SetVTSessionProperty(_vtCompressionSession, _vcpCompressionSession, kVTCompressionPropertyKey_AverageBitRate, bitrateBps);
     862    auto actualTarget = _isBelowExpectedFrameRate ? bitrateBps / kBelowFrameRateBitRateDecrease : bitrateBps;
     863    SetVTSessionProperty(_vtCompressionSession, _vcpCompressionSession, kVTCompressionPropertyKey_AverageBitRate, actualTarget);
    843864
    844865    // With zero |_maxAllowedFrameRate|, we fall back to automatic frame rate detection.
     
    850871    // TODO(tkchin): Add a helper method to set array value.
    851872    int64_t dataLimitBytesPerSecondValue =
    852         static_cast<int64_t>(bitrateBps * kLimitToAverageBitRateFactor / 8);
     873        static_cast<int64_t>(_isBelowExpectedFrameRate ? actualTarget / 8 : actualTarget * kLimitToAverageBitRateFactor / 8);
    853874    CFNumberRef bytesPerSecond =
    854875        CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt64Type, &dataLimitBytesPerSecondValue);
     
    874895    _encoderFrameRate = frameRate;
    875896  }
     897}
     898
     899- (void)updateBitRateAccordingActualFrameRate {
     900  _frameCount++;
     901  auto currentTime = rtc::TimeMillis();
     902  if (!_lastFrameRateEstimationTime) {
     903    _lastFrameRateEstimationTime = currentTime;
     904    return;
     905  }
     906
     907  auto timeDifference = currentTime - _lastFrameRateEstimationTime;
     908
     909  // Let's not check too often.
     910  if (timeDifference < 1000) {
     911    return;
     912  }
     913
     914  auto frameRate = _frameCount * 1000 / timeDifference;
     915  _lastFrameRateEstimationTime = currentTime;
     916  _frameCount = 0;
     917
     918  bool isBelow = frameRate * kBelowFrameRateThreshold < _encoderFrameRate;
     919  if (isBelow == _isBelowExpectedFrameRate) {
     920    return;
     921  }
     922
     923  _isBelowExpectedFrameRate = isBelow;
     924  [self setEncoderBitrateBps:_encoderBitrateBps frameRate:_encoderFrameRate];
    876925}
    877926
Note: See TracChangeset for help on using the changeset viewer.