fix: stream freeze when turning screen off and on with AVSB
This commit is contained in:
1 parent
bd9e2e3215
commit
5df330f608
1 file changed
+54
-16
@@ -297,7 +297,7 @@ int DrSubmitDecodeUnit(PDECODE_UNIT decodeUnit);
|
|||||||
- (void)renderFrame:(Frame *)frame atTime:(CMTime)targetTime {
|
- (void)renderFrame:(Frame *)frame atTime:(CMTime)targetTime {
|
||||||
CMSampleBufferSetOutputPresentationTimeStamp(frame.sampleBuffer, targetTime);
|
CMSampleBufferSetOutputPresentationTimeStamp(frame.sampleBuffer, targetTime);
|
||||||
|
|
||||||
if (frame.frameNumber == 1) {
|
if ([self->_displayLayer controlTimebase] == NULL) {
|
||||||
// On first frame, set timebase to the initial presentation time.
|
// On first frame, set timebase to the initial presentation time.
|
||||||
// This will let us present frames using the local clock (vsync pacing) or
|
// This will let us present frames using the local clock (vsync pacing) or
|
||||||
// the pts timestamps from the host.
|
// the pts timestamps from the host.
|
||||||
@@ -310,6 +310,9 @@ int DrSubmitDecodeUnit(PDECODE_UNIT decodeUnit);
|
|||||||
CMTimebaseSetRate(timebase, 1.0);
|
CMTimebaseSetRate(timebase, 1.0);
|
||||||
|
|
||||||
[self->_displayLayer setControlTimebase:timebase];
|
[self->_displayLayer setControlTimebase:timebase];
|
||||||
|
if (timebase) {
|
||||||
|
CFRelease(timebase);
|
||||||
|
}
|
||||||
Log(LOG_I, @"Setting timebase for stream to %d / %d", pts.value, pts.timescale);
|
Log(LOG_I, @"Setting timebase for stream to %d / %d", pts.value, pts.timescale);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -821,7 +824,28 @@ int DrSubmitDecodeUnit(PDECODE_UNIT decodeUnit);
|
|||||||
frameNumber:(int)frameNumber
|
frameNumber:(int)frameNumber
|
||||||
frameType:(int)frameType
|
frameType:(int)frameType
|
||||||
decodeStartTime:(CFTimeInterval)decodeStartTime {
|
decodeStartTime:(CFTimeInterval)decodeStartTime {
|
||||||
if (frameType == FRAME_TYPE_IDR || _decompressionSession == nil) {
|
@synchronized(self) {
|
||||||
|
// If the decompression session is nil, we must decide what to do.
|
||||||
|
if (_decompressionSession == nil) {
|
||||||
|
// We can only create a session with an IDR frame.
|
||||||
|
if (frameType == FRAME_TYPE_IDR) {
|
||||||
|
// This is the startup path. Create the session now.
|
||||||
|
[self setupDecompressionSession];
|
||||||
|
|
||||||
|
// If session creation still fails, we cannot proceed.
|
||||||
|
if (_decompressionSession == nil) {
|
||||||
|
Log(LOG_E, @"Failed to create initial decompression session with IDR frame.");
|
||||||
|
return kVTInvalidSessionErr;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// This happens when resuming from background; we've reset the session
|
||||||
|
// and are waiting for a new IDR frame. Drop all other frames until it arrives.
|
||||||
|
return noErr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Also re-create the session if a new IDR frame arrives during an active stream,
|
||||||
|
// as it may contain updated video parameters (e.g., HDR toggled).
|
||||||
|
else if (frameType == FRAME_TYPE_IDR) {
|
||||||
[self setupDecompressionSession];
|
[self setupDecompressionSession];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -977,6 +1001,7 @@ int DrSubmitDecodeUnit(PDECODE_UNIT decodeUnit);
|
|||||||
});
|
});
|
||||||
|
|
||||||
return status;
|
return status;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)setHdrMode:(BOOL)enabled {
|
- (void)setHdrMode:(BOOL)enabled {
|
||||||
@@ -1130,25 +1155,38 @@ int DrSubmitDecodeUnit(PDECODE_UNIT decodeUnit);
|
|||||||
|
|
||||||
- (void)resetFramePacing {
|
- (void)resetFramePacing {
|
||||||
// Ensure this only runs for the AVSampleBuffer rendering backend and that the display link exists.
|
// Ensure this only runs for the AVSampleBuffer rendering backend and that the display link exists.
|
||||||
if (_renderingBackend == RENDER_AVSB && _displayLink) {
|
if (_renderingBackend == RENDER_AVSB) {
|
||||||
Log(LOG_I, @"Frame pacing is being reset to %d FPS...", self->_frameRate);
|
Log(LOG_I, @"Resetting video pipeline for AVSB renderer after resuming...");
|
||||||
|
|
||||||
// Toggling the paused state can help re-engage the display link with the
|
// 1. Reset the receiver layers on the main thread
|
||||||
// run loop correctly after the app resumes from a background state like PiP.
|
dispatch_async(dispatch_get_main_queue(), ^{
|
||||||
_displayLink.paused = YES;
|
// Reset the main display layer
|
||||||
|
if (self->_displayLayer) {
|
||||||
// Re-apply the desired frame rate range. This is the critical hint for ProMotion
|
[self->_displayLayer flushAndRemoveImage];
|
||||||
// that may have been lost or ignored during the PiP transition.
|
[self->_displayLayer setControlTimebase:NULL];
|
||||||
if (@available(iOS 15.0, *)) {
|
|
||||||
_displayLink.preferredFrameRateRange = CAFrameRateRangeMake(self->_frameRate, self->_frameRate, self->_frameRate);
|
|
||||||
} else {
|
|
||||||
_displayLink.preferredFramesPerSecond = self->_frameRate;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Resume the display link immediately.
|
// FIX: Also reset the PiP layer if it exists
|
||||||
_displayLink.paused = NO;
|
if (self.pipLayer) {
|
||||||
|
[self.pipLayer flushAndRemoveImage];
|
||||||
|
[self.pipLayer setControlTimebase:NULL];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// 2. Reset the sender (the Decompression Session)
|
||||||
|
@synchronized(self) {
|
||||||
|
if (_decompressionSession != nil) {
|
||||||
|
VTDecompressionSessionInvalidate(_decompressionSession);
|
||||||
|
CFRelease(_decompressionSession);
|
||||||
|
_decompressionSession = nil;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. Request a new keyframe to restart the video stream cleanly
|
||||||
|
LiRequestIdrFrame();
|
||||||
}
|
}
|
||||||
else if (_renderingBackend == RENDER_METAL) {
|
else if (_renderingBackend == RENDER_METAL) {
|
||||||
|
// (The existing, correct logic for the Metal renderer remains here)
|
||||||
Log(LOG_I, @"Resetting video pipeline for Metal renderer after resuming...");
|
Log(LOG_I, @"Resetting video pipeline for Metal renderer after resuming...");
|
||||||
|
|
||||||
// 1. Reset the receiver (the PiP Layer) on the main thread
|
// 1. Reset the receiver (the PiP Layer) on the main thread
|
||||||
|
|||||||
Reference in new issue
Block a user