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