feat: use CAPABILITY_PULL_RENDERER for legacy pacing mode
This commit is contained in:
1 parent
8547cee1b9
commit
ebf46042ba
2 files changed
+87
-16
No files matched your search
@@ -9,6 +9,7 @@
|
|||||||
#import "Connection.h"
|
#import "Connection.h"
|
||||||
#import "Plot.h"
|
#import "Plot.h"
|
||||||
#import "Utils.h"
|
#import "Utils.h"
|
||||||
|
#import "DataManager.h"
|
||||||
|
|
||||||
#import <VideoToolbox/VideoToolbox.h>
|
#import <VideoToolbox/VideoToolbox.h>
|
||||||
|
|
||||||
@@ -505,10 +506,19 @@ void ClSetControllerLED(uint16_t controllerNumber, uint8_t r, uint8_t g, uint8_t
|
|||||||
LiInitializeVideoCallbacks(&_drCallbacks);
|
LiInitializeVideoCallbacks(&_drCallbacks);
|
||||||
_drCallbacks.setup = DrDecoderSetup;
|
_drCallbacks.setup = DrDecoderSetup;
|
||||||
_drCallbacks.cleanup = DrCleanup;
|
_drCallbacks.cleanup = DrCleanup;
|
||||||
_drCallbacks.submitDecodeUnit = DrSubmitDecodeUnit;
|
// Use pull renderer for legacy frame pacing, direct submit for modern frame pacing
|
||||||
_drCallbacks.capabilities = CAPABILITY_DIRECT_SUBMIT |
|
DataManager* dataMan = [[DataManager alloc] init];
|
||||||
CAPABILITY_REFERENCE_FRAME_INVALIDATION_HEVC |
|
if ([[dataMan getSettings].framePacingMode integerValue] == FramePacingModeLegacy) {
|
||||||
CAPABILITY_REFERENCE_FRAME_INVALIDATION_AV1;
|
_drCallbacks.capabilities = CAPABILITY_PULL_RENDERER |
|
||||||
|
CAPABILITY_REFERENCE_FRAME_INVALIDATION_HEVC |
|
||||||
|
CAPABILITY_REFERENCE_FRAME_INVALIDATION_AV1;
|
||||||
|
_drCallbacks.submitDecodeUnit = NULL;
|
||||||
|
} else {
|
||||||
|
_drCallbacks.capabilities = CAPABILITY_DIRECT_SUBMIT |
|
||||||
|
CAPABILITY_REFERENCE_FRAME_INVALIDATION_HEVC |
|
||||||
|
CAPABILITY_REFERENCE_FRAME_INVALIDATION_AV1;
|
||||||
|
_drCallbacks.submitDecodeUnit = DrSubmitDecodeUnit;
|
||||||
|
}
|
||||||
|
|
||||||
LiInitializeAudioCallbacks(&_arCallbacks);
|
LiInitializeAudioCallbacks(&_arCallbacks);
|
||||||
_arCallbacks.init = ArInit;
|
_arCallbacks.init = ArInit;
|
||||||
|
|||||||
@@ -55,6 +55,7 @@ extern int ff_isom_write_av1c(AVIOContext *pb, const uint8_t *buf, int size,
|
|||||||
NSInteger _maxRefreshRate;
|
NSInteger _maxRefreshRate;
|
||||||
RenderingBackend _renderingBackend;
|
RenderingBackend _renderingBackend;
|
||||||
|
|
||||||
|
BOOL _useLegacyPacing;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)reinitializeDisplayLayer
|
- (void)reinitializeDisplayLayer
|
||||||
@@ -129,6 +130,8 @@ extern int ff_isom_write_av1c(AVIOContext *pb, const uint8_t *buf, int size,
|
|||||||
|
|
||||||
DataManager* dataMan = [[DataManager alloc] init];
|
DataManager* dataMan = [[DataManager alloc] init];
|
||||||
|
|
||||||
|
_useLegacyPacing = [[dataMan getSettings].framePacingMode integerValue] == FramePacingModeLegacy;
|
||||||
|
|
||||||
_frameQueue = [FrameQueue sharedInstance];
|
_frameQueue = [FrameQueue sharedInstance];
|
||||||
[_frameQueue start];
|
[_frameQueue start];
|
||||||
[_frameQueue setHighWaterMark:(int)[[dataMan getSettings].frameQueueSize integerValue]];
|
[_frameQueue setHighWaterMark:(int)[[dataMan getSettings].frameQueueSize integerValue]];
|
||||||
@@ -155,11 +158,22 @@ extern int ff_isom_write_av1c(AVIOContext *pb, const uint8_t *buf, int size,
|
|||||||
|
|
||||||
DataManager* dataMan = [[DataManager alloc] init];
|
DataManager* dataMan = [[DataManager alloc] init];
|
||||||
if ([[dataMan getSettings].renderingBackend integerValue] == RENDER_AVSB) {
|
if ([[dataMan getSettings].renderingBackend integerValue] == RENDER_AVSB) {
|
||||||
// PACING_MODE_VSYNC:
|
|
||||||
// Deliver 1 frame at each vsync interval. Ignores server pts timestamps.
|
|
||||||
// Drop frames intelligently to maintain chosen queue size.
|
|
||||||
_renderingBackend = RENDER_AVSB;
|
_renderingBackend = RENDER_AVSB;
|
||||||
_displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(renderModeAVSB:)];
|
|
||||||
|
// Choose the appropriate selector based on frame pacing mode
|
||||||
|
SEL displayLinkSelector;
|
||||||
|
if (_useLegacyPacing) {
|
||||||
|
// Legacy frame pacing: use simple displayLinkCallback
|
||||||
|
displayLinkSelector = @selector(displayLinkCallback:);
|
||||||
|
} else {
|
||||||
|
// PACING_MODE_VSYNC:
|
||||||
|
// Deliver 1 frame at each vsync interval. Ignores server pts timestamps.
|
||||||
|
// Drop frames intelligently to maintain chosen queue size.
|
||||||
|
// Queue-based frame pacing: use renderModeAVSB
|
||||||
|
displayLinkSelector = @selector(renderModeAVSB:);
|
||||||
|
}
|
||||||
|
|
||||||
|
_displayLink = [CADisplayLink displayLinkWithTarget:self selector:displayLinkSelector];
|
||||||
|
|
||||||
if (@available(iOS 15.0, tvOS 15.0, *)) {
|
if (@available(iOS 15.0, tvOS 15.0, *)) {
|
||||||
_displayLink.preferredFrameRateRange = CAFrameRateRangeMake(self->_frameRate, self->_frameRate, self->_frameRate);
|
_displayLink.preferredFrameRateRange = CAFrameRateRangeMake(self->_frameRate, self->_frameRate, self->_frameRate);
|
||||||
@@ -307,6 +321,33 @@ int DrSubmitDecodeUnit(PDECODE_UNIT decodeUnit);
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#pragma mark DisplayLink - Legacy Frame Pacing
|
||||||
|
|
||||||
|
// Legacy frame pacing callback - matches upstream/Integration behavior exactly
|
||||||
|
- (void)displayLinkCallback:(CADisplayLink *)sender
|
||||||
|
{
|
||||||
|
VIDEO_FRAME_HANDLE handle;
|
||||||
|
PDECODE_UNIT du;
|
||||||
|
|
||||||
|
while (LiPollNextVideoFrame(&handle, &du)) {
|
||||||
|
LiCompleteVideoFrame(handle, DrSubmitDecodeUnit(du));
|
||||||
|
|
||||||
|
// Calculate the actual display refresh rate
|
||||||
|
double displayRefreshRate = 1 / (_displayLink.targetTimestamp - _displayLink.timestamp);
|
||||||
|
|
||||||
|
// Only pace frames if the display refresh rate is >= 90% of our stream frame rate.
|
||||||
|
// Battery saver, accessibility settings, or device thermals can cause the actual
|
||||||
|
// refresh rate of the display to drop below the physical maximum.
|
||||||
|
if (displayRefreshRate >= _frameRate * 0.9f) {
|
||||||
|
// Keep one pending frame to smooth out gaps due to
|
||||||
|
// network jitter at the cost of 1 frame of latency
|
||||||
|
if (LiGetPendingVideoFrames() == 1) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Render frame at a specific targetTime
|
// Render frame at a specific targetTime
|
||||||
- (void)renderFrame:(Frame *)frame atTime:(CMTime)targetTime {
|
- (void)renderFrame:(Frame *)frame atTime:(CMTime)targetTime {
|
||||||
CMSampleBufferSetOutputPresentationTimeStamp(frame.sampleBuffer, targetTime);
|
CMSampleBufferSetOutputPresentationTimeStamp(frame.sampleBuffer, targetTime);
|
||||||
@@ -813,15 +854,21 @@ int DrSubmitDecodeUnit(PDECODE_UNIT decodeUnit);
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CMSampleBufferRef sampleBuffer;
|
||||||
|
CMTime presentationTimeStamp;
|
||||||
|
if (_useLegacyPacing) {
|
||||||
|
presentationTimeStamp = CMTimeMake(du->presentationTimeUs / 1000, 1000);
|
||||||
|
} else {
|
||||||
|
presentationTimeStamp = CMTimeMake((int64_t)du->rtpTimestamp, 90000);
|
||||||
|
}
|
||||||
// Set the current frame's pts, in RTP 90khz units. We will set the duration
|
// Set the current frame's pts, in RTP 90khz units. We will set the duration
|
||||||
// later in FrameQueue because it requires the next frame's timestamp.
|
// later in FrameQueue because it requires the next frame's timestamp.
|
||||||
CMSampleTimingInfo sampleTiming = {
|
CMSampleTimingInfo sampleTiming = {
|
||||||
.duration = kCMTimeInvalid,
|
.duration = kCMTimeInvalid,
|
||||||
.presentationTimeStamp = CMTimeMake((int64_t)du->rtpTimestamp, 90000),
|
.presentationTimeStamp = presentationTimeStamp,
|
||||||
.decodeTimeStamp = kCMTimeInvalid,
|
.decodeTimeStamp = kCMTimeInvalid,
|
||||||
};
|
};
|
||||||
|
|
||||||
CMSampleBufferRef sampleBuffer;
|
|
||||||
status = CMSampleBufferCreateReady(kCFAllocatorDefault,
|
status = CMSampleBufferCreateReady(kCFAllocatorDefault,
|
||||||
frameBlockBuffer,
|
frameBlockBuffer,
|
||||||
_formatDesc, 1, 1,
|
_formatDesc, 1, 1,
|
||||||
@@ -834,10 +881,24 @@ int DrSubmitDecodeUnit(PDECODE_UNIT decodeUnit);
|
|||||||
return DR_NEED_IDR;
|
return DR_NEED_IDR;
|
||||||
}
|
}
|
||||||
|
|
||||||
OSStatus decodeStatus = [self decodeFrameWithSampleBuffer:sampleBuffer
|
if (_useLegacyPacing) {
|
||||||
frameNumber:du->frameNumber
|
// Enqueue the next frame
|
||||||
frameType:du->frameType
|
[self->_displayLayer enqueueSampleBuffer:sampleBuffer];
|
||||||
decodeStartTime:decodeStartTime];
|
|
||||||
|
if (du->frameType == FRAME_TYPE_IDR) {
|
||||||
|
// Ensure the layer is visible now
|
||||||
|
self->_displayLayer.hidden = NO;
|
||||||
|
|
||||||
|
// Tell our parent VC to hide the progress indicator
|
||||||
|
[self->_callbacks videoContentShown];
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
OSStatus decodeStatus = [self decodeFrameWithSampleBuffer:sampleBuffer
|
||||||
|
frameNumber:du->frameNumber
|
||||||
|
frameType:du->frameType
|
||||||
|
decodeStartTime:decodeStartTime];
|
||||||
|
}
|
||||||
|
|
||||||
// Dereference the buffers
|
// Dereference the buffers
|
||||||
CFRelease(dataBlockBuffer);
|
CFRelease(dataBlockBuffer);
|
||||||
CFRelease(frameBlockBuffer);
|
CFRelease(frameBlockBuffer);
|
||||||
|
|||||||
Reference in new issue
Block a user