1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim:set ts=2 sw=2 sts=2 et cindent: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #if !defined(PlatformDecoderModule_h_)
8 # define PlatformDecoderModule_h_
12 # include "DecoderDoctorLogger.h"
13 # include "GMPCrashHelper.h"
14 # include "MediaCodecsSupport.h"
15 # include "MediaEventSource.h"
16 # include "MediaInfo.h"
17 # include "MediaResult.h"
18 # include "mozilla/EnumSet.h"
19 # include "mozilla/EnumTypeTraits.h"
20 # include "mozilla/MozPromise.h"
21 # include "mozilla/RefPtr.h"
22 # include "mozilla/TaskQueue.h"
23 # include "mozilla/layers/KnowsCompositor.h"
24 # include "mozilla/layers/LayersTypes.h"
25 # include "mozilla/ipc/UtilityAudioDecoder.h"
26 # include "nsTArray.h"
27 # include "PerformanceRecorder.h"
34 class DecoderDoctorDiagnostics
;
40 class MediaDataDecoder
;
41 class RemoteDecoderModule
;
44 static LazyLogModule
sPDMLog("PlatformDecoderModule");
51 HardwareDecoderNotAllowed
,
53 ErrorIfNoInitializationData
, // By default frames delivered before
54 // initialization data are dropped. Pass this
55 // option to raise an error if frames are
56 // delivered before initialization data.
57 DefaultPlaybackDeviceMono
, // Currently only used by Opus on RDD to avoid
58 // initialization of audio backends on RDD
60 SENTINEL
// one past the last valid value
62 using OptionSet
= EnumSet
<Option
>;
64 struct UseNullDecoder
{
65 UseNullDecoder() = default;
66 explicit UseNullDecoder(bool aUseNullDecoder
) : mUse(aUseNullDecoder
) {}
70 // Do not wrap H264 decoder in a H264Converter.
72 NoWrapper() = default;
73 explicit NoWrapper(bool aDontUseWrapper
) : mDontUseWrapper(aDontUseWrapper
) {}
74 bool mDontUseWrapper
= false;
77 struct VideoFrameRate
{
78 VideoFrameRate() = default;
79 explicit VideoFrameRate(float aFramerate
) : mValue(aFramerate
) {}
85 struct CreateDecoderParams
;
86 struct CreateDecoderParamsForAsync
{
87 using Option
= media::Option
;
88 using OptionSet
= media::OptionSet
;
89 explicit CreateDecoderParamsForAsync(const CreateDecoderParams
& aParams
);
90 CreateDecoderParamsForAsync(CreateDecoderParamsForAsync
&& aParams
);
92 const VideoInfo
& VideoConfig() const {
93 MOZ_ASSERT(mConfig
->IsVideo());
94 return *mConfig
->GetAsVideoInfo();
97 const AudioInfo
& AudioConfig() const {
98 MOZ_ASSERT(mConfig
->IsAudio());
99 return *mConfig
->GetAsAudioInfo();
102 UniquePtr
<TrackInfo
> mConfig
;
103 const RefPtr
<layers::ImageContainer
> mImageContainer
;
104 const RefPtr
<layers::KnowsCompositor
> mKnowsCompositor
;
105 const RefPtr
<GMPCrashHelper
> mCrashHelper
;
106 const media::UseNullDecoder mUseNullDecoder
;
107 const media::NoWrapper mNoWrapper
;
108 const TrackInfo::TrackType mType
= TrackInfo::kUndefinedTrack
;
109 std::function
<MediaEventProducer
<TrackInfo::TrackType
>*()>
110 mOnWaitingForKeyEvent
;
111 const OptionSet mOptions
= OptionSet(Option::Default
);
112 const media::VideoFrameRate mRate
;
113 const Maybe
<uint64_t> mMediaEngineId
;
114 const Maybe
<TrackingId
> mTrackingId
;
117 struct MOZ_STACK_CLASS CreateDecoderParams final
{
118 using Option
= media::Option
;
119 using OptionSet
= media::OptionSet
;
120 using UseNullDecoder
= media::UseNullDecoder
;
121 using NoWrapper
= media::NoWrapper
;
122 using VideoFrameRate
= media::VideoFrameRate
;
124 explicit CreateDecoderParams(const TrackInfo
& aConfig
) : mConfig(aConfig
) {}
125 CreateDecoderParams(const CreateDecoderParams
& aParams
) = default;
127 MOZ_IMPLICIT
CreateDecoderParams(const CreateDecoderParamsForAsync
& aParams
)
128 : mConfig(*aParams
.mConfig
.get()),
129 mImageContainer(aParams
.mImageContainer
),
130 mKnowsCompositor(aParams
.mKnowsCompositor
),
131 mCrashHelper(aParams
.mCrashHelper
),
132 mUseNullDecoder(aParams
.mUseNullDecoder
),
133 mNoWrapper(aParams
.mNoWrapper
),
134 mType(aParams
.mType
),
135 mOnWaitingForKeyEvent(aParams
.mOnWaitingForKeyEvent
),
136 mOptions(aParams
.mOptions
),
137 mRate(aParams
.mRate
),
138 mMediaEngineId(aParams
.mMediaEngineId
),
139 mTrackingId(aParams
.mTrackingId
) {}
141 template <typename T1
, typename
... Ts
>
142 CreateDecoderParams(const TrackInfo
& aConfig
, T1
&& a1
, Ts
&&... args
)
144 Set(std::forward
<T1
>(a1
), std::forward
<Ts
>(args
)...);
147 template <typename T1
, typename
... Ts
>
148 CreateDecoderParams(const CreateDecoderParams
& aParams
, T1
&& a1
, Ts
&&... args
)
149 : CreateDecoderParams(aParams
) {
150 Set(std::forward
<T1
>(a1
), std::forward
<Ts
>(args
)...);
153 const VideoInfo
& VideoConfig() const {
154 MOZ_ASSERT(mConfig
.IsVideo());
155 return *mConfig
.GetAsVideoInfo();
158 const AudioInfo
& AudioConfig() const {
159 MOZ_ASSERT(mConfig
.IsAudio());
160 return *mConfig
.GetAsAudioInfo();
163 layers::LayersBackend
GetLayersBackend() const {
164 if (mKnowsCompositor
) {
165 return mKnowsCompositor
->GetCompositorBackendType();
167 return layers::LayersBackend::LAYERS_NONE
;
170 // CreateDecoderParams is a MOZ_STACK_CLASS, it is only used to
171 // simplify the passing of arguments to Create*Decoder.
172 // It is safe to use references and raw pointers.
173 const TrackInfo
& mConfig
;
174 layers::ImageContainer
* mImageContainer
= nullptr;
175 MediaResult
* mError
= nullptr;
176 layers::KnowsCompositor
* mKnowsCompositor
= nullptr;
177 GMPCrashHelper
* mCrashHelper
= nullptr;
178 media::UseNullDecoder mUseNullDecoder
;
179 media::NoWrapper mNoWrapper
;
180 TrackInfo::TrackType mType
= TrackInfo::kUndefinedTrack
;
181 std::function
<MediaEventProducer
<TrackInfo::TrackType
>*()>
182 mOnWaitingForKeyEvent
;
183 OptionSet mOptions
= OptionSet(Option::Default
);
184 media::VideoFrameRate mRate
;
185 // Used on Windows when the MF media engine playback is enabled.
186 Maybe
<uint64_t> mMediaEngineId
;
187 Maybe
<TrackingId
> mTrackingId
;
190 void Set(layers::ImageContainer
* aImageContainer
) {
191 mImageContainer
= aImageContainer
;
193 void Set(MediaResult
* aError
) { mError
= aError
; }
194 void Set(GMPCrashHelper
* aCrashHelper
) { mCrashHelper
= aCrashHelper
; }
195 void Set(UseNullDecoder aUseNullDecoder
) {
196 mUseNullDecoder
= aUseNullDecoder
;
198 void Set(NoWrapper aNoWrapper
) { mNoWrapper
= aNoWrapper
; }
199 void Set(OptionSet aOptions
) { mOptions
= aOptions
; }
200 void Set(VideoFrameRate aRate
) { mRate
= aRate
; }
201 void Set(layers::KnowsCompositor
* aKnowsCompositor
) {
202 if (aKnowsCompositor
) {
203 mKnowsCompositor
= aKnowsCompositor
;
204 MOZ_ASSERT(aKnowsCompositor
->IsThreadSafe());
207 void Set(TrackInfo::TrackType aType
) { mType
= aType
; }
208 void Set(std::function
<MediaEventProducer
<TrackInfo::TrackType
>*()>&&
210 mOnWaitingForKeyEvent
= std::move(aOnWaitingForKey
);
212 void Set(const std::function
<MediaEventProducer
<TrackInfo::TrackType
>*()>&
214 mOnWaitingForKeyEvent
= aOnWaitingForKey
;
216 void Set(const Maybe
<uint64_t>& aMediaEngineId
) {
217 mMediaEngineId
= aMediaEngineId
;
219 void Set(const Maybe
<TrackingId
>& aTrackingId
) { mTrackingId
= aTrackingId
; }
220 void Set(const CreateDecoderParams
& aParams
) {
221 // Set all but mTrackInfo;
222 mImageContainer
= aParams
.mImageContainer
;
223 mError
= aParams
.mError
;
224 mKnowsCompositor
= aParams
.mKnowsCompositor
;
225 mCrashHelper
= aParams
.mCrashHelper
;
226 mUseNullDecoder
= aParams
.mUseNullDecoder
;
227 mNoWrapper
= aParams
.mNoWrapper
;
228 mType
= aParams
.mType
;
229 mOnWaitingForKeyEvent
= aParams
.mOnWaitingForKeyEvent
;
230 mOptions
= aParams
.mOptions
;
231 mRate
= aParams
.mRate
;
232 mMediaEngineId
= aParams
.mMediaEngineId
;
233 mTrackingId
= aParams
.mTrackingId
;
235 template <typename T1
, typename T2
, typename
... Ts
>
236 void Set(T1
&& a1
, T2
&& a2
, Ts
&&... args
) {
237 Set(std::forward
<T1
>(a1
));
238 Set(std::forward
<T2
>(a2
), std::forward
<Ts
>(args
)...);
242 struct MOZ_STACK_CLASS SupportDecoderParams final
{
243 using Option
= media::Option
;
244 using OptionSet
= media::OptionSet
;
245 using UseNullDecoder
= media::UseNullDecoder
;
246 using NoWrapper
= media::NoWrapper
;
247 using VideoFrameRate
= media::VideoFrameRate
;
249 explicit SupportDecoderParams(const TrackInfo
& aConfig
) : mConfig(aConfig
) {}
251 explicit SupportDecoderParams(const CreateDecoderParams
& aParams
)
252 : mConfig(aParams
.mConfig
),
253 mError(aParams
.mError
),
254 mKnowsCompositor(aParams
.mKnowsCompositor
),
255 mUseNullDecoder(aParams
.mUseNullDecoder
),
256 mNoWrapper(aParams
.mNoWrapper
),
257 mOptions(aParams
.mOptions
),
258 mRate(aParams
.mRate
),
259 mMediaEngineId(aParams
.mMediaEngineId
) {}
261 template <typename T1
, typename
... Ts
>
262 SupportDecoderParams(const TrackInfo
& aConfig
, T1
&& a1
, Ts
&&... args
)
264 Set(std::forward
<T1
>(a1
), std::forward
<Ts
>(args
)...);
267 const nsCString
& MimeType() const { return mConfig
.mMimeType
; }
269 const TrackInfo
& mConfig
;
270 DecoderDoctorDiagnostics
* mDiagnostics
= nullptr;
271 MediaResult
* mError
= nullptr;
272 RefPtr
<layers::KnowsCompositor
> mKnowsCompositor
;
273 UseNullDecoder mUseNullDecoder
;
274 NoWrapper mNoWrapper
;
275 OptionSet mOptions
= OptionSet(Option::Default
);
276 VideoFrameRate mRate
;
277 Maybe
<uint64_t> mMediaEngineId
;
280 void Set(DecoderDoctorDiagnostics
* aDiagnostics
) {
281 mDiagnostics
= aDiagnostics
;
283 void Set(MediaResult
* aError
) { mError
= aError
; }
284 void Set(media::UseNullDecoder aUseNullDecoder
) {
285 mUseNullDecoder
= aUseNullDecoder
;
287 void Set(media::NoWrapper aNoWrapper
) { mNoWrapper
= aNoWrapper
; }
288 void Set(media::OptionSet aOptions
) { mOptions
= aOptions
; }
289 void Set(media::VideoFrameRate aRate
) { mRate
= aRate
; }
290 void Set(layers::KnowsCompositor
* aKnowsCompositor
) {
291 if (aKnowsCompositor
) {
292 mKnowsCompositor
= aKnowsCompositor
;
293 MOZ_ASSERT(aKnowsCompositor
->IsThreadSafe());
296 void Set(const Maybe
<uint64_t>& aMediaEngineId
) {
297 mMediaEngineId
= aMediaEngineId
;
300 template <typename T1
, typename T2
, typename
... Ts
>
301 void Set(T1
&& a1
, T2
&& a2
, Ts
&&... args
) {
302 Set(std::forward
<T1
>(a1
));
303 Set(std::forward
<T2
>(a2
), std::forward
<Ts
>(args
)...);
307 // Used for IPDL serialization.
308 // The 'value' have to be the biggest enum from CreateDecoderParams::Option.
310 struct MaxEnumValue
<::mozilla::CreateDecoderParams::Option
> {
311 static constexpr unsigned int value
=
312 static_cast<unsigned int>(CreateDecoderParams::Option::SENTINEL
);
315 // The PlatformDecoderModule interface is used by the MediaFormatReader to
316 // abstract access to decoders provided by various
318 // Each platform (Windows, MacOSX, Linux, B2G etc) must implement a
319 // PlatformDecoderModule to provide access to its decoders in order to get
320 // decompressed H.264/AAC from the MediaFormatReader.
322 // Decoding is asynchronous, and should be performed on the task queue
323 // provided if the underlying platform isn't already exposing an async API.
325 // A cross-platform decoder module that discards input and produces "blank"
326 // output samples exists for testing, and is created when the pref
327 // "media.use-blank-decoder" is true.
329 class PlatformDecoderModule
{
331 NS_INLINE_DECL_THREADSAFE_REFCOUNTING(PlatformDecoderModule
)
333 // Perform any per-instance initialization.
334 // This is called on the decode task queue.
335 virtual nsresult
Startup() { return NS_OK
; }
337 // Indicates if the PlatformDecoderModule supports decoding of aMimeType,
338 // and whether or not hardware-accelerated decoding is supported.
339 // The answer to both SupportsMimeType and Supports doesn't guarantee that
340 // creation of a decoder will actually succeed.
341 virtual media::DecodeSupportSet
SupportsMimeType(
342 const nsACString
& aMimeType
,
343 DecoderDoctorDiagnostics
* aDiagnostics
) const = 0;
345 virtual media::DecodeSupportSet
Supports(
346 const SupportDecoderParams
& aParams
,
347 DecoderDoctorDiagnostics
* aDiagnostics
) const {
348 const TrackInfo
& trackInfo
= aParams
.mConfig
;
349 const media::DecodeSupportSet support
=
350 SupportsMimeType(trackInfo
.mMimeType
, aDiagnostics
);
352 // Bail early if we don't support this format at all
353 if (support
.isEmpty()) {
357 const auto* videoInfo
= trackInfo
.GetAsVideoInfo();
360 // No video stream = software decode only
361 return media::DecodeSupport::SoftwareDecode
;
364 // Check whether we support the desired color depth
365 if (!SupportsColorDepth(videoInfo
->mColorDepth
, aDiagnostics
)) {
366 return media::DecodeSupportSet
{};
371 using CreateDecoderPromise
= MozPromise
<RefPtr
<MediaDataDecoder
>, MediaResult
,
372 /* IsExclusive = */ true>;
375 PlatformDecoderModule() = default;
376 virtual ~PlatformDecoderModule() = default;
378 friend class MediaChangeMonitor
;
379 friend class PDMFactory
;
380 friend class EMEDecoderModule
;
381 friend class RemoteDecoderModule
;
383 // Indicates if the PlatformDecoderModule supports decoding of aColorDepth.
384 // Should override this method when the platform can support color depth != 8.
385 virtual bool SupportsColorDepth(
386 gfx::ColorDepth aColorDepth
,
387 DecoderDoctorDiagnostics
* aDiagnostics
) const {
388 return aColorDepth
== gfx::ColorDepth::COLOR_8
;
391 // Creates a Video decoder. The layers backend is passed in so that
392 // decoders can determine whether hardware accelerated decoding can be used.
393 // On Windows the task queue's threads in have MSCOM initialized with
394 // COINIT_MULTITHREADED.
395 // Returns nullptr if the decoder can't be created.
396 // It is not safe to store a reference to aParams or aParams.mConfig as the
397 // object isn't guaranteed to live after the call.
398 // CreateVideoDecoder may need to make additional checks if the
399 // CreateDecoderParams argument is actually supported and return nullptr if
400 // not to allow for fallback PDMs to be tried.
401 virtual already_AddRefed
<MediaDataDecoder
> CreateVideoDecoder(
402 const CreateDecoderParams
& aParams
) = 0;
404 // Creates an Audio decoder with the specified properties.
405 // Returns nullptr if the decoder can't be created.
406 // On Windows the task queue's threads in have MSCOM initialized with
407 // COINIT_MULTITHREADED.
408 // It is not safe to store a reference to aParams or aParams.mConfig as the
409 // object isn't guaranteed to live after the call.
410 // CreateAudioDecoder may need to make additional checks if the
411 // CreateDecoderParams argument is actually supported and return nullptr if
412 // not to allow for fallback PDMs to be tried.
413 virtual already_AddRefed
<MediaDataDecoder
> CreateAudioDecoder(
414 const CreateDecoderParams
& aParams
) = 0;
416 // Asychronously create a decoder.
417 virtual RefPtr
<CreateDecoderPromise
> AsyncCreateDecoder(
418 const CreateDecoderParams
& aParams
);
421 DDLoggedTypeDeclName(MediaDataDecoder
);
423 // MediaDataDecoder is the interface exposed by decoders created by the
424 // PlatformDecoderModule's Create*Decoder() functions. The type of
425 // media data that the decoder accepts as valid input and produces as
426 // output is determined when the MediaDataDecoder is created.
428 // Unless otherwise noted, all functions are only called on the decode task
429 // queue. An exception is the MediaDataDecoder in
430 // MediaFormatReader::IsVideoAccelerated() for which all calls (Init(),
431 // IsHardwareAccelerated(), and Shutdown()) are from the main thread.
433 // Don't block inside these functions, unless it's explicitly noted that you
434 // should (like in Flush()).
436 // Decoding is done asynchronously.
437 class MediaDataDecoder
: public DecoderDoctorLifeLogger
<MediaDataDecoder
> {
439 virtual ~MediaDataDecoder() = default;
442 typedef TrackInfo::TrackType TrackType
;
443 typedef nsTArray
<RefPtr
<MediaData
>> DecodedData
;
444 typedef MozPromise
<TrackType
, MediaResult
, /* IsExclusive = */ true>
446 typedef MozPromise
<DecodedData
, MediaResult
, /* IsExclusive = */ true>
448 typedef MozPromise
<bool, MediaResult
, /* IsExclusive = */ true> FlushPromise
;
450 NS_INLINE_DECL_PURE_VIRTUAL_REFCOUNTING
452 // Initialize the decoder. The decoder should be ready to decode once
453 // the promise resolves. The decoder should do any initialization here, rather
454 // than in its constructor or PlatformDecoderModule::Create*Decoder(),
455 // so that if the MediaFormatReader needs to shutdown during initialization,
456 // it can call Shutdown() to cancel this operation. Any initialization
457 // that requires blocking the calling thread in this function *must*
458 // be done here so that it can be canceled by calling Shutdown()!
459 // Methods Decode, DecodeBatch, Drain, Flush, Shutdown are guaranteed to be
460 // called on the thread where Init() first ran.
461 virtual RefPtr
<InitPromise
> Init() = 0;
463 // Inserts a sample into the decoder's decode pipeline. The DecodePromise will
464 // be resolved with the decoded MediaData. In case the decoder needs more
465 // input, the DecodePromise may be resolved with an empty array of samples to
466 // indicate that Decode should be called again before a MediaData is returned.
467 virtual RefPtr
<DecodePromise
> Decode(MediaRawData
* aSample
) = 0;
469 // This could probably be implemented as a wrapper that takes a
470 // generic MediaDataDecoder and manages batching as needed. For now
471 // only AudioTrimmer with RemoteMediaDataDecoder supports batch
473 // Inserts an array of samples into the decoder's decode pipeline. The
474 // DecodePromise will be resolved with the decoded MediaData. In case
475 // the decoder needs more input, the DecodePromise may be resolved
476 // with an empty array of samples to indicate that Decode should be
477 // called again before a MediaData is returned.
478 virtual bool CanDecodeBatch() const { return false; }
479 virtual RefPtr
<DecodePromise
> DecodeBatch(
480 nsTArray
<RefPtr
<MediaRawData
>>&& aSamples
) {
481 MOZ_CRASH("DecodeBatch not implemented yet");
482 return MediaDataDecoder::DecodePromise::CreateAndReject(
483 NS_ERROR_DOM_MEDIA_DECODE_ERR
, __func__
);
486 // Causes all complete samples in the pipeline that can be decoded to be
487 // output. If the decoder can't produce samples from the current output,
488 // it drops the input samples. The decoder may be holding onto samples
489 // that are required to decode samples that it expects to get in future.
490 // This is called when the demuxer reaches end of stream.
491 // This function is asynchronous.
492 // The MediaDataDecoder shall resolve the pending DecodePromise with drained
493 // samples. Drain will be called multiple times until the resolved
494 // DecodePromise is empty which indicates that there are no more samples to
496 virtual RefPtr
<DecodePromise
> Drain() = 0;
498 // Causes all samples in the decoding pipeline to be discarded. When this
499 // promise resolves, the decoder must be ready to accept new data for
500 // decoding. This function is called when the demuxer seeks, before decoding
501 // resumes after the seek. The current DecodePromise if any shall be rejected
502 // with NS_ERROR_DOM_MEDIA_CANCELED
503 virtual RefPtr
<FlushPromise
> Flush() = 0;
505 // Cancels all init/decode/drain operations, and shuts down the decoder. The
506 // platform decoder should clean up any resources it's using and release
507 // memory etc. The shutdown promise will be resolved once the decoder has
508 // completed shutdown. The reader calls Flush() before calling Shutdown(). The
509 // reader will delete the decoder once the promise is resolved.
510 // The ShutdownPromise must only ever be resolved.
511 // Shutdown() may not be called if init hasn't been called first. It is
512 // possible under some circumstances for the decoder to be deleted without
513 // Init having been called first.
514 virtual RefPtr
<ShutdownPromise
> Shutdown() = 0;
516 // Called from the state machine task queue or main thread. Decoder needs to
517 // decide whether or not hardware acceleration is supported after creating.
518 // It doesn't need to call Init() before calling this function.
519 virtual bool IsHardwareAccelerated(nsACString
& aFailureReason
) const {
523 // Return the name of the MediaDataDecoder, only used for decoding.
524 // May be accessed in a non thread-safe fashion.
525 virtual nsCString
GetDescriptionName() const = 0;
527 virtual nsCString
GetProcessName() const {
528 nsCString rv
= nsCString(XRE_GetProcessTypeString());
529 if (XRE_IsUtilityProcess()) {
530 rv
+= "+"_ns
+ mozilla::ipc::GetChildAudioActorName();
534 virtual nsCString
GetCodecName() const = 0;
536 // Set a hint of seek target time to decoder. Decoder will drop any decoded
537 // data which pts is smaller than this value. This threshold needs to be clear
538 // after reset decoder. To clear it explicitly, call this method with
539 // TimeUnit::Invalid().
540 // Decoder may not honor this value. However, it'd be better that
541 // video decoder implements this API to improve seek performance.
542 // Note: it should be called before Input() or after Flush().
543 virtual void SetSeekThreshold(const media::TimeUnit
& aTime
) {}
545 // When playing adaptive playback, recreating an Android video decoder will
546 // cause the transition not smooth during resolution change.
547 // Reuse the decoder if the decoder support recycling.
548 // Currently, only Android video decoder will return true.
549 virtual bool SupportDecoderRecycling() const { return false; }
551 enum class ConversionRequired
{
557 // Indicates that the decoder requires a specific format.
558 // The demuxed data will be converted accordingly before feeding it to
560 virtual ConversionRequired
NeedsConversion() const {
561 return ConversionRequired::kNeedNone
;
565 } // namespace mozilla