From 4f7b6406c469415590c682af50963587ce2f5071 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A1ri=20Tristan=20Helgason?= Date: Mon, 16 Apr 2018 11:39:53 +0200 Subject: [PATCH] Add checks that we don't redraw the previous frame. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bug: webrtc:9149 Change-Id: Ia1f61fd43ea9be6c341a111595e8a290a809c72f Reviewed-on: https://webrtc-review.googlesource.com/69810 Reviewed-by: Peter Hanspers Commit-Queue: Kári Helgason Cr-Commit-Position: refs/heads/master@{#22937} --- .../Framework/Classes/Metal/RTCMTLVideoView.m | 10 +++- .../UnitTests/RTCMTLVideoView_xctest.mm | 51 +++++++++++++++++++ 2 files changed, 59 insertions(+), 2 deletions(-) diff --git a/sdk/objc/Framework/Classes/Metal/RTCMTLVideoView.m b/sdk/objc/Framework/Classes/Metal/RTCMTLVideoView.m index 0fd8a1a912..24dc29f7f3 100644 --- a/sdk/objc/Framework/Classes/Metal/RTCMTLVideoView.m +++ b/sdk/objc/Framework/Classes/Metal/RTCMTLVideoView.m @@ -33,7 +33,9 @@ @property(atomic, strong) RTCVideoFrame *videoFrame; @end -@implementation RTCMTLVideoView +@implementation RTCMTLVideoView { + int64_t _lastFrameTimeNs; +} @synthesize rendererI420 = _rendererI420; @synthesize rendererNV12 = _rendererNV12; @@ -106,7 +108,8 @@ - (void)drawInMTKView:(nonnull MTKView *)view { NSAssert(view == self.metalView, @"Receiving draw callbacks from foreign instance."); RTCVideoFrame *videoFrame = self.videoFrame; - if (!videoFrame) { + // Skip rendering if we've already rendered this frame. + if (!videoFrame || videoFrame.timeStampNs == _lastFrameTimeNs) { return; } @@ -116,6 +119,7 @@ if (![self.rendererNV12 addRenderingDestination:self.metalView]) { self.rendererNV12 = nil; RTCLogError(@"Failed to create NV12 renderer"); + return; } } [self.rendererNV12 drawFrame:videoFrame]; @@ -125,10 +129,12 @@ if (![self.rendererI420 addRenderingDestination:self.metalView]) { self.rendererI420 = nil; RTCLogError(@"Failed to create I420 renderer"); + return; } } [self.rendererI420 drawFrame:videoFrame]; } + _lastFrameTimeNs = videoFrame.timeStampNs; } - (void)mtkView:(MTKView *)view drawableSizeWillChange:(CGSize)size { diff --git a/sdk/objc/Framework/UnitTests/RTCMTLVideoView_xctest.mm b/sdk/objc/Framework/UnitTests/RTCMTLVideoView_xctest.mm index 782bc64b1a..8533a31066 100644 --- a/sdk/objc/Framework/UnitTests/RTCMTLVideoView_xctest.mm +++ b/sdk/objc/Framework/UnitTests/RTCMTLVideoView_xctest.mm @@ -73,6 +73,7 @@ } else { OCMStub([frameMock buffer]).andReturn([[RTCI420Buffer alloc] initWithWidth:200 height:200]); } + OCMStub([frameMock timeStampNs]).andReturn(arc4random_uniform(INT_MAX)); return frameMock; } @@ -196,4 +197,54 @@ [self.classMock verify]; } +- (void)testDontRedrawOldFrame { + OCMStub([self.classMock isMetalAvailable]).andReturn(YES); + self.rendererNV12Mock = [self rendererMockWithSuccessfulSetup:YES]; + self.frameMock = [self frameMockWithCVPixelBuffer:YES]; + + OCMExpect([self.rendererNV12Mock drawFrame:self.frameMock]); + OCMExpect([self.classMock createNV12Renderer]).andReturn(self.rendererNV12Mock); + [[self.classMock reject] createI420Renderer]; + + RTCMTLVideoView *realView = [[RTCMTLVideoView alloc] init]; + [realView renderFrame:self.frameMock]; + [realView drawInMTKView:nil]; + + [self.rendererNV12Mock verify]; + [self.classMock verify]; + + [[self.rendererNV12Mock reject] drawFrame:[OCMArg any]]; + + [realView renderFrame:self.frameMock]; + [realView drawInMTKView:nil]; + + [self.rendererNV12Mock verify]; +} + +- (void)testDoDrawNewFrame { + OCMStub([self.classMock isMetalAvailable]).andReturn(YES); + self.rendererNV12Mock = [self rendererMockWithSuccessfulSetup:YES]; + self.frameMock = [self frameMockWithCVPixelBuffer:YES]; + + OCMExpect([self.rendererNV12Mock drawFrame:self.frameMock]); + OCMExpect([self.classMock createNV12Renderer]).andReturn(self.rendererNV12Mock); + [[self.classMock reject] createI420Renderer]; + + RTCMTLVideoView *realView = [[RTCMTLVideoView alloc] init]; + [realView renderFrame:self.frameMock]; + [realView drawInMTKView:nil]; + + [self.rendererNV12Mock verify]; + [self.classMock verify]; + + // Get new frame. + self.frameMock = [self frameMockWithCVPixelBuffer:YES]; + OCMExpect([self.rendererNV12Mock drawFrame:self.frameMock]); + + [realView renderFrame:self.frameMock]; + [realView drawInMTKView:nil]; + + [self.rendererNV12Mock verify]; +} + @end