e2b425818c94b27b0735c5cecbbbe78a5c7e55d3
[WebKit-https.git] / Source / WebCore / platform / ios / PlaybackSessionInterfaceAVKit.mm
1 /*
2  * Copyright (C) 2014, 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. ``AS IS'' AND ANY
14  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
17  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24  */
25
26
27 #import "config.h"
28 #import "PlaybackSessionInterfaceAVKit.h"
29
30 #if PLATFORM(IOS_FAMILY)
31 #if HAVE(AVKIT)
32
33 #import "Logging.h"
34 #import "MediaSelectionOption.h"
35 #import "PlaybackSessionModel.h"
36 #import "TimeRanges.h"
37 #import "WebAVPlayerController.h"
38 #import <AVFoundation/AVTime.h>
39 #import <pal/spi/cocoa/AVKitSPI.h>
40 #import <wtf/RetainPtr.h>
41 #import <wtf/text/CString.h>
42 #import <wtf/text/WTFString.h>
43
44 #import <pal/cf/CoreMediaSoftLink.h>
45
46 SOFTLINK_AVKIT_FRAMEWORK()
47 SOFT_LINK_CLASS_OPTIONAL(AVKit, AVValueTiming)
48
49 namespace WebCore {
50 using namespace PAL;
51
52 PlaybackSessionInterfaceAVKit::PlaybackSessionInterfaceAVKit(PlaybackSessionModel& model)
53     : m_playerController(adoptNS([[WebAVPlayerController alloc] init]))
54     , m_playbackSessionModel(&model)
55 {
56     model.addClient(*this);
57     [m_playerController setPlaybackSessionInterface:this];
58     [m_playerController setDelegate:&model];
59
60     durationChanged(model.duration());
61     currentTimeChanged(model.currentTime(), [[NSProcessInfo processInfo] systemUptime]);
62     bufferedTimeChanged(model.bufferedTime());
63     rateChanged(model.isPlaying(), model.playbackRate());
64     seekableRangesChanged(model.seekableRanges(), model.seekableTimeRangesLastModifiedTime(), model.liveUpdateInterval());
65     canPlayFastReverseChanged(model.canPlayFastReverse());
66     audioMediaSelectionOptionsChanged(model.audioMediaSelectionOptions(), model.audioMediaSelectedIndex());
67     legibleMediaSelectionOptionsChanged(model.legibleMediaSelectionOptions(), model.legibleMediaSelectedIndex());
68     externalPlaybackChanged(model.externalPlaybackEnabled(), model.externalPlaybackTargetType(), model.externalPlaybackLocalizedDeviceName());
69     wirelessVideoPlaybackDisabledChanged(model.wirelessVideoPlaybackDisabled());
70 }
71
72 PlaybackSessionInterfaceAVKit::~PlaybackSessionInterfaceAVKit()
73 {
74     [m_playerController setPlaybackSessionInterface:nullptr];
75     [m_playerController setExternalPlaybackActive:false];
76
77     invalidate();
78 }
79
80 void PlaybackSessionInterfaceAVKit::durationChanged(double duration)
81 {
82     WebAVPlayerController* playerController = m_playerController.get();
83
84     playerController.contentDuration = duration;
85     playerController.contentDurationWithinEndTimes = duration;
86
87     // FIXME: we take this as an indication that playback is ready.
88     playerController.canPlay = YES;
89     playerController.canPause = YES;
90     playerController.canTogglePlayback = YES;
91     playerController.hasEnabledAudio = YES;
92     playerController.canSeek = YES;
93     playerController.status = AVPlayerControllerStatusReadyToPlay;
94 }
95
96 void PlaybackSessionInterfaceAVKit::currentTimeChanged(double currentTime, double anchorTime)
97 {
98     if ([m_playerController isScrubbing])
99         return;
100
101     NSTimeInterval anchorTimeStamp = ![m_playerController rate] ? NAN : anchorTime;
102     AVValueTiming *timing = [getAVValueTimingClass() valueTimingWithAnchorValue:currentTime
103         anchorTimeStamp:anchorTimeStamp rate:0];
104
105     [m_playerController setTiming:timing];
106 }
107
108 void PlaybackSessionInterfaceAVKit::bufferedTimeChanged(double bufferedTime)
109 {
110     WebAVPlayerController* playerController = m_playerController.get();
111     double duration = playerController.contentDuration;
112     double normalizedBufferedTime;
113     if (!duration)
114         normalizedBufferedTime = 0;
115     else
116         normalizedBufferedTime = bufferedTime / duration;
117     playerController.loadedTimeRanges = @[@0, @(normalizedBufferedTime)];
118 }
119
120 void PlaybackSessionInterfaceAVKit::rateChanged(bool isPlaying, float playbackRate)
121 {
122     [m_playerController setRate:isPlaying ? playbackRate : 0.];
123 }
124
125 void PlaybackSessionInterfaceAVKit::seekableRangesChanged(const TimeRanges& timeRanges, double lastModifiedTime, double liveUpdateInterval)
126 {
127     RetainPtr<NSMutableArray> seekableRanges = adoptNS([[NSMutableArray alloc] init]);
128
129 #if !PLATFORM(WATCHOS)
130     for (unsigned i = 0; i < timeRanges.length(); i++) {
131         double start = timeRanges.start(i).releaseReturnValue();
132         double end = timeRanges.end(i).releaseReturnValue();
133
134         CMTimeRange range = CMTimeRangeMake(CMTimeMakeWithSeconds(start, 1000), CMTimeMakeWithSeconds(end-start, 1000));
135         [seekableRanges addObject:[NSValue valueWithCMTimeRange:range]];
136     }
137 #else
138     UNUSED_PARAM(timeRanges);
139 #endif
140
141     [m_playerController setSeekableTimeRanges:seekableRanges.get()];
142     [m_playerController setSeekableTimeRangesLastModifiedTime: lastModifiedTime];
143     [m_playerController setLiveUpdateInterval:liveUpdateInterval];
144 }
145
146 void PlaybackSessionInterfaceAVKit::canPlayFastReverseChanged(bool canPlayFastReverse)
147 {
148     [m_playerController setCanScanBackward:canPlayFastReverse];
149 }
150
151 static RetainPtr<NSMutableArray> mediaSelectionOptions(const Vector<MediaSelectionOption>& options)
152 {
153     RetainPtr<NSMutableArray> webOptions = adoptNS([[NSMutableArray alloc] initWithCapacity:options.size()]);
154     for (auto& option : options) {
155         RetainPtr<WebAVMediaSelectionOption> webOption = adoptNS([[WebAVMediaSelectionOption alloc] init]);
156         [webOption setLocalizedDisplayName:option.displayName];
157         [webOptions addObject:webOption.get()];
158     }
159     return webOptions;
160 }
161
162 void PlaybackSessionInterfaceAVKit::audioMediaSelectionOptionsChanged(const Vector<MediaSelectionOption>& options, uint64_t selectedIndex)
163 {
164     RetainPtr<NSMutableArray> webOptions = mediaSelectionOptions(options);
165     [m_playerController setAudioMediaSelectionOptions:webOptions.get()];
166     if (selectedIndex < [webOptions count])
167         [m_playerController setCurrentAudioMediaSelectionOption:[webOptions objectAtIndex:static_cast<NSUInteger>(selectedIndex)]];
168 }
169
170 void PlaybackSessionInterfaceAVKit::legibleMediaSelectionOptionsChanged(const Vector<MediaSelectionOption>& options, uint64_t selectedIndex)
171 {
172     RetainPtr<NSMutableArray> webOptions = mediaSelectionOptions(options);
173     [m_playerController setLegibleMediaSelectionOptions:webOptions.get()];
174     if (selectedIndex < [webOptions count])
175         [m_playerController setCurrentLegibleMediaSelectionOption:[webOptions objectAtIndex:static_cast<NSUInteger>(selectedIndex)]];
176 }
177
178 void PlaybackSessionInterfaceAVKit::externalPlaybackChanged(bool enabled, PlaybackSessionModel::ExternalPlaybackTargetType targetType, const String& localizedDeviceName)
179 {
180     AVPlayerControllerExternalPlaybackType externalPlaybackType = AVPlayerControllerExternalPlaybackTypeNone;
181     if (targetType == PlaybackSessionModel::TargetTypeAirPlay)
182         externalPlaybackType = AVPlayerControllerExternalPlaybackTypeAirPlay;
183     else if (targetType == PlaybackSessionModel::TargetTypeTVOut)
184         externalPlaybackType = AVPlayerControllerExternalPlaybackTypeTVOut;
185
186     WebAVPlayerController* playerController = m_playerController.get();
187     playerController.externalPlaybackAirPlayDeviceLocalizedName = localizedDeviceName;
188     playerController.externalPlaybackType = externalPlaybackType;
189     playerController.externalPlaybackActive = enabled;
190 }
191
192 void PlaybackSessionInterfaceAVKit::wirelessVideoPlaybackDisabledChanged(bool disabled)
193 {
194     [m_playerController setAllowsExternalPlayback:!disabled];
195 }
196
197 void PlaybackSessionInterfaceAVKit::mutedChanged(bool muted)
198 {
199     [m_playerController setMuted:muted];
200 }
201
202 void PlaybackSessionInterfaceAVKit::volumeChanged(double volume)
203 {
204     [m_playerController volumeChanged:volume];
205 }
206
207 void PlaybackSessionInterfaceAVKit::invalidate()
208 {
209     if (!m_playbackSessionModel)
210         return;
211
212     [m_playerController setDelegate:nullptr];
213     m_playbackSessionModel->removeClient(*this);
214     m_playbackSessionModel = nullptr;
215 }
216
217 }
218
219 #endif // HAVE(AVKIT)
220 #endif // PLATFORM(IOS_FAMILY)