frame queue tweaks
This commit is contained in:
1 parent
5480e15595
commit
652e989927
4 files changed
+48
-54
No files matched your search
@@ -73,6 +73,10 @@ final class SafeTimer: NSObject {
|
|||||||
self.shouldRunHandler = true
|
self.shouldRunHandler = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@objc func isRunning() -> Bool {
|
||||||
|
return shouldRunHandler;
|
||||||
|
}
|
||||||
|
|
||||||
/// 彻底清理 timer,返回后保证 handler 不再执行
|
/// 彻底清理 timer,返回后保证 handler 不再执行
|
||||||
@objc func clean() {
|
@objc func clean() {
|
||||||
|
|||||||
@@ -53,12 +53,19 @@ extern int ff_isom_write_av1c(AVIOContext *pb, const uint8_t *buf, int size,
|
|||||||
|
|
||||||
CADisplayLink *_displayLink;
|
CADisplayLink *_displayLink;
|
||||||
FrameQueue *_frameQueue;
|
FrameQueue *_frameQueue;
|
||||||
|
uint8_t queueSize;
|
||||||
|
bool isFirstFrame;
|
||||||
NSInteger _maxRefreshRate;
|
NSInteger _maxRefreshRate;
|
||||||
RenderingBackend _renderingBackend;
|
RenderingBackend _renderingBackend;
|
||||||
|
|
||||||
FramePacingMode _framePacingMode;
|
FramePacingMode _framePacingMode;
|
||||||
bool _enableTimebase;
|
bool _enableTimebase;
|
||||||
bool _asyncFrameDequeue;
|
bool _asyncFrameDequeue;
|
||||||
|
|
||||||
|
bool isIPhone;
|
||||||
|
|
||||||
|
// CMTime playTime;
|
||||||
|
// NSTimeInterval previousLinkTime;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)reinitializeDisplayLayer
|
- (void)reinitializeDisplayLayer
|
||||||
@@ -139,12 +146,17 @@ extern int ff_isom_write_av1c(AVIOContext *pb, const uint8_t *buf, int size,
|
|||||||
_framePacingMode = tempSettings.framePacingMode.integerValue;
|
_framePacingMode = tempSettings.framePacingMode.integerValue;
|
||||||
_asyncFrameDequeue = tempSettings.asyncFrameDequeue;
|
_asyncFrameDequeue = tempSettings.asyncFrameDequeue;
|
||||||
_enableTimebase = false;
|
_enableTimebase = false;
|
||||||
|
queueSize = tempSettings.frameQueueSize.intValue;
|
||||||
|
isFirstFrame = true;
|
||||||
|
isIPhone = [Utils isIPhone];
|
||||||
|
|
||||||
_frameQueue = [FrameQueue sharedInstance];
|
_frameQueue = [FrameQueue sharedInstance];
|
||||||
[_frameQueue start];
|
[_frameQueue start];
|
||||||
[_frameQueue setHighWaterMark:(int)[tempSettings.frameQueueSize integerValue]];
|
[_frameQueue setHighWaterMark:(int)[tempSettings.frameQueueSize integerValue]];
|
||||||
|
//[_frameQueue setHighWaterMark:5];
|
||||||
|
|
||||||
[self reinitializeDisplayLayer];
|
[self reinitializeDisplayLayer];
|
||||||
|
NSTimeInterval interval = 1.0/tempSettings.framerate.intValue;
|
||||||
|
|
||||||
[[NSNotificationCenter defaultCenter] addObserver:self
|
[[NSNotificationCenter defaultCenter] addObserver:self
|
||||||
selector:@selector(reinitializeDisplayLayer)
|
selector:@selector(reinitializeDisplayLayer)
|
||||||
@@ -271,37 +283,52 @@ int DrSubmitDecodeUnit(PDECODE_UNIT decodeUnit);
|
|||||||
// DisplayLink calls us every vsync we we try to present the most recent frame. We try to maintain a user-configurable buffer
|
// DisplayLink calls us every vsync we we try to present the most recent frame. We try to maintain a user-configurable buffer
|
||||||
// of 1-5 frames. If the buffer is full, every other frame is dropped which just appears to the user as a lower framerate stream.
|
// of 1-5 frames. If the buffer is full, every other frame is dropped which just appears to the user as a lower framerate stream.
|
||||||
- (void)renderModeAVSB:(CADisplayLink *)link {
|
- (void)renderModeAVSB:(CADisplayLink *)link {
|
||||||
|
// NSTimeInterval current = link.targetTimestamp;
|
||||||
|
// NSLog(@"link %f", link.duration);
|
||||||
|
// previousLinkTime = current;
|
||||||
|
|
||||||
CFTimeInterval start = link.timestamp;
|
CFTimeInterval start = link.timestamp;
|
||||||
CFTimeInterval deadline = link.targetTimestamp;
|
CFTimeInterval nextFrameTime = link.targetTimestamp;
|
||||||
static CFTimeInterval lastTargetLocal = 0.0f;
|
static CFTimeInterval lastTargetLocal = 0.0f;
|
||||||
CFTimeInterval dl0 = CACurrentMediaTime();
|
CFTimeInterval dl0 = CACurrentMediaTime();
|
||||||
|
|
||||||
|
/*
|
||||||
static int lateCallbacks = 0;
|
static int lateCallbacks = 0;
|
||||||
if (dl0 > deadline) {
|
if (dl0 > nextFrameTime) {
|
||||||
// we already missed it, count how often this happens
|
// we already missed it, count how often this happens
|
||||||
lateCallbacks++;
|
lateCallbacks++;
|
||||||
return;
|
return;
|
||||||
}
|
}*/
|
||||||
|
|
||||||
[self checkDisplayLayer];
|
[self checkDisplayLayer];
|
||||||
|
|
||||||
static CFTimeInterval avgOverhead = 0.004f; // averaged each callback
|
CFTimeInterval waitFor = nextFrameTime - dl0;
|
||||||
CFTimeInterval waitFor = deadline - dl0 - avgOverhead;
|
|
||||||
if (waitFor < 0.001f) {
|
if (waitFor < 0.001f) {
|
||||||
waitFor = 0.0f;
|
waitFor = isIPhone ? waitFor : 0.0f;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(_asyncFrameDequeue){
|
if(isFirstFrame ? _frameQueue.count>0 : true){
|
||||||
[_frameQueue dequeueWithTimeout:waitFor completion:^(Frame *frame) {
|
// if(true){
|
||||||
|
if(isFirstFrame) isFirstFrame = false;
|
||||||
|
if(_asyncFrameDequeue){
|
||||||
|
[_frameQueue dequeueWithTimeout:waitFor completion:^(Frame *frame) {
|
||||||
|
if (frame) {
|
||||||
|
// LogOnce(LOG_I, @"Frame pacing: using AVSampleBufferDisplayLayer target %f Hz with %d FPS stream", 1.0f / (deadline - start), self->_frameRate);
|
||||||
|
[self renderFrame:frame atTime:CMTimeMakeWithSeconds(nextFrameTime, NSEC_PER_SEC)];
|
||||||
|
}
|
||||||
|
}];
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
Frame *frame = [_frameQueue dequeueWithTimeoutSync:waitFor];
|
||||||
if (frame) {
|
if (frame) {
|
||||||
CFTimeInterval dl1 = CACurrentMediaTime();
|
// CFTimeInterval dl1 = CACurrentMediaTime();
|
||||||
|
|
||||||
// LogOnce(LOG_I, @"Frame pacing: using AVSampleBufferDisplayLayer target %f Hz with %d FPS stream", 1.0f / (deadline - start), self->_frameRate);
|
// LogOnce(LOG_I, @"Frame pacing: using AVSampleBufferDisplayLayer target %f Hz with %d FPS stream", 1.0f / (deadline - start), self->_frameRate);
|
||||||
|
|
||||||
// The system works best with properly timed video frames, which we time to the end of the next vsync period,
|
// The system works best with properly timed video frames, which we time to the end of the next vsync period,
|
||||||
// the earliest they can be displayed due to double-buffering.
|
// the earliest they can be displayed due to double-buffering.
|
||||||
CFTimeInterval targetLocal = deadline + link.duration;
|
CFTimeInterval targetLocal = nextFrameTime;
|
||||||
|
|
||||||
[self renderFrame:frame atTime:CMTimeMakeWithSeconds(targetLocal, NSEC_PER_SEC)];
|
[self renderFrame:frame atTime:CMTimeMakeWithSeconds(targetLocal, NSEC_PER_SEC)];
|
||||||
|
|
||||||
@@ -313,7 +340,7 @@ int DrSubmitDecodeUnit(PDECODE_UNIT decodeUnit);
|
|||||||
// Update metrics
|
// Update metrics
|
||||||
if (lastTargetLocal != 0) {
|
if (lastTargetLocal != 0) {
|
||||||
CFTimeInterval frametime = targetLocal - lastTargetLocal;
|
CFTimeInterval frametime = targetLocal - lastTargetLocal;
|
||||||
if (frametime > deadline - start + 0.0005f) {
|
if (frametime > nextFrameTime - start + 0.0005f) {
|
||||||
// we missed a callback
|
// we missed a callback
|
||||||
// Log(LOG_W, @"*** slow frametime %.3f ms", frametime * 1000.0);
|
// Log(LOG_W, @"*** slow frametime %.3f ms", frametime * 1000.0);
|
||||||
}
|
}
|
||||||
@@ -322,49 +349,7 @@ int DrSubmitDecodeUnit(PDECODE_UNIT decodeUnit);
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
lastTargetLocal = targetLocal;
|
lastTargetLocal = targetLocal;
|
||||||
|
|
||||||
// weighted moving average of how much time displayLink needs after dequeuing a frame.
|
|
||||||
// This is used to avoid overshooting a vsync by waiting too long.
|
|
||||||
const double alpha = 0.1f;
|
|
||||||
avgOverhead = ((CACurrentMediaTime() - dl1) * alpha) + (avgOverhead * (1.0 - alpha));
|
|
||||||
}
|
}
|
||||||
}];
|
|
||||||
}
|
|
||||||
else{
|
|
||||||
Frame *frame = [_frameQueue dequeueWithTimeoutSync:waitFor];
|
|
||||||
if (frame) {
|
|
||||||
CFTimeInterval dl1 = CACurrentMediaTime();
|
|
||||||
|
|
||||||
// LogOnce(LOG_I, @"Frame pacing: using AVSampleBufferDisplayLayer target %f Hz with %d FPS stream", 1.0f / (deadline - start), self->_frameRate);
|
|
||||||
|
|
||||||
// The system works best with properly timed video frames, which we time to the end of the next vsync period,
|
|
||||||
// the earliest they can be displayed due to double-buffering.
|
|
||||||
CFTimeInterval targetLocal = deadline + link.duration;
|
|
||||||
|
|
||||||
[self renderFrame:frame atTime:CMTimeMakeWithSeconds(targetLocal, NSEC_PER_SEC)];
|
|
||||||
|
|
||||||
#ifdef DISPLAYLINK_VERBOSE
|
|
||||||
Log(LOG_I, @"[%.3f] rendering frame %d, waitFor %.3f ms, overhead %.3f ms, lateCallbacks %d, queue size %d",
|
|
||||||
deadline, frame.frameNumber, waitFor * 1000.0, avgOverhead * 1000.0, lateCallbacks, [_frameQueue count]);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// Update metrics
|
|
||||||
if (lastTargetLocal != 0) {
|
|
||||||
CFTimeInterval frametime = targetLocal - lastTargetLocal;
|
|
||||||
if (frametime > deadline - start + 0.0005f) {
|
|
||||||
// we missed a callback
|
|
||||||
// Log(LOG_W, @"*** slow frametime %.3f ms", frametime * 1000.0);
|
|
||||||
}
|
|
||||||
if ([[UIApplication sharedApplication] applicationState] != UIApplicationStateBackground) {
|
|
||||||
[[ImGuiPlots sharedInstance] observeFloat:PLOT_FRAMETIME value:frametime * 1000.0];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
lastTargetLocal = targetLocal;
|
|
||||||
|
|
||||||
// weighted moving average of how much time displayLink needs after dequeuing a frame.
|
|
||||||
// This is used to avoid overshooting a vsync by waiting too long.
|
|
||||||
const double alpha = 0.1f;
|
|
||||||
avgOverhead = ((CACurrentMediaTime() - dl1) * alpha) + (avgOverhead * (1.0 - alpha));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ FOUNDATION_EXPORT NSString *const deviceName;
|
|||||||
+ (NSString*) addressAndPortToAddressPortString:(NSString*)address port:(unsigned short)port;
|
+ (NSString*) addressAndPortToAddressPortString:(NSString*)address port:(unsigned short)port;
|
||||||
+ (bool)hdrSupported;
|
+ (bool)hdrSupported;
|
||||||
+ (bool)needUpdateDefaultSettings;
|
+ (bool)needUpdateDefaultSettings;
|
||||||
|
+ (BOOL)isIPhone;
|
||||||
|
|
||||||
#if !TARGET_OS_TV
|
#if !TARGET_OS_TV
|
||||||
+ (void) launchUrl:(NSString*)urlString;
|
+ (void) launchUrl:(NSString*)urlString;
|
||||||
|
|||||||
@@ -167,6 +167,10 @@ NSString *const deviceName = @"roth";
|
|||||||
return NO;
|
return NO;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
+ (BOOL)isIPhone{
|
||||||
|
return ([UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPhone);
|
||||||
|
}
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
@implementation NSString (NSStringWithTrim)
|
@implementation NSString (NSStringWithTrim)
|
||||||
|
|||||||
Reference in new issue
Block a user