|
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" |
|
8 |
|
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" |
|
25 |
|
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 |
|
32 |
|
33 namespace mozilla { |
|
34 |
|
35 namespace dom { |
|
36 |
|
37 class TimeRanges; |
|
38 |
|
39 } // namespace dom |
|
40 |
|
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 } |
|
51 |
|
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 } |
|
59 |
|
60 bool IsWaitingMediaResources() MOZ_OVERRIDE |
|
61 { |
|
62 return mDecoders.IsEmpty() && mPendingDecoders.IsEmpty(); |
|
63 } |
|
64 |
|
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(); |
|
73 |
|
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(); |
|
80 |
|
81 return rv; |
|
82 } |
|
83 |
|
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); |
|
92 |
|
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(); |
|
99 |
|
100 if (rv) { |
|
101 return true; |
|
102 } |
|
103 |
|
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); |
|
108 |
|
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 } |
|
118 |
|
119 bool HasVideo() MOZ_OVERRIDE |
|
120 { |
|
121 return mInfo.HasVideo(); |
|
122 } |
|
123 |
|
124 bool HasAudio() MOZ_OVERRIDE |
|
125 { |
|
126 return mInfo.HasAudio(); |
|
127 } |
|
128 |
|
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); |
|
135 |
|
136 void CallDecoderInitialization(); |
|
137 |
|
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()); |
|
145 |
|
146 GetVideoReader()->SetIdle(); |
|
147 |
|
148 WaitForPendingDecoders(); |
|
149 |
|
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); |
|
156 |
|
157 GetVideoReader()->SetActive(); |
|
158 GetVideoReader()->DecodeToTarget(aTimeThreshold); |
|
159 |
|
160 return true; |
|
161 } |
|
162 return false; |
|
163 } |
|
164 |
|
165 MediaDecoderReader* GetAudioReader() { |
|
166 if (mActiveAudioDecoder == -1) { |
|
167 return nullptr; |
|
168 } |
|
169 return mDecoders[mActiveAudioDecoder]->GetReader(); |
|
170 } |
|
171 |
|
172 MediaDecoderReader* GetVideoReader() { |
|
173 if (mActiveVideoDecoder == -1) { |
|
174 return nullptr; |
|
175 } |
|
176 return mDecoders[mActiveVideoDecoder]->GetReader(); |
|
177 } |
|
178 |
|
179 void WaitForPendingDecoders(); |
|
180 |
|
181 nsTArray<nsRefPtr<SubBufferDecoder>> mPendingDecoders; |
|
182 nsTArray<nsRefPtr<SubBufferDecoder>> mDecoders; |
|
183 |
|
184 int32_t mActiveVideoDecoder; |
|
185 int32_t mActiveAudioDecoder; |
|
186 dom::MediaSource* mMediaSource; |
|
187 }; |
|
188 |
|
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 } |
|
198 |
|
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 } |
|
206 |
|
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 } |
|
215 |
|
216 private: |
|
217 void CallDecoderInitialization() { |
|
218 if (!mReader) { |
|
219 return; |
|
220 } |
|
221 static_cast<MediaSourceReader*>(mReader.get())->CallDecoderInitialization(); |
|
222 } |
|
223 }; |
|
224 |
|
225 MediaSourceDecoder::MediaSourceDecoder(dom::HTMLMediaElement* aElement) |
|
226 : mMediaSource(nullptr) |
|
227 { |
|
228 Init(aElement); |
|
229 } |
|
230 |
|
231 MediaDecoder* |
|
232 MediaSourceDecoder::Clone() |
|
233 { |
|
234 // TODO: Sort out cloning. |
|
235 return nullptr; |
|
236 } |
|
237 |
|
238 MediaDecoderStateMachine* |
|
239 MediaSourceDecoder::CreateStateMachine() |
|
240 { |
|
241 return new MediaSourceStateMachine(this, new MediaSourceReader(this, mMediaSource)); |
|
242 } |
|
243 |
|
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 } |
|
253 |
|
254 return mDecoderStateMachine->Init(nullptr); |
|
255 } |
|
256 |
|
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 } |
|
272 |
|
273 /*static*/ |
|
274 already_AddRefed<MediaResource> |
|
275 MediaSourceDecoder::CreateResource() |
|
276 { |
|
277 return nsRefPtr<MediaResource>(new MediaSourceResource()).forget(); |
|
278 } |
|
279 |
|
280 void |
|
281 MediaSourceDecoder::AttachMediaSource(dom::MediaSource* aMediaSource) |
|
282 { |
|
283 MOZ_ASSERT(!mMediaSource && !mDecoderStateMachine); |
|
284 mMediaSource = aMediaSource; |
|
285 } |
|
286 |
|
287 void |
|
288 MediaSourceDecoder::DetachMediaSource() |
|
289 { |
|
290 mMediaSource = nullptr; |
|
291 } |
|
292 |
|
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 } |
|
301 |
|
302 nsresult |
|
303 MediaSourceDecoder::EnqueueDecoderInitialization() |
|
304 { |
|
305 if (!mDecoderStateMachine) { |
|
306 return NS_ERROR_FAILURE; |
|
307 } |
|
308 return static_cast<MediaSourceStateMachine*>(mDecoderStateMachine.get())->EnqueueDecoderInitialization(); |
|
309 } |
|
310 |
|
311 class ReleaseDecodersTask : public nsRunnable { |
|
312 public: |
|
313 ReleaseDecodersTask(nsTArray<nsRefPtr<SubBufferDecoder>>& aDecoders) |
|
314 { |
|
315 mDecoders.SwapElements(aDecoders); |
|
316 } |
|
317 |
|
318 NS_IMETHOD Run() MOZ_OVERRIDE MOZ_FINAL { |
|
319 mDecoders.Clear(); |
|
320 return NS_OK; |
|
321 } |
|
322 |
|
323 private: |
|
324 nsTArray<nsRefPtr<SubBufferDecoder>> mDecoders; |
|
325 }; |
|
326 |
|
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); |
|
335 |
|
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 } |
|
350 |
|
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 } |
|
356 |
|
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 } |
|
368 |
|
369 void |
|
370 MediaSourceReader::WaitForPendingDecoders() |
|
371 { |
|
372 ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor()); |
|
373 while (!mPendingDecoders.IsEmpty()) { |
|
374 mon.Wait(); |
|
375 } |
|
376 } |
|
377 |
|
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 } |
|
400 |
|
401 nsresult |
|
402 MediaSourceReader::Seek(int64_t aTime, int64_t aStartTime, int64_t aEndTime, |
|
403 int64_t aCurrentTime) |
|
404 { |
|
405 ResetDecode(); |
|
406 |
|
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 } |
|
424 |
|
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 } |
|
436 |
|
437 nsresult |
|
438 MediaSourceReader::ReadMetadata(MediaInfo* aInfo, MetadataTags** aTags) |
|
439 { |
|
440 mDecoder->SetMediaSeekable(true); |
|
441 mDecoder->SetTransportSeekable(false); |
|
442 |
|
443 MSE_DEBUG("%p: MSR::ReadMetadata pending=%u", this, mPendingDecoders.Length()); |
|
444 |
|
445 WaitForPendingDecoders(); |
|
446 |
|
447 MSE_DEBUG("%p: MSR::ReadMetadata decoders=%u", this, mDecoders.Length()); |
|
448 |
|
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(); |
|
459 |
|
460 reader->SetActive(); // XXX check where this should be called |
|
461 |
|
462 MediaInfo mi = reader->GetMediaInfo(); |
|
463 |
|
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 } |
|
479 |
|
480 if (maxDuration != -1) { |
|
481 ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor()); |
|
482 mDecoder->SetMediaDuration(maxDuration); |
|
483 ErrorResult dummy; |
|
484 mMediaSource->SetDuration(maxDuration, dummy); |
|
485 } |
|
486 |
|
487 *aInfo = mInfo; |
|
488 *aTags = nullptr; // TODO: Handle metadata. |
|
489 |
|
490 return NS_OK; |
|
491 } |
|
492 |
|
493 } // namespace mozilla |