tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

EncodedAudioChunk.cpp (9007B)


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