Bug 1885602 - Part 5: Implement navigating to the SUMO help topic from the menu heade...
[gecko.git] / dom / media / webcodecs / EncodedAudioChunk.cpp
blob1cbb2ff9bdec4b615ec8d56540e44e5a15afea5e
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 #include "mozilla/dom/EncodedAudioChunk.h"
8 #include "mozilla/dom/EncodedAudioChunkBinding.h"
10 #include <utility>
12 #include "MediaData.h"
13 #include "TimeUnits.h"
14 #include "mozilla/CheckedInt.h"
15 #include "mozilla/Logging.h"
16 #include "mozilla/PodOperations.h"
17 #include "mozilla/dom/StructuredCloneHolder.h"
18 #include "mozilla/dom/StructuredCloneTags.h"
19 #include "mozilla/dom/WebCodecsUtils.h"
21 extern mozilla::LazyLogModule gWebCodecsLog;
22 using mozilla::media::TimeUnit;
24 namespace mozilla::dom {
26 #ifdef LOG_INTERNAL
27 # undef LOG_INTERNAL
28 #endif // LOG_INTERNAL
29 #define LOG_INTERNAL(level, msg, ...) \
30 MOZ_LOG(gWebCodecsLog, LogLevel::level, (msg, ##__VA_ARGS__))
32 #ifdef LOGW
33 # undef LOGW
34 #endif // LOGW
35 #define LOGW(msg, ...) LOG_INTERNAL(Warning, msg, ##__VA_ARGS__)
37 #ifdef LOGE
38 # undef LOGE
39 #endif // LOGE
40 #define LOGE(msg, ...) LOG_INTERNAL(Error, msg, ##__VA_ARGS__)
42 // Only needed for refcounted objects.
43 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(EncodedAudioChunk, mParent)
44 NS_IMPL_CYCLE_COLLECTING_ADDREF(EncodedAudioChunk)
45 NS_IMPL_CYCLE_COLLECTING_RELEASE(EncodedAudioChunk)
46 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(EncodedAudioChunk)
47 NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
48 NS_INTERFACE_MAP_ENTRY(nsISupports)
49 NS_INTERFACE_MAP_END
51 EncodedAudioChunkData::EncodedAudioChunkData(
52 already_AddRefed<MediaAlignedByteBuffer> aBuffer,
53 const EncodedAudioChunkType& aType, int64_t aTimestamp,
54 Maybe<uint64_t>&& aDuration)
55 : mBuffer(aBuffer),
56 mType(aType),
57 mTimestamp(aTimestamp),
58 mDuration(aDuration) {
59 MOZ_ASSERT(mBuffer);
60 MOZ_ASSERT(mBuffer->Length() == mBuffer->Size());
61 MOZ_ASSERT(mBuffer->Length() <=
62 static_cast<size_t>(std::numeric_limits<uint32_t>::max()));
65 UniquePtr<EncodedAudioChunkData> EncodedAudioChunkData::Clone() const {
66 if (!mBuffer) {
67 LOGE("No buffer in EncodedAudioChunkData %p to clone!", this);
68 return nullptr;
71 // Since EncodedAudioChunkData can be zero-sized, cloning a zero-sized chunk
72 // is allowed.
73 if (mBuffer->Size() == 0) {
74 LOGW("Cloning an empty EncodedAudioChunkData %p", this);
77 auto buffer =
78 MakeRefPtr<MediaAlignedByteBuffer>(mBuffer->Data(), mBuffer->Length());
79 if (!buffer || buffer->Size() != mBuffer->Size()) {
80 LOGE("OOM to copy EncodedAudioChunkData %p", this);
81 return nullptr;
84 return MakeUnique<EncodedAudioChunkData>(buffer.forget(), mType, mTimestamp,
85 Maybe<uint64_t>(mDuration));
88 already_AddRefed<MediaRawData> EncodedAudioChunkData::TakeData() {
89 if (!mBuffer || !(*mBuffer)) {
90 LOGE("EncodedAudioChunkData %p has no data!", this);
91 return nullptr;
94 RefPtr<MediaRawData> sample(new MediaRawData(std::move(*mBuffer)));
95 sample->mKeyframe = mType == EncodedAudioChunkType::Key;
96 sample->mTime = TimeUnit::FromMicroseconds(mTimestamp);
97 sample->mTimecode = TimeUnit::FromMicroseconds(mTimestamp);
99 if (mDuration) {
100 CheckedInt64 duration(*mDuration);
101 if (!duration.isValid()) {
102 LOGE("EncodedAudioChunkData %p 's duration exceeds TimeUnit's limit",
103 this);
104 return nullptr;
106 sample->mDuration = TimeUnit::FromMicroseconds(duration.value());
109 return sample.forget();
112 EncodedAudioChunk::EncodedAudioChunk(
113 nsIGlobalObject* aParent, already_AddRefed<MediaAlignedByteBuffer> aBuffer,
114 const EncodedAudioChunkType& aType, int64_t aTimestamp,
115 Maybe<uint64_t>&& aDuration)
116 : EncodedAudioChunkData(std::move(aBuffer), aType, aTimestamp,
117 std::move(aDuration)),
118 mParent(aParent) {}
120 EncodedAudioChunk::EncodedAudioChunk(nsIGlobalObject* aParent,
121 const EncodedAudioChunkData& aData)
122 : EncodedAudioChunkData(aData), mParent(aParent) {}
124 nsIGlobalObject* EncodedAudioChunk::GetParentObject() const {
125 AssertIsOnOwningThread();
127 return mParent.get();
130 JSObject* EncodedAudioChunk::WrapObject(JSContext* aCx,
131 JS::Handle<JSObject*> aGivenProto) {
132 AssertIsOnOwningThread();
134 return EncodedAudioChunk_Binding::Wrap(aCx, this, aGivenProto);
137 // https://w3c.github.io/webcodecs/#encodedaudiochunk-constructors
138 /* static */
139 already_AddRefed<EncodedAudioChunk> EncodedAudioChunk::Constructor(
140 const GlobalObject& aGlobal, const EncodedAudioChunkInit& aInit,
141 ErrorResult& aRv) {
142 nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(aGlobal.GetAsSupports());
143 if (!global) {
144 aRv.Throw(NS_ERROR_FAILURE);
145 return nullptr;
148 auto buffer = ProcessTypedArrays(
149 aInit.mData,
150 [&](const Span<uint8_t>& aData,
151 JS::AutoCheckCannotGC&&) -> RefPtr<MediaAlignedByteBuffer> {
152 // Make sure it's in uint32_t's range.
153 CheckedUint32 byteLength(aData.Length());
154 if (!byteLength.isValid()) {
155 aRv.Throw(NS_ERROR_INVALID_ARG);
156 return nullptr;
158 if (aData.Length() == 0) {
159 LOGW("Buffer for constructing EncodedAudioChunk is empty!");
161 RefPtr<MediaAlignedByteBuffer> buf = MakeRefPtr<MediaAlignedByteBuffer>(
162 aData.Elements(), aData.Length());
164 // Instead of checking *buf, size comparision is used to allow
165 // constructing a zero-sized EncodedAudioChunk.
166 if (!buf || buf->Size() != aData.Length()) {
167 aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
168 return nullptr;
170 return buf;
173 RefPtr<EncodedAudioChunk> chunk(new EncodedAudioChunk(
174 global, buffer.forget(), aInit.mType, aInit.mTimestamp,
175 OptionalToMaybe(aInit.mDuration)));
176 return aRv.Failed() ? nullptr : chunk.forget();
179 EncodedAudioChunkType EncodedAudioChunk::Type() const {
180 AssertIsOnOwningThread();
182 return mType;
185 int64_t EncodedAudioChunk::Timestamp() const {
186 AssertIsOnOwningThread();
188 return mTimestamp;
191 Nullable<uint64_t> EncodedAudioChunk::GetDuration() const {
192 AssertIsOnOwningThread();
193 return MaybeToNullable(mDuration);
196 uint32_t EncodedAudioChunk::ByteLength() const {
197 AssertIsOnOwningThread();
198 MOZ_ASSERT(mBuffer);
200 return static_cast<uint32_t>(mBuffer->Length());
203 // https://w3c.github.io/webcodecs/#dom-encodedaudiochunk-copyto
204 void EncodedAudioChunk::CopyTo(
205 const MaybeSharedArrayBufferViewOrMaybeSharedArrayBuffer& aDestination,
206 ErrorResult& aRv) {
207 AssertIsOnOwningThread();
209 ProcessTypedArraysFixed(aDestination, [&](const Span<uint8_t>& aData) {
210 if (mBuffer->Size() > aData.size_bytes()) {
211 aRv.ThrowTypeError(
212 "Destination ArrayBuffer smaller than source EncodedAudioChunk");
213 return;
216 PodCopy(aData.data(), mBuffer->Data(), mBuffer->Size());
220 // https://w3c.github.io/webcodecs/#ref-for-deserialization-steps
221 /* static */
222 JSObject* EncodedAudioChunk::ReadStructuredClone(
223 JSContext* aCx, nsIGlobalObject* aGlobal, JSStructuredCloneReader* aReader,
224 const EncodedAudioChunkData& aData) {
225 JS::Rooted<JS::Value> value(aCx, JS::NullValue());
226 // To avoid a rooting hazard error from returning a raw JSObject* before
227 // running the RefPtr destructor, RefPtr needs to be destructed before
228 // returning the raw JSObject*, which is why the RefPtr<EncodedAudioChunk> is
229 // created in the scope below. Otherwise, the static analysis infers the
230 // RefPtr cannot be safely destructed while the unrooted return JSObject* is
231 // on the stack.
233 auto frame = MakeRefPtr<EncodedAudioChunk>(aGlobal, aData);
234 if (!GetOrCreateDOMReflector(aCx, frame, &value) || !value.isObject()) {
235 return nullptr;
238 return value.toObjectOrNull();
241 // https://w3c.github.io/webcodecs/#ref-for-serialization-steps
242 bool EncodedAudioChunk::WriteStructuredClone(
243 JSStructuredCloneWriter* aWriter, StructuredCloneHolder* aHolder) const {
244 AssertIsOnOwningThread();
246 // Indexing the chunk and send the index to the receiver.
247 const uint32_t index =
248 static_cast<uint32_t>(aHolder->EncodedAudioChunks().Length());
249 // The serialization is limited to the same process scope so it's ok to
250 // serialize a reference instead of a copy.
251 aHolder->EncodedAudioChunks().AppendElement(EncodedAudioChunkData(*this));
252 return !NS_WARN_IF(
253 !JS_WriteUint32Pair(aWriter, SCTAG_DOM_ENCODEDAUDIOCHUNK, index));
256 #undef LOGW
257 #undef LOGE
258 #undef LOG_INTERNAL
260 } // namespace mozilla::dom