tor-browser

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

AudioTrimmer.cpp (8293B)


      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 "AudioTrimmer.h"
      8 
      9 #define LOG(arg, ...)                                                  \
     10  DDMOZ_LOG(sPDMLog, mozilla::LogLevel::Debug, "::%s: " arg, __func__, \
     11            ##__VA_ARGS__)
     12 
     13 #define LOGV(arg, ...)                                                   \
     14  DDMOZ_LOG(sPDMLog, mozilla::LogLevel::Verbose, "::%s: " arg, __func__, \
     15            ##__VA_ARGS__)
     16 
     17 namespace mozilla {
     18 
     19 using media::TimeInterval;
     20 using media::TimeUnit;
     21 
     22 RefPtr<MediaDataDecoder::InitPromise> AudioTrimmer::Init() {
     23  mThread = GetCurrentSerialEventTarget();
     24  return mDecoder->Init();
     25 }
     26 
     27 RefPtr<MediaDataDecoder::DecodePromise> AudioTrimmer::Decode(
     28    MediaRawData* aSample) {
     29  MOZ_ASSERT(mThread->IsOnCurrentThread(),
     30             "We're not on the thread we were first initialized on");
     31  LOG("AudioTrimmer::Decode");
     32  PrepareTrimmers(aSample);
     33  RefPtr<AudioTrimmer> self = this;
     34  RefPtr<DecodePromise> p = mDecoder->Decode(aSample)->Then(
     35      GetCurrentSerialEventTarget(), __func__,
     36      [self](DecodePromise::ResolveOrRejectValue&& aValue) {
     37        return self->HandleDecodedResult(std::move(aValue));
     38      });
     39  return p;
     40 }
     41 
     42 RefPtr<MediaDataDecoder::FlushPromise> AudioTrimmer::Flush() {
     43  MOZ_ASSERT(mThread->IsOnCurrentThread(),
     44             "We're not on the thread we were first initialized on");
     45  LOG("Flushing");
     46  RefPtr<FlushPromise> p = mDecoder->Flush();
     47  mTrimmers.Clear();
     48  return p;
     49 }
     50 
     51 RefPtr<MediaDataDecoder::DecodePromise> AudioTrimmer::Drain() {
     52  MOZ_ASSERT(mThread->IsOnCurrentThread(),
     53             "We're not on the thread we were first initialized on");
     54  LOG("Draining");
     55  RefPtr<DecodePromise> p = mDecoder->Drain()->Then(
     56      GetCurrentSerialEventTarget(), __func__,
     57      [self = RefPtr{this}](DecodePromise::ResolveOrRejectValue&& aValue) {
     58        return self->HandleDecodedResult(std::move(aValue));
     59      });
     60  return p;
     61 }
     62 
     63 RefPtr<ShutdownPromise> AudioTrimmer::Shutdown() {
     64  // mThread may not be set if Init hasn't been called first.
     65  MOZ_ASSERT(!mThread || mThread->IsOnCurrentThread());
     66  return mDecoder->Shutdown();
     67 }
     68 
     69 nsCString AudioTrimmer::GetDescriptionName() const {
     70  return mDecoder->GetDescriptionName();
     71 }
     72 
     73 nsCString AudioTrimmer::GetProcessName() const {
     74  return mDecoder->GetProcessName();
     75 }
     76 
     77 nsCString AudioTrimmer::GetCodecName() const {
     78  return mDecoder->GetCodecName();
     79 }
     80 
     81 bool AudioTrimmer::IsHardwareAccelerated(nsACString& aFailureReason) const {
     82  return mDecoder->IsHardwareAccelerated(aFailureReason);
     83 }
     84 
     85 void AudioTrimmer::SetSeekThreshold(const media::TimeUnit& aTime) {
     86  mDecoder->SetSeekThreshold(aTime);
     87 }
     88 
     89 bool AudioTrimmer::SupportDecoderRecycling() const {
     90  return mDecoder->SupportDecoderRecycling();
     91 }
     92 
     93 bool AudioTrimmer::ShouldDecoderAlwaysBeRecycled() const {
     94  return mDecoder->ShouldDecoderAlwaysBeRecycled();
     95 }
     96 
     97 MediaDataDecoder::ConversionRequired AudioTrimmer::NeedsConversion() const {
     98  return mDecoder->NeedsConversion();
     99 }
    100 
    101 RefPtr<MediaDataDecoder::DecodePromise> AudioTrimmer::HandleDecodedResult(
    102    DecodePromise::ResolveOrRejectValue&& aValue) {
    103  MOZ_ASSERT(mThread->IsOnCurrentThread(),
    104             "We're not on the thread we were first initialized on");
    105  if (aValue.IsReject()) {
    106    return DecodePromise::CreateAndReject(std::move(aValue.RejectValue()),
    107                                          __func__);
    108  }
    109  MediaDataDecoder::DecodedData results = std::move(aValue.ResolveValue());
    110  LOG("HandleDecodedResults: %zu decoded data, %zu trimmers", results.Length(),
    111      mTrimmers.Length());
    112  if (results.IsEmpty()) {
    113    // No samples returned, we assume this is due to the latency of the
    114    // decoder and that the related decoded sample will be returned during
    115    // the next call to Decode().
    116    LOGV("No sample returned -- decoder has latency");
    117    return DecodePromise::CreateAndResolve(std::move(results), __func__);
    118  }
    119 
    120  for (uint32_t i = 0; i < results.Length();) {
    121    const RefPtr<MediaData>& data = results[i];
    122    MOZ_ASSERT(data->mType == MediaData::Type::AUDIO_DATA);
    123 
    124    if (!data->mDuration.IsValid()) {
    125      return DecodePromise::CreateAndReject(std::move(aValue.RejectValue()),
    126                                            __func__);
    127    }
    128    TimeInterval sampleInterval(data->mTime, data->GetEndTime());
    129    if (mTrimmers.IsEmpty()) {
    130      // mTrimmers being empty can only occurs if the decoder returned more
    131      // frames than we pushed in. We can't handle this case, abort trimming.
    132      LOG("decoded buffer [%s, %s] has no trimming information)",
    133          sampleInterval.mStart.ToString().get(),
    134          sampleInterval.mEnd.ToString().get());
    135      i++;
    136      continue;
    137    }
    138 
    139    Maybe<TimeInterval> trimmer = mTrimmers[0];
    140    mTrimmers.RemoveElementAt(0);
    141    if (!trimmer) {
    142      // Those frames didn't need trimming.
    143      LOGV("decoded buffer [%s, %s] doesn't need trimming",
    144           sampleInterval.mStart.ToString().get(),
    145           sampleInterval.mEnd.ToString().get());
    146      i++;
    147      continue;
    148    }
    149    if (!trimmer->Intersects(sampleInterval)) {
    150      LOGV("decoded buffer [%s, %s] would be empty after trimming, dropping it",
    151           sampleInterval.mStart.ToString().get(),
    152           sampleInterval.mEnd.ToString().get());
    153      results.RemoveElementAt(i);
    154      continue;
    155    }
    156    LOGV("Trimming sample[%s,%s] to [%s,%s]",
    157         sampleInterval.mStart.ToString().get(),
    158         sampleInterval.mEnd.ToString().get(), trimmer->mStart.ToString().get(),
    159         trimmer->mEnd.ToString().get());
    160 
    161    TimeInterval trim({std::max(trimmer->mStart, sampleInterval.mStart),
    162                       std::min(trimmer->mEnd, sampleInterval.mEnd)});
    163    AudioData* sample = static_cast<AudioData*>(data.get());
    164    bool ok = sample->SetTrimWindow(trim);
    165    NS_ASSERTION(ok, "Trimming of audio sample failed");
    166    (void)ok;
    167    if (sample->Frames() == 0) {
    168      LOGV("sample[%s, %s] is empty after trimming, dropping it",
    169           sampleInterval.mStart.ToString().get(),
    170           sampleInterval.mEnd.ToString().get());
    171      results.RemoveElementAt(i);
    172      continue;
    173    }
    174    i++;
    175  }
    176  return DecodePromise::CreateAndResolve(std::move(results), __func__);
    177 }
    178 
    179 RefPtr<MediaDataDecoder::DecodePromise> AudioTrimmer::DecodeBatch(
    180    nsTArray<RefPtr<MediaRawData>>&& aSamples) {
    181  MOZ_ASSERT(mThread->IsOnCurrentThread(),
    182             "We're not on the thread we were first initialized on");
    183  LOGV("DecodeBatch");
    184 
    185  for (auto&& sample : aSamples) {
    186    PrepareTrimmers(sample);
    187  }
    188  RefPtr<DecodePromise> p =
    189      mDecoder->DecodeBatch(std::move(aSamples))
    190          ->Then(GetCurrentSerialEventTarget(), __func__,
    191                 [self = RefPtr{this}](
    192                     DecodePromise::ResolveOrRejectValue&& aValue) {
    193                   return self->HandleDecodedResult(std::move(aValue));
    194                 });
    195  return p;
    196 }
    197 
    198 void AudioTrimmer::PrepareTrimmers(MediaRawData* aRaw) {
    199  // A compress sample indicates that it needs to be trimmed after decoding by
    200  // having its mOriginalPresentationWindow member set; in which case
    201  // mOriginalPresentationWindow contains the original time and duration of
    202  // the frame set by the demuxer and mTime and mDuration set to what it
    203  // should be after trimming.
    204  if (aRaw->mOriginalPresentationWindow) {
    205    LOGV("sample[%s, %s] has trimming info ([%s, %s]",
    206         aRaw->mOriginalPresentationWindow->mStart.ToString().get(),
    207         aRaw->mOriginalPresentationWindow->mEnd.ToString().get(),
    208         aRaw->mTime.ToString().get(), aRaw->GetEndTime().ToString().get());
    209    mTrimmers.AppendElement(
    210        Some(TimeInterval(aRaw->mTime, aRaw->GetEndTime())));
    211    aRaw->mTime = aRaw->mOriginalPresentationWindow->mStart;
    212    aRaw->mDuration = aRaw->mOriginalPresentationWindow->Length();
    213  } else {
    214    LOGV("sample[%s,%s] no trimming information", aRaw->mTime.ToString().get(),
    215         aRaw->GetEndTime().ToString().get());
    216    mTrimmers.AppendElement(Nothing());
    217  }
    218 }
    219 
    220 }  // namespace mozilla
    221 
    222 #undef LOG
    223 #undef LOGV