[MediaStream] Sync video preview layer and parent layer sizes
authoreric.carlson@apple.com <eric.carlson@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 30 Nov 2016 19:20:32 +0000 (19:20 +0000)
committereric.carlson@apple.com <eric.carlson@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 30 Nov 2016 19:20:32 +0000 (19:20 +0000)
https://bugs.webkit.org/show_bug.cgi?id=165139

Reviewed by Jer Noble.

CALayer auto-resizing doesn't work on iOS, so manually reize the video capture preview layer
whenever the background layer's bounds change.

* platform/mediastream/mac/AVVideoCaptureSource.mm:
(WebCore::AVVideoSourcePreview::AVVideoSourcePreview): Create layer observer.
(WebCore::AVVideoSourcePreview::backgroundLayerBoundsChanged): Sync preview layer and background
  layer sizes.
(WebCore::AVVideoSourcePreview::invalidate): Invalidate and clear observer.
(-[WebCoreAVVideoCaptureSourceObserver initWithParent:]):
(-[WebCoreAVVideoCaptureSourceObserver setParent:]): Add KVO bounds observer.
(-[WebCoreAVVideoCaptureSourceObserver observeValueForKeyPath:ofObject:change:context:]): Call
parent when "bounds" changes.
(-[WebCoreAVVideoCaptureSourceObserver actionForKey:]): Return nil to disable all animations.

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@209141 268f45cc-cd09-0410-ab3c-d52691b4dbfc

Source/WebCore/ChangeLog
Source/WebCore/platform/mediastream/mac/AVVideoCaptureSource.mm

index 38caec7..773ba68 100644 (file)
@@ -1,3 +1,24 @@
+2016-11-30  Eric Carlson  <eric.carlson@apple.com>
+
+        [MediaStream] Sync video preview layer and parent layer sizes
+        https://bugs.webkit.org/show_bug.cgi?id=165139
+
+        Reviewed by Jer Noble.
+
+        CALayer auto-resizing doesn't work on iOS, so manually reize the video capture preview layer
+        whenever the background layer's bounds change.
+
+        * platform/mediastream/mac/AVVideoCaptureSource.mm:
+        (WebCore::AVVideoSourcePreview::AVVideoSourcePreview): Create layer observer.
+        (WebCore::AVVideoSourcePreview::backgroundLayerBoundsChanged): Sync preview layer and background
+          layer sizes.
+        (WebCore::AVVideoSourcePreview::invalidate): Invalidate and clear observer.
+        (-[WebCoreAVVideoCaptureSourceObserver initWithParent:]):
+        (-[WebCoreAVVideoCaptureSourceObserver setParent:]): Add KVO bounds observer.
+        (-[WebCoreAVVideoCaptureSourceObserver observeValueForKeyPath:ofObject:change:context:]): Call
+        parent when "bounds" changes.
+        (-[WebCoreAVVideoCaptureSourceObserver actionForKey:]): Return nil to disable all animations.
+
 2016-11-30  Dave Hyatt  <hyatt@apple.com>
 
         [CSS Parser] Fix crash in -webkit-shape-outside parsing
index ef5e45c..f4ab33a 100644 (file)
 #import "RealtimeMediaSourceCenter.h"
 #import "RealtimeMediaSourcePreview.h"
 #import "RealtimeMediaSourceSettings.h"
+#import "WebActionDisablingCALayerDelegate.h"
 #import <AVFoundation/AVFoundation.h>
 #import <objc/runtime.h>
 
+#if PLATFORM(IOS)
+#include "WebCoreThread.h"
+#include "WebCoreThreadRun.h"
+#endif
+
 #import "CoreMediaSoftLink.h"
 #import "CoreVideoSoftLink.h"
 
@@ -79,7 +85,7 @@ SOFT_LINK_POINTER(AVFoundation, AVCaptureSessionPreset1280x720, NSString *)
 SOFT_LINK_POINTER(AVFoundation, AVCaptureSessionPreset960x540, NSString *)
 SOFT_LINK_POINTER(AVFoundation, AVCaptureSessionPreset640x480, NSString *)
 SOFT_LINK_POINTER(AVFoundation, AVCaptureSessionPreset352x288, NSString *)
-SOFT_LINK_POINTER(AVFoundation, AVCaptureSessionPreset320x240, NSString*)
+SOFT_LINK_POINTER(AVFoundation, AVCaptureSessionPreset320x240, NSString *)
 SOFT_LINK_POINTER(AVFoundation, AVCaptureSessionPresetLow, NSString *)
 
 #define AVCaptureSessionPreset1280x720 getAVCaptureSessionPreset1280x720()
@@ -89,14 +95,28 @@ SOFT_LINK_POINTER(AVFoundation, AVCaptureSessionPresetLow, NSString *)
 #define AVCaptureSessionPreset320x240 getAVCaptureSessionPreset320x240()
 #define AVCaptureSessionPresetLow getAVCaptureSessionPresetLow()
 
+using namespace WebCore;
+
+@interface WebCoreAVVideoCaptureSourceObserver : NSObject<CALayerDelegate> {
+    AVVideoSourcePreview *_parent;
+    BOOL _hasObserver;
+}
+
+- (void)setParent:(AVVideoSourcePreview *)parent;
+- (void)observeValueForKeyPath:keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context;
+@end
+
 namespace WebCore {
 
 class AVVideoSourcePreview: public AVMediaSourcePreview {
 public:
-    static RefPtr<AVMediaSourcePreview> create(AVCaptureSession *, AVCaptureDeviceTypedef *, AVVideoCaptureSource*);
+    static RefPtr<AVMediaSourcePreview> create(AVCaptureSession*, AVCaptureDeviceTypedef*, AVVideoCaptureSource*);
+
+    void backgroundLayerBoundsChanged();
+    PlatformLayer* platformLayer() const final { return m_previewBackgroundLayer.get(); }
 
 private:
-    AVVideoSourcePreview(AVCaptureSession *, AVCaptureDeviceTypedef *, AVVideoCaptureSource*);
+    AVVideoSourcePreview(AVCaptureSession*, AVCaptureDeviceTypedef*, AVVideoCaptureSource*);
 
     void invalidate() final;
 
@@ -104,13 +124,12 @@ private:
     void pause() const final;
     void setVolume(double) const final { };
     void setEnabled(bool) final;
-    PlatformLayer* platformLayer() const final { return m_previewBackgroundLayer.get(); }
-    
     void setPaused(bool) const;
 
     RetainPtr<AVCaptureVideoPreviewLayerType> m_previewLayer;
     RetainPtr<PlatformLayer> m_previewBackgroundLayer;
     RetainPtr<AVCaptureDeviceTypedef> m_device;
+    RetainPtr<WebCoreAVVideoCaptureSourceObserver> m_objcObserver;
 };
 
 RefPtr<AVMediaSourcePreview> AVVideoSourcePreview::create(AVCaptureSession *session, AVCaptureDeviceTypedef* device, AVVideoCaptureSource* parent)
@@ -120,33 +139,40 @@ RefPtr<AVMediaSourcePreview> AVVideoSourcePreview::create(AVCaptureSession *sess
 
 AVVideoSourcePreview::AVVideoSourcePreview(AVCaptureSession *session, AVCaptureDeviceTypedef* device, AVVideoCaptureSource* parent)
     : AVMediaSourcePreview(parent)
+    , m_objcObserver(adoptNS([[WebCoreAVVideoCaptureSourceObserver alloc] init]))
 {
     m_device = device;
     m_previewLayer = adoptNS([allocAVCaptureVideoPreviewLayerInstance() initWithSession:session]);
-#ifndef NDEBUG
-    m_previewLayer.get().name = @"AVVideoCaptureSource preview layer";
-#endif
-
     m_previewLayer.get().contentsGravity = kCAGravityResize;
     m_previewLayer.get().anchorPoint = CGPointZero;
-#if !PLATFORM(IOS)
-    m_previewLayer.get().autoresizingMask = kCALayerWidthSizable | kCALayerHeightSizable;
-#endif
+    [m_previewLayer.get() setDelegate:[WebActionDisablingCALayerDelegate shared]];
 
     m_previewBackgroundLayer = adoptNS([[CALayer alloc] init]);
-    m_previewBackgroundLayer.get().name = @"AVVideoSourcePreview parent layer";
     m_previewBackgroundLayer.get().contentsGravity = kCAGravityResizeAspect;
     m_previewBackgroundLayer.get().anchorPoint = CGPointZero;
     m_previewBackgroundLayer.get().needsDisplayOnBoundsChange = YES;
-#if !PLATFORM(IOS)
-    m_previewBackgroundLayer.get().autoresizingMask = kCALayerWidthSizable | kCALayerHeightSizable;
+    [m_previewBackgroundLayer.get() setDelegate:[WebActionDisablingCALayerDelegate shared]];
+
+#ifndef NDEBUG
+    m_previewLayer.get().name = @"AVVideoCaptureSource preview layer";
+    m_previewBackgroundLayer.get().name = @"AVVideoSourcePreview parent layer";
 #endif
 
     [m_previewBackgroundLayer addSublayer:m_previewLayer.get()];
+
+    [m_objcObserver.get() setParent:this];
+}
+
+void AVVideoSourcePreview::backgroundLayerBoundsChanged()
+{
+    if (m_previewBackgroundLayer && m_previewLayer)
+        [m_previewLayer.get() setBounds:m_previewBackgroundLayer.get().bounds];
 }
 
 void AVVideoSourcePreview::invalidate()
 {
+    [m_objcObserver.get() setParent:nil];
+    m_objcObserver = nullptr;
     m_previewLayer = nullptr;
     m_previewBackgroundLayer = nullptr;
     m_device = nullptr;
@@ -587,4 +613,46 @@ bool AVVideoCaptureSource::supportsSizeAndFrameRate(std::optional<int> width, st
 
 } // namespace WebCore
 
+@implementation WebCoreAVVideoCaptureSourceObserver
+
+static NSString * const KeyValueBoundsChangeKey = @"bounds";
+
+- (void)setParent:(AVVideoSourcePreview *)parent
+{
+    if (_parent && _hasObserver && _parent->platformLayer()) {
+        _hasObserver = false;
+        [_parent->platformLayer() removeObserver:self forKeyPath:KeyValueBoundsChangeKey];
+    }
+
+    _parent = parent;
+
+    if (_parent && _parent->platformLayer()) {
+        _hasObserver = true;
+        [_parent->platformLayer() addObserver:self forKeyPath:KeyValueBoundsChangeKey options:0 context:nullptr];
+    }
+}
+
+- (void)observeValueForKeyPath:keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
+{
+    UNUSED_PARAM(context);
+
+    if (!_parent)
+        return;
+
+    if ([[change valueForKey:NSKeyValueChangeNotificationIsPriorKey] boolValue])
+        return;
+
+#if PLATFORM(IOS)
+    WebThreadRun(^ {
+        if ([keyPath isEqual:KeyValueBoundsChangeKey] && object == _parent->platformLayer())
+            _parent->backgroundLayerBoundsChanged();
+    });
+#else
+    if ([keyPath isEqual:KeyValueBoundsChangeKey] && object == _parent->platformLayer())
+        _parent->backgroundLayerBoundsChanged();
+#endif
+}
+
+@end
+
 #endif // ENABLE(MEDIA_STREAM)