OLD | NEW |
1 // Copyright 2017 The Chromium Authors. All rights reserved. | 1 // Copyright 2017 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "modules/media_capabilities/MediaCapabilities.h" | 5 #include "modules/media_capabilities/MediaCapabilities.h" |
6 | 6 |
7 #include "bindings/core/v8/CallbackPromiseAdapter.h" | 7 #include "bindings/core/v8/CallbackPromiseAdapter.h" |
8 #include "bindings/core/v8/ScriptPromise.h" | 8 #include "bindings/core/v8/ScriptPromise.h" |
9 #include "bindings/core/v8/ScriptPromiseResolver.h" | 9 #include "bindings/core/v8/ScriptPromiseResolver.h" |
10 #include "core/dom/DOMException.h" | 10 #include "core/dom/DOMException.h" |
11 #include "modules/media_capabilities/MediaCapabilitiesInfo.h" | 11 #include "modules/media_capabilities/MediaCapabilitiesInfo.h" |
12 #include "modules/media_capabilities/MediaConfiguration.h" | 12 #include "modules/media_capabilities/MediaConfiguration.h" |
13 #include "modules/media_capabilities/MediaDecodingConfiguration.h" | 13 #include "modules/media_capabilities/MediaDecodingConfiguration.h" |
14 #include "modules/media_capabilities/MediaEncodingConfiguration.h" | 14 #include "modules/media_capabilities/MediaEncodingConfiguration.h" |
15 #include "platform/bindings/ScriptState.h" | 15 #include "platform/bindings/ScriptState.h" |
16 #include "platform/network/ParsedContentType.h" | 16 #include "platform/network/ParsedContentType.h" |
17 #include "public/platform/Platform.h" | 17 #include "public/platform/Platform.h" |
18 #include "public/platform/WebMediaRecorderHandler.h" | 18 #include "public/platform/WebMediaRecorderHandler.h" |
19 #include "public/platform/modules/media_capabilities/WebMediaCapabilitiesClient.
h" | 19 #include "public/platform/modules/media_capabilities/WebMediaCapabilitiesClient.
h" |
20 #include "public/platform/modules/media_capabilities/WebMediaCapabilitiesInfo.h" | 20 #include "public/platform/modules/media_capabilities/WebMediaCapabilitiesInfo.h" |
21 #include "public/platform/modules/media_capabilities/WebMediaConfiguration.h" | 21 #include "public/platform/modules/media_capabilities/WebMediaConfiguration.h" |
22 | 22 |
23 namespace blink { | 23 namespace blink { |
24 | 24 |
25 namespace { | 25 namespace { |
26 | 26 |
| 27 constexpr const char* kApplicationMimeTypePrefix = "application/"; |
| 28 constexpr const char* kAudioMimeTypePrefix = "audio/"; |
| 29 constexpr const char* kVideoMimeTypePrefix = "video/"; |
| 30 constexpr const char* kCodecsMimeTypeParam = "codecs"; |
| 31 |
| 32 bool IsValidMimeType(const String& content_type, const String& prefix) { |
| 33 ParsedContentType parsed_content_type(content_type, |
| 34 ParsedContentType::Mode::kStrict); |
| 35 if (!parsed_content_type.IsValid()) |
| 36 return false; |
| 37 |
| 38 if (!parsed_content_type.MimeType().StartsWith(prefix) && |
| 39 !parsed_content_type.MimeType().StartsWith(kApplicationMimeTypePrefix)) { |
| 40 return false; |
| 41 } |
| 42 |
| 43 if (parsed_content_type.ParameterCount() > 1) |
| 44 return false; |
| 45 |
| 46 if (parsed_content_type.ParameterCount() == 1 && |
| 47 parsed_content_type.ParameterValueForName(kCodecsMimeTypeParam) |
| 48 .IsNull()) { |
| 49 return false; |
| 50 } |
| 51 |
| 52 return true; |
| 53 } |
| 54 |
| 55 bool IsValidMediaConfiguration(const MediaConfiguration& configuration) { |
| 56 return configuration.hasAudio() || configuration.hasVideo(); |
| 57 } |
| 58 |
| 59 bool IsValidVideoConfiguration(const VideoConfiguration& configuration) { |
| 60 DCHECK(configuration.hasContentType()); |
| 61 |
| 62 if (!IsValidMimeType(configuration.contentType(), kVideoMimeTypePrefix)) |
| 63 return false; |
| 64 |
| 65 DCHECK(configuration.hasFramerate()); |
| 66 if (!std::isfinite(configuration.framerate()) || |
| 67 configuration.framerate() <= 0) { |
| 68 return false; |
| 69 } |
| 70 |
| 71 return true; |
| 72 } |
| 73 |
| 74 bool IsValidAudioConfiguration(const AudioConfiguration& configuration) { |
| 75 DCHECK(configuration.hasContentType()); |
| 76 |
| 77 if (!IsValidMimeType(configuration.contentType(), kAudioMimeTypePrefix)) |
| 78 return false; |
| 79 |
| 80 return true; |
| 81 } |
| 82 |
27 WebAudioConfiguration ToWebAudioConfiguration( | 83 WebAudioConfiguration ToWebAudioConfiguration( |
28 const AudioConfiguration& configuration) { | 84 const AudioConfiguration& configuration) { |
29 WebAudioConfiguration web_configuration; | 85 WebAudioConfiguration web_configuration; |
30 | 86 |
31 // |contentType| is mandatory. | 87 // |contentType| is mandatory. |
32 DCHECK(configuration.hasContentType()); | 88 DCHECK(configuration.hasContentType()); |
33 ParsedContentType parsed_content_type(configuration.contentType(), | 89 ParsedContentType parsed_content_type(configuration.contentType(), |
34 ParsedContentType::Mode::kStrict); | 90 ParsedContentType::Mode::kStrict); |
35 | 91 DCHECK(parsed_content_type.IsValid()); |
36 // TODO(chcunningham): Throw TypeError for invalid input. | |
37 // DCHECK(parsed_content_type.IsValid()); | |
38 | 92 |
39 DEFINE_STATIC_LOCAL(const String, codecs, ("codecs")); | 93 DEFINE_STATIC_LOCAL(const String, codecs, ("codecs")); |
40 web_configuration.mime_type = parsed_content_type.MimeType().LowerASCII(); | 94 web_configuration.mime_type = parsed_content_type.MimeType().LowerASCII(); |
41 web_configuration.codec = parsed_content_type.ParameterValueForName(codecs); | 95 web_configuration.codec = parsed_content_type.ParameterValueForName(codecs); |
42 | 96 |
43 // |channels| is optional and will be set to a null WebString if not present. | 97 // |channels| is optional and will be set to a null WebString if not present. |
44 web_configuration.channels = configuration.hasChannels() | 98 web_configuration.channels = configuration.hasChannels() |
45 ? WebString(configuration.channels()) | 99 ? WebString(configuration.channels()) |
46 : WebString(); | 100 : WebString(); |
47 | 101 |
48 if (configuration.hasBitrate()) | 102 if (configuration.hasBitrate()) |
49 web_configuration.bitrate = configuration.bitrate(); | 103 web_configuration.bitrate = configuration.bitrate(); |
50 | 104 |
51 if (configuration.hasSamplerate()) | 105 if (configuration.hasSamplerate()) |
52 web_configuration.samplerate = configuration.samplerate(); | 106 web_configuration.samplerate = configuration.samplerate(); |
53 | 107 |
54 return web_configuration; | 108 return web_configuration; |
55 } | 109 } |
56 | 110 |
57 WebVideoConfiguration ToWebVideoConfiguration( | 111 WebVideoConfiguration ToWebVideoConfiguration( |
58 const VideoConfiguration& configuration) { | 112 const VideoConfiguration& configuration) { |
59 WebVideoConfiguration web_configuration; | 113 WebVideoConfiguration web_configuration; |
60 | 114 |
61 // All the properties are mandatory. | 115 // All the properties are mandatory. |
62 DCHECK(configuration.hasContentType()); | 116 DCHECK(configuration.hasContentType()); |
63 ParsedContentType parsed_content_type(configuration.contentType(), | 117 ParsedContentType parsed_content_type(configuration.contentType(), |
64 ParsedContentType::Mode::kStrict); | 118 ParsedContentType::Mode::kStrict); |
65 | 119 DCHECK(parsed_content_type.IsValid()); |
66 // TODO(chcunningham): Throw TypeError for invalid input. | |
67 // DCHECK(parsed_content_type.IsValid()); | |
68 | 120 |
69 DEFINE_STATIC_LOCAL(const String, codecs, ("codecs")); | 121 DEFINE_STATIC_LOCAL(const String, codecs, ("codecs")); |
70 web_configuration.mime_type = parsed_content_type.MimeType().LowerASCII(); | 122 web_configuration.mime_type = parsed_content_type.MimeType().LowerASCII(); |
71 web_configuration.codec = parsed_content_type.ParameterValueForName(codecs); | 123 web_configuration.codec = parsed_content_type.ParameterValueForName(codecs); |
72 | 124 |
73 DCHECK(configuration.hasWidth()); | 125 DCHECK(configuration.hasWidth()); |
74 web_configuration.width = configuration.width(); | 126 web_configuration.width = configuration.width(); |
75 | 127 |
76 DCHECK(configuration.hasHeight()); | 128 DCHECK(configuration.hasHeight()); |
77 web_configuration.height = configuration.height(); | 129 web_configuration.height = configuration.height(); |
78 | 130 |
79 DCHECK(configuration.hasBitrate()); | 131 DCHECK(configuration.hasBitrate()); |
80 web_configuration.bitrate = configuration.bitrate(); | 132 web_configuration.bitrate = configuration.bitrate(); |
81 | 133 |
82 DCHECK(configuration.hasFramerate()); | 134 DCHECK(configuration.hasFramerate()); |
83 web_configuration.framerate = configuration.framerate(); | 135 web_configuration.framerate = configuration.framerate(); |
84 | 136 |
85 return web_configuration; | 137 return web_configuration; |
86 } | 138 } |
87 | 139 |
88 WebMediaConfiguration ToWebMediaConfiguration( | 140 WebMediaConfiguration ToWebMediaConfiguration( |
89 const MediaConfiguration& configuration) { | 141 const MediaDecodingConfiguration& configuration) { |
90 WebMediaConfiguration web_configuration; | 142 WebMediaConfiguration web_configuration; |
91 | 143 |
| 144 // |type| is mandatory. |
| 145 DCHECK(configuration.hasType()); |
| 146 if (configuration.type() == "file") |
| 147 web_configuration.type = MediaConfigurationType::kFile; |
| 148 else if (configuration.type() == "media-source") |
| 149 web_configuration.type = MediaConfigurationType::kMediaSource; |
| 150 else |
| 151 NOTREACHED(); |
| 152 |
92 if (configuration.hasAudio()) { | 153 if (configuration.hasAudio()) { |
93 web_configuration.audio_configuration = | 154 web_configuration.audio_configuration = |
94 ToWebAudioConfiguration(configuration.audio()); | 155 ToWebAudioConfiguration(configuration.audio()); |
| 156 } |
| 157 |
| 158 if (configuration.hasVideo()) { |
| 159 web_configuration.video_configuration = |
| 160 ToWebVideoConfiguration(configuration.video()); |
| 161 } |
| 162 |
| 163 return web_configuration; |
| 164 } |
| 165 |
| 166 WebMediaConfiguration ToWebMediaConfiguration( |
| 167 const MediaEncodingConfiguration& configuration) { |
| 168 WebMediaConfiguration web_configuration; |
| 169 |
| 170 // TODO(mcasas): parse and set the type for encoding. |
| 171 |
| 172 if (configuration.hasAudio()) { |
| 173 web_configuration.audio_configuration = |
| 174 ToWebAudioConfiguration(configuration.audio()); |
95 } | 175 } |
96 | 176 |
97 if (configuration.hasVideo()) { | 177 if (configuration.hasVideo()) { |
98 web_configuration.video_configuration = | 178 web_configuration.video_configuration = |
99 ToWebVideoConfiguration(configuration.video()); | 179 ToWebVideoConfiguration(configuration.video()); |
100 } | 180 } |
101 | 181 |
102 return web_configuration; | 182 return web_configuration; |
103 } | 183 } |
104 | 184 |
105 } // anonymous namespace | 185 } // anonymous namespace |
106 | 186 |
107 MediaCapabilities::MediaCapabilities() = default; | 187 MediaCapabilities::MediaCapabilities() = default; |
108 | 188 |
109 ScriptPromise MediaCapabilities::decodingInfo( | 189 ScriptPromise MediaCapabilities::decodingInfo( |
110 ScriptState* script_state, | 190 ScriptState* script_state, |
111 const MediaDecodingConfiguration& configuration) { | 191 const MediaDecodingConfiguration& configuration) { |
112 ScriptPromiseResolver* resolver = ScriptPromiseResolver::Create(script_state); | 192 ScriptPromiseResolver* resolver = ScriptPromiseResolver::Create(script_state); |
113 ScriptPromise promise = resolver->Promise(); | 193 ScriptPromise promise = resolver->Promise(); |
114 | 194 |
115 // |type| is mandatory. | 195 if (!IsValidMediaConfiguration(configuration)) { |
116 DCHECK(configuration.hasType()); | 196 resolver->Reject(V8ThrowException::CreateTypeError( |
| 197 script_state->GetIsolate(), |
| 198 "The configuration dictionary has neither |video| nor |audio| " |
| 199 "specified and needs at least one of them.")); |
| 200 return promise; |
| 201 } |
| 202 |
| 203 if (configuration.hasVideo() && |
| 204 !IsValidVideoConfiguration(configuration.video())) { |
| 205 resolver->Reject(V8ThrowException::CreateTypeError( |
| 206 script_state->GetIsolate(), |
| 207 "The video configuration dictionary is not valid.")); |
| 208 return promise; |
| 209 } |
| 210 |
| 211 if (configuration.hasAudio() && |
| 212 !IsValidAudioConfiguration(configuration.audio())) { |
| 213 resolver->Reject(V8ThrowException::CreateTypeError( |
| 214 script_state->GetIsolate(), |
| 215 "The audio configuration dictionary is not valid.")); |
| 216 return promise; |
| 217 } |
117 | 218 |
118 Platform::Current()->MediaCapabilitiesClient()->DecodingInfo( | 219 Platform::Current()->MediaCapabilitiesClient()->DecodingInfo( |
119 ToWebMediaConfiguration(configuration), | 220 ToWebMediaConfiguration(configuration), |
120 WTF::MakeUnique<CallbackPromiseAdapter<MediaCapabilitiesInfo, void>>( | 221 WTF::MakeUnique<CallbackPromiseAdapter<MediaCapabilitiesInfo, void>>( |
121 resolver)); | 222 resolver)); |
122 | 223 |
123 return promise; | 224 return promise; |
124 } | 225 } |
125 | 226 |
126 ScriptPromise MediaCapabilities::encodingInfo( | 227 ScriptPromise MediaCapabilities::encodingInfo( |
127 ScriptState* script_state, | 228 ScriptState* script_state, |
128 const MediaEncodingConfiguration& configuration) { | 229 const MediaEncodingConfiguration& configuration) { |
129 ScriptPromiseResolver* resolver = ScriptPromiseResolver::Create(script_state); | 230 ScriptPromiseResolver* resolver = ScriptPromiseResolver::Create(script_state); |
130 ScriptPromise promise = resolver->Promise(); | 231 ScriptPromise promise = resolver->Promise(); |
131 | 232 |
132 if (!configuration.hasVideo() && !configuration.hasAudio()) { | 233 if (!IsValidMediaConfiguration(configuration)) { |
133 resolver->Reject(DOMException::Create( | 234 resolver->Reject(V8ThrowException::CreateTypeError( |
134 kSyntaxError, | 235 script_state->GetIsolate(), |
135 "The configuration dictionary has neither |video| nor |audio| " | 236 "The configuration dictionary has neither |video| nor |audio| " |
136 "specified and needs at least one of them.")); | 237 "specified and needs at least one of them.")); |
137 return promise; | 238 return promise; |
138 } | 239 } |
139 | 240 |
| 241 if (configuration.hasVideo() && |
| 242 !IsValidVideoConfiguration(configuration.video())) { |
| 243 resolver->Reject(V8ThrowException::CreateTypeError( |
| 244 script_state->GetIsolate(), |
| 245 "The video configuration dictionary is not valid.")); |
| 246 return promise; |
| 247 } |
| 248 |
| 249 if (configuration.hasAudio() && |
| 250 !IsValidAudioConfiguration(configuration.audio())) { |
| 251 resolver->Reject(V8ThrowException::CreateTypeError( |
| 252 script_state->GetIsolate(), |
| 253 "The audio configuration dictionary is not valid.")); |
| 254 return promise; |
| 255 } |
| 256 |
140 std::unique_ptr<WebMediaRecorderHandler> handler = | 257 std::unique_ptr<WebMediaRecorderHandler> handler = |
141 Platform::Current()->CreateMediaRecorderHandler(); | 258 Platform::Current()->CreateMediaRecorderHandler(); |
142 if (!handler) { | 259 if (!handler) { |
143 resolver->Reject(DOMException::Create( | 260 resolver->Reject(DOMException::Create( |
144 kInvalidStateError, | 261 kInvalidStateError, |
145 "Platform error: could not create MediaRecorderHandler.")); | 262 "Platform error: could not create MediaRecorderHandler.")); |
146 return promise; | 263 return promise; |
147 } | 264 } |
148 | 265 |
149 handler->EncodingInfo( | 266 handler->EncodingInfo( |
150 ToWebMediaConfiguration(configuration), | 267 ToWebMediaConfiguration(configuration), |
151 WTF::MakeUnique<CallbackPromiseAdapter<MediaCapabilitiesInfo, void>>( | 268 WTF::MakeUnique<CallbackPromiseAdapter<MediaCapabilitiesInfo, void>>( |
152 resolver)); | 269 resolver)); |
153 return promise; | 270 return promise; |
154 } | 271 } |
155 | 272 |
156 DEFINE_TRACE(MediaCapabilities) {} | 273 DEFINE_TRACE(MediaCapabilities) {} |
157 | 274 |
158 } // namespace blink | 275 } // namespace blink |
OLD | NEW |