Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/dom/media/ipc/VideoDecoderChild.cpp
Line
Count
Source (jump to first uncovered line)
1
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2
/* vim: set ts=8 sts=2 et sw=2 tw=99: */
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 "VideoDecoderChild.h"
7
#include "VideoDecoderManagerChild.h"
8
#include "mozilla/layers/TextureClient.h"
9
#include "mozilla/Telemetry.h"
10
#include "base/thread.h"
11
#include "MediaInfo.h"
12
#include "ImageContainer.h"
13
#include "GPUVideoImage.h"
14
15
namespace mozilla {
16
namespace dom {
17
18
using base::Thread;
19
using namespace ipc;
20
using namespace layers;
21
using namespace gfx;
22
23
#ifdef XP_WIN
24
static void
25
ReportUnblacklistingTelemetry(bool isGPUProcessCrashed,
26
                              const nsCString& aD3D11BlacklistedDriver,
27
                              const nsCString& aD3D9BlacklistedDriver)
28
{
29
  const nsCString& blacklistedDLL = !aD3D11BlacklistedDriver.IsEmpty()
30
                                    ? aD3D11BlacklistedDriver
31
                                    : aD3D9BlacklistedDriver;
32
33
  if (!blacklistedDLL.IsEmpty()) {
34
    Telemetry::Accumulate(Telemetry::VIDEO_UNBLACKINGLISTING_DXVA_DRIVER_RUNTIME_STATUS,
35
                          blacklistedDLL,
36
                          isGPUProcessCrashed ? 1 : 0);
37
  }
38
}
39
#endif // XP_WIN
40
41
VideoDecoderChild::VideoDecoderChild()
42
  : mThread(VideoDecoderManagerChild::GetManagerThread())
43
  , mCanSend(false)
44
  , mInitialized(false)
45
  , mIsHardwareAccelerated(false)
46
  , mConversion(MediaDataDecoder::ConversionRequired::kNeedNone)
47
  , mNeedNewDecoder(false)
48
0
{
49
0
}
50
51
VideoDecoderChild::~VideoDecoderChild()
52
0
{
53
0
  AssertOnManagerThread();
54
0
  mInitPromise.RejectIfExists(NS_ERROR_DOM_MEDIA_CANCELED, __func__);
55
0
}
56
57
mozilla::ipc::IPCResult
58
VideoDecoderChild::RecvOutput(const VideoDataIPDL& aData)
59
0
{
60
0
  AssertOnManagerThread();
61
0
62
0
  // The Image here creates a TextureData object that takes ownership
63
0
  // of the SurfaceDescriptor, and is responsible for making sure that
64
0
  // it gets deallocated.
65
0
  RefPtr<Image> image = new GPUVideoImage(GetManager(), aData.sd(), aData.frameSize());
66
0
67
0
  RefPtr<VideoData> video = VideoData::CreateFromImage(
68
0
    aData.display(),
69
0
    aData.base().offset(),
70
0
    media::TimeUnit::FromMicroseconds(aData.base().time()),
71
0
    media::TimeUnit::FromMicroseconds(aData.base().duration()),
72
0
    image,
73
0
    aData.base().keyframe(),
74
0
    media::TimeUnit::FromMicroseconds(aData.base().timecode()));
75
0
76
0
  mDecodedData.AppendElement(std::move(video));
77
0
  return IPC_OK();
78
0
}
79
80
mozilla::ipc::IPCResult
81
VideoDecoderChild::RecvInputExhausted()
82
0
{
83
0
  AssertOnManagerThread();
84
0
  mDecodePromise.ResolveIfExists(mDecodedData, __func__);
85
0
  mDecodedData.Clear();
86
0
  return IPC_OK();
87
0
}
88
89
mozilla::ipc::IPCResult
90
VideoDecoderChild::RecvDrainComplete()
91
0
{
92
0
  AssertOnManagerThread();
93
0
  mDrainPromise.ResolveIfExists(mDecodedData, __func__);
94
0
  mDecodedData.Clear();
95
0
  return IPC_OK();
96
0
}
97
98
mozilla::ipc::IPCResult
99
VideoDecoderChild::RecvError(const nsresult& aError)
100
0
{
101
0
  AssertOnManagerThread();
102
0
  mDecodedData.Clear();
103
0
  mDecodePromise.RejectIfExists(aError, __func__);
104
0
  mDrainPromise.RejectIfExists(aError, __func__);
105
0
  mFlushPromise.RejectIfExists(aError, __func__);
106
0
  return IPC_OK();
107
0
}
108
109
mozilla::ipc::IPCResult
110
VideoDecoderChild::RecvInitComplete(const nsCString& aDecoderDescription,
111
                                    const bool& aHardware,
112
                                    const nsCString& aHardwareReason,
113
                                    const uint32_t& aConversion)
114
0
{
115
0
  AssertOnManagerThread();
116
0
  mInitPromise.ResolveIfExists(TrackInfo::kVideoTrack, __func__);
117
0
  mInitialized = true;
118
0
  mDescription = aDecoderDescription;
119
0
  mIsHardwareAccelerated = aHardware;
120
0
  mHardwareAcceleratedReason = aHardwareReason;
121
0
  mConversion = static_cast<MediaDataDecoder::ConversionRequired>(aConversion);
122
0
  return IPC_OK();
123
0
}
124
125
mozilla::ipc::IPCResult
126
VideoDecoderChild::RecvInitFailed(const nsresult& aReason)
127
0
{
128
0
  AssertOnManagerThread();
129
0
  mInitPromise.RejectIfExists(aReason, __func__);
130
0
  return IPC_OK();
131
0
}
132
133
mozilla::ipc::IPCResult
134
VideoDecoderChild::RecvFlushComplete()
135
0
{
136
0
  AssertOnManagerThread();
137
0
  mFlushPromise.ResolveIfExists(true, __func__);
138
0
  return IPC_OK();
139
0
}
140
141
void
142
VideoDecoderChild::ActorDestroy(ActorDestroyReason aWhy)
143
0
{
144
0
  if (aWhy == AbnormalShutdown) {
145
0
    // GPU process crashed, record the time and send back to MFR for telemetry.
146
0
    mGPUCrashTime = TimeStamp::Now();
147
0
148
0
    // Defer reporting an error until we've recreated the manager so that
149
0
    // it'll be safe for MediaFormatReader to recreate decoders
150
0
    RefPtr<VideoDecoderChild> ref = this;
151
0
    GetManager()->RunWhenRecreated(
152
0
      NS_NewRunnableFunction("dom::VideoDecoderChild::ActorDestroy", [=]() {
153
0
        MediaResult error(NS_ERROR_DOM_MEDIA_NEED_NEW_DECODER);
154
0
        error.SetGPUCrashTimeStamp(ref->mGPUCrashTime);
155
0
        if (ref->mInitialized) {
156
0
          mDecodedData.Clear();
157
0
          mDecodePromise.RejectIfExists(error, __func__);
158
0
          mDrainPromise.RejectIfExists(error, __func__);
159
0
          mFlushPromise.RejectIfExists(error, __func__);
160
0
          // Make sure the next request will be rejected accordingly if ever
161
0
          // called.
162
0
          mNeedNewDecoder = true;
163
0
        } else {
164
0
          ref->mInitPromise.RejectIfExists(error, __func__);
165
0
        }
166
0
      }));
167
0
  }
168
0
  mCanSend = false;
169
0
170
#ifdef XP_WIN
171
  ReportUnblacklistingTelemetry(aWhy == AbnormalShutdown,
172
                                mBlacklistedD3D11Driver,
173
                                mBlacklistedD3D9Driver);
174
#endif // XP_WIN
175
}
176
177
MediaResult
178
VideoDecoderChild::InitIPDL(const VideoInfo& aVideoInfo,
179
                            float aFramerate,
180
                            const layers::TextureFactoryIdentifier& aIdentifier)
181
0
{
182
0
  RefPtr<VideoDecoderManagerChild> manager =
183
0
    VideoDecoderManagerChild::GetSingleton();
184
0
185
0
  // The manager isn't available because VideoDecoderManagerChild has been
186
0
  // initialized with null end points and we don't want to decode video on GPU
187
0
  // process anymore. Return false here so that we can fallback to other PDMs.
188
0
  if (!manager) {
189
0
    return MediaResult(NS_ERROR_DOM_MEDIA_FATAL_ERR,
190
0
                       RESULT_DETAIL("VideoDecoderManager is not available."));
191
0
  }
192
0
193
0
  // The manager doesn't support sending messages because we've just crashed
194
0
  // and are working on reinitialization. Don't initialize mIPDLSelfRef and
195
0
  // leave us in an error state. We'll then immediately reject the promise when
196
0
  // Init() is called and the caller can try again. Hopefully by then the new
197
0
  // manager is ready, or we've notified the caller of it being no longer
198
0
  // available. If not, then the cycle repeats until we're ready.
199
0
  if (!manager->CanSend()) {
200
0
    return NS_OK;
201
0
  }
202
0
203
0
  mIPDLSelfRef = this;
204
0
  bool success = false;
205
0
  nsCString errorDescription;
206
0
  if (manager->SendPVideoDecoderConstructor(this,
207
0
                                            aVideoInfo,
208
0
                                            aFramerate,
209
0
                                            aIdentifier,
210
0
                                            &success,
211
0
                                            &mBlacklistedD3D11Driver,
212
0
                                            &mBlacklistedD3D9Driver,
213
0
                                            &errorDescription)) {
214
0
    mCanSend = true;
215
0
  }
216
0
217
0
  return success ? MediaResult(NS_OK) :
218
0
                   MediaResult(NS_ERROR_DOM_MEDIA_FATAL_ERR, errorDescription);
219
0
}
220
221
void
222
VideoDecoderChild::DestroyIPDL()
223
0
{
224
0
  if (mCanSend) {
225
0
    PVideoDecoderChild::Send__delete__(this);
226
0
  }
227
0
}
228
229
void
230
VideoDecoderChild::IPDLActorDestroyed()
231
0
{
232
0
  mIPDLSelfRef = nullptr;
233
0
}
234
235
// MediaDataDecoder methods
236
237
RefPtr<MediaDataDecoder::InitPromise>
238
VideoDecoderChild::Init()
239
0
{
240
0
  AssertOnManagerThread();
241
0
242
0
  if (!mIPDLSelfRef) {
243
0
    return MediaDataDecoder::InitPromise::CreateAndReject(
244
0
      NS_ERROR_DOM_MEDIA_DECODE_ERR, __func__);
245
0
  }
246
0
  // If we failed to send this, then we'll still resolve the Init promise
247
0
  // as ActorDestroy handles it.
248
0
  if (mCanSend) {
249
0
    SendInit();
250
0
  }
251
0
  return mInitPromise.Ensure(__func__);
252
0
}
253
254
RefPtr<MediaDataDecoder::DecodePromise>
255
VideoDecoderChild::Decode(MediaRawData* aSample)
256
0
{
257
0
  AssertOnManagerThread();
258
0
259
0
  if (mNeedNewDecoder) {
260
0
    MediaResult error(NS_ERROR_DOM_MEDIA_NEED_NEW_DECODER);
261
0
    error.SetGPUCrashTimeStamp(mGPUCrashTime);
262
0
    return MediaDataDecoder::DecodePromise::CreateAndReject(error, __func__);
263
0
  }
264
0
  if (!mCanSend) {
265
0
    // We're here if the IPC channel has died but we're still waiting for the
266
0
    // RunWhenRecreated task to complete. The decode promise will be rejected
267
0
    // when that task is run.
268
0
    return mDecodePromise.Ensure(__func__);
269
0
  }
270
0
271
0
  // TODO: It would be nice to add an allocator method to
272
0
  // MediaDataDecoder so that the demuxer could write directly
273
0
  // into shmem rather than requiring a copy here.
274
0
  Shmem buffer;
275
0
  if (!AllocShmem(aSample->Size(), Shmem::SharedMemory::TYPE_BASIC, &buffer)) {
276
0
    return MediaDataDecoder::DecodePromise::CreateAndReject(
277
0
      NS_ERROR_DOM_MEDIA_DECODE_ERR, __func__);
278
0
  }
279
0
280
0
  memcpy(buffer.get<uint8_t>(), aSample->Data(), aSample->Size());
281
0
282
0
  MediaRawDataIPDL sample(MediaDataIPDL(aSample->mOffset,
283
0
                                        aSample->mTime.ToMicroseconds(),
284
0
                                        aSample->mTimecode.ToMicroseconds(),
285
0
                                        aSample->mDuration.ToMicroseconds(),
286
0
                                        aSample->mFrames,
287
0
                                        aSample->mKeyframe),
288
0
                          buffer);
289
0
  SendInput(sample);
290
0
  return mDecodePromise.Ensure(__func__);
291
0
}
292
293
RefPtr<MediaDataDecoder::FlushPromise>
294
VideoDecoderChild::Flush()
295
0
{
296
0
  AssertOnManagerThread();
297
0
  mDecodePromise.RejectIfExists(NS_ERROR_DOM_MEDIA_CANCELED, __func__);
298
0
  mDrainPromise.RejectIfExists(NS_ERROR_DOM_MEDIA_CANCELED, __func__);
299
0
  if (mNeedNewDecoder) {
300
0
    MediaResult error(NS_ERROR_DOM_MEDIA_NEED_NEW_DECODER);
301
0
    error.SetGPUCrashTimeStamp(mGPUCrashTime);
302
0
    return MediaDataDecoder::FlushPromise::CreateAndReject(error, __func__);
303
0
  }
304
0
  if (mCanSend) {
305
0
    SendFlush();
306
0
  }
307
0
  return mFlushPromise.Ensure(__func__);
308
0
}
309
310
RefPtr<MediaDataDecoder::DecodePromise>
311
VideoDecoderChild::Drain()
312
0
{
313
0
  AssertOnManagerThread();
314
0
  if (mNeedNewDecoder) {
315
0
    MediaResult error(NS_ERROR_DOM_MEDIA_NEED_NEW_DECODER);
316
0
    error.SetGPUCrashTimeStamp(mGPUCrashTime);
317
0
    return MediaDataDecoder::DecodePromise::CreateAndReject(error, __func__);
318
0
  }
319
0
  if (mCanSend) {
320
0
    SendDrain();
321
0
  }
322
0
  return mDrainPromise.Ensure(__func__);
323
0
}
324
325
void
326
VideoDecoderChild::Shutdown()
327
0
{
328
0
  AssertOnManagerThread();
329
0
  mInitPromise.RejectIfExists(NS_ERROR_DOM_MEDIA_CANCELED, __func__);
330
0
  if (mCanSend) {
331
0
    SendShutdown();
332
0
  }
333
0
  mInitialized = false;
334
0
}
335
336
bool
337
VideoDecoderChild::IsHardwareAccelerated(nsACString& aFailureReason) const
338
0
{
339
0
  AssertOnManagerThread();
340
0
  aFailureReason = mHardwareAcceleratedReason;
341
0
  return mIsHardwareAccelerated;
342
0
}
343
344
nsCString
345
VideoDecoderChild::GetDescriptionName() const
346
0
{
347
0
  AssertOnManagerThread();
348
0
  return mDescription;
349
0
}
350
351
void
352
VideoDecoderChild::SetSeekThreshold(const media::TimeUnit& aTime)
353
0
{
354
0
  AssertOnManagerThread();
355
0
  if (mCanSend) {
356
0
    SendSetSeekThreshold(aTime.ToMicroseconds());
357
0
  }
358
0
}
359
360
MediaDataDecoder::ConversionRequired
361
VideoDecoderChild::NeedsConversion() const
362
0
{
363
0
  AssertOnManagerThread();
364
0
  return mConversion;
365
0
}
366
367
void
368
VideoDecoderChild::AssertOnManagerThread() const
369
0
{
370
0
  MOZ_ASSERT(NS_GetCurrentThread() == mThread);
371
0
}
372
373
VideoDecoderManagerChild*
374
VideoDecoderChild::GetManager()
375
0
{
376
0
  if (!mCanSend) {
377
0
    return nullptr;
378
0
  }
379
0
  return static_cast<VideoDecoderManagerChild*>(Manager());
380
0
}
381
382
} // namespace dom
383
} // namespace mozilla