Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/dom/media/ReaderProxy.cpp
Line
Count
Source (jump to first uncovered line)
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 "MediaFormatReader.h"
9
#include "ReaderProxy.h"
10
#include "TimeUnits.h"
11
12
namespace mozilla {
13
14
ReaderProxy::ReaderProxy(AbstractThread* aOwnerThread,
15
                         MediaFormatReader* aReader)
16
  : mOwnerThread(aOwnerThread)
17
  , mReader(aReader)
18
  , mWatchManager(this, aReader->OwnerThread())
19
  , mDuration(aReader->OwnerThread(),
20
              media::NullableTimeUnit(),
21
              "ReaderProxy::mDuration (Mirror)")
22
  , mSeamlessLoopingBlocked(false)
23
  , mSeamlessLoopingEnabled(false)
24
0
{
25
0
  // Must support either heuristic buffering or WaitForData().
26
0
  MOZ_ASSERT(mReader->UseBufferingHeuristics() ||
27
0
             mReader->IsWaitForDataSupported());
28
0
}
29
30
ReaderProxy::~ReaderProxy()
31
0
{}
32
33
media::TimeUnit
34
ReaderProxy::StartTime() const
35
0
{
36
0
  MOZ_ASSERT(mOwnerThread->IsCurrentThreadIn());
37
0
  return mStartTime.ref();
38
0
}
39
40
RefPtr<ReaderProxy::MetadataPromise>
41
ReaderProxy::ReadMetadata()
42
0
{
43
0
  MOZ_ASSERT(mOwnerThread->IsCurrentThreadIn());
44
0
  MOZ_ASSERT(!mShutdown);
45
0
  return InvokeAsync(mReader->OwnerThread(),
46
0
                     mReader.get(),
47
0
                     __func__,
48
0
                     &MediaFormatReader::AsyncReadMetadata)
49
0
    ->Then(mOwnerThread,
50
0
           __func__,
51
0
           this,
52
0
           &ReaderProxy::OnMetadataRead,
53
0
           &ReaderProxy::OnMetadataNotRead);
54
0
}
55
56
RefPtr<ReaderProxy::AudioDataPromise>
57
ReaderProxy::OnAudioDataRequestCompleted(RefPtr<AudioData> aAudio)
58
0
{
59
0
  MOZ_ASSERT(mOwnerThread->IsCurrentThreadIn());
60
0
61
0
  // Subtract the start time and add the looping-offset time.
62
0
  int64_t offset =
63
0
    StartTime().ToMicroseconds() - mLoopingOffset.ToMicroseconds();
64
0
  aAudio->AdjustForStartTime(offset);
65
0
  if (aAudio->mTime.IsValid()) {
66
0
    mLastAudioEndTime = aAudio->mTime;
67
0
    return AudioDataPromise::CreateAndResolve(aAudio.forget(), __func__);
68
0
  }
69
0
  return AudioDataPromise::CreateAndReject(NS_ERROR_DOM_MEDIA_OVERFLOW_ERR,
70
0
                                           __func__);
71
0
}
72
73
RefPtr<ReaderProxy::AudioDataPromise>
74
ReaderProxy::OnAudioDataRequestFailed(const MediaResult& aError)
75
0
{
76
0
  MOZ_ASSERT(mOwnerThread->IsCurrentThreadIn());
77
0
78
0
  if (mSeamlessLoopingBlocked || !mSeamlessLoopingEnabled ||
79
0
      aError.Code() != NS_ERROR_DOM_MEDIA_END_OF_STREAM) {
80
0
    return AudioDataPromise::CreateAndReject(aError, __func__);
81
0
  }
82
0
83
0
  // The data time in the audio queue is assumed to be increased linearly,
84
0
  // so we need to add the last ending time as the offset to correct the
85
0
  // audio data time in the next round when seamless looping is enabled.
86
0
  mLoopingOffset = mLastAudioEndTime;
87
0
88
0
  // Save the duration of the audio track if it hasn't been set.
89
0
  if (!mAudioDuration.IsValid()) {
90
0
    mAudioDuration = mLastAudioEndTime;
91
0
  }
92
0
93
0
  // For seamless looping, the demuxer is sought to the beginning and then
94
0
  // keep requesting decoded data in advance, upon receiving EOS.
95
0
  // The MDSM will not be aware of the EOS and keep receiving decoded data
96
0
  // as usual while looping is on.
97
0
  RefPtr<ReaderProxy> self = this;
98
0
  RefPtr<MediaFormatReader> reader = mReader;
99
0
  ResetDecode(TrackInfo::kAudioTrack);
100
0
  return SeekInternal(SeekTarget(media::TimeUnit::Zero(), SeekTarget::Accurate))
101
0
    ->Then(mReader->OwnerThread(),
102
0
           __func__,
103
0
           [reader]() { return reader->RequestAudioData(); },
104
0
           [](const SeekRejectValue& aReject) {
105
0
             return AudioDataPromise::CreateAndReject(aReject.mError, __func__);
106
0
           })
107
0
    ->Then(mOwnerThread,
108
0
           __func__,
109
0
           [self](RefPtr<AudioData> aAudio) {
110
0
             return self->OnAudioDataRequestCompleted(aAudio.forget());
111
0
           },
112
0
           [](const MediaResult& aError) {
113
0
             return AudioDataPromise::CreateAndReject(aError, __func__);
114
0
           });
115
0
}
116
117
RefPtr<ReaderProxy::AudioDataPromise>
118
ReaderProxy::RequestAudioData()
119
0
{
120
0
  MOZ_ASSERT(mOwnerThread->IsCurrentThreadIn());
121
0
  MOZ_ASSERT(!mShutdown);
122
0
123
0
  mSeamlessLoopingBlocked = false;
124
0
  return InvokeAsync(mReader->OwnerThread(),
125
0
                     mReader.get(),
126
0
                     __func__,
127
0
                     &MediaFormatReader::RequestAudioData)
128
0
    ->Then(mOwnerThread,
129
0
           __func__,
130
0
           this,
131
0
           &ReaderProxy::OnAudioDataRequestCompleted,
132
0
           &ReaderProxy::OnAudioDataRequestFailed);
133
0
}
134
135
RefPtr<ReaderProxy::VideoDataPromise>
136
ReaderProxy::RequestVideoData(const media::TimeUnit& aTimeThreshold)
137
0
{
138
0
  MOZ_ASSERT(mOwnerThread->IsCurrentThreadIn());
139
0
  MOZ_ASSERT(!mShutdown);
140
0
141
0
  mSeamlessLoopingBlocked = false;
142
0
  const auto threshold = aTimeThreshold > media::TimeUnit::Zero()
143
0
                         ? aTimeThreshold + StartTime()
144
0
                         : aTimeThreshold;
145
0
146
0
  int64_t startTime = StartTime().ToMicroseconds();
147
0
  return InvokeAsync(mReader->OwnerThread(),
148
0
                     mReader.get(),
149
0
                     __func__,
150
0
                     &MediaFormatReader::RequestVideoData,
151
0
                     threshold)
152
0
    ->Then(mOwnerThread,
153
0
           __func__,
154
0
           [startTime](RefPtr<VideoData> aVideo) {
155
0
             aVideo->AdjustForStartTime(startTime);
156
0
             return aVideo->mTime.IsValid()
157
0
                      ? VideoDataPromise::CreateAndResolve(aVideo.forget(),
158
0
                                                           __func__)
159
0
                      : VideoDataPromise::CreateAndReject(
160
0
                          NS_ERROR_DOM_MEDIA_OVERFLOW_ERR, __func__);
161
0
           },
162
0
           [](const MediaResult& aError) {
163
0
             return VideoDataPromise::CreateAndReject(aError, __func__);
164
0
           });
165
0
}
166
167
RefPtr<ReaderProxy::SeekPromise>
168
ReaderProxy::Seek(const SeekTarget& aTarget)
169
0
{
170
0
  MOZ_ASSERT(mOwnerThread->IsCurrentThreadIn());
171
0
  mSeamlessLoopingBlocked = true;
172
0
  // Reset the members for seamless looping if the seek is triggered outside.
173
0
  mLoopingOffset = media::TimeUnit::Zero();
174
0
  mLastAudioEndTime = media::TimeUnit::Zero();
175
0
  mAudioDuration = media::TimeUnit::Invalid();
176
0
  return SeekInternal(aTarget);
177
0
}
178
179
RefPtr<ReaderProxy::SeekPromise>
180
ReaderProxy::SeekInternal(const SeekTarget& aTarget)
181
0
{
182
0
  MOZ_ASSERT(mOwnerThread->IsCurrentThreadIn());
183
0
  SeekTarget adjustedTarget = aTarget;
184
0
  adjustedTarget.SetTime(adjustedTarget.GetTime() + StartTime());
185
0
  return InvokeAsync(mReader->OwnerThread(),
186
0
                     mReader.get(),
187
0
                     __func__,
188
0
                     &MediaFormatReader::Seek,
189
0
                     std::move(adjustedTarget));
190
0
}
191
192
RefPtr<ReaderProxy::WaitForDataPromise>
193
ReaderProxy::WaitForData(MediaData::Type aType)
194
0
{
195
0
  MOZ_ASSERT(mOwnerThread->IsCurrentThreadIn());
196
0
  MOZ_ASSERT(mReader->IsWaitForDataSupported());
197
0
  return InvokeAsync(mReader->OwnerThread(),
198
0
                     mReader.get(),
199
0
                     __func__,
200
0
                     &MediaFormatReader::WaitForData,
201
0
                     aType);
202
0
}
203
204
void
205
ReaderProxy::ReleaseResources()
206
0
{
207
0
  MOZ_ASSERT(mOwnerThread->IsCurrentThreadIn());
208
0
  nsCOMPtr<nsIRunnable> r =
209
0
    NewRunnableMethod("MediaFormatReader::ReleaseResources",
210
0
                      mReader,
211
0
                      &MediaFormatReader::ReleaseResources);
212
0
  nsresult rv = mReader->OwnerThread()->Dispatch(r.forget());
213
0
  MOZ_DIAGNOSTIC_ASSERT(NS_SUCCEEDED(rv));
214
0
  Unused << rv;
215
0
}
216
217
void
218
ReaderProxy::ResetDecode(TrackSet aTracks)
219
0
{
220
0
  MOZ_ASSERT(mOwnerThread->IsCurrentThreadIn());
221
0
  nsCOMPtr<nsIRunnable> r =
222
0
    NewRunnableMethod<TrackSet>("MediaFormatReader::ResetDecode",
223
0
                                mReader,
224
0
                                &MediaFormatReader::ResetDecode,
225
0
                                aTracks);
226
0
  nsresult rv = mReader->OwnerThread()->Dispatch(r.forget());
227
0
  MOZ_DIAGNOSTIC_ASSERT(NS_SUCCEEDED(rv));
228
0
  Unused << rv;
229
0
}
230
231
RefPtr<ShutdownPromise>
232
ReaderProxy::Shutdown()
233
0
{
234
0
  MOZ_ASSERT(mOwnerThread->IsCurrentThreadIn());
235
0
  mShutdown = true;
236
0
  RefPtr<ReaderProxy> self = this;
237
0
  return InvokeAsync(mReader->OwnerThread(), __func__, [self]() {
238
0
    self->mDuration.DisconnectIfConnected();
239
0
    self->mWatchManager.Shutdown();
240
0
    return self->mReader->Shutdown();
241
0
  });
242
0
}
243
244
RefPtr<ReaderProxy::MetadataPromise>
245
ReaderProxy::OnMetadataRead(MetadataHolder&& aMetadata)
246
0
{
247
0
  MOZ_ASSERT(mOwnerThread->IsCurrentThreadIn());
248
0
  if (mShutdown) {
249
0
    return MetadataPromise::CreateAndReject(
250
0
      NS_ERROR_DOM_MEDIA_ABORT_ERR, __func__);
251
0
  }
252
0
253
0
  if (mStartTime.isNothing()) {
254
0
    mStartTime.emplace(aMetadata.mInfo->mStartTime);
255
0
  }
256
0
  return MetadataPromise::CreateAndResolve(std::move(aMetadata), __func__);
257
0
}
258
259
RefPtr<ReaderProxy::MetadataPromise>
260
ReaderProxy::OnMetadataNotRead(const MediaResult& aError)
261
0
{
262
0
  return MetadataPromise::CreateAndReject(aError, __func__);
263
0
}
264
265
void
266
ReaderProxy::SetVideoBlankDecode(bool aIsBlankDecode)
267
0
{
268
0
  MOZ_ASSERT(mOwnerThread->IsCurrentThreadIn());
269
0
  nsCOMPtr<nsIRunnable> r =
270
0
    NewRunnableMethod<bool>("MediaFormatReader::SetVideoNullDecode",
271
0
                            mReader,
272
0
                            &MediaFormatReader::SetVideoNullDecode,
273
0
                            aIsBlankDecode);
274
0
  nsresult rv = mReader->OwnerThread()->Dispatch(r.forget());
275
0
  MOZ_DIAGNOSTIC_ASSERT(NS_SUCCEEDED(rv));
276
0
  Unused << rv;
277
0
}
278
279
void
280
ReaderProxy::UpdateDuration()
281
0
{
282
0
  MOZ_ASSERT(mReader->OwnerThread()->IsCurrentThreadIn());
283
0
  mReader->UpdateDuration(mDuration.Ref().ref());
284
0
}
285
286
void
287
ReaderProxy::SetCanonicalDuration(
288
  AbstractCanonical<media::NullableTimeUnit>* aCanonical)
289
0
{
290
0
  using DurationT = AbstractCanonical<media::NullableTimeUnit>;
291
0
  RefPtr<ReaderProxy> self = this;
292
0
  RefPtr<DurationT> canonical = aCanonical;
293
0
  nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction(
294
0
    "ReaderProxy::SetCanonicalDuration", [this, self, canonical]() {
295
0
      mDuration.Connect(canonical);
296
0
      mWatchManager.Watch(mDuration, &ReaderProxy::UpdateDuration);
297
0
    });
298
0
  nsresult rv = mReader->OwnerThread()->Dispatch(r.forget());
299
0
  MOZ_DIAGNOSTIC_ASSERT(NS_SUCCEEDED(rv));
300
0
  Unused << rv;
301
0
}
302
303
void
304
ReaderProxy::SetSeamlessLoopingEnabled(bool aEnabled)
305
0
{
306
0
  MOZ_ASSERT(mOwnerThread->IsCurrentThreadIn());
307
0
  mSeamlessLoopingEnabled = aEnabled;
308
0
}
309
310
void
311
ReaderProxy::AdjustByLooping(media::TimeUnit& aTime)
312
0
{
313
0
  MOZ_ASSERT(mOwnerThread->IsCurrentThreadIn());
314
0
  MOZ_ASSERT(!mShutdown);
315
0
  MOZ_ASSERT(!mSeamlessLoopingEnabled || !mSeamlessLoopingBlocked);
316
0
  if (mAudioDuration.IsValid() && mAudioDuration.IsPositive()) {
317
0
    aTime = aTime % mAudioDuration.ToMicroseconds();
318
0
  }
319
0
}
320
321
} // namespace mozilla