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"
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
{
28 #endif // LOG_INTERNAL
29 #define LOG_INTERNAL(level, msg, ...) \
30 MOZ_LOG(gWebCodecsLog, LogLevel::level, (msg, ##__VA_ARGS__))
35 #define LOGW(msg, ...) LOG_INTERNAL(Warning, msg, ##__VA_ARGS__)
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
)
51 EncodedAudioChunkData::EncodedAudioChunkData(
52 already_AddRefed
<MediaAlignedByteBuffer
> aBuffer
,
53 const EncodedAudioChunkType
& aType
, int64_t aTimestamp
,
54 Maybe
<uint64_t>&& aDuration
)
57 mTimestamp(aTimestamp
),
58 mDuration(aDuration
) {
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 {
67 LOGE("No buffer in EncodedAudioChunkData %p to clone!", this);
71 // Since EncodedAudioChunkData can be zero-sized, cloning a zero-sized chunk
73 if (mBuffer
->Size() == 0) {
74 LOGW("Cloning an empty EncodedAudioChunkData %p", this);
78 MakeRefPtr
<MediaAlignedByteBuffer
>(mBuffer
->Data(), mBuffer
->Length());
79 if (!buffer
|| buffer
->Size() != mBuffer
->Size()) {
80 LOGE("OOM to copy EncodedAudioChunkData %p", this);
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);
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
);
100 CheckedInt64
duration(*mDuration
);
101 if (!duration
.isValid()) {
102 LOGE("EncodedAudioChunkData %p 's duration exceeds TimeUnit's limit",
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
)),
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
139 already_AddRefed
<EncodedAudioChunk
> EncodedAudioChunk::Constructor(
140 const GlobalObject
& aGlobal
, const EncodedAudioChunkInit
& aInit
,
142 nsCOMPtr
<nsIGlobalObject
> global
= do_QueryInterface(aGlobal
.GetAsSupports());
144 aRv
.Throw(NS_ERROR_FAILURE
);
148 auto buffer
= ProcessTypedArrays(
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
);
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
);
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();
185 int64_t EncodedAudioChunk::Timestamp() const {
186 AssertIsOnOwningThread();
191 Nullable
<uint64_t> EncodedAudioChunk::GetDuration() const {
192 AssertIsOnOwningThread();
193 return MaybeToNullable(mDuration
);
196 uint32_t EncodedAudioChunk::ByteLength() const {
197 AssertIsOnOwningThread();
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
,
207 AssertIsOnOwningThread();
209 ProcessTypedArraysFixed(aDestination
, [&](const Span
<uint8_t>& aData
) {
210 if (mBuffer
->Size() > aData
.size_bytes()) {
212 "Destination ArrayBuffer smaller than source EncodedAudioChunk");
216 PodCopy(aData
.data(), mBuffer
->Data(), mBuffer
->Size());
220 // https://w3c.github.io/webcodecs/#ref-for-deserialization-steps
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
233 auto frame
= MakeRefPtr
<EncodedAudioChunk
>(aGlobal
, aData
);
234 if (!GetOrCreateDOMReflector(aCx
, frame
, &value
) || !value
.isObject()) {
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));
253 !JS_WriteUint32Pair(aWriter
, SCTAG_DOM_ENCODEDAUDIOCHUNK
, index
));
260 } // namespace mozilla::dom