/src/mozilla-central/dom/media/MediaFormatReader.cpp
Line | Count | Source (jump to first uncovered line) |
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 "MediaFormatReader.h" |
8 | | |
9 | | #include "AllocationPolicy.h" |
10 | | #include "MediaData.h" |
11 | | #include "MediaInfo.h" |
12 | | #include "VideoFrameContainer.h" |
13 | | #include "VideoUtils.h" |
14 | | #include "mozilla/AbstractThread.h" |
15 | | #include "mozilla/CDMProxy.h" |
16 | | #include "mozilla/ClearOnShutdown.h" |
17 | | #include "mozilla/NotNull.h" |
18 | | #include "mozilla/Preferences.h" |
19 | | #include "mozilla/SharedThreadPool.h" |
20 | | #include "mozilla/StaticPrefs.h" |
21 | | #include "mozilla/TaskQueue.h" |
22 | | #include "mozilla/Telemetry.h" |
23 | | #include "mozilla/Unused.h" |
24 | | #include "nsContentUtils.h" |
25 | | #include "nsPrintfCString.h" |
26 | | |
27 | | #include <algorithm> |
28 | | #include <map> |
29 | | #include <queue> |
30 | | |
31 | | using namespace mozilla::media; |
32 | | |
33 | | static mozilla::LazyLogModule sFormatDecoderLog("MediaFormatReader"); |
34 | | mozilla::LazyLogModule gMediaDemuxerLog("MediaDemuxer"); |
35 | | |
36 | | #define LOG(arg, ...) \ |
37 | 0 | DDMOZ_LOG(sFormatDecoderLog, \ |
38 | 0 | mozilla::LogLevel::Debug, \ |
39 | 0 | "::%s: " arg, \ |
40 | 0 | __func__, \ |
41 | 0 | ##__VA_ARGS__) |
42 | | #define LOGV(arg, ...) \ |
43 | 0 | DDMOZ_LOG(sFormatDecoderLog, \ |
44 | 0 | mozilla::LogLevel::Verbose, \ |
45 | 0 | "::%s: " arg, \ |
46 | 0 | __func__, \ |
47 | 0 | ##__VA_ARGS__) |
48 | | |
49 | | #define NS_DispatchToMainThread(...) CompileError_UseAbstractMainThreadInstead |
50 | | |
51 | | namespace mozilla { |
52 | | |
53 | | |
54 | | typedef void* MediaDataDecoderID; |
55 | | |
56 | | /** |
57 | | * This helper class is used to report telemetry of the time used to recover a |
58 | | * decoder from GPU crash. |
59 | | * It uses MediaDecoderOwnerID to identify which video we're dealing with. |
60 | | * It uses MediaDataDecoderID to make sure that the old MediaDataDecoder has |
61 | | * been deleted and we're already recovered. |
62 | | * It reports two recovery times, one is calculated from GPU crashed (that is, |
63 | | * the time when VideoDecoderChild::ActorDestory() is called) and the other is |
64 | | * calculated from the MFR is notified with NS_ERROR_DOM_MEDIA_NEED_NEW_DECODER |
65 | | * error. |
66 | | */ |
67 | | class GPUProcessCrashTelemetryLogger |
68 | | { |
69 | | struct GPUCrashData |
70 | | { |
71 | | GPUCrashData(MediaDataDecoderID aMediaDataDecoderID, |
72 | | mozilla::TimeStamp aGPUCrashTime, |
73 | | mozilla::TimeStamp aErrorNotifiedTime) |
74 | | : mMediaDataDecoderID(aMediaDataDecoderID) |
75 | | , mGPUCrashTime(aGPUCrashTime) |
76 | | , mErrorNotifiedTime(aErrorNotifiedTime) |
77 | 0 | { |
78 | 0 | MOZ_ASSERT(mMediaDataDecoderID); |
79 | 0 | MOZ_ASSERT(!mGPUCrashTime.IsNull()); |
80 | 0 | MOZ_ASSERT(!mErrorNotifiedTime.IsNull()); |
81 | 0 | } |
82 | | |
83 | | MediaDataDecoderID mMediaDataDecoderID; |
84 | | mozilla::TimeStamp mGPUCrashTime; |
85 | | mozilla::TimeStamp mErrorNotifiedTime; |
86 | | }; |
87 | | |
88 | | public: |
89 | | static void |
90 | | RecordGPUCrashData(MediaDecoderOwnerID aMediaDecoderOwnerID, |
91 | | MediaDataDecoderID aMediaDataDecoderID, |
92 | | const TimeStamp& aGPUCrashTime, |
93 | | const TimeStamp& aErrorNotifiedTime) |
94 | 0 | { |
95 | 0 | MOZ_ASSERT(aMediaDecoderOwnerID); |
96 | 0 | MOZ_ASSERT(aMediaDataDecoderID); |
97 | 0 | MOZ_ASSERT(!aGPUCrashTime.IsNull()); |
98 | 0 | MOZ_ASSERT(!aErrorNotifiedTime.IsNull()); |
99 | 0 | StaticMutexAutoLock lock(sGPUCrashMapMutex); |
100 | 0 | auto it = sGPUCrashDataMap.find(aMediaDecoderOwnerID); |
101 | 0 | if (it == sGPUCrashDataMap.end()) { |
102 | 0 | sGPUCrashDataMap.insert(std::make_pair(aMediaDecoderOwnerID, |
103 | 0 | GPUCrashData(aMediaDataDecoderID, |
104 | 0 | aGPUCrashTime, |
105 | 0 | aErrorNotifiedTime))); |
106 | 0 | } |
107 | 0 | } |
108 | | |
109 | | static void |
110 | | ReportTelemetry(MediaDecoderOwnerID aMediaDecoderOwnerID, |
111 | | MediaDataDecoderID aMediaDataDecoderID) |
112 | 0 | { |
113 | 0 | MOZ_ASSERT(aMediaDecoderOwnerID); |
114 | 0 | MOZ_ASSERT(aMediaDataDecoderID); |
115 | 0 | StaticMutexAutoLock lock(sGPUCrashMapMutex); |
116 | 0 | auto it = sGPUCrashDataMap.find(aMediaDecoderOwnerID); |
117 | 0 | if (it != sGPUCrashDataMap.end() && |
118 | 0 | it->second.mMediaDataDecoderID != aMediaDataDecoderID) { |
119 | 0 | Telemetry::AccumulateTimeDelta( |
120 | 0 | Telemetry::VIDEO_HW_DECODER_CRASH_RECOVERY_TIME_SINCE_GPU_CRASHED_MS, |
121 | 0 | it->second.mGPUCrashTime); |
122 | 0 | Telemetry::AccumulateTimeDelta( |
123 | 0 | Telemetry::VIDEO_HW_DECODER_CRASH_RECOVERY_TIME_SINCE_MFR_NOTIFIED_MS, |
124 | 0 | it->second.mErrorNotifiedTime); |
125 | 0 | sGPUCrashDataMap.erase(aMediaDecoderOwnerID); |
126 | 0 | } |
127 | 0 | } |
128 | | |
129 | | private: |
130 | | static std::map<MediaDecoderOwnerID, GPUCrashData> sGPUCrashDataMap; |
131 | | static StaticMutex sGPUCrashMapMutex; |
132 | | }; |
133 | | |
134 | | std::map<MediaDecoderOwnerID, GPUProcessCrashTelemetryLogger::GPUCrashData> |
135 | | GPUProcessCrashTelemetryLogger::sGPUCrashDataMap; |
136 | | StaticMutex GPUProcessCrashTelemetryLogger::sGPUCrashMapMutex; |
137 | | |
138 | | /** |
139 | | * This class addresses the concern of bug 1339310 comment 4 where the Widevine |
140 | | * CDM doesn't support running multiple instances of a video decoder at once per |
141 | | * CDM instance by sequencing the order of decoder creation and shutdown. Note |
142 | | * this class addresses a different concern from that of GlobalAllocPolicy which |
143 | | * controls a system-wide number of decoders while this class control a per-MFR |
144 | | * number (which is one per CDM requirement). |
145 | | */ |
146 | | class LocalAllocPolicy |
147 | | { |
148 | | using TrackType = TrackInfo::TrackType; |
149 | | using Promise = GlobalAllocPolicy::Promise; |
150 | | using Token = GlobalAllocPolicy::Token; |
151 | | |
152 | | NS_INLINE_DECL_THREADSAFE_REFCOUNTING(LocalAllocPolicy) |
153 | | |
154 | | public: |
155 | | LocalAllocPolicy(TrackType aTrack, TaskQueue* aOwnerThread) |
156 | | : mTrack(aTrack) |
157 | | , mOwnerThread(aOwnerThread) |
158 | 0 | { |
159 | 0 | } |
160 | | |
161 | | // Acquire a token for decoder creation. Note the resolved token will |
162 | | // aggregate a GlobalAllocPolicy token to comply to its policy. Note |
163 | | // this function shouldn't be called again until the returned promise |
164 | | // is resolved or rejected. |
165 | | RefPtr<Promise> Alloc(); |
166 | | |
167 | | // Cancel the request to GlobalAllocPolicy and reject the current token |
168 | | // request. Note this must happen before mOwnerThread->BeginShutdown(). |
169 | | void Cancel(); |
170 | | |
171 | | private: |
172 | | /* |
173 | | * An RAII class to manage LocalAllocPolicy::mDecoderLimit. |
174 | | */ |
175 | | class AutoDeallocToken : public Token |
176 | | { |
177 | | public: |
178 | | explicit AutoDeallocToken(LocalAllocPolicy* aOwner) |
179 | | : mOwner(aOwner) |
180 | 0 | { |
181 | 0 | MOZ_DIAGNOSTIC_ASSERT(mOwner->mDecoderLimit > 0); |
182 | 0 | --mOwner->mDecoderLimit; |
183 | 0 | } |
184 | | // Aggregate a GlobalAllocPolicy token to present a single instance of |
185 | | // Token to the client so the client doesn't have to deal with |
186 | | // GlobalAllocPolicy and LocalAllocPolicy separately. |
187 | | void Append(Token* aToken) |
188 | 0 | { |
189 | 0 | mToken = aToken; |
190 | 0 | } |
191 | | private: |
192 | | // Release tokens allocated from GlobalAllocPolicy and LocalAllocPolicy |
193 | | // and process next token request if any. |
194 | | ~AutoDeallocToken() |
195 | 0 | { |
196 | 0 | mToken = nullptr; // Dealloc the global token. |
197 | 0 | ++mOwner->mDecoderLimit; // Dealloc the local token. |
198 | 0 | mOwner->ProcessRequest(); // Process next pending request. |
199 | 0 | } |
200 | | RefPtr<LocalAllocPolicy> mOwner; |
201 | | RefPtr<Token> mToken; |
202 | | }; |
203 | | |
204 | 0 | ~LocalAllocPolicy() { } |
205 | | void ProcessRequest(); |
206 | | |
207 | | int mDecoderLimit = 1; |
208 | | const TrackType mTrack; |
209 | | RefPtr<TaskQueue> mOwnerThread; |
210 | | MozPromiseHolder<Promise> mPendingPromise; |
211 | | MozPromiseRequestHolder<Promise> mTokenRequest; |
212 | | }; |
213 | | |
214 | | RefPtr<LocalAllocPolicy::Promise> |
215 | | LocalAllocPolicy::Alloc() |
216 | 0 | { |
217 | 0 | MOZ_ASSERT(mOwnerThread->IsCurrentThreadIn()); |
218 | 0 | MOZ_DIAGNOSTIC_ASSERT(mPendingPromise.IsEmpty()); |
219 | 0 | RefPtr<Promise> p = mPendingPromise.Ensure(__func__); |
220 | 0 | if (mDecoderLimit > 0) { |
221 | 0 | ProcessRequest(); |
222 | 0 | } |
223 | 0 | return p.forget(); |
224 | 0 | } |
225 | | |
226 | | void |
227 | | LocalAllocPolicy::ProcessRequest() |
228 | 0 | { |
229 | 0 | MOZ_ASSERT(mOwnerThread->IsCurrentThreadIn()); |
230 | 0 | MOZ_DIAGNOSTIC_ASSERT(mDecoderLimit > 0); |
231 | 0 |
|
232 | 0 | // No pending request. |
233 | 0 | if (mPendingPromise.IsEmpty()) { |
234 | 0 | return; |
235 | 0 | } |
236 | 0 | |
237 | 0 | RefPtr<AutoDeallocToken> token = new AutoDeallocToken(this); |
238 | 0 | RefPtr<LocalAllocPolicy> self = this; |
239 | 0 |
|
240 | 0 | GlobalAllocPolicy::Instance(mTrack).Alloc()->Then( |
241 | 0 | mOwnerThread, __func__, |
242 | 0 | [self, token](RefPtr<Token> aToken) { |
243 | 0 | self->mTokenRequest.Complete(); |
244 | 0 | token->Append(aToken); |
245 | 0 | self->mPendingPromise.Resolve(token, __func__); |
246 | 0 | }, |
247 | 0 | [self, token]() { |
248 | 0 | self->mTokenRequest.Complete(); |
249 | 0 | self->mPendingPromise.Reject(true, __func__); |
250 | 0 | })->Track(mTokenRequest); |
251 | 0 | } |
252 | | |
253 | | void |
254 | | LocalAllocPolicy::Cancel() |
255 | 0 | { |
256 | 0 | MOZ_ASSERT(mOwnerThread->IsCurrentThreadIn()); |
257 | 0 | mPendingPromise.RejectIfExists(true, __func__); |
258 | 0 | mTokenRequest.DisconnectIfExists(); |
259 | 0 | } |
260 | | |
261 | | /** |
262 | | * This class tracks shutdown promises to ensure all decoders are shut down |
263 | | * completely before MFR continues the rest of the shutdown procedure. |
264 | | */ |
265 | | class MediaFormatReader::ShutdownPromisePool |
266 | | { |
267 | | public: |
268 | | ShutdownPromisePool() |
269 | | : mOnShutdownComplete(new ShutdownPromise::Private(__func__)) |
270 | 0 | { |
271 | 0 | } |
272 | | |
273 | | // Return a promise which will be resolved when all the tracking promises |
274 | | // are resolved. Note no more promises should be added for tracking once |
275 | | // this function is called. |
276 | | RefPtr<ShutdownPromise> Shutdown(); |
277 | | |
278 | | // Track a shutdown promise. |
279 | | void Track(RefPtr<ShutdownPromise> aPromise); |
280 | | |
281 | | // Shut down a decoder and track its shutdown promise. |
282 | | void ShutdownDecoder(already_AddRefed<MediaDataDecoder> aDecoder) |
283 | 0 | { |
284 | 0 | Track(RefPtr<MediaDataDecoder>(aDecoder)->Shutdown()); |
285 | 0 | } |
286 | | |
287 | | private: |
288 | | bool mShutdown = false; |
289 | | const RefPtr<ShutdownPromise::Private> mOnShutdownComplete; |
290 | | nsTHashtable<nsRefPtrHashKey<ShutdownPromise>> mPromises; |
291 | | }; |
292 | | |
293 | | RefPtr<ShutdownPromise> |
294 | | MediaFormatReader::ShutdownPromisePool::Shutdown() |
295 | 0 | { |
296 | 0 | MOZ_DIAGNOSTIC_ASSERT(!mShutdown); |
297 | 0 | mShutdown = true; |
298 | 0 | if (mPromises.Count() == 0) { |
299 | 0 | mOnShutdownComplete->Resolve(true, __func__); |
300 | 0 | } |
301 | 0 | return mOnShutdownComplete; |
302 | 0 | } |
303 | | |
304 | | void |
305 | | MediaFormatReader::ShutdownPromisePool::Track(RefPtr<ShutdownPromise> aPromise) |
306 | 0 | { |
307 | 0 | MOZ_DIAGNOSTIC_ASSERT(!mShutdown); |
308 | 0 | MOZ_DIAGNOSTIC_ASSERT(!mPromises.Contains(aPromise)); |
309 | 0 | mPromises.PutEntry(aPromise); |
310 | 0 | aPromise->Then( |
311 | 0 | AbstractThread::GetCurrent(), __func__, |
312 | 0 | [aPromise, this]() { |
313 | 0 | MOZ_DIAGNOSTIC_ASSERT(mPromises.Contains(aPromise)); |
314 | 0 | mPromises.RemoveEntry(aPromise); |
315 | 0 | if (mShutdown && mPromises.Count() == 0) { |
316 | 0 | mOnShutdownComplete->Resolve(true, __func__); |
317 | 0 | } |
318 | 0 | }); |
319 | 0 | } |
320 | | |
321 | | void |
322 | | MediaFormatReader::DecoderData::ShutdownDecoder() |
323 | 0 | { |
324 | 0 | MutexAutoLock lock(mMutex); |
325 | 0 |
|
326 | 0 | if (!mDecoder) { |
327 | 0 | // No decoder to shut down. |
328 | 0 | return; |
329 | 0 | } |
330 | 0 | |
331 | 0 | if (mFlushing) { |
332 | 0 | // Flush is is in action. Shutdown will be initiated after flush completes. |
333 | 0 | MOZ_DIAGNOSTIC_ASSERT(mShutdownPromise); |
334 | 0 | mOwner->mShutdownPromisePool->Track(mShutdownPromise->Ensure(__func__)); |
335 | 0 | // The order of decoder creation and shutdown is handled by LocalAllocPolicy |
336 | 0 | // and ShutdownPromisePool. MFR can now reset these members to a fresh state |
337 | 0 | // and be ready to create new decoders again without explicitly waiting for |
338 | 0 | // flush/shutdown to complete. |
339 | 0 | mShutdownPromise = nullptr; |
340 | 0 | mFlushing = false; |
341 | 0 | } else { |
342 | 0 | // No flush is in action. We can shut down the decoder now. |
343 | 0 | mOwner->mShutdownPromisePool->Track(mDecoder->Shutdown()); |
344 | 0 | } |
345 | 0 |
|
346 | 0 | // mShutdownPromisePool will handle the order of decoder shutdown so |
347 | 0 | // we can forget mDecoder and be ready to create a new one. |
348 | 0 | mDecoder = nullptr; |
349 | 0 | mDescription = NS_LITERAL_CSTRING("shutdown"); |
350 | 0 | mOwner->ScheduleUpdate(mType == MediaData::AUDIO_DATA |
351 | 0 | ? TrackType::kAudioTrack |
352 | 0 | : TrackType::kVideoTrack); |
353 | 0 | } |
354 | | |
355 | | void |
356 | | MediaFormatReader::DecoderData::Flush() |
357 | 0 | { |
358 | 0 | if (mFlushing || mFlushed) { |
359 | 0 | // Flush still pending or already flushed, nothing more to do. |
360 | 0 | return; |
361 | 0 | } |
362 | 0 | mDecodeRequest.DisconnectIfExists(); |
363 | 0 | mDrainRequest.DisconnectIfExists(); |
364 | 0 | mDrainState = DrainState::None; |
365 | 0 | CancelWaitingForKey(); |
366 | 0 | mOutput.Clear(); |
367 | 0 | mNumSamplesInput = 0; |
368 | 0 | mNumSamplesOutput = 0; |
369 | 0 | mSizeOfQueue = 0; |
370 | 0 | if (mDecoder) { |
371 | 0 | TrackType type = mType == MediaData::AUDIO_DATA |
372 | 0 | ? TrackType::kAudioTrack |
373 | 0 | : TrackType::kVideoTrack; |
374 | 0 | mFlushing = true; |
375 | 0 | MOZ_DIAGNOSTIC_ASSERT(!mShutdownPromise); |
376 | 0 | mShutdownPromise = new SharedShutdownPromiseHolder(); |
377 | 0 | RefPtr<SharedShutdownPromiseHolder> p = mShutdownPromise; |
378 | 0 | RefPtr<MediaDataDecoder> d = mDecoder; |
379 | 0 | DDLOGEX2("MediaFormatReader::DecoderData", |
380 | 0 | this, |
381 | 0 | DDLogCategory::Log, |
382 | 0 | "flushing", |
383 | 0 | DDNoValue{}); |
384 | 0 | mDecoder->Flush()->Then(mOwner->OwnerThread(), |
385 | 0 | __func__, |
386 | 0 | [type, this, p, d]() { |
387 | 0 | DDLOGEX2("MediaFormatReader::DecoderData", |
388 | 0 | this, |
389 | 0 | DDLogCategory::Log, |
390 | 0 | "flushed", |
391 | 0 | DDNoValue{}); |
392 | 0 | if (!p->IsEmpty()) { |
393 | 0 | // Shutdown happened before flush completes. |
394 | 0 | // Let's continue to shut down the decoder. Note |
395 | 0 | // we don't access |this| because this decoder |
396 | 0 | // is no longer managed by MFR::DecoderData. |
397 | 0 | d->Shutdown()->ChainTo(p->Steal(), __func__); |
398 | 0 | return; |
399 | 0 | } |
400 | 0 | mFlushing = false; |
401 | 0 | mShutdownPromise = nullptr; |
402 | 0 | mOwner->ScheduleUpdate(type); |
403 | 0 | }, |
404 | 0 | [type, this, p, d](const MediaResult& aError) { |
405 | 0 | DDLOGEX2("MediaFormatReader::DecoderData", |
406 | 0 | this, |
407 | 0 | DDLogCategory::Log, |
408 | 0 | "flush_error", |
409 | 0 | aError); |
410 | 0 | if (!p->IsEmpty()) { |
411 | 0 | d->Shutdown()->ChainTo(p->Steal(), __func__); |
412 | 0 | return; |
413 | 0 | } |
414 | 0 | mFlushing = false; |
415 | 0 | mShutdownPromise = nullptr; |
416 | 0 | mOwner->NotifyError(type, aError); |
417 | 0 | }); |
418 | 0 | } |
419 | 0 | mFlushed = true; |
420 | 0 | } |
421 | | |
422 | | class MediaFormatReader::DecoderFactory |
423 | | { |
424 | | using InitPromise = MediaDataDecoder::InitPromise; |
425 | | using TokenPromise = GlobalAllocPolicy::Promise; |
426 | | using Token = GlobalAllocPolicy::Token; |
427 | | |
428 | | public: |
429 | | explicit DecoderFactory(MediaFormatReader* aOwner) |
430 | | : mAudio(aOwner->mAudio, TrackInfo::kAudioTrack, aOwner->OwnerThread()) |
431 | | , mVideo(aOwner->mVideo, TrackInfo::kVideoTrack, aOwner->OwnerThread()) |
432 | | , mOwner(WrapNotNull(aOwner)) |
433 | 0 | { |
434 | 0 | DecoderDoctorLogger::LogConstruction("MediaFormatReader::DecoderFactory", |
435 | 0 | this); |
436 | 0 | DecoderDoctorLogger::LinkParentAndChild( |
437 | 0 | aOwner, "decoder factory", "MediaFormatReader::DecoderFactory", this); |
438 | 0 | } |
439 | | |
440 | | ~DecoderFactory() |
441 | 0 | { |
442 | 0 | DecoderDoctorLogger::LogDestruction("MediaFormatReader::DecoderFactory", |
443 | 0 | this); |
444 | 0 | } |
445 | | |
446 | | void CreateDecoder(TrackType aTrack); |
447 | | |
448 | | // Shutdown any decoder pending initialization and reset mAudio/mVideo to its |
449 | | // pristine state so CreateDecoder() is ready to be called again immediately. |
450 | | void ShutdownDecoder(TrackType aTrack) |
451 | 0 | { |
452 | 0 | MOZ_ASSERT(aTrack == TrackInfo::kAudioTrack || |
453 | 0 | aTrack == TrackInfo::kVideoTrack); |
454 | 0 | auto& data = aTrack == TrackInfo::kAudioTrack ? mAudio : mVideo; |
455 | 0 | data.mPolicy->Cancel(); |
456 | 0 | data.mTokenRequest.DisconnectIfExists(); |
457 | 0 | data.mInitRequest.DisconnectIfExists(); |
458 | 0 | if (data.mDecoder) { |
459 | 0 | mOwner->mShutdownPromisePool->ShutdownDecoder(data.mDecoder.forget()); |
460 | 0 | } |
461 | 0 | data.mStage = Stage::None; |
462 | 0 | MOZ_ASSERT(!data.mToken); |
463 | 0 | } |
464 | | |
465 | | private: |
466 | | enum class Stage : int8_t |
467 | | { |
468 | | None, |
469 | | WaitForToken, |
470 | | CreateDecoder, |
471 | | WaitForInit |
472 | | }; |
473 | | |
474 | | struct Data |
475 | | { |
476 | | Data(DecoderData& aOwnerData, TrackType aTrack, TaskQueue* aThread) |
477 | | : mOwnerData(aOwnerData) |
478 | | , mTrack(aTrack) |
479 | 0 | , mPolicy(new LocalAllocPolicy(aTrack, aThread)) { } |
480 | | DecoderData& mOwnerData; |
481 | | const TrackType mTrack; |
482 | | RefPtr<LocalAllocPolicy> mPolicy; |
483 | | Stage mStage = Stage::None; |
484 | | RefPtr<Token> mToken; |
485 | | RefPtr<MediaDataDecoder> mDecoder; |
486 | | MozPromiseRequestHolder<TokenPromise> mTokenRequest; |
487 | | MozPromiseRequestHolder<InitPromise> mInitRequest; |
488 | | } mAudio, mVideo; |
489 | | |
490 | | void RunStage(Data& aData); |
491 | | MediaResult DoCreateDecoder(Data& aData); |
492 | | void DoInitDecoder(Data& aData); |
493 | | |
494 | | // guaranteed to be valid by the owner. |
495 | | const NotNull<MediaFormatReader*> mOwner; |
496 | | }; |
497 | | |
498 | | void |
499 | | MediaFormatReader::DecoderFactory::CreateDecoder(TrackType aTrack) |
500 | 0 | { |
501 | 0 | MOZ_ASSERT(aTrack == TrackInfo::kAudioTrack || |
502 | 0 | aTrack == TrackInfo::kVideoTrack); |
503 | 0 | RunStage(aTrack == TrackInfo::kAudioTrack ? mAudio : mVideo); |
504 | 0 | } |
505 | | |
506 | | void |
507 | | MediaFormatReader::DecoderFactory::RunStage(Data& aData) |
508 | 0 | { |
509 | 0 | switch (aData.mStage) { |
510 | 0 | case Stage::None: { |
511 | 0 | MOZ_ASSERT(!aData.mToken); |
512 | 0 | aData.mPolicy->Alloc()->Then( |
513 | 0 | mOwner->OwnerThread(), __func__, |
514 | 0 | [this, &aData] (RefPtr<Token> aToken) { |
515 | 0 | aData.mTokenRequest.Complete(); |
516 | 0 | aData.mToken = aToken.forget(); |
517 | 0 | aData.mStage = Stage::CreateDecoder; |
518 | 0 | RunStage(aData); |
519 | 0 | }, |
520 | 0 | [&aData] () { |
521 | 0 | aData.mTokenRequest.Complete(); |
522 | 0 | aData.mStage = Stage::None; |
523 | 0 | })->Track(aData.mTokenRequest); |
524 | 0 | aData.mStage = Stage::WaitForToken; |
525 | 0 | break; |
526 | 0 | } |
527 | 0 |
|
528 | 0 | case Stage::WaitForToken: { |
529 | 0 | MOZ_ASSERT(!aData.mToken); |
530 | 0 | MOZ_ASSERT(aData.mTokenRequest.Exists()); |
531 | 0 | break; |
532 | 0 | } |
533 | 0 |
|
534 | 0 | case Stage::CreateDecoder: { |
535 | 0 | MOZ_ASSERT(aData.mToken); |
536 | 0 | MOZ_ASSERT(!aData.mDecoder); |
537 | 0 | MOZ_ASSERT(!aData.mInitRequest.Exists()); |
538 | 0 |
|
539 | 0 | MediaResult rv = DoCreateDecoder(aData); |
540 | 0 | if (NS_FAILED(rv)) { |
541 | 0 | NS_WARNING("Error constructing decoders"); |
542 | 0 | aData.mToken = nullptr; |
543 | 0 | aData.mStage = Stage::None; |
544 | 0 | aData.mOwnerData.mDescription = rv.Description(); |
545 | 0 | DDLOGEX2("MediaFormatReader::DecoderFactory", |
546 | 0 | this, |
547 | 0 | DDLogCategory::Log, |
548 | 0 | "create_decoder_error", |
549 | 0 | rv); |
550 | 0 | mOwner->NotifyError(aData.mTrack, rv); |
551 | 0 | return; |
552 | 0 | } |
553 | 0 |
|
554 | 0 | aData.mDecoder = |
555 | 0 | new AllocationWrapper(aData.mDecoder.forget(), aData.mToken.forget()); |
556 | 0 | DecoderDoctorLogger::LinkParentAndChild( |
557 | 0 | aData.mDecoder.get(), |
558 | 0 | "decoder", |
559 | 0 | "MediaFormatReader::DecoderFactory", |
560 | 0 | this); |
561 | 0 |
|
562 | 0 | DoInitDecoder(aData); |
563 | 0 | aData.mStage = Stage::WaitForInit; |
564 | 0 | break; |
565 | 0 | } |
566 | 0 |
|
567 | 0 | case Stage::WaitForInit: { |
568 | 0 | MOZ_ASSERT(aData.mDecoder); |
569 | 0 | MOZ_ASSERT(aData.mInitRequest.Exists()); |
570 | 0 | break; |
571 | 0 | } |
572 | 0 | } |
573 | 0 | } |
574 | | |
575 | | MediaResult |
576 | | MediaFormatReader::DecoderFactory::DoCreateDecoder(Data& aData) |
577 | 0 | { |
578 | 0 | auto& ownerData = aData.mOwnerData; |
579 | 0 | auto& decoder = mOwner->GetDecoderData(aData.mTrack); |
580 | 0 | auto& platform = |
581 | 0 | decoder.IsEncrypted() ? mOwner->mEncryptedPlatform : mOwner->mPlatform; |
582 | 0 |
|
583 | 0 | if (!platform) { |
584 | 0 | platform = new PDMFactory(); |
585 | 0 | if (decoder.IsEncrypted()) { |
586 | 0 | MOZ_ASSERT(mOwner->mCDMProxy); |
587 | 0 | platform->SetCDMProxy(mOwner->mCDMProxy); |
588 | 0 | } |
589 | 0 | } |
590 | 0 |
|
591 | 0 | // result may not be updated by PDMFactory::CreateDecoder, as such it must be |
592 | 0 | // initialized to a fatal error by default. |
593 | 0 | MediaResult result = MediaResult( |
594 | 0 | NS_ERROR_DOM_MEDIA_FATAL_ERR, |
595 | 0 | nsPrintfCString("error creating %s decoder", TrackTypeToStr(aData.mTrack))); |
596 | 0 |
|
597 | 0 | switch (aData.mTrack) { |
598 | 0 | case TrackInfo::kAudioTrack: { |
599 | 0 | aData.mDecoder = platform->CreateDecoder({ |
600 | 0 | *ownerData.GetCurrentInfo()->GetAsAudioInfo(), |
601 | 0 | ownerData.mTaskQueue, |
602 | 0 | mOwner->mCrashHelper, |
603 | 0 | CreateDecoderParams::UseNullDecoder(ownerData.mIsNullDecode), |
604 | 0 | &result, |
605 | 0 | TrackInfo::kAudioTrack, |
606 | 0 | &mOwner->OnTrackWaitingForKeyProducer() |
607 | 0 | }); |
608 | 0 | break; |
609 | 0 | } |
610 | 0 |
|
611 | 0 | case TrackType::kVideoTrack: { |
612 | 0 | // Decoders use the layers backend to decide if they can use hardware decoding, |
613 | 0 | // so specify LAYERS_NONE if we want to forcibly disable it. |
614 | 0 | aData.mDecoder = platform->CreateDecoder( |
615 | 0 | { *ownerData.GetCurrentInfo()->GetAsVideoInfo(), |
616 | 0 | ownerData.mTaskQueue, |
617 | 0 | mOwner->mKnowsCompositor, |
618 | 0 | mOwner->GetImageContainer(), |
619 | 0 | mOwner->mCrashHelper, |
620 | 0 | CreateDecoderParams::UseNullDecoder(ownerData.mIsNullDecode), |
621 | 0 | &result, |
622 | 0 | TrackType::kVideoTrack, |
623 | 0 | &mOwner->OnTrackWaitingForKeyProducer(), |
624 | 0 | CreateDecoderParams::VideoFrameRate(ownerData.mMeanRate.Mean()) }); |
625 | 0 | break; |
626 | 0 | } |
627 | 0 |
|
628 | 0 | default: |
629 | 0 | break; |
630 | 0 | } |
631 | 0 | |
632 | 0 | if (aData.mDecoder) { |
633 | 0 | return NS_OK; |
634 | 0 | } |
635 | 0 | |
636 | 0 | MOZ_RELEASE_ASSERT(NS_FAILED(result), "PDM returned an invalid error code"); |
637 | 0 |
|
638 | 0 | return result; |
639 | 0 | } |
640 | | |
641 | | void |
642 | | MediaFormatReader::DecoderFactory::DoInitDecoder(Data& aData) |
643 | 0 | { |
644 | 0 | auto& ownerData = aData.mOwnerData; |
645 | 0 |
|
646 | 0 | DDLOGEX2("MediaFormatReader::DecoderFactory", |
647 | 0 | this, |
648 | 0 | DDLogCategory::Log, |
649 | 0 | "initialize_decoder", |
650 | 0 | DDNoValue{}); |
651 | 0 | aData.mDecoder->Init() |
652 | 0 | ->Then(mOwner->OwnerThread(), __func__, |
653 | 0 | [this, &aData, &ownerData](TrackType aTrack) { |
654 | 0 | aData.mInitRequest.Complete(); |
655 | 0 | aData.mStage = Stage::None; |
656 | 0 | MutexAutoLock lock(ownerData.mMutex); |
657 | 0 | ownerData.mDecoder = aData.mDecoder.forget(); |
658 | 0 | ownerData.mDescription = ownerData.mDecoder->GetDescriptionName(); |
659 | 0 | DDLOGEX2("MediaFormatReader::DecoderFactory", |
660 | 0 | this, |
661 | 0 | DDLogCategory::Log, |
662 | 0 | "decoder_initialized", |
663 | 0 | DDNoValue{}); |
664 | 0 | DecoderDoctorLogger::LinkParentAndChild( |
665 | 0 | "MediaFormatReader::DecoderData", |
666 | 0 | &ownerData, |
667 | 0 | "decoder", |
668 | 0 | ownerData.mDecoder.get()); |
669 | 0 | mOwner->SetVideoDecodeThreshold(); |
670 | 0 | mOwner->ScheduleUpdate(aTrack); |
671 | 0 | }, |
672 | 0 | [this, &aData, &ownerData](const MediaResult& aError) { |
673 | 0 | aData.mInitRequest.Complete(); |
674 | 0 | MOZ_RELEASE_ASSERT(!ownerData.mDecoder, |
675 | 0 | "Can't have a decoder already set"); |
676 | 0 | aData.mStage = Stage::None; |
677 | 0 | mOwner->mShutdownPromisePool->ShutdownDecoder(aData.mDecoder.forget()); |
678 | 0 | DDLOGEX2("MediaFormatReader::DecoderFactory", |
679 | 0 | this, |
680 | 0 | DDLogCategory::Log, |
681 | 0 | "initialize_decoder_error", |
682 | 0 | aError); |
683 | 0 | mOwner->NotifyError(aData.mTrack, aError); |
684 | 0 | }) |
685 | 0 | ->Track(aData.mInitRequest); |
686 | 0 | } |
687 | | |
688 | | // DemuxerProxy ensures that the original main demuxer is only ever accessed |
689 | | // via its own dedicated task queue. |
690 | | // This ensure that the reader's taskqueue will never blocked while a demuxer |
691 | | // is itself blocked attempting to access the MediaCache or the MediaResource. |
692 | | class MediaFormatReader::DemuxerProxy |
693 | | { |
694 | | using TrackType = TrackInfo::TrackType; |
695 | | class Wrapper; |
696 | | |
697 | | public: |
698 | | explicit DemuxerProxy(MediaDataDemuxer* aDemuxer) |
699 | | : mTaskQueue( |
700 | | new TaskQueue(GetMediaThreadPool(MediaThreadType::PLATFORM_DECODER), |
701 | | "DemuxerProxy::mTaskQueue")) |
702 | | , mData(new Data(aDemuxer)) |
703 | 0 | { |
704 | 0 | MOZ_COUNT_CTOR(DemuxerProxy); |
705 | 0 | } |
706 | | |
707 | | ~DemuxerProxy() |
708 | 0 | { |
709 | 0 | MOZ_COUNT_DTOR(DemuxerProxy); |
710 | 0 | } |
711 | | |
712 | | RefPtr<ShutdownPromise> Shutdown() |
713 | 0 | { |
714 | 0 | RefPtr<Data> data = mData.forget(); |
715 | 0 | return InvokeAsync(mTaskQueue, __func__, [data]() { |
716 | 0 | // We need to clear our reference to the demuxer now. So that in the event |
717 | 0 | // the init promise wasn't resolved, such as what can happen with the |
718 | 0 | // mediasource demuxer that is waiting on more data, it will force the |
719 | 0 | // init promise to be rejected. |
720 | 0 | data->mDemuxer = nullptr; |
721 | 0 | data->mAudioDemuxer = nullptr; |
722 | 0 | data->mVideoDemuxer = nullptr; |
723 | 0 | return ShutdownPromise::CreateAndResolve(true, __func__); |
724 | 0 | }); |
725 | 0 | } |
726 | | |
727 | | RefPtr<MediaDataDemuxer::InitPromise> Init(); |
728 | | |
729 | | Wrapper* |
730 | | GetTrackDemuxer(TrackType aTrack, uint32_t aTrackNumber) |
731 | 0 | { |
732 | 0 | MOZ_RELEASE_ASSERT(mData && mData->mInitDone); |
733 | 0 |
|
734 | 0 | switch (aTrack) { |
735 | 0 | case TrackInfo::kAudioTrack: |
736 | 0 | return mData->mAudioDemuxer; |
737 | 0 | case TrackInfo::kVideoTrack: |
738 | 0 | return mData->mVideoDemuxer; |
739 | 0 | default: |
740 | 0 | return nullptr; |
741 | 0 | } |
742 | 0 | } |
743 | | |
744 | | uint32_t GetNumberTracks(TrackType aTrack) const |
745 | 0 | { |
746 | 0 | MOZ_RELEASE_ASSERT(mData && mData->mInitDone); |
747 | 0 |
|
748 | 0 | switch (aTrack) { |
749 | 0 | case TrackInfo::kAudioTrack: |
750 | 0 | return mData->mNumAudioTrack; |
751 | 0 | case TrackInfo::kVideoTrack: |
752 | 0 | return mData->mNumVideoTrack; |
753 | 0 | default: |
754 | 0 | return 0; |
755 | 0 | } |
756 | 0 | } |
757 | | |
758 | | bool IsSeekable() const |
759 | 0 | { |
760 | 0 | MOZ_RELEASE_ASSERT(mData && mData->mInitDone); |
761 | 0 |
|
762 | 0 | return mData->mSeekable; |
763 | 0 | } |
764 | | |
765 | | bool IsSeekableOnlyInBufferedRanges() const |
766 | 0 | { |
767 | 0 | MOZ_RELEASE_ASSERT(mData && mData->mInitDone); |
768 | 0 |
|
769 | 0 | return mData->mSeekableOnlyInBufferedRange; |
770 | 0 | } |
771 | | |
772 | | UniquePtr<EncryptionInfo> GetCrypto() const |
773 | 0 | { |
774 | 0 | MOZ_RELEASE_ASSERT(mData && mData->mInitDone); |
775 | 0 |
|
776 | 0 | if (!mData->mCrypto) { |
777 | 0 | return nullptr; |
778 | 0 | } |
779 | 0 | auto crypto = MakeUnique<EncryptionInfo>(); |
780 | 0 | *crypto = *mData->mCrypto; |
781 | 0 | return crypto; |
782 | 0 | } |
783 | | |
784 | | RefPtr<NotifyDataArrivedPromise> NotifyDataArrived(); |
785 | | |
786 | | bool ShouldComputeStartTime() const |
787 | 0 | { |
788 | 0 | MOZ_RELEASE_ASSERT(mData && mData->mInitDone); |
789 | 0 |
|
790 | 0 | return mData->mShouldComputeStartTime; |
791 | 0 | } |
792 | | |
793 | | private: |
794 | | const RefPtr<TaskQueue> mTaskQueue; |
795 | | struct Data |
796 | | { |
797 | | NS_INLINE_DECL_THREADSAFE_REFCOUNTING(Data) |
798 | | |
799 | | explicit Data(MediaDataDemuxer* aDemuxer) |
800 | | : mInitDone(false) |
801 | | , mDemuxer(aDemuxer) |
802 | 0 | { |
803 | 0 | } |
804 | | |
805 | | Atomic<bool> mInitDone; |
806 | | // Only ever accessed over mTaskQueue once. |
807 | | RefPtr<MediaDataDemuxer> mDemuxer; |
808 | | // Only accessed once InitPromise has been resolved and immutable after. |
809 | | // So we can safely access them without the use of the mutex. |
810 | | uint32_t mNumAudioTrack = 0; |
811 | | RefPtr<Wrapper> mAudioDemuxer; |
812 | | uint32_t mNumVideoTrack = 0; |
813 | | RefPtr<Wrapper> mVideoDemuxer; |
814 | | bool mSeekable = false; |
815 | | bool mSeekableOnlyInBufferedRange = false; |
816 | | bool mShouldComputeStartTime = true; |
817 | | UniquePtr<EncryptionInfo> mCrypto; |
818 | | private: |
819 | 0 | ~Data() { } |
820 | | }; |
821 | | RefPtr<Data> mData; |
822 | | }; |
823 | | |
824 | | class MediaFormatReader::DemuxerProxy::Wrapper : public MediaTrackDemuxer |
825 | | { |
826 | | public: |
827 | | Wrapper(MediaTrackDemuxer* aTrackDemuxer, TaskQueue* aTaskQueue) |
828 | | : mMutex("TrackDemuxer Mutex") |
829 | | , mTaskQueue(aTaskQueue) |
830 | | , mGetSamplesMayBlock(aTrackDemuxer->GetSamplesMayBlock()) |
831 | | , mInfo(aTrackDemuxer->GetInfo()) |
832 | | , mTrackDemuxer(aTrackDemuxer) |
833 | 0 | { |
834 | 0 | DecoderDoctorLogger::LogConstructionAndBase( |
835 | 0 | "MediaFormatReader::DemuxerProxy::Wrapper", |
836 | 0 | this, |
837 | 0 | static_cast<const MediaTrackDemuxer*>(this)); |
838 | 0 | DecoderDoctorLogger::LinkParentAndChild( |
839 | 0 | "MediaFormatReader::DemuxerProxy::Wrapper", |
840 | 0 | this, |
841 | 0 | "track demuxer", |
842 | 0 | aTrackDemuxer); |
843 | 0 | } |
844 | | |
845 | | UniquePtr<TrackInfo> GetInfo() const override |
846 | 0 | { |
847 | 0 | if (!mInfo) { |
848 | 0 | return nullptr; |
849 | 0 | } |
850 | 0 | return mInfo->Clone(); |
851 | 0 | } |
852 | | |
853 | | RefPtr<SeekPromise> Seek(const TimeUnit& aTime) override |
854 | 0 | { |
855 | 0 | RefPtr<Wrapper> self = this; |
856 | 0 | return InvokeAsync( |
857 | 0 | mTaskQueue, __func__, |
858 | 0 | [self, aTime]() { return self->mTrackDemuxer->Seek(aTime); }) |
859 | 0 | ->Then(mTaskQueue, __func__, |
860 | 0 | [self](const TimeUnit& aTime) { |
861 | 0 | self->UpdateRandomAccessPoint(); |
862 | 0 | return SeekPromise::CreateAndResolve(aTime, __func__); |
863 | 0 | }, |
864 | 0 | [self](const MediaResult& aError) { |
865 | 0 | self->UpdateRandomAccessPoint(); |
866 | 0 | return SeekPromise::CreateAndReject(aError, __func__); |
867 | 0 | }); |
868 | 0 | } |
869 | | |
870 | | RefPtr<SamplesPromise> GetSamples(int32_t aNumSamples) override |
871 | 0 | { |
872 | 0 | RefPtr<Wrapper> self = this; |
873 | 0 | return InvokeAsync(mTaskQueue, __func__, |
874 | 0 | [self, aNumSamples]() { |
875 | 0 | return self->mTrackDemuxer->GetSamples(aNumSamples); |
876 | 0 | }) |
877 | 0 | ->Then(mTaskQueue, __func__, |
878 | 0 | [self](RefPtr<SamplesHolder> aSamples) { |
879 | 0 | self->UpdateRandomAccessPoint(); |
880 | 0 | return SamplesPromise::CreateAndResolve(aSamples.forget(), __func__); |
881 | 0 | }, |
882 | 0 | [self](const MediaResult& aError) { |
883 | 0 | self->UpdateRandomAccessPoint(); |
884 | 0 | return SamplesPromise::CreateAndReject(aError, __func__); |
885 | 0 | }); |
886 | 0 | } |
887 | | |
888 | | bool GetSamplesMayBlock() const override |
889 | 0 | { |
890 | 0 | return mGetSamplesMayBlock; |
891 | 0 | } |
892 | | |
893 | | void Reset() override |
894 | 0 | { |
895 | 0 | RefPtr<Wrapper> self = this; |
896 | 0 | nsresult rv = |
897 | 0 | mTaskQueue->Dispatch( |
898 | 0 | NS_NewRunnableFunction("MediaFormatReader::DemuxerProxy::Wrapper::Reset", |
899 | 0 | [self]() { self->mTrackDemuxer->Reset(); })); |
900 | 0 | MOZ_DIAGNOSTIC_ASSERT(NS_SUCCEEDED(rv)); |
901 | 0 | Unused << rv; |
902 | 0 | } |
903 | | |
904 | | nsresult GetNextRandomAccessPoint(TimeUnit* aTime) override |
905 | 0 | { |
906 | 0 | MutexAutoLock lock(mMutex); |
907 | 0 | if (NS_SUCCEEDED(mNextRandomAccessPointResult)) { |
908 | 0 | *aTime = mNextRandomAccessPoint; |
909 | 0 | } |
910 | 0 | return mNextRandomAccessPointResult; |
911 | 0 | } |
912 | | |
913 | | RefPtr<SkipAccessPointPromise> |
914 | | SkipToNextRandomAccessPoint(const TimeUnit& aTimeThreshold) override |
915 | 0 | { |
916 | 0 | RefPtr<Wrapper> self = this; |
917 | 0 | return InvokeAsync( |
918 | 0 | mTaskQueue, __func__, |
919 | 0 | [self, aTimeThreshold]() { |
920 | 0 | return self->mTrackDemuxer->SkipToNextRandomAccessPoint( |
921 | 0 | aTimeThreshold); |
922 | 0 | }) |
923 | 0 | ->Then(mTaskQueue, __func__, |
924 | 0 | [self](uint32_t aVal) { |
925 | 0 | self->UpdateRandomAccessPoint(); |
926 | 0 | return SkipAccessPointPromise::CreateAndResolve(aVal, __func__); |
927 | 0 | }, |
928 | 0 | [self](const SkipFailureHolder& aError) { |
929 | 0 | self->UpdateRandomAccessPoint(); |
930 | 0 | return SkipAccessPointPromise::CreateAndReject(aError, __func__); |
931 | 0 | }); |
932 | 0 | } |
933 | | |
934 | | TimeIntervals GetBuffered() override |
935 | 0 | { |
936 | 0 | MutexAutoLock lock(mMutex); |
937 | 0 | return mBuffered; |
938 | 0 | } |
939 | | |
940 | 0 | void BreakCycles() override { } |
941 | | |
942 | | private: |
943 | | Mutex mMutex; |
944 | | const RefPtr<TaskQueue> mTaskQueue; |
945 | | const bool mGetSamplesMayBlock; |
946 | | const UniquePtr<TrackInfo> mInfo; |
947 | | // mTrackDemuxer is only ever accessed on demuxer's task queue. |
948 | | RefPtr<MediaTrackDemuxer> mTrackDemuxer; |
949 | | // All following members are protected by mMutex |
950 | | nsresult mNextRandomAccessPointResult = NS_OK; |
951 | | TimeUnit mNextRandomAccessPoint; |
952 | | TimeIntervals mBuffered; |
953 | | friend class DemuxerProxy; |
954 | | |
955 | | ~Wrapper() |
956 | 0 | { |
957 | 0 | RefPtr<MediaTrackDemuxer> trackDemuxer = mTrackDemuxer.forget(); |
958 | 0 | nsresult rv = |
959 | 0 | mTaskQueue->Dispatch(NS_NewRunnableFunction( |
960 | 0 | "MediaFormatReader::DemuxerProxy::Wrapper::~Wrapper", |
961 | 0 | [trackDemuxer]() { trackDemuxer->BreakCycles(); })); |
962 | 0 | MOZ_DIAGNOSTIC_ASSERT(NS_SUCCEEDED(rv)); |
963 | 0 | Unused << rv; |
964 | 0 | DecoderDoctorLogger::LogDestruction( |
965 | 0 | "MediaFormatReader::DemuxerProxy::Wrapper", this); |
966 | 0 | } |
967 | | |
968 | | void UpdateRandomAccessPoint() |
969 | 0 | { |
970 | 0 | MOZ_ASSERT(mTaskQueue->IsCurrentThreadIn()); |
971 | 0 | if (!mTrackDemuxer) { |
972 | 0 | // Detached. |
973 | 0 | return; |
974 | 0 | } |
975 | 0 | MutexAutoLock lock(mMutex); |
976 | 0 | mNextRandomAccessPointResult = |
977 | 0 | mTrackDemuxer->GetNextRandomAccessPoint(&mNextRandomAccessPoint); |
978 | 0 | } |
979 | | |
980 | | void UpdateBuffered() |
981 | 0 | { |
982 | 0 | MOZ_ASSERT(mTaskQueue->IsCurrentThreadIn()); |
983 | 0 | if (!mTrackDemuxer) { |
984 | 0 | // Detached. |
985 | 0 | return; |
986 | 0 | } |
987 | 0 | MutexAutoLock lock(mMutex); |
988 | 0 | mBuffered = mTrackDemuxer->GetBuffered(); |
989 | 0 | } |
990 | | }; |
991 | | |
992 | | RefPtr<MediaDataDemuxer::InitPromise> |
993 | | MediaFormatReader::DemuxerProxy::Init() |
994 | 0 | { |
995 | 0 | using InitPromise = MediaDataDemuxer::InitPromise; |
996 | 0 |
|
997 | 0 | RefPtr<Data> data = mData; |
998 | 0 | RefPtr<TaskQueue> taskQueue = mTaskQueue; |
999 | 0 | return InvokeAsync(mTaskQueue, __func__, |
1000 | 0 | [data, taskQueue]() { |
1001 | 0 | if (!data->mDemuxer) { |
1002 | 0 | return InitPromise::CreateAndReject( |
1003 | 0 | NS_ERROR_DOM_MEDIA_CANCELED, __func__); |
1004 | 0 | } |
1005 | 0 | return data->mDemuxer->Init(); |
1006 | 0 | }) |
1007 | 0 | ->Then(taskQueue, __func__, |
1008 | 0 | [data, taskQueue]() { |
1009 | 0 | if (!data->mDemuxer) { // Was shutdown. |
1010 | 0 | return InitPromise::CreateAndReject( |
1011 | 0 | NS_ERROR_DOM_MEDIA_CANCELED, __func__); |
1012 | 0 | } |
1013 | 0 | data->mNumAudioTrack = |
1014 | 0 | data->mDemuxer->GetNumberTracks(TrackInfo::kAudioTrack); |
1015 | 0 | if (data->mNumAudioTrack) { |
1016 | 0 | RefPtr<MediaTrackDemuxer> d = |
1017 | 0 | data->mDemuxer->GetTrackDemuxer(TrackInfo::kAudioTrack, 0); |
1018 | 0 | if (d) { |
1019 | 0 | RefPtr<Wrapper> wrapper = |
1020 | 0 | new DemuxerProxy::Wrapper(d, taskQueue); |
1021 | 0 | wrapper->UpdateBuffered(); |
1022 | 0 | data->mAudioDemuxer = wrapper; |
1023 | 0 | DecoderDoctorLogger::LinkParentAndChild( |
1024 | 0 | data->mDemuxer.get(), |
1025 | 0 | "decoder factory wrapper", |
1026 | 0 | "MediaFormatReader::DecoderFactory::Wrapper", |
1027 | 0 | wrapper.get()); |
1028 | 0 | } |
1029 | 0 | } |
1030 | 0 | data->mNumVideoTrack = |
1031 | 0 | data->mDemuxer->GetNumberTracks(TrackInfo::kVideoTrack); |
1032 | 0 | if (data->mNumVideoTrack) { |
1033 | 0 | RefPtr<MediaTrackDemuxer> d = |
1034 | 0 | data->mDemuxer->GetTrackDemuxer(TrackInfo::kVideoTrack, 0); |
1035 | 0 | if (d) { |
1036 | 0 | RefPtr<Wrapper> wrapper = |
1037 | 0 | new DemuxerProxy::Wrapper(d, taskQueue); |
1038 | 0 | wrapper->UpdateBuffered(); |
1039 | 0 | data->mVideoDemuxer = wrapper; |
1040 | 0 | DecoderDoctorLogger::LinkParentAndChild( |
1041 | 0 | data->mDemuxer.get(), |
1042 | 0 | "decoder factory wrapper", |
1043 | 0 | "MediaFormatReader::DecoderFactory::Wrapper", |
1044 | 0 | wrapper.get()); |
1045 | 0 | } |
1046 | 0 | } |
1047 | 0 | data->mCrypto = data->mDemuxer->GetCrypto(); |
1048 | 0 | data->mSeekable = data->mDemuxer->IsSeekable(); |
1049 | 0 | data->mSeekableOnlyInBufferedRange = |
1050 | 0 | data->mDemuxer->IsSeekableOnlyInBufferedRanges(); |
1051 | 0 | data->mShouldComputeStartTime = |
1052 | 0 | data->mDemuxer->ShouldComputeStartTime(); |
1053 | 0 | data->mInitDone = true; |
1054 | 0 | return InitPromise::CreateAndResolve(NS_OK, __func__); |
1055 | 0 | }, |
1056 | 0 | [](const MediaResult& aError) { |
1057 | 0 | return InitPromise::CreateAndReject(aError, __func__); |
1058 | 0 | }); |
1059 | 0 | } |
1060 | | |
1061 | | RefPtr<MediaFormatReader::NotifyDataArrivedPromise> |
1062 | | MediaFormatReader::DemuxerProxy::NotifyDataArrived() |
1063 | 0 | { |
1064 | 0 | RefPtr<Data> data = mData; |
1065 | 0 | return InvokeAsync(mTaskQueue, __func__, [data]() { |
1066 | 0 | if (!data->mDemuxer) { |
1067 | 0 | // Was shutdown. |
1068 | 0 | return NotifyDataArrivedPromise::CreateAndReject( |
1069 | 0 | NS_ERROR_DOM_MEDIA_CANCELED, __func__); |
1070 | 0 | } |
1071 | 0 | data->mDemuxer->NotifyDataArrived(); |
1072 | 0 | if (data->mAudioDemuxer) { |
1073 | 0 | data->mAudioDemuxer->UpdateBuffered(); |
1074 | 0 | } |
1075 | 0 | if (data->mVideoDemuxer) { |
1076 | 0 | data->mVideoDemuxer->UpdateBuffered(); |
1077 | 0 | } |
1078 | 0 | return NotifyDataArrivedPromise::CreateAndResolve(true, __func__); |
1079 | 0 | }); |
1080 | 0 | } |
1081 | | |
1082 | | MediaFormatReader::MediaFormatReader(MediaFormatReaderInit& aInit, |
1083 | | MediaDataDemuxer* aDemuxer) |
1084 | | : mTaskQueue(new TaskQueue(GetMediaThreadPool(MediaThreadType::PLAYBACK), |
1085 | | "MediaFormatReader::mTaskQueue", |
1086 | | /* aSupportsTailDispatch = */ true)) |
1087 | | , mAudio(this, MediaData::AUDIO_DATA, |
1088 | | StaticPrefs::MediaAudioMaxDecodeError()) |
1089 | | , mVideo(this, MediaData::VIDEO_DATA, |
1090 | | StaticPrefs::MediaVideoMaxDecodeError()) |
1091 | | , mDemuxer(new DemuxerProxy(aDemuxer)) |
1092 | | , mDemuxerInitDone(false) |
1093 | | , mPendingNotifyDataArrived(false) |
1094 | | , mLastReportedNumDecodedFrames(0) |
1095 | | , mPreviousDecodedKeyframeTime_us(sNoPreviousDecodedKeyframe) |
1096 | | , mKnowsCompositor(aInit.mKnowsCompositor) |
1097 | | , mInitDone(false) |
1098 | | , mTrackDemuxersMayBlock(false) |
1099 | | , mSeekScheduled(false) |
1100 | | , mVideoFrameContainer(aInit.mVideoFrameContainer) |
1101 | | , mCrashHelper(aInit.mCrashHelper) |
1102 | | , mDecoderFactory(new DecoderFactory(this)) |
1103 | | , mShutdownPromisePool(new ShutdownPromisePool()) |
1104 | | , mBuffered(mTaskQueue, |
1105 | | TimeIntervals(), |
1106 | | "MediaFormatReader::mBuffered (Canonical)") |
1107 | | , mFrameStats(aInit.mFrameStats) |
1108 | | , mMediaDecoderOwnerID(aInit.mMediaDecoderOwnerID) |
1109 | 0 | { |
1110 | 0 | MOZ_ASSERT(aDemuxer); |
1111 | 0 | MOZ_COUNT_CTOR(MediaFormatReader); |
1112 | 0 | DDLINKCHILD( |
1113 | 0 | "audio decoder data", "MediaFormatReader::DecoderDataWithPromise", &mAudio); |
1114 | 0 | DDLINKCHILD( |
1115 | 0 | "video decoder data", "MediaFormatReader::DecoderDataWithPromise", &mVideo); |
1116 | 0 | DDLINKCHILD("demuxer", aDemuxer); |
1117 | 0 | mOnTrackWaitingForKeyListener = OnTrackWaitingForKey().Connect( |
1118 | 0 | mTaskQueue, this, &MediaFormatReader::NotifyWaitingForKey); |
1119 | 0 | } |
1120 | | |
1121 | | MediaFormatReader::~MediaFormatReader() |
1122 | 0 | { |
1123 | 0 | MOZ_COUNT_DTOR(MediaFormatReader); |
1124 | 0 | MOZ_ASSERT(mShutdown); |
1125 | 0 | } |
1126 | | |
1127 | | RefPtr<ShutdownPromise> |
1128 | | MediaFormatReader::Shutdown() |
1129 | 0 | { |
1130 | 0 | MOZ_ASSERT(OnTaskQueue()); |
1131 | 0 | LOG(""); |
1132 | 0 |
|
1133 | 0 | mDemuxerInitRequest.DisconnectIfExists(); |
1134 | 0 | mNotifyDataArrivedPromise.DisconnectIfExists(); |
1135 | 0 | mMetadataPromise.RejectIfExists(NS_ERROR_DOM_MEDIA_CANCELED, __func__); |
1136 | 0 | mSeekPromise.RejectIfExists(NS_ERROR_DOM_MEDIA_CANCELED, __func__); |
1137 | 0 | mSkipRequest.DisconnectIfExists(); |
1138 | 0 | mSetCDMPromise.RejectIfExists( |
1139 | 0 | MediaResult(NS_ERROR_DOM_INVALID_STATE_ERR, |
1140 | 0 | "MediaFormatReader is shutting down"), |
1141 | 0 | __func__); |
1142 | 0 |
|
1143 | 0 | if (mAudio.HasPromise()) { |
1144 | 0 | mAudio.RejectPromise(NS_ERROR_DOM_MEDIA_CANCELED, __func__); |
1145 | 0 | } |
1146 | 0 | if (mVideo.HasPromise()) { |
1147 | 0 | mVideo.RejectPromise(NS_ERROR_DOM_MEDIA_CANCELED, __func__); |
1148 | 0 | } |
1149 | 0 |
|
1150 | 0 | if (HasAudio()) { |
1151 | 0 | mAudio.ResetDemuxer(); |
1152 | 0 | mAudio.mTrackDemuxer->BreakCycles(); |
1153 | 0 | mAudio.mTrackDemuxer = nullptr; |
1154 | 0 | mAudio.ResetState(); |
1155 | 0 | ShutdownDecoder(TrackInfo::kAudioTrack); |
1156 | 0 | } |
1157 | 0 |
|
1158 | 0 | if (HasVideo()) { |
1159 | 0 | mVideo.ResetDemuxer(); |
1160 | 0 | mVideo.mTrackDemuxer->BreakCycles(); |
1161 | 0 | mVideo.mTrackDemuxer = nullptr; |
1162 | 0 | mVideo.ResetState(); |
1163 | 0 | ShutdownDecoder(TrackInfo::kVideoTrack); |
1164 | 0 | } |
1165 | 0 |
|
1166 | 0 | mShutdownPromisePool->Track(mDemuxer->Shutdown()); |
1167 | 0 | mDemuxer = nullptr; |
1168 | 0 |
|
1169 | 0 | mOnTrackWaitingForKeyListener.Disconnect(); |
1170 | 0 |
|
1171 | 0 | mShutdown = true; |
1172 | 0 | return mShutdownPromisePool->Shutdown() |
1173 | 0 | ->Then(OwnerThread(), __func__, this, |
1174 | 0 | &MediaFormatReader::TearDownDecoders, |
1175 | 0 | &MediaFormatReader::TearDownDecoders); |
1176 | 0 | } |
1177 | | |
1178 | | void |
1179 | | MediaFormatReader::ShutdownDecoder(TrackType aTrack) |
1180 | 0 | { |
1181 | 0 | LOGV("%s", TrackTypeToStr(aTrack)); |
1182 | 0 |
|
1183 | 0 | // Shut down the pending decoder if any. |
1184 | 0 | mDecoderFactory->ShutdownDecoder(aTrack); |
1185 | 0 |
|
1186 | 0 | auto& decoder = GetDecoderData(aTrack); |
1187 | 0 | // Flush the decoder if necessary. |
1188 | 0 | decoder.Flush(); |
1189 | 0 | // Shut down the decoder if any. |
1190 | 0 | decoder.ShutdownDecoder(); |
1191 | 0 | } |
1192 | | |
1193 | | RefPtr<ShutdownPromise> |
1194 | | MediaFormatReader::TearDownDecoders() |
1195 | 0 | { |
1196 | 0 | if (mAudio.mTaskQueue) { |
1197 | 0 | mAudio.mTaskQueue->BeginShutdown(); |
1198 | 0 | mAudio.mTaskQueue->AwaitShutdownAndIdle(); |
1199 | 0 | mAudio.mTaskQueue = nullptr; |
1200 | 0 | } |
1201 | 0 | if (mVideo.mTaskQueue) { |
1202 | 0 | mVideo.mTaskQueue->BeginShutdown(); |
1203 | 0 | mVideo.mTaskQueue->AwaitShutdownAndIdle(); |
1204 | 0 | mVideo.mTaskQueue = nullptr; |
1205 | 0 | } |
1206 | 0 |
|
1207 | 0 | mDecoderFactory = nullptr; |
1208 | 0 | mPlatform = nullptr; |
1209 | 0 | mEncryptedPlatform = nullptr; |
1210 | 0 | mVideoFrameContainer = nullptr; |
1211 | 0 |
|
1212 | 0 | ReleaseResources(); |
1213 | 0 | mBuffered.DisconnectAll(); |
1214 | 0 | return mTaskQueue->BeginShutdown(); |
1215 | 0 | } |
1216 | | |
1217 | | nsresult |
1218 | | MediaFormatReader::Init() |
1219 | 0 | { |
1220 | 0 | MOZ_ASSERT(NS_IsMainThread(), "Must be on main thread."); |
1221 | 0 |
|
1222 | 0 | mAudio.mTaskQueue = new TaskQueue( |
1223 | 0 | GetMediaThreadPool(MediaThreadType::PLATFORM_DECODER), |
1224 | 0 | "MFR::mAudio::mTaskQueue"); |
1225 | 0 |
|
1226 | 0 | mVideo.mTaskQueue = new TaskQueue( |
1227 | 0 | GetMediaThreadPool(MediaThreadType::PLATFORM_DECODER), |
1228 | 0 | "MFR::mVideo::mTaskQueue"); |
1229 | 0 |
|
1230 | 0 | return NS_OK; |
1231 | 0 | } |
1232 | | |
1233 | | bool |
1234 | | MediaFormatReader::ResolveSetCDMPromiseIfDone(TrackType aTrack) |
1235 | 0 | { |
1236 | 0 | // When a CDM proxy is set, MFR would shutdown the existing MediaDataDecoder |
1237 | 0 | // and would create new one for specific track in the next Update. |
1238 | 0 | MOZ_ASSERT(OnTaskQueue()); |
1239 | 0 |
|
1240 | 0 | if (mSetCDMPromise.IsEmpty()) { |
1241 | 0 | return true; |
1242 | 0 | } |
1243 | 0 | |
1244 | 0 | MOZ_ASSERT(mCDMProxy); |
1245 | 0 | if (mSetCDMForTracks.contains(aTrack)) { |
1246 | 0 | mSetCDMForTracks -= aTrack; |
1247 | 0 | } |
1248 | 0 |
|
1249 | 0 | if (mSetCDMForTracks.isEmpty()) { |
1250 | 0 | LOGV("%s : Done ", __func__); |
1251 | 0 | mSetCDMPromise.Resolve(/* aIgnored = */ true, __func__); |
1252 | 0 | if (HasAudio()) { |
1253 | 0 | ScheduleUpdate(TrackInfo::kAudioTrack); |
1254 | 0 | } |
1255 | 0 | if (HasVideo()) { |
1256 | 0 | ScheduleUpdate(TrackInfo::kVideoTrack); |
1257 | 0 | } |
1258 | 0 | return true; |
1259 | 0 | } |
1260 | 0 | LOGV("%s : %s track is ready.", __func__, TrackTypeToStr(aTrack)); |
1261 | 0 | return false; |
1262 | 0 | } |
1263 | | |
1264 | | void |
1265 | | MediaFormatReader::PrepareToSetCDMForTrack(TrackType aTrack) |
1266 | 0 | { |
1267 | 0 | MOZ_ASSERT(OnTaskQueue()); |
1268 | 0 | LOGV("%s : %s", __func__, TrackTypeToStr(aTrack)); |
1269 | 0 |
|
1270 | 0 | mSetCDMForTracks += aTrack; |
1271 | 0 | if (mCDMProxy) { |
1272 | 0 | // An old cdm proxy exists, so detaching old cdm proxy by shutting down |
1273 | 0 | // MediaDataDecoder. |
1274 | 0 | ShutdownDecoder(aTrack); |
1275 | 0 | } |
1276 | 0 | ScheduleUpdate(aTrack); |
1277 | 0 | } |
1278 | | |
1279 | | bool |
1280 | | MediaFormatReader::IsDecoderWaitingForCDM(TrackType aTrack) |
1281 | 0 | { |
1282 | 0 | MOZ_ASSERT(OnTaskQueue()); |
1283 | 0 | return GetDecoderData(aTrack).IsEncrypted() && |
1284 | 0 | mSetCDMForTracks.contains(aTrack) && !mCDMProxy; |
1285 | 0 | } |
1286 | | |
1287 | | RefPtr<SetCDMPromise> |
1288 | | MediaFormatReader::SetCDMProxy(CDMProxy* aProxy) |
1289 | 0 | { |
1290 | 0 | MOZ_ASSERT(OnTaskQueue()); |
1291 | 0 | LOGV("SetCDMProxy (%p)", aProxy); |
1292 | 0 |
|
1293 | 0 | if (mShutdown) { |
1294 | 0 | return SetCDMPromise::CreateAndReject( |
1295 | 0 | MediaResult(NS_ERROR_DOM_INVALID_STATE_ERR, |
1296 | 0 | "MediaFormatReader is shutting down"), |
1297 | 0 | __func__); |
1298 | 0 | } |
1299 | 0 | |
1300 | 0 | mSetCDMPromise.RejectIfExists( |
1301 | 0 | MediaResult(NS_ERROR_DOM_INVALID_STATE_ERR, |
1302 | 0 | "Another new CDM proxy is being set."), |
1303 | 0 | __func__); |
1304 | 0 |
|
1305 | 0 | // Shutdown all decoders as switching CDM proxy indicates that it's |
1306 | 0 | // inappropriate for the existing decoders to continue decoding via the old |
1307 | 0 | // CDM proxy. |
1308 | 0 | if (HasAudio()) { |
1309 | 0 | PrepareToSetCDMForTrack(TrackInfo::kAudioTrack); |
1310 | 0 | } |
1311 | 0 | if (HasVideo()) { |
1312 | 0 | PrepareToSetCDMForTrack(TrackInfo::kVideoTrack); |
1313 | 0 | } |
1314 | 0 |
|
1315 | 0 | mCDMProxy = aProxy; |
1316 | 0 |
|
1317 | 0 | // Release old PDMFactory which contains an EMEDecoderModule. |
1318 | 0 | mEncryptedPlatform = nullptr; |
1319 | 0 |
|
1320 | 0 | if (!mInitDone || mSetCDMForTracks.isEmpty() || !mCDMProxy) { |
1321 | 0 | // 1) MFR is not initialized yet or |
1322 | 0 | // 2) Demuxer is initialized without active audio and video or |
1323 | 0 | // 3) A null cdm proxy is set |
1324 | 0 | // the promise can be resolved directly. |
1325 | 0 | mSetCDMForTracks.clear(); |
1326 | 0 | return SetCDMPromise::CreateAndResolve(/* aIgnored = */ true, __func__); |
1327 | 0 | } |
1328 | 0 | |
1329 | 0 | RefPtr<SetCDMPromise> p = mSetCDMPromise.Ensure(__func__); |
1330 | 0 | return p; |
1331 | 0 | } |
1332 | | |
1333 | | bool |
1334 | | MediaFormatReader::IsWaitingOnCDMResource() |
1335 | 0 | { |
1336 | 0 | MOZ_ASSERT(OnTaskQueue()); |
1337 | 0 | return IsEncrypted() && !mCDMProxy; |
1338 | 0 | } |
1339 | | |
1340 | | RefPtr<MediaFormatReader::MetadataPromise> |
1341 | | MediaFormatReader::AsyncReadMetadata() |
1342 | 0 | { |
1343 | 0 | MOZ_ASSERT(OnTaskQueue()); |
1344 | 0 |
|
1345 | 0 | MOZ_DIAGNOSTIC_ASSERT(mMetadataPromise.IsEmpty()); |
1346 | 0 |
|
1347 | 0 | if (mInitDone) { |
1348 | 0 | // We are returning from dormant. |
1349 | 0 | MetadataHolder metadata; |
1350 | 0 | metadata.mInfo = MakeUnique<MediaInfo>(mInfo); |
1351 | 0 | return MetadataPromise::CreateAndResolve(std::move(metadata), __func__); |
1352 | 0 | } |
1353 | 0 | |
1354 | 0 | RefPtr<MetadataPromise> p = mMetadataPromise.Ensure(__func__); |
1355 | 0 |
|
1356 | 0 | mDemuxer->Init() |
1357 | 0 | ->Then(OwnerThread(), __func__, this, |
1358 | 0 | &MediaFormatReader::OnDemuxerInitDone, |
1359 | 0 | &MediaFormatReader::OnDemuxerInitFailed) |
1360 | 0 | ->Track(mDemuxerInitRequest); |
1361 | 0 | return p; |
1362 | 0 | } |
1363 | | |
1364 | | void |
1365 | | MediaFormatReader::OnDemuxerInitDone(const MediaResult& aResult) |
1366 | 0 | { |
1367 | 0 | MOZ_ASSERT(OnTaskQueue()); |
1368 | 0 | mDemuxerInitRequest.Complete(); |
1369 | 0 |
|
1370 | 0 | if (NS_FAILED(aResult) && StaticPrefs::MediaPlaybackWarningsAsErrors()) { |
1371 | 0 | mMetadataPromise.Reject(aResult, __func__); |
1372 | 0 | return; |
1373 | 0 | } |
1374 | 0 | |
1375 | 0 | mDemuxerInitDone = true; |
1376 | 0 |
|
1377 | 0 | UniquePtr<MetadataTags> tags(MakeUnique<MetadataTags>()); |
1378 | 0 |
|
1379 | 0 | RefPtr<PDMFactory> platform; |
1380 | 0 | if (!IsWaitingOnCDMResource()) { |
1381 | 0 | platform = new PDMFactory(); |
1382 | 0 | } |
1383 | 0 |
|
1384 | 0 | // To decode, we need valid video and a place to put it. |
1385 | 0 | bool videoActive = |
1386 | 0 | !!mDemuxer->GetNumberTracks(TrackInfo::kVideoTrack) && GetImageContainer(); |
1387 | 0 |
|
1388 | 0 | if (videoActive) { |
1389 | 0 | // We currently only handle the first video track. |
1390 | 0 | mVideo.mTrackDemuxer = mDemuxer->GetTrackDemuxer(TrackInfo::kVideoTrack, 0); |
1391 | 0 | if (!mVideo.mTrackDemuxer) { |
1392 | 0 | mMetadataPromise.Reject(NS_ERROR_DOM_MEDIA_METADATA_ERR, __func__); |
1393 | 0 | return; |
1394 | 0 | } |
1395 | 0 | |
1396 | 0 | UniquePtr<TrackInfo> videoInfo = mVideo.mTrackDemuxer->GetInfo(); |
1397 | 0 | videoActive = videoInfo && videoInfo->IsValid(); |
1398 | 0 | if (videoActive) { |
1399 | 0 | if (platform && |
1400 | 0 | !platform->SupportsMimeType(videoInfo->mMimeType, nullptr)) { |
1401 | 0 | // We have no decoder for this track. Error. |
1402 | 0 | mMetadataPromise.Reject(NS_ERROR_DOM_MEDIA_METADATA_ERR, __func__); |
1403 | 0 | return; |
1404 | 0 | } |
1405 | 0 | { |
1406 | 0 | MutexAutoLock lock(mVideo.mMutex); |
1407 | 0 | mInfo.mVideo = *videoInfo->GetAsVideoInfo(); |
1408 | 0 | } |
1409 | 0 | for (const MetadataTag& tag : videoInfo->mTags) { |
1410 | 0 | tags->Put(tag.mKey, tag.mValue); |
1411 | 0 | } |
1412 | 0 | mVideo.mOriginalInfo = std::move(videoInfo); |
1413 | 0 | mTrackDemuxersMayBlock |= mVideo.mTrackDemuxer->GetSamplesMayBlock(); |
1414 | 0 | } else { |
1415 | 0 | mVideo.mTrackDemuxer->BreakCycles(); |
1416 | 0 | mVideo.mTrackDemuxer = nullptr; |
1417 | 0 | } |
1418 | 0 | } |
1419 | 0 |
|
1420 | 0 | bool audioActive = !!mDemuxer->GetNumberTracks(TrackInfo::kAudioTrack); |
1421 | 0 | if (audioActive) { |
1422 | 0 | mAudio.mTrackDemuxer = mDemuxer->GetTrackDemuxer(TrackInfo::kAudioTrack, 0); |
1423 | 0 | if (!mAudio.mTrackDemuxer) { |
1424 | 0 | mMetadataPromise.Reject(NS_ERROR_DOM_MEDIA_METADATA_ERR, __func__); |
1425 | 0 | return; |
1426 | 0 | } |
1427 | 0 | |
1428 | 0 | UniquePtr<TrackInfo> audioInfo = mAudio.mTrackDemuxer->GetInfo(); |
1429 | 0 | // We actively ignore audio tracks that we know we can't play. |
1430 | 0 | audioActive = |
1431 | 0 | audioInfo && audioInfo->IsValid() && |
1432 | 0 | (!platform || platform->SupportsMimeType(audioInfo->mMimeType, nullptr)); |
1433 | 0 |
|
1434 | 0 | if (audioActive) { |
1435 | 0 | { |
1436 | 0 | MutexAutoLock lock(mAudio.mMutex); |
1437 | 0 | mInfo.mAudio = *audioInfo->GetAsAudioInfo(); |
1438 | 0 | } |
1439 | 0 | for (const MetadataTag& tag : audioInfo->mTags) { |
1440 | 0 | tags->Put(tag.mKey, tag.mValue); |
1441 | 0 | } |
1442 | 0 | mAudio.mOriginalInfo = std::move(audioInfo); |
1443 | 0 | mTrackDemuxersMayBlock |= mAudio.mTrackDemuxer->GetSamplesMayBlock(); |
1444 | 0 | } else { |
1445 | 0 | mAudio.mTrackDemuxer->BreakCycles(); |
1446 | 0 | mAudio.mTrackDemuxer = nullptr; |
1447 | 0 | } |
1448 | 0 | } |
1449 | 0 |
|
1450 | 0 | UniquePtr<EncryptionInfo> crypto = mDemuxer->GetCrypto(); |
1451 | 0 | if (crypto && crypto->IsEncrypted()) { |
1452 | 0 | // Try and dispatch 'encrypted'. Won't go if ready state still HAVE_NOTHING. |
1453 | 0 | for (uint32_t i = 0; i < crypto->mInitDatas.Length(); i++) { |
1454 | 0 | mOnEncrypted.Notify(crypto->mInitDatas[i].mInitData, |
1455 | 0 | crypto->mInitDatas[i].mType); |
1456 | 0 | } |
1457 | 0 | mInfo.mCrypto = *crypto; |
1458 | 0 | } |
1459 | 0 |
|
1460 | 0 | auto videoDuration = HasVideo() ? mInfo.mVideo.mDuration : TimeUnit::Zero(); |
1461 | 0 | auto audioDuration = HasAudio() ? mInfo.mAudio.mDuration : TimeUnit::Zero(); |
1462 | 0 |
|
1463 | 0 | auto duration = std::max(videoDuration, audioDuration); |
1464 | 0 | if (duration.IsPositive()) { |
1465 | 0 | mInfo.mMetadataDuration = Some(duration); |
1466 | 0 | } |
1467 | 0 |
|
1468 | 0 | mInfo.mMediaSeekable = mDemuxer->IsSeekable(); |
1469 | 0 | mInfo.mMediaSeekableOnlyInBufferedRanges = |
1470 | 0 | mDemuxer->IsSeekableOnlyInBufferedRanges(); |
1471 | 0 |
|
1472 | 0 | if (!videoActive && !audioActive) { |
1473 | 0 | mMetadataPromise.Reject(NS_ERROR_DOM_MEDIA_METADATA_ERR, __func__); |
1474 | 0 | return; |
1475 | 0 | } |
1476 | 0 | |
1477 | 0 | mTags = std::move(tags); |
1478 | 0 | mInitDone = true; |
1479 | 0 |
|
1480 | 0 | // Try to get the start time. |
1481 | 0 | // For MSE case, the start time of each track is assumed to be 0. |
1482 | 0 | // For others, we must demux the first sample to know the start time for each |
1483 | 0 | // track. |
1484 | 0 | if (!mDemuxer->ShouldComputeStartTime()) { |
1485 | 0 | mAudio.mFirstDemuxedSampleTime.emplace(TimeUnit::Zero()); |
1486 | 0 | mVideo.mFirstDemuxedSampleTime.emplace(TimeUnit::Zero()); |
1487 | 0 | } else { |
1488 | 0 | if (HasAudio()) { |
1489 | 0 | RequestDemuxSamples(TrackInfo::kAudioTrack); |
1490 | 0 | } |
1491 | 0 |
|
1492 | 0 | if (HasVideo()) { |
1493 | 0 | RequestDemuxSamples(TrackInfo::kVideoTrack); |
1494 | 0 | } |
1495 | 0 | } |
1496 | 0 |
|
1497 | 0 | if (aResult != NS_OK) { |
1498 | 0 | mOnDecodeWarning.Notify(aResult); |
1499 | 0 | } |
1500 | 0 |
|
1501 | 0 | MaybeResolveMetadataPromise(); |
1502 | 0 | } |
1503 | | |
1504 | | void |
1505 | | MediaFormatReader::MaybeResolveMetadataPromise() |
1506 | 0 | { |
1507 | 0 | MOZ_ASSERT(OnTaskQueue()); |
1508 | 0 |
|
1509 | 0 | if ((HasAudio() && mAudio.mFirstDemuxedSampleTime.isNothing()) || |
1510 | 0 | (HasVideo() && mVideo.mFirstDemuxedSampleTime.isNothing())) { |
1511 | 0 | return; |
1512 | 0 | } |
1513 | 0 | |
1514 | 0 | TimeUnit startTime = |
1515 | 0 | std::min(mAudio.mFirstDemuxedSampleTime.refOr(TimeUnit::FromInfinity()), |
1516 | 0 | mVideo.mFirstDemuxedSampleTime.refOr(TimeUnit::FromInfinity())); |
1517 | 0 |
|
1518 | 0 | if (!startTime.IsInfinite()) { |
1519 | 0 | mInfo.mStartTime = startTime; // mInfo.mStartTime is initialized to 0. |
1520 | 0 | } |
1521 | 0 |
|
1522 | 0 | MetadataHolder metadata; |
1523 | 0 | metadata.mInfo = MakeUnique<MediaInfo>(mInfo); |
1524 | 0 | metadata.mTags = mTags->Count() ? std::move(mTags) : nullptr; |
1525 | 0 |
|
1526 | 0 | // We now have all the informations required to calculate the initial buffered |
1527 | 0 | // range. |
1528 | 0 | mHasStartTime = true; |
1529 | 0 | UpdateBuffered(); |
1530 | 0 |
|
1531 | 0 | mMetadataPromise.Resolve(std::move(metadata), __func__); |
1532 | 0 | } |
1533 | | |
1534 | | bool |
1535 | | MediaFormatReader::IsEncrypted() const |
1536 | 0 | { |
1537 | 0 | return (HasAudio() && mAudio.GetCurrentInfo()->mCrypto.mValid) || |
1538 | 0 | (HasVideo() && mVideo.GetCurrentInfo()->mCrypto.mValid); |
1539 | 0 | } |
1540 | | |
1541 | | void |
1542 | | MediaFormatReader::OnDemuxerInitFailed(const MediaResult& aError) |
1543 | 0 | { |
1544 | 0 | mDemuxerInitRequest.Complete(); |
1545 | 0 | mMetadataPromise.Reject(aError, __func__); |
1546 | 0 | } |
1547 | | |
1548 | | void |
1549 | | MediaFormatReader::ReadUpdatedMetadata(MediaInfo* aInfo) |
1550 | 0 | { |
1551 | 0 | *aInfo = mInfo; |
1552 | 0 | } |
1553 | | |
1554 | | MediaFormatReader::DecoderData& |
1555 | | MediaFormatReader::GetDecoderData(TrackType aTrack) |
1556 | 0 | { |
1557 | 0 | MOZ_ASSERT(aTrack == TrackInfo::kAudioTrack || |
1558 | 0 | aTrack == TrackInfo::kVideoTrack); |
1559 | 0 | if (aTrack == TrackInfo::kAudioTrack) { |
1560 | 0 | return mAudio; |
1561 | 0 | } |
1562 | 0 | return mVideo; |
1563 | 0 | } |
1564 | | |
1565 | | bool |
1566 | | MediaFormatReader::ShouldSkip(TimeUnit aTimeThreshold) |
1567 | 0 | { |
1568 | 0 | MOZ_ASSERT(HasVideo()); |
1569 | 0 |
|
1570 | 0 | if (!StaticPrefs::MediaDecoderSkipToNextKeyFrameEnabled()) { |
1571 | 0 | return false; |
1572 | 0 | } |
1573 | 0 | |
1574 | 0 | TimeUnit nextKeyframe; |
1575 | 0 | nsresult rv = mVideo.mTrackDemuxer->GetNextRandomAccessPoint(&nextKeyframe); |
1576 | 0 | if (NS_FAILED(rv)) { |
1577 | 0 | // Only OggTrackDemuxer with video type gets into here. |
1578 | 0 | // We don't support skip-to-next-frame for this case. |
1579 | 0 | return false; |
1580 | 0 | } |
1581 | 0 | return (nextKeyframe <= aTimeThreshold || |
1582 | 0 | (mVideo.mTimeThreshold && |
1583 | 0 | mVideo.mTimeThreshold.ref().EndTime() < aTimeThreshold)) && |
1584 | 0 | nextKeyframe.ToMicroseconds() >= 0 && !nextKeyframe.IsInfinite(); |
1585 | 0 | } |
1586 | | |
1587 | | RefPtr<MediaFormatReader::VideoDataPromise> |
1588 | | MediaFormatReader::RequestVideoData(const TimeUnit& aTimeThreshold) |
1589 | 0 | { |
1590 | 0 | MOZ_ASSERT(OnTaskQueue()); |
1591 | 0 | MOZ_DIAGNOSTIC_ASSERT(mSeekPromise.IsEmpty(), |
1592 | 0 | "No sample requests allowed while seeking"); |
1593 | 0 | MOZ_DIAGNOSTIC_ASSERT(!mVideo.HasPromise(), "No duplicate sample requests"); |
1594 | 0 | MOZ_DIAGNOSTIC_ASSERT(!mVideo.mSeekRequest.Exists() || |
1595 | 0 | mVideo.mTimeThreshold.isSome()); |
1596 | 0 | MOZ_DIAGNOSTIC_ASSERT(!IsSeeking(), "called mid-seek"); |
1597 | 0 | LOGV("RequestVideoData(%" PRId64 ")", aTimeThreshold.ToMicroseconds()); |
1598 | 0 |
|
1599 | 0 | if (!HasVideo()) { |
1600 | 0 | LOG("called with no video track"); |
1601 | 0 | return VideoDataPromise::CreateAndReject(NS_ERROR_DOM_MEDIA_FATAL_ERR, |
1602 | 0 | __func__); |
1603 | 0 | } |
1604 | 0 |
|
1605 | 0 | if (IsSeeking()) { |
1606 | 0 | LOG("called mid-seek. Rejecting."); |
1607 | 0 | return VideoDataPromise::CreateAndReject(NS_ERROR_DOM_MEDIA_CANCELED, |
1608 | 0 | __func__); |
1609 | 0 | } |
1610 | 0 |
|
1611 | 0 | if (mShutdown) { |
1612 | 0 | NS_WARNING("RequestVideoData on shutdown MediaFormatReader!"); |
1613 | 0 | return VideoDataPromise::CreateAndReject(NS_ERROR_DOM_MEDIA_CANCELED, |
1614 | 0 | __func__); |
1615 | 0 | } |
1616 | 0 |
|
1617 | 0 | // Ensure we have no pending seek going as ShouldSkip could return out of date |
1618 | 0 | // information. |
1619 | 0 | if (!mVideo.HasInternalSeekPending() && ShouldSkip(aTimeThreshold)) { |
1620 | 0 | RefPtr<VideoDataPromise> p = mVideo.EnsurePromise(__func__); |
1621 | 0 | SkipVideoDemuxToNextKeyFrame(aTimeThreshold); |
1622 | 0 | return p; |
1623 | 0 | } |
1624 | 0 | |
1625 | 0 | RefPtr<VideoDataPromise> p = mVideo.EnsurePromise(__func__); |
1626 | 0 | ScheduleUpdate(TrackInfo::kVideoTrack); |
1627 | 0 |
|
1628 | 0 | return p; |
1629 | 0 | } |
1630 | | |
1631 | | void |
1632 | | MediaFormatReader::OnDemuxFailed(TrackType aTrack, const MediaResult& aError) |
1633 | 0 | { |
1634 | 0 | MOZ_ASSERT(OnTaskQueue()); |
1635 | 0 | LOG("Failed to demux %s, failure:%s", |
1636 | 0 | aTrack == TrackType::kVideoTrack ? "video" : "audio", |
1637 | 0 | aError.ErrorName().get()); |
1638 | 0 | auto& decoder = GetDecoderData(aTrack); |
1639 | 0 | decoder.mDemuxRequest.Complete(); |
1640 | 0 | switch (aError.Code()) { |
1641 | 0 | case NS_ERROR_DOM_MEDIA_END_OF_STREAM: |
1642 | 0 | DDLOG(DDLogCategory::Log, |
1643 | 0 | aTrack == TrackType::kVideoTrack ? "video_demux_interruption" |
1644 | 0 | : "audio_demux_interruption", |
1645 | 0 | aError); |
1646 | 0 | if (!decoder.mWaitingForData) { |
1647 | 0 | decoder.RequestDrain(); |
1648 | 0 | } |
1649 | 0 | NotifyEndOfStream(aTrack); |
1650 | 0 | break; |
1651 | 0 | case NS_ERROR_DOM_MEDIA_WAITING_FOR_DATA: |
1652 | 0 | DDLOG(DDLogCategory::Log, |
1653 | 0 | aTrack == TrackType::kVideoTrack ? "video_demux_interruption" |
1654 | 0 | : "audio_demux_interruption", |
1655 | 0 | aError); |
1656 | 0 | if (!decoder.mWaitingForData) { |
1657 | 0 | decoder.RequestDrain(); |
1658 | 0 | } |
1659 | 0 | NotifyWaitingForData(aTrack); |
1660 | 0 | break; |
1661 | 0 | case NS_ERROR_DOM_MEDIA_CANCELED: |
1662 | 0 | DDLOG(DDLogCategory::Log, |
1663 | 0 | aTrack == TrackType::kVideoTrack ? "video_demux_interruption" |
1664 | 0 | : "audio_demux_interruption", |
1665 | 0 | aError); |
1666 | 0 | if (decoder.HasPromise()) { |
1667 | 0 | decoder.RejectPromise(NS_ERROR_DOM_MEDIA_CANCELED, __func__); |
1668 | 0 | } |
1669 | 0 | break; |
1670 | 0 | default: |
1671 | 0 | DDLOG(DDLogCategory::Log, |
1672 | 0 | aTrack == TrackType::kVideoTrack ? "video_demux_error" |
1673 | 0 | : "audio_demux_error", |
1674 | 0 | aError); |
1675 | 0 | NotifyError(aTrack, aError); |
1676 | 0 | break; |
1677 | 0 | } |
1678 | 0 | } |
1679 | | |
1680 | | void |
1681 | | MediaFormatReader::DoDemuxVideo() |
1682 | 0 | { |
1683 | 0 | using SamplesPromise = MediaTrackDemuxer::SamplesPromise; |
1684 | 0 |
|
1685 | 0 | DDLOG(DDLogCategory::Log, "video_demuxing", DDNoValue{}); |
1686 | 0 | auto p = mVideo.mTrackDemuxer->GetSamples(1); |
1687 | 0 |
|
1688 | 0 | if (mVideo.mFirstDemuxedSampleTime.isNothing()) { |
1689 | 0 | RefPtr<MediaFormatReader> self = this; |
1690 | 0 | p = p->Then( |
1691 | 0 | OwnerThread(), |
1692 | 0 | __func__, |
1693 | 0 | [self](RefPtr<MediaTrackDemuxer::SamplesHolder> aSamples) { |
1694 | 0 | DDLOGEX( |
1695 | 0 | self.get(), DDLogCategory::Log, "video_first_demuxed", DDNoValue{}); |
1696 | 0 | self->OnFirstDemuxCompleted(TrackInfo::kVideoTrack, aSamples); |
1697 | 0 | return SamplesPromise::CreateAndResolve(aSamples.forget(), __func__); |
1698 | 0 | }, |
1699 | 0 | [self](const MediaResult& aError) { |
1700 | 0 | DDLOGEX( |
1701 | 0 | self.get(), DDLogCategory::Log, "video_first_demuxing_error", aError); |
1702 | 0 | self->OnFirstDemuxFailed(TrackInfo::kVideoTrack, aError); |
1703 | 0 | return SamplesPromise::CreateAndReject(aError, __func__); |
1704 | 0 | }); |
1705 | 0 | } |
1706 | 0 |
|
1707 | 0 | p->Then(OwnerThread(), __func__, this, |
1708 | 0 | &MediaFormatReader::OnVideoDemuxCompleted, |
1709 | 0 | &MediaFormatReader::OnVideoDemuxFailed) |
1710 | 0 | ->Track(mVideo.mDemuxRequest); |
1711 | 0 | } |
1712 | | |
1713 | | void |
1714 | | MediaFormatReader::OnVideoDemuxCompleted( |
1715 | | RefPtr<MediaTrackDemuxer::SamplesHolder> aSamples) |
1716 | 0 | { |
1717 | 0 | LOGV("%zu video samples demuxed (sid:%d)", |
1718 | 0 | aSamples->mSamples.Length(), |
1719 | 0 | aSamples->mSamples[0]->mTrackInfo |
1720 | 0 | ? aSamples->mSamples[0]->mTrackInfo->GetID() |
1721 | 0 | : 0); |
1722 | 0 | DDLOG(DDLogCategory::Log, |
1723 | 0 | "video_demuxed_samples", |
1724 | 0 | uint64_t(aSamples->mSamples.Length())); |
1725 | 0 | mVideo.mDemuxRequest.Complete(); |
1726 | 0 | mVideo.mQueuedSamples.AppendElements(aSamples->mSamples); |
1727 | 0 | ScheduleUpdate(TrackInfo::kVideoTrack); |
1728 | 0 | } |
1729 | | |
1730 | | RefPtr<MediaFormatReader::AudioDataPromise> |
1731 | | MediaFormatReader::RequestAudioData() |
1732 | 0 | { |
1733 | 0 | MOZ_ASSERT(OnTaskQueue()); |
1734 | 0 | MOZ_DIAGNOSTIC_ASSERT(!mAudio.HasPromise(), "No duplicate sample requests"); |
1735 | 0 | MOZ_DIAGNOSTIC_ASSERT(IsVideoSeeking() || mSeekPromise.IsEmpty(), |
1736 | 0 | "No sample requests allowed while seeking"); |
1737 | 0 | MOZ_DIAGNOSTIC_ASSERT(IsVideoSeeking() || !mAudio.mSeekRequest.Exists() || |
1738 | 0 | mAudio.mTimeThreshold.isSome()); |
1739 | 0 | MOZ_DIAGNOSTIC_ASSERT(IsVideoSeeking() || !IsSeeking(), "called mid-seek"); |
1740 | 0 | LOGV(""); |
1741 | 0 |
|
1742 | 0 | if (!HasAudio()) { |
1743 | 0 | LOG("called with no audio track"); |
1744 | 0 | return AudioDataPromise::CreateAndReject(NS_ERROR_DOM_MEDIA_FATAL_ERR, |
1745 | 0 | __func__); |
1746 | 0 | } |
1747 | 0 |
|
1748 | 0 | if (IsSeeking()) { |
1749 | 0 | LOG("called mid-seek. Rejecting."); |
1750 | 0 | return AudioDataPromise::CreateAndReject(NS_ERROR_DOM_MEDIA_CANCELED, |
1751 | 0 | __func__); |
1752 | 0 | } |
1753 | 0 |
|
1754 | 0 | if (mShutdown) { |
1755 | 0 | NS_WARNING("RequestAudioData on shutdown MediaFormatReader!"); |
1756 | 0 | return AudioDataPromise::CreateAndReject(NS_ERROR_DOM_MEDIA_CANCELED, |
1757 | 0 | __func__); |
1758 | 0 | } |
1759 | 0 |
|
1760 | 0 | RefPtr<AudioDataPromise> p = mAudio.EnsurePromise(__func__); |
1761 | 0 | ScheduleUpdate(TrackInfo::kAudioTrack); |
1762 | 0 |
|
1763 | 0 | return p; |
1764 | 0 | } |
1765 | | |
1766 | | void |
1767 | | MediaFormatReader::DoDemuxAudio() |
1768 | 0 | { |
1769 | 0 | using SamplesPromise = MediaTrackDemuxer::SamplesPromise; |
1770 | 0 |
|
1771 | 0 | DDLOG(DDLogCategory::Log, "audio_demuxing", DDNoValue{}); |
1772 | 0 | auto p = mAudio.mTrackDemuxer->GetSamples(1); |
1773 | 0 |
|
1774 | 0 | if (mAudio.mFirstDemuxedSampleTime.isNothing()) { |
1775 | 0 | RefPtr<MediaFormatReader> self = this; |
1776 | 0 | p = p->Then( |
1777 | 0 | OwnerThread(), |
1778 | 0 | __func__, |
1779 | 0 | [self](RefPtr<MediaTrackDemuxer::SamplesHolder> aSamples) { |
1780 | 0 | DDLOGEX( |
1781 | 0 | self.get(), DDLogCategory::Log, "audio_first_demuxed", DDNoValue{}); |
1782 | 0 | self->OnFirstDemuxCompleted(TrackInfo::kAudioTrack, aSamples); |
1783 | 0 | return SamplesPromise::CreateAndResolve(aSamples.forget(), __func__); |
1784 | 0 | }, |
1785 | 0 | [self](const MediaResult& aError) { |
1786 | 0 | DDLOGEX( |
1787 | 0 | self.get(), DDLogCategory::Log, "audio_first_demuxing_error", aError); |
1788 | 0 | self->OnFirstDemuxFailed(TrackInfo::kAudioTrack, aError); |
1789 | 0 | return SamplesPromise::CreateAndReject(aError, __func__); |
1790 | 0 | }); |
1791 | 0 | } |
1792 | 0 |
|
1793 | 0 | p->Then(OwnerThread(), __func__, this, |
1794 | 0 | &MediaFormatReader::OnAudioDemuxCompleted, |
1795 | 0 | &MediaFormatReader::OnAudioDemuxFailed) |
1796 | 0 | ->Track(mAudio.mDemuxRequest); |
1797 | 0 | } |
1798 | | |
1799 | | void |
1800 | | MediaFormatReader::OnAudioDemuxCompleted( |
1801 | | RefPtr<MediaTrackDemuxer::SamplesHolder> aSamples) |
1802 | 0 | { |
1803 | 0 | LOGV("%zu audio samples demuxed (sid:%d)", |
1804 | 0 | aSamples->mSamples.Length(), |
1805 | 0 | aSamples->mSamples[0]->mTrackInfo |
1806 | 0 | ? aSamples->mSamples[0]->mTrackInfo->GetID() |
1807 | 0 | : 0); |
1808 | 0 | DDLOG(DDLogCategory::Log, |
1809 | 0 | "audio_demuxed_samples", |
1810 | 0 | uint64_t(aSamples->mSamples.Length())); |
1811 | 0 | mAudio.mDemuxRequest.Complete(); |
1812 | 0 | mAudio.mQueuedSamples.AppendElements(aSamples->mSamples); |
1813 | 0 | ScheduleUpdate(TrackInfo::kAudioTrack); |
1814 | 0 | } |
1815 | | |
1816 | | void |
1817 | | MediaFormatReader::NotifyNewOutput( |
1818 | | TrackType aTrack, const MediaDataDecoder::DecodedData& aResults) |
1819 | 0 | { |
1820 | 0 | MOZ_ASSERT(OnTaskQueue()); |
1821 | 0 | auto& decoder = GetDecoderData(aTrack); |
1822 | 0 | if (aResults.IsEmpty()) { |
1823 | 0 | DDLOG(DDLogCategory::Log, |
1824 | 0 | aTrack == TrackInfo::kAudioTrack ? "decoded_audio" : "decoded_video", |
1825 | 0 | "no output samples"); |
1826 | 0 | } else |
1827 | 0 | for (auto& sample : aResults) { |
1828 | 0 | if (DecoderDoctorLogger::IsDDLoggingEnabled()) { |
1829 | 0 | switch (sample->mType) { |
1830 | 0 | case MediaData::AUDIO_DATA: |
1831 | 0 | DDLOGPR(DDLogCategory::Log, |
1832 | 0 | aTrack == TrackInfo::kAudioTrack ? "decoded_audio" |
1833 | 0 | : "decoded_got_audio!?", |
1834 | 0 | "{\"type\":\"AudioData\", \"offset\":%" PRIi64 |
1835 | 0 | ", \"time_us\":%" PRIi64 ", \"timecode_us\":%" PRIi64 |
1836 | 0 | ", \"duration_us\":%" PRIi64 ", \"frames\":%" PRIu32 |
1837 | 0 | ", \"kf\":%s, \"channels\":%" PRIu32 ", \"rate\":%" PRIu32 |
1838 | 0 | ", \"bytes\":%zu}", |
1839 | 0 | sample->mOffset, |
1840 | 0 | sample->mTime.ToMicroseconds(), |
1841 | 0 | sample->mTimecode.ToMicroseconds(), |
1842 | 0 | sample->mDuration.ToMicroseconds(), |
1843 | 0 | sample->mFrames, |
1844 | 0 | sample->mKeyframe ? "true" : "false", |
1845 | 0 | sample->As<AudioData>()->mChannels, |
1846 | 0 | sample->As<AudioData>()->mRate, |
1847 | 0 | sample->As<AudioData>()->mAudioData.Size()); |
1848 | 0 | break; |
1849 | 0 | case MediaData::VIDEO_DATA: |
1850 | 0 | DDLOGPR(DDLogCategory::Log, |
1851 | 0 | aTrack == TrackInfo::kVideoTrack ? "decoded_video" |
1852 | 0 | : "decoded_got_video!?", |
1853 | 0 | "{\"type\":\"VideoData\", \"offset\":%" PRIi64 |
1854 | 0 | ", \"time_us\":%" PRIi64 ", \"timecode_us\":%" PRIi64 |
1855 | 0 | ", \"duration_us\":%" PRIi64 ", \"frames\":%" PRIu32 |
1856 | 0 | ", \"kf\":%s, \"size\":[%" PRIi32 ",%" PRIi32 "]}", |
1857 | 0 | sample->mOffset, |
1858 | 0 | sample->mTime.ToMicroseconds(), |
1859 | 0 | sample->mTimecode.ToMicroseconds(), |
1860 | 0 | sample->mDuration.ToMicroseconds(), |
1861 | 0 | sample->mFrames, |
1862 | 0 | sample->mKeyframe ? "true" : "false", |
1863 | 0 | sample->As<VideoData>()->mDisplay.width, |
1864 | 0 | sample->As<VideoData>()->mDisplay.height); |
1865 | 0 | break; |
1866 | 0 | case MediaData::RAW_DATA: |
1867 | 0 | DDLOGPR(DDLogCategory::Log, |
1868 | 0 | aTrack == TrackInfo::kAudioTrack |
1869 | 0 | ? "decoded_audio" |
1870 | 0 | : aTrack == TrackInfo::kVideoTrack ? "decoded_video" |
1871 | 0 | : "decoded_?", |
1872 | 0 | "{\"type\":\"RawData\", \"offset\":%" PRIi64 |
1873 | 0 | " \"time_us\":%" PRIi64 ", \"timecode_us\":%" PRIi64 |
1874 | 0 | ", \"duration_us\":%" PRIi64 ", \"frames\":%" PRIu32 |
1875 | 0 | ", \"kf\":%s}", |
1876 | 0 | sample->mOffset, |
1877 | 0 | sample->mTime.ToMicroseconds(), |
1878 | 0 | sample->mTimecode.ToMicroseconds(), |
1879 | 0 | sample->mDuration.ToMicroseconds(), |
1880 | 0 | sample->mFrames, |
1881 | 0 | sample->mKeyframe ? "true" : "false"); |
1882 | 0 | break; |
1883 | 0 | case MediaData::NULL_DATA: |
1884 | 0 | DDLOGPR(DDLogCategory::Log, |
1885 | 0 | aTrack == TrackInfo::kAudioTrack |
1886 | 0 | ? "decoded_audio" |
1887 | 0 | : aTrack == TrackInfo::kVideoTrack ? "decoded_video" |
1888 | 0 | : "decoded_?", |
1889 | 0 | "{\"type\":\"NullData\", \"offset\":%" PRIi64 |
1890 | 0 | " \"time_us\":%" PRIi64 ", \"timecode_us\":%" PRIi64 |
1891 | 0 | ", \"duration_us\":%" PRIi64 ", \"frames\":%" PRIu32 |
1892 | 0 | ", \"kf\":%s}", |
1893 | 0 | sample->mOffset, |
1894 | 0 | sample->mTime.ToMicroseconds(), |
1895 | 0 | sample->mTimecode.ToMicroseconds(), |
1896 | 0 | sample->mDuration.ToMicroseconds(), |
1897 | 0 | sample->mFrames, |
1898 | 0 | sample->mKeyframe ? "true" : "false"); |
1899 | 0 | break; |
1900 | 0 | } |
1901 | 0 | } |
1902 | 0 | LOGV("Received new %s sample time:%" PRId64 " duration:%" PRId64, |
1903 | 0 | TrackTypeToStr(aTrack), |
1904 | 0 | sample->mTime.ToMicroseconds(), |
1905 | 0 | sample->mDuration.ToMicroseconds()); |
1906 | 0 | decoder.mOutput.AppendElement(sample); |
1907 | 0 | decoder.mNumSamplesOutput++; |
1908 | 0 | decoder.mNumOfConsecutiveError = 0; |
1909 | 0 | } |
1910 | 0 | LOG("Done processing new %s samples", TrackTypeToStr(aTrack)); |
1911 | 0 |
|
1912 | 0 | if (!aResults.IsEmpty()) { |
1913 | 0 | // We have decoded our first frame, we can now starts to skip future errors. |
1914 | 0 | decoder.mFirstFrameTime.reset(); |
1915 | 0 | } |
1916 | 0 | ScheduleUpdate(aTrack); |
1917 | 0 | } |
1918 | | |
1919 | | void |
1920 | | MediaFormatReader::NotifyError(TrackType aTrack, const MediaResult& aError) |
1921 | 0 | { |
1922 | 0 | MOZ_ASSERT(OnTaskQueue()); |
1923 | 0 | NS_WARNING(aError.Description().get()); |
1924 | 0 | LOGV("%s Decoding error", TrackTypeToStr(aTrack)); |
1925 | 0 | auto& decoder = GetDecoderData(aTrack); |
1926 | 0 | decoder.mError = decoder.HasFatalError() ? decoder.mError : Some(aError); |
1927 | 0 |
|
1928 | 0 | // The GPU process had crashed and we receive a |
1929 | 0 | // NS_ERROR_DOM_MEDIA_NEED_NEW_DECODER because we were doing HW decoding. |
1930 | 0 | // Now, save the related data and we will report the recovery time when a new |
1931 | 0 | // decoder is ready. |
1932 | 0 | if (aTrack == TrackType::kVideoTrack && |
1933 | 0 | aError == NS_ERROR_DOM_MEDIA_NEED_NEW_DECODER && |
1934 | 0 | !aError.GPUCrashTimeStamp().IsNull()) { |
1935 | 0 |
|
1936 | 0 | GPUProcessCrashTelemetryLogger::RecordGPUCrashData(mMediaDecoderOwnerID, |
1937 | 0 | decoder.mDecoder.get(), |
1938 | 0 | aError.GPUCrashTimeStamp(), |
1939 | 0 | TimeStamp::Now()); |
1940 | 0 | } |
1941 | 0 |
|
1942 | 0 | ScheduleUpdate(aTrack); |
1943 | 0 | } |
1944 | | |
1945 | | void |
1946 | | MediaFormatReader::NotifyWaitingForData(TrackType aTrack) |
1947 | 0 | { |
1948 | 0 | MOZ_ASSERT(OnTaskQueue()); |
1949 | 0 | auto& decoder = GetDecoderData(aTrack); |
1950 | 0 | decoder.mWaitingForData = true; |
1951 | 0 | if (decoder.mTimeThreshold) { |
1952 | 0 | decoder.mTimeThreshold.ref().mWaiting = true; |
1953 | 0 | } |
1954 | 0 | ScheduleUpdate(aTrack); |
1955 | 0 | } |
1956 | | |
1957 | | void |
1958 | | MediaFormatReader::NotifyWaitingForKey(TrackType aTrack) |
1959 | 0 | { |
1960 | 0 | MOZ_ASSERT(OnTaskQueue()); |
1961 | 0 | auto& decoder = GetDecoderData(aTrack); |
1962 | 0 | mOnWaitingForKey.Notify(); |
1963 | 0 | if (!decoder.mDecodeRequest.Exists()) { |
1964 | 0 | LOGV("WaitingForKey received while no pending decode. Ignoring"); |
1965 | 0 | return; |
1966 | 0 | } |
1967 | 0 | decoder.mWaitingForKey = true; |
1968 | 0 | ScheduleUpdate(aTrack); |
1969 | 0 | } |
1970 | | |
1971 | | void |
1972 | | MediaFormatReader::NotifyEndOfStream(TrackType aTrack) |
1973 | 0 | { |
1974 | 0 | MOZ_ASSERT(OnTaskQueue()); |
1975 | 0 | auto& decoder = GetDecoderData(aTrack); |
1976 | 0 | decoder.mDemuxEOS = true; |
1977 | 0 | ScheduleUpdate(aTrack); |
1978 | 0 | } |
1979 | | |
1980 | | bool |
1981 | | MediaFormatReader::NeedInput(DecoderData& aDecoder) |
1982 | 0 | { |
1983 | 0 | // The decoder will not be fed a new raw sample until the current decoding |
1984 | 0 | // requests has completed. |
1985 | 0 | return |
1986 | 0 | (aDecoder.HasPromise() || aDecoder.mTimeThreshold.isSome()) && |
1987 | 0 | !aDecoder.HasPendingDrain() && |
1988 | 0 | !aDecoder.HasFatalError() && |
1989 | 0 | !aDecoder.mDemuxRequest.Exists() && |
1990 | 0 | !aDecoder.mOutput.Length() && |
1991 | 0 | !aDecoder.HasInternalSeekPending() && |
1992 | 0 | !aDecoder.mDecodeRequest.Exists(); |
1993 | 0 | } |
1994 | | |
1995 | | void |
1996 | | MediaFormatReader::ScheduleUpdate(TrackType aTrack) |
1997 | 0 | { |
1998 | 0 | MOZ_ASSERT(OnTaskQueue()); |
1999 | 0 | if (mShutdown) { |
2000 | 0 | return; |
2001 | 0 | } |
2002 | 0 | auto& decoder = GetDecoderData(aTrack); |
2003 | 0 | MOZ_RELEASE_ASSERT(decoder.GetCurrentInfo(), |
2004 | 0 | "Can only schedule update when track exists"); |
2005 | 0 |
|
2006 | 0 | if (decoder.mUpdateScheduled) { |
2007 | 0 | return; |
2008 | 0 | } |
2009 | 0 | LOGV("SchedulingUpdate(%s)", TrackTypeToStr(aTrack)); |
2010 | 0 | decoder.mUpdateScheduled = true; |
2011 | 0 | RefPtr<nsIRunnable> task(NewRunnableMethod<TrackType>( |
2012 | 0 | "MediaFormatReader::Update", this, &MediaFormatReader::Update, aTrack)); |
2013 | 0 | nsresult rv = OwnerThread()->Dispatch(task.forget()); |
2014 | 0 | MOZ_DIAGNOSTIC_ASSERT(NS_SUCCEEDED(rv)); |
2015 | 0 | Unused << rv; |
2016 | 0 | } |
2017 | | |
2018 | | bool |
2019 | | MediaFormatReader::UpdateReceivedNewData(TrackType aTrack) |
2020 | 0 | { |
2021 | 0 | MOZ_ASSERT(OnTaskQueue()); |
2022 | 0 | auto& decoder = GetDecoderData(aTrack); |
2023 | 0 |
|
2024 | 0 | if (!decoder.mReceivedNewData) { |
2025 | 0 | return false; |
2026 | 0 | } |
2027 | 0 | |
2028 | 0 | // We do not want to clear mWaitingForData while there are pending |
2029 | 0 | // demuxing or seeking operations that could affect the value of this flag. |
2030 | 0 | // This is in order to ensure that we will retry once they complete as we may |
2031 | 0 | // now have new data that could potentially allow those operations to |
2032 | 0 | // successfully complete if tried again. |
2033 | 0 | if (decoder.mSeekRequest.Exists()) { |
2034 | 0 | // Nothing more to do until this operation complete. |
2035 | 0 | return true; |
2036 | 0 | } |
2037 | 0 | |
2038 | 0 | if (aTrack == TrackType::kVideoTrack && mSkipRequest.Exists()) { |
2039 | 0 | LOGV("Skipping in progress, nothing more to do"); |
2040 | 0 | return true; |
2041 | 0 | } |
2042 | 0 |
|
2043 | 0 | if (decoder.mDemuxRequest.Exists()) { |
2044 | 0 | // We may have pending operations to process, so we want to continue |
2045 | 0 | // after UpdateReceivedNewData returns. |
2046 | 0 | return false; |
2047 | 0 | } |
2048 | 0 | |
2049 | 0 | if (decoder.HasPendingDrain()) { |
2050 | 0 | // We do not want to clear mWaitingForData or mDemuxEOS while |
2051 | 0 | // a drain is in progress in order to properly complete the operation. |
2052 | 0 | return false; |
2053 | 0 | } |
2054 | 0 | |
2055 | 0 | decoder.mReceivedNewData = false; |
2056 | 0 | if (decoder.mTimeThreshold) { |
2057 | 0 | decoder.mTimeThreshold.ref().mWaiting = false; |
2058 | 0 | } |
2059 | 0 | decoder.mWaitingForData = false; |
2060 | 0 |
|
2061 | 0 | if (decoder.HasFatalError()) { |
2062 | 0 | return false; |
2063 | 0 | } |
2064 | 0 | |
2065 | 0 | if (!mSeekPromise.IsEmpty() && |
2066 | 0 | (!IsVideoSeeking() || aTrack == TrackInfo::kVideoTrack)) { |
2067 | 0 | MOZ_ASSERT(!decoder.HasPromise()); |
2068 | 0 | MOZ_DIAGNOSTIC_ASSERT( |
2069 | 0 | (IsVideoSeeking() || !mAudio.mTimeThreshold) && !mVideo.mTimeThreshold, |
2070 | 0 | "InternalSeek must have been aborted when Seek was first called"); |
2071 | 0 | MOZ_DIAGNOSTIC_ASSERT( |
2072 | 0 | (IsVideoSeeking() || !mAudio.HasWaitingPromise()) && |
2073 | 0 | !mVideo.HasWaitingPromise(), |
2074 | 0 | "Waiting promises must have been rejected when Seek was first called"); |
2075 | 0 | if (mVideo.mSeekRequest.Exists() || |
2076 | 0 | (!IsVideoSeeking() && mAudio.mSeekRequest.Exists())) { |
2077 | 0 | // Already waiting for a seek to complete. Nothing more to do. |
2078 | 0 | return true; |
2079 | 0 | } |
2080 | 0 | LOG("Attempting Seek"); |
2081 | 0 | ScheduleSeek(); |
2082 | 0 | return true; |
2083 | 0 | } |
2084 | 0 | if (decoder.HasInternalSeekPending() || decoder.HasWaitingPromise()) { |
2085 | 0 | if (decoder.HasInternalSeekPending()) { |
2086 | 0 | LOG("Attempting Internal Seek"); |
2087 | 0 | InternalSeek(aTrack, decoder.mTimeThreshold.ref()); |
2088 | 0 | } |
2089 | 0 | if (decoder.HasWaitingPromise() && !decoder.IsWaitingForKey() && |
2090 | 0 | !decoder.IsWaitingForData()) { |
2091 | 0 | MOZ_ASSERT(!decoder.HasPromise()); |
2092 | 0 | LOG("We have new data. Resolving WaitingPromise"); |
2093 | 0 | decoder.mWaitingPromise.Resolve(decoder.mType, __func__); |
2094 | 0 | } |
2095 | 0 | return true; |
2096 | 0 | } |
2097 | 0 | return false; |
2098 | 0 | } |
2099 | | |
2100 | | void |
2101 | | MediaFormatReader::RequestDemuxSamples(TrackType aTrack) |
2102 | 0 | { |
2103 | 0 | MOZ_ASSERT(OnTaskQueue()); |
2104 | 0 | auto& decoder = GetDecoderData(aTrack); |
2105 | 0 | MOZ_ASSERT(!decoder.mDemuxRequest.Exists()); |
2106 | 0 |
|
2107 | 0 | if (!decoder.mQueuedSamples.IsEmpty()) { |
2108 | 0 | // No need to demux new samples. |
2109 | 0 | return; |
2110 | 0 | } |
2111 | 0 | |
2112 | 0 | if (decoder.mDemuxEOS) { |
2113 | 0 | // Nothing left to demux. |
2114 | 0 | // We do not want to attempt to demux while in waiting for data mode |
2115 | 0 | // as it would retrigger an unnecessary drain. |
2116 | 0 | return; |
2117 | 0 | } |
2118 | 0 | |
2119 | 0 | LOGV("Requesting extra demux %s", TrackTypeToStr(aTrack)); |
2120 | 0 | if (aTrack == TrackInfo::kVideoTrack) { |
2121 | 0 | DoDemuxVideo(); |
2122 | 0 | } else { |
2123 | 0 | DoDemuxAudio(); |
2124 | 0 | } |
2125 | 0 | } |
2126 | | |
2127 | | void |
2128 | | MediaFormatReader::DecodeDemuxedSamples(TrackType aTrack, |
2129 | | MediaRawData* aSample) |
2130 | 0 | { |
2131 | 0 | MOZ_ASSERT(OnTaskQueue()); |
2132 | 0 | auto& decoder = GetDecoderData(aTrack); |
2133 | 0 | RefPtr<MediaFormatReader> self = this; |
2134 | 0 | decoder.mFlushed = false; |
2135 | 0 | DDLOGPR(DDLogCategory::Log, |
2136 | 0 | aTrack == TrackInfo::kAudioTrack |
2137 | 0 | ? "decode_audio" |
2138 | 0 | : aTrack == TrackInfo::kVideoTrack ? "decode_video" : "decode_?", |
2139 | 0 | "{\"type\":\"MediaRawData\", \"offset\":%" PRIi64 |
2140 | 0 | ", \"bytes\":%zu, \"time_us\":%" PRIi64 ", \"timecode_us\":%" PRIi64 |
2141 | 0 | ", \"duration_us\":%" PRIi64 ", \"frames\":%" PRIu32 "%s%s}", |
2142 | 0 | aSample->mOffset, |
2143 | 0 | aSample->Size(), |
2144 | 0 | aSample->mTime.ToMicroseconds(), |
2145 | 0 | aSample->mTimecode.ToMicroseconds(), |
2146 | 0 | aSample->mDuration.ToMicroseconds(), |
2147 | 0 | aSample->mFrames, |
2148 | 0 | aSample->mKeyframe ? " kf" : "", |
2149 | 0 | aSample->mEOS ? " eos" : ""); |
2150 | 0 | decoder.mDecoder->Decode(aSample) |
2151 | 0 | ->Then(mTaskQueue, __func__, |
2152 | 0 | [self, aTrack, &decoder] |
2153 | 0 | (const MediaDataDecoder::DecodedData& aResults) { |
2154 | 0 | decoder.mDecodeRequest.Complete(); |
2155 | 0 | self->NotifyNewOutput(aTrack, aResults); |
2156 | 0 |
|
2157 | 0 | // When we recovered from a GPU crash and get the first decoded |
2158 | 0 | // frame, report the recovery time telemetry. |
2159 | 0 | if (aTrack == TrackType::kVideoTrack) { |
2160 | 0 | GPUProcessCrashTelemetryLogger::ReportTelemetry( |
2161 | 0 | self->mMediaDecoderOwnerID, decoder.mDecoder.get()); |
2162 | 0 | } |
2163 | 0 | }, |
2164 | 0 | [self, aTrack, &decoder](const MediaResult& aError) { |
2165 | 0 | decoder.mDecodeRequest.Complete(); |
2166 | 0 | self->NotifyError(aTrack, aError); |
2167 | 0 | }) |
2168 | 0 | ->Track(decoder.mDecodeRequest); |
2169 | 0 | } |
2170 | | |
2171 | | void |
2172 | | MediaFormatReader::HandleDemuxedSamples( |
2173 | | TrackType aTrack, FrameStatistics::AutoNotifyDecoded& aA) |
2174 | 0 | { |
2175 | 0 | MOZ_ASSERT(OnTaskQueue()); |
2176 | 0 |
|
2177 | 0 | auto& decoder = GetDecoderData(aTrack); |
2178 | 0 |
|
2179 | 0 | if (decoder.mFlushing) { |
2180 | 0 | LOGV("Decoder operation in progress, let it complete."); |
2181 | 0 | return; |
2182 | 0 | } |
2183 | 0 |
|
2184 | 0 | if (decoder.mQueuedSamples.IsEmpty()) { |
2185 | 0 | return; |
2186 | 0 | } |
2187 | 0 | |
2188 | 0 | RefPtr<MediaRawData> sample = decoder.mQueuedSamples[0]; |
2189 | 0 | const RefPtr<TrackInfoSharedPtr> info = sample->mTrackInfo; |
2190 | 0 |
|
2191 | 0 | if (info && decoder.mLastStreamSourceID != info->GetID()) { |
2192 | 0 | nsTArray<RefPtr<MediaRawData>> samples; |
2193 | 0 | if (decoder.mDecoder) { |
2194 | 0 | bool recyclable = |
2195 | 0 | StaticPrefs::MediaDecoderRecycleEnabled() && |
2196 | 0 | decoder.mDecoder->SupportDecoderRecycling() && |
2197 | 0 | (*info)->mCrypto.mValid == decoder.GetCurrentInfo()->mCrypto.mValid && |
2198 | 0 | (*info)->mMimeType == decoder.GetCurrentInfo()->mMimeType; |
2199 | 0 | if (!recyclable && decoder.mTimeThreshold.isNothing() && |
2200 | 0 | (decoder.mNextStreamSourceID.isNothing() || |
2201 | 0 | decoder.mNextStreamSourceID.ref() != info->GetID())) { |
2202 | 0 | LOG("%s stream id has changed from:%d to:%d, draining decoder.", |
2203 | 0 | TrackTypeToStr(aTrack), |
2204 | 0 | decoder.mLastStreamSourceID, |
2205 | 0 | info->GetID()); |
2206 | 0 | decoder.RequestDrain(); |
2207 | 0 | decoder.mNextStreamSourceID = Some(info->GetID()); |
2208 | 0 | ScheduleUpdate(aTrack); |
2209 | 0 | return; |
2210 | 0 | } |
2211 | 0 |
|
2212 | 0 | // If flushing is required, it will clear our array of queued samples. |
2213 | 0 | // So we may need to make a copy. |
2214 | 0 | samples = decoder.mQueuedSamples; |
2215 | 0 | if (!recyclable) { |
2216 | 0 | LOG("Decoder does not support recycling, recreate decoder."); |
2217 | 0 | ShutdownDecoder(aTrack); |
2218 | 0 | } else if (decoder.HasWaitingPromise()) { |
2219 | 0 | decoder.Flush(); |
2220 | 0 | } |
2221 | 0 | } |
2222 | 0 |
|
2223 | 0 | LOG("%s stream id has changed from:%d to:%d.", |
2224 | 0 | TrackTypeToStr(aTrack), |
2225 | 0 | decoder.mLastStreamSourceID, |
2226 | 0 | info->GetID()); |
2227 | 0 |
|
2228 | 0 | decoder.mNextStreamSourceID.reset(); |
2229 | 0 | decoder.mLastStreamSourceID = info->GetID(); |
2230 | 0 | decoder.mInfo = info; |
2231 | 0 |
|
2232 | 0 | decoder.mMeanRate.Reset(); |
2233 | 0 |
|
2234 | 0 | if (sample->mKeyframe) { |
2235 | 0 | if (samples.Length()) { |
2236 | 0 | decoder.mQueuedSamples = std::move(samples); |
2237 | 0 | } |
2238 | 0 | } else { |
2239 | 0 | auto time = TimeInterval(sample->mTime, sample->GetEndTime()); |
2240 | 0 | InternalSeekTarget seekTarget = |
2241 | 0 | decoder.mTimeThreshold.refOr(InternalSeekTarget(time, false)); |
2242 | 0 | LOG("Stream change occurred on a non-keyframe. Seeking to:%" PRId64, |
2243 | 0 | sample->mTime.ToMicroseconds()); |
2244 | 0 | InternalSeek(aTrack, seekTarget); |
2245 | 0 | return; |
2246 | 0 | } |
2247 | 0 | } |
2248 | 0 |
|
2249 | 0 | // Calculate the average frame rate. The first frame will be accounted |
2250 | 0 | // for twice. |
2251 | 0 | decoder.mMeanRate.Update(sample->mDuration); |
2252 | 0 |
|
2253 | 0 | if (!decoder.mDecoder) { |
2254 | 0 | mDecoderFactory->CreateDecoder(aTrack); |
2255 | 0 | return; |
2256 | 0 | } |
2257 | 0 | |
2258 | 0 | LOGV("Input:%" PRId64 " (dts:%" PRId64 " kf:%d)", |
2259 | 0 | sample->mTime.ToMicroseconds(), sample->mTimecode.ToMicroseconds(), |
2260 | 0 | sample->mKeyframe); |
2261 | 0 | decoder.mNumSamplesInput++; |
2262 | 0 | decoder.mSizeOfQueue++; |
2263 | 0 | if (aTrack == TrackInfo::kVideoTrack) { |
2264 | 0 | aA.mStats.mParsedFrames++; |
2265 | 0 | } |
2266 | 0 |
|
2267 | 0 | DecodeDemuxedSamples(aTrack, sample); |
2268 | 0 |
|
2269 | 0 | decoder.mQueuedSamples.RemoveElementAt(0); |
2270 | 0 | } |
2271 | | |
2272 | | void |
2273 | | MediaFormatReader::InternalSeek(TrackType aTrack, |
2274 | | const InternalSeekTarget& aTarget) |
2275 | 0 | { |
2276 | 0 | MOZ_ASSERT(OnTaskQueue()); |
2277 | 0 | LOG("%s internal seek to %f", |
2278 | 0 | TrackTypeToStr(aTrack), aTarget.Time().ToSeconds()); |
2279 | 0 |
|
2280 | 0 | auto& decoder = GetDecoderData(aTrack); |
2281 | 0 | decoder.Flush(); |
2282 | 0 | decoder.ResetDemuxer(); |
2283 | 0 | decoder.mTimeThreshold = Some(aTarget); |
2284 | 0 | DDLOG(DDLogCategory::Log, "seeking", DDNoValue{}); |
2285 | 0 | RefPtr<MediaFormatReader> self = this; |
2286 | 0 | decoder.mTrackDemuxer->Seek(decoder.mTimeThreshold.ref().Time()) |
2287 | 0 | ->Then( |
2288 | 0 | OwnerThread(), |
2289 | 0 | __func__, |
2290 | 0 | [self, aTrack](TimeUnit aTime) { |
2291 | 0 | DDLOGEX(self.get(), DDLogCategory::Log, "seeked", DDNoValue{}); |
2292 | 0 | auto& decoder = self->GetDecoderData(aTrack); |
2293 | 0 | decoder.mSeekRequest.Complete(); |
2294 | 0 | MOZ_ASSERT( |
2295 | 0 | decoder.mTimeThreshold, |
2296 | 0 | "Seek promise must be disconnected when timethreshold is reset"); |
2297 | 0 | decoder.mTimeThreshold.ref().mHasSeeked = true; |
2298 | 0 | self->SetVideoDecodeThreshold(); |
2299 | 0 | self->ScheduleUpdate(aTrack); |
2300 | 0 | }, |
2301 | 0 | [self, aTrack](const MediaResult& aError) { |
2302 | 0 | auto& decoder = self->GetDecoderData(aTrack); |
2303 | 0 | decoder.mSeekRequest.Complete(); |
2304 | 0 | switch (aError.Code()) { |
2305 | 0 | case NS_ERROR_DOM_MEDIA_WAITING_FOR_DATA: |
2306 | 0 | DDLOGEX( |
2307 | 0 | self.get(), DDLogCategory::Log, "seeking_interrupted", aError); |
2308 | 0 | self->NotifyWaitingForData(aTrack); |
2309 | 0 | break; |
2310 | 0 | case NS_ERROR_DOM_MEDIA_END_OF_STREAM: |
2311 | 0 | DDLOGEX( |
2312 | 0 | self.get(), DDLogCategory::Log, "seeking_interrupted", aError); |
2313 | 0 | decoder.mTimeThreshold.reset(); |
2314 | 0 | self->NotifyEndOfStream(aTrack); |
2315 | 0 | break; |
2316 | 0 | case NS_ERROR_DOM_MEDIA_CANCELED: |
2317 | 0 | DDLOGEX( |
2318 | 0 | self.get(), DDLogCategory::Log, "seeking_interrupted", aError); |
2319 | 0 | decoder.mTimeThreshold.reset(); |
2320 | 0 | break; |
2321 | 0 | default: |
2322 | 0 | DDLOGEX(self.get(), DDLogCategory::Log, "seeking_error", aError); |
2323 | 0 | decoder.mTimeThreshold.reset(); |
2324 | 0 | self->NotifyError(aTrack, aError); |
2325 | 0 | break; |
2326 | 0 | } |
2327 | 0 | }) |
2328 | 0 | ->Track(decoder.mSeekRequest); |
2329 | 0 | } |
2330 | | |
2331 | | void |
2332 | | MediaFormatReader::DrainDecoder(TrackType aTrack) |
2333 | 0 | { |
2334 | 0 | MOZ_ASSERT(OnTaskQueue()); |
2335 | 0 |
|
2336 | 0 | auto& decoder = GetDecoderData(aTrack); |
2337 | 0 | if (decoder.mDrainState == DrainState::Draining) { |
2338 | 0 | return; |
2339 | 0 | } |
2340 | 0 | if (!decoder.mDecoder || |
2341 | 0 | (decoder.mDrainState != DrainState::PartialDrainPending && |
2342 | 0 | decoder.mNumSamplesInput == decoder.mNumSamplesOutput)) { |
2343 | 0 | // No frames to drain. |
2344 | 0 | LOGV("Draining %s with nothing to drain", TrackTypeToStr(aTrack)); |
2345 | 0 | decoder.mDrainState = DrainState::DrainAborted; |
2346 | 0 | ScheduleUpdate(aTrack); |
2347 | 0 | return; |
2348 | 0 | } |
2349 | 0 |
|
2350 | 0 | decoder.mDrainState = DrainState::Draining; |
2351 | 0 |
|
2352 | 0 | DDLOG(DDLogCategory::Log, "draining", DDNoValue{}); |
2353 | 0 | RefPtr<MediaFormatReader> self = this; |
2354 | 0 | decoder.mDecoder->Drain() |
2355 | 0 | ->Then(mTaskQueue, __func__, |
2356 | 0 | [self, aTrack, &decoder] |
2357 | 0 | (const MediaDataDecoder::DecodedData& aResults) { |
2358 | 0 | decoder.mDrainRequest.Complete(); |
2359 | 0 | DDLOGEX(self.get(), DDLogCategory::Log, "drained", DDNoValue{}); |
2360 | 0 | if (aResults.IsEmpty()) { |
2361 | 0 | decoder.mDrainState = DrainState::DrainCompleted; |
2362 | 0 | } else { |
2363 | 0 | self->NotifyNewOutput(aTrack, aResults); |
2364 | 0 | // Let's see if we have any more data available to drain. |
2365 | 0 | decoder.mDrainState = DrainState::PartialDrainPending; |
2366 | 0 | } |
2367 | 0 | self->ScheduleUpdate(aTrack); |
2368 | 0 | }, |
2369 | 0 | [self, aTrack, &decoder](const MediaResult& aError) { |
2370 | 0 | decoder.mDrainRequest.Complete(); |
2371 | 0 | DDLOGEX(self.get(), DDLogCategory::Log, "draining_error", aError); |
2372 | 0 | self->NotifyError(aTrack, aError); |
2373 | 0 | }) |
2374 | 0 | ->Track(decoder.mDrainRequest); |
2375 | 0 | LOG("Requesting %s decoder to drain", TrackTypeToStr(aTrack)); |
2376 | 0 | } |
2377 | | |
2378 | | void |
2379 | | MediaFormatReader::Update(TrackType aTrack) |
2380 | 0 | { |
2381 | 0 | MOZ_ASSERT(OnTaskQueue()); |
2382 | 0 |
|
2383 | 0 | if (mShutdown) { |
2384 | 0 | return; |
2385 | 0 | } |
2386 | 0 | |
2387 | 0 | LOGV("Processing update for %s", TrackTypeToStr(aTrack)); |
2388 | 0 |
|
2389 | 0 | bool needOutput = false; |
2390 | 0 | auto& decoder = GetDecoderData(aTrack); |
2391 | 0 | decoder.mUpdateScheduled = false; |
2392 | 0 |
|
2393 | 0 | if (!mInitDone) { |
2394 | 0 | return; |
2395 | 0 | } |
2396 | 0 | |
2397 | 0 | if (aTrack == TrackType::kVideoTrack && mSkipRequest.Exists()) { |
2398 | 0 | LOGV("Skipping in progress, nothing more to do"); |
2399 | 0 | return; |
2400 | 0 | } |
2401 | 0 |
|
2402 | 0 | if (UpdateReceivedNewData(aTrack)) { |
2403 | 0 | LOGV("Nothing more to do"); |
2404 | 0 | return; |
2405 | 0 | } |
2406 | 0 |
|
2407 | 0 | if (decoder.mSeekRequest.Exists()) { |
2408 | 0 | LOGV("Seeking hasn't completed, nothing more to do"); |
2409 | 0 | return; |
2410 | 0 | } |
2411 | 0 |
|
2412 | 0 | MOZ_DIAGNOSTIC_ASSERT( |
2413 | 0 | !decoder.HasInternalSeekPending() || |
2414 | 0 | (!decoder.mOutput.Length() && !decoder.mQueuedSamples.Length()), |
2415 | 0 | "No frames can be demuxed or decoded while an internal seek is pending"); |
2416 | 0 |
|
2417 | 0 | // Record number of frames decoded and parsed. Automatically update the |
2418 | 0 | // stats counters using the AutoNotifyDecoded stack-based class. |
2419 | 0 | FrameStatistics::AutoNotifyDecoded a(mFrameStats); |
2420 | 0 |
|
2421 | 0 | // Drop any frames found prior our internal seek target. |
2422 | 0 | while (decoder.mTimeThreshold && decoder.mOutput.Length()) { |
2423 | 0 | RefPtr<MediaData>& output = decoder.mOutput[0]; |
2424 | 0 | InternalSeekTarget target = decoder.mTimeThreshold.ref(); |
2425 | 0 | auto time = output->mTime; |
2426 | 0 | if (time >= target.Time()) { |
2427 | 0 | // We have reached our internal seek target. |
2428 | 0 | decoder.mTimeThreshold.reset(); |
2429 | 0 | // We might have dropped some keyframes. |
2430 | 0 | mPreviousDecodedKeyframeTime_us = sNoPreviousDecodedKeyframe; |
2431 | 0 | } |
2432 | 0 | if (time < target.Time() || (target.mDropTarget && target.Contains(time))) { |
2433 | 0 | LOGV("Internal Seeking: Dropping %s frame time:%f wanted:%f (kf:%d)", |
2434 | 0 | TrackTypeToStr(aTrack), |
2435 | 0 | output->mTime.ToSeconds(), |
2436 | 0 | target.Time().ToSeconds(), |
2437 | 0 | output->mKeyframe); |
2438 | 0 | decoder.mOutput.RemoveElementAt(0); |
2439 | 0 | decoder.mSizeOfQueue -= 1; |
2440 | 0 | } |
2441 | 0 | } |
2442 | 0 |
|
2443 | 0 | while (decoder.mOutput.Length() && |
2444 | 0 | decoder.mOutput[0]->mType == MediaData::NULL_DATA) { |
2445 | 0 | LOGV("Dropping null data. Time: %" PRId64, |
2446 | 0 | decoder.mOutput[0]->mTime.ToMicroseconds()); |
2447 | 0 | decoder.mOutput.RemoveElementAt(0); |
2448 | 0 | decoder.mSizeOfQueue -= 1; |
2449 | 0 | } |
2450 | 0 |
|
2451 | 0 | if (decoder.HasPromise()) { |
2452 | 0 | needOutput = true; |
2453 | 0 | if (decoder.mOutput.Length()) { |
2454 | 0 | RefPtr<MediaData> output = decoder.mOutput[0]; |
2455 | 0 | decoder.mOutput.RemoveElementAt(0); |
2456 | 0 | decoder.mSizeOfQueue -= 1; |
2457 | 0 | decoder.mLastDecodedSampleTime = |
2458 | 0 | Some(TimeInterval(output->mTime, output->GetEndTime())); |
2459 | 0 | decoder.mNumSamplesOutputTotal++; |
2460 | 0 | ReturnOutput(output, aTrack); |
2461 | 0 | // We have a decoded sample ready to be returned. |
2462 | 0 | if (aTrack == TrackType::kVideoTrack) { |
2463 | 0 | uint64_t delta = |
2464 | 0 | decoder.mNumSamplesOutputTotal - mLastReportedNumDecodedFrames; |
2465 | 0 | a.mStats.mDecodedFrames = static_cast<uint32_t>(delta); |
2466 | 0 | mLastReportedNumDecodedFrames = decoder.mNumSamplesOutputTotal; |
2467 | 0 | if (output->mKeyframe) { |
2468 | 0 | if (mPreviousDecodedKeyframeTime_us < output->mTime.ToMicroseconds()) { |
2469 | 0 | // There is a previous keyframe -> Record inter-keyframe stats. |
2470 | 0 | uint64_t segment_us = |
2471 | 0 | output->mTime.ToMicroseconds() - mPreviousDecodedKeyframeTime_us; |
2472 | 0 | a.mStats.mInterKeyframeSum_us += segment_us; |
2473 | 0 | a.mStats.mInterKeyframeCount += 1; |
2474 | 0 | if (a.mStats.mInterKeyFrameMax_us < segment_us) { |
2475 | 0 | a.mStats.mInterKeyFrameMax_us = segment_us; |
2476 | 0 | } |
2477 | 0 | } |
2478 | 0 | mPreviousDecodedKeyframeTime_us = output->mTime.ToMicroseconds(); |
2479 | 0 | } |
2480 | 0 | nsCString error; |
2481 | 0 | mVideo.mIsHardwareAccelerated = |
2482 | 0 | mVideo.mDecoder && mVideo.mDecoder->IsHardwareAccelerated(error); |
2483 | | #ifdef XP_WIN |
2484 | | // D3D11_YCBCR_IMAGE images are GPU based, we try to limit the amount |
2485 | | // of GPU RAM used. |
2486 | | VideoData* videoData = static_cast<VideoData*>(output.get()); |
2487 | | mVideo.mIsHardwareAccelerated = |
2488 | | mVideo.mIsHardwareAccelerated || |
2489 | | (videoData->mImage && |
2490 | | videoData->mImage->GetFormat() == ImageFormat::D3D11_YCBCR_IMAGE); |
2491 | | #endif |
2492 | | } |
2493 | 0 | } else if (decoder.HasFatalError()) { |
2494 | 0 | LOG("Rejecting %s promise: DECODE_ERROR", TrackTypeToStr(aTrack)); |
2495 | 0 | decoder.RejectPromise(decoder.mError.ref(), __func__); |
2496 | 0 | return; |
2497 | 0 | } else if (decoder.HasCompletedDrain()) { |
2498 | 0 | if (decoder.mDemuxEOS) { |
2499 | 0 | LOG("Rejecting %s promise: EOS", TrackTypeToStr(aTrack)); |
2500 | 0 | decoder.RejectPromise(NS_ERROR_DOM_MEDIA_END_OF_STREAM, __func__); |
2501 | 0 | } else if (decoder.mWaitingForData) { |
2502 | 0 | if (decoder.mDrainState == DrainState::DrainCompleted && |
2503 | 0 | decoder.mLastDecodedSampleTime && |
2504 | 0 | !decoder.mNextStreamSourceID) { |
2505 | 0 | // We have completed draining the decoder following WaitingForData. |
2506 | 0 | // Set up the internal seek machinery to be able to resume from the |
2507 | 0 | // last sample decoded. |
2508 | 0 | LOG("Seeking to last sample time: %" PRId64, |
2509 | 0 | decoder.mLastDecodedSampleTime.ref().mStart.ToMicroseconds()); |
2510 | 0 | InternalSeek(aTrack, |
2511 | 0 | InternalSeekTarget(decoder.mLastDecodedSampleTime.ref(), true)); |
2512 | 0 | } |
2513 | 0 | if (!decoder.mReceivedNewData) { |
2514 | 0 | LOG("Rejecting %s promise: WAITING_FOR_DATA", TrackTypeToStr(aTrack)); |
2515 | 0 | decoder.RejectPromise(NS_ERROR_DOM_MEDIA_WAITING_FOR_DATA, __func__); |
2516 | 0 | } |
2517 | 0 | } |
2518 | 0 |
|
2519 | 0 | decoder.mDrainState = DrainState::None; |
2520 | 0 |
|
2521 | 0 | // Now that draining has completed, we check if we have received |
2522 | 0 | // new data again as the result may now be different from the earlier |
2523 | 0 | // run. |
2524 | 0 | if (UpdateReceivedNewData(aTrack) || decoder.mSeekRequest.Exists()) { |
2525 | 0 | LOGV("Nothing more to do"); |
2526 | 0 | return; |
2527 | 0 | } |
2528 | 0 | } else if (decoder.mDemuxEOS && |
2529 | 0 | !decoder.HasPendingDrain() && |
2530 | 0 | decoder.mQueuedSamples.IsEmpty()) { |
2531 | 0 | // It is possible to transition from WAITING_FOR_DATA directly to EOS |
2532 | 0 | // state during the internal seek; in which case no draining would occur. |
2533 | 0 | // There is no more samples left to be decoded and we are already in |
2534 | 0 | // EOS state. We can immediately reject the data promise. |
2535 | 0 | LOG("Rejecting %s promise: EOS", TrackTypeToStr(aTrack)); |
2536 | 0 | decoder.RejectPromise(NS_ERROR_DOM_MEDIA_END_OF_STREAM, __func__); |
2537 | 0 | } else if (decoder.mWaitingForKey) { |
2538 | 0 | LOG("Rejecting %s promise: WAITING_FOR_DATA due to waiting for key", |
2539 | 0 | TrackTypeToStr(aTrack)); |
2540 | 0 | decoder.RejectPromise(NS_ERROR_DOM_MEDIA_WAITING_FOR_DATA, __func__); |
2541 | 0 | } else if (IsDecoderWaitingForCDM(aTrack)) { |
2542 | 0 | // Rejecting the promise could lead to entering buffering state for MDSM, |
2543 | 0 | // once a qualified(with the same key system and sessions created by the |
2544 | 0 | // same InitData) new cdm proxy is set, decoding can be resumed. |
2545 | 0 | LOG("Rejecting %s promise: WAITING_FOR_DATA due to waiting for CDM", |
2546 | 0 | TrackTypeToStr(aTrack)); |
2547 | 0 | decoder.RejectPromise(NS_ERROR_DOM_MEDIA_WAITING_FOR_DATA, __func__); |
2548 | 0 | } |
2549 | 0 | } |
2550 | 0 |
|
2551 | 0 | if (decoder.mDrainState == DrainState::DrainRequested || |
2552 | 0 | decoder.mDrainState == DrainState::PartialDrainPending) { |
2553 | 0 | if (decoder.mOutput.IsEmpty()) { |
2554 | 0 | DrainDecoder(aTrack); |
2555 | 0 | } |
2556 | 0 | return; |
2557 | 0 | } |
2558 | 0 |
|
2559 | 0 | if (decoder.mError && !decoder.HasFatalError()) { |
2560 | 0 | MOZ_RELEASE_ASSERT(!decoder.HasInternalSeekPending(), |
2561 | 0 | "No error can occur while an internal seek is pending"); |
2562 | 0 | bool needsNewDecoder = |
2563 | 0 | decoder.mError.ref() == NS_ERROR_DOM_MEDIA_NEED_NEW_DECODER; |
2564 | 0 | if (!needsNewDecoder && |
2565 | 0 | ++decoder.mNumOfConsecutiveError > decoder.mMaxConsecutiveError) { |
2566 | 0 | DDLOG(DDLogCategory::Log, "too_many_decode_errors", decoder.mError.ref()); |
2567 | 0 | NotifyError(aTrack, decoder.mError.ref()); |
2568 | 0 | return; |
2569 | 0 | } |
2570 | 0 | decoder.mError.reset(); |
2571 | 0 |
|
2572 | 0 | LOG("%s decoded error count %d", TrackTypeToStr(aTrack), |
2573 | 0 | decoder.mNumOfConsecutiveError); |
2574 | 0 |
|
2575 | 0 | if (needsNewDecoder) { |
2576 | 0 | LOG("Error: Need new decoder"); |
2577 | 0 | ShutdownDecoder(aTrack); |
2578 | 0 | } |
2579 | 0 | if (decoder.mFirstFrameTime) { |
2580 | 0 | TimeInterval seekInterval = TimeInterval(decoder.mFirstFrameTime.ref(), |
2581 | 0 | decoder.mFirstFrameTime.ref()); |
2582 | 0 | InternalSeek(aTrack, InternalSeekTarget(seekInterval, false)); |
2583 | 0 | return; |
2584 | 0 | } |
2585 | 0 | |
2586 | 0 | TimeUnit nextKeyframe; |
2587 | 0 | if (aTrack == TrackType::kVideoTrack && |
2588 | 0 | NS_SUCCEEDED( |
2589 | 0 | decoder.mTrackDemuxer->GetNextRandomAccessPoint(&nextKeyframe)) && |
2590 | 0 | !nextKeyframe.IsInfinite()) { |
2591 | 0 | SkipVideoDemuxToNextKeyFrame( |
2592 | 0 | decoder.mLastDecodedSampleTime.refOr(TimeInterval()).Length()); |
2593 | 0 | } else if (aTrack == TrackType::kAudioTrack) { |
2594 | 0 | decoder.Flush(); |
2595 | 0 | } else { |
2596 | 0 | DDLOG(DDLogCategory::Log, "no_keyframe", NS_ERROR_DOM_MEDIA_FATAL_ERR); |
2597 | 0 | // We can't recover from this error. |
2598 | 0 | NotifyError(aTrack, NS_ERROR_DOM_MEDIA_FATAL_ERR); |
2599 | 0 | } |
2600 | 0 | return; |
2601 | 0 | } |
2602 | 0 |
|
2603 | 0 | bool needInput = NeedInput(decoder); |
2604 | 0 |
|
2605 | 0 | LOGV("Update(%s) ni=%d no=%d in:%" PRIu64 " out:%" PRIu64 |
2606 | 0 | " qs=%u decoding:%d flushing:%d desc:%s pending:%u waiting:%d eos:%d " |
2607 | 0 | "ds:%d sid:%u waitcdm:%d", |
2608 | 0 | TrackTypeToStr(aTrack), |
2609 | 0 | needInput, |
2610 | 0 | needOutput, |
2611 | 0 | decoder.mNumSamplesInput, |
2612 | 0 | decoder.mNumSamplesOutput, |
2613 | 0 | uint32_t(size_t(decoder.mSizeOfQueue)), |
2614 | 0 | decoder.mDecodeRequest.Exists(), |
2615 | 0 | decoder.mFlushing, |
2616 | 0 | decoder.mDescription.get(), |
2617 | 0 | uint32_t(decoder.mOutput.Length()), |
2618 | 0 | decoder.mWaitingForData, |
2619 | 0 | decoder.mDemuxEOS, |
2620 | 0 | int32_t(decoder.mDrainState), |
2621 | 0 | decoder.mLastStreamSourceID, |
2622 | 0 | IsDecoderWaitingForCDM(aTrack)); |
2623 | 0 |
|
2624 | 0 | if (IsWaitingOnCDMResource() || !ResolveSetCDMPromiseIfDone(aTrack)) { |
2625 | 0 | // If the content is encrypted, MFR won't start to create decoder until |
2626 | 0 | // CDMProxy is set. |
2627 | 0 | return; |
2628 | 0 | } |
2629 | 0 | |
2630 | 0 | if ((decoder.IsWaitingForData() && |
2631 | 0 | (!decoder.mTimeThreshold || decoder.mTimeThreshold.ref().mWaiting)) || |
2632 | 0 | (decoder.IsWaitingForKey())) { |
2633 | 0 | // Nothing more we can do at present. |
2634 | 0 | LOGV("Still waiting for data or key. data(%d)/key(%d)", |
2635 | 0 | decoder.mWaitingForData, |
2636 | 0 | decoder.mWaitingForKey); |
2637 | 0 | return; |
2638 | 0 | } |
2639 | 0 |
|
2640 | 0 | if (decoder.CancelWaitingForKey()) { |
2641 | 0 | LOGV("No longer waiting for key. Resolving waiting promise"); |
2642 | 0 | return; |
2643 | 0 | } |
2644 | 0 |
|
2645 | 0 | if (!needInput) { |
2646 | 0 | LOGV("No need for additional input (pending:%u)", |
2647 | 0 | uint32_t(decoder.mOutput.Length())); |
2648 | 0 | return; |
2649 | 0 | } |
2650 | 0 |
|
2651 | 0 | // Demux samples if we don't have some. |
2652 | 0 | RequestDemuxSamples(aTrack); |
2653 | 0 |
|
2654 | 0 | HandleDemuxedSamples(aTrack, a); |
2655 | 0 | } |
2656 | | |
2657 | | void |
2658 | | MediaFormatReader::ReturnOutput(MediaData* aData, TrackType aTrack) |
2659 | 0 | { |
2660 | 0 | MOZ_ASSERT(GetDecoderData(aTrack).HasPromise()); |
2661 | 0 | MOZ_DIAGNOSTIC_ASSERT(aData->mType != MediaData::NULL_DATA); |
2662 | 0 | LOG("Resolved data promise for %s [%" PRId64 ", %" PRId64 "]", |
2663 | 0 | TrackTypeToStr(aTrack), |
2664 | 0 | aData->mTime.ToMicroseconds(), |
2665 | 0 | aData->GetEndTime().ToMicroseconds()); |
2666 | 0 |
|
2667 | 0 | if (aTrack == TrackInfo::kAudioTrack) { |
2668 | 0 | AudioData* audioData = static_cast<AudioData*>(aData); |
2669 | 0 |
|
2670 | 0 | if (audioData->mChannels != mInfo.mAudio.mChannels || |
2671 | 0 | audioData->mRate != mInfo.mAudio.mRate) { |
2672 | 0 | LOG("change of audio format (rate:%d->%d). " |
2673 | 0 | "This is an unsupported configuration", |
2674 | 0 | mInfo.mAudio.mRate, |
2675 | 0 | audioData->mRate); |
2676 | 0 | mInfo.mAudio.mRate = audioData->mRate; |
2677 | 0 | mInfo.mAudio.mChannels = audioData->mChannels; |
2678 | 0 | } |
2679 | 0 | mAudio.ResolvePromise(audioData, __func__); |
2680 | 0 | } else if (aTrack == TrackInfo::kVideoTrack) { |
2681 | 0 | VideoData* videoData = static_cast<VideoData*>(aData); |
2682 | 0 |
|
2683 | 0 | if (videoData->mDisplay != mInfo.mVideo.mDisplay) { |
2684 | 0 | LOG("change of video display size (%dx%d->%dx%d)", |
2685 | 0 | mInfo.mVideo.mDisplay.width, mInfo.mVideo.mDisplay.height, |
2686 | 0 | videoData->mDisplay.width, videoData->mDisplay.height); |
2687 | 0 | mInfo.mVideo.mDisplay = videoData->mDisplay; |
2688 | 0 | } |
2689 | 0 |
|
2690 | 0 | TimeUnit nextKeyframe; |
2691 | 0 | if (!mVideo.HasInternalSeekPending() && |
2692 | 0 | NS_SUCCEEDED( |
2693 | 0 | mVideo.mTrackDemuxer->GetNextRandomAccessPoint(&nextKeyframe))) { |
2694 | 0 | videoData->SetNextKeyFrameTime(nextKeyframe); |
2695 | 0 | } |
2696 | 0 |
|
2697 | 0 | mVideo.ResolvePromise(videoData, __func__); |
2698 | 0 | } |
2699 | 0 | } |
2700 | | |
2701 | | size_t |
2702 | | MediaFormatReader::SizeOfVideoQueueInFrames() |
2703 | 0 | { |
2704 | 0 | return SizeOfQueue(TrackInfo::kVideoTrack); |
2705 | 0 | } |
2706 | | |
2707 | | size_t |
2708 | | MediaFormatReader::SizeOfAudioQueueInFrames() |
2709 | 0 | { |
2710 | 0 | return SizeOfQueue(TrackInfo::kAudioTrack); |
2711 | 0 | } |
2712 | | |
2713 | | size_t |
2714 | | MediaFormatReader::SizeOfQueue(TrackType aTrack) |
2715 | 0 | { |
2716 | 0 | auto& decoder = GetDecoderData(aTrack); |
2717 | 0 | return decoder.mSizeOfQueue; |
2718 | 0 | } |
2719 | | |
2720 | | RefPtr<MediaFormatReader::WaitForDataPromise> |
2721 | | MediaFormatReader::WaitForData(MediaData::Type aType) |
2722 | 0 | { |
2723 | 0 | MOZ_ASSERT(OnTaskQueue()); |
2724 | 0 | TrackType trackType = aType == MediaData::VIDEO_DATA ? |
2725 | 0 | TrackType::kVideoTrack : TrackType::kAudioTrack; |
2726 | 0 | auto& decoder = GetDecoderData(trackType); |
2727 | 0 | if (!decoder.IsWaitingForData() && !decoder.IsWaitingForKey()) { |
2728 | 0 | // We aren't waiting for anything. |
2729 | 0 | return WaitForDataPromise::CreateAndResolve(decoder.mType, __func__); |
2730 | 0 | } |
2731 | 0 | RefPtr<WaitForDataPromise> p = decoder.mWaitingPromise.Ensure(__func__); |
2732 | 0 | ScheduleUpdate(trackType); |
2733 | 0 | return p; |
2734 | 0 | } |
2735 | | |
2736 | | nsresult |
2737 | | MediaFormatReader::ResetDecode(TrackSet aTracks) |
2738 | 0 | { |
2739 | 0 | MOZ_ASSERT(OnTaskQueue()); |
2740 | 0 | LOGV(""); |
2741 | 0 |
|
2742 | 0 | mSeekPromise.RejectIfExists(NS_OK, __func__); |
2743 | 0 | mSkipRequest.DisconnectIfExists(); |
2744 | 0 |
|
2745 | 0 | // Do the same for any data wait promises. |
2746 | 0 | if (aTracks.contains(TrackInfo::kAudioTrack)) { |
2747 | 0 | mAudio.mWaitingPromise.RejectIfExists( |
2748 | 0 | WaitForDataRejectValue(MediaData::AUDIO_DATA, |
2749 | 0 | WaitForDataRejectValue::CANCELED), __func__); |
2750 | 0 | } |
2751 | 0 |
|
2752 | 0 | if (aTracks.contains(TrackInfo::kVideoTrack)) { |
2753 | 0 | mVideo.mWaitingPromise.RejectIfExists( |
2754 | 0 | WaitForDataRejectValue(MediaData::VIDEO_DATA, |
2755 | 0 | WaitForDataRejectValue::CANCELED), __func__); |
2756 | 0 | } |
2757 | 0 |
|
2758 | 0 | // Reset miscellaneous seeking state. |
2759 | 0 | mPendingSeekTime.reset(); |
2760 | 0 |
|
2761 | 0 | if (HasVideo() && aTracks.contains(TrackInfo::kVideoTrack)) { |
2762 | 0 | mVideo.ResetDemuxer(); |
2763 | 0 | mVideo.mFirstFrameTime = Some(media::TimeUnit::Zero()); |
2764 | 0 | Reset(TrackInfo::kVideoTrack); |
2765 | 0 | if (mVideo.HasPromise()) { |
2766 | 0 | mVideo.RejectPromise(NS_ERROR_DOM_MEDIA_CANCELED, __func__); |
2767 | 0 | } |
2768 | 0 | } |
2769 | 0 |
|
2770 | 0 | if (HasAudio() && aTracks.contains(TrackInfo::kAudioTrack)) { |
2771 | 0 | mAudio.ResetDemuxer(); |
2772 | 0 | mVideo.mFirstFrameTime = Some(media::TimeUnit::Zero()); |
2773 | 0 | Reset(TrackInfo::kAudioTrack); |
2774 | 0 | if (mAudio.HasPromise()) { |
2775 | 0 | mAudio.RejectPromise(NS_ERROR_DOM_MEDIA_CANCELED, __func__); |
2776 | 0 | } |
2777 | 0 | } |
2778 | 0 |
|
2779 | 0 | return NS_OK; |
2780 | 0 | } |
2781 | | |
2782 | | void |
2783 | | MediaFormatReader::Reset(TrackType aTrack) |
2784 | 0 | { |
2785 | 0 | MOZ_ASSERT(OnTaskQueue()); |
2786 | 0 | LOG("Reset(%s) BEGIN", TrackTypeToStr(aTrack)); |
2787 | 0 |
|
2788 | 0 | auto& decoder = GetDecoderData(aTrack); |
2789 | 0 |
|
2790 | 0 | decoder.ResetState(); |
2791 | 0 | decoder.Flush(); |
2792 | 0 |
|
2793 | 0 | LOG("Reset(%s) END", TrackTypeToStr(aTrack)); |
2794 | 0 | } |
2795 | | |
2796 | | void |
2797 | | MediaFormatReader::DropDecodedSamples(TrackType aTrack) |
2798 | 0 | { |
2799 | 0 | MOZ_ASSERT(OnTaskQueue()); |
2800 | 0 | auto& decoder = GetDecoderData(aTrack); |
2801 | 0 | size_t lengthDecodedQueue = decoder.mOutput.Length(); |
2802 | 0 | if (lengthDecodedQueue && decoder.mTimeThreshold.isSome()) { |
2803 | 0 | auto time = decoder.mOutput.LastElement()->mTime; |
2804 | 0 | if (time >= decoder.mTimeThreshold.ref().Time()) { |
2805 | 0 | // We would have reached our internal seek target. |
2806 | 0 | decoder.mTimeThreshold.reset(); |
2807 | 0 | } |
2808 | 0 | } |
2809 | 0 | decoder.mOutput.Clear(); |
2810 | 0 | decoder.mSizeOfQueue -= lengthDecodedQueue; |
2811 | 0 | if (aTrack == TrackInfo::kVideoTrack && mFrameStats) { |
2812 | 0 | mFrameStats->Accumulate({ 0, 0, lengthDecodedQueue, 0 }); |
2813 | 0 | } |
2814 | 0 | } |
2815 | | |
2816 | | void |
2817 | | MediaFormatReader::SkipVideoDemuxToNextKeyFrame(TimeUnit aTimeThreshold) |
2818 | 0 | { |
2819 | 0 | MOZ_ASSERT(OnTaskQueue()); |
2820 | 0 | LOG("Skipping up to %" PRId64, aTimeThreshold.ToMicroseconds()); |
2821 | 0 |
|
2822 | 0 | // We've reached SkipVideoDemuxToNextKeyFrame when our decoding is late. |
2823 | 0 | // As such we can drop all already decoded samples and discard all pending |
2824 | 0 | // samples. |
2825 | 0 | DropDecodedSamples(TrackInfo::kVideoTrack); |
2826 | 0 |
|
2827 | 0 | mVideo.mTrackDemuxer->SkipToNextRandomAccessPoint(aTimeThreshold) |
2828 | 0 | ->Then(OwnerThread(), __func__, this, |
2829 | 0 | &MediaFormatReader::OnVideoSkipCompleted, |
2830 | 0 | &MediaFormatReader::OnVideoSkipFailed) |
2831 | 0 | ->Track(mSkipRequest); |
2832 | 0 | } |
2833 | | |
2834 | | void |
2835 | | MediaFormatReader::VideoSkipReset(uint32_t aSkipped) |
2836 | 0 | { |
2837 | 0 | MOZ_ASSERT(OnTaskQueue()); |
2838 | 0 |
|
2839 | 0 | // Some frames may have been output by the decoder since we initiated the |
2840 | 0 | // videoskip process and we know they would be late. |
2841 | 0 | DropDecodedSamples(TrackInfo::kVideoTrack); |
2842 | 0 | // Report the pending frames as dropped. |
2843 | 0 | if (mFrameStats) { |
2844 | 0 | mFrameStats->Accumulate({ 0, 0, SizeOfVideoQueueInFrames(), 0 }); |
2845 | 0 | } |
2846 | 0 |
|
2847 | 0 | // Cancel any pending demux request and pending demuxed samples. |
2848 | 0 | mVideo.mDemuxRequest.DisconnectIfExists(); |
2849 | 0 | Reset(TrackType::kVideoTrack); |
2850 | 0 |
|
2851 | 0 | if (mFrameStats) { |
2852 | 0 | mFrameStats->Accumulate({ aSkipped, 0, aSkipped, 0 }); |
2853 | 0 | } |
2854 | 0 |
|
2855 | 0 | mVideo.mNumSamplesSkippedTotal += aSkipped; |
2856 | 0 | } |
2857 | | |
2858 | | void |
2859 | | MediaFormatReader::OnVideoSkipCompleted(uint32_t aSkipped) |
2860 | 0 | { |
2861 | 0 | MOZ_ASSERT(OnTaskQueue()); |
2862 | 0 | LOG("Skipping succeeded, skipped %u frames", aSkipped); |
2863 | 0 | mSkipRequest.Complete(); |
2864 | 0 |
|
2865 | 0 | DDLOG(DDLogCategory::Log, "video_skipped", DDNoValue()); |
2866 | 0 |
|
2867 | 0 | VideoSkipReset(aSkipped); |
2868 | 0 |
|
2869 | 0 | ScheduleUpdate(TrackInfo::kVideoTrack); |
2870 | 0 | } |
2871 | | |
2872 | | void |
2873 | | MediaFormatReader::OnVideoSkipFailed( |
2874 | | MediaTrackDemuxer::SkipFailureHolder aFailure) |
2875 | 0 | { |
2876 | 0 | MOZ_ASSERT(OnTaskQueue()); |
2877 | 0 | LOG("Skipping failed, skipped %u frames", aFailure.mSkipped); |
2878 | 0 | mSkipRequest.Complete(); |
2879 | 0 |
|
2880 | 0 | switch (aFailure.mFailure.Code()) { |
2881 | 0 | case NS_ERROR_DOM_MEDIA_END_OF_STREAM: |
2882 | 0 | case NS_ERROR_DOM_MEDIA_WAITING_FOR_DATA: |
2883 | 0 | DDLOG( |
2884 | 0 | DDLogCategory::Log, "video_skipping_interruption", aFailure.mFailure); |
2885 | 0 | // Some frames may have been output by the decoder since we initiated the |
2886 | 0 | // videoskip process and we know they would be late. |
2887 | 0 | DropDecodedSamples(TrackInfo::kVideoTrack); |
2888 | 0 | // We can't complete the skip operation, will just service a video frame |
2889 | 0 | // normally. |
2890 | 0 | ScheduleUpdate(TrackInfo::kVideoTrack); |
2891 | 0 | break; |
2892 | 0 | case NS_ERROR_DOM_MEDIA_CANCELED: |
2893 | 0 | DDLOG( |
2894 | 0 | DDLogCategory::Log, "video_skipping_interruption", aFailure.mFailure); |
2895 | 0 | if (mVideo.HasPromise()) { |
2896 | 0 | mVideo.RejectPromise(aFailure.mFailure, __func__); |
2897 | 0 | } |
2898 | 0 | break; |
2899 | 0 | default: |
2900 | 0 | DDLOG(DDLogCategory::Log, "video_skipping_error", aFailure.mFailure); |
2901 | 0 | NotifyError(TrackType::kVideoTrack, aFailure.mFailure); |
2902 | 0 | break; |
2903 | 0 | } |
2904 | 0 | } |
2905 | | |
2906 | | RefPtr<MediaFormatReader::SeekPromise> |
2907 | | MediaFormatReader::Seek(const SeekTarget& aTarget) |
2908 | 0 | { |
2909 | 0 | MOZ_ASSERT(OnTaskQueue()); |
2910 | 0 |
|
2911 | 0 | LOG("aTarget=(%" PRId64 ")", aTarget.GetTime().ToMicroseconds()); |
2912 | 0 |
|
2913 | 0 | MOZ_DIAGNOSTIC_ASSERT(mSeekPromise.IsEmpty()); |
2914 | 0 | MOZ_DIAGNOSTIC_ASSERT(!mVideo.HasPromise()); |
2915 | 0 | MOZ_DIAGNOSTIC_ASSERT(aTarget.IsVideoOnly() || !mAudio.HasPromise()); |
2916 | 0 | MOZ_DIAGNOSTIC_ASSERT(mPendingSeekTime.isNothing()); |
2917 | 0 | MOZ_DIAGNOSTIC_ASSERT(mVideo.mTimeThreshold.isNothing()); |
2918 | 0 | MOZ_DIAGNOSTIC_ASSERT(aTarget.IsVideoOnly() || |
2919 | 0 | mAudio.mTimeThreshold.isNothing()); |
2920 | 0 |
|
2921 | 0 | if (!mInfo.mMediaSeekable && !mInfo.mMediaSeekableOnlyInBufferedRanges) { |
2922 | 0 | LOG("Seek() END (Unseekable)"); |
2923 | 0 | return SeekPromise::CreateAndReject(NS_ERROR_FAILURE, __func__); |
2924 | 0 | } |
2925 | 0 |
|
2926 | 0 | if (mShutdown) { |
2927 | 0 | return SeekPromise::CreateAndReject(NS_ERROR_FAILURE, __func__); |
2928 | 0 | } |
2929 | 0 | |
2930 | 0 | SetSeekTarget(aTarget); |
2931 | 0 |
|
2932 | 0 | RefPtr<SeekPromise> p = mSeekPromise.Ensure(__func__); |
2933 | 0 |
|
2934 | 0 | ScheduleSeek(); |
2935 | 0 |
|
2936 | 0 | return p; |
2937 | 0 | } |
2938 | | |
2939 | | void |
2940 | | MediaFormatReader::SetSeekTarget(const SeekTarget& aTarget) |
2941 | 0 | { |
2942 | 0 | MOZ_ASSERT(OnTaskQueue()); |
2943 | 0 |
|
2944 | 0 | mOriginalSeekTarget = aTarget; |
2945 | 0 | mFallbackSeekTime = mPendingSeekTime = Some(aTarget.GetTime()); |
2946 | 0 | } |
2947 | | |
2948 | | void |
2949 | | MediaFormatReader::ScheduleSeek() |
2950 | 0 | { |
2951 | 0 | if (mSeekScheduled) { |
2952 | 0 | return; |
2953 | 0 | } |
2954 | 0 | mSeekScheduled = true; |
2955 | 0 | nsresult rv = |
2956 | 0 | OwnerThread()->Dispatch(NewRunnableMethod( |
2957 | 0 | "MediaFormatReader::AttemptSeek", this, &MediaFormatReader::AttemptSeek)); |
2958 | 0 | MOZ_DIAGNOSTIC_ASSERT(NS_SUCCEEDED(rv)); |
2959 | 0 | Unused << rv; |
2960 | 0 | } |
2961 | | |
2962 | | void |
2963 | | MediaFormatReader::AttemptSeek() |
2964 | 0 | { |
2965 | 0 | MOZ_ASSERT(OnTaskQueue()); |
2966 | 0 |
|
2967 | 0 | mSeekScheduled = false; |
2968 | 0 |
|
2969 | 0 | if (mPendingSeekTime.isNothing()) { |
2970 | 0 | return; |
2971 | 0 | } |
2972 | 0 | |
2973 | 0 | if (HasVideo()) { |
2974 | 0 | mVideo.ResetDemuxer(); |
2975 | 0 | mVideo.ResetState(); |
2976 | 0 | } |
2977 | 0 |
|
2978 | 0 | // Don't reset the audio demuxer not state when seeking video only |
2979 | 0 | // as it will cause the audio to seek back to the beginning |
2980 | 0 | // resulting in out-of-sync audio from video. |
2981 | 0 | if (HasAudio() && !mOriginalSeekTarget.IsVideoOnly()) { |
2982 | 0 | mAudio.ResetDemuxer(); |
2983 | 0 | mAudio.ResetState(); |
2984 | 0 | } |
2985 | 0 |
|
2986 | 0 | if (HasVideo()) { |
2987 | 0 | DoVideoSeek(); |
2988 | 0 | } else if (HasAudio()) { |
2989 | 0 | DoAudioSeek(); |
2990 | 0 | } else { |
2991 | 0 | MOZ_CRASH(); |
2992 | 0 | } |
2993 | 0 | } |
2994 | | |
2995 | | void |
2996 | | MediaFormatReader::OnSeekFailed(TrackType aTrack, const MediaResult& aError) |
2997 | 0 | { |
2998 | 0 | MOZ_ASSERT(OnTaskQueue()); |
2999 | 0 | LOGV("%s failure:%s", TrackTypeToStr(aTrack), aError.ErrorName().get()); |
3000 | 0 | if (aTrack == TrackType::kVideoTrack) { |
3001 | 0 | mVideo.mSeekRequest.Complete(); |
3002 | 0 | } else { |
3003 | 0 | mAudio.mSeekRequest.Complete(); |
3004 | 0 | } |
3005 | 0 |
|
3006 | 0 | if (aError == NS_ERROR_DOM_MEDIA_WAITING_FOR_DATA) { |
3007 | 0 | if (HasVideo() && |
3008 | 0 | aTrack == TrackType::kAudioTrack && |
3009 | 0 | mFallbackSeekTime.isSome() && |
3010 | 0 | mPendingSeekTime.ref() != mFallbackSeekTime.ref()) { |
3011 | 0 | // We have failed to seek audio where video seeked to earlier. |
3012 | 0 | // Attempt to seek instead to the closest point that we know we have in |
3013 | 0 | // order to limit A/V sync discrepency. |
3014 | 0 |
|
3015 | 0 | // Ensure we have the most up to date buffered ranges. |
3016 | 0 | UpdateReceivedNewData(TrackType::kAudioTrack); |
3017 | 0 | Maybe<TimeUnit> nextSeekTime; |
3018 | 0 | // Find closest buffered time found after video seeked time. |
3019 | 0 | for (const auto& timeRange : mAudio.mTimeRanges) { |
3020 | 0 | if (timeRange.mStart >= mPendingSeekTime.ref()) { |
3021 | 0 | nextSeekTime.emplace(timeRange.mStart); |
3022 | 0 | break; |
3023 | 0 | } |
3024 | 0 | } |
3025 | 0 | if (nextSeekTime.isNothing() || |
3026 | 0 | nextSeekTime.ref() > mFallbackSeekTime.ref()) { |
3027 | 0 | nextSeekTime = Some(mFallbackSeekTime.ref()); |
3028 | 0 | LOG("Unable to seek audio to video seek time. A/V sync may be broken"); |
3029 | 0 | } else { |
3030 | 0 | mFallbackSeekTime.reset(); |
3031 | 0 | } |
3032 | 0 | mPendingSeekTime = nextSeekTime; |
3033 | 0 | DoAudioSeek(); |
3034 | 0 | return; |
3035 | 0 | } |
3036 | 0 | NotifyWaitingForData(aTrack); |
3037 | 0 | } |
3038 | 0 | MOZ_ASSERT(!mVideo.mSeekRequest.Exists() && !mAudio.mSeekRequest.Exists()); |
3039 | 0 | mPendingSeekTime.reset(); |
3040 | 0 |
|
3041 | 0 | auto type = aTrack == TrackType::kAudioTrack ? MediaData::AUDIO_DATA |
3042 | 0 | : MediaData::VIDEO_DATA; |
3043 | 0 | mSeekPromise.Reject(SeekRejectValue(type, aError), __func__); |
3044 | 0 | } |
3045 | | |
3046 | | void |
3047 | | MediaFormatReader::DoVideoSeek() |
3048 | 0 | { |
3049 | 0 | MOZ_ASSERT(mPendingSeekTime.isSome()); |
3050 | 0 | LOGV("Seeking video to %" PRId64, mPendingSeekTime.ref().ToMicroseconds()); |
3051 | 0 | auto seekTime = mPendingSeekTime.ref(); |
3052 | 0 | mVideo.mTrackDemuxer->Seek(seekTime) |
3053 | 0 | ->Then(OwnerThread(), __func__, this, |
3054 | 0 | &MediaFormatReader::OnVideoSeekCompleted, |
3055 | 0 | &MediaFormatReader::OnVideoSeekFailed) |
3056 | 0 | ->Track(mVideo.mSeekRequest); |
3057 | 0 | } |
3058 | | |
3059 | | void |
3060 | | MediaFormatReader::OnVideoSeekCompleted(TimeUnit aTime) |
3061 | 0 | { |
3062 | 0 | MOZ_ASSERT(OnTaskQueue()); |
3063 | 0 | LOGV("Video seeked to %" PRId64, aTime.ToMicroseconds()); |
3064 | 0 | mVideo.mSeekRequest.Complete(); |
3065 | 0 |
|
3066 | 0 | mVideo.mFirstFrameTime = Some(aTime); |
3067 | 0 | mPreviousDecodedKeyframeTime_us = sNoPreviousDecodedKeyframe; |
3068 | 0 |
|
3069 | 0 | SetVideoDecodeThreshold(); |
3070 | 0 |
|
3071 | 0 | if (HasAudio() && !mOriginalSeekTarget.IsVideoOnly()) { |
3072 | 0 | MOZ_ASSERT(mPendingSeekTime.isSome()); |
3073 | 0 | if (mOriginalSeekTarget.IsFast()) { |
3074 | 0 | // We are performing a fast seek. We need to seek audio to where the |
3075 | 0 | // video seeked to, to ensure proper A/V sync once playback resume. |
3076 | 0 | mPendingSeekTime = Some(aTime); |
3077 | 0 | } |
3078 | 0 | DoAudioSeek(); |
3079 | 0 | } else { |
3080 | 0 | mPendingSeekTime.reset(); |
3081 | 0 | mSeekPromise.Resolve(aTime, __func__); |
3082 | 0 | } |
3083 | 0 | } |
3084 | | |
3085 | | void |
3086 | | MediaFormatReader::OnVideoSeekFailed(const MediaResult& aError) |
3087 | 0 | { |
3088 | 0 | mPreviousDecodedKeyframeTime_us = sNoPreviousDecodedKeyframe; |
3089 | 0 | OnSeekFailed(TrackType::kVideoTrack, aError); |
3090 | 0 | } |
3091 | | |
3092 | | void |
3093 | | MediaFormatReader::SetVideoDecodeThreshold() |
3094 | 0 | { |
3095 | 0 | MOZ_ASSERT(OnTaskQueue()); |
3096 | 0 |
|
3097 | 0 | if (!HasVideo() || !mVideo.mDecoder) { |
3098 | 0 | return; |
3099 | 0 | } |
3100 | 0 | |
3101 | 0 | if (!mVideo.mTimeThreshold && !IsSeeking()) { |
3102 | 0 | return; |
3103 | 0 | } |
3104 | 0 | |
3105 | 0 | TimeUnit threshold; |
3106 | 0 | if (mVideo.mTimeThreshold) { |
3107 | 0 | // For internalSeek. |
3108 | 0 | threshold = mVideo.mTimeThreshold.ref().Time(); |
3109 | 0 | } else if (IsSeeking()) { |
3110 | 0 | // If IsSeeking() is true, then video seek must have completed already. |
3111 | 0 | TimeUnit keyframe; |
3112 | 0 | if (NS_FAILED(mVideo.mTrackDemuxer->GetNextRandomAccessPoint(&keyframe))) { |
3113 | 0 | return; |
3114 | 0 | } |
3115 | 0 | |
3116 | 0 | // If the key frame is invalid/infinite, it means the target position is |
3117 | 0 | // closing to end of stream. We don't want to skip any frame at this point. |
3118 | 0 | if (!keyframe.IsValid() || keyframe.IsInfinite()) { |
3119 | 0 | return; |
3120 | 0 | } |
3121 | 0 | threshold = mOriginalSeekTarget.GetTime(); |
3122 | 0 | } else { |
3123 | 0 | return; |
3124 | 0 | } |
3125 | 0 | |
3126 | 0 | LOG("Set seek threshold to %" PRId64, threshold.ToMicroseconds()); |
3127 | 0 | mVideo.mDecoder->SetSeekThreshold(threshold); |
3128 | 0 | } |
3129 | | |
3130 | | void |
3131 | | MediaFormatReader::DoAudioSeek() |
3132 | 0 | { |
3133 | 0 | MOZ_ASSERT(mPendingSeekTime.isSome()); |
3134 | 0 | LOGV("Seeking audio to %" PRId64, mPendingSeekTime.ref().ToMicroseconds()); |
3135 | 0 | auto seekTime = mPendingSeekTime.ref(); |
3136 | 0 | mAudio.mTrackDemuxer->Seek(seekTime) |
3137 | 0 | ->Then(OwnerThread(), __func__, this, |
3138 | 0 | &MediaFormatReader::OnAudioSeekCompleted, |
3139 | 0 | &MediaFormatReader::OnAudioSeekFailed) |
3140 | 0 | ->Track(mAudio.mSeekRequest); |
3141 | 0 | } |
3142 | | |
3143 | | void |
3144 | | MediaFormatReader::OnAudioSeekCompleted(TimeUnit aTime) |
3145 | 0 | { |
3146 | 0 | MOZ_ASSERT(OnTaskQueue()); |
3147 | 0 | LOGV("Audio seeked to %" PRId64, aTime.ToMicroseconds()); |
3148 | 0 | mAudio.mSeekRequest.Complete(); |
3149 | 0 | mAudio.mFirstFrameTime = Some(aTime); |
3150 | 0 | mPendingSeekTime.reset(); |
3151 | 0 | mSeekPromise.Resolve(aTime, __func__); |
3152 | 0 | } |
3153 | | |
3154 | | void |
3155 | | MediaFormatReader::OnAudioSeekFailed(const MediaResult& aError) |
3156 | 0 | { |
3157 | 0 | OnSeekFailed(TrackType::kAudioTrack, aError); |
3158 | 0 | } |
3159 | | |
3160 | | void MediaFormatReader::ReleaseResources() |
3161 | 0 | { |
3162 | 0 | LOGV(""); |
3163 | 0 | if (mShutdown) { |
3164 | 0 | return; |
3165 | 0 | } |
3166 | 0 | ShutdownDecoder(TrackInfo::kAudioTrack); |
3167 | 0 | ShutdownDecoder(TrackInfo::kVideoTrack); |
3168 | 0 | } |
3169 | | |
3170 | | bool |
3171 | | MediaFormatReader::VideoIsHardwareAccelerated() const |
3172 | 0 | { |
3173 | 0 | return mVideo.mIsHardwareAccelerated; |
3174 | 0 | } |
3175 | | |
3176 | | void |
3177 | | MediaFormatReader::NotifyTrackDemuxers() |
3178 | 0 | { |
3179 | 0 | MOZ_ASSERT(OnTaskQueue()); |
3180 | 0 |
|
3181 | 0 | LOGV(""); |
3182 | 0 |
|
3183 | 0 | if (!mInitDone) { |
3184 | 0 | return; |
3185 | 0 | } |
3186 | 0 | |
3187 | 0 | if (HasVideo()) { |
3188 | 0 | mVideo.mReceivedNewData = true; |
3189 | 0 | ScheduleUpdate(TrackType::kVideoTrack); |
3190 | 0 | } |
3191 | 0 | if (HasAudio()) { |
3192 | 0 | mAudio.mReceivedNewData = true; |
3193 | 0 | ScheduleUpdate(TrackType::kAudioTrack); |
3194 | 0 | } |
3195 | 0 | } |
3196 | | |
3197 | | void |
3198 | | MediaFormatReader::NotifyDataArrived() |
3199 | 0 | { |
3200 | 0 | MOZ_ASSERT(OnTaskQueue()); |
3201 | 0 |
|
3202 | 0 | if (mShutdown || !mDemuxer || !mDemuxerInitDone) { |
3203 | 0 | return; |
3204 | 0 | } |
3205 | 0 | |
3206 | 0 | if (mNotifyDataArrivedPromise.Exists()) { |
3207 | 0 | // Already one in progress. Set the dirty flag so we can process it later. |
3208 | 0 | mPendingNotifyDataArrived = true; |
3209 | 0 | return; |
3210 | 0 | } |
3211 | 0 | |
3212 | 0 | RefPtr<MediaFormatReader> self = this; |
3213 | 0 | mDemuxer->NotifyDataArrived() |
3214 | 0 | ->Then(OwnerThread(), __func__, |
3215 | 0 | [self]() { |
3216 | 0 | self->mNotifyDataArrivedPromise.Complete(); |
3217 | 0 | self->UpdateBuffered(); |
3218 | 0 | self->NotifyTrackDemuxers(); |
3219 | 0 | if (self->mPendingNotifyDataArrived) { |
3220 | 0 | self->mPendingNotifyDataArrived = false; |
3221 | 0 | self->NotifyDataArrived(); |
3222 | 0 | } |
3223 | 0 | }, |
3224 | 0 | [self]() { self->mNotifyDataArrivedPromise.Complete(); }) |
3225 | 0 | ->Track(mNotifyDataArrivedPromise); |
3226 | 0 | } |
3227 | | |
3228 | | void |
3229 | | MediaFormatReader::UpdateBuffered() |
3230 | 0 | { |
3231 | 0 | MOZ_ASSERT(OnTaskQueue()); |
3232 | 0 |
|
3233 | 0 | if (mShutdown) { |
3234 | 0 | return; |
3235 | 0 | } |
3236 | 0 | |
3237 | 0 | if (!mInitDone || !mHasStartTime) { |
3238 | 0 | mBuffered = TimeIntervals(); |
3239 | 0 | return; |
3240 | 0 | } |
3241 | 0 | |
3242 | 0 | if (HasVideo()) { |
3243 | 0 | mVideo.mTimeRanges = mVideo.mTrackDemuxer->GetBuffered(); |
3244 | 0 | bool hasLastEnd; |
3245 | 0 | auto lastEnd = mVideo.mTimeRanges.GetEnd(&hasLastEnd); |
3246 | 0 | if (hasLastEnd) { |
3247 | 0 | if (mVideo.mLastTimeRangesEnd && |
3248 | 0 | mVideo.mLastTimeRangesEnd.ref() < lastEnd) { |
3249 | 0 | // New data was added after our previous end, we can clear the EOS flag. |
3250 | 0 | mVideo.mDemuxEOS = false; |
3251 | 0 | ScheduleUpdate(TrackInfo::kVideoTrack); |
3252 | 0 | } |
3253 | 0 | mVideo.mLastTimeRangesEnd = Some(lastEnd); |
3254 | 0 | } |
3255 | 0 | } |
3256 | 0 | if (HasAudio()) { |
3257 | 0 | mAudio.mTimeRanges = mAudio.mTrackDemuxer->GetBuffered(); |
3258 | 0 | bool hasLastEnd; |
3259 | 0 | auto lastEnd = mAudio.mTimeRanges.GetEnd(&hasLastEnd); |
3260 | 0 | if (hasLastEnd) { |
3261 | 0 | if (mAudio.mLastTimeRangesEnd && |
3262 | 0 | mAudio.mLastTimeRangesEnd.ref() < lastEnd) { |
3263 | 0 | // New data was added after our previous end, we can clear the EOS flag. |
3264 | 0 | mAudio.mDemuxEOS = false; |
3265 | 0 | ScheduleUpdate(TrackInfo::kAudioTrack); |
3266 | 0 | } |
3267 | 0 | mAudio.mLastTimeRangesEnd = Some(lastEnd); |
3268 | 0 | } |
3269 | 0 | } |
3270 | 0 |
|
3271 | 0 | media::TimeIntervals intervals; |
3272 | 0 | if (HasAudio() && HasVideo()) { |
3273 | 0 | intervals = media::Intersection(mVideo.mTimeRanges, mAudio.mTimeRanges); |
3274 | 0 | } else if (HasAudio()) { |
3275 | 0 | intervals = mAudio.mTimeRanges; |
3276 | 0 | } else if (HasVideo()) { |
3277 | 0 | intervals = mVideo.mTimeRanges; |
3278 | 0 | } |
3279 | 0 |
|
3280 | 0 | if (!intervals.Length() || intervals.GetStart() == TimeUnit::Zero()) { |
3281 | 0 | // IntervalSet already starts at 0 or is empty, nothing to shift. |
3282 | 0 | mBuffered = intervals; |
3283 | 0 | } else { |
3284 | 0 | mBuffered = |
3285 | 0 | intervals.Shift(TimeUnit::Zero() - mInfo.mStartTime); |
3286 | 0 | } |
3287 | 0 | } |
3288 | | |
3289 | | layers::ImageContainer* |
3290 | | MediaFormatReader::GetImageContainer() |
3291 | 0 | { |
3292 | 0 | return mVideoFrameContainer ? mVideoFrameContainer->GetImageContainer() |
3293 | 0 | : nullptr; |
3294 | 0 | } |
3295 | | |
3296 | | void |
3297 | | MediaFormatReader::GetMozDebugReaderData(nsACString& aString) |
3298 | 0 | { |
3299 | 0 | nsCString result; |
3300 | 0 | nsAutoCString audioDecoderName("unavailable"); |
3301 | 0 | nsAutoCString videoDecoderName = audioDecoderName; |
3302 | 0 | nsAutoCString audioType("none"); |
3303 | 0 | nsAutoCString videoType("none"); |
3304 | 0 |
|
3305 | 0 | AudioInfo audioInfo = mAudio.GetCurrentInfo() |
3306 | 0 | ? *mAudio.GetCurrentInfo()->GetAsAudioInfo() |
3307 | 0 | : AudioInfo(); |
3308 | 0 | if (HasAudio()) |
3309 | 0 | { |
3310 | 0 | MutexAutoLock lock(mAudio.mMutex); |
3311 | 0 | audioDecoderName = mAudio.mDecoder |
3312 | 0 | ? mAudio.mDecoder->GetDescriptionName() |
3313 | 0 | : mAudio.mDescription; |
3314 | 0 | audioType = audioInfo.mMimeType; |
3315 | 0 | } |
3316 | 0 | VideoInfo videoInfo = mVideo.GetCurrentInfo() |
3317 | 0 | ? *mVideo.GetCurrentInfo()->GetAsVideoInfo() |
3318 | 0 | : VideoInfo(); |
3319 | 0 | if (HasVideo()) { |
3320 | 0 | MutexAutoLock mon(mVideo.mMutex); |
3321 | 0 | videoDecoderName = mVideo.mDecoder |
3322 | 0 | ? mVideo.mDecoder->GetDescriptionName() |
3323 | 0 | : mVideo.mDescription; |
3324 | 0 | videoType = videoInfo.mMimeType; |
3325 | 0 | } |
3326 | 0 |
|
3327 | 0 | result += |
3328 | 0 | nsPrintfCString("Audio Decoder(%s, %u channels @ %0.1fkHz): %s\n", |
3329 | 0 | audioType.get(), |
3330 | 0 | audioInfo.mChannels, |
3331 | 0 | audioInfo.mRate / 1000.0f, |
3332 | 0 | audioDecoderName.get()); |
3333 | 0 | result += nsPrintfCString("Audio Frames Decoded: %" PRIu64 "\n", |
3334 | 0 | mAudio.mNumSamplesOutputTotal); |
3335 | 0 | if (HasAudio()) { |
3336 | 0 | result += nsPrintfCString( |
3337 | 0 | "Audio State: ni=%d no=%d wp=%d demuxr=%d demuxq=%u decoder=%d tt=%.1f " |
3338 | 0 | "tths=%d in=%" PRIu64 " out=%" PRIu64 |
3339 | 0 | " qs=%u pending=%u wfd=%d eos=%d ds=%d wfk=%d sid=%u\n", |
3340 | 0 | NeedInput(mAudio), |
3341 | 0 | mAudio.HasPromise(), |
3342 | 0 | !mAudio.mWaitingPromise.IsEmpty(), |
3343 | 0 | mAudio.mDemuxRequest.Exists(), |
3344 | 0 | uint32_t(mAudio.mQueuedSamples.Length()), |
3345 | 0 | mAudio.mDecodeRequest.Exists(), |
3346 | 0 | mAudio.mTimeThreshold ? mAudio.mTimeThreshold.ref().Time().ToSeconds() |
3347 | 0 | : -1.0, |
3348 | 0 | mAudio.mTimeThreshold ? mAudio.mTimeThreshold.ref().mHasSeeked : -1, |
3349 | 0 | mAudio.mNumSamplesInput, |
3350 | 0 | mAudio.mNumSamplesOutput, |
3351 | 0 | unsigned(size_t(mAudio.mSizeOfQueue)), |
3352 | 0 | unsigned(mAudio.mOutput.Length()), |
3353 | 0 | mAudio.mWaitingForData, |
3354 | 0 | mAudio.mDemuxEOS, |
3355 | 0 | int32_t(mAudio.mDrainState), |
3356 | 0 | mAudio.mWaitingForKey, |
3357 | 0 | mAudio.mLastStreamSourceID); |
3358 | 0 | } |
3359 | 0 |
|
3360 | 0 | result += nsPrintfCString( |
3361 | 0 | "Video Decoder(%s, %dx%d @ %0.2f): %s\n", |
3362 | 0 | videoType.get(), |
3363 | 0 | videoInfo.mDisplay.width < 0 ? 0 : videoInfo.mDisplay.width, |
3364 | 0 | videoInfo.mDisplay.height < 0 ? 0 : videoInfo.mDisplay.height, |
3365 | 0 | mVideo.mMeanRate.Mean(), |
3366 | 0 | videoDecoderName.get()); |
3367 | 0 |
|
3368 | 0 | result += |
3369 | 0 | nsPrintfCString("Hardware Video Decoding: %s\n", |
3370 | 0 | VideoIsHardwareAccelerated() ? "enabled" : "disabled"); |
3371 | 0 | result += |
3372 | 0 | nsPrintfCString("Video Frames Decoded: %" PRIu64 " (skipped=%" PRIu64 ")\n", |
3373 | 0 | mVideo.mNumSamplesOutputTotal, |
3374 | 0 | mVideo.mNumSamplesSkippedTotal); |
3375 | 0 | if (HasVideo()) { |
3376 | 0 | result += nsPrintfCString( |
3377 | 0 | "Video State: ni=%d no=%d wp=%d demuxr=%d demuxq=%u decoder=%d tt=%.1f " |
3378 | 0 | "tths=%d in=%" PRIu64 " out=%" PRIu64 |
3379 | 0 | " qs=%u pending:%u wfd=%d eos=%d ds=%d wfk=%d sid=%u\n", |
3380 | 0 | NeedInput(mVideo), |
3381 | 0 | mVideo.HasPromise(), |
3382 | 0 | !mVideo.mWaitingPromise.IsEmpty(), |
3383 | 0 | mVideo.mDemuxRequest.Exists(), |
3384 | 0 | uint32_t(mVideo.mQueuedSamples.Length()), |
3385 | 0 | mVideo.mDecodeRequest.Exists(), |
3386 | 0 | mVideo.mTimeThreshold ? mVideo.mTimeThreshold.ref().Time().ToSeconds() |
3387 | 0 | : -1.0, |
3388 | 0 | mVideo.mTimeThreshold ? mVideo.mTimeThreshold.ref().mHasSeeked : -1, |
3389 | 0 | mVideo.mNumSamplesInput, |
3390 | 0 | mVideo.mNumSamplesOutput, |
3391 | 0 | unsigned(size_t(mVideo.mSizeOfQueue)), |
3392 | 0 | unsigned(mVideo.mOutput.Length()), |
3393 | 0 | mVideo.mWaitingForData, |
3394 | 0 | mVideo.mDemuxEOS, |
3395 | 0 | int32_t(mVideo.mDrainState), |
3396 | 0 | mVideo.mWaitingForKey, |
3397 | 0 | mVideo.mLastStreamSourceID); |
3398 | 0 | } |
3399 | 0 | aString += result; |
3400 | 0 | } |
3401 | | |
3402 | | void |
3403 | | MediaFormatReader::SetVideoNullDecode(bool aIsNullDecode) |
3404 | 0 | { |
3405 | 0 | MOZ_ASSERT(OnTaskQueue()); |
3406 | 0 | return SetNullDecode(TrackType::kVideoTrack, aIsNullDecode); |
3407 | 0 | } |
3408 | | |
3409 | | void |
3410 | | MediaFormatReader::UpdateCompositor( |
3411 | | already_AddRefed<layers::KnowsCompositor> aCompositor) |
3412 | 0 | { |
3413 | 0 | MOZ_ASSERT(OnTaskQueue()); |
3414 | 0 | mKnowsCompositor = aCompositor; |
3415 | 0 | } |
3416 | | |
3417 | | void |
3418 | | MediaFormatReader::SetNullDecode(TrackType aTrack, bool aIsNullDecode) |
3419 | 0 | { |
3420 | 0 | MOZ_ASSERT(OnTaskQueue()); |
3421 | 0 |
|
3422 | 0 | auto& decoder = GetDecoderData(aTrack); |
3423 | 0 | if (decoder.mIsNullDecode == aIsNullDecode) { |
3424 | 0 | return; |
3425 | 0 | } |
3426 | 0 | |
3427 | 0 | LOG("%s, decoder.mIsNullDecode = %d => aIsNullDecode = %d", |
3428 | 0 | TrackTypeToStr(aTrack), decoder.mIsNullDecode, aIsNullDecode); |
3429 | 0 |
|
3430 | 0 | decoder.mIsNullDecode = aIsNullDecode; |
3431 | 0 | ShutdownDecoder(aTrack); |
3432 | 0 | } |
3433 | | |
3434 | | void |
3435 | | MediaFormatReader::OnFirstDemuxCompleted( |
3436 | | TrackInfo::TrackType aType, RefPtr<MediaTrackDemuxer::SamplesHolder> aSamples) |
3437 | 0 | { |
3438 | 0 | MOZ_ASSERT(OnTaskQueue()); |
3439 | 0 |
|
3440 | 0 | if (mShutdown) { |
3441 | 0 | return; |
3442 | 0 | } |
3443 | 0 | |
3444 | 0 | auto& decoder = GetDecoderData(aType); |
3445 | 0 | MOZ_ASSERT(decoder.mFirstDemuxedSampleTime.isNothing()); |
3446 | 0 | decoder.mFirstDemuxedSampleTime.emplace(aSamples->mSamples[0]->mTime); |
3447 | 0 | MaybeResolveMetadataPromise(); |
3448 | 0 | } |
3449 | | |
3450 | | void |
3451 | | MediaFormatReader::OnFirstDemuxFailed(TrackInfo::TrackType aType, |
3452 | | const MediaResult& aError) |
3453 | 0 | { |
3454 | 0 | MOZ_ASSERT(OnTaskQueue()); |
3455 | 0 |
|
3456 | 0 | if (mShutdown) { |
3457 | 0 | return; |
3458 | 0 | } |
3459 | 0 | |
3460 | 0 | auto& decoder = GetDecoderData(aType); |
3461 | 0 | MOZ_ASSERT(decoder.mFirstDemuxedSampleTime.isNothing()); |
3462 | 0 | decoder.mFirstDemuxedSampleTime.emplace(TimeUnit::FromInfinity()); |
3463 | 0 | MaybeResolveMetadataPromise(); |
3464 | 0 | } |
3465 | | |
3466 | | } // namespace mozilla |
3467 | | |
3468 | | #undef NS_DispatchToMainThread |