1/*
2 * Copyright (C) 2017 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#include "config.h"
27#include "MediaCapabilities.h"
28
29#include "ContentType.h"
30#include "Document.h"
31#include "JSMediaCapabilitiesDecodingInfo.h"
32#include "JSMediaCapabilitiesEncodingInfo.h"
33#include "MediaCapabilitiesDecodingInfo.h"
34#include "MediaCapabilitiesEncodingInfo.h"
35#include "MediaDecodingConfiguration.h"
36#include "MediaEncodingConfiguration.h"
37#include "MediaEngineConfigurationFactory.h"
38#include "Settings.h"
39#include <wtf/HashSet.h>
40
41namespace WebCore {
42
43static const HashSet<String>& bucketMIMETypes()
44{
45 // A "bucket" MIME types is one whose container type does not uniquely specify a codec.
46 // See: https://tools.ietf.org/html/rfc6381
47 static NeverDestroyed<HashSet<String>> bucketMIMETypes = HashSet<String>({
48 "audio/3gpp",
49 "video/3gpp",
50 "audio/3gpp2",
51 "video/3gpp2",
52 "audio/mp4",
53 "video/mp4",
54 "application/mp4",
55 "video/quicktime",
56 "application/mp21",
57 "audio/vnd.apple.mpegurl",
58 "video/vnd.apple.mpegurl",
59 "audio/ogg",
60 "video/ogg",
61 "video/webm",
62 "audio/webm",
63 });
64 return bucketMIMETypes;
65}
66
67static bool isValidMediaMIMEType(const ContentType& contentType)
68{
69 // 2.1.4. MIME types
70 // https://wicg.github.io/media-capabilities/#valid-media-mime-type
71 // A valid media MIME type is a string that is a valid MIME type per [mimesniff]. If the MIME type does
72 // not imply a codec, the string MUST also have one and only one parameter that is named codecs with a
73 // value describing a single media codec. Otherwise, it MUST contain no parameters.
74 if (contentType.isEmpty())
75 return false;
76
77 auto codecs = contentType.codecs();
78
79 // FIXME: The spec requires that the "codecs" parameter is the only parameter present.
80 if (bucketMIMETypes().contains(contentType.containerType()))
81 return codecs.size() == 1;
82 return !codecs.size();
83}
84
85static bool isValidVideoMIMEType(const ContentType& contentType)
86{
87 // 2.1.4 MIME Types
88 // https://wicg.github.io/media-capabilities/#valid-video-mime-type
89 // A valid video MIME type is a string that is a valid media MIME type and for which the type per [RFC7231]
90 // is either video or application.
91 if (!isValidMediaMIMEType(contentType))
92 return false;
93
94 auto containerType = contentType.containerType();
95 if (!startsWithLettersIgnoringASCIICase(containerType, "video/") && !startsWithLettersIgnoringASCIICase(containerType, "application/"))
96 return false;
97
98 return true;
99}
100
101static bool isValidAudioMIMEType(const ContentType& contentType)
102{
103 // 2.1.4 MIME Types
104 // https://wicg.github.io/media-capabilities/#valid-audio-mime-type
105 // A valid audio MIME type is a string that is a valid media MIME type and for which the type per [RFC7231]
106 // is either audio or application.
107 if (!isValidMediaMIMEType(contentType))
108 return false;
109
110 auto containerType = contentType.containerType();
111 if (!startsWithLettersIgnoringASCIICase(containerType, "audio/") && !startsWithLettersIgnoringASCIICase(containerType, "application/"))
112 return false;
113
114 return true;
115}
116
117static bool isValidVideoConfiguration(const VideoConfiguration& configuration)
118{
119 // 2.1.5. VideoConfiguration
120 // https://wicg.github.io/media-capabilities/#valid-video-configuration
121 // 1. If configuration’s contentType is not a valid video MIME type, return false and abort these steps.
122 if (!isValidVideoMIMEType(ContentType(configuration.contentType)))
123 return false;
124
125 // 2. If none of the following is true, return false and abort these steps:
126 // o. Applying the rules for parsing floating-point number values to configuration’s framerate
127 // results in a number that is finite and greater than 0.
128 if (!std::isfinite(configuration.framerate) || configuration.framerate <= 0)
129 return false;
130
131 // 3. Return true.
132 return true;
133}
134
135static bool isValidAudioConfiguration(const AudioConfiguration& configuration)
136{
137 // 2.1.6. AudioConfiguration
138 // https://wicg.github.io/media-capabilities/#audioconfiguration
139 // 1. If configuration’s contentType is not a valid audio MIME type, return false and abort these steps.
140 if (!isValidAudioMIMEType(ContentType(configuration.contentType)))
141 return false;
142
143 // 2. Return true.
144 return true;
145}
146
147static bool isValidMediaConfiguration(const MediaConfiguration& configuration)
148{
149 // 2.1.1. MediaConfiguration
150 // https://wicg.github.io/media-capabilities/#mediaconfiguration
151 // For a MediaConfiguration to be a valid MediaConfiguration, audio or video MUST be present.
152 if (!configuration.video && !configuration.audio)
153 return false;
154
155 if (configuration.video && !isValidVideoConfiguration(configuration.video.value()))
156 return false;
157
158 if (configuration.audio && !isValidAudioConfiguration(configuration.audio.value()))
159 return false;
160
161 return true;
162}
163
164void MediaCapabilities::decodingInfo(Document& document, MediaDecodingConfiguration&& configuration, Ref<DeferredPromise>&& promise)
165{
166 // 2.4 Media Capabilities Interface
167 // https://wicg.github.io/media-capabilities/#media-capabilities-interface
168
169 // 1. If configuration is not a valid MediaConfiguration, return a Promise rejected with a TypeError.
170 // 2. If configuration.video is present and is not a valid video configuration, return a Promise rejected with a TypeError.
171 // 3. If configuration.audio is present and is not a valid audio configuration, return a Promise rejected with a TypeError.
172 if (!isValidMediaConfiguration(configuration)) {
173 promise->reject(TypeError);
174 return;
175 }
176
177 if (!document.settings().mediaCapabilitiesExtensionsEnabled() && configuration.video)
178 configuration.video.value().alphaChannel.reset();
179
180 // 4. Let p be a new promise.
181 // 5. In parallel, run the create a MediaCapabilitiesInfo algorithm with configuration and resolve p with its result.
182 // 6. Return p.
183 m_taskQueue.enqueueTask([configuration = WTFMove(configuration), promise = WTFMove(promise)] () mutable {
184
185 // 2.2.3 If configuration is of type MediaDecodingConfiguration, run the following substeps:
186 MediaEngineConfigurationFactory::DecodingConfigurationCallback callback = [promise = WTFMove(promise)] (auto info) mutable {
187 // 2.2.3.1. If the user agent is able to decode the media represented by
188 // configuration, set supported to true. Otherwise set it to false.
189 // 2.2.3.2. If the user agent is able to decode the media represented by
190 // configuration at a pace that allows a smooth playback, set smooth to
191 // true. Otherwise set it to false.
192 // 2.2.3.3. If the user agent is able to decode the media represented by
193 // configuration in a power efficient manner, set powerEfficient to
194 // true. Otherwise set it to false. The user agent SHOULD NOT take into
195 // consideration the current power source in order to determine the
196 // decoding power efficiency unless the device’s power source has side
197 // effects such as enabling different decoding modules.
198 promise->resolve<IDLDictionary<MediaCapabilitiesDecodingInfo>>(WTFMove(info));
199 };
200
201 MediaEngineConfigurationFactory::createDecodingConfiguration(WTFMove(configuration), WTFMove(callback));
202 });
203}
204
205void MediaCapabilities::encodingInfo(MediaEncodingConfiguration&& configuration, Ref<DeferredPromise>&& promise)
206{
207 // 2.4 Media Capabilities Interface
208 // https://wicg.github.io/media-capabilities/#media-capabilities-interface
209
210 // 1. If configuration is not a valid MediaConfiguration, return a Promise rejected with a TypeError.
211 // 2. If configuration.video is present and is not a valid video configuration, return a Promise rejected with a TypeError.
212 // 3. If configuration.audio is present and is not a valid audio configuration, return a Promise rejected with a TypeError.
213 if (!isValidMediaConfiguration(configuration)) {
214 promise->reject(TypeError);
215 return;
216 }
217
218 // 4. Let p be a new promise.
219 // 5. In parallel, run the create a MediaCapabilitiesInfo algorithm with configuration and resolve p with its result.
220 // 6. Return p.
221 m_taskQueue.enqueueTask([configuration = WTFMove(configuration), promise = WTFMove(promise)] () mutable {
222
223 // 2.2.4. If configuration is of type MediaEncodingConfiguration, run the following substeps:
224 MediaEngineConfigurationFactory::EncodingConfigurationCallback callback = [promise = WTFMove(promise)] (auto info) mutable {
225 // 2.2.4.1. If the user agent is able to encode the media
226 // represented by configuration, set supported to true. Otherwise
227 // set it to false.
228 // 2.2.4.2. If the user agent is able to encode the media
229 // represented by configuration at a pace that allows encoding
230 // frames at the same pace as they are sent to the encoder, set
231 // smooth to true. Otherwise set it to false.
232 // 2.2.4.3. If the user agent is able to encode the media
233 // represented by configuration in a power efficient manner, set
234 // powerEfficient to true. Otherwise set it to false. The user agent
235 // SHOULD NOT take into consideration the current power source in
236 // order to determine the encoding power efficiency unless the
237 // device’s power source has side effects such as enabling different
238 // encoding modules.
239 promise->resolve<IDLDictionary<MediaCapabilitiesEncodingInfo>>(WTFMove(info));
240 };
241
242 MediaEngineConfigurationFactory::createEncodingConfiguration(WTFMove(configuration), WTFMove(callback));
243
244 });
245}
246
247}
248