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