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 "ChannelMediaDecoder.h"
8 #include "ChannelMediaResource.h"
9 #include "DecoderTraits.h"
10 #include "MediaDecoderStateMachine.h"
11 #include "MediaFormatReader.h"
12 #include "BaseMediaResource.h"
13 #include "MediaShutdownManager.h"
14 #include "mozilla/Preferences.h"
15 #include "mozilla/StaticPrefs_media.h"
16 #include "VideoUtils.h"
17
18 namespace mozilla {
19
20 extern LazyLogModule gMediaDecoderLog;
21 #define LOG(x, ...) \
22 DDMOZ_LOG(gMediaDecoderLog, LogLevel::Debug, x, ##__VA_ARGS__)
23
ResourceCallback(AbstractThread * aMainThread)24 ChannelMediaDecoder::ResourceCallback::ResourceCallback(
25 AbstractThread* aMainThread)
26 : mAbstractMainThread(aMainThread) {
27 MOZ_ASSERT(aMainThread);
28 DecoderDoctorLogger::LogConstructionAndBase(
29 "ChannelMediaDecoder::ResourceCallback", this,
30 static_cast<const MediaResourceCallback*>(this));
31 }
32
~ResourceCallback()33 ChannelMediaDecoder::ResourceCallback::~ResourceCallback() {
34 DecoderDoctorLogger::LogDestruction("ChannelMediaDecoder::ResourceCallback",
35 this);
36 }
37
Connect(ChannelMediaDecoder * aDecoder)38 void ChannelMediaDecoder::ResourceCallback::Connect(
39 ChannelMediaDecoder* aDecoder) {
40 MOZ_ASSERT(NS_IsMainThread());
41 mDecoder = aDecoder;
42 DecoderDoctorLogger::LinkParentAndChild(
43 "ChannelMediaDecoder::ResourceCallback", this, "decoder", mDecoder);
44 mTimer = NS_NewTimer(mAbstractMainThread->AsEventTarget());
45 }
46
Disconnect()47 void ChannelMediaDecoder::ResourceCallback::Disconnect() {
48 MOZ_ASSERT(NS_IsMainThread());
49 if (mDecoder) {
50 DecoderDoctorLogger::UnlinkParentAndChild(
51 "ChannelMediaDecoder::ResourceCallback", this, mDecoder);
52 mDecoder = nullptr;
53 mTimer->Cancel();
54 mTimer = nullptr;
55 }
56 }
57
AbstractMainThread() const58 AbstractThread* ChannelMediaDecoder::ResourceCallback::AbstractMainThread()
59 const {
60 return mAbstractMainThread;
61 }
62
GetMediaOwner() const63 MediaDecoderOwner* ChannelMediaDecoder::ResourceCallback::GetMediaOwner()
64 const {
65 MOZ_ASSERT(NS_IsMainThread());
66 return mDecoder ? mDecoder->GetOwner() : nullptr;
67 }
68
NotifyNetworkError(const MediaResult & aError)69 void ChannelMediaDecoder::ResourceCallback::NotifyNetworkError(
70 const MediaResult& aError) {
71 MOZ_ASSERT(NS_IsMainThread());
72 DDLOGEX2("ChannelMediaDecoder::ResourceCallback", this, DDLogCategory::Log,
73 "network_error", aError);
74 if (mDecoder) {
75 mDecoder->NetworkError(aError);
76 }
77 }
78
79 /* static */
TimerCallback(nsITimer * aTimer,void * aClosure)80 void ChannelMediaDecoder::ResourceCallback::TimerCallback(nsITimer* aTimer,
81 void* aClosure) {
82 MOZ_ASSERT(NS_IsMainThread());
83 ResourceCallback* thiz = static_cast<ResourceCallback*>(aClosure);
84 MOZ_ASSERT(thiz->mDecoder);
85 thiz->mDecoder->NotifyReaderDataArrived();
86 thiz->mTimerArmed = false;
87 }
88
NotifyDataArrived()89 void ChannelMediaDecoder::ResourceCallback::NotifyDataArrived() {
90 MOZ_ASSERT(NS_IsMainThread());
91 DDLOGEX2("ChannelMediaDecoder::ResourceCallback", this, DDLogCategory::Log,
92 "data_arrived", true);
93
94 if (!mDecoder) {
95 return;
96 }
97
98 mDecoder->DownloadProgressed();
99
100 if (mTimerArmed) {
101 return;
102 }
103 // In situations where these notifications come from stochastic network
104 // activity, we can save significant computation by throttling the
105 // calls to MediaDecoder::NotifyDataArrived() which will update the buffer
106 // ranges of the reader.
107 mTimerArmed = true;
108 mTimer->InitWithNamedFuncCallback(
109 TimerCallback, this, sDelay, nsITimer::TYPE_ONE_SHOT,
110 "ChannelMediaDecoder::ResourceCallback::TimerCallback");
111 }
112
NotifyDataEnded(nsresult aStatus)113 void ChannelMediaDecoder::ResourceCallback::NotifyDataEnded(nsresult aStatus) {
114 DDLOGEX2("ChannelMediaDecoder::ResourceCallback", this, DDLogCategory::Log,
115 "data_ended", aStatus);
116 MOZ_ASSERT(NS_IsMainThread());
117 if (mDecoder) {
118 mDecoder->NotifyDownloadEnded(aStatus);
119 }
120 }
121
NotifyPrincipalChanged()122 void ChannelMediaDecoder::ResourceCallback::NotifyPrincipalChanged() {
123 MOZ_ASSERT(NS_IsMainThread());
124 DDLOGEX2("ChannelMediaDecoder::ResourceCallback", this, DDLogCategory::Log,
125 "principal_changed", true);
126 if (mDecoder) {
127 mDecoder->NotifyPrincipalChanged();
128 }
129 }
130
NotifyPrincipalChanged()131 void ChannelMediaDecoder::NotifyPrincipalChanged() {
132 MOZ_ASSERT(NS_IsMainThread());
133 MediaDecoder::NotifyPrincipalChanged();
134 if (!mInitialChannelPrincipalKnown) {
135 // We'll receive one notification when the channel's initial principal
136 // is known, after all HTTP redirects have resolved. This isn't really a
137 // principal change, so return here to avoid the mSameOriginMedia check
138 // below.
139 mInitialChannelPrincipalKnown = true;
140 return;
141 }
142 if (!mSameOriginMedia &&
143 Preferences::GetBool("media.block-midflight-redirects", true)) {
144 // Block mid-flight redirects to non CORS same origin destinations.
145 // See bugs 1441153, 1443942.
146 LOG("ChannnelMediaDecoder prohibited cross origin redirect blocked.");
147 NetworkError(MediaResult(NS_ERROR_DOM_BAD_URI,
148 "Prohibited cross origin redirect blocked"));
149 }
150 }
151
NotifySuspendedStatusChanged(bool aSuspendedByCache)152 void ChannelMediaDecoder::ResourceCallback::NotifySuspendedStatusChanged(
153 bool aSuspendedByCache) {
154 MOZ_ASSERT(NS_IsMainThread());
155 DDLOGEX2("ChannelMediaDecoder::ResourceCallback", this, DDLogCategory::Log,
156 "suspended_status_changed", aSuspendedByCache);
157 MediaDecoderOwner* owner = GetMediaOwner();
158 if (owner) {
159 owner->NotifySuspendedByCache(aSuspendedByCache);
160 }
161 }
162
ChannelMediaDecoder(MediaDecoderInit & aInit)163 ChannelMediaDecoder::ChannelMediaDecoder(MediaDecoderInit& aInit)
164 : MediaDecoder(aInit),
165 mResourceCallback(
166 new ResourceCallback(aInit.mOwner->AbstractMainThread())) {
167 mResourceCallback->Connect(this);
168 }
169
170 /* static */
Create(MediaDecoderInit & aInit,DecoderDoctorDiagnostics * aDiagnostics)171 already_AddRefed<ChannelMediaDecoder> ChannelMediaDecoder::Create(
172 MediaDecoderInit& aInit, DecoderDoctorDiagnostics* aDiagnostics) {
173 MOZ_ASSERT(NS_IsMainThread());
174 RefPtr<ChannelMediaDecoder> decoder;
175 if (DecoderTraits::CanHandleContainerType(aInit.mContainerType,
176 aDiagnostics) != CANPLAY_NO) {
177 decoder = new ChannelMediaDecoder(aInit);
178 return decoder.forget();
179 }
180
181 return nullptr;
182 }
183
CanClone()184 bool ChannelMediaDecoder::CanClone() {
185 MOZ_ASSERT(NS_IsMainThread());
186 return mResource && mResource->CanClone();
187 }
188
Clone(MediaDecoderInit & aInit)189 already_AddRefed<ChannelMediaDecoder> ChannelMediaDecoder::Clone(
190 MediaDecoderInit& aInit) {
191 if (!mResource || DecoderTraits::CanHandleContainerType(
192 aInit.mContainerType, nullptr) == CANPLAY_NO) {
193 return nullptr;
194 }
195 RefPtr<ChannelMediaDecoder> decoder = new ChannelMediaDecoder(aInit);
196 nsresult rv = decoder->Load(mResource);
197 if (NS_FAILED(rv)) {
198 decoder->Shutdown();
199 return nullptr;
200 }
201 return decoder.forget();
202 }
203
CreateStateMachine()204 MediaDecoderStateMachine* ChannelMediaDecoder::CreateStateMachine() {
205 MOZ_ASSERT(NS_IsMainThread());
206 MediaFormatReaderInit init;
207 init.mVideoFrameContainer = GetVideoFrameContainer();
208 init.mKnowsCompositor = GetCompositor();
209 init.mCrashHelper = GetOwner()->CreateGMPCrashHelper();
210 init.mFrameStats = mFrameStats;
211 init.mResource = mResource;
212 init.mMediaDecoderOwnerID = mOwner;
213 mReader = DecoderTraits::CreateReader(ContainerType(), init);
214 return new MediaDecoderStateMachine(this, mReader);
215 }
216
Shutdown()217 void ChannelMediaDecoder::Shutdown() {
218 mResourceCallback->Disconnect();
219 MediaDecoder::Shutdown();
220
221 if (mResource) {
222 // Force any outstanding seek and byterange requests to complete
223 // to prevent shutdown from deadlocking.
224 mResourceClosePromise = mResource->Close();
225 }
226 }
227
ShutdownInternal()228 void ChannelMediaDecoder::ShutdownInternal() {
229 if (!mResourceClosePromise) {
230 MediaShutdownManager::Instance().Unregister(this);
231 return;
232 }
233
234 mResourceClosePromise->Then(
235 AbstractMainThread(), __func__,
236 [self = RefPtr<ChannelMediaDecoder>(this)] {
237 MediaShutdownManager::Instance().Unregister(self);
238 });
239 }
240
Load(nsIChannel * aChannel,bool aIsPrivateBrowsing,nsIStreamListener ** aStreamListener)241 nsresult ChannelMediaDecoder::Load(nsIChannel* aChannel,
242 bool aIsPrivateBrowsing,
243 nsIStreamListener** aStreamListener) {
244 MOZ_ASSERT(NS_IsMainThread());
245 MOZ_ASSERT(!mResource);
246 MOZ_ASSERT(aStreamListener);
247
248 mResource = BaseMediaResource::Create(mResourceCallback, aChannel,
249 aIsPrivateBrowsing);
250 if (!mResource) {
251 return NS_ERROR_FAILURE;
252 }
253 DDLINKCHILD("resource", mResource.get());
254
255 nsresult rv = MediaShutdownManager::Instance().Register(this);
256 if (NS_WARN_IF(NS_FAILED(rv))) {
257 return rv;
258 }
259
260 rv = mResource->Open(aStreamListener);
261 NS_ENSURE_SUCCESS(rv, rv);
262
263 SetStateMachine(CreateStateMachine());
264 NS_ENSURE_TRUE(GetStateMachine(), NS_ERROR_FAILURE);
265
266 GetStateMachine()->DispatchIsLiveStream(mResource->IsLiveStream());
267
268 return InitializeStateMachine();
269 }
270
Load(BaseMediaResource * aOriginal)271 nsresult ChannelMediaDecoder::Load(BaseMediaResource* aOriginal) {
272 MOZ_ASSERT(NS_IsMainThread());
273 MOZ_ASSERT(!mResource);
274
275 mResource = aOriginal->CloneData(mResourceCallback);
276 if (!mResource) {
277 return NS_ERROR_FAILURE;
278 }
279 DDLINKCHILD("resource", mResource.get());
280
281 nsresult rv = MediaShutdownManager::Instance().Register(this);
282 if (NS_WARN_IF(NS_FAILED(rv))) {
283 return rv;
284 }
285
286 SetStateMachine(CreateStateMachine());
287 NS_ENSURE_TRUE(GetStateMachine(), NS_ERROR_FAILURE);
288
289 GetStateMachine()->DispatchIsLiveStream(mResource->IsLiveStream());
290
291 return InitializeStateMachine();
292 }
293
NotifyDownloadEnded(nsresult aStatus)294 void ChannelMediaDecoder::NotifyDownloadEnded(nsresult aStatus) {
295 MOZ_ASSERT(NS_IsMainThread());
296 MOZ_DIAGNOSTIC_ASSERT(!IsShutdown());
297
298 LOG("NotifyDownloadEnded, status=%" PRIx32, static_cast<uint32_t>(aStatus));
299
300 if (NS_SUCCEEDED(aStatus)) {
301 // Download ends successfully. This is a stream with a finite length.
302 GetStateMachine()->DispatchIsLiveStream(false);
303 }
304
305 MediaDecoderOwner* owner = GetOwner();
306 if (NS_SUCCEEDED(aStatus) || aStatus == NS_BASE_STREAM_CLOSED) {
307 nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction(
308 "ChannelMediaDecoder::UpdatePlaybackRate",
309 [stats = mPlaybackStatistics,
310 res = RefPtr<BaseMediaResource>(mResource), duration = mDuration]() {
311 auto rate = ComputePlaybackRate(stats, res, duration);
312 UpdatePlaybackRate(rate, res);
313 });
314 nsresult rv = GetStateMachine()->OwnerThread()->Dispatch(r.forget());
315 MOZ_DIAGNOSTIC_ASSERT(NS_SUCCEEDED(rv));
316 Unused << rv;
317 owner->DownloadSuspended();
318 // NotifySuspendedStatusChanged will tell the element that download
319 // has been suspended "by the cache", which is true since we never
320 // download anything. The element can then transition to HAVE_ENOUGH_DATA.
321 owner->NotifySuspendedByCache(true);
322 } else if (aStatus == NS_BINDING_ABORTED) {
323 // Download has been cancelled by user.
324 owner->LoadAborted();
325 } else {
326 NetworkError(MediaResult(aStatus, "Download aborted"));
327 }
328 }
329
CanPlayThroughImpl()330 bool ChannelMediaDecoder::CanPlayThroughImpl() {
331 MOZ_ASSERT(NS_IsMainThread());
332 return mCanPlayThrough;
333 }
334
OnPlaybackEvent(MediaPlaybackEvent && aEvent)335 void ChannelMediaDecoder::OnPlaybackEvent(MediaPlaybackEvent&& aEvent) {
336 MOZ_ASSERT(NS_IsMainThread());
337 switch (aEvent.mType) {
338 case MediaPlaybackEvent::PlaybackStarted:
339 mPlaybackPosition = aEvent.mData.as<int64_t>();
340 mPlaybackStatistics.Start();
341 break;
342 case MediaPlaybackEvent::PlaybackProgressed: {
343 int64_t newPos = aEvent.mData.as<int64_t>();
344 mPlaybackStatistics.AddBytes(newPos - mPlaybackPosition);
345 mPlaybackPosition = newPos;
346 break;
347 }
348 case MediaPlaybackEvent::PlaybackStopped: {
349 int64_t newPos = aEvent.mData.as<int64_t>();
350 mPlaybackStatistics.AddBytes(newPos - mPlaybackPosition);
351 mPlaybackPosition = newPos;
352 mPlaybackStatistics.Stop();
353 break;
354 }
355 default:
356 break;
357 }
358 MediaDecoder::OnPlaybackEvent(std::move(aEvent));
359 }
360
DurationChanged()361 void ChannelMediaDecoder::DurationChanged() {
362 MOZ_ASSERT(NS_IsMainThread());
363 MediaDecoder::DurationChanged();
364 // Duration has changed so we should recompute playback rate
365 nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction(
366 "ChannelMediaDecoder::UpdatePlaybackRate",
367 [stats = mPlaybackStatistics, res = RefPtr<BaseMediaResource>(mResource),
368 duration = mDuration]() {
369 auto rate = ComputePlaybackRate(stats, res, duration);
370 UpdatePlaybackRate(rate, res);
371 });
372 nsresult rv = GetStateMachine()->OwnerThread()->Dispatch(r.forget());
373 MOZ_DIAGNOSTIC_ASSERT(NS_SUCCEEDED(rv));
374 Unused << rv;
375 }
376
DownloadProgressed()377 void ChannelMediaDecoder::DownloadProgressed() {
378 MOZ_ASSERT(NS_IsMainThread());
379 MOZ_DIAGNOSTIC_ASSERT(!IsShutdown());
380
381 GetOwner()->DownloadProgressed();
382
383 using StatsPromise = MozPromise<MediaStatistics, bool, true>;
384 InvokeAsync(GetStateMachine()->OwnerThread(), __func__,
385 [playbackStats = mPlaybackStatistics,
386 res = RefPtr<BaseMediaResource>(mResource), duration = mDuration,
387 pos = mPlaybackPosition]() {
388 auto rate = ComputePlaybackRate(playbackStats, res, duration);
389 UpdatePlaybackRate(rate, res);
390 MediaStatistics stats = GetStatistics(rate, res, pos);
391 return StatsPromise::CreateAndResolve(stats, __func__);
392 })
393 ->Then(
394 mAbstractMainThread, __func__,
395 [=,
396 self = RefPtr<ChannelMediaDecoder>(this)](MediaStatistics aStats) {
397 if (IsShutdown()) {
398 return;
399 }
400 mCanPlayThrough = aStats.CanPlayThrough();
401 GetStateMachine()->DispatchCanPlayThrough(mCanPlayThrough);
402 mResource->ThrottleReadahead(ShouldThrottleDownload(aStats));
403 // Update readyState since mCanPlayThrough might have changed.
404 GetOwner()->UpdateReadyState();
405 },
406 []() { MOZ_ASSERT_UNREACHABLE("Promise not resolved"); });
407 }
408
409 /* static */ ChannelMediaDecoder::PlaybackRateInfo
ComputePlaybackRate(const MediaChannelStatistics & aStats,BaseMediaResource * aResource,double aDuration)410 ChannelMediaDecoder::ComputePlaybackRate(const MediaChannelStatistics& aStats,
411 BaseMediaResource* aResource,
412 double aDuration) {
413 MOZ_ASSERT(!NS_IsMainThread());
414
415 int64_t length = aResource->GetLength();
416 if (mozilla::IsFinite<double>(aDuration) && aDuration > 0 && length >= 0 &&
417 length / aDuration < UINT32_MAX) {
418 return {uint32_t(length / aDuration), true};
419 }
420
421 bool reliable = false;
422 uint32_t rate = aStats.GetRate(&reliable);
423 return {rate, reliable};
424 }
425
426 /* static */
UpdatePlaybackRate(const PlaybackRateInfo & aInfo,BaseMediaResource * aResource)427 void ChannelMediaDecoder::UpdatePlaybackRate(const PlaybackRateInfo& aInfo,
428 BaseMediaResource* aResource) {
429 MOZ_ASSERT(!NS_IsMainThread());
430
431 uint32_t rate = aInfo.mRate;
432
433 if (aInfo.mReliable) {
434 // Avoid passing a zero rate
435 rate = std::max(rate, 1u);
436 } else {
437 // Set a minimum rate of 10,000 bytes per second ... sometimes we just
438 // don't have good data
439 rate = std::max(rate, 10000u);
440 }
441
442 aResource->SetPlaybackRate(rate);
443 }
444
445 /* static */
GetStatistics(const PlaybackRateInfo & aInfo,BaseMediaResource * aRes,int64_t aPlaybackPosition)446 MediaStatistics ChannelMediaDecoder::GetStatistics(
447 const PlaybackRateInfo& aInfo, BaseMediaResource* aRes,
448 int64_t aPlaybackPosition) {
449 MOZ_ASSERT(!NS_IsMainThread());
450
451 MediaStatistics result;
452 result.mDownloadRate = aRes->GetDownloadRate(&result.mDownloadRateReliable);
453 result.mDownloadPosition = aRes->GetCachedDataEnd(aPlaybackPosition);
454 result.mTotalBytes = aRes->GetLength();
455 result.mPlaybackRate = aInfo.mRate;
456 result.mPlaybackRateReliable = aInfo.mReliable;
457 result.mPlaybackPosition = aPlaybackPosition;
458 return result;
459 }
460
ShouldThrottleDownload(const MediaStatistics & aStats)461 bool ChannelMediaDecoder::ShouldThrottleDownload(
462 const MediaStatistics& aStats) {
463 // We throttle the download if either the throttle override pref is set
464 // (so that we always throttle at the readahead limit on mobile if using
465 // a cellular network) or if the download is fast enough that there's no
466 // concern about playback being interrupted.
467 MOZ_ASSERT(NS_IsMainThread());
468 NS_ENSURE_TRUE(GetStateMachine(), false);
469
470 int64_t length = aStats.mTotalBytes;
471 if (length > 0 &&
472 length <= int64_t(StaticPrefs::media_memory_cache_max_size()) * 1024) {
473 // Don't throttle the download of small resources. This is to speed
474 // up seeking, as seeks into unbuffered ranges would require starting
475 // up a new HTTP transaction, which adds latency.
476 return false;
477 }
478
479 if (OnCellularConnection() &&
480 Preferences::GetBool(
481 "media.throttle-cellular-regardless-of-download-rate", false)) {
482 return true;
483 }
484
485 if (!aStats.mDownloadRateReliable || !aStats.mPlaybackRateReliable) {
486 return false;
487 }
488 uint32_t factor =
489 std::max(2u, Preferences::GetUint("media.throttle-factor", 2));
490 return aStats.mDownloadRate > factor * aStats.mPlaybackRate;
491 }
492
AddSizeOfResources(ResourceSizes * aSizes)493 void ChannelMediaDecoder::AddSizeOfResources(ResourceSizes* aSizes) {
494 MOZ_ASSERT(NS_IsMainThread());
495 if (mResource) {
496 aSizes->mByteSize += mResource->SizeOfIncludingThis(aSizes->mMallocSizeOf);
497 }
498 }
499
GetCurrentPrincipal()500 already_AddRefed<nsIPrincipal> ChannelMediaDecoder::GetCurrentPrincipal() {
501 MOZ_ASSERT(NS_IsMainThread());
502 return mResource ? mResource->GetCurrentPrincipal() : nullptr;
503 }
504
HadCrossOriginRedirects()505 bool ChannelMediaDecoder::HadCrossOriginRedirects() {
506 MOZ_ASSERT(NS_IsMainThread());
507 return mResource ? mResource->HadCrossOriginRedirects() : false;
508 }
509
IsTransportSeekable()510 bool ChannelMediaDecoder::IsTransportSeekable() {
511 MOZ_ASSERT(NS_IsMainThread());
512 return mResource->IsTransportSeekable();
513 }
514
SetLoadInBackground(bool aLoadInBackground)515 void ChannelMediaDecoder::SetLoadInBackground(bool aLoadInBackground) {
516 MOZ_ASSERT(NS_IsMainThread());
517 if (mResource) {
518 mResource->SetLoadInBackground(aLoadInBackground);
519 }
520 }
521
Suspend()522 void ChannelMediaDecoder::Suspend() {
523 MOZ_ASSERT(NS_IsMainThread());
524 if (mResource) {
525 mResource->Suspend(true);
526 }
527 MediaDecoder::Suspend();
528 }
529
Resume()530 void ChannelMediaDecoder::Resume() {
531 MOZ_ASSERT(NS_IsMainThread());
532 if (mResource) {
533 mResource->Resume();
534 }
535 MediaDecoder::Resume();
536 }
537
MetadataLoaded(UniquePtr<MediaInfo> aInfo,UniquePtr<MetadataTags> aTags,MediaDecoderEventVisibility aEventVisibility)538 void ChannelMediaDecoder::MetadataLoaded(
539 UniquePtr<MediaInfo> aInfo, UniquePtr<MetadataTags> aTags,
540 MediaDecoderEventVisibility aEventVisibility) {
541 MediaDecoder::MetadataLoaded(std::move(aInfo), std::move(aTags),
542 aEventVisibility);
543 // Set mode to PLAYBACK after reading metadata.
544 mResource->SetReadMode(MediaCacheStream::MODE_PLAYBACK);
545 }
546
GetDebugInfo(dom::MediaDecoderDebugInfo & aInfo)547 void ChannelMediaDecoder::GetDebugInfo(dom::MediaDecoderDebugInfo& aInfo) {
548 MediaDecoder::GetDebugInfo(aInfo);
549 if (mResource) {
550 mResource->GetDebugInfo(aInfo.mResource);
551 }
552 }
553
554 } // namespace mozilla
555
556 // avoid redefined macro in unified build
557 #undef LOG
558