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
7 #include "mozilla/MozPromise.h"
8 #include "MediaDecoderReaderWrapper.h"
9
10 namespace mozilla {
11
12 extern LazyLogModule gMediaDecoderLog;
13
14 #undef LOG
15 #define LOG(...) \
16 MOZ_LOG(gMediaDecoderLog, mozilla::LogLevel::Debug, (__VA_ARGS__))
17
18 // StartTimeRendezvous is a helper class that quarantines the first sample
19 // until it gets a sample from both channels, such that we can be guaranteed
20 // to know the start time by the time On{Audio,Video}Decoded is called on MDSM.
21 class StartTimeRendezvous {
22 typedef MediaDecoderReader::MediaDataPromise MediaDataPromise;
23 NS_INLINE_DECL_THREADSAFE_REFCOUNTING(StartTimeRendezvous);
24
25 public:
StartTimeRendezvous(AbstractThread * aOwnerThread,bool aHasAudio,bool aHasVideo,bool aForceZeroStartTime)26 StartTimeRendezvous(AbstractThread* aOwnerThread,
27 bool aHasAudio,
28 bool aHasVideo,
29 bool aForceZeroStartTime)
30 : mOwnerThread(aOwnerThread)
31 {
32 if (aForceZeroStartTime) {
33 mAudioStartTime.emplace(0);
34 mVideoStartTime.emplace(0);
35 return;
36 }
37 if (!aHasAudio) {
38 mAudioStartTime.emplace(INT64_MAX);
39 }
40 if (!aHasVideo) {
41 mVideoStartTime.emplace(INT64_MAX);
42 }
43 }
44
Destroy()45 void Destroy()
46 {
47 mAudioStartTime = Some(mAudioStartTime.refOr(INT64_MAX));
48 mVideoStartTime = Some(mVideoStartTime.refOr(INT64_MAX));
49 mHaveStartTimePromise.RejectIfExists(false, __func__);
50 }
51
AwaitStartTime()52 RefPtr<HaveStartTimePromise> AwaitStartTime()
53 {
54 if (HaveStartTime()) {
55 return HaveStartTimePromise::CreateAndResolve(true, __func__);
56 }
57 return mHaveStartTimePromise.Ensure(__func__);
58 }
59
60 template<MediaData::Type SampleType>
61 RefPtr<MediaDataPromise>
ProcessFirstSample(MediaData * aData)62 ProcessFirstSample(MediaData* aData)
63 {
64 typedef typename MediaDataPromise::Private PromisePrivate;
65 MOZ_ASSERT(mOwnerThread->IsCurrentThreadIn());
66
67 MaybeSetChannelStartTime<SampleType>(aData->mTime);
68
69 RefPtr<PromisePrivate> p = new PromisePrivate(__func__);
70 RefPtr<MediaData> data = aData;
71 RefPtr<StartTimeRendezvous> self = this;
72 AwaitStartTime()->Then(
73 mOwnerThread, __func__,
74 [p, data, self] () {
75 MOZ_ASSERT(self->mOwnerThread->IsCurrentThreadIn());
76 p->Resolve(data, __func__);
77 },
78 [p] () {
79 p->Reject(NS_ERROR_DOM_MEDIA_CANCELED, __func__);
80 });
81
82 return p.forget();
83 }
84
85 template<MediaData::Type SampleType>
FirstSampleRejected(const MediaResult & aError)86 void FirstSampleRejected(const MediaResult& aError)
87 {
88 MOZ_ASSERT(mOwnerThread->IsCurrentThreadIn());
89 if (aError == NS_ERROR_DOM_MEDIA_END_OF_STREAM) {
90 LOG("StartTimeRendezvous=%p SampleType(%d) Has no samples.",
91 this, SampleType);
92 MaybeSetChannelStartTime<SampleType>(INT64_MAX);
93 } else if (aError != NS_ERROR_DOM_MEDIA_WAITING_FOR_DATA) {
94 mHaveStartTimePromise.RejectIfExists(false, __func__);
95 }
96 }
97
HaveStartTime() const98 bool HaveStartTime() const
99 {
100 return mAudioStartTime.isSome() && mVideoStartTime.isSome();
101 }
102
StartTime() const103 int64_t StartTime() const
104 {
105 int64_t time = std::min(mAudioStartTime.ref(), mVideoStartTime.ref());
106 return time == INT64_MAX ? 0 : time;
107 }
108
109 private:
~StartTimeRendezvous()110 ~StartTimeRendezvous() {}
111
112 template<MediaData::Type SampleType>
MaybeSetChannelStartTime(int64_t aStartTime)113 void MaybeSetChannelStartTime(int64_t aStartTime)
114 {
115 if (ChannelStartTime(SampleType).isSome()) {
116 // If we're initialized with aForceZeroStartTime=true, the channel start
117 // times are already set.
118 return;
119 }
120
121 LOG("StartTimeRendezvous=%p Setting SampleType(%d) start time to %lld",
122 this, SampleType, aStartTime);
123
124 ChannelStartTime(SampleType).emplace(aStartTime);
125 if (HaveStartTime()) {
126 mHaveStartTimePromise.ResolveIfExists(true, __func__);
127 }
128 }
129
ChannelStartTime(MediaData::Type aType)130 Maybe<int64_t>& ChannelStartTime(MediaData::Type aType)
131 {
132 return aType == MediaData::AUDIO_DATA ? mAudioStartTime : mVideoStartTime;
133 }
134
135 MozPromiseHolder<HaveStartTimePromise> mHaveStartTimePromise;
136 RefPtr<AbstractThread> mOwnerThread;
137 Maybe<int64_t> mAudioStartTime;
138 Maybe<int64_t> mVideoStartTime;
139 };
140
MediaDecoderReaderWrapper(AbstractThread * aOwnerThread,MediaDecoderReader * aReader)141 MediaDecoderReaderWrapper::MediaDecoderReaderWrapper(AbstractThread* aOwnerThread,
142 MediaDecoderReader* aReader)
143 : mForceZeroStartTime(aReader->ForceZeroStartTime())
144 , mOwnerThread(aOwnerThread)
145 , mReader(aReader)
146 {}
147
~MediaDecoderReaderWrapper()148 MediaDecoderReaderWrapper::~MediaDecoderReaderWrapper()
149 {}
150
151 media::TimeUnit
StartTime() const152 MediaDecoderReaderWrapper::StartTime() const
153 {
154 MOZ_ASSERT(mOwnerThread->IsCurrentThreadIn());
155 MOZ_ASSERT(!mShutdown);
156 return media::TimeUnit::FromMicroseconds(mStartTimeRendezvous->StartTime());
157 }
158
159 RefPtr<MediaDecoderReaderWrapper::MetadataPromise>
ReadMetadata()160 MediaDecoderReaderWrapper::ReadMetadata()
161 {
162 MOZ_ASSERT(mOwnerThread->IsCurrentThreadIn());
163 MOZ_ASSERT(!mShutdown);
164 return InvokeAsync(mReader->OwnerThread(), mReader.get(), __func__,
165 &MediaDecoderReader::AsyncReadMetadata)
166 ->Then(mOwnerThread, __func__, this,
167 &MediaDecoderReaderWrapper::OnMetadataRead,
168 &MediaDecoderReaderWrapper::OnMetadataNotRead)
169 ->CompletionPromise();
170 }
171
172 RefPtr<HaveStartTimePromise>
AwaitStartTime()173 MediaDecoderReaderWrapper::AwaitStartTime()
174 {
175 MOZ_ASSERT(mOwnerThread->IsCurrentThreadIn());
176 MOZ_ASSERT(!mShutdown);
177 return mStartTimeRendezvous->AwaitStartTime();
178 }
179
180 void
RequestAudioData()181 MediaDecoderReaderWrapper::RequestAudioData()
182 {
183 MOZ_ASSERT(mOwnerThread->IsCurrentThreadIn());
184 MOZ_ASSERT(!mShutdown);
185
186 auto p = InvokeAsync(mReader->OwnerThread(), mReader.get(), __func__,
187 &MediaDecoderReader::RequestAudioData);
188
189 if (!mStartTimeRendezvous->HaveStartTime()) {
190 p = p->Then(mOwnerThread, __func__, mStartTimeRendezvous.get(),
191 &StartTimeRendezvous::ProcessFirstSample<MediaData::AUDIO_DATA>,
192 &StartTimeRendezvous::FirstSampleRejected<MediaData::AUDIO_DATA>)
193 ->CompletionPromise();
194 }
195
196 RefPtr<MediaDecoderReaderWrapper> self = this;
197 mAudioDataRequest.Begin(p->Then(mOwnerThread, __func__,
198 [self] (MediaData* aAudioSample) {
199 self->mAudioDataRequest.Complete();
200 aAudioSample->AdjustForStartTime(self->StartTime().ToMicroseconds());
201 self->mAudioCallback.Notify(AsVariant(aAudioSample));
202 },
203 [self] (const MediaResult& aError) {
204 self->mAudioDataRequest.Complete();
205 self->mAudioCallback.Notify(AsVariant(aError));
206 }));
207 }
208
209 void
RequestVideoData(bool aSkipToNextKeyframe,media::TimeUnit aTimeThreshold)210 MediaDecoderReaderWrapper::RequestVideoData(bool aSkipToNextKeyframe,
211 media::TimeUnit aTimeThreshold)
212 {
213 MOZ_ASSERT(mOwnerThread->IsCurrentThreadIn());
214 MOZ_ASSERT(!mShutdown);
215
216 // Time the video decode and send this value back to callbacks who accept
217 // a TimeStamp as its second parameter.
218 TimeStamp videoDecodeStartTime = TimeStamp::Now();
219
220 if (aTimeThreshold.ToMicroseconds() > 0 &&
221 mStartTimeRendezvous->HaveStartTime()) {
222 aTimeThreshold += StartTime();
223 }
224
225 auto p = InvokeAsync(mReader->OwnerThread(), mReader.get(), __func__,
226 &MediaDecoderReader::RequestVideoData,
227 aSkipToNextKeyframe, aTimeThreshold.ToMicroseconds());
228
229 if (!mStartTimeRendezvous->HaveStartTime()) {
230 p = p->Then(mOwnerThread, __func__, mStartTimeRendezvous.get(),
231 &StartTimeRendezvous::ProcessFirstSample<MediaData::VIDEO_DATA>,
232 &StartTimeRendezvous::FirstSampleRejected<MediaData::VIDEO_DATA>)
233 ->CompletionPromise();
234 }
235
236 RefPtr<MediaDecoderReaderWrapper> self = this;
237 mVideoDataRequest.Begin(p->Then(mOwnerThread, __func__,
238 [self, videoDecodeStartTime] (MediaData* aVideoSample) {
239 self->mVideoDataRequest.Complete();
240 aVideoSample->AdjustForStartTime(self->StartTime().ToMicroseconds());
241 self->mVideoCallback.Notify(AsVariant(MakeTuple(aVideoSample, videoDecodeStartTime)));
242 },
243 [self] (const MediaResult& aError) {
244 self->mVideoDataRequest.Complete();
245 self->mVideoCallback.Notify(AsVariant(aError));
246 }));
247 }
248
249 bool
IsRequestingAudioData() const250 MediaDecoderReaderWrapper::IsRequestingAudioData() const
251 {
252 MOZ_ASSERT(mOwnerThread->IsCurrentThreadIn());
253 return mAudioDataRequest.Exists();
254 }
255
256 bool
IsRequestingVideoData() const257 MediaDecoderReaderWrapper::IsRequestingVideoData() const
258 {
259 MOZ_ASSERT(mOwnerThread->IsCurrentThreadIn());
260 return mVideoDataRequest.Exists();
261 }
262
263 bool
IsWaitingAudioData() const264 MediaDecoderReaderWrapper::IsWaitingAudioData() const
265 {
266 MOZ_ASSERT(mOwnerThread->IsCurrentThreadIn());
267 return mAudioWaitRequest.Exists();
268 }
269
270 bool
IsWaitingVideoData() const271 MediaDecoderReaderWrapper::IsWaitingVideoData() const
272 {
273 MOZ_ASSERT(mOwnerThread->IsCurrentThreadIn());
274 return mVideoWaitRequest.Exists();
275 }
276
277 RefPtr<MediaDecoderReader::SeekPromise>
Seek(SeekTarget aTarget,media::TimeUnit aEndTime)278 MediaDecoderReaderWrapper::Seek(SeekTarget aTarget, media::TimeUnit aEndTime)
279 {
280 MOZ_ASSERT(mOwnerThread->IsCurrentThreadIn());
281 aTarget.SetTime(aTarget.GetTime() + StartTime());
282 return InvokeAsync(mReader->OwnerThread(), mReader.get(), __func__,
283 &MediaDecoderReader::Seek, aTarget,
284 aEndTime.ToMicroseconds());
285 }
286
287 void
WaitForData(MediaData::Type aType)288 MediaDecoderReaderWrapper::WaitForData(MediaData::Type aType)
289 {
290 MOZ_ASSERT(mOwnerThread->IsCurrentThreadIn());
291
292 auto p = InvokeAsync(mReader->OwnerThread(), mReader.get(), __func__,
293 &MediaDecoderReader::WaitForData, aType);
294
295 RefPtr<MediaDecoderReaderWrapper> self = this;
296 WaitRequestRef(aType).Begin(p->Then(mOwnerThread, __func__,
297 [self] (MediaData::Type aType) {
298 self->WaitRequestRef(aType).Complete();
299 self->WaitCallbackRef(aType).Notify(AsVariant(aType));
300 },
301 [self, aType] (WaitForDataRejectValue aRejection) {
302 self->WaitRequestRef(aType).Complete();
303 self->WaitCallbackRef(aType).Notify(AsVariant(aRejection));
304 }));
305 }
306
307 MediaCallbackExc<WaitCallbackData>&
WaitCallbackRef(MediaData::Type aType)308 MediaDecoderReaderWrapper::WaitCallbackRef(MediaData::Type aType)
309 {
310 MOZ_ASSERT(mOwnerThread->IsCurrentThreadIn());
311 return aType == MediaData::AUDIO_DATA ? mAudioWaitCallback : mVideoWaitCallback;
312 }
313
314 MozPromiseRequestHolder<MediaDecoderReader::WaitForDataPromise>&
WaitRequestRef(MediaData::Type aType)315 MediaDecoderReaderWrapper::WaitRequestRef(MediaData::Type aType)
316 {
317 MOZ_ASSERT(mOwnerThread->IsCurrentThreadIn());
318 return aType == MediaData::AUDIO_DATA ? mAudioWaitRequest : mVideoWaitRequest;
319 }
320
321 RefPtr<MediaDecoderReaderWrapper::BufferedUpdatePromise>
UpdateBufferedWithPromise()322 MediaDecoderReaderWrapper::UpdateBufferedWithPromise()
323 {
324 MOZ_ASSERT(mOwnerThread->IsCurrentThreadIn());
325 return InvokeAsync(mReader->OwnerThread(), mReader.get(), __func__,
326 &MediaDecoderReader::UpdateBufferedWithPromise);
327 }
328
329 void
ReleaseResources()330 MediaDecoderReaderWrapper::ReleaseResources()
331 {
332 MOZ_ASSERT(mOwnerThread->IsCurrentThreadIn());
333 nsCOMPtr<nsIRunnable> r =
334 NewRunnableMethod(mReader, &MediaDecoderReader::ReleaseResources);
335 mReader->OwnerThread()->Dispatch(r.forget());
336 }
337
338 void
SetIdle()339 MediaDecoderReaderWrapper::SetIdle()
340 {
341 MOZ_ASSERT(mOwnerThread->IsCurrentThreadIn());
342 nsCOMPtr<nsIRunnable> r =
343 NewRunnableMethod(mReader, &MediaDecoderReader::SetIdle);
344 mReader->OwnerThread()->Dispatch(r.forget());
345 }
346
347 void
ResetDecode(TrackSet aTracks)348 MediaDecoderReaderWrapper::ResetDecode(TrackSet aTracks)
349 {
350 MOZ_ASSERT(mOwnerThread->IsCurrentThreadIn());
351
352 if (aTracks.contains(TrackInfo::kAudioTrack)) {
353 mAudioDataRequest.DisconnectIfExists();
354 mAudioWaitRequest.DisconnectIfExists();
355 }
356
357 if (aTracks.contains(TrackInfo::kVideoTrack)) {
358 mVideoDataRequest.DisconnectIfExists();
359 mVideoWaitRequest.DisconnectIfExists();
360 }
361
362 nsCOMPtr<nsIRunnable> r =
363 NewRunnableMethod<TrackSet>(mReader,
364 &MediaDecoderReader::ResetDecode,
365 aTracks);
366 mReader->OwnerThread()->Dispatch(r.forget());
367 }
368
369 RefPtr<ShutdownPromise>
Shutdown()370 MediaDecoderReaderWrapper::Shutdown()
371 {
372 MOZ_ASSERT(mOwnerThread->IsCurrentThreadIn());
373 MOZ_ASSERT(!mAudioDataRequest.Exists());
374 MOZ_ASSERT(!mVideoDataRequest.Exists());
375
376 mShutdown = true;
377 if (mStartTimeRendezvous) {
378 mStartTimeRendezvous->Destroy();
379 mStartTimeRendezvous = nullptr;
380 }
381 return InvokeAsync(mReader->OwnerThread(), mReader.get(), __func__,
382 &MediaDecoderReader::Shutdown);
383 }
384
385 void
OnMetadataRead(MetadataHolder * aMetadata)386 MediaDecoderReaderWrapper::OnMetadataRead(MetadataHolder* aMetadata)
387 {
388 MOZ_ASSERT(mOwnerThread->IsCurrentThreadIn());
389 if (mShutdown) {
390 return;
391 }
392 // Set up the start time rendezvous if it doesn't already exist (which is
393 // generally the case, unless we're coming out of dormant mode).
394 if (!mStartTimeRendezvous) {
395 mStartTimeRendezvous = new StartTimeRendezvous(
396 mOwnerThread, aMetadata->mInfo.HasAudio(),
397 aMetadata->mInfo.HasVideo(), mForceZeroStartTime);
398
399 RefPtr<MediaDecoderReaderWrapper> self = this;
400 mStartTimeRendezvous->AwaitStartTime()->Then(
401 mOwnerThread, __func__,
402 [self] () {
403 NS_ENSURE_TRUE_VOID(!self->mShutdown);
404 self->mReader->DispatchSetStartTime(self->StartTime().ToMicroseconds());
405 },
406 [] () {
407 NS_WARNING("Setting start time on reader failed");
408 });
409 }
410 }
411
412 void
SetVideoBlankDecode(bool aIsBlankDecode)413 MediaDecoderReaderWrapper::SetVideoBlankDecode(bool aIsBlankDecode)
414 {
415 MOZ_ASSERT(mOwnerThread->IsCurrentThreadIn());
416 nsCOMPtr<nsIRunnable> r =
417 NewRunnableMethod<bool>(mReader, &MediaDecoderReader::SetVideoBlankDecode,
418 aIsBlankDecode);
419 mReader->OwnerThread()->Dispatch(r.forget());
420 }
421
422 } // namespace mozilla
423