WebCodecsUtils.h (14015B)
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/. */ 6 7 #ifndef MOZILLA_DOM_WEBCODECS_WEBCODECSUTILS_H 8 #define MOZILLA_DOM_WEBCODECS_WEBCODECSUTILS_H 9 10 #include "ErrorList.h" 11 #include "MediaData.h" 12 #include "PlatformEncoderModule.h" 13 #include "js/TypeDecls.h" 14 #include "mozilla/Maybe.h" 15 #include "mozilla/MozPromise.h" 16 #include "mozilla/ProfilerMarkers.h" 17 #include "mozilla/Result.h" 18 #include "mozilla/TaskQueue.h" 19 #include "mozilla/dom/AudioDataBinding.h" 20 #include "mozilla/dom/BindingDeclarations.h" 21 #include "mozilla/dom/BufferSourceBindingFwd.h" 22 #include "mozilla/dom/Nullable.h" 23 #include "mozilla/dom/VideoColorSpaceBinding.h" 24 #include "mozilla/dom/VideoEncoderBinding.h" 25 #include "mozilla/dom/VideoFrameBinding.h" 26 #include "nsIGlobalObject.h" 27 28 namespace mozilla { 29 30 namespace dom { 31 class VideoEncoderConfigInternal; 32 class VideoDecoderConfigInternal; 33 } // namespace dom 34 35 #define WEBCODECS_MARKER(codecType, desc, options, markerType, ...) \ 36 do { \ 37 if (profiler_is_collecting_markers()) { \ 38 nsFmtCString marker(FMT_STRING("{}{}"), codecType, desc); \ 39 PROFILER_MARKER( \ 40 ProfilerString8View::WrapNullTerminatedString(marker.get()), \ 41 MEDIA_RT, options, markerType, __VA_ARGS__); \ 42 } \ 43 } while (0) 44 45 #define WEBCODECS_MARKER_INTERVAL_START(type, desc) \ 46 WEBCODECS_MARKER(type, desc, {MarkerTiming::IntervalStart()}, Tracing, \ 47 "WebCodecs") 48 #define WEBCODECS_MARKER_INTERVAL_END(type, desc) \ 49 WEBCODECS_MARKER(type, desc, {MarkerTiming::IntervalEnd()}, Tracing, \ 50 "WebCodecs") 51 52 class AutoWebCodecsMarker { 53 public: 54 AutoWebCodecsMarker(const char* aType, const char* aDesc) 55 : mType(aType), mDesc(aDesc) { 56 WEBCODECS_MARKER_INTERVAL_START(mType, mDesc); 57 } 58 59 AutoWebCodecsMarker(AutoWebCodecsMarker&& aOther) noexcept { 60 MOZ_ASSERT(!aOther.mEnded, "Ended marker should not be moved"); 61 mType = std::move(aOther.mType); 62 mDesc = std::move(aOther.mDesc); 63 mEnded = std::move(aOther.mEnded); 64 aOther.mEnded = true; 65 } 66 67 AutoWebCodecsMarker& operator=(AutoWebCodecsMarker&& aOther) noexcept { 68 if (this != &aOther) { 69 MOZ_ASSERT(!aOther.mEnded, "Ended marker should not be moved"); 70 mType = std::move(aOther.mType); 71 mDesc = std::move(aOther.mDesc); 72 mEnded = std::move(aOther.mEnded); 73 aOther.mEnded = true; 74 } 75 return *this; 76 } 77 78 AutoWebCodecsMarker(const AutoWebCodecsMarker& aOther) = delete; 79 AutoWebCodecsMarker& operator=(const AutoWebCodecsMarker& aOther) = delete; 80 81 ~AutoWebCodecsMarker() { End(); } 82 83 void End() { 84 if (!mEnded) { 85 WEBCODECS_MARKER_INTERVAL_END(mType, mDesc); 86 mEnded = true; 87 } 88 } 89 90 private: 91 const char* mType; 92 const char* mDesc; 93 bool mEnded = false; 94 }; 95 96 // Use this macro only when you do not need to call `AutoWebCodecsMarker::End()` 97 // manually; it will automatically end the marker when the object goes out of 98 // scope. 99 #define AUTO_WEBCODECS_MARKER(type, desc) \ 100 AutoWebCodecsMarker PROFILER_RAII(type, desc) 101 102 class AsyncDurationTracker { 103 public: 104 AsyncDurationTracker() : mOwningThread(GetCurrentSerialEventTarget()) {} 105 ~AsyncDurationTracker() { Clear(); } 106 107 void Start(int64_t aTimestamp, AutoWebCodecsMarker&& aMarker) { 108 MOZ_ASSERT(mOwningThread->IsOnCurrentThread()); 109 mEntries.push_back( 110 Entry{.mTimestamp = aTimestamp, .mMarker = std::move(aMarker)}); 111 } 112 113 size_t End(int64_t aTimestamp) { 114 MOZ_ASSERT(mOwningThread->IsOnCurrentThread()); 115 size_t popped = 0; 116 while (!mEntries.empty() && mEntries.front().mTimestamp <= aTimestamp) { 117 mEntries.front().mMarker.End(); 118 popped += 1; 119 mEntries.pop_front(); 120 } 121 return popped; 122 } 123 124 void Clear() { 125 MOZ_ASSERT(mOwningThread->IsOnCurrentThread()); 126 while (!mEntries.empty()) { 127 mEntries.front().mMarker.End(); 128 mEntries.pop_front(); 129 } 130 } 131 132 private: 133 struct Entry { 134 int64_t mTimestamp; 135 AutoWebCodecsMarker mMarker; 136 }; 137 138 std::deque<Entry> mEntries; 139 const nsCOMPtr<nsISerialEventTarget> mOwningThread; 140 }; 141 142 namespace gfx { 143 enum class ColorRange : uint8_t; 144 enum class ColorSpace2 : uint8_t; 145 enum class SurfaceFormat : int8_t; 146 enum class TransferFunction : uint8_t; 147 enum class YUVColorSpace : uint8_t; 148 } // namespace gfx 149 150 using WebCodecsId = size_t; 151 152 extern std::atomic<WebCodecsId> sNextId; 153 154 class EncoderConfigurationChangeList; 155 156 namespace dom { 157 158 /* 159 * The followings are helpers for WebCodecs methods. 160 */ 161 162 nsTArray<nsCString> GuessContainers(const nsAString& aCodec); 163 164 Maybe<nsString> ParseCodecString(const nsAString& aCodec); 165 166 bool IsSameColorSpace(const VideoColorSpaceInit& aLhs, 167 const VideoColorSpaceInit& aRhs); 168 169 /* 170 * Below are helpers for conversion among Maybe, Optional, and Nullable. 171 */ 172 173 template <typename T> 174 Maybe<T> OptionalToMaybe(const Optional<T>& aOptional) { 175 if (aOptional.WasPassed()) { 176 return Some(aOptional.Value()); 177 } 178 return Nothing(); 179 } 180 181 template <typename T> 182 const T* OptionalToPointer(const Optional<T>& aOptional) { 183 return aOptional.WasPassed() ? &aOptional.Value() : nullptr; 184 } 185 186 template <typename T> 187 Maybe<T> NullableToMaybe(const Nullable<T>& aNullable) { 188 if (!aNullable.IsNull()) { 189 return Some(aNullable.Value()); 190 } 191 return Nothing(); 192 } 193 194 template <typename T> 195 Nullable<T> MaybeToNullable(const Maybe<T>& aOptional) { 196 if (aOptional.isSome()) { 197 return Nullable<T>(aOptional.value()); 198 } 199 return Nullable<T>(); 200 } 201 202 /* 203 * Below are helpers to operate ArrayBuffer or ArrayBufferView. 204 */ 205 206 Result<Ok, nsresult> CloneBuffer(JSContext* aCx, 207 OwningAllowSharedBufferSource& aDest, 208 const OwningAllowSharedBufferSource& aSrc, 209 ErrorResult& aRv); 210 211 Result<RefPtr<MediaByteBuffer>, nsresult> GetExtraDataFromArrayBuffer( 212 const OwningAllowSharedBufferSource& aBuffer); 213 214 bool CopyExtradataToDescription(JSContext* aCx, Span<const uint8_t>& aSrc, 215 OwningAllowSharedBufferSource& aDest); 216 217 /* 218 * The following are utilities to convert between VideoColorSpace values to 219 * gfx's values. 220 */ 221 222 struct VideoColorSpaceInternal { 223 explicit VideoColorSpaceInternal(const VideoColorSpaceInit& aColorSpaceInit); 224 VideoColorSpaceInternal() = default; 225 VideoColorSpaceInternal(const bool& aFullRange, 226 const VideoMatrixCoefficients& aMatrix, 227 const VideoColorPrimaries& aPrimaries, 228 const VideoTransferCharacteristics& aTransfer) 229 : mFullRange(Some(aFullRange)), 230 mMatrix(Some(aMatrix)), 231 mPrimaries(Some(aPrimaries)), 232 mTransfer(Some(aTransfer)) {} 233 VideoColorSpaceInternal(const VideoColorSpaceInternal& aOther) = default; 234 VideoColorSpaceInternal(VideoColorSpaceInternal&& aOther) = default; 235 236 VideoColorSpaceInternal& operator=(const VideoColorSpaceInternal& aOther) = 237 default; 238 VideoColorSpaceInternal& operator=(VideoColorSpaceInternal&& aOther) = 239 default; 240 241 bool operator==(const VideoColorSpaceInternal& aOther) const { 242 return mFullRange == aOther.mFullRange && mMatrix == aOther.mMatrix && 243 mPrimaries == aOther.mPrimaries && mTransfer == aOther.mTransfer; 244 } 245 bool operator!=(const VideoColorSpaceInternal& aOther) const { 246 return !(*this == aOther); 247 } 248 249 VideoColorSpaceInit ToColorSpaceInit() const; 250 nsCString ToString() const; 251 252 Maybe<bool> mFullRange; 253 Maybe<VideoMatrixCoefficients> mMatrix; 254 Maybe<VideoColorPrimaries> mPrimaries; 255 Maybe<VideoTransferCharacteristics> mTransfer; 256 }; 257 258 gfx::ColorRange ToColorRange(bool aIsFullRange); 259 260 gfx::YUVColorSpace ToColorSpace(VideoMatrixCoefficients aMatrix); 261 262 gfx::TransferFunction ToTransferFunction( 263 VideoTransferCharacteristics aTransfer); 264 265 gfx::ColorSpace2 ToPrimaries(VideoColorPrimaries aPrimaries); 266 267 bool ToFullRange(const gfx::ColorRange& aColorRange); 268 269 Maybe<VideoMatrixCoefficients> ToMatrixCoefficients( 270 const gfx::YUVColorSpace& aColorSpace); 271 272 Maybe<VideoTransferCharacteristics> ToTransferCharacteristics( 273 const gfx::TransferFunction& aTransferFunction); 274 275 Maybe<VideoColorPrimaries> ToPrimaries(const gfx::ColorSpace2& aColorSpace); 276 277 /* 278 * The following are utilities to convert from gfx's formats to 279 * VideoPixelFormats. 280 */ 281 282 enum class ImageBitmapFormat : uint8_t; 283 enum class VideoPixelFormat : uint8_t; 284 285 Maybe<VideoPixelFormat> SurfaceFormatToVideoPixelFormat( 286 gfx::SurfaceFormat aFormat); 287 288 Maybe<VideoPixelFormat> ImageBitmapFormatToVideoPixelFormat( 289 ImageBitmapFormat aFormat); 290 291 template <typename T> 292 class MessageRequestHolder { 293 public: 294 MessageRequestHolder() = default; 295 ~MessageRequestHolder() = default; 296 297 MozPromiseRequestHolder<T>& Request() { return mRequest; } 298 void Disconnect() { mRequest.DisconnectIfExists(); } 299 void Complete() { mRequest.Complete(); } 300 bool Exists() const { return mRequest.Exists(); } 301 302 protected: 303 MozPromiseRequestHolder<T> mRequest{}; 304 }; 305 306 enum class MessageProcessedResult { NotProcessed, Processed }; 307 308 bool IsOnAndroid(); 309 bool IsOnMacOS(); 310 bool IsOnLinux(); 311 312 // Wrap a type to make it unique. This allows using ergonomically in the Variant 313 // below. Simply aliasing with `using` isn't enough, because typedefs in C++ 314 // don't produce strong types, so two integer variants result in 315 // the same type, making it ambiguous to the Variant code. 316 // T is the type to be wrapped. Phantom is a type that is only used to 317 // disambiguate and should be unique in the program. 318 template <typename T, typename Phantom> 319 class StrongTypedef { 320 public: 321 explicit StrongTypedef(T const& value) : mValue(value) {} 322 explicit StrongTypedef(T&& value) : mValue(std::move(value)) {} 323 T& get() { return mValue; } 324 T const& get() const { return mValue; } 325 326 private: 327 T mValue; 328 }; 329 330 using CodecChange = StrongTypedef<nsString, struct CodecChangeTypeWebCodecs>; 331 using DimensionsChange = 332 StrongTypedef<gfx::IntSize, struct DimensionsChangeTypeWebCodecs>; 333 using DisplayDimensionsChange = 334 StrongTypedef<Maybe<gfx::IntSize>, 335 struct DisplayDimensionsChangeTypeWebCodecs>; 336 using BitrateChange = 337 StrongTypedef<Maybe<uint32_t>, struct BitrateChangeTypeWebCodecs>; 338 using FramerateChange = 339 StrongTypedef<Maybe<double>, struct FramerateChangeTypeWebCodecs>; 340 using HardwareAccelerationChange = 341 StrongTypedef<dom::HardwareAcceleration, 342 struct HardwareAccelerationChangeTypeWebCodecs>; 343 using AlphaChange = 344 StrongTypedef<dom::AlphaOption, struct AlphaChangeTypeWebCodecs>; 345 using ScalabilityModeChange = 346 StrongTypedef<Maybe<nsString>, struct ScalabilityModeChangeTypeWebCodecs>; 347 using BitrateModeChange = StrongTypedef<dom::VideoEncoderBitrateMode, 348 struct BitrateModeChangeTypeWebCodecs>; 349 using LatencyModeChange = 350 StrongTypedef<dom::LatencyMode, struct LatencyModeTypeChangeTypeWebCodecs>; 351 using ContentHintChange = 352 StrongTypedef<Maybe<nsString>, struct ContentHintTypeTypeWebCodecs>; 353 354 using WebCodecsEncoderConfigurationItem = 355 Variant<CodecChange, DimensionsChange, DisplayDimensionsChange, 356 BitrateModeChange, BitrateChange, FramerateChange, 357 HardwareAccelerationChange, AlphaChange, ScalabilityModeChange, 358 LatencyModeChange, ContentHintChange>; 359 360 struct WebCodecsConfigurationChangeList { 361 NS_INLINE_DECL_THREADSAFE_REFCOUNTING(WebCodecsConfigurationChangeList) 362 bool Empty() const { return mChanges.IsEmpty(); } 363 template <typename T> 364 void Push(const T& aItem) { 365 mChanges.AppendElement(aItem); 366 } 367 // This returns true if it should be possible to attempt to reconfigure the 368 // encoder on the fly. It can fail, in which case the encoder will be flushed 369 // and a new one will be created with the new set of parameters. 370 bool CanAttemptReconfigure() const; 371 372 // Convert this to the format the underlying PEM can understand 373 RefPtr<EncoderConfigurationChangeList> ToPEMChangeList() const; 374 nsCString ToString() const; 375 376 nsTArray<WebCodecsEncoderConfigurationItem> mChanges; 377 378 private: 379 ~WebCodecsConfigurationChangeList() = default; 380 }; 381 382 nsCString ColorSpaceInitToString( 383 const dom::VideoColorSpaceInit& aColorSpaceInit); 384 385 RefPtr<TaskQueue> GetWebCodecsEncoderTaskQueue(); 386 VideoColorSpaceInternal FallbackColorSpaceForVideoContent(); 387 VideoColorSpaceInternal FallbackColorSpaceForWebContent(); 388 389 Maybe<CodecType> CodecStringToCodecType(const nsAString& aCodecString); 390 391 nsCString ConfigToString(const VideoDecoderConfig& aConfig); 392 393 // Returns true if a particular codec is supported by WebCodecs. 394 bool IsSupportedVideoCodec(const nsAString& aCodec); 395 bool IsSupportedAudioCodec(const nsAString& aCodec); 396 397 // Returns the codec string to use in Gecko for a particular container and 398 // codec name given by WebCodecs. This maps pcm description to the profile 399 // number, and simply returns the codec name for all other codecs. 400 nsCString ConvertCodecName(const nsCString& aContainer, 401 const nsCString& aCodec); 402 403 uint32_t BytesPerSamples(const mozilla::dom::AudioSampleFormat& aFormat); 404 405 // If resisting fingerprinting, remove all hardware/software preference. 406 void ApplyResistFingerprintingIfNeeded( 407 const RefPtr<VideoEncoderConfigInternal>& aConfig, 408 nsIGlobalObject* aGlobal); 409 void ApplyResistFingerprintingIfNeeded( 410 const RefPtr<VideoDecoderConfigInternal>& aConfig, 411 nsIGlobalObject* aGlobal); 412 } // namespace dom 413 } // namespace mozilla 414 415 #endif // MOZILLA_DOM_WEBCODECS_WEBCODECSUTILS_H