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