Tue, 06 Jan 2015 21:39:09 +0100
Conditionally force memory storage according to privacy.thirdparty.isolate;
This solves Tor bug #9701, complying with disk avoidance documented in
https://www.torproject.org/projects/torbrowser/design/#disk-avoidance.
1 /* -*- mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
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 #include "MediaSourceResource.h"
7 #include "MediaSourceDecoder.h"
9 #include "AbstractMediaDecoder.h"
10 #include "MediaDecoderReader.h"
11 #include "MediaDecoderStateMachine.h"
12 #include "mozilla/Assertions.h"
13 #include "mozilla/FloatingPoint.h"
14 #include "mozilla/dom/HTMLMediaElement.h"
15 #include "mozilla/dom/TimeRanges.h"
16 #include "mozilla/mozalloc.h"
17 #include "nsISupports.h"
18 #include "nsIThread.h"
19 #include "prlog.h"
20 #include "MediaSource.h"
21 #include "SubBufferDecoder.h"
22 #include "SourceBufferResource.h"
23 #include "SourceBufferList.h"
24 #include "VideoUtils.h"
26 #ifdef PR_LOGGING
27 extern PRLogModuleInfo* gMediaSourceLog;
28 #define MSE_DEBUG(...) PR_LOG(gMediaSourceLog, PR_LOG_DEBUG, (__VA_ARGS__))
29 #else
30 #define MSE_DEBUG(...)
31 #endif
33 namespace mozilla {
35 namespace dom {
37 class TimeRanges;
39 } // namespace dom
41 class MediaSourceReader : public MediaDecoderReader
42 {
43 public:
44 MediaSourceReader(MediaSourceDecoder* aDecoder, dom::MediaSource* aSource)
45 : MediaDecoderReader(aDecoder)
46 , mActiveVideoDecoder(-1)
47 , mActiveAudioDecoder(-1)
48 , mMediaSource(aSource)
49 {
50 }
52 nsresult Init(MediaDecoderReader* aCloneDonor) MOZ_OVERRIDE
53 {
54 // Although we technically don't implement anything here, we return NS_OK
55 // so that when the state machine initializes and calls this function
56 // we don't return an error code back to the media element.
57 return NS_OK;
58 }
60 bool IsWaitingMediaResources() MOZ_OVERRIDE
61 {
62 return mDecoders.IsEmpty() && mPendingDecoders.IsEmpty();
63 }
65 bool DecodeAudioData() MOZ_OVERRIDE
66 {
67 if (!GetAudioReader()) {
68 MSE_DEBUG("%p DecodeAudioFrame called with no audio reader", this);
69 MOZ_ASSERT(mPendingDecoders.IsEmpty());
70 return false;
71 }
72 bool rv = GetAudioReader()->DecodeAudioData();
74 nsAutoTArray<AudioData*, 10> audio;
75 GetAudioReader()->AudioQueue().GetElementsAfter(-1, &audio);
76 for (uint32_t i = 0; i < audio.Length(); ++i) {
77 AudioQueue().Push(audio[i]);
78 }
79 GetAudioReader()->AudioQueue().Empty();
81 return rv;
82 }
84 bool DecodeVideoFrame(bool& aKeyFrameSkip, int64_t aTimeThreshold) MOZ_OVERRIDE
85 {
86 if (!GetVideoReader()) {
87 MSE_DEBUG("%p DecodeVideoFrame called with no video reader", this);
88 MOZ_ASSERT(mPendingDecoders.IsEmpty());
89 return false;
90 }
91 bool rv = GetVideoReader()->DecodeVideoFrame(aKeyFrameSkip, aTimeThreshold);
93 nsAutoTArray<VideoData*, 10> video;
94 GetVideoReader()->VideoQueue().GetElementsAfter(-1, &video);
95 for (uint32_t i = 0; i < video.Length(); ++i) {
96 VideoQueue().Push(video[i]);
97 }
98 GetVideoReader()->VideoQueue().Empty();
100 if (rv) {
101 return true;
102 }
104 MSE_DEBUG("%p MSR::DecodeVF %d (%p) returned false (readers=%u)",
105 this, mActiveVideoDecoder, mDecoders[mActiveVideoDecoder].get(), mDecoders.Length());
106 if (SwitchVideoReaders(aTimeThreshold)) {
107 rv = GetVideoReader()->DecodeVideoFrame(aKeyFrameSkip, aTimeThreshold);
109 nsAutoTArray<VideoData*, 10> video;
110 GetVideoReader()->VideoQueue().GetElementsAfter(-1, &video);
111 for (uint32_t i = 0; i < video.Length(); ++i) {
112 VideoQueue().Push(video[i]);
113 }
114 GetVideoReader()->VideoQueue().Empty();
115 }
116 return rv;
117 }
119 bool HasVideo() MOZ_OVERRIDE
120 {
121 return mInfo.HasVideo();
122 }
124 bool HasAudio() MOZ_OVERRIDE
125 {
126 return mInfo.HasAudio();
127 }
129 nsresult ReadMetadata(MediaInfo* aInfo, MetadataTags** aTags) MOZ_OVERRIDE;
130 nsresult Seek(int64_t aTime, int64_t aStartTime, int64_t aEndTime,
131 int64_t aCurrentTime) MOZ_OVERRIDE;
132 nsresult GetBuffered(dom::TimeRanges* aBuffered, int64_t aStartTime) MOZ_OVERRIDE;
133 already_AddRefed<SubBufferDecoder> CreateSubDecoder(const nsACString& aType,
134 MediaSourceDecoder* aParentDecoder);
136 void CallDecoderInitialization();
138 private:
139 bool SwitchVideoReaders(int64_t aTimeThreshold) {
140 MOZ_ASSERT(mActiveVideoDecoder != -1);
141 // XXX: We switch when the first reader is depleted, but it might be
142 // better to switch as soon as the next reader is ready to decode and
143 // has data for the current media time.
144 ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
146 GetVideoReader()->SetIdle();
148 WaitForPendingDecoders();
150 for (uint32_t i = mActiveVideoDecoder + 1; i < mDecoders.Length(); ++i) {
151 if (!mDecoders[i]->GetReader()->GetMediaInfo().HasVideo()) {
152 continue;
153 }
154 mActiveVideoDecoder = i;
155 MSE_DEBUG("%p MSR::DecodeVF switching to %d", this, mActiveVideoDecoder);
157 GetVideoReader()->SetActive();
158 GetVideoReader()->DecodeToTarget(aTimeThreshold);
160 return true;
161 }
162 return false;
163 }
165 MediaDecoderReader* GetAudioReader() {
166 if (mActiveAudioDecoder == -1) {
167 return nullptr;
168 }
169 return mDecoders[mActiveAudioDecoder]->GetReader();
170 }
172 MediaDecoderReader* GetVideoReader() {
173 if (mActiveVideoDecoder == -1) {
174 return nullptr;
175 }
176 return mDecoders[mActiveVideoDecoder]->GetReader();
177 }
179 void WaitForPendingDecoders();
181 nsTArray<nsRefPtr<SubBufferDecoder>> mPendingDecoders;
182 nsTArray<nsRefPtr<SubBufferDecoder>> mDecoders;
184 int32_t mActiveVideoDecoder;
185 int32_t mActiveAudioDecoder;
186 dom::MediaSource* mMediaSource;
187 };
189 class MediaSourceStateMachine : public MediaDecoderStateMachine
190 {
191 public:
192 MediaSourceStateMachine(MediaDecoder* aDecoder,
193 MediaDecoderReader* aReader,
194 bool aRealTime = false)
195 : MediaDecoderStateMachine(aDecoder, aReader, aRealTime)
196 {
197 }
199 already_AddRefed<SubBufferDecoder> CreateSubDecoder(const nsACString& aType,
200 MediaSourceDecoder* aParentDecoder) {
201 if (!mReader) {
202 return nullptr;
203 }
204 return static_cast<MediaSourceReader*>(mReader.get())->CreateSubDecoder(aType, aParentDecoder);
205 }
207 nsresult EnqueueDecoderInitialization() {
208 AssertCurrentThreadInMonitor();
209 if (!mReader) {
210 return NS_ERROR_FAILURE;
211 }
212 return mDecodeTaskQueue->Dispatch(NS_NewRunnableMethod(this,
213 &MediaSourceStateMachine::CallDecoderInitialization));
214 }
216 private:
217 void CallDecoderInitialization() {
218 if (!mReader) {
219 return;
220 }
221 static_cast<MediaSourceReader*>(mReader.get())->CallDecoderInitialization();
222 }
223 };
225 MediaSourceDecoder::MediaSourceDecoder(dom::HTMLMediaElement* aElement)
226 : mMediaSource(nullptr)
227 {
228 Init(aElement);
229 }
231 MediaDecoder*
232 MediaSourceDecoder::Clone()
233 {
234 // TODO: Sort out cloning.
235 return nullptr;
236 }
238 MediaDecoderStateMachine*
239 MediaSourceDecoder::CreateStateMachine()
240 {
241 return new MediaSourceStateMachine(this, new MediaSourceReader(this, mMediaSource));
242 }
244 nsresult
245 MediaSourceDecoder::Load(nsIStreamListener**, MediaDecoder*)
246 {
247 MOZ_ASSERT(!mDecoderStateMachine);
248 mDecoderStateMachine = CreateStateMachine();
249 if (!mDecoderStateMachine) {
250 NS_WARNING("Failed to create state machine!");
251 return NS_ERROR_FAILURE;
252 }
254 return mDecoderStateMachine->Init(nullptr);
255 }
257 nsresult
258 MediaSourceDecoder::GetSeekable(dom::TimeRanges* aSeekable)
259 {
260 double duration = mMediaSource->Duration();
261 if (IsNaN(duration)) {
262 // Return empty range.
263 } else if (duration > 0 && mozilla::IsInfinite(duration)) {
264 nsRefPtr<dom::TimeRanges> bufferedRanges = new dom::TimeRanges();
265 GetBuffered(bufferedRanges);
266 aSeekable->Add(bufferedRanges->GetStartTime(), bufferedRanges->GetEndTime());
267 } else {
268 aSeekable->Add(0, duration);
269 }
270 return NS_OK;
271 }
273 /*static*/
274 already_AddRefed<MediaResource>
275 MediaSourceDecoder::CreateResource()
276 {
277 return nsRefPtr<MediaResource>(new MediaSourceResource()).forget();
278 }
280 void
281 MediaSourceDecoder::AttachMediaSource(dom::MediaSource* aMediaSource)
282 {
283 MOZ_ASSERT(!mMediaSource && !mDecoderStateMachine);
284 mMediaSource = aMediaSource;
285 }
287 void
288 MediaSourceDecoder::DetachMediaSource()
289 {
290 mMediaSource = nullptr;
291 }
293 already_AddRefed<SubBufferDecoder>
294 MediaSourceDecoder::CreateSubDecoder(const nsACString& aType)
295 {
296 if (!mDecoderStateMachine) {
297 return nullptr;
298 }
299 return static_cast<MediaSourceStateMachine*>(mDecoderStateMachine.get())->CreateSubDecoder(aType, this);
300 }
302 nsresult
303 MediaSourceDecoder::EnqueueDecoderInitialization()
304 {
305 if (!mDecoderStateMachine) {
306 return NS_ERROR_FAILURE;
307 }
308 return static_cast<MediaSourceStateMachine*>(mDecoderStateMachine.get())->EnqueueDecoderInitialization();
309 }
311 class ReleaseDecodersTask : public nsRunnable {
312 public:
313 ReleaseDecodersTask(nsTArray<nsRefPtr<SubBufferDecoder>>& aDecoders)
314 {
315 mDecoders.SwapElements(aDecoders);
316 }
318 NS_IMETHOD Run() MOZ_OVERRIDE MOZ_FINAL {
319 mDecoders.Clear();
320 return NS_OK;
321 }
323 private:
324 nsTArray<nsRefPtr<SubBufferDecoder>> mDecoders;
325 };
327 void
328 MediaSourceReader::CallDecoderInitialization()
329 {
330 ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
331 for (uint32_t i = 0; i < mPendingDecoders.Length(); ++i) {
332 nsRefPtr<SubBufferDecoder> decoder = mPendingDecoders[i];
333 MediaDecoderReader* reader = decoder->GetReader();
334 MSE_DEBUG("%p: Initializating subdecoder %p reader %p", this, decoder.get(), reader);
336 reader->SetActive();
337 MediaInfo mi;
338 nsAutoPtr<MetadataTags> tags; // TODO: Handle metadata.
339 nsresult rv;
340 {
341 ReentrantMonitorAutoExit exitMon(mDecoder->GetReentrantMonitor());
342 rv = reader->ReadMetadata(&mi, getter_Transfers(tags));
343 }
344 reader->SetIdle();
345 if (NS_FAILED(rv)) {
346 // XXX: Need to signal error back to owning SourceBuffer.
347 MSE_DEBUG("%p: Reader %p failed to initialize, rv=%x", this, reader, rv);
348 continue;
349 }
351 bool active = false;
352 if (mi.HasVideo() || mi.HasAudio()) {
353 MSE_DEBUG("%p: Reader %p has video=%d audio=%d", this, reader, mi.HasVideo(), mi.HasAudio());
354 active = true;
355 }
357 if (active) {
358 mDecoders.AppendElement(decoder);
359 } else {
360 MSE_DEBUG("%p: Reader %p not activated", this, reader);
361 }
362 }
363 NS_DispatchToMainThread(new ReleaseDecodersTask(mPendingDecoders));
364 MOZ_ASSERT(mPendingDecoders.IsEmpty());
365 mDecoder->NotifyWaitingForResourcesStatusChanged();
366 mon.NotifyAll();
367 }
369 void
370 MediaSourceReader::WaitForPendingDecoders()
371 {
372 ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
373 while (!mPendingDecoders.IsEmpty()) {
374 mon.Wait();
375 }
376 }
378 already_AddRefed<SubBufferDecoder>
379 MediaSourceReader::CreateSubDecoder(const nsACString& aType, MediaSourceDecoder* aParentDecoder)
380 {
381 // XXX: Why/when is mDecoder null here, since it should be equal to aParentDecoder?!
382 nsRefPtr<SubBufferDecoder> decoder =
383 new SubBufferDecoder(new SourceBufferResource(nullptr, aType), aParentDecoder);
384 nsAutoPtr<MediaDecoderReader> reader(DecoderTraits::CreateReader(aType, decoder));
385 if (!reader) {
386 return nullptr;
387 }
388 reader->Init(nullptr);
389 ReentrantMonitorAutoEnter mon(aParentDecoder->GetReentrantMonitor());
390 MSE_DEBUG("Registered subdecoder %p subreader %p", decoder.get(), reader.get());
391 decoder->SetReader(reader.forget());
392 mPendingDecoders.AppendElement(decoder);
393 if (NS_FAILED(static_cast<MediaSourceDecoder*>(mDecoder)->EnqueueDecoderInitialization())) {
394 MSE_DEBUG("%p: Failed to enqueue decoder initialization task", this);
395 return nullptr;
396 }
397 mDecoder->NotifyWaitingForResourcesStatusChanged();
398 return decoder.forget();
399 }
401 nsresult
402 MediaSourceReader::Seek(int64_t aTime, int64_t aStartTime, int64_t aEndTime,
403 int64_t aCurrentTime)
404 {
405 ResetDecode();
407 dom::SourceBufferList* sbl = mMediaSource->ActiveSourceBuffers();
408 if (sbl->AllContainsTime (aTime / USECS_PER_S)) {
409 if (GetAudioReader()) {
410 nsresult rv = GetAudioReader()->Seek(aTime, aStartTime, aEndTime, aCurrentTime);
411 if (NS_FAILED(rv)) {
412 return rv;
413 }
414 }
415 if (GetVideoReader()) {
416 nsresult rv = GetVideoReader()->Seek(aTime, aStartTime, aEndTime, aCurrentTime);
417 if (NS_FAILED(rv)) {
418 return rv;
419 }
420 }
421 }
422 return NS_OK;
423 }
425 nsresult
426 MediaSourceReader::GetBuffered(dom::TimeRanges* aBuffered, int64_t aStartTime)
427 {
428 for (uint32_t i = 0; i < mDecoders.Length(); ++i) {
429 nsRefPtr<dom::TimeRanges> r = new dom::TimeRanges();
430 mDecoders[i]->GetBuffered(r);
431 aBuffered->Add(r->GetStartTime(), r->GetEndTime());
432 }
433 aBuffered->Normalize();
434 return NS_OK;
435 }
437 nsresult
438 MediaSourceReader::ReadMetadata(MediaInfo* aInfo, MetadataTags** aTags)
439 {
440 mDecoder->SetMediaSeekable(true);
441 mDecoder->SetTransportSeekable(false);
443 MSE_DEBUG("%p: MSR::ReadMetadata pending=%u", this, mPendingDecoders.Length());
445 WaitForPendingDecoders();
447 MSE_DEBUG("%p: MSR::ReadMetadata decoders=%u", this, mDecoders.Length());
449 // XXX: Make subdecoder setup async, so that use cases like bug 989888 can
450 // work. This will require teaching the state machine about dynamic track
451 // changes (and multiple tracks).
452 // Shorter term, make this block until we've got at least one video track
453 // and lie about having an audio track, then resample/remix as necessary
454 // to match any audio track added later to fit the format we lied about
455 // now. For now we just configure what we've got and cross our fingers.
456 int64_t maxDuration = -1;
457 for (uint32_t i = 0; i < mDecoders.Length(); ++i) {
458 MediaDecoderReader* reader = mDecoders[i]->GetReader();
460 reader->SetActive(); // XXX check where this should be called
462 MediaInfo mi = reader->GetMediaInfo();
464 if (mi.HasVideo() && !mInfo.HasVideo()) {
465 MOZ_ASSERT(mActiveVideoDecoder == -1);
466 mActiveVideoDecoder = i;
467 mInfo.mVideo = mi.mVideo;
468 maxDuration = std::max(maxDuration, mDecoders[i]->GetMediaDuration());
469 MSE_DEBUG("%p: MSR::ReadMetadata video decoder=%u maxDuration=%lld", this, i, maxDuration);
470 }
471 if (mi.HasAudio() && !mInfo.HasAudio()) {
472 MOZ_ASSERT(mActiveAudioDecoder == -1);
473 mActiveAudioDecoder = i;
474 mInfo.mAudio = mi.mAudio;
475 maxDuration = std::max(maxDuration, mDecoders[i]->GetMediaDuration());
476 MSE_DEBUG("%p: MSR::ReadMetadata audio decoder=%u maxDuration=%lld", this, i, maxDuration);
477 }
478 }
480 if (maxDuration != -1) {
481 ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
482 mDecoder->SetMediaDuration(maxDuration);
483 ErrorResult dummy;
484 mMediaSource->SetDuration(maxDuration, dummy);
485 }
487 *aInfo = mInfo;
488 *aTags = nullptr; // TODO: Handle metadata.
490 return NS_OK;
491 }
493 } // namespace mozilla