diff --git a/VoidLink/Stream/VideoDecoderRenderer.m b/VoidLink/Stream/VideoDecoderRenderer.m index c5aba53d..8ea22814 100644 --- a/VoidLink/Stream/VideoDecoderRenderer.m +++ b/VoidLink/Stream/VideoDecoderRenderer.m @@ -852,11 +852,10 @@ int DrSubmitDecodeUnit(PDECODE_UNIT decodeUnit); LiRequestIdrFrame(); // Request an IDR to restart the stream return; } - if (self->_renderingBackend == RENDER_METAL && self.pipLayer) { -// NSLog(@"[Metal PiP Path] Entered for frame number %d.", frameNumber); + + if (self.pipLayer) { CVPixelBufferRetain(imageBuffer); dispatch_async(dispatch_get_main_queue(), ^{ -// NSLog(@"[Metal PiP Path] Executing on main thread for frame %d.", frameNumber); @synchronized(self) { if (self.pipLayer.controlTimebase == NULL) { NSLog(@"[Metal PiP Path] Timebase is NULL. Setting it now."); @@ -886,7 +885,6 @@ int DrSubmitDecodeUnit(PDECODE_UNIT decodeUnit); CMSampleTimingInfo sampleTiming = {kCMTimeInvalid, presentationTimestamp, presentationDuration}; OSStatus err = CMSampleBufferCreateReadyWithImageBuffer(kCFAllocatorDefault, imageBuffer, self->_pipFormatDesc, &sampleTiming, &sampleBufferForPip); if (err == noErr && sampleBufferForPip) { -// NSLog(@"[Metal PiP Path] Sample buffer created. Enqueuing to _pipLayer."); [self.pipLayer enqueueSampleBuffer:sampleBufferForPip]; CFRelease(sampleBufferForPip); } else { @@ -899,7 +897,7 @@ int DrSubmitDecodeUnit(PDECODE_UNIT decodeUnit); } // --- MAIN RENDERER PATH --- if (self->_renderingBackend == RENDER_AVSB) { -// self.pipLayer.hidden = YES; + self.pipLayer.hidden = NO; if (self->_avsbFormatDesc == NULL || !CMVideoFormatDescriptionMatchesImageBuffer(self->_avsbFormatDesc, imageBuffer)) { if (self->_avsbFormatDesc) { CFRelease(self->_avsbFormatDesc); @@ -915,7 +913,21 @@ int DrSubmitDecodeUnit(PDECODE_UNIT decodeUnit); dispatch_async(self->_vtq, ^{ Frame *frame = [[Frame alloc] initWithSampleBuffer:sampleBufferForAvsb frameNumber:frameNumber frameType:frameType]; int framesDropped = [self->_frameQueue enqueue:frame withSlackSize:3]; - // ... stats and metrics updates + static PlotMetrics frameQueueMetrics = {}; + [[ImGuiPlots sharedInstance] observeFloatReturnMetrics:PLOT_QUEUED_FRAMES value:[self->_frameQueue count] plotMetrics:&frameQueueMetrics]; + [self safeCopyMetricsTo:&self->_frameQueueMetrics from:&frameQueueMetrics]; + + [[ImGuiPlots sharedInstance] observeFloat:PLOT_DROPPED value:framesDropped]; + + static CFTimeInterval lastHostFrame = 0.0f; + if (lastHostFrame != 0) { + [[ImGuiPlots sharedInstance] observeFloat:PLOT_HOST_FRAMETIME value:(frame.pts - lastHostFrame) * 1000.0]; + } + lastHostFrame = frame.pts; + + static PlotMetrics decodeMetrics = {}; + [[ImGuiPlots sharedInstance] observeFloatReturnMetrics:PLOT_DECODE value:(CACurrentMediaTime() - decodeStartTime) * 1000.0 plotMetrics:&decodeMetrics]; + [self safeCopyMetricsTo:&self->_decodeMetrics from:&decodeMetrics]; }); } else { if (sampleBufferForAvsb) { diff --git a/VoidLink/ViewControllers/StreamFrameViewController.m b/VoidLink/ViewControllers/StreamFrameViewController.m index 51541778..f08301ac 100644 --- a/VoidLink/ViewControllers/StreamFrameViewController.m +++ b/VoidLink/ViewControllers/StreamFrameViewController.m @@ -97,6 +97,7 @@ - (void)pictureInPictureControllerWillStartPictureInPicture:(AVPictureInPictureController *)pictureInPictureController { _streamView.hidden = YES; + _pipLayer.hidden = YES; if (self.imguiView) { self.imguiView.mtkView.hidden = YES; Log(LOG_I, @"Hiding ImGui view for PiP start."); @@ -116,6 +117,7 @@ - (void)pictureInPictureControllerDidStopPictureInPicture:(AVPictureInPictureController *)pictureInPictureController { _streamView.hidden = NO; + _pipLayer.hidden = NO; if (self.imguiView) { self.imguiView.mtkView.hidden = NO; Log(LOG_I, @"Showing ImGui view after PiP stop."); @@ -129,6 +131,7 @@ - (void)pictureInPictureController:(AVPictureInPictureController *)pictureInPictureController restoreUserInterfaceForPictureInPictureStopWithCompletionHandler:(void (^)(BOOL restored))completionHandler { _isRestoringFromPiP = YES; _streamView.hidden = NO; + _pipLayer.hidden = NO; if (self.imguiView) { self.imguiView.mtkView.hidden = NO; Log(LOG_I, @"Showing ImGui view for PiP restore."); @@ -178,27 +181,23 @@ return (_settings.touchMode.intValue == RelativeTouch || _settings.touchMode.intValue == NativeTouch || _settings.touchMode.intValue == AbsoluteTouch) && _settings.onscreenControls.intValue == OnScreenControlsLevelCustom; } -- (void)setupPiPControllerWithRenderer:(VideoDecoderRenderer *)videoRenderer { // Ensure we have the renderer and its layer +- (void)setupPiPControllerWithRenderer:(VideoDecoderRenderer *)videoRenderer { if (self.pipController) { return; } Log(LOG_I, @"Setting up PiP controller..."); - - AVSampleBufferDisplayLayer* sourceLayer; + + // Both Metal and AVSB will use the dedicated _pipLayer for PiP. + AVSampleBufferDisplayLayer* sourceLayer = _pipLayer; + if ([_settings.renderingBackend intValue] == RENDER_METAL) { - sourceLayer = _pipLayer; - NSLog(@"[PiP LOG] Backend: Metal. Using _pipLayer: %p", sourceLayer); - if (!sourceLayer) { - NSLog(@"[PiP LOG] ERROR: _pipLayer is nil during setup."); - return; - } + NSLog(@"[PiP LOG] Backend: Metal. Using dedicated _pipLayer: %p", sourceLayer); } else { // RENDER_AVSB - sourceLayer = videoRenderer.displayLayer; - NSLog(@"[PiP LOG] Backend: AVSB. Using _displayLayer: %p", sourceLayer); + NSLog(@"[PiP LOG] Backend: AVSB. Using dedicated _pipLayer: %p", sourceLayer); } - if (!_pipLayer) { - Log(LOG_E, @"PiP setup failed: Dedicated PiP layer not ready."); + if (!sourceLayer) { + Log(LOG_E, @"PiP setup failed: Dedicated PiP layer is nil."); return; }