ccbb0c40ec64a3b7288900db49e8eaded0cae14f
[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     NSTimeInterval anchorTimeStamp = ![m_playerController rate] ? NAN : anchorTime;
99     AVValueTiming *timing = [getAVValueTimingClass() valueTimingWithAnchorValue:currentTime
100         anchorTimeStamp:anchorTimeStamp rate:0];
101
102     [m_playerController setTiming:timing];
103 }
104
105 void PlaybackSessionInterfaceAVKit::bufferedTimeChanged(double bufferedTime)
106 {
107     WebAVPlayerController* playerController = m_playerController.get();
108     double duration = playerController.contentDuration;
109     double normalizedBufferedTime;
110     if (!duration)
111         normalizedBufferedTime = 0;
112     else
113         normalizedBufferedTime = bufferedTime / duration;
114     playerController.loadedTimeRanges = @[@0, @(normalizedBufferedTime)];
115 }
116
117 void PlaybackSessionInterfaceAVKit::rateChanged(bool isPlaying, float playbackRate)
118 {
119     [m_playerController setRate:isPlaying ? playbackRate : 0.];
120 }
121
122 void PlaybackSessionInterfaceAVKit::seekableRangesChanged(const TimeRanges& timeRanges, double lastModifiedTime, double liveUpdateInterval)
123 {
124     RetainPtr<NSMutableArray> seekableRanges = adoptNS([[NSMutableArray alloc] init]);
125
126 #if !PLATFORM(WATCHOS)
127     for (unsigned i = 0; i < timeRanges.length(); i++) {
128         double start = timeRanges.start(i).releaseReturnValue();
129         double end = timeRanges.end(i).releaseReturnValue();
130
131         CMTimeRange range = CMTimeRangeMake(CMTimeMakeWithSeconds(start, 1000), CMTimeMakeWithSeconds(end-start, 1000));
132         [seekableRanges addObject:[NSValue valueWithCMTimeRange:range]];
133     }
134 #else
135     UNUSED_PARAM(timeRanges);
136 #endif
137
138     [m_playerController setSeekableTimeRanges:seekableRanges.get()];
139     [m_playerController setSeekableTimeRangesLastModifiedTime: lastModifiedTime];
140     [m_playerController setLiveUpdateInterval:liveUpdateInterval];
141 }
142
143 void PlaybackSessionInterfaceAVKit::canPlayFastReverseChanged(bool canPlayFastReverse)
144 {
145     [m_playerController setCanScanBackward:canPlayFastReverse];
146 }
147
148 static RetainPtr<NSMutableArray> mediaSelectionOptions(const Vector<MediaSelectionOption>& options)
149 {
150     RetainPtr<NSMutableArray> webOptions = adoptNS([[NSMutableArray alloc] initWithCapacity:options.size()]);
151     for (auto& option : options) {
152         RetainPtr<WebAVMediaSelectionOption> webOption = adoptNS([[WebAVMediaSelectionOption alloc] init]);
153         [webOption setLocalizedDisplayName:option.displayName];
154         [webOptions addObject:webOption.get()];
155     }
156     return webOptions;
157 }
158
159 void PlaybackSessionInterfaceAVKit::audioMediaSelectionOptionsChanged(const Vector<MediaSelectionOption>& options, uint64_t selectedIndex)
160 {
161     RetainPtr<NSMutableArray> webOptions = mediaSelectionOptions(options);
162     [m_playerController setAudioMediaSelectionOptions:webOptions.get()];
163     if (selectedIndex < [webOptions count])
164         [m_playerController setCurrentAudioMediaSelectionOption:[webOptions objectAtIndex:static_cast<NSUInteger>(selectedIndex)]];
165 }
166
167 void PlaybackSessionInterfaceAVKit::legibleMediaSelectionOptionsChanged(const Vector<MediaSelectionOption>& options, uint64_t selectedIndex)
168 {
169     RetainPtr<NSMutableArray> webOptions = mediaSelectionOptions(options);
170     [m_playerController setLegibleMediaSelectionOptions:webOptions.get()];
171     if (selectedIndex < [webOptions count])
172         [m_playerController setCurrentLegibleMediaSelectionOption:[webOptions objectAtIndex:static_cast<NSUInteger>(selectedIndex)]];
173 }
174
175 void PlaybackSessionInterfaceAVKit::externalPlaybackChanged(bool enabled, PlaybackSessionModel::ExternalPlaybackTargetType targetType, const String& localizedDeviceName)
176 {
177     AVPlayerControllerExternalPlaybackType externalPlaybackType = AVPlayerControllerExternalPlaybackTypeNone;
178     if (targetType == PlaybackSessionModel::TargetTypeAirPlay)
179         externalPlaybackType = AVPlayerControllerExternalPlaybackTypeAirPlay;
180     else if (targetType == PlaybackSessionModel::TargetTypeTVOut)
181         externalPlaybackType = AVPlayerControllerExternalPlaybackTypeTVOut;
182
183     WebAVPlayerController* playerController = m_playerController.get();
184     playerController.externalPlaybackAirPlayDeviceLocalizedName = localizedDeviceName;
185     playerController.externalPlaybackType = externalPlaybackType;
186     playerController.externalPlaybackActive = enabled;
187 }
188
189 void PlaybackSessionInterfaceAVKit::wirelessVideoPlaybackDisabledChanged(bool disabled)
190 {
191     [m_playerController setAllowsExternalPlayback:!disabled];
192 }
193
194 void PlaybackSessionInterfaceAVKit::mutedChanged(bool muted)
195 {
196     [m_playerController setMuted:muted];
197 }
198
199 void PlaybackSessionInterfaceAVKit::volumeChanged(double volume)
200 {
201     [m_playerController volumeChanged:volume];
202 }
203
204 void PlaybackSessionInterfaceAVKit::invalidate()
205 {
206     if (!m_playbackSessionModel)
207         return;
208
209     [m_playerController setDelegate:nullptr];
210     m_playbackSessionModel->removeClient(*this);
211     m_playbackSessionModel = nullptr;
212 }
213
214 }
215
216 #endif // HAVE(AVKIT)
217 #endif // PLATFORM(IOS_FAMILY)