Use std::make_unique<> when creating an unique_ptr object.
[WebKit-https.git] / Source / WebCore / platform / graphics / avfoundation / objc / MediaPlaybackTargetPickerMac.mm
1 /*
2  * Copyright (C) 2015 Apple Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
14  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23  * THE POSSIBILITY OF SUCH DAMAGE.
24  */
25
26 #import "config.h"
27 #import "MediaPlaybackTargetPickerMac.h"
28
29 #if ENABLE(WIRELESS_PLAYBACK_TARGET) && !PLATFORM(IOS)
30
31 #import "Logging.h"
32 #import <WebCore/AVFoundationSPI.h>
33 #import <WebCore/AVKitSPI.h>
34 #import <WebCore/FloatRect.h>
35 #import <WebCore/MediaPlaybackTargetMac.h>
36 #import <WebCore/SoftLinking.h>
37 #import <objc/runtime.h>
38 #import <wtf/MainThread.h>
39
40 typedef AVOutputContext AVOutputContextType;
41 typedef AVOutputDeviceMenuController AVOutputDeviceMenuControllerType;
42
43 SOFT_LINK_FRAMEWORK_OPTIONAL(AVFoundation)
44 SOFT_LINK_FRAMEWORK_OPTIONAL(AVKit)
45
46 SOFT_LINK_CLASS(AVFoundation, AVOutputContext)
47 SOFT_LINK_CLASS(AVKit, AVOutputDeviceMenuController)
48
49 using namespace WebCore;
50
51 static NSString *externalOutputDeviceAvailableKeyName = @"externalOutputDeviceAvailable";
52 static NSString *externalOutputDevicePickedKeyName = @"externalOutputDevicePicked";
53
54 // FIXME: remove this once the headers are available.
55 @interface AVOutputDeviceMenuController (ForwardDeclaration)
56 - (BOOL)showMenuForRect:(NSRect)screenRect appearanceName:(NSString *)appearanceName allowReselectionOfSelectedOutputDevice:(BOOL)allowReselectionOfSelectedOutputDevice;
57 @end
58
59 @interface WebAVOutputDeviceMenuControllerHelper : NSObject {
60     MediaPlaybackTargetPickerMac* m_callback;
61 }
62
63 - (instancetype)initWithCallback:(MediaPlaybackTargetPickerMac*)callback;
64 - (void)clearCallback;
65 - (void)observeValueForKeyPath:(id)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context;
66 @end
67
68 namespace WebCore {
69
70 MediaPlaybackTargetPickerMac::MediaPlaybackTargetPickerMac(MediaPlaybackTargetPicker::Client& client)
71     : MediaPlaybackTargetPicker(client)
72     , m_outputDeviceMenuControllerDelegate(adoptNS([[WebAVOutputDeviceMenuControllerHelper alloc] initWithCallback:this]))
73 {
74 }
75
76 MediaPlaybackTargetPickerMac::~MediaPlaybackTargetPickerMac()
77 {
78     setClient(nullptr);
79     [m_outputDeviceMenuControllerDelegate clearCallback];
80 }
81
82 bool MediaPlaybackTargetPickerMac::externalOutputDeviceAvailable()
83 {
84     return devicePicker().externalOutputDeviceAvailable;
85 }
86
87 Ref<MediaPlaybackTarget> MediaPlaybackTargetPickerMac::playbackTarget()
88 {
89     AVOutputContext* context = m_outputDeviceMenuController ? [m_outputDeviceMenuController.get() outputContext] : nullptr;
90
91     return WebCore::MediaPlaybackTargetMac::create(context);
92 }
93
94 AVOutputDeviceMenuControllerType *MediaPlaybackTargetPickerMac::devicePicker()
95 {
96     if (!getAVOutputDeviceMenuControllerClass())
97         return nullptr;
98
99     if (!m_outputDeviceMenuController) {
100         LOG(Media, "MediaPlaybackTargetPickerMac::devicePicker - allocating picker");
101
102         RetainPtr<AVOutputContextType> context = adoptNS([[getAVOutputContextClass() alloc] init]);
103         m_outputDeviceMenuController = adoptNS([[getAVOutputDeviceMenuControllerClass() alloc] initWithOutputContext:context.get()]);
104
105         [m_outputDeviceMenuController.get() addObserver:m_outputDeviceMenuControllerDelegate.get() forKeyPath:externalOutputDeviceAvailableKeyName options:NSKeyValueObservingOptionNew context:nullptr];
106         [m_outputDeviceMenuController.get() addObserver:m_outputDeviceMenuControllerDelegate.get() forKeyPath:externalOutputDevicePickedKeyName options:NSKeyValueObservingOptionNew context:nullptr];
107
108         LOG(Media, "MediaPlaybackTargetPickerMac::devicePicker - allocated menu controller %p", m_outputDeviceMenuController.get());
109
110         if (m_outputDeviceMenuController.get().externalOutputDeviceAvailable)
111             availableDevicesDidChange();
112     }
113
114     return m_outputDeviceMenuController.get();
115 }
116
117 void MediaPlaybackTargetPickerMac::showPlaybackTargetPicker(const FloatRect& location, bool checkActiveRoute)
118 {
119     if (!client() || m_showingMenu)
120         return;
121
122     LOG(Media, "MediaPlaybackTargetPickerMac::showPlaybackTargetPicker - checkActiveRoute = %i", (int)checkActiveRoute);
123
124     AVOutputDeviceMenuControllerType *picker = devicePicker();
125     if (![picker respondsToSelector:@selector(showMenuForRect:appearanceName:allowReselectionOfSelectedOutputDevice:)])
126         return;
127
128     m_showingMenu = true;
129     if ([picker showMenuForRect:location appearanceName:NSAppearanceNameVibrantLight allowReselectionOfSelectedOutputDevice:!checkActiveRoute]) {
130         if (!checkActiveRoute)
131             currentDeviceDidChange();
132     }
133     m_showingMenu = false;
134 }
135
136 void MediaPlaybackTargetPickerMac::startingMonitoringPlaybackTargets()
137 {
138     LOG(Media, "MediaPlaybackTargetPickerMac::startingMonitoringPlaybackTargets");
139
140     devicePicker();
141 }
142
143 void MediaPlaybackTargetPickerMac::stopMonitoringPlaybackTargets()
144 {
145     LOG(Media, "MediaPlaybackTargetPickerMac::stopMonitoringPlaybackTargets");
146     // Nothing to do, AirPlay takes care of this automatically.
147 }
148
149 void MediaPlaybackTargetPickerMac::invalidatePlaybackTargets()
150 {
151     LOG(Media, "MediaPlaybackTargetPickerMac::invalidatePlaybackTargets");
152
153     if (m_outputDeviceMenuController) {
154         [m_outputDeviceMenuController removeObserver:m_outputDeviceMenuControllerDelegate.get() forKeyPath:externalOutputDeviceAvailableKeyName];
155         [m_outputDeviceMenuController removeObserver:m_outputDeviceMenuControllerDelegate.get() forKeyPath:externalOutputDevicePickedKeyName];
156         m_outputDeviceMenuController = nullptr;
157     }
158     currentDeviceDidChange();
159 }
160
161 } // namespace WebCore
162
163 @implementation WebAVOutputDeviceMenuControllerHelper
164 - (instancetype)initWithCallback:(MediaPlaybackTargetPickerMac*)callback
165 {
166     if (!(self = [super init]))
167         return nil;
168
169     m_callback = callback;
170
171     return self;
172 }
173
174 - (void)clearCallback
175 {
176     m_callback = nil;
177 }
178
179 - (void)observeValueForKeyPath:(id)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
180 {
181     UNUSED_PARAM(object);
182     UNUSED_PARAM(change);
183     UNUSED_PARAM(context);
184
185     if (!m_callback)
186         return;
187
188     LOG(Media, "MediaPlaybackTargetPickerMac::observeValueForKeyPath - key = %s", [keyPath UTF8String]);
189
190     if (![keyPath isEqualToString:externalOutputDeviceAvailableKeyName] && ![keyPath isEqualToString:externalOutputDevicePickedKeyName])
191         return;
192
193     RetainPtr<WebAVOutputDeviceMenuControllerHelper> strongSelf = self;
194     RetainPtr<NSString> strongKeyPath = keyPath;
195     callOnMainThread([strongSelf, strongKeyPath] {
196         MediaPlaybackTargetPickerMac* callback = strongSelf->m_callback;
197         if (!callback)
198             return;
199
200         if ([strongKeyPath isEqualToString:externalOutputDeviceAvailableKeyName])
201             callback->availableDevicesDidChange();
202         else if ([strongKeyPath isEqualToString:externalOutputDevicePickedKeyName])
203             callback->currentDeviceDidChange();
204     });
205 }
206 @end
207
208 #endif // ENABLE(WIRELESS_PLAYBACK_TARGET)