fix: stream freeze when turning screen off and on with AVSB

This commit is contained in:
Acaki committed 2025-07-16 02:15:28 +08:00
1 parent bd9e2e3215
commit 5df330f608
1 file changed
+54 -16
+54 -16
View File
@@ -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